Web based virtual Nixie Tube Clock
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

il y a 3 ans
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545
  1. /****************************************************************************
  2. Virtual nixie tube display, clock & calculator DHTML components
  3. v 1.05, 20080214a
  4. (c) 2007-08 Cestmir Hybl, cestmir.hybl@nustep.net
  5. http://cestmir.freeside.sk/projects/dhtml-nixie-display
  6. license: free for non-commercial use, copyright must be preserved
  7. ****************************************************************************/
  8. /* NixieDisplay */
  9. // public class NixieDisplay
  10. function NixieDisplay()
  11. {
  12. // public
  13. this.id = 'nixie';
  14. this.elContainer = null;
  15. this.charCount = 10;
  16. this.autoDecimalPoint = true; // automatically extracts decimal point index in setText() call
  17. this.align = 'left'; // alignment of text via setText() call
  18. this.afterUpdate = null; // after display update callback
  19. this.charWidth = 62;
  20. this.charHeight = 150;
  21. this.charGapWidth = 0;
  22. this.extraGapsWidths = [];
  23. this.createCharElements = true;
  24. this.text = '';
  25. this.decimalPoint = -1;
  26. this.urlCharsetImage = 'nixie/zm1082_l1_09bdm_62x150_8b.png';
  27. this.charMap = {
  28. 0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9,
  29. '0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9, ' ': 10, '-': 11,
  30. 'default': 10
  31. };
  32. // maps displayable chars onto glyph matrix indexes
  33. // protected
  34. function _drawChar(index)
  35. {
  36. var el = document.getElementById(this.id + '_d' + index);
  37. var charIndex = this.charMap[this.text.charAt(index)];
  38. if (!charIndex && charIndex !== 0)
  39. charIndex = this.charMap['default'];
  40. var x = - (charIndex * this.charWidth);
  41. var y = (index === this.decimalPoint ? - this.charHeight : 0);
  42. el.style.backgroundPosition = x + 'px ' + y + 'px';
  43. }
  44. this._drawChar = _drawChar;
  45. // Shows given string on display
  46. // public
  47. function setText(text, updateDecimalPoint)
  48. {
  49. // force string type
  50. this.text = text + '';
  51. // extract decimal point
  52. updateDecimalPoint = (typeof(updateDecimalPoint) != 'undefined' ? updateDecimalPoint : this.autoDecimalPoint);
  53. if (updateDecimalPoint) {
  54. var i = this.text.indexOf('.');
  55. if (i >= 0) {
  56. this.decimalPoint = i - 1;
  57. // alert(this.decimalPoint);
  58. this.text = this.text.substr(0, i) + this.text.substr(i + 1);
  59. } else
  60. this.decimalPoint = -1;
  61. }
  62. // pad up to display width (from left/right acording to this.align)
  63. if (this.text.length < this.charCount) {
  64. var pad = '';
  65. var padWidth = this.charCount - this.text.length;
  66. for (var i = 0; i < padWidth; i++)
  67. pad += ' ';
  68. if (this.align == 'left')
  69. this.text = this.text + pad;
  70. else {
  71. if (this.decimalPoint >= 0)
  72. this.decimalPoint += padWidth;
  73. this.text = pad + this.text;
  74. }
  75. }
  76. if (this.text.length > this.charCount)
  77. this.text = this.text.substr(0, this.charCount);
  78. // draw chars
  79. for (var i = 0; i < this.text.length; i++) {
  80. this._drawChar(i);
  81. }
  82. if (this.afterUpdate)
  83. this.afterUpdate(this);
  84. }
  85. this.setText = setText;
  86. // Sets char at given display position
  87. // public
  88. function setChar(index, chr)
  89. {
  90. // alert(chr);
  91. this.text = this.text.substring(0, index) + chr + this.text.substring(index + 1);
  92. this.setText(this.text, false);
  93. }
  94. this.setChar = setChar;
  95. function setDecimalPoint(index)
  96. {
  97. var oldDecimalPoint = this.decimalPoint;
  98. this.decimalPoint = ((!index && index !== 0) ? -1 : index);
  99. if (oldDecimalPoint != this.decimalPoint) {
  100. if (oldDecimalPoint >= 0)
  101. this._drawChar(oldDecimalPoint);
  102. if (this.decimalPoint >= 0)
  103. this._drawChar(this.decimalPoint);
  104. }
  105. }
  106. this.setDecimalPoint = setDecimalPoint;
  107. // Clears display - fills all positions with given char (space by default).
  108. // public
  109. function clear(chr)
  110. {
  111. chr = (typeof(chr) == 'undefined' ? ' ' : chr);
  112. this.text = '';
  113. for (var i = 0; i < this.charCount; i++)
  114. this.text += chr;
  115. this.decimalPoint = -1;
  116. this.setText(this.text);
  117. }
  118. this.clear = clear;
  119. // Shifts display contents left or right
  120. // public
  121. function shift(direction, step)
  122. {
  123. step = (!step && step !== 0 ? 1 : step);
  124. direction = (!direction ? 'left' : direction);
  125. if (this.decimalPoint >= 0) {
  126. this.decimalPoint += (direction == 'left' ? - step : + step);
  127. if (this.decimalPoint >= this.charCount)
  128. this.decimalPoint = -1;
  129. }
  130. if (direction == 'left')
  131. this.text = this.text.substr(step) + ' '; // @todo padding for step != +/-1
  132. else if (direction == 'right')
  133. this.text = ' ' + this.text.substr(0, this.text.length - 1); // @todo padding for step != +/-1
  134. this.setText(this.text, false);
  135. }
  136. this.shift = shift;
  137. // public
  138. function init()
  139. {
  140. if (!this.elContainer) {
  141. this.elContainer = document.getElementById(this.id);
  142. if (!this.elContainer)
  143. throw "Container element '" + this.id + "' not found";
  144. }
  145. this.elContainer.style.position = 'relative';
  146. if (this.createCharElements) {
  147. var totalWidth = 0;
  148. for (var i = 0; i < this.charCount; i++) {
  149. var charWidthIncludingGap = (this.charWidth + this.charGapWidth);
  150. var elId = this.id + '_d' + i;
  151. var el0 = document.getElementById(elId);
  152. var el = (el0 ? el0 : document.createElement('div'));
  153. el.id = this.id + '_d' + i;
  154. el.className = 'digit d' + i;
  155. el.style.position = 'absolute';
  156. el.style.left = totalWidth + 'px';
  157. el.style.width = this.charWidth + 'px';
  158. el.style.height = this.charHeight + 'px';
  159. el.style.background = 'url(' + this.urlCharsetImage + ')';
  160. if (!el.parentNode)
  161. this.elContainer.appendChild(el);
  162. totalWidth += charWidthIncludingGap + (this.extraGapsWidths[i] ? this.extraGapsWidths[i] : 0);
  163. }
  164. this.elContainer.style.width = totalWidth + 'px';
  165. this.elContainer.style.height = this.charHeight + 'px';
  166. }
  167. if (this.text)
  168. this.setText(this.text)
  169. else
  170. this.clear();
  171. }
  172. this.init = init;
  173. }
  174. /* NixieClock */
  175. // public class NixieClock : NixieDisplay
  176. function NixieClock()
  177. {
  178. // public
  179. // private
  180. this.lastSeconds = -1;
  181. // Show current time on "display"
  182. // public
  183. function showCurrentTime(refreshAfterChangeOnly)
  184. {
  185. var d = new Date();
  186. var s = d.getSeconds();
  187. if (refreshAfterChangeOnly && s == this.lastSeconds)
  188. return;
  189. else
  190. this.lastSeconds = s;
  191. var h = d.getHours();
  192. var m = d.getMinutes();
  193. var digits = '';
  194. digits += (h / 10) | 0;
  195. digits += h % 10;
  196. digits += (m / 10) | 0;
  197. digits += m % 10;
  198. digits += (s / 10) | 0;
  199. digits += s % 10;
  200. this.setText(digits);
  201. }
  202. this.showCurrentTime = showCurrentTime;
  203. // Run clock (via scheduling a periodic callback to showCurrentTime())
  204. // public
  205. function run()
  206. {
  207. if (!this.elContainer)
  208. this.init();
  209. var __nixieClock = this;
  210. window.setInterval(function() { __nixieClock.showCurrentTime(true); }, 100);
  211. }
  212. this.run = run;
  213. this.ancestor = NixieDisplay;
  214. this.ancestor();
  215. this.charCount = 6;
  216. this.extraGapsWidths[1] = 20;
  217. this.extraGapsWidths[3] = 20;
  218. }
  219. /* NixieCalculator */
  220. // @todo rounding of rightmost digit
  221. // public class NixieCalculator : NixieDisplay
  222. function NixieCalculator()
  223. {
  224. // public
  225. this.id = 'nixieCalc';
  226. this.digitCount = 13;
  227. this.display = new NixieDisplay();
  228. // private
  229. this.operandStack = [];
  230. this.newValueAtNextChar = false;
  231. this.fullPrecisionValue = 0;
  232. // private
  233. function push(value)
  234. {
  235. this.operandStack[this.operandStack.length] = value; // JS50 compatible .push()
  236. }
  237. this.push = push;
  238. // private
  239. function pop()
  240. {
  241. if (!this.operandStack.length)
  242. return null;
  243. var v = this.operandStack[this.operandStack.length - 1];
  244. this.operandStack = this.operandStack.slice(0, this.operandStack.length - 1); // JS50 compatible .pop()
  245. return v;
  246. }
  247. this.pop = pop;
  248. // public
  249. function getValue()
  250. {
  251. if (this.fullPrecisionValue !== null)
  252. return this.fullPrecisionValue;
  253. var v = this.display.text;
  254. // insert decimal point
  255. if (this.display.decimalPoint >= 0 && this.display.decimalPoint < this.digitCount - 1)
  256. v = v.substr(0, this.display.decimalPoint + 1) + '.' + v.substr(this.display.decimalPoint + 1);
  257. // remove padding spaces
  258. var i = 0;
  259. while (i < v.length && v.charAt(i) == ' ')
  260. i++;
  261. v = v.substr(i);
  262. // convert to number
  263. v = parseFloat(v);
  264. return v;
  265. }
  266. this.getValue = getValue;
  267. // public
  268. function setValue(v)
  269. {
  270. if (typeof(v) != 'number')
  271. v = parseFloat(v);
  272. if (isNaN(v) || v > this.maxNumber || v < -this.maxNumber)
  273. this.error();
  274. else {
  275. this.fullPrecisionValue = v;
  276. if (v.toFixed) {
  277. // force fixed-point notation (JS5.5+)
  278. var s = (v >= 0 ? ' ' : '') + v.toFixed(1);
  279. s = s.substring(0, s.length - 2);
  280. // (s now contains string with integer part of value, prefixed by either ' ' or '-')
  281. v = v.toFixed(this.digitCount - s.length); // to fixed point + round rightmost digit
  282. } else {
  283. v = v.toString();
  284. if (v.toLowerCase().indexOf('e') >= 0) {
  285. // we won't handle exp notation in JS<5.5
  286. this.error();
  287. return;
  288. }
  289. }
  290. if (v !== '0') {
  291. if (v.charAt(0) != '-')
  292. v = ' ' + v;
  293. var c = this.digitCount + (v.indexOf('.') >= 0 ? 1 : 0);
  294. if (v.length > c)
  295. v = v.substr(0, c);
  296. v = v.replace(/^(.{1,}?)\.?0+$/g, '$1'); // strip zero's from right
  297. }
  298. this.display.setText(v);
  299. }
  300. }
  301. this.setValue = setValue;
  302. // private
  303. function eval(v1, o, v2)
  304. {
  305. try {
  306. switch (o) {
  307. case '+':
  308. return v1 + v2;
  309. case '-':
  310. return v1 - v2;
  311. case '*':
  312. return v1 * v2;
  313. case '/':
  314. return v1 / v2;
  315. case '^':
  316. return Math.pow(v1, v2);
  317. case 'sqrt':
  318. return Math.sqrt(v1);
  319. case 'sqr':
  320. return v1 * v1;
  321. default:
  322. throw "Unsupported operand: '" + o + "'";
  323. }
  324. } catch(e) {
  325. this.error();
  326. }
  327. }
  328. this.eval = eval;
  329. // public
  330. function error()
  331. {
  332. var s= '';
  333. for (var i = 0; i < this.digitCount; i++)
  334. s += '-';
  335. this.operandStack = [];
  336. this.newValueAtNextChar = true;
  337. this.fullPrecisionValue = null;
  338. this.display.setText(s);
  339. }
  340. this.error = error;
  341. // public
  342. function clear()
  343. {
  344. this.display.clear();
  345. this.setValue(0);
  346. this.operandStack = [];
  347. this.fullPrecisionValue = null;
  348. }
  349. this.clear = clear;
  350. // public
  351. function keyDown(event0)
  352. {
  353. var e = (event0 ? event0 : event);
  354. var k = e.keyCode;
  355. var cancelEvent = true;
  356. if (k == 8) {
  357. // backspace
  358. if (this.display.text.charAt(this.digitCount - 2) == ' ')
  359. this.display.setChar(this.digitCount - 1, '0');
  360. else
  361. this.display.shift('right');
  362. this.fullPrecisionValue = null;
  363. } else if (k == 27) {
  364. // escape
  365. this.clear();
  366. } else
  367. cancelEvent = false;
  368. return !cancelEvent;
  369. }
  370. this.keyDown = keyDown;
  371. // public
  372. function keyPress(event0)
  373. {
  374. var e = (event0 ? event0 : event);
  375. var k = (e.keyCode ? e.keyCode : e.which); // IE: .keyCode, FF: .which
  376. var chr = String.fromCharCode(k);
  377. var cancelEvent = true;
  378. var newValueAtThisChar = this.newValueAtNextChar;
  379. this.newValueAtNextChar = true;
  380. if (chr >= '0' && chr <= '9') {
  381. this.fullPrecisionValue = null;
  382. if (newValueAtThisChar) {
  383. this.display.clear();
  384. }
  385. if (this.display.text.charAt(1) == ' ' || this.display.text.charAt(1) == '-') {
  386. if (this.display.text.charAt(this.digitCount - 1) == '0' && this.display.text.charAt(this.digitCount - 2) == ' ' && this.display.decimalPoint < 0)
  387. ;
  388. else
  389. this.display.shift('left');
  390. this.display.setChar(this.digitCount - 1, chr);
  391. }
  392. this.newValueAtNextChar = false;
  393. }
  394. else if (chr == '.' || chr == ',') {
  395. this.fullPrecisionValue = null;
  396. if (newValueAtThisChar)
  397. this.display.setText(0);
  398. if (this.display.decimalPoint < 0)
  399. this.display.setDecimalPoint(this.digitCount - 1);
  400. this.newValueAtNextChar = false;
  401. }
  402. else if (chr == '+' || chr == '-' || chr == '*' || chr == '/' || chr == '^') {
  403. if (this.operandStack.length > 2)
  404. // cancel repeated evaluation
  405. this.operandStack = [];
  406. if (this.operandStack.length == 2) {
  407. // previous expression without explicit '=', evaluate it
  408. this.setValue(this.eval(this.operandStack[this.operandStack.length - 2], this.operandStack[this.operandStack.length - 1], this.getValue()));
  409. this.operandStack = [];
  410. }
  411. // push left operand
  412. this.push(this.getValue());
  413. // push operator
  414. this.push(chr);
  415. }
  416. else if (chr == 'm' || chr == 'M') {
  417. this.setValue(- this.getValue());
  418. this.newValueAtNextChar = false;
  419. }
  420. else if (chr == 'p' || chr == 'P') {
  421. this.setValue(Math.PI);
  422. }
  423. else if (chr == 'q') {
  424. this.setValue(this.eval(this.getValue(), 'sqrt', null));
  425. }
  426. else if (chr == 'Q') {
  427. this.setValue(this.eval(this.getValue(), 'sqr', null));
  428. }
  429. else if (k == 13 || chr == '=') {
  430. if (this.operandStack.length >= 2) {
  431. if (this.operandStack.length <= 2)
  432. // push right operand
  433. this.push(this.getValue())
  434. else
  435. // repeated evaluation (e.g. [1] [+] [1] [=] [=] ...), replace left operand with current display value
  436. this.operandStack[this.operandStack.length - 3] = this.getValue();
  437. // alert(this.operandStack);
  438. var result = this.eval(this.operandStack[this.operandStack.length - 3], this.operandStack[this.operandStack.length - 2], this.operandStack[this.operandStack.length - 1]);
  439. this.setValue(result);
  440. // this.operandStack = [];
  441. }
  442. }
  443. // else if (k == 27) { // Fix: chromium: Esc not handled in keyPress(), http://code.google.com/p/chromium/issues/detail?id=12744
  444. // // escape
  445. // this.clear();
  446. // }
  447. else {
  448. cancelEvent = false;
  449. this.newValueAtNextChar = false;
  450. }
  451. return !cancelEvent;
  452. }
  453. this.keyPress = keyPress;
  454. // public
  455. function init()
  456. {
  457. this.display.id = this.id;
  458. this.display.charCount = this.digitCount;
  459. this.display.align = 'right';
  460. this.display.init();
  461. this.display.setText('0');
  462. this.maxNumber = Math.pow(10, this.digitCount - 1) - 1;
  463. }
  464. this.init = init;
  465. // assign default glyph matrix
  466. this.display.urlCharsetImage = 'nixie/zm1080_l2_09bdm_45x75_8b.png';
  467. this.display.charWidth = 45;
  468. this.display.charHeight = 75;
  469. this.display.charGapWidth = 5;
  470. }