muck-engine 0.2.15 → 0.2.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,6 @@
1
+ NOTE: the jquery.autocomplete.pack.js file is modified from the original to work with luvfoo. Specifically line
2
+ 314 was changed from
3
+ if ( data && data.length && hasFocus ) {
4
+ to
5
+ if ( data && data.length ) {
6
+ For some reason the hasFocus value was always 0 which prevented the autocomplete from ever showing up.
@@ -0,0 +1,15 @@
1
+ /*
2
+ * Autocomplete - jQuery plugin 1.0.2
3
+ *
4
+ * Copyright (c) 2007 Dylan Verheul, Dan G. Switzer, Anjesh Tuladhar, Jörn Zaefferer
5
+ *
6
+ * Dual licensed under the MIT and GPL licenses:
7
+ * http://www.opensource.org/licenses/mit-license.php
8
+ * http://www.gnu.org/licenses/gpl.html
9
+ *
10
+ * Revision: $Id: jquery.autocomplete.js 5747 2008-06-25 18:30:55Z joern.zaefferer $
11
+ *
12
+ */;(function($){$.fn.extend({autocomplete:function(urlOrData,options){var isUrl=typeof urlOrData=="string";options=$.extend({},$.Autocompleter.defaults,{url:isUrl?urlOrData:null,data:isUrl?null:urlOrData,delay:isUrl?$.Autocompleter.defaults.delay:10,max:options&&!options.scroll?10:150},options);options.highlight=options.highlight||function(value){return value;};options.formatMatch=options.formatMatch||options.formatItem;return this.each(function(){new $.Autocompleter(this,options);});},result:function(handler){return this.bind("result",handler);},search:function(handler){return this.trigger("search",[handler]);},flushCache:function(){return this.trigger("flushCache");},setOptions:function(options){return this.trigger("setOptions",[options]);},unautocomplete:function(){return this.trigger("unautocomplete");}});$.Autocompleter=function(input,options){var KEY={UP:38,DOWN:40,DEL:46,TAB:9,RETURN:13,ESC:27,COMMA:188,PAGEUP:33,PAGEDOWN:34,BACKSPACE:8};var $input=$(input).attr("autocomplete","off").addClass(options.inputClass);var timeout;var previousValue="";var cache=$.Autocompleter.Cache(options);var hasFocus=0;var lastKeyPressCode;var config={mouseDownOnSelect:false};var select=$.Autocompleter.Select(options,input,selectCurrent,config);var blockSubmit;$.browser.opera&&$(input.form).bind("submit.autocomplete",function(){if(blockSubmit){blockSubmit=false;return false;}});$input.bind(($.browser.opera?"keypress":"keydown")+".autocomplete",function(event){lastKeyPressCode=event.keyCode;switch(event.keyCode){case KEY.UP:event.preventDefault();if(select.visible()){select.prev();}else{onChange(0,true);}break;case KEY.DOWN:event.preventDefault();if(select.visible()){select.next();}else{onChange(0,true);}break;case KEY.PAGEUP:event.preventDefault();if(select.visible()){select.pageUp();}else{onChange(0,true);}break;case KEY.PAGEDOWN:event.preventDefault();if(select.visible()){select.pageDown();}else{onChange(0,true);}break;case options.multiple&&$.trim(options.multipleSeparator)==","&&KEY.COMMA:case KEY.TAB:case KEY.RETURN:if(selectCurrent()){event.preventDefault();blockSubmit=true;return false;}break;case KEY.ESC:select.hide();break;default:clearTimeout(timeout);timeout=setTimeout(onChange,options.delay);break;}}).focus(function(){hasFocus++;}).blur(function(){hasFocus=0;if(!config.mouseDownOnSelect){hideResults();}}).click(function(){if(hasFocus++>1&&!select.visible()){onChange(0,true);}}).bind("search",function(){var fn=(arguments.length>1)?arguments[1]:null;function findValueCallback(q,data){var result;if(data&&data.length){for(var i=0;i<data.length;i++){if(data[i].result.toLowerCase()==q.toLowerCase()){result=data[i];break;}}}if(typeof fn=="function")fn(result);else $input.trigger("result",result&&[result.data,result.value]);}$.each(trimWords($input.val()),function(i,value){request(value,findValueCallback,findValueCallback);});}).bind("flushCache",function(){cache.flush();}).bind("setOptions",function(){$.extend(options,arguments[1]);if("data"in arguments[1])cache.populate();}).bind("unautocomplete",function(){select.unbind();$input.unbind();$(input.form).unbind(".autocomplete");});function selectCurrent(){var selected=select.selected();if(!selected)return false;var v=selected.result;previousValue=v;if(options.multiple){var words=trimWords($input.val());if(words.length>1){v=words.slice(0,words.length-1).join(options.multipleSeparator)+options.multipleSeparator+v;}v+=options.multipleSeparator;}$input.val(v);hideResultsNow();$input.trigger("result",[selected.data,selected.value]);return true;}function onChange(crap,skipPrevCheck){if(lastKeyPressCode==KEY.DEL){select.hide();return;}var currentValue=$input.val();if(!skipPrevCheck&&currentValue==previousValue)return;previousValue=currentValue;currentValue=lastWord(currentValue);if(currentValue.length>=options.minChars){$input.addClass(options.loadingClass);if(!options.matchCase)currentValue=currentValue.toLowerCase();request(currentValue,receiveData,hideResultsNow);}else{stopLoading();select.hide();}};function trimWords(value){if(!value){return[""];}var words=value.split(options.multipleSeparator);var result=[];$.each(words,function(i,value){if($.trim(value))result[i]=$.trim(value);});return result;}function lastWord(value){if(!options.multiple)return value;var words=trimWords(value);return words[words.length-1];}function autoFill(q,sValue){if(options.autoFill&&(lastWord($input.val()).toLowerCase()==q.toLowerCase())&&lastKeyPressCode!=KEY.BACKSPACE){$input.val($input.val()+sValue.substring(lastWord(previousValue).length));$.Autocompleter.Selection(input,previousValue.length,previousValue.length+sValue.length);}};function hideResults(){clearTimeout(timeout);timeout=setTimeout(hideResultsNow,200);};function hideResultsNow(){var wasVisible=select.visible();select.hide();clearTimeout(timeout);stopLoading();if(options.mustMatch){$input.search(function(result){if(!result){if(options.multiple){var words=trimWords($input.val()).slice(0,-1);$input.val(words.join(options.multipleSeparator)+(words.length?options.multipleSeparator:""));}else
13
+ $input.val("");}});}if(wasVisible)$.Autocompleter.Selection(input,input.value.length,input.value.length);};function receiveData(q,data){if(data&&data.length&&hasFocus){stopLoading();select.display(data,q);autoFill(q,data[0].value);select.show();}else{hideResultsNow();}};function request(term,success,failure){if(!options.matchCase)term=term.toLowerCase();var data=cache.load(term);if(data&&data.length){success(term,data);}else if((typeof options.url=="string")&&(options.url.length>0)){var extraParams={timestamp:+new Date()};$.each(options.extraParams,function(key,param){extraParams[key]=typeof param=="function"?param():param;});$.ajax({mode:"abort",port:"autocomplete"+input.name,dataType:options.dataType,url:options.url,data:$.extend({q:lastWord(term),limit:options.max},extraParams),success:function(data){var parsed=options.parse&&options.parse(data)||parse(data);cache.add(term,parsed);success(term,parsed);}});}else{select.emptyList();failure(term);}};function parse(data){var parsed=[];var rows=data.split("\n");for(var i=0;i<rows.length;i++){var row=$.trim(rows[i]);if(row){row=row.split("|");parsed[parsed.length]={data:row,value:row[0],result:options.formatResult&&options.formatResult(row,row[0])||row[0]};}}return parsed;};function stopLoading(){$input.removeClass(options.loadingClass);};};$.Autocompleter.defaults={inputClass:"ac_input",resultsClass:"ac_results",loadingClass:"ac_loading",minChars:1,delay:400,matchCase:false,matchSubset:true,matchContains:false,cacheLength:10,max:100,mustMatch:false,extraParams:{},selectFirst:true,formatItem:function(row){return row[0];},formatMatch:null,autoFill:false,width:0,multiple:false,multipleSeparator:", ",highlight:function(value,term){return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)("+term.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi,"\\$1")+")(?![^<>]*>)(?![^&;]+;)","gi"),"<strong>$1</strong>");},scroll:true,scrollHeight:180};$.Autocompleter.Cache=function(options){var data={};var length=0;function matchSubset(s,sub){if(!options.matchCase)s=s.toLowerCase();var i=s.indexOf(sub);if(i==-1)return false;return i==0||options.matchContains;};function add(q,value){if(length>options.cacheLength){flush();}if(!data[q]){length++;}data[q]=value;}function populate(){if(!options.data)return false;var stMatchSets={},nullData=0;if(!options.url)options.cacheLength=1;stMatchSets[""]=[];for(var i=0,ol=options.data.length;i<ol;i++){var rawValue=options.data[i];rawValue=(typeof rawValue=="string")?[rawValue]:rawValue;var value=options.formatMatch(rawValue,i+1,options.data.length);if(value===false)continue;var firstChar=value.charAt(0).toLowerCase();if(!stMatchSets[firstChar])stMatchSets[firstChar]=[];var row={value:value,data:rawValue,result:options.formatResult&&options.formatResult(rawValue)||value};stMatchSets[firstChar].push(row);if(nullData++<options.max){stMatchSets[""].push(row);}};$.each(stMatchSets,function(i,value){options.cacheLength++;add(i,value);});}setTimeout(populate,25);function flush(){data={};length=0;}return{flush:flush,add:add,populate:populate,load:function(q){if(!options.cacheLength||!length)return null;if(!options.url&&options.matchContains){var csub=[];for(var k in data){if(k.length>0){var c=data[k];$.each(c,function(i,x){if(matchSubset(x.value,q)){csub.push(x);}});}}return csub;}else
14
+ if(data[q]){return data[q];}else
15
+ if(options.matchSubset){for(var i=q.length-1;i>=options.minChars;i--){var c=data[q.substr(0,i)];if(c){var csub=[];$.each(c,function(i,x){if(matchSubset(x.value,q)){csub[csub.length]=x;}});return csub;}}}return null;}};};$.Autocompleter.Select=function(options,input,select,config){var CLASSES={ACTIVE:"ac_over"};var listItems,active=-1,data,term="",needsInit=true,element,list;function init(){if(!needsInit)return;element=$("<div/>").hide().addClass(options.resultsClass).css("position","absolute").appendTo(document.body);list=$("<ul/>").appendTo(element).mouseover(function(event){if(target(event).nodeName&&target(event).nodeName.toUpperCase()=='LI'){active=$("li",list).removeClass(CLASSES.ACTIVE).index(target(event));$(target(event)).addClass(CLASSES.ACTIVE);}}).click(function(event){$(target(event)).addClass(CLASSES.ACTIVE);select();input.focus();return false;}).mousedown(function(){config.mouseDownOnSelect=true;}).mouseup(function(){config.mouseDownOnSelect=false;});if(options.width>0)element.css("width",options.width);needsInit=false;}function target(event){var element=event.target;while(element&&element.tagName!="LI")element=element.parentNode;if(!element)return[];return element;}function moveSelect(step){listItems.slice(active,active+1).removeClass(CLASSES.ACTIVE);movePosition(step);var activeItem=listItems.slice(active,active+1).addClass(CLASSES.ACTIVE);if(options.scroll){var offset=0;listItems.slice(0,active).each(function(){offset+=this.offsetHeight;});if((offset+activeItem[0].offsetHeight-list.scrollTop())>list[0].clientHeight){list.scrollTop(offset+activeItem[0].offsetHeight-list.innerHeight());}else if(offset<list.scrollTop()){list.scrollTop(offset);}}};function movePosition(step){active+=step;if(active<0){active=listItems.size()-1;}else if(active>=listItems.size()){active=0;}}function limitNumberOfItems(available){return options.max&&options.max<available?options.max:available;}function fillList(){list.empty();var max=limitNumberOfItems(data.length);for(var i=0;i<max;i++){if(!data[i])continue;var formatted=options.formatItem(data[i].data,i+1,max,data[i].value,term);if(formatted===false)continue;var li=$("<li/>").html(options.highlight(formatted,term)).addClass(i%2==0?"ac_even":"ac_odd").appendTo(list)[0];$.data(li,"ac_data",data[i]);}listItems=list.find("li");if(options.selectFirst){listItems.slice(0,1).addClass(CLASSES.ACTIVE);active=0;}if($.fn.bgiframe)list.bgiframe();}return{display:function(d,q){init();data=d;term=q;fillList();},next:function(){moveSelect(1);},prev:function(){moveSelect(-1);},pageUp:function(){if(active!=0&&active-8<0){moveSelect(-active);}else{moveSelect(-8);}},pageDown:function(){if(active!=listItems.size()-1&&active+8>listItems.size()){moveSelect(listItems.size()-1-active);}else{moveSelect(8);}},hide:function(){element&&element.hide();listItems&&listItems.removeClass(CLASSES.ACTIVE);active=-1;},visible:function(){return element&&element.is(":visible");},current:function(){return this.visible()&&(listItems.filter("."+CLASSES.ACTIVE)[0]||options.selectFirst&&listItems[0]);},show:function(){var offset=$(input).offset();element.css({width:typeof options.width=="string"||options.width>0?options.width:$(input).width(),top:offset.top+input.offsetHeight,left:offset.left}).show();if(options.scroll){list.scrollTop(0);list.css({maxHeight:options.scrollHeight,overflow:'auto'});if($.browser.msie&&typeof document.body.style.maxHeight==="undefined"){var listHeight=0;listItems.each(function(){listHeight+=this.offsetHeight;});var scrollbarsVisible=listHeight>options.scrollHeight;list.css('height',scrollbarsVisible?options.scrollHeight:listHeight);if(!scrollbarsVisible){listItems.width(list.width()-parseInt(listItems.css("padding-left"))-parseInt(listItems.css("padding-right")));}}}},selected:function(){var selected=listItems&&listItems.filter("."+CLASSES.ACTIVE).removeClass(CLASSES.ACTIVE);return selected&&selected.length&&$.data(selected[0],"ac_data");},emptyList:function(){list&&list.empty();},unbind:function(){element&&element.remove();}};};$.Autocompleter.Selection=function(field,start,end){if(field.createTextRange){var selRange=field.createTextRange();selRange.collapse(true);selRange.moveStart("character",start);selRange.moveEnd("character",end);selRange.select();}else if(field.setSelectionRange){field.setSelectionRange(start,end);}else{if(field.selectionStart){field.selectionStart=start;field.selectionEnd=end;}}field.focus();};})(jQuery);
@@ -0,0 +1,767 @@
1
+ /**********************************************************************************************
2
+ * NOTE: this version is modified from the original to work with luvfoo. Specifically line
3
+ * 314 was changed from
4
+ * if ( data && data.length && hasFocus ) {
5
+ * to
6
+ * if ( data && data.length ) {
7
+ * For some reason the hasFocus value was always 0 which prevented the autocomplete from ever showing up.
8
+ **********************************************************************************************
9
+ *
10
+ * Autocomplete - jQuery plugin 1.0.2
11
+ *
12
+ * Copyright (c) 2007 Dylan Verheul, Dan G. Switzer, Anjesh Tuladhar, Jörn Zaefferer
13
+ *
14
+ * Dual licensed under the MIT and GPL licenses:
15
+ * http://www.opensource.org/licenses/mit-license.php
16
+ * http://www.gnu.org/licenses/gpl.html
17
+ *
18
+ * Revision: $Id: jquery.autocomplete.js 5747 2008-06-25 18:30:55Z joern.zaefferer $
19
+ *
20
+ */
21
+
22
+ ;(function($) {
23
+
24
+ $.fn.extend({
25
+ autocomplete: function(urlOrData, options) {
26
+ var isUrl = typeof urlOrData == "string";
27
+ options = $.extend({}, $.Autocompleter.defaults, {
28
+ url: isUrl ? urlOrData : null,
29
+ data: isUrl ? null : urlOrData,
30
+ delay: isUrl ? $.Autocompleter.defaults.delay : 10,
31
+ max: options && !options.scroll ? 10 : 150
32
+ }, options);
33
+
34
+ // if highlight is set to false, replace it with a do-nothing function
35
+ options.highlight = options.highlight || function(value) { return value; };
36
+
37
+ // if the formatMatch option is not specified, then use formatItem for backwards compatibility
38
+ options.formatMatch = options.formatMatch || options.formatItem;
39
+
40
+ return this.each(function() {
41
+ new $.Autocompleter(this, options);
42
+ });
43
+ },
44
+ result: function(handler) {
45
+ return this.bind("result", handler);
46
+ },
47
+ search: function(handler) {
48
+ return this.trigger("search", [handler]);
49
+ },
50
+ flushCache: function() {
51
+ return this.trigger("flushCache");
52
+ },
53
+ setOptions: function(options){
54
+ return this.trigger("setOptions", [options]);
55
+ },
56
+ unautocomplete: function() {
57
+ return this.trigger("unautocomplete");
58
+ }
59
+ });
60
+
61
+ $.Autocompleter = function(input, options) {
62
+
63
+ var KEY = {
64
+ UP: 38,
65
+ DOWN: 40,
66
+ DEL: 46,
67
+ TAB: 9,
68
+ RETURN: 13,
69
+ ESC: 27,
70
+ COMMA: 188,
71
+ PAGEUP: 33,
72
+ PAGEDOWN: 34,
73
+ BACKSPACE: 8
74
+ };
75
+
76
+ // Create $ object for input element
77
+ var $input = $(input).attr("autocomplete", "off").addClass(options.inputClass);
78
+
79
+ var timeout;
80
+ var previousValue = "";
81
+ var cache = $.Autocompleter.Cache(options);
82
+ var hasFocus = 0;
83
+ var lastKeyPressCode;
84
+ var config = {
85
+ mouseDownOnSelect: false
86
+ };
87
+ var select = $.Autocompleter.Select(options, input, selectCurrent, config);
88
+
89
+ var blockSubmit;
90
+
91
+ // prevent form submit in opera when selecting with return key
92
+ $.browser.opera && $(input.form).bind("submit.autocomplete", function() {
93
+ if (blockSubmit) {
94
+ blockSubmit = false;
95
+ return false;
96
+ }
97
+ });
98
+
99
+ // only opera doesn't trigger keydown multiple times while pressed, others don't work with keypress at all
100
+ $input.bind(($.browser.opera ? "keypress" : "keydown") + ".autocomplete", function(event) {
101
+ // track last key pressed
102
+ lastKeyPressCode = event.keyCode;
103
+ switch(event.keyCode) {
104
+
105
+ case KEY.UP:
106
+ event.preventDefault();
107
+ if ( select.visible() ) {
108
+ select.prev();
109
+ } else {
110
+ onChange(0, true);
111
+ }
112
+ break;
113
+
114
+ case KEY.DOWN:
115
+ event.preventDefault();
116
+ if ( select.visible() ) {
117
+ select.next();
118
+ } else {
119
+ onChange(0, true);
120
+ }
121
+ break;
122
+
123
+ case KEY.PAGEUP:
124
+ event.preventDefault();
125
+ if ( select.visible() ) {
126
+ select.pageUp();
127
+ } else {
128
+ onChange(0, true);
129
+ }
130
+ break;
131
+
132
+ case KEY.PAGEDOWN:
133
+ event.preventDefault();
134
+ if ( select.visible() ) {
135
+ select.pageDown();
136
+ } else {
137
+ onChange(0, true);
138
+ }
139
+ break;
140
+
141
+ // matches also semicolon
142
+ case options.multiple && $.trim(options.multipleSeparator) == "," && KEY.COMMA:
143
+ case KEY.TAB:
144
+ case KEY.RETURN:
145
+ if( selectCurrent() ) {
146
+ // stop default to prevent a form submit, Opera needs special handling
147
+ event.preventDefault();
148
+ blockSubmit = true;
149
+ return false;
150
+ }
151
+ break;
152
+
153
+ case KEY.ESC:
154
+ select.hide();
155
+ break;
156
+
157
+ default:
158
+ clearTimeout(timeout);
159
+ timeout = setTimeout(onChange, options.delay);
160
+ break;
161
+ }
162
+ }).focus(function(){
163
+ // track whether the field has focus, we shouldn't process any
164
+ // results if the field no longer has focus
165
+ hasFocus++;
166
+ }).blur(function() {
167
+ hasFocus = 0;
168
+ if (!config.mouseDownOnSelect) {
169
+ hideResults();
170
+ }
171
+ }).click(function() {
172
+ // show select when clicking in a focused field
173
+ if ( hasFocus++ > 1 && !select.visible() ) {
174
+ onChange(0, true);
175
+ }
176
+ }).bind("search", function() {
177
+ // TODO why not just specifying both arguments?
178
+ var fn = (arguments.length > 1) ? arguments[1] : null;
179
+ function findValueCallback(q, data) {
180
+ var result;
181
+ if( data && data.length ) {
182
+ for (var i=0; i < data.length; i++) {
183
+ if( data[i].result.toLowerCase() == q.toLowerCase() ) {
184
+ result = data[i];
185
+ break;
186
+ }
187
+ }
188
+ }
189
+ if( typeof fn == "function" ) fn(result);
190
+ else $input.trigger("result", result && [result.data, result.value]);
191
+ }
192
+ $.each(trimWords($input.val()), function(i, value) {
193
+ request(value, findValueCallback, findValueCallback);
194
+ });
195
+ }).bind("flushCache", function() {
196
+ cache.flush();
197
+ }).bind("setOptions", function() {
198
+ $.extend(options, arguments[1]);
199
+ // if we've updated the data, repopulate
200
+ if ( "data" in arguments[1] )
201
+ cache.populate();
202
+ }).bind("unautocomplete", function() {
203
+ select.unbind();
204
+ $input.unbind();
205
+ $(input.form).unbind(".autocomplete");
206
+ });
207
+
208
+
209
+ function selectCurrent() {
210
+ var selected = select.selected();
211
+ if( !selected )
212
+ return false;
213
+
214
+ var v = selected.result;
215
+ previousValue = v;
216
+
217
+ if ( options.multiple ) {
218
+ var words = trimWords($input.val());
219
+ if ( words.length > 1 ) {
220
+ v = words.slice(0, words.length - 1).join( options.multipleSeparator ) + options.multipleSeparator + v;
221
+ }
222
+ v += options.multipleSeparator;
223
+ }
224
+
225
+ $input.val(v);
226
+ hideResultsNow();
227
+ $input.trigger("result", [selected.data, selected.value]);
228
+ return true;
229
+ }
230
+
231
+ function onChange(crap, skipPrevCheck) {
232
+ if( lastKeyPressCode == KEY.DEL ) {
233
+ select.hide();
234
+ return;
235
+ }
236
+
237
+ var currentValue = $input.val();
238
+
239
+ if ( !skipPrevCheck && currentValue == previousValue )
240
+ return;
241
+
242
+ previousValue = currentValue;
243
+
244
+ currentValue = lastWord(currentValue);
245
+ if ( currentValue.length >= options.minChars) {
246
+ $input.addClass(options.loadingClass);
247
+ if (!options.matchCase)
248
+ currentValue = currentValue.toLowerCase();
249
+ request(currentValue, receiveData, hideResultsNow);
250
+ } else {
251
+ stopLoading();
252
+ select.hide();
253
+ }
254
+ };
255
+
256
+ function trimWords(value) {
257
+ if ( !value ) {
258
+ return [""];
259
+ }
260
+ var words = value.split( options.multipleSeparator );
261
+ var result = [];
262
+ $.each(words, function(i, value) {
263
+ if ( $.trim(value) )
264
+ result[i] = $.trim(value);
265
+ });
266
+ return result;
267
+ }
268
+
269
+ function lastWord(value) {
270
+ if ( !options.multiple )
271
+ return value;
272
+ var words = trimWords(value);
273
+ return words[words.length - 1];
274
+ }
275
+
276
+ // fills in the input box w/the first match (assumed to be the best match)
277
+ // q: the term entered
278
+ // sValue: the first matching result
279
+ function autoFill(q, sValue){
280
+ // autofill in the complete box w/the first match as long as the user hasn't entered in more data
281
+ // if the last user key pressed was backspace, don't autofill
282
+ if( options.autoFill && (lastWord($input.val()).toLowerCase() == q.toLowerCase()) && lastKeyPressCode != KEY.BACKSPACE ) {
283
+ // fill in the value (keep the case the user has typed)
284
+ $input.val($input.val() + sValue.substring(lastWord(previousValue).length));
285
+ // select the portion of the value not typed by the user (so the next character will erase)
286
+ $.Autocompleter.Selection(input, previousValue.length, previousValue.length + sValue.length);
287
+ }
288
+ };
289
+
290
+ function hideResults() {
291
+ clearTimeout(timeout);
292
+ timeout = setTimeout(hideResultsNow, 200);
293
+ };
294
+
295
+ function hideResultsNow() {
296
+ var wasVisible = select.visible();
297
+ select.hide();
298
+ clearTimeout(timeout);
299
+ stopLoading();
300
+ if (options.mustMatch) {
301
+ // call search and run callback
302
+ $input.search(
303
+ function (result){
304
+ // if no value found, clear the input box
305
+ if( !result ) {
306
+ if (options.multiple) {
307
+ var words = trimWords($input.val()).slice(0, -1);
308
+ $input.val( words.join(options.multipleSeparator) + (words.length ? options.multipleSeparator : "") );
309
+ }
310
+ else
311
+ $input.val( "" );
312
+ }
313
+ }
314
+ );
315
+ }
316
+ if (wasVisible)
317
+ // position cursor at end of input field
318
+ $.Autocompleter.Selection(input, input.value.length, input.value.length);
319
+ };
320
+
321
+ function receiveData(q, data) {
322
+ if ( data && data.length ) {
323
+ stopLoading();
324
+ select.display(data, q);
325
+ autoFill(q, data[0].value);
326
+ select.show();
327
+ } else {
328
+ hideResultsNow();
329
+ }
330
+ };
331
+
332
+ function request(term, success, failure) {
333
+ if (!options.matchCase)
334
+ term = term.toLowerCase();
335
+ var data = cache.load(term);
336
+ // recieve the cached data
337
+ if (data && data.length) {
338
+ success(term, data);
339
+ // if an AJAX url has been supplied, try loading the data now
340
+ } else if( (typeof options.url == "string") && (options.url.length > 0) ){
341
+
342
+ var extraParams = {
343
+ timestamp: +new Date()
344
+ };
345
+ $.each(options.extraParams, function(key, param) {
346
+ extraParams[key] = typeof param == "function" ? param() : param;
347
+ });
348
+
349
+ $.ajax({
350
+ // try to leverage ajaxQueue plugin to abort previous requests
351
+ mode: "abort",
352
+ // limit abortion to this input
353
+ port: "autocomplete" + input.name,
354
+ dataType: options.dataType,
355
+ url: options.url,
356
+ data: $.extend({
357
+ q: lastWord(term),
358
+ limit: options.max
359
+ }, extraParams),
360
+ success: function(data) {
361
+ var parsed = options.parse && options.parse(data) || parse(data);
362
+ cache.add(term, parsed);
363
+ success(term, parsed);
364
+ }
365
+ });
366
+ } else {
367
+ // if we have a failure, we need to empty the list -- this prevents the the [TAB] key from selecting the last successful match
368
+ select.emptyList();
369
+ failure(term);
370
+ }
371
+ };
372
+
373
+ function parse(data) {
374
+ var parsed = [];
375
+ var rows = data.split("\n");
376
+ for (var i=0; i < rows.length; i++) {
377
+ var row = $.trim(rows[i]);
378
+ if (row) {
379
+ row = row.split("|");
380
+ parsed[parsed.length] = {
381
+ data: row,
382
+ value: row[0],
383
+ result: options.formatResult && options.formatResult(row, row[0]) || row[0]
384
+ };
385
+ }
386
+ }
387
+ return parsed;
388
+ };
389
+
390
+ function stopLoading() {
391
+ $input.removeClass(options.loadingClass);
392
+ };
393
+
394
+ };
395
+
396
+ $.Autocompleter.defaults = {
397
+ inputClass: "ac_input",
398
+ resultsClass: "ac_results",
399
+ loadingClass: "ac_loading",
400
+ minChars: 1,
401
+ delay: 400,
402
+ matchCase: false,
403
+ matchSubset: true,
404
+ matchContains: false,
405
+ cacheLength: 10,
406
+ max: 100,
407
+ mustMatch: false,
408
+ extraParams: {},
409
+ selectFirst: true,
410
+ formatItem: function(row) { return row[0]; },
411
+ formatMatch: null,
412
+ autoFill: false,
413
+ width: 0,
414
+ multiple: false,
415
+ multipleSeparator: ", ",
416
+ highlight: function(value, term) {
417
+ return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi, "\\$1") + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<strong>$1</strong>");
418
+ },
419
+ scroll: true,
420
+ scrollHeight: 180
421
+ };
422
+
423
+ $.Autocompleter.Cache = function(options) {
424
+
425
+ var data = {};
426
+ var length = 0;
427
+
428
+ function matchSubset(s, sub) {
429
+ if (!options.matchCase)
430
+ s = s.toLowerCase();
431
+ var i = s.indexOf(sub);
432
+ if (i == -1) return false;
433
+ return i == 0 || options.matchContains;
434
+ };
435
+
436
+ function add(q, value) {
437
+ if (length > options.cacheLength){
438
+ flush();
439
+ }
440
+ if (!data[q]){
441
+ length++;
442
+ }
443
+ data[q] = value;
444
+ }
445
+
446
+ function populate(){
447
+ if( !options.data ) return false;
448
+ // track the matches
449
+ var stMatchSets = {},
450
+ nullData = 0;
451
+
452
+ // no url was specified, we need to adjust the cache length to make sure it fits the local data store
453
+ if( !options.url ) options.cacheLength = 1;
454
+
455
+ // track all options for minChars = 0
456
+ stMatchSets[""] = [];
457
+
458
+ // loop through the array and create a lookup structure
459
+ for ( var i = 0, ol = options.data.length; i < ol; i++ ) {
460
+ var rawValue = options.data[i];
461
+ // if rawValue is a string, make an array otherwise just reference the array
462
+ rawValue = (typeof rawValue == "string") ? [rawValue] : rawValue;
463
+
464
+ var value = options.formatMatch(rawValue, i+1, options.data.length);
465
+ if ( value === false )
466
+ continue;
467
+
468
+ var firstChar = value.charAt(0).toLowerCase();
469
+ // if no lookup array for this character exists, look it up now
470
+ if( !stMatchSets[firstChar] )
471
+ stMatchSets[firstChar] = [];
472
+
473
+ // if the match is a string
474
+ var row = {
475
+ value: value,
476
+ data: rawValue,
477
+ result: options.formatResult && options.formatResult(rawValue) || value
478
+ };
479
+
480
+ // push the current match into the set list
481
+ stMatchSets[firstChar].push(row);
482
+
483
+ // keep track of minChars zero items
484
+ if ( nullData++ < options.max ) {
485
+ stMatchSets[""].push(row);
486
+ }
487
+ };
488
+
489
+ // add the data items to the cache
490
+ $.each(stMatchSets, function(i, value) {
491
+ // increase the cache size
492
+ options.cacheLength++;
493
+ // add to the cache
494
+ add(i, value);
495
+ });
496
+ }
497
+
498
+ // populate any existing data
499
+ setTimeout(populate, 25);
500
+
501
+ function flush(){
502
+ data = {};
503
+ length = 0;
504
+ }
505
+
506
+ return {
507
+ flush: flush,
508
+ add: add,
509
+ populate: populate,
510
+ load: function(q) {
511
+ if (!options.cacheLength || !length)
512
+ return null;
513
+ /*
514
+ * if dealing w/local data and matchContains than we must make sure
515
+ * to loop through all the data collections looking for matches
516
+ */
517
+ if( !options.url && options.matchContains ){
518
+ // track all matches
519
+ var csub = [];
520
+ // loop through all the data grids for matches
521
+ for( var k in data ){
522
+ // don't search through the stMatchSets[""] (minChars: 0) cache
523
+ // this prevents duplicates
524
+ if( k.length > 0 ){
525
+ var c = data[k];
526
+ $.each(c, function(i, x) {
527
+ // if we've got a match, add it to the array
528
+ if (matchSubset(x.value, q)) {
529
+ csub.push(x);
530
+ }
531
+ });
532
+ }
533
+ }
534
+ return csub;
535
+ } else
536
+ // if the exact item exists, use it
537
+ if (data[q]){
538
+ return data[q];
539
+ } else
540
+ if (options.matchSubset) {
541
+ for (var i = q.length - 1; i >= options.minChars; i--) {
542
+ var c = data[q.substr(0, i)];
543
+ if (c) {
544
+ var csub = [];
545
+ $.each(c, function(i, x) {
546
+ if (matchSubset(x.value, q)) {
547
+ csub[csub.length] = x;
548
+ }
549
+ });
550
+ return csub;
551
+ }
552
+ }
553
+ }
554
+ return null;
555
+ }
556
+ };
557
+ };
558
+
559
+ $.Autocompleter.Select = function (options, input, select, config) {
560
+ var CLASSES = {
561
+ ACTIVE: "ac_over"
562
+ };
563
+
564
+ var listItems,
565
+ active = -1,
566
+ data,
567
+ term = "",
568
+ needsInit = true,
569
+ element,
570
+ list;
571
+
572
+ // Create results
573
+ function init() {
574
+ if (!needsInit)
575
+ return;
576
+ element = $("<div/>")
577
+ .hide()
578
+ .addClass(options.resultsClass)
579
+ .css("position", "absolute")
580
+ .appendTo(document.body);
581
+
582
+ list = $("<ul/>").appendTo(element).mouseover( function(event) {
583
+ if(target(event).nodeName && target(event).nodeName.toUpperCase() == 'LI') {
584
+ active = $("li", list).removeClass(CLASSES.ACTIVE).index(target(event));
585
+ $(target(event)).addClass(CLASSES.ACTIVE);
586
+ }
587
+ }).click(function(event) {
588
+ $(target(event)).addClass(CLASSES.ACTIVE);
589
+ select();
590
+ // TODO provide option to avoid setting focus again after selection? useful for cleanup-on-focus
591
+ input.focus();
592
+ return false;
593
+ }).mousedown(function() {
594
+ config.mouseDownOnSelect = true;
595
+ }).mouseup(function() {
596
+ config.mouseDownOnSelect = false;
597
+ });
598
+
599
+ if( options.width > 0 )
600
+ element.css("width", options.width);
601
+
602
+ needsInit = false;
603
+ }
604
+
605
+ function target(event) {
606
+ var element = event.target;
607
+ while(element && element.tagName != "LI")
608
+ element = element.parentNode;
609
+ // more fun with IE, sometimes event.target is empty, just ignore it then
610
+ if(!element)
611
+ return [];
612
+ return element;
613
+ }
614
+
615
+ function moveSelect(step) {
616
+ listItems.slice(active, active + 1).removeClass(CLASSES.ACTIVE);
617
+ movePosition(step);
618
+ var activeItem = listItems.slice(active, active + 1).addClass(CLASSES.ACTIVE);
619
+ if(options.scroll) {
620
+ var offset = 0;
621
+ listItems.slice(0, active).each(function() {
622
+ offset += this.offsetHeight;
623
+ });
624
+ if((offset + activeItem[0].offsetHeight - list.scrollTop()) > list[0].clientHeight) {
625
+ list.scrollTop(offset + activeItem[0].offsetHeight - list.innerHeight());
626
+ } else if(offset < list.scrollTop()) {
627
+ list.scrollTop(offset);
628
+ }
629
+ }
630
+ };
631
+
632
+ function movePosition(step) {
633
+ active += step;
634
+ if (active < 0) {
635
+ active = listItems.size() - 1;
636
+ } else if (active >= listItems.size()) {
637
+ active = 0;
638
+ }
639
+ }
640
+
641
+ function limitNumberOfItems(available) {
642
+ return options.max && options.max < available
643
+ ? options.max
644
+ : available;
645
+ }
646
+
647
+ function fillList() {
648
+ list.empty();
649
+ var max = limitNumberOfItems(data.length);
650
+ for (var i=0; i < max; i++) {
651
+ if (!data[i])
652
+ continue;
653
+ var formatted = options.formatItem(data[i].data, i+1, max, data[i].value, term);
654
+ if ( formatted === false )
655
+ continue;
656
+ var li = $("<li/>").html( options.highlight(formatted, term) ).addClass(i%2 == 0 ? "ac_even" : "ac_odd").appendTo(list)[0];
657
+ $.data(li, "ac_data", data[i]);
658
+ }
659
+ listItems = list.find("li");
660
+ if ( options.selectFirst ) {
661
+ listItems.slice(0, 1).addClass(CLASSES.ACTIVE);
662
+ active = 0;
663
+ }
664
+ // apply bgiframe if available
665
+ if ( $.fn.bgiframe )
666
+ list.bgiframe();
667
+ }
668
+
669
+ return {
670
+ display: function(d, q) {
671
+ init();
672
+ data = d;
673
+ term = q;
674
+ fillList();
675
+ },
676
+ next: function() {
677
+ moveSelect(1);
678
+ },
679
+ prev: function() {
680
+ moveSelect(-1);
681
+ },
682
+ pageUp: function() {
683
+ if (active != 0 && active - 8 < 0) {
684
+ moveSelect( -active );
685
+ } else {
686
+ moveSelect(-8);
687
+ }
688
+ },
689
+ pageDown: function() {
690
+ if (active != listItems.size() - 1 && active + 8 > listItems.size()) {
691
+ moveSelect( listItems.size() - 1 - active );
692
+ } else {
693
+ moveSelect(8);
694
+ }
695
+ },
696
+ hide: function() {
697
+ element && element.hide();
698
+ listItems && listItems.removeClass(CLASSES.ACTIVE);
699
+ active = -1;
700
+ },
701
+ visible : function() {
702
+ return element && element.is(":visible");
703
+ },
704
+ current: function() {
705
+ return this.visible() && (listItems.filter("." + CLASSES.ACTIVE)[0] || options.selectFirst && listItems[0]);
706
+ },
707
+ show: function() {
708
+ var offset = $(input).offset();
709
+ element.css({
710
+ width: typeof options.width == "string" || options.width > 0 ? options.width : $(input).width(),
711
+ top: offset.top + input.offsetHeight,
712
+ left: offset.left
713
+ }).show();
714
+ if(options.scroll) {
715
+ list.scrollTop(0);
716
+ list.css({
717
+ maxHeight: options.scrollHeight,
718
+ overflow: 'auto'
719
+ });
720
+
721
+ if($.browser.msie && typeof document.body.style.maxHeight === "undefined") {
722
+ var listHeight = 0;
723
+ listItems.each(function() {
724
+ listHeight += this.offsetHeight;
725
+ });
726
+ var scrollbarsVisible = listHeight > options.scrollHeight;
727
+ list.css('height', scrollbarsVisible ? options.scrollHeight : listHeight );
728
+ if (!scrollbarsVisible) {
729
+ // IE doesn't recalculate width when scrollbar disappears
730
+ listItems.width( list.width() - parseInt(listItems.css("padding-left")) - parseInt(listItems.css("padding-right")) );
731
+ }
732
+ }
733
+
734
+ }
735
+ },
736
+ selected: function() {
737
+ var selected = listItems && listItems.filter("." + CLASSES.ACTIVE).removeClass(CLASSES.ACTIVE);
738
+ return selected && selected.length && $.data(selected[0], "ac_data");
739
+ },
740
+ emptyList: function (){
741
+ list && list.empty();
742
+ },
743
+ unbind: function() {
744
+ element && element.remove();
745
+ }
746
+ };
747
+ };
748
+
749
+ $.Autocompleter.Selection = function(field, start, end) {
750
+ if( field.createTextRange ){
751
+ var selRange = field.createTextRange();
752
+ selRange.collapse(true);
753
+ selRange.moveStart("character", start);
754
+ selRange.moveEnd("character", end);
755
+ selRange.select();
756
+ } else if( field.setSelectionRange ){
757
+ field.setSelectionRange(start, end);
758
+ } else {
759
+ if( field.selectionStart ){
760
+ field.selectionStart = start;
761
+ field.selectionEnd = end;
762
+ }
763
+ }
764
+ field.focus();
765
+ };
766
+
767
+ })(jQuery);