Projet

Général

Profil

Révision 503b3f7b

Ajouté par Assos Assos il y a environ 10 ans

Weekly update of contrib modules

Voir les différences:

drupal7/sites/all/modules/jquery_update/replace/ui/ui/jquery.ui.autocomplete.js
1
/*
2
 * jQuery UI Autocomplete 1.8.11
1
/*!
2
 * jQuery UI Autocomplete 1.10.2
3
 * http://jqueryui.com
3 4
 *
4
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
5
 * Dual licensed under the MIT or GPL Version 2 licenses.
5
 * Copyright 2013 jQuery Foundation and other contributors
6
 * Released under the MIT license.
6 7
 * http://jquery.org/license
7 8
 *
8
 * http://docs.jquery.com/UI/Autocomplete
9
 * http://api.jqueryui.com/autocomplete/
9 10
 *
10 11
 * Depends:
11 12
 *	jquery.ui.core.js
12 13
 *	jquery.ui.widget.js
13 14
 *	jquery.ui.position.js
15
 *	jquery.ui.menu.js
14 16
 */
15 17
(function( $, undefined ) {
16 18

  
......
18 20
var requestIndex = 0;
19 21

  
20 22
$.widget( "ui.autocomplete", {
23
	version: "1.10.2",
24
	defaultElement: "<input>",
21 25
	options: {
22
		appendTo: "body",
26
		appendTo: null,
23 27
		autoFocus: false,
24 28
		delay: 300,
25 29
		minLength: 1,
......
28 32
			at: "left bottom",
29 33
			collision: "none"
30 34
		},
31
		source: null
35
		source: null,
36

  
37
		// callbacks
38
		change: null,
39
		close: null,
40
		focus: null,
41
		open: null,
42
		response: null,
43
		search: null,
44
		select: null
32 45
	},
33 46

  
34 47
	pending: 0,
35 48

  
36 49
	_create: function() {
37
		var self = this,
38
			doc = this.element[ 0 ].ownerDocument,
39
			suppressKeyPress;
50
		// Some browsers only repeat keydown events, not keypress events,
51
		// so we use the suppressKeyPress flag to determine if we've already
52
		// handled the keydown event. #7269
53
		// Unfortunately the code for & in keypress is the same as the up arrow,
54
		// so we use the suppressKeyPressRepeat flag to avoid handling keypress
55
		// events when we know the keydown event was used to modify the
56
		// search term. #7799
57
		var suppressKeyPress, suppressKeyPressRepeat, suppressInput,
58
			nodeName = this.element[0].nodeName.toLowerCase(),
59
			isTextarea = nodeName === "textarea",
60
			isInput = nodeName === "input";
61

  
62
		this.isMultiLine =
63
			// Textareas are always multi-line
64
			isTextarea ? true :
65
			// Inputs are always single-line, even if inside a contentEditable element
66
			// IE also treats inputs as contentEditable
67
			isInput ? false :
68
			// All other element types are determined by whether or not they're contentEditable
69
			this.element.prop( "isContentEditable" );
70

  
71
		this.valueMethod = this.element[ isTextarea || isInput ? "val" : "text" ];
72
		this.isNewMenu = true;
40 73

  
41 74
		this.element
42 75
			.addClass( "ui-autocomplete-input" )
43
			.attr( "autocomplete", "off" )
44
			// TODO verify these actually work as intended
45
			.attr({
46
				role: "textbox",
47
				"aria-autocomplete": "list",
48
				"aria-haspopup": "true"
49
			})
50
			.bind( "keydown.autocomplete", function( event ) {
51
				if ( self.options.disabled || self.element.attr( "readonly" ) ) {
76
			.attr( "autocomplete", "off" );
77

  
78
		this._on( this.element, {
79
			keydown: function( event ) {
80
				/*jshint maxcomplexity:15*/
81
				if ( this.element.prop( "readOnly" ) ) {
82
					suppressKeyPress = true;
83
					suppressInput = true;
84
					suppressKeyPressRepeat = true;
52 85
					return;
53 86
				}
54 87

  
55 88
				suppressKeyPress = false;
89
				suppressInput = false;
90
				suppressKeyPressRepeat = false;
56 91
				var keyCode = $.ui.keyCode;
57 92
				switch( event.keyCode ) {
58 93
				case keyCode.PAGE_UP:
59
					self._move( "previousPage", event );
94
					suppressKeyPress = true;
95
					this._move( "previousPage", event );
60 96
					break;
61 97
				case keyCode.PAGE_DOWN:
62
					self._move( "nextPage", event );
98
					suppressKeyPress = true;
99
					this._move( "nextPage", event );
63 100
					break;
64 101
				case keyCode.UP:
65
					self._move( "previous", event );
66
					// prevent moving cursor to beginning of text field in some browsers
67
					event.preventDefault();
102
					suppressKeyPress = true;
103
					this._keyEvent( "previous", event );
68 104
					break;
69 105
				case keyCode.DOWN:
70
					self._move( "next", event );
71
					// prevent moving cursor to end of text field in some browsers
72
					event.preventDefault();
106
					suppressKeyPress = true;
107
					this._keyEvent( "next", event );
73 108
					break;
74 109
				case keyCode.ENTER:
75 110
				case keyCode.NUMPAD_ENTER:
76 111
					// when menu is open and has focus
77
					if ( self.menu.active ) {
112
					if ( this.menu.active ) {
78 113
						// #6055 - Opera still allows the keypress to occur
79 114
						// which causes forms to submit
80 115
						suppressKeyPress = true;
81 116
						event.preventDefault();
117
						this.menu.select( event );
82 118
					}
83
					//passthrough - ENTER and TAB both select the current element
119
					break;
84 120
				case keyCode.TAB:
85
					if ( !self.menu.active ) {
86
						return;
121
					if ( this.menu.active ) {
122
						this.menu.select( event );
87 123
					}
88
					self.menu.select( event );
89 124
					break;
90 125
				case keyCode.ESCAPE:
91
					self.element.val( self.term );
92
					self.close( event );
126
					if ( this.menu.element.is( ":visible" ) ) {
127
						this._value( this.term );
128
						this.close( event );
129
						// Different browsers have different default behavior for escape
130
						// Single press can mean undo or clear
131
						// Double press in IE means clear the whole form
132
						event.preventDefault();
133
					}
93 134
					break;
94 135
				default:
95
					// keypress is triggered before the input value is changed
96
					clearTimeout( self.searching );
97
					self.searching = setTimeout(function() {
98
						// only search if the value has changed
99
						if ( self.term != self.element.val() ) {
100
							self.selectedItem = null;
101
							self.search( null, event );
102
						}
103
					}, self.options.delay );
136
					suppressKeyPressRepeat = true;
137
					// search timeout should be triggered before the input value is changed
138
					this._searchTimeout( event );
104 139
					break;
105 140
				}
106
			})
107
			.bind( "keypress.autocomplete", function( event ) {
141
			},
142
			keypress: function( event ) {
108 143
				if ( suppressKeyPress ) {
109 144
					suppressKeyPress = false;
110 145
					event.preventDefault();
146
					return;
111 147
				}
112
			})
113
			.bind( "focus.autocomplete", function() {
114
				if ( self.options.disabled ) {
148
				if ( suppressKeyPressRepeat ) {
115 149
					return;
116 150
				}
117 151

  
118
				self.selectedItem = null;
119
				self.previous = self.element.val();
120
			})
121
			.bind( "blur.autocomplete", function( event ) {
122
				if ( self.options.disabled ) {
152
				// replicate some key handlers to allow them to repeat in Firefox and Opera
153
				var keyCode = $.ui.keyCode;
154
				switch( event.keyCode ) {
155
				case keyCode.PAGE_UP:
156
					this._move( "previousPage", event );
157
					break;
158
				case keyCode.PAGE_DOWN:
159
					this._move( "nextPage", event );
160
					break;
161
				case keyCode.UP:
162
					this._keyEvent( "previous", event );
163
					break;
164
				case keyCode.DOWN:
165
					this._keyEvent( "next", event );
166
					break;
167
				}
168
			},
169
			input: function( event ) {
170
				if ( suppressInput ) {
171
					suppressInput = false;
172
					event.preventDefault();
173
					return;
174
				}
175
				this._searchTimeout( event );
176
			},
177
			focus: function() {
178
				this.selectedItem = null;
179
				this.previous = this._value();
180
			},
181
			blur: function( event ) {
182
				if ( this.cancelBlur ) {
183
					delete this.cancelBlur;
123 184
					return;
124 185
				}
125 186

  
126
				clearTimeout( self.searching );
127
				// clicks on the menu (or a button to trigger a search) will cause a blur event
128
				self.closing = setTimeout(function() {
129
					self.close( event );
130
					self._change( event );
131
				}, 150 );
132
			});
187
				clearTimeout( this.searching );
188
				this.close( event );
189
				this._change( event );
190
			}
191
		});
192

  
133 193
		this._initSource();
134
		this.response = function() {
135
			return self._response.apply( self, arguments );
136
		};
137
		this.menu = $( "<ul></ul>" )
138
			.addClass( "ui-autocomplete" )
139
			.appendTo( $( this.options.appendTo || "body", doc )[0] )
140
			// prevent the close-on-blur in case of a "slow" click on the menu (long mousedown)
141
			.mousedown(function( event ) {
194
		this.menu = $( "<ul>" )
195
			.addClass( "ui-autocomplete ui-front" )
196
			.appendTo( this._appendTo() )
197
			.menu({
198
				// custom key handling for now
199
				input: $(),
200
				// disable ARIA support, the live region takes care of that
201
				role: null
202
			})
203
			.hide()
204
			.data( "ui-menu" );
205

  
206
		this._on( this.menu.element, {
207
			mousedown: function( event ) {
208
				// prevent moving focus out of the text field
209
				event.preventDefault();
210

  
211
				// IE doesn't prevent moving focus even with event.preventDefault()
212
				// so we set a flag to know when we should ignore the blur event
213
				this.cancelBlur = true;
214
				this._delay(function() {
215
					delete this.cancelBlur;
216
				});
217

  
142 218
				// clicking on the scrollbar causes focus to shift to the body
143 219
				// but we can't detect a mouseup or a click immediately afterward
144 220
				// so we have to track the next mousedown and close the menu if
145 221
				// the user clicks somewhere outside of the autocomplete
146
				var menuElement = self.menu.element[ 0 ];
222
				var menuElement = this.menu.element[ 0 ];
147 223
				if ( !$( event.target ).closest( ".ui-menu-item" ).length ) {
148
					setTimeout(function() {
149
						$( document ).one( 'mousedown', function( event ) {
150
							if ( event.target !== self.element[ 0 ] &&
151
								event.target !== menuElement &&
152
								!$.ui.contains( menuElement, event.target ) ) {
153
								self.close();
224
					this._delay(function() {
225
						var that = this;
226
						this.document.one( "mousedown", function( event ) {
227
							if ( event.target !== that.element[ 0 ] &&
228
									event.target !== menuElement &&
229
									!$.contains( menuElement, event.target ) ) {
230
								that.close();
154 231
							}
155 232
						});
156
					}, 1 );
233
					});
157 234
				}
235
			},
236
			menufocus: function( event, ui ) {
237
				// support: Firefox
238
				// Prevent accidental activation of menu items in Firefox (#7024 #9118)
239
				if ( this.isNewMenu ) {
240
					this.isNewMenu = false;
241
					if ( event.originalEvent && /^mouse/.test( event.originalEvent.type ) ) {
242
						this.menu.blur();
243

  
244
						this.document.one( "mousemove", function() {
245
							$( event.target ).trigger( event.originalEvent );
246
						});
158 247

  
159
				// use another timeout to make sure the blur-event-handler on the input was already triggered
160
				setTimeout(function() {
161
					clearTimeout( self.closing );
162
				}, 13);
163
			})
164
			.menu({
165
				focus: function( event, ui ) {
166
					var item = ui.item.data( "item.autocomplete" );
167
					if ( false !== self._trigger( "focus", event, { item: item } ) ) {
168
						// use value to match what will end up in the input, if it was a key event
169
						if ( /^key/.test(event.originalEvent.type) ) {
170
							self.element.val( item.value );
171
						}
172
					}
173
				},
174
				selected: function( event, ui ) {
175
					var item = ui.item.data( "item.autocomplete" ),
176
						previous = self.previous;
177

  
178
					// only trigger when focus was lost (click on menu)
179
					if ( self.element[0] !== doc.activeElement ) {
180
						self.element.focus();
181
						self.previous = previous;
182
						// #6109 - IE triggers two focus events and the second
183
						// is asynchronous, so we need to reset the previous
184
						// term synchronously and asynchronously :-(
185
						setTimeout(function() {
186
							self.previous = previous;
187
							self.selectedItem = item;
188
						}, 1);
248
						return;
189 249
					}
250
				}
190 251

  
191
					if ( false !== self._trigger( "select", event, { item: item } ) ) {
192
						self.element.val( item.value );
193
					}
194
					// reset the term after the select event
195
					// this allows custom select handling to work properly
196
					self.term = self.element.val();
197

  
198
					self.close( event );
199
					self.selectedItem = item;
200
				},
201
				blur: function( event, ui ) {
202
					// don't set the value of the text field if it's already correct
203
					// this prevents moving the cursor unnecessarily
204
					if ( self.menu.element.is(":visible") &&
205
						( self.element.val() !== self.term ) ) {
206
						self.element.val( self.term );
252
				var item = ui.item.data( "ui-autocomplete-item" );
253
				if ( false !== this._trigger( "focus", event, { item: item } ) ) {
254
					// use value to match what will end up in the input, if it was a key event
255
					if ( event.originalEvent && /^key/.test( event.originalEvent.type ) ) {
256
						this._value( item.value );
207 257
					}
258
				} else {
259
					// Normally the input is populated with the item's value as the
260
					// menu is navigated, causing screen readers to notice a change and
261
					// announce the item. Since the focus event was canceled, this doesn't
262
					// happen, so we update the live region so that screen readers can
263
					// still notice the change and announce it.
264
					this.liveRegion.text( item.value );
208 265
				}
266
			},
267
			menuselect: function( event, ui ) {
268
				var item = ui.item.data( "ui-autocomplete-item" ),
269
					previous = this.previous;
270

  
271
				// only trigger when focus was lost (click on menu)
272
				if ( this.element[0] !== this.document[0].activeElement ) {
273
					this.element.focus();
274
					this.previous = previous;
275
					// #6109 - IE triggers two focus events and the second
276
					// is asynchronous, so we need to reset the previous
277
					// term synchronously and asynchronously :-(
278
					this._delay(function() {
279
						this.previous = previous;
280
						this.selectedItem = item;
281
					});
282
				}
283

  
284
				if ( false !== this._trigger( "select", event, { item: item } ) ) {
285
					this._value( item.value );
286
				}
287
				// reset the term after the select event
288
				// this allows custom select handling to work properly
289
				this.term = this._value();
290

  
291
				this.close( event );
292
				this.selectedItem = item;
293
			}
294
		});
295

  
296
		this.liveRegion = $( "<span>", {
297
				role: "status",
298
				"aria-live": "polite"
209 299
			})
210
			.zIndex( this.element.zIndex() + 1 )
211
			// workaround for jQuery bug #5781 http://dev.jquery.com/ticket/5781
212
			.css({ top: 0, left: 0 })
213
			.hide()
214
			.data( "menu" );
215
		if ( $.fn.bgiframe ) {
216
			 this.menu.element.bgiframe();
217
		}
300
			.addClass( "ui-helper-hidden-accessible" )
301
			.insertAfter( this.element );
302

  
303
		// turning off autocomplete prevents the browser from remembering the
304
		// value when navigating through history, so we re-enable autocomplete
305
		// if the page is unloaded before the widget is destroyed. #7790
306
		this._on( this.window, {
307
			beforeunload: function() {
308
				this.element.removeAttr( "autocomplete" );
309
			}
310
		});
218 311
	},
219 312

  
220
	destroy: function() {
313
	_destroy: function() {
314
		clearTimeout( this.searching );
221 315
		this.element
222 316
			.removeClass( "ui-autocomplete-input" )
223
			.removeAttr( "autocomplete" )
224
			.removeAttr( "role" )
225
			.removeAttr( "aria-autocomplete" )
226
			.removeAttr( "aria-haspopup" );
317
			.removeAttr( "autocomplete" );
227 318
		this.menu.element.remove();
228
		$.Widget.prototype.destroy.call( this );
319
		this.liveRegion.remove();
229 320
	},
230 321

  
231 322
	_setOption: function( key, value ) {
232
		$.Widget.prototype._setOption.apply( this, arguments );
323
		this._super( key, value );
233 324
		if ( key === "source" ) {
234 325
			this._initSource();
235 326
		}
236 327
		if ( key === "appendTo" ) {
237
			this.menu.element.appendTo( $( value || "body", this.element[0].ownerDocument )[0] )
328
			this.menu.element.appendTo( this._appendTo() );
238 329
		}
239 330
		if ( key === "disabled" && value && this.xhr ) {
240 331
			this.xhr.abort();
241 332
		}
242 333
	},
243 334

  
335
	_appendTo: function() {
336
		var element = this.options.appendTo;
337

  
338
		if ( element ) {
339
			element = element.jquery || element.nodeType ?
340
				$( element ) :
341
				this.document.find( element ).eq( 0 );
342
		}
343

  
344
		if ( !element ) {
345
			element = this.element.closest( ".ui-front" );
346
		}
347

  
348
		if ( !element.length ) {
349
			element = this.document[0].body;
350
		}
351

  
352
		return element;
353
	},
354

  
244 355
	_initSource: function() {
245
		var self = this,
246
			array,
247
			url;
356
		var array, url,
357
			that = this;
248 358
		if ( $.isArray(this.options.source) ) {
249 359
			array = this.options.source;
250 360
			this.source = function( request, response ) {
251
				response( $.ui.autocomplete.filter(array, request.term) );
361
				response( $.ui.autocomplete.filter( array, request.term ) );
252 362
			};
253 363
		} else if ( typeof this.options.source === "string" ) {
254 364
			url = this.options.source;
255 365
			this.source = function( request, response ) {
256
				if ( self.xhr ) {
257
					self.xhr.abort();
366
				if ( that.xhr ) {
367
					that.xhr.abort();
258 368
				}
259
				self.xhr = $.ajax({
369
				that.xhr = $.ajax({
260 370
					url: url,
261 371
					data: request,
262 372
					dataType: "json",
263
					autocompleteRequest: ++requestIndex,
264
					success: function( data, status ) {
265
						if ( this.autocompleteRequest === requestIndex ) {
266
							response( data );
267
						}
373
					success: function( data ) {
374
						response( data );
268 375
					},
269 376
					error: function() {
270
						if ( this.autocompleteRequest === requestIndex ) {
271
							response( [] );
272
						}
377
						response( [] );
273 378
					}
274 379
				});
275 380
			};
......
278 383
		}
279 384
	},
280 385

  
386
	_searchTimeout: function( event ) {
387
		clearTimeout( this.searching );
388
		this.searching = this._delay(function() {
389
			// only search if the value has changed
390
			if ( this.term !== this._value() ) {
391
				this.selectedItem = null;
392
				this.search( null, event );
393
			}
394
		}, this.options.delay );
395
	},
396

  
281 397
	search: function( value, event ) {
282
		value = value != null ? value : this.element.val();
398
		value = value != null ? value : this._value();
283 399

  
284 400
		// always save the actual value, not the one passed as an argument
285
		this.term = this.element.val();
401
		this.term = this._value();
286 402

  
287 403
		if ( value.length < this.options.minLength ) {
288 404
			return this.close( event );
289 405
		}
290 406

  
291
		clearTimeout( this.closing );
292 407
		if ( this._trigger( "search", event ) === false ) {
293 408
			return;
294 409
		}
......
299 414
	_search: function( value ) {
300 415
		this.pending++;
301 416
		this.element.addClass( "ui-autocomplete-loading" );
417
		this.cancelSearch = false;
418

  
419
		this.source( { term: value }, this._response() );
420
	},
421

  
422
	_response: function() {
423
		var that = this,
424
			index = ++requestIndex;
302 425

  
303
		this.source( { term: value }, this.response );
426
		return function( content ) {
427
			if ( index === requestIndex ) {
428
				that.__response( content );
429
			}
430

  
431
			that.pending--;
432
			if ( !that.pending ) {
433
				that.element.removeClass( "ui-autocomplete-loading" );
434
			}
435
		};
304 436
	},
305 437

  
306
	_response: function( content ) {
307
		if ( !this.options.disabled && content && content.length ) {
438
	__response: function( content ) {
439
		if ( content ) {
308 440
			content = this._normalize( content );
441
		}
442
		this._trigger( "response", null, { content: content } );
443
		if ( !this.options.disabled && content && content.length && !this.cancelSearch ) {
309 444
			this._suggest( content );
310 445
			this._trigger( "open" );
311 446
		} else {
312
			this.close();
313
		}
314
		this.pending--;
315
		if ( !this.pending ) {
316
			this.element.removeClass( "ui-autocomplete-loading" );
447
			// use ._close() instead of .close() so we don't cancel future searches
448
			this._close();
317 449
		}
318 450
	},
319 451

  
320 452
	close: function( event ) {
321
		clearTimeout( this.closing );
322
		if ( this.menu.element.is(":visible") ) {
453
		this.cancelSearch = true;
454
		this._close( event );
455
	},
456

  
457
	_close: function( event ) {
458
		if ( this.menu.element.is( ":visible" ) ) {
323 459
			this.menu.element.hide();
324
			this.menu.deactivate();
460
			this.menu.blur();
461
			this.isNewMenu = true;
325 462
			this._trigger( "close", event );
326 463
		}
327 464
	},
328
	
465

  
329 466
	_change: function( event ) {
330
		if ( this.previous !== this.element.val() ) {
467
		if ( this.previous !== this._value() ) {
331 468
			this._trigger( "change", event, { item: this.selectedItem } );
332 469
		}
333 470
	},
......
337 474
		if ( items.length && items[0].label && items[0].value ) {
338 475
			return items;
339 476
		}
340
		return $.map( items, function(item) {
477
		return $.map( items, function( item ) {
341 478
			if ( typeof item === "string" ) {
342 479
				return {
343 480
					label: item,
......
352 489
	},
353 490

  
354 491
	_suggest: function( items ) {
355
		var ul = this.menu.element
356
			.empty()
357
			.zIndex( this.element.zIndex() + 1 );
492
		var ul = this.menu.element.empty();
358 493
		this._renderMenu( ul, items );
359
		// TODO refresh should check if the active item is still in the dom, removing the need for a manual deactivate
360
		this.menu.deactivate();
494
		this.isNewMenu = true;
361 495
		this.menu.refresh();
362 496

  
363 497
		// size and position menu
......
368 502
		}, this.options.position ));
369 503

  
370 504
		if ( this.options.autoFocus ) {
371
			this.menu.next( new $.Event("mouseover") );
505
			this.menu.next();
372 506
		}
373 507
	},
374 508

  
375 509
	_resizeMenu: function() {
376 510
		var ul = this.menu.element;
377 511
		ul.outerWidth( Math.max(
378
			ul.width( "" ).outerWidth(),
512
			// Firefox wraps long text (possibly a rounding bug)
513
			// so we add 1px to avoid the wrapping (#7513)
514
			ul.width( "" ).outerWidth() + 1,
379 515
			this.element.outerWidth()
380 516
		) );
381 517
	},
382 518

  
383 519
	_renderMenu: function( ul, items ) {
384
		var self = this;
520
		var that = this;
385 521
		$.each( items, function( index, item ) {
386
			self._renderItem( ul, item );
522
			that._renderItemData( ul, item );
387 523
		});
388 524
	},
389 525

  
390
	_renderItem: function( ul, item) {
391
		return $( "<li></li>" )
392
			.data( "item.autocomplete", item )
393
			.append( $( "<a></a>" ).text( item.label ) )
526
	_renderItemData: function( ul, item ) {
527
		return this._renderItem( ul, item ).data( "ui-autocomplete-item", item );
528
	},
529

  
530
	_renderItem: function( ul, item ) {
531
		return $( "<li>" )
532
			.append( $( "<a>" ).text( item.label ) )
394 533
			.appendTo( ul );
395 534
	},
396 535

  
397 536
	_move: function( direction, event ) {
398
		if ( !this.menu.element.is(":visible") ) {
537
		if ( !this.menu.element.is( ":visible" ) ) {
399 538
			this.search( null, event );
400 539
			return;
401 540
		}
402
		if ( this.menu.first() && /^previous/.test(direction) ||
403
				this.menu.last() && /^next/.test(direction) ) {
404
			this.element.val( this.term );
405
			this.menu.deactivate();
541
		if ( this.menu.isFirstItem() && /^previous/.test( direction ) ||
542
				this.menu.isLastItem() && /^next/.test( direction ) ) {
543
			this._value( this.term );
544
			this.menu.blur();
406 545
			return;
407 546
		}
408 547
		this.menu[ direction ]( event );
......
410 549

  
411 550
	widget: function() {
412 551
		return this.menu.element;
552
	},
553

  
554
	_value: function() {
555
		return this.valueMethod.apply( this.element, arguments );
556
	},
557

  
558
	_keyEvent: function( keyEvent, event ) {
559
		if ( !this.isMultiLine || this.menu.element.is( ":visible" ) ) {
560
			this._move( keyEvent, event );
561

  
562
			// prevents moving cursor to beginning/end of the text field in some browsers
563
			event.preventDefault();
564
		}
413 565
	}
414 566
});
415 567

  
416 568
$.extend( $.ui.autocomplete, {
417 569
	escapeRegex: function( value ) {
418
		return value.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
570
		return value.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&");
419 571
	},
420 572
	filter: function(array, term) {
421 573
		var matcher = new RegExp( $.ui.autocomplete.escapeRegex(term), "i" );
......
425 577
	}
426 578
});
427 579

  
428
}( jQuery ));
429

  
430
/*
431
 * jQuery UI Menu (not officially released)
432
 * 
433
 * This widget isn't yet finished and the API is subject to change. We plan to finish
434
 * it for the next release. You're welcome to give it a try anyway and give us feedback,
435
 * as long as you're okay with migrating your code later on. We can help with that, too.
436
 *
437
 * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
438
 * Dual licensed under the MIT or GPL Version 2 licenses.
439
 * http://jquery.org/license
440
 *
441
 * http://docs.jquery.com/UI/Menu
442
 *
443
 * Depends:
444
 *	jquery.ui.core.js
445
 *  jquery.ui.widget.js
446
 */
447
(function($) {
448

  
449
$.widget("ui.menu", {
450
	_create: function() {
451
		var self = this;
452
		this.element
453
			.addClass("ui-menu ui-widget ui-widget-content ui-corner-all")
454
			.attr({
455
				role: "listbox",
456
				"aria-activedescendant": "ui-active-menuitem"
457
			})
458
			.click(function( event ) {
459
				if ( !$( event.target ).closest( ".ui-menu-item a" ).length ) {
460
					return;
461
				}
462
				// temporary
463
				event.preventDefault();
464
				self.select( event );
465
			});
466
		this.refresh();
467
	},
468
	
469
	refresh: function() {
470
		var self = this;
471

  
472
		// don't refresh list items that are already adapted
473
		var items = this.element.children("li:not(.ui-menu-item):has(a)")
474
			.addClass("ui-menu-item")
475
			.attr("role", "menuitem");
476
		
477
		items.children("a")
478
			.addClass("ui-corner-all")
479
			.attr("tabindex", -1)
480
			// mouseenter doesn't work with event delegation
481
			.mouseenter(function( event ) {
482
				self.activate( event, $(this).parent() );
483
			})
484
			.mouseleave(function() {
485
				self.deactivate();
486
			});
487
	},
488 580

  
489
	activate: function( event, item ) {
490
		this.deactivate();
491
		if (this.hasScroll()) {
492
			var offset = item.offset().top - this.element.offset().top,
493
				scroll = this.element.attr("scrollTop"),
494
				elementHeight = this.element.height();
495
			if (offset < 0) {
496
				this.element.attr("scrollTop", scroll + offset);
497
			} else if (offset >= elementHeight) {
498
				this.element.attr("scrollTop", scroll + offset - elementHeight + item.height());
581
// live region extension, adding a `messages` option
582
// NOTE: This is an experimental API. We are still investigating
583
// a full solution for string manipulation and internationalization.
584
$.widget( "ui.autocomplete", $.ui.autocomplete, {
585
	options: {
586
		messages: {
587
			noResults: "No search results.",
588
			results: function( amount ) {
589
				return amount + ( amount > 1 ? " results are" : " result is" ) +
590
					" available, use up and down arrow keys to navigate.";
499 591
			}
500 592
		}
501
		this.active = item.eq(0)
502
			.children("a")
503
				.addClass("ui-state-hover")
504
				.attr("id", "ui-active-menuitem")
505
			.end();
506
		this._trigger("focus", event, { item: item });
507
	},
508

  
509
	deactivate: function() {
510
		if (!this.active) { return; }
511

  
512
		this.active.children("a")
513
			.removeClass("ui-state-hover")
514
			.removeAttr("id");
515
		this._trigger("blur");
516
		this.active = null;
517
	},
518

  
519
	next: function(event) {
520
		this.move("next", ".ui-menu-item:first", event);
521
	},
522

  
523
	previous: function(event) {
524
		this.move("prev", ".ui-menu-item:last", event);
525 593
	},
526 594

  
527
	first: function() {
528
		return this.active && !this.active.prevAll(".ui-menu-item").length;
529
	},
530

  
531
	last: function() {
532
		return this.active && !this.active.nextAll(".ui-menu-item").length;
533
	},
534

  
535
	move: function(direction, edge, event) {
536
		if (!this.active) {
537
			this.activate(event, this.element.children(edge));
595
	__response: function( content ) {
596
		var message;
597
		this._superApply( arguments );
598
		if ( this.options.disabled || this.cancelSearch ) {
538 599
			return;
539 600
		}
540
		var next = this.active[direction + "All"](".ui-menu-item").eq(0);
541
		if (next.length) {
542
			this.activate(event, next);
601
		if ( content && content.length ) {
602
			message = this.options.messages.results( content.length );
543 603
		} else {
544
			this.activate(event, this.element.children(edge));
604
			message = this.options.messages.noResults;
545 605
		}
546
	},
547

  
548
	// TODO merge with previousPage
549
	nextPage: function(event) {
550
		if (this.hasScroll()) {
551
			// TODO merge with no-scroll-else
552
			if (!this.active || this.last()) {
553
				this.activate(event, this.element.children(".ui-menu-item:first"));
554
				return;
555
			}
556
			var base = this.active.offset().top,
557
				height = this.element.height(),
558
				result = this.element.children(".ui-menu-item").filter(function() {
559
					var close = $(this).offset().top - base - height + $(this).height();
560
					// TODO improve approximation
561
					return close < 10 && close > -10;
562
				});
563

  
564
			// TODO try to catch this earlier when scrollTop indicates the last page anyway
565
			if (!result.length) {
566
				result = this.element.children(".ui-menu-item:last");
567
			}
568
			this.activate(event, result);
569
		} else {
570
			this.activate(event, this.element.children(".ui-menu-item")
571
				.filter(!this.active || this.last() ? ":first" : ":last"));
572
		}
573
	},
574

  
575
	// TODO merge with nextPage
576
	previousPage: function(event) {
577
		if (this.hasScroll()) {
578
			// TODO merge with no-scroll-else
579
			if (!this.active || this.first()) {
580
				this.activate(event, this.element.children(".ui-menu-item:last"));
581
				return;
582
			}
583

  
584
			var base = this.active.offset().top,
585
				height = this.element.height();
586
				result = this.element.children(".ui-menu-item").filter(function() {
587
					var close = $(this).offset().top - base + height - $(this).height();
588
					// TODO improve approximation
589
					return close < 10 && close > -10;
590
				});
591

  
592
			// TODO try to catch this earlier when scrollTop indicates the last page anyway
593
			if (!result.length) {
594
				result = this.element.children(".ui-menu-item:first");
595
			}
596
			this.activate(event, result);
597
		} else {
598
			this.activate(event, this.element.children(".ui-menu-item")
599
				.filter(!this.active || this.first() ? ":last" : ":first"));
600
		}
601
	},
602

  
603
	hasScroll: function() {
604
		return this.element.height() < this.element.attr("scrollHeight");
605
	},
606

  
607
	select: function( event ) {
608
		this._trigger("selected", event, { item: this.active });
606
		this.liveRegion.text( message );
609 607
	}
610 608
});
611 609

  
612
}(jQuery));
610
}( jQuery ));

Formats disponibles : Unified diff