@helping-ai-workflow/md2doc 3.17.4 → 3.17.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/editor/inline-md.js +138 -21
- package/lib/editor/paste-md.js +135 -0
- package/package.json +1 -1
package/lib/editor/inline-md.js
CHANGED
|
@@ -171,6 +171,10 @@
|
|
|
171
171
|
return '[' + label + '](' + (href || '') + ')';
|
|
172
172
|
}
|
|
173
173
|
|
|
174
|
+
// (What a mark's own edges must look like -- whitespace and punctuation
|
|
175
|
+
// just inside its delimiters -- is a separate rule, v3.17.5, written above
|
|
176
|
+
// `splitEdges` / `appendPiece` further down.)
|
|
177
|
+
//
|
|
174
178
|
// v3.4.0 / backlog 3 (Ruling T6-7, then a fix-round regression): STRONG
|
|
175
179
|
// and EM each emit a fixed, context-blind '*'-based delimiter for their
|
|
176
180
|
// own tag — '**'/'**' or '*'/'*' — with no awareness of what the
|
|
@@ -267,6 +271,116 @@
|
|
|
267
271
|
// STRONG, DEL/S, U, the citation label) only ever needs `.md`, because
|
|
268
272
|
// its own outer delimiter is a fixed, known tag regardless of what's
|
|
269
273
|
// nested inside.
|
|
274
|
+
// v3.17.5 M1 + R1: a delimiter run only counts when CommonMark's flanking
|
|
275
|
+
// rule accepts it, and STRONG / EM / DEL used to wrap their content blind.
|
|
276
|
+
// Measured on 4d7669f, all three rendered literal asterisks in the reader:
|
|
277
|
+
// Delete down to a leading space `alpha ** charlie** delta`
|
|
278
|
+
// Ctrl+B on 「引號」 `前面**「引號」**後面`
|
|
279
|
+
// Ctrl+B on `charlie ` (with space) `alpha **charlie **delta`
|
|
280
|
+
// The rule, in marked 14's own classes (whitespace = JS `\s`, punctuation =
|
|
281
|
+
// `\p{P}\p{S}`): the character just INSIDE a run may not be whitespace, and
|
|
282
|
+
// when it is punctuation the character just OUTSIDE must be whitespace,
|
|
283
|
+
// punctuation or the edge of the text.
|
|
284
|
+
//
|
|
285
|
+
// Two fixes, one per half of the rule:
|
|
286
|
+
// * whitespace inside -> moved outside (`splitEdges`), byte for byte, so
|
|
287
|
+
// ` charlie` in bold becomes ` **charlie**`; a mark holding nothing
|
|
288
|
+
// else is no mark at all. A hard break (`\` + newline) at an edge
|
|
289
|
+
// moves out whole: peeling only its newline would leave the backslash
|
|
290
|
+
// escaping the delimiter.
|
|
291
|
+
// * punctuation inside, a word character outside -> `<!-- -->` between
|
|
292
|
+
// them (`appendPiece`). The comment is the same inert separator the
|
|
293
|
+
// same-tag guard above uses; marked masks an HTML tag as punctuation,
|
|
294
|
+
// so the run then has punctuation on both sides, which the rule allows.
|
|
295
|
+
// Only `*` runs need it: marked's `~~` is not punctuation-flanking
|
|
296
|
+
// (`~~中文。~~後面` parses), and `_` is never emitted.
|
|
297
|
+
// The punctuation check runs where two pieces are joined, on the bytes
|
|
298
|
+
// themselves, because the character outside a run may belong to a sibling,
|
|
299
|
+
// or to a parent's sibling when the run starts or ends a bare SPAN: every
|
|
300
|
+
// level joins its own pieces, so a run at the edge of a nested call is
|
|
301
|
+
// checked again by its parent. A run's inside is found by skipping its bare
|
|
302
|
+
// `*`s: escapeText() escapes every literal `*`, so a `*` at either end of a
|
|
303
|
+
// piece is a delimiter unless an odd run of backslashes precedes it.
|
|
304
|
+
const FLANK_WS = /\s/;
|
|
305
|
+
const FLANK_PUNCT = /[\p{P}\p{S}]/u;
|
|
306
|
+
function isWordish(ch) {
|
|
307
|
+
return ch !== undefined && !FLANK_WS.test(ch) && !FLANK_PUNCT.test(ch);
|
|
308
|
+
}
|
|
309
|
+
function lastCodePoint(s, end) {
|
|
310
|
+
if (end <= 0) return undefined;
|
|
311
|
+
const lo = s.charCodeAt(end - 1);
|
|
312
|
+
if (lo >= 0xdc00 && lo <= 0xdfff && end >= 2) {
|
|
313
|
+
const hi = s.charCodeAt(end - 2);
|
|
314
|
+
if (hi >= 0xd800 && hi <= 0xdbff) return s.slice(end - 2, end);
|
|
315
|
+
}
|
|
316
|
+
return s[end - 1];
|
|
317
|
+
}
|
|
318
|
+
function firstCodePoint(s, start) {
|
|
319
|
+
if (start >= s.length) return undefined;
|
|
320
|
+
return String.fromCodePoint(s.codePointAt(start));
|
|
321
|
+
}
|
|
322
|
+
// The character just inside a leading `*` run, or undefined when `s` does
|
|
323
|
+
// not open with one.
|
|
324
|
+
function openingRunInside(s) {
|
|
325
|
+
let i = 0;
|
|
326
|
+
while (s[i] === '*') i++;
|
|
327
|
+
return i ? firstCodePoint(s, i) : undefined;
|
|
328
|
+
}
|
|
329
|
+
// The character just inside a trailing `*` run, or undefined when `s` does
|
|
330
|
+
// not close with one.
|
|
331
|
+
function closingRunInside(s) {
|
|
332
|
+
let k = 0;
|
|
333
|
+
while (k < s.length && s[s.length - 1 - k] === '*') k++;
|
|
334
|
+
if (!k) return undefined;
|
|
335
|
+
let b = 0;
|
|
336
|
+
while (s[s.length - 1 - k - b] === '\\') b++;
|
|
337
|
+
if (b % 2) return k > 1 ? '*' : undefined; // the run's first `*` is an escaped literal
|
|
338
|
+
return lastCodePoint(s, s.length - k);
|
|
339
|
+
}
|
|
340
|
+
// Two v3.17.5 final-review findings shape the outside characters:
|
|
341
|
+
// * I2 -- marked reads the character before an opening run as ONE UTF-16
|
|
342
|
+
// unit (`token.raw.slice(-1)`), so an astral symbol there (📌, \p{So})
|
|
343
|
+
// is a lone surrogate to it, i.e. a word character. Match that.
|
|
344
|
+
// * I1 -- a closing run touching the next piece's opening run fuses with
|
|
345
|
+
// it (`**注意:***斜體*`); the character after the fused run is the one
|
|
346
|
+
// inside the right piece's run, not its `*`. The opening side needs no
|
|
347
|
+
// such care: once the left run has closed, marked sees a `*`
|
|
348
|
+
// (punctuation) before the rest, which the rule accepts.
|
|
349
|
+
function needsFlankSeparator(left, right) {
|
|
350
|
+
if (!left || !right) return false;
|
|
351
|
+
const open = openingRunInside(right);
|
|
352
|
+
if (open !== undefined && FLANK_PUNCT.test(open) && isWordish(left[left.length - 1])) return true;
|
|
353
|
+
const close = closingRunInside(left);
|
|
354
|
+
const after = right[0] === '*' ? openingRunInside(right) : firstCodePoint(right, 0);
|
|
355
|
+
return close !== undefined && FLANK_PUNCT.test(close) && isWordish(after);
|
|
356
|
+
}
|
|
357
|
+
function appendPiece(out, piece) {
|
|
358
|
+
return out + (needsFlankSeparator(out, piece) ? '<!-- -->' : '') + piece;
|
|
359
|
+
}
|
|
360
|
+
// `md` -> { lead, core, trail }: whitespace (and whole hard breaks) peeled
|
|
361
|
+
// off both ends of a mark's content.
|
|
362
|
+
function splitEdges(md) {
|
|
363
|
+
let start = 0;
|
|
364
|
+
let end = md.length;
|
|
365
|
+
for (;;) {
|
|
366
|
+
if (start < end && FLANK_WS.test(md[start])) { start++; continue; }
|
|
367
|
+
// a leading `\` is always a hard break's: a literal one is `\\`
|
|
368
|
+
if (md[start] === '\\' && md[start + 1] === '\n' && start + 2 <= end) { start += 2; continue; }
|
|
369
|
+
break;
|
|
370
|
+
}
|
|
371
|
+
for (;;) {
|
|
372
|
+
if (end > start && md[end - 1] === '\n') {
|
|
373
|
+
let b = 0;
|
|
374
|
+
while (end - 2 - b >= start && md[end - 2 - b] === '\\') b++;
|
|
375
|
+
end -= b % 2 ? 2 : 1;
|
|
376
|
+
continue;
|
|
377
|
+
}
|
|
378
|
+
if (end > start && FLANK_WS.test(md[end - 1])) { end--; continue; }
|
|
379
|
+
break;
|
|
380
|
+
}
|
|
381
|
+
return { lead: md.slice(0, start), core: md.slice(start, end), trail: md.slice(end) };
|
|
382
|
+
}
|
|
383
|
+
|
|
270
384
|
function markIsSearchHit(node) {
|
|
271
385
|
if (typeof node.getAttribute !== 'function') return false;
|
|
272
386
|
return (' ' + (node.getAttribute('class') || '') + ' ').indexOf(' search-hit ') !== -1;
|
|
@@ -279,7 +393,7 @@
|
|
|
279
393
|
const node = nodes[i];
|
|
280
394
|
if (node.nodeType === 3) {
|
|
281
395
|
const text = escapeText(node.textContent);
|
|
282
|
-
out
|
|
396
|
+
out = appendPiece(out, text);
|
|
283
397
|
if (text.length) lastMarkTag = null;
|
|
284
398
|
firstSegment = false;
|
|
285
399
|
continue;
|
|
@@ -291,7 +405,7 @@
|
|
|
291
405
|
if (!firstSegment) added += '<br>';
|
|
292
406
|
const inner = walkChildren(node.childNodes, unsupported);
|
|
293
407
|
added += inner.md;
|
|
294
|
-
out
|
|
408
|
+
out = appendPiece(out, added);
|
|
295
409
|
if (added.length) lastMarkTag = inner.md.length ? inner.lastMarkTag : null;
|
|
296
410
|
firstSegment = false;
|
|
297
411
|
continue;
|
|
@@ -300,7 +414,7 @@
|
|
|
300
414
|
// v3.14.0 T9: the reader's search paints its hits as this element and
|
|
301
415
|
// it is transient, so it serializes as what it wraps.
|
|
302
416
|
const inner = walkChildren(node.childNodes, unsupported);
|
|
303
|
-
out
|
|
417
|
+
out = appendPiece(out, inner.md);
|
|
304
418
|
if (inner.md.length) lastMarkTag = inner.lastMarkTag;
|
|
305
419
|
firstSegment = false;
|
|
306
420
|
continue;
|
|
@@ -310,23 +424,25 @@
|
|
|
310
424
|
unsupported.push(name);
|
|
311
425
|
} else {
|
|
312
426
|
const inner = walkChildren(node.childNodes, unsupported);
|
|
313
|
-
out
|
|
427
|
+
out = appendPiece(out, inner.md);
|
|
314
428
|
if (inner.md.length) lastMarkTag = inner.lastMarkTag;
|
|
315
429
|
}
|
|
316
430
|
firstSegment = false;
|
|
317
431
|
continue;
|
|
318
432
|
}
|
|
319
|
-
if (name === 'STRONG' || name === 'B') {
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
433
|
+
if (name === 'STRONG' || name === 'B' || name === 'EM' || name === 'I') {
|
|
434
|
+
const tag = (name === 'STRONG' || name === 'B') ? 'STRONG' : 'EM';
|
|
435
|
+
const delim = tag === 'STRONG' ? '**' : '*';
|
|
436
|
+
const e = splitEdges(walkChildren(node.childNodes, unsupported).md);
|
|
437
|
+
if (e.core) {
|
|
438
|
+
// a hoisted leading space already separates two same-tag runs
|
|
439
|
+
const guard = !e.lead && lastMarkTag === tag ? '<!-- -->' : '';
|
|
440
|
+
out = appendPiece(out, e.lead + guard + delim + e.core + delim + e.trail);
|
|
441
|
+
lastMarkTag = e.trail ? null : tag;
|
|
442
|
+
} else if (e.lead || e.trail) {
|
|
443
|
+
out = appendPiece(out, e.lead + e.trail);
|
|
444
|
+
lastMarkTag = null;
|
|
445
|
+
}
|
|
330
446
|
firstSegment = false;
|
|
331
447
|
continue;
|
|
332
448
|
}
|
|
@@ -336,8 +452,9 @@
|
|
|
336
452
|
// accepted on input (some contenteditable/paste paths produce it)
|
|
337
453
|
// but the toolbar/serializer always speak DEL, matching what marked
|
|
338
454
|
// itself emits.
|
|
339
|
-
|
|
340
|
-
|
|
455
|
+
const e = splitEdges(walkChildren(node.childNodes, unsupported).md);
|
|
456
|
+
out = appendPiece(out, e.core ? e.lead + '~~' + e.core + '~~' + e.trail : e.lead + e.trail);
|
|
457
|
+
if (e.core || e.lead || e.trail) lastMarkTag = null;
|
|
341
458
|
firstSegment = false;
|
|
342
459
|
continue;
|
|
343
460
|
}
|
|
@@ -352,19 +469,19 @@
|
|
|
352
469
|
// backtick, literal `<u>` typed as plain text is already escaped by
|
|
353
470
|
// the `<` -> `<` rule above, so it can never collide with a real
|
|
354
471
|
// toolbar-made underline.
|
|
355
|
-
out
|
|
472
|
+
out = appendPiece(out, '<u>' + walkChildren(node.childNodes, unsupported).md + '</u>');
|
|
356
473
|
lastMarkTag = null;
|
|
357
474
|
firstSegment = false;
|
|
358
475
|
continue;
|
|
359
476
|
}
|
|
360
477
|
if (name === 'CODE') {
|
|
361
|
-
out
|
|
478
|
+
out = appendPiece(out, serializeCode(node));
|
|
362
479
|
lastMarkTag = null;
|
|
363
480
|
firstSegment = false;
|
|
364
481
|
continue;
|
|
365
482
|
}
|
|
366
483
|
if (name === 'A') {
|
|
367
|
-
out
|
|
484
|
+
out = appendPiece(out, serializeAnchor(node, unsupported));
|
|
368
485
|
lastMarkTag = null;
|
|
369
486
|
firstSegment = false;
|
|
370
487
|
continue;
|
|
@@ -402,7 +519,7 @@
|
|
|
402
519
|
// reach this code.
|
|
403
520
|
const hardBreak = typeof node.getAttribute === 'function' &&
|
|
404
521
|
node.getAttribute('data-hard-break') === '1';
|
|
405
|
-
out
|
|
522
|
+
out = appendPiece(out, hardBreak ? '\\\n' : '<br>');
|
|
406
523
|
lastMarkTag = null;
|
|
407
524
|
firstSegment = false;
|
|
408
525
|
continue;
|
package/lib/editor/paste-md.js
CHANGED
|
@@ -55,6 +55,140 @@ function addStrikethroughRule(service) {
|
|
|
55
55
|
});
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
// --- bold / italic next to punctuation (v3.17.5 R2) -------------------------
|
|
59
|
+
//
|
|
60
|
+
// CommonMark only takes a `*` run whose inside character, when it is
|
|
61
|
+
// punctuation (marked 14: \p{P}\p{S}), has whitespace, punctuation or the edge
|
|
62
|
+
// of the text on its outside. turndown's own strong / emphasis rules wrap
|
|
63
|
+
// blind, so a pasted `前面<b>「引號」</b>後面` became `前面**「引號」**後面` and
|
|
64
|
+
// the reader showed the asterisks. These two rules replace them and put
|
|
65
|
+
// `<!-- -->` (marked reads an HTML tag as punctuation) on a side that needs
|
|
66
|
+
// it -- the same separator, for the same rule, as walkChildren() in
|
|
67
|
+
// inline-md.js; the classes below are that file's FLANK_WS / FLANK_PUNCT.
|
|
68
|
+
// Whitespace at the edges never reaches them: turndown moves it outside
|
|
69
|
+
// first (`node.flankingWhitespace`), and a side that got some needs nothing.
|
|
70
|
+
//
|
|
71
|
+
// The outside character is read from the DOM, because the markdown around
|
|
72
|
+
// this node does not exist yet: the nearest text before / after it within the
|
|
73
|
+
// block, through bare inline wrappers. A neighbour that writes a delimiter or
|
|
74
|
+
// a bracket of its own there (a parent <b>, <a>, <code>...) is punctuation; an
|
|
75
|
+
// <img>, <br> or <hr> writes `)` or whitespace. Any other element with no text
|
|
76
|
+
// (a Word bookmark `<a name>`, an empty <span>, `<o:p>`) writes nothing and is
|
|
77
|
+
// looked past; the wrappers below write only their content (v3.17.5 final
|
|
78
|
+
// review I3: <u>, <sup>, <ins>... were missing, and an empty neighbour stopped
|
|
79
|
+
// the search).
|
|
80
|
+
//
|
|
81
|
+
// Before the run, the character is one UTF-16 unit, as marked reads it
|
|
82
|
+
// (`token.raw.slice(-1)`): an astral symbol there (📌) is a lone surrogate to
|
|
83
|
+
// it, a word character (final review I2).
|
|
84
|
+
var FLANK_WS = /\s/;
|
|
85
|
+
var FLANK_PUNCT = /[\p{P}\p{S}]/u;
|
|
86
|
+
var TRANSPARENT_INLINE = /^(SPAN|FONT|MARK|SMALL|BIG|ABBR|CITE|DFN|Q|TIME|DATA|BDI|BDO|U|INS|SUB|SUP|KBD|SAMP|VAR|LABEL|O:P)$/;
|
|
87
|
+
var WRITES_ITS_OWN = /^(IMG|BR|HR)$/;
|
|
88
|
+
|
|
89
|
+
function isWordish(ch) {
|
|
90
|
+
return ch !== undefined && !FLANK_WS.test(ch) && !FLANK_PUNCT.test(ch);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function edgeChar(text, last) {
|
|
94
|
+
if (!text) return undefined;
|
|
95
|
+
return last ? text[text.length - 1] : Array.from(text)[0];
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Only an inline element's text is read as the neighbour. A block, or an
|
|
99
|
+
// element not on these lists, is a line edge or unknown: the answer is
|
|
100
|
+
// "not a word character", so no separator -- which can never put `<!--` at a
|
|
101
|
+
// line start, where marked reads it as an HTML block that ends the paragraph
|
|
102
|
+
// (re-review I1: `<div>前</div><b>「x」</b>`).
|
|
103
|
+
var MARKUP_INLINE = /^(STRONG|B|EM|I|DEL|S|STRIKE|A|CODE)$/;
|
|
104
|
+
|
|
105
|
+
// An element with no text writes nothing only when it is one of the inline
|
|
106
|
+
// elements above and nothing inside it writes either. An <img>, <br> or <hr>
|
|
107
|
+
// writes `)` or a line break (re-review C1: `<span><br></span>` was looked
|
|
108
|
+
// past, and the separator landed at the start of a line); a block -- or an
|
|
109
|
+
// element not on the lists -- writes a line edge even when empty (round 3,
|
|
110
|
+
// I1': `<p></p>` is a blank line). A bold / italic with no text is dropped by
|
|
111
|
+
// its own rule whatever it holds (`<b><br></b>`).
|
|
112
|
+
function writesNothing(el) {
|
|
113
|
+
var name = el.nodeName;
|
|
114
|
+
if (/^(STRONG|B|EM|I)$/.test(name) && !el.textContent.trim()) return true;
|
|
115
|
+
if (!TRANSPARENT_INLINE.test(name) && !MARKUP_INLINE.test(name)) return false;
|
|
116
|
+
for (var c = el.firstChild; c; c = c.nextSibling) {
|
|
117
|
+
if (c.nodeType === 1 && !writesNothing(c)) return false;
|
|
118
|
+
}
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// The character a neighbour writes at the edge facing the run: its last (or
|
|
123
|
+
// first) descendant that writes anything. A <br> / <img> / <hr> there, or a
|
|
124
|
+
// block or unknown element on the way, answers undefined -- so
|
|
125
|
+
// `<span>前<br></span>` is a line edge, not 前.
|
|
126
|
+
function edgeOf(node, last) {
|
|
127
|
+
if (node.nodeType === 3) return node.textContent ? edgeChar(node.textContent, last) : undefined;
|
|
128
|
+
if (node.nodeType !== 1) return undefined;
|
|
129
|
+
if (!TRANSPARENT_INLINE.test(node.nodeName) && !MARKUP_INLINE.test(node.nodeName)) return undefined;
|
|
130
|
+
for (var c = last ? node.lastChild : node.firstChild; c; c = last ? c.previousSibling : c.nextSibling) {
|
|
131
|
+
if (c.nodeType === 3 && !c.textContent) continue;
|
|
132
|
+
if (c.nodeType === 1 && !c.textContent && writesNothing(c)) continue;
|
|
133
|
+
if (c.nodeType !== 1 && c.nodeType !== 3) continue;
|
|
134
|
+
return edgeOf(c, last);
|
|
135
|
+
}
|
|
136
|
+
return undefined;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function outsideChar(node, dir) {
|
|
140
|
+
for (var n = node; n; n = n.parentNode) {
|
|
141
|
+
var sib = n[dir];
|
|
142
|
+
while (sib && (sib.nodeType === 1 ? !sib.textContent && writesNothing(sib)
|
|
143
|
+
: sib.nodeType !== 3 || !sib.textContent)) sib = sib[dir];
|
|
144
|
+
if (sib) return edgeOf(sib, dir === 'previousSibling');
|
|
145
|
+
var parent = n.parentNode;
|
|
146
|
+
if (!parent || parent.nodeType !== 1 || !TRANSPARENT_INLINE.test(parent.nodeName)) return undefined;
|
|
147
|
+
}
|
|
148
|
+
return undefined;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function insideChar(md, last) {
|
|
152
|
+
var chars = Array.from(md);
|
|
153
|
+
if (last) {
|
|
154
|
+
var i = chars.length - 1;
|
|
155
|
+
while (i >= 0 && chars[i] === '*') i -= 1;
|
|
156
|
+
return chars[i];
|
|
157
|
+
}
|
|
158
|
+
var j = 0;
|
|
159
|
+
while (j < chars.length && chars[j] === '*') j += 1;
|
|
160
|
+
return chars[j];
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function flankedReplacement(delimiterOf) {
|
|
164
|
+
return function (content, node, options) {
|
|
165
|
+
if (!content.trim()) return '';
|
|
166
|
+
// turndown moves the text's own edge whitespace outside, but not a <br>'s
|
|
167
|
+
// ` \n` at an edge (final review): peel that too
|
|
168
|
+
var lead = /^\s*/.exec(content)[0];
|
|
169
|
+
var trail = /\s*$/.exec(content)[0];
|
|
170
|
+
var delim = delimiterOf(options);
|
|
171
|
+
var md = delim + content.slice(lead.length, content.length - trail.length) + delim;
|
|
172
|
+
var ws = node.flankingWhitespace || { leading: '', trailing: '' };
|
|
173
|
+
var before = !lead && !ws.leading && FLANK_PUNCT.test(insideChar(md, false) || '') &&
|
|
174
|
+
isWordish(outsideChar(node, 'previousSibling'));
|
|
175
|
+
var after = !trail && !ws.trailing && FLANK_PUNCT.test(insideChar(md, true) || '') &&
|
|
176
|
+
isWordish(outsideChar(node, 'nextSibling'));
|
|
177
|
+
return lead + (before ? '<!-- -->' : '') + md + (after ? '<!-- -->' : '') + trail;
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function addFlankingEmphasisRules(service) {
|
|
182
|
+
service.addRule('strongFlanking', {
|
|
183
|
+
filter: ['strong', 'b'],
|
|
184
|
+
replacement: flankedReplacement(function (o) { return o.strongDelimiter; })
|
|
185
|
+
});
|
|
186
|
+
service.addRule('emphasisFlanking', {
|
|
187
|
+
filter: ['em', 'i'],
|
|
188
|
+
replacement: flankedReplacement(function (o) { return o.emDelimiter; })
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
|
|
58
192
|
function isFirstTbody(el) {
|
|
59
193
|
var prev = el.previousSibling;
|
|
60
194
|
return (
|
|
@@ -209,6 +343,7 @@ function htmlToMarkdown(html) {
|
|
|
209
343
|
emDelimiter: '*'
|
|
210
344
|
});
|
|
211
345
|
addStrikethroughRule(service);
|
|
346
|
+
addFlankingEmphasisRules(service);
|
|
212
347
|
addTableRules(service);
|
|
213
348
|
return service.turndown(html == null ? '' : String(html));
|
|
214
349
|
}
|