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