Move all auto completion code to a separate file

This commit is contained in:
Pavel Djundik 2017-08-24 17:44:40 +03:00
parent 60e69a83fc
commit 1e2d35f206
2 changed files with 219 additions and 213 deletions

217
client/js/autocompletion.js Normal file
View 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);
}

View File

@ -2,7 +2,6 @@
// vendor libraries
require("jquery-ui/ui/widgets/sortable");
require("jquery-textcomplete");
const $ = require("jquery");
const moment = require("moment");
const Mousetrap = require("mousetrap");
@ -10,18 +9,17 @@ const URI = require("urijs");
const fuzzy = require("fuzzy");
// our libraries
const emojiMap = require("./libs/simplemap.json");
require("./libs/jquery/inputhistory");
require("./libs/jquery/stickyscroll");
require("./libs/jquery/tabcomplete");
const helpers_roundBadgeNumber = require("./libs/handlebars/roundBadgeNumber");
const slideoutMenu = require("./libs/slideout");
const templates = require("../views");
const socket = require("./socket");
require("./socket-events");
const constants = require("./constants");
const storage = require("./localStorage");
const options = require("./options");
const utils = require("./utils");
require("./autocompletion");
require("./webpush");
$(function() {
@ -44,140 +42,7 @@ $(function() {
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 viewport = $("#viewport");
var sidebarSlide = slideoutMenu(viewport[0], sidebar[0]);
var contextMenuContainer = $("#context-menu-container");
@ -290,33 +155,8 @@ $(function() {
) + "px";
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;
if (!("ontouchstart" in window || navigator.maxTouchPoints > 0)) {
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", () => {
if (sidebar.find(".highlight").length === 0) {
utils.toggleNotificationMarkers(false);