mirror of
https://github.com/discourse/discourse.git
synced 2024-12-16 04:53:58 +08:00
DEV: Export Tag class to modify methods in plugin
This commit is contained in:
parent
c4de36624f
commit
3eff6a0e9b
|
@ -1,11 +1,15 @@
|
||||||
import parseHTML from 'discourse/helpers/parse-html';
|
import parseHTML from "discourse/helpers/parse-html";
|
||||||
|
|
||||||
const trimLeft = text => text.replace(/^\s+/, "");
|
const trimLeft = text => text.replace(/^\s+/, "");
|
||||||
const trimRight = text => text.replace(/\s+$/, "");
|
const trimRight = text => text.replace(/\s+$/, "");
|
||||||
const countPipes = text => (text.replace(/\\\|/, "").match(/\|/g) || []).length;
|
const countPipes = text => (text.replace(/\\\|/, "").match(/\|/g) || []).length;
|
||||||
const msoListClasses = ["MsoListParagraphCxSpFirst", "MsoListParagraphCxSpMiddle", "MsoListParagraphCxSpLast"];
|
const msoListClasses = [
|
||||||
|
"MsoListParagraphCxSpFirst",
|
||||||
|
"MsoListParagraphCxSpMiddle",
|
||||||
|
"MsoListParagraphCxSpLast"
|
||||||
|
];
|
||||||
|
|
||||||
class Tag {
|
export class Tag {
|
||||||
constructor(name, prefix = "", suffix = "", inline = false) {
|
constructor(name, prefix = "", suffix = "", inline = false) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.prefix = prefix;
|
this.prefix = prefix;
|
||||||
|
@ -36,8 +40,28 @@ class Tag {
|
||||||
}
|
}
|
||||||
|
|
||||||
static blocks() {
|
static blocks() {
|
||||||
return ["address", "article", "aside", "dd", "div", "dl", "dt", "fieldset", "figcaption", "figure",
|
return [
|
||||||
"footer", "form", "header", "hgroup", "hr", "main", "nav", "p", "pre", "section"];
|
"address",
|
||||||
|
"article",
|
||||||
|
"aside",
|
||||||
|
"dd",
|
||||||
|
"div",
|
||||||
|
"dl",
|
||||||
|
"dt",
|
||||||
|
"fieldset",
|
||||||
|
"figcaption",
|
||||||
|
"figure",
|
||||||
|
"footer",
|
||||||
|
"form",
|
||||||
|
"header",
|
||||||
|
"hgroup",
|
||||||
|
"hr",
|
||||||
|
"main",
|
||||||
|
"nav",
|
||||||
|
"p",
|
||||||
|
"pre",
|
||||||
|
"section"
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
static headings() {
|
static headings() {
|
||||||
|
@ -45,7 +69,14 @@ class Tag {
|
||||||
}
|
}
|
||||||
|
|
||||||
static emphases() {
|
static emphases() {
|
||||||
return [ ["b", "**"], ["strong", "**"], ["i", "*"], ["em", "*"], ["s", "~~"], ["strike", "~~"] ];
|
return [
|
||||||
|
["b", "**"],
|
||||||
|
["strong", "**"],
|
||||||
|
["i", "*"],
|
||||||
|
["em", "*"],
|
||||||
|
["s", "~~"],
|
||||||
|
["strike", "~~"]
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
static slices() {
|
static slices() {
|
||||||
|
@ -53,7 +84,21 @@ class Tag {
|
||||||
}
|
}
|
||||||
|
|
||||||
static trimmable() {
|
static trimmable() {
|
||||||
return [...Tag.blocks(), ...Tag.headings(), ...Tag.slices(), "li", "td", "th", "br", "hr", "blockquote", "table", "ol", "tr", "ul"];
|
return [
|
||||||
|
...Tag.blocks(),
|
||||||
|
...Tag.headings(),
|
||||||
|
...Tag.slices(),
|
||||||
|
"li",
|
||||||
|
"td",
|
||||||
|
"th",
|
||||||
|
"br",
|
||||||
|
"hr",
|
||||||
|
"blockquote",
|
||||||
|
"table",
|
||||||
|
"ol",
|
||||||
|
"tr",
|
||||||
|
"ul"
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
static block(name, prefix, suffix) {
|
static block(name, prefix, suffix) {
|
||||||
|
@ -162,7 +207,9 @@ class Tag {
|
||||||
const height = attr.height || pAttr.height;
|
const height = attr.height || pAttr.height;
|
||||||
|
|
||||||
if (width && height) {
|
if (width && height) {
|
||||||
const pipe = this.element.parentNames.includes("table") ? "\\|" : "|";
|
const pipe = this.element.parentNames.includes("table")
|
||||||
|
? "\\|"
|
||||||
|
: "|";
|
||||||
alt = `${alt}${pipe}${width}x${height}`;
|
alt = `${alt}${pipe}${width}x${height}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,9 +245,10 @@ class Tag {
|
||||||
toMarkdown() {
|
toMarkdown() {
|
||||||
const text = this.element.innerMarkdown().trim();
|
const text = this.element.innerMarkdown().trim();
|
||||||
|
|
||||||
if(text.includes("\n")) { // Unsupported format inside Markdown table cells
|
if (text.includes("\n")) {
|
||||||
|
// Unsupported format inside Markdown table cells
|
||||||
let e = this.element;
|
let e = this.element;
|
||||||
while(e = e.parent) {
|
while ((e = e.parent)) {
|
||||||
if (e.name === "table") {
|
if (e.name === "table") {
|
||||||
e.tag().invalid();
|
e.tag().invalid();
|
||||||
break;
|
break;
|
||||||
|
@ -216,12 +264,18 @@ class Tag {
|
||||||
static li() {
|
static li() {
|
||||||
return class extends Tag.slice("li", "\n") {
|
return class extends Tag.slice("li", "\n") {
|
||||||
decorate(text) {
|
decorate(text) {
|
||||||
let indent = this.element.filterParentNames(["ol", "ul"]).slice(1).map(() => "\t").join("");
|
let indent = this.element
|
||||||
|
.filterParentNames(["ol", "ul"])
|
||||||
|
.slice(1)
|
||||||
|
.map(() => "\t")
|
||||||
|
.join("");
|
||||||
const attrs = this.element.attributes;
|
const attrs = this.element.attributes;
|
||||||
|
|
||||||
if (msoListClasses.includes(attrs.class)) {
|
if (msoListClasses.includes(attrs.class)) {
|
||||||
try {
|
try {
|
||||||
const level = parseInt(attrs.style.match(/level./)[0].replace("level", ""));
|
const level = parseInt(
|
||||||
|
attrs.style.match(/level./)[0].replace("level", "")
|
||||||
|
);
|
||||||
indent = Array(level).join("\t") + indent;
|
indent = Array(level).join("\t") + indent;
|
||||||
} finally {
|
} finally {
|
||||||
if (attrs.class === "MsoListParagraphCxSpFirst") {
|
if (attrs.class === "MsoListParagraphCxSpFirst") {
|
||||||
|
@ -245,13 +299,15 @@ class Tag {
|
||||||
|
|
||||||
decorate(text) {
|
decorate(text) {
|
||||||
if (this.element.parentNames.includes("pre")) {
|
if (this.element.parentNames.includes("pre")) {
|
||||||
this.prefix = '\n\n```\n';
|
this.prefix = "\n\n```\n";
|
||||||
this.suffix = '\n```\n\n';
|
this.suffix = "\n```\n\n";
|
||||||
} else {
|
} else {
|
||||||
this.inline = true;
|
this.inline = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
text = $('<textarea />').html(text).text();
|
text = $("<textarea />")
|
||||||
|
.html(text)
|
||||||
|
.text();
|
||||||
return super.decorate(text);
|
return super.decorate(text);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -264,7 +320,10 @@ class Tag {
|
||||||
}
|
}
|
||||||
|
|
||||||
decorate(text) {
|
decorate(text) {
|
||||||
text = text.trim().replace(/\n{2,}>/g, "\n>").replace(/\n/g, "\n> ");
|
text = text
|
||||||
|
.trim()
|
||||||
|
.replace(/\n{2,}>/g, "\n>")
|
||||||
|
.replace(/\n/g, "\n> ");
|
||||||
return super.decorate(text);
|
return super.decorate(text);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -281,7 +340,7 @@ class Tag {
|
||||||
this.isValid = false;
|
this.isValid = false;
|
||||||
if (this.element.parentNames.includes("table")) {
|
if (this.element.parentNames.includes("table")) {
|
||||||
let e = this.element;
|
let e = this.element;
|
||||||
while(e = e.parent) {
|
while ((e = e.parent)) {
|
||||||
if (e.name === "table") {
|
if (e.name === "table") {
|
||||||
e.tag().invalid();
|
e.tag().invalid();
|
||||||
break;
|
break;
|
||||||
|
@ -294,10 +353,15 @@ class Tag {
|
||||||
text = super.decorate(text).replace(/\|\n{2,}\|/g, "|\n|");
|
text = super.decorate(text).replace(/\|\n{2,}\|/g, "|\n|");
|
||||||
const rows = text.trim().split("\n");
|
const rows = text.trim().split("\n");
|
||||||
const pipeCount = countPipes(rows[0]);
|
const pipeCount = countPipes(rows[0]);
|
||||||
this.isValid = this.isValid && rows.length > 1 && pipeCount > 2 && rows.reduce((a, c) => a && countPipes(c) <= pipeCount); // Unsupported table format for Markdown conversion
|
this.isValid =
|
||||||
|
this.isValid &&
|
||||||
|
rows.length > 1 &&
|
||||||
|
pipeCount > 2 &&
|
||||||
|
rows.reduce((a, c) => a && countPipes(c) <= pipeCount); // Unsupported table format for Markdown conversion
|
||||||
|
|
||||||
if (this.isValid) {
|
if (this.isValid) {
|
||||||
const splitterRow = [...Array(pipeCount-1)].map(() => "| --- ").join("") + "|\n";
|
const splitterRow =
|
||||||
|
[...Array(pipeCount - 1)].map(() => "| --- ").join("") + "|\n";
|
||||||
text = text.replace("|\n", "|\n" + splitterRow);
|
text = text.replace("|\n", "|\n" + splitterRow);
|
||||||
} else {
|
} else {
|
||||||
text = text.replace(/\|/g, " ");
|
text = text.replace(/\|/g, " ");
|
||||||
|
@ -330,7 +394,11 @@ class Tag {
|
||||||
text = "\n" + text;
|
text = "\n" + text;
|
||||||
const bullet = text.match(/\n\t*\*/)[0];
|
const bullet = text.match(/\n\t*\*/)[0];
|
||||||
|
|
||||||
for (let i = parseInt(this.element.attributes.start || 1); text.includes(bullet); i++) {
|
for (
|
||||||
|
let i = parseInt(this.element.attributes.start || 1);
|
||||||
|
text.includes(bullet);
|
||||||
|
i++
|
||||||
|
) {
|
||||||
text = text.replace(bullet, bullet.replace("*", `${i}.`));
|
text = text.replace(bullet, bullet.replace("*", `${i}.`));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -349,19 +417,35 @@ class Tag {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const tags = [
|
function tags() {
|
||||||
...Tag.blocks().map((b) => Tag.block(b)),
|
return [
|
||||||
|
...Tag.blocks().map(b => Tag.block(b)),
|
||||||
...Tag.headings().map((h, i) => Tag.heading(h, i + 1)),
|
...Tag.headings().map((h, i) => Tag.heading(h, i + 1)),
|
||||||
...Tag.slices().map((s) => Tag.slice(s, "\n")),
|
...Tag.slices().map(s => Tag.slice(s, "\n")),
|
||||||
...Tag.emphases().map((e) => Tag.emphasis(e[0], e[1])),
|
...Tag.emphases().map(e => Tag.emphasis(e[0], e[1])),
|
||||||
Tag.cell("td"), Tag.cell("th"),
|
Tag.cell("td"),
|
||||||
Tag.replace("br", "\n"), Tag.replace("hr", "\n---\n"), Tag.replace("head", ""),
|
Tag.cell("th"),
|
||||||
Tag.keep("ins"), Tag.keep("del"), Tag.keep("small"), Tag.keep("big"), Tag.keep("kbd"),
|
Tag.replace("br", "\n"),
|
||||||
Tag.li(), Tag.link(), Tag.image(), Tag.code(), Tag.blockquote(), Tag.table(), Tag.tr(), Tag.ol(), Tag.list("ul"),
|
Tag.replace("hr", "\n---\n"),
|
||||||
|
Tag.replace("head", ""),
|
||||||
|
Tag.keep("ins"),
|
||||||
|
Tag.keep("del"),
|
||||||
|
Tag.keep("small"),
|
||||||
|
Tag.keep("big"),
|
||||||
|
Tag.keep("kbd"),
|
||||||
|
Tag.li(),
|
||||||
|
Tag.link(),
|
||||||
|
Tag.image(),
|
||||||
|
Tag.code(),
|
||||||
|
Tag.blockquote(),
|
||||||
|
Tag.table(),
|
||||||
|
Tag.tr(),
|
||||||
|
Tag.ol(),
|
||||||
|
Tag.list("ul")
|
||||||
];
|
];
|
||||||
|
}
|
||||||
|
|
||||||
class Element {
|
class Element {
|
||||||
constructor(element, parent, previous, next) {
|
constructor(element, parent, previous, next) {
|
||||||
|
@ -390,7 +474,8 @@ class Element {
|
||||||
}
|
}
|
||||||
|
|
||||||
tag() {
|
tag() {
|
||||||
const tag = new (tags.filter(t => (new t().name === this.name))[0] || Tag)();
|
const tag = new (tags().filter(t => new t().name === this.name)[0] ||
|
||||||
|
Tag)();
|
||||||
tag.element = this;
|
tag.element = this;
|
||||||
return tag;
|
return tag;
|
||||||
}
|
}
|
||||||
|
@ -451,8 +536,8 @@ class Element {
|
||||||
let result = [];
|
let result = [];
|
||||||
|
|
||||||
for (let i = 0; i < elements.length; i++) {
|
for (let i = 0; i < elements.length; i++) {
|
||||||
const prev = (i === 0) ? null : elements[i-1];
|
const prev = i === 0 ? null : elements[i - 1];
|
||||||
const next = (i === elements.length) ? null : elements[i+1];
|
const next = i === elements.length ? null : elements[i + 1];
|
||||||
|
|
||||||
result.push(Element.toMarkdown(elements[i], parent, prev, next));
|
result.push(Element.toMarkdown(elements[i], parent, prev, next));
|
||||||
}
|
}
|
||||||
|
@ -470,7 +555,7 @@ function trimUnwanted(html) {
|
||||||
html = html.replace(/\r|\n| /g, " ");
|
html = html.replace(/\r|\n| /g, " ");
|
||||||
|
|
||||||
let match;
|
let match;
|
||||||
while (match = html.match(/<[^\s>]+[^>]*>\s{2,}<[^\s>]+[^>]*>/)) {
|
while ((match = html.match(/<[^\s>]+[^>]*>\s{2,}<[^\s>]+[^>]*>/))) {
|
||||||
html = html.replace(match[0], match[0].replace(/>\s{2,}</, "> <"));
|
html = html.replace(match[0], match[0].replace(/>\s{2,}</, "> <"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -488,7 +573,11 @@ function putPlaceholders(html) {
|
||||||
while (match) {
|
while (match) {
|
||||||
const placeholder = `DISCOURSE_PLACEHOLDER_${placeholders.length + 1}`;
|
const placeholder = `DISCOURSE_PLACEHOLDER_${placeholders.length + 1}`;
|
||||||
let code = match[1];
|
let code = match[1];
|
||||||
code = $('<div />').html(code).text().replace(/^\n/, '').replace(/\n$/, '');
|
code = $("<div />")
|
||||||
|
.html(code)
|
||||||
|
.text()
|
||||||
|
.replace(/^\n/, "")
|
||||||
|
.replace(/\n$/, "");
|
||||||
placeholders.push([placeholder, code]);
|
placeholders.push([placeholder, code]);
|
||||||
html = html.replace(match[0], `<code>${placeholder}</code>`);
|
html = html.replace(match[0], `<code>${placeholder}</code>`);
|
||||||
match = codeRegEx.exec(origHtml);
|
match = codeRegEx.exec(origHtml);
|
||||||
|
@ -509,8 +598,16 @@ export default function toMarkdown(html) {
|
||||||
try {
|
try {
|
||||||
const { elements, placeholders } = putPlaceholders(html);
|
const { elements, placeholders } = putPlaceholders(html);
|
||||||
let markdown = Element.parse(elements).trim();
|
let markdown = Element.parse(elements).trim();
|
||||||
markdown = markdown.replace(/^<b>/, "").replace(/<\/b>$/, "").trim(); // fix for google doc copy paste
|
markdown = markdown
|
||||||
markdown = markdown.replace(/\n +/g, "\n").replace(/ +\n/g, "\n").replace(/ {2,}/g, " ").replace(/\n{3,}/g, "\n\n").replace(/\t/g, " ");
|
.replace(/^<b>/, "")
|
||||||
|
.replace(/<\/b>$/, "")
|
||||||
|
.trim(); // fix for google doc copy paste
|
||||||
|
markdown = markdown
|
||||||
|
.replace(/\n +/g, "\n")
|
||||||
|
.replace(/ +\n/g, "\n")
|
||||||
|
.replace(/ {2,}/g, " ")
|
||||||
|
.replace(/\n{3,}/g, "\n\n")
|
||||||
|
.replace(/\t/g, " ");
|
||||||
return replacePlaceholders(markdown, placeholders);
|
return replacePlaceholders(markdown, placeholders);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return "";
|
return "";
|
||||||
|
|
Loading…
Reference in New Issue
Block a user