tiny-markdown-editor 0.2.3 → 0.2.5

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