1/*! jQuery UI - v1.12.1 - 2018-12-06 2* http://jqueryui.com 3* Includes: widget.js, position.js, keycode.js, unique-id.js, widgets/autocomplete.js, widgets/menu.js 4* Copyright jQuery Foundation and other contributors; Licensed MIT */ 5 6(function( factory ) { 7 if ( typeof define === "function" && define.amd ) { 8 9 // AMD. Register as an anonymous module. 10 define([ "jquery" ], factory ); 11 } else { 12 13 // Browser globals 14 factory( jQuery ); 15 } 16}(function( $ ) { 17 18$.ui = $.ui || {}; 19 20var version = $.ui.version = "1.12.1"; 21 22 23/*! 24 * jQuery UI Widget 1.12.1 25 * http://jqueryui.com 26 * 27 * Copyright jQuery Foundation and other contributors 28 * Released under the MIT license. 29 * http://jquery.org/license 30 */ 31 32//>>label: Widget 33//>>group: Core 34//>>description: Provides a factory for creating stateful widgets with a common API. 35//>>docs: http://api.jqueryui.com/jQuery.widget/ 36//>>demos: http://jqueryui.com/widget/ 37 38 39 40var widgetUuid = 0; 41var widgetSlice = Array.prototype.slice; 42 43$.cleanData = ( function( orig ) { 44 return function( elems ) { 45 var events, elem, i; 46 for ( i = 0; ( elem = elems[ i ] ) != null; i++ ) { 47 try { 48 49 // Only trigger remove when necessary to save time 50 events = $._data( elem, "events" ); 51 if ( events && events.remove ) { 52 $( elem ).triggerHandler( "remove" ); 53 } 54 55 // Http://bugs.jquery.com/ticket/8235 56 } catch ( e ) {} 57 } 58 orig( elems ); 59 }; 60} )( $.cleanData ); 61 62$.widget = function( name, base, prototype ) { 63 var existingConstructor, constructor, basePrototype; 64 65 // ProxiedPrototype allows the provided prototype to remain unmodified 66 // so that it can be used as a mixin for multiple widgets (#8876) 67 var proxiedPrototype = {}; 68 69 var namespace = name.split( "." )[ 0 ]; 70 name = name.split( "." )[ 1 ]; 71 var fullName = namespace + "-" + name; 72 73 if ( !prototype ) { 74 prototype = base; 75 base = $.Widget; 76 } 77 78 if ( $.isArray( prototype ) ) { 79 prototype = $.extend.apply( null, [ {} ].concat( prototype ) ); 80 } 81 82 // Create selector for plugin 83 $.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) { 84 return !!$.data( elem, fullName ); 85 }; 86 87 $[ namespace ] = $[ namespace ] || {}; 88 existingConstructor = $[ namespace ][ name ]; 89 constructor = $[ namespace ][ name ] = function( options, element ) { 90 91 // Allow instantiation without "new" keyword 92 if ( !this._createWidget ) { 93 return new constructor( options, element ); 94 } 95 96 // Allow instantiation without initializing for simple inheritance 97 // must use "new" keyword (the code above always passes args) 98 if ( arguments.length ) { 99 this._createWidget( options, element ); 100 } 101 }; 102 103 // Extend with the existing constructor to carry over any static properties 104 $.extend( constructor, existingConstructor, { 105 version: prototype.version, 106 107 // Copy the object used to create the prototype in case we need to 108 // redefine the widget later 109 _proto: $.extend( {}, prototype ), 110 111 // Track widgets that inherit from this widget in case this widget is 112 // redefined after a widget inherits from it 113 _childConstructors: [] 114 } ); 115 116 basePrototype = new base(); 117 118 // We need to make the options hash a property directly on the new instance 119 // otherwise we'll modify the options hash on the prototype that we're 120 // inheriting from 121 basePrototype.options = $.widget.extend( {}, basePrototype.options ); 122 $.each( prototype, function( prop, value ) { 123 if ( !$.isFunction( value ) ) { 124 proxiedPrototype[ prop ] = value; 125 return; 126 } 127 proxiedPrototype[ prop ] = ( function() { 128 function _super() { 129 return base.prototype[ prop ].apply( this, arguments ); 130 } 131 132 function _superApply( args ) { 133 return base.prototype[ prop ].apply( this, args ); 134 } 135 136 return function() { 137 var __super = this._super; 138 var __superApply = this._superApply; 139 var returnValue; 140 141 this._super = _super; 142 this._superApply = _superApply; 143 144 returnValue = value.apply( this, arguments ); 145 146 this._super = __super; 147 this._superApply = __superApply; 148 149 return returnValue; 150 }; 151 } )(); 152 } ); 153 constructor.prototype = $.widget.extend( basePrototype, { 154 155 // TODO: remove support for widgetEventPrefix 156 // always use the name + a colon as the prefix, e.g., draggable:start 157 // don't prefix for widgets that aren't DOM-based 158 widgetEventPrefix: existingConstructor ? ( basePrototype.widgetEventPrefix || name ) : name 159 }, proxiedPrototype, { 160 constructor: constructor, 161 namespace: namespace, 162 widgetName: name, 163 widgetFullName: fullName 164 } ); 165 166 // If this widget is being redefined then we need to find all widgets that 167 // are inheriting from it and redefine all of them so that they inherit from 168 // the new version of this widget. We're essentially trying to replace one 169 // level in the prototype chain. 170 if ( existingConstructor ) { 171 $.each( existingConstructor._childConstructors, function( i, child ) { 172 var childPrototype = child.prototype; 173 174 // Redefine the child widget using the same prototype that was 175 // originally used, but inherit from the new version of the base 176 $.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor, 177 child._proto ); 178 } ); 179 180 // Remove the list of existing child constructors from the old constructor 181 // so the old child constructors can be garbage collected 182 delete existingConstructor._childConstructors; 183 } else { 184 base._childConstructors.push( constructor ); 185 } 186 187 $.widget.bridge( name, constructor ); 188 189 return constructor; 190}; 191 192$.widget.extend = function( target ) { 193 var input = widgetSlice.call( arguments, 1 ); 194 var inputIndex = 0; 195 var inputLength = input.length; 196 var key; 197 var value; 198 199 for ( ; inputIndex < inputLength; inputIndex++ ) { 200 for ( key in input[ inputIndex ] ) { 201 value = input[ inputIndex ][ key ]; 202 if ( input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) { 203 204 // Clone objects 205 if ( $.isPlainObject( value ) ) { 206 target[ key ] = $.isPlainObject( target[ key ] ) ? 207 $.widget.extend( {}, target[ key ], value ) : 208 209 // Don't extend strings, arrays, etc. with objects 210 $.widget.extend( {}, value ); 211 212 // Copy everything else by reference 213 } else { 214 target[ key ] = value; 215 } 216 } 217 } 218 } 219 return target; 220}; 221 222$.widget.bridge = function( name, object ) { 223 var fullName = object.prototype.widgetFullName || name; 224 $.fn[ name ] = function( options ) { 225 var isMethodCall = typeof options === "string"; 226 var args = widgetSlice.call( arguments, 1 ); 227 var returnValue = this; 228 229 if ( isMethodCall ) { 230 231 // If this is an empty collection, we need to have the instance method 232 // return undefined instead of the jQuery instance 233 if ( !this.length && options === "instance" ) { 234 returnValue = undefined; 235 } else { 236 this.each( function() { 237 var methodValue; 238 var instance = $.data( this, fullName ); 239 240 if ( options === "instance" ) { 241 returnValue = instance; 242 return false; 243 } 244 245 if ( !instance ) { 246 return $.error( "cannot call methods on " + name + 247 " prior to initialization; " + 248 "attempted to call method '" + options + "'" ); 249 } 250 251 if ( !$.isFunction( instance[ options ] ) || options.charAt( 0 ) === "_" ) { 252 return $.error( "no such method '" + options + "' for " + name + 253 " widget instance" ); 254 } 255 256 methodValue = instance[ options ].apply( instance, args ); 257 258 if ( methodValue !== instance && methodValue !== undefined ) { 259 returnValue = methodValue && methodValue.jquery ? 260 returnValue.pushStack( methodValue.get() ) : 261 methodValue; 262 return false; 263 } 264 } ); 265 } 266 } else { 267 268 // Allow multiple hashes to be passed on init 269 if ( args.length ) { 270 options = $.widget.extend.apply( null, [ options ].concat( args ) ); 271 } 272 273 this.each( function() { 274 var instance = $.data( this, fullName ); 275 if ( instance ) { 276 instance.option( options || {} ); 277 if ( instance._init ) { 278 instance._init(); 279 } 280 } else { 281 $.data( this, fullName, new object( options, this ) ); 282 } 283 } ); 284 } 285 286 return returnValue; 287 }; 288}; 289 290$.Widget = function( /* options, element */ ) {}; 291$.Widget._childConstructors = []; 292 293$.Widget.prototype = { 294 widgetName: "widget", 295 widgetEventPrefix: "", 296 defaultElement: "<div>", 297 298 options: { 299 classes: {}, 300 disabled: false, 301 302 // Callbacks 303 create: null 304 }, 305 306 _createWidget: function( options, element ) { 307 element = $( element || this.defaultElement || this )[ 0 ]; 308 this.element = $( element ); 309 this.uuid = widgetUuid++; 310 this.eventNamespace = "." + this.widgetName + this.uuid; 311 312 this.bindings = $(); 313 this.hoverable = $(); 314 this.focusable = $(); 315 this.classesElementLookup = {}; 316 317 if ( element !== this ) { 318 $.data( element, this.widgetFullName, this ); 319 this._on( true, this.element, { 320 remove: function( event ) { 321 if ( event.target === element ) { 322 this.destroy(); 323 } 324 } 325 } ); 326 this.document = $( element.style ? 327 328 // Element within the document 329 element.ownerDocument : 330 331 // Element is window or document 332 element.document || element ); 333 this.window = $( this.document[ 0 ].defaultView || this.document[ 0 ].parentWindow ); 334 } 335 336 this.options = $.widget.extend( {}, 337 this.options, 338 this._getCreateOptions(), 339 options ); 340 341 this._create(); 342 343 if ( this.options.disabled ) { 344 this._setOptionDisabled( this.options.disabled ); 345 } 346 347 this._trigger( "create", null, this._getCreateEventData() ); 348 this._init(); 349 }, 350 351 _getCreateOptions: function() { 352 return {}; 353 }, 354 355 _getCreateEventData: $.noop, 356 357 _create: $.noop, 358 359 _init: $.noop, 360 361 destroy: function() { 362 var that = this; 363 364 this._destroy(); 365 $.each( this.classesElementLookup, function( key, value ) { 366 that._removeClass( value, key ); 367 } ); 368 369 // We can probably remove the unbind calls in 2.0 370 // all event bindings should go through this._on() 371 this.element 372 .off( this.eventNamespace ) 373 .removeData( this.widgetFullName ); 374 this.widget() 375 .off( this.eventNamespace ) 376 .removeAttr( "aria-disabled" ); 377 378 // Clean up events and states 379 this.bindings.off( this.eventNamespace ); 380 }, 381 382 _destroy: $.noop, 383 384 widget: function() { 385 return this.element; 386 }, 387 388 option: function( key, value ) { 389 var options = key; 390 var parts; 391 var curOption; 392 var i; 393 394 if ( arguments.length === 0 ) { 395 396 // Don't return a reference to the internal hash 397 return $.widget.extend( {}, this.options ); 398 } 399 400 if ( typeof key === "string" ) { 401 402 // Handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } } 403 options = {}; 404 parts = key.split( "." ); 405 key = parts.shift(); 406 if ( parts.length ) { 407 curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] ); 408 for ( i = 0; i < parts.length - 1; i++ ) { 409 curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {}; 410 curOption = curOption[ parts[ i ] ]; 411 } 412 key = parts.pop(); 413 if ( arguments.length === 1 ) { 414 return curOption[ key ] === undefined ? null : curOption[ key ]; 415 } 416 curOption[ key ] = value; 417 } else { 418 if ( arguments.length === 1 ) { 419 return this.options[ key ] === undefined ? null : this.options[ key ]; 420 } 421 options[ key ] = value; 422 } 423 } 424 425 this._setOptions( options ); 426 427 return this; 428 }, 429 430 _setOptions: function( options ) { 431 var key; 432 433 for ( key in options ) { 434 this._setOption( key, options[ key ] ); 435 } 436 437 return this; 438 }, 439 440 _setOption: function( key, value ) { 441 if ( key === "classes" ) { 442 this._setOptionClasses( value ); 443 } 444 445 this.options[ key ] = value; 446 447 if ( key === "disabled" ) { 448 this._setOptionDisabled( value ); 449 } 450 451 return this; 452 }, 453 454 _setOptionClasses: function( value ) { 455 var classKey, elements, currentElements; 456 457 for ( classKey in value ) { 458 currentElements = this.classesElementLookup[ classKey ]; 459 if ( value[ classKey ] === this.options.classes[ classKey ] || 460 !currentElements || 461 !currentElements.length ) { 462 continue; 463 } 464 465 // We are doing this to create a new jQuery object because the _removeClass() call 466 // on the next line is going to destroy the reference to the current elements being 467 // tracked. We need to save a copy of this collection so that we can add the new classes 468 // below. 469 elements = $( currentElements.get() ); 470 this._removeClass( currentElements, classKey ); 471 472 // We don't use _addClass() here, because that uses this.options.classes 473 // for generating the string of classes. We want to use the value passed in from 474 // _setOption(), this is the new value of the classes option which was passed to 475 // _setOption(). We pass this value directly to _classes(). 476 elements.addClass( this._classes( { 477 element: elements, 478 keys: classKey, 479 classes: value, 480 add: true 481 } ) ); 482 } 483 }, 484 485 _setOptionDisabled: function( value ) { 486 this._toggleClass( this.widget(), this.widgetFullName + "-disabled", null, !!value ); 487 488 // If the widget is becoming disabled, then nothing is interactive 489 if ( value ) { 490 this._removeClass( this.hoverable, null, "ui-state-hover" ); 491 this._removeClass( this.focusable, null, "ui-state-focus" ); 492 } 493 }, 494 495 enable: function() { 496 return this._setOptions( { disabled: false } ); 497 }, 498 499 disable: function() { 500 return this._setOptions( { disabled: true } ); 501 }, 502 503 _classes: function( options ) { 504 var full = []; 505 var that = this; 506 507 options = $.extend( { 508 element: this.element, 509 classes: this.options.classes || {} 510 }, options ); 511 512 function processClassString( classes, checkOption ) { 513 var current, i; 514 for ( i = 0; i < classes.length; i++ ) { 515 current = that.classesElementLookup[ classes[ i ] ] || $(); 516 if ( options.add ) { 517 current = $( $.unique( current.get().concat( options.element.get() ) ) ); 518 } else { 519 current = $( current.not( options.element ).get() ); 520 } 521 that.classesElementLookup[ classes[ i ] ] = current; 522 full.push( classes[ i ] ); 523 if ( checkOption && options.classes[ classes[ i ] ] ) { 524 full.push( options.classes[ classes[ i ] ] ); 525 } 526 } 527 } 528 529 this._on( options.element, { 530 "remove": "_untrackClassesElement" 531 } ); 532 533 if ( options.keys ) { 534 processClassString( options.keys.match( /\S+/g ) || [], true ); 535 } 536 if ( options.extra ) { 537 processClassString( options.extra.match( /\S+/g ) || [] ); 538 } 539 540 return full.join( " " ); 541 }, 542 543 _untrackClassesElement: function( event ) { 544 var that = this; 545 $.each( that.classesElementLookup, function( key, value ) { 546 if ( $.inArray( event.target, value ) !== -1 ) { 547 that.classesElementLookup[ key ] = $( value.not( event.target ).get() ); 548 } 549 } ); 550 }, 551 552 _removeClass: function( element, keys, extra ) { 553 return this._toggleClass( element, keys, extra, false ); 554 }, 555 556 _addClass: function( element, keys, extra ) { 557 return this._toggleClass( element, keys, extra, true ); 558 }, 559 560 _toggleClass: function( element, keys, extra, add ) { 561 add = ( typeof add === "boolean" ) ? add : extra; 562 var shift = ( typeof element === "string" || element === null ), 563 options = { 564 extra: shift ? keys : extra, 565 keys: shift ? element : keys, 566 element: shift ? this.element : element, 567 add: add 568 }; 569 options.element.toggleClass( this._classes( options ), add ); 570 return this; 571 }, 572 573 _on: function( suppressDisabledCheck, element, handlers ) { 574 var delegateElement; 575 var instance = this; 576 577 // No suppressDisabledCheck flag, shuffle arguments 578 if ( typeof suppressDisabledCheck !== "boolean" ) { 579 handlers = element; 580 element = suppressDisabledCheck; 581 suppressDisabledCheck = false; 582 } 583 584 // No element argument, shuffle and use this.element 585 if ( !handlers ) { 586 handlers = element; 587 element = this.element; 588 delegateElement = this.widget(); 589 } else { 590 element = delegateElement = $( element ); 591 this.bindings = this.bindings.add( element ); 592 } 593 594 $.each( handlers, function( event, handler ) { 595 function handlerProxy() { 596 597 // Allow widgets to customize the disabled handling 598 // - disabled as an array instead of boolean 599 // - disabled class as method for disabling individual parts 600 if ( !suppressDisabledCheck && 601 ( instance.options.disabled === true || 602 $( this ).hasClass( "ui-state-disabled" ) ) ) { 603 return; 604 } 605 return ( typeof handler === "string" ? instance[ handler ] : handler ) 606 .apply( instance, arguments ); 607 } 608 609 // Copy the guid so direct unbinding works 610 if ( typeof handler !== "string" ) { 611 handlerProxy.guid = handler.guid = 612 handler.guid || handlerProxy.guid || $.guid++; 613 } 614 615 var match = event.match( /^([\w:-]*)\s*(.*)$/ ); 616 var eventName = match[ 1 ] + instance.eventNamespace; 617 var selector = match[ 2 ]; 618 619 if ( selector ) { 620 delegateElement.on( eventName, selector, handlerProxy ); 621 } else { 622 element.on( eventName, handlerProxy ); 623 } 624 } ); 625 }, 626 627 _off: function( element, eventName ) { 628 eventName = ( eventName || "" ).split( " " ).join( this.eventNamespace + " " ) + 629 this.eventNamespace; 630 element.off( eventName ).off( eventName ); 631 632 // Clear the stack to avoid memory leaks (#10056) 633 this.bindings = $( this.bindings.not( element ).get() ); 634 this.focusable = $( this.focusable.not( element ).get() ); 635 this.hoverable = $( this.hoverable.not( element ).get() ); 636 }, 637 638 _delay: function( handler, delay ) { 639 function handlerProxy() { 640 return ( typeof handler === "string" ? instance[ handler ] : handler ) 641 .apply( instance, arguments ); 642 } 643 var instance = this; 644 return setTimeout( handlerProxy, delay || 0 ); 645 }, 646 647 _hoverable: function( element ) { 648 this.hoverable = this.hoverable.add( element ); 649 this._on( element, { 650 mouseenter: function( event ) { 651 this._addClass( $( event.currentTarget ), null, "ui-state-hover" ); 652 }, 653 mouseleave: function( event ) { 654 this._removeClass( $( event.currentTarget ), null, "ui-state-hover" ); 655 } 656 } ); 657 }, 658 659 _focusable: function( element ) { 660 this.focusable = this.focusable.add( element ); 661 this._on( element, { 662 focusin: function( event ) { 663 this._addClass( $( event.currentTarget ), null, "ui-state-focus" ); 664 }, 665 focusout: function( event ) { 666 this._removeClass( $( event.currentTarget ), null, "ui-state-focus" ); 667 } 668 } ); 669 }, 670 671 _trigger: function( type, event, data ) { 672 var prop, orig; 673 var callback = this.options[ type ]; 674 675 data = data || {}; 676 event = $.Event( event ); 677 event.type = ( type === this.widgetEventPrefix ? 678 type : 679 this.widgetEventPrefix + type ).toLowerCase(); 680 681 // The original event may come from any element 682 // so we need to reset the target on the new event 683 event.target = this.element[ 0 ]; 684 685 // Copy original event properties over to the new event 686 orig = event.originalEvent; 687 if ( orig ) { 688 for ( prop in orig ) { 689 if ( !( prop in event ) ) { 690 event[ prop ] = orig[ prop ]; 691 } 692 } 693 } 694 695 this.element.trigger( event, data ); 696 return !( $.isFunction( callback ) && 697 callback.apply( this.element[ 0 ], [ event ].concat( data ) ) === false || 698 event.isDefaultPrevented() ); 699 } 700}; 701 702$.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) { 703 $.Widget.prototype[ "_" + method ] = function( element, options, callback ) { 704 if ( typeof options === "string" ) { 705 options = { effect: options }; 706 } 707 708 var hasOptions; 709 var effectName = !options ? 710 method : 711 options === true || typeof options === "number" ? 712 defaultEffect : 713 options.effect || defaultEffect; 714 715 options = options || {}; 716 if ( typeof options === "number" ) { 717 options = { duration: options }; 718 } 719 720 hasOptions = !$.isEmptyObject( options ); 721 options.complete = callback; 722 723 if ( options.delay ) { 724 element.delay( options.delay ); 725 } 726 727 if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) { 728 element[ method ]( options ); 729 } else if ( effectName !== method && element[ effectName ] ) { 730 element[ effectName ]( options.duration, options.easing, callback ); 731 } else { 732 element.queue( function( next ) { 733 $( this )[ method ](); 734 if ( callback ) { 735 callback.call( element[ 0 ] ); 736 } 737 next(); 738 } ); 739 } 740 }; 741} ); 742 743var widget = $.widget; 744 745 746/*! 747 * jQuery UI Position 1.12.1 748 * http://jqueryui.com 749 * 750 * Copyright jQuery Foundation and other contributors 751 * Released under the MIT license. 752 * http://jquery.org/license 753 * 754 * http://api.jqueryui.com/position/ 755 */ 756 757//>>label: Position 758//>>group: Core 759//>>description: Positions elements relative to other elements. 760//>>docs: http://api.jqueryui.com/position/ 761//>>demos: http://jqueryui.com/position/ 762 763 764( function() { 765var cachedScrollbarWidth, 766 max = Math.max, 767 abs = Math.abs, 768 rhorizontal = /left|center|right/, 769 rvertical = /top|center|bottom/, 770 roffset = /[\+\-]\d+(\.[\d]+)?%?/, 771 rposition = /^\w+/, 772 rpercent = /%$/, 773 _position = $.fn.position; 774 775function getOffsets( offsets, width, height ) { 776 return [ 777 parseFloat( offsets[ 0 ] ) * ( rpercent.test( offsets[ 0 ] ) ? width / 100 : 1 ), 778 parseFloat( offsets[ 1 ] ) * ( rpercent.test( offsets[ 1 ] ) ? height / 100 : 1 ) 779 ]; 780} 781 782function parseCss( element, property ) { 783 return parseInt( $.css( element, property ), 10 ) || 0; 784} 785 786function getDimensions( elem ) { 787 var raw = elem[ 0 ]; 788 if ( raw.nodeType === 9 ) { 789 return { 790 width: elem.width(), 791 height: elem.height(), 792 offset: { top: 0, left: 0 } 793 }; 794 } 795 if ( $.isWindow( raw ) ) { 796 return { 797 width: elem.width(), 798 height: elem.height(), 799 offset: { top: elem.scrollTop(), left: elem.scrollLeft() } 800 }; 801 } 802 if ( raw.preventDefault ) { 803 return { 804 width: 0, 805 height: 0, 806 offset: { top: raw.pageY, left: raw.pageX } 807 }; 808 } 809 return { 810 width: elem.outerWidth(), 811 height: elem.outerHeight(), 812 offset: elem.offset() 813 }; 814} 815 816$.position = { 817 scrollbarWidth: function() { 818 if ( cachedScrollbarWidth !== undefined ) { 819 return cachedScrollbarWidth; 820 } 821 var w1, w2, 822 div = $( "<div " + 823 "style='display:block;position:absolute;width:50px;height:50px;overflow:hidden;'>" + 824 "<div style='height:100px;width:auto;'></div></div>" ), 825 innerDiv = div.children()[ 0 ]; 826 827 $( "body" ).append( div ); 828 w1 = innerDiv.offsetWidth; 829 div.css( "overflow", "scroll" ); 830 831 w2 = innerDiv.offsetWidth; 832 833 if ( w1 === w2 ) { 834 w2 = div[ 0 ].clientWidth; 835 } 836 837 div.remove(); 838 839 return ( cachedScrollbarWidth = w1 - w2 ); 840 }, 841 getScrollInfo: function( within ) { 842 var overflowX = within.isWindow || within.isDocument ? "" : 843 within.element.css( "overflow-x" ), 844 overflowY = within.isWindow || within.isDocument ? "" : 845 within.element.css( "overflow-y" ), 846 hasOverflowX = overflowX === "scroll" || 847 ( overflowX === "auto" && within.width < within.element[ 0 ].scrollWidth ), 848 hasOverflowY = overflowY === "scroll" || 849 ( overflowY === "auto" && within.height < within.element[ 0 ].scrollHeight ); 850 return { 851 width: hasOverflowY ? $.position.scrollbarWidth() : 0, 852 height: hasOverflowX ? $.position.scrollbarWidth() : 0 853 }; 854 }, 855 getWithinInfo: function( element ) { 856 var withinElement = $( element || window ), 857 isWindow = $.isWindow( withinElement[ 0 ] ), 858 isDocument = !!withinElement[ 0 ] && withinElement[ 0 ].nodeType === 9, 859 hasOffset = !isWindow && !isDocument; 860 return { 861 element: withinElement, 862 isWindow: isWindow, 863 isDocument: isDocument, 864 offset: hasOffset ? $( element ).offset() : { left: 0, top: 0 }, 865 scrollLeft: withinElement.scrollLeft(), 866 scrollTop: withinElement.scrollTop(), 867 width: withinElement.outerWidth(), 868 height: withinElement.outerHeight() 869 }; 870 } 871}; 872 873$.fn.position = function( options ) { 874 if ( !options || !options.of ) { 875 return _position.apply( this, arguments ); 876 } 877 878 // Make a copy, we don't want to modify arguments 879 options = $.extend( {}, options ); 880 881 var atOffset, targetWidth, targetHeight, targetOffset, basePosition, dimensions, 882 target = $( options.of ), 883 within = $.position.getWithinInfo( options.within ), 884 scrollInfo = $.position.getScrollInfo( within ), 885 collision = ( options.collision || "flip" ).split( " " ), 886 offsets = {}; 887 888 dimensions = getDimensions( target ); 889 if ( target[ 0 ].preventDefault ) { 890 891 // Force left top to allow flipping 892 options.at = "left top"; 893 } 894 targetWidth = dimensions.width; 895 targetHeight = dimensions.height; 896 targetOffset = dimensions.offset; 897 898 // Clone to reuse original targetOffset later 899 basePosition = $.extend( {}, targetOffset ); 900 901 // Force my and at to have valid horizontal and vertical positions 902 // if a value is missing or invalid, it will be converted to center 903 $.each( [ "my", "at" ], function() { 904 var pos = ( options[ this ] || "" ).split( " " ), 905 horizontalOffset, 906 verticalOffset; 907 908 if ( pos.length === 1 ) { 909 pos = rhorizontal.test( pos[ 0 ] ) ? 910 pos.concat( [ "center" ] ) : 911 rvertical.test( pos[ 0 ] ) ? 912 [ "center" ].concat( pos ) : 913 [ "center", "center" ]; 914 } 915 pos[ 0 ] = rhorizontal.test( pos[ 0 ] ) ? pos[ 0 ] : "center"; 916 pos[ 1 ] = rvertical.test( pos[ 1 ] ) ? pos[ 1 ] : "center"; 917 918 // Calculate offsets 919 horizontalOffset = roffset.exec( pos[ 0 ] ); 920 verticalOffset = roffset.exec( pos[ 1 ] ); 921 offsets[ this ] = [ 922 horizontalOffset ? horizontalOffset[ 0 ] : 0, 923 verticalOffset ? verticalOffset[ 0 ] : 0 924 ]; 925 926 // Reduce to just the positions without the offsets 927 options[ this ] = [ 928 rposition.exec( pos[ 0 ] )[ 0 ], 929 rposition.exec( pos[ 1 ] )[ 0 ] 930 ]; 931 } ); 932 933 // Normalize collision option 934 if ( collision.length === 1 ) { 935 collision[ 1 ] = collision[ 0 ]; 936 } 937 938 if ( options.at[ 0 ] === "right" ) { 939 basePosition.left += targetWidth; 940 } else if ( options.at[ 0 ] === "center" ) { 941 basePosition.left += targetWidth / 2; 942 } 943 944 if ( options.at[ 1 ] === "bottom" ) { 945 basePosition.top += targetHeight; 946 } else if ( options.at[ 1 ] === "center" ) { 947 basePosition.top += targetHeight / 2; 948 } 949 950 atOffset = getOffsets( offsets.at, targetWidth, targetHeight ); 951 basePosition.left += atOffset[ 0 ]; 952 basePosition.top += atOffset[ 1 ]; 953 954 return this.each( function() { 955 var collisionPosition, using, 956 elem = $( this ), 957 elemWidth = elem.outerWidth(), 958 elemHeight = elem.outerHeight(), 959 marginLeft = parseCss( this, "marginLeft" ), 960 marginTop = parseCss( this, "marginTop" ), 961 collisionWidth = elemWidth + marginLeft + parseCss( this, "marginRight" ) + 962 scrollInfo.width, 963 collisionHeight = elemHeight + marginTop + parseCss( this, "marginBottom" ) + 964 scrollInfo.height, 965 position = $.extend( {}, basePosition ), 966 myOffset = getOffsets( offsets.my, elem.outerWidth(), elem.outerHeight() ); 967 968 if ( options.my[ 0 ] === "right" ) { 969 position.left -= elemWidth; 970 } else if ( options.my[ 0 ] === "center" ) { 971 position.left -= elemWidth / 2; 972 } 973 974 if ( options.my[ 1 ] === "bottom" ) { 975 position.top -= elemHeight; 976 } else if ( options.my[ 1 ] === "center" ) { 977 position.top -= elemHeight / 2; 978 } 979 980 position.left += myOffset[ 0 ]; 981 position.top += myOffset[ 1 ]; 982 983 collisionPosition = { 984 marginLeft: marginLeft, 985 marginTop: marginTop 986 }; 987 988 $.each( [ "left", "top" ], function( i, dir ) { 989 if ( $.ui.position[ collision[ i ] ] ) { 990 $.ui.position[ collision[ i ] ][ dir ]( position, { 991 targetWidth: targetWidth, 992 targetHeight: targetHeight, 993 elemWidth: elemWidth, 994 elemHeight: elemHeight, 995 collisionPosition: collisionPosition, 996 collisionWidth: collisionWidth, 997 collisionHeight: collisionHeight, 998 offset: [ atOffset[ 0 ] + myOffset[ 0 ], atOffset [ 1 ] + myOffset[ 1 ] ], 999 my: options.my, 1000 at: options.at, 1001 within: within, 1002 elem: elem 1003 } ); 1004 } 1005 } ); 1006 1007 if ( options.using ) { 1008 1009 // Adds feedback as second argument to using callback, if present 1010 using = function( props ) { 1011 var left = targetOffset.left - position.left, 1012 right = left + targetWidth - elemWidth, 1013 top = targetOffset.top - position.top, 1014 bottom = top + targetHeight - elemHeight, 1015 feedback = { 1016 target: { 1017 element: target, 1018 left: targetOffset.left, 1019 top: targetOffset.top, 1020 width: targetWidth, 1021 height: targetHeight 1022 }, 1023 element: { 1024 element: elem, 1025 left: position.left, 1026 top: position.top, 1027 width: elemWidth, 1028 height: elemHeight 1029 }, 1030 horizontal: right < 0 ? "left" : left > 0 ? "right" : "center", 1031 vertical: bottom < 0 ? "top" : top > 0 ? "bottom" : "middle" 1032 }; 1033 if ( targetWidth < elemWidth && abs( left + right ) < targetWidth ) { 1034 feedback.horizontal = "center"; 1035 } 1036 if ( targetHeight < elemHeight && abs( top + bottom ) < targetHeight ) { 1037 feedback.vertical = "middle"; 1038 } 1039 if ( max( abs( left ), abs( right ) ) > max( abs( top ), abs( bottom ) ) ) { 1040 feedback.important = "horizontal"; 1041 } else { 1042 feedback.important = "vertical"; 1043 } 1044 options.using.call( this, props, feedback ); 1045 }; 1046 } 1047 1048 elem.offset( $.extend( position, { using: using } ) ); 1049 } ); 1050}; 1051 1052$.ui.position = { 1053 fit: { 1054 left: function( position, data ) { 1055 var within = data.within, 1056 withinOffset = within.isWindow ? within.scrollLeft : within.offset.left, 1057 outerWidth = within.width, 1058 collisionPosLeft = position.left - data.collisionPosition.marginLeft, 1059 overLeft = withinOffset - collisionPosLeft, 1060 overRight = collisionPosLeft + data.collisionWidth - outerWidth - withinOffset, 1061 newOverRight; 1062 1063 // Element is wider than within 1064 if ( data.collisionWidth > outerWidth ) { 1065 1066 // Element is initially over the left side of within 1067 if ( overLeft > 0 && overRight <= 0 ) { 1068 newOverRight = position.left + overLeft + data.collisionWidth - outerWidth - 1069 withinOffset; 1070 position.left += overLeft - newOverRight; 1071 1072 // Element is initially over right side of within 1073 } else if ( overRight > 0 && overLeft <= 0 ) { 1074 position.left = withinOffset; 1075 1076 // Element is initially over both left and right sides of within 1077 } else { 1078 if ( overLeft > overRight ) { 1079 position.left = withinOffset + outerWidth - data.collisionWidth; 1080 } else { 1081 position.left = withinOffset; 1082 } 1083 } 1084 1085 // Too far left -> align with left edge 1086 } else if ( overLeft > 0 ) { 1087 position.left += overLeft; 1088 1089 // Too far right -> align with right edge 1090 } else if ( overRight > 0 ) { 1091 position.left -= overRight; 1092 1093 // Adjust based on position and margin 1094 } else { 1095 position.left = max( position.left - collisionPosLeft, position.left ); 1096 } 1097 }, 1098 top: function( position, data ) { 1099 var within = data.within, 1100 withinOffset = within.isWindow ? within.scrollTop : within.offset.top, 1101 outerHeight = data.within.height, 1102 collisionPosTop = position.top - data.collisionPosition.marginTop, 1103 overTop = withinOffset - collisionPosTop, 1104 overBottom = collisionPosTop + data.collisionHeight - outerHeight - withinOffset, 1105 newOverBottom; 1106 1107 // Element is taller than within 1108 if ( data.collisionHeight > outerHeight ) { 1109 1110 // Element is initially over the top of within 1111 if ( overTop > 0 && overBottom <= 0 ) { 1112 newOverBottom = position.top + overTop + data.collisionHeight - outerHeight - 1113 withinOffset; 1114 position.top += overTop - newOverBottom; 1115 1116 // Element is initially over bottom of within 1117 } else if ( overBottom > 0 && overTop <= 0 ) { 1118 position.top = withinOffset; 1119 1120 // Element is initially over both top and bottom of within 1121 } else { 1122 if ( overTop > overBottom ) { 1123 position.top = withinOffset + outerHeight - data.collisionHeight; 1124 } else { 1125 position.top = withinOffset; 1126 } 1127 } 1128 1129 // Too far up -> align with top 1130 } else if ( overTop > 0 ) { 1131 position.top += overTop; 1132 1133 // Too far down -> align with bottom edge 1134 } else if ( overBottom > 0 ) { 1135 position.top -= overBottom; 1136 1137 // Adjust based on position and margin 1138 } else { 1139 position.top = max( position.top - collisionPosTop, position.top ); 1140 } 1141 } 1142 }, 1143 flip: { 1144 left: function( position, data ) { 1145 var within = data.within, 1146 withinOffset = within.offset.left + within.scrollLeft, 1147 outerWidth = within.width, 1148 offsetLeft = within.isWindow ? within.scrollLeft : within.offset.left, 1149 collisionPosLeft = position.left - data.collisionPosition.marginLeft, 1150 overLeft = collisionPosLeft - offsetLeft, 1151 overRight = collisionPosLeft + data.collisionWidth - outerWidth - offsetLeft, 1152 myOffset = data.my[ 0 ] === "left" ? 1153 -data.elemWidth : 1154 data.my[ 0 ] === "right" ? 1155 data.elemWidth : 1156 0, 1157 atOffset = data.at[ 0 ] === "left" ? 1158 data.targetWidth : 1159 data.at[ 0 ] === "right" ? 1160 -data.targetWidth : 1161 0, 1162 offset = -2 * data.offset[ 0 ], 1163 newOverRight, 1164 newOverLeft; 1165 1166 if ( overLeft < 0 ) { 1167 newOverRight = position.left + myOffset + atOffset + offset + data.collisionWidth - 1168 outerWidth - withinOffset; 1169 if ( newOverRight < 0 || newOverRight < abs( overLeft ) ) { 1170 position.left += myOffset + atOffset + offset; 1171 } 1172 } else if ( overRight > 0 ) { 1173 newOverLeft = position.left - data.collisionPosition.marginLeft + myOffset + 1174 atOffset + offset - offsetLeft; 1175 if ( newOverLeft > 0 || abs( newOverLeft ) < overRight ) { 1176 position.left += myOffset + atOffset + offset; 1177 } 1178 } 1179 }, 1180 top: function( position, data ) { 1181 var within = data.within, 1182 withinOffset = within.offset.top + within.scrollTop, 1183 outerHeight = within.height, 1184 offsetTop = within.isWindow ? within.scrollTop : within.offset.top, 1185 collisionPosTop = position.top - data.collisionPosition.marginTop, 1186 overTop = collisionPosTop - offsetTop, 1187 overBottom = collisionPosTop + data.collisionHeight - outerHeight - offsetTop, 1188 top = data.my[ 1 ] === "top", 1189 myOffset = top ? 1190 -data.elemHeight : 1191 data.my[ 1 ] === "bottom" ? 1192 data.elemHeight : 1193 0, 1194 atOffset = data.at[ 1 ] === "top" ? 1195 data.targetHeight : 1196 data.at[ 1 ] === "bottom" ? 1197 -data.targetHeight : 1198 0, 1199 offset = -2 * data.offset[ 1 ], 1200 newOverTop, 1201 newOverBottom; 1202 if ( overTop < 0 ) { 1203 newOverBottom = position.top + myOffset + atOffset + offset + data.collisionHeight - 1204 outerHeight - withinOffset; 1205 if ( newOverBottom < 0 || newOverBottom < abs( overTop ) ) { 1206 position.top += myOffset + atOffset + offset; 1207 } 1208 } else if ( overBottom > 0 ) { 1209 newOverTop = position.top - data.collisionPosition.marginTop + myOffset + atOffset + 1210 offset - offsetTop; 1211 if ( newOverTop > 0 || abs( newOverTop ) < overBottom ) { 1212 position.top += myOffset + atOffset + offset; 1213 } 1214 } 1215 } 1216 }, 1217 flipfit: { 1218 left: function() { 1219 $.ui.position.flip.left.apply( this, arguments ); 1220 $.ui.position.fit.left.apply( this, arguments ); 1221 }, 1222 top: function() { 1223 $.ui.position.flip.top.apply( this, arguments ); 1224 $.ui.position.fit.top.apply( this, arguments ); 1225 } 1226 } 1227}; 1228 1229} )(); 1230 1231var position = $.ui.position; 1232 1233 1234/*! 1235 * jQuery UI Keycode 1.12.1 1236 * http://jqueryui.com 1237 * 1238 * Copyright jQuery Foundation and other contributors 1239 * Released under the MIT license. 1240 * http://jquery.org/license 1241 */ 1242 1243//>>label: Keycode 1244//>>group: Core 1245//>>description: Provide keycodes as keynames 1246//>>docs: http://api.jqueryui.com/jQuery.ui.keyCode/ 1247 1248 1249var keycode = $.ui.keyCode = { 1250 BACKSPACE: 8, 1251 COMMA: 188, 1252 DELETE: 46, 1253 DOWN: 40, 1254 END: 35, 1255 ENTER: 13, 1256 ESCAPE: 27, 1257 HOME: 36, 1258 LEFT: 37, 1259 PAGE_DOWN: 34, 1260 PAGE_UP: 33, 1261 PERIOD: 190, 1262 RIGHT: 39, 1263 SPACE: 32, 1264 TAB: 9, 1265 UP: 38 1266}; 1267 1268 1269/*! 1270 * jQuery UI Unique ID 1.12.1 1271 * http://jqueryui.com 1272 * 1273 * Copyright jQuery Foundation and other contributors 1274 * Released under the MIT license. 1275 * http://jquery.org/license 1276 */ 1277 1278//>>label: uniqueId 1279//>>group: Core 1280//>>description: Functions to generate and remove uniqueId's 1281//>>docs: http://api.jqueryui.com/uniqueId/ 1282 1283 1284 1285var uniqueId = $.fn.extend( { 1286 uniqueId: ( function() { 1287 var uuid = 0; 1288 1289 return function() { 1290 return this.each( function() { 1291 if ( !this.id ) { 1292 this.id = "ui-id-" + ( ++uuid ); 1293 } 1294 } ); 1295 }; 1296 } )(), 1297 1298 removeUniqueId: function() { 1299 return this.each( function() { 1300 if ( /^ui-id-\d+$/.test( this.id ) ) { 1301 $( this ).removeAttr( "id" ); 1302 } 1303 } ); 1304 } 1305} ); 1306 1307 1308 1309var safeActiveElement = $.ui.safeActiveElement = function( document ) { 1310 var activeElement; 1311 1312 // Support: IE 9 only 1313 // IE9 throws an "Unspecified error" accessing document.activeElement from an <iframe> 1314 try { 1315 activeElement = document.activeElement; 1316 } catch ( error ) { 1317 activeElement = document.body; 1318 } 1319 1320 // Support: IE 9 - 11 only 1321 // IE may return null instead of an element 1322 // Interestingly, this only seems to occur when NOT in an iframe 1323 if ( !activeElement ) { 1324 activeElement = document.body; 1325 } 1326 1327 // Support: IE 11 only 1328 // IE11 returns a seemingly empty object in some cases when accessing 1329 // document.activeElement from an <iframe> 1330 if ( !activeElement.nodeName ) { 1331 activeElement = document.body; 1332 } 1333 1334 return activeElement; 1335}; 1336 1337 1338/*! 1339 * jQuery UI Menu 1.12.1 1340 * http://jqueryui.com 1341 * 1342 * Copyright jQuery Foundation and other contributors 1343 * Released under the MIT license. 1344 * http://jquery.org/license 1345 */ 1346 1347//>>label: Menu 1348//>>group: Widgets 1349//>>description: Creates nestable menus. 1350//>>docs: http://api.jqueryui.com/menu/ 1351//>>demos: http://jqueryui.com/menu/ 1352//>>css.structure: ../../themes/base/core.css 1353//>>css.structure: ../../themes/base/menu.css 1354//>>css.theme: ../../themes/base/theme.css 1355 1356 1357 1358var widgetsMenu = $.widget( "ui.menu", { 1359 version: "1.12.1", 1360 defaultElement: "<ul>", 1361 delay: 300, 1362 options: { 1363 icons: { 1364 submenu: "ui-icon-caret-1-e" 1365 }, 1366 items: "> *", 1367 menus: "ul", 1368 position: { 1369 my: "left top", 1370 at: "right top" 1371 }, 1372 role: "menu", 1373 1374 // Callbacks 1375 blur: null, 1376 focus: null, 1377 select: null 1378 }, 1379 1380 _create: function() { 1381 this.activeMenu = this.element; 1382 1383 // Flag used to prevent firing of the click handler 1384 // as the event bubbles up through nested menus 1385 this.mouseHandled = false; 1386 this.element 1387 .uniqueId() 1388 .attr( { 1389 role: this.options.role, 1390 tabIndex: 0 1391 } ); 1392 1393 this._addClass( "ui-menu", "ui-widget ui-widget-content" ); 1394 this._on( { 1395 1396 // Prevent focus from sticking to links inside menu after clicking 1397 // them (focus should always stay on UL during navigation). 1398 "mousedown .ui-menu-item": function( event ) { 1399 event.preventDefault(); 1400 }, 1401 "click .ui-menu-item": function( event ) { 1402 var target = $( event.target ); 1403 var active = $( $.ui.safeActiveElement( this.document[ 0 ] ) ); 1404 if ( !this.mouseHandled && target.not( ".ui-state-disabled" ).length ) { 1405 this.select( event ); 1406 1407 // Only set the mouseHandled flag if the event will bubble, see #9469. 1408 if ( !event.isPropagationStopped() ) { 1409 this.mouseHandled = true; 1410 } 1411 1412 // Open submenu on click 1413 if ( target.has( ".ui-menu" ).length ) { 1414 this.expand( event ); 1415 } else if ( !this.element.is( ":focus" ) && 1416 active.closest( ".ui-menu" ).length ) { 1417 1418 // Redirect focus to the menu 1419 this.element.trigger( "focus", [ true ] ); 1420 1421 // If the active item is on the top level, let it stay active. 1422 // Otherwise, blur the active item since it is no longer visible. 1423 if ( this.active && this.active.parents( ".ui-menu" ).length === 1 ) { 1424 clearTimeout( this.timer ); 1425 } 1426 } 1427 } 1428 }, 1429 "mouseenter .ui-menu-item": function( event ) { 1430 1431 // Ignore mouse events while typeahead is active, see #10458. 1432 // Prevents focusing the wrong item when typeahead causes a scroll while the mouse 1433 // is over an item in the menu 1434 if ( this.previousFilter ) { 1435 return; 1436 } 1437 1438 var actualTarget = $( event.target ).closest( ".ui-menu-item" ), 1439 target = $( event.currentTarget ); 1440 1441 // Ignore bubbled events on parent items, see #11641 1442 if ( actualTarget[ 0 ] !== target[ 0 ] ) { 1443 return; 1444 } 1445 1446 // Remove ui-state-active class from siblings of the newly focused menu item 1447 // to avoid a jump caused by adjacent elements both having a class with a border 1448 this._removeClass( target.siblings().children( ".ui-state-active" ), 1449 null, "ui-state-active" ); 1450 this.focus( event, target ); 1451 }, 1452 mouseleave: "collapseAll", 1453 "mouseleave .ui-menu": "collapseAll", 1454 focus: function( event, keepActiveItem ) { 1455 1456 // If there's already an active item, keep it active 1457 // If not, activate the first item 1458 var item = this.active || this.element.find( this.options.items ).eq( 0 ); 1459 1460 if ( !keepActiveItem ) { 1461 this.focus( event, item ); 1462 } 1463 }, 1464 blur: function( event ) { 1465 this._delay( function() { 1466 var notContained = !$.contains( 1467 this.element[ 0 ], 1468 $.ui.safeActiveElement( this.document[ 0 ] ) 1469 ); 1470 if ( notContained ) { 1471 this.collapseAll( event ); 1472 } 1473 } ); 1474 }, 1475 keydown: "_keydown" 1476 } ); 1477 1478 this.refresh(); 1479 1480 // Clicks outside of a menu collapse any open menus 1481 this._on( this.document, { 1482 click: function( event ) { 1483 if ( this._closeOnDocumentClick( event ) ) { 1484 this.collapseAll( event ); 1485 } 1486 1487 // Reset the mouseHandled flag 1488 this.mouseHandled = false; 1489 } 1490 } ); 1491 }, 1492 1493 _destroy: function() { 1494 var items = this.element.find( ".ui-menu-item" ) 1495 .removeAttr( "role aria-disabled" ), 1496 submenus = items.children( ".ui-menu-item-wrapper" ) 1497 .removeUniqueId() 1498 .removeAttr( "tabIndex role aria-haspopup" ); 1499 1500 // Destroy (sub)menus 1501 this.element 1502 .removeAttr( "aria-activedescendant" ) 1503 .find( ".ui-menu" ).addBack() 1504 .removeAttr( "role aria-labelledby aria-expanded aria-hidden aria-disabled " + 1505 "tabIndex" ) 1506 .removeUniqueId() 1507 .show(); 1508 1509 submenus.children().each( function() { 1510 var elem = $( this ); 1511 if ( elem.data( "ui-menu-submenu-caret" ) ) { 1512 elem.remove(); 1513 } 1514 } ); 1515 }, 1516 1517 _keydown: function( event ) { 1518 var match, prev, character, skip, 1519 preventDefault = true; 1520 1521 switch ( event.keyCode ) { 1522 case $.ui.keyCode.PAGE_UP: 1523 this.previousPage( event ); 1524 break; 1525 case $.ui.keyCode.PAGE_DOWN: 1526 this.nextPage( event ); 1527 break; 1528 case $.ui.keyCode.HOME: 1529 this._move( "first", "first", event ); 1530 break; 1531 case $.ui.keyCode.END: 1532 this._move( "last", "last", event ); 1533 break; 1534 case $.ui.keyCode.UP: 1535 this.previous( event ); 1536 break; 1537 case $.ui.keyCode.DOWN: 1538 this.next( event ); 1539 break; 1540 case $.ui.keyCode.LEFT: 1541 this.collapse( event ); 1542 break; 1543 case $.ui.keyCode.RIGHT: 1544 if ( this.active && !this.active.is( ".ui-state-disabled" ) ) { 1545 this.expand( event ); 1546 } 1547 break; 1548 case $.ui.keyCode.ENTER: 1549 case $.ui.keyCode.SPACE: 1550 this._activate( event ); 1551 break; 1552 case $.ui.keyCode.ESCAPE: 1553 this.collapse( event ); 1554 break; 1555 default: 1556 preventDefault = false; 1557 prev = this.previousFilter || ""; 1558 skip = false; 1559 1560 // Support number pad values 1561 character = event.keyCode >= 96 && event.keyCode <= 105 ? 1562 ( event.keyCode - 96 ).toString() : String.fromCharCode( event.keyCode ); 1563 1564 clearTimeout( this.filterTimer ); 1565 1566 if ( character === prev ) { 1567 skip = true; 1568 } else { 1569 character = prev + character; 1570 } 1571 1572 match = this._filterMenuItems( character ); 1573 match = skip && match.index( this.active.next() ) !== -1 ? 1574 this.active.nextAll( ".ui-menu-item" ) : 1575 match; 1576 1577 // If no matches on the current filter, reset to the last character pressed 1578 // to move down the menu to the first item that starts with that character 1579 if ( !match.length ) { 1580 character = String.fromCharCode( event.keyCode ); 1581 match = this._filterMenuItems( character ); 1582 } 1583 1584 if ( match.length ) { 1585 this.focus( event, match ); 1586 this.previousFilter = character; 1587 this.filterTimer = this._delay( function() { 1588 delete this.previousFilter; 1589 }, 1000 ); 1590 } else { 1591 delete this.previousFilter; 1592 } 1593 } 1594 1595 if ( preventDefault ) { 1596 event.preventDefault(); 1597 } 1598 }, 1599 1600 _activate: function( event ) { 1601 if ( this.active && !this.active.is( ".ui-state-disabled" ) ) { 1602 if ( this.active.children( "[aria-haspopup='true']" ).length ) { 1603 this.expand( event ); 1604 } else { 1605 this.select( event ); 1606 } 1607 } 1608 }, 1609 1610 refresh: function() { 1611 var menus, items, newSubmenus, newItems, newWrappers, 1612 that = this, 1613 icon = this.options.icons.submenu, 1614 submenus = this.element.find( this.options.menus ); 1615 1616 this._toggleClass( "ui-menu-icons", null, !!this.element.find( ".ui-icon" ).length ); 1617 1618 // Initialize nested menus 1619 newSubmenus = submenus.filter( ":not(.ui-menu)" ) 1620 .hide() 1621 .attr( { 1622 role: this.options.role, 1623 "aria-hidden": "true", 1624 "aria-expanded": "false" 1625 } ) 1626 .each( function() { 1627 var menu = $( this ), 1628 item = menu.prev(), 1629 submenuCaret = $( "<span>" ).data( "ui-menu-submenu-caret", true ); 1630 1631 that._addClass( submenuCaret, "ui-menu-icon", "ui-icon " + icon ); 1632 item 1633 .attr( "aria-haspopup", "true" ) 1634 .prepend( submenuCaret ); 1635 menu.attr( "aria-labelledby", item.attr( "id" ) ); 1636 } ); 1637 1638 this._addClass( newSubmenus, "ui-menu", "ui-widget ui-widget-content ui-front" ); 1639 1640 menus = submenus.add( this.element ); 1641 items = menus.find( this.options.items ); 1642 1643 // Initialize menu-items containing spaces and/or dashes only as dividers 1644 items.not( ".ui-menu-item" ).each( function() { 1645 var item = $( this ); 1646 if ( that._isDivider( item ) ) { 1647 that._addClass( item, "ui-menu-divider", "ui-widget-content" ); 1648 } 1649 } ); 1650 1651 // Don't refresh list items that are already adapted 1652 newItems = items.not( ".ui-menu-item, .ui-menu-divider" ); 1653 newWrappers = newItems.children() 1654 .not( ".ui-menu" ) 1655 .uniqueId() 1656 .attr( { 1657 tabIndex: -1, 1658 role: this._itemRole() 1659 } ); 1660 this._addClass( newItems, "ui-menu-item" ) 1661 ._addClass( newWrappers, "ui-menu-item-wrapper" ); 1662 1663 // Add aria-disabled attribute to any disabled menu item 1664 items.filter( ".ui-state-disabled" ).attr( "aria-disabled", "true" ); 1665 1666 // If the active item has been removed, blur the menu 1667 if ( this.active && !$.contains( this.element[ 0 ], this.active[ 0 ] ) ) { 1668 this.blur(); 1669 } 1670 }, 1671 1672 _itemRole: function() { 1673 return { 1674 menu: "menuitem", 1675 listbox: "option" 1676 }[ this.options.role ]; 1677 }, 1678 1679 _setOption: function( key, value ) { 1680 if ( key === "icons" ) { 1681 var icons = this.element.find( ".ui-menu-icon" ); 1682 this._removeClass( icons, null, this.options.icons.submenu ) 1683 ._addClass( icons, null, value.submenu ); 1684 } 1685 this._super( key, value ); 1686 }, 1687 1688 _setOptionDisabled: function( value ) { 1689 this._super( value ); 1690 1691 this.element.attr( "aria-disabled", String( value ) ); 1692 this._toggleClass( null, "ui-state-disabled", !!value ); 1693 }, 1694 1695 focus: function( event, item ) { 1696 var nested, focused, activeParent; 1697 this.blur( event, event && event.type === "focus" ); 1698 1699 this._scrollIntoView( item ); 1700 1701 this.active = item.first(); 1702 1703 focused = this.active.children( ".ui-menu-item-wrapper" ); 1704 this._addClass( focused, null, "ui-state-active" ); 1705 1706 // Only update aria-activedescendant if there's a role 1707 // otherwise we assume focus is managed elsewhere 1708 if ( this.options.role ) { 1709 this.element.attr( "aria-activedescendant", focused.attr( "id" ) ); 1710 } 1711 1712 // Highlight active parent menu item, if any 1713 activeParent = this.active 1714 .parent() 1715 .closest( ".ui-menu-item" ) 1716 .children( ".ui-menu-item-wrapper" ); 1717 this._addClass( activeParent, null, "ui-state-active" ); 1718 1719 if ( event && event.type === "keydown" ) { 1720 this._close(); 1721 } else { 1722 this.timer = this._delay( function() { 1723 this._close(); 1724 }, this.delay ); 1725 } 1726 1727 nested = item.children( ".ui-menu" ); 1728 if ( nested.length && event && ( /^mouse/.test( event.type ) ) ) { 1729 this._startOpening( nested ); 1730 } 1731 this.activeMenu = item.parent(); 1732 1733 this._trigger( "focus", event, { item: item } ); 1734 }, 1735 1736 _scrollIntoView: function( item ) { 1737 var borderTop, paddingTop, offset, scroll, elementHeight, itemHeight; 1738 if ( this._hasScroll() ) { 1739 borderTop = parseFloat( $.css( this.activeMenu[ 0 ], "borderTopWidth" ) ) || 0; 1740 paddingTop = parseFloat( $.css( this.activeMenu[ 0 ], "paddingTop" ) ) || 0; 1741 offset = item.offset().top - this.activeMenu.offset().top - borderTop - paddingTop; 1742 scroll = this.activeMenu.scrollTop(); 1743 elementHeight = this.activeMenu.height(); 1744 itemHeight = item.outerHeight(); 1745 1746 if ( offset < 0 ) { 1747 this.activeMenu.scrollTop( scroll + offset ); 1748 } else if ( offset + itemHeight > elementHeight ) { 1749 this.activeMenu.scrollTop( scroll + offset - elementHeight + itemHeight ); 1750 } 1751 } 1752 }, 1753 1754 blur: function( event, fromFocus ) { 1755 if ( !fromFocus ) { 1756 clearTimeout( this.timer ); 1757 } 1758 1759 if ( !this.active ) { 1760 return; 1761 } 1762 1763 this._removeClass( this.active.children( ".ui-menu-item-wrapper" ), 1764 null, "ui-state-active" ); 1765 1766 this._trigger( "blur", event, { item: this.active } ); 1767 this.active = null; 1768 }, 1769 1770 _startOpening: function( submenu ) { 1771 clearTimeout( this.timer ); 1772 1773 // Don't open if already open fixes a Firefox bug that caused a .5 pixel 1774 // shift in the submenu position when mousing over the caret icon 1775 if ( submenu.attr( "aria-hidden" ) !== "true" ) { 1776 return; 1777 } 1778 1779 this.timer = this._delay( function() { 1780 this._close(); 1781 this._open( submenu ); 1782 }, this.delay ); 1783 }, 1784 1785 _open: function( submenu ) { 1786 var position = $.extend( { 1787 of: this.active 1788 }, this.options.position ); 1789 1790 clearTimeout( this.timer ); 1791 this.element.find( ".ui-menu" ).not( submenu.parents( ".ui-menu" ) ) 1792 .hide() 1793 .attr( "aria-hidden", "true" ); 1794 1795 submenu 1796 .show() 1797 .removeAttr( "aria-hidden" ) 1798 .attr( "aria-expanded", "true" ) 1799 .position( position ); 1800 }, 1801 1802 collapseAll: function( event, all ) { 1803 clearTimeout( this.timer ); 1804 this.timer = this._delay( function() { 1805 1806 // If we were passed an event, look for the submenu that contains the event 1807 var currentMenu = all ? this.element : 1808 $( event && event.target ).closest( this.element.find( ".ui-menu" ) ); 1809 1810 // If we found no valid submenu ancestor, use the main menu to close all 1811 // sub menus anyway 1812 if ( !currentMenu.length ) { 1813 currentMenu = this.element; 1814 } 1815 1816 this._close( currentMenu ); 1817 1818 this.blur( event ); 1819 1820 // Work around active item staying active after menu is blurred 1821 this._removeClass( currentMenu.find( ".ui-state-active" ), null, "ui-state-active" ); 1822 1823 this.activeMenu = currentMenu; 1824 }, this.delay ); 1825 }, 1826 1827 // With no arguments, closes the currently active menu - if nothing is active 1828 // it closes all menus. If passed an argument, it will search for menus BELOW 1829 _close: function( startMenu ) { 1830 if ( !startMenu ) { 1831 startMenu = this.active ? this.active.parent() : this.element; 1832 } 1833 1834 startMenu.find( ".ui-menu" ) 1835 .hide() 1836 .attr( "aria-hidden", "true" ) 1837 .attr( "aria-expanded", "false" ); 1838 }, 1839 1840 _closeOnDocumentClick: function( event ) { 1841 return !$( event.target ).closest( ".ui-menu" ).length; 1842 }, 1843 1844 _isDivider: function( item ) { 1845 1846 // Match hyphen, em dash, en dash 1847 return !/[^\-\u2014\u2013\s]/.test( item.text() ); 1848 }, 1849 1850 collapse: function( event ) { 1851 var newItem = this.active && 1852 this.active.parent().closest( ".ui-menu-item", this.element ); 1853 if ( newItem && newItem.length ) { 1854 this._close(); 1855 this.focus( event, newItem ); 1856 } 1857 }, 1858 1859 expand: function( event ) { 1860 var newItem = this.active && 1861 this.active 1862 .children( ".ui-menu " ) 1863 .find( this.options.items ) 1864 .first(); 1865 1866 if ( newItem && newItem.length ) { 1867 this._open( newItem.parent() ); 1868 1869 // Delay so Firefox will not hide activedescendant change in expanding submenu from AT 1870 this._delay( function() { 1871 this.focus( event, newItem ); 1872 } ); 1873 } 1874 }, 1875 1876 next: function( event ) { 1877 this._move( "next", "first", event ); 1878 }, 1879 1880 previous: function( event ) { 1881 this._move( "prev", "last", event ); 1882 }, 1883 1884 isFirstItem: function() { 1885 return this.active && !this.active.prevAll( ".ui-menu-item" ).length; 1886 }, 1887 1888 isLastItem: function() { 1889 return this.active && !this.active.nextAll( ".ui-menu-item" ).length; 1890 }, 1891 1892 _move: function( direction, filter, event ) { 1893 var next; 1894 if ( this.active ) { 1895 if ( direction === "first" || direction === "last" ) { 1896 next = this.active 1897 [ direction === "first" ? "prevAll" : "nextAll" ]( ".ui-menu-item" ) 1898 .eq( -1 ); 1899 } else { 1900 next = this.active 1901 [ direction + "All" ]( ".ui-menu-item" ) 1902 .eq( 0 ); 1903 } 1904 } 1905 if ( !next || !next.length || !this.active ) { 1906 next = this.activeMenu.find( this.options.items )[ filter ](); 1907 } 1908 1909 this.focus( event, next ); 1910 }, 1911 1912 nextPage: function( event ) { 1913 var item, base, height; 1914 1915 if ( !this.active ) { 1916 this.next( event ); 1917 return; 1918 } 1919 if ( this.isLastItem() ) { 1920 return; 1921 } 1922 if ( this._hasScroll() ) { 1923 base = this.active.offset().top; 1924 height = this.element.height(); 1925 this.active.nextAll( ".ui-menu-item" ).each( function() { 1926 item = $( this ); 1927 return item.offset().top - base - height < 0; 1928 } ); 1929 1930 this.focus( event, item ); 1931 } else { 1932 this.focus( event, this.activeMenu.find( this.options.items ) 1933 [ !this.active ? "first" : "last" ]() ); 1934 } 1935 }, 1936 1937 previousPage: function( event ) { 1938 var item, base, height; 1939 if ( !this.active ) { 1940 this.next( event ); 1941 return; 1942 } 1943 if ( this.isFirstItem() ) { 1944 return; 1945 } 1946 if ( this._hasScroll() ) { 1947 base = this.active.offset().top; 1948 height = this.element.height(); 1949 this.active.prevAll( ".ui-menu-item" ).each( function() { 1950 item = $( this ); 1951 return item.offset().top - base + height > 0; 1952 } ); 1953 1954 this.focus( event, item ); 1955 } else { 1956 this.focus( event, this.activeMenu.find( this.options.items ).first() ); 1957 } 1958 }, 1959 1960 _hasScroll: function() { 1961 return this.element.outerHeight() < this.element.prop( "scrollHeight" ); 1962 }, 1963 1964 select: function( event ) { 1965 1966 // TODO: It should never be possible to not have an active item at this 1967 // point, but the tests don't trigger mouseenter before click. 1968 this.active = this.active || $( event.target ).closest( ".ui-menu-item" ); 1969 var ui = { item: this.active }; 1970 if ( !this.active.has( ".ui-menu" ).length ) { 1971 this.collapseAll( event, true ); 1972 } 1973 this._trigger( "select", event, ui ); 1974 }, 1975 1976 _filterMenuItems: function( character ) { 1977 var escapedCharacter = character.replace( /[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&" ), 1978 regex = new RegExp( "^" + escapedCharacter, "i" ); 1979 1980 return this.activeMenu 1981 .find( this.options.items ) 1982 1983 // Only match on items, not dividers or other content (#10571) 1984 .filter( ".ui-menu-item" ) 1985 .filter( function() { 1986 return regex.test( 1987 $.trim( $( this ).children( ".ui-menu-item-wrapper" ).text() ) ); 1988 } ); 1989 } 1990} ); 1991 1992 1993/*! 1994 * jQuery UI Autocomplete 1.12.1 1995 * http://jqueryui.com 1996 * 1997 * Copyright jQuery Foundation and other contributors 1998 * Released under the MIT license. 1999 * http://jquery.org/license 2000 */ 2001 2002//>>label: Autocomplete 2003//>>group: Widgets 2004//>>description: Lists suggested words as the user is typing. 2005//>>docs: http://api.jqueryui.com/autocomplete/ 2006//>>demos: http://jqueryui.com/autocomplete/ 2007//>>css.structure: ../../themes/base/core.css 2008//>>css.structure: ../../themes/base/autocomplete.css 2009//>>css.theme: ../../themes/base/theme.css 2010 2011 2012 2013$.widget( "ui.autocomplete", { 2014 version: "1.12.1", 2015 defaultElement: "<input>", 2016 options: { 2017 appendTo: null, 2018 autoFocus: false, 2019 delay: 300, 2020 minLength: 1, 2021 position: { 2022 my: "left top", 2023 at: "left bottom", 2024 collision: "none" 2025 }, 2026 source: null, 2027 2028 // Callbacks 2029 change: null, 2030 close: null, 2031 focus: null, 2032 open: null, 2033 response: null, 2034 search: null, 2035 select: null 2036 }, 2037 2038 requestIndex: 0, 2039 pending: 0, 2040 2041 _create: function() { 2042 2043 // Some browsers only repeat keydown events, not keypress events, 2044 // so we use the suppressKeyPress flag to determine if we've already 2045 // handled the keydown event. #7269 2046 // Unfortunately the code for & in keypress is the same as the up arrow, 2047 // so we use the suppressKeyPressRepeat flag to avoid handling keypress 2048 // events when we know the keydown event was used to modify the 2049 // search term. #7799 2050 var suppressKeyPress, suppressKeyPressRepeat, suppressInput, 2051 nodeName = this.element[ 0 ].nodeName.toLowerCase(), 2052 isTextarea = nodeName === "textarea", 2053 isInput = nodeName === "input"; 2054 2055 // Textareas are always multi-line 2056 // Inputs are always single-line, even if inside a contentEditable element 2057 // IE also treats inputs as contentEditable 2058 // All other element types are determined by whether or not they're contentEditable 2059 this.isMultiLine = isTextarea || !isInput && this._isContentEditable( this.element ); 2060 2061 this.valueMethod = this.element[ isTextarea || isInput ? "val" : "text" ]; 2062 this.isNewMenu = true; 2063 2064 this._addClass( "ui-autocomplete-input" ); 2065 this.element.attr( "autocomplete", "off" ); 2066 2067 this._on( this.element, { 2068 keydown: function( event ) { 2069 if ( this.element.prop( "readOnly" ) ) { 2070 suppressKeyPress = true; 2071 suppressInput = true; 2072 suppressKeyPressRepeat = true; 2073 return; 2074 } 2075 2076 suppressKeyPress = false; 2077 suppressInput = false; 2078 suppressKeyPressRepeat = false; 2079 var keyCode = $.ui.keyCode; 2080 switch ( event.keyCode ) { 2081 case keyCode.PAGE_UP: 2082 suppressKeyPress = true; 2083 this._move( "previousPage", event ); 2084 break; 2085 case keyCode.PAGE_DOWN: 2086 suppressKeyPress = true; 2087 this._move( "nextPage", event ); 2088 break; 2089 case keyCode.UP: 2090 suppressKeyPress = true; 2091 this._keyEvent( "previous", event ); 2092 break; 2093 case keyCode.DOWN: 2094 suppressKeyPress = true; 2095 this._keyEvent( "next", event ); 2096 break; 2097 case keyCode.ENTER: 2098 2099 // when menu is open and has focus 2100 if ( this.menu.active ) { 2101 2102 // #6055 - Opera still allows the keypress to occur 2103 // which causes forms to submit 2104 suppressKeyPress = true; 2105 event.preventDefault(); 2106 this.menu.select( event ); 2107 } 2108 break; 2109 case keyCode.TAB: 2110 if ( this.menu.active ) { 2111 this.menu.select( event ); 2112 } 2113 break; 2114 case keyCode.ESCAPE: 2115 if ( this.menu.element.is( ":visible" ) ) { 2116 if ( !this.isMultiLine ) { 2117 this._value( this.term ); 2118 } 2119 this.close( event ); 2120 2121 // Different browsers have different default behavior for escape 2122 // Single press can mean undo or clear 2123 // Double press in IE means clear the whole form 2124 event.preventDefault(); 2125 } 2126 break; 2127 default: 2128 suppressKeyPressRepeat = true; 2129 2130 // search timeout should be triggered before the input value is changed 2131 this._searchTimeout( event ); 2132 break; 2133 } 2134 }, 2135 keypress: function( event ) { 2136 if ( suppressKeyPress ) { 2137 suppressKeyPress = false; 2138 if ( !this.isMultiLine || this.menu.element.is( ":visible" ) ) { 2139 event.preventDefault(); 2140 } 2141 return; 2142 } 2143 if ( suppressKeyPressRepeat ) { 2144 return; 2145 } 2146 2147 // Replicate some key handlers to allow them to repeat in Firefox and Opera 2148 var keyCode = $.ui.keyCode; 2149 switch ( event.keyCode ) { 2150 case keyCode.PAGE_UP: 2151 this._move( "previousPage", event ); 2152 break; 2153 case keyCode.PAGE_DOWN: 2154 this._move( "nextPage", event ); 2155 break; 2156 case keyCode.UP: 2157 this._keyEvent( "previous", event ); 2158 break; 2159 case keyCode.DOWN: 2160 this._keyEvent( "next", event ); 2161 break; 2162 } 2163 }, 2164 input: function( event ) { 2165 if ( suppressInput ) { 2166 suppressInput = false; 2167 event.preventDefault(); 2168 return; 2169 } 2170 this._searchTimeout( event ); 2171 }, 2172 focus: function() { 2173 this.selectedItem = null; 2174 this.previous = this._value(); 2175 }, 2176 blur: function( event ) { 2177 if ( this.cancelBlur ) { 2178 delete this.cancelBlur; 2179 return; 2180 } 2181 2182 clearTimeout( this.searching ); 2183 this.close( event ); 2184 this._change( event ); 2185 } 2186 } ); 2187 2188 this._initSource(); 2189 this.menu = $( "<ul>" ) 2190 .appendTo( this._appendTo() ) 2191 .menu( { 2192 2193 // disable ARIA support, the live region takes care of that 2194 role: null 2195 } ) 2196 .hide() 2197 .menu( "instance" ); 2198 2199 this._addClass( this.menu.element, "ui-autocomplete", "ui-front" ); 2200 this._on( this.menu.element, { 2201 mousedown: function( event ) { 2202 2203 // prevent moving focus out of the text field 2204 event.preventDefault(); 2205 2206 // IE doesn't prevent moving focus even with event.preventDefault() 2207 // so we set a flag to know when we should ignore the blur event 2208 this.cancelBlur = true; 2209 this._delay( function() { 2210 delete this.cancelBlur; 2211 2212 // Support: IE 8 only 2213 // Right clicking a menu item or selecting text from the menu items will 2214 // result in focus moving out of the input. However, we've already received 2215 // and ignored the blur event because of the cancelBlur flag set above. So 2216 // we restore focus to ensure that the menu closes properly based on the user's 2217 // next actions. 2218 if ( this.element[ 0 ] !== $.ui.safeActiveElement( this.document[ 0 ] ) ) { 2219 this.element.trigger( "focus" ); 2220 } 2221 } ); 2222 }, 2223 menufocus: function( event, ui ) { 2224 var label, item; 2225 2226 // support: Firefox 2227 // Prevent accidental activation of menu items in Firefox (#7024 #9118) 2228 if ( this.isNewMenu ) { 2229 this.isNewMenu = false; 2230 if ( event.originalEvent && /^mouse/.test( event.originalEvent.type ) ) { 2231 this.menu.blur(); 2232 2233 this.document.one( "mousemove", function() { 2234 $( event.target ).trigger( event.originalEvent ); 2235 } ); 2236 2237 return; 2238 } 2239 } 2240 2241 item = ui.item.data( "ui-autocomplete-item" ); 2242 if ( false !== this._trigger( "focus", event, { item: item } ) ) { 2243 2244 // use value to match what will end up in the input, if it was a key event 2245 if ( event.originalEvent && /^key/.test( event.originalEvent.type ) ) { 2246 this._value( item.value ); 2247 } 2248 } 2249 2250 // Announce the value in the liveRegion 2251 label = ui.item.attr( "aria-label" ) || item.value; 2252 if ( label && $.trim( label ).length ) { 2253 this.liveRegion.children().hide(); 2254 $( "<div>" ).text( label ).appendTo( this.liveRegion ); 2255 } 2256 }, 2257 menuselect: function( event, ui ) { 2258 var item = ui.item.data( "ui-autocomplete-item" ), 2259 previous = this.previous; 2260 2261 // Only trigger when focus was lost (click on menu) 2262 if ( this.element[ 0 ] !== $.ui.safeActiveElement( this.document[ 0 ] ) ) { 2263 this.element.trigger( "focus" ); 2264 this.previous = previous; 2265 2266 // #6109 - IE triggers two focus events and the second 2267 // is asynchronous, so we need to reset the previous 2268 // term synchronously and asynchronously :-( 2269 this._delay( function() { 2270 this.previous = previous; 2271 this.selectedItem = item; 2272 } ); 2273 } 2274 2275 if ( false !== this._trigger( "select", event, { item: item } ) ) { 2276 this._value( item.value ); 2277 } 2278 2279 // reset the term after the select event 2280 // this allows custom select handling to work properly 2281 this.term = this._value(); 2282 2283 this.close( event ); 2284 this.selectedItem = item; 2285 } 2286 } ); 2287 2288 this.liveRegion = $( "<div>", { 2289 role: "status", 2290 "aria-live": "assertive", 2291 "aria-relevant": "additions" 2292 } ) 2293 .appendTo( this.document[ 0 ].body ); 2294 2295 this._addClass( this.liveRegion, null, "ui-helper-hidden-accessible" ); 2296 2297 // Turning off autocomplete prevents the browser from remembering the 2298 // value when navigating through history, so we re-enable autocomplete 2299 // if the page is unloaded before the widget is destroyed. #7790 2300 this._on( this.window, { 2301 beforeunload: function() { 2302 this.element.removeAttr( "autocomplete" ); 2303 } 2304 } ); 2305 }, 2306 2307 _destroy: function() { 2308 clearTimeout( this.searching ); 2309 this.element.removeAttr( "autocomplete" ); 2310 this.menu.element.remove(); 2311 this.liveRegion.remove(); 2312 }, 2313 2314 _setOption: function( key, value ) { 2315 this._super( key, value ); 2316 if ( key === "source" ) { 2317 this._initSource(); 2318 } 2319 if ( key === "appendTo" ) { 2320 this.menu.element.appendTo( this._appendTo() ); 2321 } 2322 if ( key === "disabled" && value && this.xhr ) { 2323 this.xhr.abort(); 2324 } 2325 }, 2326 2327 _isEventTargetInWidget: function( event ) { 2328 var menuElement = this.menu.element[ 0 ]; 2329 2330 return event.target === this.element[ 0 ] || 2331 event.target === menuElement || 2332 $.contains( menuElement, event.target ); 2333 }, 2334 2335 _closeOnClickOutside: function( event ) { 2336 if ( !this._isEventTargetInWidget( event ) ) { 2337 this.close(); 2338 } 2339 }, 2340 2341 _appendTo: function() { 2342 var element = this.options.appendTo; 2343 2344 if ( element ) { 2345 element = element.jquery || element.nodeType ? 2346 $( element ) : 2347 this.document.find( element ).eq( 0 ); 2348 } 2349 2350 if ( !element || !element[ 0 ] ) { 2351 element = this.element.closest( ".ui-front, dialog" ); 2352 } 2353 2354 if ( !element.length ) { 2355 element = this.document[ 0 ].body; 2356 } 2357 2358 return element; 2359 }, 2360 2361 _initSource: function() { 2362 var array, url, 2363 that = this; 2364 if ( $.isArray( this.options.source ) ) { 2365 array = this.options.source; 2366 this.source = function( request, response ) { 2367 response( $.ui.autocomplete.filter( array, request.term ) ); 2368 }; 2369 } else if ( typeof this.options.source === "string" ) { 2370 url = this.options.source; 2371 this.source = function( request, response ) { 2372 if ( that.xhr ) { 2373 that.xhr.abort(); 2374 } 2375 that.xhr = $.ajax( { 2376 url: url, 2377 data: request, 2378 dataType: "json", 2379 success: function( data ) { 2380 response( data ); 2381 }, 2382 error: function() { 2383 response( [] ); 2384 } 2385 } ); 2386 }; 2387 } else { 2388 this.source = this.options.source; 2389 } 2390 }, 2391 2392 _searchTimeout: function( event ) { 2393 clearTimeout( this.searching ); 2394 this.searching = this._delay( function() { 2395 2396 // Search if the value has changed, or if the user retypes the same value (see #7434) 2397 var equalValues = this.term === this._value(), 2398 menuVisible = this.menu.element.is( ":visible" ), 2399 modifierKey = event.altKey || event.ctrlKey || event.metaKey || event.shiftKey; 2400 2401 if ( !equalValues || ( equalValues && !menuVisible && !modifierKey ) ) { 2402 this.selectedItem = null; 2403 this.search( null, event ); 2404 } 2405 }, this.options.delay ); 2406 }, 2407 2408 search: function( value, event ) { 2409 value = value != null ? value : this._value(); 2410 2411 // Always save the actual value, not the one passed as an argument 2412 this.term = this._value(); 2413 2414 if ( value.length < this.options.minLength ) { 2415 return this.close( event ); 2416 } 2417 2418 if ( this._trigger( "search", event ) === false ) { 2419 return; 2420 } 2421 2422 return this._search( value ); 2423 }, 2424 2425 _search: function( value ) { 2426 this.pending++; 2427 this._addClass( "ui-autocomplete-loading" ); 2428 this.cancelSearch = false; 2429 2430 this.source( { term: value }, this._response() ); 2431 }, 2432 2433 _response: function() { 2434 var index = ++this.requestIndex; 2435 2436 return $.proxy( function( content ) { 2437 if ( index === this.requestIndex ) { 2438 this.__response( content ); 2439 } 2440 2441 this.pending--; 2442 if ( !this.pending ) { 2443 this._removeClass( "ui-autocomplete-loading" ); 2444 } 2445 }, this ); 2446 }, 2447 2448 __response: function( content ) { 2449 if ( content ) { 2450 content = this._normalize( content ); 2451 } 2452 this._trigger( "response", null, { content: content } ); 2453 if ( !this.options.disabled && content && content.length && !this.cancelSearch ) { 2454 this._suggest( content ); 2455 this._trigger( "open" ); 2456 } else { 2457 2458 // use ._close() instead of .close() so we don't cancel future searches 2459 this._close(); 2460 } 2461 }, 2462 2463 close: function( event ) { 2464 this.cancelSearch = true; 2465 this._close( event ); 2466 }, 2467 2468 _close: function( event ) { 2469 2470 // Remove the handler that closes the menu on outside clicks 2471 this._off( this.document, "mousedown" ); 2472 2473 if ( this.menu.element.is( ":visible" ) ) { 2474 this.menu.element.hide(); 2475 this.menu.blur(); 2476 this.isNewMenu = true; 2477 this._trigger( "close", event ); 2478 } 2479 }, 2480 2481 _change: function( event ) { 2482 if ( this.previous !== this._value() ) { 2483 this._trigger( "change", event, { item: this.selectedItem } ); 2484 } 2485 }, 2486 2487 _normalize: function( items ) { 2488 2489 // assume all items have the right format when the first item is complete 2490 if ( items.length && items[ 0 ].label && items[ 0 ].value ) { 2491 return items; 2492 } 2493 return $.map( items, function( item ) { 2494 if ( typeof item === "string" ) { 2495 return { 2496 label: item, 2497 value: item 2498 }; 2499 } 2500 return $.extend( {}, item, { 2501 label: item.label || item.value, 2502 value: item.value || item.label 2503 } ); 2504 } ); 2505 }, 2506 2507 _suggest: function( items ) { 2508 var ul = this.menu.element.empty(); 2509 this._renderMenu( ul, items ); 2510 this.isNewMenu = true; 2511 this.menu.refresh(); 2512 2513 // Size and position menu 2514 ul.show(); 2515 this._resizeMenu(); 2516 ul.position( $.extend( { 2517 of: this.element 2518 }, this.options.position ) ); 2519 2520 if ( this.options.autoFocus ) { 2521 this.menu.next(); 2522 } 2523 2524 // Listen for interactions outside of the widget (#6642) 2525 this._on( this.document, { 2526 mousedown: "_closeOnClickOutside" 2527 } ); 2528 }, 2529 2530 _resizeMenu: function() { 2531 var ul = this.menu.element; 2532 ul.outerWidth( Math.max( 2533 2534 // Firefox wraps long text (possibly a rounding bug) 2535 // so we add 1px to avoid the wrapping (#7513) 2536 ul.width( "" ).outerWidth() + 1, 2537 this.element.outerWidth() 2538 ) ); 2539 }, 2540 2541 _renderMenu: function( ul, items ) { 2542 var that = this; 2543 $.each( items, function( index, item ) { 2544 that._renderItemData( ul, item ); 2545 } ); 2546 }, 2547 2548 _renderItemData: function( ul, item ) { 2549 return this._renderItem( ul, item ).data( "ui-autocomplete-item", item ); 2550 }, 2551 2552 _renderItem: function( ul, item ) { 2553 return $( "<li>" ) 2554 .append( $( "<div>" ).text( item.label ) ) 2555 .appendTo( ul ); 2556 }, 2557 2558 _move: function( direction, event ) { 2559 if ( !this.menu.element.is( ":visible" ) ) { 2560 this.search( null, event ); 2561 return; 2562 } 2563 if ( this.menu.isFirstItem() && /^previous/.test( direction ) || 2564 this.menu.isLastItem() && /^next/.test( direction ) ) { 2565 2566 if ( !this.isMultiLine ) { 2567 this._value( this.term ); 2568 } 2569 2570 this.menu.blur(); 2571 return; 2572 } 2573 this.menu[ direction ]( event ); 2574 }, 2575 2576 widget: function() { 2577 return this.menu.element; 2578 }, 2579 2580 _value: function() { 2581 return this.valueMethod.apply( this.element, arguments ); 2582 }, 2583 2584 _keyEvent: function( keyEvent, event ) { 2585 if ( !this.isMultiLine || this.menu.element.is( ":visible" ) ) { 2586 this._move( keyEvent, event ); 2587 2588 // Prevents moving cursor to beginning/end of the text field in some browsers 2589 event.preventDefault(); 2590 } 2591 }, 2592 2593 // Support: Chrome <=50 2594 // We should be able to just use this.element.prop( "isContentEditable" ) 2595 // but hidden elements always report false in Chrome. 2596 // https://code.google.com/p/chromium/issues/detail?id=313082 2597 _isContentEditable: function( element ) { 2598 if ( !element.length ) { 2599 return false; 2600 } 2601 2602 var editable = element.prop( "contentEditable" ); 2603 2604 if ( editable === "inherit" ) { 2605 return this._isContentEditable( element.parent() ); 2606 } 2607 2608 return editable === "true"; 2609 } 2610} ); 2611 2612$.extend( $.ui.autocomplete, { 2613 escapeRegex: function( value ) { 2614 return value.replace( /[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&" ); 2615 }, 2616 filter: function( array, term ) { 2617 var matcher = new RegExp( $.ui.autocomplete.escapeRegex( term ), "i" ); 2618 return $.grep( array, function( value ) { 2619 return matcher.test( value.label || value.value || value ); 2620 } ); 2621 } 2622} ); 2623 2624// Live region extension, adding a `messages` option 2625// NOTE: This is an experimental API. We are still investigating 2626// a full solution for string manipulation and internationalization. 2627$.widget( "ui.autocomplete", $.ui.autocomplete, { 2628 options: { 2629 messages: { 2630 noResults: "No search results.", 2631 results: function( amount ) { 2632 return amount + ( amount > 1 ? " results are" : " result is" ) + 2633 " available, use up and down arrow keys to navigate."; 2634 } 2635 } 2636 }, 2637 2638 __response: function( content ) { 2639 var message; 2640 this._superApply( arguments ); 2641 if ( this.options.disabled || this.cancelSearch ) { 2642 return; 2643 } 2644 if ( content && content.length ) { 2645 message = this.options.messages.results( content.length ); 2646 } else { 2647 message = this.options.messages.noResults; 2648 } 2649 this.liveRegion.children().hide(); 2650 $( "<div>" ).text( message ).appendTo( this.liveRegion ); 2651 } 2652} ); 2653 2654var widgetsAutocomplete = $.ui.autocomplete; 2655 2656 2657 2658 2659}));