@ecency/render-helper 2.4.15 → 2.4.17

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.
@@ -78,8 +78,8 @@ var LBRY_REGEX = /^(https?:)?\/\/lbry.tv\/\$\/embed\/[^?#]+(?:$|[?#])/i;
78
78
  var ODYSEE_REGEX = /^(https?:)?\/\/odysee\.com\/(?:\$|%24)\/embed\/[^/?#]+(?:$|[?#])/i;
79
79
  var SKATEHIVE_IPFS_REGEX = /^https?:\/\/ipfs\.skatehive\.app\/ipfs\/([^/?#]+)/i;
80
80
  var ARCH_REGEX = /^(https?:)?\/\/archive.org\/embed\/[^/?#]+(?:$|[?#])/i;
81
- var SPEAK_REGEX = /(?:https?:\/\/(?:(?:play\.)?3speak.([a-z]+)\/watch\?v=)|(?:(?:play\.)?3speak.([a-z]+)\/embed\?v=))([A-Za-z0-9\_\-\.\/]+)(&.*)?/i;
82
- var SPEAK_EMBED_REGEX = /^(https?:)?\/\/(?:play\.)?3speak.([a-z]+)\/embed\?[^/]+$/i;
81
+ var SPEAK_REGEX = /(?:https?:\/\/(?:(?:play\.)?3speak\.([a-z]+)\/watch\?v=)|(?:(?:play\.)?3speak\.([a-z]+)\/embed\?v=))([A-Za-z0-9_\-\.\/]+)(&.*)?/i;
82
+ var SPEAK_EMBED_REGEX = /^(https?:)?\/\/(?:play\.)?3speak\.([a-z]+)\/(?:embed|watch)\?.+$/i;
83
83
  var TWITTER_REGEX = /(?:https?:\/\/(?:(?:twitter\.com\/(.*?)\/status\/(.*))))/gi;
84
84
  var SPOTIFY_REGEX = /^https:\/\/open\.spotify\.com\/playlist\/(.*)?$/gi;
85
85
  var RUMBLE_REGEX = /^https:\/\/rumble.com\/embed\/([a-zA-Z0-9-]+)\/\?pub=\w+/;
@@ -287,15 +287,13 @@ function sanitizeHtml(html) {
287
287
  });
288
288
  }
289
289
  var proxyBase = "https://images.ecency.com";
290
- var fileExtension = true;
291
290
  function setProxyBase(p2) {
292
291
  proxyBase = p2;
293
- fileExtension = proxyBase == "https://images.ecency.com";
294
292
  }
295
293
  function extractPHash(url) {
296
294
  if (url.startsWith(`${proxyBase}/p/`)) {
297
295
  const [hash] = url.split("/p/")[1].split("?");
298
- return hash.replace(/.webp/, "").replace(/.png/, "");
296
+ return hash.replace(/\.(webp|png)$/, "");
299
297
  }
300
298
  return null;
301
299
  }
@@ -310,7 +308,7 @@ function getLatestUrl(str) {
310
308
  const [last] = [...str.replace(/https?:\/\//g, "\n$&").trim().split("\n")].reverse();
311
309
  return last;
312
310
  }
313
- function proxifyImageSrc(url, width = 0, height = 0, format = "match") {
311
+ function proxifyImageSrc(url, width = 0, height = 0, _format = "match") {
314
312
  if (!url || typeof url !== "string" || !isValidUrl(url)) {
315
313
  return "";
316
314
  }
@@ -323,7 +321,7 @@ function proxifyImageSrc(url, width = 0, height = 0, format = "match") {
323
321
  const realUrl = getLatestUrl(url);
324
322
  const pHash = extractPHash(realUrl);
325
323
  const options = {
326
- format,
324
+ format: "match",
327
325
  mode: "fit"
328
326
  };
329
327
  if (width > 0) {
@@ -334,31 +332,29 @@ function proxifyImageSrc(url, width = 0, height = 0, format = "match") {
334
332
  }
335
333
  const qs = querystring.stringify(options);
336
334
  if (pHash) {
337
- if (fileExtension) {
338
- return `${proxyBase}/p/${pHash}${format === "webp" ? ".webp" : ".png"}?${qs}`;
339
- } else {
340
- return `${proxyBase}/p/${pHash}?${qs}`;
341
- }
335
+ return `${proxyBase}/p/${pHash}?${qs}`;
342
336
  }
343
337
  const b58url = multihash.toB58String(Buffer.from(realUrl.toString()));
344
- return `${proxyBase}/p/${b58url}${fileExtension ? format === "webp" ? ".webp" : ".png" : ""}?${qs}`;
338
+ return `${proxyBase}/p/${b58url}?${qs}`;
345
339
  }
346
340
 
347
341
  // src/methods/img.method.ts
348
- function img(el, webp, state) {
349
- let src = el.getAttribute("src") || "";
342
+ function img(el, state) {
343
+ const src = el.getAttribute("src") || "";
350
344
  const decodedSrc = decodeURIComponent(
351
345
  src.replace(/&#(\d+);/g, (_, dec) => String.fromCharCode(dec)).replace(/&#x([0-9a-f]+);/gi, (_, hex) => String.fromCharCode(parseInt(hex, 16)))
352
346
  ).trim();
347
+ ["onerror", "dynsrc", "lowsrc", "width", "height"].forEach((attr) => el.removeAttribute(attr));
353
348
  const isInvalid = !src || decodedSrc.startsWith("javascript") || decodedSrc.startsWith("vbscript") || decodedSrc === "x";
354
349
  if (isInvalid) {
355
- src = "";
350
+ el.removeAttribute("src");
351
+ return;
356
352
  }
357
353
  const isRelative = !/^https?:\/\//i.test(decodedSrc) && !decodedSrc.startsWith("/");
358
354
  if (isRelative) {
359
- src = "";
355
+ el.removeAttribute("src");
356
+ return;
360
357
  }
361
- ["onerror", "dynsrc", "lowsrc", "width", "height"].forEach((attr) => el.removeAttribute(attr));
362
358
  el.setAttribute("itemprop", "image");
363
359
  const isLCP = state && !state.firstImageFound;
364
360
  if (isLCP) {
@@ -373,14 +369,17 @@ function img(el, webp, state) {
373
369
  const shouldReplace = !cls.includes("no-replace");
374
370
  const hasAlreadyProxied = src.startsWith("https://images.ecency.com");
375
371
  if (shouldReplace && !hasAlreadyProxied) {
376
- const proxified = proxifyImageSrc(src, 0, 0, webp ? "webp" : "match");
377
- el.setAttribute("src", proxified);
372
+ const proxified = proxifyImageSrc(decodedSrc);
373
+ if (proxified) {
374
+ el.setAttribute("src", proxified);
375
+ }
378
376
  }
379
377
  }
380
- function createImageHTML(src, isLCP, webp) {
378
+ function createImageHTML(src, isLCP) {
379
+ const proxified = proxifyImageSrc(src);
380
+ if (!proxified) return "";
381
381
  const loading = isLCP ? "eager" : "lazy";
382
382
  const fetch = isLCP ? 'fetchpriority="high"' : 'decoding="async"';
383
- const proxified = proxifyImageSrc(src, 0, 0, webp ? "webp" : "match");
384
383
  return `<img
385
384
  class="markdown-img-link"
386
385
  src="${proxified}"
@@ -432,7 +431,7 @@ var addLineBreakBeforePostLink = (el, forApp, isInline) => {
432
431
  el.parentNode.insertBefore(br, el);
433
432
  }
434
433
  };
435
- function a(el, forApp, webp, parentDomain = "ecency.com", seoContext) {
434
+ function a(el, forApp, parentDomain = "ecency.com", seoContext) {
436
435
  if (!el || !el.parentNode) {
437
436
  return;
438
437
  }
@@ -450,10 +449,10 @@ function a(el, forApp, webp, parentDomain = "ecency.com", seoContext) {
450
449
  }
451
450
  if (href.match(IMG_REGEX) && href.trim().replace(/&amp;/g, "&") === getSerializedInnerHTML(el).trim().replace(/&amp;/g, "&")) {
452
451
  const isLCP = false;
453
- const imgHTML = createImageHTML(href, isLCP, webp);
452
+ const imgHTML = createImageHTML(href, isLCP);
454
453
  const doc = DOMParser.parseFromString(imgHTML, "text/html");
455
454
  const replaceNode = doc.body?.firstChild || doc.firstChild;
456
- if (replaceNode) {
455
+ if (replaceNode && el.parentNode) {
457
456
  const importedNode = el.ownerDocument.importNode(replaceNode, true);
458
457
  el.parentNode.replaceChild(importedNode, el);
459
458
  }
@@ -772,7 +771,7 @@ function a(el, forApp, webp, parentDomain = "ecency.com", seoContext) {
772
771
  el.setAttribute("class", "markdown-video-link markdown-video-link-youtube");
773
772
  el.removeAttribute("href");
774
773
  const vid = match[1];
775
- const thumbnail = proxifyImageSrc(`https://img.youtube.com/vi/${vid.split("?")[0]}/hqdefault.jpg`, 0, 0, webp ? "webp" : "match");
774
+ const thumbnail = proxifyImageSrc(`https://img.youtube.com/vi/${vid.split("?")[0]}/hqdefault.jpg`, 0, 0, "match");
776
775
  const embedSrc = `https://www.youtube.com/embed/${vid}?autoplay=1`;
777
776
  el.textContent = "";
778
777
  el.setAttribute("data-embed-src", embedSrc);
@@ -868,7 +867,7 @@ function a(el, forApp, webp, parentDomain = "ecency.com", seoContext) {
868
867
  if (imgEls.length === 1) {
869
868
  const src = imgEls[0].getAttribute("src");
870
869
  if (src) {
871
- const thumbnail = proxifyImageSrc(src.replace(/\s+/g, ""), 0, 0, webp ? "webp" : "match");
870
+ const thumbnail = proxifyImageSrc(src.replace(/\s+/g, ""), 0, 0, "match");
872
871
  const thumbImg = el.ownerDocument.createElement("img");
873
872
  thumbImg.setAttribute("class", "no-replace video-thumbnail");
874
873
  thumbImg.setAttribute("itemprop", "thumbnailUrl");
@@ -903,7 +902,7 @@ function a(el, forApp, webp, parentDomain = "ecency.com", seoContext) {
903
902
  const imgEls2 = el.getElementsByTagName("img");
904
903
  if (imgEls2.length === 1 || el.textContent.trim() === href) {
905
904
  if ((match[1] || match[2]) && match[3]) {
906
- const videoHref = `https://play.3speak.tv/watch?v=${match[3]}&mode=iframe`;
905
+ const videoHref = `https://play.3speak.tv/embed?v=${match[3]}&mode=iframe`;
907
906
  el.setAttribute("class", "markdown-video-link markdown-video-link-speak");
908
907
  el.removeAttribute("href");
909
908
  el.setAttribute("data-embed-src", videoHref);
@@ -913,7 +912,7 @@ function a(el, forApp, webp, parentDomain = "ecency.com", seoContext) {
913
912
  if (imgEls2.length === 1) {
914
913
  const src = imgEls2[0].getAttribute("src");
915
914
  if (src) {
916
- const thumbnail = proxifyImageSrc(src.replace(/\s+/g, ""), 0, 0, webp ? "webp" : "match");
915
+ const thumbnail = proxifyImageSrc(src.replace(/\s+/g, ""), 0, 0, "match");
917
916
  const thumbImg = el.ownerDocument.createElement("img");
918
917
  thumbImg.setAttribute("class", "no-replace video-thumbnail");
919
918
  thumbImg.setAttribute("itemprop", "thumbnailUrl");
@@ -947,7 +946,9 @@ function a(el, forApp, webp, parentDomain = "ecency.com", seoContext) {
947
946
  blockquote.appendChild(p2);
948
947
  blockquote.appendChild(textNode);
949
948
  blockquote.appendChild(a2);
950
- el.parentNode.replaceChild(blockquote, el);
949
+ if (el.parentNode) {
950
+ el.parentNode.replaceChild(blockquote, el);
951
+ }
951
952
  return;
952
953
  }
953
954
  }
@@ -1036,7 +1037,8 @@ function iframe(el, parentDomain = "ecency.com") {
1036
1037
  return;
1037
1038
  }
1038
1039
  if (src.match(SPEAK_EMBED_REGEX)) {
1039
- let normalizedSrc = src.replace(/3speak\.[a-z]+/i, "play.3speak.tv");
1040
+ let normalizedSrc = src.replace(/(?:play\.)?3speak\.[a-z]+/i, "play.3speak.tv");
1041
+ normalizedSrc = normalizedSrc.replace(/\/watch\?/, "/embed?");
1040
1042
  const hasMode = /[?&]mode=/.test(normalizedSrc);
1041
1043
  if (!hasMode) {
1042
1044
  normalizedSrc = `${normalizedSrc}&mode=iframe`;
@@ -1044,6 +1046,7 @@ function iframe(el, parentDomain = "ecency.com") {
1044
1046
  const hasAutoplay = /[?&]autoplay=/.test(normalizedSrc);
1045
1047
  const s = hasAutoplay ? normalizedSrc : `${normalizedSrc}&autoplay=true`;
1046
1048
  el.setAttribute("src", s);
1049
+ el.setAttribute("class", "speak-iframe");
1047
1050
  return;
1048
1051
  }
1049
1052
  if (src.match(SPOTIFY_EMBED_REGEX)) {
@@ -1152,7 +1155,7 @@ function p(el) {
1152
1155
  }
1153
1156
 
1154
1157
  // src/methods/linkify.method.ts
1155
- function linkify(content, forApp, webp) {
1158
+ function linkify(content, forApp) {
1156
1159
  content = content.replace(/(^|\s|>)(#[-a-z\d]+)/gi, (tag) => {
1157
1160
  if (/#[\d]+$/.test(tag)) return tag;
1158
1161
  const preceding = /^\s|>/.test(tag) ? tag[0] : "";
@@ -1194,21 +1197,21 @@ function linkify(content, forApp, webp) {
1194
1197
  content = content.replace(IMG_REGEX, (imglink) => {
1195
1198
  const isLCP = !firstImageUsed;
1196
1199
  firstImageUsed = true;
1197
- return createImageHTML(imglink, isLCP, webp);
1200
+ return createImageHTML(imglink, isLCP);
1198
1201
  });
1199
1202
  return content;
1200
1203
  }
1201
1204
 
1202
1205
  // src/methods/text.method.ts
1203
- function text(node, forApp, webp) {
1206
+ function text(node, forApp) {
1204
1207
  if (!node || !node.parentNode) {
1205
1208
  return;
1206
1209
  }
1207
- if (node.parentNode && ["a", "code"].includes(node.parentNode.nodeName.toLowerCase())) {
1210
+ if (["a", "code"].includes(node.parentNode.nodeName.toLowerCase())) {
1208
1211
  return;
1209
1212
  }
1210
1213
  const nodeValue = node.nodeValue || "";
1211
- const linkified = linkify(nodeValue, forApp, webp);
1214
+ const linkified = linkify(nodeValue, forApp);
1212
1215
  if (linkified !== nodeValue) {
1213
1216
  const doc = DOMParser.parseFromString(
1214
1217
  `<span class="wr">${linkified}</span>`,
@@ -1223,7 +1226,7 @@ function text(node, forApp, webp) {
1223
1226
  }
1224
1227
  if (nodeValue.match(IMG_REGEX)) {
1225
1228
  const isLCP = false;
1226
- const imageHTML = createImageHTML(nodeValue, isLCP, webp);
1229
+ const imageHTML = createImageHTML(nodeValue, isLCP);
1227
1230
  const doc = DOMParser.parseFromString(imageHTML, "text/html");
1228
1231
  const replaceNode = doc.body?.firstChild || doc.firstChild;
1229
1232
  if (replaceNode) {
@@ -1235,7 +1238,7 @@ function text(node, forApp, webp) {
1235
1238
  const e = YOUTUBE_REGEX.exec(nodeValue);
1236
1239
  if (e && e[1]) {
1237
1240
  const vid = e[1];
1238
- const thumbnail = proxifyImageSrc(`https://img.youtube.com/vi/${vid.split("?")[0]}/hqdefault.jpg`, 0, 0, webp ? "webp" : "match");
1241
+ const thumbnail = proxifyImageSrc(`https://img.youtube.com/vi/${vid.split("?")[0]}/hqdefault.jpg`, 0, 0, "match");
1239
1242
  const embedSrc = `https://www.youtube.com/embed/${vid}?autoplay=1`;
1240
1243
  const startTime = extractYtStartTime(nodeValue);
1241
1244
  const container = node.ownerDocument.createElement("p");
@@ -1281,7 +1284,7 @@ function text(node, forApp, webp) {
1281
1284
  }
1282
1285
 
1283
1286
  // src/methods/traverse.method.ts
1284
- function traverse(node, forApp, depth = 0, webp = false, state = { firstImageFound: false }, parentDomain = "ecency.com", seoContext) {
1287
+ function traverse(node, forApp, depth = 0, state = { firstImageFound: false }, parentDomain = "ecency.com", seoContext) {
1285
1288
  if (!node || !node.childNodes) {
1286
1289
  return;
1287
1290
  }
@@ -1289,23 +1292,23 @@ function traverse(node, forApp, depth = 0, webp = false, state = { firstImageFou
1289
1292
  const child = node.childNodes[i];
1290
1293
  if (!child) return;
1291
1294
  if (child.nodeName.toLowerCase() === "a") {
1292
- a(child, forApp, webp, parentDomain, seoContext);
1295
+ a(child, forApp, parentDomain, seoContext);
1293
1296
  }
1294
1297
  if (child.nodeName.toLowerCase() === "iframe") {
1295
1298
  iframe(child, parentDomain);
1296
1299
  }
1297
1300
  if (child.nodeName === "#text") {
1298
- text(child, forApp, webp);
1301
+ text(child, forApp);
1299
1302
  }
1300
1303
  if (child.nodeName.toLowerCase() === "img") {
1301
- img(child, webp, state);
1304
+ img(child, state);
1302
1305
  }
1303
1306
  if (child.nodeName.toLowerCase() === "p") {
1304
1307
  p(child);
1305
1308
  }
1306
1309
  const currentChild = node.childNodes[i];
1307
1310
  if (currentChild) {
1308
- traverse(currentChild, forApp, depth + 1, webp, state, parentDomain, seoContext);
1311
+ traverse(currentChild, forApp, depth + 1, state, parentDomain, seoContext);
1309
1312
  }
1310
1313
  });
1311
1314
  }
@@ -1356,7 +1359,7 @@ function fixBlockLevelTagsInParagraphs(html) {
1356
1359
  html = html.replace(/<p><br>\s*<\/p>/g, "");
1357
1360
  return html;
1358
1361
  }
1359
- function markdownToHTML(input, forApp, webp, parentDomain = "ecency.com", seoContext) {
1362
+ function markdownToHTML(input, forApp, parentDomain = "ecency.com", seoContext) {
1360
1363
  input = input.replace(new RegExp("https://leofinance.io/threads/view/", "g"), "/@");
1361
1364
  input = input.replace(new RegExp("https://leofinance.io/posts/", "g"), "/@");
1362
1365
  input = input.replace(new RegExp("https://leofinance.io/threads/", "g"), "/@");
@@ -1416,7 +1419,7 @@ function markdownToHTML(input, forApp, webp, parentDomain = "ecency.com", seoCon
1416
1419
  output = md.render(input);
1417
1420
  output = fixBlockLevelTagsInParagraphs(output);
1418
1421
  const doc = DOMParser.parseFromString(`<body id="root">${removeDuplicateAttributes(output)}</body>`, "text/html");
1419
- traverse(doc, forApp, 0, webp, { firstImageFound: false }, parentDomain, seoContext);
1422
+ traverse(doc, forApp, 0, { firstImageFound: false }, parentDomain, seoContext);
1420
1423
  output = serializer.serializeToString(doc);
1421
1424
  } catch (error) {
1422
1425
  try {
@@ -1429,7 +1432,7 @@ function markdownToHTML(input, forApp, webp, parentDomain = "ecency.com", seoCon
1429
1432
  });
1430
1433
  const repairedHtml = domSerializer(dom.children);
1431
1434
  const doc = DOMParser.parseFromString(`<body id="root">${removeDuplicateAttributes(repairedHtml)}</body>`, "text/html");
1432
- traverse(doc, forApp, 0, webp, { firstImageFound: false }, parentDomain, seoContext);
1435
+ traverse(doc, forApp, 0, { firstImageFound: false }, parentDomain, seoContext);
1433
1436
  output = serializer.serializeToString(doc);
1434
1437
  } catch (fallbackError) {
1435
1438
  const escapedContent = he2.encode(output || md.render(input));
@@ -1457,18 +1460,18 @@ function cacheSet(key, value) {
1457
1460
  }
1458
1461
 
1459
1462
  // src/markdown-2-html.ts
1460
- function markdown2Html(obj, forApp = true, webp = false, parentDomain = "ecency.com", seoContext) {
1463
+ function markdown2Html(obj, forApp = true, _webp = false, parentDomain = "ecency.com", seoContext) {
1461
1464
  if (typeof obj === "string") {
1462
1465
  const cleanedStr = cleanReply(obj);
1463
- return markdownToHTML(cleanedStr, forApp, webp, parentDomain, seoContext);
1466
+ return markdownToHTML(cleanedStr, forApp, parentDomain, seoContext);
1464
1467
  }
1465
- const key = `${makeEntryCacheKey(obj)}-md${webp ? "-webp" : ""}-${forApp ? "app" : "site"}-${parentDomain}${seoContext ? `-seo${seoContext.authorReputation ?? ""}-${seoContext.postPayout ?? ""}` : ""}`;
1468
+ const key = `${makeEntryCacheKey(obj)}-md-${forApp ? "app" : "site"}-${parentDomain}${seoContext ? `-seo${seoContext.authorReputation ?? ""}-${seoContext.postPayout ?? ""}` : ""}`;
1466
1469
  const item = cacheGet(key);
1467
1470
  if (item) {
1468
1471
  return item;
1469
1472
  }
1470
1473
  const cleanBody = cleanReply(obj.body);
1471
- const res = markdownToHTML(cleanBody, forApp, webp, parentDomain, seoContext);
1474
+ const res = markdownToHTML(cleanBody, forApp, parentDomain, seoContext);
1472
1475
  cacheSet(key, res);
1473
1476
  return res;
1474
1477
  }