@ecency/render-helper 2.4.30 → 2.4.32

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.
@@ -152,6 +152,8 @@ var ALLOWED_ATTRIBUTES = {
152
152
  ],
153
153
  "img": [
154
154
  "src",
155
+ "srcset",
156
+ "sizes",
155
157
  "alt",
156
158
  "class",
157
159
  "loading",
@@ -195,17 +197,16 @@ var ALLOWED_ATTRIBUTES = {
195
197
  "del": [],
196
198
  "ins": []
197
199
  };
198
- var lenientErrorHandler = (level, msg, context) => {
200
+ var hasDOMParser = typeof globalThis.DOMParser !== "undefined";
201
+ var hasXMLSerializer = typeof globalThis.XMLSerializer !== "undefined";
202
+ var lenientErrorHandler = (level, msg, _context) => {
199
203
  if (process.env.NODE_ENV === "development") {
200
204
  console.warn("[DOMParser]", level, msg);
201
205
  }
202
206
  return void 0;
203
207
  };
204
- var DOMParser = new xmldom.DOMParser({
205
- // Use onError instead of deprecated errorHandler
206
- // By providing a non-throwing error handler, parsing continues despite malformed HTML
207
- onError: lenientErrorHandler
208
- });
208
+ var DOMParser = hasDOMParser ? new globalThis.DOMParser() : new xmldom.DOMParser({ onError: lenientErrorHandler });
209
+ var XMLSerializer = hasXMLSerializer ? globalThis.XMLSerializer : xmldom.XMLSerializer;
209
210
 
210
211
  // src/helper.ts
211
212
  function removeDuplicateAttributes(html) {
@@ -283,8 +284,10 @@ function isValidUsername(username) {
283
284
  !label.includes("..");
284
285
  });
285
286
  }
287
+
288
+ // src/methods/get-inner-html.method.ts
286
289
  function getSerializedInnerHTML(node) {
287
- const serializer = new xmldom.XMLSerializer();
290
+ const serializer = new XMLSerializer();
288
291
  if (node.childNodes[0]) {
289
292
  return serializer.serializeToString(node.childNodes[0]);
290
293
  }
@@ -310,6 +313,10 @@ function sanitizeHtml(html) {
310
313
  const decodedLower = decoded.toLowerCase();
311
314
  if (name.startsWith("on")) return "";
312
315
  if (tag === "img" && name === "src" && (!/^https?:\/\//.test(decodedLower) || decodedLower.startsWith("javascript:"))) return "";
316
+ if (tag === "img" && name === "srcset") {
317
+ const candidates = decoded.split(",").map((c) => c.trim().split(/\s+/)[0]);
318
+ if (candidates.some((url) => !/^https?:\/\//.test(url))) return "";
319
+ }
313
320
  if (tag === "video" && ["src", "poster"].includes(name) && (!/^https?:\/\//.test(decodedLower) || decodedLower.startsWith("javascript:"))) return "";
314
321
  if (tag === "img" && ["dynsrc", "lowsrc"].includes(name)) return "";
315
322
  if (tag === "span" && name === "class" && decoded.toLowerCase().trim() === "wr") return "";
@@ -324,6 +331,9 @@ var proxyBase = "https://images.ecency.com";
324
331
  function setProxyBase(p2) {
325
332
  proxyBase = p2;
326
333
  }
334
+ function getProxyBase() {
335
+ return proxyBase;
336
+ }
327
337
  function extractPHash(url) {
328
338
  if (url.startsWith(`${proxyBase}/p/`)) {
329
339
  const [hash] = url.split("/p/")[1].split("?");
@@ -371,8 +381,24 @@ function proxifyImageSrc(url, width = 0, height = 0, _format = "match") {
371
381
  const b58url = multihash__default.default.toB58String(Buffer.from(realUrl.toString()));
372
382
  return `${proxyBase}/p/${b58url}?${qs}`;
373
383
  }
384
+ var SRCSET_WIDTHS = [320, 600, 800, 1024, 1280];
385
+ function buildSrcSet(url) {
386
+ if (!url || typeof url !== "string") return "";
387
+ const escapedBase = proxyBase.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
388
+ const proxyPattern = new RegExp(`^${escapedBase}/p/([^?]+)`);
389
+ const match = url.match(proxyPattern);
390
+ if (match) {
391
+ const phash = extractPHash(url) || match[1];
392
+ return SRCSET_WIDTHS.map((w) => `${proxyBase}/p/${phash}?format=match&mode=fit&width=${w} ${w}w`).join(", ");
393
+ }
394
+ return SRCSET_WIDTHS.map((w) => {
395
+ const proxied = proxifyImageSrc(url, w);
396
+ return proxied ? `${proxied} ${w}w` : "";
397
+ }).filter(Boolean).join(", ");
398
+ }
374
399
 
375
400
  // src/methods/img.method.ts
401
+ var IMAGE_SIZES = "(max-width: 768px) 100vw, 700px";
376
402
  function img(el, state) {
377
403
  const src = el.getAttribute("src") || "";
378
404
  const decodedSrc = decodeURIComponent(
@@ -382,11 +408,15 @@ function img(el, state) {
382
408
  const isInvalid = !src || decodedSrc.startsWith("javascript") || decodedSrc.startsWith("vbscript") || decodedSrc === "x";
383
409
  if (isInvalid) {
384
410
  el.removeAttribute("src");
411
+ el.removeAttribute("srcset");
412
+ el.removeAttribute("sizes");
385
413
  return;
386
414
  }
387
415
  const isRelative = !/^https?:\/\//i.test(decodedSrc) && !decodedSrc.startsWith("/");
388
416
  if (isRelative) {
389
417
  el.removeAttribute("src");
418
+ el.removeAttribute("srcset");
419
+ el.removeAttribute("sizes");
390
420
  return;
391
421
  }
392
422
  el.setAttribute("itemprop", "image");
@@ -401,22 +431,41 @@ function img(el, state) {
401
431
  }
402
432
  const cls = el.getAttribute("class") || "";
403
433
  const shouldReplace = !cls.includes("no-replace");
404
- const hasAlreadyProxied = src.startsWith("https://images.ecency.com/p/") || src.startsWith("https://images.ecency.com/u/") || /^https:\/\/images\.ecency\.com\/\d+x\d+\//.test(src);
434
+ const base = getProxyBase().replace(/\/+$/, "");
435
+ const hasAlreadyProxied = src.startsWith(`${base}/p/`) || src.startsWith(`${base}/u/`) || new RegExp(`^${base.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}/\\d+x\\d+/`).test(src);
405
436
  if (shouldReplace && !hasAlreadyProxied) {
406
437
  const proxified = proxifyImageSrc(decodedSrc);
407
438
  if (proxified) {
408
439
  el.setAttribute("src", proxified);
440
+ const srcset = buildSrcSet(decodedSrc);
441
+ if (srcset) {
442
+ el.setAttribute("srcset", srcset);
443
+ el.setAttribute("sizes", IMAGE_SIZES);
444
+ }
445
+ }
446
+ } else if (shouldReplace && hasAlreadyProxied) {
447
+ if (src.startsWith(`${base}/p/`)) {
448
+ const srcset = buildSrcSet(src);
449
+ if (srcset) {
450
+ el.setAttribute("srcset", srcset);
451
+ el.setAttribute("sizes", IMAGE_SIZES);
452
+ }
409
453
  }
410
454
  }
411
455
  }
412
456
  function createImageHTML(src, isLCP) {
413
457
  const proxified = proxifyImageSrc(src);
414
458
  if (!proxified) return "";
459
+ const base = getProxyBase().replace(/\/+$/, "");
460
+ const isAlreadyProxied = src.startsWith(`${base}/u/`) || new RegExp(`^${base.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}/\\d+x\\d+/`).test(src);
461
+ const srcset = isAlreadyProxied ? "" : buildSrcSet(src);
415
462
  const loading = isLCP ? "eager" : "lazy";
416
463
  const fetch = isLCP ? 'fetchpriority="high"' : 'decoding="async"';
464
+ const srcsetAttr = srcset ? `srcset="${srcset}" sizes="${IMAGE_SIZES}"` : "";
417
465
  return `<img
418
466
  class="markdown-img-link"
419
467
  src="${proxified}"
468
+ ${srcsetAttr}
420
469
  loading="${loading}"
421
470
  ${fetch}
422
471
  itemprop="image"
@@ -1538,7 +1587,7 @@ function markdownToHTML(input, forApp, parentDomain = "ecency.com", seoContext,
1538
1587
  "sub",
1539
1588
  "sup"
1540
1589
  ]);
1541
- const serializer = new xmldom.XMLSerializer();
1590
+ const serializer = new XMLSerializer();
1542
1591
  if (!input) {
1543
1592
  return "";
1544
1593
  }
@@ -1810,6 +1859,7 @@ function getPostBodySummary(obj, length, platform) {
1810
1859
  }
1811
1860
 
1812
1861
  exports.SECTION_LIST = SECTION_LIST;
1862
+ exports.buildSrcSet = buildSrcSet;
1813
1863
  exports.catchPostImage = catchPostImage;
1814
1864
  exports.isValidPermlink = isValidPermlink;
1815
1865
  exports.postBodySummary = getPostBodySummary;