/** * Add spinner handling for input elements. * * The `.uiSpinner()` method add spinner handling to an input elements. * * The `.uiSpinner()` method is a validation input also and using the validation types ''date'' and ''number'' from * the ep.validate module. Have a look at the additional options, some options are only available for * a special validation types. * * ### Examples * Apply .uiSpinner() to input element named percent. * * JavaScript: * * ep(':input[name=percent]') * .uiSpinner({ * required: true, * type: 'number', * format: 'p', * min: 10, * max: 50, * step: 10 * }); * * HTML: * * <form> * <input type="text" name="percent" /> * </form> * * * @class jQuery.ui.uiSpinner * @extends jQuery.ui.uiValidate * * @uses ep.ui.validate * @uses ep.date * @uses ep.sprite * @since 6.11.0 */ /** * @cfg {String} type A value respectively validation type: 'number' or 'date'. */ /** * @cfg {String} unit A unit for example 'GByte', es this option only for type 'number' an format 'n*' */ /** * @cfg {Number} step A difference to next/previous step. */ /** * @cfg {Number} stepType One of the supported types: 'fullYear', 'month', 'date', 'hours', 'minutes' or 'seconds'. For spinner type date only. */ /** * See `jQuery.ui.uiSpinner` for details. * * @param {Object} [options] A map of additional options pass to the method. * @param {String} type A value respectively validation type: 'number' or 'date'. * @param {String} unit A unit for example 'GByte', es this option only for type 'number' an format 'n*' * @param {Number} step A difference to next/previous step. * @param {Number} stepType One of the supported types: 'fullYear', 'month', 'date', 'hours', 'minutes' or 'seconds'. For spinner type date only. * * @method uiSpinner * @member jQuery * * @since 6.11.0 */ /* * @copyright © Copyright 2006-2010, epages GmbH, All Rights Reserved. * * @module ep.ui.spinner * * @revision $Revision: 1.19 $ */ /*jslint browser: true, nomen: true, plusplus: true*/ /*global define*/ define("ep/ui/spinner", [ "jquery", "ep", "ep/i18n", "ep/date", "ep/sprite", "ep/ui/validate" ], function ($, ep) { 'use strict'; /* * @dictionary ep.dict * * @translation {INVALID_STEP} */ var is = function (val) { return !!(val === 0 || (!isNaN(val) && val)); }, html = $('html'); $.widget('ui.uiSpinner', $.ui.uiValidate, { options: { // ep.ui.Input // big: true, // autofocus: false, // info: 'text', // ep.ui.Validate // accept: '', // min: 1, // max: 20, // minlength: 1, // maxlength: 4, // pattern: [\w]+, // required: true, // tooltipOffset: [0,-7], // valid: true, // type: 'basic', // format: '', // ep.ui.Spinner // unit: 'GByte', type: 'number', format: 'n0', step: 1, stepType: 'hours', validateStep: false }, _create: function () { var self = this; this._superApply(arguments); this.options = $.extend({ step: parseInt(this.elem.attr('step'), 10) }, this.options); this.elem.addClass('ep-uiSpinner') .on('keydown', function (event) { if (event.keyCode === 38 || event.keyCode === 40) { event.preventDefault(); if (!self._interval) { self._start(event.keyCode === 38 ? 1 : -1); } } }) .on('keyup', function (event) { if (event.keyCode === 38 || event.keyCode === 40) { event.preventDefault(); self._stop(); } }); this.stepper = $('<span>') .addClass('ep-uiInput ep-uiInput-button ep-uiSpinner-stepper') .insertAfter(this.elem); this.stepUp = ep(this.dict.parse('<a href="javascript:;">+</a>')) .addClass('ep-uiSpinner-stepUp') .attr({ 'tabIndex': -1 }) .on('mousedown.uiSpinner', function (event) { if (event.button === 0) { self._start(1); } }) .appendTo(this.stepper); this.stepDown = ep(this.dict.parse('<a href="javascript:;">–</a>')) .addClass('ep-uiSpinner-stepDown') .attr({ 'tabIndex': -1 }) .on('mousedown.uiSpinner', function (event) { if (event.button === 0) { self._start(-1); } }) .appendTo(this.stepper); this.stack.push(this.stepper[0], this.stepUp[0], this.stepDown[0]); }, _init: function () { var o = this.options, formatMatch, formatDecimals, stepSplit, stepDecimals, minSplit, minDecimals, maxSplit, maxDecimals, decimals; this._superApply(arguments); this._unitInit(); // Detect format by format,step, min and max options if (o.type !== 'date') { formatMatch = (/(c|n|p)(\d{0,})/).exec(o.format); formatDecimals = parseInt(formatMatch[2] || 0, 10); stepSplit = o.step.toString().split('.'); stepDecimals = stepSplit[1] ? stepSplit[1].length : 0; minSplit = o.min.toString().split('.'); minDecimals = minSplit[1] ? minSplit[1].length : 0; maxSplit = o.max.toString().split('.'); maxDecimals = maxSplit[1] ? maxSplit[1].length : 0; // Get highest value of decimals from format/step/min/max decimals = Math.max(Math.max(Math.max(formatDecimals, stepDecimals), minDecimals), maxDecimals); this.options.format = formatMatch[1] + decimals; } }, _unitInit: function () { var padding; if (!this.unit && this.options.unit) { this.unit = $('<span>') .addClass('ep-uiSpinner-unit') .insertBefore(this.stepper); this.stack.push(this.unit[0]); } if (this.unit) { this.unit.text(this.options.unit); padding = (this.unit.width() + 4) + 'px'; this.elem.css('padding-right', padding); this.unit.css('margin-left', '-' + padding); } }, _tooltipInit: function () { this._superApply(arguments); if (this.tooltip) { this.tooltip.uiTooltip('option', 'offsetAdjust', [30, -1]); } }, _changeAttr: function (event, attr) { var self = this; $.each(attr, function (name, value) { if ((/^(accept|pattern|required)$/i).test(name)) { self.options[name] = value; } else if ((/^(min|max|minlength|maxlength|step)$/i).test(name)) { self.options[name] = parseInt(value, 10); } }); }, _parseValue: function () { var o = this.options, region = { region: o.region }, val = this.elem.attr('value'); if (o.type === 'date') { if (!is(this._val)) { this._val = is(o.min) ? new ep.Date(o.min) : new ep.Date(); } val = $.i18n.parseDate(val, o.format, region); this._val = is(val) ? new ep.Date(val) : this._val; } else { if (!is(this._val)) { this._val = is(o.min) ? o.min : 0; } region.currency = o.currency; val = $.i18n.parseNumber(val, 10, region); this._val = is(val) ? val : this._val; } }, _start: function (inc) { this._parseValue(); this._type = '_spin' + (this.options.type === 'date' ? 'Date' : 'Number'); this._step = this.options.step * inc; this._count = 0; this[this._type](); // set interval for auto count up/down this._interval = setInterval($.proxy(this, '_spin'), 250); html.on('mouseup.uiSpinner', $.proxy(this, '_stop')); }, _spin: function () { this._count++; if ((/^(10|20|30)$/).test(this._count.toString())) { clearInterval(this._interval); // update interval speed for auto count up/down this._interval = setInterval($.proxy(this, '_spin'), 150 / this._count); } this[this._type](); }, _spinNumber: function () { var o = this.options; this._val += this._step; if (is(o.min) && o.min > this._val) { this._val = o.min; this._stop(); } else if (is(o.max) && o.max < this._val) { this._val = o.max; this._stop(); } this.elem.val($.i18n.formatNumber(this._val, o.format, { region: o.region, currency: o.currency })); }, _spinDate: function () { var o = this.options; this._val[$.camelCase('add-' + o.stepType)](this._step); if (is(o.min) && o.min > this._val.getTime()) { this._val = new ep.Date(o.min); this._stop(); } else if (is(o.max) && o.max < this._val.getTime()) { this._val = new ep.Date(o.max); this._stop(); } this.elem.val($.i18n.formatDate(this._val, o.format, { region: o.region })); }, _stop: function () { html.off('mouseup.uiSpinner', $.proxy(this, '_stop')); this._interval = clearInterval(this._interval); this.elem.filter(':visible') .focus() .end() .trigger('changeValue'); }, _createErrorMsgOptions: function () { var self = this, o = self.options, tmpOptions = this._superApply(arguments), exampleValue; if (this.valid === "INVALID_STEP") { exampleValue = o.min + ', ' + (o.min + o.step) + ', ' + (o.min + 2 * o.step); exampleValue = { example: exampleValue }; } return exampleValue || tmpOptions; }, _validate: function (event) { var self = this, o = self.options, val = $.i18n.parseNumber(this.elem.val()); this._superApply(arguments); if (o.validateStep && this.valid === true) { if (((val - o.min) .toFixed(3) * 1000).toFixed(3) % (o.step * 1000)) { this._setValidStatus("INVALID_STEP"); if (event.type !== 'changeAttr') { $(this.elem[0].form) .trigger('change'); } } } }, destroy: function () { this.elem.off('.uiSpinner') .removeClass('epWidth-29') .removeClass(/ep-uiSpinner[\w\d\-]*/g); this._superApply(arguments); } }); return ep; });