1/*! 2 * pickadate.js v3.5.6, 2015/04/20 3 * By Amsul, http://amsul.ca 4 * Hosted on http://amsul.github.io/pickadate.js 5 * Licensed under MIT 6 */ 7 8(function ( factory ) { 9 10 // AMD. 11 if ( typeof define == 'function' && define.amd ) 12 define( 'picker', ['jquery'], factory ) 13 14 // Node.js/browserify. 15 else if ( typeof exports == 'object' ) 16 module.exports = factory( require('jquery') ) 17 18 // Browser globals. 19 else this.Picker = factory( jQuery ) 20 21}(function( $ ) { 22 23var $window = $( window ) 24var $document = $( document ) 25var $html = $( document.documentElement ) 26var supportsTransitions = document.documentElement.style.transition != null 27 28 29/** 30 * The picker constructor that creates a blank picker. 31 */ 32function PickerConstructor( ELEMENT, NAME, COMPONENT, OPTIONS ) { 33 34 // If thereâs no element, return the picker constructor. 35 if ( !ELEMENT ) return PickerConstructor 36 37 38 var 39 IS_DEFAULT_THEME = false, 40 41 42 // The state of the picker. 43 STATE = { 44 id: ELEMENT.id || 'P' + Math.abs( ~~(Math.random() * new Date()) ) 45 }, 46 47 48 // Merge the defaults and options passed. 49 SETTINGS = COMPONENT ? $.extend( true, {}, COMPONENT.defaults, OPTIONS ) : OPTIONS || {}, 50 51 52 // Merge the default classes with the settings classes. 53 CLASSES = $.extend( {}, PickerConstructor.klasses(), SETTINGS.klass ), 54 55 56 // The element node wrapper into a jQuery object. 57 $ELEMENT = $( ELEMENT ), 58 59 60 // Pseudo picker constructor. 61 PickerInstance = function() { 62 return this.start() 63 }, 64 65 66 // The picker prototype. 67 P = PickerInstance.prototype = { 68 69 constructor: PickerInstance, 70 71 $node: $ELEMENT, 72 73 74 /** 75 * Initialize everything 76 */ 77 start: function() { 78 79 // If itâs already started, do nothing. 80 if ( STATE && STATE.start ) return P 81 82 83 // Update the picker states. 84 STATE.methods = {} 85 STATE.start = true 86 STATE.open = false 87 STATE.type = ELEMENT.type 88 89 90 // Confirm focus state, convert into text input to remove UA stylings, 91 // and set as readonly to prevent keyboard popup. 92 ELEMENT.autofocus = ELEMENT == getActiveElement() 93 ELEMENT.readOnly = !SETTINGS.editable 94 ELEMENT.id = ELEMENT.id || STATE.id 95 if ( ELEMENT.type != 'text' ) { 96 ELEMENT.type = 'text' 97 } 98 99 100 // Create a new picker component with the settings. 101 P.component = new COMPONENT(P, SETTINGS) 102 103 104 // Create the picker root and then prepare it. 105 P.$root = $( '<div class="' + CLASSES.picker + '" id="' + ELEMENT.id + '_root" />' ) 106 prepareElementRoot() 107 108 109 // Create the picker holder and then prepare it. 110 P.$holder = $( createWrappedComponent() ).appendTo( P.$root ) 111 prepareElementHolder() 112 113 114 // If thereâs a format for the hidden input element, create the element. 115 if ( SETTINGS.formatSubmit ) { 116 prepareElementHidden() 117 } 118 119 120 // Prepare the input element. 121 prepareElement() 122 123 124 // Insert the hidden input as specified in the settings. 125 if ( SETTINGS.containerHidden ) $( SETTINGS.containerHidden ).append( P._hidden ) 126 else $ELEMENT.after( P._hidden ) 127 128 129 // Insert the root as specified in the settings. 130 if ( SETTINGS.container ) $( SETTINGS.container ).append( P.$root ) 131 else $ELEMENT.after( P.$root ) 132 133 134 // Bind the default component and settings events. 135 P.on({ 136 start: P.component.onStart, 137 render: P.component.onRender, 138 stop: P.component.onStop, 139 open: P.component.onOpen, 140 close: P.component.onClose, 141 set: P.component.onSet 142 }).on({ 143 start: SETTINGS.onStart, 144 render: SETTINGS.onRender, 145 stop: SETTINGS.onStop, 146 open: SETTINGS.onOpen, 147 close: SETTINGS.onClose, 148 set: SETTINGS.onSet 149 }) 150 151 152 // Once weâre all set, check the theme in use. 153 IS_DEFAULT_THEME = isUsingDefaultTheme( P.$holder[0] ) 154 155 156 // If the element has autofocus, open the picker. 157 if ( ELEMENT.autofocus ) { 158 P.open() 159 } 160 161 162 // Trigger queued the âstartâ and ârenderâ events. 163 return P.trigger( 'start' ).trigger( 'render' ) 164 }, //start 165 166 167 /**
168 * Render a new picker 169 */ 170 render: function( entireComponent ) { 171 172 // Insert a new component holder in the root or box. 173 if ( entireComponent ) { 174 P.$holder = $( createWrappedComponent() ) 175 prepareElementHolder() 176 P.$root.html( P.$holder ) 177 } 178 else P.$root.find( '.' + CLASSES.box ).html( P.component.nodes( STATE.open ) ) 179 180 // Trigger the queued ârenderâ events. 181 return P.trigger( 'render' ) 182 }, //render 183 184 185 /** 186 * Destroy everything 187 */ 188 stop: function() { 189 190 // If itâs already stopped, do nothing. 191 if ( !STATE.start ) return P 192 193 // Then close the picker. 194 P.close() 195 196 // Remove the hidden field. 197 if ( P._hidden ) { 198 P._hidden.parentNode.removeChild( P._hidden ) 199 } 200 201 // Remove the root. 202 P.$root.remove() 203 204 // Remove the input class, remove the stored data, and unbind 205 // the events (after a tick for IE - see `P.close`). 206 $ELEMENT.removeClass( CLASSES.input ).removeData( NAME ) 207 setTimeout( function() { 208 $ELEMENT.off( '.' + STATE.id ) 209 }, 0) 210 211 // Restore the element state 212 ELEMENT.type = STATE.type 213 ELEMENT.readOnly = false 214 215 // Trigger the queued âstopâ events. 216 P.trigger( 'stop' ) 217 218 // Reset the picker states. 219 STATE.methods = {} 220 STATE.start = false 221 222 return P 223 }, //stop 224 225 226 /** 227 * Open up the picker 228 */ 229 open: function( dontGiveFocus ) { 230 231 // If itâs already open, do nothing. 232 if ( STATE.open ) return P 233 234 // Add the âactiveâ class. 235 $ELEMENT.addClass( CLASSES.active ) 236 aria( ELEMENT, 'expanded', true ) 237 238 // * A Firefox bug, when `html` has `overflow:hidden`, results in 239 // killing transitions :(. So add the âopenedâ state on the next tick. 240 // Bug: https://bugzilla.mozilla.org/show_bug.cgi?id=625289 241 setTimeout( function() { 242 243 // Add the âopenedâ class to the picker root. 244 P.$root.addClass( CLASSES.opened ) 245 aria( P.$root[0], 'hidden', false ) 246 247 }, 0 ) 248 249 // If we have to give focus, bind the element and doc events. 250 if ( dontGiveFocus !== false ) { 251 252 // Set it as open. 253 STATE.open = true 254 255 // Prevent the page from scrolling. 256 if ( IS_DEFAULT_THEME ) { 257 $html. 258 css( 'overflow', 'hidden' ). 259 css( 'padding-right', '+=' + getScrollbarWidth() ) 260 } 261 262 // Pass focus to the root elementâs jQuery object. 263 focusPickerOnceOpened() 264 265 // Bind the document events. 266 $document.on( 'click.' + STATE.id + ' focusin.' + STATE.id, function( event ) { 267 268 var target = event.target 269 270 // If the target of the event is not the element, close the picker picker. 271 // * Donât worry about clicks or focusins on the root because those donât bubble up. 272 // Also, for Firefox, a click on an `option` element bubbles up directly 273 // to the doc. So make sure the target wasn't the doc. 274 // * In Firefox stopPropagation() doesnât prevent right-click events from bubbling, 275 // which causes the picker to unexpectedly close when right-clicking it. So make 276 // sure the event wasnât a right-click. 277 if ( target != ELEMENT && target != document && event.which != 3 ) { 278 279 // If the target was the holder that c
279overs the screen, 280 // keep the element focused to maintain tabindex. 281 P.close( target === P.$holder[0] ) 282 } 283 284 }).on( 'keydown.' + STATE.id, function( event ) { 285 286 var 287 // Get the keycode. 288 keycode = event.keyCode, 289 290 // Translate that to a selection change. 291 keycodeToMove = P.component.key[ keycode ], 292 293 // Grab the target. 294 target = event.target 295 296 297 // On escape, close the picker and give focus. 298 if ( keycode == 27 ) { 299 P.close( true ) 300 } 301 302 303 // Check if there is a key movement or âenterâ keypress on the element. 304 else if ( target == P.$holder[0] && ( keycodeToMove || keycode == 13 ) ) { 305 306 // Prevent the default action to stop page movement. 307 event.preventDefault() 308 309 // Trigger the key movement action. 310 if ( keycodeToMove ) { 311 PickerConstructor._.trigger( P.component.key.go, P, [ PickerConstructor._.trigger( keycodeToMove ) ] ) 312 } 313 314 // On âenterâ, if the highlighted item isnât disabled, set the value and close. 315 else if ( !P.$root.find( '.' + CLASSES.highlighted ).hasClass( CLASSES.disabled ) ) { 316 P.set( 'select', P.component.item.highlight ) 317 if ( SETTINGS.closeOnSelect ) { 318 P.close( true ) 319 } 320 } 321 } 322 323 324 // If the target is within the root and âenterâ is pressed, 325 // prevent the default action and trigger a click on the target instead. 326 else if ( $.contains( P.$root[0], target ) && keycode == 13 ) { 327 event.preventDefault() 328 target.click() 329 } 330 }) 331 } 332 333 // Trigger the queued âopenâ events. 334 return P.trigger( 'open' ) 335 }, //open 336 337 338 /** 339 * Close the picker 340 */ 341 close: function( giveFocus ) { 342 343 // If we need to give focus, do it before changing states. 344 if ( giveFocus ) { 345 if ( SETTINGS.editable ) { 346 ELEMENT.focus() 347 } 348 else { 349 // ....ah yes! It wouldâve been incomplete without a crazy workaround for IE :| 350 // The focus is triggered *after* the close has completed - causing it 351 // to open again. So unbind and rebind the event at the next tick. 352 P.$holder.off( 'focus.toOpen' ).focus() 353 setTimeout( function() { 354 P.$holder.on( 'focus.toOpen', handleFocusToOpenEvent ) 355 }, 0 ) 356 } 357 } 358 359 // Remove the âactiveâ class. 360 $ELEMENT.removeClass( CLASSES.active ) 361 aria( ELEMENT, 'expanded', false ) 362 363 // * A Firefox bug, when `html` has `overflow:hidden`, results in 364 // killing transitions :(. So remove the âopenedâ state on the next tick. 365 // Bug: https://bugzilla.mozilla.org/show_bug.cgi?id=625289 366 setTimeout( function() { 367 368 // Remove the âopenedâ and âfocusedâ class from the picker root. 369 P.$root.removeClass( CLASSES.opened + ' ' + CLASSES.focused ) 370 aria( P.$root[0], 'hidden', true ) 371 372 }, 0 ) 373 374 // If itâs already closed, do nothing more. 375 if ( !STATE.open ) return P 376 377 // Set it as closed. 378 STATE.open = false 379 380 // Allow the page to scroll. 381 if ( IS_DEFAULT_THEME ) { 382 $html. 383 css( 'overflow', '' ). 384 css( 'padding-right', '-=' + getScrollbarWidth() ) 385 } 386 387 // Unbind the document events. 388 $document.off( '.' + STATE.id ) 389 390 // Trigger the queued âcloseâ events. 391 return P.trigger( 'close' ) 392 }, //close 393 394 395 /** 396 * Clear the values 397 */ 398 clear: function( options ) { 399 return P.set( 'clear', null, options ) 400 }, //clear 401 402 403 /** 404 * Set something 405 */ 406 set: function( thing, value, options ) { 407 408 var thingItem, thingValue, 409 thingIsObject = $.isPlainObject( thing ), 410 thingObject = thingIsObject ? thing : {} 411 412 // Make sure we have usable options. 413 options = thingIsObject && $.isPlainObject( value ) ? value : options || {} 414 415 if ( thing ) { 416 417 // If the thing isnât an object, make it one. 418 if ( !thingIsObject ) { 419 thingObject[ thing ] = value 420 } 421 422 // Go through the things of items to set. 423 for ( thingItem in thingObject ) { 424 425 // Grab the value of the thing. 426 thingValue = thingObject[ thingItem ] 427 428 // First, if the item exists and thereâs a value, set it. 429 if ( thingItem in P.component.item ) { 430 if ( thingValue === undefined ) thingValue = null 431 P.component.set( thingItem, thingValue, options ) 432 } 433 434 // Then, check to update the element value and broadcast a change. 435 if ( thingItem == 'select' || thingItem == 'clear' ) { 436 $ELEMENT. 437 val( thingItem == 'clear' ? '' : P.get( thingItem, SETTINGS.format ) ). 438 trigger( 'change' ) 439 } 440 } 441 442 // Render a new picker. 443 P.render() 444 } 445 446 // When the method isnât muted, trigger queued âsetâ events and pass the `thingObject`. 447 return options.muted ? P : P.trigger( 'set', thingObject ) 448 }, //set 449 450 451 /** 452 * Get something 453 */ 454 get: function( thing, format ) { 455 456 // Make sure thereâs something to get. 457 thing = thing || 'value' 458 459 // If a picker state exists, return that. 460 if ( STATE[ thing ] != null ) { 461 return STATE[ thing ] 462 } 463 464 // Return the submission value, if that. 465 if ( thing == 'valueSubmit' ) { 466 if ( P._hidden ) { 467 return P._hidden.value 468 } 469 thing = 'value' 470 } 471 472 // Return the value, if that. 473 if ( thing == 'value' ) { 474 return ELEMENT.value 475 } 476 477 // Check if a component item exists, return that. 478 if ( thing in P.component.item ) { 479 if ( typeof format == 'string' ) { 480 var thingValue = P.component.get( thing ) 481 return thingValue ? 482 PickerConstructor._.trigger( 483 P.component.formats.toString, 484 P.component, 485 [ format, thingValue ] 486 ) : '' 487 } 488 return P.component.get( thing ) 489 } 490 }, //get 491 492 493 494 /** 495 * Bind events on the things. 496 */ 497 on: function( thing, method, internal ) { 498 499 var thingName, thingMethod, 500 thingIsObject = $.isPlainObject( thing ), 501 thingObject = thingIsObject ? thing : {} 502 503 if ( thing ) { 504 505 // If the thing isnât an object, make it one. 506 if ( !thingIsObject ) { 507 thingObject[ thing ] = method 508 } 509 510 // Go through the things to bind to. 511 for ( thingName in thingObject ) { 512 513 // Grab the method of the thing. 514 thingMethod = thingObject[ thingName ] 515 516 // If it was an internal binding, prefix it. 517 if ( internal ) { 518 thingName = '_' + thingName 519 } 520 521 // Make sure the thing methods collection exists. 522 STATE.methods[ thingName ] = STATE.methods[ thingName ] || [] 523 524 // Add the method to the relative method collection. 525 STATE.methods[ thingName ].push( thingMethod ) 526 } 527 } 528 529 return P 530 }, //on 531 532 533 534 /** 535 * Unbind events on the things. 536 */ 537 off: function() { 538 var i, thingName, 539 names = arguments; 540 for ( i = 0, namesCount = names.length; i < namesCount; i += 1 ) { 541 thingName = names[i] 542 if ( thingName in STATE.methods ) { 543 delete STATE.methods[thingName] 544 } 545 } 546 return P 547 }, 548 549 550 /** 551 * Fire off method events. 552 */ 553 trigger: function( name, data ) { 554 var _trigger = function( name ) { 555 var methodList = STATE.methods[ name ] 556 if ( methodList ) { 557 methodList.map( function( method ) { 558 PickerConstructor._.trigger( method, P, [ data ] ) 559 }) 560 } 561 } 562 _trigger( '_' + name ) 563 _trigger( name ) 564 return P 565 }
565 //trigger 566 } //PickerInstance.prototype 567 568 569 /** 570 * Wrap the picker holder components together. 571 */ 572 function createWrappedComponent() { 573 574 // Create a picker wrapper holder 575 return PickerConstructor._.node( 'div', 576 577 // Create a picker wrapper node 578 PickerConstructor._.node( 'div', 579 580 // Create a picker frame 581 PickerConstructor._.node( 'div', 582 583 // Create a picker box node 584 PickerConstructor._.node( 'div', 585 586 // Create the components nodes. 587 P.component.nodes( STATE.open ), 588 589 // The picker box class 590 CLASSES.box 591 ), 592 593 // Picker wrap class 594 CLASSES.wrap 595 ), 596 597 // Picker frame class 598 CLASSES.frame 599 ), 600 601 // Picker holder class 602 CLASSES.holder, 603 604 'tabindex="-1"' 605 ) //endreturn 606 } //createWrappedComponent 607 608 609 610 /** 611 * Prepare the input element with all bindings. 612 */ 613 function prepareElement() { 614 615 $ELEMENT. 616 617 // Store the picker data by component name. 618 data(NAME, P). 619 620 // Add the âinputâ class name. 621 addClass(CLASSES.input). 622 623 // If thereâs a `data-value`, update the value of the element. 624 val( $ELEMENT.data('value') ? 625 P.get('select', SETTINGS.format) : 626 ELEMENT.value 627 ) 628 629 630 // Only bind keydown events if the element isnât editable. 631 if ( !SETTINGS.editable ) { 632 633 $ELEMENT. 634 635 // On focus/click, open the picker. 636 on( 'focus.' + STATE.id + ' click.' + STATE.id, function(event) { 637 event.preventDefault() 638 P.open() 639 }). 640 641 // Handle keyboard event based on the picker being opened or not. 642 on( 'keydown.' + STATE.id, handleKeydownEvent ) 643 } 644 645 646 // Update the aria attributes. 647 aria(ELEMENT, { 648 haspopup: true, 649 expanded: false, 650 readonly: false, 651 owns: ELEMENT.id + '_root' 652 }) 653 } 654 655 656 /** 657 * Prepare the root picker element with all bindings. 658 */ 659 function prepareElementRoot() { 660 aria( P.$root[0], 'hidden', true ) 661 } 662 663 664 /** 665 * Prepare the holder picker element with all bindings. 666 */ 667 function prepareElementHolder() { 668 669 P.$holder. 670 671 on({ 672 673 // For iOS8. 674 keydown: handleKeydownEvent, 675 676 'focus.toOpen': handleFocusToOpenEvent, 677 678 blur: function() { 679 // Remove the âtargetâ class. 680 $ELEMENT.removeClass( CLASSES.target ) 681 }, 682 683 // When something within the holder is focused, stop from bubbling 684 // to the doc and remove the âfocusedâ state from the root. 685 focusin: function( event ) { 686 P.$root.removeClass( CLASSES.focused ) 687 event.stopPropagation() 688 }, 689 690 // When something within the holder is clicked, stop it 691 // from bubbling to the doc. 692 'mousedown click': function( event ) { 693 694 var target = event.target 695 696 // Make sure the target isnât the root holder so it can bubble up. 697 if ( target != P.$holder[0] ) { 698 699 event.stopPropagation() 700 701 // * For mousedown events, cancel the default action in order to 702 // prevent cases where focus is shifted onto external elements 703 // when using things like jQuery mobile or MagnificPopup (ref: #249 & #120). 704 // Also, for Firefox, donât prevent action on the `option` element. 705 if ( event.type == 'mousedown' && !$( target ).is( 'input, select, textarea, button, option' )) { 706 707 event.preventDefault() 708 709 // Re-focus onto the holder so that users can click away 710 // from elements focused within the picker. 711 P.$holder[0].focus() 712 } 713 } 714 } 715 716 }). 717 718 // If thereâs a click on an actionable element, carry out the actions.
719 on( 'click', '[data-pick], [data-nav], [data-clear], [data-close]', function() { 720 721 var $target = $( this ), 722 targetData = $target.data(), 723 targetDisabled = $target.hasClass( CLASSES.navDisabled ) || $target.hasClass( CLASSES.disabled ), 724 725 // * For IE, non-focusable elements can be active elements as well 726 // (http://stackoverflow.com/a/2684561). 727 activeElement = getActiveElement() 728 activeElement = activeElement && ( activeElement.type || activeElement.href ) 729 730 // If itâs disabled or nothing inside is actively focused, re-focus the element. 731 if ( targetDisabled || activeElement && !$.contains( P.$root[0], activeElement ) ) { 732 P.$holder[0].focus() 733 } 734 735 // If something is superficially changed, update the `highlight` based on the `nav`. 736 if ( !targetDisabled && targetData.nav ) { 737 P.set( 'highlight', P.component.item.highlight, { nav: targetData.nav } ) 738 } 739 740 // If something is picked, set `select` then close with focus. 741 else if ( !targetDisabled && 'pick' in targetData ) { 742 P.set( 'select', targetData.pick ) 743 if ( SETTINGS.closeOnSelect ) { 744 P.close( true ) 745 } 746 } 747 748 // If a âclearâ button is pressed, empty the values and close with focus. 749 else if ( targetData.clear ) { 750 P.clear() 751 if ( SETTINGS.closeOnClear ) { 752 P.close( true ) 753 } 754 } 755 756 else if ( targetData.close ) { 757 P.close( true ) 758 } 759 760 }) //P.$holder 761 762 } 763 764 765 /** 766 * Prepare the hidden input element along with all bindings. 767 */ 768 function prepareElementHidden() { 769 770 var name 771 772 if ( SETTINGS.hiddenName === true ) { 773 name = ELEMENT.name 774 ELEMENT.name = '' 775 } 776 else { 777 name = [ 778 typeof SETTINGS.hiddenPrefix == 'string' ? SETTINGS.hiddenPrefix : '', 779 typeof SETTINGS.hiddenSuffix == 'string' ? SETTINGS.hiddenSuffix : '_submit' 780 ] 781 name = name[0] + ELEMENT.name + name[1] 782 } 783 784 P._hidden = $( 785 '<input ' + 786 'type=hidden ' + 787 788 // Create the name using the original inputâs with a prefix and suffix. 789 'name="' + name + '"' + 790 791 // If the element has a value, set the hidden value as well. 792 ( 793 $ELEMENT.data('value') || ELEMENT.value ? 794 ' value="' + P.get('select', SETTINGS.formatSubmit) + '"' : 795 '' 796 ) + 797 '>' 798 )[0] 799 800 $ELEMENT. 801 802 // If the value changes, update the hidden input with the correct format. 803 on('change.' + STATE.id, function() { 804 P._hidden.value = ELEMENT.value ? 805 P.get('select', SETTINGS.formatSubmit) : 806 '' 807 }) 808 } 809 810 811 // Wait for transitions to end before focusing the holder. Otherwise, while 812 // using the `container` option, the view jumps to the container. 813 function focusPickerOnceOpened() { 814 815 if (IS_DEFAULT_THEME && supportsTransitions) { 816 P.$holder.find('.' + CLASSES.frame).one('transitionend', function() { 817 P.$holder[0].focus() 818 }) 819 } 820 else { 821 P.$holder[0].focus() 822 } 823 } 824 825 826 function handleFocusToOpenEvent(event) { 827 828 // Stop the event from propagating to the doc. 829 event.stopPropagation() 830 831 // Add the âtargetâ class. 832 $ELEMENT.addClass( CLASSES.target ) 833 834 // Add the âfocusedâ class to the root. 835 P.$root.addClass( CLASSES.focused ) 836 837 // And then finally open the picker. 838 P.open() 839 } 840 841 842 // For iOS8. 843 function handleKeydownEvent( event ) { 844 845 var keycode = event.keyCode, 846 847 // Check if one of the delete keys was pressed. 848 isKeycodeDelete = /^(8|46)$/.test(keycode) 849 850 // For some reason IE clears the input value on âescapeâ. 851 if ( keycode == 27 ) { 852 P.close( true ) 853 return false 854 } 855 856 // Check if `space` or `delete` was pressed or the picker is closed with a key movement. 857 if ( keycode == 32 || isKeycodeDelete || !STATE.open && P.component.key[keycode] ) { 858
859 // Prevent it from moving the page and bubbling to doc. 860 event.preventDefault() 861 event.stopPropagation() 862 863 // If `delete` was pressed, clear the values and close the picker. 864 // Otherwise open the picker. 865 if ( isKeycodeDelete ) { P.clear().close() } 866 else { P.open() } 867 } 868 } 869 870 871 // Return a new picker instance. 872 return new PickerInstance() 873} //PickerConstructor 874 875 876 877/** 878 * The default classes and prefix to use for the HTML classes. 879 */ 880PickerConstructor.klasses = function( prefix ) { 881 prefix = prefix || 'picker' 882 return { 883 884 picker: prefix, 885 opened: prefix + '--opened', 886 focused: prefix + '--focused', 887 888 input: prefix + '__input', 889 active: prefix + '__input--active', 890 target: prefix + '__input--target', 891 892 holder: prefix + '__holder', 893 894 frame: prefix + '__frame', 895 wrap: prefix + '__wrap', 896 897 box: prefix + '__box' 898 } 899} //PickerConstructor.klasses 900 901 902 903/** 904 * Check if the default theme is being used. 905 */ 906function isUsingDefaultTheme( element ) { 907 908 var theme, 909 prop = 'position' 910 911 // For IE. 912 if ( element.currentStyle ) { 913 theme = element.currentStyle[prop] 914 } 915 916 // For normal browsers. 917 else if ( window.getComputedStyle ) { 918 theme = getComputedStyle( element )[prop] 919 } 920 921 return theme == 'fixed' 922} 923 924 925 926/** 927 * Get the width of the browserâs scrollbar. 928 * Taken from: https://github.com/VodkaBears/Remodal/blob/master/src/jquery.remodal.js 929 */ 930function getScrollbarWidth() { 931 932 if ( $html.height() <= $window.height() ) { 933 return 0 934 } 935 936 var $outer = $( '<div style="visibility:hidden;width:100px" />' ). 937 appendTo( 'body' ) 938 939 // Get the width without scrollbars. 940 var widthWithoutScroll = $outer[0].offsetWidth 941 942 // Force adding scrollbars. 943 $outer.css( 'overflow', 'scroll' ) 944 945 // Add the inner div. 946 var $inner = $( '<div style="width:100%" />' ).appendTo( $outer ) 947 948 // Get the width with scrollbars. 949 var widthWithScroll = $inner[0].offsetWidth 950 951 // Remove the divs. 952 $outer.remove() 953 954 // Return the difference between the widths. 955 return widthWithoutScroll - widthWithScroll 956} 957 958 959 960/** 961 * PickerConstructor helper methods. 962 */ 963PickerConstructor._ = { 964 965 /** 966 * Create a group of nodes. Expects: 967 * ` 968 { 969 min: {Integer}, 970 max: {Integer}, 971 i: {Integer}, 972 node: {String}, 973 item: {Function} 974 } 975 * ` 976 */ 977 group: function( groupObject ) { 978 979 var 980 // Scope for the looped object 981 loopObjectScope, 982 983 // Create the nodes list 984 nodesList = '', 985 986 // The counter starts from the `min` 987 counter = PickerConstructor._.trigger( groupObject.min, groupObject ) 988 989 990 // Loop from the `min` to `max`, incrementing by `i` 991 for ( ; counter <= PickerConstructor._.trigger( groupObject.max, groupObject, [ counter ] ); counter += groupObject.i ) { 992 993 // Trigger the `item` function within scope of the object 994 loopObjectScope = PickerConstructor._.trigger( groupObject.item, groupObject, [ counter ] ) 995 996 // Splice the subgroup and create nodes out of the sub nodes 997 nodesList += PickerConstructor._.node( 998 groupObject.node, 999 loopObjectScope[ 0 ], // the node 1000 loopObjectScope[ 1 ], // the classes 1001 loopObjectScope[ 2 ] // the attributes 1002 ) 1003 } 1004 1005 // Return the list of nodes 1006 return nodesList 1007 }, //group 1008 1009 1010 /** 1011 * Create a dom node string 1012 */ 1013 node: function( wrapper, item, klass, attribute ) { 1014 1015 // If the item is false-y, just return an empty string 1016 if ( !item ) return '' 1017 1018 // If the item is an array, do a join 1019 item = $.isArray( item ) ? item.join( '' ) : item 1020 1021 // Check for the class 1022 klass = klass ? ' class="' + klass + '"' : '' 1023 1024 // Check for any attributes 1025 attribute = attribute ? ' ' + attribute : '' 1026 1027 // Return the wrapped item 1028 return '<' + wrapper + klass + attribute + '>' + item + '</' + wrapper + '>' 1029 }, //node 1030 1031 1032 /** 1033 * Lead numbers below 10 with a zero. 1034 */ 1035 lead: function( number ) { 1036 return ( number < 10 ? '0': '' ) + number 1037 }, 1038 1039 1040 /** 1041 * Trigger a function otherwise return the value. 1042 */ 1043 trigger: function( callback, scope, args ) { 1044 return typeof callback == 'function' ? callback.apply( scope, args || [] ) : callback 1045 }, 1046 1047 1048 /** 1049 * If the second character is a digit, length is 2 otherwise 1. 1050 */ 1051 digits: function( string ) { 1052 return ( /\d/ ).test( string[ 1 ] ) ? 2 : 1 1053 }, 1054 1055 1056 /** 1057 * Tell if something is a date object. 1058 */ 1059 isDate: function( value ) { 1060 return {}.toString.call( value ).indexOf( 'Date' ) > -1 && this.isInteger( value.getDate() ) 1061 }, 1062 1063 1064 /** 1065 * Tell if something is an integer. 1066 */ 1067 isInteger: function( value ) { 1068 return {}.toString.call( value ).indexOf( 'Number' ) > -1 && value % 1 === 0 1069 }, 1070 1071 1072 /** 1073 * Create ARIA attribute strings. 1074 */ 1075 ariaAttr: ariaAttr 1076} //PickerConstructor._ 1077 1078 1079 1080/** 1081 * Extend the picker with a component and defaults. 1082 */ 1083PickerConstructor.extend = function( name, Component ) { 1084 1085 // Extend jQuery. 1086 $.fn[ name ] = function( options, action ) { 1087 1088 // Grab the component data. 1089 var componentData = this.data( name ) 1090 1091 // If the picker is requested, return the data object. 1092 if ( options == 'picker' ) { 1093 return componentData 1094 } 1095 1096 // If the component data exists and `options` is a string, carry out the action. 1097 if ( componentData && typeof options == 'string' ) { 1098 return PickerConstructor._.trigger( componentData[ options ], componentData, [ action ] ) 1099 } 1100 1101 // Otherwise go through each matched element and if the component 1102 // doesnât exist, create a new picker using `this` element 1103 // and merging the defaults and options with a deep copy. 1104 return this.each( function() { 1105 var $this = $( this ) 1106 if ( !$this.data( name ) ) { 1107 new PickerConstructor( this, name, Component, options ) 1108 } 1109 }) 1110 } 1111 1112 // Set the defaults. 1113 $.fn[ name ].defaults = Component.defaults 1114} //PickerConstructor.extend 1115 1116 1117 1118function aria(element, attribute, value) { 1119 if ( $.isPlainObject(attribute) ) { 1120 for ( var key in attribute ) { 1121 ariaSet(element, key, attribute[key]) 1122 } 1123 } 1124 else { 1125 ariaSet(element, attribute, value) 1126 } 1127} 1128function ariaSet(element, attribute, value) { 1129 element.setAttribute( 1130 (attribute == 'role' ? '' : 'aria-') + attribute, 1131 value 1132 ) 1133} 1134function ariaAttr(attribute, data) { 1135 if ( !$.isPlainObject(attribute) ) { 1136 attribute = { attribute: data } 1137 } 1138 data = '' 1139 for ( var key in attribute ) { 1140 var attr = (key == 'role' ? '' : 'aria-') + key, 1141 attrVal = attribute[key] 1142 data += attrVal == null ? '' : attr + '="' + attribute[key] + '"' 1143 } 1144 return data 1145} 1146 1147// IE8 bug throws an error for activeElements within iframes. 1148function getActiveElement() { 1149 try { 1150 return document.activeElement 1151 } catch ( err ) { } 1152} 1153 1154 1155 1156// Expose the picker constructor. 1157return PickerConstructor 1158 1159 1160})); 1161 1162 1163
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.