|
|
@@ -11,57 +11,57 @@ |
|
|
* jquery.ui.core.js |
|
|
* jquery.ui.widget.js |
|
|
*/ |
|
|
(function($) { |
|
|
(function( $ ) { |
|
|
|
|
|
$.widget("ui.autocomplete", { |
|
|
$.widget( "ui.autocomplete", { |
|
|
options: { |
|
|
minLength: 1, |
|
|
delay: 300 |
|
|
}, |
|
|
_create: function() { |
|
|
var self = this; |
|
|
this.element |
|
|
.addClass("ui-autocomplete ui-widget ui-widget-content ui-corner-all") |
|
|
.attr("autocomplete", "off") |
|
|
.addClass( "ui-autocomplete ui-widget ui-widget-content ui-corner-all" ) |
|
|
.attr( "autocomplete", "off" ) |
|
|
// TODO verify these actually work as intended |
|
|
.attr({ |
|
|
role: "textbox", |
|
|
"aria-autocomplete": "list", |
|
|
"aria-haspopup": "true" |
|
|
}) |
|
|
.bind("keydown.autocomplete", function(event) { |
|
|
.bind( "keydown.autocomplete", function( event ) { |
|
|
var keyCode = $.ui.keyCode; |
|
|
switch(event.keyCode) { |
|
|
switch( event.keyCode ) { |
|
|
case keyCode.PAGE_UP: |
|
|
self._move("previousPage", event); |
|
|
self._move( "previousPage", event ); |
|
|
break; |
|
|
case keyCode.PAGE_DOWN: |
|
|
self._move("nextPage", event); |
|
|
self._move( "nextPage", event ); |
|
|
break; |
|
|
case keyCode.UP: |
|
|
self._move("previous", event); |
|
|
self._move( "previous", event ); |
|
|
// prevent moving cursor to beginning of text field in some browsers |
|
|
event.preventDefault(); |
|
|
break; |
|
|
case keyCode.DOWN: |
|
|
self._move("next", event); |
|
|
self._move( "next", event ); |
|
|
// prevent moving cursor to end of text field in some browsers |
|
|
event.preventDefault(); |
|
|
break; |
|
|
case keyCode.ENTER: |
|
|
// when menu is open or has focus |
|
|
if (self.menu && self.menu.active) { |
|
|
if ( self.menu && self.menu.active ) { |
|
|
event.preventDefault(); |
|
|
} |
|
|
case keyCode.TAB: |
|
|
if (!self.menu || !self.menu.active) { |
|
|
if ( !self.menu || !self.menu.active ) { |
|
|
return; |
|
|
} |
|
|
self.menu.select(); |
|
|
break; |
|
|
case keyCode.ESCAPE: |
|
|
self.element.val(self.term); |
|
|
self.close(event); |
|
|
self.element.val( self.term ); |
|
|
self.close( event ); |
|
|
break; |
|
|
case 16: |
|
|
case 17: |
|
|
@@ -70,123 +70,123 @@ $.widget("ui.autocomplete", { |
|
|
break; |
|
|
default: |
|
|
// keypress is triggered before the input value is changed |
|
|
clearTimeout(self.searching); |
|
|
clearTimeout( self.searching ); |
|
|
self.searching = setTimeout(function() { |
|
|
self.search(null, event); |
|
|
}, self.options.delay); |
|
|
self.search( null, event ); |
|
|
}, self.options.delay ); |
|
|
break; |
|
|
} |
|
|
}) |
|
|
.bind("focus.autocomplete", function() { |
|
|
.bind( "focus.autocomplete", function() { |
|
|
self.previous = self.element.val(); |
|
|
}) |
|
|
.bind("blur.autocomplete", function(event) { |
|
|
clearTimeout(self.searching); |
|
|
.bind( "blur.autocomplete", function( event ) { |
|
|
clearTimeout( self.searching ); |
|
|
// clicks on the menu (or a button to trigger a search) will cause a blur event |
|
|
// TODO try to implement this without a timeout, see clearTimeout in search() |
|
|
self.closing = setTimeout(function() { |
|
|
self.close(event); |
|
|
}, 150); |
|
|
self.close( event ); |
|
|
}, 150 ); |
|
|
}); |
|
|
this._initSource(); |
|
|
this.response = function() { |
|
|
return self._response.apply(self, arguments); |
|
|
return self._response.apply( self, arguments ); |
|
|
}; |
|
|
}, |
|
|
|
|
|
destroy: function() { |
|
|
this.element |
|
|
.removeClass("ui-autocomplete ui-widget ui-widget-content ui-corner-all") |
|
|
.removeAttr("autocomplete") |
|
|
.removeAttr("role") |
|
|
.removeAttr("aria-autocomplete") |
|
|
.removeAttr("aria-haspopup"); |
|
|
if (this.menu) { |
|
|
.removeClass( "ui-autocomplete ui-widget ui-widget-content ui-corner-all" ) |
|
|
.removeAttr( "autocomplete" ) |
|
|
.removeAttr( "role" ) |
|
|
.removeAttr( "aria-autocomplete" ) |
|
|
.removeAttr( "aria-haspopup" ); |
|
|
if ( this.menu ) { |
|
|
this.menu.element.remove(); |
|
|
} |
|
|
$.Widget.prototype.destroy.call(this); |
|
|
$.Widget.prototype.destroy.call( this ); |
|
|
}, |
|
|
|
|
|
_setOption: function(key) { |
|
|
$.Widget.prototype._setOption.apply(this, arguments); |
|
|
if (key == "source") { |
|
|
_setOption: function( key ) { |
|
|
$.Widget.prototype._setOption.apply( this, arguments ); |
|
|
if ( key == "source" ) { |
|
|
this._initSource(); |
|
|
} |
|
|
}, |
|
|
|
|
|
_initSource: function() { |
|
|
if ($.isArray(this.options.source)) { |
|
|
if ( $.isArray(this.options.source) ) { |
|
|
var array = this.options.source; |
|
|
this.source = function(request, response) { |
|
|
this.source = function( request, response ) { |
|
|
// escape regex characters |
|
|
var matcher = new RegExp($.ui.autocomplete.escapeRegex(request.term), "i"); |
|
|
response($.grep(array, function(value) { |
|
|
return matcher.test(value.value || value.label || value); |
|
|
})); |
|
|
var matcher = new RegExp( $.ui.autocomplete.escapeRegex(request.term), "i" ); |
|
|
response( $.grep( array, function(value) { |
|
|
return matcher.test( value.value || value.label || value ); |
|
|
}) ); |
|
|
}; |
|
|
} else if (typeof this.options.source == "string") { |
|
|
} else if ( typeof this.options.source == "string" ) { |
|
|
var url = this.options.source; |
|
|
this.source = function(request, response) { |
|
|
$.getJSON(url, request, response); |
|
|
this.source = function( request, response ) { |
|
|
$.getJSON( url, request, response ); |
|
|
}; |
|
|
} else { |
|
|
this.source = this.options.source; |
|
|
} |
|
|
}, |
|
|
|
|
|
search: function(value, event) { |
|
|
search: function( value, event ) { |
|
|
value = value != null ? value : this.element.val(); |
|
|
if (value.length < this.options.minLength) { |
|
|
return this.close(event); |
|
|
if ( value.length < this.options.minLength ) { |
|
|
return this.close( event ); |
|
|
} |
|
|
|
|
|
clearTimeout(this.closing); |
|
|
if (this._trigger("search") === false) { |
|
|
clearTimeout( this.closing ); |
|
|
if ( this._trigger("search") === false ) { |
|
|
return; |
|
|
} |
|
|
|
|
|
return this._search(value); |
|
|
return this._search( value ); |
|
|
}, |
|
|
|
|
|
_search: function(value) { |
|
|
_search: function( value ) { |
|
|
this.term = this.element |
|
|
.addClass("ui-autocomplete-loading") |
|
|
.addClass( "ui-autocomplete-loading" ) |
|
|
// always save the actual value, not the one passed as an argument |
|
|
.val(); |
|
|
|
|
|
this.source({ term: value }, this.response); |
|
|
this.source( { term: value }, this.response ); |
|
|
}, |
|
|
|
|
|
_response: function(content) { |
|
|
if (content.length) { |
|
|
content = this._normalize(content); |
|
|
this._trigger("open"); |
|
|
this._suggest(content); |
|
|
_response: function( content ) { |
|
|
if ( content.length ) { |
|
|
content = this._normalize( content ); |
|
|
this._trigger( "open" ); |
|
|
this._suggest( content ); |
|
|
} else { |
|
|
this.close(); |
|
|
} |
|
|
this.element.removeClass("ui-autocomplete-loading"); |
|
|
this.element.removeClass( "ui-autocomplete-loading" ); |
|
|
}, |
|
|
|
|
|
close: function(event) { |
|
|
clearTimeout(this.closing); |
|
|
if (this.menu) { |
|
|
this._trigger("close", event); |
|
|
close: function( event ) { |
|
|
clearTimeout( this.closing ); |
|
|
if ( this.menu ) { |
|
|
this._trigger( "close", event ); |
|
|
this.menu.element.remove(); |
|
|
this.menu = null; |
|
|
} |
|
|
if (this.previous != this.element.val()) { |
|
|
this._trigger("change", event); |
|
|
if ( this.previous != this.element.val() ) { |
|
|
this._trigger( "change", event ); |
|
|
} |
|
|
}, |
|
|
|
|
|
_normalize: function(items) { |
|
|
_normalize: function( items ) { |
|
|
// assume all items have the right format when the first item is complete |
|
|
if (items.length && items[0].label && items[0].value) { |
|
|
if ( items.length && items[0].label && items[0].value ) { |
|
|
return items; |
|
|
} |
|
|
return $.map(items, function(item) { |
|
|
if (typeof item == "string") { |
|
|
return $.map( items, function(item) { |
|
|
if ( typeof item == "string" ) { |
|
|
return { |
|
|
label: item, |
|
|
value: item |
|
|
@@ -195,90 +195,92 @@ $.widget("ui.autocomplete", { |
|
|
return $.extend({ |
|
|
label: item.label || item.value, |
|
|
value: item.value || item.label |
|
|
}, item); |
|
|
}, item ); |
|
|
}); |
|
|
}, |
|
|
|
|
|
_suggest: function(items) { |
|
|
(this.menu && this.menu.element.remove()); |
|
|
_suggest: function( items ) { |
|
|
if (this.menu) { |
|
|
this.menu.element.remove(); |
|
|
} |
|
|
var self = this, |
|
|
ul = $("<ul></ul>"), |
|
|
ul = $( "<ul></ul>" ), |
|
|
parent = this.element.parent(); |
|
|
|
|
|
$.each(items, function(index, item) { |
|
|
$("<li></li>") |
|
|
.data("item.autocomplete", item) |
|
|
.append("<a>" + item.label + "</a>") |
|
|
.appendTo(ul); |
|
|
$.each( items, function( index, item ) { |
|
|
$( "<li></li>" ) |
|
|
.data( "item.autocomplete", item ) |
|
|
.append( "<a>" + item.label + "</a>" ) |
|
|
.appendTo( ul ); |
|
|
}); |
|
|
this.menu = ul |
|
|
.addClass("ui-autocomplete-menu") |
|
|
.appendTo(parent) |
|
|
.addClass( "ui-autocomplete-menu" ) |
|
|
.appendTo( parent ) |
|
|
.menu({ |
|
|
focus: function(event, ui) { |
|
|
var item = ui.item.data("item.autocomplete"); |
|
|
if (false !== self._trigger("focus", null, { item: item })) { |
|
|
focus: function( event, ui ) { |
|
|
var item = ui.item.data( "item.autocomplete" ); |
|
|
if ( false !== self._trigger( "focus", null, { item: item } ) ) { |
|
|
// use value to match what will end up in the input |
|
|
self.element.val(item.value); |
|
|
self.element.val( item.value ); |
|
|
} |
|
|
}, |
|
|
selected: function(event, ui) { |
|
|
var item = ui.item.data("item.autocomplete"); |
|
|
if (false !== self._trigger('select', event, { item: item })) { |
|
|
selected: function( event, ui ) { |
|
|
var item = ui.item.data( "item.autocomplete" ); |
|
|
if ( false !== self._trigger( "select", event, { item: item } ) ) { |
|
|
self.element.val( item.value ); |
|
|
} |
|
|
self.close(event); |
|
|
self.close( event ); |
|
|
self.previous = self.element.val(); |
|
|
// only trigger when focus was lost (click on menu) |
|
|
if (self.element[0] != document.activeElement) { |
|
|
if ( self.element[0] != document.activeElement ) { |
|
|
self.element.focus(); |
|
|
} |
|
|
} |
|
|
}) |
|
|
.zIndex(this.element.zIndex() + 1) |
|
|
.zIndex( this.element.zIndex() + 1 ) |
|
|
// workaround for jQuery bug #5781 http://dev.jquery.com/ticket/5781 |
|
|
.css({ top: 0, left: 0 }) |
|
|
.position({ |
|
|
my: "left top", |
|
|
at: "left bottom", |
|
|
of: this.element |
|
|
}) |
|
|
.data("menu"); |
|
|
if (ul.width() <= this.element.width()) { |
|
|
ul.width(this.element.width()); |
|
|
.data( "menu" ); |
|
|
if ( ul.width() <= this.element.width() ) { |
|
|
ul.width( this.element.width() ); |
|
|
} |
|
|
if ($.fn.bgiframe) { |
|
|
if ( $.fn.bgiframe ) { |
|
|
ul.bgiframe(); |
|
|
} |
|
|
}, |
|
|
|
|
|
_move: function(direction, event) { |
|
|
if (!this.menu) { |
|
|
this.search(null, event); |
|
|
_move: function( direction, event ) { |
|
|
if ( !this.menu ) { |
|
|
this.search( null, event ); |
|
|
return; |
|
|
} |
|
|
if (this.menu.first() && /^previous/.test(direction) |
|
|
|| this.menu.last() && /^next/.test(direction)) { |
|
|
this.element.val(this.term); |
|
|
if ( this.menu.first() && /^previous/.test(direction) || |
|
|
this.menu.last() && /^next/.test(direction) ) { |
|
|
this.element.val( this.term ); |
|
|
this.menu.deactivate(); |
|
|
return; |
|
|
} |
|
|
this.menu[direction](); |
|
|
this.menu[ direction ](); |
|
|
}, |
|
|
|
|
|
widget: function() { |
|
|
// return empty jQuery object when menu isn't initialized yet |
|
|
return this.menu && this.menu.element || $([]); |
|
|
return this.menu ? this.menu.element : $([]); |
|
|
} |
|
|
}); |
|
|
|
|
|
$.extend($.ui.autocomplete, { |
|
|
escapeRegex: function(value) { |
|
|
return value.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi, "\\$1"); |
|
|
$.extend( $.ui.autocomplete, { |
|
|
escapeRegex: function( value ) { |
|
|
return value.replace( /([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi, "\\$1" ); |
|
|
} |
|
|
}); |
|
|
|
|
|
})(jQuery); |
|
|
})( jQuery ); |
|
|
|
|
|
/* |
|
|
* jQuery UI Menu (not officially released) |
|
|
|