|
|
- // Based on https://github.com/orcnog/autocomplete-whisper/blob/master/scripts/autocomplete-whisper.js
- export default class Autocomplete {
-
- handleRenderSidebarTab(app, html, data) {
- /* Set up markup for our UI to be injected */
- const $whisperMenuContainer = $('<div id="command-menu"></div>');
- const $ghostTextarea = $('<textarea class="chatghosttextarea" autocomplete="off" readonly disabled></textarea>');
- let $whisperMenu = $('<nav id="context-menu" class="expand-up"><ol class="context-items"></ol></nav>');
-
- let regex = new RegExp("\/[A-z]*");
-
- /* Add our UI to the DOM */
- $("#chat-message").before($whisperMenuContainer);
- $("#chat-message").after($ghostTextarea);
-
- /* Unbind original FVTT chat textarea keydown handler and implemnt our own to catch up/down keys first */
- $("#chat-message").off("keydown");
- $("#chat-message").on("keydown.menufocus", jumpToMenuHandler);
- /* Listen for chat input. Do stuff.*/
- $("#chat-message").on("input.whisperer", handleChatInput);
- /* Listen for "]" to close an array of targets (names) */
- $("#chat-message").on("keydown.closearray", listFinishHandler);
- /* Listen for up/down arrow presses to navigate exposed menu */
- $("#command-menu").on("keydown.menufocus", menuNavigationHandler);
- /* Listen for click on a menu item */
- $("#command-menu").on("click", "li", menuItemSelectionHandler);
-
- function handleChatInput() {
-
- if (!game.settings.get("_chatcommands", "autocomplete")) return;
-
- resetGhostText();
- const val = $("#chat-message").val();
- //console.log(val);
- if (val.match(regex)) {
-
- // It's a commands! Show a menu of commands and typeahead text if possible
- // let splt = val.split(regex);
- // console.log(splt);
-
- let input = val;
-
-
- let allCommands = [];
- allCommands = allCommands.concat(window.game.chatCommands.registeredCommands);
-
- if (game.settings.get("_chatcommands", "includeCoreCommands")) {
- allCommands = allCommands.concat(_getCoreCommands());
- }
-
- let matchingCommands = allCommands.filter((target) => {
- const p = target.commandKey.toUpperCase();
- const i = val.toUpperCase();
- return p.indexOf(i) >= 0 && p !== i;
- });
-
- //console.log(matchingCommands);
-
- if (matchingCommands.length > 0) {
-
- // At least one potential target exists.
- // show ghost text to autocomplete if there's a match starting with the characters already typed
- ghostText(input, matchingCommands);
- // set up and display the menu of whisperable names
- let listOfPlayers = "";
- for (let p in matchingCommands) {
- if (isNaN(p)) continue;
- let command = matchingCommands[p];
- const name = command.commandKey;
- let nameHtml = name;
- let startIndex = name.toUpperCase().indexOf(input.toUpperCase());
- if (input && startIndex > -1) {
- nameHtml = name.substr(0, startIndex) + "<strong>" + name.substr(startIndex, input.length) + "</strong>" + name.substr(startIndex + input.length);
- }
- listOfPlayers += `<li class="context-item" data-name="${name}" tabIndex="0"><i class="fas ${command.iconClass} fa-fw" style="padding-right: 5px;"></i>${nameHtml} - ${command.description}</li>`;
- }
- $whisperMenu.find("ol").html(listOfPlayers);
- $("#command-menu").html($whisperMenu);
-
- // set up click-outside listener to close menu
- $(window).on("click.outsidewhispermenu", (e) => {
- var $target = $(e.target);
- if (!$target.closest("#command-menu").length) {
- closeWhisperMenu();
- }
- });
- } else {
- closeWhisperMenu();
- }
- } else {
- closeWhisperMenu();
- }
- }
-
- function _getCoreCommands() {
- let commands = [];
-
- let chatCommands = window.game.chatCommands;
-
- commands.push(chatCommands.createCommandFromData({
- commandKey: "/ic",
- invokeOnCommand: (chatlog, messageText, chatdata) => {
- },
- shouldDisplayToChat: false,
- iconClass: "fa-dice-d20",
- description: "Speak in character"
- }));
-
- commands.push(chatCommands.createCommandFromData({
- commandKey: "/ooc",
- invokeOnCommand: (chatlog, messageText, chatdata) => {
- },
- shouldDisplayToChat: false,
- iconClass: "fa-dice-d20",
- description: "Speak out of character"
- }));
-
- commands.push(chatCommands.createCommandFromData({
- commandKey: "/emote",
- invokeOnCommand: (chatlog, messageText, chatdata) => {
- },
- shouldDisplayToChat: false,
- iconClass: "fa-dice-d20",
- description: "Emote in character"
- }));
-
- commands.push(chatCommands.createCommandFromData({
- commandKey: "/whisper",
- invokeOnCommand: (chatlog, messageText, chatdata) => {
- },
- shouldDisplayToChat: false,
- iconClass: "fa-dice-d20",
- description: "Send a whisper to another player"
- }));
-
- commands.push(chatCommands.createCommandFromData({
- commandKey: "/w",
- invokeOnCommand: (chatlog, messageText, chatdata) => {
- },
- shouldDisplayToChat: false,
- iconClass: "fa-dice-d20",
- description: "Send a whisper to another player"
- }));
-
- commands.push(chatCommands.createCommandFromData({
- commandKey: "/roll",
- invokeOnCommand: (chatlog, messageText, chatdata) => {
- },
- shouldDisplayToChat: false,
- iconClass: "fa-dice-d20",
- description: "Roll dice"
- }));
-
- commands.push(chatCommands.createCommandFromData({
- commandKey: "/gmroll",
- invokeOnCommand: (chatlog, messageText, chatdata) => {
- },
- shouldDisplayToChat: false,
- iconClass: "fa-dice-d20",
- description: "Roll dice that only the GM can see"
- }));
-
- commands.push(chatCommands.createCommandFromData({
- commandKey: "/blindroll",
- invokeOnCommand: (chatlog, messageText, chatdata) => {
- },
- shouldDisplayToChat: false,
- iconClass: "fa-dice-d20",
- description: "Roll dice that are hidden"
- }));
-
- commands.push(chatCommands.createCommandFromData({
- commandKey: "/selfroll",
- invokeOnCommand: (chatlog, messageText, chatdata) => {
- },
- shouldDisplayToChat: false,
- iconClass: "fa-dice-d20",
- description: "Roll dice that only you can see"
- }));
-
- return commands;
- }
-
- function listFinishHandler(e) {
- if (e.which == 221) { // `]`
- let val = $("#chat-message").val();
- if (val.match(listOfNamesRegex)) {
- if (typeof e === "object") e.preventDefault();
- val = val.trim();
- const newval = val.substring(val.length - 1) === "," ? val.substring(0, val.length - 1) : val; // remove `,` from the end
- $("#chat-message").val(newval + "] ");
- closeWhisperMenu();
- }
- }
- }
-
- function jumpToMenuHandler(e) {
- if ($("#command-menu").find("li").length) {
- if (e.which === 38) { // `up`
- $("#command-menu li:last-child").focus();
- return false;
- } else if (e.which === 40) { // `down`
- $("#command-menu li:first-child").focus();
- return false;
- }
- }
- if (game.modules.get("autocomplete-whisper") != undefined) {
- if ($("#whisper-menu").find("li").length) {
- if (e.which === 38) { // `up`
- $("#whisper-menu li:last-child").focus();
- return false;
- } else if (e.which === 40) { // `down`
- $("#whisper-menu li:first-child").focus();
- return false;
- }
- }
- }
- // if player menu is not visible/DNE, execute FVTT's original keydown handler
- ui.chat._onChatKeyDown(e);
- }
-
- function menuNavigationHandler(e) {
- if ($(e.target).is("li.context-item")) {
- if (e.which === 38) { // `up`
- if ($(e.target).is(":first-child")) {
- $("#chat-message").focus();
- } else {
- $(e.target).prev().focus();
- }
- return false;
- } else if (e.which === 40) { // `down`
- if ($(e.target).is(":last-child")) {
- $("#chat-message").focus();
- } else {
- $(e.target).next().focus();
- }
- return false;
- } else if (e.which === 13) { // `enter`
- menuItemSelectionHandler(e);
- return false;
- }
- }
- }
-
- function menuItemSelectionHandler(e) {
- e.stopPropagation();
- var autocompleteText = autocomplete($(e.target).text());
- $("#chat-message").val(autocompleteText.ghost);
- $("#chat-message").focus();
- closeWhisperMenu();
- if ($("#chat-message").val().indexOf("[") > -1) {
- handleChatInput();
- }
- }
-
- function ghostText(input, matches) {
- // show ghost text to autocomplete if there's a match starting with the characters already typed
- let filteredMatches = matches.filter((target) => {
- const p = target.commandKey.toUpperCase();
- const i = input.toUpperCase();
- return p.indexOf(i) === 0 && p !== i;
- });
- if (filteredMatches.length === 1) {
- var autocompleteText = autocomplete(filteredMatches[0].commandKey);
- $(".chatghosttextarea").val(autocompleteText.ghost);
- $(".chatghosttextarea").addClass("show");
- $("#chat-message").on("keydown.ghosttab", (e) => {
- if (e.which == 9) { // tab
- e.preventDefault();
- $("#chat-message").val(autocompleteText.ghost);
- resetGhostText();
- $("#chat-message").focus();
- closeWhisperMenu();
- if ($("#chat-message").val().indexOf("[") > -1) {
- handleChatInput();
- }
- }
- });
- } else {
- resetGhostText();
- }
- }
-
- function autocomplete(match) {
- if (!match) return;
-
- const typedCharacters = $("#chat-message").val();
- let nameToAdd = '';
- if (match.toUpperCase().indexOf(typedCharacters.toUpperCase()) === 0) {
- var restOfTheName = match.substr(typedCharacters.length);
- nameToAdd = typedCharacters + restOfTheName;
- } else {
- nameToAdd = match;
- }
-
- if (nameToAdd.includes(" - ")) {
- nameToAdd = nameToAdd.substr(0, nameToAdd.indexOf(" - "));
- }
-
- const ghostString = nameToAdd;
-
- return ({
- ghost: ghostString
- });
- }
-
- function closeWhisperMenu() {
- $("#command-menu").empty();
- $(window).off("click.outsidewhispermenu");
- resetGhostText();
- }
-
- function resetGhostText() {
- $("#chat-message").off("keydown.ghosttab");
- $(".chatghosttextarea").val("").removeClass("show");
- }
- }
- }
|