|
|
|
@ -11,11 +11,21 @@ |
|
|
|
|
Parameters: |
|
|
|
|
input |
|
|
|
|
The input, either id or DOM element reference (the input must have an id anyway). |
|
|
|
|
dataLoader(hint, value) |
|
|
|
|
dataLoader(hint, value[, more]) |
|
|
|
|
Callback which should load autocomplete options and then call |
|
|
|
|
hint.replaceItems([ [ name, value ], [ name, value ], ... ]) |
|
|
|
|
'hint' parameter will be this autocompleter object, and the guess |
|
|
|
|
should be done based on 'value' parameter (string). |
|
|
|
|
hint.replaceItems(newOptions, keepPosition), where: |
|
|
|
|
newOptions is [ [ name, value ], [ name, value ], ... ] and |
|
|
|
|
keepPosition should be true when more was > 0. |
|
|
|
|
Callback parameters: |
|
|
|
|
hint |
|
|
|
|
This SimpleAutocomplete object |
|
|
|
|
value |
|
|
|
|
The string guess should be done based on |
|
|
|
|
more |
|
|
|
|
This is optional and means the times user has triggered 'load more items'. |
|
|
|
|
i.e. if the original list had 10 items (more=0), after first 'more' click |
|
|
|
|
user would expect 20 items (more=1), and etc. |
|
|
|
|
See also moreMarker option below. |
|
|
|
|
|
|
|
|
|
params attribute is an object with optional parameters: |
|
|
|
|
multipleDelimiter |
|
|
|
@ -39,6 +49,14 @@ |
|
|
|
|
delay |
|
|
|
|
If this is set to a non-zero value, the autocompleter does no more than |
|
|
|
|
1 request in each delay milliseconds. |
|
|
|
|
moreMarker |
|
|
|
|
The server supplying hint options usually limits their count. |
|
|
|
|
But it's not always convenient having to type additional characters for |
|
|
|
|
narrowing down the selection. Optionally you can supply additional item |
|
|
|
|
with special value '#MORE' (or 'moreMarker' option value if it is there) |
|
|
|
|
at the end of the list, and when it will be clicked, SimpleAutocomplete |
|
|
|
|
will issue another request to dataLoader with incremented 'more' parameter. |
|
|
|
|
You can also set moreMarker to false to disable this feature. |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
// *** Constructor ***
|
|
|
|
@ -59,18 +77,24 @@ var SimpleAutocomplete = function(input, dataLoader, params) |
|
|
|
|
this.emptyText = params.emptyText; |
|
|
|
|
this.allowHTML = params.allowHTML; |
|
|
|
|
this.delay = params.delay; |
|
|
|
|
this.moreMarker = params.moreMarker; |
|
|
|
|
|
|
|
|
|
// Default values
|
|
|
|
|
if (this.moreMarker === undefined) |
|
|
|
|
this.moreMarker = '#MORE'; |
|
|
|
|
if (this.delay === undefined) |
|
|
|
|
this.delay = 300; |
|
|
|
|
if (this.emptyText === undefined) |
|
|
|
|
this.emptyText = 'No items found'; |
|
|
|
|
|
|
|
|
|
// Variables
|
|
|
|
|
this.isMoreClick = false; |
|
|
|
|
this.more = 0; |
|
|
|
|
this.timer = null; |
|
|
|
|
this.closure = []; |
|
|
|
|
this.items = []; |
|
|
|
|
this.skipHideCounter = 0; |
|
|
|
|
this.selectedIndex = -1; |
|
|
|
|
this.id = input.id; |
|
|
|
|
this.disabled = false; |
|
|
|
|
|
|
|
|
|
// *** Call initialise ***
|
|
|
|
@ -83,6 +107,10 @@ var SimpleAutocomplete = function(input, dataLoader, params) |
|
|
|
|
SimpleAutocomplete.prototype.init = function() |
|
|
|
|
{ |
|
|
|
|
var e = this.input; |
|
|
|
|
var l = SimpleAutocomplete.SimpleAutocompletes; |
|
|
|
|
this.id = this.input.id + l.length; |
|
|
|
|
l.push(this); |
|
|
|
|
|
|
|
|
|
var p = getOffset(e); |
|
|
|
|
|
|
|
|
|
// Create hint layer
|
|
|
|
@ -106,7 +134,6 @@ SimpleAutocomplete.prototype.init = function() |
|
|
|
|
// Remember instance
|
|
|
|
|
e.SimpleAutocomplete_input = this; |
|
|
|
|
t.SimpleAutocomplete_layer = this; |
|
|
|
|
SimpleAutocomplete.SimpleAutocompletes.push(this); |
|
|
|
|
|
|
|
|
|
// Set event listeners
|
|
|
|
|
var self = this; |
|
|
|
@ -127,10 +154,13 @@ SimpleAutocomplete.prototype.init = function() |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
// obj = [ [ name, value, disabled ], [ name, value ], ... ]
|
|
|
|
|
SimpleAutocomplete.prototype.replaceItems = function(items) |
|
|
|
|
SimpleAutocomplete.prototype.replaceItems = function(items, keepPosition) |
|
|
|
|
{ |
|
|
|
|
this.hintLayer.innerHTML = ''; |
|
|
|
|
this.hintLayer.scrollTop = 0; |
|
|
|
|
if (!keepPosition) |
|
|
|
|
{ |
|
|
|
|
this.hintLayer.scrollTop = 0; |
|
|
|
|
} |
|
|
|
|
this.items = []; |
|
|
|
|
if (!items || items.length == 0) |
|
|
|
|
{ |
|
|
|
@ -203,6 +233,8 @@ SimpleAutocomplete.prototype.makeItem = function(name, value, checked) |
|
|
|
|
d.title = value; |
|
|
|
|
if (this.allowHTML) |
|
|
|
|
d.innerHTML = name; |
|
|
|
|
else |
|
|
|
|
d.appendChild(document.createTextNode(name)); |
|
|
|
|
if (this.multipleDelimiter) |
|
|
|
|
{ |
|
|
|
|
var c = document.createElement('input'); |
|
|
|
@ -216,8 +248,6 @@ SimpleAutocomplete.prototype.makeItem = function(name, value, checked) |
|
|
|
|
d.appendChild(c); |
|
|
|
|
addListener(c, 'click', this.preventCheck); |
|
|
|
|
} |
|
|
|
|
if (!this.allowHTML) |
|
|
|
|
d.appendChild(document.createTextNode(name)); |
|
|
|
|
var self = this; |
|
|
|
|
addListener(d, 'mouseover', function() { return self.onItemMouseOver(this); }); |
|
|
|
|
addListener(d, 'mousedown', function(ev) { return self.onItemClick(ev, this); }); |
|
|
|
@ -275,6 +305,7 @@ SimpleAutocomplete.prototype.selectItem = function(index) |
|
|
|
|
var old = this.input.value.split(this.multipleDelimiter); |
|
|
|
|
for (var i = 0; i < old.length; i++) |
|
|
|
|
old[i] = old[i].trim(); |
|
|
|
|
// Turn the clicked item on or off, preserving order
|
|
|
|
|
if (!this.items[index][2]) |
|
|
|
|
{ |
|
|
|
|
for (var i = old.length-1; i >= 0; i--) |
|
|
|
@ -376,7 +407,17 @@ SimpleAutocomplete.prototype.onItemMouseOver = function(elm) |
|
|
|
|
// Handle item clicks
|
|
|
|
|
SimpleAutocomplete.prototype.onItemClick = function(ev, elm) |
|
|
|
|
{ |
|
|
|
|
this.selectItem(parseInt(elm.id.substr(this.id.length+6))); |
|
|
|
|
var index = parseInt(elm.id.substr(this.id.length+6)); |
|
|
|
|
if (this.moreMarker && this.items[index][1] == this.moreMarker) |
|
|
|
|
{ |
|
|
|
|
// User clicked 'more'. Load more items without delay.
|
|
|
|
|
this.isMoreClick = true; |
|
|
|
|
this.more++; |
|
|
|
|
this.onChange(); |
|
|
|
|
this.isMoreClick = false; |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
this.selectItem(index); |
|
|
|
|
return true; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
@ -384,16 +425,20 @@ SimpleAutocomplete.prototype.onItemClick = function(ev, elm) |
|
|
|
|
SimpleAutocomplete.prototype.onChange = function() |
|
|
|
|
{ |
|
|
|
|
var v = this.input.value.trim(); |
|
|
|
|
if (v != this.curValue) |
|
|
|
|
if (!this.isMoreClick) |
|
|
|
|
{ |
|
|
|
|
this.more = 0; |
|
|
|
|
} |
|
|
|
|
if (v != this.curValue || this.isMoreClick) |
|
|
|
|
{ |
|
|
|
|
this.curValue = v; |
|
|
|
|
if (!this.delay) |
|
|
|
|
this.dataLoader(this, v); |
|
|
|
|
if (!this.delay || this.isMoreClick) |
|
|
|
|
this.dataLoader(this, v, this.more); |
|
|
|
|
else if (!this.timer) |
|
|
|
|
{ |
|
|
|
|
var self = this; |
|
|
|
|
this.timer = setTimeout(function() { |
|
|
|
|
self.dataLoader(self, self.curValue); |
|
|
|
|
self.dataLoader(self, self.curValue, self.more); |
|
|
|
|
self.timer = null; |
|
|
|
|
}, this.delay); |
|
|
|
|
} |
|
|
|
|