1 | /* |
---|
2 | FCBKcomplete 2.7 |
---|
3 | - Jquery version required: 1.2.x, 1.3.x, 1.4.x |
---|
4 | |
---|
5 | Changelog: |
---|
6 | - 2.00 new version of fcbkcomplete |
---|
7 | |
---|
8 | - 2.01 fixed bugs & added features |
---|
9 | fixed filter bug for preadded items |
---|
10 | focus on the input after selecting tag |
---|
11 | the element removed pressing backspace when the element is selected |
---|
12 | input tag in the control has a border in IE7 |
---|
13 | added iterate over each match and apply the plugin separately |
---|
14 | set focus on the input after selecting tag |
---|
15 | |
---|
16 | - 2.02 fixed fist element selected bug |
---|
17 | fixed defaultfilter error bug |
---|
18 | |
---|
19 | - 2.5 removed selected="selected" attribute due ie bug |
---|
20 | element search algorithm changed |
---|
21 | better performance fix added |
---|
22 | fixed many small bugs |
---|
23 | onselect event added |
---|
24 | onremove event added |
---|
25 | |
---|
26 | - 2.6 ie6/7 support fix added |
---|
27 | added new public method addItem due request |
---|
28 | added new options "firstselected" that you can set true/false to select first element on dropdown list |
---|
29 | autoexpand input element added |
---|
30 | removeItem bug fixed |
---|
31 | and many more bug fixed |
---|
32 | fixed public method to use it $("elem").trigger("addItem",[{"title": "test", "value": "test"}]); |
---|
33 | |
---|
34 | - 2.7 jquery 1.4 compability |
---|
35 | item lock possability added by adding locked class to preadded option <option value="value" class="selected locked">text</option> |
---|
36 | maximum item that can be added to the list |
---|
37 | */ |
---|
38 | /* Coded by: emposha <admin@emposha.com> */ |
---|
39 | /* Copyright: Emposha.com <http://www.emposha.com/> - Distributed under MIT - Keep this message! */ |
---|
40 | /* |
---|
41 | * json_url - url to fetch json object |
---|
42 | * cache - use cache |
---|
43 | * height - maximum number of element shown before scroll will apear |
---|
44 | * newel - show typed text like a element |
---|
45 | * firstselected - automaticly select first element from dropdown |
---|
46 | * filter_case - case sensitive filter |
---|
47 | * filter_selected - filter selected items from list |
---|
48 | * complete_text - text for complete page |
---|
49 | * maxshownitems - maximum numbers that will be shown at dropdown list (less better performance) |
---|
50 | * onselect - fire event on item select |
---|
51 | * onremove - fire event on item remove |
---|
52 | * maxitimes - maximum items that can be added |
---|
53 | */ |
---|
54 | jQuery(function($){ |
---|
55 | $.fn.fcbkcomplete = function(opt){ |
---|
56 | return this.each(function(){ |
---|
57 | function init(){ |
---|
58 | createFCBK(); |
---|
59 | preSet(); |
---|
60 | addInput(0); |
---|
61 | } |
---|
62 | |
---|
63 | function createFCBK(){ |
---|
64 | element.hide(); |
---|
65 | element.attr("multiple", "multiple"); |
---|
66 | if (element.attr("name").indexOf("[]") == -1) { |
---|
67 | element.attr("name", element.attr("name") + "[]"); |
---|
68 | } |
---|
69 | |
---|
70 | holder = $(document.createElement("ul")); |
---|
71 | holder.attr("class", "holder"); |
---|
72 | element.after(holder); |
---|
73 | |
---|
74 | complete = $(document.createElement("div")); |
---|
75 | complete.addClass("facebook-auto"); |
---|
76 | complete.append('<div class="default">' + options.complete_text + "</div>"); |
---|
77 | |
---|
78 | if (browser_msie) { |
---|
79 | complete.append('<iframe class="ie6fix" scrolling="no" frameborder="0"></iframe>'); |
---|
80 | browser_msie_frame = complete.children('.ie6fix'); |
---|
81 | } |
---|
82 | |
---|
83 | feed = $(document.createElement("ul")); |
---|
84 | feed.attr("id", elemid + "_feed"); |
---|
85 | |
---|
86 | complete.prepend(feed); |
---|
87 | holder.after(complete); |
---|
88 | feed.css("width", complete.width()); |
---|
89 | } |
---|
90 | |
---|
91 | function preSet(){ |
---|
92 | element.children("option").each(function(i, option){ |
---|
93 | option = $(option); |
---|
94 | if (option.hasClass("selected")) { |
---|
95 | addItem(option.text(), option.val(), true, option.hasClass("locked")); |
---|
96 | option.attr("selected", "selected"); |
---|
97 | } |
---|
98 | else { |
---|
99 | option.removeAttr("selected"); |
---|
100 | } |
---|
101 | |
---|
102 | cache.push({ |
---|
103 | caption: option.text(), |
---|
104 | value: option.val() |
---|
105 | }); |
---|
106 | search_string += "" + (cache.length - 1) + ":" + option.text() + ";"; |
---|
107 | }); |
---|
108 | } |
---|
109 | |
---|
110 | //public method to add new item |
---|
111 | $(this).bind("addItem", function(event, data){ |
---|
112 | addItem(data.title, data.value); |
---|
113 | }); |
---|
114 | |
---|
115 | function addItem(title, value, preadded, locked){ |
---|
116 | if (!maxItems()) { |
---|
117 | return false; |
---|
118 | } |
---|
119 | var li = document.createElement("li"); |
---|
120 | var txt = document.createTextNode(title); |
---|
121 | var aclose = document.createElement("a"); |
---|
122 | var liclass = "bit-box" + (locked ? " locked" : ""); |
---|
123 | $(li).attr({ |
---|
124 | "class": liclass, |
---|
125 | "rel": value |
---|
126 | }); |
---|
127 | $(li).prepend(txt); |
---|
128 | $(aclose).attr({ |
---|
129 | "class": "closebutton", |
---|
130 | "href": "#" |
---|
131 | }); |
---|
132 | |
---|
133 | li.appendChild(aclose); |
---|
134 | holder.append(li); |
---|
135 | |
---|
136 | $(aclose).click(function(){ |
---|
137 | removeItem($(this).parent("li")); |
---|
138 | return false; |
---|
139 | }); |
---|
140 | |
---|
141 | if (!preadded) { |
---|
142 | $("#" + elemid + "_annoninput").remove(); |
---|
143 | var _item; |
---|
144 | addInput(1); |
---|
145 | if (element.children("option[value=" + value + "]").length) { |
---|
146 | _item = element.children("option[value=" + value + "]"); |
---|
147 | _item.get(0).setAttribute("selected", "selected"); |
---|
148 | if (!_item.hasClass("selected")) { |
---|
149 | _item.addClass("selected"); |
---|
150 | } |
---|
151 | } |
---|
152 | else { |
---|
153 | var _item = $(document.createElement("option")); |
---|
154 | _item.attr("value", value).get(0).setAttribute("selected", "selected"); |
---|
155 | _item.attr("value", value).addClass("selected"); |
---|
156 | _item.text(title); |
---|
157 | element.append(_item); |
---|
158 | } |
---|
159 | if (options.onselect.length) { |
---|
160 | funCall(options.onselect, _item) |
---|
161 | } |
---|
162 | } |
---|
163 | holder.children("li.bit-box.deleted").removeClass("deleted"); |
---|
164 | feed.hide(); |
---|
165 | browser_msie ? browser_msie_frame.hide() : ''; |
---|
166 | } |
---|
167 | |
---|
168 | function removeItem(item){ |
---|
169 | var parent = item.parent("li"); |
---|
170 | if (!parent.hasClass('locked')) { |
---|
171 | parent.fadeOut("fast"); |
---|
172 | if (options.onremove.length) { |
---|
173 | var _item = element.children("option[value=" + item.attr("rel") + "]"); |
---|
174 | funCall(options.onremove, _item) |
---|
175 | } |
---|
176 | element.children("option[value=" + item.attr("rel") + "]").removeAttr("selected"); |
---|
177 | element.children("option[value=" + item.attr("rel") + "]").removeClass("selected"); |
---|
178 | item.remove(); |
---|
179 | deleting = 0; |
---|
180 | } |
---|
181 | } |
---|
182 | |
---|
183 | function addInput(focusme){ |
---|
184 | var li = $(document.createElement("li")); |
---|
185 | var input = $(document.createElement("input")); |
---|
186 | |
---|
187 | li.attr({ |
---|
188 | "class": "bit-input", |
---|
189 | "id": elemid + "_annoninput" |
---|
190 | }); |
---|
191 | input.attr({ |
---|
192 | "type": "text", |
---|
193 | "class": "maininput", |
---|
194 | "size": "1" |
---|
195 | }); |
---|
196 | holder.append(li.append(input)); |
---|
197 | |
---|
198 | input.focus(function(){ |
---|
199 | complete.fadeIn("fast"); |
---|
200 | }); |
---|
201 | |
---|
202 | input.blur(function(){ |
---|
203 | complete.fadeOut("fast"); |
---|
204 | }); |
---|
205 | |
---|
206 | holder.click(function(){ |
---|
207 | input.focus(); |
---|
208 | if (feed.length && input.val().length) { |
---|
209 | feed.show(); |
---|
210 | } |
---|
211 | else { |
---|
212 | feed.hide(); |
---|
213 | browser_msie ? browser_msie_frame.hide() : ''; |
---|
214 | complete.children(".default").show(); |
---|
215 | } |
---|
216 | }); |
---|
217 | |
---|
218 | input.keypress(function(event){ |
---|
219 | if (event.keyCode == 13) { |
---|
220 | return false; |
---|
221 | } |
---|
222 | //auto expand input |
---|
223 | input.attr("size", input.val().length + 1); |
---|
224 | }); |
---|
225 | |
---|
226 | input.keydown(function(event){ |
---|
227 | //prevent to enter some bad chars when input is empty |
---|
228 | if (event.keyCode == 191) { |
---|
229 | event.preventDefault(); |
---|
230 | return false; |
---|
231 | } |
---|
232 | }); |
---|
233 | |
---|
234 | input.keyup(function(event){ |
---|
235 | var etext = xssPrevent(input.val()); |
---|
236 | |
---|
237 | if (event.keyCode == 8 && etext.length == 0) { |
---|
238 | feed.hide(); |
---|
239 | browser_msie ? browser_msie_frame.hide() : ''; |
---|
240 | if (!holder.children("li.bit-box:last").hasClass('locked')) { |
---|
241 | if (holder.children("li.bit-box.deleted").length == 0) { |
---|
242 | holder.children("li.bit-box:last").addClass("deleted"); |
---|
243 | return false; |
---|
244 | } |
---|
245 | else { |
---|
246 | if (deleting) { |
---|
247 | return; |
---|
248 | } |
---|
249 | deleting = 1; |
---|
250 | holder.children("li.bit-box.deleted").fadeOut("fast", function(){ |
---|
251 | removeItem($(this)); |
---|
252 | return false; |
---|
253 | }); |
---|
254 | } |
---|
255 | } |
---|
256 | } |
---|
257 | |
---|
258 | if (event.keyCode != 40 && event.keyCode != 38 && etext.length != 0) { |
---|
259 | counter = 0; |
---|
260 | |
---|
261 | if (options.json_url) { |
---|
262 | if (options.cache && json_cache) { |
---|
263 | addMembers(etext); |
---|
264 | bindEvents(); |
---|
265 | } |
---|
266 | else { |
---|
267 | $.getJSON(options.json_url + "?tag=" + etext, null, function(data){ |
---|
268 | addMembers(etext, data); |
---|
269 | json_cache = true; |
---|
270 | bindEvents(); |
---|
271 | }); |
---|
272 | } |
---|
273 | } |
---|
274 | else { |
---|
275 | addMembers(etext); |
---|
276 | bindEvents(); |
---|
277 | } |
---|
278 | complete.children(".default").hide(); |
---|
279 | feed.show(); |
---|
280 | } |
---|
281 | }); |
---|
282 | if (focusme) { |
---|
283 | setTimeout(function(){ |
---|
284 | input.focus(); |
---|
285 | complete.children(".default").show(); |
---|
286 | }, 1); |
---|
287 | } |
---|
288 | } |
---|
289 | |
---|
290 | function addMembers(etext, data){ |
---|
291 | feed.html(''); |
---|
292 | |
---|
293 | if (!options.cache) { |
---|
294 | cache = new Array(); |
---|
295 | search_string = ""; |
---|
296 | } |
---|
297 | |
---|
298 | addTextItem(etext); |
---|
299 | |
---|
300 | if (data != null && data.length) { |
---|
301 | $.each(data, function(i, val){ |
---|
302 | cache.push({ |
---|
303 | caption: val.caption, |
---|
304 | value: val.value |
---|
305 | }); |
---|
306 | search_string += "" + (cache.length - 1) + ":" + val.caption + ";"; |
---|
307 | }); |
---|
308 | } |
---|
309 | |
---|
310 | var maximum = options.maxshownitems < cache.length ? options.maxshownitems : cache.length; |
---|
311 | var filter = "i"; |
---|
312 | if (options.filter_case) { |
---|
313 | filter = ""; |
---|
314 | } |
---|
315 | |
---|
316 | var myregexp, match; |
---|
317 | try { |
---|
318 | myregexp = eval('/(?:^|;)\\s*(\\d+)\\s*:[^;]*?' + etext + '[^;]*/g' + filter); |
---|
319 | match = myregexp.exec(search_string); |
---|
320 | } |
---|
321 | catch (ex) { |
---|
322 | }; |
---|
323 | |
---|
324 | var content = ''; |
---|
325 | while (match != null && maximum > 0) { |
---|
326 | var id = match[1]; |
---|
327 | var object = cache[id]; |
---|
328 | if (options.filter_selected && element.children("option[value=" + object.value + "]").hasClass("selected")) { |
---|
329 | //nothing here... |
---|
330 | } |
---|
331 | else { |
---|
332 | content += '<li rel="' + object.value + '">' + itemIllumination(object.caption, etext) + '</li>'; |
---|
333 | counter++; |
---|
334 | maximum--; |
---|
335 | } |
---|
336 | match = myregexp.exec(search_string); |
---|
337 | } |
---|
338 | feed.append(content); |
---|
339 | |
---|
340 | if (options.firstselected) { |
---|
341 | focuson = feed.children("li:visible:first"); |
---|
342 | focuson.addClass("auto-focus"); |
---|
343 | } |
---|
344 | |
---|
345 | if (counter > options.height) { |
---|
346 | feed.css({ |
---|
347 | "height": (options.height * 24) + "px", |
---|
348 | "overflow": "auto" |
---|
349 | }); |
---|
350 | if (browser_msie) { |
---|
351 | browser_msie_frame.css({ |
---|
352 | "height": (options.height * 24) + "px", |
---|
353 | "width": feed.width() + "px" |
---|
354 | }).show(); |
---|
355 | } |
---|
356 | } |
---|
357 | else { |
---|
358 | feed.css("height", "auto"); |
---|
359 | if (browser_msie) { |
---|
360 | browser_msie_frame.css({ |
---|
361 | "height": feed.height() + "px", |
---|
362 | "width": feed.width() + "px" |
---|
363 | }).show(); |
---|
364 | } |
---|
365 | } |
---|
366 | } |
---|
367 | |
---|
368 | function itemIllumination(text, etext){ |
---|
369 | if (options.filter_case) { |
---|
370 | try { |
---|
371 | eval("var text = text.replace(/(.*)(" + etext + ")(.*)/gi,'$1<em>$2</em>$3');"); |
---|
372 | } |
---|
373 | catch (ex) { |
---|
374 | }; |
---|
375 | } |
---|
376 | else { |
---|
377 | try { |
---|
378 | eval("var text = text.replace(/(.*)(" + etext.toLowerCase() + ")(.*)/gi,'$1<em>$2</em>$3');"); |
---|
379 | } |
---|
380 | catch (ex) { |
---|
381 | }; |
---|
382 | } |
---|
383 | return text; |
---|
384 | } |
---|
385 | |
---|
386 | function bindFeedEvent(){ |
---|
387 | feed.children("li").mouseover(function(){ |
---|
388 | feed.children("li").removeClass("auto-focus"); |
---|
389 | $(this).addClass("auto-focus"); |
---|
390 | focuson = $(this); |
---|
391 | }); |
---|
392 | |
---|
393 | feed.children("li").mouseout(function(){ |
---|
394 | $(this).removeClass("auto-focus"); |
---|
395 | focuson = null; |
---|
396 | }); |
---|
397 | } |
---|
398 | |
---|
399 | function removeFeedEvent(){ |
---|
400 | feed.children("li").unbind("mouseover"); |
---|
401 | feed.children("li").unbind("mouseout"); |
---|
402 | feed.mousemove(function(){ |
---|
403 | bindFeedEvent(); |
---|
404 | feed.unbind("mousemove"); |
---|
405 | }); |
---|
406 | } |
---|
407 | |
---|
408 | function bindEvents(){ |
---|
409 | var maininput = $("#" + elemid + "_annoninput").children(".maininput"); |
---|
410 | bindFeedEvent(); |
---|
411 | feed.children("li").unbind("mousedown"); |
---|
412 | feed.children("li").mousedown(function(){ |
---|
413 | var option = $(this); |
---|
414 | addItem(option.text(), option.attr("rel")); |
---|
415 | feed.hide(); |
---|
416 | browser_msie ? browser_msie_frame.hide() : ''; |
---|
417 | complete.hide(); |
---|
418 | }); |
---|
419 | |
---|
420 | maininput.unbind("keydown"); |
---|
421 | maininput.keydown(function(event){ |
---|
422 | if (event.keyCode == 191) { |
---|
423 | event.preventDefault(); |
---|
424 | return false; |
---|
425 | } |
---|
426 | |
---|
427 | if (event.keyCode != 8) { |
---|
428 | holder.children("li.bit-box.deleted").removeClass("deleted"); |
---|
429 | } |
---|
430 | |
---|
431 | if (event.keyCode == 13 && checkFocusOn()) { |
---|
432 | var option = focuson; |
---|
433 | addItem(option.text(), option.attr("rel")); |
---|
434 | complete.hide(); |
---|
435 | event.preventDefault(); |
---|
436 | focuson = null; |
---|
437 | return false; |
---|
438 | } |
---|
439 | |
---|
440 | if (event.keyCode == 13 && !checkFocusOn()) { |
---|
441 | if (options.newel) { |
---|
442 | var value = xssPrevent($(this).val()); |
---|
443 | addItem(value, value); |
---|
444 | complete.hide(); |
---|
445 | event.preventDefault(); |
---|
446 | focuson = null; |
---|
447 | } |
---|
448 | return false; |
---|
449 | } |
---|
450 | |
---|
451 | if (event.keyCode == 40) { |
---|
452 | removeFeedEvent(); |
---|
453 | if (focuson == null || focuson.length == 0) { |
---|
454 | focuson = feed.children("li:visible:first"); |
---|
455 | feed.get(0).scrollTop = 0; |
---|
456 | } |
---|
457 | else { |
---|
458 | focuson.removeClass("auto-focus"); |
---|
459 | focuson = focuson.nextAll("li:visible:first"); |
---|
460 | var prev = parseInt(focuson.prevAll("li:visible").length, 10); |
---|
461 | var next = parseInt(focuson.nextAll("li:visible").length, 10); |
---|
462 | if ((prev > Math.round(options.height / 2) || next <= Math.round(options.height / 2)) && typeof(focuson.get(0)) != "undefined") { |
---|
463 | feed.get(0).scrollTop = parseInt(focuson.get(0).scrollHeight, 10) * (prev - Math.round(options.height / 2)); |
---|
464 | } |
---|
465 | } |
---|
466 | feed.children("li").removeClass("auto-focus"); |
---|
467 | focuson.addClass("auto-focus"); |
---|
468 | } |
---|
469 | if (event.keyCode == 38) { |
---|
470 | removeFeedEvent(); |
---|
471 | if (focuson == null || focuson.length == 0) { |
---|
472 | focuson = feed.children("li:visible:last"); |
---|
473 | feed.get(0).scrollTop = parseInt(focuson.get(0).scrollHeight, 10) * (parseInt(feed.children("li:visible").length, 10) - Math.round(options.height / 2)); |
---|
474 | } |
---|
475 | else { |
---|
476 | focuson.removeClass("auto-focus"); |
---|
477 | focuson = focuson.prevAll("li:visible:first"); |
---|
478 | var prev = parseInt(focuson.prevAll("li:visible").length, 10); |
---|
479 | var next = parseInt(focuson.nextAll("li:visible").length, 10); |
---|
480 | if ((next > Math.round(options.height / 2) || prev <= Math.round(options.height / 2)) && typeof(focuson.get(0)) != "undefined") { |
---|
481 | feed.get(0).scrollTop = parseInt(focuson.get(0).scrollHeight, 10) * (prev - Math.round(options.height / 2)); |
---|
482 | } |
---|
483 | } |
---|
484 | feed.children("li").removeClass("auto-focus"); |
---|
485 | focuson.addClass("auto-focus"); |
---|
486 | } |
---|
487 | }); |
---|
488 | } |
---|
489 | |
---|
490 | function maxItems(){ |
---|
491 | if (options.maxitems != 0) { |
---|
492 | if (holder.children("li.bit-box").length < options.maxitems) { |
---|
493 | return true; |
---|
494 | } |
---|
495 | else { |
---|
496 | return false; |
---|
497 | } |
---|
498 | } |
---|
499 | } |
---|
500 | |
---|
501 | function addTextItem(value){ |
---|
502 | if (options.newel && maxItems()) { |
---|
503 | feed.children("li[fckb=1]").remove(); |
---|
504 | if (value.length == 0) { |
---|
505 | return; |
---|
506 | } |
---|
507 | var li = $(document.createElement("li")); |
---|
508 | li.attr({ |
---|
509 | "rel": value, |
---|
510 | "fckb": "1" |
---|
511 | }).html(value); |
---|
512 | feed.prepend(li); |
---|
513 | counter++; |
---|
514 | } |
---|
515 | else { |
---|
516 | return; |
---|
517 | } |
---|
518 | } |
---|
519 | |
---|
520 | function funCall(func, item){ |
---|
521 | var _object = ""; |
---|
522 | for (i = 0; i < item.get(0).attributes.length; i++) { |
---|
523 | if (item.get(0).attributes[i].nodeValue != null) { |
---|
524 | _object += "\"_" + item.get(0).attributes[i].nodeName + "\": \"" + item.get(0).attributes[i].nodeValue + "\","; |
---|
525 | } |
---|
526 | } |
---|
527 | _object = "{" + _object + " notinuse: 0}"; |
---|
528 | try { |
---|
529 | eval(func + "(" + _object + ")"); |
---|
530 | } |
---|
531 | catch (ex) { |
---|
532 | }; |
---|
533 | } |
---|
534 | |
---|
535 | function checkFocusOn(){ |
---|
536 | if (focuson == null) { |
---|
537 | return false; |
---|
538 | } |
---|
539 | if (focuson.length == 0) { |
---|
540 | return false; |
---|
541 | } |
---|
542 | return true; |
---|
543 | } |
---|
544 | |
---|
545 | function xssPrevent(string){ |
---|
546 | string = string.replace(/[\"\'][\s]*javascript:(.*)[\"\']/g, "\"\""); |
---|
547 | string = string.replace(/script(.*)/g, ""); |
---|
548 | string = string.replace(/eval\((.*)\)/g, ""); |
---|
549 | string = string.replace('/([\x00-\x08,\x0b-\x0c,\x0e-\x19])/', ''); |
---|
550 | return string; |
---|
551 | } |
---|
552 | |
---|
553 | var options = $.extend({ |
---|
554 | json_url: null, |
---|
555 | cache: false, |
---|
556 | height: "10", |
---|
557 | newel: false, |
---|
558 | firstselected: false, |
---|
559 | filter_case: false, |
---|
560 | filter_hide: false, |
---|
561 | complete_text: "Start to type...", |
---|
562 | maxshownitems: 30, |
---|
563 | maxitems: 0, |
---|
564 | onselect: "", |
---|
565 | onremove: "" |
---|
566 | }, opt); |
---|
567 | |
---|
568 | //system variables |
---|
569 | var holder = null; |
---|
570 | var feed = null; |
---|
571 | var complete = null; |
---|
572 | var counter = 0; |
---|
573 | var cache = new Array(); |
---|
574 | var json_cache = false; |
---|
575 | var search_string = ""; |
---|
576 | var focuson = null; |
---|
577 | var deleting = 0; |
---|
578 | var browser_msie = "\v" == "v"; |
---|
579 | var browser_msie_frame; |
---|
580 | |
---|
581 | var element = $(this); |
---|
582 | var elemid = element.attr("id"); |
---|
583 | init(); |
---|
584 | |
---|
585 | return this; |
---|
586 | }); |
---|
587 | }; |
---|
588 | }); |
---|