source: main/trunk/greenstone3/web/interfaces/oran/js/jquery-ui-1.8rc1/ui/jquery.ui.autocomplete.js@ 24245

Last change on this file since 24245 was 24245, checked in by sjb48, 13 years ago

Oran code for supporting format changes to document.

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