Add table viewing code.
This commit is contained in:
parent
f6f29adacc
commit
d69b78469c
4 changed files with 87 additions and 2 deletions
|
@ -64,6 +64,15 @@ export class ListBlock {
|
||||||
get type() { return "list"; }
|
get type() { return "list"; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class TableBlock {
|
||||||
|
constructor(head, body) {
|
||||||
|
this.head = head;
|
||||||
|
this.body = body;
|
||||||
|
}
|
||||||
|
|
||||||
|
get type() { return "table"; }
|
||||||
|
}
|
||||||
|
|
||||||
export class RulePart {
|
export class RulePart {
|
||||||
get type( ) { return "rule"; }
|
get type( ) { return "rule"; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { MessageBody, HeaderBlock, ListBlock, CodeBlock, PillPart, FormatPart, NewLinePart, RulePart, TextPart, LinkPart, ImagePart } from "./MessageBody.js"
|
import { MessageBody, HeaderBlock, TableBlock, ListBlock, CodeBlock, PillPart, FormatPart, NewLinePart, RulePart, TextPart, LinkPart, ImagePart } from "./MessageBody.js"
|
||||||
import { linkify } from "./linkify/linkify.js";
|
import { linkify } from "./linkify/linkify.js";
|
||||||
import { parsePillLink } from "./pills.js"
|
import { parsePillLink } from "./pills.js"
|
||||||
|
|
||||||
|
@ -53,6 +53,12 @@ class Deserializer {
|
||||||
return new ListBlock(start, nodes);
|
return new ListBlock(start, nodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_ensureElement(node, tag) {
|
||||||
|
return node &&
|
||||||
|
this.result.isElementNode(node) &&
|
||||||
|
this.result.getNodeElementName(node) === tag;
|
||||||
|
}
|
||||||
|
|
||||||
parseCodeBlock(node) {
|
parseCodeBlock(node) {
|
||||||
const result = this.result;
|
const result = this.result;
|
||||||
let codeNode;
|
let codeNode;
|
||||||
|
@ -60,7 +66,7 @@ class Deserializer {
|
||||||
codeNode = child;
|
codeNode = child;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!(codeNode && result.getNodeElementName(codeNode) === "CODE")) {
|
if (!this._ensureElement(codeNode, "CODE")) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
let language = "";
|
let language = "";
|
||||||
|
@ -89,6 +95,56 @@ class Deserializer {
|
||||||
return new ImagePart(url, width, height, alt, title);
|
return new ImagePart(url, width, height, alt, title);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
parseTableRow(row, tag) {
|
||||||
|
const cells = [];
|
||||||
|
for (const node of this.result.getChildNodes(row)) {
|
||||||
|
if(!this._ensureElement(node, tag)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const children = this.result.getChildNodes(node);
|
||||||
|
const inlines = this.parseInlineNodes(children);
|
||||||
|
cells.push(inlines);
|
||||||
|
}
|
||||||
|
return cells;
|
||||||
|
}
|
||||||
|
|
||||||
|
parseTableHead(head) {
|
||||||
|
let headRow = null;
|
||||||
|
for (const node of this.result.getChildNodes(head)) {
|
||||||
|
headRow = node;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (this._ensureElement(headRow, "TR")) {
|
||||||
|
return this.parseTableRow(headRow, "TH");
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
parseTableBody(body) {
|
||||||
|
const rows = [];
|
||||||
|
for (const node of this.result.getChildNodes(body)) {
|
||||||
|
if(!this._ensureElement(node, "TR")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
rows.push(this.parseTableRow(node, "TD"));
|
||||||
|
}
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
parseTable(node) {
|
||||||
|
// We are only assuming iterable, so convert to arrary for indexing.
|
||||||
|
const children = Array.from(this.result.getChildNodes(node));
|
||||||
|
let head, body;
|
||||||
|
if (this._ensureElement(children[0], "THEAD") && this._ensureElement(children[1], "TBODY")) {
|
||||||
|
head = this.parseTableHead(children[0]);
|
||||||
|
body = this.parseTableBody(children[1]);
|
||||||
|
} else if (this._ensureElement(children[0], "TBODY")) {
|
||||||
|
head = null;
|
||||||
|
body = this.parseTableBody(children[0]);
|
||||||
|
}
|
||||||
|
return new TableBlock(head, body);
|
||||||
|
}
|
||||||
|
|
||||||
/** Once a node is known to be an element,
|
/** Once a node is known to be an element,
|
||||||
* attempt to interpret it as an inline element.
|
* attempt to interpret it as an inline element.
|
||||||
*
|
*
|
||||||
|
@ -161,6 +217,8 @@ class Deserializer {
|
||||||
const inlines = this.parseInlineNodes(children);
|
const inlines = this.parseInlineNodes(children);
|
||||||
return new FormatPart(tag, inlines);
|
return new FormatPart(tag, inlines);
|
||||||
}
|
}
|
||||||
|
case "TABLE":
|
||||||
|
return this.parseTable(node);
|
||||||
default: {
|
default: {
|
||||||
if (!basicBlock.includes(tag)) {
|
if (!basicBlock.includes(tag)) {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -94,6 +94,7 @@ export const TAG_NAMES = {
|
||||||
[HTML_NS]: [
|
[HTML_NS]: [
|
||||||
"br", "a", "ol", "ul", "li", "div", "h1", "h2", "h3", "h4", "h5", "h6",
|
"br", "a", "ol", "ul", "li", "div", "h1", "h2", "h3", "h4", "h5", "h6",
|
||||||
"p", "strong", "em", "span", "img", "section", "main", "article", "aside", "del", "blockquote",
|
"p", "strong", "em", "span", "img", "section", "main", "article", "aside", "del", "blockquote",
|
||||||
|
"table", "thead", "tbody", "tr", "th", "td",
|
||||||
"pre", "code", "button", "time", "input", "textarea", "label", "form", "progress", "output", "video"],
|
"pre", "code", "button", "time", "input", "textarea", "label", "form", "progress", "output", "video"],
|
||||||
[SVG_NS]: ["svg", "circle"]
|
[SVG_NS]: ["svg", "circle"]
|
||||||
};
|
};
|
||||||
|
|
|
@ -64,12 +64,29 @@ function renderPill(pillPart) {
|
||||||
return tag.a({ class: "pill", href: pillPart.href }, children);
|
return tag.a({ class: "pill", href: pillPart.href }, children);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderTable(tablePart) {
|
||||||
|
const children = [];
|
||||||
|
if (tablePart.head) {
|
||||||
|
const headers = tablePart.head
|
||||||
|
.map(cell => tag.th({}, renderParts(cell)));
|
||||||
|
children.push(tag.thead({}, tag.tr({}, headers)))
|
||||||
|
}
|
||||||
|
const rows = [];
|
||||||
|
for (const row of tablePart.body) {
|
||||||
|
const data = row.map(cell => tag.td({}, renderParts(cell)));
|
||||||
|
rows.push(tag.tr({}, data));
|
||||||
|
}
|
||||||
|
children.push(tag.tbody({}, rows));
|
||||||
|
return tag.table({}, children);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Map from part to function that outputs DOM for the part
|
* Map from part to function that outputs DOM for the part
|
||||||
*/
|
*/
|
||||||
const formatFunction = {
|
const formatFunction = {
|
||||||
header: headerBlock => tag["h" + Math.min(6,headerBlock.level)]({}, renderParts(headerBlock.inlines)),
|
header: headerBlock => tag["h" + Math.min(6,headerBlock.level)]({}, renderParts(headerBlock.inlines)),
|
||||||
codeblock: codeBlock => tag.pre({}, tag.code({}, text(codeBlock.text))),
|
codeblock: codeBlock => tag.pre({}, tag.code({}, text(codeBlock.text))),
|
||||||
|
table: tableBlock => renderTable(tableBlock),
|
||||||
emph: emphPart => tag.em({}, renderParts(emphPart.inlines)),
|
emph: emphPart => tag.em({}, renderParts(emphPart.inlines)),
|
||||||
code: codePart => tag.code({}, text(codePart.text)),
|
code: codePart => tag.code({}, text(codePart.text)),
|
||||||
text: textPart => text(textPart.text),
|
text: textPart => text(textPart.text),
|
||||||
|
|
Reference in a new issue