diff --git a/nixie.html b/nixie.html new file mode 100644 index 0000000..569f7a6 --- /dev/null +++ b/nixie.html @@ -0,0 +1,287 @@ + + + + + + Virtual nixie tube display + + + + + + +
+
skin...
+
+
+
+
+
+
+
+ +
+
skin...
+
+
+
+
+
+
+
+ +
+
skin...
+
+
+
+
+
+
+ (just type expressions on keyboard as on any basic electronic calculator)
+ [q] sqrt   [Q] sqr   [^] pow   [m] +/-   [p] PI   [Enter] =   [Esc] clear +
+
+
+ + + + + + + diff --git a/nixie.js b/nixie.js new file mode 100644 index 0000000..b5a315e --- /dev/null +++ b/nixie.js @@ -0,0 +1,545 @@ + +/**************************************************************************** + + Virtual nixie tube display, clock & calculator DHTML components + + v 1.05, 20080214a + + (c) 2007-08 Cestmir Hybl, cestmir.hybl@nustep.net + http://cestmir.freeside.sk/projects/dhtml-nixie-display + + license: free for non-commercial use, copyright must be preserved + + ****************************************************************************/ + + + +/* NixieDisplay */ + +// public class NixieDisplay +function NixieDisplay() +{ + // public + this.id = 'nixie'; + this.elContainer = null; + this.charCount = 10; + this.autoDecimalPoint = true; // automatically extracts decimal point index in setText() call + this.align = 'left'; // alignment of text via setText() call + this.afterUpdate = null; // after display update callback + + this.charWidth = 62; + this.charHeight = 150; + this.charGapWidth = 0; + this.extraGapsWidths = []; + this.createCharElements = true; + + this.text = ''; + this.decimalPoint = -1; + + this.urlCharsetImage = 'nixie/zm1082_l1_09bdm_62x150_8b.png'; + this.charMap = { + 0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9, + '0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9, ' ': 10, '-': 11, + 'default': 10 + }; + // maps displayable chars onto glyph matrix indexes + + // protected + function _drawChar(index) + { + var el = document.getElementById(this.id + '_d' + index); + var charIndex = this.charMap[this.text.charAt(index)]; + if (!charIndex && charIndex !== 0) + charIndex = this.charMap['default']; + var x = - (charIndex * this.charWidth); + var y = (index === this.decimalPoint ? - this.charHeight : 0); + el.style.backgroundPosition = x + 'px ' + y + 'px'; + } + this._drawChar = _drawChar; + + // Shows given string on display + // public + function setText(text, updateDecimalPoint) + { + // force string type + this.text = text + ''; + + // extract decimal point + updateDecimalPoint = (typeof(updateDecimalPoint) != 'undefined' ? updateDecimalPoint : this.autoDecimalPoint); + if (updateDecimalPoint) { + var i = this.text.indexOf('.'); + if (i >= 0) { + this.decimalPoint = i - 1; + // alert(this.decimalPoint); + this.text = this.text.substr(0, i) + this.text.substr(i + 1); + } else + this.decimalPoint = -1; + } + + // pad up to display width (from left/right acording to this.align) + if (this.text.length < this.charCount) { + var pad = ''; + var padWidth = this.charCount - this.text.length; + for (var i = 0; i < padWidth; i++) + pad += ' '; + if (this.align == 'left') + this.text = this.text + pad; + else { + if (this.decimalPoint >= 0) + this.decimalPoint += padWidth; + this.text = pad + this.text; + } + } + + if (this.text.length > this.charCount) + this.text = this.text.substr(0, this.charCount); + + // draw chars + for (var i = 0; i < this.text.length; i++) { + this._drawChar(i); + } + + if (this.afterUpdate) + this.afterUpdate(this); + } + this.setText = setText; + + // Sets char at given display position + // public + function setChar(index, chr) + { + // alert(chr); + this.text = this.text.substring(0, index) + chr + this.text.substring(index + 1); + this.setText(this.text, false); + } + this.setChar = setChar; + + function setDecimalPoint(index) + { + var oldDecimalPoint = this.decimalPoint; + this.decimalPoint = ((!index && index !== 0) ? -1 : index); + if (oldDecimalPoint != this.decimalPoint) { + if (oldDecimalPoint >= 0) + this._drawChar(oldDecimalPoint); + if (this.decimalPoint >= 0) + this._drawChar(this.decimalPoint); + } + } + this.setDecimalPoint = setDecimalPoint; + + // Clears display - fills all positions with given char (space by default). + // public + function clear(chr) + { + chr = (typeof(chr) == 'undefined' ? ' ' : chr); + this.text = ''; + for (var i = 0; i < this.charCount; i++) + this.text += chr; + this.decimalPoint = -1; + this.setText(this.text); + } + this.clear = clear; + + // Shifts display contents left or right + // public + function shift(direction, step) + { + step = (!step && step !== 0 ? 1 : step); + direction = (!direction ? 'left' : direction); + + if (this.decimalPoint >= 0) { + this.decimalPoint += (direction == 'left' ? - step : + step); + if (this.decimalPoint >= this.charCount) + this.decimalPoint = -1; + } + + if (direction == 'left') + this.text = this.text.substr(step) + ' '; // @todo padding for step != +/-1 + else if (direction == 'right') + this.text = ' ' + this.text.substr(0, this.text.length - 1); // @todo padding for step != +/-1 + this.setText(this.text, false); + } + this.shift = shift; + + // public + function init() + { + if (!this.elContainer) { + this.elContainer = document.getElementById(this.id); + if (!this.elContainer) + throw "Container element '" + this.id + "' not found"; + } + this.elContainer.style.position = 'relative'; + + if (this.createCharElements) { + var totalWidth = 0; + for (var i = 0; i < this.charCount; i++) { + var charWidthIncludingGap = (this.charWidth + this.charGapWidth); + + var elId = this.id + '_d' + i; + var el0 = document.getElementById(elId); + var el = (el0 ? el0 : document.createElement('div')); + el.id = this.id + '_d' + i; + el.className = 'digit d' + i; + el.style.position = 'absolute'; + el.style.left = totalWidth + 'px'; + el.style.width = this.charWidth + 'px'; + el.style.height = this.charHeight + 'px'; + el.style.background = 'url(' + this.urlCharsetImage + ')'; + if (!el.parentNode) + this.elContainer.appendChild(el); + + totalWidth += charWidthIncludingGap + (this.extraGapsWidths[i] ? this.extraGapsWidths[i] : 0); + } + this.elContainer.style.width = totalWidth + 'px'; + this.elContainer.style.height = this.charHeight + 'px'; + } + + if (this.text) + this.setText(this.text) + else + this.clear(); + } + this.init = init; +} + + + +/* NixieClock */ + +// public class NixieClock : NixieDisplay +function NixieClock() +{ + // public + + // private + this.lastSeconds = -1; + + // Show current time on "display" + // public + function showCurrentTime(refreshAfterChangeOnly) + { + var d = new Date(); + + var s = d.getSeconds(); + + if (refreshAfterChangeOnly && s == this.lastSeconds) + return; + else + this.lastSeconds = s; + + var h = d.getHours(); + var m = d.getMinutes(); + + var digits = ''; + + digits += (h / 10) | 0; + digits += h % 10; + digits += (m / 10) | 0; + digits += m % 10; + digits += (s / 10) | 0; + digits += s % 10; + + this.setText(digits); + } + this.showCurrentTime = showCurrentTime; + + // Run clock (via scheduling a periodic callback to showCurrentTime()) + // public + function run() + { + if (!this.elContainer) + this.init(); + var __nixieClock = this; + window.setInterval(function() { __nixieClock.showCurrentTime(true); }, 100); + } + this.run = run; + + this.ancestor = NixieDisplay; + this.ancestor(); + + this.charCount = 6; + this.extraGapsWidths[1] = 20; + this.extraGapsWidths[3] = 20; +} + + + +/* NixieCalculator */ + +// @todo rounding of rightmost digit + +// public class NixieCalculator : NixieDisplay +function NixieCalculator() +{ + // public + this.id = 'nixieCalc'; + this.digitCount = 13; + this.display = new NixieDisplay(); + + // private + this.operandStack = []; + this.newValueAtNextChar = false; + this.fullPrecisionValue = 0; + + // private + function push(value) + { + this.operandStack[this.operandStack.length] = value; // JS50 compatible .push() + } + this.push = push; + + // private + function pop() + { + if (!this.operandStack.length) + return null; + var v = this.operandStack[this.operandStack.length - 1]; + this.operandStack = this.operandStack.slice(0, this.operandStack.length - 1); // JS50 compatible .pop() + return v; + } + this.pop = pop; + + // public + function getValue() + { + if (this.fullPrecisionValue !== null) + return this.fullPrecisionValue; + + var v = this.display.text; + + // insert decimal point + if (this.display.decimalPoint >= 0 && this.display.decimalPoint < this.digitCount - 1) + v = v.substr(0, this.display.decimalPoint + 1) + '.' + v.substr(this.display.decimalPoint + 1); + + // remove padding spaces + var i = 0; + while (i < v.length && v.charAt(i) == ' ') + i++; + v = v.substr(i); + + // convert to number + v = parseFloat(v); + + return v; + } + this.getValue = getValue; + + // public + function setValue(v) + { + if (typeof(v) != 'number') + v = parseFloat(v); + if (isNaN(v) || v > this.maxNumber || v < -this.maxNumber) + this.error(); + else { + this.fullPrecisionValue = v; + if (v.toFixed) { + // force fixed-point notation (JS5.5+) + var s = (v >= 0 ? ' ' : '') + v.toFixed(1); + s = s.substring(0, s.length - 2); + // (s now contains string with integer part of value, prefixed by either ' ' or '-') + v = v.toFixed(this.digitCount - s.length); // to fixed point + round rightmost digit + } else { + v = v.toString(); + if (v.toLowerCase().indexOf('e') >= 0) { + // we won't handle exp notation in JS<5.5 + this.error(); + return; + } + } + if (v !== '0') { + if (v.charAt(0) != '-') + v = ' ' + v; + var c = this.digitCount + (v.indexOf('.') >= 0 ? 1 : 0); + if (v.length > c) + v = v.substr(0, c); + v = v.replace(/^(.{1,}?)\.?0+$/g, '$1'); // strip zero's from right + } + this.display.setText(v); + } + } + this.setValue = setValue; + + // private + function eval(v1, o, v2) + { + try { + switch (o) { + case '+': + return v1 + v2; + case '-': + return v1 - v2; + case '*': + return v1 * v2; + case '/': + return v1 / v2; + case '^': + return Math.pow(v1, v2); + case 'sqrt': + return Math.sqrt(v1); + case 'sqr': + return v1 * v1; + default: + throw "Unsupported operand: '" + o + "'"; + } + } catch(e) { + this.error(); + } + } + this.eval = eval; + + // public + function error() + { + var s= ''; + for (var i = 0; i < this.digitCount; i++) + s += '-'; + this.operandStack = []; + this.newValueAtNextChar = true; + this.fullPrecisionValue = null; + this.display.setText(s); + } + this.error = error; + + // public + function clear() + { + this.display.clear(); + this.setValue(0); + this.operandStack = []; + this.fullPrecisionValue = null; + } + this.clear = clear; + + // public + function keyDown(event0) + { + var e = (event0 ? event0 : event); + var k = e.keyCode; + + var cancelEvent = true; + + if (k == 8) { + // backspace + if (this.display.text.charAt(this.digitCount - 2) == ' ') + this.display.setChar(this.digitCount - 1, '0'); + else + this.display.shift('right'); + this.fullPrecisionValue = null; + } else if (k == 27) { + // escape + this.clear(); + } else + cancelEvent = false; + + return !cancelEvent; + } + this.keyDown = keyDown; + + // public + function keyPress(event0) + { + var e = (event0 ? event0 : event); + var k = (e.keyCode ? e.keyCode : e.which); // IE: .keyCode, FF: .which + var chr = String.fromCharCode(k); + + var cancelEvent = true; + + var newValueAtThisChar = this.newValueAtNextChar; + this.newValueAtNextChar = true; + + if (chr >= '0' && chr <= '9') { + this.fullPrecisionValue = null; + if (newValueAtThisChar) { + this.display.clear(); + } + if (this.display.text.charAt(1) == ' ' || this.display.text.charAt(1) == '-') { + if (this.display.text.charAt(this.digitCount - 1) == '0' && this.display.text.charAt(this.digitCount - 2) == ' ' && this.display.decimalPoint < 0) + ; + else + this.display.shift('left'); + this.display.setChar(this.digitCount - 1, chr); + } + this.newValueAtNextChar = false; + } + else if (chr == '.' || chr == ',') { + this.fullPrecisionValue = null; + if (newValueAtThisChar) + this.display.setText(0); + if (this.display.decimalPoint < 0) + this.display.setDecimalPoint(this.digitCount - 1); + this.newValueAtNextChar = false; + } + else if (chr == '+' || chr == '-' || chr == '*' || chr == '/' || chr == '^') { + if (this.operandStack.length > 2) + // cancel repeated evaluation + this.operandStack = []; + if (this.operandStack.length == 2) { + // previous expression without explicit '=', evaluate it + this.setValue(this.eval(this.operandStack[this.operandStack.length - 2], this.operandStack[this.operandStack.length - 1], this.getValue())); + this.operandStack = []; + } + // push left operand + this.push(this.getValue()); + // push operator + this.push(chr); + } + else if (chr == 'm' || chr == 'M') { + this.setValue(- this.getValue()); + this.newValueAtNextChar = false; + } + else if (chr == 'p' || chr == 'P') { + this.setValue(Math.PI); + } + else if (chr == 'q') { + this.setValue(this.eval(this.getValue(), 'sqrt', null)); + } + else if (chr == 'Q') { + this.setValue(this.eval(this.getValue(), 'sqr', null)); + } + else if (k == 13 || chr == '=') { + if (this.operandStack.length >= 2) { + if (this.operandStack.length <= 2) + // push right operand + this.push(this.getValue()) + else + // repeated evaluation (e.g. [1] [+] [1] [=] [=] ...), replace left operand with current display value + this.operandStack[this.operandStack.length - 3] = this.getValue(); + // alert(this.operandStack); + var result = this.eval(this.operandStack[this.operandStack.length - 3], this.operandStack[this.operandStack.length - 2], this.operandStack[this.operandStack.length - 1]); + this.setValue(result); + // this.operandStack = []; + } + } + // else if (k == 27) { // Fix: chromium: Esc not handled in keyPress(), http://code.google.com/p/chromium/issues/detail?id=12744 + // // escape + // this.clear(); + // } + else { + cancelEvent = false; + this.newValueAtNextChar = false; + } + + return !cancelEvent; + } + this.keyPress = keyPress; + + // public + function init() + { + this.display.id = this.id; + this.display.charCount = this.digitCount; + this.display.align = 'right'; + this.display.init(); + this.display.setText('0'); + this.maxNumber = Math.pow(10, this.digitCount - 1) - 1; + } + this.init = init; + + // assign default glyph matrix + this.display.urlCharsetImage = 'nixie/zm1080_l2_09bdm_45x75_8b.png'; + this.display.charWidth = 45; + this.display.charHeight = 75; + this.display.charGapWidth = 5; +} diff --git a/zm1080_d1_09bdm_30x50_8b.png b/zm1080_d1_09bdm_30x50_8b.png new file mode 100644 index 0000000..14b52fb Binary files /dev/null and b/zm1080_d1_09bdm_30x50_8b.png differ diff --git a/zm1080_d1_09bdm_46x75_8b.png b/zm1080_d1_09bdm_46x75_8b.png new file mode 100644 index 0000000..5e7d109 Binary files /dev/null and b/zm1080_d1_09bdm_46x75_8b.png differ diff --git a/zm1080_d1_09bdm_62x100_8b.png b/zm1080_d1_09bdm_62x100_8b.png new file mode 100644 index 0000000..b885f12 Binary files /dev/null and b/zm1080_d1_09bdm_62x100_8b.png differ diff --git a/zm1080_l1_09bdm_42x100_8b.png b/zm1080_l1_09bdm_42x100_8b.png new file mode 100644 index 0000000..d4d1b58 Binary files /dev/null and b/zm1080_l1_09bdm_42x100_8b.png differ diff --git a/zm1080_l1_09bdm_62x150_8b.png b/zm1080_l1_09bdm_62x150_8b.png new file mode 100644 index 0000000..6a69821 Binary files /dev/null and b/zm1080_l1_09bdm_62x150_8b.png differ diff --git a/zm1080_l2_09bdm_30x50_8b.png b/zm1080_l2_09bdm_30x50_8b.png new file mode 100644 index 0000000..8fdf309 Binary files /dev/null and b/zm1080_l2_09bdm_30x50_8b.png differ diff --git a/zm1080_l2_09bdm_45x75_8b.png b/zm1080_l2_09bdm_45x75_8b.png new file mode 100644 index 0000000..f34628c Binary files /dev/null and b/zm1080_l2_09bdm_45x75_8b.png differ diff --git a/zm1080_l2_09bdm_90x150_8b.png b/zm1080_l2_09bdm_90x150_8b.png new file mode 100644 index 0000000..3281f07 Binary files /dev/null and b/zm1080_l2_09bdm_90x150_8b.png differ diff --git a/zm1080_r_anim_86x150_100ms_5s.gif b/zm1080_r_anim_86x150_100ms_5s.gif new file mode 100644 index 0000000..bda2765 Binary files /dev/null and b/zm1080_r_anim_86x150_100ms_5s.gif differ diff --git a/zm1082_l1_09bdm_42x100_8b.png b/zm1082_l1_09bdm_42x100_8b.png new file mode 100644 index 0000000..9b3a6df Binary files /dev/null and b/zm1082_l1_09bdm_42x100_8b.png differ diff --git a/zm1082_l1_09bdm_62x150_8b.png b/zm1082_l1_09bdm_62x150_8b.png new file mode 100644 index 0000000..666e71d Binary files /dev/null and b/zm1082_l1_09bdm_62x150_8b.png differ