/** * JQuery Searchable DropDown Plugin * * @required jQuery 1.3.x or above * @author Sascha Woo * $Id: jquery.searchabledropdown.js 53 2012-11-22 08:48:14Z xhaggi $ * * Copyright (c) 2012 xhaggi * https://sourceforge.net/projects/jsearchdropdown/ * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ (function($) { // register plugin var plugin = register("searchable"); // defaults plugin.defaults = { maxListSize: 100, maxMultiMatch: 50, exactMatch: false, wildcards: true, ignoreCase: true, warnMultiMatch: "top {0} matches ...", warnNoMatch: "no matches ...", latency: 200, zIndex: "auto" }; /** * Execute function * element-specific code here * @param {Options} settings Settings */ plugin.execute = function(settings, zindex) { var timer = null; var searchCache = null; var search = null; // do not attach on IE6 or lower if ($.browser.msie && parseInt(jQuery.browser.version) < 7) return this; // only active select elements with drop down capability if (this.nodeName != "SELECT" || this.size > 1) return this; var self = $(this); var storage = {index: -1, options: null}; // holds data for restoring var idxAttr = "lang"; var enabled = false; // detecting chrome $.browser.chrome = /chrome/.test(navigator.userAgent.toLowerCase()); if($.browser.chrome) $.browser.safari = false; // lets you override the options // inside the dom objects class property // requires the jQuery metadata plugin //
ddd
if ($.meta){ settings = $.extend({}, options, self.data()); } // objects var wrapper = $("
"); var overlay = $("
"); var input = $(""); var selector = $(" elements always return scrollTop of 0 // Affects up to Opera 10 beta 1, can be removed if bug is fixed // http://www.greywyvern.com/code/opera/bugs/selectScrollTop return true; //jun's if($.browser.opera && parseFloat(jQuery.browser.version) >= 9.8) return true; // get font-size of option var fs = Math.floor(parseFloat(/([0-9\.]+)px/.exec(selectorHelper.option(0).css("font-size")))); // calc line height depends on browser var fsdiff = 4; if($.browser.opera) fsdiff = 2.5; if($.browser.safari || $.browser.chrome) fsdiff = 3; fs += Math.round(fs / fsdiff); // set selectedIndex depends on mouse position and line height selectorHelper.selectedIndex(Math.floor((e.pageY - selector.offset().top + this.scrollTop) / fs)); }); // toggle click event on overlay div overlay.click(function(e) { input.click(); }); // trigger event keyup input.keyup(function(e) { // break searching while using navigation keys if(jQuery.inArray(e.keyCode, new Array(9, 13, 16, 33, 34, 35, 36, 38, 40)) > -1) return true; // set search text search = $.trim(input.val().toLowerCase()); // if a previous timer is running, stop it clearSearchTimer(); // start new timer timer = setTimeout(searching, settings.latency); }); // trigger keydown event for keyboard usage input.keydown(function(e) { // tab stop if(e.keyCode == 9) { disable(e); } // return on shift, ctrl, alt key mode if(e.shiftKey || e.ctrlKey || e.altKey) return; // which key is pressed switch(e.keyCode) { case 13: // enter disable(e); self.focus(); break; case 27: // escape disable(e, true); self.focus(); break; case 33: // pgup if (selectorHelper.selectedIndex() - selector.attr("size") > 0) { selectorHelper.selectedIndex(selectorHelper.selectedIndex() - selector.attr("size")); } else { selectorHelper.selectedIndex(0); } synchronize(); break; case 34: // pgdown if (selectorHelper.selectedIndex() + selector.attr("size") < selector.get(0).options.length - 1) { selectorHelper.selectedIndex(selectorHelper.selectedIndex() + selector.attr("size")); } else { selectorHelper.selectedIndex(selector.get(0).options.length-1); } synchronize(); break; case 38: // up if (selectorHelper.selectedIndex() > 0){ selectorHelper.selectedIndex(selectorHelper.selectedIndex()-1); synchronize(); } break; case 40: // down if (selectorHelper.selectedIndex() < selector.get(0).options.length - 1){ selectorHelper.selectedIndex(selectorHelper.selectedIndex()+1); synchronize(); } break; default: return true; } // we handled the key.stop // doing anything with it! return false; }); /** * Draw the needed elements */ function draw() { // fix some styles self.css("text-decoration", "none"); self.width(self.outerWidth()); self.height(self.outerHeight()); // wrapper styles wrapper.css("position", "relative"); wrapper.css("width", self.outerWidth()); // relative div needs an z-index (related to IE z-index bug) if($.browser.msie) wrapper.css("z-index", zindex); // overlay div to block events of source select element overlay.css({ "position": "absolute", "top": 0, "left": 0, "width": self.outerWidth(), "height": self.outerHeight(), "background-color": "#FFFFFF", "opacity": "0.01" }); // overlay text field for searching capability input.attr("type", "text"); input.hide(); input.height(self.outerHeight()); // default styles for text field input.css({ "position": "absolute", "top": 0, "left": 0, "margin": "0px", "padding": "0px", "outline-style": "none", "border-style": "solid", "border-bottom-style": "none", "border-color": "transparent", "background-color": "transparent" // "background-color": "red" }); // copy selected styles to text field var sty = new Array(); sty.push("border-left-width"); sty.push("border-top-width"); //sty.push("font-family"); sty.push("font-size"); sty.push("font-stretch"); sty.push("font-variant"); sty.push("font-weight"); sty.push("color"); sty.push("text-align"); sty.push("text-indent"); sty.push("text-shadow"); sty.push("text-transform"); sty.push("padding-left"); sty.push("padding-top"); for(var i=0; i < sty.length;i++) input.css(sty[i], self.css(sty[i])); // adjust search text field // IE7 if($.browser.msie && parseInt(jQuery.browser.version) < 8) { input.css("padding", "0px"); input.css("padding-left", "3px"); input.css("border-left-width", "2px"); input.css("border-top-width", "3px"); } // chrome else if($.browser.chrome) { input.height(self.innerHeight()); input.css("text-transform", "none"); input.css("padding-left", parseFloatPx(input.css("padding-left"))+3); input.css("padding-top", 2); } // safari else if($.browser.safari) { input.height(self.innerHeight()); input.css("padding-top", 2); input.css("padding-left", 3); input.css("text-transform", "none"); } // opera else if($.browser.opera) { input.height(self.innerHeight()); var pl = parseFloatPx(self.css("padding-left")); input.css("padding-left", pl == 1 ? pl+1 : pl); input.css("padding-top", 0); } else if($.browser.mozilla) { input.css("padding-top", "0px"); input.css("border-top", "0px"); input.css("padding-left", parseFloatPx(self.css("padding-left"))+3); } // all other browsers else { input.css("padding-left", parseFloatPx(self.css("padding-left"))+3); input.css("padding-top", parseFloatPx(self.css("padding-top"))+1); } // adjust width of search field var offset = parseFloatPx(self.css("padding-left")) + parseFloatPx(self.css("padding-right")) + parseFloatPx(self.css("border-left-width")) + parseFloatPx(self.css("border-left-width")) + 23; input.width(self.outerWidth() - offset); // store css width of source select object then set width // to auto to obtain the maximum width depends on the longest entry. // this is nessesary to set the width of the selector, because min-width // do not work in all browser. var w = self.css("width"); var ow = self.outerWidth(); self.css("width", "auto"); ow = ow > self.outerWidth() ? ow : self.outerWidth(); self.css("width", w); // entries selector replacement selector.hide(); selectorHelper.size(self.get(0).length); selector.css({ "position": "absolute", "top": self.outerHeight(), "left": 0, "width": ow, "border": "1px solid #333", "font-weight": "normal", "padding": 0, "background-color": self.css("background-color"), "text-transform": self.css("text-transform") }); // z-index handling var zIndex = /^\d+$/.test(self.css("z-index")) ? self.css("z-index") : 1; // if z-index option is defined, use it instead of select element z-index if (settings.zIndex && /^\d+$/.test(settings.zIndex)) zIndex = settings.zIndex; overlay.css("z-index", (zIndex).toString(10)); input.css("z-index", (zIndex+1).toString(10)); selector.css("z-index", (zIndex+2).toString(10)); // append to container self.wrap(wrapper); self.after(overlay); self.after(input); self.after(selector); }; /** * Enable the search facilities * * @param {Object} e Event * @param {boolean} s Show selector * @param {boolean} v Verbose enabling */ function enable(e, s, v) { // exit event on disabled select element if(self.attr("disabled")) return false; // prepend empty option self.prepend("