641 lines
20 KiB
JavaScript
641 lines
20 KiB
JavaScript
const MOBILEDOC_VERSION = '0.3.1';
|
|
const GHOST_VERSION = '4.0';
|
|
const BLANK_DOC$1 = {
|
|
version: MOBILEDOC_VERSION,
|
|
ghostVersion: GHOST_VERSION,
|
|
markups: [],
|
|
atoms: [],
|
|
cards: [],
|
|
sections: [[1, 'p', [[0, [], 0, '']]]]
|
|
};
|
|
const MD_TEXT_SECTION = 1;
|
|
const MD_LIST_SECTION = 3;
|
|
const MD_CARD_SECTION = 10;
|
|
const MD_TEXT_MARKER = 0;
|
|
const MD_ATOM_MARKER = 1;
|
|
const L_IS_BOLD = 1;
|
|
const L_IS_ITALIC = 1 << 1;
|
|
const L_IS_STRIKETHROUGH = 1 << 2;
|
|
const L_IS_UNDERLINE = 1 << 3;
|
|
const L_IS_CODE = 1 << 4;
|
|
const L_IS_SUBSCRIPT = 1 << 5;
|
|
const L_IS_SUPERSCRIPT = 1 << 6;
|
|
const L_FORMAT_MAP = new Map([[L_IS_BOLD, 'strong'], [L_IS_ITALIC, 'em'], [L_IS_STRIKETHROUGH, 's'], [L_IS_UNDERLINE, 'u'], [L_IS_CODE, 'code'], [L_IS_SUBSCRIPT, 'sub'], [L_IS_SUPERSCRIPT, 'sup']]);
|
|
const HEADING_TYPES = ['heading', 'extended-heading'];
|
|
const TEXT_TYPES = ['text', 'extended-text'];
|
|
|
|
// TODO: Feels a little too explicit as it will need updating every time we add a new card.
|
|
//
|
|
// One alternative is to use a list of all built-in Lexical types and assume that anything
|
|
// not listed is a card but that feels more dangerous.
|
|
//
|
|
// Another alternative is to grab the list of cards from kg-default-nodes but that's creating
|
|
// more inter-dependencies that makes development setup tricky.
|
|
const KNOWN_CARDS = ['audio', 'bookmark', 'button', 'callout', 'codeblock', 'email-cta', 'email', 'embed', 'file', 'gallery', 'header', 'horizontalrule', 'html', 'image', 'markdown', 'paywall', 'product', 'signup', 'toggle', 'video'];
|
|
const CARD_NAME_MAP$1 = {
|
|
codeblock: 'code',
|
|
horizontalrule: 'hr'
|
|
};
|
|
const CARD_PROPERTY_MAP$1 = {
|
|
embed: {
|
|
embedType: 'type'
|
|
}
|
|
};
|
|
function lexicalToMobiledoc(serializedLexical) {
|
|
if (serializedLexical === null || serializedLexical === undefined || serializedLexical === '') {
|
|
return JSON.stringify(BLANK_DOC$1);
|
|
}
|
|
const lexical = JSON.parse(serializedLexical);
|
|
if (!lexical.root) {
|
|
return JSON.stringify(BLANK_DOC$1);
|
|
}
|
|
const mobiledoc = buildEmptyDoc$1();
|
|
lexical.root.children.forEach(child => addRootChild$1(child, mobiledoc));
|
|
return JSON.stringify(mobiledoc);
|
|
}
|
|
|
|
/* internal functions ------------------------------------------------------- */
|
|
|
|
function buildEmptyDoc$1() {
|
|
return {
|
|
version: MOBILEDOC_VERSION,
|
|
ghostVersion: GHOST_VERSION,
|
|
atoms: [],
|
|
cards: [],
|
|
markups: [],
|
|
sections: []
|
|
};
|
|
}
|
|
function getOrSetMarkupIndex(markup, mobiledoc) {
|
|
let index = mobiledoc.markups.findIndex(m => m[0] === markup);
|
|
if (index === -1) {
|
|
mobiledoc.markups.push([markup]);
|
|
index = mobiledoc.markups.length - 1;
|
|
}
|
|
return index;
|
|
}
|
|
function getOrSetAtomIndex(atom, mobiledoc) {
|
|
let index = mobiledoc.atoms.findIndex(m => m[0] === atom);
|
|
if (index === -1) {
|
|
mobiledoc.atoms.push(atom);
|
|
index = mobiledoc.atoms.length - 1;
|
|
}
|
|
return index;
|
|
}
|
|
function addRootChild$1(child, mobiledoc) {
|
|
if (child.type === 'paragraph') {
|
|
addTextSection(child, mobiledoc);
|
|
}
|
|
if (HEADING_TYPES.includes(child.type)) {
|
|
addTextSection(child, mobiledoc, child.tag);
|
|
}
|
|
if (child.type === 'quote') {
|
|
addTextSection(child, mobiledoc, 'blockquote');
|
|
}
|
|
if (child.type === 'aside') {
|
|
addTextSection(child, mobiledoc, 'aside');
|
|
}
|
|
if (child.type === 'list') {
|
|
addListSection(child, mobiledoc, child.tag);
|
|
}
|
|
if (KNOWN_CARDS.includes(child.type)) {
|
|
addCardSection(child, mobiledoc);
|
|
}
|
|
}
|
|
function addTextSection(childWithFormats, mobiledoc, tagName = 'p') {
|
|
const markers = buildMarkers(childWithFormats, mobiledoc);
|
|
const section = [MD_TEXT_SECTION, tagName, markers];
|
|
mobiledoc.sections.push(section);
|
|
}
|
|
function addListSection(listChild, mobiledoc, tagName = 'ul') {
|
|
const listItems = buildListItems(listChild, mobiledoc);
|
|
const section = [MD_LIST_SECTION, tagName, listItems];
|
|
mobiledoc.sections.push(section);
|
|
}
|
|
function buildListItems(listRoot, mobiledoc) {
|
|
const listItems = [];
|
|
flattenListChildren(listRoot);
|
|
listRoot.children.forEach(listItemChild => {
|
|
if (listItemChild.type === 'listitem') {
|
|
const markers = buildMarkers(listItemChild, mobiledoc);
|
|
listItems.push(markers);
|
|
}
|
|
});
|
|
return listItems;
|
|
}
|
|
function flattenListChildren(listRoot) {
|
|
const flatListItems = [];
|
|
function traverse(item) {
|
|
item.children?.forEach(child => {
|
|
child.children?.forEach(grandchild => {
|
|
if (grandchild.type === 'list') {
|
|
traverse(grandchild);
|
|
child.children.splice(child.children.indexOf(grandchild), 1);
|
|
}
|
|
});
|
|
if (child.type === 'listitem' && child.children.length) {
|
|
flatListItems.push(child);
|
|
}
|
|
});
|
|
}
|
|
traverse(listRoot);
|
|
listRoot.children = flatListItems;
|
|
}
|
|
function buildMarkers(childWithFormats, mobiledoc) {
|
|
const markers = [];
|
|
if (!childWithFormats.children.length) {
|
|
markers.push([MD_TEXT_MARKER, [], 0, '']);
|
|
} else {
|
|
// mobiledoc tracks opened/closed formats across markers whereas lexical
|
|
// lists all formats for each marker so we need to manually track open formats
|
|
let openMarkups = [];
|
|
|
|
// markup: a specific format, or tag name+attributes
|
|
// marker: a piece of text with 0 or more markups
|
|
|
|
childWithFormats.children.forEach((child, childIndex) => {
|
|
if (TEXT_TYPES.includes(child.type)) {
|
|
if (child.format !== 0) {
|
|
// text child has formats, track which are new and which have closed
|
|
const openedFormats = [];
|
|
const childFormats = readFormat(child.format);
|
|
let closedFormatCount = 0;
|
|
childFormats.forEach(format => {
|
|
if (!openMarkups.includes(format)) {
|
|
openMarkups.push(format);
|
|
openedFormats.push(format);
|
|
}
|
|
});
|
|
|
|
// mobiledoc will immediately close any formats if the next section doesn't use them or it's not a text section
|
|
if (!childWithFormats.children[childIndex + 1] || !TEXT_TYPES.includes(childWithFormats.children[childIndex + 1].type)) {
|
|
// no more children, close all formats
|
|
closedFormatCount = openMarkups.length;
|
|
openMarkups = [];
|
|
} else {
|
|
const nextChild = childWithFormats.children[childIndex + 1];
|
|
const nextFormats = readFormat(nextChild.format);
|
|
const firstMissingFormatIndex = openMarkups.findIndex(format => !nextFormats.includes(format));
|
|
if (firstMissingFormatIndex !== -1) {
|
|
const formatsToClose = openMarkups.slice(firstMissingFormatIndex);
|
|
closedFormatCount = formatsToClose.length;
|
|
openMarkups = openMarkups.slice(0, firstMissingFormatIndex);
|
|
}
|
|
}
|
|
const markupIndexes = openedFormats.map(format => getOrSetMarkupIndex(format, mobiledoc));
|
|
markers.push([MD_TEXT_MARKER, markupIndexes, closedFormatCount, child.text]);
|
|
} else {
|
|
// text child has no formats so we close all formats in mobiledoc
|
|
let closedFormatCount = openMarkups.length;
|
|
openMarkups = [];
|
|
markers.push([MD_TEXT_MARKER, [], closedFormatCount, child.text]);
|
|
}
|
|
}
|
|
if (child.type === 'link') {
|
|
const linkMarkup = ['a', ['href', child.url]];
|
|
const linkMarkupIndex = mobiledoc.markups.push(linkMarkup) - 1;
|
|
child.children.forEach((linkChild, linkChildIndex) => {
|
|
if (linkChild.format !== 0) {
|
|
const openedMarkupIndexes = [];
|
|
const openedFormats = [];
|
|
|
|
// first child of a link opens the link markup
|
|
if (linkChildIndex === 0) {
|
|
openMarkups.push(linkMarkup);
|
|
openedMarkupIndexes.push(linkMarkupIndex);
|
|
}
|
|
|
|
// text child has formats, track which are new and which have closed
|
|
const childFormats = readFormat(linkChild.format);
|
|
let closedMarkupCount = 0;
|
|
childFormats.forEach(format => {
|
|
if (!openMarkups.includes(format)) {
|
|
openMarkups.push(format);
|
|
openedFormats.push(format);
|
|
}
|
|
});
|
|
|
|
// mobiledoc will immediately close any formats if the next section doesn't use them
|
|
if (!child.children[linkChildIndex + 1]) {
|
|
// last child of a link closes all markups
|
|
closedMarkupCount = openMarkups.length;
|
|
openMarkups = [];
|
|
} else {
|
|
const nextChild = child.children[linkChildIndex + 1];
|
|
const nextFormats = readFormat(nextChild.format);
|
|
const firstMissingFormatIndex = openMarkups.findIndex(markup => {
|
|
const markupIsLink = JSON.stringify(markup) === JSON.stringify(linkMarkup);
|
|
return !markupIsLink && !nextFormats.includes(markup);
|
|
});
|
|
if (firstMissingFormatIndex !== -1) {
|
|
const formatsToClose = openMarkups.slice(firstMissingFormatIndex);
|
|
closedMarkupCount = formatsToClose.length;
|
|
openMarkups = openMarkups.slice(0, firstMissingFormatIndex);
|
|
}
|
|
}
|
|
openedMarkupIndexes.push(...openedFormats.map(format => getOrSetMarkupIndex(format, mobiledoc)));
|
|
markers.push([MD_TEXT_MARKER, openedMarkupIndexes, closedMarkupCount, linkChild.text]);
|
|
} else {
|
|
const openedMarkupIndexes = [];
|
|
|
|
// first child of a link opens the link markup
|
|
if (linkChildIndex === 0) {
|
|
openMarkups.push(linkMarkup);
|
|
openedMarkupIndexes.push(linkMarkupIndex);
|
|
}
|
|
let closedMarkupCount = openMarkups.length - 1; // don't close the link markup, just the others
|
|
|
|
// last child of a link closes all markups
|
|
if (!child.children[linkChildIndex + 1]) {
|
|
closedMarkupCount += 1; // close the link markup
|
|
openMarkups = [];
|
|
}
|
|
markers.push([MD_TEXT_MARKER, openedMarkupIndexes, closedMarkupCount, linkChild.text]);
|
|
}
|
|
});
|
|
}
|
|
if (child.type === 'linebreak') {
|
|
const atom = ['soft-return', '', {}];
|
|
const atomIndex = getOrSetAtomIndex(atom, mobiledoc);
|
|
markers.push([MD_ATOM_MARKER, [], 0, atomIndex]);
|
|
}
|
|
});
|
|
}
|
|
return markers;
|
|
}
|
|
|
|
// Lexical stores formats as a bitmask, so we need to read the bitmask to
|
|
// determine which formats are present
|
|
function readFormat(format) {
|
|
const formats = [];
|
|
L_FORMAT_MAP.forEach((value, key) => {
|
|
if ((format & key) !== 0) {
|
|
formats.push(value);
|
|
}
|
|
});
|
|
return formats;
|
|
}
|
|
function addCardSection(child, mobiledoc) {
|
|
const cardType = child.type;
|
|
let cardName = child.type;
|
|
// rename card if there's a difference between lexical/mobiledoc
|
|
if (CARD_NAME_MAP$1[cardName]) {
|
|
cardName = CARD_NAME_MAP$1[cardName];
|
|
}
|
|
// don't include type in the payload
|
|
delete child.type;
|
|
|
|
// rename any properties to match mobiledoc
|
|
if (CARD_PROPERTY_MAP$1[cardType]) {
|
|
const map = CARD_PROPERTY_MAP$1[cardType];
|
|
for (const [key, value] of Object.entries(map)) {
|
|
child[value] = child[key];
|
|
delete child[key];
|
|
}
|
|
}
|
|
const card = [cardName, child];
|
|
mobiledoc.cards.push(card);
|
|
const cardIndex = mobiledoc.cards.length - 1;
|
|
const section = [MD_CARD_SECTION, cardIndex];
|
|
mobiledoc.sections.push(section);
|
|
}
|
|
|
|
const BLANK_DOC = {
|
|
root: {
|
|
children: [],
|
|
direction: null,
|
|
format: '',
|
|
indent: 0,
|
|
type: 'root',
|
|
version: 1
|
|
}
|
|
};
|
|
const TAG_TO_LEXICAL_NODE = {
|
|
p: {
|
|
type: 'paragraph'
|
|
},
|
|
h1: {
|
|
type: 'heading',
|
|
tag: 'h1'
|
|
},
|
|
h2: {
|
|
type: 'heading',
|
|
tag: 'h2'
|
|
},
|
|
h3: {
|
|
type: 'heading',
|
|
tag: 'h3'
|
|
},
|
|
h4: {
|
|
type: 'heading',
|
|
tag: 'h4'
|
|
},
|
|
h5: {
|
|
type: 'heading',
|
|
tag: 'h5'
|
|
},
|
|
h6: {
|
|
type: 'heading',
|
|
tag: 'h6'
|
|
},
|
|
blockquote: {
|
|
type: 'quote'
|
|
},
|
|
aside: {
|
|
type: 'aside'
|
|
},
|
|
a: {
|
|
type: 'link',
|
|
rel: null,
|
|
target: null,
|
|
title: null,
|
|
url: null
|
|
}
|
|
};
|
|
const ATOM_TO_LEXICAL_NODE = {
|
|
'soft-return': {
|
|
type: 'linebreak',
|
|
version: 1
|
|
}
|
|
};
|
|
const MARKUP_TO_FORMAT = {
|
|
strong: 1,
|
|
b: 1,
|
|
em: 1 << 1,
|
|
i: 1 << 1,
|
|
s: 1 << 2,
|
|
u: 1 << 3,
|
|
code: 1 << 4,
|
|
sub: 1 << 5,
|
|
sup: 1 << 6
|
|
};
|
|
const CARD_NAME_MAP = {
|
|
code: 'codeblock',
|
|
hr: 'horizontalrule'
|
|
};
|
|
const CARD_PROPERTY_MAP = {
|
|
embed: {
|
|
type: 'embedType'
|
|
}
|
|
};
|
|
const CARD_FIXES_MAP = {
|
|
callout: payload => {
|
|
if (payload.backgroundColor && !payload.backgroundColor.match(/^[a-zA-Z\d-]+$/)) {
|
|
payload.backgroundColor = 'white';
|
|
}
|
|
return payload;
|
|
}
|
|
};
|
|
function mobiledocToLexical(serializedMobiledoc) {
|
|
if (serializedMobiledoc === null || serializedMobiledoc === undefined || serializedMobiledoc === '') {
|
|
return JSON.stringify(BLANK_DOC);
|
|
}
|
|
const mobiledoc = JSON.parse(serializedMobiledoc);
|
|
if (!mobiledoc.sections) {
|
|
return JSON.stringify(BLANK_DOC);
|
|
}
|
|
const lexical = buildEmptyDoc();
|
|
mobiledoc.sections.forEach(child => addRootChild(child, mobiledoc, lexical));
|
|
return JSON.stringify(lexical);
|
|
}
|
|
|
|
/* internal functions ------------------------------------------------------- */
|
|
|
|
function buildEmptyDoc() {
|
|
return {
|
|
root: {
|
|
children: [],
|
|
direction: null,
|
|
format: '',
|
|
indent: 0,
|
|
type: 'root',
|
|
version: 1
|
|
}
|
|
};
|
|
}
|
|
function addRootChild(child, mobiledoc, lexical) {
|
|
const sectionTypeIdentifier = child[0];
|
|
if (sectionTypeIdentifier === 1) {
|
|
// Markup (text) section
|
|
const lexicalChild = convertMarkupSectionToLexical(child, mobiledoc);
|
|
lexical.root.children.push(lexicalChild);
|
|
|
|
// Set direction to ltr if there is any text
|
|
// Otherwise direction should be null
|
|
// Not sure if this is necessary:
|
|
// if we don't plan to support RTL, we could just set 'ltr' in all cases and ignore null
|
|
if (lexicalChild.children?.length > 0) {
|
|
lexical.root.direction = 'ltr';
|
|
}
|
|
} else if (sectionTypeIdentifier === 2) ; else if (sectionTypeIdentifier === 3) {
|
|
// List section
|
|
const lexicalChild = convertListSectionToLexical(child, mobiledoc);
|
|
lexical.root.children.push(lexicalChild);
|
|
lexical.root.direction = 'ltr'; // mobiledoc only supports LTR
|
|
} else if (sectionTypeIdentifier === 10) {
|
|
// Card section
|
|
const lexicalChild = convertCardSectionToLexical(child, mobiledoc);
|
|
lexical.root.children.push(lexicalChild);
|
|
}
|
|
}
|
|
function convertMarkupSectionToLexical(section, mobiledoc) {
|
|
const tagName = section[1]; // e.g. 'p'
|
|
const markers = section[2]; // e.g. [[0, [0], 0, "Hello world"]]
|
|
|
|
// Create an empty Lexical node from the tag name
|
|
// We will add nodes to the children array later
|
|
const lexicalNode = createEmptyLexicalNode(tagName);
|
|
populateLexicalNodeWithMarkers(lexicalNode, markers, mobiledoc);
|
|
return lexicalNode;
|
|
}
|
|
function populateLexicalNodeWithMarkers(lexicalNode, markers, mobiledoc) {
|
|
const markups = mobiledoc.markups;
|
|
const atoms = mobiledoc.atoms;
|
|
|
|
// Initiate some variables before looping over all the markers
|
|
let openMarkups = []; // tracks which markup tags are open for the current marker
|
|
let linkNode = undefined; // tracks current link node or undefined if no a tag is open
|
|
let href = undefined; // tracks the href for the current link node or undefined if no a tag is open
|
|
let rel = undefined; //tracks the rel attribute for the current link node or undefined if no a tag is open
|
|
let openLinkMarkup = false; // tracks whether the current node is a link node
|
|
|
|
// loop over markers and convert each one to lexical
|
|
for (let i = 0; i < markers.length; i++) {
|
|
// grab the attributes from the current marker
|
|
const [textTypeIdentifier, openMarkupsIndexes, numberOfClosedMarkups, value] = markers[i];
|
|
|
|
// Markers are either text (markup) or atoms
|
|
const markerType = textTypeIdentifier === 0 ? 'markup' : 'atom';
|
|
|
|
// If the current marker is an atom, convert the atom to Lexical and add to the node
|
|
if (markerType === 'atom') {
|
|
const atom = atoms[value];
|
|
const atomName = atom[0];
|
|
const childNode = ATOM_TO_LEXICAL_NODE[atomName];
|
|
embedChildNode(lexicalNode, childNode);
|
|
continue;
|
|
}
|
|
|
|
// calculate which markups are open for the current marker
|
|
openMarkupsIndexes.forEach(markupIndex => {
|
|
const markup = markups[markupIndex];
|
|
// Extract the href from the markup if it's a link
|
|
if (markup[0] === 'a') {
|
|
openLinkMarkup = true;
|
|
if (markup[1] && markup[1][0] === 'href') {
|
|
href = markup[1][1];
|
|
}
|
|
if (markup[1] && markup[1][2] === 'rel') {
|
|
rel = markup[1][3];
|
|
}
|
|
}
|
|
// Add the markup to the list of open markups
|
|
openMarkups.push(markup);
|
|
});
|
|
if (value !== undefined) {
|
|
// Convert the open markups to a bitmask compatible with Lexical
|
|
const format = convertMarkupTagsToLexicalFormatBitmask(openMarkups);
|
|
|
|
// If there is an open link tag, add the text to the link node
|
|
// Otherwise add the text to the parent node
|
|
if (openLinkMarkup) {
|
|
// link is open
|
|
// Create an empty link node if it doesn't exist already
|
|
linkNode = linkNode !== undefined ? linkNode : createEmptyLexicalNode('a', {
|
|
url: href,
|
|
rel: rel || null
|
|
});
|
|
|
|
// Create a text node and add it to the link node
|
|
const textNode = createTextNode(value, format);
|
|
embedChildNode(linkNode, textNode);
|
|
} else {
|
|
const textNode = createTextNode(value, format);
|
|
embedChildNode(lexicalNode, textNode);
|
|
}
|
|
}
|
|
|
|
// Close any markups that are closed after the current marker
|
|
// Remove any closed markups from openMarkups list
|
|
for (let j = 0; j < numberOfClosedMarkups; j++) {
|
|
// Remove the most recently opened markup from the list of open markups
|
|
const markup = openMarkups.pop();
|
|
|
|
// If we're closing a link tag, add the linkNode to the node
|
|
// Reset href and linkNode for the next markup
|
|
if (markup && markup[0] === 'a') {
|
|
embedChildNode(lexicalNode, linkNode);
|
|
openLinkMarkup = false;
|
|
href = undefined;
|
|
linkNode = undefined;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Creates a text node from the given text and format
|
|
function createTextNode(text, format) {
|
|
return {
|
|
detail: 0,
|
|
format: format,
|
|
mode: 'normal',
|
|
style: '',
|
|
text: text,
|
|
type: 'text',
|
|
version: 1
|
|
};
|
|
}
|
|
|
|
// Creates an empty Lexical node from the given tag name and additional attributes
|
|
function createEmptyLexicalNode(tagName, attributes = {}) {
|
|
const nodeParams = TAG_TO_LEXICAL_NODE[tagName];
|
|
const node = {
|
|
children: [],
|
|
direction: 'ltr',
|
|
format: '',
|
|
indent: 0,
|
|
...nodeParams,
|
|
...attributes,
|
|
version: 1
|
|
};
|
|
return node;
|
|
}
|
|
|
|
// Adds a child node to a parent node
|
|
function embedChildNode(parentNode, childNode) {
|
|
// If there is no child node, do nothing
|
|
if (!childNode) {
|
|
return;
|
|
}
|
|
// Add textNode to node's children
|
|
parentNode.children.push(childNode);
|
|
|
|
// If there is any text (e.g. not a blank text node), set the direction to ltr
|
|
if (childNode && 'text' in childNode && childNode.text) {
|
|
parentNode.direction = 'ltr';
|
|
}
|
|
}
|
|
|
|
// Lexical stores formats as a bitmask
|
|
// Mobiledoc stores formats as a list of open markup tags
|
|
// This function converts a list of open tags to a bitmask compatible with lexical
|
|
function convertMarkupTagsToLexicalFormatBitmask(tags) {
|
|
let format = 0;
|
|
tags.forEach(tag => {
|
|
if (tag in MARKUP_TO_FORMAT) {
|
|
format = format | MARKUP_TO_FORMAT[tag];
|
|
}
|
|
});
|
|
return format;
|
|
}
|
|
function convertListSectionToLexical(child, mobiledoc) {
|
|
const tag = child[1];
|
|
const listType = tag === 'ul' ? 'bullet' : 'number';
|
|
const listNode = createEmptyLexicalNode(tag, {
|
|
tag,
|
|
type: 'list',
|
|
listType,
|
|
start: 1,
|
|
direction: 'ltr'
|
|
});
|
|
child[2]?.forEach((listItem, i) => {
|
|
const listItemNode = createEmptyLexicalNode('li', {
|
|
type: 'listitem',
|
|
value: i + 1,
|
|
direction: 'ltr'
|
|
});
|
|
populateLexicalNodeWithMarkers(listItemNode, listItem, mobiledoc);
|
|
listNode.children.push(listItemNode);
|
|
});
|
|
return listNode;
|
|
}
|
|
function convertCardSectionToLexical(child, mobiledoc) {
|
|
let [cardName, payload] = mobiledoc.cards[child[1]];
|
|
|
|
// rename card if there's a difference between mobiledoc and lexical
|
|
cardName = CARD_NAME_MAP[cardName] || cardName;
|
|
|
|
// rename any properties to match lexical
|
|
if (CARD_PROPERTY_MAP[cardName]) {
|
|
const map = CARD_PROPERTY_MAP[cardName];
|
|
for (const [oldName, newName] of Object.entries(map)) {
|
|
payload[newName] = payload[oldName];
|
|
delete payload[oldName];
|
|
}
|
|
}
|
|
|
|
// run any payload fixes
|
|
if (CARD_FIXES_MAP[cardName]) {
|
|
payload = CARD_FIXES_MAP[cardName](payload);
|
|
}
|
|
delete payload.type;
|
|
const decoratorNode = {
|
|
type: cardName,
|
|
...payload
|
|
};
|
|
return decoratorNode;
|
|
}
|
|
|
|
export { lexicalToMobiledoc, mobiledocToLexical };
|
|
//# sourceMappingURL=kg-converters.js.map
|