turndown 7.2.2 → 7.2.4

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