diff --git a/docs/gui/DistortionsGUI.js b/docs/gui/DistortionsGUI.js index 523aa687..5283fa2b 100644 --- a/docs/gui/DistortionsGUI.js +++ b/docs/gui/DistortionsGUI.js @@ -114,7 +114,7 @@ const DistortionsGUI = window.DistortionsGUI = { for (let prop in rulesMap) { if (rulesMap[prop] instanceof DistortionsRules) data.rules[prop] = rulesMap[prop].configurationAsJSON(); - else if (prop in ["source", "sourceGraphIndex"]) + else if (prop === "source") data[prop] = rulesMap[prop]; else data.rules[prop] = rulesMap[prop]; @@ -135,7 +135,8 @@ const DistortionsGUI = window.DistortionsGUI = { // XXX ajvincent Need to let the GUI know this value name is taken return; - const valueFromSource = graph.valueGetterEditor.getValue(); + let valueFromSource = graph.valueGetterEditor.getValue(); + await DistortionsManager.BlobLoader.addNamedValue(valueName, valueFromSource); const panel = document.createElement("section"); @@ -152,12 +153,16 @@ const DistortionsGUI = window.DistortionsGUI = { const value = DistortionsManager.BlobLoader.valuesByName.get(panel.dataset.valueName); const rules = this.buildDistortions(panel, value); - DistortionsManager.valueNameToRulesMap.set( - panel.dataset.hash, { - "value": rules, - "source": valueFromSource, - } - ); + const distortionsSet = { + "about": { + "valueName": valueName, + "isFunction": (typeof value === "function"), + "getExample": valueFromSource.split("\n").slice(1, -2).join("\n"), + }, + "value": rules, + }; + DistortionsManager.valueNameToRulesMap.set(panel.dataset.hash, distortionsSet); + graph.distortionMaps.push(distortionsSet); OuterGridManager.panels.appendChild(panel); diff --git a/docs/gui/DistortionsRules.js b/docs/gui/DistortionsRules.js index 6da6b55c..d527fbe5 100644 --- a/docs/gui/DistortionsRules.js +++ b/docs/gui/DistortionsRules.js @@ -222,11 +222,8 @@ DistortionsRules.prototype = { throw new Error("Not implemented!"); }, - configurationAsJSON: function() { + exportJSON: function() { const rv = { - formatVersion: "0.8.2", - dataVersion: "0.1", - /* filterOwnKeys: [] || null, inheritOwnKeys: false, diff --git a/docs/gui/HandlerNames.js b/docs/gui/HandlerNames.js index e78813b9..2f625cde 100644 --- a/docs/gui/HandlerNames.js +++ b/docs/gui/HandlerNames.js @@ -85,13 +85,13 @@ const HandlerNames = window.HandlerNames = { * @param config {JSONObject} The configuration. */ importConfig: function(config) { - for (let i = 0; i < config.graphNames.length; i++) { - this.setRow(i, config.graphNames[i], config.graphSymbolLists.includes(i)); + for (let i = 0; i < config.graphs.length; i++) { + this.setRow(i, config.graphs[i].name, config.graphs[i].isSymbol); } const range = document.createRange(); const delButton = this.grid.getElementsByTagName("button")[ - config.graphNames.length + config.graphs.length ]; range.setEndBefore(this.grid.lastChild); range.setStartAfter(delButton); diff --git a/docs/gui/OuterGridManager.js b/docs/gui/OuterGridManager.js index 07c7a887..8059835b 100644 --- a/docs/gui/OuterGridManager.js +++ b/docs/gui/OuterGridManager.js @@ -113,8 +113,15 @@ const OuterGridManager = window.OuterGridManager = { // Update the cached configuration { const [graphNames, graphSymbolLists] = HandlerNames.serializableNames(); - config.graphNames = graphNames; - config.graphSymbolLists = graphSymbolLists; + if (!Array.isArray(config.graphs)) { + config.graphs = []; + } + while (config.graphs.length < graphNames.length) + config.graphs.push({}); + graphNames.forEach(function(name, index) { + config.graphs[index].name = name; + config.graphs[index].isSymbol = graphSymbolLists.includes(index); + }); } // Define our object graph managers @@ -126,8 +133,8 @@ const OuterGridManager = window.OuterGridManager = { this.graphNamesCache.controllers.push(new ObjectGraphManager()); } const controller = this.graphNamesCache.controllers[i]; + controller.importJSON(config.graphs[i]); controller.setGraphName(name); - controller.readDistortionsData(config.distortionsByGraph[i]); } const deadControllers = this.graphNamesCache.controllers.slice(names.length); diff --git a/docs/gui/mainPanels/graphs.js b/docs/gui/mainPanels/graphs.js index 34a417bd..efd5fd8f 100644 --- a/docs/gui/mainPanels/graphs.js +++ b/docs/gui/mainPanels/graphs.js @@ -1,10 +1,13 @@ function ObjectGraphManager() { this.radioClass = `graphpanel-${ObjectGraphManager.instanceCount}`; ObjectGraphManager.instanceCount++; - this.distortions = []; + + this.distortionMaps = []; this.passThroughEditor = null; this.valueGetterEditor = null; + this.jsonBase = null; + this.buildUI(); } ObjectGraphManager.instanceCount = 0; @@ -32,7 +35,58 @@ ObjectGraphManager.prototype.buildUI = function() { this.radio.addEventListener("change", this, true); } -ObjectGraphManager.prototype.setGraphName = function(name) { +ObjectGraphManager.prototype.importJSON = function(data) { + this.jsonBase = { + "name": data.name, + "isSymbol": data.isSymbol + }; +}; + +ObjectGraphManager.prototype.exportJSON = function(graphIndex) { + const rv = { + "name": this.jsonBase.name, + "isSymbol": this.jsonBase.isSymbol, + "passThroughSource": null, + "passThroughEnabled": null, + "primordialsPass": false, + "distortions": [], + }; + + if (this.passThroughEditor) { + let lines = this.passThroughEditor.getValue().split("\n"); + lines = lines.slice(2, -6); + rv.passThroughSource = lines.join("\n"); + + rv.passThroughEnabled = this.passThroughCheckbox.checked; + rv.primordialsPass = this.primordialsCheckbox.checked; + } + else { + const config = MembranePanel.cachedConfig; + const lastGraph = Array.isArray(config.graphs) ? config.graphs[graphIndex] : null; + if (lastGraph) { + rv.passThroughSource = lastGraph.passThroughSource; + rv.passThroughEnabled = lastGraph.passThroughEnabled; + rv.primordialsPass = lastGraph.primordialsPass; + } + } + + this.distortionMaps.forEach(function(dm) { + let d = {}, keys = Reflect.ownKeys(dm); + keys.forEach(function(key) { + if (key === "about") { + d[key] = dm[key]; + return; + } + d[key] = dm[key].exportJSON(); + }); + + rv.distortions.push(d); + }); + + return rv; +}; + +ObjectGraphManager.prototype.setGraphName = function(name, jsonName, isSymbol) { this.groupLabel.firstChild.nodeValue = name; if (!this.groupLabel.parentNode) { OuterGridManager.addGraphUI( @@ -41,10 +95,6 @@ ObjectGraphManager.prototype.setGraphName = function(name) { } }; -ObjectGraphManager.prototype.readDistortionsData = function(data) { - -}; - ObjectGraphManager.prototype.remove = function() { }; @@ -88,6 +138,32 @@ ObjectGraphManager.prototype.handlePassThrough = function(event) { CodeMirrorManager.setEditorEnabled(this.passThroughEditor, checked); }; +ObjectGraphManager.prototype.getPassThrough = function(fullSource = false) { + if (!this.passThroughCheckbox.checked) + return null; + + if (!this.passThroughEditor) { + const config = MembranePanel.cachedConfig; + if (Array.isArray(config.graphs)) { + let index = OuterGridManager.graphNamesCache.controllers.indexOf(this); + if (config.graphs[index]) + return config.graphs[index].passThrough; + } + return null; + } + + if (!fullSource) { + let lines = this.passThroughEditor.getValue().split("\n"); + lines = lines.slice(2, -6); + return lines.join("\n"); + } + + let value = this.passThroughEditor.getValue(); + value = value.substr(value.indexOf("(")); + value = value.replace(/;\n$/, ",\n"); + return value; +}; + ObjectGraphManager.prototype.handleEvent = function(event) { if (event.target === this.primordialsCheckbox) return this.handlePrimordials(event); diff --git a/docs/gui/mainPanels/load.js b/docs/gui/mainPanels/load.js index 08a8971f..0d69ef63 100644 --- a/docs/gui/mainPanels/load.js +++ b/docs/gui/mainPanels/load.js @@ -32,9 +32,8 @@ window.LoadPanel = { } }, - validateDistortions: - function(instructions, index, graphIndex, graphsTotal) { - const errorPrefix = `config.distortionsByGraph[${graphIndex}][${index}]`; + validateDistortions: function(instructions, index, graphIndex) { + const errorPrefix = `config.graphs[${graphIndex}].distortions[${index}]`; function requireType(field, type) { if (typeof instructions[field] !== type) throw new Error(`${errorPrefix}.${field} must be of type ${type}`); @@ -49,22 +48,6 @@ window.LoadPanel = { requireType("rules", "object"); // XXX ajvincent We're not going to attempt parsing instructions.source now. - - { - const max = graphsTotal - 1; - if (!Number.isInteger(instructions.sourceGraphIndex) || - (instructions.sourceGraphIndex < 0) || - (instructions.sourceGraphIndex >= graphsTotal)) - throw new Error( - `${errorPrefix}.sourceGraphIndex must be an integer from 0 to ${max}` - ); - } - - if (instructions.sourceGraphIndex === graphIndex) - throw new Error( - `${errorPrefix}.sourceGraphIndex cannot be the target graph index ${graphIndex}` - ); - const rulesMembers = ["value"]; if (instructions.isFunction) { /* @@ -135,15 +118,14 @@ window.LoadPanel = { } var config = { - commonFiles: [], - passThrough: null, - graphNames: [], - graphSymbolLists: [], - distortionsByGraph: [], + "configurationSetup": {}, + "membrane": {}, + "graphs": [] }; if (!this.configFileInput.files.length && ( !this.testMode || !this.testMode.configSource)) return config; + try { { let p, jsonAsText; @@ -161,55 +143,33 @@ window.LoadPanel = { // Validate the configuration. { - if (!Array.isArray(config.graphNames)) - throw new Error("config.graphNames must be an array of strings"); - - if (!Array.isArray(config.graphSymbolLists) || - !config.graphSymbolLists.every(function(key, index) { - let rv = (Number.isInteger(key) && (0 <= key) && - (key < config.graphNames.length)); - if (rv && (index > 0)) - rv = key > config.graphSymbolLists[index - 1]; - return rv; - })) - throw new Error( - "config.graphSymbolLists must be an ordered array of unique " + - "non-negative integers, each member of which is less than " + - "config.graphNames.length" - ); + if (!Array.isArray(config.graphs)) + throw new Error("config.graphs must be an array of objects"); let stringKeys = new Set(); - config.graphNames.forEach((key, index) => { - if (typeof key !== "string") - throw new Error("config.graphNames must be an array of strings"); - if (!(config.graphSymbolLists.includes(index))) { - if (stringKeys.has(key)) { + config.graphs.forEach((graph, graphIndex) => { + if (typeof graph.name !== "string") + throw new Error(`config.graphs[${graphIndex}].name must be a string`); + if (typeof graph.isSymbol !== "boolean") + throw new Error(`config.graphs[${graphIndex}].isSymbol must be a boolean`); + if (!graph.isSymbol) { + if (stringKeys.has(graph.name)) { throw new Error( - `config.graphNames[${index}] = "${key}", ` + - "but this string name appears earlier in config.graphNames" + `config.graphs[${graphIndex}].name = "${graph.name}", ` + + "but this name appears earlier in config.graphs, and neither name is a symbol" ); } - stringKeys.add(key); + stringKeys.add(graph.name); } - }); - if (!Array.isArray(config.distortionsByGraph) || - (config.distortionsByGraph.length != config.graphNames.length) || - !config.distortionsByGraph.every(Array.isArray)) - { - throw new Error( - `config.distortionsByGraph must be an array with length ` + - config.graphNames.length + ` of arrays` - ); - } + if (!Array.isArray(graph.distortions)) { + throw new Error(`config.graphs[${graphIndex}].distortions must be an array`); + } - config.distortionsByGraph.forEach(function(items, graphIndex) { - items.forEach(function(item, index) { - this.validateDistortions( - item, index, graphIndex, config.graphNames.length - ); + graph.distortions.forEach(function(item, index) { + this.validateDistortions(item, index, graphIndex); }, this); - }, this); + }); } HandlerNames.importConfig(config); diff --git a/docs/gui/mainPanels/membrane.js b/docs/gui/mainPanels/membrane.js index fa711bde..2d79f62f 100644 --- a/docs/gui/mainPanels/membrane.js +++ b/docs/gui/mainPanels/membrane.js @@ -45,9 +45,13 @@ window.MembranePanel = { return this.handlePassThrough(event); }, - getPassThrough: function() { - if (!this.passThroughCheckbox.checked) - return null; + getPassThrough: function(fullSource = false) { + if (!fullSource) { + let lines = this.passThroughEditor.getValue().split("\n"); + lines = lines.slice(2, -6); + return lines.join("\n"); + } + let value = this.passThroughEditor.getValue(); value = value.substr(value.indexOf("(")); value = value.replace(/;\n$/, ",\n"); diff --git a/docs/gui/mainPanels/output.js b/docs/gui/mainPanels/output.js index 039e62cf..78e0b4db 100644 --- a/docs/gui/mainPanels/output.js +++ b/docs/gui/mainPanels/output.js @@ -68,25 +68,32 @@ const OutputPanel = window.OutputPanel = { for (let i = 0; i < fileList.length; i++) commonFiles.push(fileList[i].name); } - const PassThroughSource = MembranePanel.getPassThrough(); - - var [graphNames, graphSymbolLists] = HandlerNames.serializableNames(); /************************************************************************** - * Step 2: Get the DistortionsRules' configurations. * + * Step 2: Generate Distortions GUI JSON file. * **************************************************************************/ - const distortionsData = DistortionsGUI.metadataInGraphOrder(); + const guiConfigAsJSON = { + "configurationSetup": { + "commonFiles": commonFiles, + "formatVersion": 1.0, + "lastUpdated": (new Date()).toUTCString(), + }, + "membrane": { + "passThroughSource": MembranePanel.getPassThrough(), + "passThroughEnabled": MembranePanel.passThroughCheckbox.checked, + "primordialsPass": MembranePanel.primordialsCheckbox.checked, + }, + + "graphs": [] + }; - /************************************************************************** - * Step 3: Generate Distortions GUI JSON file. * - **************************************************************************/ - const guiConfig = JSON.stringify({ - "commonFiles": commonFiles, - "passThrough": PassThroughSource, - "graphNames": graphNames, - "graphSymbolLists": graphSymbolLists, - "distortionsByGraph": distortionsData, - }, null, 2) + "\n"; + { + const controllers = OuterGridManager.graphNamesCache.controllers; + controllers.forEach(function(c, i) { + guiConfigAsJSON.graphs.push(c.exportJSON(i)); + }); + } + const guiConfig = JSON.stringify(guiConfigAsJSON, null, 2) + "\n"; this.configEditor.setValue(guiConfig); { @@ -96,40 +103,56 @@ const OutputPanel = window.OutputPanel = { } /************************************************************************** - * Step 4: Generate Membrane crafting JavaScript file. * + * Step 3: Generate Membrane crafting JavaScript file. * **************************************************************************/ - var script = `function buildMembrane(utilities) { + const PassThroughSource = MembranePanel.getPassThrough(true); + const formattedNames = HandlerNames.getFormattedNames(); + var script = `function buildMembrane(___utilities___) { "use strict"; - const devMembrane = new Membrane({ - logger: (utilities.logger || null)`; + const rvMembrane = new Membrane({ + logger: (___utilities___.logger || null)`; if (PassThroughSource) { script += `, passThrough: ${PassThroughSource.replace(/\n/gm, "\n ")}`; } script += ` }); - const graphNames = [\n ${HandlerNames.getFormattedNames().join(",\n ")}\n ]; - const graphs = graphNames.map(function(name) { - return devMembrane.getHandlerByName(name, true); - });\n\n`; - distortionsData.forEach(function(dataArray, graphIndex) { - if (dataArray.length === 0) - return; - const nl = "\n "; - script += ` {\n const rules = devMembrane.modifyRules.createDistortionsListener();\n`; - dataArray.forEach(function(data) { - script += `${nl}rules.addListener(${data.name}, "value", `; - script += JSON.stringify(data.rules.value, null, 2).replace(/\n/gm, nl) + `);\n`; - if (!("proto" in data.rules)) - return; - script += `${nl}rules.addListener(${data.name}, "proto", `; - script += JSON.stringify(data.rules.proto, null, 2).replace(/\n/gm, nl) + `);\n`; + +`; + + { + const controllers = OuterGridManager.graphNamesCache.controllers; + controllers.forEach(function(c, index) { + const graph = guiConfigAsJSON.graphs[index]; + + script += ` {\n `; + if (graph.distortions.length || c.passThroughCheckbox.checked) + script += `const ___graph___ = `; + script += `rvMembrane.getHandlerByName(${formattedNames[index]}, { mustCreate: true });\n`; + + if (c.passThroughCheckbox.checked) { + script += ` ___graph___.passThroughFilter = ${c.getPassThrough(true)};\n`; + } + + if (graph.distortions.length) { + script += ` const ___listener___ = rvMembrane.modifyRules.createDistortionsListener();\n`; + graph.distortions.forEach(function(d) { + const keys = Reflect.ownKeys(d); + keys.splice(keys.indexOf("about"), 1); + keys.forEach(function(k) { + const distortionAsJSON = JSON.stringify(d[k], null, 2).replace(/\n/gm, "\n "); + script += ` ___listener___.addListener(${d.about.valueName}, "${k}", ${distortionAsJSON});\n\n`; + }); + }); + script += ` ___listener___.bindToHandler(___graph___);\n`; + } + + script += ` }\n\n`; }); - script += `${nl}rules.bindToHandler(graphs[${graphIndex}]);\n }\n\n`; - }); + } - script += " return devMembrane;\n}\n"; + script += " return rvMembrane;\n}\n"; this.jsEditor.setValue(script); { diff --git a/docs/gui/other.css b/docs/gui/other.css index 93b8026b..b3ce68b6 100644 --- a/docs/gui/other.css +++ b/docs/gui/other.css @@ -15,3 +15,8 @@ *.CodeMirror.disabled { opacity: 0.3; } + +h2 { + margin-top: 0; + margin-bottom: 0; +} diff --git a/docs/gui/specification.html b/docs/gui/specification.html index 7c0f8035..d45a78c2 100644 --- a/docs/gui/specification.html +++ b/docs/gui/specification.html @@ -48,7 +48,7 @@