UNPKG

@contentstack/utils

Version:
1,065 lines (1,037 loc) 73.7 kB
'use strict'; function replaceHtmlEntities(text) { return text .replace(/&/g, '&amp;') .replace(/</g, '&lt;') .replace(/>/g, '&gt;') .replace(/"/g, '&quot;'); } var forbiddenAttrChars = ['"', "'", '>', '<', '/', '=']; function createMetadata(attribute) { return { text: attribute['#text'], itemUid: attribute['data-sys-entry-uid'] || attribute['data-sys-asset-uid'], itemType: attribute.type, styleType: attribute['sys-style-type'], attributes: attribute, contentTypeUid: attribute['data-sys-content-type-uid'], }; } function nodeToMetadata(attribute, textNode) { return { text: textNode.text, itemUid: attribute['entry-uid'] || attribute['asset-uid'], itemType: attribute.type, styleType: attribute['display-type'], attributes: attribute, contentTypeUid: attribute['content-type-uid'], }; } /** * Serializes an Attributes object to a string of HTML attribute key-value pairs * (e.g. ` key1="value1" key2="value2"`). Keys containing forbidden characters * are skipped. Values are HTML-entity-encoded. Arrays are joined with `, `; * nested objects are serialized as `key:value;` pairs. * * @param attributes - The attributes object to serialize (e.g. from a node or metadata). * @returns A string starting with a space, followed by `key="value"` pairs suitable for inclusion in an HTML tag. */ function attributeToString(attributes) { var result = ''; var _loop_1 = function (key) { if (Object.prototype.hasOwnProperty.call(attributes, key)) { if (forbiddenAttrChars.some(function (char) { return key.includes(char); })) { return "continue"; } var value = attributes[key]; if (Array.isArray(value)) { value = value.join(', '); } else if (typeof value === 'object') { var elementString = ''; for (var subKey in value) { if (Object.prototype.hasOwnProperty.call(value, subKey)) { var subValue = value[subKey]; if (subValue != null && subValue !== '') { elementString += "".concat(subKey, ":").concat(subValue, "; "); } } } value = elementString; } result += " ".concat(key, "=\"").concat(replaceHtmlEntities(String(value)), "\""); } }; for (var key in attributes) { _loop_1(key); } return result; } var StyleType; (function (StyleType) { StyleType["BLOCK"] = "block"; StyleType["INLINE"] = "inline"; StyleType["LINK"] = "link"; StyleType["DISPLAY"] = "display"; StyleType["DOWNLOAD"] = "download"; })(StyleType || (StyleType = {})); var StyleType$1 = StyleType; /****************************************************************************** Copyright (c) Microsoft Corporation. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ***************************************************************************** */ /* global Reflect, Promise, SuppressedError, Symbol, Iterator */ var extendStatics = function(d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; function __extends(d, b) { if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); } var __assign = function() { __assign = Object.assign || function __assign(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; function __spreadArray(to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); } typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) { var e = new Error(message); return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; }; function elementToJson(element) { var obj = {}; for (var i = 0; i < element.attributes.length; i++) { obj[element.attributes.item(i).name] = element.attributes.item(i).value; } element.childNodes.forEach(function (chileNode) { var node = (chileNode); obj = __assign(__assign({}, obj), parseElement(node)); }); return obj; } function parseElement(node) { var obj = {}; if (node.nodeType === 3) { obj['#text'] = node.textContent; } else if (node.nodeType === 1) { obj[node.nodeName.toLowerCase()] = elementToJson(node); } return obj; } var frameflag = 'documentfragmentcontainer'; String.prototype.forEachEmbeddedItem = function (callbackfn) { var str = "<".concat(frameflag, ">").concat(this.toString(), "</").concat(frameflag, ">"); var root = (new DOMParser()).parseFromString(str, 'text/html'); var embeddedEntries = root.querySelectorAll(".embedded-entry"); embeddedEntries.forEach(function (element) { callbackfn(element.outerHTML, createMetadata(elementToJson(element))); }); var embeddedAsset = root.querySelectorAll(".embedded-asset"); embeddedAsset.forEach(function (element) { callbackfn(element.outerHTML, createMetadata(elementToJson(element))); }); }; function sanitizeHTML(input, allowedTags, allowedAttributes) { if (allowedTags === void 0) { allowedTags = ['p', 'a', 'strong', 'em', 'ul', 'ol', 'li', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'sub', 'u', 'table', 'thead', 'tbody', 'tr', 'th', 'td', 'span', 'fragment', 'sup', 'strike', 'br', 'img', 'colgroup', 'col', 'div']; } if (allowedAttributes === void 0) { allowedAttributes = ['href', 'title', 'target', 'alt', 'src', 'class', 'id', 'style', 'colspan', 'rowspan', 'content-type-uid', 'data-sys-asset-uid', 'sys-style-type', 'data-type', 'data-width', 'data-rows', 'data-cols', 'data-mtec']; } // Replace newline characters with <br /> before processing the HTML tags input = input.replace(/\n/g, '<br />'); // Regular expression to find and remove all HTML tags except the allowed ones var sanitized = input.replace(/<\/?([a-z][a-z0-9]*)\b[^<>]*>/gi, function (match, tag) { return allowedTags.includes(tag.toLowerCase()) ? match : ''; }); // Regular expression to remove all attributes except the allowed ones var cleaned = sanitized.replace(/<([a-z][a-z0-9]*)\b[^<>]*>/gi, function (match, tag) { if (!allowedTags.includes(tag.toLowerCase())) { return match; // Ignore tags not in allowedTags } // For each tag that is allowed, clean its attributes return match.replace(/\s([a-z\-]+)=['"][^'"]*['"]/gi, function (attributeMatch, attribute) { return allowedAttributes.includes(attribute.toLowerCase()) ? attributeMatch : ''; }); }); return cleaned; } var _a$1; var defaultOptions = (_a$1 = {}, _a$1[StyleType$1.BLOCK] = function (item) { var title = sanitizeHTML(item.title || item.uid); var content_type_uid = sanitizeHTML(item._content_type_uid || (item.system ? item.system.content_type_uid : '')); return "<div><p>".concat(title, "</p><p>Content type: <span>").concat(content_type_uid, "</span></p></div>"); }, _a$1[StyleType$1.INLINE] = function (item) { var title = sanitizeHTML(item.title || item.uid); return "<span>".concat(title, "</span>"); }, _a$1[StyleType$1.LINK] = function (item, metadata) { var url = item.url ? encodeURI(sanitizeHTML(item.url)) : null; var text = sanitizeHTML(metadata.text || item.title || item.uid || (item.system ? item.system.uid : '')); var hrefAttr = url ? " href=\"".concat(url, "\"") : ''; return "<a".concat(hrefAttr, ">").concat(text, "</a>"); }, _a$1[StyleType$1.DISPLAY] = function (item, metadata) { var url = item.url ? encodeURI(sanitizeHTML(item.url)) : null; var alt = sanitizeHTML(metadata.attributes.alt || item.title || item.filename || item.uid || (item.system ? item.system.uid : '')); var srcAttr = url ? " src=\"".concat(url, "\"") : ''; return "<img".concat(srcAttr, " alt=\"").concat(alt, "\" />"); }, _a$1[StyleType$1.DOWNLOAD] = function (item, metadata) { var href = item.url ? encodeURI(sanitizeHTML(item.url)) : null; var text = sanitizeHTML(metadata.text || item.title || item.uid || (item.system ? item.system.content_type_uid : '')); var hrefAttr = href ? " href=\"".concat(href, "\"") : ''; return "<a".concat(hrefAttr, ">").concat(text, "</a>"); }, _a$1); // This function will find Embedded object present in string function findEmbeddedEntry(uid, contentTypeUid, embeddeditems) { if (embeddeditems === void 0) { embeddeditems = []; } return embeddeditems.filter(function (entry) { if (!entry) return false; return ((entry.uid && entry.uid === uid && entry._content_type_uid === contentTypeUid) || (entry.system && entry.system.uid === uid && entry.system.content_type_uid === contentTypeUid)); }); } function findEmbeddedAsset(uid, embeddedAssets) { if (embeddedAssets === void 0) { embeddedAssets = []; } return embeddedAssets.filter(function (asset) { if (!asset) return false; return ((asset.uid && asset.uid === uid) || (asset.system && asset.system.uid === uid)); }); } function findGQLEmbeddedItems(metadata, items) { if (!metadata || !items) return []; if (metadata.itemType === 'entry') { return findEmbeddedEntry(metadata.itemUid, metadata.contentTypeUid, items); } else { return findEmbeddedAsset(metadata.itemUid, items); } } function findEmbeddedItems(object, entry) { if (object && object !== undefined && entry && entry !== undefined) { if (entry._embedded_items !== undefined) { var entryEmbedable = entry; var items = Object.values(entryEmbedable._embedded_items || []).reduce(function (accumulator, value) { return accumulator.concat(value); }, []); return findGQLEmbeddedItems(object, items); } } return []; } function findRenderString(item, metadata, renderOptions) { if ((!item && item === undefined) || (!metadata && metadata === undefined)) { return ''; } if (renderOptions && renderOptions[metadata.styleType] !== undefined) { var renderFunction = renderOptions[metadata.styleType]; if (metadata.attributes['data-sys-content-type-uid'] !== undefined && typeof renderFunction !== 'function' && renderFunction[metadata.attributes['data-sys-content-type-uid']] !== undefined) { return renderFunction[metadata.attributes['data-sys-content-type-uid']](item, metadata); } else if (metadata.attributes['data-sys-content-type-uid'] !== undefined && typeof renderFunction !== 'function' && renderFunction.$default !== undefined) { return renderFunction.$default(item, metadata); } else if (metadata.contentTypeUid !== undefined && typeof renderFunction !== 'function' && renderFunction[metadata.contentTypeUid] !== undefined) { return renderFunction[metadata.contentTypeUid](item, metadata); } else if (metadata.contentTypeUid !== undefined && typeof renderFunction !== 'function' && renderFunction.$default !== undefined) { return renderFunction.$default(item, metadata); } else if (typeof renderFunction === 'function') { return renderFunction(item, metadata); } } var defaultRenderFunction = defaultOptions[metadata.styleType]; return defaultRenderFunction(item, metadata); } function findRenderContent(keyPaths, entry, render) { getContent(keyPaths.split("."), entry, render); } function getContent(keys, object, render) { if (keys) { var key = keys[0]; if (keys.length === 1 && object[key]) { object[key] = render(object[key]); } else if (keys.length > 0) { if (object[key]) { var newKeys = keys.slice(1); if (Array.isArray(object[key])) { // tslint:disable-next-line: prefer-for-of for (var _i = 0, _a = object[key]; _i < _a.length; _i++) { var objKey = _a[_i]; getContent(newKeys, objKey, render); } } else if (typeof object[key] === 'object') { getContent(newKeys, object[key], render); } } } } } /** * Renders RTE (Rich Text Editor) content with embedded objects in-place. * Mutates the entry/entries by replacing embedded item tags with HTML produced * by the provided render options. Works with a single entry or an array of entries. * * @param option - Configuration for rendering. * @param option.entry - Entry or array of entries containing RTE fields with embedded objects. * @param option.renderOption - Optional render options (node/item handlers) to produce HTML for embedded content. * @param option.paths - Optional key paths to specific RTE fields. If omitted, all RTE paths on the entry are rendered. */ function render(option) { function findContent(path, entry) { findRenderContent(path, entry, function (content) { return renderContent(content, { entry: entry, renderOption: option.renderOption }); }); } function findAndRender(entry) { if (!option.paths || option.paths.length === 0) { Object.keys(__assign({}, entry._embedded_items)).forEach(function (path) { findContent(path, entry); }); } else { option.paths.forEach(function (path) { findContent(path, entry); }); } } if (option.entry instanceof Array) { option.entry.forEach(function (entry) { findAndRender(entry); }); } else { findAndRender(option.entry); } } /** * Renders a single RTE content string or array of strings by replacing embedded * item tags with HTML. Uses the entry and renderOption from the given option to * resolve embedded references and produce output. * * @param content - RTE content string or array of strings containing embedded item tags. * @param option - Must include the entry (for resolving embedded items) and optionally renderOption. * @returns The same shape as content: a string or array of strings with embedded tags replaced by rendered HTML. */ function renderContent(content, option) { // return blank if content not present if (!content || content === undefined) { return ''; } // render content of type string if (typeof content === 'string') { var contentToReplace_1 = content; content.forEachEmbeddedItem(function (embededObjectTag, object) { contentToReplace_1 = findAndReplaceEmbeddedItem(contentToReplace_1, embededObjectTag, object, option); }); return contentToReplace_1; } // render content of type array of string var resultContent = []; content.forEach(function (element) { resultContent.push(renderContent(element, option)); }); return resultContent; } function findAndReplaceEmbeddedItem(content, embededObjectTag, metadata, option) { var embeddedObjects = findEmbeddedItems(metadata, option.entry); var renderString = findRenderString(embeddedObjects[0], metadata, option.renderOption); return content.replace(embededObjectTag, renderString); } var NodeType; (function (NodeType) { NodeType["DOCUMENT"] = "doc"; NodeType["PARAGRAPH"] = "p"; NodeType["LINK"] = "a"; NodeType["IMAGE"] = "img"; NodeType["EMBED"] = "embed"; NodeType["HEADING_1"] = "h1"; NodeType["HEADING_2"] = "h2"; NodeType["HEADING_3"] = "h3"; NodeType["HEADING_4"] = "h4"; NodeType["HEADING_5"] = "h5"; NodeType["HEADING_6"] = "h6"; NodeType["ORDER_LIST"] = "ol"; NodeType["UNORDER_LIST"] = "ul"; NodeType["LIST_ITEM"] = "li"; NodeType["FRAGMENT"] = "fragment"; NodeType["HR"] = "hr"; NodeType["TABLE"] = "table"; NodeType["TABLE_HEADER"] = "thead"; NodeType["TABLE_BODY"] = "tbody"; NodeType["TABLE_FOOTER"] = "tfoot"; NodeType["TABLE_ROW"] = "tr"; NodeType["TABLE_HEAD"] = "th"; NodeType["TABLE_DATA"] = "td"; NodeType["COL_GROUP"] = "colgroup"; NodeType["COL"] = "col"; NodeType["BLOCK_QUOTE"] = "blockquote"; NodeType["CODE"] = "code"; NodeType["TEXT"] = "text"; NodeType["REFERENCE"] = "reference"; })(NodeType || (NodeType = {})); var NodeType$1 = NodeType; var MarkType; (function (MarkType) { MarkType["BOLD"] = "bold"; MarkType["ITALIC"] = "italic"; MarkType["UNDERLINE"] = "underline"; MarkType["CLASSNAME_OR_ID"] = "classnameOrId"; MarkType["STRIKE_THROUGH"] = "strikethrough"; MarkType["INLINE_CODE"] = "inlineCode"; MarkType["SUBSCRIPT"] = "subscript"; MarkType["SUPERSCRIPT"] = "superscript"; MarkType["BREAK"] = "break"; })(MarkType || (MarkType = {})); var MarkType$1 = MarkType; var Node = /** @class */ (function () { function Node() { } return Node; }()); var Document = /** @class */ (function (_super) { __extends(Document, _super); function Document() { var _this = _super.call(this) || this; _this.type = NodeType$1.DOCUMENT; return _this; } return Document; }(Node)); var TextNode = /** @class */ (function (_super) { __extends(TextNode, _super); function TextNode(text) { var _this = _super.call(this) || this; _this.text = text; return _this; } return TextNode; }(Node)); var _a; /** * Safely gets an attribute value from node.attrs */ function getAttr(node, key) { var _a; return (_a = node.attrs) === null || _a === void 0 ? void 0 : _a[key]; } /** * Safely gets a string attribute value from node.attrs */ function getAttrString(node, key) { var _a; var value = (_a = node.attrs) === null || _a === void 0 ? void 0 : _a[key]; return typeof value === 'string' ? value : undefined; } /** * Builds common HTML attributes string (style, class-name, id) */ function buildCommonAttrs(node) { if (!node.attrs) return ''; var attrs = []; if (node.attrs.style) { attrs.push(" style=\"".concat(node.attrs.style, "\"")); } if (node.attrs['class-name']) { attrs.push(" class=\"".concat(node.attrs['class-name'], "\"")); } if (node.attrs.id) { attrs.push(" id=\"".concat(node.attrs.id, "\"")); } return attrs.join(''); } /** * JSON RTE exports nested lists as siblings of the preceding <li> (not children). * This folds any ol/ul that immediately follows an li into that li's children * so the rendered HTML is valid (nested list inside the li). */ function foldNestedListSiblingsIntoPreviousLi(children) { var result = []; for (var i = 0; i < children.length; i++) { var node = children[i]; var isList = node && (node.type === NodeType$1.ORDER_LIST || node.type === NodeType$1.UNORDER_LIST); var last = result[result.length - 1]; var lastIsLi = last && last.type === NodeType$1.LIST_ITEM; if (isList && lastIsLi && last) { result[result.length - 1] = __assign(__assign({}, last), { children: __spreadArray(__spreadArray([], (last.children || []), true), [node], false) }); } else { result.push(children[i]); } } return result; } var defaultNodeOption = (_a = {}, _a[NodeType$1.DOCUMENT] = function () { return ""; }, _a[NodeType$1.PARAGRAPH] = function (node, next) { return "<p".concat(buildCommonAttrs(node), ">").concat(sanitizeHTML(next(node.children)), "</p>"); }, _a[NodeType$1.LINK] = function (node, next) { var href = getAttrString(node, 'href') || getAttrString(node, 'url') || ''; var sanitizedHref = sanitizeHTML(href); var target = getAttrString(node, 'target'); var targetAttr = target ? " target=\"".concat(target, "\"") : ''; return "<a".concat(buildCommonAttrs(node)).concat(sanitizedHref ? " href=\"".concat(sanitizedHref, "\"") : '').concat(targetAttr, ">").concat(sanitizeHTML(next(node.children)), "</a>"); }, _a[NodeType$1.IMAGE] = function (node, next) { var src = getAttrString(node, 'src') || getAttrString(node, 'url'); var sanitizedSrc = src ? encodeURI(sanitizeHTML(src)) : ''; return "<img".concat(buildCommonAttrs(node)).concat(sanitizedSrc ? " src=\"".concat(sanitizedSrc, "\"") : '', " />").concat(sanitizeHTML(next(node.children))); }, _a[NodeType$1.EMBED] = function (node, next) { var src = getAttrString(node, 'src') || getAttrString(node, 'url'); var sanitizedSrc = src ? encodeURI(sanitizeHTML(src)) : ''; return "<iframe".concat(buildCommonAttrs(node)).concat(sanitizedSrc ? " src=\"".concat(sanitizedSrc, "\"") : '', ">").concat(sanitizeHTML(next(node.children)), "</iframe>"); }, _a[NodeType$1.HEADING_1] = function (node, next) { return "<h1".concat(buildCommonAttrs(node), ">").concat(sanitizeHTML(next(node.children)), "</h1>"); }, _a[NodeType$1.HEADING_2] = function (node, next) { return "<h2".concat(buildCommonAttrs(node), ">").concat(sanitizeHTML(next(node.children)), "</h2>"); }, _a[NodeType$1.HEADING_3] = function (node, next) { return "<h3".concat(buildCommonAttrs(node), ">").concat(sanitizeHTML(next(node.children)), "</h3>"); }, _a[NodeType$1.HEADING_4] = function (node, next) { return "<h4".concat(buildCommonAttrs(node), ">").concat(sanitizeHTML(next(node.children)), "</h4>"); }, _a[NodeType$1.HEADING_5] = function (node, next) { return "<h5".concat(buildCommonAttrs(node), ">").concat(sanitizeHTML(next(node.children)), "</h5>"); }, _a[NodeType$1.HEADING_6] = function (node, next) { return "<h6".concat(buildCommonAttrs(node), ">").concat(sanitizeHTML(next(node.children)), "</h6>"); }, _a[NodeType$1.ORDER_LIST] = function (node, next) { var children = foldNestedListSiblingsIntoPreviousLi(node.children); return "<ol".concat(buildCommonAttrs(node), ">").concat(sanitizeHTML(next(children)), "</ol>"); }, _a[NodeType$1.FRAGMENT] = function (node, next) { return "<fragment>".concat(sanitizeHTML(next(node.children)), "</fragment>"); }, _a[NodeType$1.UNORDER_LIST] = function (node, next) { var children = foldNestedListSiblingsIntoPreviousLi(node.children); return "<ul".concat(buildCommonAttrs(node), ">").concat(sanitizeHTML(next(children)), "</ul>"); }, _a[NodeType$1.LIST_ITEM] = function (node, next) { return "<li".concat(buildCommonAttrs(node), ">").concat(sanitizeHTML(next(node.children)), "</li>"); }, _a[NodeType$1.HR] = function () { return "<hr>"; }, _a[NodeType$1.TABLE] = function (node, next) { // Generate colgroup if colWidths attribute is present var colgroupHTML = ''; var colWidths = getAttr(node, 'colWidths'); if (colWidths && Array.isArray(colWidths)) { var totalWidth_1 = colWidths.reduce(function (sum, width) { return sum + width; }, 0); colgroupHTML = "<".concat(NodeType$1.COL_GROUP, " data-width=\"").concat(totalWidth_1, "\">"); colWidths.forEach(function (colWidth) { var widthPercentage = (colWidth / totalWidth_1) * 100; colgroupHTML += "<".concat(NodeType$1.COL, " style=\"width:").concat(widthPercentage.toFixed(2), "%\"/>"); }); colgroupHTML += "</".concat(NodeType$1.COL_GROUP, ">"); } // Generate table with colgroup and other attributes return "<table".concat(buildCommonAttrs(node), ">").concat(colgroupHTML).concat(sanitizeHTML(next(node.children)), "</table>"); }, _a[NodeType$1.TABLE_HEADER] = function (node, next) { return "<thead".concat(buildCommonAttrs(node), ">").concat(sanitizeHTML(next(node.children)), "</thead>"); }, _a[NodeType$1.TABLE_BODY] = function (node, next) { return "<tbody".concat(buildCommonAttrs(node), ">").concat(sanitizeHTML(next(node.children)), "</tbody>"); }, _a[NodeType$1.TABLE_FOOTER] = function (node, next) { return "<tfoot".concat(buildCommonAttrs(node), ">").concat(sanitizeHTML(next(node.children)), "</tfoot>"); }, _a[NodeType$1.TABLE_ROW] = function (node, next) { return "<tr".concat(buildCommonAttrs(node), ">").concat(sanitizeHTML(next(node.children)), "</tr>"); }, _a[NodeType$1.TABLE_HEAD] = function (node, next) { if (getAttr(node, 'void')) return ''; var rowSpan = getAttr(node, 'rowSpan'); var colSpan = getAttr(node, 'colSpan'); var rowSpanAttr = rowSpan ? " rowspan=\"".concat(rowSpan, "\"") : ''; var colSpanAttr = colSpan ? " colspan=\"".concat(colSpan, "\"") : ''; return "<th".concat(rowSpanAttr).concat(colSpanAttr).concat(buildCommonAttrs(node), ">").concat(sanitizeHTML(next(node.children)), "</th>"); }, _a[NodeType$1.TABLE_DATA] = function (node, next) { if (getAttr(node, 'void')) return ''; var rowSpan = getAttr(node, 'rowSpan'); var colSpan = getAttr(node, 'colSpan'); var rowSpanAttr = rowSpan ? " rowspan=\"".concat(rowSpan, "\"") : ''; var colSpanAttr = colSpan ? " colspan=\"".concat(colSpan, "\"") : ''; return "<td".concat(rowSpanAttr).concat(colSpanAttr).concat(buildCommonAttrs(node), ">").concat(sanitizeHTML(next(node.children)), "</td>"); }, _a[NodeType$1.BLOCK_QUOTE] = function (node, next) { return "<blockquote".concat(buildCommonAttrs(node), ">").concat(sanitizeHTML(next(node.children)), "</blockquote>"); }, _a[NodeType$1.CODE] = function (node, next) { return "<code".concat(buildCommonAttrs(node), ">").concat(sanitizeHTML(next(node.children)), "</code>"); }, _a['reference'] = function (node, next) { var type = getAttr(node, 'type'); var displayType = getAttr(node, 'display-type'); if ((type === 'entry' || type === 'asset') && displayType === 'link') { var href = getAttrString(node, 'href') || getAttrString(node, 'url') || ''; var target = getAttrString(node, 'target'); var assetUid = getAttrString(node, 'asset-uid'); var aTagAttrs = buildCommonAttrs(node); if (href) aTagAttrs += " href=\"".concat(href, "\""); if (target) { aTagAttrs += " target=\"".concat(target, "\""); } if (type === 'asset') { aTagAttrs += " type=\"asset\" content-type-uid=\"sys_assets\""; if (assetUid) { aTagAttrs += " data-sys-asset-uid=\"".concat(assetUid, "\""); } aTagAttrs += " sys-style-type=\"download\""; } return "<a".concat(aTagAttrs, ">").concat(sanitizeHTML(next(node.children)), "</a>"); } if (type === 'asset') { var assetLink = getAttrString(node, 'asset-link'); var src = assetLink ? encodeURI(assetLink) : ''; var redactorAttrs = getAttr(node, 'redactor-attributes'); var alt = redactorAttrs === null || redactorAttrs === void 0 ? void 0 : redactorAttrs['alt']; var link = getAttrString(node, 'link'); var target = getAttrString(node, 'target') || ""; var caption = (redactorAttrs === null || redactorAttrs === void 0 ? void 0 : redactorAttrs['asset-caption']) || getAttrString(node, 'asset-caption') || ""; var style = getAttrString(node, 'style'); var assetUid = getAttrString(node, 'asset-uid'); var className = getAttrString(node, 'class-name'); var assetUidAttr = assetUid ? " asset_uid=\"".concat(assetUid, "\"") : ''; var classAttr = className ? " class=\"".concat(sanitizeHTML(className), "\"") : ''; var srcAttr = src ? " src=\"".concat(sanitizeHTML(src), "\"") : ''; var altAttr = alt ? " alt=\"".concat(alt, "\"") : ''; var targetAttr = target === "_blank" ? " target=\"_blank\"" : ''; var styleAttr = style ? " style=\"".concat(style, "\"") : ''; var imageTag = "<img".concat(assetUidAttr).concat(classAttr).concat(srcAttr).concat(altAttr).concat(targetAttr).concat(styleAttr, " />"); var styleAttrFig = style ? " style=\"".concat(style, "\"") : ''; return "<figure".concat(styleAttrFig, ">") + (link ? "<a href=\"".concat(link, "\" target=\"").concat(target || "", "\">") : "") + imageTag + (link ? "</a>" : "") + (caption ? "<figcaption>".concat(caption, "</figcaption>") : "") + "</figure>"; } return ""; }, _a['default'] = function (node, next) { return sanitizeHTML(next(node.children)); }, _a[MarkType$1.BOLD] = function (text) { return "<strong>".concat(sanitizeHTML(text), "</strong>"); }, _a[MarkType$1.ITALIC] = function (text) { return "<em>".concat(sanitizeHTML(text), "</em>"); }, _a[MarkType$1.UNDERLINE] = function (text) { return "<u>".concat(sanitizeHTML(text), "</u>"); }, _a[MarkType$1.STRIKE_THROUGH] = function (text) { return "<strike>".concat(sanitizeHTML(text), "</strike>"); }, _a[MarkType$1.INLINE_CODE] = function (text) { return "<span data-type='inlineCode'>".concat(sanitizeHTML(text), "</span>"); }, _a[MarkType$1.SUBSCRIPT] = function (text) { return "<sub>".concat(sanitizeHTML(text), "</sub>"); }, _a[MarkType$1.SUPERSCRIPT] = function (text) { return "<sup>".concat(sanitizeHTML(text), "</sup>"); }, _a[MarkType$1.BREAK] = function (text) { // Check if text is only newlines (which will be converted to <br /> by sanitizeHTML) // If so, don't add an extra <br /> to avoid duplication var onlyNewlines = /^\n+$/.test(text); if (onlyNewlines) { return sanitizeHTML(text); } return "<br />".concat(sanitizeHTML(text)); }, _a[MarkType$1.CLASSNAME_OR_ID] = function (text, classname, id) { return "<span".concat(classname ? " class=\"".concat(classname, "\"") : "").concat(id ? " id=\"".concat(id, "\"") : "", ">").concat(sanitizeHTML(text), "</span>"); }, _a); function enumerate(entries, process) { for (var _i = 0, entries_1 = entries; _i < entries_1.length; _i++) { var entry = entries_1[_i]; process(entry); } } function enumerateContents(content, renderOption, renderEmbed) { if (!(content instanceof Array) && content.type !== 'doc') { return content; } if (content instanceof Array) { var result_1 = []; content.forEach(function (doc) { result_1.push(enumerateContents(doc, renderOption, renderEmbed)); }); return result_1; } var commonRenderOption = __assign(__assign({}, defaultNodeOption), renderOption); return nodeChildrenToHTML(content.children, commonRenderOption, renderEmbed); } function textNodeToHTML(node, renderOption) { var text = replaceHtmlEntities(node.text); // Convert newlines to <br /> tags if there are no other marks // This ensures newlines are always handled consistently var hasMarks = false; if (node.classname || node.id) { text = renderOption[MarkType$1.CLASSNAME_OR_ID](text, node.classname, node.id); hasMarks = true; } if (node.break) { text = renderOption[MarkType$1.BREAK](text); hasMarks = true; } if (node.superscript) { text = renderOption[MarkType$1.SUPERSCRIPT](text); hasMarks = true; } if (node.subscript) { text = renderOption[MarkType$1.SUBSCRIPT](text); hasMarks = true; } if (node.inlineCode) { text = renderOption[MarkType$1.INLINE_CODE](text); hasMarks = true; } if (node.strikethrough) { text = renderOption[MarkType$1.STRIKE_THROUGH](text); hasMarks = true; } if (node.underline) { text = renderOption[MarkType$1.UNDERLINE](text); hasMarks = true; } if (node.italic) { text = renderOption[MarkType$1.ITALIC](text); hasMarks = true; } if (node.bold) { text = renderOption[MarkType$1.BOLD](text); hasMarks = true; } // If no marks were applied, but text contains newlines, convert them to <br /> if (!hasMarks && text.includes('\n')) { text = text.replace(/\n/g, '<br />'); } return text; } function referenceToHTML(node, renderOption, renderEmbed) { var _a; function sendToRenderOption(referenceNode) { var next = function (nodes) { return nodeChildrenToHTML(nodes, renderOption, renderEmbed); }; return renderOption[referenceNode.type](referenceNode, next); } if ((node.attrs.type === 'entry' || node.attrs.type === 'asset') && node.attrs['display-type'] === 'link') { var entryText = node.children ? nodeChildrenToHTML(node.children, renderOption, renderEmbed) : ''; if (renderOption[node.type] !== undefined) { return sendToRenderOption(node); } var href = node.attrs.href || node.attrs.url; var aTagAttrs = "".concat(node.attrs.style ? " style=\"".concat(node.attrs.style, "\"") : "").concat(node.attrs['class-name'] ? " class=\"".concat(node.attrs['class-name'], "\"") : "").concat(node.attrs.id ? " id=\"".concat(node.attrs.id, "\"") : "").concat(href ? " href=\"".concat(href, "\"") : ""); if (node.attrs.target) { aTagAttrs += " target=\"".concat(node.attrs.target, "\""); } if (node.attrs.type == 'asset') { aTagAttrs += " type=\"asset\" content-type-uid=\"sys_assets\" ".concat(node.attrs['asset-uid'] ? "data-sys-asset-uid=\"".concat(node.attrs['asset-uid'], "\"") : "", " sys-style-type=\"download\""); } var aTag = "<a".concat(aTagAttrs, ">").concat(entryText, "</a>"); return aTag; } if (!renderEmbed && renderOption[node.type] !== undefined) { return sendToRenderOption(node); } if (!renderEmbed) { return ''; } var metadata = nodeToMetadata(node.attrs, (((_a = node.children) === null || _a === void 0 ? void 0 : _a.length) > 0 ? node.children[0] : {})); var item = renderEmbed(metadata); if (!item && renderOption[node.type] !== undefined) { return sendToRenderOption(node); } return findRenderString(item, metadata, renderOption); } function nodeChildrenToHTML(nodes, renderOption, renderEmbed) { return nodes.map(function (node) { return nodeToHTML(node, renderOption, renderEmbed); }).join(''); } function styleObjectToString(styleObj) { if (!styleObj) return ''; if (typeof styleObj === 'string') { return styleObj; } var styleString = ''; for (var key in styleObj) { if (styleObj.hasOwnProperty(key)) { var value = styleObj[key]; styleString += "".concat(key, ":").concat(value, ";"); } } return styleString; } function nodeToHTML(node, renderOption, renderEmbed) { var _a; if ((_a = node === null || node === void 0 ? void 0 : node.attrs) === null || _a === void 0 ? void 0 : _a.style) { node.attrs.style = styleObjectToString(node.attrs.style); } if (!node.type) { return textNodeToHTML(node, renderOption); } else if (node.type === 'reference') { return referenceToHTML(node, renderOption, renderEmbed); } else { var next = function (nodes) { return nodeChildrenToHTML(nodes, renderOption, renderEmbed); }; if (renderOption[node.type] !== undefined) { return renderOption[node.type](node, next); } else { return renderOption.default(node, next); } } } /** * Converts Supercharged RTE (JSON) content to HTML for one or more entries. * Walks the given paths on each entry, finds JSON RTE content, resolves embedded * items from the entry, and renders nodes using the optional renderOption. Mutates * the entry JSON in-place by replacing content with the generated HTML. * * @param option - Configuration for conversion. * @param option.entry - Entry or array of entries that contain Supercharged RTE (JSON) fields. * @param option.paths - Key paths to the JSON RTE fields (e.g. `['rte_field_uid', 'group.rte_uid']`). * @param option.renderOption - Optional render options to customize how nodes and embedded items are rendered to HTML. */ function jsonToHTML$1(option) { if (option.entry instanceof Array) { enumerate(option.entry, function (entry) { jsonToHTML$1({ entry: entry, paths: option.paths, renderOption: option.renderOption }); }); } else { enumerateKeys$1({ entry: option.entry, paths: option.paths, renderOption: option.renderOption, }); } } function enumerateKeys$1(option) { for (var _i = 0, _a = option.paths; _i < _a.length; _i++) { var key = _a[_i]; findRenderContent(key, option.entry, (function (content) { return enumerateContents(content, option.renderOption, function (metadata) { return findEmbeddedItems(metadata, option.entry)[0]; }); })); } } function jsonToHTML(option) { if (option.entry instanceof Array) { enumerate(option.entry, function (entry) { jsonToHTML({ entry: entry, paths: option.paths, renderOption: option.renderOption }); }); } else { enumerateKeys({ entry: option.entry, paths: option.paths, renderOption: option.renderOption, }); } } function enumerateKeys(option) { for (var _i = 0, _a = option.paths; _i < _a.length; _i++) { var key = _a[_i]; findRenderContent(key, option.entry, (function (content) { if (content === null || content === void 0 ? void 0 : content.json) { var edges = content.embedded_itemsConnection ? content.embedded_itemsConnection.edges : []; var items_1 = Object.values(edges || []).reduce(function (accumulator, value) { return accumulator.concat(value.node); }, []); return enumerateContents(content.json, option.renderOption, function (metadata) { return findGQLEmbeddedItems(metadata, items_1)[0]; }); } return content; })); } } /** * GraphQL API utilities for Contentstack. Provides methods to work with * content fetched via the GraphQL API, including rendering Supercharged RTE * (JSON) with embedded items from the GQL response. */ var GQL = { /** * Converts Supercharged RTE (JSON) content to HTML for entries from a GraphQL response. * Uses `embedded_itemsConnection.edges` to resolve embedded items. Mutates the entry * JSON in-place by replacing JSON RTE content with the generated HTML. * * @param option - Configuration for conversion. * @param option.entry - Entry or array of entries (EmbeddedItem) from a GQL response with JSON RTE and embedded_itemsConnection. * @param option.paths - Key paths to the JSON RTE fields on the entry. * @param option.renderOption - Optional render options to customize how nodes and embedded items are rendered to HTML. */ jsonToHTML: jsonToHTML }; /** * Adds Contentstack Live Preview (CSLP) data tags to an entry for editable UIs. * Mutates the entry by attaching a `$` property with tag strings or objects * (e.g. `data-cslp` / `data-cslp-parent-field`) for each field, including nested * objects and references. Supports variant-aware tagging when the entry has * applied variants. * * @param entry - The entry (EmbeddedItem) to tag. Must have uid and optional system/applied variants. * @param contentTypeUid - Content type UID (e.g. `blog_post`). Used as part of the tag path. * @param tagsAsObject - If true, tags are stored as objects (e.g. `{ "data-cslp": "..." }`); if false, as strings (e.g. `data-cslp=...`). * @param locale - Locale code for the tag path (default: `'en-us'`). * @param options.useLowerCaseLocale - Optional boolean to make locale case-insensitive. */ function addTags(entry, contentTypeUid, tagsAsObject, locale, options) { var _a; if (locale === void 0) { locale = 'en-us'; } var _b = (options || {}).useLowerCaseLocale, useLowerCaseLocale = _b === void 0 ? true : _b; if (entry) { // handle case senstivity for contentTypeUid and locale contentTypeUid = contentTypeUid.toLowerCase(); locale = useLowerCaseLocale ? locale.toLowerCase() : locale; var appliedVariants = entry._applied_variants || ((_a = entry === null || entry === void 0 ? void 0 : entry.system) === null || _a === void 0 ? void 0 : _a.applied_variants) || null; entry.$ = getTag(entry, "".concat(contentTypeUid, ".").concat(entry.uid, ".").concat(locale), tagsAsObject, locale, { _applied_variants: appliedVariants, shouldApplyVariant: !!appliedVariants, metaKey: '' }); } } /** @internal Exported for testing the null/undefined guard (Issue #193). */ function getTag(content, prefix, tagsAsObject, locale, appliedVariants) { if (content == null) { return {}; } var tags = {}; var shouldApplyVariant = appliedVariants.shouldApplyVariant, _applied_variants = appliedVariants._applied_variants; Object.entries(content).forEach(function (_a) { var _b, _c; var key = _a[0], value = _a[1]; if (key === '$') return; var metaUID = (_c = (_b = value === null || value === void 0 ? void 0 : value._metadata) === null || _b === void 0 ? void 0 : _b.uid) !== null && _c !== void 0 ? _c : ''; var metaKeyPrefix = appliedVariants.metaKey ? appliedVariants.metaKey + '.' : ''; var updatedMetakey = appliedVariants.shouldApplyVariant ? "".concat(metaKeyPrefix).concat(key) : ''; if (metaUID && updatedMetakey) updatedMetakey = updatedMetakey + '.' + metaUID; switch (typeof value) { case "object": if (Array.isArray(value)) { value.forEach(function (obj, index) { var _a, _b, _c; if (obj === null || obj === undefined) { return; } var childKey = "".concat(key, "__").concat(index); var parentKey = "".concat(key, "__parent"); metaUID = (_b = (_a = obj === null || obj === void 0 ? void 0 : obj._metadata) === null || _a === void 0 ? void 0 : _a.uid) !== null && _b !== void 0 ? _b : ''; updatedMetakey = appliedVariants.shouldApplyVariant ? "".concat(metaKeyPrefix).concat(key) : ''; if (metaUID && updatedMetakey) updatedMetakey = updatedMetakey + '.' + metaUID; /** * Indexes of array are handled here * { * "array": ["hello", "world"], * "$": { * "array": {"data-cslp": "content_type_uid.entry_uid.locale.array"} * "array__0": {"data-cslp": "content_type_uid.entry_uid.locale.array.0"} * "array__1": {"data-cslp": "content_type_uid.entry_uid.locale.array.1"} * } * } */ tags[childKey] = tagsAsObject ? getTagsValueAsObject("".concat(prefix, ".").concat(key, ".").concat(index), { _applied_variants: _applied_variants, shouldApplyVariant: shouldApplyVariant, metaKey: updatedMetakey }) : getTagsValueAsString("".concat(prefix, ".").concat(key, ".").concat(index), { _applied_variants: _applied_variants, shouldApplyVariant: shouldApplyVariant, metaKey: updatedMetakey }); tags[parentKey] = tagsAsObject ? getParentTagsValueAsObject("".concat(prefix, ".").concat(key)) : getParentTagsValueAsString("".concat(prefix, ".").concat(key)); if ((obj === null || obj === void 0 ? void 0 : obj._content_type_uid) !== undefined && (obj === null || obj === void 0 ? void 0 : obj.uid) !== undefined) { /** * References are handled here * { * "reference": [{ * "title": "title", * "uid": "ref_uid", * "_content_type_uid": "ref_content_type_uid", * "$": {"title": {"data-cslp": "ref_content_type_uid.ref_uid.locale.title"}} * }] * } */ var newAppliedVariants = obj._applied_variants || ((_c = obj === null || obj === void 0 ? void 0 : obj.system) === null || _c === void 0 ? void 0 : _c.applied_variants) || null; //check for _applied_variants in the reference object only return null if not present , do not check in the parent object; var newShouldApplyVariant = !!newAppliedVariants; value[index].$ = getTag(obj, "".concat(obj._content_type_uid, ".").concat(obj.uid, ".").concat(obj.locale || locale), tagsAsObject, locale, { _applied_variants: newAppliedVariants, shouldApplyVariant: newShouldApplyVariant, metaKey: "" }); } else if (typeof obj === "object") { /** * Objects inside Array like modular blocks are handled here * { * "array": [{ * "title": "title", * "$": {"title": {"data-cslp": "content_type_uid.entry_uid.locale.array.0.title"}} * }] * } */ obj.$ = getTag(obj, "".concat(prefix, ".").concat(key, ".").concat(index), tagsAsObject, locale, { _applied_variants: _applied_variants, shouldApplyVariant: shouldApplyVariant, metaKey: updatedMetakey }); } }); } else { if (value) { /** * Objects are handled here * { * "object": { * "title": "title", * "$": { * "title": {"data-cslp": "content_type_uid.entry_uid.locale.o