JAVASCRIPT   18

wickedpicker js

Guest on 28th July 2022 06:37:34 PM

  1. /**
  2.  * wickedpicker v0.4.1 - A simple jQuery timepicker.
  3.  * Copyright (c) 2015-2016 Eric Gagnon - http://github.com/wickedRidge/wickedpicker
  4.  * License: MIT
  5.  */
  6.  
  7. (function ($, window, document) {
  8.  
  9.     "use strict";
  10.  
  11.     if (typeof String.prototype.endsWith != 'function') {
  12.         /*
  13.          * Checks if this string end ends with another string
  14.          *
  15.          * @param {string} the string to be checked
  16.          *
  17.          * @return {bool}
  18.          */
  19.         String.prototype.endsWith = function (string) {
  20.             return string.length > 0 && this.substring(this.length - string.length, this.length) === string;
  21.         }
  22.     }
  23.  
  24.     /*
  25.      * Returns if the user agent is mobile
  26.      *
  27.      * @return {bool}
  28.      */
  29.     var isMobile = function () {
  30.         return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
  31.     };
  32.  
  33.     var today = new Date();
  34.  
  35.     var pluginName = "wickedpicker",
  36.         defaults = {
  37.             now: today.getHours() + ':' + today.getMinutes(),
  38.             twentyFour: false,
  39.             upArrow: 'wickedpicker__controls__control-up',
  40.             downArrow: 'wickedpicker__controls__control-down',
  41.             close: 'wickedpicker__close',
  42.             hoverState: 'hover-state',
  43.             title: 'Timepicker',
  44.             showSeconds: false,
  45.             secondsInterval: 1,
  46.             minutesInterval: 1,
  47.             beforeShow: null,
  48.             show: null,
  49.             clearable: false
  50.         };
  51.  
  52.     /*
  53.      * @param {object} The input object the timepicker is attached to.
  54.      * @param {object} The object containing options
  55.      */
  56.     function Wickedpicker(element, options) {
  57.         this.element = $(element);
  58.         this.options = $.extend({}, defaults, options);
  59.  
  60.         this.element.addClass('hasWickedpicker');
  61.         this.element.attr('onkeypress', 'return false;');
  62.         this.element.attr('aria-showingpicker', 'false');
  63.         this.createPicker();
  64.         this.timepicker = $('.wickedpicker');
  65.         this.up = $('.' + this.options.upArrow);
  66.         this.down = $('.' + this.options.downArrow);
  67.         this.separator = $('.wickedpicker__controls__control--separator');
  68.         this.hoursElem = $('.wickedpicker__controls__control--hours');
  69.         this.minutesElem = $('.wickedpicker__controls__control--minutes');
  70.         this.secondsElem = $('.wickedpicker__controls__control--seconds');
  71.         this.meridiemElem = $('.wickedpicker__controls__control--meridiem');
  72.         this.close = $('.' + this.options.close);
  73.  
  74.         //Create a new Date object based on the default or passing in now value
  75.         var time = this.timeArrayFromString(this.options.now);
  76.         this.options.now = new Date(today.getFullYear(), today.getMonth(), today.getDate(), time[0], time[1], time[2]);
  77.         this.selectedHour = this.parseHours(this.options.now.getHours());
  78.         this.selectedMin = this.parseSecMin(this.options.now.getMinutes());
  79.         this.selectedSec = this.parseSecMin(this.options.now.getSeconds());
  80.         this.selectedMeridiem = this.parseMeridiem(this.options.now.getHours());
  81.         this.setHoverState();
  82.         this.attach(element);
  83.         this.setText(element);
  84.     }
  85.  
  86.     $.extend(Wickedpicker.prototype, {
  87.  
  88.         /*
  89.          * Show given input's timepicker
  90.          *
  91.          * @param {object} The input being clicked
  92.          */
  93.         showPicker: function (element) {
  94.             //If there is a beforeShow function, then call it with the input calling the timepicker and the
  95.             // timepicker itself
  96.             if (typeof this.options.beforeShow === 'function') {
  97.                 this.options.beforeShow(element, this.timepicker);
  98.             }
  99.             var timepickerPos = $(element).offset();
  100.  
  101.             $(element).attr({'aria-showingpicker': 'true', 'tabindex': -1});
  102.             this.setText(element);
  103.             this.showHideMeridiemControl();
  104.             if (this.getText(element) !== this.getTime()) {
  105.                 var inputTime = this.getText(element).replace(/:/g, '').split(' ');
  106.                 var newTime = {};
  107.                 newTime.hours = inputTime[0];
  108.                 newTime.minutes = inputTime[2];
  109.                 if (this.options.showSeconds) {
  110.                     newTime.seconds = inputTime[4];
  111.                     newTime.meridiem = inputTime[5];
  112.                 } else {
  113.                     newTime.meridiem = inputTime[3];
  114.                 }
  115.                 this.setTime(newTime);
  116.             }
  117.             this.timepicker.css({
  118.                 'z-index': this.element.css('z-index') + 1,
  119.                 position: 'absolute',
  120.                 left: timepickerPos.left,
  121.                 top: timepickerPos.top + $(element)[0].offsetHeight
  122.             }).show();
  123.             //If there is a show function, then call it with the input calling the timepicker and the
  124.             // timepicker itself
  125.             if (typeof this.options.show === 'function') {
  126.                 this.options.show(element, this.timepicker);
  127.             }
  128.  
  129.             this.handleTimeAdjustments(element);
  130.         },
  131.  
  132.         /*
  133.          * Hides the timepicker that is currently shown if it is not part of the timepicker
  134.          *
  135.          * @param {Object} The DOM object being clicked on the page
  136.          */
  137.         hideTimepicker: function (element) {
  138.             this.timepicker.hide();
  139.             var pickerHidden = {
  140.                 start: function () {
  141.                     var setShowPickerFalse = $.Deferred();
  142.                     $('[aria-showingpicker="true"]').attr('aria-showingpicker', 'false');
  143.                     return setShowPickerFalse.promise();
  144.                 }
  145.             };
  146.  
  147.             function setTabIndex(index) {
  148.                 setTimeout(function () {
  149.                     $('[aria-showingpicker="false"]').attr('tabindex', index);
  150.                 }, 400);
  151.             }
  152.  
  153.             pickerHidden.start().then(setTabIndex(0));
  154.         },
  155.  
  156.         /*
  157.          * Create a new timepicker. A single timepicker per page
  158.          */
  159.         createPicker: function () {
  160.             if ($('.wickedpicker').length === 0) {
  161.                 var picker = '<div class="wickedpicker"><p class="wickedpicker__title">' + this.options.title + '<span class="wickedpicker__close"></span></p><ul class="wickedpicker__controls"><li class="wickedpicker__controls__control"><span class="' + this.options.upArrow + '"></span><span class="wickedpicker__controls__control--hours" tabindex="-1">00</span><span class="' + this.options.downArrow + '"></span></li><li class="wickedpicker__controls__control--separator"><span class="wickedpicker__controls__control--separator-inner">:</span></li><li class="wickedpicker__controls__control"><span class="' + this.options.upArrow + '"></span><span class="wickedpicker__controls__control--minutes" tabindex="-1">00</span><span class="' + this.options.downArrow + '"></span></li>';
  162.                 if (this.options.showSeconds) {
  163.                     picker += '<li class="wickedpicker__controls__control--separator"><span class="wickedpicker__controls__control--separator-inner">:</span></li><li class="wickedpicker__controls__control"><span class="' + this.options.upArrow + '"></span><span class="wickedpicker__controls__control--seconds" tabindex="-1">00</span><span class="' + this.options.downArrow + '"></span> </li>';
  164.                 }
  165.                 picker += '<li class="wickedpicker__controls__control"><span class="' + this.options.upArrow + '"></span><span class="wickedpicker__controls__control--meridiem" tabindex="-1">AM</span><span class="' + this.options.downArrow + '"></span></li></ul></div>';
  166.                 $('body').append(picker);
  167.                 this.attachKeyboardEvents();
  168.             }
  169.         },
  170.  
  171.         /*
  172.          * Hides the meridiem control if this timepicker is a 24 hour clock
  173.          */
  174.         showHideMeridiemControl: function () {
  175.             if (this.options.twentyFour === false) {
  176.                 $(this.meridiemElem).parent().show();
  177.             }
  178.             else {
  179.                 $(this.meridiemElem).parent().hide();
  180.             }
  181.         },
  182.  
  183.         /*
  184.          * Hides the seconds control if this timepicker has showSeconds set to true
  185.          */
  186.         showHideSecondsControl: function () {
  187.             if (this.options.showSeconds) {
  188.                 $(this.secondsElem).parent().show();
  189.             }
  190.             else {
  191.                 $(this.secondsElem).parent().hide();
  192.             }
  193.         },
  194.  
  195.         /*
  196.          * Bind the click events to the input
  197.          *
  198.          * @param {object} The input element
  199.          */
  200.         attach: function (element) {
  201.             var self = this;
  202.  
  203.             if (this.options.clearable) {
  204.                 self.makePickerInputClearable(element);
  205.             }
  206.  
  207.             $(element).attr('tabindex', 0);
  208.             $(element).on('click focus', function (event) {
  209.                 //Prevent multiple firings
  210.                 if ($(self.timepicker).is(':hidden')) {
  211.                     self.showPicker($(this));
  212.                     $(self.hoursElem).focus();
  213.                 }
  214.             });
  215.            
  216.             //Handle click events for closing Wickedpicker
  217.             var clickHandler = function (event) {
  218.                 //Only fire the hide event when you have to
  219.                 if ($(self.timepicker).is(':visible')) {
  220.                     //Clicking the X
  221.                     if ($(event.target).is(self.close)) {
  222.                         self.hideTimepicker(element);
  223.                     } else if ($(event.target).closest(self.timepicker).length || $(event.target).closest($('.hasWickedpicker')).length) { //Clicking the Wickedpicker or one of it's inputs
  224.                         event.stopPropagation();
  225.                     } else {   //Everything else
  226.                         self.hideTimepicker(element);
  227.                     }
  228.                 }
  229.             };
  230.             $(document).off('click', clickHandler).on('click', clickHandler);
  231.         },
  232.  
  233.         /**
  234.          * Added keyboard functionality to improve usabil
  235.          */
  236.         attachKeyboardEvents: function () {
  237.             $(document).on('keydown', $.proxy(function (event) {
  238.                 switch (event.keyCode) {
  239.                     case 9:
  240.                         if (event.target.className !== 'hasWickedpicker') {
  241.                             $(this.close).trigger('click');
  242.                         }
  243.                         break;
  244.                     case 27:
  245.                         $(this.close).trigger('click');
  246.                         break;
  247.                     case 37: //Left arrow
  248.                         if (event.target.className !== this.hoursElem[0].className) {
  249.                             $(event.target).parent().prevAll('li').not(this.separator.selector).first().children()[1].focus();
  250.                         } else {
  251.                             $(event.target).parent().siblings(':last').children()[1].focus();
  252.                         }
  253.                         break;
  254.                     case 39: //Right arrow
  255.                         if (event.target.className !== this.meridiemElem[0].className) {
  256.                             $(event.target).parent().nextAll('li').not(this.separator.selector).first().children()[1].focus();
  257.                         } else {
  258.                             $(event.target).parent().siblings(':first').children()[1].focus();
  259.                         }
  260.                         break;
  261.                     case 38: //Up arrow
  262.                         $(':focus').prev().trigger('click');
  263.                         break;
  264.                     case 40: //Down arrow
  265.                         $(':focus').next().trigger('click');
  266.                         break;
  267.                     default:
  268.                         break;
  269.                 }
  270.             }, this));
  271.         },
  272.  
  273.         /*
  274.          * Set the time on the timepicker
  275.          *
  276.          * @param {object} The date being set
  277.          */
  278.         setTime: function (time) {
  279.             this.setHours(time.hours);
  280.             this.setMinutes(time.minutes);
  281.             this.setMeridiem(time.meridiem);
  282.             if (this.options.showSeconds) {
  283.                 this.setSeconds(time.seconds);
  284.             }
  285.         },
  286.  
  287.         /*
  288.          * Get the time from the timepicker
  289.          */
  290.         getTime: function () {
  291.             return [this.formatTime(this.getHours(), this.getMinutes(), this.getMeridiem(), this.getSeconds())];
  292.         },
  293.  
  294.         /*
  295.          * Set the timpicker's hour(s) value
  296.          *
  297.          * @param {string} hours
  298.          */
  299.         setHours: function (hours) {
  300.             var hour = new Date();
  301.             hour.setHours(hours);
  302.             var hoursText = this.parseHours(hour.getHours());
  303.             this.hoursElem.text(hoursText);
  304.             this.selectedHour = hoursText;
  305.         },
  306.  
  307.         /*
  308.          * Get the hour(s) value from the timepicker
  309.          *
  310.          * @return {integer}
  311.          */
  312.         getHours: function () {
  313.             var hours = new Date();
  314.             hours.setHours(this.hoursElem.text());
  315.             return hours.getHours();
  316.         },
  317.  
  318.         /*
  319.          * Returns the correct hour value based on the type of clock, 12 or 24 hour
  320.          *
  321.          * @param {integer} The hours value before parsing
  322.          *
  323.          * @return {string|integer}
  324.          */
  325.         parseHours: function (hours) {
  326.             return (this.options.twentyFour === false) ? ((hours + 11) % 12) + 1 : (hours < 10) ? '0' + hours : hours;
  327.         },
  328.  
  329.         /*
  330.          * Sets the timpicker's minutes value
  331.          *
  332.          * @param {string} minutes
  333.          */
  334.         setMinutes: function (minutes) {
  335.             var minute = new Date();
  336.             minute.setMinutes(minutes);
  337.             var minutesText = minute.getMinutes();
  338.             var min = this.parseSecMin(minutesText);
  339.             this.minutesElem.text(min);
  340.             this.selectedMin = min;
  341.         },
  342.  
  343.         /*
  344.          * Get the minutes value from the timepicker
  345.          *
  346.          * @return {integer}
  347.          */
  348.         getMinutes: function () {
  349.             var minutes = new Date();
  350.             minutes.setMinutes(this.minutesElem.text());
  351.             return minutes.getMinutes();
  352.         },
  353.  
  354.  
  355.         /*
  356.          * Return a human-readable minutes/seconds value
  357.          *
  358.          * @param {string} value seconds or minutes
  359.          *
  360.          * @return {string|integer}
  361.          */
  362.         parseSecMin: function (value) {
  363.             return ((value < 10) ? '0' : '') + value;
  364.         },
  365.  
  366.         /*
  367.          * Set the timepicker's meridiem value, AM or PM
  368.          *
  369.          * @param {string} The new meridiem
  370.          */
  371.         setMeridiem: function (inputMeridiem) {
  372.             var newMeridiem = '';
  373.             if (inputMeridiem === undefined) {
  374.                 var meridiem = this.getMeridiem();
  375.                 newMeridiem = (meridiem === 'PM') ? 'AM' : 'PM';
  376.             } else {
  377.                 newMeridiem = inputMeridiem;
  378.             }
  379.             this.meridiemElem.text(newMeridiem);
  380.             this.selectedMeridiem = newMeridiem;
  381.         },
  382.  
  383.         /*
  384.          * Get the timepicker's meridiem value, AM or PM
  385.          *
  386.          * @return {string}
  387.          */
  388.         getMeridiem: function () {
  389.             return this.meridiemElem.text();
  390.         },
  391.  
  392.         /*
  393.          * Set the timepicker's seconds value
  394.          *
  395.          * @param {string} seconds
  396.          */
  397.         setSeconds: function (seconds) {
  398.             var second = new Date();
  399.             second.setSeconds(seconds);
  400.             var secondsText = second.getSeconds();
  401.             var sec = this.parseSecMin(secondsText);
  402.             this.secondsElem.text(sec);
  403.             this.selectedSec = sec;
  404.         },
  405.  
  406.         /*
  407.          * Get the timepicker's seconds value
  408.          *
  409.          * return {string}
  410.          */
  411.         getSeconds: function () {
  412.             var seconds = new Date();
  413.             seconds.setSeconds(this.secondsElem.text());
  414.             return seconds.getSeconds();
  415.         },
  416.  
  417.         /*
  418.          * Get the correct meridiem based on the hours given
  419.          *
  420.          * @param {string|integer} hours
  421.          *
  422.          * @return {string}
  423.          */
  424.         parseMeridiem: function (hours) {
  425.             return (hours > 11) ? 'PM' : 'AM';
  426.         },
  427.  
  428.         /*
  429.          * Handles time incrementing and decrementing and passes
  430.          * the operator, '+' or '-', the input to be set after the change
  431.          * and the current arrow clicked, to decipher if hours, ninutes, or meridiem.
  432.          *
  433.          * @param {object} The input element
  434.          */
  435.         handleTimeAdjustments: function (element) {
  436.             var timeOut = 0;
  437.             //Click and click and hold timepicker incrementer and decrementer
  438.             $(this.up).add(this.down).off('mousedown click touchstart').on('mousedown click', {
  439.                 'Wickedpicker': this,
  440.                 'input': element
  441.             }, function (event) {
  442.                 var operator = (this.className.indexOf('up') > -1) ? '+' : '-';
  443.                 var passedData = event.data;
  444.                 if (event.type == 'mousedown') {
  445.                     timeOut = setInterval($.proxy(function (args) {
  446.                         args.Wickedpicker.changeValue(operator, args.input, this);
  447.                     }, this, {'Wickedpicker': passedData.Wickedpicker, 'input': passedData.input}), 200);
  448.                 } else {
  449.                     passedData.Wickedpicker.changeValue(operator, passedData.input, this);
  450.                 }
  451.             }).bind('mouseup touchend', function () {
  452.                 clearInterval(timeOut);
  453.             });
  454.         },
  455.  
  456.         /*
  457.          * Change the timepicker's time base on what is clicked
  458.          *
  459.          * @param {string} The + or - operator
  460.          * @param {object} The timepicker's associated input to be set post change
  461.          * @param {object} The DOM arrow object clicked, determines if it is hours,
  462.          * minutes, or meridiem base on the operator and its siblings
  463.          */
  464.         changeValue: function (operator, input, clicked) {
  465.             var target = (operator === '+') ? clicked.nextSibling : clicked.previousSibling;
  466.             var targetClass = $(target).attr('class');
  467.             if (targetClass.endsWith('hours')) {
  468.                 this.setHours(eval(this.getHours() + operator + 1));
  469.             } else if (targetClass.endsWith('minutes')) {
  470.                 this.setMinutes(eval(this.getMinutes() + operator + this.options.minutesInterval));
  471.             } else if (targetClass.endsWith('seconds')) {
  472.                 this.setSeconds(eval(this.getSeconds() + operator + this.options.secondsInterval));
  473.             } else {
  474.                 this.setMeridiem();
  475.             }
  476.             this.setText(input);
  477.         },
  478.  
  479.  
  480.         /*
  481.          * Sets the give input's text to the current timepicker's time
  482.          *
  483.          * @param {object} The input element
  484.          */
  485.         setText: function (input) {
  486.             $(input).val(this.formatTime(this.selectedHour, this.selectedMin, this.selectedMeridiem, this.selectedSec)).change();
  487.         },
  488.  
  489.         /*
  490.          * Get the given input's value
  491.          *
  492.          * @param {object} The input element
  493.          *
  494.          * @return {string}
  495.          */
  496.         getText: function (input) {
  497.             return $(input).val();
  498.         },
  499.  
  500.         /*
  501.          * Returns the correct time format as a string
  502.          *
  503.          * @param {string} hour
  504.          * @param {string} minutes
  505.          * @param {string} meridiem
  506.          *
  507.          * @return {string}
  508.          */
  509.         formatTime: function (hour, min, meridiem, seconds) {
  510.             var formattedTime = hour + ' : ' + min;
  511.             if (this.options.twentyFour) {
  512.                 formattedTime = hour + ' : ' + min;
  513.             }
  514.             if (this.options.showSeconds) {
  515.                 formattedTime += ' : ' + seconds;
  516.             }
  517.             if (this.options.twentyFour === false) {
  518.                 formattedTime += ' ' + meridiem;
  519.             }
  520.             return formattedTime;
  521.         },
  522.  
  523.         /**
  524.          *  Apply the hover class to the arrows and close icon fonts
  525.          */
  526.         setHoverState: function () {
  527.             var self = this;
  528.             if (!isMobile()) {
  529.                 $(this.up).add(this.down).add(this.close).hover(function () {
  530.                     $(this).toggleClass(self.options.hoverState);
  531.                 });
  532.             }
  533.         },
  534.  
  535.         /**
  536.          * Wrapping the given input field with the clearable container
  537.          * , add a span that will contain the x, and bind the clear
  538.          * input event to the span
  539.          *
  540.          * @param input
  541.          */
  542.         makePickerInputClearable: function(input) {
  543.             $(input).wrap('<div class="clearable-picker"></div>').after('<span data-clear-picker>&times;</span>');
  544.  
  545.             //When the x is clicked, clear its sibling input field
  546.             $('[data-clear-picker]').on('click', function(event) {
  547.                $(this).siblings('.hasWickedpicker').val('');
  548.             });
  549.         },
  550.  
  551.         /**
  552.          * Convert the options time string format
  553.          * to an array
  554.          *
  555.          * returns => [hours, minutes, seconds]
  556.          *
  557.          * @param stringTime
  558.          * @returns {*}
  559.          */
  560.         timeArrayFromString: function (stringTime) {
  561.             if (stringTime.length) {
  562.                 var time = stringTime.split(':');
  563.                 time[2] = (time.length < 3) ? '00' : time[2];
  564.                 return time;
  565.             }
  566.             return false;
  567.         },
  568.  
  569.         //public functions
  570.         /*
  571.          * Returns the requested input element's value
  572.          */
  573.         _time: function () {
  574.             var inputValue = $(this.element).val();
  575.             return (inputValue === '') ? this.formatTime(this.selectedHour, this.selectedMin, this.selectedMeridiem, this.selectedSec) : inputValue;
  576.         }
  577.     });
  578.  
  579.     //optional index if multiple inputs share the same class
  580.     $.fn[pluginName] = function (options, index) {
  581.         if (!$.isFunction(Wickedpicker.prototype['_' + options])) {
  582.             return this.each(function () {
  583.                 if (!$.data(this, "plugin_" + pluginName)) {
  584.                     $.data(this, "plugin_" + pluginName, new Wickedpicker(this, options));
  585.                 }
  586.             });
  587.         }
  588.         else if ($(this).hasClass('hasWickedpicker')) {
  589.             if (index !== undefined) {
  590.                 return $.data($(this)[index], 'plugin_' + pluginName)['_' + options]();
  591.             }
  592.             else {
  593.                 return $.data($(this)[0], 'plugin_' + pluginName)['_' + options]();
  594.             }
  595.         }
  596.     };
  597.  
  598. })(jQuery, window, document);

Raw Paste


Login or Register to edit or fork this paste. It's free.