UI.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806
  1. var UI = {};
  2. UI.Element = function () {};
  3. UI.Element.prototype = {
  4. setClass: function ( name ) {
  5. this.dom.className = name;
  6. return this;
  7. },
  8. setStyle: function ( style, array ) {
  9. for ( var i = 0; i < array.length; i ++ ) {
  10. this.dom.style[ style ] = array[ i ];
  11. }
  12. },
  13. setTextContent: function ( value ) {
  14. this.dom.textContent = value;
  15. return this;
  16. }
  17. }
  18. // properties
  19. var properties = [ 'position', 'left', 'top', 'right', 'bottom', 'width', 'height', 'border', 'borderLeft',
  20. 'borderTop', 'borderRight', 'borderBottom', 'margin', 'marginLeft', 'marginTop', 'marginRight',
  21. 'marginBottom', 'padding', 'paddingLeft', 'paddingTop', 'paddingRight', 'paddingBottom', 'color',
  22. 'backgroundColor', 'fontSize', 'fontWeight', 'display', 'overflow', 'cursor' ];
  23. properties.forEach( function ( property ) {
  24. var method = 'set' + property.substr( 0, 1 ).toUpperCase() + property.substr( 1, property.length );
  25. UI.Element.prototype[ method ] = function () {
  26. this.setStyle( property, arguments );
  27. return this;
  28. };
  29. } );
  30. // events
  31. var events = [ 'MouseOver', 'MouseOut', 'Click' ];
  32. events.forEach( function ( event ) {
  33. var method = 'on' + event;
  34. UI.Element.prototype[ method ] = function ( callback ) {
  35. this.dom.addEventListener( event.toLowerCase(), callback, false );
  36. return this;
  37. };
  38. } );
  39. // Panel
  40. UI.Panel = function () {
  41. UI.Element.call( this );
  42. var dom = document.createElement( 'div' );
  43. dom.className = 'Panel';
  44. dom.style.userSelect = 'none';
  45. dom.style.WebkitUserSelect = 'none';
  46. dom.style.MozUserSelect = 'none';
  47. this.dom = dom;
  48. return this;
  49. };
  50. UI.Panel.prototype = Object.create( UI.Element.prototype );
  51. UI.Panel.prototype.add = function () {
  52. for ( var i = 0; i < arguments.length; i ++ ) {
  53. this.dom.appendChild( arguments[ i ].dom );
  54. }
  55. return this;
  56. };
  57. // Text
  58. UI.Text = function () {
  59. UI.Element.call( this );
  60. var dom = document.createElement( 'span' );
  61. dom.className = 'Text';
  62. dom.style.cursor = 'default';
  63. dom.style.display = 'inline-block';
  64. dom.style.verticalAlign = 'top';
  65. this.dom = dom;
  66. return this;
  67. };
  68. UI.Text.prototype = Object.create( UI.Element.prototype );
  69. UI.Text.prototype.setValue = function ( value ) {
  70. this.dom.textContent = value;
  71. return this;
  72. };
  73. // Input
  74. UI.Input = function () {
  75. UI.Element.call( this );
  76. var scope = this;
  77. var dom = document.createElement( 'input' );
  78. dom.className = 'Input';
  79. dom.style.padding = '2px';
  80. dom.style.marginTop = '-2px';
  81. dom.style.marginLeft = '-2px';
  82. dom.style.border = '1px solid #ccc';
  83. this.dom = dom;
  84. this.onChangeCallback = null;
  85. this.dom.addEventListener( 'change', function ( event ) {
  86. if ( scope.onChangeCallback ) scope.onChangeCallback();
  87. }, false );
  88. return this;
  89. };
  90. UI.Input.prototype = Object.create( UI.Element.prototype );
  91. UI.Input.prototype.getValue = function () {
  92. return this.dom.value;
  93. };
  94. UI.Input.prototype.setValue = function ( value ) {
  95. this.dom.value = value;
  96. return this;
  97. };
  98. UI.Input.prototype.onChange = function ( callback ) {
  99. this.onChangeCallback = callback;
  100. return this;
  101. };
  102. // TextArea
  103. UI.TextArea = function () {
  104. UI.Element.call( this );
  105. var scope = this;
  106. var dom = document.createElement( 'textarea' );
  107. dom.className = 'TextArea';
  108. dom.style.padding = '2px';
  109. dom.style.marginTop = '-2px';
  110. dom.style.marginLeft = '-2px';
  111. dom.style.border = '1px solid #ccc';
  112. this.dom = dom;
  113. this.onChangeCallback = null;
  114. this.dom.addEventListener( 'change', function ( event ) {
  115. if ( scope.onChangeCallback ) scope.onChangeCallback();
  116. }, false );
  117. return this;
  118. };
  119. UI.TextArea.prototype = Object.create( UI.Element.prototype );
  120. UI.TextArea.prototype.getValue = function () {
  121. return this.dom.value;
  122. };
  123. UI.TextArea.prototype.setValue = function ( value ) {
  124. this.dom.value = value;
  125. return this;
  126. };
  127. UI.TextArea.prototype.onChange = function ( callback ) {
  128. this.onChangeCallback = callback;
  129. return this;
  130. };
  131. // Select
  132. UI.Select = function () {
  133. UI.Element.call( this );
  134. var scope = this;
  135. var dom = document.createElement( 'select' );
  136. dom.className = 'Select';
  137. dom.style.width = '64px';
  138. dom.style.height = '16px';
  139. dom.style.border = '0px';
  140. dom.style.padding = '0px';
  141. this.dom = dom;
  142. this.onChangeCallback = null;
  143. this.dom.addEventListener( 'change', function ( event ) {
  144. if ( scope.onChangeCallback ) scope.onChangeCallback();
  145. }, false );
  146. return this;
  147. };
  148. UI.Select.prototype = Object.create( UI.Element.prototype );
  149. UI.Select.prototype.setMultiple = function ( boolean ) {
  150. this.dom.multiple = boolean;
  151. return this;
  152. };
  153. UI.Select.prototype.setOptions = function ( options ) {
  154. while ( this.dom.children.length > 0 ) {
  155. this.dom.removeChild( this.dom.firstChild );
  156. }
  157. for ( var key in options ) {
  158. var option = document.createElement( 'option' );
  159. option.value = key;
  160. option.innerHTML = options[ key ];
  161. this.dom.appendChild( option );
  162. }
  163. return this;
  164. };
  165. UI.Select.prototype.getValue = function () {
  166. return this.dom.value;
  167. };
  168. UI.Select.prototype.setValue = function ( value ) {
  169. this.dom.value = value;
  170. return this;
  171. };
  172. UI.Select.prototype.onChange = function ( callback ) {
  173. this.onChangeCallback = callback;
  174. return this;
  175. };
  176. // FancySelect
  177. UI.FancySelect = function () {
  178. UI.Element.call( this );
  179. var scope = this;
  180. var dom = document.createElement( 'div' );
  181. dom.className = 'FancySelect';
  182. dom.style.background = '#fff';
  183. dom.style.border = '1px solid #ccc';
  184. dom.style.padding = '0';
  185. dom.style.cursor = 'default';
  186. dom.style.overflow = 'auto';
  187. this.dom = dom;
  188. this.onChangeCallback = null;
  189. this.options = [];
  190. this.selectedValue = null;
  191. return this;
  192. };
  193. UI.FancySelect.prototype = Object.create( UI.Element.prototype );
  194. UI.FancySelect.prototype.setOptions = function ( options ) {
  195. var scope = this;
  196. while ( scope.dom.children.length > 0 ) {
  197. scope.dom.removeChild( scope.dom.firstChild );
  198. }
  199. scope.options = [];
  200. var generateOptionCallback = function ( element, value ) {
  201. return function ( event ) {
  202. for ( var i = 0; i < scope.options.length; i ++ ) {
  203. scope.options[ i ].style.backgroundColor = '#f0f0f0';
  204. }
  205. element.style.backgroundColor = '#f0f0f0';
  206. scope.selectedValue = value;
  207. if ( scope.onChangeCallback ) scope.onChangeCallback();
  208. }
  209. };
  210. for ( var key in options ) {
  211. var option = document.createElement( 'div' );
  212. option.style.padding = '4px';
  213. option.style.whiteSpace = 'nowrap';
  214. option.innerHTML = options[ key ];
  215. option.value = key;
  216. scope.dom.appendChild( option );
  217. scope.options.push( option );
  218. option.addEventListener( 'click', generateOptionCallback( option, key ), false );
  219. }
  220. return scope;
  221. };
  222. UI.FancySelect.prototype.getValue = function () {
  223. return this.selectedValue;
  224. };
  225. UI.FancySelect.prototype.setValue = function ( value ) {
  226. // must convert raw value into string for compatibility with UI.Select
  227. // which uses string values (initialized from options keys)
  228. var key = value ? value.toString() : value;
  229. for ( var i = 0; i < this.options.length; i ++ ) {
  230. var element = this.options[ i ];
  231. if ( element.value === key ) {
  232. element.style.backgroundColor = '#f0f0f0';
  233. } else {
  234. element.style.backgroundColor = '';
  235. }
  236. }
  237. this.selectedValue = value;
  238. return this;
  239. };
  240. UI.FancySelect.prototype.onChange = function ( callback ) {
  241. this.onChangeCallback = callback;
  242. return this;
  243. };
  244. // Checkbox
  245. UI.Checkbox = function () {
  246. UI.Element.call( this );
  247. var scope = this;
  248. var dom = document.createElement( 'input' );
  249. dom.className = 'Checkbox';
  250. dom.type = 'checkbox';
  251. this.dom = dom;
  252. this.onChangeCallback = null;
  253. this.dom.addEventListener( 'change', function ( event ) {
  254. if ( scope.onChangeCallback ) scope.onChangeCallback();
  255. }, false );
  256. return this;
  257. };
  258. UI.Checkbox.prototype = Object.create( UI.Element.prototype );
  259. UI.Checkbox.prototype.getValue = function () {
  260. return this.dom.checked;
  261. };
  262. UI.Checkbox.prototype.setValue = function ( value ) {
  263. this.dom.checked = value;
  264. return this;
  265. };
  266. UI.Checkbox.prototype.onChange = function ( callback ) {
  267. this.onChangeCallback = callback;
  268. return this;
  269. };
  270. // Color
  271. UI.Color = function () {
  272. UI.Element.call( this );
  273. var scope = this;
  274. var dom = document.createElement( 'input' );
  275. dom.className = 'Color';
  276. dom.style.width = '64px';
  277. dom.style.height = '16px';
  278. dom.style.border = '0px';
  279. dom.style.padding = '0px';
  280. dom.style.backgroundColor = 'transparent';
  281. dom.type = 'color';
  282. this.dom = dom;
  283. this.onChangeCallback = null;
  284. this.dom.addEventListener( 'change', function ( event ) {
  285. if ( scope.onChangeCallback ) scope.onChangeCallback();
  286. }, false );
  287. return this;
  288. };
  289. UI.Color.prototype = Object.create( UI.Element.prototype );
  290. UI.Color.prototype.getValue = function () {
  291. return this.dom.value;
  292. };
  293. UI.Color.prototype.getHexValue = function () {
  294. return parseInt( this.dom.value.substr( 1 ), 16 );
  295. };
  296. UI.Color.prototype.setValue = function ( value ) {
  297. this.dom.value = value;
  298. return this;
  299. };
  300. UI.Color.prototype.setHexValue = function ( hex ) {
  301. this.dom.value = "#" + ( '000000' + hex.toString( 16 ) ).slice( -6 );
  302. return this;
  303. };
  304. UI.Color.prototype.onChange = function ( callback ) {
  305. this.onChangeCallback = callback;
  306. return this;
  307. };
  308. // Number
  309. UI.Number = function () {
  310. UI.Element.call( this );
  311. var scope = this;
  312. var dom = document.createElement( 'input' );
  313. dom.className = 'Number';
  314. dom.style.color = '#0080f0';
  315. dom.style.fontSize = '12px';
  316. dom.style.backgroundColor = 'transparent';
  317. dom.style.border = '1px solid transparent';
  318. dom.style.marginTop = '-2px';
  319. dom.style.marginLegt = '-2px';
  320. dom.style.padding = '2px';
  321. dom.style.cursor = 'col-resize';
  322. dom.value = '0.00';
  323. this.dom = dom;
  324. this.min = - Infinity;
  325. this.max = Infinity;
  326. this.precision = 2;
  327. this.step = 1;
  328. this.onChangeCallback = null;
  329. var distance = 0;
  330. var onMouseDownValue = 0;
  331. var onMouseDown = function ( event ) {
  332. event.preventDefault();
  333. distance = 0;
  334. onMouseDownValue = parseFloat( dom.value );
  335. document.addEventListener( 'mousemove', onMouseMove, false );
  336. document.addEventListener( 'mouseup', onMouseUp, false );
  337. };
  338. var onMouseMove = function ( event ) {
  339. var movementX = event.movementX || event.webkitMovementX || event.mozMovementX || 0;
  340. var movementY = event.movementY || event.webkitMovementY || event.mozMovementY || 0;
  341. distance += movementX - movementY;
  342. var number = onMouseDownValue + ( distance / ( event.shiftKey ? 10 : 100 ) ) * scope.step;
  343. dom.value = Math.min( scope.max, Math.max( scope.min, number ) ).toFixed( scope.precision );
  344. if ( scope.onChangeCallback ) scope.onChangeCallback();
  345. };
  346. var onMouseUp = function ( event ) {
  347. document.removeEventListener( 'mousemove', onMouseMove, false );
  348. document.removeEventListener( 'mouseup', onMouseUp, false );
  349. if ( Math.abs( distance ) < 2 ) {
  350. dom.focus();
  351. dom.select();
  352. }
  353. };
  354. var onChange = function ( event ) {
  355. var number = parseFloat( dom.value );
  356. if ( isNaN( number ) === false ) {
  357. dom.value = number;
  358. if ( scope.onChangeCallback ) scope.onChangeCallback();
  359. }
  360. };
  361. var onFocus = function ( event ) {
  362. dom.style.backgroundColor = '';
  363. dom.style.borderColor = '#ccc';
  364. dom.style.cursor = '';
  365. };
  366. var onBlur = function ( event ) {
  367. dom.style.backgroundColor = 'transparent';
  368. dom.style.borderColor = 'transparent';
  369. dom.style.cursor = 'col-resize';
  370. };
  371. dom.addEventListener( 'mousedown', onMouseDown, false );
  372. dom.addEventListener( 'change', onChange, false );
  373. dom.addEventListener( 'focus', onFocus, false );
  374. dom.addEventListener( 'blur', onBlur, false );
  375. return this;
  376. };
  377. UI.Number.prototype = Object.create( UI.Element.prototype );
  378. UI.Number.prototype.getValue = function () {
  379. return parseFloat( this.dom.value );
  380. };
  381. UI.Number.prototype.setValue = function ( value ) {
  382. this.dom.value = value.toFixed( this.precision );
  383. return this;
  384. };
  385. UI.Number.prototype.setRange = function ( min, max ) {
  386. this.min = min;
  387. this.max = max;
  388. return this;
  389. };
  390. UI.Number.prototype.setPrecision = function ( precision ) {
  391. this.precision = precision;
  392. if ( precision > 2 ) {
  393. this.step = Math.pow( 10, -( precision - 1 ) );
  394. }
  395. return this;
  396. };
  397. UI.Number.prototype.onChange = function ( callback ) {
  398. this.onChangeCallback = callback;
  399. return this;
  400. };
  401. // Break
  402. UI.Break = function () {
  403. UI.Element.call( this );
  404. var dom = document.createElement( 'br' );
  405. dom.className = 'Break';
  406. this.dom = dom;
  407. return this;
  408. };
  409. UI.Break.prototype = Object.create( UI.Element.prototype );
  410. // HorizontalRule
  411. UI.HorizontalRule = function () {
  412. UI.Element.call( this );
  413. var dom = document.createElement( 'hr' );
  414. dom.className = 'HorizontalRule';
  415. this.dom = dom;
  416. return this;
  417. };
  418. UI.HorizontalRule.prototype = Object.create( UI.Element.prototype );
  419. // Button
  420. UI.Button = function () {
  421. UI.Element.call( this );
  422. var scope = this;
  423. var dom = document.createElement( 'button' );
  424. dom.className = 'Button';
  425. this.dom = dom;
  426. this.onClickCallback = null;
  427. this.dom.addEventListener( 'click', function ( event ) {
  428. scope.onClickCallback();
  429. }, false );
  430. return this;
  431. };
  432. UI.Button.prototype = Object.create( UI.Element.prototype );
  433. UI.Button.prototype.setLabel = function ( value ) {
  434. this.dom.textContent = value;
  435. return this;
  436. };
  437. UI.Button.prototype.onClick = function ( callback ) {
  438. this.onClickCallback = callback;
  439. return this;
  440. };