@ecency/render-helper 2.4.24 → 2.4.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser/index.d.ts +8 -2
- package/dist/browser/index.js +81 -43
- package/dist/browser/index.js.map +1 -1
- package/dist/node/index.cjs +81 -43
- package/dist/node/index.cjs.map +1 -1
- package/dist/node/index.mjs +81 -43
- package/dist/node/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/browser/index.d.ts
CHANGED
|
@@ -9,6 +9,11 @@ interface Entry {
|
|
|
9
9
|
json_metadata?: any;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
interface RenderOptions {
|
|
13
|
+
/** When true, video embeds (3Speak, YouTube, etc.) render as iframes directly without a play button overlay. */
|
|
14
|
+
embedVideosDirectly?: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
12
17
|
/**
|
|
13
18
|
* SEO context for controlling rel attributes on external links in user-generated content.
|
|
14
19
|
*
|
|
@@ -28,8 +33,9 @@ interface SeoContext {
|
|
|
28
33
|
* @param _webp - @deprecated Ignored. Format is now handled server-side via Accept header content negotiation.
|
|
29
34
|
* @param parentDomain - Parent domain for iframe embed parameters
|
|
30
35
|
* @param seoContext - Optional SEO context for structured data
|
|
36
|
+
* @param renderOptions - Optional rendering options (e.g. embedVideosDirectly)
|
|
31
37
|
*/
|
|
32
|
-
declare function markdown2Html(obj: Entry | string, forApp?: boolean, _webp?: boolean, parentDomain?: string, seoContext?: SeoContext): string;
|
|
38
|
+
declare function markdown2Html(obj: Entry | string, forApp?: boolean, _webp?: boolean, parentDomain?: string, seoContext?: SeoContext, renderOptions?: RenderOptions): string;
|
|
33
39
|
|
|
34
40
|
declare function catchPostImage(obj: Entry | string, width?: number, height?: number, format?: string): string | null;
|
|
35
41
|
|
|
@@ -55,4 +61,4 @@ declare const SECTION_LIST: string[];
|
|
|
55
61
|
|
|
56
62
|
declare function isValidPermlink(permlink: string): boolean;
|
|
57
63
|
|
|
58
|
-
export { type Entry, SECTION_LIST, type SeoContext, catchPostImage, isValidPermlink, getPostBodySummary as postBodySummary, proxifyImageSrc, markdown2Html as renderPostBody, setCacheSize, setProxyBase };
|
|
64
|
+
export { type Entry, type RenderOptions, SECTION_LIST, type SeoContext, catchPostImage, isValidPermlink, getPostBodySummary as postBodySummary, proxifyImageSrc, markdown2Html as renderPostBody, setCacheSize, setProxyBase };
|
package/dist/browser/index.js
CHANGED
|
@@ -430,7 +430,7 @@ var addLineBreakBeforePostLink = (el, forApp, isInline) => {
|
|
|
430
430
|
el.parentNode.insertBefore(br, el);
|
|
431
431
|
}
|
|
432
432
|
};
|
|
433
|
-
function a(el, forApp, parentDomain = "ecency.com", seoContext) {
|
|
433
|
+
function a(el, forApp, parentDomain = "ecency.com", seoContext, renderOptions) {
|
|
434
434
|
if (!el || !el.parentNode) {
|
|
435
435
|
return;
|
|
436
436
|
}
|
|
@@ -439,7 +439,7 @@ function a(el, forApp, parentDomain = "ecency.com", seoContext) {
|
|
|
439
439
|
return;
|
|
440
440
|
}
|
|
441
441
|
const className = el.getAttribute("class");
|
|
442
|
-
if (["markdown-author-link", "markdown-tag-link"].
|
|
442
|
+
if (className && (["markdown-author-link", "markdown-tag-link"].includes(className) || className.includes("er-author") || className.includes("er-tag"))) {
|
|
443
443
|
return;
|
|
444
444
|
}
|
|
445
445
|
if (href && href.trim().toLowerCase().startsWith("javascript:")) {
|
|
@@ -779,14 +779,29 @@ function a(el, forApp, parentDomain = "ecency.com", seoContext) {
|
|
|
779
779
|
if (startTime) {
|
|
780
780
|
el.setAttribute("data-start-time", startTime);
|
|
781
781
|
}
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
782
|
+
if (renderOptions?.embedVideosDirectly) {
|
|
783
|
+
const wrapper = el.ownerDocument.createElement("span");
|
|
784
|
+
wrapper.setAttribute("class", "er-youtube-frame");
|
|
785
|
+
wrapper.setAttribute("style", "display:block");
|
|
786
|
+
const iframe2 = el.ownerDocument.createElement("iframe");
|
|
787
|
+
iframe2.setAttribute("class", "youtube-player");
|
|
788
|
+
iframe2.setAttribute("src", embedSrc);
|
|
789
|
+
iframe2.setAttribute("title", "YouTube video");
|
|
790
|
+
iframe2.setAttribute("allow", "accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture; web-share");
|
|
791
|
+
iframe2.setAttribute("allowfullscreen", "");
|
|
792
|
+
wrapper.appendChild(iframe2);
|
|
793
|
+
el.appendChild(wrapper);
|
|
794
|
+
el.setAttribute("class", "markdown-video-link markdown-video-link-youtube er-youtube");
|
|
795
|
+
} else {
|
|
796
|
+
const thumbImg = el.ownerDocument.createElement("img");
|
|
797
|
+
thumbImg.setAttribute("class", "no-replace video-thumbnail");
|
|
798
|
+
thumbImg.setAttribute("itemprop", "thumbnailUrl");
|
|
799
|
+
thumbImg.setAttribute("src", thumbnail);
|
|
800
|
+
const play = el.ownerDocument.createElement("span");
|
|
801
|
+
play.setAttribute("class", "markdown-video-play");
|
|
802
|
+
el.appendChild(thumbImg);
|
|
803
|
+
el.appendChild(play);
|
|
804
|
+
}
|
|
790
805
|
return;
|
|
791
806
|
}
|
|
792
807
|
match = href.match(VIMEO_REGEX);
|
|
@@ -908,21 +923,36 @@ function a(el, forApp, parentDomain = "ecency.com", seoContext) {
|
|
|
908
923
|
if (el.textContent.trim() === href) {
|
|
909
924
|
el.textContent = "";
|
|
910
925
|
}
|
|
911
|
-
if (
|
|
912
|
-
const
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
926
|
+
if (renderOptions?.embedVideosDirectly) {
|
|
927
|
+
const wrapper = el.ownerDocument.createElement("span");
|
|
928
|
+
wrapper.setAttribute("class", "er-speak-frame");
|
|
929
|
+
wrapper.setAttribute("style", "display:block");
|
|
930
|
+
const iframe2 = el.ownerDocument.createElement("iframe");
|
|
931
|
+
iframe2.setAttribute("class", "speak-iframe");
|
|
932
|
+
iframe2.setAttribute("src", videoHref);
|
|
933
|
+
iframe2.setAttribute("title", "3Speak video");
|
|
934
|
+
iframe2.setAttribute("allow", "accelerometer; encrypted-media; gyroscope; picture-in-picture; web-share");
|
|
935
|
+
iframe2.setAttribute("allowfullscreen", "");
|
|
936
|
+
wrapper.appendChild(iframe2);
|
|
937
|
+
el.appendChild(wrapper);
|
|
938
|
+
el.setAttribute("class", "markdown-video-link markdown-video-link-speak er-speak");
|
|
939
|
+
} else {
|
|
940
|
+
if (imgEls2.length === 1) {
|
|
941
|
+
const src = imgEls2[0].getAttribute("src");
|
|
942
|
+
if (src) {
|
|
943
|
+
const thumbnail = proxifyImageSrc(src.replace(/\s+/g, ""), 0, 0, "match");
|
|
944
|
+
const thumbImg = el.ownerDocument.createElement("img");
|
|
945
|
+
thumbImg.setAttribute("class", "no-replace video-thumbnail");
|
|
946
|
+
thumbImg.setAttribute("itemprop", "thumbnailUrl");
|
|
947
|
+
thumbImg.setAttribute("src", thumbnail);
|
|
948
|
+
el.appendChild(thumbImg);
|
|
949
|
+
el.removeChild(imgEls2[0]);
|
|
950
|
+
}
|
|
921
951
|
}
|
|
952
|
+
const play = el.ownerDocument.createElement("span");
|
|
953
|
+
play.setAttribute("class", "markdown-video-play");
|
|
954
|
+
el.appendChild(play);
|
|
922
955
|
}
|
|
923
|
-
const play = el.ownerDocument.createElement("span");
|
|
924
|
-
play.setAttribute("class", "markdown-video-play");
|
|
925
|
-
el.appendChild(play);
|
|
926
956
|
return;
|
|
927
957
|
}
|
|
928
958
|
}
|
|
@@ -1012,7 +1042,7 @@ function a(el, forApp, parentDomain = "ecency.com", seoContext) {
|
|
|
1012
1042
|
}
|
|
1013
1043
|
|
|
1014
1044
|
// src/methods/iframe.method.ts
|
|
1015
|
-
function iframe(el, parentDomain = "ecency.com") {
|
|
1045
|
+
function iframe(el, parentDomain = "ecency.com", forApp = false) {
|
|
1016
1046
|
if (!el || !el.parentNode) {
|
|
1017
1047
|
return;
|
|
1018
1048
|
}
|
|
@@ -1056,7 +1086,10 @@ function iframe(el, parentDomain = "ecency.com") {
|
|
|
1056
1086
|
normalizedSrc = `${normalizedSrc}&mode=iframe`;
|
|
1057
1087
|
}
|
|
1058
1088
|
const hasAutoplay = /[?&]autoplay=/.test(normalizedSrc);
|
|
1059
|
-
|
|
1089
|
+
let s = hasAutoplay ? normalizedSrc : `${normalizedSrc}&autoplay=true`;
|
|
1090
|
+
if (forApp && !/[?&]layout=/.test(s)) {
|
|
1091
|
+
s = `${s}&layout=mobile`;
|
|
1092
|
+
}
|
|
1060
1093
|
el.setAttribute("src", s);
|
|
1061
1094
|
el.setAttribute("class", "speak-iframe");
|
|
1062
1095
|
return;
|
|
@@ -1181,15 +1214,17 @@ function p(el) {
|
|
|
1181
1214
|
}
|
|
1182
1215
|
|
|
1183
1216
|
// src/methods/linkify.method.ts
|
|
1184
|
-
function linkify(content, forApp) {
|
|
1217
|
+
function linkify(content, forApp, renderOptions) {
|
|
1185
1218
|
content = content.replace(/(^|\s|>)(#[-a-z\d]+)/gi, (tag) => {
|
|
1186
1219
|
if (/#[\d]+$/.test(tag)) return tag;
|
|
1187
1220
|
const preceding = /^\s|>/.test(tag) ? tag[0] : "";
|
|
1188
1221
|
tag = tag.replace(">", "");
|
|
1189
1222
|
const tag2 = tag.trim().substring(1);
|
|
1190
1223
|
const tagLower = tag2.toLowerCase();
|
|
1191
|
-
|
|
1192
|
-
|
|
1224
|
+
if (!forApp) {
|
|
1225
|
+
return `${preceding}<a class="er-tag er-tag-link" href="/trending/${tagLower}">${tag.trim()}</a>`;
|
|
1226
|
+
}
|
|
1227
|
+
return `${preceding}<a class="markdown-tag-link" data-tag="${tagLower}">${tag.trim()}</a>`;
|
|
1193
1228
|
});
|
|
1194
1229
|
content = content.replace(
|
|
1195
1230
|
/(^|[^a-zA-Z0-9_!#$%&*@@/]|(^|[^a-zA-Z0-9_+~.-/]))[@@]([a-z][-.a-z\d^/]+[a-z\d])/gi,
|
|
@@ -1197,8 +1232,11 @@ function linkify(content, forApp) {
|
|
|
1197
1232
|
const userLower = user.toLowerCase();
|
|
1198
1233
|
const preceedings = (preceeding1 || "") + (preceeding2 || "");
|
|
1199
1234
|
if (userLower.indexOf("/") === -1 && isValidUsername(user)) {
|
|
1200
|
-
|
|
1201
|
-
|
|
1235
|
+
if (!forApp) {
|
|
1236
|
+
const avatarSrc = `https://images.ecency.com/u/${userLower}/avatar/small`;
|
|
1237
|
+
return `${preceedings}<a class="er-author er-author-link" href="/@${userLower}"><img class="er-author-link-image" src="${avatarSrc}" alt="${userLower}"/><span class="er-author-link-content"><span class="er-author-link-label">Hive account</span><span>@${userLower}</span></span></a>`;
|
|
1238
|
+
}
|
|
1239
|
+
return `${preceedings}<a class="markdown-author-link" data-author="${userLower}">@${user}</a>`;
|
|
1202
1240
|
} else {
|
|
1203
1241
|
return match;
|
|
1204
1242
|
}
|
|
@@ -1255,7 +1293,7 @@ function hasAncestor(node, tagNames) {
|
|
|
1255
1293
|
}
|
|
1256
1294
|
return false;
|
|
1257
1295
|
}
|
|
1258
|
-
function text(node, forApp) {
|
|
1296
|
+
function text(node, forApp, renderOptions) {
|
|
1259
1297
|
if (!node || !node.parentNode) {
|
|
1260
1298
|
return;
|
|
1261
1299
|
}
|
|
@@ -1336,7 +1374,7 @@ function text(node, forApp) {
|
|
|
1336
1374
|
}
|
|
1337
1375
|
|
|
1338
1376
|
// src/methods/traverse.method.ts
|
|
1339
|
-
function traverse(node, forApp, depth = 0, state = { firstImageFound: false }, parentDomain = "ecency.com", seoContext) {
|
|
1377
|
+
function traverse(node, forApp, depth = 0, state = { firstImageFound: false }, parentDomain = "ecency.com", seoContext, renderOptions) {
|
|
1340
1378
|
if (!node || !node.childNodes) {
|
|
1341
1379
|
return;
|
|
1342
1380
|
}
|
|
@@ -1345,10 +1383,10 @@ function traverse(node, forApp, depth = 0, state = { firstImageFound: false }, p
|
|
|
1345
1383
|
const next = child.nextSibling;
|
|
1346
1384
|
const prev = child.previousSibling;
|
|
1347
1385
|
if (child.nodeName.toLowerCase() === "a") {
|
|
1348
|
-
a(child, forApp, parentDomain, seoContext);
|
|
1386
|
+
a(child, forApp, parentDomain, seoContext, renderOptions);
|
|
1349
1387
|
}
|
|
1350
1388
|
if (child.nodeName.toLowerCase() === "iframe") {
|
|
1351
|
-
iframe(child, parentDomain);
|
|
1389
|
+
iframe(child, parentDomain, forApp);
|
|
1352
1390
|
}
|
|
1353
1391
|
if (child.nodeName === "#text") {
|
|
1354
1392
|
text(child, forApp);
|
|
@@ -1360,11 +1398,11 @@ function traverse(node, forApp, depth = 0, state = { firstImageFound: false }, p
|
|
|
1360
1398
|
p(child);
|
|
1361
1399
|
}
|
|
1362
1400
|
if (child.parentNode) {
|
|
1363
|
-
traverse(child, forApp, depth + 1, state, parentDomain, seoContext);
|
|
1401
|
+
traverse(child, forApp, depth + 1, state, parentDomain, seoContext, renderOptions);
|
|
1364
1402
|
} else {
|
|
1365
1403
|
const possibleReplacement = next ? next.previousSibling : node.lastChild;
|
|
1366
1404
|
if (possibleReplacement && possibleReplacement !== prev && possibleReplacement.parentNode === node) {
|
|
1367
|
-
traverse(possibleReplacement, forApp, depth + 1, state, parentDomain, seoContext);
|
|
1405
|
+
traverse(possibleReplacement, forApp, depth + 1, state, parentDomain, seoContext, renderOptions);
|
|
1368
1406
|
}
|
|
1369
1407
|
}
|
|
1370
1408
|
child = next;
|
|
@@ -1417,7 +1455,7 @@ function fixBlockLevelTagsInParagraphs(html) {
|
|
|
1417
1455
|
html = html.replace(/<p><br>\s*<\/p>/g, "");
|
|
1418
1456
|
return html;
|
|
1419
1457
|
}
|
|
1420
|
-
function markdownToHTML(input, forApp, parentDomain = "ecency.com", seoContext) {
|
|
1458
|
+
function markdownToHTML(input, forApp, parentDomain = "ecency.com", seoContext, renderOptions) {
|
|
1421
1459
|
input = input.replace(new RegExp("https://leofinance.io/threads/view/", "g"), "/@");
|
|
1422
1460
|
input = input.replace(new RegExp("https://leofinance.io/posts/", "g"), "/@");
|
|
1423
1461
|
input = input.replace(new RegExp("https://leofinance.io/threads/", "g"), "/@");
|
|
@@ -1477,7 +1515,7 @@ function markdownToHTML(input, forApp, parentDomain = "ecency.com", seoContext)
|
|
|
1477
1515
|
output = md.render(input);
|
|
1478
1516
|
output = fixBlockLevelTagsInParagraphs(output);
|
|
1479
1517
|
const doc = DOMParser.parseFromString(`<body id="root">${removeDuplicateAttributes(output)}</body>`, "text/html");
|
|
1480
|
-
traverse(doc, forApp, 0, { firstImageFound: false }, parentDomain, seoContext);
|
|
1518
|
+
traverse(doc, forApp, 0, { firstImageFound: false }, parentDomain, seoContext, renderOptions);
|
|
1481
1519
|
output = serializer.serializeToString(doc);
|
|
1482
1520
|
} catch (error) {
|
|
1483
1521
|
try {
|
|
@@ -1490,7 +1528,7 @@ function markdownToHTML(input, forApp, parentDomain = "ecency.com", seoContext)
|
|
|
1490
1528
|
});
|
|
1491
1529
|
const repairedHtml = domSerializer(dom.children);
|
|
1492
1530
|
const doc = DOMParser.parseFromString(`<body id="root">${removeDuplicateAttributes(repairedHtml)}</body>`, "text/html");
|
|
1493
|
-
traverse(doc, forApp, 0, { firstImageFound: false }, parentDomain, seoContext);
|
|
1531
|
+
traverse(doc, forApp, 0, { firstImageFound: false }, parentDomain, seoContext, renderOptions);
|
|
1494
1532
|
output = serializer.serializeToString(doc);
|
|
1495
1533
|
} catch (fallbackError) {
|
|
1496
1534
|
const escapedContent = he2.encode(output || md.render(input));
|
|
@@ -1518,18 +1556,18 @@ function cacheSet(key, value) {
|
|
|
1518
1556
|
}
|
|
1519
1557
|
|
|
1520
1558
|
// src/markdown-2-html.ts
|
|
1521
|
-
function markdown2Html(obj, forApp = true, _webp = false, parentDomain = "ecency.com", seoContext) {
|
|
1559
|
+
function markdown2Html(obj, forApp = true, _webp = false, parentDomain = "ecency.com", seoContext, renderOptions) {
|
|
1522
1560
|
if (typeof obj === "string") {
|
|
1523
1561
|
const cleanedStr = cleanReply(obj);
|
|
1524
|
-
return markdownToHTML(cleanedStr, forApp, parentDomain, seoContext);
|
|
1562
|
+
return markdownToHTML(cleanedStr, forApp, parentDomain, seoContext, renderOptions);
|
|
1525
1563
|
}
|
|
1526
|
-
const key = `${makeEntryCacheKey(obj)}-md-${forApp ? "app" : "site"}-${parentDomain}${seoContext ? `-seo${seoContext.authorReputation ?? ""}-${seoContext.postPayout ?? ""}` : ""}`;
|
|
1564
|
+
const key = `${makeEntryCacheKey(obj)}-md-${forApp ? "app" : "site"}-${parentDomain}${seoContext ? `-seo${seoContext.authorReputation ?? ""}-${seoContext.postPayout ?? ""}` : ""}${renderOptions?.embedVideosDirectly ? "-embed" : ""}`;
|
|
1527
1565
|
const item = cacheGet(key);
|
|
1528
1566
|
if (item) {
|
|
1529
1567
|
return item;
|
|
1530
1568
|
}
|
|
1531
1569
|
const cleanBody = cleanReply(obj.body);
|
|
1532
|
-
const res = markdownToHTML(cleanBody, forApp, parentDomain, seoContext);
|
|
1570
|
+
const res = markdownToHTML(cleanBody, forApp, parentDomain, seoContext, renderOptions);
|
|
1533
1571
|
cacheSet(key, res);
|
|
1534
1572
|
return res;
|
|
1535
1573
|
}
|