PageSourceSearch

https://reliefseditions.com/wp-includes/js/jquery/ui/autocomplete.js?ver=1.13.2

js reliefseditions.com collected 2026-10-04 20:14:02 UTC 17,442 bytes, 674 lines download raw bytes

1/*!
2 * jQuery UI Autocomplete 1.13.2
3 * http://jqueryui.com
4 *
5 * Copyright jQuery Foundation and other contributors
6 * Released under the MIT license.
7 * http://jquery.org/license
8 */
9
10//>>label: Autocomplete
11//>>group: Widgets
12//>>description: Lists suggested words as the user is typing.
13//>>docs: http://api.jqueryui.com/autocomplete/
14//>>demos: http://jqueryui.com/autocomplete/
15//>>css.structure: ../../themes/base/core.css
16//>>css.structure: ../../themes/base/autocomplete.css
17//>>css.theme: ../../themes/base/theme.css
18
19( function( factory ) {
20	"use strict";
21
22	if ( typeof define === "function" && define.amd ) {
23
24		// AMD. Register as an anonymous module.
25		define( [
26			"jquery",
27			"./menu",
28			"./core"
29		], factory );
30	} else {
31
32		// Browser globals
33		factory( jQuery );
34	}
35} )( function( $ ) {
36"use strict";
37
38$.widget( "ui.autocomplete", {
39	version: "1.13.2
vendor: 16,539 bytes, lines 39-671
39",
40	defaultElement: "<input>",
41	options: {
42		appendTo: null,
43		autoFocus: false,
44		delay: 300,
45		minLength: 1,
46		position: {
47			my: "left top",
48			at: "left bottom",
49			collision: "none"
50		},
51		source: null,
52
53		// Callbacks
54		change: null,
55		close: null,
56		focus: null,
57		open: null,
58		response: null,
59		search: null,
60		select: null
61	},
62
63	requestIndex: 0,
64	pending: 0,
65	liveRegionTimer: null,
66
67	_create: function() {
68
69		// Some browsers only repeat keydown events, not keypress events,
70		// so we use the suppressKeyPress flag to determine if we've already
71		// handled the keydown event. #7269
72		// Unfortunately the code for & in keypress is the same as the up arrow,
73		// so we use the suppressKeyPressRepeat flag to avoid handling keypress
74		// events when we know the keydown event was used to modify the
75		// search term. #7799
76		var suppressKeyPress, suppressKeyPressRepeat, suppressInput,
77			nodeName = this.element[ 0 ].nodeName.toLowerCase(),
78			isTextarea = nodeName === "textarea",
79			isInput = nodeName === "input";
80
81		// Textareas are always multi-line
82		// Inputs are always single-line, even if inside a contentEditable element
83		// IE also treats inputs as contentEditable
84		// All other element types are determined by whether or not they're contentEditable
85		this.isMultiLine = isTextarea || !isInput && this._isContentEditable( this.element );
86
87		this.valueMethod = this.element[ isTextarea || isInput ? "val" : "text" ];
88		this.isNewMenu = true;
89
90		this._addClass( "ui-autocomplete-input" );
91		this.element.attr( "autocomplete", "off" );
92
93		this._on( this.element, {
94			keydown: function( event ) {
95				if ( this.element.prop( "readOnly" ) ) {
96					suppressKeyPress = true;
97					suppressInput = true;
98					suppressKeyPressRepeat = true;
99					return;
100				}
101
102				suppressKeyPress = false;
103				suppressInput = false;
104				suppressKeyPressRepeat = false;
105				var keyCode = $.ui.keyCode;
106				switch ( event.keyCode ) {
107				case keyCode.PAGE_UP:
108					suppressKeyPress = true;
109					this._move( "previousPage", event );
110					break;
111				case keyCode.PAGE_DOWN:
112					suppressKeyPress = true;
113					this._move( "nextPage", event );
114					break;
115				case keyCode.UP:
116					suppressKeyPress = true;
117					this._keyEvent( "previous", event );
118					break;
119				case keyCode.DOWN:
120					suppressKeyPress = true;
121					this._keyEvent( "next", event );
122					break;
123				case keyCode.ENTER:
124
125					// when menu is open and has focus
126					if ( this.menu.active ) {
127
128						// #6055 - Opera still allows the keypress to occur
129						// which causes forms to submit
130						suppressKeyPress = true;
131						event.preventDefault();
132						this.menu.select( event );
133					}
134					break;
135				case keyCode.TAB:
136					if ( this.menu.active ) {
137						this.menu.select( event );
138					}
139					break;
140				case keyCode.ESCAPE:
141					if ( this.menu.element.is( ":visible" ) ) {
142						if ( !this.isMultiLine ) {
143							this._value( this.term );
144						}
145						this.close( event );
146
147						// Different browsers have different default behavior for escape
148						// Single press can mean undo or clear
149						// Double press in IE means clear the whole form
150						event.preventDefault();
151					}
152					break;
153				default:
154					suppressKeyPressRepeat = true;
155
156					// search timeout should be triggered before the input value is changed
157					this._searchTimeout( event );
158					break;
159				}
160			},
161			keypress: function( event ) {
162				if ( suppressKeyPress ) {
163					suppressKeyPress = false;
164					if ( !this.isMultiLine || this.menu.element.is( ":visible" ) ) {
165						event.preventDefault();
166					}
167					return;
168				}
169				if ( suppressKeyPressRepeat ) {
170					return;
171				}
172
173				// Replicate some key handlers to allow them to repeat in Firefox and Opera
174				var keyCode = $.ui.keyCode;
175				switch ( event.keyCode ) {
176				case keyCode.PAGE_UP:
177					this._move( "previousPage", event );
178					break;
179				case keyCode.PAGE_DOWN:
180					this._move( "nextPage", event );
181					break;
182				case keyCode.UP:
183					this._keyEvent( "previous", event );
184					break;
185				case keyCode.DOWN:
186					this._keyEvent( "next", event );
187					break;
188				}
189			},
190			input: function( event ) {
191				if ( suppressInput ) {
192					suppressInput = false;
193					event.preventDefault();
194					return;
195				}
196				this._searchTimeout( event );
197			},
198			focus: function() {
199				this.selectedItem = null;
200				this.previous = this._value();
201			},
202			blur: function( event ) {
203				clearTimeout( this.searching );
204				this.close( event );
205				this._change( event );
206			}
207		} );
208
209		this._initSource();
210		this.menu = $( "<ul>" )
211			.appendTo( this._appendTo() )
212			.menu( {
213
214				// disable ARIA support, the live region takes care of that
215				role: null
216			} )
217			.hide()
218
219			// Support: IE 11 only, Edge <= 14
220			// For other browsers, we preventDefault() on the mousedown event
221			// to keep the dropdown from taking focus from the input. This doesn't
222			// work for IE/Edge, causing problems with selection and scrolling (#9638)
223			// Happily, IE and Edge support an "unselectable" attribute that
224			// prevents an element from receiving focus, exactly what we want here.
225			.attr( {
226				"unselectable": "on"
227			} )
228			.menu( "instance" );
229
230		this._addClass( this.menu.element, "ui-autocomplete", "ui-front" );
231		this._on( this.menu.element, {
232			mousedown: function( event ) {
233
234				// Prevent moving focus out of the text field
235				event.preventDefault();
236			},
237			menufocus: function( event, ui ) {
238				var label, item;
239
240				// support: Firefox
241				// Prevent accidental activation of menu items in Firefox (#7024 #9118)
242				if ( this.isNewMenu ) {
243					this.isNewMenu = false;
244					if ( event.originalEvent && /^mouse/.test( event.originalEvent.type ) ) {
245						this.menu.blur();
246
247						this.document.one( "mousemove", function() {
248							$( event.target ).trigger( event.originalEvent );
249						} );
250
251						return;
252					}
253				}
254
255				item = ui.item.data( "ui-autocomplete-item" );
256				if ( false !== this._trigger( "focus", event, { item: item } ) ) {
257
258					// use value to match what will end up in the input, if it was a key event
259					if ( event.originalEvent && /^key/.test( event.originalEvent.type ) ) {
260						this._value( item.value );
261					}
262				}
263
264				// Announce the value in the liveRegion
265				label = ui.item.attr( "aria-label" ) || item.value;
266				if ( label && String.prototype.trim.call( label ).length ) {
267					clearTimeout( this.liveRegionTimer );
268					this.liveRegionTimer = this._delay( function() {
269						this.liveRegion.html( $( "<div>" ).text( label ) );
270					}, 100 );
271				}
272			},
273			menuselect: function( event, ui ) {
274				var item = ui.item.data( "ui-autocomplete-item" ),
275					previous = this.previous;
276
277				// Only trigger when focus was lost (click on menu)
278				if ( this.element[ 0 ] !== $.ui.safeActiveElement( this.document[ 0 ] ) ) {
279					this.element.trigger( "focus" );
280					this.previous = previous;
281
282					// #6109 - IE triggers two focus events and the second
283					// is asynchronous, so we need to reset the previous
284					// term synchronously and asynchronously :-(
285					this._delay( function() {
286						this.previous = previous;
287						this.selectedItem = item;
288					} );
289				}
290
291				if ( false !== this._trigger( "select", event, { item: item } ) ) {
292					this._value( item.value );
293				}
294
295				// reset the term after the select event
296				// this allows custom select handling to work properly
297				this.term = this._value();
298
299				this.close( event );
300				this.selectedItem = item;
301			}
302		} );
303
304		this.liveRegion = $( "<div>", {
305			role: "status",
306			"aria-live": "assertive",
307			"aria-relevant": "additions"
308		} )
309			.appendTo( this.document[ 0 ].body );
310
311		this._addClass( this.liveRegion, null, "ui-helper-hidden-accessible" );
312
313		// Turning off autocomplete prevents the browser from remembering the
314		// value when navigating through history, so we re-enable autocomplete
315		// if the page is unloaded before the widget is destroyed. #7790
316		this._on( this.window, {
317			beforeunload: function() {
318				this.element.removeAttr( "autocomplete" );
319			}
320		} );
321	},
322
323	_destroy: function() {
324		clearTimeout( this.searching );
325		this.element.removeAttr( "autocomplete" );
326		this.menu.element.remove();
327		this.liveRegion.remove();
328	},
329
330	_setOption: function( key, value ) {
331		this._super( key, value );
332		if ( key === "source" ) {
333			this._initSource();
334		}
335		if ( key === "appendTo" ) {
336			this.menu.element.appendTo( this._appendTo() );
337		}
338		if ( key === "disabled" && value && this.xhr ) {
339			this.xhr.abort();
340		}
341	},
342
343	_isEventTargetInWidget: function( event ) {
344		var menuElement = this.menu.element[ 0 ];
345
346		return event.target === this.element[ 0 ] ||
347			event.target === menuElement ||
348			$.contains( menuElement, event.target );
349	},
350
351	_closeOnClickOutside: function( event ) {
352		if ( !this._isEventTargetInWidget( event ) ) {
353			this.close();
354		}
355	},
356
357	_appendTo: function() {
358		var element = this.options.appendTo;
359
360		if ( element ) {
361			element = element.jquery || element.nodeType ?
362				$( element ) :
363				this.document.find( element ).eq( 0 );
364		}
365
366		if ( !element || !element[ 0 ] ) {
367			element = this.element.closest( ".ui-front, dialog" );
368		}
369
370		if ( !element.length ) {
371			element = this.document[ 0 ].body;
372		}
373
374		return element;
375	},
376
377	_initSource: function() {
378		var array, url,
379			that = this;
380		if ( Array.isArray( this.options.source ) ) {
381			array = this.options.source;
382			this.source = function( request, response ) {
383				response( $.ui.autocomplete.filter( array, request.term ) );
384			};
385		} else if ( typeof this.options.source === "string" ) {
386			url = this.options.source;
387			this.source = function( request, response ) {
388				if ( that.xhr ) {
389					that.xhr.abort();
390				}
391				that.xhr = $.ajax( {
392					url: url,
393					data: request,
394					dataType: "json",
395					success: function( data ) {
396						response( data );
397					},
398					error: function() {
399						response( [] );
400					}
401				} );
402			};
403		} else {
404			this.source = this.options.source;
405		}
406	},
407
408	_searchTimeout: function( event ) {
409		clearTimeout( this.searching );
410		this.searching = this._delay( function() {
411
412			// Search if the value has changed, or if the user retypes the same value (see #7434)
413			var equalValues = this.term === this._value(),
414				menuVisible = this.menu.element.is( ":visible" ),
415				modifierKey = event.altKey || event.ctrlKey || event.metaKey || event.shiftKey;
416
417			if ( !equalValues || ( equalValues && !menuVisible && !modifierKey ) ) {
418				this.selectedItem = null;
419				this.search( null, event );
420			}
421		}, this.options.delay );
422	},
423
424	search: function( value, event ) {
425		value = value != null ? value : this._value();
426
427		// Always save the actual value, not the one passed as an argument
428		this.term = this._value();
429
430		if ( value.length < this.options.minLength ) {
431			return this.close( event );
432		}
433
434		if ( this._trigger( "search", event ) === false ) {
435			return;
436		}
437
438		return this._search( value );
439	},
440
441	_search: function( value ) {
442		this.pending++;
443		this._addClass( "ui-autocomplete-loading" );
444		this.cancelSearch = false;
445
446		this.source( { term: value }, this._response() );
447	},
448
449	_response: function() {
450		var index = ++this.requestIndex;
451
452		return function( content ) {
453			if ( index === this.requestIndex ) {
454				this.__response( content );
455			}
456
457			this.pending--;
458			if ( !this.pending ) {
459				this._removeClass( "ui-autocomplete-loading" );
460			}
461		}.bind( this );
462	},
463
464	__response: function( content ) {
465		if ( content ) {
466			content = this._normalize( content );
467		}
468		this._trigger( "response", null, { content: content } );
469		if ( !this.options.disabled && content && content.length && !this.cancelSearch ) {
470			this._suggest( content );
471			this._trigger( "open" );
472		} else {
473
474			// use ._close() instead of .close() so we don't cancel future searches
475			this._close();
476		}
477	},
478
479	close: function( event ) {
480		this.cancelSearch = true;
481		this._close( event );
482	},
483
484	_close: function( event ) {
485
486		// Remove the handler that closes the menu on outside clicks
487		this._off( this.document, "mousedown" );
488
489		if ( this.menu.element.is( ":visible" ) ) {
490			this.menu.element.hide();
491			this.menu.blur();
492			this.isNewMenu = true;
493			this._trigger( "close", event );
494		}
495	},
496
497	_change: function( event ) {
498		if ( this.previous !== this._value() ) {
499			this._trigger( "change", event, { item: this.selectedItem } );
500		}
501	},
502
503	_normalize: function( items ) {
504
505		// assume all items have the right format when the first item is complete
506		if ( items.length && items[ 0 ].label && items[ 0 ].value ) {
507			return items;
508		}
509		return $.map( items, function( item ) {
510			if ( typeof item === "string" ) {
511				return {
512					label: item,
513					value: item
514				};
515			}
516			return $.extend( {}, item, {
517				label: item.label || item.value,
518				value: item.value || item.label
519			} );
520		} );
521	},
522
523	_suggest: function( items ) {
524		var ul = this.menu.element.empty();
525		this._renderMenu( ul, items );
526		this.isNewMenu = true;
527		this.menu.refresh();
528
529		// Size and position menu
530		ul.show();
531		this._resizeMenu();
532		ul.position( $.extend( {
533			of: this.element
534		}, this.options.position ) );
535
536		if ( this.options.autoFocus ) {
537			this.menu.next();
538		}
539
540		// Listen for interactions outside of the widget (#6642)
541		this._on( this.document, {
542			mousedown: "_closeOnClickOutside"
543		} );
544	},
545
546	_resizeMenu: function() {
547		var ul = this.menu.element;
548		ul.outerWidth( Math.max(
549
550			// Firefox wraps long text (possibly a rounding bug)
551			// so we add 1px to avoid the wrapping (#7513)
552			ul.width( "" ).outerWidth() + 1,
553			this.element.outerWidth()
554		) );
555	},
556
557	_renderMenu: function( ul, items ) {
558		var that = this;
559		$.each( items, function( index, item ) {
560			that._renderItemData( ul, item );
561		} );
562	},
563
564	_renderItemData: function( ul, item ) {
565		return this._renderItem( ul, item ).data( "ui-autocomplete-item", item );
566	},
567
568	_renderItem: function( ul, item ) {
569		return $( "<li>" )
570			.append( $( "<div>" ).text( item.label ) )
571			.appendTo( ul );
572	},
573
574	_move: function( direction, event ) {
575		if ( !this.menu.element.is( ":visible" ) ) {
576			this.search( null, event );
577			return;
578		}
579		if ( this.menu.isFirstItem() && /^previous/.test( direction ) ||
580				this.menu.isLastItem() && /^next/.test( direction ) ) {
581
582			if ( !this.isMultiLine ) {
583				this._value( this.term );
584			}
585
586			this.menu.blur();
587			return;
588		}
589		this.menu[ direction ]( event );
590	},
591
592	widget: function() {
593		return this.menu.element;
594	},
595
596	_value: function() {
597		return this.valueMethod.apply( this.element, arguments );
598	},
599
600	_keyEvent: function( keyEvent, event ) {
601		if ( !this.isMultiLine || this.menu.element.is( ":visible" ) ) {
602			this._move( keyEvent, event );
603
604			// Prevents moving cursor to beginning/end of the text field in some browsers
605			event.preventDefault();
606		}
607	},
608
609	// Support: Chrome <=50
610	// We should be able to just use this.element.prop( "isContentEditable" )
611	// but hidden elements always report false in Chrome.
612	// https://code.google.com/p/chromium/issues/detail?id=313082
613	_isContentEditable: function( element ) {
614		if ( !element.length ) {
615			return false;
616		}
617
618		var editable = element.prop( "contentEditable" );
619
620		if ( editable === "inherit" ) {
621			return this._isContentEditable( element.parent() );
622		}
623
624		return editable === "true";
625	}
626} );
627
628$.extend( $.ui.autocomplete, {
629	escapeRegex: function( value ) {
630		return value.replace( /[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&" );
631	},
632	filter: function( array, term ) {
633		var matcher = new RegExp( $.ui.autocomplete.escapeRegex( term ), "i" );
634		return $.grep( array, function( value ) {
635			return matcher.test( value.label || value.value || value );
636		} );
637	}
638} );
639
640// Live region extension, adding a `messages` option
641// NOTE: This is an experimental API. We are still investigating
642// a full solution for string manipulation and internationalization.
643$.widget( "ui.autocomplete", $.ui.autocomplete, {
644	options: {
645		messages: {
646			noResults: "No search results.",
647			results: function( amount ) {
648				return amount + ( amount > 1 ? " results are" : " result is" ) +
649					" available, use up and down arrow keys to navigate.";
650			}
651		}
652	},
653
654	__response: function( content ) {
655		var message;
656		this._superApply( arguments );
657		if ( this.options.disabled || this.cancelSearch ) {
658			return;
659		}
660		if ( content && content.length ) {
661			message = this.options.messages.results( content.length );
662		} else {
663			message = this.options.messages.noResults;
664		}
665		clearTimeout( this.liveRegionTimer );
666		this.liveRegionTimer = this._delay( function() {
667			this.liveRegion.html( $( "<div>" ).text( message ) );
668		}, 100 );
669	}
670} );
671
672return $.ui.autocomplete;
673
674} );

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.