DEV: Export Tag class to modify methods in plugin

This commit is contained in:
Vinoth Kannan 2018-08-01 15:10:15 +05:30 committed by Guo Xiang Tan
parent c4de36624f
commit 3eff6a0e9b

View File

@ -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|&nbsp;/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 "";
}
}