tiny-markdown-editor 0.1.30 → 0.1.32
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.
- package/README.md +15 -11
- package/dist/tiny-mde.js +1280 -808
- package/dist/tiny-mde.min.css +1 -1
- package/dist/tiny-mde.min.js +1 -1
- package/dist/tiny-mde.tiny.js +1 -1
- package/lib/TinyMDE.js +89 -54
- package/lib/TinyMDECommandBar.js +114 -101
- package/lib/grammar.js +0 -1
- package/lib/index.js +1 -1
- package/lib/svg/svg.js +4 -1
- package/lib/tiny.js +1 -1
- package/package.json +25 -25
package/lib/TinyMDE.js
CHANGED
|
@@ -44,8 +44,73 @@ class Editor {
|
|
|
44
44
|
if (this.textarea) {
|
|
45
45
|
this.textarea.style.display = "none";
|
|
46
46
|
}
|
|
47
|
+
this.undoStack = [];
|
|
48
|
+
this.redoStack = [];
|
|
49
|
+
this.isRestoringHistory = false;
|
|
50
|
+
this.maxHistory = 100;
|
|
47
51
|
this.createEditorElement(element, props);
|
|
48
52
|
this.setContent(typeof props.content === "string" ? props.content : this.textarea ? this.textarea.value : "# Hello TinyMDE!\nEdit **here**");
|
|
53
|
+
// Keyboard shortcuts for undo/redo
|
|
54
|
+
this.e.addEventListener("keydown", e => this.handleUndoRedoKey(e));
|
|
55
|
+
}
|
|
56
|
+
pushHistory() {
|
|
57
|
+
if (this.isRestoringHistory) return;
|
|
58
|
+
this.pushCurrentState();
|
|
59
|
+
this.redoStack = [];
|
|
60
|
+
}
|
|
61
|
+
pushCurrentState() {
|
|
62
|
+
this.undoStack.push({
|
|
63
|
+
content: this.getContent(),
|
|
64
|
+
selection: this.getSelection(),
|
|
65
|
+
anchor: this.getSelection(true)
|
|
66
|
+
});
|
|
67
|
+
if (this.undoStack.length > this.maxHistory) this.undoStack.shift();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Undoes the last action.
|
|
72
|
+
*/
|
|
73
|
+
undo() {
|
|
74
|
+
if (this.undoStack.length < 2) return; // Don't undo initial state
|
|
75
|
+
this.isRestoringHistory = true;
|
|
76
|
+
this.pushCurrentState();
|
|
77
|
+
const current = this.undoStack.pop();
|
|
78
|
+
this.redoStack.push(current);
|
|
79
|
+
const prev = this.undoStack[this.undoStack.length - 1];
|
|
80
|
+
this.setContent(prev.content);
|
|
81
|
+
if (prev.selection) this.setSelection(prev.selection, prev.anchor);
|
|
82
|
+
this.undoStack.pop();
|
|
83
|
+
this.isRestoringHistory = false;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Redoes the last undone action.
|
|
88
|
+
*/
|
|
89
|
+
redo() {
|
|
90
|
+
if (!this.redoStack.length) return;
|
|
91
|
+
this.isRestoringHistory = true;
|
|
92
|
+
this.pushCurrentState();
|
|
93
|
+
const next = this.redoStack.pop();
|
|
94
|
+
this.setContent(next.content);
|
|
95
|
+
if (next.selection) this.setSelection(next.selection, next.anchor);
|
|
96
|
+
this.isRestoringHistory = false;
|
|
97
|
+
}
|
|
98
|
+
handleUndoRedoKey(e) {
|
|
99
|
+
const isMac = /Mac|iPod|iPhone|iPad/.test(navigator.platform);
|
|
100
|
+
const ctrl = isMac ? e.metaKey : e.ctrlKey;
|
|
101
|
+
if (ctrl && !e.altKey) {
|
|
102
|
+
if (e.key === "z" || e.key === "Z") {
|
|
103
|
+
if (e.shiftKey) {
|
|
104
|
+
this.redo();
|
|
105
|
+
} else {
|
|
106
|
+
this.undo();
|
|
107
|
+
}
|
|
108
|
+
e.preventDefault();
|
|
109
|
+
} else if (e.key === "y" || e.key === "Y") {
|
|
110
|
+
this.redo();
|
|
111
|
+
e.preventDefault();
|
|
112
|
+
}
|
|
113
|
+
}
|
|
49
114
|
}
|
|
50
115
|
|
|
51
116
|
/**
|
|
@@ -98,6 +163,7 @@ class Editor {
|
|
|
98
163
|
this.lineTypes = new Array(this.lines.length);
|
|
99
164
|
this.updateFormatting();
|
|
100
165
|
this.fireChange();
|
|
166
|
+
if (!this.isRestoringHistory) this.pushHistory();
|
|
101
167
|
}
|
|
102
168
|
|
|
103
169
|
/**
|
|
@@ -162,7 +228,6 @@ class Editor {
|
|
|
162
228
|
this.lineElements[lineNum].removeAttribute("style");
|
|
163
229
|
this.lineElements[lineNum].innerHTML = contentHTML == "" ? "<br />" : contentHTML; // Prevent empty elements which can't be selected etc.
|
|
164
230
|
}
|
|
165
|
-
|
|
166
231
|
this.lineElements[lineNum].dataset.lineNum = lineNum;
|
|
167
232
|
}
|
|
168
233
|
}
|
|
@@ -170,7 +235,7 @@ class Editor {
|
|
|
170
235
|
/**
|
|
171
236
|
* Determines line types for all lines based on the line / block grammar. Captures the results of the respective line
|
|
172
237
|
* grammar regular expressions.
|
|
173
|
-
* Updates this.lineTypes, this.lineCaptures, and this.lineReplacements.
|
|
238
|
+
* Updates this.lineTypes, this.lineCaptures, and this.lineReplacements, as well as this.lineDirty.
|
|
174
239
|
*/
|
|
175
240
|
updateLineTypes() {
|
|
176
241
|
let codeBlockType = false;
|
|
@@ -467,7 +532,6 @@ class Editor {
|
|
|
467
532
|
linkDetails.push(""); // Empty end delimiter for destination
|
|
468
533
|
linkDetails.push(cap[0]); // Whitespace in between destination and title
|
|
469
534
|
}
|
|
470
|
-
|
|
471
535
|
break;
|
|
472
536
|
case 3:
|
|
473
537
|
linkDetails.push(cap[0]);
|
|
@@ -491,7 +555,6 @@ class Editor {
|
|
|
491
555
|
return false;
|
|
492
556
|
// We should never get here
|
|
493
557
|
}
|
|
494
|
-
|
|
495
558
|
currentOffset += cap[0].length;
|
|
496
559
|
continue inlineOuter;
|
|
497
560
|
}
|
|
@@ -528,7 +591,6 @@ class Editor {
|
|
|
528
591
|
return false;
|
|
529
592
|
// After link title was closed, without closing parenthesis
|
|
530
593
|
}
|
|
531
|
-
|
|
532
594
|
currentOffset += cap[0].length;
|
|
533
595
|
continue inlineOuter;
|
|
534
596
|
}
|
|
@@ -602,7 +664,6 @@ class Editor {
|
|
|
602
664
|
return false;
|
|
603
665
|
// After link title was closed, without closing parenthesis
|
|
604
666
|
}
|
|
605
|
-
|
|
606
667
|
currentOffset++;
|
|
607
668
|
continue inlineOuter;
|
|
608
669
|
}
|
|
@@ -629,7 +690,6 @@ class Editor {
|
|
|
629
690
|
} else {
|
|
630
691
|
parenthesisLevel--; // This should decrease it from 1 to 0...
|
|
631
692
|
}
|
|
632
|
-
|
|
633
693
|
if (parenthesisLevel == 0) {
|
|
634
694
|
// No invalid condition, let's make sure the linkDetails array is complete
|
|
635
695
|
while (linkDetails.length < 7) linkDetails.push("");
|
|
@@ -670,16 +730,13 @@ class Editor {
|
|
|
670
730
|
return false;
|
|
671
731
|
// After link title was closed, without closing parenthesis
|
|
672
732
|
}
|
|
673
|
-
|
|
674
733
|
currentOffset += cap[0].length;
|
|
675
734
|
continue inlineOuter;
|
|
676
735
|
}
|
|
677
736
|
throw "Infinite loop"; // we should never get here since the last test matches any character
|
|
678
737
|
}
|
|
679
|
-
|
|
680
738
|
if (parenthesisLevel > 0) return false; // Parenthes(es) not closed
|
|
681
739
|
}
|
|
682
|
-
|
|
683
740
|
if (linkRef !== false) {
|
|
684
741
|
// Ref link; check that linkRef is valid
|
|
685
742
|
let valid = false;
|
|
@@ -876,7 +933,6 @@ class Editor {
|
|
|
876
933
|
});
|
|
877
934
|
processed = ""; // Current formatted output has been pushed on the stack and will be prepended when the stack gets popped
|
|
878
935
|
}
|
|
879
|
-
|
|
880
936
|
offset += cap[0].length;
|
|
881
937
|
string = string.substr(cap[0].length);
|
|
882
938
|
continue outer;
|
|
@@ -926,13 +982,13 @@ class Editor {
|
|
|
926
982
|
let firstChangedLine = 0;
|
|
927
983
|
while (firstChangedLine <= this.lines.length && firstChangedLine <= this.lineElements.length && this.lineElements[firstChangedLine] &&
|
|
928
984
|
// Check that the line element hasn't been deleted
|
|
929
|
-
this.lines[firstChangedLine] == this.lineElements[firstChangedLine].textContent) {
|
|
985
|
+
this.lines[firstChangedLine] == this.lineElements[firstChangedLine].textContent && this.lineTypes[firstChangedLine] == this.lineElements[firstChangedLine].className) {
|
|
930
986
|
firstChangedLine++;
|
|
931
987
|
}
|
|
932
988
|
|
|
933
989
|
// End also from the end
|
|
934
990
|
let lastChangedLine = -1;
|
|
935
|
-
while (-lastChangedLine < this.lines.length && -lastChangedLine < this.lineElements.length && this.lines[this.lines.length + lastChangedLine] == this.lineElements[this.lineElements.length + lastChangedLine].textContent) {
|
|
991
|
+
while (-lastChangedLine < this.lines.length && -lastChangedLine < this.lineElements.length && this.lines[this.lines.length + lastChangedLine] == this.lineElements[this.lineElements.length + lastChangedLine].textContent && this.lineTypes[this.lines.length + lastChangedLine] == this.lineElements[this.lineElements.length + lastChangedLine].className) {
|
|
936
992
|
lastChangedLine--;
|
|
937
993
|
}
|
|
938
994
|
let linesToDelete = this.lines.length + lastChangedLine + 1 - firstChangedLine;
|
|
@@ -948,9 +1004,10 @@ class Editor {
|
|
|
948
1004
|
for (let line = 0; line < this.lineElements.length; line++) {
|
|
949
1005
|
let e = this.lineElements[line];
|
|
950
1006
|
let ct = e.textContent;
|
|
951
|
-
if (this.lines[line] !== ct) {
|
|
1007
|
+
if (this.lines[line] !== ct || this.lineTypes[line] !== e.className) {
|
|
952
1008
|
// Line changed, update it
|
|
953
1009
|
this.lines[line] = ct;
|
|
1010
|
+
this.lineTypes[line] = e.className;
|
|
954
1011
|
this.lineDirty[line] = true;
|
|
955
1012
|
}
|
|
956
1013
|
}
|
|
@@ -982,14 +1039,12 @@ class Editor {
|
|
|
982
1039
|
break;
|
|
983
1040
|
}
|
|
984
1041
|
let lines = this.lines[sel.row].replace(/\n\n$/, "\n").split(/(?:\r\n|\n|\r)/);
|
|
985
|
-
if (lines.length
|
|
986
|
-
//
|
|
987
|
-
this.
|
|
988
|
-
|
|
1042
|
+
if (lines.length > 1) {
|
|
1043
|
+
// New line
|
|
1044
|
+
this.spliceLines(sel.row, 1, lines, true);
|
|
1045
|
+
sel.row++;
|
|
1046
|
+
sel.col = 0;
|
|
989
1047
|
}
|
|
990
|
-
this.spliceLines(sel.row, 1, lines, true);
|
|
991
|
-
sel.row++;
|
|
992
|
-
sel.col = 0;
|
|
993
1048
|
if (continuableType) {
|
|
994
1049
|
// Check if the previous line was non-empty
|
|
995
1050
|
let capture = _grammar.lineGrammar[continuableType].regexp.exec(this.lines[sel.row - 1]);
|
|
@@ -1017,32 +1072,6 @@ class Editor {
|
|
|
1017
1072
|
this.updateFormatting();
|
|
1018
1073
|
}
|
|
1019
1074
|
|
|
1020
|
-
// /**
|
|
1021
|
-
// * Processes a "delete" input action.
|
|
1022
|
-
// * @param {object} focus The selection
|
|
1023
|
-
// * @param {boolean} forward If true, performs a forward delete, otherwise performs a backward delete
|
|
1024
|
-
// */
|
|
1025
|
-
// processDelete(focus, forward) {
|
|
1026
|
-
// if (!focus) return;
|
|
1027
|
-
// let anchor = this.getSelection(true);
|
|
1028
|
-
// // Do we have a non-empty selection?
|
|
1029
|
-
// if (focus.col != anchor.col || focus.row != anchor.row) {
|
|
1030
|
-
// // non-empty. direction doesn't matter.
|
|
1031
|
-
// this.paste('', anchor, focus);
|
|
1032
|
-
// } else {
|
|
1033
|
-
// if (forward) {
|
|
1034
|
-
// if (focus.col < this.lines[focus.row].length) this.paste('', {row: focus.row, col: focus.col + 1}, focus);
|
|
1035
|
-
// else if (focus.col < this.lines.length) this.paste('', {row: focus.row + 1, col: 0}, focus);
|
|
1036
|
-
// // Otherwise, we're at the very end and can't delete forward
|
|
1037
|
-
// } else {
|
|
1038
|
-
// if (focus.col > 0) this.paste('', {row: focus.row, col: focus.col - 1}, focus);
|
|
1039
|
-
// else if (focus.row > 0) this.paste('', {row: focus.row - 1, col: this.lines[focus.row - 1].length - 1}, focus);
|
|
1040
|
-
// // Otherwise, we're at the very beginning and can't delete backwards
|
|
1041
|
-
// }
|
|
1042
|
-
// }
|
|
1043
|
-
|
|
1044
|
-
// }
|
|
1045
|
-
|
|
1046
1075
|
/**
|
|
1047
1076
|
* Gets the current position of the selection counted by row and column of the editor Markdown content (as opposed to the position in the DOM).
|
|
1048
1077
|
*
|
|
@@ -1201,7 +1230,6 @@ class Editor {
|
|
|
1201
1230
|
setSelection(focus) {
|
|
1202
1231
|
let anchor = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
|
|
1203
1232
|
if (!focus) return;
|
|
1204
|
-
let range = document.createRange();
|
|
1205
1233
|
let {
|
|
1206
1234
|
node: focusNode,
|
|
1207
1235
|
offset: focusOffset
|
|
@@ -1216,17 +1244,15 @@ class Editor {
|
|
|
1216
1244
|
anchorNode = node;
|
|
1217
1245
|
anchorOffset = offset;
|
|
1218
1246
|
}
|
|
1219
|
-
if (anchorNode) range.setStart(anchorNode, anchorOffset);else range.setStart(focusNode, focusOffset);
|
|
1220
|
-
range.setEnd(focusNode, focusOffset);
|
|
1221
1247
|
let windowSelection = window.getSelection();
|
|
1222
|
-
windowSelection.
|
|
1223
|
-
windowSelection.addRange(range);
|
|
1248
|
+
windowSelection.setBaseAndExtent(focusNode, focusOffset, anchorNode || focusNode, anchorNode ? anchorOffset : focusOffset);
|
|
1224
1249
|
}
|
|
1225
1250
|
|
|
1226
1251
|
/**
|
|
1227
1252
|
* Event handler for input events
|
|
1228
1253
|
*/
|
|
1229
1254
|
handleInputEvent(event) {
|
|
1255
|
+
if (!this.isRestoringHistory) this.pushHistory();
|
|
1230
1256
|
// For composition input, we are only updating the text after we have received
|
|
1231
1257
|
// a compositionend event, so we return upon insertCompositionText.
|
|
1232
1258
|
// Otherwise, the DOM changes break the text input.
|
|
@@ -1334,6 +1360,7 @@ class Editor {
|
|
|
1334
1360
|
* Event handler for the "paste" event
|
|
1335
1361
|
*/
|
|
1336
1362
|
handlePaste(event) {
|
|
1363
|
+
if (!this.isRestoringHistory) this.pushHistory();
|
|
1337
1364
|
event.preventDefault();
|
|
1338
1365
|
|
|
1339
1366
|
// get text representation of clipboard
|
|
@@ -1359,7 +1386,6 @@ class Editor {
|
|
|
1359
1386
|
col: this.lines[this.lines.length - 1].length
|
|
1360
1387
|
}; // Insert at end
|
|
1361
1388
|
}
|
|
1362
|
-
|
|
1363
1389
|
if (!anchor) {
|
|
1364
1390
|
anchor = focus;
|
|
1365
1391
|
}
|
|
@@ -1464,7 +1490,6 @@ class Editor {
|
|
|
1464
1490
|
end.row--;
|
|
1465
1491
|
end.col = this.lines[end.row].length; // Selection to beginning of next line is said to end at the beginning of the last line
|
|
1466
1492
|
}
|
|
1467
|
-
|
|
1468
1493
|
for (let cmd in _grammar.commands) {
|
|
1469
1494
|
if (_grammar.commands[cmd].type == "inline") {
|
|
1470
1495
|
if (!focus || focus.row != anchor.row || !this.isInlineFormattingAllowed(focus, anchor)) {
|
|
@@ -1500,6 +1525,7 @@ class Editor {
|
|
|
1500
1525
|
* @param {boolean} state
|
|
1501
1526
|
*/
|
|
1502
1527
|
setCommandState(command, state) {
|
|
1528
|
+
if (!this.isRestoringHistory) this.pushHistory();
|
|
1503
1529
|
if (_grammar.commands[command].type == "inline") {
|
|
1504
1530
|
let anchor = this.getSelection(true);
|
|
1505
1531
|
let focus = this.getSelection(false);
|
|
@@ -1635,6 +1661,7 @@ class Editor {
|
|
|
1635
1661
|
wrapSelection(pre, post) {
|
|
1636
1662
|
let focus = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
|
|
1637
1663
|
let anchor = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;
|
|
1664
|
+
if (!this.isRestoringHistory) this.pushHistory();
|
|
1638
1665
|
if (!focus) focus = this.getSelection(false);
|
|
1639
1666
|
if (!anchor) anchor = this.getSelection(true);
|
|
1640
1667
|
if (!focus || !anchor || focus.row != anchor.row) return;
|
|
@@ -1725,5 +1752,13 @@ class Editor {
|
|
|
1725
1752
|
this.listeners.drop.push(listener);
|
|
1726
1753
|
}
|
|
1727
1754
|
}
|
|
1755
|
+
|
|
1756
|
+
// Optionally, expose canUndo/canRedo
|
|
1757
|
+
get canUndo() {
|
|
1758
|
+
return this.undoStack.length > 1;
|
|
1759
|
+
}
|
|
1760
|
+
get canRedo() {
|
|
1761
|
+
return this.redoStack.length > 0;
|
|
1762
|
+
}
|
|
1728
1763
|
}
|
|
1729
1764
|
var _default = exports.default = Editor;
|
package/lib/TinyMDECommandBar.js
CHANGED
|
@@ -5,96 +5,110 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.default = void 0;
|
|
7
7
|
var _svg = _interopRequireDefault(require("./svg/svg"));
|
|
8
|
-
function _interopRequireDefault(
|
|
8
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
9
9
|
const isMacLike = /(Mac|iPhone|iPod|iPad)/i.test(typeof navigator !== "undefined" ? navigator.platform : "");
|
|
10
10
|
const DefaultCommands = {
|
|
11
|
-
|
|
12
|
-
name:
|
|
13
|
-
action:
|
|
11
|
+
bold: {
|
|
12
|
+
name: "bold",
|
|
13
|
+
action: "bold",
|
|
14
14
|
innerHTML: _svg.default.bold,
|
|
15
|
-
title:
|
|
16
|
-
hotkey:
|
|
15
|
+
title: "Bold",
|
|
16
|
+
hotkey: "Mod-B"
|
|
17
17
|
},
|
|
18
|
-
|
|
19
|
-
name:
|
|
20
|
-
action:
|
|
18
|
+
italic: {
|
|
19
|
+
name: "italic",
|
|
20
|
+
action: "italic",
|
|
21
21
|
innerHTML: _svg.default.italic,
|
|
22
|
-
title:
|
|
23
|
-
hotkey:
|
|
22
|
+
title: "Italic",
|
|
23
|
+
hotkey: "Mod-I"
|
|
24
24
|
},
|
|
25
|
-
|
|
26
|
-
name:
|
|
27
|
-
action:
|
|
25
|
+
strikethrough: {
|
|
26
|
+
name: "strikethrough",
|
|
27
|
+
action: "strikethrough",
|
|
28
28
|
innerHTML: _svg.default.strikethrough,
|
|
29
|
-
title:
|
|
30
|
-
hotkey:
|
|
29
|
+
title: "Strikethrough",
|
|
30
|
+
hotkey: "Mod2-Shift-5"
|
|
31
31
|
},
|
|
32
|
-
|
|
33
|
-
name:
|
|
34
|
-
action:
|
|
32
|
+
code: {
|
|
33
|
+
name: "code",
|
|
34
|
+
action: "code",
|
|
35
35
|
innerHTML: _svg.default.code,
|
|
36
|
-
title:
|
|
36
|
+
title: "Format as code"
|
|
37
37
|
},
|
|
38
|
-
|
|
39
|
-
name:
|
|
40
|
-
action:
|
|
38
|
+
h1: {
|
|
39
|
+
name: "h1",
|
|
40
|
+
action: "h1",
|
|
41
41
|
innerHTML: _svg.default.h1,
|
|
42
|
-
title:
|
|
43
|
-
hotkey:
|
|
42
|
+
title: "Level 1 heading",
|
|
43
|
+
hotkey: "Mod-Shift-1"
|
|
44
44
|
},
|
|
45
|
-
|
|
46
|
-
name:
|
|
47
|
-
action:
|
|
45
|
+
h2: {
|
|
46
|
+
name: "h2",
|
|
47
|
+
action: "h2",
|
|
48
48
|
innerHTML: _svg.default.h2,
|
|
49
|
-
title:
|
|
50
|
-
hotkey:
|
|
49
|
+
title: "Level 2 heading",
|
|
50
|
+
hotkey: "Mod-Shift-2"
|
|
51
51
|
},
|
|
52
|
-
|
|
53
|
-
name:
|
|
54
|
-
action:
|
|
52
|
+
ul: {
|
|
53
|
+
name: "ul",
|
|
54
|
+
action: "ul",
|
|
55
55
|
innerHTML: _svg.default.ul,
|
|
56
|
-
title:
|
|
56
|
+
title: "Bulleted list"
|
|
57
57
|
},
|
|
58
|
-
|
|
59
|
-
name:
|
|
60
|
-
action:
|
|
58
|
+
ol: {
|
|
59
|
+
name: "ol",
|
|
60
|
+
action: "ol",
|
|
61
61
|
innerHTML: _svg.default.ol,
|
|
62
|
-
title:
|
|
62
|
+
title: "Numbered list"
|
|
63
63
|
},
|
|
64
|
-
|
|
65
|
-
name:
|
|
66
|
-
action:
|
|
64
|
+
blockquote: {
|
|
65
|
+
name: "blockquote",
|
|
66
|
+
action: "blockquote",
|
|
67
67
|
innerHTML: _svg.default.blockquote,
|
|
68
|
-
title:
|
|
69
|
-
hotkey:
|
|
68
|
+
title: "Quote",
|
|
69
|
+
hotkey: "Mod2-Shift-Q"
|
|
70
70
|
},
|
|
71
|
-
|
|
72
|
-
name:
|
|
71
|
+
insertLink: {
|
|
72
|
+
name: "insertLink",
|
|
73
73
|
action: editor => {
|
|
74
|
-
if (editor.isInlineFormattingAllowed()) editor.wrapSelection(
|
|
74
|
+
if (editor.isInlineFormattingAllowed()) editor.wrapSelection("[", "]()");
|
|
75
75
|
},
|
|
76
76
|
enabled: (editor, focus, anchor) => editor.isInlineFormattingAllowed(focus, anchor) ? false : null,
|
|
77
77
|
innerHTML: _svg.default.link,
|
|
78
|
-
title:
|
|
79
|
-
hotkey:
|
|
78
|
+
title: "Insert link",
|
|
79
|
+
hotkey: "Mod-K"
|
|
80
80
|
},
|
|
81
|
-
|
|
82
|
-
name:
|
|
81
|
+
insertImage: {
|
|
82
|
+
name: "insertImage",
|
|
83
83
|
action: editor => {
|
|
84
|
-
if (editor.isInlineFormattingAllowed()) editor.wrapSelection(
|
|
84
|
+
if (editor.isInlineFormattingAllowed()) editor.wrapSelection("![", "]()");
|
|
85
85
|
},
|
|
86
86
|
enabled: (editor, focus, anchor) => editor.isInlineFormattingAllowed(focus, anchor) ? false : null,
|
|
87
87
|
innerHTML: _svg.default.image,
|
|
88
|
-
title:
|
|
89
|
-
hotkey:
|
|
88
|
+
title: "Insert image",
|
|
89
|
+
hotkey: "Mod2-Shift-I"
|
|
90
90
|
},
|
|
91
|
-
|
|
92
|
-
name:
|
|
93
|
-
action: editor => editor.paste(
|
|
91
|
+
hr: {
|
|
92
|
+
name: "hr",
|
|
93
|
+
action: editor => editor.paste("\n***\n"),
|
|
94
94
|
enabled: () => false,
|
|
95
95
|
innerHTML: _svg.default.hr,
|
|
96
|
-
title:
|
|
97
|
-
hotkey:
|
|
96
|
+
title: "Insert horizontal line",
|
|
97
|
+
hotkey: "Mod2-Shift-L"
|
|
98
|
+
},
|
|
99
|
+
undo: {
|
|
100
|
+
name: "undo",
|
|
101
|
+
action: editor => editor.undo(),
|
|
102
|
+
enabled: editor => editor.canUndo ? false : null,
|
|
103
|
+
innerHTML: _svg.default.undo,
|
|
104
|
+
title: "Undo"
|
|
105
|
+
},
|
|
106
|
+
redo: {
|
|
107
|
+
name: "redo",
|
|
108
|
+
action: editor => editor.redo(),
|
|
109
|
+
enabled: editor => editor.canRedo ? false : null,
|
|
110
|
+
innerHTML: _svg.default.redo,
|
|
111
|
+
title: "Redo"
|
|
98
112
|
}
|
|
99
113
|
};
|
|
100
114
|
class CommandBar {
|
|
@@ -112,17 +126,17 @@ class CommandBar {
|
|
|
112
126
|
if (!element) {
|
|
113
127
|
element = document.body;
|
|
114
128
|
}
|
|
115
|
-
this.createCommandBarElement(element, props.commands || [
|
|
116
|
-
document.addEventListener(
|
|
129
|
+
this.createCommandBarElement(element, props.commands || ["bold", "italic", "strikethrough", "|", "code", "|", "h1", "h2", "|", "ul", "ol", "|", "blockquote", "hr", "|", "undo", "redo", "|", "insertLink", "insertImage"]);
|
|
130
|
+
document.addEventListener("keydown", e => this.handleKeydown(e));
|
|
117
131
|
if (props.editor) this.setEditor(props.editor);
|
|
118
132
|
}
|
|
119
133
|
createCommandBarElement(parentElement, commands) {
|
|
120
|
-
this.e = document.createElement(
|
|
121
|
-
this.e.className =
|
|
134
|
+
this.e = document.createElement("div");
|
|
135
|
+
this.e.className = "TMCommandBar";
|
|
122
136
|
for (let command of commands) {
|
|
123
|
-
if (command ==
|
|
124
|
-
let el = document.createElement(
|
|
125
|
-
el.className =
|
|
137
|
+
if (command == "|") {
|
|
138
|
+
let el = document.createElement("div");
|
|
139
|
+
el.className = "TMCommandDivider";
|
|
126
140
|
this.e.appendChild(el);
|
|
127
141
|
} else {
|
|
128
142
|
let commandName;
|
|
@@ -145,54 +159,53 @@ class CommandBar {
|
|
|
145
159
|
}
|
|
146
160
|
let title = this.commands[commandName].title || commandName;
|
|
147
161
|
if (this.commands[commandName].hotkey) {
|
|
148
|
-
const keys = this.commands[commandName].hotkey.split(
|
|
162
|
+
const keys = this.commands[commandName].hotkey.split("-");
|
|
149
163
|
// construct modifiers
|
|
150
164
|
let modifiers = [];
|
|
151
165
|
let modifierexplanation = [];
|
|
152
166
|
for (let i = 0; i < keys.length - 1; i++) {
|
|
153
167
|
switch (keys[i]) {
|
|
154
|
-
case
|
|
155
|
-
modifiers.push(
|
|
156
|
-
modifierexplanation.push(
|
|
168
|
+
case "Ctrl":
|
|
169
|
+
modifiers.push("ctrlKey");
|
|
170
|
+
modifierexplanation.push("Ctrl");
|
|
157
171
|
break;
|
|
158
|
-
case
|
|
159
|
-
modifiers.push(
|
|
160
|
-
modifierexplanation.push(
|
|
172
|
+
case "Cmd":
|
|
173
|
+
modifiers.push("metaKey");
|
|
174
|
+
modifierexplanation.push("⌘");
|
|
161
175
|
break;
|
|
162
|
-
case
|
|
163
|
-
modifiers.push(
|
|
164
|
-
modifierexplanation.push(
|
|
176
|
+
case "Alt":
|
|
177
|
+
modifiers.push("altKey");
|
|
178
|
+
modifierexplanation.push("Alt");
|
|
165
179
|
break;
|
|
166
|
-
case
|
|
167
|
-
modifiers.push(
|
|
168
|
-
modifierexplanation.push(
|
|
180
|
+
case "Option":
|
|
181
|
+
modifiers.push("altKey");
|
|
182
|
+
modifierexplanation.push("⌥");
|
|
169
183
|
break;
|
|
170
|
-
case
|
|
171
|
-
modifiers.push(
|
|
172
|
-
modifierexplanation.push(
|
|
184
|
+
case "Win":
|
|
185
|
+
modifiers.push("metaKey");
|
|
186
|
+
modifierexplanation.push("⊞ Win");
|
|
173
187
|
break;
|
|
174
|
-
case
|
|
175
|
-
modifiers.push(
|
|
176
|
-
modifierexplanation.push(
|
|
188
|
+
case "Shift":
|
|
189
|
+
modifiers.push("shiftKey");
|
|
190
|
+
modifierexplanation.push("⇧");
|
|
177
191
|
break;
|
|
178
|
-
case
|
|
192
|
+
case "Mod":
|
|
179
193
|
// Mod is a convenience mechanism: Ctrl on Windows, Cmd on Mac
|
|
180
194
|
if (isMacLike) {
|
|
181
|
-
modifiers.push(
|
|
182
|
-
modifierexplanation.push(
|
|
195
|
+
modifiers.push("metaKey");
|
|
196
|
+
modifierexplanation.push("⌘");
|
|
183
197
|
} else {
|
|
184
|
-
modifiers.push(
|
|
185
|
-
modifierexplanation.push(
|
|
198
|
+
modifiers.push("ctrlKey");
|
|
199
|
+
modifierexplanation.push("Ctrl");
|
|
186
200
|
}
|
|
187
201
|
break;
|
|
188
|
-
case
|
|
189
|
-
modifiers.push(
|
|
190
|
-
if (isMacLike) modifierexplanation.push(
|
|
202
|
+
case "Mod2":
|
|
203
|
+
modifiers.push("altKey");
|
|
204
|
+
if (isMacLike) modifierexplanation.push("⌥");else modifierexplanation.push("Alt");
|
|
191
205
|
break;
|
|
192
206
|
// Mod2 is a convenience mechanism: Alt on Windows, Option on Mac
|
|
193
207
|
}
|
|
194
208
|
}
|
|
195
|
-
|
|
196
209
|
modifierexplanation.push(keys[keys.length - 1]);
|
|
197
210
|
let hotkey = {
|
|
198
211
|
modifiers: modifiers,
|
|
@@ -205,13 +218,13 @@ class CommandBar {
|
|
|
205
218
|
hotkey.key = keys[keys.length - 1].toLowerCase();
|
|
206
219
|
}
|
|
207
220
|
this.hotkeys.push(hotkey);
|
|
208
|
-
title = title.concat(` (${modifierexplanation.join(
|
|
221
|
+
title = title.concat(` (${modifierexplanation.join("+")})`);
|
|
209
222
|
}
|
|
210
|
-
this.buttons[commandName] = document.createElement(
|
|
211
|
-
this.buttons[commandName].className =
|
|
223
|
+
this.buttons[commandName] = document.createElement("div");
|
|
224
|
+
this.buttons[commandName].className = "TMCommandButton TMCommandButton_Disabled";
|
|
212
225
|
this.buttons[commandName].title = title;
|
|
213
226
|
this.buttons[commandName].innerHTML = this.commands[commandName].innerHTML;
|
|
214
|
-
this.buttons[commandName].addEventListener(
|
|
227
|
+
this.buttons[commandName].addEventListener("mousedown", e => this.handleClick(commandName, e));
|
|
215
228
|
this.e.appendChild(this.buttons[commandName]);
|
|
216
229
|
}
|
|
217
230
|
}
|
|
@@ -228,7 +241,7 @@ class CommandBar {
|
|
|
228
241
|
}
|
|
229
242
|
setEditor(editor) {
|
|
230
243
|
this.editor = editor;
|
|
231
|
-
editor.addEventListener(
|
|
244
|
+
editor.addEventListener("selection", e => this.handleSelection(e));
|
|
232
245
|
}
|
|
233
246
|
handleSelection(event) {
|
|
234
247
|
if (event.commandState) {
|
|
@@ -239,11 +252,11 @@ class CommandBar {
|
|
|
239
252
|
this.state[command] = event.commandState[command];
|
|
240
253
|
}
|
|
241
254
|
if (this.state[command] === true) {
|
|
242
|
-
this.buttons[command].className =
|
|
255
|
+
this.buttons[command].className = "TMCommandButton TMCommandButton_Active";
|
|
243
256
|
} else if (this.state[command] === false) {
|
|
244
|
-
this.buttons[command].className =
|
|
257
|
+
this.buttons[command].className = "TMCommandButton TMCommandButton_Inactive";
|
|
245
258
|
} else {
|
|
246
|
-
this.buttons[command].className =
|
|
259
|
+
this.buttons[command].className = "TMCommandButton TMCommandButton_Disabled";
|
|
247
260
|
}
|
|
248
261
|
}
|
|
249
262
|
}
|
package/lib/grammar.js
CHANGED
package/lib/index.js
CHANGED
|
@@ -17,4 +17,4 @@ Object.defineProperty(exports, "Editor", {
|
|
|
17
17
|
});
|
|
18
18
|
var _TinyMDECommandBar = _interopRequireDefault(require("./TinyMDECommandBar"));
|
|
19
19
|
var _TinyMDE = _interopRequireDefault(require("./TinyMDE"));
|
|
20
|
-
function _interopRequireDefault(
|
|
20
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|