/* global window, document, print, alert, console,setTimeout, clearTimeout, _ $ */ /* eslint no-console: 0 */ /** UI Builder V1.0 Created by Matti 'Menithal' Lahtinen 24/5/2017 Copyright 2017 High Fidelity, Inc. This can eventually be expanded to all of Edit, for now, starting with Particles Only. This is created for the sole purpose of streamliming the bridge, and to simplify the logic between an inputfield in WebView and Entities in High Fidelity. We also do not need anything as heavy as jquery or any other platform, as we are mostly only building for QT (while, all the other JS frameworks usually do alot of polyfilling) Available Types: JSONInputField - Accepts JSON input, once one presses Save, it will be propegated. Button- A Button that listens for a custom event as defined by callback Boolean - Creates a checkbox that the user can either check or uncheck SliderFloat - Creates a slider (with input) that has Float values from min to max. Default is min 0, max 1 SliderInteger - Creates a slider (with input) that has a Integer value from min to max. Default is min 1, max 10000 SliderRadian - Creates a slider (with input) that has Float values in degrees, that are converted to radians. default is min 0, max Math.PI. Texture - Creates a Image with an url input field that points to texture. If image cannot form, show "cannot find image" VecQuaternion - Creates a 3D Vector field that converts to quaternions. Checkbox exists to show quaternions instead. Color - Create field color button, that when pressed, opens the color picker. Vector - Create a 3D Vector field that has one to one correspondence. The script will use this structure to build a UI that is connected The id fields within High Fidelity This should make editing, and everything related much more simpler to maintain, and If there is any changes to either the Entities or properties of **/ var RADIANS_PER_DEGREE = Math.PI / 180; var roundFloat = function (input, round) { round = round ? round : 1000; var sanitizedInput; if (typeof input === "string") { sanitizedInput = parseFloat(input); } else { sanitizedInput = input; } return Math.round(sanitizedInput * round) / round; }; function HifiEntityUI(parent) { this.parent = parent; var self = this; this.webBridgeSync = _.debounce(function (id, val) { if (self.EventBridge) { var sendPackage = {}; sendPackage[id] = val; self.submitChanges(sendPackage); } }, 125); } HifiEntityUI.prototype = { setOnSelect: function (callback) { this.onSelect = callback; }, submitChanges: function (structure) { var message = { messageType: "settings_update", updatedSettings: structure }; this.EventBridge.emitWebEvent(JSON.stringify(message)); }, setUI: function (structure) { this.structure = structure; }, disableFields: function () { var fields = document.getElementsByTagName("input"); for (var i = 0; i < fields.length; i++) { if (fields[i].getAttribute("type") !== "button") { fields[i].value = ""; } fields[i].setAttribute("disabled", true); } var textures = document.getElementsByTagName("img"); for (i = 0; i < textures.length; i++) { textures[i].src = ""; } textures = document.getElementsByClassName("with-texture"); for (i = 0; i < textures.length; i++) { textures[i].classList.remove("with-textures"); textures[i].classList.add("no-texture"); } var textareas = document.getElementsByTagName("textarea"); for (var x = 0; x < textareas.length; x++) { textareas[x].remove(); } }, getSettings: function () { var self = this; var json = {}; var keys = Object.keys(self.builtRows); for (var i = 0; i < keys.length; i++) { var key = keys[i]; var el = self.builtRows[key]; if (el.className.indexOf("checkbox") !== -1) { json[key] = document.getElementById(key) .checked ? true : false; } else if (el.className.indexOf("vector-section") !== -1) { var vector = {}; if (el.className.indexOf("rgb") !== -1) { var red = document.getElementById(key + "-red"); var blue = document.getElementById(key + "-blue"); var green = document.getElementById(key + "-green"); vector.red = red.value; vector.blue = blue.value; vector.green = green.value; } else if (el.className.indexOf("pyr") !== -1) { var p = document.getElementById(key + "-Pitch"); var y = document.getElementById(key + "-Yaw"); var r = document.getElementById(key + "-Roll"); vector.x = p.value; vector.y = y.value; vector.z = r.value; } else { var x = document.getElementById(key + "-x"); var ey = document.getElementById(key + "-y"); var z = document.getElementById(key + "-z"); vector.x = x.value; vector.y = ey.value; vector.z = z.value; } json[key] = vector; } else if (el.className.length > 0) { json[key] = document.getElementById(key) .value; } } return json; }, fillFields: function (currentProperties) { var self = this; var fields = document.getElementsByTagName("input"); if (!currentProperties.locked) { for (var i = 0; i < fields.length; i++) { fields[i].removeAttribute("disabled"); } } if (self.onSelect) { self.onSelect(); } var keys = Object.keys(currentProperties); for (var e in keys) { if (keys.hasOwnProperty(e)) { var value = keys[e]; var property = currentProperties[value]; var field = self.builtRows[value]; if (field) { var el = document.getElementById(value); if (field.className.indexOf("radian") !== -1) { el.value = property / RADIANS_PER_DEGREE; el.onchange({ target: el }); } else if (field.className.indexOf("range") !== -1 || field.className.indexOf("texture") !== -1) { el.value = property; el.onchange({ target: el }); } else if (field.className.indexOf("checkbox") !== -1) { if (property) { el.setAttribute("checked", property); } else { el.removeAttribute("checked"); } } else if (field.className.indexOf("vector-section") !== -1) { if (field.className.indexOf("rgb") !== -1) { var red = document.getElementById(value + "-red"); var blue = document.getElementById(value + "-blue"); var green = document.getElementById(value + "-green"); red.value = parseInt(property.red); blue.value = parseInt(property.blue); green.value = parseInt(property.green); red.oninput({ target: red }); } else if (field.className.indexOf("xyz") !== -1) { var x = document.getElementById(value + "-x"); var y = document.getElementById(value + "-y"); var z = document.getElementById(value + "-z"); x.value = roundFloat(property.x, 100); y.value = roundFloat(property.y, 100); z.value = roundFloat(property.z, 100); } else if (field.className.indexOf("pyr") !== -1) { var pitch = document.getElementById(value + "-Pitch"); var yaw = document.getElementById(value + "-Yaw"); var roll = document.getElementById(value + "-Roll"); pitch.value = roundFloat(property.x, 100); yaw.value = roundFloat(property.y, 100); roll.value = roundFloat(property.z, 100); } } } } } }, connect: function (EventBridge) { this.EventBridge = EventBridge; var self = this; EventBridge.emitWebEvent(JSON.stringify({ messageType: 'page_loaded' })); EventBridge.scriptEventReceived.connect(function (data) { data = JSON.parse(data); if (data.messageType === 'particle_settings') { // Update settings var currentProperties = data.currentProperties; self.fillFields(currentProperties); // Do expected property match with structure; } else if (data.messageType === 'particle_close') { self.disableFields(); } }); }, build: function () { var self = this; var sections = Object.keys(this.structure); this.builtRows = {}; sections.forEach(function (section, index) { var properties = self.structure[section]; self.addSection(self.parent, section, properties, index); }); }, addSection: function (parent, section, properties, index) { var self = this; var sectionDivHeader = document.createElement("div"); var title = document.createElement("label"); var dropDown = document.createElement("span"); dropDown.className = "arrow"; sectionDivHeader.className = "section-header"; title.innerHTML = section; sectionDivHeader.appendChild(title); sectionDivHeader.appendChild(dropDown); var collapsed = index !== 0; dropDown.innerHTML = collapsed ? "L" : "M"; sectionDivHeader.setAttribute("collapsed", collapsed); parent.appendChild(sectionDivHeader); var sectionDivBody = document.createElement("div"); sectionDivBody.className = "property-group"; var animationWrapper = document.createElement("div"); animationWrapper.className = "section-wrap"; for (var property in properties) { if (properties.hasOwnProperty(property)) { var builtRow = self.addElement(animationWrapper, properties[property]); var id = properties[property].id; if (id) { self.builtRows[id] = builtRow; } } } sectionDivBody.appendChild(animationWrapper); parent.appendChild(sectionDivBody); _.defer(function () { var height = (animationWrapper.clientHeight) + "px"; if (collapsed) { sectionDivBody.classList.remove("visible"); sectionDivBody.style.maxHeight = "0px"; } else { sectionDivBody.classList.add("visible"); sectionDivBody.style.maxHeight = height; } sectionDivHeader.onclick = function () { collapsed = !collapsed; if (collapsed) { sectionDivBody.classList.remove("visible"); sectionDivBody.style.maxHeight = "0px"; } else { sectionDivBody.classList.add("visible"); sectionDivBody.style.maxHeight = (animationWrapper.clientHeight) + "px"; } // sectionDivBody.style.display = collapsed ? "none": "block"; dropDown.innerHTML = collapsed ? "L" : "M"; sectionDivHeader.setAttribute("collapsed", collapsed); }; }); }, addLabel: function (parent, group) { var label = document.createElement("label"); label.innerHTML = group.name; parent.appendChild(label); if (group.unit) { var span = document.createElement("span"); span.innerHTML = group.unit; span.className = "unit"; label.appendChild(span); } return label; }, addVector: function (parent, group, labels, domArray) { var self = this; var inputs = labels ? labels : ["x", "y", "z"]; domArray = domArray ? domArray : []; parent.id = group.id; for (var index in inputs) { var element = document.createElement("input"); element.setAttribute("type", "number"); element.className = inputs[index]; element.id = group.id + "-" + inputs[index]; if (group.defaultRange) { if (group.defaultRange.min) { element.setAttribute("min", group.defaultRange.min); } if (group.defaultRange.max) { element.setAttribute("max", group.defaultRange.max); } if (group.defaultRange.step) { element.setAttribute("step", group.defaultRange.step); } } if (group.oninput) { element.oninput = group.oninput; } else { element.oninput = function (event) { self.webBridgeSync(group.id, { x: domArray[0].value, y: domArray[1].value, z: domArray[2].value }); }; } element.onchange = element.oninput; domArray.push(element); } this.addLabel(parent, group); var className = ""; for (var i = 0; i < inputs.length; i++) { className += inputs[i].charAt(0) .toLowerCase(); } parent.className += " property vector-section " + className; // Add Tuple and the rest var tupleContainer = document.createElement("div"); tupleContainer.className = "tuple"; for (var domIndex in domArray) { var container = domArray[domIndex]; var div = document.createElement("div"); var label = document.createElement("label"); label.innerHTML = inputs[domIndex] + ":"; label.setAttribute("for", container.id); div.appendChild(container); div.appendChild(label); tupleContainer.appendChild(div); } parent.appendChild(tupleContainer); }, addVectorQuaternion: function (parent, group) { this.addVector(parent, group, ["Pitch", "Yaw", "Roll"]); }, addColorPicker: function (parent, group) { var self = this; var $colPickContainer = $('