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