Move SimpleAutocomplete functions to the prototype, remove obsolete SHint
parent
f2320f0aa4
commit
c16781a238
752
hinter.js
752
hinter.js
|
@ -1,9 +1,13 @@
|
|||
/* Simple autocomplete for text inputs, with the support for multiple selection.
|
||||
|
||||
Homepage: http://yourcmc.ru/wiki/SimpleAutocomplete
|
||||
(c) Vitaliy Filippov 2011
|
||||
License: FSF LGPL v3 or later (http://www.gnu.org/licenses/lgpl.html)
|
||||
(c) Vitaliy Filippov 2011-2012
|
||||
|
||||
Usage:
|
||||
Include hinter.css, hinter.js on your page. Then write:
|
||||
var hint = new SimpleAutocomplete(input, dataLoader, multipleDelimiter, onChangeListener, maxHeight, emptyText, allowHTML);
|
||||
|
||||
Parameters:
|
||||
input
|
||||
The input, either id or DOM element reference (the input must have an id anyway).
|
||||
|
@ -12,6 +16,7 @@
|
|||
hint.replaceItems([ [ name, value ], [ name, value ], ... ])
|
||||
'hint' parameter will be this autocompleter object, and the guess
|
||||
should be done based on 'value' parameter (string).
|
||||
|
||||
Optional parameters:
|
||||
multipleDelimiter
|
||||
Pass a delimiter string (for example ',' or ';') to enable multiple selection.
|
||||
|
@ -33,6 +38,8 @@
|
|||
If true, HTML code will be allowed in option names.
|
||||
*/
|
||||
|
||||
// *** Constructor ***
|
||||
|
||||
var SimpleAutocomplete = function(input, dataLoader, multipleDelimiter, onChangeListener, maxHeight, emptyText, allowHTML)
|
||||
{
|
||||
if (typeof(input) == 'string')
|
||||
|
@ -41,377 +48,384 @@ var SimpleAutocomplete = function(input, dataLoader, multipleDelimiter, onChange
|
|||
emptyText = 'No items found';
|
||||
|
||||
// Parameters
|
||||
var self = this;
|
||||
self.input = input;
|
||||
self.multipleDelimiter = multipleDelimiter;
|
||||
self.dataLoader = dataLoader;
|
||||
self.onChangeListener = onChangeListener;
|
||||
self.maxHeight = maxHeight;
|
||||
self.emptyText = emptyText;
|
||||
self.allowHTML = allowHTML;
|
||||
this.input = input;
|
||||
this.multipleDelimiter = multipleDelimiter;
|
||||
this.dataLoader = dataLoader;
|
||||
this.onChangeListener = onChangeListener;
|
||||
this.maxHeight = maxHeight;
|
||||
this.emptyText = emptyText;
|
||||
this.allowHTML = allowHTML;
|
||||
|
||||
// Variables
|
||||
self.items = [];
|
||||
self.skipHideCounter = 0;
|
||||
self.selectedIndex = -1;
|
||||
self.id = input.id;
|
||||
self.disabled = false;
|
||||
|
||||
// Initialiser
|
||||
var init = function()
|
||||
{
|
||||
var e = self.input;
|
||||
var p = getOffset(e);
|
||||
|
||||
// Create hint layer
|
||||
var t = self.hintLayer = document.createElement('div');
|
||||
t.className = 'hintLayer';
|
||||
t.style.display = 'none';
|
||||
t.style.position = 'absolute';
|
||||
t.style.top = (p.top+e.offsetHeight) + 'px';
|
||||
t.style.zIndex = 1000;
|
||||
t.style.left = p.left + 'px';
|
||||
if (self.maxHeight)
|
||||
{
|
||||
t.style.overflowY = 'scroll';
|
||||
try { t.style.overflow = '-moz-scrollbars-vertical'; } catch(exc) {}
|
||||
t.style.maxHeight = self.maxHeight+'px';
|
||||
if (!t.style.maxHeight)
|
||||
self.scriptMaxHeight = true;
|
||||
}
|
||||
document.body.appendChild(t);
|
||||
|
||||
// Remember instance
|
||||
e.SimpleAutocomplete_input = self;
|
||||
t.SimpleAutocomplete_layer = self;
|
||||
SimpleAutocomplete.SimpleAutocompletes.push(self);
|
||||
|
||||
// Set event listeners
|
||||
var ie_opera = navigator.userAgent.match('MSIE') || navigator.userAgent.match('Opera');
|
||||
if (ie_opera)
|
||||
addListener(e, 'keydown', self.onKeyPress);
|
||||
else
|
||||
{
|
||||
addListener(e, 'keydown', self.onKeyDown);
|
||||
addListener(e, 'keypress', self.onKeyPress);
|
||||
}
|
||||
addListener(e, 'keyup', self.onKeyUp);
|
||||
addListener(e, 'change', self.onChange);
|
||||
addListener(e, 'focus', self.onInputFocus);
|
||||
addListener(e, 'blur', self.onInputBlur);
|
||||
self.onChange();
|
||||
};
|
||||
|
||||
// obj = [ [ name, value, disabled ], [ name, value ], ... ]
|
||||
self.replaceItems = function(items)
|
||||
{
|
||||
self.hintLayer.innerHTML = '';
|
||||
self.hintLayer.scrollTop = 0;
|
||||
self.items = [];
|
||||
if (!items || items.length == 0)
|
||||
{
|
||||
if (self.emptyText)
|
||||
{
|
||||
var d = document.createElement('div');
|
||||
d.className = 'hintEmptyText';
|
||||
d.innerHTML = self.emptyText;
|
||||
self.hintLayer.appendChild(d);
|
||||
}
|
||||
else
|
||||
self.disable();
|
||||
return;
|
||||
}
|
||||
self.enable();
|
||||
var h = {};
|
||||
if (self.multipleDelimiter)
|
||||
{
|
||||
var old = self.input.value.split(self.multipleDelimiter);
|
||||
for (var i = 0; i < old.length; i++)
|
||||
h[old[i].trim()] = true;
|
||||
}
|
||||
for (var i in items)
|
||||
self.hintLayer.appendChild(self.makeItem(items[i][0], items[i][1], h[items[i][1]]));
|
||||
if (self.maxHeight)
|
||||
{
|
||||
self.hintLayer.style.height =
|
||||
(self.hintLayer.scrollHeight > self.maxHeight
|
||||
? self.maxHeight : self.hintLayer.scrollHeight) + 'px';
|
||||
}
|
||||
};
|
||||
|
||||
// Create a drop-down list item, include checkbox if self.multipleDelimiter is true
|
||||
self.makeItem = function(name, value, checked)
|
||||
{
|
||||
var d = document.createElement('div');
|
||||
d.id = self.id+'_item_'+self.items.length;
|
||||
d.className = 'hintItem';
|
||||
d.title = value;
|
||||
if (self.allowHTML)
|
||||
d.innerHTML = name;
|
||||
if (self.multipleDelimiter)
|
||||
{
|
||||
var c = document.createElement('input');
|
||||
c.type = 'checkbox';
|
||||
c.id = self.id+'_check_'+self.items.length;
|
||||
c.checked = checked && true;
|
||||
c.value = value;
|
||||
if (d.childNodes.length)
|
||||
d.insertBefore(c, d.firstChild);
|
||||
else
|
||||
d.appendChild(c);
|
||||
addListener(c, 'click', self.preventCheck);
|
||||
}
|
||||
if (!self.allowHTML)
|
||||
d.appendChild(document.createTextNode(name));
|
||||
addListener(d, 'mouseover', self.onItemMouseOver);
|
||||
addListener(d, 'mousedown', self.onItemClick);
|
||||
self.items.push([name, value, checked]);
|
||||
return d;
|
||||
};
|
||||
|
||||
// Prevent default action on checkbox
|
||||
self.preventCheck = function(ev)
|
||||
{
|
||||
ev = ev||window.event;
|
||||
return stopEvent(ev, false, true);
|
||||
};
|
||||
|
||||
// Handle item mouse over
|
||||
self.onItemMouseOver = function()
|
||||
{
|
||||
return self.highlightItem(this);
|
||||
};
|
||||
|
||||
// Handle item clicks
|
||||
self.onItemClick = function(ev)
|
||||
{
|
||||
self.selectItem(parseInt(this.id.substr(self.id.length+6)));
|
||||
return true;
|
||||
};
|
||||
|
||||
// Move highlight forward or back by 'by' items (integer)
|
||||
self.moveHighlight = function(by)
|
||||
{
|
||||
var n = self.selectedIndex+by;
|
||||
if (n < 0)
|
||||
n = 0;
|
||||
var elem = document.getElementById(self.id+'_item_'+n);
|
||||
if (!elem)
|
||||
return true;
|
||||
return self.highlightItem(elem);
|
||||
};
|
||||
|
||||
// Make item 'elem' active (highlighted)
|
||||
self.highlightItem = function(elem)
|
||||
{
|
||||
if (self.selectedIndex >= 0)
|
||||
{
|
||||
var c = self.getItem();
|
||||
if (c)
|
||||
c.className = 'hintItem';
|
||||
}
|
||||
self.selectedIndex = parseInt(elem.id.substr(self.id.length+6));
|
||||
elem.className = 'hintActiveItem';
|
||||
return false;
|
||||
};
|
||||
|
||||
// Get index'th item, or current when index is null
|
||||
self.getItem = function(index)
|
||||
{
|
||||
if (index == null)
|
||||
index = self.selectedIndex;
|
||||
if (index < 0)
|
||||
return null;
|
||||
return document.getElementById(self.id+'_item_'+self.selectedIndex);
|
||||
};
|
||||
|
||||
// Select index'th item - change the input value and hide the hint if not a multi-select
|
||||
self.selectItem = function(index)
|
||||
{
|
||||
if (!self.multipleDelimiter)
|
||||
{
|
||||
self.input.value = self.items[index][1];
|
||||
self.hide();
|
||||
}
|
||||
else
|
||||
{
|
||||
document.getElementById(self.id+'_check_'+index).checked = self.items[index][2] = !self.items[index][2];
|
||||
var old = self.input.value.split(self.multipleDelimiter);
|
||||
for (var i = 0; i < old.length; i++)
|
||||
old[i] = old[i].trim();
|
||||
if (!self.items[index][2])
|
||||
{
|
||||
for (var i = old.length-1; i >= 0; i--)
|
||||
if (old[i] == self.items[index][1])
|
||||
old.splice(i, 1);
|
||||
self.input.value = old.join(self.multipleDelimiter+' ');
|
||||
}
|
||||
else
|
||||
{
|
||||
var h = {};
|
||||
for (var i = 0; i < self.items.length; i++)
|
||||
if (self.items[i][2])
|
||||
h[self.items[i][1]] = true;
|
||||
var nl = [];
|
||||
for (var i = 0; i < old.length; i++)
|
||||
{
|
||||
if (h[old[i]])
|
||||
{
|
||||
delete h[old[i]];
|
||||
nl.push(old[i]);
|
||||
}
|
||||
}
|
||||
for (var i = 0; i < self.items.length; i++)
|
||||
if (self.items[i][2] && h[self.items[i][1]])
|
||||
nl.push(self.items[i][1]);
|
||||
self.input.value = nl.join(self.multipleDelimiter+' ');
|
||||
}
|
||||
}
|
||||
self.curValue = self.input.value;
|
||||
if (self.onChangeListener)
|
||||
self.onChangeListener(self, index);
|
||||
};
|
||||
|
||||
// Handle user input, load new items
|
||||
self.onChange = function()
|
||||
{
|
||||
var v = self.input.value.trim();
|
||||
if (v != self.curValue)
|
||||
{
|
||||
self.curValue = v;
|
||||
self.dataLoader(self, v);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
// Handle Enter key presses, cancel handling of arrow keys
|
||||
self.onKeyUp = function(ev)
|
||||
{
|
||||
ev = ev||window.event;
|
||||
if (ev.keyCode != 10 && ev.keyCode != 13)
|
||||
self.show();
|
||||
if (ev.keyCode == 38 || ev.keyCode == 40 || ev.keyCode == 10 || ev.keyCode == 13)
|
||||
{
|
||||
if (self.hintLayer.style.display == '')
|
||||
return stopEvent(ev, true, true);
|
||||
else
|
||||
return true;
|
||||
}
|
||||
self.onChange();
|
||||
return true;
|
||||
};
|
||||
|
||||
// Cancel handling of Enter key
|
||||
self.onKeyDown = function(ev)
|
||||
{
|
||||
ev = ev||window.event;
|
||||
if (ev.keyCode == 10 || ev.keyCode == 13)
|
||||
{
|
||||
if (self.hintLayer.style.display == '')
|
||||
return stopEvent(ev, true, true);
|
||||
else
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
// Handle arrow keys and Enter
|
||||
self.onKeyPress = function(ev)
|
||||
{
|
||||
if (self.hintLayer.style.display == 'none')
|
||||
return true;
|
||||
ev = ev||window.event;
|
||||
if (ev.keyCode == 38) // up
|
||||
self.moveHighlight(-1);
|
||||
else if (ev.keyCode == 40) // down
|
||||
self.moveHighlight(1);
|
||||
else if (ev.keyCode == 10 || ev.keyCode == 13) // enter
|
||||
{
|
||||
if (self.selectedIndex >= 0)
|
||||
self.selectItem(self.selectedIndex);
|
||||
return stopEvent(ev, true, true);
|
||||
}
|
||||
else
|
||||
return true;
|
||||
// scrolling
|
||||
if (self.selectedIndex >= 0)
|
||||
{
|
||||
var c = self.getItem();
|
||||
var t = self.hintLayer;
|
||||
var ct = getOffset(c).top + t.scrollTop - t.style.top.substr(0, t.style.top.length-2);
|
||||
var ch = c.scrollHeight;
|
||||
if (ct+ch-t.offsetHeight > t.scrollTop)
|
||||
t.scrollTop = ct+ch-t.offsetHeight;
|
||||
else if (ct < t.scrollTop)
|
||||
t.scrollTop = ct;
|
||||
}
|
||||
return stopEvent(ev, true, true);
|
||||
};
|
||||
|
||||
// Called when input receives focus
|
||||
self.onInputFocus = function()
|
||||
{
|
||||
self.show();
|
||||
self.input.autocomplete = 'off';
|
||||
self.hasFocus = true;
|
||||
return true;
|
||||
};
|
||||
|
||||
// Called when input loses focus
|
||||
self.onInputBlur = function()
|
||||
{
|
||||
self.hide();
|
||||
self.input.autocomplete = 'on';
|
||||
self.hasFocus = false;
|
||||
return true;
|
||||
};
|
||||
|
||||
// Hide hinter
|
||||
self.hide = function()
|
||||
{
|
||||
if (!self.skipHideCounter)
|
||||
{
|
||||
self.hintLayer.style.display = 'none';
|
||||
self.input.autocomplete = 'on';
|
||||
}
|
||||
else
|
||||
self.skipHideCounter = 0;
|
||||
};
|
||||
|
||||
// Show hinter
|
||||
self.show = function()
|
||||
{
|
||||
if (!self.disabled)
|
||||
{
|
||||
var p = getOffset(self.input);
|
||||
self.hintLayer.style.top = (p.top+self.input.offsetHeight) + 'px';
|
||||
self.hintLayer.style.left = p.left + 'px';
|
||||
self.hintLayer.style.display = '';
|
||||
self.input.autocomplete = 'off';
|
||||
}
|
||||
};
|
||||
|
||||
// Disable hinter, for the case when there is no items and no empty text
|
||||
self.disable = function()
|
||||
{
|
||||
self.disabled = true;
|
||||
self.hide();
|
||||
};
|
||||
|
||||
// Enable hinter
|
||||
self.enable = function()
|
||||
{
|
||||
var show = self.disabled;
|
||||
self.disabled = false;
|
||||
if (show)
|
||||
self.show();
|
||||
}
|
||||
this.items = [];
|
||||
this.skipHideCounter = 0;
|
||||
this.selectedIndex = -1;
|
||||
this.id = input.id;
|
||||
this.disabled = false;
|
||||
|
||||
// *** Call initialise ***
|
||||
init();
|
||||
this.init();
|
||||
};
|
||||
|
||||
// Global variable
|
||||
// *** Instance methods ***
|
||||
|
||||
// Initialiser
|
||||
SimpleAutocomplete.prototype.init = function()
|
||||
{
|
||||
var e = this.input;
|
||||
var p = getOffset(e);
|
||||
|
||||
// Create hint layer
|
||||
var t = this.hintLayer = document.createElement('div');
|
||||
t.className = 'hintLayer';
|
||||
t.style.display = 'none';
|
||||
t.style.position = 'absolute';
|
||||
t.style.top = (p.top+e.offsetHeight) + 'px';
|
||||
t.style.zIndex = 1000;
|
||||
t.style.left = p.left + 'px';
|
||||
if (this.maxHeight)
|
||||
{
|
||||
t.style.overflowY = 'scroll';
|
||||
try { t.style.overflow = '-moz-scrollbars-vertical'; } catch(exc) {}
|
||||
t.style.maxHeight = this.maxHeight+'px';
|
||||
if (!t.style.maxHeight)
|
||||
this.scriptMaxHeight = true;
|
||||
}
|
||||
document.body.appendChild(t);
|
||||
|
||||
// Remember instance
|
||||
e.SimpleAutocomplete_input = self;
|
||||
t.SimpleAutocomplete_layer = self;
|
||||
SimpleAutocomplete.SimpleAutocompletes.push(self);
|
||||
|
||||
// Set event listeners
|
||||
var self = this;
|
||||
var ie_opera = navigator.userAgent.match('MSIE') || navigator.userAgent.match('Opera');
|
||||
if (ie_opera)
|
||||
addListener(e, 'keydown', function(ev) { return self.onKeyPress(ev); });
|
||||
else
|
||||
{
|
||||
addListener(e, 'keydown', function(ev) { return self.onKeyDown(ev); });
|
||||
addListener(e, 'keypress', function(ev) { return self.onKeyPress(ev); });
|
||||
}
|
||||
addListener(e, 'keyup', function(ev) { return self.onKeyUp(ev); });
|
||||
addListener(e, 'change', function() { return self.onChange(); });
|
||||
addListener(e, 'focus', function() { return self.onInputFocus(); });
|
||||
addListener(e, 'blur', function() { return self.onInputBlur(); });
|
||||
this.onChange();
|
||||
};
|
||||
|
||||
// obj = [ [ name, value, disabled ], [ name, value ], ... ]
|
||||
SimpleAutocomplete.prototype.replaceItems = function(items)
|
||||
{
|
||||
this.hintLayer.innerHTML = '';
|
||||
this.hintLayer.scrollTop = 0;
|
||||
this.items = [];
|
||||
if (!items || items.length == 0)
|
||||
{
|
||||
if (this.emptyText)
|
||||
{
|
||||
var d = document.createElement('div');
|
||||
d.className = 'hintEmptyText';
|
||||
d.innerHTML = this.emptyText;
|
||||
this.hintLayer.appendChild(d);
|
||||
}
|
||||
else
|
||||
this.disable();
|
||||
return;
|
||||
}
|
||||
this.enable();
|
||||
var h = {};
|
||||
if (this.multipleDelimiter)
|
||||
{
|
||||
var old = this.input.value.split(this.multipleDelimiter);
|
||||
for (var i = 0; i < old.length; i++)
|
||||
h[old[i].trim()] = true;
|
||||
}
|
||||
for (var i in items)
|
||||
this.hintLayer.appendChild(this.makeItem(items[i][0], items[i][1], h[items[i][1]]));
|
||||
if (this.maxHeight)
|
||||
{
|
||||
this.hintLayer.style.height =
|
||||
(this.hintLayer.scrollHeight > this.maxHeight
|
||||
? this.maxHeight : this.hintLayer.scrollHeight) + 'px';
|
||||
}
|
||||
};
|
||||
|
||||
// Create a drop-down list item, include checkbox if this.multipleDelimiter is true
|
||||
SimpleAutocomplete.prototype.makeItem = function(name, value, checked)
|
||||
{
|
||||
var d = document.createElement('div');
|
||||
d.id = this.id+'_item_'+this.items.length;
|
||||
d.className = 'hintItem';
|
||||
d.title = value;
|
||||
if (this.allowHTML)
|
||||
d.innerHTML = name;
|
||||
if (this.multipleDelimiter)
|
||||
{
|
||||
var c = document.createElement('input');
|
||||
c.type = 'checkbox';
|
||||
c.id = this.id+'_check_'+this.items.length;
|
||||
c.checked = checked && true;
|
||||
c.value = value;
|
||||
if (d.childNodes.length)
|
||||
d.insertBefore(c, d.firstChild);
|
||||
else
|
||||
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() { return self.onItemClick(this); });
|
||||
this.items.push([name, value, checked]);
|
||||
return d;
|
||||
};
|
||||
|
||||
// Move highlight forward or back by 'by' items (integer)
|
||||
SimpleAutocomplete.prototype.moveHighlight = function(by)
|
||||
{
|
||||
var n = this.selectedIndex+by;
|
||||
if (n < 0)
|
||||
n = 0;
|
||||
var elem = document.getElementById(this.id+'_item_'+n);
|
||||
if (!elem)
|
||||
return true;
|
||||
return this.highlightItem(elem);
|
||||
};
|
||||
|
||||
// Make item 'elem' active (highlighted)
|
||||
SimpleAutocomplete.prototype.highlightItem = function(elem)
|
||||
{
|
||||
if (this.selectedIndex >= 0)
|
||||
{
|
||||
var c = this.getItem();
|
||||
if (c)
|
||||
c.className = 'hintItem';
|
||||
}
|
||||
this.selectedIndex = parseInt(elem.id.substr(this.id.length+6));
|
||||
elem.className = 'hintActiveItem';
|
||||
return false;
|
||||
};
|
||||
|
||||
// Get index'th item, or current when index is null
|
||||
SimpleAutocomplete.prototype.getItem = function(index)
|
||||
{
|
||||
if (index == null)
|
||||
index = this.selectedIndex;
|
||||
if (index < 0)
|
||||
return null;
|
||||
return document.getElementById(this.id+'_item_'+this.selectedIndex);
|
||||
};
|
||||
|
||||
// Select index'th item - change the input value and hide the hint if not a multi-select
|
||||
SimpleAutocomplete.prototype.selectItem = function(index)
|
||||
{
|
||||
if (!this.multipleDelimiter)
|
||||
{
|
||||
this.input.value = this.items[index][1];
|
||||
this.hide();
|
||||
}
|
||||
else
|
||||
{
|
||||
document.getElementById(this.id+'_check_'+index).checked = this.items[index][2] = !this.items[index][2];
|
||||
var old = this.input.value.split(this.multipleDelimiter);
|
||||
for (var i = 0; i < old.length; i++)
|
||||
old[i] = old[i].trim();
|
||||
if (!this.items[index][2])
|
||||
{
|
||||
for (var i = old.length-1; i >= 0; i--)
|
||||
if (old[i] == this.items[index][1])
|
||||
old.splice(i, 1);
|
||||
this.input.value = old.join(this.multipleDelimiter+' ');
|
||||
}
|
||||
else
|
||||
{
|
||||
var h = {};
|
||||
for (var i = 0; i < this.items.length; i++)
|
||||
if (this.items[i][2])
|
||||
h[this.items[i][1]] = true;
|
||||
var nl = [];
|
||||
for (var i = 0; i < old.length; i++)
|
||||
{
|
||||
if (h[old[i]])
|
||||
{
|
||||
delete h[old[i]];
|
||||
nl.push(old[i]);
|
||||
}
|
||||
}
|
||||
for (var i = 0; i < this.items.length; i++)
|
||||
if (this.items[i][2] && h[this.items[i][1]])
|
||||
nl.push(this.items[i][1]);
|
||||
this.input.value = nl.join(this.multipleDelimiter+' ');
|
||||
}
|
||||
}
|
||||
this.curValue = this.input.value;
|
||||
if (this.onChangeListener)
|
||||
this.onChangeListener(self, index);
|
||||
};
|
||||
|
||||
// Hide hinter
|
||||
SimpleAutocomplete.prototype.hide = function()
|
||||
{
|
||||
if (!this.skipHideCounter)
|
||||
{
|
||||
this.hintLayer.style.display = 'none';
|
||||
this.input.autocomplete = 'on';
|
||||
}
|
||||
else
|
||||
this.skipHideCounter = 0;
|
||||
};
|
||||
|
||||
// Show hinter
|
||||
SimpleAutocomplete.prototype.show = function()
|
||||
{
|
||||
if (!this.disabled)
|
||||
{
|
||||
var p = getOffset(this.input);
|
||||
this.hintLayer.style.top = (p.top+this.input.offsetHeight) + 'px';
|
||||
this.hintLayer.style.left = p.left + 'px';
|
||||
this.hintLayer.style.display = '';
|
||||
this.input.autocomplete = 'off';
|
||||
}
|
||||
};
|
||||
|
||||
// Disable hinter, for the case when there is no items and no empty text
|
||||
SimpleAutocomplete.prototype.disable = function()
|
||||
{
|
||||
this.disabled = true;
|
||||
this.hide();
|
||||
};
|
||||
|
||||
// Enable hinter
|
||||
SimpleAutocomplete.prototype.enable = function()
|
||||
{
|
||||
var show = this.disabled;
|
||||
this.disabled = false;
|
||||
if (show)
|
||||
this.show();
|
||||
}
|
||||
|
||||
// *** Event handlers ***
|
||||
|
||||
// Prevent default action on checkbox
|
||||
SimpleAutocomplete.prototype.preventCheck = function(ev)
|
||||
{
|
||||
ev = ev||window.event;
|
||||
return stopEvent(ev, false, true);
|
||||
};
|
||||
|
||||
// Handle item mouse over
|
||||
SimpleAutocomplete.prototype.onItemMouseOver = function(elm)
|
||||
{
|
||||
return this.highlightItem(elm);
|
||||
};
|
||||
|
||||
// Handle item clicks
|
||||
SimpleAutocomplete.prototype.onItemClick = function(elm)
|
||||
{
|
||||
this.selectItem(parseInt(elm.id.substr(this.id.length+6)));
|
||||
return true;
|
||||
};
|
||||
|
||||
// Handle user input, load new items
|
||||
SimpleAutocomplete.prototype.onChange = function()
|
||||
{
|
||||
var v = this.input.value.trim();
|
||||
if (v != this.curValue)
|
||||
{
|
||||
this.curValue = v;
|
||||
this.dataLoader(this, v);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
// Handle Enter key presses, cancel handling of arrow keys
|
||||
SimpleAutocomplete.prototype.onKeyUp = function(ev)
|
||||
{
|
||||
ev = ev||window.event;
|
||||
if (ev.keyCode != 10 && ev.keyCode != 13)
|
||||
this.show();
|
||||
if (ev.keyCode == 38 || ev.keyCode == 40 || ev.keyCode == 10 || ev.keyCode == 13)
|
||||
{
|
||||
if (this.hintLayer.style.display == '')
|
||||
return stopEvent(ev, true, true);
|
||||
else
|
||||
return true;
|
||||
}
|
||||
this.onChange();
|
||||
return true;
|
||||
};
|
||||
|
||||
// Cancel handling of Enter key
|
||||
SimpleAutocomplete.prototype.onKeyDown = function(ev)
|
||||
{
|
||||
ev = ev||window.event;
|
||||
if (ev.keyCode == 10 || ev.keyCode == 13)
|
||||
{
|
||||
if (this.hintLayer.style.display == '')
|
||||
return stopEvent(ev, true, true);
|
||||
else
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
// Handle arrow keys and Enter
|
||||
SimpleAutocomplete.prototype.onKeyPress = function(ev)
|
||||
{
|
||||
if (this.hintLayer.style.display == 'none')
|
||||
return true;
|
||||
ev = ev||window.event;
|
||||
if (ev.keyCode == 38) // up
|
||||
this.moveHighlight(-1);
|
||||
else if (ev.keyCode == 40) // down
|
||||
this.moveHighlight(1);
|
||||
else if (ev.keyCode == 10 || ev.keyCode == 13) // enter
|
||||
{
|
||||
if (this.selectedIndex >= 0)
|
||||
this.selectItem(this.selectedIndex);
|
||||
return stopEvent(ev, true, true);
|
||||
}
|
||||
else
|
||||
return true;
|
||||
// scrolling
|
||||
if (this.selectedIndex >= 0)
|
||||
{
|
||||
var c = this.getItem();
|
||||
var t = this.hintLayer;
|
||||
var ct = getOffset(c).top + t.scrollTop - t.style.top.substr(0, t.style.top.length-2);
|
||||
var ch = c.scrollHeight;
|
||||
if (ct+ch-t.offsetHeight > t.scrollTop)
|
||||
t.scrollTop = ct+ch-t.offsetHeight;
|
||||
else if (ct < t.scrollTop)
|
||||
t.scrollTop = ct;
|
||||
}
|
||||
return stopEvent(ev, true, true);
|
||||
};
|
||||
|
||||
// Called when input receives focus
|
||||
SimpleAutocomplete.prototype.onInputFocus = function()
|
||||
{
|
||||
this.show();
|
||||
this.input.autocomplete = 'off';
|
||||
this.hasFocus = true;
|
||||
return true;
|
||||
};
|
||||
|
||||
// Called when input loses focus
|
||||
SimpleAutocomplete.prototype.onInputBlur = function()
|
||||
{
|
||||
this.hide();
|
||||
this.input.autocomplete = 'on';
|
||||
this.hasFocus = false;
|
||||
return true;
|
||||
};
|
||||
|
||||
// *** Global variables ***
|
||||
|
||||
// List of all instances
|
||||
SimpleAutocomplete.SimpleAutocompletes = [];
|
||||
|
||||
// Global mousedown handler, hides dropdowns when clicked outside
|
||||
|
@ -438,8 +452,8 @@ SimpleAutocomplete.GlobalMouseDown = function(ev)
|
|||
return true;
|
||||
};
|
||||
|
||||
//// UTILITY FUNCTIONS ////
|
||||
// You can delete this section if you already have them somewhere in your scripts //
|
||||
// *** UTILITY FUNCTIONS ***
|
||||
// Remove this section if you already have these functions somewhere else included
|
||||
|
||||
// Cross-browser adding of event listeners
|
||||
var addListener = function()
|
||||
|
@ -526,7 +540,7 @@ var getOffsetSum = function(elem)
|
|||
return { top: top, left: left };
|
||||
};
|
||||
|
||||
//// END UTILITY FUNCTIONS ////
|
||||
// *** END UTILITY FUNCTIONS ***
|
||||
|
||||
// Set global mousedown listener
|
||||
addListener(window, 'load', function() { addListener(document, 'mousedown', SimpleAutocomplete.GlobalMouseDown) });
|
||||
|
|
Loading…
Reference in New Issue