]> git.mxchange.org Git - friendica.git/blob - library/jquery_ac/friendica.complete.js
FR update to the strings THX Perig
[friendica.git] / library / jquery_ac / friendica.complete.js
1 /**\r
2 *  Ajax Autocomplete for jQuery, version 1.1.3\r
3 *  (c) 2010 Tomas Kirda\r
4 *\r
5 *  Ajax Autocomplete for jQuery is freely distributable under the terms of an MIT-style license.\r
6 *  For details, see the web site: http://www.devbridge.com/projects/autocomplete/jquery/\r
7 *\r
8 *  Last Review: 04/19/2010\r
9 *  Heavily modified for contact completion in Friendica (add photos, hover tips. etc.) 11-May-2012 mike@macgirvin.com\r
10 */\r
11 \r
12 /*jslint onevar: true, evil: true, nomen: true, eqeqeq: true, bitwise: true, regexp: true, newcap: true, immed: true */\r
13 /*global window: true, document: true, clearInterval: true, setInterval: true, jQuery: true */\r
14 \r
15 (function($) {\r
16 \r
17   var reEscape = new RegExp('(\\' + ['/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\'].join('|\\') + ')', 'g');\r
18 \r
19   function fnFormatResult(value, data, currentValue) {\r
20     var pattern = '(' + currentValue.replace(reEscape, '\\$1') + ')';\r
21     return value.replace(new RegExp(pattern, 'gi'), '<strong>$1<\/strong>');\r
22   }\r
23 \r
24   function Autocomplete(el, options) {\r
25     this.el = $(el);\r
26     this.el.attr('autocomplete', 'off');\r
27     this.suggestions = [];\r
28     this.data = [];\r
29     this.badQueries = [];\r
30     this.selectedIndex = -1;\r
31     this.currentValue = this.el.val();\r
32     this.intervalId = 0;\r
33     this.cachedResponse = [];\r
34     this.onChangeInterval = null;\r
35     this.ignoreValueChange = false;\r
36     this.serviceUrl = options.serviceUrl;\r
37     this.isLocal = false;\r
38     this.options = {\r
39       autoSubmit: false,\r
40       minChars: 1,\r
41       maxHeight: 300,\r
42       deferRequestBy: 0,\r
43       width: 0,\r
44       highlight: true,\r
45       params: {},\r
46       fnFormatResult: fnFormatResult,\r
47       delimiter: null,\r
48       zIndex: 9999\r
49     };\r
50     this.initialize();\r
51     this.setOptions(options);\r
52   }\r
53   \r
54   $.fn.autocomplete = function(options) {\r
55     return new Autocomplete(this.get(0)||$('<input />'), options);\r
56   };\r
57 \r
58 \r
59   Autocomplete.prototype = {\r
60 \r
61     killerFn: null,\r
62 \r
63     initialize: function() {\r
64 \r
65       var me, uid, autocompleteElId;\r
66       me = this;\r
67       uid = Math.floor(Math.random()*0x100000).toString(16);\r
68       autocompleteElId = 'Autocomplete_' + uid;\r
69 \r
70       this.killerFn = function(e) {\r
71         if ($(e.target).parents('.autocomplete').size() === 0) {\r
72           me.killSuggestions();\r
73           me.disableKillerFn();\r
74         }\r
75       };\r
76 \r
77       if (!this.options.width) { this.options.width = this.el.width(); }\r
78       this.mainContainerId = 'AutocompleteContainter_' + uid;\r
79 \r
80       $('<div id="' + this.mainContainerId + '" style="position:absolute;z-index:9999;"><div class="autocomplete-w1"><div class="autocomplete" id="' + autocompleteElId + '" style="display:none; width:300px;"></div></div></div>').appendTo('body');\r
81 \r
82       this.container = $('#' + autocompleteElId);\r
83       this.fixPosition();\r
84       if (window.opera) {\r
85         this.el.keypress(function(e) { me.onKeyPress(e); });\r
86       } else {\r
87         this.el.keydown(function(e) { me.onKeyPress(e); });\r
88       }\r
89       this.el.keyup(function(e) { me.onKeyUp(e); });\r
90       this.el.blur(function() { me.enableKillerFn(); });\r
91       this.el.focus(function() { me.fixPosition(); });\r
92     },\r
93     \r
94     setOptions: function(options){\r
95       var o = this.options;\r
96       $.extend(o, options);\r
97       if(o.lookup){\r
98         this.isLocal = true;\r
99         if($.isArray(o.lookup)){ o.lookup = { suggestions:o.lookup, data:[] }; }\r
100       }\r
101       $('#'+this.mainContainerId).css({ zIndex:o.zIndex });\r
102       this.container.css({ maxHeight: o.maxHeight + 'px', width:o.width });\r
103     },\r
104     \r
105     clearCache: function(){\r
106       this.cachedResponse = [];\r
107       this.badQueries = [];\r
108     },\r
109     \r
110     disable: function(){\r
111       this.disabled = true;\r
112     },\r
113     \r
114     enable: function(){\r
115       this.disabled = false;\r
116     },\r
117 \r
118     fixPosition: function() {\r
119       var offset = this.el.offset();\r
120       $('#' + this.mainContainerId).css({ top: (offset.top + this.el.innerHeight()) + 'px', left: offset.left + 'px' });\r
121     },\r
122 \r
123     enableKillerFn: function() {\r
124       var me = this;\r
125       $(document).bind('click', me.killerFn);\r
126     },\r
127 \r
128     disableKillerFn: function() {\r
129       var me = this;\r
130       $(document).unbind('click', me.killerFn);\r
131     },\r
132 \r
133     killSuggestions: function() {\r
134       var me = this;\r
135       this.stopKillSuggestions();\r
136       this.intervalId = window.setInterval(function() { me.hide(); me.stopKillSuggestions(); }, 300);\r
137     },\r
138 \r
139     stopKillSuggestions: function() {\r
140       window.clearInterval(this.intervalId);\r
141     },\r
142 \r
143     onKeyPress: function(e) {\r
144       if (this.disabled || !this.enabled) { return; }\r
145       // return will exit the function\r
146       // and event will not be prevented\r
147       switch (e.keyCode) {\r
148         case 27: //KEY_ESC:\r
149           this.el.val(this.currentValue);\r
150           this.hide();\r
151           break;\r
152         case 9: //KEY_TAB:\r
153         case 13: //KEY_RETURN:\r
154           if (this.selectedIndex === -1) {\r
155             this.hide();\r
156             return;\r
157           }\r
158           this.select(this.selectedIndex);\r
159           if(e.keyCode === 9){ return; }\r
160           break;\r
161         case 38: //KEY_UP:\r
162           this.moveUp();\r
163           break;\r
164         case 40: //KEY_DOWN:\r
165           this.moveDown();\r
166           break;\r
167         default:\r
168           return;\r
169       }\r
170       e.stopImmediatePropagation();\r
171       e.preventDefault();\r
172     },\r
173 \r
174     onKeyUp: function(e) {\r
175       if(this.disabled){ return; }\r
176       switch (e.keyCode) {\r
177         case 38: //KEY_UP:\r
178         case 40: //KEY_DOWN:\r
179           return;\r
180       }\r
181       clearInterval(this.onChangeInterval);\r
182       if (this.currentValue !== this.el.val()) {\r
183         if (this.options.deferRequestBy > 0) {\r
184           // Defer lookup in case when value changes very quickly:\r
185           var me = this;\r
186           this.onChangeInterval = setInterval(function() { me.onValueChange(); }, this.options.deferRequestBy);\r
187         } else {\r
188           this.onValueChange();\r
189         }\r
190       }\r
191     },\r
192 \r
193     onValueChange: function() {\r
194       clearInterval(this.onChangeInterval);\r
195       this.currentValue = this.el.val();\r
196       var q = this.getQuery(this.currentValue);\r
197       this.selectedIndex = -1;\r
198       if (this.ignoreValueChange) {\r
199         this.ignoreValueChange = false;\r
200         return;\r
201       }\r
202       if (q === '' || q.length < this.options.minChars) {\r
203         this.hide();\r
204       } else {\r
205         this.getSuggestions(q);\r
206       }\r
207     },\r
208 \r
209     getQuery: function(val) {\r
210       var d, arr;\r
211       d = this.options.delimiter;\r
212       if (!d) { return $.trim(val); }\r
213       arr = val.split(d);\r
214       return $.trim(arr[arr.length - 1]);\r
215     },\r
216 \r
217     getSuggestionsLocal: function(q) {\r
218       var ret, arr, len, val, i;\r
219       arr = this.options.lookup;\r
220       len = arr.suggestions.length;\r
221       ret = { suggestions:[], data:[] };\r
222       q = q.toLowerCase();\r
223       for(i=0; i< len; i++){\r
224         val = arr.suggestions[i];\r
225         if(val.toLowerCase().indexOf(q) === 0){\r
226           ret.suggestions.push(val);\r
227           ret.data.push(arr.data[i]);\r
228         }\r
229       }\r
230       return ret;\r
231     },\r
232     \r
233     getSuggestions: function(q) {\r
234       var cr, me;\r
235       cr = this.isLocal ? this.getSuggestionsLocal(q) : this.cachedResponse[q];\r
236       if (cr && $.isArray(cr.suggestions)) {\r
237         this.suggestions = cr.suggestions;\r
238         this.data = cr.data;\r
239         this.suggest();\r
240       } else if (!this.isBadQuery(q)) {\r
241         me = this;\r
242         me.options.params.query = q;\r
243         $.get(this.serviceUrl, me.options.params, function(txt) { me.processResponse(txt); }, 'text');\r
244       }\r
245     },\r
246 \r
247     isBadQuery: function(q) {\r
248       var i = this.badQueries.length;\r
249       while (i--) {\r
250         if (q.indexOf(this.badQueries[i]) === 0) { return true; }\r
251       }\r
252       return false;\r
253     },\r
254 \r
255     hide: function() {\r
256       this.enabled = false;\r
257       this.selectedIndex = -1;\r
258       this.container.hide();\r
259     },\r
260 \r
261     suggest: function() {\r
262       if (this.suggestions.length === 0) {\r
263         this.hide();\r
264         return;\r
265       }\r
266 \r
267       var me, len, div, f, v, i, s, mOver, mClick, l, img;\r
268       me = this;\r
269       len = this.suggestions.length;\r
270       f = this.options.fnFormatResult;\r
271       v = this.getQuery(this.currentValue);\r
272       mOver = function(xi) { return function() { me.activate(xi); }; };\r
273       mClick = function(xi) { return function() { me.select(xi); }; };\r
274       this.container.hide().empty();\r
275       for (i = 0; i < len; i++) {\r
276         s = this.suggestions[i];\r
277                 l = this.links[i];\r
278                 img = '<img height="24" width="24" src="' + this.photos[i] + '" alt="' + s + '" />&nbsp;';\r
279         div = $((me.selectedIndex === i ? '<div class="selected"' : '<div') + ' title="' + l + '">' + img + f(s, this.data[i], v) + '</div>');\r
280         div.mouseover(mOver(i));\r
281         div.click(mClick(i));\r
282         this.container.append(div);\r
283       }\r
284       this.enabled = true;\r
285       this.container.show();\r
286     },\r
287 \r
288     processResponse: function(text) {\r
289       var response;\r
290       try {\r
291         response = eval('(' + text + ')');\r
292       } catch (err) { return; }\r
293       if (!$.isArray(response.data)) { response.data = []; }\r
294       if(!this.options.noCache){\r
295         this.cachedResponse[response.query] = response;\r
296         if (response.suggestions.length === 0) { this.badQueries.push(response.query); }\r
297       }\r
298       if (response.query === this.getQuery(this.currentValue)) {\r
299                 this.photos = response.photos;\r
300                 this.links = response.links;\r
301         this.suggestions = response.suggestions;\r
302         this.data = response.data;\r
303         this.suggest(); \r
304       }\r
305     },\r
306 \r
307     activate: function(index) {\r
308       var divs, activeItem;\r
309       divs = this.container.children();\r
310       // Clear previous selection:\r
311       if (this.selectedIndex !== -1 && divs.length > this.selectedIndex) {\r
312         $(divs.get(this.selectedIndex)).removeClass();\r
313       }\r
314       this.selectedIndex = index;\r
315       if (this.selectedIndex !== -1 && divs.length > this.selectedIndex) {\r
316         activeItem = divs.get(this.selectedIndex);\r
317         $(activeItem).addClass('selected');\r
318       }\r
319       return activeItem;\r
320     },\r
321 \r
322     deactivate: function(div, index) {\r
323       div.className = '';\r
324       if (this.selectedIndex === index) { this.selectedIndex = -1; }\r
325     },\r
326 \r
327     select: function(i) {\r
328       var selectedValue, f;\r
329       selectedValue = this.suggestions[i];\r
330       if (selectedValue) {\r
331         this.el.val(selectedValue);\r
332         if (this.options.autoSubmit) {\r
333           f = this.el.parents('form');\r
334           if (f.length > 0) { f.get(0).submit(); }\r
335         }\r
336         this.ignoreValueChange = true;\r
337         this.hide();\r
338         this.onSelect(i);\r
339       }\r
340     },\r
341 \r
342     moveUp: function() {\r
343       if (this.selectedIndex === -1) { return; }\r
344       if (this.selectedIndex === 0) {\r
345         this.container.children().get(0).className = '';\r
346         this.selectedIndex = -1;\r
347         this.el.val(this.currentValue);\r
348         return;\r
349       }\r
350       this.adjustScroll(this.selectedIndex - 1);\r
351     },\r
352 \r
353     moveDown: function() {\r
354       if (this.selectedIndex === (this.suggestions.length - 1)) { return; }\r
355       this.adjustScroll(this.selectedIndex + 1);\r
356     },\r
357 \r
358     adjustScroll: function(i) {\r
359       var activeItem, offsetTop, upperBound, lowerBound;\r
360       activeItem = this.activate(i);\r
361       offsetTop = activeItem.offsetTop;\r
362       upperBound = this.container.scrollTop();\r
363       lowerBound = upperBound + this.options.maxHeight - 25;\r
364       if (offsetTop < upperBound) {\r
365         this.container.scrollTop(offsetTop);\r
366       } else if (offsetTop > lowerBound) {\r
367         this.container.scrollTop(offsetTop - this.options.maxHeight + 25);\r
368       }\r
369       this.el.val(this.getValue(this.suggestions[i]));\r
370     },\r
371 \r
372     onSelect: function(i) {\r
373       var me, fn, s, d;\r
374       me = this;\r
375       fn = me.options.onSelect;\r
376       s = me.suggestions[i];\r
377       d = me.data[i];\r
378       me.el.val(me.getValue(s));\r
379       if ($.isFunction(fn)) { fn(s, d, me.el); }\r
380     },\r
381     \r
382     getValue: function(value){\r
383         var del, currVal, arr, me;\r
384         me = this;\r
385         del = me.options.delimiter;\r
386         if (!del) { return value; }\r
387         currVal = me.currentValue;\r
388         arr = currVal.split(del);\r
389         if (arr.length === 1) { return value; }\r
390         return currVal.substr(0, currVal.length - arr[arr.length - 1].length) + value;\r
391     }\r
392 \r
393   };\r
394 \r
395 }(jQuery));\r