/*! * tabcomplete * http://github.com/erming/tabcomplete * v1.3.1 */ (function($) { var keys = { backspace: 8, tab: 9, up: 38, down: 40 }; $.tabcomplete = {}; $.tabcomplete.defaultOptions = { after: "", arrowKeys: false, caseSensitive: false, hint: "placeholder", minLength: 1 }; $.fn.tab = // Alias $.fn.tabcomplete = function(args, options) { if (this.length > 1) { return this.each(function() { $(this).tabcomplete(args, options); }); } // Only enable the plugin on and elements. var tag = this.prop("tagName"); if (tag != "INPUT" && tag != "TEXTAREA") { return; } // Set default options. options = $.extend( $.tabcomplete.defaultOptions, options ); // Remove any leftovers. // This allows us to override the plugin if necessary. this.unbind(".tabcomplete"); this.prev(".hint").remove(); var self = this; var backspace = false; var i = -1; var words = []; var last = ""; var hint = $.noop; // Determine what type of hinting to use. switch (options.hint) { case "placeholder": hint = placeholder; break; case "select": hint = select; break; } this.on("input.tabcomplete", function() { var input = self.val(); var word = input.split(/ |\n/).pop(); // Reset iteration. i = -1; last = ""; words = []; // Check for matches if the current word is the last word. if (self[0].selectionStart == input.length && word.length) { if (typeof args === "function") { // If the user supplies a function, invoke it // and keep the result. words = args(word); } else { // Otherwise, call the .match() function. words = match(word, args, options.caseSensitive); } // Append 'after' to each word. if (options.after) { words = $.map(words, function(w) { return w + options.after; }); } } // Emit the number of matching words with the 'match' event. self.trigger("match", words.length); if (options.hint) { if (!(options.hint == "select" && backspace) && word.length >= options.minLength) { // Show hint. hint.call(self, words[0]); } else { // Clear hinting. // This call is needed when using backspace. hint.call(self, ""); } } if (backspace) { backspace = false; } }); this.on("keydown.tabcomplete", function(e) { var key = e.which; if (key == keys.tab || (options.arrowKeys && (key == keys.up || key == keys.down))) { // Don't lose focus on tab click. e.preventDefault(); // Iterate the matches with tab and the up and down keys by incrementing // or decrementing the 'i' variable. if (key != keys.up) { i++; } else { if (i == -1) return; if (i == 0) { // Jump to the last word. i = words.length - 1; } else { i--; } } // Get next match. var word = words[i % words.length]; if (!word) { return; } var value = self.val(); last = last || value.split(/ |\n/).pop(); // Return if the 'minLength' requirement isn't met. if (last.length < options.minLength) { return; } // Update element with the completed text. var text = value.substr(0, self[0].selectionStart - last.length) + word; self.val(text); // Put the cursor at the end after completion. // This isn't strictly necessary, but solves an issue with // Internet Explorer. if (options.hint == "select") { self[0].selectionStart = text.length; } // Remember the word until next time. last = word; // Emit event. self.trigger("tabcomplete", last); if (options.hint) { // Turn off any additional hinting. hint.call(self, ""); } } else if (e.which == keys.backspace) { // Remember that backspace was pressed. This is used // by the 'input' event. backspace = true; // Reset iteration. i = -1; last = ""; } }); if (options.hint) { // If enabled, turn on hinting. hint.call(this, ""); } return this; } // Simple matching. // Filter the array and return the items that begins with 'word'. function match(word, array, caseSensitive) { return $.grep( array, function(w) { if (caseSensitive) { return !w.indexOf(word); } else { return !w.toLowerCase().indexOf(word.toLowerCase()); } } ); } // Show placeholder text. // This works by creating a copy of the input and placing it behind // the real input. function placeholder(word) { var input = this; var clone = input.prev(".hint"); input.css({ backgroundColor: "transparent", position: "relative", }); // Lets create a clone of the input if it does // not already exist. if (!clone.length) { input.wrap( $("").css({position: "relative", height: input.css("height")}) ); clone = input .clone() .attr("tabindex", -1) .removeAttr("id name placeholder") .addClass("hint") .insertBefore(input); clone.css({ position: "absolute", }); } var hint = ""; if (typeof word !== "undefined") { var value = input.val(); hint = value + word.substr(value.split(/ |\n/).pop().length); } clone.val(hint); } // Hint by selecting part of the suggested word. function select(word) { var input = this; var value = input.val(); if (word) { input.val( value + word.substr(value.split(/ |\n/).pop().length) ); // Select hint. input[0].selectionStart = value.length; } } })(jQuery);