gui.full.js 25 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051
  1. var GUI = function() {
  2. var _this = this;
  3. var MIN_WIDTH = 240;
  4. var MAX_WIDTH = 500;
  5. var controllers = [];
  6. var listening = [];
  7. var autoListen = true;
  8. var listenInterval;
  9. // Sum total of heights of controllers in this gui
  10. var controllerHeight;
  11. var curControllerContainerHeight = 0;
  12. var _this = this;
  13. var open = false;
  14. var width = 280;
  15. // Prevents checkForOverflow bug in which loaded gui appearance
  16. // settings are not respected by presence of scrollbar.
  17. var explicitOpenHeight = false;
  18. // How big we get when we open
  19. var openHeight;
  20. var name;
  21. var resizeTo = 0;
  22. var resizeTimeout;
  23. this.domElement = document.createElement('div');
  24. this.domElement.setAttribute('class', 'guidat');
  25. this.domElement.style.width = width+'px';
  26. var controllerContainer = document.createElement('div');
  27. controllerContainer.setAttribute('class', 'guidat-controllers');
  28. // Firefox hack to prevent horizontal scrolling
  29. controllerContainer.addEventListener('DOMMouseScroll', function(e) {
  30. var scrollAmount = this.scrollTop;
  31. if (e.wheelDelta) {
  32. scrollAmount+=e.wheelDelta;
  33. } else if (e.detail) {
  34. scrollAmount+=e.detail;
  35. }
  36. if (e.preventDefault) {
  37. e.preventDefault();
  38. }
  39. e.returnValue = false;
  40. controllerContainer.scrollTop = scrollAmount;
  41. }, false);
  42. controllerContainer.style.height = '0px';
  43. var toggleButton = document.createElement('a');
  44. toggleButton.setAttribute('class', 'guidat-toggle');
  45. toggleButton.setAttribute('href', '#');
  46. toggleButton.innerHTML = "Show Controls";
  47. var toggleDragged = false;
  48. var dragDisplacementY = 0;
  49. var togglePressed = false;
  50. var my, pmy, mx, pmx;
  51. var resize = function(e) {
  52. pmy = my;
  53. pmx = mx;
  54. my = e.pageY;
  55. mx = e.pageX;
  56. var dmy = my - pmy;
  57. if (!open) {
  58. if (dmy > 0) {
  59. open = true;
  60. curControllerContainerHeight = openHeight = 1;
  61. toggleButton.innerHTML = name || "Hide Controls";
  62. } else {
  63. return;
  64. }
  65. }
  66. // TODO: Flip this if you want to resize to the left.
  67. var dmx = pmx - mx;
  68. if (dmy > 0 &&
  69. curControllerContainerHeight > controllerHeight) {
  70. var d = GUI.map(curControllerContainerHeight, controllerHeight, controllerHeight + 100, 1, 0);
  71. dmy *= d;
  72. }
  73. toggleDragged = true;
  74. dragDisplacementY += dmy;
  75. dragDisplacementX += dmx;
  76. openHeight += dmy;
  77. width += dmx;
  78. curControllerContainerHeight += dmy;
  79. controllerContainer.style.height = openHeight+'px';
  80. width = GUI.constrain(width, MIN_WIDTH, MAX_WIDTH);
  81. _this.domElement.style.width = width+'px';
  82. checkForOverflow();
  83. };
  84. toggleButton.addEventListener('mousedown', function(e) {
  85. pmy = my = e.pageY;
  86. pmx = mx = e.pageX;
  87. togglePressed = true;
  88. e.preventDefault();
  89. dragDisplacementY = 0;
  90. dragDisplacementX = 0;
  91. document.addEventListener('mousemove', resize, false);
  92. return false;
  93. }, false);
  94. toggleButton.addEventListener('click', function(e) {
  95. e.preventDefault();
  96. return false;
  97. }, false);
  98. document.addEventListener('mouseup', function(e) {
  99. if (togglePressed && !toggleDragged) {
  100. _this.toggle();
  101. // Clears lingering slider column
  102. _this.domElement.style.width = (width+1)+'px';
  103. setTimeout(function() {
  104. _this.domElement.style.width = width+'px';
  105. }, 1);
  106. }
  107. if (togglePressed && toggleDragged) {
  108. if (dragDisplacementX == 0) {
  109. // Clears lingering slider column
  110. _this.domElement.style.width = (width+1)+'px';
  111. setTimeout(function() {
  112. _this.domElement.style.width = width+'px';
  113. }, 1);
  114. }
  115. if (openHeight > controllerHeight) {
  116. clearTimeout(resizeTimeout);
  117. openHeight = resizeTo = controllerHeight;
  118. beginResize();
  119. } else if (controllerContainer.children.length >= 1) {
  120. var singleControllerHeight = controllerContainer.children[0].offsetHeight;
  121. clearTimeout(resizeTimeout);
  122. var target = Math.round(curControllerContainerHeight/singleControllerHeight)*singleControllerHeight-1;
  123. resizeTo = target;
  124. if (resizeTo <= 0) {
  125. _this.hide();
  126. openHeight = singleControllerHeight*2;
  127. } else {
  128. openHeight = resizeTo;
  129. beginResize();
  130. }
  131. }
  132. }
  133. document.removeEventListener('mousemove', resize, false);
  134. e.preventDefault();
  135. toggleDragged = false;
  136. togglePressed = false;
  137. return false;
  138. }, false);
  139. this.domElement.appendChild(controllerContainer);
  140. this.domElement.appendChild(toggleButton);
  141. if (GUI.autoPlace) {
  142. if(GUI.autoPlaceContainer == null) {
  143. GUI.autoPlaceContainer = document.createElement('div');
  144. GUI.autoPlaceContainer.setAttribute("id", "guidat");
  145. document.body.appendChild(GUI.autoPlaceContainer);
  146. }
  147. GUI.autoPlaceContainer.appendChild(this.domElement);
  148. }
  149. this.autoListenIntervalTime = 1000/60;
  150. var createListenInterval = function() {
  151. listenInterval = setInterval(function() {
  152. _this.listen();
  153. }, this.autoListenIntervalTime);
  154. }
  155. this.__defineSetter__("autoListen", function(v) {
  156. autoListen = v;
  157. if (!autoListen) {
  158. clearInterval(listenInterval);
  159. } else {
  160. if (listening.length > 0) createListenInterval();
  161. }
  162. });
  163. this.__defineGetter__("autoListen", function(v) {
  164. return autoListen;
  165. });
  166. this.listenTo = function(controller) {
  167. // TODO: check for duplicates
  168. if (listening.length == 0) {
  169. createListenInterval();
  170. }
  171. listening.push(controller);
  172. };
  173. this.unlistenTo = function(controller) {
  174. // TODO: test this
  175. for(var i = 0; i < listening.length; i++) {
  176. if(listening[i] == controller) listening.splice(i, 1);
  177. }
  178. if(listening.length <= 0) {
  179. clearInterval(listenInterval);
  180. }
  181. };
  182. this.listen = function(whoToListenTo) {
  183. var arr = whoToListenTo || listening;
  184. for (var i in arr) {
  185. arr[i].updateDisplay();
  186. }
  187. };
  188. this.listenAll = function() {
  189. this.listen(controllers);
  190. }
  191. this.autoListen = true;
  192. var alreadyControlled = function(object, propertyName) {
  193. for (var i in controllers) {
  194. if (controllers[i].object == object &&
  195. controllers[i].propertyName == propertyName) {
  196. return true;
  197. }
  198. }
  199. return false;
  200. };
  201. var construct = function(constructor, args) {
  202. function F() {
  203. return constructor.apply(this, args);
  204. }
  205. F.prototype = constructor.prototype;
  206. return new F();
  207. };
  208. this.add = function() {
  209. var object = arguments[0];
  210. var propertyName = arguments[1];
  211. // Have we already added this?
  212. if (alreadyControlled(object, propertyName)) {
  213. // GUI.error("Controller for \"" + propertyName+"\" already added.");
  214. // return;
  215. }
  216. var value = object[propertyName];
  217. // Does this value exist? Is it accessible?
  218. if (value == undefined) {
  219. GUI.error(object + " either has no property \""+propertyName+"\", or the property is inaccessible.");
  220. return;
  221. }
  222. var type = typeof value;
  223. var handler = handlerTypes[type];
  224. // Do we know how to deal with this data type?
  225. if (handler == undefined) {
  226. GUI.error("Cannot create controller for data type \""+type+"\"");
  227. return;
  228. }
  229. var args = [this]; // Set first arg (parent) to this
  230. for (var j = 0; j < arguments.length; j++) {
  231. args.push(arguments[j]);
  232. }
  233. var controllerObject = construct(handler, args);
  234. // Were we able to make the controller?
  235. if (!controllerObject) {
  236. GUI.error("Error creating controller for \""+propertyName+"\".");
  237. return;
  238. }
  239. // Success.
  240. controllerContainer.appendChild(controllerObject.domElement);
  241. controllers.push(controllerObject);
  242. GUI.allControllers.push(controllerObject);
  243. // Do we have a saved value for this controller?
  244. if (type != "function" &&
  245. GUI.saveIndex < GUI.savedValues.length) {
  246. controllerObject.setValue(GUI.savedValues[GUI.saveIndex]);
  247. GUI.saveIndex++;
  248. }
  249. // Compute sum height of controllers.
  250. checkForOverflow();
  251. // Prevents checkForOverflow bug in which loaded gui appearance
  252. // settings are not respected by presence of scrollbar.
  253. if (!explicitOpenHeight) {
  254. openHeight = controllerHeight;
  255. }
  256. return controllerObject;
  257. }
  258. var checkForOverflow = function() {
  259. controllerHeight = 0;
  260. for (var i in controllers) {
  261. controllerHeight += controllers[i].domElement.offsetHeight;
  262. }
  263. if (controllerHeight - 1 > openHeight) {
  264. controllerContainer.style.overflowY = "auto";
  265. } else {
  266. controllerContainer.style.overflowY = "hidden";
  267. }
  268. }
  269. var handlerTypes = {
  270. "number": GUI.NumberController,
  271. "string": GUI.StringController,
  272. "boolean": GUI.BooleanController,
  273. "function": GUI.FunctionController
  274. };
  275. var alreadyControlled = function(object, propertyName) {
  276. for (var i in controllers) {
  277. if (controllers[i].object == object &&
  278. controllers[i].propertyName == propertyName) {
  279. return true;
  280. }
  281. }
  282. return false;
  283. };
  284. var construct = function(constructor, args) {
  285. function F() {
  286. return constructor.apply(this, args);
  287. }
  288. F.prototype = constructor.prototype;
  289. return new F();
  290. };
  291. this.reset = function() {
  292. // TODO
  293. }
  294. // GUI ... GUI
  295. this.toggle = function() {
  296. open ? this.hide() : this.show();
  297. };
  298. this.show = function() {
  299. toggleButton.innerHTML = name || "Hide Controls";
  300. resizeTo = openHeight;
  301. clearTimeout(resizeTimeout);
  302. beginResize();
  303. open = true;
  304. }
  305. this.hide = function() {
  306. toggleButton.innerHTML = name || "Show Controls";
  307. resizeTo = 0;
  308. clearTimeout(resizeTimeout);
  309. beginResize();
  310. open = false;
  311. }
  312. this.name = function(n) {
  313. name = n;
  314. toggleButton.innerHTML = n;
  315. }
  316. // used in saveURL
  317. this.appearanceVars = function() {
  318. return [open, width, openHeight, controllerContainer.scrollTop]
  319. }
  320. var beginResize = function() {
  321. //console.log("Resizing from " + curControllerContainerHeight + " to " + resizeTo);
  322. curControllerContainerHeight += (resizeTo - curControllerContainerHeight)*0.6;
  323. if (Math.abs(curControllerContainerHeight-resizeTo) < 1) {
  324. curControllerContainerHeight = resizeTo;
  325. } else {
  326. resizeTimeout = setTimeout(beginResize, 1000/30);
  327. }
  328. controllerContainer.style.height = Math.round(curControllerContainerHeight)+'px';
  329. checkForOverflow();
  330. }
  331. // Load saved appearance:
  332. if (GUI.guiIndex < GUI.savedAppearanceVars.length) {
  333. width = parseInt(GUI.savedAppearanceVars[GUI.guiIndex][1]);
  334. _this.domElement.style.width = width+"px";
  335. openHeight = parseInt(GUI.savedAppearanceVars[GUI.guiIndex][2]);
  336. explicitOpenHeight = true;
  337. if (eval(GUI.savedAppearanceVars[GUI.guiIndex][0]) == true) {
  338. curControllerContainerHeight = openHeight;
  339. var t = GUI.savedAppearanceVars[GUI.guiIndex][3]
  340. // Hack.
  341. setTimeout(function() {
  342. controllerContainer.scrollTop = t;
  343. }, 0);
  344. if (GUI.scrollTop > -1) {
  345. document.body.scrollTop = GUI.scrollTop;
  346. }
  347. resizeTo = openHeight;
  348. this.show();
  349. }
  350. GUI.guiIndex++;
  351. }
  352. GUI.allGuis.push(this);
  353. };
  354. // Static members
  355. GUI.autoPlace = true;
  356. GUI.autoPlaceContainer = null;
  357. GUI.allControllers = [];
  358. GUI.allGuis = [];
  359. GUI.saveURL = function() {
  360. title = window.location;
  361. url = GUI.replaceGetVar("saveString", GUI.getSaveString());
  362. window.location = url;
  363. };
  364. GUI.scrollTop = -1;
  365. GUI.load = function(saveString) {
  366. //GUI.savedAppearanceVars = [];
  367. var vals = saveString.split(",");
  368. var numGuis = parseInt(vals[0]);
  369. GUI.scrollTop = parseInt(vals[1]);
  370. for (var i = 0; i < numGuis; i++) {
  371. var appr = vals.splice(2, 4);
  372. GUI.savedAppearanceVars.push(appr);
  373. }
  374. GUI.savedValues = vals.splice(2, vals.length);
  375. };
  376. GUI.savedValues = [];
  377. GUI.savedAppearanceVars = [];
  378. GUI.getSaveString = function() {
  379. var vals = [];
  380. vals.push(GUI.allGuis.length);
  381. vals.push(document.body.scrollTop);
  382. for (var i in GUI.allGuis) {
  383. var av = GUI.allGuis[i].appearanceVars();
  384. for (var j = 0; j < av.length; j++) {
  385. vals.push(av[j]);
  386. }
  387. }
  388. for (var i in GUI.allControllers) {
  389. // We don't save values for functions.
  390. if (GUI.allControllers[i].type == "function") {
  391. continue;
  392. }
  393. var v = GUI.allControllers[i].getValue();
  394. // Round numbers so they don't get enormous
  395. if (GUI.allControllers[i].type == "number") {
  396. v = GUI.roundToDecimal(v, 4);
  397. }
  398. vals.push(v);
  399. }
  400. return vals.join(',');
  401. }
  402. GUI.getVarFromURL = function(v) {
  403. var vars = [], hash;
  404. var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
  405. for (var i = 0; i < hashes.length; i++) {
  406. hash = hashes[i].split("=")
  407. if (hash == undefined) continue;
  408. if (hash[0] == v) {
  409. return hash[1];
  410. }
  411. }
  412. return null;
  413. };
  414. GUI.replaceGetVar = function(varName, val) {
  415. var vars = [], hash;
  416. var loc = window.location.href;
  417. var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
  418. for (var i = 0; i < hashes.length; i++) {
  419. hash = hashes[i].split("=")
  420. if (hash == undefined) continue;
  421. if (hash[0] == varName) {
  422. return loc.replace(hash[1], val);
  423. }
  424. }
  425. if (window.location.href.indexOf('?') != -1) {
  426. return loc + "&"+varName+"="+val;
  427. }
  428. return loc+"?"+varName+"="+val;
  429. };
  430. GUI.saveIndex = 0;
  431. GUI.guiIndex = 0;
  432. GUI.showSaveString = function() {
  433. alert(GUI.getSaveString());
  434. }
  435. // Util functions
  436. GUI.makeUnselectable = function(elem) {
  437. elem.onselectstart = function() { return false; };
  438. elem.style.MozUserSelect = "none";
  439. elem.style.KhtmlUserSelect = "none";
  440. elem.unselectable = "on";
  441. }
  442. GUI.makeSelectable = function(elem) {
  443. elem.onselectstart = function() { };
  444. elem.style.MozUserSelect = "auto";
  445. elem.style.KhtmlUserSelect = "auto";
  446. elem.unselectable = "off";
  447. }
  448. GUI.map = function(v, i1, i2, o1, o2) {
  449. var v = o1 + (o2 - o1) * ((v - i1) / (i2 - i1));
  450. return v;
  451. }
  452. GUI.constrain = function (v, o1, o2) {
  453. if (v < o1) v = o1;
  454. else if (v > o2) v = o2;
  455. return v;
  456. }
  457. GUI.error = function(str) {
  458. if (typeof console.error == 'function') {
  459. console.error("[GUI ERROR] " + str);
  460. }
  461. };
  462. GUI.roundToDecimal = function(n, decimals) {
  463. var t = Math.pow(10, decimals);
  464. return Math.round(n*t)/t;
  465. }
  466. GUI.extendController = function(clazz) {
  467. clazz.prototype = new GUI.Controller();
  468. clazz.prototype.constructor = clazz;
  469. }
  470. if (GUI.getVarFromURL('saveString') != null) GUI.load(GUI.getVarFromURL('saveString'));GUI.Slider = function(numberController, min, max, step, initValue) {
  471. var min = min;
  472. var max = max;
  473. var step = step;
  474. var clicked = false;
  475. var _this = this;
  476. var x, px;
  477. this.domElement = document.createElement('div');
  478. this.domElement.setAttribute('class', 'guidat-slider-bg');
  479. this.fg = document.createElement('div');
  480. this.fg.setAttribute('class', 'guidat-slider-fg');
  481. this.domElement.appendChild(this.fg);
  482. var findPos = function(obj) {
  483. var curleft = curtop = 0;
  484. if (obj.offsetParent) {
  485. do {
  486. curleft += obj.offsetLeft;
  487. curtop += obj.offsetTop;
  488. } while (obj = obj.offsetParent);
  489. return [curleft,curtop];
  490. }
  491. }
  492. this.__defineSetter__('value', function(e) {
  493. var pct = GUI.map(e, min, max, 0, 100);
  494. this.fg.style.width = pct+"%";
  495. });
  496. var onDrag = function(e) {
  497. if (!clicked) return;
  498. var pos = findPos(_this.domElement);
  499. var val = GUI.map(e.pageX, pos[0], pos[0] + _this.domElement.offsetWidth, min, max);
  500. val = Math.round(val/step)*step;
  501. numberController.setValue(val);
  502. }
  503. this.domElement.addEventListener('mousedown', function(e) {
  504. clicked = true;
  505. x = px = e.pageX;
  506. _this.domElement.setAttribute('class', 'guidat-slider-bg active');
  507. _this.fg.setAttribute('class', 'guidat-slider-fg active');
  508. onDrag(e);
  509. document.addEventListener('mouseup', mouseup, false);
  510. }, false);
  511. var mouseup = function(e) {
  512. _this.domElement.setAttribute('class', 'guidat-slider-bg');
  513. _this.fg.setAttribute('class', 'guidat-slider-fg');
  514. clicked = false;
  515. if (numberController.finishChangeFunction != null) {
  516. numberController.finishChangeFunction.call(this, numberController.getValue());
  517. }
  518. document.removeEventListener('mouseup', mouseup, false);
  519. };
  520. document.addEventListener('mousemove', onDrag, false);
  521. this.value = initValue;
  522. }
  523. GUI.Controller = function() {
  524. this.parent = arguments[0];
  525. this.object = arguments[1];
  526. this.propertyName = arguments[2];
  527. if (arguments.length > 0) this.initialValue = this.propertyName[this.object];
  528. this.domElement = document.createElement('div');
  529. this.domElement.setAttribute('class', 'guidat-controller ' + this.type);
  530. this.propertyNameElement = document.createElement('span');
  531. this.propertyNameElement.setAttribute('class', 'guidat-propertyname');
  532. this.name(this.propertyName);
  533. this.domElement.appendChild(this.propertyNameElement);
  534. GUI.makeUnselectable(this.domElement);
  535. };
  536. GUI.Controller.prototype.changeFunction = null;
  537. GUI.Controller.prototype.finishChangeFunction = null;
  538. GUI.Controller.prototype.name = function(n) {
  539. this.propertyNameElement.innerHTML = n;
  540. return this;
  541. };
  542. GUI.Controller.prototype.reset = function() {
  543. this.setValue(this.initialValue);
  544. return this;
  545. };
  546. GUI.Controller.prototype.listen = function() {
  547. this.parent.listenTo(this);
  548. return this;
  549. }
  550. GUI.Controller.prototype.unlisten = function() {
  551. this.parent.unlistenTo(this); // <--- hasn't been tested yet
  552. return this;
  553. }
  554. GUI.Controller.prototype.setValue = function(n) {
  555. this.object[this.propertyName] = n;
  556. if (this.changeFunction != null) {
  557. this.changeFunction.call(this, n);
  558. }
  559. this.updateDisplay();
  560. return this;
  561. }
  562. GUI.Controller.prototype.getValue = function() {
  563. return this.object[this.propertyName];
  564. }
  565. GUI.Controller.prototype.updateDisplay = function() {}
  566. GUI.Controller.prototype.onChange = function(fnc) {
  567. this.changeFunction = fnc;
  568. return this;
  569. }
  570. GUI.Controller.prototype.onFinishChange = function(fnc) {
  571. this.finishChangeFunction = fnc;
  572. return this;
  573. }
  574. GUI.Controller.prototype.options = function() {
  575. var _this = this;
  576. var select = document.createElement('select');
  577. if (arguments.length == 1) {
  578. var arr = arguments[0];
  579. for (var i in arr) {
  580. var opt = document.createElement('option');
  581. opt.innerHTML = i;
  582. opt.setAttribute('value', arr[i]);
  583. select.appendChild(opt);
  584. }
  585. } else {
  586. for (var i = 0; i < arguments.length; i++) {
  587. var opt = document.createElement('option');
  588. opt.innerHTML = arguments[i];
  589. opt.setAttribute('value', arguments[i]);
  590. select.appendChild(opt);
  591. }
  592. }
  593. select.addEventListener('change', function() {
  594. _this.setValue(this.value);
  595. if (_this.finishChangeFunction != null) {
  596. _this.finishChangeFunction.call(this, _this.getValue());
  597. }
  598. }, false);
  599. _this.domElement.appendChild(select);
  600. return this;
  601. }
  602. GUI.BooleanController = function() {
  603. this.type = "boolean";
  604. GUI.Controller.apply(this, arguments);
  605. var _this = this;
  606. var input = document.createElement('input');
  607. input.setAttribute('type', 'checkbox');
  608. this.domElement.addEventListener('click', function(e) {
  609. input.checked = !input.checked;
  610. e.preventDefault();
  611. _this.setValue(input.checked);
  612. }, false);
  613. input.addEventListener('mouseup', function(e) {
  614. input.checked = !input.checked; // counteracts default.
  615. }, false);
  616. this.domElement.style.cursor = "pointer";
  617. this.propertyNameElement.style.cursor = "pointer";
  618. this.domElement.appendChild(input);
  619. this.updateDisplay = function() {
  620. input.checked = _this.getValue();
  621. };
  622. this.setValue = function(val) {
  623. if (typeof val != "boolean") {
  624. try {
  625. val = eval(val);
  626. } catch (e) {}
  627. }
  628. return GUI.Controller.prototype.setValue.call(this, val);
  629. }
  630. };
  631. GUI.extendController(GUI.BooleanController);GUI.FunctionController = function() {
  632. this.type = "function";
  633. var _this = this;
  634. GUI.Controller.apply(this, arguments);
  635. this.domElement.addEventListener('click', function() {
  636. _this.fire();
  637. }, false);
  638. this.domElement.style.cursor = "pointer";
  639. this.propertyNameElement.style.cursor = "pointer";
  640. var fireFunction = null;
  641. this.onFire = function(fnc) {
  642. fireFunction = fnc;
  643. return this;
  644. }
  645. this.fire = function() {
  646. if (fireFunction != null) {
  647. fireFunction.call(this);
  648. }
  649. _this.object[_this.propertyName].call(_this.object);
  650. };
  651. };
  652. GUI.extendController(GUI.FunctionController);
  653. GUI.NumberController = function() {
  654. this.type = "number";
  655. GUI.Controller.apply(this, arguments);
  656. var _this = this;
  657. // If we simply click and release a number field, we want to highlight it.
  658. // This variable keeps track of whether or not we've dragged
  659. var draggedNumberField = false;
  660. var clickedNumberField = false;
  661. var y = py = 0;
  662. var min = arguments[3];
  663. var max = arguments[4];
  664. var step = arguments[5];
  665. if (!step) {
  666. if (min != undefined && max != undefined) {
  667. step = (max-min)*0.01;
  668. } else {
  669. step = 1;
  670. }
  671. }
  672. var numberField = document.createElement('input');
  673. numberField.setAttribute('id', this.propertyName);
  674. numberField.setAttribute('type', 'text');
  675. numberField.setAttribute('value', this.getValue());
  676. if (step) numberField.setAttribute('step', step);
  677. this.domElement.appendChild(numberField);
  678. var slider;
  679. if (min != undefined && max != undefined) {
  680. slider = new GUI.Slider(this, min, max, step, this.getValue());
  681. this.domElement.appendChild(slider.domElement);
  682. }
  683. numberField.addEventListener('blur', function(e) {
  684. var val = parseFloat(this.value);
  685. console.log(val);
  686. if (!isNaN(val)) {
  687. _this.setValue(val);
  688. }
  689. }, false);
  690. numberField.addEventListener('mousewheel', function(e) {
  691. e.preventDefault();
  692. _this.setValue(_this.getValue() + Math.abs(e.wheelDeltaY)/e.wheelDeltaY*step);
  693. return false;
  694. }, false);
  695. numberField.addEventListener('mousedown', function(e) {
  696. py = y = e.pageY;
  697. clickedNumberField = true;
  698. document.addEventListener('mousemove', dragNumberField, false);
  699. document.addEventListener('mouseup', mouseup, false);
  700. }, false);
  701. // Handle up arrow and down arrow
  702. numberField.addEventListener('keydown', function(e) {
  703. switch(e.keyCode) {
  704. case 38: // up
  705. var newVal = _this.getValue() + step;
  706. _this.setValue(newVal);
  707. break;
  708. case 40: // down
  709. var newVal = _this.getValue() - step;
  710. _this.setValue(newVal);
  711. break;
  712. }
  713. }, false);
  714. var mouseup = function(e) {
  715. document.removeEventListener('mousemove', dragNumberField, false);
  716. GUI.makeSelectable(_this.parent.domElement);
  717. GUI.makeSelectable(numberField);
  718. if (clickedNumberField && !draggedNumberField) {
  719. numberField.focus();
  720. numberField.select();
  721. }
  722. draggedNumberField = false;
  723. clickedNumberField = false;
  724. if (_this.finishChangeFunction != null) {
  725. _this.finishChangeFunction.call(this, _this.getValue());
  726. }
  727. document.removeEventListener('mouseup', mouseup, false);
  728. }
  729. var dragNumberField = function(e) {
  730. draggedNumberField = true;
  731. e.preventDefault();
  732. // We don't want to be highlighting this field as we scroll.
  733. // Or any other fields in this gui for that matter ...
  734. // TODO: Make makeUselectable go through each element and child element.
  735. GUI.makeUnselectable(_this.parent.domElement);
  736. GUI.makeUnselectable(numberField);
  737. py = y;
  738. y = e.pageY;
  739. var dy = py - y;
  740. var newVal = _this.getValue() + dy*step;
  741. _this.setValue(newVal);
  742. return false;
  743. }
  744. this.options = function() {
  745. _this.noSlider();
  746. _this.domElement.removeChild(numberField);
  747. return GUI.Controller.prototype.options.apply(this, arguments);
  748. };
  749. this.noSlider = function() {
  750. if (slider) {
  751. _this.domElement.removeChild(slider.domElement);
  752. }
  753. return this;
  754. };
  755. this.setValue = function(val) {
  756. val = parseFloat(val);
  757. if (min != undefined && val <= min) {
  758. val = min;
  759. } else if (max != undefined && val >= max) {
  760. val = max;
  761. }
  762. return GUI.Controller.prototype.setValue.call(this, val);
  763. }
  764. this.updateDisplay = function() {
  765. numberField.value = GUI.roundToDecimal(_this.getValue(), 4);
  766. if (slider) slider.value = _this.getValue();
  767. }
  768. };
  769. GUI.extendController(GUI.NumberController);
  770. GUI.StringController = function() {
  771. this.type = "string";
  772. var _this = this;
  773. GUI.Controller.apply(this, arguments);
  774. var input = document.createElement('input');
  775. var initialValue = this.getValue();
  776. input.setAttribute('value', initialValue);
  777. input.setAttribute('spellcheck', 'false');
  778. this.domElement.addEventListener('mouseup', function() {
  779. input.focus();
  780. input.select();
  781. }, false);
  782. // TODO: getting messed up on ctrl a
  783. input.addEventListener('keyup', function(e) {
  784. if (e.keyCode == 13 && _this.finishChangeFunction != null) {
  785. _this.finishChangeFunction.call(this, _this.getValue());
  786. }
  787. _this.setValue(input.value);
  788. }, false);
  789. input.addEventListener('blur', function() {
  790. if (_this.finishChangeFunction != null) {
  791. _this.finishChangeFunction.call(this, _this.getValue());
  792. }
  793. }, false);
  794. this.updateDisplay = function() {
  795. input.value = _this.getValue();
  796. }
  797. this.options = function() {
  798. _this.domElement.removeChild(input);
  799. return GUI.Controller.prototype.options.apply(this, arguments);
  800. };
  801. this.domElement.appendChild(input);
  802. };
  803. GUI.extendController(GUI.StringController);