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