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