first commit
This commit is contained in:
@@ -1,2 +1,4 @@
|
||||
# lxplan_lib_creator
|
||||
|
||||
## Comment installer
|
||||
Telecharger le code puis ouvrir le fichier "index.html" avec un navigateur web (pas besoin de serveur web).
|
||||
9369
examples/all-factory-symbols.lxkey
Normal file
9369
examples/all-factory-symbols.lxkey
Normal file
File diff suppressed because it is too large
Load Diff
1478
examples/mylib.lxkey
Normal file
1478
examples/mylib.lxkey
Normal file
File diff suppressed because it is too large
Load Diff
11133
examples/test-lib-non-valide-1.lxxplot
Normal file
11133
examples/test-lib-non-valide-1.lxxplot
Normal file
File diff suppressed because it is too large
Load Diff
11175
examples/test-lib-valide-1.lxxplot
Normal file
11175
examples/test-lib-valide-1.lxxplot
Normal file
File diff suppressed because it is too large
Load Diff
11240
examples/test-lib-valide-2.lxxplot
Normal file
11240
examples/test-lib-valide-2.lxxplot
Normal file
File diff suppressed because it is too large
Load Diff
11342
examples/test-lib-valide-3.lxxplot
Normal file
11342
examples/test-lib-valide-3.lxxplot
Normal file
File diff suppressed because it is too large
Load Diff
11056
examples/test-symbol.lxxplot
Normal file
11056
examples/test-symbol.lxxplot
Normal file
File diff suppressed because it is too large
Load Diff
BIN
favicon.ico
Normal file
BIN
favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
102
index.html
Normal file
102
index.html
Normal file
@@ -0,0 +1,102 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>LxPlan keyGen</title>
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<nav class="tab-nav-container" id="tab-nav">
|
||||
<div class="buttons-set">
|
||||
<button class="tab" data-tab="home">Home</button>
|
||||
<button class="tab" data-tab="makeKey">Clef</button>
|
||||
<button class="tab active" data-tab="makeKeySet">Trousseau</button>
|
||||
<button class="tab" id="generateKeyFile" onclick="generateKeyFile()" hidden>Générer le fichier</button>
|
||||
</div>
|
||||
</nav>
|
||||
<main class="tab-content">
|
||||
<section id="home" class="tab-pane">
|
||||
<h1>LxPlan KeyGen</h1>
|
||||
<div class="container">
|
||||
<h2>Doc</h2>
|
||||
<details>
|
||||
<summary>Cliquer içi pour voir le topo</summary>
|
||||
<p>LxPlan utilise des fichiers de librairies au format texte xml.<br />On peut l'éditer avec un editeur de
|
||||
texte.<br />Dans ce fichier, chaque bécane s'appelle une "kentry" qui est dans l'ensemble principal qui
|
||||
s'appelle key.<br />Les 2 fichiers que génere ce script sont les memes sauf que l'un part d'un fichier
|
||||
*.lxxplot pour faire une seule lib et le deuxieme rassemble plusieurs libs en une seule.</p>
|
||||
</details>
|
||||
</div>
|
||||
<div class="container">
|
||||
<h2>Clef</h2>
|
||||
<details>
|
||||
<summary>Cliquer içi pour voir comment faire une lib</summary>
|
||||
<p>Pour créer une lib, il faut :
|
||||
<ul>
|
||||
<li>Dessiner le symbole dans lxplan ou poser un symbole existant</li>
|
||||
<li>Le symbole doit etre sur le premier layer</li>
|
||||
<li>Une fois fini, le symbole doit etre mis dans un groupe</li>
|
||||
<li>Enregistrer le projet dans un fichier .lxxplot</li>
|
||||
<li>Utiliser ce script en important le fichier lxxplot puis en remplissant les champs obligatoires et les
|
||||
autres si besoin</li>
|
||||
<li>Quand tout est valide, un bouton rouge "Générer le fichier" apparait en haut à droite.</li>
|
||||
<li>Sauvegarder le fichier qui va donner un fichier .lxkey qui sera à mettre dans le dossier de la
|
||||
bibliotheque (voir l'onglet "library" dans les options de lxplan)</li>
|
||||
</ul>
|
||||
<p>Note: si il y a des symboles déjà existants, les originaux seront automatiquement ajoutés dans la lib. Par
|
||||
exemple : si on fait une barre de pars, un par solo original sera ajouté à la fin de la lib.</p>
|
||||
</p>
|
||||
</details>
|
||||
</div>
|
||||
<div class="container">
|
||||
<h2>Trousseau</h2>
|
||||
<details>
|
||||
<summary>Cliquer içi pour voir comment faire un trousseau de clefs</summary>
|
||||
<p>Pour créer un trousseau, il faut :
|
||||
<ul>
|
||||
<li>Charger plusieurs fichiers de clefs.</li>
|
||||
<li>Le script vérifie si les fichiers ont l'air crédibles.</li>
|
||||
<li>On peut choisir les clefs à garder ou pas.</li>
|
||||
<li>Quand tout est valide, un bouton rouge "Générer le fichier" apparait en haut à droite.</li>
|
||||
<li>Sauvegarder le fichier qui va donner un fichier .lxkey qui sera à mettre dans le dossier de la
|
||||
bibliotheque (voir l'onglet "library" dans les options de lxplan)</li>
|
||||
</ul>
|
||||
</p>
|
||||
</details>
|
||||
</div>
|
||||
</section>
|
||||
<section id="makeKey" class="tab-pane">
|
||||
<div id="makeKeyLogMessage"></div>
|
||||
<div class="container">
|
||||
<h2>Charger les fichiers lxxplot</h2>
|
||||
<p>Sélectionnez un ou plusieurs fichiers llxplot sur votre appareil :</p>
|
||||
<input type="file" multiple id="plotFilesInput" accept=".lxxplot" onclick="resetMakeKeyChecks()" />
|
||||
</div>
|
||||
<div class="container">
|
||||
<div id="plotFilesList">
|
||||
<p>... En attente</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section id="makeKeySet" class="tab-pane active">
|
||||
<div id="makeKeySetLogMessages"></div>
|
||||
<div class="container">
|
||||
<h2>Charger les fichiers lxkey</h2>
|
||||
<p>Sélectionnez plusieurs fichiers lxkey sur votre appareil :</p>
|
||||
<input type="file" multiple id="keyFilesInput" accept=".lxkey" onclick="resetMakeKeySetChecks()" />
|
||||
</div>
|
||||
<div class="container">
|
||||
<div id="keyFilesList">
|
||||
<p>... En attente</p>
|
||||
</div>
|
||||
</div>
|
||||
<div id="filesList"></div>
|
||||
</section>
|
||||
</main>
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
32
notes.md
Normal file
32
notes.md
Normal file
@@ -0,0 +1,32 @@
|
||||
<code js>
|
||||
resultsDiv.innerHTML += `<p><strong>${file.name}</strong> - Failed to read ❌</p>`;
|
||||
</code>
|
||||
|
||||
<code js>
|
||||
Object.keys(file.fields.mandatory).length
|
||||
</code>
|
||||
|
||||
<code js>
|
||||
Object.entries(file.fields.mandatory).forEach(([key, value]) => {
|
||||
</code>
|
||||
|
||||
<code js>
|
||||
Array.from(files).map(file => {
|
||||
</code>
|
||||
|
||||
<code js>
|
||||
document.querySelectorAll('input[id^="mandatoryFields-"]').forEach(input => {
|
||||
</code>
|
||||
|
||||
<code js>
|
||||
for (let shape of shapes) {
|
||||
</code>
|
||||
|
||||
<code js>
|
||||
for (const [cle, valeur] of Object.entries(addedKeys)) { console.log("key", cle);console.log("valeur", valeur)}
|
||||
</code>
|
||||
|
||||
length of an associative array
|
||||
<code js>
|
||||
console.log(Reflect.ownKeys(addedKeys).length);
|
||||
</code>
|
||||
721
script.js
Normal file
721
script.js
Normal file
@@ -0,0 +1,721 @@
|
||||
/*
|
||||
* Variables
|
||||
*/
|
||||
|
||||
var inputFiles = [];
|
||||
var isValidFileCount = 0;
|
||||
var displayKeyFields = false;
|
||||
var filledFields = 0;
|
||||
var canGenerateKeyFile = false;
|
||||
var addedKeys = [];
|
||||
var selectedKeys = [];
|
||||
var customSymbolsXml = '';
|
||||
const makeKeyLogMessageDiv = document.getElementById('makeKeyLogMessage');
|
||||
const makeKeySetLogMessagesDiv = document.getElementById('makeKeySetLogMessages');
|
||||
const plotFilesListDiv = document.getElementById("plotFilesList");
|
||||
const keyFilesListDiv = document.getElementById("keyFilesList");
|
||||
const plotFilesInput = document.getElementById('plotFilesInput');
|
||||
const keyFilesInput = document.getElementById('keyFilesInput');
|
||||
const generateKeyFileButton = document.getElementById('generateKeyFile');
|
||||
|
||||
// Tableau des options de la liste de sélection
|
||||
const selectOptions = [
|
||||
{ id: "ers", label: "ERS", fields: "sid;have;lamp;watts;frame;beam;cd;abm;wt;note;d_mk;d_adj_x;d_adj_y;d_adj_z" },
|
||||
{ id: "zers", label: "Zoom Leko / ERS", fields: "sid;have;lamp;watts;frame;bm_w;bm_t;cd_w;cd_t;abm_w;abm_t;wt;note;d_mk;d_adj_x;d_adj_y;d_adj_z" },
|
||||
{ id: "fres", label: "Fresnel / PC", fields: "sid;have;lamp;watts;frame;bm_w;bm_t;cd_w;cd_t;abm_w;abm_t;wt;note;d_mk;d_adj_x;d_adj_y;d_adj_z" },
|
||||
{ id: "par", label: "PAR", fields: "sid;have;lamp;watts;frame;bm_x;bm_y;cd;abm_x;abm_y;wt;note;d_mk;d_adj_x;d_adj_y;d_adj_z" },
|
||||
{ id: "fl", label: "Flood", fields: "sid;have;lamp;watts;frame;beam;cd;abm;wt;note;d_mk;d_adj_x;d_adj_y;d_adj_z" },
|
||||
{ id: "strip", label: "Striplight", fields: "sid;have;lamp;watts;frame;bm_x;bm_y;cd;abm_x;abm_y;wt;cpf;lpc;dbl;note;d_mk;d_adj_x;d_adj_y;d_adj_z" },
|
||||
{ id: "scroll", label: "DMX Device", fields: "sid;have;note;scroll;dmode;d_mk;acc_loc" },
|
||||
{ id: "mirror", label: "Mirror", fields: "sid;have;note;scroll;dmode;d_mk;acc_loc" },
|
||||
{ id: "focus", label: "Focus Point", fields: "sid" },
|
||||
{ id: "misc", label: "Other", fields: "sid;have;acc_loc" },
|
||||
{ id: "mover", label: "Automated Fixture", fields: "sid;have;lamp;watts;frame;bm_w;bm_t;cd_w;cd_t;abm_w;abm_t;wt;note;scroll;dmode;d_mk;d_adj_x;d_adj_y;d_adj_z" },
|
||||
{ id: "cmymvr", label: "Color Mixing Automated Fixture", fields: "sid;have;lamp;watts;frame;bm_w;bm_t;cd_w;cd_t;abm_w;abm_t;wt;note;scroll;dmode;mixtype;d_mk;d_adj_x;d_adj_y;d_adj_z" },
|
||||
{ id: "rgb", label: "Color Mixing Rectangular Beam (obsolete rgb)", fields: "sid;have;lamp;watts;frame;bm_x;bm_y;cd;abm_x;abm_y;wt;note;scroll;dmode;mixtype;d_adj_x;d_adj_y;d_adj_z" },
|
||||
{ id: "rgba", label: "Color Mixing Rectangular Beam (obsolete rgba)", fields: "sid;have;lamp;watts;frame;bm_x;bm_y;cd;abm_x;abm_y;wt;note;scroll;dmode;mixtype;d_adj_x;d_adj_y;d_adj_z" },
|
||||
{ id: "led7", label: "Color Mixing Rectangular Beam", fields: "sid;have;lamp;watts;frame;bm_x;bm_y;cd;abm_x;abm_y;wt;note;scroll;dmode;mixtype;d_adj_x;d_adj_y;d_adj_z" },
|
||||
{ id: "led7s", label: "Color Mixing Striplight", fields: "sid;have;lamp;watts;frame;bm_x;bm_y;cd;abm_x;abm_y;wt;cpf;lpc;dbl;note;scroll;dmode;mixtype;d_adj_x;d_adj_y;d_adj_z" },
|
||||
{ id: "cmers", label: "Color Mixing Leko / ERS", fields: "sid;have;lamp;watts;frame;beam;cd;abm;wt;note;scroll;dmode;mixtype;d_mk;d_adj_x;d_adj_y;d_adj_z" },
|
||||
{ id: "cmzers", label: "Color Mixing Zoom ERS", fields: "sid;have;lamp;watts;frame;bm_w;bm_t;cd_w;cd_t;abm_w;abm_t;wt;note;scroll;dmode;mixtype;d_mk;d_adj_x;d_adj_y;d_adj_z" },
|
||||
{ id: "cmscroll", label: "Color Mixing DMX Device", fields: "sid;have;note;scroll;dmode;mixtype;d_mk" },
|
||||
{ id: "netnode", label: "Network Device", fields: "sid;have;note;dmode" },
|
||||
];
|
||||
|
||||
// Tableau des champs disponibles
|
||||
const availableFields = [
|
||||
{ id: "name", label: "Type", type: "text", help: "Nom court" },
|
||||
{ id: "fname", label: "Full Name", type: "text", help: "Nom complet" },
|
||||
{ id: "id", label: "ID", type: "text", help: "" },
|
||||
{ id: "sid", label: "Symbol ID", type: "text", help: "" },
|
||||
{ id: "have", label: "Inventory", type: "number", help: "Quantités en stock" },
|
||||
{ id: "balance", label: "Balance", type: "number", help: "Poids" },
|
||||
{ id: "lamp", label: "Lamp", type: "text", help: "cp70 ? cp90 ?" },
|
||||
{ id: "watts", label: "Watts", type: "number", help: "" },
|
||||
{ id: "frame", label: "Color Frame", type: "text", help: "" },
|
||||
{ id: "beam", label: "Field Angle", type: "number", help: "" },
|
||||
{ id: "bm_x", label: "Field X", type: "number", help: "" },
|
||||
{ id: "bm_y", label: "Field Y", type: "number", help: "" },
|
||||
{ id: "bm_w", label: "Field Wide", type: "number", help: "" },
|
||||
{ id: "bm_t", label: "Field Tight", type: "number", help: "" },
|
||||
{ id: "cd", label: "Candela", type: "number", help: "" },
|
||||
{ id: "cd_w", label: "Candela Wide", type: "number", help: "" },
|
||||
{ id: "cd_t", label: "Candela Tight", type: "number", help: "" },
|
||||
{ id: "abm", label: "Beam Angle", type: "number", help: "" },
|
||||
{ id: "abm_x", label: "Beam X", type: "number", help: "" },
|
||||
{ id: "abm_y", label: "Beam Y", type: "number", help: "" },
|
||||
{ id: "abm_w", label: "Beam Wide", type: "number", help: "" },
|
||||
{ id: "abm_t", label: "Beam Tight", type: "number", help: "" },
|
||||
{ id: "wt", label: "Weight", type: "number", help: "" },
|
||||
{ id: "cpf", label: "Sections Per Fixture", type: "number", help: "" },
|
||||
{ id: "lpc", label: "Lamps Per Circuit", type: "number", help: "" },
|
||||
{ id: "dbl", label: "Distance Between Lamps", type: "number", help: "" },
|
||||
{ id: "note", label: "More Info", type: "text", help: "" },
|
||||
{ id: "scroll", label: "Device Params", type: "text", help: "" },
|
||||
{ id: "dmode", label: "Mode", type: "text", help: "" },
|
||||
{ id: "mixtype", label: "Mix Type", type: "text", help: "" },
|
||||
{ id: "d_mk", label: "Default Mark", type: "text", help: "" },
|
||||
{ id: "acc_loc", label: "Placement", type: "text", help: "" },
|
||||
{ id: "d_adj_x", label: "Default X Offset", type: "number", help: "" },
|
||||
{ id: "d_adj_y", label: "Default Y Offset", type: "number", help: "" },
|
||||
{ id: "d_adj_z", label: "Default Z Offset", type: "number", help: "" },
|
||||
];
|
||||
|
||||
/**
|
||||
* Listeners
|
||||
*/
|
||||
|
||||
// listener une fois que la page est chargée
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
manageHtmlPanels();
|
||||
//loadCustomSymbols();
|
||||
});
|
||||
|
||||
// listener pour savoir quand un champ d'appareil est changé
|
||||
document.body.addEventListener('change', function (event) {
|
||||
let index = event.target.id.substring(event.target.id.indexOf('-') + 1);
|
||||
|
||||
// ecoute si la liste de selection de type d'appareil est changée
|
||||
if (event.target.tagName === 'SELECT' && event.target.classList.contains('kind-select-list')) {
|
||||
displayoptionalFields(index, event.target.value);
|
||||
inputFiles[index].fields.mandatory.kind = event.target.value;
|
||||
checkFilledFields();
|
||||
}
|
||||
// ecoute si un champ obligatoire est changé
|
||||
if (event.target.tagName === 'INPUT' && event.target.classList.contains('mandatory-field')) {
|
||||
inputFiles[index].fields.mandatory[event.target.name] = event.target.value;
|
||||
checkFilledFields();
|
||||
}
|
||||
// ecoute si un champ optionnel est changé
|
||||
if (event.target.tagName === 'INPUT' && event.target.classList.contains('optional-field')) {
|
||||
inputFiles[index].fields.optional[event.target.name] = event.target.value;
|
||||
}
|
||||
// ecoute si une checkbox de la liste d'appareils est changée
|
||||
if (event.target.tagName === 'INPUT' && (event.target.type === "checkbox")) {
|
||||
countCheckedKentries();
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Analyse
|
||||
*/
|
||||
|
||||
function loadCustomSymbols() {
|
||||
customSymbolsXml = '';
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', 'symbols.xml', false); // Synchronous request
|
||||
xhr.overrideMimeType('text/xml');
|
||||
xhr.send(null);
|
||||
customSymbolsXml = xhr.responseXML;
|
||||
}
|
||||
|
||||
// listener pour voir quand on selectionne les fichiers de plot
|
||||
plotFilesInput.addEventListener('change', async function (event) {
|
||||
const files = event.target.files;
|
||||
const results = [];
|
||||
|
||||
// Cree un tableau de promesse pour chaque fichier
|
||||
const filePromises = Array.from(files).map(file => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.onload = () => {
|
||||
|
||||
file.isXML = false;
|
||||
file.isLxplot = false;
|
||||
file.hasShape = false;
|
||||
file.hasGroup = false;
|
||||
|
||||
// recupere le contenu du fichier
|
||||
file.content = reader.result;
|
||||
|
||||
// Parse la chaine xml
|
||||
const parser = new DOMParser();
|
||||
const xmlDoc = parser.parseFromString(file.content, "text/xml");
|
||||
|
||||
// Verifie si le parsing s'est bien passé
|
||||
const parserErrors = xmlDoc.getElementsByTagName("parsererror");
|
||||
if (parserErrors.length === 0) {
|
||||
file.isXML = true;
|
||||
}
|
||||
|
||||
// Verifie si l'element root est nommé "lxplot"
|
||||
const rootElement = xmlDoc.documentElement;
|
||||
if (rootElement.tagName === "lxplot") {
|
||||
file.isLxplot = true;
|
||||
}
|
||||
|
||||
// vérifie si il y a un element shape dans le premier layer
|
||||
const firstLayer = rootElement.getElementsByTagName("layer")[0];
|
||||
const firstShapeOfLayer = firstLayer.getElementsByTagName("shape")[0];
|
||||
if (firstShapeOfLayer) {
|
||||
file.hasShape = true;
|
||||
file.shapeXml = firstShapeOfLayer;
|
||||
// verifie si il y a d'autres symboles intégrés dans la shape
|
||||
const subShapes = firstShapeOfLayer.getElementsByTagName("shape");
|
||||
for (let shape of subShapes) {
|
||||
const shapeFirstClass = shape.getElementsByTagName("class")[0];
|
||||
if (shapeFirstClass.textContent === "LXLight") {
|
||||
// on recupere le kid
|
||||
const kid = shape.getElementsByTagName("kid")[0];
|
||||
// si oui, on cherche la clef dans le fichier
|
||||
const kentries = rootElement.getElementsByTagName("kentry");
|
||||
for (let kentry of kentries) {
|
||||
if (kentry.getElementsByTagName("id")[0].textContent === kid.textContent) {
|
||||
addedKeys.push(kentry);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// verifie si la premiere shape contient un groupe nommé lxgroup
|
||||
const classElement = firstShapeOfLayer.getElementsByTagName("class")[0];
|
||||
if (classElement && classElement.textContent === "LXGroup") {
|
||||
file.hasGroup = true;
|
||||
}
|
||||
|
||||
file.isValidFile =
|
||||
file.isXML &&
|
||||
file.isLxplot &&
|
||||
file.hasShape &&
|
||||
file.hasGroup;
|
||||
if (file.isValidFile) {
|
||||
isValidFileCount++;
|
||||
}
|
||||
file.index = inputFiles.length;
|
||||
inputFiles.push(file);
|
||||
resolve({
|
||||
name: file.name,
|
||||
isXML: file.isXML,
|
||||
isLxplot: file.isLxplot,
|
||||
hasShape: file.hasShape,
|
||||
hasGroup: file.hasGroup
|
||||
});
|
||||
};
|
||||
reader.onerror = () => reject(new Error(`Failed to read ${file.name}`));
|
||||
reader.readAsText(file); // or readAsDataURL for binary
|
||||
});
|
||||
});
|
||||
|
||||
try {
|
||||
// attendre que tous les fichiers soient traités
|
||||
const allResults = await Promise.all(filePromises);
|
||||
|
||||
// affiche les resultats seulement quand tous les fichiers sont traités
|
||||
displayKeyFields = (isValidFileCount === inputFiles.length);
|
||||
if (!displayKeyFields) {
|
||||
makeKeyLogMessageDiv.innerHTML +=
|
||||
`<p class="error-message">Pour générer une clef, il ne faut que des fichiers valides.</p>`
|
||||
}
|
||||
fillPlotFileInfosDiv(allResults);
|
||||
} catch (error) {
|
||||
console.error('Error processing files:', error);
|
||||
}
|
||||
});
|
||||
|
||||
// listener pour voir quand on selectionne les fichiers de key
|
||||
keyFilesInput.addEventListener('change', async function (event) {
|
||||
const files = event.target.files;
|
||||
const results = [];
|
||||
|
||||
// Cree un tableau de promesse pour chaque fichier
|
||||
const filePromises = Array.from(files).map(file => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.onload = () => {
|
||||
|
||||
file.isXML = false;
|
||||
file.isLxKey = false;
|
||||
|
||||
// recupere le contenu du fichier
|
||||
file.content = reader.result;
|
||||
|
||||
// Parse la chaine xml
|
||||
const parser = new DOMParser();
|
||||
const xmlDoc = parser.parseFromString(file.content, "text/xml");
|
||||
|
||||
// Verifie si le parsing s'est bien passé
|
||||
const parserErrors = xmlDoc.getElementsByTagName("parsererror");
|
||||
if (parserErrors.length === 0) {
|
||||
file.isXML = true;
|
||||
}
|
||||
|
||||
// Verifie si l'element root est nommé "key"
|
||||
const rootElement = xmlDoc.documentElement;
|
||||
if (rootElement.tagName === "key") {
|
||||
file.isLxKey = true;
|
||||
}
|
||||
|
||||
// recupere toutes les clefs
|
||||
const kentries = rootElement.getElementsByTagName("kentry");
|
||||
for (let kentry of kentries) {
|
||||
const keyId = kentry.getElementsByTagName("id")[0].textContent;
|
||||
//addedKeys.push(kentry);
|
||||
if (!addedKeys[keyId]) {
|
||||
addedKeys[keyId] = kentry;
|
||||
}
|
||||
}
|
||||
|
||||
file.isValidFile =
|
||||
file.isXML &&
|
||||
file.isLxKey;
|
||||
if (file.isValidFile) {
|
||||
isValidFileCount++;
|
||||
}
|
||||
file.index = inputFiles.length;
|
||||
inputFiles.push(file);
|
||||
resolve({
|
||||
name: file.name,
|
||||
isXML: file.isXML,
|
||||
isLxKey: file.isLxKey
|
||||
});
|
||||
};
|
||||
reader.onerror = () => reject(new Error(`Failed to read ${file.name}`));
|
||||
reader.readAsText(file); // or readAsDataURL for binary
|
||||
});
|
||||
});
|
||||
|
||||
try {
|
||||
// attendre que tous les fichiers soient traités
|
||||
const allResults = await Promise.all(filePromises);
|
||||
|
||||
// affiche les resultats seulement quand tous les fichiers sont traités
|
||||
displayKeyFields = (isValidFileCount === inputFiles.length);
|
||||
if (!displayKeyFields) {
|
||||
makeKeySetLogMessagesDiv.innerHTML +=
|
||||
`<p class="error-message">Pour générer une clef, il ne faut que des fichiers valides.</p>`
|
||||
}
|
||||
fillKeyFileInfosDiv(allResults);
|
||||
} catch (error) {
|
||||
console.error('Error processing files:', error);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Affichage
|
||||
*/
|
||||
|
||||
// affiche la liste des fichiers de plot
|
||||
function fillPlotFileInfosDiv(results) {
|
||||
generateKeyFileButton.name = "key";
|
||||
plotFilesListDiv.innerHTML = ``;
|
||||
let index = 0;
|
||||
results.forEach(result => {
|
||||
let fileInfosDiv = document.createElement('div');
|
||||
fileInfosDiv.className = "container";
|
||||
fileInfosDiv.id = "fileInformations-" + index;
|
||||
let fileInfos = document.createElement('ul');
|
||||
fileInfos.className = "fileInformations";
|
||||
fileInfos.innerHTML =
|
||||
'<li>Nom du fichier : <span class="file-name">' + result.name + '</span></li>' +
|
||||
'<li>Le fichier est il un xml ? : ' + (result.isXML ?
|
||||
'<span class="valid">oui</span>' :
|
||||
'<span class="invalid">non</span>') + '</li>' +
|
||||
'<li>Le fichier est il un lxplot ? : ' + (result.isLxplot ?
|
||||
'<span class="valid">oui</span>' :
|
||||
'<span class="invalid">non</span>') + '</li>' +
|
||||
'<li>Le fichier possede une shape ? : ' + (result.hasShape ?
|
||||
'<span class="valid">oui</span>' :
|
||||
'<span class="invalid">non</span>') + '</li>' +
|
||||
'<li>La shape est dans un groupe ? : ' + (result.hasGroup ?
|
||||
'<span class="valid">oui</span>' :
|
||||
'<span class="invalid">non</span>') + '</li>';
|
||||
fileInfosDiv.appendChild(fileInfos);
|
||||
if (displayKeyFields) {
|
||||
fileInfosDiv.appendChild(buildKeyFields(index));
|
||||
}
|
||||
plotFilesListDiv.appendChild(fileInfosDiv);
|
||||
index++;
|
||||
});
|
||||
}
|
||||
|
||||
// affiche la liste des fichiers de clefs
|
||||
function fillKeyFileInfosDiv(results) {
|
||||
generateKeyFileButton.name = "keySet";
|
||||
keyFilesListDiv.innerHTML = ``;
|
||||
let index = 0;
|
||||
results.forEach(result => {
|
||||
let fileInfosDiv = document.createElement('div');
|
||||
fileInfosDiv.className = "container";
|
||||
fileInfosDiv.id = "fileInformations-" + index;
|
||||
let fileInfos = document.createElement('ul');
|
||||
fileInfos.className = "fileInformations";
|
||||
fileInfos.innerHTML =
|
||||
'<li>Nom du fichier : <span class="file-name">' + result.name + '</span></li>' +
|
||||
'<li>Le fichier est il un xml ? : ' + (result.isXML ?
|
||||
'<span class="valid">oui</span>' :
|
||||
'<span class="invalid">non</span>') + '</li>' +
|
||||
'<li>Le fichier est il une lib lxkey ? : ' + (result.isLxKey ?
|
||||
'<span class="valid">oui</span>' :
|
||||
'<span class="invalid">non</span>') + '</li>';
|
||||
fileInfosDiv.appendChild(fileInfos);
|
||||
keyFilesListDiv.appendChild(fileInfosDiv);
|
||||
index++;
|
||||
});
|
||||
|
||||
if (displayKeyFields) {
|
||||
keyFilesListDiv.appendChild(buildKeyList());
|
||||
// coche tout le monde ou personne
|
||||
const checkAll = document.getElementById('selectAll');
|
||||
const checkboxes = document.querySelectorAll('input[name="kentrySelection"]');
|
||||
checkAll.addEventListener('change', function () {
|
||||
checkboxes.forEach(checkbox => {
|
||||
checkbox.checked = checkAll.checked;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function buildKeyList() {
|
||||
let index = 0
|
||||
const keylist = document.createElement("div");
|
||||
keylist.classList.add("container");
|
||||
const keyListTable = document.createElement("table");
|
||||
const keyListTableHeaders = document.createElement("tr");
|
||||
keyListTableHeaders.innerHTML = `
|
||||
<th><input type="checkbox" id="selectAll" checked /></th>
|
||||
<th>Name</th>
|
||||
<th>Full Name</th>
|
||||
<th>Kind</th>
|
||||
`;
|
||||
keyListTable.appendChild(keyListTableHeaders);
|
||||
//const keylistUl = document.createElement("ul");
|
||||
for (const [key, value] of Object.entries(addedKeys)) {
|
||||
let name = addedKeys[key].getElementsByTagName("name")[0].textContent;
|
||||
let fname = addedKeys[key].getElementsByTagName("fname")[0].textContent;
|
||||
let kind = addedKeys[key].getElementsByTagName("kind")[0].textContent;
|
||||
// cree le tag tr
|
||||
const keyListTableTr = document.createElement("tr");
|
||||
// cree le tag td
|
||||
keyListTableTr.innerHTML = `
|
||||
<td><input type="checkbox" name="kentrySelection" value="${key}" checked="false"></input></td>
|
||||
<td>${name}</td>
|
||||
<td>${fname}</td>
|
||||
<td>${kind}</td>
|
||||
`;
|
||||
|
||||
keyListTable.appendChild(keyListTableTr);
|
||||
selectedKeys.push(key);
|
||||
index++;
|
||||
}
|
||||
canGenerateKeyFile = false;
|
||||
generateKeyFileButton.hidden = true;
|
||||
if (selectedKeys.length > 0) {
|
||||
canGenerateKeyFile = true;
|
||||
generateKeyFileButton.hidden = false;
|
||||
}
|
||||
keylist.appendChild(keyListTable);
|
||||
|
||||
return keylist;
|
||||
}
|
||||
|
||||
// construit les champs pour chaque fichier
|
||||
function buildKeyFields(index) {
|
||||
// div principale
|
||||
let keyFieldsDiv = document.createElement('div');
|
||||
keyFieldsDiv.id = "dataFields-" + index;
|
||||
keyFieldsDiv.className = 'form-section';
|
||||
keyFieldsDiv.innerHTML += `<h2>Données de la librairie</h2>`;
|
||||
inputFiles[index].fields = [];
|
||||
// champs obligatoires
|
||||
inputFiles[index].fields.mandatory = [];
|
||||
keyFieldsDiv.innerHTML += `<h3>Champs obligatoires</h3>`;
|
||||
let mandatoryFieldsDiv = buildMandatoryFields(index);
|
||||
keyFieldsDiv.appendChild(mandatoryFieldsDiv);
|
||||
// champs optionnels
|
||||
inputFiles[index].fields.optional = [];
|
||||
keyFieldsDiv.innerHTML += `<h3>Champs optionnels</h3>`;
|
||||
let optionalFieldsDiv = document.createElement('div');
|
||||
optionalFieldsDiv.id = "optionalFields-" + index;
|
||||
optionalFieldsDiv.innerHTML += `<p>Selectionner d'abord un type d'appareil</p>`;
|
||||
// on met sa div dans la div principale
|
||||
keyFieldsDiv.appendChild(optionalFieldsDiv);
|
||||
return keyFieldsDiv;
|
||||
}
|
||||
|
||||
// construit la select list pour les types d'appareils
|
||||
function buildKindSelectList(index) {
|
||||
// div
|
||||
let kindSelectListDiv = document.createElement('div');
|
||||
kindSelectListDiv.id = "kindSelect-" + index;
|
||||
kindSelectListDiv.classList.add('mandatory-field');
|
||||
// label
|
||||
kindSelectListDiv.innerHTML += `<label for="kindSelect-${index}">Type d'appareil</label>`;
|
||||
// select
|
||||
let kindSelectList = document.createElement('select');
|
||||
kindSelectList.id = "kindSelect-" + index;
|
||||
kindSelectList.name = "kindSelect-" + index;
|
||||
kindSelectList.classList.add('kind-select-list');
|
||||
// option par defaut
|
||||
let kindSelectListDefault = document.createElement('option');
|
||||
kindSelectListDefault.value = "none";
|
||||
kindSelectListDefault.textContent = "Sélectionner un élément dans la liste";
|
||||
kindSelectListDefault.selected = true;
|
||||
// on ajoute la valeur par defaut dans la balise select
|
||||
kindSelectList.appendChild(kindSelectListDefault);
|
||||
// on fabrique la liste deroulante
|
||||
selectOptions.forEach((option) => {
|
||||
const optElement = document.createElement("option");
|
||||
optElement.value = option.id;
|
||||
optElement.textContent = option.label;
|
||||
kindSelectList.appendChild(optElement);
|
||||
});
|
||||
// on ajoute la balise select dans la div de depart
|
||||
kindSelectListDiv.appendChild((kindSelectList));
|
||||
return kindSelectListDiv;
|
||||
}
|
||||
|
||||
// construit la liste des champs obligatoires
|
||||
function buildMandatoryFields(index) {
|
||||
let mandatoryFieldsDiv = document.createElement('div');
|
||||
mandatoryFieldsDiv.id = "mandatoryFields-" + index;
|
||||
mandatoryFieldsDiv.classList.add("container");
|
||||
mandatoryFieldsDiv.innerHTML = `
|
||||
<div>
|
||||
<label for="name-${index}">Type</label>
|
||||
<input class="mandatory-field" type="text" id="name-${index}" name="name" title="Nom court" required />
|
||||
</div>
|
||||
<div>
|
||||
<label for="fname-${index}">Full Name</label>
|
||||
<input class="mandatory-field" type="text" id="fname-${index}" name="fname" title="Nom complet" required />
|
||||
</div>
|
||||
<div>
|
||||
<label for="id-${index}">Id</label>
|
||||
<input class="mandatory-field" type="text" id="id-${index}" name="id" title="Id unique" required />
|
||||
</div>
|
||||
`;
|
||||
mandatoryFieldsDiv.appendChild(buildKindSelectList(index));
|
||||
return mandatoryFieldsDiv;
|
||||
}
|
||||
|
||||
// Affiche les champs optionnels en fonction du type d'appareil sélectionné
|
||||
function displayoptionalFields(index, selectedOptionId) {
|
||||
let optionalFields = document.getElementById("optionalFields-" + index);
|
||||
optionalFields.classList.add("fields-set");
|
||||
optionalFields.classList.add("container");
|
||||
optionalFields.innerHTML = ``;// Efface les champs précédents
|
||||
|
||||
// Trouve l'option sélectionnée
|
||||
const selectedOption = selectOptions.find((option) => option.id === selectedOptionId);
|
||||
|
||||
if (!selectedOption || !selectedOption.fields) return;
|
||||
|
||||
// Récupère les IDs des champs à afficher
|
||||
const fieldIds = selectedOption.fields.split(";");
|
||||
|
||||
// Affiche chaque champ
|
||||
fieldIds.forEach((fieldId) => {
|
||||
const field = availableFields.find((f) => f.id === fieldId);
|
||||
if (field) {
|
||||
const fieldDiv = document.createElement("div");
|
||||
fieldDiv.innerHTML = `
|
||||
<label for="${field.id}-${index}">${field.label}:</label>
|
||||
<input class="optional-field" type="${field.type}" id="${field.id}-${index}" name="${field.id}" title="${field.help}" />
|
||||
`;
|
||||
optionalFields.appendChild(fieldDiv);
|
||||
}
|
||||
});
|
||||
return optionalFields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generation du fichier
|
||||
*/
|
||||
|
||||
function generateKeyFile() {
|
||||
// const inputValue = document.querySelector('#mandatoryFields-1 input[id="name"]').value;
|
||||
const xmlDoc = new DOMParser().parseFromString('<key></key>', 'application/xml');
|
||||
const root = xmlDoc.documentElement;
|
||||
|
||||
switch (generateKeyFileButton.name) {
|
||||
case "key":
|
||||
// ajoute les clefs de chaque fichier
|
||||
inputFiles.forEach((file) => {
|
||||
root.appendChild(buildXmlKentry(xmlDoc, file));
|
||||
});
|
||||
|
||||
// ajoute les clefs supplementaires si existantes
|
||||
if (addedKeys.length > 0) {
|
||||
// recuperer la clef
|
||||
for (let addedKey of addedKeys) {
|
||||
root.appendChild(addedKey);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "keySet":
|
||||
// ajoute les clefs si existantes
|
||||
if (selectedKeys.length > 0) {
|
||||
// recuperer la clef
|
||||
for (let selectedKey of selectedKeys) {
|
||||
root.appendChild(addedKeys[selectedKey]);
|
||||
console.log(addedKeys[selectedKey]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
// conversion en chaine de characteres
|
||||
const xmlString = "<?xml version='1.0' encoding='utf-8'?>\n" + new XMLSerializer().serializeToString(xmlDoc);
|
||||
//console.log(xmlString);
|
||||
|
||||
// creation fichier
|
||||
const blob = new Blob([xmlString], { type: 'application/xml;charset=utf-8' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
|
||||
// envoi du fichier
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = 'mylib.lxkey';
|
||||
a.click();
|
||||
|
||||
// nettoyage
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
// verifier que les champs soient bien remplis
|
||||
function checkFilledFields() {
|
||||
let mandatoryFieldsCount = 0;
|
||||
inputFiles.forEach((file) => {
|
||||
let filledFieldsCount = 0;
|
||||
Object.entries(file.fields.mandatory).forEach(([key, value]) => {
|
||||
if (value !== '' && value !== 'none') {
|
||||
filledFieldsCount++;
|
||||
}
|
||||
});
|
||||
// check if mandatory fields are filled
|
||||
if (filledFieldsCount === 4) {
|
||||
mandatoryFieldsCount++;
|
||||
}
|
||||
});
|
||||
if (mandatoryFieldsCount === inputFiles.length) {
|
||||
canGenerateKeyFile = true;
|
||||
generateKeyFileButton.hidden = false;
|
||||
}
|
||||
else {
|
||||
canGenerateKeyFile = false;
|
||||
generateKeyFileButton.hidden = true;
|
||||
}
|
||||
}
|
||||
|
||||
function getFilledChildren(id) {
|
||||
// recupere les champs
|
||||
const divElement = document.getElementById(id);
|
||||
const inputNodes = divElement.getElementsByTagName('input');
|
||||
|
||||
// teste les champs un par un
|
||||
for (let i = 0; i < inputNodes.length; i++) {
|
||||
if (inputNodes[i].value.trim() !== '') {
|
||||
filledFields.push({ "id": inputNodes[i].id, "value": inputNodes[i].value });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function countCheckedKentries() {
|
||||
selectedKeys = [];
|
||||
canGenerateKeyFile = false;
|
||||
generateKeyFileButton.hidden = true;
|
||||
const checkboxes = document.querySelectorAll('input[name="kentrySelection"]');
|
||||
checkboxes.forEach((checkbox) => {
|
||||
if (checkbox.checked) {
|
||||
selectedKeys.push(checkbox.value);
|
||||
}
|
||||
});
|
||||
if (selectedKeys.length > 0) {
|
||||
canGenerateKeyFile = true;
|
||||
generateKeyFileButton.hidden = false;
|
||||
}
|
||||
}
|
||||
|
||||
function buildXmlKentry(xmlDoc, file) {
|
||||
// creation des elements basiques
|
||||
const kentryElement = xmlDoc.createElement('kentry');
|
||||
const customElement = xmlDoc.createElement("custom");
|
||||
const symbolElement = xmlDoc.createElement("symbol");
|
||||
const groupElement = xmlDoc.createElement("group");
|
||||
// ajout de la shape dans un groupe
|
||||
groupElement.appendChild(file.shapeXml);
|
||||
// ajout du groupe dans un symbole
|
||||
symbolElement.appendChild(groupElement);
|
||||
// ajout du symbole dans un custom
|
||||
customElement.appendChild(symbolElement);
|
||||
// ajout des champs obligatoires
|
||||
Object.entries(file.fields.mandatory).forEach(([key, value]) => {
|
||||
const mandatoryField = xmlDoc.createElement(key);
|
||||
mandatoryField.textContent = value;
|
||||
kentryElement.appendChild(mandatoryField);
|
||||
});
|
||||
// ajout des champs optionnels
|
||||
Object.entries(file.fields.optional).forEach(([key, value]) => {
|
||||
const optionalField = xmlDoc.createElement(key);
|
||||
optionalField.textContent = value;
|
||||
kentryElement.appendChild(optionalField);
|
||||
});
|
||||
// ajout du custom dans la kentry
|
||||
kentryElement.appendChild(customElement);
|
||||
return kentryElement;
|
||||
}
|
||||
|
||||
// gestion des panneaux html
|
||||
function manageHtmlPanels() {
|
||||
// gestion des panneaux
|
||||
const tabs = document.querySelectorAll('.tab');
|
||||
const tabPanes = document.querySelectorAll('.tab-pane');
|
||||
|
||||
// gere le click
|
||||
tabs.forEach(tab => {
|
||||
tab.addEventListener('click', () => {
|
||||
if (tab.id != "generateKeyFile") {
|
||||
const tabId = tab.getAttribute('data-tab');
|
||||
|
||||
// met a jour le panneau actif
|
||||
tabs.forEach(t => t.classList.remove('active'));
|
||||
tab.classList.add('active');
|
||||
|
||||
// montre le contenu correspondant
|
||||
tabPanes.forEach(pane => {
|
||||
pane.classList.remove('active');
|
||||
if (pane.id === tabId) {
|
||||
pane.classList.add('active');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// reset
|
||||
function globalResets() {
|
||||
inputFiles = [];
|
||||
addedKeys = [];
|
||||
filledFields = 0;
|
||||
selectedKeys = [];
|
||||
makeKeyLogMessageDiv.innerHTML = "";
|
||||
plotFilesListDiv.innerHTML = `<p>... En attente</p>`;
|
||||
makeKeySetLogMessagesDiv.innerHTML = "";
|
||||
keyFilesListDiv.innerHTML = `<p>... En attente</p>`;
|
||||
isValidFileCount = 0;
|
||||
displayKeyFields = false;
|
||||
canGenerateKeyFile = false;
|
||||
generateKeyFileButton.hidden = true;
|
||||
}
|
||||
236
styles.css
Normal file
236
styles.css
Normal file
@@ -0,0 +1,236 @@
|
||||
/* styles.css */
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
/*box-sizing: border-box;*/
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
background-color: #1a1a1a;
|
||||
color: #ddd;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
text-align: center;
|
||||
border: 1px solid orange;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.tab-nav-container {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
/*background-color: #ffffff;*/
|
||||
/*border-bottom: 1px solid #ddd;*/
|
||||
/*position: fixed;*/
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 60px;
|
||||
padding: 10px 0;
|
||||
/*box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1);*/
|
||||
/*margin: 20px;*/
|
||||
}
|
||||
|
||||
.tab {
|
||||
flex: 1;
|
||||
padding: 10px 0;
|
||||
border: none;
|
||||
font-size: 14px;
|
||||
background-color: #1a1a1a;
|
||||
color: #8b8b8b;
|
||||
border: 1px solid orange;
|
||||
cursor: pointer;
|
||||
transition: color 0.3s ease;
|
||||
border-radius: 8px;
|
||||
max-width: 30%;
|
||||
min-height: 100%;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.tab.active {
|
||||
background-color: #000;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
border: 2px solid red;
|
||||
}
|
||||
|
||||
.tab:hover {
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.tab#generateKeyFile {
|
||||
background-color: red;
|
||||
border: 2px solid orange;
|
||||
font-weight: bold;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.tab-content {
|
||||
/*padding-top: 60px;*/
|
||||
height: calc(100vh - 60px);
|
||||
overflow-y: auto;
|
||||
/*background-color: red;*/
|
||||
}
|
||||
|
||||
.tab-pane {
|
||||
display: none;
|
||||
/*margin: 2%;*/
|
||||
padding: 10px;
|
||||
/*background-color: #202020f9;*/
|
||||
/*min-height: 100%;*/
|
||||
/*border: 1 px solid white;
|
||||
border-radius:5%;*/
|
||||
}
|
||||
|
||||
.tab-pane.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* old */
|
||||
.container {
|
||||
border: 1px solid #777;
|
||||
border-radius: 5px;
|
||||
margin: 5px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.container ul {
|
||||
margin-left: 40px;
|
||||
}
|
||||
|
||||
.buttons-set {
|
||||
display: contents;
|
||||
}
|
||||
|
||||
.valid {
|
||||
color: white;
|
||||
background-color: green;
|
||||
}
|
||||
|
||||
.invalid {
|
||||
color: white;
|
||||
background-color: red;
|
||||
animation: blinker-one 1.4s linear infinite;
|
||||
}
|
||||
|
||||
.no-answer {
|
||||
color: black;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.file-name {
|
||||
font-weight: bold;
|
||||
font-size: larger;
|
||||
}
|
||||
|
||||
#plotLogMessages {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
border: 1px solid red;
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
color: #000000;
|
||||
background-color: red;
|
||||
/* text-shadow: 4px 1px 2px #555555; */
|
||||
font-size: larger;
|
||||
animation: blinker-one 1.4s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes blinker-one {
|
||||
100% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/** form **/
|
||||
.form-section {
|
||||
font-family: Arial, sans-serif;
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
#mandatoryFields>div,
|
||||
#optionalFields>div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
label {
|
||||
min-width: 150px;
|
||||
font-weight: bold;
|
||||
margin-right: 15px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
input[type="text"],
|
||||
input[type="number"],
|
||||
select {
|
||||
flex: 1;
|
||||
padding: 8px;
|
||||
/*border: 1px solid #ccc;*/
|
||||
border-radius: 4px;
|
||||
/*background-color: #bbbaba;*/
|
||||
color: black;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
input[type="number"] {
|
||||
background-color: #111;
|
||||
color: #aaa;
|
||||
/*border: none;
|
||||
font-size: 1em;*/
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#optionalFields details {
|
||||
margin-top: 20px;
|
||||
padding: 10px;
|
||||
border: 1px solid #eee;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
#optionalFields summary {
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.custom-number-input::after {
|
||||
content: "⌄";
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
pointer-events: none;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
table, th, td {
|
||||
border:1px solid grey;
|
||||
}
|
||||
|
||||
th {
|
||||
background-color: black;
|
||||
}
|
||||
|
||||
th, td {
|
||||
padding: 5px;
|
||||
}
|
||||
1005
symbols.xml
Normal file
1005
symbols.xml
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user