From 5be42748bdd1ca3a6e7801f8fd6208ce14638b31 Mon Sep 17 00:00:00 2001 From: George Michael Brower Date: Wed, 2 Feb 2011 13:12:55 -0500 Subject: [PATCH] Now we have a FUNCTIONING mock :) --- controllers/controller.boolean.js | 9 +- controllers/controller.function.js | 7 +- controllers/controller.js | 23 ++- controllers/controller.number.js | 28 ++- controllers/controller.string.js | 7 +- controllers/scrubber.js | 278 +++++++++++++++++++++++++++++ controllers/slider.js | 2 +- demo/demo.css | 6 +- gui-bare.css | 26 --- gui.css | 21 ++- gui.js | 88 +++++---- index.html | 259 +++------------------------ 12 files changed, 410 insertions(+), 344 deletions(-) create mode 100644 controllers/scrubber.js delete mode 100644 gui-bare.css diff --git a/controllers/controller.boolean.js b/controllers/controller.boolean.js index 8c52605..66808ca 100644 --- a/controllers/controller.boolean.js +++ b/controllers/controller.boolean.js @@ -1,7 +1,7 @@ -var BooleanController = function() { +GUI.BooleanController = function() { this.type = "boolean"; - Controller.apply(this, arguments); + GUI.Controller.apply(this, arguments); var _this = this; var input = document.createElement('input'); @@ -32,9 +32,8 @@ var BooleanController = function() { val = eval(val); } catch (e) {} } - return Controller.prototype.setValue.call(this, val); + return GUI.Controller.prototype.setValue.call(this, val); } }; -BooleanController.prototype = new Controller(); -BooleanController.prototype.constructor = BooleanController; +GUI.extendController(GUI.BooleanController); \ No newline at end of file diff --git a/controllers/controller.function.js b/controllers/controller.function.js index c95c5ec..bf17507 100644 --- a/controllers/controller.function.js +++ b/controllers/controller.function.js @@ -1,12 +1,11 @@ -var FunctionController = function() { +GUI.FunctionController = function() { this.type = "function"; var that = this; - Controller.apply(this, arguments); + GUI.Controller.apply(this, arguments); this.domElement.addEventListener('click', function() { that.object[that.propertyName].call(that.object); }, false); this.domElement.style.cursor = "pointer"; this.propertyNameElement.style.cursor = "pointer"; }; -FunctionController.prototype = new Controller(); -FunctionController.prototype.constructor = FunctionController; +GUI.extendController(GUI.FunctionController); diff --git a/controllers/controller.js b/controllers/controller.js index be4510c..d5b4047 100644 --- a/controllers/controller.js +++ b/controllers/controller.js @@ -1,4 +1,4 @@ -var Controller = function() { +GUI.Controller = function() { this.parent = arguments[0]; this.object = arguments[1]; @@ -14,38 +14,35 @@ var Controller = function() { this.name(this.propertyName); this.domElement.appendChild(this.propertyNameElement); - this.timeElement = document.createElement('div'); - this.timeElement.setAttribute('class', 'guidat-time'); - this.domElement.appendChild(this.timeElement); GUI.makeUnselectable(this.domElement); }; -Controller.prototype.changeFunction = null; +GUI.Controller.prototype.changeFunction = null; -Controller.prototype.name = function(n) { +GUI.Controller.prototype.name = function(n) { this.propertyNameElement.innerHTML = n; return this; }; -Controller.prototype.reset = function() { +GUI.Controller.prototype.reset = function() { this.setValue(this.initialValue); return this; }; -Controller.prototype.listen = function() { +GUI.Controller.prototype.listen = function() { this.parent.listenTo(this); return this; } -Controller.prototype.unlisten = function() { +GUI.Controller.prototype.unlisten = function() { this.parent.unlistenTo(this); // <--- hasn't been tested yet return this; } -Controller.prototype.setValue = function(n) { +GUI.Controller.prototype.setValue = function(n) { this.object[this.propertyName] = n; if (this.changeFunction != null) { this.changeFunction.call(this, n); @@ -56,13 +53,13 @@ Controller.prototype.setValue = function(n) { return this; } -Controller.prototype.getValue = function() { +GUI.Controller.prototype.getValue = function() { return this.object[this.propertyName]; } -Controller.prototype.updateDisplay = function() {} +GUI.Controller.prototype.updateDisplay = function() {} -Controller.prototype.onChange = function(fnc) { +GUI.Controller.prototype.onChange = function(fnc) { this.changeFunction = fnc; return this; } diff --git a/controllers/controller.number.js b/controllers/controller.number.js index 3f8e3fb..4c8dcaa 100644 --- a/controllers/controller.number.js +++ b/controllers/controller.number.js @@ -1,8 +1,8 @@ -var NumberController = function() { +GUI.NumberController = function() { this.type = "number"; - Controller.apply(this, arguments); + GUI.Controller.apply(this, arguments); var _this = this; @@ -16,7 +16,22 @@ var NumberController = function() { var min = arguments[3]; var max = arguments[4]; - var step = arguments[5]; + var step = arguments[5]; + + this.step = function(s) { + step = s; + return this; + } + + this.min = function(s) { + min = s; + return this; + } + + this.max = function(s) { + max = s; + return this; + } if (!step) { if (min != undefined && max != undefined) { @@ -38,7 +53,7 @@ var NumberController = function() { var slider; if (min != undefined && max != undefined) { - slider = new Slider(this, min, max, step, this.getValue()); + slider = new GUI.Slider(this, min, max, step, this.getValue()); this.domElement.appendChild(slider.domElement); } @@ -121,7 +136,7 @@ var NumberController = function() { val = max; } - return Controller.prototype.setValue.call(this, val); + return GUI.Controller.prototype.setValue.call(this, val); } @@ -131,5 +146,4 @@ var NumberController = function() { } }; -NumberController.prototype = new Controller(); -NumberController.prototype.constructor = NumberController; +GUI.extendController(GUI.NumberController); diff --git a/controllers/controller.string.js b/controllers/controller.string.js index 57cb832..d29804d 100644 --- a/controllers/controller.string.js +++ b/controllers/controller.string.js @@ -1,9 +1,9 @@ -var StringController = function() { +GUI.StringController = function() { this.type = "string"; var _this = this; - Controller.apply(this, arguments); + GUI.Controller.apply(this, arguments); var input = document.createElement('input'); @@ -30,5 +30,4 @@ var StringController = function() { }; -StringController.prototype = new Controller(); -StringController.prototype.constructor = StringController; +GUI.extendController(GUI.StringController); \ No newline at end of file diff --git a/controllers/scrubber.js b/controllers/scrubber.js new file mode 100644 index 0000000..8ea11cc --- /dev/null +++ b/controllers/scrubber.js @@ -0,0 +1,278 @@ +// Would really love to make it so that as FEW changes as possible are required to gui.js in order to make this work. Would love to make it so you simply include gui.scrubber.min.js in addition to gui.min.js. + +GUI.Controller.prototype.at = function(when, what, tween) { + + this.scrubber.addPoint(new GUI.ScrubberPoint(this.scrubber, when, what)); + this.scrubber.render(); + return this; + +} + +GUI.Scrubber = function(controller, timer) { + + var _this = this; + + var points = []; + this.timer = timer; + this.controller = controller; + this.controller.scrubber = this; + + + this.domElement = document.createElement('div'); + this.domElement.setAttribute('class', 'guidat-time'); + + var canvas = document.createElement('canvas'); + this.domElement.appendChild(canvas); + + this.g = canvas.getContext('2d'); + + var width; + var height; + + this.__defineGetter__("width", function() { + return width; + }); + + this.__defineGetter__("height", function() { + return height; + }); + + controller.domElement.insertBefore(this.domElement, controller.propertyNameElement.nextSibling); + + + this.addPoint = function(point) { + points.push(point); + points.sort(function(a,b) { + return a.time - b.time; + }); + } + + + + this.render = function() { + + // TODO: if visible ... + _this.g.clearRect(0, 0, width, height); + + for (var i in points) { + points[i].render(); + } + + // Draw playhead + + _this.g.strokeStyle = "red"; + var t = Math.round(GUI.map(_this.timer.playhead, _this.timer.windowMin, _this.timer.windowMin+_this.timer.windowWidth, 0, width))+0.5; + _this.g.beginPath(); + _this.g.moveTo(t, 0); + _this.g.lineTo(t, height); + _this.g.stroke(); + + } + + this.render(); + + var onResize = function() { + canvas.width = width = _this.domElement.offsetWidth; + canvas.height = height = _this.domElement.offsetHeight; + _this.render(); + }; + + window.addEventListener('resize', function(e) { + onResize(); + }, false); + + onResize(); + + this.timer.addPlayListener(this.render); + + var onPlayChange = function(curTime, prevTime) { + + // This assumes a SORTED point array + // And a PROGRESSING/INCREASING/GROWING playhead + for (var i = 0; i < points.length; i++) { + var cur = points[i]; + + if (cur.time < prevTime) { + continue; + } + + if (cur.time >= prevTime && cur.time <= curTime) { + pointHandlers[_this.controller.type].call(_this, cur); + } + + } + + }; + + var pointHandlers = { + + 'function': function(point) { + this.controller.getValue().call(this); + }, + + 'boolean': function(point) { + this.controller.setValue(point.value); + }, + + 'string': function(point) { + this.controller.setValue(point.value); + }, + + 'number': function(point) { + // + } + + } + + this.timer.addPlayListener(onPlayChange); + + this.timer.addWindowListener(this.render); + + +}; + +GUI.ScrubberPoint = function(scrubber, time, value) { + + var _this = this; + + var g = scrubber.g; + var timer = scrubber.timer; + this.value = value; + var rectSize = 4; + + this.__defineGetter__("time", function() { + return time; + }); + + this.render = function() { + + var x = GUI.map(time, timer.windowMin, timer.windowMin+timer.windowWidth, 0, 1); + + if (x >= 0 && x <= 1) { + x = Math.round(GUI.map(x, 0, 1, 0, scrubber.width)); + } + + g.save(); + g.translate(x-rectSize/2, 0); + g.fillStyle = "#ffd800"; + g.fillRect(0, 0, rectSize/2, scrubber.height-1); + g.fillStyle = "#ff9000"; + g.fillRect(rectSize/2, 0, rectSize/2, scrubber.height-1); + g.restore(); + + } + +} + +GUI.Timer = function(gui) { + + var _this = this; + + this.gui = gui; + this.gui.timer = this; + + var playhead = 0; + var lastPlayhead = 0; + var playListeners = []; + var windowListeners = []; + + var windowMin = 0; + var windowWidth = 10000; + + var thisTime; + var lastTime; + + var playInterval = -1; + var playResolution = 1000/60; + + var playing = false; + + var millis = function() { + var d = new Date(); + return d.getTime(); + } + + this.__defineGetter__("windowMin", function() { + return windowMin; + }); + + this.__defineSetter__("windowMin", function(v) { + windowMin = v; + for (var i in windowListeners) { + windowListeners[i].call(windowListeners[i]); + } + }); + + this.__defineGetter__("windowWidth", function() { + return windowWidth; + }); + + + this.__defineSetter__("windowWidth", function(v) { + windowWidth = v; + for (var i in windowListeners) { + windowListeners[i].call(windowListeners[i]); + } + }); + + this.__defineGetter__("playhead", function() { + return playhead; + }); + + this.__defineSetter__("playhead", function(t) { + lastPlayhead = playhead; + playhead = t; + for (var i = 0; i < playListeners.length; i++) { + playListeners[i].call(this, playhead, lastPlayhead); + } + }); + + this.__defineGetter__("playing", function() { + return playing; + }); + + this.play = function() { + playing = true; + lastTime = millis(); + if (playInterval == -1) { + playInterval = setInterval(this.update, playResolution); + } + }; + + this.update = function() { + thisTime = millis(); + _this.playhead = _this.playhead + (thisTime - lastTime); + lastTime = thisTime; + }; + + this.pause = function() { + playing = false; + clearInterval(playInterval); + playInterval = -1; + }; + + this.playPause = function() { + if (playing) { + this.pause(); + } else { + this.play(); + } + } + + + this.stop = function() { + this.pause(); + this.playhead = 0; + }; + + this.addPlayListener = function(fnc) { + playListeners.push(fnc); + }; + + this.addWindowListener = function(fnc) { + windowListeners.push(fnc); + }; + + + +} \ No newline at end of file diff --git a/controllers/slider.js b/controllers/slider.js index ee2902e..9c7c28e 100644 --- a/controllers/slider.js +++ b/controllers/slider.js @@ -1,4 +1,4 @@ -var Slider = function(numberController, min, max, step, initValue) { +GUI.Slider = function(numberController, min, max, step, initValue) { var min = min; var max = max; diff --git a/demo/demo.css b/demo/demo.css index 966a9ad..9e5ce3d 100644 --- a/demo/demo.css +++ b/demo/demo.css @@ -66,10 +66,6 @@ div.collapsed h2, div.expanded h2 { width: 100%; cursor: pointer; } -h2.last { -border-bottom: 1px dotted #ccc; -margin-bottom: 20px; -} div.expanded h2:before { content: '-'; @@ -162,10 +158,12 @@ a:active { } footer { +margin-top: 20px; background-color: #eee; width: 510px; padding: 10px; clear: both; + color: #444; } diff --git a/gui-bare.css b/gui-bare.css deleted file mode 100644 index a61a1f2..0000000 --- a/gui-bare.css +++ /dev/null @@ -1,26 +0,0 @@ -#guidat { - position: fixed; - width: 250px; - z-index: 200; - top: 0; - left: 100%; - margin-left: -270px; -} - -#guidat-controllers { - height: 300px; - overflow-y: auto; -} - -#guidat-toggle { - cursor: pointer; -} - -.guidat-controller { - clear: both; -} - -.guidat-controller input { - float: right; - clear: both; -} diff --git a/gui.css b/gui.css index 5ba1d17..db30521 100644 --- a/gui.css +++ b/gui.css @@ -13,14 +13,14 @@ } .guidat-time { -width: 75%; -float: right; -margin-top: -3px; -margin-right: -2px; -margin-left: 3px; -background-color: #333; -border-bottom: 1px solid #444; -height: 31px; + width: 75%; + float: right; + margin-top: -3px; + margin-right: -2px; + margin-left: 3px; + background-color: #333; + border-bottom: 1px solid #444; + height: 31px; } .guidat { @@ -47,6 +47,11 @@ height: 31px; */ } +.guidat-controllers hr { +height: 0; +border-top: 1px solid #000; +} + a.guidat-toggle { text-decoration: none; cursor: pointer; diff --git a/gui.js b/gui.js index cb9cffd..a8b4c93 100644 --- a/gui.js +++ b/gui.js @@ -17,9 +17,6 @@ var GUI = function() { var curControllerContainerHeight = 0; - // How big we get when we open - - var _this = this; var open = false; @@ -27,6 +24,8 @@ var GUI = function() { // 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; @@ -41,7 +40,10 @@ var GUI = function() { var controllerContainer = document.createElement('div'); controllerContainer.setAttribute('class', 'guidat-controllers'); - // Firefox hack for horizontal scrolling + // For use with GUIScrubber + this.timer = null; + + // Firefox hack to prevent horizontal scrolling controllerContainer.addEventListener('DOMMouseScroll', function(e) { var scrollAmount = this.scrollTop; @@ -80,19 +82,17 @@ var GUI = function() { 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; + var dmy = pmy-my; + + 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; @@ -107,7 +107,7 @@ var GUI = function() { dragDisplacementY += dmy; dragDisplacementX += dmx; openHeight += dmy; - width += dmx; + //width += dmx; curControllerContainerHeight += dmy; controllerContainer.style.height = openHeight+'px'; width = GUI.constrain(width, MIN_WIDTH, MAX_WIDTH); @@ -141,9 +141,9 @@ var GUI = function() { _this.toggle(); // Clears lingering slider column - _this.domElement.style.width = (width+1)+'px'; + // _this.domElement.style.width = (width+1)+'px'; setTimeout(function() { - _this.domElement.style.width = width+'px'; + // _this.domElement.style.width = width+'px'; }, 1); } @@ -194,8 +194,8 @@ var GUI = function() { }, false); - this.domElement.appendChild(controllerContainer); this.domElement.appendChild(toggleButton); + this.domElement.appendChild(controllerContainer); if (GUI.autoPlace) { if(GUI.autoPlaceContainer == null) { @@ -259,13 +259,6 @@ var GUI = function() { this.autoListen = true; - var handlerTypes = { - "number": NumberController, - "string": StringController, - "boolean": BooleanController, - "function": FunctionController - }; - var alreadyControlled = function(object, propertyName) { for (var i in controllers) { if (controllers[i].object == object && @@ -284,10 +277,22 @@ var GUI = function() { F.prototype = constructor.prototype; return new F(); }; + + this.divider = function() { + controllerContainer.appendChild(document.createElement('hr')); + } this.add = function() { - + var object = arguments[0]; + + if (arguments.length == 1) { + for (var i in object) { + this.add(object, i); + } + return; + } + var propertyName = arguments[1]; // Have we already added this? @@ -347,6 +352,10 @@ var GUI = function() { openHeight = controllerHeight; } + if (this.timer != null) { + new GUI.Scrubber(controllerObject, this.timer); + } + return controllerObject; } @@ -361,14 +370,13 @@ var GUI = function() { } else { controllerContainer.style.overflowY = "hidden"; } - // console.log(controllerHeight, openHeight); } - var addHandlers = { - "number": NumberController, - "string": StringController, - "boolean": BooleanController, - "function": FunctionController + var handlerTypes = { + "number": GUI.NumberController, + "string": GUI.StringController, + "boolean": GUI.BooleanController, + "function": GUI.FunctionController }; var alreadyControlled = function(object, propertyName) { @@ -390,7 +398,7 @@ var GUI = function() { }; this.reset = function() { - // + // TODO } // GUI ... GUI @@ -485,10 +493,8 @@ GUI.saveURL = function() { GUI.scrollTop = -1; -// TODO: Not working in FF. GUI.load = function(saveString) { - //GUI.savedAppearanceVars = []; var vals = saveString.split(","); var numGuis = parseInt(vals[0]); @@ -499,6 +505,7 @@ GUI.load = function(saveString) { } GUI.savedValues = vals.splice(2, vals.length); + }; GUI.savedValues = []; @@ -616,7 +623,7 @@ GUI.constrain = function (v, o1, o2) { GUI.error = function(str) { if (typeof console.error == 'function') { - console.GUI.error("[GUI ERROR] " + str); + console.error("[GUI ERROR] " + str); } }; @@ -625,4 +632,9 @@ GUI.roundToDecimal = function(n, decimals) { return Math.round(n*t)/t; } +GUI.extendController = function(clazz) { + clazz.prototype = new GUI.Controller(); + clazz.prototype.constructor = clazz; +} + if (GUI.getVarFromURL('saveString') != null) GUI.load(GUI.getVarFromURL('saveString')); \ No newline at end of file diff --git a/index.html b/index.html index 783b71f..c115756 100644 --- a/index.html +++ b/index.html @@ -3,8 +3,6 @@ - - gui-dat @@ -22,6 +20,8 @@ + @@ -34,17 +34,18 @@ window.onload = function() { - prettyPrint(); - - var fizzyText = new FizzyText("gui-dat"); var gui = new GUI(); - - - // Text field - gui.add(fizzyText, "message"); + var timer = new GUI.Timer(gui); + + // Text field + gui.add(fizzyText, "message") + .at(1000, "is") + .at(2000, "the") + .at(3000, "shit"); + // Sliders with min and max gui.add(fizzyText, "maxSize", 0.5, 7); gui.add(fizzyText, "growthSpeed", 0.01, 1); @@ -57,245 +58,35 @@ gui.add(fizzyText, "displayOutline"); // Fires a function called "explode" - gui.add(fizzyText, "explode").name("Explode!"); // Specify a custom name. + gui.add(fizzyText, "explode") + .at(1000) + .at(2000) + .at(3000); // Specify a custom name. - gui.add(GUI, "showSaveString"); - gui.add(GUI, "saveURL"); + gui.divider(); + + // gui.add(timer, "playhead").step(100).listen(); + gui.add(timer, "playPause"); + /* + gui.add(timer, "windowMin").listen(); + gui.add(timer, "windowWidth").listen(); + */ + + + //gui.add(timer, "pause"); - - // Javascript for documentation - getCollapsables(); - handleListening(); }; - function toggle(e) { - - if(this.className == 'collapsed') { - this.className = 'expanded'; - } else { - this.className = 'collapsed'; - } - } - - function getCollapsables() { - - if (document.getElementsByClassName == undefined) { - document.getElementsByClassName = function(className) - { - var hasClassName = new RegExp("(?:^|\\s)" + className + "(?:$|\\s)"); - var allElements = document.getElementsByTagName("*"); - var results = []; - - var element; - for (var i = 0; (element = allElements[i]) != null; i++) { - var elementClass = element.className; - if (elementClass && elementClass.indexOf(className) != -1 && hasClassName.test(elementClass)) - results.push(element); - } - - return results; - }; - } - - collapsed = document.getElementsByClassName('collapsed'); - expanded = document.getElementsByClassName('expanded'); - } - - function handleListening() { - - for(var i = 0; i < collapsed.length; i++) { - collapsed[i].addEventListener('click', toggle, false); - } - - for(var j = 0; j < expanded.length; j++) { - expanded[i].addEventListener('click', toggle, false); - } - } //]]> -
- -
-

GUI-DAT flag

- -

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

- - - -

Basic Usage

-
-<script type="text/javascript" src="gui.min.js"></script>
-<script type="text/javascript">
-
-window.onload = function() {
-
-   var fizzyText = new FizzyText("gui-dat");
-
-   var gui = new GUI();
-
-   // Text field
-   gui.add(fizzyText, "message");
-
-   // Sliders with min and max
-   gui.add(fizzyText, "maxSize", 0.5, 7);
-   gui.add(fizzyText, "growthSpeed", 0.01, 1);
-   gui.add(fizzyText, "speed", 0.1, 2);
-
-   // Sliders with min, max and increment.
-   gui.add(fizzyText, "noiseStrength", 10, 100, 5);
-
-   // Boolean checkbox
-   gui.add(fizzyText, "displayOutline");
-
-   // Fires a function called "explode"
-   gui.add(fizzyText, "explode").name("Explode!"); // Specify a custom name.
-
-};
-
-</script>
-
- - - - - - - - - - - - - - - - -