marksmith 0.4.1 → 0.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/assets/javascripts/list_continuation_controller-full.esm.js +1 -1
- data/app/assets/javascripts/list_continuation_controller-no-stimulus.esm.js +1 -1
- data/app/assets/javascripts/marksmith_controller-full.esm.js +452 -17
- data/app/assets/javascripts/marksmith_controller-no-stimulus.esm.js +452 -17
- data/app/frontend/entrypoints/javascript/controllers/marksmith_controller.js +14 -1
- data/app/helpers/marksmith/marksmith_helper.rb +8 -2
- data/app/models/marksmith/editor.rb +8 -0
- data/app/views/marksmith/shared/_action_bar.html.erb +4 -4
- data/app/views/marksmith/shared/_editor.html.erb +2 -2
- data/app/views/marksmith/shared/_editor_pane.html.erb +1 -1
- data/app/views/marksmith/shared/_preview_pane.html.erb +1 -1
- data/app/views/marksmith/shared/_toolbar.html.erb +1 -1
- data/lib/marksmith/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bac426dee50d826889905eef507ee85bf15741a4f4d7ed85d28f88b77d60c7f7
|
4
|
+
data.tar.gz: 11374fff22c72c904840bf9435de06fafd6e9cc23d360f9fb873277757603479
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6a0f7eca2d748c4b8ac57b33e45a07aace4cdcec9195af7d7ce781a7230b5ad60edaff8cde18cc36aecb6e2aabe07142703681c2678a8c8a0611fe6c8c07cd6e
|
7
|
+
data.tar.gz: 5ec63e1912cce26a044f9fdfc902cc4436ef7660cc6c04e67dd07a25c8dd1e0549f94a201b00df8a6d0ed29ce1e95c8cd2fdf1e8a5dbf875a76bde199bc14c3c
|
@@ -1,5 +1,5 @@
|
|
1
1
|
/*!
|
2
|
-
Marksmith 0.4.
|
2
|
+
Marksmith 0.4.4
|
3
3
|
*/
|
4
4
|
var MarksmithController = (function () {
|
5
5
|
'use strict';
|
@@ -2417,15 +2417,15 @@ var MarksmithController = (function () {
|
|
2417
2417
|
}
|
2418
2418
|
el.addEventListener('paste', unsetSkipFormattedFlag);
|
2419
2419
|
}
|
2420
|
-
function uninstall$
|
2420
|
+
function uninstall$6(el) {
|
2421
2421
|
el.removeEventListener('keydown', setSkipFormattingFlag);
|
2422
2422
|
el.removeEventListener('paste', unsetSkipFormattedFlag);
|
2423
2423
|
}
|
2424
2424
|
|
2425
|
-
function install$
|
2425
|
+
function install$5(el) {
|
2426
2426
|
el.addEventListener('paste', onPaste$4);
|
2427
2427
|
}
|
2428
|
-
function uninstall$
|
2428
|
+
function uninstall$5(el) {
|
2429
2429
|
el.removeEventListener('paste', onPaste$4);
|
2430
2430
|
}
|
2431
2431
|
function onPaste$4(event) {
|
@@ -2539,12 +2539,12 @@ var MarksmithController = (function () {
|
|
2539
2539
|
return ((_a = link.textContent) === null || _a === undefined ? undefined : _a.slice(0, 1)) === '@' && link.getAttribute('data-hovercard-type') === 'team';
|
2540
2540
|
}
|
2541
2541
|
|
2542
|
-
function install$
|
2542
|
+
function install$4(el) {
|
2543
2543
|
el.addEventListener('dragover', onDragover$1);
|
2544
2544
|
el.addEventListener('drop', onDrop$1);
|
2545
2545
|
el.addEventListener('paste', onPaste$3);
|
2546
2546
|
}
|
2547
|
-
function uninstall$
|
2547
|
+
function uninstall$4(el) {
|
2548
2548
|
el.removeEventListener('dragover', onDragover$1);
|
2549
2549
|
el.removeEventListener('drop', onDrop$1);
|
2550
2550
|
el.removeEventListener('paste', onPaste$3);
|
@@ -2607,12 +2607,12 @@ var MarksmithController = (function () {
|
|
2607
2607
|
}
|
2608
2608
|
|
2609
2609
|
const pasteLinkAsPlainTextOverSelectedTextMap = new WeakMap();
|
2610
|
-
function install$
|
2610
|
+
function install$3(el, optionConfig) {
|
2611
2611
|
var _a;
|
2612
2612
|
pasteLinkAsPlainTextOverSelectedTextMap.set(el, ((_a = optionConfig === null || optionConfig === undefined ? undefined : optionConfig.defaultPlainTextPaste) === null || _a === undefined ? undefined : _a.urlLinks) === true);
|
2613
2613
|
el.addEventListener('paste', onPaste$2);
|
2614
2614
|
}
|
2615
|
-
function uninstall$
|
2615
|
+
function uninstall$3(el) {
|
2616
2616
|
el.removeEventListener('paste', onPaste$2);
|
2617
2617
|
}
|
2618
2618
|
function onPaste$2(event) {
|
@@ -2676,12 +2676,12 @@ var MarksmithController = (function () {
|
|
2676
2676
|
return url.endsWith('/') ? url.slice(0, url.length - 1) : url;
|
2677
2677
|
}
|
2678
2678
|
|
2679
|
-
function install$
|
2679
|
+
function install$2(el) {
|
2680
2680
|
el.addEventListener('dragover', onDragover);
|
2681
2681
|
el.addEventListener('drop', onDrop);
|
2682
2682
|
el.addEventListener('paste', onPaste$1);
|
2683
2683
|
}
|
2684
|
-
function uninstall$
|
2684
|
+
function uninstall$2(el) {
|
2685
2685
|
el.removeEventListener('dragover', onDragover);
|
2686
2686
|
el.removeEventListener('drop', onDrop);
|
2687
2687
|
el.removeEventListener('paste', onPaste$1);
|
@@ -2772,10 +2772,10 @@ var MarksmithController = (function () {
|
|
2772
2772
|
return [start, formattedTable, end].join('').replace(/<meta.*?>/, '');
|
2773
2773
|
}
|
2774
2774
|
|
2775
|
-
function install(el) {
|
2775
|
+
function install$1(el) {
|
2776
2776
|
el.addEventListener('paste', onPaste);
|
2777
2777
|
}
|
2778
|
-
function uninstall(el) {
|
2778
|
+
function uninstall$1(el) {
|
2779
2779
|
el.removeEventListener('paste', onPaste);
|
2780
2780
|
}
|
2781
2781
|
function onPaste(event) {
|
@@ -2800,19 +2800,442 @@ var MarksmithController = (function () {
|
|
2800
2800
|
}
|
2801
2801
|
|
2802
2802
|
function subscribe(el, optionConfig) {
|
2803
|
-
installAround(el, [install$
|
2803
|
+
installAround(el, [install$2, install$4, install$3, install$1, install$5], optionConfig);
|
2804
2804
|
return {
|
2805
2805
|
unsubscribe: () => {
|
2806
|
+
uninstall$6(el);
|
2807
|
+
uninstall$2(el);
|
2806
2808
|
uninstall$5(el);
|
2807
|
-
uninstall$1(el);
|
2808
2809
|
uninstall$4(el);
|
2809
2810
|
uninstall$3(el);
|
2810
|
-
uninstall$
|
2811
|
-
uninstall(el);
|
2811
|
+
uninstall$1(el);
|
2812
2812
|
},
|
2813
2813
|
};
|
2814
2814
|
}
|
2815
2815
|
|
2816
|
+
class Leaf {
|
2817
|
+
constructor(trie) {
|
2818
|
+
this.children = [];
|
2819
|
+
this.parent = trie;
|
2820
|
+
}
|
2821
|
+
delete(value) {
|
2822
|
+
const index = this.children.indexOf(value);
|
2823
|
+
if (index === -1)
|
2824
|
+
return false;
|
2825
|
+
this.children = this.children.slice(0, index).concat(this.children.slice(index + 1));
|
2826
|
+
if (this.children.length === 0) {
|
2827
|
+
this.parent.delete(this);
|
2828
|
+
}
|
2829
|
+
return true;
|
2830
|
+
}
|
2831
|
+
add(value) {
|
2832
|
+
this.children.push(value);
|
2833
|
+
return this;
|
2834
|
+
}
|
2835
|
+
}
|
2836
|
+
class RadixTrie {
|
2837
|
+
constructor(trie) {
|
2838
|
+
this.parent = null;
|
2839
|
+
this.children = {};
|
2840
|
+
this.parent = trie || null;
|
2841
|
+
}
|
2842
|
+
get(edge) {
|
2843
|
+
return this.children[edge];
|
2844
|
+
}
|
2845
|
+
insert(edges) {
|
2846
|
+
let currentNode = this;
|
2847
|
+
for (let i = 0; i < edges.length; i += 1) {
|
2848
|
+
const edge = edges[i];
|
2849
|
+
let nextNode = currentNode.get(edge);
|
2850
|
+
if (i === edges.length - 1) {
|
2851
|
+
if (nextNode instanceof RadixTrie) {
|
2852
|
+
currentNode.delete(nextNode);
|
2853
|
+
nextNode = null;
|
2854
|
+
}
|
2855
|
+
if (!nextNode) {
|
2856
|
+
nextNode = new Leaf(currentNode);
|
2857
|
+
currentNode.children[edge] = nextNode;
|
2858
|
+
}
|
2859
|
+
return nextNode;
|
2860
|
+
}
|
2861
|
+
else {
|
2862
|
+
if (nextNode instanceof Leaf)
|
2863
|
+
nextNode = null;
|
2864
|
+
if (!nextNode) {
|
2865
|
+
nextNode = new RadixTrie(currentNode);
|
2866
|
+
currentNode.children[edge] = nextNode;
|
2867
|
+
}
|
2868
|
+
}
|
2869
|
+
currentNode = nextNode;
|
2870
|
+
}
|
2871
|
+
return currentNode;
|
2872
|
+
}
|
2873
|
+
delete(node) {
|
2874
|
+
for (const edge in this.children) {
|
2875
|
+
const currentNode = this.children[edge];
|
2876
|
+
if (currentNode === node) {
|
2877
|
+
const success = delete this.children[edge];
|
2878
|
+
if (Object.keys(this.children).length === 0 && this.parent) {
|
2879
|
+
this.parent.delete(this);
|
2880
|
+
}
|
2881
|
+
return success;
|
2882
|
+
}
|
2883
|
+
}
|
2884
|
+
return false;
|
2885
|
+
}
|
2886
|
+
}
|
2887
|
+
|
2888
|
+
const macosSymbolLayerKeys = {
|
2889
|
+
['¡']: '1',
|
2890
|
+
['™']: '2',
|
2891
|
+
['£']: '3',
|
2892
|
+
['¢']: '4',
|
2893
|
+
['∞']: '5',
|
2894
|
+
['§']: '6',
|
2895
|
+
['¶']: '7',
|
2896
|
+
['•']: '8',
|
2897
|
+
['ª']: '9',
|
2898
|
+
['º']: '0',
|
2899
|
+
['–']: '-',
|
2900
|
+
['≠']: '=',
|
2901
|
+
['⁄']: '!',
|
2902
|
+
['€']: '@',
|
2903
|
+
['‹']: '#',
|
2904
|
+
['›']: '$',
|
2905
|
+
['fi']: '%',
|
2906
|
+
['fl']: '^',
|
2907
|
+
['‡']: '&',
|
2908
|
+
['°']: '*',
|
2909
|
+
['·']: '(',
|
2910
|
+
['‚']: ')',
|
2911
|
+
['—']: '_',
|
2912
|
+
['±']: '+',
|
2913
|
+
['œ']: 'q',
|
2914
|
+
['∑']: 'w',
|
2915
|
+
['®']: 'r',
|
2916
|
+
['†']: 't',
|
2917
|
+
['¥']: 'y',
|
2918
|
+
['ø']: 'o',
|
2919
|
+
['π']: 'p',
|
2920
|
+
['“']: '[',
|
2921
|
+
['‘']: ']',
|
2922
|
+
['«']: '\\',
|
2923
|
+
['Œ']: 'Q',
|
2924
|
+
['„']: 'W',
|
2925
|
+
['´']: 'E',
|
2926
|
+
['‰']: 'R',
|
2927
|
+
['ˇ']: 'T',
|
2928
|
+
['Á']: 'Y',
|
2929
|
+
['¨']: 'U',
|
2930
|
+
['ˆ']: 'I',
|
2931
|
+
['Ø']: 'O',
|
2932
|
+
['∏']: 'P',
|
2933
|
+
['”']: '{',
|
2934
|
+
['’']: '}',
|
2935
|
+
['»']: '|',
|
2936
|
+
['å']: 'a',
|
2937
|
+
['ß']: 's',
|
2938
|
+
['∂']: 'd',
|
2939
|
+
['ƒ']: 'f',
|
2940
|
+
['©']: 'g',
|
2941
|
+
['˙']: 'h',
|
2942
|
+
['∆']: 'j',
|
2943
|
+
['˚']: 'k',
|
2944
|
+
['¬']: 'l',
|
2945
|
+
['…']: ';',
|
2946
|
+
['æ']: "'",
|
2947
|
+
['Å']: 'A',
|
2948
|
+
['Í']: 'S',
|
2949
|
+
['Î']: 'D',
|
2950
|
+
['Ï']: 'F',
|
2951
|
+
['˝']: 'G',
|
2952
|
+
['Ó']: 'H',
|
2953
|
+
['Ô']: 'J',
|
2954
|
+
['']: 'K',
|
2955
|
+
['Ò']: 'L',
|
2956
|
+
['Ú']: ':',
|
2957
|
+
['Æ']: '"',
|
2958
|
+
['Ω']: 'z',
|
2959
|
+
['≈']: 'x',
|
2960
|
+
['ç']: 'c',
|
2961
|
+
['√']: 'v',
|
2962
|
+
['∫']: 'b',
|
2963
|
+
['µ']: 'm',
|
2964
|
+
['≤']: ',',
|
2965
|
+
['≥']: '.',
|
2966
|
+
['÷']: '/',
|
2967
|
+
['¸']: 'Z',
|
2968
|
+
['˛']: 'X',
|
2969
|
+
['Ç']: 'C',
|
2970
|
+
['◊']: 'V',
|
2971
|
+
['ı']: 'B',
|
2972
|
+
['˜']: 'N',
|
2973
|
+
['Â']: 'M',
|
2974
|
+
['¯']: '<',
|
2975
|
+
['˘']: '>',
|
2976
|
+
['¿']: '?'
|
2977
|
+
};
|
2978
|
+
|
2979
|
+
const macosUppercaseLayerKeys = {
|
2980
|
+
['`']: '~',
|
2981
|
+
['1']: '!',
|
2982
|
+
['2']: '@',
|
2983
|
+
['3']: '#',
|
2984
|
+
['4']: '$',
|
2985
|
+
['5']: '%',
|
2986
|
+
['6']: '^',
|
2987
|
+
['7']: '&',
|
2988
|
+
['8']: '*',
|
2989
|
+
['9']: '(',
|
2990
|
+
['0']: ')',
|
2991
|
+
['-']: '_',
|
2992
|
+
['=']: '+',
|
2993
|
+
['[']: '{',
|
2994
|
+
[']']: '}',
|
2995
|
+
['\\']: '|',
|
2996
|
+
[';']: ':',
|
2997
|
+
["'"]: '"',
|
2998
|
+
[',']: '<',
|
2999
|
+
['.']: '>',
|
3000
|
+
['/']: '?',
|
3001
|
+
['q']: 'Q',
|
3002
|
+
['w']: 'W',
|
3003
|
+
['e']: 'E',
|
3004
|
+
['r']: 'R',
|
3005
|
+
['t']: 'T',
|
3006
|
+
['y']: 'Y',
|
3007
|
+
['u']: 'U',
|
3008
|
+
['i']: 'I',
|
3009
|
+
['o']: 'O',
|
3010
|
+
['p']: 'P',
|
3011
|
+
['a']: 'A',
|
3012
|
+
['s']: 'S',
|
3013
|
+
['d']: 'D',
|
3014
|
+
['f']: 'F',
|
3015
|
+
['g']: 'G',
|
3016
|
+
['h']: 'H',
|
3017
|
+
['j']: 'J',
|
3018
|
+
['k']: 'K',
|
3019
|
+
['l']: 'L',
|
3020
|
+
['z']: 'Z',
|
3021
|
+
['x']: 'X',
|
3022
|
+
['c']: 'C',
|
3023
|
+
['v']: 'V',
|
3024
|
+
['b']: 'B',
|
3025
|
+
['n']: 'N',
|
3026
|
+
['m']: 'M'
|
3027
|
+
};
|
3028
|
+
|
3029
|
+
const syntheticKeyNames = {
|
3030
|
+
' ': 'Space',
|
3031
|
+
'+': 'Plus'
|
3032
|
+
};
|
3033
|
+
function eventToHotkeyString(event, platform = navigator.platform) {
|
3034
|
+
var _a, _b, _c;
|
3035
|
+
const { ctrlKey, altKey, metaKey, shiftKey, key } = event;
|
3036
|
+
const hotkeyString = [];
|
3037
|
+
const modifiers = [ctrlKey, altKey, metaKey, shiftKey];
|
3038
|
+
for (const [i, mod] of modifiers.entries()) {
|
3039
|
+
if (mod)
|
3040
|
+
hotkeyString.push(modifierKeyNames[i]);
|
3041
|
+
}
|
3042
|
+
if (!modifierKeyNames.includes(key)) {
|
3043
|
+
const altNormalizedKey = hotkeyString.includes('Alt') && matchApplePlatform.test(platform) ? (_a = macosSymbolLayerKeys[key]) !== null && _a !== undefined ? _a : key : key;
|
3044
|
+
const shiftNormalizedKey = hotkeyString.includes('Shift') && matchApplePlatform.test(platform)
|
3045
|
+
? (_b = macosUppercaseLayerKeys[altNormalizedKey]) !== null && _b !== undefined ? _b : altNormalizedKey
|
3046
|
+
: altNormalizedKey;
|
3047
|
+
const syntheticKey = (_c = syntheticKeyNames[shiftNormalizedKey]) !== null && _c !== undefined ? _c : shiftNormalizedKey;
|
3048
|
+
hotkeyString.push(syntheticKey);
|
3049
|
+
}
|
3050
|
+
return hotkeyString.join('+');
|
3051
|
+
}
|
3052
|
+
const modifierKeyNames = ['Control', 'Alt', 'Meta', 'Shift'];
|
3053
|
+
function normalizeHotkey(hotkey, platform) {
|
3054
|
+
let result;
|
3055
|
+
result = localizeMod(hotkey);
|
3056
|
+
result = sortModifiers(result);
|
3057
|
+
return result;
|
3058
|
+
}
|
3059
|
+
const matchApplePlatform = /Mac|iPod|iPhone|iPad/i;
|
3060
|
+
function localizeMod(hotkey, platform) {
|
3061
|
+
var _a;
|
3062
|
+
const ssrSafeWindow = typeof window === 'undefined' ? undefined : window;
|
3063
|
+
const safePlatform = (_a = ssrSafeWindow === null || ssrSafeWindow === undefined ? undefined : ssrSafeWindow.navigator.platform) !== null && _a !== undefined ? _a : '';
|
3064
|
+
const localModifier = matchApplePlatform.test(safePlatform) ? 'Meta' : 'Control';
|
3065
|
+
return hotkey.replace('Mod', localModifier);
|
3066
|
+
}
|
3067
|
+
function sortModifiers(hotkey) {
|
3068
|
+
const key = hotkey.split('+').pop();
|
3069
|
+
const modifiers = [];
|
3070
|
+
for (const modifier of ['Control', 'Alt', 'Meta', 'Shift']) {
|
3071
|
+
if (hotkey.includes(modifier)) {
|
3072
|
+
modifiers.push(modifier);
|
3073
|
+
}
|
3074
|
+
}
|
3075
|
+
if (key)
|
3076
|
+
modifiers.push(key);
|
3077
|
+
return modifiers.join('+');
|
3078
|
+
}
|
3079
|
+
|
3080
|
+
const SEQUENCE_DELIMITER = ' ';
|
3081
|
+
class SequenceTracker {
|
3082
|
+
constructor({ onReset } = {}) {
|
3083
|
+
this._path = [];
|
3084
|
+
this.timer = null;
|
3085
|
+
this.onReset = onReset;
|
3086
|
+
}
|
3087
|
+
get path() {
|
3088
|
+
return this._path;
|
3089
|
+
}
|
3090
|
+
get sequence() {
|
3091
|
+
return this._path.join(SEQUENCE_DELIMITER);
|
3092
|
+
}
|
3093
|
+
registerKeypress(event) {
|
3094
|
+
this._path = [...this._path, eventToHotkeyString(event)];
|
3095
|
+
this.startTimer();
|
3096
|
+
}
|
3097
|
+
reset() {
|
3098
|
+
var _a;
|
3099
|
+
this.killTimer();
|
3100
|
+
this._path = [];
|
3101
|
+
(_a = this.onReset) === null || _a === undefined ? undefined : _a.call(this);
|
3102
|
+
}
|
3103
|
+
killTimer() {
|
3104
|
+
if (this.timer != null) {
|
3105
|
+
window.clearTimeout(this.timer);
|
3106
|
+
}
|
3107
|
+
this.timer = null;
|
3108
|
+
}
|
3109
|
+
startTimer() {
|
3110
|
+
this.killTimer();
|
3111
|
+
this.timer = window.setTimeout(() => this.reset(), SequenceTracker.CHORD_TIMEOUT);
|
3112
|
+
}
|
3113
|
+
}
|
3114
|
+
SequenceTracker.CHORD_TIMEOUT = 1500;
|
3115
|
+
|
3116
|
+
function isFormField(element) {
|
3117
|
+
if (!(element instanceof HTMLElement)) {
|
3118
|
+
return false;
|
3119
|
+
}
|
3120
|
+
const name = element.nodeName.toLowerCase();
|
3121
|
+
const type = (element.getAttribute('type') || '').toLowerCase();
|
3122
|
+
return (name === 'select' ||
|
3123
|
+
name === 'textarea' ||
|
3124
|
+
(name === 'input' &&
|
3125
|
+
type !== 'submit' &&
|
3126
|
+
type !== 'reset' &&
|
3127
|
+
type !== 'checkbox' &&
|
3128
|
+
type !== 'radio' &&
|
3129
|
+
type !== 'file') ||
|
3130
|
+
element.isContentEditable);
|
3131
|
+
}
|
3132
|
+
function fireDeterminedAction(el, path) {
|
3133
|
+
const delegateEvent = new CustomEvent('hotkey-fire', { cancelable: true, detail: { path } });
|
3134
|
+
const cancelled = !el.dispatchEvent(delegateEvent);
|
3135
|
+
if (cancelled)
|
3136
|
+
return;
|
3137
|
+
if (isFormField(el)) {
|
3138
|
+
el.focus();
|
3139
|
+
}
|
3140
|
+
else {
|
3141
|
+
el.click();
|
3142
|
+
}
|
3143
|
+
}
|
3144
|
+
function expandHotkeyToEdges(hotkey) {
|
3145
|
+
const output = [];
|
3146
|
+
let acc = [''];
|
3147
|
+
let commaIsSeparator = false;
|
3148
|
+
for (let i = 0; i < hotkey.length; i++) {
|
3149
|
+
if (commaIsSeparator && hotkey[i] === ',') {
|
3150
|
+
output.push(acc);
|
3151
|
+
acc = [''];
|
3152
|
+
commaIsSeparator = false;
|
3153
|
+
continue;
|
3154
|
+
}
|
3155
|
+
if (hotkey[i] === SEQUENCE_DELIMITER) {
|
3156
|
+
acc.push('');
|
3157
|
+
commaIsSeparator = false;
|
3158
|
+
continue;
|
3159
|
+
}
|
3160
|
+
else if (hotkey[i] === '+') {
|
3161
|
+
commaIsSeparator = false;
|
3162
|
+
}
|
3163
|
+
else {
|
3164
|
+
commaIsSeparator = true;
|
3165
|
+
}
|
3166
|
+
acc[acc.length - 1] += hotkey[i];
|
3167
|
+
}
|
3168
|
+
output.push(acc);
|
3169
|
+
return output.map(h => h.map(k => normalizeHotkey(k)).filter(k => k !== '')).filter(h => h.length > 0);
|
3170
|
+
}
|
3171
|
+
|
3172
|
+
const hotkeyRadixTrie = new RadixTrie();
|
3173
|
+
const elementsLeaves = new WeakMap();
|
3174
|
+
let currentTriePosition = hotkeyRadixTrie;
|
3175
|
+
const sequenceTracker = new SequenceTracker({
|
3176
|
+
onReset() {
|
3177
|
+
currentTriePosition = hotkeyRadixTrie;
|
3178
|
+
}
|
3179
|
+
});
|
3180
|
+
function keyDownHandler(event) {
|
3181
|
+
if (event.defaultPrevented)
|
3182
|
+
return;
|
3183
|
+
if (!(event.target instanceof Node))
|
3184
|
+
return;
|
3185
|
+
if (isFormField(event.target)) {
|
3186
|
+
const target = event.target;
|
3187
|
+
if (!target.id)
|
3188
|
+
return;
|
3189
|
+
if (!target.ownerDocument.querySelector(`[data-hotkey-scope="${target.id}"]`))
|
3190
|
+
return;
|
3191
|
+
}
|
3192
|
+
const newTriePosition = currentTriePosition.get(eventToHotkeyString(event));
|
3193
|
+
if (!newTriePosition) {
|
3194
|
+
sequenceTracker.reset();
|
3195
|
+
return;
|
3196
|
+
}
|
3197
|
+
sequenceTracker.registerKeypress(event);
|
3198
|
+
currentTriePosition = newTriePosition;
|
3199
|
+
if (newTriePosition instanceof Leaf) {
|
3200
|
+
const target = event.target;
|
3201
|
+
let shouldFire = false;
|
3202
|
+
let elementToFire;
|
3203
|
+
const formField = isFormField(target);
|
3204
|
+
for (let i = newTriePosition.children.length - 1; i >= 0; i -= 1) {
|
3205
|
+
elementToFire = newTriePosition.children[i];
|
3206
|
+
const scope = elementToFire.getAttribute('data-hotkey-scope');
|
3207
|
+
if ((!formField && !scope) || (formField && target.id === scope)) {
|
3208
|
+
shouldFire = true;
|
3209
|
+
break;
|
3210
|
+
}
|
3211
|
+
}
|
3212
|
+
if (elementToFire && shouldFire) {
|
3213
|
+
fireDeterminedAction(elementToFire, sequenceTracker.path);
|
3214
|
+
event.preventDefault();
|
3215
|
+
}
|
3216
|
+
sequenceTracker.reset();
|
3217
|
+
}
|
3218
|
+
}
|
3219
|
+
function install(element, hotkey) {
|
3220
|
+
if (Object.keys(hotkeyRadixTrie.children).length === 0) {
|
3221
|
+
document.addEventListener('keydown', keyDownHandler);
|
3222
|
+
}
|
3223
|
+
const hotkeys = expandHotkeyToEdges(element.getAttribute('data-hotkey') || '');
|
3224
|
+
const leaves = hotkeys.map(h => hotkeyRadixTrie.insert(h).add(element));
|
3225
|
+
elementsLeaves.set(element, leaves);
|
3226
|
+
}
|
3227
|
+
function uninstall(element) {
|
3228
|
+
const leaves = elementsLeaves.get(element);
|
3229
|
+
if (leaves && leaves.length) {
|
3230
|
+
for (const leaf of leaves) {
|
3231
|
+
leaf && leaf.delete(element);
|
3232
|
+
}
|
3233
|
+
}
|
3234
|
+
if (Object.keys(hotkeyRadixTrie.children).length === 0) {
|
3235
|
+
document.removeEventListener('keydown', keyDownHandler);
|
3236
|
+
}
|
3237
|
+
}
|
3238
|
+
|
2816
3239
|
/* eslint-disable camelcase */
|
2817
3240
|
|
2818
3241
|
// upload code from Jeremy Smith's blog post
|
@@ -2839,7 +3262,19 @@ var MarksmithController = (function () {
|
|
2839
3262
|
}
|
2840
3263
|
|
2841
3264
|
connect() {
|
2842
|
-
subscribe(this.
|
3265
|
+
subscribe(this.fieldElementTarget);
|
3266
|
+
|
3267
|
+
// Install all the hotkeys on the page
|
3268
|
+
for (const el of document.querySelectorAll('[data-hotkey]')) {
|
3269
|
+
install(el);
|
3270
|
+
}
|
3271
|
+
}
|
3272
|
+
|
3273
|
+
disconnect() {
|
3274
|
+
// Uninstall all the hotkeys on the page
|
3275
|
+
for (const el of document.querySelectorAll('[data-hotkey]')) {
|
3276
|
+
uninstall(el);
|
3277
|
+
}
|
2843
3278
|
}
|
2844
3279
|
|
2845
3280
|
switchToWrite(event) {
|
@@ -1,5 +1,5 @@
|
|
1
1
|
/*!
|
2
|
-
Marksmith 0.4.
|
2
|
+
Marksmith 0.4.4
|
3
3
|
*/
|
4
4
|
var MarksmithController = (function (stimulus) {
|
5
5
|
'use strict';
|
@@ -1927,15 +1927,15 @@ var MarksmithController = (function (stimulus) {
|
|
1927
1927
|
}
|
1928
1928
|
el.addEventListener('paste', unsetSkipFormattedFlag);
|
1929
1929
|
}
|
1930
|
-
function uninstall$
|
1930
|
+
function uninstall$6(el) {
|
1931
1931
|
el.removeEventListener('keydown', setSkipFormattingFlag);
|
1932
1932
|
el.removeEventListener('paste', unsetSkipFormattedFlag);
|
1933
1933
|
}
|
1934
1934
|
|
1935
|
-
function install$
|
1935
|
+
function install$5(el) {
|
1936
1936
|
el.addEventListener('paste', onPaste$4);
|
1937
1937
|
}
|
1938
|
-
function uninstall$
|
1938
|
+
function uninstall$5(el) {
|
1939
1939
|
el.removeEventListener('paste', onPaste$4);
|
1940
1940
|
}
|
1941
1941
|
function onPaste$4(event) {
|
@@ -2049,12 +2049,12 @@ var MarksmithController = (function (stimulus) {
|
|
2049
2049
|
return ((_a = link.textContent) === null || _a === undefined ? undefined : _a.slice(0, 1)) === '@' && link.getAttribute('data-hovercard-type') === 'team';
|
2050
2050
|
}
|
2051
2051
|
|
2052
|
-
function install$
|
2052
|
+
function install$4(el) {
|
2053
2053
|
el.addEventListener('dragover', onDragover$1);
|
2054
2054
|
el.addEventListener('drop', onDrop$1);
|
2055
2055
|
el.addEventListener('paste', onPaste$3);
|
2056
2056
|
}
|
2057
|
-
function uninstall$
|
2057
|
+
function uninstall$4(el) {
|
2058
2058
|
el.removeEventListener('dragover', onDragover$1);
|
2059
2059
|
el.removeEventListener('drop', onDrop$1);
|
2060
2060
|
el.removeEventListener('paste', onPaste$3);
|
@@ -2117,12 +2117,12 @@ var MarksmithController = (function (stimulus) {
|
|
2117
2117
|
}
|
2118
2118
|
|
2119
2119
|
const pasteLinkAsPlainTextOverSelectedTextMap = new WeakMap();
|
2120
|
-
function install$
|
2120
|
+
function install$3(el, optionConfig) {
|
2121
2121
|
var _a;
|
2122
2122
|
pasteLinkAsPlainTextOverSelectedTextMap.set(el, ((_a = optionConfig === null || optionConfig === undefined ? undefined : optionConfig.defaultPlainTextPaste) === null || _a === undefined ? undefined : _a.urlLinks) === true);
|
2123
2123
|
el.addEventListener('paste', onPaste$2);
|
2124
2124
|
}
|
2125
|
-
function uninstall$
|
2125
|
+
function uninstall$3(el) {
|
2126
2126
|
el.removeEventListener('paste', onPaste$2);
|
2127
2127
|
}
|
2128
2128
|
function onPaste$2(event) {
|
@@ -2186,12 +2186,12 @@ var MarksmithController = (function (stimulus) {
|
|
2186
2186
|
return url.endsWith('/') ? url.slice(0, url.length - 1) : url;
|
2187
2187
|
}
|
2188
2188
|
|
2189
|
-
function install$
|
2189
|
+
function install$2(el) {
|
2190
2190
|
el.addEventListener('dragover', onDragover);
|
2191
2191
|
el.addEventListener('drop', onDrop);
|
2192
2192
|
el.addEventListener('paste', onPaste$1);
|
2193
2193
|
}
|
2194
|
-
function uninstall$
|
2194
|
+
function uninstall$2(el) {
|
2195
2195
|
el.removeEventListener('dragover', onDragover);
|
2196
2196
|
el.removeEventListener('drop', onDrop);
|
2197
2197
|
el.removeEventListener('paste', onPaste$1);
|
@@ -2282,10 +2282,10 @@ var MarksmithController = (function (stimulus) {
|
|
2282
2282
|
return [start, formattedTable, end].join('').replace(/<meta.*?>/, '');
|
2283
2283
|
}
|
2284
2284
|
|
2285
|
-
function install(el) {
|
2285
|
+
function install$1(el) {
|
2286
2286
|
el.addEventListener('paste', onPaste);
|
2287
2287
|
}
|
2288
|
-
function uninstall(el) {
|
2288
|
+
function uninstall$1(el) {
|
2289
2289
|
el.removeEventListener('paste', onPaste);
|
2290
2290
|
}
|
2291
2291
|
function onPaste(event) {
|
@@ -2310,19 +2310,442 @@ var MarksmithController = (function (stimulus) {
|
|
2310
2310
|
}
|
2311
2311
|
|
2312
2312
|
function subscribe(el, optionConfig) {
|
2313
|
-
installAround(el, [install$
|
2313
|
+
installAround(el, [install$2, install$4, install$3, install$1, install$5], optionConfig);
|
2314
2314
|
return {
|
2315
2315
|
unsubscribe: () => {
|
2316
|
+
uninstall$6(el);
|
2317
|
+
uninstall$2(el);
|
2316
2318
|
uninstall$5(el);
|
2317
|
-
uninstall$1(el);
|
2318
2319
|
uninstall$4(el);
|
2319
2320
|
uninstall$3(el);
|
2320
|
-
uninstall$
|
2321
|
-
uninstall(el);
|
2321
|
+
uninstall$1(el);
|
2322
2322
|
},
|
2323
2323
|
};
|
2324
2324
|
}
|
2325
2325
|
|
2326
|
+
class Leaf {
|
2327
|
+
constructor(trie) {
|
2328
|
+
this.children = [];
|
2329
|
+
this.parent = trie;
|
2330
|
+
}
|
2331
|
+
delete(value) {
|
2332
|
+
const index = this.children.indexOf(value);
|
2333
|
+
if (index === -1)
|
2334
|
+
return false;
|
2335
|
+
this.children = this.children.slice(0, index).concat(this.children.slice(index + 1));
|
2336
|
+
if (this.children.length === 0) {
|
2337
|
+
this.parent.delete(this);
|
2338
|
+
}
|
2339
|
+
return true;
|
2340
|
+
}
|
2341
|
+
add(value) {
|
2342
|
+
this.children.push(value);
|
2343
|
+
return this;
|
2344
|
+
}
|
2345
|
+
}
|
2346
|
+
class RadixTrie {
|
2347
|
+
constructor(trie) {
|
2348
|
+
this.parent = null;
|
2349
|
+
this.children = {};
|
2350
|
+
this.parent = trie || null;
|
2351
|
+
}
|
2352
|
+
get(edge) {
|
2353
|
+
return this.children[edge];
|
2354
|
+
}
|
2355
|
+
insert(edges) {
|
2356
|
+
let currentNode = this;
|
2357
|
+
for (let i = 0; i < edges.length; i += 1) {
|
2358
|
+
const edge = edges[i];
|
2359
|
+
let nextNode = currentNode.get(edge);
|
2360
|
+
if (i === edges.length - 1) {
|
2361
|
+
if (nextNode instanceof RadixTrie) {
|
2362
|
+
currentNode.delete(nextNode);
|
2363
|
+
nextNode = null;
|
2364
|
+
}
|
2365
|
+
if (!nextNode) {
|
2366
|
+
nextNode = new Leaf(currentNode);
|
2367
|
+
currentNode.children[edge] = nextNode;
|
2368
|
+
}
|
2369
|
+
return nextNode;
|
2370
|
+
}
|
2371
|
+
else {
|
2372
|
+
if (nextNode instanceof Leaf)
|
2373
|
+
nextNode = null;
|
2374
|
+
if (!nextNode) {
|
2375
|
+
nextNode = new RadixTrie(currentNode);
|
2376
|
+
currentNode.children[edge] = nextNode;
|
2377
|
+
}
|
2378
|
+
}
|
2379
|
+
currentNode = nextNode;
|
2380
|
+
}
|
2381
|
+
return currentNode;
|
2382
|
+
}
|
2383
|
+
delete(node) {
|
2384
|
+
for (const edge in this.children) {
|
2385
|
+
const currentNode = this.children[edge];
|
2386
|
+
if (currentNode === node) {
|
2387
|
+
const success = delete this.children[edge];
|
2388
|
+
if (Object.keys(this.children).length === 0 && this.parent) {
|
2389
|
+
this.parent.delete(this);
|
2390
|
+
}
|
2391
|
+
return success;
|
2392
|
+
}
|
2393
|
+
}
|
2394
|
+
return false;
|
2395
|
+
}
|
2396
|
+
}
|
2397
|
+
|
2398
|
+
const macosSymbolLayerKeys = {
|
2399
|
+
['¡']: '1',
|
2400
|
+
['™']: '2',
|
2401
|
+
['£']: '3',
|
2402
|
+
['¢']: '4',
|
2403
|
+
['∞']: '5',
|
2404
|
+
['§']: '6',
|
2405
|
+
['¶']: '7',
|
2406
|
+
['•']: '8',
|
2407
|
+
['ª']: '9',
|
2408
|
+
['º']: '0',
|
2409
|
+
['–']: '-',
|
2410
|
+
['≠']: '=',
|
2411
|
+
['⁄']: '!',
|
2412
|
+
['€']: '@',
|
2413
|
+
['‹']: '#',
|
2414
|
+
['›']: '$',
|
2415
|
+
['fi']: '%',
|
2416
|
+
['fl']: '^',
|
2417
|
+
['‡']: '&',
|
2418
|
+
['°']: '*',
|
2419
|
+
['·']: '(',
|
2420
|
+
['‚']: ')',
|
2421
|
+
['—']: '_',
|
2422
|
+
['±']: '+',
|
2423
|
+
['œ']: 'q',
|
2424
|
+
['∑']: 'w',
|
2425
|
+
['®']: 'r',
|
2426
|
+
['†']: 't',
|
2427
|
+
['¥']: 'y',
|
2428
|
+
['ø']: 'o',
|
2429
|
+
['π']: 'p',
|
2430
|
+
['“']: '[',
|
2431
|
+
['‘']: ']',
|
2432
|
+
['«']: '\\',
|
2433
|
+
['Œ']: 'Q',
|
2434
|
+
['„']: 'W',
|
2435
|
+
['´']: 'E',
|
2436
|
+
['‰']: 'R',
|
2437
|
+
['ˇ']: 'T',
|
2438
|
+
['Á']: 'Y',
|
2439
|
+
['¨']: 'U',
|
2440
|
+
['ˆ']: 'I',
|
2441
|
+
['Ø']: 'O',
|
2442
|
+
['∏']: 'P',
|
2443
|
+
['”']: '{',
|
2444
|
+
['’']: '}',
|
2445
|
+
['»']: '|',
|
2446
|
+
['å']: 'a',
|
2447
|
+
['ß']: 's',
|
2448
|
+
['∂']: 'd',
|
2449
|
+
['ƒ']: 'f',
|
2450
|
+
['©']: 'g',
|
2451
|
+
['˙']: 'h',
|
2452
|
+
['∆']: 'j',
|
2453
|
+
['˚']: 'k',
|
2454
|
+
['¬']: 'l',
|
2455
|
+
['…']: ';',
|
2456
|
+
['æ']: "'",
|
2457
|
+
['Å']: 'A',
|
2458
|
+
['Í']: 'S',
|
2459
|
+
['Î']: 'D',
|
2460
|
+
['Ï']: 'F',
|
2461
|
+
['˝']: 'G',
|
2462
|
+
['Ó']: 'H',
|
2463
|
+
['Ô']: 'J',
|
2464
|
+
['']: 'K',
|
2465
|
+
['Ò']: 'L',
|
2466
|
+
['Ú']: ':',
|
2467
|
+
['Æ']: '"',
|
2468
|
+
['Ω']: 'z',
|
2469
|
+
['≈']: 'x',
|
2470
|
+
['ç']: 'c',
|
2471
|
+
['√']: 'v',
|
2472
|
+
['∫']: 'b',
|
2473
|
+
['µ']: 'm',
|
2474
|
+
['≤']: ',',
|
2475
|
+
['≥']: '.',
|
2476
|
+
['÷']: '/',
|
2477
|
+
['¸']: 'Z',
|
2478
|
+
['˛']: 'X',
|
2479
|
+
['Ç']: 'C',
|
2480
|
+
['◊']: 'V',
|
2481
|
+
['ı']: 'B',
|
2482
|
+
['˜']: 'N',
|
2483
|
+
['Â']: 'M',
|
2484
|
+
['¯']: '<',
|
2485
|
+
['˘']: '>',
|
2486
|
+
['¿']: '?'
|
2487
|
+
};
|
2488
|
+
|
2489
|
+
const macosUppercaseLayerKeys = {
|
2490
|
+
['`']: '~',
|
2491
|
+
['1']: '!',
|
2492
|
+
['2']: '@',
|
2493
|
+
['3']: '#',
|
2494
|
+
['4']: '$',
|
2495
|
+
['5']: '%',
|
2496
|
+
['6']: '^',
|
2497
|
+
['7']: '&',
|
2498
|
+
['8']: '*',
|
2499
|
+
['9']: '(',
|
2500
|
+
['0']: ')',
|
2501
|
+
['-']: '_',
|
2502
|
+
['=']: '+',
|
2503
|
+
['[']: '{',
|
2504
|
+
[']']: '}',
|
2505
|
+
['\\']: '|',
|
2506
|
+
[';']: ':',
|
2507
|
+
["'"]: '"',
|
2508
|
+
[',']: '<',
|
2509
|
+
['.']: '>',
|
2510
|
+
['/']: '?',
|
2511
|
+
['q']: 'Q',
|
2512
|
+
['w']: 'W',
|
2513
|
+
['e']: 'E',
|
2514
|
+
['r']: 'R',
|
2515
|
+
['t']: 'T',
|
2516
|
+
['y']: 'Y',
|
2517
|
+
['u']: 'U',
|
2518
|
+
['i']: 'I',
|
2519
|
+
['o']: 'O',
|
2520
|
+
['p']: 'P',
|
2521
|
+
['a']: 'A',
|
2522
|
+
['s']: 'S',
|
2523
|
+
['d']: 'D',
|
2524
|
+
['f']: 'F',
|
2525
|
+
['g']: 'G',
|
2526
|
+
['h']: 'H',
|
2527
|
+
['j']: 'J',
|
2528
|
+
['k']: 'K',
|
2529
|
+
['l']: 'L',
|
2530
|
+
['z']: 'Z',
|
2531
|
+
['x']: 'X',
|
2532
|
+
['c']: 'C',
|
2533
|
+
['v']: 'V',
|
2534
|
+
['b']: 'B',
|
2535
|
+
['n']: 'N',
|
2536
|
+
['m']: 'M'
|
2537
|
+
};
|
2538
|
+
|
2539
|
+
const syntheticKeyNames = {
|
2540
|
+
' ': 'Space',
|
2541
|
+
'+': 'Plus'
|
2542
|
+
};
|
2543
|
+
function eventToHotkeyString(event, platform = navigator.platform) {
|
2544
|
+
var _a, _b, _c;
|
2545
|
+
const { ctrlKey, altKey, metaKey, shiftKey, key } = event;
|
2546
|
+
const hotkeyString = [];
|
2547
|
+
const modifiers = [ctrlKey, altKey, metaKey, shiftKey];
|
2548
|
+
for (const [i, mod] of modifiers.entries()) {
|
2549
|
+
if (mod)
|
2550
|
+
hotkeyString.push(modifierKeyNames[i]);
|
2551
|
+
}
|
2552
|
+
if (!modifierKeyNames.includes(key)) {
|
2553
|
+
const altNormalizedKey = hotkeyString.includes('Alt') && matchApplePlatform.test(platform) ? (_a = macosSymbolLayerKeys[key]) !== null && _a !== undefined ? _a : key : key;
|
2554
|
+
const shiftNormalizedKey = hotkeyString.includes('Shift') && matchApplePlatform.test(platform)
|
2555
|
+
? (_b = macosUppercaseLayerKeys[altNormalizedKey]) !== null && _b !== undefined ? _b : altNormalizedKey
|
2556
|
+
: altNormalizedKey;
|
2557
|
+
const syntheticKey = (_c = syntheticKeyNames[shiftNormalizedKey]) !== null && _c !== undefined ? _c : shiftNormalizedKey;
|
2558
|
+
hotkeyString.push(syntheticKey);
|
2559
|
+
}
|
2560
|
+
return hotkeyString.join('+');
|
2561
|
+
}
|
2562
|
+
const modifierKeyNames = ['Control', 'Alt', 'Meta', 'Shift'];
|
2563
|
+
function normalizeHotkey(hotkey, platform) {
|
2564
|
+
let result;
|
2565
|
+
result = localizeMod(hotkey);
|
2566
|
+
result = sortModifiers(result);
|
2567
|
+
return result;
|
2568
|
+
}
|
2569
|
+
const matchApplePlatform = /Mac|iPod|iPhone|iPad/i;
|
2570
|
+
function localizeMod(hotkey, platform) {
|
2571
|
+
var _a;
|
2572
|
+
const ssrSafeWindow = typeof window === 'undefined' ? undefined : window;
|
2573
|
+
const safePlatform = (_a = ssrSafeWindow === null || ssrSafeWindow === undefined ? undefined : ssrSafeWindow.navigator.platform) !== null && _a !== undefined ? _a : '';
|
2574
|
+
const localModifier = matchApplePlatform.test(safePlatform) ? 'Meta' : 'Control';
|
2575
|
+
return hotkey.replace('Mod', localModifier);
|
2576
|
+
}
|
2577
|
+
function sortModifiers(hotkey) {
|
2578
|
+
const key = hotkey.split('+').pop();
|
2579
|
+
const modifiers = [];
|
2580
|
+
for (const modifier of ['Control', 'Alt', 'Meta', 'Shift']) {
|
2581
|
+
if (hotkey.includes(modifier)) {
|
2582
|
+
modifiers.push(modifier);
|
2583
|
+
}
|
2584
|
+
}
|
2585
|
+
if (key)
|
2586
|
+
modifiers.push(key);
|
2587
|
+
return modifiers.join('+');
|
2588
|
+
}
|
2589
|
+
|
2590
|
+
const SEQUENCE_DELIMITER = ' ';
|
2591
|
+
class SequenceTracker {
|
2592
|
+
constructor({ onReset } = {}) {
|
2593
|
+
this._path = [];
|
2594
|
+
this.timer = null;
|
2595
|
+
this.onReset = onReset;
|
2596
|
+
}
|
2597
|
+
get path() {
|
2598
|
+
return this._path;
|
2599
|
+
}
|
2600
|
+
get sequence() {
|
2601
|
+
return this._path.join(SEQUENCE_DELIMITER);
|
2602
|
+
}
|
2603
|
+
registerKeypress(event) {
|
2604
|
+
this._path = [...this._path, eventToHotkeyString(event)];
|
2605
|
+
this.startTimer();
|
2606
|
+
}
|
2607
|
+
reset() {
|
2608
|
+
var _a;
|
2609
|
+
this.killTimer();
|
2610
|
+
this._path = [];
|
2611
|
+
(_a = this.onReset) === null || _a === undefined ? undefined : _a.call(this);
|
2612
|
+
}
|
2613
|
+
killTimer() {
|
2614
|
+
if (this.timer != null) {
|
2615
|
+
window.clearTimeout(this.timer);
|
2616
|
+
}
|
2617
|
+
this.timer = null;
|
2618
|
+
}
|
2619
|
+
startTimer() {
|
2620
|
+
this.killTimer();
|
2621
|
+
this.timer = window.setTimeout(() => this.reset(), SequenceTracker.CHORD_TIMEOUT);
|
2622
|
+
}
|
2623
|
+
}
|
2624
|
+
SequenceTracker.CHORD_TIMEOUT = 1500;
|
2625
|
+
|
2626
|
+
function isFormField(element) {
|
2627
|
+
if (!(element instanceof HTMLElement)) {
|
2628
|
+
return false;
|
2629
|
+
}
|
2630
|
+
const name = element.nodeName.toLowerCase();
|
2631
|
+
const type = (element.getAttribute('type') || '').toLowerCase();
|
2632
|
+
return (name === 'select' ||
|
2633
|
+
name === 'textarea' ||
|
2634
|
+
(name === 'input' &&
|
2635
|
+
type !== 'submit' &&
|
2636
|
+
type !== 'reset' &&
|
2637
|
+
type !== 'checkbox' &&
|
2638
|
+
type !== 'radio' &&
|
2639
|
+
type !== 'file') ||
|
2640
|
+
element.isContentEditable);
|
2641
|
+
}
|
2642
|
+
function fireDeterminedAction(el, path) {
|
2643
|
+
const delegateEvent = new CustomEvent('hotkey-fire', { cancelable: true, detail: { path } });
|
2644
|
+
const cancelled = !el.dispatchEvent(delegateEvent);
|
2645
|
+
if (cancelled)
|
2646
|
+
return;
|
2647
|
+
if (isFormField(el)) {
|
2648
|
+
el.focus();
|
2649
|
+
}
|
2650
|
+
else {
|
2651
|
+
el.click();
|
2652
|
+
}
|
2653
|
+
}
|
2654
|
+
function expandHotkeyToEdges(hotkey) {
|
2655
|
+
const output = [];
|
2656
|
+
let acc = [''];
|
2657
|
+
let commaIsSeparator = false;
|
2658
|
+
for (let i = 0; i < hotkey.length; i++) {
|
2659
|
+
if (commaIsSeparator && hotkey[i] === ',') {
|
2660
|
+
output.push(acc);
|
2661
|
+
acc = [''];
|
2662
|
+
commaIsSeparator = false;
|
2663
|
+
continue;
|
2664
|
+
}
|
2665
|
+
if (hotkey[i] === SEQUENCE_DELIMITER) {
|
2666
|
+
acc.push('');
|
2667
|
+
commaIsSeparator = false;
|
2668
|
+
continue;
|
2669
|
+
}
|
2670
|
+
else if (hotkey[i] === '+') {
|
2671
|
+
commaIsSeparator = false;
|
2672
|
+
}
|
2673
|
+
else {
|
2674
|
+
commaIsSeparator = true;
|
2675
|
+
}
|
2676
|
+
acc[acc.length - 1] += hotkey[i];
|
2677
|
+
}
|
2678
|
+
output.push(acc);
|
2679
|
+
return output.map(h => h.map(k => normalizeHotkey(k)).filter(k => k !== '')).filter(h => h.length > 0);
|
2680
|
+
}
|
2681
|
+
|
2682
|
+
const hotkeyRadixTrie = new RadixTrie();
|
2683
|
+
const elementsLeaves = new WeakMap();
|
2684
|
+
let currentTriePosition = hotkeyRadixTrie;
|
2685
|
+
const sequenceTracker = new SequenceTracker({
|
2686
|
+
onReset() {
|
2687
|
+
currentTriePosition = hotkeyRadixTrie;
|
2688
|
+
}
|
2689
|
+
});
|
2690
|
+
function keyDownHandler(event) {
|
2691
|
+
if (event.defaultPrevented)
|
2692
|
+
return;
|
2693
|
+
if (!(event.target instanceof Node))
|
2694
|
+
return;
|
2695
|
+
if (isFormField(event.target)) {
|
2696
|
+
const target = event.target;
|
2697
|
+
if (!target.id)
|
2698
|
+
return;
|
2699
|
+
if (!target.ownerDocument.querySelector(`[data-hotkey-scope="${target.id}"]`))
|
2700
|
+
return;
|
2701
|
+
}
|
2702
|
+
const newTriePosition = currentTriePosition.get(eventToHotkeyString(event));
|
2703
|
+
if (!newTriePosition) {
|
2704
|
+
sequenceTracker.reset();
|
2705
|
+
return;
|
2706
|
+
}
|
2707
|
+
sequenceTracker.registerKeypress(event);
|
2708
|
+
currentTriePosition = newTriePosition;
|
2709
|
+
if (newTriePosition instanceof Leaf) {
|
2710
|
+
const target = event.target;
|
2711
|
+
let shouldFire = false;
|
2712
|
+
let elementToFire;
|
2713
|
+
const formField = isFormField(target);
|
2714
|
+
for (let i = newTriePosition.children.length - 1; i >= 0; i -= 1) {
|
2715
|
+
elementToFire = newTriePosition.children[i];
|
2716
|
+
const scope = elementToFire.getAttribute('data-hotkey-scope');
|
2717
|
+
if ((!formField && !scope) || (formField && target.id === scope)) {
|
2718
|
+
shouldFire = true;
|
2719
|
+
break;
|
2720
|
+
}
|
2721
|
+
}
|
2722
|
+
if (elementToFire && shouldFire) {
|
2723
|
+
fireDeterminedAction(elementToFire, sequenceTracker.path);
|
2724
|
+
event.preventDefault();
|
2725
|
+
}
|
2726
|
+
sequenceTracker.reset();
|
2727
|
+
}
|
2728
|
+
}
|
2729
|
+
function install(element, hotkey) {
|
2730
|
+
if (Object.keys(hotkeyRadixTrie.children).length === 0) {
|
2731
|
+
document.addEventListener('keydown', keyDownHandler);
|
2732
|
+
}
|
2733
|
+
const hotkeys = expandHotkeyToEdges(element.getAttribute('data-hotkey') || '');
|
2734
|
+
const leaves = hotkeys.map(h => hotkeyRadixTrie.insert(h).add(element));
|
2735
|
+
elementsLeaves.set(element, leaves);
|
2736
|
+
}
|
2737
|
+
function uninstall(element) {
|
2738
|
+
const leaves = elementsLeaves.get(element);
|
2739
|
+
if (leaves && leaves.length) {
|
2740
|
+
for (const leaf of leaves) {
|
2741
|
+
leaf && leaf.delete(element);
|
2742
|
+
}
|
2743
|
+
}
|
2744
|
+
if (Object.keys(hotkeyRadixTrie.children).length === 0) {
|
2745
|
+
document.removeEventListener('keydown', keyDownHandler);
|
2746
|
+
}
|
2747
|
+
}
|
2748
|
+
|
2326
2749
|
/* eslint-disable camelcase */
|
2327
2750
|
|
2328
2751
|
// upload code from Jeremy Smith's blog post
|
@@ -2349,7 +2772,19 @@ var MarksmithController = (function (stimulus) {
|
|
2349
2772
|
}
|
2350
2773
|
|
2351
2774
|
connect() {
|
2352
|
-
subscribe(this.
|
2775
|
+
subscribe(this.fieldElementTarget);
|
2776
|
+
|
2777
|
+
// Install all the hotkeys on the page
|
2778
|
+
for (const el of document.querySelectorAll('[data-hotkey]')) {
|
2779
|
+
install(el);
|
2780
|
+
}
|
2781
|
+
}
|
2782
|
+
|
2783
|
+
disconnect() {
|
2784
|
+
// Uninstall all the hotkeys on the page
|
2785
|
+
for (const el of document.querySelectorAll('[data-hotkey]')) {
|
2786
|
+
uninstall(el);
|
2787
|
+
}
|
2353
2788
|
}
|
2354
2789
|
|
2355
2790
|
switchToWrite(event) {
|
@@ -4,6 +4,7 @@ import { Controller } from '@hotwired/stimulus'
|
|
4
4
|
import { DirectUpload } from '@rails/activestorage'
|
5
5
|
import { post } from '@rails/request.js'
|
6
6
|
import { subscribe } from '@github/paste-markdown'
|
7
|
+
import {install, uninstall} from '@github/hotkey'
|
7
8
|
|
8
9
|
// upload code from Jeremy Smith's blog post
|
9
10
|
// https://hybrd.co/posts/github-issue-style-file-uploader-using-stimulus-and-active-storage
|
@@ -29,7 +30,19 @@ export default class extends Controller {
|
|
29
30
|
}
|
30
31
|
|
31
32
|
connect() {
|
32
|
-
subscribe(this.
|
33
|
+
subscribe(this.fieldElementTarget)
|
34
|
+
|
35
|
+
// Install all the hotkeys on the page
|
36
|
+
for (const el of document.querySelectorAll('[data-hotkey]')) {
|
37
|
+
install(el)
|
38
|
+
}
|
39
|
+
}
|
40
|
+
|
41
|
+
disconnect() {
|
42
|
+
// Uninstall all the hotkeys on the page
|
43
|
+
for (const el of document.querySelectorAll('[data-hotkey]')) {
|
44
|
+
uninstall(el)
|
45
|
+
}
|
33
46
|
}
|
34
47
|
|
35
48
|
switchToWrite(event) {
|
@@ -26,8 +26,14 @@ module Marksmith
|
|
26
26
|
)
|
27
27
|
end
|
28
28
|
|
29
|
-
def marksmith_toolbar_button(name, **kwargs)
|
30
|
-
content_tag "md-#{name}", marksmith_toolbar_svg(name),
|
29
|
+
def marksmith_toolbar_button(name, hotkey_scope: nil, hotkey: nil, **kwargs)
|
30
|
+
content_tag "md-#{name}", marksmith_toolbar_svg(name),
|
31
|
+
title: t("marksmith.#{name.to_s.gsub("-", "_")}").humanize,
|
32
|
+
class: marksmith_button_classes,
|
33
|
+
data: {
|
34
|
+
hotkey_scope:,
|
35
|
+
hotkey:
|
36
|
+
}
|
31
37
|
end
|
32
38
|
|
33
39
|
def marksmith_tab_classes
|
@@ -1,10 +1,10 @@
|
|
1
|
-
<%= tag.markdown_toolbar for:
|
1
|
+
<%= tag.markdown_toolbar for: textarea_id,
|
2
2
|
class: class_names("ms:flex ms:flex-wrap ms:px-2 ms:py-1", "ms:pointer-events-none": disabled),
|
3
3
|
data: { marksmith_target: "toolbar" } do
|
4
4
|
%>
|
5
|
-
<%= marksmith_toolbar_button "bold" %>
|
6
|
-
<%= marksmith_toolbar_button "header" %>
|
7
|
-
<%= marksmith_toolbar_button "italic" %>
|
5
|
+
<%= marksmith_toolbar_button "bold", hotkey: "Meta+b", hotkey_scope: textarea_id %>
|
6
|
+
<%= marksmith_toolbar_button "header", hotkey: "Meta+h", hotkey_scope: textarea_id %>
|
7
|
+
<%= marksmith_toolbar_button "italic", hotkey: "Meta+i", hotkey_scope: textarea_id %>
|
8
8
|
<%= marksmith_toolbar_button "quote" %>
|
9
9
|
<%= marksmith_toolbar_button "code" %>
|
10
10
|
<%= marksmith_toolbar_button "link" %>
|
@@ -15,9 +15,9 @@
|
|
15
15
|
marksmith_extra_preview_params_value: editor.extra_preview_params.as_json,
|
16
16
|
**editor.controller_data_attributes,
|
17
17
|
} do %>
|
18
|
-
<%= render partial: "marksmith/shared/toolbar", locals: {
|
18
|
+
<%= render partial: "marksmith/shared/toolbar", locals: { textarea_id: editor.textarea_id, disabled: editor.disabled} %>
|
19
19
|
<div class="ms:border-t ms:w-full ms:border-neutral-500 ms:flex ms:flex-1">
|
20
20
|
<%= render partial: "marksmith/shared/editor_pane", locals: { editor: } %>
|
21
|
-
<%= render partial: "marksmith/shared/preview_pane", locals: {
|
21
|
+
<%= render partial: "marksmith/shared/preview_pane", locals: { editor: } %>
|
22
22
|
</div>
|
23
23
|
<% end %>
|
@@ -1,6 +1,6 @@
|
|
1
1
|
<%= content_tag :div, class: "ms:flex ms:flex-1 ms:flex-col ms:size-full", data: { marksmith_target: "fieldContainer" } do %>
|
2
2
|
<%= text_area_tag editor.field_name, editor.value,
|
3
|
-
id: editor.
|
3
|
+
id: editor.textarea_id,
|
4
4
|
class: class_names(
|
5
5
|
"ms:flex ms:flex-1 ms:border-none ms:resize-none ms:focus:outline-none ms:font-mono ms:focus:ring-0 ms:leading-normal ms:p-2 ms:text-sm ms:field-sizing-content ms:min-h-60",
|
6
6
|
"ms:dark:bg-neutral-800 ms:dark:text-neutral-200",
|
@@ -1,6 +1,6 @@
|
|
1
1
|
<%= content_tag :div,
|
2
2
|
class: "ms:hidden ms:markdown-preview ms:size-full ms:flex-1 ms:flex ms:size-full ms:p-2 ms:overflow-auto ms:bg-white ms:dark:bg-neutral-800 ms:rounded-b-md",
|
3
|
-
id:
|
3
|
+
id: editor.preview_pane_id,
|
4
4
|
data: {
|
5
5
|
marksmith_target: "previewPane",
|
6
6
|
} do %>
|
data/lib/marksmith/version.rb
CHANGED