tiny-markdown-editor 0.1.8 → 0.1.10

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/globals.d.ts ADDED
@@ -0,0 +1,172 @@
1
+ declare module "tiny-markdown-editor" {
2
+ type CursorPosition = {
3
+ col: number;
4
+ row: number;
5
+ node?: HTMLElement;
6
+ };
7
+
8
+ type CommonCommandName =
9
+ | "bold"
10
+ | "italic"
11
+ | "strikethrough"
12
+ | "code"
13
+ | "h1"
14
+ | "h2"
15
+ | "ul"
16
+ | "ol"
17
+ | "blockquote";
18
+
19
+ type CommandBarCommandName =
20
+ | CommonCommandName
21
+ | "hr"
22
+ | "insertLink"
23
+ | "insertImage";
24
+
25
+ type EditorCommandName = CommonCommandName | "h3" | "h4" | "h5" | "h6";
26
+
27
+ type ChangeEvent = {
28
+ content: string;
29
+ linesDirty: boolean[];
30
+ };
31
+
32
+ type SelectionEvent = {
33
+ focus: CursorPosition;
34
+ anchor: CursorPosition;
35
+ commandState: Record<EditorCommandName, boolean>;
36
+ };
37
+
38
+ type Listener<T extends "change" | "selection"> = (
39
+ event: T extends "change" ? ChangeEvent : SelectionEvent
40
+ ) => void;
41
+
42
+ type EditorParams = {
43
+ element?: string | HTMLElement;
44
+ content?: string;
45
+ textarea?: string | HTMLElement;
46
+ };
47
+
48
+ export class Editor {
49
+ constructor(params: EditorParams?);
50
+
51
+ public getContent(): string;
52
+
53
+ public getSelection(getAnchor: boolean): CursorPosition | null;
54
+
55
+ public setSelection(
56
+ focus: CursorPosition,
57
+ anchor?: CursorPosition | null
58
+ ): void;
59
+
60
+ public paste(
61
+ text: string,
62
+ anchor: CursorPosition,
63
+ focus: CursorPosition
64
+ ): void;
65
+
66
+ public wrapSelection(
67
+ pre: string,
68
+ post: string,
69
+ anchor?: CursorPosition | null,
70
+ focus?: CursorPosition | null
71
+ ): void;
72
+
73
+ public addEventListener<T extends "change" | "selection">(
74
+ type: T,
75
+ listener: Listener<T>
76
+ ): void;
77
+ }
78
+
79
+ type ModifierKey =
80
+ | "Ctrl"
81
+ | "Cmd"
82
+ | "Alt"
83
+ | "Option"
84
+ | "Win"
85
+ | "Shift"
86
+ | "Mod"
87
+ | "Mod2";
88
+ type AlphanumericKey =
89
+ | "A"
90
+ | "B"
91
+ | "C"
92
+ | "D"
93
+ | "E"
94
+ | "F"
95
+ | "G"
96
+ | "H"
97
+ | "I"
98
+ | "J"
99
+ | "K"
100
+ | "L"
101
+ | "M"
102
+ | "N"
103
+ | "O"
104
+ | "P"
105
+ | "Q"
106
+ | "R"
107
+ | "S"
108
+ | "T"
109
+ | "U"
110
+ | "V"
111
+ | "W"
112
+ | "X"
113
+ | "Y"
114
+ | "Z"
115
+ | "a"
116
+ | "b"
117
+ | "c"
118
+ | "d"
119
+ | "e"
120
+ | "f"
121
+ | "g"
122
+ | "h"
123
+ | "i"
124
+ | "j"
125
+ | "k"
126
+ | "l"
127
+ | "m"
128
+ | "n"
129
+ | "o"
130
+ | "p"
131
+ | "q"
132
+ | "r"
133
+ | "s"
134
+ | "t"
135
+ | "u"
136
+ | "v"
137
+ | "w"
138
+ | "x"
139
+ | "y"
140
+ | "z"
141
+ | "0"
142
+ | "1"
143
+ | "2"
144
+ | "3"
145
+ | "4"
146
+ | "5"
147
+ | "6"
148
+ | "7"
149
+ | "8"
150
+ | "9";
151
+ type HotKey = AlphanumericKey | `${ModifierKey}-${HotKey}`;
152
+
153
+ type CommandBarCommand =
154
+ | CommandBarCommandName
155
+ | "|"
156
+ | {
157
+ name: CommandBarCommandName | string;
158
+ title?: string | undefined;
159
+ innerHTML?: string;
160
+ action?: (editor: Editor) => void;
161
+ hotkey?: HotKey;
162
+ };
163
+
164
+ type CommandBarParams = {
165
+ element?: string | HTMLElement;
166
+ editor: Editor;
167
+ commands?: CommandBarCommand[];
168
+ };
169
+ export class CommandBar {
170
+ constructor(params: CommandBarParams);
171
+ }
172
+ }
package/lib/TinyMDE.js CHANGED
@@ -1,10 +1,4 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.default = void 0;
7
- var _grammar = require("./grammar");
1
+ import { inlineGrammar, lineGrammar, punctuationLeading, punctuationTrailing, htmlescape, htmlBlockGrammar, commands } from "./grammar";
8
2
  class Editor {
9
3
  constructor() {
10
4
  let props = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
@@ -121,7 +115,7 @@ class Editor {
121
115
  this.linkLabels = [];
122
116
  for (let l = 0; l < this.lines.length; l++) {
123
117
  if (this.lineTypes[l] == 'TMLinkReferenceDefinition') {
124
- this.linkLabels.push(this.lineCaptures[l][_grammar.lineGrammar.TMLinkReferenceDefinition.labelPlaceholder]);
118
+ this.linkLabels.push(this.lineCaptures[l][lineGrammar.TMLinkReferenceDefinition.labelPlaceholder]);
125
119
  }
126
120
  }
127
121
  }
@@ -138,7 +132,7 @@ class Editor {
138
132
  */
139
133
  replace(replacement, capture) {
140
134
  return replacement.replace(/(\${1,2})([0-9])/g, (str, p1, p2) => {
141
- if (p1 == '$') return (0, _grammar.htmlescape)(capture[p2]);else return `<span class="TMInlineFormatted">${this.processInlineStyles(capture[p2])}</span>`;
135
+ if (p1 == '$') return htmlescape(capture[p2]);else return `<span class="TMInlineFormatted">${this.processInlineStyles(capture[p2])}</span>`;
142
136
  });
143
137
  }
144
138
 
@@ -178,10 +172,10 @@ class Editor {
178
172
  // if (lineNum > 0 && (this.lineTypes[lineNum - 1] == 'TMCodeFenceBacktickOpen' || this.lineTypes[lineNum - 1] == 'TMFencedCodeBacktick')) {
179
173
  if (codeBlockType == 'TMCodeFenceBacktickOpen') {
180
174
  // We're in a backtick-fenced code block, check if the current line closes it
181
- let capture = _grammar.lineGrammar.TMCodeFenceBacktickClose.regexp.exec(this.lines[lineNum]);
175
+ let capture = lineGrammar.TMCodeFenceBacktickClose.regexp.exec(this.lines[lineNum]);
182
176
  if (capture && capture.groups['seq'].length >= codeBlockSeqLength) {
183
177
  lineType = 'TMCodeFenceBacktickClose';
184
- lineReplacement = _grammar.lineGrammar.TMCodeFenceBacktickClose.replacement;
178
+ lineReplacement = lineGrammar.TMCodeFenceBacktickClose.replacement;
185
179
  lineCapture = capture;
186
180
  codeBlockType = false;
187
181
  } else {
@@ -193,10 +187,10 @@ class Editor {
193
187
  // if (lineNum > 0 && (this.lineTypes[lineNum - 1] == 'TMCodeFenceTildeOpen' || this.lineTypes[lineNum - 1] == 'TMFencedCodeTilde')) {
194
188
  else if (codeBlockType == 'TMCodeFenceTildeOpen') {
195
189
  // We're in a tilde-fenced code block
196
- let capture = _grammar.lineGrammar.TMCodeFenceTildeClose.regexp.exec(this.lines[lineNum]);
190
+ let capture = lineGrammar.TMCodeFenceTildeClose.regexp.exec(this.lines[lineNum]);
197
191
  if (capture && capture.groups['seq'].length >= codeBlockSeqLength) {
198
192
  lineType = 'TMCodeFenceTildeClose';
199
- lineReplacement = _grammar.lineGrammar.TMCodeFenceTildeClose.replacement;
193
+ lineReplacement = lineGrammar.TMCodeFenceTildeClose.replacement;
200
194
  lineCapture = capture;
201
195
  codeBlockType = false;
202
196
  } else {
@@ -208,7 +202,7 @@ class Editor {
208
202
 
209
203
  // Check HTML block types
210
204
  if (lineType == 'TMPara' && htmlBlock === false) {
211
- for (let htmlBlockType of _grammar.htmlBlockGrammar) {
205
+ for (let htmlBlockType of htmlBlockGrammar) {
212
206
  if (this.lines[lineNum].match(htmlBlockType.start)) {
213
207
  // Matching start condition. Check if this tag can start here (not all start conditions allow breaking a paragraph).
214
208
  if (htmlBlockType.paraInterrupt || lineNum == 0 || !(this.lineTypes[lineNum - 1] == 'TMPara' || this.lineTypes[lineNum - 1] == 'TMUL' || this.lineTypes[lineNum - 1] == 'TMOL' || this.lineTypes[lineNum - 1] == 'TMBlockquote')) {
@@ -231,7 +225,7 @@ class Editor {
231
225
  }
232
226
  } else {
233
227
  // No specific end condition, ends with blank line
234
- if (lineNum == this.lines.length - 1 || this.lines[lineNum + 1].match(_grammar.lineGrammar.TMBlankLine.regexp)) {
228
+ if (lineNum == this.lines.length - 1 || this.lines[lineNum + 1].match(lineGrammar.TMBlankLine.regexp)) {
235
229
  htmlBlock = false;
236
230
  }
237
231
  }
@@ -239,12 +233,12 @@ class Editor {
239
233
 
240
234
  // Check all regexps if we haven't applied one of the code block types
241
235
  if (lineType == 'TMPara') {
242
- for (let type in _grammar.lineGrammar) {
243
- if (_grammar.lineGrammar[type].regexp) {
244
- let capture = _grammar.lineGrammar[type].regexp.exec(this.lines[lineNum]);
236
+ for (let type in lineGrammar) {
237
+ if (lineGrammar[type].regexp) {
238
+ let capture = lineGrammar[type].regexp.exec(this.lines[lineNum]);
245
239
  if (capture) {
246
240
  lineType = type;
247
- lineReplacement = _grammar.lineGrammar[type].replacement;
241
+ lineReplacement = lineGrammar[type].replacement;
248
242
  lineCapture = capture;
249
243
  break;
250
244
  }
@@ -268,10 +262,10 @@ class Editor {
268
262
 
269
263
  // Setext H2 markers that can also be interpreted as an empty list item should be regarded as such (as per CommonMark spec)
270
264
  if (lineType == 'TMSetextH2Marker') {
271
- let capture = _grammar.lineGrammar.TMUL.regexp.exec(this.lines[lineNum]);
265
+ let capture = lineGrammar.TMUL.regexp.exec(this.lines[lineNum]);
272
266
  if (capture) {
273
267
  lineType = 'TMUL';
274
- lineReplacement = _grammar.lineGrammar.TMUL.replacement;
268
+ lineReplacement = lineGrammar.TMUL.replacement;
275
269
  lineCapture = capture;
276
270
  }
277
271
  }
@@ -280,12 +274,12 @@ class Editor {
280
274
  if (lineType == 'TMSetextH1Marker' || lineType == 'TMSetextH2Marker') {
281
275
  if (lineNum == 0 || this.lineTypes[lineNum - 1] != 'TMPara') {
282
276
  // Setext marker is invalid. However, a H2 marker might still be a valid HR, so let's check that
283
- let capture = _grammar.lineGrammar.TMHR.regexp.exec(this.lines[lineNum]);
277
+ let capture = lineGrammar.TMHR.regexp.exec(this.lines[lineNum]);
284
278
  if (capture) {
285
279
  // Valid HR
286
280
  lineType = 'TMHR';
287
281
  lineCapture = capture;
288
- lineReplacement = _grammar.lineGrammar.TMHR.replacement;
282
+ lineReplacement = lineGrammar.TMHR.replacement;
289
283
  } else {
290
284
  // Not valid HR, format as TMPara
291
285
  lineType = 'TMPara';
@@ -353,7 +347,7 @@ class Editor {
353
347
  // Capture any escapes and code blocks at current position, they bind more strongly than links
354
348
  // We don't have to actually process them here, that'll be done later in case the link / image is valid, but we need to skip over them.
355
349
  for (let rule of ['escape', 'code', 'autolink', 'html']) {
356
- let cap = _grammar.inlineGrammar[rule].regexp.exec(string);
350
+ let cap = inlineGrammar[rule].regexp.exec(string);
357
351
  if (cap) {
358
352
  currentOffset += cap[0].length;
359
353
  continue textOuter;
@@ -361,7 +355,7 @@ class Editor {
361
355
  }
362
356
 
363
357
  // Check for image. It's okay for an image to be included in a link or image
364
- if (string.match(_grammar.inlineGrammar.imageOpen.regexp)) {
358
+ if (string.match(inlineGrammar.imageOpen.regexp)) {
365
359
  // Opening image. It's okay if this is a matching pair of brackets
366
360
  bracketLevel++;
367
361
  currentOffset += 2;
@@ -369,7 +363,7 @@ class Editor {
369
363
  }
370
364
 
371
365
  // Check for link (not an image because that would have been captured and skipped over above)
372
- if (string.match(_grammar.inlineGrammar.linkOpen.regexp)) {
366
+ if (string.match(inlineGrammar.linkOpen.regexp)) {
373
367
  // Opening bracket. Two things to do:
374
368
  // 1) it's okay if this part of a pair of brackets.
375
369
  // 2) If we are currently trying to parse a link, this nested bracket musn't start a valid link (no nested links allowed)
@@ -409,14 +403,14 @@ class Editor {
409
403
  // REFERENCE LINKS
410
404
  if (nextChar == '[') {
411
405
  let string = originalString.substr(currentOffset);
412
- let cap = _grammar.inlineGrammar.linkLabel.regexp.exec(string);
406
+ let cap = inlineGrammar.linkLabel.regexp.exec(string);
413
407
  if (cap) {
414
408
  // Valid link label
415
409
  currentOffset += cap[0].length;
416
410
  linkLabel.push(cap[1], cap[2], cap[3]);
417
- if (cap[_grammar.inlineGrammar.linkLabel.labelPlaceholder]) {
411
+ if (cap[inlineGrammar.linkLabel.labelPlaceholder]) {
418
412
  // Full reference link
419
- linkRef = cap[_grammar.inlineGrammar.linkLabel.labelPlaceholder];
413
+ linkRef = cap[inlineGrammar.linkLabel.labelPlaceholder];
420
414
  } else {
421
415
  // Collapsed reference link
422
416
  linkRef = linkText.trim();
@@ -490,7 +484,7 @@ class Editor {
490
484
  }
491
485
 
492
486
  // Process backslash escapes
493
- cap = _grammar.inlineGrammar.escape.regexp.exec(string);
487
+ cap = inlineGrammar.escape.regexp.exec(string);
494
488
  if (cap) {
495
489
  switch (linkDetails.length) {
496
490
  case 0:
@@ -719,20 +713,20 @@ class Editor {
719
713
  outer: while (string) {
720
714
  // Process simple rules (non-delimiter)
721
715
  for (let rule of ['escape', 'code', 'autolink', 'html']) {
722
- let cap = _grammar.inlineGrammar[rule].regexp.exec(string);
716
+ let cap = inlineGrammar[rule].regexp.exec(string);
723
717
  if (cap) {
724
718
  string = string.substr(cap[0].length);
725
719
  offset += cap[0].length;
726
- processed += _grammar.inlineGrammar[rule].replacement
720
+ processed += inlineGrammar[rule].replacement
727
721
  // .replace(/\$\$([1-9])/g, (str, p1) => processInlineStyles(cap[p1])) // todo recursive calling
728
- .replace(/\$([1-9])/g, (str, p1) => (0, _grammar.htmlescape)(cap[p1]));
722
+ .replace(/\$([1-9])/g, (str, p1) => htmlescape(cap[p1]));
729
723
  continue outer;
730
724
  }
731
725
  }
732
726
 
733
727
  // Check for links / images
734
- let potentialLink = string.match(_grammar.inlineGrammar.linkOpen.regexp);
735
- let potentialImage = string.match(_grammar.inlineGrammar.imageOpen.regexp);
728
+ let potentialLink = string.match(inlineGrammar.linkOpen.regexp);
729
+ let potentialImage = string.match(inlineGrammar.imageOpen.regexp);
736
730
  if (potentialImage || potentialLink) {
737
731
  let result = this.parseLinkOrImage(string, potentialImage);
738
732
  if (result) {
@@ -756,8 +750,8 @@ class Editor {
756
750
 
757
751
  const preceding = offset > 0 ? originalString.substr(0, offset) : ' '; // beginning and end of line count as whitespace
758
752
  const following = offset + cap[0].length < originalString.length ? string : ' ';
759
- const punctuationFollows = following.match(_grammar.punctuationLeading);
760
- const punctuationPrecedes = preceding.match(_grammar.punctuationTrailing);
753
+ const punctuationFollows = following.match(punctuationLeading);
754
+ const punctuationPrecedes = preceding.match(punctuationTrailing);
761
755
  const whitespaceFollows = following.match(/^\s/);
762
756
  const whitespacePrecedes = preceding.match(/\s$/);
763
757
 
@@ -876,11 +870,11 @@ class Editor {
876
870
  }
877
871
 
878
872
  // Process 'default' rule
879
- cap = _grammar.inlineGrammar.default.regexp.exec(string);
873
+ cap = inlineGrammar.default.regexp.exec(string);
880
874
  if (cap) {
881
875
  string = string.substr(cap[0].length);
882
876
  offset += cap[0].length;
883
- processed += _grammar.inlineGrammar.default.replacement.replace(/\$([1-9])/g, (str, p1) => (0, _grammar.htmlescape)(cap[p1]));
877
+ processed += inlineGrammar.default.replacement.replace(/\$([1-9])/g, (str, p1) => htmlescape(cap[p1]));
884
878
  continue outer;
885
879
  }
886
880
  throw 'Infinite loop!';
@@ -984,7 +978,7 @@ class Editor {
984
978
  sel.col = 0;
985
979
  if (continuableType) {
986
980
  // Check if the previous line was non-empty
987
- let capture = _grammar.lineGrammar[continuableType].regexp.exec(this.lines[sel.row - 1]);
981
+ let capture = lineGrammar[continuableType].regexp.exec(this.lines[sel.row - 1]);
988
982
  if (capture) {
989
983
  // Convention: capture[1] is the line type marker, capture[2] is the content
990
984
  if (capture[2]) {
@@ -1169,6 +1163,7 @@ class Editor {
1169
1163
  /**
1170
1164
  * Sets the selection based on rows and columns within the editor Markdown content.
1171
1165
  * @param {object} focus Object representing the selection, needs to have properties row and col.
1166
+ * @param anchor Anchor of the selection. If not given, assumes the current anchor.
1172
1167
  */
1173
1168
  setSelection(focus) {
1174
1169
  let anchor = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
@@ -1371,6 +1366,7 @@ class Editor {
1371
1366
  /**
1372
1367
  * Returns the state (true / false) of all commands.
1373
1368
  * @param focus Focus of the selection. If not given, assumes the current focus.
1369
+ * @param anchor Anchor of the selection. If not given, assumes the current anchor.
1374
1370
  */
1375
1371
  getCommandState() {
1376
1372
  let focus = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
@@ -1379,7 +1375,7 @@ class Editor {
1379
1375
  if (!focus) focus = this.getSelection(false);
1380
1376
  if (!anchor) anchor = this.getSelection(true);
1381
1377
  if (!focus) {
1382
- for (let cmd in _grammar.commands) {
1378
+ for (let cmd in commands) {
1383
1379
  commandState[cmd] = null;
1384
1380
  }
1385
1381
  return commandState;
@@ -1398,25 +1394,25 @@ class Editor {
1398
1394
  end.col = this.lines[end.row].length; // Selection to beginning of next line is said to end at the beginning of the last line
1399
1395
  }
1400
1396
 
1401
- for (let cmd in _grammar.commands) {
1402
- if (_grammar.commands[cmd].type == 'inline') {
1397
+ for (let cmd in commands) {
1398
+ if (commands[cmd].type == 'inline') {
1403
1399
  if (!focus || focus.row != anchor.row || !this.isInlineFormattingAllowed(focus, anchor)) {
1404
1400
  commandState[cmd] = null;
1405
1401
  } else {
1406
1402
  // The command state is true if there is a respective enclosing markup node (e.g., the selection is enclosed in a <b>..</b>) ...
1407
- commandState[cmd] = !!this.computeEnclosingMarkupNode(focus, anchor, _grammar.commands[cmd].className) ||
1403
+ commandState[cmd] = !!this.computeEnclosingMarkupNode(focus, anchor, commands[cmd].className) ||
1408
1404
  // ... or if it's an empty string preceded by and followed by formatting markers, e.g. **|** where | is the cursor
1409
1405
 
1410
- focus.col == anchor.col && !!this.lines[focus.row].substr(0, focus.col).match(_grammar.commands[cmd].unset.prePattern) && !!this.lines[focus.row].substr(focus.col).match(_grammar.commands[cmd].unset.postPattern);
1406
+ focus.col == anchor.col && !!this.lines[focus.row].substr(0, focus.col).match(commands[cmd].unset.prePattern) && !!this.lines[focus.row].substr(focus.col).match(commands[cmd].unset.postPattern);
1411
1407
  }
1412
1408
  }
1413
- if (_grammar.commands[cmd].type == 'line') {
1409
+ if (commands[cmd].type == 'line') {
1414
1410
  if (!focus) {
1415
1411
  commandState[cmd] = null;
1416
1412
  } else {
1417
- let state = this.lineTypes[start.row] == _grammar.commands[cmd].className;
1413
+ let state = this.lineTypes[start.row] == commands[cmd].className;
1418
1414
  for (let line = start.row; line <= end.row; line++) {
1419
- if (this.lineTypes[line] == _grammar.commands[cmd].className != state) {
1415
+ if (this.lineTypes[line] == commands[cmd].className != state) {
1420
1416
  state = null;
1421
1417
  break;
1422
1418
  }
@@ -1434,14 +1430,14 @@ class Editor {
1434
1430
  * @param {boolean} state
1435
1431
  */
1436
1432
  setCommandState(command, state) {
1437
- if (_grammar.commands[command].type == 'inline') {
1433
+ if (commands[command].type == 'inline') {
1438
1434
  let anchor = this.getSelection(true);
1439
1435
  let focus = this.getSelection(false);
1440
1436
  if (!anchor) anchor = focus;
1441
1437
  if (!anchor) return;
1442
1438
  if (anchor.row != focus.row) return;
1443
1439
  if (!this.isInlineFormattingAllowed(focus, anchor)) return;
1444
- let markupNode = this.computeEnclosingMarkupNode(focus, anchor, _grammar.commands[command].className);
1440
+ let markupNode = this.computeEnclosingMarkupNode(focus, anchor, commands[command].className);
1445
1441
  this.clearDirtyFlag();
1446
1442
 
1447
1443
  // First case: There's an enclosing markup node, remove the markers around that markup node
@@ -1449,24 +1445,26 @@ class Editor {
1449
1445
  this.lineDirty[focus.row] = true;
1450
1446
  const startCol = this.computeColumn(markupNode, 0);
1451
1447
  const len = markupNode.textContent.length;
1452
- const left = this.lines[focus.row].substr(0, startCol).replace(_grammar.commands[command].unset.prePattern, '');
1448
+ const left = this.lines[focus.row].substr(0, startCol).replace(commands[command].unset.prePattern, '');
1453
1449
  const mid = this.lines[focus.row].substr(startCol, len);
1454
- const right = this.lines[focus.row].substr(startCol + len).replace(_grammar.commands[command].unset.postPattern, '');
1450
+ const right = this.lines[focus.row].substr(startCol + len).replace(commands[command].unset.postPattern, '');
1455
1451
  this.lines[focus.row] = left.concat(mid, right);
1456
1452
  anchor.col = left.length;
1457
1453
  focus.col = anchor.col + len;
1458
1454
  this.updateFormatting();
1459
1455
  this.setSelection(focus, anchor);
1456
+ this.fireChange();
1460
1457
 
1461
1458
  // Second case: Empty selection with surrounding formatting markers, remove those
1462
- } else if (focus.col == anchor.col && !!this.lines[focus.row].substr(0, focus.col).match(_grammar.commands[command].unset.prePattern) && !!this.lines[focus.row].substr(focus.col).match(_grammar.commands[command].unset.postPattern)) {
1459
+ } else if (focus.col == anchor.col && !!this.lines[focus.row].substr(0, focus.col).match(commands[command].unset.prePattern) && !!this.lines[focus.row].substr(focus.col).match(commands[command].unset.postPattern)) {
1463
1460
  this.lineDirty[focus.row] = true;
1464
- const left = this.lines[focus.row].substr(0, focus.col).replace(_grammar.commands[command].unset.prePattern, '');
1465
- const right = this.lines[focus.row].substr(focus.col).replace(_grammar.commands[command].unset.postPattern, '');
1461
+ const left = this.lines[focus.row].substr(0, focus.col).replace(commands[command].unset.prePattern, '');
1462
+ const right = this.lines[focus.row].substr(focus.col).replace(commands[command].unset.postPattern, '');
1466
1463
  this.lines[focus.row] = left.concat(right);
1467
1464
  focus.col = anchor.col = left.length;
1468
1465
  this.updateFormatting();
1469
1466
  this.setSelection(focus, anchor);
1467
+ this.fireChange();
1470
1468
 
1471
1469
  // Not currently formatted, insert formatting markers
1472
1470
  } else {
@@ -1490,10 +1488,11 @@ class Editor {
1490
1488
  anchor.col = endCol;
1491
1489
 
1492
1490
  // Just insert markup before and after and hope for the best.
1493
- this.wrapSelection(_grammar.commands[command].set.pre, _grammar.commands[command].set.post, focus, anchor);
1491
+ this.wrapSelection(commands[command].set.pre, commands[command].set.post, focus, anchor);
1492
+ this.fireChange();
1494
1493
  // TODO clean this up so that markup remains properly nested
1495
1494
  }
1496
- } else if (_grammar.commands[command].type == 'line') {
1495
+ } else if (commands[command].type == 'line') {
1497
1496
  let anchor = this.getSelection(true);
1498
1497
  let focus = this.getSelection(false);
1499
1498
  if (!anchor) anchor = focus;
@@ -1505,12 +1504,12 @@ class Editor {
1505
1504
  end.row--;
1506
1505
  }
1507
1506
  for (let line = start.row; line <= end.row; line++) {
1508
- if (state && this.lineTypes[line] != _grammar.commands[command].className) {
1509
- this.lines[line] = this.lines[line].replace(_grammar.commands[command].set.pattern, _grammar.commands[command].set.replacement.replace('$#', line - start.row + 1));
1507
+ if (state && this.lineTypes[line] != commands[command].className) {
1508
+ this.lines[line] = this.lines[line].replace(commands[command].set.pattern, commands[command].set.replacement.replace('$#', line - start.row + 1));
1510
1509
  this.lineDirty[line] = true;
1511
1510
  }
1512
- if (!state && this.lineTypes[line] == _grammar.commands[command].className) {
1513
- this.lines[line] = this.lines[line].replace(_grammar.commands[command].unset.pattern, _grammar.commands[command].unset.replacement);
1511
+ if (!state && this.lineTypes[line] == commands[command].className) {
1512
+ this.lines[line] = this.lines[line].replace(commands[command].unset.pattern, commands[command].unset.replacement);
1514
1513
  this.lineDirty[line] = true;
1515
1514
  }
1516
1515
  }
@@ -1522,6 +1521,7 @@ class Editor {
1522
1521
  row: start.row,
1523
1522
  col: 0
1524
1523
  });
1524
+ this.fireChange();
1525
1525
  }
1526
1526
  }
1527
1527
 
@@ -1642,4 +1642,4 @@ class Editor {
1642
1642
  }
1643
1643
  }
1644
1644
  }
1645
- var _default = exports.default = Editor;
1645
+ export default Editor;
@@ -1,70 +1,63 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.default = void 0;
7
- var _svg = _interopRequireDefault(require("./svg/svg"));
8
- function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
1
+ import svg from './svg/svg';
9
2
  const isMacLike = /(Mac|iPhone|iPod|iPad)/i.test(navigator.platform);
10
3
  const DefaultCommands = {
11
4
  'bold': {
12
5
  name: 'bold',
13
6
  action: 'bold',
14
- innerHTML: _svg.default.bold,
7
+ innerHTML: svg.bold,
15
8
  title: 'Bold',
16
9
  hotkey: 'Mod-B'
17
10
  },
18
11
  'italic': {
19
12
  name: 'italic',
20
13
  action: 'italic',
21
- innerHTML: _svg.default.italic,
14
+ innerHTML: svg.italic,
22
15
  title: 'Italic',
23
16
  hotkey: 'Mod-I'
24
17
  },
25
18
  'strikethrough': {
26
19
  name: 'strikethrough',
27
20
  action: 'strikethrough',
28
- innerHTML: _svg.default.strikethrough,
21
+ innerHTML: svg.strikethrough,
29
22
  title: 'Strikethrough',
30
23
  hotkey: 'Mod2-Shift-5'
31
24
  },
32
25
  'code': {
33
26
  name: 'code',
34
27
  action: 'code',
35
- innerHTML: _svg.default.code,
28
+ innerHTML: svg.code,
36
29
  title: 'Format as code'
37
30
  },
38
31
  'h1': {
39
32
  name: 'h1',
40
33
  action: 'h1',
41
- innerHTML: _svg.default.h1,
34
+ innerHTML: svg.h1,
42
35
  title: 'Level 1 heading',
43
36
  hotkey: 'Mod-Shift-1'
44
37
  },
45
38
  'h2': {
46
39
  name: 'h2',
47
40
  action: 'h2',
48
- innerHTML: _svg.default.h2,
41
+ innerHTML: svg.h2,
49
42
  title: 'Level 2 heading',
50
43
  hotkey: 'Mod-Shift-2'
51
44
  },
52
45
  'ul': {
53
46
  name: 'ul',
54
47
  action: 'ul',
55
- innerHTML: _svg.default.ul,
48
+ innerHTML: svg.ul,
56
49
  title: 'Bulleted list'
57
50
  },
58
51
  'ol': {
59
52
  name: 'ol',
60
53
  action: 'ol',
61
- innerHTML: _svg.default.ol,
54
+ innerHTML: svg.ol,
62
55
  title: 'Numbered list'
63
56
  },
64
57
  'blockquote': {
65
58
  name: 'blockquote',
66
59
  action: 'blockquote',
67
- innerHTML: _svg.default.blockquote,
60
+ innerHTML: svg.blockquote,
68
61
  title: 'Quote',
69
62
  hotkey: 'Mod2-Shift-Q'
70
63
  },
@@ -74,7 +67,7 @@ const DefaultCommands = {
74
67
  if (editor.isInlineFormattingAllowed()) editor.wrapSelection('[', ']()');
75
68
  },
76
69
  enabled: (editor, focus, anchor) => editor.isInlineFormattingAllowed(focus, anchor) ? false : null,
77
- innerHTML: _svg.default.link,
70
+ innerHTML: svg.link,
78
71
  title: 'Insert link',
79
72
  hotkey: 'Mod-K'
80
73
  },
@@ -84,7 +77,7 @@ const DefaultCommands = {
84
77
  if (editor.isInlineFormattingAllowed()) editor.wrapSelection('![', ']()');
85
78
  },
86
79
  enabled: (editor, focus, anchor) => editor.isInlineFormattingAllowed(focus, anchor) ? false : null,
87
- innerHTML: _svg.default.image,
80
+ innerHTML: svg.image,
88
81
  title: 'Insert image',
89
82
  hotkey: 'Mod2-Shift-I'
90
83
  },
@@ -92,7 +85,7 @@ const DefaultCommands = {
92
85
  name: 'hr',
93
86
  action: editor => editor.paste('\n***\n'),
94
87
  enabled: () => false,
95
- innerHTML: _svg.default.hr,
88
+ innerHTML: svg.hr,
96
89
  title: 'Insert horizontal line',
97
90
  hotkey: 'Mod2-Shift-L'
98
91
  }
@@ -262,4 +255,4 @@ class CommandBar {
262
255
  }
263
256
  }
264
257
  }
265
- var _default = exports.default = CommandBar;
258
+ export default CommandBar;