/* Q Light Controller Plus devtool.js Copyright (c) Heikki Junnila Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.txt Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ var devtool = Object; devtool.gridwidth = null; devtool.gridheight = null; devtool.gridsize = null; devtool.currentStep = 0; devtool.testTimer = null; devtool.alternate = false; devtool.initDefinitions = function() { document.getElementById("apiversion").value = testAlgo.apiVersion; document.getElementById("name").value = testAlgo.name; document.getElementById("author").value = testAlgo.author; if (typeof testAlgo.acceptColors !== "undefined") { document.getElementById("acceptColors").value = testAlgo.acceptColors; } else { document.getElementById("acceptColors").value = "2 (Default)"; } } devtool.writeSelectOptions = function(item) { var opt = document.createElement("option"); var t = document.createTextNode(item); if (this.currentValue === item) { opt.selected = "selected"; } opt.setAttribute("value", item); opt.appendChild(t); this.inputElement.appendChild(opt); } devtool.addPropertyTableEntry = function(property) { var table = document.getElementById("properties"); var row = table.insertRow(-1); var i = 0; var keys = new Array(); var input; for (i = 0; i < property.length; i++) { keys.push(property[i][0]); } var name = ""; if (keys.indexOf("name") >= 0) { name = property[keys.indexOf("name")][1]; } var displayName = name; if (keys.indexOf("display") >= 0) { displayName = property[keys.indexOf("display")][1]; } var typeProperty = "string"; if (keys.indexOf("type") >= 0) { // list: defines a list of strings that will be displayed by the QLC+ RGB Matrix Editor // range: defined a range of integer values that this property can handle // integer: an integer value that QLC+ can exchange with the script // string: a string that QLC+ can exchange with the script typeProperty = property[keys.indexOf("type")][1]; } var values = Array(0, 0); if (keys.indexOf("values") >= 0) { values = property[keys.indexOf("values")][1].split(","); } var writeFunction = property[keys.indexOf("write")][1]; if (name !== "") { var storedValue = localStorage.getItem(name); if (storedValue !== null) { window.testAlgo[writeFunction](storedValue); } } var readFunction = property[keys.indexOf("read")][1]; var nameCell = row.insertCell(-1); var t = document.createTextNode(displayName); nameCell.appendChild(t); var currentValue = window.testAlgo[readFunction](); var formCell = row.insertCell(-1); if (typeProperty === "list") { input = document.createElement("select"); input.name = name; input.id = name; input.setAttribute("onChange", "devtool.writeFunction('" + writeFunction + "', '" + name + "', this.value); devtool.setStep(0); devtool.writeCurrentStep()"); var selectOption = new Object(); selectOption.currentValue = currentValue; selectOption.readFunction = readFunction; selectOption.inputElement = input; values.forEach(devtool.writeSelectOptions, selectOption); formCell.appendChild(input); } else if (typeProperty === "range") { input = document.createElement("input"); input.type = "number"; input.required = "required"; input.name = name; input.setAttribute("value", currentValue); input.id = name; input.min = values[0]; input.max = values[1]; input.setAttribute("onChange", "devtool.writeFunction('" + writeFunction + "', '" + name + "', this.value); devtool.setStep(0); devtool.writeCurrentStep()"); formCell.appendChild(input); } else if (typeProperty === "float") { input = document.createElement("input"); input.type = "number"; input.required = "required"; input.name = name; input.setAttribute("value", currentValue); input.id = name; input.min = -1000000; input.max = 1000000; input.step = 0.001; input.setAttribute("onChange", "devtool.writeFunction('" + writeFunction + "', '" + name + "', this.value); devtool.setStep(0); devtool.writeCurrentStep()"); formCell.appendChild(input); } else { // string input = document.createElement("input"); input.type = "text"; input.name = name; input.setAttribute("value", currentValue); input.id = name; input.setAttribute("onChange", "devtool.writeFunction('" + writeFunction + "', '" + name + "', this.value); devtool.setStep(0); devtool.writeCurrentStep()"); formCell.appendChild(input); } } devtool.initProperties = function() { var table = document.getElementById("properties"); var properties = Array(); var property = Array(); var i = 0; var entry = 0; // Cleanup the table before updating its contents for (i = table.rows.length - 1; i >= 0; i--) { table.deleteRow(i); } // Algo properties not supported by versions prior to 2. if (testAlgo.apiVersion < 2) { return; } // Get the properties for (i = 0; i < testAlgo.properties.length; i++) { var propDef = testAlgo.properties[i]; var propKeyValue = propDef.split("|"); property = Array(); for (entry = 0; entry < propKeyValue.length; entry++) { var keyValue = propKeyValue[entry].split(":"); var key = keyValue[0]; keyValue.shift(); property.push(Array(key, keyValue.join(":"))); } properties.push(property); } // Write the properties properties.forEach(devtool.addPropertyTableEntry); } devtool.initPixelColors = function() { var pixelColorChooser = document.getElementById("pixelColorChooser"); pixelColorChooser.hidden = testAlgo.acceptColors === 0; var secondaryColorChooser = document.getElementById("secondaryColorChooser"); secondaryColorChooser.hidden = testAlgo.acceptColors === 1; } devtool.initSpeedValue = function() { var speed = localStorage.getItem("devtool.speed"); if (speed === null) { speed = 500; } document.getElementById("speed").value = speed; } devtool.initAlternateValue = function() { var alternate = localStorage.getItem("devtool.alternate"); if (alternate === true) { alternate = true; } else { alternate = false; } document.getElementById("alternate").checked = alternate; } devtool.initColorValues = function() { var primary = localStorage.getItem("devtool.primaryColor"); if (primary === null || Number.isNaN(parseInt("0x" + primary, 16))) { primary = "ff0000"; } primary = primary.padStart(6,"0"); document.getElementById("primaryColorText").value = primary; document.getElementById("primaryColorPicker").value = "#" + primary; var secondary = localStorage.getItem("devtool.secondaryColor"); if (secondary === null || secondary === "" || Number.isNaN(parseInt("0x" + secondary, 16))) { document.getElementById("secondaryColorText").value = ""; document.getElementById("secondaryColorPicker").value = "#000000"; } else { secondary = secondary.padStart(6,"0"); document.getElementById("secondaryColorText").value = secondary; document.getElementById("secondaryColorPicker").value = "#" + secondary; } } devtool.initGridSize = function() { devtool.gridwidth = localStorage.getItem("devtool.gridwidth"); if (devtool.gridwidth === null) { devtool.gridwidth = 15; } document.getElementById("gridwidth").value = devtool.gridwidth; devtool.gridheight = localStorage.getItem("devtool.gridheight"); if (devtool.gridheight === null) { devtool.gridheight = 15; } document.getElementById("gridheight").value = devtool.gridheight; devtool.gridsize = localStorage.getItem("devtool.gridsize"); if (devtool.gridsize === null) { devtool.gridsize = 20; } document.getElementById("gridsize").value = devtool.gridsize; } devtool.getRgbFromColorInt = function(color) { var red = color >> 16; var green = (color >> 8) - red * 256; var blue = color - red * 256 * 256 - green * 256; return [red, green, blue]; } devtool.getCurrentColorInt = function() { var primaryColorInput = document.getElementById("primaryColorText"); var primaryColor = parseInt(primaryColorInput.value, 16); var secondaryColorInput = document.getElementById("secondaryColorText"); var secondaryColor = parseInt(secondaryColorInput.value, 16); if (testAlgo.acceptColors === 0 || Number.isNaN(primaryColor)) { return null; } if (testAlgo.acceptColors === 1 || Number.isNaN(secondaryColor) || devtool.stepCount() <= 1) { return primaryColor; } var primaryColorRgb = devtool.getRgbFromColorInt(primaryColor); var secondaryColorRgb = devtool.getRgbFromColorInt(secondaryColor); var primaryFactor = (devtool.stepCount() - devtool.currentStep - 1) / (devtool.stepCount() - 1); var secondaryFactor = devtool.currentStep / (devtool.stepCount() - 1); var red = Math.round(primaryColorRgb[0] * primaryFactor + secondaryColorRgb[0] * secondaryFactor); var green = Math.round(primaryColorRgb[1] * primaryFactor + secondaryColorRgb[1] * secondaryFactor); var blue = Math.round(primaryColorRgb[2] * primaryFactor + secondaryColorRgb[2] * secondaryFactor); return red * 256 * 256 + green * 256 + blue; } devtool.writeCurrentStep = function() { devtool.currentStep = parseInt(document.getElementById("currentStep").value); // currentStep may have been changed manually var map = document.getElementById("map"); for (var i = map.rows.length - 1; i >= 0; i--) { map.deleteRow(i); } var rgb = testAlgo.rgbMap(devtool.gridwidth, devtool.gridheight, devtool.getCurrentColorInt(), devtool.currentStep); for (var y = 0; y < devtool.gridheight; y++) { var row = map.insertRow(y); for (var x = 0; x < devtool.gridwidth; x++) { var cell = row.insertCell(x); var rgbStr = rgb[y][x].toString(16); while (rgbStr.length !== 6) { rgbStr = "0" + rgbStr; } rgbStr = "#" + rgbStr; cell.style.backgroundColor = rgbStr; cell.style.height = devtool.gridsize + "px"; cell.style.width = devtool.gridsize + "px"; cell.title = "(" + x + ", " + y + "): " + rgbStr + " – " + cell.style.backgroundColor; // rgbStr will be #rrggbb whereas the cell style will be rgb(255, 255, 255) } } } devtool.setStep = function(step) { devtool.currentStep = step; document.getElementById("currentStep").value = devtool.currentStep; devtool.writeCurrentStep(); } // Do not cache this value - the program also doesn't // and there are scripts changing this number on any parameter change devtool.stepCount = function() { return testAlgo.rgbMapStepCount(devtool.gridwidth, devtool.gridheight); } devtool.updateStepCount = function() { document.getElementById("stepCount").value = devtool.stepCount(); document.getElementById("currentStep").max = devtool.stepCount() - 1; } devtool.onGridSizeUpdated = function() { devtool.gridwidth = parseInt(document.getElementById("gridwidth").value); localStorage.setItem("devtool.gridwidth", devtool.gridwidth); devtool.gridheight = parseInt(document.getElementById("gridheight").value); localStorage.setItem("devtool.gridheight", devtool.gridheight); devtool.gridsize = parseInt(document.getElementById("gridsize").value); localStorage.setItem("devtool.gridsize", devtool.gridsize); devtool.updateStepCount(); devtool.writeCurrentStep(); } devtool.onColorTextChange = function() { var primary = parseInt("0x" + document.getElementById("primaryColorText").value).toString(16); localStorage.setItem("devtool.primaryColor", primary); var secondary = parseInt("0x" + document.getElementById("secondaryColorText").value).toString(16); if(primary === "NaN"){ document.getElementById("primaryColorPicker").value = "#000000"; } else { document.getElementById("primaryColorPicker").value = "#" + primary.padStart(6,"0");; } if (secondary === "NaN") { // Evaluation of the string. document.getElementById("secondaryColorPicker").value = "#000000"; localStorage.setItem("devtool.secondaryColor", ""); } else { document.getElementById("secondaryColorPicker").value = "#" + secondary.padStart(6,"0");; localStorage.setItem("devtool.secondaryColor", secondary); } devtool.writeCurrentStep(); } devtool.onPrimaryColorPickerChange = function() { document.getElementById("primaryColorText").value = document.getElementById("primaryColorPicker").value.substring(1); devtool.onColorTextChange(); } devtool.onSecondaryColorPickerChange = function() { document.getElementById("secondaryColorText").value = document.getElementById("secondaryColorPicker").value.substring(1); devtool.onColorTextChange(); } devtool.startTest = function(inc) { var speed = document.getElementById("speed").value; window.clearInterval(devtool.testTimer); // avoid multiple timers running simultaneously if (inc > 0) { devtool.testTimer = window.setInterval("devtool.nextStep()", speed); } else { devtool.testTimer = window.setInterval("devtool.previousStep()", speed); } localStorage.setItem("devtool.timerRunning", inc); } devtool.stopTest = function() { window.clearInterval(devtool.testTimer); localStorage.setItem("devtool.timerRunning", 0); } devtool.initTestStatus = function() { let timerStatus = localStorage.getItem("devtool.timerRunning"); if (timerStatus === null || parseInt(timerStatus) !== 0) { devtool.startTest(parseInt(timerStatus)); } } devtool.init = function() { if (typeof testAlgo === "undefined") { return; } devtool.initDefinitions(); devtool.initGridSize(); devtool.setStep(0); devtool.initSpeedValue(); devtool.initColorValues(); devtool.initProperties(); devtool.initPixelColors(); devtool.onGridSizeUpdated(); devtool.writeCurrentStep(); devtool.initTestStatus(); } devtool.handleLoadError = function() { return; } devtool.onFilenameUpdated = function(filename) { if (filename === "") { return; } localStorage.setItem("devtool.filename", filename); var script = document.getElementById("algoScript"); if (script === null) { script = document.createElement("script"); script.id = "algoScript"; script.addEventListener("load", () => devtool.init(), false); script.addEventListener("error", () => devtool.handleLoadError(), false); script.type = "text/javascript"; script.src = filename; document.head.appendChild(script); } else { script.src = filename; } // devtool.init(); // is called onload. } devtool.loadAlgoFile = function() { var storedValue = localStorage.getItem("devtool.filename"); if (storedValue === null) { storedValue = "evenodd.js"; } document.getElementById("filename").value = storedValue; devtool.onFilenameUpdated(storedValue); } devtool.writeFunction = function(functionName, propertyName, value) { window.testAlgo[functionName](value); localStorage.setItem(propertyName, value); devtool.updateStepCount(); } devtool.onAlternateChanged = function() { var alternate = document.getElementById("alternate").checked; localStorage.setItem("devtool.alternate", alternate); devtool.alternate = alternate; } devtool.onSpeedChanged = function() { var speed = document.getElementById("speed").value; localStorage.setItem("devtool.speed", speed); devtool.initTestStatus(); } devtool.nextStep = function() { if (devtool.currentStep + 1 < devtool.stepCount()) { devtool.setStep(devtool.currentStep + 1); } else { let timerStatus = localStorage.getItem("devtool.timerRunning"); let alternate = document.getElementById("alternate").checked; if (timerStatus == "1" && alternate) { devtool.startTest(-1); } else { devtool.setStep(0); } } } devtool.previousStep = function() { if (devtool.currentStep > 0 && (devtool.currentStep - 1) < devtool.stepCount()) { devtool.setStep(devtool.currentStep - 1); } else { let timerStatus = localStorage.getItem("devtool.timerRunning"); let alternate = document.getElementById("alternate").checked; if (timerStatus == "-1" && alternate) { devtool.startTest(1); } else { devtool.setStep(devtool.stepCount() - 1); // last step } } }