From 561b4a1411ed13b37be8ff974174d46b1c09e843 Mon Sep 17 00:00:00 2001 From: George Michael Brower Date: Mon, 18 Apr 2011 12:01:45 -0700 Subject: [PATCH] Moved everything to the DAT namespace, and officially renamed the project dat.gui :) --- .gitignore | 2 +- demo/demo.js | 4 +- index.html | 60 +- src/controllers/boolean.js | 8 +- src/controllers/controller.js | 28 +- src/controllers/function.js | 6 +- src/controllers/number.js | 22 +- src/controllers/slider.js | 7 +- src/controllers/string.js | 8 +- src/gui.js | 1114 +++++++++++++++++---------------- 10 files changed, 632 insertions(+), 627 deletions(-) diff --git a/.gitignore b/.gitignore index ee21cd16..723ef36f 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -GUIDAT.tmproj \ No newline at end of file +.idea \ No newline at end of file diff --git a/demo/demo.js b/demo/demo.js index 0cc49ef0..72f4c7a8 100644 --- a/demo/demo.js +++ b/demo/demo.js @@ -26,7 +26,7 @@ function FizzyText(message) { createBitmap(message); }); - // We can even add functions to the GUI! As long as they have + // We can even add functions to the DAT.GUI! As long as they have // 0 arguments, we can call them from the dat-gui panel. this.explode = function() { @@ -205,7 +205,7 @@ function FizzyText(message) { this.x += Math.cos(angle) * _this.speed + this.vx; this.y += -Math.sin(angle) * _this.speed + this.vy; - this.r = GUI.constrain(this.r, 0, _this.maxSize); + this.r = DAT.GUI.constrain(this.r, 0, _this.maxSize); // If we're tiny, keep moving around until we find a black // pixel. diff --git a/index.html b/index.html index 5a72f9f7..6798fe47 100644 --- a/index.html +++ b/index.html @@ -1,6 +1,6 @@ - gui-dat + dat.gui @@ -25,9 +25,9 @@ prettyPrint(); - var fizzyText = new FizzyText("gui-dat"); + var fizzyText = new FizzyText("dat.gui"); - var gui = new GUI(); + var gui = new DAT.GUI(); // Text field gui.add(fizzyText, "message"); @@ -107,20 +107,20 @@
- +
-

GUI-DAT flag

+

dat.gui flag

-

gui-dat is a lightweight controller library for JavaScript. It allows you to easily manipulate variables and fire functions on the fly.

+

dat.gui is a lightweight controller library for JavaScript. It allows you to easily manipulate variables and fire functions on the fly.

Basic Usage

@@ -130,9 +130,9 @@

Basic Usage

window.onload = function() { - var fizzyText = new FizzyText("gui-dat"); + var fizzyText = new FizzyText("dat.gui"); - var gui = new GUI(); + var gui = new DAT.GUI(); // Text field gui.add(fizzyText, "message"); @@ -157,7 +157,7 @@

Basic Usage

@@ -186,22 +186,22 @@

Advanced saving

Let's say you'd like to share your settings with someone. Instead of sending a long link with lots of parameters stored in it, you can make your saved settings the defaults.

-

First, add the method GUI.showSaveString() to a gui object:

-
var gui = new GUI();
+                

First, add the method DAT.GUI.showSaveString() to a gui object:

+
var gui = new DAT.GUI();
 
 // Add some stuff (and pretend I change their values);
 gui.add(someObject, "someProperty");
 gui.add(someObject, "someOtherProperty");
 
 // Make a save button.
-gui.add(GUI, "showSaveString");
+gui.add(DAT.GUI, "showSaveString");
-

Clicking the "showSaveString" button bring up an alert with a string. Copy and paste that string into the method GUI.load() before you instantiate any gui objects.

+

Clicking the "showSaveString" button bring up an alert with a string. Copy and paste that string into the method DAT.GUI.load() before you instantiate any gui objects.

 // Replace COPIED STRING with the value you got from showSaveString()
-GUI.load("COPIED STRING");
+DAT.GUI.load("COPIED STRING");
 
-var gui = new GUI();
+var gui = new DAT.GUI();
 
 // Now these properties will be set to the values you just saved.
 gui.add(someObject, "someProperty");
@@ -239,7 +239,7 @@ 

Listen for variable changes inside the GUI

});

Finally, if you'd like to do a little something extra when a function is called, use the following:

gui.add(obj, "functionName").onFire(function() {
-   alert("You called a function with gui-dat");
+   alert("You called a function with dat.gui");
 });
@@ -248,7 +248,7 @@

Listen for variable changes inside the GUI

Listen for variable changes outside of the GUI

-

Let's say you have a variable that changes by itself from time to time. If you'd like the GUI to reflect those changes, use the listen() method.

+

Let's say you have a variable that changes by itself from time to time. If you'd like the DAT.GUI to reflect those changes, use the listen() method.

gui.add(obj, "changingProperty").listen();
@@ -258,7 +258,7 @@

Listen for variable changes outside of the GUI

Advanced listening

-

By default, gui-dat will create an internal interval that checks for changes in the values you've marked with listen(). If you'd like to check for these changes in an interval of your own definition, use the following:

+

By default, dat.gui will create an internal interval that checks for changes in the values you've marked with listen(). If you'd like to check for these changes in an interval of your own definition, use the following:

 gui.autoListen = false; // disables internal interval
 gui.add(obj, "changingProperty").listen();
@@ -287,21 +287,21 @@ 

Advanced listening

Multiple panels and custom placement

-

You can instantiate multiple GUI objects and name them however you'd like.

-
var gui1 = new GUI();
-var gui2 = new GUI();
+                

You can instantiate multiple DAT.GUI objects and name them however you'd like.

+
var gui1 = new DAT.GUI();
+var gui2 = new DAT.GUI();
 
 // The name function overwrites the "Show Controls" text.
 gui1.name("Utilities");
 gui2.name("Camera Placement");
-

