1 | var TurndownService = (function () {
|
2 | 'use strict';
|
3 |
|
4 | function extend (destination) {
|
5 | for (var i = 1; i < arguments.length; i++) {
|
6 | var source = arguments[i];
|
7 | for (var key in source) {
|
8 | if (source.hasOwnProperty(key)) destination[key] = source[key];
|
9 | }
|
10 | }
|
11 | return destination
|
12 | }
|
13 |
|
14 | function repeat (character, count) {
|
15 | return Array(count + 1).join(character)
|
16 | }
|
17 |
|
18 | function trimLeadingNewlines (string) {
|
19 | return string.replace(/^\n*/, '')
|
20 | }
|
21 |
|
22 | function trimTrailingNewlines (string) {
|
23 |
|
24 | var indexEnd = string.length;
|
25 | while (indexEnd > 0 && string[indexEnd - 1] === '\n') indexEnd--;
|
26 | return string.substring(0, indexEnd)
|
27 | }
|
28 |
|
29 | var blockElements = [
|
30 | 'ADDRESS', 'ARTICLE', 'ASIDE', 'AUDIO', 'BLOCKQUOTE', 'BODY', 'CANVAS',
|
31 | 'CENTER', 'DD', 'DIR', 'DIV', 'DL', 'DT', 'FIELDSET', 'FIGCAPTION', 'FIGURE',
|
32 | 'FOOTER', 'FORM', 'FRAMESET', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'HEADER',
|
33 | 'HGROUP', 'HR', 'HTML', 'ISINDEX', 'LI', 'MAIN', 'MENU', 'NAV', 'NOFRAMES',
|
34 | 'NOSCRIPT', 'OL', 'OUTPUT', 'P', 'PRE', 'SECTION', 'TABLE', 'TBODY', 'TD',
|
35 | 'TFOOT', 'TH', 'THEAD', 'TR', 'UL'
|
36 | ];
|
37 |
|
38 | function isBlock (node) {
|
39 | return is(node, blockElements)
|
40 | }
|
41 |
|
42 | var voidElements = [
|
43 | 'AREA', 'BASE', 'BR', 'COL', 'COMMAND', 'EMBED', 'HR', 'IMG', 'INPUT',
|
44 | 'KEYGEN', 'LINK', 'META', 'PARAM', 'SOURCE', 'TRACK', 'WBR'
|
45 | ];
|
46 |
|
47 | function isVoid (node) {
|
48 | return is(node, voidElements)
|
49 | }
|
50 |
|
51 | function hasVoid (node) {
|
52 | return has(node, voidElements)
|
53 | }
|
54 |
|
55 | var meaningfulWhenBlankElements = [
|
56 | 'A', 'TABLE', 'THEAD', 'TBODY', 'TFOOT', 'TH', 'TD', 'IFRAME', 'SCRIPT',
|
57 | 'AUDIO', 'VIDEO'
|
58 | ];
|
59 |
|
60 | function isMeaningfulWhenBlank (node) {
|
61 | return is(node, meaningfulWhenBlankElements)
|
62 | }
|
63 |
|
64 | function hasMeaningfulWhenBlank (node) {
|
65 | return has(node, meaningfulWhenBlankElements)
|
66 | }
|
67 |
|
68 | function is (node, tagNames) {
|
69 | return tagNames.indexOf(node.nodeName) >= 0
|
70 | }
|
71 |
|
72 | function has (node, tagNames) {
|
73 | return (
|
74 | node.getElementsByTagName &&
|
75 | tagNames.some(function (tagName) {
|
76 | return node.getElementsByTagName(tagName).length
|
77 | })
|
78 | )
|
79 | }
|
80 |
|
81 | var rules = {};
|
82 |
|
83 | rules.paragraph = {
|
84 | filter: 'p',
|
85 |
|
86 | replacement: function (content) {
|
87 | return '\n\n' + content + '\n\n'
|
88 | }
|
89 | };
|
90 |
|
91 | rules.lineBreak = {
|
92 | filter: 'br',
|
93 |
|
94 | replacement: function (content, node, options) {
|
95 | return options.br + '\n'
|
96 | }
|
97 | };
|
98 |
|
99 | rules.heading = {
|
100 | filter: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'],
|
101 |
|
102 | replacement: function (content, node, options) {
|
103 | var hLevel = Number(node.nodeName.charAt(1));
|
104 |
|
105 | if (options.headingStyle === 'setext' && hLevel < 3) {
|
106 | var underline = repeat((hLevel === 1 ? '=' : '-'), content.length);
|
107 | return (
|
108 | '\n\n' + content + '\n' + underline + '\n\n'
|
109 | )
|
110 | } else {
|
111 | return '\n\n' + repeat('#', hLevel) + ' ' + content + '\n\n'
|
112 | }
|
113 | }
|
114 | };
|
115 |
|
116 | rules.blockquote = {
|
117 | filter: 'blockquote',
|
118 |
|
119 | replacement: function (content) {
|
120 | content = content.replace(/^\n+|\n+$/g, '');
|
121 | content = content.replace(/^/gm, '> ');
|
122 | return '\n\n' + content + '\n\n'
|
123 | }
|
124 | };
|
125 |
|
126 | rules.list = {
|
127 | filter: ['ul', 'ol'],
|
128 |
|
129 | replacement: function (content, node) {
|
130 | var parent = node.parentNode;
|
131 | if (parent.nodeName === 'LI' && parent.lastElementChild === node) {
|
132 | return '\n' + content
|
133 | } else {
|
134 | return '\n\n' + content + '\n\n'
|
135 | }
|
136 | }
|
137 | };
|
138 |
|
139 | rules.listItem = {
|
140 | filter: 'li',
|
141 |
|
142 | replacement: function (content, node, options) {
|
143 | content = content
|
144 | .replace(/^\n+/, '')
|
145 | .replace(/\n+$/, '\n')
|
146 | .replace(/\n/gm, '\n ');
|
147 | var prefix = options.bulletListMarker + ' ';
|
148 | var parent = node.parentNode;
|
149 | if (parent.nodeName === 'OL') {
|
150 | var start = parent.getAttribute('start');
|
151 | var index = Array.prototype.indexOf.call(parent.children, node);
|
152 | prefix = (start ? Number(start) + index : index + 1) + '. ';
|
153 | }
|
154 | return (
|
155 | prefix + content + (node.nextSibling && !/\n$/.test(content) ? '\n' : '')
|
156 | )
|
157 | }
|
158 | };
|
159 |
|
160 | rules.indentedCodeBlock = {
|
161 | filter: function (node, options) {
|
162 | return (
|
163 | options.codeBlockStyle === 'indented' &&
|
164 | node.nodeName === 'PRE' &&
|
165 | node.firstChild &&
|
166 | node.firstChild.nodeName === 'CODE'
|
167 | )
|
168 | },
|
169 |
|
170 | replacement: function (content, node, options) {
|
171 | return (
|
172 | '\n\n ' +
|
173 | node.firstChild.textContent.replace(/\n/g, '\n ') +
|
174 | '\n\n'
|
175 | )
|
176 | }
|
177 | };
|
178 |
|
179 | rules.fencedCodeBlock = {
|
180 | filter: function (node, options) {
|
181 | return (
|
182 | options.codeBlockStyle === 'fenced' &&
|
183 | node.nodeName === 'PRE' &&
|
184 | node.firstChild &&
|
185 | node.firstChild.nodeName === 'CODE'
|
186 | )
|
187 | },
|
188 |
|
189 | replacement: function (content, node, options) {
|
190 | var className = node.firstChild.getAttribute('class') || '';
|
191 | var language = (className.match(/language-(\S+)/) || [null, ''])[1];
|
192 | var code = node.firstChild.textContent;
|
193 |
|
194 | var fenceChar = options.fence.charAt(0);
|
195 | var fenceSize = 3;
|
196 | var fenceInCodeRegex = new RegExp('^' + fenceChar + '{3,}', 'gm');
|
197 |
|
198 | var match;
|
199 | while ((match = fenceInCodeRegex.exec(code))) {
|
200 | if (match[0].length >= fenceSize) {
|
201 | fenceSize = match[0].length + 1;
|
202 | }
|
203 | }
|
204 |
|
205 | var fence = repeat(fenceChar, fenceSize);
|
206 |
|
207 | return (
|
208 | '\n\n' + fence + language + '\n' +
|
209 | code.replace(/\n$/, '') +
|
210 | '\n' + fence + '\n\n'
|
211 | )
|
212 | }
|
213 | };
|
214 |
|
215 | rules.horizontalRule = {
|
216 | filter: 'hr',
|
217 |
|
218 | replacement: function (content, node, options) {
|
219 | return '\n\n' + options.hr + '\n\n'
|
220 | }
|
221 | };
|
222 |
|
223 | rules.inlineLink = {
|
224 | filter: function (node, options) {
|
225 | return (
|
226 | options.linkStyle === 'inlined' &&
|
227 | node.nodeName === 'A' &&
|
228 | node.getAttribute('href')
|
229 | )
|
230 | },
|
231 |
|
232 | replacement: function (content, node) {
|
233 | var href = node.getAttribute('href');
|
234 | var title = cleanAttribute(node.getAttribute('title'));
|
235 | if (title) title = ' "' + title + '"';
|
236 | return '[' + content + '](' + href + title + ')'
|
237 | }
|
238 | };
|
239 |
|
240 | rules.referenceLink = {
|
241 | filter: function (node, options) {
|
242 | return (
|
243 | options.linkStyle === 'referenced' &&
|
244 | node.nodeName === 'A' &&
|
245 | node.getAttribute('href')
|
246 | )
|
247 | },
|
248 |
|
249 | replacement: function (content, node, options) {
|
250 | var href = node.getAttribute('href');
|
251 | var title = cleanAttribute(node.getAttribute('title'));
|
252 | if (title) title = ' "' + title + '"';
|
253 | var replacement;
|
254 | var reference;
|
255 |
|
256 | switch (options.linkReferenceStyle) {
|
257 | case 'collapsed':
|
258 | replacement = '[' + content + '][]';
|
259 | reference = '[' + content + ']: ' + href + title;
|
260 | break
|
261 | case 'shortcut':
|
262 | replacement = '[' + content + ']';
|
263 | reference = '[' + content + ']: ' + href + title;
|
264 | break
|
265 | default:
|
266 | var id = this.references.length + 1;
|
267 | replacement = '[' + content + '][' + id + ']';
|
268 | reference = '[' + id + ']: ' + href + title;
|
269 | }
|
270 |
|
271 | this.references.push(reference);
|
272 | return replacement
|
273 | },
|
274 |
|
275 | references: [],
|
276 |
|
277 | append: function (options) {
|
278 | var references = '';
|
279 | if (this.references.length) {
|
280 | references = '\n\n' + this.references.join('\n') + '\n\n';
|
281 | this.references = [];
|
282 | }
|
283 | return references
|
284 | }
|
285 | };
|
286 |
|
287 | rules.emphasis = {
|
288 | filter: ['em', 'i'],
|
289 |
|
290 | replacement: function (content, node, options) {
|
291 | if (!content.trim()) return ''
|
292 | return options.emDelimiter + content + options.emDelimiter
|
293 | }
|
294 | };
|
295 |
|
296 | rules.strong = {
|
297 | filter: ['strong', 'b'],
|
298 |
|
299 | replacement: function (content, node, options) {
|
300 | if (!content.trim()) return ''
|
301 | return options.strongDelimiter + content + options.strongDelimiter
|
302 | }
|
303 | };
|
304 |
|
305 | rules.code = {
|
306 | filter: function (node) {
|
307 | var hasSiblings = node.previousSibling || node.nextSibling;
|
308 | var isCodeBlock = node.parentNode.nodeName === 'PRE' && !hasSiblings;
|
309 |
|
310 | return node.nodeName === 'CODE' && !isCodeBlock
|
311 | },
|
312 |
|
313 | replacement: function (content) {
|
314 | if (!content) return ''
|
315 | content = content.replace(/\r?\n|\r/g, ' ');
|
316 |
|
317 | var extraSpace = /^`|^ .*?[^ ].* $|`$/.test(content) ? ' ' : '';
|
318 | var delimiter = '`';
|
319 | var matches = content.match(/`+/gm) || [];
|
320 | while (matches.indexOf(delimiter) !== -1) delimiter = delimiter + '`';
|
321 |
|
322 | return delimiter + extraSpace + content + extraSpace + delimiter
|
323 | }
|
324 | };
|
325 |
|
326 | rules.image = {
|
327 | filter: 'img',
|
328 |
|
329 | replacement: function (content, node) {
|
330 | var alt = cleanAttribute(node.getAttribute('alt'));
|
331 | var src = node.getAttribute('src') || '';
|
332 | var title = cleanAttribute(node.getAttribute('title'));
|
333 | var titlePart = title ? ' "' + title + '"' : '';
|
334 | return src ? '![' + alt + ']' + '(' + src + titlePart + ')' : ''
|
335 | }
|
336 | };
|
337 |
|
338 | function cleanAttribute (attribute) {
|
339 | return attribute ? attribute.replace(/(\n+\s*)+/g, '\n') : ''
|
340 | }
|
341 |
|
342 | |
343 |
|
344 |
|
345 |
|
346 | function Rules (options) {
|
347 | this.options = options;
|
348 | this._keep = [];
|
349 | this._remove = [];
|
350 |
|
351 | this.blankRule = {
|
352 | replacement: options.blankReplacement
|
353 | };
|
354 |
|
355 | this.keepReplacement = options.keepReplacement;
|
356 |
|
357 | this.defaultRule = {
|
358 | replacement: options.defaultReplacement
|
359 | };
|
360 |
|
361 | this.array = [];
|
362 | for (var key in options.rules) this.array.push(options.rules[key]);
|
363 | }
|
364 |
|
365 | Rules.prototype = {
|
366 | add: function (key, rule) {
|
367 | this.array.unshift(rule);
|
368 | },
|
369 |
|
370 | keep: function (filter) {
|
371 | this._keep.unshift({
|
372 | filter: filter,
|
373 | replacement: this.keepReplacement
|
374 | });
|
375 | },
|
376 |
|
377 | remove: function (filter) {
|
378 | this._remove.unshift({
|
379 | filter: filter,
|
380 | replacement: function () {
|
381 | return ''
|
382 | }
|
383 | });
|
384 | },
|
385 |
|
386 | forNode: function (node) {
|
387 | if (node.isBlank) return this.blankRule
|
388 | var rule;
|
389 |
|
390 | if ((rule = findRule(this.array, node, this.options))) return rule
|
391 | if ((rule = findRule(this._keep, node, this.options))) return rule
|
392 | if ((rule = findRule(this._remove, node, this.options))) return rule
|
393 |
|
394 | return this.defaultRule
|
395 | },
|
396 |
|
397 | forEach: function (fn) {
|
398 | for (var i = 0; i < this.array.length; i++) fn(this.array[i], i);
|
399 | }
|
400 | };
|
401 |
|
402 | function findRule (rules, node, options) {
|
403 | for (var i = 0; i < rules.length; i++) {
|
404 | var rule = rules[i];
|
405 | if (filterValue(rule, node, options)) return rule
|
406 | }
|
407 | return void 0
|
408 | }
|
409 |
|
410 | function filterValue (rule, node, options) {
|
411 | var filter = rule.filter;
|
412 | if (typeof filter === 'string') {
|
413 | if (filter === node.nodeName.toLowerCase()) return true
|
414 | } else if (Array.isArray(filter)) {
|
415 | if (filter.indexOf(node.nodeName.toLowerCase()) > -1) return true
|
416 | } else if (typeof filter === 'function') {
|
417 | if (filter.call(rule, node, options)) return true
|
418 | } else {
|
419 | throw new TypeError('`filter` needs to be a string, array, or function')
|
420 | }
|
421 | }
|
422 |
|
423 | |
424 |
|
425 |
|
426 |
|
427 |
|
428 |
|
429 |
|
430 |
|
431 |
|
432 |
|
433 |
|
434 |
|
435 |
|
436 |
|
437 |
|
438 |
|
439 |
|
440 |
|
441 |
|
442 |
|
443 |
|
444 |
|
445 |
|
446 |
|
447 |
|
448 |
|
449 |
|
450 | |
451 |
|
452 |
|
453 |
|
454 |
|
455 | function collapseWhitespace (options) {
|
456 | var element = options.element;
|
457 | var isBlock = options.isBlock;
|
458 | var isVoid = options.isVoid;
|
459 | var isPre = options.isPre || function (node) {
|
460 | return node.nodeName === 'PRE'
|
461 | };
|
462 |
|
463 | if (!element.firstChild || isPre(element)) return
|
464 |
|
465 | var prevText = null;
|
466 | var keepLeadingWs = false;
|
467 |
|
468 | var prev = null;
|
469 | var node = next(prev, element, isPre);
|
470 |
|
471 | while (node !== element) {
|
472 | if (node.nodeType === 3 || node.nodeType === 4) {
|
473 | var text = node.data.replace(/[ \r\n\t]+/g, ' ');
|
474 |
|
475 | if ((!prevText || / $/.test(prevText.data)) &&
|
476 | !keepLeadingWs && text[0] === ' ') {
|
477 | text = text.substr(1);
|
478 | }
|
479 |
|
480 |
|
481 | if (!text) {
|
482 | node = remove(node);
|
483 | continue
|
484 | }
|
485 |
|
486 | node.data = text;
|
487 |
|
488 | prevText = node;
|
489 | } else if (node.nodeType === 1) {
|
490 | if (isBlock(node) || node.nodeName === 'BR') {
|
491 | if (prevText) {
|
492 | prevText.data = prevText.data.replace(/ $/, '');
|
493 | }
|
494 |
|
495 | prevText = null;
|
496 | keepLeadingWs = false;
|
497 | } else if (isVoid(node) || isPre(node)) {
|
498 |
|
499 | prevText = null;
|
500 | keepLeadingWs = true;
|
501 | } else if (prevText) {
|
502 |
|
503 | keepLeadingWs = false;
|
504 | }
|
505 | } else {
|
506 | node = remove(node);
|
507 | continue
|
508 | }
|
509 |
|
510 | var nextNode = next(prev, node, isPre);
|
511 | prev = node;
|
512 | node = nextNode;
|
513 | }
|
514 |
|
515 | if (prevText) {
|
516 | prevText.data = prevText.data.replace(/ $/, '');
|
517 | if (!prevText.data) {
|
518 | remove(prevText);
|
519 | }
|
520 | }
|
521 | }
|
522 |
|
523 | |
524 |
|
525 |
|
526 |
|
527 |
|
528 |
|
529 |
|
530 | function remove (node) {
|
531 | var next = node.nextSibling || node.parentNode;
|
532 |
|
533 | node.parentNode.removeChild(node);
|
534 |
|
535 | return next
|
536 | }
|
537 |
|
538 | |
539 |
|
540 |
|
541 |
|
542 |
|
543 |
|
544 |
|
545 |
|
546 |
|
547 | function next (prev, current, isPre) {
|
548 | if ((prev && prev.parentNode === current) || isPre(current)) {
|
549 | return current.nextSibling || current.parentNode
|
550 | }
|
551 |
|
552 | return current.firstChild || current.nextSibling || current.parentNode
|
553 | }
|
554 |
|
555 | |
556 |
|
557 |
|
558 |
|
559 | var root = (typeof window !== 'undefined' ? window : {});
|
560 |
|
561 | |
562 |
|
563 |
|
564 |
|
565 | function canParseHTMLNatively () {
|
566 | var Parser = root.DOMParser;
|
567 | var canParse = false;
|
568 |
|
569 |
|
570 |
|
571 | try {
|
572 |
|
573 | if (new Parser().parseFromString('', 'text/html')) {
|
574 | canParse = true;
|
575 | }
|
576 | } catch (e) {}
|
577 |
|
578 | return canParse
|
579 | }
|
580 |
|
581 | function createHTMLParser () {
|
582 | var Parser = function () {};
|
583 |
|
584 | {
|
585 | if (shouldUseActiveX()) {
|
586 | Parser.prototype.parseFromString = function (string) {
|
587 | var doc = new window.ActiveXObject('htmlfile');
|
588 | doc.designMode = 'on';
|
589 | doc.open();
|
590 | doc.write(string);
|
591 | doc.close();
|
592 | return doc
|
593 | };
|
594 | } else {
|
595 | Parser.prototype.parseFromString = function (string) {
|
596 | var doc = document.implementation.createHTMLDocument('');
|
597 | doc.open();
|
598 | doc.write(string);
|
599 | doc.close();
|
600 | return doc
|
601 | };
|
602 | }
|
603 | }
|
604 | return Parser
|
605 | }
|
606 |
|
607 | function shouldUseActiveX () {
|
608 | var useActiveX = false;
|
609 | try {
|
610 | document.implementation.createHTMLDocument('').open();
|
611 | } catch (e) {
|
612 | if (root.ActiveXObject) useActiveX = true;
|
613 | }
|
614 | return useActiveX
|
615 | }
|
616 |
|
617 | var HTMLParser = canParseHTMLNatively() ? root.DOMParser : createHTMLParser();
|
618 |
|
619 | function RootNode (input, options) {
|
620 | var root;
|
621 | if (typeof input === 'string') {
|
622 | var doc = htmlParser().parseFromString(
|
623 |
|
624 |
|
625 |
|
626 | '<x-turndown id="turndown-root">' + input + '</x-turndown>',
|
627 | 'text/html'
|
628 | );
|
629 | root = doc.getElementById('turndown-root');
|
630 | } else {
|
631 | root = input.cloneNode(true);
|
632 | }
|
633 | collapseWhitespace({
|
634 | element: root,
|
635 | isBlock: isBlock,
|
636 | isVoid: isVoid,
|
637 | isPre: options.preformattedCode ? isPreOrCode : null
|
638 | });
|
639 |
|
640 | return root
|
641 | }
|
642 |
|
643 | var _htmlParser;
|
644 | function htmlParser () {
|
645 | _htmlParser = _htmlParser || new HTMLParser();
|
646 | return _htmlParser
|
647 | }
|
648 |
|
649 | function isPreOrCode (node) {
|
650 | return node.nodeName === 'PRE' || node.nodeName === 'CODE'
|
651 | }
|
652 |
|
653 | function Node (node, options) {
|
654 | node.isBlock = isBlock(node);
|
655 | node.isCode = node.nodeName === 'CODE' || node.parentNode.isCode;
|
656 | node.isBlank = isBlank(node);
|
657 | node.flankingWhitespace = flankingWhitespace(node, options);
|
658 | return node
|
659 | }
|
660 |
|
661 | function isBlank (node) {
|
662 | return (
|
663 | !isVoid(node) &&
|
664 | !isMeaningfulWhenBlank(node) &&
|
665 | /^\s*$/i.test(node.textContent) &&
|
666 | !hasVoid(node) &&
|
667 | !hasMeaningfulWhenBlank(node)
|
668 | )
|
669 | }
|
670 |
|
671 | function flankingWhitespace (node, options) {
|
672 | if (node.isBlock || (options.preformattedCode && node.isCode)) {
|
673 | return { leading: '', trailing: '' }
|
674 | }
|
675 |
|
676 | var edges = edgeWhitespace(node.textContent);
|
677 |
|
678 |
|
679 | if (edges.leadingAscii && isFlankedByWhitespace('left', node, options)) {
|
680 | edges.leading = edges.leadingNonAscii;
|
681 | }
|
682 |
|
683 |
|
684 | if (edges.trailingAscii && isFlankedByWhitespace('right', node, options)) {
|
685 | edges.trailing = edges.trailingNonAscii;
|
686 | }
|
687 |
|
688 | return { leading: edges.leading, trailing: edges.trailing }
|
689 | }
|
690 |
|
691 | function edgeWhitespace (string) {
|
692 | var m = string.match(/^(([ \t\r\n]*)(\s*))(?:(?=\S)[\s\S]*\S)?((\s*?)([ \t\r\n]*))$/);
|
693 | return {
|
694 | leading: m[1],
|
695 | leadingAscii: m[2],
|
696 | leadingNonAscii: m[3],
|
697 | trailing: m[4],
|
698 | trailingNonAscii: m[5],
|
699 | trailingAscii: m[6]
|
700 | }
|
701 | }
|
702 |
|
703 | function isFlankedByWhitespace (side, node, options) {
|
704 | var sibling;
|
705 | var regExp;
|
706 | var isFlanked;
|
707 |
|
708 | if (side === 'left') {
|
709 | sibling = node.previousSibling;
|
710 | regExp = / $/;
|
711 | } else {
|
712 | sibling = node.nextSibling;
|
713 | regExp = /^ /;
|
714 | }
|
715 |
|
716 | if (sibling) {
|
717 | if (sibling.nodeType === 3) {
|
718 | isFlanked = regExp.test(sibling.nodeValue);
|
719 | } else if (options.preformattedCode && sibling.nodeName === 'CODE') {
|
720 | isFlanked = false;
|
721 | } else if (sibling.nodeType === 1 && !isBlock(sibling)) {
|
722 | isFlanked = regExp.test(sibling.textContent);
|
723 | }
|
724 | }
|
725 | return isFlanked
|
726 | }
|
727 |
|
728 | var reduce = Array.prototype.reduce;
|
729 | var escapes = [
|
730 | [/\\/g, '\\\\'],
|
731 | [/\*/g, '\\*'],
|
732 | [/^-/g, '\\-'],
|
733 | [/^\+ /g, '\\+ '],
|
734 | [/^(=+)/g, '\\$1'],
|
735 | [/^(#{1,6}) /g, '\\$1 '],
|
736 | [/`/g, '\\`'],
|
737 | [/^~~~/g, '\\~~~'],
|
738 | [/\[/g, '\\['],
|
739 | [/\]/g, '\\]'],
|
740 | [/^>/g, '\\>'],
|
741 | [/_/g, '\\_'],
|
742 | [/^(\d+)\. /g, '$1\\. ']
|
743 | ];
|
744 |
|
745 | function TurndownService (options) {
|
746 | if (!(this instanceof TurndownService)) return new TurndownService(options)
|
747 |
|
748 | var defaults = {
|
749 | rules: rules,
|
750 | headingStyle: 'setext',
|
751 | hr: '* * *',
|
752 | bulletListMarker: '*',
|
753 | codeBlockStyle: 'indented',
|
754 | fence: '```',
|
755 | emDelimiter: '_',
|
756 | strongDelimiter: '**',
|
757 | linkStyle: 'inlined',
|
758 | linkReferenceStyle: 'full',
|
759 | br: ' ',
|
760 | preformattedCode: false,
|
761 | blankReplacement: function (content, node) {
|
762 | return node.isBlock ? '\n\n' : ''
|
763 | },
|
764 | keepReplacement: function (content, node) {
|
765 | return node.isBlock ? '\n\n' + node.outerHTML + '\n\n' : node.outerHTML
|
766 | },
|
767 | defaultReplacement: function (content, node) {
|
768 | return node.isBlock ? '\n\n' + content + '\n\n' : content
|
769 | }
|
770 | };
|
771 | this.options = extend({}, defaults, options);
|
772 | this.rules = new Rules(this.options);
|
773 | }
|
774 |
|
775 | TurndownService.prototype = {
|
776 | |
777 |
|
778 |
|
779 |
|
780 |
|
781 |
|
782 |
|
783 |
|
784 | turndown: function (input) {
|
785 | if (!canConvert(input)) {
|
786 | throw new TypeError(
|
787 | input + ' is not a string, or an element/document/fragment node.'
|
788 | )
|
789 | }
|
790 |
|
791 | if (input === '') return ''
|
792 |
|
793 | var output = process.call(this, new RootNode(input, this.options));
|
794 | return postProcess.call(this, output)
|
795 | },
|
796 |
|
797 | |
798 |
|
799 |
|
800 |
|
801 |
|
802 |
|
803 |
|
804 |
|
805 | use: function (plugin) {
|
806 | if (Array.isArray(plugin)) {
|
807 | for (var i = 0; i < plugin.length; i++) this.use(plugin[i]);
|
808 | } else if (typeof plugin === 'function') {
|
809 | plugin(this);
|
810 | } else {
|
811 | throw new TypeError('plugin must be a Function or an Array of Functions')
|
812 | }
|
813 | return this
|
814 | },
|
815 |
|
816 | |
817 |
|
818 |
|
819 |
|
820 |
|
821 |
|
822 |
|
823 |
|
824 |
|
825 | addRule: function (key, rule) {
|
826 | this.rules.add(key, rule);
|
827 | return this
|
828 | },
|
829 |
|
830 | |
831 |
|
832 |
|
833 |
|
834 |
|
835 |
|
836 |
|
837 |
|
838 | keep: function (filter) {
|
839 | this.rules.keep(filter);
|
840 | return this
|
841 | },
|
842 |
|
843 | |
844 |
|
845 |
|
846 |
|
847 |
|
848 |
|
849 |
|
850 |
|
851 | remove: function (filter) {
|
852 | this.rules.remove(filter);
|
853 | return this
|
854 | },
|
855 |
|
856 | |
857 |
|
858 |
|
859 |
|
860 |
|
861 |
|
862 |
|
863 |
|
864 | escape: function (string) {
|
865 | return escapes.reduce(function (accumulator, escape) {
|
866 | return accumulator.replace(escape[0], escape[1])
|
867 | }, string)
|
868 | }
|
869 | };
|
870 |
|
871 | |
872 |
|
873 |
|
874 |
|
875 |
|
876 |
|
877 |
|
878 |
|
879 | function process (parentNode) {
|
880 | var self = this;
|
881 | return reduce.call(parentNode.childNodes, function (output, node) {
|
882 | node = new Node(node, self.options);
|
883 |
|
884 | var replacement = '';
|
885 | if (node.nodeType === 3) {
|
886 | replacement = node.isCode ? node.nodeValue : self.escape(node.nodeValue);
|
887 | } else if (node.nodeType === 1) {
|
888 | replacement = replacementForNode.call(self, node);
|
889 | }
|
890 |
|
891 | return join(output, replacement)
|
892 | }, '')
|
893 | }
|
894 |
|
895 | |
896 |
|
897 |
|
898 |
|
899 |
|
900 |
|
901 |
|
902 |
|
903 | function postProcess (output) {
|
904 | var self = this;
|
905 | this.rules.forEach(function (rule) {
|
906 | if (typeof rule.append === 'function') {
|
907 | output = join(output, rule.append(self.options));
|
908 | }
|
909 | });
|
910 |
|
911 | return output.replace(/^[\t\r\n]+/, '').replace(/[\t\r\n\s]+$/, '')
|
912 | }
|
913 |
|
914 | |
915 |
|
916 |
|
917 |
|
918 |
|
919 |
|
920 |
|
921 |
|
922 | function replacementForNode (node) {
|
923 | var rule = this.rules.forNode(node);
|
924 | var content = process.call(this, node);
|
925 | var whitespace = node.flankingWhitespace;
|
926 | if (whitespace.leading || whitespace.trailing) content = content.trim();
|
927 | return (
|
928 | whitespace.leading +
|
929 | rule.replacement(content, node, this.options) +
|
930 | whitespace.trailing
|
931 | )
|
932 | }
|
933 |
|
934 | |
935 |
|
936 |
|
937 |
|
938 |
|
939 |
|
940 |
|
941 |
|
942 |
|
943 | function join (output, replacement) {
|
944 | var s1 = trimTrailingNewlines(output);
|
945 | var s2 = trimLeadingNewlines(replacement);
|
946 | var nls = Math.max(output.length - s1.length, replacement.length - s2.length);
|
947 | var separator = '\n\n'.substring(0, nls);
|
948 |
|
949 | return s1 + separator + s2
|
950 | }
|
951 |
|
952 | |
953 |
|
954 |
|
955 |
|
956 |
|
957 |
|
958 |
|
959 |
|
960 | function canConvert (input) {
|
961 | return (
|
962 | input != null && (
|
963 | typeof input === 'string' ||
|
964 | (input.nodeType && (
|
965 | input.nodeType === 1 || input.nodeType === 9 || input.nodeType === 11
|
966 | ))
|
967 | )
|
968 | )
|
969 | }
|
970 |
|
971 | return TurndownService;
|
972 |
|
973 | }());
|