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