Skip to content

Commit 92da817

Browse files
committed
Add plugin support for Cmd on Macs rather than Ctrl (optional)
Tested with i18n-hljs.html, but undocumented
1 parent 5fcaac5 commit 92da817

File tree

6 files changed

+84
-29
lines changed

6 files changed

+84
-29
lines changed

code-input.d.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ export namespace plugins {
138138
* @param {boolean} useCtrlF Should Ctrl+F be overriden for find-and-replace find functionality? Either way, you can also trigger it yourself using (instance of this plugin)`.showPrompt(code-input element, false)`.
139139
* @param {boolean} useCtrlH Should Ctrl+H be overriden for find-and-replace replace functionality? Either way, you can also trigger it yourself using (instance of this plugin)`.showPrompt(code-input element, true)`.
140140
* @param {Object} instructionTranslations: user interface string keys mapped to translated versions for localisation. Look at the find-and-replace.js source code for the English text.
141+
* @param {boolean} alwaysCtrl: if true always use Ctrl+F/H as the keyboard shortcut; if false use Cmd+F/H on Apple devices and Ctrl+F/H elsewhere. False highly recommended; defaults to true for backwards compatiblity.
141142
*/
142143
constructor(useCtrlF?: boolean, useCtrlH?: boolean,
143144
instructionTranslations?: {
@@ -159,7 +160,8 @@ export namespace plugins {
159160
replaceAction?: string;
160161
replaceAllActionShort?: string;
161162
replaceAllAction?: string
162-
}
163+
},
164+
alwaysCtrl?: boolean
163165
);
164166
/**
165167
* Show a find-and-replace dialog.
@@ -190,7 +192,9 @@ export namespace plugins {
190192
guidanceColumnRange?: (line: Number, current: Number, max: Number) => string;
191193
guidanceValidLine?: (line: Number) => string;
192194
guidanceValidColumn?: (line: Number, column: Number) => string;
193-
});
195+
},
196+
alwaysCtrl?: boolean
197+
);
194198
/**
195199
* Show a search-like dialog prompting line number.
196200
* @param {codeInput.CodeInput} codeInput the `<code-input>` element.
@@ -213,6 +217,7 @@ export namespace plugins {
213217
* @param {Object} bracketPairs Opening brackets mapped to closing brackets, default and example {"(": ")", "[": "]", "{": "}"}. All brackets must only be one character, and this can be left as null to remove bracket-based indentation behaviour.
214218
* @param {boolean} escTabToChangeFocus Whether pressing the Escape key before (Shift+)Tab should make this keypress focus on a different element (Tab's default behaviour). You should always either enable this or use this plugin's disableTabIndentation and enableTabIndentation methods linked to other keyboard shortcuts, for accessibility.
215219
* @param {Object} instructionTranslations: user interface string keys mapped to translated versions for localisation. Look at the go-to-line.js source code for the English text.
220+
* @param {boolean} alwaysCtrl: if true always use Ctrl+G as the keyboard shortcut; if false use Cmd+G on Apple devices and Ctrl+G elsewhere. False highly recommended; defaults to true for backwards compatiblity.
216221
*/
217222
constructor(defaultSpaces?: boolean, numSpaces?: Number, bracketPairs?: Object, escTabToChangeFocus?: boolean, instructionTranslations?: {
218223
tabForIndentation?: string;

plugins/find-and-replace.js

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,13 @@ codeInput.plugins.FindAndReplace = class extends codeInput.Plugin {
3636
* @param {boolean} useCtrlF Should Ctrl+F be overriden for find-and-replace find functionality? Either way, you can also trigger it yourself using (instance of this plugin)`.showPrompt(code-input element, false)`.
3737
* @param {boolean} useCtrlH Should Ctrl+H be overriden for find-and-replace replace functionality? Either way, you can also trigger it yourself using (instance of this plugin)`.showPrompt(code-input element, true)`.
3838
* @param {Object} instructionTranslations: user interface string keys mapped to translated versions for localisation. Look at the find-and-replace.js source code for the English text and available keys.
39+
* @param {boolean} alwaysCtrl: if true always use Ctrl+F/H as the keyboard shortcut; if false use Cmd+F/H on Apple devices and Ctrl+F/H elsewhere. False highly recommended; defaults to true for backwards compatiblity.
3940
*/
40-
constructor(useCtrlF = true, useCtrlH = true, instructionTranslations = {}) {
41+
constructor(useCtrlF = true, useCtrlH = true, instructionTranslations = {}, alwaysCtrl = true) {
4142
super([]); // No observed attributes
4243
this.useCtrlF = useCtrlF;
4344
this.useCtrlH = useCtrlH;
45+
this.alwaysCtrl = alwaysCtrl;
4446
this.addTranslations(this.instructions, instructionTranslations);
4547
}
4648

@@ -420,17 +422,29 @@ codeInput.plugins.FindAndReplace = class extends codeInput.Plugin {
420422
this.updateFindMatches(dialog);
421423
}
422424

425+
hasModifier(event) {
426+
if(this.alwaysCtrl) return event.ctrlKey;
427+
// Thanks to https://developer.mozilla.org/en-US/docs/Web/API/Navigator/platform
428+
if(navigator.platform.startsWith("Mac") || navigator.platform === "iPhone") {
429+
// Command
430+
return event.metaKey;
431+
} else {
432+
// Control
433+
return event.ctrlKey;
434+
}
435+
}
436+
423437
/* Event handler for keydown event that makes Ctrl+F open find dialog */
424438
checkCtrlF(codeInput, event) {
425-
if (event.ctrlKey && event.key == 'f') {
439+
if (this.hasModifier(event) && event.key == 'f') {
426440
event.preventDefault();
427441
this.showPrompt(codeInput, false);
428442
}
429443
}
430444

431445
/* Event handler for keydown event that makes Ctrl+H open find+replace dialog */
432446
checkCtrlH(codeInput, event) {
433-
if (event.ctrlKey && event.key == 'h') {
447+
if (this.hasModifier(event) && event.key == 'h') {
434448
event.preventDefault();
435449
this.showPrompt(codeInput, true);
436450
}

plugins/go-to-line.js

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,11 @@ codeInput.plugins.GoToLine = class extends codeInput.Plugin {
1919

2020
/**
2121
* Create a go-to-line command plugin to pass into a template
22-
* @param {boolean} useCtrlG Should Ctrl+G be overriden for go-to-line functionality? Either way, you can trigger it yourself using (instance of this plugin)`.showPrompt(code-input element)`.
22+
* @param {boolean} useCtrlG Should Ctrl/Cmd+G be overriden for go-to-line functionality? Either way, you can trigger it yourself using (instance of this plugin)`.showPrompt(code-input element)`.
2323
* @param {Object} instructionTranslations: user interface string keys mapped to translated versions for localisation. Look at the go-to-line.js source code for the available keys and English text.
24+
* @param {boolean} alwaysCtrl: if true always use Ctrl+G as the keyboard shortcut; if false use Cmd+G on Apple devices and Ctrl+G elsewhere. False highly recommended; defaults to true for backwards compatiblity.
2425
*/
25-
constructor(useCtrlG = true, instructionTranslations = {}) {
26+
constructor(useCtrlG = true, instructionTranslations = {}, alwaysCtrl = true) {
2627
super([]); // No observed attributes
2728
this.useCtrlG = useCtrlG;
2829
this.addTranslations(this.instructions, instructionTranslations);
@@ -202,9 +203,21 @@ codeInput.plugins.GoToLine = class extends codeInput.Plugin {
202203
}
203204
}
204205

206+
hasModifier(event) {
207+
if(this.alwaysCtrl) return event.ctrlKey;
208+
// Thanks to https://developer.mozilla.org/en-US/docs/Web/API/Navigator/platform
209+
if(navigator.platform.startsWith("Mac") || navigator.platform === "iPhone") {
210+
// Command
211+
return event.metaKey;
212+
} else {
213+
// Control
214+
return event.ctrlKey;
215+
}
216+
}
217+
205218
/* Event handler for keydown event that makes Ctrl+G open go to line dialog */
206219
checkCtrlG(codeInput, event) {
207-
if (event.ctrlKey && event.key == 'g') {
220+
if (this.hasModifier(event) && event.key == 'g') {
208221
event.preventDefault();
209222
this.showPrompt(codeInput);
210223
}

tests/i18n-hljs.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@
6262
}
6363
}),
6464
new codeInput.plugins.Autodetect(),
65-
new codeInput.plugins.FindAndReplace(true, true, findAndReplaceTranslations),
66-
new codeInput.plugins.GoToLine(true, goToLineTranslations),
65+
new codeInput.plugins.FindAndReplace(true, true, findAndReplaceTranslations, false),
66+
new codeInput.plugins.GoToLine(true, goToLineTranslations, false),
6767
new codeInput.plugins.Indent(true, 2, {"(": ")", "[": "]", "{": "}"}, true, indentTranslations),
6868
new codeInput.plugins.SelectTokenCallbacks(codeInput.plugins.SelectTokenCallbacks.TokenSelectorCallbacks.createClassSynchronisation("in-selection"), false, true, true, true, true, false),
6969
//new codeInput.plugins.SpecialChars(true),

tests/i18n-prism.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,8 @@
6161
popupElem.style.display = "none";
6262
}
6363
}),
64-
new codeInput.plugins.FindAndReplace(true, true, findAndReplaceTranslations),
65-
new codeInput.plugins.GoToLine(true, goToLineTranslations),
64+
new codeInput.plugins.FindAndReplace(true, true, findAndReplaceTranslations, false),
65+
new codeInput.plugins.GoToLine(true, goToLineTranslations, false),
6666
new codeInput.plugins.Indent(true, 2, {"(": ")", "[": "]", "{": "}"}, true, indentTranslations),
6767
new codeInput.plugins.SelectTokenCallbacks(new codeInput.plugins.SelectTokenCallbacks.TokenSelectorCallbacks(selectBrace, deselectAllBraces), true),
6868
//new codeInput.plugins.SpecialChars(true),

tests/tester.js

Lines changed: 40 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,8 @@ function beginTest(isHLJS) {
120120
}
121121
}),
122122
new codeInput.plugins.Autodetect(),
123-
new codeInput.plugins.FindAndReplace(),
124-
new codeInput.plugins.GoToLine(),
123+
new codeInput.plugins.FindAndReplace(true, true, {}, false),
124+
new codeInput.plugins.GoToLine(true, {}, false),
125125
new codeInput.plugins.Indent(true, 2),
126126
new codeInput.plugins.SelectTokenCallbacks(codeInput.plugins.SelectTokenCallbacks.TokenSelectorCallbacks.createClassSynchronisation("in-selection"), false, true, true, true, true, false),
127127
new codeInput.plugins.SpecialChars(true),
@@ -138,8 +138,8 @@ function beginTest(isHLJS) {
138138
popupElem.style.display = "none";
139139
}
140140
}),
141-
new codeInput.plugins.FindAndReplace(),
142-
new codeInput.plugins.GoToLine(),
141+
new codeInput.plugins.FindAndReplace(true, true, {}, false),
142+
new codeInput.plugins.GoToLine(true, {}, false),
143143
new codeInput.plugins.Indent(true, 2),
144144
new codeInput.plugins.SelectTokenCallbacks(new codeInput.plugins.SelectTokenCallbacks.TokenSelectorCallbacks(selectBrace, deselectAllBraces), true),
145145
new codeInput.plugins.SpecialChars(true),
@@ -489,7 +489,12 @@ console.log("I've got another line!", 2 &lt; 3, "should be true.");
489489
await waitAsync(50); // Wait for highlighting so text updates
490490

491491
// Open dialog and get interactive elements
492-
textarea.dispatchEvent(new KeyboardEvent("keydown", { "cancelable": true, "key": "f", "ctrlKey": true }));
492+
// Thanks to https://developer.mozilla.org/en-US/docs/Web/API/Navigator/platform
493+
if(navigator.platform.startsWith("Mac") || navigator.platform === "iPhone") {
494+
textarea.dispatchEvent(new KeyboardEvent("keydown", { "cancelable": true, "key": "f", "metaKey": true }));
495+
} else {
496+
textarea.dispatchEvent(new KeyboardEvent("keydown", { "cancelable": true, "key": "f", "ctrlKey": true }));
497+
}
493498
let inputBoxes = codeInputElement.querySelectorAll(".code-input_find-and-replace_dialog input");
494499
let findInput = inputBoxes[0];
495500
let regExpCheckbox = inputBoxes[1];
@@ -534,8 +539,11 @@ console.log("I've got another line!", 2 &lt; 3, "should be true.");
534539
assertEqual("FindAndReplace", "Selection End on Focused Match when Dialog Exited", textarea.selectionEnd, 8);
535540

536541
// Open replace dialog; conduct a find and replace
537-
textarea.dispatchEvent(new KeyboardEvent("keydown", { "cancelable": true, "key": "h", "ctrlKey": true }));
538-
findInput.value = "";
542+
if(navigator.platform.startsWith("Mac") || navigator.platform === "iPhone") {
543+
textarea.dispatchEvent(new KeyboardEvent("keydown", { "cancelable": true, "key": "h", "metaKey": true }));
544+
} else {
545+
textarea.dispatchEvent(new KeyboardEvent("keydown", { "cancelable": true, "key": "h", "ctrlKey": true }));
546+
} findInput.value = "";
539547
findInput.focus();
540548
allowInputEvents(findInput);
541549
addText(findInput, "hello");
@@ -575,33 +583,48 @@ console.log("I've got another line!", 2 &lt; 3, "should be true.");
575583
backspace(textarea);
576584
addText(textarea, "// 7 times table\nlet i = 1;\nwhile(i <= 12) { console.log(`7 x ${i} = ${7*i}`) }\n// That's my code.\n// This is another comment\n// Another\n// Line");
577585

578-
textarea.dispatchEvent(new KeyboardEvent("keydown", { "cancelable": true, "key": "g", "ctrlKey": true }));
579-
let lineInput = codeInputElement.querySelector(".code-input_go-to-line_dialog input");
586+
if(navigator.platform.startsWith("Mac") || navigator.platform === "iPhone") {
587+
textarea.dispatchEvent(new KeyboardEvent("keydown", { "cancelable": true, "key": "g", "metaKey": true }));
588+
} else {
589+
textarea.dispatchEvent(new KeyboardEvent("keydown", { "cancelable": true, "key": "g", "ctrlKey": true }));
590+
} let lineInput = codeInputElement.querySelector(".code-input_go-to-line_dialog input");
580591
lineInput.value = "1";
581592
lineInput.dispatchEvent(new KeyboardEvent("keydown", { "key": "Enter" }));
582593
lineInput.dispatchEvent(new KeyboardEvent("keyup", { "key": "Enter" }));
583594
assertEqual("GoToLine", "Line Only", textarea.selectionStart, 0);
584595

585-
textarea.dispatchEvent(new KeyboardEvent("keydown", { "cancelable": true, "key": "g", "ctrlKey": true }));
586-
lineInput.value = "3:18";
596+
if(navigator.platform.startsWith("Mac") || navigator.platform === "iPhone") {
597+
textarea.dispatchEvent(new KeyboardEvent("keydown", { "cancelable": true, "key": "g", "metaKey": true }));
598+
} else {
599+
textarea.dispatchEvent(new KeyboardEvent("keydown", { "cancelable": true, "key": "g", "ctrlKey": true }));
600+
} lineInput.value = "3:18";
587601
lineInput.dispatchEvent(new KeyboardEvent("keydown", { "key": "Enter" }));
588602
lineInput.dispatchEvent(new KeyboardEvent("keyup", { "key": "Enter" }));
589603
assertEqual("GoToLine", "Line and Column", textarea.selectionStart, 45);
590604

591-
textarea.dispatchEvent(new KeyboardEvent("keydown", { "cancelable": true, "key": "g", "ctrlKey": true }));
592-
lineInput.value = "10";
605+
if(navigator.platform.startsWith("Mac") || navigator.platform === "iPhone") {
606+
textarea.dispatchEvent(new KeyboardEvent("keydown", { "cancelable": true, "key": "g", "metaKey": true }));
607+
} else {
608+
textarea.dispatchEvent(new KeyboardEvent("keydown", { "cancelable": true, "key": "g", "ctrlKey": true }));
609+
} lineInput.value = "10";
593610
lineInput.dispatchEvent(new KeyboardEvent("keydown", { "key": "Enter" }));
594611
lineInput.dispatchEvent(new KeyboardEvent("keyup", { "key": "Enter" }));
595612
assertEqual("GoToLine", "Rejects Out-of-range Line", lineInput.classList.contains("code-input_go-to-line_error"), true);
596613

597-
textarea.dispatchEvent(new KeyboardEvent("keydown", { "cancelable": true, "key": "g", "ctrlKey": true }));
598-
lineInput.value = "2:12";
614+
if(navigator.platform.startsWith("Mac") || navigator.platform === "iPhone") {
615+
textarea.dispatchEvent(new KeyboardEvent("keydown", { "cancelable": true, "key": "g", "metaKey": true }));
616+
} else {
617+
textarea.dispatchEvent(new KeyboardEvent("keydown", { "cancelable": true, "key": "g", "ctrlKey": true }));
618+
} lineInput.value = "2:12";
599619
lineInput.dispatchEvent(new KeyboardEvent("keydown", { "key": "Enter" }));
600620
lineInput.dispatchEvent(new KeyboardEvent("keyup", { "key": "Enter" }));
601621
assertEqual("GoToLine", "Rejects Out-of-range Column", lineInput.classList.contains("code-input_go-to-line_error"), true);
602622

603-
textarea.dispatchEvent(new KeyboardEvent("keydown", { "cancelable": true, "key": "g", "ctrlKey": true }));
604-
lineInput.value = "sausages";
623+
if(navigator.platform.startsWith("Mac") || navigator.platform === "iPhone") {
624+
textarea.dispatchEvent(new KeyboardEvent("keydown", { "cancelable": true, "key": "g", "metaKey": true }));
625+
} else {
626+
textarea.dispatchEvent(new KeyboardEvent("keydown", { "cancelable": true, "key": "g", "ctrlKey": true }));
627+
} lineInput.value = "sausages";
605628
lineInput.dispatchEvent(new KeyboardEvent("keydown", { "key": "Enter" }));
606629
lineInput.dispatchEvent(new KeyboardEvent("keyup", { "key": "Enter" }));
607630
assertEqual("GoToLine", "Rejects Invalid Input", lineInput.classList.contains("code-input_go-to-line_error"), true);

0 commit comments

Comments
 (0)