By default, gui-dat panels will be automatically added to the HTML document and fixed to the top of the screen. You can disable this behavior / styling and append the gui DOM element to a container of your choosing.

+

By default, dat.gui panels will be automatically added to the HTML document and fixed to the top of the screen. You can disable this behavior / styling and append the gui DOM element to a container of your choosing.

-// Notice this belongs to the GUI class (uppercase) 
+// Notice this belongs to the DAT.GUI class (uppercase)
 // and not an instance thereof.
-GUI.autoPlace = false; 
+DAT.GUI.autoPlace = false;
 
-var gui = new GUI();
+var gui = new DAT.GUI();
 
 // Do some custom styles ...
 gui.domElement.style.position = "absolute";
@@ -318,7 +318,7 @@ 

Pro tips.

    -
  1. gui-dat panels are resizeable. Drag the show/hide button.
  2. +
  3. dat.gui panels are resizeable. Drag the show/hide button.
  4. Press 'H' to show/hide GUI's.
diff --git a/src/controllers/boolean.js b/src/controllers/boolean.js index 1d2ceb9c..d85e35dd 100644 --- a/src/controllers/boolean.js +++ b/src/controllers/boolean.js @@ -1,7 +1,7 @@ -GUI.BooleanController = function() { +DAT.GUI.BooleanController = function() { this.type = "boolean"; - GUI.Controller.apply(this, arguments); + DAT.GUI.Controller.apply(this, arguments); var _this = this; var input = document.createElement('input'); @@ -40,8 +40,8 @@ GUI.BooleanController = function() { val = eval(val); } catch (e) {} } - return GUI.Controller.prototype.setValue.call(this, val); + return DAT.GUI.Controller.prototype.setValue.call(this, val); }; }; -GUI.extendController(GUI.BooleanController); +DAT.GUI.extendController(DAT.GUI.BooleanController); diff --git a/src/controllers/controller.js b/src/controllers/controller.js index c054fa09..d5f2165c 100644 --- a/src/controllers/controller.js +++ b/src/controllers/controller.js @@ -1,4 +1,4 @@ -GUI.Controller = function() { +DAT.GUI.Controller = function() { this.parent = arguments[0]; this.object = arguments[1]; @@ -14,34 +14,34 @@ GUI.Controller = function() { this.name(this.propertyName); this.domElement.appendChild(this.propertyNameElement); - GUI.makeUnselectable(this.domElement); + DAT.GUI.makeUnselectable(this.domElement); }; -GUI.Controller.prototype.changeFunction = null; -GUI.Controller.prototype.finishChangeFunction = null; +DAT.GUI.Controller.prototype.changeFunction = null; +DAT.GUI.Controller.prototype.finishChangeFunction = null; -GUI.Controller.prototype.name = function(n) { +DAT.GUI.Controller.prototype.name = function(n) { this.propertyNameElement.innerHTML = n; return this; }; -GUI.Controller.prototype.reset = function() { +DAT.GUI.Controller.prototype.reset = function() { this.setValue(this.initialValue); return this; }; -GUI.Controller.prototype.listen = function() { +DAT.GUI.Controller.prototype.listen = function() { this.parent.listenTo(this); return this; }; -GUI.Controller.prototype.unlisten = function() { +DAT.GUI.Controller.prototype.unlisten = function() { this.parent.unlistenTo(this); // <--- hasn't been tested yet return this; }; -GUI.Controller.prototype.setValue = function(n) { +DAT.GUI.Controller.prototype.setValue = function(n) { this.object[this.propertyName] = n; if (this.changeFunction != null) { this.changeFunction.call(this, n); @@ -50,23 +50,23 @@ GUI.Controller.prototype.setValue = function(n) { return this; }; -GUI.Controller.prototype.getValue = function() { +DAT.GUI.Controller.prototype.getValue = function() { return this.object[this.propertyName]; }; -GUI.Controller.prototype.updateDisplay = function() {}; +DAT.GUI.Controller.prototype.updateDisplay = function() {}; -GUI.Controller.prototype.onChange = function(fnc) { +DAT.GUI.Controller.prototype.onChange = function(fnc) { this.changeFunction = fnc; return this; }; -GUI.Controller.prototype.onFinishChange = function(fnc) { +DAT.GUI.Controller.prototype.onFinishChange = function(fnc) { this.finishChangeFunction = fnc; return this; }; -GUI.Controller.prototype.options = function() { +DAT.GUI.Controller.prototype.options = function() { var _this = this; var select = document.createElement('select'); if (arguments.length == 1) { diff --git a/src/controllers/function.js b/src/controllers/function.js index 75c84b9e..ae2e1320 100644 --- a/src/controllers/function.js +++ b/src/controllers/function.js @@ -1,10 +1,10 @@ -GUI.FunctionController = function() { +DAT.GUI.FunctionController = function() { this.type = "function"; var _this = this; - GUI.Controller.apply(this, arguments); + DAT.GUI.Controller.apply(this, arguments); this.domElement.addEventListener('click', function() { _this.fire(); @@ -26,4 +26,4 @@ GUI.FunctionController = function() { _this.object[_this.propertyName].call(_this.object); }; }; -GUI.extendController(GUI.FunctionController); +DAT.GUI.extendController(DAT.GUI.FunctionController); diff --git a/src/controllers/number.js b/src/controllers/number.js index a08ecc09..7f819a10 100644 --- a/src/controllers/number.js +++ b/src/controllers/number.js @@ -1,8 +1,8 @@ -GUI.NumberController = function() { +DAT.GUI.NumberController = function() { this.type = "number"; - GUI.Controller.apply(this, arguments); + DAT.GUI.Controller.apply(this, arguments); var _this = this; @@ -38,7 +38,7 @@ GUI.NumberController = function() { var slider; if (min != undefined && max != undefined) { - slider = new GUI.Slider(this, min, max, step, this.getValue()); + slider = new DAT.GUI.Slider(this, min, max, step, this.getValue()); this.domElement.appendChild(slider.domElement); } @@ -83,8 +83,8 @@ GUI.NumberController = function() { var mouseup = function(e) { document.removeEventListener('mousemove', dragNumberField, false); - GUI.makeSelectable(_this.parent.domElement); - GUI.makeSelectable(numberField); + DAT.GUI.makeSelectable(_this.parent.domElement); + DAT.GUI.makeSelectable(numberField); if (clickedNumberField && !draggedNumberField) { numberField.focus(); numberField.select(); @@ -107,8 +107,8 @@ GUI.NumberController = function() { // Or any other fields in this gui for that matter ... // TODO: Make makeUselectable go through each element and child element. - GUI.makeUnselectable(_this.parent.domElement); - GUI.makeUnselectable(numberField); + DAT.GUI.makeUnselectable(_this.parent.domElement); + DAT.GUI.makeUnselectable(numberField); if(slider) slider.domElement.className += ' active'; @@ -123,7 +123,7 @@ GUI.NumberController = function() { this.options = function() { _this.noSlider(); _this.domElement.removeChild(numberField); - return GUI.Controller.prototype.options.apply(this, arguments); + return DAT.GUI.Controller.prototype.options.apply(this, arguments); }; this.noSlider = function() { @@ -143,14 +143,14 @@ GUI.NumberController = function() { val = max; } - return GUI.Controller.prototype.setValue.call(this, val); + return DAT.GUI.Controller.prototype.setValue.call(this, val); }; this.updateDisplay = function() { - numberField.value = GUI.roundToDecimal(_this.getValue(), 4); + numberField.value = DAT.GUI.roundToDecimal(_this.getValue(), 4); if (slider) slider.value = _this.getValue(); }; }; -GUI.extendController(GUI.NumberController); +DAT.GUI.extendController(DAT.GUI.NumberController); diff --git a/src/controllers/slider.js b/src/controllers/slider.js index dc19180c..d9cc958d 100644 --- a/src/controllers/slider.js +++ b/src/controllers/slider.js @@ -1,4 +1,4 @@ -GUI.Slider = function(numberController, min, max, step, initValue) { +DAT.GUI.Slider = function(numberController, min, max, step, initValue) { var clicked = false; var _this = this; @@ -16,7 +16,8 @@ GUI.Slider = function(numberController, min, max, step, initValue) { var onDrag = function(e) { if (!clicked) return; var pos = findPos(_this.domElement); - var val = GUI.map(e.pageX, pos[0], pos[0] + _this.domElement.offsetWidth, min, max); + var val = DAT.GUI.map(e.pageX, pos[0], pos[0] + _this.domElement + .offsetWidth, min, max); val = Math.round(val/step)*step; numberController.setValue(val); }; @@ -54,7 +55,7 @@ GUI.Slider = function(numberController, min, max, step, initValue) { }; this.__defineSetter__('value', function(e) { - var pct = GUI.map(e, min, max, 0, 100); + var pct = DAT.GUI.map(e, min, max, 0, 100); this.fg.style.width = pct+"%"; }); diff --git a/src/controllers/string.js b/src/controllers/string.js index 1b6fa455..55a884aa 100644 --- a/src/controllers/string.js +++ b/src/controllers/string.js @@ -1,9 +1,9 @@ -GUI.StringController = function() { +DAT.GUI.StringController = function() { this.type = "string"; var _this = this; - GUI.Controller.apply(this, arguments); + DAT.GUI.Controller.apply(this, arguments); var input = document.createElement('input'); @@ -37,11 +37,11 @@ GUI.StringController = function() { this.options = function() { _this.domElement.removeChild(input); - return GUI.Controller.prototype.options.apply(this, arguments); + return DAT.GUI.Controller.prototype.options.apply(this, arguments); }; this.domElement.appendChild(input); }; -GUI.extendController(GUI.StringController); +DAT.GUI.extendController(DAT.GUI.StringController); diff --git a/src/gui.js b/src/gui.js index 222d8b04..1ac85441 100644 --- a/src/gui.js +++ b/src/gui.js @@ -1,661 +1,665 @@ -var GUI = function(parameters) { - - if (parameters == undefined) { - parameters = {}; - } - - var _this = this; - - var MIN_WIDTH = 240; - var MAX_WIDTH = 500; - - var controllers = []; - var listening = []; - - var autoListen = true; - - var listenInterval; - - // Sum total of heights of controllers in this gui - var controllerHeight; - - var curControllerContainerHeight = 0; - - var _this = this; - - var open = false; - var width = 280; +var DAT = DAT || {}; - // Prevents checkForOverflow bug in which loaded gui appearance - // settings are not respected by presence of scrollbar. - var explicitOpenHeight = false; - - // How big we get when we open - var openHeight; - - var name; - - var resizeTo = 0; - var resizeTimeout; - - - this.domElement = document.createElement('div'); - this.domElement.setAttribute('class', 'guidat'); - this.domElement.style.width = width+'px'; - - var controllerContainer = document.createElement('div'); - controllerContainer.setAttribute('class', 'guidat-controllers'); - - // Firefox hack to prevent horizontal scrolling - controllerContainer.addEventListener('DOMMouseScroll', function(e) { - - var scrollAmount = this.scrollTop; - - if (e.wheelDelta) { - scrollAmount+=e.wheelDelta; - } else if (e.detail) { - scrollAmount+=e.detail; - } - - if (e.preventDefault) { - e.preventDefault(); - } - e.returnValue = false; - - controllerContainer.scrollTop = scrollAmount; - - }, false); - - controllerContainer.style.height = '0px'; - - var toggleButton = document.createElement('a'); - toggleButton.setAttribute('class', 'guidat-toggle'); - toggleButton.setAttribute('href', '#'); - toggleButton.innerHTML = "Show Controls"; - - var toggleDragged = false; - var dragDisplacementY = 0; - var togglePressed = false; - - var my, pmy, mx, pmx; - - var resize = function(e) { - pmy = my; - pmx = mx; - my = e.pageY; - mx = e.pageX; - - var dmy = my - pmy; - - if (!open) { - if (dmy > 0) { - open = true; - curControllerContainerHeight = openHeight = 1; - toggleButton.innerHTML = name || "Hide Controls"; - } else { - return; - } - } - - // TODO: Flip this if you want to resize to the left. - var dmx = pmx - mx; - - if (dmy > 0 && - curControllerContainerHeight > controllerHeight) { - var d = GUI.map(curControllerContainerHeight, controllerHeight, controllerHeight + 100, 1, 0); - dmy *= d; - } - - toggleDragged = true; - dragDisplacementY += dmy; - dragDisplacementX += dmx; - openHeight += dmy; - width += dmx; - curControllerContainerHeight += dmy; - controllerContainer.style.height = openHeight+'px'; - width = GUI.constrain(width, MIN_WIDTH, MAX_WIDTH); - _this.domElement.style.width = width+'px'; - checkForOverflow(); - }; - - toggleButton.addEventListener('mousedown', function(e) { - pmy = my = e.pageY; - pmx = mx = e.pageX; - togglePressed = true; - e.preventDefault(); - dragDisplacementY = 0; - dragDisplacementX = 0; - document.addEventListener('mousemove', resize, false); - return false; +DAT.GUI = function(parameters) { - }, false); + if (parameters == undefined) { + parameters = {}; + } - toggleButton.addEventListener('click', function(e) { - e.preventDefault(); - return false; - }, false); + var MIN_WIDTH = 240; + var MAX_WIDTH = 500; - document.addEventListener('mouseup', function(e) { - - if (togglePressed && !toggleDragged) { - _this.toggle(); - } - - if (togglePressed && toggleDragged) { - - if (dragDisplacementX == 0) { - adaptToScrollbar(); - } - - if (openHeight > controllerHeight) { - - clearTimeout(resizeTimeout); - openHeight = resizeTo = controllerHeight; - beginResize(); - - } else if (controllerContainer.children.length >= 1) { - - var singleControllerHeight = controllerContainer.children[0].offsetHeight; - clearTimeout(resizeTimeout); - var target = Math.round(curControllerContainerHeight/singleControllerHeight)*singleControllerHeight-1; - resizeTo = target; - if (resizeTo <= 0) { - _this.hide(); - openHeight = singleControllerHeight*2; - } else { - openHeight = resizeTo; - beginResize(); - } - } - }; - - - document.removeEventListener('mousemove', resize, false); - e.preventDefault(); - toggleDragged = false; - togglePressed = false; - - return false; + var controllers = []; + var listening = []; - }, false); - - this.domElement.appendChild(controllerContainer); - this.domElement.appendChild(toggleButton); + var autoListen = true; - if ( parameters.domElement ) { - GUI.autoPlace = false; - parameters.domElement.appendChild(this.domElement); - } + var listenInterval; - if (GUI.autoPlace) { - if(GUI.autoPlaceContainer == null) { - GUI.autoPlaceContainer = document.createElement('div'); - GUI.autoPlaceContainer.setAttribute("id", "guidat"); - - document.body.appendChild(GUI.autoPlaceContainer); - } - GUI.autoPlaceContainer.appendChild(this.domElement); - } - - this.autoListenIntervalTime = 1000/60; - - var createListenInterval = function() { - listenInterval = setInterval(function() { - _this.listen(); - }, this.autoListenIntervalTime); - }; - - this.__defineSetter__("autoListen", function(v) { - autoListen = v; - if (!autoListen) { - clearInterval(listenInterval); - } else { - if (listening.length > 0) createListenInterval(); - } - }); - - this.__defineGetter__("autoListen", function(v) { - return autoListen; - }); - - this.listenTo = function(controller) { - // TODO: check for duplicates - if (listening.length == 0) { - createListenInterval(); - } - listening.push(controller); - }; - - this.unlistenTo = function(controller) { - // TODO: test this - for(var i = 0; i < listening.length; i++) { - if(listening[i] == controller) listening.splice(i, 1); - } - if(listening.length <= 0) { - clearInterval(listenInterval); - } - }; - - this.listen = function(whoToListenTo) { - var arr = whoToListenTo || listening; - for (var i in arr) { - arr[i].updateDisplay(); - } - }; - - this.listenAll = function() { - this.listen(controllers); - } - - this.autoListen = true; - - var alreadyControlled = function(object, propertyName) { - for (var i in controllers) { - if (controllers[i].object == object && - controllers[i].propertyName == propertyName) { - return true; - } - } - return false; - }; + // Sum total of heights of controllers in this gui + var controllerHeight; + var curControllerContainerHeight = 0; - var construct = function(constructor, args) { - function F() { - return constructor.apply(this, args); - } - F.prototype = constructor.prototype; - return new F(); - }; + var _this = this; - this.add = function() { + var open = false; + var width = 280; - var object = arguments[0]; - var propertyName = arguments[1]; + // Prevents checkForOverflow bug in which loaded gui appearance + // settings are not respected by presence of scrollbar. + var explicitOpenHeight = false; - // Have we already added this? - if (alreadyControlled(object, propertyName)) { - // GUI.error("Controller for \"" + propertyName+"\" already added."); - // return; - } + // How big we get when we open + var openHeight; - var value = object[propertyName]; + var name; - // Does this value exist? Is it accessible? - if (value == undefined) { - GUI.error(object + " either has no property \""+propertyName+"\", or the property is inaccessible."); - return; - } + var resizeTo = 0; + var resizeTimeout; - var type = typeof value; - var handler = handlerTypes[type]; - // Do we know how to deal with this data type? - if (handler == undefined) { - GUI.error("Cannot create controller for data type \""+type+"\""); - return; - } - - var args = [this]; // Set first arg (parent) to this - for (var j = 0; j < arguments.length; j++) { - args.push(arguments[j]); - } - - var controllerObject = construct(handler, args); - - // Were we able to make the controller? - if (!controllerObject) { - GUI.error("Error creating controller for \""+propertyName+"\"."); - return; - } + this.domElement = document.createElement('div'); + this.domElement.setAttribute('class', 'guidat'); + this.domElement.style.width = width + 'px'; - // Success. - controllerContainer.appendChild(controllerObject.domElement); - controllers.push(controllerObject); - GUI.allControllers.push(controllerObject); + var controllerContainer = document.createElement('div'); + controllerContainer.setAttribute('class', 'guidat-controllers'); - // Do we have a saved value for this controller? - if (type != "function" && - GUI.saveIndex < GUI.savedValues.length) { - controllerObject.setValue(GUI.savedValues[GUI.saveIndex]); - GUI.saveIndex++; - } - - // Compute sum height of controllers. - checkForOverflow(); - - // Prevents checkForOverflow bug in which loaded gui appearance - // settings are not respected by presence of scrollbar. - if (!explicitOpenHeight) { - openHeight = controllerHeight; - } - - return controllerObject; - - } - - var checkForOverflow = function() { - controllerHeight = 0; - for (var i in controllers) { - controllerHeight += controllers[i].domElement.offsetHeight; - } - if (controllerHeight - 1 > openHeight) { - controllerContainer.style.overflowY = "auto"; - } else { - controllerContainer.style.overflowY = "hidden"; - } - }; - - var handlerTypes = { - "number": GUI.NumberController, - "string": GUI.StringController, - "boolean": GUI.BooleanController, - "function": GUI.FunctionController - }; - - var alreadyControlled = function(object, propertyName) { - for (var i in controllers) { - if (controllers[i].object == object && - controllers[i].propertyName == propertyName) { - return true; - } - } - return false; - }; - - var construct = function(constructor, args) { + // Firefox hack to prevent horizontal scrolling + controllerContainer.addEventListener('DOMMouseScroll', function(e) { - function F() { - return constructor.apply(this, args); - } - F.prototype = constructor.prototype; - return new F(); - }; + var scrollAmount = this.scrollTop; - this.reset = function() { - // TODO + if (e.wheelDelta) { + scrollAmount += e.wheelDelta; + } else if (e.detail) { + scrollAmount += e.detail; } - // GUI ... GUI - - this.toggle = function() { - open ? this.hide() : this.show(); - }; + if (e.preventDefault) { + e.preventDefault(); + } + e.returnValue = false; - this.show = function() { - toggleButton.innerHTML = name || "Hide Controls"; - resizeTo = openHeight; - clearTimeout(resizeTimeout); - beginResize(); + controllerContainer.scrollTop = scrollAmount; + + }, false); + + controllerContainer.style.height = '0px'; + + var toggleButton = document.createElement('a'); + toggleButton.setAttribute('class', 'guidat-toggle'); + toggleButton.setAttribute('href', '#'); + toggleButton.innerHTML = "Show Controls"; + + var toggleDragged = false; + var dragDisplacementY = 0; + var togglePressed = false; + + var my, pmy, mx, pmx; + + var resize = function(e) { + pmy = my; + pmx = mx; + my = e.pageY; + mx = e.pageX; + + var dmy = my - pmy; + + if (!open) { + if (dmy > 0) { open = true; + curControllerContainerHeight = openHeight = 1; + toggleButton.innerHTML = name || "Hide Controls"; + } else { + return; + } + } + + // TODO: Flip this if you want to resize to the left. + var dmx = pmx - mx; + + if (dmy > 0 && + curControllerContainerHeight > controllerHeight) { + var d = DAT.GUI.map(curControllerContainerHeight, controllerHeight, controllerHeight + 100, 1, 0); + dmy *= d; + } + + toggleDragged = true; + dragDisplacementY += dmy; + dragDisplacementX += dmx; + openHeight += dmy; + width += dmx; + curControllerContainerHeight += dmy; + controllerContainer.style.height = openHeight + 'px'; + width = DAT.GUI.constrain(width, MIN_WIDTH, MAX_WIDTH); + _this.domElement.style.width = width + 'px'; + checkForOverflow(); + }; + + toggleButton.addEventListener('mousedown', function(e) { + pmy = my = e.pageY; + pmx = mx = e.pageX; + togglePressed = true; + e.preventDefault(); + dragDisplacementY = 0; + dragDisplacementX = 0; + document.addEventListener('mousemove', resize, false); + return false; + + }, false); + + toggleButton.addEventListener('click', function(e) { + e.preventDefault(); + return false; + }, false); + + document.addEventListener('mouseup', function(e) { + + if (togglePressed && !toggleDragged) { + _this.toggle(); } - this.hide = function() { - toggleButton.innerHTML = name || "Show Controls"; - resizeTo = 0; + if (togglePressed && toggleDragged) { + + if (dragDisplacementX == 0) { + adaptToScrollbar(); + } + + if (openHeight > controllerHeight) { + clearTimeout(resizeTimeout); + openHeight = resizeTo = controllerHeight; beginResize(); - open = false; - } - - this.name = function(n) { - name = n; - toggleButton.innerHTML = n; - } - - // used in saveURL - this.appearanceVars = function() { - return [open, width, openHeight, controllerContainer.scrollTop] - } - - var beginResize = function() { - //console.log("Resizing from " + curControllerContainerHeight + " to " + resizeTo); - curControllerContainerHeight += (resizeTo - curControllerContainerHeight)*0.6; - if (Math.abs(curControllerContainerHeight-resizeTo) < 1) { - curControllerContainerHeight = resizeTo; - adaptToScrollbar(); - - } else { - resizeTimeout = setTimeout(beginResize, 1000/30); - } - controllerContainer.style.height = Math.round(curControllerContainerHeight)+'px'; - checkForOverflow(); - } - - var adaptToScrollbar = function() { - // Clears lingering slider column - _this.domElement.style.width = (width+1)+'px'; - setTimeout(function() { - _this.domElement.style.width = width+'px'; - }, 1); - }; - - // Load saved appearance: - - if (GUI.guiIndex < GUI.savedAppearanceVars.length) { - - - width = parseInt(GUI.savedAppearanceVars[GUI.guiIndex][1]); - _this.domElement.style.width = width+"px"; - - openHeight = parseInt(GUI.savedAppearanceVars[GUI.guiIndex][2]); - explicitOpenHeight = true; - if (eval(GUI.savedAppearanceVars[GUI.guiIndex][0]) == true) { - curControllerContainerHeight = openHeight; - var t = GUI.savedAppearanceVars[GUI.guiIndex][3] - - // Hack. - setTimeout(function() { - controllerContainer.scrollTop = t; - }, 0); - - if (GUI.scrollTop > -1) { - document.body.scrollTop = GUI.scrollTop; - } - resizeTo = openHeight; - this.show(); + + } else if (controllerContainer.children.length >= 1) { + + var singleControllerHeight = controllerContainer.children[0].offsetHeight; + clearTimeout(resizeTimeout); + var target = Math.round(curControllerContainerHeight / singleControllerHeight) * singleControllerHeight - 1; + resizeTo = target; + if (resizeTo <= 0) { + _this.hide(); + openHeight = singleControllerHeight * 2; + } else { + openHeight = resizeTo; + beginResize(); } + } + } + ; - GUI.guiIndex++; + + document.removeEventListener('mousemove', resize, false); + e.preventDefault(); + toggleDragged = false; + togglePressed = false; + + return false; + + }, false); + + this.domElement.appendChild(controllerContainer); + this.domElement.appendChild(toggleButton); + + if (parameters.domElement) { + DAT.GUI.autoPlace = false; + parameters.domElement.appendChild(this.domElement); + } + + if (DAT.GUI.autoPlace) { + if (DAT.GUI.autoPlaceContainer == null) { + DAT.GUI.autoPlaceContainer = document.createElement('div'); + DAT.GUI.autoPlaceContainer.setAttribute("id", "guidat"); + + document.body.appendChild(DAT.GUI.autoPlaceContainer); + } + DAT.GUI.autoPlaceContainer.appendChild(this.domElement); + } + + this.autoListenIntervalTime = 1000 / 60; + + var createListenInterval = function() { + listenInterval = setInterval(function() { + _this.listen(); + }, this.autoListenIntervalTime); + }; + + this.__defineSetter__("autoListen", function(v) { + autoListen = v; + if (!autoListen) { + clearInterval(listenInterval); + } else { + if (listening.length > 0) createListenInterval(); } + }); - GUI.allGuis.push(this); + this.__defineGetter__("autoListen", function(v) { + return autoListen; + }); - // Add hide listener if this is the first GUI. - if (GUI.allGuis.length == 1) { - window.addEventListener('keyup', function(e) { - // Hide on "H" - if (e.keyCode == 72) { - GUI.toggleHide(); - } - }, false); - } + this.listenTo = function(controller) { + // TODO: check for duplicates + if (listening.length == 0) { + createListenInterval(); + } + listening.push(controller); + }; + + this.unlistenTo = function(controller) { + // TODO: test this + for (var i = 0; i < listening.length; i++) { + if (listening[i] == controller) listening.splice(i, 1); + } + if (listening.length <= 0) { + clearInterval(listenInterval); + } + }; + + this.listen = function(whoToListenTo) { + var arr = whoToListenTo || listening; + for (var i in arr) { + arr[i].updateDisplay(); + } + }; + + this.listenAll = function() { + this.listen(controllers); + } + + this.autoListen = true; + + var alreadyControlled = function(object, propertyName) { + for (var i in controllers) { + if (controllers[i].object == object && + controllers[i].propertyName == propertyName) { + return true; + } + } + return false; + }; + + + var construct = function(constructor, args) { + function F() { + return constructor.apply(this, args); + } + + F.prototype = constructor.prototype; + return new F(); + }; + + this.add = function() { + + var object = arguments[0]; + var propertyName = arguments[1]; + + // Have we already added this? + if (alreadyControlled(object, propertyName)) { + // DAT.GUI.error("Controller for \"" + propertyName+"\" already added."); + // return; + } + + var value = object[propertyName]; + + // Does this value exist? Is it accessible? + if (value == undefined) { + DAT.GUI.error(object + " either has no property \"" + propertyName + "\", or the property is inaccessible."); + return; + } + + var type = typeof value; + var handler = handlerTypes[type]; + + // Do we know how to deal with this data type? + if (handler == undefined) { + DAT.GUI.error("Cannot create controller for data type \"" + type + "\""); + return; + } + + var args = [this]; // Set first arg (parent) to this + for (var j = 0; j < arguments.length; j++) { + args.push(arguments[j]); + } + + var controllerObject = construct(handler, args); + + // Were we able to make the controller? + if (!controllerObject) { + DAT.GUI.error("Error creating controller for \"" + propertyName + "\"."); + return; + } + + // Success. + controllerContainer.appendChild(controllerObject.domElement); + controllers.push(controllerObject); + DAT.GUI.allControllers.push(controllerObject); + + // Do we have a saved value for this controller? + if (type != "function" && + DAT.GUI.saveIndex < DAT.GUI.savedValues.length) { + controllerObject.setValue(DAT.GUI.savedValues[DAT.GUI.saveIndex]); + DAT.GUI.saveIndex++; + } + + // Compute sum height of controllers. + checkForOverflow(); + + // Prevents checkForOverflow bug in which loaded gui appearance + // settings are not respected by presence of scrollbar. + if (!explicitOpenHeight) { + openHeight = controllerHeight; + } + + return controllerObject; + + } + + var checkForOverflow = function() { + controllerHeight = 0; + for (var i in controllers) { + controllerHeight += controllers[i].domElement.offsetHeight; + } + if (controllerHeight - 1 > openHeight) { + controllerContainer.style.overflowY = "auto"; + } else { + controllerContainer.style.overflowY = "hidden"; + } + }; + + var handlerTypes = { + "number": DAT.GUI.NumberController, + "string": DAT.GUI.StringController, + "boolean": DAT.GUI.BooleanController, + "function": DAT.GUI.FunctionController + }; + + var alreadyControlled = function(object, propertyName) { + for (var i in controllers) { + if (controllers[i].object == object && + controllers[i].propertyName == propertyName) { + return true; + } + } + return false; + }; + + var construct = function(constructor, args) { + + function F() { + return constructor.apply(this, args); + } + + F.prototype = constructor.prototype; + return new F(); + }; + + this.reset = function() { + // TODO + } + + // DAT.GUI ... DAT.GUI + + this.toggle = function() { + open ? this.hide() : this.show(); + }; + + this.show = function() { + toggleButton.innerHTML = name || "Hide Controls"; + resizeTo = openHeight; + clearTimeout(resizeTimeout); + beginResize(); + open = true; + } + + this.hide = function() { + toggleButton.innerHTML = name || "Show Controls"; + resizeTo = 0; + clearTimeout(resizeTimeout); + beginResize(); + open = false; + } + + this.name = function(n) { + name = n; + toggleButton.innerHTML = n; + } + + // used in saveURL + this.appearanceVars = function() { + return [open, width, openHeight, controllerContainer.scrollTop] + } + + var beginResize = function() { + //console.log("Resizing from " + curControllerContainerHeight + " to " + resizeTo); + curControllerContainerHeight += (resizeTo - curControllerContainerHeight) * 0.6; + if (Math.abs(curControllerContainerHeight - resizeTo) < 1) { + curControllerContainerHeight = resizeTo; + adaptToScrollbar(); + + } else { + resizeTimeout = setTimeout(beginResize, 1000 / 30); + } + controllerContainer.style.height = Math.round(curControllerContainerHeight) + 'px'; + checkForOverflow(); + } + + var adaptToScrollbar = function() { + // Clears lingering slider column + _this.domElement.style.width = (width + 1) + 'px'; + setTimeout(function() { + _this.domElement.style.width = width + 'px'; + }, 1); + }; + + // Load saved appearance: + + if (DAT.GUI.guiIndex < DAT.GUI.savedAppearanceVars.length) { + + width = parseInt(DAT.GUI.savedAppearanceVars[DAT.GUI.guiIndex][1]); + _this.domElement.style.width = width + "px"; + + openHeight = parseInt(DAT.GUI.savedAppearanceVars[DAT.GUI.guiIndex][2]); + explicitOpenHeight = true; + if (eval(DAT.GUI.savedAppearanceVars[DAT.GUI.guiIndex][0]) == true) { + curControllerContainerHeight = openHeight; + var t = DAT.GUI.savedAppearanceVars[DAT.GUI.guiIndex][3] + + // Hack. + setTimeout(function() { + controllerContainer.scrollTop = t; + }, 0); + + if (DAT.GUI.scrollTop > -1) { + document.body.scrollTop = DAT.GUI.scrollTop; + } + resizeTo = openHeight; + this.show(); + } + + DAT.GUI.guiIndex++; + } + + DAT.GUI.allGuis.push(this); + + // Add hide listener if this is the first DAT.GUI. + if (DAT.GUI.allGuis.length == 1) { + window.addEventListener('keyup', function(e) { + // Hide on "H" + if (e.keyCode == 72) { + DAT.GUI.toggleHide(); + } + }, false); + } }; // Do not set this directly. -GUI.hidden = false; - +DAT.GUI.hidden = false; // Static members -GUI.autoPlace = true; -GUI.autoPlaceContainer = null; -GUI.allControllers = []; -GUI.allGuis = []; +DAT.GUI.autoPlace = true; +DAT.GUI.autoPlaceContainer = null; +DAT.GUI.allControllers = []; +DAT.GUI.allGuis = []; -GUI.toggleHide = function() { - if (GUI.hidden) { - GUI.show(); - } else { - GUI.hide(); - } +DAT.GUI.toggleHide = function() { + if (DAT.GUI.hidden) { + DAT.GUI.show(); + } else { + DAT.GUI.hide(); + } } -GUI.show = function() { - GUI.hidden = false; - for (var i in GUI.allGuis) { - GUI.allGuis[i].domElement.style.display = "block"; - } +DAT.GUI.show = function() { + DAT.GUI.hidden = false; + for (var i in DAT.GUI.allGuis) { + DAT.GUI.allGuis[i].domElement.style.display = "block"; + } } -GUI.hide = function() { - GUI.hidden = true; - for (var i in GUI.allGuis) { - GUI.allGuis[i].domElement.style.display = "none"; - } +DAT.GUI.hide = function() { + DAT.GUI.hidden = true; + for (var i in DAT.GUI.allGuis) { + DAT.GUI.allGuis[i].domElement.style.display = "none"; + } } -GUI.saveURL = function() { - var url = GUI.replaceGetVar("saveString", GUI.getSaveString()); - window.location = url; +DAT.GUI.saveURL = function() { + var url = DAT.GUI.replaceGetVar("saveString", DAT.GUI.getSaveString()); + window.location = url; }; -GUI.scrollTop = -1; +DAT.GUI.scrollTop = -1; -GUI.load = function(saveString) { +DAT.GUI.load = function(saveString) { - //GUI.savedAppearanceVars = []; - var vals = saveString.split(","); - var numGuis = parseInt(vals[0]); - GUI.scrollTop = parseInt(vals[1]); - for (var i = 0; i < numGuis; i++) { - var appr = vals.splice(2, 4); - GUI.savedAppearanceVars.push(appr); - } + //DAT.GUI.savedAppearanceVars = []; + var vals = saveString.split(","); + var numGuis = parseInt(vals[0]); + DAT.GUI.scrollTop = parseInt(vals[1]); + for (var i = 0; i < numGuis; i++) { + var appr = vals.splice(2, 4); + DAT.GUI.savedAppearanceVars.push(appr); + } - GUI.savedValues = vals.splice(2, vals.length); + DAT.GUI.savedValues = vals.splice(2, vals.length); }; -GUI.savedValues = []; -GUI.savedAppearanceVars = []; +DAT.GUI.savedValues = []; +DAT.GUI.savedAppearanceVars = []; -GUI.getSaveString = function() { +DAT.GUI.getSaveString = function() { - var vals = [], - i; + var vals = [], + i; - vals.push(GUI.allGuis.length); - vals.push(document.body.scrollTop); + vals.push(DAT.GUI.allGuis.length); + vals.push(document.body.scrollTop); - for (i in GUI.allGuis) { - var av = GUI.allGuis[i].appearanceVars(); - for (var j = 0; j < av.length; j++) { - vals.push(av[j]); - } + for (i in DAT.GUI.allGuis) { + var av = DAT.GUI.allGuis[i].appearanceVars(); + for (var j = 0; j < av.length; j++) { + vals.push(av[j]); } + } - for (i in GUI.allControllers) { + for (i in DAT.GUI.allControllers) { - // We don't save values for functions. - if (GUI.allControllers[i].type == "function") { - continue; - } + // We don't save values for functions. + if (DAT.GUI.allControllers[i].type == "function") { + continue; + } - var v = GUI.allControllers[i].getValue(); + var v = DAT.GUI.allControllers[i].getValue(); - // Round numbers so they don't get enormous - if (GUI.allControllers[i].type == "number") { - v = GUI.roundToDecimal(v, 4); - } + // Round numbers so they don't get enormous + if (DAT.GUI.allControllers[i].type == "number") { + v = DAT.GUI.roundToDecimal(v, 4); + } - vals.push(v); + vals.push(v); - } + } - return vals.join(','); + return vals.join(','); }; -GUI.getVarFromURL = function(v) { +DAT.GUI.getVarFromURL = function(v) { - var vars = [], hash; - var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&'); + var vars = [], hash; + var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&'); - for (var i = 0; i < hashes.length; i++) { - hash = hashes[i].split("="); - if (hash == undefined) continue; - if (hash[0] == v) { - return hash[1]; - } + for (var i = 0; i < hashes.length; i++) { + hash = hashes[i].split("="); + if (hash == undefined) continue; + if (hash[0] == v) { + return hash[1]; } + } - return null; + return null; }; -GUI.replaceGetVar = function(varName, val) { +DAT.GUI.replaceGetVar = function(varName, val) { - var vars = [], hash; - var loc = window.location.href; - var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&'); + var vars = [], hash; + var loc = window.location.href; + var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&'); - for (var i = 0; i < hashes.length; i++) { - hash = hashes[i].split("="); - if (hash == undefined) continue; - if (hash[0] == varName) { - return loc.replace(hash[1], val); - } + for (var i = 0; i < hashes.length; i++) { + hash = hashes[i].split("="); + if (hash == undefined) continue; + if (hash[0] == varName) { + return loc.replace(hash[1], val); } + } - if (window.location.href.indexOf('?') != -1) { - return loc + "&"+varName+"="+val; - } + if (window.location.href.indexOf('?') != -1) { + return loc + "&" + varName + "=" + val; + } - return loc+"?"+varName+"="+val; + return loc + "?" + varName + "=" + val; }; -GUI.saveIndex = 0; -GUI.guiIndex = 0; +DAT.GUI.saveIndex = 0; +DAT.GUI.guiIndex = 0; -GUI.showSaveString = function() { - alert(GUI.getSaveString()); +DAT.GUI.showSaveString = function() { + alert(DAT.GUI.getSaveString()); }; // Util functions -GUI.makeUnselectable = function(elem) { - elem.onselectstart = function() { return false; }; - elem.style.MozUserSelect = "none"; - elem.style.KhtmlUserSelect = "none"; - elem.unselectable = "on"; +DAT.GUI.makeUnselectable = function(elem) { + elem.onselectstart = function() { + return false; + }; + elem.style.MozUserSelect = "none"; + elem.style.KhtmlUserSelect = "none"; + elem.unselectable = "on"; }; -GUI.makeSelectable = function(elem) { - elem.onselectstart = function() { }; - elem.style.MozUserSelect = "auto"; - elem.style.KhtmlUserSelect = "auto"; - elem.unselectable = "off"; +DAT.GUI.makeSelectable = function(elem) { + elem.onselectstart = function() { + }; + elem.style.MozUserSelect = "auto"; + elem.style.KhtmlUserSelect = "auto"; + elem.unselectable = "off"; }; -GUI.map = function(v, i1, i2, o1, o2) { - return o1 + (o2 - o1) * ((v - i1) / (i2 - i1)); +DAT.GUI.map = function(v, i1, i2, o1, o2) { + return o1 + (o2 - o1) * ((v - i1) / (i2 - i1)); }; -GUI.constrain = function (v, o1, o2) { - if (v < o1) v = o1; - else if (v > o2) v = o2; - return v; +DAT.GUI.constrain = function (v, o1, o2) { + if (v < o1) v = o1; + else if (v > o2) v = o2; + return v; }; -GUI.error = function(str) { - if (typeof console.error == 'function') { - console.error("[GUI ERROR] " + str); - } +DAT.GUI.error = function(str) { + if (typeof console.error == 'function') { + console.error("[DAT.GUI ERROR] " + str); + } }; -GUI.roundToDecimal = function(n, decimals) { - var t = Math.pow(10, decimals); - return Math.round(n*t)/t; +DAT.GUI.roundToDecimal = function(n, decimals) { + var t = Math.pow(10, decimals); + return Math.round(n * t) / t; }; -GUI.extendController = function(clazz) { - clazz.prototype = new GUI.Controller(); - clazz.prototype.constructor = clazz; +DAT.GUI.extendController = function(clazz) { + clazz.prototype = new DAT.GUI.Controller(); + clazz.prototype.constructor = clazz; }; -if (GUI.getVarFromURL('saveString') != null) GUI.load(GUI.getVarFromURL('saveString')); +if (DAT.GUI.getVarFromURL('saveString') != null) DAT.GUI.load(DAT.GUI.getVarFromURL('saveString'));