annotate misc/autocomplete.js @ 1:c1f4ac30525a 6.0

Drupal 6.0
author Franck Deroche <webmaster@defr.org>
date Tue, 23 Dec 2008 14:28:28 +0100
parents
children
rev   line source
webmaster@1 1 // $Id: autocomplete.js,v 1.23 2008/01/04 11:53:21 goba Exp $
webmaster@1 2
webmaster@1 3 /**
webmaster@1 4 * Attaches the autocomplete behavior to all required fields
webmaster@1 5 */
webmaster@1 6 Drupal.behaviors.autocomplete = function (context) {
webmaster@1 7 var acdb = [];
webmaster@1 8 $('input.autocomplete:not(.autocomplete-processed)', context).each(function () {
webmaster@1 9 var uri = this.value;
webmaster@1 10 if (!acdb[uri]) {
webmaster@1 11 acdb[uri] = new Drupal.ACDB(uri);
webmaster@1 12 }
webmaster@1 13 var input = $('#' + this.id.substr(0, this.id.length - 13))
webmaster@1 14 .attr('autocomplete', 'OFF')[0];
webmaster@1 15 $(input.form).submit(Drupal.autocompleteSubmit);
webmaster@1 16 new Drupal.jsAC(input, acdb[uri]);
webmaster@1 17 $(this).addClass('autocomplete-processed');
webmaster@1 18 });
webmaster@1 19 };
webmaster@1 20
webmaster@1 21 /**
webmaster@1 22 * Prevents the form from submitting if the suggestions popup is open
webmaster@1 23 * and closes the suggestions popup when doing so.
webmaster@1 24 */
webmaster@1 25 Drupal.autocompleteSubmit = function () {
webmaster@1 26 return $('#autocomplete').each(function () {
webmaster@1 27 this.owner.hidePopup();
webmaster@1 28 }).size() == 0;
webmaster@1 29 };
webmaster@1 30
webmaster@1 31 /**
webmaster@1 32 * An AutoComplete object
webmaster@1 33 */
webmaster@1 34 Drupal.jsAC = function (input, db) {
webmaster@1 35 var ac = this;
webmaster@1 36 this.input = input;
webmaster@1 37 this.db = db;
webmaster@1 38
webmaster@1 39 $(this.input)
webmaster@1 40 .keydown(function (event) { return ac.onkeydown(this, event); })
webmaster@1 41 .keyup(function (event) { ac.onkeyup(this, event); })
webmaster@1 42 .blur(function () { ac.hidePopup(); ac.db.cancel(); });
webmaster@1 43
webmaster@1 44 };
webmaster@1 45
webmaster@1 46 /**
webmaster@1 47 * Handler for the "keydown" event
webmaster@1 48 */
webmaster@1 49 Drupal.jsAC.prototype.onkeydown = function (input, e) {
webmaster@1 50 if (!e) {
webmaster@1 51 e = window.event;
webmaster@1 52 }
webmaster@1 53 switch (e.keyCode) {
webmaster@1 54 case 40: // down arrow
webmaster@1 55 this.selectDown();
webmaster@1 56 return false;
webmaster@1 57 case 38: // up arrow
webmaster@1 58 this.selectUp();
webmaster@1 59 return false;
webmaster@1 60 default: // all other keys
webmaster@1 61 return true;
webmaster@1 62 }
webmaster@1 63 };
webmaster@1 64
webmaster@1 65 /**
webmaster@1 66 * Handler for the "keyup" event
webmaster@1 67 */
webmaster@1 68 Drupal.jsAC.prototype.onkeyup = function (input, e) {
webmaster@1 69 if (!e) {
webmaster@1 70 e = window.event;
webmaster@1 71 }
webmaster@1 72 switch (e.keyCode) {
webmaster@1 73 case 16: // shift
webmaster@1 74 case 17: // ctrl
webmaster@1 75 case 18: // alt
webmaster@1 76 case 20: // caps lock
webmaster@1 77 case 33: // page up
webmaster@1 78 case 34: // page down
webmaster@1 79 case 35: // end
webmaster@1 80 case 36: // home
webmaster@1 81 case 37: // left arrow
webmaster@1 82 case 38: // up arrow
webmaster@1 83 case 39: // right arrow
webmaster@1 84 case 40: // down arrow
webmaster@1 85 return true;
webmaster@1 86
webmaster@1 87 case 9: // tab
webmaster@1 88 case 13: // enter
webmaster@1 89 case 27: // esc
webmaster@1 90 this.hidePopup(e.keyCode);
webmaster@1 91 return true;
webmaster@1 92
webmaster@1 93 default: // all other keys
webmaster@1 94 if (input.value.length > 0)
webmaster@1 95 this.populatePopup();
webmaster@1 96 else
webmaster@1 97 this.hidePopup(e.keyCode);
webmaster@1 98 return true;
webmaster@1 99 }
webmaster@1 100 };
webmaster@1 101
webmaster@1 102 /**
webmaster@1 103 * Puts the currently highlighted suggestion into the autocomplete field
webmaster@1 104 */
webmaster@1 105 Drupal.jsAC.prototype.select = function (node) {
webmaster@1 106 this.input.value = node.autocompleteValue;
webmaster@1 107 };
webmaster@1 108
webmaster@1 109 /**
webmaster@1 110 * Highlights the next suggestion
webmaster@1 111 */
webmaster@1 112 Drupal.jsAC.prototype.selectDown = function () {
webmaster@1 113 if (this.selected && this.selected.nextSibling) {
webmaster@1 114 this.highlight(this.selected.nextSibling);
webmaster@1 115 }
webmaster@1 116 else {
webmaster@1 117 var lis = $('li', this.popup);
webmaster@1 118 if (lis.size() > 0) {
webmaster@1 119 this.highlight(lis.get(0));
webmaster@1 120 }
webmaster@1 121 }
webmaster@1 122 };
webmaster@1 123
webmaster@1 124 /**
webmaster@1 125 * Highlights the previous suggestion
webmaster@1 126 */
webmaster@1 127 Drupal.jsAC.prototype.selectUp = function () {
webmaster@1 128 if (this.selected && this.selected.previousSibling) {
webmaster@1 129 this.highlight(this.selected.previousSibling);
webmaster@1 130 }
webmaster@1 131 };
webmaster@1 132
webmaster@1 133 /**
webmaster@1 134 * Highlights a suggestion
webmaster@1 135 */
webmaster@1 136 Drupal.jsAC.prototype.highlight = function (node) {
webmaster@1 137 if (this.selected) {
webmaster@1 138 $(this.selected).removeClass('selected');
webmaster@1 139 }
webmaster@1 140 $(node).addClass('selected');
webmaster@1 141 this.selected = node;
webmaster@1 142 };
webmaster@1 143
webmaster@1 144 /**
webmaster@1 145 * Unhighlights a suggestion
webmaster@1 146 */
webmaster@1 147 Drupal.jsAC.prototype.unhighlight = function (node) {
webmaster@1 148 $(node).removeClass('selected');
webmaster@1 149 this.selected = false;
webmaster@1 150 };
webmaster@1 151
webmaster@1 152 /**
webmaster@1 153 * Hides the autocomplete suggestions
webmaster@1 154 */
webmaster@1 155 Drupal.jsAC.prototype.hidePopup = function (keycode) {
webmaster@1 156 // Select item if the right key or mousebutton was pressed
webmaster@1 157 if (this.selected && ((keycode && keycode != 46 && keycode != 8 && keycode != 27) || !keycode)) {
webmaster@1 158 this.input.value = this.selected.autocompleteValue;
webmaster@1 159 }
webmaster@1 160 // Hide popup
webmaster@1 161 var popup = this.popup;
webmaster@1 162 if (popup) {
webmaster@1 163 this.popup = null;
webmaster@1 164 $(popup).fadeOut('fast', function() { $(popup).remove(); });
webmaster@1 165 }
webmaster@1 166 this.selected = false;
webmaster@1 167 };
webmaster@1 168
webmaster@1 169 /**
webmaster@1 170 * Positions the suggestions popup and starts a search
webmaster@1 171 */
webmaster@1 172 Drupal.jsAC.prototype.populatePopup = function () {
webmaster@1 173 // Show popup
webmaster@1 174 if (this.popup) {
webmaster@1 175 $(this.popup).remove();
webmaster@1 176 }
webmaster@1 177 this.selected = false;
webmaster@1 178 this.popup = document.createElement('div');
webmaster@1 179 this.popup.id = 'autocomplete';
webmaster@1 180 this.popup.owner = this;
webmaster@1 181 $(this.popup).css({
webmaster@1 182 marginTop: this.input.offsetHeight +'px',
webmaster@1 183 width: (this.input.offsetWidth - 4) +'px',
webmaster@1 184 display: 'none'
webmaster@1 185 });
webmaster@1 186 $(this.input).before(this.popup);
webmaster@1 187
webmaster@1 188 // Do search
webmaster@1 189 this.db.owner = this;
webmaster@1 190 this.db.search(this.input.value);
webmaster@1 191 };
webmaster@1 192
webmaster@1 193 /**
webmaster@1 194 * Fills the suggestion popup with any matches received
webmaster@1 195 */
webmaster@1 196 Drupal.jsAC.prototype.found = function (matches) {
webmaster@1 197 // If no value in the textfield, do not show the popup.
webmaster@1 198 if (!this.input.value.length) {
webmaster@1 199 return false;
webmaster@1 200 }
webmaster@1 201
webmaster@1 202 // Prepare matches
webmaster@1 203 var ul = document.createElement('ul');
webmaster@1 204 var ac = this;
webmaster@1 205 for (key in matches) {
webmaster@1 206 var li = document.createElement('li');
webmaster@1 207 $(li)
webmaster@1 208 .html('<div>'+ matches[key] +'</div>')
webmaster@1 209 .mousedown(function () { ac.select(this); })
webmaster@1 210 .mouseover(function () { ac.highlight(this); })
webmaster@1 211 .mouseout(function () { ac.unhighlight(this); });
webmaster@1 212 li.autocompleteValue = key;
webmaster@1 213 $(ul).append(li);
webmaster@1 214 }
webmaster@1 215
webmaster@1 216 // Show popup with matches, if any
webmaster@1 217 if (this.popup) {
webmaster@1 218 if (ul.childNodes.length > 0) {
webmaster@1 219 $(this.popup).empty().append(ul).show();
webmaster@1 220 }
webmaster@1 221 else {
webmaster@1 222 $(this.popup).css({visibility: 'hidden'});
webmaster@1 223 this.hidePopup();
webmaster@1 224 }
webmaster@1 225 }
webmaster@1 226 };
webmaster@1 227
webmaster@1 228 Drupal.jsAC.prototype.setStatus = function (status) {
webmaster@1 229 switch (status) {
webmaster@1 230 case 'begin':
webmaster@1 231 $(this.input).addClass('throbbing');
webmaster@1 232 break;
webmaster@1 233 case 'cancel':
webmaster@1 234 case 'error':
webmaster@1 235 case 'found':
webmaster@1 236 $(this.input).removeClass('throbbing');
webmaster@1 237 break;
webmaster@1 238 }
webmaster@1 239 };
webmaster@1 240
webmaster@1 241 /**
webmaster@1 242 * An AutoComplete DataBase object
webmaster@1 243 */
webmaster@1 244 Drupal.ACDB = function (uri) {
webmaster@1 245 this.uri = uri;
webmaster@1 246 this.delay = 300;
webmaster@1 247 this.cache = {};
webmaster@1 248 };
webmaster@1 249
webmaster@1 250 /**
webmaster@1 251 * Performs a cached and delayed search
webmaster@1 252 */
webmaster@1 253 Drupal.ACDB.prototype.search = function (searchString) {
webmaster@1 254 var db = this;
webmaster@1 255 this.searchString = searchString;
webmaster@1 256
webmaster@1 257 // See if this key has been searched for before
webmaster@1 258 if (this.cache[searchString]) {
webmaster@1 259 return this.owner.found(this.cache[searchString]);
webmaster@1 260 }
webmaster@1 261
webmaster@1 262 // Initiate delayed search
webmaster@1 263 if (this.timer) {
webmaster@1 264 clearTimeout(this.timer);
webmaster@1 265 }
webmaster@1 266 this.timer = setTimeout(function() {
webmaster@1 267 db.owner.setStatus('begin');
webmaster@1 268
webmaster@1 269 // Ajax GET request for autocompletion
webmaster@1 270 $.ajax({
webmaster@1 271 type: "GET",
webmaster@1 272 url: db.uri +'/'+ Drupal.encodeURIComponent(searchString),
webmaster@1 273 dataType: 'json',
webmaster@1 274 success: function (matches) {
webmaster@1 275 if (typeof matches['status'] == 'undefined' || matches['status'] != 0) {
webmaster@1 276 db.cache[searchString] = matches;
webmaster@1 277 // Verify if these are still the matches the user wants to see
webmaster@1 278 if (db.searchString == searchString) {
webmaster@1 279 db.owner.found(matches);
webmaster@1 280 }
webmaster@1 281 db.owner.setStatus('found');
webmaster@1 282 }
webmaster@1 283 },
webmaster@1 284 error: function (xmlhttp) {
webmaster@1 285 alert(Drupal.ahahError(xmlhttp, db.uri));
webmaster@1 286 }
webmaster@1 287 });
webmaster@1 288 }, this.delay);
webmaster@1 289 };
webmaster@1 290
webmaster@1 291 /**
webmaster@1 292 * Cancels the current autocomplete request
webmaster@1 293 */
webmaster@1 294 Drupal.ACDB.prototype.cancel = function() {
webmaster@1 295 if (this.owner) this.owner.setStatus('cancel');
webmaster@1 296 if (this.timer) clearTimeout(this.timer);
webmaster@1 297 this.searchString = '';
webmaster@1 298 };