@mirrormedia/lilith-draft-editor
Version:
## Installation
284 lines (223 loc) • 9.32 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.convertToHtml = convertToHtml;
var _lodash = _interopRequireDefault(require("lodash"));
var _const = require("../const");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
// Modified from https://github.com/dburrows/draft-js-basic-html-editor/blob/master/src/utils/processInlineStylesAndEntities.js
/**
* @typedef {import('./jsdoc').RawDraftContentBlock} RawDraftContentBlock
* @typedef {import('./jsdoc').EntityMap} EntityMap
* @typedef {import('./jsdoc').RawDraftEntityRange} RawDraftEntityRange
* @typedef {import('./jsdoc').DraftInlineStyleType} DraftInlineStyleType
* @typedef {import('./jsdoc').RawDraftInlineStyleRange} RawDraftInlineStyleRange
* @typedef {import('./jsdoc').EntityTagMap} EntityTagMap
* @typedef {import('./jsdoc').InlineTagMap} InlineTagMap
* @typedef {Record<string, any>} TagInsertMap
*/
/**
* @param {DraftInlineStyleType} style
*/
function tagForCustomInlineStyle(style) {
const customInlineStylePrefixs = [_const.CUSTOM_STYLE_PREFIX_FONT_COLOR, _const.CUSTOM_STYLE_PREFIX_BACKGROUND_COLOR];
const stylePrefix = customInlineStylePrefixs.find(prefix => style.startsWith(prefix));
/** @type {string[]} */
let tag, value;
switch (stylePrefix) {
case _const.CUSTOM_STYLE_PREFIX_FONT_COLOR:
value = style.split(_const.CUSTOM_STYLE_PREFIX_FONT_COLOR)[1];
tag = [`<span style="color: ${value}">`, '</span>'];
break;
case _const.CUSTOM_STYLE_PREFIX_BACKGROUND_COLOR:
value = style.split(_const.CUSTOM_STYLE_PREFIX_BACKGROUND_COLOR)[1];
tag = [`<span style="background-color: ${value}">`, '</span>'];
break;
default:
tag = [];
break;
}
return tag;
}
/**
* @param {RawDraftContentBlock} block
*/
function _fullfilIntersection(block) {
// SORT BEFORE PROCESSING
const sortedISRanges = _lodash.default.sortBy(block.inlineStyleRanges, 'offset');
const sortedEntityRanges = _lodash.default.sortBy(block.entityRanges, 'offset');
const splitedISInline = [];
for (let i = 0; i < sortedEntityRanges.length; i++) {
const entityRange = sortedEntityRanges[i];
for (let j = 0; j < sortedISRanges.length; j++) {
const entityOffset = _lodash.default.get(entityRange, 'offset', 0);
const entityLength = _lodash.default.get(entityRange, 'length', 0);
const inlineLength = _lodash.default.get(sortedISRanges, [j, 'length'], 0);
const inlineOffset = _lodash.default.get(sortedISRanges, [j, 'offset'], 0);
const inlineStyle =
/** @type {DraftInlineStyleType}*/
_lodash.default.get(sortedISRanges, [j, 'style'], '');
const nextEntityOffset = _lodash.default.get(sortedEntityRanges, [i + 1, 'offset'], 0);
const nextEntityLength = _lodash.default.get(sortedEntityRanges, [i + 1, 'length'], 0); // handle intersections of inline style and entity
// <a></a> is entity
// <abbr></abbr> is next entity
// <strong></strong> is inline style
if (nextEntityOffset >= inlineOffset && nextEntityOffset < inlineOffset + inlineLength && nextEntityOffset + nextEntityLength > inlineOffset + inlineLength && // <a><strong></a></strong>
entityOffset < inlineOffset && entityOffset + entityLength > inlineOffset && entityOffset + entityLength <= inlineOffset + inlineLength) {
// <strong><abbr></strong></abbr>
// situation: <a><strong></a><abbr></strong></abbr>
// should be: <a><strong></strong></a><strong></strong><abbr><strong></strong></abbr>
// skip next entity checking
i = i + 1;
splitedISInline.push({
index: j,
replace: [{
length: entityOffset + entityLength - inlineOffset,
offset: inlineOffset,
style: inlineStyle
}, {
length: nextEntityOffset - (entityOffset + entityLength),
offset: entityOffset + entityLength,
style: inlineStyle
}, {
length: inlineOffset + inlineLength - nextEntityOffset,
offset: nextEntityOffset,
style: inlineStyle
}]
});
} else if (entityOffset >= inlineOffset && entityOffset < inlineOffset + inlineLength && entityOffset + entityLength > inlineOffset + inlineLength) {
// situation: <strong><a></strong></a>
// should be: <strong></strong><a><strong></strong></a>
splitedISInline.push({
index: j,
replace: [{
length: entityOffset - inlineOffset,
offset: inlineOffset,
style: inlineStyle
}, {
length: inlineOffset + inlineLength - entityOffset,
offset: entityOffset,
style: inlineStyle
}]
});
} else if (entityOffset < inlineOffset && entityOffset + entityLength > inlineOffset && entityOffset + entityLength <= inlineOffset + inlineLength) {
// situation: <a><strong></a></strong>
// should be: <a><strong></strong></a><strong></strong>
splitedISInline.push({
index: j,
replace: [{
length: entityOffset + entityLength - inlineOffset,
offset: inlineOffset,
style: inlineStyle
}, {
length: inlineOffset + inlineLength - entityOffset - entityLength,
offset: entityOffset + entityLength,
style: inlineStyle
}]
});
}
}
}
_lodash.default.forEachRight(splitedISInline, ele => {
sortedISRanges.splice(ele.index, 1, ...ele.replace);
});
return sortedISRanges;
}
/**
* @param {InlineTagMap} inlineTagMap
* @param {RawDraftInlineStyleRange[]} inlineStyleRanges
* @param {TagInsertMap} [tagInsertMap]
*/
function _inlineTag(inlineTagMap, inlineStyleRanges, tagInsertMap = {}) {
// SORT BEFORE PROCESSING
const sortedRanges = _lodash.default.sortBy(inlineStyleRanges, 'offset'); // map all the tag insertions we're going to do
sortedRanges.forEach(function (range) {
let tag = inlineTagMap[range.style]; // handle dynamic inline style
if (!tag) {
tag = tagForCustomInlineStyle(range.style);
}
if (!tagInsertMap[range.offset]) {
tagInsertMap[range.offset] = [];
} // add starting tag to the end of the array to form the tag nesting
tagInsertMap[range.offset].push(tag[0]);
if (tag[1]) {
if (!tagInsertMap[range.offset + range.length]) {
tagInsertMap[range.offset + range.length] = [];
} // add closing tags to start of array, otherwise tag nesting will be invalid
tagInsertMap[range.offset + range.length].unshift(tag[1]);
}
});
return tagInsertMap;
}
/**
* @param {EntityTagMap} entityTagMap
* @param {EntityMap} entityMap
* @param {RawDraftEntityRange[]} entityRanges
* @param {TagInsertMap} tagInsertMap
*/
function _entityTag(entityTagMap, entityMap, entityRanges, tagInsertMap = {}) {
_lodash.default.forEach(entityRanges, range => {
const entity = entityMap[range.key];
const type = entity.type && entity.type.toUpperCase();
const tag = entityTagMap[type];
const data = entity.data;
const compiledTag0 = _lodash.default.template(tag[0], {
variable: 'data'
})(data);
const compiledTag1 = _lodash.default.template(tag[1], {
variable: 'data'
})(data);
if (!tagInsertMap[range.offset]) {
tagInsertMap[range.offset] = [];
} // add starting tag
tagInsertMap[range.offset].push(compiledTag0);
if (tag[1]) {
if (!tagInsertMap[range.offset + range.length]) {
tagInsertMap[range.offset + range.length] = [];
} // add closing tags to start of array, otherwise tag nesting will be invalid
tagInsertMap[range.offset + range.length].unshift(compiledTag1);
}
});
return tagInsertMap;
}
/**
* @param {InlineTagMap} inlineTagMap
* @param {EntityTagMap} entityTagMap
* @param {EntityMap} entityMap
* @param {RawDraftContentBlock} block
*/
function convertToHtml(inlineTagMap, entityTagMap, entityMap, block) {
// exit if there is no inlineStyleRanges/entityRanges or length === 0 as well
if (!block.inlineStyleRanges && !block.entityRanges || block.inlineStyleRanges.length === 0 && block.entityRanges.length === 0) {
return block.text;
}
let html = block.text;
const inlineStyleRanges = _fullfilIntersection(block);
/** @type {TagInsertMap} */
let tagInsertMap = {};
tagInsertMap = _entityTag(entityTagMap, entityMap, block.entityRanges, tagInsertMap);
tagInsertMap = _inlineTag(inlineTagMap, inlineStyleRanges, tagInsertMap); // sort on position, as we'll need to keep track of offset
const orderedKeys = Object.keys(tagInsertMap).sort(function (a, b) {
const x = Number(a);
const y = Number(b);
if (x > y) {
return 1;
}
if (x < y) {
return -1;
}
return 0;
}); // insert tags into string, keep track of offset caused by our text insertions
let offset = 0;
orderedKeys.forEach(function (pos) {
const index = Number(pos);
tagInsertMap[pos].forEach(function (
/** @type {any} */
tag) {
html = html.substr(0, offset + index) + tag + html.substr(offset + index);
offset += tag.length;
});
});
return html;
}