balmet.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

jquery.bootstrap-touchspin.js (19540B)


      1 (function($) {
      2   'use strict';
      3 
      4   var _currentSpinnerId = 0;
      5 
      6   function _scopedEventName(name, id) {
      7     return name + '.touchspin_' + id;
      8   }
      9 
     10   function _scopeEventNames(names, id) {
     11     return $.map(names, function(name) {
     12       return _scopedEventName(name, id);
     13     });
     14   }
     15 
     16   $.fn.TouchSpin = function(options) {
     17 
     18     if (options === 'destroy') {
     19       this.each(function() {
     20         var originalinput = $(this),
     21             originalinput_data = originalinput.data();
     22         $(document).off(_scopeEventNames([
     23           'mouseup',
     24           'touchend',
     25           'touchcancel',
     26           'mousemove',
     27           'touchmove',
     28           'scroll',
     29           'scrollstart'], originalinput_data.spinnerid).join(' '));
     30       });
     31       return;
     32     }
     33 
     34     var defaults = {
     35       min: 0,
     36       max: 100,
     37       initval: '',
     38       replacementval: '',
     39       step: 1,
     40       decimals: 0,
     41       stepinterval: 100,
     42       forcestepdivisibility: 'round', // none | floor | round | ceil
     43       stepintervaldelay: 500,
     44       verticalbuttons: false,
     45       verticalupclass: 'glyphicon glyphicon-chevron-up',
     46       verticaldownclass: 'glyphicon glyphicon-chevron-down',
     47       prefix: '',
     48       postfix: '',
     49       prefix_extraclass: '',
     50       postfix_extraclass: '',
     51       booster: true,
     52       boostat: 10,
     53       maxboostedstep: false,
     54       mousewheel: true,
     55       buttondown_class: 'btn btn-default',
     56       buttonup_class: 'btn btn-default',
     57 	  buttondown_txt: '-',
     58 	  buttonup_txt: '+'
     59     };
     60 
     61     var attributeMap = {
     62       min: 'min',
     63       max: 'max',
     64       initval: 'init-val',
     65       replacementval: 'replacement-val',
     66       step: 'step',
     67       decimals: 'decimals',
     68       stepinterval: 'step-interval',
     69       verticalbuttons: 'vertical-buttons',
     70       verticalupclass: 'vertical-up-class',
     71       verticaldownclass: 'vertical-down-class',
     72       forcestepdivisibility: 'force-step-divisibility',
     73       stepintervaldelay: 'step-interval-delay',
     74       prefix: 'prefix',
     75       postfix: 'postfix',
     76       prefix_extraclass: 'prefix-extra-class',
     77       postfix_extraclass: 'postfix-extra-class',
     78       booster: 'booster',
     79       boostat: 'boostat',
     80       maxboostedstep: 'max-boosted-step',
     81       mousewheel: 'mouse-wheel',
     82       buttondown_class: 'button-down-class',
     83       buttonup_class: 'button-up-class',
     84 	  buttondown_txt: 'button-down-txt',
     85 	  buttonup_txt: 'button-up-txt'
     86     };
     87 
     88     return this.each(function() {
     89 
     90       var settings,
     91           originalinput = $(this),
     92           originalinput_data = originalinput.data(),
     93           container,
     94           elements,
     95           value,
     96           downSpinTimer,
     97           upSpinTimer,
     98           downDelayTimeout,
     99           upDelayTimeout,
    100           spincount = 0,
    101           spinning = false;
    102 
    103       init();
    104 
    105 
    106       function init() {
    107         if (originalinput.data('alreadyinitialized')) {
    108           return;
    109         }
    110 
    111         originalinput.data('alreadyinitialized', true);
    112         _currentSpinnerId += 1;
    113         originalinput.data('spinnerid', _currentSpinnerId);
    114 
    115 
    116         if (!originalinput.is('input')) {
    117           console.log('Must be an input.');
    118           return;
    119         }
    120 
    121         _initSettings();
    122         _setInitval();
    123         _checkValue();
    124         _buildHtml();
    125         _initElements();
    126         _hideEmptyPrefixPostfix();
    127         _bindEvents();
    128         _bindEventsInterface();
    129         elements.input.css('display', 'block');
    130       }
    131 
    132       function _setInitval() {
    133         if (settings.initval !== '' && originalinput.val() === '') {
    134           originalinput.val(settings.initval);
    135         }
    136       }
    137 
    138       function changeSettings(newsettings) {
    139         _updateSettings(newsettings);
    140         _checkValue();
    141 
    142         var value = elements.input.val();
    143 
    144         if (value !== '') {
    145           value = Number(elements.input.val());
    146           elements.input.val(value.toFixed(settings.decimals));
    147         }
    148       }
    149 
    150       function _initSettings() {
    151         settings = $.extend({}, defaults, originalinput_data, _parseAttributes(), options);
    152       }
    153 
    154       function _parseAttributes() {
    155         var data = {};
    156         $.each(attributeMap, function(key, value) {
    157           var attrName = 'bts-' + value + '';
    158           if (originalinput.is('[data-' + attrName + ']')) {
    159             data[key] = originalinput.data(attrName);
    160           }
    161         });
    162         return data;
    163       }
    164 
    165       function _updateSettings(newsettings) {
    166         settings = $.extend({}, settings, newsettings);
    167       }
    168 
    169       function _buildHtml() {
    170         var initval = originalinput.val(),
    171             parentelement = originalinput.parent();
    172 
    173         if (initval !== '') {
    174           initval = Number(initval).toFixed(settings.decimals);
    175         }
    176 
    177         originalinput.data('initvalue', initval).val(initval);
    178         originalinput.addClass('form-control');
    179 
    180         if (parentelement.hasClass('input-group')) {
    181           _advanceInputGroup(parentelement);
    182         }
    183         else {
    184           _buildInputGroup();
    185         }
    186       }
    187 
    188       function _advanceInputGroup(parentelement) {
    189         parentelement.addClass('bootstrap-touchspin');
    190 
    191         var prev = originalinput.prev(),
    192             next = originalinput.next();
    193 
    194         var downhtml,
    195             uphtml,
    196             prefixhtml = '<span class="input-group-addon bootstrap-touchspin-prefix">' + settings.prefix + '</span>',
    197             postfixhtml = '<span class="input-group-addon bootstrap-touchspin-postfix">' + settings.postfix + '</span>';
    198 
    199         if (prev.hasClass('input-group-btn')) {
    200           downhtml = '<button class="' + settings.buttondown_class + ' bootstrap-touchspin-down" type="button">' + settings.buttondown_txt + '</button>';
    201           prev.append(downhtml);
    202         }
    203         else {
    204           downhtml = '<span class="input-group-btn"><button class="' + settings.buttondown_class + ' bootstrap-touchspin-down" type="button">' + settings.buttondown_txt + '</button></span>';
    205           $(downhtml).insertBefore(originalinput);
    206         }
    207 
    208         if (next.hasClass('input-group-btn')) {
    209           uphtml = '<button class="' + settings.buttonup_class + ' bootstrap-touchspin-up" type="button">' + settings.buttonup_txt + '</button>';
    210           next.prepend(uphtml);
    211         }
    212         else {
    213           uphtml = '<span class="input-group-btn"><button class="' + settings.buttonup_class + ' bootstrap-touchspin-up" type="button">' + settings.buttonup_txt + '</button></span>';
    214           $(uphtml).insertAfter(originalinput);
    215         }
    216 
    217         $(prefixhtml).insertBefore(originalinput);
    218         $(postfixhtml).insertAfter(originalinput);
    219 
    220         container = parentelement;
    221       }
    222 
    223       function _buildInputGroup() {
    224         var html;
    225 
    226         if (settings.verticalbuttons) {
    227           html = '<div class="input-group bootstrap-touchspin"><span class="input-group-addon bootstrap-touchspin-prefix">' + settings.prefix + '</span><span class="input-group-addon bootstrap-touchspin-postfix">' + settings.postfix + '</span><span class="input-group-btn-vertical"><button class="' + settings.buttondown_class + ' bootstrap-touchspin-up" type="button"><i class="' + settings.verticalupclass + '"></i></button><button class="' + settings.buttonup_class + ' bootstrap-touchspin-down" type="button"><i class="' + settings.verticaldownclass + '"></i></button></span></div>';
    228         }
    229         else {
    230           html = '<div class="input-group bootstrap-touchspin"><span class="input-group-btn"><button class="' + settings.buttondown_class + ' bootstrap-touchspin-down" type="button">' + settings.buttondown_txt + '</button></span><span class="input-group-addon bootstrap-touchspin-prefix">' + settings.prefix + '</span><span class="input-group-addon bootstrap-touchspin-postfix">' + settings.postfix + '</span><span class="input-group-btn"><button class="' + settings.buttonup_class + ' bootstrap-touchspin-up" type="button">' + settings.buttonup_txt + '</button></span></div>';
    231         }
    232 
    233         container = $(html).insertBefore(originalinput);
    234 
    235         $('.bootstrap-touchspin-prefix', container).after(originalinput);
    236 
    237         if (originalinput.hasClass('input-sm')) {
    238           container.addClass('input-group-sm');
    239         }
    240         else if (originalinput.hasClass('input-lg')) {
    241           container.addClass('input-group-lg');
    242         }
    243       }
    244 
    245       function _initElements() {
    246         elements = {
    247           down: $('.bootstrap-touchspin-down', container),
    248           up: $('.bootstrap-touchspin-up', container),
    249           input: $('input', container),
    250           prefix: $('.bootstrap-touchspin-prefix', container).addClass(settings.prefix_extraclass),
    251           postfix: $('.bootstrap-touchspin-postfix', container).addClass(settings.postfix_extraclass)
    252         };
    253       }
    254 
    255       function _hideEmptyPrefixPostfix() {
    256         if (settings.prefix === '') {
    257           elements.prefix.hide();
    258         }
    259 
    260         if (settings.postfix === '') {
    261           elements.postfix.hide();
    262         }
    263       }
    264 
    265       function _bindEvents() {
    266         originalinput.on('keydown', function(ev) {
    267           var code = ev.keyCode || ev.which;
    268 
    269           if (code === 38) {
    270             if (spinning !== 'up') {
    271               upOnce();
    272               startUpSpin();
    273             }
    274             ev.preventDefault();
    275           }
    276           else if (code === 40) {
    277             if (spinning !== 'down') {
    278               downOnce();
    279               startDownSpin();
    280             }
    281             ev.preventDefault();
    282           }
    283         });
    284 
    285         originalinput.on('keyup', function(ev) {
    286           var code = ev.keyCode || ev.which;
    287 
    288           if (code === 38) {
    289             stopSpin();
    290           }
    291           else if (code === 40) {
    292             stopSpin();
    293           }
    294         });
    295 
    296         originalinput.on('blur', function() {
    297           _checkValue();
    298         });
    299 
    300         elements.down.on('keydown', function(ev) {
    301           var code = ev.keyCode || ev.which;
    302 
    303           if (code === 32 || code === 13) {
    304             if (spinning !== 'down') {
    305               downOnce();
    306               startDownSpin();
    307             }
    308             ev.preventDefault();
    309           }
    310         });
    311 
    312         elements.down.on('keyup', function(ev) {
    313           var code = ev.keyCode || ev.which;
    314 
    315           if (code === 32 || code === 13) {
    316             stopSpin();
    317           }
    318         });
    319 
    320         elements.up.on('keydown', function(ev) {
    321           var code = ev.keyCode || ev.which;
    322 
    323           if (code === 32 || code === 13) {
    324             if (spinning !== 'up') {
    325               upOnce();
    326               startUpSpin();
    327             }
    328             ev.preventDefault();
    329           }
    330         });
    331 
    332         elements.up.on('keyup', function(ev) {
    333           var code = ev.keyCode || ev.which;
    334 
    335           if (code === 32 || code === 13) {
    336             stopSpin();
    337           }
    338         });
    339 
    340         elements.down.on('mousedown.touchspin', function(ev) {
    341           elements.down.off('touchstart.touchspin');  // android 4 workaround
    342 
    343           if (originalinput.is(':disabled')) {
    344             return;
    345           }
    346 
    347           downOnce();
    348           startDownSpin();
    349 
    350           ev.preventDefault();
    351           ev.stopPropagation();
    352         });
    353 
    354         elements.down.on('touchstart.touchspin', function(ev) {
    355           elements.down.off('mousedown.touchspin');  // android 4 workaround
    356 
    357           if (originalinput.is(':disabled')) {
    358             return;
    359           }
    360 
    361           downOnce();
    362           startDownSpin();
    363 
    364           ev.preventDefault();
    365           ev.stopPropagation();
    366         });
    367 
    368         elements.up.on('mousedown.touchspin', function(ev) {
    369           elements.up.off('touchstart.touchspin');  // android 4 workaround
    370 
    371           if (originalinput.is(':disabled')) {
    372             return;
    373           }
    374 
    375           upOnce();
    376           startUpSpin();
    377 
    378           ev.preventDefault();
    379           ev.stopPropagation();
    380         });
    381 
    382         elements.up.on('touchstart.touchspin', function(ev) {
    383           elements.up.off('mousedown.touchspin');  // android 4 workaround
    384 
    385           if (originalinput.is(':disabled')) {
    386             return;
    387           }
    388 
    389           upOnce();
    390           startUpSpin();
    391 
    392           ev.preventDefault();
    393           ev.stopPropagation();
    394         });
    395 
    396         elements.up.on('mouseout touchleave touchend touchcancel', function(ev) {
    397           if (!spinning) {
    398             return;
    399           }
    400 
    401           ev.stopPropagation();
    402           stopSpin();
    403         });
    404 
    405         elements.down.on('mouseout touchleave touchend touchcancel', function(ev) {
    406           if (!spinning) {
    407             return;
    408           }
    409 
    410           ev.stopPropagation();
    411           stopSpin();
    412         });
    413 
    414         elements.down.on('mousemove touchmove', function(ev) {
    415           if (!spinning) {
    416             return;
    417           }
    418 
    419           ev.stopPropagation();
    420           ev.preventDefault();
    421         });
    422 
    423         elements.up.on('mousemove touchmove', function(ev) {
    424           if (!spinning) {
    425             return;
    426           }
    427 
    428           ev.stopPropagation();
    429           ev.preventDefault();
    430         });
    431 
    432         $(document).on(_scopeEventNames(['mouseup', 'touchend', 'touchcancel'], _currentSpinnerId).join(' '), function(ev) {
    433           if (!spinning) {
    434             return;
    435           }
    436 
    437           ev.preventDefault();
    438           stopSpin();
    439         });
    440 
    441         $(document).on(_scopeEventNames(['mousemove', 'touchmove', 'scroll', 'scrollstart'], _currentSpinnerId).join(' '), function(ev) {
    442           if (!spinning) {
    443             return;
    444           }
    445 
    446           ev.preventDefault();
    447           stopSpin();
    448         });
    449 
    450         originalinput.on('mousewheel DOMMouseScroll', function(ev) {
    451           if (!settings.mousewheel || !originalinput.is(':focus')) {
    452             return;
    453           }
    454 
    455           var delta = ev.originalEvent.wheelDelta || -ev.originalEvent.deltaY || -ev.originalEvent.detail;
    456 
    457           ev.stopPropagation();
    458           ev.preventDefault();
    459 
    460           if (delta < 0) {
    461             downOnce();
    462           }
    463           else {
    464             upOnce();
    465           }
    466         });
    467       }
    468 
    469       function _bindEventsInterface() {
    470         originalinput.on('touchspin.uponce', function() {
    471           stopSpin();
    472           upOnce();
    473         });
    474 
    475         originalinput.on('touchspin.downonce', function() {
    476           stopSpin();
    477           downOnce();
    478         });
    479 
    480         originalinput.on('touchspin.startupspin', function() {
    481           startUpSpin();
    482         });
    483 
    484         originalinput.on('touchspin.startdownspin', function() {
    485           startDownSpin();
    486         });
    487 
    488         originalinput.on('touchspin.stopspin', function() {
    489           stopSpin();
    490         });
    491 
    492         originalinput.on('touchspin.updatesettings', function(e, newsettings) {
    493           changeSettings(newsettings);
    494         });
    495       }
    496 
    497       function _forcestepdivisibility(value) {
    498         switch (settings.forcestepdivisibility) {
    499           case 'round':
    500             return (Math.round(value / settings.step) * settings.step).toFixed(settings.decimals);
    501           case 'floor':
    502             return (Math.floor(value / settings.step) * settings.step).toFixed(settings.decimals);
    503           case 'ceil':
    504             return (Math.ceil(value / settings.step) * settings.step).toFixed(settings.decimals);
    505           default:
    506             return value;
    507         }
    508       }
    509 
    510       function _checkValue() {
    511         var val, parsedval, returnval;
    512 
    513         val = originalinput.val();
    514 
    515         if (val === '') {
    516           if (settings.replacementval !== '') {
    517             originalinput.val(settings.replacementval);
    518             originalinput.trigger('change');
    519           }
    520           return;
    521         }
    522 
    523         if (settings.decimals > 0 && val === '.') {
    524           return;
    525         }
    526 
    527         parsedval = parseFloat(val);
    528 
    529         if (isNaN(parsedval)) {
    530           if (settings.replacementval !== '') {
    531             parsedval = settings.replacementval;
    532           }
    533           else {
    534             parsedval = 0;
    535           }
    536         }
    537 
    538         returnval = parsedval;
    539 
    540         if (parsedval.toString() !== val) {
    541           returnval = parsedval;
    542         }
    543 
    544         if (parsedval < settings.min) {
    545           returnval = settings.min;
    546         }
    547 
    548         if (parsedval > settings.max) {
    549           returnval = settings.max;
    550         }
    551 
    552         returnval = _forcestepdivisibility(returnval);
    553 
    554         if (Number(val).toString() !== returnval.toString()) {
    555           originalinput.val(returnval);
    556           originalinput.trigger('change');
    557         }
    558       }
    559 
    560       function _getBoostedStep() {
    561         if (!settings.booster) {
    562           return settings.step;
    563         }
    564         else {
    565           var boosted = Math.pow(2, Math.floor(spincount / settings.boostat)) * settings.step;
    566 
    567           if (settings.maxboostedstep) {
    568             if (boosted > settings.maxboostedstep) {
    569               boosted = settings.maxboostedstep;
    570               value = Math.round((value / boosted)) * boosted;
    571             }
    572           }
    573 
    574           return Math.max(settings.step, boosted);
    575         }
    576       }
    577 
    578       function upOnce() {
    579         _checkValue();
    580 
    581         value = parseFloat(elements.input.val());
    582         if (isNaN(value)) {
    583           value = 0;
    584         }
    585 
    586         var initvalue = value,
    587             boostedstep = _getBoostedStep();
    588 
    589         value = value + boostedstep;
    590 
    591         if (value > settings.max) {
    592           value = settings.max;
    593           originalinput.trigger('touchspin.on.max');
    594           stopSpin();
    595         }
    596 
    597         elements.input.val(Number(value).toFixed(settings.decimals));
    598 
    599         if (initvalue !== value) {
    600           originalinput.trigger('change');
    601         }
    602       }
    603 
    604       function downOnce() {
    605         _checkValue();
    606 
    607         value = parseFloat(elements.input.val());
    608         if (isNaN(value)) {
    609           value = 0;
    610         }
    611 
    612         var initvalue = value,
    613             boostedstep = _getBoostedStep();
    614 
    615         value = value - boostedstep;
    616 
    617         if (value < settings.min) {
    618           value = settings.min;
    619           originalinput.trigger('touchspin.on.min');
    620           stopSpin();
    621         }
    622 
    623         elements.input.val(value.toFixed(settings.decimals));
    624 
    625         if (initvalue !== value) {
    626           originalinput.trigger('change');
    627         }
    628       }
    629 
    630       function startDownSpin() {
    631         stopSpin();
    632 
    633         spincount = 0;
    634         spinning = 'down';
    635 
    636         originalinput.trigger('touchspin.on.startspin');
    637         originalinput.trigger('touchspin.on.startdownspin');
    638 
    639         downDelayTimeout = setTimeout(function() {
    640           downSpinTimer = setInterval(function() {
    641             spincount++;
    642             downOnce();
    643           }, settings.stepinterval);
    644         }, settings.stepintervaldelay);
    645       }
    646 
    647       function startUpSpin() {
    648         stopSpin();
    649 
    650         spincount = 0;
    651         spinning = 'up';
    652 
    653         originalinput.trigger('touchspin.on.startspin');
    654         originalinput.trigger('touchspin.on.startupspin');
    655 
    656         upDelayTimeout = setTimeout(function() {
    657           upSpinTimer = setInterval(function() {
    658             spincount++;
    659             upOnce();
    660           }, settings.stepinterval);
    661         }, settings.stepintervaldelay);
    662       }
    663 
    664       function stopSpin() {
    665         clearTimeout(downDelayTimeout);
    666         clearTimeout(upDelayTimeout);
    667         clearInterval(downSpinTimer);
    668         clearInterval(upSpinTimer);
    669 
    670         switch (spinning) {
    671           case 'up':
    672             originalinput.trigger('touchspin.on.stopupspin');
    673             originalinput.trigger('touchspin.on.stopspin');
    674             break;
    675           case 'down':
    676             originalinput.trigger('touchspin.on.stopdownspin');
    677             originalinput.trigger('touchspin.on.stopspin');
    678             break;
    679         }
    680 
    681         spincount = 0;
    682         spinning = false;
    683       }
    684 
    685     });
    686 
    687   };
    688 
    689 })(jQuery);