├── LICENSE ├── README.md ├── about ├── ChromeWebStore_Badge_v2_496x150.png ├── banner-large.jpg ├── banner.jpg ├── demo.gif ├── snapshot-1.jpg ├── snapshot-2.jpg ├── snapshot-3.jpg ├── snapshot-4.jpg ├── snapshot.jpg ├── snapshot.png └── thumb.png └── src ├── assets ├── eye.svg ├── format.svg ├── fs.svg ├── fs_exit.svg └── gears.svg ├── background.js ├── content_script.js ├── devtools.html ├── devtools.js ├── icon_128.png ├── icon_16.png ├── icon_48.png ├── manifest.json ├── panel.html ├── panel.js └── treeView ├── item.svg ├── minus_square.svg ├── plus_square.svg ├── treeView.css └── treeView.js /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Jaume Sanchez 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Three.js Editor Extension for Google Chrome 2 | 3 | A Chrome DevTools extension to manage any three.js project. 4 | 5 | [Install the extension from the Chrome Store](https://chrome.google.com/webstore/detail/threejs-editor-extension/fbgbekpggeldiacgjkacbkkcbjhmakea/) 6 | [![npm](/about/ChromeWebStore_Badge_v2_496x150.png)](https://chrome.google.com/webstore/detail/threejs-editor-extension/fbgbekpggeldiacgjkacbkkcbjhmakea/) 7 | 8 | ![Demo](/about/demo.gif) 9 | 10 | Twin project of [WebGL GLSL Shader Editor Extension for Google Chrome](https://github.com/spite/ShaderEditorExtension) 11 | 12 | ### How to install ### 13 | 14 | While in beta, you can load the extension from disk directly: 15 | - Checkout the repo 16 | - Open Chrome's Extensions page (``Settings / More tools / Extensions``) 17 | - Enable ``Developer Mode`` 18 | - Click on ``Load unpacked extension`... 19 | - Select the folder /src in the checked out project 20 | 21 | Alternatively, you can pack the extension yourself and load by dropping the .crx file in the Extensions page. 22 | 23 | ### How to use ### 24 | 25 | - Browse to a page with three.js content (you can find many here http://threejs.org/ or here https://www.chromeexperiments.com/webgl) 26 | - Open DevTools 27 | - Select the ``Three.js Editor`` tab 28 | - The extension needs to instrument ``THREE``, so the inspected tab has to be reloaded with the script injected. Hit the ``Reload`` button 29 | - If you are developing the page, make sure ``THREE`` is global 30 | - The extension will begin to track Scene and other objects allocations 31 | - Select an object to see its properties and change them 32 | 33 | #### License #### 34 | 35 | MIT licensed 36 | 37 | Copyright (C) 2015 Jaume Sanchez Elias, http://www.clicktorelease.com -------------------------------------------------------------------------------- /about/ChromeWebStore_Badge_v2_496x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/ThreeJSEditorExtension/472412ee1709bb3317270f1884424eb751d2c9f0/about/ChromeWebStore_Badge_v2_496x150.png -------------------------------------------------------------------------------- /about/banner-large.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/ThreeJSEditorExtension/472412ee1709bb3317270f1884424eb751d2c9f0/about/banner-large.jpg -------------------------------------------------------------------------------- /about/banner.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/ThreeJSEditorExtension/472412ee1709bb3317270f1884424eb751d2c9f0/about/banner.jpg -------------------------------------------------------------------------------- /about/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/ThreeJSEditorExtension/472412ee1709bb3317270f1884424eb751d2c9f0/about/demo.gif -------------------------------------------------------------------------------- /about/snapshot-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/ThreeJSEditorExtension/472412ee1709bb3317270f1884424eb751d2c9f0/about/snapshot-1.jpg -------------------------------------------------------------------------------- /about/snapshot-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/ThreeJSEditorExtension/472412ee1709bb3317270f1884424eb751d2c9f0/about/snapshot-2.jpg -------------------------------------------------------------------------------- /about/snapshot-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/ThreeJSEditorExtension/472412ee1709bb3317270f1884424eb751d2c9f0/about/snapshot-3.jpg -------------------------------------------------------------------------------- /about/snapshot-4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/ThreeJSEditorExtension/472412ee1709bb3317270f1884424eb751d2c9f0/about/snapshot-4.jpg -------------------------------------------------------------------------------- /about/snapshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/ThreeJSEditorExtension/472412ee1709bb3317270f1884424eb751d2c9f0/about/snapshot.jpg -------------------------------------------------------------------------------- /about/snapshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/ThreeJSEditorExtension/472412ee1709bb3317270f1884424eb751d2c9f0/about/snapshot.png -------------------------------------------------------------------------------- /about/thumb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/ThreeJSEditorExtension/472412ee1709bb3317270f1884424eb751d2c9f0/about/thumb.png -------------------------------------------------------------------------------- /src/assets/eye.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/format.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/fs.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/fs_exit.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/gears.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/background.js: -------------------------------------------------------------------------------- 1 | // chrome.extension calls 2 | var connections = {}; 3 | 4 | chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) { 5 | // console.log('incoming message from injected script'); 6 | // console.log(request); 7 | 8 | // Messages from content scripts should have sender.tab set 9 | if (sender.tab) { 10 | var tabId = sender.tab.id; 11 | if (tabId in connections) { 12 | connections[tabId].postMessage(request); 13 | } else { 14 | console.log("Tab not found in connection list."); 15 | } 16 | } else { 17 | console.log("sender.tab not defined."); 18 | } 19 | return true; 20 | }); 21 | 22 | chrome.runtime.onConnect.addListener(function(port) { 23 | 24 | // Listen to messages sent from the DevTools page 25 | port.onMessage.addListener(function(request) { 26 | // console.log('incoming message from dev tools page'); 27 | 28 | // Register initial connection 29 | if (request.name == 'init') { 30 | connections[request.tabId] = port; 31 | 32 | port.onDisconnect.addListener(function() { 33 | delete connections[request.tabId]; 34 | }); 35 | 36 | return; 37 | } 38 | }); 39 | 40 | }); 41 | /* 42 | chrome.webNavigation.onCommitted.addListener( function() { 43 | for( var j in connections ) { 44 | connections[ j ].postMessage( { method: 'onCommitted' } ); 45 | } 46 | } ); 47 | 48 | chrome.tabs.onUpdated.addListener( function( tabId ) { 49 | 50 | connections[ tabId ].postMessage( { method: 'onUpdated' } ); 51 | 52 | } );*/ 53 | 54 | chrome.webNavigation.onBeforeNavigate.addListener(function(data) 55 | { 56 | //console.log("onBeforeNavigate: " + data.url + ". Frame: " + data.frameId + ". Tab: " + data.tabId); 57 | }); 58 | 59 | chrome.webNavigation.onCommitted.addListener(function(data) { 60 | 61 | //console.log("onCommitted: " + data.url + ". Frame: " + data.frameId + ". Tab: " + data.tabId); 62 | 63 | if( connections[ data.tabId ] ) { 64 | if( data.frameId === 0 ) { 65 | connections[ data.tabId ].postMessage( { method: 'inject' } ); 66 | } 67 | } 68 | 69 | }); 70 | 71 | chrome.webNavigation.onReferenceFragmentUpdated.addListener(function(data) 72 | { 73 | //console.log("onReferenceFragmentUpdated: " + data.url + ". Frame: " + data.frameId + ". Tab: " + data.tabId); 74 | }); 75 | 76 | chrome.webNavigation.onErrorOccurred.addListener(function(data) 77 | { 78 | //console.log("onErrorOccurred: " + data.url + ". Frame: " + data.frameId + ". Tab: " + data.tabId + ". Error: " + data.error); 79 | }); 80 | 81 | chrome.webNavigation.onReferenceFragmentUpdated.addListener(function(data) 82 | { 83 | //console.log("onReferenceFragmentUpdated: " + data.url + ". Frame: " + data.frameId + ". Tab: " + data.tabId); 84 | }); 85 | 86 | /*chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) 87 | { 88 | //console.log("tabs.onUpdated: " + changeInfo.url + ". Status: " + changeInfo.status + ". Tab: " + tabId); 89 | }); 90 | 91 | chrome.history.onVisited.addListener(function(historyItem) 92 | { 93 | //console.log("history.onVisited: " + historyItem.url); 94 | });*/ 95 | /* 96 | chrome.webNavigation.onCompleted.addListener(function(data) 97 | { 98 | //console.log("onCompleted: " + data.url + ". Frame: " + data.frameId + ". Tab: " + data.tabId); 99 | });*/ 100 | -------------------------------------------------------------------------------- /src/content_script.js: -------------------------------------------------------------------------------- 1 | window.addEventListener('message', function(event) { 2 | 3 | //console.log( 'message ', event ); 4 | 5 | if (event.source !== window) { 6 | return; 7 | } 8 | 9 | var message = event.data; 10 | 11 | // Only accept messages that we know are ours 12 | if (typeof message !== 'object' || message === null ) { 13 | return; 14 | } 15 | 16 | chrome.runtime.sendMessage(message); 17 | }); -------------------------------------------------------------------------------- /src/devtools.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/devtools.js: -------------------------------------------------------------------------------- 1 | // chrome.devtools calls 2 | 3 | chrome.devtools.panels.create( "Three.js Editor", 4 | "icon.png", 5 | "panel.html", 6 | function(panel) { 7 | 8 | // code invoked on panel creation 9 | } 10 | ); 11 | 12 | // Create a connection to the background page 13 | var backgroundPageConnection = chrome.runtime.connect({ 14 | name: 'panel' 15 | }); 16 | 17 | backgroundPageConnection.postMessage({ 18 | name: 'init', 19 | tabId: chrome.devtools.inspectedWindow.tabId 20 | }); 21 | 22 | backgroundPageConnection.onMessage.addListener(function(msg) { 23 | //console.log( 'devtools.js', msg ); 24 | }); -------------------------------------------------------------------------------- /src/icon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/ThreeJSEditorExtension/472412ee1709bb3317270f1884424eb751d2c9f0/src/icon_128.png -------------------------------------------------------------------------------- /src/icon_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/ThreeJSEditorExtension/472412ee1709bb3317270f1884424eb751d2c9f0/src/icon_16.png -------------------------------------------------------------------------------- /src/icon_48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spite/ThreeJSEditorExtension/472412ee1709bb3317270f1884424eb751d2c9f0/src/icon_48.png -------------------------------------------------------------------------------- /src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Three.js Editor Extension", 3 | "version": "1.0.0", 4 | "minimum_chrome_version": "10.0", 5 | "devtools_page": "devtools.html", 6 | "icons": { 7 | "128": "icon_128.png", 8 | "48": "icon_48.png", 9 | "16": "icon_16.png" 10 | }, 11 | "description": "Three.js editor extension", 12 | "background": { 13 | "scripts": [ 14 | "background.js" 15 | ] 16 | }, 17 | "content_scripts": [{ 18 | "matches": [""], 19 | "js": ["content_script.js"], 20 | "run_at": "document_end", 21 | "all_frames": true 22 | } ], 23 | "permissions": [ 24 | "http://*/*", 25 | "https://*/*", 26 | "webNavigation" 27 | ], 28 | "manifest_version": 2, 29 | "content_security_policy": "default-src 'self' chrome-extension-resource: ; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-eval'; connect-src *; frame-src *;", 30 | "web_accessible_resources": [ 31 | "*" 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /src/panel.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 39 | 40 | 41 | 42 | 43 |
44 |

Welcome!

45 |

Three.js scene editor extension
v1.0.0 (beta)

46 |

To start tracking three.js scenes,
the extension needs to reload the page.

47 | 48 |

Bugs, ideas and feedback: GitHub page
@thespite | www.clicktorelease.com

49 |
50 |
51 |

Waiting for scenes to be created...

52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |

Object3D

60 |
UUID
61 |
Name
62 |
Visible
63 |
Position
64 |
Rotation
65 |
Scale
66 |
Cast shadow
67 |
Receive shadow
68 |
Data
69 |
70 |
71 |

Light

72 |
Color
73 |
Ground Color
74 |
Intensity
75 |
Distance
76 |
Angle
77 |
Decay
78 |
Exponent
79 |
80 |
81 |

Camera

82 |
FOV
83 |
Near
84 |
Far
85 |
Aspect
86 |
Left
87 |
Right
88 |
Top
89 |
Bottom
90 |
91 |
92 |
93 |
94 |
95 |

Settings

96 |
97 | 98 | 99 | -------------------------------------------------------------------------------- /src/panel.js: -------------------------------------------------------------------------------- 1 | function f() { 2 | 3 | window.__Injected = true; 4 | 5 | //function log() { console.log( arguments ); } 6 | //function error() { console.error( arguments ); } 7 | function log() {} 8 | function error() {} 9 | 10 | function log( msg ) { logMsg( 'LOG: ' + msg )} 11 | function error( msg ) { logMsg( 'ERROR: ' + msg )} 12 | 13 | function logMsg() { 14 | 15 | var args = []; 16 | for( var j = 0; j < arguments.length; j++ ) { 17 | args.push( arguments[ j ] ); 18 | } 19 | 20 | window.postMessage( { source: 'ThreejsEditor', method: 'log', arguments: args }, '*'); 21 | } 22 | 23 | /* var methods = [ 24 | 'createProgram', 'linkProgram', 'useProgram', 25 | 'createShader', 'shaderSource', 'compileShader', 'attachShader', 'detachShader', 26 | 'getUniformLocation', 27 | 'getAttribLocation', 'vertexAttribPointer', 'enableVertexAttribArray', 'bindAttribLocation', 28 | 'bindBuffer' 29 | ]; 30 | 31 | this.references = {}; 32 | methods.forEach( function( f ) { 33 | this.references[ f ] = WebGLRenderingContext.prototype[ f ]; 34 | }.bind ( this ) );*/ 35 | 36 | function _h( f, c ) { 37 | 38 | return function() { 39 | var res = f.apply( this, arguments ); 40 | res = c.apply( this, [ res, arguments ] ) || res; 41 | return res; 42 | } 43 | 44 | } 45 | 46 | function _h2( f, c ) { 47 | 48 | return function() { 49 | return c.apply( this, arguments ); 50 | } 51 | 52 | } 53 | 54 | var generateUUID = ( function() { 55 | 56 | // http://www.broofa.com/Tools/Math.uuid.htm 57 | 58 | var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split( '' ); 59 | var uuid = new Array( 36 ); 60 | var rnd = 0, r; 61 | 62 | return function () { 63 | 64 | for ( var i = 0; i < 36; i ++ ) { 65 | 66 | if ( i == 8 || i == 13 || i == 18 || i == 23 ) { 67 | 68 | uuid[ i ] = '-'; 69 | 70 | } else if ( i == 14 ) { 71 | 72 | uuid[ i ] = '4'; 73 | 74 | } else { 75 | 76 | if ( rnd <= 0x02 ) rnd = 0x2000000 + ( Math.random() * 0x1000000 ) | 0; 77 | r = rnd & 0xf; 78 | rnd = rnd >> 4; 79 | uuid[ i ] = chars[ ( i == 19 ) ? ( r & 0x3 ) | 0x8 : r ]; 80 | 81 | } 82 | } 83 | 84 | return uuid.join( '' ); 85 | 86 | }; 87 | 88 | } )(); 89 | 90 | function sniffType( object ) { 91 | 92 | for( var j in types ) { 93 | if( object instanceof THREE[ types[ j ] ] ) { 94 | return types[ j ]; 95 | } 96 | } 97 | 98 | debugger; // dafuc? 99 | 100 | } 101 | 102 | var objects = {}; 103 | var types = []; 104 | 105 | function checkThreeJs() { 106 | 107 | if( window.THREE && window.THREE.REVISION ) { 108 | instrument(); 109 | } else { 110 | setTimeout( checkThreeJs, 10 ); 111 | } 112 | } 113 | 114 | checkThreeJs(); 115 | 116 | function processAddObject( object, parent ) { 117 | 118 | if( !object.uuid ) object.uuid = generateUUID(); 119 | addObject( object, parent ); 120 | 121 | object.children.forEach( function( child ) { 122 | if( child instanceof THREE.Object3D ) { 123 | processAddObject( child, object ); 124 | } 125 | } ); 126 | 127 | } 128 | 129 | function processRemoveObject( object, parent ) { 130 | 131 | removeObject( object, parent ); 132 | 133 | object.children.forEach( function( child ) { 134 | if( child instanceof THREE.Object3D ) { 135 | processRemoveObject( child, object ); 136 | } 137 | } ); 138 | 139 | } 140 | 141 | function instrumentRendererRender( renderer ) { 142 | 143 | var oldRender = renderer.render; 144 | renderer.render = function() { 145 | oldRender.apply( renderer, arguments ); 146 | var scene = arguments[ 0 ]; 147 | var camera = arguments[ 1 ]; 148 | window.postMessage( { source: 'ThreejsEditor', method: 'render', sceneId: scene.uuid, cameraId: camera.uuid }, '*'); 149 | } 150 | 151 | } 152 | 153 | function instrumentLate() { 154 | 155 | var propertiesToIgnore = [ 'webkitStorageInfo', 'webkitIndexedDB' ]; 156 | 157 | for( var j in window ) { 158 | if( propertiesToIgnore.indexOf( j ) >= 0 ) continue; 159 | if( window[ j ] instanceof THREE.WebGLRenderer ) { 160 | logMsg( '++ Existing WebGLRenderer' ); 161 | var object = window[ j ]; 162 | instrumentRendererRender( object ); 163 | } 164 | if( window[ j ] instanceof THREE.Object3D ) { 165 | logMsg( '++ Existing Object3D' ); 166 | var object = window[ j ]; 167 | 168 | processAddObject( object ); 169 | } 170 | } 171 | 172 | } 173 | 174 | function addObject( object, parent ) { 175 | 176 | var type = sniffType( object ); 177 | objects[ object.uuid ] = object; 178 | 179 | if( parent && ( type === 'PerspectiveCamera' || type === 'OrthographicCamera' ) ) { 180 | if( sniffType( parent ) === 'Scene' ) { 181 | return; 182 | } 183 | } 184 | 185 | //logMsg( '++ Adding Object ' + type + ' (' + object.uuid + ') (parent ' + ( parent?parent.uuid:'' ) + ')' ); 186 | window.postMessage( { source: 'ThreejsEditor', method: 'addObject', id: object.uuid, parentId: parent?parent.uuid:null, type: type, label: type, visible: object.visible }, '*'); 187 | 188 | } 189 | 190 | function removeObject( object, parent ) { 191 | 192 | //objects[ object.uuid ] = object; 193 | 194 | var type = sniffType( object ); 195 | //logMsg( '++ Removing Object ' + type + ' (' + object.uuid + ') (parent ' + ( parent?parent.uuid:'' ) + ')' ); 196 | window.postMessage( { source: 'ThreejsEditor', method: 'removeObject', id: object.uuid, parentId: parent?parent.uuid:null }, '*'); 197 | 198 | } 199 | 200 | function extractTypes() { 201 | 202 | for( var j in THREE ) { 203 | if( typeof THREE[ j ] === 'function' ) { 204 | types.unshift( j ); 205 | } 206 | } 207 | 208 | } 209 | 210 | function instrument() { 211 | 212 | extractTypes(); 213 | logMsg( 'INSTRUMENT LATE' ) 214 | instrumentLate(); 215 | logMsg( 'DONE' ); 216 | 217 | THREE.WebGLRenderer = _h( THREE.WebGLRenderer, function() { 218 | logMsg( '++ NEW WebGLRenderer' ); 219 | instrumentRendererRender( this ); 220 | } ); 221 | 222 | var oldObject3D = THREE.Object3D; 223 | THREE.Object3D = _h( THREE.Object3D, function() { 224 | logMsg( '++ NEW Object3D' ); 225 | var object = this; 226 | if( !object.uuid ) object.uuid = generateUUID(); 227 | addObject( object ); 228 | } ); 229 | THREE.Object3D.prototype = oldObject3D.prototype; 230 | for( var j in oldObject3D ) { 231 | if( oldObject3D.hasOwnProperty( j ) ) { 232 | THREE.Object3D[ j ] = oldObject3D[ j ]; 233 | } 234 | } 235 | 236 | THREE.Object3D.prototype.add = _h( THREE.Object3D.prototype.add, function() { 237 | 238 | var parent = this; 239 | if( !parent.uuid ) parent.uuid = generateUUID(); 240 | for( var j = 0; j < arguments[ 1 ].length; j++ ) { 241 | logMsg( '++ Object3D.add' ); 242 | var object = arguments[ 1 ][ j ]; 243 | if( !object.uuid ) object.uuid = generateUUID(); 244 | processAddObject( object, parent ); 245 | } 246 | 247 | } ); 248 | 249 | THREE.Object3D.prototype.remove = _h( THREE.Object3D.prototype.remove, function() { 250 | 251 | var parent = this; 252 | for( var j = 0; j < arguments[ 1 ].length; j++ ) { 253 | logMsg( '++ Object3D.remove' ); 254 | var object = arguments[ 1 ][ j ]; 255 | processRemoveObject( object, parent ); 256 | } 257 | 258 | } ); 259 | 260 | THREE.WebGLRenderer.prototype.render = _h( THREE.WebGLRenderer.prototype.render, function() { 261 | 262 | processAddObject( object, parent ); 263 | 264 | } ); 265 | 266 | } 267 | 268 | /* 269 | Object3D: uuid, name, parent, position, rotation, scale, visible, data 270 | Mesh: uuid, name, parent, position, rotation, scale, visible, data, geometry, material 271 | 272 | PointLight: uuid, name, parent, position, intensity, color, distance, decay, visible, data 273 | SpotLight: uuid, name, parent, position, intensity, color, distance, angle, exponent, decay, visible, data 274 | DirectionalLight: uuid, name, parent, position, intensity, color, visible, data 275 | HemisphereLight: uuid, name, parent, position, intensity, color, ground color, visible, data 276 | AmbientLight: uuid, name, parent, position, color, visible, data 277 | PerspectiveCamera: uuid, name, parent, position, rotation, scale, fov, near, far, visible, data 278 | */ 279 | 280 | var fields = { 281 | 'uuid': { type: 'ti' }, 282 | 'name': { type: 'ti' }, 283 | 'position': { type: 'v3' }, 284 | 'rotation': { type: 'v3' }, 285 | 'scale': { type: 'v3' }, 286 | 'visible': { type: 'b' }, 287 | 'userData': { type: 'tj' }, 288 | 'intensity': { type: 'f' }, 289 | 'color': { type: 'c' }, 290 | 'groundColor': { type: 'c' }, 291 | 'distance': { type: 'f' }, 292 | 'angle': { type: 'f' }, 293 | 'decay': { type: 'f' }, 294 | 'exponent': { type: 'f' }, 295 | 'fov': { type: 'f' }, 296 | 'near': { type: 'f' }, 297 | 'far': { type: 'f' }, 298 | 'left': { type: 'f' }, 299 | 'right': { type: 'f' }, 300 | 'top': { type: 'f' }, 301 | 'bottom': { type: 'f' }, 302 | 'aspect': { type: 'f' }, 303 | 'castShadow': { type: 'b' }, 304 | 'receiveShadow': { type: 'b' } 305 | } 306 | 307 | var categories = { 308 | 'object3d': [ 'uuid', 'name', 'visible', 'position', 'rotation', 'scale', 'userData', 'castShadow', 'receiveShadow' ], 309 | 'light': [ 'intensity', 'color', 'groundColor', 'distance', 'angle', 'decay', 'exponent' ], 310 | 'camera': [ 'fov', 'near', 'far', 'left', 'right', 'top', 'bottom', 'aspect' ] 311 | } 312 | 313 | var properties = { 314 | 'Object3D': [ 'uuid', 'name', 'position', 'visible', 'userData', 'castShadow', 'receiveShadow' ], 315 | 'Mesh': [ 'rotation', 'scale' ], 316 | 'PointCloud': [ 'rotation', 'scale' ], 317 | 'PointLight': [ 'intensity', 'color', 'distance', 'decay' ], 318 | 'SpotLight': [ 'intensity', 'color' ], 319 | 'HemisphereLight': [ 'intensity', 'color', 'groundColor' ], 320 | 'DirectionalLight': [ 'intensity', 'color' ], 321 | 'AmbientLight': [ 'color' ], 322 | 'PerspectiveCamera': [ 'rotation', 'scale', 'fov', 'near', 'far', 'aspect' ], 323 | 'OrthographicCamera': [ 'left', 'right', 'top', 'bottom', 'near', 'far' ] 324 | } 325 | 326 | window.UISelect = function( id ) { 327 | 328 | var o = objects[ id ]; 329 | var data = { 330 | id: o.id, 331 | }; 332 | 333 | var p = []; 334 | for( var j in properties ) { 335 | if( o instanceof THREE[ j ] ) { 336 | logMsg( j ); 337 | for( var i in properties[ j ] ) { 338 | var property = properties[ j ][ i ]; 339 | var type = fields[ property ].type; 340 | logMsg( property, type ); 341 | switch( type ) { 342 | case 'f': 343 | case 't': 344 | case 'ti': 345 | case 'b': 346 | data[ property ] = o[ property ]; 347 | break; 348 | case 'tj': 349 | data[ property ] = JSON.stringify( o[ property ] ); 350 | break; 351 | case 'v3': 352 | data[ property ] = { 353 | x: o[ property ].x, 354 | y: o[ property ].y, 355 | z: o[ property ].z, 356 | }; 357 | break; 358 | case 'c': 359 | data[ property ] = o[ property ].getHexString(); 360 | break; 361 | } 362 | //data[ property ][ 'instance' ] = j; 363 | } 364 | } 365 | } 366 | 367 | logMsg( JSON.stringify( data ) ); 368 | 369 | window.postMessage( { source: 'ThreejsEditor', method: 'objectSelected', id: id, data: JSON.stringify( data ) }, '*'); 370 | 371 | } 372 | 373 | window.ChangeProperty = function( id, data ) { 374 | 375 | logMsg( JSON.stringify( data ) ); 376 | 377 | var o = objects[ id ]; 378 | var dataFields = data.property.split( '-' ); 379 | var v = o; 380 | for( var j = 1; j < dataFields.length; j++ ) { 381 | if( j === dataFields.length - 1 ) { 382 | var f = fields[ dataFields[ j ] ]; 383 | if( f && f.type === 'c' ) { 384 | v[ dataFields[ j ] ].set( data.value ); 385 | } else { 386 | if( f && f.type === 'tj' ) { 387 | v[ dataFields[ j ] ] = JSON.parse( data.value ); 388 | } else { 389 | v[ dataFields[ j ] ] = data.value; 390 | } 391 | } 392 | } else { 393 | v = v[ dataFields[ j ] ]; 394 | } 395 | } 396 | 397 | if( dataFields[ 0 ] === 'camera' ) { 398 | o.updateProjectionMatrix(); 399 | if( o instanceof THREE.PerspectiveCamera ) { 400 | } 401 | if( o instanceof THREE.OrthographicCamera ) { 402 | } 403 | } 404 | 405 | } 406 | 407 | window.addEventListener( 'load', function() { 408 | window.postMessage( { source: 'ThreejsEditor', method: 'init' }, '*'); 409 | window.postMessage( { source: 'ThreejsEditor', method: 'activateFields', fields: JSON.stringify( fields ), categories: JSON.stringify( categories ) }, '*'); 410 | } ); 411 | 412 | } 413 | 414 | var links = document.querySelectorAll( 'a[rel=external]' ); 415 | for( var j = 0; j < links.length; j++ ) { 416 | var a = links[ j ]; 417 | a.addEventListener( 'click', function( e ) { 418 | window.open( this.href, '_blank' ); 419 | e.preventDefault(); 420 | }, false ); 421 | } 422 | 423 | var button = document.getElementById( 'reload' ), 424 | container = document.getElementById( 'container' ), 425 | info = document.getElementById( 'info' ), 426 | waiting = document.getElementById( 'waiting' ), 427 | log = document.getElementById( 'log' ), 428 | ul = document.querySelector( '#container ul' ), 429 | treeViewContainer = document.getElementById( 'treeView' ); 430 | 431 | var verbose = !true; 432 | if( verbose ) { 433 | log.style.left = '50%'; 434 | log.style.display = 'block'; 435 | container.style.right= '50%'; 436 | log.addEventListener( 'click', function() { 437 | this.textContent = ''; 438 | } ); 439 | } 440 | 441 | function logMsg() { 442 | 443 | var args = []; 444 | for( var j = 0; j < arguments.length; j++ ) { 445 | args.push( arguments[ j ] ); 446 | } 447 | var p = document.createElement( 'p' ); 448 | p.textContent = args.join( ' ' ); 449 | log.appendChild( p ); 450 | 451 | } 452 | 453 | logMsg( 'starting' ); 454 | 455 | button.addEventListener( 'click', function( e ) { 456 | chrome.devtools.inspectedWindow.reload( { 457 | ignoreCache: false 458 | } ); 459 | } ); 460 | 461 | var backgroundPageConnection = chrome.runtime.connect({ 462 | name: 'panel' 463 | }); 464 | 465 | backgroundPageConnection.postMessage({ 466 | name: 'init', 467 | tabId: chrome.devtools.inspectedWindow.tabId 468 | }); 469 | 470 | var settings = { 471 | } 472 | 473 | /*var stored = chrome.storage.sync.get( 'highlight', function( i ) { 474 | 475 | logMsg( 'retrieved' ); 476 | logMsg( i ); 477 | settings.highlight = i; 478 | document.getElementById( 'highlightButton' ).style.opacity = settings.highlight ? 1 : .5; 479 | 480 | } );*/ 481 | 482 | var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; 483 | 484 | function encodeSource(input) { 485 | var str = String(input); 486 | for ( 487 | var block, charCode, idx = 0, map = chars, output = ''; 488 | str.charAt(idx | 0) || (map = '=', idx % 1); 489 | output += map.charAt(63 & block >> 8 - idx % 1 * 8) 490 | ) { 491 | charCode = str.charCodeAt(idx += 3/4); 492 | if (charCode > 0xFF) { 493 | throw new InvalidCharacterError("'btoa' failed: The string to be encoded contains characters outside of the Latin1 range."); 494 | } 495 | block = block << 8 | charCode; 496 | } 497 | return output; 498 | } 499 | 500 | function tearDown() { 501 | 502 | log.textContent = ''; 503 | logMsg( '>> tear down' ); 504 | 505 | while( treeViewContainer.firstChild ) treeViewContainer.removeChild( treeViewContainer.firstChild ); 506 | objects = {}; 507 | scenes = {}; 508 | 509 | treeView = new TreeView( treeViewContainer ); 510 | treeView.onSelect = function( id ) { 511 | logMsg( 'SELECTED ' + id ); 512 | chrome.devtools.inspectedWindow.eval( 'UISelect( \'' + id + '\' )' ); 513 | } 514 | r = new TreeViewItem( 'Renderer', null ).setVisible( true ); 515 | treeView.getRoot().appendChild( r ); 516 | 517 | for( var i in categories ){ 518 | var el = document.getElementById( i + '-panel' ); 519 | el.style.display = 'none'; 520 | } 521 | 522 | } 523 | 524 | var objects = {}; 525 | var scenes = {}; 526 | var treeView = null; 527 | var r = null; 528 | var currentObject = null; 529 | 530 | var panel = {}; 531 | var fields = {}; 532 | var categories = {}; 533 | 534 | function hashCode(str) { // java String#hashCode 535 | var hash = 0; 536 | for (var i = 0; i < str.length; i++) { 537 | hash = str.charCodeAt(i) + ((hash << 5) - hash); 538 | } 539 | return hash; 540 | } 541 | 542 | function intToRGB(i){ 543 | var c = (i & 0x00FFFFFF) 544 | .toString(16) 545 | .toUpperCase(); 546 | 547 | return "00000".substring(0, 6 - c.length) + c; 548 | } 549 | 550 | function parseFields( ) { 551 | 552 | var category = null; 553 | 554 | for( var j in fields ) { 555 | var t = fields[ j ].type; 556 | for( var i in categories ) { 557 | var c = i; 558 | for( var k in categories[ i ] ) { 559 | if( categories[ i ][ k ] === j ) { 560 | category = c; 561 | } 562 | } 563 | } 564 | 565 | var id = category + '-' + j; 566 | logMsg( j, t, category, id ); 567 | 568 | switch( t ) { 569 | case 't': 570 | case 'ti': 571 | case 'f': 572 | panel[ j ] = document.getElementById( id ); 573 | ( function( id ) { panel[ j ].addEventListener( 'change', function( e ) { 574 | chrome.devtools.inspectedWindow.eval( 'ChangeProperty( \'' + currentObject.id + '\', { property: \'' + id + '\', value: ' + this.value + ' } )' ); 575 | } ) } )( id ); 576 | break; 577 | case 'tj': 578 | panel[ j ] = document.getElementById( id ); 579 | ( function( id ) { panel[ j ].addEventListener( 'change', function( e ) { 580 | chrome.devtools.inspectedWindow.eval( 'ChangeProperty( \'' + currentObject.id + '\', { property: \'' + id + '\', value: \'' + this.value + '\' } )' ); 581 | } ) } )( id ); 582 | break; 583 | case 'c': 584 | panel[ j ] = document.getElementById( id ); 585 | ( function( id ) { panel[ j ].addEventListener( 'change', function( e ) { 586 | var c = this.value.toString(); 587 | chrome.devtools.inspectedWindow.eval( 'ChangeProperty( \'' + currentObject.id + '\', { property: \'' + id + '\', value: \'' + c + '\' } )' ); 588 | } ) } )( id ); 589 | break; 590 | case 'b': 591 | panel[ j ] = document.getElementById( id ); 592 | ( function( id ) { panel[ j ].addEventListener( 'change', function( e ) { 593 | chrome.devtools.inspectedWindow.eval( 'ChangeProperty( \'' + currentObject.id + '\', { property: \'' + id + '\', value: ' + this.checked + ' } )' ); 594 | } ) } )( id ); 595 | break; 596 | case 'v3': 597 | panel[ j + 'x' ] = document.getElementById( id + '-x' ); 598 | panel[ j + 'y' ] = document.getElementById( id + '-y' ); 599 | panel[ j + 'z' ] = document.getElementById( id + '-z' ); 600 | ( function( id ) { panel[ j + 'x' ].addEventListener( 'change', function( e ) { 601 | chrome.devtools.inspectedWindow.eval( 'ChangeProperty( \'' + currentObject.id + '\', { property: \'' + id + '-x\', value: ' + this.value + ' } )' ); 602 | } ) } )( id ); 603 | ( function( id ) { panel[ j + 'y' ].addEventListener( 'change', function( e ) { 604 | chrome.devtools.inspectedWindow.eval( 'ChangeProperty( \'' + currentObject.id + '\', { property: \'' + id + '-y\', value: ' + this.value + ' } )' ); 605 | } ) } )( id ); 606 | ( function( id ) { panel[ j + 'z' ].addEventListener( 'change', function( e ) { 607 | chrome.devtools.inspectedWindow.eval( 'ChangeProperty( \'' + currentObject.id + '\', { property: \'' + id + '-z\', value: ' + this.value + ' } )' ); 608 | } ) } )( id ); 609 | break; 610 | } 611 | 612 | } 613 | 614 | logMsg( 'PANEL', JSON.stringify( panel ) ); 615 | 616 | } 617 | 618 | backgroundPageConnection.onMessage.addListener( function( msg ) { 619 | 620 | switch( msg.method ) { 621 | case 'activateFields': 622 | fields = JSON.parse( msg.fields ); 623 | categories = JSON.parse( msg.categories ); 624 | parseFields(); 625 | break; 626 | case 'inject': 627 | logMsg( '>> inject' ); 628 | tearDown(); 629 | logMsg( chrome.devtools.inspectedWindow.eval( '(' + f.toString() + ')()' ) ); 630 | break; 631 | case 'init': 632 | logMsg( '>> init' ); 633 | info.style.display = 'none'; 634 | waiting.style.display = 'none'; 635 | container.style.display = 'block'; 636 | break; 637 | case 'addObject': 638 | //logMsg( '>> ADD OBJECT', JSON.stringify( msg ) ); 639 | logMsg( '>> ADD OBJECT', msg.type, msg.id, msg.parentId ); 640 | 641 | //logMsg( ' -- OBJECTS RIGHT NOW: ', JSON.stringify( objects ) ); 642 | if( objects[ msg.id ] === undefined ) { 643 | 644 | var n = new TreeViewItem( msg.label, msg.id ).setVisible( msg.visible ); 645 | data = { 646 | type: msg.type, 647 | node: n 648 | } 649 | 650 | objects[ msg.id ] = { 651 | id: msg.id, 652 | parent: msg.parentId, 653 | data: data 654 | } 655 | //logMsg( '>> ADDED' ); 656 | } 657 | 658 | if( msg.parentId ) { 659 | objects[ msg.id ].data.parent = msg.parentId; 660 | } 661 | 662 | if( msg.parentId ) { 663 | //logMsg( '>> CONNECT #', objects[ msg.parentId ], '#', objects[ msg.id ], '#' ); 664 | if( objects[ msg.id ].data.node.parentNode ) objects[ msg.id ].data.node.parentNode.removeChild( objects[ msg.id ].data.node ); 665 | objects[ msg.parentId ].data.node.appendChild( objects[ msg.id ].data.node ); 666 | objects[ msg.id ].parent = msg.parentId; 667 | } else { 668 | if( !objects[ msg.id ].data.node.parentNode ) r.appendChild( objects[ msg.id ].data.node ); 669 | } 670 | //logMsg( '>> DONE' ); 671 | break; 672 | case 'removeObject': 673 | logMsg( '>> REMOVE OBJECT', msg.id ); 674 | //logMsg( ' -- OBJECTS RIGHT NOW: ', JSON.stringify( objects ) ); 675 | if( objects[ msg.id ] !== undefined ) { 676 | if( objects[ msg.id ].data.node.parentNode ) objects[ msg.id ].data.node.parentNode.removeChild( objects[ msg.id ].data.node ); 677 | objects[ msg.id ] = undefined; 678 | //logMsg( '>> REMOVED' ); 679 | } else { 680 | //logMsg( ' -- CACHED' ); 681 | } 682 | /*if( msg.parentId ) { 683 | logMsg( '>> CONNECT #', objects[ msg.parentId ], '#', objects[ msg.id ], '#' ); 684 | g.setEdge( msg.parentId, msg.id, { lineInterpolate: 'basis', arrowhead: 'normal' } ); 685 | objects[ msg.id ].parent = parentId; 686 | }*/ 687 | logMsg( '>> DONE' ); 688 | break; 689 | case 'objectSelected' : 690 | logMsg( '>>> OBJECT SELECTED' ); 691 | currentObject = objects[ msg.id ]; 692 | var data = JSON.parse( msg.data ); 693 | for( var j in panel ) { 694 | panel[ j ].parentElement.parentElement.style.display = 'none'; 695 | } 696 | for( var i in categories ){ 697 | var el = document.getElementById( i + '-panel' ); 698 | el.style.display = 'none'; 699 | logMsg( 'HIDE ' + i ); 700 | } 701 | 702 | for( var j in data ) { 703 | if( fields[ j ] != undefined ) { 704 | var type = fields[ j ].type; 705 | logMsg( j, panel[ j ], type ); 706 | switch( type ) { 707 | case 't': 708 | case 'ti': 709 | case 'tj': 710 | case 'f': 711 | panel[ j ].value = data[ j ]; 712 | panel[ j ].parentElement.parentElement.style.display = 'block'; 713 | break; 714 | case 'b': 715 | panel[ j ].checked = data[ j ] === true; 716 | panel[ j ].parentElement.parentElement.style.display = 'block'; 717 | break; 718 | case 'c': 719 | panel[ j ].value = '#' + data[ j ]; 720 | panel[ j ].parentElement.parentElement.style.display = 'block'; 721 | break; 722 | case 'v3': 723 | panel[ j + 'x' ].value = data[ j ].x; 724 | panel[ j + 'y' ].value = data[ j ].y; 725 | panel[ j + 'z' ].value = data[ j ].z; 726 | panel[ j + 'x' ].parentElement.parentElement.style.display = 'block'; 727 | panel[ j + 'y' ].parentElement.parentElement.style.display = 'block'; 728 | panel[ j + 'z' ].parentElement.parentElement.style.display = 'block'; 729 | break; 730 | } 731 | } 732 | 733 | for( var i in categories ){ 734 | for (var k in categories[ i ] ) { 735 | if( categories[ i ][ k ] === j ) { 736 | var el = document.getElementById( i + '-panel' ); 737 | el.style.display = 'block'; 738 | logMsg( 'SHOW ' + i ); 739 | } 740 | } 741 | } 742 | } 743 | 744 | break; 745 | case 'render': 746 | /*g.setEdge( msg.cameraId, msg.sceneId, { 747 | lineInterpolate: 'basis', 748 | arrowhead: 'normal', 749 | style: "stroke-dasharray: 5, 5;", 750 | } );*/ 751 | break; 752 | case 'log': 753 | logMsg( msg.arguments ); 754 | break; 755 | } 756 | 757 | } ); 758 | -------------------------------------------------------------------------------- /src/treeView/item.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/treeView/minus_square.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/treeView/plus_square.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/treeView/treeView.css: -------------------------------------------------------------------------------- 1 | ul.treeView li{ margin-left: 1.2em; position: relative;} 2 | ul.treeView li span.icon{ 3 | width: 1em; 4 | height: 1em; 5 | display: block; 6 | float: left; 7 | margin-right: .4em; 8 | margin-top: .2em; 9 | background-size: contain; 10 | background-repeat: no-repeat; 11 | background-position: 50% 50%; 12 | opacity: .6; 13 | position: absolute; 14 | } 15 | ul.treeView li span.button{ 16 | background-image: url(minus_square.svg); 17 | cursor: pointer; 18 | } 19 | ul.treeView li span.item{ 20 | background-image: url(item.svg); 21 | } 22 | ul.treeView{ list-style-type: none; 23 | -webkit-touch-callout: none; 24 | -webkit-user-select: none; 25 | -khtml-user-select: none; 26 | -moz-user-select: none; 27 | -ms-user-select: none;.7 28 | user-select: none; 29 | line-height: 1.5em; 30 | position: absolute; 31 | left: 0; 32 | top: 0; 33 | } 34 | ul.treeView ul{ list-style-type: none } 35 | ul.treeView > li:first-child{ margin-left: .2em;} 36 | ul.treeView li p{ display: inline; cursor: pointer; margin-left: 1.3em;} 37 | ul.treeView li p:hover{ color: #222;} 38 | ul.treeView li.collapsed{ height: 1.5em; overflow: hidden;} 39 | ul.treeView li.collapsed span.button{ background-image: url(plus_square.svg); } 40 | ul.treeView li{ color: #AAA; } 41 | ul.treeView li.visible{ color: #777;} 42 | ul.treeView .active{ font-weight: bold; color: black;} 43 | -------------------------------------------------------------------------------- /src/treeView/treeView.js: -------------------------------------------------------------------------------- 1 | function TreeViewItem( label, id ) { 2 | 3 | this.label = label || ''; 4 | this.children = []; 5 | this.collapsed = false; 6 | this.id = id || null; 7 | 8 | this.li = document.createElement( 'li' ); 9 | var p = document.createElement( 'p' ); 10 | p.textContent = this.label; 11 | this.span = document.createElement( 'span' ); 12 | this.span.className = 'icon item'; 13 | this.li.appendChild( this.span ); 14 | this.li.appendChild( p ); 15 | 16 | this.ul = null; 17 | this.parent = null; 18 | this.parentNode = null; 19 | 20 | var li = this.li; 21 | 22 | p.addEventListener( 'click', function( e ) { 23 | 24 | this.parent.clear(); 25 | p.classList.add( 'active' ); 26 | 27 | this.parent.onSelect( this.id ); 28 | 29 | e.preventDefault(); 30 | 31 | }.bind( this ) ); 32 | 33 | this.span.addEventListener( 'click', function( e ) { 34 | 35 | if( this.children.length ) { 36 | li.classList.toggle( 'collapsed' ); 37 | } 38 | 39 | e.preventDefault(); 40 | 41 | }.bind( this ) ); 42 | 43 | } 44 | 45 | TreeViewItem.prototype.createRootNode = function() { 46 | 47 | this.ul = document.createElement( 'ul' ); 48 | 49 | } 50 | 51 | TreeViewItem.prototype.render = function() { 52 | 53 | 54 | } 55 | 56 | TreeViewItem.prototype.setVisible = function( boolean ) { 57 | 58 | this.li.classList.toggle( 'visible', boolean ); 59 | return this; 60 | 61 | } 62 | 63 | TreeViewItem.prototype.appendChild = function( child ) { 64 | 65 | child.parent = this.parent; 66 | child.parentNode = this; 67 | 68 | if( this.ul === null ) { 69 | this.ul = document.createElement( 'ul' ); 70 | this.li.appendChild( this.ul ); 71 | } 72 | 73 | this.children.push( child ); 74 | this.ul.appendChild( child.li ); 75 | 76 | this.span.className = 'icon button'; 77 | 78 | } 79 | 80 | TreeViewItem.prototype.removeChild = function( child ) { 81 | 82 | child.parent = null; 83 | child.parentNode = null; 84 | 85 | if( this.ul === null ) { 86 | this.li.removeChild( this.ul ); 87 | } 88 | 89 | for( var j = 0; j < this.children.length; j++ ) { 90 | if( this.children[ j ] === child ) { 91 | this.children.splice( j, 1 ); 92 | break; 93 | } 94 | } 95 | this.ul.removeChild( child.li ); 96 | 97 | if( this.children.length ) { 98 | this.span.className = 'icon item'; 99 | } 100 | 101 | } 102 | 103 | function TreeView( base ) { 104 | 105 | this.base = base; 106 | this.root = new TreeViewItem( 'WebGLRenderer'); 107 | this.root.createRootNode(); 108 | this.root.ul.classList.add( 'treeView' ); 109 | this.root.parent = this; 110 | this.base.appendChild( this.root.ul ); 111 | 112 | } 113 | 114 | TreeView.prototype.clear = function() { 115 | 116 | var sel = this.root.ul.querySelector( '.active' ); 117 | if( sel ) sel.classList.remove( 'active' ); 118 | 119 | } 120 | 121 | TreeView.prototype.getRoot = function() { 122 | 123 | return this.root; 124 | 125 | } 126 | 127 | TreeView.prototype.render = function() { 128 | 129 | } 130 | 131 | TreeView.prototype.onSelect = function() { 132 | 133 | } 134 | --------------------------------------------------------------------------------