@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.
@@ -107,8 +107,8 @@ var LBRY_REGEX = /^(https?:)?\/\/lbry.tv\/\$\/embed\/[^?#]+(?:$|[?#])/i;
107
107
  var ODYSEE_REGEX = /^(https?:)?\/\/odysee\.com\/(?:\$|%24)\/embed\/[^/?#]+(?:$|[?#])/i;
108
108
  var SKATEHIVE_IPFS_REGEX = /^https?:\/\/ipfs\.skatehive\.app\/ipfs\/([^/?#]+)/i;
109
109
  var ARCH_REGEX = /^(https?:)?\/\/archive.org\/embed\/[^/?#]+(?:$|[?#])/i;
110
- var SPEAK_REGEX = /(?:https?:\/\/(?:(?:play\.)?3speak.([a-z]+)\/watch\?v=)|(?:(?:play\.)?3speak.([a-z]+)\/embed\?v=))([A-Za-z0-9\_\-\.\/]+)(&.*)?/i;
111
- var SPEAK_EMBED_REGEX = /^(https?:)?\/\/(?:play\.)?3speak.([a-z]+)\/embed\?[^/]+$/i;
110
+ var SPEAK_REGEX = /(?:https?:\/\/(?:(?:play\.)?3speak\.([a-z]+)\/watch\?v=)|(?:(?:play\.)?3speak\.([a-z]+)\/embed\?v=))([A-Za-z0-9_\-\.\/]+)(&.*)?/i;
111
+ var SPEAK_EMBED_REGEX = /^(https?:)?\/\/(?:play\.)?3speak\.([a-z]+)\/(?:embed|watch)\?.+$/i;
112
112
  var TWITTER_REGEX = /(?:https?:\/\/(?:(?:twitter\.com\/(.*?)\/status\/(.*))))/gi;
113
113
  var SPOTIFY_REGEX = /^https:\/\/open\.spotify\.com\/playlist\/(.*)?$/gi;
114
114
  var RUMBLE_REGEX = /^https:\/\/rumble.com\/embed\/([a-zA-Z0-9-]+)\/\?pub=\w+/;
@@ -316,15 +316,13 @@ function sanitizeHtml(html) {
316
316
  });
317
317
  }
318
318
  var proxyBase = "https://images.ecency.com";
319
- var fileExtension = true;
320
319
  function setProxyBase(p2) {
321
320
  proxyBase = p2;
322
- fileExtension = proxyBase == "https://images.ecency.com";
323
321
  }
324
322
  function extractPHash(url) {
325
323
  if (url.startsWith(`${proxyBase}/p/`)) {
326
324
  const [hash] = url.split("/p/")[1].split("?");
327
- return hash.replace(/.webp/, "").replace(/.png/, "");
325
+ return hash.replace(/\.(webp|png)$/, "");
328
326
  }
329
327
  return null;
330
328
  }
@@ -339,7 +337,7 @@ function getLatestUrl(str) {
339
337
  const [last] = [...str.replace(/https?:\/\//g, "\n$&").trim().split("\n")].reverse();
340
338
  return last;
341
339
  }
342
- function proxifyImageSrc(url, width = 0, height = 0, format = "match") {
340
+ function proxifyImageSrc(url, width = 0, height = 0, _format = "match") {
343
341
  if (!url || typeof url !== "string" || !isValidUrl(url)) {
344
342
  return "";
345
343
  }
@@ -352,7 +350,7 @@ function proxifyImageSrc(url, width = 0, height = 0, format = "match") {
352
350
  const realUrl = getLatestUrl(url);
353
351
  const pHash = extractPHash(realUrl);
354
352
  const options = {
355
- format,
353
+ format: "match",
356
354
  mode: "fit"
357
355
  };
358
356
  if (width > 0) {
@@ -363,31 +361,29 @@ function proxifyImageSrc(url, width = 0, height = 0, format = "match") {
363
361
  }
364
362
  const qs = querystring__default.default.stringify(options);
365
363
  if (pHash) {
366
- if (fileExtension) {
367
- return `${proxyBase}/p/${pHash}${format === "webp" ? ".webp" : ".png"}?${qs}`;
368
- } else {
369
- return `${proxyBase}/p/${pHash}?${qs}`;
370
- }
364
+ return `${proxyBase}/p/${pHash}?${qs}`;
371
365
  }
372
366
  const b58url = multihash__default.default.toB58String(Buffer.from(realUrl.toString()));
373
- return `${proxyBase}/p/${b58url}${fileExtension ? format === "webp" ? ".webp" : ".png" : ""}?${qs}`;
367
+ return `${proxyBase}/p/${b58url}?${qs}`;
374
368
  }
375
369
 
376
370
  // src/methods/img.method.ts
377
- function img(el, webp, state) {
378
- let src = el.getAttribute("src") || "";
371
+ function img(el, state) {
372
+ const src = el.getAttribute("src") || "";
379
373
  const decodedSrc = decodeURIComponent(
380
374
  src.replace(/&#(\d+);/g, (_, dec) => String.fromCharCode(dec)).replace(/&#x([0-9a-f]+);/gi, (_, hex) => String.fromCharCode(parseInt(hex, 16)))
381
375
  ).trim();
376
+ ["onerror", "dynsrc", "lowsrc", "width", "height"].forEach((attr) => el.removeAttribute(attr));
382
377
  const isInvalid = !src || decodedSrc.startsWith("javascript") || decodedSrc.startsWith("vbscript") || decodedSrc === "x";
383
378
  if (isInvalid) {
384
- src = "";
379
+ el.removeAttribute("src");
380
+ return;
385
381
  }
386
382
  const isRelative = !/^https?:\/\//i.test(decodedSrc) && !decodedSrc.startsWith("/");
387
383
  if (isRelative) {
388
- src = "";
384
+ el.removeAttribute("src");
385
+ return;
389
386
  }
390
- ["onerror", "dynsrc", "lowsrc", "width", "height"].forEach((attr) => el.removeAttribute(attr));
391
387
  el.setAttribute("itemprop", "image");
392
388
  const isLCP = state && !state.firstImageFound;
393
389
  if (isLCP) {
@@ -402,14 +398,17 @@ function img(el, webp, state) {
402
398
  const shouldReplace = !cls.includes("no-replace");
403
399
  const hasAlreadyProxied = src.startsWith("https://images.ecency.com");
404
400
  if (shouldReplace && !hasAlreadyProxied) {
405
- const proxified = proxifyImageSrc(src, 0, 0, webp ? "webp" : "match");
406
- el.setAttribute("src", proxified);
401
+ const proxified = proxifyImageSrc(decodedSrc);
402
+ if (proxified) {
403
+ el.setAttribute("src", proxified);
404
+ }
407
405
  }
408
406
  }
409
- function createImageHTML(src, isLCP, webp) {
407
+ function createImageHTML(src, isLCP) {
408
+ const proxified = proxifyImageSrc(src);
409
+ if (!proxified) return "";
410
410
  const loading = isLCP ? "eager" : "lazy";
411
411
  const fetch = isLCP ? 'fetchpriority="high"' : 'decoding="async"';
412
- const proxified = proxifyImageSrc(src, 0, 0, webp ? "webp" : "match");
413
412
  return `<img
414
413
  class="markdown-img-link"
415
414
  src="${proxified}"
@@ -461,7 +460,7 @@ var addLineBreakBeforePostLink = (el, forApp, isInline) => {
461
460
  el.parentNode.insertBefore(br, el);
462
461
  }
463
462
  };
464
- function a(el, forApp, webp, parentDomain = "ecency.com", seoContext) {
463
+ function a(el, forApp, parentDomain = "ecency.com", seoContext) {
465
464
  if (!el || !el.parentNode) {
466
465
  return;
467
466
  }
@@ -479,10 +478,10 @@ function a(el, forApp, webp, parentDomain = "ecency.com", seoContext) {
479
478
  }
480
479
  if (href.match(IMG_REGEX) && href.trim().replace(/&amp;/g, "&") === getSerializedInnerHTML(el).trim().replace(/&amp;/g, "&")) {
481
480
  const isLCP = false;
482
- const imgHTML = createImageHTML(href, isLCP, webp);
481
+ const imgHTML = createImageHTML(href, isLCP);
483
482
  const doc = DOMParser.parseFromString(imgHTML, "text/html");
484
483
  const replaceNode = doc.body?.firstChild || doc.firstChild;
485
- if (replaceNode) {
484
+ if (replaceNode && el.parentNode) {
486
485
  const importedNode = el.ownerDocument.importNode(replaceNode, true);
487
486
  el.parentNode.replaceChild(importedNode, el);
488
487
  }
@@ -801,7 +800,7 @@ function a(el, forApp, webp, parentDomain = "ecency.com", seoContext) {
801
800
  el.setAttribute("class", "markdown-video-link markdown-video-link-youtube");
802
801
  el.removeAttribute("href");
803
802
  const vid = match[1];
804
- const thumbnail = proxifyImageSrc(`https://img.youtube.com/vi/${vid.split("?")[0]}/hqdefault.jpg`, 0, 0, webp ? "webp" : "match");
803
+ const thumbnail = proxifyImageSrc(`https://img.youtube.com/vi/${vid.split("?")[0]}/hqdefault.jpg`, 0, 0, "match");
805
804
  const embedSrc = `https://www.youtube.com/embed/${vid}?autoplay=1`;
806
805
  el.textContent = "";
807
806
  el.setAttribute("data-embed-src", embedSrc);
@@ -897,7 +896,7 @@ function a(el, forApp, webp, parentDomain = "ecency.com", seoContext) {
897
896
  if (imgEls.length === 1) {
898
897
  const src = imgEls[0].getAttribute("src");
899
898
  if (src) {
900
- const thumbnail = proxifyImageSrc(src.replace(/\s+/g, ""), 0, 0, webp ? "webp" : "match");
899
+ const thumbnail = proxifyImageSrc(src.replace(/\s+/g, ""), 0, 0, "match");
901
900
  const thumbImg = el.ownerDocument.createElement("img");
902
901
  thumbImg.setAttribute("class", "no-replace video-thumbnail");
903
902
  thumbImg.setAttribute("itemprop", "thumbnailUrl");
@@ -932,7 +931,7 @@ function a(el, forApp, webp, parentDomain = "ecency.com", seoContext) {
932
931
  const imgEls2 = el.getElementsByTagName("img");
933
932
  if (imgEls2.length === 1 || el.textContent.trim() === href) {
934
933
  if ((match[1] || match[2]) && match[3]) {
935
- const videoHref = `https://play.3speak.tv/watch?v=${match[3]}&mode=iframe`;
934
+ const videoHref = `https://play.3speak.tv/embed?v=${match[3]}&mode=iframe`;
936
935
  el.setAttribute("class", "markdown-video-link markdown-video-link-speak");
937
936
  el.removeAttribute("href");
938
937
  el.setAttribute("data-embed-src", videoHref);
@@ -942,7 +941,7 @@ function a(el, forApp, webp, parentDomain = "ecency.com", seoContext) {
942
941
  if (imgEls2.length === 1) {
943
942
  const src = imgEls2[0].getAttribute("src");
944
943
  if (src) {
945
- const thumbnail = proxifyImageSrc(src.replace(/\s+/g, ""), 0, 0, webp ? "webp" : "match");
944
+ const thumbnail = proxifyImageSrc(src.replace(/\s+/g, ""), 0, 0, "match");
946
945
  const thumbImg = el.ownerDocument.createElement("img");
947
946
  thumbImg.setAttribute("class", "no-replace video-thumbnail");
948
947
  thumbImg.setAttribute("itemprop", "thumbnailUrl");
@@ -976,7 +975,9 @@ function a(el, forApp, webp, parentDomain = "ecency.com", seoContext) {
976
975
  blockquote.appendChild(p2);
977
976
  blockquote.appendChild(textNode);
978
977
  blockquote.appendChild(a2);
979
- el.parentNode.replaceChild(blockquote, el);
978
+ if (el.parentNode) {
979
+ el.parentNode.replaceChild(blockquote, el);
980
+ }
980
981
  return;
981
982
  }
982
983
  }
@@ -1065,7 +1066,8 @@ function iframe(el, parentDomain = "ecency.com") {
1065
1066
  return;
1066
1067
  }
1067
1068
  if (src.match(SPEAK_EMBED_REGEX)) {
1068
- let normalizedSrc = src.replace(/3speak\.[a-z]+/i, "play.3speak.tv");
1069
+ let normalizedSrc = src.replace(/(?:play\.)?3speak\.[a-z]+/i, "play.3speak.tv");
1070
+ normalizedSrc = normalizedSrc.replace(/\/watch\?/, "/embed?");
1069
1071
  const hasMode = /[?&]mode=/.test(normalizedSrc);
1070
1072
  if (!hasMode) {
1071
1073
  normalizedSrc = `${normalizedSrc}&mode=iframe`;
@@ -1073,6 +1075,7 @@ function iframe(el, parentDomain = "ecency.com") {
1073
1075
  const hasAutoplay = /[?&]autoplay=/.test(normalizedSrc);
1074
1076
  const s = hasAutoplay ? normalizedSrc : `${normalizedSrc}&autoplay=true`;
1075
1077
  el.setAttribute("src", s);
1078
+ el.setAttribute("class", "speak-iframe");
1076
1079
  return;
1077
1080
  }
1078
1081
  if (src.match(SPOTIFY_EMBED_REGEX)) {
@@ -1181,7 +1184,7 @@ function p(el) {
1181
1184
  }
1182
1185
 
1183
1186
  // src/methods/linkify.method.ts
1184
- function linkify(content, forApp, webp) {
1187
+ function linkify(content, forApp) {
1185
1188
  content = content.replace(/(^|\s|>)(#[-a-z\d]+)/gi, (tag) => {
1186
1189
  if (/#[\d]+$/.test(tag)) return tag;
1187
1190
  const preceding = /^\s|>/.test(tag) ? tag[0] : "";
@@ -1223,21 +1226,21 @@ function linkify(content, forApp, webp) {
1223
1226
  content = content.replace(IMG_REGEX, (imglink) => {
1224
1227
  const isLCP = !firstImageUsed;
1225
1228
  firstImageUsed = true;
1226
- return createImageHTML(imglink, isLCP, webp);
1229
+ return createImageHTML(imglink, isLCP);
1227
1230
  });
1228
1231
  return content;
1229
1232
  }
1230
1233
 
1231
1234
  // src/methods/text.method.ts
1232
- function text(node, forApp, webp) {
1235
+ function text(node, forApp) {
1233
1236
  if (!node || !node.parentNode) {
1234
1237
  return;
1235
1238
  }
1236
- if (node.parentNode && ["a", "code"].includes(node.parentNode.nodeName.toLowerCase())) {
1239
+ if (["a", "code"].includes(node.parentNode.nodeName.toLowerCase())) {
1237
1240
  return;
1238
1241
  }
1239
1242
  const nodeValue = node.nodeValue || "";
1240
- const linkified = linkify(nodeValue, forApp, webp);
1243
+ const linkified = linkify(nodeValue, forApp);
1241
1244
  if (linkified !== nodeValue) {
1242
1245
  const doc = DOMParser.parseFromString(
1243
1246
  `<span class="wr">${linkified}</span>`,
@@ -1252,7 +1255,7 @@ function text(node, forApp, webp) {
1252
1255
  }
1253
1256
  if (nodeValue.match(IMG_REGEX)) {
1254
1257
  const isLCP = false;
1255
- const imageHTML = createImageHTML(nodeValue, isLCP, webp);
1258
+ const imageHTML = createImageHTML(nodeValue, isLCP);
1256
1259
  const doc = DOMParser.parseFromString(imageHTML, "text/html");
1257
1260
  const replaceNode = doc.body?.firstChild || doc.firstChild;
1258
1261
  if (replaceNode) {
@@ -1264,7 +1267,7 @@ function text(node, forApp, webp) {
1264
1267
  const e = YOUTUBE_REGEX.exec(nodeValue);
1265
1268
  if (e && e[1]) {
1266
1269
  const vid = e[1];
1267
- const thumbnail = proxifyImageSrc(`https://img.youtube.com/vi/${vid.split("?")[0]}/hqdefault.jpg`, 0, 0, webp ? "webp" : "match");
1270
+ const thumbnail = proxifyImageSrc(`https://img.youtube.com/vi/${vid.split("?")[0]}/hqdefault.jpg`, 0, 0, "match");
1268
1271
  const embedSrc = `https://www.youtube.com/embed/${vid}?autoplay=1`;
1269
1272
  const startTime = extractYtStartTime(nodeValue);
1270
1273
  const container = node.ownerDocument.createElement("p");
@@ -1310,7 +1313,7 @@ function text(node, forApp, webp) {
1310
1313
  }
1311
1314
 
1312
1315
  // src/methods/traverse.method.ts
1313
- function traverse(node, forApp, depth = 0, webp = false, state = { firstImageFound: false }, parentDomain = "ecency.com", seoContext) {
1316
+ function traverse(node, forApp, depth = 0, state = { firstImageFound: false }, parentDomain = "ecency.com", seoContext) {
1314
1317
  if (!node || !node.childNodes) {
1315
1318
  return;
1316
1319
  }
@@ -1318,23 +1321,23 @@ function traverse(node, forApp, depth = 0, webp = false, state = { firstImageFou
1318
1321
  const child = node.childNodes[i];
1319
1322
  if (!child) return;
1320
1323
  if (child.nodeName.toLowerCase() === "a") {
1321
- a(child, forApp, webp, parentDomain, seoContext);
1324
+ a(child, forApp, parentDomain, seoContext);
1322
1325
  }
1323
1326
  if (child.nodeName.toLowerCase() === "iframe") {
1324
1327
  iframe(child, parentDomain);
1325
1328
  }
1326
1329
  if (child.nodeName === "#text") {
1327
- text(child, forApp, webp);
1330
+ text(child, forApp);
1328
1331
  }
1329
1332
  if (child.nodeName.toLowerCase() === "img") {
1330
- img(child, webp, state);
1333
+ img(child, state);
1331
1334
  }
1332
1335
  if (child.nodeName.toLowerCase() === "p") {
1333
1336
  p(child);
1334
1337
  }
1335
1338
  const currentChild = node.childNodes[i];
1336
1339
  if (currentChild) {
1337
- traverse(currentChild, forApp, depth + 1, webp, state, parentDomain, seoContext);
1340
+ traverse(currentChild, forApp, depth + 1, state, parentDomain, seoContext);
1338
1341
  }
1339
1342
  });
1340
1343
  }
@@ -1385,7 +1388,7 @@ function fixBlockLevelTagsInParagraphs(html) {
1385
1388
  html = html.replace(/<p><br>\s*<\/p>/g, "");
1386
1389
  return html;
1387
1390
  }
1388
- function markdownToHTML(input, forApp, webp, parentDomain = "ecency.com", seoContext) {
1391
+ function markdownToHTML(input, forApp, parentDomain = "ecency.com", seoContext) {
1389
1392
  input = input.replace(new RegExp("https://leofinance.io/threads/view/", "g"), "/@");
1390
1393
  input = input.replace(new RegExp("https://leofinance.io/posts/", "g"), "/@");
1391
1394
  input = input.replace(new RegExp("https://leofinance.io/threads/", "g"), "/@");
@@ -1445,7 +1448,7 @@ function markdownToHTML(input, forApp, webp, parentDomain = "ecency.com", seoCon
1445
1448
  output = md.render(input);
1446
1449
  output = fixBlockLevelTagsInParagraphs(output);
1447
1450
  const doc = DOMParser.parseFromString(`<body id="root">${removeDuplicateAttributes(output)}</body>`, "text/html");
1448
- traverse(doc, forApp, 0, webp, { firstImageFound: false }, parentDomain, seoContext);
1451
+ traverse(doc, forApp, 0, { firstImageFound: false }, parentDomain, seoContext);
1449
1452
  output = serializer.serializeToString(doc);
1450
1453
  } catch (error) {
1451
1454
  try {
@@ -1458,7 +1461,7 @@ function markdownToHTML(input, forApp, webp, parentDomain = "ecency.com", seoCon
1458
1461
  });
1459
1462
  const repairedHtml = domSerializer(dom.children);
1460
1463
  const doc = DOMParser.parseFromString(`<body id="root">${removeDuplicateAttributes(repairedHtml)}</body>`, "text/html");
1461
- traverse(doc, forApp, 0, webp, { firstImageFound: false }, parentDomain, seoContext);
1464
+ traverse(doc, forApp, 0, { firstImageFound: false }, parentDomain, seoContext);
1462
1465
  output = serializer.serializeToString(doc);
1463
1466
  } catch (fallbackError) {
1464
1467
  const escapedContent = he2__default.default.encode(output || md.render(input));
@@ -1486,18 +1489,18 @@ function cacheSet(key, value) {
1486
1489
  }
1487
1490
 
1488
1491
  // src/markdown-2-html.ts
1489
- function markdown2Html(obj, forApp = true, webp = false, parentDomain = "ecency.com", seoContext) {
1492
+ function markdown2Html(obj, forApp = true, _webp = false, parentDomain = "ecency.com", seoContext) {
1490
1493
  if (typeof obj === "string") {
1491
1494
  const cleanedStr = cleanReply(obj);
1492
- return markdownToHTML(cleanedStr, forApp, webp, parentDomain, seoContext);
1495
+ return markdownToHTML(cleanedStr, forApp, parentDomain, seoContext);
1493
1496
  }
1494
- const key = `${makeEntryCacheKey(obj)}-md${webp ? "-webp" : ""}-${forApp ? "app" : "site"}-${parentDomain}${seoContext ? `-seo${seoContext.authorReputation ?? ""}-${seoContext.postPayout ?? ""}` : ""}`;
1497
+ const key = `${makeEntryCacheKey(obj)}-md-${forApp ? "app" : "site"}-${parentDomain}${seoContext ? `-seo${seoContext.authorReputation ?? ""}-${seoContext.postPayout ?? ""}` : ""}`;
1495
1498
  const item = cacheGet(key);
1496
1499
  if (item) {
1497
1500
  return item;
1498
1501
  }
1499
1502
  const cleanBody = cleanReply(obj.body);
1500
- const res = markdownToHTML(cleanBody, forApp, webp, parentDomain, seoContext);
1503
+ const res = markdownToHTML(cleanBody, forApp, parentDomain, seoContext);
1501
1504
  cacheSet(key, res);
1502
1505
  return res;
1503
1506
  }