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,13 +460,10 @@ 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
  if (shouldUseActiveX()) {
588
469
  Parser.prototype.parseFromString = function (string) {
@@ -591,7 +472,7 @@ function createHTMLParser () {
591
472
  doc.open();
592
473
  doc.write(string);
593
474
  doc.close();
594
- return doc
475
+ return doc;
595
476
  };
596
477
  } else {
597
478
  Parser.prototype.parseFromString = function (string) {
@@ -599,35 +480,31 @@ function createHTMLParser () {
599
480
  doc.open();
600
481
  doc.write(string);
601
482
  doc.close();
602
- return doc
483
+ return doc;
603
484
  };
604
485
  }
605
486
  }
606
- return Parser
487
+ return Parser;
607
488
  }
608
-
609
- function shouldUseActiveX () {
489
+ function shouldUseActiveX() {
610
490
  var useActiveX = false;
611
491
  try {
612
492
  document.implementation.createHTMLDocument('').open();
613
493
  } catch (e) {
614
494
  if (root.ActiveXObject) useActiveX = true;
615
495
  }
616
- return useActiveX
496
+ return useActiveX;
617
497
  }
618
-
619
498
  var HTMLParser = canParseHTMLNatively() ? root.DOMParser : createHTMLParser();
620
499
 
621
- function RootNode (input, options) {
500
+ function RootNode(input, options) {
622
501
  var root;
623
502
  if (typeof input === 'string') {
624
503
  var doc = htmlParser().parseFromString(
625
- // DOM parsers arrange elements in the <head> and <body>.
626
- // Wrapping in a custom element ensures elements are reliably arranged in
627
- // a single element.
628
- '<x-turndown id="turndown-root">' + input + '</x-turndown>',
629
- 'text/html'
630
- );
504
+ // DOM parsers arrange elements in the <head> and <body>.
505
+ // Wrapping in a custom element ensures elements are reliably arranged in
506
+ // a single element.
507
+ '<x-turndown id="turndown-root">' + input + '</x-turndown>', 'text/html');
631
508
  root = doc.getElementById('turndown-root');
632
509
  } else {
633
510
  root = input.cloneNode(true);
@@ -638,43 +515,34 @@ function RootNode (input, options) {
638
515
  isVoid: isVoid,
639
516
  isPre: options.preformattedCode ? isPreOrCode : null
640
517
  });
641
-
642
- return root
518
+ return root;
643
519
  }
644
-
645
520
  var _htmlParser;
646
- function htmlParser () {
521
+ function htmlParser() {
647
522
  _htmlParser = _htmlParser || new HTMLParser();
648
- return _htmlParser
523
+ return _htmlParser;
649
524
  }
650
-
651
- function isPreOrCode (node) {
652
- return node.nodeName === 'PRE' || node.nodeName === 'CODE'
525
+ function isPreOrCode(node) {
526
+ return node.nodeName === 'PRE' || node.nodeName === 'CODE';
653
527
  }
654
528
 
655
- function Node (node, options) {
529
+ function Node(node, options) {
656
530
  node.isBlock = isBlock(node);
657
531
  node.isCode = node.nodeName === 'CODE' || node.parentNode.isCode;
658
532
  node.isBlank = isBlank(node);
659
533
  node.flankingWhitespace = flankingWhitespace(node, options);
660
- return node
534
+ return node;
661
535
  }
662
-
663
- function isBlank (node) {
664
- return (
665
- !isVoid(node) &&
666
- !isMeaningfulWhenBlank(node) &&
667
- /^\s*$/i.test(node.textContent) &&
668
- !hasVoid(node) &&
669
- !hasMeaningfulWhenBlank(node)
670
- )
536
+ function isBlank(node) {
537
+ return !isVoid(node) && !isMeaningfulWhenBlank(node) && /^\s*$/i.test(node.textContent) && !hasVoid(node) && !hasMeaningfulWhenBlank(node);
671
538
  }
672
-
673
- function flankingWhitespace (node, options) {
674
- if (node.isBlock || (options.preformattedCode && node.isCode)) {
675
- return { leading: '', trailing: '' }
539
+ function flankingWhitespace(node, options) {
540
+ if (node.isBlock || options.preformattedCode && node.isCode) {
541
+ return {
542
+ leading: '',
543
+ trailing: ''
544
+ };
676
545
  }
677
-
678
546
  var edges = edgeWhitespace(node.textContent);
679
547
 
680
548
  // abandon leading ASCII WS if left-flanked by ASCII WS
@@ -686,27 +554,28 @@ function flankingWhitespace (node, options) {
686
554
  if (edges.trailingAscii && isFlankedByWhitespace('right', node, options)) {
687
555
  edges.trailing = edges.trailingNonAscii;
688
556
  }
689
-
690
- return { leading: edges.leading, trailing: edges.trailing }
557
+ return {
558
+ leading: edges.leading,
559
+ trailing: edges.trailing
560
+ };
691
561
  }
692
-
693
- function edgeWhitespace (string) {
562
+ function edgeWhitespace(string) {
694
563
  var m = string.match(/^(([ \t\r\n]*)(\s*))(?:(?=\S)[\s\S]*\S)?((\s*?)([ \t\r\n]*))$/);
695
564
  return {
696
- leading: m[1], // whole string for whitespace-only strings
565
+ leading: m[1],
566
+ // whole string for whitespace-only strings
697
567
  leadingAscii: m[2],
698
568
  leadingNonAscii: m[3],
699
- trailing: m[4], // empty for whitespace-only strings
569
+ trailing: m[4],
570
+ // empty for whitespace-only strings
700
571
  trailingNonAscii: m[5],
701
572
  trailingAscii: m[6]
702
- }
573
+ };
703
574
  }
704
-
705
- function isFlankedByWhitespace (side, node, options) {
575
+ function isFlankedByWhitespace(side, node, options) {
706
576
  var sibling;
707
577
  var regExp;
708
578
  var isFlanked;
709
-
710
579
  if (side === 'left') {
711
580
  sibling = node.previousSibling;
712
581
  regExp = / $/;
@@ -714,7 +583,6 @@ function isFlankedByWhitespace (side, node, options) {
714
583
  sibling = node.nextSibling;
715
584
  regExp = /^ /;
716
585
  }
717
-
718
586
  if (sibling) {
719
587
  if (sibling.nodeType === 3) {
720
588
  isFlanked = regExp.test(sibling.nodeValue);
@@ -724,29 +592,12 @@ function isFlankedByWhitespace (side, node, options) {
724
592
  isFlanked = regExp.test(sibling.textContent);
725
593
  }
726
594
  }
727
- return isFlanked
595
+ return isFlanked;
728
596
  }
729
597
 
730
598
  var reduce = Array.prototype.reduce;
731
- var escapes = [
732
- [/\\/g, '\\\\'],
733
- [/\*/g, '\\*'],
734
- [/^-/g, '\\-'],
735
- [/^\+ /g, '\\+ '],
736
- [/^(=+)/g, '\\$1'],
737
- [/^(#{1,6}) /g, '\\$1 '],
738
- [/`/g, '\\`'],
739
- [/^~~~/g, '\\~~~'],
740
- [/\[/g, '\\['],
741
- [/\]/g, '\\]'],
742
- [/^>/g, '\\>'],
743
- [/_/g, '\\_'],
744
- [/^(\d+)\. /g, '$1\\. ']
745
- ];
746
-
747
- function TurndownService (options) {
748
- if (!(this instanceof TurndownService)) return new TurndownService(options)
749
-
599
+ function TurndownService(options) {
600
+ if (!(this instanceof TurndownService)) return new TurndownService(options);
750
601
  var defaults = {
751
602
  rules: rules,
752
603
  headingStyle: 'setext',
@@ -761,19 +612,18 @@ function TurndownService (options) {
761
612
  br: ' ',
762
613
  preformattedCode: false,
763
614
  blankReplacement: function (content, node) {
764
- return node.isBlock ? '\n\n' : ''
615
+ return node.isBlock ? '\n\n' : '';
765
616
  },
766
617
  keepReplacement: function (content, node) {
767
- return node.isBlock ? '\n\n' + node.outerHTML + '\n\n' : node.outerHTML
618
+ return node.isBlock ? '\n\n' + node.outerHTML + '\n\n' : node.outerHTML;
768
619
  },
769
620
  defaultReplacement: function (content, node) {
770
- return node.isBlock ? '\n\n' + content + '\n\n' : content
621
+ return node.isBlock ? '\n\n' + content + '\n\n' : content;
771
622
  }
772
623
  };
773
624
  this.options = extend({}, defaults, options);
774
625
  this.rules = new Rules(this.options);
775
626
  }
776
-
777
627
  TurndownService.prototype = {
778
628
  /**
779
629
  * The entry point for converting a string or DOM node to Markdown
@@ -785,17 +635,12 @@ TurndownService.prototype = {
785
635
 
786
636
  turndown: function (input) {
787
637
  if (!canConvert(input)) {
788
- throw new TypeError(
789
- input + ' is not a string, or an element/document/fragment node.'
790
- )
638
+ throw new TypeError(input + ' is not a string, or an element/document/fragment node.');
791
639
  }
792
-
793
- if (input === '') return ''
794
-
640
+ if (input === '') return '';
795
641
  var output = process.call(this, new RootNode(input, this.options));
796
- return postProcess.call(this, output)
642
+ return postProcess.call(this, output);
797
643
  },
798
-
799
644
  /**
800
645
  * Add one or more plugins
801
646
  * @public
@@ -810,11 +655,10 @@ TurndownService.prototype = {
810
655
  } else if (typeof plugin === 'function') {
811
656
  plugin(this);
812
657
  } else {
813
- throw new TypeError('plugin must be a Function or an Array of Functions')
658
+ throw new TypeError('plugin must be a Function or an Array of Functions');
814
659
  }
815
- return this
660
+ return this;
816
661
  },
817
-
818
662
  /**
819
663
  * Adds a rule
820
664
  * @public
@@ -826,9 +670,8 @@ TurndownService.prototype = {
826
670
 
827
671
  addRule: function (key, rule) {
828
672
  this.rules.add(key, rule);
829
- return this
673
+ return this;
830
674
  },
831
-
832
675
  /**
833
676
  * Keep a node (as HTML) that matches the filter
834
677
  * @public
@@ -839,9 +682,8 @@ TurndownService.prototype = {
839
682
 
840
683
  keep: function (filter) {
841
684
  this.rules.keep(filter);
842
- return this
685
+ return this;
843
686
  },
844
-
845
687
  /**
846
688
  * Remove a node that matches the filter
847
689
  * @public
@@ -852,9 +694,8 @@ TurndownService.prototype = {
852
694
 
853
695
  remove: function (filter) {
854
696
  this.rules.remove(filter);
855
- return this
697
+ return this;
856
698
  },
857
-
858
699
  /**
859
700
  * Escapes Markdown syntax
860
701
  * @public
@@ -864,9 +705,7 @@ TurndownService.prototype = {
864
705
  */
865
706
 
866
707
  escape: function (string) {
867
- return escapes.reduce(function (accumulator, escape) {
868
- return accumulator.replace(escape[0], escape[1])
869
- }, string)
708
+ return escapeMarkdown(string);
870
709
  }
871
710
  };
872
711
 
@@ -878,20 +717,18 @@ TurndownService.prototype = {
878
717
  * @type String
879
718
  */
880
719
 
881
- function process (parentNode) {
720
+ function process(parentNode) {
882
721
  var self = this;
883
722
  return reduce.call(parentNode.childNodes, function (output, node) {
884
723
  node = new Node(node, self.options);
885
-
886
724
  var replacement = '';
887
725
  if (node.nodeType === 3) {
888
726
  replacement = node.isCode ? node.nodeValue : self.escape(node.nodeValue);
889
727
  } else if (node.nodeType === 1) {
890
728
  replacement = replacementForNode.call(self, node);
891
729
  }
892
-
893
- return join(output, replacement)
894
- }, '')
730
+ return join(output, replacement);
731
+ }, '');
895
732
  }
896
733
 
897
734
  /**
@@ -902,15 +739,14 @@ function process (parentNode) {
902
739
  * @type String
903
740
  */
904
741
 
905
- function postProcess (output) {
742
+ function postProcess(output) {
906
743
  var self = this;
907
744
  this.rules.forEach(function (rule) {
908
745
  if (typeof rule.append === 'function') {
909
746
  output = join(output, rule.append(self.options));
910
747
  }
911
748
  });
912
-
913
- return output.replace(/^[\t\r\n]+/, '').replace(/[\t\r\n\s]+$/, '')
749
+ return output.replace(/^[\t\r\n]+/, '').replace(/[\t\r\n\s]+$/, '');
914
750
  }
915
751
 
916
752
  /**
@@ -921,16 +757,12 @@ function postProcess (output) {
921
757
  * @type String
922
758
  */
923
759
 
924
- function replacementForNode (node) {
760
+ function replacementForNode(node) {
925
761
  var rule = this.rules.forNode(node);
926
762
  var content = process.call(this, node);
927
763
  var whitespace = node.flankingWhitespace;
928
764
  if (whitespace.leading || whitespace.trailing) content = content.trim();
929
- return (
930
- whitespace.leading +
931
- rule.replacement(content, node, this.options) +
932
- whitespace.trailing
933
- )
765
+ return whitespace.leading + rule.replacement(content, node, this.options) + whitespace.trailing;
934
766
  }
935
767
 
936
768
  /**
@@ -942,13 +774,12 @@ function replacementForNode (node) {
942
774
  * @type String
943
775
  */
944
776
 
945
- function join (output, replacement) {
777
+ function join(output, replacement) {
946
778
  var s1 = trimTrailingNewlines(output);
947
779
  var s2 = trimLeadingNewlines(replacement);
948
780
  var nls = Math.max(output.length - s1.length, replacement.length - s2.length);
949
781
  var separator = '\n\n'.substring(0, nls);
950
-
951
- return s1 + separator + s2
782
+ return s1 + separator + s2;
952
783
  }
953
784
 
954
785
  /**
@@ -959,15 +790,8 @@ function join (output, replacement) {
959
790
  * @type String|Object|Array|Boolean|Number
960
791
  */
961
792
 
962
- function canConvert (input) {
963
- return (
964
- input != null && (
965
- typeof input === 'string' ||
966
- (input.nodeType && (
967
- input.nodeType === 1 || input.nodeType === 9 || input.nodeType === 11
968
- ))
969
- )
970
- )
793
+ function canConvert(input) {
794
+ return input != null && (typeof input === 'string' || input.nodeType && (input.nodeType === 1 || input.nodeType === 9 || input.nodeType === 11));
971
795
  }
972
796
 
973
797
  module.exports = TurndownService;