summaryrefslogtreecommitdiff
path: root/misc/autocomplete.js
diff options
context:
space:
mode:
Diffstat (limited to 'misc/autocomplete.js')
-rw-r--r--misc/autocomplete.js231
1 files changed, 120 insertions, 111 deletions
diff --git a/misc/autocomplete.js b/misc/autocomplete.js
index 49e7e0210..e71f4afea 100644
--- a/misc/autocomplete.js
+++ b/misc/autocomplete.js
@@ -1,76 +1,51 @@
// $Id$
-// Global Killswitch
-if (isJsEnabled()) {
- addLoadEvent(autocompleteAutoAttach);
-}
-
/**
* Attaches the autocomplete behaviour to all required fields
*/
-function autocompleteAutoAttach() {
+Drupal.autocompleteAutoAttach = function () {
var acdb = [];
- var inputs = document.getElementsByTagName('input');
- for (i = 0; input = inputs[i]; i++) {
- if (input && hasClass(input, 'autocomplete')) {
- uri = input.value;
- if (!acdb[uri]) {
- acdb[uri] = new ACDB(uri);
- }
- input = $(input.id.substr(0, input.id.length - 13));
- input.setAttribute('autocomplete', 'OFF');
- addSubmitEvent(input.form, autocompleteSubmit);
- new jsAC(input, acdb[uri]);
+ $('input.autocomplete').each(function () {
+ var uri = this.value;
+ if (!acdb[uri]) {
+ acdb[uri] = new Drupal.ACDB(uri);
}
- }
+ var input = $('#' + this.id.substr(0, this.id.length - 13))
+ .attr('autocomplete', 'OFF')[0];
+ $(input.form).submit(Drupal.autocompleteSubmit);
+ new Drupal.jsAC(input, acdb[uri]);
+ });
}
/**
* Prevents the form from submitting if the suggestions popup is open
+ * and closes the suggestions popup when doing so.
*/
-function autocompleteSubmit() {
- var popup = document.getElementById('autocomplete');
- if (popup) {
- popup.owner.hidePopup();
- return false;
- }
- return true;
+Drupal.autocompleteSubmit = function () {
+ return $('#autocomplete').each(function () {
+ this.owner.hidePopup();
+ }).size() == 0;
}
-
/**
* An AutoComplete object
*/
-function jsAC(input, db) {
+Drupal.jsAC = function (input, db) {
var ac = this;
this.input = input;
this.db = db;
- this.input.onkeydown = function (event) { return ac.onkeydown(this, event); };
- this.input.onkeyup = function (event) { ac.onkeyup(this, event) };
- this.input.onblur = function () { ac.hidePopup(); ac.db.cancel(); };
- this.popup = document.createElement('div');
- this.popup.id = 'autocomplete';
- this.popup.owner = this;
-};
-/**
- * Hides the autocomplete suggestions
- */
-jsAC.prototype.hidePopup = function (keycode) {
- if (this.selected && ((keycode && keycode != 46 && keycode != 8 && keycode != 27) || !keycode)) {
- this.input.value = this.selected.autocompleteValue;
- }
- if (this.popup.parentNode && this.popup.parentNode.tagName) {
- removeNode(this.popup);
- }
- this.selected = false;
-}
+ $(this.input)
+ .keydown(function (event) { return ac.onkeydown(this, event); })
+ .keyup(function (event) { ac.onkeyup(this, event) })
+ .blur(function () { ac.hidePopup(); ac.db.cancel(); });
+};
/**
* Handler for the "keydown" event
*/
-jsAC.prototype.onkeydown = function (input, e) {
+Drupal.jsAC.prototype.onkeydown = function (input, e) {
if (!e) {
e = window.event;
}
@@ -89,7 +64,7 @@ jsAC.prototype.onkeydown = function (input, e) {
/**
* Handler for the "keyup" event
*/
-jsAC.prototype.onkeyup = function (input, e) {
+Drupal.jsAC.prototype.onkeyup = function (input, e) {
if (!e) {
e = window.event;
}
@@ -126,21 +101,21 @@ jsAC.prototype.onkeyup = function (input, e) {
/**
* Puts the currently highlighted suggestion into the autocomplete field
*/
-jsAC.prototype.select = function (node) {
+Drupal.jsAC.prototype.select = function (node) {
this.input.value = node.autocompleteValue;
}
/**
* Highlights the next suggestion
*/
-jsAC.prototype.selectDown = function () {
+Drupal.jsAC.prototype.selectDown = function () {
if (this.selected && this.selected.nextSibling) {
this.highlight(this.selected.nextSibling);
}
else {
- var lis = this.popup.getElementsByTagName('li');
- if (lis.length > 0) {
- this.highlight(lis[0]);
+ var lis = $('li', this.popup);
+ if (lis.size() > 0) {
+ this.highlight(lis.get(0));
}
}
}
@@ -148,7 +123,7 @@ jsAC.prototype.selectDown = function () {
/**
* Highlights the previous suggestion
*/
-jsAC.prototype.selectUp = function () {
+Drupal.jsAC.prototype.selectUp = function () {
if (this.selected && this.selected.previousSibling) {
this.highlight(this.selected.previousSibling);
}
@@ -157,30 +132,61 @@ jsAC.prototype.selectUp = function () {
/**
* Highlights a suggestion
*/
-jsAC.prototype.highlight = function (node) {
- removeClass(this.selected, 'selected');
- addClass(node, 'selected');
+Drupal.jsAC.prototype.highlight = function (node) {
+ if (this.selected) {
+ $(this.selected).removeClass('selected');
+ }
+ $(node).addClass('selected');
this.selected = node;
}
/**
* Unhighlights a suggestion
*/
-jsAC.prototype.unhighlight = function (node) {
- removeClass(node, 'selected');
+Drupal.jsAC.prototype.unhighlight = function (node) {
+ $(node).removeClass('selected');
+ this.selected = false;
+}
+
+/**
+ * Hides the autocomplete suggestions
+ */
+Drupal.jsAC.prototype.hidePopup = function (keycode) {
+ // Select item if the right key or mousebutton was pressed
+ if (this.selected && ((keycode && keycode != 46 && keycode != 8 && keycode != 27) || !keycode)) {
+ this.input.value = this.selected.autocompleteValue;
+ }
+ // Hide popup
+ var popup = this.popup;
+ if (popup) {
+ this.popup = null;
+ $(popup).fadeOut('fast', function() { $(popup).remove(); });
+ }
this.selected = false;
}
/**
* Positions the suggestions popup and starts a search
*/
-jsAC.prototype.populatePopup = function () {
- var ac = this;
- var pos = absolutePosition(this.input);
+Drupal.jsAC.prototype.populatePopup = function () {
+ // Show popup
+ if (this.popup) {
+ $(this.popup).remove();
+ }
+ var pos = Drupal.absolutePosition(this.input);
this.selected = false;
- this.popup.style.top = (pos.y + this.input.offsetHeight) +'px';
- this.popup.style.left = pos.x +'px';
- this.popup.style.width = (this.input.offsetWidth - 4) +'px';
+ this.popup = document.createElement('div');
+ this.popup.id = 'autocomplete';
+ this.popup.owner = this;
+ $(this.popup).css({
+ top: (pos.y + this.input.offsetHeight) +'px',
+ left: pos.x +'px',
+ width: (this.input.offsetWidth - 4) +'px',
+ display: 'none'
+ });
+ $('body').append(this.popup);
+
+ // Do search
this.db.owner = this;
this.db.search(this.input.value);
}
@@ -188,45 +194,40 @@ jsAC.prototype.populatePopup = function () {
/**
* Fills the suggestion popup with any matches received
*/
-jsAC.prototype.found = function (matches) {
- while (this.popup.hasChildNodes()) {
- this.popup.removeChild(this.popup.childNodes[0]);
- }
- if (!this.popup.parentNode || !this.popup.parentNode.tagName) {
- document.getElementsByTagName('body')[0].appendChild(this.popup);
- }
+Drupal.jsAC.prototype.found = function (matches) {
+ // Prepare matches
var ul = document.createElement('ul');
var ac = this;
-
for (key in matches) {
var li = document.createElement('li');
- var div = document.createElement('div');
- div.innerHTML = matches[key];
- li.appendChild(div);
+ $(li)
+ .html('<div>'+ matches[key] +'</div>')
+ .mousedown(function () { ac.select(this); })
+ .mouseover(function () { ac.highlight(this); })
+ .mouseout(function () { ac.unhighlight(this); });
li.autocompleteValue = key;
- li.onmousedown = function() { ac.select(this); };
- li.onmouseover = function() { ac.highlight(this); };
- li.onmouseout = function() { ac.unhighlight(this); };
- ul.appendChild(li);
+ $(ul).append(li);
}
+ // Show popup with matches, if any
if (ul.childNodes.length > 0) {
- this.popup.appendChild(ul);
+ $(this.popup).empty().append(ul).show();
}
else {
+ $(this.popup).css({visibility: 'hidden'});
this.hidePopup();
}
}
-jsAC.prototype.setStatus = function (status) {
+Drupal.jsAC.prototype.setStatus = function (status) {
switch (status) {
case 'begin':
- addClass(this.input, 'throbbing');
+ $(this.input).addClass('throbbing');
break;
case 'cancel':
case 'error':
case 'found':
- removeClass(this.input, 'throbbing');
+ $(this.input).removeClass('throbbing');
break;
}
}
@@ -234,7 +235,7 @@ jsAC.prototype.setStatus = function (status) {
/**
* An AutoComplete DataBase object
*/
-function ACDB(uri) {
+Drupal.ACDB = function (uri) {
this.uri = uri;
this.delay = 300;
this.cache = {};
@@ -243,47 +244,55 @@ function ACDB(uri) {
/**
* Performs a cached and delayed search
*/
-ACDB.prototype.search = function(searchString) {
+Drupal.ACDB.prototype.search = function (searchString) {
+ var db = this;
this.searchString = searchString;
+
+ // See if this key has been searched for before
if (this.cache[searchString]) {
return this.owner.found(this.cache[searchString]);
}
+
+ // Initiate delayed search
if (this.timer) {
clearTimeout(this.timer);
}
- var db = this;
this.timer = setTimeout(function() {
db.owner.setStatus('begin');
- db.transport = HTTPGet(db.uri +'/'+ encodeURIComponent(searchString), db.receive, db);
- }, this.delay);
-}
-/**
- * HTTP callback function. Passes suggestions to the autocomplete object
- */
-ACDB.prototype.receive = function(string, xmlhttp, acdb) {
- // Note: Safari returns 'undefined' status if the request returns no data.
- if (xmlhttp.status != 200 && typeof xmlhttp.status != 'undefined') {
- acdb.owner.setStatus('error');
- return alert('An HTTP error '+ xmlhttp.status +' occured.\n'+ acdb.uri);
- }
- // Parse back result
- var matches = parseJson(string);
- if (typeof matches['status'] == 'undefined' || matches['status'] != 0) {
- acdb.cache[acdb.searchString] = matches;
- acdb.owner.found(matches);
- acdb.owner.setStatus('found');
- }
+ // Ajax GET request for autocompletion
+ $.ajax({
+ type: "GET",
+ url: db.uri +'/'+ Drupal.encodeURIComponent(searchString),
+ success: function (xmlhttp) {
+ // Parse back result
+ var matches = Drupal.parseJson(xmlhttp.responseText);
+ if (typeof matches['status'] == 'undefined' || matches['status'] != 0) {
+ db.cache[searchString] = matches;
+ // Verify if these are still the matches the user wants to see
+ if (db.searchString == searchString) {
+ db.owner.found(matches);
+ }
+ db.owner.setStatus('found');
+ }
+ },
+ error: function (xmlhttp) {
+ alert('An HTTP error '+ xmlhttp.status +' occured.\n'+ db.uri);
+ }
+ });
+ }, this.delay);
}
/**
* Cancels the current autocomplete request
*/
-ACDB.prototype.cancel = function() {
+Drupal.ACDB.prototype.cancel = function() {
if (this.owner) this.owner.setStatus('cancel');
if (this.timer) clearTimeout(this.timer);
- if (this.transport) {
- this.transport.onreadystatechange = function() {};
- this.transport.abort();
- }
+ this.searchString = '';
+}
+
+// Global Killswitch
+if (Drupal.jsEnabled) {
+ $(document).ready(Drupal.autocompleteAutoAttach);
}