/* * jQuery UI Spinner @VERSION * * Copyright (c) 2008 jQuery * Dual licensed under the MIT (MIT-LICENSE.txt) * and GPL (GPL-LICENSE.txt) licenses. * * http://docs.jquery.com/UI/Spinner * * Depends: * ui.core.js */ (function($) { $.widget('ui.spinner', { _init: function() { // terminate initialization if spinner already applied to current element if($.data(this.element[0], 'spinner')) return; // check for onInit callback if (this.options.init) { this.options.init(this.ui(null)); } // check for decimals in steppinng and set _decimals as internal (needs cleaning up) this._decimals = 0; if (this.options.stepping.toString().indexOf('.') != -1) { var s = this.options.stepping.toString(); this._decimals = s.slice(s.indexOf('.')+1, s.length).length; } //Initialize needed constants var self = this; this.element .addClass('ui-spinner-box') .attr('autocomplete', 'off'); // switch off autocomplete in opera this._setValue( isNaN(this._getValue()) ? this.options.start : this._getValue() ); this.element .wrap('
') .parent() .addClass('ui-spinner') .append('') .find('.ui-spinner-up') .bind('mousedown', function(e) { $(this).addClass('ui-spinner-pressed'); if(!self.counter) self.counter = 1; self._mousedown(100, '_up', e); }) .bind('mouseup', function(e) { $(this).removeClass('ui-spinner-pressed'); if(self.counter == 1) self._up(e); self._mouseup(e); }) .bind('mouseout', function(e) { $(this).removeClass('ui-spinner-pressed'); self._mouseup(e); }) // mousedown/mouseup capture first click, now handle second click .bind('dblclick', function(e) { $(this).removeClass('ui-spinner-pressed'); self._up(e); }) .bind('keydown.spinner', function(e) { var KEYS = $.keyCode; if (e.keyCode == KEYS.SPACE || e.keyCode == KEYS.ENTER) { $(this).addClass('ui-spinner-pressed'); if(!self.counter) self.counter = 1; self._up.call(self, e); } else if (e.keyCode == KEYS.DOWN || e.keyCode == KEYS.RIGHT) { self.element.siblings('.ui-spinner-down').focus(); } else if (e.keyCode == KEYS.LEFT) { self.element.focus(); } }) .bind('keyup.spinner', function(e) { $(this).removeClass('ui-spinner-pressed'); self.counter = 0; self._propagate('change', e); }) .end() .append('') .find('.ui-spinner-down') .bind('mousedown', function(e) { $(this).addClass('ui-spinner-pressed'); if(!self.counter) self.counter = 1; self._mousedown(100, '_down', e); }) .bind('mouseup', function(e) { $(this).removeClass('ui-spinner-pressed'); if(self.counter == 1) self._down(); self._mouseup(e); }) .bind('mouseout', function(e) { $(this).removeClass('ui-spinner-pressed'); self._mouseup(e); }) // mousedown/mouseup capture first click, now handle second click .bind('dblclick', function(e) { $(this).removeClass('ui-spinner-pressed'); self._down(e); }) .bind('keydown.spinner', function(e) { var KEYS = $.keyCode; if (e.keyCode == KEYS.SPACE || e.keyCode == KEYS.ENTER) { $(this).addClass('ui-spinner-pressed'); if(!self.counter) self.counter = 1; self._down.call(self, e); } else if (e.keyCode == KEYS.UP || e.keyCode == KEYS.LEFT) { self.element.siblings('.ui-spinner-up').focus(); } }) .bind('keyup.spinner', function(e) { $(this).removeClass('ui-spinner-pressed'); self.counter = 0; self._propagate('change', e); }) .end(); // DataList: Set contraints for object length and step size. // Manipulate height of spinner. this._items = this.element.children().length; if (this._items > 1) { this.element .addClass('ui-spinner-list') .css('height', this.element.outerHeight()/this._items) .children() .addClass('ui-spinner-listitem') .end() .parent() .css('height', this.element.outerHeight()) .end(); this.options.stepping = 1; this.options.min = 0; this.options.max = this._items-1; } this.element .bind('keydown.spinner', function(e) { if(!self.counter) self.counter = 1; return self._keydown.call(self, e); }) .bind('keyup.spinner', function(e) { self.counter = 0; self._propagate('change', e); }) .bind('blur.spinner', function(e) { self._cleanUp(); }); if ($.fn.mousewheel) { this.element.mousewheel(function(e, delta) { self._mousewheel(e, delta); }); } }, _constrain: function() { if(this.options.min != undefined && this._getValue() < this.options.min) this._setValue(this.options.min); if(this.options.max != undefined && this._getValue() > this.options.max) this._setValue(this.options.max); }, _cleanUp: function() { this._setValue(this._getValue()); this._constrain(); }, _spin: function(d, e) { if (this.disabled) return; if(isNaN(this._getValue())) this._setValue(this.options.start); this._setValue(this._getValue() + (d == 'up' ? 1:-1) * (this.options.incremental && this.counter > 100 ? (this.counter > 200 ? 100 : 10) : 1) * this.options.stepping); this._animate(d); this._constrain(); if(this.counter) this.counter++; this._propagate('spin', e); }, _down: function(e) { this._spin('down', e); this._propagate('down', e); }, _up: function(e) { this._spin('up', e); this._propagate('up', e); }, _mousedown: function(i, d, e) { var self = this; i = i || 100; if(this.timer) window.clearInterval(this.timer); this.timer = window.setInterval(function() { self[d](e); if(self.counter > 20) self._mousedown(20, d, e); }, i); }, _mouseup: function(e) { this.counter = 0; if(this.timer) window.clearInterval(this.timer); this.element[0].focus(); this._propagate('change', e); }, _keydown: function(e) { var KEYS = $.keyCode; if(e.keyCode == KEYS.UP) this._up(e); if(e.keyCode == KEYS.DOWN) this._down(e); if(e.keyCode == KEYS.HOME) this._setValue(this.options.min || this.options.start); //Home key goes to min, if defined, else to start if(e.keyCode == KEYS.END && this.options.max != undefined) this._setValue(this.options.max); //End key goes to maximum return (e.keyCode == KEYS.TAB || e.keyCode == KEYS.BACKSPACE || e.keyCode == KEYS.LEFT || e.keyCode == KEYS.RIGHT || e.keyCode == KEYS.PERIOD || e.keyCode == KEYS.NUMPAD_DECIMAL || e.keyCode == KEYS.NUMPAD_SUBTRACT || (e.keyCode >= 96 && e.keyCode <= 105) || // add support for numeric keypad 0-9 (/[0-9\-\.]/).test(String.fromCharCode(e.keyCode))) ? true : false; }, _mousewheel: function(e, delta) { delta = ($.browser.opera ? -delta / Math.abs(delta) : delta); delta > 0 ? this._up(e) : this._down(e); e.preventDefault(); }, _getValue: function() { return parseFloat(this.element.val().replace(/[^0-9\-\.]/g, '')); }, _setValue: function(newVal) { if(isNaN(newVal)) newVal = this.options.start; this.element.val( this.options.currency ? $.ui.spinner.format.currency(newVal, this.options.currency) : $.ui.spinner.format.number(newVal, this._decimals) ); }, _animate: function(d) { if (this.element.hasClass('ui-spinner-list') && ((d == 'up' && this._getValue() <= this.options.max) || (d == 'down' && this._getValue() >= this.options.min)) ) { this.element.animate({marginTop: '-' + this._getValue() * this.element.outerHeight() }, { duration: 'fast', queue: false }); } }, _addItem: function(html) { if (!this.element.is('input')) { var wrapper = 'div'; if (this.element.is('ol') || this.element.is('ul')) { wrapper = 'li'; } this.element.append('<'+ wrapper +' class="ui-spinner-dyn">'+ html + ''); } }, plugins: {}, ui: function(e) { return { options: this.options, element: this.element, value: this._getValue(), add: this._addItem }; }, _propagate: function(n,e) { $.ui.plugin.call(this, n, [e, this.ui()]); return this.element.triggerHandler(n == 'spin' ? n : 'spin'+n, [e, this.ui()], this.options[n]); }, destroy: function() { if(!$.data(this.element[0], 'spinner')) return; if ($.fn.mousewheel) { this.element.unmousewheel(); } this.element .removeClass('ui-spinner-box ui-spinner-list') .removeAttr('disabled') .removeAttr('autocomplete') .removeData('spinner') .unbind('.spinner') .siblings() .remove() .end() .children() .removeClass('ui-spinner-listitem') .remove('.ui-spinner-dyn') .end() .parent() .removeClass('ui-spinner ui-spinner-disabled') .before(this.element.clone()) .remove() .end(); }, enable: function() { this.element .removeAttr('disabled') .siblings() .removeAttr('disabled') .parent() .removeClass('ui-spinner-disabled'); this.disabled = false; }, disable: function() { this.element .attr('disabled', true) .siblings() .attr('disabled', true) .parent() .addClass('ui-spinner-disabled'); this.disabled = true; } }); $.extend($.ui.spinner, { defaults: { stepping: 1, start: 0, incremental: true, currency: false }, format: { number: function(num, dec) { return this.round(num, dec); }, currency: function(num, sym) { return (num !== Math.abs(num) ? '-' : '') + sym + this.round(Math.abs(num), 2); }, round: function(num, dec) { var s = Math.round(parseFloat(num)*Math.pow(10, dec)) / Math.pow(10, dec); // round off weird decimals if (dec > 0) { s = s + ((s.toString().indexOf('.') == -1) ? '.' : '') + '0000000001'; s = s.substr(0, s.indexOf('.')+1+dec); } else { s = Math.round(s); } return s; } } }); })(jQuery);