├── .gitignore
├── README.md
├── assets
├── codechisel-github-image.png
└── codechisel-github-image.psd
├── examples
├── CodeChisel3D_2014-12-16_demo.html
├── simple.html
└── webvr.html
├── lib
└── experiments.js
└── vendor
├── dat.gui.js
├── lively.ast.dev.js
├── lively.lang.dev.js
├── lively.vm.dev-bundle.js
├── lively.vm.dev.js
├── mousetrap.js
├── three-codeeditor
├── codeeditor3d.dev.js
├── keybinding-emacs.js
├── mode-javascript.js
├── snippets
│ ├── javascript.js
│ └── text.js
└── theme-twilight.js
├── three-world-with-tquery.dev.js
└── three
├── OrbitControls.js
├── TransformControls.js
├── VRControls.js
├── VREffect.js
├── three.js
└── tquery.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | A 3D live programming experiment.
4 |
5 | For more info see the [project page](https://robert.kra.hn/projects/live-programming-with-three-and-webvr).
6 |
--------------------------------------------------------------------------------
/assets/codechisel-github-image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cdglabs/CodeChisel3D/67db648bbef0ffe8614b15d5d05369fd83ac3cab/assets/codechisel-github-image.png
--------------------------------------------------------------------------------
/assets/codechisel-github-image.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cdglabs/CodeChisel3D/67db648bbef0ffe8614b15d5d05369fd83ac3cab/assets/codechisel-github-image.psd
--------------------------------------------------------------------------------
/examples/CodeChisel3D_2014-12-16_demo.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CodeChisel3D 2014-12-16 demo
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
153 |
154 |
155 |
156 |
157 |
158 |
--------------------------------------------------------------------------------
/examples/simple.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Simple THREE codeeditor setup
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
--------------------------------------------------------------------------------
/examples/webvr.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Simple THREE codeeditor setup
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/lib/experiments.js:
--------------------------------------------------------------------------------
1 | // "imports"
2 |
3 | var pickingRay = THREE.CodeEditor.raycasting.pickingRay;
4 | var getRelativeMouseXYFromEvent = THREE.CodeEditor.raycasting.getRelativeMouseXYFromEvent;
5 | var getRelativeMouseXY = THREE.CodeEditor.raycasting.getRelativeMouseXY;
6 | var pickObjFromDOMEvent = THREE.CodeEditor.raycasting.pickObjFromDOMEvent;
7 | var isFullscreen = THREE.CodeEditor.domevents.isFullscreen;
8 |
9 | var userOptions = lively.lang.obj.extend({
10 | "fullscreen": function() { world.enterFullScreen(); },
11 | "align": "not aligned",
12 | "editor height": codeEditor.getHeight(),
13 | "editor width": codeEditor.getWidth(),
14 | "shoot ray": function() { drawRay({x:0,y:0}); },
15 | "remove rays": removeRays,
16 | "show console": false,
17 | _setConsole: function _setConsole(val) {
18 | gui && gui.saveToLocalStorageIfPossible();
19 | var log = document.querySelector("#log");
20 | if (!log && !val) return;
21 | if (!log) return loadConsoleScript(function() { _setConsole(val); });
22 | var style = log.style;
23 | style.display = val ? "" : "none";
24 | }
25 | }, userOptions || {});
26 |
27 |
28 | // some convenient extension of global objects:
29 | lively.lang.deprecatedLivelyPatches();
30 |
31 | var storage = setupLocalstorage();
32 | storage.restore();
33 | var gui = setupDatGui(gui, userOptions, world, codeEditor);
34 | gui.update();
35 |
36 | var inputState = initEvents(inputState);
37 |
38 |
39 | // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
40 | // events
41 | // -=-=-=-
42 | function initEvents(inputState) {
43 |
44 | inputState = inputState || {
45 | transformControl: null,
46 |
47 | keysPressed: [],
48 |
49 | keyPressed: function(key) {
50 | console.log("[inputState] key pressed: " + key);
51 | lively.lang.arr.pushIfNotIncluded(inputState.keysPressed, key);
52 | },
53 |
54 | keyReleased: function(key) {
55 | console.log("[inputState] key released: " + key);
56 | inputState.keysPressed = lively.lang.arr.without(inputState.keysPressed, key);
57 | },
58 |
59 | metaKeyPressed: function() {
60 | console.log(inputState.keysPressed);
61 | return inputState.keysPressed.indexOf("command") > -1
62 | || inputState.keysPressed.indexOf("ctrl") > -1;
63 | },
64 |
65 | browserMousePosition: {x:0,y:0},
66 |
67 | get mouseHandler() { return this._mouseHandler; },
68 | set mouseHandler(val) {
69 | console.log("[inputState] mouseHandler changed: %s -> %s",
70 | printmouseHandler(this._mouseHandler), printmouseHandler(val));
71 | return this._mouseHandler = val;
72 | function printmouseHandler(val) {
73 | if (!val) return "no handler";
74 | else if (val.name) return val.name;
75 | else return "unknown handler " + val;
76 | }
77 | }
78 | }
79 |
80 | var el = world.renderer.domElement;
81 |
82 | function mouseDownRaw(evt) { window.onMouseDown(evt); }
83 | function mouseUpRaw(evt) { window.onMouseUp(evt); }
84 | function mouseMoveRaw(evt) {
85 | inputState.browserMousePosition.x = evt.pageX;
86 | inputState.browserMousePosition.y = evt.pageY;
87 | window.onMouseMove(evt);
88 | }
89 |
90 | el.addEventListener("mousedown", mouseDownRaw, false);
91 | el.addEventListener("mouseup", mouseUpRaw, false);
92 | el.addEventListener("mousemove", mouseMoveRaw, false);
93 |
94 | Mousetrap.bind("ctrl", function(evt) { inputState.keyReleased("ctrl"); }, 'keyup');
95 | Mousetrap.bind("ctrl", function(evt) { inputState.keyPressed("ctrl"); }, 'keydown');
96 | Mousetrap.bind("command", function(evt) { inputState.keyReleased("command"); }, 'keyup');
97 | Mousetrap.bind("command", function(evt) { inputState.keyPressed("command"); }, 'keydown');
98 | Mousetrap.bind("alt", function(evt) { inputState.keyReleased("alt"); }, 'keyup');
99 | Mousetrap.bind("alt", function(evt) { inputState.keyPressed("alt"); }, 'keydown');
100 |
101 | // editor alignment shortcuts
102 | [["alt+2", "center"], ["alt+1", "left"], ["alt+3", "right"]].forEach(function(keyCommand) {
103 | Mousetrap.bind(keyCommand[0], function(evt) {
104 | evt.preventDefault(); evt.stopPropagation();
105 | if (userOptions.align === keyCommand[1]) userOptions.align = "not aligned";
106 | else userOptions.align = keyCommand[1];
107 | gui.update();
108 | }, 'keydown');
109 | });
110 |
111 | Mousetrap.bind("f4", function() { drawRay(); });
112 |
113 | Mousetrap.stopCallback = lively.lang.fun.wrap(Mousetrap.stopCallback, function(proceed, e, element) { return false; })
114 |
115 | // transformControl
116 | inputState.transformControl = new THREE.TransformControls(world.camera, el);
117 |
118 | inputState.transformControl.addEventListener('change', function() {
119 | inputState.transformControl.update();
120 | world.renderer.render( world.scene, world.camera );
121 | });
122 |
123 |
124 | var oc = world.orbitControl;
125 | if (oc) {
126 | // oc.__defineGetter__("enabled", function() { return !codeEditor.aceEditor.isFocused(); });
127 | oc.__defineGetter__("enabled", function() { return inputState.keysPressed.indexOf("alt") > -1; });
128 |
129 | oc.addEventListener('change', function(evt) { console.log('[inputState] orbit change ' + evt.type); })
130 | oc.addEventListener('start', function(evt) { console.log('[inputState] orbit start'); })
131 | oc.addEventListener('end', function(evt) { console.log('[inputState] orbit end'); })
132 | }
133 |
134 | return inputState;
135 | }
136 |
137 | // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
138 | // mouse handlers
139 | // -=-=-=-=-=-=-=-
140 |
141 | // handlers
142 |
143 | function transformControlHandler(evt) {
144 | console.log("meta pressed? " + inputState.metaKeyPressed());
145 | if (!inputState.metaKeyPressed()) return null;
146 |
147 |
148 | var hit = pickObjFromDOMEvent(evt, world.camera, world.scene.children, !!codeEditor.vr);
149 | if (!hit) return null;
150 | world.scene.add(inputState.transformControl);
151 | inputState.transformControl.attach(hit.object);
152 |
153 | var ctrl = inputState.transformControl;
154 |
155 | Mousetrap.bind("q", function(evt) { ctrl.setSpace(ctrl.space == "local" ? "world" : "local"); });
156 | Mousetrap.bind("w", function(evt) { ctrl.setMode("translate"); });
157 | Mousetrap.bind("e", function(evt) { ctrl.setMode("rotate"); });
158 | Mousetrap.bind("r", function(evt) { ctrl.setMode("scale"); });
159 | Mousetrap.bind("-", function(evt) { ctrl.setSize(ctrl.size - 0.1);})
160 | Mousetrap.bind("+", function(evt) { ctrl.setSize(ctrl.size + 0.1);})
161 |
162 | return {
163 | name: "transformControlHandler",
164 | handleMouseDown: function(evt) {
165 | if (!inputState.metaKeyPressed()) { inputState.transformControl.update(); return; }
166 | console.log('[inputState] transform control released');
167 | inputState.transformControl.detach(inputState.transformControl.object);
168 | world.scene.remove(inputState.transformControl);
169 | inputState.mouseHandler = null;
170 |
171 | },
172 | handleMouseMove: function(evt) {
173 | inputState.transformControl.update();
174 | },
175 | handleMouseUp: function(evt) {},
176 | }
177 | }
178 |
179 | function dragHandler(evt) {
180 | evt.preventDefault(); evt.stopPropagation();
181 | var camera = world.camera;
182 | var hit = pickObjFromDOMEvent(evt, world.camera, tQuery('box')._lists, !!codeEditor.vr);
183 | if (!hit) return null;
184 |
185 | var dragSphere = new THREE.Sphere(
186 | camera.position,
187 | camera.position.distanceTo(
188 | hit.object.position.clone()));
189 |
190 | console.log('[inputState] spherical drag handler installed');
191 | return {
192 | name: "dragHandler",
193 | dragTarget: hit.object,
194 | hit: hit,
195 | dragSphere: dragSphere,
196 | handleMouseDown: function(evt) { inputState.mouseHandler = null; },
197 | handleMouseMove: function(evt) {
198 | evt.preventDefault(); evt.stopPropagation();
199 | var coords = getRelativeMouseXYFromEvent(evt, !!codeEditor.vr);
200 | var raycaster = pickingRay(coords, camera)
201 | var dragToPoint = raycaster.ray.intersectSphere(dragSphere);
202 | hit.object.position.copy(dragToPoint);
203 | },
204 | handleMouseUp: function() {
205 | inputState.mouseHandler = null;
206 | console.log('[inputState] spherical drag handler released');
207 | }
208 | }
209 | }
210 |
211 | function onMouseDown(evt) {
212 | console.log("window down");
213 | window.LastEvent = evt;
214 | if (inputState.mouseHandler) inputState.mouseHandler.handleMouseDown(evt);
215 |
216 | if (inputState.mouseHandler) return;
217 |
218 | inputState.mouseHandler = transformControlHandler(evt)
219 | || dragHandler(evt);
220 | }
221 |
222 | function onMouseUp(evt) {
223 | if (inputState.mouseHandler)
224 | inputState.mouseHandler.handleMouseUp(evt);
225 | }
226 |
227 | function onMouseMove(evt) {
228 | if (inputState.mouseHandler)
229 | inputState.mouseHandler.handleMouseMove(evt);
230 | }
231 |
232 | // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
233 | // raycasting
234 | // -=-=-=-=-=-
235 |
236 | function drawRay(coords) {
237 | coords = coords || getRelativeMouseXY(inputState.browserMousePosition.x,inputState.browserMousePosition.y, world.renderer.domElement, !!codeEditor.vr);
238 | var raycaster = pickingRay(coords, world.camera)
239 | var intersection = raycaster.intersectObjects(world.scene.children.withoutAll(world.scene.children.groupByKey("type").Line || []))[0];
240 | var from = randomPointOnSphere(5, raycaster.ray.origin)
241 | var to = intersection ? intersection.point : raycaster.ray.at(10000);
242 | console.log("[RAY] distance: %s, hit: %s", from.distanceTo(to), intersection ? intersection.object.type : 'none');
243 | return tQuery.createLine(from, to).addTo(tQueryWorld)
244 | }
245 |
246 | function removeRays() {
247 | tQuery(world.scene.children.groupByKey("type").Line || []).removeFrom(tQueryWorld)
248 | }
249 |
250 |
251 | // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
252 | // dat gui setup
253 | // -=-=-=-=-=-=-=-
254 |
255 | function setupDatGui(gui, userOptions, world, codeEditor) {
256 | if (gui) gui.destroy();
257 | gui = new dat.GUI({hideable: false});
258 |
259 | gui.update = function() {
260 | var controllers = lively.lang.tree.map(this,
261 | function(ea) { return ea.__controllers; },
262 | function(ea) { return lively.lang.obj.values(ea.__folders); })
263 | lively.lang.chain(controllers).flatten()
264 | .filter(function(ea) { return ["number", "string", "boolean"].indexOf(typeof ea.getValue()) > -1; })
265 | .forEach(function(ea) { ea.setValue(ea.getValue()); });
266 | };
267 |
268 | gui.addUserOptions = function(userOptions, world, codeEditor) {
269 | gui.add(userOptions, "fullscreen");
270 |
271 | var f1 = gui.addFolder('editor')
272 | f1.open();
273 | var f2 = gui.addFolder('debugging');
274 |
275 | f1.add(userOptions, "align", ["not aligned", "left", "right", "center"]).onChange(function(dir) {
276 | if (dir === "not aligned") codeEditor.stopAutoAlignWithCamera();
277 | else codeEditor.autoAlignWithCamera(dir, world.camera);
278 | storage.store();
279 | });
280 |
281 | f1.add(userOptions, 'editor height', 100, window.innerHeight+400).listen().onChange(function(val) {
282 | codeEditor.setHeight(val);
283 | storage.store();
284 | });
285 | var ctl = f1.add(userOptions, 'editor width', 100, window.innerWidth).listen().onChange(function(val) {
286 | codeEditor.setWidth(val);
287 | storage.store();
288 | });
289 |
290 | f2.add(userOptions, "shoot ray");
291 | f2.add(userOptions, "remove rays");
292 | f2.add(userOptions, 'show console').onChange(userOptions._setConsole)
293 | };
294 |
295 | gui.addUserOptions(userOptions, world, codeEditor);
296 |
297 | return gui;
298 | }
299 |
300 |
301 | // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
302 | // local storage of editor content
303 | // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
304 |
305 | function setupLocalstorage() {
306 |
307 | var key = "editor:" + document.URL;
308 | var ed = codeEditor.aceEditor;
309 |
310 | if (codeEditor._autosaveOnChange)
311 | codeEditor.aceEditor.off("change", codeEditor._autosaveOnChange)
312 | codeEditor._autosaveOnChange = function() { lively.lang.fun.debounceNamed("editor-localstorage-process", 1000, store)(); }
313 | codeEditor.aceEditor.on("change", codeEditor._autosaveOnChange);
314 |
315 | window.addEventListener('beforeunload', function(evt) { store(); });
316 |
317 | return {
318 | deleteStoredContent: deleteStoredContent,
319 | getStoredContent: getStoredContent,
320 | restore: restore,
321 | store: store
322 | }
323 |
324 | // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
325 |
326 | function deleteStoredContent() { delete localStorage[key]; }
327 |
328 | function getStoredContent() {
329 | try { var stored = JSON.parse(localStorage[key]); } catch(e) {}
330 | stored = stored || {};
331 | if (!stored.editorContent) stored.editorContent = "";
332 | if (!stored.versions) stored.versions = [];
333 | return stored;
334 | }
335 |
336 | function restore() {
337 | var stored = getStoredContent();
338 |
339 | stored.versions.forEach(function(ea) {
340 | if (!ea || !ea.trim()) return;
341 | ed.setValue(ea);
342 | ed.session.markUndoGroup();
343 | });
344 | if (stored.editorContent && stored.editorContent.trim().length)
345 | ed.setValue(stored.editorContent);
346 |
347 | userOptions = lively.lang.obj.extend(userOptions || {}, stored.userOptions || {});
348 |
349 | console.log("[storage] restored editor content from localStorage");
350 | }
351 |
352 | function store() {
353 | var stored = getStoredContent();
354 | if (stored.versions.length > 50) stored.versions.shift();
355 | if (stored.editorContent && stored.editorContent !== stored.versions[stored.versions.length-1])
356 | stored.versions.push(stored.editorContent);
357 | stored.editorContent = ed.getValue();
358 |
359 | stored.userOptions = userOptions;
360 |
361 | localStorage[key] = JSON.stringify(stored);
362 |
363 | // console.log("[storage] stored editor content to localStorage");
364 | }
365 |
366 | }
367 |
368 |
369 | // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
370 | // some helper
371 |
372 | function randomPointOnSphere(radius, sphereCenter) {
373 | // https://gielberkers.com/evenly-distribute-particles-shape-sphere-threejs/
374 | var x = -1 + Math.random() * 2;
375 | var y = -1 + Math.random() * 2;
376 | var z = -1 + Math.random() * 2;
377 | var d = 1 / Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2) + Math.pow(z, 2));
378 | x *= d;
379 | y *= d;
380 | z *= d;
381 | return new THREE.Vector3(x * radius,y * radius,z * radius).add(sphereCenter);
382 | }
383 |
384 |
385 | function planeAtObjectParallelToCamera(rayHit) {
386 | var hitPos = rayHit.point.clone();
387 | var dist = hitPos.distanceTo(tQuery.v(0,0,0));
388 | // var dist = hitPos.distanceTo(rayHit.object.position.clone());
389 | var vectorToPlane = hitPos.clone().sub(world.camera.position);
390 | var normal = vectorToPlane.negate().normalize()
391 | var plane = new THREE.Plane(normal, dist);
392 | return plane;
393 | }
394 |
395 | function loadConsoleScript(thenDo) {
396 | lively.lang.arr.mapAsyncSeries([
397 | ["https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.js", function() { return !!window.jQuery; }],
398 | ["https://lively-web.org/users/robertkrahn/just-the-core/html-console.js", function() { return !!document.querySelector("#log"); }]
399 | ], function(loadData, _, n) {
400 | var s = document.createElement("script");
401 | s.src = loadData[0];
402 | document.body.appendChild(s);
403 | lively.lang.fun.waitFor(3000, loadData[1], n)
404 | }, thenDo);
405 | }
406 |
--------------------------------------------------------------------------------
/vendor/lively.vm.dev.js:
--------------------------------------------------------------------------------
1 | var isCommonJS = typeof module !== "undefined" && module.require;
2 | var Global = typeof window !== "undefined" ? window : global;
3 | var lang = typeof lively !== "undefined" ? lively.lang : isCommonJS ? module.require("lively.lang") : {};
4 | var ast = typeof lively !== "undefined" && lively.ast ? lively.ast : (isCommonJS ? module.require("lively.ast") : (function() { throw new Error("Cannot find lively.lang") })());
5 | var lv = Global.lively || {};
6 | lv.ast = ast;
7 | lv.lang = lang;
8 |
9 | var env = {
10 | isCommonJS: isCommonJS,
11 | Global: Global,
12 | lively: lv
13 | }
14 |
15 | lang.obj.extend(isCommonJS ? module.exports : Global, env);
16 | ;
17 | /*global module,lively*/
18 |
19 | var exports = typeof module !== "undefined" && module.require ? module.exports : (lively.vm = {});
20 | var lang = typeof module !== "undefined" && module.require ? module.require("lively.lang") : lively.lang;
21 | var ast = typeof module !== "undefined" && module.require ? module.require("lively.ast") : lively.ast;
22 |
23 | var arr = lang.arr;
24 |
25 | lang.obj.extend(exports, {
26 |
27 | transformForVarRecord: function(code, varRecorder, varRecorderName, blacklist, defRangeRecorder) {
28 | // variable declaration and references in the the source code get
29 | // transformed so that they are bound to `varRecorderName` aren't local
30 | // state. THis makes it possible to capture eval results, e.g. for
31 | // inspection, watching and recording changes, workspace vars, and
32 | // incrementally evaluating var declarations and having values bound later.
33 | blacklist = blacklist || [];
34 | var undeclaredToTransform = lang.arr.withoutAll(Object.keys(varRecorder), blacklist),
35 | transformed = ast.transform.replaceTopLevelVarDeclAndUsageForCapturing(
36 | code, {name: varRecorderName, type: "Identifier"},
37 | {ignoreUndeclaredExcept: undeclaredToTransform,
38 | exclude: blacklist, recordDefRanges: !!defRangeRecorder});
39 | code = transformed.source;
40 | if (defRangeRecorder) lang.obj.extend(defRangeRecorder, transformed.defRanges);
41 | return code;
42 | },
43 |
44 | transformSingleExpression: function(code) {
45 | // evaling certain expressions such as single functions or object
46 | // literals will fail or not work as intended. When the code being
47 | // evaluated consists just out of a single expression we will wrap it in
48 | // parens to allow for those cases
49 | try {
50 | var parsed = ast.fuzzyParse(code);
51 | if (parsed.body.length === 1 &&
52 | (parsed.body[0].type === 'FunctionDeclaration'
53 | || parsed.body[0].type === 'BlockStatement')) {
54 | code = '(' + code.replace(/;\s*$/, '') + ')';
55 | }
56 | } catch(e) {
57 | if (typeof lively && lively.Config && lively.Config.showImprovedJavaScriptEvalErrors) $world.logError(e)
58 | else console.error("Eval preprocess error: %s", e.stack || e);
59 | }
60 | return code;
61 | },
62 |
63 | evalCodeTransform: function(code, options) {
64 | var vm = exports,
65 | recorder = options.topLevelVarRecorder,
66 | varRecorderName = options.varRecorderName || '__lvVarRecorder';
67 |
68 | if (recorder) code = vm.transformForVarRecord(
69 | code, recorder, varRecorderName,
70 | options.dontTransform, options.topLevelDefRangeRecorder);
71 | code = vm.transformSingleExpression(code);
72 |
73 | if (options.sourceURL) code += "\n//# sourceURL=" + options.sourceURL.replace(/\s/g, "_");
74 |
75 | return code;
76 | },
77 |
78 | getGlobal: function() {
79 | return (function() { return this; })();
80 | },
81 |
82 | _eval: function(__lvEvalStatement, __lvVarRecorder/*needed as arg for capturing*/) {
83 | return eval(__lvEvalStatement);
84 | },
85 |
86 | runEval: function (code, options, thenDo) {
87 | // The main function where all eval options are configured.
88 | // options can include {
89 | // varRecorderName: STRING, // default is '__lvVarRecorder'
90 | // topLevelVarRecorder: OBJECT,
91 | // context: OBJECT,
92 | // sourceURL: STRING
93 | // }
94 | if (typeof options === 'function' && arguments.length === 2) {
95 | thenDo = options; options = {};
96 | } else if (!options) options = {};
97 |
98 | var vm = exports, result, err,
99 | context = options.context || vm.getGlobal(),
100 | recorder = options.topLevelVarRecorder;
101 |
102 | try {
103 | code = vm.evalCodeTransform(code, options);
104 | typeof $morph !== "undefined" && $morph('log') && ($morph('log').textString = code);
105 | result = vm._eval.call(context, code, recorder);
106 | } catch (e) { err = e; } finally { thenDo(err, result); }
107 | },
108 |
109 | syncEval: function(string, options) {
110 | // See #runEval for options.
111 | // Although the defaul eval is synchronous we assume that the general
112 | // evaluation might not return immediatelly. This makes is possible to
113 | // change the evaluation backend, e.g. to be a remotely attached runtime
114 | var result;
115 | exports.runEval(string, options, function(e, r) { result = e || r; });
116 | return result;
117 | }
118 |
119 | });
120 |
121 | //# sourceMappingURL=lively.vm.dev.js.map
--------------------------------------------------------------------------------
/vendor/mousetrap.js:
--------------------------------------------------------------------------------
1 | /*global define:false */
2 | /**
3 | * Copyright 2013 Craig Campbell
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | *
17 | * Mousetrap is a simple keyboard shortcut library for Javascript with
18 | * no external dependencies
19 | *
20 | * @version 1.4.6
21 | * @url craig.is/killing/mice
22 | */
23 | (function(window, document, undefined) {
24 |
25 | /**
26 | * mapping of special keycodes to their corresponding keys
27 | *
28 | * everything in this dictionary cannot use keypress events
29 | * so it has to be here to map to the correct keycodes for
30 | * keyup/keydown events
31 | *
32 | * @type {Object}
33 | */
34 | var _MAP = {
35 | 8: 'backspace',
36 | 9: 'tab',
37 | 13: 'enter',
38 | 16: 'shift',
39 | 17: 'ctrl',
40 | 18: 'alt',
41 | 20: 'capslock',
42 | 27: 'esc',
43 | 32: 'space',
44 | 33: 'pageup',
45 | 34: 'pagedown',
46 | 35: 'end',
47 | 36: 'home',
48 | 37: 'left',
49 | 38: 'up',
50 | 39: 'right',
51 | 40: 'down',
52 | 45: 'ins',
53 | 46: 'del',
54 | 91: 'meta',
55 | 93: 'meta',
56 | 224: 'meta'
57 | },
58 |
59 | /**
60 | * mapping for special characters so they can support
61 | *
62 | * this dictionary is only used incase you want to bind a
63 | * keyup or keydown event to one of these keys
64 | *
65 | * @type {Object}
66 | */
67 | _KEYCODE_MAP = {
68 | 106: '*',
69 | 107: '+',
70 | 109: '-',
71 | 110: '.',
72 | 111 : '/',
73 | 186: ';',
74 | 187: '=',
75 | 188: ',',
76 | 189: '-',
77 | 190: '.',
78 | 191: '/',
79 | 192: '`',
80 | 219: '[',
81 | 220: '\\',
82 | 221: ']',
83 | 222: '\''
84 | },
85 |
86 | /**
87 | * this is a mapping of keys that require shift on a US keypad
88 | * back to the non shift equivelents
89 | *
90 | * this is so you can use keyup events with these keys
91 | *
92 | * note that this will only work reliably on US keyboards
93 | *
94 | * @type {Object}
95 | */
96 | _SHIFT_MAP = {
97 | '~': '`',
98 | '!': '1',
99 | '@': '2',
100 | '#': '3',
101 | '$': '4',
102 | '%': '5',
103 | '^': '6',
104 | '&': '7',
105 | '*': '8',
106 | '(': '9',
107 | ')': '0',
108 | '_': '-',
109 | '+': '=',
110 | ':': ';',
111 | '\"': '\'',
112 | '<': ',',
113 | '>': '.',
114 | '?': '/',
115 | '|': '\\'
116 | },
117 |
118 | /**
119 | * this is a list of special strings you can use to map
120 | * to modifier keys when you specify your keyboard shortcuts
121 | *
122 | * @type {Object}
123 | */
124 | _SPECIAL_ALIASES = {
125 | 'option': 'alt',
126 | 'command': 'meta',
127 | 'return': 'enter',
128 | 'escape': 'esc',
129 | 'mod': /Mac|iPod|iPhone|iPad/.test(navigator.platform) ? 'meta' : 'ctrl'
130 | },
131 |
132 | /**
133 | * variable to store the flipped version of _MAP from above
134 | * needed to check if we should use keypress or not when no action
135 | * is specified
136 | *
137 | * @type {Object|undefined}
138 | */
139 | _REVERSE_MAP,
140 |
141 | /**
142 | * a list of all the callbacks setup via Mousetrap.bind()
143 | *
144 | * @type {Object}
145 | */
146 | _callbacks = {},
147 |
148 | /**
149 | * direct map of string combinations to callbacks used for trigger()
150 | *
151 | * @type {Object}
152 | */
153 | _directMap = {},
154 |
155 | /**
156 | * keeps track of what level each sequence is at since multiple
157 | * sequences can start out with the same sequence
158 | *
159 | * @type {Object}
160 | */
161 | _sequenceLevels = {},
162 |
163 | /**
164 | * variable to store the setTimeout call
165 | *
166 | * @type {null|number}
167 | */
168 | _resetTimer,
169 |
170 | /**
171 | * temporary state where we will ignore the next keyup
172 | *
173 | * @type {boolean|string}
174 | */
175 | _ignoreNextKeyup = false,
176 |
177 | /**
178 | * temporary state where we will ignore the next keypress
179 | *
180 | * @type {boolean}
181 | */
182 | _ignoreNextKeypress = false,
183 |
184 | /**
185 | * are we currently inside of a sequence?
186 | * type of action ("keyup" or "keydown" or "keypress") or false
187 | *
188 | * @type {boolean|string}
189 | */
190 | _nextExpectedAction = false;
191 |
192 | /**
193 | * loop through the f keys, f1 to f19 and add them to the map
194 | * programatically
195 | */
196 | for (var i = 1; i < 20; ++i) {
197 | _MAP[111 + i] = 'f' + i;
198 | }
199 |
200 | /**
201 | * loop through to map numbers on the numeric keypad
202 | */
203 | for (i = 0; i <= 9; ++i) {
204 | _MAP[i + 96] = i;
205 | }
206 |
207 | /**
208 | * cross browser add event method
209 | *
210 | * @param {Element|HTMLDocument} object
211 | * @param {string} type
212 | * @param {Function} callback
213 | * @returns void
214 | */
215 | function _addEvent(object, type, callback) {
216 | if (object.addEventListener) {
217 | object.addEventListener(type, callback, false);
218 | return;
219 | }
220 |
221 | object.attachEvent('on' + type, callback);
222 | }
223 |
224 | /**
225 | * takes the event and returns the key character
226 | *
227 | * @param {Event} e
228 | * @return {string}
229 | */
230 | function _characterFromEvent(e) {
231 |
232 | // for keypress events we should return the character as is
233 | if (e.type == 'keypress') {
234 | var character = String.fromCharCode(e.which);
235 |
236 | // if the shift key is not pressed then it is safe to assume
237 | // that we want the character to be lowercase. this means if
238 | // you accidentally have caps lock on then your key bindings
239 | // will continue to work
240 | //
241 | // the only side effect that might not be desired is if you
242 | // bind something like 'A' cause you want to trigger an
243 | // event when capital A is pressed caps lock will no longer
244 | // trigger the event. shift+a will though.
245 | if (!e.shiftKey) {
246 | character = character.toLowerCase();
247 | }
248 |
249 | return character;
250 | }
251 |
252 | // for non keypress events the special maps are needed
253 | if (_MAP[e.which]) {
254 | return _MAP[e.which];
255 | }
256 |
257 | if (_KEYCODE_MAP[e.which]) {
258 | return _KEYCODE_MAP[e.which];
259 | }
260 |
261 | // if it is not in the special map
262 |
263 | // with keydown and keyup events the character seems to always
264 | // come in as an uppercase character whether you are pressing shift
265 | // or not. we should make sure it is always lowercase for comparisons
266 | return String.fromCharCode(e.which).toLowerCase();
267 | }
268 |
269 | /**
270 | * checks if two arrays are equal
271 | *
272 | * @param {Array} modifiers1
273 | * @param {Array} modifiers2
274 | * @returns {boolean}
275 | */
276 | function _modifiersMatch(modifiers1, modifiers2) {
277 | return modifiers1.sort().join(',') === modifiers2.sort().join(',');
278 | }
279 |
280 | /**
281 | * resets all sequence counters except for the ones passed in
282 | *
283 | * @param {Object} doNotReset
284 | * @returns void
285 | */
286 | function _resetSequences(doNotReset) {
287 | doNotReset = doNotReset || {};
288 |
289 | var activeSequences = false,
290 | key;
291 |
292 | for (key in _sequenceLevels) {
293 | if (doNotReset[key]) {
294 | activeSequences = true;
295 | continue;
296 | }
297 | _sequenceLevels[key] = 0;
298 | }
299 |
300 | if (!activeSequences) {
301 | _nextExpectedAction = false;
302 | }
303 | }
304 |
305 | /**
306 | * finds all callbacks that match based on the keycode, modifiers,
307 | * and action
308 | *
309 | * @param {string} character
310 | * @param {Array} modifiers
311 | * @param {Event|Object} e
312 | * @param {string=} sequenceName - name of the sequence we are looking for
313 | * @param {string=} combination
314 | * @param {number=} level
315 | * @returns {Array}
316 | */
317 | function _getMatches(character, modifiers, e, sequenceName, combination, level) {
318 | var i,
319 | callback,
320 | matches = [],
321 | action = e.type;
322 |
323 | // if there are no events related to this keycode
324 | if (!_callbacks[character]) {
325 | return [];
326 | }
327 |
328 | // if a modifier key is coming up on its own we should allow it
329 | if (action == 'keyup' && _isModifier(character)) {
330 | modifiers = [character];
331 | }
332 |
333 | // loop through all callbacks for the key that was pressed
334 | // and see if any of them match
335 | for (i = 0; i < _callbacks[character].length; ++i) {
336 | callback = _callbacks[character][i];
337 |
338 | // if a sequence name is not specified, but this is a sequence at
339 | // the wrong level then move onto the next match
340 | if (!sequenceName && callback.seq && _sequenceLevels[callback.seq] != callback.level) {
341 | continue;
342 | }
343 |
344 | // if the action we are looking for doesn't match the action we got
345 | // then we should keep going
346 | if (action != callback.action) {
347 | continue;
348 | }
349 |
350 | // if this is a keypress event and the meta key and control key
351 | // are not pressed that means that we need to only look at the
352 | // character, otherwise check the modifiers as well
353 | //
354 | // chrome will not fire a keypress if meta or control is down
355 | // safari will fire a keypress if meta or meta+shift is down
356 | // firefox will fire a keypress if meta or control is down
357 | if ((action == 'keypress' && !e.metaKey && !e.ctrlKey) || _modifiersMatch(modifiers, callback.modifiers)) {
358 |
359 | // when you bind a combination or sequence a second time it
360 | // should overwrite the first one. if a sequenceName or
361 | // combination is specified in this call it does just that
362 | //
363 | // @todo make deleting its own method?
364 | var deleteCombo = !sequenceName && callback.combo == combination;
365 | var deleteSequence = sequenceName && callback.seq == sequenceName && callback.level == level;
366 | if (deleteCombo || deleteSequence) {
367 | _callbacks[character].splice(i, 1);
368 | }
369 |
370 | matches.push(callback);
371 | }
372 | }
373 |
374 | return matches;
375 | }
376 |
377 | /**
378 | * takes a key event and figures out what the modifiers are
379 | *
380 | * @param {Event} e
381 | * @returns {Array}
382 | */
383 | function _eventModifiers(e) {
384 | var modifiers = [];
385 |
386 | if (e.shiftKey) {
387 | modifiers.push('shift');
388 | }
389 |
390 | if (e.altKey) {
391 | modifiers.push('alt');
392 | }
393 |
394 | if (e.ctrlKey) {
395 | modifiers.push('ctrl');
396 | }
397 |
398 | if (e.metaKey) {
399 | modifiers.push('meta');
400 | }
401 |
402 | return modifiers;
403 | }
404 |
405 | /**
406 | * prevents default for this event
407 | *
408 | * @param {Event} e
409 | * @returns void
410 | */
411 | function _preventDefault(e) {
412 | if (e.preventDefault) {
413 | e.preventDefault();
414 | return;
415 | }
416 |
417 | e.returnValue = false;
418 | }
419 |
420 | /**
421 | * stops propogation for this event
422 | *
423 | * @param {Event} e
424 | * @returns void
425 | */
426 | function _stopPropagation(e) {
427 | if (e.stopPropagation) {
428 | e.stopPropagation();
429 | return;
430 | }
431 |
432 | e.cancelBubble = true;
433 | }
434 |
435 | /**
436 | * actually calls the callback function
437 | *
438 | * if your callback function returns false this will use the jquery
439 | * convention - prevent default and stop propogation on the event
440 | *
441 | * @param {Function} callback
442 | * @param {Event} e
443 | * @returns void
444 | */
445 | function _fireCallback(callback, e, combo, sequence) {
446 |
447 | // if this event should not happen stop here
448 | if (Mousetrap.stopCallback(e, e.target || e.srcElement, combo, sequence)) {
449 | return;
450 | }
451 |
452 | if (callback(e, combo) === false) {
453 | _preventDefault(e);
454 | _stopPropagation(e);
455 | }
456 | }
457 |
458 | /**
459 | * handles a character key event
460 | *
461 | * @param {string} character
462 | * @param {Array} modifiers
463 | * @param {Event} e
464 | * @returns void
465 | */
466 | function _handleKey(character, modifiers, e) {
467 | var callbacks = _getMatches(character, modifiers, e),
468 | i,
469 | doNotReset = {},
470 | maxLevel = 0,
471 | processedSequenceCallback = false;
472 |
473 | // Calculate the maxLevel for sequences so we can only execute the longest callback sequence
474 | for (i = 0; i < callbacks.length; ++i) {
475 | if (callbacks[i].seq) {
476 | maxLevel = Math.max(maxLevel, callbacks[i].level);
477 | }
478 | }
479 |
480 | // loop through matching callbacks for this key event
481 | for (i = 0; i < callbacks.length; ++i) {
482 |
483 | // fire for all sequence callbacks
484 | // this is because if for example you have multiple sequences
485 | // bound such as "g i" and "g t" they both need to fire the
486 | // callback for matching g cause otherwise you can only ever
487 | // match the first one
488 | if (callbacks[i].seq) {
489 |
490 | // only fire callbacks for the maxLevel to prevent
491 | // subsequences from also firing
492 | //
493 | // for example 'a option b' should not cause 'option b' to fire
494 | // even though 'option b' is part of the other sequence
495 | //
496 | // any sequences that do not match here will be discarded
497 | // below by the _resetSequences call
498 | if (callbacks[i].level != maxLevel) {
499 | continue;
500 | }
501 |
502 | processedSequenceCallback = true;
503 |
504 | // keep a list of which sequences were matches for later
505 | doNotReset[callbacks[i].seq] = 1;
506 | _fireCallback(callbacks[i].callback, e, callbacks[i].combo, callbacks[i].seq);
507 | continue;
508 | }
509 |
510 | // if there were no sequence matches but we are still here
511 | // that means this is a regular match so we should fire that
512 | if (!processedSequenceCallback) {
513 | _fireCallback(callbacks[i].callback, e, callbacks[i].combo);
514 | }
515 | }
516 |
517 | // if the key you pressed matches the type of sequence without
518 | // being a modifier (ie "keyup" or "keypress") then we should
519 | // reset all sequences that were not matched by this event
520 | //
521 | // this is so, for example, if you have the sequence "h a t" and you
522 | // type "h e a r t" it does not match. in this case the "e" will
523 | // cause the sequence to reset
524 | //
525 | // modifier keys are ignored because you can have a sequence
526 | // that contains modifiers such as "enter ctrl+space" and in most
527 | // cases the modifier key will be pressed before the next key
528 | //
529 | // also if you have a sequence such as "ctrl+b a" then pressing the
530 | // "b" key will trigger a "keypress" and a "keydown"
531 | //
532 | // the "keydown" is expected when there is a modifier, but the
533 | // "keypress" ends up matching the _nextExpectedAction since it occurs
534 | // after and that causes the sequence to reset
535 | //
536 | // we ignore keypresses in a sequence that directly follow a keydown
537 | // for the same character
538 | var ignoreThisKeypress = e.type == 'keypress' && _ignoreNextKeypress;
539 | if (e.type == _nextExpectedAction && !_isModifier(character) && !ignoreThisKeypress) {
540 | _resetSequences(doNotReset);
541 | }
542 |
543 | _ignoreNextKeypress = processedSequenceCallback && e.type == 'keydown';
544 | }
545 |
546 | /**
547 | * handles a keydown event
548 | *
549 | * @param {Event} e
550 | * @returns void
551 | */
552 | function _handleKeyEvent(e) {
553 |
554 | // normalize e.which for key events
555 | // @see http://stackoverflow.com/questions/4285627/javascript-keycode-vs-charcode-utter-confusion
556 | if (typeof e.which !== 'number') {
557 | e.which = e.keyCode;
558 | }
559 |
560 | var character = _characterFromEvent(e);
561 |
562 | // no character found then stop
563 | if (!character) {
564 | return;
565 | }
566 |
567 | // need to use === for the character check because the character can be 0
568 | if (e.type == 'keyup' && _ignoreNextKeyup === character) {
569 | _ignoreNextKeyup = false;
570 | return;
571 | }
572 |
573 | Mousetrap.handleKey(character, _eventModifiers(e), e);
574 | }
575 |
576 | /**
577 | * determines if the keycode specified is a modifier key or not
578 | *
579 | * @param {string} key
580 | * @returns {boolean}
581 | */
582 | function _isModifier(key) {
583 | return key == 'shift' || key == 'ctrl' || key == 'alt' || key == 'meta';
584 | }
585 |
586 | /**
587 | * called to set a 1 second timeout on the specified sequence
588 | *
589 | * this is so after each key press in the sequence you have 1 second
590 | * to press the next key before you have to start over
591 | *
592 | * @returns void
593 | */
594 | function _resetSequenceTimer() {
595 | clearTimeout(_resetTimer);
596 | _resetTimer = setTimeout(_resetSequences, 1000);
597 | }
598 |
599 | /**
600 | * reverses the map lookup so that we can look for specific keys
601 | * to see what can and can't use keypress
602 | *
603 | * @return {Object}
604 | */
605 | function _getReverseMap() {
606 | if (!_REVERSE_MAP) {
607 | _REVERSE_MAP = {};
608 | for (var key in _MAP) {
609 |
610 | // pull out the numeric keypad from here cause keypress should
611 | // be able to detect the keys from the character
612 | if (key > 95 && key < 112) {
613 | continue;
614 | }
615 |
616 | if (_MAP.hasOwnProperty(key)) {
617 | _REVERSE_MAP[_MAP[key]] = key;
618 | }
619 | }
620 | }
621 | return _REVERSE_MAP;
622 | }
623 |
624 | /**
625 | * picks the best action based on the key combination
626 | *
627 | * @param {string} key - character for key
628 | * @param {Array} modifiers
629 | * @param {string=} action passed in
630 | */
631 | function _pickBestAction(key, modifiers, action) {
632 |
633 | // if no action was picked in we should try to pick the one
634 | // that we think would work best for this key
635 | if (!action) {
636 | action = _getReverseMap()[key] ? 'keydown' : 'keypress';
637 | }
638 |
639 | // modifier keys don't work as expected with keypress,
640 | // switch to keydown
641 | if (action == 'keypress' && modifiers.length) {
642 | action = 'keydown';
643 | }
644 |
645 | return action;
646 | }
647 |
648 | /**
649 | * binds a key sequence to an event
650 | *
651 | * @param {string} combo - combo specified in bind call
652 | * @param {Array} keys
653 | * @param {Function} callback
654 | * @param {string=} action
655 | * @returns void
656 | */
657 | function _bindSequence(combo, keys, callback, action) {
658 |
659 | // start off by adding a sequence level record for this combination
660 | // and setting the level to 0
661 | _sequenceLevels[combo] = 0;
662 |
663 | /**
664 | * callback to increase the sequence level for this sequence and reset
665 | * all other sequences that were active
666 | *
667 | * @param {string} nextAction
668 | * @returns {Function}
669 | */
670 | function _increaseSequence(nextAction) {
671 | return function() {
672 | _nextExpectedAction = nextAction;
673 | ++_sequenceLevels[combo];
674 | _resetSequenceTimer();
675 | };
676 | }
677 |
678 | /**
679 | * wraps the specified callback inside of another function in order
680 | * to reset all sequence counters as soon as this sequence is done
681 | *
682 | * @param {Event} e
683 | * @returns void
684 | */
685 | function _callbackAndReset(e) {
686 | _fireCallback(callback, e, combo);
687 |
688 | // we should ignore the next key up if the action is key down
689 | // or keypress. this is so if you finish a sequence and
690 | // release the key the final key will not trigger a keyup
691 | if (action !== 'keyup') {
692 | _ignoreNextKeyup = _characterFromEvent(e);
693 | }
694 |
695 | // weird race condition if a sequence ends with the key
696 | // another sequence begins with
697 | setTimeout(_resetSequences, 10);
698 | }
699 |
700 | // loop through keys one at a time and bind the appropriate callback
701 | // function. for any key leading up to the final one it should
702 | // increase the sequence. after the final, it should reset all sequences
703 | //
704 | // if an action is specified in the original bind call then that will
705 | // be used throughout. otherwise we will pass the action that the
706 | // next key in the sequence should match. this allows a sequence
707 | // to mix and match keypress and keydown events depending on which
708 | // ones are better suited to the key provided
709 | for (var i = 0; i < keys.length; ++i) {
710 | var isFinal = i + 1 === keys.length;
711 | var wrappedCallback = isFinal ? _callbackAndReset : _increaseSequence(action || _getKeyInfo(keys[i + 1]).action);
712 | _bindSingle(keys[i], wrappedCallback, action, combo, i);
713 | }
714 | }
715 |
716 | /**
717 | * Converts from a string key combination to an array
718 | *
719 | * @param {string} combination like "command+shift+l"
720 | * @return {Array}
721 | */
722 | function _keysFromString(combination) {
723 | if (combination === '+') {
724 | return ['+'];
725 | }
726 |
727 | return combination.split('+');
728 | }
729 |
730 | /**
731 | * Gets info for a specific key combination
732 | *
733 | * @param {string} combination key combination ("command+s" or "a" or "*")
734 | * @param {string=} action
735 | * @returns {Object}
736 | */
737 | function _getKeyInfo(combination, action) {
738 | var keys,
739 | key,
740 | i,
741 | modifiers = [];
742 |
743 | // take the keys from this pattern and figure out what the actual
744 | // pattern is all about
745 | keys = _keysFromString(combination);
746 |
747 | for (i = 0; i < keys.length; ++i) {
748 | key = keys[i];
749 |
750 | // normalize key names
751 | if (_SPECIAL_ALIASES[key]) {
752 | key = _SPECIAL_ALIASES[key];
753 | }
754 |
755 | // if this is not a keypress event then we should
756 | // be smart about using shift keys
757 | // this will only work for US keyboards however
758 | if (action && action != 'keypress' && _SHIFT_MAP[key]) {
759 | key = _SHIFT_MAP[key];
760 | modifiers.push('shift');
761 | }
762 |
763 | // if this key is a modifier then add it to the list of modifiers
764 | if (_isModifier(key)) {
765 | modifiers.push(key);
766 | }
767 | }
768 |
769 | // depending on what the key combination is
770 | // we will try to pick the best event for it
771 | action = _pickBestAction(key, modifiers, action);
772 |
773 | return {
774 | key: key,
775 | modifiers: modifiers,
776 | action: action
777 | };
778 | }
779 |
780 | /**
781 | * binds a single keyboard combination
782 | *
783 | * @param {string} combination
784 | * @param {Function} callback
785 | * @param {string=} action
786 | * @param {string=} sequenceName - name of sequence if part of sequence
787 | * @param {number=} level - what part of the sequence the command is
788 | * @returns void
789 | */
790 | function _bindSingle(combination, callback, action, sequenceName, level) {
791 |
792 | // store a direct mapped reference for use with Mousetrap.trigger
793 | _directMap[combination + ':' + action] = callback;
794 |
795 | // make sure multiple spaces in a row become a single space
796 | combination = combination.replace(/\s+/g, ' ');
797 |
798 | var sequence = combination.split(' '),
799 | info;
800 |
801 | // if this pattern is a sequence of keys then run through this method
802 | // to reprocess each pattern one key at a time
803 | if (sequence.length > 1) {
804 | _bindSequence(combination, sequence, callback, action);
805 | return;
806 | }
807 |
808 | info = _getKeyInfo(combination, action);
809 |
810 | // make sure to initialize array if this is the first time
811 | // a callback is added for this key
812 | _callbacks[info.key] = _callbacks[info.key] || [];
813 |
814 | // remove an existing match if there is one
815 | _getMatches(info.key, info.modifiers, {type: info.action}, sequenceName, combination, level);
816 |
817 | // add this call back to the array
818 | // if it is a sequence put it at the beginning
819 | // if not put it at the end
820 | //
821 | // this is important because the way these are processed expects
822 | // the sequence ones to come first
823 | _callbacks[info.key][sequenceName ? 'unshift' : 'push']({
824 | callback: callback,
825 | modifiers: info.modifiers,
826 | action: info.action,
827 | seq: sequenceName,
828 | level: level,
829 | combo: combination
830 | });
831 | }
832 |
833 | /**
834 | * binds multiple combinations to the same callback
835 | *
836 | * @param {Array} combinations
837 | * @param {Function} callback
838 | * @param {string|undefined} action
839 | * @returns void
840 | */
841 | function _bindMultiple(combinations, callback, action) {
842 | for (var i = 0; i < combinations.length; ++i) {
843 | _bindSingle(combinations[i], callback, action);
844 | }
845 | }
846 |
847 | // start!
848 | _addEvent(document, 'keypress', _handleKeyEvent);
849 | _addEvent(document, 'keydown', _handleKeyEvent);
850 | _addEvent(document, 'keyup', _handleKeyEvent);
851 |
852 | var Mousetrap = {
853 |
854 | /**
855 | * binds an event to mousetrap
856 | *
857 | * can be a single key, a combination of keys separated with +,
858 | * an array of keys, or a sequence of keys separated by spaces
859 | *
860 | * be sure to list the modifier keys first to make sure that the
861 | * correct key ends up getting bound (the last key in the pattern)
862 | *
863 | * @param {string|Array} keys
864 | * @param {Function} callback
865 | * @param {string=} action - 'keypress', 'keydown', or 'keyup'
866 | * @returns void
867 | */
868 | bind: function(keys, callback, action) {
869 | keys = keys instanceof Array ? keys : [keys];
870 | _bindMultiple(keys, callback, action);
871 | return this;
872 | },
873 |
874 | /**
875 | * unbinds an event to mousetrap
876 | *
877 | * the unbinding sets the callback function of the specified key combo
878 | * to an empty function and deletes the corresponding key in the
879 | * _directMap dict.
880 | *
881 | * TODO: actually remove this from the _callbacks dictionary instead
882 | * of binding an empty function
883 | *
884 | * the keycombo+action has to be exactly the same as
885 | * it was defined in the bind method
886 | *
887 | * @param {string|Array} keys
888 | * @param {string} action
889 | * @returns void
890 | */
891 | unbind: function(keys, action) {
892 | return Mousetrap.bind(keys, function() {}, action);
893 | },
894 |
895 | /**
896 | * triggers an event that has already been bound
897 | *
898 | * @param {string} keys
899 | * @param {string=} action
900 | * @returns void
901 | */
902 | trigger: function(keys, action) {
903 | if (_directMap[keys + ':' + action]) {
904 | _directMap[keys + ':' + action]({}, keys);
905 | }
906 | return this;
907 | },
908 |
909 | /**
910 | * resets the library back to its initial state. this is useful
911 | * if you want to clear out the current keyboard shortcuts and bind
912 | * new ones - for example if you switch to another page
913 | *
914 | * @returns void
915 | */
916 | reset: function() {
917 | _callbacks = {};
918 | _directMap = {};
919 | return this;
920 | },
921 |
922 | /**
923 | * should we stop this event before firing off callbacks
924 | *
925 | * @param {Event} e
926 | * @param {Element} element
927 | * @return {boolean}
928 | */
929 | stopCallback: function(e, element) {
930 |
931 | // if the element has the class "mousetrap" then no need to stop
932 | if ((' ' + element.className + ' ').indexOf(' mousetrap ') > -1) {
933 | return false;
934 | }
935 |
936 | // stop for input, select, and textarea
937 | return element.tagName == 'INPUT' || element.tagName == 'SELECT' || element.tagName == 'TEXTAREA' || element.isContentEditable;
938 | },
939 |
940 | /**
941 | * exposes _handleKey publicly so it can be overwritten by extensions
942 | */
943 | handleKey: _handleKey
944 | };
945 |
946 | // expose mousetrap to the global object
947 | window.Mousetrap = Mousetrap;
948 |
949 | // expose mousetrap as an AMD module
950 | if (typeof define === 'function' && define.amd) {
951 | define(Mousetrap);
952 | }
953 | }) (window, document);
954 |
--------------------------------------------------------------------------------
/vendor/three-codeeditor/keybinding-emacs.js:
--------------------------------------------------------------------------------
1 | ace.define("ace/occur",["require","exports","module","ace/lib/oop","ace/range","ace/search","ace/edit_session","ace/search_highlight","ace/lib/dom"], function(require, exports, module) {
2 | "use strict";
3 |
4 | var oop = require("./lib/oop");
5 | var Range = require("./range").Range;
6 | var Search = require("./search").Search;
7 | var EditSession = require("./edit_session").EditSession;
8 | var SearchHighlight = require("./search_highlight").SearchHighlight;
9 | function Occur() {}
10 |
11 | oop.inherits(Occur, Search);
12 |
13 | (function() {
14 | this.enter = function(editor, options) {
15 | if (!options.needle) return false;
16 | var pos = editor.getCursorPosition();
17 | this.displayOccurContent(editor, options);
18 | var translatedPos = this.originalToOccurPosition(editor.session, pos);
19 | editor.moveCursorToPosition(translatedPos);
20 | return true;
21 | }
22 | this.exit = function(editor, options) {
23 | var pos = options.translatePosition && editor.getCursorPosition();
24 | var translatedPos = pos && this.occurToOriginalPosition(editor.session, pos);
25 | this.displayOriginalContent(editor);
26 | if (translatedPos)
27 | editor.moveCursorToPosition(translatedPos);
28 | return true;
29 | }
30 |
31 | this.highlight = function(sess, regexp) {
32 | var hl = sess.$occurHighlight = sess.$occurHighlight || sess.addDynamicMarker(
33 | new SearchHighlight(null, "ace_occur-highlight", "text"));
34 | hl.setRegexp(regexp);
35 | sess._emit("changeBackMarker"); // force highlight layer redraw
36 | }
37 |
38 | this.displayOccurContent = function(editor, options) {
39 | this.$originalSession = editor.session;
40 | var found = this.matchingLines(editor.session, options);
41 | var lines = found.map(function(foundLine) { return foundLine.content; });
42 | var occurSession = new EditSession(lines.join('\n'));
43 | occurSession.$occur = this;
44 | occurSession.$occurMatchingLines = found;
45 | editor.setSession(occurSession);
46 | this.$useEmacsStyleLineStart = this.$originalSession.$useEmacsStyleLineStart;
47 | occurSession.$useEmacsStyleLineStart = this.$useEmacsStyleLineStart;
48 | this.highlight(occurSession, options.re);
49 | occurSession._emit('changeBackMarker');
50 | }
51 |
52 | this.displayOriginalContent = function(editor) {
53 | editor.setSession(this.$originalSession);
54 | this.$originalSession.$useEmacsStyleLineStart = this.$useEmacsStyleLineStart;
55 | }
56 | this.originalToOccurPosition = function(session, pos) {
57 | var lines = session.$occurMatchingLines;
58 | var nullPos = {row: 0, column: 0};
59 | if (!lines) return nullPos;
60 | for (var i = 0; i < lines.length; i++) {
61 | if (lines[i].row === pos.row)
62 | return {row: i, column: pos.column};
63 | }
64 | return nullPos;
65 | }
66 | this.occurToOriginalPosition = function(session, pos) {
67 | var lines = session.$occurMatchingLines;
68 | if (!lines || !lines[pos.row])
69 | return pos;
70 | return {row: lines[pos.row].row, column: pos.column};
71 | }
72 |
73 | this.matchingLines = function(session, options) {
74 | options = oop.mixin({}, options);
75 | if (!session || !options.needle) return [];
76 | var search = new Search();
77 | search.set(options);
78 | return search.findAll(session).reduce(function(lines, range) {
79 | var row = range.start.row;
80 | var last = lines[lines.length-1];
81 | return last && last.row === row ?
82 | lines :
83 | lines.concat({row: row, content: session.getLine(row)});
84 | }, []);
85 | }
86 |
87 | }).call(Occur.prototype);
88 |
89 | var dom = require('./lib/dom');
90 | dom.importCssString(".ace_occur-highlight {\n\
91 | border-radius: 4px;\n\
92 | background-color: rgba(87, 255, 8, 0.25);\n\
93 | position: absolute;\n\
94 | z-index: 4;\n\
95 | -moz-box-sizing: border-box;\n\
96 | -webkit-box-sizing: border-box;\n\
97 | box-sizing: border-box;\n\
98 | box-shadow: 0 0 4px rgb(91, 255, 50);\n\
99 | }\n\
100 | .ace_dark .ace_occur-highlight {\n\
101 | background-color: rgb(80, 140, 85);\n\
102 | box-shadow: 0 0 4px rgb(60, 120, 70);\n\
103 | }\n", "incremental-occur-highlighting");
104 |
105 | exports.Occur = Occur;
106 |
107 | });
108 |
109 | ace.define("ace/commands/occur_commands",["require","exports","module","ace/config","ace/occur","ace/keyboard/hash_handler","ace/lib/oop"], function(require, exports, module) {
110 |
111 | var config = require("../config"),
112 | Occur = require("../occur").Occur;
113 | var occurStartCommand = {
114 | name: "occur",
115 | exec: function(editor, options) {
116 | var alreadyInOccur = !!editor.session.$occur;
117 | var occurSessionActive = new Occur().enter(editor, options);
118 | if (occurSessionActive && !alreadyInOccur)
119 | OccurKeyboardHandler.installIn(editor);
120 | },
121 | readOnly: true
122 | };
123 |
124 | var occurCommands = [{
125 | name: "occurexit",
126 | bindKey: 'esc|Ctrl-G',
127 | exec: function(editor) {
128 | var occur = editor.session.$occur;
129 | if (!occur) return;
130 | occur.exit(editor, {});
131 | if (!editor.session.$occur) OccurKeyboardHandler.uninstallFrom(editor);
132 | },
133 | readOnly: true
134 | }, {
135 | name: "occuraccept",
136 | bindKey: 'enter',
137 | exec: function(editor) {
138 | var occur = editor.session.$occur;
139 | if (!occur) return;
140 | occur.exit(editor, {translatePosition: true});
141 | if (!editor.session.$occur) OccurKeyboardHandler.uninstallFrom(editor);
142 | },
143 | readOnly: true
144 | }];
145 |
146 | var HashHandler = require("../keyboard/hash_handler").HashHandler;
147 | var oop = require("../lib/oop");
148 |
149 |
150 | function OccurKeyboardHandler() {}
151 |
152 | oop.inherits(OccurKeyboardHandler, HashHandler);
153 |
154 | ;(function() {
155 |
156 | this.isOccurHandler = true;
157 |
158 | this.attach = function(editor) {
159 | HashHandler.call(this, occurCommands, editor.commands.platform);
160 | this.$editor = editor;
161 | }
162 |
163 | var handleKeyboard$super = this.handleKeyboard;
164 | this.handleKeyboard = function(data, hashId, key, keyCode) {
165 | var cmd = handleKeyboard$super.call(this, data, hashId, key, keyCode);
166 | return (cmd && cmd.command) ? cmd : undefined;
167 | }
168 |
169 | }).call(OccurKeyboardHandler.prototype);
170 |
171 | OccurKeyboardHandler.installIn = function(editor) {
172 | var handler = new this();
173 | editor.keyBinding.addKeyboardHandler(handler);
174 | editor.commands.addCommands(occurCommands);
175 | }
176 |
177 | OccurKeyboardHandler.uninstallFrom = function(editor) {
178 | editor.commands.removeCommands(occurCommands);
179 | var handler = editor.getKeyboardHandler();
180 | if (handler.isOccurHandler)
181 | editor.keyBinding.removeKeyboardHandler(handler);
182 | }
183 |
184 | exports.occurStartCommand = occurStartCommand;
185 |
186 | });
187 |
188 | ace.define("ace/commands/incremental_search_commands",["require","exports","module","ace/config","ace/lib/oop","ace/keyboard/hash_handler","ace/commands/occur_commands"], function(require, exports, module) {
189 |
190 | var config = require("../config");
191 | var oop = require("../lib/oop");
192 | var HashHandler = require("../keyboard/hash_handler").HashHandler;
193 | var occurStartCommand = require("./occur_commands").occurStartCommand;
194 | exports.iSearchStartCommands = [{
195 | name: "iSearch",
196 | bindKey: {win: "Ctrl-F", mac: "Command-F"},
197 | exec: function(editor, options) {
198 | config.loadModule(["core", "ace/incremental_search"], function(e) {
199 | var iSearch = e.iSearch = e.iSearch || new e.IncrementalSearch();
200 | iSearch.activate(editor, options.backwards);
201 | if (options.jumpToFirstMatch) iSearch.next(options);
202 | });
203 | },
204 | readOnly: true
205 | }, {
206 | name: "iSearchBackwards",
207 | exec: function(editor, jumpToNext) { editor.execCommand('iSearch', {backwards: true}); },
208 | readOnly: true
209 | }, {
210 | name: "iSearchAndGo",
211 | bindKey: {win: "Ctrl-K", mac: "Command-G"},
212 | exec: function(editor, jumpToNext) { editor.execCommand('iSearch', {jumpToFirstMatch: true, useCurrentOrPrevSearch: true}); },
213 | readOnly: true
214 | }, {
215 | name: "iSearchBackwardsAndGo",
216 | bindKey: {win: "Ctrl-Shift-K", mac: "Command-Shift-G"},
217 | exec: function(editor) { editor.execCommand('iSearch', {jumpToFirstMatch: true, backwards: true, useCurrentOrPrevSearch: true}); },
218 | readOnly: true
219 | }];
220 | exports.iSearchCommands = [{
221 | name: "restartSearch",
222 | bindKey: {win: "Ctrl-F", mac: "Command-F"},
223 | exec: function(iSearch) {
224 | iSearch.cancelSearch(true);
225 | },
226 | readOnly: true,
227 | isIncrementalSearchCommand: true
228 | }, {
229 | name: "searchForward",
230 | bindKey: {win: "Ctrl-S|Ctrl-K", mac: "Ctrl-S|Command-G"},
231 | exec: function(iSearch, options) {
232 | options.useCurrentOrPrevSearch = true;
233 | iSearch.next(options);
234 | },
235 | readOnly: true,
236 | isIncrementalSearchCommand: true
237 | }, {
238 | name: "searchBackward",
239 | bindKey: {win: "Ctrl-R|Ctrl-Shift-K", mac: "Ctrl-R|Command-Shift-G"},
240 | exec: function(iSearch, options) {
241 | options.useCurrentOrPrevSearch = true;
242 | options.backwards = true;
243 | iSearch.next(options);
244 | },
245 | readOnly: true,
246 | isIncrementalSearchCommand: true
247 | }, {
248 | name: "extendSearchTerm",
249 | exec: function(iSearch, string) {
250 | iSearch.addString(string);
251 | },
252 | readOnly: true,
253 | isIncrementalSearchCommand: true
254 | }, {
255 | name: "extendSearchTermSpace",
256 | bindKey: "space",
257 | exec: function(iSearch) { iSearch.addString(' '); },
258 | readOnly: true,
259 | isIncrementalSearchCommand: true
260 | }, {
261 | name: "shrinkSearchTerm",
262 | bindKey: "backspace",
263 | exec: function(iSearch) {
264 | iSearch.removeChar();
265 | },
266 | readOnly: true,
267 | isIncrementalSearchCommand: true
268 | }, {
269 | name: 'confirmSearch',
270 | bindKey: 'return',
271 | exec: function(iSearch) { iSearch.deactivate(); },
272 | readOnly: true,
273 | isIncrementalSearchCommand: true
274 | }, {
275 | name: 'cancelSearch',
276 | bindKey: 'esc|Ctrl-G',
277 | exec: function(iSearch) { iSearch.deactivate(true); },
278 | readOnly: true,
279 | isIncrementalSearchCommand: true
280 | }, {
281 | name: 'occurisearch',
282 | bindKey: 'Ctrl-O',
283 | exec: function(iSearch) {
284 | var options = oop.mixin({}, iSearch.$options);
285 | iSearch.deactivate();
286 | occurStartCommand.exec(iSearch.$editor, options);
287 | },
288 | readOnly: true,
289 | isIncrementalSearchCommand: true
290 | }, {
291 | name: "yankNextWord",
292 | bindKey: "Ctrl-w",
293 | exec: function(iSearch) {
294 | var ed = iSearch.$editor,
295 | range = ed.selection.getRangeOfMovements(function(sel) { sel.moveCursorWordRight(); }),
296 | string = ed.session.getTextRange(range);
297 | iSearch.addString(string);
298 | },
299 | readOnly: true,
300 | isIncrementalSearchCommand: true
301 | }, {
302 | name: "yankNextChar",
303 | bindKey: "Ctrl-Alt-y",
304 | exec: function(iSearch) {
305 | var ed = iSearch.$editor,
306 | range = ed.selection.getRangeOfMovements(function(sel) { sel.moveCursorRight(); }),
307 | string = ed.session.getTextRange(range);
308 | iSearch.addString(string);
309 | },
310 | readOnly: true,
311 | isIncrementalSearchCommand: true
312 | }, {
313 | name: 'recenterTopBottom',
314 | bindKey: 'Ctrl-l',
315 | exec: function(iSearch) { iSearch.$editor.execCommand('recenterTopBottom'); },
316 | readOnly: true,
317 | isIncrementalSearchCommand: true
318 | }, {
319 | name: 'selectAllMatches',
320 | bindKey: 'Ctrl-space',
321 | exec: function(iSearch) {
322 | var ed = iSearch.$editor,
323 | hl = ed.session.$isearchHighlight,
324 | ranges = hl && hl.cache ? hl.cache
325 | .reduce(function(ranges, ea) {
326 | return ranges.concat(ea ? ea : []); }, []) : [];
327 | iSearch.deactivate(false);
328 | ranges.forEach(ed.selection.addRange.bind(ed.selection));
329 | },
330 | readOnly: true,
331 | isIncrementalSearchCommand: true
332 | }, {
333 | name: 'searchAsRegExp',
334 | bindKey: 'Alt-r',
335 | exec: function(iSearch) {
336 | iSearch.convertNeedleToRegExp();
337 | },
338 | readOnly: true,
339 | isIncrementalSearchCommand: true
340 | }];
341 |
342 | function IncrementalSearchKeyboardHandler(iSearch) {
343 | this.$iSearch = iSearch;
344 | }
345 |
346 | oop.inherits(IncrementalSearchKeyboardHandler, HashHandler);
347 |
348 | ;(function() {
349 |
350 | this.attach = function(editor) {
351 | var iSearch = this.$iSearch;
352 | HashHandler.call(this, exports.iSearchCommands, editor.commands.platform);
353 | this.$commandExecHandler = editor.commands.addEventListener('exec', function(e) {
354 | if (!e.command.isIncrementalSearchCommand) return undefined;
355 | e.stopPropagation();
356 | e.preventDefault();
357 | return e.command.exec(iSearch, e.args || {});
358 | });
359 | }
360 |
361 | this.detach = function(editor) {
362 | if (!this.$commandExecHandler) return;
363 | editor.commands.removeEventListener('exec', this.$commandExecHandler);
364 | delete this.$commandExecHandler;
365 | }
366 |
367 | var handleKeyboard$super = this.handleKeyboard;
368 | this.handleKeyboard = function(data, hashId, key, keyCode) {
369 | if (((hashId === 1/*ctrl*/ || hashId === 8/*command*/) && key === 'v')
370 | || (hashId === 1/*ctrl*/ && key === 'y')) return null;
371 | var cmd = handleKeyboard$super.call(this, data, hashId, key, keyCode);
372 | if (cmd.command) { return cmd; }
373 | if (hashId == -1) {
374 | var extendCmd = this.commands.extendSearchTerm;
375 | if (extendCmd) { return {command: extendCmd, args: key}; }
376 | }
377 | return {command: "null", passEvent: hashId == 0 || hashId == 4};
378 | }
379 |
380 | }).call(IncrementalSearchKeyboardHandler.prototype);
381 |
382 |
383 | exports.IncrementalSearchKeyboardHandler = IncrementalSearchKeyboardHandler;
384 |
385 | });
386 |
387 | ace.define("ace/incremental_search",["require","exports","module","ace/lib/oop","ace/range","ace/search","ace/search_highlight","ace/commands/incremental_search_commands","ace/lib/dom","ace/commands/command_manager","ace/editor","ace/config"], function(require, exports, module) {
388 | "use strict";
389 |
390 | var oop = require("./lib/oop");
391 | var Range = require("./range").Range;
392 | var Search = require("./search").Search;
393 | var SearchHighlight = require("./search_highlight").SearchHighlight;
394 | var iSearchCommandModule = require("./commands/incremental_search_commands");
395 | var ISearchKbd = iSearchCommandModule.IncrementalSearchKeyboardHandler;
396 | function IncrementalSearch() {
397 | this.$options = {wrap: false, skipCurrent: false};
398 | this.$keyboardHandler = new ISearchKbd(this);
399 | }
400 |
401 | oop.inherits(IncrementalSearch, Search);
402 |
403 | function isRegExp(obj) {
404 | return obj instanceof RegExp;
405 | }
406 |
407 | function regExpToObject(re) {
408 | var string = String(re),
409 | start = string.indexOf('/'),
410 | flagStart = string.lastIndexOf('/');
411 | return {
412 | expression: string.slice(start+1, flagStart),
413 | flags: string.slice(flagStart+1)
414 | }
415 | }
416 |
417 | function stringToRegExp(string, flags) {
418 | try {
419 | return new RegExp(string, flags);
420 | } catch (e) { return string; }
421 | }
422 |
423 | function objectToRegExp(obj) {
424 | return stringToRegExp(obj.expression, obj.flags);
425 | }
426 |
427 | ;(function() {
428 |
429 | this.activate = function(ed, backwards) {
430 | this.$editor = ed;
431 | this.$startPos = this.$currentPos = ed.getCursorPosition();
432 | this.$options.needle = '';
433 | this.$options.backwards = backwards;
434 | ed.keyBinding.addKeyboardHandler(this.$keyboardHandler);
435 | this.$originalEditorOnPaste = ed.onPaste; ed.onPaste = this.onPaste.bind(this);
436 | this.$mousedownHandler = ed.addEventListener('mousedown', this.onMouseDown.bind(this));
437 | this.selectionFix(ed);
438 | this.statusMessage(true);
439 | }
440 |
441 | this.deactivate = function(reset) {
442 | this.cancelSearch(reset);
443 | var ed = this.$editor;
444 | ed.keyBinding.removeKeyboardHandler(this.$keyboardHandler);
445 | if (this.$mousedownHandler) {
446 | ed.removeEventListener('mousedown', this.$mousedownHandler);
447 | delete this.$mousedownHandler;
448 | }
449 | ed.onPaste = this.$originalEditorOnPaste;
450 | this.message('');
451 | }
452 |
453 | this.selectionFix = function(editor) {
454 | if (editor.selection.isEmpty() && !editor.session.$emacsMark) {
455 | editor.clearSelection();
456 | }
457 | }
458 |
459 | this.highlight = function(regexp) {
460 | var sess = this.$editor.session,
461 | hl = sess.$isearchHighlight = sess.$isearchHighlight || sess.addDynamicMarker(
462 | new SearchHighlight(null, "ace_isearch-result", "text"));
463 | hl.setRegexp(regexp);
464 | sess._emit("changeBackMarker"); // force highlight layer redraw
465 | }
466 |
467 | this.cancelSearch = function(reset) {
468 | var e = this.$editor;
469 | this.$prevNeedle = this.$options.needle;
470 | this.$options.needle = '';
471 | if (reset) {
472 | e.moveCursorToPosition(this.$startPos);
473 | this.$currentPos = this.$startPos;
474 | } else {
475 | e.pushEmacsMark && e.pushEmacsMark(this.$startPos, false);
476 | }
477 | this.highlight(null);
478 | return Range.fromPoints(this.$currentPos, this.$currentPos);
479 | }
480 |
481 | this.highlightAndFindWithNeedle = function(moveToNext, needleUpdateFunc) {
482 | if (!this.$editor) return null;
483 | var options = this.$options;
484 | if (needleUpdateFunc) {
485 | options.needle = needleUpdateFunc.call(this, options.needle || '') || '';
486 | }
487 | if (options.needle.length === 0) {
488 | this.statusMessage(true);
489 | return this.cancelSearch(true);
490 | };
491 | options.start = this.$currentPos;
492 | var session = this.$editor.session,
493 | found = this.find(session),
494 | shouldSelect = this.$editor.emacsMark ?
495 | !!this.$editor.emacsMark() : !this.$editor.selection.isEmpty();
496 | if (found) {
497 | if (options.backwards) found = Range.fromPoints(found.end, found.start);
498 | this.$editor.selection.setRange(Range.fromPoints(shouldSelect ? this.$startPos : found.end, found.end));
499 | if (moveToNext) this.$currentPos = found.end;
500 | this.highlight(options.re)
501 | }
502 |
503 | this.statusMessage(found);
504 |
505 | return found;
506 | }
507 |
508 | this.addString = function(s) {
509 | return this.highlightAndFindWithNeedle(false, function(needle) {
510 | if (!isRegExp(needle))
511 | return needle + s;
512 | var reObj = regExpToObject(needle);
513 | reObj.expression += s;
514 | return objectToRegExp(reObj);
515 | });
516 | }
517 |
518 | this.removeChar = function(c) {
519 | return this.highlightAndFindWithNeedle(false, function(needle) {
520 | if (!isRegExp(needle))
521 | return needle.substring(0, needle.length-1);
522 | var reObj = regExpToObject(needle);
523 | reObj.expression = reObj.expression.substring(0, reObj.expression.length-1);
524 | return objectToRegExp(reObj);
525 | });
526 | }
527 |
528 | this.next = function(options) {
529 | options = options || {};
530 | this.$options.backwards = !!options.backwards;
531 | this.$currentPos = this.$editor.getCursorPosition();
532 | return this.highlightAndFindWithNeedle(true, function(needle) {
533 | return options.useCurrentOrPrevSearch && needle.length === 0 ?
534 | this.$prevNeedle || '' : needle;
535 | });
536 | }
537 |
538 | this.onMouseDown = function(evt) {
539 | this.deactivate();
540 | return true;
541 | }
542 |
543 | this.onPaste = function(text) {
544 | this.addString(text);
545 | }
546 |
547 | this.convertNeedleToRegExp = function() {
548 | return this.highlightAndFindWithNeedle(false, function(needle) {
549 | return isRegExp(needle) ? needle : stringToRegExp(needle, 'ig');
550 | });
551 | }
552 |
553 | this.convertNeedleToString = function() {
554 | return this.highlightAndFindWithNeedle(false, function(needle) {
555 | return isRegExp(needle) ? regExpToObject(needle).expression : needle;
556 | });
557 | }
558 |
559 | this.statusMessage = function(found) {
560 | var options = this.$options, msg = '';
561 | msg += options.backwards ? 'reverse-' : '';
562 | msg += 'isearch: ' + options.needle;
563 | msg += found ? '' : ' (not found)';
564 | this.message(msg);
565 | }
566 |
567 | this.message = function(msg) {
568 | if (this.$editor.showCommandLine) {
569 | this.$editor.showCommandLine(msg);
570 | this.$editor.focus();
571 | } else {
572 | console.log(msg);
573 | }
574 | }
575 |
576 | }).call(IncrementalSearch.prototype);
577 |
578 |
579 | exports.IncrementalSearch = IncrementalSearch;
580 |
581 | var dom = require('./lib/dom');
582 | dom.importCssString && dom.importCssString("\
583 | .ace_marker-layer .ace_isearch-result {\
584 | position: absolute;\
585 | z-index: 6;\
586 | -moz-box-sizing: border-box;\
587 | -webkit-box-sizing: border-box;\
588 | box-sizing: border-box;\
589 | }\
590 | div.ace_isearch-result {\
591 | border-radius: 4px;\
592 | background-color: rgba(255, 200, 0, 0.5);\
593 | box-shadow: 0 0 4px rgb(255, 200, 0);\
594 | }\
595 | .ace_dark div.ace_isearch-result {\
596 | background-color: rgb(100, 110, 160);\
597 | box-shadow: 0 0 4px rgb(80, 90, 140);\
598 | }", "incremental-search-highlighting");
599 | var commands = require("./commands/command_manager");
600 | (function() {
601 | this.setupIncrementalSearch = function(editor, val) {
602 | if (this.usesIncrementalSearch == val) return;
603 | this.usesIncrementalSearch = val;
604 | var iSearchCommands = iSearchCommandModule.iSearchStartCommands;
605 | var method = val ? 'addCommands' : 'removeCommands';
606 | this[method](iSearchCommands);
607 | };
608 | }).call(commands.CommandManager.prototype);
609 | var Editor = require("./editor").Editor;
610 | require("./config").defineOptions(Editor.prototype, "editor", {
611 | useIncrementalSearch: {
612 | set: function(val) {
613 | this.keyBinding.$handlers.forEach(function(handler) {
614 | if (handler.setupIncrementalSearch) {
615 | handler.setupIncrementalSearch(this, val);
616 | }
617 | });
618 | this._emit('incrementalSearchSettingChanged', {isEnabled: val});
619 | }
620 | }
621 | });
622 |
623 | });
624 |
625 | ace.define("ace/keyboard/emacs",["require","exports","module","ace/lib/dom","ace/incremental_search","ace/commands/incremental_search_commands","ace/keyboard/hash_handler","ace/lib/keys"], function(require, exports, module) {
626 | "use strict";
627 |
628 | var dom = require("../lib/dom");
629 | require("../incremental_search");
630 | var iSearchCommandModule = require("../commands/incremental_search_commands");
631 |
632 |
633 | var screenToTextBlockCoordinates = function(x, y) {
634 | var canvasPos = this.scroller.getBoundingClientRect();
635 |
636 | var col = Math.floor(
637 | (x + this.scrollLeft - canvasPos.left - this.$padding) / this.characterWidth
638 | );
639 | var row = Math.floor(
640 | (y + this.scrollTop - canvasPos.top) / this.lineHeight
641 | );
642 |
643 | return this.session.screenToDocumentPosition(row, col);
644 | };
645 |
646 | var HashHandler = require("./hash_handler").HashHandler;
647 | exports.handler = new HashHandler();
648 |
649 | exports.handler.isEmacs = true;
650 | exports.handler.$id = "ace/keyboard/emacs";
651 |
652 | var initialized = false;
653 | var $formerLongWords;
654 | var $formerLineStart;
655 |
656 | exports.handler.attach = function(editor) {
657 | if (!initialized) {
658 | initialized = true;
659 | dom.importCssString('\
660 | .emacs-mode .ace_cursor{\
661 | border: 1px rgba(50,250,50,0.8) solid!important;\
662 | -moz-box-sizing: border-box!important;\
663 | -webkit-box-sizing: border-box!important;\
664 | box-sizing: border-box!important;\
665 | background-color: rgba(0,250,0,0.9);\
666 | opacity: 0.5;\
667 | }\
668 | .emacs-mode .ace_hidden-cursors .ace_cursor{\
669 | opacity: 1;\
670 | background-color: transparent;\
671 | }\
672 | .emacs-mode .ace_overwrite-cursors .ace_cursor {\
673 | opacity: 1;\
674 | background-color: transparent;\
675 | border-width: 0 0 2px 2px !important;\
676 | }\
677 | .emacs-mode .ace_text-layer {\
678 | z-index: 4\
679 | }\
680 | .emacs-mode .ace_cursor-layer {\
681 | z-index: 2\
682 | }', 'emacsMode'
683 | );
684 | }
685 | $formerLongWords = editor.session.$selectLongWords;
686 | editor.session.$selectLongWords = true;
687 | $formerLineStart = editor.session.$useEmacsStyleLineStart;
688 | editor.session.$useEmacsStyleLineStart = true;
689 |
690 | editor.session.$emacsMark = null; // the active mark
691 | editor.session.$emacsMarkRing = editor.session.$emacsMarkRing || [];
692 |
693 | editor.emacsMark = function() {
694 | return this.session.$emacsMark;
695 | };
696 |
697 | editor.setEmacsMark = function(p) {
698 | this.session.$emacsMark = p;
699 | };
700 |
701 | editor.pushEmacsMark = function(p, activate) {
702 | var prevMark = this.session.$emacsMark;
703 | if (prevMark)
704 | this.session.$emacsMarkRing.push(prevMark);
705 | if (!p || activate) this.setEmacsMark(p);
706 | else this.session.$emacsMarkRing.push(p);
707 | };
708 |
709 | editor.popEmacsMark = function() {
710 | var mark = this.emacsMark();
711 | if (mark) { this.setEmacsMark(null); return mark; }
712 | return this.session.$emacsMarkRing.pop();
713 | };
714 |
715 | editor.getLastEmacsMark = function(p) {
716 | return this.session.$emacsMark || this.session.$emacsMarkRing.slice(-1)[0];
717 | };
718 |
719 | editor.emacsMarkForSelection = function(replacement) {
720 | var sel = this.selection,
721 | multiRangeLength = this.multiSelect ?
722 | this.multiSelect.getAllRanges().length : 1,
723 | selIndex = sel.index || 0,
724 | markRing = this.session.$emacsMarkRing,
725 | markIndex = markRing.length - (multiRangeLength - selIndex),
726 | lastMark = markRing[markIndex] || sel.anchor;
727 | if (replacement) {
728 | markRing.splice(markIndex, 1,
729 | "row" in replacement && "column" in replacement ?
730 | replacement : undefined);
731 | }
732 | return lastMark;
733 | }
734 |
735 | editor.on("click", $resetMarkMode);
736 | editor.on("changeSession", $kbSessionChange);
737 | editor.renderer.screenToTextCoordinates = screenToTextBlockCoordinates;
738 | editor.setStyle("emacs-mode");
739 | editor.commands.addCommands(commands);
740 | exports.handler.platform = editor.commands.platform;
741 | editor.$emacsModeHandler = this;
742 | editor.addEventListener('copy', this.onCopy);
743 | editor.addEventListener('paste', this.onPaste);
744 | };
745 |
746 | exports.handler.detach = function(editor) {
747 | delete editor.renderer.screenToTextCoordinates;
748 | editor.session.$selectLongWords = $formerLongWords;
749 | editor.session.$useEmacsStyleLineStart = $formerLineStart;
750 | editor.removeEventListener("click", $resetMarkMode);
751 | editor.removeEventListener("changeSession", $kbSessionChange);
752 | editor.unsetStyle("emacs-mode");
753 | editor.commands.removeCommands(commands);
754 | editor.removeEventListener('copy', this.onCopy);
755 | editor.removeEventListener('paste', this.onPaste);
756 | editor.$emacsModeHandler = null;
757 | };
758 |
759 | var $kbSessionChange = function(e) {
760 | if (e.oldSession) {
761 | e.oldSession.$selectLongWords = $formerLongWords;
762 | e.oldSession.$useEmacsStyleLineStart = $formerLineStart;
763 | }
764 |
765 | $formerLongWords = e.session.$selectLongWords;
766 | e.session.$selectLongWords = true;
767 | $formerLineStart = e.session.$useEmacsStyleLineStart;
768 | e.session.$useEmacsStyleLineStart = true;
769 |
770 | if (!e.session.hasOwnProperty('$emacsMark'))
771 | e.session.$emacsMark = null;
772 | if (!e.session.hasOwnProperty('$emacsMarkRing'))
773 | e.session.$emacsMarkRing = [];
774 | };
775 |
776 | var $resetMarkMode = function(e) {
777 | e.editor.session.$emacsMark = null;
778 | };
779 |
780 | var keys = require("../lib/keys").KEY_MODS;
781 | var eMods = {C: "ctrl", S: "shift", M: "alt", CMD: "command"};
782 | var combinations = ["C-S-M-CMD",
783 | "S-M-CMD", "C-M-CMD", "C-S-CMD", "C-S-M",
784 | "M-CMD", "S-CMD", "S-M", "C-CMD", "C-M", "C-S",
785 | "CMD", "M", "S", "C"];
786 | combinations.forEach(function(c) {
787 | var hashId = 0;
788 | c.split("-").forEach(function(c) {
789 | hashId = hashId | keys[eMods[c]];
790 | });
791 | eMods[hashId] = c.toLowerCase() + "-";
792 | });
793 |
794 | exports.handler.onCopy = function(e, editor) {
795 | if (editor.$handlesEmacsOnCopy) return;
796 | editor.$handlesEmacsOnCopy = true;
797 | exports.handler.commands.killRingSave.exec(editor);
798 | editor.$handlesEmacsOnCopy = false;
799 | };
800 |
801 | exports.handler.onPaste = function(e, editor) {
802 | editor.pushEmacsMark(editor.getCursorPosition());
803 | };
804 |
805 | exports.handler.bindKey = function(key, command) {
806 | if (typeof key == "object")
807 | key = key[this.platform];
808 | if (!key)
809 | return;
810 |
811 | var ckb = this.commandKeyBinding;
812 | key.split("|").forEach(function(keyPart) {
813 | keyPart = keyPart.toLowerCase();
814 | ckb[keyPart] = command;
815 | var keyParts = keyPart.split(" ").slice(0,-1);
816 | keyParts.reduce(function(keyMapKeys, keyPart, i) {
817 | var prefix = keyMapKeys[i-1] ? keyMapKeys[i-1] + ' ' : '';
818 | return keyMapKeys.concat([prefix + keyPart]);
819 | }, []).forEach(function(keyPart) {
820 | if (!ckb[keyPart]) ckb[keyPart] = "null";
821 | });
822 | }, this);
823 | };
824 |
825 | exports.handler.getStatusText = function(editor, data) {
826 | var str = "";
827 | if (data.count)
828 | str += data.count;
829 | if (data.keyChain)
830 | str += " " + data.keyChain
831 | return str;
832 | };
833 |
834 | exports.handler.handleKeyboard = function(data, hashId, key, keyCode) {
835 | if (keyCode === -1) return undefined;
836 |
837 | var editor = data.editor;
838 | editor._signal("changeStatus");
839 | if (hashId == -1) {
840 | editor.pushEmacsMark();
841 | if (data.count) {
842 | var str = new Array(data.count + 1).join(key);
843 | data.count = null;
844 | return {command: "insertstring", args: str};
845 | }
846 | }
847 |
848 | var modifier = eMods[hashId];
849 | if (modifier == "c-" || data.count) {
850 | var count = parseInt(key[key.length - 1]);
851 | if (typeof count === 'number' && !isNaN(count)) {
852 | data.count = Math.max(data.count, 0) || 0;
853 | data.count = 10 * data.count + count;
854 | return {command: "null"};
855 | }
856 | }
857 | if (modifier) key = modifier + key;
858 | if (data.keyChain) key = data.keyChain += " " + key;
859 | var command = this.commandKeyBinding[key];
860 | data.keyChain = command == "null" ? key : "";
861 | if (!command) return undefined;
862 | if (command === "null") return {command: "null"};
863 |
864 | if (command === "universalArgument") {
865 | data.count = -4;
866 | return {command: "null"};
867 | }
868 | var args;
869 | if (typeof command !== "string") {
870 | args = command.args;
871 | if (command.command) command = command.command;
872 | if (command === "goorselect") {
873 | command = editor.emacsMark() ? args[1] : args[0];
874 | args = null;
875 | }
876 | }
877 |
878 | if (typeof command === "string") {
879 | if (command === "insertstring" ||
880 | command === "splitline" ||
881 | command === "togglecomment") {
882 | editor.pushEmacsMark();
883 | }
884 | command = this.commands[command] || editor.commands.commands[command];
885 | if (!command) return undefined;
886 | }
887 |
888 | if (!command.readOnly && !command.isYank)
889 | data.lastCommand = null;
890 |
891 | if (!command.readOnly && editor.emacsMark())
892 | editor.setEmacsMark(null)
893 |
894 | if (data.count) {
895 | var count = data.count;
896 | data.count = 0;
897 | if (!command || !command.handlesCount) {
898 | return {
899 | args: args,
900 | command: {
901 | exec: function(editor, args) {
902 | for (var i = 0; i < count; i++)
903 | command.exec(editor, args);
904 | },
905 | multiSelectAction: command.multiSelectAction
906 | }
907 | };
908 | } else {
909 | if (!args) args = {};
910 | if (typeof args === 'object') args.count = count;
911 | }
912 | }
913 |
914 | return {command: command, args: args};
915 | };
916 |
917 | exports.emacsKeys = {
918 | "Up|C-p" : {command: "goorselect", args: ["golineup","selectup"]},
919 | "Down|C-n" : {command: "goorselect", args: ["golinedown","selectdown"]},
920 | "Left|C-b" : {command: "goorselect", args: ["gotoleft","selectleft"]},
921 | "Right|C-f" : {command: "goorselect", args: ["gotoright","selectright"]},
922 | "C-Left|M-b" : {command: "goorselect", args: ["gotowordleft","selectwordleft"]},
923 | "C-Right|M-f" : {command: "goorselect", args: ["gotowordright","selectwordright"]},
924 | "Home|C-a" : {command: "goorselect", args: ["gotolinestart","selecttolinestart"]},
925 | "End|C-e" : {command: "goorselect", args: ["gotolineend","selecttolineend"]},
926 | "C-Home|S-M-,": {command: "goorselect", args: ["gotostart","selecttostart"]},
927 | "C-End|S-M-." : {command: "goorselect", args: ["gotoend","selecttoend"]},
928 | "S-Up|S-C-p" : "selectup",
929 | "S-Down|S-C-n" : "selectdown",
930 | "S-Left|S-C-b" : "selectleft",
931 | "S-Right|S-C-f" : "selectright",
932 | "S-C-Left|S-M-b" : "selectwordleft",
933 | "S-C-Right|S-M-f" : "selectwordright",
934 | "S-Home|S-C-a" : "selecttolinestart",
935 | "S-End|S-C-e" : "selecttolineend",
936 | "S-C-Home" : "selecttostart",
937 | "S-C-End" : "selecttoend",
938 |
939 | "C-l" : "recenterTopBottom",
940 | "M-s" : "centerselection",
941 | "M-g": "gotoline",
942 | "C-x C-p": "selectall",
943 | "C-Down": {command: "goorselect", args: ["gotopagedown","selectpagedown"]},
944 | "C-Up": {command: "goorselect", args: ["gotopageup","selectpageup"]},
945 | "PageDown|C-v": {command: "goorselect", args: ["gotopagedown","selectpagedown"]},
946 | "PageUp|M-v": {command: "goorselect", args: ["gotopageup","selectpageup"]},
947 | "S-C-Down": "selectpagedown",
948 | "S-C-Up": "selectpageup",
949 |
950 | "C-s": "iSearch",
951 | "C-r": "iSearchBackwards",
952 |
953 | "M-C-s": "findnext",
954 | "M-C-r": "findprevious",
955 | "S-M-5": "replace",
956 | "Backspace": "backspace",
957 | "Delete|C-d": "del",
958 | "Return|C-m": {command: "insertstring", args: "\n"}, // "newline"
959 | "C-o": "splitline",
960 |
961 | "M-d|C-Delete": {command: "killWord", args: "right"},
962 | "C-Backspace|M-Backspace|M-Delete": {command: "killWord", args: "left"},
963 | "C-k": "killLine",
964 |
965 | "C-y|S-Delete": "yank",
966 | "M-y": "yankRotate",
967 | "C-g": "keyboardQuit",
968 |
969 | "C-w|C-S-W": "killRegion",
970 | "M-w": "killRingSave",
971 | "C-Space": "setMark",
972 | "C-x C-x": "exchangePointAndMark",
973 |
974 | "C-t": "transposeletters",
975 | "M-u": "touppercase", // Doesn't work
976 | "M-l": "tolowercase",
977 | "M-/": "autocomplete", // Doesn't work
978 | "C-u": "universalArgument",
979 |
980 | "M-;": "togglecomment",
981 |
982 | "C-/|C-x u|S-C--|C-z": "undo",
983 | "S-C-/|S-C-x u|C--|S-C-z": "redo", //infinite undo?
984 | "C-x r": "selectRectangularRegion",
985 | "M-x": {command: "focusCommandLine", args: "M-x "}
986 | };
987 |
988 |
989 | exports.handler.bindKeys(exports.emacsKeys);
990 |
991 | exports.handler.addCommands({
992 | recenterTopBottom: function(editor) {
993 | var renderer = editor.renderer;
994 | var pos = renderer.$cursorLayer.getPixelPosition();
995 | var h = renderer.$size.scrollerHeight - renderer.lineHeight;
996 | var scrollTop = renderer.scrollTop;
997 | if (Math.abs(pos.top - scrollTop) < 2) {
998 | scrollTop = pos.top - h;
999 | } else if (Math.abs(pos.top - scrollTop - h * 0.5) < 2) {
1000 | scrollTop = pos.top;
1001 | } else {
1002 | scrollTop = pos.top - h * 0.5;
1003 | }
1004 | editor.session.setScrollTop(scrollTop);
1005 | },
1006 | selectRectangularRegion: function(editor) {
1007 | editor.multiSelect.toggleBlockSelection();
1008 | },
1009 | setMark: {
1010 | exec: function(editor, args) {
1011 |
1012 | if (args && args.count) {
1013 | if (editor.inMultiSelectMode) editor.forEachSelection(moveToMark);
1014 | else moveToMark();
1015 | moveToMark();
1016 | return;
1017 | }
1018 |
1019 | var mark = editor.emacsMark(),
1020 | ranges = editor.selection.getAllRanges(),
1021 | rangePositions = ranges.map(function(r) { return {row: r.start.row, column: r.start.column}; }),
1022 | transientMarkModeActive = true,
1023 | hasNoSelection = ranges.every(function(range) { return range.isEmpty(); });
1024 | if (transientMarkModeActive && (mark || !hasNoSelection)) {
1025 | if (editor.inMultiSelectMode) editor.forEachSelection({exec: editor.clearSelection.bind(editor)})
1026 | else editor.clearSelection();
1027 | if (mark) editor.pushEmacsMark(null);
1028 | return;
1029 | }
1030 |
1031 | if (!mark) {
1032 | rangePositions.forEach(function(pos) { editor.pushEmacsMark(pos); });
1033 | editor.setEmacsMark(rangePositions[rangePositions.length-1]);
1034 | return;
1035 | }
1036 |
1037 | function moveToMark() {
1038 | var mark = editor.popEmacsMark();
1039 | mark && editor.moveCursorToPosition(mark);
1040 | }
1041 |
1042 | },
1043 | readOnly: true,
1044 | handlesCount: true
1045 | },
1046 | exchangePointAndMark: {
1047 | exec: function exchangePointAndMark$exec(editor, args) {
1048 | var sel = editor.selection;
1049 | if (!args.count && !sel.isEmpty()) { // just invert selection
1050 | sel.setSelectionRange(sel.getRange(), !sel.isBackwards());
1051 | return;
1052 | }
1053 |
1054 | if (args.count) { // replace mark and point
1055 | var pos = {row: sel.lead.row, column: sel.lead.column};
1056 | sel.clearSelection();
1057 | sel.moveCursorToPosition(editor.emacsMarkForSelection(pos));
1058 | } else { // create selection to last mark
1059 | sel.selectToPosition(editor.emacsMarkForSelection());
1060 | }
1061 | },
1062 | readOnly: true,
1063 | handlesCount: true,
1064 | multiSelectAction: "forEach"
1065 | },
1066 | killWord: {
1067 | exec: function(editor, dir) {
1068 | editor.clearSelection();
1069 | if (dir == "left")
1070 | editor.selection.selectWordLeft();
1071 | else
1072 | editor.selection.selectWordRight();
1073 |
1074 | var range = editor.getSelectionRange();
1075 | var text = editor.session.getTextRange(range);
1076 | exports.killRing.add(text);
1077 |
1078 | editor.session.remove(range);
1079 | editor.clearSelection();
1080 | },
1081 | multiSelectAction: "forEach"
1082 | },
1083 | killLine: function(editor) {
1084 | editor.pushEmacsMark(null);
1085 | var pos = editor.getCursorPosition();
1086 | if (pos.column === 0 &&
1087 | editor.session.doc.getLine(pos.row).length === 0) {
1088 | editor.selection.selectLine();
1089 | } else {
1090 | editor.clearSelection();
1091 | editor.selection.selectLineEnd();
1092 | }
1093 | var range = editor.getSelectionRange();
1094 | var text = editor.session.getTextRange(range);
1095 | exports.killRing.add(text);
1096 |
1097 | editor.session.remove(range);
1098 | editor.clearSelection();
1099 | },
1100 | yank: function(editor) {
1101 | editor.onPaste(exports.killRing.get() || '');
1102 | editor.keyBinding.$data.lastCommand = "yank";
1103 | },
1104 | yankRotate: function(editor) {
1105 | if (editor.keyBinding.$data.lastCommand != "yank")
1106 | return;
1107 | editor.undo();
1108 | editor.session.$emacsMarkRing.pop(); // also undo recording mark
1109 | editor.onPaste(exports.killRing.rotate());
1110 | editor.keyBinding.$data.lastCommand = "yank";
1111 | },
1112 | killRegion: {
1113 | exec: function(editor) {
1114 | exports.killRing.add(editor.getCopyText());
1115 | editor.commands.byName.cut.exec(editor);
1116 | },
1117 | readOnly: true,
1118 | multiSelectAction: "forEach"
1119 | },
1120 | killRingSave: {
1121 | exec: function(editor) {
1122 |
1123 | editor.$handlesEmacsOnCopy = true;
1124 | var marks = editor.session.$emacsMarkRing.slice(),
1125 | deselectedMarks = [];
1126 | exports.killRing.add(editor.getCopyText());
1127 |
1128 | setTimeout(function() {
1129 | function deselect() {
1130 | var sel = editor.selection, range = sel.getRange(),
1131 | pos = sel.isBackwards() ? range.end : range.start;
1132 | deselectedMarks.push({row: pos.row, column: pos.column});
1133 | sel.clearSelection();
1134 | }
1135 | editor.$handlesEmacsOnCopy = false;
1136 | if (editor.inMultiSelectMode) editor.forEachSelection({exec: deselect});
1137 | else deselect();
1138 | editor.session.$emacsMarkRing = marks.concat(deselectedMarks.reverse());
1139 | }, 0);
1140 | },
1141 | readOnly: true
1142 | },
1143 | keyboardQuit: function(editor) {
1144 | editor.selection.clearSelection();
1145 | editor.setEmacsMark(null);
1146 | editor.keyBinding.$data.count = null;
1147 | },
1148 | focusCommandLine: function(editor, arg) {
1149 | if (editor.showCommandLine)
1150 | editor.showCommandLine(arg);
1151 | }
1152 | });
1153 |
1154 | exports.handler.addCommands(iSearchCommandModule.iSearchStartCommands);
1155 |
1156 | var commands = exports.handler.commands;
1157 | commands.yank.isYank = true;
1158 | commands.yankRotate.isYank = true;
1159 |
1160 | exports.killRing = {
1161 | $data: [],
1162 | add: function(str) {
1163 | str && this.$data.push(str);
1164 | if (this.$data.length > 30)
1165 | this.$data.shift();
1166 | },
1167 | get: function(n) {
1168 | n = n || 1;
1169 | return this.$data.slice(this.$data.length-n, this.$data.length).reverse().join('\n');
1170 | },
1171 | pop: function() {
1172 | if (this.$data.length > 1)
1173 | this.$data.pop();
1174 | return this.get();
1175 | },
1176 | rotate: function() {
1177 | this.$data.unshift(this.$data.pop());
1178 | return this.get();
1179 | }
1180 | };
1181 |
1182 | });
1183 |
--------------------------------------------------------------------------------
/vendor/three-codeeditor/snippets/javascript.js:
--------------------------------------------------------------------------------
1 | ace.define("ace/snippets/javascript",["require","exports","module"], function(require, exports, module) {
2 | "use strict";
3 |
4 | exports.snippetText = "# Prototype\nsnippet proto\n\t${1:class}.prototype.${2:method} = function(${3:arg}) {\n\t\t${4:// body...}\n\t};\n# Function\nsnippet fun\n\tfunction ${1?:functionName}(${2}) {${0}}\n# Anonymous Function\nregex /((=)\\s*|(:)\\s*|(\\()|\\b)/f/\nname f\n\tfunction($2) {${0:$TM_SELECTED_TEXT}}${M2?;}${M3?,}\n# Anonymous Function\nregex /((=)\\s*|(:)\\s*|(\\()|\\b)/f2/(\\))?/\nname f2\n\tfunction${M1?: ${1:functionName}}($2) {${0:$TM_SELECTED_TEXT}}${M2?;}${M3?,}${M4?)}\n# Immediate function\ntrigger \\(?f\\(\nendTrigger \\)?\nsnippet f(\n\t(function ${1:functionName}(${2}) {\n\t\t${0:${TM_SELECTED_TEXT:/* code */}}\n\t})(${2});\n# if\nsnippet if\n\tif (${1:true}) {\n\t\t${0}\n\t}\n# if ... else\nsnippet ife\n\tif (${1:true}) {\n\t\t${2}\n\t} else {\n\t\t${0}\n\t}\nsnippet err\n\tif (${1:err}) { ${2:thenDo}(err, null); return; }\n# tertiary conditional\nsnippet ter\n\t${1:/* condition */} ? ${2:a} : ${3:b}\n# switch\nsnippet switch\n\tswitch (${1:expression}) {\n\t\tcase '${3:case}':\n\t\t\t${4:// code}\n\t\t\tbreak;\n\t\t${5}\n\t\tdefault:\n\t\t\t${2:// code}\n\t}\n# case\nsnippet case\n\tcase '${1:case}':\n\t\t${2:// code}\n\t\tbreak;\n\t${3}\n\n# while (...) {...}\nsnippet wh\n\twhile (${1:/* condition */}) {${0:/* code */}}\n# try\nsnippet try\n\ttry {${0:/* code */}} catch (e) {}\n# do...while\nsnippet do\n\tdo {${2:/* code */}} while (${1:/* condition */});\n# Object Method\nsnippet :f\nregex /([,{[])|^\\s*/:f/\n\t${1:methodName}: function(${2:attribute}) {${0}}${3:,}\n# Object Property\nsnippet :p\nregex /([,{[])|^\\s*/:p/\n\t${1:propName}: ${0:value,}\n# setTimeout function\nsnippet setTimeout\nregex /\\b/st|timeout|setTimeo?u?t?/\n\tsetTimeout(function() {${0:$TM_SELECTED_TEXT}}, ${1:100});\nsnippet setInterval\nregex /\\b/int|setI?n?t?e?r?v?a?l?/\n\tvar i = setInterval(function() {${0:$TM_SELECTED_TEXT}}, ${1:1000});\n\tclearInterval(i);\n# delay function\nsnippet delay\nregex /\\b/st|del(ay)?/\n\t(function() {${0}})${2:.bind(this)}.delay(${1:0});\n# Get Elements\nsnippet gett\n\tgetElementsBy${1:TagName}('${2}')${3}\n# Get Element\nsnippet get\n\tgetElementBy${1:Id}('${2}')${3}\n# console.log (Firebug)\nsnippet cl\n\tconsole.log(${1});\n# show (lively)\nsnippet s\n\tshow(\"${1:%o}\", ${2});\n# session snippet\nsnippet getS\n\tvar s = lively.net.SessionTracker.getSession();\n# return\nsnippet ret\n\treturn ${1:result};\n# for (property in object ) { ... }\nsnippet fori\n\tfor (var ${1:prop} in ${2:Things}) {\n\t\t${0:$2[$1]}\n\t}\n# hasOwnProperty\nsnippet has\n\thasOwnProperty(${1})\n# printit comment\nsnippet />\n\t// => \n# block comment\nsnippet /*\n\t/*\n\t * ${1:description}\n\t */\n# docstring\nsnippet /**\n\t/**\n\t * ${1:description}\n\t *\n\t */\nsnippet @par\nregex /^\\s*\\*\\s*/@(para?m?)?/\n\t@param {${1:type}} ${2:name} ${3:description}\nsnippet @ret\n\t@return {${1:type}} ${2:description}\n# JSON.parse\nsnippet jsonp\n\tJSON.parse(${1:jstr});\n# JSON.stringify\nsnippet jsons\n\tJSON.stringify(${1:object});\n# self-defining function\nsnippet sdf\n\tvar ${1:function_name} = function(${2:argument}) {\n\t\t${3:// initial code ...}\n\t\t$1 = function($2) {${4:// main code}};\n\t}\n# class\nname subclass\nregex /^\\s*([^\\.]+)?/\\.?(sub)?class/\n\t${M1?:${1:Object}}.subclass(\"${2:Name}\",\n\t\"initializing\", {\n\t\tinitialize: function(${3}) {\n\t\t\t${0}\n\t\t}\n\t});\n# TestClass\nname TestClass\nregex /^\\s*/TestCl?a?s?s?/\n\tTestCase.subclass(\"${1:TestClass}\",\n\t\"running\", {\n\t\tsetUp: function($super) {},\n\t\ttearDown: function($super) {}\n\t},\n\t'testing', {\n\t\t${2:test1}: function() {${0}}\n\t});\n# initialize methods\nsnippet init\n\tinitialize: function(${1}) {${0}}\n# initialize category\nsnippet initCat\n\t'initializing', {\n\t\tinitialize: function(${1}) {${0}}\n\t}\n# category\nsnippet cat\n\t'${1:category name}', {${0}}${2:,}\n# addMethods\nname addMethods\nregex /^\\s*([^\\.]+)?/\\.?addMe?t?h?o?d?s?/\n\t${M1?:${1:SomeClass}}.addMethods(\n\t\"${2:category}\", {\n\t\t${0}\n\t});\n# Object.extend\nsnippet Oe\n\tObject.extend(${1:obj}, {$0});\n# collection iter\nregex /(\\.?[^\\.\\s]+)/\\.?((iter|forEa?c?h?)|(select|collect|reject|detect|map|filter))/\n\t${M1?:${1:list}}.${M4?${M4}:${2:forEach}}(function(${3:ea}) {$0});\n# collection inject\nregex /(\\.?[^\\.\\s]+|^\\s*)/\\.?inje?c?t?/\n\t${M1?:${1:list}}.inject(${2:collector}, function($2, ea) { ${0:return $2;} });\n# collection withAllSubmorphsDo\nregex /(\\.?[^\\.\\s]+|^\\s*)/\\.?withAl?l?S?u?b?m?o?r?p?h?s?D?o?/\n\t${M1?:${1:owner}}.withAllSubmorphsDo(function(ea) { return ${0:ea}; });\n# addScript\nregex /(\\.?[^\\.\\s]+|^\\s*)/\\.?addSc?r?i?p?t?/\n\t${M1?:${1:morph}}.addScript(function ${2:scriptName}(${3}) {${0}});\n# lively.bindings.connect\nsnippet con\n\tlively.bindings.connect(${1:source}, '${2:sourceAttr}', ${3:target}, '${4:targetAttr}');\n# lively.bindings.connect converter\nsnippet conC\n\tlively.bindings.connect(${1:source}, '${2:sourceAttr}', ${3:target}, '${4:targetAttr}', {\n\t\tconverter: function(val) { ${0:return val;} }});\n# lively.bindings.connect updater\nsnippet conU\n\tlively.bindings.connect(${1:source}, '${2:sourceAttr}', ${3:target}, '${4:targetAttr}', {\n\t\tupdater: function(\\$upd, val) { ${0:\\$upd(val);} }});\n# lively.bindings.signal\nsnippet sig\n\tlively.bindings.signal(${1:source}, '${2:sourceAttr}', ${3:value});\n# lively meta - grep\nsnippet $g\n\t\\$grep('${1:string}', '${0:location}')\n# lively world\nsnippet $w\n\t\\$world\n# $morph\nsnippet $m\n\t\\$morph('${1:name}')${0}\n# \nsnippet for-\n\tfor (var ${1:i} = ${2:THINGS}.length; ${1:i}--; ) {\n\t\t${0:${2:THINGS}[${1:i}];}\n\t}\n# for (...) {...}\nsnippet for\n\tfor (var ${1:i} = 0; $1 < ${2:Things}.length; $1++) {\n\t\t${3:$2[$1]}$0\n\t}\n# for (...) {...} (Improved Native For-Loop)\nsnippet forr\n\tfor (var ${1:i} = ${2:Things}.length - 1; $1 >= 0; $1--) {\n\t\t${3:$2[$1]}$0\n\t}\n#modules\nsnippet def\n\tdefine(function(require, exports, module) {\n\t\"use strict\";\n\tvar ${1/.*\\///} = require(\"${1}\");\n\t\n\t$TM_SELECTED_TEXT\n\t});\nsnippet req\nguard ^\\s*\n\tvar ${1/.*\\///} = require(\"${1}\");\n\t$0\nsnippet requ\nguard ^\\s*\n\tvar ${1/.*\\/(.)/\\u$1/} = require(\"${1}\").${1/.*\\/(.)/\\u$1/};\n\t$0\n# Functions.composeAsync\nsnippet async\n\tFunctions.composeAsync(\n\t\tfunction(${1}next) { ${0:next(null);} }\n\t)(${1}function(err) { if (err) show(err); });\n# typeof tests\nsnippet typef\n\ttypeof ${1:object} === \"function\"${0}\nsnippet typeu\n\ttypeof ${1:object} === \"undefined\"${0}\nsnippet typenu\n\ttypeof ${1:object} !== \"undefined\"${0}\n# $world snippets\nsnippet prompt\n\t\\$world.prompt(\"${1:query text}\", function(input) {\n\t\tif (!input) return;\n\t\t${0}\n\t}, {input: \"${2:initial input}\", historyId: \"${3:prompt-id}\"});\nsnippet confirm\n\t\\$world.confirm(\"${1:query text}\", function(input) {\n\t\tif (!input) return;\n\t\t${0}\n\t});\n"
5 | exports.scope = "javascript";
6 |
7 | });
8 |
--------------------------------------------------------------------------------
/vendor/three-codeeditor/snippets/text.js:
--------------------------------------------------------------------------------
1 | ace.define("ace/snippets/text",["require","exports","module"], function(require, exports, module) {
2 | "use strict";
3 |
4 | exports.snippetText =undefined;
5 | exports.scope = "text";
6 |
7 | });
8 |
--------------------------------------------------------------------------------
/vendor/three-codeeditor/theme-twilight.js:
--------------------------------------------------------------------------------
1 | ace.define("ace/theme/twilight",["require","exports","module","ace/lib/dom"], function(require, exports, module) {
2 |
3 | exports.isDark = true;
4 | exports.cssClass = "ace-twilight";
5 | exports.cssText = ".ace-twilight .ace_gutter {\
6 | background: #232323;\
7 | color: #E2E2E2\
8 | }\
9 | .ace-twilight .ace_print-margin {\
10 | width: 1px;\
11 | background: #232323\
12 | }\
13 | .ace-twilight {\
14 | background-color: #141414;\
15 | color: #F8F8F8\
16 | }\
17 | .ace-twilight .ace_cursor {\
18 | color: #A7A7A7\
19 | }\
20 | .ace-twilight .ace_marker-layer .ace_selection {\
21 | background: rgba(221, 240, 255, 0.20)\
22 | }\
23 | .ace-twilight.ace_multiselect .ace_selection.ace_start {\
24 | box-shadow: 0 0 3px 0px #141414;\
25 | border-radius: 2px\
26 | }\
27 | .ace-twilight .ace_marker-layer .ace_step {\
28 | background: rgb(102, 82, 0)\
29 | }\
30 | .ace-twilight .ace_marker-layer .ace_bracket {\
31 | margin: -1px 0 0 -1px;\
32 | border: 1px solid rgba(255, 255, 255, 0.25)\
33 | }\
34 | .ace-twilight .ace_marker-layer .ace_active-line {\
35 | background: rgba(255, 255, 255, 0.031)\
36 | }\
37 | .ace-twilight .ace_gutter-active-line {\
38 | background-color: rgba(255, 255, 255, 0.031)\
39 | }\
40 | .ace-twilight .ace_marker-layer .ace_selected-word {\
41 | border: 1px solid rgba(221, 240, 255, 0.20)\
42 | }\
43 | .ace-twilight .ace_invisible {\
44 | color: rgba(255, 255, 255, 0.25)\
45 | }\
46 | .ace-twilight .ace_keyword,\
47 | .ace-twilight .ace_meta {\
48 | color: #CDA869\
49 | }\
50 | .ace-twilight .ace_constant,\
51 | .ace-twilight .ace_constant.ace_character,\
52 | .ace-twilight .ace_constant.ace_character.ace_escape,\
53 | .ace-twilight .ace_constant.ace_other,\
54 | .ace-twilight .ace_heading,\
55 | .ace-twilight .ace_markup.ace_heading,\
56 | .ace-twilight .ace_support.ace_constant {\
57 | color: #CF6A4C\
58 | }\
59 | .ace-twilight .ace_invalid.ace_illegal {\
60 | color: #F8F8F8;\
61 | background-color: rgba(86, 45, 86, 0.75)\
62 | }\
63 | .ace-twilight .ace_invalid.ace_deprecated {\
64 | text-decoration: underline;\
65 | font-style: italic;\
66 | color: #D2A8A1\
67 | }\
68 | .ace-twilight .ace_support {\
69 | color: #9B859D\
70 | }\
71 | .ace-twilight .ace_fold {\
72 | background-color: #AC885B;\
73 | border-color: #F8F8F8\
74 | }\
75 | .ace-twilight .ace_support.ace_function {\
76 | color: #DAD085\
77 | }\
78 | .ace-twilight .ace_list,\
79 | .ace-twilight .ace_markup.ace_list,\
80 | .ace-twilight .ace_storage {\
81 | color: #F9EE98\
82 | }\
83 | .ace-twilight .ace_entity.ace_name.ace_function,\
84 | .ace-twilight .ace_meta.ace_tag,\
85 | .ace-twilight .ace_variable {\
86 | color: #AC885B\
87 | }\
88 | .ace-twilight .ace_string {\
89 | color: #8F9D6A\
90 | }\
91 | .ace-twilight .ace_string.ace_regexp {\
92 | color: #E9C062\
93 | }\
94 | .ace-twilight .ace_comment {\
95 | font-style: italic;\
96 | color: #5F5A60\
97 | }\
98 | .ace-twilight .ace_variable {\
99 | color: #7587A6\
100 | }\
101 | .ace-twilight .ace_xml-pe {\
102 | color: #494949\
103 | }\
104 | .ace-twilight .ace_indent-guide {\
105 | background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAEklEQVQImWMQERFpYLC1tf0PAAgOAnPnhxyiAAAAAElFTkSuQmCC) right repeat-y\
106 | }";
107 |
108 | var dom = require("../lib/dom");
109 | dom.importCssString(exports.cssText, exports.cssClass);
110 | });
111 |
--------------------------------------------------------------------------------
/vendor/three/OrbitControls.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author qiao / https://github.com/qiao
3 | * @author mrdoob / http://mrdoob.com
4 | * @author alteredq / http://alteredqualia.com/
5 | * @author WestLangley / http://github.com/WestLangley
6 | * @author erich666 / http://erichaines.com
7 | */
8 | /*global THREE, console */
9 |
10 | // This set of controls performs orbiting, dollying (zooming), and panning. It maintains
11 | // the "up" direction as +Y, unlike the TrackballControls. Touch on tablet and phones is
12 | // supported.
13 | //
14 | // Orbit - left mouse / touch: one finger move
15 | // Zoom - middle mouse, or mousewheel / touch: two finger spread or squish
16 | // Pan - right mouse, or arrow keys / touch: three finter swipe
17 | //
18 | // This is a drop-in replacement for (most) TrackballControls used in examples.
19 | // That is, include this js file and wherever you see:
20 | // controls = new THREE.TrackballControls( camera );
21 | // controls.target.z = 150;
22 | // Simple substitute "OrbitControls" and the control should work as-is.
23 |
24 | THREE.OrbitControls = function ( object, domElement ) {
25 |
26 | this.object = object;
27 | this.domElement = ( domElement !== undefined ) ? domElement : document;
28 |
29 | // API
30 |
31 | // Set to false to disable this control
32 | this.enabled = true;
33 |
34 | // "target" sets the location of focus, where the control orbits around
35 | // and where it pans with respect to.
36 | this.target = new THREE.Vector3();
37 |
38 | // center is old, deprecated; use "target" instead
39 | this.center = this.target;
40 |
41 | // This option actually enables dollying in and out; left as "zoom" for
42 | // backwards compatibility
43 | this.noZoom = false;
44 | this.zoomSpeed = 1.0;
45 |
46 | // Limits to how far you can dolly in and out
47 | this.minDistance = 0;
48 | this.maxDistance = Infinity;
49 |
50 | // Set to true to disable this control
51 | this.noRotate = false;
52 | this.rotateSpeed = 1.0;
53 |
54 | // Set to true to disable this control
55 | this.noPan = false;
56 | this.keyPanSpeed = 7.0; // pixels moved per arrow key push
57 |
58 | // Set to true to automatically rotate around the target
59 | this.autoRotate = false;
60 | this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60
61 |
62 | // How far you can orbit vertically, upper and lower limits.
63 | // Range is 0 to Math.PI radians.
64 | this.minPolarAngle = 0; // radians
65 | this.maxPolarAngle = Math.PI; // radians
66 |
67 | // How far you can orbit horizontally, upper and lower limits.
68 | // If set, must be a sub-interval of the interval [ - Math.PI, Math.PI ].
69 | this.minAzimuthAngle = - Infinity; // radians
70 | this.maxAzimuthAngle = Infinity; // radians
71 |
72 | // Set to true to disable use of the keys
73 | this.noKeys = false;
74 |
75 | // The four arrow keys
76 | this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 };
77 |
78 | // Mouse buttons
79 | this.mouseButtons = { ORBIT: THREE.MOUSE.LEFT, ZOOM: THREE.MOUSE.MIDDLE, PAN: THREE.MOUSE.RIGHT };
80 |
81 | ////////////
82 | // internals
83 |
84 | var scope = this;
85 |
86 | var EPS = 0.000001;
87 |
88 | var rotateStart = new THREE.Vector2();
89 | var rotateEnd = new THREE.Vector2();
90 | var rotateDelta = new THREE.Vector2();
91 |
92 | var panStart = new THREE.Vector2();
93 | var panEnd = new THREE.Vector2();
94 | var panDelta = new THREE.Vector2();
95 | var panOffset = new THREE.Vector3();
96 |
97 | var offset = new THREE.Vector3();
98 |
99 | var dollyStart = new THREE.Vector2();
100 | var dollyEnd = new THREE.Vector2();
101 | var dollyDelta = new THREE.Vector2();
102 |
103 | var phiDelta = 0;
104 | var thetaDelta = 0;
105 | var scale = 1;
106 | var pan = new THREE.Vector3();
107 |
108 | var lastPosition = new THREE.Vector3();
109 | var lastQuaternion = new THREE.Quaternion();
110 |
111 | var STATE = { NONE : -1, ROTATE : 0, DOLLY : 1, PAN : 2, TOUCH_ROTATE : 3, TOUCH_DOLLY : 4, TOUCH_PAN : 5 };
112 |
113 | var state = STATE.NONE;
114 |
115 | // for reset
116 |
117 | this.target0 = this.target.clone();
118 | this.position0 = this.object.position.clone();
119 |
120 | // so camera.up is the orbit axis
121 |
122 | var quat = new THREE.Quaternion().setFromUnitVectors( object.up, new THREE.Vector3( 0, 1, 0 ) );
123 | var quatInverse = quat.clone().inverse();
124 |
125 | // events
126 |
127 | var changeEvent = { type: 'change' };
128 | var startEvent = { type: 'start'};
129 | var endEvent = { type: 'end'};
130 |
131 | this.rotateLeft = function ( angle ) {
132 |
133 | if ( angle === undefined ) {
134 |
135 | angle = getAutoRotationAngle();
136 |
137 | }
138 |
139 | thetaDelta -= angle;
140 |
141 | };
142 |
143 | this.rotateUp = function ( angle ) {
144 |
145 | if ( angle === undefined ) {
146 |
147 | angle = getAutoRotationAngle();
148 |
149 | }
150 |
151 | phiDelta -= angle;
152 |
153 | };
154 |
155 | // pass in distance in world space to move left
156 | this.panLeft = function ( distance ) {
157 |
158 | var te = this.object.matrix.elements;
159 |
160 | // get X column of matrix
161 | panOffset.set( te[ 0 ], te[ 1 ], te[ 2 ] );
162 | panOffset.multiplyScalar( - distance );
163 |
164 | pan.add( panOffset );
165 |
166 | };
167 |
168 | // pass in distance in world space to move up
169 | this.panUp = function ( distance ) {
170 |
171 | var te = this.object.matrix.elements;
172 |
173 | // get Y column of matrix
174 | panOffset.set( te[ 4 ], te[ 5 ], te[ 6 ] );
175 | panOffset.multiplyScalar( distance );
176 |
177 | pan.add( panOffset );
178 |
179 | };
180 |
181 | // pass in x,y of change desired in pixel space,
182 | // right and down are positive
183 | this.pan = function ( deltaX, deltaY ) {
184 |
185 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
186 |
187 | if ( scope.object.fov !== undefined ) {
188 |
189 | // perspective
190 | var position = scope.object.position;
191 | var offset = position.clone().sub( scope.target );
192 | var targetDistance = offset.length();
193 |
194 | // half of the fov is center to top of screen
195 | targetDistance *= Math.tan( ( scope.object.fov / 2 ) * Math.PI / 180.0 );
196 |
197 | // we actually don't use screenWidth, since perspective camera is fixed to screen height
198 | scope.panLeft( 2 * deltaX * targetDistance / element.clientHeight );
199 | scope.panUp( 2 * deltaY * targetDistance / element.clientHeight );
200 |
201 | } else if ( scope.object.top !== undefined ) {
202 |
203 | // orthographic
204 | scope.panLeft( deltaX * (scope.object.right - scope.object.left) / element.clientWidth );
205 | scope.panUp( deltaY * (scope.object.top - scope.object.bottom) / element.clientHeight );
206 |
207 | } else {
208 |
209 | // camera neither orthographic or perspective
210 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' );
211 |
212 | }
213 |
214 | };
215 |
216 | this.dollyIn = function ( dollyScale ) {
217 |
218 | if ( dollyScale === undefined ) {
219 |
220 | dollyScale = getZoomScale();
221 |
222 | }
223 |
224 | scale /= dollyScale;
225 |
226 | };
227 |
228 | this.dollyOut = function ( dollyScale ) {
229 |
230 | if ( dollyScale === undefined ) {
231 |
232 | dollyScale = getZoomScale();
233 |
234 | }
235 |
236 | scale *= dollyScale;
237 |
238 | };
239 |
240 | this.update = function () {
241 |
242 | var position = this.object.position;
243 |
244 | offset.copy( position ).sub( this.target );
245 |
246 | // rotate offset to "y-axis-is-up" space
247 | offset.applyQuaternion( quat );
248 |
249 | // angle from z-axis around y-axis
250 |
251 | var theta = Math.atan2( offset.x, offset.z );
252 |
253 | // angle from y-axis
254 |
255 | var phi = Math.atan2( Math.sqrt( offset.x * offset.x + offset.z * offset.z ), offset.y );
256 |
257 | if ( this.autoRotate ) {
258 |
259 | this.rotateLeft( getAutoRotationAngle() );
260 |
261 | }
262 |
263 | theta += thetaDelta;
264 | phi += phiDelta;
265 |
266 | // restrict theta to be between desired limits
267 | theta = Math.max( this.minAzimuthAngle, Math.min( this.maxAzimuthAngle, theta ) );
268 |
269 | // restrict phi to be between desired limits
270 | phi = Math.max( this.minPolarAngle, Math.min( this.maxPolarAngle, phi ) );
271 |
272 | // restrict phi to be betwee EPS and PI-EPS
273 | phi = Math.max( EPS, Math.min( Math.PI - EPS, phi ) );
274 |
275 | var radius = offset.length() * scale;
276 |
277 | // restrict radius to be between desired limits
278 | radius = Math.max( this.minDistance, Math.min( this.maxDistance, radius ) );
279 |
280 | // move target to panned location
281 | this.target.add( pan );
282 |
283 | offset.x = radius * Math.sin( phi ) * Math.sin( theta );
284 | offset.y = radius * Math.cos( phi );
285 | offset.z = radius * Math.sin( phi ) * Math.cos( theta );
286 |
287 | // rotate offset back to "camera-up-vector-is-up" space
288 | offset.applyQuaternion( quatInverse );
289 |
290 | position.copy( this.target ).add( offset );
291 |
292 | this.object.lookAt( this.target );
293 |
294 | thetaDelta = 0;
295 | phiDelta = 0;
296 | scale = 1;
297 | pan.set( 0, 0, 0 );
298 |
299 | // update condition is:
300 | // min(camera displacement, camera rotation in radians)^2 > EPS
301 | // using small-angle approximation cos(x/2) = 1 - x^2 / 8
302 |
303 | if ( lastPosition.distanceToSquared( this.object.position ) > EPS
304 | || 8 * (1 - lastQuaternion.dot(this.object.quaternion)) > EPS ) {
305 |
306 | this.dispatchEvent( changeEvent );
307 |
308 | lastPosition.copy( this.object.position );
309 | lastQuaternion.copy (this.object.quaternion );
310 |
311 | }
312 |
313 | };
314 |
315 |
316 | this.reset = function () {
317 |
318 | state = STATE.NONE;
319 |
320 | this.target.copy( this.target0 );
321 | this.object.position.copy( this.position0 );
322 |
323 | this.update();
324 |
325 | };
326 |
327 | function getAutoRotationAngle() {
328 |
329 | return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed;
330 |
331 | }
332 |
333 | function getZoomScale() {
334 |
335 | return Math.pow( 0.95, scope.zoomSpeed );
336 |
337 | }
338 |
339 | function onMouseDown( event ) {
340 |
341 | if ( scope.enabled === false ) return;
342 | event.preventDefault();
343 |
344 | if ( event.button === scope.mouseButtons.ORBIT ) {
345 | if ( scope.noRotate === true ) return;
346 |
347 | state = STATE.ROTATE;
348 |
349 | rotateStart.set( event.clientX, event.clientY );
350 |
351 | } else if ( event.button === scope.mouseButtons.ZOOM ) {
352 | if ( scope.noZoom === true ) return;
353 |
354 | state = STATE.DOLLY;
355 |
356 | dollyStart.set( event.clientX, event.clientY );
357 |
358 | } else if ( event.button === scope.mouseButtons.PAN ) {
359 | if ( scope.noPan === true ) return;
360 |
361 | state = STATE.PAN;
362 |
363 | panStart.set( event.clientX, event.clientY );
364 |
365 | }
366 |
367 | document.addEventListener( 'mousemove', onMouseMove, false );
368 | document.addEventListener( 'mouseup', onMouseUp, false );
369 | scope.dispatchEvent( startEvent );
370 |
371 | }
372 |
373 | function onMouseMove( event ) {
374 |
375 | if ( scope.enabled === false ) return;
376 |
377 | event.preventDefault();
378 |
379 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
380 |
381 | if ( state === STATE.ROTATE ) {
382 |
383 | if ( scope.noRotate === true ) return;
384 |
385 | rotateEnd.set( event.clientX, event.clientY );
386 | rotateDelta.subVectors( rotateEnd, rotateStart );
387 |
388 | // rotating across whole screen goes 360 degrees around
389 | scope.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed );
390 |
391 | // rotating up and down along whole screen attempts to go 360, but limited to 180
392 | scope.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed );
393 |
394 | rotateStart.copy( rotateEnd );
395 |
396 | } else if ( state === STATE.DOLLY ) {
397 |
398 | if ( scope.noZoom === true ) return;
399 |
400 | dollyEnd.set( event.clientX, event.clientY );
401 | dollyDelta.subVectors( dollyEnd, dollyStart );
402 |
403 | if ( dollyDelta.y > 0 ) {
404 |
405 | scope.dollyIn();
406 |
407 | } else {
408 |
409 | scope.dollyOut();
410 |
411 | }
412 |
413 | dollyStart.copy( dollyEnd );
414 |
415 | } else if ( state === STATE.PAN ) {
416 |
417 | if ( scope.noPan === true ) return;
418 |
419 | panEnd.set( event.clientX, event.clientY );
420 | panDelta.subVectors( panEnd, panStart );
421 |
422 | scope.pan( panDelta.x, panDelta.y );
423 |
424 | panStart.copy( panEnd );
425 |
426 | }
427 |
428 | scope.update();
429 |
430 | }
431 |
432 | function onMouseUp( /* event */ ) {
433 |
434 | if ( scope.enabled === false ) return;
435 |
436 | document.removeEventListener( 'mousemove', onMouseMove, false );
437 | document.removeEventListener( 'mouseup', onMouseUp, false );
438 | scope.dispatchEvent( endEvent );
439 | state = STATE.NONE;
440 |
441 | }
442 |
443 | function onMouseWheel( event ) {
444 |
445 | if ( scope.enabled === false || scope.noZoom === true ) return;
446 |
447 | event.preventDefault();
448 | event.stopPropagation();
449 |
450 | var delta = 0;
451 |
452 | if ( event.wheelDelta !== undefined ) { // WebKit / Opera / Explorer 9
453 |
454 | delta = event.wheelDelta;
455 |
456 | } else if ( event.detail !== undefined ) { // Firefox
457 |
458 | delta = - event.detail;
459 |
460 | }
461 |
462 | if ( delta > 0 ) {
463 |
464 | scope.dollyOut();
465 |
466 | } else {
467 |
468 | scope.dollyIn();
469 |
470 | }
471 |
472 | scope.update();
473 | scope.dispatchEvent( startEvent );
474 | scope.dispatchEvent( endEvent );
475 |
476 | }
477 |
478 | function onKeyDown( event ) {
479 |
480 | if ( scope.enabled === false || scope.noKeys === true || scope.noPan === true ) return;
481 |
482 | switch ( event.keyCode ) {
483 |
484 | case scope.keys.UP:
485 | scope.pan( 0, scope.keyPanSpeed );
486 | scope.update();
487 | break;
488 |
489 | case scope.keys.BOTTOM:
490 | scope.pan( 0, - scope.keyPanSpeed );
491 | scope.update();
492 | break;
493 |
494 | case scope.keys.LEFT:
495 | scope.pan( scope.keyPanSpeed, 0 );
496 | scope.update();
497 | break;
498 |
499 | case scope.keys.RIGHT:
500 | scope.pan( - scope.keyPanSpeed, 0 );
501 | scope.update();
502 | break;
503 |
504 | }
505 |
506 | }
507 |
508 | function touchstart( event ) {
509 |
510 | if ( scope.enabled === false ) return;
511 |
512 | switch ( event.touches.length ) {
513 |
514 | case 1: // one-fingered touch: rotate
515 |
516 | if ( scope.noRotate === true ) return;
517 |
518 | state = STATE.TOUCH_ROTATE;
519 |
520 | rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
521 | break;
522 |
523 | case 2: // two-fingered touch: dolly
524 |
525 | if ( scope.noZoom === true ) return;
526 |
527 | state = STATE.TOUCH_DOLLY;
528 |
529 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
530 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
531 | var distance = Math.sqrt( dx * dx + dy * dy );
532 | dollyStart.set( 0, distance );
533 | break;
534 |
535 | case 3: // three-fingered touch: pan
536 |
537 | if ( scope.noPan === true ) return;
538 |
539 | state = STATE.TOUCH_PAN;
540 |
541 | panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
542 | break;
543 |
544 | default:
545 |
546 | state = STATE.NONE;
547 |
548 | }
549 |
550 | scope.dispatchEvent( startEvent );
551 |
552 | }
553 |
554 | function touchmove( event ) {
555 |
556 | if ( scope.enabled === false ) return;
557 |
558 | event.preventDefault();
559 | event.stopPropagation();
560 |
561 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
562 |
563 | switch ( event.touches.length ) {
564 |
565 | case 1: // one-fingered touch: rotate
566 |
567 | if ( scope.noRotate === true ) return;
568 | if ( state !== STATE.TOUCH_ROTATE ) return;
569 |
570 | rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
571 | rotateDelta.subVectors( rotateEnd, rotateStart );
572 |
573 | // rotating across whole screen goes 360 degrees around
574 | scope.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed );
575 | // rotating up and down along whole screen attempts to go 360, but limited to 180
576 | scope.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed );
577 |
578 | rotateStart.copy( rotateEnd );
579 |
580 | scope.update();
581 | break;
582 |
583 | case 2: // two-fingered touch: dolly
584 |
585 | if ( scope.noZoom === true ) return;
586 | if ( state !== STATE.TOUCH_DOLLY ) return;
587 |
588 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
589 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
590 | var distance = Math.sqrt( dx * dx + dy * dy );
591 |
592 | dollyEnd.set( 0, distance );
593 | dollyDelta.subVectors( dollyEnd, dollyStart );
594 |
595 | if ( dollyDelta.y > 0 ) {
596 |
597 | scope.dollyOut();
598 |
599 | } else {
600 |
601 | scope.dollyIn();
602 |
603 | }
604 |
605 | dollyStart.copy( dollyEnd );
606 |
607 | scope.update();
608 | break;
609 |
610 | case 3: // three-fingered touch: pan
611 |
612 | if ( scope.noPan === true ) return;
613 | if ( state !== STATE.TOUCH_PAN ) return;
614 |
615 | panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
616 | panDelta.subVectors( panEnd, panStart );
617 |
618 | scope.pan( panDelta.x, panDelta.y );
619 |
620 | panStart.copy( panEnd );
621 |
622 | scope.update();
623 | break;
624 |
625 | default:
626 |
627 | state = STATE.NONE;
628 |
629 | }
630 |
631 | }
632 |
633 | function touchend( /* event */ ) {
634 |
635 | if ( scope.enabled === false ) return;
636 |
637 | scope.dispatchEvent( endEvent );
638 | state = STATE.NONE;
639 |
640 | }
641 |
642 | this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false );
643 | this.domElement.addEventListener( 'mousedown', onMouseDown, false );
644 | this.domElement.addEventListener( 'mousewheel', onMouseWheel, false );
645 | this.domElement.addEventListener( 'DOMMouseScroll', onMouseWheel, false ); // firefox
646 |
647 | this.domElement.addEventListener( 'touchstart', touchstart, false );
648 | this.domElement.addEventListener( 'touchend', touchend, false );
649 | this.domElement.addEventListener( 'touchmove', touchmove, false );
650 |
651 | window.addEventListener( 'keydown', onKeyDown, false );
652 |
653 | // force an update at start
654 | this.update();
655 |
656 | };
657 |
658 | THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype );
659 |
--------------------------------------------------------------------------------
/vendor/three/TransformControls.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author arodic / https://github.com/arodic
3 | */
4 | /*jshint sub:true*/
5 |
6 | (function () {
7 |
8 | 'use strict';
9 |
10 | var GizmoMaterial = function ( parameters ) {
11 |
12 | THREE.MeshBasicMaterial.call( this );
13 |
14 | this.depthTest = false;
15 | this.depthWrite = false;
16 | this.side = THREE.FrontSide;
17 | this.transparent = true;
18 |
19 | this.setValues( parameters );
20 |
21 | this.oldColor = this.color.clone();
22 | this.oldOpacity = this.opacity;
23 |
24 | this.highlight = function( highlighted ) {
25 |
26 | if ( highlighted ) {
27 |
28 | this.color.setRGB( 1, 1, 0 );
29 | this.opacity = 1;
30 |
31 | } else {
32 |
33 | this.color.copy( this.oldColor );
34 | this.opacity = this.oldOpacity;
35 |
36 | }
37 |
38 | };
39 |
40 | };
41 |
42 | GizmoMaterial.prototype = Object.create( THREE.MeshBasicMaterial.prototype );
43 |
44 | var GizmoLineMaterial = function ( parameters ) {
45 |
46 | THREE.LineBasicMaterial.call( this );
47 |
48 | this.depthTest = false;
49 | this.depthWrite = false;
50 | this.transparent = true;
51 | this.linewidth = 1;
52 |
53 | this.setValues( parameters );
54 |
55 | this.oldColor = this.color.clone();
56 | this.oldOpacity = this.opacity;
57 |
58 | this.highlight = function( highlighted ) {
59 |
60 | if ( highlighted ) {
61 |
62 | this.color.setRGB( 1, 1, 0 );
63 | this.opacity = 1;
64 |
65 | } else {
66 |
67 | this.color.copy( this.oldColor );
68 | this.opacity = this.oldOpacity;
69 |
70 | }
71 |
72 | };
73 |
74 | };
75 |
76 | GizmoLineMaterial.prototype = Object.create( THREE.LineBasicMaterial.prototype );
77 |
78 | THREE.TransformGizmo = function () {
79 |
80 | var scope = this;
81 | var showPickers = false; //debug
82 | var showActivePlane = false; //debug
83 |
84 | this.init = function () {
85 |
86 | THREE.Object3D.call( this );
87 |
88 | this.handles = new THREE.Object3D();
89 | this.pickers = new THREE.Object3D();
90 | this.planes = new THREE.Object3D();
91 |
92 | this.add(this.handles);
93 | this.add(this.pickers);
94 | this.add(this.planes);
95 |
96 | //// PLANES
97 |
98 | var planeGeometry = new THREE.PlaneGeometry( 50, 50, 2, 2 );
99 | var planeMaterial = new THREE.MeshBasicMaterial( { wireframe: true } );
100 | planeMaterial.side = THREE.DoubleSide;
101 |
102 | var planes = {
103 | "XY": new THREE.Mesh( planeGeometry, planeMaterial ),
104 | "YZ": new THREE.Mesh( planeGeometry, planeMaterial ),
105 | "XZ": new THREE.Mesh( planeGeometry, planeMaterial ),
106 | "XYZE": new THREE.Mesh( planeGeometry, planeMaterial )
107 | };
108 |
109 | this.activePlane = planes["XYZE"];
110 |
111 | planes["YZ"].rotation.set( 0, Math.PI/2, 0 );
112 | planes["XZ"].rotation.set( -Math.PI/2, 0, 0 );
113 |
114 | for (var i in planes) {
115 | planes[i].name = i;
116 | this.planes.add(planes[i]);
117 | this.planes[i] = planes[i];
118 | planes[i].visible = false;
119 | }
120 |
121 | //// HANDLES AND PICKERS
122 |
123 | var setupGizmos = function( gizmoMap, parent ) {
124 |
125 | for ( var name in gizmoMap ) {
126 |
127 | for ( i = gizmoMap[name].length; i--;) {
128 |
129 | var object = gizmoMap[name][i][0];
130 | var position = gizmoMap[name][i][1];
131 | var rotation = gizmoMap[name][i][2];
132 |
133 | object.name = name;
134 |
135 | if ( position ) object.position.set( position[0], position[1], position[2] );
136 | if ( rotation ) object.rotation.set( rotation[0], rotation[1], rotation[2] );
137 |
138 | parent.add( object );
139 |
140 | }
141 |
142 | }
143 |
144 | };
145 |
146 | setupGizmos(this.handleGizmos, this.handles);
147 | setupGizmos(this.pickerGizmos, this.pickers);
148 |
149 | // reset Transformations
150 |
151 | this.traverse(function ( child ) {
152 | if (child instanceof THREE.Mesh) {
153 | child.updateMatrix();
154 |
155 | var tempGeometry = new THREE.Geometry();
156 | tempGeometry.merge( child.geometry, child.matrix );
157 |
158 | child.geometry = tempGeometry;
159 | child.position.set( 0, 0, 0 );
160 | child.rotation.set( 0, 0, 0 );
161 | child.scale.set( 1, 1, 1 );
162 | }
163 | });
164 |
165 | };
166 |
167 | this.hide = function () {
168 | this.traverse(function( child ) {
169 | child.visible = false;
170 | });
171 | };
172 |
173 | this.show = function () {
174 | this.traverse(function( child ) {
175 | child.visible = true;
176 | if (child.parent == scope.pickers ) child.visible = showPickers;
177 | if (child.parent == scope.planes ) child.visible = false;
178 | });
179 | this.activePlane.visible = showActivePlane;
180 | };
181 |
182 | this.highlight = function ( axis ) {
183 | this.traverse(function( child ) {
184 | if ( child.material && child.material.highlight ){
185 | if ( child.name == axis ) {
186 | child.material.highlight( true );
187 | } else {
188 | child.material.highlight( false );
189 | }
190 | }
191 | });
192 | };
193 |
194 | };
195 |
196 | THREE.TransformGizmo.prototype = Object.create( THREE.Object3D.prototype );
197 |
198 | THREE.TransformGizmo.prototype.update = function ( rotation, eye ) {
199 |
200 | var vec1 = new THREE.Vector3( 0, 0, 0 );
201 | var vec2 = new THREE.Vector3( 0, 1, 0 );
202 | var lookAtMatrix = new THREE.Matrix4();
203 |
204 | this.traverse(function(child) {
205 | if ( child.name.search("E") != -1 ) {
206 | child.quaternion.setFromRotationMatrix( lookAtMatrix.lookAt( eye, vec1, vec2 ) );
207 | } else if ( child.name.search("X") != -1 || child.name.search("Y") != -1 || child.name.search("Z") != -1 ) {
208 | child.quaternion.setFromEuler( rotation );
209 | }
210 | });
211 |
212 | };
213 |
214 | THREE.TransformGizmoTranslate = function () {
215 |
216 | THREE.TransformGizmo.call( this );
217 |
218 | var arrowGeometry = new THREE.Geometry();
219 | var mesh = new THREE.Mesh( new THREE.CylinderGeometry( 0, 0.05, 0.2, 12, 1, false ) );
220 | mesh.position.y = 0.5;
221 | mesh.updateMatrix();
222 |
223 | arrowGeometry.merge( mesh.geometry, mesh.matrix );
224 |
225 | var lineXGeometry = new THREE.Geometry();
226 | lineXGeometry.vertices.push( new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 1, 0, 0 ) );
227 |
228 | var lineYGeometry = new THREE.Geometry();
229 | lineYGeometry.vertices.push( new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 0, 1, 0 ) );
230 |
231 | var lineZGeometry = new THREE.Geometry();
232 | lineZGeometry.vertices.push( new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 0, 0, 1 ) );
233 |
234 | this.handleGizmos = {
235 | X: [
236 | [ new THREE.Mesh( arrowGeometry, new GizmoMaterial( { color: 0xff0000 } ) ), [ 0.5, 0, 0 ], [ 0, 0, -Math.PI/2 ] ],
237 | [ new THREE.Line( lineXGeometry, new GizmoLineMaterial( { color: 0xff0000 } ) ) ]
238 | ],
239 | Y: [
240 | [ new THREE.Mesh( arrowGeometry, new GizmoMaterial( { color: 0x00ff00 } ) ), [ 0, 0.5, 0 ] ],
241 | [ new THREE.Line( lineYGeometry, new GizmoLineMaterial( { color: 0x00ff00 } ) ) ]
242 | ],
243 | Z: [
244 | [ new THREE.Mesh( arrowGeometry, new GizmoMaterial( { color: 0x0000ff } ) ), [ 0, 0, 0.5 ], [ Math.PI/2, 0, 0 ] ],
245 | [ new THREE.Line( lineZGeometry, new GizmoLineMaterial( { color: 0x0000ff } ) ) ]
246 | ],
247 | XYZ: [
248 | [ new THREE.Mesh( new THREE.OctahedronGeometry( 0.1, 0 ), new GizmoMaterial( { color: 0xffffff, opacity: 0.25 } ) ), [ 0, 0, 0 ], [ 0, 0, 0 ] ]
249 | ],
250 | XY: [
251 | [ new THREE.Mesh( new THREE.PlaneGeometry( 0.29, 0.29 ), new GizmoMaterial( { color: 0xffff00, opacity: 0.25 } ) ), [ 0.15, 0.15, 0 ] ]
252 | ],
253 | YZ: [
254 | [ new THREE.Mesh( new THREE.PlaneGeometry( 0.29, 0.29 ), new GizmoMaterial( { color: 0x00ffff, opacity: 0.25 } ) ), [ 0, 0.15, 0.15 ], [ 0, Math.PI/2, 0 ] ]
255 | ],
256 | XZ: [
257 | [ new THREE.Mesh( new THREE.PlaneGeometry( 0.29, 0.29 ), new GizmoMaterial( { color: 0xff00ff, opacity: 0.25 } ) ), [ 0.15, 0, 0.15 ], [ -Math.PI/2, 0, 0 ] ]
258 | ]
259 | };
260 |
261 | this.pickerGizmos = {
262 | X: [
263 | [ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 1, 4, 1, false ), new GizmoMaterial( { color: 0xff0000, opacity: 0.25 } ) ), [ 0.6, 0, 0 ], [ 0, 0, -Math.PI/2 ] ]
264 | ],
265 | Y: [
266 | [ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 1, 4, 1, false ), new GizmoMaterial( { color: 0x00ff00, opacity: 0.25 } ) ), [ 0, 0.6, 0 ] ]
267 | ],
268 | Z: [
269 | [ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 1, 4, 1, false ), new GizmoMaterial( { color: 0x0000ff, opacity: 0.25 } ) ), [ 0, 0, 0.6 ], [ Math.PI/2, 0, 0 ] ]
270 | ],
271 | XYZ: [
272 | [ new THREE.Mesh( new THREE.OctahedronGeometry( 0.2, 0 ), new GizmoMaterial( { color: 0xffffff, opacity: 0.25 } ) ) ]
273 | ],
274 | XY: [
275 | [ new THREE.Mesh( new THREE.PlaneGeometry( 0.4, 0.4 ), new GizmoMaterial( { color: 0xffff00, opacity: 0.25 } ) ), [ 0.2, 0.2, 0 ] ]
276 | ],
277 | YZ: [
278 | [ new THREE.Mesh( new THREE.PlaneGeometry( 0.4, 0.4 ), new GizmoMaterial( { color: 0x00ffff, opacity: 0.25 } ) ), [ 0, 0.2, 0.2 ], [ 0, Math.PI/2, 0 ] ]
279 | ],
280 | XZ: [
281 | [ new THREE.Mesh( new THREE.PlaneGeometry( 0.4, 0.4 ), new GizmoMaterial( { color: 0xff00ff, opacity: 0.25 } ) ), [ 0.2, 0, 0.2 ], [ -Math.PI/2, 0, 0 ] ]
282 | ]
283 | };
284 |
285 | this.setActivePlane = function ( axis, eye ) {
286 |
287 | var tempMatrix = new THREE.Matrix4();
288 | eye.applyMatrix4( tempMatrix.getInverse( tempMatrix.extractRotation( this.planes[ "XY" ].matrixWorld ) ) );
289 |
290 | if ( axis == "X" ) {
291 | this.activePlane = this.planes[ "XY" ];
292 | if ( Math.abs(eye.y) > Math.abs(eye.z) ) this.activePlane = this.planes[ "XZ" ];
293 | }
294 |
295 | if ( axis == "Y" ){
296 | this.activePlane = this.planes[ "XY" ];
297 | if ( Math.abs(eye.x) > Math.abs(eye.z) ) this.activePlane = this.planes[ "YZ" ];
298 | }
299 |
300 | if ( axis == "Z" ){
301 | this.activePlane = this.planes[ "XZ" ];
302 | if ( Math.abs(eye.x) > Math.abs(eye.y) ) this.activePlane = this.planes[ "YZ" ];
303 | }
304 |
305 | if ( axis == "XYZ" ) this.activePlane = this.planes[ "XYZE" ];
306 |
307 | if ( axis == "XY" ) this.activePlane = this.planes[ "XY" ];
308 |
309 | if ( axis == "YZ" ) this.activePlane = this.planes[ "YZ" ];
310 |
311 | if ( axis == "XZ" ) this.activePlane = this.planes[ "XZ" ];
312 |
313 | this.hide();
314 | this.show();
315 |
316 | };
317 |
318 | this.init();
319 |
320 | };
321 |
322 | THREE.TransformGizmoTranslate.prototype = Object.create( THREE.TransformGizmo.prototype );
323 |
324 | THREE.TransformGizmoRotate = function () {
325 |
326 | THREE.TransformGizmo.call( this );
327 |
328 | var CircleGeometry = function ( radius, facing, arc ) {
329 |
330 | var geometry = new THREE.Geometry();
331 | arc = arc ? arc : 1;
332 | for ( var i = 0; i <= 64 * arc; ++i ) {
333 | if ( facing == 'x' ) geometry.vertices.push( new THREE.Vector3( 0, Math.cos( i / 32 * Math.PI ), Math.sin( i / 32 * Math.PI ) ).multiplyScalar(radius) );
334 | if ( facing == 'y' ) geometry.vertices.push( new THREE.Vector3( Math.cos( i / 32 * Math.PI ), 0, Math.sin( i / 32 * Math.PI ) ).multiplyScalar(radius) );
335 | if ( facing == 'z' ) geometry.vertices.push( new THREE.Vector3( Math.sin( i / 32 * Math.PI ), Math.cos( i / 32 * Math.PI ), 0 ).multiplyScalar(radius) );
336 | }
337 |
338 | return geometry;
339 | };
340 |
341 | this.handleGizmos = {
342 | X: [
343 | [ new THREE.Line( new CircleGeometry(1,'x',0.5), new GizmoLineMaterial( { color: 0xff0000 } ) ) ]
344 | ],
345 | Y: [
346 | [ new THREE.Line( new CircleGeometry(1,'y',0.5), new GizmoLineMaterial( { color: 0x00ff00 } ) ) ]
347 | ],
348 | Z: [
349 | [ new THREE.Line( new CircleGeometry(1,'z',0.5), new GizmoLineMaterial( { color: 0x0000ff } ) ) ]
350 | ],
351 | E: [
352 | [ new THREE.Line( new CircleGeometry(1.25,'z',1), new GizmoLineMaterial( { color: 0xcccc00 } ) ) ]
353 | ],
354 | XYZE: [
355 | [ new THREE.Line( new CircleGeometry(1,'z',1), new GizmoLineMaterial( { color: 0x787878 } ) ) ]
356 | ]
357 | };
358 |
359 | this.pickerGizmos = {
360 | X: [
361 | [ new THREE.Mesh( new THREE.TorusGeometry( 1, 0.12, 4, 12, Math.PI ), new GizmoMaterial( { color: 0xff0000, opacity: 0.25 } ) ), [ 0, 0, 0 ], [ 0, -Math.PI/2, -Math.PI/2 ] ]
362 | ],
363 | Y: [
364 | [ new THREE.Mesh( new THREE.TorusGeometry( 1, 0.12, 4, 12, Math.PI ), new GizmoMaterial( { color: 0x00ff00, opacity: 0.25 } ) ), [ 0, 0, 0 ], [ Math.PI/2, 0, 0 ] ]
365 | ],
366 | Z: [
367 | [ new THREE.Mesh( new THREE.TorusGeometry( 1, 0.12, 4, 12, Math.PI ), new GizmoMaterial( { color: 0x0000ff, opacity: 0.25 } ) ), [ 0, 0, 0 ], [ 0, 0, -Math.PI/2 ] ]
368 | ],
369 | E: [
370 | [ new THREE.Mesh( new THREE.TorusGeometry( 1.25, 0.12, 2, 24 ), new GizmoMaterial( { color: 0xffff00, opacity: 0.25 } ) ) ]
371 | ],
372 | XYZE: [
373 | [ new THREE.Mesh( new THREE.Geometry() ) ]// TODO
374 | ]
375 | };
376 |
377 | this.setActivePlane = function ( axis ) {
378 |
379 | if ( axis == "E" ) this.activePlane = this.planes[ "XYZE" ];
380 |
381 | if ( axis == "X" ) this.activePlane = this.planes[ "YZ" ];
382 |
383 | if ( axis == "Y" ) this.activePlane = this.planes[ "XZ" ];
384 |
385 | if ( axis == "Z" ) this.activePlane = this.planes[ "XY" ];
386 |
387 | this.hide();
388 | this.show();
389 |
390 | };
391 |
392 | this.update = function ( rotation, eye2 ) {
393 |
394 | THREE.TransformGizmo.prototype.update.apply( this, arguments );
395 |
396 | var group = {
397 | handles: this["handles"],
398 | pickers: this["pickers"],
399 | };
400 |
401 | var tempMatrix = new THREE.Matrix4();
402 | var worldRotation = new THREE.Euler( 0, 0, 1 );
403 | var tempQuaternion = new THREE.Quaternion();
404 | var unitX = new THREE.Vector3( 1, 0, 0 );
405 | var unitY = new THREE.Vector3( 0, 1, 0 );
406 | var unitZ = new THREE.Vector3( 0, 0, 1 );
407 | var quaternionX = new THREE.Quaternion();
408 | var quaternionY = new THREE.Quaternion();
409 | var quaternionZ = new THREE.Quaternion();
410 | var eye = eye2.clone();
411 |
412 | worldRotation.copy( this.planes["XY"].rotation );
413 | tempQuaternion.setFromEuler( worldRotation );
414 |
415 | tempMatrix.makeRotationFromQuaternion( tempQuaternion ).getInverse( tempMatrix );
416 | eye.applyMatrix4( tempMatrix );
417 |
418 | this.traverse(function(child) {
419 |
420 | tempQuaternion.setFromEuler( worldRotation );
421 |
422 | if ( child.name == "X" ) {
423 | quaternionX.setFromAxisAngle( unitX, Math.atan2( -eye.y, eye.z ) );
424 | tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionX );
425 | child.quaternion.copy( tempQuaternion );
426 | }
427 |
428 | if ( child.name == "Y" ) {
429 | quaternionY.setFromAxisAngle( unitY, Math.atan2( eye.x, eye.z ) );
430 | tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionY );
431 | child.quaternion.copy( tempQuaternion );
432 | }
433 |
434 | if ( child.name == "Z" ) {
435 | quaternionZ.setFromAxisAngle( unitZ, Math.atan2( eye.y, eye.x ) );
436 | tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionZ );
437 | child.quaternion.copy( tempQuaternion );
438 | }
439 |
440 | });
441 |
442 | };
443 |
444 | this.init();
445 |
446 | };
447 |
448 | THREE.TransformGizmoRotate.prototype = Object.create( THREE.TransformGizmo.prototype );
449 |
450 | THREE.TransformGizmoScale = function () {
451 |
452 | THREE.TransformGizmo.call( this );
453 |
454 | var arrowGeometry = new THREE.Geometry();
455 | var mesh = new THREE.Mesh( new THREE.BoxGeometry( 0.125, 0.125, 0.125 ) );
456 | mesh.position.y = 0.5;
457 | mesh.updateMatrix();
458 |
459 | arrowGeometry.merge( mesh.geometry, mesh.matrix );
460 |
461 | var lineXGeometry = new THREE.Geometry();
462 | lineXGeometry.vertices.push( new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 1, 0, 0 ) );
463 |
464 | var lineYGeometry = new THREE.Geometry();
465 | lineYGeometry.vertices.push( new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 0, 1, 0 ) );
466 |
467 | var lineZGeometry = new THREE.Geometry();
468 | lineZGeometry.vertices.push( new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 0, 0, 1 ) );
469 |
470 | this.handleGizmos = {
471 | X: [
472 | [ new THREE.Mesh( arrowGeometry, new GizmoMaterial( { color: 0xff0000 } ) ), [ 0.5, 0, 0 ], [ 0, 0, -Math.PI/2 ] ],
473 | [ new THREE.Line( lineXGeometry, new GizmoLineMaterial( { color: 0xff0000 } ) ) ]
474 | ],
475 | Y: [
476 | [ new THREE.Mesh( arrowGeometry, new GizmoMaterial( { color: 0x00ff00 } ) ), [ 0, 0.5, 0 ] ],
477 | [ new THREE.Line( lineYGeometry, new GizmoLineMaterial( { color: 0x00ff00 } ) ) ]
478 | ],
479 | Z: [
480 | [ new THREE.Mesh( arrowGeometry, new GizmoMaterial( { color: 0x0000ff } ) ), [ 0, 0, 0.5 ], [ Math.PI/2, 0, 0 ] ],
481 | [ new THREE.Line( lineZGeometry, new GizmoLineMaterial( { color: 0x0000ff } ) ) ]
482 | ],
483 | XYZ: [
484 | [ new THREE.Mesh( new THREE.BoxGeometry( 0.125, 0.125, 0.125 ), new GizmoMaterial( { color: 0xffffff, opacity: 0.25 } ) ) ]
485 | ]
486 | };
487 |
488 | this.pickerGizmos = {
489 | X: [
490 | [ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 1, 4, 1, false ), new GizmoMaterial( { color: 0xff0000, opacity: 0.25 } ) ), [ 0.6, 0, 0 ], [ 0, 0, -Math.PI/2 ] ]
491 | ],
492 | Y: [
493 | [ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 1, 4, 1, false ), new GizmoMaterial( { color: 0x00ff00, opacity: 0.25 } ) ), [ 0, 0.6, 0 ] ]
494 | ],
495 | Z: [
496 | [ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 1, 4, 1, false ), new GizmoMaterial( { color: 0x0000ff, opacity: 0.25 } ) ), [ 0, 0, 0.6 ], [ Math.PI/2, 0, 0 ] ]
497 | ],
498 | XYZ: [
499 | [ new THREE.Mesh( new THREE.BoxGeometry( 0.4, 0.4, 0.4 ), new GizmoMaterial( { color: 0xffffff, opacity: 0.25 } ) ) ]
500 | ]
501 | };
502 |
503 | this.setActivePlane = function ( axis, eye ) {
504 |
505 | var tempMatrix = new THREE.Matrix4();
506 | eye.applyMatrix4( tempMatrix.getInverse( tempMatrix.extractRotation( this.planes[ "XY" ].matrixWorld ) ) );
507 |
508 | if ( axis == "X" ) {
509 | this.activePlane = this.planes[ "XY" ];
510 | if ( Math.abs(eye.y) > Math.abs(eye.z) ) this.activePlane = this.planes[ "XZ" ];
511 | }
512 |
513 | if ( axis == "Y" ){
514 | this.activePlane = this.planes[ "XY" ];
515 | if ( Math.abs(eye.x) > Math.abs(eye.z) ) this.activePlane = this.planes[ "YZ" ];
516 | }
517 |
518 | if ( axis == "Z" ){
519 | this.activePlane = this.planes[ "XZ" ];
520 | if ( Math.abs(eye.x) > Math.abs(eye.y) ) this.activePlane = this.planes[ "YZ" ];
521 | }
522 |
523 | if ( axis == "XYZ" ) this.activePlane = this.planes[ "XYZE" ];
524 |
525 | this.hide();
526 | this.show();
527 |
528 | };
529 |
530 | this.init();
531 |
532 | };
533 |
534 | THREE.TransformGizmoScale.prototype = Object.create( THREE.TransformGizmo.prototype );
535 |
536 | THREE.TransformControls = function ( camera, domElement ) {
537 |
538 | // TODO: Make non-uniform scale and rotate play nice in hierarchies
539 | // TODO: ADD RXYZ contol
540 |
541 | THREE.Object3D.call( this );
542 |
543 | domElement = ( domElement !== undefined ) ? domElement : document;
544 |
545 | this.gizmo = {};
546 | this.gizmo["translate"] = new THREE.TransformGizmoTranslate();
547 | this.gizmo["rotate"] = new THREE.TransformGizmoRotate();
548 | this.gizmo["scale"] = new THREE.TransformGizmoScale();
549 |
550 | this.add(this.gizmo["translate"]);
551 | this.add(this.gizmo["rotate"]);
552 | this.add(this.gizmo["scale"]);
553 |
554 | this.gizmo["translate"].hide();
555 | this.gizmo["rotate"].hide();
556 | this.gizmo["scale"].hide();
557 |
558 | this.object = undefined;
559 | this.snap = null;
560 | this.space = "world";
561 | this.size = 1;
562 | this.axis = null;
563 |
564 | var scope = this;
565 |
566 | var _dragging = false;
567 | var _mode = "translate";
568 | var _plane = "XY";
569 |
570 | var changeEvent = { type: "change" };
571 | var mouseDownEvent = { type: "mouseDown" };
572 | var mouseUpEvent = { type: "mouseUp", mode: _mode };
573 | var objectChangeEvent = { type: "objectChange" };
574 |
575 | var ray = new THREE.Raycaster();
576 | var pointerVector = new THREE.Vector3();
577 |
578 | var point = new THREE.Vector3();
579 | var offset = new THREE.Vector3();
580 |
581 | var rotation = new THREE.Vector3();
582 | var offsetRotation = new THREE.Vector3();
583 | var scale = 1;
584 |
585 | var lookAtMatrix = new THREE.Matrix4();
586 | var eye = new THREE.Vector3();
587 |
588 | var tempMatrix = new THREE.Matrix4();
589 | var tempVector = new THREE.Vector3();
590 | var tempQuaternion = new THREE.Quaternion();
591 | var unitX = new THREE.Vector3( 1, 0, 0 );
592 | var unitY = new THREE.Vector3( 0, 1, 0 );
593 | var unitZ = new THREE.Vector3( 0, 0, 1 );
594 |
595 | var quaternionXYZ = new THREE.Quaternion();
596 | var quaternionX = new THREE.Quaternion();
597 | var quaternionY = new THREE.Quaternion();
598 | var quaternionZ = new THREE.Quaternion();
599 | var quaternionE = new THREE.Quaternion();
600 |
601 | var oldPosition = new THREE.Vector3();
602 | var oldScale = new THREE.Vector3();
603 | var oldRotationMatrix = new THREE.Matrix4();
604 |
605 | var parentRotationMatrix = new THREE.Matrix4();
606 | var parentScale = new THREE.Vector3();
607 |
608 | var worldPosition = new THREE.Vector3();
609 | var worldRotation = new THREE.Euler();
610 | var worldRotationMatrix = new THREE.Matrix4();
611 | var camPosition = new THREE.Vector3();
612 | var camRotation = new THREE.Euler();
613 |
614 | domElement.addEventListener( "mousedown", onPointerDown, false );
615 | domElement.addEventListener( "touchstart", onPointerDown, false );
616 |
617 | domElement.addEventListener( "mousemove", onPointerHover, false );
618 | domElement.addEventListener( "touchmove", onPointerHover, false );
619 |
620 | domElement.addEventListener( "mousemove", onPointerMove, false );
621 | domElement.addEventListener( "touchmove", onPointerMove, false );
622 |
623 | domElement.addEventListener( "mouseup", onPointerUp, false );
624 | domElement.addEventListener( "mouseout", onPointerUp, false );
625 | domElement.addEventListener( "touchend", onPointerUp, false );
626 | domElement.addEventListener( "touchcancel", onPointerUp, false );
627 | domElement.addEventListener( "touchleave", onPointerUp, false );
628 |
629 | this.attach = function ( object ) {
630 |
631 | scope.object = object;
632 |
633 | this.gizmo["translate"].hide();
634 | this.gizmo["rotate"].hide();
635 | this.gizmo["scale"].hide();
636 | this.gizmo[_mode].show();
637 |
638 | scope.update();
639 |
640 | };
641 |
642 | this.detach = function ( object ) {
643 |
644 | scope.object = undefined;
645 | this.axis = null;
646 |
647 | this.gizmo["translate"].hide();
648 | this.gizmo["rotate"].hide();
649 | this.gizmo["scale"].hide();
650 |
651 | };
652 |
653 | this.setMode = function ( mode ) {
654 |
655 | _mode = mode ? mode : _mode;
656 |
657 | if ( _mode == "scale" ) scope.space = "local";
658 |
659 | this.gizmo["translate"].hide();
660 | this.gizmo["rotate"].hide();
661 | this.gizmo["scale"].hide();
662 | this.gizmo[_mode].show();
663 |
664 | this.update();
665 | scope.dispatchEvent( changeEvent );
666 |
667 | };
668 |
669 | this.setSnap = function ( snap ) {
670 |
671 | scope.snap = snap;
672 |
673 | };
674 |
675 | this.setSize = function ( size ) {
676 |
677 | scope.size = size;
678 | this.update();
679 | scope.dispatchEvent( changeEvent );
680 |
681 | };
682 |
683 | this.setSpace = function ( space ) {
684 |
685 | scope.space = space;
686 | this.update();
687 | scope.dispatchEvent( changeEvent );
688 |
689 | };
690 |
691 | this.update = function () {
692 |
693 | if ( scope.object === undefined ) return;
694 |
695 | scope.object.updateMatrixWorld();
696 | worldPosition.setFromMatrixPosition( scope.object.matrixWorld );
697 | worldRotation.setFromRotationMatrix( tempMatrix.extractRotation( scope.object.matrixWorld ) );
698 |
699 | camera.updateMatrixWorld();
700 | camPosition.setFromMatrixPosition( camera.matrixWorld );
701 | camRotation.setFromRotationMatrix( tempMatrix.extractRotation( camera.matrixWorld ) );
702 |
703 | scale = worldPosition.distanceTo( camPosition ) / 6 * scope.size;
704 | this.position.copy( worldPosition );
705 | this.scale.set( scale, scale, scale );
706 |
707 | eye.copy( camPosition ).sub( worldPosition ).normalize();
708 |
709 | if ( scope.space == "local" )
710 | this.gizmo[_mode].update( worldRotation, eye );
711 |
712 | else if ( scope.space == "world" )
713 | this.gizmo[_mode].update( new THREE.Euler(), eye );
714 |
715 | this.gizmo[_mode].highlight( scope.axis );
716 |
717 | };
718 |
719 | function onPointerHover( event ) {
720 |
721 | if ( scope.object === undefined || _dragging === true ) return;
722 |
723 | event.preventDefault();
724 |
725 | var pointer = event.changedTouches ? event.changedTouches[ 0 ] : event;
726 |
727 | var intersect = intersectObjects( pointer, scope.gizmo[_mode].pickers.children );
728 |
729 | var axis = null;
730 |
731 | if ( intersect ) {
732 |
733 | axis = intersect.object.name;
734 |
735 | }
736 |
737 | if ( scope.axis !== axis ) {
738 |
739 | scope.axis = axis;
740 | scope.update();
741 | scope.dispatchEvent( changeEvent );
742 |
743 | }
744 |
745 | }
746 |
747 | function onPointerDown( event ) {
748 |
749 | if ( scope.object === undefined || _dragging === true ) return;
750 |
751 | event.preventDefault();
752 | event.stopPropagation();
753 |
754 | var pointer = event.changedTouches ? event.changedTouches[ 0 ] : event;
755 |
756 | if ( pointer.button === 0 || pointer.button === undefined ) {
757 |
758 | var intersect = intersectObjects( pointer, scope.gizmo[_mode].pickers.children );
759 |
760 | if ( intersect ) {
761 |
762 | scope.dispatchEvent( mouseDownEvent );
763 |
764 | scope.axis = intersect.object.name;
765 |
766 | scope.update();
767 |
768 | eye.copy( camPosition ).sub( worldPosition ).normalize();
769 |
770 | scope.gizmo[_mode].setActivePlane( scope.axis, eye );
771 |
772 | var planeIntersect = intersectObjects( pointer, [scope.gizmo[_mode].activePlane] );
773 |
774 | oldPosition.copy( scope.object.position );
775 | oldScale.copy( scope.object.scale );
776 |
777 | oldRotationMatrix.extractRotation( scope.object.matrix );
778 | worldRotationMatrix.extractRotation( scope.object.matrixWorld );
779 |
780 | parentRotationMatrix.extractRotation( scope.object.parent.matrixWorld );
781 | parentScale.setFromMatrixScale( tempMatrix.getInverse( scope.object.parent.matrixWorld ) );
782 |
783 | offset.copy( planeIntersect.point );
784 |
785 | }
786 |
787 | }
788 |
789 | _dragging = true;
790 |
791 | }
792 |
793 | function onPointerMove( event ) {
794 |
795 | if ( scope.object === undefined || scope.axis === null || _dragging === false ) return;
796 |
797 | event.preventDefault();
798 | event.stopPropagation();
799 |
800 | var pointer = event.changedTouches? event.changedTouches[0] : event;
801 |
802 | var planeIntersect = intersectObjects( pointer, [scope.gizmo[_mode].activePlane] );
803 |
804 | point.copy( planeIntersect.point );
805 |
806 | if ( _mode == "translate" ) {
807 |
808 | point.sub( offset );
809 | point.multiply(parentScale);
810 |
811 | if ( scope.space == "local" ) {
812 |
813 | point.applyMatrix4( tempMatrix.getInverse( worldRotationMatrix ) );
814 |
815 | if ( scope.axis.search("X") == -1 ) point.x = 0;
816 | if ( scope.axis.search("Y") == -1 ) point.y = 0;
817 | if ( scope.axis.search("Z") == -1 ) point.z = 0;
818 |
819 | point.applyMatrix4( oldRotationMatrix );
820 |
821 | scope.object.position.copy( oldPosition );
822 | scope.object.position.add( point );
823 |
824 | }
825 |
826 | if ( scope.space == "world" || scope.axis.search("XYZ") != -1 ) {
827 |
828 | if ( scope.axis.search("X") == -1 ) point.x = 0;
829 | if ( scope.axis.search("Y") == -1 ) point.y = 0;
830 | if ( scope.axis.search("Z") == -1 ) point.z = 0;
831 |
832 | point.applyMatrix4( tempMatrix.getInverse( parentRotationMatrix ) );
833 |
834 | scope.object.position.copy( oldPosition );
835 | scope.object.position.add( point );
836 |
837 | }
838 |
839 | if ( scope.snap !== null ) {
840 |
841 | if ( scope.axis.search("X") != -1 ) scope.object.position.x = Math.round( scope.object.position.x / scope.snap ) * scope.snap;
842 | if ( scope.axis.search("Y") != -1 ) scope.object.position.y = Math.round( scope.object.position.y / scope.snap ) * scope.snap;
843 | if ( scope.axis.search("Z") != -1 ) scope.object.position.z = Math.round( scope.object.position.z / scope.snap ) * scope.snap;
844 |
845 | }
846 |
847 | } else if ( _mode == "scale" ) {
848 |
849 | point.sub( offset );
850 | point.multiply(parentScale);
851 |
852 | if ( scope.space == "local" ) {
853 |
854 | if ( scope.axis == "XYZ") {
855 |
856 | scale = 1 + ( ( point.y ) / 50 );
857 |
858 | scope.object.scale.x = oldScale.x * scale;
859 | scope.object.scale.y = oldScale.y * scale;
860 | scope.object.scale.z = oldScale.z * scale;
861 |
862 | } else {
863 |
864 | point.applyMatrix4( tempMatrix.getInverse( worldRotationMatrix ) );
865 |
866 | if ( scope.axis == "X" ) scope.object.scale.x = oldScale.x * ( 1 + point.x / 50 );
867 | if ( scope.axis == "Y" ) scope.object.scale.y = oldScale.y * ( 1 + point.y / 50 );
868 | if ( scope.axis == "Z" ) scope.object.scale.z = oldScale.z * ( 1 + point.z / 50 );
869 |
870 | }
871 |
872 | }
873 |
874 | } else if ( _mode == "rotate" ) {
875 |
876 | point.sub( worldPosition );
877 | point.multiply(parentScale);
878 | tempVector.copy(offset).sub( worldPosition );
879 | tempVector.multiply(parentScale);
880 |
881 | if ( scope.axis == "E" ) {
882 |
883 | point.applyMatrix4( tempMatrix.getInverse( lookAtMatrix ) );
884 | tempVector.applyMatrix4( tempMatrix.getInverse( lookAtMatrix ) );
885 |
886 | rotation.set( Math.atan2( point.z, point.y ), Math.atan2( point.x, point.z ), Math.atan2( point.y, point.x ) );
887 | offsetRotation.set( Math.atan2( tempVector.z, tempVector.y ), Math.atan2( tempVector.x, tempVector.z ), Math.atan2( tempVector.y, tempVector.x ) );
888 |
889 | tempQuaternion.setFromRotationMatrix( tempMatrix.getInverse( parentRotationMatrix ) );
890 |
891 | quaternionE.setFromAxisAngle( eye, rotation.z - offsetRotation.z );
892 | quaternionXYZ.setFromRotationMatrix( worldRotationMatrix );
893 |
894 | tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionE );
895 | tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionXYZ );
896 |
897 | scope.object.quaternion.copy( tempQuaternion );
898 |
899 | } else if ( scope.axis == "XYZE" ) {
900 |
901 | quaternionE.setFromEuler( point.clone().cross(tempVector).normalize() ); // rotation axis
902 |
903 | tempQuaternion.setFromRotationMatrix( tempMatrix.getInverse( parentRotationMatrix ) );
904 | quaternionX.setFromAxisAngle( quaternionE, - point.clone().angleTo(tempVector) );
905 | quaternionXYZ.setFromRotationMatrix( worldRotationMatrix );
906 |
907 | tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionX );
908 | tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionXYZ );
909 |
910 | scope.object.quaternion.copy( tempQuaternion );
911 |
912 | } else if ( scope.space == "local" ) {
913 |
914 | point.applyMatrix4( tempMatrix.getInverse( worldRotationMatrix ) );
915 |
916 | tempVector.applyMatrix4( tempMatrix.getInverse( worldRotationMatrix ) );
917 |
918 | rotation.set( Math.atan2( point.z, point.y ), Math.atan2( point.x, point.z ), Math.atan2( point.y, point.x ) );
919 | offsetRotation.set( Math.atan2( tempVector.z, tempVector.y ), Math.atan2( tempVector.x, tempVector.z ), Math.atan2( tempVector.y, tempVector.x ) );
920 |
921 | quaternionXYZ.setFromRotationMatrix( oldRotationMatrix );
922 | quaternionX.setFromAxisAngle( unitX, rotation.x - offsetRotation.x );
923 | quaternionY.setFromAxisAngle( unitY, rotation.y - offsetRotation.y );
924 | quaternionZ.setFromAxisAngle( unitZ, rotation.z - offsetRotation.z );
925 |
926 | if ( scope.axis == "X" ) quaternionXYZ.multiplyQuaternions( quaternionXYZ, quaternionX );
927 | if ( scope.axis == "Y" ) quaternionXYZ.multiplyQuaternions( quaternionXYZ, quaternionY );
928 | if ( scope.axis == "Z" ) quaternionXYZ.multiplyQuaternions( quaternionXYZ, quaternionZ );
929 |
930 | scope.object.quaternion.copy( quaternionXYZ );
931 |
932 | } else if ( scope.space == "world" ) {
933 |
934 | rotation.set( Math.atan2( point.z, point.y ), Math.atan2( point.x, point.z ), Math.atan2( point.y, point.x ) );
935 | offsetRotation.set( Math.atan2( tempVector.z, tempVector.y ), Math.atan2( tempVector.x, tempVector.z ), Math.atan2( tempVector.y, tempVector.x ) );
936 |
937 | tempQuaternion.setFromRotationMatrix( tempMatrix.getInverse( parentRotationMatrix ) );
938 |
939 | quaternionX.setFromAxisAngle( unitX, rotation.x - offsetRotation.x );
940 | quaternionY.setFromAxisAngle( unitY, rotation.y - offsetRotation.y );
941 | quaternionZ.setFromAxisAngle( unitZ, rotation.z - offsetRotation.z );
942 | quaternionXYZ.setFromRotationMatrix( worldRotationMatrix );
943 |
944 | if ( scope.axis == "X" ) tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionX );
945 | if ( scope.axis == "Y" ) tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionY );
946 | if ( scope.axis == "Z" ) tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionZ );
947 |
948 | tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionXYZ );
949 |
950 | scope.object.quaternion.copy( tempQuaternion );
951 |
952 | }
953 |
954 | }
955 |
956 | scope.update();
957 | scope.dispatchEvent( changeEvent );
958 | scope.dispatchEvent( objectChangeEvent );
959 |
960 | }
961 |
962 | function onPointerUp( event ) {
963 |
964 | if ( _dragging && ( scope.axis !== null ) ) {
965 | mouseUpEvent.mode = _mode;
966 | scope.dispatchEvent( mouseUpEvent )
967 | }
968 | _dragging = false;
969 | onPointerHover( event );
970 |
971 | }
972 |
973 | function intersectObjects( pointer, objects ) {
974 | var coords = THREE.CodeEditor.raycasting.getRelativeMouseXYFromEvent(pointer);
975 |
976 | pointerVector.copy(coords);
977 | pointerVector.unproject( camera );
978 |
979 | ray.set( camPosition, pointerVector.sub( camPosition ).normalize() );
980 |
981 | var intersections = ray.intersectObjects( objects, true );
982 | return intersections[0] ? intersections[0] : false;
983 |
984 | }
985 |
986 | };
987 |
988 | THREE.TransformControls.prototype = Object.create( THREE.Object3D.prototype );
989 |
990 | }());
991 |
--------------------------------------------------------------------------------
/vendor/three/VRControls.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author dmarcos / https://github.com/dmarcos
3 | * @author mrdoob / http://mrdoob.com
4 | */
5 |
6 | THREE.VRControls = function ( object, callback ) {
7 |
8 | var vrInput;
9 |
10 | var onVRDevices = function ( devices ) {
11 |
12 | for ( var i = 0; i < devices.length; i ++ ) {
13 |
14 | var device = devices[ i ];
15 |
16 | if ( device instanceof PositionSensorVRDevice ) {
17 |
18 | vrInput = devices[ i ];
19 | return; // We keep the first we encounter
20 |
21 | }
22 |
23 | }
24 |
25 | if ( callback !== undefined ) {
26 |
27 | callback( 'HMD not available' );
28 |
29 | }
30 |
31 | };
32 |
33 | if ( navigator.getVRDevices !== undefined ) {
34 |
35 | navigator.getVRDevices().then( onVRDevices );
36 |
37 | } else if ( callback !== undefined ) {
38 |
39 | callback( 'Your browser is not VR Ready' );
40 |
41 | }
42 |
43 | this.update = function () {
44 |
45 | if ( vrInput === undefined ) return;
46 |
47 | var state = vrInput.getState();
48 |
49 | if ( state.orientation !== null ) {
50 |
51 | object.quaternion.copy( state.orientation );
52 |
53 | }
54 |
55 | if ( state.position !== null ) {
56 |
57 | object.position.copy( state.position );
58 |
59 | }
60 |
61 | };
62 |
63 | this.zeroSensor = function () {
64 |
65 | if ( vrInput === undefined ) return;
66 |
67 | vrInput.zeroSensor();
68 |
69 | };
70 |
71 | };
72 |
--------------------------------------------------------------------------------
/vendor/three/VREffect.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author dmarcos / https://github.com/dmarcos
3 | *
4 | * It handles stereo rendering
5 | * If mozGetVRDevices and getVRDevices APIs are not available it gracefuly falls back to a
6 | * regular renderer
7 | *
8 | * The HMD supported is the Oculus DK1 and The Web API doesn't currently allow
9 | * to query for the display resolution (only the chrome API allows it).
10 | * The dimensions of the screen are temporarly hardcoded (1280 x 800).
11 | *
12 | * For VR mode to work it has to be used with the Oculus enabled builds of Firefox or Chrome:
13 | *
14 | * Firefox:
15 | *
16 | * OSX: http://people.mozilla.com/~vladimir/vr/firefox-33.0a1.en-US.mac.dmg
17 | * WIN: http://people.mozilla.com/~vladimir/vr/firefox-33.0a1.en-US.win64-x86_64.zip
18 | *
19 | * Chrome builds:
20 | *
21 | * https://drive.google.com/folderview?id=0BzudLt22BqGRbW9WTHMtOWMzNjQ&usp=sharing#list
22 | *
23 | */
24 | THREE.VREffect = function ( renderer, done ) {
25 |
26 | var cameraLeft = new THREE.PerspectiveCamera();
27 | var cameraRight = new THREE.PerspectiveCamera();
28 |
29 | this._renderer = renderer;
30 |
31 | this._init = function() {
32 | var self = this;
33 | if ( !navigator.mozGetVRDevices && !navigator.getVRDevices ) {
34 | if ( done ) {
35 | done("Your browser is not VR Ready");
36 | }
37 | return;
38 | }
39 | if ( navigator.getVRDevices ) {
40 | navigator.getVRDevices().then( gotVRDevices );
41 | } else {
42 | navigator.mozGetVRDevices( gotVRDevices );
43 | }
44 | function gotVRDevices( devices ) {
45 | var vrHMD;
46 | var error;
47 | for ( var i = 0; i < devices.length; ++i ) {
48 | if ( devices[i] instanceof HMDVRDevice ) {
49 | vrHMD = devices[i];
50 | self._vrHMD = vrHMD;
51 | self.leftEyeTranslation = vrHMD.getEyeTranslation( "left" );
52 | self.rightEyeTranslation = vrHMD.getEyeTranslation( "right" );
53 | self.leftEyeFOV = vrHMD.getRecommendedEyeFieldOfView( "left" );
54 | self.rightEyeFOV = vrHMD.getRecommendedEyeFieldOfView( "right" );
55 | break; // We keep the first we encounter
56 | }
57 | }
58 | if ( done ) {
59 | if ( !vrHMD ) {
60 | error = 'HMD not available';
61 | }
62 | done( error );
63 | }
64 | }
65 | };
66 |
67 | this._init();
68 |
69 | this.render = function ( scene, camera ) {
70 | var renderer = this._renderer;
71 | var vrHMD = this._vrHMD;
72 | // VR render mode if HMD is available
73 | if ( vrHMD ) {
74 | this.renderStereo.apply( this, arguments );
75 | return;
76 | }
77 | // Regular render mode if not HMD
78 | renderer.render.apply( this._renderer, arguments );
79 | };
80 |
81 | this.renderStereo = function( scene, camera, renderTarget, forceClear ) {
82 |
83 | var leftEyeTranslation = this.leftEyeTranslation;
84 | var rightEyeTranslation = this.rightEyeTranslation;
85 | var renderer = this._renderer;
86 | var rendererWidth = renderer.domElement.width / renderer.devicePixelRatio;
87 | var rendererHeight = renderer.domElement.height / renderer.devicePixelRatio;
88 | var eyeDivisionLine = rendererWidth / 2;
89 |
90 | renderer.enableScissorTest( true );
91 | renderer.clear();
92 |
93 | if ( camera.parent === undefined ) {
94 | camera.updateMatrixWorld();
95 | }
96 |
97 | cameraLeft.projectionMatrix = this.FovToProjection( this.leftEyeFOV, true, camera.near, camera.far );
98 | cameraRight.projectionMatrix = this.FovToProjection( this.rightEyeFOV, true, camera.near, camera.far );
99 |
100 | camera.matrixWorld.decompose( cameraLeft.position, cameraLeft.quaternion, cameraLeft.scale );
101 | camera.matrixWorld.decompose( cameraRight.position, cameraRight.quaternion, cameraRight.scale );
102 |
103 | cameraLeft.translateX( leftEyeTranslation.x );
104 | cameraRight.translateX( rightEyeTranslation.x );
105 |
106 | // render left eye
107 | renderer.setViewport( 0, 0, eyeDivisionLine, rendererHeight );
108 | renderer.setScissor( 0, 0, eyeDivisionLine, rendererHeight );
109 | renderer.render( scene, cameraLeft );
110 |
111 | // render right eye
112 | renderer.setViewport( eyeDivisionLine, 0, eyeDivisionLine, rendererHeight );
113 | renderer.setScissor( eyeDivisionLine, 0, eyeDivisionLine, rendererHeight );
114 | renderer.render( scene, cameraRight );
115 |
116 | renderer.enableScissorTest( false );
117 |
118 | };
119 |
120 | this.setSize = function( width, height ) {
121 | renderer.setSize( width, height );
122 | };
123 |
124 | this.setFullScreen = function( enable ) {
125 | var renderer = this._renderer;
126 | var vrHMD = this._vrHMD;
127 | var canvasOriginalSize = this._canvasOriginalSize;
128 | if (!vrHMD) {
129 | return;
130 | }
131 | // If state doesn't change we do nothing
132 | if ( enable === this._fullScreen ) {
133 | return;
134 | }
135 | this._fullScreen = !!enable;
136 |
137 | // VR Mode disabled
138 | if ( !enable ) {
139 | // Restores canvas original size
140 | renderer.setSize( canvasOriginalSize.width, canvasOriginalSize.height );
141 | return;
142 | }
143 | // VR Mode enabled
144 | this._canvasOriginalSize = {
145 | width: renderer.domElement.width,
146 | height: renderer.domElement.height
147 | };
148 | // Hardcoded Rift display size
149 | renderer.setSize( 1280, 800, false );
150 | this.startFullscreen();
151 | };
152 |
153 | this.startFullscreen = function() {
154 | var self = this;
155 | var renderer = this._renderer;
156 | var vrHMD = this._vrHMD;
157 | var canvas = renderer.domElement;
158 | var fullScreenChange =
159 | canvas.mozRequestFullScreen? 'mozfullscreenchange' : 'webkitfullscreenchange';
160 |
161 | document.addEventListener( fullScreenChange, onFullScreenChanged, false );
162 | function onFullScreenChanged() {
163 | if ( !document.mozFullScreenElement && !document.webkitFullScreenElement ) {
164 | self.setFullScreen( false );
165 | }
166 | }
167 | if ( canvas.mozRequestFullScreen ) {
168 | canvas.mozRequestFullScreen( { vrDisplay: vrHMD } );
169 | } else {
170 | canvas.webkitRequestFullscreen( { vrDisplay: vrHMD } );
171 | }
172 | };
173 |
174 | this.FovToNDCScaleOffset = function( fov ) {
175 | var pxscale = 2.0 / (fov.leftTan + fov.rightTan);
176 | var pxoffset = (fov.leftTan - fov.rightTan) * pxscale * 0.5;
177 | var pyscale = 2.0 / (fov.upTan + fov.downTan);
178 | var pyoffset = (fov.upTan - fov.downTan) * pyscale * 0.5;
179 | return { scale: [pxscale, pyscale], offset: [pxoffset, pyoffset] };
180 | };
181 |
182 | this.FovPortToProjection = function( fov, rightHanded /* = true */, zNear /* = 0.01 */, zFar /* = 10000.0 */ )
183 | {
184 | rightHanded = rightHanded === undefined ? true : rightHanded;
185 | zNear = zNear === undefined ? 0.01 : zNear;
186 | zFar = zFar === undefined ? 10000.0 : zFar;
187 |
188 | var handednessScale = rightHanded ? -1.0 : 1.0;
189 |
190 | // start with an identity matrix
191 | var mobj = new THREE.Matrix4();
192 | var m = mobj.elements;
193 |
194 | // and with scale/offset info for normalized device coords
195 | var scaleAndOffset = this.FovToNDCScaleOffset(fov);
196 |
197 | // X result, map clip edges to [-w,+w]
198 | m[0*4+0] = scaleAndOffset.scale[0];
199 | m[0*4+1] = 0.0;
200 | m[0*4+2] = scaleAndOffset.offset[0] * handednessScale;
201 | m[0*4+3] = 0.0;
202 |
203 | // Y result, map clip edges to [-w,+w]
204 | // Y offset is negated because this proj matrix transforms from world coords with Y=up,
205 | // but the NDC scaling has Y=down (thanks D3D?)
206 | m[1*4+0] = 0.0;
207 | m[1*4+1] = scaleAndOffset.scale[1];
208 | m[1*4+2] = -scaleAndOffset.offset[1] * handednessScale;
209 | m[1*4+3] = 0.0;
210 |
211 | // Z result (up to the app)
212 | m[2*4+0] = 0.0;
213 | m[2*4+1] = 0.0;
214 | m[2*4+2] = zFar / (zNear - zFar) * -handednessScale;
215 | m[2*4+3] = (zFar * zNear) / (zNear - zFar);
216 |
217 | // W result (= Z in)
218 | m[3*4+0] = 0.0;
219 | m[3*4+1] = 0.0;
220 | m[3*4+2] = handednessScale;
221 | m[3*4+3] = 0.0;
222 |
223 | mobj.transpose();
224 |
225 | return mobj;
226 | };
227 |
228 | this.FovToProjection = function( fov, rightHanded /* = true */, zNear /* = 0.01 */, zFar /* = 10000.0 */ )
229 | {
230 | var fovPort = {
231 | upTan: Math.tan(fov.upDegrees * Math.PI / 180.0),
232 | downTan: Math.tan(fov.downDegrees * Math.PI / 180.0),
233 | leftTan: Math.tan(fov.leftDegrees * Math.PI / 180.0),
234 | rightTan: Math.tan(fov.rightDegrees * Math.PI / 180.0)
235 | };
236 | return this.FovPortToProjection(fovPort, rightHanded, zNear, zFar);
237 | };
238 |
239 | };
240 |
--------------------------------------------------------------------------------