tiny-markdown-editor 0.2.2 → 0.2.4

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