diff options
| author | Federico Igne <git@federicoigne.com> | 2022-08-13 11:33:30 +0100 |
|---|---|---|
| committer | Federico Igne <git@federicoigne.com> | 2022-08-13 11:33:30 +0100 |
| commit | 2fd9cc46ff2428bb2fa891f47002e9d3e7585583 (patch) | |
| tree | 81b8b0122a00e9cd06d5f55091a3b087e430ddb0 /lib/dat.gui.js | |
| download | rubik-2fd9cc46ff2428bb2fa891f47002e9d3e7585583.tar.gz rubik-2fd9cc46ff2428bb2fa891f47002e9d3e7585583.zip | |
init: import project
Diffstat (limited to 'lib/dat.gui.js')
| -rwxr-xr-x | lib/dat.gui.js | 3672 |
1 files changed, 3672 insertions, 0 deletions
diff --git a/lib/dat.gui.js b/lib/dat.gui.js new file mode 100755 index 0000000..315c470 --- /dev/null +++ b/lib/dat.gui.js | |||
| @@ -0,0 +1,3672 @@ | |||
| 1 | /** | ||
| 2 | * dat-gui JavaScript Controller Library | ||
| 3 | * http://code.google.com/p/dat-gui | ||
| 4 | * | ||
| 5 | * Copyright 2011 Data Arts Team, Google Creative Lab | ||
| 6 | * | ||
| 7 | * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 8 | * you may not use this file except in compliance with the License. | ||
| 9 | * You may obtain a copy of the License at | ||
| 10 | * | ||
| 11 | * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 12 | */ | ||
| 13 | |||
| 14 | /** @namespace */ | ||
| 15 | var dat = dat || {}; | ||
| 16 | |||
| 17 | /** @namespace */ | ||
| 18 | dat.gui = dat.gui || {}; | ||
| 19 | |||
| 20 | /** @namespace */ | ||
| 21 | dat.utils = dat.utils || {}; | ||
| 22 | |||
| 23 | /** @namespace */ | ||
| 24 | dat.controllers = dat.controllers || {}; | ||
| 25 | |||
| 26 | /** @namespace */ | ||
| 27 | dat.dom = dat.dom || {}; | ||
| 28 | |||
| 29 | /** @namespace */ | ||
| 30 | dat.color = dat.color || {}; | ||
| 31 | |||
| 32 | dat.utils.css = (function () { | ||
| 33 | return { | ||
| 34 | load: function (url, doc) { | ||
| 35 | doc = doc || document; | ||
| 36 | var link = doc.createElement('link'); | ||
| 37 | link.type = 'text/css'; | ||
| 38 | link.rel = 'stylesheet'; | ||
| 39 | link.href = url; | ||
| 40 | doc.getElementsByTagName('head')[0].appendChild(link); | ||
| 41 | }, | ||
| 42 | inject: function(css, doc) { | ||
| 43 | doc = doc || document; | ||
| 44 | var injected = document.createElement('style'); | ||
| 45 | injected.type = 'text/css'; | ||
| 46 | injected.innerHTML = css; | ||
| 47 | doc.getElementsByTagName('head')[0].appendChild(injected); | ||
| 48 | } | ||
| 49 | } | ||
| 50 | })(); | ||
| 51 | |||
| 52 | |||
| 53 | dat.utils.common = (function () { | ||
| 54 | |||
| 55 | var ARR_EACH = Array.prototype.forEach; | ||
| 56 | var ARR_SLICE = Array.prototype.slice; | ||
| 57 | |||
| 58 | /** | ||
| 59 | * Band-aid methods for things that should be a lot easier in JavaScript. | ||
| 60 | * Implementation and structure inspired by underscore.js | ||
| 61 | * http://documentcloud.github.com/underscore/ | ||
| 62 | */ | ||
| 63 | |||
| 64 | return { | ||
| 65 | |||
| 66 | BREAK: {}, | ||
| 67 | |||
| 68 | extend: function(target) { | ||
| 69 | |||
| 70 | this.each(ARR_SLICE.call(arguments, 1), function(obj) { | ||
| 71 | |||
| 72 | for (var key in obj) | ||
| 73 | if (!this.isUndefined(obj[key])) | ||
| 74 | target[key] = obj[key]; | ||
| 75 | |||
| 76 | }, this); | ||
| 77 | |||
| 78 | return target; | ||
| 79 | |||
| 80 | }, | ||
| 81 | |||
| 82 | defaults: function(target) { | ||
| 83 | |||
| 84 | this.each(ARR_SLICE.call(arguments, 1), function(obj) { | ||
| 85 | |||
| 86 | for (var key in obj) | ||
| 87 | if (this.isUndefined(target[key])) | ||
| 88 | target[key] = obj[key]; | ||
| 89 | |||
| 90 | }, this); | ||
| 91 | |||
| 92 | return target; | ||
| 93 | |||
| 94 | }, | ||
| 95 | |||
| 96 | compose: function() { | ||
| 97 | var toCall = ARR_SLICE.call(arguments); | ||
| 98 | return function() { | ||
| 99 | var args = ARR_SLICE.call(arguments); | ||
| 100 | for (var i = toCall.length -1; i >= 0; i--) { | ||
| 101 | args = [toCall[i].apply(this, args)]; | ||
| 102 | } | ||
| 103 | return args[0]; | ||
| 104 | } | ||
| 105 | }, | ||
| 106 | |||
| 107 | each: function(obj, itr, scope) { | ||
| 108 | |||
| 109 | if (!obj) return; | ||
| 110 | |||
| 111 | if (ARR_EACH && obj.forEach && obj.forEach === ARR_EACH) { | ||
| 112 | |||
| 113 | obj.forEach(itr, scope); | ||
| 114 | |||
| 115 | } else if (obj.length === obj.length + 0) { // Is number but not NaN | ||
| 116 | |||
| 117 | for (var key = 0, l = obj.length; key < l; key++) | ||
| 118 | if (key in obj && itr.call(scope, obj[key], key) === this.BREAK) | ||
| 119 | return; | ||
| 120 | |||
| 121 | } else { | ||
| 122 | |||
| 123 | for (var key in obj) | ||
| 124 | if (itr.call(scope, obj[key], key) === this.BREAK) | ||
| 125 | return; | ||
| 126 | |||
| 127 | } | ||
| 128 | |||
| 129 | }, | ||
| 130 | |||
| 131 | defer: function(fnc) { | ||
| 132 | setTimeout(fnc, 0); | ||
| 133 | }, | ||
| 134 | |||
| 135 | toArray: function(obj) { | ||
| 136 | if (obj.toArray) return obj.toArray(); | ||
| 137 | return ARR_SLICE.call(obj); | ||
| 138 | }, | ||
| 139 | |||
| 140 | isUndefined: function(obj) { | ||
| 141 | return obj === undefined; | ||
| 142 | }, | ||
| 143 | |||
| 144 | isNull: function(obj) { | ||
| 145 | return obj === null; | ||
| 146 | }, | ||
| 147 | |||
| 148 | isNaN: function(obj) { | ||
| 149 | return obj !== obj; | ||
| 150 | }, | ||
| 151 | |||
| 152 | isArray: Array.isArray || function(obj) { | ||
| 153 | return obj.constructor === Array; | ||
| 154 | }, | ||
| 155 | |||
| 156 | isObject: function(obj) { | ||
| 157 | return obj === Object(obj); | ||
| 158 | }, | ||
| 159 | |||
| 160 | isNumber: function(obj) { | ||
| 161 | return obj === obj+0; | ||
| 162 | }, | ||
| 163 | |||
| 164 | isString: function(obj) { | ||
| 165 | return obj === obj+''; | ||
| 166 | }, | ||
| 167 | |||
| 168 | isBoolean: function(obj) { | ||
| 169 | return obj === false || obj === true; | ||
| 170 | }, | ||
| 171 | |||
| 172 | isFunction: function(obj) { | ||
| 173 | return Object.prototype.toString.call(obj) === '[object Function]'; | ||
| 174 | } | ||
| 175 | |||
| 176 | }; | ||
| 177 | |||
| 178 | })(); | ||
| 179 | |||
| 180 | |||
| 181 | dat.controllers.Controller = (function (common) { | ||
| 182 | |||
| 183 | /** | ||
| 184 | * @class An "abstract" class that represents a given property of an object. | ||
| 185 | * | ||
| 186 | * @param {Object} object The object to be manipulated | ||
| 187 | * @param {string} property The name of the property to be manipulated | ||
| 188 | * | ||
| 189 | * @member dat.controllers | ||
| 190 | */ | ||
| 191 | var Controller = function(object, property) { | ||
| 192 | |||
| 193 | this.initialValue = object[property]; | ||
| 194 | |||
| 195 | /** | ||
| 196 | * Those who extend this class will put their DOM elements in here. | ||
| 197 | * @type {DOMElement} | ||
| 198 | */ | ||
| 199 | this.domElement = document.createElement('div'); | ||
| 200 | |||
| 201 | /** | ||
| 202 | * The object to manipulate | ||
| 203 | * @type {Object} | ||
| 204 | */ | ||
| 205 | this.object = object; | ||
| 206 | |||
| 207 | /** | ||
| 208 | * The name of the property to manipulate | ||
| 209 | * @type {String} | ||
| 210 | */ | ||
| 211 | this.property = property; | ||
| 212 | |||
| 213 | /** | ||
| 214 | * The function to be called on change. | ||
| 215 | * @type {Function} | ||
| 216 | * @ignore | ||
| 217 | */ | ||
| 218 | this.__onChange = undefined; | ||
| 219 | |||
| 220 | /** | ||
| 221 | * The function to be called on finishing change. | ||
| 222 | * @type {Function} | ||
| 223 | * @ignore | ||
| 224 | */ | ||
| 225 | this.__onFinishChange = undefined; | ||
| 226 | |||
| 227 | }; | ||
| 228 | |||
| 229 | common.extend( | ||
| 230 | |||
| 231 | Controller.prototype, | ||
| 232 | |||
| 233 | /** @lends dat.controllers.Controller.prototype */ | ||
| 234 | { | ||
| 235 | |||
| 236 | /** | ||
| 237 | * Specify that a function fire every time someone changes the value with | ||
| 238 | * this Controller. | ||
| 239 | * | ||
| 240 | * @param {Function} fnc This function will be called whenever the value | ||
| 241 | * is modified via this Controller. | ||
| 242 | * @returns {dat.controllers.Controller} this | ||
| 243 | */ | ||
| 244 | onChange: function(fnc) { | ||
| 245 | this.__onChange = fnc; | ||
| 246 | return this; | ||
| 247 | }, | ||
| 248 | |||
| 249 | /** | ||
| 250 | * Specify that a function fire every time someone "finishes" changing | ||
| 251 | * the value wih this Controller. Useful for values that change | ||
| 252 | * incrementally like numbers or strings. | ||
| 253 | * | ||
| 254 | * @param {Function} fnc This function will be called whenever | ||
| 255 | * someone "finishes" changing the value via this Controller. | ||
| 256 | * @returns {dat.controllers.Controller} this | ||
| 257 | */ | ||
| 258 | onFinishChange: function(fnc) { | ||
| 259 | this.__onFinishChange = fnc; | ||
| 260 | return this; | ||
| 261 | }, | ||
| 262 | |||
| 263 | /** | ||
| 264 | * Change the value of <code>object[property]</code> | ||
| 265 | * | ||
| 266 | * @param {Object} newValue The new value of <code>object[property]</code> | ||
| 267 | */ | ||
| 268 | setValue: function(newValue) { | ||
| 269 | this.object[this.property] = newValue; | ||
| 270 | if (this.__onChange) { | ||
| 271 | this.__onChange.call(this, newValue); | ||
| 272 | } | ||
| 273 | this.updateDisplay(); | ||
| 274 | return this; | ||
| 275 | }, | ||
| 276 | |||
| 277 | /** | ||
| 278 | * Gets the value of <code>object[property]</code> | ||
| 279 | * | ||
| 280 | * @returns {Object} The current value of <code>object[property]</code> | ||
| 281 | */ | ||
| 282 | getValue: function() { | ||
| 283 | return this.object[this.property]; | ||
| 284 | }, | ||
| 285 | |||
| 286 | /** | ||
| 287 | * Refreshes the visual display of a Controller in order to keep sync | ||
| 288 | * with the object's current value. | ||
| 289 | * @returns {dat.controllers.Controller} this | ||
| 290 | */ | ||
| 291 | updateDisplay: function() { | ||
| 292 | return this; | ||
| 293 | }, | ||
| 294 | |||
| 295 | /** | ||
| 296 | * @returns {Boolean} true if the value has deviated from initialValue | ||
| 297 | */ | ||
| 298 | isModified: function() { | ||
| 299 | return this.initialValue !== this.getValue() | ||
| 300 | } | ||
| 301 | |||
| 302 | } | ||
| 303 | |||
| 304 | ); | ||
| 305 | |||
| 306 | return Controller; | ||
| 307 | |||
| 308 | |||
| 309 | })(dat.utils.common); | ||
| 310 | |||
| 311 | |||
| 312 | dat.dom.dom = (function (common) { | ||
| 313 | |||
| 314 | var EVENT_MAP = { | ||
| 315 | 'HTMLEvents': ['change'], | ||
| 316 | 'MouseEvents': ['click','mousemove','mousedown','mouseup', 'mouseover'], | ||
| 317 | 'KeyboardEvents': ['keydown'] | ||
| 318 | }; | ||
| 319 | |||
| 320 | var EVENT_MAP_INV = {}; | ||
| 321 | common.each(EVENT_MAP, function(v, k) { | ||
| 322 | common.each(v, function(e) { | ||
| 323 | EVENT_MAP_INV[e] = k; | ||
| 324 | }); | ||
| 325 | }); | ||
| 326 | |||
| 327 | var CSS_VALUE_PIXELS = /(\d+(\.\d+)?)px/; | ||
| 328 | |||
| 329 | function cssValueToPixels(val) { | ||
| 330 | |||
| 331 | if (val === '0' || common.isUndefined(val)) return 0; | ||
| 332 | |||
| 333 | var match = val.match(CSS_VALUE_PIXELS); | ||
| 334 | |||
| 335 | if (!common.isNull(match)) { | ||
| 336 | return parseFloat(match[1]); | ||
| 337 | } | ||
| 338 | |||
| 339 | // TODO ...ems? %? | ||
| 340 | |||
| 341 | return 0; | ||
| 342 | |||
| 343 | } | ||
| 344 | |||
| 345 | /** | ||
| 346 | * @namespace | ||
| 347 | * @member dat.dom | ||
| 348 | */ | ||
| 349 | var dom = { | ||
| 350 | |||
| 351 | /** | ||
| 352 | * | ||
| 353 | * @param elem | ||
| 354 | * @param selectable | ||
| 355 | */ | ||
| 356 | makeSelectable: function(elem, selectable) { | ||
| 357 | |||
| 358 | if (elem === undefined || elem.style === undefined) return; | ||
| 359 | |||
| 360 | elem.onselectstart = selectable ? function() { | ||
| 361 | return false; | ||
| 362 | } : function() { | ||
| 363 | }; | ||
| 364 | |||
| 365 | elem.style.MozUserSelect = selectable ? 'auto' : 'none'; | ||
| 366 | elem.style.KhtmlUserSelect = selectable ? 'auto' : 'none'; | ||
| 367 | elem.unselectable = selectable ? 'on' : 'off'; | ||
| 368 | |||
| 369 | }, | ||
| 370 | |||
| 371 | /** | ||
| 372 | * | ||
| 373 | * @param elem | ||
| 374 | * @param horizontal | ||
| 375 | * @param vertical | ||
| 376 | */ | ||
| 377 | makeFullscreen: function(elem, horizontal, vertical) { | ||
| 378 | |||
| 379 | if (common.isUndefined(horizontal)) horizontal = true; | ||
| 380 | if (common.isUndefined(vertical)) vertical = true; | ||
| 381 | |||
| 382 | elem.style.position = 'absolute'; | ||
| 383 | |||
| 384 | if (horizontal) { | ||
| 385 | elem.style.left = 0; | ||
| 386 | elem.style.right = 0; | ||
| 387 | } | ||
| 388 | if (vertical) { | ||
| 389 | elem.style.top = 0; | ||
| 390 | elem.style.bottom = 0; | ||
| 391 | } | ||
| 392 | |||
| 393 | }, | ||
| 394 | |||
| 395 | /** | ||
| 396 | * | ||
| 397 | * @param elem | ||
| 398 | * @param eventType | ||
| 399 | * @param params | ||
| 400 | */ | ||
| 401 | fakeEvent: function(elem, eventType, params, aux) { | ||
| 402 | params = params || {}; | ||
| 403 | var className = EVENT_MAP_INV[eventType]; | ||
| 404 | if (!className) { | ||
| 405 | throw new Error('Event type ' + eventType + ' not supported.'); | ||
| 406 | } | ||
| 407 | var evt = document.createEvent(className); | ||
| 408 | switch (className) { | ||
| 409 | case 'MouseEvents': | ||
| 410 | var clientX = params.x || params.clientX || 0; | ||
| 411 | var clientY = params.y || params.clientY || 0; | ||
| 412 | evt.initMouseEvent(eventType, params.bubbles || false, | ||
| 413 | params.cancelable || true, window, params.clickCount || 1, | ||
| 414 | 0, //screen X | ||
| 415 | 0, //screen Y | ||
| 416 | clientX, //client X | ||
| 417 | clientY, //client Y | ||
| 418 | false, false, false, false, 0, null); | ||
| 419 | break; | ||
| 420 | case 'KeyboardEvents': | ||
| 421 | var init = evt.initKeyboardEvent || evt.initKeyEvent; // webkit || moz | ||
| 422 | common.defaults(params, { | ||
| 423 | cancelable: true, | ||
| 424 | ctrlKey: false, | ||
| 425 | altKey: false, | ||
| 426 | shiftKey: false, | ||
| 427 | metaKey: false, | ||
| 428 | keyCode: undefined, | ||
| 429 | charCode: undefined | ||
| 430 | }); | ||
| 431 | init(eventType, params.bubbles || false, | ||
| 432 | params.cancelable, window, | ||
| 433 | params.ctrlKey, params.altKey, | ||
| 434 | params.shiftKey, params.metaKey, | ||
| 435 | params.keyCode, params.charCode); | ||
| 436 | break; | ||
| 437 | default: | ||
| 438 | evt.initEvent(eventType, params.bubbles || false, | ||
| 439 | params.cancelable || true); | ||
| 440 | break; | ||
| 441 | } | ||
| 442 | common.defaults(evt, aux); | ||
| 443 | elem.dispatchEvent(evt); | ||
| 444 | }, | ||
| 445 | |||
| 446 | /** | ||
| 447 | * | ||
| 448 | * @param elem | ||
| 449 | * @param event | ||
| 450 | * @param func | ||
| 451 | * @param bool | ||
| 452 | */ | ||
| 453 | bind: function(elem, event, func, bool) { | ||
| 454 | bool = bool || false; | ||
| 455 | if (elem.addEventListener) | ||
| 456 | elem.addEventListener(event, func, bool); | ||
| 457 | else if (elem.attachEvent) | ||
| 458 | elem.attachEvent('on' + event, func); | ||
| 459 | return dom; | ||
| 460 | }, | ||
| 461 | |||
| 462 | /** | ||
| 463 | * | ||
| 464 | * @param elem | ||
| 465 | * @param event | ||
| 466 | * @param func | ||
| 467 | * @param bool | ||
| 468 | */ | ||
| 469 | unbind: function(elem, event, func, bool) { | ||
| 470 | bool = bool || false; | ||
| 471 | if (elem.removeEventListener) | ||
| 472 | elem.removeEventListener(event, func, bool); | ||
| 473 | else if (elem.detachEvent) | ||
| 474 | elem.detachEvent('on' + event, func); | ||
| 475 | return dom; | ||
| 476 | }, | ||
| 477 | |||
| 478 | /** | ||
| 479 | * | ||
| 480 | * @param elem | ||
| 481 | * @param className | ||
| 482 | */ | ||
| 483 | addClass: function(elem, className) { | ||
| 484 | if (elem.className === undefined) { | ||
| 485 | elem.className = className; | ||
| 486 | } else if (elem.className !== className) { | ||
| 487 | var classes = elem.className.split(/ +/); | ||
| 488 | if (classes.indexOf(className) == -1) { | ||
| 489 | classes.push(className); | ||
| 490 | elem.className = classes.join(' ').replace(/^\s+/, '').replace(/\s+$/, ''); | ||
| 491 | } | ||
| 492 | } | ||
| 493 | return dom; | ||
| 494 | }, | ||
| 495 | |||
| 496 | /** | ||
| 497 | * | ||
| 498 | * @param elem | ||
| 499 | * @param className | ||
| 500 | */ | ||
| 501 | removeClass: function(elem, className) { | ||
| 502 | if (className) { | ||
| 503 | if (elem.className === undefined) { | ||
| 504 | // elem.className = className; | ||
| 505 | } else if (elem.className === className) { | ||
| 506 | elem.removeAttribute('class'); | ||
| 507 | } else { | ||
| 508 | var classes = elem.className.split(/ +/); | ||
| 509 | var index = classes.indexOf(className); | ||
| 510 | if (index != -1) { | ||
| 511 | classes.splice(index, 1); | ||
| 512 | elem.className = classes.join(' '); | ||
| 513 | } | ||
| 514 | } | ||
| 515 | } else { | ||
| 516 | elem.className = undefined; | ||
| 517 | } | ||
| 518 | return dom; | ||
| 519 | }, | ||
| 520 | |||
| 521 | hasClass: function(elem, className) { | ||
| 522 | return new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)').test(elem.className) || false; | ||
| 523 | }, | ||
| 524 | |||
| 525 | /** | ||
| 526 | * | ||
| 527 | * @param elem | ||
| 528 | */ | ||
| 529 | getWidth: function(elem) { | ||
| 530 | |||
| 531 | var style = getComputedStyle(elem); | ||
| 532 | |||
| 533 | return cssValueToPixels(style['border-left-width']) + | ||
| 534 | cssValueToPixels(style['border-right-width']) + | ||
| 535 | cssValueToPixels(style['padding-left']) + | ||
| 536 | cssValueToPixels(style['padding-right']) + | ||
| 537 | cssValueToPixels(style['width']); | ||
| 538 | }, | ||
| 539 | |||
| 540 | /** | ||
| 541 | * | ||
| 542 | * @param elem | ||
| 543 | */ | ||
| 544 | getHeight: function(elem) { | ||
| 545 | |||
| 546 | var style = getComputedStyle(elem); | ||
| 547 | |||
| 548 | return cssValueToPixels(style['border-top-width']) + | ||
| 549 | cssValueToPixels(style['border-bottom-width']) + | ||
| 550 | cssValueToPixels(style['padding-top']) + | ||
| 551 | cssValueToPixels(style['padding-bottom']) + | ||
| 552 | cssValueToPixels(style['height']); | ||
| 553 | }, | ||
| 554 | |||
| 555 | /** | ||
| 556 | * | ||
| 557 | * @param elem | ||
| 558 | */ | ||
| 559 | getOffset: function(elem) { | ||
| 560 | var offset = {left: 0, top:0}; | ||
| 561 | if (elem.offsetParent) { | ||
| 562 | do { | ||
| 563 | offset.left += elem.offsetLeft; | ||
| 564 | offset.top += elem.offsetTop; | ||
| 565 | } while (elem = elem.offsetParent); | ||
| 566 | } | ||
| 567 | return offset; | ||
| 568 | }, | ||
| 569 | |||
| 570 | // http://stackoverflow.com/posts/2684561/revisions | ||
| 571 | /** | ||
| 572 | * | ||
| 573 | * @param elem | ||
| 574 | */ | ||
| 575 | isActive: function(elem) { | ||
| 576 | return elem === document.activeElement && ( elem.type || elem.href ); | ||
| 577 | } | ||
| 578 | |||
| 579 | }; | ||
| 580 | |||
| 581 | return dom; | ||
| 582 | |||
| 583 | })(dat.utils.common); | ||
| 584 | |||
| 585 | |||
| 586 | dat.controllers.OptionController = (function (Controller, dom, common) { | ||
| 587 | |||
| 588 | /** | ||
| 589 | * @class Provides a select input to alter the property of an object, using a | ||
| 590 | * list of accepted values. | ||
| 591 | * | ||
| 592 | * @extends dat.controllers.Controller | ||
| 593 | * | ||
| 594 | * @param {Object} object The object to be manipulated | ||
| 595 | * @param {string} property The name of the property to be manipulated | ||
| 596 | * @param {Object|string[]} options A map of labels to acceptable values, or | ||
| 597 | * a list of acceptable string values. | ||
| 598 | * | ||
| 599 | * @member dat.controllers | ||
| 600 | */ | ||
| 601 | var OptionController = function(object, property, options) { | ||
| 602 | |||
| 603 | OptionController.superclass.call(this, object, property); | ||
| 604 | |||
| 605 | var _this = this; | ||
| 606 | |||
| 607 | /** | ||
| 608 | * The drop down menu | ||
| 609 | * @ignore | ||
| 610 | */ | ||
| 611 | this.__select = document.createElement('select'); | ||
| 612 | |||
| 613 | if (common.isArray(options)) { | ||
| 614 | var map = {}; | ||
| 615 | common.each(options, function(element) { | ||
| 616 | map[element] = element; | ||
| 617 | }); | ||
| 618 | options = map; | ||
| 619 | } | ||
| 620 | |||
| 621 | common.each(options, function(value, key) { | ||
| 622 | |||
| 623 | var opt = document.createElement('option'); | ||
| 624 | opt.innerHTML = key; | ||
| 625 | opt.setAttribute('value', value); | ||
| 626 | _this.__select.appendChild(opt); | ||
| 627 | |||
| 628 | }); | ||
| 629 | |||
| 630 | // Acknowledge original value | ||
| 631 | this.updateDisplay(); | ||
| 632 | |||
| 633 | dom.bind(this.__select, 'change', function() { | ||
| 634 | var desiredValue = this.options[this.selectedIndex].value; | ||
| 635 | _this.setValue(desiredValue); | ||
| 636 | }); | ||
| 637 | |||
| 638 | this.domElement.appendChild(this.__select); | ||
| 639 | |||
| 640 | }; | ||
| 641 | |||
| 642 | OptionController.superclass = Controller; | ||
| 643 | |||
| 644 | common.extend( | ||
| 645 | |||
| 646 | OptionController.prototype, | ||
| 647 | Controller.prototype, | ||
| 648 | |||
| 649 | { | ||
| 650 | |||
| 651 | setValue: function(v) { | ||
| 652 | var toReturn = OptionController.superclass.prototype.setValue.call(this, v); | ||
| 653 | if (this.__onFinishChange) { | ||
| 654 | this.__onFinishChange.call(this, this.getValue()); | ||
| 655 | } | ||
| 656 | return toReturn; | ||
| 657 | }, | ||
| 658 | |||
| 659 | updateDisplay: function() { | ||
| 660 | this.__select.value = this.getValue(); | ||
| 661 | return OptionController.superclass.prototype.updateDisplay.call(this); | ||
| 662 | } | ||
| 663 | |||
| 664 | } | ||
| 665 | |||
| 666 | ); | ||
| 667 | |||
| 668 | return OptionController; | ||
| 669 | |||
| 670 | })(dat.controllers.Controller, | ||
| 671 | dat.dom.dom, | ||
| 672 | dat.utils.common); | ||
| 673 | |||
| 674 | |||
| 675 | dat.controllers.NumberController = (function (Controller, common) { | ||
| 676 | |||
| 677 | /** | ||
| 678 | * @class Represents a given property of an object that is a number. | ||
| 679 | * | ||
| 680 | * @extends dat.controllers.Controller | ||
| 681 | * | ||
| 682 | * @param {Object} object The object to be manipulated | ||
| 683 | * @param {string} property The name of the property to be manipulated | ||
| 684 | * @param {Object} [params] Optional parameters | ||
| 685 | * @param {Number} [params.min] Minimum allowed value | ||
| 686 | * @param {Number} [params.max] Maximum allowed value | ||
| 687 | * @param {Number} [params.step] Increment by which to change value | ||
| 688 | * | ||
| 689 | * @member dat.controllers | ||
| 690 | */ | ||
| 691 | var NumberController = function(object, property, params) { | ||
| 692 | |||
| 693 | NumberController.superclass.call(this, object, property); | ||
| 694 | |||
| 695 | params = params || {}; | ||
| 696 | |||
| 697 | this.__min = params.min; | ||
| 698 | this.__max = params.max; | ||
| 699 | this.__step = params.step; | ||
| 700 | |||
| 701 | if (common.isUndefined(this.__step)) { | ||
| 702 | |||
| 703 | if (this.initialValue == 0) { | ||
| 704 | this.__impliedStep = 1; // What are we, psychics? | ||
| 705 | } else { | ||
| 706 | // Hey Doug, check this out. | ||
| 707 | this.__impliedStep = Math.pow(10, Math.floor(Math.log(this.initialValue)/Math.LN10))/10; | ||
| 708 | } | ||
| 709 | |||
| 710 | } else { | ||
| 711 | |||
| 712 | this.__impliedStep = this.__step; | ||
| 713 | |||
| 714 | } | ||
| 715 | |||
| 716 | this.__precision = numDecimals(this.__impliedStep); | ||
| 717 | |||
| 718 | |||
| 719 | }; | ||
| 720 | |||
| 721 | NumberController.superclass = Controller; | ||
| 722 | |||
| 723 | common.extend( | ||
| 724 | |||
| 725 | NumberController.prototype, | ||
| 726 | Controller.prototype, | ||
| 727 | |||
| 728 | /** @lends dat.controllers.NumberController.prototype */ | ||
| 729 | { | ||
| 730 | |||
| 731 | setValue: function(v) { | ||
| 732 | |||
| 733 | if (this.__min !== undefined && v < this.__min) { | ||
| 734 | v = this.__min; | ||
| 735 | } else if (this.__max !== undefined && v > this.__max) { | ||
| 736 | v = this.__max; | ||
| 737 | } | ||
| 738 | |||
| 739 | if (this.__step !== undefined && v % this.__step != 0) { | ||
| 740 | v = Math.round(v / this.__step) * this.__step; | ||
| 741 | } | ||
| 742 | |||
| 743 | return NumberController.superclass.prototype.setValue.call(this, v); | ||
| 744 | |||
| 745 | }, | ||
| 746 | |||
| 747 | /** | ||
| 748 | * Specify a minimum value for <code>object[property]</code>. | ||
| 749 | * | ||
| 750 | * @param {Number} minValue The minimum value for | ||
| 751 | * <code>object[property]</code> | ||
| 752 | * @returns {dat.controllers.NumberController} this | ||
| 753 | */ | ||
| 754 | min: function(v) { | ||
| 755 | this.__min = v; | ||
| 756 | return this; | ||
| 757 | }, | ||
| 758 | |||
| 759 | /** | ||
| 760 | * Specify a maximum value for <code>object[property]</code>. | ||
| 761 | * | ||
| 762 | * @param {Number} maxValue The maximum value for | ||
| 763 | * <code>object[property]</code> | ||
| 764 | * @returns {dat.controllers.NumberController} this | ||
| 765 | */ | ||
| 766 | max: function(v) { | ||
| 767 | this.__max = v; | ||
| 768 | return this; | ||
| 769 | }, | ||
| 770 | |||
| 771 | /** | ||
| 772 | * Specify a step value that dat.controllers.NumberController | ||
| 773 | * increments by. | ||
| 774 | * | ||
| 775 | * @param {Number} stepValue The step value for | ||
| 776 | * dat.controllers.NumberController | ||
| 777 | * @default if minimum and maximum specified increment is 1% of the | ||
| 778 | * difference otherwise stepValue is 1 | ||
| 779 | * @returns {dat.controllers.NumberController} this | ||
| 780 | */ | ||
| 781 | step: function(v) { | ||
| 782 | this.__step = v; | ||
| 783 | this.__impliedStep = v; | ||
| 784 | this.__precision = numDecimals(v); | ||
| 785 | return this; | ||
| 786 | } | ||
| 787 | |||
| 788 | } | ||
| 789 | |||
| 790 | ); | ||
| 791 | |||
| 792 | function numDecimals(x) { | ||
| 793 | x = x.toString(); | ||
| 794 | if (x.indexOf('.') > -1) { | ||
| 795 | return x.length - x.indexOf('.') - 1; | ||
| 796 | } else { | ||
| 797 | return 0; | ||
| 798 | } | ||
| 799 | } | ||
| 800 | |||
| 801 | return NumberController; | ||
| 802 | |||
| 803 | })(dat.controllers.Controller, | ||
| 804 | dat.utils.common); | ||
| 805 | |||
| 806 | |||
| 807 | dat.controllers.NumberControllerBox = (function (NumberController, dom, common) { | ||
| 808 | |||
| 809 | /** | ||
| 810 | * @class Represents a given property of an object that is a number and | ||
| 811 | * provides an input element with which to manipulate it. | ||
| 812 | * | ||
| 813 | * @extends dat.controllers.Controller | ||
| 814 | * @extends dat.controllers.NumberController | ||
| 815 | * | ||
| 816 | * @param {Object} object The object to be manipulated | ||
| 817 | * @param {string} property The name of the property to be manipulated | ||
| 818 | * @param {Object} [params] Optional parameters | ||
| 819 | * @param {Number} [params.min] Minimum allowed value | ||
| 820 | * @param {Number} [params.max] Maximum allowed value | ||
| 821 | * @param {Number} [params.step] Increment by which to change value | ||
| 822 | * | ||
| 823 | * @member dat.controllers | ||
| 824 | */ | ||
| 825 | var NumberControllerBox = function(object, property, params) { | ||
| 826 | |||
| 827 | this.__truncationSuspended = false; | ||
| 828 | |||
| 829 | NumberControllerBox.superclass.call(this, object, property, params); | ||
| 830 | |||
| 831 | var _this = this; | ||
| 832 | |||
| 833 | /** | ||
| 834 | * {Number} Previous mouse y position | ||
| 835 | * @ignore | ||
| 836 | */ | ||
| 837 | var prev_y; | ||
| 838 | |||
| 839 | this.__input = document.createElement('input'); | ||
| 840 | this.__input.setAttribute('type', 'text'); | ||
| 841 | |||
| 842 | // Makes it so manually specified values are not truncated. | ||
| 843 | |||
| 844 | dom.bind(this.__input, 'change', onChange); | ||
| 845 | dom.bind(this.__input, 'blur', onBlur); | ||
| 846 | dom.bind(this.__input, 'mousedown', onMouseDown); | ||
| 847 | dom.bind(this.__input, 'keydown', function(e) { | ||
| 848 | |||
| 849 | // When pressing entire, you can be as precise as you want. | ||
| 850 | if (e.keyCode === 13) { | ||
| 851 | _this.__truncationSuspended = true; | ||
| 852 | this.blur(); | ||
| 853 | _this.__truncationSuspended = false; | ||
| 854 | } | ||
| 855 | |||
| 856 | }); | ||
| 857 | |||
| 858 | function onChange() { | ||
| 859 | var attempted = parseFloat(_this.__input.value); | ||
| 860 | if (!common.isNaN(attempted)) _this.setValue(attempted); | ||
| 861 | } | ||
| 862 | |||
| 863 | function onBlur() { | ||
| 864 | onChange(); | ||
| 865 | if (_this.__onFinishChange) { | ||
| 866 | _this.__onFinishChange.call(_this, _this.getValue()); | ||
| 867 | } | ||
| 868 | } | ||
| 869 | |||
| 870 | function onMouseDown(e) { | ||
| 871 | dom.bind(window, 'mousemove', onMouseDrag); | ||
| 872 | dom.bind(window, 'mouseup', onMouseUp); | ||
| 873 | prev_y = e.clientY; | ||
| 874 | } | ||
| 875 | |||
| 876 | function onMouseDrag(e) { | ||
| 877 | |||
| 878 | var diff = prev_y - e.clientY; | ||
| 879 | _this.setValue(_this.getValue() + diff * _this.__impliedStep); | ||
| 880 | |||
| 881 | prev_y = e.clientY; | ||
| 882 | |||
| 883 | } | ||
| 884 | |||
| 885 | function onMouseUp() { | ||
| 886 | dom.unbind(window, 'mousemove', onMouseDrag); | ||
| 887 | dom.unbind(window, 'mouseup', onMouseUp); | ||
| 888 | } | ||
| 889 | |||
| 890 | this.updateDisplay(); | ||
| 891 | |||
| 892 | this.domElement.appendChild(this.__input); | ||
| 893 | |||
| 894 | }; | ||
| 895 | |||
| 896 | NumberControllerBox.superclass = NumberController; | ||
| 897 | |||
| 898 | common.extend( | ||
| 899 | |||
| 900 | NumberControllerBox.prototype, | ||
| 901 | NumberController.prototype, | ||
| 902 | |||
| 903 | { | ||
| 904 | |||
| 905 | updateDisplay: function() { | ||
| 906 | |||
| 907 | this.__input.value = this.__truncationSuspended ? this.getValue() : roundToDecimal(this.getValue(), this.__precision); | ||
| 908 | return NumberControllerBox.superclass.prototype.updateDisplay.call(this); | ||
| 909 | } | ||
| 910 | |||
| 911 | } | ||
| 912 | |||
| 913 | ); | ||
| 914 | |||
| 915 | function roundToDecimal(value, decimals) { | ||
| 916 | var tenTo = Math.pow(10, decimals); | ||
| 917 | return Math.round(value * tenTo) / tenTo; | ||
| 918 | } | ||
| 919 | |||
| 920 | return NumberControllerBox; | ||
| 921 | |||
| 922 | })(dat.controllers.NumberController, | ||
| 923 | dat.dom.dom, | ||
| 924 | dat.utils.common); | ||
| 925 | |||
| 926 | |||
| 927 | dat.controllers.NumberControllerSlider = (function (NumberController, dom, css, common, styleSheet) { | ||
| 928 | |||
| 929 | /** | ||
| 930 | * @class Represents a given property of an object that is a number, contains | ||
| 931 | * a minimum and maximum, and provides a slider element with which to | ||
| 932 | * manipulate it. It should be noted that the slider element is made up of | ||
| 933 | * <code><div></code> tags, <strong>not</strong> the html5 | ||
| 934 | * <code><slider></code> element. | ||
| 935 | * | ||
| 936 | * @extends dat.controllers.Controller | ||
| 937 | * @extends dat.controllers.NumberController | ||
| 938 | * | ||
| 939 | * @param {Object} object The object to be manipulated | ||
| 940 | * @param {string} property The name of the property to be manipulated | ||
| 941 | * @param {Number} minValue Minimum allowed value | ||
| 942 | * @param {Number} maxValue Maximum allowed value | ||
| 943 | * @param {Number} stepValue Increment by which to change value | ||
| 944 | * | ||
| 945 | * @member dat.controllers | ||
| 946 | */ | ||
| 947 | var NumberControllerSlider = function(object, property, min, max, step) { | ||
| 948 | |||
| 949 | NumberControllerSlider.superclass.call(this, object, property, { min: min, max: max, step: step }); | ||
| 950 | |||
| 951 | var _this = this; | ||
| 952 | |||
| 953 | this.__background = document.createElement('div'); | ||
| 954 | this.__foreground = document.createElement('div'); | ||
| 955 | |||
| 956 | |||
| 957 | |||
| 958 | dom.bind(this.__background, 'mousedown', onMouseDown); | ||
| 959 | |||
| 960 | dom.addClass(this.__background, 'slider'); | ||
| 961 | dom.addClass(this.__foreground, 'slider-fg'); | ||
| 962 | |||
| 963 | function onMouseDown(e) { | ||
| 964 | |||
| 965 | dom.bind(window, 'mousemove', onMouseDrag); | ||
| 966 | dom.bind(window, 'mouseup', onMouseUp); | ||
| 967 | |||
| 968 | onMouseDrag(e); | ||
| 969 | } | ||
| 970 | |||
| 971 | function onMouseDrag(e) { | ||
| 972 | |||
| 973 | e.preventDefault(); | ||
| 974 | |||
| 975 | var offset = dom.getOffset(_this.__background); | ||
| 976 | var width = dom.getWidth(_this.__background); | ||
| 977 | |||
| 978 | _this.setValue( | ||
| 979 | map(e.clientX, offset.left, offset.left + width, _this.__min, _this.__max) | ||
| 980 | ); | ||
| 981 | |||
| 982 | return false; | ||
| 983 | |||
| 984 | } | ||
| 985 | |||
| 986 | function onMouseUp() { | ||
| 987 | dom.unbind(window, 'mousemove', onMouseDrag); | ||
| 988 | dom.unbind(window, 'mouseup', onMouseUp); | ||
| 989 | if (_this.__onFinishChange) { | ||
| 990 | _this.__onFinishChange.call(_this, _this.getValue()); | ||
| 991 | } | ||
| 992 | } | ||
| 993 | |||
| 994 | this.updateDisplay(); | ||
| 995 | |||
| 996 | this.__background.appendChild(this.__foreground); | ||
| 997 | this.domElement.appendChild(this.__background); | ||
| 998 | |||
| 999 | }; | ||
| 1000 | |||
| 1001 | NumberControllerSlider.superclass = NumberController; | ||
| 1002 | |||
| 1003 | /** | ||
| 1004 | * Injects default stylesheet for slider elements. | ||
| 1005 | */ | ||
| 1006 | NumberControllerSlider.useDefaultStyles = function() { | ||
| 1007 | css.inject(styleSheet); | ||
| 1008 | }; | ||
| 1009 | |||
| 1010 | common.extend( | ||
| 1011 | |||
| 1012 | NumberControllerSlider.prototype, | ||
| 1013 | NumberController.prototype, | ||
| 1014 | |||
| 1015 | { | ||
| 1016 | |||
| 1017 | updateDisplay: function() { | ||
| 1018 | var pct = (this.getValue() - this.__min)/(this.__max - this.__min); | ||
| 1019 | this.__foreground.style.width = pct*100+'%'; | ||
| 1020 | return NumberControllerSlider.superclass.prototype.updateDisplay.call(this); | ||
| 1021 | } | ||
| 1022 | |||
| 1023 | } | ||
| 1024 | |||
| 1025 | |||
| 1026 | |||
| 1027 | ); | ||
| 1028 | |||
| 1029 | function map(v, i1, i2, o1, o2) { | ||
| 1030 | return o1 + (o2 - o1) * ((v - i1) / (i2 - i1)); | ||
| 1031 | } | ||
| 1032 | |||
| 1033 | return NumberControllerSlider; | ||
| 1034 | |||
| 1035 | })(dat.controllers.NumberController, | ||
| 1036 | dat.dom.dom, | ||
| 1037 | dat.utils.css, | ||
| 1038 | dat.utils.common, | ||
| 1039 | "/**\n * dat-gui JavaScript Controller Library\n * http://code.google.com/p/dat-gui\n *\n * Copyright 2011 Data Arts Team, Google Creative Lab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n */\n\n.slider {\n box-shadow: inset 0 2px 4px rgba(0,0,0,0.15);\n height: 1em;\n border-radius: 1em;\n background-color: #eee;\n padding: 0 0.5em;\n overflow: hidden;\n}\n\n.slider-fg {\n padding: 1px 0 2px 0;\n background-color: #aaa;\n height: 1em;\n margin-left: -0.5em;\n padding-right: 0.5em;\n border-radius: 1em 0 0 1em;\n}\n\n.slider-fg:after {\n display: inline-block;\n border-radius: 1em;\n background-color: #fff;\n border: 1px solid #aaa;\n content: '';\n float: right;\n margin-right: -1em;\n margin-top: -1px;\n height: 0.9em;\n width: 0.9em;\n}"); | ||
| 1040 | |||
| 1041 | |||
| 1042 | dat.controllers.FunctionController = (function (Controller, dom, common) { | ||
| 1043 | |||
| 1044 | /** | ||
| 1045 | * @class Provides a GUI interface to fire a specified method, a property of an object. | ||
| 1046 | * | ||
| 1047 | * @extends dat.controllers.Controller | ||
| 1048 | * | ||
| 1049 | * @param {Object} object The object to be manipulated | ||
| 1050 | * @param {string} property The name of the property to be manipulated | ||
| 1051 | * | ||
| 1052 | * @member dat.controllers | ||
| 1053 | */ | ||
| 1054 | var FunctionController = function(object, property, text) { | ||
| 1055 | |||
| 1056 | FunctionController.superclass.call(this, object, property); | ||
| 1057 | |||
| 1058 | var _this = this; | ||
| 1059 | |||
| 1060 | this.__button = document.createElement('div'); | ||
| 1061 | this.__button.innerHTML = text === undefined ? 'Fire' : text; | ||
| 1062 | dom.bind(this.__button, 'click', function(e) { | ||
| 1063 | e.preventDefault(); | ||
| 1064 | _this.fire(); | ||
| 1065 | return false; | ||
| 1066 | }); | ||
| 1067 | |||
| 1068 | dom.addClass(this.__button, 'button'); | ||
| 1069 | |||
| 1070 | this.domElement.appendChild(this.__button); | ||
| 1071 | |||
| 1072 | |||
| 1073 | }; | ||
| 1074 | |||
| 1075 | FunctionController.superclass = Controller; | ||
| 1076 | |||
| 1077 | common.extend( | ||
| 1078 | |||
| 1079 | FunctionController.prototype, | ||
| 1080 | Controller.prototype, | ||
| 1081 | { | ||
| 1082 | |||
| 1083 | fire: function() { | ||
| 1084 | if (this.__onChange) { | ||
| 1085 | this.__onChange.call(this); | ||
| 1086 | } | ||
| 1087 | if (this.__onFinishChange) { | ||
| 1088 | this.__onFinishChange.call(this, this.getValue()); | ||
| 1089 | } | ||
| 1090 | this.getValue().call(this.object); | ||
| 1091 | } | ||
| 1092 | } | ||
| 1093 | |||
| 1094 | ); | ||
| 1095 | |||
| 1096 | return FunctionController; | ||
| 1097 | |||
| 1098 | })(dat.controllers.Controller, | ||
| 1099 | dat.dom.dom, | ||
| 1100 | dat.utils.common); | ||
| 1101 | |||
| 1102 | |||
| 1103 | dat.controllers.BooleanController = (function (Controller, dom, common) { | ||
| 1104 | |||
| 1105 | /** | ||
| 1106 | * @class Provides a checkbox input to alter the boolean property of an object. | ||
| 1107 | * @extends dat.controllers.Controller | ||
| 1108 | * | ||
| 1109 | * @param {Object} object The object to be manipulated | ||
| 1110 | * @param {string} property The name of the property to be manipulated | ||
| 1111 | * | ||
| 1112 | * @member dat.controllers | ||
| 1113 | */ | ||
| 1114 | var BooleanController = function(object, property) { | ||
| 1115 | |||
| 1116 | BooleanController.superclass.call(this, object, property); | ||
| 1117 | |||
| 1118 | var _this = this; | ||
| 1119 | this.__prev = this.getValue(); | ||
| 1120 | |||
| 1121 | this.__checkbox = document.createElement('input'); | ||
| 1122 | this.__checkbox.setAttribute('type', 'checkbox'); | ||
| 1123 | |||
| 1124 | |||
| 1125 | dom.bind(this.__checkbox, 'change', onChange, false); | ||
| 1126 | |||
| 1127 | this.domElement.appendChild(this.__checkbox); | ||
| 1128 | |||
| 1129 | // Match original value | ||
| 1130 | this.updateDisplay(); | ||
| 1131 | |||
| 1132 | function onChange() { | ||
| 1133 | _this.setValue(!_this.__prev); | ||
| 1134 | } | ||
| 1135 | |||
| 1136 | }; | ||
| 1137 | |||
| 1138 | BooleanController.superclass = Controller; | ||
| 1139 | |||
| 1140 | common.extend( | ||
| 1141 | |||
| 1142 | BooleanController.prototype, | ||
| 1143 | Controller.prototype, | ||
| 1144 | |||
| 1145 | { | ||
| 1146 | |||
| 1147 | setValue: function(v) { | ||
| 1148 | var toReturn = BooleanController.superclass.prototype.setValue.call(this, v); | ||
| 1149 | if (this.__onFinishChange) { | ||
| 1150 | this.__onFinishChange.call(this, this.getValue()); | ||
| 1151 | } | ||
| 1152 | this.__prev = this.getValue(); | ||
| 1153 | return toReturn; | ||
| 1154 | }, | ||
| 1155 | |||
| 1156 | updateDisplay: function() { | ||
| 1157 | |||
| 1158 | if (this.getValue() === true) { | ||
| 1159 | this.__checkbox.setAttribute('checked', 'checked'); | ||
| 1160 | this.__checkbox.checked = true; | ||
| 1161 | } else { | ||
| 1162 | this.__checkbox.checked = false; | ||
| 1163 | } | ||
| 1164 | |||
| 1165 | return BooleanController.superclass.prototype.updateDisplay.call(this); | ||
| 1166 | |||
| 1167 | } | ||
| 1168 | |||
| 1169 | |||
| 1170 | } | ||
| 1171 | |||
| 1172 | ); | ||
| 1173 | |||
| 1174 | return BooleanController; | ||
| 1175 | |||
| 1176 | })(dat.controllers.Controller, | ||
| 1177 | dat.dom.dom, | ||
| 1178 | dat.utils.common); | ||
| 1179 | |||
| 1180 | |||
| 1181 | dat.color.toString = (function (common) { | ||
| 1182 | |||
| 1183 | return function(color) { | ||
| 1184 | |||
| 1185 | if (color.a == 1 || common.isUndefined(color.a)) { | ||
| 1186 | |||
| 1187 | var s = color.hex.toString(16); | ||
| 1188 | while (s.length < 6) { | ||
| 1189 | s = '0' + s; | ||
| 1190 | } | ||
| 1191 | |||
| 1192 | return '#' + s; | ||
| 1193 | |||
| 1194 | } else { | ||
| 1195 | |||
| 1196 | return 'rgba(' + Math.round(color.r) + ',' + Math.round(color.g) + ',' + Math.round(color.b) + ',' + color.a + ')'; | ||
| 1197 | |||
| 1198 | } | ||
| 1199 | |||
| 1200 | } | ||
| 1201 | |||
| 1202 | })(dat.utils.common); | ||
| 1203 | |||
| 1204 | |||
| 1205 | dat.color.interpret = (function (toString, common) { | ||
| 1206 | |||
| 1207 | var result, toReturn; | ||
| 1208 | |||
| 1209 | var interpret = function() { | ||
| 1210 | |||
| 1211 | toReturn = false; | ||
| 1212 | |||
| 1213 | var original = arguments.length > 1 ? common.toArray(arguments) : arguments[0]; | ||
| 1214 | |||
| 1215 | common.each(INTERPRETATIONS, function(family) { | ||
| 1216 | |||
| 1217 | if (family.litmus(original)) { | ||
| 1218 | |||
| 1219 | common.each(family.conversions, function(conversion, conversionName) { | ||
| 1220 | |||
| 1221 | result = conversion.read(original); | ||
| 1222 | |||
| 1223 | if (toReturn === false && result !== false) { | ||
| 1224 | toReturn = result; | ||
| 1225 | result.conversionName = conversionName; | ||
| 1226 | result.conversion = conversion; | ||
| 1227 | return common.BREAK; | ||
| 1228 | |||
| 1229 | } | ||
| 1230 | |||
| 1231 | }); | ||
| 1232 | |||
| 1233 | return common.BREAK; | ||
| 1234 | |||
| 1235 | } | ||
| 1236 | |||
| 1237 | }); | ||
| 1238 | |||
| 1239 | return toReturn; | ||
| 1240 | |||
| 1241 | }; | ||
| 1242 | |||
| 1243 | var INTERPRETATIONS = [ | ||
| 1244 | |||
| 1245 | // Strings | ||
| 1246 | { | ||
| 1247 | |||
| 1248 | litmus: common.isString, | ||
| 1249 | |||
| 1250 | conversions: { | ||
| 1251 | |||
| 1252 | THREE_CHAR_HEX: { | ||
| 1253 | |||
| 1254 | read: function(original) { | ||
| 1255 | |||
| 1256 | var test = original.match(/^#([A-F0-9])([A-F0-9])([A-F0-9])$/i); | ||
| 1257 | if (test === null) return false; | ||
| 1258 | |||
| 1259 | return { | ||
| 1260 | space: 'HEX', | ||
| 1261 | hex: parseInt( | ||
| 1262 | '0x' + | ||
| 1263 | test[1].toString() + test[1].toString() + | ||
| 1264 | test[2].toString() + test[2].toString() + | ||
| 1265 | test[3].toString() + test[3].toString()) | ||
| 1266 | }; | ||
| 1267 | |||
| 1268 | }, | ||
| 1269 | |||
| 1270 | write: toString | ||
| 1271 | |||
| 1272 | }, | ||
| 1273 | |||
| 1274 | SIX_CHAR_HEX: { | ||
| 1275 | |||
| 1276 | read: function(original) { | ||
| 1277 | |||
| 1278 | var test = original.match(/^#([A-F0-9]{6})$/i); | ||
| 1279 | if (test === null) return false; | ||
| 1280 | |||
| 1281 | return { | ||
| 1282 | space: 'HEX', | ||
| 1283 | hex: parseInt('0x' + test[1].toString()) | ||
| 1284 | }; | ||
| 1285 | |||
| 1286 | }, | ||
| 1287 | |||
| 1288 | write: toString | ||
| 1289 | |||
| 1290 | }, | ||
| 1291 | |||
| 1292 | CSS_RGB: { | ||
| 1293 | |||
| 1294 | read: function(original) { | ||
| 1295 | |||
| 1296 | var test = original.match(/^rgb\(\s*(.+)\s*,\s*(.+)\s*,\s*(.+)\s*\)/); | ||
| 1297 | if (test === null) return false; | ||
| 1298 | |||
| 1299 | return { | ||
| 1300 | space: 'RGB', | ||
| 1301 | r: parseFloat(test[1]), | ||
| 1302 | g: parseFloat(test[2]), | ||
| 1303 | b: parseFloat(test[3]) | ||
| 1304 | }; | ||
| 1305 | |||
| 1306 | }, | ||
| 1307 | |||
| 1308 | write: toString | ||
| 1309 | |||
| 1310 | }, | ||
| 1311 | |||
| 1312 | CSS_RGBA: { | ||
| 1313 | |||
| 1314 | read: function(original) { | ||
| 1315 | |||
| 1316 | var test = original.match(/^rgba\(\s*(.+)\s*,\s*(.+)\s*,\s*(.+)\s*\,\s*(.+)\s*\)/); | ||
| 1317 | if (test === null) return false; | ||
| 1318 | |||
| 1319 | return { | ||
| 1320 | space: 'RGB', | ||
| 1321 | r: parseFloat(test[1]), | ||
| 1322 | g: parseFloat(test[2]), | ||
| 1323 | b: parseFloat(test[3]), | ||
| 1324 | a: parseFloat(test[4]) | ||
| 1325 | }; | ||
| 1326 | |||
| 1327 | }, | ||
| 1328 | |||
| 1329 | write: toString | ||
| 1330 | |||
| 1331 | } | ||
| 1332 | |||
| 1333 | } | ||
| 1334 | |||
| 1335 | }, | ||
| 1336 | |||
| 1337 | // Numbers | ||
| 1338 | { | ||
| 1339 | |||
| 1340 | litmus: common.isNumber, | ||
| 1341 | |||
| 1342 | conversions: { | ||
| 1343 | |||
| 1344 | HEX: { | ||
| 1345 | read: function(original) { | ||
| 1346 | return { | ||
| 1347 | space: 'HEX', | ||
| 1348 | hex: original, | ||
| 1349 | conversionName: 'HEX' | ||
| 1350 | } | ||
| 1351 | }, | ||
| 1352 | |||
| 1353 | write: function(color) { | ||
| 1354 | return color.hex; | ||
| 1355 | } | ||
| 1356 | } | ||
| 1357 | |||
| 1358 | } | ||
| 1359 | |||
| 1360 | }, | ||
| 1361 | |||
| 1362 | // Arrays | ||
| 1363 | { | ||
| 1364 | |||
| 1365 | litmus: common.isArray, | ||
| 1366 | |||
| 1367 | conversions: { | ||
| 1368 | |||
| 1369 | RGB_ARRAY: { | ||
| 1370 | read: function(original) { | ||
| 1371 | if (original.length != 3) return false; | ||
| 1372 | return { | ||
| 1373 | space: 'RGB', | ||
| 1374 | r: original[0], | ||
| 1375 | g: original[1], | ||
| 1376 | b: original[2] | ||
| 1377 | }; | ||
| 1378 | }, | ||
| 1379 | |||
| 1380 | write: function(color) { | ||
| 1381 | return [color.r, color.g, color.b]; | ||
| 1382 | } | ||
| 1383 | |||
| 1384 | }, | ||
| 1385 | |||
| 1386 | RGBA_ARRAY: { | ||
| 1387 | read: function(original) { | ||
| 1388 | if (original.length != 4) return false; | ||
| 1389 | return { | ||
| 1390 | space: 'RGB', | ||
| 1391 | r: original[0], | ||
| 1392 | g: original[1], | ||
| 1393 | b: original[2], | ||
| 1394 | a: original[3] | ||
| 1395 | }; | ||
| 1396 | }, | ||
| 1397 | |||
| 1398 | write: function(color) { | ||
| 1399 | return [color.r, color.g, color.b, color.a]; | ||
| 1400 | } | ||
| 1401 | |||
| 1402 | } | ||
| 1403 | |||
| 1404 | } | ||
| 1405 | |||
| 1406 | }, | ||
| 1407 | |||
| 1408 | // Objects | ||
| 1409 | { | ||
| 1410 | |||
| 1411 | litmus: common.isObject, | ||
| 1412 | |||
| 1413 | conversions: { | ||
| 1414 | |||
| 1415 | RGBA_OBJ: { | ||
| 1416 | read: function(original) { | ||
| 1417 | if (common.isNumber(original.r) && | ||
| 1418 | common.isNumber(original.g) && | ||
| 1419 | common.isNumber(original.b) && | ||
| 1420 | common.isNumber(original.a)) { | ||
| 1421 | return { | ||
| 1422 | space: 'RGB', | ||
| 1423 | r: original.r, | ||
| 1424 | g: original.g, | ||
| 1425 | b: original.b, | ||
| 1426 | a: original.a | ||
| 1427 | } | ||
| 1428 | } | ||
| 1429 | return false; | ||
| 1430 | }, | ||
| 1431 | |||
| 1432 | write: function(color) { | ||
| 1433 | return { | ||
| 1434 | r: color.r, | ||
| 1435 | g: color.g, | ||
| 1436 | b: color.b, | ||
| 1437 | a: color.a | ||
| 1438 | } | ||
| 1439 | } | ||
| 1440 | }, | ||
| 1441 | |||
| 1442 | RGB_OBJ: { | ||
| 1443 | read: function(original) { | ||
| 1444 | if (common.isNumber(original.r) && | ||
| 1445 | common.isNumber(original.g) && | ||
| 1446 | common.isNumber(original.b)) { | ||
| 1447 | return { | ||
| 1448 | space: 'RGB', | ||
| 1449 | r: original.r, | ||
| 1450 | g: original.g, | ||
| 1451 | b: original.b | ||
| 1452 | } | ||
| 1453 | } | ||
| 1454 | return false; | ||
| 1455 | }, | ||
| 1456 | |||
| 1457 | write: function(color) { | ||
| 1458 | return { | ||
| 1459 | r: color.r, | ||
| 1460 | g: color.g, | ||
| 1461 | b: color.b | ||
| 1462 | } | ||
| 1463 | } | ||
| 1464 | }, | ||
| 1465 | |||
| 1466 | HSVA_OBJ: { | ||
| 1467 | read: function(original) { | ||
| 1468 | if (common.isNumber(original.h) && | ||
| 1469 | common.isNumber(original.s) && | ||
| 1470 | common.isNumber(original.v) && | ||
| 1471 | common.isNumber(original.a)) { | ||
| 1472 | return { | ||
| 1473 | space: 'HSV', | ||
| 1474 | h: original.h, | ||
| 1475 | s: original.s, | ||
| 1476 | v: original.v, | ||
| 1477 | a: original.a | ||
| 1478 | } | ||
| 1479 | } | ||
| 1480 | return false; | ||
| 1481 | }, | ||
| 1482 | |||
| 1483 | write: function(color) { | ||
| 1484 | return { | ||
| 1485 | h: color.h, | ||
| 1486 | s: color.s, | ||
| 1487 | v: color.v, | ||
| 1488 | a: color.a | ||
| 1489 | } | ||
| 1490 | } | ||
| 1491 | }, | ||
| 1492 | |||
| 1493 | HSV_OBJ: { | ||
| 1494 | read: function(original) { | ||
| 1495 | if (common.isNumber(original.h) && | ||
| 1496 | common.isNumber(original.s) && | ||
| 1497 | common.isNumber(original.v)) { | ||
| 1498 | return { | ||
| 1499 | space: 'HSV', | ||
| 1500 | h: original.h, | ||
| 1501 | s: original.s, | ||
| 1502 | v: original.v | ||
| 1503 | } | ||
| 1504 | } | ||
| 1505 | return false; | ||
| 1506 | }, | ||
| 1507 | |||
| 1508 | write: function(color) { | ||
| 1509 | return { | ||
| 1510 | h: color.h, | ||
| 1511 | s: color.s, | ||
| 1512 | v: color.v | ||
| 1513 | } | ||
| 1514 | } | ||
| 1515 | |||
| 1516 | } | ||
| 1517 | |||
| 1518 | } | ||
| 1519 | |||
| 1520 | } | ||
| 1521 | |||
| 1522 | |||
| 1523 | ]; | ||
| 1524 | |||
| 1525 | return interpret; | ||
| 1526 | |||
| 1527 | |||
| 1528 | })(dat.color.toString, | ||
| 1529 | dat.utils.common); | ||
| 1530 | |||
| 1531 | |||
| 1532 | dat.GUI = dat.gui.GUI = (function (css, saveDialogueContents, styleSheet, controllerFactory, Controller, BooleanController, FunctionController, NumberControllerBox, NumberControllerSlider, OptionController, ColorController, requestAnimationFrame, CenteredDiv, dom, common) { | ||
| 1533 | |||
| 1534 | css.inject(styleSheet); | ||
| 1535 | |||
| 1536 | /** Outer-most className for GUI's */ | ||
| 1537 | var CSS_NAMESPACE = 'dg'; | ||
| 1538 | |||
| 1539 | var HIDE_KEY_CODE = 72; | ||
| 1540 | |||
| 1541 | /** The only value shared between the JS and SCSS. Use caution. */ | ||
| 1542 | var CLOSE_BUTTON_HEIGHT = 20; | ||
| 1543 | |||
| 1544 | var DEFAULT_DEFAULT_PRESET_NAME = 'Default'; | ||
| 1545 | |||
| 1546 | var SUPPORTS_LOCAL_STORAGE = (function() { | ||
| 1547 | try { | ||
| 1548 | return 'localStorage' in window && window['localStorage'] !== null; | ||
| 1549 | } catch (e) { | ||
| 1550 | return false; | ||
| 1551 | } | ||
| 1552 | })(); | ||
| 1553 | |||
| 1554 | var SAVE_DIALOGUE; | ||
| 1555 | |||
| 1556 | /** Have we yet to create an autoPlace GUI? */ | ||
| 1557 | var auto_place_virgin = true; | ||
| 1558 | |||
| 1559 | /** Fixed position div that auto place GUI's go inside */ | ||
| 1560 | var auto_place_container; | ||
| 1561 | |||
| 1562 | /** Are we hiding the GUI's ? */ | ||
| 1563 | var hide = false; | ||
| 1564 | |||
| 1565 | /** GUI's which should be hidden */ | ||
| 1566 | var hideable_guis = []; | ||
| 1567 | |||
| 1568 | /** | ||
| 1569 | * A lightweight controller library for JavaScript. It allows you to easily | ||
| 1570 | * manipulate variables and fire functions on the fly. | ||
| 1571 | * @class | ||
| 1572 | * | ||
| 1573 | * @member dat.gui | ||
| 1574 | * | ||
| 1575 | * @param {Object} [params] | ||
| 1576 | * @param {String} [params.name] The name of this GUI. | ||
| 1577 | * @param {Object} [params.load] JSON object representing the saved state of | ||
| 1578 | * this GUI. | ||
| 1579 | * @param {Boolean} [params.auto=true] | ||
| 1580 | * @param {dat.gui.GUI} [params.parent] The GUI I'm nested in. | ||
| 1581 | * @param {Boolean} [params.closed] If true, starts closed | ||
| 1582 | */ | ||
| 1583 | var GUI = function(params) { | ||
| 1584 | |||
| 1585 | var _this = this; | ||
| 1586 | |||
| 1587 | /** | ||
| 1588 | * Outermost DOM Element | ||
| 1589 | * @type DOMElement | ||
| 1590 | */ | ||
| 1591 | this.domElement = document.createElement('div'); | ||
| 1592 | this.__ul = document.createElement('ul'); | ||
| 1593 | this.domElement.appendChild(this.__ul); | ||
| 1594 | |||
| 1595 | dom.addClass(this.domElement, CSS_NAMESPACE); | ||
| 1596 | |||
| 1597 | /** | ||
| 1598 | * Nested GUI's by name | ||
| 1599 | * @ignore | ||
| 1600 | */ | ||
| 1601 | this.__folders = {}; | ||
| 1602 | |||
| 1603 | this.__controllers = []; | ||
| 1604 | |||
| 1605 | /** | ||
| 1606 | * List of objects I'm remembering for save, only used in top level GUI | ||
| 1607 | * @ignore | ||
| 1608 | */ | ||
| 1609 | this.__rememberedObjects = []; | ||
| 1610 | |||
| 1611 | /** | ||
| 1612 | * Maps the index of remembered objects to a map of controllers, only used | ||
| 1613 | * in top level GUI. | ||
| 1614 | * | ||
| 1615 | * @private | ||
| 1616 | * @ignore | ||
| 1617 | * | ||
| 1618 | * @example | ||
| 1619 | * [ | ||
| 1620 | * { | ||
| 1621 | * propertyName: Controller, | ||
| 1622 | * anotherPropertyName: Controller | ||
| 1623 | * }, | ||
| 1624 | * { | ||
| 1625 | * propertyName: Controller | ||
| 1626 | * } | ||
| 1627 | * ] | ||
| 1628 | */ | ||
| 1629 | this.__rememberedObjectIndecesToControllers = []; | ||
| 1630 | |||
| 1631 | this.__listening = []; | ||
| 1632 | |||
| 1633 | params = params || {}; | ||
| 1634 | |||
| 1635 | // Default parameters | ||
| 1636 | params = common.defaults(params, { | ||
| 1637 | autoPlace: true, | ||
| 1638 | width: GUI.DEFAULT_WIDTH | ||
| 1639 | }); | ||
| 1640 | |||
| 1641 | params = common.defaults(params, { | ||
| 1642 | resizable: params.autoPlace, | ||
| 1643 | hideable: params.autoPlace | ||
| 1644 | }); | ||
| 1645 | |||
| 1646 | |||
| 1647 | if (!common.isUndefined(params.load)) { | ||
| 1648 | |||
| 1649 | // Explicit preset | ||
| 1650 | if (params.preset) params.load.preset = params.preset; | ||
| 1651 | |||
| 1652 | } else { | ||
| 1653 | |||
| 1654 | params.load = { preset: DEFAULT_DEFAULT_PRESET_NAME }; | ||
| 1655 | |||
| 1656 | } | ||
| 1657 | |||
| 1658 | if (common.isUndefined(params.parent) && params.hideable) { | ||
| 1659 | hideable_guis.push(this); | ||
| 1660 | } | ||
| 1661 | |||
| 1662 | // Only root level GUI's are resizable. | ||
| 1663 | params.resizable = common.isUndefined(params.parent) && params.resizable; | ||
| 1664 | |||
| 1665 | |||
| 1666 | if (params.autoPlace && common.isUndefined(params.scrollable)) { | ||
| 1667 | params.scrollable = true; | ||
| 1668 | } | ||
| 1669 | // params.scrollable = common.isUndefined(params.parent) && params.scrollable === true; | ||
| 1670 | |||
| 1671 | // Not part of params because I don't want people passing this in via | ||
| 1672 | // constructor. Should be a 'remembered' value. | ||
| 1673 | var use_local_storage = | ||
| 1674 | SUPPORTS_LOCAL_STORAGE && | ||
| 1675 | localStorage.getItem(getLocalStorageHash(this, 'isLocal')) === 'true'; | ||
| 1676 | |||
| 1677 | var saveToLocalStorage; | ||
| 1678 | |||
| 1679 | Object.defineProperties(this, | ||
| 1680 | |||
| 1681 | /** @lends dat.gui.GUI.prototype */ | ||
| 1682 | { | ||
| 1683 | |||
| 1684 | /** | ||
| 1685 | * The parent <code>GUI</code> | ||
| 1686 | * @type dat.gui.GUI | ||
| 1687 | */ | ||
| 1688 | parent: { | ||
| 1689 | get: function() { | ||
| 1690 | return params.parent; | ||
| 1691 | } | ||
| 1692 | }, | ||
| 1693 | |||
| 1694 | scrollable: { | ||
| 1695 | get: function() { | ||
| 1696 | return params.scrollable; | ||
| 1697 | } | ||
| 1698 | }, | ||
| 1699 | |||
| 1700 | /** | ||
| 1701 | * Handles <code>GUI</code>'s element placement for you | ||
| 1702 | * @type Boolean | ||
| 1703 | */ | ||
| 1704 | autoPlace: { | ||
| 1705 | get: function() { | ||
| 1706 | return params.autoPlace; | ||
| 1707 | } | ||
| 1708 | }, | ||
| 1709 | |||
| 1710 | /** | ||
| 1711 | * The identifier for a set of saved values | ||
| 1712 | * @type String | ||
| 1713 | */ | ||
| 1714 | preset: { | ||
| 1715 | |||
| 1716 | get: function() { | ||
| 1717 | if (_this.parent) { | ||
| 1718 | return _this.getRoot().preset; | ||
| 1719 | } else { | ||
| 1720 | return params.load.preset; | ||
| 1721 | } | ||
| 1722 | }, | ||
| 1723 | |||
| 1724 | set: function(v) { | ||
| 1725 | if (_this.parent) { | ||
| 1726 | _this.getRoot().preset = v; | ||
| 1727 | } else { | ||
| 1728 | params.load.preset = v; | ||
| 1729 | } | ||
| 1730 | setPresetSelectIndex(this); | ||
| 1731 | _this.revert(); | ||
| 1732 | } | ||
| 1733 | |||
| 1734 | }, | ||
| 1735 | |||
| 1736 | /** | ||
| 1737 | * The width of <code>GUI</code> element | ||
| 1738 | * @type Number | ||
| 1739 | */ | ||
| 1740 | width: { | ||
| 1741 | get: function() { | ||
| 1742 | return params.width; | ||
| 1743 | }, | ||
| 1744 | set: function(v) { | ||
| 1745 | params.width = v; | ||
| 1746 | setWidth(_this, v); | ||
| 1747 | } | ||
| 1748 | }, | ||
| 1749 | |||
| 1750 | /** | ||
| 1751 | * The name of <code>GUI</code>. Used for folders. i.e | ||
| 1752 | * a folder's name | ||
| 1753 | * @type String | ||
| 1754 | */ | ||
| 1755 | name: { | ||
| 1756 | get: function() { | ||
| 1757 | return params.name; | ||
| 1758 | }, | ||
| 1759 | set: function(v) { | ||
| 1760 | // TODO Check for collisions among sibling folders | ||
| 1761 | params.name = v; | ||
| 1762 | if (title_row_name) { | ||
| 1763 | title_row_name.innerHTML = params.name; | ||
| 1764 | } | ||
| 1765 | } | ||
| 1766 | }, | ||
| 1767 | |||
| 1768 | /** | ||
| 1769 | * Whether the <code>GUI</code> is collapsed or not | ||
| 1770 | * @type Boolean | ||
| 1771 | */ | ||
| 1772 | closed: { | ||
| 1773 | get: function() { | ||
| 1774 | return params.closed; | ||
| 1775 | }, | ||
| 1776 | set: function(v) { | ||
| 1777 | params.closed = v; | ||
| 1778 | if (params.closed) { | ||
| 1779 | dom.addClass(_this.__ul, GUI.CLASS_CLOSED); | ||
| 1780 | } else { | ||
| 1781 | dom.removeClass(_this.__ul, GUI.CLASS_CLOSED); | ||
| 1782 | } | ||
| 1783 | // For browsers that aren't going to respect the CSS transition, | ||
| 1784 | // Lets just check our height against the window height right off | ||
| 1785 | // the bat. | ||
| 1786 | this.onResize(); | ||
| 1787 | |||
| 1788 | if (_this.__closeButton) { | ||
| 1789 | _this.__closeButton.innerHTML = v ? GUI.TEXT_OPEN : GUI.TEXT_CLOSED; | ||
| 1790 | } | ||
| 1791 | } | ||
| 1792 | }, | ||
| 1793 | |||
| 1794 | /** | ||
| 1795 | * Contains all presets | ||
| 1796 | * @type Object | ||
| 1797 | */ | ||
| 1798 | load: { | ||
| 1799 | get: function() { | ||
| 1800 | return params.load; | ||
| 1801 | } | ||
| 1802 | }, | ||
| 1803 | |||
| 1804 | /** | ||
| 1805 | * Determines whether or not to use <a href="https://developer.mozilla.org/en/DOM/Storage#localStorage">localStorage</a> as the means for | ||
| 1806 | * <code>remember</code>ing | ||
| 1807 | * @type Boolean | ||
| 1808 | */ | ||
| 1809 | useLocalStorage: { | ||
| 1810 | |||
| 1811 | get: function() { | ||
| 1812 | return use_local_storage; | ||
| 1813 | }, | ||
| 1814 | set: function(bool) { | ||
| 1815 | if (SUPPORTS_LOCAL_STORAGE) { | ||
| 1816 | use_local_storage = bool; | ||
| 1817 | if (bool) { | ||
| 1818 | dom.bind(window, 'unload', saveToLocalStorage); | ||
| 1819 | } else { | ||
| 1820 | dom.unbind(window, 'unload', saveToLocalStorage); | ||
| 1821 | } | ||
| 1822 | localStorage.setItem(getLocalStorageHash(_this, 'isLocal'), bool); | ||
| 1823 | } | ||
| 1824 | } | ||
| 1825 | |||
| 1826 | } | ||
| 1827 | |||
| 1828 | }); | ||
| 1829 | |||
| 1830 | // Are we a root level GUI? | ||
| 1831 | if (common.isUndefined(params.parent)) { | ||
| 1832 | |||
| 1833 | params.closed = false; | ||
| 1834 | |||
| 1835 | dom.addClass(this.domElement, GUI.CLASS_MAIN); | ||
| 1836 | dom.makeSelectable(this.domElement, false); | ||
| 1837 | |||
| 1838 | // Are we supposed to be loading locally? | ||
| 1839 | if (SUPPORTS_LOCAL_STORAGE) { | ||
| 1840 | |||
| 1841 | if (use_local_storage) { | ||
| 1842 | |||
| 1843 | _this.useLocalStorage = true; | ||
| 1844 | |||
| 1845 | var saved_gui = localStorage.getItem(getLocalStorageHash(this, 'gui')); | ||
| 1846 | |||
| 1847 | if (saved_gui) { | ||
| 1848 | params.load = JSON.parse(saved_gui); | ||
| 1849 | } | ||
| 1850 | |||
| 1851 | } | ||
| 1852 | |||
| 1853 | } | ||
| 1854 | |||
| 1855 | this.__closeButton = document.createElement('div'); | ||
| 1856 | this.__closeButton.innerHTML = GUI.TEXT_CLOSED; | ||
| 1857 | dom.addClass(this.__closeButton, GUI.CLASS_CLOSE_BUTTON); | ||
| 1858 | this.domElement.appendChild(this.__closeButton); | ||
| 1859 | |||
| 1860 | dom.bind(this.__closeButton, 'click', function() { | ||
| 1861 | |||
| 1862 | _this.closed = !_this.closed; | ||
| 1863 | |||
| 1864 | |||
| 1865 | }); | ||
| 1866 | |||
| 1867 | |||
| 1868 | // Oh, you're a nested GUI! | ||
| 1869 | } else { | ||
| 1870 | |||
| 1871 | if (params.closed === undefined) { | ||
| 1872 | params.closed = true; | ||
| 1873 | } | ||
| 1874 | |||
| 1875 | var title_row_name = document.createTextNode(params.name); | ||
| 1876 | dom.addClass(title_row_name, 'controller-name'); | ||
| 1877 | |||
| 1878 | var title_row = addRow(_this, title_row_name); | ||
| 1879 | |||
| 1880 | var on_click_title = function(e) { | ||
| 1881 | e.preventDefault(); | ||
| 1882 | _this.closed = !_this.closed; | ||
| 1883 | return false; | ||
| 1884 | }; | ||
| 1885 | |||
| 1886 | dom.addClass(this.__ul, GUI.CLASS_CLOSED); | ||
| 1887 | |||
| 1888 | dom.addClass(title_row, 'title'); | ||
| 1889 | dom.bind(title_row, 'click', on_click_title); | ||
| 1890 | |||
| 1891 | if (!params.closed) { | ||
| 1892 | this.closed = false; | ||
| 1893 | } | ||
| 1894 | |||
| 1895 | } | ||
| 1896 | |||
| 1897 | if (params.autoPlace) { | ||
| 1898 | |||
| 1899 | if (common.isUndefined(params.parent)) { | ||
| 1900 | |||
| 1901 | if (auto_place_virgin) { | ||
| 1902 | auto_place_container = document.createElement('div'); | ||
| 1903 | dom.addClass(auto_place_container, CSS_NAMESPACE); | ||
| 1904 | dom.addClass(auto_place_container, GUI.CLASS_AUTO_PLACE_CONTAINER); | ||
| 1905 | document.body.appendChild(auto_place_container); | ||
| 1906 | auto_place_virgin = false; | ||
| 1907 | } | ||
| 1908 | |||
| 1909 | // Put it in the dom for you. | ||
| 1910 | auto_place_container.appendChild(this.domElement); | ||
| 1911 | |||
| 1912 | // Apply the auto styles | ||
| 1913 | dom.addClass(this.domElement, GUI.CLASS_AUTO_PLACE); | ||
| 1914 | |||
| 1915 | } | ||
| 1916 | |||
| 1917 | |||
| 1918 | // Make it not elastic. | ||
| 1919 | if (!this.parent) setWidth(_this, params.width); | ||
| 1920 | |||
| 1921 | } | ||
| 1922 | |||
| 1923 | dom.bind(window, 'resize', function() { _this.onResize() }); | ||
| 1924 | dom.bind(this.__ul, 'webkitTransitionEnd', function() { _this.onResize(); }); | ||
| 1925 | dom.bind(this.__ul, 'transitionend', function() { _this.onResize() }); | ||
| 1926 | dom.bind(this.__ul, 'oTransitionEnd', function() { _this.onResize() }); | ||
| 1927 | this.onResize(); | ||
| 1928 | |||
| 1929 | |||
| 1930 | if (params.resizable) { | ||
| 1931 | addResizeHandle(this); | ||
| 1932 | } | ||
| 1933 | |||
| 1934 | saveToLocalStorage = function () { | ||
| 1935 | if (SUPPORTS_LOCAL_STORAGE && localStorage.getItem(getLocalStorageHash(_this, 'isLocal')) === 'true') { | ||
| 1936 | localStorage.setItem(getLocalStorageHash(_this, 'gui'), JSON.stringify(_this.getSaveObject())); | ||
| 1937 | } | ||
| 1938 | } | ||
| 1939 | |||
| 1940 | // expose this method publicly | ||
| 1941 | this.saveToLocalStorageIfPossible = saveToLocalStorage; | ||
| 1942 | |||
| 1943 | var root = _this.getRoot(); | ||
| 1944 | function resetWidth() { | ||
| 1945 | var root = _this.getRoot(); | ||
| 1946 | root.width += 1; | ||
| 1947 | common.defer(function() { | ||
| 1948 | root.width -= 1; | ||
| 1949 | }); | ||
| 1950 | } | ||
| 1951 | |||
| 1952 | if (!params.parent) { | ||
| 1953 | resetWidth(); | ||
| 1954 | } | ||
| 1955 | |||
| 1956 | }; | ||
| 1957 | |||
| 1958 | GUI.toggleHide = function() { | ||
| 1959 | |||
| 1960 | hide = !hide; | ||
| 1961 | common.each(hideable_guis, function(gui) { | ||
| 1962 | gui.domElement.style.zIndex = hide ? -999 : 999; | ||
| 1963 | gui.domElement.style.opacity = hide ? 0 : 1; | ||
| 1964 | }); | ||
| 1965 | }; | ||
| 1966 | |||
| 1967 | GUI.CLASS_AUTO_PLACE = 'a'; | ||
| 1968 | GUI.CLASS_AUTO_PLACE_CONTAINER = 'ac'; | ||
| 1969 | GUI.CLASS_MAIN = 'main'; | ||
| 1970 | GUI.CLASS_CONTROLLER_ROW = 'cr'; | ||
| 1971 | GUI.CLASS_TOO_TALL = 'taller-than-window'; | ||
| 1972 | GUI.CLASS_CLOSED = 'closed'; | ||
| 1973 | GUI.CLASS_CLOSE_BUTTON = 'close-button'; | ||
| 1974 | GUI.CLASS_DRAG = 'drag'; | ||
| 1975 | |||
| 1976 | GUI.DEFAULT_WIDTH = 245; | ||
| 1977 | GUI.TEXT_CLOSED = 'Close Controls'; | ||
| 1978 | GUI.TEXT_OPEN = 'Open Controls'; | ||
| 1979 | |||
| 1980 | dom.bind(window, 'keydown', function(e) { | ||
| 1981 | |||
| 1982 | if (document.activeElement.type !== 'text' && | ||
| 1983 | (e.which === HIDE_KEY_CODE || e.keyCode == HIDE_KEY_CODE)) { | ||
| 1984 | GUI.toggleHide(); | ||
| 1985 | } | ||
| 1986 | |||
| 1987 | }, false); | ||
| 1988 | |||
| 1989 | common.extend( | ||
| 1990 | |||
| 1991 | GUI.prototype, | ||
| 1992 | |||
| 1993 | /** @lends dat.gui.GUI */ | ||
| 1994 | { | ||
| 1995 | |||
| 1996 | /** | ||
| 1997 | * @param object | ||
| 1998 | * @param property | ||
| 1999 | * @returns {dat.controllers.Controller} The new controller that was added. | ||
| 2000 | * @instance | ||
| 2001 | */ | ||
| 2002 | add: function(object, property) { | ||
| 2003 | |||
| 2004 | return add( | ||
| 2005 | this, | ||
| 2006 | object, | ||
| 2007 | property, | ||
| 2008 | { | ||
| 2009 | factoryArgs: Array.prototype.slice.call(arguments, 2) | ||
| 2010 | } | ||
| 2011 | ); | ||
| 2012 | |||
| 2013 | }, | ||
| 2014 | |||
| 2015 | /** | ||
| 2016 | * @param object | ||
| 2017 | * @param property | ||
| 2018 | * @returns {dat.controllers.ColorController} The new controller that was added. | ||
| 2019 | * @instance | ||
| 2020 | */ | ||
| 2021 | addColor: function(object, property) { | ||
| 2022 | |||
| 2023 | return add( | ||
| 2024 | this, | ||
| 2025 | object, | ||
| 2026 | property, | ||
| 2027 | { | ||
| 2028 | color: true | ||
| 2029 | } | ||
| 2030 | ); | ||
| 2031 | |||
| 2032 | }, | ||
| 2033 | |||
| 2034 | /** | ||
| 2035 | * @param controller | ||
| 2036 | * @instance | ||
| 2037 | */ | ||
| 2038 | remove: function(controller) { | ||
| 2039 | |||
| 2040 | // TODO listening? | ||
| 2041 | this.__ul.removeChild(controller.__li); | ||
| 2042 | this.__controllers.slice(this.__controllers.indexOf(controller), 1); | ||
| 2043 | var _this = this; | ||
| 2044 | common.defer(function() { | ||
| 2045 | _this.onResize(); | ||
| 2046 | }); | ||
| 2047 | |||
| 2048 | }, | ||
| 2049 | |||
| 2050 | destroy: function() { | ||
| 2051 | |||
| 2052 | if (this.autoPlace) { | ||
| 2053 | auto_place_container.removeChild(this.domElement); | ||
| 2054 | } | ||
| 2055 | |||
| 2056 | }, | ||
| 2057 | |||
| 2058 | /** | ||
| 2059 | * @param name | ||
| 2060 | * @returns {dat.gui.GUI} The new folder. | ||
| 2061 | * @throws {Error} if this GUI already has a folder by the specified | ||
| 2062 | * name | ||
| 2063 | * @instance | ||
| 2064 | */ | ||
| 2065 | addFolder: function(name) { | ||
| 2066 | |||
| 2067 | // We have to prevent collisions on names in order to have a key | ||
| 2068 | // by which to remember saved values | ||
| 2069 | if (this.__folders[name] !== undefined) { | ||
| 2070 | throw new Error('You already have a folder in this GUI by the' + | ||
| 2071 | ' name "' + name + '"'); | ||
| 2072 | } | ||
| 2073 | |||
| 2074 | var new_gui_params = { name: name, parent: this }; | ||
| 2075 | |||
| 2076 | // We need to pass down the autoPlace trait so that we can | ||
| 2077 | // attach event listeners to open/close folder actions to | ||
| 2078 | // ensure that a scrollbar appears if the window is too short. | ||
| 2079 | new_gui_params.autoPlace = this.autoPlace; | ||
| 2080 | |||
| 2081 | // Do we have saved appearance data for this folder? | ||
| 2082 | |||
| 2083 | if (this.load && // Anything loaded? | ||
| 2084 | this.load.folders && // Was my parent a dead-end? | ||
| 2085 | this.load.folders[name]) { // Did daddy remember me? | ||
| 2086 | |||
| 2087 | // Start me closed if I was closed | ||
| 2088 | new_gui_params.closed = this.load.folders[name].closed; | ||
| 2089 | |||
| 2090 | // Pass down the loaded data | ||
| 2091 | new_gui_params.load = this.load.folders[name]; | ||
| 2092 | |||
| 2093 | } | ||
| 2094 | |||
| 2095 | var gui = new GUI(new_gui_params); | ||
| 2096 | this.__folders[name] = gui; | ||
| 2097 | |||
| 2098 | var li = addRow(this, gui.domElement); | ||
| 2099 | dom.addClass(li, 'folder'); | ||
| 2100 | return gui; | ||
| 2101 | |||
| 2102 | }, | ||
| 2103 | |||
| 2104 | open: function() { | ||
| 2105 | this.closed = false; | ||
| 2106 | }, | ||
| 2107 | |||
| 2108 | close: function() { | ||
| 2109 | this.closed = true; | ||
| 2110 | }, | ||
| 2111 | |||
| 2112 | onResize: function() { | ||
| 2113 | |||
| 2114 | var root = this.getRoot(); | ||
| 2115 | |||
| 2116 | if (root.scrollable) { | ||
| 2117 | |||
| 2118 | var top = dom.getOffset(root.__ul).top; | ||
| 2119 | var h = 0; | ||
| 2120 | |||
| 2121 | common.each(root.__ul.childNodes, function(node) { | ||
| 2122 | if (! (root.autoPlace && node === root.__save_row)) | ||
| 2123 | h += dom.getHeight(node); | ||
| 2124 | }); | ||
| 2125 | |||
| 2126 | if (window.innerHeight - top - CLOSE_BUTTON_HEIGHT < h) { | ||
| 2127 | dom.addClass(root.domElement, GUI.CLASS_TOO_TALL); | ||
| 2128 | root.__ul.style.height = window.innerHeight - top - CLOSE_BUTTON_HEIGHT + 'px'; | ||
| 2129 | } else { | ||
| 2130 | dom.removeClass(root.domElement, GUI.CLASS_TOO_TALL); | ||
| 2131 | root.__ul.style.height = 'auto'; | ||
| 2132 | } | ||
| 2133 | |||
| 2134 | } | ||
| 2135 | |||
| 2136 | if (root.__resize_handle) { | ||
| 2137 | common.defer(function() { | ||
| 2138 | root.__resize_handle.style.height = root.__ul.offsetHeight + 'px'; | ||
| 2139 | }); | ||
| 2140 | } | ||
| 2141 | |||
| 2142 | if (root.__closeButton) { | ||
| 2143 | root.__closeButton.style.width = root.width + 'px'; | ||
| 2144 | } | ||
| 2145 | |||
| 2146 | }, | ||
| 2147 | |||
| 2148 | /** | ||
| 2149 | * Mark objects for saving. The order of these objects cannot change as | ||
| 2150 | * the GUI grows. When remembering new objects, append them to the end | ||
| 2151 | * of the list. | ||
| 2152 | * | ||
| 2153 | * @param {Object...} objects | ||
| 2154 | * @throws {Error} if not called on a top level GUI. | ||
| 2155 | * @instance | ||
| 2156 | */ | ||
| 2157 | remember: function() { | ||
| 2158 | |||
| 2159 | if (common.isUndefined(SAVE_DIALOGUE)) { | ||
| 2160 | SAVE_DIALOGUE = new CenteredDiv(); | ||
| 2161 | SAVE_DIALOGUE.domElement.innerHTML = saveDialogueContents; | ||
| 2162 | } | ||
| 2163 | |||
| 2164 | if (this.parent) { | ||
| 2165 | throw new Error("You can only call remember on a top level GUI."); | ||
| 2166 | } | ||
| 2167 | |||
| 2168 | var _this = this; | ||
| 2169 | |||
| 2170 | common.each(Array.prototype.slice.call(arguments), function(object) { | ||
| 2171 | if (_this.__rememberedObjects.length == 0) { | ||
| 2172 | addSaveMenu(_this); | ||
| 2173 | } | ||
| 2174 | if (_this.__rememberedObjects.indexOf(object) == -1) { | ||
| 2175 | _this.__rememberedObjects.push(object); | ||
| 2176 | } | ||
| 2177 | }); | ||
| 2178 | |||
| 2179 | if (this.autoPlace) { | ||
| 2180 | // Set save row width | ||
| 2181 | setWidth(this, this.width); | ||
| 2182 | } | ||
| 2183 | |||
| 2184 | }, | ||
| 2185 | |||
| 2186 | /** | ||
| 2187 | * @returns {dat.gui.GUI} the topmost parent GUI of a nested GUI. | ||
| 2188 | * @instance | ||
| 2189 | */ | ||
| 2190 | getRoot: function() { | ||
| 2191 | var gui = this; | ||
| 2192 | while (gui.parent) { | ||
| 2193 | gui = gui.parent; | ||
| 2194 | } | ||
| 2195 | return gui; | ||
| 2196 | }, | ||
| 2197 | |||
| 2198 | /** | ||
| 2199 | * @returns {Object} a JSON object representing the current state of | ||
| 2200 | * this GUI as well as its remembered properties. | ||
| 2201 | * @instance | ||
| 2202 | */ | ||
| 2203 | getSaveObject: function() { | ||
| 2204 | |||
| 2205 | var toReturn = this.load; | ||
| 2206 | |||
| 2207 | toReturn.closed = this.closed; | ||
| 2208 | |||
| 2209 | // Am I remembering any values? | ||
| 2210 | if (this.__rememberedObjects.length > 0) { | ||
| 2211 | |||
| 2212 | toReturn.preset = this.preset; | ||
| 2213 | |||
| 2214 | if (!toReturn.remembered) { | ||
| 2215 | toReturn.remembered = {}; | ||
| 2216 | } | ||
| 2217 | |||
| 2218 | toReturn.remembered[this.preset] = getCurrentPreset(this); | ||
| 2219 | |||
| 2220 | } | ||
| 2221 | |||
| 2222 | toReturn.folders = {}; | ||
| 2223 | common.each(this.__folders, function(element, key) { | ||
| 2224 | toReturn.folders[key] = element.getSaveObject(); | ||
| 2225 | }); | ||
| 2226 | |||
| 2227 | return toReturn; | ||
| 2228 | |||
| 2229 | }, | ||
| 2230 | |||
| 2231 | save: function() { | ||
| 2232 | |||
| 2233 | if (!this.load.remembered) { | ||
| 2234 | this.load.remembered = {}; | ||
| 2235 | } | ||
| 2236 | |||
| 2237 | this.load.remembered[this.preset] = getCurrentPreset(this); | ||
| 2238 | markPresetModified(this, false); | ||
| 2239 | this.saveToLocalStorageIfPossible(); | ||
| 2240 | |||
| 2241 | }, | ||
| 2242 | |||
| 2243 | saveAs: function(presetName) { | ||
| 2244 | |||
| 2245 | if (!this.load.remembered) { | ||
| 2246 | |||
| 2247 | // Retain default values upon first save | ||
| 2248 | this.load.remembered = {}; | ||
| 2249 | this.load.remembered[DEFAULT_DEFAULT_PRESET_NAME] = getCurrentPreset(this, true); | ||
| 2250 | |||
| 2251 | } | ||
| 2252 | |||
| 2253 | this.load.remembered[presetName] = getCurrentPreset(this); | ||
| 2254 | this.preset = presetName; | ||
| 2255 | addPresetOption(this, presetName, true); | ||
| 2256 | this.saveToLocalStorageIfPossible(); | ||
| 2257 | |||
| 2258 | }, | ||
| 2259 | |||
| 2260 | revert: function(gui) { | ||
| 2261 | |||
| 2262 | common.each(this.__controllers, function(controller) { | ||
| 2263 | // Make revert work on Default. | ||
| 2264 | if (!this.getRoot().load.remembered) { | ||
| 2265 | controller.setValue(controller.initialValue); | ||
| 2266 | } else { | ||
| 2267 | recallSavedValue(gui || this.getRoot(), controller); | ||
| 2268 | } | ||
| 2269 | }, this); | ||
| 2270 | |||
| 2271 | common.each(this.__folders, function(folder) { | ||
| 2272 | folder.revert(folder); | ||
| 2273 | }); | ||
| 2274 | |||
| 2275 | if (!gui) { | ||
| 2276 | markPresetModified(this.getRoot(), false); | ||
| 2277 | } | ||
| 2278 | |||
| 2279 | |||
| 2280 | }, | ||
| 2281 | |||
| 2282 | listen: function(controller) { | ||
| 2283 | |||
| 2284 | var init = this.__listening.length == 0; | ||
| 2285 | this.__listening.push(controller); | ||
| 2286 | if (init) updateDisplays(this.__listening); | ||
| 2287 | |||
| 2288 | } | ||
| 2289 | |||
| 2290 | } | ||
| 2291 | |||
| 2292 | ); | ||
| 2293 | |||
| 2294 | function add(gui, object, property, params) { | ||
| 2295 | |||
| 2296 | if (object[property] === undefined) { | ||
| 2297 | throw new Error("Object " + object + " has no property \"" + property + "\""); | ||
| 2298 | } | ||
| 2299 | |||
| 2300 | var controller; | ||
| 2301 | |||
| 2302 | if (params.color) { | ||
| 2303 | |||
| 2304 | controller = new ColorController(object, property); | ||
| 2305 | |||
| 2306 | } else { | ||
| 2307 | |||
| 2308 | var factoryArgs = [object,property].concat(params.factoryArgs); | ||
| 2309 | controller = controllerFactory.apply(gui, factoryArgs); | ||
| 2310 | |||
| 2311 | } | ||
| 2312 | |||
| 2313 | if (params.before instanceof Controller) { | ||
| 2314 | params.before = params.before.__li; | ||
| 2315 | } | ||
| 2316 | |||
| 2317 | recallSavedValue(gui, controller); | ||
| 2318 | |||
| 2319 | dom.addClass(controller.domElement, 'c'); | ||
| 2320 | |||
| 2321 | var name = document.createElement('span'); | ||
| 2322 | dom.addClass(name, 'property-name'); | ||
| 2323 | name.innerHTML = controller.property; | ||
| 2324 | |||
| 2325 | var container = document.createElement('div'); | ||
| 2326 | container.appendChild(name); | ||
| 2327 | container.appendChild(controller.domElement); | ||
| 2328 | |||
| 2329 | var li = addRow(gui, container, params.before); | ||
| 2330 | |||
| 2331 | dom.addClass(li, GUI.CLASS_CONTROLLER_ROW); | ||
| 2332 | dom.addClass(li, typeof controller.getValue()); | ||
| 2333 | |||
| 2334 | augmentController(gui, li, controller); | ||
| 2335 | |||
| 2336 | gui.__controllers.push(controller); | ||
| 2337 | |||
| 2338 | return controller; | ||
| 2339 | |||
| 2340 | } | ||
| 2341 | |||
| 2342 | /** | ||
| 2343 | * Add a row to the end of the GUI or before another row. | ||
| 2344 | * | ||
| 2345 | * @param gui | ||
| 2346 | * @param [dom] If specified, inserts the dom content in the new row | ||
| 2347 | * @param [liBefore] If specified, places the new row before another row | ||
| 2348 | */ | ||
| 2349 | function addRow(gui, dom, liBefore) { | ||
| 2350 | var li = document.createElement('li'); | ||
| 2351 | if (dom) li.appendChild(dom); | ||
| 2352 | if (liBefore) { | ||
| 2353 | gui.__ul.insertBefore(li, params.before); | ||
| 2354 | } else { | ||
| 2355 | gui.__ul.appendChild(li); | ||
| 2356 | } | ||
| 2357 | gui.onResize(); | ||
| 2358 | return li; | ||
| 2359 | } | ||
| 2360 | |||
| 2361 | function augmentController(gui, li, controller) { | ||
| 2362 | |||
| 2363 | controller.__li = li; | ||
| 2364 | controller.__gui = gui; | ||
| 2365 | |||
| 2366 | common.extend(controller, { | ||
| 2367 | |||
| 2368 | options: function(options) { | ||
| 2369 | |||
| 2370 | if (arguments.length > 1) { | ||
| 2371 | controller.remove(); | ||
| 2372 | |||
| 2373 | return add( | ||
| 2374 | gui, | ||
| 2375 | controller.object, | ||
| 2376 | controller.property, | ||
| 2377 | { | ||
| 2378 | before: controller.__li.nextElementSibling, | ||
| 2379 | factoryArgs: [common.toArray(arguments)] | ||
| 2380 | } | ||
| 2381 | ); | ||
| 2382 | |||
| 2383 | } | ||
| 2384 | |||
| 2385 | if (common.isArray(options) || common.isObject(options)) { | ||
| 2386 | controller.remove(); | ||
| 2387 | |||
| 2388 | return add( | ||
| 2389 | gui, | ||
| 2390 | controller.object, | ||
| 2391 | controller.property, | ||
| 2392 | { | ||
| 2393 | before: controller.__li.nextElementSibling, | ||
| 2394 | factoryArgs: [options] | ||
| 2395 | } | ||
| 2396 | ); | ||
| 2397 | |||
| 2398 | } | ||
| 2399 | |||
| 2400 | }, | ||
| 2401 | |||
| 2402 | name: function(v) { | ||
| 2403 | controller.__li.firstElementChild.firstElementChild.innerHTML = v; | ||
| 2404 | return controller; | ||
| 2405 | }, | ||
| 2406 | |||
| 2407 | listen: function() { | ||
| 2408 | controller.__gui.listen(controller); | ||
| 2409 | return controller; | ||
| 2410 | }, | ||
| 2411 | |||
| 2412 | remove: function() { | ||
| 2413 | controller.__gui.remove(controller); | ||
| 2414 | return controller; | ||
| 2415 | } | ||
| 2416 | |||
| 2417 | }); | ||
| 2418 | |||
| 2419 | // All sliders should be accompanied by a box. | ||
| 2420 | if (controller instanceof NumberControllerSlider) { | ||
| 2421 | |||
| 2422 | var box = new NumberControllerBox(controller.object, controller.property, | ||
| 2423 | { min: controller.__min, max: controller.__max, step: controller.__step }); | ||
| 2424 | |||
| 2425 | common.each(['updateDisplay', 'onChange', 'onFinishChange'], function(method) { | ||
| 2426 | var pc = controller[method]; | ||
| 2427 | var pb = box[method]; | ||
| 2428 | controller[method] = box[method] = function() { | ||
| 2429 | var args = Array.prototype.slice.call(arguments); | ||
| 2430 | pc.apply(controller, args); | ||
| 2431 | return pb.apply(box, args); | ||
| 2432 | } | ||
| 2433 | }); | ||
| 2434 | |||
| 2435 | dom.addClass(li, 'has-slider'); | ||
| 2436 | controller.domElement.insertBefore(box.domElement, controller.domElement.firstElementChild); | ||
| 2437 | |||
| 2438 | } | ||
| 2439 | else if (controller instanceof NumberControllerBox) { | ||
| 2440 | |||
| 2441 | var r = function(returned) { | ||
| 2442 | |||
| 2443 | // Have we defined both boundaries? | ||
| 2444 | if (common.isNumber(controller.__min) && common.isNumber(controller.__max)) { | ||
| 2445 | |||
| 2446 | // Well, then lets just replace this with a slider. | ||
| 2447 | controller.remove(); | ||
| 2448 | return add( | ||
| 2449 | gui, | ||
| 2450 | controller.object, | ||
| 2451 | controller.property, | ||
| 2452 | { | ||
| 2453 | before: controller.__li.nextElementSibling, | ||
| 2454 | factoryArgs: [controller.__min, controller.__max, controller.__step] | ||
| 2455 | }); | ||
| 2456 | |||
| 2457 | } | ||
| 2458 | |||
| 2459 | return returned; | ||
| 2460 | |||
| 2461 | }; | ||
| 2462 | |||
| 2463 | controller.min = common.compose(r, controller.min); | ||
| 2464 | controller.max = common.compose(r, controller.max); | ||
| 2465 | |||
| 2466 | } | ||
| 2467 | else if (controller instanceof BooleanController) { | ||
| 2468 | |||
| 2469 | dom.bind(li, 'click', function() { | ||
| 2470 | dom.fakeEvent(controller.__checkbox, 'click'); | ||
| 2471 | }); | ||
| 2472 | |||
| 2473 | dom.bind(controller.__checkbox, 'click', function(e) { | ||
| 2474 | e.stopPropagation(); // Prevents double-toggle | ||
| 2475 | }) | ||
| 2476 | |||
| 2477 | } | ||
| 2478 | else if (controller instanceof FunctionController) { | ||
| 2479 | |||
| 2480 | dom.bind(li, 'click', function() { | ||
| 2481 | dom.fakeEvent(controller.__button, 'click'); | ||
| 2482 | }); | ||
| 2483 | |||
| 2484 | dom.bind(li, 'mouseover', function() { | ||
| 2485 | dom.addClass(controller.__button, 'hover'); | ||
| 2486 | }); | ||
| 2487 | |||
| 2488 | dom.bind(li, 'mouseout', function() { | ||
| 2489 | dom.removeClass(controller.__button, 'hover'); | ||
| 2490 | }); | ||
| 2491 | |||
| 2492 | } | ||
| 2493 | else if (controller instanceof ColorController) { | ||
| 2494 | |||
| 2495 | dom.addClass(li, 'color'); | ||
| 2496 | controller.updateDisplay = common.compose(function(r) { | ||
| 2497 | li.style.borderLeftColor = controller.__color.toString(); | ||
| 2498 | return r; | ||
| 2499 | }, controller.updateDisplay); | ||
| 2500 | |||
| 2501 | controller.updateDisplay(); | ||
| 2502 | |||
| 2503 | } | ||
| 2504 | |||
| 2505 | controller.setValue = common.compose(function(r) { | ||
| 2506 | if (gui.getRoot().__preset_select && controller.isModified()) { | ||
| 2507 | markPresetModified(gui.getRoot(), true); | ||
| 2508 | } | ||
| 2509 | return r; | ||
| 2510 | }, controller.setValue); | ||
| 2511 | |||
| 2512 | } | ||
| 2513 | |||
| 2514 | function recallSavedValue(gui, controller) { | ||
| 2515 | |||
| 2516 | // Find the topmost GUI, that's where remembered objects live. | ||
| 2517 | var root = gui.getRoot(); | ||
| 2518 | |||
| 2519 | // Does the object we're controlling match anything we've been told to | ||
| 2520 | // remember? | ||
| 2521 | var matched_index = root.__rememberedObjects.indexOf(controller.object); | ||
| 2522 | |||
| 2523 | // Why yes, it does! | ||
| 2524 | if (matched_index != -1) { | ||
| 2525 | |||
| 2526 | // Let me fetch a map of controllers for thcommon.isObject. | ||
| 2527 | var controller_map = | ||
| 2528 | root.__rememberedObjectIndecesToControllers[matched_index]; | ||
| 2529 | |||
| 2530 | // Ohp, I believe this is the first controller we've created for this | ||
| 2531 | // object. Lets make the map fresh. | ||
| 2532 | if (controller_map === undefined) { | ||
| 2533 | controller_map = {}; | ||
| 2534 | root.__rememberedObjectIndecesToControllers[matched_index] = | ||
| 2535 | controller_map; | ||
| 2536 | } | ||
| 2537 | |||
| 2538 | // Keep track of this controller | ||
| 2539 | controller_map[controller.property] = controller; | ||
| 2540 | |||
| 2541 | // Okay, now have we saved any values for this controller? | ||
| 2542 | if (root.load && root.load.remembered) { | ||
| 2543 | |||
| 2544 | var preset_map = root.load.remembered; | ||
| 2545 | |||
| 2546 | // Which preset are we trying to load? | ||
| 2547 | var preset; | ||
| 2548 | |||
| 2549 | if (preset_map[gui.preset]) { | ||
| 2550 | |||
| 2551 | preset = preset_map[gui.preset]; | ||
| 2552 | |||
| 2553 | } else if (preset_map[DEFAULT_DEFAULT_PRESET_NAME]) { | ||
| 2554 | |||
| 2555 | // Uhh, you can have the default instead? | ||
| 2556 | preset = preset_map[DEFAULT_DEFAULT_PRESET_NAME]; | ||
| 2557 | |||
| 2558 | } else { | ||
| 2559 | |||
| 2560 | // Nada. | ||
| 2561 | |||
| 2562 | return; | ||
| 2563 | |||
| 2564 | } | ||
| 2565 | |||
| 2566 | |||
| 2567 | // Did the loaded object remember thcommon.isObject? | ||
| 2568 | if (preset[matched_index] && | ||
| 2569 | |||
| 2570 | // Did we remember this particular property? | ||
| 2571 | preset[matched_index][controller.property] !== undefined) { | ||
| 2572 | |||
| 2573 | // We did remember something for this guy ... | ||
| 2574 | var value = preset[matched_index][controller.property]; | ||
| 2575 | |||
| 2576 | // And that's what it is. | ||
| 2577 | controller.initialValue = value; | ||
| 2578 | controller.setValue(value); | ||
| 2579 | |||
| 2580 | } | ||
| 2581 | |||
| 2582 | } | ||
| 2583 | |||
| 2584 | } | ||
| 2585 | |||
| 2586 | } | ||
| 2587 | |||
| 2588 | function getLocalStorageHash(gui, key) { | ||
| 2589 | // TODO how does this deal with multiple GUI's? | ||
| 2590 | return document.location.href + '.' + key; | ||
| 2591 | |||
| 2592 | } | ||
| 2593 | |||
| 2594 | function addSaveMenu(gui) { | ||
| 2595 | |||
| 2596 | var div = gui.__save_row = document.createElement('li'); | ||
| 2597 | |||
| 2598 | dom.addClass(gui.domElement, 'has-save'); | ||
| 2599 | |||
| 2600 | gui.__ul.insertBefore(div, gui.__ul.firstChild); | ||
| 2601 | |||
| 2602 | dom.addClass(div, 'save-row'); | ||
| 2603 | |||
| 2604 | var gears = document.createElement('span'); | ||
| 2605 | gears.innerHTML = ' '; | ||
| 2606 | dom.addClass(gears, 'button gears'); | ||
| 2607 | |||
| 2608 | // TODO replace with FunctionController | ||
| 2609 | var button = document.createElement('span'); | ||
| 2610 | button.innerHTML = 'Save'; | ||
| 2611 | dom.addClass(button, 'button'); | ||
| 2612 | dom.addClass(button, 'save'); | ||
| 2613 | |||
| 2614 | var button2 = document.createElement('span'); | ||
| 2615 | button2.innerHTML = 'New'; | ||
| 2616 | dom.addClass(button2, 'button'); | ||
| 2617 | dom.addClass(button2, 'save-as'); | ||
| 2618 | |||
| 2619 | var button3 = document.createElement('span'); | ||
| 2620 | button3.innerHTML = 'Revert'; | ||
| 2621 | dom.addClass(button3, 'button'); | ||
| 2622 | dom.addClass(button3, 'revert'); | ||
| 2623 | |||
| 2624 | var select = gui.__preset_select = document.createElement('select'); | ||
| 2625 | |||
| 2626 | if (gui.load && gui.load.remembered) { | ||
| 2627 | |||
| 2628 | common.each(gui.load.remembered, function(value, key) { | ||
| 2629 | addPresetOption(gui, key, key == gui.preset); | ||
| 2630 | }); | ||
| 2631 | |||
| 2632 | } else { | ||
| 2633 | addPresetOption(gui, DEFAULT_DEFAULT_PRESET_NAME, false); | ||
| 2634 | } | ||
| 2635 | |||
| 2636 | dom.bind(select, 'change', function() { | ||
| 2637 | |||
| 2638 | |||
| 2639 | for (var index = 0; index < gui.__preset_select.length; index++) { | ||
| 2640 | gui.__preset_select[index].innerHTML = gui.__preset_select[index].value; | ||
| 2641 | } | ||
| 2642 | |||
| 2643 | gui.preset = this.value; | ||
| 2644 | |||
| 2645 | }); | ||
| 2646 | |||
| 2647 | div.appendChild(select); | ||
| 2648 | div.appendChild(gears); | ||
| 2649 | div.appendChild(button); | ||
| 2650 | div.appendChild(button2); | ||
| 2651 | div.appendChild(button3); | ||
| 2652 | |||
| 2653 | if (SUPPORTS_LOCAL_STORAGE) { | ||
| 2654 | |||
| 2655 | var saveLocally = document.getElementById('dg-save-locally'); | ||
| 2656 | var explain = document.getElementById('dg-local-explain'); | ||
| 2657 | |||
| 2658 | saveLocally.style.display = 'block'; | ||
| 2659 | |||
| 2660 | var localStorageCheckBox = document.getElementById('dg-local-storage'); | ||
| 2661 | |||
| 2662 | if (localStorage.getItem(getLocalStorageHash(gui, 'isLocal')) === 'true') { | ||
| 2663 | localStorageCheckBox.setAttribute('checked', 'checked'); | ||
| 2664 | } | ||
| 2665 | |||
| 2666 | function showHideExplain() { | ||
| 2667 | explain.style.display = gui.useLocalStorage ? 'block' : 'none'; | ||
| 2668 | } | ||
| 2669 | |||
| 2670 | showHideExplain(); | ||
| 2671 | |||
| 2672 | // TODO: Use a boolean controller, fool! | ||
| 2673 | dom.bind(localStorageCheckBox, 'change', function() { | ||
| 2674 | gui.useLocalStorage = !gui.useLocalStorage; | ||
| 2675 | showHideExplain(); | ||
| 2676 | }); | ||
| 2677 | |||
| 2678 | } | ||
| 2679 | |||
| 2680 | var newConstructorTextArea = document.getElementById('dg-new-constructor'); | ||
| 2681 | |||
| 2682 | dom.bind(newConstructorTextArea, 'keydown', function(e) { | ||
| 2683 | if (e.metaKey && (e.which === 67 || e.keyCode == 67)) { | ||
| 2684 | SAVE_DIALOGUE.hide(); | ||
| 2685 | } | ||
| 2686 | }); | ||
| 2687 | |||
| 2688 | dom.bind(gears, 'click', function() { | ||
| 2689 | newConstructorTextArea.innerHTML = JSON.stringify(gui.getSaveObject(), undefined, 2); | ||
| 2690 | SAVE_DIALOGUE.show(); | ||
| 2691 | newConstructorTextArea.focus(); | ||
| 2692 | newConstructorTextArea.select(); | ||
| 2693 | }); | ||
| 2694 | |||
| 2695 | dom.bind(button, 'click', function() { | ||
| 2696 | gui.save(); | ||
| 2697 | }); | ||
| 2698 | |||
| 2699 | dom.bind(button2, 'click', function() { | ||
| 2700 | var presetName = prompt('Enter a new preset name.'); | ||
| 2701 | if (presetName) gui.saveAs(presetName); | ||
| 2702 | }); | ||
| 2703 | |||
| 2704 | dom.bind(button3, 'click', function() { | ||
| 2705 | gui.revert(); | ||
| 2706 | }); | ||
| 2707 | |||
| 2708 | // div.appendChild(button2); | ||
| 2709 | |||
| 2710 | } | ||
| 2711 | |||
| 2712 | function addResizeHandle(gui) { | ||
| 2713 | |||
| 2714 | gui.__resize_handle = document.createElement('div'); | ||
| 2715 | |||
| 2716 | common.extend(gui.__resize_handle.style, { | ||
| 2717 | |||
| 2718 | width: '6px', | ||
| 2719 | marginLeft: '-3px', | ||
| 2720 | height: '200px', | ||
| 2721 | cursor: 'ew-resize', | ||
| 2722 | position: 'absolute' | ||
| 2723 | // border: '1px solid blue' | ||
| 2724 | |||
| 2725 | }); | ||
| 2726 | |||
| 2727 | var pmouseX; | ||
| 2728 | |||
| 2729 | dom.bind(gui.__resize_handle, 'mousedown', dragStart); | ||
| 2730 | dom.bind(gui.__closeButton, 'mousedown', dragStart); | ||
| 2731 | |||
| 2732 | gui.domElement.insertBefore(gui.__resize_handle, gui.domElement.firstElementChild); | ||
| 2733 | |||
| 2734 | function dragStart(e) { | ||
| 2735 | |||
| 2736 | e.preventDefault(); | ||
| 2737 | |||
| 2738 | pmouseX = e.clientX; | ||
| 2739 | |||
| 2740 | dom.addClass(gui.__closeButton, GUI.CLASS_DRAG); | ||
| 2741 | dom.bind(window, 'mousemove', drag); | ||
| 2742 | dom.bind(window, 'mouseup', dragStop); | ||
| 2743 | |||
| 2744 | return false; | ||
| 2745 | |||
| 2746 | } | ||
| 2747 | |||
| 2748 | function drag(e) { | ||
| 2749 | |||
| 2750 | e.preventDefault(); | ||
| 2751 | |||
| 2752 | gui.width += pmouseX - e.clientX; | ||
| 2753 | gui.onResize(); | ||
| 2754 | pmouseX = e.clientX; | ||
| 2755 | |||
| 2756 | return false; | ||
| 2757 | |||
| 2758 | } | ||
| 2759 | |||
| 2760 | function dragStop() { | ||
| 2761 | |||
| 2762 | dom.removeClass(gui.__closeButton, GUI.CLASS_DRAG); | ||
| 2763 | dom.unbind(window, 'mousemove', drag); | ||
| 2764 | dom.unbind(window, 'mouseup', dragStop); | ||
| 2765 | |||
| 2766 | } | ||
| 2767 | |||
| 2768 | } | ||
| 2769 | |||
| 2770 | function setWidth(gui, w) { | ||
| 2771 | gui.domElement.style.width = w + 'px'; | ||
| 2772 | // Auto placed save-rows are position fixed, so we have to | ||
| 2773 | // set the width manually if we want it to bleed to the edge | ||
| 2774 | if (gui.__save_row && gui.autoPlace) { | ||
| 2775 | gui.__save_row.style.width = w + 'px'; | ||
| 2776 | }if (gui.__closeButton) { | ||
| 2777 | gui.__closeButton.style.width = w + 'px'; | ||
| 2778 | } | ||
| 2779 | } | ||
| 2780 | |||
| 2781 | function getCurrentPreset(gui, useInitialValues) { | ||
| 2782 | |||
| 2783 | var toReturn = {}; | ||
| 2784 | |||
| 2785 | // For each object I'm remembering | ||
| 2786 | common.each(gui.__rememberedObjects, function(val, index) { | ||
| 2787 | |||
| 2788 | var saved_values = {}; | ||
| 2789 | |||
| 2790 | // The controllers I've made for thcommon.isObject by property | ||
| 2791 | var controller_map = | ||
| 2792 | gui.__rememberedObjectIndecesToControllers[index]; | ||
| 2793 | |||
| 2794 | // Remember each value for each property | ||
| 2795 | common.each(controller_map, function(controller, property) { | ||
| 2796 | saved_values[property] = useInitialValues ? controller.initialValue : controller.getValue(); | ||
| 2797 | }); | ||
| 2798 | |||
| 2799 | // Save the values for thcommon.isObject | ||
| 2800 | toReturn[index] = saved_values; | ||
| 2801 | |||
| 2802 | }); | ||
| 2803 | |||
| 2804 | return toReturn; | ||
| 2805 | |||
| 2806 | } | ||
| 2807 | |||
| 2808 | function addPresetOption(gui, name, setSelected) { | ||
| 2809 | var opt = document.createElement('option'); | ||
| 2810 | opt.innerHTML = name; | ||
| 2811 | opt.value = name; | ||
| 2812 | gui.__preset_select.appendChild(opt); | ||
| 2813 | if (setSelected) { | ||
| 2814 | gui.__preset_select.selectedIndex = gui.__preset_select.length - 1; | ||
| 2815 | } | ||
| 2816 | } | ||
| 2817 | |||
| 2818 | function setPresetSelectIndex(gui) { | ||
| 2819 | for (var index = 0; index < gui.__preset_select.length; index++) { | ||
| 2820 | if (gui.__preset_select[index].value == gui.preset) { | ||
| 2821 | gui.__preset_select.selectedIndex = index; | ||
| 2822 | } | ||
| 2823 | } | ||
| 2824 | } | ||
| 2825 | |||
| 2826 | function markPresetModified(gui, modified) { | ||
| 2827 | var opt = gui.__preset_select[gui.__preset_select.selectedIndex]; | ||
| 2828 | // console.log('mark', modified, opt); | ||
| 2829 | if (modified) { | ||
| 2830 | opt.innerHTML = opt.value + "*"; | ||
| 2831 | } else { | ||
| 2832 | opt.innerHTML = opt.value; | ||
| 2833 | } | ||
| 2834 | } | ||
| 2835 | |||
| 2836 | function updateDisplays(controllerArray) { | ||
| 2837 | |||
| 2838 | |||
| 2839 | if (controllerArray.length != 0) { | ||
| 2840 | |||
| 2841 | requestAnimationFrame(function() { | ||
| 2842 | updateDisplays(controllerArray); | ||
| 2843 | }); | ||
| 2844 | |||
| 2845 | } | ||
| 2846 | |||
| 2847 | common.each(controllerArray, function(c) { | ||
| 2848 | c.updateDisplay(); | ||
| 2849 | }); | ||
| 2850 | |||
| 2851 | } | ||
| 2852 | |||
| 2853 | return GUI; | ||
| 2854 | |||
| 2855 | })(dat.utils.css, | ||
| 2856 | "<div id=\"dg-save\" class=\"dg dialogue\">\n\n Here's the new load parameter for your <code>GUI</code>'s constructor:\n\n <textarea id=\"dg-new-constructor\"></textarea>\n\n <div id=\"dg-save-locally\">\n\n <input id=\"dg-local-storage\" type=\"checkbox\"/> Automatically save\n values to <code>localStorage</code> on exit.\n\n <div id=\"dg-local-explain\">The values saved to <code>localStorage</code> will\n override those passed to <code>dat.GUI</code>'s constructor. This makes it\n easier to work incrementally, but <code>localStorage</code> is fragile,\n and your friends may not see the same values you do.\n \n </div>\n \n </div>\n\n</div>", | ||
| 2857 | ".dg {\n /** Clear list styles */\n /* Auto-place container */\n /* Auto-placed GUI's */\n /* Line items that don't contain folders. */\n /** Folder names */\n /** Hides closed items */\n /** Controller row */\n /** Name-half (left) */\n /** Controller-half (right) */\n /** Controller placement */\n /** Shorter number boxes when slider is present. */\n /** Ensure the entire boolean and function row shows a hand */ }\n .dg ul {\n list-style: none;\n margin: 0;\n padding: 0;\n width: 100%;\n clear: both; }\n .dg.ac {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n height: 0;\n z-index: 0; }\n .dg:not(.ac) .main {\n /** Exclude mains in ac so that we don't hide close button */\n overflow: hidden; }\n .dg.main {\n -webkit-transition: opacity 0.1s linear;\n -o-transition: opacity 0.1s linear;\n -moz-transition: opacity 0.1s linear;\n transition: opacity 0.1s linear; }\n .dg.main.taller-than-window {\n overflow-y: auto; }\n .dg.main.taller-than-window .close-button {\n opacity: 1;\n /* TODO, these are style notes */\n margin-top: -1px;\n border-top: 1px solid #2c2c2c; }\n .dg.main ul.closed .close-button {\n opacity: 1 !important; }\n .dg.main:hover .close-button,\n .dg.main .close-button.drag {\n opacity: 1; }\n .dg.main .close-button {\n /*opacity: 0;*/\n -webkit-transition: opacity 0.1s linear;\n -o-transition: opacity 0.1s linear;\n -moz-transition: opacity 0.1s linear;\n transition: opacity 0.1s linear;\n border: 0;\n position: absolute;\n line-height: 19px;\n height: 20px;\n /* TODO, these are style notes */\n cursor: pointer;\n text-align: center;\n background-color: #000; }\n .dg.main .close-button:hover {\n background-color: #111; }\n .dg.a {\n float: right;\n margin-right: 15px;\n overflow-x: hidden; }\n .dg.a.has-save > ul {\n margin-top: 27px; }\n .dg.a.has-save > ul.closed {\n margin-top: 0; }\n .dg.a .save-row {\n position: fixed;\n top: 0;\n z-index: 1002; }\n .dg li {\n -webkit-transition: height 0.1s ease-out;\n -o-transition: height 0.1s ease-out;\n -moz-transition: height 0.1s ease-out;\n transition: height 0.1s ease-out; }\n .dg li:not(.folder) {\n cursor: auto;\n height: 27px;\n line-height: 27px;\n overflow: hidden;\n padding: 0 4px 0 5px; }\n .dg li.folder {\n padding: 0;\n border-left: 4px solid rgba(0, 0, 0, 0); }\n .dg li.title {\n cursor: pointer;\n margin-left: -4px; }\n .dg .closed li:not(.title),\n .dg .closed ul li,\n .dg .closed ul li > * {\n height: 0;\n overflow: hidden;\n border: 0; }\n .dg .cr {\n clear: both;\n padding-left: 3px;\n height: 27px; }\n .dg .property-name {\n cursor: default;\n float: left;\n clear: left;\n width: 40%;\n overflow: hidden;\n text-overflow: ellipsis; }\n .dg .c {\n float: left;\n width: 60%; }\n .dg .c input[type=text] {\n border: 0;\n margin-top: 4px;\n padding: 3px;\n width: 100%;\n float: right; }\n .dg .has-slider input[type=text] {\n width: 30%;\n /*display: none;*/\n margin-left: 0; }\n .dg .slider {\n float: left;\n width: 66%;\n margin-left: -5px;\n margin-right: 0;\n height: 19px;\n margin-top: 4px; }\n .dg .slider-fg {\n height: 100%; }\n .dg .c input[type=checkbox] {\n margin-top: 9px; }\n .dg .c select {\n margin-top: 5px; }\n .dg .cr.function,\n .dg .cr.function .property-name,\n .dg .cr.function *,\n .dg .cr.boolean,\n .dg .cr.boolean * {\n cursor: pointer; }\n .dg .selector {\n display: none;\n position: absolute;\n margin-left: -9px;\n margin-top: 23px;\n z-index: 10; }\n .dg .c:hover .selector,\n .dg .selector.drag {\n display: block; }\n .dg li.save-row {\n padding: 0; }\n .dg li.save-row .button {\n display: inline-block;\n padding: 0px 6px; }\n .dg.dialogue {\n background-color: #222;\n width: 460px;\n padding: 15px;\n font-size: 13px;\n line-height: 15px; }\n\n/* TODO Separate style and structure */\n#dg-new-constructor {\n padding: 10px;\n color: #222;\n font-family: Monaco, monospace;\n font-size: 10px;\n border: 0;\n resize: none;\n box-shadow: inset 1px 1px 1px #888;\n word-wrap: break-word;\n margin: 12px 0;\n display: block;\n width: 440px;\n overflow-y: scroll;\n height: 100px;\n position: relative; }\n\n#dg-local-explain {\n display: none;\n font-size: 11px;\n line-height: 17px;\n border-radius: 3px;\n background-color: #333;\n padding: 8px;\n margin-top: 10px; }\n #dg-local-explain code {\n font-size: 10px; }\n\n#dat-gui-save-locally {\n display: none; }\n\n/** Main type */\n.dg {\n color: #eee;\n font: 11px 'Lucida Grande', sans-serif;\n text-shadow: 0 -1px 0 #111;\n /** Auto place */\n /* Controller row, <li> */\n /** Controllers */ }\n .dg.main {\n /** Scrollbar */ }\n .dg.main::-webkit-scrollbar {\n width: 5px;\n background: #1a1a1a; }\n .dg.main::-webkit-scrollbar-corner {\n height: 0;\n display: none; }\n .dg.main::-webkit-scrollbar-thumb {\n border-radius: 5px;\n background: #676767; }\n .dg li:not(.folder) {\n background: #1a1a1a;\n border-bottom: 1px solid #2c2c2c; }\n .dg li.save-row {\n line-height: 25px;\n background: #dad5cb;\n border: 0; }\n .dg li.save-row select {\n margin-left: 5px;\n width: 108px; }\n .dg li.save-row .button {\n margin-left: 5px;\n margin-top: 1px;\n border-radius: 2px;\n font-size: 9px;\n line-height: 7px;\n padding: 4px 4px 5px 4px;\n background: #c5bdad;\n color: #fff;\n text-shadow: 0 1px 0 #b0a58f;\n box-shadow: 0 -1px 0 #b0a58f;\n cursor: pointer; }\n .dg li.save-row .button.gears {\n 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;\n height: 7px;\n width: 8px; }\n .dg li.save-row .button:hover {\n background-color: #bab19e;\n box-shadow: 0 -1px 0 #b0a58f; }\n .dg li.folder {\n border-bottom: 0; }\n .dg li.title {\n padding-left: 16px;\n background: black url(data:image/gif;base64,R0lGODlhBQAFAJEAAP////Pz8////////yH5BAEAAAIALAAAAAAFAAUAAAIIlI+hKgFxoCgAOw==) 6px 10px no-repeat;\n cursor: pointer;\n border-bottom: 1px solid rgba(255, 255, 255, 0.2); }\n .dg .closed li.title {\n background-image: url(data:image/gif;base64,R0lGODlhBQAFAJEAAP////Pz8////////yH5BAEAAAIALAAAAAAFAAUAAAIIlGIWqMCbWAEAOw==); }\n .dg .cr.boolean {\n border-left: 3px solid #806787; }\n .dg .cr.function {\n border-left: 3px solid #e61d5f; }\n .dg .cr.number {\n border-left: 3px solid #2fa1d6; }\n .dg .cr.number input[type=text] {\n color: #2fa1d6; }\n .dg .cr.string {\n border-left: 3px solid #1ed36f; }\n .dg .cr.string input[type=text] {\n color: #1ed36f; }\n .dg .cr.function:hover, .dg .cr.boolean:hover {\n background: #111; }\n .dg .c input[type=text] {\n background: #303030;\n outline: none; }\n .dg .c input[type=text]:hover {\n background: #3c3c3c; }\n .dg .c input[type=text]:focus {\n background: #494949;\n color: #fff; }\n .dg .c .slider {\n background: #303030;\n cursor: ew-resize; }\n .dg .c .slider-fg {\n background: #2fa1d6; }\n .dg .c .slider:hover {\n background: #3c3c3c; }\n .dg .c .slider:hover .slider-fg {\n background: #44abda; }\n", | ||
| 2858 | dat.controllers.factory = (function (OptionController, NumberControllerBox, NumberControllerSlider, StringController, FunctionController, BooleanController, common) { | ||
| 2859 | |||
| 2860 | return function(object, property) { | ||
| 2861 | |||
| 2862 | var initialValue = object[property]; | ||
| 2863 | |||
| 2864 | // Providing options? | ||
| 2865 | if (common.isArray(arguments[2]) || common.isObject(arguments[2])) { | ||
| 2866 | return new OptionController(object, property, arguments[2]); | ||
| 2867 | } | ||
| 2868 | |||
| 2869 | // Providing a map? | ||
| 2870 | |||
| 2871 | if (common.isNumber(initialValue)) { | ||
| 2872 | |||
| 2873 | if (common.isNumber(arguments[2]) && common.isNumber(arguments[3])) { | ||
| 2874 | |||
| 2875 | // Has min and max. | ||
| 2876 | return new NumberControllerSlider(object, property, arguments[2], arguments[3]); | ||
| 2877 | |||
| 2878 | } else { | ||
| 2879 | |||
| 2880 | return new NumberControllerBox(object, property, { min: arguments[2], max: arguments[3] }); | ||
| 2881 | |||
| 2882 | } | ||
| 2883 | |||
| 2884 | } | ||
| 2885 | |||
| 2886 | if (common.isString(initialValue)) { | ||
| 2887 | return new StringController(object, property); | ||
| 2888 | } | ||
| 2889 | |||
| 2890 | if (common.isFunction(initialValue)) { | ||
| 2891 | return new FunctionController(object, property, ''); | ||
| 2892 | } | ||
| 2893 | |||
| 2894 | if (common.isBoolean(initialValue)) { | ||
| 2895 | return new BooleanController(object, property); | ||
| 2896 | } | ||
| 2897 | |||
| 2898 | } | ||
| 2899 | |||
| 2900 | })(dat.controllers.OptionController, | ||
| 2901 | dat.controllers.NumberControllerBox, | ||
| 2902 | dat.controllers.NumberControllerSlider, | ||
| 2903 | dat.controllers.StringController = (function (Controller, dom, common) { | ||
| 2904 | |||
| 2905 | /** | ||
| 2906 | * @class Provides a text input to alter the string property of an object. | ||
| 2907 | * | ||
| 2908 | * @extends dat.controllers.Controller | ||
| 2909 | * | ||
| 2910 | * @param {Object} object The object to be manipulated | ||
| 2911 | * @param {string} property The name of the property to be manipulated | ||
| 2912 | * | ||
| 2913 | * @member dat.controllers | ||
| 2914 | */ | ||
| 2915 | var StringController = function(object, property) { | ||
| 2916 | |||
| 2917 | StringController.superclass.call(this, object, property); | ||
| 2918 | |||
| 2919 | var _this = this; | ||
| 2920 | |||
| 2921 | this.__input = document.createElement('input'); | ||
| 2922 | this.__input.setAttribute('type', 'text'); | ||
| 2923 | |||
| 2924 | dom.bind(this.__input, 'keyup', onChange); | ||
| 2925 | dom.bind(this.__input, 'change', onChange); | ||
| 2926 | dom.bind(this.__input, 'blur', onBlur); | ||
| 2927 | dom.bind(this.__input, 'keydown', function(e) { | ||
| 2928 | if (e.keyCode === 13) { | ||
| 2929 | this.blur(); | ||
| 2930 | } | ||
| 2931 | }); | ||
| 2932 | |||
| 2933 | |||
| 2934 | function onChange() { | ||
| 2935 | _this.setValue(_this.__input.value); | ||
| 2936 | } | ||
| 2937 | |||
| 2938 | function onBlur() { | ||
| 2939 | if (_this.__onFinishChange) { | ||
| 2940 | _this.__onFinishChange.call(_this, _this.getValue()); | ||
| 2941 | } | ||
| 2942 | } | ||
| 2943 | |||
| 2944 | this.updateDisplay(); | ||
| 2945 | |||
| 2946 | this.domElement.appendChild(this.__input); | ||
| 2947 | |||
| 2948 | }; | ||
| 2949 | |||
| 2950 | StringController.superclass = Controller; | ||
| 2951 | |||
| 2952 | common.extend( | ||
| 2953 | |||
| 2954 | StringController.prototype, | ||
| 2955 | Controller.prototype, | ||
| 2956 | |||
| 2957 | { | ||
| 2958 | |||
| 2959 | updateDisplay: function() { | ||
| 2960 | // Stops the caret from moving on account of: | ||
| 2961 | // keyup -> setValue -> updateDisplay | ||
| 2962 | if (!dom.isActive(this.__input)) { | ||
| 2963 | this.__input.value = this.getValue(); | ||
| 2964 | } | ||
| 2965 | return StringController.superclass.prototype.updateDisplay.call(this); | ||
| 2966 | } | ||
| 2967 | |||
| 2968 | } | ||
| 2969 | |||
| 2970 | ); | ||
| 2971 | |||
| 2972 | return StringController; | ||
| 2973 | |||
| 2974 | })(dat.controllers.Controller, | ||
| 2975 | dat.dom.dom, | ||
| 2976 | dat.utils.common), | ||
| 2977 | dat.controllers.FunctionController, | ||
| 2978 | dat.controllers.BooleanController, | ||
| 2979 | dat.utils.common), | ||
| 2980 | dat.controllers.Controller, | ||
| 2981 | dat.controllers.BooleanController, | ||
| 2982 | dat.controllers.FunctionController, | ||
| 2983 | dat.controllers.NumberControllerBox, | ||
| 2984 | dat.controllers.NumberControllerSlider, | ||
| 2985 | dat.controllers.OptionController, | ||
| 2986 | dat.controllers.ColorController = (function (Controller, dom, Color, interpret, common) { | ||
| 2987 | |||
| 2988 | var ColorController = function(object, property) { | ||
| 2989 | |||
| 2990 | ColorController.superclass.call(this, object, property); | ||
| 2991 | |||
| 2992 | this.__color = new Color(this.getValue()); | ||
| 2993 | this.__temp = new Color(0); | ||
| 2994 | |||
| 2995 | var _this = this; | ||
| 2996 | |||
| 2997 | this.domElement = document.createElement('div'); | ||
| 2998 | |||
| 2999 | dom.makeSelectable(this.domElement, false); | ||
| 3000 | |||
| 3001 | this.__selector = document.createElement('div'); | ||
| 3002 | this.__selector.className = 'selector'; | ||
| 3003 | |||
| 3004 | this.__saturation_field = document.createElement('div'); | ||
| 3005 | this.__saturation_field.className = 'saturation-field'; | ||
| 3006 | |||
| 3007 | this.__field_knob = document.createElement('div'); | ||
| 3008 | this.__field_knob.className = 'field-knob'; | ||
| 3009 | this.__field_knob_border = '2px solid '; | ||
| 3010 | |||
| 3011 | this.__hue_knob = document.createElement('div'); | ||
| 3012 | this.__hue_knob.className = 'hue-knob'; | ||
| 3013 | |||
| 3014 | this.__hue_field = document.createElement('div'); | ||
| 3015 | this.__hue_field.className = 'hue-field'; | ||
| 3016 | |||
| 3017 | this.__input = document.createElement('input'); | ||
| 3018 | this.__input.type = 'text'; | ||
| 3019 | this.__input_textShadow = '0 1px 1px '; | ||
| 3020 | |||
| 3021 | dom.bind(this.__input, 'keydown', function(e) { | ||
| 3022 | if (e.keyCode === 13) { // on enter | ||
| 3023 | onBlur.call(this); | ||
| 3024 | } | ||
| 3025 | }); | ||
| 3026 | |||
| 3027 | dom.bind(this.__input, 'blur', onBlur); | ||
| 3028 | |||
| 3029 | dom.bind(this.__selector, 'mousedown', function(e) { | ||
| 3030 | |||
| 3031 | dom | ||
| 3032 | .addClass(this, 'drag') | ||
| 3033 | .bind(window, 'mouseup', function(e) { | ||
| 3034 | dom.removeClass(_this.__selector, 'drag'); | ||
| 3035 | }); | ||
| 3036 | |||
| 3037 | }); | ||
| 3038 | |||
| 3039 | var value_field = document.createElement('div'); | ||
| 3040 | |||
| 3041 | common.extend(this.__selector.style, { | ||
| 3042 | width: '122px', | ||
| 3043 | height: '102px', | ||
| 3044 | padding: '3px', | ||
| 3045 | backgroundColor: '#222', | ||
| 3046 | boxShadow: '0px 1px 3px rgba(0,0,0,0.3)' | ||
| 3047 | }); | ||
| 3048 | |||
| 3049 | common.extend(this.__field_knob.style, { | ||
| 3050 | position: 'absolute', | ||
| 3051 | width: '12px', | ||
| 3052 | height: '12px', | ||
| 3053 | border: this.__field_knob_border + (this.__color.v < .5 ? '#fff' : '#000'), | ||
| 3054 | boxShadow: '0px 1px 3px rgba(0,0,0,0.5)', | ||
| 3055 | borderRadius: '12px', | ||
| 3056 | zIndex: 1 | ||
| 3057 | }); | ||
| 3058 | |||
| 3059 | common.extend(this.__hue_knob.style, { | ||
| 3060 | position: 'absolute', | ||
| 3061 | width: '15px', | ||
| 3062 | height: '2px', | ||
| 3063 | borderRight: '4px solid #fff', | ||
| 3064 | zIndex: 1 | ||
| 3065 | }); | ||
| 3066 | |||
| 3067 | common.extend(this.__saturation_field.style, { | ||
| 3068 | width: '100px', | ||
| 3069 | height: '100px', | ||
| 3070 | border: '1px solid #555', | ||
| 3071 | marginRight: '3px', | ||
| 3072 | display: 'inline-block', | ||
| 3073 | cursor: 'pointer' | ||
| 3074 | }); | ||
| 3075 | |||
| 3076 | common.extend(value_field.style, { | ||
| 3077 | width: '100%', | ||
| 3078 | height: '100%', | ||
| 3079 | background: 'none' | ||
| 3080 | }); | ||
| 3081 | |||
| 3082 | linearGradient(value_field, 'top', 'rgba(0,0,0,0)', '#000'); | ||
| 3083 | |||
| 3084 | common.extend(this.__hue_field.style, { | ||
| 3085 | width: '15px', | ||
| 3086 | height: '100px', | ||
| 3087 | display: 'inline-block', | ||
| 3088 | border: '1px solid #555', | ||
| 3089 | cursor: 'ns-resize' | ||
| 3090 | }); | ||
| 3091 | |||
| 3092 | hueGradient(this.__hue_field); | ||
| 3093 | |||
| 3094 | common.extend(this.__input.style, { | ||
| 3095 | outline: 'none', | ||
| 3096 | // width: '120px', | ||
| 3097 | textAlign: 'center', | ||
| 3098 | // padding: '4px', | ||
| 3099 | // marginBottom: '6px', | ||
| 3100 | color: '#fff', | ||
| 3101 | border: 0, | ||
| 3102 | fontWeight: 'bold', | ||
| 3103 | textShadow: this.__input_textShadow + 'rgba(0,0,0,0.7)' | ||
| 3104 | }); | ||
| 3105 | |||
| 3106 | dom.bind(this.__saturation_field, 'mousedown', fieldDown); | ||
| 3107 | dom.bind(this.__field_knob, 'mousedown', fieldDown); | ||
| 3108 | |||
| 3109 | dom.bind(this.__hue_field, 'mousedown', function(e) { | ||
| 3110 | setH(e); | ||
| 3111 | dom.bind(window, 'mousemove', setH); | ||
| 3112 | dom.bind(window, 'mouseup', unbindH); | ||
| 3113 | }); | ||
| 3114 | |||
| 3115 | function fieldDown(e) { | ||
| 3116 | setSV(e); | ||
| 3117 | // document.body.style.cursor = 'none'; | ||
| 3118 | dom.bind(window, 'mousemove', setSV); | ||
| 3119 | dom.bind(window, 'mouseup', unbindSV); | ||
| 3120 | } | ||
| 3121 | |||
| 3122 | function unbindSV() { | ||
| 3123 | dom.unbind(window, 'mousemove', setSV); | ||
| 3124 | dom.unbind(window, 'mouseup', unbindSV); | ||
| 3125 | // document.body.style.cursor = 'default'; | ||
| 3126 | } | ||
| 3127 | |||
| 3128 | function onBlur() { | ||
| 3129 | var i = interpret(this.value); | ||
| 3130 | if (i !== false) { | ||
| 3131 | _this.__color.__state = i; | ||
| 3132 | _this.setValue(_this.__color.toOriginal()); | ||
| 3133 | } else { | ||
| 3134 | this.value = _this.__color.toString(); | ||
| 3135 | } | ||
| 3136 | } | ||
| 3137 | |||
| 3138 | function unbindH() { | ||
| 3139 | dom.unbind(window, 'mousemove', setH); | ||
| 3140 | dom.unbind(window, 'mouseup', unbindH); | ||
| 3141 | } | ||
| 3142 | |||
| 3143 | this.__saturation_field.appendChild(value_field); | ||
| 3144 | this.__selector.appendChild(this.__field_knob); | ||
| 3145 | this.__selector.appendChild(this.__saturation_field); | ||
| 3146 | this.__selector.appendChild(this.__hue_field); | ||
| 3147 | this.__hue_field.appendChild(this.__hue_knob); | ||
| 3148 | |||
| 3149 | this.domElement.appendChild(this.__input); | ||
| 3150 | this.domElement.appendChild(this.__selector); | ||
| 3151 | |||
| 3152 | this.updateDisplay(); | ||
| 3153 | |||
| 3154 | function setSV(e) { | ||
| 3155 | |||
| 3156 | e.preventDefault(); | ||
| 3157 | |||
| 3158 | var w = dom.getWidth(_this.__saturation_field); | ||
| 3159 | var o = dom.getOffset(_this.__saturation_field); | ||
| 3160 | var s = (e.clientX - o.left + document.body.scrollLeft) / w; | ||
| 3161 | var v = 1 - (e.clientY - o.top + document.body.scrollTop) / w; | ||
| 3162 | |||
| 3163 | if (v > 1) v = 1; | ||
| 3164 | else if (v < 0) v = 0; | ||
| 3165 | |||
| 3166 | if (s > 1) s = 1; | ||
| 3167 | else if (s < 0) s = 0; | ||
| 3168 | |||
| 3169 | _this.__color.v = v; | ||
| 3170 | _this.__color.s = s; | ||
| 3171 | |||
| 3172 | _this.setValue(_this.__color.toOriginal()); | ||
| 3173 | |||
| 3174 | |||
| 3175 | return false; | ||
| 3176 | |||
| 3177 | } | ||
| 3178 | |||
| 3179 | function setH(e) { | ||
| 3180 | |||
| 3181 | e.preventDefault(); | ||
| 3182 | |||
| 3183 | var s = dom.getHeight(_this.__hue_field); | ||
| 3184 | var o = dom.getOffset(_this.__hue_field); | ||
| 3185 | var h = 1 - (e.clientY - o.top + document.body.scrollTop) / s; | ||
| 3186 | |||
| 3187 | if (h > 1) h = 1; | ||
| 3188 | else if (h < 0) h = 0; | ||
| 3189 | |||
| 3190 | _this.__color.h = h * 360; | ||
| 3191 | |||
| 3192 | _this.setValue(_this.__color.toOriginal()); | ||
| 3193 | |||
| 3194 | return false; | ||
| 3195 | |||
| 3196 | } | ||
| 3197 | |||
| 3198 | }; | ||
| 3199 | |||
| 3200 | ColorController.superclass = Controller; | ||
| 3201 | |||
| 3202 | common.extend( | ||
| 3203 | |||
| 3204 | ColorController.prototype, | ||
| 3205 | Controller.prototype, | ||
| 3206 | |||
| 3207 | { | ||
| 3208 | |||
| 3209 | updateDisplay: function() { | ||
| 3210 | |||
| 3211 | var i = interpret(this.getValue()); | ||
| 3212 | |||
| 3213 | if (i !== false) { | ||
| 3214 | |||
| 3215 | var mismatch = false; | ||
| 3216 | |||
| 3217 | // Check for mismatch on the interpreted value. | ||
| 3218 | |||
| 3219 | common.each(Color.COMPONENTS, function(component) { | ||
| 3220 | if (!common.isUndefined(i[component]) && | ||
| 3221 | !common.isUndefined(this.__color.__state[component]) && | ||
| 3222 | i[component] !== this.__color.__state[component]) { | ||
| 3223 | mismatch = true; | ||
| 3224 | return {}; // break | ||
| 3225 | } | ||
| 3226 | }, this); | ||
| 3227 | |||
| 3228 | // If nothing diverges, we keep our previous values | ||
| 3229 | // for statefulness, otherwise we recalculate fresh | ||
| 3230 | if (mismatch) { | ||
| 3231 | common.extend(this.__color.__state, i); | ||
| 3232 | } | ||
| 3233 | |||
| 3234 | } | ||
| 3235 | |||
| 3236 | common.extend(this.__temp.__state, this.__color.__state); | ||
| 3237 | |||
| 3238 | this.__temp.a = 1; | ||
| 3239 | |||
| 3240 | var flip = (this.__color.v < .5 || this.__color.s > .5) ? 255 : 0; | ||
| 3241 | var _flip = 255 - flip; | ||
| 3242 | |||
| 3243 | common.extend(this.__field_knob.style, { | ||
| 3244 | marginLeft: 100 * this.__color.s - 7 + 'px', | ||
| 3245 | marginTop: 100 * (1 - this.__color.v) - 7 + 'px', | ||
| 3246 | backgroundColor: this.__temp.toString(), | ||
| 3247 | border: this.__field_knob_border + 'rgb(' + flip + ',' + flip + ',' + flip +')' | ||
| 3248 | }); | ||
| 3249 | |||
| 3250 | this.__hue_knob.style.marginTop = (1 - this.__color.h / 360) * 100 + 'px' | ||
| 3251 | |||
| 3252 | this.__temp.s = 1; | ||
| 3253 | this.__temp.v = 1; | ||
| 3254 | |||
| 3255 | linearGradient(this.__saturation_field, 'left', '#fff', this.__temp.toString()); | ||
| 3256 | |||
| 3257 | common.extend(this.__input.style, { | ||
| 3258 | backgroundColor: this.__input.value = this.__color.toString(), | ||
| 3259 | color: 'rgb(' + flip + ',' + flip + ',' + flip +')', | ||
| 3260 | textShadow: this.__input_textShadow + 'rgba(' + _flip + ',' + _flip + ',' + _flip +',.7)' | ||
| 3261 | }); | ||
| 3262 | |||
| 3263 | } | ||
| 3264 | |||
| 3265 | } | ||
| 3266 | |||
| 3267 | ); | ||
| 3268 | |||
| 3269 | var vendors = ['-moz-','-o-','-webkit-','-ms-','']; | ||
| 3270 | |||
| 3271 | function linearGradient(elem, x, a, b) { | ||
| 3272 | elem.style.background = ''; | ||
| 3273 | common.each(vendors, function(vendor) { | ||
| 3274 | elem.style.cssText += 'background: ' + vendor + 'linear-gradient('+x+', '+a+' 0%, ' + b + ' 100%); '; | ||
| 3275 | }); | ||
| 3276 | } | ||
| 3277 | |||
| 3278 | function hueGradient(elem) { | ||
| 3279 | elem.style.background = ''; | ||
| 3280 | elem.style.cssText += 'background: -moz-linear-gradient(top, #ff0000 0%, #ff00ff 17%, #0000ff 34%, #00ffff 50%, #00ff00 67%, #ffff00 84%, #ff0000 100%);' | ||
| 3281 | elem.style.cssText += 'background: -webkit-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);' | ||
| 3282 | elem.style.cssText += 'background: -o-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);' | ||
| 3283 | elem.style.cssText += 'background: -ms-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);' | ||
| 3284 | elem.style.cssText += 'background: linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);' | ||
| 3285 | } | ||
| 3286 | |||
| 3287 | |||
| 3288 | return ColorController; | ||
| 3289 | |||
| 3290 | })(dat.controllers.Controller, | ||
| 3291 | dat.dom.dom, | ||
| 3292 | dat.color.Color = (function (interpret, math, toString, common) { | ||
| 3293 | |||
| 3294 | var Color = function() { | ||
| 3295 | |||
| 3296 | this.__state = interpret.apply(this, arguments); | ||
| 3297 | |||
| 3298 | if (this.__state === false) { | ||
| 3299 | throw 'Failed to interpret color arguments'; | ||
| 3300 | } | ||
| 3301 | |||
| 3302 | this.__state.a = this.__state.a || 1; | ||
| 3303 | |||
| 3304 | |||
| 3305 | }; | ||
| 3306 | |||
| 3307 | Color.COMPONENTS = ['r','g','b','h','s','v','hex','a']; | ||
| 3308 | |||
| 3309 | common.extend(Color.prototype, { | ||
| 3310 | |||
| 3311 | toString: function() { | ||
| 3312 | return toString(this); | ||
| 3313 | }, | ||
| 3314 | |||
| 3315 | toOriginal: function() { | ||
| 3316 | return this.__state.conversion.write(this); | ||
| 3317 | } | ||
| 3318 | |||
| 3319 | }); | ||
| 3320 | |||
| 3321 | defineRGBComponent(Color.prototype, 'r', 2); | ||
| 3322 | defineRGBComponent(Color.prototype, 'g', 1); | ||
| 3323 | defineRGBComponent(Color.prototype, 'b', 0); | ||
| 3324 | |||
| 3325 | defineHSVComponent(Color.prototype, 'h'); | ||
| 3326 | defineHSVComponent(Color.prototype, 's'); | ||
| 3327 | defineHSVComponent(Color.prototype, 'v'); | ||
| 3328 | |||
| 3329 | Object.defineProperty(Color.prototype, 'a', { | ||
| 3330 | |||
| 3331 | get: function() { | ||
| 3332 | return this.__state.a; | ||
| 3333 | }, | ||
| 3334 | |||
| 3335 | set: function(v) { | ||
| 3336 | this.__state.a = v; | ||
| 3337 | } | ||
| 3338 | |||
| 3339 | }); | ||
| 3340 | |||
| 3341 | Object.defineProperty(Color.prototype, 'hex', { | ||
| 3342 | |||
| 3343 | get: function() { | ||
| 3344 | |||
| 3345 | if (!this.__state.space !== 'HEX') { | ||
| 3346 | this.__state.hex = math.rgb_to_hex(this.r, this.g, this.b); | ||
| 3347 | } | ||
| 3348 | |||
| 3349 | return this.__state.hex; | ||
| 3350 | |||
| 3351 | }, | ||
| 3352 | |||
| 3353 | set: function(v) { | ||
| 3354 | |||
| 3355 | this.__state.space = 'HEX'; | ||
| 3356 | this.__state.hex = v; | ||
| 3357 | |||
| 3358 | } | ||
| 3359 | |||
| 3360 | }); | ||
| 3361 | |||
| 3362 | function defineRGBComponent(target, component, componentHexIndex) { | ||
| 3363 | |||
| 3364 | Object.defineProperty(target, component, { | ||
| 3365 | |||
| 3366 | get: function() { | ||
| 3367 | |||
| 3368 | if (this.__state.space === 'RGB') { | ||
| 3369 | return this.__state[component]; | ||
| 3370 | } | ||
| 3371 | |||
| 3372 | recalculateRGB(this, component, componentHexIndex); | ||
| 3373 | |||
| 3374 | return this.__state[component]; | ||
| 3375 | |||
| 3376 | }, | ||
| 3377 | |||
| 3378 | set: function(v) { | ||
| 3379 | |||
| 3380 | if (this.__state.space !== 'RGB') { | ||
| 3381 | recalculateRGB(this, component, componentHexIndex); | ||
| 3382 | this.__state.space = 'RGB'; | ||
| 3383 | } | ||
| 3384 | |||
| 3385 | this.__state[component] = v; | ||
| 3386 | |||
| 3387 | } | ||
| 3388 | |||
| 3389 | }); | ||
| 3390 | |||
| 3391 | } | ||
| 3392 | |||
| 3393 | function defineHSVComponent(target, component) { | ||
| 3394 | |||
| 3395 | Object.defineProperty(target, component, { | ||
| 3396 | |||
| 3397 | get: function() { | ||
| 3398 | |||
| 3399 | if (this.__state.space === 'HSV') | ||
| 3400 | return this.__state[component]; | ||
| 3401 | |||
| 3402 | recalculateHSV(this); | ||
| 3403 | |||
| 3404 | return this.__state[component]; | ||
| 3405 | |||
| 3406 | }, | ||
| 3407 | |||
| 3408 | set: function(v) { | ||
| 3409 | |||
| 3410 | if (this.__state.space !== 'HSV') { | ||
| 3411 | recalculateHSV(this); | ||
| 3412 | this.__state.space = 'HSV'; | ||
| 3413 | } | ||
| 3414 | |||
| 3415 | this.__state[component] = v; | ||
| 3416 | |||
| 3417 | } | ||
| 3418 | |||
| 3419 | }); | ||
| 3420 | |||
| 3421 | } | ||
| 3422 | |||
| 3423 | function recalculateRGB(color, component, componentHexIndex) { | ||
| 3424 | |||
| 3425 | if (color.__state.space === 'HEX') { | ||
| 3426 | |||
| 3427 | color.__state[component] = math.component_from_hex(color.__state.hex, componentHexIndex); | ||
| 3428 | |||
| 3429 | } else if (color.__state.space === 'HSV') { | ||
| 3430 | |||
| 3431 | common.extend(color.__state, math.hsv_to_rgb(color.__state.h, color.__state.s, color.__state.v)); | ||
| 3432 | |||
| 3433 | } else { | ||
| 3434 | |||
| 3435 | throw 'Corrupted color state'; | ||
| 3436 | |||
| 3437 | } | ||
| 3438 | |||
| 3439 | } | ||
| 3440 | |||
| 3441 | function recalculateHSV(color) { | ||
| 3442 | |||
| 3443 | var result = math.rgb_to_hsv(color.r, color.g, color.b); | ||
| 3444 | |||
| 3445 | common.extend(color.__state, | ||
| 3446 | { | ||
| 3447 | s: result.s, | ||
| 3448 | v: result.v | ||
| 3449 | } | ||
| 3450 | ); | ||
| 3451 | |||
| 3452 | if (!common.isNaN(result.h)) { | ||
| 3453 | color.__state.h = result.h; | ||
| 3454 | } else if (common.isUndefined(color.__state.h)) { | ||
| 3455 | color.__state.h = 0; | ||
| 3456 | } | ||
| 3457 | |||
| 3458 | } | ||
| 3459 | |||
| 3460 | return Color; | ||
| 3461 | |||
| 3462 | })(dat.color.interpret, | ||
| 3463 | dat.color.math = (function () { | ||
| 3464 | |||
| 3465 | var tmpComponent; | ||
| 3466 | |||
| 3467 | return { | ||
| 3468 | |||
| 3469 | hsv_to_rgb: function(h, s, v) { | ||
| 3470 | |||
| 3471 | var hi = Math.floor(h / 60) % 6; | ||
| 3472 | |||
| 3473 | var f = h / 60 - Math.floor(h / 60); | ||
| 3474 | var p = v * (1.0 - s); | ||
| 3475 | var q = v * (1.0 - (f * s)); | ||
| 3476 | var t = v * (1.0 - ((1.0 - f) * s)); | ||
| 3477 | var c = [ | ||
| 3478 | [v, t, p], | ||
| 3479 | [q, v, p], | ||
| 3480 | [p, v, t], | ||
| 3481 | [p, q, v], | ||
| 3482 | [t, p, v], | ||
| 3483 | [v, p, q] | ||
| 3484 | ][hi]; | ||
| 3485 | |||
| 3486 | return { | ||
| 3487 | r: c[0] * 255, | ||
| 3488 | g: c[1] * 255, | ||
| 3489 | b: c[2] * 255 | ||
| 3490 | }; | ||
| 3491 | |||
| 3492 | }, | ||
| 3493 | |||
| 3494 | rgb_to_hsv: function(r, g, b) { | ||
| 3495 | |||
| 3496 | var min = Math.min(r, g, b), | ||
| 3497 | max = Math.max(r, g, b), | ||
| 3498 | delta = max - min, | ||
| 3499 | h, s; | ||
| 3500 | |||
| 3501 | if (max != 0) { | ||
| 3502 | s = delta / max; | ||
| 3503 | } else { | ||
| 3504 | return { | ||
| 3505 | h: NaN, | ||
| 3506 | s: 0, | ||
| 3507 | v: 0 | ||
| 3508 | }; | ||
| 3509 | } | ||
| 3510 | |||
| 3511 | if (r == max) { | ||
| 3512 | h = (g - b) / delta; | ||
| 3513 | } else if (g == max) { | ||
| 3514 | h = 2 + (b - r) / delta; | ||
| 3515 | } else { | ||
| 3516 | h = 4 + (r - g) / delta; | ||
| 3517 | } | ||
| 3518 | h /= 6; | ||
| 3519 | if (h < 0) { | ||
| 3520 | h += 1; | ||
| 3521 | } | ||
| 3522 | |||
| 3523 | return { | ||
| 3524 | h: h * 360, | ||
| 3525 | s: s, | ||
| 3526 | v: max / 255 | ||
| 3527 | }; | ||
| 3528 | }, | ||
| 3529 | |||
| 3530 | rgb_to_hex: function(r, g, b) { | ||
| 3531 | var hex = this.hex_with_component(0, 2, r); | ||
| 3532 | hex = this.hex_with_component(hex, 1, g); | ||
| 3533 | hex = this.hex_with_component(hex, 0, b); | ||
| 3534 | return hex; | ||
| 3535 | }, | ||
| 3536 | |||
| 3537 | component_from_hex: function(hex, componentIndex) { | ||
| 3538 | return (hex >> (componentIndex * 8)) & 0xFF; | ||
| 3539 | }, | ||
| 3540 | |||
| 3541 | hex_with_component: function(hex, componentIndex, value) { | ||
| 3542 | return value << (tmpComponent = componentIndex * 8) | (hex & ~ (0xFF << tmpComponent)); | ||
| 3543 | } | ||
| 3544 | |||
| 3545 | } | ||
| 3546 | |||
| 3547 | })(), | ||
| 3548 | dat.color.toString, | ||
| 3549 | dat.utils.common), | ||
| 3550 | dat.color.interpret, | ||
| 3551 | dat.utils.common), | ||
| 3552 | dat.utils.requestAnimationFrame = (function () { | ||
| 3553 | |||
| 3554 | /** | ||
| 3555 | * requirejs version of Paul Irish's RequestAnimationFrame | ||
| 3556 | * http://paulirish.com/2011/requestanimationframe-for-smart-animating/ | ||
| 3557 | */ | ||
| 3558 | |||
| 3559 | return window.webkitRequestAnimationFrame || | ||
| 3560 | window.mozRequestAnimationFrame || | ||
| 3561 | window.oRequestAnimationFrame || | ||
| 3562 | window.msRequestAnimationFrame || | ||
| 3563 | function(callback, element) { | ||
| 3564 | |||
| 3565 | window.setTimeout(callback, 1000 / 60); | ||
| 3566 | |||
| 3567 | }; | ||
| 3568 | })(), | ||
| 3569 | dat.dom.CenteredDiv = (function (dom, common) { | ||
| 3570 | |||
| 3571 | |||
| 3572 | var CenteredDiv = function() { | ||
| 3573 | |||
| 3574 | this.backgroundElement = document.createElement('div'); | ||
| 3575 | common.extend(this.backgroundElement.style, { | ||
| 3576 | backgroundColor: 'rgba(0,0,0,0.8)', | ||
| 3577 | top: 0, | ||
| 3578 | left: 0, | ||
| 3579 | display: 'none', | ||
| 3580 | zIndex: '1000', | ||
| 3581 | opacity: 0, | ||
| 3582 | WebkitTransition: 'opacity 0.2s linear' | ||
| 3583 | }); | ||
| 3584 | |||
| 3585 | dom.makeFullscreen(this.backgroundElement); | ||
| 3586 | this.backgroundElement.style.position = 'fixed'; | ||
| 3587 | |||
| 3588 | this.domElement = document.createElement('div'); | ||
| 3589 | common.extend(this.domElement.style, { | ||
| 3590 | position: 'fixed', | ||
| 3591 | display: 'none', | ||
| 3592 | zIndex: '1001', | ||
| 3593 | opacity: 0, | ||
| 3594 | WebkitTransition: '-webkit-transform 0.2s ease-out, opacity 0.2s linear' | ||
| 3595 | }); | ||
| 3596 | |||
| 3597 | |||
| 3598 | document.body.appendChild(this.backgroundElement); | ||
| 3599 | document.body.appendChild(this.domElement); | ||
| 3600 | |||
| 3601 | var _this = this; | ||
| 3602 | dom.bind(this.backgroundElement, 'click', function() { | ||
| 3603 | _this.hide(); | ||
| 3604 | }); | ||
| 3605 | |||
| 3606 | |||
| 3607 | }; | ||
| 3608 | |||
| 3609 | CenteredDiv.prototype.show = function() { | ||
| 3610 | |||
| 3611 | var _this = this; | ||
| 3612 | |||
| 3613 | |||
| 3614 | |||
| 3615 | this.backgroundElement.style.display = 'block'; | ||
| 3616 | |||
| 3617 | this.domElement.style.display = 'block'; | ||
| 3618 | this.domElement.style.opacity = 0; | ||
| 3619 | // this.domElement.style.top = '52%'; | ||
| 3620 | this.domElement.style.webkitTransform = 'scale(1.1)'; | ||
| 3621 | |||
| 3622 | this.layout(); | ||
| 3623 | |||
| 3624 | common.defer(function() { | ||
| 3625 | _this.backgroundElement.style.opacity = 1; | ||
| 3626 | _this.domElement.style.opacity = 1; | ||
| 3627 | _this.domElement.style.webkitTransform = 'scale(1)'; | ||
| 3628 | }); | ||
| 3629 | |||
| 3630 | }; | ||
| 3631 | |||
| 3632 | CenteredDiv.prototype.hide = function() { | ||
| 3633 | |||
| 3634 | var _this = this; | ||
| 3635 | |||
| 3636 | var hide = function() { | ||
| 3637 | |||
| 3638 | _this.domElement.style.display = 'none'; | ||
| 3639 | _this.backgroundElement.style.display = 'none'; | ||
| 3640 | |||
| 3641 | dom.unbind(_this.domElement, 'webkitTransitionEnd', hide); | ||
| 3642 | dom.unbind(_this.domElement, 'transitionend', hide); | ||
| 3643 | dom.unbind(_this.domElement, 'oTransitionEnd', hide); | ||
| 3644 | |||
| 3645 | }; | ||
| 3646 | |||
| 3647 | dom.bind(this.domElement, 'webkitTransitionEnd', hide); | ||
| 3648 | dom.bind(this.domElement, 'transitionend', hide); | ||
| 3649 | dom.bind(this.domElement, 'oTransitionEnd', hide); | ||
| 3650 | |||
| 3651 | this.backgroundElement.style.opacity = 0; | ||
| 3652 | // this.domElement.style.top = '48%'; | ||
| 3653 | this.domElement.style.opacity = 0; | ||
| 3654 | this.domElement.style.webkitTransform = 'scale(1.1)'; | ||
| 3655 | |||
| 3656 | }; | ||
| 3657 | |||
| 3658 | CenteredDiv.prototype.layout = function() { | ||
| 3659 | this.domElement.style.left = window.innerWidth/2 - dom.getWidth(this.domElement) / 2 + 'px'; | ||
| 3660 | this.domElement.style.top = window.innerHeight/2 - dom.getHeight(this.domElement) / 2 + 'px'; | ||
| 3661 | }; | ||
| 3662 | |||
| 3663 | function lockScroll(e) { | ||
| 3664 | console.log(e); | ||
| 3665 | } | ||
| 3666 | |||
| 3667 | return CenteredDiv; | ||
| 3668 | |||
| 3669 | })(dat.dom.dom, | ||
| 3670 | dat.utils.common), | ||
| 3671 | dat.dom.dom, | ||
| 3672 | dat.utils.common); | ||
