@ecency/render-helper 2.5.28 → 2.5.30
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 +21 -7
- package/dist/browser/index.js +504 -21
- package/dist/browser/index.js.map +1 -1
- package/dist/node/index.cjs +504 -21
- package/dist/node/index.cjs.map +1 -1
- package/dist/node/index.mjs +504 -21
- package/dist/node/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/node/index.cjs
CHANGED
|
@@ -860,15 +860,31 @@ function img(el, state, forApp = true) {
|
|
|
860
860
|
return;
|
|
861
861
|
}
|
|
862
862
|
el.setAttribute("itemprop", "image");
|
|
863
|
-
const
|
|
864
|
-
|
|
863
|
+
const avatarRoute = new RegExp(`^${trimTrailingSlash(getProxyBase()).replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}/u/[^/]+/avatar(?:/[a-z]+)?$`);
|
|
864
|
+
const classTokens = (el.getAttribute("class") || "").split(/\s+/);
|
|
865
|
+
const isAvatar = avatarRoute.test(decodedSrc) || classTokens.includes("er-author-link-image");
|
|
866
|
+
if (isAvatar) {
|
|
867
|
+
el.setAttribute("loading", "lazy");
|
|
868
|
+
el.setAttribute("decoding", "async");
|
|
869
|
+
}
|
|
870
|
+
const imageIndex = !state || isAvatar ? 3 : state.imageCount ?? (state.firstImageFound ? 1 : 0);
|
|
871
|
+
if (state && !isAvatar) {
|
|
872
|
+
state.imageCount = imageIndex + 1;
|
|
873
|
+
state.firstImageFound = true;
|
|
874
|
+
}
|
|
875
|
+
if (isAvatar) ; else if (imageIndex === 0) {
|
|
865
876
|
el.setAttribute("loading", "eager");
|
|
866
877
|
el.setAttribute("fetchpriority", "high");
|
|
867
|
-
|
|
878
|
+
} else if (imageIndex === 1) {
|
|
879
|
+
el.setAttribute("loading", "eager");
|
|
880
|
+
el.setAttribute("decoding", "async");
|
|
868
881
|
} else {
|
|
869
882
|
el.setAttribute("loading", "lazy");
|
|
870
883
|
el.setAttribute("decoding", "async");
|
|
871
884
|
}
|
|
885
|
+
if (isAvatar || imageIndex !== 0) {
|
|
886
|
+
el.removeAttribute("fetchpriority");
|
|
887
|
+
}
|
|
872
888
|
const cls = el.getAttribute("class") || "";
|
|
873
889
|
const shouldReplace = !cls.includes("no-replace");
|
|
874
890
|
const base = trimTrailingSlash(getProxyBase());
|
|
@@ -10270,28 +10286,455 @@ function isGifLink(link) {
|
|
|
10270
10286
|
var BACKTICK_FENCE_RE = /```[\s\S]*?```/g;
|
|
10271
10287
|
var TILDE_FENCE_RE = /~~~[\s\S]*?~~~/g;
|
|
10272
10288
|
var INLINE_CODE_RE = /`[^`\n]*`/g;
|
|
10273
|
-
var
|
|
10289
|
+
var OPEN_TAG_NAME_END = /[\t\f\r />]/;
|
|
10290
|
+
var CLOSE_TAG_NAME_END = /[\s>]/;
|
|
10291
|
+
function isWholeTagName(lower, idx, end) {
|
|
10292
|
+
const next = lower[idx];
|
|
10293
|
+
return next === void 0 || end.test(next);
|
|
10294
|
+
}
|
|
10295
|
+
function findTag(lower, tag, from, end) {
|
|
10296
|
+
let at = lower.indexOf(tag, from);
|
|
10297
|
+
while (at !== -1 && !isWholeTagName(lower, at + tag.length, end)) {
|
|
10298
|
+
at = lower.indexOf(tag, at + tag.length);
|
|
10299
|
+
}
|
|
10300
|
+
return at;
|
|
10301
|
+
}
|
|
10302
|
+
function findOpenTagEnd(lower, openAt) {
|
|
10303
|
+
let quote = "";
|
|
10304
|
+
for (let i2 = openAt + 1; i2 < lower.length; i2++) {
|
|
10305
|
+
const c = lower[i2];
|
|
10306
|
+
if (c === "\n") return NaN;
|
|
10307
|
+
if (quote) {
|
|
10308
|
+
if (c === quote) quote = "";
|
|
10309
|
+
} else if (c === '"' || c === "'") {
|
|
10310
|
+
quote = c;
|
|
10311
|
+
} else if (c === ">") {
|
|
10312
|
+
return lower[i2 - 1] === "/" ? NaN : i2;
|
|
10313
|
+
}
|
|
10314
|
+
}
|
|
10315
|
+
return -1;
|
|
10316
|
+
}
|
|
10317
|
+
var HTML_BLOCK_TAGS = /* @__PURE__ */ new Set([
|
|
10318
|
+
"article",
|
|
10319
|
+
"aside",
|
|
10320
|
+
"button",
|
|
10321
|
+
"blockquote",
|
|
10322
|
+
"body",
|
|
10323
|
+
"canvas",
|
|
10324
|
+
"caption",
|
|
10325
|
+
"col",
|
|
10326
|
+
"colgroup",
|
|
10327
|
+
"dd",
|
|
10328
|
+
"div",
|
|
10329
|
+
"dl",
|
|
10330
|
+
"dt",
|
|
10331
|
+
"embed",
|
|
10332
|
+
"fieldset",
|
|
10333
|
+
"figcaption",
|
|
10334
|
+
"figure",
|
|
10335
|
+
"footer",
|
|
10336
|
+
"form",
|
|
10337
|
+
"h1",
|
|
10338
|
+
"h2",
|
|
10339
|
+
"h3",
|
|
10340
|
+
"h4",
|
|
10341
|
+
"h5",
|
|
10342
|
+
"h6",
|
|
10343
|
+
"header",
|
|
10344
|
+
"hgroup",
|
|
10345
|
+
"hr",
|
|
10346
|
+
"iframe",
|
|
10347
|
+
"li",
|
|
10348
|
+
"map",
|
|
10349
|
+
"object",
|
|
10350
|
+
"ol",
|
|
10351
|
+
"output",
|
|
10352
|
+
"p",
|
|
10353
|
+
"pre",
|
|
10354
|
+
"progress",
|
|
10355
|
+
"script",
|
|
10356
|
+
"section",
|
|
10357
|
+
"style",
|
|
10358
|
+
"table",
|
|
10359
|
+
"tbody",
|
|
10360
|
+
"td",
|
|
10361
|
+
"textarea",
|
|
10362
|
+
"tfoot",
|
|
10363
|
+
"th",
|
|
10364
|
+
"tr",
|
|
10365
|
+
"thead",
|
|
10366
|
+
"ul",
|
|
10367
|
+
"video"
|
|
10368
|
+
]);
|
|
10369
|
+
var HTML_BLOCK_LINE_RE = /^ {0,3}<(?:[!?]|([a-z]{1,15})[\s/>]|\/([a-z]{1,15})[\s>])/;
|
|
10370
|
+
var BLOCKQUOTE_PREFIX_RE = /^ {0,3}> ?/;
|
|
10371
|
+
var LIST_PREFIX_RE = /^(?:[-*+]|\d{1,9}[.)]) +/;
|
|
10372
|
+
function markLines(lower) {
|
|
10373
|
+
const block2 = new Uint8Array(lower.length);
|
|
10374
|
+
const code2 = new Uint8Array(lower.length);
|
|
10375
|
+
let inBlock = false;
|
|
10376
|
+
let listIndent = 0;
|
|
10377
|
+
let nestedItem = false;
|
|
10378
|
+
let lineStart = 0;
|
|
10379
|
+
while (lineStart <= lower.length) {
|
|
10380
|
+
let lineEnd = lower.indexOf("\n", lineStart);
|
|
10381
|
+
if (lineEnd === -1) lineEnd = lower.length;
|
|
10382
|
+
let line = lower.slice(lineStart, lineEnd);
|
|
10383
|
+
if (inBlock) {
|
|
10384
|
+
if (line.trim() === "") {
|
|
10385
|
+
inBlock = false;
|
|
10386
|
+
listIndent = 0;
|
|
10387
|
+
nestedItem = false;
|
|
10388
|
+
} else {
|
|
10389
|
+
block2.fill(1, lineStart, lineEnd);
|
|
10390
|
+
}
|
|
10391
|
+
lineStart = lineEnd + 1;
|
|
10392
|
+
continue;
|
|
10393
|
+
}
|
|
10394
|
+
let stripped = 0;
|
|
10395
|
+
let sawList = false;
|
|
10396
|
+
let lastWasList = false;
|
|
10397
|
+
let inlineRemainder = false;
|
|
10398
|
+
for (; ; ) {
|
|
10399
|
+
const bq = BLOCKQUOTE_PREFIX_RE.exec(line);
|
|
10400
|
+
if (bq) {
|
|
10401
|
+
line = line.slice(bq[0].length);
|
|
10402
|
+
stripped += bq[0].length;
|
|
10403
|
+
lastWasList = false;
|
|
10404
|
+
inlineRemainder = false;
|
|
10405
|
+
continue;
|
|
10406
|
+
}
|
|
10407
|
+
const lm = LIST_PREFIX_RE.exec(line);
|
|
10408
|
+
if (lm) {
|
|
10409
|
+
line = line.slice(lm[0].length);
|
|
10410
|
+
stripped += lm[0].length;
|
|
10411
|
+
if (lastWasList) inlineRemainder = true;
|
|
10412
|
+
sawList = true;
|
|
10413
|
+
lastWasList = true;
|
|
10414
|
+
continue;
|
|
10415
|
+
}
|
|
10416
|
+
break;
|
|
10417
|
+
}
|
|
10418
|
+
if (sawList) {
|
|
10419
|
+
listIndent = stripped;
|
|
10420
|
+
nestedItem = inlineRemainder;
|
|
10421
|
+
} else if (listIndent > 0 && line.trim() !== "") {
|
|
10422
|
+
let indent = 0;
|
|
10423
|
+
while (indent < listIndent && line[indent] === " ") indent++;
|
|
10424
|
+
if (indent >= Math.min(listIndent, 2)) {
|
|
10425
|
+
line = line.slice(indent);
|
|
10426
|
+
inlineRemainder = nestedItem;
|
|
10427
|
+
} else {
|
|
10428
|
+
listIndent = 0;
|
|
10429
|
+
nestedItem = false;
|
|
10430
|
+
}
|
|
10431
|
+
}
|
|
10432
|
+
const blank = line.trim() === "";
|
|
10433
|
+
if (blank) {
|
|
10434
|
+
inBlock = false;
|
|
10435
|
+
listIndent = 0;
|
|
10436
|
+
nestedItem = false;
|
|
10437
|
+
} else if (inlineRemainder) {
|
|
10438
|
+
inBlock = false;
|
|
10439
|
+
} else if (!inBlock && /^(?: {4}|\t)/.test(line)) {
|
|
10440
|
+
code2.fill(1, lineStart, lineEnd);
|
|
10441
|
+
} else if (!inBlock) {
|
|
10442
|
+
const m = HTML_BLOCK_LINE_RE.exec(line);
|
|
10443
|
+
if (m) {
|
|
10444
|
+
const tag = m[1] ?? m[2];
|
|
10445
|
+
inBlock = tag === void 0 || HTML_BLOCK_TAGS.has(tag);
|
|
10446
|
+
}
|
|
10447
|
+
}
|
|
10448
|
+
if (inBlock && !blank) block2.fill(1, lineStart, lineEnd);
|
|
10449
|
+
lineStart = lineEnd + 1;
|
|
10450
|
+
}
|
|
10451
|
+
return { block: block2, code: code2 };
|
|
10452
|
+
}
|
|
10453
|
+
var blankChars = (s) => s.replace(/[^\n]/g, " ");
|
|
10454
|
+
function blankMatches(text3, re) {
|
|
10455
|
+
return text3.replace(re, blankChars);
|
|
10456
|
+
}
|
|
10457
|
+
function blankSpans(input, open, close, tagNames, blockMask) {
|
|
10458
|
+
const { text: text3, lower } = input;
|
|
10459
|
+
const findOpen = (from2) => {
|
|
10460
|
+
if (!tagNames) return lower.indexOf(open, from2);
|
|
10461
|
+
let at = findTag(lower, open, from2, OPEN_TAG_NAME_END);
|
|
10462
|
+
while (at !== -1 && (Number.isNaN(findOpenTagEnd(lower, at)) || blockMask !== null && !blockMask[at])) {
|
|
10463
|
+
at = findTag(lower, open, at + open.length, OPEN_TAG_NAME_END);
|
|
10464
|
+
}
|
|
10465
|
+
return at;
|
|
10466
|
+
};
|
|
10467
|
+
const findClose = (from2) => tagNames ? findTag(lower, close, from2, CLOSE_TAG_NAME_END) : lower.indexOf(close, from2);
|
|
10468
|
+
let start = findOpen(0);
|
|
10469
|
+
if (start === -1) return input;
|
|
10470
|
+
const textParts = [];
|
|
10471
|
+
const lowerParts = [];
|
|
10472
|
+
let from = 0;
|
|
10473
|
+
while (start !== -1) {
|
|
10474
|
+
const end = findClose(start + open.length);
|
|
10475
|
+
let to;
|
|
10476
|
+
if (end === -1) {
|
|
10477
|
+
to = text3.length;
|
|
10478
|
+
} else if (tagNames) {
|
|
10479
|
+
const gt = lower.indexOf(">", end + close.length);
|
|
10480
|
+
to = gt === -1 ? text3.length : gt + 1;
|
|
10481
|
+
} else {
|
|
10482
|
+
to = end + close.length;
|
|
10483
|
+
}
|
|
10484
|
+
const blanked = blankChars(lower.slice(start, to));
|
|
10485
|
+
textParts.push(text3.slice(from, start), blanked);
|
|
10486
|
+
lowerParts.push(lower.slice(from, start), blanked);
|
|
10487
|
+
from = to;
|
|
10488
|
+
start = to >= text3.length ? -1 : findOpen(to);
|
|
10489
|
+
}
|
|
10490
|
+
textParts.push(text3.slice(from));
|
|
10491
|
+
lowerParts.push(lower.slice(from));
|
|
10492
|
+
return { text: textParts.join(""), lower: lowerParts.join("") };
|
|
10493
|
+
}
|
|
10494
|
+
function blankMasked(input, mask) {
|
|
10495
|
+
let text3 = "";
|
|
10496
|
+
let lower = "";
|
|
10497
|
+
let from = 0;
|
|
10498
|
+
for (let i2 = 0; i2 < mask.length; i2++) {
|
|
10499
|
+
if (!mask[i2]) continue;
|
|
10500
|
+
let j = i2;
|
|
10501
|
+
while (j < mask.length && mask[j]) j++;
|
|
10502
|
+
text3 += input.text.slice(from, i2) + blankChars(input.text.slice(i2, j));
|
|
10503
|
+
lower += input.lower.slice(from, i2) + blankChars(input.lower.slice(i2, j));
|
|
10504
|
+
from = j;
|
|
10505
|
+
i2 = j;
|
|
10506
|
+
}
|
|
10507
|
+
if (from === 0) return input;
|
|
10508
|
+
return { text: text3 + input.text.slice(from), lower: lower + input.lower.slice(from) };
|
|
10509
|
+
}
|
|
10510
|
+
function stripHiddenRegions(text3) {
|
|
10511
|
+
let spellings = { text: text3, lower: text3.toLowerCase() };
|
|
10512
|
+
const { block: blockMask, code: codeMask } = markLines(spellings.lower);
|
|
10513
|
+
spellings = blankMasked(spellings, codeMask);
|
|
10514
|
+
spellings = blankSpans(spellings, "<!--", "-->", false, null);
|
|
10515
|
+
spellings = blankSpans(spellings, "<style", "</style", true, null);
|
|
10516
|
+
spellings = blankSpans(spellings, "<pre", "</pre", true, blockMask);
|
|
10517
|
+
spellings = blankSpans(spellings, "<code", "</code", true, blockMask);
|
|
10518
|
+
return spellings.text;
|
|
10519
|
+
}
|
|
10274
10520
|
var MD_IMAGE_RE = /!\[[^[\]]*\]\(\s*([^)\s]{1,2048})(?:\s+["'][^"']*["'])?\s*\)/;
|
|
10275
10521
|
var MD_IMAGE_PRESENT_RE = /!\[[^[\]]*\]\(\s*[^\s)]/;
|
|
10276
10522
|
var HTML_IMAGE_RE = /<img\b[^>]*?\bsrc\s*=\s*["']([^"']+)["']/i;
|
|
10277
|
-
var
|
|
10523
|
+
var URL_TOKEN_RE = /https?:\/\/[^\s<>"'()[\]]+/gi;
|
|
10524
|
+
var IMAGE_EXT_G = /\.(?:tiff?|jpe?g|gif|png|svg|ico|heic|webp|arw)/gi;
|
|
10525
|
+
var YOUTUBE_ID_RE = /^[^"&?/\s]{11}$/;
|
|
10526
|
+
function imageToken(token) {
|
|
10527
|
+
let end = -1;
|
|
10528
|
+
for (const m of token.matchAll(IMAGE_EXT_G)) {
|
|
10529
|
+
end = (m.index ?? 0) + m[0].length;
|
|
10530
|
+
}
|
|
10531
|
+
if (end === -1) return null;
|
|
10532
|
+
if (end < token.length && (token[end] === "?" || token[end] === "#")) end = token.length;
|
|
10533
|
+
const url = token.slice(0, end);
|
|
10534
|
+
return SAFE_URL_RE.test(url) ? url : null;
|
|
10535
|
+
}
|
|
10536
|
+
function youtubeIdOf(url) {
|
|
10537
|
+
const m = /^https?:\/\/([^/?#]+)/i.exec(url);
|
|
10538
|
+
if (!m) return null;
|
|
10539
|
+
const host = m[1].toLowerCase();
|
|
10540
|
+
const isShort = host === "youtu.be";
|
|
10541
|
+
const isFull = host === "youtube.com" || host.endsWith(".youtube.com");
|
|
10542
|
+
if (!isShort && !isFull) return null;
|
|
10543
|
+
const rest = url.slice(m[0].length);
|
|
10544
|
+
const hashAt = rest.indexOf("#");
|
|
10545
|
+
const beforeHash = hashAt === -1 ? rest : rest.slice(0, hashAt);
|
|
10546
|
+
const qAt = beforeHash.indexOf("?");
|
|
10547
|
+
const path = qAt === -1 ? beforeHash : beforeHash.slice(0, qAt);
|
|
10548
|
+
const query = qAt === -1 ? "" : beforeHash.slice(qAt + 1);
|
|
10549
|
+
const candidate = (value) => {
|
|
10550
|
+
const id = value === void 0 ? "" : value.slice(0, 11);
|
|
10551
|
+
return YOUTUBE_ID_RE.test(id) ? id : null;
|
|
10552
|
+
};
|
|
10553
|
+
if (isFull && query) {
|
|
10554
|
+
for (const part of query.split("&")) {
|
|
10555
|
+
if (part.startsWith("v=")) {
|
|
10556
|
+
const id = candidate(part.slice(2));
|
|
10557
|
+
if (id) return id;
|
|
10558
|
+
}
|
|
10559
|
+
}
|
|
10560
|
+
}
|
|
10561
|
+
const segments = path.split("/").filter((seg) => seg.length > 0);
|
|
10562
|
+
if (isShort) return candidate(segments[0]);
|
|
10563
|
+
if (segments.length >= 2 && ["v", "e", "embed", "shorts"].includes(segments[0].toLowerCase())) {
|
|
10564
|
+
return candidate(segments[1]);
|
|
10565
|
+
}
|
|
10566
|
+
if (segments.length >= 3) return candidate(segments[segments.length - 1]);
|
|
10567
|
+
return null;
|
|
10568
|
+
}
|
|
10569
|
+
function isAutolinkAt(text3, idx) {
|
|
10570
|
+
return /^https?:\/\//i.test(text3.slice(idx, idx + 8));
|
|
10571
|
+
}
|
|
10572
|
+
function markInsideTags(text3) {
|
|
10573
|
+
const marks = new Uint8Array(text3.length);
|
|
10574
|
+
let inTag = false;
|
|
10575
|
+
let quote = "";
|
|
10576
|
+
for (let i2 = 0; i2 < text3.length; i2++) {
|
|
10577
|
+
const c = text3[i2];
|
|
10578
|
+
if (!inTag) {
|
|
10579
|
+
if (c === "<" && i2 + 1 < text3.length && /[A-Za-z/!?]/.test(text3[i2 + 1]) && !isAutolinkAt(text3, i2 + 1)) {
|
|
10580
|
+
inTag = true;
|
|
10581
|
+
marks[i2] = 1;
|
|
10582
|
+
}
|
|
10583
|
+
continue;
|
|
10584
|
+
}
|
|
10585
|
+
marks[i2] = 1;
|
|
10586
|
+
if (quote) {
|
|
10587
|
+
if (c === quote) quote = "";
|
|
10588
|
+
} else if (c === '"' || c === "'") {
|
|
10589
|
+
quote = c;
|
|
10590
|
+
} else if (c === ">") {
|
|
10591
|
+
inTag = false;
|
|
10592
|
+
}
|
|
10593
|
+
}
|
|
10594
|
+
return marks;
|
|
10595
|
+
}
|
|
10596
|
+
function isStandalone(scan, idx) {
|
|
10597
|
+
if (scan.inTag[idx]) return false;
|
|
10598
|
+
if (idx === 0) return true;
|
|
10599
|
+
const text3 = scan.text;
|
|
10600
|
+
const prev = text3[idx - 1];
|
|
10601
|
+
if (/[\w/.:%?&=#[-]/.test(prev)) return false;
|
|
10602
|
+
const prev2 = idx > 1 ? text3[idx - 2] : "";
|
|
10603
|
+
if (prev === "(" && prev2 === "]") return false;
|
|
10604
|
+
return true;
|
|
10605
|
+
}
|
|
10606
|
+
function* standaloneMatches(scan, classify) {
|
|
10607
|
+
for (const m of scan.text.matchAll(URL_TOKEN_RE)) {
|
|
10608
|
+
const idx = m.index ?? 0;
|
|
10609
|
+
if (!isStandalone(scan, idx)) continue;
|
|
10610
|
+
const value = classify(m[0]);
|
|
10611
|
+
if (value !== null) yield { url: value, pos: idx };
|
|
10612
|
+
}
|
|
10613
|
+
}
|
|
10614
|
+
function firstStandalone(scan, classify) {
|
|
10615
|
+
for (const hit of standaloneMatches(scan, classify)) return hit;
|
|
10616
|
+
return null;
|
|
10617
|
+
}
|
|
10618
|
+
var HREF_ATTR_RE = /\shref\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'>]+))/i;
|
|
10619
|
+
function hasGluedAttribute(tag) {
|
|
10620
|
+
let quote = "";
|
|
10621
|
+
for (let i2 = 0; i2 < tag.length; i2++) {
|
|
10622
|
+
const c = tag[i2];
|
|
10623
|
+
if (quote) {
|
|
10624
|
+
if (c === quote) {
|
|
10625
|
+
quote = "";
|
|
10626
|
+
const next = tag[i2 + 1];
|
|
10627
|
+
if (next !== void 0 && /[A-Za-z]/.test(next)) return true;
|
|
10628
|
+
}
|
|
10629
|
+
} else if (c === '"' || c === "'") {
|
|
10630
|
+
quote = c;
|
|
10631
|
+
}
|
|
10632
|
+
}
|
|
10633
|
+
return false;
|
|
10634
|
+
}
|
|
10278
10635
|
var MD_LINK_RE = /\[([^[\]]*)\]\(\s*([^)\s[]+)(?:\s+["'][^"']*["'])?\s*\)/g;
|
|
10279
|
-
var IMG_HREF_RE = /https?:\/\/.*\.(?:tiff?|jpe?g|gif|png|svg|ico|heic|webp|arw)/i;
|
|
10280
10636
|
var SAFE_URL_RE = /^https?:\/\//i;
|
|
10637
|
+
var IMG_EXT_RE = /\.(?:tiff?|jpe?g|gif|png|svg|ico|heic|webp|arw)/i;
|
|
10638
|
+
var isImageHref = (href) => SAFE_URL_RE.test(href) && IMG_EXT_RE.test(href);
|
|
10281
10639
|
function findFirstImageUrl(body, includeBareUrls = false) {
|
|
10282
|
-
|
|
10283
|
-
|
|
10640
|
+
return findFirstImageCandidate(prepareBody(body), includeBareUrls).candidate?.url ?? null;
|
|
10641
|
+
}
|
|
10642
|
+
function stripCodeRegions(body) {
|
|
10643
|
+
let text3 = blankMatches(body, BACKTICK_FENCE_RE);
|
|
10644
|
+
text3 = blankMatches(text3, TILDE_FENCE_RE);
|
|
10645
|
+
text3 = blankMatches(text3, INLINE_CODE_RE);
|
|
10646
|
+
return stripHiddenRegions(text3);
|
|
10647
|
+
}
|
|
10648
|
+
function blankUnequalAnchors(cleaned, textContent) {
|
|
10649
|
+
const lower = cleaned.toLowerCase();
|
|
10650
|
+
const parts = [];
|
|
10651
|
+
let from = 0;
|
|
10652
|
+
let at = findTag(lower, "<a", 0, OPEN_TAG_NAME_END);
|
|
10653
|
+
while (at !== -1) {
|
|
10654
|
+
const gt = findOpenTagEnd(lower, at);
|
|
10655
|
+
if (Number.isNaN(gt) || gt === -1 || hasGluedAttribute(cleaned.slice(at, gt))) {
|
|
10656
|
+
at = findTag(lower, "<a", at + 2, OPEN_TAG_NAME_END);
|
|
10657
|
+
continue;
|
|
10658
|
+
}
|
|
10659
|
+
const closeAt = findTag(lower, "</a", gt + 1, CLOSE_TAG_NAME_END);
|
|
10660
|
+
const innerEnd = closeAt === -1 ? cleaned.length : closeAt;
|
|
10661
|
+
let spanEnd = cleaned.length;
|
|
10662
|
+
if (closeAt !== -1) {
|
|
10663
|
+
const closeGt = lower.indexOf(">", closeAt + 3);
|
|
10664
|
+
spanEnd = closeGt === -1 ? cleaned.length : closeGt + 1;
|
|
10665
|
+
}
|
|
10666
|
+
const hrefMatch = HREF_ATTR_RE.exec(cleaned.slice(at, gt));
|
|
10667
|
+
const href = hrefMatch ? hrefMatch[1] ?? hrefMatch[2] ?? hrefMatch[3] ?? "" : "";
|
|
10668
|
+
const inner = cleaned.slice(gt + 1, innerEnd);
|
|
10669
|
+
let text3;
|
|
10670
|
+
if (textContent) {
|
|
10671
|
+
text3 = stripHtmlTags(inner);
|
|
10672
|
+
} else {
|
|
10673
|
+
const firstTag = inner.search(/<[A-Za-z/!]/);
|
|
10674
|
+
text3 = firstTag === -1 ? inner : inner.slice(0, firstTag);
|
|
10675
|
+
}
|
|
10676
|
+
if (!href || decodeEntities(text3.trim()) !== decodeEntities(href.trim())) {
|
|
10677
|
+
parts.push(cleaned.slice(from, at), blankChars(cleaned.slice(at, spanEnd)));
|
|
10678
|
+
from = spanEnd;
|
|
10679
|
+
}
|
|
10680
|
+
at = spanEnd >= cleaned.length ? -1 : findTag(lower, "<a", spanEnd, OPEN_TAG_NAME_END);
|
|
10681
|
+
}
|
|
10682
|
+
if (parts.length === 0) return cleaned;
|
|
10683
|
+
parts.push(cleaned.slice(from));
|
|
10684
|
+
return parts.join("");
|
|
10685
|
+
}
|
|
10686
|
+
var EMPTY_SCAN = { text: "", inTag: new Uint8Array(0) };
|
|
10687
|
+
function prepareBody(body) {
|
|
10688
|
+
const cleaned = body ? stripCodeRegions(body) : "";
|
|
10689
|
+
if (!cleaned) return { cleaned, image: EMPTY_SCAN, video: EMPTY_SCAN };
|
|
10690
|
+
const imageText = blankUnequalAnchors(cleaned, false);
|
|
10691
|
+
const videoText = blankUnequalAnchors(cleaned, true);
|
|
10692
|
+
return {
|
|
10693
|
+
cleaned,
|
|
10694
|
+
image: { text: imageText, inTag: markInsideTags(imageText) },
|
|
10695
|
+
video: { text: videoText, inTag: markInsideTags(videoText) }
|
|
10696
|
+
};
|
|
10697
|
+
}
|
|
10698
|
+
function findFirstVideoPoster(prepared) {
|
|
10699
|
+
const { cleaned } = prepared;
|
|
10700
|
+
if (!cleaned) return null;
|
|
10701
|
+
let best = null;
|
|
10702
|
+
for (const hit of standaloneMatches(prepared.video, youtubeIdOf)) {
|
|
10703
|
+
best = { url: hit.url, pos: hit.pos };
|
|
10704
|
+
break;
|
|
10705
|
+
}
|
|
10706
|
+
for (const m of cleaned.matchAll(MD_LINK_RE)) {
|
|
10707
|
+
const idx = m.index ?? 0;
|
|
10708
|
+
if (idx > 0 && cleaned[idx - 1] === "!") continue;
|
|
10709
|
+
if (best && idx >= best.pos) break;
|
|
10710
|
+
const href = m[2];
|
|
10711
|
+
if (href && m[1].trim() === href) {
|
|
10712
|
+
const id = youtubeIdOf(href);
|
|
10713
|
+
if (id) {
|
|
10714
|
+
best = { url: id, pos: idx };
|
|
10715
|
+
break;
|
|
10716
|
+
}
|
|
10717
|
+
}
|
|
10718
|
+
}
|
|
10719
|
+
if (!best) return null;
|
|
10720
|
+
return { url: `https://img.youtube.com/vi/${best.url.split("?")[0]}/hqdefault.jpg`, pos: best.pos };
|
|
10721
|
+
}
|
|
10722
|
+
var NONE = { candidate: null, ambiguous: false };
|
|
10723
|
+
var AMBIGUOUS = { candidate: null, ambiguous: true };
|
|
10724
|
+
function findFirstImageCandidate(prepared, includeBareUrls = false) {
|
|
10725
|
+
const { cleaned } = prepared;
|
|
10726
|
+
if (!cleaned) return NONE;
|
|
10284
10727
|
const mdMatch = cleaned.match(MD_IMAGE_RE);
|
|
10285
10728
|
const htmlMatch = cleaned.match(HTML_IMAGE_RE);
|
|
10286
10729
|
if (mdMatch) {
|
|
10287
10730
|
const url = mdMatch[1];
|
|
10288
10731
|
if (!url || !SAFE_URL_RE.test(url) || url.includes("(")) {
|
|
10289
|
-
return
|
|
10732
|
+
return AMBIGUOUS;
|
|
10290
10733
|
}
|
|
10291
10734
|
}
|
|
10292
10735
|
const priorRegion = mdMatch ? cleaned.slice(0, mdMatch.index ?? 0) : cleaned;
|
|
10293
10736
|
if (MD_IMAGE_PRESENT_RE.test(priorRegion)) {
|
|
10294
|
-
return
|
|
10737
|
+
return AMBIGUOUS;
|
|
10295
10738
|
}
|
|
10296
10739
|
const candidates = [];
|
|
10297
10740
|
if (mdMatch) candidates.push({ url: mdMatch[1], pos: mdMatch.index ?? 0 });
|
|
@@ -10299,24 +10742,49 @@ function findFirstImageUrl(body, includeBareUrls = false) {
|
|
|
10299
10742
|
candidates.push({ url: htmlMatch[1], pos: htmlMatch.index ?? 0 });
|
|
10300
10743
|
}
|
|
10301
10744
|
if (includeBareUrls) {
|
|
10302
|
-
const bareMatch =
|
|
10303
|
-
if (bareMatch &&
|
|
10304
|
-
candidates.push(
|
|
10745
|
+
const bareMatch = firstStandalone(prepared.image, imageToken);
|
|
10746
|
+
if (bareMatch && SAFE_URL_RE.test(bareMatch.url)) {
|
|
10747
|
+
candidates.push(bareMatch);
|
|
10305
10748
|
}
|
|
10306
10749
|
const deAmp = (s) => s.trim().replace(/&/g, "&");
|
|
10307
10750
|
for (const m of cleaned.matchAll(MD_LINK_RE)) {
|
|
10308
10751
|
const idx = m.index ?? 0;
|
|
10309
10752
|
if (idx > 0 && cleaned[idx - 1] === "!") continue;
|
|
10310
10753
|
const href = m[2];
|
|
10311
|
-
if (href &&
|
|
10754
|
+
if (href && isImageHref(href) && deAmp(m[1]) === deAmp(href)) {
|
|
10312
10755
|
candidates.push({ url: href, pos: idx });
|
|
10313
10756
|
break;
|
|
10314
10757
|
}
|
|
10315
10758
|
}
|
|
10316
10759
|
}
|
|
10317
|
-
if (candidates.length === 0) return
|
|
10760
|
+
if (candidates.length === 0) return NONE;
|
|
10318
10761
|
candidates.sort((a2, b) => a2.pos - b.pos);
|
|
10319
|
-
return candidates[0]
|
|
10762
|
+
return { candidate: candidates[0], ambiguous: false };
|
|
10763
|
+
}
|
|
10764
|
+
function fastBodyImage(body, width, height, format) {
|
|
10765
|
+
const prepared = prepareBody(body);
|
|
10766
|
+
const strict = findFirstImageCandidate(prepared, false);
|
|
10767
|
+
if (strict.candidate) {
|
|
10768
|
+
return proxifyFound(strict.candidate.url, width, height, format);
|
|
10769
|
+
}
|
|
10770
|
+
if (strict.ambiguous) {
|
|
10771
|
+
return null;
|
|
10772
|
+
}
|
|
10773
|
+
const bare = findFirstImageCandidate(prepared, true).candidate;
|
|
10774
|
+
const poster = findFirstVideoPoster(prepared);
|
|
10775
|
+
if (poster && (!bare || poster.pos < bare.pos)) {
|
|
10776
|
+
return proxifyFound(proxifyImageSrc(poster.url, 0, 0, "match"), width, height, format);
|
|
10777
|
+
}
|
|
10778
|
+
return bare ? proxifyFound(bare.url, width, height, format) : null;
|
|
10779
|
+
}
|
|
10780
|
+
function firstMetaUrl(value) {
|
|
10781
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
10782
|
+
return value;
|
|
10783
|
+
}
|
|
10784
|
+
if (Array.isArray(value)) {
|
|
10785
|
+
return value.find((url) => typeof url === "string" && url.trim().length > 0);
|
|
10786
|
+
}
|
|
10787
|
+
return void 0;
|
|
10320
10788
|
}
|
|
10321
10789
|
function proxifyFound(src, width, height, format) {
|
|
10322
10790
|
const decoded = decodeEntities(src);
|
|
@@ -10325,7 +10793,7 @@ function proxifyFound(src, width, height, format) {
|
|
|
10325
10793
|
}
|
|
10326
10794
|
return proxifyImageSrc(decoded, width, height, format);
|
|
10327
10795
|
}
|
|
10328
|
-
function getImage(entry, width = 0, height = 0, format = "match") {
|
|
10796
|
+
function getImage(entry, width = 0, height = 0, format = "match", fastMode = false) {
|
|
10329
10797
|
let meta;
|
|
10330
10798
|
if (typeof entry.json_metadata === "object") {
|
|
10331
10799
|
meta = entry.json_metadata;
|
|
@@ -10336,6 +10804,14 @@ function getImage(entry, width = 0, height = 0, format = "match") {
|
|
|
10336
10804
|
meta = null;
|
|
10337
10805
|
}
|
|
10338
10806
|
}
|
|
10807
|
+
const thumbnail = firstMetaUrl(meta?.thumbnails);
|
|
10808
|
+
if (thumbnail) {
|
|
10809
|
+
const decodedThumbnail = decodeEntities(thumbnail);
|
|
10810
|
+
const proxied = isGifLink(decodedThumbnail) ? proxifyImageSrc(decodedThumbnail, 0, 0, format) : proxifyImageSrc(decodedThumbnail, width, height, format);
|
|
10811
|
+
if (proxied) {
|
|
10812
|
+
return proxied;
|
|
10813
|
+
}
|
|
10814
|
+
}
|
|
10339
10815
|
if (meta && typeof meta.image === "string" && meta.image.length > 0) {
|
|
10340
10816
|
const decodedImage = decodeEntities(meta.image);
|
|
10341
10817
|
if (isGifLink(decodedImage)) {
|
|
@@ -10356,6 +10832,9 @@ function getImage(entry, width = 0, height = 0, format = "match") {
|
|
|
10356
10832
|
}
|
|
10357
10833
|
return proxifyImageSrc(meta.image[0], width, height, format);
|
|
10358
10834
|
}
|
|
10835
|
+
if (fastMode) {
|
|
10836
|
+
return fastBodyImage(entry.body, width, height, format);
|
|
10837
|
+
}
|
|
10359
10838
|
const fast = findFirstImageUrl(entry.body);
|
|
10360
10839
|
if (fast) {
|
|
10361
10840
|
return proxifyFound(fast, width, height, format);
|
|
@@ -10399,8 +10878,12 @@ function getEntryImageRawUrl(obj) {
|
|
|
10399
10878
|
const bodySrc = findFirstImageUrl(obj.body, true);
|
|
10400
10879
|
return bodySrc ? decodeImageSrc(bodySrc) : null;
|
|
10401
10880
|
}
|
|
10402
|
-
function catchPostImage(obj, width = 0, height = 0, format = "match") {
|
|
10881
|
+
function catchPostImage(obj, width = 0, height = 0, format = "match", options = {}) {
|
|
10882
|
+
const fastMode = options.fast === true;
|
|
10403
10883
|
if (typeof obj === "string") {
|
|
10884
|
+
if (fastMode) {
|
|
10885
|
+
return fastBodyImage(obj, width, height, format);
|
|
10886
|
+
}
|
|
10404
10887
|
const fast = findFirstImageUrl(obj);
|
|
10405
10888
|
if (fast) {
|
|
10406
10889
|
return proxifyFound(fast, width, height, format);
|
|
@@ -10420,12 +10903,12 @@ function catchPostImage(obj, width = 0, height = 0, format = "match") {
|
|
|
10420
10903
|
}
|
|
10421
10904
|
return null;
|
|
10422
10905
|
}
|
|
10423
|
-
const key = `${makeEntryCacheKey(obj)}-${width}x${height}-${format}`;
|
|
10906
|
+
const key = `${makeEntryCacheKey(obj)}-${width}x${height}-${format}${fastMode ? "-fast" : ""}`;
|
|
10424
10907
|
const item = cacheGet(key);
|
|
10425
|
-
if (item) {
|
|
10908
|
+
if (item !== void 0) {
|
|
10426
10909
|
return item;
|
|
10427
10910
|
}
|
|
10428
|
-
const res = getImage(obj, width, height, format);
|
|
10911
|
+
const res = getImage(obj, width, height, format, fastMode);
|
|
10429
10912
|
cacheSet(key, res);
|
|
10430
10913
|
return res;
|
|
10431
10914
|
}
|