1 /** |
1 /** |
2 * Javascript auto-completion for form fields. This supercedes the code in autocomplete.js for MOZILLA ONLY. It doesn't seem to work real |
2 * Javascript auto-completion for form fields. jQuery based in 1.1.5. |
3 * well with other browsers yet. |
3 * Different types of auto-completion fields can be defined (e.g. with different data sets). For each one, a schema |
|
4 * can be created describing how to draw each row. |
4 */ |
5 */ |
5 |
6 |
6 // fill schemas |
|
7 var autofill_schemas = {}; |
7 var autofill_schemas = {}; |
8 |
8 |
9 // default, generic schema |
9 /** |
|
10 * SCHEMA - GENERIC |
|
11 */ |
|
12 |
10 autofill_schemas.generic = { |
13 autofill_schemas.generic = { |
11 template: '<div id="--ID--_region" spry:region="autofill_ds_--CLASS--" class="tblholder">' + "\n" + |
14 init: function(element, fillclass, params) |
12 ' <table border="0" cellspacing="1" cellpadding="3" style="font-size: smaller;">' + "\n" + |
15 { |
13 ' <tr spry:repeat="autofill_region_--ID--">' + "\n" + |
16 $(element).autocomplete(makeUrlNS('Special', 'Autofill', 'type=' + fillclass) + '&userinput=', { |
14 ' <td class="row1" spry:suggest="{name}">{name}</td>' + "\n" + |
17 minChars: 3 |
15 ' </tr>' + "\n" + |
18 }); |
16 ' </table>' + "\n" + |
19 } |
17 '</div>', |
20 } |
|
21 |
|
22 autofill_schemas.username = { |
|
23 init: function(element, fillclass, params) |
|
24 { |
|
25 $(element).autocomplete(makeUrlNS('Special', 'Autofill', 'type=' + fillclass) + '&userinput=', { |
|
26 minChars: 3, |
|
27 formatItem: function(row, _, __) |
|
28 { |
|
29 var html = row.name_highlight + '<br />'; |
|
30 html += '<span style="' + row.rank_style + '">' + row.rank_title + '</span>'; |
|
31 return html; |
|
32 }, |
|
33 tableHeader: '<tr><th>' + $lang.get('user_autofill_heading_suggestions') + '</th></tr>', |
|
34 }); |
|
35 } |
|
36 } |
|
37 |
|
38 window.autofill_onload = function() |
|
39 { |
|
40 if ( this.loaded ) |
|
41 { |
|
42 return true; |
|
43 } |
18 |
44 |
19 init: function(element, fillclass) |
45 var inputs = document.getElementsByClassName('input', 'autofill'); |
|
46 |
|
47 if ( inputs.length > 0 ) |
20 { |
48 { |
21 // calculate positions before spry f***s everything up |
49 // we have at least one input that needs to be made an autofill element. |
22 var top = $dynano(element).Top() + $dynano(element).Height() - 10; // tblholder has 10px top margin |
50 // is spry data loaded? |
23 var left = $dynano(element).Left(); |
51 load_component('template-compiler'); |
24 |
|
25 // dataset name |
|
26 var ds_name = 'autofill_ds_' + fillclass; |
|
27 |
|
28 // setup the dataset |
|
29 window[ds_name] = new Spry.Data.JSONDataSet(makeUrlNS('Special', 'Autofill', 'type=' + fillclass)); |
|
30 |
|
31 // inject our HTML wrapper |
|
32 var template = this.template.replace(new RegExp('--ID--', 'g'), element.id).replace(new RegExp('--CLASS--', 'g', fillclass)); |
|
33 var wrapper = element.parentNode; // document.createElement('div'); |
|
34 if ( !wrapper.id ) |
|
35 wrapper.id = 'autofill_wrap_' + element.id; |
|
36 |
|
37 // a bunch of hacks to add a spry wrapper |
|
38 wrapper.innerHTML = template + wrapper.innerHTML; |
|
39 |
|
40 var autosuggest = new Spry.Widget.AutoSuggest(wrapper.id, element.id + '_region', window[ds_name], 'name', {loadFromServer: true, urlParam: 'userinput', hoverSuggestClass: 'row2', minCharsType: 3}); |
|
41 var regiondiv = document.getElementById(element.id + '_region'); |
|
42 regiondiv.style.position = 'absolute'; |
|
43 regiondiv.style.top = top + 'px'; |
|
44 regiondiv.style.left = left + 'px'; |
|
45 } |
52 } |
46 }; |
53 |
|
54 this.loaded = true; |
|
55 |
|
56 for ( var i = 0; i < inputs.length; i++ ) |
|
57 { |
|
58 autofill_init_element(inputs[i]); |
|
59 } |
|
60 } |
47 |
61 |
48 function autofill_init_element(element, params) |
62 function autofill_init_element(element, params) |
49 { |
63 { |
50 if ( !Spry.Data ); |
|
51 load_spry_data(); |
|
52 |
|
53 params = params || {}; |
64 params = params || {}; |
54 // assign an ID if it doesn't have one yet |
65 // assign an ID if it doesn't have one yet |
55 if ( !element.id ) |
66 if ( !element.id ) |
56 { |
67 { |
57 element.id = 'autofill_' + Math.floor(Math.random() * 100000); |
68 element.id = 'autofill_' + Math.floor(Math.random() * 100000); |
71 schema.init(element, fillclass, params); |
82 schema.init(element, fillclass, params); |
72 |
83 |
73 element.af_initted = true; |
84 element.af_initted = true; |
74 } |
85 } |
75 |
86 |
76 var autofill_onload = function() |
87 function AutofillUsername(el, allow_anon) |
77 { |
88 { |
78 if ( this.loaded ) |
89 el.onkeyup = null; |
|
90 el.className = 'autofill username'; |
|
91 autofill_init_element(el, { allow_anon: allow_anon }); |
|
92 } |
|
93 |
|
94 function AutofillPage(el) |
|
95 { |
|
96 el.onkeyup = null; |
|
97 el.className = 'autofill page'; |
|
98 autofill_init_element(el, {}); |
|
99 } |
|
100 |
|
101 addOnloadHook(function() |
79 { |
102 { |
80 return true; |
103 load_component('jquery'); |
81 } |
104 load_component('jquery-ui'); |
82 |
105 |
83 autofill_schemas.username = { |
106 if ( !window.jQuery ) |
84 template: '<div id="--ID--_region" spry:region="autofill_ds_username" class="tblholder">' + "\n" + |
|
85 ' <table border="0" cellspacing="1" cellpadding="3" style="font-size: smaller;">' + "\n" + |
|
86 ' <tr>' + "\n" + |
|
87 ' <th>' + $lang.get('user_autofill_heading_suggestions') + '</th>' + "\n" + |
|
88 ' </tr>' + "\n" + |
|
89 ' <tr spry:repeat="autofill_region_--ID--">' + "\n" + |
|
90 ' <td class="row1" spry:suggest="{name}">{name_highlight}<br /><small style="{rank_style}">{rank_title}</small></td>' + "\n" + |
|
91 ' </tr>' + "\n" + |
|
92 ' </table>' + "\n" + |
|
93 '</div>', |
|
94 |
|
95 init: function(element, fillclass, params) |
|
96 { |
107 { |
97 // calculate positions before spry f***s everything up |
108 throw('jQuery didn\'t load properly. Aborting auto-complete init.'); |
98 var top = $dynano(element).Top() + $dynano(element).Height() - 10; // tblholder has 10px top margin |
109 } |
99 var left = $dynano(element).Left(); |
110 |
|
111 jQuery.autocomplete = function(input, options) { |
|
112 // Create a link to self |
|
113 var me = this; |
|
114 |
|
115 // Create jQuery object for input element |
|
116 var $input = $(input).attr("autocomplete", "off"); |
|
117 |
|
118 // Apply inputClass if necessary |
|
119 if (options.inputClass) { |
|
120 $input.addClass(options.inputClass); |
|
121 } |
|
122 |
|
123 // Create results |
|
124 var results = document.createElement("div"); |
|
125 $(results).addClass('tblholder').css('z-index', getHighestZ() + 1).css('margin-top', 0); |
|
126 |
|
127 // Create jQuery object for results |
|
128 // var $results = $(results); |
|
129 var $results = $(results).hide().addClass(options.resultsClass).css("position", "absolute"); |
|
130 if( options.width > 0 ) { |
|
131 $results.css("width", options.width); |
|
132 } |
|
133 else |
|
134 { |
|
135 $results.css("width", "200px"); |
|
136 } |
|
137 |
|
138 // Add to body element |
|
139 $("body").append(results); |
|
140 |
|
141 input.autocompleter = me; |
|
142 |
|
143 var timeout = null; |
|
144 var prev = ""; |
|
145 var active = -1; |
|
146 var cache = {}; |
|
147 var keyb = false; |
|
148 var hasFocus = false; |
|
149 var lastKeyPressCode = null; |
|
150 var mouseDownOnSelect = false; |
|
151 var hidingResults = false; |
|
152 |
|
153 // flush cache |
|
154 function flushCache(){ |
|
155 cache = {}; |
|
156 cache.data = {}; |
|
157 cache.length = 0; |
|
158 }; |
|
159 |
|
160 // flush cache |
|
161 flushCache(); |
|
162 |
|
163 // if there is a data array supplied |
|
164 if( options.data != null ){ |
|
165 var sFirstChar = "", stMatchSets = {}, row = []; |
|
166 |
|
167 // no url was specified, we need to adjust the cache length to make sure it fits the local data store |
|
168 if( typeof options.url != "string" ) { |
|
169 options.cacheLength = 1; |
|
170 } |
|
171 |
|
172 // loop through the array and create a lookup structure |
|
173 for( var i=0; i < options.data.length; i++ ){ |
|
174 // if row is a string, make an array otherwise just reference the array |
|
175 row = ((typeof options.data[i] == "string") ? [options.data[i]] : options.data[i]); |
|
176 |
|
177 // if the length is zero, don't add to list |
|
178 if( row[0].length > 0 ){ |
|
179 // get the first character |
|
180 sFirstChar = row[0].substring(0, 1).toLowerCase(); |
|
181 // if no lookup array for this character exists, look it up now |
|
182 if( !stMatchSets[sFirstChar] ) stMatchSets[sFirstChar] = []; |
|
183 // if the match is a string |
|
184 stMatchSets[sFirstChar].push(row); |
|
185 } |
|
186 } |
|
187 |
|
188 // add the data items to the cache |
|
189 for( var k in stMatchSets ) { |
|
190 // increase the cache size |
|
191 options.cacheLength++; |
|
192 // add to the cache |
|
193 addToCache(k, stMatchSets[k]); |
|
194 } |
|
195 } |
|
196 |
|
197 $input |
|
198 .keydown(function(e) { |
|
199 // track last key pressed |
|
200 lastKeyPressCode = e.keyCode; |
|
201 switch(e.keyCode) { |
|
202 case 38: // up |
|
203 e.preventDefault(); |
|
204 moveSelect(-1); |
|
205 break; |
|
206 case 40: // down |
|
207 e.preventDefault(); |
|
208 moveSelect(1); |
|
209 break; |
|
210 case 9: // tab |
|
211 case 13: // return |
|
212 if( selectCurrent() ){ |
|
213 // make sure to blur off the current field |
|
214 $input.get(0).blur(); |
|
215 e.preventDefault(); |
|
216 } |
|
217 break; |
|
218 default: |
|
219 active = -1; |
|
220 if (timeout) clearTimeout(timeout); |
|
221 timeout = setTimeout(function(){onChange();}, options.delay); |
|
222 break; |
|
223 } |
|
224 }) |
|
225 .focus(function(){ |
|
226 // track whether the field has focus, we shouldn't process any results if the field no longer has focus |
|
227 hasFocus = true; |
|
228 }) |
|
229 .blur(function() { |
|
230 // track whether the field has focus |
|
231 hasFocus = false; |
|
232 if (!mouseDownOnSelect) { |
|
233 hideResults(); |
|
234 } |
|
235 }); |
|
236 |
|
237 hideResultsNow(); |
|
238 |
|
239 function onChange() { |
|
240 // ignore if the following keys are pressed: [del] [shift] [capslock] |
|
241 if( lastKeyPressCode == 46 || (lastKeyPressCode > 8 && lastKeyPressCode < 32) ) return $results.hide(); |
|
242 var v = $input.val(); |
|
243 if (v == prev) return; |
|
244 prev = v; |
|
245 if (v.length >= options.minChars) { |
|
246 $input.addClass(options.loadingClass); |
|
247 requestData(v); |
|
248 } else { |
|
249 $input.removeClass(options.loadingClass); |
|
250 $results.hide(); |
|
251 } |
|
252 }; |
|
253 |
|
254 function moveSelect(step) { |
|
255 |
|
256 var lis = $("td", results); |
|
257 if (!lis) return; |
|
258 |
|
259 active += step; |
|
260 |
|
261 if (active < 0) { |
|
262 active = 0; |
|
263 } else if (active >= lis.size()) { |
|
264 active = lis.size() - 1; |
|
265 } |
|
266 |
|
267 lis.removeClass("row2"); |
|
268 |
|
269 $(lis[active]).addClass("row2"); |
|
270 |
|
271 // Weird behaviour in IE |
|
272 // if (lis[active] && lis[active].scrollIntoView) { |
|
273 // lis[active].scrollIntoView(false); |
|
274 // } |
|
275 |
|
276 }; |
|
277 |
|
278 function selectCurrent() { |
|
279 var li = $("td.row2", results)[0]; |
|
280 if (!li) { |
|
281 var $li = $("td", results); |
|
282 if (options.selectOnly) { |
|
283 if ($li.length == 1) li = $li[0]; |
|
284 } else if (options.selectFirst) { |
|
285 li = $li[0]; |
|
286 } |
|
287 } |
|
288 if (li) { |
|
289 selectItem(li); |
|
290 return true; |
|
291 } else { |
|
292 return false; |
|
293 } |
|
294 }; |
|
295 |
|
296 function selectItem(li) { |
|
297 if (!li) { |
|
298 li = document.createElement("li"); |
|
299 li.extra = []; |
|
300 li.selectValue = ""; |
|
301 } |
|
302 var v = $.trim(li.selectValue ? li.selectValue : li.innerHTML); |
|
303 input.lastSelected = v; |
|
304 prev = v; |
|
305 $results.html(""); |
|
306 $input.val(v); |
|
307 hideResultsNow(); |
|
308 if (options.onItemSelect) { |
|
309 setTimeout(function() { options.onItemSelect(li) }, 1); |
|
310 } |
|
311 }; |
|
312 |
|
313 // selects a portion of the input string |
|
314 function createSelection(start, end){ |
|
315 // get a reference to the input element |
|
316 var field = $input.get(0); |
|
317 if( field.createTextRange ){ |
|
318 var selRange = field.createTextRange(); |
|
319 selRange.collapse(true); |
|
320 selRange.moveStart("character", start); |
|
321 selRange.moveEnd("character", end); |
|
322 selRange.select(); |
|
323 } else if( field.setSelectionRange ){ |
|
324 field.setSelectionRange(start, end); |
|
325 } else { |
|
326 if( field.selectionStart ){ |
|
327 field.selectionStart = start; |
|
328 field.selectionEnd = end; |
|
329 } |
|
330 } |
|
331 field.focus(); |
|
332 }; |
|
333 |
|
334 // fills in the input box w/the first match (assumed to be the best match) |
|
335 function autoFill(sValue){ |
|
336 // if the last user key pressed was backspace, don't autofill |
|
337 if( lastKeyPressCode != 8 ){ |
|
338 // fill in the value (keep the case the user has typed) |
|
339 $input.val($input.val() + sValue.substring(prev.length)); |
|
340 // select the portion of the value not typed by the user (so the next character will erase) |
|
341 createSelection(prev.length, sValue.length); |
|
342 } |
|
343 }; |
|
344 |
|
345 function showResults() { |
|
346 // get the position of the input field right now (in case the DOM is shifted) |
|
347 var pos = findPos(input); |
|
348 // either use the specified width, or autocalculate based on form element |
|
349 var iWidth = (options.width > 0) ? options.width : $input.width(); |
|
350 // reposition |
|
351 $results.css({ |
|
352 width: parseInt(iWidth) + "px", |
|
353 top: (pos.y + input.offsetHeight) + "px", |
|
354 left: pos.x + "px" |
|
355 }); |
|
356 if ( !$results.is(":visible") ) |
|
357 { |
|
358 $results.show("blind", {}, 350); |
|
359 } |
|
360 }; |
|
361 |
|
362 function hideResults() { |
|
363 if (timeout) clearTimeout(timeout); |
|
364 timeout = setTimeout(hideResultsNow, 200); |
|
365 }; |
|
366 |
|
367 function hideResultsNow() { |
|
368 if (hidingResults) { |
|
369 return; |
|
370 } |
|
371 hidingResults = true; |
100 |
372 |
101 var allow_anon = ( params.allow_anon ) ? '1' : '0'; |
373 if (timeout) { |
102 // setup the dataset |
374 clearTimeout(timeout); |
103 if ( !window.autofill_ds_username ) |
375 } |
104 { |
376 |
105 window.autofill_ds_username = new Spry.Data.JSONDataSet(makeUrlNS('Special', 'Autofill', 'type=' + fillclass + '&allow_anon' + allow_anon)); |
377 var v = $input.removeClass(options.loadingClass).val(); |
|
378 |
|
379 if ($results.is(":visible")) { |
|
380 $results.hide(); |
|
381 } |
|
382 |
|
383 if (options.mustMatch) { |
|
384 if (!input.lastSelected || input.lastSelected != v) { |
|
385 selectItem(null); |
|
386 } |
|
387 } |
|
388 |
|
389 hidingResults = false; |
|
390 }; |
|
391 |
|
392 function receiveData(q, data) { |
|
393 if (data) { |
|
394 $input.removeClass(options.loadingClass); |
|
395 results.innerHTML = ""; |
|
396 |
|
397 // if the field no longer has focus or if there are no matches, do not display the drop down |
|
398 if( !hasFocus || data.length == 0 ) return hideResultsNow(); |
|
399 |
|
400 if ($.browser.msie) { |
|
401 // we put a styled iframe behind the calendar so HTML SELECT elements don't show through |
|
402 $results.append(document.createElement('iframe')); |
|
403 } |
|
404 results.appendChild(dataToDom(data)); |
|
405 // autofill in the complete box w/the first match as long as the user hasn't entered in more data |
|
406 if( options.autoFill && ($input.val().toLowerCase() == q.toLowerCase()) ) autoFill(data[0][0]); |
|
407 showResults(); |
|
408 } else { |
|
409 hideResultsNow(); |
|
410 } |
|
411 }; |
|
412 |
|
413 function parseData(data) { |
|
414 if (!data) return null; |
|
415 var parsed = parseJSON(data); |
|
416 return parsed; |
|
417 }; |
|
418 |
|
419 function dataToDom(data) { |
|
420 var ul = document.createElement("table"); |
|
421 $(ul).attr("border", "0").attr("cellspacing", "1").attr("cellpadding", "3"); |
|
422 var num = data.length; |
|
423 |
|
424 if ( options.tableHeader ) |
|
425 { |
|
426 ul.innerHTML = options.tableHeader; |
|
427 } |
|
428 |
|
429 // limited results to a max number |
|
430 if( (options.maxItemsToShow > 0) && (options.maxItemsToShow < num) ) num = options.maxItemsToShow; |
|
431 |
|
432 for (var i=0; i < num; i++) { |
|
433 var row = data[i]; |
|
434 if (!row) continue; |
|
435 |
|
436 var li = document.createElement("tr"); |
|
437 var td = document.createElement("td"); |
|
438 td.selectValue = row[0]; |
|
439 $(td).addClass('row1'); |
|
440 $(td).css("font-size", "smaller"); |
|
441 console.debug(ul, li, td); |
|
442 |
|
443 if ( options.formatItem ) |
|
444 { |
|
445 td.innerHTML = options.formatItem(row, i, num); |
|
446 } |
|
447 else |
|
448 { |
|
449 td.innerHTML = row[0]; |
|
450 } |
|
451 li.appendChild(td); |
|
452 var extra = null; |
|
453 if (row.length > 1) { |
|
454 extra = []; |
|
455 for (var j=1; j < row.length; j++) { |
|
456 extra[extra.length] = row[j]; |
|
457 } |
|
458 } |
|
459 td.extra = extra; |
|
460 ul.appendChild(li); |
|
461 |
|
462 $(td).hover( |
|
463 function() { $("tr", ul).removeClass("row2"); $(this).addClass("row2"); active = $("tr", ul).indexOf($(this).get(0)); }, |
|
464 function() { $(this).removeClass("row2"); } |
|
465 ).click(function(e) { |
|
466 e.preventDefault(); |
|
467 e.stopPropagation(); |
|
468 selectItem(this) |
|
469 }); |
|
470 |
|
471 /* |
|
472 var li = document.createElement("li"); |
|
473 if (options.formatItem) { |
|
474 li.innerHTML = options.formatItem(row, i, num); |
|
475 li.selectValue = row[0]; |
|
476 } else { |
|
477 li.innerHTML = row[0]; |
|
478 li.selectValue = row[0]; |
|
479 } |
|
480 var extra = null; |
|
481 if (row.length > 1) { |
|
482 extra = []; |
|
483 for (var j=1; j < row.length; j++) { |
|
484 extra[extra.length] = row[j]; |
|
485 } |
|
486 } |
|
487 li.extra = extra; |
|
488 ul.appendChild(li); |
|
489 |
|
490 $(li).hover( |
|
491 function() { $("li", ul).removeClass("ac_over"); $(this).addClass("ac_over"); active = $("li", ul).indexOf($(this).get(0)); }, |
|
492 function() { $(this).removeClass("ac_over"); } |
|
493 ).click(function(e) { |
|
494 e.preventDefault(); |
|
495 e.stopPropagation(); |
|
496 selectItem(this) |
|
497 }); |
|
498 */ |
|
499 |
|
500 } |
|
501 $(ul).mousedown(function() { |
|
502 mouseDownOnSelect = true; |
|
503 }).mouseup(function() { |
|
504 mouseDownOnSelect = false; |
|
505 }); |
|
506 return ul; |
|
507 }; |
|
508 |
|
509 function requestData(q) { |
|
510 if (!options.matchCase) q = q.toLowerCase(); |
|
511 var data = options.cacheLength ? loadFromCache(q) : null; |
|
512 // recieve the cached data |
|
513 if (data) { |
|
514 receiveData(q, data); |
|
515 // if an AJAX url has been supplied, try loading the data now |
|
516 } else if( (typeof options.url == "string") && (options.url.length > 0) ){ |
|
517 $.get(makeUrl(q), function(data) { |
|
518 data = parseData(data); |
|
519 addToCache(q, data); |
|
520 receiveData(q, data); |
|
521 }); |
|
522 // if there's been no data found, remove the loading class |
|
523 } else { |
|
524 $input.removeClass(options.loadingClass); |
|
525 } |
|
526 }; |
|
527 |
|
528 function makeUrl(q) { |
|
529 var sep = options.url.indexOf('?') == -1 ? '?' : '&'; |
|
530 var url = options.url + encodeURI(q); |
|
531 for (var i in options.extraParams) { |
|
532 url += "&" + i + "=" + encodeURI(options.extraParams[i]); |
|
533 } |
|
534 return url; |
|
535 }; |
|
536 |
|
537 function loadFromCache(q) { |
|
538 if (!q) return null; |
|
539 if (cache.data[q]) return cache.data[q]; |
|
540 if (options.matchSubset) { |
|
541 for (var i = q.length - 1; i >= options.minChars; i--) { |
|
542 var qs = q.substr(0, i); |
|
543 var c = cache.data[qs]; |
|
544 if (c) { |
|
545 var csub = []; |
|
546 for (var j = 0; j < c.length; j++) { |
|
547 var x = c[j]; |
|
548 var x0 = x[0]; |
|
549 if (matchSubset(x0, q)) { |
|
550 csub[csub.length] = x; |
|
551 } |
|
552 } |
|
553 return csub; |
|
554 } |
|
555 } |
|
556 } |
|
557 return null; |
|
558 }; |
|
559 |
|
560 function matchSubset(s, sub) { |
|
561 if (!options.matchCase) s = s.toLowerCase(); |
|
562 var i = s.indexOf(sub); |
|
563 if (i == -1) return false; |
|
564 return i == 0 || options.matchContains; |
|
565 }; |
|
566 |
|
567 this.flushCache = function() { |
|
568 flushCache(); |
|
569 }; |
|
570 |
|
571 this.setExtraParams = function(p) { |
|
572 options.extraParams = p; |
|
573 }; |
|
574 |
|
575 this.findValue = function(){ |
|
576 var q = $input.val(); |
|
577 |
|
578 if (!options.matchCase) q = q.toLowerCase(); |
|
579 var data = options.cacheLength ? loadFromCache(q) : null; |
|
580 if (data) { |
|
581 findValueCallback(q, data); |
|
582 } else if( (typeof options.url == "string") && (options.url.length > 0) ){ |
|
583 $.get(makeUrl(q), function(data) { |
|
584 data = parseData(data) |
|
585 addToCache(q, data); |
|
586 findValueCallback(q, data); |
|
587 }); |
|
588 } else { |
|
589 // no matches |
|
590 findValueCallback(q, null); |
|
591 } |
106 } |
592 } |
107 |
593 |
108 // inject our HTML wrapper |
594 function findValueCallback(q, data){ |
109 var template = this.template.replace(new RegExp('--ID--', 'g'), element.id); |
595 if (data) $input.removeClass(options.loadingClass); |
110 var wrapper = element.parentNode; // document.createElement('div'); |
596 |
111 if ( !wrapper.id ) |
597 var num = (data) ? data.length : 0; |
112 wrapper.id = 'autofill_wrap_' + element.id; |
598 var li = null; |
113 |
599 |
114 // a bunch of hacks to add a spry wrapper |
600 for (var i=0; i < num; i++) { |
115 wrapper.innerHTML = template + wrapper.innerHTML; |
601 var row = data[i]; |
116 |
602 |
117 var autosuggest = new Spry.Widget.AutoSuggest(wrapper.id, element.id + '_region', window.autofill_ds_username, 'name', {loadFromServer: true, urlParam: 'userinput', hoverSuggestClass: 'row2', minCharsType: 3}); |
603 if( row[0].toLowerCase() == q.toLowerCase() ){ |
118 var regiondiv = document.getElementById(element.id + '_region'); |
604 li = document.createElement("li"); |
119 regiondiv.style.position = 'absolute'; |
605 if (options.formatItem) { |
120 regiondiv.style.top = top + 'px'; |
606 li.innerHTML = options.formatItem(row, i, num); |
121 regiondiv.style.left = left + 'px'; |
607 li.selectValue = row[0]; |
|
608 } else { |
|
609 li.innerHTML = row[0]; |
|
610 li.selectValue = row[0]; |
|
611 } |
|
612 var extra = null; |
|
613 if( row.length > 1 ){ |
|
614 extra = []; |
|
615 for (var j=1; j < row.length; j++) { |
|
616 extra[extra.length] = row[j]; |
|
617 } |
|
618 } |
|
619 li.extra = extra; |
|
620 } |
|
621 } |
|
622 |
|
623 if( options.onFindValue ) setTimeout(function() { options.onFindValue(li) }, 1); |
|
624 } |
|
625 |
|
626 function addToCache(q, data) { |
|
627 if (!data || !q || !options.cacheLength) return; |
|
628 if (!cache.length || cache.length > options.cacheLength) { |
|
629 flushCache(); |
|
630 cache.length++; |
|
631 } else if (!cache[q]) { |
|
632 cache.length++; |
|
633 } |
|
634 cache.data[q] = data; |
|
635 }; |
|
636 |
|
637 function findPos(obj) { |
|
638 var curleft = obj.offsetLeft || 0; |
|
639 var curtop = obj.offsetTop || 0; |
|
640 while (obj = obj.offsetParent) { |
|
641 curleft += obj.offsetLeft |
|
642 curtop += obj.offsetTop |
|
643 } |
|
644 return {x:curleft,y:curtop}; |
|
645 } |
122 } |
646 } |
123 }; |
647 |
124 |
648 jQuery.fn.autocomplete = function(url, options, data) { |
125 autofill_schemas.page = { |
649 // Make sure options exists |
126 template: '<div id="--ID--_region" spry:region="autofill_region_--ID--" class="tblholder">' + "\n" + |
650 options = options || {}; |
127 ' <table border="0" cellspacing="1" cellpadding="3" style="font-size: smaller;">' + "\n" + |
651 // Set url as option |
128 ' <tr>' + "\n" + |
652 options.url = url; |
129 ' <th colspan="2">' + $lang.get('page_autosuggest_heading') + '</th>' + "\n" + |
653 // set some bulk local data |
130 ' </tr>' + "\n" + |
654 options.data = ((typeof data == "object") && (data.constructor == Array)) ? data : null; |
131 ' <tr spry:repeat="autofill_region_--ID--">' + "\n" + |
655 |
132 ' <td class="row1" spry:suggest="{page_id}">{pid_highlight}<br /><small>{name_highlight}</small></td>' + "\n" + |
656 // Set default values for required options |
133 ' </tr>' + "\n" + |
657 options = $.extend({ |
134 ' </table>' + "\n" + |
658 inputClass: "ac_input", |
135 '</div>' |
659 resultsClass: "ac_results", |
136 } |
660 lineSeparator: "\n", |
137 |
661 cellSeparator: "|", |
138 var inputs = document.getElementsByClassName('input', 'autofill'); |
662 minChars: 1, |
139 |
663 delay: 400, |
140 if ( inputs.length > 0 ) |
664 matchCase: 0, |
141 { |
665 matchSubset: 1, |
142 // we have at least one input that needs to be made an autofill element. |
666 matchContains: 0, |
143 // is spry data loaded? |
667 cacheLength: 1, |
144 if ( !Spry.Data ) |
668 mustMatch: 0, |
145 { |
669 extraParams: {}, |
146 load_spry_data(); |
670 loadingClass: "ac_loading", |
147 return true; |
671 selectFirst: false, |
|
672 selectOnly: false, |
|
673 maxItemsToShow: -1, |
|
674 autoFill: false, |
|
675 width: 0 |
|
676 }, options); |
|
677 options.width = parseInt(options.width, 10); |
|
678 |
|
679 this.each(function() { |
|
680 var input = this; |
|
681 new jQuery.autocomplete(input, options); |
|
682 }); |
|
683 |
|
684 // Don't break the chain |
|
685 return this; |
148 } |
686 } |
149 } |
687 |
150 |
688 jQuery.fn.autocompleteArray = function(data, options) { |
151 this.loaded = true; |
689 return this.autocomplete(null, options, data); |
152 |
690 } |
153 for ( var i = 0; i < inputs.length; i++ ) |
691 |
154 { |
692 jQuery.fn.indexOf = function(e){ |
155 autofill_init_element(inputs[i]); |
693 for( var i=0; i<this.length; i++ ){ |
156 } |
694 if( this[i] == e ) return i; |
157 } |
695 } |
158 |
696 return -1; |
159 addOnloadHook(autofill_onload); |
697 }; |
160 |
698 |
161 function autofill_force_region_refresh() |
699 autofill_onload(); |
162 { |
700 }); |
163 Spry.Data.initRegions(); |
|
164 } |
|
165 |
|
166 function AutofillUsername(element, event, allowanon) |
|
167 { |
|
168 element.onkeyup = element.onkeydown = element.onkeypress = function(e) {}; |
|
169 |
|
170 element.className = 'autofill username'; |
|
171 |
|
172 allowanon = allowanon ? true : false; |
|
173 autofill_init_element(element, { |
|
174 allow_anon: allowanon |
|
175 }); |
|
176 } |
|
177 |
|
178 // load spry data components |
|
179 function load_spry_data() |
|
180 { |
|
181 var scripts = [ 'SpryData.js', 'SpryJSONDataSet.js', 'SpryAutoSuggest.js' ]; |
|
182 for ( var i = 0; i < scripts.length; i++ ) |
|
183 { |
|
184 load_component(scripts[i]); |
|
185 } |
|
186 autofill_onload(); |
|
187 } |
|