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