diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..3c35754 --- /dev/null +++ b/.babelrc @@ -0,0 +1,5 @@ +{ + "stage": 0, + "loose": "all" +} + diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..7734a2a --- /dev/null +++ b/.editorconfig @@ -0,0 +1,17 @@ +# http://editorconfig.org +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 2 +indent_style = space +max_line_length = 80 +trim_trailing_whitespace = true + +[*.md] +max_line_length = 0 +trim_trailing_whitespace = false + +[COMMIT_EDITMSG] +max_line_length = 0 diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..30bf6d6 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,10 @@ +{ + "extends": "eslint-config-airbnb", + "rules": { + "comma-dangle": 0, + "func-names": 0, + "no-alert": 1, + "no-console": 1, + "no-use-before-define": 1 + } +} \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..9fb85ec --- /dev/null +++ b/.gitattributes @@ -0,0 +1,14 @@ +# Set the default behavior, in case people don't have core.autocrlf set. +* text=auto + +# Explicitly declare text files you want to always be normalized and converted +# to native line endings on checkout. +*.c text +*.h text + +# Declare files that will always have CRLF line endings on checkout. +*.sln text eol=crlf + +# Denote all files that are truly binary and should not be modified. +*.png binary +*.jpg binary \ No newline at end of file diff --git a/.gitignore b/.gitignore index b5d1df4..b4a8af4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .DS_Store .sass-cache -.idea +.idea/ +node_modules/ \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..d6868ee --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,26 @@ +## Changelog + +### 0.6.1 + * Fixed all eslint issues + +### 0.6.0 + * Using common.js + * Using webpack for build + * Fixed an issue with colors based on arrays - https://github.com/dataarts/dat.gui/pull/57 + * Update factory.js, Step param was not used - https://github.com/dataarts/dat.gui/pull/45 + +### 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. diff --git a/README.md b/README.md index c384a56..cc72679 100644 --- a/README.md +++ b/README.md @@ -1,87 +1,47 @@ -#dat.GUI +# 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. ----- +## Live demo -##Packaged Builds +Simple live demo [is available here](http://korczis.github.io/dat.gui-ng/). + +## 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: ``` - + ``` ----- +## Directory Contents -##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: ``` - +├── build - Compiled source code. +├── node_modules - External node dependencies. +├── src - Source files. +├── tests - Tests. +└── webpack - Webpack config files. ``` -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 +## Building your own dat.GUI In the terminal, enter the following: ``` -$ cd utils -$ node build_gui.js +$ npm run build ``` +## npm scripts + +- npm test - Run ESLint +- npm run build - Build development and production version of scripts. +- npm run dev - Build development version of script watch for changes. + 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 +## 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/) diff --git a/bower.json b/bower.json index 85fee55..8cd9b65 100644 --- a/bower.json +++ b/bower.json @@ -1,12 +1,12 @@ { - "name": "dat-gui", - "version": "0.5.0", - "homepage": "https://github.com/dataarts/dat.gui", + "name": "dat.gui-ng", + "version": "0.6.1", + "homepage": "https://github.com/korczis/dat.gui.git", "authors": [ "Google Data Arts Team " ], "description": "dat.gui is a lightweight controller library for JavaScript.", - "main": "./build/dat.gui.js", + "main": "./index.js", "keywords": [ "controller", "javascript", diff --git a/build/dat.gui.js b/build/dat.gui.js index b321b51..83f2b5a 100644 --- a/build/dat.gui.js +++ b/build/dat.gui.js @@ -1,3673 +1,9729 @@ -/** - * 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.gui = dat.gui || {}; - -/** @namespace */ -dat.utils = dat.utils || {}; - -/** @namespace */ -dat.controllers = dat.controllers || {}; - -/** @namespace */ -dat.dom = dat.dom || {}; - -/** @namespace */ -dat.color = dat.color || {}; - -dat.utils.css = (function () { - return { - load: function (url, doc) { - doc = doc || document; - var link = doc.createElement('link'); - link.type = 'text/css'; - link.rel = 'stylesheet'; - link.href = url; - doc.getElementsByTagName('head')[0].appendChild(link); - }, - inject: function(css, doc) { - doc = doc || document; - var injected = document.createElement('style'); - injected.type = 'text/css'; - injected.innerHTML = css; - doc.getElementsByTagName('head')[0].appendChild(injected); - } - } -})(); - - -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 (!obj) return; - - if (ARR_EACH && obj.forEach && 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(Math.abs(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; - this.__impliedStep = v; - this.__precision = numDecimals(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)); +(function webpackUniversalModuleDefinition(root, factory) { + if(typeof exports === 'object' && typeof module === 'object') + module.exports = factory(); + else if(typeof define === 'function' && define.amd) + define([], factory); + else if(typeof exports === 'object') + exports["dat"] = factory(); + else + root["dat"] = factory(); +})(this, function() { +return /******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) +/******/ return installedModules[moduleId].exports; +/******/ +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ exports: {}, +/******/ id: moduleId, +/******/ loaded: false +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.loaded = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(0); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * 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 + */ + + 'use strict'; + + exports.__esModule = true; + __webpack_require__(1); + + exports['default'] = __webpack_require__(193); + module.exports = exports['default']; + +/***/ }, +/* 1 */ +/***/ function(module, exports, __webpack_require__) { + + module.exports = __webpack_require__(2); + + +/***/ }, +/* 2 */ +/***/ function(module, exports, __webpack_require__) { + + module.exports = __webpack_require__(3); + + +/***/ }, +/* 3 */ +/***/ function(module, exports, __webpack_require__) { + + /* WEBPACK VAR INJECTION */(function(global) {"use strict"; + + __webpack_require__(4); + + __webpack_require__(191); + + if (global._babelPolyfill) { + throw new Error("only one instance of babel/polyfill is allowed"); + } + global._babelPolyfill = true; + /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()))) + +/***/ }, +/* 4 */ +/***/ function(module, exports, __webpack_require__) { + + __webpack_require__(5); + __webpack_require__(38); + __webpack_require__(44); + __webpack_require__(46); + __webpack_require__(48); + __webpack_require__(50); + __webpack_require__(52); + __webpack_require__(54); + __webpack_require__(55); + __webpack_require__(56); + __webpack_require__(57); + __webpack_require__(58); + __webpack_require__(59); + __webpack_require__(60); + __webpack_require__(61); + __webpack_require__(62); + __webpack_require__(63); + __webpack_require__(64); + __webpack_require__(65); + __webpack_require__(68); + __webpack_require__(69); + __webpack_require__(70); + __webpack_require__(72); + __webpack_require__(73); + __webpack_require__(74); + __webpack_require__(75); + __webpack_require__(76); + __webpack_require__(77); + __webpack_require__(78); + __webpack_require__(80); + __webpack_require__(81); + __webpack_require__(82); + __webpack_require__(84); + __webpack_require__(85); + __webpack_require__(86); + __webpack_require__(88); + __webpack_require__(89); + __webpack_require__(90); + __webpack_require__(91); + __webpack_require__(92); + __webpack_require__(93); + __webpack_require__(94); + __webpack_require__(95); + __webpack_require__(96); + __webpack_require__(97); + __webpack_require__(98); + __webpack_require__(99); + __webpack_require__(100); + __webpack_require__(101); + __webpack_require__(106); + __webpack_require__(107); + __webpack_require__(111); + __webpack_require__(112); + __webpack_require__(114); + __webpack_require__(115); + __webpack_require__(120); + __webpack_require__(121); + __webpack_require__(124); + __webpack_require__(126); + __webpack_require__(128); + __webpack_require__(130); + __webpack_require__(131); + __webpack_require__(132); + __webpack_require__(134); + __webpack_require__(135); + __webpack_require__(137); + __webpack_require__(138); + __webpack_require__(139); + __webpack_require__(140); + __webpack_require__(147); + __webpack_require__(150); + __webpack_require__(151); + __webpack_require__(153); + __webpack_require__(154); + __webpack_require__(155); + __webpack_require__(156); + __webpack_require__(157); + __webpack_require__(158); + __webpack_require__(159); + __webpack_require__(160); + __webpack_require__(161); + __webpack_require__(162); + __webpack_require__(163); + __webpack_require__(164); + __webpack_require__(166); + __webpack_require__(167); + __webpack_require__(168); + __webpack_require__(169); + __webpack_require__(170); + __webpack_require__(171); + __webpack_require__(173); + __webpack_require__(174); + __webpack_require__(175); + __webpack_require__(176); + __webpack_require__(178); + __webpack_require__(179); + __webpack_require__(181); + __webpack_require__(182); + __webpack_require__(184); + __webpack_require__(185); + __webpack_require__(186); + __webpack_require__(189); + __webpack_require__(190); + module.exports = __webpack_require__(9); + +/***/ }, +/* 5 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + var $ = __webpack_require__(6) + , $export = __webpack_require__(7) + , DESCRIPTORS = __webpack_require__(12) + , createDesc = __webpack_require__(11) + , html = __webpack_require__(18) + , cel = __webpack_require__(19) + , has = __webpack_require__(21) + , cof = __webpack_require__(22) + , invoke = __webpack_require__(23) + , fails = __webpack_require__(13) + , anObject = __webpack_require__(24) + , aFunction = __webpack_require__(17) + , isObject = __webpack_require__(20) + , toObject = __webpack_require__(25) + , toIObject = __webpack_require__(27) + , toInteger = __webpack_require__(29) + , toIndex = __webpack_require__(30) + , toLength = __webpack_require__(31) + , IObject = __webpack_require__(28) + , IE_PROTO = __webpack_require__(15)('__proto__') + , createArrayMethod = __webpack_require__(32) + , arrayIndexOf = __webpack_require__(37)(false) + , ObjectProto = Object.prototype + , ArrayProto = Array.prototype + , arraySlice = ArrayProto.slice + , arrayJoin = ArrayProto.join + , defineProperty = $.setDesc + , getOwnDescriptor = $.getDesc + , defineProperties = $.setDescs + , factories = {} + , IE8_DOM_DEFINE; + + if(!DESCRIPTORS){ + IE8_DOM_DEFINE = !fails(function(){ + return defineProperty(cel('div'), 'a', {get: function(){ return 7; }}).a != 7; + }); + $.setDesc = function(O, P, Attributes){ + if(IE8_DOM_DEFINE)try { + return defineProperty(O, P, Attributes); + } catch(e){ /* empty */ } + if('get' in Attributes || 'set' in Attributes)throw TypeError('Accessors not supported!'); + if('value' in Attributes)anObject(O)[P] = Attributes.value; + return O; + }; + $.getDesc = function(O, P){ + if(IE8_DOM_DEFINE)try { + return getOwnDescriptor(O, P); + } catch(e){ /* empty */ } + if(has(O, P))return createDesc(!ObjectProto.propertyIsEnumerable.call(O, P), O[P]); + }; + $.setDescs = defineProperties = function(O, Properties){ + anObject(O); + var keys = $.getKeys(Properties) + , length = keys.length + , i = 0 + , P; + while(length > i)$.setDesc(O, P = keys[i++], Properties[P]); + return O; + }; + } + $export($export.S + $export.F * !DESCRIPTORS, 'Object', { + // 19.1.2.6 / 15.2.3.3 Object.getOwnPropertyDescriptor(O, P) + getOwnPropertyDescriptor: $.getDesc, + // 19.1.2.4 / 15.2.3.6 Object.defineProperty(O, P, Attributes) + defineProperty: $.setDesc, + // 19.1.2.3 / 15.2.3.7 Object.defineProperties(O, Properties) + defineProperties: defineProperties + }); + + // IE 8- don't enum bug keys + var keys1 = ('constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,' + + 'toLocaleString,toString,valueOf').split(',') + // Additional keys for getOwnPropertyNames + , keys2 = keys1.concat('length', 'prototype') + , keysLen1 = keys1.length; + + // Create object with `null` prototype: use iframe Object with cleared prototype + var createDict = function(){ + // Thrash, waste and sodomy: IE GC bug + var iframe = cel('iframe') + , i = keysLen1 + , gt = '>' + , iframeDocument; + iframe.style.display = 'none'; + html.appendChild(iframe); + iframe.src = 'javascript:'; // eslint-disable-line no-script-url + // createDict = iframe.contentWindow.Object; + // html.removeChild(iframe); + iframeDocument = iframe.contentWindow.document; + iframeDocument.open(); + iframeDocument.write(' + \ No newline at end of file diff --git a/utils/license.txt b/index.js similarity index 83% rename from utils/license.txt rename to index.js index dd7aa97..d24dec3 100644 --- a/utils/license.txt +++ b/index.js @@ -10,3 +10,7 @@ * * http://www.apache.org/licenses/LICENSE-2.0 */ + +require('babel/polyfill'); + +export default require('./src/dat/index'); diff --git a/package.json b/package.json new file mode 100644 index 0000000..6434774 --- /dev/null +++ b/package.json @@ -0,0 +1,45 @@ +{ + "name": "dat.gui", + "version": "0.6.1", + "description": "A lightweight graphical user interface for changing variables in JavaScript.", + "main": "index.js", + "directories": { + "test": "tests" + }, + "scripts": { + "test": "node ./node_modules/eslint/bin/eslint.js src/", + "build": "webpack --config ./webpack/webpack.config.js --devtool sourcemap && webpack --config ./webpack/webpack.config.min.js", + "dev": "webpack --progress --colors --watch --config webpack/webpack.config.js --devtool sourcemap" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/korczis/dat.gui.git" + }, + "author": "Data Arts Team, Google Creative Lab", + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/korczis/dat.gui/issues" + }, + "homepage": "https://github.com/korczis/dat.gui#readme", + "devDependencies": { + "babel-core": "^5.8.22", + "babel-eslint": "^4.0.5", + "babel-loader": "^5.3.2", + "css-loader": "^0.16.0", + "esdoc": "^0.2.1", + "esdoc-es7-plugin": "0.0.1", + "eslint": "^1.1.0", + "eslint-config-airbnb": "0.0.7", + "eslint-loader": "^1.0.0", + "eslint-plugin-react": "^3.2.2", + "extend": "^3.0.0", + "html-loader": "^0.3.0", + "node-sass": "^3.2.0", + "sass-loader": "^2.0.0", + "style-loader": "^0.12.3", + "webpack": "^1.11.0" + }, + "dependencies": { + "babel": "^5.8.21" + } +} diff --git a/src/dat/color/Color.js b/src/dat/color/Color.js index 429d314..aeface0 100644 --- a/src/dat/color/Color.js +++ b/src/dat/color/Color.js @@ -11,179 +11,138 @@ * http://www.apache.org/licenses/LICENSE-2.0 */ -define([ - 'dat/color/interpret', - 'dat/color/math', - 'dat/color/toString', - 'dat/utils/common' -], function(interpret, math, toString, common) { - - var Color = function() { +import interpret from './interpret'; +import math from './math'; +import colorToString from './toString'; +import common from '../utils/common'; +class Color { + constructor() { this.__state = interpret.apply(this, arguments); if (this.__state === false) { - throw 'Failed to interpret color arguments'; + throw new Error('Failed to interpret color arguments'); } this.__state.a = this.__state.a || 1; + } + toString() { + return colorToString(this); + } - }; - - 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', { + toOriginal() { + return this.__state.conversion.write(this); + } +} +function defineRGBComponent(target, component, componentHexIndex) { + Object.defineProperty(target, component, { get: function() { - return this.__state.a; + if (this.__state.space === 'RGB') { + return this.__state[component]; + } + + Color.recalculateRGB(this, component, componentHexIndex); + + return this.__state[component]; }, 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); + if (this.__state.space !== 'RGB') { + Color.recalculateRGB(this, component, componentHexIndex); + this.__state.space = 'RGB'; } - return this.__state.hex; + this.__state[component] = v; + } + }); +} +function defineHSVComponent(target, component) { + Object.defineProperty(target, component, { + get: function() { + if (this.__state.space === 'HSV') { + return this.__state[component]; + } + + Color.recalculateHSV(this); + + return this.__state[component]; }, set: function(v) { + if (this.__state.space !== 'HSV') { + Color.recalculateHSV(this); + this.__state.space = 'HSV'; + } - this.__state.space = 'HEX'; - this.__state.hex = v; - + this.__state[component] = 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; - - } - - }); +Color.recalculateRGB = function(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 new Error('Corrupted color state'); } +}; - 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'; +Color.recalculateHSV = function(color) { + const 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; } +}; - function recalculateHSV(color) { +Color.COMPONENTS = ['r', 'g', 'b', 'h', 's', 'v', 'hex', 'a']; - var result = math.rgb_to_hsv(color.r, color.g, color.b); +defineRGBComponent(Color.prototype, 'r', 2); +defineRGBComponent(Color.prototype, 'g', 1); +defineRGBComponent(Color.prototype, 'b', 0); - common.extend(color.__state, - { - s: result.s, - v: result.v - } - ); +defineHSVComponent(Color.prototype, 'h'); +defineHSVComponent(Color.prototype, 's'); +defineHSVComponent(Color.prototype, 'v'); - if (!common.isNaN(result.h)) { - color.__state.h = result.h; - } else if (common.isUndefined(color.__state.h)) { - color.__state.h = 0; - } +Object.defineProperty(Color.prototype, 'a', { + get: function() { + return this.__state.a; + }, + set: function(v) { + this.__state.a = v; } - - return Color; - }); + +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; + } +}); + +export default Color; diff --git a/src/dat/color/interpret.js b/src/dat/color/interpret.js index 8e64d03..fd22637 100644 --- a/src/dat/color/interpret.js +++ b/src/dat/color/interpret.js @@ -11,330 +11,290 @@ * http://www.apache.org/licenses/LICENSE-2.0 */ -define([ - 'dat/color/toString', - 'dat/utils/common' -], 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 - } - } +import toString from './toString'; +import common from '../utils/common'; + +const INTERPRETATIONS = [ + // Strings + { + litmus: common.isString, + conversions: { + THREE_CHAR_HEX: { + read: function(original) { + const test = original.match(/^#([A-F0-9])([A-F0-9])([A-F0-9])$/i); + if (test === null) { return false; - }, - - write: function(color) { - return { - r: color.r, - g: color.g, - b: color.b, - a: color.a - } } + + return { + space: 'HEX', + hex: parseInt( + '0x' + + test[1].toString() + test[1].toString() + + test[2].toString() + test[2].toString() + + test[3].toString() + test[3].toString(), 0) + }; }, - 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: toString + }, - write: function(color) { - return { - r: color.r, - g: color.g, - b: color.b - } + SIX_CHAR_HEX: { + read: function(original) { + const test = original.match(/^#([A-F0-9]{6})$/i); + if (test === null) { + return false; } + + return { + space: 'HEX', + hex: parseInt('0x' + test[1].toString(), 0) + }; }, - 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: toString + }, - write: function(color) { - return { - h: color.h, - s: color.s, - v: color.v, - a: color.a - } + CSS_RGB: { + read: function(original) { + const 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]) + }; }, - 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: toString + }, - write: function(color) { - return { - h: color.h, - s: color.s, - v: color.v - } + CSS_RGBA: { + read: function(original) { + const 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 interpret; + return { + space: 'RGB', + r: original[0], + g: original[1], + b: original[2] + }; + }, + write: function(color) { + return [color.r, color.g, color.b]; + } + }, -}); \ No newline at end of file + 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 + }; + } + } + } + } +]; + +let result; +let toReturn; + +const interpret = function() { + toReturn = false; + + const 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; +}; + +export default interpret; diff --git a/src/dat/color/math.js b/src/dat/color/math.js index 6be2613..e9f02d5 100644 --- a/src/dat/color/math.js +++ b/src/dat/color/math.js @@ -11,90 +11,83 @@ * http://www.apache.org/licenses/LICENSE-2.0 */ -define([ +let tmpComponent; -], function() { +const ColorMath = { + hsv_to_rgb: function(h, s, v) { + const hi = Math.floor(h / 60) % 6; - var tmpComponent; + const f = h / 60 - Math.floor(h / 60); + const p = v * (1.0 - s); + const q = v * (1.0 - (f * s)); + const t = v * (1.0 - ((1.0 - f) * s)); - return { + const c = [ + [v, t, p], + [q, v, p], + [p, v, t], + [p, q, v], + [t, p, v], + [v, p, q] + ][hi]; - hsv_to_rgb: function(h, s, v) { + return { + r: c[0] * 255, + g: c[1] * 255, + b: c[2] * 255 + }; + }, - 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]; + rgb_to_hsv: function(r, g, b) { + const min = Math.min(r, g, b); + const max = Math.max(r, g, b); + const delta = max - min; + let h; + let s; + if (max !== 0) { + s = delta / max; + } else { return { - r: c[0] * 255, - g: c[1] * 255, - b: c[2] * 255 + h: NaN, + s: 0, + v: 0 }; - - }, - - 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)); } - } + 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; + } -}); \ No newline at end of file + return { + h: h * 360, + s: s, + v: max / 255 + }; + }, + + rgb_to_hex: function(r, g, b) { + let 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)); + } +}; + +export default ColorMath; diff --git a/src/dat/color/toString.js b/src/dat/color/toString.js index c052a79..ec87966 100644 --- a/src/dat/color/toString.js +++ b/src/dat/color/toString.js @@ -11,27 +11,16 @@ * http://www.apache.org/licenses/LICENSE-2.0 */ -define([ - 'dat/utils/common' -], 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 + ')'; +import common from '../utils/common'; +export default function(color) { + if (color.a === 1 || common.isUndefined(color.a)) { + let s = color.hex.toString(16); + while (s.length < 6) { + s = '0' + s; } - + return '#' + s; } -}); \ No newline at end of file + return 'rgba(' + Math.round(color.r) + ',' + Math.round(color.g) + ',' + Math.round(color.b) + ',' + color.a + ')'; +} diff --git a/src/dat/controllers/BooleanController.js b/src/dat/controllers/BooleanController.js index 49ddf57..8c06d89 100644 --- a/src/dat/controllers/BooleanController.js +++ b/src/dat/controllers/BooleanController.js @@ -11,31 +11,31 @@ * http://www.apache.org/licenses/LICENSE-2.0 */ -define([ - 'dat/controllers/Controller', - 'dat/dom/dom', - 'dat/utils/common' -], function(Controller, dom, common) { +import Controller from './Controller'; +import dom from '../dom/dom'; - /** - * @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) { +/** + * @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 + */ +class BooleanController extends Controller { + constructor(object, property) { + super(object, property); - BooleanController.superclass.call(this, object, property); - - var _this = this; + const _this = this; this.__prev = this.getValue(); this.__checkbox = document.createElement('input'); this.__checkbox.setAttribute('type', 'checkbox'); + function onChange() { + _this.setValue(!_this.__prev); + } dom.bind(this.__checkbox, 'change', onChange, false); @@ -43,49 +43,27 @@ define([ // Match original value this.updateDisplay(); + } - function onChange() { - _this.setValue(!_this.__prev); + setValue(v) { + const toReturn = super.setValue(v); + if (this.__onFinishChange) { + this.__onFinishChange.call(this, this.getValue()); + } + this.__prev = this.getValue(); + return toReturn; + } + + updateDisplay() { + if (this.getValue() === true) { + this.__checkbox.setAttribute('checked', 'checked'); + this.__checkbox.checked = true; + } else { + this.__checkbox.checked = false; } - }; + return super.updateDisplay(); + } +} - 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; - -}); \ No newline at end of file +export default BooleanController; diff --git a/src/dat/controllers/ColorController.js b/src/dat/controllers/ColorController.js index eef8978..495d14a 100644 --- a/src/dat/controllers/ColorController.js +++ b/src/dat/controllers/ColorController.js @@ -11,22 +11,20 @@ * http://www.apache.org/licenses/LICENSE-2.0 */ -define([ - 'dat/controllers/Controller', - 'dat/dom/dom', - 'dat/color/Color', - 'dat/color/interpret', - 'dat/utils/common' -], function(Controller, dom, Color, interpret, common) { +import Controller from './Controller'; +import dom from '../dom/dom'; +import Color from '../color/Color'; +import interpret from '../color/interpret'; +import common from '../utils/common'; - var ColorController = function(object, property) { - - ColorController.superclass.call(this, object, property); +class ColorController extends Controller { + constructor(object, property) { + super(object, property); this.__color = new Color(this.getValue()); this.__temp = new Color(0); - var _this = this; + const _this = this; this.domElement = document.createElement('div'); @@ -60,17 +58,15 @@ define([ dom.bind(this.__input, 'blur', onBlur); - dom.bind(this.__selector, 'mousedown', function(e) { - + dom.bind(this.__selector, 'mousedown', function(/* e */) { dom .addClass(this, 'drag') - .bind(window, 'mouseup', function(e) { + .bind(window, 'mouseup', function(/* e */) { dom.removeClass(_this.__selector, 'drag'); }); - }); - var value_field = document.createElement('div'); + const valueField = document.createElement('div'); common.extend(this.__selector.style, { width: '122px', @@ -84,12 +80,12 @@ define([ position: 'absolute', width: '12px', height: '12px', - border: this.__field_knob_border + (this.__color.v < .5 ? '#fff' : '#000'), + 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 }); - + common.extend(this.__hue_knob.style, { position: 'absolute', width: '15px', @@ -107,13 +103,13 @@ define([ cursor: 'pointer' }); - common.extend(value_field.style, { + common.extend(valueField.style, { width: '100%', height: '100%', background: 'none' }); - - linearGradient(value_field, 'top', 'rgba(0,0,0,0)', '#000'); + + linearGradient(valueField, 'top', 'rgba(0,0,0,0)', '#000'); common.extend(this.__hue_field.style, { width: '15px', @@ -160,7 +156,7 @@ define([ } function onBlur() { - var i = interpret(this.value); + const i = interpret(this.value); if (i !== false) { _this.__color.__state = i; _this.setValue(_this.__color.toOriginal()); @@ -174,7 +170,7 @@ define([ dom.unbind(window, 'mouseup', unbindH); } - this.__saturation_field.appendChild(value_field); + this.__saturation_field.appendChild(valueField); this.__selector.appendChild(this.__field_knob); this.__selector.appendChild(this.__saturation_field); this.__selector.appendChild(this.__hue_field); @@ -186,19 +182,24 @@ define([ 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; + const w = dom.getWidth(_this.__saturation_field); + const o = dom.getOffset(_this.__saturation_field); + let s = (e.clientX - o.left + document.body.scrollLeft) / w; + let v = 1 - (e.clientY - o.top + document.body.scrollTop) / w; - if (v > 1) v = 1; - else if (v < 0) v = 0; + if (v > 1) { + v = 1; + } else if (v < 0) { + v = 0; + } - if (s > 1) s = 1; - else if (s < 0) s = 0; + if (s > 1) { + s = 1; + } else if (s < 0) { + s = 0; + } _this.__color.v = v; _this.__color.s = s; @@ -207,118 +208,97 @@ define([ 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; + const s = dom.getHeight(_this.__hue_field); + const o = dom.getOffset(_this.__hue_field); + let h = 1 - (e.clientY - o.top + document.body.scrollTop) / s; - if (h > 1) h = 1; - else if (h < 0) h = 0; + if (h > 1) { + h = 1; + } else if (h < 0) { + h = 0; + } _this.__color.h = h * 360; _this.setValue(_this.__color.toOriginal()); return false; + } + } + updateDisplay() { + const i = interpret(this.getValue()); + + if (i !== false) { + let 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); - ColorController.superclass = Controller; + this.__temp.a = 1; - common.extend( + const flip = (this.__color.v < 0.5 || this.__color.s > 0.5) ? 255 : 0; + const _flip = 255 - flip; - ColorController.prototype, - Controller.prototype, + 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'; - updateDisplay: function() { + this.__temp.s = 1; + this.__temp.v = 1; - var i = interpret(this.getValue()); + linearGradient(this.__saturation_field, 'left', '#fff', this.__temp.toString()); - 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%); '; + 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)' }); } - - 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%);' - } +} +const vendors = ['-moz-', '-o-', '-webkit-', '-ms-', '']; - return ColorController; +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%); '; + }); +} -}); \ No newline at end of file +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%);'; +} + +export default ColorController; diff --git a/src/dat/controllers/Controller.js b/src/dat/controllers/Controller.js index 3a44df3..56041a3 100644 --- a/src/dat/controllers/Controller.js +++ b/src/dat/controllers/Controller.js @@ -11,20 +11,16 @@ * http://www.apache.org/licenses/LICENSE-2.0 */ -define([ - 'dat/utils/common' -], 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) { - +/** + * @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 + */ +class Controller { + constructor(object, property) { this.initialValue = object[property]; /** @@ -58,87 +54,74 @@ define([ * @ignore */ this.__onFinishChange = undefined; + } - }; + /** + * 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 {Controller} this + */ + onChange(fnc) { + this.__onChange = fnc; + return this; + } - common.extend( + /** + * 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 {Controller} this + */ + onFinishChange(fnc) { + this.__onFinishChange = fnc; + return this; + } - Controller.prototype, + /** + * Change the value of object[property] + * + * @param {Object} newValue The new value of object[property] + */ + setValue(newValue) { + this.object[this.property] = newValue; + if (this.__onChange) { + this.__onChange.call(this, newValue); + } - /** @lends dat.controllers.Controller.prototype */ - { + this.updateDisplay(); + return this; + } - /** - * 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; - }, + /** + * Gets the value of object[property] + * + * @returns {Object} The current value of object[property] + */ + getValue() { + return this.object[this.property]; + } - /** - * 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; - }, + /** + * Refreshes the visual display of a Controller in order to keep sync + * with the object's current value. + * @returns {Controller} this + */ + updateDisplay() { + 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; - }, + /** + * @returns {Boolean} true if the value has deviated from initialValue + */ + isModified() { + return this.initialValue !== this.getValue(); + } +} - /** - * 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; - - -}); \ No newline at end of file +export default Controller; diff --git a/src/dat/controllers/ControllerFactory.js b/src/dat/controllers/ControllerFactory.js new file mode 100644 index 0000000..eb42e92 --- /dev/null +++ b/src/dat/controllers/ControllerFactory.js @@ -0,0 +1,56 @@ +/** + * 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 + */ + +import OptionController from './OptionController'; +import NumberControllerBox from './NumberControllerBox'; +import NumberControllerSlider from './NumberControllerSlider'; +import StringController from './StringController'; +import FunctionController from './FunctionController'; +import BooleanController from './BooleanController'; +import common from '../utils/common'; + +const ControllerFactory = function(object, property) { + const 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. + if (common.isNumber(arguments[4])) { // has step + return new NumberControllerSlider(object, property, arguments[2], arguments[3], arguments[4]); + } + + return new NumberControllerSlider(object, property, arguments[2], arguments[3]); + } + 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); + } +}; + +export default ControllerFactory; diff --git a/src/dat/controllers/FunctionController.js b/src/dat/controllers/FunctionController.js index e7748cc..4873754 100644 --- a/src/dat/controllers/FunctionController.js +++ b/src/dat/controllers/FunctionController.js @@ -11,30 +11,28 @@ * http://www.apache.org/licenses/LICENSE-2.0 */ -define([ - 'dat/controllers/Controller', - 'dat/dom/dom', - 'dat/utils/common' -], function(Controller, dom, common) { +import Controller from './Controller'; +import dom from '../dom/dom'; - /** - * @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) { +/** + * @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 + */ +class FunctionController extends Controller { + constructor(object, property, text) { + super(object, property); - FunctionController.superclass.call(this, object, property); - - var _this = this; + const _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(); @@ -44,31 +42,17 @@ define([ dom.addClass(this.__button, 'button'); this.domElement.appendChild(this.__button); + } + fire() { + if (this.__onChange) { + this.__onChange.call(this); + } + this.getValue().call(this.object); + if (this.__onFinishChange) { + this.__onFinishChange.call(this, this.getValue()); + } + } +} - }; - - FunctionController.superclass = Controller; - - common.extend( - - FunctionController.prototype, - Controller.prototype, - { - - fire: function() { - if (this.__onChange) { - this.__onChange.call(this); - } - this.getValue().call(this.object); - if (this.__onFinishChange) { - this.__onFinishChange.call(this, this.getValue()); - } - } - } - - ); - - return FunctionController; - -}); +export default FunctionController; diff --git a/src/dat/controllers/NumberController.js b/src/dat/controllers/NumberController.js index 4cab081..dd3aef6 100644 --- a/src/dat/controllers/NumberController.js +++ b/src/dat/controllers/NumberController.js @@ -11,135 +11,112 @@ * http://www.apache.org/licenses/LICENSE-2.0 */ -define([ - 'dat/controllers/Controller', - 'dat/utils/common' -], function(Controller, common) { +import Controller from './Controller'; +import common from '../utils/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) { +function numDecimals(x) { + const _x = x.toString(); + if (_x.indexOf('.') > -1) { + return _x.length - _x.indexOf('.') - 1; + } - NumberController.superclass.call(this, object, property); + return 0; +} - params = params || {}; +/** + * @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 + */ +class NumberController extends Controller { + constructor(object, property, params) { + super(object, property); - this.__min = params.min; - this.__max = params.max; - this.__step = params.step; + const _params = params || {}; + + this.__min = _params.min; + this.__max = _params.max; + this.__step = _params.step; if (common.isUndefined(this.__step)) { - - if (this.initialValue == 0) { + 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(Math.abs(this.initialValue))/Math.LN10))/10; + this.__impliedStep = Math.pow(10, Math.floor(Math.log(Math.abs(this.initialValue)) / Math.LN10)) / 10; } - } else { - - this.__impliedStep = this.__step; - + 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; - this.__impliedStep = v; - this.__precision = numDecimals(v); - return this; - } - - } - - ); - - function numDecimals(x) { - x = x.toString(); - if (x.indexOf('.') > -1) { - return x.length - x.indexOf('.') - 1; - } else { - return 0; - } } - return NumberController; + setValue(v) { + let _v = 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 super.setValue(_v); + } + + /** + * Specify a minimum value for object[property]. + * + * @param {Number} minValue The minimum value for + * object[property] + * @returns {dat.controllers.NumberController} this + */ + min(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(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(v) { + this.__step = v; + this.__impliedStep = v; + this.__precision = numDecimals(v); + return this; + } +} + +export default NumberController; diff --git a/src/dat/controllers/NumberControllerBox.js b/src/dat/controllers/NumberControllerBox.js index 6ae574d..35be91b 100644 --- a/src/dat/controllers/NumberControllerBox.js +++ b/src/dat/controllers/NumberControllerBox.js @@ -11,41 +11,76 @@ * http://www.apache.org/licenses/LICENSE-2.0 */ -define([ - 'dat/controllers/NumberController', - 'dat/dom/dom', - 'dat/utils/common' -], function(NumberController, dom, common) { +import NumberController from './NumberController'; +import dom from '../dom/dom'; +import common from '../utils/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) { +function roundToDecimal(value, decimals) { + const tenTo = Math.pow(10, decimals); + return Math.round(value * tenTo) / tenTo; +} + +/** + * @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 + */ +class NumberControllerBox extends NumberController { + constructor(object, property, params) { + super(object, property, params); this.__truncationSuspended = false; - NumberControllerBox.superclass.call(this, object, property, params); - - var _this = this; + const _this = this; /** * {Number} Previous mouse y position * @ignore */ - var prev_y; + let prevY; + + function onChange() { + const 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 onMouseDrag(e) { + const diff = prevY - e.clientY; + _this.setValue(_this.getValue() + diff * _this.__impliedStep); + + prevY = e.clientY; + } + + function onMouseUp() { + dom.unbind(window, 'mousemove', onMouseDrag); + dom.unbind(window, 'mouseup', onMouseUp); + } + + function onMouseDown(e) { + dom.bind(window, 'mousemove', onMouseDrag); + dom.bind(window, 'mouseup', onMouseUp); + prevY = e.clientY; + } this.__input = document.createElement('input'); this.__input.setAttribute('type', 'text'); @@ -56,79 +91,25 @@ define([ 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; + updateDisplay() { + this.__input.value = this.__truncationSuspended ? this.getValue() : roundToDecimal(this.getValue(), this.__precision); + return super.updateDisplay(); + } +} + +export default NumberControllerBox; -}); diff --git a/src/dat/controllers/NumberControllerSlider.js b/src/dat/controllers/NumberControllerSlider.js index 6794008..c83209b 100644 --- a/src/dat/controllers/NumberControllerSlider.js +++ b/src/dat/controllers/NumberControllerSlider.js @@ -11,51 +11,48 @@ * http://www.apache.org/licenses/LICENSE-2.0 */ -define([ - 'dat/controllers/NumberController', - 'dat/dom/dom', - 'dat/utils/css', - 'dat/utils/common', - 'text!dat/controllers/NumberControllerSlider.css' -], -function(NumberController, dom, css, common, styleSheet) { +import NumberController from './NumberController'; +import dom from '../dom/dom'; +import css from '../utils/css'; +import styleSheet from '!css!sass!./NumberControllerSlider.scss'; - /** - * @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) { +function map(v, i1, i2, o1, o2) { + return o1 + (o2 - o1) * ((v - i1) / (i2 - i1)); +} - NumberControllerSlider.superclass.call(this, object, property, { min: min, max: max, step: step }); +/** + * @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 + */ +class NumberControllerSlider extends NumberController { + constructor(object, property, min, max, step) { + super(object, property, {min: min, max: max, step: step}); - var _this = this; + const _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); @@ -63,18 +60,16 @@ function(NumberController, dom, css, common, styleSheet) { } function onMouseDrag(e) { - e.preventDefault(); - var offset = dom.getOffset(_this.__background); - var width = dom.getWidth(_this.__background); - + const offset = dom.getOffset(_this.__background); + const width = dom.getWidth(_this.__background); + _this.setValue( - map(e.clientX, offset.left, offset.left + width, _this.__min, _this.__max) + map(e.clientX, offset.left, offset.left + width, _this.__min, _this.__max) ); return false; - } function onMouseUp() { @@ -89,41 +84,20 @@ function(NumberController, dom, css, common, styleSheet) { this.__background.appendChild(this.__foreground); this.domElement.appendChild(this.__background); + } - }; + updateDisplay() { + const pct = (this.getValue() - this.__min) / (this.__max - this.__min); + this.__foreground.style.width = pct * 100 + '%'; + return super.updateDisplay(); + } +} - NumberControllerSlider.superclass = NumberController; +/** + * Injects default stylesheet for slider elements. + */ +NumberControllerSlider.useDefaultStyles = function() { + css.inject(styleSheet.toString()); +}; - /** - * 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; - -}); \ No newline at end of file +export default NumberControllerSlider; diff --git a/src/dat/controllers/NumberControllerSlider.css b/src/dat/controllers/NumberControllerSlider.scss similarity index 75% rename from src/dat/controllers/NumberControllerSlider.css rename to src/dat/controllers/NumberControllerSlider.scss index c52186c..0cae8f6 100644 --- a/src/dat/controllers/NumberControllerSlider.css +++ b/src/dat/controllers/NumberControllerSlider.scss @@ -12,20 +12,16 @@ */ .slider { - box-shadow: inset 0 2px 4px rgba(0,0,0,0.15); + box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15); height: 1em; - border-radius: 1em; + border-radius: 1em 0 0 1em; background-color: #eee; - padding: 0 0.5em; - overflow: hidden; } .slider-fg { - padding: 1px 0 2px 0; background-color: #aaa; height: 1em; margin-left: -0.5em; - padding-right: 0.5em; border-radius: 1em 0 0 1em; } @@ -33,11 +29,11 @@ display: inline-block; border-radius: 1em; background-color: #fff; - border: 1px solid #aaa; + border: 1px solid #aaa; content: ''; float: right; - margin-right: -1em; - margin-top: -1px; + margin-right: -0.5em; + margin-top: 3px; height: 0.9em; width: 0.9em; } \ No newline at end of file diff --git a/src/dat/controllers/OptionController.js b/src/dat/controllers/OptionController.js index 1e9c53e..60ce434 100644 --- a/src/dat/controllers/OptionController.js +++ b/src/dat/controllers/OptionController.js @@ -11,31 +11,30 @@ * http://www.apache.org/licenses/LICENSE-2.0 */ -define([ - 'dat/controllers/Controller', - 'dat/dom/dom', - 'dat/utils/common' -], -function(Controller, dom, common) { +import Controller from './Controller'; +import dom from '../dom/dom'; +import common from '../utils/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) { +/** + * @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 + */ +class OptionController extends Controller { + constructor(object, property, opts) { + super(object, property); - OptionController.superclass.call(this, object, property); + let options = opts; - var _this = this; + const _this = this; /** * The drop down menu @@ -44,7 +43,7 @@ function(Controller, dom, common) { this.__select = document.createElement('select'); if (common.isArray(options)) { - var map = {}; + const map = {}; common.each(options, function(element) { map[element] = element; }); @@ -52,52 +51,36 @@ function(Controller, dom, common) { } common.each(options, function(value, key) { - - var opt = document.createElement('option'); + const 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; + const desiredValue = this.options[this.selectedIndex].value; _this.setValue(desiredValue); }); this.domElement.appendChild(this.__select); + } - }; + setValue(v) { + const toReturn = super.setValue(v); - OptionController.superclass = Controller; + if (this.__onFinishChange) { + this.__onFinishChange.call(this, this.getValue()); + } + return toReturn; + } - common.extend( + updateDisplay() { + this.__select.value = this.getValue(); + return super.updateDisplay(); + } +} - 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; - -}); \ No newline at end of file +export default OptionController; diff --git a/src/dat/controllers/StringController.js b/src/dat/controllers/StringController.js index 75a5fd2..a11ae0a 100644 --- a/src/dat/controllers/StringController.js +++ b/src/dat/controllers/StringController.js @@ -11,27 +11,34 @@ * http://www.apache.org/licenses/LICENSE-2.0 */ -define([ - 'dat/controllers/Controller', - 'dat/dom/dom', - 'dat/utils/common' -], function(Controller, dom, common) { +import Controller from './Controller'; +import dom from '../dom/dom'; - /** - * @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) { +/** + * @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 + */ +class StringController extends Controller { + constructor(object, property) { + super(object, property); - StringController.superclass.call(this, object, property); + const _this = this; - var _this = this; + function onChange() { + _this.setValue(_this.__input.value); + } + + function onBlur() { + if (_this.__onFinishChange) { + _this.__onFinishChange.call(_this, _this.getValue()); + } + } this.__input = document.createElement('input'); this.__input.setAttribute('type', 'text'); @@ -44,46 +51,20 @@ define([ 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); + } - }; + updateDisplay() { + // Stops the caret from moving on account of: + // keyup -> setValue -> updateDisplay + if (!dom.isActive(this.__input)) { + this.__input.value = this.getValue(); + } + return super.updateDisplay(); + } +} - 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; - -}); \ No newline at end of file +export default StringController; diff --git a/src/dat/controllers/factory.js b/src/dat/controllers/factory.js deleted file mode 100644 index 897b267..0000000 --- a/src/dat/controllers/factory.js +++ /dev/null @@ -1,65 +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 - */ - -define([ - 'dat/controllers/OptionController', - 'dat/controllers/NumberControllerBox', - 'dat/controllers/NumberControllerSlider', - 'dat/controllers/StringController', - 'dat/controllers/FunctionController', - 'dat/controllers/BooleanController', - 'dat/utils/common' -], - 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); - } - - } - - }); \ No newline at end of file diff --git a/src/dat/dom/CenteredDiv.js b/src/dat/dom/CenteredDiv.js index 98f0602..a422ba5 100644 --- a/src/dat/dom/CenteredDiv.js +++ b/src/dat/dom/CenteredDiv.js @@ -11,14 +11,11 @@ * http://www.apache.org/licenses/LICENSE-2.0 */ -define([ - 'dat/dom/dom', - 'dat/utils/common' -], function(dom, common) { - - - var CenteredDiv = function() { +import dom from '../dom/dom'; +import common from '../utils/common'; +class CenteredDiv { + constructor() { this.backgroundElement = document.createElement('div'); common.extend(this.backgroundElement.style, { backgroundColor: 'rgba(0,0,0,0.8)', @@ -48,17 +45,14 @@ define([ document.body.appendChild(this.backgroundElement); document.body.appendChild(this.domElement); - var _this = this; + const _this = this; dom.bind(this.backgroundElement, 'click', function() { _this.hide(); }); + } - - }; - - CenteredDiv.prototype.show = function() { - - var _this = this; + show() { + const _this = this; this.backgroundElement.style.display = 'block'; @@ -74,22 +68,21 @@ define([ _this.domElement.style.opacity = 1; _this.domElement.style.webkitTransform = 'scale(1)'; }); + } - }; - - CenteredDiv.prototype.hide = function() { - - var _this = this; - - var hide = function() { + /** + * Hide centered div + */ + hide() { + const _this = this; + const 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); @@ -100,18 +93,12 @@ define([ // 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; + layout() { + 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'; + } +} -}); \ No newline at end of file +export default CenteredDiv; diff --git a/src/dat/dom/dom.js b/src/dat/dom/dom.js index 734bc3e..15f92f5 100644 --- a/src/dat/dom/dom.js +++ b/src/dat/dom/dom.js @@ -11,277 +11,282 @@ * http://www.apache.org/licenses/LICENSE-2.0 */ -define([ - 'dat/utils/common' -], function(common) { +import common from '../utils/common'; - var EVENT_MAP = { - 'HTMLEvents': ['change'], - 'MouseEvents': ['click','mousemove','mousedown','mouseup', 'mouseover'], - 'KeyboardEvents': ['keydown'] - }; +const 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; - }); +const 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? %? +const CSS_VALUE_PIXELS = /(\d+(\.\d+)?)px/; +function cssValueToPixels(val) { + if (val === '0' || common.isUndefined(val)) { return 0; - } + const match = val.match(CSS_VALUE_PIXELS); + + if (!common.isNull(match)) { + return parseFloat(match[1]); + } + + // TODO ...ems? %? + + return 0; +} + +/** + * @namespace + * @member dat.dom + */ +const dom = { + /** - * @namespace - * @member dat.dom + * + * @param elem + * @param selectable */ - var dom = { + makeSelectable: function(elem, selectable) { + if (elem === undefined || elem.style === undefined) return; - /** - * - * @param elem - * @param selectable - */ - makeSelectable: function(elem, selectable) { + elem.onselectstart = selectable ? function() { + return false; + } : function() { + }; - if (elem === undefined || elem.style === undefined) return; + elem.style.MozUserSelect = selectable ? 'auto' : 'none'; + elem.style.KhtmlUserSelect = selectable ? 'auto' : 'none'; + elem.unselectable = selectable ? 'on' : 'off'; + }, - elem.onselectstart = selectable ? function() { - return false; - } : function() { - }; + /** + * + * @param elem + * @param horizontal + * @param vert + */ + makeFullscreen: function(elem, hor, vert) { + let vertical = vert; + let horizontal = hor; - 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 ); + if (common.isUndefined(horizontal)) { + horizontal = true; } - }; + if (common.isUndefined(vertical)) { + vertical = true; + } - return dom; + elem.style.position = 'absolute'; -}); \ No newline at end of file + 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, pars, aux) { + const params = pars || {}; + const className = EVENT_MAP_INV[eventType]; + if (!className) { + throw new Error('Event type ' + eventType + ' not supported.'); + } + const evt = document.createEvent(className); + switch (className) { + case 'MouseEvents': + { + const clientX = params.x || params.clientX || 0; + const 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': + { + const 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, newBool) { + const bool = newBool || 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, newBool) { + const bool = newBool || 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) { + const 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 === className) { + elem.removeAttribute('class'); + } else { + const classes = elem.className.split(/ +/); + const 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) { + const 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) { + const 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 el + */ + getOffset: function(el) { + let elem = el; + const offset = {left: 0, top: 0}; + if (elem.offsetParent) { + do { + offset.left += elem.offsetLeft; + offset.top += elem.offsetTop; + elem = elem.offsetParent; + } while (elem); + } + return offset; + }, + + // http://stackoverflow.com/posts/2684561/revisions + /** + * + * @param elem + */ + isActive: function(elem) { + return elem === document.activeElement && ( elem.type || elem.href ); + } + +}; + +export default dom; diff --git a/src/dat/gui/GUI.js b/src/dat/gui/GUI.js index b0f61d3..26d1986 100644 --- a/src/dat/gui/GUI.js +++ b/src/dat/gui/GUI.js @@ -11,1350 +11,1213 @@ * http://www.apache.org/licenses/LICENSE-2.0 */ -define([ +import css from '../utils/css'; +import saveDialogueContents from 'html!./saveDialogue.html'; +import styleSheet from '!style!css!sass!./style.scss'; +import ControllerFactory from '../controllers/ControllerFactory'; +import Controller from '../controllers/Controller'; +import BooleanController from '../controllers/BooleanController'; +import FunctionController from '../controllers/FunctionController'; +import NumberControllerBox from '../controllers/NumberControllerBox'; +import NumberControllerSlider from '../controllers/NumberControllerSlider'; +import ColorController from '../controllers/ColorController'; +import requestAnimationFrame from '../utils/requestAnimationFrame'; +import CenteredDiv from '../dom/CenteredDiv'; +import dom from '../dom/dom'; +import common from '../utils/common'; - 'dat/utils/css', +css.inject(styleSheet); - 'text!dat/gui/saveDialogue.html', - 'text!dat/gui/style.css', +/** Outer-most className for GUI's */ +const CSS_NAMESPACE = 'dg'; - 'dat/controllers/factory', - 'dat/controllers/Controller', - 'dat/controllers/BooleanController', - 'dat/controllers/FunctionController', - 'dat/controllers/NumberControllerBox', - 'dat/controllers/NumberControllerSlider', - 'dat/controllers/OptionController', - 'dat/controllers/ColorController', +const HIDE_KEY_CODE = 72; - 'dat/utils/requestAnimationFrame', +/** The only value shared between the JS and SCSS. Use caution. */ +const CLOSE_BUTTON_HEIGHT = 20; - 'dat/dom/CenteredDiv', - 'dat/dom/dom', +const DEFAULT_DEFAULT_PRESET_NAME = 'Default'; - 'dat/utils/common' +const SUPPORTS_LOCAL_STORAGE = (function() { + try { + return 'localStorage' in window && window.localStorage !== null; + } catch (e) { + return false; + } +})(); -], function(css, saveDialogueContents, styleSheet, controllerFactory, Controller, BooleanController, FunctionController, NumberControllerBox, NumberControllerSlider, OptionController, ColorController, requestAnimationFrame, CenteredDiv, dom, common) { +let SAVE_DIALOGUE; - css.inject(styleSheet); +/** Have we yet to create an autoPlace GUI? */ +let autoPlaceVirgin = true; - /** Outer-most className for GUI's */ - var CSS_NAMESPACE = 'dg'; +/** Fixed position div that auto place GUI's go inside */ +let autoPlaceContainer; - var HIDE_KEY_CODE = 72; +/** Are we hiding the GUI's ? */ +let hide = false; - /** The only value shared between the JS and SCSS. Use caution. */ - var CLOSE_BUTTON_HEIGHT = 20; +/** GUI's which should be hidden */ +const hideableGuis = []; - var DEFAULT_DEFAULT_PRESET_NAME = 'Default'; +/** + * 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 + */ +const GUI = function(pars) { + const _this = this; - 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 = []; + let params = pars || {}; /** - * 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 + * Outermost DOM Element + * @type DOMElement */ - var GUI = function(params) { + this.domElement = document.createElement('div'); + this.__ul = document.createElement('ul'); + this.domElement.appendChild(this.__ul); - var _this = this; + dom.addClass(this.domElement, CSS_NAMESPACE); - /** - * Outermost DOM Element - * @type DOMElement - */ - this.domElement = document.createElement('div'); - this.__ul = document.createElement('ul'); - this.domElement.appendChild(this.__ul); + /** + * Nested GUI's by name + * @ignore + */ + this.__folders = {}; - dom.addClass(this.domElement, CSS_NAMESPACE); + this.__controllers = []; - /** - * Nested GUI's by name - * @ignore - */ - this.__folders = {}; + /** + * List of objects I'm remembering for save, only used in top level GUI + * @ignore + */ + this.__rememberedObjects = []; - 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 - * [ - * { + /** + * 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.__rememberedObjectIndecesToControllers = []; - this.__listening = []; + this.__listening = []; - params = params || {}; + // Default parameters + params = common.defaults(params, { + autoPlace: true, + width: GUI.DEFAULT_WIDTH + }); - // 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 }; + 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); - } + if (common.isUndefined(params.parent) && params.hideable) { + hideableGuis.push(this); + } - // Only root level GUI's are resizable. - params.resizable = common.isUndefined(params.parent) && params.resizable; + // 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; - } + 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'; + // Not part of params because I don't want people passing this in via + // constructor. Should be a 'remembered' value. + let useLocalStorage = + SUPPORTS_LOCAL_STORAGE && + localStorage.getItem(getLocalStorageHash(this, 'isLocal')) === 'true'; - var saveToLocalStorage; + let saveToLocalStorage; - Object.defineProperties(this, + Object.defineProperties(this, + /** @lends dat.gui.GUI.prototype */ + { + /** + * The parent GUI + * @type dat.gui.GUI + */ + parent: { + get: function() { + return params.parent; + } + }, - /** @lends dat.gui.GUI.prototype */ - { + scrollable: { + get: function() { + return params.scrollable; + } + }, - /** - * 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); - } - } + /** + * 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; } + 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 (titleRowName) { + titleRowName.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 useLocalStorage; + }, + set: function(bool) { + if (SUPPORTS_LOCAL_STORAGE) { + useLocalStorage = 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 (useLocalStorage) { + _this.useLocalStorage = true; + + const savedGui = localStorage.getItem(getLocalStorageHash(this, 'gui')); + + if (savedGui) { + params.load = JSON.parse(savedGui); + } + } + } + + 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; + } + + const titleRowName = document.createTextNode(params.name); + dom.addClass(titleRowName, 'controller-name'); + + const titleRow = addRow(_this, titleRowName); + + const onClickTitle = function(e) { + e.preventDefault(); + _this.closed = !_this.closed; + return false; + }; + + dom.addClass(this.__ul, GUI.CLASS_CLOSED); + + dom.addClass(titleRow, 'title'); + dom.bind(titleRow, 'click', onClickTitle); + + if (!params.closed) { + this.closed = false; + } + } + + if (params.autoPlace) { + if (common.isUndefined(params.parent)) { + if (autoPlaceVirgin) { + autoPlaceContainer = document.createElement('div'); + dom.addClass(autoPlaceContainer, CSS_NAMESPACE); + dom.addClass(autoPlaceContainer, GUI.CLASS_AUTO_PLACE_CONTAINER); + document.body.appendChild(autoPlaceContainer); + autoPlaceVirgin = false; + } + + // Put it in the dom for you. + autoPlaceContainer.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); + } + + saveToLocalStorage = function() { + if (SUPPORTS_LOCAL_STORAGE && localStorage.getItem(getLocalStorageHash(_this, 'isLocal')) === 'true') { + localStorage.setItem(getLocalStorageHash(_this, 'gui'), JSON.stringify(_this.getSaveObject())); + } + }; + + // expose this method publicly + this.saveToLocalStorageIfPossible = saveToLocalStorage; + + function resetWidth() { + const root = _this.getRoot(); + root.width += 1; + common.defer(function() { + root.width -= 1; + }); + } + + if (!params.parent) { + resetWidth(); + } +}; + +GUI.toggleHide = function() { + hide = !hide; + common.each(hideableGuis, 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.splice(this.__controllers.indexOf(controller), 1); + const _this = this; + common.defer(function() { + _this.onResize(); + }); + }, + + destroy: function() { + if (this.autoPlace) { + autoPlaceContainer.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 + '"'); + } + + const newGuiParams = {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. + newGuiParams.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 + newGuiParams.closed = this.load.folders[name].closed; + + // Pass down the loaded data + newGuiParams.load = this.load.folders[name]; + } + + const gui = new GUI(newGuiParams); + this.__folders[name] = gui; + + const li = addRow(this, gui.domElement); + dom.addClass(li, 'folder'); + return gui; + }, + + open: function() { + this.closed = false; + }, + + close: function() { + this.closed = true; + }, + + onResize: function() { + const root = this.getRoot(); + if (root.scrollable) { + const top = dom.getOffset(root.__ul).top; + let h = 0; + + common.each(root.__ul.childNodes, function(node) { + if (!(root.autoPlace && node === root.__save_row)) { + h += dom.getHeight(node); + } }); - // 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); - } - - saveToLocalStorage = function () { - if (SUPPORTS_LOCAL_STORAGE && localStorage.getItem(getLocalStorageHash(_this, 'isLocal')) === 'true') { - localStorage.setItem(getLocalStorageHash(_this, 'gui'), JSON.stringify(_this.getSaveObject())); - } - } - - // expose this method publicly - this.saveToLocalStorageIfPossible = saveToLocalStorage; - - 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.splice(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); - this.saveToLocalStorageIfPossible(); - - }, - - 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); - this.saveToLocalStorageIfPossible(); - - }, - - 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]; - + 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'; + } + } - // Nada. + if (root.__resize_handle) { + common.defer(function() { + root.__resize_handle.style.height = root.__ul.offsetHeight + 'px'; + }); + } - return; + 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.'); + } + + const _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() { + let 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() { + const 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); + } - // Did the loaded object remember thcommon.isObject? - if (preset[matched_index] && + toReturn.folders = {}; + common.each(this.__folders, function(element, key) { + toReturn.folders[key] = element.getSaveObject(); + }); - // Did we remember this particular property? - preset[matched_index][controller.property] !== undefined) { + return toReturn; + }, - // We did remember something for this guy ... - var value = preset[matched_index][controller.property]; + save: function() { + if (!this.load.remembered) { + this.load.remembered = {}; + } - // And that's what it is. - controller.initialValue = value; - controller.setValue(value); + this.load.remembered[this.preset] = getCurrentPreset(this); + markPresetModified(this, false); + this.saveToLocalStorageIfPossible(); + }, + 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); + this.saveToLocalStorageIfPossible(); + }, + + 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); - } - - } - - } - - 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); + common.each(this.__folders, function(folder) { + folder.revert(folder); }); - } 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; + if (!gui) { + markPresetModified(this.getRoot(), false); } + }, - 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; + listen: function(controller) { + const init = this.__listening.length === 0; + this.__listening.push(controller); + if (init) { + updateDisplays(this.__listening); } } } +); - 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; - } +/** + * Add a row to the end of the GUI or before another row. + * + * @param gui + * @param [newDom] If specified, inserts the dom content in the new row + * @param [liBefore] If specified, places the new row before another row + */ +function addRow(gui, newDom, liBefore) { + const li = document.createElement('li'); + if (newDom) { + li.appendChild(newDom); } - function updateDisplays(controllerArray) { + if (liBefore) { + gui.__ul.insertBefore(li, params.before); + } else { + gui.__ul.appendChild(li); + } + gui.onResize(); + return li; +} +function markPresetModified(gui, modified) { + const opt = gui.__preset_select[gui.__preset_select.selectedIndex]; - if (controllerArray.length != 0) { + // console.log('mark', modified, opt); + if (modified) { + opt.innerHTML = opt.value + '*'; + } else { + opt.innerHTML = opt.value; + } +} - requestAnimationFrame(function() { - updateDisplays(controllerArray); - }); +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; } + }); - common.each(controllerArray, function(c) { - c.updateDisplay(); + // All sliders should be accompanied by a box. + if (controller instanceof NumberControllerSlider) { + const box = new NumberControllerBox(controller.object, controller.property, + {min: controller.__min, max: controller.__max, step: controller.__step}); + + common.each(['updateDisplay', 'onChange', 'onFinishChange'], function(method) { + const pc = controller[method]; + const pb = box[method]; + controller[method] = box[method] = function() { + const 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) { + const 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(val) { + li.style.borderLeftColor = controller.__color.toString(); + return val; + }, controller.updateDisplay); + + controller.updateDisplay(); } - return GUI; + controller.setValue = common.compose(function(val) { + if (gui.getRoot().__preset_select && controller.isModified()) { + markPresetModified(gui.getRoot(), true); + } -}); + return val; + }, controller.setValue); +} + +function recallSavedValue(gui, controller) { + // Find the topmost GUI, that's where remembered objects live. + const root = gui.getRoot(); + + // Does the object we're controlling match anything we've been told to + // remember? + const matchedIndex = root.__rememberedObjects.indexOf(controller.object); + + // Why yes, it does! + if (matchedIndex !== -1) { + // Let me fetch a map of controllers for thcommon.isObject. + let controllerMap = root.__rememberedObjectIndecesToControllers[matchedIndex]; + + // Ohp, I believe this is the first controller we've created for this + // object. Lets make the map fresh. + if (controllerMap === undefined) { + controllerMap = {}; + root.__rememberedObjectIndecesToControllers[matchedIndex] = + controllerMap; + } + + // Keep track of this controller + controllerMap[controller.property] = controller; + + // Okay, now have we saved any values for this controller? + if (root.load && root.load.remembered) { + const presetMap = root.load.remembered; + + // Which preset are we trying to load? + let preset; + + if (presetMap[gui.preset]) { + preset = presetMap[gui.preset]; + } else if (presetMap[DEFAULT_DEFAULT_PRESET_NAME]) { + // Uhh, you can have the default instead? + preset = presetMap[DEFAULT_DEFAULT_PRESET_NAME]; + } else { + // Nada. + return; + } + + // Did the loaded object remember thcommon.isObject? && Did we remember this particular property? + if (preset[matchedIndex] && preset[matchedIndex][controller.property] !== undefined) { + // We did remember something for this guy ... + const value = preset[matchedIndex][controller.property]; + + // And that's what it is. + controller.initialValue = value; + controller.setValue(value); + } + } + } +} + +function add(gui, object, property, params) { + if (object[property] === undefined) { + throw new Error(`Object "${object}" has no property "${property}"`); + } + + let controller; + + if (params.color) { + controller = new ColorController(object, property); + } else { + const 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'); + + const name = document.createElement('span'); + dom.addClass(name, 'property-name'); + name.innerHTML = controller.property; + + const container = document.createElement('div'); + container.appendChild(name); + container.appendChild(controller.domElement); + + const li = addRow(gui, container, params.before); + + dom.addClass(li, GUI.CLASS_CONTROLLER_ROW); + if (controller instanceof ColorController) { + dom.addClass(li, 'color'); + } else { + dom.addClass(li, typeof controller.getValue()); + } + + augmentController(gui, li, controller); + + gui.__controllers.push(controller); + + return controller; +} + +function getLocalStorageHash(gui, key) { + // TODO how does this deal with multiple GUI's? + return document.location.href + '.' + key; +} + +function addPresetOption(gui, name, setSelected) { + const 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 showHideExplain(gui, explain) { + explain.style.display = gui.useLocalStorage ? 'block' : 'none'; +} + +function addSaveMenu(gui) { + const 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'); + + const gears = document.createElement('span'); + gears.innerHTML = ' '; + dom.addClass(gears, 'button gears'); + + // TODO replace with FunctionController + const button = document.createElement('span'); + button.innerHTML = 'Save'; + dom.addClass(button, 'button'); + dom.addClass(button, 'save'); + + const button2 = document.createElement('span'); + button2.innerHTML = 'New'; + dom.addClass(button2, 'button'); + dom.addClass(button2, 'save-as'); + + const button3 = document.createElement('span'); + button3.innerHTML = 'Revert'; + dom.addClass(button3, 'button'); + dom.addClass(button3, 'revert'); + + const 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 (let 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) { + const explain = document.getElementById('dg-local-explain'); + const localStorageCheckBox = document.getElementById('dg-local-storage'); + const saveLocally = document.getElementById('dg-save-locally'); + + saveLocally.style.display = 'block'; + + if (localStorage.getItem(getLocalStorageHash(gui, 'isLocal')) === 'true') { + localStorageCheckBox.setAttribute('checked', 'checked'); + } + + showHideExplain(gui, explain); + + // TODO: Use a boolean controller, fool! + dom.bind(localStorageCheckBox, 'change', function() { + gui.useLocalStorage = !gui.useLocalStorage; + showHideExplain(gui, explain); + }); + } + + const 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() { + const 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) { + let pmouseX; + + 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' + + }); + + 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 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; + } + + dom.bind(gui.__resize_handle, 'mousedown', dragStart); + dom.bind(gui.__closeButton, 'mousedown', dragStart); + + gui.domElement.insertBefore(gui.__resize_handle, gui.domElement.firstElementChild); +} + +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) { + const toReturn = {}; + + // For each object I'm remembering + common.each(gui.__rememberedObjects, function(val, index) { + const savedValues = {}; + + // The controllers I've made for thcommon.isObject by property + const controllerMap = + gui.__rememberedObjectIndecesToControllers[index]; + + // Remember each value for each property + common.each(controllerMap, function(controller, property) { + savedValues[property] = useInitialValues ? controller.initialValue : controller.getValue(); + }); + + // Save the values for thcommon.isObject + toReturn[index] = savedValues; + }); + + return toReturn; +} + +function setPresetSelectIndex(gui) { + for (let index = 0; index < gui.__preset_select.length; index++) { + if (gui.__preset_select[index].value === gui.preset) { + gui.__preset_select.selectedIndex = index; + } + } +} + +function updateDisplays(controllerArray) { + if (controllerArray.length !== 0) { + requestAnimationFrame(function() { + updateDisplays(controllerArray); + }); + } + + common.each(controllerArray, function(c) { + c.updateDisplay(); + }); +} + +module.exports = GUI; diff --git a/src/dat/gui/GUI.css b/src/dat/gui/GUI.scss similarity index 100% rename from src/dat/gui/GUI.css rename to src/dat/gui/GUI.scss diff --git a/src/dat/gui/_structure.scss b/src/dat/gui/_structure.scss index 4b4a80b..9e316a2 100644 --- a/src/dat/gui/_structure.scss +++ b/src/dat/gui/_structure.scss @@ -16,12 +16,12 @@ $button-height: 20px; /* Auto-place container */ &.ac { - position: fixed; - top: 0; - left: 0; - right: 0; - height: 0; - z-index: 0; + position: fixed; + top: 0; + left: 0; + right: 0; + height: 0; + z-index: 0; } &:not(.ac) .main { @@ -53,7 +53,6 @@ $button-height: 20px; opacity: 1 !important; } - &:hover .close-button, .close-button.drag { opacity: 1; @@ -68,7 +67,7 @@ $button-height: 20px; line-height: $button-height - 1; height: $button-height; - /* TODO, these are style notes */ + /* TODO, these are style notes */ cursor: pointer; text-align: center; background-color: #000; @@ -94,8 +93,6 @@ $button-height: 20px; } } - - .save-row { position: fixed; top: 0; @@ -104,7 +101,7 @@ $button-height: 20px; } - li { + li { @include transition(height, 0.1s, ease-out); } @@ -117,10 +114,10 @@ $button-height: 20px; padding: 0 4px 0 5px; } - li.folder { - padding: 0; - border-left: $nest-margin solid rgba(0,0,0,0); - + li.folder { + padding: 0; + border-left: $nest-margin solid rgba(0, 0, 0, 0); + } /** Folder names */ @@ -137,8 +134,8 @@ $button-height: 20px; overflow: hidden; border: 0; } - - /** Controller row */ + + /** Controller row */ .cr { clear: both; padding-left: 3px; @@ -156,7 +153,7 @@ $button-height: 20px; } /** Controller-half (right) */ - .c { + .c { float: left; width: 60%; } @@ -197,8 +194,8 @@ $button-height: 20px; .c select { margin-top: 5px; } - - /** Ensure the entire boolean and function row shows a hand */ + + /** Ensure the entire boolean and function row shows a hand */ .cr.function, .cr.function .property-name, /* Don't know why I need to be this explicit */ .cr.function *, @@ -207,7 +204,6 @@ $button-height: 20px; cursor: pointer; } - .selector { display: none; position: absolute; @@ -216,13 +212,11 @@ $button-height: 20px; z-index: 10; } - .c:hover .selector, .selector.drag { display: block; } - li.save-row { padding: 0; @@ -242,7 +236,6 @@ $button-height: 20px; line-height: 15px; } - } /* TODO Separate style and structure */ @@ -273,7 +266,7 @@ $button-height: 20px; margin-top: 10px; code { font-size: 10px; - } + } } #dat-gui-save-locally { diff --git a/src/dat/gui/saveDialogue.html b/src/dat/gui/saveDialogue.html index bbb0ff7..a9dd79a 100644 --- a/src/dat/gui/saveDialogue.html +++ b/src/dat/gui/saveDialogue.html @@ -13,9 +13,9 @@ override those passed to dat.GUI's constructor. This makes it easier to work incrementally, but localStorage is fragile, and your friends may not see the same values you do. - + - + \ No newline at end of file diff --git a/src/dat/gui/style.css b/src/dat/gui/style.css deleted file mode 100644 index e53790d..0000000 --- a/src/dat/gui/style.css +++ /dev/null @@ -1,280 +0,0 @@ -.dg { - /** Clear list styles */ - /* Auto-place container */ - /* Auto-placed GUI's */ - /* Line items that don't contain folders. */ - /** Folder names */ - /** Hides closed items */ - /** Controller row */ - /** Name-half (left) */ - /** Controller-half (right) */ - /** Controller placement */ - /** Shorter number boxes when slider is present. */ - /** Ensure the entire boolean and function row shows a hand */ } - .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 { - /** Exclude mains in ac so that we don't hide close button */ - 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; - /* TODO, these are style notes */ - 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 { - /*opacity: 0;*/ - -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; - /* TODO, these are style notes */ - 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%; - /*display: none;*/ - 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; } - -/* TODO Separate style and structure */ -#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; } - -/** Main type */ -.dg { - color: #eee; - font: 11px 'Lucida Grande', sans-serif; - text-shadow: 0 -1px 0 #111; - /** Auto place */ - /* Controller row,
  • */ - /** Controllers */ } - .dg.main { - /** Scrollbar */ } - .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: black 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; } diff --git a/src/dat/gui/style.scss b/src/dat/gui/style.scss index 358c65f..a1b82e4 100644 --- a/src/dat/gui/style.scss +++ b/src/dat/gui/style.scss @@ -51,7 +51,6 @@ $input-color: lighten($background-color, 8.5%); /** Main type */ .dg { - color: #eee; font: 11px 'Lucida Grande', sans-serif; text-shadow: 0 -1px 0 #111; @@ -135,6 +134,10 @@ $input-color: lighten($background-color, 8.5%); border-left: 3px solid $boolean-color; } + &.color { + border-left: 3px solid; + } + &.function { border-left: 3px solid $function-color; } @@ -153,19 +156,16 @@ $input-color: lighten($background-color, 8.5%); } } - &.function:hover , + &.function:hover, &.boolean:hover { background: #111; } } - /** Controllers */ .c { - - input[type=text] { background: $input-color; @@ -180,7 +180,6 @@ $input-color: lighten($background-color, 8.5%); } - .slider { background: $input-color; cursor: ew-resize; diff --git a/src/dat/index.js b/src/dat/index.js new file mode 100644 index 0000000..7b8db55 --- /dev/null +++ b/src/dat/index.js @@ -0,0 +1,42 @@ +/** + * 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 + */ + +export default { + color: { + Color: require('./color/Color'), + math: require('./color/math'), + interpret: require('./color/interpret') + }, + + controllers: { + Controller: require('./controllers/Controller'), + BooleanController: require('./controllers/BooleanController'), + OptionController: require('./controllers/OptionController'), + StringController: require('./controllers/StringController'), + NumberController: require('./controllers/NumberController'), + NumberControllerBox: require('./controllers/NumberControllerBox'), + NumberControllerSlider: require('./controllers/NumberControllerSlider'), + FunctionController: require('./controllers/FunctionController'), + ColorController: require('./controllers/ColorController') + }, + + dom: { + dom: require('./dom/dom') + }, + + gui: { + GUI: require('./gui/GUI') + }, + + GUI: require('./gui/GUI') +}; diff --git a/src/dat/utils/common.js b/src/dat/utils/common.js index b5bcc34..a66b07f 100644 --- a/src/dat/utils/common.js +++ b/src/dat/utils/common.js @@ -11,130 +11,122 @@ * http://www.apache.org/licenses/LICENSE-2.0 */ -define([ -], function() { - - var ARR_EACH = Array.prototype.forEach; - var ARR_SLICE = Array.prototype.slice; +const ARR_EACH = Array.prototype.forEach; +const 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/ - */ +/** + * 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) { +const Common = { + BREAK: {}, - if (!obj) return; - - if (ARR_EACH && obj.forEach && 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; - + extend: function(target) { + this.each(ARR_SLICE.call(arguments, 1), function(obj) { + for (const key in obj) { + if (!this.isUndefined(obj[key])) { + target[key] = obj[key]; + } } - - }, - - defer: function(fnc) { - setTimeout(fnc, 0); - }, - - toArray: function(obj) { - if (obj.toArray) return obj.toArray(); - return ARR_SLICE.call(obj); - }, + }, this); - 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]'; + return target; + }, + + defaults: function(target) { + this.each(ARR_SLICE.call(arguments, 1), function(obj) { + for (const key in obj) { + if (this.isUndefined(target[key])) { + target[key] = obj[key]; + } + } + }, this); + + return target; + }, + + compose: function() { + const toCall = ARR_SLICE.call(arguments); + return function() { + let args = ARR_SLICE.call(arguments); + for (let i = toCall.length - 1; i >= 0; i--) { + args = [toCall[i].apply(this, args)]; + } + return args[0]; + }; + }, + + each: function(obj, itr, scope) { + if (!obj) { + return; } - - }; - -}); \ No newline at end of file + + if (ARR_EACH && obj.forEach && obj.forEach === ARR_EACH) { + obj.forEach(itr, scope); + } else if (obj.length === obj.length + 0) { // Is number but not NaN + let key; + let l; + for (key = 0, l = obj.length; key < l; key++) { + if (key in obj && itr.call(scope, obj[key], key) === this.BREAK) { + return; + } + } + } else { + for (const 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 isNaN(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]'; + } + +}; + +export default Common; diff --git a/src/dat/utils/css.js b/src/dat/utils/css.js index 4b8a926..3a5c479 100644 --- a/src/dat/utils/css.js +++ b/src/dat/utils/css.js @@ -11,23 +11,21 @@ * http://www.apache.org/licenses/LICENSE-2.0 */ -define([], -function() { - return { - load: function (url, doc) { - doc = doc || document; - var link = doc.createElement('link'); - link.type = 'text/css'; - link.rel = 'stylesheet'; - link.href = url; - doc.getElementsByTagName('head')[0].appendChild(link); - }, - inject: function(css, doc) { - doc = doc || document; - var injected = document.createElement('style'); - injected.type = 'text/css'; - injected.innerHTML = css; - doc.getElementsByTagName('head')[0].appendChild(injected); - } +module.exports = { + load: function(url, indoc) { + const doc = indoc || document; + const link = doc.createElement('link'); + link.type = 'text/css'; + link.rel = 'stylesheet'; + link.href = url; + doc.getElementsByTagName('head')[0].appendChild(link); + }, + + inject: function(css, indoc) { + const doc = indoc || document; + const injected = document.createElement('style'); + injected.type = 'text/css'; + injected.innerHTML = css; + doc.getElementsByTagName('head')[0].appendChild(injected); } -}); +}; diff --git a/src/dat/utils/requestAnimationFrame.js b/src/dat/utils/requestAnimationFrame.js index ee1f27d..147925a 100644 --- a/src/dat/utils/requestAnimationFrame.js +++ b/src/dat/utils/requestAnimationFrame.js @@ -11,22 +11,16 @@ * http://www.apache.org/licenses/LICENSE-2.0 */ -define([ -], function() { - - /** - * requirejs version of Paul Irish's RequestAnimationFrame - * http://paulirish.com/2011/requestanimationframe-for-smart-animating/ - */ +export default function() { + function requestAnimationFrame(callback) { + // TODO: Get rid of window + window.setTimeout(callback, 1000 / 60); + } return window.requestAnimationFrame || - window.webkitRequestAnimationFrame || - window.mozRequestAnimationFrame || - window.oRequestAnimationFrame || - window.msRequestAnimationFrame || - function(callback, element) { - - window.setTimeout(callback, 1000 / 60); - - }; -}); + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame || + requestAnimationFrame; +} diff --git a/tests/index.html b/tests/index.html index 2246526..fceb577 100644 --- a/tests/index.html +++ b/tests/index.html @@ -7,12 +7,11 @@ - +