tiny-markdown-editor 0.2.6 → 0.2.7

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/lib/TinyMDE.js ADDED
@@ -0,0 +1,1551 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Editor = void 0;
4
+ const grammar_1 = require("./grammar");
5
+ class Editor {
6
+ constructor(props = {}) {
7
+ this.e = null;
8
+ this.textarea = null;
9
+ this.lines = [];
10
+ this.lineElements = [];
11
+ this.lineTypes = [];
12
+ this.lineCaptures = [];
13
+ this.lineReplacements = [];
14
+ this.linkLabels = [];
15
+ this.lineDirty = [];
16
+ this.lastCommandState = null;
17
+ this.customInlineGrammar = {};
18
+ this.mergedInlineGrammar = grammar_1.inlineGrammar;
19
+ this.hasFocus = true;
20
+ this.listeners = {
21
+ change: [],
22
+ selection: [],
23
+ drop: [],
24
+ };
25
+ this.undoStack = [];
26
+ this.redoStack = [];
27
+ this.isRestoringHistory = false;
28
+ this.maxHistory = 100;
29
+ this.e = null;
30
+ this.textarea = null;
31
+ this.lines = [];
32
+ this.lineElements = [];
33
+ this.lineTypes = [];
34
+ this.lineCaptures = [];
35
+ this.lineReplacements = [];
36
+ this.linkLabels = [];
37
+ this.lineDirty = [];
38
+ this.lastCommandState = null;
39
+ this.hasFocus = true;
40
+ this.customInlineGrammar = props.customInlineGrammar || {};
41
+ this.mergedInlineGrammar = (0, grammar_1.createMergedInlineGrammar)(this.customInlineGrammar);
42
+ this.listeners = {
43
+ change: [],
44
+ selection: [],
45
+ drop: [],
46
+ };
47
+ let element = null;
48
+ if (typeof props.element === 'string') {
49
+ element = document.getElementById(props.element);
50
+ }
51
+ else if (props.element) {
52
+ element = props.element;
53
+ }
54
+ if (typeof props.textarea === 'string') {
55
+ this.textarea = document.getElementById(props.textarea);
56
+ }
57
+ else if (props.textarea) {
58
+ this.textarea = props.textarea;
59
+ }
60
+ if (this.textarea) {
61
+ if (!element)
62
+ element = this.textarea;
63
+ }
64
+ if (!element) {
65
+ element = document.getElementsByTagName("body")[0];
66
+ }
67
+ if (element && element.tagName === "TEXTAREA") {
68
+ this.textarea = element;
69
+ element = this.textarea.parentNode;
70
+ }
71
+ if (this.textarea) {
72
+ this.textarea.style.display = "none";
73
+ }
74
+ this.undoStack = [];
75
+ this.redoStack = [];
76
+ this.isRestoringHistory = false;
77
+ this.maxHistory = 100;
78
+ this.createEditorElement(element, props);
79
+ this.setContent(typeof props.content === "string"
80
+ ? props.content
81
+ : this.textarea
82
+ ? this.textarea.value
83
+ : "# Hello TinyMDE!\nEdit **here**");
84
+ this.e.addEventListener("keydown", (e) => this.handleUndoRedoKey(e));
85
+ }
86
+ get canUndo() {
87
+ return this.undoStack.length >= 2;
88
+ }
89
+ get canRedo() {
90
+ return this.redoStack.length > 0;
91
+ }
92
+ pushHistory() {
93
+ if (this.isRestoringHistory)
94
+ return;
95
+ this.pushCurrentState();
96
+ this.redoStack = [];
97
+ }
98
+ pushCurrentState() {
99
+ this.undoStack.push({
100
+ content: this.getContent(),
101
+ selection: this.getSelection(),
102
+ anchor: this.getSelection(true),
103
+ });
104
+ if (this.undoStack.length > this.maxHistory)
105
+ this.undoStack.shift();
106
+ }
107
+ undo() {
108
+ if (this.undoStack.length < 2)
109
+ return;
110
+ this.isRestoringHistory = true;
111
+ this.pushCurrentState();
112
+ const current = this.undoStack.pop();
113
+ this.redoStack.push(current);
114
+ const prev = this.undoStack[this.undoStack.length - 1];
115
+ this.setContent(prev.content);
116
+ if (prev.selection)
117
+ this.setSelection(prev.selection, prev.anchor);
118
+ this.undoStack.pop();
119
+ this.isRestoringHistory = false;
120
+ }
121
+ redo() {
122
+ if (!this.redoStack.length)
123
+ return;
124
+ this.isRestoringHistory = true;
125
+ this.pushCurrentState();
126
+ const next = this.redoStack.pop();
127
+ this.setContent(next.content);
128
+ if (next.selection)
129
+ this.setSelection(next.selection, next.anchor);
130
+ this.isRestoringHistory = false;
131
+ }
132
+ handleUndoRedoKey(e) {
133
+ const isMac = /Mac|iPod|iPhone|iPad/.test(navigator.platform);
134
+ const ctrl = isMac ? e.metaKey : e.ctrlKey;
135
+ if (ctrl && !e.altKey) {
136
+ if (e.key === "z" || e.key === "Z") {
137
+ if (e.shiftKey) {
138
+ this.redo();
139
+ }
140
+ else {
141
+ this.undo();
142
+ }
143
+ e.preventDefault();
144
+ }
145
+ else if (e.key === "y" || e.key === "Y") {
146
+ this.redo();
147
+ e.preventDefault();
148
+ }
149
+ }
150
+ }
151
+ createEditorElement(element, props) {
152
+ if (props && props.editor !== undefined) {
153
+ if (typeof props.editor === 'string') {
154
+ this.e = document.getElementById(props.editor);
155
+ }
156
+ else {
157
+ this.e = props.editor;
158
+ }
159
+ }
160
+ else {
161
+ this.e = document.createElement("div");
162
+ }
163
+ this.e.classList.add("TinyMDE");
164
+ this.e.contentEditable = "true";
165
+ this.e.style.whiteSpace = "pre-wrap";
166
+ this.e.style.webkitUserModify = "read-write-plaintext-only";
167
+ if (props.editor === undefined) {
168
+ if (this.textarea &&
169
+ this.textarea.parentNode === element &&
170
+ this.textarea.nextSibling) {
171
+ element.insertBefore(this.e, this.textarea.nextSibling);
172
+ }
173
+ else {
174
+ element.appendChild(this.e);
175
+ }
176
+ }
177
+ this.e.addEventListener("input", (e) => this.handleInputEvent(e));
178
+ this.e.addEventListener("beforeinput", (e) => this.handleBeforeInputEvent(e));
179
+ this.e.addEventListener("compositionend", (e) => this.handleInputEvent(e));
180
+ document.addEventListener("selectionchange", (e) => {
181
+ if (this.hasFocus) {
182
+ this.handleSelectionChangeEvent(e);
183
+ }
184
+ });
185
+ this.e.addEventListener("blur", () => this.hasFocus = false);
186
+ this.e.addEventListener("focus", () => this.hasFocus = true);
187
+ this.e.addEventListener("paste", (e) => this.handlePaste(e));
188
+ this.e.addEventListener("drop", (e) => this.handleDrop(e));
189
+ this.lineElements = this.e.childNodes;
190
+ }
191
+ setContent(content) {
192
+ while (this.e.firstChild) {
193
+ this.e.removeChild(this.e.firstChild);
194
+ }
195
+ this.lines = content.split(/(?:\r\n|\r|\n)/);
196
+ this.lineDirty = [];
197
+ for (let lineNum = 0; lineNum < this.lines.length; lineNum++) {
198
+ let le = document.createElement("div");
199
+ this.e.appendChild(le);
200
+ this.lineDirty.push(true);
201
+ }
202
+ this.lineTypes = new Array(this.lines.length);
203
+ this.updateFormatting();
204
+ this.fireChange();
205
+ if (!this.isRestoringHistory)
206
+ this.pushHistory();
207
+ }
208
+ getContent() {
209
+ return this.lines.join("\n");
210
+ }
211
+ updateFormatting() {
212
+ this.updateLineTypes();
213
+ this.updateLinkLabels();
214
+ this.applyLineTypes();
215
+ }
216
+ updateLinkLabels() {
217
+ this.linkLabels = [];
218
+ for (let l = 0; l < this.lines.length; l++) {
219
+ if (this.lineTypes[l] === "TMLinkReferenceDefinition") {
220
+ this.linkLabels.push(this.lineCaptures[l][grammar_1.lineGrammar.TMLinkReferenceDefinition.labelPlaceholder]);
221
+ }
222
+ }
223
+ }
224
+ replace(replacement, capture) {
225
+ return replacement.replace(/(\${1,2})([0-9])/g, (str, p1, p2) => {
226
+ if (p1 === "$")
227
+ return (0, grammar_1.htmlescape)(capture[parseInt(p2)]);
228
+ else
229
+ return `<span class="TMInlineFormatted">${this.processInlineStyles(capture[parseInt(p2)])}</span>`;
230
+ });
231
+ }
232
+ applyLineTypes() {
233
+ for (let lineNum = 0; lineNum < this.lines.length; lineNum++) {
234
+ if (this.lineDirty[lineNum]) {
235
+ let contentHTML = this.replace(this.lineReplacements[lineNum], this.lineCaptures[lineNum]);
236
+ this.lineElements[lineNum].className = this.lineTypes[lineNum];
237
+ this.lineElements[lineNum].removeAttribute("style");
238
+ this.lineElements[lineNum].innerHTML =
239
+ contentHTML === "" ? "<br />" : contentHTML;
240
+ }
241
+ this.lineElements[lineNum].dataset.lineNum = lineNum.toString();
242
+ }
243
+ }
244
+ updateLineTypes() {
245
+ let codeBlockType = false;
246
+ let codeBlockSeqLength = 0;
247
+ let htmlBlock = false;
248
+ for (let lineNum = 0; lineNum < this.lines.length; lineNum++) {
249
+ let lineType = "TMPara";
250
+ let lineCapture = [this.lines[lineNum]];
251
+ let lineReplacement = "$$0";
252
+ // Check ongoing code blocks
253
+ if (codeBlockType === "TMCodeFenceBacktickOpen") {
254
+ let capture = grammar_1.lineGrammar.TMCodeFenceBacktickClose.regexp.exec(this.lines[lineNum]);
255
+ if (capture && capture.groups["seq"].length >= codeBlockSeqLength) {
256
+ lineType = "TMCodeFenceBacktickClose";
257
+ lineReplacement = grammar_1.lineGrammar.TMCodeFenceBacktickClose.replacement;
258
+ lineCapture = capture;
259
+ codeBlockType = false;
260
+ }
261
+ else {
262
+ lineType = "TMFencedCodeBacktick";
263
+ lineReplacement = '<span class="TMFencedCode">$0<br /></span>';
264
+ lineCapture = [this.lines[lineNum]];
265
+ }
266
+ }
267
+ else if (codeBlockType === "TMCodeFenceTildeOpen") {
268
+ let capture = grammar_1.lineGrammar.TMCodeFenceTildeClose.regexp.exec(this.lines[lineNum]);
269
+ if (capture && capture.groups["seq"].length >= codeBlockSeqLength) {
270
+ lineType = "TMCodeFenceTildeClose";
271
+ lineReplacement = grammar_1.lineGrammar.TMCodeFenceTildeClose.replacement;
272
+ lineCapture = capture;
273
+ codeBlockType = false;
274
+ }
275
+ else {
276
+ lineType = "TMFencedCodeTilde";
277
+ lineReplacement = '<span class="TMFencedCode">$0<br /></span>';
278
+ lineCapture = [this.lines[lineNum]];
279
+ }
280
+ }
281
+ // Check HTML block types
282
+ if (lineType === "TMPara" && htmlBlock === false) {
283
+ for (let htmlBlockType of grammar_1.htmlBlockGrammar) {
284
+ if (this.lines[lineNum].match(htmlBlockType.start)) {
285
+ if (htmlBlockType.paraInterrupt ||
286
+ lineNum === 0 ||
287
+ !(this.lineTypes[lineNum - 1] === "TMPara" ||
288
+ this.lineTypes[lineNum - 1] === "TMUL" ||
289
+ this.lineTypes[lineNum - 1] === "TMOL" ||
290
+ this.lineTypes[lineNum - 1] === "TMBlockquote")) {
291
+ htmlBlock = htmlBlockType;
292
+ break;
293
+ }
294
+ }
295
+ }
296
+ }
297
+ if (htmlBlock !== false) {
298
+ lineType = "TMHTMLBlock";
299
+ lineReplacement = '<span class="TMHTMLContent">$0<br /></span>';
300
+ lineCapture = [this.lines[lineNum]];
301
+ if (htmlBlock.end) {
302
+ if (this.lines[lineNum].match(htmlBlock.end)) {
303
+ htmlBlock = false;
304
+ }
305
+ }
306
+ else {
307
+ if (lineNum === this.lines.length - 1 ||
308
+ this.lines[lineNum + 1].match(grammar_1.lineGrammar.TMBlankLine.regexp)) {
309
+ htmlBlock = false;
310
+ }
311
+ }
312
+ }
313
+ // Check all regexps if we haven't applied one of the code block types
314
+ if (lineType === "TMPara") {
315
+ for (let type in grammar_1.lineGrammar) {
316
+ if (grammar_1.lineGrammar[type].regexp) {
317
+ let capture = grammar_1.lineGrammar[type].regexp.exec(this.lines[lineNum]);
318
+ if (capture) {
319
+ lineType = type;
320
+ lineReplacement = grammar_1.lineGrammar[type].replacement;
321
+ lineCapture = capture;
322
+ break;
323
+ }
324
+ }
325
+ }
326
+ }
327
+ // If we've opened a code block, remember that
328
+ if (lineType === "TMCodeFenceBacktickOpen" || lineType === "TMCodeFenceTildeOpen") {
329
+ codeBlockType = lineType;
330
+ codeBlockSeqLength = lineCapture.groups["seq"].length;
331
+ }
332
+ // Link reference definition and indented code can't interrupt a paragraph
333
+ if ((lineType === "TMIndentedCode" || lineType === "TMLinkReferenceDefinition") &&
334
+ lineNum > 0 &&
335
+ (this.lineTypes[lineNum - 1] === "TMPara" ||
336
+ this.lineTypes[lineNum - 1] === "TMUL" ||
337
+ this.lineTypes[lineNum - 1] === "TMOL" ||
338
+ this.lineTypes[lineNum - 1] === "TMBlockquote")) {
339
+ lineType = "TMPara";
340
+ lineCapture = [this.lines[lineNum]];
341
+ lineReplacement = "$$0";
342
+ }
343
+ // Setext H2 markers that can also be interpreted as an empty list item should be regarded as such
344
+ if (lineType === "TMSetextH2Marker") {
345
+ let capture = grammar_1.lineGrammar.TMUL.regexp.exec(this.lines[lineNum]);
346
+ if (capture) {
347
+ lineType = "TMUL";
348
+ lineReplacement = grammar_1.lineGrammar.TMUL.replacement;
349
+ lineCapture = capture;
350
+ }
351
+ }
352
+ // Setext headings are only valid if preceded by a paragraph
353
+ if (lineType === "TMSetextH1Marker" || lineType === "TMSetextH2Marker") {
354
+ if (lineNum === 0 || this.lineTypes[lineNum - 1] !== "TMPara") {
355
+ let capture = grammar_1.lineGrammar.TMHR.regexp.exec(this.lines[lineNum]);
356
+ if (capture) {
357
+ lineType = "TMHR";
358
+ lineCapture = capture;
359
+ lineReplacement = grammar_1.lineGrammar.TMHR.replacement;
360
+ }
361
+ else {
362
+ lineType = "TMPara";
363
+ lineCapture = [this.lines[lineNum]];
364
+ lineReplacement = "$$0";
365
+ }
366
+ }
367
+ else {
368
+ let headingLine = lineNum - 1;
369
+ const headingLineType = lineType === "TMSetextH1Marker" ? "TMSetextH1" : "TMSetextH2";
370
+ do {
371
+ if (this.lineTypes[headingLine] !== headingLineType) {
372
+ this.lineTypes[headingLine] = headingLineType;
373
+ this.lineDirty[headingLine] = true;
374
+ }
375
+ this.lineReplacements[headingLine] = "$$0";
376
+ this.lineCaptures[headingLine] = [this.lines[headingLine]];
377
+ headingLine--;
378
+ } while (headingLine >= 0 && this.lineTypes[headingLine] === "TMPara");
379
+ }
380
+ }
381
+ if (this.lineTypes[lineNum] !== lineType) {
382
+ this.lineTypes[lineNum] = lineType;
383
+ this.lineDirty[lineNum] = true;
384
+ }
385
+ this.lineReplacements[lineNum] = lineReplacement;
386
+ this.lineCaptures[lineNum] = lineCapture;
387
+ }
388
+ }
389
+ getSelection(getAnchor = false) {
390
+ var _a, _b;
391
+ const selection = window.getSelection();
392
+ let startNode = getAnchor ? selection.anchorNode : selection.focusNode;
393
+ if (!startNode)
394
+ return null;
395
+ let offset = getAnchor ? selection.anchorOffset : selection.focusOffset;
396
+ if (startNode === this.e) {
397
+ if (offset < this.lines.length)
398
+ return {
399
+ row: offset,
400
+ col: 0,
401
+ };
402
+ return {
403
+ row: offset - 1,
404
+ col: this.lines[offset - 1].length,
405
+ };
406
+ }
407
+ let col = this.computeColumn(startNode, offset);
408
+ if (col === null)
409
+ return null;
410
+ let node = startNode;
411
+ while (node.parentElement !== this.e) {
412
+ node = node.parentElement;
413
+ }
414
+ let row = 0;
415
+ // If the node doesn't have a previous sibling, it must be the first line
416
+ if (node.previousSibling) {
417
+ const currentLineNumData = (_a = node.dataset) === null || _a === void 0 ? void 0 : _a.lineNum;
418
+ const previousLineNumData = (_b = node.previousSibling.dataset) === null || _b === void 0 ? void 0 : _b.lineNum;
419
+ if (currentLineNumData && previousLineNumData) {
420
+ const currentLineNum = parseInt(currentLineNumData);
421
+ const previousLineNum = parseInt(previousLineNumData);
422
+ if (currentLineNum === previousLineNum + 1) {
423
+ row = currentLineNum;
424
+ }
425
+ else {
426
+ // If the current line is NOT the previous line + 1, then either
427
+ // the current line got split in two or merged with the previous line
428
+ // Either way, we need to recalculate the row number
429
+ while (node.previousSibling) {
430
+ row++;
431
+ node = node.previousSibling;
432
+ }
433
+ }
434
+ }
435
+ }
436
+ return { row: row, col: col };
437
+ }
438
+ setSelection(focus, anchor = null) {
439
+ if (!focus)
440
+ return;
441
+ let { node: focusNode, offset: focusOffset } = this.computeNodeAndOffset(focus.row, focus.col, anchor ? anchor.row === focus.row && anchor.col > focus.col : false);
442
+ let anchorNode = null, anchorOffset = null;
443
+ if (anchor && (anchor.row !== focus.row || anchor.col !== focus.col)) {
444
+ let { node, offset } = this.computeNodeAndOffset(anchor.row, anchor.col, focus.row === anchor.row && focus.col > anchor.col);
445
+ anchorNode = node;
446
+ anchorOffset = offset;
447
+ }
448
+ let windowSelection = window.getSelection();
449
+ windowSelection.setBaseAndExtent(focusNode, focusOffset, anchorNode || focusNode, anchorNode ? anchorOffset : focusOffset);
450
+ }
451
+ paste(text, anchor = null, focus = null) {
452
+ if (!anchor)
453
+ anchor = this.getSelection(true);
454
+ if (!focus)
455
+ focus = this.getSelection(false);
456
+ let beginning, end;
457
+ if (!focus) {
458
+ focus = {
459
+ row: this.lines.length - 1,
460
+ col: this.lines[this.lines.length - 1].length,
461
+ };
462
+ }
463
+ if (!anchor) {
464
+ anchor = focus;
465
+ }
466
+ if (anchor.row < focus.row ||
467
+ (anchor.row === focus.row && anchor.col <= focus.col)) {
468
+ beginning = anchor;
469
+ end = focus;
470
+ }
471
+ else {
472
+ beginning = focus;
473
+ end = anchor;
474
+ }
475
+ let insertedLines = text.split(/(?:\r\n|\r|\n)/);
476
+ let lineBefore = this.lines[beginning.row].substr(0, beginning.col);
477
+ let lineEnd = this.lines[end.row].substr(end.col);
478
+ insertedLines[0] = lineBefore.concat(insertedLines[0]);
479
+ let endColPos = insertedLines[insertedLines.length - 1].length;
480
+ insertedLines[insertedLines.length - 1] = insertedLines[insertedLines.length - 1].concat(lineEnd);
481
+ this.spliceLines(beginning.row, 1 + end.row - beginning.row, insertedLines);
482
+ focus.row = beginning.row + insertedLines.length - 1;
483
+ focus.col = endColPos;
484
+ this.updateFormatting();
485
+ this.setSelection(focus);
486
+ this.fireChange();
487
+ }
488
+ wrapSelection(pre, post, focus = null, anchor = null) {
489
+ if (!this.isRestoringHistory)
490
+ this.pushHistory();
491
+ if (!focus)
492
+ focus = this.getSelection(false);
493
+ if (!anchor)
494
+ anchor = this.getSelection(true);
495
+ if (!focus || !anchor || focus.row !== anchor.row)
496
+ return;
497
+ this.lineDirty[focus.row] = true;
498
+ const startCol = focus.col < anchor.col ? focus.col : anchor.col;
499
+ const endCol = focus.col < anchor.col ? anchor.col : focus.col;
500
+ const left = this.lines[focus.row].substr(0, startCol).concat(pre);
501
+ const mid = endCol === startCol ? "" : this.lines[focus.row].substr(startCol, endCol - startCol);
502
+ const right = post.concat(this.lines[focus.row].substr(endCol));
503
+ this.lines[focus.row] = left.concat(mid, right);
504
+ anchor.col = left.length;
505
+ focus.col = anchor.col + mid.length;
506
+ this.updateFormatting();
507
+ this.setSelection(focus, anchor);
508
+ }
509
+ addEventListener(type, listener) {
510
+ if (type.match(/^(?:change|input)$/i)) {
511
+ this.listeners.change.push(listener);
512
+ }
513
+ if (type.match(/^(?:selection|selectionchange)$/i)) {
514
+ this.listeners.selection.push(listener);
515
+ }
516
+ if (type.match(/^(?:drop)$/i)) {
517
+ this.listeners.drop.push(listener);
518
+ }
519
+ }
520
+ fireChange() {
521
+ if (!this.textarea && !this.listeners.change.length)
522
+ return;
523
+ const content = this.getContent();
524
+ if (this.textarea)
525
+ this.textarea.value = content;
526
+ for (let listener of this.listeners.change) {
527
+ listener({
528
+ content: content,
529
+ linesDirty: [...this.lineDirty],
530
+ });
531
+ }
532
+ }
533
+ /**
534
+ * beforeinput handler, exclusively to handle insertParagraph and
535
+ * insertLineBreak events. These used to be handled in the input event,
536
+ * but that caused issues with Firefox where the input event would be
537
+ * sometimes not be fired for these input types.
538
+ * @param event The input event
539
+ * @returns nothing
540
+ */
541
+ handleBeforeInputEvent(event) {
542
+ const beforeInputEvent = event;
543
+ if (beforeInputEvent.inputType !== "insertParagraph" &&
544
+ beforeInputEvent.inputType !== "insertLineBreak")
545
+ return;
546
+ if (!this.isRestoringHistory)
547
+ this.pushHistory();
548
+ event.preventDefault();
549
+ this.clearDirtyFlag();
550
+ const focus = this.getSelection();
551
+ const anchor = this.getSelection(true);
552
+ if (!focus || !anchor)
553
+ return;
554
+ // If focus and anchor are in different lines, simply remove everything
555
+ // after the beginning of the selection and before the end of the selection
556
+ // and remove any lines in between
557
+ if (focus.row !== anchor.row) {
558
+ const beginning = focus.row < anchor.row ? focus : anchor;
559
+ const end = focus.row < anchor.row ? anchor : focus;
560
+ this.lines[beginning.row] = this.lines[beginning.row].substring(0, beginning.col);
561
+ this.lines[end.row] = this.lines[end.row].substring(end.col);
562
+ this.spliceLines(beginning.row + 1, end.row - beginning.row - 1);
563
+ focus.row = beginning.row + 1;
564
+ focus.col = 0;
565
+ }
566
+ else {
567
+ let continuableType = false;
568
+ switch (this.lineTypes[focus.row]) {
569
+ case "TMUL":
570
+ continuableType = "TMUL";
571
+ break;
572
+ case "TMOL":
573
+ continuableType = "TMOL";
574
+ break;
575
+ case "TMIndentedCode":
576
+ continuableType = "TMIndentedCode";
577
+ break;
578
+ }
579
+ const lineBeforeBreak = this.lines[focus.row].substring(0, focus.col <= anchor.col ? focus.col : anchor.col);
580
+ const lineAfterBreak = this.lines[focus.row].substring(focus.col <= anchor.col ? anchor.col : focus.col);
581
+ this.spliceLines(focus.row, 1, [lineBeforeBreak, lineAfterBreak]);
582
+ focus.row += 1;
583
+ focus.col = 0;
584
+ if (continuableType) {
585
+ let capture = grammar_1.lineGrammar[continuableType].regexp.exec(this.lines[focus.row - 1]);
586
+ if (capture) {
587
+ if (capture[2]) {
588
+ if (continuableType === "TMOL") {
589
+ capture[1] = capture[1].replace(/\d{1,9}/, (result) => {
590
+ return (parseInt(result) + 1).toString();
591
+ });
592
+ }
593
+ this.lines[focus.row] = `${capture[1]}${this.lines[focus.row]}`;
594
+ this.lineDirty[focus.row] = true;
595
+ focus.col = capture[1].length;
596
+ }
597
+ else {
598
+ this.lines[focus.row - 1] = "";
599
+ this.lineDirty[focus.row - 1] = true;
600
+ }
601
+ }
602
+ }
603
+ }
604
+ this.updateFormatting();
605
+ this.setSelection(focus);
606
+ this.fireChange();
607
+ }
608
+ handleInputEvent(event) {
609
+ const inputEvent = event;
610
+ if (inputEvent.inputType === "insertCompositionText")
611
+ return;
612
+ if (!this.isRestoringHistory)
613
+ this.pushHistory();
614
+ let focus = this.getSelection();
615
+ if ((inputEvent.inputType === "insertParagraph" || inputEvent.inputType === "insertLineBreak") && focus) {
616
+ // This should never happen since these are handled by the beforeinput handler
617
+ return;
618
+ }
619
+ else {
620
+ if (!this.e.firstChild) {
621
+ this.e.innerHTML = '<div class="TMBlankLine"><br></div>';
622
+ }
623
+ else {
624
+ this.fixNodeHierarchy();
625
+ }
626
+ this.updateLineContentsAndFormatting();
627
+ }
628
+ if (focus) {
629
+ this.setSelection(focus);
630
+ }
631
+ this.fireChange();
632
+ }
633
+ handleSelectionChangeEvent(_e) {
634
+ this.fireSelection();
635
+ }
636
+ handlePaste(event) {
637
+ if (!this.isRestoringHistory)
638
+ this.pushHistory();
639
+ event.preventDefault();
640
+ let text = event.clipboardData.getData("text/plain");
641
+ this.paste(text);
642
+ }
643
+ handleDrop(event) {
644
+ event.preventDefault();
645
+ this.fireDrop(event.dataTransfer);
646
+ }
647
+ processInlineStyles(originalString) {
648
+ let processed = "";
649
+ let stack = [];
650
+ let offset = 0;
651
+ let string = originalString;
652
+ outer: while (string) {
653
+ // Process simple rules (non-delimiter)
654
+ for (let rule of ["escape", "code", "autolink", "html"]) {
655
+ if (this.mergedInlineGrammar[rule]) {
656
+ let cap = this.mergedInlineGrammar[rule].regexp.exec(string);
657
+ if (cap) {
658
+ string = string.substr(cap[0].length);
659
+ offset += cap[0].length;
660
+ processed += this.mergedInlineGrammar[rule].replacement.replace(/\$([1-9])/g, (str, p1) => (0, grammar_1.htmlescape)(cap[p1]));
661
+ continue outer;
662
+ }
663
+ }
664
+ }
665
+ // Process custom inline grammar rules
666
+ for (let rule in this.customInlineGrammar) {
667
+ if (rule !== "escape" && rule !== "code" && rule !== "autolink" && rule !== "html" && rule !== "linkOpen" && rule !== "imageOpen" && rule !== "linkLabel" && rule !== "default") {
668
+ let cap = this.mergedInlineGrammar[rule].regexp.exec(string);
669
+ if (cap) {
670
+ string = string.substr(cap[0].length);
671
+ offset += cap[0].length;
672
+ processed += this.mergedInlineGrammar[rule].replacement.replace(/\$([1-9])/g, (str, p1) => (0, grammar_1.htmlescape)(cap[p1]));
673
+ continue outer;
674
+ }
675
+ }
676
+ }
677
+ // Check for links / images
678
+ let potentialLink = string.match(this.mergedInlineGrammar.linkOpen.regexp);
679
+ let potentialImage = string.match(this.mergedInlineGrammar.imageOpen.regexp);
680
+ if (potentialImage || potentialLink) {
681
+ let result = this.parseLinkOrImage(string, !!potentialImage);
682
+ if (result) {
683
+ processed = `${processed}${result.output}`;
684
+ string = string.substr(result.charCount);
685
+ offset += result.charCount;
686
+ continue outer;
687
+ }
688
+ }
689
+ // Check for em / strong delimiters
690
+ let cap = /(^\*+)|(^_+)/.exec(string);
691
+ if (cap) {
692
+ let delimCount = cap[0].length;
693
+ const delimString = cap[0];
694
+ const currentDelimiter = cap[0][0];
695
+ string = string.substr(cap[0].length);
696
+ const preceding = offset > 0 ? originalString.substr(0, offset) : " ";
697
+ const following = offset + cap[0].length < originalString.length ? string : " ";
698
+ const punctuationFollows = following.match(grammar_1.punctuationLeading);
699
+ const punctuationPrecedes = preceding.match(grammar_1.punctuationTrailing);
700
+ const whitespaceFollows = following.match(/^\s/);
701
+ const whitespacePrecedes = preceding.match(/\s$/);
702
+ let canOpen = !whitespaceFollows && (!punctuationFollows || !!whitespacePrecedes || !!punctuationPrecedes);
703
+ let canClose = !whitespacePrecedes && (!punctuationPrecedes || !!whitespaceFollows || !!punctuationFollows);
704
+ if (currentDelimiter === "_" && canOpen && canClose) {
705
+ canOpen = !!punctuationPrecedes;
706
+ canClose = !!punctuationFollows;
707
+ }
708
+ if (canClose) {
709
+ let stackPointer = stack.length - 1;
710
+ while (delimCount && stackPointer >= 0) {
711
+ if (stack[stackPointer].delimiter === currentDelimiter) {
712
+ while (stackPointer < stack.length - 1) {
713
+ const entry = stack.pop();
714
+ processed = `${entry.output}${entry.delimString.substr(0, entry.count)}${processed}`;
715
+ }
716
+ if (delimCount >= 2 && stack[stackPointer].count >= 2) {
717
+ processed = `<span class="TMMark">${currentDelimiter}${currentDelimiter}</span><strong class="TMStrong">${processed}</strong><span class="TMMark">${currentDelimiter}${currentDelimiter}</span>`;
718
+ delimCount -= 2;
719
+ stack[stackPointer].count -= 2;
720
+ }
721
+ else {
722
+ processed = `<span class="TMMark">${currentDelimiter}</span><em class="TMEm">${processed}</em><span class="TMMark">${currentDelimiter}</span>`;
723
+ delimCount -= 1;
724
+ stack[stackPointer].count -= 1;
725
+ }
726
+ if (stack[stackPointer].count === 0) {
727
+ let entry = stack.pop();
728
+ processed = `${entry.output}${processed}`;
729
+ stackPointer--;
730
+ }
731
+ }
732
+ else {
733
+ stackPointer--;
734
+ }
735
+ }
736
+ }
737
+ if (delimCount && canOpen) {
738
+ stack.push({
739
+ delimiter: currentDelimiter,
740
+ delimString: delimString,
741
+ count: delimCount,
742
+ output: processed,
743
+ });
744
+ processed = "";
745
+ delimCount = 0;
746
+ }
747
+ if (delimCount) {
748
+ processed = `${processed}${delimString.substr(0, delimCount)}`;
749
+ }
750
+ offset += cap[0].length;
751
+ continue outer;
752
+ }
753
+ // Check for strikethrough delimiter
754
+ cap = /^~~/.exec(string);
755
+ if (cap) {
756
+ let consumed = false;
757
+ let stackPointer = stack.length - 1;
758
+ while (!consumed && stackPointer >= 0) {
759
+ if (stack[stackPointer].delimiter === "~") {
760
+ while (stackPointer < stack.length - 1) {
761
+ const entry = stack.pop();
762
+ processed = `${entry.output}${entry.delimString.substr(0, entry.count)}${processed}`;
763
+ }
764
+ processed = `<span class="TMMark">~~</span><del class="TMStrikethrough">${processed}</del><span class="TMMark">~~</span>`;
765
+ let entry = stack.pop();
766
+ processed = `${entry.output}${processed}`;
767
+ consumed = true;
768
+ }
769
+ else {
770
+ stackPointer--;
771
+ }
772
+ }
773
+ if (!consumed) {
774
+ stack.push({
775
+ delimiter: "~",
776
+ delimString: "~~",
777
+ count: 2,
778
+ output: processed,
779
+ });
780
+ processed = "";
781
+ }
782
+ offset += cap[0].length;
783
+ string = string.substr(cap[0].length);
784
+ continue outer;
785
+ }
786
+ // Process 'default' rule
787
+ cap = this.mergedInlineGrammar.default.regexp.exec(string);
788
+ if (cap) {
789
+ string = string.substr(cap[0].length);
790
+ offset += cap[0].length;
791
+ processed += this.mergedInlineGrammar.default.replacement.replace(/\$([1-9])/g, (str, p1) => (0, grammar_1.htmlescape)(cap[p1]));
792
+ continue outer;
793
+ }
794
+ throw "Infinite loop!";
795
+ }
796
+ while (stack.length) {
797
+ const entry = stack.pop();
798
+ processed = `${entry.output}${entry.delimString.substr(0, entry.count)}${processed}`;
799
+ }
800
+ return processed;
801
+ }
802
+ computeColumn(startNode, offset) {
803
+ let node = startNode;
804
+ let col;
805
+ while (node && node.parentNode !== this.e) {
806
+ node = node.parentNode;
807
+ }
808
+ if (node === null)
809
+ return null;
810
+ if (startNode.nodeType === Node.TEXT_NODE || offset === 0) {
811
+ col = offset;
812
+ node = startNode;
813
+ }
814
+ else if (offset > 0) {
815
+ node = startNode.childNodes[offset - 1];
816
+ col = node.textContent.length;
817
+ }
818
+ else {
819
+ col = 0;
820
+ node = startNode;
821
+ }
822
+ while (node.parentNode !== this.e) {
823
+ if (node.previousSibling) {
824
+ node = node.previousSibling;
825
+ col += node.textContent.length;
826
+ }
827
+ else {
828
+ node = node.parentNode;
829
+ }
830
+ }
831
+ return col;
832
+ }
833
+ computeNodeAndOffset(row, col, bindRight = false) {
834
+ if (row >= this.lineElements.length) {
835
+ row = this.lineElements.length - 1;
836
+ col = this.lines[row].length;
837
+ }
838
+ if (col > this.lines[row].length) {
839
+ col = this.lines[row].length;
840
+ }
841
+ const parentNode = this.lineElements[row];
842
+ let node = parentNode.firstChild;
843
+ let childrenComplete = false;
844
+ let rv = {
845
+ node: parentNode.firstChild ? parentNode.firstChild : parentNode,
846
+ offset: 0,
847
+ };
848
+ while (node !== parentNode) {
849
+ if (!childrenComplete && node.nodeType === Node.TEXT_NODE) {
850
+ if (node.nodeValue.length >= col) {
851
+ if (bindRight && node.nodeValue.length === col) {
852
+ rv = { node: node, offset: col };
853
+ col = 0;
854
+ }
855
+ else {
856
+ return { node: node, offset: col };
857
+ }
858
+ }
859
+ else {
860
+ col -= node.nodeValue.length;
861
+ }
862
+ }
863
+ if (!childrenComplete && node.firstChild) {
864
+ node = node.firstChild;
865
+ }
866
+ else if (node.nextSibling) {
867
+ childrenComplete = false;
868
+ node = node.nextSibling;
869
+ }
870
+ else {
871
+ childrenComplete = true;
872
+ node = node.parentNode;
873
+ }
874
+ }
875
+ return rv;
876
+ }
877
+ updateLineContentsAndFormatting() {
878
+ this.clearDirtyFlag();
879
+ this.updateLineContents();
880
+ this.updateFormatting();
881
+ }
882
+ clearDirtyFlag() {
883
+ this.lineDirty = new Array(this.lines.length);
884
+ for (let i = 0; i < this.lineDirty.length; i++) {
885
+ this.lineDirty[i] = false;
886
+ }
887
+ }
888
+ updateLineContents() {
889
+ let lineDelta = this.e.childElementCount - this.lines.length;
890
+ if (lineDelta) {
891
+ let firstChangedLine = 0;
892
+ while (firstChangedLine <= this.lines.length &&
893
+ firstChangedLine <= this.lineElements.length &&
894
+ this.lineElements[firstChangedLine] &&
895
+ this.lines[firstChangedLine] === this.lineElements[firstChangedLine].textContent &&
896
+ this.lineTypes[firstChangedLine] === this.lineElements[firstChangedLine].className) {
897
+ firstChangedLine++;
898
+ }
899
+ let lastChangedLine = -1;
900
+ while (-lastChangedLine < this.lines.length &&
901
+ -lastChangedLine < this.lineElements.length &&
902
+ this.lines[this.lines.length + lastChangedLine] ===
903
+ this.lineElements[this.lineElements.length + lastChangedLine].textContent &&
904
+ this.lineTypes[this.lines.length + lastChangedLine] ===
905
+ this.lineElements[this.lineElements.length + lastChangedLine].className) {
906
+ lastChangedLine--;
907
+ }
908
+ let linesToDelete = this.lines.length + lastChangedLine + 1 - firstChangedLine;
909
+ if (linesToDelete < -lineDelta)
910
+ linesToDelete = -lineDelta;
911
+ if (linesToDelete < 0)
912
+ linesToDelete = 0;
913
+ let linesToAdd = [];
914
+ for (let l = 0; l < linesToDelete + lineDelta; l++) {
915
+ linesToAdd.push(this.lineElements[firstChangedLine + l].textContent || "");
916
+ }
917
+ this.spliceLines(firstChangedLine, linesToDelete, linesToAdd, false);
918
+ }
919
+ else {
920
+ for (let line = 0; line < this.lineElements.length; line++) {
921
+ let e = this.lineElements[line];
922
+ let ct = e.textContent || "";
923
+ if (this.lines[line] !== ct || this.lineTypes[line] !== e.className) {
924
+ this.lines[line] = ct;
925
+ this.lineTypes[line] = e.className;
926
+ this.lineDirty[line] = true;
927
+ }
928
+ }
929
+ }
930
+ }
931
+ spliceLines(startLine, linesToDelete = 0, linesToInsert = [], adjustLineElements = true) {
932
+ if (adjustLineElements) {
933
+ for (let i = 0; i < linesToDelete; i++) {
934
+ this.e.removeChild(this.e.childNodes[startLine]);
935
+ }
936
+ }
937
+ let insertedBlank = [];
938
+ let insertedDirty = [];
939
+ for (let i = 0; i < linesToInsert.length; i++) {
940
+ insertedBlank.push("");
941
+ insertedDirty.push(true);
942
+ if (adjustLineElements) {
943
+ if (this.e.childNodes[startLine])
944
+ this.e.insertBefore(document.createElement("div"), this.e.childNodes[startLine]);
945
+ else
946
+ this.e.appendChild(document.createElement("div"));
947
+ }
948
+ }
949
+ this.lines.splice(startLine, linesToDelete, ...linesToInsert);
950
+ this.lineTypes.splice(startLine, linesToDelete, ...insertedBlank);
951
+ this.lineDirty.splice(startLine, linesToDelete, ...insertedDirty);
952
+ }
953
+ fixNodeHierarchy() {
954
+ const originalChildren = Array.from(this.e.childNodes);
955
+ const replaceChild = (child, ...newChildren) => {
956
+ const parent = child.parentElement;
957
+ const nextSibling = child.nextSibling;
958
+ parent.removeChild(child);
959
+ newChildren.forEach((newChild) => nextSibling ? parent.insertBefore(newChild, nextSibling) : parent.appendChild(newChild));
960
+ };
961
+ originalChildren.forEach((child) => {
962
+ if (child.nodeType !== Node.ELEMENT_NODE || child.tagName !== "DIV") {
963
+ const divWrapper = document.createElement("div");
964
+ replaceChild(child, divWrapper);
965
+ divWrapper.appendChild(child);
966
+ }
967
+ else if (child.childNodes.length === 0) {
968
+ child.appendChild(document.createElement("br"));
969
+ }
970
+ else {
971
+ const grandChildren = Array.from(child.childNodes);
972
+ if (grandChildren.some((grandChild) => grandChild.nodeType === Node.ELEMENT_NODE && grandChild.tagName === "DIV")) {
973
+ return replaceChild(child, ...grandChildren);
974
+ }
975
+ }
976
+ });
977
+ }
978
+ parseLinkOrImage(originalString, isImage) {
979
+ // Skip the opening bracket
980
+ let textOffset = isImage ? 2 : 1;
981
+ let opener = originalString.substr(0, textOffset);
982
+ let type = isImage ? "TMImage" : "TMLink";
983
+ let currentOffset = textOffset;
984
+ let bracketLevel = 1;
985
+ let linkText = false;
986
+ let linkRef = false;
987
+ let linkLabel = [];
988
+ let linkDetails = [];
989
+ textOuter: while (currentOffset < originalString.length &&
990
+ linkText === false) {
991
+ let string = originalString.substr(currentOffset);
992
+ // Capture any escapes and code blocks at current position
993
+ for (let rule of ["escape", "code", "autolink", "html"]) {
994
+ let cap = this.mergedInlineGrammar[rule].regexp.exec(string);
995
+ if (cap) {
996
+ currentOffset += cap[0].length;
997
+ continue textOuter;
998
+ }
999
+ }
1000
+ // Check for image
1001
+ if (string.match(this.mergedInlineGrammar.imageOpen.regexp)) {
1002
+ bracketLevel++;
1003
+ currentOffset += 2;
1004
+ continue textOuter;
1005
+ }
1006
+ // Check for link
1007
+ if (string.match(this.mergedInlineGrammar.linkOpen.regexp)) {
1008
+ bracketLevel++;
1009
+ if (!isImage) {
1010
+ if (this.parseLinkOrImage(string, false)) {
1011
+ return false;
1012
+ }
1013
+ }
1014
+ currentOffset += 1;
1015
+ continue textOuter;
1016
+ }
1017
+ // Check for closing bracket
1018
+ if (string.match(/^\]/)) {
1019
+ bracketLevel--;
1020
+ if (bracketLevel === 0) {
1021
+ linkText = originalString.substr(textOffset, currentOffset - textOffset);
1022
+ currentOffset++;
1023
+ continue textOuter;
1024
+ }
1025
+ }
1026
+ // Nothing matches, proceed to next char
1027
+ currentOffset++;
1028
+ }
1029
+ // Did we find a link text?
1030
+ if (linkText === false)
1031
+ return false;
1032
+ // Check what type of link this is
1033
+ let nextChar = currentOffset < originalString.length ? originalString.substr(currentOffset, 1) : "";
1034
+ // REFERENCE LINKS
1035
+ if (nextChar === "[") {
1036
+ let string = originalString.substr(currentOffset);
1037
+ let cap = this.mergedInlineGrammar.linkLabel.regexp.exec(string);
1038
+ if (cap) {
1039
+ currentOffset += cap[0].length;
1040
+ linkLabel.push(cap[1], cap[2], cap[3]);
1041
+ if (cap[this.mergedInlineGrammar.linkLabel.labelPlaceholder]) {
1042
+ linkRef = cap[this.mergedInlineGrammar.linkLabel.labelPlaceholder];
1043
+ }
1044
+ else {
1045
+ linkRef = linkText.trim();
1046
+ }
1047
+ }
1048
+ else {
1049
+ return false;
1050
+ }
1051
+ }
1052
+ else if (nextChar !== "(") {
1053
+ // Shortcut ref link
1054
+ linkRef = linkText.trim();
1055
+ }
1056
+ else {
1057
+ // INLINE LINKS
1058
+ currentOffset++;
1059
+ let parenthesisLevel = 1;
1060
+ inlineOuter: while (currentOffset < originalString.length && parenthesisLevel > 0) {
1061
+ let string = originalString.substr(currentOffset);
1062
+ // Process whitespace
1063
+ let cap = /^\s+/.exec(string);
1064
+ if (cap) {
1065
+ switch (linkDetails.length) {
1066
+ case 0:
1067
+ linkDetails.push(cap[0]);
1068
+ break;
1069
+ case 1:
1070
+ linkDetails.push(cap[0]);
1071
+ break;
1072
+ case 2:
1073
+ if (linkDetails[0].match(/</)) {
1074
+ linkDetails[1] = linkDetails[1].concat(cap[0]);
1075
+ }
1076
+ else {
1077
+ if (parenthesisLevel !== 1)
1078
+ return false;
1079
+ linkDetails.push("");
1080
+ linkDetails.push(cap[0]);
1081
+ }
1082
+ break;
1083
+ case 3:
1084
+ linkDetails.push(cap[0]);
1085
+ break;
1086
+ case 4:
1087
+ return false;
1088
+ case 5:
1089
+ linkDetails.push("");
1090
+ case 6:
1091
+ linkDetails[5] = linkDetails[5].concat(cap[0]);
1092
+ break;
1093
+ case 7:
1094
+ linkDetails[6] = linkDetails[6].concat(cap[0]);
1095
+ break;
1096
+ default:
1097
+ return false;
1098
+ }
1099
+ currentOffset += cap[0].length;
1100
+ continue inlineOuter;
1101
+ }
1102
+ // Process backslash escapes
1103
+ cap = this.mergedInlineGrammar.escape.regexp.exec(string);
1104
+ if (cap) {
1105
+ switch (linkDetails.length) {
1106
+ case 0:
1107
+ linkDetails.push("");
1108
+ case 1:
1109
+ linkDetails.push(cap[0]);
1110
+ break;
1111
+ case 2:
1112
+ linkDetails[1] = linkDetails[1].concat(cap[0]);
1113
+ break;
1114
+ case 3:
1115
+ return false;
1116
+ case 4:
1117
+ return false;
1118
+ case 5:
1119
+ linkDetails.push("");
1120
+ case 6:
1121
+ linkDetails[5] = linkDetails[5].concat(cap[0]);
1122
+ break;
1123
+ default:
1124
+ return false;
1125
+ }
1126
+ currentOffset += cap[0].length;
1127
+ continue inlineOuter;
1128
+ }
1129
+ // Process opening angle bracket
1130
+ if (linkDetails.length < 2 && string.match(/^</)) {
1131
+ if (linkDetails.length === 0)
1132
+ linkDetails.push("");
1133
+ linkDetails[0] = linkDetails[0].concat("<");
1134
+ currentOffset++;
1135
+ continue inlineOuter;
1136
+ }
1137
+ // Process closing angle bracket
1138
+ if ((linkDetails.length === 1 || linkDetails.length === 2) && string.match(/^>/)) {
1139
+ if (linkDetails.length === 1)
1140
+ linkDetails.push("");
1141
+ linkDetails.push(">");
1142
+ currentOffset++;
1143
+ continue inlineOuter;
1144
+ }
1145
+ // Process non-parenthesis delimiter for title
1146
+ cap = /^["']/.exec(string);
1147
+ if (cap && (linkDetails.length === 0 || linkDetails.length === 1 || linkDetails.length === 4)) {
1148
+ while (linkDetails.length < 4)
1149
+ linkDetails.push("");
1150
+ linkDetails.push(cap[0]);
1151
+ currentOffset++;
1152
+ continue inlineOuter;
1153
+ }
1154
+ if (cap && (linkDetails.length === 5 || linkDetails.length === 6) && linkDetails[4] === cap[0]) {
1155
+ if (linkDetails.length === 5)
1156
+ linkDetails.push("");
1157
+ linkDetails.push(cap[0]);
1158
+ currentOffset++;
1159
+ continue inlineOuter;
1160
+ }
1161
+ // Process opening parenthesis
1162
+ if (string.match(/^\(/)) {
1163
+ switch (linkDetails.length) {
1164
+ case 0:
1165
+ linkDetails.push("");
1166
+ case 1:
1167
+ linkDetails.push("");
1168
+ case 2:
1169
+ linkDetails[1] = linkDetails[1].concat("(");
1170
+ if (!linkDetails[0].match(/<$/))
1171
+ parenthesisLevel++;
1172
+ break;
1173
+ case 3:
1174
+ linkDetails.push("");
1175
+ case 4:
1176
+ linkDetails.push("(");
1177
+ break;
1178
+ case 5:
1179
+ linkDetails.push("");
1180
+ case 6:
1181
+ if (linkDetails[4] === "(")
1182
+ return false;
1183
+ linkDetails[5] = linkDetails[5].concat("(");
1184
+ break;
1185
+ default:
1186
+ return false;
1187
+ }
1188
+ currentOffset++;
1189
+ continue inlineOuter;
1190
+ }
1191
+ // Process closing parenthesis
1192
+ if (string.match(/^\)/)) {
1193
+ if (linkDetails.length <= 2) {
1194
+ while (linkDetails.length < 2)
1195
+ linkDetails.push("");
1196
+ if (!linkDetails[0].match(/<$/))
1197
+ parenthesisLevel--;
1198
+ if (parenthesisLevel > 0) {
1199
+ linkDetails[1] = linkDetails[1].concat(")");
1200
+ }
1201
+ }
1202
+ else if (linkDetails.length === 5 || linkDetails.length === 6) {
1203
+ if (linkDetails[4] === "(") {
1204
+ if (linkDetails.length === 5)
1205
+ linkDetails.push("");
1206
+ linkDetails.push(")");
1207
+ }
1208
+ else {
1209
+ if (linkDetails.length === 5)
1210
+ linkDetails.push(")");
1211
+ else
1212
+ linkDetails[5] = linkDetails[5].concat(")");
1213
+ }
1214
+ }
1215
+ else {
1216
+ parenthesisLevel--;
1217
+ }
1218
+ if (parenthesisLevel === 0) {
1219
+ while (linkDetails.length < 7)
1220
+ linkDetails.push("");
1221
+ }
1222
+ currentOffset++;
1223
+ continue inlineOuter;
1224
+ }
1225
+ // Any old character
1226
+ cap = /^./.exec(string);
1227
+ if (cap) {
1228
+ switch (linkDetails.length) {
1229
+ case 0:
1230
+ linkDetails.push("");
1231
+ case 1:
1232
+ linkDetails.push(cap[0]);
1233
+ break;
1234
+ case 2:
1235
+ linkDetails[1] = linkDetails[1].concat(cap[0]);
1236
+ break;
1237
+ case 3:
1238
+ return false;
1239
+ case 4:
1240
+ return false;
1241
+ case 5:
1242
+ linkDetails.push("");
1243
+ case 6:
1244
+ linkDetails[5] = linkDetails[5].concat(cap[0]);
1245
+ break;
1246
+ default:
1247
+ return false;
1248
+ }
1249
+ currentOffset += cap[0].length;
1250
+ continue inlineOuter;
1251
+ }
1252
+ throw "Infinite loop";
1253
+ }
1254
+ if (parenthesisLevel > 0)
1255
+ return false;
1256
+ }
1257
+ if (linkRef !== false) {
1258
+ // Reference link; check that linkRef is valid
1259
+ let valid = false;
1260
+ for (let label of this.linkLabels) {
1261
+ if (label === linkRef) {
1262
+ valid = true;
1263
+ break;
1264
+ }
1265
+ }
1266
+ let labelClass = valid ? "TMLinkLabel TMLinkLabel_Valid" : "TMLinkLabel TMLinkLabel_Invalid";
1267
+ let output = `<span class="TMMark TMMark_${type}">${opener}</span><span class="${type} ${linkLabel.length < 3 || !linkLabel[1] ? labelClass : ""}">${this.processInlineStyles(linkText)}</span><span class="TMMark TMMark_${type}">]</span>`;
1268
+ if (linkLabel.length >= 3) {
1269
+ output = output.concat(`<span class="TMMark TMMark_${type}">${linkLabel[0]}</span>`, `<span class="${labelClass}">${linkLabel[1]}</span>`, `<span class="TMMark TMMark_${type}">${linkLabel[2]}</span>`);
1270
+ }
1271
+ return {
1272
+ output: output,
1273
+ charCount: currentOffset,
1274
+ };
1275
+ }
1276
+ else if (linkDetails.length > 0) {
1277
+ // Inline link
1278
+ while (linkDetails.length < 7) {
1279
+ linkDetails.push("");
1280
+ }
1281
+ return {
1282
+ output: `<span class="TMMark TMMark_${type}">${opener}</span><span class="${type}">${this.processInlineStyles(linkText)}</span><span class="TMMark TMMark_${type}">](${linkDetails[0]}</span><span class="${type}Destination">${linkDetails[1]}</span><span class="TMMark TMMark_${type}">${linkDetails[2]}${linkDetails[3]}${linkDetails[4]}</span><span class="${type}Title">${linkDetails[5]}</span><span class="TMMark TMMark_${type}">${linkDetails[6]})</span>`,
1283
+ charCount: currentOffset,
1284
+ };
1285
+ }
1286
+ return false;
1287
+ }
1288
+ computeCommonAncestor(node1, node2) {
1289
+ if (!node1 || !node2)
1290
+ return null;
1291
+ if (node1 === node2)
1292
+ return node1;
1293
+ const ancestry = (node) => {
1294
+ let ancestry = [];
1295
+ while (node) {
1296
+ ancestry.unshift(node);
1297
+ node = node.parentNode;
1298
+ }
1299
+ return ancestry;
1300
+ };
1301
+ const ancestry1 = ancestry(node1);
1302
+ const ancestry2 = ancestry(node2);
1303
+ if (ancestry1[0] !== ancestry2[0])
1304
+ return null;
1305
+ let i;
1306
+ for (i = 0; ancestry1[i] === ancestry2[i]; i++)
1307
+ ;
1308
+ return ancestry1[i - 1];
1309
+ }
1310
+ computeEnclosingMarkupNode(focus, anchor, className) {
1311
+ let node = null;
1312
+ if (!focus)
1313
+ return null;
1314
+ if (!anchor) {
1315
+ const sel = window.getSelection();
1316
+ if (!sel || !sel.focusNode)
1317
+ return null;
1318
+ node = sel.focusNode;
1319
+ }
1320
+ else {
1321
+ if (focus.row !== anchor.row)
1322
+ return null;
1323
+ const sel = window.getSelection();
1324
+ if (!sel)
1325
+ return null;
1326
+ node = this.computeCommonAncestor(sel.focusNode, sel.anchorNode);
1327
+ }
1328
+ if (!node)
1329
+ return null;
1330
+ while (node !== this.e) {
1331
+ if (node.className && node.className.includes(className))
1332
+ return node;
1333
+ node = node.parentNode;
1334
+ }
1335
+ return null;
1336
+ }
1337
+ getCommandState(focus = null, anchor = null) {
1338
+ let commandState = {};
1339
+ if (!focus)
1340
+ focus = this.getSelection(false);
1341
+ if (!anchor)
1342
+ anchor = this.getSelection(true);
1343
+ if (!focus) {
1344
+ for (let cmd in grammar_1.commands) {
1345
+ commandState[cmd] = null;
1346
+ }
1347
+ return commandState;
1348
+ }
1349
+ if (!anchor)
1350
+ anchor = focus;
1351
+ let start, end;
1352
+ if (anchor.row < focus.row || (anchor.row === focus.row && anchor.col < focus.col)) {
1353
+ start = anchor;
1354
+ end = focus;
1355
+ }
1356
+ else {
1357
+ start = focus;
1358
+ end = anchor;
1359
+ }
1360
+ if (end.row > start.row && end.col === 0) {
1361
+ end.row--;
1362
+ end.col = this.lines[end.row].length;
1363
+ }
1364
+ for (let cmd in grammar_1.commands) {
1365
+ if (grammar_1.commands[cmd].type === "inline") {
1366
+ if (!focus || focus.row !== anchor.row || !this.isInlineFormattingAllowed()) {
1367
+ commandState[cmd] = null;
1368
+ }
1369
+ else {
1370
+ commandState[cmd] =
1371
+ !!this.computeEnclosingMarkupNode(focus, anchor, grammar_1.commands[cmd].className) ||
1372
+ (focus.col === anchor.col &&
1373
+ !!this.lines[focus.row].substr(0, focus.col).match(grammar_1.commands[cmd].unset.prePattern) &&
1374
+ !!this.lines[focus.row].substr(focus.col).match(grammar_1.commands[cmd].unset.postPattern));
1375
+ }
1376
+ }
1377
+ if (grammar_1.commands[cmd].type === "line") {
1378
+ if (!focus) {
1379
+ commandState[cmd] = null;
1380
+ }
1381
+ else {
1382
+ let state = this.lineTypes[start.row] === grammar_1.commands[cmd].className;
1383
+ for (let line = start.row; line <= end.row; line++) {
1384
+ if ((this.lineTypes[line] === grammar_1.commands[cmd].className) !== state) {
1385
+ state = null;
1386
+ break;
1387
+ }
1388
+ }
1389
+ commandState[cmd] = state;
1390
+ }
1391
+ }
1392
+ }
1393
+ return commandState;
1394
+ }
1395
+ setCommandState(command, state) {
1396
+ if (!this.isRestoringHistory)
1397
+ this.pushHistory();
1398
+ if (grammar_1.commands[command].type === "inline") {
1399
+ let anchor = this.getSelection(true);
1400
+ let focus = this.getSelection(false);
1401
+ if (!anchor)
1402
+ anchor = focus;
1403
+ if (!anchor)
1404
+ return;
1405
+ if (anchor.row !== focus.row)
1406
+ return;
1407
+ if (!this.isInlineFormattingAllowed())
1408
+ return;
1409
+ let markupNode = this.computeEnclosingMarkupNode(focus, anchor, grammar_1.commands[command].className);
1410
+ this.clearDirtyFlag();
1411
+ if (markupNode) {
1412
+ this.lineDirty[focus.row] = true;
1413
+ const startCol = this.computeColumn(markupNode, 0);
1414
+ const len = markupNode.textContent.length;
1415
+ const left = this.lines[focus.row]
1416
+ .substr(0, startCol)
1417
+ .replace(grammar_1.commands[command].unset.prePattern, "");
1418
+ const mid = this.lines[focus.row].substr(startCol, len);
1419
+ const right = this.lines[focus.row]
1420
+ .substr(startCol + len)
1421
+ .replace(grammar_1.commands[command].unset.postPattern, "");
1422
+ this.lines[focus.row] = left.concat(mid, right);
1423
+ anchor.col = left.length;
1424
+ focus.col = anchor.col + len;
1425
+ this.updateFormatting();
1426
+ this.setSelection(focus, anchor);
1427
+ this.fireChange();
1428
+ }
1429
+ else if (focus.col === anchor.col &&
1430
+ !!this.lines[focus.row].substr(0, focus.col).match(grammar_1.commands[command].unset.prePattern) &&
1431
+ !!this.lines[focus.row].substr(focus.col).match(grammar_1.commands[command].unset.postPattern)) {
1432
+ this.lineDirty[focus.row] = true;
1433
+ const left = this.lines[focus.row]
1434
+ .substr(0, focus.col)
1435
+ .replace(grammar_1.commands[command].unset.prePattern, "");
1436
+ const right = this.lines[focus.row]
1437
+ .substr(focus.col)
1438
+ .replace(grammar_1.commands[command].unset.postPattern, "");
1439
+ this.lines[focus.row] = left.concat(right);
1440
+ focus.col = anchor.col = left.length;
1441
+ this.updateFormatting();
1442
+ this.setSelection(focus, anchor);
1443
+ this.fireChange();
1444
+ }
1445
+ else {
1446
+ let { startCol, endCol } = focus.col < anchor.col
1447
+ ? { startCol: focus.col, endCol: anchor.col }
1448
+ : { startCol: anchor.col, endCol: focus.col };
1449
+ let match = this.lines[focus.row]
1450
+ .substr(startCol, endCol - startCol)
1451
+ .match(/^(?<leading>\s*).*\S(?<trailing>\s*)$/);
1452
+ if (match) {
1453
+ startCol += match.groups.leading.length;
1454
+ endCol -= match.groups.trailing.length;
1455
+ }
1456
+ focus.col = startCol;
1457
+ anchor.col = endCol;
1458
+ this.wrapSelection(grammar_1.commands[command].set.pre, grammar_1.commands[command].set.post, focus, anchor);
1459
+ this.fireChange();
1460
+ }
1461
+ }
1462
+ else if (grammar_1.commands[command].type === "line") {
1463
+ let anchor = this.getSelection(true);
1464
+ let focus = this.getSelection(false);
1465
+ if (!anchor)
1466
+ anchor = focus;
1467
+ if (!focus)
1468
+ return;
1469
+ this.clearDirtyFlag();
1470
+ let start = anchor.row > focus.row ? focus : anchor;
1471
+ let end = anchor.row > focus.row ? anchor : focus;
1472
+ if (end.row > start.row && end.col === 0) {
1473
+ end.row--;
1474
+ }
1475
+ for (let line = start.row; line <= end.row; line++) {
1476
+ if (state && this.lineTypes[line] !== grammar_1.commands[command].className) {
1477
+ this.lines[line] = this.lines[line].replace(grammar_1.commands[command].set.pattern, grammar_1.commands[command].set.replacement.replace("$#", (line - start.row + 1).toString()));
1478
+ this.lineDirty[line] = true;
1479
+ }
1480
+ if (!state && this.lineTypes[line] === grammar_1.commands[command].className) {
1481
+ this.lines[line] = this.lines[line].replace(grammar_1.commands[command].unset.pattern, grammar_1.commands[command].unset.replacement);
1482
+ this.lineDirty[line] = true;
1483
+ }
1484
+ }
1485
+ this.updateFormatting();
1486
+ this.setSelection({ row: end.row, col: this.lines[end.row].length }, { row: start.row, col: 0 });
1487
+ this.fireChange();
1488
+ }
1489
+ }
1490
+ isInlineFormattingAllowed() {
1491
+ const sel = window.getSelection();
1492
+ if (!sel || !sel.focusNode || !sel.anchorNode)
1493
+ return false;
1494
+ if (sel.isCollapsed &&
1495
+ sel.focusNode.nodeType === 3 &&
1496
+ sel.focusOffset === sel.focusNode.nodeValue.length) {
1497
+ let node;
1498
+ for (node = sel.focusNode; node && node.nextSibling === null; node = node.parentNode)
1499
+ ;
1500
+ if (node &&
1501
+ node.nextSibling &&
1502
+ node.nextSibling.className &&
1503
+ node.nextSibling.className.includes("TMInlineFormatted"))
1504
+ return true;
1505
+ }
1506
+ let ancestor = this.computeCommonAncestor(sel.focusNode, sel.anchorNode);
1507
+ if (!ancestor)
1508
+ return false;
1509
+ while (ancestor && ancestor !== this.e) {
1510
+ if (ancestor.className &&
1511
+ typeof ancestor.className.includes === "function" &&
1512
+ (ancestor.className.includes("TMInlineFormatted") ||
1513
+ ancestor.className.includes("TMBlankLine")))
1514
+ return true;
1515
+ ancestor = ancestor.parentNode;
1516
+ }
1517
+ return false;
1518
+ }
1519
+ toggleCommandState(command) {
1520
+ if (!this.lastCommandState)
1521
+ this.lastCommandState = this.getCommandState();
1522
+ this.setCommandState(command, !this.lastCommandState[command]);
1523
+ }
1524
+ fireDrop(dataTransfer) {
1525
+ for (let listener of this.listeners.drop) {
1526
+ listener({ dataTransfer });
1527
+ }
1528
+ }
1529
+ fireSelection() {
1530
+ if (this.listeners.selection && this.listeners.selection.length) {
1531
+ let focus = this.getSelection(false);
1532
+ let anchor = this.getSelection(true);
1533
+ let commandState = this.getCommandState(focus, anchor);
1534
+ if (this.lastCommandState) {
1535
+ Object.assign(this.lastCommandState, commandState);
1536
+ }
1537
+ else {
1538
+ this.lastCommandState = Object.assign({}, commandState);
1539
+ }
1540
+ for (let listener of this.listeners.selection) {
1541
+ listener({
1542
+ focus: focus,
1543
+ anchor: anchor,
1544
+ commandState: this.lastCommandState,
1545
+ });
1546
+ }
1547
+ }
1548
+ }
1549
+ }
1550
+ exports.Editor = Editor;
1551
+ exports.default = Editor;