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.