Merge pull request #1453 from thelounge/xpaw/tabcomplete-module
Move all auto completion code to a separate file
This commit is contained in:
commit
19f3cbd10e
217
client/js/autocompletion.js
Normal file
217
client/js/autocompletion.js
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
const $ = require("jquery");
|
||||||
|
const fuzzy = require("fuzzy");
|
||||||
|
const emojiMap = require("./libs/simplemap.json");
|
||||||
|
const options = require("./options");
|
||||||
|
const constants = require("./constants");
|
||||||
|
require("jquery-textcomplete");
|
||||||
|
require("./libs/jquery/tabcomplete");
|
||||||
|
|
||||||
|
const chat = $("#chat");
|
||||||
|
const sidebar = $("#sidebar");
|
||||||
|
const emojiSearchTerms = Object.keys(emojiMap);
|
||||||
|
const emojiStrategy = {
|
||||||
|
id: "emoji",
|
||||||
|
match: /\B:([-+\w:?]{2,}):?$/,
|
||||||
|
search(term, callback) {
|
||||||
|
// Trim colon from the matched term,
|
||||||
|
// as we are unable to get a clean string from match regex
|
||||||
|
term = term.replace(/:$/, ""),
|
||||||
|
callback(fuzzyGrep(term, emojiSearchTerms));
|
||||||
|
},
|
||||||
|
template([string, original]) {
|
||||||
|
return `<span class="emoji">${emojiMap[original]}</span> ${string}`;
|
||||||
|
},
|
||||||
|
replace([, original]) {
|
||||||
|
return emojiMap[original];
|
||||||
|
},
|
||||||
|
index: 1
|
||||||
|
};
|
||||||
|
|
||||||
|
const nicksStrategy = {
|
||||||
|
id: "nicks",
|
||||||
|
match: /\B(@([a-zA-Z_[\]\\^{}|`@][a-zA-Z0-9_[\]\\^{}|`-]*)?)$/,
|
||||||
|
search(term, callback) {
|
||||||
|
term = term.slice(1);
|
||||||
|
if (term[0] === "@") {
|
||||||
|
callback(completeNicks(term.slice(1), true)
|
||||||
|
.map((val) => ["@" + val[0], "@" + val[1]]));
|
||||||
|
} else {
|
||||||
|
callback(completeNicks(term, true));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
template([string, ]) {
|
||||||
|
return string;
|
||||||
|
},
|
||||||
|
replace([, original]) {
|
||||||
|
return original;
|
||||||
|
},
|
||||||
|
index: 1
|
||||||
|
};
|
||||||
|
|
||||||
|
const chanStrategy = {
|
||||||
|
id: "chans",
|
||||||
|
match: /\B((#|\+|&|![A-Z0-9]{5})([^\x00\x0A\x0D\x20\x2C\x3A]+(:[^\x00\x0A\x0D\x20\x2C\x3A]*)?)?)$/,
|
||||||
|
search(term, callback, match) {
|
||||||
|
callback(completeChans(match[0]));
|
||||||
|
},
|
||||||
|
template([string,]) {
|
||||||
|
return string;
|
||||||
|
},
|
||||||
|
replace([, original]) {
|
||||||
|
return original;
|
||||||
|
},
|
||||||
|
index: 1
|
||||||
|
};
|
||||||
|
|
||||||
|
const commandStrategy = {
|
||||||
|
id: "commands",
|
||||||
|
match: /^\/(\w*)$/,
|
||||||
|
search(term, callback) {
|
||||||
|
callback(completeCommands("/" + term));
|
||||||
|
},
|
||||||
|
template([string, ]) {
|
||||||
|
return string;
|
||||||
|
},
|
||||||
|
replace([, original]) {
|
||||||
|
return original;
|
||||||
|
},
|
||||||
|
index: 1
|
||||||
|
};
|
||||||
|
|
||||||
|
const foregroundColorStrategy = {
|
||||||
|
id: "foreground-colors",
|
||||||
|
match: /\x03(\d{0,2}|[A-Za-z ]{0,10})$/,
|
||||||
|
search(term, callback) {
|
||||||
|
term = term.toLowerCase();
|
||||||
|
|
||||||
|
const matchingColorCodes = constants.colorCodeMap
|
||||||
|
.filter((i) => fuzzy.test(term, i[0]) || fuzzy.test(term, i[1]))
|
||||||
|
.map((i) => {
|
||||||
|
if (fuzzy.test(term, i[1])) {
|
||||||
|
return [i[0], fuzzy.match(term, i[1], {
|
||||||
|
pre: "<b>",
|
||||||
|
post: "</b>"
|
||||||
|
}).rendered];
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
});
|
||||||
|
|
||||||
|
callback(matchingColorCodes);
|
||||||
|
},
|
||||||
|
template(value) {
|
||||||
|
return `<span class="irc-fg${parseInt(value[0], 10)}">${value[1]}</span>`;
|
||||||
|
},
|
||||||
|
replace(value) {
|
||||||
|
return "\x03" + value[0];
|
||||||
|
},
|
||||||
|
index: 1
|
||||||
|
};
|
||||||
|
|
||||||
|
const backgroundColorStrategy = {
|
||||||
|
id: "background-colors",
|
||||||
|
match: /\x03(\d{2}),(\d{0,2}|[A-Za-z ]{0,10})$/,
|
||||||
|
search(term, callback, match) {
|
||||||
|
term = term.toLowerCase();
|
||||||
|
const matchingColorCodes = constants.colorCodeMap
|
||||||
|
.filter((i) => fuzzy.test(term, i[0]) || fuzzy.test(term, i[1]))
|
||||||
|
.map((pair) => {
|
||||||
|
if (fuzzy.test(term, pair[1])) {
|
||||||
|
return [pair[0], fuzzy.match(term, pair[1], {
|
||||||
|
pre: "<b>",
|
||||||
|
post: "</b>"
|
||||||
|
}).rendered];
|
||||||
|
}
|
||||||
|
return pair;
|
||||||
|
})
|
||||||
|
.map((pair) => pair.concat(match[1])); // Needed to pass fg color to `template`...
|
||||||
|
|
||||||
|
callback(matchingColorCodes);
|
||||||
|
},
|
||||||
|
template(value) {
|
||||||
|
return `<span class="irc-fg${parseInt(value[2], 10)} irc-bg irc-bg${parseInt(value[0], 10)}">${value[1]}</span>`;
|
||||||
|
},
|
||||||
|
replace(value) {
|
||||||
|
return "\x03$1," + value[0];
|
||||||
|
},
|
||||||
|
index: 2
|
||||||
|
};
|
||||||
|
|
||||||
|
const input = $("#input")
|
||||||
|
.tab((word) => completeNicks(word, false), {hint: false})
|
||||||
|
.on("autocomplete:on", function() {
|
||||||
|
enableAutocomplete();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (options.autocomplete) {
|
||||||
|
enableAutocomplete();
|
||||||
|
}
|
||||||
|
|
||||||
|
function enableAutocomplete() {
|
||||||
|
input.textcomplete([
|
||||||
|
emojiStrategy, nicksStrategy, chanStrategy, commandStrategy,
|
||||||
|
foregroundColorStrategy, backgroundColorStrategy
|
||||||
|
], {
|
||||||
|
dropdownClassName: "textcomplete-menu",
|
||||||
|
placement: "top"
|
||||||
|
}).on({
|
||||||
|
"textComplete:show": function() {
|
||||||
|
$(this).data("autocompleting", true);
|
||||||
|
},
|
||||||
|
"textComplete:hide": function() {
|
||||||
|
$(this).data("autocompleting", false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function fuzzyGrep(term, array) {
|
||||||
|
const results = fuzzy.filter(
|
||||||
|
term,
|
||||||
|
array,
|
||||||
|
{
|
||||||
|
pre: "<b>",
|
||||||
|
post: "</b>"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return results.map((el) => [el.string, el.original]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function completeNicks(word, isFuzzy) {
|
||||||
|
const users = chat.find(".active .users");
|
||||||
|
word = word.toLowerCase();
|
||||||
|
|
||||||
|
// Lobbies and private chats do not have an user list
|
||||||
|
if (!users.length) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const words = users.data("nicks");
|
||||||
|
if (isFuzzy) {
|
||||||
|
return fuzzyGrep(word, words);
|
||||||
|
}
|
||||||
|
return $.grep(
|
||||||
|
words,
|
||||||
|
(w) => !w.toLowerCase().indexOf(word)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function completeCommands(word) {
|
||||||
|
const words = constants.commands.slice();
|
||||||
|
|
||||||
|
return fuzzyGrep(word, words);
|
||||||
|
}
|
||||||
|
|
||||||
|
function completeChans(word) {
|
||||||
|
const words = [];
|
||||||
|
|
||||||
|
sidebar.find(".chan")
|
||||||
|
.each(function() {
|
||||||
|
const self = $(this);
|
||||||
|
if (!self.hasClass("lobby")) {
|
||||||
|
words.push(self.data("title"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return fuzzyGrep(word, words);
|
||||||
|
}
|
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
// vendor libraries
|
// vendor libraries
|
||||||
require("jquery-ui/ui/widgets/sortable");
|
require("jquery-ui/ui/widgets/sortable");
|
||||||
require("jquery-textcomplete");
|
|
||||||
const $ = require("jquery");
|
const $ = require("jquery");
|
||||||
const moment = require("moment");
|
const moment = require("moment");
|
||||||
const Mousetrap = require("mousetrap");
|
const Mousetrap = require("mousetrap");
|
||||||
@ -10,18 +9,17 @@ const URI = require("urijs");
|
|||||||
const fuzzy = require("fuzzy");
|
const fuzzy = require("fuzzy");
|
||||||
|
|
||||||
// our libraries
|
// our libraries
|
||||||
const emojiMap = require("./libs/simplemap.json");
|
|
||||||
require("./libs/jquery/inputhistory");
|
require("./libs/jquery/inputhistory");
|
||||||
require("./libs/jquery/stickyscroll");
|
require("./libs/jquery/stickyscroll");
|
||||||
require("./libs/jquery/tabcomplete");
|
|
||||||
const helpers_roundBadgeNumber = require("./libs/handlebars/roundBadgeNumber");
|
const helpers_roundBadgeNumber = require("./libs/handlebars/roundBadgeNumber");
|
||||||
const slideoutMenu = require("./libs/slideout");
|
const slideoutMenu = require("./libs/slideout");
|
||||||
const templates = require("../views");
|
const templates = require("../views");
|
||||||
const socket = require("./socket");
|
const socket = require("./socket");
|
||||||
require("./socket-events");
|
require("./socket-events");
|
||||||
const constants = require("./constants");
|
|
||||||
const storage = require("./localStorage");
|
const storage = require("./localStorage");
|
||||||
|
const options = require("./options");
|
||||||
const utils = require("./utils");
|
const utils = require("./utils");
|
||||||
|
require("./autocompletion");
|
||||||
require("./webpush");
|
require("./webpush");
|
||||||
|
|
||||||
$(function() {
|
$(function() {
|
||||||
@ -44,140 +42,7 @@ $(function() {
|
|||||||
pop.play();
|
pop.play();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Autocompletion Strategies
|
|
||||||
|
|
||||||
const emojiSearchTerms = Object.keys(emojiMap);
|
|
||||||
const emojiStrategy = {
|
|
||||||
id: "emoji",
|
|
||||||
match: /\B:([-+\w:?]{2,}):?$/,
|
|
||||||
search(term, callback) {
|
|
||||||
// Trim colon from the matched term,
|
|
||||||
// as we are unable to get a clean string from match regex
|
|
||||||
term = term.replace(/:$/, ""),
|
|
||||||
callback(fuzzyGrep(term, emojiSearchTerms));
|
|
||||||
},
|
|
||||||
template([string, original]) {
|
|
||||||
return `<span class="emoji">${emojiMap[original]}</span> ${string}`;
|
|
||||||
},
|
|
||||||
replace([, original]) {
|
|
||||||
return emojiMap[original];
|
|
||||||
},
|
|
||||||
index: 1
|
|
||||||
};
|
|
||||||
|
|
||||||
const nicksStrategy = {
|
|
||||||
id: "nicks",
|
|
||||||
match: /\B(@([a-zA-Z_[\]\\^{}|`@][a-zA-Z0-9_[\]\\^{}|`-]*)?)$/,
|
|
||||||
search(term, callback) {
|
|
||||||
term = term.slice(1);
|
|
||||||
if (term[0] === "@") {
|
|
||||||
callback(completeNicks(term.slice(1), true)
|
|
||||||
.map((val) => ["@" + val[0], "@" + val[1]]));
|
|
||||||
} else {
|
|
||||||
callback(completeNicks(term, true));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
template([string, ]) {
|
|
||||||
return string;
|
|
||||||
},
|
|
||||||
replace([, original]) {
|
|
||||||
return original;
|
|
||||||
},
|
|
||||||
index: 1
|
|
||||||
};
|
|
||||||
|
|
||||||
const chanStrategy = {
|
|
||||||
id: "chans",
|
|
||||||
match: /\B((#|\+|&|![A-Z0-9]{5})([^\x00\x0A\x0D\x20\x2C\x3A]+(:[^\x00\x0A\x0D\x20\x2C\x3A]*)?)?)$/,
|
|
||||||
search(term, callback, match) {
|
|
||||||
callback(completeChans(match[0]));
|
|
||||||
},
|
|
||||||
template([string,]) {
|
|
||||||
return string;
|
|
||||||
},
|
|
||||||
replace([, original]) {
|
|
||||||
return original;
|
|
||||||
},
|
|
||||||
index: 1
|
|
||||||
};
|
|
||||||
|
|
||||||
const commandStrategy = {
|
|
||||||
id: "commands",
|
|
||||||
match: /^\/(\w*)$/,
|
|
||||||
search(term, callback) {
|
|
||||||
callback(completeCommands("/" + term));
|
|
||||||
},
|
|
||||||
template([string, ]) {
|
|
||||||
return string;
|
|
||||||
},
|
|
||||||
replace([, original]) {
|
|
||||||
return original;
|
|
||||||
},
|
|
||||||
index: 1
|
|
||||||
};
|
|
||||||
|
|
||||||
const foregroundColorStrategy = {
|
|
||||||
id: "foreground-colors",
|
|
||||||
match: /\x03(\d{0,2}|[A-Za-z ]{0,10})$/,
|
|
||||||
search(term, callback) {
|
|
||||||
term = term.toLowerCase();
|
|
||||||
|
|
||||||
const matchingColorCodes = constants.colorCodeMap
|
|
||||||
.filter((i) => fuzzy.test(term, i[0]) || fuzzy.test(term, i[1]))
|
|
||||||
.map((i) => {
|
|
||||||
if (fuzzy.test(term, i[1])) {
|
|
||||||
return [i[0], fuzzy.match(term, i[1], {
|
|
||||||
pre: "<b>",
|
|
||||||
post: "</b>"
|
|
||||||
}).rendered];
|
|
||||||
}
|
|
||||||
return i;
|
|
||||||
});
|
|
||||||
|
|
||||||
callback(matchingColorCodes);
|
|
||||||
},
|
|
||||||
template(value) {
|
|
||||||
return `<span class="irc-fg${parseInt(value[0], 10)}">${value[1]}</span>`;
|
|
||||||
},
|
|
||||||
replace(value) {
|
|
||||||
return "\x03" + value[0];
|
|
||||||
},
|
|
||||||
index: 1
|
|
||||||
};
|
|
||||||
|
|
||||||
const backgroundColorStrategy = {
|
|
||||||
id: "background-colors",
|
|
||||||
match: /\x03(\d{2}),(\d{0,2}|[A-Za-z ]{0,10})$/,
|
|
||||||
search(term, callback, match) {
|
|
||||||
term = term.toLowerCase();
|
|
||||||
const matchingColorCodes = constants.colorCodeMap
|
|
||||||
.filter((i) => fuzzy.test(term, i[0]) || fuzzy.test(term, i[1]))
|
|
||||||
.map((pair) => {
|
|
||||||
if (fuzzy.test(term, pair[1])) {
|
|
||||||
return [pair[0], fuzzy.match(term, pair[1], {
|
|
||||||
pre: "<b>",
|
|
||||||
post: "</b>"
|
|
||||||
}).rendered];
|
|
||||||
}
|
|
||||||
return pair;
|
|
||||||
})
|
|
||||||
.map((pair) => pair.concat(match[1])); // Needed to pass fg color to `template`...
|
|
||||||
|
|
||||||
callback(matchingColorCodes);
|
|
||||||
},
|
|
||||||
template(value) {
|
|
||||||
return `<span class="irc-fg${parseInt(value[2], 10)} irc-bg irc-bg${parseInt(value[0], 10)}">${value[1]}</span>`;
|
|
||||||
},
|
|
||||||
replace(value) {
|
|
||||||
return "\x03$1," + value[0];
|
|
||||||
},
|
|
||||||
index: 2
|
|
||||||
};
|
|
||||||
|
|
||||||
var options = require("./options");
|
|
||||||
|
|
||||||
var windows = $("#windows");
|
var windows = $("#windows");
|
||||||
|
|
||||||
var viewport = $("#viewport");
|
var viewport = $("#viewport");
|
||||||
var sidebarSlide = slideoutMenu(viewport[0], sidebar[0]);
|
var sidebarSlide = slideoutMenu(viewport[0], sidebar[0]);
|
||||||
var contextMenuContainer = $("#context-menu-container");
|
var contextMenuContainer = $("#context-menu-container");
|
||||||
@ -290,33 +155,8 @@ $(function() {
|
|||||||
) + "px";
|
) + "px";
|
||||||
|
|
||||||
chat.find(".chan.active .chat").trigger("msg.sticky"); // fix growing
|
chat.find(".chan.active .chat").trigger("msg.sticky"); // fix growing
|
||||||
})
|
|
||||||
.tab((word) => completeNicks(word, false), {hint: false})
|
|
||||||
.on("autocomplete:on", function() {
|
|
||||||
enableAutocomplete();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (options.autocomplete) {
|
|
||||||
enableAutocomplete();
|
|
||||||
}
|
|
||||||
|
|
||||||
function enableAutocomplete() {
|
|
||||||
input.textcomplete([
|
|
||||||
emojiStrategy, nicksStrategy, chanStrategy, commandStrategy,
|
|
||||||
foregroundColorStrategy, backgroundColorStrategy
|
|
||||||
], {
|
|
||||||
dropdownClassName: "textcomplete-menu",
|
|
||||||
placement: "top"
|
|
||||||
}).on({
|
|
||||||
"textComplete:show": function() {
|
|
||||||
$(this).data("autocompleting", true);
|
|
||||||
},
|
|
||||||
"textComplete:hide": function() {
|
|
||||||
$(this).data("autocompleting", false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
var focus = $.noop;
|
var focus = $.noop;
|
||||||
if (!("ontouchstart" in window || navigator.maxTouchPoints > 0)) {
|
if (!("ontouchstart" in window || navigator.maxTouchPoints > 0)) {
|
||||||
focus = function() {
|
focus = function() {
|
||||||
@ -932,57 +772,6 @@ $(function() {
|
|||||||
}
|
}
|
||||||
}());
|
}());
|
||||||
|
|
||||||
function fuzzyGrep(term, array) {
|
|
||||||
const results = fuzzy.filter(
|
|
||||||
term,
|
|
||||||
array,
|
|
||||||
{
|
|
||||||
pre: "<b>",
|
|
||||||
post: "</b>"
|
|
||||||
}
|
|
||||||
);
|
|
||||||
return results.map((el) => [el.string, el.original]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function completeNicks(word, isFuzzy) {
|
|
||||||
const users = chat.find(".active .users");
|
|
||||||
word = word.toLowerCase();
|
|
||||||
|
|
||||||
// Lobbies and private chats do not have an user list
|
|
||||||
if (!users.length) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const words = users.data("nicks");
|
|
||||||
if (isFuzzy) {
|
|
||||||
return fuzzyGrep(word, words);
|
|
||||||
}
|
|
||||||
return $.grep(
|
|
||||||
words,
|
|
||||||
(w) => !w.toLowerCase().indexOf(word)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function completeCommands(word) {
|
|
||||||
const words = constants.commands.slice();
|
|
||||||
|
|
||||||
return fuzzyGrep(word, words);
|
|
||||||
}
|
|
||||||
|
|
||||||
function completeChans(word) {
|
|
||||||
const words = [];
|
|
||||||
|
|
||||||
sidebar.find(".chan")
|
|
||||||
.each(function() {
|
|
||||||
const self = $(this);
|
|
||||||
if (!self.hasClass("lobby")) {
|
|
||||||
words.push(self.data("title"));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return fuzzyGrep(word, words);
|
|
||||||
}
|
|
||||||
|
|
||||||
$(document).on("visibilitychange focus click", () => {
|
$(document).on("visibilitychange focus click", () => {
|
||||||
if (sidebar.find(".highlight").length === 0) {
|
if (sidebar.find(".highlight").length === 0) {
|
||||||
utils.toggleNotificationMarkers(false);
|
utils.toggleNotificationMarkers(false);
|
||||||
|
Loading…
Reference in New Issue
Block a user