2 * Interface Elements for jQuery
5 * http://interface.eyecon.ro
7 * Copyright (c) 2006 Stefan Petre
8 * Dual licensed under the MIT (MIT-LICENSE.txt)
9 * and GPL (GPL-LICENSE.txt) licenses.
14 * Attach AJAX driven autocomplete/sugestion box to text input fields.
19 * @description Attach AJAX driven autocomplete/sugestion box to text input fields.
20 * @param Hash hash A hash of parameters
21 * @option String source the URL to request
22 * @option Integer delay (optional) the delayed time to start the AJAX request
23 * @option Boolean autofill (optional) when true the first sugested value fills the input
24 * @option String helperClass (optional) the CSS class applied to sugestion box
25 * @option String selectClass (optional) the CSS class applied to selected/hovered item
26 * @option Integer minchars (optional) the number of characters needed before starting AJAX request
27 * @option Hash fx (optional) {type:[slide|blind|fade]; duration: integer} the fx type to apply to sugestion box and duration for that fx
28 * @option Function onSelect (optional) A function to be executed whenever an item it is selected
29 * @option Function onShow (optional) A function to be executed whenever the suggection box is displayed
30 * @option Function onHide (optional) A function to be executed whenever the suggection box is hidden
31 * @option Function onHighlight (optional) A function to be executed whenever an item it is highlighted
34 * @cat Plugins/Interface
35 * @author Stefan Petre
50 jQuery.iAuto.content.empty();
51 if (jQuery.iAuto.iframe) {
52 jQuery.iAuto.iframe.hide();
58 jQuery.iAuto.items = null;
59 jQuery.iAuto.selectedItem = null;
60 jQuery.iAuto.lastValue = jQuery.iAuto.subject.value;
61 if(jQuery.iAuto.helper.css('display') == 'block') {
62 if (jQuery.iAuto.subject.autoCFG.fx) {
63 switch(jQuery.iAuto.subject.autoCFG.fx.type) {
65 jQuery.iAuto.helper.fadeOut(jQuery.iAuto.subject.autoCFG.fx.duration, jQuery.iAuto.empty);
68 jQuery.iAuto.helper.SlideOutUp(jQuery.iAuto.subject.autoCFG.fx.duration, jQuery.iAuto.empty);
71 jQuery.iAuto.helper.BlindUp(jQuery.iAuto.subject.autoCFG.fx.duration, jQuery.iAuto.empty);
75 jQuery.iAuto.helper.hide();
77 if (jQuery.iAuto.subject.autoCFG.onHide)
78 jQuery.iAuto.subject.autoCFG.onHide.apply(jQuery.iAuto.subject, [jQuery.iAuto.helper, jQuery.iAuto.iframe]);
82 window.clearTimeout(jQuery.iAuto.timer);
87 var subject = jQuery.iAuto.subject;
88 var subjectValue = jQuery.iAuto.getFieldValues(subject);
89 //var selectionStart = jQuery.iAuto.getSelectionStart(subject);
90 if (subject && subjectValue.item != jQuery.iAuto.lastValue && subjectValue.item.length >= subject.autoCFG.minchars) {
91 jQuery.iAuto.lastValue = subjectValue.item;
92 jQuery.iAuto.currentValue = subjectValue.item;
95 field: jQuery(subject).attr('name')||'field',
96 value: subjectValue.item
102 data: jQuery.param(data),
103 success: function(xml)
105 subject.autoCFG.lastSuggestion = jQuery('item',xml);
106 size = subject.autoCFG.lastSuggestion.size();
109 subject.autoCFG.lastSuggestion.each(
112 toWrite += '<li rel="' + jQuery('value', this).text() + '" dir="' + nr + '" style="cursor: default;">' + jQuery('text', this).text() + '</li>';
115 if (subject.autoCFG.autofill) {
116 var valueToAdd = jQuery('value', subject.autoCFG.lastSuggestion.get(0)).text();
117 subject.value = subjectValue.pre + valueToAdd + subject.autoCFG.multipleSeparator + subjectValue.post;
118 jQuery.iAuto.selection(
120 subjectValue.item.length != valueToAdd.length ? (subjectValue.pre.length + subjectValue.item.length) : valueToAdd.length,
121 subjectValue.item.length != valueToAdd.length ? (subjectValue.pre.length + valueToAdd.length) : valueToAdd.length
126 jQuery.iAuto.writeItems(subject, toWrite);
128 jQuery.iAuto.clear();
131 jQuery.iAuto.clear();
134 url : subject.autoCFG.source
140 writeItems : function(subject, toWrite)
142 jQuery.iAuto.content.html(toWrite);
143 jQuery.iAuto.items = jQuery('li', jQuery.iAuto.content.get(0));
145 .mouseover(jQuery.iAuto.hoverItem)
146 .bind('click', jQuery.iAuto.clickItem);
147 var position = jQuery.iUtil.getPosition(subject);
148 var size = jQuery.iUtil.getSize(subject);
150 .css('top', position.y + size.hb + 'px')
151 .css('left', position.x + 'px')
152 .addClass(subject.autoCFG.helperClass);
153 if (jQuery.iAuto.iframe) {
155 .css('display', 'block')
156 .css('top', position.y + size.hb + 'px')
157 .css('left', position.x + 'px')
158 .css('width', jQuery.iAuto.helper.css('width'))
159 .css('height', jQuery.iAuto.helper.css('height'));
161 jQuery.iAuto.selectedItem = 0;
162 jQuery.iAuto.items.get(0).className = subject.autoCFG.selectClass;
163 jQuery.iAuto.applyOn(subject,subject.autoCFG.lastSuggestion.get(0), 'onHighlight');
165 if (jQuery.iAuto.helper.css('display') == 'none') {
166 if (subject.autoCFG.inputWidth) {
167 var borders = jQuery.iUtil.getPadding(subject, true);
168 var paddings = jQuery.iUtil.getBorder(subject, true);
169 jQuery.iAuto.helper.css('width', subject.offsetWidth - (jQuery.boxModel ? (borders.l + borders.r + paddings.l + paddings.r) : 0 ) + 'px');
171 if (subject.autoCFG.fx) {
172 switch(subject.autoCFG.fx.type) {
174 jQuery.iAuto.helper.fadeIn(subject.autoCFG.fx.duration);
177 jQuery.iAuto.helper.SlideInUp(subject.autoCFG.fx.duration);
180 jQuery.iAuto.helper.BlindDown(subject.autoCFG.fx.duration);
184 jQuery.iAuto.helper.show();
187 if (jQuery.iAuto.subject.autoCFG.onShow)
188 jQuery.iAuto.subject.autoCFG.onShow.apply(jQuery.iAuto.subject, [jQuery.iAuto.helper, jQuery.iAuto.iframe]);
192 checkCache : function()
195 if (subject.autoCFG.lastSuggestion) {
197 jQuery.iAuto.lastValue = subject.value;
198 jQuery.iAuto.currentValue = subject.value;
201 subject.autoCFG.lastSuggestion.each(
204 value = jQuery('value', this).text().toLowerCase();
205 inputValue = subject.value.toLowerCase();
206 if (value.indexOf(inputValue) == 0) {
207 toWrite += '<li rel="' + jQuery('value', this).text() + '" dir="' + nr + '" style="cursor: default;">' + jQuery('text', this).text() + '</li>';
213 jQuery.iAuto.writeItems(subject, toWrite);
215 this.autoCFG.inCache = true;
219 subject.autoCFG.lastSuggestion = null;
220 this.autoCFG.inCache = false;
223 selection : function(field, start, end)
225 if (field.createTextRange) {
226 var selRange = field.createTextRange();
227 selRange.collapse(true);
228 selRange.moveStart("character", start);
229 selRange.moveEnd("character", - end + start);
231 } else if (field.setSelectionRange) {
232 field.setSelectionRange(start, end);
234 if (field.selectionStart) {
235 field.selectionStart = start;
236 field.selectionEnd = end;
242 getSelectionStart : function(field)
244 if (field.selectionStart)
245 return field.selectionStart;
246 else if(field.createTextRange) {
247 var selRange = document.selection.createRange();
248 var selRange2 = selRange.duplicate();
249 return 0 - selRange2.moveStart('character', -100000);
250 //result.end = result.start + range.text.length;
251 /*var selRange = document.selection.createRange();
252 var isCollapsed = selRange.compareEndPoints("StartToEnd", selRange) == 0;
254 selRange.collapse(true);
255 var bookmark = selRange.getBookmark();
256 return bookmark.charCodeAt(2) - 2;*/
260 getFieldValues : function(field)
269 if(field.autoCFG.multiple) {
270 var finishedPre = false;
271 var selectionStart = jQuery.iAuto.getSelectionStart(field)||0;
272 var chunks = fieldData.value.split(field.autoCFG.multipleSeparator);
273 for (var i=0; i<chunks.length; i++) {
275 (fieldData.pre.length + chunks[i].length >= selectionStart
281 if (fieldData.pre.length <= selectionStart)
282 fieldData.item = chunks[i];
284 fieldData.post += chunks[i] + (chunks[i] != '' ? field.autoCFG.multipleSeparator : '');
286 } else if (finishedPre){
287 fieldData.post += chunks[i] + (chunks[i] != '' ? field.autoCFG.multipleSeparator : '');
290 fieldData.pre += chunks[i] + (chunks.length > 1 ? field.autoCFG.multipleSeparator : '');
294 fieldData.item = fieldData.value;
299 autocomplete : function(e)
301 window.clearTimeout(jQuery.iAuto.timer);
302 var subject = jQuery.iAuto.getFieldValues(this);
304 var pressedKey = e.charCode || e.keyCode || -1;
305 if (/13|27|35|36|38|40|9/.test(pressedKey) && jQuery.iAuto.items) {
307 window.event.cancelBubble = true;
308 window.event.returnValue = false;
313 if (jQuery.iAuto.selectedItem != null)
314 jQuery.iAuto.items.get(jQuery.iAuto.selectedItem||0).className = '';
316 jQuery.iAuto.selectedItem = -1;
321 if (jQuery.iAuto.selectedItem == -1)
322 jQuery.iAuto.selectedItem = 0;
323 var selectedItem = jQuery.iAuto.items.get(jQuery.iAuto.selectedItem||0);
324 var valueToAdd = selectedItem.getAttribute('rel');
325 this.value = subject.pre + valueToAdd + this.autoCFG.multipleSeparator + subject.post;
326 jQuery.iAuto.lastValue = subject.item;
327 jQuery.iAuto.selection(
329 subject.pre.length + valueToAdd.length + this.autoCFG.multipleSeparator.length,
330 subject.pre.length + valueToAdd.length + this.autoCFG.multipleSeparator.length
332 jQuery.iAuto.clear();
333 if (this.autoCFG.onSelect) {
334 iteration = parseInt(selectedItem.getAttribute('dir'))||0;
335 jQuery.iAuto.applyOn(this,this.autoCFG.lastSuggestion.get(iteration), 'onSelect');
337 if (this.scrollIntoView)
338 this.scrollIntoView(false);
339 return pressedKey != 13;
343 this.value = subject.pre + jQuery.iAuto.lastValue + this.autoCFG.multipleSeparator + subject.post;
344 this.autoCFG.lastSuggestion = null;
345 jQuery.iAuto.clear();
346 if (this.scrollIntoView)
347 this.scrollIntoView(false);
352 jQuery.iAuto.selectedItem = jQuery.iAuto.items.size() - 1;
356 jQuery.iAuto.selectedItem = 0;
360 jQuery.iAuto.selectedItem --;
361 if (jQuery.iAuto.selectedItem < 0)
362 jQuery.iAuto.selectedItem = jQuery.iAuto.items.size() - 1;
365 jQuery.iAuto.selectedItem ++;
366 if (jQuery.iAuto.selectedItem == jQuery.iAuto.items.size())
367 jQuery.iAuto.selectedItem = 0;
370 jQuery.iAuto.applyOn(this,this.autoCFG.lastSuggestion.get(jQuery.iAuto.selectedItem||0), 'onHighlight');
371 jQuery.iAuto.items.get(jQuery.iAuto.selectedItem||0).className = this.autoCFG.selectClass;
372 if (jQuery.iAuto.items.get(jQuery.iAuto.selectedItem||0).scrollIntoView)
373 jQuery.iAuto.items.get(jQuery.iAuto.selectedItem||0).scrollIntoView(false);
374 if(this.autoCFG.autofill) {
375 var valToAdd = jQuery.iAuto.items.get(jQuery.iAuto.selectedItem||0).getAttribute('rel');
376 this.value = subject.pre + valToAdd + this.autoCFG.multipleSeparator + subject.post;
377 if(jQuery.iAuto.lastValue.length != valToAdd.length)
378 jQuery.iAuto.selection(
380 subject.pre.length + jQuery.iAuto.lastValue.length,
381 subject.pre.length + valToAdd.length
386 jQuery.iAuto.checkCache.apply(this);
388 if (this.autoCFG.inCache == false) {
389 if (subject.item != jQuery.iAuto.lastValue && subject.item.length >= this.autoCFG.minchars)
390 jQuery.iAuto.timer = window.setTimeout(jQuery.iAuto.update, this.autoCFG.delay);
391 if (jQuery.iAuto.items) {
392 jQuery.iAuto.clear();
398 applyOn: function(field, item, type)
400 if (field.autoCFG[type]) {
402 childs = item.getElementsByTagName('*');
403 for(i=0; i<childs.length; i++){
404 data[childs[i].tagName] = childs[i].firstChild.nodeValue;
406 field.autoCFG[type].apply(field,[data]);
410 hoverItem : function(e)
412 if (jQuery.iAuto.items) {
413 if (jQuery.iAuto.selectedItem != null)
414 jQuery.iAuto.items.get(jQuery.iAuto.selectedItem||0).className = '';
415 jQuery.iAuto.items.get(jQuery.iAuto.selectedItem||0).className = '';
416 jQuery.iAuto.selectedItem = parseInt(this.getAttribute('dir'))||0;
417 jQuery.iAuto.items.get(jQuery.iAuto.selectedItem||0).className = jQuery.iAuto.subject.autoCFG.selectClass;
421 clickItem : function(event)
423 window.clearTimeout(jQuery.iAuto.timer);
425 event = event || jQuery.event.fix( window.event );
426 event.preventDefault();
427 event.stopPropagation();
428 var subject = jQuery.iAuto.getFieldValues(jQuery.iAuto.subject);
429 var valueToAdd = this.getAttribute('rel');
430 jQuery.iAuto.subject.value = subject.pre + valueToAdd + jQuery.iAuto.subject.autoCFG.multipleSeparator + subject.post;
431 jQuery.iAuto.lastValue = this.getAttribute('rel');
432 jQuery.iAuto.selection(
433 jQuery.iAuto.subject,
434 subject.pre.length + valueToAdd.length + jQuery.iAuto.subject.autoCFG.multipleSeparator.length,
435 subject.pre.length + valueToAdd.length + jQuery.iAuto.subject.autoCFG.multipleSeparator.length
437 jQuery.iAuto.clear();
438 if (jQuery.iAuto.subject.autoCFG.onSelect) {
439 iteration = parseInt(this.getAttribute('dir'))||0;
440 jQuery.iAuto.applyOn(jQuery.iAuto.subject,jQuery.iAuto.subject.autoCFG.lastSuggestion.get(iteration), 'onSelect');
446 protect : function(e)
448 pressedKey = e.charCode || e.keyCode || -1;
449 if (/13|27|35|36|38|40/.test(pressedKey) && jQuery.iAuto.items) {
451 window.event.cancelBubble = true;
452 window.event.returnValue = false;
461 build : function(options)
463 if (!options.source || !jQuery.iUtil) {
467 if (!jQuery.iAuto.helper) {
468 if (jQuery.browser.msie) {
469 jQuery('body', document).append('<iframe style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" id="autocompleteIframe" src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
470 jQuery.iAuto.iframe = jQuery('#autocompleteIframe');
472 jQuery('body', document).append('<div id="autocompleteHelper" style="position: absolute; top: 0; left: 0; z-index: 30001; display: none;"><ul style="margin: 0;padding: 0; list-style: none; z-index: 30002;"> </ul></div>');
473 jQuery.iAuto.helper = jQuery('#autocompleteHelper');
474 jQuery.iAuto.content = jQuery('ul', jQuery.iAuto.helper);
480 //modified for contagged: accept textareas, too
481 if (! ((this.tagName == 'INPUT' && this.getAttribute('type') == 'text' ) || this.tagName == 'TEXTAREA'))
484 this.autoCFG.source = options.source;
485 this.autoCFG.minchars = Math.abs(parseInt(options.minchars)||1);
486 this.autoCFG.helperClass = options.helperClass ? options.helperClass : '';
487 this.autoCFG.selectClass = options.selectClass ? options.selectClass : '';
488 this.autoCFG.onSelect = options.onSelect && options.onSelect.constructor == Function ? options.onSelect : null;
489 this.autoCFG.onShow = options.onShow && options.onShow.constructor == Function ? options.onShow : null;
490 this.autoCFG.onHide = options.onHide && options.onHide.constructor == Function ? options.onHide : null;
491 this.autoCFG.onHighlight = options.onHighlight && options.onHighlight.constructor == Function ? options.onHighlight : null;
492 this.autoCFG.inputWidth = options.inputWidth||false;
493 this.autoCFG.multiple = options.multiple||false;
494 this.autoCFG.multipleSeparator = this.autoCFG.multiple ? (options.multipleSeparator||', '):'';
495 this.autoCFG.autofill = options.autofill ? true : false;
496 this.autoCFG.delay = Math.abs(parseInt(options.delay)||1000);
497 if (options.fx && options.fx.constructor == Object) {
498 if (!options.fx.type || !/fade|slide|blind/.test(options.fx.type)) {
499 options.fx.type = 'slide';
501 if (options.fx.type == 'slide' && !jQuery.fx.slide)
503 if (options.fx.type == 'blind' && !jQuery.fx.BlindDirection)
506 options.fx.duration = Math.abs(parseInt(options.fx.duration)||400);
507 if (options.fx.duration > this.autoCFG.delay) {
508 options.fx.duration = this.autoCFG.delay - 100;
510 this.autoCFG.fx = options.fx;
512 this.autoCFG.lastSuggestion = null;
513 this.autoCFG.inCache = false;
516 .attr('autocomplete', 'off')
520 jQuery.iAuto.subject = this;
521 jQuery.iAuto.lastValue = this.value;
524 .keypress(jQuery.iAuto.protect)
525 .keyup(jQuery.iAuto.autocomplete)
530 jQuery.iAuto.timer = window.setTimeout(jQuery.iAuto.clear, 200);
537 jQuery.fn.Autocomplete = jQuery.iAuto.build;