PageSourceSearch

https://www.wiener.hr/assets/js/picker.js

js wiener.hr collected 2026-10-06 21:34:20 UTC 112,014 bytes, 3,608 lines download raw bytes

1/* WEB Marketing INIT */
2function initDatePicker(elementID, dateFormat, closeCallback) {
3  var selector = elementID;
4  if (typeof (selector) == 'string') {
5    selector = (selector[0] != '#' && selector[0] != '.' ? '#' : '') + selector;
6  }
7
8  jQuery(selector).each(function () {
9    var target = jQuery(this);
10
11    if (target.siblings('.input-calendar').length == 0) {
12      target.after('<a class="input-calendar" href="javascript:void(0)"><img src="/assets/images/interface/calendar.png" alt="Calendar" /></a>');
13    }
14
15    var targetLink = target.parent().find('.input-calendar');
16    setPopupOnClick(target, targetLink);
17
18    if (target.closest('.date-input').length == 0) {
19      target.parent().parent().addClass('date-input');
20    }
21
22    var val = target.val();
23
24    target.addClass('fieldset__input js__datepicker picker__input');
25    /// "ako je touch stavim read only na input kontrolu da se ne pojavi tipkovnica" - ovo je izmijenjeno i stavljeno uvijek false
26    // TODO  bug u verziji datepickera v3.5.5, ako je editable:true ne radi u IE-u, provjeriti fix kada izađe:
27    //https://github.com/amsul/pickadate.js/issues/640
28    target.pickadate({
29      format: dateFormat,
30      formatSubmit: 'dd.mm.yyyy.',
31      editable: true,
32      container: '#frmMain',
33      onOpen: function () {
34        jQuery('form').addClass('date-picker-active');
35      },
36      onClose: function () {
37        jQuery('form').removeClass('date-picker-active');
38        if (closeCallback) closeCallback();
39       
40      }
41    });
42
43    if (val && val.length > 0) {
44      target.pickadate('picker').set('select', val, { format: dateFormat });
45    }
46  });
47}
48
49/* WEB Marketing - postavlja click event clickElementID koji će na click dignuti datepicker popup */
50function setPopupOnClick(inputElementID, clickElementID) {
51  var selector = inputElementID;
52  if (typeof (selector) == 'string') {
53    selector = (selector[0] != '#' && selector[0] != '.' ? '#' : '') + selector;
54  }
55  var inputElement = jQuery(selector);
56
57  var clickselector = clickElementID;
58  if (typeof (clickselector) == 'string') {
59    clickselector = (clickselector[0] != '#' && clickselector[0] != '.' ? '#' : '') + clickselector;
60  }
61  var clickElement = jQuery(clickselector);
62
63  jQuery(clickElement).bind('click', function (e) {
64    jQuery(inputElement).pickadate('picker').open();
65    e.stopPropagation();
66  });
67  jQuery(inputElement).bind('click', function (e) {
68    jQuery(clickElement).trigger('click');
69  });
70
71  jQuery(inputElement).on('keydown', function (e) {
72    if (e.which === 32) { // space
73      jQuery(inputElement).pickadate('picker').open();
74   
75    }
76  });
77}
78
79/*!
80 * pickadate.js v3.5.5, 2015/02/08
81 * By Amsul, http://amsul.ca
82 * Hosted on http://amsul.github.io/pickadate.js
83 * Licensed under MIT
84 */
85
86(function (factory) {
87
88  // AMD.
89  if (typeof define == 'function' && define.amd)
90    define('picker', ['jquery'], factory)
91
92    // Node.js/browserify.
93  else if (typeof exports == 'object')
94    module.exports = factory(require('jquery'))
95
96    // Browser globals.
97  else this.Picker = factory(jQuery)
98
99}(function ($) {
100
101  var $window = $(window)
102  var $document = $(document)
103  var $html = $(document.documentElement)
104  var supportsTransitions = function () { return document.body.style.transition != null; }
105
106
107  /**
108   * The picker constructor that creates a blank picker.
109   */
110  function PickerConstructor(ELEMENT, NAME, COMPONENT, OPTIONS) {
111
112    // If there’s no element, return the picker constructor.
113    if (!ELEMENT) return PickerConstructor
114
115
116    var
117        IS_DEFAULT_THEME = false,
118
119
120        // The state of the picker.
121        STATE = {
122          id: ELEMENT.id || 'P' + Math.abs(~~(Math.random() * new Date()))
123        },
124
125
126        // Merge the defaults and options passed.
127        SETTINGS = COMPONENT ? $.extend(true, {}, COMPONENT.defaults, OPTIONS) : OPTIONS || {},
128
129
130        // Merge the default classes with the settings classes.
131        CLASSES = $.extend({}, PickerConstructor.klasses(), SETTINGS.klass),
132
133
134        // The element node wrapper into a jQuery object.
135        $ELEMENT = $(ELEMENT),
136
137
138        // Pseudo picker constructor.
139        PickerInstance = function () {
140          return this.start()
141        },
142
143
144        // The picker prototype.
145        P = PickerInstance.prototype = {
146
147          constructor: PickerInstance,
148
149          $node: $ELEMENT,
150
151
152          /**
153           * Initialize everything
154           */
155          start: function () {
156
157            // If it’s already started, do nothing.
158            if (STATE && STATE.start) return P
159
160
161            // Update the picker states.
162            STATE.methods = {}
163            STATE.start = true
164            STATE.open = false
165            STATE.type = ELEMENT.type
166
167
168            // Confirm focus state, convert into text input to remove UA stylings,
169            // and set as readonly to prevent keyboard popup.
170            ELEMENT.autofocus = ELEMENT == getActiveElement()
171            ELEMENT.readOnly = !SETTINGS.editable
172            ELEMENT.id = ELEMENT.id || STATE.id
173            if (ELEMENT.type != 'text') {
174              ELEMENT.type = 'text'
175            }
176
177
178            // Create a new picker component with the settings.
179            P.component = new COMPONENT(P, SETTINGS)
180
181
182            // Create the picker root and then prepare it.
183            P.$root = $('<div class="' + CLASSES.picker + '" id="' + ELEMENT.id + '_root" />')
184            prepareElementRoot()
185
186
187            // Create the picker holder and then prepare it.
188            P.$holder = $(createWrappedComponent()).appendTo(P.$root)
189            prepareElementHolder()
190
191
192            // If there’s a format for the hidden input element, create the element.
193            if (SETTINGS.formatSubmit) {
194              prepareElementHidden()
195            }
196
197
198            // Prepare the input element.
199            prepareElement()
200
201
202            // Insert the hidden input as specified in the settings.
203            if (SETTINGS.containerHidden) $(SETTINGS.containerHidden).append(P._hidden)
204            else $ELEMENT.after(P._hidden)
205
206
207            // Insert the root as specified in the settings.
208            if (SETTINGS.container) $(SETTINGS.container).append(P.$root)
209            else $ELEMENT.after(P.$root)
210
211
212            // Bind the default component and settings events.
213            P.on({
214              start: P.component.onStart,
215              render: P.component.onRender,
216              stop: P.component.onStop,
217              open: P.component.onOpen,
218              close: P.component.onClose,
219              set: P.component.onSet
220            }).on({
221              start: SETTINGS.onStart,
222              render: SETTINGS.onRender,
223              stop: SETTINGS.onStop,
224              open: SETTINGS.onOpen,
225              close: SETTINGS.onClose,
226              set: SETTINGS.onSet
227            })
228
229
230            // Once we’re all set, check the theme in use.
231            IS_DEFAULT_THEME = isUsingDefaultTheme(P.$holder[0])
232
233
234            // If the element has autofocus, open the picker.
235            if (ELEMENT.autofocus) {
236              P.open()
237            }
238
239
240            // Trigger queued the “start” and “render” events.
241            return P.trigger('start').trigger('render')
242          }, //start
243
244
245          /**
246           * Render a new picker
247           */
248          render: function (entireComponent) {
249
250            // Insert a new component holder in the root or box.
251            if (entireComponent) {
252              P.$holder = createWrappedComponent()
253              P.$root.html(P.$holder)
254            }
255            else P.$root.find('.' + CLASSES.box).html(P.component.nodes(STATE.open))
256
257            // Trigger the queued “render” events.
258            return P.trigger('render')
259          }, //render
260
261
262          /**
263           * Destroy everything
264           */
265          stop: function () {
266
267            // If it’s already stopped, do nothing.
268            if (!STATE.start) return P
269
270            // Then close the picker.
271            P.close()
272
273            // Remove the hidden field.
274            if (P._hidden) {
275              P._hidden.parentNode.removeChild(P._hidden)
276            }
277
278            // Remove the root.
279            P.$root.remove()
280
281            // Remove the input class, remove the stored data, and unbind
282            // the events (after a tick for IE - see `P.close`).
283            $ELEMENT.removeClass(CLASSES.input).removeData(NAME)
284            setTimeout(function () {
285              $ELEMENT.off('.' + STATE.id)
286            }, 0)
287
288            // Restore the element state
289            ELEMENT.type = STATE.type
290            ELEMENT.readOnly = false
291
292            // Trigger the queued “stop” events.
293            P.trigger('stop')
294
295            // Reset the picker states.
296            STATE.methods = {}
297            STATE.start = false
298
299            return P
300          }, //stop
301
302
303          /**
304           * Open up the picker
305           */
306          open: function (dontGiveFocus) {
307
308            // If it’s already open, do nothing.
309            if (STATE.open) return P
310
311            // Add the “active” class.
312            $ELEMENT.addClass(CLASSES.active)
313            aria(ELEMENT, 'expanded', true)
314
315            // * A Firefox bug, when `html` has `overflow:hidden`, results in
316            //   killing transitions :(. So add the “opened” state on the next tick.
317            //   Bug: https://bugzilla.mozilla.org/show_bug.cgi?id=625289
318            setTimeout(function () {
319
320              // Add the “opened” class to the picker root.
321              P.$root.addClass(CLASSES.opened)
322              aria(P.$root[0], 'hidden', false)
323
324            }, 0)
325
326            // If we have to give focus, bind the element and doc events.
327            if (dontGiveFocus !== false) {
328
329              // Set it as open.
330              STATE.open = true
331
332              // Prevent the page from scrolling.
333              if (IS_DEFAULT_THEME) {
334                $html.
335                    css('overflow', 'hidden').
336                    css('padding-right', '+=' + getScrollbarWidth())
337              }
338
339              // Pass focus to the root element’s jQuery object.
340              focusPickerOnceOpened()
341
342              // Bind the document events.
343              $document.on('click.' + STATE.id + ' focusin.' + STATE.id, function (event) {
344
345                var target = event.target
346
347                // If the target of the event is not the element, close the picker picker.
348                // * Don’t worry about clicks or focusins on the root because those don’t bubble up.
349                //   Also, for Firefox, a click on an `option` element bubbles up directly
350                //   to the doc. So make sure the target wasn't the doc.
351                // * In Firefox stopPropagation() doesn’t prevent right-click events from bubbling,
352                //   which causes the picker to unexpectedly close when right-clicking it. So make
353                //   sure the event wasn’t a right-click.
354                if (target != ELEMENT && target != document && event.which != 3) {
355
356                  // If the target was the holder that covers the screen,
357                  // keep the element focused to maintain tabindex.
358                  P.close(target === P.$holder[0])
359                }
360
361              }).on('keydown.' + STATE.id, function (event) {
362
363                var
364                    // Get the keycode.
365                    keycode = event.keyCode,
366
367                    // Translate that to a selection change.
368                    keycodeToMove = P.component.key[keycode],
369
370                    // Grab the target.
371                    target = event.target
372
373
374                // On escape, close the picker and give focus.
375                if (keycode == 27) {
376                  P.close(true)
377                }
378
379
380                  // Check if there is a key movement or “enter” keypress on the element.
381                else if (target == P.$holder[0] && (keycodeToMove || keycode == 13)) {
382
383                  // Prevent the default action to stop page movement.
384                  event.preventDefault()
385
386                  // Trigger the key movement action.
387                  if (keycodeToMove) {
388                    PickerConstructor._.trigger(P.component.key.go, P, [PickerConstructor._.trigger(keycodeToMove)])
389                  }
390
391                    // On “enter”, if the highlighted item isn’t disabled, set the value and close.
392                  else if (!P.$root.find('.' + CLASSES.highlighted).hasClass(CLASSES.disabled)) {
393                    P.set('select', P.component.item.highlight)
394                    if (SETTINGS.closeOnSelect) {
395                      P.close(true)
396                    }
397                  }
398                }
399
400
401                  // If the target is within the root and “enter” is pressed,
402                  // prevent the default action and trigger a click on the target instead.
403                else if ($.contains(P.$root[0], target) && keycode == 13) {
404                  event.preventDefault()
405                  target.click()
406                }
407              })
408            }
409
410            // Trigger the queued “open” events.
411            return P.trigger('open')
412          }, //open
413
414
415          /**
416           * Close the picker
417           */
418          close: function (giveFocus) {
419
420            // If we need to give focus, do it before changing states.
421            if (giveFocus) {
422              if (SETTINGS.editable) {
423                ELEMENT.focus()
424              }
425              else {
426                // ....ah yes! It would’ve been incomplete without a crazy workaround for IE :|
427                // The focus is triggered *after* the close has completed - causing it
428                // to open again. So unbind and rebind the event at the next tick.
429                P.$holder.off('focus.toOpen').focus()
430                setTimeout(function () {
431                  P.$holder.on('focus.toOpen', handleFocusToOpenEvent)
432                }, 0)
433              }
434            }
435
436            // Remove the “active” class.
437            $ELEMENT.removeClass(CLASSES.active)
438            aria(ELEMENT, 'expanded', false)
439
440            // * A Firefox bug, when `html` has `overflow:hidden`, results in
441            //   killing transitions :(. So remove the “opened” state on the next tick.
442            //   Bug: https://bugzilla.mozilla.org/show_bug.cgi?id=625289
443            setTimeout(function () {
444
445              // Remove the “opened” and “focused” class from the picker root.
446              P.$root.removeClass(CLASSES.opened + ' ' + CLASSES.focused)
447              aria(P.$root[0], 'hidden', true)
448
449            }, 0)
450
451            // If it’s already closed, do nothing more.
452            if (!STATE.open) return P
453
454            // Set it as closed.
455            STATE.open = false
456
457            // Allow the page to scroll.
458            if (IS_DEFAULT_THEME) {
459              $html.
460                  css('overflow', '').
461                  css('padding-right', '-=' + getScrollbarWidth())
462            }
463
464            // Unbind the document events.
465            $document.off('.' + STATE.id)
466
467            // Trigger the queued “close” events.
468            return P.trigger('close')
469          }, //close
470
471
472          /**
473           * Clear the values
474           */
475          clear: function (options) {
476            return P.set('clear', null, options)
477          }, //clear
478
479
480          /**
481           * Set something
482           */
483          set: function (thing, value, options) {
484
485            var thingItem, thingValue,
486                thingIsObject = $.isPlainObject(thing),
487                thingObject = thingIsObject ? thing : {}
488
489            // Make sure we have usable options.
490            options = thingIsObject && $.isPlainObject(value) ? value : options || {}
491
492            if (thing) {
493
494              // If the thing isn’t an object, make it one.
495              if (!thingIsObject) {
496                thingObject[thing] = value
497              }
498
499              // Go through the things of items to set.
500              for (thingItem in thingObject) {
501
502                // Grab the value of the thing.
503                thingValue = thingObject[thingItem]
504
505                // First, if the item exists and there’s a value, set it.
506                if (thingItem in P.component.item) {
507                  if (thingValue === undefined) thingValue = null
508                  P.component.set(thingItem, thingValue, options)
509                }
510
511                // Then, check to update the element value and broadcast a change.
512                if (thingItem == 'select' || thingItem == 'clear') {
513                  $ELEMENT.
514                      val(thingItem == 'clear' ? '' : P.get(thingItem, SETTINGS.format)).
515                      trigger('change')
516                }
517              }
518
519              // Render a new picker.
520              P.render()
521            }
522
523            // When the method isn’t muted, trigger queued “set” events and pass the `thingObject`.
524            return options.muted ? P : P.trigger('set', thingObject)
525          }, //set
526
527
528          /**
529           * Get something
530           */
531          get: function (thing, format) {
532
533            // Make sure there’s something to get.
534            thing = thing || 'value'
535
536            // If a picker state exists, return that.
537            if (STATE[thing] != null) {
538              return STATE[thing]
539            }
540
541            // Return the submission value, if that.
542            if (thing == 'valueSubmit') {
543              if (P._hidden) {
544                return P._hidden.value
545              }
546              thing = 'value'
547            }
548
549            // Return the value, if that.
550            if (thing == 'value') {
551              return ELEMENT.value
552            }
553
554            // Check if a component item exists, return that.
555            if (thing in P.component.item) {
556              if (typeof format == 'string') {
557                var thingValue = P.component.get(thing)
558                return thingValue ?
559                    PickerConstructor._.trigger(
560                        P.component.formats.toString,
561                        P.component,
562                        [format, thingValue]
563                    ) : ''
564              }
565              return P.component.get(thing)
566            }
567          }, //get
568
569
570
571          /**
572           * Bind events on the things.
573           */
574          on: function (thing, method, internal) {
575
576            var thingName, thingMethod,
577                thingIsObject = $.isPlainObject(thing),
578                thingObject = thingIsObject ? thing : {}
579
580            if (thing) {
581
582              // If the thing isn’t an object, make it one.
583              if (!thingIsObject) {
584                thingObject[thing] = method
585              }
586
587              // Go through the things to bind to.
588              for (thingName in thingObject) {
589
590                // Grab the method of the thing.
591                thingMethod = thingObject[thingName]
592
593                // If it was an internal binding, prefix it.
594                if (internal) {
595                  thingName = '_' + thingName
596                }
597
598                // Make sure the thing methods collection exists.
599                STATE.methods[thingName] = STATE.methods[thingName] || []
600
601                // Add the method to the relative method collection.
602                STATE.methods[thingName].push(thingMethod)
603              }
604            }
605
606            return P
607          }, //on
608
609
610
611          /**
612           * Unbind events on the things.
613           */
614          off: function () {
615            var i, thingName,
616                names = arguments;
617            for (i = 0, namesCount = names.length; i < namesCount; i += 1) {
618              thingName = names[i]
619              if (thingName in STATE.methods) {
620                delete STATE.methods[thingName]
621              }
622            }
623            return P
624          },
625
626
627          /**
628           * Fire off method events.
629           */
630          trigger: function (name, data) {
631            var _trigger = function (name) {
632              var methodList = STATE.methods[name]
633              if (methodList) {
634                methodList.map(function (method) {
635                  PickerConstructor._.trigger(method, P, [data])
636                })
637              }
638            }
639            _trigger('_' + name)
640            _trigger(name)
641            return P
642          }
642 //trigger
643        } //PickerInstance.prototype
644
645
646    /**
647     * Wrap the picker holder components together.
648     */
649    function createWrappedComponent() {
650
651      // Create a picker wrapper holder
652      return PickerConstructor._.node('div',
653
654          // Create a picker wrapper node
655          PickerConstructor._.node('div',
656
657              // Create a picker frame
658              PickerConstructor._.node('div',
659
660                  // Create a picker box node
661                  PickerConstructor._.node('div',
662
663                      // Create the components nodes.
664                      P.component.nodes(STATE.open),
665
666                      // The picker box class
667                      CLASSES.box
668                  ),
669
670                  // Picker wrap class
671                  CLASSES.wrap
672              ),
673
674              // Picker frame class
675              CLASSES.frame
676          ),
677
678          // Picker holder class
679          CLASSES.holder,
680
681          'tabindex="-1"'
682      ) //endreturn
683    } //createWrappedComponent
684
685
686
687    /**
688     * Prepare the input element with all bindings.
689     */
690    function prepareElement() {
691
692      $ELEMENT.
693
694          // Store the picker data by component name.
695          data(NAME, P).
696
697          // Add the “input” class name.
698          addClass(CLASSES.input).
699
700          // If there’s a `data-value`, update the value of the element.
701          val($ELEMENT.data('value') ?
702              P.get('select', SETTINGS.format) :
703              ELEMENT.value
704          )
705
706
707      // Only bind keydown events if the element isn’t editable.
708      if (!SETTINGS.editable) {
709
710        $ELEMENT.
711
712            // On focus/click, open the picker.
713            on('focus.' + STATE.id + ' click.' + STATE.id, function (event) {
714              event.preventDefault()
715              P.open()
716            }).
717
718            // Handle keyboard event based on the picker being opened or not.
719            on('keydown.' + STATE.id, handleKeydownEvent)
720      }
721
722
723      // Update the aria attributes.
724      aria(ELEMENT, {
725        haspopup: true,
726        expanded: false,
727        readonly: false,
728        owns: ELEMENT.id + '_root'
729      })
730    }
731
732
733    /**
734     * Prepare the root picker element with all bindings.
735     */
736    function prepareElementRoot() {
737      aria(P.$root[0], 'hidden', true)
738    }
739
740
741    /**
742     * Prepare the holder picker element with all bindings.
743     */
744    function prepareElementHolder() {
745
746      P.$holder.
747
748          on({
749
750            // For iOS8.
751            keydown: handleKeydownEvent,
752
753            'focus.toOpen': handleFocusToOpenEvent,
754
755            blur: function () {
756              // Remove the “target” class.
757              $ELEMENT.removeClass(CLASSES.target)
758            },
759
760            // When something within the holder is focused, stop from bubbling
761            // to the doc and remove the “focused” state from the root.
762            focusin: function (event) {
763              P.$root.removeClass(CLASSES.focused)
764              event.stopPropagation()
765            },
766
767            // When something within the holder is clicked, stop it
768            // from bubbling to the doc.
769            'mousedown click': function (event) {
770
771              var target = event.target
772
773              // Make sure the target isn’t the root holder so it can bubble up.
774              if (target != P.$holder[0]) {
775
776                event.stopPropagation()
777
778                // * For mousedown events, cancel the default action in order to
779                //   prevent cases where focus is shifted onto external elements
780                //   when using things like jQuery mobile or MagnificPopup (ref: #249 & #120).
781                //   Also, for Firefox, don’t prevent action on the `option` element.
782                if (event.type == 'mousedown' && !$(target).is('input, select, textarea, button, option')) {
783
784                  event.preventDefault()
785
786                  // Re-focus onto the holder so that users can click away
787                  // from elements focused within the picker.
788                  P.$holder[0].focus()
789                }
790              }
791            }
792
793          }).
794
795          // If there’s a click on an actionable element, carry out the actions.
796          on('click', '[data-pick], [data-nav], [data-clear], [data-close]', function () {
797
798            var $target = $(this),
799                targetData = $target.data(),
800                targetDisabled = $target.hasClass(CLASSES.navDisabled) || $target.hasClass(CLASSES.disabled),
801
802                // * For IE, non-focusable elements can be active elements as well
803                //   (http://stackoverflow.com/a/2684561).
804                activeElement = getActiveElement()
805            activeElement = activeElement && (activeElement.type || activeElement.href)
806
807            // If it’s disabled or nothing inside is actively focused, re-focus the element.
808            if (targetDisabled || activeElement && !$.contains(P.$root[0], activeElement)) {
809              P.$holder[0].focus()
810            }
811
812            // If something is superficially changed, update the `highlight` based on the `nav`.
813            if (!targetDisabled && targetData.nav) {
814              P.set('highlight', P.component.item.highlight, { nav: targetData.nav })
815            }
816
817              // If something is picked, set `select` then close with focus.
818            else if (!targetDisabled && 'pick' in targetData) {
819              P.set('select', targetData.pick)
820              if (SETTINGS.closeOnSelect) {
821                P.close(true)
822              }
823            }
824
825              // If a “clear” button is pressed, empty the values and close with focus.
826            else if (targetData.clear) {
827              P.clear()
828              if (SETTINGS.closeOnClear) {
829                P.close(true)
830              }
831            }
832
833            else if (targetData.close) {
834              P.close(true)
835            }
836
837          }) //P.$holder
838
839    }
840
841
842    /**
843     * Prepare the hidden input element along with all bindings.
844     */
845    function prepareElementHidden() {
846
847      var name
848
849      if (SETTINGS.hiddenName === true) {
850        name = ELEMENT.name
851        ELEMENT.name = ''
852      }
853      else {
854        name = [
855            typeof SETTINGS.hiddenPrefix == 'string' ? SETTINGS.hiddenPrefix : '',
856            typeof SETTINGS.hiddenSuffix == 'string' ? SETTINGS.hiddenSuffix : '_submit'
857        ]
858        name = name[0] + ELEMENT.name + name[1]
859      }
860
861      P._hidden = $(
862          '<input ' +
863          'type=hidden ' +
864
865          // Create the name using the original input’s with a prefix and suffix.
866          'name="' + name + '"' +
867
868          // If the element has a value, set the hidden value as well.
869          (
870              $ELEMENT.data('value') || ELEMENT.value ?
871                  ' value="' + P.get('select', SETTINGS.formatSubmit) + '"' :
872                  ''
873          ) +
874          '>'
875      )[0]
876
877      $ELEMENT.
878
879          // If the value changes, update the hidden input with the correct format.
880          on('change.' + STATE.id, function () {
881            P._hidden.value = ELEMENT.value ?
882                P.get('select', SETTINGS.formatSubmit) :
883                ''
884          })
885    }
886
887
888    // Wait for transitions to end before focusing the holder. Otherwise, while
889    // using the `container` option, the view jumps to the container.
890    function focusPickerOnceOpened() {
891
892      if (IS_DEFAULT_THEME && supportsTransitions()) {
893        P.$holder.find('.' + CLASSES.frame).one('transitionend', function () {
894          P.$holder[0].focus()
895        })
896      }
897      else {
898        P.$holder[0].focus()
899      }
900    }
901
902
903    function handleFocusToOpenEvent(event) {
904
905      // Stop the event from propagating to the doc.
906      event.stopPropagation()
907
908      // Add the “target” class.
909      $ELEMENT.addClass(CLASSES.target)
910
911      // Add the “focused” class to the root.
912      P.$root.addClass(CLASSES.focused)
913
914      // And then finally open the picker.
915      P.open()
916    }
917
918
919    // For iOS8.
920    function handleKeydownEvent(event) {
921
922      var keycode = event.keyCode,
923
924          // Check if one of the delete keys was pressed.
925          isKeycodeDelete = /^(8|46)$/.test(keycode)
926
927      // For some reason IE clears the input value on “escape”.
928      if (keycode == 27) {
929        P.close(true)
930        return false
931      }
932
933      // Check if `space` or `delete` was pressed or the picker is closed with a key movement.
934      if (keycode == 32 || isKeycodeDelete || !STATE.open && P.component.key[keycode]) {
935
936        // Prevent it from moving the page and bubbling to doc.
937        event.preventDefault()
938        event.stopPropagation()
939
940        // If `delete` was pressed, clear the values and close the picker.
941        // Otherwise open the picker.
942        if (isKeycodeDelete) { P.clear().close() }
943        else { P.open() }
944      }
945    }
946
947
948    // Return a new picker instance.
949    return new PickerInstance()
950  } //PickerConstructor
951
952
953
954  /**
955   * The default classes and prefix to use for the HTML classes.
956   */
957  PickerConstructor.klasses = function (prefix) {
958    prefix = prefix || 'picker'
959    return {
960
961      picker: prefix,
962      opened: prefix + '--opened',
963      focused: prefix + '--focused',
964
965      input: prefix + '__input',
966      active: prefix + '__input--active',
967      target: prefix + '__input--target',
968
969      holder: prefix + '__holder',
970
971      frame: prefix + '__frame',
972      wrap: prefix + '__wrap',
973
974      box: prefix + '__box'
975    }
976  } //PickerConstructor.klasses
977
978
979
980  /**
981   * Check if the default theme is being used.
982   */
983  function isUsingDefaultTheme(element) {
984
985    var theme,
986        prop = 'position'
987
988    // For IE.
989    if (element.currentStyle) {
990      theme = element.currentStyle[prop]
991    }
992
993      // For normal browsers.
994    else if (window.getComputedStyle) {
995      theme = getComputedStyle(element)[prop]
996    }
997
998    return theme == 'fixed'
999  }
1000
1001
1002
1003  /**
1004   * Get the width of the browser’s scrollbar.
1005   * Taken from: https://github.com/VodkaBears/Remodal/blob/master/src/jquery.remodal.js
1006   */
1007  function getScrollbarWidth() {
1008
1009    if ($html.height() <= $window.height()) {
1010      return 0
1011    }
1012
1013    var $outer = $('<div style="visibility:hidden;width:100px" />').
1014        appendTo('body')
1015
1016    // Get the width without scrollbars.
1017    var widthWithoutScroll = $outer[0].offsetWidth
1018
1019    // Force adding scrollbars.
1020    $outer.css('overflow', 'scroll')
1021
1022    // Add the inner div.
1023    var $inner = $('<div style="width:100%" />').appendTo($outer)
1024
1025    // Get the width with scrollbars.
1026    var widthWithScroll = $inner[0].offsetWidth
1027
1028    // Remove the divs.
1029    $outer.remove()
1030
1031    // Return the difference between the widths.
1032    return widthWithoutScroll - widthWithScroll
1033  }
1034
1035
1036
1037  /**
1038   * PickerConstructor helper methods.
1039   */
1040  PickerConstructor._ = {
1041
1042    /**
1043     * Create a group of nodes. Expects:
1044     * `
1045        {
1046            min:    {Integer},
1047            max:    {Integer},
1048            i:      {Integer},
1049            node:   {String},
1050            item:   {Function}
1051        }
1052     * `
1053     */
1054    group: function (groupObject) {
1055
1056      var
1057          // Scope for the looped object
1058          loopObjectScope,
1059
1060          // Create the nodes list
1061          nodesList = '',
1062
1063          // The counter starts from the `min`
1064          counter = PickerConstructor._.trigger(groupObject.min, groupObject)
1065
1066
1067      // Loop from the `min` to `max`, incrementing by `i`
1068      for (; counter <= PickerConstructor._.trigger(groupObject.max, groupObject, [counter]) ; counter += groupObject.i) {
1069
1070        // Trigger the `item` function within scope of the object
1071        loopObjectScope = PickerConstructor._.trigger(groupObject.item, groupObject, [counter])
1072
1073        // Splice the subgroup and create nodes out of the sub nodes
1074        nodesList += PickerConstructor._.node(
1075            groupObject.node,
1076            loopObjectScope[0],   // the node
1077            loopObjectScope[1],   // the classes
1078            loopObjectScope[2]    // the attributes
1079        )
1080      }
1081
1082      // Return the list of nodes
1083      return nodesList
1084    }, //group
1085
1086
1087    /**
1088     * Create a dom node string
1089     */
1090    node: function (wrapper, item, klass, attribute) {
1091
1092      // If the item is false-y, just return an empty string
1093      if (!item) return ''
1094
1095      // If the item is an array, do a join
1096      item = $.isArray(item) ? item.join('') : item
1097
1098      // Check for the class
1099      klass = klass ? ' class="' + klass + '"' : ''
1100
1101      // Check for any attributes
1102      attribute = attribute ? ' ' + attribute : ''
1103
1104      // Return the wrapped item
1105      return '<' + wrapper + klass + attribute + '>' + item + '</' + wrapper + '>'
1106    }, //node
1107
1108
1109    /**
1110     * Lead numbers below 10 with a zero.
1111     */
1112    lead: function (number) {
1113      return (number < 10 ? '0' : '') + number
1114    },
1115
1116
1117    /**
1118     * Trigger a function otherwise return the value.
1119     */
1120    trigger: function (callback, scope, args) {
1121      return typeof callback == 'function' ? callback.apply(scope, args || []) : callback
1122    },
1123
1124
1125    /**
1126     * If the second character is a digit, length is 2 otherwise 1.
1127     */
1128    digits: function (string) {
1129      return (/\d/).test(string[1]) ? 2 : 1
1130    },
1131
1132
1133    /**
1134     * Tell if something is a date object.
1135     */
1136    isDate: function (value) {
1137      return {}.toString.call(value).indexOf('Date') > -1 && this.isInteger(value.getDate())
1138    },
1139
1140
1141    /**
1142     * Tell if something is an integer.
1143     */
1144    isInteger: function (value) {
1145      return {}.toString.call(value).indexOf('Number') > -1 && value % 1 === 0
1146    },
1147
1148
1149    /**
1150     * Create ARIA attribute strings.
1151     */
1152    ariaAttr: ariaAttr
1153  } //PickerConstructor._
1154
1155
1156
1157  /**
1158   * Extend the picker with a component and defaults.
1159   */
1160  PickerConstructor.extend = function (name, Component) {
1161
1162    // Extend jQuery.
1163    $.fn[name] = function (options, action) {
1164
1165      // Grab the component data.
1166      var componentData = this.data(name)
1167
1168      // If the picker is requested, return the data object.
1169      if (options == 'picker') {
1170        return componentData
1171      }
1172
1173      // If the component data exists and `options` is a string, carry out the action.
1174      if (componentData && typeof options == 'string') {
1175        return PickerConstructor._.trigger(componentData[options], componentData, [action])
1176      }
1177
1178      // Otherwise go through each matched element and if the component
1179      // doesn’t exist, create a new picker using `this` element
1180      // and merging the defaults and options with a deep copy.
1181      return this.each(function () {
1182        var $this = $(this)
1183        if (!$this.data(name)) {
1184          new PickerConstructor(this, name, Component, options)
1185        }
1186      })
1187    }
1188
1189    // Set the defaults.
1190    $.fn[name].defaults = Component.defaults
1191  } //PickerConstructor.extend
1192
1193
1194
1195  function aria(element, attribute, value) {
1196    if ($.isPlainObject(attribute)) {
1197      for (var key in attribute) {
1198        ariaSet(element, key, attribute[key])
1199      }
1200    }
1201    else {
1202      ariaSet(element, attribute, value)
1203    }
1204  }
1205  function ariaSet(element, attribute, value) {
1206    element.setAttribute(
1207        (attribute == 'role' ? '' : 'aria-') + attribute,
1208        value
1209    )
1210  }
1211  function ariaAttr(attribute, data) {
1212    if (!$.isPlainObject(attribute)) {
1213      attribute = { attribute: data }
1214    }
1215    data = ''
1216    for (var key in attribute) {
1217      var attr = (key == 'role' ? '' : 'aria-') + key,
1218          attrVal = attribute[key]
1219      data += attrVal == null ? '' : attr + '="' + attribute[key] + '"'
1220    }
1221    return data
1222  }
1223
1224  // IE8 bug throws an error for activeElements within iframes.
1225  function getActiveElement() {
1226    try {
1227      return document.activeElement
1228    } catch (err) { }
1229  }
1230
1231
1232
1233  // Expose the picker constructor.
1234  return PickerConstructor
1235
1236
1237}));
1238
1239/*!
1240 * Date picker for pickadate.js v3.5.5
1241 * http://amsul.github.io/pickadate.js/date.htm
1242 */
1243
1244(function (factory) {
1245
1246  // AMD.
1247  if (typeof define == 'function' && define.amd)
1248    define(['picker', 'jquery'], factory)
1249
1250    // Node.js/browserify.
1251  else if (typeof exports == 'object')
1252    module.exports = factory(require('./picker.js'), require('jquery'))
1253
1254    // Browser globals.
1255  else factory(Picker, jQuery)
1256
1257}(function (Picker, $) {
1258
1259
1260  /**
1261   * Globals and constants
1262   */
1263  var DAYS_IN_WEEK = 7,
1264      WEEKS_IN_CALENDAR = 6,
1265      _ = Picker._
1266
1267
1268
1269  /**
1270   * The date picker constructor
1271   */
1272  function DatePicker(picker, settings) {
1273
1274    var calendar = this,
1275        element = picker.$node[0],
1276        elementValue = element.value,
1277        elementDataValue = picker.$node.data('value'),
1278        valueString = elementDataValue || elementValue,
1279        formatString = elementDataValue ? settings.formatSubmit : settings.format,
1280        isRTL = function () {
1281
1282          return element.currentStyle ?
1283
1284              // For IE.
1285              element.currentStyle.direction == 'rtl' :
1286
1287              // For normal browsers.
1288              getComputedStyle(picker.$root[0]).direction == 'rtl'
1289        }
1290
1291    calendar.settings = settings
1292    calendar.$node = picker.$node
1293
1294    // The queue of methods that will be used to build item objects.
1295    calendar.queue = {
1296      min: 'measure create',
1297      max: 'measure create',
1298      now: 'now create',
1299      select: 'parse create validate',
1300      highlight: 'parse navigate create validate',
1301      view: 'parse create validate viewset',
1302      disable: 'deactivate',
1303      enable: 'activate'
1304    }
1305
1306    // The component's item object.
1307    calendar.item = {}
1308
1309    calendar.item.clear = null
1310    calendar.item.disable = (settings.disable || []).slice(0)
1311    calendar.item.enable = -(function (collectionDisabled) {
1312      return collectionDisabled[0] === true ? collectionDisabled.shift() : -1
1313    })(calendar.item.disable)
1314
1315    calendar.
1316        set('min', settings.min).
1317        set('max', settings.max).
1318        set('now')
1319
1320    // When there’s a value, set the `select`, which in turn
1321    // also sets the `highlight` and `view`.
1322    if (valueString) {
1323      calendar.set('select', valueString, {
1324        format: formatString,
1325        defaultValue: true
1326      })
1327    }
1328
1329      // If there’s no value, default to highlighting “today”.
1330    else {
1331      calendar.
1332          set('select', null).
1333          set('highlight', calendar.item.now)
1334    }
1335
1336
1337    // The keycode to movement mapping.
1338    calendar.key = {
1339      40: 7, // Down
1340      38: -7, // Up
1341      39: function () { return isRTL() ? -1 : 1 }, // Right
1342      37: function () { return isRTL() ? 1 : -1 }, // Left
1343      go: function (timeChange) {
1344        var highlightedObject = calendar.item.highlight,
1345            targetDate = new Date(highlightedObject.year, highlightedObject.month, highlightedObject.date + timeChange)
1346        calendar.set(
1347            'highlight',
1348            targetDate,
1349            { interval: timeChange }
1350        )
1351        this.render()
1352      }
1353    }
1354
1355
1356    // Bind some picker events.
1357    picker.
1358        on('render', function () {
1359          picker.$root.find('.' + settings.klass.selectMonth).on('change', function () {
1360            var value = this.value
1361            if (value) {
1362              picker.set('highlight', [picker.get('view').year, value, picker.get('highlight').date])
1363              picker.$root.find('.' + settings.klass.selectMonth).trigger('focus')
1364            }
1365          })
1366          picker.$root.find('.' + settings.klass.selectYear).on('change', function () {
1367            var value = this.value
1368            if (value) {
1369              picker.set('highlight', [value, picker.get('view').month, picker.get('highlight').date])
1370              picker.$root.find('.' + settings.klass.selectYear).trigger('focus')
1371            }
1372          })
1373        }, 1).
1374        on('open', function () {
1375          var includeToday = ''
1376          if (calendar.disabled(calendar.get('now'))) {
1377            includeToday = ':not(.' + settings.klass.buttonToday + ')'
1378          }
1379          picker.$root.find('button' + includeToday + ', select').attr('disabled', false)
1380        }, 1).
1381        on('close', function () {
1382          picker.$root.find('button, select').attr('disabled', true)
1383        }, 1)
1384
1385  } //DatePicker
1386
1387
1388  /**
1389   * Set a datepicker item object.
1390   */
1391  DatePicker.prototype.set = function (type, value, options) {
1392
1393    var calendar = this,
1394        calendarItem = calendar.item
1395
1396    // If the value is `null` just set it immediately.
1397    if (value === null) {
1398      if (type == 'clear') type = 'select'
1399      calendarItem[type] = value
1400      return calendar
1401    }
1402
1403    // Otherwise go through the queue of methods, and invoke the functions.
1404    // Update this as the time unit, and set the final value as this item.
1405    // * In the case of `enable`, keep the queue but set `disable` instead.
1406    //   And in the case of `flip`, keep the queue but set `enable` instead.
1407    calendarItem[(type == 'enable' ? 'disable' : type == 'flip' ? 'enable' : type)] = calendar.queue[type].split(' ').map(function (method) {
1408      value = calendar[method](type, value, options)
1409      return value
1410    }).pop()
1411
1412    // Check if we need to cascade through more updates.
1413    if (type == 'select') {
1414      calendar.set('highlight', calendarItem.select, options)
1415    }
1416    else if (type == 'highlight') {
1417      calendar.set('view', calendarItem.highlight, options)
1418    }
1419    else if (type.match(/^(flip|min|max|disable|enable)$/)) {
1420      if (calendarItem.select && calendar.disabled(calendarItem.select)) {
1421        calendar.set('select', calendarItem.select, options)
1422      }
1423      if (calendarItem.highlight && calendar.disabled(calendarItem.highlight)) {
1424        calendar.set('highlight', calendarItem.highlight, options)
1425      }
1426    }
1427
1428    return calendar
1429  } //DatePicker.prototype.set
1430
1431
1432  /**
1433   * Get a datepicker item object.
1434   */
1435  DatePicker.prototype.get = function (type) {
1436    return this.item[type]
1437  } //DatePicker.prototype.get
1438
1439
1440  /**
1441   * Create a picker date object.
1442   */
1443  DatePicker.prototype.create = function (type, value, options) {
1444
1445    var isInfiniteValue,
1446        calendar = this
1447
1448    // If there’s no value, use the type as the value.
1449    value = value === undefined ? type : value
1450
1451
1452    // If it’s infinity, update the value.
1453    if (value == -Infinity || value == Infinity) {
1454      isInfiniteValue = value
1455    }
1456
1457      // If it’s an object, use the native date object.
1458    else if ($.isPlainObject(value) && _.isInteger(value.pick)) {
1459      value = value.obj
1460    }
1461
1462      // If it’s an array, convert it into a date and make sure
1463      // that it’s a valid date – otherwise default to today.
1464    else if ($.isArray(value)) {
1465      value = new Date(value[0], value[1], value[2])
1466      value = _.isDate(value) ? value : calendar.create().obj
1467    }
1468
1469      // If it’s a number or date object, make a normalized date.
1470    else if (_.isInteger(value) || _.isDate(value)) {
1471      value = calendar.normalize(new Date(value), options)
1472    }
1473
1474      // If it’s a literal true or any other case, set it to now.
1475    else /*if ( value === true )*/ {
1476      value = calendar.now(type, value, options)
1477    }
1478
1479    // Return the compiled object.
1480    return {
1481      year: isInfiniteValue || value.getFullYear(),
1482      month: isInfiniteValue || value.getMonth(),
1483      date: isInfiniteValue || value.getDate(),
1484      day: isInfiniteValue || value.getDay(),
1485      obj: isInfiniteValue || value,
1486      pick: isInfiniteValue || value.getTime()
1487    }
1488  } //DatePicker.prototype.create
1489
1490
1491  /**
1492   * Create a range limit object using an array, date object,
1493   * literal “true”, or integer relative to another time.
1494   */
1495  DatePicker.prototype.createRange = function (from, to) {
1496
1497    var calendar = this,
1498        createDate = function (date) {
1499          if (date === true || $.isArray(date) || _.isDate(date)) {
1500            return calendar.create(date)
1501          }
1502          return date
1503        }
1504
1505    // Create objects if possible.
1506    if (!_.isInteger(from)) {
1507      from = createDate(from)
1508    }
1509    if (!_.isInteger(to)) {
1510      to = createDate(to)
1511    }
1512
1513    // Create relative dates.
1514    if (_.isInteger(from) && $.isPlainObject(to)) {
1515      from = [to.year, to.month, to.date + from];
1516    }
1517    else if (_.isInteger(to) && $.isPlainObject(from)) {
1518      to = [from.year, from.month, from.date + to];
1519    }
1520
1521    return {
1522      from: createDate(from),
1523      to: createDate(to)
1524    }
1525  } //DatePicker.prototype.createRange
1526
1527
1528  /**
1529   * Check if a date unit falls within a date range object.
1530   */
1531  DatePicker.prototype.withinRange = function (range, dateUnit) {
1532    range = this.createRange(range.from, range.to)
1533    return dateUnit.pick >= range.from.pick && dateUnit.pick <= range.to.pick
1534  }
1535
1536
1537  /**
1538   * Check if two date range objects overlap.
1539   */
1540  DatePicker.prototype.overlapRanges = function (one, two) {
1541
1542    var calendar = this
1543
1544    // Convert the ranges into comparable dates.
1545    one = calend
1545ar.createRange(one.from, one.to)
1546    two = calendar.createRange(two.from, two.to)
1547
1548    return calendar.withinRange(one, two.from) || calendar.withinRange(one, two.to) ||
1549        calendar.withinRange(two, one.from) || calendar.withinRange(two, one.to)
1550  }
1551
1552
1553  /**
1554   * Get the date today.
1555   */
1556  DatePicker.prototype.now = function (type, value, options) {
1557    value = new Date()
1558    if (options && options.rel) {
1559      value.setDate(value.getDate() + options.rel)
1560    }
1561    return this.normalize(value, options)
1562  }
1563
1564
1565  /**
1566   * Navigate to next/prev month.
1567   */
1568  DatePicker.prototype.navigate = function (type, value, options) {
1569
1570    var targetDateObject,
1571        targetYear,
1572        targetMonth,
1573        targetDate,
1574        isTargetArray = $.isArray(value),
1575        isTargetObject = $.isPlainObject(value),
1576        viewsetObject = this.item.view/*,
1577        safety = 100*/
1578
1579
1580    if (isTargetArray || isTargetObject) {
1581
1582      if (isTargetObject) {
1583        targetYear = value.year
1584        targetMonth = value.month
1585        targetDate = value.date
1586      }
1587      else {
1588        targetYear = +value[0]
1589        targetMonth = +value[1]
1590        targetDate = +value[2]
1591      }
1592
1593      // If we’re navigating months but the view is in a different
1594      // month, navigate to the view’s year and month.
1595      if (options && options.nav && viewsetObject && viewsetObject.month !== targetMonth) {
1596        targetYear = viewsetObject.year
1597        targetMonth = viewsetObject.month
1598      }
1599
1600      // Figure out the expected target year and month.
1601      targetDateObject = new Date(targetYear, targetMonth + (options && options.nav ? options.nav : 0), 1)
1602      targetYear = targetDateObject.getFullYear()
1603      targetMonth = targetDateObject.getMonth()
1604
1605      // If the month we’re going to doesn’t have enough days,
1606      // keep decreasing the date until we reach the month’s last date.
1607      while ( /*safety &&*/ new Date(targetYear, targetMonth, targetDate).getMonth() !== targetMonth) {
1608        targetDate -= 1
1609        /*safety -= 1
1610        if ( !safety ) {
1611            throw 'Fell into an infinite loop while navigating to ' + new Date( targetYear, targetMonth, targetDate ) + '.'
1612        }*/
1613      }
1614
1615      value = [targetYear, targetMonth, targetDate]
1616    }
1617
1618    return value
1619  } //DatePicker.prototype.navigate
1620
1621
1622  /**
1623   * Normalize a date by setting the hours to midnight.
1624   */
1625  DatePicker.prototype.normalize = function (value/*, options*/) {
1626    value.setHours(0, 0, 0, 0)
1627    return value
1628  }
1629
1630
1631  /**
1632   * Measure the range of dates.
1633   */
1634  DatePicker.prototype.measure = function (type, value/*, options*/) {
1635
1636    var calendar = this
1637
1638    // If it’s anything false-y, remove the limits.
1639    if (!value) {
1640      value = type == 'min' ? -Infinity : Infinity
1641    }
1642
1643      // If it’s a string, parse it.
1644    else if (typeof value == 'string') {
1645      value = calendar.parse(type, value)
1646    }
1647
1648      // If it's an integer, get a date relative to today.
1649    else if (_.isInteger(value)) {
1650      value = calendar.now(type, value, { rel: value })
1651    }
1652
1653    return value
1654  } ///DatePicker.prototype.measure
1655
1656
1657  /**
1658   * Create a viewset object based on navigation.
1659   */
1660  DatePicker.prototype.viewset = function (type, dateObject/*, options*/) {
1661    return this.create([dateObject.year, dateObject.month, 1])
1662  }
1663
1664
1665  /**
1666   * Validate a date as enabled and shift if needed.
1667   */
1668  DatePicker.prototype.validate = function (type, dateObject, options) {
1669
1670    var calendar = this,
1671
1672        // Keep a reference to the original date.
1673        originalDateObject = dateObject,
1674
1675        // Make sure we have an interval.
1676        interval = options && options.interval ? options.interval : 1,
1677
1678        // Check if the calendar enabled dates are inverted.
1679        isFlippedBase = calendar.item.enable === -1,
1680
1681        // Check if we have any enabled dates after/before now.
1682        hasEnabledBeforeTarget, hasEnabledAfterTarget,
1683
1684        // The min & max limits.
1685        minLimitObject = calendar.item.min,
1686        maxLimitObject = calendar.item.max,
1687
1688        // Check if we’ve reached the limit during shifting.
1689        reachedMin, reachedMax,
1690
1691        // Check if the calendar is inverted and at least one weekday is enabled.
1692        hasEnabledWeekdays = isFlippedBase && calendar.item.disable.filter(function (value) {
1693
1694          // If there’s a date, check where it is relative to the target.
1695          if ($.isArray(value)) {
1696            var dateTime = calendar.create(value).pick
1697            if (dateTime < dateObject.pick) hasEnabledBeforeTarget = true
1698            else if (dateTime > dateObject.pick) hasEnabledAfterTarget = true
1699          }
1700
1701          // Return only integers for enabled weekdays.
1702          return _.isInteger(value)
1703        }).length/*,
1704
1705        safety = 100*/
1706
1707
1708
1709    // Cases to validate for:
1710    // [1] Not inverted and date disabled.
1711    // [2] Inverted and some dates enabled.
1712    // [3] Not inverted and out of range.
1713    //
1714    // Cases to **not** validate for:
1715    // • Navigating months.
1716    // • Not inverted and date enabled.
1717    // • Inverted and all dates disabled.
1718    // • ..and anything else.
1719    if (!options || (!options.nav && !options.defaultValue)) if (
1720      /* 1 */ (!isFlippedBase && calendar.disabled(dateObject)) ||
1721      /* 2 */ (isFlippedBase && calendar.disabled(dateObject) && (hasEnabledWeekdays || hasEnabledBeforeTarget || hasEnabledAfterTarget)) ||
1722      /* 3 */ (!isFlippedBase && (dateObject.pick <= minLimitObject.pick || dateObject.pick >= maxLimitObject.pick))
1723    ) {
1724
1725
1726      // When inverted, flip the direction if there aren’t any enabled weekdays
1727      // and there are no enabled dates in the direction of the interval.
1728      if (isFlippedBase && !hasEnabledWeekdays && ((!hasEnabledAfterTarget && interval > 0) || (!hasEnabledBeforeTarget && interval < 0))) {
1729        interval *= -1
1730      }
1731
1732
1733      // Keep looping until we reach an enabled date.
1734      while ( /*safety &&*/ calendar.disabled(dateObject)) {
1735
1736        /*safety -= 1
1737        if ( !safety ) {
1738            throw 'Fell into an infinite loop while validating ' + dateObject.obj + '.'
1739        }*/
1740
1741
1742        // If we’ve looped into the next/prev month with a large interval, return to the original date and flatten the interval.
1743        if (Math.abs(interval) > 1 && (dateObject.month < originalDateObject.month || dateObject.month > originalDateObject.month)) {
1744          dateObject = originalDateObject
1745          interval = interval > 0 ? 1 : -1
1746        }
1747
1748
1749        // If we’ve reached the min/max limit, reverse the direction, flatten the interval and set it to the limit.
1750        if (dateObject.pick <= minLimitObject.pick) {
1751          reachedMin = true
1752          interval = 1
1753          dateObject = calendar.create([
1754              minLimitObject.year,
1755              minLimitObject.month,
1756              minLimitObject.date + (dateObject.pick === minLimitObject.pick ? 0 : -1)
1757          ])
1758        }
1759        else if (dateObject.pick >= maxLimitObject.pick) {
1760          reachedMax = true
1761          interval = -1
1762          dateObject = calendar.create([
1763              maxLimitObject.year,
1764              maxLimitObject.month,
1765              maxLimitObject.date + (dateObject.pick === maxLimitObject.pick ? 0 : 1)
1766          ])
1767        }
1768
1769
1770        // If we’ve reached both limits, just break out of the loop.
1771        if (reachedMin && reachedMax) {
1772          break
1773        }
1774
1775
1776        // Finally, create the shifted date using the interval and keep looping.
1777        dateObject = calendar.create([dateObject.year, dateObject.month, dateObject.date + interval])
1778      }
1779
1780    } //endif
1781
1782
1783    // Return the date object settled on.
1784    return dateObject
1785  } //DatePicker.prototype.validate
1786
1787
1788  /**
1789   * Check if a date is disabled.
1790   */
1791  DatePicker.prototype.disabled = function (dateToVerify) {
1792
1793    var
1794        calendar = this,
1795
1796        // Filter through the disabled dates to check if this is one.
1797        isDisabledMatch = calendar.item.disable.filter(function (dateToDisable) {
1798
1799          // If the date is a number, match the weekday with 0index and `firstDay` check.
1800          if (_.isInteger(dateToDisable)) {
1801            return dateToVerify.day === (calendar.settings.firstDay ? dateToDisable : dateToDisable - 1) % 7
1802          }
1803
1804          // If it’s an array or a native JS date, create and match the exact date.
1805          if ($.isArray(dateToDisable) || _.isDate(dateToDisable)) {
1806            return dateToVerify.pick === calendar.create(dateToDisable).pick
1807          }
1808
1809          // If it’s an object, match a date within the “from” and “to” range.
1810          if ($.isPlainObject(dateToDisable)) {
1811            return calendar.withinRange(dateToDisable, dateToVerify)
1812          }
1813        })
1814
1815    // If this date matches a disabled date, confirm it’s not inverted.
1816    isDisabledMatch = isDisabledMatch.length && !isDisabledMatch.filter(function (dateToDisable) {
1817      return $.isArray(dateToDisable) && dateToDisable[3] == 'inverted' ||
1818          $.isPlainObject(dateToDisable) && dateToDisable.inverted
1819    }).length
1820
1821    // Check the calendar “enabled” flag and respectively flip the
1822    // disabled state. Then also check if it’s beyond the min/max limits.
1823    return calendar.item.enable === -1 ? !isDisabledMatch : isDisabledMatch ||
1824        dateToVerify.pick < calendar.item.min.pick ||
1825        dateToVerify.pick > calendar.item.max.pick
1826
1827  } //DatePicker.prototype.disabled
1828
1829
1830  /**
1831   * Parse a string into a usable type.
1832   */
1833  DatePicker.prototype.parse = function (type, value, options) {
1834
1835    var calendar = this,
1836        parsingObject = {}
1837
1838    // If it’s already parsed, we’re good.
1839    if (!value || typeof value != 'string') {
1840      return value
1841    }
1842
1843    // We need a `.format` to parse the value with.
1844    if (!(options && options.format)) {
1845      options = options || {}
1846      options.format = calendar.settings.format
1847    }
1848
1849    // Convert the format into an array and then map through it.
1850    calendar.formats.toArray(options.format).map(function (label) {
1851
1852      var
1853          // Grab the formatting label.
1854          formattingLabel = calendar.formats[label],
1855
1856          // The format length is from the formatting label function or the
1857          // label length without the escaping exclamation (!) mark.
1858          formatLength = formattingLabel ? _.trigger(formattingLabel, calendar, [value, parsingObject]) : label.replace(/^!/, '').length
1859
1860      // If there's a format label, split the value up to the format length.
1861      // Then add it to the parsing object with appropriate label.
1862      if (formattingLabel) {
1863        parsingObject[label] = value.substr(0, formatLength)
1864      }
1865
1866      // Update the value as the substring from format length to end.
1867      value = value.substr(formatLength)
1868    })
1869
1870    // Compensate for month 0index.
1871    return [
1872        parsingObject.yyyy || parsingObject.yy,
1873        +(parsingObject.mm || parsingObject.m) - 1,
1874        parsingObject.dd || parsingObject.d
1875    ]
1876  } //DatePicker.prototype.parse
1877
1878
1879  /**
1880   * Various formats to display the object in.
1881   */
1882  DatePicker.prototype.formats = (function () {
1883
1884    // Return the length of the first word in a collection.
1885    function getWordLengthFromCollection(string, collection, dateObject) {
1886
1887      // Grab the first word from the string.
1888      // Regex pattern from http://stackoverflow.com/q/150033
1889      var word = string.match(/[^\x00-\x7F]+|\w+/)[0]
1890
1891      // If there's no month index, add it to the date object
1892      if (!dateObject.mm && !dateObject.m) {
1893        dateObject.m = collection.indexOf(word) + 1
1894      }
1895
1896      // Return the length of the word.
1897      return word.length
1898    }
1899
1900    // Get the length of the first word in a string.
1901    function getFirstWordLength(string) {
1902      return string.match(/\w+/)[0].length
1903    }
1904
1905    return {
1906
1907      d: function (string, dateObject) {
1908
1909        // If there's string, then get the digits length.
1910        // Otherwise return the selected date.
1911        return string ? _.digits(string) : dateObject.date
1912      },
1913      dd: function (string, dateObject) {
1914
1915        // If there's a string, then the length is always 2.
1916        // Otherwise return the selected date with a leading zero.
1917        return string ? 2 : _.lead(dateObject.date)
1918      },
1919      ddd: function (string, dateObject) {
1920
1921        // If there's a string, then get the length of the first word.
1922        // Otherwise return the short selected weekday.
1923        return string ? getFirstWordLength(string) : this.settings.weekdaysShort[dateObject.day]
1924      },
1925      dddd: function (string, dateObject) {
1926
1927        // If there's a string, then get the length of the first word.
1928        // Otherwise return the full selected weekday.
1929        return string ? getFirstWordLength(string) : this.settings.weekdaysFull[dateObject.day]
1930      },
1931      m: function (string, dateObject) {
1932
1933        // If there's a string, then get the length of the digits
1934        // Otherwise return the selected month with 0index compensation.
1935        return string ? _.digits(string) : dateObject.month + 1
1936      },
1937      mm: function (string, dateObject) {
1938
1939        // If there's a string, then the length is always 2.
1940        // Otherwise return the selected month with 0index and leading zero.
1941        return string ? 2 : _.lead(dateObject.month + 1)
1942      },
1943      mmm: function (string, dateObject) {
1944
1945        var collection = this.settings.monthsShort
1946
1947        // If there's a string, get length of the relevant month from the short
1948        // months collection. Otherwise return the selected month from that c
1948ollection.
1949        return string ? getWordLengthFromCollection(string, collection, dateObject) : collection[dateObject.month]
1950      },
1951      mmmm: function (string, dateObject) {
1952
1953        var collection = this.settings.monthsFull
1954
1955        // If there's a string, get length of the relevant month from the full
1956        // months collection. Otherwise return the selected month from that collection.
1957        return string ? getWordLengthFromCollection(string, collection, dateObject) : collection[dateObject.month]
1958      },
1959      yy: function (string, dateObject) {
1960
1961        // If there's a string, then the length is always 2.
1962        // Otherwise return the selected year by slicing out the first 2 digits.
1963        return string ? 2 : ('' + dateObject.year).slice(2)
1964      },
1965      yyyy: function (string, dateObject) {
1966
1967        // If there's a string, then the length is always 4.
1968        // Otherwise return the selected year.
1969        return string ? 4 : dateObject.year
1970      },
1971
1972      // Create an array by splitting the formatting string passed.
1973      toArray: function (formatString) { return formatString.split(/(d{1,4}|m{1,4}|y{4}|yy|!.)/g) },
1974
1975      // Format an object into a string using the formatting options.
1976      toString: function (formatString, itemObject) {
1977        var calendar = this
1978        return calendar.formats.toArray(formatString).map(function (label) {
1979          return _.trigger(calendar.formats[label], calendar, [0, itemObject]) || label.replace(/^!/, '')
1980        }).join('')
1981      }
1982    }
1983  })() //DatePicker.prototype.formats
1984
1985
1986
1987
1988  /**
1989   * Check if two date units are the exact.
1990   */
1991  DatePicker.prototype.isDateExact = function (one, two) {
1992
1993    var calendar = this
1994
1995    // When we’re working with weekdays, do a direct comparison.
1996    if (
1997        (_.isInteger(one) && _.isInteger(two)) ||
1998        (typeof one == 'boolean' && typeof two == 'boolean')
1999     ) {
2000      return one === two
2001    }
2002
2003    // When we’re working with date representations, compare the “pick” value.
2004    if (
2005        (_.isDate(one) || $.isArray(one)) &&
2006        (_.isDate(two) || $.isArray(two))
2007    ) {
2008      return calendar.create(one).pick === calendar.create(two).pick
2009    }
2010
2011    // When we’re working with range objects, compare the “from” and “to”.
2012    if ($.isPlainObject(one) && $.isPlainObject(two)) {
2013      return calendar.isDateExact(one.from, two.from) && calendar.isDateExact(one.to, two.to)
2014    }
2015
2016    return false
2017  }
2018
2019
2020  /**
2021   * Check if two date units overlap.
2022   */
2023  DatePicker.prototype.isDateOverlap = function (one, two) {
2024
2025    var calendar = this,
2026      firstDay = calendar.settings.firstDay ? 1 : 0      
2027
2028    // When we’re working with a weekday index, compare the days.
2029    if (_.isInteger(one) && (_.isDate(two) || $.isArray(two))) {
2030      one = one % 7 + firstDay
2031      return one === calendar.create(two).day + 1
2032    }
2033    if (_.isInteger(two) && (_.isDate(one) || $.isArray(one))) {
2034      two = two % 7 + firstDay
2035      return two === calendar.create(one).day + 1
2036    }
2037
2038    // When we’re working with range objects, check if the ranges overlap.
2039    if ($.isPlainObject(one) && $.isPlainObject(two)) {
2040      return calendar.overlapRanges(one, two)
2041    }
2042
2043    return false
2044  }
2045
2046
2047  /**
2048   * Flip the “enabled” state.
2049   */
2050  DatePicker.prototype.flipEnable = function (val) {
2051    var itemObject = this.item
2052    itemObject.enable = val || (itemObject.enable == -1 ? 1 : -1)
2053  }
2054
2055
2056  /**
2057   * Mark a collection of dates as “disabled”.
2058   */
2059  DatePicker.prototype.deactivate = function (type, datesToDisable) {
2060
2061    var calendar = this,
2062        disabledItems = calendar.item.disable.slice(0)
2063
2064
2065    // If we’re flipping, that’s all we need to do.
2066    if (datesToDisable == 'flip') {
2067      calendar.flipEnable()
2068    }
2069
2070    else if (datesToDisable === false) {
2071      calendar.flipEnable(1)
2072      disabledItems = []
2073    }
2074
2075    else if (datesToDisable === true) {
2076      calendar.flipEnable(-1)
2077      disabledItems = []
2078    }
2079
2080      // Otherwise go through the dates to disable.
2081    else {
2082
2083      datesToDisable.map(function (unitToDisable) {
2084
2085        var matchFound
2086
2087        // When we have disabled items, check for matches.
2088        // If something is matched, immediately break out.
2089        for (var index = 0; index < disabledItems.length; index += 1) {
2090          if (calendar.isDateExact(unitToDisable, disabledItems[index])) {
2091            matchFound = true
2092            break
2093          }
2094        }
2095
2096        // If nothing was found, add the validated unit to the collection.
2097        if (!matchFound) {
2098          if (
2099              _.isInteger(unitToDisable) ||
2100              _.isDate(unitToDisable) ||
2101              $.isArray(unitToDisable) ||
2102              ($.isPlainObject(unitToDisable) && unitToDisable.from && unitToDisable.to)
2103          ) {
2104            disabledItems.push(unitToDisable)
2105          }
2106        }
2107      })
2108    }
2109
2110    // Return the updated collection.
2111    return disabledItems
2112  } //DatePicker.prototype.deactivate
2113
2114
2115  /**
2116   * Mark a collection of dates as “enabled”.
2117   */
2118  DatePicker.prototype.activate = function (type, datesToEnable) {
2119
2120    var calendar = this,
2121        disabledItems = calendar.item.disable,
2122        disabledItemsCount = disabledItems.length
2123
2124    // If we’re flipping, that’s all we need to do.
2125    if (datesToEnable == 'flip') {
2126      calendar.flipEnable()
2127    }
2128
2129    else if (datesToEnable === true) {
2130      calendar.flipEnable(1)
2131      disabledItems = []
2132    }
2133
2134    else if (datesToEnable === false) {
2135      calendar.flipEnable(-1)
2136      disabledItems = []
2137    }
2138
2139      // Otherwise go through the disabled dates.
2140    else {
2141
2142      datesToEnable.map(function (unitToEnable) {
2143
2144        var matchFound,
2145            disabledUnit,
2146            index,
2147            isExactRange
2148
2149        // Go through the disabled items and try to find a match.
2150        for (index = 0; index < disabledItemsCount; index += 1) {
2151
2152          disabledUnit = disabledItems[index]
2153
2154          // When an exact match is found, remove it from the collection.
2155          if (calendar.isDateExact(disabledUnit, unitToEnable)) {
2156            matchFound = disabledItems[index] = null
2157            isExactRange = true
2158            break
2159          }
2160
2161            // When an overlapped match is found, add the “inverted” state to it.
2162          else if (calendar.isDateOverlap(disabledUnit, unitToEnable)) {
2163            if ($.isPlainObject(unitToEnable)) {
2164              unitToEnable.inverted = true
2165              matchFound = unitToEnable
2166            }
2167            else if ($.isArray(unitToEnable)) {
2168              matchFound = unitToEnable
2169              if (!matchFound[3]) matchFound.push('inverted')
2170            }
2171            else if (_.isDate(unitToEnable)) {
2172              matchFound = [unitToEnable.getFullYear(), unitToEnable.getMonth(), unitToEnable.getDate(), 'inverted']
2173            }
2174            break
2175          }
2176        }
2177
2178        // If a match was found, remove a previous duplicate entry.
2179        if (matchFound) for (index = 0; index < disabledItemsCount; index += 1) {
2180          if (calendar.isDateExact(disabledItems[index], unitToEnable)) {
2181            disabledItems[index] = null
2182            break
2183          }
2184        }
2185
2186        // In the event that we’re dealing with an exact range of dates,
2187        // make sure there are no “inverted” dates because of it.
2188        if (isExactRange) for (index = 0; index < disabledItemsCount; index += 1) {
2189          if (calendar.isDateOverlap(disabledItems[index], unitToEnable)) {
2190            disabledItems[index] = null
2191            break
2192          }
2193        }
2194
2195        // If something is still matched, add it into the collection.
2196        if (matchFound) {
2197          disabledItems.push(matchFound)
2198        }
2199      })
2200    }
2201
2202    // Return the updated collection.
2203    return disabledItems.filter(function (val) { return val != null })
2204  } //DatePicker.prototype.activate
2205
2206
2207  /**
2208   * Create a string for the nodes in the picker.
2209   */
2210  DatePicker.prototype.nodes = function (isOpen) {
2211
2212    var
2213        calendar = this,
2214        settings = calendar.settings,
2215        calendarItem = calendar.item,
2216        nowObject = calendarItem.now,
2217        selectedObject = calendarItem.select,
2218        highlightedObject = calendarItem.highlight,
2219        viewsetObject = calendarItem.view,
2220        disabledCollection = calendarItem.disable,
2221        minLimitObject = calendarItem.min,
2222        maxLimitObject = calendarItem.max,
2223
2224
2225        // Create the calendar table head using a copy of weekday labels collection.
2226        // * We do a copy so we don't mutate the original array.
2227        tableHead = (function (collection, fullCollection) {
2228
2229          // If the first day should be Monday, move Sunday to the end.
2230          if (settings.firstDay) {
2231            collection.push(collection.shift())
2232            fullCollection.push(fullCollection.shift())
2233          }
2234
2235          // Create and return the table head group.
2236          return _.node(
2237              'thead',
2238              _.node(
2239                  'tr',
2240                  _.group({
2241                    min: 0,
2242                    max: DAYS_IN_WEEK - 1,
2243                    i: 1,
2244                    node: 'th',
2245                    item: function (counter) {
2246                      return [
2247                          collection[counter],
2248                          settings.klass.weekdays,
2249                          'scope=col title="' + fullCollection[counter] + '"'
2250                      ]
2251                    }
2252                  })
2253              )
2254          ) //endreturn
2255        })((settings.showWeekdaysFull ? settings.weekdaysFull : settings.weekdaysShort).slice(0), settings.weekdaysFull.slice(0)), //tableHead
2256
2257
2258        // Create the nav for next/prev month.
2259        createMonthNav = function (next) {
2260
2261          // Otherwise, return the created month tag.
2262          return _.node(
2263              'div',
2264              ' ',
2265              settings.klass['nav' + (next ? 'Next' : 'Prev')] + (
2266
2267                  // If the focused month is outside the range, disabled the button.
2268                  (next && viewsetObject.year >= maxLimitObject.year && viewsetObject.month >= maxLimitObject.month) ||
2269                  (!next && viewsetObject.year <= minLimitObject.year && viewsetObject.month <= minLimitObject.month) ?
2270                  ' ' + settings.klass.navDisabled : ''
2271              ),
2272              'data-nav=' + (next || -1) + ' ' +
2273              _.ariaAttr({
2274                role: 'button',
2275                controls: calendar.$node[0].id + '_table'
2276              }) + ' ' +
2277              'title="' + (next ? settings.labelMonthNext : settings.labelMonthPrev) + '"'
2278          ) //endreturn
2279        }, //createMonthNav
2280
2281
2282        // Create the month label.
2283        createMonthLabel = function () {
2284
2285          var monthsCollection = settings.showMonthsShort ? settings.monthsShort : settings.monthsFull
2286
2287          // If there are months to select, add a dropdown menu.
2288          if (settings.selectMonths) {
2289
2290            return _.node('select',
2291                _.group({
2292                  min: 0,
2293                  max: 11,
2294                  i: 1,
2295                  node: 'option',
2296                  item: function (loopedMonth) {
2297
2298                    return [
2299
2300                        // The looped month and no classes.
2301                        monthsCollection[loopedMonth], 0,
2302
2303                        // Set the value and selected index.
2304                        'value=' + loopedMonth +
2305                        (viewsetObject.month == loopedMonth ? ' selected' : '') +
2306                        (
2307                            (
2308                                (viewsetObject.year == minLimitObject.year && loopedMonth < minLimitObject.month) ||
2309                                (viewsetObject.year == maxLimitObject.year && loopedMonth > maxLimitObject.month)
2310                            ) ?
2311                            ' disabled' : ''
2312                        )
2313                    ]
2314                  }
2315                }),
2316                settings.klass.selectMonth,
2317                (isOpen ? '' : 'disabled') + ' ' +
2318                _.ariaAttr({ controls: calendar.$node[0].id + '_table' }) + ' ' +
2319                'title="' + settings.labelMonthSelect + '"'
2320            )
2321          }
2322
2323          // If there's a need for a month selector
2324          return _.node('div', monthsCollection[viewsetObject.month], settings.klass.month)
2325        }, //createMonthLabel
2326
2327
2328        // Create the year label.
2329        createYearLabel = function () {
2330
2331          var focusedYear = viewsetObject.year,
2332
2333          // If years selector is set to a literal "true", set it to 5. Otherwise
2334          // divide in half to get half before and half after focused year.
2335          numberYears = settings.selectYears === true ? 5 : ~~(settings.selectYears / 2)
2336
2337          // If there are years to select, add a dropdown menu.
2338          if (numberYears) {
2339
2340            var
2341                minYear = minLimitObject.year,
2342                maxYear = maxLimitObject.year,
2343                lowestYear = focusedYear - numberYears,
2344                highestYear = focusedYear + numberYears
2345
2346            // If the min year is greater than the lowest year, increase the highest year
2347            // by the difference and set the lowest year to the min year.
2348            if (minYear > lowestYear) {
2349              highestYear += minYear - lowestYear
2350              lowestYear = minYear
2351            }
2352
2353            // If the max year is less than the highest year, decrease the lowest year
2354            // by the lower of the two: available and needed years. Then set the
2355            // highest year to the max year.
2356            if (maxYear < highestYear) {
2357
2358              var availableYears = lowestYear - minYear,
2359                  neededYears = highestYear - maxYear
2360
2361              lowestYear -= availableYears > neededYears ? neededYears : availableYears
2362              highestYear = maxYear
2363            }
2364
2365            return _.node('select',
2366                _.group({
2367                  min: lowestYear,
2368                  max: highestYear,
2369                  i: 1,
2370                  node: 'option',
2371                  item: function (loopedYear) {
2372                    return [
2373
2374                        // The looped year and no classes.
2375                        loopedYear, 0,
2376
2377                        // Set the value and selected index.
2378                        'value=' + loopedYear + (focusedYear == loopedYear ? ' selected' : '')
2379                    ]
2380                  }
2381                }),
2382                settings.klass.selectYear,
2383                (isOpen ? '' : 'disabled') + ' ' + _.ariaAttr({ controls: calendar.$node[0].id + '_table' }) + ' ' +
2384                'title="' + settings.labelYearSelect + '"'
2385            )
2386          }
2387
2388          // Otherwise just return the year focused
2389          return _.node('div', focusedYear, settings.klass.year)
2390        } //createYearLabel
2391
2392
2393    // Create and return the entire calendar.
2394    return _.node(
2395        'div',
2396        (settings.selectYears ? createYearLabel() + createMonthLabel() : createMonthLabel() + createYearLabel()) +
2397        createMonthNav() + createMonthNav(1),
2398        settings.klass.header
2399    ) + _.node(
2400        'table',
2401        tableHead +
2402        _.node(
2403            'tbody',
2404            _.group({
2405              min: 0,
2406              max: WEEKS_IN_CALENDAR - 1,
2407              i: 1,
2408              node: 'tr',
2409              item: function (rowCounter) {
2410
2411                // If Monday is the first day and the month starts on Sunday, shift the date back a week.
2412                var shiftDateBy = settings.firstDay && calendar.create([viewsetObject.year, viewsetObject.month, 1]).day === 0 ? -7 : 0
2413
2414                return [
2415                    _.group({
2416                      min: DAYS_IN_WEEK * rowCounter - viewsetObject.day + shiftDateBy + 1, // Add 1 for weekday 0index
2417                      max: function () {
2418                        return this.min + DAYS_IN_WEEK - 1
2419                      },
2420                      i: 1,
2421                      node: 'td',
2422                      item: function (targetDate) {
2423
2424                        // Convert the time date from a relative date to a target date.
2425                        targetDate = calendar.create([viewsetObject.year, viewsetObject.month, targetDate + (settings.firstDay ? 1 : 0)])
2426
2427                        var isSelected = selectedObject && selectedObject.pick == targetDate.pick,
2428                            isHighlighted = highlightedObject && highlightedObject.pick == targetDate.pick,
2429                            isDisabled = disabledCollection && calendar.disabled(targetDate) || targetDate.pick < minLimitObject.pick || targetDate.pick > maxLimitObject.pick,
2430                            formattedDate = _.trigger(calendar.formats.toString, calendar, [settings.format, targetDate])
2431
2432                        return [
2433                            _.node(
2434                                'div',
2435                                targetDate.date,
2436                                (function (klasses) {
2437
2438                                  // Add the `infocus` or `outfocus` classes based on month in view.
2439                                  klasses.push(viewsetObject.month == targetDate.month ? settings.klass.infocus : settings.klass.outfocus)
2440
2441                                  // Add the `today` class if needed.
2442                                  if (nowObject.pick == targetDate.pick) {
2443                                    klasses.push(settings.klass.now)
2444                                  }
2445
2446                                  // Add the `selected` class if something's selected and the time matches.
2447                                  if (isSelected) {
2448                                    klasses.push(settings.klass.selected)
2449                                  }
2450
2451                                  // Add the `highlighted` class if something's highlighted and the time matches.
2452                                  if (isHighlighted) {
2453                                    klasses.push(settings.klass.highlighted)
2454                                  }
2455
2456                                  // Add the `disabled` class if something's disabled and the object matches.
2457                                  if (isDisabled) {
2458                                    klasses.push(settings.klass.disabled)
2459                                  }
2460
2461                                  return klasses.join(' ')
2462                                })([settings.klass.day]),
2463                                'data-pick=' + targetDate.pick + ' ' + _.ariaAttr({
2464                                  role: 'gridcell',
2465                                  label: formattedDate,
2466                                  selected: isSelected && calendar.$node.val() === formattedDate ? true : null,
2467                                  activedescendant: isHighlighted ? true : null,
2468                                  disabled: isDisabled ? true : null
2469                                })
2470                            ),
2471                            '',
2472                            _.ariaAttr({ role: 'presentation' })
2473                        ] //endreturn
2474                      }
2475                    })
2476                ] //endreturn
2477              }
2478            })
2479        ),
2480        settings.klass.table,
2481        'id="' + calendar.$node[0].id + '_table' + '" ' + _.ariaAttr({
2482          role: 'grid',
2483          controls: calendar.$node[0].id,
2484          readonly: true
2485        })
2486    ) +
2487
2488    // * For Firefox forms to submit, make sure to set the buttons’ `type` attributes as “button”.
2489    _.node(
2490        'div',
2491        _.node('button', settings.today, settings.klass.buttonToday,
2492            'type=button data-pick=' + nowObject.pick +
2493            (isOpen && !calendar.disabled(nowObject) ? '' : ' disabled') + ' ' +
2494            _.ariaAttr({ controls: calendar.$node[0].id })) +
2495        _.node('button', settings.clear, settings.klass.buttonClear,
2496            'type=button data-clear=1' +
2497            (isOpen ? '' : ' disabled') + ' ' +
2498            _.ariaAttr({ controls: calendar.$node[0].id })) +
2499        _.node('button', settings.close, settings.klass.buttonClose,
2500            'type=button data-close=true ' +
2501            (isOpen ? '' : ' disabled') + ' ' +
2502            _.ariaAttr({ controls: calendar.$node[0].id })),
2503        settings.klass.footer
2504    ) //endreturn
2505  } //DatePicker.prototype.nodes
2506
2507
2508
2509
2510  /**
2511   * The date picker defaults.
2512   */
2513  DatePicker.defaults = (function (prefix) {
2514
2515    return {
2516
2517      // The title label to use for the month nav buttons
2518      labelMonthNext: 'Sljedeći mjesec',
2519      labelMonthPrev: 'Prethodni mjesec',
2520
2521      // The title label to use for the dropdown selectors
2522      labelMonthSelect: 'Odaberi mjesec',
2523      labelYearSelect: 'Odaberi godinu',
2524
2525      // Months and weekdays
2526      monthsFull: ['Siječanj', 'Veljača', 'Ožujak', 'Travanj', 'Svibanj', 'Lipanj', 'Srpanj', 'Kolovoz', 'Rujan', 'Listopad', 'Studeni', 'Prosinac'],
2527      monthsShort: ['Sij', 'Velj', 'Ožu', 'Tra', 'Svi', 'Lip', 'Srp', 'Kol', 'Ruj', 'Lis', 'Stu', 'Pro'],
2528      weekdaysFull: ['Nedjelja', 'Ponedjeljak', 'Utorak', 'Srijeda', 'Četvrtak', 'Petak', 'Subota'],
2529      weekdaysShort: ['Ned', 'Pon', 'Uto', 'Sri', 'Čet', 'Pet', 'Sub'],
2530
2531      firstDay: true,
2532
2533      // Today and clear
2534      today: 'Danas',
2535      clear: 'Obriši',
2536      close: 'Zatvori',
2537
2538      // Picker close behavior
2539      closeOnSelect: true,
2540      closeOnClear: true,
2541
2542      // The format to show on the `input` element
2543      format: 'd mmmm, yyyy',
2544
2545      // Classes
2546      klass: {
2547
2548        table: prefix + 'table',
2549
2550        header: prefix + 'header',
2551
2552        navPrev: prefix + 'nav--prev',
2553        navNext: prefix + 'nav--next',
2554        navDisabled: prefix + 'nav--disabled',
2555
2556        month: prefix + 'month',
2557        year: prefix + 'year',
2558
2559        selectMonth: prefix + 'select--month',
2560        selectYear: prefix + 'select--year',
2561
2562        weekdays: prefix + 'weekday',
2563
2564        day: prefix + 'day',
2565        disabled: prefix + 'day--disabled',
2566        selected: prefix + 'day--selected',
2567        highlighted: prefix + 'day--highlighted',
2568        now: prefix + 'day--today',
2569        infocus: prefix + 'day--infocus',
2570        outfocus: prefix + 'day--outfocus',
2571
2572        footer: prefix + 'footer',
2573
2574        buttonClear: prefix + 'button--clear',
2575        buttonToday: prefix + 'button--today',
2576        buttonClose: prefix + 'button--close'
2577      }
2578    }
2579  })(Picker.klasses().picker + '__')
2580
2581
2582
2583
2584
2585  /**
2586   * Extend the picker to add the date picker.
2587   */
2588  Picker.extend('pickadate', DatePicker)
2589
2590
2591}));
2592
2593/*!
2594 * Time picker for pickadate.js v3.5.5
2595 * http://amsul.github.io/pickadate.js/time.htm
2596 */
2597
2598(function (factory) {
2599
2600  // AMD.
2601  if (typeof define == 'function' && define.amd)
2602    define(['picker', 'jquery'], factory)
2603
2604    // Node.js/browserify.
2605  else if (typeof exports == 'object')
2606    module.exports = factory(require('./picker.js'), require('jquery'))
2607
2608    // Browser globals.
2609  else factory(Picker, jQuery)
2610
2611}(function (Picker, $) {
2612
2613
2614  /**
2615   * Globals and constants
2616   */
2617  var HOURS_IN_DAY = 24,
2618      MINUTES_IN_HOUR = 60,
2619      HOURS_TO_NOON = 12,
2620      MINUTES_IN_DAY = HOURS_IN_DAY * MINUTES_IN_HOUR,
2621      _ = Picker._
2622
2623
2624
2625  /**
2626   * The time picker constructor
2627   */
2628  function TimePicker(picker, settings) {
2629
2630    var clock = this,
2631        elementValue = picker.$node[0].value,
2632        elementDataValue = picker.$node.data('value'),
2633        valueString = elementDataValue || elementValue,
2634        formatString = elementDataValue ? settings.formatSubmit : settings.format
2635
2636    clock.settings = settings
2637    clock.$node = picker.$node
2638
2639    // The queue of methods that will be used to build item objects.
2640    clock.queue = {
2641      interval: 'i',
2642      min: 'measure create',
2643      max: 'measure create',
2644      now: 'now create',
2645      select: 'parse create validate',
2646      highlight: 'parse create validate',
2647      view: 'parse create validate',
2648      disable: 'deactivate',
2649      enable: 'activate'
2650    }
2651
2652    // The component's item object.
2653    clock.item = {}
2654
2655    clock.item.clear = null
2656    clock.item.interval = settings.interval || 30
2657    clock.item.disable = (settings.disable || []).slice(0)
2658    clock.item.enable = -(function (collectionDisabled) {
2659      return collectionDisabled[0] === true ? collectionDisabled.shift() : -1
2660    })(clock.item.disable)
2661
2662    clock.
2663        set('min', settings.min).
2664        set('max', settings.max).
2665        set('now')
2666
2667    // When there’s a value, set the `select`, which in turn
2668    // also sets the `highlight` and `view`.
2669    if (valueString) {
2670      clock.set('select', valueString, {
2671        format: formatString
2672      })
2673    }
2674
2675      // If there’s no value, default to highlighting “today”.
2676    else {
2677      clock.
2678          set('select', null).
2679          set('highlight', clock.item.now)
2680    }
2681
2682    // The keycode to movement mapping.
2683    clock.key = {
2684      40: 1, // Down
2685      38: -1, // Up
2686      39: 1, // Right
2687      37: -1, // Left
2688      go: function (timeChange) {
2689        clock.set(
2690            'highlight',
2691            clock.item.highlight.pick + timeChange * clock.item.interval,
2692            { interval: timeChange * clock.item.interval }
2693        )
2694        this.render()
2695      }
2696    }
2697
2698
2699    // Bind some picker events.
2700    picker.
2701        on('render', function () {
2702          var $pickerHolder = picker.$root.children(),
2703              $viewset = $pickerHolder.find('.' + settings.klass.viewset),
2704              vendors = function (prop) {
2705                return ['webkit', 'moz', 'ms', 'o', ''].map(function (vendor) {
2706                  return (vendor ? '-' + vendor + '-' : '') + prop
2707                })
2708              },
2709              animations = function ($el, state) {
2710                vendors('transform').map(function (prop) {
2711                  $el.css(prop, state)
2712                })
2713                vendors('transition').map(function (prop) {
2714                  $el.css(prop, state)
2715                })
2716              }
2717          if ($viewset.length) {
2718            animations($pickerHolder, 'none')
2719            $pickerHolder[0].scrollTop = ~~$viewset.position().top - ($viewset[0].clientHeight * 2)
2720            animations($pickerHolder, '')
2721          }
2722        }, 1).
2723        on('open', function () {
2724          picker.$root.find('button').attr('disabled', false)
2725        }, 1).
2726        on('close', function () {
2727          picker.$root.find('button').attr('disabled', true)
2728        }, 1)
2729
2730  } //TimePicker
2731
2732
2733  /**
2734   * Set a timepicker item object.
2735   */
2736  TimePicker.prototype.set = function (type, value, options) {
2737
2738    var clock = this,
2739        clockItem = clock.item
2740
2741    // If the value is `null` just set it immediately.
2742    if (value === null) {
2743      if (type == 'clear') type = 'select'
2744      clockItem[type] = value
2745      return clock
2746    }
2747
2748    // Otherwise go through the queue of methods, and invoke the functions.
2749    // Update this as the time unit, and set the final value as this item.
2750    // * In the case of `enable`, keep the queue but set `disable` instead.
2751    //   And in the case of `flip`, keep the queue but set `enable` instead.
2752    clockItem[(type == 'enable' ? 'disable' : type == 'flip' ? 'enable' : type)] = clock.queue[type].split(' ').map(function (method) {
2753      value = clock[method](type, value, options)
2754      return value
2755    }).pop()
2756
2757    // Check if we need to cascade through more updates.
2758    if (type == 'select') {
2759      clock.set('highlight', clockItem.select, options)
2760    }
2761    else if (type == 'highlight') {
2762      clock.set('view', clockItem.highlight, options)
2763    }
2764    else if (type == 'interval') {
2765      clock.
2766          set('min', clockItem.min, options).
2767          set('max', clockItem.max, options)
2768    }
2769    else if (type.match(/^(flip|min|max|disable|enable)$/)) {
2770      if (clockItem.select && clock.disabled(clockItem.select)) {
2771        clock.set('select', value, options)
2772      }
2773      if (clockItem.highlight && clock.disabled(clockItem.highlight)) {
2774        clock.set('highlight', value, options)
2775      }
2776      if (type == 'min') {
2777        clock.set('max', clockItem.max, options)
2778      }
2779    }
2780
2781    return clock
2782  } //TimePicker.prototype.set
2783
2784
2785  /**
2786   * Get a timepicker item object.
2787   */
2788  TimePicker.prototype.get = function (type) {
2789    return this.item[type]
2790  } //TimePicker.prototype.get
2791
2792
2793  /**
2794   * Create a picker time object.
2795   */
2796  TimePicker.prototype.create = function (type, value, options) {
2797
2798    var clock = this
2799
2800    // If there’s no value, use the type as the value.
2801    value = value === undefined ? type : value
2802
2803    // If it’s a date object, convert it into an array.
2804    if (_.isDate(value)) {
2805      value = [value.getHours(), value.getMinutes()]
2806    }
2807
2808    // If it’s an object, use the “pick” value.
2809    if ($.isPlainObject(value) && _.isInteger(value.pick)) {
2810      value = value.pick
2811    }
2812
2813      // If it’s an array, convert it into minutes.
2814    else if ($.isArray(value)) {
2815      value = +value[0] * MINUTES_IN_HOUR + (+value[1])
2816    }
2817
2818      // If no valid value is passed, set it to “now”.
2819    else if (!_.isInteger(value)) {
2820      value = clock.now(type, value, options)
2821    }
2822
2823    // If we’re setting the max, make sure it’s greater than the min.
2824    if (type == 'max' && value < clock.item.min.pick) {
2825      value += MINUTES_IN_DAY
2826    }
2827
2828    // If the value doesn’t fall directly on the interval,
2829    // add one interval to indicate it as “passed”.
2830    if (type != 'min' && type != 'max' && (value - clock.item.min.pick) % clock.item.interval !== 0) {
2831      value += clock.item.interval
2832    }
2833
2834    // Normalize it into a “reachable” interval.
2835    value = clock.normalize(type, value, options)
2836
2837    // Return the compiled object.
2838    return {
2839
2840      // Divide to get hours from minutes.
2841      hour: ~~(HOURS_IN_DAY + value / MINUTES_IN_HOUR) % HOURS_IN_DAY,
2842
2843      // The remainder is the minutes.
2844      mins: (MINUTES_IN_HOUR + value % MINUTES_IN_HOUR) % MINUTES_IN_HOUR,
2845
2846      // The time in total minutes.
2847      time: (MINUTES_IN_DAY + value) % MINUTES_IN_DAY,
2848
2849      // Reference to the “relative” value to pick.
2850      pick: value % MINUTES_IN_DAY
2851    }
2852  } //TimePicker.prototype.create
2853
2854
2855  /**
2856   * Create a range limit object using an array, date object,
2857   * literal “true”, or integer relative to another time.
2858   */
2859  TimePicker.prototype.createRange = function (from, to) {
2860
2861    var clock = this,
2862        createTime = function (time) {
2863          if (time === true || $.isArray(time) || _.isDate(time)) {
2864            return clock.create(time)
2865          }
2866          return time
2867        }
2868
2869    // Create objects if possible.
2870    if (!_.isInteger(from)) {
2871      from = createTime(from)
2872    }
2873    if (!_.isInteger(to)) {
2874      to = createTime(to)
2875    }
2876
2877    // Create relative times.
2878    if (_.isInteger(from) && $.isPlainObject(to)) {
2879      from = [to.hour, to.mins + (from * clock.settings.interval)];
2880    }
2881    else if (_.isInteger(to) && $.isPlainObject(from)) {
2882      to = [from.hour, from.mins + (to * clock.settings.interval)];
2883    }
2884
2885    return {
2886      from: createTime(from),
2887      to: createTime(to)
2888    }
2889  } //TimePicker.prototype.createRange
2890
2891
2892  /**
2893   * Check if a time unit falls within a time range object.
2894   */
2895  TimePicker.prototype.withinRange = function (range, timeUnit) {
2896    range = this.createRange(range.from, range.to)
2897    return timeUnit.pick >= range.from.pick && timeUnit.pick <= range.to.pick
2898  }
2899
2900
2901  /**
2902   * Check if two time range objects overlap.
2903   */
2904  TimePicker.prototype.overlapRanges = function (one, two) {
2905
2906    var clock = this
2907
2908    // Convert the ranges into comparable times.
2909    one = clock.createRange(one.from, one.to)
2910    two = clock.createRange(two.from, two.to)
2911
2912    return clock.withinRange(one, two.from) || clock.withinRange(one, two.to) ||
2913        clock.withinRange(two, one.from) || clock.withinRange(two, one.to)
2914  }
2915
2916
2917  /**
2918   * Get the time relative to now.
2919   */
2920  TimePicker.prototype.now = function (type, value/*, options*/) {
2921
2922    var interval = this.item.interval,
2923        date = new Date(),
2924        nowMinutes = date.getHours() * MINUTES_IN_HOUR + date.getMinutes(),
2925        isValueInteger = _.isInteger(value),
2926        isBelowInterval
2927
2928    // Make sure “now” falls within the interval range.
2929    nowMinutes -= nowMinutes % interval
2930
2931    // Check if the difference is less than the interval itself.
2932    isBelowInterval = value < 0 && interval * value + nowMinutes <= -interval
2933
2934    // Add an interval because the time has “passed”.
2935    nowMinutes += type == 'min' && isBelowInterval ? 0 : interval
2936
2937    // If the value is a number, adjust by that many intervals.
2938    if (isValueInteger) {
2939      nowMinutes += interval * (
2940          isBelowInterval && type != 'max' ?
2941              value + 1 :
2942              value
2943          )
2944    }
2945
2946    // Return the final calculation.
2947    return nowMinutes
2948  } //TimePicker.prototype.now
2949
2950
2951  /**
2952   * Normalize minutes to be “reachable” based on the min and interval.
2953   */
2954  TimePicker.prototype.normalize = function (type, value/*, options*/) {
2955
2956    var interval = this.item.interval,
2957        minTime = this.item.min && this.item.min.pick || 0
2958
2959    // If setting min time, don’t shift anything.
2960    // Otherwise get the value and min difference and then
2961    // normalize the difference with the interval.
2962    value -= type == 'min' ? 0 : (value - minTime) % interval
2963
2964    // Return the adjusted value.
2965    return value
2966  } //TimePicker.prototype.normalize
2967
2968
2969  /**
2970   * Measure the range of minutes.
2971   */
2972  TimePicker.prototype.measure = function (type, value, options) {
2973
2974    var clock = this
2975
2976    // If it’s anything false-y, set it to the default.
2977    if (!value) {
2978      value = type == 'min' ? [0, 0] : [HOURS_IN_DAY - 1, MINUTES_IN_HOUR - 1]
2979    }
2980
2981    // If it’s a string, parse it.
2982    if (typeof value == 'string') {
2983      value = clock.parse(type, value)
2984    }
2985
2986      // If it’s a literal true, or an integer, make it relative to now.
2987    else if (value === true || _.isInteger(value)) {
2988      value = clock.now(type, value, options)
2989    }
2990
2991      // If it’s an object already, just normalize it.
2992    else if ($.isPlainObject(value) && _.isInteger(value.pick)) {
2993      value = clock.normalize(type, value.pick, options)
2994    }
2995
2996    return value
2997  } ///TimePicker.prototype.measure
2998
2999
3000  /**
3001   * Validate an object as enabled.
3002   */
3003  TimePicker.prototype.validate = function (type, timeObject, options) {
3004
3005    var clock = this,
3006        interval = options && options.interval ? options.interval : clock.item.interval
3007
3008    // Check if the object is disabled.
3009    if (clock.disabled(timeObject)) {
3010
3011      // Shift with the interval until we reach an enabled time.
3012      timeObject = clock.shift(timeObject, interval)
3013    }
3014
3015    // Scope the object into range.
3016    timeObject = clock.scope(timeObject)
3017
3018    // Do a second check to see if we landed on a disabled min/max.
3019    // In that case, shift using the opposite interval as before.
3020    if (clock.disabled(timeObject)) {
3021      timeObject = clock.shift(timeObject, interval * -1)
3022    }
3023
3024    // Return the final object.
3025    return timeObject
3026  } //TimePicker.prototype.validate
3027
3028
3029  /**
3030   * Check if an object is disabled.
3031   */
3032  TimePicker.prototype.disabled = function (timeToVerify) {
3033
3034    var clock = this,
3035
3036        // Filter through the disabled times to check if this is one.
3037        isDisabledMatch = clock.item.disable.filter(function (timeToDisable) {
3038
3039          // If the time is a number, match the hours.
3040          if (_.isInteger(timeToDisable)) {
3041            return timeToVerify.hour == timeToDisable
3042          }
3043
3044          // If it’s an array, create the object and match the times.
3045          if ($.isArray(timeToDisable) || _.isDate(timeToDisable)) {
3046            return timeToVerify.pick == clock.create(timeToDisable).pick
3047          }
3048
3049          // If it’s an object, match a time within the “from” and “to” range.
3050          if ($.isPlainObject(timeToDisable)) {
3051            return clock.withinRange(timeToDisable, timeToVerify)
3052          }
3053        })
3054
3055    // If this time matches a disabled time, confirm it’s not inverted.
3056    isDisabledMatch = isDisabledMatch.length && !isDisabledMatch.filter(function (timeToDisable) {
3057      return $.isArray(timeToDisable) && timeToDisable[2] == 'inverted' ||
3058          $.isPlainObject(timeToDisable) && timeToDisable.inverted
3059    }).length
3060
3061    // If the clock is "enabled" flag is flipped, flip the condition.
3062    return clock.item.enable === -1 ? !isDisabledMatch : isDisabledMatch ||
3063        timeToVerify.pick < clock.item.min.pick ||
3064        timeToVerify.pick > clock.item.max.pick
3065  } //TimePicker.prototype.disabled
3066
3067
3068  /**
3069   * Shift an object by an interval until we reach an enabled object.
3070   */
3071  TimePicker.prototype.shift = function (timeObject, interval) {
3072
3073    var clock = this,
3074        minLimit = clock.item.min.pick,
3075        maxLimit = clock.item.max.pick/*,
3076        safety = 1000*/
3077
3078    interval = interval || clock.item.interval
3079
3080    // Keep looping as long as the time is disabled.
3081    while ( /*safety &&*/ clock.disabled(timeObject)) {
3082
3083      /*safety -= 1
3084      if ( !safety ) {
3085          throw 'Fell into an infinite loop while shifting to ' + timeObject.hour + ':' + timeObject.mins + '.'
3086      }*/
3087
3088      // Increase/decrease the time by the interval and keep looping.
3089      timeObject = clock.create(timeObject.pick += interval)
3090
3091      // If we've looped beyond the limits, break out of the loop.
3092      if (timeObject.pick <= minLimit || timeObject.pick >= maxLimit) {
3093        break
3094      }
3095    }
3096
3097    // Return the final object.
3098    return timeObject
3099  } //TimePicker.prototype.shift
3100
3101
3102  /**
3103   * Scope an object to be within range of min and max.
3104   */
3105  TimePicker.prototype.scope = function (timeObject) {
3106    var minLimit = this.item.min.pick,
3107        maxLimit = this.item.max.pick
3108    return this.create(timeObject.pick > maxLimit ? maxLimit : timeObject.pick < minLimit ? minLimit : timeObject)
3109  } //TimePicker.prototype.scope
3110
3111
3112  /**
3113   * Parse a string into a usable type.
3114   */
3115  TimePicker.prototype.parse = function (type, value, options) {
3116
3117    var hour, minutes, isPM, item, parseValue,
3118        clock = this,
3119        parsingObject = {}
3120
3121    // If it’s already parsed, we’re good.
3122    if (!value || typeof value != 'string') {
3123      return value
3124    }
3125
3126    // We need a `.format` to parse the value with.
3127    if (!(options && options.format)) {
3128      options = options || {}
3129      options.format = clock.settings.format
3130    }
3131
3132    // Convert the format into an array and then map through it.
3133    clock.formats.toArray(options.format).map(function (label) {
3134
3135      var
3136          substring,
3137
3138          // Grab the formatting label.
3139          formattingLabel = clock.formats[label],
3140
3141          // The format length is from the formatting label function or the
3142          // label length without the escaping exclamation (!) mark.
3143          formatLength = formattingLabel ?
3144              _.trigger(formattingLabel, clock, [value, parsingObject]) :
3145              label.replace(/^!/, '').length
3146
3147      // If there's a format label, split the value up to the format length.
3148      // Then add it to the parsing object with appropriate label.
3149      if (formattingLabel) {
3150        substring = value.substr(0, formatLength)
3151        parsingObject[label] = substring.match(/^\d+$/) ? +substring : substring
3152      }
3153
3154      // Update the time value as the substring from format length to end.
3155      value = value.substr(formatLength)
3156    })
3157
3158    // Grab the hour and minutes from the parsing object.
3159    for (item in parsingObject) {
3160      parseValue = parsingObject[item]
3161      if (_.isInteger(parseValue)) {
3162        if (item.match(/^(h|hh)$/i)) {
3163          hour = parseValue
3164          if (item == 'h' || item == 'hh') {
3165            hour %= 12
3166          }
3167        }
3168        else if (item == 'i') {
3169          minutes = parseValue
3170        }
3171      }
3172      else if (item.match(/^a$/i) && parseValue.match(/^p/i) && ('h' in parsingObject || 'hh' in parsingObject)) {
3173        isPM = true
3174      }
3175    }
3176
3177    // Calculate it in minutes and return.
3178    return (isPM ? hour + 12 : hour) * MINUTES_IN_HOUR + minutes
3179  } //TimePicker.prototype.parse
3180
3181
3182  /**
3183   * Various formats to display the object in.
3184   */
3185  TimePicker.prototype.formats = {
3186
3187    h: function (string, timeObject) {
3188
3189      // If there's string, then get the digits length.
3190      // Otherwise return the selected hour in "standard" format.
3191      return string ? _.digits(string) : timeObject.hour % HOURS_TO_NOON || HOURS_TO_NOON
3192    },
3193    hh: function (string, timeObject) {
3194
3195      // If there's a string, then the length is always 2.
3196      // Otherwise return the selected hour in "standard" format with a leading zero.
3197      return string ? 2 : _.lead(timeObject.hour % HOURS_TO_NOON || HOURS_TO_NOON)
3198    },
3199    H: function (string, timeObject) {
3200
3201      // If there's string, then get the digits length.
3202      // Otherwise return the selected hour in "military" format as a string.
3203      return string ? _.digits(string) : '' + (timeObject.hour % 24)
3204    },
3205    HH: function (string, timeObject) {
3206
3207      // If there's string, then get the digits length.
3208      // Otherwise return the selected hour in "military" format with a leading zero.
3209      return string ? _.digits(string) : _.lead(timeObject.hour % 24)
3210    },
3211    i: function (string, timeObject) {
3212
3213      // If there's a string, then the length is always 2.
3214      // Otherwise return the selected minutes.
3215      return string ? 2 : _.lead(timeObject.mins)
3216    },
3217    a: function (string, timeObject) {
3218
3219      // If there's a string, then the length is always 4.
3220      // Otherwise check if it's more than "noon" and return either am/pm.
3221      return string ? 4 : MINUTES_IN_DAY / 2 > timeObject.time % MINUTES_IN_DAY ? 'a.m.' : 'p.m.'
3222    },
3223    A: function (string, timeObject) {
3224
3225      // If there's a string, then the length is always 2.
3226      // Otherwise check if it's more than "noon" and return either am/pm.
3227      return string ? 2 : MINUTES_IN_DAY / 2 > timeObject.time % MINUTES_IN_DAY ? 'AM' : 'PM'
3228    },
3229
3230    // Create an array by splitting the formatting string passed.
3231    toArray: function (formatString) { return formatString.split(/(h{1,2}|H{1,2}|i|a|A|!.)/g) },
3232
3233    // Format an object into a string using the formatting options.
3234    toString: function (formatString, itemObject) {
3235      var clock = this
3236      return clock.formats.toArray(formatString).map(function (label) {
3237        return _.trigger(clock.formats[label], clock, [0, itemObject]) || label.replace(/^!/, '')
3238      }).join('')
3239    }
3240  } //TimePicker.prototype.formats
3241
3242
3243
3244
3245  /**
3246   * Check if two time units are the exact.
3247   */
3248  TimePicker.prototype.isTimeExact = function (one, two) {
3249
3250    var clock = this
3251
3252    // When we’re working with minutes, do a direct comparison.
3253    if (
3254        (_.isInteger(one) && _.isInteger(two)) ||
3255        (typeof one == 'boolean' && typeof two == 'boolean')
3256     ) {
3257      return one === two
3258    }
3259
3260    // When we’re working with time representations, compare the “pick” value.
3261    if (
3262        (_.isDate(one) || $.isArray(one)) &&
3263        (_.isDate(two) || $.isArray(two))
3264    ) {
3265      return clock.create(one).pick === clock.create(two).pick
3266    }
3267
3268    // When we’re working with range objects, compare the “from” and “to”.
3269    if ($.isPlainObject(one) && $.isPlainObject(two)) {
3270      return clock.isTimeExact(one.from, two.from) && clock.isTimeExact(one.to, two.to)
3271    }
3272
3273    return false
3274  }
3275
3276
3277  /**
3278   * Check if two time units overlap.
3279   */
3280  TimePicker.prototype.isTimeOverlap = function (one, two) {
3281
3282    var clock = this
3283
3284    // When we’re working with an integer, compare the hours.
3285    if (_.isInteger(one) && (_.isDate(two) || $.isArray(two))) {
3286      return one === clock.create(two).hour
3287    }
3288    if (_.isInteger(two) && (_.isDate(one) || $.isArray(one))) {
3289      return two === clock.create(one).hour
3290    }
3291
3292    // When we’re working with range objects, check if the ranges overlap.
3293    if ($.isPlainObject(one) && $.isPlainObject(two)) {
3294      return clock.overlapRanges(one, two)
3295    }
3296
3297    return false
3298  }
3299
3300
3301  /**
3302   * Flip the “enabled” state.
3303   */
3304  TimePicker.prototype.flipEnable = function (val) {
3305    var itemObject = this.item
3306    itemObject.enable = val || (itemObject.enable == -1 ? 1 : -1)
3307  }
3308
3309
3310  /**
3311   * Mark a collection of times as “disabled”.
3312   */
3313  TimePicker.prototype.deactivate = function (type, timesToDisable) {
3314
3315    var clock = this,
3316        disabledItems = clock.item.disable.slice(0)
3317
3318
3319    // If we’re flipping, that’s all we need to do.
3320    if (timesToDisable == 'flip') {
3321      clock.flipEnable()
3322    }
3323
3324    else if (timesToDisable === false) {
3325      clock.flipEnable(1)
3326      disabledItems = []
3327    }
3328
3329    else if (timesToDisable === true) {
3330      clock.flipEnable(-1)
3331      disabledItems = []
3332    }
3333
3334      // Otherwise go through the times to disable.
3335    else {
3336
3337      timesToDisable.map(function (unitToDisable) {
3338
3339        var matchFound
3340
3341        // When we have disabled items, check for matches.
3342        // If something is matched, immediately break out.
3343        for (var index = 0; index < disabledItems.length; index += 1) {
3344          if (clock.isTimeExact(unitToDisable, disabledItems[index])) {
3345            matchFound = true
3346            break
3347          }
3348        }
3349
3350        // If nothing was found, add the validated unit to the collection.
3351        if (!matchFound) {
3352          if (
3353              _.isInteger(unitToDisable) ||
3354              _.isDate(unitToDisable) ||
3355              $.isArray(unitToDisable) ||
3356              ($.isPlainObject(unitToDisable) && unitToDisable.from && unitToDisable.to)
3357          ) {
3358            disabledItems.push(unitToDisable)
3359          }
3360        }
3361      })
3362    }
3363
3364    // Return the updated collection.
3365    return disabledItems
3366  } //TimePicker.prototype.deactivate
3367
3368
3369  /**
3370   * Mark a collection of times as “enabled”.
3371   */
3372  TimePicker.prototype.activate = function (type, timesToEnable) {
3373
3374    var clock = this,
3375        disabledItems = clock.item.disable,
3376        disabledItemsCount = disabledItems.length
3377
3378    // If we’re flipping, that’s all we need to do.
3379    if (timesToEnable == 'flip') {
3380      clock.flipEnable()
3381    }
3382
3383    else if (timesToEnable === true) {
3384      clock.flipEnable(1)
3385      disabledItems = []
3386    }
3387
3388    else if (timesToEnable === false) {
3389      clock.flipEnable(-1)
3390      disabledItems = []
3391    }
3392
3393      // Otherwise go through the disabled times.
3394    else {
3395
3396      timesToEnable.map(function (unitToEnable) {
3397
3398        var matchFound,
3399            disabledUnit,
3400            index,
3401            isRangeMatched
3402
3403        // Go through the disabled items and try to find a match.
3404        for (index = 0; index < disabledItemsCount; index += 1) {
3405
3406          disabledUnit = disabledItems[index]
3407
3408          // When an exact match is found, remove it from the collection.
3409          if (clock.isTimeExact(disabledUnit, unitToEnable)) {
3410            matchFound = disabledItems[index] = null
3411            isRangeMatched = true
3412            break
3413          }
3414
3415            // When an overlapped match is found, add the “inverted” state to it.
3416          else if (clock.isTimeOverlap(disabledUnit, unitToEnable)) {
3417            if ($.isPlainObject(unitToEnable)) {
3418              unitToEnable.inverted = true
3419              matchFound = unitToEnable
3420            }
3421            else if ($.isArray(unitToEnable)) {
3422              matchFound = unitToEnable
3423              if (!matchFound[2]) matchFound.push('inverted')
3424            }
3425            else if (_.isDate(unitToEnable)) {
3426              matchFound = [unitToEnable.getFullYear(), unitToEnable.getMonth(), unitToEnable.getDate(), 'inverted']
3427            }
3428            break
3429          }
3430        }
3431
3432        // If a match was found, remove a previous duplicate entry.
3433        if (matchFound) for (index = 0; index < disabledItemsCount; index += 1) {
3434          if (clock.isTimeExact(disabledItems[index], unitToEnable)) {
3435            disabledItems[index] = null
3436            break
3437          }
3438        }
3439
3440        // In the event that we’re dealing with an overlap of range times,
3441        // make sure there are no “inverted” times because of it.
3442        if (isRangeMatched) for (index = 0; index < disabledItemsCount; index += 1) {
3443          if (clock.isTimeOverlap(disabledItems[index], unitToEnable)) {
3444            disabledItems[index] = null
3445            break
3446          }
3447        }
3448
3449        // If something is still matched, add it into the collection.
3450        if (matchFound) {
3451          disabledItems.push(matchFound)
3452        }
3453      })
3454    }
3455
3456    // Return the updated collection.
3457    return disabledItems.filter(function (val) { return val != null })
3458  } //TimePicker.prototype.activate
3459
3460
3461  /**
3462   * The division to use for the range intervals.
3463   */
3464  TimePicker.prototype.i = function (type, value/*, options*/) {
3465    return _.isInteger(value) && value > 0 ? value : this.item.interval
3466  }
3467
3468
3469  /**
3470   * Create a string for the nodes in the picker.
3471   */
3472  TimePicker.prototype.nodes = function (isOpen) {
3473
3474    var
3475        clock = this,
3476        settings = clock.settings,
3477        selectedObject = clock.item.select,
3478        highlightedObject = clock.item.highlight,
3479        viewsetObject = clock.item.view,
3480        disabledCollection = clock.item.disable
3481
3482    return _.node(
3483        'ul',
3484        _.group({
3485          min: clock.item.min.pick,
3486          max: clock.item.max.pick,
3487          i: clock.item.interval,
3488          node: 'li',
3489          item: function (loopedTime) {
3490            loopedTime = clock.create(loopedTime)
3491            var timeMinutes = loopedTime.pick,
3492                isSelected = selectedObject && selectedObject.pick == timeMinutes,
3493                isHighlighted = highlightedObject && highlightedObject.pick == timeMinutes,
3494                isDisabled = disabledCollection && clock.disabled(loopedTime),
3495                formattedTime = _.trigger(clock.formats.toString, clock, [settings.format, loopedTime])
3496            return [
3497                _.trigger(clock.formats.toString, clock, [_.trigger(settings.formatLabel, clock, [loopedTime]) || settings.format, loopedTime]),
3498                (function (klasses) {
3499
3500                  if (isSelected) {
3501                    klasses.push(settings.klass.selected)
3502                  }
3503
3504                  if (isHighlighted) {
3505                    klasses.push(settings.klass.highlighted)
3506                  }
3507
3508                  if (viewsetObject && viewsetObject.pick == timeMinutes) {
3509                    klasses.push(settings.klass.viewset)
3510                  }
3511
3512                  if (isDisabled) {
3513                    klasses.push(settings.klass.disabled)
3514                  }
3515
3516                  return klasses.join(' ')
3517                })([settings.klass.listItem]),
3518                'data-pick=' + loopedTime.pick + ' ' + _.ariaAttr({
3519                  role: 'option',
3520                  label: formattedTime,
3521                  selected: isSelected && clock.$node.val() === formattedTime ? true : null,
3522                  activedescendant: isHighlighted ? true : null,
3523                  disabled: isDisabled ? true : null
3524                })
3525            ]
3526          }
3527        }) +
3528
3529        // * For Firefox forms to submit, make sure to set the button’s `type` attribute as “button”.
3530        _.node(
3531            'li',
3532            _.node(
3533                'button',
3534                settings.clear,
3535                settings.klass.buttonClear,
3536                'type=button data-clear=1' + (isOpen ? '' : ' disabled') + ' ' +
3537                _.ariaAttr({ controls: clock.$node[0].id })
3538            ),
3539            '', _.ariaAttr({ role: 'presentation' })
3540        ),
3541        settings.klass.list,
3542        _.ariaAttr({ role: 'listbox', controls: clock.$node[0].id })
3543    )
3544  } //TimePicker.prototype.nodes
3545
3546
3547
3548
3549
3550
3551
3552  /**
3553   * Extend the picker to add the component with the defaults.
3554   */
3555  TimePicker.defaults = (function (prefix) {
3556
3557    return {
3558
3559      // Clear
3560      clear: 'Clear',
3561
3562      // The format to show on the `input` element
3563      format: 'h:i A',
3564
3565      // The interval between each time
3566      interval: 30,
3567
3568      // Picker close behavior
3569      closeOnSelect: true,
3570      closeOnClear: true,
3571
3572      // Classes
3573      klass: {
3574
3575        picker: prefix + ' ' + prefix + '--time',
3576        holder: prefix + '__holder',
3577
3578        list: prefix + '__list',
3579        listItem: prefix + '__list-item',
3580
3581        disabled: prefix + '__list-item--disabled',
3582        selected: prefix + '__list-item--selected',
3583        highlighted: prefix + '__list-item--highlighted',
3584        viewset: prefix + '__list-item--viewset',
3585        now: prefix + '__list-item--now',
3586
3587        buttonClear: prefix + '__button--clear'
3588      }
3589    }
3590  })(Picker.klasses().picker)
3591
3592
3593
3594
3595
3596  /**
3597   * Extend the picker to add the time picker.
3598   */
3599  Picker.extend('pickatime', TimePicker)
3600
3601
3602}));
3603
3604
3605
3606
3607
3608

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.