mirror of
https://github.com/discourse/discourse.git
synced 2024-12-15 13:03:43 +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 trimRight = text => text.replace(/\s+$/,"");
|
||||
const countPipes = text => (text.replace(/\\\|/,"").match(/\|/g) || []).length;
|
||||
const msoListClasses = ["MsoListParagraphCxSpFirst", "MsoListParagraphCxSpMiddle", "MsoListParagraphCxSpLast"];
|
||||
const trimLeft = text => text.replace(/^\s+/, "");
|
||||
const trimRight = text => text.replace(/\s+$/, "");
|
||||
const countPipes = text => (text.replace(/\\\|/, "").match(/\|/g) || []).length;
|
||||
const msoListClasses = [
|
||||
"MsoListParagraphCxSpFirst",
|
||||
"MsoListParagraphCxSpMiddle",
|
||||
"MsoListParagraphCxSpLast"
|
||||
];
|
||||
|
||||
class Tag {
|
||||
export class Tag {
|
||||
constructor(name, prefix = "", suffix = "", inline = false) {
|
||||
this.name = name;
|
||||
this.prefix = prefix;
|
||||
|
@ -36,8 +40,28 @@ class Tag {
|
|||
}
|
||||
|
||||
static blocks() {
|
||||
return ["address", "article", "aside", "dd", "div", "dl", "dt", "fieldset", "figcaption", "figure",
|
||||
"footer", "form", "header", "hgroup", "hr", "main", "nav", "p", "pre", "section"];
|
||||
return [
|
||||
"address",
|
||||
"article",
|
||||
"aside",
|
||||
"dd",
|
||||
"div",
|
||||
"dl",
|
||||
"dt",
|
||||
"fieldset",
|
||||
"figcaption",
|
||||
"figure",
|
||||
"footer",
|
||||
"form",
|
||||
"header",
|
||||
"hgroup",
|
||||
"hr",
|
||||
"main",
|
||||
"nav",
|
||||
"p",
|
||||
"pre",
|
||||
"section"
|
||||
];
|
||||
}
|
||||
|
||||
static headings() {
|
||||
|
@ -45,7 +69,14 @@ class Tag {
|
|||
}
|
||||
|
||||
static emphases() {
|
||||
return [ ["b", "**"], ["strong", "**"], ["i", "*"], ["em", "*"], ["s", "~~"], ["strike", "~~"] ];
|
||||
return [
|
||||
["b", "**"],
|
||||
["strong", "**"],
|
||||
["i", "*"],
|
||||
["em", "*"],
|
||||
["s", "~~"],
|
||||
["strike", "~~"]
|
||||
];
|
||||
}
|
||||
|
||||
static slices() {
|
||||
|
@ -53,7 +84,21 @@ class Tag {
|
|||
}
|
||||
|
||||
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) {
|
||||
|
@ -162,7 +207,9 @@ class Tag {
|
|||
const height = attr.height || pAttr.height;
|
||||
|
||||
if (width && height) {
|
||||
const pipe = this.element.parentNames.includes("table") ? "\\|" : "|";
|
||||
const pipe = this.element.parentNames.includes("table")
|
||||
? "\\|"
|
||||
: "|";
|
||||
alt = `${alt}${pipe}${width}x${height}`;
|
||||
}
|
||||
|
||||
|
@ -198,9 +245,10 @@ class Tag {
|
|||
toMarkdown() {
|
||||
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;
|
||||
while(e = e.parent) {
|
||||
while ((e = e.parent)) {
|
||||
if (e.name === "table") {
|
||||
e.tag().invalid();
|
||||
break;
|
||||
|
@ -216,12 +264,18 @@ class Tag {
|
|||
static li() {
|
||||
return class extends Tag.slice("li", "\n") {
|
||||
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;
|
||||
|
||||
if (msoListClasses.includes(attrs.class)) {
|
||||
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;
|
||||
} finally {
|
||||
if (attrs.class === "MsoListParagraphCxSpFirst") {
|
||||
|
@ -245,13 +299,15 @@ class Tag {
|
|||
|
||||
decorate(text) {
|
||||
if (this.element.parentNames.includes("pre")) {
|
||||
this.prefix = '\n\n```\n';
|
||||
this.suffix = '\n```\n\n';
|
||||
this.prefix = "\n\n```\n";
|
||||
this.suffix = "\n```\n\n";
|
||||
} else {
|
||||
this.inline = true;
|
||||
}
|
||||
|
||||
text = $('<textarea />').html(text).text();
|
||||
text = $("<textarea />")
|
||||
.html(text)
|
||||
.text();
|
||||
return super.decorate(text);
|
||||
}
|
||||
};
|
||||
|
@ -264,7 +320,10 @@ class Tag {
|
|||
}
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
@ -281,7 +340,7 @@ class Tag {
|
|||
this.isValid = false;
|
||||
if (this.element.parentNames.includes("table")) {
|
||||
let e = this.element;
|
||||
while(e = e.parent) {
|
||||
while ((e = e.parent)) {
|
||||
if (e.name === "table") {
|
||||
e.tag().invalid();
|
||||
break;
|
||||
|
@ -294,10 +353,15 @@ class Tag {
|
|||
text = super.decorate(text).replace(/\|\n{2,}\|/g, "|\n|");
|
||||
const rows = text.trim().split("\n");
|
||||
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) {
|
||||
const splitterRow = [...Array(pipeCount-1)].map(() => "| --- ").join("") + "|\n";
|
||||
const splitterRow =
|
||||
[...Array(pipeCount - 1)].map(() => "| --- ").join("") + "|\n";
|
||||
text = text.replace("|\n", "|\n" + splitterRow);
|
||||
} else {
|
||||
text = text.replace(/\|/g, " ");
|
||||
|
@ -330,7 +394,11 @@ class Tag {
|
|||
text = "\n" + text;
|
||||
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}.`));
|
||||
}
|
||||
|
||||
|
@ -349,19 +417,35 @@ class Tag {
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const tags = [
|
||||
...Tag.blocks().map((b) => Tag.block(b)),
|
||||
...Tag.headings().map((h, i) => Tag.heading(h, i + 1)),
|
||||
...Tag.slices().map((s) => Tag.slice(s, "\n")),
|
||||
...Tag.emphases().map((e) => Tag.emphasis(e[0], e[1])),
|
||||
Tag.cell("td"), Tag.cell("th"),
|
||||
Tag.replace("br", "\n"), 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"),
|
||||
];
|
||||
function tags() {
|
||||
return [
|
||||
...Tag.blocks().map(b => Tag.block(b)),
|
||||
...Tag.headings().map((h, i) => Tag.heading(h, i + 1)),
|
||||
...Tag.slices().map(s => Tag.slice(s, "\n")),
|
||||
...Tag.emphases().map(e => Tag.emphasis(e[0], e[1])),
|
||||
Tag.cell("td"),
|
||||
Tag.cell("th"),
|
||||
Tag.replace("br", "\n"),
|
||||
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 {
|
||||
constructor(element, parent, previous, next) {
|
||||
|
@ -390,7 +474,8 @@ class Element {
|
|||
}
|
||||
|
||||
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;
|
||||
return tag;
|
||||
}
|
||||
|
@ -424,7 +509,7 @@ class Element {
|
|||
}
|
||||
|
||||
toMarkdown() {
|
||||
switch(this.type) {
|
||||
switch (this.type) {
|
||||
case "text":
|
||||
return this.text();
|
||||
break;
|
||||
|
@ -451,8 +536,8 @@ class Element {
|
|||
let result = [];
|
||||
|
||||
for (let i = 0; i < elements.length; i++) {
|
||||
const prev = (i === 0) ? null : elements[i-1];
|
||||
const next = (i === elements.length) ? null : elements[i+1];
|
||||
const prev = i === 0 ? null : elements[i - 1];
|
||||
const next = i === elements.length ? null : elements[i + 1];
|
||||
|
||||
result.push(Element.toMarkdown(elements[i], parent, prev, next));
|
||||
}
|
||||
|
@ -470,7 +555,7 @@ function trimUnwanted(html) {
|
|||
html = html.replace(/\r|\n| /g, " ");
|
||||
|
||||
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,}</, "> <"));
|
||||
}
|
||||
|
||||
|
@ -485,10 +570,14 @@ function putPlaceholders(html) {
|
|||
let match = codeRegEx.exec(origHtml);
|
||||
let placeholders = [];
|
||||
|
||||
while(match) {
|
||||
while (match) {
|
||||
const placeholder = `DISCOURSE_PLACEHOLDER_${placeholders.length + 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]);
|
||||
html = html.replace(match[0], `<code>${placeholder}</code>`);
|
||||
match = codeRegEx.exec(origHtml);
|
||||
|
@ -509,10 +598,18 @@ export default function toMarkdown(html) {
|
|||
try {
|
||||
const { elements, placeholders } = putPlaceholders(html);
|
||||
let markdown = Element.parse(elements).trim();
|
||||
markdown = markdown.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, " ");
|
||||
markdown = markdown
|
||||
.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);
|
||||
} catch(err) {
|
||||
} catch (err) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user