@ecency/render-helper 2.4.15 → 2.4.16

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,7 +449,7 @@ 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
455
  if (replaceNode) {
@@ -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");
@@ -1036,7 +1035,8 @@ function iframe(el, parentDomain = "ecency.com") {
1036
1035
  return;
1037
1036
  }
1038
1037
  if (src.match(SPEAK_EMBED_REGEX)) {
1039
- let normalizedSrc = src.replace(/3speak\.[a-z]+/i, "play.3speak.tv");
1038
+ let normalizedSrc = src.replace(/(?:play\.)?3speak\.[a-z]+/i, "play.3speak.tv");
1039
+ normalizedSrc = normalizedSrc.replace(/\/watch\?/, "/embed?");
1040
1040
  const hasMode = /[?&]mode=/.test(normalizedSrc);
1041
1041
  if (!hasMode) {
1042
1042
  normalizedSrc = `${normalizedSrc}&mode=iframe`;
@@ -1044,6 +1044,7 @@ function iframe(el, parentDomain = "ecency.com") {
1044
1044
  const hasAutoplay = /[?&]autoplay=/.test(normalizedSrc);
1045
1045
  const s = hasAutoplay ? normalizedSrc : `${normalizedSrc}&autoplay=true`;
1046
1046
  el.setAttribute("src", s);
1047
+ el.setAttribute("class", "speak-iframe");
1047
1048
  return;
1048
1049
  }
1049
1050
  if (src.match(SPOTIFY_EMBED_REGEX)) {
@@ -1152,7 +1153,7 @@ function p(el) {
1152
1153
  }
1153
1154
 
1154
1155
  // src/methods/linkify.method.ts
1155
- function linkify(content, forApp, webp) {
1156
+ function linkify(content, forApp) {
1156
1157
  content = content.replace(/(^|\s|>)(#[-a-z\d]+)/gi, (tag) => {
1157
1158
  if (/#[\d]+$/.test(tag)) return tag;
1158
1159
  const preceding = /^\s|>/.test(tag) ? tag[0] : "";
@@ -1194,21 +1195,21 @@ function linkify(content, forApp, webp) {
1194
1195
  content = content.replace(IMG_REGEX, (imglink) => {
1195
1196
  const isLCP = !firstImageUsed;
1196
1197
  firstImageUsed = true;
1197
- return createImageHTML(imglink, isLCP, webp);
1198
+ return createImageHTML(imglink, isLCP);
1198
1199
  });
1199
1200
  return content;
1200
1201
  }
1201
1202
 
1202
1203
  // src/methods/text.method.ts
1203
- function text(node, forApp, webp) {
1204
+ function text(node, forApp) {
1204
1205
  if (!node || !node.parentNode) {
1205
1206
  return;
1206
1207
  }
1207
- if (node.parentNode && ["a", "code"].includes(node.parentNode.nodeName.toLowerCase())) {
1208
+ if (["a", "code"].includes(node.parentNode.nodeName.toLowerCase())) {
1208
1209
  return;
1209
1210
  }
1210
1211
  const nodeValue = node.nodeValue || "";
1211
- const linkified = linkify(nodeValue, forApp, webp);
1212
+ const linkified = linkify(nodeValue, forApp);
1212
1213
  if (linkified !== nodeValue) {
1213
1214
  const doc = DOMParser.parseFromString(
1214
1215
  `<span class="wr">${linkified}</span>`,
@@ -1223,7 +1224,7 @@ function text(node, forApp, webp) {
1223
1224
  }
1224
1225
  if (nodeValue.match(IMG_REGEX)) {
1225
1226
  const isLCP = false;
1226
- const imageHTML = createImageHTML(nodeValue, isLCP, webp);
1227
+ const imageHTML = createImageHTML(nodeValue, isLCP);
1227
1228
  const doc = DOMParser.parseFromString(imageHTML, "text/html");
1228
1229
  const replaceNode = doc.body?.firstChild || doc.firstChild;
1229
1230
  if (replaceNode) {
@@ -1235,7 +1236,7 @@ function text(node, forApp, webp) {
1235
1236
  const e = YOUTUBE_REGEX.exec(nodeValue);
1236
1237
  if (e && e[1]) {
1237
1238
  const vid = e[1];
1238
- const thumbnail = proxifyImageSrc(`https://img.youtube.com/vi/${vid.split("?")[0]}/hqdefault.jpg`, 0, 0, webp ? "webp" : "match");
1239
+ const thumbnail = proxifyImageSrc(`https://img.youtube.com/vi/${vid.split("?")[0]}/hqdefault.jpg`, 0, 0, "match");
1239
1240
  const embedSrc = `https://www.youtube.com/embed/${vid}?autoplay=1`;
1240
1241
  const startTime = extractYtStartTime(nodeValue);
1241
1242
  const container = node.ownerDocument.createElement("p");
@@ -1281,7 +1282,7 @@ function text(node, forApp, webp) {
1281
1282
  }
1282
1283
 
1283
1284
  // src/methods/traverse.method.ts
1284
- function traverse(node, forApp, depth = 0, webp = false, state = { firstImageFound: false }, parentDomain = "ecency.com", seoContext) {
1285
+ function traverse(node, forApp, depth = 0, state = { firstImageFound: false }, parentDomain = "ecency.com", seoContext) {
1285
1286
  if (!node || !node.childNodes) {
1286
1287
  return;
1287
1288
  }
@@ -1289,23 +1290,23 @@ function traverse(node, forApp, depth = 0, webp = false, state = { firstImageFou
1289
1290
  const child = node.childNodes[i];
1290
1291
  if (!child) return;
1291
1292
  if (child.nodeName.toLowerCase() === "a") {
1292
- a(child, forApp, webp, parentDomain, seoContext);
1293
+ a(child, forApp, parentDomain, seoContext);
1293
1294
  }
1294
1295
  if (child.nodeName.toLowerCase() === "iframe") {
1295
1296
  iframe(child, parentDomain);
1296
1297
  }
1297
1298
  if (child.nodeName === "#text") {
1298
- text(child, forApp, webp);
1299
+ text(child, forApp);
1299
1300
  }
1300
1301
  if (child.nodeName.toLowerCase() === "img") {
1301
- img(child, webp, state);
1302
+ img(child, state);
1302
1303
  }
1303
1304
  if (child.nodeName.toLowerCase() === "p") {
1304
1305
  p(child);
1305
1306
  }
1306
1307
  const currentChild = node.childNodes[i];
1307
1308
  if (currentChild) {
1308
- traverse(currentChild, forApp, depth + 1, webp, state, parentDomain, seoContext);
1309
+ traverse(currentChild, forApp, depth + 1, state, parentDomain, seoContext);
1309
1310
  }
1310
1311
  });
1311
1312
  }
@@ -1356,7 +1357,7 @@ function fixBlockLevelTagsInParagraphs(html) {
1356
1357
  html = html.replace(/<p><br>\s*<\/p>/g, "");
1357
1358
  return html;
1358
1359
  }
1359
- function markdownToHTML(input, forApp, webp, parentDomain = "ecency.com", seoContext) {
1360
+ function markdownToHTML(input, forApp, parentDomain = "ecency.com", seoContext) {
1360
1361
  input = input.replace(new RegExp("https://leofinance.io/threads/view/", "g"), "/@");
1361
1362
  input = input.replace(new RegExp("https://leofinance.io/posts/", "g"), "/@");
1362
1363
  input = input.replace(new RegExp("https://leofinance.io/threads/", "g"), "/@");
@@ -1416,7 +1417,7 @@ function markdownToHTML(input, forApp, webp, parentDomain = "ecency.com", seoCon
1416
1417
  output = md.render(input);
1417
1418
  output = fixBlockLevelTagsInParagraphs(output);
1418
1419
  const doc = DOMParser.parseFromString(`<body id="root">${removeDuplicateAttributes(output)}</body>`, "text/html");
1419
- traverse(doc, forApp, 0, webp, { firstImageFound: false }, parentDomain, seoContext);
1420
+ traverse(doc, forApp, 0, { firstImageFound: false }, parentDomain, seoContext);
1420
1421
  output = serializer.serializeToString(doc);
1421
1422
  } catch (error) {
1422
1423
  try {
@@ -1429,7 +1430,7 @@ function markdownToHTML(input, forApp, webp, parentDomain = "ecency.com", seoCon
1429
1430
  });
1430
1431
  const repairedHtml = domSerializer(dom.children);
1431
1432
  const doc = DOMParser.parseFromString(`<body id="root">${removeDuplicateAttributes(repairedHtml)}</body>`, "text/html");
1432
- traverse(doc, forApp, 0, webp, { firstImageFound: false }, parentDomain, seoContext);
1433
+ traverse(doc, forApp, 0, { firstImageFound: false }, parentDomain, seoContext);
1433
1434
  output = serializer.serializeToString(doc);
1434
1435
  } catch (fallbackError) {
1435
1436
  const escapedContent = he2.encode(output || md.render(input));
@@ -1457,18 +1458,18 @@ function cacheSet(key, value) {
1457
1458
  }
1458
1459
 
1459
1460
  // src/markdown-2-html.ts
1460
- function markdown2Html(obj, forApp = true, webp = false, parentDomain = "ecency.com", seoContext) {
1461
+ function markdown2Html(obj, forApp = true, _webp = false, parentDomain = "ecency.com", seoContext) {
1461
1462
  if (typeof obj === "string") {
1462
1463
  const cleanedStr = cleanReply(obj);
1463
- return markdownToHTML(cleanedStr, forApp, webp, parentDomain, seoContext);
1464
+ return markdownToHTML(cleanedStr, forApp, parentDomain, seoContext);
1464
1465
  }
1465
- const key = `${makeEntryCacheKey(obj)}-md${webp ? "-webp" : ""}-${forApp ? "app" : "site"}-${parentDomain}${seoContext ? `-seo${seoContext.authorReputation ?? ""}-${seoContext.postPayout ?? ""}` : ""}`;
1466
+ const key = `${makeEntryCacheKey(obj)}-md-${forApp ? "app" : "site"}-${parentDomain}${seoContext ? `-seo${seoContext.authorReputation ?? ""}-${seoContext.postPayout ?? ""}` : ""}`;
1466
1467
  const item = cacheGet(key);
1467
1468
  if (item) {
1468
1469
  return item;
1469
1470
  }
1470
1471
  const cleanBody = cleanReply(obj.body);
1471
- const res = markdownToHTML(cleanBody, forApp, webp, parentDomain, seoContext);
1472
+ const res = markdownToHTML(cleanBody, forApp, parentDomain, seoContext);
1472
1473
  cacheSet(key, res);
1473
1474
  return res;
1474
1475
  }