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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ (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