aboutsummaryrefslogtreecommitdiff
path: root/lib/dat.gui.js
diff options
context:
space:
mode:
authorFederico Igne <git@federicoigne.com>2022-08-13 11:33:30 +0100
committerFederico Igne <git@federicoigne.com>2022-08-13 11:33:30 +0100
commit2fd9cc46ff2428bb2fa891f47002e9d3e7585583 (patch)
tree81b8b0122a00e9cd06d5f55091a3b087e430ddb0 /lib/dat.gui.js
downloadrubik-2fd9cc46ff2428bb2fa891f47002e9d3e7585583.tar.gz
rubik-2fd9cc46ff2428bb2fa891f47002e9d3e7585583.zip
init: import project
Diffstat (limited to 'lib/dat.gui.js')
-rwxr-xr-xlib/dat.gui.js3672
1 files changed, 3672 insertions, 0 deletions
diff --git a/lib/dat.gui.js b/lib/dat.gui.js
new file mode 100755
index 0000000..315c470
--- /dev/null
+++ b/lib/dat.gui.js
@@ -0,0 +1,3672 @@
1/**
2 * dat-gui JavaScript Controller Library
3 * http://code.google.com/p/dat-gui
4 *
5 * Copyright 2011 Data Arts Team, Google Creative Lab
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 */
13
14/** @namespace */
15var dat = dat || {};
16
17/** @namespace */
18dat.gui = dat.gui || {};
19
20/** @namespace */
21dat.utils = dat.utils || {};
22
23/** @namespace */
24dat.controllers = dat.controllers || {};
25
26/** @namespace */
27dat.dom = dat.dom || {};
28
29/** @namespace */
30dat.color = dat.color || {};
31
32dat.utils.css = (function () {
33 return {
34 load: function (url, doc) {
35 doc = doc || document;
36 var link = doc.createElement('link');
37 link.type = 'text/css';
38 link.rel = 'stylesheet';
39 link.href = url;
40 doc.getElementsByTagName('head')[0].appendChild(link);
41 },
42 inject: function(css, doc) {
43 doc = doc || document;
44 var injected = document.createElement('style');
45 injected.type = 'text/css';
46 injected.innerHTML = css;
47 doc.getElementsByTagName('head')[0].appendChild(injected);
48 }
49 }
50})();
51
52
53dat.utils.common = (function () {
54
55 var ARR_EACH = Array.prototype.forEach;
56 var ARR_SLICE = Array.prototype.slice;
57
58 /**
59 * Band-aid methods for things that should be a lot easier in JavaScript.
60 * Implementation and structure inspired by underscore.js
61 * http://documentcloud.github.com/underscore/
62 */
63
64 return {
65
66 BREAK: {},
67
68 extend: function(target) {
69
70 this.each(ARR_SLICE.call(arguments, 1), function(obj) {
71
72 for (var key in obj)
73 if (!this.isUndefined(obj[key]))
74 target[key] = obj[key];
75
76 }, this);
77
78 return target;
79
80 },
81
82 defaults: function(target) {
83
84 this.each(ARR_SLICE.call(arguments, 1), function(obj) {
85
86 for (var key in obj)
87 if (this.isUndefined(target[key]))
88 target[key] = obj[key];
89
90 }, this);
91
92 return target;
93
94 },
95
96 compose: function() {
97 var toCall = ARR_SLICE.call(arguments);
98 return function() {
99 var args = ARR_SLICE.call(arguments);
100 for (var i = toCall.length -1; i >= 0; i--) {
101 args = [toCall[i].apply(this, args)];
102 }
103 return args[0];
104 }
105 },
106
107 each: function(obj, itr, scope) {
108
109 if (!obj) return;
110
111 if (ARR_EACH && obj.forEach && obj.forEach === ARR_EACH) {
112
113 obj.forEach(itr, scope);
114
115 } else if (obj.length === obj.length + 0) { // Is number but not NaN
116
117 for (var key = 0, l = obj.length; key < l; key++)
118 if (key in obj && itr.call(scope, obj[key], key) === this.BREAK)
119 return;
120
121 } else {
122
123 for (var key in obj)
124 if (itr.call(scope, obj[key], key) === this.BREAK)
125 return;
126
127 }
128
129 },
130
131 defer: function(fnc) {
132 setTimeout(fnc, 0);
133 },
134
135 toArray: function(obj) {
136 if (obj.toArray) return obj.toArray();
137 return ARR_SLICE.call(obj);
138 },
139
140 isUndefined: function(obj) {
141 return obj === undefined;
142 },
143
144 isNull: function(obj) {
145 return obj === null;
146 },
147
148 isNaN: function(obj) {
149 return obj !== obj;
150 },
151
152 isArray: Array.isArray || function(obj) {
153 return obj.constructor === Array;
154 },
155
156 isObject: function(obj) {
157 return obj === Object(obj);
158 },
159
160 isNumber: function(obj) {
161 return obj === obj+0;
162 },
163
164 isString: function(obj) {
165 return obj === obj+'';
166 },
167
168 isBoolean: function(obj) {
169 return obj === false || obj === true;
170 },
171
172 isFunction: function(obj) {
173 return Object.prototype.toString.call(obj) === '[object Function]';
174 }
175
176 };
177
178})();
179
180
181dat.controllers.Controller = (function (common) {
182
183 /**
184 * @class An "abstract" class that represents a given property of an object.
185 *
186 * @param {Object} object The object to be manipulated
187 * @param {string} property The name of the property to be manipulated
188 *
189 * @member dat.controllers
190 */
191 var Controller = function(object, property) {
192
193 this.initialValue = object[property];
194
195 /**
196 * Those who extend this class will put their DOM elements in here.
197 * @type {DOMElement}
198 */
199 this.domElement = document.createElement('div');
200
201 /**
202 * The object to manipulate
203 * @type {Object}
204 */
205 this.object = object;
206
207 /**
208 * The name of the property to manipulate
209 * @type {String}
210 */
211 this.property = property;
212
213 /**
214 * The function to be called on change.
215 * @type {Function}
216 * @ignore
217 */
218 this.__onChange = undefined;
219
220 /**
221 * The function to be called on finishing change.
222 * @type {Function}
223 * @ignore
224 */
225 this.__onFinishChange = undefined;
226
227 };
228
229 common.extend(
230
231 Controller.prototype,
232
233 /** @lends dat.controllers.Controller.prototype */
234 {
235
236 /**
237 * Specify that a function fire every time someone changes the value with
238 * this Controller.
239 *
240 * @param {Function} fnc This function will be called whenever the value
241 * is modified via this Controller.
242 * @returns {dat.controllers.Controller} this
243 */
244 onChange: function(fnc) {
245 this.__onChange = fnc;
246 return this;
247 },
248
249 /**
250 * Specify that a function fire every time someone "finishes" changing
251 * the value wih this Controller. Useful for values that change
252 * incrementally like numbers or strings.
253 *
254 * @param {Function} fnc This function will be called whenever
255 * someone "finishes" changing the value via this Controller.
256 * @returns {dat.controllers.Controller} this
257 */
258 onFinishChange: function(fnc) {
259 this.__onFinishChange = fnc;
260 return this;
261 },
262
263 /**
264 * Change the value of <code>object[property]</code>
265 *
266 * @param {Object} newValue The new value of <code>object[property]</code>
267 */
268 setValue: function(newValue) {
269 this.object[this.property] = newValue;
270 if (this.__onChange) {
271 this.__onChange.call(this, newValue);
272 }
273 this.updateDisplay();
274 return this;
275 },
276
277 /**
278 * Gets the value of <code>object[property]</code>
279 *
280 * @returns {Object} The current value of <code>object[property]</code>
281 */
282 getValue: function() {
283 return this.object[this.property];
284 },
285
286 /**
287 * Refreshes the visual display of a Controller in order to keep sync
288 * with the object's current value.
289 * @returns {dat.controllers.Controller} this
290 */
291 updateDisplay: function() {
292 return this;
293 },
294
295 /**
296 * @returns {Boolean} true if the value has deviated from initialValue
297 */
298 isModified: function() {
299 return this.initialValue !== this.getValue()
300 }
301
302 }
303
304 );
305
306 return Controller;
307
308
309})(dat.utils.common);
310
311
312dat.dom.dom = (function (common) {
313
314 var EVENT_MAP = {
315 'HTMLEvents': ['change'],
316 'MouseEvents': ['click','mousemove','mousedown','mouseup', 'mouseover'],
317 'KeyboardEvents': ['keydown']
318 };
319
320 var EVENT_MAP_INV = {};
321 common.each(EVENT_MAP, function(v, k) {
322 common.each(v, function(e) {
323 EVENT_MAP_INV[e] = k;
324 });
325 });
326
327 var CSS_VALUE_PIXELS = /(\d+(\.\d+)?)px/;
328
329 function cssValueToPixels(val) {
330
331 if (val === '0' || common.isUndefined(val)) return 0;
332
333 var match = val.match(CSS_VALUE_PIXELS);
334
335 if (!common.isNull(match)) {
336 return parseFloat(match[1]);
337 }
338
339 // TODO ...ems? %?
340
341 return 0;
342
343 }
344
345 /**
346 * @namespace
347 * @member dat.dom
348 */
349 var dom = {
350
351 /**
352 *
353 * @param elem
354 * @param selectable
355 */
356 makeSelectable: function(elem, selectable) {
357
358 if (elem === undefined || elem.style === undefined) return;
359
360 elem.onselectstart = selectable ? function() {
361 return false;
362 } : function() {
363 };
364
365 elem.style.MozUserSelect = selectable ? 'auto' : 'none';
366 elem.style.KhtmlUserSelect = selectable ? 'auto' : 'none';
367 elem.unselectable = selectable ? 'on' : 'off';
368
369 },
370
371 /**
372 *
373 * @param elem
374 * @param horizontal
375 * @param vertical
376 */
377 makeFullscreen: function(elem, horizontal, vertical) {
378
379 if (common.isUndefined(horizontal)) horizontal = true;
380 if (common.isUndefined(vertical)) vertical = true;
381
382 elem.style.position = 'absolute';
383
384 if (horizontal) {
385 elem.style.left = 0;
386 elem.style.right = 0;
387 }
388 if (vertical) {
389 elem.style.top = 0;
390 elem.style.bottom = 0;
391 }
392
393 },
394
395 /**
396 *
397 * @param elem
398 * @param eventType
399 * @param params
400 */
401 fakeEvent: function(elem, eventType, params, aux) {
402 params = params || {};
403 var className = EVENT_MAP_INV[eventType];
404 if (!className) {
405 throw new Error('Event type ' + eventType + ' not supported.');
406 }
407 var evt = document.createEvent(className);
408 switch (className) {
409 case 'MouseEvents':
410 var clientX = params.x || params.clientX || 0;
411 var clientY = params.y || params.clientY || 0;
412 evt.initMouseEvent(eventType, params.bubbles || false,
413 params.cancelable || true, window, params.clickCount || 1,
414 0, //screen X
415 0, //screen Y
416 clientX, //client X
417 clientY, //client Y
418 false, false, false, false, 0, null);
419 break;
420 case 'KeyboardEvents':
421 var init = evt.initKeyboardEvent || evt.initKeyEvent; // webkit || moz
422 common.defaults(params, {
423 cancelable: true,
424 ctrlKey: false,
425 altKey: false,
426 shiftKey: false,
427 metaKey: false,
428 keyCode: undefined,
429 charCode: undefined
430 });
431 init(eventType, params.bubbles || false,
432 params.cancelable, window,
433 params.ctrlKey, params.altKey,
434 params.shiftKey, params.metaKey,
435 params.keyCode, params.charCode);
436 break;
437 default:
438 evt.initEvent(eventType, params.bubbles || false,
439 params.cancelable || true);
440 break;
441 }
442 common.defaults(evt, aux);
443 elem.dispatchEvent(evt);
444 },
445
446 /**
447 *
448 * @param elem
449 * @param event
450 * @param func
451 * @param bool
452 */
453 bind: function(elem, event, func, bool) {
454 bool = bool || false;
455 if (elem.addEventListener)
456 elem.addEventListener(event, func, bool);
457 else if (elem.attachEvent)
458 elem.attachEvent('on' + event, func);
459 return dom;
460 },
461
462 /**
463 *
464 * @param elem
465 * @param event
466 * @param func
467 * @param bool
468 */
469 unbind: function(elem, event, func, bool) {
470 bool = bool || false;
471 if (elem.removeEventListener)
472 elem.removeEventListener(event, func, bool);
473 else if (elem.detachEvent)
474 elem.detachEvent('on' + event, func);
475 return dom;
476 },
477
478 /**
479 *
480 * @param elem
481 * @param className
482 */
483 addClass: function(elem, className) {
484 if (elem.className === undefined) {
485 elem.className = className;
486 } else if (elem.className !== className) {
487 var classes = elem.className.split(/ +/);
488 if (classes.indexOf(className) == -1) {
489 classes.push(className);
490 elem.className = classes.join(' ').replace(/^\s+/, '').replace(/\s+$/, '');
491 }
492 }
493 return dom;
494 },
495
496 /**
497 *
498 * @param elem
499 * @param className
500 */
501 removeClass: function(elem, className) {
502 if (className) {
503 if (elem.className === undefined) {
504 // elem.className = className;
505 } else if (elem.className === className) {
506 elem.removeAttribute('class');
507 } else {
508 var classes = elem.className.split(/ +/);
509 var index = classes.indexOf(className);
510 if (index != -1) {
511 classes.splice(index, 1);
512 elem.className = classes.join(' ');
513 }
514 }
515 } else {
516 elem.className = undefined;
517 }
518 return dom;
519 },
520
521 hasClass: function(elem, className) {
522 return new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)').test(elem.className) || false;
523 },
524
525 /**
526 *
527 * @param elem
528 */
529 getWidth: function(elem) {
530
531 var style = getComputedStyle(elem);
532
533 return cssValueToPixels(style['border-left-width']) +
534 cssValueToPixels(style['border-right-width']) +
535 cssValueToPixels(style['padding-left']) +
536 cssValueToPixels(style['padding-right']) +
537 cssValueToPixels(style['width']);
538 },
539
540 /**
541 *
542 * @param elem
543 */
544 getHeight: function(elem) {
545
546 var style = getComputedStyle(elem);
547
548 return cssValueToPixels(style['border-top-width']) +
549 cssValueToPixels(style['border-bottom-width']) +
550 cssValueToPixels(style['padding-top']) +
551 cssValueToPixels(style['padding-bottom']) +
552 cssValueToPixels(style['height']);
553 },
554
555 /**
556 *
557 * @param elem
558 */
559 getOffset: function(elem) {
560 var offset = {left: 0, top:0};
561 if (elem.offsetParent) {
562 do {
563 offset.left += elem.offsetLeft;
564 offset.top += elem.offsetTop;
565 } while (elem = elem.offsetParent);
566 }
567 return offset;
568 },
569
570 // http://stackoverflow.com/posts/2684561/revisions
571 /**
572 *
573 * @param elem
574 */
575 isActive: function(elem) {
576 return elem === document.activeElement && ( elem.type || elem.href );
577 }
578
579 };
580
581 return dom;
582
583})(dat.utils.common);
584
585
586dat.controllers.OptionController = (function (Controller, dom, common) {
587
588 /**
589 * @class Provides a select input to alter the property of an object, using a
590 * list of accepted values.
591 *
592 * @extends dat.controllers.Controller
593 *
594 * @param {Object} object The object to be manipulated
595 * @param {string} property The name of the property to be manipulated
596 * @param {Object|string[]} options A map of labels to acceptable values, or
597 * a list of acceptable string values.
598 *
599 * @member dat.controllers
600 */
601 var OptionController = function(object, property, options) {
602
603 OptionController.superclass.call(this, object, property);
604
605 var _this = this;
606
607 /**
608 * The drop down menu
609 * @ignore
610 */
611 this.__select = document.createElement('select');
612
613 if (common.isArray(options)) {
614 var map = {};
615 common.each(options, function(element) {
616 map[element] = element;
617 });
618 options = map;
619 }
620
621 common.each(options, function(value, key) {
622
623 var opt = document.createElement('option');
624 opt.innerHTML = key;
625 opt.setAttribute('value', value);
626 _this.__select.appendChild(opt);
627
628 });
629
630 // Acknowledge original value
631 this.updateDisplay();
632
633 dom.bind(this.__select, 'change', function() {
634 var desiredValue = this.options[this.selectedIndex].value;
635 _this.setValue(desiredValue);
636 });
637
638 this.domElement.appendChild(this.__select);
639
640 };
641
642 OptionController.superclass = Controller;
643
644 common.extend(
645
646 OptionController.prototype,
647 Controller.prototype,
648
649 {
650
651 setValue: function(v) {
652 var toReturn = OptionController.superclass.prototype.setValue.call(this, v);
653 if (this.__onFinishChange) {
654 this.__onFinishChange.call(this, this.getValue());
655 }
656 return toReturn;
657 },
658
659 updateDisplay: function() {
660 this.__select.value = this.getValue();
661 return OptionController.superclass.prototype.updateDisplay.call(this);
662 }
663
664 }
665
666 );
667
668 return OptionController;
669
670})(dat.controllers.Controller,
671dat.dom.dom,
672dat.utils.common);
673
674
675dat.controllers.NumberController = (function (Controller, common) {
676
677 /**
678 * @class Represents a given property of an object that is a number.
679 *
680 * @extends dat.controllers.Controller
681 *
682 * @param {Object} object The object to be manipulated
683 * @param {string} property The name of the property to be manipulated
684 * @param {Object} [params] Optional parameters
685 * @param {Number} [params.min] Minimum allowed value
686 * @param {Number} [params.max] Maximum allowed value
687 * @param {Number} [params.step] Increment by which to change value
688 *
689 * @member dat.controllers
690 */
691 var NumberController = function(object, property, params) {
692
693 NumberController.superclass.call(this, object, property);
694
695 params = params || {};
696
697 this.__min = params.min;
698 this.__max = params.max;
699 this.__step = params.step;
700
701 if (common.isUndefined(this.__step)) {
702
703 if (this.initialValue == 0) {
704 this.__impliedStep = 1; // What are we, psychics?
705 } else {
706 // Hey Doug, check this out.
707 this.__impliedStep = Math.pow(10, Math.floor(Math.log(this.initialValue)/Math.LN10))/10;
708 }
709
710 } else {
711
712 this.__impliedStep = this.__step;
713
714 }
715
716 this.__precision = numDecimals(this.__impliedStep);
717
718
719 };
720
721 NumberController.superclass = Controller;
722
723 common.extend(
724
725 NumberController.prototype,
726 Controller.prototype,
727
728 /** @lends dat.controllers.NumberController.prototype */
729 {
730
731 setValue: function(v) {
732
733 if (this.__min !== undefined && v < this.__min) {
734 v = this.__min;
735 } else if (this.__max !== undefined && v > this.__max) {
736 v = this.__max;
737 }
738
739 if (this.__step !== undefined && v % this.__step != 0) {
740 v = Math.round(v / this.__step) * this.__step;
741 }
742
743 return NumberController.superclass.prototype.setValue.call(this, v);
744
745 },
746
747 /**
748 * Specify a minimum value for <code>object[property]</code>.
749 *
750 * @param {Number} minValue The minimum value for
751 * <code>object[property]</code>
752 * @returns {dat.controllers.NumberController} this
753 */
754 min: function(v) {
755 this.__min = v;
756 return this;
757 },
758
759 /**
760 * Specify a maximum value for <code>object[property]</code>.
761 *
762 * @param {Number} maxValue The maximum value for
763 * <code>object[property]</code>
764 * @returns {dat.controllers.NumberController} this
765 */
766 max: function(v) {
767 this.__max = v;
768 return this;
769 },
770
771 /**
772 * Specify a step value that dat.controllers.NumberController
773 * increments by.
774 *
775 * @param {Number} stepValue The step value for
776 * dat.controllers.NumberController
777 * @default if minimum and maximum specified increment is 1% of the
778 * difference otherwise stepValue is 1
779 * @returns {dat.controllers.NumberController} this
780 */
781 step: function(v) {
782 this.__step = v;
783 this.__impliedStep = v;
784 this.__precision = numDecimals(v);
785 return this;
786 }
787
788 }
789
790 );
791
792 function numDecimals(x) {
793 x = x.toString();
794 if (x.indexOf('.') > -1) {
795 return x.length - x.indexOf('.') - 1;
796 } else {
797 return 0;
798 }
799 }
800
801 return NumberController;
802
803})(dat.controllers.Controller,
804dat.utils.common);
805
806
807dat.controllers.NumberControllerBox = (function (NumberController, dom, common) {
808
809 /**
810 * @class Represents a given property of an object that is a number and
811 * provides an input element with which to manipulate it.
812 *
813 * @extends dat.controllers.Controller
814 * @extends dat.controllers.NumberController
815 *
816 * @param {Object} object The object to be manipulated
817 * @param {string} property The name of the property to be manipulated
818 * @param {Object} [params] Optional parameters
819 * @param {Number} [params.min] Minimum allowed value
820 * @param {Number} [params.max] Maximum allowed value
821 * @param {Number} [params.step] Increment by which to change value
822 *
823 * @member dat.controllers
824 */
825 var NumberControllerBox = function(object, property, params) {
826
827 this.__truncationSuspended = false;
828
829 NumberControllerBox.superclass.call(this, object, property, params);
830
831 var _this = this;
832
833 /**
834 * {Number} Previous mouse y position
835 * @ignore
836 */
837 var prev_y;
838
839 this.__input = document.createElement('input');
840 this.__input.setAttribute('type', 'text');
841
842 // Makes it so manually specified values are not truncated.
843
844 dom.bind(this.__input, 'change', onChange);
845 dom.bind(this.__input, 'blur', onBlur);
846 dom.bind(this.__input, 'mousedown', onMouseDown);
847 dom.bind(this.__input, 'keydown', function(e) {
848
849 // When pressing entire, you can be as precise as you want.
850 if (e.keyCode === 13) {
851 _this.__truncationSuspended = true;
852 this.blur();
853 _this.__truncationSuspended = false;
854 }
855
856 });
857
858 function onChange() {
859 var attempted = parseFloat(_this.__input.value);
860 if (!common.isNaN(attempted)) _this.setValue(attempted);
861 }
862
863 function onBlur() {
864 onChange();
865 if (_this.__onFinishChange) {
866 _this.__onFinishChange.call(_this, _this.getValue());
867 }
868 }
869
870 function onMouseDown(e) {
871 dom.bind(window, 'mousemove', onMouseDrag);
872 dom.bind(window, 'mouseup', onMouseUp);
873 prev_y = e.clientY;
874 }
875
876 function onMouseDrag(e) {
877
878 var diff = prev_y - e.clientY;
879 _this.setValue(_this.getValue() + diff * _this.__impliedStep);
880
881 prev_y = e.clientY;
882
883 }
884
885 function onMouseUp() {
886 dom.unbind(window, 'mousemove', onMouseDrag);
887 dom.unbind(window, 'mouseup', onMouseUp);
888 }
889
890 this.updateDisplay();
891
892 this.domElement.appendChild(this.__input);
893
894 };
895
896 NumberControllerBox.superclass = NumberController;
897
898 common.extend(
899
900 NumberControllerBox.prototype,
901 NumberController.prototype,
902
903 {
904
905 updateDisplay: function() {
906
907 this.__input.value = this.__truncationSuspended ? this.getValue() : roundToDecimal(this.getValue(), this.__precision);
908 return NumberControllerBox.superclass.prototype.updateDisplay.call(this);
909 }
910
911 }
912
913 );
914
915 function roundToDecimal(value, decimals) {
916 var tenTo = Math.pow(10, decimals);
917 return Math.round(value * tenTo) / tenTo;
918 }
919
920 return NumberControllerBox;
921
922})(dat.controllers.NumberController,
923dat.dom.dom,
924dat.utils.common);
925
926
927dat.controllers.NumberControllerSlider = (function (NumberController, dom, css, common, styleSheet) {
928
929 /**
930 * @class Represents a given property of an object that is a number, contains
931 * a minimum and maximum, and provides a slider element with which to
932 * manipulate it. It should be noted that the slider element is made up of
933 * <code>&lt;div&gt;</code> tags, <strong>not</strong> the html5
934 * <code>&lt;slider&gt;</code> element.
935 *
936 * @extends dat.controllers.Controller
937 * @extends dat.controllers.NumberController
938 *
939 * @param {Object} object The object to be manipulated
940 * @param {string} property The name of the property to be manipulated
941 * @param {Number} minValue Minimum allowed value
942 * @param {Number} maxValue Maximum allowed value
943 * @param {Number} stepValue Increment by which to change value
944 *
945 * @member dat.controllers
946 */
947 var NumberControllerSlider = function(object, property, min, max, step) {
948
949 NumberControllerSlider.superclass.call(this, object, property, { min: min, max: max, step: step });
950
951 var _this = this;
952
953 this.__background = document.createElement('div');
954 this.__foreground = document.createElement('div');
955
956
957
958 dom.bind(this.__background, 'mousedown', onMouseDown);
959
960 dom.addClass(this.__background, 'slider');
961 dom.addClass(this.__foreground, 'slider-fg');
962
963 function onMouseDown(e) {
964
965 dom.bind(window, 'mousemove', onMouseDrag);
966 dom.bind(window, 'mouseup', onMouseUp);
967
968 onMouseDrag(e);
969 }
970
971 function onMouseDrag(e) {
972
973 e.preventDefault();
974
975 var offset = dom.getOffset(_this.__background);
976 var width = dom.getWidth(_this.__background);
977
978 _this.setValue(
979 map(e.clientX, offset.left, offset.left + width, _this.__min, _this.__max)
980 );
981
982 return false;
983
984 }
985
986 function onMouseUp() {
987 dom.unbind(window, 'mousemove', onMouseDrag);
988 dom.unbind(window, 'mouseup', onMouseUp);
989 if (_this.__onFinishChange) {
990 _this.__onFinishChange.call(_this, _this.getValue());
991 }
992 }
993
994 this.updateDisplay();
995
996 this.__background.appendChild(this.__foreground);
997 this.domElement.appendChild(this.__background);
998
999 };
1000
1001 NumberControllerSlider.superclass = NumberController;
1002
1003 /**
1004 * Injects default stylesheet for slider elements.
1005 */
1006 NumberControllerSlider.useDefaultStyles = function() {
1007 css.inject(styleSheet);
1008 };
1009
1010 common.extend(
1011
1012 NumberControllerSlider.prototype,
1013 NumberController.prototype,
1014
1015 {
1016
1017 updateDisplay: function() {
1018 var pct = (this.getValue() - this.__min)/(this.__max - this.__min);
1019 this.__foreground.style.width = pct*100+'%';
1020 return NumberControllerSlider.superclass.prototype.updateDisplay.call(this);
1021 }
1022
1023 }
1024
1025
1026
1027 );
1028
1029 function map(v, i1, i2, o1, o2) {
1030 return o1 + (o2 - o1) * ((v - i1) / (i2 - i1));
1031 }
1032
1033 return NumberControllerSlider;
1034
1035})(dat.controllers.NumberController,
1036dat.dom.dom,
1037dat.utils.css,
1038dat.utils.common,
1039"/**\n * dat-gui JavaScript Controller Library\n * http://code.google.com/p/dat-gui\n *\n * Copyright 2011 Data Arts Team, Google Creative Lab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n */\n\n.slider {\n box-shadow: inset 0 2px 4px rgba(0,0,0,0.15);\n height: 1em;\n border-radius: 1em;\n background-color: #eee;\n padding: 0 0.5em;\n overflow: hidden;\n}\n\n.slider-fg {\n padding: 1px 0 2px 0;\n background-color: #aaa;\n height: 1em;\n margin-left: -0.5em;\n padding-right: 0.5em;\n border-radius: 1em 0 0 1em;\n}\n\n.slider-fg:after {\n display: inline-block;\n border-radius: 1em;\n background-color: #fff;\n border: 1px solid #aaa;\n content: '';\n float: right;\n margin-right: -1em;\n margin-top: -1px;\n height: 0.9em;\n width: 0.9em;\n}");
1040
1041
1042dat.controllers.FunctionController = (function (Controller, dom, common) {
1043
1044 /**
1045 * @class Provides a GUI interface to fire a specified method, a property of an object.
1046 *
1047 * @extends dat.controllers.Controller
1048 *
1049 * @param {Object} object The object to be manipulated
1050 * @param {string} property The name of the property to be manipulated
1051 *
1052 * @member dat.controllers
1053 */
1054 var FunctionController = function(object, property, text) {
1055
1056 FunctionController.superclass.call(this, object, property);
1057
1058 var _this = this;
1059
1060 this.__button = document.createElement('div');
1061 this.__button.innerHTML = text === undefined ? 'Fire' : text;
1062 dom.bind(this.__button, 'click', function(e) {
1063 e.preventDefault();
1064 _this.fire();
1065 return false;
1066 });
1067
1068 dom.addClass(this.__button, 'button');
1069
1070 this.domElement.appendChild(this.__button);
1071
1072
1073 };
1074
1075 FunctionController.superclass = Controller;
1076
1077 common.extend(
1078
1079 FunctionController.prototype,
1080 Controller.prototype,
1081 {
1082
1083 fire: function() {
1084 if (this.__onChange) {
1085 this.__onChange.call(this);
1086 }
1087 if (this.__onFinishChange) {
1088 this.__onFinishChange.call(this, this.getValue());
1089 }
1090 this.getValue().call(this.object);
1091 }
1092 }
1093
1094 );
1095
1096 return FunctionController;
1097
1098})(dat.controllers.Controller,
1099dat.dom.dom,
1100dat.utils.common);
1101
1102
1103dat.controllers.BooleanController = (function (Controller, dom, common) {
1104
1105 /**
1106 * @class Provides a checkbox input to alter the boolean property of an object.
1107 * @extends dat.controllers.Controller
1108 *
1109 * @param {Object} object The object to be manipulated
1110 * @param {string} property The name of the property to be manipulated
1111 *
1112 * @member dat.controllers
1113 */
1114 var BooleanController = function(object, property) {
1115
1116 BooleanController.superclass.call(this, object, property);
1117
1118 var _this = this;
1119 this.__prev = this.getValue();
1120
1121 this.__checkbox = document.createElement('input');
1122 this.__checkbox.setAttribute('type', 'checkbox');
1123
1124
1125 dom.bind(this.__checkbox, 'change', onChange, false);
1126
1127 this.domElement.appendChild(this.__checkbox);
1128
1129 // Match original value
1130 this.updateDisplay();
1131
1132 function onChange() {
1133 _this.setValue(!_this.__prev);
1134 }
1135
1136 };
1137
1138 BooleanController.superclass = Controller;
1139
1140 common.extend(
1141
1142 BooleanController.prototype,
1143 Controller.prototype,
1144
1145 {
1146
1147 setValue: function(v) {
1148 var toReturn = BooleanController.superclass.prototype.setValue.call(this, v);
1149 if (this.__onFinishChange) {
1150 this.__onFinishChange.call(this, this.getValue());
1151 }
1152 this.__prev = this.getValue();
1153 return toReturn;
1154 },
1155
1156 updateDisplay: function() {
1157
1158 if (this.getValue() === true) {
1159 this.__checkbox.setAttribute('checked', 'checked');
1160 this.__checkbox.checked = true;
1161 } else {
1162 this.__checkbox.checked = false;
1163 }
1164
1165 return BooleanController.superclass.prototype.updateDisplay.call(this);
1166
1167 }
1168
1169
1170 }
1171
1172 );
1173
1174 return BooleanController;
1175
1176})(dat.controllers.Controller,
1177dat.dom.dom,
1178dat.utils.common);
1179
1180
1181dat.color.toString = (function (common) {
1182
1183 return function(color) {
1184
1185 if (color.a == 1 || common.isUndefined(color.a)) {
1186
1187 var s = color.hex.toString(16);
1188 while (s.length < 6) {
1189 s = '0' + s;
1190 }
1191
1192 return '#' + s;
1193
1194 } else {
1195
1196 return 'rgba(' + Math.round(color.r) + ',' + Math.round(color.g) + ',' + Math.round(color.b) + ',' + color.a + ')';
1197
1198 }
1199
1200 }
1201
1202})(dat.utils.common);
1203
1204
1205dat.color.interpret = (function (toString, common) {
1206
1207 var result, toReturn;
1208
1209 var interpret = function() {
1210
1211 toReturn = false;
1212
1213 var original = arguments.length > 1 ? common.toArray(arguments) : arguments[0];
1214
1215 common.each(INTERPRETATIONS, function(family) {
1216
1217 if (family.litmus(original)) {
1218
1219 common.each(family.conversions, function(conversion, conversionName) {
1220
1221 result = conversion.read(original);
1222
1223 if (toReturn === false && result !== false) {
1224 toReturn = result;
1225 result.conversionName = conversionName;
1226 result.conversion = conversion;
1227 return common.BREAK;
1228
1229 }
1230
1231 });
1232
1233 return common.BREAK;
1234
1235 }
1236
1237 });
1238
1239 return toReturn;
1240
1241 };
1242
1243 var INTERPRETATIONS = [
1244
1245 // Strings
1246 {
1247
1248 litmus: common.isString,
1249
1250 conversions: {
1251
1252 THREE_CHAR_HEX: {
1253
1254 read: function(original) {
1255
1256 var test = original.match(/^#([A-F0-9])([A-F0-9])([A-F0-9])$/i);
1257 if (test === null) return false;
1258
1259 return {
1260 space: 'HEX',
1261 hex: parseInt(
1262 '0x' +
1263 test[1].toString() + test[1].toString() +
1264 test[2].toString() + test[2].toString() +
1265 test[3].toString() + test[3].toString())
1266 };
1267
1268 },
1269
1270 write: toString
1271
1272 },
1273
1274 SIX_CHAR_HEX: {
1275
1276 read: function(original) {
1277
1278 var test = original.match(/^#([A-F0-9]{6})$/i);
1279 if (test === null) return false;
1280
1281 return {
1282 space: 'HEX',
1283 hex: parseInt('0x' + test[1].toString())
1284 };
1285
1286 },
1287
1288 write: toString
1289
1290 },
1291
1292 CSS_RGB: {
1293
1294 read: function(original) {
1295
1296 var test = original.match(/^rgb\(\s*(.+)\s*,\s*(.+)\s*,\s*(.+)\s*\)/);
1297 if (test === null) return false;
1298
1299 return {
1300 space: 'RGB',
1301 r: parseFloat(test[1]),
1302 g: parseFloat(test[2]),
1303 b: parseFloat(test[3])
1304 };
1305
1306 },
1307
1308 write: toString
1309
1310 },
1311
1312 CSS_RGBA: {
1313
1314 read: function(original) {
1315
1316 var test = original.match(/^rgba\(\s*(.+)\s*,\s*(.+)\s*,\s*(.+)\s*\,\s*(.+)\s*\)/);
1317 if (test === null) return false;
1318
1319 return {
1320 space: 'RGB',
1321 r: parseFloat(test[1]),
1322 g: parseFloat(test[2]),
1323 b: parseFloat(test[3]),
1324 a: parseFloat(test[4])
1325 };
1326
1327 },
1328
1329 write: toString
1330
1331 }
1332
1333 }
1334
1335 },
1336
1337 // Numbers
1338 {
1339
1340 litmus: common.isNumber,
1341
1342 conversions: {
1343
1344 HEX: {
1345 read: function(original) {
1346 return {
1347 space: 'HEX',
1348 hex: original,
1349 conversionName: 'HEX'
1350 }
1351 },
1352
1353 write: function(color) {
1354 return color.hex;
1355 }
1356 }
1357
1358 }
1359
1360 },
1361
1362 // Arrays
1363 {
1364
1365 litmus: common.isArray,
1366
1367 conversions: {
1368
1369 RGB_ARRAY: {
1370 read: function(original) {
1371 if (original.length != 3) return false;
1372 return {
1373 space: 'RGB',
1374 r: original[0],
1375 g: original[1],
1376 b: original[2]
1377 };
1378 },
1379
1380 write: function(color) {
1381 return [color.r, color.g, color.b];
1382 }
1383
1384 },
1385
1386 RGBA_ARRAY: {
1387 read: function(original) {
1388 if (original.length != 4) return false;
1389 return {
1390 space: 'RGB',
1391 r: original[0],
1392 g: original[1],
1393 b: original[2],
1394 a: original[3]
1395 };
1396 },
1397
1398 write: function(color) {
1399 return [color.r, color.g, color.b, color.a];
1400 }
1401
1402 }
1403
1404 }
1405
1406 },
1407
1408 // Objects
1409 {
1410
1411 litmus: common.isObject,
1412
1413 conversions: {
1414
1415 RGBA_OBJ: {
1416 read: function(original) {
1417 if (common.isNumber(original.r) &&
1418 common.isNumber(original.g) &&
1419 common.isNumber(original.b) &&
1420 common.isNumber(original.a)) {
1421 return {
1422 space: 'RGB',
1423 r: original.r,
1424 g: original.g,
1425 b: original.b,
1426 a: original.a
1427 }
1428 }
1429 return false;
1430 },
1431
1432 write: function(color) {
1433 return {
1434 r: color.r,
1435 g: color.g,
1436 b: color.b,
1437 a: color.a
1438 }
1439 }
1440 },
1441
1442 RGB_OBJ: {
1443 read: function(original) {
1444 if (common.isNumber(original.r) &&
1445 common.isNumber(original.g) &&
1446 common.isNumber(original.b)) {
1447 return {
1448 space: 'RGB',
1449 r: original.r,
1450 g: original.g,
1451 b: original.b
1452 }
1453 }
1454 return false;
1455 },
1456
1457 write: function(color) {
1458 return {
1459 r: color.r,
1460 g: color.g,
1461 b: color.b
1462 }
1463 }
1464 },
1465
1466 HSVA_OBJ: {
1467 read: function(original) {
1468 if (common.isNumber(original.h) &&
1469 common.isNumber(original.s) &&
1470 common.isNumber(original.v) &&
1471 common.isNumber(original.a)) {
1472 return {
1473 space: 'HSV',
1474 h: original.h,
1475 s: original.s,
1476 v: original.v,
1477 a: original.a
1478 }
1479 }
1480 return false;
1481 },
1482
1483 write: function(color) {
1484 return {
1485 h: color.h,
1486 s: color.s,
1487 v: color.v,
1488 a: color.a
1489 }
1490 }
1491 },
1492
1493 HSV_OBJ: {
1494 read: function(original) {
1495 if (common.isNumber(original.h) &&
1496 common.isNumber(original.s) &&
1497 common.isNumber(original.v)) {
1498 return {
1499 space: 'HSV',
1500 h: original.h,
1501 s: original.s,
1502 v: original.v
1503 }
1504 }
1505 return false;
1506 },
1507
1508 write: function(color) {
1509 return {
1510 h: color.h,
1511 s: color.s,
1512 v: color.v
1513 }
1514 }
1515
1516 }
1517
1518 }
1519
1520 }
1521
1522
1523 ];
1524
1525 return interpret;
1526
1527
1528})(dat.color.toString,
1529dat.utils.common);
1530
1531
1532dat.GUI = dat.gui.GUI = (function (css, saveDialogueContents, styleSheet, controllerFactory, Controller, BooleanController, FunctionController, NumberControllerBox, NumberControllerSlider, OptionController, ColorController, requestAnimationFrame, CenteredDiv, dom, common) {
1533
1534 css.inject(styleSheet);
1535
1536 /** Outer-most className for GUI's */
1537 var CSS_NAMESPACE = 'dg';
1538
1539 var HIDE_KEY_CODE = 72;
1540
1541 /** The only value shared between the JS and SCSS. Use caution. */
1542 var CLOSE_BUTTON_HEIGHT = 20;
1543
1544 var DEFAULT_DEFAULT_PRESET_NAME = 'Default';
1545
1546 var SUPPORTS_LOCAL_STORAGE = (function() {
1547 try {
1548 return 'localStorage' in window && window['localStorage'] !== null;
1549 } catch (e) {
1550 return false;
1551 }
1552 })();
1553
1554 var SAVE_DIALOGUE;
1555
1556 /** Have we yet to create an autoPlace GUI? */
1557 var auto_place_virgin = true;
1558
1559 /** Fixed position div that auto place GUI's go inside */
1560 var auto_place_container;
1561
1562 /** Are we hiding the GUI's ? */
1563 var hide = false;
1564
1565 /** GUI's which should be hidden */
1566 var hideable_guis = [];
1567
1568 /**
1569 * A lightweight controller library for JavaScript. It allows you to easily
1570 * manipulate variables and fire functions on the fly.
1571 * @class
1572 *
1573 * @member dat.gui
1574 *
1575 * @param {Object} [params]
1576 * @param {String} [params.name] The name of this GUI.
1577 * @param {Object} [params.load] JSON object representing the saved state of
1578 * this GUI.
1579 * @param {Boolean} [params.auto=true]
1580 * @param {dat.gui.GUI} [params.parent] The GUI I'm nested in.
1581 * @param {Boolean} [params.closed] If true, starts closed
1582 */
1583 var GUI = function(params) {
1584
1585 var _this = this;
1586
1587 /**
1588 * Outermost DOM Element
1589 * @type DOMElement
1590 */
1591 this.domElement = document.createElement('div');
1592 this.__ul = document.createElement('ul');
1593 this.domElement.appendChild(this.__ul);
1594
1595 dom.addClass(this.domElement, CSS_NAMESPACE);
1596
1597 /**
1598 * Nested GUI's by name
1599 * @ignore
1600 */
1601 this.__folders = {};
1602
1603 this.__controllers = [];
1604
1605 /**
1606 * List of objects I'm remembering for save, only used in top level GUI
1607 * @ignore
1608 */
1609 this.__rememberedObjects = [];
1610
1611 /**
1612 * Maps the index of remembered objects to a map of controllers, only used
1613 * in top level GUI.
1614 *
1615 * @private
1616 * @ignore
1617 *
1618 * @example
1619 * [
1620 * {
1621 * propertyName: Controller,
1622 * anotherPropertyName: Controller
1623 * },
1624 * {
1625 * propertyName: Controller
1626 * }
1627 * ]
1628 */
1629 this.__rememberedObjectIndecesToControllers = [];
1630
1631 this.__listening = [];
1632
1633 params = params || {};
1634
1635 // Default parameters
1636 params = common.defaults(params, {
1637 autoPlace: true,
1638 width: GUI.DEFAULT_WIDTH
1639 });
1640
1641 params = common.defaults(params, {
1642 resizable: params.autoPlace,
1643 hideable: params.autoPlace
1644 });
1645
1646
1647 if (!common.isUndefined(params.load)) {
1648
1649 // Explicit preset
1650 if (params.preset) params.load.preset = params.preset;
1651
1652 } else {
1653
1654 params.load = { preset: DEFAULT_DEFAULT_PRESET_NAME };
1655
1656 }
1657
1658 if (common.isUndefined(params.parent) && params.hideable) {
1659 hideable_guis.push(this);
1660 }
1661
1662 // Only root level GUI's are resizable.
1663 params.resizable = common.isUndefined(params.parent) && params.resizable;
1664
1665
1666 if (params.autoPlace && common.isUndefined(params.scrollable)) {
1667 params.scrollable = true;
1668 }
1669// params.scrollable = common.isUndefined(params.parent) && params.scrollable === true;
1670
1671 // Not part of params because I don't want people passing this in via
1672 // constructor. Should be a 'remembered' value.
1673 var use_local_storage =
1674 SUPPORTS_LOCAL_STORAGE &&
1675 localStorage.getItem(getLocalStorageHash(this, 'isLocal')) === 'true';
1676
1677 var saveToLocalStorage;
1678
1679 Object.defineProperties(this,
1680
1681 /** @lends dat.gui.GUI.prototype */
1682 {
1683
1684 /**
1685 * The parent <code>GUI</code>
1686 * @type dat.gui.GUI
1687 */
1688 parent: {
1689 get: function() {
1690 return params.parent;
1691 }
1692 },
1693
1694 scrollable: {
1695 get: function() {
1696 return params.scrollable;
1697 }
1698 },
1699
1700 /**
1701 * Handles <code>GUI</code>'s element placement for you
1702 * @type Boolean
1703 */
1704 autoPlace: {
1705 get: function() {
1706 return params.autoPlace;
1707 }
1708 },
1709
1710 /**
1711 * The identifier for a set of saved values
1712 * @type String
1713 */
1714 preset: {
1715
1716 get: function() {
1717 if (_this.parent) {
1718 return _this.getRoot().preset;
1719 } else {
1720 return params.load.preset;
1721 }
1722 },
1723
1724 set: function(v) {
1725 if (_this.parent) {
1726 _this.getRoot().preset = v;
1727 } else {
1728 params.load.preset = v;
1729 }
1730 setPresetSelectIndex(this);
1731 _this.revert();
1732 }
1733
1734 },
1735
1736 /**
1737 * The width of <code>GUI</code> element
1738 * @type Number
1739 */
1740 width: {
1741 get: function() {
1742 return params.width;
1743 },
1744 set: function(v) {
1745 params.width = v;
1746 setWidth(_this, v);
1747 }
1748 },
1749
1750 /**
1751 * The name of <code>GUI</code>. Used for folders. i.e
1752 * a folder's name
1753 * @type String
1754 */
1755 name: {
1756 get: function() {
1757 return params.name;
1758 },
1759 set: function(v) {
1760 // TODO Check for collisions among sibling folders
1761 params.name = v;
1762 if (title_row_name) {
1763 title_row_name.innerHTML = params.name;
1764 }
1765 }
1766 },
1767
1768 /**
1769 * Whether the <code>GUI</code> is collapsed or not
1770 * @type Boolean
1771 */
1772 closed: {
1773 get: function() {
1774 return params.closed;
1775 },
1776 set: function(v) {
1777 params.closed = v;
1778 if (params.closed) {
1779 dom.addClass(_this.__ul, GUI.CLASS_CLOSED);
1780 } else {
1781 dom.removeClass(_this.__ul, GUI.CLASS_CLOSED);
1782 }
1783 // For browsers that aren't going to respect the CSS transition,
1784 // Lets just check our height against the window height right off
1785 // the bat.
1786 this.onResize();
1787
1788 if (_this.__closeButton) {
1789 _this.__closeButton.innerHTML = v ? GUI.TEXT_OPEN : GUI.TEXT_CLOSED;
1790 }
1791 }
1792 },
1793
1794 /**
1795 * Contains all presets
1796 * @type Object
1797 */
1798 load: {
1799 get: function() {
1800 return params.load;
1801 }
1802 },
1803
1804 /**
1805 * Determines whether or not to use <a href="https://developer.mozilla.org/en/DOM/Storage#localStorage">localStorage</a> as the means for
1806 * <code>remember</code>ing
1807 * @type Boolean
1808 */
1809 useLocalStorage: {
1810
1811 get: function() {
1812 return use_local_storage;
1813 },
1814 set: function(bool) {
1815 if (SUPPORTS_LOCAL_STORAGE) {
1816 use_local_storage = bool;
1817 if (bool) {
1818 dom.bind(window, 'unload', saveToLocalStorage);
1819 } else {
1820 dom.unbind(window, 'unload', saveToLocalStorage);
1821 }
1822 localStorage.setItem(getLocalStorageHash(_this, 'isLocal'), bool);
1823 }
1824 }
1825
1826 }
1827
1828 });
1829
1830 // Are we a root level GUI?
1831 if (common.isUndefined(params.parent)) {
1832
1833 params.closed = false;
1834
1835 dom.addClass(this.domElement, GUI.CLASS_MAIN);
1836 dom.makeSelectable(this.domElement, false);
1837
1838 // Are we supposed to be loading locally?
1839 if (SUPPORTS_LOCAL_STORAGE) {
1840
1841 if (use_local_storage) {
1842
1843 _this.useLocalStorage = true;
1844
1845 var saved_gui = localStorage.getItem(getLocalStorageHash(this, 'gui'));
1846
1847 if (saved_gui) {
1848 params.load = JSON.parse(saved_gui);
1849 }
1850
1851 }
1852
1853 }
1854
1855 this.__closeButton = document.createElement('div');
1856 this.__closeButton.innerHTML = GUI.TEXT_CLOSED;
1857 dom.addClass(this.__closeButton, GUI.CLASS_CLOSE_BUTTON);
1858 this.domElement.appendChild(this.__closeButton);
1859
1860 dom.bind(this.__closeButton, 'click', function() {
1861
1862 _this.closed = !_this.closed;
1863
1864
1865 });
1866
1867
1868 // Oh, you're a nested GUI!
1869 } else {
1870
1871 if (params.closed === undefined) {
1872 params.closed = true;
1873 }
1874
1875 var title_row_name = document.createTextNode(params.name);
1876 dom.addClass(title_row_name, 'controller-name');
1877
1878 var title_row = addRow(_this, title_row_name);
1879
1880 var on_click_title = function(e) {
1881 e.preventDefault();
1882 _this.closed = !_this.closed;
1883 return false;
1884 };
1885
1886 dom.addClass(this.__ul, GUI.CLASS_CLOSED);
1887
1888 dom.addClass(title_row, 'title');
1889 dom.bind(title_row, 'click', on_click_title);
1890
1891 if (!params.closed) {
1892 this.closed = false;
1893 }
1894
1895 }
1896
1897 if (params.autoPlace) {
1898
1899 if (common.isUndefined(params.parent)) {
1900
1901 if (auto_place_virgin) {
1902 auto_place_container = document.createElement('div');
1903 dom.addClass(auto_place_container, CSS_NAMESPACE);
1904 dom.addClass(auto_place_container, GUI.CLASS_AUTO_PLACE_CONTAINER);
1905 document.body.appendChild(auto_place_container);
1906 auto_place_virgin = false;
1907 }
1908
1909 // Put it in the dom for you.
1910 auto_place_container.appendChild(this.domElement);
1911
1912 // Apply the auto styles
1913 dom.addClass(this.domElement, GUI.CLASS_AUTO_PLACE);
1914
1915 }
1916
1917
1918 // Make it not elastic.
1919 if (!this.parent) setWidth(_this, params.width);
1920
1921 }
1922
1923 dom.bind(window, 'resize', function() { _this.onResize() });
1924 dom.bind(this.__ul, 'webkitTransitionEnd', function() { _this.onResize(); });
1925 dom.bind(this.__ul, 'transitionend', function() { _this.onResize() });
1926 dom.bind(this.__ul, 'oTransitionEnd', function() { _this.onResize() });
1927 this.onResize();
1928
1929
1930 if (params.resizable) {
1931 addResizeHandle(this);
1932 }
1933
1934 saveToLocalStorage = function () {
1935 if (SUPPORTS_LOCAL_STORAGE && localStorage.getItem(getLocalStorageHash(_this, 'isLocal')) === 'true') {
1936 localStorage.setItem(getLocalStorageHash(_this, 'gui'), JSON.stringify(_this.getSaveObject()));
1937 }
1938 }
1939
1940 // expose this method publicly
1941 this.saveToLocalStorageIfPossible = saveToLocalStorage;
1942
1943 var root = _this.getRoot();
1944 function resetWidth() {
1945 var root = _this.getRoot();
1946 root.width += 1;
1947 common.defer(function() {
1948 root.width -= 1;
1949 });
1950 }
1951
1952 if (!params.parent) {
1953 resetWidth();
1954 }
1955
1956 };
1957
1958 GUI.toggleHide = function() {
1959
1960 hide = !hide;
1961 common.each(hideable_guis, function(gui) {
1962 gui.domElement.style.zIndex = hide ? -999 : 999;
1963 gui.domElement.style.opacity = hide ? 0 : 1;
1964 });
1965 };
1966
1967 GUI.CLASS_AUTO_PLACE = 'a';
1968 GUI.CLASS_AUTO_PLACE_CONTAINER = 'ac';
1969 GUI.CLASS_MAIN = 'main';
1970 GUI.CLASS_CONTROLLER_ROW = 'cr';
1971 GUI.CLASS_TOO_TALL = 'taller-than-window';
1972 GUI.CLASS_CLOSED = 'closed';
1973 GUI.CLASS_CLOSE_BUTTON = 'close-button';
1974 GUI.CLASS_DRAG = 'drag';
1975
1976 GUI.DEFAULT_WIDTH = 245;
1977 GUI.TEXT_CLOSED = 'Close Controls';
1978 GUI.TEXT_OPEN = 'Open Controls';
1979
1980 dom.bind(window, 'keydown', function(e) {
1981
1982 if (document.activeElement.type !== 'text' &&
1983 (e.which === HIDE_KEY_CODE || e.keyCode == HIDE_KEY_CODE)) {
1984 GUI.toggleHide();
1985 }
1986
1987 }, false);
1988
1989 common.extend(
1990
1991 GUI.prototype,
1992
1993 /** @lends dat.gui.GUI */
1994 {
1995
1996 /**
1997 * @param object
1998 * @param property
1999 * @returns {dat.controllers.Controller} The new controller that was added.
2000 * @instance
2001 */
2002 add: function(object, property) {
2003
2004 return add(
2005 this,
2006 object,
2007 property,
2008 {
2009 factoryArgs: Array.prototype.slice.call(arguments, 2)
2010 }
2011 );
2012
2013 },
2014
2015 /**
2016 * @param object
2017 * @param property
2018 * @returns {dat.controllers.ColorController} The new controller that was added.
2019 * @instance
2020 */
2021 addColor: function(object, property) {
2022
2023 return add(
2024 this,
2025 object,
2026 property,
2027 {
2028 color: true
2029 }
2030 );
2031
2032 },
2033
2034 /**
2035 * @param controller
2036 * @instance
2037 */
2038 remove: function(controller) {
2039
2040 // TODO listening?
2041 this.__ul.removeChild(controller.__li);
2042 this.__controllers.slice(this.__controllers.indexOf(controller), 1);
2043 var _this = this;
2044 common.defer(function() {
2045 _this.onResize();
2046 });
2047
2048 },
2049
2050 destroy: function() {
2051
2052 if (this.autoPlace) {
2053 auto_place_container.removeChild(this.domElement);
2054 }
2055
2056 },
2057
2058 /**
2059 * @param name
2060 * @returns {dat.gui.GUI} The new folder.
2061 * @throws {Error} if this GUI already has a folder by the specified
2062 * name
2063 * @instance
2064 */
2065 addFolder: function(name) {
2066
2067 // We have to prevent collisions on names in order to have a key
2068 // by which to remember saved values
2069 if (this.__folders[name] !== undefined) {
2070 throw new Error('You already have a folder in this GUI by the' +
2071 ' name "' + name + '"');
2072 }
2073
2074 var new_gui_params = { name: name, parent: this };
2075
2076 // We need to pass down the autoPlace trait so that we can
2077 // attach event listeners to open/close folder actions to
2078 // ensure that a scrollbar appears if the window is too short.
2079 new_gui_params.autoPlace = this.autoPlace;
2080
2081 // Do we have saved appearance data for this folder?
2082
2083 if (this.load && // Anything loaded?
2084 this.load.folders && // Was my parent a dead-end?
2085 this.load.folders[name]) { // Did daddy remember me?
2086
2087 // Start me closed if I was closed
2088 new_gui_params.closed = this.load.folders[name].closed;
2089
2090 // Pass down the loaded data
2091 new_gui_params.load = this.load.folders[name];
2092
2093 }
2094
2095 var gui = new GUI(new_gui_params);
2096 this.__folders[name] = gui;
2097
2098 var li = addRow(this, gui.domElement);
2099 dom.addClass(li, 'folder');
2100 return gui;
2101
2102 },
2103
2104 open: function() {
2105 this.closed = false;
2106 },
2107
2108 close: function() {
2109 this.closed = true;
2110 },
2111
2112 onResize: function() {
2113
2114 var root = this.getRoot();
2115
2116 if (root.scrollable) {
2117
2118 var top = dom.getOffset(root.__ul).top;
2119 var h = 0;
2120
2121 common.each(root.__ul.childNodes, function(node) {
2122 if (! (root.autoPlace && node === root.__save_row))
2123 h += dom.getHeight(node);
2124 });
2125
2126 if (window.innerHeight - top - CLOSE_BUTTON_HEIGHT < h) {
2127 dom.addClass(root.domElement, GUI.CLASS_TOO_TALL);
2128 root.__ul.style.height = window.innerHeight - top - CLOSE_BUTTON_HEIGHT + 'px';
2129 } else {
2130 dom.removeClass(root.domElement, GUI.CLASS_TOO_TALL);
2131 root.__ul.style.height = 'auto';
2132 }
2133
2134 }
2135
2136 if (root.__resize_handle) {
2137 common.defer(function() {
2138 root.__resize_handle.style.height = root.__ul.offsetHeight + 'px';
2139 });
2140 }
2141
2142 if (root.__closeButton) {
2143 root.__closeButton.style.width = root.width + 'px';
2144 }
2145
2146 },
2147
2148 /**
2149 * Mark objects for saving. The order of these objects cannot change as
2150 * the GUI grows. When remembering new objects, append them to the end
2151 * of the list.
2152 *
2153 * @param {Object...} objects
2154 * @throws {Error} if not called on a top level GUI.
2155 * @instance
2156 */
2157 remember: function() {
2158
2159 if (common.isUndefined(SAVE_DIALOGUE)) {
2160 SAVE_DIALOGUE = new CenteredDiv();
2161 SAVE_DIALOGUE.domElement.innerHTML = saveDialogueContents;
2162 }
2163
2164 if (this.parent) {
2165 throw new Error("You can only call remember on a top level GUI.");
2166 }
2167
2168 var _this = this;
2169
2170 common.each(Array.prototype.slice.call(arguments), function(object) {
2171 if (_this.__rememberedObjects.length == 0) {
2172 addSaveMenu(_this);
2173 }
2174 if (_this.__rememberedObjects.indexOf(object) == -1) {
2175 _this.__rememberedObjects.push(object);
2176 }
2177 });
2178
2179 if (this.autoPlace) {
2180 // Set save row width
2181 setWidth(this, this.width);
2182 }
2183
2184 },
2185
2186 /**
2187 * @returns {dat.gui.GUI} the topmost parent GUI of a nested GUI.
2188 * @instance
2189 */
2190 getRoot: function() {
2191 var gui = this;
2192 while (gui.parent) {
2193 gui = gui.parent;
2194 }
2195 return gui;
2196 },
2197
2198 /**
2199 * @returns {Object} a JSON object representing the current state of
2200 * this GUI as well as its remembered properties.
2201 * @instance
2202 */
2203 getSaveObject: function() {
2204
2205 var toReturn = this.load;
2206
2207 toReturn.closed = this.closed;
2208
2209 // Am I remembering any values?
2210 if (this.__rememberedObjects.length > 0) {
2211
2212 toReturn.preset = this.preset;
2213
2214 if (!toReturn.remembered) {
2215 toReturn.remembered = {};
2216 }
2217
2218 toReturn.remembered[this.preset] = getCurrentPreset(this);
2219
2220 }
2221
2222 toReturn.folders = {};
2223 common.each(this.__folders, function(element, key) {
2224 toReturn.folders[key] = element.getSaveObject();
2225 });
2226
2227 return toReturn;
2228
2229 },
2230
2231 save: function() {
2232
2233 if (!this.load.remembered) {
2234 this.load.remembered = {};
2235 }
2236
2237 this.load.remembered[this.preset] = getCurrentPreset(this);
2238 markPresetModified(this, false);
2239 this.saveToLocalStorageIfPossible();
2240
2241 },
2242
2243 saveAs: function(presetName) {
2244
2245 if (!this.load.remembered) {
2246
2247 // Retain default values upon first save
2248 this.load.remembered = {};
2249 this.load.remembered[DEFAULT_DEFAULT_PRESET_NAME] = getCurrentPreset(this, true);
2250
2251 }
2252
2253 this.load.remembered[presetName] = getCurrentPreset(this);
2254 this.preset = presetName;
2255 addPresetOption(this, presetName, true);
2256 this.saveToLocalStorageIfPossible();
2257
2258 },
2259
2260 revert: function(gui) {
2261
2262 common.each(this.__controllers, function(controller) {
2263 // Make revert work on Default.
2264 if (!this.getRoot().load.remembered) {
2265 controller.setValue(controller.initialValue);
2266 } else {
2267 recallSavedValue(gui || this.getRoot(), controller);
2268 }
2269 }, this);
2270
2271 common.each(this.__folders, function(folder) {
2272 folder.revert(folder);
2273 });
2274
2275 if (!gui) {
2276 markPresetModified(this.getRoot(), false);
2277 }
2278
2279
2280 },
2281
2282 listen: function(controller) {
2283
2284 var init = this.__listening.length == 0;
2285 this.__listening.push(controller);
2286 if (init) updateDisplays(this.__listening);
2287
2288 }
2289
2290 }
2291
2292 );
2293
2294 function add(gui, object, property, params) {
2295
2296 if (object[property] === undefined) {
2297 throw new Error("Object " + object + " has no property \"" + property + "\"");
2298 }
2299
2300 var controller;
2301
2302 if (params.color) {
2303
2304 controller = new ColorController(object, property);
2305
2306 } else {
2307
2308 var factoryArgs = [object,property].concat(params.factoryArgs);
2309 controller = controllerFactory.apply(gui, factoryArgs);
2310
2311 }
2312
2313 if (params.before instanceof Controller) {
2314 params.before = params.before.__li;
2315 }
2316
2317 recallSavedValue(gui, controller);
2318
2319 dom.addClass(controller.domElement, 'c');
2320
2321 var name = document.createElement('span');
2322 dom.addClass(name, 'property-name');
2323 name.innerHTML = controller.property;
2324
2325 var container = document.createElement('div');
2326 container.appendChild(name);
2327 container.appendChild(controller.domElement);
2328
2329 var li = addRow(gui, container, params.before);
2330
2331 dom.addClass(li, GUI.CLASS_CONTROLLER_ROW);
2332 dom.addClass(li, typeof controller.getValue());
2333
2334 augmentController(gui, li, controller);
2335
2336 gui.__controllers.push(controller);
2337
2338 return controller;
2339
2340 }
2341
2342 /**
2343 * Add a row to the end of the GUI or before another row.
2344 *
2345 * @param gui
2346 * @param [dom] If specified, inserts the dom content in the new row
2347 * @param [liBefore] If specified, places the new row before another row
2348 */
2349 function addRow(gui, dom, liBefore) {
2350 var li = document.createElement('li');
2351 if (dom) li.appendChild(dom);
2352 if (liBefore) {
2353 gui.__ul.insertBefore(li, params.before);
2354 } else {
2355 gui.__ul.appendChild(li);
2356 }
2357 gui.onResize();
2358 return li;
2359 }
2360
2361 function augmentController(gui, li, controller) {
2362
2363 controller.__li = li;
2364 controller.__gui = gui;
2365
2366 common.extend(controller, {
2367
2368 options: function(options) {
2369
2370 if (arguments.length > 1) {
2371 controller.remove();
2372
2373 return add(
2374 gui,
2375 controller.object,
2376 controller.property,
2377 {
2378 before: controller.__li.nextElementSibling,
2379 factoryArgs: [common.toArray(arguments)]
2380 }
2381 );
2382
2383 }
2384
2385 if (common.isArray(options) || common.isObject(options)) {
2386 controller.remove();
2387
2388 return add(
2389 gui,
2390 controller.object,
2391 controller.property,
2392 {
2393 before: controller.__li.nextElementSibling,
2394 factoryArgs: [options]
2395 }
2396 );
2397
2398 }
2399
2400 },
2401
2402 name: function(v) {
2403 controller.__li.firstElementChild.firstElementChild.innerHTML = v;
2404 return controller;
2405 },
2406
2407 listen: function() {
2408 controller.__gui.listen(controller);
2409 return controller;
2410 },
2411
2412 remove: function() {
2413 controller.__gui.remove(controller);
2414 return controller;
2415 }
2416
2417 });
2418
2419 // All sliders should be accompanied by a box.
2420 if (controller instanceof NumberControllerSlider) {
2421
2422 var box = new NumberControllerBox(controller.object, controller.property,
2423 { min: controller.__min, max: controller.__max, step: controller.__step });
2424
2425 common.each(['updateDisplay', 'onChange', 'onFinishChange'], function(method) {
2426 var pc = controller[method];
2427 var pb = box[method];
2428 controller[method] = box[method] = function() {
2429 var args = Array.prototype.slice.call(arguments);
2430 pc.apply(controller, args);
2431 return pb.apply(box, args);
2432 }
2433 });
2434
2435 dom.addClass(li, 'has-slider');
2436 controller.domElement.insertBefore(box.domElement, controller.domElement.firstElementChild);
2437
2438 }
2439 else if (controller instanceof NumberControllerBox) {
2440
2441 var r = function(returned) {
2442
2443 // Have we defined both boundaries?
2444 if (common.isNumber(controller.__min) && common.isNumber(controller.__max)) {
2445
2446 // Well, then lets just replace this with a slider.
2447 controller.remove();
2448 return add(
2449 gui,
2450 controller.object,
2451 controller.property,
2452 {
2453 before: controller.__li.nextElementSibling,
2454 factoryArgs: [controller.__min, controller.__max, controller.__step]
2455 });
2456
2457 }
2458
2459 return returned;
2460
2461 };
2462
2463 controller.min = common.compose(r, controller.min);
2464 controller.max = common.compose(r, controller.max);
2465
2466 }
2467 else if (controller instanceof BooleanController) {
2468
2469 dom.bind(li, 'click', function() {
2470 dom.fakeEvent(controller.__checkbox, 'click');
2471 });
2472
2473 dom.bind(controller.__checkbox, 'click', function(e) {
2474 e.stopPropagation(); // Prevents double-toggle
2475 })
2476
2477 }
2478 else if (controller instanceof FunctionController) {
2479
2480 dom.bind(li, 'click', function() {
2481 dom.fakeEvent(controller.__button, 'click');
2482 });
2483
2484 dom.bind(li, 'mouseover', function() {
2485 dom.addClass(controller.__button, 'hover');
2486 });
2487
2488 dom.bind(li, 'mouseout', function() {
2489 dom.removeClass(controller.__button, 'hover');
2490 });
2491
2492 }
2493 else if (controller instanceof ColorController) {
2494
2495 dom.addClass(li, 'color');
2496 controller.updateDisplay = common.compose(function(r) {
2497 li.style.borderLeftColor = controller.__color.toString();
2498 return r;
2499 }, controller.updateDisplay);
2500
2501 controller.updateDisplay();
2502
2503 }
2504
2505 controller.setValue = common.compose(function(r) {
2506 if (gui.getRoot().__preset_select && controller.isModified()) {
2507 markPresetModified(gui.getRoot(), true);
2508 }
2509 return r;
2510 }, controller.setValue);
2511
2512 }
2513
2514 function recallSavedValue(gui, controller) {
2515
2516 // Find the topmost GUI, that's where remembered objects live.
2517 var root = gui.getRoot();
2518
2519 // Does the object we're controlling match anything we've been told to
2520 // remember?
2521 var matched_index = root.__rememberedObjects.indexOf(controller.object);
2522
2523 // Why yes, it does!
2524 if (matched_index != -1) {
2525
2526 // Let me fetch a map of controllers for thcommon.isObject.
2527 var controller_map =
2528 root.__rememberedObjectIndecesToControllers[matched_index];
2529
2530 // Ohp, I believe this is the first controller we've created for this
2531 // object. Lets make the map fresh.
2532 if (controller_map === undefined) {
2533 controller_map = {};
2534 root.__rememberedObjectIndecesToControllers[matched_index] =
2535 controller_map;
2536 }
2537
2538 // Keep track of this controller
2539 controller_map[controller.property] = controller;
2540
2541 // Okay, now have we saved any values for this controller?
2542 if (root.load && root.load.remembered) {
2543
2544 var preset_map = root.load.remembered;
2545
2546 // Which preset are we trying to load?
2547 var preset;
2548
2549 if (preset_map[gui.preset]) {
2550
2551 preset = preset_map[gui.preset];
2552
2553 } else if (preset_map[DEFAULT_DEFAULT_PRESET_NAME]) {
2554
2555 // Uhh, you can have the default instead?
2556 preset = preset_map[DEFAULT_DEFAULT_PRESET_NAME];
2557
2558 } else {
2559
2560 // Nada.
2561
2562 return;
2563
2564 }
2565
2566
2567 // Did the loaded object remember thcommon.isObject?
2568 if (preset[matched_index] &&
2569
2570 // Did we remember this particular property?
2571 preset[matched_index][controller.property] !== undefined) {
2572
2573 // We did remember something for this guy ...
2574 var value = preset[matched_index][controller.property];
2575
2576 // And that's what it is.
2577 controller.initialValue = value;
2578 controller.setValue(value);
2579
2580 }
2581
2582 }
2583
2584 }
2585
2586 }
2587
2588 function getLocalStorageHash(gui, key) {
2589 // TODO how does this deal with multiple GUI's?
2590 return document.location.href + '.' + key;
2591
2592 }
2593
2594 function addSaveMenu(gui) {
2595
2596 var div = gui.__save_row = document.createElement('li');
2597
2598 dom.addClass(gui.domElement, 'has-save');
2599
2600 gui.__ul.insertBefore(div, gui.__ul.firstChild);
2601
2602 dom.addClass(div, 'save-row');
2603
2604 var gears = document.createElement('span');
2605 gears.innerHTML = '&nbsp;';
2606 dom.addClass(gears, 'button gears');
2607
2608 // TODO replace with FunctionController
2609 var button = document.createElement('span');
2610 button.innerHTML = 'Save';
2611 dom.addClass(button, 'button');
2612 dom.addClass(button, 'save');
2613
2614 var button2 = document.createElement('span');
2615 button2.innerHTML = 'New';
2616 dom.addClass(button2, 'button');
2617 dom.addClass(button2, 'save-as');
2618
2619 var button3 = document.createElement('span');
2620 button3.innerHTML = 'Revert';
2621 dom.addClass(button3, 'button');
2622 dom.addClass(button3, 'revert');
2623
2624 var select = gui.__preset_select = document.createElement('select');
2625
2626 if (gui.load && gui.load.remembered) {
2627
2628 common.each(gui.load.remembered, function(value, key) {
2629 addPresetOption(gui, key, key == gui.preset);
2630 });
2631
2632 } else {
2633 addPresetOption(gui, DEFAULT_DEFAULT_PRESET_NAME, false);
2634 }
2635
2636 dom.bind(select, 'change', function() {
2637
2638
2639 for (var index = 0; index < gui.__preset_select.length; index++) {
2640 gui.__preset_select[index].innerHTML = gui.__preset_select[index].value;
2641 }
2642
2643 gui.preset = this.value;
2644
2645 });
2646
2647 div.appendChild(select);
2648 div.appendChild(gears);
2649 div.appendChild(button);
2650 div.appendChild(button2);
2651 div.appendChild(button3);
2652
2653 if (SUPPORTS_LOCAL_STORAGE) {
2654
2655 var saveLocally = document.getElementById('dg-save-locally');
2656 var explain = document.getElementById('dg-local-explain');
2657
2658 saveLocally.style.display = 'block';
2659
2660 var localStorageCheckBox = document.getElementById('dg-local-storage');
2661
2662 if (localStorage.getItem(getLocalStorageHash(gui, 'isLocal')) === 'true') {
2663 localStorageCheckBox.setAttribute('checked', 'checked');
2664 }
2665
2666 function showHideExplain() {
2667 explain.style.display = gui.useLocalStorage ? 'block' : 'none';
2668 }
2669
2670 showHideExplain();
2671
2672 // TODO: Use a boolean controller, fool!
2673 dom.bind(localStorageCheckBox, 'change', function() {
2674 gui.useLocalStorage = !gui.useLocalStorage;
2675 showHideExplain();
2676 });
2677
2678 }
2679
2680 var newConstructorTextArea = document.getElementById('dg-new-constructor');
2681
2682 dom.bind(newConstructorTextArea, 'keydown', function(e) {
2683 if (e.metaKey && (e.which === 67 || e.keyCode == 67)) {
2684 SAVE_DIALOGUE.hide();
2685 }
2686 });
2687
2688 dom.bind(gears, 'click', function() {
2689 newConstructorTextArea.innerHTML = JSON.stringify(gui.getSaveObject(), undefined, 2);
2690 SAVE_DIALOGUE.show();
2691 newConstructorTextArea.focus();
2692 newConstructorTextArea.select();
2693 });
2694
2695 dom.bind(button, 'click', function() {
2696 gui.save();
2697 });
2698
2699 dom.bind(button2, 'click', function() {
2700 var presetName = prompt('Enter a new preset name.');
2701 if (presetName) gui.saveAs(presetName);
2702 });
2703
2704 dom.bind(button3, 'click', function() {
2705 gui.revert();
2706 });
2707
2708// div.appendChild(button2);
2709
2710 }
2711
2712 function addResizeHandle(gui) {
2713
2714 gui.__resize_handle = document.createElement('div');
2715
2716 common.extend(gui.__resize_handle.style, {
2717
2718 width: '6px',
2719 marginLeft: '-3px',
2720 height: '200px',
2721 cursor: 'ew-resize',
2722 position: 'absolute'
2723// border: '1px solid blue'
2724
2725 });
2726
2727 var pmouseX;
2728
2729 dom.bind(gui.__resize_handle, 'mousedown', dragStart);
2730 dom.bind(gui.__closeButton, 'mousedown', dragStart);
2731
2732 gui.domElement.insertBefore(gui.__resize_handle, gui.domElement.firstElementChild);
2733
2734 function dragStart(e) {
2735
2736 e.preventDefault();
2737
2738 pmouseX = e.clientX;
2739
2740 dom.addClass(gui.__closeButton, GUI.CLASS_DRAG);
2741 dom.bind(window, 'mousemove', drag);
2742 dom.bind(window, 'mouseup', dragStop);
2743
2744 return false;
2745
2746 }
2747
2748 function drag(e) {
2749
2750 e.preventDefault();
2751
2752 gui.width += pmouseX - e.clientX;
2753 gui.onResize();
2754 pmouseX = e.clientX;
2755
2756 return false;
2757
2758 }
2759
2760 function dragStop() {
2761
2762 dom.removeClass(gui.__closeButton, GUI.CLASS_DRAG);
2763 dom.unbind(window, 'mousemove', drag);
2764 dom.unbind(window, 'mouseup', dragStop);
2765
2766 }
2767
2768 }
2769
2770 function setWidth(gui, w) {
2771 gui.domElement.style.width = w + 'px';
2772 // Auto placed save-rows are position fixed, so we have to
2773 // set the width manually if we want it to bleed to the edge
2774 if (gui.__save_row && gui.autoPlace) {
2775 gui.__save_row.style.width = w + 'px';
2776 }if (gui.__closeButton) {
2777 gui.__closeButton.style.width = w + 'px';
2778 }
2779 }
2780
2781 function getCurrentPreset(gui, useInitialValues) {
2782
2783 var toReturn = {};
2784
2785 // For each object I'm remembering
2786 common.each(gui.__rememberedObjects, function(val, index) {
2787
2788 var saved_values = {};
2789
2790 // The controllers I've made for thcommon.isObject by property
2791 var controller_map =
2792 gui.__rememberedObjectIndecesToControllers[index];
2793
2794 // Remember each value for each property
2795 common.each(controller_map, function(controller, property) {
2796 saved_values[property] = useInitialValues ? controller.initialValue : controller.getValue();
2797 });
2798
2799 // Save the values for thcommon.isObject
2800 toReturn[index] = saved_values;
2801
2802 });
2803
2804 return toReturn;
2805
2806 }
2807
2808 function addPresetOption(gui, name, setSelected) {
2809 var opt = document.createElement('option');
2810 opt.innerHTML = name;
2811 opt.value = name;
2812 gui.__preset_select.appendChild(opt);
2813 if (setSelected) {
2814 gui.__preset_select.selectedIndex = gui.__preset_select.length - 1;
2815 }
2816 }
2817
2818 function setPresetSelectIndex(gui) {
2819 for (var index = 0; index < gui.__preset_select.length; index++) {
2820 if (gui.__preset_select[index].value == gui.preset) {
2821 gui.__preset_select.selectedIndex = index;
2822 }
2823 }
2824 }
2825
2826 function markPresetModified(gui, modified) {
2827 var opt = gui.__preset_select[gui.__preset_select.selectedIndex];
2828// console.log('mark', modified, opt);
2829 if (modified) {
2830 opt.innerHTML = opt.value + "*";
2831 } else {
2832 opt.innerHTML = opt.value;
2833 }
2834 }
2835
2836 function updateDisplays(controllerArray) {
2837
2838
2839 if (controllerArray.length != 0) {
2840
2841 requestAnimationFrame(function() {
2842 updateDisplays(controllerArray);
2843 });
2844
2845 }
2846
2847 common.each(controllerArray, function(c) {
2848 c.updateDisplay();
2849 });
2850
2851 }
2852
2853 return GUI;
2854
2855})(dat.utils.css,
2856"<div id=\"dg-save\" class=\"dg dialogue\">\n\n Here's the new load parameter for your <code>GUI</code>'s constructor:\n\n <textarea id=\"dg-new-constructor\"></textarea>\n\n <div id=\"dg-save-locally\">\n\n <input id=\"dg-local-storage\" type=\"checkbox\"/> Automatically save\n values to <code>localStorage</code> on exit.\n\n <div id=\"dg-local-explain\">The values saved to <code>localStorage</code> will\n override those passed to <code>dat.GUI</code>'s constructor. This makes it\n easier to work incrementally, but <code>localStorage</code> is fragile,\n and your friends may not see the same values you do.\n \n </div>\n \n </div>\n\n</div>",
2857".dg {\n /** Clear list styles */\n /* Auto-place container */\n /* Auto-placed GUI's */\n /* Line items that don't contain folders. */\n /** Folder names */\n /** Hides closed items */\n /** Controller row */\n /** Name-half (left) */\n /** Controller-half (right) */\n /** Controller placement */\n /** Shorter number boxes when slider is present. */\n /** Ensure the entire boolean and function row shows a hand */ }\n .dg ul {\n list-style: none;\n margin: 0;\n padding: 0;\n width: 100%;\n clear: both; }\n .dg.ac {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n height: 0;\n z-index: 0; }\n .dg:not(.ac) .main {\n /** Exclude mains in ac so that we don't hide close button */\n overflow: hidden; }\n .dg.main {\n -webkit-transition: opacity 0.1s linear;\n -o-transition: opacity 0.1s linear;\n -moz-transition: opacity 0.1s linear;\n transition: opacity 0.1s linear; }\n .dg.main.taller-than-window {\n overflow-y: auto; }\n .dg.main.taller-than-window .close-button {\n opacity: 1;\n /* TODO, these are style notes */\n margin-top: -1px;\n border-top: 1px solid #2c2c2c; }\n .dg.main ul.closed .close-button {\n opacity: 1 !important; }\n .dg.main:hover .close-button,\n .dg.main .close-button.drag {\n opacity: 1; }\n .dg.main .close-button {\n /*opacity: 0;*/\n -webkit-transition: opacity 0.1s linear;\n -o-transition: opacity 0.1s linear;\n -moz-transition: opacity 0.1s linear;\n transition: opacity 0.1s linear;\n border: 0;\n position: absolute;\n line-height: 19px;\n height: 20px;\n /* TODO, these are style notes */\n cursor: pointer;\n text-align: center;\n background-color: #000; }\n .dg.main .close-button:hover {\n background-color: #111; }\n .dg.a {\n float: right;\n margin-right: 15px;\n overflow-x: hidden; }\n .dg.a.has-save > ul {\n margin-top: 27px; }\n .dg.a.has-save > ul.closed {\n margin-top: 0; }\n .dg.a .save-row {\n position: fixed;\n top: 0;\n z-index: 1002; }\n .dg li {\n -webkit-transition: height 0.1s ease-out;\n -o-transition: height 0.1s ease-out;\n -moz-transition: height 0.1s ease-out;\n transition: height 0.1s ease-out; }\n .dg li:not(.folder) {\n cursor: auto;\n height: 27px;\n line-height: 27px;\n overflow: hidden;\n padding: 0 4px 0 5px; }\n .dg li.folder {\n padding: 0;\n border-left: 4px solid rgba(0, 0, 0, 0); }\n .dg li.title {\n cursor: pointer;\n margin-left: -4px; }\n .dg .closed li:not(.title),\n .dg .closed ul li,\n .dg .closed ul li > * {\n height: 0;\n overflow: hidden;\n border: 0; }\n .dg .cr {\n clear: both;\n padding-left: 3px;\n height: 27px; }\n .dg .property-name {\n cursor: default;\n float: left;\n clear: left;\n width: 40%;\n overflow: hidden;\n text-overflow: ellipsis; }\n .dg .c {\n float: left;\n width: 60%; }\n .dg .c input[type=text] {\n border: 0;\n margin-top: 4px;\n padding: 3px;\n width: 100%;\n float: right; }\n .dg .has-slider input[type=text] {\n width: 30%;\n /*display: none;*/\n margin-left: 0; }\n .dg .slider {\n float: left;\n width: 66%;\n margin-left: -5px;\n margin-right: 0;\n height: 19px;\n margin-top: 4px; }\n .dg .slider-fg {\n height: 100%; }\n .dg .c input[type=checkbox] {\n margin-top: 9px; }\n .dg .c select {\n margin-top: 5px; }\n .dg .cr.function,\n .dg .cr.function .property-name,\n .dg .cr.function *,\n .dg .cr.boolean,\n .dg .cr.boolean * {\n cursor: pointer; }\n .dg .selector {\n display: none;\n position: absolute;\n margin-left: -9px;\n margin-top: 23px;\n z-index: 10; }\n .dg .c:hover .selector,\n .dg .selector.drag {\n display: block; }\n .dg li.save-row {\n padding: 0; }\n .dg li.save-row .button {\n display: inline-block;\n padding: 0px 6px; }\n .dg.dialogue {\n background-color: #222;\n width: 460px;\n padding: 15px;\n font-size: 13px;\n line-height: 15px; }\n\n/* TODO Separate style and structure */\n#dg-new-constructor {\n padding: 10px;\n color: #222;\n font-family: Monaco, monospace;\n font-size: 10px;\n border: 0;\n resize: none;\n box-shadow: inset 1px 1px 1px #888;\n word-wrap: break-word;\n margin: 12px 0;\n display: block;\n width: 440px;\n overflow-y: scroll;\n height: 100px;\n position: relative; }\n\n#dg-local-explain {\n display: none;\n font-size: 11px;\n line-height: 17px;\n border-radius: 3px;\n background-color: #333;\n padding: 8px;\n margin-top: 10px; }\n #dg-local-explain code {\n font-size: 10px; }\n\n#dat-gui-save-locally {\n display: none; }\n\n/** Main type */\n.dg {\n color: #eee;\n font: 11px 'Lucida Grande', sans-serif;\n text-shadow: 0 -1px 0 #111;\n /** Auto place */\n /* Controller row, <li> */\n /** Controllers */ }\n .dg.main {\n /** Scrollbar */ }\n .dg.main::-webkit-scrollbar {\n width: 5px;\n background: #1a1a1a; }\n .dg.main::-webkit-scrollbar-corner {\n height: 0;\n display: none; }\n .dg.main::-webkit-scrollbar-thumb {\n border-radius: 5px;\n background: #676767; }\n .dg li:not(.folder) {\n background: #1a1a1a;\n border-bottom: 1px solid #2c2c2c; }\n .dg li.save-row {\n line-height: 25px;\n background: #dad5cb;\n border: 0; }\n .dg li.save-row select {\n margin-left: 5px;\n width: 108px; }\n .dg li.save-row .button {\n margin-left: 5px;\n margin-top: 1px;\n border-radius: 2px;\n font-size: 9px;\n line-height: 7px;\n padding: 4px 4px 5px 4px;\n background: #c5bdad;\n color: #fff;\n text-shadow: 0 1px 0 #b0a58f;\n box-shadow: 0 -1px 0 #b0a58f;\n cursor: pointer; }\n .dg li.save-row .button.gears {\n background: #c5bdad url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAANCAYAAAB/9ZQ7AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQJJREFUeNpiYKAU/P//PwGIC/ApCABiBSAW+I8AClAcgKxQ4T9hoMAEUrxx2QSGN6+egDX+/vWT4e7N82AMYoPAx/evwWoYoSYbACX2s7KxCxzcsezDh3evFoDEBYTEEqycggWAzA9AuUSQQgeYPa9fPv6/YWm/Acx5IPb7ty/fw+QZblw67vDs8R0YHyQhgObx+yAJkBqmG5dPPDh1aPOGR/eugW0G4vlIoTIfyFcA+QekhhHJhPdQxbiAIguMBTQZrPD7108M6roWYDFQiIAAv6Aow/1bFwXgis+f2LUAynwoIaNcz8XNx3Dl7MEJUDGQpx9gtQ8YCueB+D26OECAAQDadt7e46D42QAAAABJRU5ErkJggg==) 2px 1px no-repeat;\n height: 7px;\n width: 8px; }\n .dg li.save-row .button:hover {\n background-color: #bab19e;\n box-shadow: 0 -1px 0 #b0a58f; }\n .dg li.folder {\n border-bottom: 0; }\n .dg li.title {\n padding-left: 16px;\n background: black url(data:image/gif;base64,R0lGODlhBQAFAJEAAP////Pz8////////yH5BAEAAAIALAAAAAAFAAUAAAIIlI+hKgFxoCgAOw==) 6px 10px no-repeat;\n cursor: pointer;\n border-bottom: 1px solid rgba(255, 255, 255, 0.2); }\n .dg .closed li.title {\n background-image: url(data:image/gif;base64,R0lGODlhBQAFAJEAAP////Pz8////////yH5BAEAAAIALAAAAAAFAAUAAAIIlGIWqMCbWAEAOw==); }\n .dg .cr.boolean {\n border-left: 3px solid #806787; }\n .dg .cr.function {\n border-left: 3px solid #e61d5f; }\n .dg .cr.number {\n border-left: 3px solid #2fa1d6; }\n .dg .cr.number input[type=text] {\n color: #2fa1d6; }\n .dg .cr.string {\n border-left: 3px solid #1ed36f; }\n .dg .cr.string input[type=text] {\n color: #1ed36f; }\n .dg .cr.function:hover, .dg .cr.boolean:hover {\n background: #111; }\n .dg .c input[type=text] {\n background: #303030;\n outline: none; }\n .dg .c input[type=text]:hover {\n background: #3c3c3c; }\n .dg .c input[type=text]:focus {\n background: #494949;\n color: #fff; }\n .dg .c .slider {\n background: #303030;\n cursor: ew-resize; }\n .dg .c .slider-fg {\n background: #2fa1d6; }\n .dg .c .slider:hover {\n background: #3c3c3c; }\n .dg .c .slider:hover .slider-fg {\n background: #44abda; }\n",
2858dat.controllers.factory = (function (OptionController, NumberControllerBox, NumberControllerSlider, StringController, FunctionController, BooleanController, common) {
2859
2860 return function(object, property) {
2861
2862 var initialValue = object[property];
2863
2864 // Providing options?
2865 if (common.isArray(arguments[2]) || common.isObject(arguments[2])) {
2866 return new OptionController(object, property, arguments[2]);
2867 }
2868
2869 // Providing a map?
2870
2871 if (common.isNumber(initialValue)) {
2872
2873 if (common.isNumber(arguments[2]) && common.isNumber(arguments[3])) {
2874
2875 // Has min and max.
2876 return new NumberControllerSlider(object, property, arguments[2], arguments[3]);
2877
2878 } else {
2879
2880 return new NumberControllerBox(object, property, { min: arguments[2], max: arguments[3] });
2881
2882 }
2883
2884 }
2885
2886 if (common.isString(initialValue)) {
2887 return new StringController(object, property);
2888 }
2889
2890 if (common.isFunction(initialValue)) {
2891 return new FunctionController(object, property, '');
2892 }
2893
2894 if (common.isBoolean(initialValue)) {
2895 return new BooleanController(object, property);
2896 }
2897
2898 }
2899
2900 })(dat.controllers.OptionController,
2901dat.controllers.NumberControllerBox,
2902dat.controllers.NumberControllerSlider,
2903dat.controllers.StringController = (function (Controller, dom, common) {
2904
2905 /**
2906 * @class Provides a text input to alter the string property of an object.
2907 *
2908 * @extends dat.controllers.Controller
2909 *
2910 * @param {Object} object The object to be manipulated
2911 * @param {string} property The name of the property to be manipulated
2912 *
2913 * @member dat.controllers
2914 */
2915 var StringController = function(object, property) {
2916
2917 StringController.superclass.call(this, object, property);
2918
2919 var _this = this;
2920
2921 this.__input = document.createElement('input');
2922 this.__input.setAttribute('type', 'text');
2923
2924 dom.bind(this.__input, 'keyup', onChange);
2925 dom.bind(this.__input, 'change', onChange);
2926 dom.bind(this.__input, 'blur', onBlur);
2927 dom.bind(this.__input, 'keydown', function(e) {
2928 if (e.keyCode === 13) {
2929 this.blur();
2930 }
2931 });
2932
2933
2934 function onChange() {
2935 _this.setValue(_this.__input.value);
2936 }
2937
2938 function onBlur() {
2939 if (_this.__onFinishChange) {
2940 _this.__onFinishChange.call(_this, _this.getValue());
2941 }
2942 }
2943
2944 this.updateDisplay();
2945
2946 this.domElement.appendChild(this.__input);
2947
2948 };
2949
2950 StringController.superclass = Controller;
2951
2952 common.extend(
2953
2954 StringController.prototype,
2955 Controller.prototype,
2956
2957 {
2958
2959 updateDisplay: function() {
2960 // Stops the caret from moving on account of:
2961 // keyup -> setValue -> updateDisplay
2962 if (!dom.isActive(this.__input)) {
2963 this.__input.value = this.getValue();
2964 }
2965 return StringController.superclass.prototype.updateDisplay.call(this);
2966 }
2967
2968 }
2969
2970 );
2971
2972 return StringController;
2973
2974})(dat.controllers.Controller,
2975dat.dom.dom,
2976dat.utils.common),
2977dat.controllers.FunctionController,
2978dat.controllers.BooleanController,
2979dat.utils.common),
2980dat.controllers.Controller,
2981dat.controllers.BooleanController,
2982dat.controllers.FunctionController,
2983dat.controllers.NumberControllerBox,
2984dat.controllers.NumberControllerSlider,
2985dat.controllers.OptionController,
2986dat.controllers.ColorController = (function (Controller, dom, Color, interpret, common) {
2987
2988 var ColorController = function(object, property) {
2989
2990 ColorController.superclass.call(this, object, property);
2991
2992 this.__color = new Color(this.getValue());
2993 this.__temp = new Color(0);
2994
2995 var _this = this;
2996
2997 this.domElement = document.createElement('div');
2998
2999 dom.makeSelectable(this.domElement, false);
3000
3001 this.__selector = document.createElement('div');
3002 this.__selector.className = 'selector';
3003
3004 this.__saturation_field = document.createElement('div');
3005 this.__saturation_field.className = 'saturation-field';
3006
3007 this.__field_knob = document.createElement('div');
3008 this.__field_knob.className = 'field-knob';
3009 this.__field_knob_border = '2px solid ';
3010
3011 this.__hue_knob = document.createElement('div');
3012 this.__hue_knob.className = 'hue-knob';
3013
3014 this.__hue_field = document.createElement('div');
3015 this.__hue_field.className = 'hue-field';
3016
3017 this.__input = document.createElement('input');
3018 this.__input.type = 'text';
3019 this.__input_textShadow = '0 1px 1px ';
3020
3021 dom.bind(this.__input, 'keydown', function(e) {
3022 if (e.keyCode === 13) { // on enter
3023 onBlur.call(this);
3024 }
3025 });
3026
3027 dom.bind(this.__input, 'blur', onBlur);
3028
3029 dom.bind(this.__selector, 'mousedown', function(e) {
3030
3031 dom
3032 .addClass(this, 'drag')
3033 .bind(window, 'mouseup', function(e) {
3034 dom.removeClass(_this.__selector, 'drag');
3035 });
3036
3037 });
3038
3039 var value_field = document.createElement('div');
3040
3041 common.extend(this.__selector.style, {
3042 width: '122px',
3043 height: '102px',
3044 padding: '3px',
3045 backgroundColor: '#222',
3046 boxShadow: '0px 1px 3px rgba(0,0,0,0.3)'
3047 });
3048
3049 common.extend(this.__field_knob.style, {
3050 position: 'absolute',
3051 width: '12px',
3052 height: '12px',
3053 border: this.__field_knob_border + (this.__color.v < .5 ? '#fff' : '#000'),
3054 boxShadow: '0px 1px 3px rgba(0,0,0,0.5)',
3055 borderRadius: '12px',
3056 zIndex: 1
3057 });
3058
3059 common.extend(this.__hue_knob.style, {
3060 position: 'absolute',
3061 width: '15px',
3062 height: '2px',
3063 borderRight: '4px solid #fff',
3064 zIndex: 1
3065 });
3066
3067 common.extend(this.__saturation_field.style, {
3068 width: '100px',
3069 height: '100px',
3070 border: '1px solid #555',
3071 marginRight: '3px',
3072 display: 'inline-block',
3073 cursor: 'pointer'
3074 });
3075
3076 common.extend(value_field.style, {
3077 width: '100%',
3078 height: '100%',
3079 background: 'none'
3080 });
3081
3082 linearGradient(value_field, 'top', 'rgba(0,0,0,0)', '#000');
3083
3084 common.extend(this.__hue_field.style, {
3085 width: '15px',
3086 height: '100px',
3087 display: 'inline-block',
3088 border: '1px solid #555',
3089 cursor: 'ns-resize'
3090 });
3091
3092 hueGradient(this.__hue_field);
3093
3094 common.extend(this.__input.style, {
3095 outline: 'none',
3096// width: '120px',
3097 textAlign: 'center',
3098// padding: '4px',
3099// marginBottom: '6px',
3100 color: '#fff',
3101 border: 0,
3102 fontWeight: 'bold',
3103 textShadow: this.__input_textShadow + 'rgba(0,0,0,0.7)'
3104 });
3105
3106 dom.bind(this.__saturation_field, 'mousedown', fieldDown);
3107 dom.bind(this.__field_knob, 'mousedown', fieldDown);
3108
3109 dom.bind(this.__hue_field, 'mousedown', function(e) {
3110 setH(e);
3111 dom.bind(window, 'mousemove', setH);
3112 dom.bind(window, 'mouseup', unbindH);
3113 });
3114
3115 function fieldDown(e) {
3116 setSV(e);
3117 // document.body.style.cursor = 'none';
3118 dom.bind(window, 'mousemove', setSV);
3119 dom.bind(window, 'mouseup', unbindSV);
3120 }
3121
3122 function unbindSV() {
3123 dom.unbind(window, 'mousemove', setSV);
3124 dom.unbind(window, 'mouseup', unbindSV);
3125 // document.body.style.cursor = 'default';
3126 }
3127
3128 function onBlur() {
3129 var i = interpret(this.value);
3130 if (i !== false) {
3131 _this.__color.__state = i;
3132 _this.setValue(_this.__color.toOriginal());
3133 } else {
3134 this.value = _this.__color.toString();
3135 }
3136 }
3137
3138 function unbindH() {
3139 dom.unbind(window, 'mousemove', setH);
3140 dom.unbind(window, 'mouseup', unbindH);
3141 }
3142
3143 this.__saturation_field.appendChild(value_field);
3144 this.__selector.appendChild(this.__field_knob);
3145 this.__selector.appendChild(this.__saturation_field);
3146 this.__selector.appendChild(this.__hue_field);
3147 this.__hue_field.appendChild(this.__hue_knob);
3148
3149 this.domElement.appendChild(this.__input);
3150 this.domElement.appendChild(this.__selector);
3151
3152 this.updateDisplay();
3153
3154 function setSV(e) {
3155
3156 e.preventDefault();
3157
3158 var w = dom.getWidth(_this.__saturation_field);
3159 var o = dom.getOffset(_this.__saturation_field);
3160 var s = (e.clientX - o.left + document.body.scrollLeft) / w;
3161 var v = 1 - (e.clientY - o.top + document.body.scrollTop) / w;
3162
3163 if (v > 1) v = 1;
3164 else if (v < 0) v = 0;
3165
3166 if (s > 1) s = 1;
3167 else if (s < 0) s = 0;
3168
3169 _this.__color.v = v;
3170 _this.__color.s = s;
3171
3172 _this.setValue(_this.__color.toOriginal());
3173
3174
3175 return false;
3176
3177 }
3178
3179 function setH(e) {
3180
3181 e.preventDefault();
3182
3183 var s = dom.getHeight(_this.__hue_field);
3184 var o = dom.getOffset(_this.__hue_field);
3185 var h = 1 - (e.clientY - o.top + document.body.scrollTop) / s;
3186
3187 if (h > 1) h = 1;
3188 else if (h < 0) h = 0;
3189
3190 _this.__color.h = h * 360;
3191
3192 _this.setValue(_this.__color.toOriginal());
3193
3194 return false;
3195
3196 }
3197
3198 };
3199
3200 ColorController.superclass = Controller;
3201
3202 common.extend(
3203
3204 ColorController.prototype,
3205 Controller.prototype,
3206
3207 {
3208
3209 updateDisplay: function() {
3210
3211 var i = interpret(this.getValue());
3212
3213 if (i !== false) {
3214
3215 var mismatch = false;
3216
3217 // Check for mismatch on the interpreted value.
3218
3219 common.each(Color.COMPONENTS, function(component) {
3220 if (!common.isUndefined(i[component]) &&
3221 !common.isUndefined(this.__color.__state[component]) &&
3222 i[component] !== this.__color.__state[component]) {
3223 mismatch = true;
3224 return {}; // break
3225 }
3226 }, this);
3227
3228 // If nothing diverges, we keep our previous values
3229 // for statefulness, otherwise we recalculate fresh
3230 if (mismatch) {
3231 common.extend(this.__color.__state, i);
3232 }
3233
3234 }
3235
3236 common.extend(this.__temp.__state, this.__color.__state);
3237
3238 this.__temp.a = 1;
3239
3240 var flip = (this.__color.v < .5 || this.__color.s > .5) ? 255 : 0;
3241 var _flip = 255 - flip;
3242
3243 common.extend(this.__field_knob.style, {
3244 marginLeft: 100 * this.__color.s - 7 + 'px',
3245 marginTop: 100 * (1 - this.__color.v) - 7 + 'px',
3246 backgroundColor: this.__temp.toString(),
3247 border: this.__field_knob_border + 'rgb(' + flip + ',' + flip + ',' + flip +')'
3248 });
3249
3250 this.__hue_knob.style.marginTop = (1 - this.__color.h / 360) * 100 + 'px'
3251
3252 this.__temp.s = 1;
3253 this.__temp.v = 1;
3254
3255 linearGradient(this.__saturation_field, 'left', '#fff', this.__temp.toString());
3256
3257 common.extend(this.__input.style, {
3258 backgroundColor: this.__input.value = this.__color.toString(),
3259 color: 'rgb(' + flip + ',' + flip + ',' + flip +')',
3260 textShadow: this.__input_textShadow + 'rgba(' + _flip + ',' + _flip + ',' + _flip +',.7)'
3261 });
3262
3263 }
3264
3265 }
3266
3267 );
3268
3269 var vendors = ['-moz-','-o-','-webkit-','-ms-',''];
3270
3271 function linearGradient(elem, x, a, b) {
3272 elem.style.background = '';
3273 common.each(vendors, function(vendor) {
3274 elem.style.cssText += 'background: ' + vendor + 'linear-gradient('+x+', '+a+' 0%, ' + b + ' 100%); ';
3275 });
3276 }
3277
3278 function hueGradient(elem) {
3279 elem.style.background = '';
3280 elem.style.cssText += 'background: -moz-linear-gradient(top, #ff0000 0%, #ff00ff 17%, #0000ff 34%, #00ffff 50%, #00ff00 67%, #ffff00 84%, #ff0000 100%);'
3281 elem.style.cssText += 'background: -webkit-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);'
3282 elem.style.cssText += 'background: -o-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);'
3283 elem.style.cssText += 'background: -ms-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);'
3284 elem.style.cssText += 'background: linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);'
3285 }
3286
3287
3288 return ColorController;
3289
3290})(dat.controllers.Controller,
3291dat.dom.dom,
3292dat.color.Color = (function (interpret, math, toString, common) {
3293
3294 var Color = function() {
3295
3296 this.__state = interpret.apply(this, arguments);
3297
3298 if (this.__state === false) {
3299 throw 'Failed to interpret color arguments';
3300 }
3301
3302 this.__state.a = this.__state.a || 1;
3303
3304
3305 };
3306
3307 Color.COMPONENTS = ['r','g','b','h','s','v','hex','a'];
3308
3309 common.extend(Color.prototype, {
3310
3311 toString: function() {
3312 return toString(this);
3313 },
3314
3315 toOriginal: function() {
3316 return this.__state.conversion.write(this);
3317 }
3318
3319 });
3320
3321 defineRGBComponent(Color.prototype, 'r', 2);
3322 defineRGBComponent(Color.prototype, 'g', 1);
3323 defineRGBComponent(Color.prototype, 'b', 0);
3324
3325 defineHSVComponent(Color.prototype, 'h');
3326 defineHSVComponent(Color.prototype, 's');
3327 defineHSVComponent(Color.prototype, 'v');
3328
3329 Object.defineProperty(Color.prototype, 'a', {
3330
3331 get: function() {
3332 return this.__state.a;
3333 },
3334
3335 set: function(v) {
3336 this.__state.a = v;
3337 }
3338
3339 });
3340
3341 Object.defineProperty(Color.prototype, 'hex', {
3342
3343 get: function() {
3344
3345 if (!this.__state.space !== 'HEX') {
3346 this.__state.hex = math.rgb_to_hex(this.r, this.g, this.b);
3347 }
3348
3349 return this.__state.hex;
3350
3351 },
3352
3353 set: function(v) {
3354
3355 this.__state.space = 'HEX';
3356 this.__state.hex = v;
3357
3358 }
3359
3360 });
3361
3362 function defineRGBComponent(target, component, componentHexIndex) {
3363
3364 Object.defineProperty(target, component, {
3365
3366 get: function() {
3367
3368 if (this.__state.space === 'RGB') {
3369 return this.__state[component];
3370 }
3371
3372 recalculateRGB(this, component, componentHexIndex);
3373
3374 return this.__state[component];
3375
3376 },
3377
3378 set: function(v) {
3379
3380 if (this.__state.space !== 'RGB') {
3381 recalculateRGB(this, component, componentHexIndex);
3382 this.__state.space = 'RGB';
3383 }
3384
3385 this.__state[component] = v;
3386
3387 }
3388
3389 });
3390
3391 }
3392
3393 function defineHSVComponent(target, component) {
3394
3395 Object.defineProperty(target, component, {
3396
3397 get: function() {
3398
3399 if (this.__state.space === 'HSV')
3400 return this.__state[component];
3401
3402 recalculateHSV(this);
3403
3404 return this.__state[component];
3405
3406 },
3407
3408 set: function(v) {
3409
3410 if (this.__state.space !== 'HSV') {
3411 recalculateHSV(this);
3412 this.__state.space = 'HSV';
3413 }
3414
3415 this.__state[component] = v;
3416
3417 }
3418
3419 });
3420
3421 }
3422
3423 function recalculateRGB(color, component, componentHexIndex) {
3424
3425 if (color.__state.space === 'HEX') {
3426
3427 color.__state[component] = math.component_from_hex(color.__state.hex, componentHexIndex);
3428
3429 } else if (color.__state.space === 'HSV') {
3430
3431 common.extend(color.__state, math.hsv_to_rgb(color.__state.h, color.__state.s, color.__state.v));
3432
3433 } else {
3434
3435 throw 'Corrupted color state';
3436
3437 }
3438
3439 }
3440
3441 function recalculateHSV(color) {
3442
3443 var result = math.rgb_to_hsv(color.r, color.g, color.b);
3444
3445 common.extend(color.__state,
3446 {
3447 s: result.s,
3448 v: result.v
3449 }
3450 );
3451
3452 if (!common.isNaN(result.h)) {
3453 color.__state.h = result.h;
3454 } else if (common.isUndefined(color.__state.h)) {
3455 color.__state.h = 0;
3456 }
3457
3458 }
3459
3460 return Color;
3461
3462})(dat.color.interpret,
3463dat.color.math = (function () {
3464
3465 var tmpComponent;
3466
3467 return {
3468
3469 hsv_to_rgb: function(h, s, v) {
3470
3471 var hi = Math.floor(h / 60) % 6;
3472
3473 var f = h / 60 - Math.floor(h / 60);
3474 var p = v * (1.0 - s);
3475 var q = v * (1.0 - (f * s));
3476 var t = v * (1.0 - ((1.0 - f) * s));
3477 var c = [
3478 [v, t, p],
3479 [q, v, p],
3480 [p, v, t],
3481 [p, q, v],
3482 [t, p, v],
3483 [v, p, q]
3484 ][hi];
3485
3486 return {
3487 r: c[0] * 255,
3488 g: c[1] * 255,
3489 b: c[2] * 255
3490 };
3491
3492 },
3493
3494 rgb_to_hsv: function(r, g, b) {
3495
3496 var min = Math.min(r, g, b),
3497 max = Math.max(r, g, b),
3498 delta = max - min,
3499 h, s;
3500
3501 if (max != 0) {
3502 s = delta / max;
3503 } else {
3504 return {
3505 h: NaN,
3506 s: 0,
3507 v: 0
3508 };
3509 }
3510
3511 if (r == max) {
3512 h = (g - b) / delta;
3513 } else if (g == max) {
3514 h = 2 + (b - r) / delta;
3515 } else {
3516 h = 4 + (r - g) / delta;
3517 }
3518 h /= 6;
3519 if (h < 0) {
3520 h += 1;
3521 }
3522
3523 return {
3524 h: h * 360,
3525 s: s,
3526 v: max / 255
3527 };
3528 },
3529
3530 rgb_to_hex: function(r, g, b) {
3531 var hex = this.hex_with_component(0, 2, r);
3532 hex = this.hex_with_component(hex, 1, g);
3533 hex = this.hex_with_component(hex, 0, b);
3534 return hex;
3535 },
3536
3537 component_from_hex: function(hex, componentIndex) {
3538 return (hex >> (componentIndex * 8)) & 0xFF;
3539 },
3540
3541 hex_with_component: function(hex, componentIndex, value) {
3542 return value << (tmpComponent = componentIndex * 8) | (hex & ~ (0xFF << tmpComponent));
3543 }
3544
3545 }
3546
3547})(),
3548dat.color.toString,
3549dat.utils.common),
3550dat.color.interpret,
3551dat.utils.common),
3552dat.utils.requestAnimationFrame = (function () {
3553
3554 /**
3555 * requirejs version of Paul Irish's RequestAnimationFrame
3556 * http://paulirish.com/2011/requestanimationframe-for-smart-animating/
3557 */
3558
3559 return window.webkitRequestAnimationFrame ||
3560 window.mozRequestAnimationFrame ||
3561 window.oRequestAnimationFrame ||
3562 window.msRequestAnimationFrame ||
3563 function(callback, element) {
3564
3565 window.setTimeout(callback, 1000 / 60);
3566
3567 };
3568})(),
3569dat.dom.CenteredDiv = (function (dom, common) {
3570
3571
3572 var CenteredDiv = function() {
3573
3574 this.backgroundElement = document.createElement('div');
3575 common.extend(this.backgroundElement.style, {
3576 backgroundColor: 'rgba(0,0,0,0.8)',
3577 top: 0,
3578 left: 0,
3579 display: 'none',
3580 zIndex: '1000',
3581 opacity: 0,
3582 WebkitTransition: 'opacity 0.2s linear'
3583 });
3584
3585 dom.makeFullscreen(this.backgroundElement);
3586 this.backgroundElement.style.position = 'fixed';
3587
3588 this.domElement = document.createElement('div');
3589 common.extend(this.domElement.style, {
3590 position: 'fixed',
3591 display: 'none',
3592 zIndex: '1001',
3593 opacity: 0,
3594 WebkitTransition: '-webkit-transform 0.2s ease-out, opacity 0.2s linear'
3595 });
3596
3597
3598 document.body.appendChild(this.backgroundElement);
3599 document.body.appendChild(this.domElement);
3600
3601 var _this = this;
3602 dom.bind(this.backgroundElement, 'click', function() {
3603 _this.hide();
3604 });
3605
3606
3607 };
3608
3609 CenteredDiv.prototype.show = function() {
3610
3611 var _this = this;
3612
3613
3614
3615 this.backgroundElement.style.display = 'block';
3616
3617 this.domElement.style.display = 'block';
3618 this.domElement.style.opacity = 0;
3619// this.domElement.style.top = '52%';
3620 this.domElement.style.webkitTransform = 'scale(1.1)';
3621
3622 this.layout();
3623
3624 common.defer(function() {
3625 _this.backgroundElement.style.opacity = 1;
3626 _this.domElement.style.opacity = 1;
3627 _this.domElement.style.webkitTransform = 'scale(1)';
3628 });
3629
3630 };
3631
3632 CenteredDiv.prototype.hide = function() {
3633
3634 var _this = this;
3635
3636 var hide = function() {
3637
3638 _this.domElement.style.display = 'none';
3639 _this.backgroundElement.style.display = 'none';
3640
3641 dom.unbind(_this.domElement, 'webkitTransitionEnd', hide);
3642 dom.unbind(_this.domElement, 'transitionend', hide);
3643 dom.unbind(_this.domElement, 'oTransitionEnd', hide);
3644
3645 };
3646
3647 dom.bind(this.domElement, 'webkitTransitionEnd', hide);
3648 dom.bind(this.domElement, 'transitionend', hide);
3649 dom.bind(this.domElement, 'oTransitionEnd', hide);
3650
3651 this.backgroundElement.style.opacity = 0;
3652// this.domElement.style.top = '48%';
3653 this.domElement.style.opacity = 0;
3654 this.domElement.style.webkitTransform = 'scale(1.1)';
3655
3656 };
3657
3658 CenteredDiv.prototype.layout = function() {
3659 this.domElement.style.left = window.innerWidth/2 - dom.getWidth(this.domElement) / 2 + 'px';
3660 this.domElement.style.top = window.innerHeight/2 - dom.getHeight(this.domElement) / 2 + 'px';
3661 };
3662
3663 function lockScroll(e) {
3664 console.log(e);
3665 }
3666
3667 return CenteredDiv;
3668
3669})(dat.dom.dom,
3670dat.utils.common),
3671dat.dom.dom,
3672dat.utils.common);