turndown 7.2.2 → 7.2.3

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