From 1e2d35f206238b3f5222ae09e6112bcb9cb8bd3a Mon Sep 17 00:00:00 2001 From: Pavel Djundik Date: Thu, 24 Aug 2017 17:44:40 +0300 Subject: [PATCH] Move all auto completion code to a separate file --- client/js/autocompletion.js | 217 ++++++++++++++++++++++++++++++++++++ client/js/lounge.js | 215 +---------------------------------- 2 files changed, 219 insertions(+), 213 deletions(-) create mode 100644 client/js/autocompletion.js diff --git a/client/js/autocompletion.js b/client/js/autocompletion.js new file mode 100644 index 00000000..cdd39ec6 --- /dev/null +++ b/client/js/autocompletion.js @@ -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 `${emojiMap[original]} ${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: "", + post: "" + }).rendered]; + } + return i; + }); + + callback(matchingColorCodes); + }, + template(value) { + return `${value[1]}`; + }, + 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: "", + post: "" + }).rendered]; + } + return pair; + }) + .map((pair) => pair.concat(match[1])); // Needed to pass fg color to `template`... + + callback(matchingColorCodes); + }, + template(value) { + return `${value[1]}`; + }, + 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: "", + post: "" + } + ); + 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); +} diff --git a/client/js/lounge.js b/client/js/lounge.js index 9fcf8cb1..99b0b91c 100644 --- a/client/js/lounge.js +++ b/client/js/lounge.js @@ -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 `${emojiMap[original]} ${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: "", - post: "" - }).rendered]; - } - return i; - }); - - callback(matchingColorCodes); - }, - template(value) { - return `${value[1]}`; - }, - 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: "", - post: "" - }).rendered]; - } - return pair; - }) - .map((pair) => pair.concat(match[1])); // Needed to pass fg color to `template`... - - callback(matchingColorCodes); - }, - template(value) { - return `${value[1]}`; - }, - 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: "", - post: "" - } - ); - 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);