Fix overlapping ids, support loading more items in hint

master
Vitaliy Filippov 2013-01-17 18:59:14 +00:00
parent 01cad41a87
commit 2faa1f7732
2 changed files with 66 additions and 16 deletions

View File

@ -1,12 +1,12 @@
.hintLayer {
border: 1px solid gray;
color: gray;
width: 200pt;
background-color: white;
font-size: 80%;
max-height: 300pt;
overflow-y: scroll;
overflow: -moz-scrollbars-vertical;
z-index: 1000;
}
.hintEmptyText {
padding: 3px;
@ -16,15 +16,20 @@
cursor: pointer;
padding: 1px 3px;
vertical-align: middle;
white-space: nowrap;
}
.hintActiveItem {
color: white;
background-color: #008;
cursor: pointer;
padding: 1px 3px;
white-space: nowrap;
}
.hintItem input, .hintActiveItem input {
cursor: pointer;
vertical-align: middle;
margin-right: 3px;
}
.hintItem img, .hintActiveItem img {
vertical-align: middle;
}

View File

@ -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);
}