Subversion Repositories SmartDukaan

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
8842 anupam.sin 1
/*
2
 * jQuery UI Autocomplete 1.8.1
3
 *
4
 * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
5
 * Dual licensed under the MIT (MIT-LICENSE.txt)
6
 * and GPL (GPL-LICENSE.txt) licenses.
7
 *
8
 * http://docs.jquery.com/UI/Autocomplete
9
 *
10
 * Depends:
11
 *	jquery.ui.core.js
12
 *	jquery.ui.widget.js
13
 *	jquery.ui.position.js
14
 */
15
(function( $ ) {
16
 
17
$.widget( "ui.autocomplete", {
18
	options: {
19
		minLength: 1,
20
		delay: 300
21
	},
22
	_create: function() {
23
		var self = this,
24
			doc = this.element[ 0 ].ownerDocument;
25
		this.element
26
			.addClass( "ui-autocomplete-input" )
27
			.attr( "autocomplete", "off" )
28
			// TODO verify these actually work as intended
29
			.attr({
30
				role: "textbox",
31
				"aria-autocomplete": "list",
32
				"aria-haspopup": "true"
33
			})
34
			.bind( "keydown.autocomplete", function( event ) {
35
				var keyCode = $.ui.keyCode;
36
				switch( event.keyCode ) {
37
				case keyCode.PAGE_UP:
38
					self._move( "previousPage", event );
39
					break;
40
				case keyCode.PAGE_DOWN:
41
					self._move( "nextPage", event );
42
					break;
43
				case keyCode.UP:
44
					self._move( "previous", event );
45
					// prevent moving cursor to beginning of text field in some browsers
46
					event.preventDefault();
47
					break;
48
				case keyCode.DOWN:
49
					self._move( "next", event );
50
					// prevent moving cursor to end of text field in some browsers
51
					event.preventDefault();
52
					break;
53
				case keyCode.ENTER:
54
					// when menu is open or has focus
55
					if ( self.menu.active ) {
56
						event.preventDefault();
57
					}
58
					//passthrough - ENTER and TAB both select the current element
59
				case keyCode.TAB:
60
					if ( !self.menu.active ) {
61
						return;
62
					}
63
					self.menu.select( event );
64
					break;
65
				case keyCode.ESCAPE:
66
					self.element.val( self.term );
67
					self.close( event );
68
					break;
69
				case keyCode.LEFT:
70
				case keyCode.RIGHT:
71
				case keyCode.SHIFT:
72
				case keyCode.CONTROL:
73
				case keyCode.ALT:
74
					// ignore metakeys (shift, ctrl, alt)
75
					break;
76
				default:
77
					// keypress is triggered before the input value is changed
78
					clearTimeout( self.searching );
79
					self.searching = setTimeout(function() {
80
						self.search( null, event );
81
					}, self.options.delay );
82
					break;
83
				}
84
			})
85
			.bind( "focus.autocomplete", function() {
86
				self.selectedItem = null;
87
				self.previous = self.element.val();
88
			})
89
			.bind( "blur.autocomplete", function( event ) {
90
				clearTimeout( self.searching );
91
				// clicks on the menu (or a button to trigger a search) will cause a blur event
92
				// TODO try to implement this without a timeout, see clearTimeout in search()
93
				self.closing = setTimeout(function() {
94
					self.close( event );
95
					self._change( event );
96
				}, 150 );
97
			});
98
		this._initSource();
99
		this.response = function() {
100
			return self._response.apply( self, arguments );
101
		};
102
		this.menu = $( "<ul></ul>" )
103
			.addClass( "ui-autocomplete" )
104
			.appendTo( "body", doc )
105
			.menu({
106
				focus: function( event, ui ) {
107
					var item = ui.item.data( "item.autocomplete" );
108
					if ( false !== self._trigger( "focus", null, { item: item } ) ) {
109
						// use value to match what will end up in the input, if it was a key event
110
						if ( /^key/.test(event.originalEvent.type) ) {
111
							self.element.val( item.value );
112
						}
113
					}
114
				},
115
				selected: function( event, ui ) {
116
					var item = ui.item.data( "item.autocomplete" );
117
					if ( false !== self._trigger( "select", event, { item: item } ) ) {
118
						self.element.val( item.value );
119
					}
120
					self.close( event );
121
					// only trigger when focus was lost (click on menu)
122
					var previous = self.previous;
123
					if ( self.element[0] !== doc.activeElement ) {
124
						self.element.focus();
125
						self.previous = previous;
126
					}
127
					self.selectedItem = item;
128
				},
129
				blur: function( event, ui ) {
130
					if ( self.menu.element.is(":visible") ) {
131
						self.element.val( self.term );
132
					}
133
				}
134
			})
135
			.zIndex( this.element.zIndex() + 1 )
136
			// workaround for jQuery bug #5781 http://dev.jquery.com/ticket/5781
137
			.css({ top: 0, left: 0 })
138
			.hide()
139
			.data( "menu" );
140
		if ( $.fn.bgiframe ) {
141
			 this.menu.element.bgiframe();
142
		}
143
	},
144
 
145
	destroy: function() {
146
		this.element
147
			.removeClass( "ui-autocomplete-input" )
148
			.removeAttr( "autocomplete" )
149
			.removeAttr( "role" )
150
			.removeAttr( "aria-autocomplete" )
151
			.removeAttr( "aria-haspopup" );
152
		this.menu.element.remove();
153
		$.Widget.prototype.destroy.call( this );
154
	},
155
 
156
	_setOption: function( key ) {
157
		$.Widget.prototype._setOption.apply( this, arguments );
158
		if ( key === "source" ) {
159
			this._initSource();
160
		}
161
	},
162
 
163
	_initSource: function() {
164
		var array,
165
			url;
166
		if ( $.isArray(this.options.source) ) {
167
			array = this.options.source;
168
			this.source = function( request, response ) {
169
				response( $.ui.autocomplete.filter(array, request.term) );
170
			};
171
		} else if ( typeof this.options.source === "string" ) {
172
			url = this.options.source;
173
			this.source = function( request, response ) {
174
				$.getJSON( url, request, response );
175
			};
176
		} else {
177
			this.source = this.options.source;
178
		}
179
	},
180
 
181
	search: function( value, event ) {
182
		value = value != null ? value : this.element.val();
183
		if ( value.length < this.options.minLength ) {
184
			return this.close( event );
185
		}
186
 
187
		clearTimeout( this.closing );
188
		if ( this._trigger("search") === false ) {
189
			return;
190
		}
191
 
192
		return this._search( value );
193
	},
194
 
195
	_search: function( value ) {
196
		this.term = this.element
197
			.addClass( "ui-autocomplete-loading" )
198
			// always save the actual value, not the one passed as an argument
199
			.val();
200
 
201
		this.source( { term: value }, this.response );
202
	},
203
 
204
	_response: function( content ) {
205
		if ( content.length ) {
206
			content = this._normalize( content );
207
			this._suggest( content );
208
			this._trigger( "open" );
209
		} else {
210
			this.close();
211
		}
212
		this.element.removeClass( "ui-autocomplete-loading" );
213
	},
214
 
215
	close: function( event ) {
216
		clearTimeout( this.closing );
217
		if ( this.menu.element.is(":visible") ) {
218
			this._trigger( "close", event );
219
			this.menu.element.hide();
220
			this.menu.deactivate();
221
		}
222
	},
223
 
224
	_change: function( event ) {
225
		if ( this.previous !== this.element.val() ) {
226
			this._trigger( "change", event, { item: this.selectedItem } );
227
		}
228
	},
229
 
230
	_normalize: function( items ) {
231
		// assume all items have the right format when the first item is complete
232
		if ( items.length && items[0].label && items[0].value ) {
233
			return items;
234
		}
235
		return $.map( items, function(item) {
236
			if ( typeof item === "string" ) {
237
				return {
238
					label: item,
239
					value: item
240
				};
241
			}
242
			return $.extend({
243
				label: item.label || item.value,
244
				value: item.value || item.label
245
			}, item );
246
		});
247
	},
248
 
249
	_suggest: function( items ) {
250
		var ul = this.menu.element
251
				.empty()
252
				.zIndex( this.element.zIndex() + 1 ),
253
			menuWidth,
254
			textWidth;
255
		this._renderMenu( ul, items );
256
		// TODO refresh should check if the active item is still in the dom, removing the need for a manual deactivate
257
		this.menu.deactivate();
258
		this.menu.refresh();
259
		this.menu.element.show().position({
260
			my: "left top",
261
			at: "left bottom",
262
			of: this.element,
263
			collision: "none"
264
		});
265
 
266
		menuWidth = ul.width( "" ).width();
267
		textWidth = this.element.width();
268
		ul.width( Math.max( menuWidth, textWidth ) );
269
	},
270
 
271
	_renderMenu: function( ul, items ) {
272
		var self = this;
273
		$.each( items, function( index, item ) {
274
			self._renderItem( ul, item );
275
		});
276
	},
277
 
278
	_renderItem: function( ul, item) {
279
		return $( "<li></li>" )
280
			.data( "item.autocomplete", item )
281
			.append( "<a>" + item.label + "</a>" )
282
			.appendTo( ul );
283
	},
284
 
285
	_move: function( direction, event ) {
286
		if ( !this.menu.element.is(":visible") ) {
287
			this.search( null, event );
288
			return;
289
		}
290
		if ( this.menu.first() && /^previous/.test(direction) ||
291
				this.menu.last() && /^next/.test(direction) ) {
292
			this.element.val( this.term );
293
			this.menu.deactivate();
294
			return;
295
		}
296
		this.menu[ direction ]( event );
297
	},
298
 
299
	widget: function() {
300
		return this.menu.element;
301
	}
302
});
303
 
304
$.extend( $.ui.autocomplete, {
305
	escapeRegex: function( value ) {
306
		return value.replace( /([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi, "\\$1" );
307
	},
308
	filter: function(array, term) {
309
		var matcher = new RegExp( $.ui.autocomplete.escapeRegex(term), "i" );
310
		return $.grep( array, function(value) {
311
			return matcher.test( value.label || value.value || value );
312
		});
313
	}
314
});
315
 
316
}( jQuery ));
317
 
318
/*
319
 * jQuery UI Menu (not officially released)
320
 * 
321
 * This widget isn't yet finished and the API is subject to change. We plan to finish
322
 * it for the next release. You're welcome to give it a try anyway and give us feedback,
323
 * as long as you're okay with migrating your code later on. We can help with that, too.
324
 *
325
 * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
326
 * Dual licensed under the MIT (MIT-LICENSE.txt)
327
 * and GPL (GPL-LICENSE.txt) licenses.
328
 *
329
 * http://docs.jquery.com/UI/Menu
330
 *
331
 * Depends:
332
 *	jquery.ui.core.js
333
 *  jquery.ui.widget.js
334
 */
335
(function($) {
336
 
337
$.widget("ui.menu", {
338
	_create: function() {
339
		var self = this;
340
		this.element
341
			.addClass("ui-menu ui-widget ui-widget-content-compare ui-corner-all-compare")
342
			.attr({
343
				role: "listbox",
344
				"aria-activedescendant": "ui-active-menuitem"
345
			})
346
			.click(function( event ) {
347
				if ( !$( event.target ).closest( ".ui-menu-item a" ).length ) {
348
					return;
349
				}
350
				// temporary
351
				event.preventDefault();
352
				self.select( event );
353
			});
354
		this.refresh();
355
	},
356
 
357
	refresh: function() {
358
		var self = this;
359
 
360
		// don't refresh list items that are already adapted
361
		var items = this.element.children("li:not(.ui-menu-item):has(a)")
362
			.addClass("ui-menu-item")
363
			.attr("role", "menuitem");
364
 
365
		items.children("a")
366
			.addClass("ui-corner-all")
367
			.attr("tabindex", -1)
368
			// mouseenter doesn't work with event delegation
369
			.mouseenter(function( event ) {
370
				self.activate( event, $(this).parent() );
371
			})
372
			.mouseleave(function() {
373
				self.deactivate();
374
			});
375
	},
376
 
377
	activate: function( event, item ) {
378
		this.deactivate();
379
		if (this.hasScroll()) {
380
			var offset = item.offset().top - this.element.offset().top,
381
				scroll = this.element.attr("scrollTop"),
382
				elementHeight = this.element.height();
383
			if (offset < 0) {
384
				this.element.attr("scrollTop", scroll + offset);
385
			} else if (offset > elementHeight) {
386
				this.element.attr("scrollTop", scroll + offset - elementHeight + item.height());
387
			}
388
		}
389
		this.active = item.eq(0)
390
			.children("a")
391
				.addClass("ui-state-hover")
392
				.attr("id", "ui-active-menuitem")
393
			.end();
394
		this._trigger("focus", event, { item: item });
395
	},
396
 
397
	deactivate: function() {
398
		if (!this.active) { return; }
399
 
400
		this.active.children("a")
401
			.removeClass("ui-state-hover")
402
			.removeAttr("id");
403
		this._trigger("blur");
404
		this.active = null;
405
	},
406
 
407
	next: function(event) {
408
		this.move("next", ".ui-menu-item:first", event);
409
	},
410
 
411
	previous: function(event) {
412
		this.move("prev", ".ui-menu-item:last", event);
413
	},
414
 
415
	first: function() {
416
		return this.active && !this.active.prev().length;
417
	},
418
 
419
	last: function() {
420
		return this.active && !this.active.next().length;
421
	},
422
 
423
	move: function(direction, edge, event) {
424
		if (!this.active) {
425
			this.activate(event, this.element.children(edge));
426
			return;
427
		}
428
		var next = this.active[direction + "All"](".ui-menu-item").eq(0);
429
		if (next.length) {
430
			this.activate(event, next);
431
		} else {
432
			this.activate(event, this.element.children(edge));
433
		}
434
	},
435
 
436
	// TODO merge with previousPage
437
	nextPage: function(event) {
438
		if (this.hasScroll()) {
439
			// TODO merge with no-scroll-else
440
			if (!this.active || this.last()) {
441
				this.activate(event, this.element.children(":first"));
442
				return;
443
			}
444
			var base = this.active.offset().top,
445
				height = this.element.height(),
446
				result = this.element.children("li").filter(function() {
447
					var close = $(this).offset().top - base - height + $(this).height();
448
					// TODO improve approximation
449
					return close < 10 && close > -10;
450
				});
451
 
452
			// TODO try to catch this earlier when scrollTop indicates the last page anyway
453
			if (!result.length) {
454
				result = this.element.children(":last");
455
			}
456
			this.activate(event, result);
457
		} else {
458
			this.activate(event, this.element.children(!this.active || this.last() ? ":first" : ":last"));
459
		}
460
	},
461
 
462
	// TODO merge with nextPage
463
	previousPage: function(event) {
464
		if (this.hasScroll()) {
465
			// TODO merge with no-scroll-else
466
			if (!this.active || this.first()) {
467
				this.activate(event, this.element.children(":last"));
468
				return;
469
			}
470
 
471
			var base = this.active.offset().top,
472
				height = this.element.height();
473
				result = this.element.children("li").filter(function() {
474
					var close = $(this).offset().top - base + height - $(this).height();
475
					// TODO improve approximation
476
					return close < 10 && close > -10;
477
				});
478
 
479
			// TODO try to catch this earlier when scrollTop indicates the last page anyway
480
			if (!result.length) {
481
				result = this.element.children(":first");
482
			}
483
			this.activate(event, result);
484
		} else {
485
			this.activate(event, this.element.children(!this.active || this.first() ? ":last" : ":first"));
486
		}
487
	},
488
 
489
	hasScroll: function() {
490
		return this.element.height() < this.element.attr("scrollHeight");
491
	},
492
 
493
	select: function( event ) {
494
		this._trigger("selected", event, { item: this.active });
495
	}
496
});
497
 
498
}(jQuery));