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