From 50becb5c378e80da26740a2b4702923d71481c37 Mon Sep 17 00:00:00 2001 From: David Back Date: Tue, 16 Oct 2018 12:39:34 -0700 Subject: [PATCH] merge particle explorer into properties, resurrect sliders, other tidying/fixes --- .../resources/qml/hifi/tablet/EditTabView.qml | 20 +- .../qml/hifi/tablet/EditToolsTabView.qml | 23 +- scripts/system/edit.js | 77 +- scripts/system/html/css/edit-style.css | 68 +- scripts/system/html/entityProperties.html | 1 + scripts/system/html/js/entityProperties.js | 1074 ++++++++++++----- scripts/system/html/js/underscore-min.js | 6 + 7 files changed, 863 insertions(+), 406 deletions(-) create mode 100644 scripts/system/html/js/underscore-min.js diff --git a/interface/resources/qml/hifi/tablet/EditTabView.qml b/interface/resources/qml/hifi/tablet/EditTabView.qml index 4ac8755570..bf7dd3e66b 100644 --- a/interface/resources/qml/hifi/tablet/EditTabView.qml +++ b/interface/resources/qml/hifi/tablet/EditTabView.qml @@ -175,7 +175,7 @@ TabBar { method: "newEntityButtonClicked", params: { buttonName: "newParticleButton" } }); - editTabView.currentIndex = 4 + editTabView.currentIndex = 2 } } @@ -279,21 +279,6 @@ TabBar { } } - EditTabButton { - title: "P" - active: true - enabled: true - property string originalUrl: "" - - property Component visualItem: Component { - WebView { - id: particleExplorerWebView - url: Paths.defaultScripts + "/system/particle_explorer/particleExplorer.html" - enabled: true - } - } - } - function fromScript(message) { switch (message.method) { case 'selectTab': @@ -326,9 +311,6 @@ TabBar { case 'grid': editTabView.currentIndex = 3; break; - case 'particle': - editTabView.currentIndex = 4; - break; default: console.warn('Attempt to switch to invalid tab:', id); } diff --git a/interface/resources/qml/hifi/tablet/EditToolsTabView.qml b/interface/resources/qml/hifi/tablet/EditToolsTabView.qml index 00084b8ca9..13b1caf8fb 100644 --- a/interface/resources/qml/hifi/tablet/EditToolsTabView.qml +++ b/interface/resources/qml/hifi/tablet/EditToolsTabView.qml @@ -18,7 +18,6 @@ TabBar { readonly property int create: 0 readonly property int properties: 1 readonly property int grid: 2 - readonly property int particle: 3 } readonly property HifiConstants hifi: HifiConstants {} @@ -182,7 +181,7 @@ TabBar { method: "newEntityButtonClicked", params: { buttonName: "newParticleButton" } }); - editTabView.currentIndex = tabIndex.particle + editTabView.currentIndex = tabIndex.properties } } @@ -271,21 +270,6 @@ TabBar { } } - EditTabButton { - title: "P" - active: true - enabled: true - property string originalUrl: "" - - property Component visualItem: Component { - WebView { - id: particleExplorerWebView - url: Paths.defaultScripts + "/system/particle_explorer/particleExplorer.html" - enabled: true - } - } - } - function fromScript(message) { switch (message.method) { case 'selectTab': @@ -299,7 +283,7 @@ TabBar { // Changes the current tab based on tab index or title as input function selectTab(id) { if (typeof id === 'number') { - if (id >= tabIndex.create && id <= tabIndex.particle) { + if (id >= tabIndex.create && id <= tabIndex.grid) { editTabView.currentIndex = id; } else { console.warn('Attempt to switch to invalid tab:', id); @@ -315,9 +299,6 @@ TabBar { case 'grid': editTabView.currentIndex = tabIndex.grid; break; - case 'particle': - editTabView.currentIndex = tabIndex.particle; - break; default: console.warn('Attempt to switch to invalid tab:', id); } diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 27858722ec..bcfc831d57 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -12,7 +12,7 @@ /* global Script, SelectionDisplay, LightOverlayManager, CameraManager, Grid, GridTool, EntityListTool, Vec3, SelectionManager, Overlays, OverlayWebWindow, UserActivityLogger, Settings, Entities, Tablet, Toolbars, Messages, Menu, Camera, - progressDialog, tooltip, MyAvatar, Quat, Controller, Clipboard, HMD, UndoStack, ParticleExplorerTool, OverlaySystemWindow */ + progressDialog, tooltip, MyAvatar, Quat, Controller, Clipboard, HMD, UndoStack, OverlaySystemWindow */ (function() { // BEGIN LOCAL_SCOPE @@ -32,7 +32,6 @@ Script.include([ "libraries/gridTool.js", "libraries/entityList.js", "libraries/utils.js", - "particle_explorer/particleExplorerTool.js", "libraries/entityIconOverlayManager.js" ]); @@ -109,28 +108,6 @@ var entityListTool = new EntityListTool(shouldUseEditTabletApp); selectionManager.addEventListener(function () { selectionDisplay.updateHandles(); entityIconOverlayManager.updatePositions(); - - // Update particle explorer - var needToDestroyParticleExplorer = false; - if (selectionManager.selections.length === 1) { - var selectedEntityID = selectionManager.selections[0]; - if (selectedEntityID === selectedParticleEntityID) { - return; - } - var type = Entities.getEntityProperties(selectedEntityID, "type").type; - if (type === "ParticleEffect") { - selectParticleEntity(selectedEntityID); - } else { - needToDestroyParticleExplorer = true; - } - } else { - needToDestroyParticleExplorer = true; - } - - if (needToDestroyParticleExplorer && selectedParticleEntityID !== null) { - selectedParticleEntityID = null; - particleExplorerTool.destroyWebView(); - } }); var KEY_P = 80; //Key code for letter p used for Parenting hotkey. @@ -359,10 +336,6 @@ var toolBar = (function () { properties: properties }], [], true); - if (properties.type === "ParticleEffect") { - selectParticleEntity(entityID); - } - var POST_ADJUST_ENTITY_TYPES = ["Model"]; if (POST_ADJUST_ENTITY_TYPES.indexOf(properties.type) !== -1) { // Adjust position of entity per bounding box after it has been created and auto-resized. @@ -1178,13 +1151,6 @@ function mouseClickEvent(event) { orientation = MyAvatar.orientation; intersection = rayPlaneIntersection(pickRay, P, Quat.getForward(orientation)); - if (event.isShifted) { - particleExplorerTool.destroyWebView(); - } - if (properties.type !== "ParticleEffect") { - particleExplorerTool.destroyWebView(); - } - if (!event.isShifted) { selectionManager.setSelections([foundEntity]); } else { @@ -1604,8 +1570,6 @@ function deleteSelectedEntities() { if (SelectionManager.hasSelection()) { var deletedIDs = []; - selectedParticleEntityID = null; - particleExplorerTool.destroyWebView(); SelectionManager.saveProperties(); var savedProperties = []; var newSortedSelection = sortSelectedEntities(selectionManager.selections); @@ -2572,31 +2536,6 @@ propertyMenu.onSelectMenuItem = function (name) { var showMenuItem = propertyMenu.addMenuItem("Show in Marketplace"); var propertiesTool = new PropertiesTool(); -var particleExplorerTool = new ParticleExplorerTool(createToolsWindow); -var selectedParticleEntityID = null; - -function selectParticleEntity(entityID) { - selectedParticleEntityID = entityID; - - var properties = Entities.getEntityProperties(entityID); - if (properties.emitOrientation) { - properties.emitOrientation = Quat.safeEulerAngles(properties.emitOrientation); - } - - particleExplorerTool.destroyWebView(); - particleExplorerTool.createWebView(); - - particleExplorerTool.setActiveParticleEntity(entityID); - - // Switch to particle explorer - var selectTabMethod = { method: 'selectTab', params: { id: 'particle' } }; - if (shouldUseEditTabletApp()) { - var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); - tablet.sendToQml(selectTabMethod); - } else { - createToolsWindow.sendToQml(selectTabMethod); - } -} entityListTool.webView.webEventReceived.connect(function(data) { try { @@ -2610,20 +2549,6 @@ entityListTool.webView.webEventReceived.connect(function(data) { parentSelectedEntities(); } else if (data.type === 'unparent') { unparentSelectedEntities(); - } else if (data.type === "selectionUpdate") { - var ids = data.entityIds; - if (ids.length === 1) { - if (Entities.getEntityProperties(ids[0], "type").type === "ParticleEffect") { - if (JSON.stringify(selectedParticleEntityID) === JSON.stringify(ids[0])) { - // This particle entity is already selected, so return - return; - } - // Destroy the old particles web view first - } else { - selectedParticleEntityID = 0; - particleExplorerTool.destroyWebView(); - } - } } }); diff --git a/scripts/system/html/css/edit-style.css b/scripts/system/html/css/edit-style.css index f69ace2401..8d334609a6 100644 --- a/scripts/system/html/css/edit-style.css +++ b/scripts/system/html/css/edit-style.css @@ -300,6 +300,28 @@ input[type=number].hover-down::-webkit-inner-spin-button:after { color: #ffffff; } +input[type=range] { + -webkit-appearance: none; + background: #2e2e2e; + height: 1.8rem; + border-radius: 1rem; +} +input[type=range]::-webkit-slider-thumb { + -webkit-appearance:none; + width: 0.6rem; + height: 1.8rem; + padding:0; + margin: 0; + background-color: #696969; + border-radius: 1rem; +} +input[type=range]::-webkit-slider-thumb:hover { + background-color: white; +} +input[type=range]:focus { /*#252525*/ + outline: none; +} + input.no-spin::-webkit-outer-spin-button, input.no-spin::-webkit-inner-spin-button { display: none; @@ -623,6 +645,15 @@ hr { margin-left: 10px; } +.property.range label{ + display: block; +} +.property.range input[type=number]{ + margin-left: 0.8rem; + width: 5.4rem; + height: 1.8rem; +} + .text label, .url label, .number label, .textarea label, .xy label, .wh label, .rgb label, .xyz label,.pyr label, .dropdown label, .gen label { float: left; margin-left: 1px; @@ -853,13 +884,13 @@ div.refresh input[type="button"] { color: #1080b8; } -.tuple .red:focus, .tuple .x:focus, .tuple .pitch:focus { +.tuple .red:focus, .tuple .x:focus, .tuple .pitch:focus, .tuple .width:focus { outline-color: #e2334d; } -.tuple .green:focus, .tuple .y:focus, .tuple .yaw:focus { +.tuple .green:focus, .tuple .y:focus, .tuple .yaw:focus, .tuple .height:focus { outline-color: #1ac567; } -tuple, .blue:focus, .tuple .z:focus, .tuple .roll:focus { +.tuple .blue:focus, .tuple .z:focus, .tuple .roll:focus { outline-color: #1080b8; } @@ -884,6 +915,37 @@ tuple, .blue:focus, .tuple .z:focus, .tuple .roll:focus { float: left; } +.property.texture { + display: block; +} +.property.texture input{ + margin: 0.4rem 0; +} +.texture-image img{ + padding: 0; + margin: 0; + width: 100%; + height: 100%; + display: none; +} +.texture-image { + display: block; + position: relative; + background-repeat: no-repeat; + background-position: center; + background-size: 100% 100%; + margin-top: 0.4rem; + height:128px; + width: 128px; + background-image: url('data:image/png;base64,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'); +} +.texture-image.no-texture { + background-image: url('data:image/png;base64,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'); +} +.texture-image.no-preview { + background-image: url('data:image/png;base64,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'); +} + .two-column { display: table; width: 100%; diff --git a/scripts/system/html/entityProperties.html b/scripts/system/html/entityProperties.html index 93f80e19b3..370681339e 100644 --- a/scripts/system/html/entityProperties.html +++ b/scripts/system/html/entityProperties.html @@ -20,6 +20,7 @@ + diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 1d8a397f07..328a998dda 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -58,6 +58,11 @@ const GROUPS = [ type: "string", propertyID: "parentID", }, + { + label: "Parent Joint Index", + type: "number", + propertyID: "parentJointIndex", + }, { label: "Locked", glyph: "", @@ -116,7 +121,7 @@ const GROUPS = [ type: "number", min: 0, step: 0.005, - fixedDecimals: 4, + decimals: 4, unit: "m", propertyID: "lineHeight" }, @@ -165,14 +170,14 @@ const GROUPS = [ min: 0, max: 10, step: 0.1, - fixedDecimals: 2, + decimals: 2, propertyID: "keyLight.intensity", showPropertyRule: { "keyLightMode": "enabled" }, }, { label: "Light Altitude", type: "number", - fixedDecimals: 2, + decimals: 2, unit: "deg", propertyID: "keyLight.direction.y", showPropertyRule: { "keyLightMode": "enabled" }, @@ -180,7 +185,7 @@ const GROUPS = [ { label: "Light Azimuth", type: "number", - fixedDecimals: 2, + decimals: 2, unit: "deg", propertyID: "keyLight.direction.x", showPropertyRule: { "keyLightMode": "enabled" }, @@ -228,7 +233,7 @@ const GROUPS = [ min: 0, max: 10, step: 0.1, - fixedDecimals: 2, + decimals: 2, propertyID: "ambientLight.ambientIntensity", showPropertyRule: { "ambientLightMode": "enabled" }, }, @@ -250,7 +255,7 @@ const GROUPS = [ min: 5, max: 10000, step: 5, - fixedDecimals: 0, + decimals: 0, unit: "m", propertyID: "haze.hazeRange", showPropertyRule: { "hazeMode": "enabled" }, @@ -267,7 +272,7 @@ const GROUPS = [ min: -1000, max: 1000, step: 10, - fixedDecimals: 0, + decimals: 0, unit: "m", propertyID: "haze.hazeBaseRef", showPropertyRule: { "hazeMode": "enabled" }, @@ -278,7 +283,7 @@ const GROUPS = [ min: -1000, max: 5000, step: 10, - fixedDecimals: 0, + decimals: 0, unit: "m", propertyID: "haze.hazeCeiling", showPropertyRule: { "hazeMode": "enabled" }, @@ -291,11 +296,11 @@ const GROUPS = [ }, { label: "Background Blend", - type: "number", + type: "slider", min: 0.0, max: 1.0, step: 0.01, - fixedDecimals: 2, + decimals: 2, propertyID: "haze.hazeBackgroundBlend", showPropertyRule: { "hazeMode": "enabled" }, }, @@ -313,11 +318,11 @@ const GROUPS = [ }, { label: "Glare Angle", - type: "number", + type: "slider", min: 0, max: 180, step: 1, - fixedDecimals: 0, + decimals: 0, propertyID: "haze.hazeGlareAngle", showPropertyRule: { "hazeMode": "enabled" }, }, @@ -329,31 +334,31 @@ const GROUPS = [ }, { label: "Bloom Intensity", - type: "number", + type: "slider", min: 0, max: 1, step: 0.01, - fixedDecimals: 2, + decimals: 2, propertyID: "bloom.bloomIntensity", showPropertyRule: { "bloomMode": "enabled" }, }, { label: "Bloom Threshold", - type: "number", + type: "slider", min: 0, min: 1, step: 0.01, - fixedDecimals: 2, + decimals: 2, propertyID: "bloom.bloomThreshold", showPropertyRule: { "bloomMode": "enabled" }, }, { label: "Bloom Size", - type: "number", + type: "slider", min: 0, min: 2, step: 0.01, - fixedDecimals: 2, + decimals: 2, propertyID: "bloom.bloomSize", showPropertyRule: { "bloomMode": "enabled" }, }, @@ -469,7 +474,6 @@ const GROUPS = [ { id: "light", addToGroup: "base", - addToGroup: "base", properties: [ { label: "Light Color", @@ -482,7 +486,7 @@ const GROUPS = [ type: "number", min: 0, step: 0.1, - fixedDecimals: 1, + decimals: 1, propertyID: "intensity", }, { @@ -490,7 +494,7 @@ const GROUPS = [ type: "number", min: 0, step: 0.1, - fixedDecimals: 1, + decimals: 1, unit: "m", propertyID: "falloffRadius", }, @@ -503,14 +507,14 @@ const GROUPS = [ label: "Spotlight Exponent", type: "number", step: 0.01, - fixedDecimals: 2, + decimals: 2, propertyID: "exponent", }, { label: "Spotlight Cut-Off", type: "number", step: 0.01, - fixedDecimals: 2, + decimals: 2, propertyID: "cutoff", }, ] @@ -558,19 +562,21 @@ const GROUPS = [ { label: "Material Position", type: "vec2", + vec2Type: "xy", min: 0, min: 1, step: 0.1, - vec2Type: "xy", + decimals: 4, subLabels: [ "x", "y" ], propertyID: "materialMappingPos", }, { label: "Material Scale", type: "vec2", + vec2Type: "wh", min: 0, step: 0.1, - vec2Type: "wh", + decimals: 4, subLabels: [ "width", "height" ], propertyID: "materialMappingScale", }, @@ -578,12 +584,326 @@ const GROUPS = [ label: "Material Rotation", type: "number", step: 0.1, - fixedDecimals: 2, + decimals: 2, unit: "deg", propertyID: "materialMappingRot", }, ] }, + { + id: "particles", + addToGroup: "base", + properties: [ + { + label: "Emit", + type: "bool", + propertyID: "isEmitting", + }, + { + label: "Lifespan", + type: "slider", + unit: "s", + min: 0.01, + max: 10, + step: 0.01, + propertyID: "lifespan", + }, + { + label: "Max Particles", + type: "slider", + min: 1, + max: 10000, + step: 1, + propertyID: "maxParticles", + }, + { + label: "Texture", + type: "texture", + propertyID: "particleTextures", + propertyName: "textures", // actual entity property name + }, + ] + }, + { + id: "particles_emit", + label: "EMIT", + properties: [ + { + label: "Emit Rate", + type: "slider", + min: 1, + max: 1000, + step: 1, + propertyID: "emitRate", + }, + { + label: "Emit Speed", + type: "slider", + min: 0, + max: 5, + step: 0.01, + propertyID: "emitSpeed", + }, + { + label: "Speed Spread", + type: "slider", + min: 0, + max: 5, + step: 0.01, + propertyID: "speedSpread", + }, + { + label: "Emit Dimension", + type: "vec3", + vec3Type: "xyz", + min: 0, + step: 0.01, + subLabels: [ "x", "y", "z" ], + propertyID: "emitDimensions", + }, + { + label: "Emit Orientation", + type: "vec3", + vec3Type: "pyr", + min: 0, + step: 0.01, + subLabels: [ "pitch", "yaw", "roll" ], + unit: "deg", + propertyID: "emitOrientation", + }, + { + label: "Trails", + type: "bool", + propertyID: "emitterShouldTrail", + }, + ] + }, + { + id: "particles_size", + label: "SIZE", + properties: [ + { + label: "Size", + type: "slider", + max: 4, + step: 0.01, + propertyID: "particleRadius", + }, + { + label: "Size Spread", + type: "slider", + max: 4, + step: 0.01, + propertyID: "radiusSpread", + }, + { + label: "Size Start", + type: "slider", + max: 4, + step: 0.01, + propertyID: "radiusStart", + fallbackProperty: "particleRadius", + }, + { + label: "Size Finish", + type: "slider", + max: 4, + step: 0.01, + propertyID: "radiusFinish", + fallbackProperty: "particleRadius", + }, + ] + }, + { + id: "particles_color", + label: "COLOR", + properties: [ + { + label: "Color", + type: "color", + propertyID: "particleColor", + propertyName: "color", // actual entity property name + }, + { + label: "Color Start", + type: "color", + propertyID: "colorStart", + fallbackProperty: "color", + }, + { + label: "Color Finish", + type: "color", + propertyID: "colorFinish", + fallbackProperty: "color", + }, + { + label: "Color Spread", + type: "color", + propertyID: "colorSpread", + }, + ] + }, + { + id: "particles_alpha", + label: "ALPHA", + properties: [ + { + label: "Alpha", + type: "slider", + min: 0, + max: 1, + step: 0.01, + propertyID: "alpha", + }, + { + label: "Alpha Spread", + type: "slider", + min: 0, + max: 1, + step: 0.01, + propertyID: "alphaSpread", + }, + { + label: "Alpha Start", + type: "slider", + min: 0, + max: 1, + step: 0.01, + propertyID: "alphaStart", + fallbackProperty: "alpha", + }, + { + label: "Alpha Finish", + type: "slider", + min: 0, + max: 1, + step: 0.01, + propertyID: "alphaFinish", + fallbackProperty: "alpha", + }, + ] + }, + { + id: "particles_acceleration", + label: "ACCELERATION", + properties: [ + { + label: "Emit Acceleration", + type: "vec3", + vec3Type: "xyz", + step: 0.01, + subLabels: [ "x", "y", "z" ], + propertyID: "emitAcceleration", + }, + { + label: "Acceleration Spread", + type: "vec3", + vec3Type: "xyz", + step: 0.01, + subLabels: [ "x", "y", "z" ], + propertyID: "accelerationSpread", + }, + ] + }, + { + id: "particles_spin", + label: "SPIN", + properties: [ + { + label: "Spin", + type: "slider", + min: -360, + max: 360, + step: 1, + multiplier: DEGREES_TO_RADIANS, + unit: "deg", + propertyID: "particleSpin", + }, + { + label: "Spin Spread", + type: "slider", + min: 0, + max: 360, + step: 1, + multiplier: DEGREES_TO_RADIANS, + unit: "deg", + propertyID: "spinSpread", + }, + { + label: "Spin Start", + type: "slider", + min: -360, + max: 360, + step: 1, + multiplier: DEGREES_TO_RADIANS, + unit: "deg", + propertyID: "spinStart", + fallbackProperty: "particleSpin", + }, + { + label: "Spin Finish", + type: "slider", + min: -360, + max: 360, + step: 1, + multiplier: DEGREES_TO_RADIANS, + unit: "deg", + propertyID: "spinFinish", + fallbackProperty: "particleSpin", + }, + { + label: "Rotate with Entity", + type: "bool", + propertyID: "rotateWithEntity", + }, + ] + }, + { + id: "particles_constraints", + label: "CONSTRAINTS", + properties: [ + { + label: "Horizontal Angle Start", + type: "slider", + min: 0, + max: 180, + step: 1, + multiplier: DEGREES_TO_RADIANS, + unit: "deg", + propertyID: "azimuthStart", + }, + { + label: "Horizontal Angle Finish", + type: "slider", + min: -180, + max: 0, + step: 1, + multiplier: DEGREES_TO_RADIANS, + unit: "deg", + propertyID: "azimuthFinish", + }, + { + label: "Verical Angle Start", + type: "slider", + min: 0, + max: 180, + step: 1, + multiplier: DEGREES_TO_RADIANS, + unit: "deg", + propertyID: "polarStart", + }, + { + label: "Verical Angle Finish", + type: "slider", + min: 0, + max: 180, + step: 1, + multiplier: DEGREES_TO_RADIANS, + unit: "deg", + propertyID: "polarFinish", + }, + ] + }, { id: "spatial", label: "SPATIAL", @@ -592,6 +912,7 @@ const GROUPS = [ label: "Position", type: "vec3", vec3Type: "xyz", + decimals: 4, subLabels: [ "x", "y", "z" ], unit: "m", propertyID: "position", @@ -599,8 +920,9 @@ const GROUPS = [ { label: "Rotation", type: "vec3", - step: 0.1, vec3Type: "pyr", + step: 0.1, + decimals: 4, subLabels: [ "pitch", "yaw", "roll" ], unit: "deg", propertyID: "rotation", @@ -608,8 +930,9 @@ const GROUPS = [ { label: "Dimension", type: "vec3", - step: 0.1, vec3Type: "xyz", + step: 0.1, + decimals: 4, subLabels: [ "x", "y", "z" ], unit: "m", propertyID: "dimensions", @@ -626,8 +949,9 @@ const GROUPS = [ { label: "Pivot", type: "vec3", - step: 0.1, vec3Type: "xyz", + step: 0.1, + decimals: 4, subLabels: [ "x", "y", "z" ], unit: "(ratio of dimension)", propertyID: "registrationPoint", @@ -821,6 +1145,7 @@ const GROUPS = [ label: "Linear Velocity", type: "vec3", vec3Type: "xyz", + decimals: 4, subLabels: [ "x", "y", "z" ], unit: "m/s", propertyID: "velocity", @@ -828,14 +1153,15 @@ const GROUPS = [ { label: "Linear Damping", type: "number", - fixedDecimals: 2, + decimals: 2, propertyID: "damping", }, { label: "Angular Velocity", type: "vec3", - multiplier: DEGREES_TO_RADIANS, vec3Type: "pyr", + multiplier: DEGREES_TO_RADIANS, + decimals: 4, subLabels: [ "pitch", "yaw", "roll" ], unit: "deg/s", propertyID: "angularVelocity", @@ -843,25 +1169,25 @@ const GROUPS = [ { label: "Angular Damping", type: "number", - fixedDecimals: 4, + decimals: 4, propertyID: "angularDamping", }, { label: "Bounciness", type: "number", - fixedDecimals: 4, + decimals: 4, propertyID: "restitution", }, { label: "Friction", type: "number", - fixedDecimals: 4, + decimals: 4, propertyID: "friction", }, { label: "Density", type: "number", - fixedDecimals: 4, + decimals: 4, propertyID: "density", }, { @@ -877,6 +1203,7 @@ const GROUPS = [ type: "vec3", vec3Type: "xyz", subLabels: [ "x", "y", "z" ], + decimals: 4, unit: "m/s2", propertyID: "acceleration", }, @@ -894,11 +1221,13 @@ const GROUPS_PER_TYPE = { Web: [ 'base', 'web', 'spatial', 'collision', 'behavior', 'physics' ], Light: [ 'base', 'light', 'spatial', 'collision', 'behavior', 'physics' ], Material: [ 'base', 'material', 'spatial', 'behavior' ], - ParticleEffect: [ 'base', 'spatial', 'behavior', 'physics' ], + ParticleEffect: [ 'base', 'particles', 'particles_emit', 'particles_size', 'particles_color', 'particles_alpha', + 'particles_acceleration', 'particles_spin', 'particles_constraints', 'spatial', 'behavior', 'physics' ], Multiple: [ 'base', 'spatial', 'collision', 'behavior', 'physics' ], }; const EDITOR_TIMEOUT_DURATION = 1500; +const DEBOUNCE_TIMEOUT = 125; const KEY_P = 80; // Key code for letter p used for Parenting hotkey. const MATERIAL_PREFIX_STRING = "mat::"; @@ -921,10 +1250,31 @@ function debugPrint(message) { } -// GENERAL PROPERTY/GROUP FUNCTIONS +/** + * GENERAL PROPERTY/GROUP FUNCTIONS + */ -function getPropertyElement(propertyID) { - return properties[propertyID].el; +function getPropertyInputElement(propertyID) { + let property = properties[propertyID]; + switch (property.data.type) { + case 'string': + case 'bool': + case 'number': + case 'slider': + case 'dropdown': + case 'textarea': + case 'texture': + return property.elInput; + case 'vec3': + case 'vec2': + return { x: property.elInputX, y: property.elInputY, z: property.elInputZ }; + case 'color': + return { red: property.elInputR, green: property.elInputG, blue: property.elInputB }; + case 'icon': + return property.elLabel; + default: + return undefined; + } } function enableChildren(el, selector) { @@ -944,7 +1294,7 @@ function disableChildren(el, selector) { function enableProperties() { enableChildren(document.getElementById("properties-list"), "input, textarea, checkbox, .dropdown dl, .color-picker"); enableChildren(document, ".colpick"); - var elLocked = getPropertyElement("locked"); + var elLocked = getPropertyInputElement("locked"); if (elLocked.checked === false) { removeStaticUserData(); @@ -958,7 +1308,7 @@ function disableProperties() { for (var pickKey in colorPickers) { colorPickers[pickKey].colpickHide(); } - var elLocked = getPropertyElement("locked"); + var elLocked = getPropertyInputElement("locked"); if (elLocked.checked === true) { if ($('#property-userData-editor').css('display') === "block") { @@ -971,74 +1321,70 @@ function disableProperties() { } function showPropertyElement(propertyID, show) { - let elProperty = properties[propertyID].el; - let elNode = elProperty; - if (elNode.nodeName !== "DIV") { - let elParent = elProperty.parentNode; - if (elParent === undefined && elProperty instanceof Array) { - elParent = elProperty[0].parentNode; - } - if (elParent !== undefined) { - elNode = elParent; - } - } - elNode.style.display = show ? "table" : "none"; + let elProperty = properties[propertyID].elProperty; + elProperty.style.display = show ? "table" : "none"; } function resetProperties() { for (let propertyID in properties) { - let elProperty = properties[propertyID].el; - let propertyData = properties[propertyID].data; + let property = properties[propertyID]; + let propertyData = property.data; switch (propertyData.type) { case 'string': { - elProperty.value = ""; + property.elInput.value = ""; break; } case 'bool': { - elProperty.checked = false; + property.elInput.checked = false; break; } - case 'number': { + case 'number': + case 'slider': { if (propertyData.defaultValue !== undefined) { - elProperty.value = propertyData.defaultValue; + property.elInput.value = propertyData.defaultValue; } else { - elProperty.value = ""; + property.elInput.value = ""; + } + if (property.elSlider !== undefined) { + property.elSlider.value = property.elInput.value; } break; } case 'vec3': case 'vec2': { - // vec2/vec3 are array of 2/3 elInput numbers - elProperty[0].value = ""; - elProperty[1].value = ""; - if (elProperty[2] !== undefined) { - elProperty[2].value = ""; + property.elInputX.value = ""; + property.elInputY.value = ""; + if (property.elInputZ !== undefined) { + property.elInputZ.value = ""; } break; } case 'color': { - // color is array of color picker and 3 elInput numbers - elProperty[0].style.backgroundColor = "rgb(" + 0 + "," + 0 + "," + 0 + ")"; - elProperty[1].value = ""; - elProperty[2].value = ""; - elProperty[3].value = ""; + property.elColorPicker.style.backgroundColor = "rgb(" + 0 + "," + 0 + "," + 0 + ")"; + property.elInputR.value = ""; + property.elInputG.value = ""; + property.elInputB.value = ""; break; } case 'dropdown': { - elProperty.value = ""; - setDropdownText(elProperty); + property.elInput.value = ""; + setDropdownText(property.elInput); break; } case 'textarea': { - elProperty.value = ""; - setTextareaScrolling(elProperty); + property.elInput.value = ""; + setTextareaScrolling(property.elInput); break; } case 'icon': { - // icon is array of elSpan (icon glyph) and elLabel - elProperty[0].style.display = "none"; - elProperty[1].innerHTML = propertyData.label; + property.elSpan.style.display = "none"; + property.elLabel.innerHTML = propertyData.label; + break; + } + case 'texture': { + property.elInput.value = ""; + property.elInput.imageLoad(property.elInput.value); break; } } @@ -1069,8 +1415,34 @@ function showGroupsForType(type) { } } +function getPropertyValue(propertyName) { + // if this is a compound property name (i.e. animation.running) + // then split it by . up to 3 times to find property value + let propertyValue; + let splitPropertyName = propertyName.split('.'); + if (splitPropertyName.length > 1) { + let propertyGroupName = splitPropertyName[0]; + let subPropertyName = splitPropertyName[1]; + let groupProperties = selectedEntityProperties[propertyGroupName]; + if (groupProperties === undefined || groupProperties[subPropertyName] === undefined) { + return undefined; + } + if (splitPropertyName.length === 3) { + let subSubPropertyName = splitPropertyName[2]; + propertyValue = groupProperties[subPropertyName][subSubPropertyName]; + } else { + propertyValue = groupProperties[subPropertyName]; + } + } else { + propertyValue = selectedEntityProperties[propertyName]; + } + return propertyValue; +} -// PROPERTY UPDATE FUNCTIONS + +/** + * PROPERTY UPDATE FUNCTIONS + */ function updateProperty(propertyName, propertyValue) { let propertyUpdate = {}; @@ -1114,26 +1486,24 @@ function createEmitCheckedPropertyUpdateFunction(propertyName, inverse) { }; } -function createEmitNumberPropertyUpdateFunction(propertyName, decimals) { - decimals = ((decimals === undefined) ? 4 : decimals); +function createEmitNumberPropertyUpdateFunction(propertyName, multiplier, decimals) { return function() { - let value = parseFloat(this.value).toFixed(decimals); + let value = parseFloat(this.value); + if (multiplier !== undefined) { + value *= multiplier; + } + if (decimals !== undefined) { + value = value.toFixed(decimals); + } updateProperty(propertyName, value); }; } -function createEmitVec2PropertyUpdateFunction(propertyName, elX, elY) { - return function () { - let newValue = { - x: elX.value, - y: elY.value - }; - updateProperty(propertyName, newValue); - }; -} - -function createEmitVec2PropertyUpdateFunctionWithMultiplier(propertyName, elX, elY, multiplier) { +function createEmitVec2PropertyUpdateFunction(propertyName, elX, elY, multiplier) { return function () { + if (multiplier === undefined) { + multiplier = 1; + } let newValue = { x: elX.value * multiplier, y: elY.value * multiplier @@ -1142,19 +1512,11 @@ function createEmitVec2PropertyUpdateFunctionWithMultiplier(propertyName, elX, e }; } -function createEmitVec3PropertyUpdateFunction(propertyName, elX, elY, elZ) { - return function() { - let newValue = { - x: elX.value, - y: elY.value, - z: elZ ? elZ.value : 0 - }; - updateProperty(propertyName, newValue); - }; -} - -function createEmitVec3PropertyUpdateFunctionWithMultiplier(propertyName, elX, elY, elZ, multiplier) { +function createEmitVec3PropertyUpdateFunction(propertyName, elX, elY, elZ, multiplier) { return function() { + if (multiplier === undefined) { + multiplier = 1; + } let newValue = { x: elX.value * multiplier, y: elY.value * multiplier, @@ -1199,14 +1561,16 @@ function createImageURLUpdateFunction(propertyName) { } -// PROPERTY ELEMENT CREATION FUNCTIONS +/** + * PROPERTY ELEMENT CREATION FUNCTIONS + */ function createStringProperty(property, elProperty, elLabel) { let propertyName = property.name; let elementID = property.elementID; let propertyData = property.data; - elProperty.setAttribute("class", "property text"); + elProperty.className = "property text"; let elInput = document.createElement('input'); elInput.setAttribute("id", elementID); @@ -1232,7 +1596,7 @@ function createBoolProperty(property, elProperty, elLabel) { let elementID = property.elementID; let propertyData = property.data; - elProperty.setAttribute("class", "property checkbox"); + elProperty.className = "property checkbox"; if (propertyData.glyph !== undefined) { elLabel.innerText = " " + propertyData.label; @@ -1260,15 +1624,108 @@ function createBoolProperty(property, elProperty, elLabel) { return elInput; } -function createVec3Property(property, elProperty, elLabel) { +function createNumberProperty(property, elProperty, elLabel) { let propertyName = property.name; let elementID = property.elementID; let propertyData = property.data; - elProperty.setAttribute("class", "property " + propertyData.vec3Type + " fstuple"); + elProperty.className = "property number"; + + addUnit(propertyData.unit, elLabel); + + let elInput = document.createElement('input'); + elInput.setAttribute("id", elementID); + elInput.setAttribute("type", "number"); + if (propertyData.min !== undefined) { + elInput.setAttribute("min", propertyData.min); + } + if (propertyData.max !== undefined) { + elInput.setAttribute("max", propertyData.max); + } + if (propertyData.step !== undefined) { + elInput.setAttribute("step", propertyData.step); + } + + let defaultValue = propertyData.defaultValue; + if (defaultValue !== undefined) { + elInput.value = defaultValue; + } + + elInput.addEventListener('change', createEmitNumberPropertyUpdateFunction(propertyName, propertyData.muliplier, propertyData.decimals)); + + elProperty.appendChild(elLabel); + elProperty.appendChild(elInput); + + if (propertyData.buttons !== undefined) { + addButtons(elProperty, elementID, propertyData.buttons, true); + } + + return elInput; +} + +function createSliderProperty(property, elProperty, elLabel) { + let propertyData = property.data; + + elProperty.className = "property range"; + + let elDiv = document.createElement("div"); + elDiv.className = "slider-wrapper"; + + let elSlider = document.createElement("input"); + elSlider.setAttribute("type", "range"); + + let elInput = document.createElement("input"); + elInput.setAttribute("type", "number"); + + if (propertyData.min !== undefined) { + elInput.setAttribute("min", propertyData.min); + elSlider.setAttribute("min", propertyData.min); + } + if (propertyData.max !== undefined) { + elInput.setAttribute("max", propertyData.max); + elSlider.setAttribute("max", propertyData.max); + elSlider.setAttribute("data-max", propertyData.max); + } + if (propertyData.step !== undefined) { + elInput.setAttribute("step", propertyData.step); + elSlider.setAttribute("step", propertyData.step); + } + + elInput.oninput = function (event) { + let value = event.target.value; + elSlider.value = value; + if (propertyData.multiplier !== undefined) { + value *= propertyData.multiplier; + } + updateProperty(property.name, value); + }; + elInput.onchange = elInput.oninput; + elSlider.oninput = function (event) { + let value = event.target.value; + elInput.value = value; + if (propertyData.multiplier !== undefined) { + value *= propertyData.multiplier; + } + updateProperty(property.name, value); + }; + + elDiv.appendChild(elLabel); + elDiv.appendChild(elSlider); + elDiv.appendChild(elInput); + elProperty.appendChild(elDiv); + + return [ elSlider, elInput ]; +} + +function createVec3Property(property, elProperty, elLabel) { + let propertyName = property.name; + let elementID = property.elementID; + let propertyData = property.data; + + elProperty.className = "property " + propertyData.vec3Type + " fstuple"; let elTuple = document.createElement('div'); - elTuple.setAttribute("class", "tuple"); + elTuple.className = "tuple"; addUnit(propertyData.unit, elLabel); @@ -1282,13 +1739,8 @@ function createVec3Property(property, elProperty, elLabel) { let elInputZ = createTupleNumberInput(elTuple, elementID, propertyData.subLabels[2], propertyData.min, propertyData.max, propertyData.step); - let inputChangeFunction; - if (propertyData.multiplier !== undefined) { - inputChangeFunction = createEmitVec3PropertyUpdateFunctionWithMultiplier(propertyName, elInputX, elInputY, - elInputZ, propertyData.multiplier); - } else { - inputChangeFunction = createEmitVec3PropertyUpdateFunction(propertyName, elInputX, elInputY, elInputZ); - } + let inputChangeFunction = createEmitVec3PropertyUpdateFunction(propertyName, elInputX, elInputY, + elInputZ, propertyData.multiplier); elInputX.addEventListener('change', inputChangeFunction); elInputY.addEventListener('change', inputChangeFunction); elInputZ.addEventListener('change', inputChangeFunction); @@ -1301,10 +1753,10 @@ function createVec2Property(property, elProperty, elLabel) { let elementID = property.elementID; let propertyData = property.data; - elProperty.setAttribute("class", "property " + propertyData.vec2Type + " fstuple"); + elProperty.className = "property " + propertyData.vec2Type + " fstuple"; let elTuple = document.createElement('div'); - elTuple.setAttribute("class", "tuple"); + elTuple.className = "tuple"; addUnit(propertyData.unit, elLabel); @@ -1316,13 +1768,8 @@ function createVec2Property(property, elProperty, elLabel) { let elInputY = createTupleNumberInput(elTuple, elementID, propertyData.subLabels[1], propertyData.min, propertyData.max, propertyData.step); - let inputChangeFunction; - if (propertyData.multiplier !== undefined) { - inputChangeFunction = createEmitVec2PropertyUpdateFunctionWithMultiplier(propertyName, elInputX, elInputY, - propertyData.multiplier); - } else { - inputChangeFunction = createEmitVec2PropertyUpdateFunctionWithMultiplier(propertyName, elInputX, elInputY); - } + let inputChangeFunction = createEmitVec2PropertyUpdateFunction(propertyName, elInputX, + elInputY, propertyData.multiplier); elInputX.addEventListener('change', inputChangeFunction); elInputY.addEventListener('change', inputChangeFunction); @@ -1333,14 +1780,14 @@ function createColorProperty(property, elProperty, elLabel) { let propertyName = property.name; let elementID = property.elementID; - elProperty.setAttribute("class", "property rgb fstuple"); + elProperty.className = "property rgb fstuple"; let elColorPicker = document.createElement('div'); - elColorPicker.setAttribute("class", "color-picker"); + elColorPicker.className = "color-picker"; elColorPicker.setAttribute("id", elementID); let elTuple = document.createElement('div'); - elTuple.setAttribute("class", "tuple"); + elTuple.className = "tuple"; elProperty.appendChild(elColorPicker); elProperty.appendChild(elLabel); @@ -1388,7 +1835,7 @@ function createDropdownProperty(property, propertyID, elProperty, elLabel) { let elementID = property.elementID; let propertyData = property.data; - elProperty.setAttribute("class", "property dropdown"); + elProperty.className = "property dropdown"; let elInput = document.createElement('select'); elInput.setAttribute("id", elementID); @@ -1409,43 +1856,12 @@ function createDropdownProperty(property, propertyID, elProperty, elLabel) { return elInput; } -function createNumberProperty(property, elProperty, elLabel) { - let propertyName = property.name; - let elementID = property.elementID; - let propertyData = property.data; - - elProperty.setAttribute("class", "property number"); - - addUnit(propertyData.unit, elLabel); - - let elInput = document.createElement('input'); - elInput.setAttribute("id", elementID); - elInput.setAttribute("type", "number"); - - let defaultValue = propertyData.defaultValue; - if (defaultValue !== undefined) { - elInput.value = defaultValue; - } - - let fixedDecimals = propertyData.fixedDecimals !== undefined ? propertyData.fixedDecimals : 0; - elInput.addEventListener('change', createEmitNumberPropertyUpdateFunction(propertyName, fixedDecimals)); - - elProperty.appendChild(elLabel); - elProperty.appendChild(elInput); - - if (propertyData.buttons !== undefined) { - addButtons(elProperty, elementID, propertyData.buttons, true); - } - - return elInput; -} - function createTextareaProperty(property, elProperty, elLabel) { let propertyName = property.name; let elementID = property.elementID; let propertyData = property.data; - elProperty.setAttribute("class", "property textarea"); + elProperty.className = "property textarea"; elProperty.appendChild(elLabel); @@ -1470,7 +1886,7 @@ function createIconProperty(property, elProperty, elLabel) { let elementID = property.elementID; let propertyData = property.data; - elProperty.setAttribute("class", "property value"); + elProperty.className = "property value"; elLabel.setAttribute("id", elementID); elLabel.innerHTML = " " + propertyData.label; @@ -1484,11 +1900,62 @@ function createIconProperty(property, elProperty, elLabel) { return [ elSpan, elLabel ]; } +function createTextureProperty(property, elProperty, elLabel) { + let elementID = property.elementID; + + elProperty.className = "property texture"; + + let elDiv = document.createElement("div"); + let elImage = document.createElement("img"); + elDiv.className = "texture-image no-texture"; + elDiv.appendChild(elImage); + + let elInput = document.createElement('input'); + elInput.setAttribute("id", elementID); + elInput.setAttribute("type", "text"); + + let imageLoad = _.debounce(function (url) { + if (url.slice(0, 5).toLowerCase() === "atp:/") { + elImage.src = ""; + elImage.style.display = "none"; + elDiv.classList.remove("with-texture"); + elDiv.classList.remove("no-texture"); + elDiv.classList.add("no-preview"); + } else if (url.length > 0) { + elDiv.classList.remove("no-texture"); + elDiv.classList.remove("no-preview"); + elDiv.classList.add("with-texture"); + elImage.src = url; + elImage.style.display = "block"; + } else { + elImage.src = ""; + elImage.style.display = "none"; + elDiv.classList.remove("with-texture"); + elDiv.classList.remove("no-preview"); + elDiv.classList.add("no-texture"); + } + }, DEBOUNCE_TIMEOUT * 2); + elInput.imageLoad = imageLoad; + elInput.oninput = function (event) { + // Add throttle + var url = event.target.value; + imageLoad(url); + updateProperty(property.name, url) + }; + elInput.onchange = elInput.oninput; + + elProperty.appendChild(elLabel); + elProperty.appendChild(elDiv); + elProperty.appendChild(elInput); + + return [ elImage, elInput ]; +} + function createButtonsProperty(property, elProperty, elLabel) { let elementID = property.elementID; let propertyData = property.data; - elProperty.setAttribute("class", "property text"); + elProperty.className = "property text"; let hasLabel = propertyData.label !== undefined; if (hasLabel) { @@ -1511,9 +1978,12 @@ function createTupleNumberInput(elTuple, propertyElementID, subLabel, min, max, elLabel.setAttribute("for", elementID); let elInput = document.createElement('input'); + elInput.className = subLabel; elInput.setAttribute("id", elementID); elInput.setAttribute("type", "number"); - elInput.setAttribute("class", subLabel); + elInput.setAttribute("min", min); + elInput.setAttribute("max", max); + elInput.setAttribute("step", step); elDiv.appendChild(elInput); elDiv.appendChild(elLabel); @@ -1525,7 +1995,7 @@ function createTupleNumberInput(elTuple, propertyElementID, subLabel, min, max, function addUnit(unit, elLabel) { if (unit !== undefined) { let elSpan = document.createElement('span'); - elSpan.setAttribute("class", "unit"); + elSpan.className = "unit"; elSpan.innerHTML = unit; elLabel.appendChild(elSpan); } @@ -1533,12 +2003,12 @@ function addUnit(unit, elLabel) { function addButtons(elProperty, propertyID, buttons, newRow) { let elDiv = document.createElement('div'); - elDiv.setAttribute("class", "row"); + elDiv.className = "row"; buttons.forEach(function(button) { let elButton = document.createElement('input'); + elButton.className = button.className; elButton.setAttribute("type", "button"); - elButton.setAttribute("class", button.className); elButton.setAttribute("id", propertyID + "-button-" + button.id); elButton.setAttribute("value", button.label); elButton.addEventListener("click", button.onClick); @@ -1556,7 +2026,9 @@ function addButtons(elProperty, propertyID, buttons, newRow) { } -// BUTTON CALLBACKS +/** + * BUTTON CALLBACKS + */ function rescaleDimensions() { EventBridge.emitWebEvent(JSON.stringify({ @@ -1604,16 +2076,18 @@ function reloadServerScripts() { } function copySkyboxURLToAmbientURL() { - let skyboxURL = getPropertyElement("skybox.url").value; - getPropertyElement("ambientLight.ambientURL").value = skyboxURL; + let skyboxURL = getPropertyInputElement("skybox.url").value; + getPropertyInputElement("ambientLight.ambientURL").value = skyboxURL; updateProperty("ambientLight.ambientURL", skyboxURL); } -// USER DATA FUNCTIONS +/** + * USER DATA FUNCTIONS + */ function clearUserData() { - let elUserData = getPropertyElement("userData"); + let elUserData = getPropertyInputElement("userData"); deleteJSONEditor(); elUserData.value = ""; showUserDataTextArea(); @@ -1831,10 +2305,12 @@ function saveJSONUserData(noUpdate) { } -// MATERIAL DATA FUNCTIONS +/** + * MATERIAL DATA FUNCTIONS + */ function clearMaterialData() { - let elMaterialData = getPropertyElement("materialData"); + let elMaterialData = getPropertyInputElement("materialData"); deleteJSONMaterialEditor(); elMaterialData.value = ""; showMaterialDataTextArea(); @@ -2015,7 +2491,9 @@ function bindAllNonJSONEditorElements() { } -// DROPDOWN FUNCTIONS +/** + * DROPDOWN FUNCTIONS + */ function setDropdownText(dropdown) { let lis = dropdown.parentNode.getElementsByTagName("li"); @@ -2051,7 +2529,9 @@ function setDropdownValue(event) { } -// TEXTAREA / PARENT MATERIAL NAME FUNCTIONS +/** + * TEXTAREA / PARENT MATERIAL NAME FUNCTIONS + */ function setTextareaScrolling(element) { var isScrolling = element.scrollHeight > element.offsetHeight; @@ -2084,17 +2564,17 @@ function loaded() { fieldset.appendChild(elGroup); } else { elGroup = document.createElement('fieldset'); - elGroup.setAttribute("class", "major"); + elGroup.className = "major"; elGroup.setAttribute("id", "properties-" + group.id); elPropertiesList.appendChild(elGroup); } if (group.label !== undefined) { let elLegend = document.createElement('legend'); + elLegend.className = "section-header"; elLegend.innerText = group.label; - elLegend.setAttribute("class", "section-header"); let elSpan = document.createElement('span'); - elSpan.setAttribute("class", ".collapse-icon"); + elSpan.className = ".collapse-icon"; elSpan.innerText = "M"; elLegend.appendChild(elSpan); elGroup.appendChild(elLegend); @@ -2109,8 +2589,8 @@ function loaded() { let elProperty; if (propertyType === "sub-header") { elProperty = document.createElement('legend'); + elProperty.className = "sub-section-header"; elProperty.innerText = propertyData.label; - elProperty.setAttribute("class", "sub-section-header"); } else { elProperty = document.createElement('div'); elProperty.setAttribute("id", "div-" + propertyElementID); @@ -2124,12 +2604,12 @@ function loaded() { let elColumnDiv = document.getElementById(columnDivName); if (!elColumnDiv) { elColumnDiv = document.createElement('div'); - elColumnDiv.setAttribute("class", "two-column"); + elColumnDiv.className = "two-column"; elColumnDiv.setAttribute("id", group.id + "columnDiv"); elGroup.appendChild(elColumnDiv); } elColumn = document.createElement('fieldset'); - elColumn.setAttribute("class", "column"); + elColumn.className = "column"; elColumn.setAttribute("id", columnName); elColumnDiv.appendChild(elColumn); } @@ -2142,53 +2622,79 @@ function loaded() { elLabel.innerText = propertyData.label; elLabel.setAttribute("for", propertyElementID); - properties[propertyID] = { data: propertyData, elementID: propertyElementID, name: propertyName }; - - let property = properties[propertyID]; + let property = { + data: propertyData, + elementID: propertyElementID, + name: propertyName, + elProperty: elProperty + }; + properties[propertyID] = property; switch (propertyType) { case 'string': { - properties[propertyID].el = createStringProperty(property, elProperty, elLabel); + properties[propertyID].elInput = createStringProperty(property, elProperty, elLabel); break; } case 'bool': { - properties[propertyID].el = createBoolProperty(property, elProperty, elLabel); + properties[propertyID].elInput = createBoolProperty(property, elProperty, elLabel); break; } case 'number': { - properties[propertyID].el = createNumberProperty(property, elProperty, elLabel); + properties[propertyID].elInput = createNumberProperty(property, elProperty, elLabel); + break; + } + case 'slider': { + let elSlider = createSliderProperty(property, elProperty, elLabel); + properties[propertyID].elSlider = elSlider[0]; + properties[propertyID].elInput = elSlider[1]; break; } case 'vec3': { - properties[propertyID].el = createVec3Property(property, elProperty, elLabel); + let elVec3 = createVec3Property(property, elProperty, elLabel); + properties[propertyID].elInputX = elVec3[0]; + properties[propertyID].elInputY = elVec3[1]; + properties[propertyID].elInputZ = elVec3[2]; break; } case 'vec2': { - properties[propertyID].el = createVec2Property(property, elProperty, elLabel); + let elVec2 = createVec2Property(property, elProperty, elLabel); + properties[propertyID].elInputX = elVec2[0]; + properties[propertyID].elInputY = elVec2[1]; break; } case 'color': { - properties[propertyID].el = createColorProperty(property, elProperty, elLabel); + let elColor = createColorProperty(property, elProperty, elLabel); + properties[propertyID].elColorPicker = elColor[0]; + properties[propertyID].elInputR = elColor[1]; + properties[propertyID].elInputG = elColor[2]; + properties[propertyID].elInputB = elColor[3]; break; } case 'dropdown': { - properties[propertyID].el = createDropdownProperty(property, propertyID, elProperty, elLabel); + properties[propertyID].elInput = createDropdownProperty(property, propertyID, elProperty, elLabel); break; } case 'textarea': { - properties[propertyID].el = createTextareaProperty(property, elProperty, elLabel); + properties[propertyID].elInput = createTextareaProperty(property, elProperty, elLabel); break; } case 'icon': { - properties[propertyID].el = createIconProperty(property, elProperty, elLabel); + let elIcon = createIconProperty(property, elProperty, elLabel); + properties[propertyID].elSpan = elIcon[0]; + properties[propertyID].elLabel = elIcon[1]; + break; + } + case 'texture': { + let elTexture = createTextureProperty(property, elProperty, elLabel); + properties[propertyID].elImage = elTexture[0]; + properties[propertyID].elInput = elTexture[1]; break; } case 'buttons': { - properties[propertyID].el = createButtonsProperty(property, elProperty, elLabel); + properties[propertyID].elProperty = createButtonsProperty(property, elProperty, elLabel); break; } case 'sub-header': { - properties[propertyID].el = elProperty; break; } default: { @@ -2258,13 +2764,13 @@ function loaded() { showGroupsForType("None"); deleteJSONEditor(); - getPropertyElement("userData").value = ""; + getPropertyInputElement("userData").value = ""; showUserDataTextArea(); showSaveUserDataButton(); showNewJSONEditorButton(); deleteJSONMaterialEditor(); - getPropertyElement("materialData").value = ""; + getPropertyInputElement("materialData").value = ""; showMaterialDataTextArea(); showSaveMaterialDataButton(); showNewJSONMaterialEditorButton(); @@ -2299,16 +2805,13 @@ function loaded() { showGroupsForType(type); let typeProperty = properties["type"]; - let elTypeProperty = typeProperty.el; - elTypeProperty[0].innerHTML = typeProperty.data.icons[type]; - elTypeProperty[0].style.display = "inline-block"; - elTypeProperty[1].innerHTML = type + " (" + data.selections.length + ")"; + typeProperty.elSpan.innerHTML = typeProperty.data.icons[type]; + typeProperty.elSpan.style.display = "inline-block"; + typeProperty.elLabel.innerHTML = type + " (" + data.selections.length + ")"; disableProperties(); } else { - selectedEntityProperties = data.selections[0].properties; - - showGroupsForType(selectedEntityProperties.type); + selectedEntityProperties = data.selections[0].properties; if (lastEntityID !== '"' + selectedEntityProperties.id + '"' && lastEntityID !== null) { if (editor !== null) { @@ -2335,41 +2838,26 @@ function loaded() { } } + showGroupsForType(selectedEntityProperties.type); + for (let propertyID in properties) { let property = properties[propertyID]; - let elProperty = property.el; let propertyData = property.data; let propertyName = property.name; - - // if this is a compound property name (i.e. animation.running) - // then split it by . up to 3 times to find property value - let propertyValue; - let splitPropertyName = propertyName.split('.'); - if (splitPropertyName.length > 1) { - let propertyGroupName = splitPropertyName[0]; - let subPropertyName = splitPropertyName[1]; - let groupProperties = selectedEntityProperties[propertyGroupName]; - if (groupProperties === undefined || groupProperties[subPropertyName] === undefined) { - continue; - } - if (splitPropertyName.length === 3) { - let subSubPropertyName = splitPropertyName[2]; - propertyValue = groupProperties[subPropertyName][subSubPropertyName]; - } else { - propertyValue = groupProperties[subPropertyName]; - } - } else { - propertyValue = selectedEntityProperties[propertyName]; - } + let propertyValue = getPropertyValue(propertyName); let isSubProperty = propertyData.subPropertyOf !== undefined; - if (elProperty === undefined || (propertyValue === undefined && !isSubProperty)) { + if (propertyValue === undefined && !isSubProperty) { continue; } + if (!propertyValue && propertyData.fallbackProperty !== undefined) { + propertyValue = getPropertyValue(propertyData.fallbackProperty); + } + switch (propertyData.type) { case 'string': { - elProperty.value = propertyValue; + property.elInput.value = propertyValue; break; } case 'bool': { @@ -2378,53 +2866,64 @@ function loaded() { let propertyValue = selectedEntityProperties[propertyData.subPropertyOf]; let subProperties = propertyValue.split(","); let subPropertyValue = subProperties.indexOf(propertyID) > -1; - elProperty.checked = inverse ? !subPropertyValue : subPropertyValue; + property.elInput.checked = inverse ? !subPropertyValue : subPropertyValue; } else { - elProperty.checked = inverse ? !propertyValue : propertyValue; + property.elInput.checked = inverse ? !propertyValue : propertyValue; } break; } - case 'number': { - let fixedDecimals = propertyData.fixedDecimals !== undefined ? propertyData.fixedDecimals : 0; - elProperty.value = propertyValue.toFixed(fixedDecimals); + case 'number': + case 'slider': { + let multiplier = propertyData.multiplier !== undefined ? propertyData.multiplier : 1; + let decimals = propertyData.decimals !== undefined ? propertyData.decimals : 0; + property.elInput.value = (propertyValue / multiplier).toFixed(decimals); + if (property.elSlider !== undefined) { + property.elSlider.value = property.elInput.value; + } break; } case 'vec3': case 'vec2': { - // vec2/vec3 are array of 2/3 elInput numbers let multiplier = propertyData.multiplier !== undefined ? propertyData.multiplier : 1; - elProperty[0].value = (propertyValue.x / multiplier).toFixed(4); - elProperty[1].value = (propertyValue.y / multiplier).toFixed(4); - if (elProperty[2] !== undefined) { - elProperty[2].value = (propertyValue.z / multiplier).toFixed(4); + let decimals = propertyData.decimals !== undefined ? propertyData.decimals : 0; + property.elInputX.value = (propertyValue.x / multiplier).toFixed(decimals); + property.elInputY.value = (propertyValue.y / multiplier).toFixed(decimals); + if (property.elInputZ !== undefined) { + property.elInputZ.value = (propertyValue.z / multiplier).toFixed(decimals); } break; } case 'color': { - // color is array of color picker and 3 elInput numbers - elProperty[0].style.backgroundColor = "rgb(" + propertyValue.red + "," + - propertyValue.green + "," + - propertyValue.blue + ")"; - elProperty[1].value = propertyValue.red; - elProperty[2].value = propertyValue.green; - elProperty[3].value = propertyValue.blue; + if (!propertyValue.red && propertyData.fallbackProperty !== undefined) { + propertyValue = getPropertyValue(propertyData.fallbackProperty); + } + property.elColorPicker.style.backgroundColor = "rgb(" + propertyValue.red + "," + + propertyValue.green + "," + + propertyValue.blue + ")"; + property.elInputR.value = propertyValue.red; + property.elInputG.value = propertyValue.green; + property.elInputB.value = propertyValue.blue; break; } case 'dropdown': { - elProperty.value = propertyValue; - setDropdownText(elProperty); + property.elInput.value = propertyValue; + setDropdownText(property.elInput); break; } case 'textarea': { - elProperty.value = propertyValue; - setTextareaScrolling(elProperty); + property.elInput.value = propertyValue; + setTextareaScrolling(property.elInput); break; } case 'icon': { - // icon is array of elSpan (icon glyph) and elLabel - elProperty[0].innerHTML = propertyData.icons[propertyValue]; - elProperty[0].style.display = "inline-block"; - elProperty[1].innerHTML = propertyValue; + property.elSpan.innerHTML = propertyData.icons[propertyValue]; + property.elSpan.style.display = "inline-block"; + property.elLabel.innerHTML = propertyValue; + break; + } + case 'texture': { + property.elInput.value = propertyValue; + property.elInput.imageLoad(property.elInput.value); break; } } @@ -2439,10 +2938,10 @@ function loaded() { } } - let elGrabbable = getPropertyElement("grabbable"); - let elTriggerable = getPropertyElement("triggerable"); - let elIgnoreIK = getPropertyElement("ignoreIK"); - elGrabbable.checked = getPropertyElement("dynamic").checked; + let elGrabbable = getPropertyInputElement("grabbable"); + let elTriggerable = getPropertyInputElement("triggerable"); + let elIgnoreIK = getPropertyInputElement("ignoreIK"); + elGrabbable.checked = getPropertyInputElement("dynamic").checked; elTriggerable.checked = false; elIgnoreIK.checked = true; let grabbablesSet = false; @@ -2481,11 +2980,11 @@ function loaded() { if (selectedEntityProperties.type === "Image") { let imageLink = JSON.parse(selectedEntityProperties.textures)["tex.picture"]; - getPropertyElement("image").value = imageLink; + getPropertyInputElement("image").value = imageLink; } else if (selectedEntityProperties.type === "Material") { - let elParentMaterialNameString = getPropertyElement("materialNameToReplace"); - let elParentMaterialNameNumber = getPropertyElement("submeshToReplace"); - let elParentMaterialNameCheckbox = getPropertyElement("selectSubmesh"); + let elParentMaterialNameString = getPropertyInputElement("materialNameToReplace"); + let elParentMaterialNameNumber = getPropertyInputElement("submeshToReplace"); + let elParentMaterialNameCheckbox = getPropertyInputElement("selectSubmesh"); let parentMaterialName = selectedEntityProperties.parentMaterialName; if (parentMaterialName.startsWith(MATERIAL_PREFIX_STRING)) { elParentMaterialNameString.value = parentMaterialName.replace(MATERIAL_PREFIX_STRING, ""); @@ -2504,7 +3003,7 @@ function loaded() { } catch (e) { // normal text deleteJSONEditor(); - getPropertyElement("userData").value = selectedEntityProperties.userData; + getPropertyInputElement("userData").value = selectedEntityProperties.userData; showUserDataTextArea(); showNewJSONEditorButton(); hideSaveUserDataButton(); @@ -2527,7 +3026,7 @@ function loaded() { } catch (e) { // normal text deleteJSONMaterialEditor(); - getPropertyElement("materialData").value = selectedEntityProperties.materialData; + getPropertyInputElement("materialData").value = selectedEntityProperties.materialData; showMaterialDataTextArea(); showNewJSONMaterialEditorButton(); hideSaveMaterialDataButton(); @@ -2546,7 +3045,7 @@ function loaded() { if (selectedEntityProperties.locked) { disableProperties(); - getPropertyElement("locked").removeAttribute('disabled'); + getPropertyInputElement("locked").removeAttribute('disabled'); } else { enableProperties(); disableSaveUserDataButton(); @@ -2564,10 +3063,10 @@ function loaded() { // Server Script Status let serverScriptProperty = properties["serverScripts"]; - let elServerScript = serverScriptProperty.el; + let elServerScript = serverScriptProperty.elInput; let serverScriptElementID = serverScriptProperty.elementID; let elDiv = document.createElement('div'); - elDiv.setAttribute("class", "property"); + elDiv.className = "property"; let elLabel = document.createElement('label'); elLabel.setAttribute("for", serverScriptElementID + "-status"); elLabel.innerText = "Server Script Status"; @@ -2579,19 +3078,19 @@ function loaded() { // Server Script Error elDiv = document.createElement('div'); - elDiv.setAttribute("class", "property"); + elDiv.className = "property"; let elServerScriptError = document.createElement('textarea'); elServerScriptError.setAttribute("id", serverScriptElementID + "-error"); elDiv.appendChild(elServerScriptError); elServerScript.parentNode.appendChild(elDiv); - let elScript = getPropertyElement("script"); - elScript.parentNode.setAttribute("class", "property url refresh"); - elServerScript.parentNode.setAttribute("class", "property url refresh"); + let elScript = getPropertyInputElement("script"); + elScript.parentNode.className = "property url refresh"; + elServerScript.parentNode.className = "property url refresh"; // User Data let userDataProperty = properties["userData"]; - let elUserData = userDataProperty.el; + let elUserData = userDataProperty.elInput; let userDataElementID = userDataProperty.elementID; elDiv = elUserData.parentNode; let elStaticUserData = document.createElement('div'); @@ -2607,7 +3106,7 @@ function loaded() { // Material Data let materialDataProperty = properties["materialData"]; - let elMaterialData = materialDataProperty.el; + let elMaterialData = materialDataProperty.elInput; let materialDataElementID = materialDataProperty.elementID; elDiv = elMaterialData.parentNode; let elStaticMaterialData = document.createElement('div'); @@ -2622,9 +3121,9 @@ function loaded() { elDiv.insertBefore(elMaterialDataEditor, elMaterialData); // User Data Fields - let elGrabbable = getPropertyElement("grabbable"); - let elTriggerable = getPropertyElement("triggerable"); - let elIgnoreIK = getPropertyElement("ignoreIK"); + let elGrabbable = getPropertyInputElement("grabbable"); + let elTriggerable = getPropertyInputElement("triggerable"); + let elIgnoreIK = getPropertyInputElement("ignoreIK"); elGrabbable.addEventListener('change', function() { userDataChanger("grabbableKey", "grabbable", elGrabbable, elUserData, true); }); @@ -2636,9 +3135,9 @@ function loaded() { }); // Special Property Callbacks - let elParentMaterialNameString = getPropertyElement("materialNameToReplace"); - let elParentMaterialNameNumber = getPropertyElement("submeshToReplace"); - let elParentMaterialNameCheckbox = getPropertyElement("selectSubmesh"); + let elParentMaterialNameString = getPropertyInputElement("materialNameToReplace"); + let elParentMaterialNameNumber = getPropertyInputElement("submeshToReplace"); + let elParentMaterialNameCheckbox = getPropertyInputElement("selectSubmesh"); elParentMaterialNameString.addEventListener('change', function () { updateProperty("parentMaterialName", MATERIAL_PREFIX_STRING + this.value); }); @@ -2655,7 +3154,7 @@ function loaded() { } }); - getPropertyElement("image").addEventListener('change', createImageURLUpdateFunction('textures')); + getPropertyInputElement("image").addEventListener('change', createImageURLUpdateFunction('textures')); // Collapsible sections let elCollapsible = document.getElementsByClassName("section-header"); @@ -2704,7 +3203,8 @@ function loaded() { // let elDropdowns = document.getElementsByTagName("select"); for (let dropDownIndex = 0; dropDownIndex < elDropdowns.length; ++dropDownIndex) { - let options = elDropdowns[dropDownIndex].getElementsByTagName("option"); + let elDropdown = elDropdowns[dropDownIndex]; + let options = elDropdown.getElementsByTagName("option"); let selectedOption = 0; for (let optionIndex = 0; optionIndex < options.length; ++optionIndex) { if (options[optionIndex].getAttribute("selected") === "selected") { @@ -2712,14 +3212,14 @@ function loaded() { // TODO: Shouldn't there be a break here? } } - let div = elDropdowns[dropDownIndex].parentNode; + let div = elDropdown.parentNode; let dl = document.createElement("dl"); div.appendChild(dl); let dt = document.createElement("dt"); - dt.name = elDropdowns[dropDownIndex].name; - dt.id = elDropdowns[dropDownIndex].id; + dt.name = elDropdown.name; + dt.id = elDropdown.id; dt.addEventListener("click", toggleDropdown, true); dl.appendChild(dt); @@ -2746,9 +3246,9 @@ function loaded() { ul.appendChild(li); } - let propertyID = elDropdowns[dropDownIndex].getAttribute("propertyID"); + let propertyID = elDropdown.getAttribute("propertyID"); let property = properties[propertyID]; - property.el = dt; + property.elInput = dt; dt.addEventListener('change', createEmitTextPropertyUpdateFunction(property.name)); } diff --git a/scripts/system/html/js/underscore-min.js b/scripts/system/html/js/underscore-min.js new file mode 100644 index 0000000000..f01025b7bc --- /dev/null +++ b/scripts/system/html/js/underscore-min.js @@ -0,0 +1,6 @@ +// Underscore.js 1.8.3 +// http://underscorejs.org +// (c) 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors +// Underscore may be freely distributed under the MIT license. +(function(){function n(n){function t(t,r,e,u,i,o){for(;i>=0&&o>i;i+=n){var a=u?u[i]:i;e=r(e,t[a],a,t)}return e}return function(r,e,u,i){e=b(e,i,4);var o=!k(r)&&m.keys(r),a=(o||r).length,c=n>0?0:a-1;return arguments.length<3&&(u=r[o?o[c]:c],c+=n),t(r,e,u,o,c,a)}}function t(n){return function(t,r,e){r=x(r,e);for(var u=O(t),i=n>0?0:u-1;i>=0&&u>i;i+=n)if(r(t[i],i,t))return i;return-1}}function r(n,t,r){return function(e,u,i){var o=0,a=O(e);if("number"==typeof i)n>0?o=i>=0?i:Math.max(i+a,o):a=i>=0?Math.min(i+1,a):i+a+1;else if(r&&i&&a)return i=r(e,u),e[i]===u?i:-1;if(u!==u)return i=t(l.call(e,o,a),m.isNaN),i>=0?i+o:-1;for(i=n>0?o:a-1;i>=0&&a>i;i+=n)if(e[i]===u)return i;return-1}}function e(n,t){var r=I.length,e=n.constructor,u=m.isFunction(e)&&e.prototype||a,i="constructor";for(m.has(n,i)&&!m.contains(t,i)&&t.push(i);r--;)i=I[r],i in n&&n[i]!==u[i]&&!m.contains(t,i)&&t.push(i)}var u=this,i=u._,o=Array.prototype,a=Object.prototype,c=Function.prototype,f=o.push,l=o.slice,s=a.toString,p=a.hasOwnProperty,h=Array.isArray,v=Object.keys,g=c.bind,y=Object.create,d=function(){},m=function(n){return n instanceof m?n:this instanceof m?void(this._wrapped=n):new m(n)};"undefined"!=typeof exports?("undefined"!=typeof module&&module.exports&&(exports=module.exports=m),exports._=m):u._=m,m.VERSION="1.8.3";var b=function(n,t,r){if(t===void 0)return n;switch(null==r?3:r){case 1:return function(r){return n.call(t,r)};case 2:return function(r,e){return n.call(t,r,e)};case 3:return function(r,e,u){return n.call(t,r,e,u)};case 4:return function(r,e,u,i){return n.call(t,r,e,u,i)}}return function(){return n.apply(t,arguments)}},x=function(n,t,r){return null==n?m.identity:m.isFunction(n)?b(n,t,r):m.isObject(n)?m.matcher(n):m.property(n)};m.iteratee=function(n,t){return x(n,t,1/0)};var _=function(n,t){return function(r){var e=arguments.length;if(2>e||null==r)return r;for(var u=1;e>u;u++)for(var i=arguments[u],o=n(i),a=o.length,c=0;a>c;c++){var f=o[c];t&&r[f]!==void 0||(r[f]=i[f])}return r}},j=function(n){if(!m.isObject(n))return{};if(y)return y(n);d.prototype=n;var t=new d;return d.prototype=null,t},w=function(n){return function(t){return null==t?void 0:t[n]}},A=Math.pow(2,53)-1,O=w("length"),k=function(n){var t=O(n);return"number"==typeof t&&t>=0&&A>=t};m.each=m.forEach=function(n,t,r){t=b(t,r);var e,u;if(k(n))for(e=0,u=n.length;u>e;e++)t(n[e],e,n);else{var i=m.keys(n);for(e=0,u=i.length;u>e;e++)t(n[i[e]],i[e],n)}return n},m.map=m.collect=function(n,t,r){t=x(t,r);for(var e=!k(n)&&m.keys(n),u=(e||n).length,i=Array(u),o=0;u>o;o++){var a=e?e[o]:o;i[o]=t(n[a],a,n)}return i},m.reduce=m.foldl=m.inject=n(1),m.reduceRight=m.foldr=n(-1),m.find=m.detect=function(n,t,r){var e;return e=k(n)?m.findIndex(n,t,r):m.findKey(n,t,r),e!==void 0&&e!==-1?n[e]:void 0},m.filter=m.select=function(n,t,r){var e=[];return t=x(t,r),m.each(n,function(n,r,u){t(n,r,u)&&e.push(n)}),e},m.reject=function(n,t,r){return m.filter(n,m.negate(x(t)),r)},m.every=m.all=function(n,t,r){t=x(t,r);for(var e=!k(n)&&m.keys(n),u=(e||n).length,i=0;u>i;i++){var o=e?e[i]:i;if(!t(n[o],o,n))return!1}return!0},m.some=m.any=function(n,t,r){t=x(t,r);for(var e=!k(n)&&m.keys(n),u=(e||n).length,i=0;u>i;i++){var o=e?e[i]:i;if(t(n[o],o,n))return!0}return!1},m.contains=m.includes=m.include=function(n,t,r,e){return k(n)||(n=m.values(n)),("number"!=typeof r||e)&&(r=0),m.indexOf(n,t,r)>=0},m.invoke=function(n,t){var r=l.call(arguments,2),e=m.isFunction(t);return m.map(n,function(n){var u=e?t:n[t];return null==u?u:u.apply(n,r)})},m.pluck=function(n,t){return m.map(n,m.property(t))},m.where=function(n,t){return m.filter(n,m.matcher(t))},m.findWhere=function(n,t){return m.find(n,m.matcher(t))},m.max=function(n,t,r){var e,u,i=-1/0,o=-1/0;if(null==t&&null!=n){n=k(n)?n:m.values(n);for(var a=0,c=n.length;c>a;a++)e=n[a],e>i&&(i=e)}else t=x(t,r),m.each(n,function(n,r,e){u=t(n,r,e),(u>o||u===-1/0&&i===-1/0)&&(i=n,o=u)});return i},m.min=function(n,t,r){var e,u,i=1/0,o=1/0;if(null==t&&null!=n){n=k(n)?n:m.values(n);for(var a=0,c=n.length;c>a;a++)e=n[a],i>e&&(i=e)}else t=x(t,r),m.each(n,function(n,r,e){u=t(n,r,e),(o>u||1/0===u&&1/0===i)&&(i=n,o=u)});return i},m.shuffle=function(n){for(var t,r=k(n)?n:m.values(n),e=r.length,u=Array(e),i=0;e>i;i++)t=m.random(0,i),t!==i&&(u[i]=u[t]),u[t]=r[i];return u},m.sample=function(n,t,r){return null==t||r?(k(n)||(n=m.values(n)),n[m.random(n.length-1)]):m.shuffle(n).slice(0,Math.max(0,t))},m.sortBy=function(n,t,r){return t=x(t,r),m.pluck(m.map(n,function(n,r,e){return{value:n,index:r,criteria:t(n,r,e)}}).sort(function(n,t){var r=n.criteria,e=t.criteria;if(r!==e){if(r>e||r===void 0)return 1;if(e>r||e===void 0)return-1}return n.index-t.index}),"value")};var F=function(n){return function(t,r,e){var u={};return r=x(r,e),m.each(t,function(e,i){var o=r(e,i,t);n(u,e,o)}),u}};m.groupBy=F(function(n,t,r){m.has(n,r)?n[r].push(t):n[r]=[t]}),m.indexBy=F(function(n,t,r){n[r]=t}),m.countBy=F(function(n,t,r){m.has(n,r)?n[r]++:n[r]=1}),m.toArray=function(n){return n?m.isArray(n)?l.call(n):k(n)?m.map(n,m.identity):m.values(n):[]},m.size=function(n){return null==n?0:k(n)?n.length:m.keys(n).length},m.partition=function(n,t,r){t=x(t,r);var e=[],u=[];return m.each(n,function(n,r,i){(t(n,r,i)?e:u).push(n)}),[e,u]},m.first=m.head=m.take=function(n,t,r){return null==n?void 0:null==t||r?n[0]:m.initial(n,n.length-t)},m.initial=function(n,t,r){return l.call(n,0,Math.max(0,n.length-(null==t||r?1:t)))},m.last=function(n,t,r){return null==n?void 0:null==t||r?n[n.length-1]:m.rest(n,Math.max(0,n.length-t))},m.rest=m.tail=m.drop=function(n,t,r){return l.call(n,null==t||r?1:t)},m.compact=function(n){return m.filter(n,m.identity)};var S=function(n,t,r,e){for(var u=[],i=0,o=e||0,a=O(n);a>o;o++){var c=n[o];if(k(c)&&(m.isArray(c)||m.isArguments(c))){t||(c=S(c,t,r));var f=0,l=c.length;for(u.length+=l;l>f;)u[i++]=c[f++]}else r||(u[i++]=c)}return u};m.flatten=function(n,t){return S(n,t,!1)},m.without=function(n){return m.difference(n,l.call(arguments,1))},m.uniq=m.unique=function(n,t,r,e){m.isBoolean(t)||(e=r,r=t,t=!1),null!=r&&(r=x(r,e));for(var u=[],i=[],o=0,a=O(n);a>o;o++){var c=n[o],f=r?r(c,o,n):c;t?(o&&i===f||u.push(c),i=f):r?m.contains(i,f)||(i.push(f),u.push(c)):m.contains(u,c)||u.push(c)}return u},m.union=function(){return m.uniq(S(arguments,!0,!0))},m.intersection=function(n){for(var t=[],r=arguments.length,e=0,u=O(n);u>e;e++){var i=n[e];if(!m.contains(t,i)){for(var o=1;r>o&&m.contains(arguments[o],i);o++);o===r&&t.push(i)}}return t},m.difference=function(n){var t=S(arguments,!0,!0,1);return m.filter(n,function(n){return!m.contains(t,n)})},m.zip=function(){return m.unzip(arguments)},m.unzip=function(n){for(var t=n&&m.max(n,O).length||0,r=Array(t),e=0;t>e;e++)r[e]=m.pluck(n,e);return r},m.object=function(n,t){for(var r={},e=0,u=O(n);u>e;e++)t?r[n[e]]=t[e]:r[n[e][0]]=n[e][1];return r},m.findIndex=t(1),m.findLastIndex=t(-1),m.sortedIndex=function(n,t,r,e){r=x(r,e,1);for(var u=r(t),i=0,o=O(n);o>i;){var a=Math.floor((i+o)/2);r(n[a])i;i++,n+=r)u[i]=n;return u};var E=function(n,t,r,e,u){if(!(e instanceof t))return n.apply(r,u);var i=j(n.prototype),o=n.apply(i,u);return m.isObject(o)?o:i};m.bind=function(n,t){if(g&&n.bind===g)return g.apply(n,l.call(arguments,1));if(!m.isFunction(n))throw new TypeError("Bind must be called on a function");var r=l.call(arguments,2),e=function(){return E(n,e,t,this,r.concat(l.call(arguments)))};return e},m.partial=function(n){var t=l.call(arguments,1),r=function(){for(var e=0,u=t.length,i=Array(u),o=0;u>o;o++)i[o]=t[o]===m?arguments[e++]:t[o];for(;e=e)throw new Error("bindAll must be passed function names");for(t=1;e>t;t++)r=arguments[t],n[r]=m.bind(n[r],n);return n},m.memoize=function(n,t){var r=function(e){var u=r.cache,i=""+(t?t.apply(this,arguments):e);return m.has(u,i)||(u[i]=n.apply(this,arguments)),u[i]};return r.cache={},r},m.delay=function(n,t){var r=l.call(arguments,2);return setTimeout(function(){return n.apply(null,r)},t)},m.defer=m.partial(m.delay,m,1),m.throttle=function(n,t,r){var e,u,i,o=null,a=0;r||(r={});var c=function(){a=r.leading===!1?0:m.now(),o=null,i=n.apply(e,u),o||(e=u=null)};return function(){var f=m.now();a||r.leading!==!1||(a=f);var l=t-(f-a);return e=this,u=arguments,0>=l||l>t?(o&&(clearTimeout(o),o=null),a=f,i=n.apply(e,u),o||(e=u=null)):o||r.trailing===!1||(o=setTimeout(c,l)),i}},m.debounce=function(n,t,r){var e,u,i,o,a,c=function(){var f=m.now()-o;t>f&&f>=0?e=setTimeout(c,t-f):(e=null,r||(a=n.apply(i,u),e||(i=u=null)))};return function(){i=this,u=arguments,o=m.now();var f=r&&!e;return e||(e=setTimeout(c,t)),f&&(a=n.apply(i,u),i=u=null),a}},m.wrap=function(n,t){return m.partial(t,n)},m.negate=function(n){return function(){return!n.apply(this,arguments)}},m.compose=function(){var n=arguments,t=n.length-1;return function(){for(var r=t,e=n[t].apply(this,arguments);r--;)e=n[r].call(this,e);return e}},m.after=function(n,t){return function(){return--n<1?t.apply(this,arguments):void 0}},m.before=function(n,t){var r;return function(){return--n>0&&(r=t.apply(this,arguments)),1>=n&&(t=null),r}},m.once=m.partial(m.before,2);var M=!{toString:null}.propertyIsEnumerable("toString"),I=["valueOf","isPrototypeOf","toString","propertyIsEnumerable","hasOwnProperty","toLocaleString"];m.keys=function(n){if(!m.isObject(n))return[];if(v)return v(n);var t=[];for(var r in n)m.has(n,r)&&t.push(r);return M&&e(n,t),t},m.allKeys=function(n){if(!m.isObject(n))return[];var t=[];for(var r in n)t.push(r);return M&&e(n,t),t},m.values=function(n){for(var t=m.keys(n),r=t.length,e=Array(r),u=0;r>u;u++)e[u]=n[t[u]];return e},m.mapObject=function(n,t,r){t=x(t,r);for(var e,u=m.keys(n),i=u.length,o={},a=0;i>a;a++)e=u[a],o[e]=t(n[e],e,n);return o},m.pairs=function(n){for(var t=m.keys(n),r=t.length,e=Array(r),u=0;r>u;u++)e[u]=[t[u],n[t[u]]];return e},m.invert=function(n){for(var t={},r=m.keys(n),e=0,u=r.length;u>e;e++)t[n[r[e]]]=r[e];return t},m.functions=m.methods=function(n){var t=[];for(var r in n)m.isFunction(n[r])&&t.push(r);return t.sort()},m.extend=_(m.allKeys),m.extendOwn=m.assign=_(m.keys),m.findKey=function(n,t,r){t=x(t,r);for(var e,u=m.keys(n),i=0,o=u.length;o>i;i++)if(e=u[i],t(n[e],e,n))return e},m.pick=function(n,t,r){var e,u,i={},o=n;if(null==o)return i;m.isFunction(t)?(u=m.allKeys(o),e=b(t,r)):(u=S(arguments,!1,!1,1),e=function(n,t,r){return t in r},o=Object(o));for(var a=0,c=u.length;c>a;a++){var f=u[a],l=o[f];e(l,f,o)&&(i[f]=l)}return i},m.omit=function(n,t,r){if(m.isFunction(t))t=m.negate(t);else{var e=m.map(S(arguments,!1,!1,1),String);t=function(n,t){return!m.contains(e,t)}}return m.pick(n,t,r)},m.defaults=_(m.allKeys,!0),m.create=function(n,t){var r=j(n);return t&&m.extendOwn(r,t),r},m.clone=function(n){return m.isObject(n)?m.isArray(n)?n.slice():m.extend({},n):n},m.tap=function(n,t){return t(n),n},m.isMatch=function(n,t){var r=m.keys(t),e=r.length;if(null==n)return!e;for(var u=Object(n),i=0;e>i;i++){var o=r[i];if(t[o]!==u[o]||!(o in u))return!1}return!0};var N=function(n,t,r,e){if(n===t)return 0!==n||1/n===1/t;if(null==n||null==t)return n===t;n instanceof m&&(n=n._wrapped),t instanceof m&&(t=t._wrapped);var u=s.call(n);if(u!==s.call(t))return!1;switch(u){case"[object RegExp]":case"[object String]":return""+n==""+t;case"[object Number]":return+n!==+n?+t!==+t:0===+n?1/+n===1/t:+n===+t;case"[object Date]":case"[object Boolean]":return+n===+t}var i="[object Array]"===u;if(!i){if("object"!=typeof n||"object"!=typeof t)return!1;var o=n.constructor,a=t.constructor;if(o!==a&&!(m.isFunction(o)&&o instanceof o&&m.isFunction(a)&&a instanceof a)&&"constructor"in n&&"constructor"in t)return!1}r=r||[],e=e||[];for(var c=r.length;c--;)if(r[c]===n)return e[c]===t;if(r.push(n),e.push(t),i){if(c=n.length,c!==t.length)return!1;for(;c--;)if(!N(n[c],t[c],r,e))return!1}else{var f,l=m.keys(n);if(c=l.length,m.keys(t).length!==c)return!1;for(;c--;)if(f=l[c],!m.has(t,f)||!N(n[f],t[f],r,e))return!1}return r.pop(),e.pop(),!0};m.isEqual=function(n,t){return N(n,t)},m.isEmpty=function(n){return null==n?!0:k(n)&&(m.isArray(n)||m.isString(n)||m.isArguments(n))?0===n.length:0===m.keys(n).length},m.isElement=function(n){return!(!n||1!==n.nodeType)},m.isArray=h||function(n){return"[object Array]"===s.call(n)},m.isObject=function(n){var t=typeof n;return"function"===t||"object"===t&&!!n},m.each(["Arguments","Function","String","Number","Date","RegExp","Error"],function(n){m["is"+n]=function(t){return s.call(t)==="[object "+n+"]"}}),m.isArguments(arguments)||(m.isArguments=function(n){return m.has(n,"callee")}),"function"!=typeof/./&&"object"!=typeof Int8Array&&(m.isFunction=function(n){return"function"==typeof n||!1}),m.isFinite=function(n){return isFinite(n)&&!isNaN(parseFloat(n))},m.isNaN=function(n){return m.isNumber(n)&&n!==+n},m.isBoolean=function(n){return n===!0||n===!1||"[object Boolean]"===s.call(n)},m.isNull=function(n){return null===n},m.isUndefined=function(n){return n===void 0},m.has=function(n,t){return null!=n&&p.call(n,t)},m.noConflict=function(){return u._=i,this},m.identity=function(n){return n},m.constant=function(n){return function(){return n}},m.noop=function(){},m.property=w,m.propertyOf=function(n){return null==n?function(){}:function(t){return n[t]}},m.matcher=m.matches=function(n){return n=m.extendOwn({},n),function(t){return m.isMatch(t,n)}},m.times=function(n,t,r){var e=Array(Math.max(0,n));t=b(t,r,1);for(var u=0;n>u;u++)e[u]=t(u);return e},m.random=function(n,t){return null==t&&(t=n,n=0),n+Math.floor(Math.random()*(t-n+1))},m.now=Date.now||function(){return(new Date).getTime()};var B={"&":"&","<":"<",">":">",'"':""","'":"'","`":"`"},T=m.invert(B),R=function(n){var t=function(t){return n[t]},r="(?:"+m.keys(n).join("|")+")",e=RegExp(r),u=RegExp(r,"g");return function(n){return n=null==n?"":""+n,e.test(n)?n.replace(u,t):n}};m.escape=R(B),m.unescape=R(T),m.result=function(n,t,r){var e=null==n?void 0:n[t];return e===void 0&&(e=r),m.isFunction(e)?e.call(n):e};var q=0;m.uniqueId=function(n){var t=++q+"";return n?n+t:t},m.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var K=/(.)^/,z={"'":"'","\\":"\\","\r":"r","\n":"n","\u2028":"u2028","\u2029":"u2029"},D=/\\|'|\r|\n|\u2028|\u2029/g,L=function(n){return"\\"+z[n]};m.template=function(n,t,r){!t&&r&&(t=r),t=m.defaults({},t,m.templateSettings);var e=RegExp([(t.escape||K).source,(t.interpolate||K).source,(t.evaluate||K).source].join("|")+"|$","g"),u=0,i="__p+='";n.replace(e,function(t,r,e,o,a){return i+=n.slice(u,a).replace(D,L),u=a+t.length,r?i+="'+\n((__t=("+r+"))==null?'':_.escape(__t))+\n'":e?i+="'+\n((__t=("+e+"))==null?'':__t)+\n'":o&&(i+="';\n"+o+"\n__p+='"),t}),i+="';\n",t.variable||(i="with(obj||{}){\n"+i+"}\n"),i="var __t,__p='',__j=Array.prototype.join,"+"print=function(){__p+=__j.call(arguments,'');};\n"+i+"return __p;\n";try{var o=new Function(t.variable||"obj","_",i)}catch(a){throw a.source=i,a}var c=function(n){return o.call(this,n,m)},f=t.variable||"obj";return c.source="function("+f+"){\n"+i+"}",c},m.chain=function(n){var t=m(n);return t._chain=!0,t};var P=function(n,t){return n._chain?m(t).chain():t};m.mixin=function(n){m.each(m.functions(n),function(t){var r=m[t]=n[t];m.prototype[t]=function(){var n=[this._wrapped];return f.apply(n,arguments),P(this,r.apply(m,n))}})},m.mixin(m),m.each(["pop","push","reverse","shift","sort","splice","unshift"],function(n){var t=o[n];m.prototype[n]=function(){var r=this._wrapped;return t.apply(r,arguments),"shift"!==n&&"splice"!==n||0!==r.length||delete r[0],P(this,r)}}),m.each(["concat","join","slice"],function(n){var t=o[n];m.prototype[n]=function(){return P(this,t.apply(this._wrapped,arguments))}}),m.prototype.value=function(){return this._wrapped},m.prototype.valueOf=m.prototype.toJSON=m.prototype.value,m.prototype.toString=function(){return""+this._wrapped},"function"==typeof define&&define.amd&&define("underscore",[],function(){return m})}).call(this); +//# sourceMappingURL=underscore-min.map \ No newline at end of file