Shuo/client/components/tabcomplete.js

251 lines
5.4 KiB
JavaScript
Raw Normal View History

/*!
2014-06-06 20:05:47 +00:00
* tabcomplete
2014-06-17 16:20:22 +00:00
* http://github.com/erming/tabcomplete
* v1.3.2
*/
(function($) {
2014-05-24 19:45:28 +00:00
var keys = {
2014-06-06 20:05:47 +00:00
backspace: 8,
2014-05-24 19:45:28 +00:00
tab: 9,
up: 38,
down: 40
2014-05-13 22:47:35 +00:00
};
2014-06-06 20:05:47 +00:00
$.fn.tab = // Alias
$.fn.tabcomplete = function(args, options) {
2014-05-13 22:47:35 +00:00
if (this.length > 1) {
return this.each(function() {
2014-06-06 20:05:47 +00:00
$(this).tabcomplete(args, options);
});
}
2014-05-24 19:45:28 +00:00
// Only enable the plugin on <input> and <textarea> elements.
var tag = this.prop("tagName");
if (tag != "INPUT" && tag != "TEXTAREA") {
return;
2014-05-13 22:47:35 +00:00
}
2014-05-24 19:45:28 +00:00
// Set default options.
options = $.extend({
after: "",
arrowKeys: tag == "INPUT" ? true : false,
caseSensitive: false,
2014-06-06 20:05:47 +00:00
hint: "placeholder",
minLength: 1
2014-05-24 19:45:28 +00:00
}, options);
2014-06-06 20:05:47 +00:00
// Remove any leftovers.
2014-05-13 22:47:35 +00:00
// This allows us to override the plugin if necessary.
2014-06-06 20:05:47 +00:00
this.unbind(".tabcomplete");
this.prev(".hint").remove();
2014-05-13 22:47:35 +00:00
2014-05-24 19:45:28 +00:00
var self = this;
2014-06-06 20:05:47 +00:00
var backspace = false;
2014-05-24 19:45:28 +00:00
var i = -1;
2014-05-13 22:47:35 +00:00
var words = [];
var last = "";
2014-06-06 20:05:47 +00:00
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() {
2014-05-13 22:47:35 +00:00
var input = self.val();
var word = input.split(/ |\n/).pop();
2014-06-06 20:05:47 +00:00
// 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.
2014-06-14 21:21:09 +00:00
words = args.call(self, word);
2014-06-06 20:05:47 +00:00
} 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; });
}
}
2014-05-24 19:45:28 +00:00
// Emit the number of matching words with the 'match' event.
self.trigger("match", words.length);
2014-05-13 22:47:35 +00:00
if (options.hint) {
2014-06-06 20:05:47 +00:00
if (!(options.hint == "select" && backspace) && word.length >= options.minLength) {
// Show hint.
2014-05-13 22:47:35 +00:00
hint.call(self, words[0]);
} else {
// Clear hinting.
// This call is needed when using backspace.
hint.call(self, "");
}
}
2014-06-06 20:05:47 +00:00
if (backspace) {
backspace = false;
}
2014-05-13 22:47:35 +00:00
});
2014-06-06 20:05:47 +00:00
this.on("keydown.tabcomplete", function(e) {
2014-05-13 22:47:35 +00:00
var key = e.which;
2014-06-06 20:05:47 +00:00
if (key == keys.tab
|| (options.arrowKeys && (key == keys.up || key == keys.down))) {
2014-05-13 22:47:35 +00:00
// Don't lose focus on tab click.
e.preventDefault();
2014-05-24 19:45:28 +00:00
// 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--;
}
}
2014-06-06 20:05:47 +00:00
2014-05-13 22:47:35 +00:00
// Get next match.
2014-05-24 19:45:28 +00:00
var word = words[i % words.length];
2014-05-13 22:47:35 +00:00
if (!word) {
return;
}
2014-06-06 20:05:47 +00:00
var value = self.val();
last = last || value.split(/ |\n/).pop();
2014-05-13 22:47:35 +00:00
2014-06-06 20:05:47 +00:00
// Return if the 'minLength' requirement isn't met.
2014-05-13 22:47:35 +00:00
if (last.length < options.minLength) {
return;
}
2014-06-06 20:05:47 +00:00
// 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;
}
2014-05-13 22:47:35 +00:00
// Remember the word until next time.
last = word;
2014-06-06 20:05:47 +00:00
// Emit event.
self.trigger("tabcomplete", last);
2014-05-24 19:45:28 +00:00
2014-05-13 22:47:35 +00:00
if (options.hint) {
// Turn off any additional hinting.
hint.call(self, "");
}
2014-06-06 20:05:47 +00:00
} 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 = "";
}
});
2014-05-24 19:45:28 +00:00
if (options.hint) {
// If enabled, turn on hinting.
hint.call(this, "");
}
return this;
2014-05-13 22:47:35 +00:00
}
// Simple matching.
2014-06-06 20:05:47 +00:00
// Filter the array and return the items that begins with 'word'.
function match(word, array, caseSensitive) {
2014-05-13 22:47:35 +00:00
return $.grep(
array,
function(w) {
if (caseSensitive) {
return !w.indexOf(word);
} else {
return !w.toLowerCase().indexOf(word.toLowerCase());
}
}
);
}
2014-06-06 20:05:47 +00:00
// Show placeholder text.
2014-05-13 22:47:35 +00:00
// This works by creating a copy of the input and placing it behind
// the real input.
2014-06-06 20:05:47 +00:00
function placeholder(word) {
2014-05-13 22:47:35 +00:00
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(
$("<div>").css({position: "relative"})
);
clone = input
.clone()
2014-05-24 19:45:28 +00:00
.attr("tabindex", -1)
2014-05-13 22:47:35 +00:00
.removeAttr("id name placeholder")
.addClass("hint")
.insertBefore(input);
clone.css({
position: "absolute",
});
}
var hint = "";
if (typeof word !== "undefined") {
2014-06-06 20:05:47 +00:00
var value = input.val();
hint = value + word.substr(value.split(/ |\n/).pop().length);
2014-05-13 22:47:35 +00:00
}
clone.val(hint);
}
2014-06-06 20:05:47 +00:00
// 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);