added js_native/lxplan_lib_builder.html
This commit is contained in:
448
js_native/lxplan_lib_builder.html
Normal file
448
js_native/lxplan_lib_builder.html
Normal file
@@ -0,0 +1,448 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Lxplan lib builder</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 20px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
textarea {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 8px 16px;
|
||||
background-color: #007BFF;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: #0056b3;
|
||||
}
|
||||
|
||||
.test-result>span {
|
||||
font-weight: bold;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.valid {
|
||||
color: white;
|
||||
background-color: green;
|
||||
}
|
||||
|
||||
.invalid {
|
||||
color: white;
|
||||
background-color: red;
|
||||
}
|
||||
|
||||
.no-answer {
|
||||
color: black;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.container {
|
||||
border: 1px solid black;
|
||||
border-radius: 5px;
|
||||
margin: 5px;
|
||||
padding: 10px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Lxplan lib builder</h1>
|
||||
<div class="container">
|
||||
<details>
|
||||
<summary>Cliquer içi pour lire la doc</summary>
|
||||
<p>Pour créer une librairie, il faut : <br />
|
||||
<ul>
|
||||
<li>Dessiner le symbole dans lxplan</li>
|
||||
<li>Le symbole doit etre sur le premier layer</li>
|
||||
<li>Une fois fini, le symbole doit etre 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>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>
|
||||
<div class="container">
|
||||
<h2>Charger le fichier lxxplot</h2>
|
||||
<p>Sélectionnez un fichier llxplot sur votre appareil :</p>
|
||||
<input type="file" id="fileInput" accept=".lxxplot" onclick="resetChecks()" />
|
||||
</div>
|
||||
<div class="container">
|
||||
<h2>Tester le fichier lxxplot</h2>
|
||||
<div>Le fichier est il un lxxplot ? : <span id="isLxxplot" class="test-result">...</span></div>
|
||||
<div>Le fichier possede t il une shape sur le premier layer ? :<span id="hasShape"
|
||||
class="test-result">...</span></div>
|
||||
<div>La shape, commence t elle par un groupe ? :<span id="hasGroup" class="test-result">...</span></div>
|
||||
<button onclick="loadFile()">Tester le fichier</button>
|
||||
</div>
|
||||
<div id="dataFields" class="container" hidden>
|
||||
<h2>Données de la librairie</h2>
|
||||
<div class="form-section">
|
||||
<div id="mandatoryFields">
|
||||
<h3>Champs obligatoires</h3>
|
||||
<div>
|
||||
<label for="name">Type</label>
|
||||
<input type="text" id="name" name="name" required />
|
||||
</div>
|
||||
<div>
|
||||
<label for="fname">Full Name</label>
|
||||
<input type="text" id="fname" name="fname" required />
|
||||
</div>
|
||||
<div>
|
||||
<label for="id">Id</label>
|
||||
<input type="text" id="id" name="id" required />
|
||||
</div>
|
||||
<div>
|
||||
<label for="dataType">Type d'appareil</label>
|
||||
<select id="dataType">
|
||||
<option value="">Sélectionner un type d'appareil</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div id="optionalFields">
|
||||
<h3>Champs optionnels</h3>
|
||||
<details>
|
||||
<summary>
|
||||
Cliquer içi pour editer les champs optionnels
|
||||
</summary>
|
||||
<div id="dynamicFields">Selectionner d'abord un type d'appareil</div>
|
||||
</details>
|
||||
</div>
|
||||
<button id="saveButton" onclick="buildOutput()">Créer la librairie</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// declaration des variables
|
||||
var xmlInputContent = "";
|
||||
var xmlOutputContent = "";
|
||||
var shape = "";
|
||||
var isLxxplot = false;
|
||||
var hasShape = false;
|
||||
var hasGroup = false;
|
||||
var filledOptionalFields = undefined;
|
||||
var filledFields = [];
|
||||
var isLxxplotDiv = document.getElementById("isLxxplot");
|
||||
var hasShapeDiv = document.getElementById("hasShape");
|
||||
var hasGroupDiv = document.getElementById("hasGroup");
|
||||
var dataFields = document.getElementById("dataFields");
|
||||
var dataTypeSelect = document.getElementById('dataType');
|
||||
var dynamicFields = document.getElementById('dynamicFields');
|
||||
|
||||
// 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" },
|
||||
{ id: "fname", label: "Full Name", type: "text" },
|
||||
{ id: "id", label: "ID", type: "text" },
|
||||
{ id: "sid", label: "Symbol ID", type: "text" },
|
||||
{ id: "have", label: "Inventory", type: "number" },
|
||||
{ id: "balance", label: "Balance", type: "number" },
|
||||
{ id: "lamp", label: "Lamp", type: "text" },
|
||||
{ id: "watts", label: "Watts", type: "number" },
|
||||
{ id: "frame", label: "Color Frame", type: "text" },
|
||||
{ id: "beam", label: "Field Angle", type: "number" },
|
||||
{ id: "bm_x", label: "Field X", type: "number" },
|
||||
{ id: "bm_y", label: "Field Y", type: "number" },
|
||||
{ id: "bm_w", label: "Field Wide", type: "number" },
|
||||
{ id: "bm_t", label: "Field Tight", type: "number" },
|
||||
{ id: "cd", label: "Candela", type: "number" },
|
||||
{ id: "cd_w", label: "Candela Wide", type: "number" },
|
||||
{ id: "cd_t", label: "Candela Tight", type: "number" },
|
||||
{ id: "abm", label: "Beam Angle", type: "number" },
|
||||
{ id: "abm_x", label: "Beam X", type: "number" },
|
||||
{ id: "abm_y", label: "Beam Y", type: "number" },
|
||||
{ id: "abm_w", label: "Beam Wide", type: "number" },
|
||||
{ id: "abm_t", label: "Beam Tight", type: "number" },
|
||||
{ id: "wt", label: "Weight", type: "number" },
|
||||
{ id: "cpf", label: "Sections Per Fixture", type: "number" },
|
||||
{ id: "lpc", label: "Lamps Per Circuit", type: "number" },
|
||||
{ id: "dbl", label: "Distance Between Lamps", type: "number" },
|
||||
{ id: "note", label: "More Info", type: "text" },
|
||||
{ id: "scroll", label: "Device Params", type: "text" },
|
||||
{ id: "dmode", label: "Mode", type: "text" },
|
||||
{ id: "mixtype", label: "Mix Type", type: "text" },
|
||||
{ id: "d_mk", label: "Default Mark", type: "text" },
|
||||
{ id: "acc_loc", label: "Placement", type: "text" },
|
||||
{ id: "d_adj_x", label: "Default X Offset", type: "number" },
|
||||
{ id: "d_adj_y", label: "Default Y Offset", type: "number" },
|
||||
{ id: "d_adj_z", label: "Default Z Offset", type: "number" },
|
||||
];
|
||||
|
||||
// reinitialise les resultats du test de fichier
|
||||
function resetChecks() {
|
||||
let content = '<span class="no-answer">Sans avis</span>';
|
||||
isLxxplotDiv.innerHTML = content;
|
||||
hasShapeDiv.innerHTML = content;
|
||||
hasGroupDiv.innerHTML = content;
|
||||
dataFields.hidden = true;
|
||||
}
|
||||
|
||||
// action quand le document est bien chargé
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
resetChecks();
|
||||
fillSelectOptions();
|
||||
});
|
||||
|
||||
// Remplit la liste déroulante de type d'appareil
|
||||
function fillSelectOptions() {
|
||||
dataTypeSelect.innerHTML = '<option value="">Sélectionner un type d\'appareil</option>';
|
||||
selectOptions.forEach((option) => {
|
||||
const optElement = document.createElement("option");
|
||||
optElement.value = option.id;
|
||||
optElement.textContent = option.label;
|
||||
dataTypeSelect.appendChild(optElement);
|
||||
});
|
||||
}
|
||||
|
||||
// ecoute si le type d'appareil est changé
|
||||
dataTypeSelect.addEventListener("change", () => {
|
||||
const selectedOptionId = dataTypeSelect.value;
|
||||
displayDynamicFields(selectedOptionId);
|
||||
});
|
||||
|
||||
// Affiche les champs optionnels en fonction du type d'appareil sélectionné
|
||||
function displayDynamicFields(selectedOptionId) {
|
||||
dynamicFields.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}">${field.label}:</label>
|
||||
<input type="${field.type}" id="${field.id}" name="${field.id}" />
|
||||
`;
|
||||
dynamicFields.appendChild(fieldDiv);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// charge le fichier lxxplot
|
||||
function loadFile() {
|
||||
const fileInput = document.getElementById('fileInput');
|
||||
const file = fileInput.files[0];
|
||||
if (!file) {
|
||||
alert("Veuillez sélectionner un fichier lxxplot.");
|
||||
return;
|
||||
}
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.onload = function (e) {
|
||||
xmlInputContent = e.target.result;
|
||||
dataFields.hidden = !runChecks();
|
||||
};
|
||||
reader.readAsText(file);
|
||||
}
|
||||
|
||||
// verifie le contenu du fichier lxxplot
|
||||
function runChecks() {
|
||||
isLxxplot = checkIfValidPath("/lxplot", 'isLxxplot');
|
||||
hasShape = checkIfValidPath("/lxplot/layers/layer/shape", 'hasShape');
|
||||
if (hasShape) {
|
||||
getShape();
|
||||
}
|
||||
hasGroup = checkIfValidPath("/lxplot/layers/layer/shape/class[text() = 'LXGroup']", 'hasGroup');
|
||||
return (isLxxplot && hasShape && hasGroup);
|
||||
}
|
||||
|
||||
// petit utilitaire pour tester le contenu du fichier lxxplot
|
||||
function checkIfValidPath(xpathQuery, resultDiv) {
|
||||
resultDiv = document.getElementById(resultDiv);
|
||||
let status = parseXml(xpathQuery).status;
|
||||
resultDiv.innerHTML = status ? '<span class="valid">oui</span>' : '<span class="invalid">non</span>';
|
||||
return status;
|
||||
}
|
||||
|
||||
// petit utilitaire permettant de tester le xml du fichier lxxplot
|
||||
function parseXml(xpathQuery) {
|
||||
try {
|
||||
// Parser le XML
|
||||
const parser = new DOMParser();
|
||||
const xmlDoc = parser.parseFromString(xmlInputContent, "text/xml");
|
||||
|
||||
// Exécuter la requête XPath
|
||||
const xpathResult = document.evaluate(
|
||||
xpathQuery,
|
||||
xmlDoc,
|
||||
null,
|
||||
XPathResult.ANY_TYPE,
|
||||
null
|
||||
);
|
||||
|
||||
let result = [];
|
||||
let node = xpathResult.iterateNext();
|
||||
while (node) {
|
||||
result.push(node.textContent);
|
||||
node = xpathResult.iterateNext();
|
||||
}
|
||||
|
||||
// Afficher le résultat
|
||||
if (result.length > 0) {
|
||||
return { "status": true, "data": result };
|
||||
} else {
|
||||
return { "status": false, "data": "Not found" };
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("error parsing xml", e.message);
|
||||
return { "status": false, "data": "Error : " + e.message };
|
||||
}
|
||||
}
|
||||
|
||||
// reuperer la shape depuis le fichier lxxplot
|
||||
function getShape() {
|
||||
try {
|
||||
// Parser le XML
|
||||
const parser = new DOMParser();
|
||||
const xmlDoc = parser.parseFromString(xmlInputContent, "text/xml");
|
||||
|
||||
// Exécuter la requête XPath
|
||||
const xpathResult = document.evaluate(
|
||||
"/lxplot/layers/layer/shape",
|
||||
xmlDoc,
|
||||
null,
|
||||
XPathResult.FIRST_ORDERED_NODE_TYPE,
|
||||
null
|
||||
);
|
||||
|
||||
shape = xpathResult.singleNodeValue;
|
||||
} catch (e) {
|
||||
console.error("error parsing xml", e.message);
|
||||
return { "status": false, "data": "Error : " + e.message };
|
||||
}
|
||||
}
|
||||
|
||||
// action quand on appuie sur le bouton "creer la librairie"
|
||||
function buildOutput() {
|
||||
if (checkFilledFields()) {
|
||||
buildXmlOutput();
|
||||
}
|
||||
}
|
||||
|
||||
// verifier que les champs soient bien remplis
|
||||
function checkFilledFields() {
|
||||
filledFields = [];
|
||||
getFilledChildren('mandatoryFields');
|
||||
if (Object.keys(filledFields).length < 3 || dataTypeSelect.value === '') {
|
||||
alert("Les 4 champs obligatoires doivent être remplis.");
|
||||
return false;
|
||||
}
|
||||
getFilledChildren('dynamicFields');
|
||||
return 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 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fabrique le fichier de sortie et l'envoie a l'utilisateur
|
||||
function buildXmlOutput() {
|
||||
// creation du document xml
|
||||
const xmlDoc = new DOMParser().parseFromString('<key></key>', 'application/xml');
|
||||
const root = xmlDoc.documentElement;
|
||||
|
||||
// creation des elements basiques
|
||||
const kentryElement = xmlDoc.createElement('kentry');
|
||||
const customElement = xmlDoc.createElement("custom");
|
||||
const symbolElement = xmlDoc.createElement("symbol");
|
||||
const groupElement = xmlDoc.createElement("group");
|
||||
const kindElement = xmlDoc.createElement("kind");
|
||||
kindElement.textContent = dataTypeSelect.value;
|
||||
|
||||
// ajout des elements basiques dans l'arbre xml
|
||||
groupElement.appendChild(shape);
|
||||
symbolElement.appendChild(groupElement);
|
||||
customElement.appendChild(symbolElement);
|
||||
kentryElement.appendChild(kindElement);
|
||||
|
||||
// ajout de chaque champ rempli
|
||||
filledFields.forEach((item) => {
|
||||
const newItem = xmlDoc.createElement(item.id);
|
||||
newItem.textContent = item.value;
|
||||
kentryElement.appendChild(newItem);
|
||||
});
|
||||
|
||||
kentryElement.appendChild(customElement);
|
||||
root.appendChild(kentryElement);
|
||||
|
||||
// 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);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Reference in New Issue
Block a user