/** * @license jQuery UI Spinner 1.20 * * Copyright (c) 2009-2010 Brant Burnett * Dual licensed under the MIT or GPL Version 2 licenses. */ (function($, undefined) { var // constants active = 'ui-state-active', hover = 'ui-state-hover', disabled = 'ui-state-disabled', keyCode = $.ui.keyCode, up = keyCode.UP, down = keyCode.DOWN, right = keyCode.RIGHT, left = keyCode.LEFT, pageUp = keyCode.PAGE_UP, pageDown = keyCode.PAGE_DOWN, home = keyCode.HOME, end = keyCode.END, msie = $.browser.msie, mouseWheelEventName = $.browser.mozilla ? 'DOMMouseScroll' : 'mousewheel', // namespace for events on input eventNamespace = '.uispinner', // only these special keys will be accepted, all others will be ignored unless CTRL or ALT are pressed validKeys = [up, down, right, left, pageUp, pageDown, home, end, keyCode.BACKSPACE, keyCode.DELETE, keyCode.TAB], // stores the currently focused spinner // Note: due to oddities in the focus/blur events, this is part of a two-part system for confirming focus // this must set to the control, and the focus variable must be true // this is because hitting up/down arrows with mouse causes focus to change, but blur event for previous control doesn't fire focusCtrl; $.widget('ui.spinner', { options: { min: null, max: null, allowNull: false, group: '', point: '.', prefix: '', suffix: '', places: null, // null causes it to detect the number of places in step defaultStep: 1, // real value is 'step', and should be passed as such. This value is used to detect if passed value should override HTML5 attribute largeStep: 10, mouseWheel: true, increment: 'slow', className: null, showOn: 'always', width: 16, upIconClass: "ui-icon-triangle-1-n", downIconClass: "ui-icon-triangle-1-s", format: function(num, places) { var options = this, regex = /(\d+)(\d{3})/, result = ((isNaN(num) ? 0 : Math.abs(num)).toFixed(places)) + ''; for (result = result.replace('.', options.point); regex.test(result) && options.group; result=result.replace(regex, '$1'+options.group+'$2')) {}; return (num < 0 ? '-' : '') + options.prefix + result + options.suffix; }, parse: function(val) { var options = this; if (options.group == '.') val = val.replace('.', ''); if (options.point != '.') val = val.replace(options.point, '.'); return parseFloat(val.replace(/[^0-9\-\.]/g, '')); } }, // * Widget fields * // curvalue - current value // places - currently effective number of decimal places // oWidth - original input width (used for destroy) // oMargin - original input right margin (used for destroy) // counter - number of spins at the current spin speed // incCounter - index within options.increment of the current spin speed // selfChange - indicates that change event is being fired by the widget, so don't reprocess input value // inputMaxLength - initial maxLength value on the input // focused - this spinner currently has the focus _create: function() { // shortcuts var self = this, input = self.element, type = input.attr('type'); if (!input.is('input') || ((type != 'text') && (type != 'number'))) { console.error('Invalid target for ui.spinner'); return; } self._procOptions(true); self._createButtons(input); if (!input.is(':enabled')) self.disable(); }, _createButtons: function(input) { function getMargin(margin) { // IE8 returns auto if no margin specified return margin == 'auto' ? 0 : parseInt(margin); } var self = this, options = self.options, className = options.className, buttonWidth = options.width, showOn = options.showOn, box = $.support.boxModel, height = input.outerHeight(), rightMargin = self.oMargin = getMargin(input.css('margin-right')), // store original width and right margin for later destroy wrapper = self.wrapper = input.css({ width: (self.oWidth = (box ? input.width() : input.outerWidth())) - buttonWidth, marginRight: rightMargin + buttonWidth, textAlign: 'right' }) .after('').next(), btnContainer = self.btnContainer = $( '
'), // object shortcuts upButton, downButton, buttons, icons, hoverDelay, hoverDelayCallback, // current state booleans hovered, inKeyDown, inSpecialKey, inMouseDown, // used to reverse left/right key directions rtl = input[0].dir == 'rtl'; // apply className before doing any calculations because it could affect them if (className) wrapper.addClass(className); wrapper.append(btnContainer.css({ height: height, left: -buttonWidth-rightMargin, // use offset calculation to fix vertical position in Firefox top: (input.offset().top - wrapper.offset().top) + 'px' })); buttons = self.buttons = btnContainer.find('.ui-spinner-button'); buttons.css({ width: buttonWidth - (box ? buttons.outerWidth() - buttons.width() : 0), height: height/2 - (box ? buttons.outerHeight() - buttons.height() : 0) }); upButton = buttons[0]; downButton = buttons[1]; // fix icon centering icons = buttons.find('.ui-icon'); icons.css({ marginLeft: (buttons.innerWidth() - icons.width()) / 2, marginTop: (buttons.innerHeight() - icons.height()) / 2 }); // set width of btnContainer to be the same as the buttons btnContainer.width(buttons.outerWidth()); if (showOn != 'always') btnContainer.css('opacity', 0); /* Event Bindings */ // bind hover events to show/hide buttons if (showOn == 'hover' || showOn == 'both') buttons.add(input) .bind('mouseenter' + eventNamespace, function() { setHoverDelay(function() { hovered = true; if (!self.focused || (showOn == 'hover')) // ignore focus flag if show on hover only self.showButtons(); }); }) .bind('mouseleave' + eventNamespace, function hoverOut() { setHoverDelay(function() { hovered = false; if (!self.focused || (showOn == 'hover')) // ignore focus flag if show on hover only self.hideButtons(); }); }); buttons.hover(function() { // ensure that both buttons have hover removed, sometimes they get left on self.buttons.removeClass(hover); if (!options.disabled) $(this).addClass(hover); }, function() { $(this).removeClass(hover); }) .mousedown(mouseDown) .mouseup(mouseUp) .mouseout(mouseUp); if (msie) // fixes dbl click not firing second mouse down in IE buttons.dblclick(function() { if (!options.disabled) { // make sure any changes are posted self._change(); self._doSpin((this === upButton ? 1 : -1) * options.step); } return false; }) // fixes IE8 dbl click selection highlight .bind('selectstart', function() {return false;}); input.bind('keydown' + eventNamespace, function(e) { var dir, large, limit, keyCode = e.keyCode; // shortcut for minimization if (e.ctrl || e.alt) return true; // ignore these events if (isSpecialKey(keyCode)) inSpecialKey = true; if (inKeyDown) return false; // only one direction at a time, and suppress invalid keys switch (keyCode) { case up: case pageUp: dir = 1; large = keyCode == pageUp; break; case down: case pageDown: dir = -1; large = keyCode == pageDown; break; case right: case left: dir = (keyCode == right) ^ rtl ? 1 : -1; break; case home: limit = self.options.min; if (limit != null) self._setValue(limit); return false; case end: limit = self.options.max; limit = self.options.max; if (limit != null) self._setValue(limit); return false; } if (dir) { // only process if dir was set above if (!inKeyDown && !options.disabled) { keyDir = dir; $(dir > 0 ? upButton : downButton).addClass(active); inKeyDown = true; self._startSpin(dir, large); } return false; } }) .bind('keyup' + eventNamespace, function(e) { if (e.ctrl || e.alt) return true; // ignore these events if (isSpecialKey(keyCode)) inSpecialKey = false; switch (e.keyCode) { case up: case right: case pageUp: case down: case left: case pageDown: buttons.removeClass(active) self._stopSpin(); inKeyDown = false; return false; } }) .bind('keypress' + eventNamespace, function(e) { if (invalidKey(e.keyCode, e.charCode)) return false; }) .bind('change' + eventNamespace, function() { self._change(); }) .bind('focus' + eventNamespace, function() { function selectAll() { self.element.select(); } msie ? selectAll() : setTimeout(selectAll, 0); // add delay for Chrome, but breaks IE8 self.focused = true; focusCtrl = self; if (!hovered && (showOn == 'focus' || showOn == 'both')) // hovered will only be set if hover affects show self.showButtons(); }) .bind('blur' + eventNamespace, function() { self.focused = false; if (!hovered && (showOn == 'focus' || showOn == 'both')) // hovered will only be set if hover affects show self.hideButtons(); }); function isSpecialKey(keyCode) { for (var i=0; i