/* global window, document, print, alert, console,setTimeout, clearTimeout, _ $ */ /** UI Builder V1.0 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 RADIAN = Math.PI/180; function HifiEntityUI(parent, structure){ this.parent = parent; this.structure = structure; var self = this; this.webBridgeSync = _.debounce(function(id, val){ if (self.EventBridge){ var sendPackage = {}; sendPackage[id] = val; var message = { messageType: "settings_update", updatedSettings: sendPackage }; self.EventBridge.emitWebEvent(JSON.stringify(message)); } }, 125); } HifiEntityUI.prototype = { 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 console.log(data); var currentProperties = data.currentProperties; // Do expected property match with structure; Object.keys(currentProperties).forEach(function(value, index) { 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 / RADIAN; 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 = property.red; blue.value = property.blue; green.value = property.green; red.oninput({target: red}); // crashes here. } else if (field.className.indexOf("xyz")) { var x = document.getElementById(value+"-x"); var y = document.getElementById(value+"-y"); var z = document.getElementById(value+"-z"); // crashes here. if (value === "emitOrientation") { } else { x.value = property.x; y.value = property.y; z.value = property.z; } } } } }); } else if (data.messageType === 'particle_close') { // Legacy event on particle close. This webview actually gets removed now } }); }, 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) { 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.className = sectionDivBody.className .replace("visible", "") .replace(/\s{2,}/g, " "); sectionDivBody.style.maxHeight = "0px"; } else { sectionDivBody.className += " visible"; sectionDivBody.style.maxHeight = height; } sectionDivHeader.onclick = function( ) { collapsed = !collapsed; if (collapsed) { sectionDivBody.className = sectionDivBody.className .replace("visible", "") .replace(/\s{2,}/g, " "); sectionDivBody.style.maxHeight = "0px"; } else { sectionDivBody.className += " visible"; sectionDivBody.style.maxHeight = height; } // 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){ var self = this; var inputs = ["x","y","z"]; var domArray = []; parent.id = group.id; for (var index in inputs) { var element = document.createElement("input"); if (group.defaultColor) { element.value = group.defaultColor[inputs[index]]; } else if (inputs[index] === "red"){ element.value = 255; } else { element.value = 0; } element.setAttribute("type","number"); element.className = inputs[index]; element.id = group.id + "-" + inputs[index]; 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); parent.className += " property vector-section xyz"; // 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); }, addColorPicker: function(parent, group) { var self = this; var $colPickContainer = $('