/** FCBKcomplete v2.8.9.3 is released under the MIT License - Jquery version required: 1.6.x */ /* Based on TextboxList by Guillermo Rauch http://devthought.com/ */ /** * width - element width (by default 512px) * json_url - url to fetch json object * cache - use cache * height - maximum number of element shown before scroll will apear * newel - show typed text like a element * firstselected - automaticly select first element from dropdown * filter_case - case sensitive filter * filter_selected - filter selected items from list * filter_begin - filter only from begin * complete_text - text for complete page * maxshownitems - maximum numbers that will be shown at dropdown list (less better performance) * oncreate - fire event on item create * onselect - fire event on item select * onremove - fire event on item remove * maxitimes - maximum items that can be added * delay - delay between ajax request (bigger delay, lower server time request) * addontab - add first visible element on tab or enter hit * addoncomma - add first visible element when pressing the comma key * attachto - after this element fcbkcomplete insert own elements * bricket - use square bricket with select (needed for asp or php) enabled by default * input_tabindex - the tabindex of the input element * input_min_size - minimum size of the input element (default: 1) * input_name - value of the input element's 'name'-attribute (no 'name'-attribute set if empty) * select_all_text - text for select all link */ (function( $, undefined ) { $.fn.fcbkcomplete = function(opt) { return this.queue( function() { function init() { createFCBK(); addInput(0); } function createFCBK() { holder = $('').width(options.width); if (options.attachto) { if (typeof(options.attachto) == "object") { options.attachto.append(holder); } else { $(options.attachto).append(holder); } } else { element.after(holder); } complete = $('
').width(options.width); if (options.complete_text != "") { var completeText = options.complete_text; complete.append('
' + completeText + '
'); if (options.select_all_text) { complete.children('.default').append($('' + options.select_all_text + '').click(function(){$(element).trigger('selectAll'); return false;})); } } complete.hover(function() {complete_hover = 0;}, function() {complete_hover = 1;}); feed = $('').width(options.width); holder.after(complete.prepend(feed)); elPrepare(); } function elPrepare() { name = element.attr("name"); if (options.bricket) { if (typeof(name) != 'undefined' && name.indexOf("[]") == -1) { name = name + "[]"; } } var temp_elem = $('<'+element.get(0).tagName+' name="'+name+'" id="'+elemid+'" multiple="multiple" class="' + element.get(0).className + ' hidden">').data('cache', {}); $.each(element.children('option'), function(i, option) { option = $(option); temp_elem.data('cache')[option.val()] = option.text(); if (option.hasClass("selected")) { var id = addItem(option.text(), option.val(), true, option.hasClass("locked")); temp_elem.append(''); } }); element.after(temp_elem); element.remove(); element = temp_elem; //public method to add new item $(element).bind("addItem", function(event, data) { addItem(data.title, data.value, 0, 0, 0); }); //public method to remove item $(element).bind("removeItem", function(event, data) { var item = holder.children('li[rel=' + data.value + ']'); if (item.length) { removeItem(item); } }); //public method to remove item $(element).bind("destroy", function(event, data) { holder.remove(); complete.remove(); element.show(); }); //public method to select all items $(element).bind("selectAll", function(event, data) { var currVals = $(element).val() || []; $.each($(element).data('cache'), function(key, value){ if($.inArray(key, currVals) === -1){ addItem(value, key, 0, 0, 0); } }); feed.parent().hide() }); } function addItem(title, value, preadded, locked, focusme) { if (!maxItems()) { return false; } var liclass = "bit-box" + (locked ? " locked": ""); var id = randomId(); var txt = document.createTextNode(xssDisplay(title)); var aclose = $(''); var li = $('
  • ').prepend(txt).append(aclose); holder.append(li); aclose.click( function() { removeItem($(this).parent("li")); return false; }); if (!preadded) { $("#" + elemid + "_annoninput").remove(); addInput(focusme); var _item = $(''); element.append(_item); if (options.onselect) { funCall(options.onselect, _item); } element.change(); } holder.children("li.bit-box.deleted").removeClass("deleted"); clear_feed(1); return id; } function removeItem(item) { if (!item.hasClass('locked')) { item.fadeOut("fast"); var id = item.attr('id'); if (options.onremove) { var _item = id ? $("#o" + id + "") : element.children("option[value=" + item.attr("rel") + "]"); funCall(options.onremove, _item); } if (id) { $("#o" + id + "").remove(); } else { element.children('option[value="' + item.attr("rel") + '"]').remove(); } item.remove(); element.change(); deleting = 0; } } function addInput(focusme) { var li = $('
  • '); var input = $(''); if (options.input_tabindex > 0) input.attr("tabindex", options.input_tabindex); if (options.input_name != "") input.attr("name", options.input_name); holder.append(li.append(input)); input.focus( function() { isactive = true; if (maxItems()) { complete.fadeIn("fast"); } }); input.blur( function() { isactive = false; if (complete_hover) { complete.fadeOut("fast"); } else { input.focus(); } }); holder.click( function() { if (options.input_min_size < 0 && feed.length) { load_feed(xssPrevent(input.val(), 1)); } input.focus(); if (feed.length && input.val().length > options.input_min_size) { feed.show(); } else { clear_feed(1); complete.children(".default").show(); } }); input.keypress( function(event) { if (event.keyCode == _key.enter) { return false; } //auto expand input var newsize = (options.input_min_size > input.val().length) ? options.input_min_size : (input.val().length + 1); input.attr("size", newsize).width(parseInt(input.css('font-size')) * newsize); }); input.keyup( function(event) { var etext = xssPrevent(input.val(), 1); if (event.keyCode == _key.backspace && etext.length == 0) { clear_feed(1); if (!holder.children("li.bit-box:last").hasClass('locked')) { if (holder.children("li.bit-box.deleted").length == 0) { holder.children("li.bit-box:last").addClass("deleted"); return false; } else { if (deleting) { return; } deleting = 1; holder.children("li.bit-box.deleted").fadeOut("fast", function() { removeItem($(this)); return false; }); } } } if (event.keyCode != _key.downarrow && event.keyCode != _key.uparrow && event.keyCode!= _key.leftarrow && event.keyCode!= _key.rightarrow && etext.length > options.input_min_size) { load_feed(etext); complete.children(".default").hide(); feed.show(); } }); if (options.oncreate) { funCall(options.oncreate, input); } if (focusme) { setTimeout( function() { input.focus(); complete.children(".default").show(); }, 1); } } function addMembers(etext, data) { feed.html(''); if (!options.cache && data != null) { cache.clear(); } addTextItem(etext); if (data != null && data.length) { $.each(data, function(i, val) { cache.set(xssPrevent(val.key), xssPrevent(val.value)); }); } var maximum = options.maxshownitems < cache.length() ? options.maxshownitems: cache.length(); var content = ''; $.each(cache.search(etext), function (i, object) { if (options.filter_selected && element.children('option[value="' + object.key + '"]').hasClass("selected")) { //nothing here... } else { content += '
  • ' + xssDisplay(itemIllumination(object.value, etext)) + '
  • '; counter++; maximum--; } }); feed.append(content); if (options.firstselected) { focuson = feed.children("li:visible:first"); focuson.addClass("auto-focus"); } if (counter > options.height) { feed.css({ "height": (options.height * 24) + "px", "overflow": "auto" }); } else { feed.css("height", "auto"); } if (maxItems() && complete.is(':hidden')) { complete.show(); } } function itemIllumination(text, etext) { var string_regex_adder = options.filter_begin ? '': '(.*)'; var regex_result = options.filter_begin ? '$1$2' : '$1$2$3'; var string_regex = string_regex_adder + (options.filter_case ? "(" + etext + ")(.*)" : "(" + etext.toLowerCase() + ")(.*)"); try { var regex = new RegExp(string_regex, ((options.filter_case) ? "g":"gi")); var text = text.replace(regex, regex_result); } catch(ex) {}; return text; } function bindFeedEvent() { feed.children("li").mouseover( function() { feed.children("li").removeClass("auto-focus"); focuson = $(this); focuson.addClass("auto-focus"); }); feed.children("li").mouseout( function() { $(this).removeClass("auto-focus"); focuson = null; }); } function removeFeedEvent() { feed.unbind("mouseover").unbind("mouseout").mousemove( function() { bindFeedEvent(); feed.unbind("mousemove"); }); } function bindEvents() { var maininput = $("#" + elemid + "_annoninput").children(".maininput"); bindFeedEvent(); feed.children("li").unbind("mousedown").mousedown( function() { var option = $(this); addItem(option.text(), option.attr("rel"), 0, 0, 1); clear_feed(1); complete.hide(); }); maininput.unbind("keydown"); maininput.keydown( function(event) { if (event.keyCode != _key.backspace) { holder.children("li.bit-box.deleted").removeClass("deleted"); } if ((event.keyCode == _key.enter || event.keyCode == _key.tab || event.keyCode == _key.comma) && checkFocusOn()) { var option = focuson; addItem(option.text(), option.attr("rel"), 0, 0, 1); return _preventDefault(event); } if ((event.keyCode == _key.enter || event.keyCode == _key.tab || event.keyCode == _key.comma) && !checkFocusOn()) { if (options.newel) { var value = xssPrevent($(this).val()); addItem(value, value, 0, 0, 1); return _preventDefault(event); } if ((options.addontab || options.addoncomma) && options.newel) { focuson = feed.children("li:visible:first"); var option = focuson; addItem(option.text(), option.attr("rel"), 0, 0, 1); return _preventDefault(event); } } if (event.keyCode == _key.downarrow) { nextItem('first'); } if (event.keyCode == _key.uparrow) { nextItem('last'); } }); } function nextItem(position) { removeFeedEvent(); if (focuson == null || focuson.length == 0) { focuson = feed.children("li:visible:" + position); feed.get(0).scrollTop = position == 'first' ? 0 : parseInt(focuson.get(0).scrollHeight, 10) * (parseInt(feed.children("li:visible").length, 10) - Math.round(options.height / 2)); } else { focuson.removeClass("auto-focus"); focuson = position == 'first' ? focuson.nextAll("li:visible:first") : focuson.prevAll("li:visible:first"); var prev = parseInt(focuson.prevAll("li:visible").length, 10); var next = parseInt(focuson.nextAll("li:visible").length, 10); if (((position == 'first' ? prev : next) > Math.round(options.height / 2) || (position == 'first' ? prev : next) <= Math.round(options.height / 2)) && typeof(focuson.get(0)) != "undefined") { feed.get(0).scrollTop = parseInt(focuson.get(0).scrollHeight, 10) * (prev - Math.round(options.height / 2)); } } feed.children("li").removeClass("auto-focus"); focuson.addClass("auto-focus"); } function _preventDefault(event) { complete.hide(); event.preventDefault(); focuson = null; return false; } function maxItems() { return options.maxitems != 0 && (holder.children("li.bit-box").length < options.maxitems); } function addTextItem(value) { if (options.newel && maxItems()) { feed.children("li[fckb=1]").remove(); if (value.length == 0) { return; } var li = $('
  • ').html(xssDisplay(value)); feed.prepend(li); counter++; } return; } function funCall(func, item) { var _object = {}; for (i = 0; i < item.get(0).attributes.length; i++) { if (item.get(0).attributes[i].nodeValue != null) { _object["_" + item.get(0).attributes[i].nodeName] = item.get(0).attributes[i].nodeValue; } } return func.call(func, _object); } function checkFocusOn() { if (focuson == null || focuson.length == 0) { return false; } return true; } function xssPrevent(string, flag) { if (typeof flag != "undefined") { for(i = 0; i < string.length; i++) { var charcode = string.charCodeAt(i); if ((_key.exclamation <= charcode && charcode <= _key.slash) || (_key.colon <= charcode && charcode <= _key.at) || (_key.squarebricket_left <= charcode && charcode <= _key.apostrof)) { string = string.replace(string[i], escape(string[i])); } } string = string.replace(/(\{|\}|\*)/i, "\\$1"); } return string.replace(/script(.*)/g, ""); } function xssDisplay(string, flag) { string = string.toString(); string = string.replace('\\', ""); if (typeof flag != "undefined") { return string; } return unescape(string); } function clear_feed(flag) { feed.children().remove(); if (flag) { feed.hide(); } } function load_feed(etext){ counter = 0; if (options.json_url && maxItems()) { if (options.cache && json_cache_object.get(etext)) { addMembers(etext); bindEvents(); } else { getBoxTimeout++; var getBoxTimeoutValue = getBoxTimeout; setTimeout( function() { if (getBoxTimeoutValue != getBoxTimeout) return; $.getJSON(options.json_url, {"tag": xssDisplay(etext)}, function(data) { if (!isactive) return; // prevents opening the selection again after the focus is already off addMembers(etext, data); json_cache_object.set(etext, 1); bindEvents(); }); }, options.delay); } } else { addMembers(etext); bindEvents(); } } var options = $.extend({ json_url: null, width: 512, cache: false, height: "10", newel: false, addontab: false, addoncomma: false, firstselected: false, filter_case: false, filter_selected: false, filter_begin: false, complete_text: "Start to type...", select_all_text: null, maxshownitems: 30, maxitems: 10, oncreate: null, onselect: null, onremove: null, attachto: null, delay: 350, input_tabindex: 0, input_min_size: 1, input_name: "", bricket: true }, opt); //system variables var holder = null; var feed = null; var complete = null; var counter = 0; var isactive = false; var focuson = null; var deleting = 0; var complete_hover = 1; var element = $(this); var elemid = element.attr("id"); var getBoxTimeout = 0; var json_cache_object = { 'set': function (id, val) { var data = element.data("jsoncache"); data[id] = val; element.data("jsoncache", data); }, 'get': function(id) { return element.data("jsoncache")[id] != 'undefined' ? element.data("jsoncache")[id] : null; }, 'init' : function () { element.data("jsoncache", {}); } }; var _key = { 'enter': 13, 'tab': 9, 'comma': 188, 'backspace': 8, 'leftarrow': 37, 'uparrow': 38, 'rightarrow': 39, 'downarrow': 40, 'exclamation': 33, 'slash': 47, 'colon': 58, 'at': 64, 'squarebricket_left': 91, 'apostrof': 96 }; var randomId = function() { var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz"; var randomstring = ''; for (var i = 0; i < 32; i++) { var rnum = Math.floor(Math.random() * chars.length); randomstring += chars.substring(rnum, rnum + 1); } return randomstring; }; var cache = { 'search': function (text, callback) { var temp = new Array(); var regex = new RegExp((options.filter_begin ? '^' : '') + text, (options.filter_case ? "g": "gi")); $.each(element.data("cache"), function (i, _elem) { if (typeof _elem.search === 'function') { if (_elem.search(regex) != -1) { temp.push({'key': i, 'value': _elem}); } } }); return temp; }, 'set': function (id, val) { var data = element.data("cache"); data[id] = val; element.data("cache", data); }, 'get': function(id) { return element.data("cache")[id] != 'undefined' ? element.data("cache")[id] : null; }, 'clear': function() { element.data("cache", {}); }, 'length': function() { return element.data("cache").length; }, 'init': function () { if (element.data("cache") == 'undefined') { element.data("cache", {}); } } }; //initialization init(); //cache initialization json_cache_object.init(); cache.init(); return this; }); }; })(jQuery);