usfm-editor
Version:
A WYSIWYG editor component for USFM
234 lines (185 loc) • 8.18 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.transformToSlate = transformToSlate;
exports.usfmToSlate = usfmToSlate;
require("core-js/modules/es.array.flat.js");
require("core-js/modules/es.array.unscopables.flat.js");
var usfmjs = _interopRequireWildcard(require("usfm-js"));
var _usfmjsStructureRules = require("./usfmjsStructureRules");
var _jsonTransforms = require("json-transforms");
var _slateHyperscript = require("slate-hyperscript");
var _NodeTypes = _interopRequireDefault(require("../utils/NodeTypes"));
var _basicSlateNodeFactory = require("./basicSlateNodeFactory");
var _UsfmMarkers = require("../utils/UsfmMarkers");
var _slate = require("slate");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
function isBook(e) {
return Array.isArray(e.chapters) && Array.isArray(e.headers) && e.chapters.every(isChapter);
}
function isChapter(e) {
return typeof e.chapterNumber === "string" && Array.isArray(e.verses);
}
function isVerse(e) {
return typeof e.verseNumber === "string" && Array.isArray(e.nodes);
}
function isTag(e) {
return typeof e.tag === "string";
}
function isHasText(e) {
return typeof e.text === "string";
}
function isHasContent(e) {
return typeof e.content === "string";
}
function isHasAttrib(e) {
return typeof e.attrib === "string";
}
function isHasChildren(e) {
return Array.isArray(e.children);
}
function usfmToSlate(usfm) {
const usfmJsDoc = usfmjs.toJSON(usfm);
console.log("parsed from usfm-js", usfmJsDoc);
const usfmAsArrays = (0, _jsonTransforms.transform)(usfmJsDoc, _usfmjsStructureRules.objectToArrayRules);
const processedAsArrays = (0, _jsonTransforms.transform)(usfmAsArrays, _usfmjsStructureRules.nextCharRules);
console.log("processedAsArrays", processedAsArrays);
const slateTree = transformToSlate(processedAsArrays);
console.log("slateTree", slateTree);
return Array.isArray(slateTree) ? slateTree : [slateTree];
}
function transformToSlate(el) {
if (isBook(el)) {
return book(el);
} else if (isChapter(el)) {
return [chapter(el)];
} else if (isVerse(el)) {
return [verse(el)];
} else if (isTag(el)) {
if (typeof el.tag === "string" && _UsfmMarkers.UsfmMarkers.isParagraphType(el.tag)) {
return paragraphElement(el);
} else {
// Character or Note marker
return getDescendantTextNodes(el);
}
} else if (isHasText(el)) {
return processText(String(el.text));
} else {
console.warn("Unrecognized node: ", el);
return [];
}
}
function book(book) {
const books = book.chapters.map(chapter);
const headers = (0, _slateHyperscript.jsx)("element", {
type: _NodeTypes.default.HEADERS
}, book.headers.map(transformToSlate));
const children = [headers, books].flat();
return (0, _slateHyperscript.jsx)("fragment", {}, children);
}
function chapterNumber(number) {
return (0, _slateHyperscript.jsx)("element", {
type: _UsfmMarkers.UsfmMarkers.CHAPTERS_AND_VERSES.c
}, [number]);
}
function chapter(chapter) {
const number = [chapterNumber(chapter.chapterNumber)];
const verses = chapter.verses.map(verse);
const children = number.concat(verses);
return (0, _slateHyperscript.jsx)("element", {
type: _NodeTypes.default.CHAPTER
}, children);
}
function verse(verse) {
let verseChildren = [(0, _basicSlateNodeFactory.verseNumber)(verse.verseNumber)];
let currentContainer = (0, _basicSlateNodeFactory.emptyInlineContainer)();
verseChildren = verseChildren.concat(currentContainer);
for (const node of verse.nodes) {
if (isTag(node) && _UsfmMarkers.UsfmMarkers.isParagraphType(node.tag)) {
currentContainer = paragraphElement(node);
verseChildren = verseChildren.concat(currentContainer);
} else {
currentContainer.children = currentContainer.children.concat(transformToSlate(node));
}
currentContainer = removeFirstEmptyText(currentContainer);
}
return (0, _basicSlateNodeFactory.verseWithChildren)(verseChildren);
}
function paragraphElement(tagNode) {
return textElement(tagNode);
}
function textElement(tagNode) {
const textNodes = getDescendantTextNodes(tagNode);
return (0, _slateHyperscript.jsx)("element", {
type: tagNode.tag
}, textNodes);
}
function removeFirstEmptyText(node) {
if (node.children.length > 1 && _slate.Text.isText(node.children[0]) && node.children[0].text == "") {
node.children = node.children.slice(1);
}
return node;
}
/**
* Returns a flat list of descendant text nodes and sets the appopriate marks
* on the text nodes
*/
function getDescendantTextNodes(tagNode) {
let textNodes = [{
text: ""
}];
if (isHasText(tagNode)) {
textNodes = textNodes.concat(processText(tagNode.text));
} else if (isHasContent(tagNode)) {
textNodes = textNodes.concat(processText(tagNode.content));
}
if (isHasAttrib(tagNode)) {
textNodes = textNodes.concat(processText(tagNode.attrib));
} // \w marker will not have an 'attrib' field when parsed by usfm-js.
if (isTag(tagNode) && tagNode.tag == _UsfmMarkers.UsfmMarkers.SPECIAL_FEATURES.w) {
textNodes = textNodes.concat(processText(get_w_AttributeText(tagNode)));
}
if (isHasChildren(tagNode)) {
// The children will either be additional tag nodes or simple texts, which
// will all reduce to a list of text nodes
const childText = tagNode.children.map(getDescendantTextNodes);
textNodes = textNodes.concat(...childText);
} //textNodes = textNodes.flat() // don't think this is needed, but leaving a comment in case wrong
// If this node is not a paragraph type (thus it is a character, note, or milestone type),
// we will apply the marker as a mark to every descendant text node.
if (isTag(tagNode) && !_UsfmMarkers.UsfmMarkers.isParagraphType(tagNode.tag)) {
textNodes.forEach(text => {
const destructured = _UsfmMarkers.UsfmMarkers.destructureMarker(tagNode.tag);
const bareMarker = destructured === null || destructured === void 0 ? void 0 : destructured.markerWithoutLeadingPlus;
if (bareMarker) text[bareMarker] = true;
});
}
return textNodes;
}
/**
* Usfm-js parses the \w marker differently than the other markers with attributes.
* This function returns a string that starts with a pipe character and contains
* the attributes for the \w marker. Unfortunately, the original order of the attributes
* may not be preserved since usfm-js does not tell us what the original order was.
*/
function get_w_AttributeText(tagNode) {
let text = "";
const attributes = ["lemma", "strong", "srcloc"];
attributes.forEach(value => {
if (tagNode.hasOwnProperty(value)) {
text = text === "" ? "|" : text + " ";
text += `${value}=\"${tagNode[value]}\"`;
}
});
return text;
}
function processText(text) {
return {
text: text // Slate does not accept the pipe character so we have a workaround for this
.replace(/\|/g, "&pipe;") // Remove newlines
.replace(/[\r|\n|\r\n]/g, "")
};
}