diff --git a/.gitignore b/.gitignore index c219de4..b5d1df4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .DS_Store .sass-cache +.idea diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..e69de29 diff --git a/README.md b/README.md new file mode 100644 index 0000000..f5dd860 --- /dev/null +++ b/README.md @@ -0,0 +1,89 @@ +#dat.GUI +A lightweight graphical user interface for changing variables in JavaScript. + +Get started with dat.GUI by reading the tutorial at [http://workshop.chromeexperiments.com/examples/gui]. + +---- + +##Packaged Builds +The easiest way to use dat.GUI in your code is by using the built source at `build/dat.gui.min.js`. These built JavaScript files bundle all the necessary dependencies to run dat.GUI. + +In your `head` tag, include the following code: +``` + +``` + +---- + +##Using dat.GUI with require.js +Internally, dat.GUI uses [require.js](http://requirejs.org/) to handle dependency management. If you're making changes to the source and want to see the effects of your changes without building, use require js. + +In your `head` tag, include the following code: +``` + +``` + +Then, in `path/to/main.js`: +``` +require([ + 'path/to/gui/module/GUI' +], function(GUI) { + + // No namespace necessary + var gui = new GUI(); + +}); +``` + +---- + +##Directory Contents + * build: Concatenated source code. + * src: Modular code in [require.js](http://requirejs.org/) format. Also includes css, [scss](http://sass-lang.com/), and html, some of which is included during build. + * tests: [QUnit](https://github.com/jquery/qunit) test suite. + * utils: [node.js](http://nodejs.org/) utility scripts for compiling source. + +---- + +##Building your own dat.GUI + +In the terminal, enter the following: + +``` +$ cd utils +$ node build_gui.js +``` + +This will create a namespaced, unminified build of dat.GUI at `build/dat.gui.js` + +_To export minified source using Closure Compiler, open `utils/build_gui.js` and set the `minify` parameter to `true`._ + +---- + +##Change log + +###0.5 + * Moved to requirejs for dependency management. + * Changed global namespace from *DAT* to *dat* (lowercase). + * Added support for color controllers. See [Color Controllers](http://workshop.chromeexperiments.com/examples/gui/#4--Color-Controllers). + * Added support for folders. See [Folders](http://workshop.chromeexperiments.com/examples/gui/#3--Folders). + * Added support for saving named presets. See [Presets](http://workshop.chromeexperiments.com/examples/gui/examples/gui/#6--Presets). + * Removed `height` parameter from GUI constructor. Scrollbar automatically induced when window is too short. + * `dat.GUI.autoPlace` parameter removed. Use `new dat.GUI( { autoPlace: false } )`. See [Custom Placement](http://workshop.chromeexperiments.com/examples/gui/#9--Custom-Placement). + * `gui.autoListen` and `gui.listenAll()` removed. See [Updating The Display Manually](http://workshop.chromeexperiments.com/examples/gui/#11--Updating-the-Display-Manually). + * `dat.GUI.load` removed. See [Saving Values](http://workshop.chromeexperiments.com/examples/gui/#5--Saving-Values). + * Made Controller code completely agnostic of GUI. Controllers can easily be created independent of a GUI panel. + + +#0.4 + + * Migrated from GitHub to Google Code. + +---- + +##Thanks +The following libraries / open-source projects were used in the development of dat.GUI: + * [require.js](http://requirejs.org/) + * [Sass](http://sass-lang.com/) + * [node.js](http://nodejs.org/) + * [QUnit](https://github.com/jquery/qunit) / [jquery](http://jquery.com/) \ No newline at end of file diff --git a/build/dat.color.js b/build/dat.color.js deleted file mode 100644 index 42ad20c..0000000 --- a/build/dat.color.js +++ /dev/null @@ -1,755 +0,0 @@ -/** - * dat-gui JavaScript Controller Library - * http://code.google.com/p/dat-gui - * - * Copyright 2011 Data Arts Team, Google Creative Lab - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - */ - -/** @namespace */ -var dat = dat || {}; - -/** @namespace */ -dat.color = dat.color || {}; - -/** @namespace */ -dat.utils = dat.utils || {}; - -dat.utils.common = (function () { - - var ARR_EACH = Array.prototype.forEach; - var ARR_SLICE = Array.prototype.slice; - - /** - * Band-aid methods for things that should be a lot easier in JavaScript. - * Implementation and structure inspired by underscore.js - * http://documentcloud.github.com/underscore/ - */ - - return { - - BREAK: {}, - - extend: function(target) { - - this.each(ARR_SLICE.call(arguments, 1), function(obj) { - - for (var key in obj) - if (!this.isUndefined(obj[key])) - target[key] = obj[key]; - - }, this); - - return target; - - }, - - defaults: function(target) { - - this.each(ARR_SLICE.call(arguments, 1), function(obj) { - - for (var key in obj) - if (this.isUndefined(target[key])) - target[key] = obj[key]; - - }, this); - - return target; - - }, - - compose: function() { - var toCall = ARR_SLICE.call(arguments); - return function() { - var args = ARR_SLICE.call(arguments); - for (var i = toCall.length -1; i >= 0; i--) { - args = [toCall[i].apply(this, args)]; - } - return args[0]; - } - }, - - each: function(obj, itr, scope) { - - - if (ARR_EACH && obj.forEach === ARR_EACH) { - - obj.forEach(itr, scope); - - } else if (obj.length === obj.length + 0) { // Is number but not NaN - - for (var key = 0, l = obj.length; key < l; key++) - if (key in obj && itr.call(scope, obj[key], key) === this.BREAK) - return; - - } else { - - for (var key in obj) - if (itr.call(scope, obj[key], key) === this.BREAK) - return; - - } - - }, - - defer: function(fnc) { - setTimeout(fnc, 0); - }, - - toArray: function(obj) { - if (obj.toArray) return obj.toArray(); - return ARR_SLICE.call(obj); - }, - - isUndefined: function(obj) { - return obj === undefined; - }, - - isNull: function(obj) { - return obj === null; - }, - - isNaN: function(obj) { - return obj !== obj; - }, - - isArray: Array.isArray || function(obj) { - return obj.constructor === Array; - }, - - isObject: function(obj) { - return obj === Object(obj); - }, - - isNumber: function(obj) { - return obj === obj+0; - }, - - isString: function(obj) { - return obj === obj+''; - }, - - isBoolean: function(obj) { - return obj === false || obj === true; - }, - - isFunction: function(obj) { - return Object.prototype.toString.call(obj) === '[object Function]'; - } - - }; - -})(); - - -dat.color.toString = (function (common) { - - return function(color) { - - if (color.a == 1 || common.isUndefined(color.a)) { - - var s = color.hex.toString(16); - while (s.length < 6) { - s = '0' + s; - } - - return '#' + s; - - } else { - - return 'rgba(' + Math.round(color.r) + ',' + Math.round(color.g) + ',' + Math.round(color.b) + ',' + color.a + ')'; - - } - - } - -})(dat.utils.common); - - -dat.Color = dat.color.Color = (function (interpret, math, toString, common) { - - var Color = function() { - - this.__state = interpret.apply(this, arguments); - - if (this.__state === false) { - throw 'Failed to interpret color arguments'; - } - - this.__state.a = this.__state.a || 1; - - - }; - - Color.COMPONENTS = ['r','g','b','h','s','v','hex','a']; - - common.extend(Color.prototype, { - - toString: function() { - return toString(this); - }, - - toOriginal: function() { - return this.__state.conversion.write(this); - } - - }); - - defineRGBComponent(Color.prototype, 'r', 2); - defineRGBComponent(Color.prototype, 'g', 1); - defineRGBComponent(Color.prototype, 'b', 0); - - defineHSVComponent(Color.prototype, 'h'); - defineHSVComponent(Color.prototype, 's'); - defineHSVComponent(Color.prototype, 'v'); - - Object.defineProperty(Color.prototype, 'a', { - - get: function() { - return this.__state.a; - }, - - set: function(v) { - this.__state.a = v; - } - - }); - - Object.defineProperty(Color.prototype, 'hex', { - - get: function() { - - if (!this.__state.space !== 'HEX') { - this.__state.hex = math.rgb_to_hex(this.r, this.g, this.b); - } - - return this.__state.hex; - - }, - - set: function(v) { - - this.__state.space = 'HEX'; - this.__state.hex = v; - - } - - }); - - function defineRGBComponent(target, component, componentHexIndex) { - - Object.defineProperty(target, component, { - - get: function() { - - if (this.__state.space === 'RGB') { - return this.__state[component]; - } - - recalculateRGB(this, component, componentHexIndex); - - return this.__state[component]; - - }, - - set: function(v) { - - if (this.__state.space !== 'RGB') { - recalculateRGB(this, component, componentHexIndex); - this.__state.space = 'RGB'; - } - - this.__state[component] = v; - - } - - }); - - } - - function defineHSVComponent(target, component) { - - Object.defineProperty(target, component, { - - get: function() { - - if (this.__state.space === 'HSV') - return this.__state[component]; - - recalculateHSV(this); - - return this.__state[component]; - - }, - - set: function(v) { - - if (this.__state.space !== 'HSV') { - recalculateHSV(this); - this.__state.space = 'HSV'; - } - - this.__state[component] = v; - - } - - }); - - } - - function recalculateRGB(color, component, componentHexIndex) { - - if (color.__state.space === 'HEX') { - - color.__state[component] = math.component_from_hex(color.__state.hex, componentHexIndex); - - } else if (color.__state.space === 'HSV') { - - common.extend(color.__state, math.hsv_to_rgb(color.__state.h, color.__state.s, color.__state.v)); - - } else { - - throw 'Corrupted color state'; - - } - - } - - function recalculateHSV(color) { - - var result = math.rgb_to_hsv(color.r, color.g, color.b); - - common.extend(color.__state, - { - s: result.s, - v: result.v - } - ); - - if (!common.isNaN(result.h)) { - color.__state.h = result.h; - } else if (common.isUndefined(color.__state.h)) { - color.__state.h = 0; - } - - } - - return Color; - -})(dat.color.interpret = (function (toString, common) { - - var result, toReturn; - - var interpret = function() { - - toReturn = false; - - var original = arguments.length > 1 ? common.toArray(arguments) : arguments[0]; - - common.each(INTERPRETATIONS, function(family) { - - if (family.litmus(original)) { - - common.each(family.conversions, function(conversion, conversionName) { - - result = conversion.read(original); - - if (toReturn === false && result !== false) { - toReturn = result; - result.conversionName = conversionName; - result.conversion = conversion; - return common.BREAK; - - } - - }); - - return common.BREAK; - - } - - }); - - return toReturn; - - }; - - var INTERPRETATIONS = [ - - // Strings - { - - litmus: common.isString, - - conversions: { - - THREE_CHAR_HEX: { - - read: function(original) { - - var test = original.match(/^#([A-F0-9])([A-F0-9])([A-F0-9])$/i); - if (test === null) return false; - - return { - space: 'HEX', - hex: parseInt( - '0x' + - test[1].toString() + test[1].toString() + - test[2].toString() + test[2].toString() + - test[3].toString() + test[3].toString()) - }; - - }, - - write: toString - - }, - - SIX_CHAR_HEX: { - - read: function(original) { - - var test = original.match(/^#([A-F0-9]{6})$/i); - if (test === null) return false; - - return { - space: 'HEX', - hex: parseInt('0x' + test[1].toString()) - }; - - }, - - write: toString - - }, - - CSS_RGB: { - - read: function(original) { - - var test = original.match(/^rgb\(\s*(.+)\s*,\s*(.+)\s*,\s*(.+)\s*\)/); - if (test === null) return false; - - return { - space: 'RGB', - r: parseFloat(test[1]), - g: parseFloat(test[2]), - b: parseFloat(test[3]) - }; - - }, - - write: toString - - }, - - CSS_RGBA: { - - read: function(original) { - - var test = original.match(/^rgba\(\s*(.+)\s*,\s*(.+)\s*,\s*(.+)\s*\,\s*(.+)\s*\)/); - if (test === null) return false; - - return { - space: 'RGB', - r: parseFloat(test[1]), - g: parseFloat(test[2]), - b: parseFloat(test[3]), - a: parseFloat(test[4]) - }; - - }, - - write: toString - - } - - } - - }, - - // Numbers - { - - litmus: common.isNumber, - - conversions: { - - HEX: { - read: function(original) { - return { - space: 'HEX', - hex: original, - conversionName: 'HEX' - } - }, - - write: function(color) { - return color.hex; - } - } - - } - - }, - - // Arrays - { - - litmus: common.isArray, - - conversions: { - - RGB_ARRAY: { - read: function(original) { - if (original.length != 3) return false; - return { - space: 'RGB', - r: original[0], - g: original[1], - b: original[2] - }; - }, - - write: function(color) { - return [color.r, color.g, color.b]; - } - - }, - - RGBA_ARRAY: { - read: function(original) { - if (original.length != 4) return false; - return { - space: 'RGB', - r: original[0], - g: original[1], - b: original[2], - a: original[3] - }; - }, - - write: function(color) { - return [color.r, color.g, color.b, color.a]; - } - - } - - } - - }, - - // Objects - { - - litmus: common.isObject, - - conversions: { - - RGBA_OBJ: { - read: function(original) { - if (common.isNumber(original.r) && - common.isNumber(original.g) && - common.isNumber(original.b) && - common.isNumber(original.a)) { - return { - space: 'RGB', - r: original.r, - g: original.g, - b: original.b, - a: original.a - } - } - return false; - }, - - write: function(color) { - return { - r: color.r, - g: color.g, - b: color.b, - a: color.a - } - } - }, - - RGB_OBJ: { - read: function(original) { - if (common.isNumber(original.r) && - common.isNumber(original.g) && - common.isNumber(original.b)) { - return { - space: 'RGB', - r: original.r, - g: original.g, - b: original.b - } - } - return false; - }, - - write: function(color) { - return { - r: color.r, - g: color.g, - b: color.b - } - } - }, - - HSVA_OBJ: { - read: function(original) { - if (common.isNumber(original.h) && - common.isNumber(original.s) && - common.isNumber(original.v) && - common.isNumber(original.a)) { - return { - space: 'HSV', - h: original.h, - s: original.s, - v: original.v, - a: original.a - } - } - return false; - }, - - write: function(color) { - return { - h: color.h, - s: color.s, - v: color.v, - a: color.a - } - } - }, - - HSV_OBJ: { - read: function(original) { - if (common.isNumber(original.h) && - common.isNumber(original.s) && - common.isNumber(original.v)) { - return { - space: 'HSV', - h: original.h, - s: original.s, - v: original.v - } - } - return false; - }, - - write: function(color) { - return { - h: color.h, - s: color.s, - v: color.v - } - } - - } - - } - - } - - - ]; - - return interpret; - - -})(dat.color.toString, -dat.utils.common), -dat.color.math = (function () { - - var tmpComponent; - - return { - - hsv_to_rgb: function(h, s, v) { - - var hi = Math.floor(h / 60) % 6; - - var f = h / 60 - Math.floor(h / 60); - var p = v * (1.0 - s); - var q = v * (1.0 - (f * s)); - var t = v * (1.0 - ((1.0 - f) * s)); - var c = [ - [v, t, p], - [q, v, p], - [p, v, t], - [p, q, v], - [t, p, v], - [v, p, q] - ][hi]; - - return { - r: c[0] * 255, - g: c[1] * 255, - b: c[2] * 255 - }; - - }, - - rgb_to_hsv: function(r, g, b) { - - var min = Math.min(r, g, b), - max = Math.max(r, g, b), - delta = max - min, - h, s; - - if (max != 0) { - s = delta / max; - } else { - return { - h: NaN, - s: 0, - v: 0 - }; - } - - if (r == max) { - h = (g - b) / delta; - } else if (g == max) { - h = 2 + (b - r) / delta; - } else { - h = 4 + (r - g) / delta; - } - h /= 6; - if (h < 0) { - h += 1; - } - - return { - h: h * 360, - s: s, - v: max / 255 - }; - }, - - rgb_to_hex: function(r, g, b) { - var hex = this.hex_with_component(0, 2, r); - hex = this.hex_with_component(hex, 1, g); - hex = this.hex_with_component(hex, 0, b); - return hex; - }, - - component_from_hex: function(hex, componentIndex) { - return (hex >> (componentIndex * 8)) & 0xFF; - }, - - hex_with_component: function(hex, componentIndex, value) { - return value << (tmpComponent = componentIndex * 8) | (hex & ~ (0xFF << tmpComponent)); - } - - } - -})(), -dat.color.toString, -dat.utils.common); \ No newline at end of file diff --git a/build/dat.color.min.js b/build/dat.color.min.js deleted file mode 100644 index dc61bd6..0000000 --- a/build/dat.color.min.js +++ /dev/null @@ -1,26 +0,0 @@ -/** - * dat-gui JavaScript Controller Library - * http://code.google.com/p/dat-gui - * - * Copyright 2011 Data Arts Team, Google Creative Lab - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - */ -var dat=dat||{};dat.color=dat.color||{};dat.utils=dat.utils||{}; -dat.utils.common=function(){var f=Array.prototype.forEach,b=Array.prototype.slice;return{BREAK:{},extend:function(c){this.each(b.call(arguments,1),function(b){for(var d in b)this.isUndefined(b[d])||(c[d]=b[d])},this);return c},defaults:function(c){this.each(b.call(arguments,1),function(b){for(var d in b)this.isUndefined(c[d])&&(c[d]=b[d])},this);return c},compose:function(){var c=b.call(arguments);return function(){for(var e=b.call(arguments),d=c.length-1;d>=0;d--)e=[c[d].apply(this,e)];return e[0]}}, -each:function(b,e,d){if(f&&b.forEach===f)b.forEach(e,d);else if(b.length===b.length+0)for(var a=0,h=b.length;a1?b.toArray(arguments):arguments[0];b.each(d,function(d){if(d.litmus(a))return b.each(d.conversions,function(d,f){c=d.read(a);if(e===false&&c!==false)return e=c,c.conversionName=f,c.conversion=d,b.BREAK}),b.BREAK});return e}}(dat.color.toString,dat.utils.common),dat.color.math= -function(){var f;return{hsv_to_rgb:function(b,c,e){var d=b/60-Math.floor(b/60),a=e*(1-c),f=e*(1-d*c),c=e*(1-(1-d)*c),b=[[e,c,a],[f,e,a],[a,e,c],[a,f,e],[c,a,e],[e,a,f]][Math.floor(b/60)%6];return{r:b[0]*255,g:b[1]*255,b:b[2]*255}},rgb_to_hsv:function(b,c,e){var d=Math.min(b,c,e),a=Math.max(b,c,e),d=a-d;if(a==0)return{h:NaN,s:0,v:0};b=b==a?(c-e)/d:c==a?2+(e-b)/d:4+(b-c)/d;b/=6;b<0&&(b+=1);return{h:b*360,s:d/a,v:a/255}},rgb_to_hex:function(b,c,e){b=this.hex_with_component(0,2,b);b=this.hex_with_component(b, -1,c);return b=this.hex_with_component(b,0,e)},component_from_hex:function(b,c){return b>>c*8&255},hex_with_component:function(b,c,e){return e<<(f=c*8)|b&~(255<= 0; i--) { - args = [toCall[i].apply(this, args)]; - } - return args[0]; - } - }, - - each: function(obj, itr, scope) { - - - if (ARR_EACH && obj.forEach === ARR_EACH) { - - obj.forEach(itr, scope); - - } else if (obj.length === obj.length + 0) { // Is number but not NaN - - for (var key = 0, l = obj.length; key < l; key++) - if (key in obj && itr.call(scope, obj[key], key) === this.BREAK) - return; - - } else { - - for (var key in obj) - if (itr.call(scope, obj[key], key) === this.BREAK) - return; - - } - - }, - - defer: function(fnc) { - setTimeout(fnc, 0); - }, - - toArray: function(obj) { - if (obj.toArray) return obj.toArray(); - return ARR_SLICE.call(obj); - }, - - isUndefined: function(obj) { - return obj === undefined; - }, - - isNull: function(obj) { - return obj === null; - }, - - isNaN: function(obj) { - return obj !== obj; - }, - - isArray: Array.isArray || function(obj) { - return obj.constructor === Array; - }, - - isObject: function(obj) { - return obj === Object(obj); - }, - - isNumber: function(obj) { - return obj === obj+0; - }, - - isString: function(obj) { - return obj === obj+''; - }, - - isBoolean: function(obj) { - return obj === false || obj === true; - }, - - isFunction: function(obj) { - return Object.prototype.toString.call(obj) === '[object Function]'; - } - - }; - -})(); - - -dat.controllers.Controller = (function (common) { - - /** - * @class An "abstract" class that represents a given property of an object. - * - * @param {Object} object The object to be manipulated - * @param {string} property The name of the property to be manipulated - * - * @member dat.controllers - */ - var Controller = function(object, property) { - - this.initialValue = object[property]; - - /** - * Those who extend this class will put their DOM elements in here. - * @type {DOMElement} - */ - this.domElement = document.createElement('div'); - - /** - * The object to manipulate - * @type {Object} - */ - this.object = object; - - /** - * The name of the property to manipulate - * @type {String} - */ - this.property = property; - - /** - * The function to be called on change. - * @type {Function} - * @ignore - */ - this.__onChange = undefined; - - /** - * The function to be called on finishing change. - * @type {Function} - * @ignore - */ - this.__onFinishChange = undefined; - - }; - - common.extend( - - Controller.prototype, - - /** @lends dat.controllers.Controller.prototype */ - { - - /** - * Specify that a function fire every time someone changes the value with - * this Controller. - * - * @param {Function} fnc This function will be called whenever the value - * is modified via this Controller. - * @returns {dat.controllers.Controller} this - */ - onChange: function(fnc) { - this.__onChange = fnc; - return this; - }, - - /** - * Specify that a function fire every time someone "finishes" changing - * the value wih this Controller. Useful for values that change - * incrementally like numbers or strings. - * - * @param {Function} fnc This function will be called whenever - * someone "finishes" changing the value via this Controller. - * @returns {dat.controllers.Controller} this - */ - onFinishChange: function(fnc) { - this.__onFinishChange = fnc; - return this; - }, - - /** - * Change the value of object[property] - * - * @param {Object} newValue The new value of object[property] - */ - setValue: function(newValue) { - this.object[this.property] = newValue; - if (this.__onChange) { - this.__onChange.call(this, newValue); - } - this.updateDisplay(); - return this; - }, - - /** - * Gets the value of object[property] - * - * @returns {Object} The current value of object[property] - */ - getValue: function() { - return this.object[this.property]; - }, - - /** - * Refreshes the visual display of a Controller in order to keep sync - * with the object's current value. - * @returns {dat.controllers.Controller} this - */ - updateDisplay: function() { - return this; - }, - - /** - * @returns {Boolean} true if the value has deviated from initialValue - */ - isModified: function() { - return this.initialValue !== this.getValue() - } - - } - - ); - - return Controller; - - -})(dat.utils.common); - - -dat.dom.dom = (function (common) { - - var EVENT_MAP = { - 'HTMLEvents': ['change'], - 'MouseEvents': ['click','mousemove','mousedown','mouseup', 'mouseover'], - 'KeyboardEvents': ['keydown'] - }; - - var EVENT_MAP_INV = {}; - common.each(EVENT_MAP, function(v, k) { - common.each(v, function(e) { - EVENT_MAP_INV[e] = k; - }); - }); - - var CSS_VALUE_PIXELS = /(\d+(\.\d+)?)px/; - - function cssValueToPixels(val) { - - if (val === '0' || common.isUndefined(val)) return 0; - - var match = val.match(CSS_VALUE_PIXELS); - - if (!common.isNull(match)) { - return parseFloat(match[1]); - } - - // TODO ...ems? %? - - return 0; - - } - - /** - * @namespace - * @member dat.dom - */ - var dom = { - - /** - * - * @param elem - * @param selectable - */ - makeSelectable: function(elem, selectable) { - - if (elem === undefined || elem.style === undefined) return; - - elem.onselectstart = selectable ? function() { - return false; - } : function() { - }; - - elem.style.MozUserSelect = selectable ? 'auto' : 'none'; - elem.style.KhtmlUserSelect = selectable ? 'auto' : 'none'; - elem.unselectable = selectable ? 'on' : 'off'; - - }, - - /** - * - * @param elem - * @param horizontal - * @param vertical - */ - makeFullscreen: function(elem, horizontal, vertical) { - - if (common.isUndefined(horizontal)) horizontal = true; - if (common.isUndefined(vertical)) vertical = true; - - elem.style.position = 'absolute'; - - if (horizontal) { - elem.style.left = 0; - elem.style.right = 0; - } - if (vertical) { - elem.style.top = 0; - elem.style.bottom = 0; - } - - }, - - /** - * - * @param elem - * @param eventType - * @param params - */ - fakeEvent: function(elem, eventType, params, aux) { - params = params || {}; - var className = EVENT_MAP_INV[eventType]; - if (!className) { - throw new Error('Event type ' + eventType + ' not supported.'); - } - var evt = document.createEvent(className); - switch (className) { - case 'MouseEvents': - var clientX = params.x || params.clientX || 0; - var clientY = params.y || params.clientY || 0; - evt.initMouseEvent(eventType, params.bubbles || false, - params.cancelable || true, window, params.clickCount || 1, - 0, //screen X - 0, //screen Y - clientX, //client X - clientY, //client Y - false, false, false, false, 0, null); - break; - case 'KeyboardEvents': - var init = evt.initKeyboardEvent || evt.initKeyEvent; // webkit || moz - common.defaults(params, { - cancelable: true, - ctrlKey: false, - altKey: false, - shiftKey: false, - metaKey: false, - keyCode: undefined, - charCode: undefined - }); - init(eventType, params.bubbles || false, - params.cancelable, window, - params.ctrlKey, params.altKey, - params.shiftKey, params.metaKey, - params.keyCode, params.charCode); - break; - default: - evt.initEvent(eventType, params.bubbles || false, - params.cancelable || true); - break; - } - common.defaults(evt, aux); - elem.dispatchEvent(evt); - }, - - /** - * - * @param elem - * @param event - * @param func - * @param bool - */ - bind: function(elem, event, func, bool) { - bool = bool || false; - if (elem.addEventListener) - elem.addEventListener(event, func, bool); - else if (elem.attachEvent) - elem.attachEvent('on' + event, func); - return dom; - }, - - /** - * - * @param elem - * @param event - * @param func - * @param bool - */ - unbind: function(elem, event, func, bool) { - bool = bool || false; - if (elem.removeEventListener) - elem.removeEventListener(event, func, bool); - else if (elem.detachEvent) - elem.detachEvent('on' + event, func); - return dom; - }, - - /** - * - * @param elem - * @param className - */ - addClass: function(elem, className) { - if (elem.className === undefined) { - elem.className = className; - } else if (elem.className !== className) { - var classes = elem.className.split(/ +/); - if (classes.indexOf(className) == -1) { - classes.push(className); - elem.className = classes.join(' ').replace(/^\s+/, '').replace(/\s+$/, ''); - } - } - return dom; - }, - - /** - * - * @param elem - * @param className - */ - removeClass: function(elem, className) { - if (className) { - if (elem.className === undefined) { - // elem.className = className; - } else if (elem.className === className) { - elem.removeAttribute('class'); - } else { - var classes = elem.className.split(/ +/); - var index = classes.indexOf(className); - if (index != -1) { - classes.splice(index, 1); - elem.className = classes.join(' '); - } - } - } else { - elem.className = undefined; - } - return dom; - }, - - hasClass: function(elem, className) { - return new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)').test(elem.className) || false; - }, - - /** - * - * @param elem - */ - getWidth: function(elem) { - - var style = getComputedStyle(elem); - - return cssValueToPixels(style['border-left-width']) + - cssValueToPixels(style['border-right-width']) + - cssValueToPixels(style['padding-left']) + - cssValueToPixels(style['padding-right']) + - cssValueToPixels(style['width']); - }, - - /** - * - * @param elem - */ - getHeight: function(elem) { - - var style = getComputedStyle(elem); - - return cssValueToPixels(style['border-top-width']) + - cssValueToPixels(style['border-bottom-width']) + - cssValueToPixels(style['padding-top']) + - cssValueToPixels(style['padding-bottom']) + - cssValueToPixels(style['height']); - }, - - /** - * - * @param elem - */ - getOffset: function(elem) { - var offset = {left: 0, top:0}; - if (elem.offsetParent) { - do { - offset.left += elem.offsetLeft; - offset.top += elem.offsetTop; - } while (elem = elem.offsetParent); - } - return offset; - }, - - // http://stackoverflow.com/posts/2684561/revisions - /** - * - * @param elem - */ - isActive: function(elem) { - return elem === document.activeElement && ( elem.type || elem.href ); - } - - }; - - return dom; - -})(dat.utils.common); - - -dat.controllers.OptionController = (function (Controller, dom, common) { - - /** - * @class Provides a select input to alter the property of an object, using a - * list of accepted values. - * - * @extends dat.controllers.Controller - * - * @param {Object} object The object to be manipulated - * @param {string} property The name of the property to be manipulated - * @param {Object|string[]} options A map of labels to acceptable values, or - * a list of acceptable string values. - * - * @member dat.controllers - */ - var OptionController = function(object, property, options) { - - OptionController.superclass.call(this, object, property); - - var _this = this; - - /** - * The drop down menu - * @ignore - */ - this.__select = document.createElement('select'); - - if (common.isArray(options)) { - var map = {}; - common.each(options, function(element) { - map[element] = element; - }); - options = map; - } - - common.each(options, function(value, key) { - - var opt = document.createElement('option'); - opt.innerHTML = key; - opt.setAttribute('value', value); - _this.__select.appendChild(opt); - - }); - - // Acknowledge original value - this.updateDisplay(); - - dom.bind(this.__select, 'change', function() { - var desiredValue = this.options[this.selectedIndex].value; - _this.setValue(desiredValue); - }); - - this.domElement.appendChild(this.__select); - - }; - - OptionController.superclass = Controller; - - common.extend( - - OptionController.prototype, - Controller.prototype, - - { - - setValue: function(v) { - var toReturn = OptionController.superclass.prototype.setValue.call(this, v); - if (this.__onFinishChange) { - this.__onFinishChange.call(this, this.getValue()); - } - return toReturn; - }, - - updateDisplay: function() { - this.__select.value = this.getValue(); - return OptionController.superclass.prototype.updateDisplay.call(this); - } - - } - - ); - - return OptionController; - -})(dat.controllers.Controller, -dat.dom.dom, -dat.utils.common); - - -dat.controllers.NumberController = (function (Controller, common) { - - /** - * @class Represents a given property of an object that is a number. - * - * @extends dat.controllers.Controller - * - * @param {Object} object The object to be manipulated - * @param {string} property The name of the property to be manipulated - * @param {Object} [params] Optional parameters - * @param {Number} [params.min] Minimum allowed value - * @param {Number} [params.max] Maximum allowed value - * @param {Number} [params.step] Increment by which to change value - * - * @member dat.controllers - */ - var NumberController = function(object, property, params) { - - NumberController.superclass.call(this, object, property); - - params = params || {}; - - this.__min = params.min; - this.__max = params.max; - this.__step = params.step; - - if (common.isUndefined(this.__step)) { - - if (this.initialValue == 0) { - this.__impliedStep = 1; // What are we, psychics? - } else { - // Hey Doug, check this out. - this.__impliedStep = Math.pow(10, Math.floor(Math.log(this.initialValue)/Math.LN10))/10; - } - - } else { - - this.__impliedStep = this.__step; - - } - - this.__precision = numDecimals(this.__impliedStep); - - - }; - - NumberController.superclass = Controller; - - common.extend( - - NumberController.prototype, - Controller.prototype, - - /** @lends dat.controllers.NumberController.prototype */ - { - - setValue: function(v) { - - if (this.__min !== undefined && v < this.__min) { - v = this.__min; - } else if (this.__max !== undefined && v > this.__max) { - v = this.__max; - } - - if (this.__step !== undefined && v % this.__step != 0) { - v = Math.round(v / this.__step) * this.__step; - } - - return NumberController.superclass.prototype.setValue.call(this, v); - - }, - - /** - * Specify a minimum value for object[property]. - * - * @param {Number} minValue The minimum value for - * object[property] - * @returns {dat.controllers.NumberController} this - */ - min: function(v) { - this.__min = v; - return this; - }, - - /** - * Specify a maximum value for object[property]. - * - * @param {Number} maxValue The maximum value for - * object[property] - * @returns {dat.controllers.NumberController} this - */ - max: function(v) { - this.__max = v; - return this; - }, - - /** - * Specify a step value that dat.controllers.NumberController - * increments by. - * - * @param {Number} stepValue The step value for - * dat.controllers.NumberController - * @default if minimum and maximum specified increment is 1% of the - * difference otherwise stepValue is 1 - * @returns {dat.controllers.NumberController} this - */ - step: function(v) { - this.__step = v; - return this; - } - - } - - ); - - function numDecimals(x) { - x = x.toString(); - if (x.indexOf('.') > -1) { - return x.length - x.indexOf('.') - 1; - } else { - return 0; - } - } - - return NumberController; - -})(dat.controllers.Controller, -dat.utils.common); - - -dat.controllers.NumberControllerBox = (function (NumberController, dom, common) { - - /** - * @class Represents a given property of an object that is a number and - * provides an input element with which to manipulate it. - * - * @extends dat.controllers.Controller - * @extends dat.controllers.NumberController - * - * @param {Object} object The object to be manipulated - * @param {string} property The name of the property to be manipulated - * @param {Object} [params] Optional parameters - * @param {Number} [params.min] Minimum allowed value - * @param {Number} [params.max] Maximum allowed value - * @param {Number} [params.step] Increment by which to change value - * - * @member dat.controllers - */ - var NumberControllerBox = function(object, property, params) { - - this.__truncationSuspended = false; - - NumberControllerBox.superclass.call(this, object, property, params); - - var _this = this; - - /** - * {Number} Previous mouse y position - * @ignore - */ - var prev_y; - - this.__input = document.createElement('input'); - this.__input.setAttribute('type', 'text'); - - // Makes it so manually specified values are not truncated. - - dom.bind(this.__input, 'change', onChange); - dom.bind(this.__input, 'blur', onBlur); - dom.bind(this.__input, 'mousedown', onMouseDown); - dom.bind(this.__input, 'keydown', function(e) { - - // When pressing entire, you can be as precise as you want. - if (e.keyCode === 13) { - _this.__truncationSuspended = true; - this.blur(); - _this.__truncationSuspended = false; - } - - }); - - function onChange() { - var attempted = parseFloat(_this.__input.value); - if (!common.isNaN(attempted)) _this.setValue(attempted); - } - - function onBlur() { - onChange(); - if (_this.__onFinishChange) { - _this.__onFinishChange.call(_this, _this.getValue()); - } - } - - function onMouseDown(e) { - dom.bind(window, 'mousemove', onMouseDrag); - dom.bind(window, 'mouseup', onMouseUp); - prev_y = e.clientY; - } - - function onMouseDrag(e) { - - var diff = prev_y - e.clientY; - _this.setValue(_this.getValue() + diff * _this.__impliedStep); - - prev_y = e.clientY; - - } - - function onMouseUp() { - dom.unbind(window, 'mousemove', onMouseDrag); - dom.unbind(window, 'mouseup', onMouseUp); - } - - this.updateDisplay(); - - this.domElement.appendChild(this.__input); - - }; - - NumberControllerBox.superclass = NumberController; - - common.extend( - - NumberControllerBox.prototype, - NumberController.prototype, - - { - - updateDisplay: function() { - - this.__input.value = this.__truncationSuspended ? this.getValue() : roundToDecimal(this.getValue(), this.__precision); - return NumberControllerBox.superclass.prototype.updateDisplay.call(this); - } - - } - - ); - - function roundToDecimal(value, decimals) { - var tenTo = Math.pow(10, decimals); - return Math.round(value * tenTo) / tenTo; - } - - return NumberControllerBox; - -})(dat.controllers.NumberController, -dat.dom.dom, -dat.utils.common); - - -dat.controllers.NumberControllerSlider = (function (NumberController, dom, css, common, styleSheet) { - - /** - * @class Represents a given property of an object that is a number, contains - * a minimum and maximum, and provides a slider element with which to - * manipulate it. It should be noted that the slider element is made up of - * <div> tags, not the html5 - * <slider> element. - * - * @extends dat.controllers.Controller - * @extends dat.controllers.NumberController - * - * @param {Object} object The object to be manipulated - * @param {string} property The name of the property to be manipulated - * @param {Number} minValue Minimum allowed value - * @param {Number} maxValue Maximum allowed value - * @param {Number} stepValue Increment by which to change value - * - * @member dat.controllers - */ - var NumberControllerSlider = function(object, property, min, max, step) { - - NumberControllerSlider.superclass.call(this, object, property, { min: min, max: max, step: step }); - - var _this = this; - - this.__background = document.createElement('div'); - this.__foreground = document.createElement('div'); - - - - dom.bind(this.__background, 'mousedown', onMouseDown); - - dom.addClass(this.__background, 'slider'); - dom.addClass(this.__foreground, 'slider-fg'); - - function onMouseDown(e) { - - dom.bind(window, 'mousemove', onMouseDrag); - dom.bind(window, 'mouseup', onMouseUp); - - onMouseDrag(e); - } - - function onMouseDrag(e) { - - e.preventDefault(); - - var offset = dom.getOffset(_this.__background); - var width = dom.getWidth(_this.__background); - - _this.setValue( - map(e.clientX, offset.left, offset.left + width, _this.__min, _this.__max) - ); - - return false; - - } - - function onMouseUp() { - dom.unbind(window, 'mousemove', onMouseDrag); - dom.unbind(window, 'mouseup', onMouseUp); - if (_this.__onFinishChange) { - _this.__onFinishChange.call(_this, _this.getValue()); - } - } - - this.updateDisplay(); - - this.__background.appendChild(this.__foreground); - this.domElement.appendChild(this.__background); - - }; - - NumberControllerSlider.superclass = NumberController; - - /** - * Injects default stylesheet for slider elements. - */ - NumberControllerSlider.useDefaultStyles = function() { - css.inject(styleSheet); - }; - - common.extend( - - NumberControllerSlider.prototype, - NumberController.prototype, - - { - - updateDisplay: function() { - var pct = (this.getValue() - this.__min)/(this.__max - this.__min); - this.__foreground.style.width = pct*100+'%'; - return NumberControllerSlider.superclass.prototype.updateDisplay.call(this); - } - - } - - - - ); - - function map(v, i1, i2, o1, o2) { - return o1 + (o2 - o1) * ((v - i1) / (i2 - i1)); - } - - return NumberControllerSlider; - -})(dat.controllers.NumberController, -dat.dom.dom, -dat.utils.css, -dat.utils.common, -".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}"); - - -dat.controllers.FunctionController = (function (Controller, dom, common) { - - /** - * @class Provides a GUI interface to fire a specified method, a property of an object. - * - * @extends dat.controllers.Controller - * - * @param {Object} object The object to be manipulated - * @param {string} property The name of the property to be manipulated - * - * @member dat.controllers - */ - var FunctionController = function(object, property, text) { - - FunctionController.superclass.call(this, object, property); - - var _this = this; - - this.__button = document.createElement('div'); - this.__button.innerHTML = text === undefined ? 'Fire' : text; - dom.bind(this.__button, 'click', function(e) { - e.preventDefault(); - _this.fire(); - return false; - }); - - dom.addClass(this.__button, 'button'); - - this.domElement.appendChild(this.__button); - - - }; - - FunctionController.superclass = Controller; - - common.extend( - - FunctionController.prototype, - Controller.prototype, - { - - fire: function() { - if (this.__onChange) { - this.__onChange.call(this); - } - if (this.__onFinishChange) { - this.__onFinishChange.call(this, this.getValue()); - } - this.getValue().call(this.object); - } - } - - ); - - return FunctionController; - -})(dat.controllers.Controller, -dat.dom.dom, -dat.utils.common); - - -dat.controllers.BooleanController = (function (Controller, dom, common) { - - /** - * @class Provides a checkbox input to alter the boolean property of an object. - * @extends dat.controllers.Controller - * - * @param {Object} object The object to be manipulated - * @param {string} property The name of the property to be manipulated - * - * @member dat.controllers - */ - var BooleanController = function(object, property) { - - BooleanController.superclass.call(this, object, property); - - var _this = this; - this.__prev = this.getValue(); - - this.__checkbox = document.createElement('input'); - this.__checkbox.setAttribute('type', 'checkbox'); - - - dom.bind(this.__checkbox, 'change', onChange, false); - - this.domElement.appendChild(this.__checkbox); - - // Match original value - this.updateDisplay(); - - function onChange() { - _this.setValue(!_this.__prev); - } - - }; - - BooleanController.superclass = Controller; - - common.extend( - - BooleanController.prototype, - Controller.prototype, - - { - - setValue: function(v) { - var toReturn = BooleanController.superclass.prototype.setValue.call(this, v); - if (this.__onFinishChange) { - this.__onFinishChange.call(this, this.getValue()); - } - this.__prev = this.getValue(); - return toReturn; - }, - - updateDisplay: function() { - - if (this.getValue() === true) { - this.__checkbox.setAttribute('checked', 'checked'); - this.__checkbox.checked = true; - } else { - this.__checkbox.checked = false; - } - - return BooleanController.superclass.prototype.updateDisplay.call(this); - - } - - - } - - ); - - return BooleanController; - -})(dat.controllers.Controller, -dat.dom.dom, -dat.utils.common); - - -dat.color.toString = (function (common) { - - return function(color) { - - if (color.a == 1 || common.isUndefined(color.a)) { - - var s = color.hex.toString(16); - while (s.length < 6) { - s = '0' + s; - } - - return '#' + s; - - } else { - - return 'rgba(' + Math.round(color.r) + ',' + Math.round(color.g) + ',' + Math.round(color.b) + ',' + color.a + ')'; - - } - - } - -})(dat.utils.common); - - -dat.color.interpret = (function (toString, common) { - - var result, toReturn; - - var interpret = function() { - - toReturn = false; - - var original = arguments.length > 1 ? common.toArray(arguments) : arguments[0]; - - common.each(INTERPRETATIONS, function(family) { - - if (family.litmus(original)) { - - common.each(family.conversions, function(conversion, conversionName) { - - result = conversion.read(original); - - if (toReturn === false && result !== false) { - toReturn = result; - result.conversionName = conversionName; - result.conversion = conversion; - return common.BREAK; - - } - - }); - - return common.BREAK; - - } - - }); - - return toReturn; - - }; - - var INTERPRETATIONS = [ - - // Strings - { - - litmus: common.isString, - - conversions: { - - THREE_CHAR_HEX: { - - read: function(original) { - - var test = original.match(/^#([A-F0-9])([A-F0-9])([A-F0-9])$/i); - if (test === null) return false; - - return { - space: 'HEX', - hex: parseInt( - '0x' + - test[1].toString() + test[1].toString() + - test[2].toString() + test[2].toString() + - test[3].toString() + test[3].toString()) - }; - - }, - - write: toString - - }, - - SIX_CHAR_HEX: { - - read: function(original) { - - var test = original.match(/^#([A-F0-9]{6})$/i); - if (test === null) return false; - - return { - space: 'HEX', - hex: parseInt('0x' + test[1].toString()) - }; - - }, - - write: toString - - }, - - CSS_RGB: { - - read: function(original) { - - var test = original.match(/^rgb\(\s*(.+)\s*,\s*(.+)\s*,\s*(.+)\s*\)/); - if (test === null) return false; - - return { - space: 'RGB', - r: parseFloat(test[1]), - g: parseFloat(test[2]), - b: parseFloat(test[3]) - }; - - }, - - write: toString - - }, - - CSS_RGBA: { - - read: function(original) { - - var test = original.match(/^rgba\(\s*(.+)\s*,\s*(.+)\s*,\s*(.+)\s*\,\s*(.+)\s*\)/); - if (test === null) return false; - - return { - space: 'RGB', - r: parseFloat(test[1]), - g: parseFloat(test[2]), - b: parseFloat(test[3]), - a: parseFloat(test[4]) - }; - - }, - - write: toString - - } - - } - - }, - - // Numbers - { - - litmus: common.isNumber, - - conversions: { - - HEX: { - read: function(original) { - return { - space: 'HEX', - hex: original, - conversionName: 'HEX' - } - }, - - write: function(color) { - return color.hex; - } - } - - } - - }, - - // Arrays - { - - litmus: common.isArray, - - conversions: { - - RGB_ARRAY: { - read: function(original) { - if (original.length != 3) return false; - return { - space: 'RGB', - r: original[0], - g: original[1], - b: original[2] - }; - }, - - write: function(color) { - return [color.r, color.g, color.b]; - } - - }, - - RGBA_ARRAY: { - read: function(original) { - if (original.length != 4) return false; - return { - space: 'RGB', - r: original[0], - g: original[1], - b: original[2], - a: original[3] - }; - }, - - write: function(color) { - return [color.r, color.g, color.b, color.a]; - } - - } - - } - - }, - - // Objects - { - - litmus: common.isObject, - - conversions: { - - RGBA_OBJ: { - read: function(original) { - if (common.isNumber(original.r) && - common.isNumber(original.g) && - common.isNumber(original.b) && - common.isNumber(original.a)) { - return { - space: 'RGB', - r: original.r, - g: original.g, - b: original.b, - a: original.a - } - } - return false; - }, - - write: function(color) { - return { - r: color.r, - g: color.g, - b: color.b, - a: color.a - } - } - }, - - RGB_OBJ: { - read: function(original) { - if (common.isNumber(original.r) && - common.isNumber(original.g) && - common.isNumber(original.b)) { - return { - space: 'RGB', - r: original.r, - g: original.g, - b: original.b - } - } - return false; - }, - - write: function(color) { - return { - r: color.r, - g: color.g, - b: color.b - } - } - }, - - HSVA_OBJ: { - read: function(original) { - if (common.isNumber(original.h) && - common.isNumber(original.s) && - common.isNumber(original.v) && - common.isNumber(original.a)) { - return { - space: 'HSV', - h: original.h, - s: original.s, - v: original.v, - a: original.a - } - } - return false; - }, - - write: function(color) { - return { - h: color.h, - s: color.s, - v: color.v, - a: color.a - } - } - }, - - HSV_OBJ: { - read: function(original) { - if (common.isNumber(original.h) && - common.isNumber(original.s) && - common.isNumber(original.v)) { - return { - space: 'HSV', - h: original.h, - s: original.s, - v: original.v - } - } - return false; - }, - - write: function(color) { - return { - h: color.h, - s: color.s, - v: color.v - } - } - - } - - } - - } - - - ]; - - return interpret; - - -})(dat.color.toString, -dat.utils.common); - - -dat.GUI = dat.gui.GUI = (function (css, saveDialogueContents, styleSheet, controllerFactory, Controller, BooleanController, FunctionController, NumberControllerBox, NumberControllerSlider, OptionController, ColorController, requestAnimationFrame, CenteredDiv, dom, common) { - - css.inject(styleSheet); - - /** Outer-most className for GUI's */ - var CSS_NAMESPACE = 'dg'; - - var HIDE_KEY_CODE = 72; - - /** The only value shared between the JS and SCSS. Use caution. */ - var CLOSE_BUTTON_HEIGHT = 20; - - var DEFAULT_DEFAULT_PRESET_NAME = 'Default'; - - var SUPPORTS_LOCAL_STORAGE = (function() { - try { - return 'localStorage' in window && window['localStorage'] !== null; - } catch (e) { - return false; - } - })(); - - var SAVE_DIALOGUE; - - /** Have we yet to create an autoPlace GUI? */ - var auto_place_virgin = true; - - /** Fixed position div that auto place GUI's go inside */ - var auto_place_container; - - /** Are we hiding the GUI's ? */ - var hide = false; - - /** GUI's which should be hidden */ - var hideable_guis = []; - - /** - * A lightweight controller library for JavaScript. It allows you to easily - * manipulate variables and fire functions on the fly. - * @class - * - * @member dat.gui - * - * @param {Object} [params] - * @param {String} [params.name] The name of this GUI. - * @param {Object} [params.load] JSON object representing the saved state of - * this GUI. - * @param {Boolean} [params.auto=true] - * @param {dat.gui.GUI} [params.parent] The GUI I'm nested in. - * @param {Boolean} [params.closed] If true, starts closed - */ - var GUI = function(params) { - - var _this = this; - - /** - * Outermost DOM Element - * @type DOMElement - */ - this.domElement = document.createElement('div'); - this.__ul = document.createElement('ul'); - this.domElement.appendChild(this.__ul); - - dom.addClass(this.domElement, CSS_NAMESPACE); - - /** - * Nested GUI's by name - * @ignore - */ - this.__folders = {}; - - this.__controllers = []; - - /** - * List of objects I'm remembering for save, only used in top level GUI - * @ignore - */ - this.__rememberedObjects = []; - - /** - * Maps the index of remembered objects to a map of controllers, only used - * in top level GUI. - * - * @private - * @ignore - * - * @example - * [ - * { - * propertyName: Controller, - * anotherPropertyName: Controller - * }, - * { - * propertyName: Controller - * } - * ] - */ - this.__rememberedObjectIndecesToControllers = []; - - this.__listening = []; - - params = params || {}; - - // Default parameters - params = common.defaults(params, { - autoPlace: true, - width: GUI.DEFAULT_WIDTH - }); - - params = common.defaults(params, { - resizable: params.autoPlace, - hideable: params.autoPlace - }); - - - if (!common.isUndefined(params.load)) { - - // Explicit preset - if (params.preset) params.load.preset = params.preset; - - } else { - - params.load = { preset: DEFAULT_DEFAULT_PRESET_NAME }; - - } - - if (common.isUndefined(params.parent) && params.hideable) { - hideable_guis.push(this); - } - - // Only root level GUI's are resizable. - params.resizable = common.isUndefined(params.parent) && params.resizable; - - - if (params.autoPlace && common.isUndefined(params.scrollable)) { - params.scrollable = true; - } -// params.scrollable = common.isUndefined(params.parent) && params.scrollable === true; - - // Not part of params because I don't want people passing this in via - // constructor. Should be a 'remembered' value. - var use_local_storage = - SUPPORTS_LOCAL_STORAGE && - localStorage.getItem(getLocalStorageHash(this, 'isLocal')) === 'true'; - - Object.defineProperties(this, - - /** @lends dat.gui.GUI.prototype */ - { - - /** - * The parent GUI - * @type dat.gui.GUI - */ - parent: { - get: function() { - return params.parent; - } - }, - - scrollable: { - get: function() { - return params.scrollable; - } - }, - - /** - * Handles GUI's element placement for you - * @type Boolean - */ - autoPlace: { - get: function() { - return params.autoPlace; - } - }, - - /** - * The identifier for a set of saved values - * @type String - */ - preset: { - - get: function() { - if (_this.parent) { - return _this.getRoot().preset; - } else { - return params.load.preset; - } - }, - - set: function(v) { - if (_this.parent) { - _this.getRoot().preset = v; - } else { - params.load.preset = v; - } - setPresetSelectIndex(this); - _this.revert(); - } - - }, - - /** - * The width of GUI element - * @type Number - */ - width: { - get: function() { - return params.width; - }, - set: function(v) { - params.width = v; - setWidth(_this, v); - } - }, - - /** - * The name of GUI. Used for folders. i.e - * a folder's name - * @type String - */ - name: { - get: function() { - return params.name; - }, - set: function(v) { - // TODO Check for collisions among sibling folders - params.name = v; - if (title_row_name) { - title_row_name.innerHTML = params.name; - } - } - }, - - /** - * Whether the GUI is collapsed or not - * @type Boolean - */ - closed: { - get: function() { - return params.closed; - }, - set: function(v) { - params.closed = v; - if (params.closed) { - dom.addClass(_this.__ul, GUI.CLASS_CLOSED); - } else { - dom.removeClass(_this.__ul, GUI.CLASS_CLOSED); - } - // For browsers that aren't going to respect the CSS transition, - // Lets just check our height against the window height right off - // the bat. - this.onResize(); - - if (_this.__closeButton) { - _this.__closeButton.innerHTML = v ? GUI.TEXT_OPEN : GUI.TEXT_CLOSED; - } - } - }, - - /** - * Contains all presets - * @type Object - */ - load: { - get: function() { - return params.load; - } - }, - - /** - * Determines whether or not to use localStorage as the means for - * remembering - * @type Boolean - */ - useLocalStorage: { - - get: function() { - return use_local_storage; - }, - set: function(bool) { - if (SUPPORTS_LOCAL_STORAGE) { - use_local_storage = bool; - if (bool) { - dom.bind(window, 'unload', saveToLocalStorage); - } else { - dom.unbind(window, 'unload', saveToLocalStorage); - } - localStorage.setItem(getLocalStorageHash(_this, 'isLocal'), bool); - } - } - - } - - }); - - // Are we a root level GUI? - if (common.isUndefined(params.parent)) { - - params.closed = false; - - dom.addClass(this.domElement, GUI.CLASS_MAIN); - dom.makeSelectable(this.domElement, false); - - // Are we supposed to be loading locally? - if (SUPPORTS_LOCAL_STORAGE) { - - if (use_local_storage) { - - _this.useLocalStorage = true; - - var saved_gui = localStorage.getItem(getLocalStorageHash(this, 'gui')); - - if (saved_gui) { - params.load = JSON.parse(saved_gui); - } - - } - - } - - this.__closeButton = document.createElement('div'); - this.__closeButton.innerHTML = GUI.TEXT_CLOSED; - dom.addClass(this.__closeButton, GUI.CLASS_CLOSE_BUTTON); - this.domElement.appendChild(this.__closeButton); - - dom.bind(this.__closeButton, 'click', function() { - - _this.closed = !_this.closed; - - - }); - - - // Oh, you're a nested GUI! - } else { - - if (params.closed === undefined) { - params.closed = true; - } - - var title_row_name = document.createTextNode(params.name); - dom.addClass(title_row_name, 'controller-name'); - - var title_row = addRow(_this, title_row_name); - - var on_click_title = function(e) { - e.preventDefault(); - _this.closed = !_this.closed; - return false; - }; - - dom.addClass(this.__ul, GUI.CLASS_CLOSED); - - dom.addClass(title_row, 'title'); - dom.bind(title_row, 'click', on_click_title); - - if (!params.closed) { - this.closed = false; - } - - } - - if (params.autoPlace) { - - if (common.isUndefined(params.parent)) { - - if (auto_place_virgin) { - auto_place_container = document.createElement('div'); - dom.addClass(auto_place_container, CSS_NAMESPACE); - dom.addClass(auto_place_container, GUI.CLASS_AUTO_PLACE_CONTAINER); - document.body.appendChild(auto_place_container); - auto_place_virgin = false; - } - - // Put it in the dom for you. - auto_place_container.appendChild(this.domElement); - - // Apply the auto styles - dom.addClass(this.domElement, GUI.CLASS_AUTO_PLACE); - - } - - - // Make it not elastic. - if (!this.parent) setWidth(_this, params.width); - - } - - dom.bind(window, 'resize', function() { _this.onResize() }); - dom.bind(this.__ul, 'webkitTransitionEnd', function() { _this.onResize(); }); - dom.bind(this.__ul, 'transitionend', function() { _this.onResize() }); - dom.bind(this.__ul, 'oTransitionEnd', function() { _this.onResize() }); - this.onResize(); - - - if (params.resizable) { - addResizeHandle(this); - } - - function saveToLocalStorage() { - localStorage.setItem(getLocalStorageHash(_this, 'gui'), JSON.stringify(_this.getSaveObject())); - } - - var root = _this.getRoot(); - function resetWidth() { - var root = _this.getRoot(); - root.width += 1; - common.defer(function() { - root.width -= 1; - }); - } - - if (!params.parent) { - resetWidth(); - } - - }; - - GUI.toggleHide = function() { - - hide = !hide; - common.each(hideable_guis, function(gui) { - gui.domElement.style.zIndex = hide ? -999 : 999; - gui.domElement.style.opacity = hide ? 0 : 1; - }); - }; - - GUI.CLASS_AUTO_PLACE = 'a'; - GUI.CLASS_AUTO_PLACE_CONTAINER = 'ac'; - GUI.CLASS_MAIN = 'main'; - GUI.CLASS_CONTROLLER_ROW = 'cr'; - GUI.CLASS_TOO_TALL = 'taller-than-window'; - GUI.CLASS_CLOSED = 'closed'; - GUI.CLASS_CLOSE_BUTTON = 'close-button'; - GUI.CLASS_DRAG = 'drag'; - - GUI.DEFAULT_WIDTH = 245; - GUI.TEXT_CLOSED = 'Close Controls'; - GUI.TEXT_OPEN = 'Open Controls'; - - dom.bind(window, 'keydown', function(e) { - - if (document.activeElement.type !== 'text' && - (e.which === HIDE_KEY_CODE || e.keyCode == HIDE_KEY_CODE)) { - GUI.toggleHide(); - } - - }, false); - - common.extend( - - GUI.prototype, - - /** @lends dat.gui.GUI */ - { - - /** - * @param object - * @param property - * @returns {dat.controllers.Controller} The new controller that was added. - * @instance - */ - add: function(object, property) { - - return add( - this, - object, - property, - { - factoryArgs: Array.prototype.slice.call(arguments, 2) - } - ); - - }, - - /** - * @param object - * @param property - * @returns {dat.controllers.ColorController} The new controller that was added. - * @instance - */ - addColor: function(object, property) { - - return add( - this, - object, - property, - { - color: true - } - ); - - }, - - /** - * @param controller - * @instance - */ - remove: function(controller) { - - // TODO listening? - this.__ul.removeChild(controller.__li); - this.__controllers.slice(this.__controllers.indexOf(controller), 1); - var _this = this; - common.defer(function() { - _this.onResize(); - }); - - }, - - destroy: function() { - - if (this.autoPlace) { - auto_place_container.removeChild(this.domElement); - } - - }, - - /** - * @param name - * @returns {dat.gui.GUI} The new folder. - * @throws {Error} if this GUI already has a folder by the specified - * name - * @instance - */ - addFolder: function(name) { - - // We have to prevent collisions on names in order to have a key - // by which to remember saved values - if (this.__folders[name] !== undefined) { - throw new Error('You already have a folder in this GUI by the' + - ' name "' + name + '"'); - } - - var new_gui_params = { name: name, parent: this }; - - // We need to pass down the autoPlace trait so that we can - // attach event listeners to open/close folder actions to - // ensure that a scrollbar appears if the window is too short. - new_gui_params.autoPlace = this.autoPlace; - - // Do we have saved appearance data for this folder? - - if (this.load && // Anything loaded? - this.load.folders && // Was my parent a dead-end? - this.load.folders[name]) { // Did daddy remember me? - - // Start me closed if I was closed - new_gui_params.closed = this.load.folders[name].closed; - - // Pass down the loaded data - new_gui_params.load = this.load.folders[name]; - - } - - var gui = new GUI(new_gui_params); - this.__folders[name] = gui; - - var li = addRow(this, gui.domElement); - dom.addClass(li, 'folder'); - return gui; - - }, - - open: function() { - this.closed = false; - }, - - close: function() { - this.closed = true; - }, - - onResize: function() { - - var root = this.getRoot(); - - if (root.scrollable) { - - var top = dom.getOffset(root.__ul).top; - var h = 0; - - common.each(root.__ul.childNodes, function(node) { - if (! (root.autoPlace && node === root.__save_row)) - h += dom.getHeight(node); - }); - - if (window.innerHeight - top - CLOSE_BUTTON_HEIGHT < h) { - dom.addClass(root.domElement, GUI.CLASS_TOO_TALL); - root.__ul.style.height = window.innerHeight - top - CLOSE_BUTTON_HEIGHT + 'px'; - } else { - dom.removeClass(root.domElement, GUI.CLASS_TOO_TALL); - root.__ul.style.height = 'auto'; - } - - } - - if (root.__resize_handle) { - common.defer(function() { - root.__resize_handle.style.height = root.__ul.offsetHeight + 'px'; - }); - } - - if (root.__closeButton) { - root.__closeButton.style.width = root.width + 'px'; - } - - }, - - /** - * Mark objects for saving. The order of these objects cannot change as - * the GUI grows. When remembering new objects, append them to the end - * of the list. - * - * @param {Object...} objects - * @throws {Error} if not called on a top level GUI. - * @instance - */ - remember: function() { - - if (common.isUndefined(SAVE_DIALOGUE)) { - SAVE_DIALOGUE = new CenteredDiv(); - SAVE_DIALOGUE.domElement.innerHTML = saveDialogueContents; - } - - if (this.parent) { - throw new Error("You can only call remember on a top level GUI."); - } - - var _this = this; - - common.each(Array.prototype.slice.call(arguments), function(object) { - if (_this.__rememberedObjects.length == 0) { - addSaveMenu(_this); - } - if (_this.__rememberedObjects.indexOf(object) == -1) { - _this.__rememberedObjects.push(object); - } - }); - - if (this.autoPlace) { - // Set save row width - setWidth(this, this.width); - } - - }, - - /** - * @returns {dat.gui.GUI} the topmost parent GUI of a nested GUI. - * @instance - */ - getRoot: function() { - var gui = this; - while (gui.parent) { - gui = gui.parent; - } - return gui; - }, - - /** - * @returns {Object} a JSON object representing the current state of - * this GUI as well as its remembered properties. - * @instance - */ - getSaveObject: function() { - - var toReturn = this.load; - - toReturn.closed = this.closed; - - // Am I remembering any values? - if (this.__rememberedObjects.length > 0) { - - toReturn.preset = this.preset; - - if (!toReturn.remembered) { - toReturn.remembered = {}; - } - - toReturn.remembered[this.preset] = getCurrentPreset(this); - - } - - toReturn.folders = {}; - common.each(this.__folders, function(element, key) { - toReturn.folders[key] = element.getSaveObject(); - }); - - return toReturn; - - }, - - save: function() { - - if (!this.load.remembered) { - this.load.remembered = {}; - } - - this.load.remembered[this.preset] = getCurrentPreset(this); - markPresetModified(this, false); - - }, - - saveAs: function(presetName) { - - if (!this.load.remembered) { - - // Retain default values upon first save - this.load.remembered = {}; - this.load.remembered[DEFAULT_DEFAULT_PRESET_NAME] = getCurrentPreset(this, true); - - } - - this.load.remembered[presetName] = getCurrentPreset(this); - this.preset = presetName; - addPresetOption(this, presetName, true); - - }, - - revert: function(gui) { - - common.each(this.__controllers, function(controller) { - // Make revert work on Default. - if (!this.getRoot().load.remembered) { - controller.setValue(controller.initialValue); - } else { - recallSavedValue(gui || this.getRoot(), controller); - } - }, this); - - common.each(this.__folders, function(folder) { - folder.revert(folder); - }); - - if (!gui) { - markPresetModified(this.getRoot(), false); - } - - - }, - - listen: function(controller) { - - var init = this.__listening.length == 0; - this.__listening.push(controller); - if (init) updateDisplays(this.__listening); - - } - - } - - ); - - function add(gui, object, property, params) { - - if (object[property] === undefined) { - throw new Error("Object " + object + " has no property \"" + property + "\""); - } - - var controller; - - if (params.color) { - - controller = new ColorController(object, property); - - } else { - - var factoryArgs = [object,property].concat(params.factoryArgs); - controller = controllerFactory.apply(gui, factoryArgs); - - } - - if (params.before instanceof Controller) { - params.before = params.before.__li; - } - - recallSavedValue(gui, controller); - - dom.addClass(controller.domElement, 'c'); - - var name = document.createElement('span'); - dom.addClass(name, 'property-name'); - name.innerHTML = controller.property; - - var container = document.createElement('div'); - container.appendChild(name); - container.appendChild(controller.domElement); - - var li = addRow(gui, container, params.before); - - dom.addClass(li, GUI.CLASS_CONTROLLER_ROW); - dom.addClass(li, typeof controller.getValue()); - - augmentController(gui, li, controller); - - gui.__controllers.push(controller); - - return controller; - - } - - /** - * Add a row to the end of the GUI or before another row. - * - * @param gui - * @param [dom] If specified, inserts the dom content in the new row - * @param [liBefore] If specified, places the new row before another row - */ - function addRow(gui, dom, liBefore) { - var li = document.createElement('li'); - if (dom) li.appendChild(dom); - if (liBefore) { - gui.__ul.insertBefore(li, params.before); - } else { - gui.__ul.appendChild(li); - } - gui.onResize(); - return li; - } - - function augmentController(gui, li, controller) { - - controller.__li = li; - controller.__gui = gui; - - common.extend(controller, { - - options: function(options) { - - if (arguments.length > 1) { - controller.remove(); - - return add( - gui, - controller.object, - controller.property, - { - before: controller.__li.nextElementSibling, - factoryArgs: [common.toArray(arguments)] - } - ); - - } - - if (common.isArray(options) || common.isObject(options)) { - controller.remove(); - - return add( - gui, - controller.object, - controller.property, - { - before: controller.__li.nextElementSibling, - factoryArgs: [options] - } - ); - - } - - }, - - name: function(v) { - controller.__li.firstElementChild.firstElementChild.innerHTML = v; - return controller; - }, - - listen: function() { - controller.__gui.listen(controller); - return controller; - }, - - remove: function() { - controller.__gui.remove(controller); - return controller; - } - - }); - - // All sliders should be accompanied by a box. - if (controller instanceof NumberControllerSlider) { - - var box = new NumberControllerBox(controller.object, controller.property, - { min: controller.__min, max: controller.__max, step: controller.__step }); - - common.each(['updateDisplay', 'onChange', 'onFinishChange'], function(method) { - var pc = controller[method]; - var pb = box[method]; - controller[method] = box[method] = function() { - var args = Array.prototype.slice.call(arguments); - pc.apply(controller, args); - return pb.apply(box, args); - } - }); - - dom.addClass(li, 'has-slider'); - controller.domElement.insertBefore(box.domElement, controller.domElement.firstElementChild); - - } - else if (controller instanceof NumberControllerBox) { - - var r = function(returned) { - - // Have we defined both boundaries? - if (common.isNumber(controller.__min) && common.isNumber(controller.__max)) { - - // Well, then lets just replace this with a slider. - controller.remove(); - return add( - gui, - controller.object, - controller.property, - { - before: controller.__li.nextElementSibling, - factoryArgs: [controller.__min, controller.__max, controller.__step] - }); - - } - - return returned; - - }; - - controller.min = common.compose(r, controller.min); - controller.max = common.compose(r, controller.max); - - } - else if (controller instanceof BooleanController) { - - dom.bind(li, 'click', function() { - dom.fakeEvent(controller.__checkbox, 'click'); - }); - - dom.bind(controller.__checkbox, 'click', function(e) { - e.stopPropagation(); // Prevents double-toggle - }) - - } - else if (controller instanceof FunctionController) { - - dom.bind(li, 'click', function() { - dom.fakeEvent(controller.__button, 'click'); - }); - - dom.bind(li, 'mouseover', function() { - dom.addClass(controller.__button, 'hover'); - }); - - dom.bind(li, 'mouseout', function() { - dom.removeClass(controller.__button, 'hover'); - }); - - } - else if (controller instanceof ColorController) { - - dom.addClass(li, 'color'); - controller.updateDisplay = common.compose(function(r) { - li.style.borderLeftColor = controller.__color.toString(); - return r; - }, controller.updateDisplay); - - controller.updateDisplay(); - - } - - controller.setValue = common.compose(function(r) { - if (gui.getRoot().__preset_select && controller.isModified()) { - markPresetModified(gui.getRoot(), true); - } - return r; - }, controller.setValue); - - } - - function recallSavedValue(gui, controller) { - - // Find the topmost GUI, that's where remembered objects live. - var root = gui.getRoot(); - - // Does the object we're controlling match anything we've been told to - // remember? - var matched_index = root.__rememberedObjects.indexOf(controller.object); - - // Why yes, it does! - if (matched_index != -1) { - - // Let me fetch a map of controllers for thcommon.isObject. - var controller_map = - root.__rememberedObjectIndecesToControllers[matched_index]; - - // Ohp, I believe this is the first controller we've created for this - // object. Lets make the map fresh. - if (controller_map === undefined) { - controller_map = {}; - root.__rememberedObjectIndecesToControllers[matched_index] = - controller_map; - } - - // Keep track of this controller - controller_map[controller.property] = controller; - - // Okay, now have we saved any values for this controller? - if (root.load && root.load.remembered) { - - var preset_map = root.load.remembered; - - // Which preset are we trying to load? - var preset; - - if (preset_map[gui.preset]) { - - preset = preset_map[gui.preset]; - - } else if (preset_map[DEFAULT_DEFAULT_PRESET_NAME]) { - - // Uhh, you can have the default instead? - preset = preset_map[DEFAULT_DEFAULT_PRESET_NAME]; - - } else { - - // Nada. - - return; - - } - - - // Did the loaded object remember thcommon.isObject? - if (preset[matched_index] && - - // Did we remember this particular property? - preset[matched_index][controller.property] !== undefined) { - - // We did remember something for this guy ... - var value = preset[matched_index][controller.property]; - - // And that's what it is. - controller.initialValue = value; - controller.setValue(value); - - } - - } - - } - - } - - function getLocalStorageHash(gui, key) { - // TODO how does this deal with multiple GUI's? - return document.location.href + '.' + key; - - } - - function addSaveMenu(gui) { - - var div = gui.__save_row = document.createElement('li'); - - dom.addClass(gui.domElement, 'has-save'); - - gui.__ul.insertBefore(div, gui.__ul.firstChild); - - dom.addClass(div, 'save-row'); - - var gears = document.createElement('span'); - gears.innerHTML = ' '; - dom.addClass(gears, 'button gears'); - - // TODO replace with FunctionController - var button = document.createElement('span'); - button.innerHTML = 'Save'; - dom.addClass(button, 'button'); - dom.addClass(button, 'save'); - - var button2 = document.createElement('span'); - button2.innerHTML = 'New'; - dom.addClass(button2, 'button'); - dom.addClass(button2, 'save-as'); - - var button3 = document.createElement('span'); - button3.innerHTML = 'Revert'; - dom.addClass(button3, 'button'); - dom.addClass(button3, 'revert'); - - var select = gui.__preset_select = document.createElement('select'); - - if (gui.load && gui.load.remembered) { - - common.each(gui.load.remembered, function(value, key) { - addPresetOption(gui, key, key == gui.preset); - }); - - } else { - addPresetOption(gui, DEFAULT_DEFAULT_PRESET_NAME, false); - } - - dom.bind(select, 'change', function() { - - - for (var index = 0; index < gui.__preset_select.length; index++) { - gui.__preset_select[index].innerHTML = gui.__preset_select[index].value; - } - - gui.preset = this.value; - - }); - - div.appendChild(select); - div.appendChild(gears); - div.appendChild(button); - div.appendChild(button2); - div.appendChild(button3); - - if (SUPPORTS_LOCAL_STORAGE) { - - var saveLocally = document.getElementById('dg-save-locally'); - var explain = document.getElementById('dg-local-explain'); - - saveLocally.style.display = 'block'; - - var localStorageCheckBox = document.getElementById('dg-local-storage'); - - if (localStorage.getItem(getLocalStorageHash(gui, 'isLocal')) === 'true') { - localStorageCheckBox.setAttribute('checked', 'checked'); - } - - function showHideExplain() { - explain.style.display = gui.useLocalStorage ? 'block' : 'none'; - } - - showHideExplain(); - - // TODO: Use a boolean controller, fool! - dom.bind(localStorageCheckBox, 'change', function() { - gui.useLocalStorage = !gui.useLocalStorage; - showHideExplain(); - }); - - } - - var newConstructorTextArea = document.getElementById('dg-new-constructor'); - - dom.bind(newConstructorTextArea, 'keydown', function(e) { - if (e.metaKey && (e.which === 67 || e.keyCode == 67)) { - SAVE_DIALOGUE.hide(); - } - }); - - dom.bind(gears, 'click', function() { - newConstructorTextArea.innerHTML = JSON.stringify(gui.getSaveObject(), undefined, 2); - SAVE_DIALOGUE.show(); - newConstructorTextArea.focus(); - newConstructorTextArea.select(); - }); - - dom.bind(button, 'click', function() { - gui.save(); - }); - - dom.bind(button2, 'click', function() { - var presetName = prompt('Enter a new preset name.'); - if (presetName) gui.saveAs(presetName); - }); - - dom.bind(button3, 'click', function() { - gui.revert(); - }); - -// div.appendChild(button2); - - } - - function addResizeHandle(gui) { - - gui.__resize_handle = document.createElement('div'); - - common.extend(gui.__resize_handle.style, { - - width: '6px', - marginLeft: '-3px', - height: '200px', - cursor: 'ew-resize', - position: 'absolute' -// border: '1px solid blue' - - }); - - var pmouseX; - - dom.bind(gui.__resize_handle, 'mousedown', dragStart); - dom.bind(gui.__closeButton, 'mousedown', dragStart); - - gui.domElement.insertBefore(gui.__resize_handle, gui.domElement.firstElementChild); - - function dragStart(e) { - - e.preventDefault(); - - pmouseX = e.clientX; - - dom.addClass(gui.__closeButton, GUI.CLASS_DRAG); - dom.bind(window, 'mousemove', drag); - dom.bind(window, 'mouseup', dragStop); - - return false; - - } - - function drag(e) { - - e.preventDefault(); - - gui.width += pmouseX - e.clientX; - gui.onResize(); - pmouseX = e.clientX; - - return false; - - } - - function dragStop() { - - dom.removeClass(gui.__closeButton, GUI.CLASS_DRAG); - dom.unbind(window, 'mousemove', drag); - dom.unbind(window, 'mouseup', dragStop); - - } - - } - - function setWidth(gui, w) { - gui.domElement.style.width = w + 'px'; - // Auto placed save-rows are position fixed, so we have to - // set the width manually if we want it to bleed to the edge - if (gui.__save_row && gui.autoPlace) { - gui.__save_row.style.width = w + 'px'; - }if (gui.__closeButton) { - gui.__closeButton.style.width = w + 'px'; - } - } - - function getCurrentPreset(gui, useInitialValues) { - - var toReturn = {}; - - // For each object I'm remembering - common.each(gui.__rememberedObjects, function(val, index) { - - var saved_values = {}; - - // The controllers I've made for thcommon.isObject by property - var controller_map = - gui.__rememberedObjectIndecesToControllers[index]; - - // Remember each value for each property - common.each(controller_map, function(controller, property) { - saved_values[property] = useInitialValues ? controller.initialValue : controller.getValue(); - }); - - // Save the values for thcommon.isObject - toReturn[index] = saved_values; - - }); - - return toReturn; - - } - - function addPresetOption(gui, name, setSelected) { - var opt = document.createElement('option'); - opt.innerHTML = name; - opt.value = name; - gui.__preset_select.appendChild(opt); - if (setSelected) { - gui.__preset_select.selectedIndex = gui.__preset_select.length - 1; - } - } - - function setPresetSelectIndex(gui) { - for (var index = 0; index < gui.__preset_select.length; index++) { - if (gui.__preset_select[index].value == gui.preset) { - gui.__preset_select.selectedIndex = index; - } - } - } - - function markPresetModified(gui, modified) { - var opt = gui.__preset_select[gui.__preset_select.selectedIndex]; -// console.log('mark', modified, opt); - if (modified) { - opt.innerHTML = opt.value + "*"; - } else { - opt.innerHTML = opt.value; - } - } - - function updateDisplays(controllerArray) { - - - if (controllerArray.length != 0) { - - requestAnimationFrame(function() { - updateDisplays(controllerArray); - }); - - } - - common.each(controllerArray, function(c) { - c.updateDisplay(); - }); - - } - - return GUI; - -})(dat.utils.css, -"
\n\n Here's the new load parameter for your GUI's constructor:\n\n \n\n
\n\n Automatically save\n values to localStorage on exit.\n\n
The values saved to localStorage will\n override those passed to dat.GUI's constructor. This makes it\n easier to work incrementally, but localStorage is fragile,\n and your friends may not see the same values you do.\n \n
\n \n
\n\n
", -".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", -dat.controllers.factory = (function (OptionController, NumberControllerBox, NumberControllerSlider, StringController, FunctionController, BooleanController, common) { - - return function(object, property) { - - var initialValue = object[property]; - - // Providing options? - if (common.isArray(arguments[2]) || common.isObject(arguments[2])) { - return new OptionController(object, property, arguments[2]); - } - - // Providing a map? - - if (common.isNumber(initialValue)) { - - if (common.isNumber(arguments[2]) && common.isNumber(arguments[3])) { - - // Has min and max. - return new NumberControllerSlider(object, property, arguments[2], arguments[3]); - - } else { - - return new NumberControllerBox(object, property, { min: arguments[2], max: arguments[3] }); - - } - - } - - if (common.isString(initialValue)) { - return new StringController(object, property); - } - - if (common.isFunction(initialValue)) { - return new FunctionController(object, property, ''); - } - - if (common.isBoolean(initialValue)) { - return new BooleanController(object, property); - } - - } - - })(dat.controllers.OptionController, -dat.controllers.NumberControllerBox, -dat.controllers.NumberControllerSlider, -dat.controllers.StringController = (function (Controller, dom, common) { - - /** - * @class Provides a text input to alter the string property of an object. - * - * @extends dat.controllers.Controller - * - * @param {Object} object The object to be manipulated - * @param {string} property The name of the property to be manipulated - * - * @member dat.controllers - */ - var StringController = function(object, property) { - - StringController.superclass.call(this, object, property); - - var _this = this; - - this.__input = document.createElement('input'); - this.__input.setAttribute('type', 'text'); - - dom.bind(this.__input, 'keyup', onChange); - dom.bind(this.__input, 'change', onChange); - dom.bind(this.__input, 'blur', onBlur); - dom.bind(this.__input, 'keydown', function(e) { - if (e.keyCode === 13) { - this.blur(); - } - }); - - - function onChange() { - _this.setValue(_this.__input.value); - } - - function onBlur() { - if (_this.__onFinishChange) { - _this.__onFinishChange.call(_this, _this.getValue()); - } - } - - this.updateDisplay(); - - this.domElement.appendChild(this.__input); - - }; - - StringController.superclass = Controller; - - common.extend( - - StringController.prototype, - Controller.prototype, - - { - - updateDisplay: function() { - // Stops the caret from moving on account of: - // keyup -> setValue -> updateDisplay - if (!dom.isActive(this.__input)) { - this.__input.value = this.getValue(); - } - return StringController.superclass.prototype.updateDisplay.call(this); - } - - } - - ); - - return StringController; - -})(dat.controllers.Controller, -dat.dom.dom, -dat.utils.common), -dat.controllers.FunctionController, -dat.controllers.BooleanController, -dat.utils.common), -dat.controllers.Controller, -dat.controllers.BooleanController, -dat.controllers.FunctionController, -dat.controllers.NumberControllerBox, -dat.controllers.NumberControllerSlider, -dat.controllers.OptionController, -dat.controllers.ColorController = (function (Controller, dom, Color, interpret, common) { - - var ColorController = function(object, property) { - - ColorController.superclass.call(this, object, property); - - this.__color = new Color(this.getValue()); - this.__temp = new Color(0); - - var _this = this; - - this.domElement = document.createElement('div'); - - dom.makeSelectable(this.domElement, false); - - this.__selector = document.createElement('div'); - this.__selector.className = 'selector'; - - this.__saturation_field = document.createElement('div'); - this.__saturation_field.className = 'saturation-field'; - - this.__field_knob = document.createElement('div'); - this.__field_knob.className = 'field-knob'; - this.__field_knob_border = '2px solid '; - - this.__hue_knob = document.createElement('div'); - this.__hue_knob.className = 'hue-knob'; - - this.__hue_field = document.createElement('div'); - this.__hue_field.className = 'hue-field'; - - this.__input = document.createElement('input'); - this.__input.type = 'text'; - this.__input_textShadow = '0 1px 1px '; - - dom.bind(this.__input, 'keydown', function(e) { - if (e.keyCode === 13) { // on enter - onBlur.call(this); - } - }); - - dom.bind(this.__input, 'blur', onBlur); - - dom.bind(this.__selector, 'mousedown', function(e) { - - dom - .addClass(this, 'drag') - .bind(window, 'mouseup', function(e) { - dom.removeClass(_this.__selector, 'drag'); - }); - - }); - - var value_field = document.createElement('div'); - - common.extend(this.__selector.style, { - width: '122px', - height: '102px', - padding: '3px', - backgroundColor: '#222', - boxShadow: '0px 1px 3px rgba(0,0,0,0.3)' - }); - - common.extend(this.__field_knob.style, { - position: 'absolute', - width: '12px', - height: '12px', - border: this.__field_knob_border + (this.__color.v < .5 ? '#fff' : '#000'), - boxShadow: '0px 1px 3px rgba(0,0,0,0.5)', - borderRadius: '12px', - zIndex: 1 - }); - - common.extend(this.__hue_knob.style, { - position: 'absolute', - width: '15px', - height: '2px', - borderRight: '4px solid #fff', - zIndex: 1 - }); - - common.extend(this.__saturation_field.style, { - width: '100px', - height: '100px', - border: '1px solid #555', - marginRight: '3px', - display: 'inline-block', - cursor: 'pointer' - }); - - common.extend(value_field.style, { - width: '100%', - height: '100%', - background: 'none' - }); - - linearGradient(value_field, 'top', 'rgba(0,0,0,0)', '#000'); - - common.extend(this.__hue_field.style, { - width: '15px', - height: '100px', - display: 'inline-block', - border: '1px solid #555', - cursor: 'ns-resize' - }); - - hueGradient(this.__hue_field); - - common.extend(this.__input.style, { - outline: 'none', -// width: '120px', - textAlign: 'center', -// padding: '4px', -// marginBottom: '6px', - color: '#fff', - border: 0, - fontWeight: 'bold', - textShadow: this.__input_textShadow + 'rgba(0,0,0,0.7)' - }); - - dom.bind(this.__saturation_field, 'mousedown', fieldDown); - dom.bind(this.__field_knob, 'mousedown', fieldDown); - - dom.bind(this.__hue_field, 'mousedown', function(e) { - setH(e); - dom.bind(window, 'mousemove', setH); - dom.bind(window, 'mouseup', unbindH); - }); - - function fieldDown(e) { - setSV(e); - // document.body.style.cursor = 'none'; - dom.bind(window, 'mousemove', setSV); - dom.bind(window, 'mouseup', unbindSV); - } - - function unbindSV() { - dom.unbind(window, 'mousemove', setSV); - dom.unbind(window, 'mouseup', unbindSV); - // document.body.style.cursor = 'default'; - } - - function onBlur() { - var i = interpret(this.value); - if (i !== false) { - _this.__color.__state = i; - _this.setValue(_this.__color.toOriginal()); - } else { - this.value = _this.__color.toString(); - } - } - - function unbindH() { - dom.unbind(window, 'mousemove', setH); - dom.unbind(window, 'mouseup', unbindH); - } - - this.__saturation_field.appendChild(value_field); - this.__selector.appendChild(this.__field_knob); - this.__selector.appendChild(this.__saturation_field); - this.__selector.appendChild(this.__hue_field); - this.__hue_field.appendChild(this.__hue_knob); - - this.domElement.appendChild(this.__input); - this.domElement.appendChild(this.__selector); - - this.updateDisplay(); - - function setSV(e) { - - e.preventDefault(); - - var w = dom.getWidth(_this.__saturation_field); - var o = dom.getOffset(_this.__saturation_field); - var s = (e.clientX - o.left + document.body.scrollLeft) / w; - var v = 1 - (e.clientY - o.top + document.body.scrollTop) / w; - - if (v > 1) v = 1; - else if (v < 0) v = 0; - - if (s > 1) s = 1; - else if (s < 0) s = 0; - - _this.__color.v = v; - _this.__color.s = s; - - _this.setValue(_this.__color.toOriginal()); - - - return false; - - } - - function setH(e) { - - e.preventDefault(); - - var s = dom.getHeight(_this.__hue_field); - var o = dom.getOffset(_this.__hue_field); - var h = 1 - (e.clientY - o.top + document.body.scrollTop) / s; - - if (h > 1) h = 1; - else if (h < 0) h = 0; - - _this.__color.h = h * 360; - - _this.setValue(_this.__color.toOriginal()); - - return false; - - } - - }; - - ColorController.superclass = Controller; - - common.extend( - - ColorController.prototype, - Controller.prototype, - - { - - updateDisplay: function() { - - var i = interpret(this.getValue()); - - if (i !== false) { - - var mismatch = false; - - // Check for mismatch on the interpreted value. - - common.each(Color.COMPONENTS, function(component) { - if (!common.isUndefined(i[component]) && - !common.isUndefined(this.__color.__state[component]) && - i[component] !== this.__color.__state[component]) { - mismatch = true; - return {}; // break - } - }, this); - - // If nothing diverges, we keep our previous values - // for statefulness, otherwise we recalculate fresh - if (mismatch) { - common.extend(this.__color.__state, i); - } - - } - - common.extend(this.__temp.__state, this.__color.__state); - - this.__temp.a = 1; - - var flip = (this.__color.v < .5 || this.__color.s > .5) ? 255 : 0; - var _flip = 255 - flip; - - common.extend(this.__field_knob.style, { - marginLeft: 100 * this.__color.s - 7 + 'px', - marginTop: 100 * (1 - this.__color.v) - 7 + 'px', - backgroundColor: this.__temp.toString(), - border: this.__field_knob_border + 'rgb(' + flip + ',' + flip + ',' + flip +')' - }); - - this.__hue_knob.style.marginTop = (1 - this.__color.h / 360) * 100 + 'px' - - this.__temp.s = 1; - this.__temp.v = 1; - - linearGradient(this.__saturation_field, 'left', '#fff', this.__temp.toString()); - - common.extend(this.__input.style, { - backgroundColor: this.__input.value = this.__color.toString(), - color: 'rgb(' + flip + ',' + flip + ',' + flip +')', - textShadow: this.__input_textShadow + 'rgba(' + _flip + ',' + _flip + ',' + _flip +',.7)' - }); - - } - - } - - ); - - var vendors = ['-moz-','-o-','-webkit-','-ms-','']; - - function linearGradient(elem, x, a, b) { - elem.style.background = ''; - common.each(vendors, function(vendor) { - elem.style.cssText += 'background: ' + vendor + 'linear-gradient('+x+', '+a+' 0%, ' + b + ' 100%); '; - }); - } - - function hueGradient(elem) { - elem.style.background = ''; - elem.style.cssText += 'background: -moz-linear-gradient(top, #ff0000 0%, #ff00ff 17%, #0000ff 34%, #00ffff 50%, #00ff00 67%, #ffff00 84%, #ff0000 100%);' - elem.style.cssText += 'background: -webkit-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);' - elem.style.cssText += 'background: -o-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);' - elem.style.cssText += 'background: -ms-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);' - elem.style.cssText += 'background: linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);' - } - - - return ColorController; - -})(dat.controllers.Controller, -dat.dom.dom, -dat.color.Color = (function (interpret, math, toString, common) { - - var Color = function() { - - this.__state = interpret.apply(this, arguments); - - if (this.__state === false) { - throw 'Failed to interpret color arguments'; - } - - this.__state.a = this.__state.a || 1; - - - }; - - Color.COMPONENTS = ['r','g','b','h','s','v','hex','a']; - - common.extend(Color.prototype, { - - toString: function() { - return toString(this); - }, - - toOriginal: function() { - return this.__state.conversion.write(this); - } - - }); - - defineRGBComponent(Color.prototype, 'r', 2); - defineRGBComponent(Color.prototype, 'g', 1); - defineRGBComponent(Color.prototype, 'b', 0); - - defineHSVComponent(Color.prototype, 'h'); - defineHSVComponent(Color.prototype, 's'); - defineHSVComponent(Color.prototype, 'v'); - - Object.defineProperty(Color.prototype, 'a', { - - get: function() { - return this.__state.a; - }, - - set: function(v) { - this.__state.a = v; - } - - }); - - Object.defineProperty(Color.prototype, 'hex', { - - get: function() { - - if (!this.__state.space !== 'HEX') { - this.__state.hex = math.rgb_to_hex(this.r, this.g, this.b); - } - - return this.__state.hex; - - }, - - set: function(v) { - - this.__state.space = 'HEX'; - this.__state.hex = v; - - } - - }); - - function defineRGBComponent(target, component, componentHexIndex) { - - Object.defineProperty(target, component, { - - get: function() { - - if (this.__state.space === 'RGB') { - return this.__state[component]; - } - - recalculateRGB(this, component, componentHexIndex); - - return this.__state[component]; - - }, - - set: function(v) { - - if (this.__state.space !== 'RGB') { - recalculateRGB(this, component, componentHexIndex); - this.__state.space = 'RGB'; - } - - this.__state[component] = v; - - } - - }); - - } - - function defineHSVComponent(target, component) { - - Object.defineProperty(target, component, { - - get: function() { - - if (this.__state.space === 'HSV') - return this.__state[component]; - - recalculateHSV(this); - - return this.__state[component]; - - }, - - set: function(v) { - - if (this.__state.space !== 'HSV') { - recalculateHSV(this); - this.__state.space = 'HSV'; - } - - this.__state[component] = v; - - } - - }); - - } - - function recalculateRGB(color, component, componentHexIndex) { - - if (color.__state.space === 'HEX') { - - color.__state[component] = math.component_from_hex(color.__state.hex, componentHexIndex); - - } else if (color.__state.space === 'HSV') { - - common.extend(color.__state, math.hsv_to_rgb(color.__state.h, color.__state.s, color.__state.v)); - - } else { - - throw 'Corrupted color state'; - - } - - } - - function recalculateHSV(color) { - - var result = math.rgb_to_hsv(color.r, color.g, color.b); - - common.extend(color.__state, - { - s: result.s, - v: result.v - } - ); - - if (!common.isNaN(result.h)) { - color.__state.h = result.h; - } else if (common.isUndefined(color.__state.h)) { - color.__state.h = 0; - } - - } - - return Color; - -})(dat.color.interpret, -dat.color.math = (function () { - - var tmpComponent; - - return { - - hsv_to_rgb: function(h, s, v) { - - var hi = Math.floor(h / 60) % 6; - - var f = h / 60 - Math.floor(h / 60); - var p = v * (1.0 - s); - var q = v * (1.0 - (f * s)); - var t = v * (1.0 - ((1.0 - f) * s)); - var c = [ - [v, t, p], - [q, v, p], - [p, v, t], - [p, q, v], - [t, p, v], - [v, p, q] - ][hi]; - - return { - r: c[0] * 255, - g: c[1] * 255, - b: c[2] * 255 - }; - - }, - - rgb_to_hsv: function(r, g, b) { - - var min = Math.min(r, g, b), - max = Math.max(r, g, b), - delta = max - min, - h, s; - - if (max != 0) { - s = delta / max; - } else { - return { - h: NaN, - s: 0, - v: 0 - }; - } - - if (r == max) { - h = (g - b) / delta; - } else if (g == max) { - h = 2 + (b - r) / delta; - } else { - h = 4 + (r - g) / delta; - } - h /= 6; - if (h < 0) { - h += 1; - } - - return { - h: h * 360, - s: s, - v: max / 255 - }; - }, - - rgb_to_hex: function(r, g, b) { - var hex = this.hex_with_component(0, 2, r); - hex = this.hex_with_component(hex, 1, g); - hex = this.hex_with_component(hex, 0, b); - return hex; - }, - - component_from_hex: function(hex, componentIndex) { - return (hex >> (componentIndex * 8)) & 0xFF; - }, - - hex_with_component: function(hex, componentIndex, value) { - return value << (tmpComponent = componentIndex * 8) | (hex & ~ (0xFF << tmpComponent)); - } - - } - -})(), -dat.color.toString, -dat.utils.common), -dat.color.interpret, -dat.utils.common), -dat.utils.requestAnimationFrame = (function () { - - /** - * requirejs version of Paul Irish's RequestAnimationFrame - * http://paulirish.com/2011/requestanimationframe-for-smart-animating/ - */ - - return window.webkitRequestAnimationFrame || - window.mozRequestAnimationFrame || - window.oRequestAnimationFrame || - window.msRequestAnimationFrame || - function(callback, element) { - - window.setTimeout(callback, 1000 / 60); - - }; -})(), -dat.dom.CenteredDiv = (function (dom, common) { - - - var CenteredDiv = function() { - - this.backgroundElement = document.createElement('div'); - common.extend(this.backgroundElement.style, { - backgroundColor: 'rgba(0,0,0,0.8)', - top: 0, - left: 0, - display: 'none', - zIndex: '1000', - opacity: 0, - WebkitTransition: 'opacity 0.2s linear' - }); - - dom.makeFullscreen(this.backgroundElement); - this.backgroundElement.style.position = 'fixed'; - - this.domElement = document.createElement('div'); - common.extend(this.domElement.style, { - position: 'fixed', - display: 'none', - zIndex: '1001', - opacity: 0, - WebkitTransition: '-webkit-transform 0.2s ease-out, opacity 0.2s linear' - }); - - - document.body.appendChild(this.backgroundElement); - document.body.appendChild(this.domElement); - - var _this = this; - dom.bind(this.backgroundElement, 'click', function() { - _this.hide(); - }); - - - }; - - CenteredDiv.prototype.show = function() { - - var _this = this; - - - - this.backgroundElement.style.display = 'block'; - - this.domElement.style.display = 'block'; - this.domElement.style.opacity = 0; -// this.domElement.style.top = '52%'; - this.domElement.style.webkitTransform = 'scale(1.1)'; - - this.layout(); - - common.defer(function() { - _this.backgroundElement.style.opacity = 1; - _this.domElement.style.opacity = 1; - _this.domElement.style.webkitTransform = 'scale(1)'; - }); - - }; - - CenteredDiv.prototype.hide = function() { - - var _this = this; - - var hide = function() { - - _this.domElement.style.display = 'none'; - _this.backgroundElement.style.display = 'none'; - - dom.unbind(_this.domElement, 'webkitTransitionEnd', hide); - dom.unbind(_this.domElement, 'transitionend', hide); - dom.unbind(_this.domElement, 'oTransitionEnd', hide); - - }; - - dom.bind(this.domElement, 'webkitTransitionEnd', hide); - dom.bind(this.domElement, 'transitionend', hide); - dom.bind(this.domElement, 'oTransitionEnd', hide); - - this.backgroundElement.style.opacity = 0; -// this.domElement.style.top = '48%'; - this.domElement.style.opacity = 0; - this.domElement.style.webkitTransform = 'scale(1.1)'; - - }; - - CenteredDiv.prototype.layout = function() { - this.domElement.style.left = window.innerWidth/2 - dom.getWidth(this.domElement) / 2 + 'px'; - this.domElement.style.top = window.innerHeight/2 - dom.getHeight(this.domElement) / 2 + 'px'; - }; - - function lockScroll(e) { - console.log(e); - } - - return CenteredDiv; - -})(dat.dom.dom, -dat.utils.common), -dat.dom.dom, -dat.utils.common); \ No newline at end of file diff --git a/build/dat.gui.min.js b/build/dat.gui.min.js deleted file mode 100644 index 17e4a3c..0000000 --- a/build/dat.gui.min.js +++ /dev/null @@ -1,94 +0,0 @@ -/** - * dat-gui JavaScript Controller Library - * http://code.google.com/p/dat-gui - * - * Copyright 2011 Data Arts Team, Google Creative Lab - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - */ -var dat=dat||{};dat.gui=dat.gui||{};dat.utils=dat.utils||{};dat.controllers=dat.controllers||{};dat.dom=dat.dom||{};dat.color=dat.color||{};dat.utils.css=function(){return{load:function(e,a){var a=a||document,c=a.createElement("link");c.type="text/css";c.rel="stylesheet";c.href=e;a.getElementsByTagName("head")[0].appendChild(c)},inject:function(e,a){var a=a||document,c=document.createElement("style");c.type="text/css";c.innerHTML=e;a.getElementsByTagName("head")[0].appendChild(c)}}}(); -dat.utils.common=function(){var e=Array.prototype.forEach,a=Array.prototype.slice;return{BREAK:{},extend:function(c){this.each(a.call(arguments,1),function(a){for(var f in a)this.isUndefined(a[f])||(c[f]=a[f])},this);return c},defaults:function(c){this.each(a.call(arguments,1),function(a){for(var f in a)this.isUndefined(c[f])&&(c[f]=a[f])},this);return c},compose:function(){var c=a.call(arguments);return function(){for(var d=a.call(arguments),f=c.length-1;f>=0;f--)d=[c[f].apply(this,d)];return d[0]}}, -each:function(a,d,f){if(e&&a.forEach===e)a.forEach(d,f);else if(a.length===a.length+0)for(var b=0,n=a.length;b-1?d.length-d.indexOf(".")-1:0};c.superclass=e;a.extend(c.prototype,e.prototype,{setValue:function(a){if(this.__min!==void 0&&athis.__max)a=this.__max;this.__step!==void 0&&a%this.__step!=0&&(a=Math.round(a/this.__step)*this.__step);return c.superclass.prototype.setValue.call(this,a)},min:function(a){this.__min=a;return this},max:function(a){this.__max=a;return this},step:function(a){this.__step=a;return this}});return c}(dat.controllers.Controller,dat.utils.common); -dat.controllers.NumberControllerBox=function(e,a,c){var d=function(f,b,e){function h(){var a=parseFloat(l.__input.value);c.isNaN(a)||l.setValue(a)}function j(a){var b=o-a.clientY;l.setValue(l.getValue()+b*l.__impliedStep);o=a.clientY}function m(){a.unbind(window,"mousemove",j);a.unbind(window,"mouseup",m)}this.__truncationSuspended=false;d.superclass.call(this,f,b,e);var l=this,o;this.__input=document.createElement("input");this.__input.setAttribute("type","text");a.bind(this.__input,"change",h); -a.bind(this.__input,"blur",function(){h();l.__onFinishChange&&l.__onFinishChange.call(l,l.getValue())});a.bind(this.__input,"mousedown",function(b){a.bind(window,"mousemove",j);a.bind(window,"mouseup",m);o=b.clientY});a.bind(this.__input,"keydown",function(a){if(a.keyCode===13)l.__truncationSuspended=true,this.blur(),l.__truncationSuspended=false});this.updateDisplay();this.domElement.appendChild(this.__input)};d.superclass=e;c.extend(d.prototype,e.prototype,{updateDisplay:function(){var a=this.__input, -b;if(this.__truncationSuspended)b=this.getValue();else{b=this.getValue();var c=Math.pow(10,this.__precision);b=Math.round(b*c)/c}a.value=b;return d.superclass.prototype.updateDisplay.call(this)}});return d}(dat.controllers.NumberController,dat.dom.dom,dat.utils.common); -dat.controllers.NumberControllerSlider=function(e,a,c,d,f){var b=function(d,c,f,e,l){function o(b){b.preventDefault();var d=a.getOffset(g.__background),c=a.getWidth(g.__background);g.setValue(g.__min+(g.__max-g.__min)*((b.clientX-d.left)/(d.left+c-d.left)));return false}function y(){a.unbind(window,"mousemove",o);a.unbind(window,"mouseup",y);g.__onFinishChange&&g.__onFinishChange.call(g,g.getValue())}b.superclass.call(this,d,c,{min:f,max:e,step:l});var g=this;this.__background=document.createElement("div"); -this.__foreground=document.createElement("div");a.bind(this.__background,"mousedown",function(b){a.bind(window,"mousemove",o);a.bind(window,"mouseup",y);o(b)});a.addClass(this.__background,"slider");a.addClass(this.__foreground,"slider-fg");this.updateDisplay();this.__background.appendChild(this.__foreground);this.domElement.appendChild(this.__background)};b.superclass=e;b.useDefaultStyles=function(){c.inject(f)};d.extend(b.prototype,e.prototype,{updateDisplay:function(){this.__foreground.style.width= -(this.getValue()-this.__min)/(this.__max-this.__min)*100+"%";return b.superclass.prototype.updateDisplay.call(this)}});return b}(dat.controllers.NumberController,dat.dom.dom,dat.utils.css,dat.utils.common,".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}"); -dat.controllers.FunctionController=function(e,a,c){var d=function(c,b,e){d.superclass.call(this,c,b);var h=this;this.__button=document.createElement("div");this.__button.innerHTML=e===void 0?"Fire":e;a.bind(this.__button,"click",function(a){a.preventDefault();h.fire();return false});a.addClass(this.__button,"button");this.domElement.appendChild(this.__button)};d.superclass=e;c.extend(d.prototype,e.prototype,{fire:function(){this.__onChange&&this.__onChange.call(this);this.__onFinishChange&&this.__onFinishChange.call(this, -this.getValue());this.getValue().call(this.object)}});return d}(dat.controllers.Controller,dat.dom.dom,dat.utils.common); -dat.controllers.BooleanController=function(e,a,c){var d=function(c,b){d.superclass.call(this,c,b);var e=this;this.__prev=this.getValue();this.__checkbox=document.createElement("input");this.__checkbox.setAttribute("type","checkbox");a.bind(this.__checkbox,"change",function(){e.setValue(!e.__prev)},false);this.domElement.appendChild(this.__checkbox);this.updateDisplay()};d.superclass=e;c.extend(d.prototype,e.prototype,{setValue:function(a){a=d.superclass.prototype.setValue.call(this,a);this.__onFinishChange&& -this.__onFinishChange.call(this,this.getValue());this.__prev=this.getValue();return a},updateDisplay:function(){this.getValue()===true?(this.__checkbox.setAttribute("checked","checked"),this.__checkbox.checked=true):this.__checkbox.checked=false;return d.superclass.prototype.updateDisplay.call(this)}});return d}(dat.controllers.Controller,dat.dom.dom,dat.utils.common); -dat.color.toString=function(e){return function(a){if(a.a==1||e.isUndefined(a.a)){for(a=a.hex.toString(16);a.length<6;)a="0"+a;return"#"+a}else return"rgba("+Math.round(a.r)+","+Math.round(a.g)+","+Math.round(a.b)+","+a.a+")"}}(dat.utils.common); -dat.color.interpret=function(e,a){var c,d,f=[{litmus:a.isString,conversions:{THREE_CHAR_HEX:{read:function(a){a=a.match(/^#([A-F0-9])([A-F0-9])([A-F0-9])$/i);return a===null?false:{space:"HEX",hex:parseInt("0x"+a[1].toString()+a[1].toString()+a[2].toString()+a[2].toString()+a[3].toString()+a[3].toString())}},write:e},SIX_CHAR_HEX:{read:function(a){a=a.match(/^#([A-F0-9]{6})$/i);return a===null?false:{space:"HEX",hex:parseInt("0x"+a[1].toString())}},write:e},CSS_RGB:{read:function(a){a=a.match(/^rgb\(\s*(.+)\s*,\s*(.+)\s*,\s*(.+)\s*\)/); -return a===null?false:{space:"RGB",r:parseFloat(a[1]),g:parseFloat(a[2]),b:parseFloat(a[3])}},write:e},CSS_RGBA:{read:function(a){a=a.match(/^rgba\(\s*(.+)\s*,\s*(.+)\s*,\s*(.+)\s*\,\s*(.+)\s*\)/);return a===null?false:{space:"RGB",r:parseFloat(a[1]),g:parseFloat(a[2]),b:parseFloat(a[3]),a:parseFloat(a[4])}},write:e}}},{litmus:a.isNumber,conversions:{HEX:{read:function(a){return{space:"HEX",hex:a,conversionName:"HEX"}},write:function(a){return a.hex}}}},{litmus:a.isArray,conversions:{RGB_ARRAY:{read:function(a){return a.length!= -3?false:{space:"RGB",r:a[0],g:a[1],b:a[2]}},write:function(a){return[a.r,a.g,a.b]}},RGBA_ARRAY:{read:function(a){return a.length!=4?false:{space:"RGB",r:a[0],g:a[1],b:a[2],a:a[3]}},write:function(a){return[a.r,a.g,a.b,a.a]}}}},{litmus:a.isObject,conversions:{RGBA_OBJ:{read:function(b){return a.isNumber(b.r)&&a.isNumber(b.g)&&a.isNumber(b.b)&&a.isNumber(b.a)?{space:"RGB",r:b.r,g:b.g,b:b.b,a:b.a}:false},write:function(a){return{r:a.r,g:a.g,b:a.b,a:a.a}}},RGB_OBJ:{read:function(b){return a.isNumber(b.r)&& -a.isNumber(b.g)&&a.isNumber(b.b)?{space:"RGB",r:b.r,g:b.g,b:b.b}:false},write:function(a){return{r:a.r,g:a.g,b:a.b}}},HSVA_OBJ:{read:function(b){return a.isNumber(b.h)&&a.isNumber(b.s)&&a.isNumber(b.v)&&a.isNumber(b.a)?{space:"HSV",h:b.h,s:b.s,v:b.v,a:b.a}:false},write:function(a){return{h:a.h,s:a.s,v:a.v,a:a.a}}},HSV_OBJ:{read:function(b){return a.isNumber(b.h)&&a.isNumber(b.s)&&a.isNumber(b.v)?{space:"HSV",h:b.h,s:b.s,v:b.v}:false},write:function(a){return{h:a.h,s:a.s,v:a.v}}}}}];return function(){d= -false;var b=arguments.length>1?a.toArray(arguments):arguments[0];a.each(f,function(e){if(e.litmus(b))return a.each(e.conversions,function(e,f){c=e.read(b);if(d===false&&c!==false)return d=c,c.conversionName=f,c.conversion=e,a.BREAK}),a.BREAK});return d}}(dat.color.toString,dat.utils.common); -dat.GUI=dat.gui.GUI=function(e,a,c,d,f,b,n,h,j,m,l,o,y,g,i){function q(a,b,r,c){if(b[r]===void 0)throw Error("Object "+b+' has no property "'+r+'"');c.color?b=new l(b,r):(b=[b,r].concat(c.factoryArgs),b=d.apply(a,b));if(c.before instanceof f)c.before=c.before.__li;t(a,b);g.addClass(b.domElement,"c");r=document.createElement("span");g.addClass(r,"property-name");r.innerHTML=b.property;var e=document.createElement("div");e.appendChild(r);e.appendChild(b.domElement);c=s(a,e,c.before);g.addClass(c,k.CLASS_CONTROLLER_ROW); -g.addClass(c,typeof b.getValue());p(a,c,b);a.__controllers.push(b);return b}function s(a,b,d){var c=document.createElement("li");b&&c.appendChild(b);d?a.__ul.insertBefore(c,params.before):a.__ul.appendChild(c);a.onResize();return c}function p(a,d,c){c.__li=d;c.__gui=a;i.extend(c,{options:function(b){if(arguments.length>1)return c.remove(),q(a,c.object,c.property,{before:c.__li.nextElementSibling,factoryArgs:[i.toArray(arguments)]});if(i.isArray(b)||i.isObject(b))return c.remove(),q(a,c.object,c.property, -{before:c.__li.nextElementSibling,factoryArgs:[b]})},name:function(a){c.__li.firstElementChild.firstElementChild.innerHTML=a;return c},listen:function(){c.__gui.listen(c);return c},remove:function(){c.__gui.remove(c);return c}});if(c instanceof j){var e=new h(c.object,c.property,{min:c.__min,max:c.__max,step:c.__step});i.each(["updateDisplay","onChange","onFinishChange"],function(a){var b=c[a],H=e[a];c[a]=e[a]=function(){var a=Array.prototype.slice.call(arguments);b.apply(c,a);return H.apply(e,a)}}); -g.addClass(d,"has-slider");c.domElement.insertBefore(e.domElement,c.domElement.firstElementChild)}else if(c instanceof h){var f=function(b){return i.isNumber(c.__min)&&i.isNumber(c.__max)?(c.remove(),q(a,c.object,c.property,{before:c.__li.nextElementSibling,factoryArgs:[c.__min,c.__max,c.__step]})):b};c.min=i.compose(f,c.min);c.max=i.compose(f,c.max)}else if(c instanceof b)g.bind(d,"click",function(){g.fakeEvent(c.__checkbox,"click")}),g.bind(c.__checkbox,"click",function(a){a.stopPropagation()}); -else if(c instanceof n)g.bind(d,"click",function(){g.fakeEvent(c.__button,"click")}),g.bind(d,"mouseover",function(){g.addClass(c.__button,"hover")}),g.bind(d,"mouseout",function(){g.removeClass(c.__button,"hover")});else if(c instanceof l)g.addClass(d,"color"),c.updateDisplay=i.compose(function(a){d.style.borderLeftColor=c.__color.toString();return a},c.updateDisplay),c.updateDisplay();c.setValue=i.compose(function(b){a.getRoot().__preset_select&&c.isModified()&&B(a.getRoot(),true);return b},c.setValue)} -function t(a,b){var c=a.getRoot(),d=c.__rememberedObjects.indexOf(b.object);if(d!=-1){var e=c.__rememberedObjectIndecesToControllers[d];e===void 0&&(e={},c.__rememberedObjectIndecesToControllers[d]=e);e[b.property]=b;if(c.load&&c.load.remembered){c=c.load.remembered;if(c[a.preset])c=c[a.preset];else if(c[w])c=c[w];else return;if(c[d]&&c[d][b.property]!==void 0)d=c[d][b.property],b.initialValue=d,b.setValue(d)}}}function I(a){var b=a.__save_row=document.createElement("li");g.addClass(a.domElement, -"has-save");a.__ul.insertBefore(b,a.__ul.firstChild);g.addClass(b,"save-row");var c=document.createElement("span");c.innerHTML=" ";g.addClass(c,"button gears");var d=document.createElement("span");d.innerHTML="Save";g.addClass(d,"button");g.addClass(d,"save");var e=document.createElement("span");e.innerHTML="New";g.addClass(e,"button");g.addClass(e,"save-as");var f=document.createElement("span");f.innerHTML="Revert";g.addClass(f,"button");g.addClass(f,"revert");var m=a.__preset_select=document.createElement("select"); -a.load&&a.load.remembered?i.each(a.load.remembered,function(b,c){C(a,c,c==a.preset)}):C(a,w,false);g.bind(m,"change",function(){for(var b=0;b0){a.preset=this.preset;if(!a.remembered)a.remembered={};a.remembered[this.preset]=z(this)}a.folders={};i.each(this.__folders,function(b, -c){a.folders[c]=b.getSaveObject()});return a},save:function(){if(!this.load.remembered)this.load.remembered={};this.load.remembered[this.preset]=z(this);B(this,false)},saveAs:function(a){if(!this.load.remembered)this.load.remembered={},this.load.remembered[w]=z(this,true);this.load.remembered[a]=z(this);this.preset=a;C(this,a,true)},revert:function(a){i.each(this.__controllers,function(b){this.getRoot().load.remembered?t(a||this.getRoot(),b):b.setValue(b.initialValue)},this);i.each(this.__folders, -function(a){a.revert(a)});a||B(this.getRoot(),false)},listen:function(a){var b=this.__listening.length==0;this.__listening.push(a);b&&E(this.__listening)}});return k}(dat.utils.css,'
\n\n Here\'s the new load parameter for your GUI\'s constructor:\n\n \n\n
\n\n Automatically save\n values to localStorage on exit.\n\n
The values saved to localStorage will\n override those passed to dat.GUI\'s constructor. This makes it\n easier to work incrementally, but localStorage is fragile,\n and your friends may not see the same values you do.\n \n
\n \n
\n\n
', -".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", -dat.controllers.factory=function(e,a,c,d,f,b,n){return function(h,j,m,l){var o=h[j];if(n.isArray(m)||n.isObject(m))return new e(h,j,m);if(n.isNumber(o))return n.isNumber(m)&&n.isNumber(l)?new c(h,j,m,l):new a(h,j,{min:m,max:l});if(n.isString(o))return new d(h,j);if(n.isFunction(o))return new f(h,j,"");if(n.isBoolean(o))return new b(h,j)}}(dat.controllers.OptionController,dat.controllers.NumberControllerBox,dat.controllers.NumberControllerSlider,dat.controllers.StringController=function(e,a,c){var d= -function(c,b){function e(){h.setValue(h.__input.value)}d.superclass.call(this,c,b);var h=this;this.__input=document.createElement("input");this.__input.setAttribute("type","text");a.bind(this.__input,"keyup",e);a.bind(this.__input,"change",e);a.bind(this.__input,"blur",function(){h.__onFinishChange&&h.__onFinishChange.call(h,h.getValue())});a.bind(this.__input,"keydown",function(a){a.keyCode===13&&this.blur()});this.updateDisplay();this.domElement.appendChild(this.__input)};d.superclass=e;c.extend(d.prototype, -e.prototype,{updateDisplay:function(){if(!a.isActive(this.__input))this.__input.value=this.getValue();return d.superclass.prototype.updateDisplay.call(this)}});return d}(dat.controllers.Controller,dat.dom.dom,dat.utils.common),dat.controllers.FunctionController,dat.controllers.BooleanController,dat.utils.common),dat.controllers.Controller,dat.controllers.BooleanController,dat.controllers.FunctionController,dat.controllers.NumberControllerBox,dat.controllers.NumberControllerSlider,dat.controllers.OptionController, -dat.controllers.ColorController=function(e,a,c,d,f){function b(a,b,c,d){a.style.background="";f.each(j,function(e){a.style.cssText+="background: "+e+"linear-gradient("+b+", "+c+" 0%, "+d+" 100%); "})}function n(a){a.style.background="";a.style.cssText+="background: -moz-linear-gradient(top, #ff0000 0%, #ff00ff 17%, #0000ff 34%, #00ffff 50%, #00ff00 67%, #ffff00 84%, #ff0000 100%);";a.style.cssText+="background: -webkit-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);"; -a.style.cssText+="background: -o-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);";a.style.cssText+="background: -ms-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);";a.style.cssText+="background: linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);"}var h=function(e,l){function o(b){q(b);a.bind(window,"mousemove",q);a.bind(window, -"mouseup",j)}function j(){a.unbind(window,"mousemove",q);a.unbind(window,"mouseup",j)}function g(){var a=d(this.value);a!==false?(p.__color.__state=a,p.setValue(p.__color.toOriginal())):this.value=p.__color.toString()}function i(){a.unbind(window,"mousemove",s);a.unbind(window,"mouseup",i)}function q(b){b.preventDefault();var c=a.getWidth(p.__saturation_field),d=a.getOffset(p.__saturation_field),e=(b.clientX-d.left+document.body.scrollLeft)/c,b=1-(b.clientY-d.top+document.body.scrollTop)/c;b>1?b= -1:b<0&&(b=0);e>1?e=1:e<0&&(e=0);p.__color.v=b;p.__color.s=e;p.setValue(p.__color.toOriginal());return false}function s(b){b.preventDefault();var c=a.getHeight(p.__hue_field),d=a.getOffset(p.__hue_field),b=1-(b.clientY-d.top+document.body.scrollTop)/c;b>1?b=1:b<0&&(b=0);p.__color.h=b*360;p.setValue(p.__color.toOriginal());return false}h.superclass.call(this,e,l);this.__color=new c(this.getValue());this.__temp=new c(0);var p=this;this.domElement=document.createElement("div");a.makeSelectable(this.domElement, -false);this.__selector=document.createElement("div");this.__selector.className="selector";this.__saturation_field=document.createElement("div");this.__saturation_field.className="saturation-field";this.__field_knob=document.createElement("div");this.__field_knob.className="field-knob";this.__field_knob_border="2px solid ";this.__hue_knob=document.createElement("div");this.__hue_knob.className="hue-knob";this.__hue_field=document.createElement("div");this.__hue_field.className="hue-field";this.__input= -document.createElement("input");this.__input.type="text";this.__input_textShadow="0 1px 1px ";a.bind(this.__input,"keydown",function(a){a.keyCode===13&&g.call(this)});a.bind(this.__input,"blur",g);a.bind(this.__selector,"mousedown",function(){a.addClass(this,"drag").bind(window,"mouseup",function(){a.removeClass(p.__selector,"drag")})});var t=document.createElement("div");f.extend(this.__selector.style,{width:"122px",height:"102px",padding:"3px",backgroundColor:"#222",boxShadow:"0px 1px 3px rgba(0,0,0,0.3)"}); -f.extend(this.__field_knob.style,{position:"absolute",width:"12px",height:"12px",border:this.__field_knob_border+(this.__color.v<0.5?"#fff":"#000"),boxShadow:"0px 1px 3px rgba(0,0,0,0.5)",borderRadius:"12px",zIndex:1});f.extend(this.__hue_knob.style,{position:"absolute",width:"15px",height:"2px",borderRight:"4px solid #fff",zIndex:1});f.extend(this.__saturation_field.style,{width:"100px",height:"100px",border:"1px solid #555",marginRight:"3px",display:"inline-block",cursor:"pointer"});f.extend(t.style, -{width:"100%",height:"100%",background:"none"});b(t,"top","rgba(0,0,0,0)","#000");f.extend(this.__hue_field.style,{width:"15px",height:"100px",display:"inline-block",border:"1px solid #555",cursor:"ns-resize"});n(this.__hue_field);f.extend(this.__input.style,{outline:"none",textAlign:"center",color:"#fff",border:0,fontWeight:"bold",textShadow:this.__input_textShadow+"rgba(0,0,0,0.7)"});a.bind(this.__saturation_field,"mousedown",o);a.bind(this.__field_knob,"mousedown",o);a.bind(this.__hue_field,"mousedown", -function(b){s(b);a.bind(window,"mousemove",s);a.bind(window,"mouseup",i)});this.__saturation_field.appendChild(t);this.__selector.appendChild(this.__field_knob);this.__selector.appendChild(this.__saturation_field);this.__selector.appendChild(this.__hue_field);this.__hue_field.appendChild(this.__hue_knob);this.domElement.appendChild(this.__input);this.domElement.appendChild(this.__selector);this.updateDisplay()};h.superclass=e;f.extend(h.prototype,e.prototype,{updateDisplay:function(){var a=d(this.getValue()); -if(a!==false){var e=false;f.each(c.COMPONENTS,function(b){if(!f.isUndefined(a[b])&&!f.isUndefined(this.__color.__state[b])&&a[b]!==this.__color.__state[b])return e=true,{}},this);e&&f.extend(this.__color.__state,a)}f.extend(this.__temp.__state,this.__color.__state);this.__temp.a=1;var h=this.__color.v<0.5||this.__color.s>0.5?255:0,j=255-h;f.extend(this.__field_knob.style,{marginLeft:100*this.__color.s-7+"px",marginTop:100*(1-this.__color.v)-7+"px",backgroundColor:this.__temp.toString(),border:this.__field_knob_border+ -"rgb("+h+","+h+","+h+")"});this.__hue_knob.style.marginTop=(1-this.__color.h/360)*100+"px";this.__temp.s=1;this.__temp.v=1;b(this.__saturation_field,"left","#fff",this.__temp.toString());f.extend(this.__input.style,{backgroundColor:this.__input.value=this.__color.toString(),color:"rgb("+h+","+h+","+h+")",textShadow:this.__input_textShadow+"rgba("+j+","+j+","+j+",.7)"})}});var j=["-moz-","-o-","-webkit-","-ms-",""];return h}(dat.controllers.Controller,dat.dom.dom,dat.color.Color=function(e,a,c,d){function f(a, -b,c){Object.defineProperty(a,b,{get:function(){if(this.__state.space==="RGB")return this.__state[b];n(this,b,c);return this.__state[b]},set:function(a){if(this.__state.space!=="RGB")n(this,b,c),this.__state.space="RGB";this.__state[b]=a}})}function b(a,b){Object.defineProperty(a,b,{get:function(){if(this.__state.space==="HSV")return this.__state[b];h(this);return this.__state[b]},set:function(a){if(this.__state.space!=="HSV")h(this),this.__state.space="HSV";this.__state[b]=a}})}function n(b,c,e){if(b.__state.space=== -"HEX")b.__state[c]=a.component_from_hex(b.__state.hex,e);else if(b.__state.space==="HSV")d.extend(b.__state,a.hsv_to_rgb(b.__state.h,b.__state.s,b.__state.v));else throw"Corrupted color state";}function h(b){var c=a.rgb_to_hsv(b.r,b.g,b.b);d.extend(b.__state,{s:c.s,v:c.v});if(d.isNaN(c.h)){if(d.isUndefined(b.__state.h))b.__state.h=0}else b.__state.h=c.h}var j=function(){this.__state=e.apply(this,arguments);if(this.__state===false)throw"Failed to interpret color arguments";this.__state.a=this.__state.a|| -1};j.COMPONENTS="r,g,b,h,s,v,hex,a".split(",");d.extend(j.prototype,{toString:function(){return c(this)},toOriginal:function(){return this.__state.conversion.write(this)}});f(j.prototype,"r",2);f(j.prototype,"g",1);f(j.prototype,"b",0);b(j.prototype,"h");b(j.prototype,"s");b(j.prototype,"v");Object.defineProperty(j.prototype,"a",{get:function(){return this.__state.a},set:function(a){this.__state.a=a}});Object.defineProperty(j.prototype,"hex",{get:function(){if(!this.__state.space!=="HEX")this.__state.hex= -a.rgb_to_hex(this.r,this.g,this.b);return this.__state.hex},set:function(a){this.__state.space="HEX";this.__state.hex=a}});return j}(dat.color.interpret,dat.color.math=function(){var e;return{hsv_to_rgb:function(a,c,d){var e=a/60-Math.floor(a/60),b=d*(1-c),n=d*(1-e*c),c=d*(1-(1-e)*c),a=[[d,c,b],[n,d,b],[b,d,c],[b,n,d],[c,b,d],[d,b,n]][Math.floor(a/60)%6];return{r:a[0]*255,g:a[1]*255,b:a[2]*255}},rgb_to_hsv:function(a,c,d){var e=Math.min(a,c,d),b=Math.max(a,c,d),e=b-e;if(b==0)return{h:NaN,s:0,v:0}; -a=a==b?(c-d)/e:c==b?2+(d-a)/e:4+(a-c)/e;a/=6;a<0&&(a+=1);return{h:a*360,s:e/b,v:b/255}},rgb_to_hex:function(a,c,d){a=this.hex_with_component(0,2,a);a=this.hex_with_component(a,1,c);return a=this.hex_with_component(a,0,d)},component_from_hex:function(a,c){return a>>c*8&255},hex_with_component:function(a,c,d){return d<<(e=c*8)|a&~(255< 0) this.initialValue = this.propertyName[this.object]; + if (arguments.length > 0) this.initialValue = this.object[this.propertyName]; + + this.domElement = document.createElement('div'); + this.domElement.setAttribute('class', 'guidat-controller ' + this.type); + + this.propertyNameElement = document.createElement('span'); + this.propertyNameElement.setAttribute('class', 'guidat-propertyname'); + this.name(this.propertyName); + this.domElement.appendChild(this.propertyNameElement); + + DAT.GUI.makeUnselectable(this.domElement); + +}; + +DAT.GUI.Controller.prototype.changeFunction = null; +DAT.GUI.Controller.prototype.finishChangeFunction = null; + +DAT.GUI.Controller.prototype.name = function(n) { + this.propertyNameElement.innerHTML = n; + return this; +}; + +DAT.GUI.Controller.prototype.reset = function() { + this.setValue(this.initialValue); + return this; +}; + +DAT.GUI.Controller.prototype.listen = function() { + this.parent.listenTo(this); + return this; +}; + +DAT.GUI.Controller.prototype.unlisten = function() { + this.parent.unlistenTo(this); // <--- hasn't been tested yet + return this; +}; + +DAT.GUI.Controller.prototype.setValue = function(n) { + if(this.object[this.propertyName] != undefined){ + this.object[this.propertyName] = n; + }else{ + var o = new Object(); + o[this.propertyName] = n; + this.object.set(o); + } + if (this.changeFunction != null) { + this.changeFunction.call(this, n); + } + this.updateDisplay(); + return this; +}; + +DAT.GUI.Controller.prototype.getValue = function() { + var val = this.object[this.propertyName]; + if(val == undefined) val = this.object.get(this.propertyName); + return val; +}; + +DAT.GUI.Controller.prototype.updateDisplay = function() { + +}; + +DAT.GUI.Controller.prototype.onChange = function(fnc) { + this.changeFunction = fnc; + return this; +}; + +DAT.GUI.Controller.prototype.onFinishChange = function(fnc) { + this.finishChangeFunction = fnc; + return this; +}; + +DAT.GUI.Controller.prototype.options = function() { + var _this = this; + var select = document.createElement('select'); + if (arguments.length == 1) { + var arr = arguments[0]; + for (var i in arr) { + var opt = document.createElement('option'); + opt.innerHTML = i; + opt.setAttribute('value', arr[i]); + if (arguments[i] == this.getValue()) { + opt.selected = true; + } + select.appendChild(opt); + } + } else { + for (var i = 0; i < arguments.length; i++) { + var opt = document.createElement('option'); + opt.innerHTML = arguments[i]; + opt.setAttribute('value', arguments[i]); + if (arguments[i] == this.getValue()) { + opt.selected = true; + } + select.appendChild(opt); + } + } + + select.addEventListener('change', function() { + _this.setValue(this.value); + if (_this.finishChangeFunction != null) { + _this.finishChangeFunction.call(this, _this.getValue()); + } + }, false); + _this.domElement.appendChild(select); + return this; +}; diff --git a/src/DAT/GUI/ControllerBoolean.js b/src/DAT/GUI/ControllerBoolean.js new file mode 100644 index 0000000..be4f5b0 --- /dev/null +++ b/src/DAT/GUI/ControllerBoolean.js @@ -0,0 +1,43 @@ +DAT.GUI.ControllerBoolean = function() { + + this.type = "boolean"; + DAT.GUI.Controller.apply(this, arguments); + + var _this = this; + var input = document.createElement('input'); + input.setAttribute('type', 'checkbox'); + + input.checked = this.getValue(); + this.setValue(this.getValue()); + + this.domElement.addEventListener('click', function(e) { + input.checked = !input.checked; + e.preventDefault(); + _this.setValue(input.checked); + }, false); + + input.addEventListener('mouseup', function(e) { + input.checked = !input.checked; // counteracts default. + }, false); + + this.domElement.style.cursor = "pointer"; + this.propertyNameElement.style.cursor = "pointer"; + this.domElement.appendChild(input); + + this.updateDisplay = function() { + input.checked = _this.getValue(); + }; + + + this.setValue = function(val) { + if (typeof val != "boolean") { + try { + val = eval(val); + } catch (e) { + } + } + return DAT.GUI.Controller.prototype.setValue.call(this, val); + }; + +}; +DAT.GUI.extendController(DAT.GUI.ControllerBoolean); diff --git a/src/DAT/GUI/ControllerFunction.js b/src/DAT/GUI/ControllerFunction.js new file mode 100644 index 0000000..9ef4fc0 --- /dev/null +++ b/src/DAT/GUI/ControllerFunction.js @@ -0,0 +1,30 @@ +DAT.GUI.ControllerFunction = function() { + + this.type = "function"; + + var _this = this; + + DAT.GUI.Controller.apply(this, arguments); + + this.domElement.addEventListener('click', function() { + _this.fire(); + }, false); + + this.domElement.style.cursor = "pointer"; + this.propertyNameElement.style.cursor = "pointer"; + + var fireFunction = null; + this.onFire = function(fnc) { + fireFunction = fnc; + return this; + } + + this.fire = function() { + if (fireFunction != null) { + fireFunction.call(this); + } + _this.object[_this.propertyName].call(_this.object); + }; + +}; +DAT.GUI.extendController(DAT.GUI.ControllerFunction); diff --git a/src/DAT/GUI/ControllerNumber.js b/src/DAT/GUI/ControllerNumber.js new file mode 100644 index 0000000..db45315 --- /dev/null +++ b/src/DAT/GUI/ControllerNumber.js @@ -0,0 +1,243 @@ +DAT.GUI.ControllerNumber = function() { + + this.type = "number"; + + DAT.GUI.Controller.apply(this, arguments); + + var _this = this; + + // If we simply click and release a number field, we want to highlight it. + // This variable keeps track of whether or not we've dragged + var draggedNumberField = false; + + var clickedNumberField = false; + var draggingHorizontal = false; + var draggingVertical = false; + + var y = 0, py = 0; + + var min = arguments[3]; + var max = arguments[4]; + var step = arguments[5]; + + var defaultStep = function() { + step = (max - min) * 0.01; + }; + + this.min = function() { + var needsSlider = false; + if (min == undefined && max != undefined) { + needsSlider = true; + } + if (arguments.length == 0) { + return min; + } else { + min = arguments[0]; + } + if (needsSlider) { + addSlider(); + if (step == undefined) { + defaultStep(); + } + } + return _this; + }; + + this.max = function() { + var needsSlider = false; + if (min != undefined && max == undefined) { + needsSlider = true; + } + if (arguments.length == 0) { + return max; + } else { + max = arguments[0]; + } + if (needsSlider) { + addSlider(); + if (step == undefined) { + defaultStep(); + } + } + return _this; + }; + + this.step = function() { + if (arguments.length == 0) { + return step; + } else { + step = arguments[0]; + } + return _this; + }; + + this.getMin = function() { + return min; + }; + + this.getMax = function() { + return max; + }; + + this.getStep = function() { + if (step == undefined) { + if (max != undefined && min != undefined) { + return (max-min)/100; + } else { + return 1; + } + } else { + return step; + } + } + + var numberField = document.createElement('input'); + numberField.setAttribute('id', this.propertyName); + numberField.setAttribute('type', 'text'); + numberField.setAttribute('value', this.getValue()); + + if (step) numberField.setAttribute('step', step); + + this.domElement.appendChild(numberField); + + var slider; + + var addSlider = function() { + slider = new DAT.GUI.ControllerNumberSlider(_this, min, max, step, _this.getValue()); + _this.domElement.appendChild(slider.domElement); + }; + + if (min != undefined && max != undefined) { + addSlider(); + } + + numberField.addEventListener('blur', function() { + var val = parseFloat(this.value); + if (slider) { + DAT.GUI.removeClass(_this.domElement, 'active'); + } + if (!isNaN(val)) { + _this.setValue(val); + } + }, false); + + + numberField.addEventListener('mousewheel', function(e) { + e.preventDefault(); + _this.setValue(_this.getValue() + Math.abs(e.wheelDeltaY) / e.wheelDeltaY * _this.getStep()); + return false; + }, false); + + numberField.addEventListener('mousedown', function(e) { + py = y = e.pageY; + clickedNumberField = true; + DAT.GUI.makeSelectable(numberField); + document.addEventListener('mousemove', dragNumberField, false); + document.addEventListener('mouseup', mouseup, false); + }, false); + + // Handle up arrow and down arrow + numberField.addEventListener('keydown', function(e) { + var newVal; + switch (e.keyCode) { + case 13: // enter + newVal = parseFloat(this.value); + _this.setValue(newVal); + break; + case 38: // up + newVal = _this.getValue() + _this.getStep(); + _this.setValue(newVal); + break; + case 40: // down + newVal = _this.getValue() - _this.getStep(); + _this.setValue(newVal); + break; + } + }, false); + + var mouseup = function(e) { + document.removeEventListener('mousemove', dragNumberField, false); + + DAT.GUI.makeSelectable(numberField); + if (clickedNumberField && !draggedNumberField) { + //numberField.focus(); + //numberField.select(); + } + draggedNumberField = false; + clickedNumberField = false; + if (_this.finishChangeFunction != null) { + _this.finishChangeFunction.call(this, _this.getValue()); + } + draggingHorizontal = false; + draggingVertical = false; + document.removeEventListener('mouseup', mouseup, false); + }; + + var dragNumberField = function(e) { + + py = y; + y = e.pageY; + var dy = py - y; + + + + if (!draggingHorizontal && !draggingVertical) { + if (dy == 0) { + draggingHorizontal = true; + } else { + draggingVertical = true; + } + } + + if (draggingHorizontal) { + return true; + } + + DAT.GUI.addClass(_this.domElement, 'active'); + + DAT.GUI.makeUnselectable(_this.parent.domElement); + DAT.GUI.makeUnselectable(numberField); + + draggedNumberField = true; + e.preventDefault(); + + var newVal = _this.getValue() + dy * _this.getStep(); + _this.setValue(newVal); + return false; + + }; + + this.options = function() { + _this.noSlider(); + _this.domElement.removeChild(numberField); + return DAT.GUI.Controller.prototype.options.apply(this, arguments); + }; + + this.noSlider = function() { + if (slider) { + _this.domElement.removeChild(slider.domElement); + } + return this; + }; + + this.setValue = function(val) { + + val = parseFloat(val); + + if (min != undefined && val <= min) { + val = min; + } else if (max != undefined && val >= max) { + val = max; + } + + return DAT.GUI.Controller.prototype.setValue.call(this, val); + + }; + + this.updateDisplay = function() { + numberField.value = DAT.GUI.roundToDecimal(_this.getValue(), 4); + if (slider) slider.value = _this.getValue(); + }; +}; + +DAT.GUI.extendController(DAT.GUI.ControllerNumber); diff --git a/src/DAT/GUI/ControllerNumberSlider.js b/src/DAT/GUI/ControllerNumberSlider.js new file mode 100644 index 0000000..d5521d2 --- /dev/null +++ b/src/DAT/GUI/ControllerNumberSlider.js @@ -0,0 +1,64 @@ +DAT.GUI.ControllerNumberSlider = function(numberController, min, max, step, initValue) { + + var clicked = false; + var _this = this; + + var x, px; + + this.domElement = document.createElement('div'); + this.domElement.setAttribute('class', 'guidat-slider-bg'); + + this.fg = document.createElement('div'); + this.fg.setAttribute('class', 'guidat-slider-fg'); + + this.domElement.appendChild(this.fg); + + var onDrag = function(e) { + if (!clicked) return; + var pos = findPos(_this.domElement); + var val = DAT.GUI.map(e.pageX, pos[0], pos[0] + _this.domElement + .offsetWidth, numberController.getMin(), numberController.getMax()); + val = Math.round(val / numberController.getStep()) * numberController + .getStep(); + numberController.setValue(val); + }; + + this.domElement.addEventListener('mousedown', function(e) { + clicked = true; + x = px = e.pageX; + DAT.GUI.addClass(numberController.domElement, 'active'); + onDrag(e); + document.addEventListener('mouseup', mouseup, false); + }, false); + + var mouseup = function(e) { + DAT.GUI.removeClass(numberController.domElement, 'active'); + clicked = false; + if (numberController.finishChangeFunction != null) { + numberController.finishChangeFunction.call(this, + numberController.getValue()); + } + document.removeEventListener('mouseup', mouseup, false); + }; + + var findPos = function(obj) { + var curleft = 0, curtop = 0; + if (obj.offsetParent) { + do { + curleft += obj.offsetLeft; + curtop += obj.offsetTop; + } while ((obj = obj.offsetParent)); + return [curleft,curtop]; + } + }; + + this.__defineSetter__('value', function(e) { + this.fg.style.width = DAT.GUI.map(e, numberController.getMin(), + numberController.getMax(), 0, 100) + "%"; + }); + + document.addEventListener('mousemove', onDrag, false); + + this.value = initValue; + +}; \ No newline at end of file diff --git a/src/DAT/GUI/ControllerString.js b/src/DAT/GUI/ControllerString.js new file mode 100644 index 0000000..9642e82 --- /dev/null +++ b/src/DAT/GUI/ControllerString.js @@ -0,0 +1,57 @@ +DAT.GUI.ControllerString = function() { + + this.type = "string"; + + var _this = this; + DAT.GUI.Controller.apply(this, arguments); + + var input = document.createElement('input'); + + var initialValue = this.getValue(); + + input.setAttribute('value', initialValue); + input.setAttribute('spellcheck', 'false'); + + this.domElement.addEventListener('mouseup', function() { + input.focus(); + input.select(); + }, false); + + // TODO: getting messed up on ctrl a + input.addEventListener('keyup', function(e) { + if (e.keyCode == 13 && _this.finishChangeFunction != null) { + _this.finishChangeFunction.call(this, _this.getValue()); + input.blur(); + } + _this.setValue(input.value); + }, false); + + input.addEventListener('mousedown', function(e) { + DAT.GUI.makeSelectable(input); + }, false); + + input.addEventListener('blur', function() { + DAT.GUI.supressHotKeys = false; + if (_this.finishChangeFunction != null) { + _this.finishChangeFunction.call(this, _this.getValue()); + } + }, false); + + input.addEventListener('focus', function() { + DAT.GUI.supressHotKeys = true; + }, false); + + this.updateDisplay = function() { + input.value = _this.getValue(); + }; + + this.options = function() { + _this.domElement.removeChild(input); + return DAT.GUI.Controller.prototype.options.apply(this, arguments); + }; + + this.domElement.appendChild(input); + +}; + +DAT.GUI.extendController(DAT.GUI.ControllerString); diff --git a/src/DAT/GUI/GUI.css b/src/DAT/GUI/GUI.css new file mode 100644 index 0000000..e5af34c --- /dev/null +++ b/src/DAT/GUI/GUI.css @@ -0,0 +1,168 @@ +#guidat { + position: fixed; + top: 0; + right: 0; + width: auto; + z-index: 1001; + text-align: right; +} + +.guidat { + color: #fff; + opacity: 0.97; + text-align: left; + float: right; + margin-right: 20px; + margin-bottom: 20px; + background-color: #fff; +} + +.guidat, +.guidat input { + font: 9.5px Lucida Grande, sans-serif; +} + +.guidat-controllers { + height: 300px; + overflow-y: auto; + overflow-x: hidden; + background-color: rgba(0, 0, 0, 0.1); +} + +a.guidat-toggle:link, +a.guidat-toggle:visited, +a.guidat-toggle:active { + text-decoration: none; + cursor: pointer; + color: #fff; + background-color: #222; + text-align: center; + display: block; + padding: 5px; + +} + +a.guidat-toggle:hover { + background-color: #000; +} + +.guidat-controller { + padding: 3px; + height: 25px; + clear: left; + border-bottom: 1px solid #222; + background-color: #111; +} + +.guidat-controller, +.guidat-controller input, +.guidat-slider-bg, +.guidat-slider-fg { + -moz-transition: background-color 0.15s linear; + -webkit-transition: background-color 0.15s linear; + transition: background-color 0.15s linear; +} + +.guidat-controller.boolean:hover, +.guidat-controller.function:hover { + background-color: #000; +} + +.guidat-controller input { + float: right; + outline: none; + border: 0; + padding: 4px; + margin-top: 2px; + background-color: #222; +} + +.guidat-controller select { + margin-top: 4px; + float: right; +} + +.guidat-controller input:hover { + background-color: #444; +} + +.guidat-controller input:focus, +.guidat-controller.active input { + background-color: #555; + color: #fff; +} + +.guidat-controller.number { + border-left: 5px solid #00aeff; +} + +.guidat-controller.string { + border-left: 5px solid #1ed36f; +} + +.guidat-controller.string input { + border: 0; + color: #1ed36f; + margin-right: 2px; + width: 148px; +} + +.guidat-controller.boolean { + border-left: 5px solid #54396e; +} + +.guidat-controller.function { + border-left: 5px solid #e61d5f; +} + +.guidat-controller.number input[type=text] { + width: 35px; + margin-left: 5px; + margin-right: 2px; + color: #00aeff; +} + +.guidat .guidat-controller.boolean input { + margin-top: 6px; + margin-right: 2px; + font-size: 20px; +} + +.guidat-controller:last-child { + border-bottom: none; + -webkit-box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.5); + -moz-box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.5); + box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.5); +} + +.guidat-propertyname { + padding: 5px; + padding-top: 7px; + cursor: default; + display: inline-block; +} + +.guidat-controller .guidat-slider-bg:hover, +.guidat-controller.active .guidat-slider-bg { + background-color: #444; +} + +.guidat-controller .guidat-slider-bg .guidat-slider-fg:hover, +.guidat-controller.active .guidat-slider-bg .guidat-slider-fg { + background-color: #52c8ff; +} + +.guidat-slider-bg { + background-color: #222; + cursor: ew-resize; + width: 40%; + margin-top: 2px; + float: right; + height: 21px; +} + +.guidat-slider-fg { + cursor: ew-resize; + background-color: #00aeff; + height: 21px; +} diff --git a/src/DAT/GUI/GUI.js b/src/DAT/GUI/GUI.js new file mode 100644 index 0000000..5981573 --- /dev/null +++ b/src/DAT/GUI/GUI.js @@ -0,0 +1,740 @@ +var DAT = DAT || {}; + +DAT.GUI = function(parameters) { + + if (parameters == undefined) { + parameters = {}; + } + + + var paramsExplicitHeight = false; + if (parameters.height == undefined) { + parameters.height = 300; + } else { + paramsExplicitHeight = true; + } + + var MIN_WIDTH = 240; + var MAX_WIDTH = 500; + + var controllers = []; + var listening = []; + + var autoListen = true; + + var listenInterval; + + // Sum total of heights of controllers in this gui + var controllerHeight; + + var _this = this; + + var open = true; + + var width = 280; + if (parameters.width != undefined) { + width = parameters.width; + } + + // Prevents checkForOverflow bug in which loaded gui appearance + // settings are not respected by presence of scrollbar. + var explicitOpenHeight = false; + + // How big we get when we open + var openHeight; + + var closeString = 'Close Controls'; + var openString = 'Open Controls'; + + var name; + + var resizeTo = 0; + var resizeTimeout; + + this.domElement = document.createElement('div'); + this.domElement.setAttribute('class', 'guidat'); + this.domElement.style.width = width + 'px'; + + var curControllerContainerHeight = parameters.height; + var controllerContainer = document.createElement('div'); + controllerContainer.setAttribute('class', 'guidat-controllers'); + controllerContainer.style.height = curControllerContainerHeight + 'px'; + + // Firefox hack to prevent horizontal scrolling + controllerContainer.addEventListener('DOMMouseScroll', function(e) { + + var scrollAmount = this.scrollTop; + + if (e.wheelDelta) { + scrollAmount += e.wheelDelta; + } else if (e.detail) { + scrollAmount += e.detail; + } + + if (e.preventDefault) { + e.preventDefault(); + } + + e.returnValue = false; + + controllerContainer.scrollTop = scrollAmount; + + }, false); + + + var toggleButton = document.createElement('a'); + toggleButton.setAttribute('class', 'guidat-toggle'); + toggleButton.setAttribute('href', '#'); + toggleButton.innerHTML = open ? closeString : openString; + + var toggleDragged = false; + var dragDisplacementY = 0; + var dragDisplacementX = 0; + var togglePressed = false; + + var my, pmy, mx, pmx; + + var resize = function(e) { + pmy = my; + pmx = mx; + my = e.pageY; + mx = e.pageX; + + var dmy = my - pmy; + + if (!open) { + if (dmy > 0) { + open = true; + curControllerContainerHeight = openHeight = 1; + toggleButton.innerHTML = name || closeString; + } else { + return; + } + } + + // TODO: Flip this if you want to resize to the left. + var dmx = pmx - mx; + + if (dmy > 0 && + curControllerContainerHeight > controllerHeight) { + var d = DAT.GUI.map(curControllerContainerHeight, controllerHeight, + controllerHeight + 100, 1, 0); + dmy *= d; + } + + toggleDragged = true; + + dragDisplacementY += dmy; + openHeight += dmy; + curControllerContainerHeight += dmy; + controllerContainer.style.height = openHeight + 'px'; + + dragDisplacementX += dmx; + width += dmx; + width = DAT.GUI.constrain(width, MIN_WIDTH, MAX_WIDTH); + _this.domElement.style.width = width + 'px'; + + checkForOverflow(); + + }; + + toggleButton.addEventListener('mousedown', function(e) { + pmy = my = e.pageY; + pmx = mx = e.pageX; + togglePressed = true; + e.preventDefault(); + dragDisplacementX = 0; + dragDisplacementY = 0; + document.addEventListener('mousemove', resize, false); + return false; + + }, false); + + toggleButton.addEventListener('click', function(e) { + e.preventDefault(); + return false; + }, false); + + document.addEventListener('mouseup', function(e) { + + if (togglePressed && !toggleDragged) { + _this.toggle(); + } + + if (togglePressed && toggleDragged) { + + if (dragDisplacementX == 0) { + adaptToScrollbar(); + } + + if (openHeight > controllerHeight) { + + clearTimeout(resizeTimeout); + openHeight = resizeTo = controllerHeight; + beginResize(); + + } else if (controllerContainer.children.length >= 1) { + + var singleControllerHeight = controllerContainer.children[0]. + offsetHeight; + clearTimeout(resizeTimeout); + var target = Math.round(curControllerContainerHeight / + singleControllerHeight) * singleControllerHeight - 1; + resizeTo = target; + if (resizeTo <= 0) { + _this.close(); + openHeight = singleControllerHeight * 2; + } else { + openHeight = resizeTo; + beginResize(); + } + } + } + + document.removeEventListener('mousemove', resize, false); + e.preventDefault(); + toggleDragged = false; + togglePressed = false; + + return false; + + }, false); + + this.domElement.appendChild(controllerContainer); + this.domElement.appendChild(toggleButton); + + if (parameters.domElement) { + parameters.domElement.appendChild(this.domElement); + } else if (DAT.GUI.autoPlace) { + if (DAT.GUI.autoPlaceContainer == null) { + DAT.GUI.autoPlaceContainer = document.createElement('div'); + DAT.GUI.autoPlaceContainer.setAttribute('id', 'guidat'); + + document.body.appendChild(DAT.GUI.autoPlaceContainer); + } + DAT.GUI.autoPlaceContainer.appendChild(this.domElement); + } + + this.autoListenIntervalTime = 1000 / 60; + + var createListenInterval = function() { + listenInterval = setInterval(function() { + _this.listen(); + }, this.autoListenIntervalTime); + }; + + this.__defineSetter__('autoListen', function(v) { + autoListen = v; + if (!autoListen) { + clearInterval(listenInterval); + } else { + if (listening.length > 0) createListenInterval(); + } + }); + + this.__defineGetter__('autoListen', function(v) { + return autoListen; + }); + + this.listenTo = function(controller) { + // TODO: check for duplicates + if (listening.length == 0) { + createListenInterval(); + } + listening.push(controller); + }; + + this.unlistenTo = function(controller) { + // TODO: test this + for (var i = 0; i < listening.length; i++) { + if (listening[i] == controller) listening.splice(i, 1); + } + if (listening.length <= 0) { + clearInterval(listenInterval); + } + }; + + this.listen = function(whoToListenTo) { + var arr = whoToListenTo || listening; + for (var i in arr) { + arr[i].updateDisplay(); + } + }; + + this.listenAll = function() { + this.listen(controllers); + } + + this.autoListen = true; + + var alreadyControlled = function(object, propertyName) { + for (var i in controllers) { + if (controllers[i].object == object && + controllers[i].propertyName == propertyName) { + return true; + } + } + return false; + }; + + var construct = function(constructor, args) { + function C() { + return constructor.apply(this, args); + } + + C.prototype = constructor.prototype; + return new C(); + }; + + this.add = function() { + + + if (arguments.length == 1) { + var toReturn = []; + for (var i in arguments[0]) { + toReturn.push(_this.add(arguments[0], i)); + } + return toReturn; + } + + var object = arguments[0]; + var propertyName = arguments[1]; + + // Have we already added this? + if (alreadyControlled(object, propertyName)) { + // DAT.GUI.error('Controller for \'' + propertyName+'\' already added.'); + // return; + } + + var value = object[propertyName]; + if(value == undefined && object.get) value = object.get(propertyName); + + // Does this value exist? Is it accessible? + if (value == undefined) { + DAT.GUI.error(object + ' either has no property \'' + propertyName + + '\', or the property is inaccessible.'); + return; + } + + var type = typeof value; + var handler = handlerTypes[type]; + + // Do we know how to deal with this data type? + if (handler == undefined) { + DAT.GUI.error('Cannot create controller for data type \'' + type + '\''); + return; + } + + var args = [this]; // Set first arg (parent) to this + for (var j = 0; j < arguments.length; j++) { + args.push(arguments[j]); + } + + var controllerObject = construct(handler, args); + + // Were we able to make the controller? + if (!controllerObject) { + DAT.GUI.error('Error creating controller for \'' + propertyName + '\'.'); + return; + } + + // Success. + controllerContainer.appendChild(controllerObject.domElement); + controllers.push(controllerObject); + DAT.GUI.allControllers.push(controllerObject); + + // Do we have a saved value for this controller? + if (type != 'function' && + DAT.GUI.saveIndex < DAT.GUI.savedValues.length) { + controllerObject.setValue(DAT.GUI.savedValues[DAT.GUI.saveIndex]); + DAT.GUI.saveIndex++; + } + + // Compute sum height of controllers. + checkForOverflow(); + + // Prevents checkForOverflow bug in which loaded gui appearance + // settings are not respected by presence of scrollbar. + if (!explicitOpenHeight) { + openHeight = controllerHeight; + } + + // Let's see if we're doing this on onload and lets *try* to guess how + // big you want the damned box. + if (!paramsExplicitHeight) { + try { + + // Probably a better way to do this + var caller = arguments.callee.caller; + + if (caller == window['onload']) { + curControllerContainerHeight = resizeTo = openHeight = + controllerHeight; + controllerContainer.style.height = curControllerContainerHeight + 'px'; + } + + } catch (e) {} + } + + + return controllerObject; + + } + + var checkForOverflow = function() { + controllerHeight = 0; + for (var i in controllers) { + controllerHeight += controllers[i].domElement.offsetHeight; + } + if (controllerHeight - 1 > openHeight) { + controllerContainer.style.overflowY = 'auto'; + } else { + controllerContainer.style.overflowY = 'hidden'; + } + }; + + var handlerTypes = { + 'number': DAT.GUI.ControllerNumber, + 'string': DAT.GUI.ControllerString, + 'boolean': DAT.GUI.ControllerBoolean, + 'function': DAT.GUI.ControllerFunction + }; + + this.reset = function() { + // TODO ... Set all values back to their initials. + for (var i = 0, l = DAT.GUI.allControllers.length; i < l; i++) { + // apply to each controller + DAT.GUI.allControllers[i].reset(); + } + } + + this.toggle = function() { + open ? this.close() : this.open(); + }; + + this.open = function() { + toggleButton.innerHTML = name || closeString; + resizeTo = openHeight; + clearTimeout(resizeTimeout); + beginResize(); + adaptToScrollbar(); + open = true; + } + + this.close = function() { + toggleButton.innerHTML = name || openString; + resizeTo = 0; + clearTimeout(resizeTimeout); + beginResize(); + adaptToScrollbar(); + open = false; + } + + this.name = function(n) { + name = n; + toggleButton.innerHTML = n; + } + + // used in saveURL + this.appearanceVars = function() { + return [open, width, openHeight, controllerContainer.scrollTop] + } + + var beginResize = function() { + + curControllerContainerHeight = controllerContainer.offsetHeight; + curControllerContainerHeight += (resizeTo - curControllerContainerHeight) + * 0.6; + + if (Math.abs(curControllerContainerHeight - resizeTo) < 1) { + curControllerContainerHeight = resizeTo; + } else { + resizeTimeout = setTimeout(beginResize, 1000 / 30); + } + controllerContainer.style.height = Math.round(curControllerContainerHeight) + + 'px'; + checkForOverflow(); + + } + + var adaptToScrollbar = function() { + // Clears lingering scrollbar column + _this.domElement.style.width = (width - 1) + 'px'; + setTimeout(function() { + _this.domElement.style.width = width + 'px'; + }, 1); + }; + + + // Load saved appearance: + + if (DAT.GUI.guiIndex < DAT.GUI.savedAppearanceVars.length) { + + width = parseInt(DAT.GUI.savedAppearanceVars[DAT.GUI.guiIndex][1]); + _this.domElement.style.width = width + 'px'; + + openHeight = parseInt(DAT.GUI.savedAppearanceVars[DAT.GUI.guiIndex][2]); + explicitOpenHeight = true; + if (eval(DAT.GUI.savedAppearanceVars[DAT.GUI.guiIndex][0]) == true) { + curControllerContainerHeight = openHeight; + var t = DAT.GUI.savedAppearanceVars[DAT.GUI.guiIndex][3] + + // Hack. + setTimeout(function() { + controllerContainer.scrollTop = t; + }, 0); + + if (DAT.GUI.scrollTop > -1) { + document.body.scrollTop = DAT.GUI.scrollTop; + } + resizeTo = openHeight; + this.open(); + } + + DAT.GUI.guiIndex++; + } + + DAT.GUI.allGuis.push(this); + + // Add hide listener if this is the first DAT.GUI. + + if (DAT.GUI.allGuis.length == 1) { + + window.addEventListener('keyup', function(e) { + // Hide on 'H' + if (!DAT.GUI.supressHotKeys && e.keyCode == 72) { + DAT.GUI.toggleHide(); + } + }, false); + + if (DAT.GUI.inlineCSS) { + var styleSheet = document.createElement('style'); + styleSheet.setAttribute('type', 'text/css'); + styleSheet.innerHTML = DAT.GUI.inlineCSS; + document.head.insertBefore(styleSheet, document.head.firstChild); + } + + } + +}; + +// Do not set this directly. +DAT.GUI.hidden = false; + +// Static members + +DAT.GUI.autoPlace = true; +DAT.GUI.autoPlaceContainer = null; +DAT.GUI.allControllers = []; +DAT.GUI.allGuis = []; + +DAT.GUI.supressHotKeys = false; + +DAT.GUI.toggleHide = function() { + if (DAT.GUI.hidden) { + DAT.GUI.open(); + } else { + DAT.GUI.close(); + } +} + +DAT.GUI.open = function() { + DAT.GUI.hidden = false; + for (var i in DAT.GUI.allGuis) { + DAT.GUI.allGuis[i].domElement.style.display = 'block'; + } +} + +DAT.GUI.close = function() { + DAT.GUI.hidden = true; + for (var i in DAT.GUI.allGuis) { + DAT.GUI.allGuis[i].domElement.style.display = 'none'; + } +} + +DAT.GUI.saveURL = function() { + var url = DAT.GUI.replaceGetVar('saveString', DAT.GUI.getSaveString()); + window.location = url; +}; + +DAT.GUI.scrollTop = -1; + +DAT.GUI.load = function(saveString) { + + //DAT.GUI.savedAppearanceVars = []; + var vals = saveString.split(','); + var numGuis = parseInt(vals[0]); + DAT.GUI.scrollTop = parseInt(vals[1]); + for (var i = 0; i < numGuis; i++) { + var appr = vals.splice(2, 4); + DAT.GUI.savedAppearanceVars.push(appr); + } + + DAT.GUI.savedValues = vals.splice(2, vals.length); + +}; + +DAT.GUI.savedValues = []; +DAT.GUI.savedAppearanceVars = []; + +DAT.GUI.getSaveString = function() { + + var vals = [], i; + + vals.push(DAT.GUI.allGuis.length); + vals.push(document.body.scrollTop); + + + for (i in DAT.GUI.allGuis) { + var av = DAT.GUI.allGuis[i].appearanceVars(); + for (var j = 0; j < av.length; j++) { + vals.push(av[j]); + } + } + + for (i in DAT.GUI.allControllers) { + + // We don't save values for functions. + if (DAT.GUI.allControllers[i].type == 'function') { + continue; + } + + var v = DAT.GUI.allControllers[i].getValue(); + + // Round numbers so they don't get enormous + if (DAT.GUI.allControllers[i].type == 'number') { + v = DAT.GUI.roundToDecimal(v, 4); + } + + vals.push(v); + + } + + return vals.join(','); + +}; + +DAT.GUI.getVarFromURL = function(v) { + + var vars = [], hash; + var hashes = window.location.href.slice( + window.location.href.indexOf('?') + 1).split('&'); + + for (var i = 0; i < hashes.length; i++) { + hash = hashes[i].split('='); + if (hash == undefined) continue; + if (hash[0] == v) { + return hash[1]; + } + } + + return null; + +}; + +DAT.GUI.replaceGetVar = function(varName, val) { + + var vars = [], hash; + var loc = window.location.href; + var hashes = window.location.href.slice( + window.location.href.indexOf('?') + 1).split('&'); + + for (var i = 0; i < hashes.length; i++) { + hash = hashes[i].split('='); + if (hash == undefined) continue; + if (hash[0] == varName) { + return loc.replace(hash[1], val); + } + } + + if (window.location.href.indexOf('?') != -1) { + return loc + '&' + varName + '=' + val; + } + + return loc + '?' + varName + '=' + val; + +}; + +DAT.GUI.saveIndex = 0; +DAT.GUI.guiIndex = 0; + +DAT.GUI.showSaveString = function() { + alert(DAT.GUI.getSaveString()); +}; + +// Util functions + +DAT.GUI.makeUnselectable = function(elem) { + if (elem == undefined || elem.style == undefined) return; + elem.onselectstart = function() { + return false; + }; + elem.style.MozUserSelect = 'none'; + elem.style.KhtmlUserSelect = 'none'; + elem.unselectable = 'on'; + + var kids = elem.childNodes; + for (var i = 0; i < kids.length; i++) { + DAT.GUI.makeUnselectable(kids[i]); + } + +}; + +DAT.GUI.makeSelectable = function(elem) { + if (elem == undefined || elem.style == undefined) return; + elem.onselectstart = function() { + }; + elem.style.MozUserSelect = 'auto'; + elem.style.KhtmlUserSelect = 'auto'; + elem.unselectable = 'off'; + + var kids = elem.childNodes; + for (var i = 0; i < kids.length; i++) { + DAT.GUI.makeSelectable(kids[i]); + } + +}; + +DAT.GUI.map = function(v, i1, i2, o1, o2) { + return o1 + (o2 - o1) * ((v - i1) / (i2 - i1)); +}; + +DAT.GUI.constrain = function (v, o1, o2) { + if (v < o1) v = o1; + else if (v > o2) v = o2; + return v; +}; + +DAT.GUI.error = function(str) { + if (typeof console.error == 'function') { + console.error('[DAT.GUI ERROR] ' + str); + } +}; + +DAT.GUI.roundToDecimal = function(n, decimals) { + var t = Math.pow(10, decimals); + return Math.round(n * t) / t; +}; + +DAT.GUI.extendController = function(clazz) { + clazz.prototype = new DAT.GUI.Controller(); + clazz.prototype.constructor = clazz; +}; + +DAT.GUI.addClass = function(domElement, className) { + if (DAT.GUI.hasClass(domElement, className)) return; + domElement.className += ' ' + className; +} + +DAT.GUI.hasClass = function(domElement, className) { + return domElement.className.indexOf(className) != -1; +} + +DAT.GUI.removeClass = function(domElement, className) { + var reg = new RegExp(' ' + className, 'g'); + domElement.className = domElement.className.replace(reg, ''); +} + +if (DAT.GUI.getVarFromURL('saveString') != null) { + DAT.GUI.load(DAT.GUI.getVarFromURL('saveString')); +} diff --git a/src/dat/gui/GUI.js b/src/dat/gui/GUI.js index b25b8bf..5981573 100644 --- a/src/dat/gui/GUI.js +++ b/src/dat/gui/GUI.js @@ -1,1351 +1,740 @@ -/** - * dat-gui JavaScript Controller Library - * http://code.google.com/p/dat-gui - * - * Copyright 2011 Data Arts Team, Google Creative Lab - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - */ +var DAT = DAT || {}; -define([ +DAT.GUI = function(parameters) { - 'dat/utils/css', - - 'text!dat/gui/saveDialogue.html', - 'text!dat/gui/style.css', - - 'dat/controllers/factory', - 'dat/controllers/Controller', - 'dat/controllers/BooleanController', - 'dat/controllers/FunctionController', - 'dat/controllers/NumberControllerBox', - 'dat/controllers/NumberControllerSlider', - 'dat/controllers/OptionController', - 'dat/controllers/ColorController', - - 'dat/utils/requestAnimationFrame', - - 'dat/dom/CenteredDiv', - 'dat/dom/dom', - - 'dat/utils/common' - -], function(css, saveDialogueContents, styleSheet, controllerFactory, Controller, BooleanController, FunctionController, NumberControllerBox, NumberControllerSlider, OptionController, ColorController, requestAnimationFrame, CenteredDiv, dom, common) { - - css.inject(styleSheet); - - /** Outer-most className for GUI's */ - var CSS_NAMESPACE = 'dg'; - - var HIDE_KEY_CODE = 72; - - /** The only value shared between the JS and SCSS. Use caution. */ - var CLOSE_BUTTON_HEIGHT = 20; - - var DEFAULT_DEFAULT_PRESET_NAME = 'Default'; - - var SUPPORTS_LOCAL_STORAGE = (function() { - try { - return 'localStorage' in window && window['localStorage'] !== null; - } catch (e) { - return false; - } - })(); - - var SAVE_DIALOGUE; - - /** Have we yet to create an autoPlace GUI? */ - var auto_place_virgin = true; - - /** Fixed position div that auto place GUI's go inside */ - var auto_place_container; - - /** Are we hiding the GUI's ? */ - var hide = false; - - /** GUI's which should be hidden */ - var hideable_guis = []; - - /** - * A lightweight controller library for JavaScript. It allows you to easily - * manipulate variables and fire functions on the fly. - * @class - * - * @member dat.gui - * - * @param {Object} [params] - * @param {String} [params.name] The name of this GUI. - * @param {Object} [params.load] JSON object representing the saved state of - * this GUI. - * @param {Boolean} [params.auto=true] - * @param {dat.gui.GUI} [params.parent] The GUI I'm nested in. - * @param {Boolean} [params.closed] If true, starts closed - */ - var GUI = function(params) { - - var _this = this; - - /** - * Outermost DOM Element - * @type DOMElement - */ - this.domElement = document.createElement('div'); - this.__ul = document.createElement('ul'); - this.domElement.appendChild(this.__ul); - - dom.addClass(this.domElement, CSS_NAMESPACE); - - /** - * Nested GUI's by name - * @ignore - */ - this.__folders = {}; - - this.__controllers = []; - - /** - * List of objects I'm remembering for save, only used in top level GUI - * @ignore - */ - this.__rememberedObjects = []; - - /** - * Maps the index of remembered objects to a map of controllers, only used - * in top level GUI. - * - * @private - * @ignore - * - * @example - * [ - * { - * propertyName: Controller, - * anotherPropertyName: Controller - * }, - * { - * propertyName: Controller - * } - * ] - */ - this.__rememberedObjectIndecesToControllers = []; - - this.__listening = []; - - params = params || {}; - - // Default parameters - params = common.defaults(params, { - autoPlace: true, - width: GUI.DEFAULT_WIDTH - }); - - params = common.defaults(params, { - resizable: params.autoPlace, - hideable: params.autoPlace - }); + if (parameters == undefined) { + parameters = {}; + } - if (!common.isUndefined(params.load)) { + var paramsExplicitHeight = false; + if (parameters.height == undefined) { + parameters.height = 300; + } else { + paramsExplicitHeight = true; + } - // Explicit preset - if (params.preset) params.load.preset = params.preset; + var MIN_WIDTH = 240; + var MAX_WIDTH = 500; - } else { + var controllers = []; + var listening = []; - params.load = { preset: DEFAULT_DEFAULT_PRESET_NAME }; + var autoListen = true; + var listenInterval; + + // Sum total of heights of controllers in this gui + var controllerHeight; + + var _this = this; + + var open = true; + + var width = 280; + if (parameters.width != undefined) { + width = parameters.width; + } + + // Prevents checkForOverflow bug in which loaded gui appearance + // settings are not respected by presence of scrollbar. + var explicitOpenHeight = false; + + // How big we get when we open + var openHeight; + + var closeString = 'Close Controls'; + var openString = 'Open Controls'; + + var name; + + var resizeTo = 0; + var resizeTimeout; + + this.domElement = document.createElement('div'); + this.domElement.setAttribute('class', 'guidat'); + this.domElement.style.width = width + 'px'; + + var curControllerContainerHeight = parameters.height; + var controllerContainer = document.createElement('div'); + controllerContainer.setAttribute('class', 'guidat-controllers'); + controllerContainer.style.height = curControllerContainerHeight + 'px'; + + // Firefox hack to prevent horizontal scrolling + controllerContainer.addEventListener('DOMMouseScroll', function(e) { + + var scrollAmount = this.scrollTop; + + if (e.wheelDelta) { + scrollAmount += e.wheelDelta; + } else if (e.detail) { + scrollAmount += e.detail; } - if (common.isUndefined(params.parent) && params.hideable) { - hideable_guis.push(this); + if (e.preventDefault) { + e.preventDefault(); } - // Only root level GUI's are resizable. - params.resizable = common.isUndefined(params.parent) && params.resizable; + e.returnValue = false; - - if (params.autoPlace && common.isUndefined(params.scrollable)) { - params.scrollable = true; - } -// params.scrollable = common.isUndefined(params.parent) && params.scrollable === true; - - // Not part of params because I don't want people passing this in via - // constructor. Should be a 'remembered' value. - var use_local_storage = - SUPPORTS_LOCAL_STORAGE && - localStorage.getItem(getLocalStorageHash(this, 'isLocal')) === 'true'; - - Object.defineProperties(this, - - /** @lends dat.gui.GUI.prototype */ - { - - /** - * The parent GUI - * @type dat.gui.GUI - */ - parent: { - get: function() { - return params.parent; - } - }, - - scrollable: { - get: function() { - return params.scrollable; - } - }, - - /** - * Handles GUI's element placement for you - * @type Boolean - */ - autoPlace: { - get: function() { - return params.autoPlace; - } - }, - - /** - * The identifier for a set of saved values - * @type String - */ - preset: { - - get: function() { - if (_this.parent) { - return _this.getRoot().preset; - } else { - return params.load.preset; - } - }, - - set: function(v) { - if (_this.parent) { - _this.getRoot().preset = v; - } else { - params.load.preset = v; - } - setPresetSelectIndex(this); - _this.revert(); - } - - }, - - /** - * The width of GUI element - * @type Number - */ - width: { - get: function() { - return params.width; - }, - set: function(v) { - params.width = v; - setWidth(_this, v); - } - }, - - /** - * The name of GUI. Used for folders. i.e - * a folder's name - * @type String - */ - name: { - get: function() { - return params.name; - }, - set: function(v) { - // TODO Check for collisions among sibling folders - params.name = v; - if (title_row_name) { - title_row_name.innerHTML = params.name; - } - } - }, - - /** - * Whether the GUI is collapsed or not - * @type Boolean - */ - closed: { - get: function() { - return params.closed; - }, - set: function(v) { - params.closed = v; - if (params.closed) { - dom.addClass(_this.__ul, GUI.CLASS_CLOSED); - } else { - dom.removeClass(_this.__ul, GUI.CLASS_CLOSED); - } - // For browsers that aren't going to respect the CSS transition, - // Lets just check our height against the window height right off - // the bat. - this.onResize(); - - if (_this.__closeButton) { - _this.__closeButton.innerHTML = v ? GUI.TEXT_OPEN : GUI.TEXT_CLOSED; - } - } - }, - - /** - * Contains all presets - * @type Object - */ - load: { - get: function() { - return params.load; - } - }, - - /** - * Determines whether or not to use localStorage as the means for - * remembering - * @type Boolean - */ - useLocalStorage: { - - get: function() { - return use_local_storage; - }, - set: function(bool) { - if (SUPPORTS_LOCAL_STORAGE) { - use_local_storage = bool; - if (bool) { - dom.bind(window, 'unload', saveToLocalStorage); - } else { - dom.unbind(window, 'unload', saveToLocalStorage); - } - localStorage.setItem(getLocalStorageHash(_this, 'isLocal'), bool); - } - } - - } - - }); - - // Are we a root level GUI? - if (common.isUndefined(params.parent)) { - - params.closed = false; - - dom.addClass(this.domElement, GUI.CLASS_MAIN); - dom.makeSelectable(this.domElement, false); - - // Are we supposed to be loading locally? - if (SUPPORTS_LOCAL_STORAGE) { - - if (use_local_storage) { - - _this.useLocalStorage = true; - - var saved_gui = localStorage.getItem(getLocalStorageHash(this, 'gui')); - - if (saved_gui) { - params.load = JSON.parse(saved_gui); - } - - } - - } - - this.__closeButton = document.createElement('div'); - this.__closeButton.innerHTML = GUI.TEXT_CLOSED; - dom.addClass(this.__closeButton, GUI.CLASS_CLOSE_BUTTON); - this.domElement.appendChild(this.__closeButton); - - dom.bind(this.__closeButton, 'click', function() { - - _this.closed = !_this.closed; - - - }); - - - // Oh, you're a nested GUI! - } else { - - if (params.closed === undefined) { - params.closed = true; - } - - var title_row_name = document.createTextNode(params.name); - dom.addClass(title_row_name, 'controller-name'); - - var title_row = addRow(_this, title_row_name); - - var on_click_title = function(e) { - e.preventDefault(); - _this.closed = !_this.closed; - return false; - }; - - dom.addClass(this.__ul, GUI.CLASS_CLOSED); - - dom.addClass(title_row, 'title'); - dom.bind(title_row, 'click', on_click_title); - - if (!params.closed) { - this.closed = false; - } - - } - - if (params.autoPlace) { - - if (common.isUndefined(params.parent)) { - - if (auto_place_virgin) { - auto_place_container = document.createElement('div'); - dom.addClass(auto_place_container, CSS_NAMESPACE); - dom.addClass(auto_place_container, GUI.CLASS_AUTO_PLACE_CONTAINER); - document.body.appendChild(auto_place_container); - auto_place_virgin = false; - } - - // Put it in the dom for you. - auto_place_container.appendChild(this.domElement); - - // Apply the auto styles - dom.addClass(this.domElement, GUI.CLASS_AUTO_PLACE); - - } - - - // Make it not elastic. - if (!this.parent) setWidth(_this, params.width); - - } - - dom.bind(window, 'resize', function() { _this.onResize() }); - dom.bind(this.__ul, 'webkitTransitionEnd', function() { _this.onResize(); }); - dom.bind(this.__ul, 'transitionend', function() { _this.onResize() }); - dom.bind(this.__ul, 'oTransitionEnd', function() { _this.onResize() }); - this.onResize(); - - - if (params.resizable) { - addResizeHandle(this); - } - - function saveToLocalStorage() { - localStorage.setItem(getLocalStorageHash(_this, 'gui'), JSON.stringify(_this.getSaveObject())); - } - - var root = _this.getRoot(); - function resetWidth() { - var root = _this.getRoot(); - root.width += 1; - common.defer(function() { - root.width -= 1; - }); - } - - if (!params.parent) { - resetWidth(); - } - - }; - - GUI.toggleHide = function() { - - hide = !hide; - common.each(hideable_guis, function(gui) { - gui.domElement.style.zIndex = hide ? -999 : 999; - gui.domElement.style.opacity = hide ? 0 : 1; - }); - }; - - GUI.CLASS_AUTO_PLACE = 'a'; - GUI.CLASS_AUTO_PLACE_CONTAINER = 'ac'; - GUI.CLASS_MAIN = 'main'; - GUI.CLASS_CONTROLLER_ROW = 'cr'; - GUI.CLASS_TOO_TALL = 'taller-than-window'; - GUI.CLASS_CLOSED = 'closed'; - GUI.CLASS_CLOSE_BUTTON = 'close-button'; - GUI.CLASS_DRAG = 'drag'; - - GUI.DEFAULT_WIDTH = 245; - GUI.TEXT_CLOSED = 'Close Controls'; - GUI.TEXT_OPEN = 'Open Controls'; - - dom.bind(window, 'keydown', function(e) { - - if (document.activeElement.type !== 'text' && - (e.which === HIDE_KEY_CODE || e.keyCode == HIDE_KEY_CODE)) { - GUI.toggleHide(); - } + controllerContainer.scrollTop = scrollAmount; }, false); - common.extend( - - GUI.prototype, - - /** @lends dat.gui.GUI */ - { - - /** - * @param object - * @param property - * @returns {dat.controllers.Controller} The new controller that was added. - * @instance - */ - add: function(object, property) { - - return add( - this, - object, - property, - { - factoryArgs: Array.prototype.slice.call(arguments, 2) - } - ); - - }, - - /** - * @param object - * @param property - * @returns {dat.controllers.ColorController} The new controller that was added. - * @instance - */ - addColor: function(object, property) { - - return add( - this, - object, - property, - { - color: true - } - ); - - }, - - /** - * @param controller - * @instance - */ - remove: function(controller) { - - // TODO listening? - this.__ul.removeChild(controller.__li); - this.__controllers.slice(this.__controllers.indexOf(controller), 1); - var _this = this; - common.defer(function() { - _this.onResize(); - }); - - }, - - destroy: function() { - - if (this.autoPlace) { - auto_place_container.removeChild(this.domElement); - } - - }, - - /** - * @param name - * @returns {dat.gui.GUI} The new folder. - * @throws {Error} if this GUI already has a folder by the specified - * name - * @instance - */ - addFolder: function(name) { - - // We have to prevent collisions on names in order to have a key - // by which to remember saved values - if (this.__folders[name] !== undefined) { - throw new Error('You already have a folder in this GUI by the' + - ' name "' + name + '"'); - } - - var new_gui_params = { name: name, parent: this }; - - // We need to pass down the autoPlace trait so that we can - // attach event listeners to open/close folder actions to - // ensure that a scrollbar appears if the window is too short. - new_gui_params.autoPlace = this.autoPlace; - - // Do we have saved appearance data for this folder? - - if (this.load && // Anything loaded? - this.load.folders && // Was my parent a dead-end? - this.load.folders[name]) { // Did daddy remember me? - - // Start me closed if I was closed - new_gui_params.closed = this.load.folders[name].closed; - - // Pass down the loaded data - new_gui_params.load = this.load.folders[name]; - - } - - var gui = new GUI(new_gui_params); - this.__folders[name] = gui; - - var li = addRow(this, gui.domElement); - dom.addClass(li, 'folder'); - return gui; - - }, - - open: function() { - this.closed = false; - }, - - close: function() { - this.closed = true; - }, - - onResize: function() { - - var root = this.getRoot(); - - if (root.scrollable) { - - var top = dom.getOffset(root.__ul).top; - var h = 0; - - common.each(root.__ul.childNodes, function(node) { - if (! (root.autoPlace && node === root.__save_row)) - h += dom.getHeight(node); - }); - - if (window.innerHeight - top - CLOSE_BUTTON_HEIGHT < h) { - dom.addClass(root.domElement, GUI.CLASS_TOO_TALL); - root.__ul.style.height = window.innerHeight - top - CLOSE_BUTTON_HEIGHT + 'px'; - } else { - dom.removeClass(root.domElement, GUI.CLASS_TOO_TALL); - root.__ul.style.height = 'auto'; - } - - } - - if (root.__resize_handle) { - common.defer(function() { - root.__resize_handle.style.height = root.__ul.offsetHeight + 'px'; - }); - } - - if (root.__closeButton) { - root.__closeButton.style.width = root.width + 'px'; - } - - }, - - /** - * Mark objects for saving. The order of these objects cannot change as - * the GUI grows. When remembering new objects, append them to the end - * of the list. - * - * @param {Object...} objects - * @throws {Error} if not called on a top level GUI. - * @instance - */ - remember: function() { - - if (common.isUndefined(SAVE_DIALOGUE)) { - SAVE_DIALOGUE = new CenteredDiv(); - SAVE_DIALOGUE.domElement.innerHTML = saveDialogueContents; - } - - if (this.parent) { - throw new Error("You can only call remember on a top level GUI."); - } - - var _this = this; - - common.each(Array.prototype.slice.call(arguments), function(object) { - if (_this.__rememberedObjects.length == 0) { - addSaveMenu(_this); - } - if (_this.__rememberedObjects.indexOf(object) == -1) { - _this.__rememberedObjects.push(object); - } - }); - - if (this.autoPlace) { - // Set save row width - setWidth(this, this.width); - } - - }, - - /** - * @returns {dat.gui.GUI} the topmost parent GUI of a nested GUI. - * @instance - */ - getRoot: function() { - var gui = this; - while (gui.parent) { - gui = gui.parent; - } - return gui; - }, - - /** - * @returns {Object} a JSON object representing the current state of - * this GUI as well as its remembered properties. - * @instance - */ - getSaveObject: function() { - - var toReturn = this.load; - - toReturn.closed = this.closed; - - // Am I remembering any values? - if (this.__rememberedObjects.length > 0) { - - toReturn.preset = this.preset; - - if (!toReturn.remembered) { - toReturn.remembered = {}; - } - - toReturn.remembered[this.preset] = getCurrentPreset(this); - - } - - toReturn.folders = {}; - common.each(this.__folders, function(element, key) { - toReturn.folders[key] = element.getSaveObject(); - }); - - return toReturn; - - }, - - save: function() { - - if (!this.load.remembered) { - this.load.remembered = {}; - } - - this.load.remembered[this.preset] = getCurrentPreset(this); - markPresetModified(this, false); - - }, - - saveAs: function(presetName) { - - if (!this.load.remembered) { - - // Retain default values upon first save - this.load.remembered = {}; - this.load.remembered[DEFAULT_DEFAULT_PRESET_NAME] = getCurrentPreset(this, true); - - } - - this.load.remembered[presetName] = getCurrentPreset(this); - this.preset = presetName; - addPresetOption(this, presetName, true); - - }, - - revert: function(gui) { - - common.each(this.__controllers, function(controller) { - // Make revert work on Default. - if (!this.getRoot().load.remembered) { - controller.setValue(controller.initialValue); - } else { - recallSavedValue(gui || this.getRoot(), controller); - } - }, this); - - common.each(this.__folders, function(folder) { - folder.revert(folder); - }); - - if (!gui) { - markPresetModified(this.getRoot(), false); - } - - - }, - - listen: function(controller) { - - var init = this.__listening.length == 0; - this.__listening.push(controller); - if (init) updateDisplays(this.__listening); - - } + var toggleButton = document.createElement('a'); + toggleButton.setAttribute('class', 'guidat-toggle'); + toggleButton.setAttribute('href', '#'); + toggleButton.innerHTML = open ? closeString : openString; + + var toggleDragged = false; + var dragDisplacementY = 0; + var dragDisplacementX = 0; + var togglePressed = false; + + var my, pmy, mx, pmx; + + var resize = function(e) { + pmy = my; + pmx = mx; + my = e.pageY; + mx = e.pageX; + + var dmy = my - pmy; + + if (!open) { + if (dmy > 0) { + open = true; + curControllerContainerHeight = openHeight = 1; + toggleButton.innerHTML = name || closeString; + } else { + return; + } + } + + // TODO: Flip this if you want to resize to the left. + var dmx = pmx - mx; + + if (dmy > 0 && + curControllerContainerHeight > controllerHeight) { + var d = DAT.GUI.map(curControllerContainerHeight, controllerHeight, + controllerHeight + 100, 1, 0); + dmy *= d; + } + + toggleDragged = true; + + dragDisplacementY += dmy; + openHeight += dmy; + curControllerContainerHeight += dmy; + controllerContainer.style.height = openHeight + 'px'; + + dragDisplacementX += dmx; + width += dmx; + width = DAT.GUI.constrain(width, MIN_WIDTH, MAX_WIDTH); + _this.domElement.style.width = width + 'px'; + + checkForOverflow(); + + }; + + toggleButton.addEventListener('mousedown', function(e) { + pmy = my = e.pageY; + pmx = mx = e.pageX; + togglePressed = true; + e.preventDefault(); + dragDisplacementX = 0; + dragDisplacementY = 0; + document.addEventListener('mousemove', resize, false); + return false; + + }, false); + + toggleButton.addEventListener('click', function(e) { + e.preventDefault(); + return false; + }, false); + + document.addEventListener('mouseup', function(e) { + + if (togglePressed && !toggleDragged) { + _this.toggle(); + } + + if (togglePressed && toggleDragged) { + + if (dragDisplacementX == 0) { + adaptToScrollbar(); } - ); - - function add(gui, object, property, params) { - - if (object[property] === undefined) { - throw new Error("Object " + object + " has no property \"" + property + "\""); - } - - var controller; - - if (params.color) { - - controller = new ColorController(object, property); - - } else { - - var factoryArgs = [object,property].concat(params.factoryArgs); - controller = controllerFactory.apply(gui, factoryArgs); - - } - - if (params.before instanceof Controller) { - params.before = params.before.__li; - } - - recallSavedValue(gui, controller); - - dom.addClass(controller.domElement, 'c'); - - var name = document.createElement('span'); - dom.addClass(name, 'property-name'); - name.innerHTML = controller.property; - - var container = document.createElement('div'); - container.appendChild(name); - container.appendChild(controller.domElement); - - var li = addRow(gui, container, params.before); - - dom.addClass(li, GUI.CLASS_CONTROLLER_ROW); - dom.addClass(li, typeof controller.getValue()); - - augmentController(gui, li, controller); - - gui.__controllers.push(controller); - - return controller; - - } - - /** - * Add a row to the end of the GUI or before another row. - * - * @param gui - * @param [dom] If specified, inserts the dom content in the new row - * @param [liBefore] If specified, places the new row before another row - */ - function addRow(gui, dom, liBefore) { - var li = document.createElement('li'); - if (dom) li.appendChild(dom); - if (liBefore) { - gui.__ul.insertBefore(li, params.before); - } else { - gui.__ul.appendChild(li); - } - gui.onResize(); - return li; - } - - function augmentController(gui, li, controller) { - - controller.__li = li; - controller.__gui = gui; - - common.extend(controller, { - - options: function(options) { - - if (arguments.length > 1) { - controller.remove(); - - return add( - gui, - controller.object, - controller.property, - { - before: controller.__li.nextElementSibling, - factoryArgs: [common.toArray(arguments)] - } - ); - - } - - if (common.isArray(options) || common.isObject(options)) { - controller.remove(); - - return add( - gui, - controller.object, - controller.property, - { - before: controller.__li.nextElementSibling, - factoryArgs: [options] - } - ); - - } - - }, - - name: function(v) { - controller.__li.firstElementChild.firstElementChild.innerHTML = v; - return controller; - }, - - listen: function() { - controller.__gui.listen(controller); - return controller; - }, - - remove: function() { - controller.__gui.remove(controller); - return controller; - } - - }); - - // All sliders should be accompanied by a box. - if (controller instanceof NumberControllerSlider) { - - var box = new NumberControllerBox(controller.object, controller.property, - { min: controller.__min, max: controller.__max, step: controller.__step }); - - common.each(['updateDisplay', 'onChange', 'onFinishChange'], function(method) { - var pc = controller[method]; - var pb = box[method]; - controller[method] = box[method] = function() { - var args = Array.prototype.slice.call(arguments); - pc.apply(controller, args); - return pb.apply(box, args); - } - }); - - dom.addClass(li, 'has-slider'); - controller.domElement.insertBefore(box.domElement, controller.domElement.firstElementChild); - - } - else if (controller instanceof NumberControllerBox) { - - var r = function(returned) { - - // Have we defined both boundaries? - if (common.isNumber(controller.__min) && common.isNumber(controller.__max)) { - - // Well, then lets just replace this with a slider. - controller.remove(); - return add( - gui, - controller.object, - controller.property, - { - before: controller.__li.nextElementSibling, - factoryArgs: [controller.__min, controller.__max, controller.__step] - }); - - } - - return returned; - - }; - - controller.min = common.compose(r, controller.min); - controller.max = common.compose(r, controller.max); - - } - else if (controller instanceof BooleanController) { - - dom.bind(li, 'click', function() { - dom.fakeEvent(controller.__checkbox, 'click'); - }); - - dom.bind(controller.__checkbox, 'click', function(e) { - e.stopPropagation(); // Prevents double-toggle - }) - - } - else if (controller instanceof FunctionController) { - - dom.bind(li, 'click', function() { - dom.fakeEvent(controller.__button, 'click'); - }); - - dom.bind(li, 'mouseover', function() { - dom.addClass(controller.__button, 'hover'); - }); - - dom.bind(li, 'mouseout', function() { - dom.removeClass(controller.__button, 'hover'); - }); - - } - else if (controller instanceof ColorController) { - - dom.addClass(li, 'color'); - controller.updateDisplay = common.compose(function(r) { - li.style.borderLeftColor = controller.__color.toString(); - return r; - }, controller.updateDisplay); - - controller.updateDisplay(); - - } - - controller.setValue = common.compose(function(r) { - if (gui.getRoot().__preset_select && controller.isModified()) { - markPresetModified(gui.getRoot(), true); - } - return r; - }, controller.setValue); - - } - - function recallSavedValue(gui, controller) { - - // Find the topmost GUI, that's where remembered objects live. - var root = gui.getRoot(); - - // Does the object we're controlling match anything we've been told to - // remember? - var matched_index = root.__rememberedObjects.indexOf(controller.object); - - // Why yes, it does! - if (matched_index != -1) { - - // Let me fetch a map of controllers for thcommon.isObject. - var controller_map = - root.__rememberedObjectIndecesToControllers[matched_index]; - - // Ohp, I believe this is the first controller we've created for this - // object. Lets make the map fresh. - if (controller_map === undefined) { - controller_map = {}; - root.__rememberedObjectIndecesToControllers[matched_index] = - controller_map; - } - - // Keep track of this controller - controller_map[controller.property] = controller; - - // Okay, now have we saved any values for this controller? - if (root.load && root.load.remembered) { - - var preset_map = root.load.remembered; - - // Which preset are we trying to load? - var preset; - - if (preset_map[gui.preset]) { - - preset = preset_map[gui.preset]; - - } else if (preset_map[DEFAULT_DEFAULT_PRESET_NAME]) { - - // Uhh, you can have the default instead? - preset = preset_map[DEFAULT_DEFAULT_PRESET_NAME]; - + if (openHeight > controllerHeight) { + + clearTimeout(resizeTimeout); + openHeight = resizeTo = controllerHeight; + beginResize(); + + } else if (controllerContainer.children.length >= 1) { + + var singleControllerHeight = controllerContainer.children[0]. + offsetHeight; + clearTimeout(resizeTimeout); + var target = Math.round(curControllerContainerHeight / + singleControllerHeight) * singleControllerHeight - 1; + resizeTo = target; + if (resizeTo <= 0) { + _this.close(); + openHeight = singleControllerHeight * 2; } else { + openHeight = resizeTo; + beginResize(); + } + } + } - // Nada. + document.removeEventListener('mousemove', resize, false); + e.preventDefault(); + toggleDragged = false; + togglePressed = false; - return; + return false; + }, false); + + this.domElement.appendChild(controllerContainer); + this.domElement.appendChild(toggleButton); + + if (parameters.domElement) { + parameters.domElement.appendChild(this.domElement); + } else if (DAT.GUI.autoPlace) { + if (DAT.GUI.autoPlaceContainer == null) { + DAT.GUI.autoPlaceContainer = document.createElement('div'); + DAT.GUI.autoPlaceContainer.setAttribute('id', 'guidat'); + + document.body.appendChild(DAT.GUI.autoPlaceContainer); + } + DAT.GUI.autoPlaceContainer.appendChild(this.domElement); + } + + this.autoListenIntervalTime = 1000 / 60; + + var createListenInterval = function() { + listenInterval = setInterval(function() { + _this.listen(); + }, this.autoListenIntervalTime); + }; + + this.__defineSetter__('autoListen', function(v) { + autoListen = v; + if (!autoListen) { + clearInterval(listenInterval); + } else { + if (listening.length > 0) createListenInterval(); + } + }); + + this.__defineGetter__('autoListen', function(v) { + return autoListen; + }); + + this.listenTo = function(controller) { + // TODO: check for duplicates + if (listening.length == 0) { + createListenInterval(); + } + listening.push(controller); + }; + + this.unlistenTo = function(controller) { + // TODO: test this + for (var i = 0; i < listening.length; i++) { + if (listening[i] == controller) listening.splice(i, 1); + } + if (listening.length <= 0) { + clearInterval(listenInterval); + } + }; + + this.listen = function(whoToListenTo) { + var arr = whoToListenTo || listening; + for (var i in arr) { + arr[i].updateDisplay(); + } + }; + + this.listenAll = function() { + this.listen(controllers); + } + + this.autoListen = true; + + var alreadyControlled = function(object, propertyName) { + for (var i in controllers) { + if (controllers[i].object == object && + controllers[i].propertyName == propertyName) { + return true; + } + } + return false; + }; + + var construct = function(constructor, args) { + function C() { + return constructor.apply(this, args); + } + + C.prototype = constructor.prototype; + return new C(); + }; + + this.add = function() { + + + if (arguments.length == 1) { + var toReturn = []; + for (var i in arguments[0]) { + toReturn.push(_this.add(arguments[0], i)); + } + return toReturn; + } + + var object = arguments[0]; + var propertyName = arguments[1]; + + // Have we already added this? + if (alreadyControlled(object, propertyName)) { + // DAT.GUI.error('Controller for \'' + propertyName+'\' already added.'); + // return; + } + + var value = object[propertyName]; + if(value == undefined && object.get) value = object.get(propertyName); + + // Does this value exist? Is it accessible? + if (value == undefined) { + DAT.GUI.error(object + ' either has no property \'' + propertyName + + '\', or the property is inaccessible.'); + return; + } + + var type = typeof value; + var handler = handlerTypes[type]; + + // Do we know how to deal with this data type? + if (handler == undefined) { + DAT.GUI.error('Cannot create controller for data type \'' + type + '\''); + return; + } + + var args = [this]; // Set first arg (parent) to this + for (var j = 0; j < arguments.length; j++) { + args.push(arguments[j]); + } + + var controllerObject = construct(handler, args); + + // Were we able to make the controller? + if (!controllerObject) { + DAT.GUI.error('Error creating controller for \'' + propertyName + '\'.'); + return; + } + + // Success. + controllerContainer.appendChild(controllerObject.domElement); + controllers.push(controllerObject); + DAT.GUI.allControllers.push(controllerObject); + + // Do we have a saved value for this controller? + if (type != 'function' && + DAT.GUI.saveIndex < DAT.GUI.savedValues.length) { + controllerObject.setValue(DAT.GUI.savedValues[DAT.GUI.saveIndex]); + DAT.GUI.saveIndex++; + } + + // Compute sum height of controllers. + checkForOverflow(); + + // Prevents checkForOverflow bug in which loaded gui appearance + // settings are not respected by presence of scrollbar. + if (!explicitOpenHeight) { + openHeight = controllerHeight; + } + + // Let's see if we're doing this on onload and lets *try* to guess how + // big you want the damned box. + if (!paramsExplicitHeight) { + try { + + // Probably a better way to do this + var caller = arguments.callee.caller; + + if (caller == window['onload']) { + curControllerContainerHeight = resizeTo = openHeight = + controllerHeight; + controllerContainer.style.height = curControllerContainerHeight + 'px'; } - - // Did the loaded object remember thcommon.isObject? - if (preset[matched_index] && - - // Did we remember this particular property? - preset[matched_index][controller.property] !== undefined) { - - // We did remember something for this guy ... - var value = preset[matched_index][controller.property]; - - // And that's what it is. - controller.initialValue = value; - controller.setValue(value); - - } - - } - + } catch (e) {} } - } - function getLocalStorageHash(gui, key) { - // TODO how does this deal with multiple GUI's? - return document.location.href + '.' + key; + return controllerObject; } - function addSaveMenu(gui) { - - var div = gui.__save_row = document.createElement('li'); - - dom.addClass(gui.domElement, 'has-save'); - - gui.__ul.insertBefore(div, gui.__ul.firstChild); - - dom.addClass(div, 'save-row'); - - var gears = document.createElement('span'); - gears.innerHTML = ' '; - dom.addClass(gears, 'button gears'); - - // TODO replace with FunctionController - var button = document.createElement('span'); - button.innerHTML = 'Save'; - dom.addClass(button, 'button'); - dom.addClass(button, 'save'); - - var button2 = document.createElement('span'); - button2.innerHTML = 'New'; - dom.addClass(button2, 'button'); - dom.addClass(button2, 'save-as'); - - var button3 = document.createElement('span'); - button3.innerHTML = 'Revert'; - dom.addClass(button3, 'button'); - dom.addClass(button3, 'revert'); - - var select = gui.__preset_select = document.createElement('select'); - - if (gui.load && gui.load.remembered) { - - common.each(gui.load.remembered, function(value, key) { - addPresetOption(gui, key, key == gui.preset); - }); - + var checkForOverflow = function() { + controllerHeight = 0; + for (var i in controllers) { + controllerHeight += controllers[i].domElement.offsetHeight; + } + if (controllerHeight - 1 > openHeight) { + controllerContainer.style.overflowY = 'auto'; } else { - addPresetOption(gui, DEFAULT_DEFAULT_PRESET_NAME, false); + controllerContainer.style.overflowY = 'hidden'; } + }; - dom.bind(select, 'change', function() { + var handlerTypes = { + 'number': DAT.GUI.ControllerNumber, + 'string': DAT.GUI.ControllerString, + 'boolean': DAT.GUI.ControllerBoolean, + 'function': DAT.GUI.ControllerFunction + }; - - for (var index = 0; index < gui.__preset_select.length; index++) { - gui.__preset_select[index].innerHTML = gui.__preset_select[index].value; - } - - gui.preset = this.value; - - }); - - div.appendChild(select); - div.appendChild(gears); - div.appendChild(button); - div.appendChild(button2); - div.appendChild(button3); - - if (SUPPORTS_LOCAL_STORAGE) { - - var saveLocally = document.getElementById('dg-save-locally'); - var explain = document.getElementById('dg-local-explain'); - - saveLocally.style.display = 'block'; - - var localStorageCheckBox = document.getElementById('dg-local-storage'); - - if (localStorage.getItem(getLocalStorageHash(gui, 'isLocal')) === 'true') { - localStorageCheckBox.setAttribute('checked', 'checked'); - } - - function showHideExplain() { - explain.style.display = gui.useLocalStorage ? 'block' : 'none'; - } - - showHideExplain(); - - // TODO: Use a boolean controller, fool! - dom.bind(localStorageCheckBox, 'change', function() { - gui.useLocalStorage = !gui.useLocalStorage; - showHideExplain(); - }); - - } - - var newConstructorTextArea = document.getElementById('dg-new-constructor'); - - dom.bind(newConstructorTextArea, 'keydown', function(e) { - if (e.metaKey && (e.which === 67 || e.keyCode == 67)) { - SAVE_DIALOGUE.hide(); - } - }); - - dom.bind(gears, 'click', function() { - newConstructorTextArea.innerHTML = JSON.stringify(gui.getSaveObject(), undefined, 2); - SAVE_DIALOGUE.show(); - newConstructorTextArea.focus(); - newConstructorTextArea.select(); - }); - - dom.bind(button, 'click', function() { - gui.save(); - }); - - dom.bind(button2, 'click', function() { - var presetName = prompt('Enter a new preset name.'); - if (presetName) gui.saveAs(presetName); - }); - - dom.bind(button3, 'click', function() { - gui.revert(); - }); - -// div.appendChild(button2); - - } - - function addResizeHandle(gui) { - - gui.__resize_handle = document.createElement('div'); - - common.extend(gui.__resize_handle.style, { - - width: '6px', - marginLeft: '-3px', - height: '200px', - cursor: 'ew-resize', - position: 'absolute' -// border: '1px solid blue' - - }); - - var pmouseX; - - dom.bind(gui.__resize_handle, 'mousedown', dragStart); - dom.bind(gui.__closeButton, 'mousedown', dragStart); - - gui.domElement.insertBefore(gui.__resize_handle, gui.domElement.firstElementChild); - - function dragStart(e) { - - e.preventDefault(); - - pmouseX = e.clientX; - - dom.addClass(gui.__closeButton, GUI.CLASS_DRAG); - dom.bind(window, 'mousemove', drag); - dom.bind(window, 'mouseup', dragStop); - - return false; - - } - - function drag(e) { - - e.preventDefault(); - - gui.width += pmouseX - e.clientX; - gui.onResize(); - pmouseX = e.clientX; - - return false; - - } - - function dragStop() { - - dom.removeClass(gui.__closeButton, GUI.CLASS_DRAG); - dom.unbind(window, 'mousemove', drag); - dom.unbind(window, 'mouseup', dragStop); - - } - - } - - function setWidth(gui, w) { - gui.domElement.style.width = w + 'px'; - // Auto placed save-rows are position fixed, so we have to - // set the width manually if we want it to bleed to the edge - if (gui.__save_row && gui.autoPlace) { - gui.__save_row.style.width = w + 'px'; - }if (gui.__closeButton) { - gui.__closeButton.style.width = w + 'px'; + this.reset = function() { + // TODO ... Set all values back to their initials. + for (var i = 0, l = DAT.GUI.allControllers.length; i < l; i++) { + // apply to each controller + DAT.GUI.allControllers[i].reset(); } } - function getCurrentPreset(gui, useInitialValues) { - - var toReturn = {}; - - // For each object I'm remembering - common.each(gui.__rememberedObjects, function(val, index) { - - var saved_values = {}; - - // The controllers I've made for thcommon.isObject by property - var controller_map = - gui.__rememberedObjectIndecesToControllers[index]; - - // Remember each value for each property - common.each(controller_map, function(controller, property) { - saved_values[property] = useInitialValues ? controller.initialValue : controller.getValue(); - }); - - // Save the values for thcommon.isObject - toReturn[index] = saved_values; - - }); - - return toReturn; + this.toggle = function() { + open ? this.close() : this.open(); + }; + this.open = function() { + toggleButton.innerHTML = name || closeString; + resizeTo = openHeight; + clearTimeout(resizeTimeout); + beginResize(); + adaptToScrollbar(); + open = true; } - function addPresetOption(gui, name, setSelected) { - var opt = document.createElement('option'); - opt.innerHTML = name; - opt.value = name; - gui.__preset_select.appendChild(opt); - if (setSelected) { - gui.__preset_select.selectedIndex = gui.__preset_select.length - 1; - } + this.close = function() { + toggleButton.innerHTML = name || openString; + resizeTo = 0; + clearTimeout(resizeTimeout); + beginResize(); + adaptToScrollbar(); + open = false; } - function setPresetSelectIndex(gui) { - for (var index = 0; index < gui.__preset_select.length; index++) { - if (gui.__preset_select[index].value == gui.preset) { - gui.__preset_select.selectedIndex = index; - } - } + this.name = function(n) { + name = n; + toggleButton.innerHTML = n; } - function markPresetModified(gui, modified) { - var opt = gui.__preset_select[gui.__preset_select.selectedIndex]; -// console.log('mark', modified, opt); - if (modified) { - opt.innerHTML = opt.value + "*"; + // used in saveURL + this.appearanceVars = function() { + return [open, width, openHeight, controllerContainer.scrollTop] + } + + var beginResize = function() { + + curControllerContainerHeight = controllerContainer.offsetHeight; + curControllerContainerHeight += (resizeTo - curControllerContainerHeight) + * 0.6; + + if (Math.abs(curControllerContainerHeight - resizeTo) < 1) { + curControllerContainerHeight = resizeTo; } else { - opt.innerHTML = opt.value; + resizeTimeout = setTimeout(beginResize, 1000 / 30); } - } - - function updateDisplays(controllerArray) { - - - if (controllerArray.length != 0) { - - requestAnimationFrame(function() { - updateDisplays(controllerArray); - }); - - } - - common.each(controllerArray, function(c) { - c.updateDisplay(); - }); + controllerContainer.style.height = Math.round(curControllerContainerHeight) + + 'px'; + checkForOverflow(); } - return GUI; + var adaptToScrollbar = function() { + // Clears lingering scrollbar column + _this.domElement.style.width = (width - 1) + 'px'; + setTimeout(function() { + _this.domElement.style.width = width + 'px'; + }, 1); + }; -}); + + // Load saved appearance: + + if (DAT.GUI.guiIndex < DAT.GUI.savedAppearanceVars.length) { + + width = parseInt(DAT.GUI.savedAppearanceVars[DAT.GUI.guiIndex][1]); + _this.domElement.style.width = width + 'px'; + + openHeight = parseInt(DAT.GUI.savedAppearanceVars[DAT.GUI.guiIndex][2]); + explicitOpenHeight = true; + if (eval(DAT.GUI.savedAppearanceVars[DAT.GUI.guiIndex][0]) == true) { + curControllerContainerHeight = openHeight; + var t = DAT.GUI.savedAppearanceVars[DAT.GUI.guiIndex][3] + + // Hack. + setTimeout(function() { + controllerContainer.scrollTop = t; + }, 0); + + if (DAT.GUI.scrollTop > -1) { + document.body.scrollTop = DAT.GUI.scrollTop; + } + resizeTo = openHeight; + this.open(); + } + + DAT.GUI.guiIndex++; + } + + DAT.GUI.allGuis.push(this); + + // Add hide listener if this is the first DAT.GUI. + + if (DAT.GUI.allGuis.length == 1) { + + window.addEventListener('keyup', function(e) { + // Hide on 'H' + if (!DAT.GUI.supressHotKeys && e.keyCode == 72) { + DAT.GUI.toggleHide(); + } + }, false); + + if (DAT.GUI.inlineCSS) { + var styleSheet = document.createElement('style'); + styleSheet.setAttribute('type', 'text/css'); + styleSheet.innerHTML = DAT.GUI.inlineCSS; + document.head.insertBefore(styleSheet, document.head.firstChild); + } + + } + +}; + +// Do not set this directly. +DAT.GUI.hidden = false; + +// Static members + +DAT.GUI.autoPlace = true; +DAT.GUI.autoPlaceContainer = null; +DAT.GUI.allControllers = []; +DAT.GUI.allGuis = []; + +DAT.GUI.supressHotKeys = false; + +DAT.GUI.toggleHide = function() { + if (DAT.GUI.hidden) { + DAT.GUI.open(); + } else { + DAT.GUI.close(); + } +} + +DAT.GUI.open = function() { + DAT.GUI.hidden = false; + for (var i in DAT.GUI.allGuis) { + DAT.GUI.allGuis[i].domElement.style.display = 'block'; + } +} + +DAT.GUI.close = function() { + DAT.GUI.hidden = true; + for (var i in DAT.GUI.allGuis) { + DAT.GUI.allGuis[i].domElement.style.display = 'none'; + } +} + +DAT.GUI.saveURL = function() { + var url = DAT.GUI.replaceGetVar('saveString', DAT.GUI.getSaveString()); + window.location = url; +}; + +DAT.GUI.scrollTop = -1; + +DAT.GUI.load = function(saveString) { + + //DAT.GUI.savedAppearanceVars = []; + var vals = saveString.split(','); + var numGuis = parseInt(vals[0]); + DAT.GUI.scrollTop = parseInt(vals[1]); + for (var i = 0; i < numGuis; i++) { + var appr = vals.splice(2, 4); + DAT.GUI.savedAppearanceVars.push(appr); + } + + DAT.GUI.savedValues = vals.splice(2, vals.length); + +}; + +DAT.GUI.savedValues = []; +DAT.GUI.savedAppearanceVars = []; + +DAT.GUI.getSaveString = function() { + + var vals = [], i; + + vals.push(DAT.GUI.allGuis.length); + vals.push(document.body.scrollTop); + + + for (i in DAT.GUI.allGuis) { + var av = DAT.GUI.allGuis[i].appearanceVars(); + for (var j = 0; j < av.length; j++) { + vals.push(av[j]); + } + } + + for (i in DAT.GUI.allControllers) { + + // We don't save values for functions. + if (DAT.GUI.allControllers[i].type == 'function') { + continue; + } + + var v = DAT.GUI.allControllers[i].getValue(); + + // Round numbers so they don't get enormous + if (DAT.GUI.allControllers[i].type == 'number') { + v = DAT.GUI.roundToDecimal(v, 4); + } + + vals.push(v); + + } + + return vals.join(','); + +}; + +DAT.GUI.getVarFromURL = function(v) { + + var vars = [], hash; + var hashes = window.location.href.slice( + window.location.href.indexOf('?') + 1).split('&'); + + for (var i = 0; i < hashes.length; i++) { + hash = hashes[i].split('='); + if (hash == undefined) continue; + if (hash[0] == v) { + return hash[1]; + } + } + + return null; + +}; + +DAT.GUI.replaceGetVar = function(varName, val) { + + var vars = [], hash; + var loc = window.location.href; + var hashes = window.location.href.slice( + window.location.href.indexOf('?') + 1).split('&'); + + for (var i = 0; i < hashes.length; i++) { + hash = hashes[i].split('='); + if (hash == undefined) continue; + if (hash[0] == varName) { + return loc.replace(hash[1], val); + } + } + + if (window.location.href.indexOf('?') != -1) { + return loc + '&' + varName + '=' + val; + } + + return loc + '?' + varName + '=' + val; + +}; + +DAT.GUI.saveIndex = 0; +DAT.GUI.guiIndex = 0; + +DAT.GUI.showSaveString = function() { + alert(DAT.GUI.getSaveString()); +}; + +// Util functions + +DAT.GUI.makeUnselectable = function(elem) { + if (elem == undefined || elem.style == undefined) return; + elem.onselectstart = function() { + return false; + }; + elem.style.MozUserSelect = 'none'; + elem.style.KhtmlUserSelect = 'none'; + elem.unselectable = 'on'; + + var kids = elem.childNodes; + for (var i = 0; i < kids.length; i++) { + DAT.GUI.makeUnselectable(kids[i]); + } + +}; + +DAT.GUI.makeSelectable = function(elem) { + if (elem == undefined || elem.style == undefined) return; + elem.onselectstart = function() { + }; + elem.style.MozUserSelect = 'auto'; + elem.style.KhtmlUserSelect = 'auto'; + elem.unselectable = 'off'; + + var kids = elem.childNodes; + for (var i = 0; i < kids.length; i++) { + DAT.GUI.makeSelectable(kids[i]); + } + +}; + +DAT.GUI.map = function(v, i1, i2, o1, o2) { + return o1 + (o2 - o1) * ((v - i1) / (i2 - i1)); +}; + +DAT.GUI.constrain = function (v, o1, o2) { + if (v < o1) v = o1; + else if (v > o2) v = o2; + return v; +}; + +DAT.GUI.error = function(str) { + if (typeof console.error == 'function') { + console.error('[DAT.GUI ERROR] ' + str); + } +}; + +DAT.GUI.roundToDecimal = function(n, decimals) { + var t = Math.pow(10, decimals); + return Math.round(n * t) / t; +}; + +DAT.GUI.extendController = function(clazz) { + clazz.prototype = new DAT.GUI.Controller(); + clazz.prototype.constructor = clazz; +}; + +DAT.GUI.addClass = function(domElement, className) { + if (DAT.GUI.hasClass(domElement, className)) return; + domElement.className += ' ' + className; +} + +DAT.GUI.hasClass = function(domElement, className) { + return domElement.className.indexOf(className) != -1; +} + +DAT.GUI.removeClass = function(domElement, className) { + var reg = new RegExp(' ' + className, 'g'); + domElement.className = domElement.className.replace(reg, ''); +} + +if (DAT.GUI.getVarFromURL('saveString') != null) { + DAT.GUI.load(DAT.GUI.getVarFromURL('saveString')); +} diff --git a/utils/build.py b/utils/build.py new file mode 100644 index 0000000..eef69c0 --- /dev/null +++ b/utils/build.py @@ -0,0 +1,186 @@ +#/usr/bin/env python + +from optparse import OptionParser +import httplib, urllib +import os, fnmatch, shutil, re + +usage = """usage: %prog [options] command + +Commands: + build build the script + debug print the header to include js files + clean remove any built files +""" +parser = OptionParser(usage=usage) +parser.add_option('-l', '--level', dest='level', default='SIMPLE_OPTIMIZATIONS', + help='Closure compilation level [WHITESPACE_ONLY, SIMPLE_OPTIMIZATIONS, \ + ADVANCED_OPTIMIZATIONS]') + +UTILS = os.path.dirname(os.path.relpath(__file__)) +PREFIX = os.path.join(UTILS,'..') +SRC_ROOT= os.path.join(PREFIX,'src') +BUILD_ROOT = os.path.join(PREFIX,'build') +INDEX = os.path.join(PREFIX,'index.html') +BUILD_NAME = 'DAT.GUI' +ALL_JS = ['DAT.GUI.js','DAT.GUI'] + +LICENSE = """/** + * dat.gui Javascript Controller Library + * http://dataarts.github.com/dat.gui + * + * Copyright 2011 Data Arts Team, Google Creative Lab + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +""" + +def flatten(l, ltypes=(list, tuple)): + ltype = type(l) + l = list(l) + i = 0 + while i < len(l): + while isinstance(l[i], ltypes): + if not l[i]: + l.pop(i) + i -= 1 + break + else: + l[i:i + 1] = l[i] + i += 1 + return ltype(l) + +def expand(path, globby): + matches = [] + path = path.split('.') + path.insert(0,SRC_ROOT) + filename = "%s.%s"%(path[-2],path[-1]) + if fnmatch.fnmatch(filename, globby): + tmppath = os.path.join(*(path[:-1]+[filename])) + if os.path.exists(tmppath): + path[-1] = filename + else: + path = path[:-2]+[filename] + path = os.path.join(*path) + if os.path.isdir(path): + for root, dirnames, filenames in os.walk(path): + for filename in fnmatch.filter(filenames, globby): + matches.append(os.path.join(root, filename)) + else: + matches.append(path) + return matches + +def unique(seq, idfun=None): + """Ordered uniquify function + if in 2.7 use: + OrderedDict.fromkeys(seq).keys() + """ + if idfun is None: + def idfun(x): return x + seen = {} + result = [] + for item in seq: + marker = idfun(item) + if marker in seen: continue + seen[marker] = 1 + result.append(item) + return result + +def source_list(src, globby='*.js'): + def expander(f): + return expand(f,globby) + return unique(flatten(map(expander, src))) + +def compile(code): + params = urllib.urlencode([ + ('js_code', code), + ('compilation_level', options.level), + ('output_format', 'text'), + ('output_info', 'compiled_code'), + ]) + headers = { 'Content-type': 'application/x-www-form-urlencoded' } + conn = httplib.HTTPConnection('closure-compiler.appspot.com') + conn.request('POST', '/compile', params, headers) + response = conn.getresponse() + data = response.read() + conn.close() + return data + +def bytes_to_kb(b,digits=1): + return round(0.0009765625 * b, digits) + +def clean(): + if os.path.exists(BUILD_ROOT): + shutil.rmtree(BUILD_ROOT) + print('DONE. Removed %s'%(BUILD_ROOT,)) + else: + print('DONE. Nothing to clean') + +def build(jssrc, csssrc=list([''])): + if not os.path.exists(BUILD_ROOT): + os.makedirs(BUILD_ROOT) + + if csssrc: + cssfiles = source_list(csssrc, '*.css') + print('CSS files being compiled: ', cssfiles) + css = '\n'.join([open(f).read() for f in cssfiles]) + css = re.sub(r'[ \t\n\r]+',' ',css) + + jsfiles = source_list(jssrc, '*.js') + print('JS files being compiled: ', jsfiles) + code = '\n'.join([open(f).read() for f in jsfiles]) + if csssrc: + code += """DAT.GUI.inlineCSS = '%s';\n"""%(css,) + + outpath = os.path.join(BUILD_ROOT, BUILD_NAME+'.js') + with open(outpath,'w') as f: + f.write(LICENSE) + f.write(code) + + compiled = compile(code) + outpathmin = os.path.join(BUILD_ROOT, BUILD_NAME+'.min.js') + with open(outpathmin,'w') as f: + f.write(LICENSE) + f.write(compiled) + + size = bytes_to_kb(os.path.getsize(outpath)) + sizemin = bytes_to_kb(os.path.getsize(outpathmin)) + with open(INDEX,'r') as f: + index = f.read() + with open(INDEX,'w') as f: + index = re.sub(r'\[[0-9.]+kb\]','[%skb]'%(size,),index) + index = re.sub(r'\[[0-9.]+kb\]','[%skb]'%(sizemin,),index) + f.write(index) + + print('DONE. Built files in %s.'%(BUILD_ROOT,)) + +def debug(jssrc, csssrc=list([''])): + head = "" + files = source_list(csssrc, '*.css') + for f in files: + f = f.replace(PREFIX+'/','') + head += '\n'%(f,) + files = source_list(jssrc, '*.js') + for f in files: + f = f.replace(PREFIX+'/','') + head += '\n'%(f,) + print(head) + +if __name__ == '__main__': + global options + (options, args) = parser.parse_args() + if len(args) != 1: + print(parser.usage) + exit(0) + command = args[0] + if command == 'build': + build(ALL_JS) + elif command == 'clean': + clean() + elif command == 'debug': + debug(ALL_JS) + +