@bookbox/core
Version:
Bookbox — e-book format
229 lines (228 loc) • 6.75 kB
JavaScript
import { getTextTokens } from "../utils";
import { defaultBlockNames } from "./elements";
const onlyBlocks = new Set(["image"]);
export function getElementView(item) {
if (typeof item === "string") {
return "inline";
}
const { props: userProps, name } = item;
const props = Object.assign({ block: defaultBlockNames.has(name) }, userProps);
if (props.inline) {
return "inline";
}
if (props.block) {
return "block";
}
return props.position === "inline" ? "inline" : "block";
}
export const getTextLinkedItem = (text) => ({
bookElementSchema: {
name: "text",
props: {},
children: [],
},
raw: text,
previous: null,
next: null,
previousLeaf: null,
nextLeaf: null,
parent: null,
firstChild: null,
lastChild: null,
view: "inline",
});
export const getPageLinkedItem = (count) => {
const text = `${count}`;
const textItem = getTextLinkedItem(text);
return {
bookElementSchema: {
name: "page",
props: { count, key: `page-${count}` },
children: [],
},
raw: text,
previous: null,
next: null,
previousLeaf: null,
nextLeaf: null,
parent: null,
firstChild: textItem,
lastChild: textItem,
view: "inline",
};
};
function bindList(items) {
for (let i = 1; i < items.length; i++) {
const prev = items[i - 1];
const current = items[i];
current.previous = prev;
prev.next = current;
}
}
function bindLeafList(items) {
for (let i = 1; i < items.length; i++) {
const prev = items[i - 1];
const current = items[i];
current.previousLeaf = prev;
prev.nextLeaf = current;
}
}
function bindParent(items, parent) {
var _a, _b;
for (const childItem of items) {
childItem.parent = parent;
}
parent.firstChild = (_a = items[0]) !== null && _a !== void 0 ? _a : null;
parent.lastChild = (_b = items[items.length - 1]) !== null && _b !== void 0 ? _b : null;
}
const isNotEmpty = (text) => text !== "";
export function getBookLinkedItem(item) {
if (typeof item === "string") {
const textItem = getTextLinkedItem(item);
const children = getTextTokens(item)
.filter(isNotEmpty)
.map(getTextLinkedItem);
bindList(children);
bindParent(children, textItem);
return textItem;
}
const linkedItem = {
bookElementSchema: item,
firstChild: null,
lastChild: null,
raw: null,
previous: null,
next: null,
previousLeaf: null,
nextLeaf: null,
parent: null,
view: getElementView(item),
};
const children = getBookLinkedSchema(item.children).tree;
bindList(children);
bindParent(children, linkedItem);
return linkedItem;
}
function getLinkedItemList(start, end) {
if (!start) {
return [];
}
const result = [];
let current = start;
while (current && current !== end) {
result.push(current);
current = current.next;
}
if (end) {
result.push(end);
}
return result;
}
function getLeafListLinkedItem(linkedItems) {
const list = [];
for (const item of linkedItems) {
if (item.firstChild === null) {
list.push(item);
}
else {
list.push(...getLeafListLinkedItem(getLinkedItemList(item.firstChild, item.lastChild)));
}
}
return list;
}
export function getBookLinkedSchema(schema, root) {
const linkedSchema = {
start: null,
end: null,
tree: schema.filter(isNotEmpty).map(getBookLinkedItem),
};
bindList(linkedSchema.tree);
if (schema.length === 0 || !root) {
return linkedSchema;
}
const leafList = getLeafListLinkedItem(linkedSchema.tree);
bindLeafList(leafList);
putPages(leafList);
linkedSchema.start = leafList[0];
linkedSchema.end = leafList[leafList.length - 1];
linkedSchema.tree = getLinkedItemList(linkedSchema.tree[0], null);
return linkedSchema;
}
export function getLinkedLeafList(linkedSchema) {
const list = [];
let current = linkedSchema.start;
while (current !== null) {
list.push(current);
current = current.nextLeaf;
}
return list;
}
export function getSchemaFromLinkedList(linkedList) {
const schema = [];
for (const item of linkedList) {
const { bookElementSchema, raw, firstChild, lastChild } = item;
const { name, props } = bookElementSchema;
if (name === "text" && raw && firstChild === null) {
schema.push(raw);
}
else if (name === "text") {
schema.push(...getSchemaFromLinkedList(getLinkedItemList(firstChild, lastChild)));
}
else {
schema.push({
name: name,
props: props,
children: getSchemaFromLinkedList(getLinkedItemList(firstChild, lastChild)),
});
}
}
return schema;
}
function putAfterItem(sourceItem, targetItem) {
const { next, nextLeaf, parent } = sourceItem;
targetItem.next = next;
if (next) {
next.previous = targetItem;
}
targetItem.nextLeaf = nextLeaf;
if (nextLeaf) {
nextLeaf.previousLeaf = targetItem;
}
sourceItem.next = targetItem;
targetItem.previous = sourceItem;
sourceItem.nextLeaf = targetItem;
targetItem.previousLeaf = sourceItem;
targetItem.parent = parent;
if (parent && parent.lastChild === sourceItem) {
parent.lastChild = targetItem;
}
}
const PAGE_SIZE = 500;
const NEW_LINE_SIZE = 10;
const NEW_LINE_LONG_SIZE = 50;
function putPages(items) {
var _a;
if (items.length === 0) {
return;
}
let pageCount = 0;
let currentBlockCount = PAGE_SIZE;
for (const item of items) {
const { raw, view } = item;
const isBlock = view === "block";
let increment = 0;
if (!isBlock && raw) {
const newLines = (_a = raw.match(/\n\n*/g)) !== null && _a !== void 0 ? _a : [];
const newLineCount = newLines.reduce((s, e) => s + e.length, 0);
const newLineSize = newLines.reduce((s, e) => s + (e.length > 1 ? NEW_LINE_LONG_SIZE : NEW_LINE_SIZE), 0);
const rawSize = raw.length - newLineCount;
increment = rawSize + newLineSize;
}
currentBlockCount += increment;
if (isBlock || currentBlockCount >= PAGE_SIZE) {
currentBlockCount = 0;
const pageItem = getPageLinkedItem(pageCount++);
putAfterItem(item, pageItem);
}
}
}