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