@ecency/render-helper 2.5.27 → 2.5.29
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 +506 -26
- package/dist/browser/index.js.map +1 -1
- package/dist/node/index.cjs +508 -27
- package/dist/node/index.cjs.map +1 -1
- package/dist/node/index.mjs +508 -26
- package/dist/node/index.mjs.map +1 -1
- package/package.json +2 -3
package/dist/node/index.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var xmldom = require('@xmldom/xmldom');
|
|
4
|
-
var
|
|
4
|
+
var entities$1 = require('entities');
|
|
5
5
|
var xss = require('xss');
|
|
6
6
|
var querystring = require('querystring');
|
|
7
7
|
var lruCache = require('lru-cache');
|
|
@@ -28,7 +28,6 @@ function _interopNamespace(e) {
|
|
|
28
28
|
return Object.freeze(n);
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
var he2__default = /*#__PURE__*/_interopDefault(he2);
|
|
32
31
|
var xss__default = /*#__PURE__*/_interopDefault(xss);
|
|
33
32
|
var querystring__default = /*#__PURE__*/_interopDefault(querystring);
|
|
34
33
|
var htmlparser2__namespace = /*#__PURE__*/_interopNamespace(htmlparser2);
|
|
@@ -308,8 +307,19 @@ function createParser() {
|
|
|
308
307
|
});
|
|
309
308
|
}
|
|
310
309
|
var DOMParser = createParser();
|
|
310
|
+
var LEADING_ZEROS_DEC = /�+(?=[0-9])/g;
|
|
311
|
+
var LEADING_ZEROS_HEX = /�+(?=[0-9a-f])/gi;
|
|
312
|
+
var OVERLONG_NUMERIC_REF = /&#(?:x[0-9a-f]{256,}|[0-9]{309,});?/gi;
|
|
313
|
+
function decodeEntities(value) {
|
|
314
|
+
const safe = value.replace(LEADING_ZEROS_DEC, "&#").replace(LEADING_ZEROS_HEX, (m) => m.slice(0, 3)).replace(OVERLONG_NUMERIC_REF, "\uFFFD");
|
|
315
|
+
try {
|
|
316
|
+
return entities$1.decodeHTML(safe);
|
|
317
|
+
} catch {
|
|
318
|
+
return safe;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
311
321
|
function decodeImageSrc(src) {
|
|
312
|
-
const entityDecoded =
|
|
322
|
+
const entityDecoded = decodeEntities(src);
|
|
313
323
|
try {
|
|
314
324
|
return decodeURIComponent(entityDecoded).trim();
|
|
315
325
|
} catch {
|
|
@@ -764,7 +774,7 @@ var isSafeNavValue = (value) => {
|
|
|
764
774
|
const isRelative = /^(\/\/|\/[^/]?|#|\?|[a-z0-9._\-]+(\/|$))/i.test(trimmed);
|
|
765
775
|
return isSafeScheme || isRelative;
|
|
766
776
|
};
|
|
767
|
-
var
|
|
777
|
+
var decodeEntities2 = (input) => input.replace(/&#(\d+);?/g, (_, dec) => String.fromCodePoint(Number(dec))).replace(/&#x([0-9a-f]+);?/gi, (_, hex) => String.fromCodePoint(parseInt(hex, 16)));
|
|
768
778
|
var isProxyPSrcset = (srcset) => {
|
|
769
779
|
const base = trimTrailingSlash(getProxyBase());
|
|
770
780
|
const candidates = srcset.split(",").map((c) => c.trim().split(/\s+/)[0]).filter(Boolean);
|
|
@@ -778,7 +788,7 @@ function sanitizeHtml(html) {
|
|
|
778
788
|
css: false,
|
|
779
789
|
// block style attrs entirely for safety
|
|
780
790
|
onTagAttr: (tag, name, value) => {
|
|
781
|
-
const decoded =
|
|
791
|
+
const decoded = decodeEntities2(value.trim());
|
|
782
792
|
const decodedLower = decoded.toLowerCase();
|
|
783
793
|
if (name.startsWith("on")) return "";
|
|
784
794
|
if (tag === "img" && name === "src" && !/^https?:\/\//.test(decodedLower)) return "";
|
|
@@ -10251,6 +10261,8 @@ function markdown2Html(obj, forApp = true, _webp = false, parentDomain = "ecency
|
|
|
10251
10261
|
cacheSet(key, res);
|
|
10252
10262
|
return res;
|
|
10253
10263
|
}
|
|
10264
|
+
|
|
10265
|
+
// src/catch-post-image.ts
|
|
10254
10266
|
var gifLinkRegex = /\.(gif)$/i;
|
|
10255
10267
|
function isGifLink(link) {
|
|
10256
10268
|
return gifLinkRegex.test(link);
|
|
@@ -10258,28 +10270,455 @@ function isGifLink(link) {
|
|
|
10258
10270
|
var BACKTICK_FENCE_RE = /```[\s\S]*?```/g;
|
|
10259
10271
|
var TILDE_FENCE_RE = /~~~[\s\S]*?~~~/g;
|
|
10260
10272
|
var INLINE_CODE_RE = /`[^`\n]*`/g;
|
|
10261
|
-
var
|
|
10273
|
+
var OPEN_TAG_NAME_END = /[\t\f\r />]/;
|
|
10274
|
+
var CLOSE_TAG_NAME_END = /[\s>]/;
|
|
10275
|
+
function isWholeTagName(lower, idx, end) {
|
|
10276
|
+
const next = lower[idx];
|
|
10277
|
+
return next === void 0 || end.test(next);
|
|
10278
|
+
}
|
|
10279
|
+
function findTag(lower, tag, from, end) {
|
|
10280
|
+
let at = lower.indexOf(tag, from);
|
|
10281
|
+
while (at !== -1 && !isWholeTagName(lower, at + tag.length, end)) {
|
|
10282
|
+
at = lower.indexOf(tag, at + tag.length);
|
|
10283
|
+
}
|
|
10284
|
+
return at;
|
|
10285
|
+
}
|
|
10286
|
+
function findOpenTagEnd(lower, openAt) {
|
|
10287
|
+
let quote = "";
|
|
10288
|
+
for (let i2 = openAt + 1; i2 < lower.length; i2++) {
|
|
10289
|
+
const c = lower[i2];
|
|
10290
|
+
if (c === "\n") return NaN;
|
|
10291
|
+
if (quote) {
|
|
10292
|
+
if (c === quote) quote = "";
|
|
10293
|
+
} else if (c === '"' || c === "'") {
|
|
10294
|
+
quote = c;
|
|
10295
|
+
} else if (c === ">") {
|
|
10296
|
+
return lower[i2 - 1] === "/" ? NaN : i2;
|
|
10297
|
+
}
|
|
10298
|
+
}
|
|
10299
|
+
return -1;
|
|
10300
|
+
}
|
|
10301
|
+
var HTML_BLOCK_TAGS = /* @__PURE__ */ new Set([
|
|
10302
|
+
"article",
|
|
10303
|
+
"aside",
|
|
10304
|
+
"button",
|
|
10305
|
+
"blockquote",
|
|
10306
|
+
"body",
|
|
10307
|
+
"canvas",
|
|
10308
|
+
"caption",
|
|
10309
|
+
"col",
|
|
10310
|
+
"colgroup",
|
|
10311
|
+
"dd",
|
|
10312
|
+
"div",
|
|
10313
|
+
"dl",
|
|
10314
|
+
"dt",
|
|
10315
|
+
"embed",
|
|
10316
|
+
"fieldset",
|
|
10317
|
+
"figcaption",
|
|
10318
|
+
"figure",
|
|
10319
|
+
"footer",
|
|
10320
|
+
"form",
|
|
10321
|
+
"h1",
|
|
10322
|
+
"h2",
|
|
10323
|
+
"h3",
|
|
10324
|
+
"h4",
|
|
10325
|
+
"h5",
|
|
10326
|
+
"h6",
|
|
10327
|
+
"header",
|
|
10328
|
+
"hgroup",
|
|
10329
|
+
"hr",
|
|
10330
|
+
"iframe",
|
|
10331
|
+
"li",
|
|
10332
|
+
"map",
|
|
10333
|
+
"object",
|
|
10334
|
+
"ol",
|
|
10335
|
+
"output",
|
|
10336
|
+
"p",
|
|
10337
|
+
"pre",
|
|
10338
|
+
"progress",
|
|
10339
|
+
"script",
|
|
10340
|
+
"section",
|
|
10341
|
+
"style",
|
|
10342
|
+
"table",
|
|
10343
|
+
"tbody",
|
|
10344
|
+
"td",
|
|
10345
|
+
"textarea",
|
|
10346
|
+
"tfoot",
|
|
10347
|
+
"th",
|
|
10348
|
+
"tr",
|
|
10349
|
+
"thead",
|
|
10350
|
+
"ul",
|
|
10351
|
+
"video"
|
|
10352
|
+
]);
|
|
10353
|
+
var HTML_BLOCK_LINE_RE = /^ {0,3}<(?:[!?]|([a-z]{1,15})[\s/>]|\/([a-z]{1,15})[\s>])/;
|
|
10354
|
+
var BLOCKQUOTE_PREFIX_RE = /^ {0,3}> ?/;
|
|
10355
|
+
var LIST_PREFIX_RE = /^(?:[-*+]|\d{1,9}[.)]) +/;
|
|
10356
|
+
function markLines(lower) {
|
|
10357
|
+
const block2 = new Uint8Array(lower.length);
|
|
10358
|
+
const code2 = new Uint8Array(lower.length);
|
|
10359
|
+
let inBlock = false;
|
|
10360
|
+
let listIndent = 0;
|
|
10361
|
+
let nestedItem = false;
|
|
10362
|
+
let lineStart = 0;
|
|
10363
|
+
while (lineStart <= lower.length) {
|
|
10364
|
+
let lineEnd = lower.indexOf("\n", lineStart);
|
|
10365
|
+
if (lineEnd === -1) lineEnd = lower.length;
|
|
10366
|
+
let line = lower.slice(lineStart, lineEnd);
|
|
10367
|
+
if (inBlock) {
|
|
10368
|
+
if (line.trim() === "") {
|
|
10369
|
+
inBlock = false;
|
|
10370
|
+
listIndent = 0;
|
|
10371
|
+
nestedItem = false;
|
|
10372
|
+
} else {
|
|
10373
|
+
block2.fill(1, lineStart, lineEnd);
|
|
10374
|
+
}
|
|
10375
|
+
lineStart = lineEnd + 1;
|
|
10376
|
+
continue;
|
|
10377
|
+
}
|
|
10378
|
+
let stripped = 0;
|
|
10379
|
+
let sawList = false;
|
|
10380
|
+
let lastWasList = false;
|
|
10381
|
+
let inlineRemainder = false;
|
|
10382
|
+
for (; ; ) {
|
|
10383
|
+
const bq = BLOCKQUOTE_PREFIX_RE.exec(line);
|
|
10384
|
+
if (bq) {
|
|
10385
|
+
line = line.slice(bq[0].length);
|
|
10386
|
+
stripped += bq[0].length;
|
|
10387
|
+
lastWasList = false;
|
|
10388
|
+
inlineRemainder = false;
|
|
10389
|
+
continue;
|
|
10390
|
+
}
|
|
10391
|
+
const lm = LIST_PREFIX_RE.exec(line);
|
|
10392
|
+
if (lm) {
|
|
10393
|
+
line = line.slice(lm[0].length);
|
|
10394
|
+
stripped += lm[0].length;
|
|
10395
|
+
if (lastWasList) inlineRemainder = true;
|
|
10396
|
+
sawList = true;
|
|
10397
|
+
lastWasList = true;
|
|
10398
|
+
continue;
|
|
10399
|
+
}
|
|
10400
|
+
break;
|
|
10401
|
+
}
|
|
10402
|
+
if (sawList) {
|
|
10403
|
+
listIndent = stripped;
|
|
10404
|
+
nestedItem = inlineRemainder;
|
|
10405
|
+
} else if (listIndent > 0 && line.trim() !== "") {
|
|
10406
|
+
let indent = 0;
|
|
10407
|
+
while (indent < listIndent && line[indent] === " ") indent++;
|
|
10408
|
+
if (indent >= Math.min(listIndent, 2)) {
|
|
10409
|
+
line = line.slice(indent);
|
|
10410
|
+
inlineRemainder = nestedItem;
|
|
10411
|
+
} else {
|
|
10412
|
+
listIndent = 0;
|
|
10413
|
+
nestedItem = false;
|
|
10414
|
+
}
|
|
10415
|
+
}
|
|
10416
|
+
const blank = line.trim() === "";
|
|
10417
|
+
if (blank) {
|
|
10418
|
+
inBlock = false;
|
|
10419
|
+
listIndent = 0;
|
|
10420
|
+
nestedItem = false;
|
|
10421
|
+
} else if (inlineRemainder) {
|
|
10422
|
+
inBlock = false;
|
|
10423
|
+
} else if (!inBlock && /^(?: {4}|\t)/.test(line)) {
|
|
10424
|
+
code2.fill(1, lineStart, lineEnd);
|
|
10425
|
+
} else if (!inBlock) {
|
|
10426
|
+
const m = HTML_BLOCK_LINE_RE.exec(line);
|
|
10427
|
+
if (m) {
|
|
10428
|
+
const tag = m[1] ?? m[2];
|
|
10429
|
+
inBlock = tag === void 0 || HTML_BLOCK_TAGS.has(tag);
|
|
10430
|
+
}
|
|
10431
|
+
}
|
|
10432
|
+
if (inBlock && !blank) block2.fill(1, lineStart, lineEnd);
|
|
10433
|
+
lineStart = lineEnd + 1;
|
|
10434
|
+
}
|
|
10435
|
+
return { block: block2, code: code2 };
|
|
10436
|
+
}
|
|
10437
|
+
var blankChars = (s) => s.replace(/[^\n]/g, " ");
|
|
10438
|
+
function blankMatches(text3, re) {
|
|
10439
|
+
return text3.replace(re, blankChars);
|
|
10440
|
+
}
|
|
10441
|
+
function blankSpans(input, open, close, tagNames, blockMask) {
|
|
10442
|
+
const { text: text3, lower } = input;
|
|
10443
|
+
const findOpen = (from2) => {
|
|
10444
|
+
if (!tagNames) return lower.indexOf(open, from2);
|
|
10445
|
+
let at = findTag(lower, open, from2, OPEN_TAG_NAME_END);
|
|
10446
|
+
while (at !== -1 && (Number.isNaN(findOpenTagEnd(lower, at)) || blockMask !== null && !blockMask[at])) {
|
|
10447
|
+
at = findTag(lower, open, at + open.length, OPEN_TAG_NAME_END);
|
|
10448
|
+
}
|
|
10449
|
+
return at;
|
|
10450
|
+
};
|
|
10451
|
+
const findClose = (from2) => tagNames ? findTag(lower, close, from2, CLOSE_TAG_NAME_END) : lower.indexOf(close, from2);
|
|
10452
|
+
let start = findOpen(0);
|
|
10453
|
+
if (start === -1) return input;
|
|
10454
|
+
const textParts = [];
|
|
10455
|
+
const lowerParts = [];
|
|
10456
|
+
let from = 0;
|
|
10457
|
+
while (start !== -1) {
|
|
10458
|
+
const end = findClose(start + open.length);
|
|
10459
|
+
let to;
|
|
10460
|
+
if (end === -1) {
|
|
10461
|
+
to = text3.length;
|
|
10462
|
+
} else if (tagNames) {
|
|
10463
|
+
const gt = lower.indexOf(">", end + close.length);
|
|
10464
|
+
to = gt === -1 ? text3.length : gt + 1;
|
|
10465
|
+
} else {
|
|
10466
|
+
to = end + close.length;
|
|
10467
|
+
}
|
|
10468
|
+
const blanked = blankChars(lower.slice(start, to));
|
|
10469
|
+
textParts.push(text3.slice(from, start), blanked);
|
|
10470
|
+
lowerParts.push(lower.slice(from, start), blanked);
|
|
10471
|
+
from = to;
|
|
10472
|
+
start = to >= text3.length ? -1 : findOpen(to);
|
|
10473
|
+
}
|
|
10474
|
+
textParts.push(text3.slice(from));
|
|
10475
|
+
lowerParts.push(lower.slice(from));
|
|
10476
|
+
return { text: textParts.join(""), lower: lowerParts.join("") };
|
|
10477
|
+
}
|
|
10478
|
+
function blankMasked(input, mask) {
|
|
10479
|
+
let text3 = "";
|
|
10480
|
+
let lower = "";
|
|
10481
|
+
let from = 0;
|
|
10482
|
+
for (let i2 = 0; i2 < mask.length; i2++) {
|
|
10483
|
+
if (!mask[i2]) continue;
|
|
10484
|
+
let j = i2;
|
|
10485
|
+
while (j < mask.length && mask[j]) j++;
|
|
10486
|
+
text3 += input.text.slice(from, i2) + blankChars(input.text.slice(i2, j));
|
|
10487
|
+
lower += input.lower.slice(from, i2) + blankChars(input.lower.slice(i2, j));
|
|
10488
|
+
from = j;
|
|
10489
|
+
i2 = j;
|
|
10490
|
+
}
|
|
10491
|
+
if (from === 0) return input;
|
|
10492
|
+
return { text: text3 + input.text.slice(from), lower: lower + input.lower.slice(from) };
|
|
10493
|
+
}
|
|
10494
|
+
function stripHiddenRegions(text3) {
|
|
10495
|
+
let spellings = { text: text3, lower: text3.toLowerCase() };
|
|
10496
|
+
const { block: blockMask, code: codeMask } = markLines(spellings.lower);
|
|
10497
|
+
spellings = blankMasked(spellings, codeMask);
|
|
10498
|
+
spellings = blankSpans(spellings, "<!--", "-->", false, null);
|
|
10499
|
+
spellings = blankSpans(spellings, "<style", "</style", true, null);
|
|
10500
|
+
spellings = blankSpans(spellings, "<pre", "</pre", true, blockMask);
|
|
10501
|
+
spellings = blankSpans(spellings, "<code", "</code", true, blockMask);
|
|
10502
|
+
return spellings.text;
|
|
10503
|
+
}
|
|
10262
10504
|
var MD_IMAGE_RE = /!\[[^[\]]*\]\(\s*([^)\s]{1,2048})(?:\s+["'][^"']*["'])?\s*\)/;
|
|
10263
10505
|
var MD_IMAGE_PRESENT_RE = /!\[[^[\]]*\]\(\s*[^\s)]/;
|
|
10264
10506
|
var HTML_IMAGE_RE = /<img\b[^>]*?\bsrc\s*=\s*["']([^"']+)["']/i;
|
|
10265
|
-
var
|
|
10507
|
+
var URL_TOKEN_RE = /https?:\/\/[^\s<>"'()[\]]+/gi;
|
|
10508
|
+
var IMAGE_EXT_G = /\.(?:tiff?|jpe?g|gif|png|svg|ico|heic|webp|arw)/gi;
|
|
10509
|
+
var YOUTUBE_ID_RE = /^[^"&?/\s]{11}$/;
|
|
10510
|
+
function imageToken(token) {
|
|
10511
|
+
let end = -1;
|
|
10512
|
+
for (const m of token.matchAll(IMAGE_EXT_G)) {
|
|
10513
|
+
end = (m.index ?? 0) + m[0].length;
|
|
10514
|
+
}
|
|
10515
|
+
if (end === -1) return null;
|
|
10516
|
+
if (end < token.length && (token[end] === "?" || token[end] === "#")) end = token.length;
|
|
10517
|
+
const url = token.slice(0, end);
|
|
10518
|
+
return SAFE_URL_RE.test(url) ? url : null;
|
|
10519
|
+
}
|
|
10520
|
+
function youtubeIdOf(url) {
|
|
10521
|
+
const m = /^https?:\/\/([^/?#]+)/i.exec(url);
|
|
10522
|
+
if (!m) return null;
|
|
10523
|
+
const host = m[1].toLowerCase();
|
|
10524
|
+
const isShort = host === "youtu.be";
|
|
10525
|
+
const isFull = host === "youtube.com" || host.endsWith(".youtube.com");
|
|
10526
|
+
if (!isShort && !isFull) return null;
|
|
10527
|
+
const rest = url.slice(m[0].length);
|
|
10528
|
+
const hashAt = rest.indexOf("#");
|
|
10529
|
+
const beforeHash = hashAt === -1 ? rest : rest.slice(0, hashAt);
|
|
10530
|
+
const qAt = beforeHash.indexOf("?");
|
|
10531
|
+
const path = qAt === -1 ? beforeHash : beforeHash.slice(0, qAt);
|
|
10532
|
+
const query = qAt === -1 ? "" : beforeHash.slice(qAt + 1);
|
|
10533
|
+
const candidate = (value) => {
|
|
10534
|
+
const id = value === void 0 ? "" : value.slice(0, 11);
|
|
10535
|
+
return YOUTUBE_ID_RE.test(id) ? id : null;
|
|
10536
|
+
};
|
|
10537
|
+
if (isFull && query) {
|
|
10538
|
+
for (const part of query.split("&")) {
|
|
10539
|
+
if (part.startsWith("v=")) {
|
|
10540
|
+
const id = candidate(part.slice(2));
|
|
10541
|
+
if (id) return id;
|
|
10542
|
+
}
|
|
10543
|
+
}
|
|
10544
|
+
}
|
|
10545
|
+
const segments = path.split("/").filter((seg) => seg.length > 0);
|
|
10546
|
+
if (isShort) return candidate(segments[0]);
|
|
10547
|
+
if (segments.length >= 2 && ["v", "e", "embed", "shorts"].includes(segments[0].toLowerCase())) {
|
|
10548
|
+
return candidate(segments[1]);
|
|
10549
|
+
}
|
|
10550
|
+
if (segments.length >= 3) return candidate(segments[segments.length - 1]);
|
|
10551
|
+
return null;
|
|
10552
|
+
}
|
|
10553
|
+
function isAutolinkAt(text3, idx) {
|
|
10554
|
+
return /^https?:\/\//i.test(text3.slice(idx, idx + 8));
|
|
10555
|
+
}
|
|
10556
|
+
function markInsideTags(text3) {
|
|
10557
|
+
const marks = new Uint8Array(text3.length);
|
|
10558
|
+
let inTag = false;
|
|
10559
|
+
let quote = "";
|
|
10560
|
+
for (let i2 = 0; i2 < text3.length; i2++) {
|
|
10561
|
+
const c = text3[i2];
|
|
10562
|
+
if (!inTag) {
|
|
10563
|
+
if (c === "<" && i2 + 1 < text3.length && /[A-Za-z/!?]/.test(text3[i2 + 1]) && !isAutolinkAt(text3, i2 + 1)) {
|
|
10564
|
+
inTag = true;
|
|
10565
|
+
marks[i2] = 1;
|
|
10566
|
+
}
|
|
10567
|
+
continue;
|
|
10568
|
+
}
|
|
10569
|
+
marks[i2] = 1;
|
|
10570
|
+
if (quote) {
|
|
10571
|
+
if (c === quote) quote = "";
|
|
10572
|
+
} else if (c === '"' || c === "'") {
|
|
10573
|
+
quote = c;
|
|
10574
|
+
} else if (c === ">") {
|
|
10575
|
+
inTag = false;
|
|
10576
|
+
}
|
|
10577
|
+
}
|
|
10578
|
+
return marks;
|
|
10579
|
+
}
|
|
10580
|
+
function isStandalone(scan, idx) {
|
|
10581
|
+
if (scan.inTag[idx]) return false;
|
|
10582
|
+
if (idx === 0) return true;
|
|
10583
|
+
const text3 = scan.text;
|
|
10584
|
+
const prev = text3[idx - 1];
|
|
10585
|
+
if (/[\w/.:%?&=#[-]/.test(prev)) return false;
|
|
10586
|
+
const prev2 = idx > 1 ? text3[idx - 2] : "";
|
|
10587
|
+
if (prev === "(" && prev2 === "]") return false;
|
|
10588
|
+
return true;
|
|
10589
|
+
}
|
|
10590
|
+
function* standaloneMatches(scan, classify) {
|
|
10591
|
+
for (const m of scan.text.matchAll(URL_TOKEN_RE)) {
|
|
10592
|
+
const idx = m.index ?? 0;
|
|
10593
|
+
if (!isStandalone(scan, idx)) continue;
|
|
10594
|
+
const value = classify(m[0]);
|
|
10595
|
+
if (value !== null) yield { url: value, pos: idx };
|
|
10596
|
+
}
|
|
10597
|
+
}
|
|
10598
|
+
function firstStandalone(scan, classify) {
|
|
10599
|
+
for (const hit of standaloneMatches(scan, classify)) return hit;
|
|
10600
|
+
return null;
|
|
10601
|
+
}
|
|
10602
|
+
var HREF_ATTR_RE = /\shref\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'>]+))/i;
|
|
10603
|
+
function hasGluedAttribute(tag) {
|
|
10604
|
+
let quote = "";
|
|
10605
|
+
for (let i2 = 0; i2 < tag.length; i2++) {
|
|
10606
|
+
const c = tag[i2];
|
|
10607
|
+
if (quote) {
|
|
10608
|
+
if (c === quote) {
|
|
10609
|
+
quote = "";
|
|
10610
|
+
const next = tag[i2 + 1];
|
|
10611
|
+
if (next !== void 0 && /[A-Za-z]/.test(next)) return true;
|
|
10612
|
+
}
|
|
10613
|
+
} else if (c === '"' || c === "'") {
|
|
10614
|
+
quote = c;
|
|
10615
|
+
}
|
|
10616
|
+
}
|
|
10617
|
+
return false;
|
|
10618
|
+
}
|
|
10266
10619
|
var MD_LINK_RE = /\[([^[\]]*)\]\(\s*([^)\s[]+)(?:\s+["'][^"']*["'])?\s*\)/g;
|
|
10267
|
-
var IMG_HREF_RE = /https?:\/\/.*\.(?:tiff?|jpe?g|gif|png|svg|ico|heic|webp|arw)/i;
|
|
10268
10620
|
var SAFE_URL_RE = /^https?:\/\//i;
|
|
10621
|
+
var IMG_EXT_RE = /\.(?:tiff?|jpe?g|gif|png|svg|ico|heic|webp|arw)/i;
|
|
10622
|
+
var isImageHref = (href) => SAFE_URL_RE.test(href) && IMG_EXT_RE.test(href);
|
|
10269
10623
|
function findFirstImageUrl(body, includeBareUrls = false) {
|
|
10270
|
-
|
|
10271
|
-
|
|
10624
|
+
return findFirstImageCandidate(prepareBody(body), includeBareUrls).candidate?.url ?? null;
|
|
10625
|
+
}
|
|
10626
|
+
function stripCodeRegions(body) {
|
|
10627
|
+
let text3 = blankMatches(body, BACKTICK_FENCE_RE);
|
|
10628
|
+
text3 = blankMatches(text3, TILDE_FENCE_RE);
|
|
10629
|
+
text3 = blankMatches(text3, INLINE_CODE_RE);
|
|
10630
|
+
return stripHiddenRegions(text3);
|
|
10631
|
+
}
|
|
10632
|
+
function blankUnequalAnchors(cleaned, textContent) {
|
|
10633
|
+
const lower = cleaned.toLowerCase();
|
|
10634
|
+
const parts = [];
|
|
10635
|
+
let from = 0;
|
|
10636
|
+
let at = findTag(lower, "<a", 0, OPEN_TAG_NAME_END);
|
|
10637
|
+
while (at !== -1) {
|
|
10638
|
+
const gt = findOpenTagEnd(lower, at);
|
|
10639
|
+
if (Number.isNaN(gt) || gt === -1 || hasGluedAttribute(cleaned.slice(at, gt))) {
|
|
10640
|
+
at = findTag(lower, "<a", at + 2, OPEN_TAG_NAME_END);
|
|
10641
|
+
continue;
|
|
10642
|
+
}
|
|
10643
|
+
const closeAt = findTag(lower, "</a", gt + 1, CLOSE_TAG_NAME_END);
|
|
10644
|
+
const innerEnd = closeAt === -1 ? cleaned.length : closeAt;
|
|
10645
|
+
let spanEnd = cleaned.length;
|
|
10646
|
+
if (closeAt !== -1) {
|
|
10647
|
+
const closeGt = lower.indexOf(">", closeAt + 3);
|
|
10648
|
+
spanEnd = closeGt === -1 ? cleaned.length : closeGt + 1;
|
|
10649
|
+
}
|
|
10650
|
+
const hrefMatch = HREF_ATTR_RE.exec(cleaned.slice(at, gt));
|
|
10651
|
+
const href = hrefMatch ? hrefMatch[1] ?? hrefMatch[2] ?? hrefMatch[3] ?? "" : "";
|
|
10652
|
+
const inner = cleaned.slice(gt + 1, innerEnd);
|
|
10653
|
+
let text3;
|
|
10654
|
+
if (textContent) {
|
|
10655
|
+
text3 = stripHtmlTags(inner);
|
|
10656
|
+
} else {
|
|
10657
|
+
const firstTag = inner.search(/<[A-Za-z/!]/);
|
|
10658
|
+
text3 = firstTag === -1 ? inner : inner.slice(0, firstTag);
|
|
10659
|
+
}
|
|
10660
|
+
if (!href || decodeEntities(text3.trim()) !== decodeEntities(href.trim())) {
|
|
10661
|
+
parts.push(cleaned.slice(from, at), blankChars(cleaned.slice(at, spanEnd)));
|
|
10662
|
+
from = spanEnd;
|
|
10663
|
+
}
|
|
10664
|
+
at = spanEnd >= cleaned.length ? -1 : findTag(lower, "<a", spanEnd, OPEN_TAG_NAME_END);
|
|
10665
|
+
}
|
|
10666
|
+
if (parts.length === 0) return cleaned;
|
|
10667
|
+
parts.push(cleaned.slice(from));
|
|
10668
|
+
return parts.join("");
|
|
10669
|
+
}
|
|
10670
|
+
var EMPTY_SCAN = { text: "", inTag: new Uint8Array(0) };
|
|
10671
|
+
function prepareBody(body) {
|
|
10672
|
+
const cleaned = body ? stripCodeRegions(body) : "";
|
|
10673
|
+
if (!cleaned) return { cleaned, image: EMPTY_SCAN, video: EMPTY_SCAN };
|
|
10674
|
+
const imageText = blankUnequalAnchors(cleaned, false);
|
|
10675
|
+
const videoText = blankUnequalAnchors(cleaned, true);
|
|
10676
|
+
return {
|
|
10677
|
+
cleaned,
|
|
10678
|
+
image: { text: imageText, inTag: markInsideTags(imageText) },
|
|
10679
|
+
video: { text: videoText, inTag: markInsideTags(videoText) }
|
|
10680
|
+
};
|
|
10681
|
+
}
|
|
10682
|
+
function findFirstVideoPoster(prepared) {
|
|
10683
|
+
const { cleaned } = prepared;
|
|
10684
|
+
if (!cleaned) return null;
|
|
10685
|
+
let best = null;
|
|
10686
|
+
for (const hit of standaloneMatches(prepared.video, youtubeIdOf)) {
|
|
10687
|
+
best = { url: hit.url, pos: hit.pos };
|
|
10688
|
+
break;
|
|
10689
|
+
}
|
|
10690
|
+
for (const m of cleaned.matchAll(MD_LINK_RE)) {
|
|
10691
|
+
const idx = m.index ?? 0;
|
|
10692
|
+
if (idx > 0 && cleaned[idx - 1] === "!") continue;
|
|
10693
|
+
if (best && idx >= best.pos) break;
|
|
10694
|
+
const href = m[2];
|
|
10695
|
+
if (href && m[1].trim() === href) {
|
|
10696
|
+
const id = youtubeIdOf(href);
|
|
10697
|
+
if (id) {
|
|
10698
|
+
best = { url: id, pos: idx };
|
|
10699
|
+
break;
|
|
10700
|
+
}
|
|
10701
|
+
}
|
|
10702
|
+
}
|
|
10703
|
+
if (!best) return null;
|
|
10704
|
+
return { url: `https://img.youtube.com/vi/${best.url.split("?")[0]}/hqdefault.jpg`, pos: best.pos };
|
|
10705
|
+
}
|
|
10706
|
+
var NONE = { candidate: null, ambiguous: false };
|
|
10707
|
+
var AMBIGUOUS = { candidate: null, ambiguous: true };
|
|
10708
|
+
function findFirstImageCandidate(prepared, includeBareUrls = false) {
|
|
10709
|
+
const { cleaned } = prepared;
|
|
10710
|
+
if (!cleaned) return NONE;
|
|
10272
10711
|
const mdMatch = cleaned.match(MD_IMAGE_RE);
|
|
10273
10712
|
const htmlMatch = cleaned.match(HTML_IMAGE_RE);
|
|
10274
10713
|
if (mdMatch) {
|
|
10275
10714
|
const url = mdMatch[1];
|
|
10276
10715
|
if (!url || !SAFE_URL_RE.test(url) || url.includes("(")) {
|
|
10277
|
-
return
|
|
10716
|
+
return AMBIGUOUS;
|
|
10278
10717
|
}
|
|
10279
10718
|
}
|
|
10280
10719
|
const priorRegion = mdMatch ? cleaned.slice(0, mdMatch.index ?? 0) : cleaned;
|
|
10281
10720
|
if (MD_IMAGE_PRESENT_RE.test(priorRegion)) {
|
|
10282
|
-
return
|
|
10721
|
+
return AMBIGUOUS;
|
|
10283
10722
|
}
|
|
10284
10723
|
const candidates = [];
|
|
10285
10724
|
if (mdMatch) candidates.push({ url: mdMatch[1], pos: mdMatch.index ?? 0 });
|
|
@@ -10287,33 +10726,58 @@ function findFirstImageUrl(body, includeBareUrls = false) {
|
|
|
10287
10726
|
candidates.push({ url: htmlMatch[1], pos: htmlMatch.index ?? 0 });
|
|
10288
10727
|
}
|
|
10289
10728
|
if (includeBareUrls) {
|
|
10290
|
-
const bareMatch =
|
|
10291
|
-
if (bareMatch &&
|
|
10292
|
-
candidates.push(
|
|
10729
|
+
const bareMatch = firstStandalone(prepared.image, imageToken);
|
|
10730
|
+
if (bareMatch && SAFE_URL_RE.test(bareMatch.url)) {
|
|
10731
|
+
candidates.push(bareMatch);
|
|
10293
10732
|
}
|
|
10294
10733
|
const deAmp = (s) => s.trim().replace(/&/g, "&");
|
|
10295
10734
|
for (const m of cleaned.matchAll(MD_LINK_RE)) {
|
|
10296
10735
|
const idx = m.index ?? 0;
|
|
10297
10736
|
if (idx > 0 && cleaned[idx - 1] === "!") continue;
|
|
10298
10737
|
const href = m[2];
|
|
10299
|
-
if (href &&
|
|
10738
|
+
if (href && isImageHref(href) && deAmp(m[1]) === deAmp(href)) {
|
|
10300
10739
|
candidates.push({ url: href, pos: idx });
|
|
10301
10740
|
break;
|
|
10302
10741
|
}
|
|
10303
10742
|
}
|
|
10304
10743
|
}
|
|
10305
|
-
if (candidates.length === 0) return
|
|
10744
|
+
if (candidates.length === 0) return NONE;
|
|
10306
10745
|
candidates.sort((a2, b) => a2.pos - b.pos);
|
|
10307
|
-
return candidates[0]
|
|
10746
|
+
return { candidate: candidates[0], ambiguous: false };
|
|
10747
|
+
}
|
|
10748
|
+
function fastBodyImage(body, width, height, format) {
|
|
10749
|
+
const prepared = prepareBody(body);
|
|
10750
|
+
const strict = findFirstImageCandidate(prepared, false);
|
|
10751
|
+
if (strict.candidate) {
|
|
10752
|
+
return proxifyFound(strict.candidate.url, width, height, format);
|
|
10753
|
+
}
|
|
10754
|
+
if (strict.ambiguous) {
|
|
10755
|
+
return null;
|
|
10756
|
+
}
|
|
10757
|
+
const bare = findFirstImageCandidate(prepared, true).candidate;
|
|
10758
|
+
const poster = findFirstVideoPoster(prepared);
|
|
10759
|
+
if (poster && (!bare || poster.pos < bare.pos)) {
|
|
10760
|
+
return proxifyFound(proxifyImageSrc(poster.url, 0, 0, "match"), width, height, format);
|
|
10761
|
+
}
|
|
10762
|
+
return bare ? proxifyFound(bare.url, width, height, format) : null;
|
|
10763
|
+
}
|
|
10764
|
+
function firstMetaUrl(value) {
|
|
10765
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
10766
|
+
return value;
|
|
10767
|
+
}
|
|
10768
|
+
if (Array.isArray(value)) {
|
|
10769
|
+
return value.find((url) => typeof url === "string" && url.trim().length > 0);
|
|
10770
|
+
}
|
|
10771
|
+
return void 0;
|
|
10308
10772
|
}
|
|
10309
10773
|
function proxifyFound(src, width, height, format) {
|
|
10310
|
-
const decoded =
|
|
10774
|
+
const decoded = decodeEntities(src);
|
|
10311
10775
|
if (isGifLink(decoded)) {
|
|
10312
10776
|
return proxifyImageSrc(decoded, 0, 0, format);
|
|
10313
10777
|
}
|
|
10314
10778
|
return proxifyImageSrc(decoded, width, height, format);
|
|
10315
10779
|
}
|
|
10316
|
-
function getImage(entry, width = 0, height = 0, format = "match") {
|
|
10780
|
+
function getImage(entry, width = 0, height = 0, format = "match", fastMode = false) {
|
|
10317
10781
|
let meta;
|
|
10318
10782
|
if (typeof entry.json_metadata === "object") {
|
|
10319
10783
|
meta = entry.json_metadata;
|
|
@@ -10324,8 +10788,16 @@ function getImage(entry, width = 0, height = 0, format = "match") {
|
|
|
10324
10788
|
meta = null;
|
|
10325
10789
|
}
|
|
10326
10790
|
}
|
|
10791
|
+
const thumbnail = firstMetaUrl(meta?.thumbnails);
|
|
10792
|
+
if (thumbnail) {
|
|
10793
|
+
const decodedThumbnail = decodeEntities(thumbnail);
|
|
10794
|
+
const proxied = isGifLink(decodedThumbnail) ? proxifyImageSrc(decodedThumbnail, 0, 0, format) : proxifyImageSrc(decodedThumbnail, width, height, format);
|
|
10795
|
+
if (proxied) {
|
|
10796
|
+
return proxied;
|
|
10797
|
+
}
|
|
10798
|
+
}
|
|
10327
10799
|
if (meta && typeof meta.image === "string" && meta.image.length > 0) {
|
|
10328
|
-
const decodedImage =
|
|
10800
|
+
const decodedImage = decodeEntities(meta.image);
|
|
10329
10801
|
if (isGifLink(decodedImage)) {
|
|
10330
10802
|
return proxifyImageSrc(decodedImage, 0, 0, format);
|
|
10331
10803
|
}
|
|
@@ -10333,7 +10805,7 @@ function getImage(entry, width = 0, height = 0, format = "match") {
|
|
|
10333
10805
|
}
|
|
10334
10806
|
if (meta && meta.image && !!meta.image.length && meta.image[0]) {
|
|
10335
10807
|
if (typeof meta.image[0] === "string") {
|
|
10336
|
-
const decodedImage =
|
|
10808
|
+
const decodedImage = decodeEntities(meta.image[0]);
|
|
10337
10809
|
if (isGifLink(decodedImage)) {
|
|
10338
10810
|
return proxifyImageSrc(decodedImage, 0, 0, format);
|
|
10339
10811
|
}
|
|
@@ -10344,6 +10816,9 @@ function getImage(entry, width = 0, height = 0, format = "match") {
|
|
|
10344
10816
|
}
|
|
10345
10817
|
return proxifyImageSrc(meta.image[0], width, height, format);
|
|
10346
10818
|
}
|
|
10819
|
+
if (fastMode) {
|
|
10820
|
+
return fastBodyImage(entry.body, width, height, format);
|
|
10821
|
+
}
|
|
10347
10822
|
const fast = findFirstImageUrl(entry.body);
|
|
10348
10823
|
if (fast) {
|
|
10349
10824
|
return proxifyFound(fast, width, height, format);
|
|
@@ -10387,8 +10862,12 @@ function getEntryImageRawUrl(obj) {
|
|
|
10387
10862
|
const bodySrc = findFirstImageUrl(obj.body, true);
|
|
10388
10863
|
return bodySrc ? decodeImageSrc(bodySrc) : null;
|
|
10389
10864
|
}
|
|
10390
|
-
function catchPostImage(obj, width = 0, height = 0, format = "match") {
|
|
10865
|
+
function catchPostImage(obj, width = 0, height = 0, format = "match", options = {}) {
|
|
10866
|
+
const fastMode = options.fast === true;
|
|
10391
10867
|
if (typeof obj === "string") {
|
|
10868
|
+
if (fastMode) {
|
|
10869
|
+
return fastBodyImage(obj, width, height, format);
|
|
10870
|
+
}
|
|
10392
10871
|
const fast = findFirstImageUrl(obj);
|
|
10393
10872
|
if (fast) {
|
|
10394
10873
|
return proxifyFound(fast, width, height, format);
|
|
@@ -10408,15 +10887,17 @@ function catchPostImage(obj, width = 0, height = 0, format = "match") {
|
|
|
10408
10887
|
}
|
|
10409
10888
|
return null;
|
|
10410
10889
|
}
|
|
10411
|
-
const key = `${makeEntryCacheKey(obj)}-${width}x${height}-${format}`;
|
|
10890
|
+
const key = `${makeEntryCacheKey(obj)}-${width}x${height}-${format}${fastMode ? "-fast" : ""}`;
|
|
10412
10891
|
const item = cacheGet(key);
|
|
10413
|
-
if (item) {
|
|
10892
|
+
if (item !== void 0) {
|
|
10414
10893
|
return item;
|
|
10415
10894
|
}
|
|
10416
|
-
const res = getImage(obj, width, height, format);
|
|
10895
|
+
const res = getImage(obj, width, height, format, fastMode);
|
|
10417
10896
|
cacheSet(key, res);
|
|
10418
10897
|
return res;
|
|
10419
10898
|
}
|
|
10899
|
+
|
|
10900
|
+
// src/post-body-summary.ts
|
|
10420
10901
|
var summaryRenderer = new Remarkable({
|
|
10421
10902
|
html: true,
|
|
10422
10903
|
breaks: true,
|
|
@@ -10488,7 +10969,7 @@ function postBodySummary(entryBody, length = 200, platform = "web") {
|
|
|
10488
10969
|
text3 = joint(text3.split(" "), length);
|
|
10489
10970
|
}
|
|
10490
10971
|
if (text3) {
|
|
10491
|
-
text3 =
|
|
10972
|
+
text3 = decodeEntities(text3);
|
|
10492
10973
|
}
|
|
10493
10974
|
return text3;
|
|
10494
10975
|
}
|