|
-
- /****************************************************************************
-
- 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;
- }
|