Rewrite tabcomplete
This commit is contained in:
parent
df703dc73a
commit
e462ed6270
@ -2,19 +2,22 @@
|
|||||||
|
|
||||||
const $ = require("jquery");
|
const $ = require("jquery");
|
||||||
const fuzzy = require("fuzzy");
|
const fuzzy = require("fuzzy");
|
||||||
|
const Mousetrap = require("mousetrap");
|
||||||
const emojiMap = require("./libs/simplemap.json");
|
const emojiMap = require("./libs/simplemap.json");
|
||||||
const constants = require("./constants");
|
const constants = require("./constants");
|
||||||
require("./libs/jquery/tabcomplete");
|
|
||||||
|
|
||||||
const input = $("#input");
|
const input = $("#input");
|
||||||
const Textcomplete = require("textcomplete/lib/textcomplete").default;
|
const Textcomplete = require("textcomplete/lib/textcomplete").default;
|
||||||
const Textarea = require("textcomplete/lib/textarea").default;
|
const Textarea = require("textcomplete/lib/textarea").default;
|
||||||
const editor = new Textarea(input.get(0));
|
|
||||||
let textcomplete;
|
let textcomplete;
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
enable: enableAutocomplete,
|
enable: enableAutocomplete,
|
||||||
disable: () => textcomplete.destroy(false),
|
disable: () => {
|
||||||
|
input.unbind("input.tabcomplete");
|
||||||
|
Mousetrap(input.get(0)).unbind("tab", "keydown");
|
||||||
|
textcomplete.destroy();
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const chat = $("#chat");
|
const chat = $("#chat");
|
||||||
@ -147,13 +150,53 @@ const backgroundColorStrategy = {
|
|||||||
index: 2,
|
index: 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
input
|
function enableAutocomplete() {
|
||||||
.tab((word) => completeNicks(word, false), {hint: false})
|
let tabCount = 0;
|
||||||
.on("autocomplete:on", function() {
|
let currentMatches = [];
|
||||||
enableAutocomplete();
|
|
||||||
|
input.on("input.tabcomplete", () => {
|
||||||
|
tabCount = 0;
|
||||||
|
currentMatches = [];
|
||||||
});
|
});
|
||||||
|
|
||||||
function enableAutocomplete() {
|
Mousetrap(input.get(0)).bind("tab", (e) => {
|
||||||
|
if (input.data("autocompleting")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
const text = input.val();
|
||||||
|
|
||||||
|
if (input.get(0).selectionStart !== text.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let lastWord;
|
||||||
|
|
||||||
|
if (tabCount === 0) {
|
||||||
|
lastWord = text.split(/\s/).pop();
|
||||||
|
|
||||||
|
if (lastWord.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentMatches = completeNicks(lastWord, false);
|
||||||
|
|
||||||
|
if (currentMatches.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
lastWord = nicksStrategy.replace([0, currentMatches[(tabCount - 1) % currentMatches.length]]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const matchedNick = currentMatches[tabCount % currentMatches.length];
|
||||||
|
input.val(text.substr(0, input.get(0).selectionStart - lastWord.length) + nicksStrategy.replace([0, matchedNick]));
|
||||||
|
|
||||||
|
tabCount++;
|
||||||
|
}, "keydown");
|
||||||
|
|
||||||
|
const editor = new Textarea(input.get(0));
|
||||||
textcomplete = new Textcomplete(editor, {
|
textcomplete = new Textcomplete(editor, {
|
||||||
dropdown: {
|
dropdown: {
|
||||||
className: "textcomplete-menu",
|
className: "textcomplete-menu",
|
||||||
|
258
client/js/libs/jquery/tabcomplete.js
vendored
258
client/js/libs/jquery/tabcomplete.js
vendored
@ -1,258 +0,0 @@
|
|||||||
import jQuery from "jquery";
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* 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 <input> and <textarea> 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 && !e.ctrlKey
|
|
||||||
|| (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(
|
|
||||||
$("<div>").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);
|
|
Loading…
Reference in New Issue
Block a user