diff --git a/src/dat/controllers/UndefinedController.js b/src/dat/controllers/UndefinedController.js new file mode 100644 index 0000000..3a15d7f --- /dev/null +++ b/src/dat/controllers/UndefinedController.js @@ -0,0 +1,64 @@ +/** + * dat-gui JavaScript Controller Library + * http://code.google.com/p/dat-gui + * + * Copyright 2011 Data Arts Team, Google Creative Lab + * + * UndefinedController (an adapted StringController) by mikenon + * + * 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/Controller', + 'dat/dom/dom', + 'dat/utils/common' +], function (Controller, dom, common) { + + /** + * @class Provides a *disabled* text input indicating the value of the property + * is undefined. Calling reset() on the controller will remove and re-add it. + * It is intended to be used as a placeholder, where the gui is built before + * some required variable has been initialized. + * + * @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 UndefinedController = function (object, property) { + UndefinedController.superclass.call(this, object, property); + + var _this = this; + + this.__input = document.createElement('input'); + this.__input.setAttribute('type', 'text'); + this.__input.setAttribute('disabled', true); + this.domElement.appendChild(this.__input); + }; + + UndefinedController.superclass = Controller; + + common.extend( + UndefinedController.prototype, + Controller.prototype, { + updateDisplay: function () { + if (this.__onFinishChange) { + if (!common.isUndefined(this.object[this.property])) { + this.__onFinishChange.call(this.object[this.property]) + } + } + return UndefinedController.superclass.prototype.updateDisplay.call(this); + }, + + } + ); + + return UndefinedController; +}); diff --git a/src/dat/controllers/factory.js b/src/dat/controllers/factory.js index 2ce8ecc..87cba83 100644 --- a/src/dat/controllers/factory.js +++ b/src/dat/controllers/factory.js @@ -18,9 +18,10 @@ define([ 'dat/controllers/StringController', 'dat/controllers/FunctionController', 'dat/controllers/BooleanController', + 'dat/controllers/UndefinedController', 'dat/utils/common' ], - function (OptionController, NumberControllerBox, NumberControllerSlider, StringController, FunctionController, BooleanController, common) { + function (OptionController, NumberControllerBox, NumberControllerSlider, StringController, FunctionController, BooleanController, UndefinedController, common) { return function (object, property) { @@ -63,6 +64,10 @@ define([ return new BooleanController(object, property); } + if (common.isUndefined(initialValue)) { + return new UndefinedController(object, property); + } + } }); diff --git a/src/dat/gui/GUI.js b/src/dat/gui/GUI.js index c9830c2..0f9463c 100644 --- a/src/dat/gui/GUI.js +++ b/src/dat/gui/GUI.js @@ -26,6 +26,7 @@ define([ 'dat/controllers/NumberControllerSlider', 'dat/controllers/OptionController', 'dat/controllers/ColorController', + 'dat/controllers/UndefinedController', 'dat/utils/requestAnimationFrame', @@ -34,7 +35,7 @@ define([ 'dat/utils/common' -], function (css, saveDialogueContents, styleSheet, controllerFactory, Controller, BooleanController, FunctionController, NumberControllerBox, NumberControllerSlider, OptionController, ColorController, requestAnimationFrame, CenteredDiv, dom, common) { +], function (css, saveDialogueContents, styleSheet, controllerFactory, Controller, BooleanController, FunctionController, NumberControllerBox, NumberControllerSlider, OptionController, ColorController, UndefinedController, requestAnimationFrame, CenteredDiv, dom, common) { css.inject(styleSheet); @@ -814,7 +815,7 @@ define([ function add(gui, object, property, params) { - if (object[property] === undefined) { + if (!common.hasOwnProperty(object, property)) { throw new Error("Object " + object + " has no property \"" + property + "\""); } @@ -1017,6 +1018,17 @@ define([ controller.updateDisplay(); + } else if (controller instanceof UndefinedController) { + controller.__onFinishChange = function (val) { + controller.remove(); + return add( + gui, + controller.object, + controller.property, { + before: controller.__li.nextElementSibling + } + ); + } } controller.setValue = common.compose(function (r) { diff --git a/src/dat/gui/style.css b/src/dat/gui/style.css index da2954f..1728f38 100644 --- a/src/dat/gui/style.css +++ b/src/dat/gui/style.css @@ -412,3 +412,11 @@ .dg .c .slider:hover .slider-fg { background: #44abda; } + +.dg .cr.undefined { + border-left: 3px dotted #919191; +} + +.dg .cr.undefined input[type=text] { + background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAJElEQVQIW2OcOHHi//z8fEYGJADmoEvAVSBLoGiDSaAIwowCAAMYEriV4NZdAAAAAElFTkSuQmCC) repeat; +} diff --git a/src/dat/utils/common.js b/src/dat/utils/common.js index 59dc09f..3cdf93a 100644 --- a/src/dat/utils/common.js +++ b/src/dat/utils/common.js @@ -132,6 +132,12 @@ define([], function () { isFunction: function (obj) { return Object.prototype.toString.call(obj) === '[object Function]'; + }, + + hasOwnProperty: function (obj, prop) { + var proto = obj.__proto__ || obj.constructor.prototype; + return (prop in obj) && + (!(prop in proto) || (proto[prop] !== obj[prop])); } };