diff --git a/devTools/FC.ts b/devTools/FC.ts index a3125ee3a5ec7815ea39d6a05b111eb68da4d2dc..36c899b9169ff5c84097c4634ea8c8fdb6b7c93c 100644 --- a/devTools/FC.ts +++ b/devTools/FC.ts @@ -255,6 +255,7 @@ namespace App { declare function makeElement<K extends keyof HTMLElementTagNameMap>(tag: K, content: string | Node, classNames?: string | string[]): HTMLElementTagNameMap[K]; declare function appendNewElement<K extends keyof HTMLElementTagNameMap>(tag: K, parent: ParentNode, content?: string | Node, classNames?: string | string[]): HTMLElementTagNameMap[K]; } + namespace Hotkeys { } namespace View { } namespace SlaveSummary { type AppendRenderer = (slave: FC.SlaveState, parentNode: Node) => void; diff --git a/src/001-lib/mousetrap/mousetrap.js b/src/001-lib/mousetrap/0_mousetrap.js similarity index 100% rename from src/001-lib/mousetrap/mousetrap.js rename to src/001-lib/mousetrap/0_mousetrap.js diff --git a/src/001-lib/mousetrap/1_mousetrap-record.min.js b/src/001-lib/mousetrap/1_mousetrap-record.min.js new file mode 100644 index 0000000000000000000000000000000000000000..76a7d7d8d532e8c43fc6471e6bc897c12f090691 --- /dev/null +++ b/src/001-lib/mousetrap/1_mousetrap-record.min.js @@ -0,0 +1 @@ +!function(n){var t=[],e=null,r=[],i=!1,o=null,l=n.prototype.handleKey;function u(n){var t;for(t=0;t<r.length;++t)if(r[t]===n)return;r.push(n),1===n.length&&(i=!0)}function p(){t.push(r),i=!(r=[]),clearTimeout(o),o=setTimeout(h,1e3)}function h(){e&&(function(n){var t;for(t=0;t<n.length;++t)n[t].sort(function(n,t){return!(1<n.length&&1===t.length)&&(1===n.length&&1<t.length||t<n)?1:-1}),n[t]=n[t].join("+")}(t),e(t)),t=[],e=null,r=[]}n.prototype.record=function(n){var t=this;t.recording=!0,e=function(){t.recording=!1,n.apply(t,arguments)}},n.prototype.handleKey=function(){(function(n,t,e){var o;if(this.recording)if("keydown"===e.type){for(e.preventDefault(),1===n.length&&i&&p(),o=0;o<t.length;++o)u(t[o]);u(n)}else"keyup"===e.type&&0<r.length&&p();else l.apply(this,arguments)}).apply(this,arguments)},n.init()}(Mousetrap); diff --git a/src/002-config/mousetrapConfig.js b/src/002-config/mousetrapConfig.js index df31dc5fb398961e29312bfaca764e0dab3ac096..c82ac4be193661aef007b894ecbb0a50630f938a 100644 --- a/src/002-config/mousetrapConfig.js +++ b/src/002-config/mousetrapConfig.js @@ -1,29 +1,305 @@ -/* eslint-disable */ -Mousetrap.bind("enter", function() { - $("#story-caption #endWeekButton a.macro-link").trigger("click"); +/** + * Expand mousetrap with multiple binds per key, passage dependent bindings and a menu for custom rebinding. + */ +App.UI.Hotkeys = (function() { + /** + * @typedef action + * @property {Function} callback + * @property {Array<string>} combinations 0 <= length <= 2 + * @property {Array<string>} [passages] not existing means everywhere + * @property {string|function(): string} [uiName] allow different name in hotkey settings + */ + + /** + * The key is used in the hotkey settings to describe the action + * @type {object.<string, action>} + */ + const actions = {}; + + /** + * Contains the default combinations for every action + * @type {object.<string, Array<string>>} + */ + const defaultCombinations = {}; + + /** + * References a key combination to a set of actions + * @type {object.<string, Array<string>>} + */ + const bindings = {}; + + /** + * To ensure we only record one at at time + * @type {boolean} + */ + let recording = false; + + /** + * @param {string} name used as key + * @param {action} action + */ + function addDefault(name, action) { + actions[name] = action; + for (const binding of action.combinations) { + addBinding(name, binding); + } + defaultCombinations[name] = [...action.combinations]; + } + + /** + * @param {string} actionKey + * @param {string} combination + */ + function addBinding(actionKey, combination) { + if (bindings[combination]) { + bindings[combination].push(actionKey); + } else { + bindings[combination] = [actionKey]; + Mousetrap.bind(combination, e => { + e.preventDefault(); + for (const binding of bindings[combination]) { + const action = actions[binding]; + // only activate callback if we are on the right passage + if (!action.passages || action.passages.includes(State.passage)) { + action.callback(); + } + } + }); + } + } + + /** + * @param {string} actionKey + * @param {string} combination + */ + function removeBinding(actionKey, combination) { + if (bindings[combination]) { + const index = bindings[combination].indexOf(actionKey); + if (index > -1) { + bindings[combination].splice(index, 1); + if (bindings[combination].length === 0) { + delete bindings[combination]; + Mousetrap.unbind(combination); + } + } + } + } + + /** + * @param {string} name + * @returns {string} + */ + function hotkeysForAction(name) { + if (!actions[name]) { + return ""; + } + const c = actions[name].combinations; + if (c.length === 0) { + return ""; + } + if (c.length === 1) { + return `[${formatHotkey(c[0])}]`; + } + return `[${formatHotkey(c[0])},${formatHotkey(c[1])}]`; + } + + /** + * @param {string} combination + * @returns {string} + */ + function formatHotkey(combination) { + const parts = combination.split("+"); + + for (let i = 0; i < parts.length; i++) { + parts[i] = capFirstChar(parts[i]); + } + + return parts.join("+"); + } + + /** + * @returns {HTMLDivElement} + */ + function settingsMenu() { + const div = document.createElement("div"); + div.className = "hotkey-settings"; + + for (const actionsKey in actions) { + settingsRow(div, actionsKey); + } + + return div; + } + + /** + * @param {HTMLDivElement} container + * @param {string} actionKey + */ + function settingsRow(container, actionKey) { + const action = actions[actionKey]; + // get correct name + let name = actionKey; + if (action.uiName) { + if (typeof action.uiName === "string") { + name = action.uiName; + } else { + name = action.uiName(); + } + } + App.UI.DOM.appendNewElement("div", container, name, "description"); + + settingsCell(container, actionKey, 0); + settingsCell(container, actionKey, 1); + + const button = App.UI.DOM.appendNewElement("button", container, "Reset"); + if (isDefault(actionKey)) { + button.className = "inactive"; + } else { + button.onclick = () => { + action.combinations = [...defaultCombinations[actionKey]]; + saveToStorage(); + App.UI.reload(); + }; + } + } + + /** + * Checks if the combinations assigned to an action are the default ones. + * @param {string} actionKey + * @returns {boolean} + */ + function isDefault(actionKey) { + if (defaultCombinations[actionKey].length !== actions[actionKey].combinations.length) { + return false; + } + if (defaultCombinations[actionKey].length === 0) { + return true; + } + if (defaultCombinations[actionKey][0] !== actions[actionKey].combinations[0]) { + return false; + } + if (defaultCombinations[actionKey].length === 1) { + return true; + } + return defaultCombinations[actionKey][1] === actions[actionKey].combinations[1]; + } + + /** + * @param {HTMLDivElement} container + * @param {string} actionKey + * @param {number} index + */ + function settingsCell(container, actionKey, index) { + const action = actions[actionKey]; + const button = App.UI.DOM.appendNewElement("button", container, + action.combinations[index] ? formatHotkey(action.combinations[index]) : "", "combination"); + button.onclick = () => { + if (recording) { return; } + recording = true; + + $(button).empty(); + Mousetrap.record(function(sequence) { + // sequence is an array like ['ctrl+k', 'c'] + const combination = sequence.join(" "); + if (action.combinations[index]) { + removeBinding(actionKey, action.combinations[index]); + } + action.combinations[index] = combination; + addBinding(actionKey, combination); + saveToStorage(); + App.UI.reload(); + recording = false; + }); + }; + } + + /** + * Saves custom hotkeys to browser storage + */ + function saveToStorage() { + const save = {}; + + for (const actionsKey in actions) { + if (!isDefault(actionsKey)) { + save[actionsKey] = actions[actionsKey].combinations; + } + } + + SugarCube.storage.set("hotkeys", save); + } + + /** + * Loads custom hotkeys from browser storage + */ + function loadFromStorage() { + const save = SugarCube.storage.get("hotkeys"); + + for (const saveKey in save) { + // discard obsolete hotkeys + if (actions[saveKey]) { + actions[saveKey].combinations = save[saveKey]; + } + } + } + + /** + * Initialize custom hotkeys + */ + function init() { + loadFromStorage(); + // :storyready is to late to influence the page, but it's the earliest where SugarCube.storage is available so + // we refresh the passage if we happen to be on the settings passage. + if (State.passage === "Hotkey Settings") { + App.UI.reload(); + } + } + + return { + add: addDefault, + hotkeys: hotkeysForAction, + init: init, + settings: settingsMenu, + }; +})(); + +// add hotkeys +App.UI.Hotkeys.add("endWeek", { + callback: function() { + $("#story-caption #endWeekButton a.macro-link").trigger("click"); + }, combinations: ["enter"], uiName: "Next Week" }); -Mousetrap.bind("space", function() { - $("#story-caption #nextButton a.macro-link").trigger("click"); +App.UI.Hotkeys.add("nextLink", { + callback: function() { + $("#story-caption #nextButton a.macro-link").trigger("click"); + }, combinations: ["space"], uiName: "Continue/Back" }); -Mousetrap.bind("left", function() { - $("#prevSlave a.macro-link").trigger("click"); - $("#prevChild a.macro-link").trigger("click"); +App.UI.Hotkeys.add("prevSlave", { + callback: function() { + $("#prevSlave a.macro-link").trigger("click"); + }, combinations: ["left", "q"], uiName: "Previous Slave" }); -Mousetrap.bind("q", function() { - $("#prevSlave a.macro-link").trigger("click"); - $("#prevChild a.macro-link").trigger("click"); +App.UI.Hotkeys.add("nextSlave", { + callback: function() { + $("#nextSlave a.macro-link").trigger("click"); + }, combinations: ["right", "e"], uiName: "Next Slave" }); -Mousetrap.bind("right", function() { - $("#nextSlave a.macro-link").trigger("click"); - $("#nextChild a.macro-link").trigger("click"); +App.UI.Hotkeys.add("prevChild", { + callback: function() { + $("#prevChild a.macro-link").trigger("click"); + }, combinations: ["left", "q"], uiName: "Previous Child" }); -Mousetrap.bind("e", function() { - $("#nextSlave a.macro-link").trigger("click"); - $("#nextChild a.macro-link").trigger("click"); +App.UI.Hotkeys.add("nextChild", { + callback: function() { + $("#nextChild a.macro-link").trigger("click"); + }, combinations: ["right", "e"], uiName: "Next Child" }); -Mousetrap.bind("f", function() { - $("#walkpast a.macro-link").trigger("click"); +App.UI.Hotkeys.add("walkpast", { + callback: function() { + $("#walkpast a.macro-link").trigger("click"); + }, combinations: ["f"] }); -Mousetrap.bind("h", function() { - $("#manageHG a").trigger("click"); +App.UI.Hotkeys.add("HG Select", { + callback: function() { + $("#manageHG a").trigger("click"); + }, combinations: ["h"] }); diff --git a/src/Mods/SpecialForce/SpecialForce.js b/src/Mods/SpecialForce/SpecialForce.js index 205e8ee1df8a2656e75f1a5a034982bd40889e2f..66d6dd0d2a7242233299df78e9ad1926f17c9963 100644 --- a/src/Mods/SpecialForce/SpecialForce.js +++ b/src/Mods/SpecialForce/SpecialForce.js @@ -1,7 +1,10 @@ // T=SugarCube.State.temporary; App.SF.Caps = function() { - return capFirstChar(V.SF.Lower); -} + if (V.SF.Lower) { + return capFirstChar(V.SF.Lower); + } + return "no one"; +}; App.SF.Init = function() { V.SF.Toggle = V.SF.Toggle || 0; diff --git a/src/arcologyBuilding/penthouse.js b/src/arcologyBuilding/penthouse.js index f4d9e75ca14cfddc5bb908064c603dc88f2c4463..89c3126993199518f6beb71b0535d4894a520fb7 100644 --- a/src/arcologyBuilding/penthouse.js +++ b/src/arcologyBuilding/penthouse.js @@ -25,7 +25,7 @@ App.Arcology.Cell.Penthouse = class extends App.Arcology.Cell.BaseCell { const fragment = document.createDocumentFragment(); const link = App.UI.DOM.passageLink("Penthouse", "Manage Penthouse"); - const hotkey = App.UI.DOM.makeElement("span", "[P]", "hotkey"); + const hotkey = App.UI.DOM.makeElement("span", App.UI.Hotkeys.hotkeys("Manage Penthouse"), "hotkey"); if (V.verticalizeArcologyLinks === 0) { const div = document.createElement("div"); div.append(link, " ", hotkey); diff --git a/src/facilities/nursery/childInteract.tw b/src/facilities/nursery/childInteract.tw index db203972cd30f9a6dc2811261df9ee9d49be7cde..eac1877bcd5b558ddd48419e6a4672e5ec0af104 100644 --- a/src/facilities/nursery/childInteract.tw +++ b/src/facilities/nursery/childInteract.tw @@ -18,7 +18,9 @@ <</if>> <center> -@@.cyan;[â†,Q] @@ +<span class="hotkey"> + <<print App.UI.Hotkeys.hotkeys("prevChild")>> +</span> <span id="prevChild"> <b> <<link "Prev" "Previous Child In Line">><</link>> @@ -30,7 +32,9 @@ <<link "Next" "Next Child In Line">><</link>> </b> </span> -@@.cyan; [E,→]@@ +<span class="hotkey"> + <<print App.UI.Hotkeys.hotkeys("nextChild")>> +</span> </center> <br> diff --git a/src/uncategorized/descriptionOptions.tw b/src/gui/options/descriptionOptions.tw similarity index 100% rename from src/uncategorized/descriptionOptions.tw rename to src/gui/options/descriptionOptions.tw diff --git a/src/gui/options/hotkeySettings.css b/src/gui/options/hotkeySettings.css new file mode 100644 index 0000000000000000000000000000000000000000..08df500e4bc6e6c637c2739c4ff61c1f29494414 --- /dev/null +++ b/src/gui/options/hotkeySettings.css @@ -0,0 +1,42 @@ +div.hotkey-settings { + display: grid; + grid-template-columns: repeat(3, max-content) auto; +} + +@media only screen and (min-width: 1600px) { + div.hotkey-settings { + grid-template-columns: repeat(3, max-content) auto repeat(3, max-content) auto; + } +} + +div.hotkey-settings div.description { + margin-right: 10px; + /* center text vertically */ + display: flex; + justify-content: center; + flex-direction: column; +} + +div.hotkey-settings button { + margin: 5px; + border-width: 2px; + background-color: var(--button-color); + border-color: var(--button-border-color); + width: 70px; +} + +div.hotkey-settings button.combination { + min-width: 150px; + border-width: 0; +} + +div.hotkey-settings button.inactive, div.hotkey-settings button.inactive:hover { + background-color: var(--button-selected-color); + cursor: default; + +} + +div.hotkey-settings button:hover { + background-color: var(--button-hover-color); + border-color: var(--button-border-color); +} diff --git a/src/gui/options/hotkeySettings.tw b/src/gui/options/hotkeySettings.tw new file mode 100644 index 0000000000000000000000000000000000000000..b1bf6388eacc1f4df01bd3ee5d965dceab84e39e --- /dev/null +++ b/src/gui/options/hotkeySettings.tw @@ -0,0 +1,27 @@ +:: Hotkey Settings [nobr jump-to-safe jump-from-safe] + +<<set $nextButton = "Back", $nextLink = "Main">> + +<h1>Hotkey Settings</h1> + +<p> + <ul> + <li> + On keyboard layouts other than the <a href="https://en.wikipedia.org/wiki/File:KB_United_States.svg" + target="_blank">US-QWERTY layout</a> there may be keys or combinations of keys where the recorded key is + different from the key used to listen to key events. You will have to find these keys yourself through trial + and error. + </li> + <li> + Custom hotkeys are browser specific and are not part of your save. + </li> + <li> + While we try not to overwrite browser or OS level key combinations it is possible to do so with custom + hotkeys. This also means that during recording of custom hotkeys no browser or OS level key combinations are + available. There are however keys that cannot be overwritten, the <code>Win key</code> on Windows is an + example for this. + </li> + </ul> +</p> + +<<includeDOM App.UI.Hotkeys.settings()>> diff --git a/src/gui/options.js b/src/gui/options/options.js similarity index 100% rename from src/gui/options.js rename to src/gui/options/options.js diff --git a/src/uncategorized/options.tw b/src/gui/options/options.tw similarity index 100% rename from src/uncategorized/options.tw rename to src/gui/options/options.tw diff --git a/src/uncategorized/summaryOptions.tw b/src/gui/options/summaryOptions.tw similarity index 100% rename from src/uncategorized/summaryOptions.tw rename to src/gui/options/summaryOptions.tw diff --git a/src/gui/quicklinks.js b/src/gui/quicklinks.js index 5fda6b661f6c7dc1a85bb4b9f96c3142d5a7b9de..f8e0347679c5c0c2ad1d6d7f37ff9be19edc7847 100644 --- a/src/gui/quicklinks.js +++ b/src/gui/quicklinks.js @@ -17,7 +17,7 @@ App.UI.quickMenu = (function() { const noHistory = Story.lookup("tags", "no-history").map(passage => passage.title); // if property name is a passage name, then it's a link, otherwise only text. - // category titles are never links to passages + // category titles are never links to passages // Only two values are allowed: true or an object following the same rules const layout = addOtherCategory({ Main: true, @@ -77,6 +77,7 @@ App.UI.quickMenu = (function() { "Summary Options": true, "Description Options": true, "Universal Rules": true, + "Hotkey Settings": true, } }); @@ -177,49 +178,6 @@ App.UI.quickMenu = (function() { "Manage Corporation": () => V.corp.SpecToken > 0 && V.corp.SpecTimer === 0, }); - // setup hotkeys list, upper/lower case is important! - // Due to limitation to the key capture library keys cannot be used when they are already used in - // src/002-config/mousetrapConfig.js - const hotkeys = cleanPassageMapping({ - "BG Select": "b", - "Buy Slaves": "s", - edicts: "E", - Firebase: "z", - "Future Society": "f", - Main: "m", - "Manage Arcology": "c", - "Manage Corporation":"C", - "Manage Penthouse": "p", - "Manage Personal Affairs": "x", - "Neighbor Interact": "d", - Options: "o", - "Personal assistant options": "t", - "Personal Attention Select": "a", - Policies: "y", - propagandaHub: "H", - "Recruiter Select": "u", - riotControlCenter: "R", - "Rules Assistant": "r", - secBarracks: "A", - securityHQ: "S", - "Universal Rules": "v", - // Facilities - Brothel: "1", - Club: "2", - Arcade: "3", - Dairy: "4", - Farmyard: "5", - "Servants' Quarters": "6", - "Master Suite": "7", - Schoolroom: "8", - Spa: "9", - Nursery: "0", - Clinic: "shift+1", - Cellblock: "shift+2", - Incubator: "shift+3", - Pit: "shift+4", - }); - /** * The DOM element of name of the currently played passage or any of it's parents. Used during generation to * uncollapse the category with the current passage. @@ -235,14 +193,75 @@ App.UI.quickMenu = (function() { let hotkeysEnabled = false; // register hotkeys - for (const passage in hotkeys) { - Mousetrap.bind(hotkeys[passage], () => { - if (hotkeysEnabled - // the passage is accessible - && !(hiddenPassages[passage] && hiddenPassages[passage]())) { - Engine.play(passage); - } + // this is in it's own scope as we can forget the hotkeys object immediately afterwards + { + // setup hotkeys list, upper/lower case is important! + const hotkeys = cleanPassageMapping({ + "BG Select": "b", + "Buy Slaves": "s", + edicts: "shift+e", + Firebase: "z", + "Future Society": "f", + Main: "m", + "Manage Arcology": "c", + "Manage Corporation": "shift+c", + "Manage Penthouse": "p", + "Manage Personal Affairs": "x", + "Neighbor Interact": "d", + Options: "o", + "Personal assistant options": "t", + "Personal Attention Select": "a", + Policies: "y", + propagandaHub: "shift+h", + "Recruiter Select": "u", + riotControlCenter: "shift+r", + "Rules Assistant": "r", + secBarracks: "shift+a", + securityHQ: "shift+s", + "Universal Rules": "v", + // Facilities + Brothel: "1", + Club: "2", + Arcade: "3", + Dairy: "4", + Farmyard: "5", + "Servants' Quarters": "6", + "Master Suite": "7", + Schoolroom: "8", + Spa: "9", + Nursery: "0", + Clinic: "shift+1", + Cellblock: "shift+2", + Incubator: "shift+3", + Pit: "shift+4", }); + + // register + for (const passage of jumpTo) { + if (!hidden.includes(passage)) { + const action = { + callback: () => { + if (hotkeysEnabled + // we are not already on the passage + && State.passage !== passage + // the passage is accessible + && !(hiddenPassages[passage] && hiddenPassages[passage]())) { + Engine.play(passage); + } + }, + combinations: [], + }; + // add hotkey if there is one + if (hotkeys[passage]) { + action.combinations.push(hotkeys[passage]); + } + // custom ui text + if (uiNames[passage]) { + action.uiName = uiNames[passage]; + } + App.UI.Hotkeys.add(passage, action); + } + } } // setup history @@ -268,10 +287,7 @@ App.UI.quickMenu = (function() { history.shift(); } }); - Mousetrap.bind("backspace", () => { - // jump back in history - goBack(); - }); + App.UI.Hotkeys.add("historyBack", {callback: goBack, combinations: ["backspace"], uiName: "Back in history"}); /** * Goes back in history if possible. @@ -294,7 +310,10 @@ App.UI.quickMenu = (function() { const a = document.createElement("a"); a.append("Return"); a.onclick = goBack; - div.append(a, " ", App.UI.DOM.makeElement("span", "[backspace]", "hotkey")); + const hotkey = App.UI.Hotkeys.hotkeys("historyBack"); + if (hotkey !== "") { + div.append(a, " ", App.UI.DOM.makeElement("span", hotkey, "hotkey")); + } // insert at second position linkList.splice(1, 0, div); } @@ -447,8 +466,9 @@ App.UI.quickMenu = (function() { Engine.play(passage); }; div.prepend(a); - if (hotkeys[passage]) { - div.append(" ", App.UI.DOM.makeElement("span", `[${hotkeys[passage]}]`, "hotkey")); + const hotkeyString = App.UI.Hotkeys.hotkeys(passage); + if (hotkeyString !== "") { + div.append(" ", App.UI.DOM.makeElement("span", hotkeyString, "hotkey")); } } diff --git a/src/gui/storyCaptionWidgets.tw b/src/gui/storyCaptionWidgets.tw index a8616c836cd97a0ccaa8c7a5a96f418853a33889..3242e2b91f508d773fa094bc13b6f1a8f560b3f0 100644 --- a/src/gui/storyCaptionWidgets.tw +++ b/src/gui/storyCaptionWidgets.tw @@ -7,7 +7,14 @@ <<if _Pass != "End Week">> <<if _Pass == "Main">> <strong> - <div id="endWeekButton"><<link "END WEEK">><<run endWeek()>><</link>> @@.cyan;[Ent]@@</div> + <div id="endWeekButton"> + <<link "END WEEK">> + <<run endWeek()>> + <</link>> + <span class="hotkey"> + <<print App.UI.Hotkeys.hotkeys("endWeek")>> + </span> + </div> </strong> <<if $rulesAssistantAuto == 1 && DefaultRulesError()>> <div>@@.yellow;WARNING: Rules Assistant has rules with errors!@@</div> @@ -17,7 +24,10 @@ <<if $nextButton != " ">> <<link "$nextButton">> <<goto $nextLink>> - <</link>> @@.cyan;[Space]@@ + <</link>> + <span class="hotkey"> + <<print App.UI.Hotkeys.hotkeys("nextLink")>> + </span> <</if>> </div></strong> <</if>> diff --git a/src/interaction/main/mainLinks.js b/src/interaction/main/mainLinks.js index a41eff26a9c25f0eebc58391665fbfcca9973862..d2be8893bee20de2008b2bc227d5a8d62731d11f 100644 --- a/src/interaction/main/mainLinks.js +++ b/src/interaction/main/mainLinks.js @@ -85,7 +85,7 @@ App.UI.View.mainLinks = function() { if (V.PC.health.shortDamage < 30) { const link = App.UI.DOM.makeElement("span", App.UI.DOM.passageLink("Change plans", "Personal Attention Select"), "major-link"); link.id = "managePA"; - fragment.append(" ", link, " ", App.UI.DOM.makeElement("span", "[A]", "hotkey")); + fragment.append(" ", link, " ", App.UI.DOM.makeElement("span", App.UI.Hotkeys.hotkeys("Personal Attention"), "hotkey")); } if (V.useSlaveSummaryOverviewTab === 0) { @@ -97,7 +97,7 @@ App.UI.View.mainLinks = function() { } div.append(". ", App.UI.DOM.makeElement("span", App.UI.DOM.passageLink("Manage Head Girl", "HG Select"), "major-link"), - " ", App.UI.DOM.makeElement("span", "[H]", "hotkey")); + " ", App.UI.DOM.makeElement("span", App.UI.Hotkeys.hotkeys("HG Select"), "hotkey")); div.id = "manageHG"; } else if (V.slaves.length > 1) { div.append(`You have not selected a Head Girl`); @@ -106,7 +106,7 @@ App.UI.View.mainLinks = function() { } div.append(". ", App.UI.DOM.makeElement("span", App.UI.DOM.passageLink("Select One", "HG Select"), "major-link"), - " ", App.UI.DOM.makeElement("span", "[H]", "hotkey")); + " ", App.UI.DOM.makeElement("span", App.UI.Hotkeys.hotkeys("HG Select"), "hotkey")); div.id = "manageHG"; } else { div.append(App.UI.DOM.makeElement("span", "You do not have enough slaves to keep a Head Girl", "note")); @@ -121,7 +121,7 @@ App.UI.View.mainLinks = function() { div.append("You have not selected a Recruiter. ", App.UI.DOM.makeElement("span", App.UI.DOM.passageLink("Select one", "Recruiter Select"), "major-link")); } - div.append(" ", App.UI.DOM.makeElement("span", "[U]", "hotkey")); + div.append(" ", App.UI.DOM.makeElement("span", App.UI.Hotkeys.hotkeys("Recruiter Select"), "hotkey")); div.id = "manageRecruiter"; fragment.append(div); @@ -134,7 +134,7 @@ App.UI.View.mainLinks = function() { div.append("You have not selected a Bodyguard. ", App.UI.DOM.makeElement("span", App.UI.DOM.passageLink("Select one", "BG Select"), "major-link")); } - div.append(" ", App.UI.DOM.makeElement("span", "[B]", "hotkey")); + div.append(" ", App.UI.DOM.makeElement("span", App.UI.Hotkeys.hotkeys("BG Select"), "hotkey")); div.id = "manageBG"; fragment.append(div); } diff --git a/src/js/main.js b/src/js/main.js index 1854c4e6343b57ee130cb45a5faad2fcb66b0a0e..2dea7157fec6271d7db5200973c606b85d512b5d 100644 --- a/src/js/main.js +++ b/src/js/main.js @@ -225,7 +225,7 @@ App.MainView.full = function() { const raLink = document.createElement("span"); raLink.id = "RAButton"; raLink.append(" | ", App.UI.DOM.passageLink("Rules Assistant Options", "Rules Assistant"), - " ", App.UI.DOM.makeElement("span", "[R]", ["clear-formatting", "hotkey"])); + " ", App.UI.DOM.makeElement("span", App.UI.Hotkeys.hotkeys("Rules Assistant"), ["clear-formatting", "hotkey"])); span.append(raLink); if (V.rulesAssistantAuto !== 1) { diff --git a/src/js/slaveListing.js b/src/js/slaveListing.js index 09d47f01788c95c72f6eff1d037af2e1ac7842f6..ee2d2088cd5d0ce7e2ca51b30cb08ea2a8239823 100644 --- a/src/js/slaveListing.js +++ b/src/js/slaveListing.js @@ -864,7 +864,7 @@ App.UI.SlaveList.penthousePage = function() { slaveWrapper.append(". "); const link = App.UI.DOM.makeElement("span", App.UI.DOM.passageLink("Manage Head Girl", "HG Select"), "major-link"); link.id = "manageHG"; - slaveWrapper.append(link, " ", App.UI.DOM.makeElement("span", "[H]", "hotkey")); + slaveWrapper.append(link, " ", App.UI.DOM.makeElement("span", App.UI.Hotkeys.hotkeys("HG Select"), "hotkey")); slaveWrapper.append(App.UI.SlaveList.render.listDOM([HG.ID], [], App.UI.SlaveList.SlaveInteract.penthouseInteract)); } else { @@ -874,7 +874,7 @@ App.UI.SlaveList.penthousePage = function() { slaveWrapper.append(" and Consort"); } slaveWrapper.append(". ", App.UI.DOM.makeElement("span", App.UI.DOM.passageLink("Select One", "HG Select"), "major-link"), - " ", App.UI.DOM.makeElement("span", "[H]", "hotkey")); + " ", App.UI.DOM.makeElement("span", App.UI.Hotkeys.hotkeys("HG Select"), "hotkey")); slaveWrapper.id = "manageHG"; if (V.slavePanelStyle === 2) { slaveWrapper.classList.add("slaveSummary", "card"); @@ -907,13 +907,13 @@ App.UI.SlaveList.penthousePage = function() { slaveWrapper.append(". "); const link = App.UI.DOM.makeElement("span", App.UI.DOM.passageLink("Manage Recruiter", "Recruiter Select"), "major-link"); link.id = "manageRecruiter"; - slaveWrapper.append(link, " ", App.UI.DOM.makeElement("span", "[U]", "hotkey")); + slaveWrapper.append(link, " ", App.UI.DOM.makeElement("span", App.UI.Hotkeys.hotkeys("Recruiter Select"), "hotkey")); slaveWrapper.append(App.UI.SlaveList.render.listDOM([RC.ID], [], App.UI.SlaveList.SlaveInteract.penthouseInteract)); } else { slaveWrapper.append("You have ", App.UI.DOM.makeElement("span", "not", "warning"), " selected a Recruiter. ", App.UI.DOM.makeElement("span", App.UI.DOM.passageLink("Select one", "Recruiter Select"), "major-link"), - " ", App.UI.DOM.makeElement("span", "[U]", "hotkey")); + " ", App.UI.DOM.makeElement("span", App.UI.Hotkeys.hotkeys("Recruiter Select"), "hotkey")); slaveWrapper.id = "manageRecruiter"; if (V.slavePanelStyle === 2) { slaveWrapper.classList.add("slaveSummary", "card"); @@ -930,14 +930,14 @@ App.UI.SlaveList.penthousePage = function() { " is serving as your bodyguard. "); const link = App.UI.DOM.makeElement("span", App.UI.DOM.passageLink("Manage Bodyguard", "BG Select"), "major-link"); link.id = "manageBG"; - slaveWrapper.append(link, " ", App.UI.DOM.makeElement("span", "[B]", "hotkey")); + slaveWrapper.append(link, " ", App.UI.DOM.makeElement("span", App.UI.Hotkeys.hotkeys("BG Select"), "hotkey")); slaveWrapper.append(App.UI.SlaveList.render.listDOM([BG.ID], [], App.UI.SlaveList.SlaveInteract.penthouseInteract)); slaveWrapper.append(App.MainView.useGuard()); } else { slaveWrapper.append("You have ", App.UI.DOM.makeElement("span", "not", "warning"), " selected a Bodyguard. ", App.UI.DOM.makeElement("span", App.UI.DOM.passageLink("Select one", "BG Select"), "major-link"), - " ", App.UI.DOM.makeElement("span", "[B]", "hotkey")); + " ", App.UI.DOM.makeElement("span", App.UI.Hotkeys.hotkeys("BG Select"), "hotkey")); slaveWrapper.id = "manageBG"; if (V.slavePanelStyle === 2) { slaveWrapper.classList.add("slaveSummary", "card"); diff --git a/src/uncategorized/slaveInteract.tw b/src/uncategorized/slaveInteract.tw index 3052912793446c8b4de549988409c765eda39cec..ea3c2115bf8e613d1f03a6fc621b52ab78d9124f 100644 --- a/src/uncategorized/slaveInteract.tw +++ b/src/uncategorized/slaveInteract.tw @@ -62,8 +62,8 @@ [[Cheat Edit Slave Alternative|MOD_Edit Slave Cheat New][$cheater = 1]] </div> <</if>> - <span class="cyan"> - [â†,Q] + <span class="hotkey"> + <<print App.UI.Hotkeys.hotkeys("prevSlave")>> </span> <span id="prevSlave" style="font-weight:bold"> <<link "Prev" "Slave Interact">><<set $activeSlave = getSlave(_slavesInLine[0])>><</link>> @@ -76,8 +76,8 @@ <span id="nextSlave" style="font-weight:bold"> <<link "Next" "Slave Interact">><<set $activeSlave = getSlave(_slavesInLine[1])>><</link>> </span> - <span class="cyan"> - [E,→] + <span class="hotkey"> + <<print App.UI.Hotkeys.hotkeys("nextSlave")>> </span> </p> <div class="tabbar"> diff --git a/src/zz1-last/setupEventHandlers.js b/src/zz1-last/setupEventHandlers.js index 0a6ef4160227ad480de47f3b895c24cc870cd2ba..593787eae840212bd4fdbfc1beb51a72738348e5 100644 --- a/src/zz1-last/setupEventHandlers.js +++ b/src/zz1-last/setupEventHandlers.js @@ -4,6 +4,7 @@ Config.saves.onSave = App.EventHandlers.onSave; $(document).on(':storyready', function() { App.EventHandlers.storyReady(); + App.UI.Hotkeys.init(); }); $(document).one(':passagestart', function() {