import {
Fragment,
Node as PMNode
} from 'ak-editor-core';
import schema from '../schema';
import {
BulletListNode,
DocNode,
HeadingNode,
isBulletListNode,
isHardBreakNode,
isHeadingNode,
isHorizontalRuleNode,
isListItemNode,
isOrderedListNode,
isParagraphNode,
ListItemNode,
OrderedListNode,
ParagraphNode
} from 'ak-editor-core';
export default function encode(node: DocNode) {
const doc = makeDocument();
doc.body.appendChild(encodeFragment(node.content));
const html = doc.body.innerHTML;
// JIRA encodes empty documents as nbsp
if (html === '
') {
return ' ';
}
// Normalise to XHTML style self closing tags.
return html
.replace('
', '
')
.replace('
', '
')
.replace('
', '
')
.replace('
', '
');
function encodeNode(node: PMNode) {
if (node.isText) {
return encodeText(node);
} else if (isBulletListNode(node)) {
return encodeBulletList(node);
} else if (isHeadingNode(node)) {
return encodeHeading(node);
} else if (isHorizontalRuleNode(node)) {
return encodeHorizontalRule();
} else if (isListItemNode(node)) {
return encodeListItem(node);
} else if (isOrderedListNode(node)) {
return encodeOrderedList(node);
} else if (isParagraphNode(node)) {
return encodeParagraph(node);
} else if (isHardBreakNode(node)) {
return encodeHardBreak();
} else {
throw new Error(`Unexpected node '${(node as any).type.name}' for HTML encoding`);
}
}
function makeDocument() {
const doc = document.implementation.createHTMLDocument('');
doc.body = doc.createElement('body');
doc.documentElement.appendChild(doc.body);
return doc;
}
function encodeFragment(fragment: Fragment) {
const documentFragment = doc.createDocumentFragment();
fragment.forEach(node => documentFragment.appendChild(encodeNode(node)));
return documentFragment;
}
function encodeHeading(node: HeadingNode) {
function anchorNameEncode(name: string) {
const noSpaces = name.replace(/ /g, '');
const uriEncoded = encodeURIComponent(noSpaces);
const specialsEncoded = uriEncoded
.replace(/[!'()*]/g, c => ('%' + c.charCodeAt(0).toString(16)));
return specialsEncoded;
}
const elem = doc.createElement(`h${node.attrs.level}`);
const anchor = doc.createElement('a');
anchor.setAttribute('name', anchorNameEncode(node.textContent));
elem.appendChild(anchor);
elem.appendChild(encodeFragment(node.content));
return elem;
}
function encodeParagraph(node: ParagraphNode) {
const elem = doc.createElement('p');
elem.appendChild(encodeFragment(node.content));
return elem;
}
function encodeText(node: PMNode) {
if (node.text) {
const root = doc.createDocumentFragment();
let elem = root as Node;
for (const mark of node.marks) {
switch (mark.type) {
case schema.marks.strong:
elem = elem.appendChild(doc.createElement('b'));
break;
case schema.marks.em:
elem = elem.appendChild(doc.createElement('em'));
break;
case schema.marks.mono:
elem = elem.appendChild(doc.createElement('tt'));
break;
case schema.marks.strike:
elem = elem.appendChild(doc.createElement('del'));
break;
case schema.marks.u:
elem = elem.appendChild(doc.createElement('ins'));
break;
case schema.marks.subsup:
elem = elem.appendChild(doc.createElement(mark.attrs['type']));
break;
default:
throw new Error(`Unable to encode mark '${mark.type.name}'`);
}
}
elem.textContent = node.text;
return root;
} else {
return doc.createTextNode('');
}
}
function encodeHardBreak() {
return doc.createElement('br');
}
function encodeHorizontalRule() {
return doc.createElement('hr');
}
function encodeBulletList(node: BulletListNode) {
const elem = doc.createElement('ul');
elem.setAttribute('class', 'alternate');
elem.setAttribute('type', 'square');
elem.appendChild(encodeFragment(node.content));
return elem;
}
function encodeOrderedList(node: OrderedListNode) {
const elem = doc.createElement('ol');
elem.appendChild(encodeFragment(node.content));
return elem;
}
function encodeListItem(node: ListItemNode) {
const elem = doc.createElement('li');
// Strip the paragraph node from the list item.
if (node.content.childCount) {
const paragraph = node.content.child(0) as ParagraphNode;
elem.appendChild(encodeFragment(paragraph.content));
}
return elem;
}
}