@scrider/formatter 1.8.4 → 1.8.6
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/index.cjs +636 -519
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.js +636 -519
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -361,7 +361,272 @@ function isTextNode(node) {
|
|
|
361
361
|
return node.nodeType === NODE_TYPE.TEXT_NODE;
|
|
362
362
|
}
|
|
363
363
|
|
|
364
|
+
// src/conversion/html/config.ts
|
|
365
|
+
var INLINE_FORMAT_TAGS = {
|
|
366
|
+
link: "a",
|
|
367
|
+
bold: "strong",
|
|
368
|
+
italic: "em",
|
|
369
|
+
underline: "u",
|
|
370
|
+
strike: "s",
|
|
371
|
+
subscript: "sub",
|
|
372
|
+
superscript: "sup",
|
|
373
|
+
code: "code",
|
|
374
|
+
mark: "mark",
|
|
375
|
+
kbd: "kbd"
|
|
376
|
+
};
|
|
377
|
+
var INLINE_FORMAT_ORDER = [
|
|
378
|
+
"link",
|
|
379
|
+
"bold",
|
|
380
|
+
"italic",
|
|
381
|
+
"underline",
|
|
382
|
+
"strike",
|
|
383
|
+
"subscript",
|
|
384
|
+
"superscript",
|
|
385
|
+
"code",
|
|
386
|
+
"mark",
|
|
387
|
+
"kbd"
|
|
388
|
+
];
|
|
389
|
+
var INLINE_STYLE_FORMATS = {
|
|
390
|
+
color: "color",
|
|
391
|
+
background: "background-color",
|
|
392
|
+
font: "font-family",
|
|
393
|
+
size: "font-size"
|
|
394
|
+
};
|
|
395
|
+
var BLOCK_FORMAT_TAGS = {
|
|
396
|
+
header: (value) => `h${String(value)}`,
|
|
397
|
+
blockquote: "blockquote",
|
|
398
|
+
"code-block": "pre",
|
|
399
|
+
list: "li"
|
|
400
|
+
// Wrapped in ul/ol based on list type
|
|
401
|
+
};
|
|
402
|
+
var LIST_WRAPPER_TAGS = {
|
|
403
|
+
ordered: "ol",
|
|
404
|
+
bullet: "ul",
|
|
405
|
+
checked: "ul",
|
|
406
|
+
unchecked: "ul"
|
|
407
|
+
};
|
|
408
|
+
var EMBED_RENDERERS = {
|
|
409
|
+
image: (value, attrs) => {
|
|
410
|
+
const src = typeof value === "string" ? value : "";
|
|
411
|
+
const altVal = attrs?.alt;
|
|
412
|
+
const widthVal = attrs?.width;
|
|
413
|
+
const heightVal = attrs?.height;
|
|
414
|
+
const floatVal = attrs?.float;
|
|
415
|
+
const alt = altVal != null && (typeof altVal === "string" || typeof altVal === "number") ? ` alt="${escapeHtml(String(altVal))}"` : "";
|
|
416
|
+
const width = widthVal != null && (typeof widthVal === "string" || typeof widthVal === "number") ? ` width="${String(widthVal)}"` : "";
|
|
417
|
+
const height = heightVal != null && (typeof heightVal === "string" || typeof heightVal === "number") ? ` height="${String(heightVal)}"` : "";
|
|
418
|
+
const float = floatVal != null && typeof floatVal === "string" && floatVal !== "none" ? ` data-float="${escapeHtml(floatVal)}"` : "";
|
|
419
|
+
return `<img src="${escapeHtml(src)}"${alt}${width}${height}${float}>`;
|
|
420
|
+
},
|
|
421
|
+
video: (value, attrs, context) => {
|
|
422
|
+
const src = typeof value === "string" ? value : "";
|
|
423
|
+
const floatVal = attrs?.float;
|
|
424
|
+
const widthVal = attrs?.width;
|
|
425
|
+
const heightVal = attrs?.height;
|
|
426
|
+
const float = floatVal != null && typeof floatVal === "string" && floatVal !== "none" ? ` data-float="${escapeHtml(floatVal)}"` : "";
|
|
427
|
+
const styles = [];
|
|
428
|
+
if (widthVal != null && (typeof widthVal === "string" || typeof widthVal === "number")) {
|
|
429
|
+
const w = String(widthVal);
|
|
430
|
+
if (w && w !== "auto") styles.push(`width: ${/^\d+$/.test(w) ? w + "px" : w}`);
|
|
431
|
+
}
|
|
432
|
+
if (heightVal != null && (typeof heightVal === "string" || typeof heightVal === "number")) {
|
|
433
|
+
const h = String(heightVal);
|
|
434
|
+
if (h && h !== "auto") styles.push(`height: ${/^\d+$/.test(h) ? h + "px" : h}`);
|
|
435
|
+
}
|
|
436
|
+
const style = styles.length > 0 ? ` style="${styles.join("; ")}"` : "";
|
|
437
|
+
const embedSrc = toVideoEmbedUrl(src);
|
|
438
|
+
if (embedSrc) {
|
|
439
|
+
return `<iframe src="${escapeHtml(embedSrc)}" frameborder="0" allowfullscreen${renderEmbedIframeIsolationAttrs(context, "video")}${float}${style}></iframe>`;
|
|
440
|
+
}
|
|
441
|
+
return `<video src="${escapeHtml(src)}" controls${float}${style}></video>`;
|
|
442
|
+
},
|
|
443
|
+
codeWidget: (value, attrs, context) => {
|
|
444
|
+
const src = typeof value === "string" ? value : "";
|
|
445
|
+
const floatVal = attrs?.float;
|
|
446
|
+
const widthVal = attrs?.width;
|
|
447
|
+
const heightVal = attrs?.height;
|
|
448
|
+
const float = floatVal != null && typeof floatVal === "string" && floatVal !== "none" ? ` data-float="${escapeHtml(floatVal)}"` : "";
|
|
449
|
+
const styles = [];
|
|
450
|
+
if (widthVal != null && (typeof widthVal === "string" || typeof widthVal === "number")) {
|
|
451
|
+
const w = String(widthVal);
|
|
452
|
+
if (w && w !== "auto") styles.push(`width: ${/^\d+$/.test(w) ? w + "px" : w}`);
|
|
453
|
+
}
|
|
454
|
+
if (heightVal != null && (typeof heightVal === "string" || typeof heightVal === "number")) {
|
|
455
|
+
const h = String(heightVal);
|
|
456
|
+
if (h && h !== "auto") styles.push(`height: ${/^\d+$/.test(h) ? h + "px" : h}`);
|
|
457
|
+
}
|
|
458
|
+
const style = styles.length > 0 ? ` style="${styles.join("; ")}"` : "";
|
|
459
|
+
const embedSrc = toCodeWidgetEmbedUrl(src);
|
|
460
|
+
return `<iframe data-code-widget src="${escapeHtml(embedSrc)}" frameborder="0" allowfullscreen${renderEmbedIframeIsolationAttrs(context, "codeWidget")}${float}${style}></iframe>`;
|
|
461
|
+
},
|
|
462
|
+
formula: (value) => {
|
|
463
|
+
const latex = typeof value === "string" ? value : "";
|
|
464
|
+
return `<span class="formula" data-formula="${escapeHtml(latex)}">${escapeHtml(latex)}</span>`;
|
|
465
|
+
},
|
|
466
|
+
diagram: (value) => {
|
|
467
|
+
const source = typeof value === "string" ? value : "";
|
|
468
|
+
return `<span class="diagram" data-diagram="${escapeHtml(source)}">${escapeHtml(source)}</span>`;
|
|
469
|
+
},
|
|
470
|
+
drawio: (value, attrs) => {
|
|
471
|
+
const src = typeof value === "string" ? value : "";
|
|
472
|
+
const altVal = attrs?.alt;
|
|
473
|
+
const alt = altVal != null && (typeof altVal === "string" || typeof altVal === "number") ? ` data-alt="${escapeHtml(String(altVal))}"` : "";
|
|
474
|
+
return `<span class="drawio" data-drawio-src="${escapeHtml(src)}"${alt}></span>`;
|
|
475
|
+
},
|
|
476
|
+
"footnote-ref": (value) => {
|
|
477
|
+
const id = typeof value === "string" ? value : String(value);
|
|
478
|
+
return `<sup class="footnote-ref"><a href="#fn-${escapeHtml(id)}" id="fnref-${escapeHtml(id)}">[${escapeHtml(id)}]</a></sup>`;
|
|
479
|
+
},
|
|
480
|
+
divider: () => "<hr>",
|
|
481
|
+
// Soft line break (Shift+Enter equivalent). Emitted with an explicit
|
|
482
|
+
// `data-scrider-embed` marker so that html-to-delta can distinguish this
|
|
483
|
+
// embed from the placeholder `<br>` that appears inside an empty
|
|
484
|
+
// paragraph (`<p><br></p>`) without relying solely on positional
|
|
485
|
+
// heuristics. See `soft-break.ts` for the format definition.
|
|
486
|
+
softBreak: () => "<br data-scrider-embed>"
|
|
487
|
+
};
|
|
488
|
+
var TAG_TO_INLINE_FORMAT = {
|
|
489
|
+
strong: { format: "bold", value: true },
|
|
490
|
+
b: { format: "bold", value: true },
|
|
491
|
+
em: { format: "italic", value: true },
|
|
492
|
+
i: { format: "italic", value: true },
|
|
493
|
+
u: { format: "underline", value: true },
|
|
494
|
+
ins: { format: "underline", value: true },
|
|
495
|
+
s: { format: "strike", value: true },
|
|
496
|
+
strike: { format: "strike", value: true },
|
|
497
|
+
del: { format: "strike", value: true },
|
|
498
|
+
sub: { format: "subscript", value: true },
|
|
499
|
+
sup: { format: "superscript", value: true },
|
|
500
|
+
code: { format: "code", value: true },
|
|
501
|
+
mark: { format: "mark", value: true },
|
|
502
|
+
kbd: { format: "kbd", value: true }
|
|
503
|
+
};
|
|
504
|
+
var TAG_TO_BLOCK_FORMAT = {
|
|
505
|
+
h1: { format: "header", value: 1 },
|
|
506
|
+
h2: { format: "header", value: 2 },
|
|
507
|
+
h3: { format: "header", value: 3 },
|
|
508
|
+
h4: { format: "header", value: 4 },
|
|
509
|
+
h5: { format: "header", value: 5 },
|
|
510
|
+
h6: { format: "header", value: 6 },
|
|
511
|
+
blockquote: { format: "blockquote", value: true },
|
|
512
|
+
pre: { format: "code-block", value: true }
|
|
513
|
+
};
|
|
514
|
+
var CSS_ALIGN_TO_FORMAT = {
|
|
515
|
+
left: "left",
|
|
516
|
+
center: "center",
|
|
517
|
+
right: "right",
|
|
518
|
+
justify: "justify"
|
|
519
|
+
};
|
|
520
|
+
function escapeHtml(text) {
|
|
521
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
522
|
+
}
|
|
523
|
+
function unescapeHtml(text) {
|
|
524
|
+
return text.replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/&/g, "&");
|
|
525
|
+
}
|
|
526
|
+
function toVideoEmbedUrl(url) {
|
|
527
|
+
if (url.includes("youtube.com/embed") || url.includes("player.vimeo.com") || url.includes("dailymotion.com/embed") || url.includes("video_ext.php") || url.includes("rutube.ru/play/embed")) {
|
|
528
|
+
return url;
|
|
529
|
+
}
|
|
530
|
+
const ytMatch = url.match(/youtube\.com\/watch\?v=([\w-]+)/);
|
|
531
|
+
if (ytMatch) {
|
|
532
|
+
return `https://www.youtube.com/embed/${ytMatch[1]}`;
|
|
533
|
+
}
|
|
534
|
+
const ytShortMatch = url.match(/youtu\.be\/([\w-]+)/);
|
|
535
|
+
if (ytShortMatch) {
|
|
536
|
+
return `https://www.youtube.com/embed/${ytShortMatch[1]}`;
|
|
537
|
+
}
|
|
538
|
+
const rtMatch = url.match(/rutube\.ru\/video\/([\w]+)/);
|
|
539
|
+
if (rtMatch) {
|
|
540
|
+
return `https://rutube.ru/play/embed/${rtMatch[1]}`;
|
|
541
|
+
}
|
|
542
|
+
return null;
|
|
543
|
+
}
|
|
544
|
+
function fromVideoEmbedUrl(embedUrl) {
|
|
545
|
+
const ytMatch = embedUrl.match(/youtube\.com\/embed\/([\w-]+)/);
|
|
546
|
+
if (ytMatch) {
|
|
547
|
+
return `https://www.youtube.com/watch?v=${ytMatch[1]}`;
|
|
548
|
+
}
|
|
549
|
+
const rtMatch = embedUrl.match(/rutube\.ru\/play\/embed\/([\w]+)/);
|
|
550
|
+
if (rtMatch) {
|
|
551
|
+
return `https://rutube.ru/video/${rtMatch[1]}/`;
|
|
552
|
+
}
|
|
553
|
+
return embedUrl;
|
|
554
|
+
}
|
|
555
|
+
function splitUrl(url) {
|
|
556
|
+
let rest = url;
|
|
557
|
+
let hash = "";
|
|
558
|
+
const hashIdx = rest.indexOf("#");
|
|
559
|
+
if (hashIdx >= 0) {
|
|
560
|
+
hash = rest.slice(hashIdx);
|
|
561
|
+
rest = rest.slice(0, hashIdx);
|
|
562
|
+
}
|
|
563
|
+
let query = "";
|
|
564
|
+
const qIdx = rest.indexOf("?");
|
|
565
|
+
if (qIdx >= 0) {
|
|
566
|
+
query = rest.slice(qIdx);
|
|
567
|
+
rest = rest.slice(0, qIdx);
|
|
568
|
+
}
|
|
569
|
+
return { base: rest, query, hash };
|
|
570
|
+
}
|
|
571
|
+
function hasQueryParam(url, key) {
|
|
572
|
+
const { query } = splitUrl(url);
|
|
573
|
+
return new RegExp(`[?&]${key}=`, "i").test(query);
|
|
574
|
+
}
|
|
575
|
+
function appendQueryParam(url, key, value) {
|
|
576
|
+
const { base, query, hash } = splitUrl(url);
|
|
577
|
+
const next = query ? `${query}&${key}=${value}` : `?${key}=${value}`;
|
|
578
|
+
return `${base}${next}${hash}`;
|
|
579
|
+
}
|
|
580
|
+
var CODE_WIDGET_IFRAME_ALLOW = "accelerometer; camera; encrypted-media; geolocation; gyroscope; microphone; midi; payment; usb; xr-spatial-tracking; cross-origin-isolated";
|
|
581
|
+
function renderEmbedIframeIsolationAttrs(context, kind) {
|
|
582
|
+
const opts = context?.embed;
|
|
583
|
+
const parts = [];
|
|
584
|
+
if (kind === "codeWidget" && opts?.crossOriginIsolated) {
|
|
585
|
+
parts.push(`allow="${CODE_WIDGET_IFRAME_ALLOW}"`);
|
|
586
|
+
}
|
|
587
|
+
if (opts?.credentialless) {
|
|
588
|
+
parts.push("credentialless");
|
|
589
|
+
}
|
|
590
|
+
return parts.length ? ` ${parts.join(" ")}` : "";
|
|
591
|
+
}
|
|
592
|
+
function toCodeWidgetEmbedUrl(url) {
|
|
593
|
+
const u = typeof url === "string" ? url.trim() : "";
|
|
594
|
+
if (!u) return "";
|
|
595
|
+
if (/(?:\/\/|^)(?:[\w-]+\.)*stackblitz\.com\//i.test(u)) {
|
|
596
|
+
return hasQueryParam(u, "embed") ? u : appendQueryParam(u, "embed", "1");
|
|
597
|
+
}
|
|
598
|
+
if (/(?:\/\/|^)(?:[\w-]+\.)*codesandbox\.io\//i.test(u)) {
|
|
599
|
+
if (/codesandbox\.io\/embed\//i.test(u)) return u;
|
|
600
|
+
return u.replace(/codesandbox\.io\/s\//i, "codesandbox.io/embed/");
|
|
601
|
+
}
|
|
602
|
+
if (/(?:\/\/|^)(?:[\w-]+\.)*replit\.com\//i.test(u)) {
|
|
603
|
+
return hasQueryParam(u, "embed") ? u : appendQueryParam(u, "embed", "true");
|
|
604
|
+
}
|
|
605
|
+
if (/(?:\/\/|^)(?:[\w-]+\.)*codepen\.io\//i.test(u)) {
|
|
606
|
+
if (/codepen\.io\/[^/]+\/embed\//i.test(u)) return u;
|
|
607
|
+
return u.replace(/(codepen\.io\/[^/]+)\/pen\//i, "$1/embed/");
|
|
608
|
+
}
|
|
609
|
+
if (/(?:\/\/|^)(?:[\w-]+\.)*jsfiddle\.net\//i.test(u)) {
|
|
610
|
+
const { base, query, hash } = splitUrl(u);
|
|
611
|
+
if (/\/embedded(?:\/|$)/i.test(base)) return u;
|
|
612
|
+
const trimmed = base.replace(/\/+$/, "");
|
|
613
|
+
return `${trimmed}/embedded/${query}${hash}`;
|
|
614
|
+
}
|
|
615
|
+
if (/(?:\/\/|^)(?:[\w-]+\.)*trinket\.io\//i.test(u)) {
|
|
616
|
+
if (/trinket\.io\/embed\//i.test(u)) return u;
|
|
617
|
+
return u.replace(/trinket\.io\//i, "trinket.io/embed/");
|
|
618
|
+
}
|
|
619
|
+
if (/(?:\/\/|^)(?:[\w-]+\.)*onecompiler\.com\//i.test(u)) {
|
|
620
|
+
if (/onecompiler\.com\/embed\//i.test(u)) return u;
|
|
621
|
+
return u.replace(/onecompiler\.com\//i, "onecompiler.com/embed/");
|
|
622
|
+
}
|
|
623
|
+
return u;
|
|
624
|
+
}
|
|
625
|
+
|
|
364
626
|
// src/schema/blocks/table.ts
|
|
627
|
+
var EXT_TABLE_HOST_ATTR = "data-scrider-ext-table";
|
|
628
|
+
var EXT_TABLE_HOST_CLASS = "scrider-ext-table-host";
|
|
629
|
+
var VALID_TABLE_BLOCK_FLOATS = ["left", "center", "right"];
|
|
365
630
|
var VALID_CELL_HORIZONTAL_ALIGNS = [
|
|
366
631
|
"left",
|
|
367
632
|
"center",
|
|
@@ -388,6 +653,18 @@ function getGridDimensions(cells) {
|
|
|
388
653
|
if (maxRow < 0 || maxCol < 0) return null;
|
|
389
654
|
return [maxRow + 1, maxCol + 1];
|
|
390
655
|
}
|
|
656
|
+
function rowspanCrossesHeaderBoundary(cells, headerRows) {
|
|
657
|
+
if (headerRows <= 0) return false;
|
|
658
|
+
for (const [key, cell] of Object.entries(cells)) {
|
|
659
|
+
if (cell === null) continue;
|
|
660
|
+
const parsed = parseCellKey(key);
|
|
661
|
+
if (!parsed) continue;
|
|
662
|
+
const [row] = parsed;
|
|
663
|
+
const rowspan = cell.rowspan ?? 1;
|
|
664
|
+
if (row < headerRows && row + rowspan > headerRows) return true;
|
|
665
|
+
}
|
|
666
|
+
return false;
|
|
667
|
+
}
|
|
391
668
|
function isValidCellData(cell) {
|
|
392
669
|
if (typeof cell !== "object" || cell === null || !Array.isArray(cell.ops) || cell.ops.length === 0) {
|
|
393
670
|
return false;
|
|
@@ -549,6 +826,46 @@ function renderExtendedRow(data, row, cols, defaultCellTag, context, pretty) {
|
|
|
549
826
|
html += `${ind(2)}</tr>${nl}`;
|
|
550
827
|
return html;
|
|
551
828
|
}
|
|
829
|
+
function parseTableBlockFloat(value) {
|
|
830
|
+
if (value === "left" || value === "center" || value === "right") return value;
|
|
831
|
+
return void 0;
|
|
832
|
+
}
|
|
833
|
+
function extractHostBlockWidthPx(host) {
|
|
834
|
+
const style = host.getAttribute("style") || "";
|
|
835
|
+
const widthMatch = style.match(/(?:^|;\s*)width:\s*([\d.]+)px/i);
|
|
836
|
+
if (!widthMatch?.[1]) return void 0;
|
|
837
|
+
const n = parseFloat(widthMatch[1]);
|
|
838
|
+
if (!Number.isFinite(n) || n <= 0) return void 0;
|
|
839
|
+
return Math.round(n);
|
|
840
|
+
}
|
|
841
|
+
function enrichTableBlockFromHost(data, host) {
|
|
842
|
+
const float = parseTableBlockFloat(host.getAttribute("data-float"));
|
|
843
|
+
const width = extractHostBlockWidthPx(host);
|
|
844
|
+
if (float == null && width == null) return data;
|
|
845
|
+
const next = { ...data };
|
|
846
|
+
if (float != null) next.float = float;
|
|
847
|
+
if (width != null) next.width = width;
|
|
848
|
+
return next;
|
|
849
|
+
}
|
|
850
|
+
function tableNeedsHostWrapper(data) {
|
|
851
|
+
return data.float != null && VALID_TABLE_BLOCK_FLOATS.includes(data.float) || data.width != null && data.width > 0;
|
|
852
|
+
}
|
|
853
|
+
function renderTableHostWrapperOpen(data, pretty) {
|
|
854
|
+
const nl = pretty ? "\n" : "";
|
|
855
|
+
const ind = pretty ? " " : "";
|
|
856
|
+
const attrs = [`class="${EXT_TABLE_HOST_CLASS}"`, EXT_TABLE_HOST_ATTR];
|
|
857
|
+
if (data.float) attrs.push(`data-float="${escapeHtml(data.float)}"`);
|
|
858
|
+
const styleParts = [];
|
|
859
|
+
if (data.width != null && data.width > 0) {
|
|
860
|
+
styleParts.push(`width: ${Math.round(data.width)}px`);
|
|
861
|
+
styleParts.push("max-width: 100%");
|
|
862
|
+
}
|
|
863
|
+
if (styleParts.length > 0) attrs.push(`style="${styleParts.join("; ")}"`);
|
|
864
|
+
return `<div ${attrs.join(" ")}>${nl}${ind}`;
|
|
865
|
+
}
|
|
866
|
+
function renderTableHostWrapperClose(pretty) {
|
|
867
|
+
return pretty ? "</div>\n" : "</div>";
|
|
868
|
+
}
|
|
552
869
|
function isGfmCompatible(data) {
|
|
553
870
|
if (data.colWidths && data.colWidths.some((w) => w > 0)) {
|
|
554
871
|
return false;
|
|
@@ -556,6 +873,12 @@ function isGfmCompatible(data) {
|
|
|
556
873
|
if (data.rowHeights && data.rowHeights.some((h) => h > 0)) {
|
|
557
874
|
return false;
|
|
558
875
|
}
|
|
876
|
+
if (data.width != null && data.width > 0) {
|
|
877
|
+
return false;
|
|
878
|
+
}
|
|
879
|
+
if (data.float != null) {
|
|
880
|
+
return false;
|
|
881
|
+
}
|
|
559
882
|
for (const cell of Object.values(data.cells)) {
|
|
560
883
|
if (cell !== null && cell.align !== void 0) return false;
|
|
561
884
|
}
|
|
@@ -642,7 +965,27 @@ function collectTableRows(table) {
|
|
|
642
965
|
rows.push(section);
|
|
643
966
|
}
|
|
644
967
|
}
|
|
645
|
-
return { rows, headerRowCount };
|
|
968
|
+
return { rows, headerRowCount };
|
|
969
|
+
}
|
|
970
|
+
function inferHeaderRowsFromThPrefix(rows) {
|
|
971
|
+
let count = 0;
|
|
972
|
+
for (const row of rows) {
|
|
973
|
+
let hasCell = false;
|
|
974
|
+
let allTh = true;
|
|
975
|
+
const cellChildren = row.childNodes;
|
|
976
|
+
for (let i = 0; i < cellChildren.length; i++) {
|
|
977
|
+
const cell = cellChildren[i];
|
|
978
|
+
if (!cell || !isElement(cell)) continue;
|
|
979
|
+
const tag = cell.tagName.toLowerCase();
|
|
980
|
+
if (tag !== "td" && tag !== "th") continue;
|
|
981
|
+
hasCell = true;
|
|
982
|
+
if (tag !== "th") allTh = false;
|
|
983
|
+
}
|
|
984
|
+
if (!hasCell) continue;
|
|
985
|
+
if (!allTh) break;
|
|
986
|
+
count++;
|
|
987
|
+
}
|
|
988
|
+
return count;
|
|
646
989
|
}
|
|
647
990
|
function extractColWidths(table) {
|
|
648
991
|
const children = table.childNodes;
|
|
@@ -731,7 +1074,13 @@ function extractInlineHorizontalAlign(element) {
|
|
|
731
1074
|
return null;
|
|
732
1075
|
}
|
|
733
1076
|
function parseTableElement(table, context) {
|
|
734
|
-
const
|
|
1077
|
+
const collected = collectTableRows(table);
|
|
1078
|
+
const { rows } = collected;
|
|
1079
|
+
let headerRowCount = collected.headerRowCount;
|
|
1080
|
+
if (headerRowCount === 0) {
|
|
1081
|
+
const inferred = inferHeaderRowsFromThPrefix(rows);
|
|
1082
|
+
if (inferred > 0) headerRowCount = inferred;
|
|
1083
|
+
}
|
|
735
1084
|
if (rows.length === 0) return null;
|
|
736
1085
|
const cells = {};
|
|
737
1086
|
const colAligns = [];
|
|
@@ -908,6 +1257,12 @@ var tableBlockHandler = {
|
|
|
908
1257
|
if (typeof h !== "number" || h < 0) return false;
|
|
909
1258
|
}
|
|
910
1259
|
}
|
|
1260
|
+
if (data.width !== void 0) {
|
|
1261
|
+
if (typeof data.width !== "number" || data.width <= 0) return false;
|
|
1262
|
+
}
|
|
1263
|
+
if (data.float !== void 0 && !VALID_TABLE_BLOCK_FLOATS.includes(data.float)) {
|
|
1264
|
+
return false;
|
|
1265
|
+
}
|
|
911
1266
|
if (data.colAligns !== void 0) {
|
|
912
1267
|
if (!Array.isArray(data.colAligns) || data.colAligns.length !== cols) {
|
|
913
1268
|
return false;
|
|
@@ -943,14 +1298,15 @@ var tableBlockHandler = {
|
|
|
943
1298
|
}
|
|
944
1299
|
html += `${ind(1)}</colgroup>${nl}`;
|
|
945
1300
|
}
|
|
946
|
-
|
|
1301
|
+
const splitHeaderSection = headerRows > 0 && !rowspanCrossesHeaderBoundary(data.cells, headerRows);
|
|
1302
|
+
if (splitHeaderSection) {
|
|
947
1303
|
html += `${ind(1)}<thead>${nl}`;
|
|
948
1304
|
for (let r = 0; r < headerRows; r++) {
|
|
949
1305
|
html += renderExtendedRow(data, r, cols, "th", context, pretty);
|
|
950
1306
|
}
|
|
951
1307
|
html += `${ind(1)}</thead>${nl}`;
|
|
952
1308
|
}
|
|
953
|
-
const bodyStart = headerRows;
|
|
1309
|
+
const bodyStart = splitHeaderSection ? headerRows : 0;
|
|
954
1310
|
if (bodyStart < rows) {
|
|
955
1311
|
html += `${ind(1)}<tbody>${nl}`;
|
|
956
1312
|
for (let r = bodyStart; r < rows; r++) {
|
|
@@ -959,6 +1315,9 @@ var tableBlockHandler = {
|
|
|
959
1315
|
html += `${ind(1)}</tbody>${nl}`;
|
|
960
1316
|
}
|
|
961
1317
|
html += `</table>`;
|
|
1318
|
+
if (tableNeedsHostWrapper(data)) {
|
|
1319
|
+
return renderTableHostWrapperOpen(data, pretty) + html + renderTableHostWrapperClose(pretty);
|
|
1320
|
+
}
|
|
962
1321
|
return html;
|
|
963
1322
|
},
|
|
964
1323
|
fromHtml(element, context) {
|
|
@@ -1764,544 +2123,282 @@ var boldFormat = {
|
|
|
1764
2123
|
return value === true;
|
|
1765
2124
|
}
|
|
1766
2125
|
};
|
|
1767
|
-
|
|
1768
|
-
// src/schema/formats/inline/code.ts
|
|
1769
|
-
var codeFormat = {
|
|
1770
|
-
name: "code",
|
|
1771
|
-
scope: "inline",
|
|
1772
|
-
validate(value) {
|
|
1773
|
-
return value === true;
|
|
1774
|
-
}
|
|
1775
|
-
};
|
|
1776
|
-
|
|
1777
|
-
// src/schema/formats/inline/color.ts
|
|
1778
|
-
var colorFormat = {
|
|
1779
|
-
name: "color",
|
|
1780
|
-
scope: "inline",
|
|
1781
|
-
normalize(value) {
|
|
1782
|
-
return toHexColor(value);
|
|
1783
|
-
},
|
|
1784
|
-
validate(value) {
|
|
1785
|
-
return typeof value === "string" && isValidColor(value);
|
|
1786
|
-
}
|
|
1787
|
-
};
|
|
1788
|
-
|
|
1789
|
-
// src/schema/formats/inline/font.ts
|
|
1790
|
-
var fontFormat = {
|
|
1791
|
-
name: "font",
|
|
1792
|
-
scope: "inline",
|
|
1793
|
-
validate(value) {
|
|
1794
|
-
return typeof value === "string" && value.length > 0;
|
|
1795
|
-
}
|
|
1796
|
-
};
|
|
1797
|
-
|
|
1798
|
-
// src/schema/formats/inline/italic.ts
|
|
1799
|
-
var italicFormat = {
|
|
1800
|
-
name: "italic",
|
|
1801
|
-
scope: "inline",
|
|
1802
|
-
validate(value) {
|
|
1803
|
-
return value === true;
|
|
1804
|
-
}
|
|
1805
|
-
};
|
|
1806
|
-
|
|
1807
|
-
// src/schema/formats/inline/kbd.ts
|
|
1808
|
-
var kbdFormat = {
|
|
1809
|
-
name: "kbd",
|
|
1810
|
-
scope: "inline",
|
|
1811
|
-
validate(value) {
|
|
1812
|
-
return value === true;
|
|
1813
|
-
}
|
|
1814
|
-
};
|
|
1815
|
-
|
|
1816
|
-
// src/schema/formats/inline/link.ts
|
|
1817
|
-
var linkFormat = {
|
|
1818
|
-
name: "link",
|
|
1819
|
-
scope: "inline",
|
|
1820
|
-
normalize(value) {
|
|
1821
|
-
return value.trim();
|
|
1822
|
-
},
|
|
1823
|
-
validate(value) {
|
|
1824
|
-
if (typeof value !== "string" || value.length === 0) {
|
|
1825
|
-
return false;
|
|
1826
|
-
}
|
|
1827
|
-
const trimmed = value.trim();
|
|
1828
|
-
if (trimmed.startsWith("/") || trimmed.startsWith("./") || trimmed.startsWith("../")) {
|
|
1829
|
-
return true;
|
|
1830
|
-
}
|
|
1831
|
-
if (trimmed.startsWith("//")) {
|
|
1832
|
-
return true;
|
|
1833
|
-
}
|
|
1834
|
-
if (trimmed.startsWith("mailto:") || trimmed.startsWith("tel:")) {
|
|
1835
|
-
return true;
|
|
1836
|
-
}
|
|
1837
|
-
try {
|
|
1838
|
-
const url = new URL(trimmed);
|
|
1839
|
-
return url.protocol === "http:" || url.protocol === "https:";
|
|
1840
|
-
} catch {
|
|
1841
|
-
return false;
|
|
1842
|
-
}
|
|
1843
|
-
}
|
|
1844
|
-
};
|
|
1845
|
-
|
|
1846
|
-
// src/schema/formats/inline/mark.ts
|
|
1847
|
-
var markFormat = {
|
|
1848
|
-
name: "mark",
|
|
1849
|
-
scope: "inline",
|
|
1850
|
-
validate(value) {
|
|
1851
|
-
return value === true;
|
|
1852
|
-
}
|
|
1853
|
-
};
|
|
1854
|
-
|
|
1855
|
-
// src/schema/formats/inline/size.ts
|
|
1856
|
-
var sizeFormat = {
|
|
1857
|
-
name: "size",
|
|
1858
|
-
scope: "inline",
|
|
1859
|
-
validate(value) {
|
|
1860
|
-
return typeof value === "string" && value.length > 0;
|
|
1861
|
-
}
|
|
1862
|
-
};
|
|
1863
|
-
|
|
1864
|
-
// src/schema/formats/inline/strike.ts
|
|
1865
|
-
var strikeFormat = {
|
|
1866
|
-
name: "strike",
|
|
1867
|
-
scope: "inline",
|
|
1868
|
-
validate(value) {
|
|
1869
|
-
return value === true;
|
|
1870
|
-
}
|
|
1871
|
-
};
|
|
1872
|
-
|
|
1873
|
-
// src/schema/formats/inline/subscript.ts
|
|
1874
|
-
var subscriptFormat = {
|
|
1875
|
-
name: "subscript",
|
|
1876
|
-
scope: "inline",
|
|
1877
|
-
validate(value) {
|
|
1878
|
-
return value === true;
|
|
1879
|
-
}
|
|
1880
|
-
};
|
|
1881
|
-
|
|
1882
|
-
// src/schema/formats/inline/superscript.ts
|
|
1883
|
-
var superscriptFormat = {
|
|
1884
|
-
name: "superscript",
|
|
1885
|
-
scope: "inline",
|
|
1886
|
-
validate(value) {
|
|
1887
|
-
return value === true;
|
|
1888
|
-
}
|
|
1889
|
-
};
|
|
1890
|
-
|
|
1891
|
-
// src/schema/formats/inline/underline.ts
|
|
1892
|
-
var underlineFormat = {
|
|
1893
|
-
name: "underline",
|
|
1894
|
-
scope: "inline",
|
|
1895
|
-
validate(value) {
|
|
1896
|
-
return value === true;
|
|
1897
|
-
}
|
|
1898
|
-
};
|
|
1899
|
-
|
|
1900
|
-
// src/schema/formats/block/align.ts
|
|
1901
|
-
var VALID_ALIGN_TYPES = ["left", "center", "right", "justify"];
|
|
1902
|
-
var alignFormat = {
|
|
1903
|
-
name: "align",
|
|
1904
|
-
scope: "block",
|
|
1905
|
-
normalize(value) {
|
|
1906
|
-
return value.toLowerCase();
|
|
1907
|
-
},
|
|
1908
|
-
validate(value) {
|
|
1909
|
-
return VALID_ALIGN_TYPES.includes(value);
|
|
1910
|
-
}
|
|
1911
|
-
};
|
|
1912
|
-
|
|
1913
|
-
// src/schema/formats/block/blockquote.ts
|
|
1914
|
-
var blockquoteFormat = {
|
|
1915
|
-
name: "blockquote",
|
|
1916
|
-
scope: "block",
|
|
1917
|
-
validate(value) {
|
|
1918
|
-
return value === true;
|
|
1919
|
-
}
|
|
1920
|
-
};
|
|
1921
|
-
|
|
1922
|
-
// src/schema/formats/block/code-block.ts
|
|
1923
|
-
var codeBlockFormat = {
|
|
1924
|
-
name: "code-block",
|
|
1925
|
-
scope: "block",
|
|
1926
|
-
normalize(value) {
|
|
1927
|
-
if (typeof value === "string") {
|
|
1928
|
-
return value.toLowerCase().trim();
|
|
1929
|
-
}
|
|
1930
|
-
return value;
|
|
1931
|
-
},
|
|
1932
|
-
validate(value) {
|
|
1933
|
-
if (value === true) {
|
|
1934
|
-
return true;
|
|
1935
|
-
}
|
|
1936
|
-
if (typeof value === "string" && value.length > 0) {
|
|
1937
|
-
return true;
|
|
1938
|
-
}
|
|
1939
|
-
return false;
|
|
1940
|
-
}
|
|
1941
|
-
};
|
|
1942
|
-
|
|
1943
|
-
// src/schema/formats/block/header.ts
|
|
1944
|
-
var headerFormat = {
|
|
1945
|
-
name: "header",
|
|
1946
|
-
scope: "block",
|
|
1947
|
-
normalize(value) {
|
|
1948
|
-
return Math.max(1, Math.min(6, Math.floor(value)));
|
|
1949
|
-
},
|
|
1950
|
-
validate(value) {
|
|
1951
|
-
return Number.isInteger(value) && value >= 1 && value <= 6;
|
|
1952
|
-
}
|
|
1953
|
-
};
|
|
1954
|
-
|
|
1955
|
-
// src/schema/formats/block/header-id.ts
|
|
1956
|
-
var headerIdFormat = {
|
|
1957
|
-
name: "header-id",
|
|
1958
|
-
scope: "block",
|
|
1959
|
-
normalize(value) {
|
|
1960
|
-
return String(value).trim().toLowerCase();
|
|
1961
|
-
},
|
|
1962
|
-
validate(value) {
|
|
1963
|
-
if (typeof value !== "string") return false;
|
|
1964
|
-
const trimmed = value.trim();
|
|
1965
|
-
return trimmed.length > 0 && !/\s/.test(trimmed);
|
|
1966
|
-
}
|
|
1967
|
-
};
|
|
1968
|
-
|
|
1969
|
-
// src/schema/formats/block/indent.ts
|
|
1970
|
-
var MAX_INDENT = 8;
|
|
1971
|
-
var indentFormat = {
|
|
1972
|
-
name: "indent",
|
|
1973
|
-
scope: "block",
|
|
1974
|
-
normalize(value) {
|
|
1975
|
-
return Math.max(0, Math.min(MAX_INDENT, Math.floor(value)));
|
|
1976
|
-
},
|
|
1977
|
-
validate(value) {
|
|
1978
|
-
return Number.isInteger(value) && value >= 0 && value <= MAX_INDENT;
|
|
1979
|
-
}
|
|
1980
|
-
};
|
|
1981
|
-
|
|
1982
|
-
// src/schema/formats/block/list.ts
|
|
1983
|
-
var VALID_LIST_TYPES = ["ordered", "bullet", "checked", "unchecked"];
|
|
1984
|
-
var listFormat = {
|
|
1985
|
-
name: "list",
|
|
1986
|
-
scope: "block",
|
|
1987
|
-
normalize(value) {
|
|
1988
|
-
return value.toLowerCase();
|
|
1989
|
-
},
|
|
1990
|
-
validate(value) {
|
|
1991
|
-
return VALID_LIST_TYPES.includes(value);
|
|
1992
|
-
}
|
|
1993
|
-
};
|
|
1994
|
-
|
|
1995
|
-
// src/schema/formats/block/table-row.ts
|
|
1996
|
-
var tableRowFormat = {
|
|
1997
|
-
name: "table-row",
|
|
1998
|
-
scope: "block",
|
|
1999
|
-
validate(value) {
|
|
2000
|
-
return typeof value === "number" && Number.isInteger(value) && value >= 0;
|
|
2001
|
-
}
|
|
2002
|
-
};
|
|
2003
|
-
|
|
2004
|
-
// src/schema/formats/block/table-col.ts
|
|
2005
|
-
var tableColFormat = {
|
|
2006
|
-
name: "table-col",
|
|
2007
|
-
scope: "block",
|
|
2008
|
-
validate(value) {
|
|
2009
|
-
return typeof value === "number" && Number.isInteger(value) && value >= 0;
|
|
2010
|
-
}
|
|
2011
|
-
};
|
|
2012
|
-
|
|
2013
|
-
// src/schema/formats/block/table-header.ts
|
|
2014
|
-
var tableHeaderFormat = {
|
|
2015
|
-
name: "table-header",
|
|
2016
|
-
scope: "block",
|
|
2126
|
+
|
|
2127
|
+
// src/schema/formats/inline/code.ts
|
|
2128
|
+
var codeFormat = {
|
|
2129
|
+
name: "code",
|
|
2130
|
+
scope: "inline",
|
|
2017
2131
|
validate(value) {
|
|
2018
2132
|
return value === true;
|
|
2019
2133
|
}
|
|
2020
2134
|
};
|
|
2021
2135
|
|
|
2022
|
-
// src/schema/formats/
|
|
2023
|
-
var
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
scope: "block",
|
|
2136
|
+
// src/schema/formats/inline/color.ts
|
|
2137
|
+
var colorFormat = {
|
|
2138
|
+
name: "color",
|
|
2139
|
+
scope: "inline",
|
|
2027
2140
|
normalize(value) {
|
|
2028
|
-
return value
|
|
2141
|
+
return toHexColor(value);
|
|
2029
2142
|
},
|
|
2030
2143
|
validate(value) {
|
|
2031
|
-
return
|
|
2144
|
+
return typeof value === "string" && isValidColor(value);
|
|
2032
2145
|
}
|
|
2033
2146
|
};
|
|
2034
2147
|
|
|
2035
|
-
// src/schema/formats/
|
|
2036
|
-
var
|
|
2037
|
-
name: "
|
|
2038
|
-
scope: "
|
|
2148
|
+
// src/schema/formats/inline/font.ts
|
|
2149
|
+
var fontFormat = {
|
|
2150
|
+
name: "font",
|
|
2151
|
+
scope: "inline",
|
|
2039
2152
|
validate(value) {
|
|
2040
|
-
return typeof value === "
|
|
2153
|
+
return typeof value === "string" && value.length > 0;
|
|
2041
2154
|
}
|
|
2042
2155
|
};
|
|
2043
2156
|
|
|
2044
|
-
// src/
|
|
2045
|
-
var
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
subscript: "sub",
|
|
2052
|
-
superscript: "sup",
|
|
2053
|
-
code: "code",
|
|
2054
|
-
mark: "mark",
|
|
2055
|
-
kbd: "kbd"
|
|
2056
|
-
};
|
|
2057
|
-
var INLINE_FORMAT_ORDER = [
|
|
2058
|
-
"link",
|
|
2059
|
-
"bold",
|
|
2060
|
-
"italic",
|
|
2061
|
-
"underline",
|
|
2062
|
-
"strike",
|
|
2063
|
-
"subscript",
|
|
2064
|
-
"superscript",
|
|
2065
|
-
"code",
|
|
2066
|
-
"mark",
|
|
2067
|
-
"kbd"
|
|
2068
|
-
];
|
|
2069
|
-
var INLINE_STYLE_FORMATS = {
|
|
2070
|
-
color: "color",
|
|
2071
|
-
background: "background-color",
|
|
2072
|
-
font: "font-family",
|
|
2073
|
-
size: "font-size"
|
|
2074
|
-
};
|
|
2075
|
-
var BLOCK_FORMAT_TAGS = {
|
|
2076
|
-
header: (value) => `h${String(value)}`,
|
|
2077
|
-
blockquote: "blockquote",
|
|
2078
|
-
"code-block": "pre",
|
|
2079
|
-
list: "li"
|
|
2080
|
-
// Wrapped in ul/ol based on list type
|
|
2157
|
+
// src/schema/formats/inline/italic.ts
|
|
2158
|
+
var italicFormat = {
|
|
2159
|
+
name: "italic",
|
|
2160
|
+
scope: "inline",
|
|
2161
|
+
validate(value) {
|
|
2162
|
+
return value === true;
|
|
2163
|
+
}
|
|
2081
2164
|
};
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2165
|
+
|
|
2166
|
+
// src/schema/formats/inline/kbd.ts
|
|
2167
|
+
var kbdFormat = {
|
|
2168
|
+
name: "kbd",
|
|
2169
|
+
scope: "inline",
|
|
2170
|
+
validate(value) {
|
|
2171
|
+
return value === true;
|
|
2172
|
+
}
|
|
2087
2173
|
};
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
const alt = altVal != null && (typeof altVal === "string" || typeof altVal === "number") ? ` alt="${escapeHtml(String(altVal))}"` : "";
|
|
2096
|
-
const width = widthVal != null && (typeof widthVal === "string" || typeof widthVal === "number") ? ` width="${String(widthVal)}"` : "";
|
|
2097
|
-
const height = heightVal != null && (typeof heightVal === "string" || typeof heightVal === "number") ? ` height="${String(heightVal)}"` : "";
|
|
2098
|
-
const float = floatVal != null && typeof floatVal === "string" && floatVal !== "none" ? ` data-float="${escapeHtml(floatVal)}"` : "";
|
|
2099
|
-
return `<img src="${escapeHtml(src)}"${alt}${width}${height}${float}>`;
|
|
2174
|
+
|
|
2175
|
+
// src/schema/formats/inline/link.ts
|
|
2176
|
+
var linkFormat = {
|
|
2177
|
+
name: "link",
|
|
2178
|
+
scope: "inline",
|
|
2179
|
+
normalize(value) {
|
|
2180
|
+
return value.trim();
|
|
2100
2181
|
},
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
const widthVal = attrs?.width;
|
|
2105
|
-
const heightVal = attrs?.height;
|
|
2106
|
-
const float = floatVal != null && typeof floatVal === "string" && floatVal !== "none" ? ` data-float="${escapeHtml(floatVal)}"` : "";
|
|
2107
|
-
const styles = [];
|
|
2108
|
-
if (widthVal != null && (typeof widthVal === "string" || typeof widthVal === "number")) {
|
|
2109
|
-
const w = String(widthVal);
|
|
2110
|
-
if (w && w !== "auto") styles.push(`width: ${/^\d+$/.test(w) ? w + "px" : w}`);
|
|
2182
|
+
validate(value) {
|
|
2183
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
2184
|
+
return false;
|
|
2111
2185
|
}
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2186
|
+
const trimmed = value.trim();
|
|
2187
|
+
if (trimmed.startsWith("/") || trimmed.startsWith("./") || trimmed.startsWith("../")) {
|
|
2188
|
+
return true;
|
|
2115
2189
|
}
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
if (embedSrc) {
|
|
2119
|
-
return `<iframe src="${escapeHtml(embedSrc)}" frameborder="0" allowfullscreen${renderEmbedIframeIsolationAttrs(context, "video")}${float}${style}></iframe>`;
|
|
2190
|
+
if (trimmed.startsWith("//")) {
|
|
2191
|
+
return true;
|
|
2120
2192
|
}
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
codeWidget: (value, attrs, context) => {
|
|
2124
|
-
const src = typeof value === "string" ? value : "";
|
|
2125
|
-
const floatVal = attrs?.float;
|
|
2126
|
-
const widthVal = attrs?.width;
|
|
2127
|
-
const heightVal = attrs?.height;
|
|
2128
|
-
const float = floatVal != null && typeof floatVal === "string" && floatVal !== "none" ? ` data-float="${escapeHtml(floatVal)}"` : "";
|
|
2129
|
-
const styles = [];
|
|
2130
|
-
if (widthVal != null && (typeof widthVal === "string" || typeof widthVal === "number")) {
|
|
2131
|
-
const w = String(widthVal);
|
|
2132
|
-
if (w && w !== "auto") styles.push(`width: ${/^\d+$/.test(w) ? w + "px" : w}`);
|
|
2193
|
+
if (trimmed.startsWith("mailto:") || trimmed.startsWith("tel:")) {
|
|
2194
|
+
return true;
|
|
2133
2195
|
}
|
|
2134
|
-
|
|
2135
|
-
const
|
|
2136
|
-
|
|
2196
|
+
try {
|
|
2197
|
+
const url = new URL(trimmed);
|
|
2198
|
+
return url.protocol === "http:" || url.protocol === "https:";
|
|
2199
|
+
} catch {
|
|
2200
|
+
return false;
|
|
2137
2201
|
}
|
|
2138
|
-
|
|
2139
|
-
const embedSrc = toCodeWidgetEmbedUrl(src);
|
|
2140
|
-
return `<iframe data-code-widget src="${escapeHtml(embedSrc)}" frameborder="0" allowfullscreen${renderEmbedIframeIsolationAttrs(context, "codeWidget")}${float}${style}></iframe>`;
|
|
2141
|
-
},
|
|
2142
|
-
formula: (value) => {
|
|
2143
|
-
const latex = typeof value === "string" ? value : "";
|
|
2144
|
-
return `<span class="formula" data-formula="${escapeHtml(latex)}">${escapeHtml(latex)}</span>`;
|
|
2145
|
-
},
|
|
2146
|
-
diagram: (value) => {
|
|
2147
|
-
const source = typeof value === "string" ? value : "";
|
|
2148
|
-
return `<span class="diagram" data-diagram="${escapeHtml(source)}">${escapeHtml(source)}</span>`;
|
|
2149
|
-
},
|
|
2150
|
-
drawio: (value, attrs) => {
|
|
2151
|
-
const src = typeof value === "string" ? value : "";
|
|
2152
|
-
const altVal = attrs?.alt;
|
|
2153
|
-
const alt = altVal != null && (typeof altVal === "string" || typeof altVal === "number") ? ` data-alt="${escapeHtml(String(altVal))}"` : "";
|
|
2154
|
-
return `<span class="drawio" data-drawio-src="${escapeHtml(src)}"${alt}></span>`;
|
|
2155
|
-
},
|
|
2156
|
-
"footnote-ref": (value) => {
|
|
2157
|
-
const id = typeof value === "string" ? value : String(value);
|
|
2158
|
-
return `<sup class="footnote-ref"><a href="#fn-${escapeHtml(id)}" id="fnref-${escapeHtml(id)}">[${escapeHtml(id)}]</a></sup>`;
|
|
2159
|
-
},
|
|
2160
|
-
divider: () => "<hr>",
|
|
2161
|
-
// Soft line break (Shift+Enter equivalent). Emitted with an explicit
|
|
2162
|
-
// `data-scrider-embed` marker so that html-to-delta can distinguish this
|
|
2163
|
-
// embed from the placeholder `<br>` that appears inside an empty
|
|
2164
|
-
// paragraph (`<p><br></p>`) without relying solely on positional
|
|
2165
|
-
// heuristics. See `soft-break.ts` for the format definition.
|
|
2166
|
-
softBreak: () => "<br data-scrider-embed>"
|
|
2167
|
-
};
|
|
2168
|
-
var TAG_TO_INLINE_FORMAT = {
|
|
2169
|
-
strong: { format: "bold", value: true },
|
|
2170
|
-
b: { format: "bold", value: true },
|
|
2171
|
-
em: { format: "italic", value: true },
|
|
2172
|
-
i: { format: "italic", value: true },
|
|
2173
|
-
u: { format: "underline", value: true },
|
|
2174
|
-
ins: { format: "underline", value: true },
|
|
2175
|
-
s: { format: "strike", value: true },
|
|
2176
|
-
strike: { format: "strike", value: true },
|
|
2177
|
-
del: { format: "strike", value: true },
|
|
2178
|
-
sub: { format: "subscript", value: true },
|
|
2179
|
-
sup: { format: "superscript", value: true },
|
|
2180
|
-
code: { format: "code", value: true },
|
|
2181
|
-
mark: { format: "mark", value: true },
|
|
2182
|
-
kbd: { format: "kbd", value: true }
|
|
2183
|
-
};
|
|
2184
|
-
var TAG_TO_BLOCK_FORMAT = {
|
|
2185
|
-
h1: { format: "header", value: 1 },
|
|
2186
|
-
h2: { format: "header", value: 2 },
|
|
2187
|
-
h3: { format: "header", value: 3 },
|
|
2188
|
-
h4: { format: "header", value: 4 },
|
|
2189
|
-
h5: { format: "header", value: 5 },
|
|
2190
|
-
h6: { format: "header", value: 6 },
|
|
2191
|
-
blockquote: { format: "blockquote", value: true },
|
|
2192
|
-
pre: { format: "code-block", value: true }
|
|
2202
|
+
}
|
|
2193
2203
|
};
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2204
|
+
|
|
2205
|
+
// src/schema/formats/inline/mark.ts
|
|
2206
|
+
var markFormat = {
|
|
2207
|
+
name: "mark",
|
|
2208
|
+
scope: "inline",
|
|
2209
|
+
validate(value) {
|
|
2210
|
+
return value === true;
|
|
2211
|
+
}
|
|
2199
2212
|
};
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
if (url.includes("youtube.com/embed") || url.includes("player.vimeo.com") || url.includes("dailymotion.com/embed") || url.includes("video_ext.php") || url.includes("rutube.ru/play/embed")) {
|
|
2208
|
-
return url;
|
|
2213
|
+
|
|
2214
|
+
// src/schema/formats/inline/size.ts
|
|
2215
|
+
var sizeFormat = {
|
|
2216
|
+
name: "size",
|
|
2217
|
+
scope: "inline",
|
|
2218
|
+
validate(value) {
|
|
2219
|
+
return typeof value === "string" && value.length > 0;
|
|
2209
2220
|
}
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2221
|
+
};
|
|
2222
|
+
|
|
2223
|
+
// src/schema/formats/inline/strike.ts
|
|
2224
|
+
var strikeFormat = {
|
|
2225
|
+
name: "strike",
|
|
2226
|
+
scope: "inline",
|
|
2227
|
+
validate(value) {
|
|
2228
|
+
return value === true;
|
|
2213
2229
|
}
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2230
|
+
};
|
|
2231
|
+
|
|
2232
|
+
// src/schema/formats/inline/subscript.ts
|
|
2233
|
+
var subscriptFormat = {
|
|
2234
|
+
name: "subscript",
|
|
2235
|
+
scope: "inline",
|
|
2236
|
+
validate(value) {
|
|
2237
|
+
return value === true;
|
|
2217
2238
|
}
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2239
|
+
};
|
|
2240
|
+
|
|
2241
|
+
// src/schema/formats/inline/superscript.ts
|
|
2242
|
+
var superscriptFormat = {
|
|
2243
|
+
name: "superscript",
|
|
2244
|
+
scope: "inline",
|
|
2245
|
+
validate(value) {
|
|
2246
|
+
return value === true;
|
|
2221
2247
|
}
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2248
|
+
};
|
|
2249
|
+
|
|
2250
|
+
// src/schema/formats/inline/underline.ts
|
|
2251
|
+
var underlineFormat = {
|
|
2252
|
+
name: "underline",
|
|
2253
|
+
scope: "inline",
|
|
2254
|
+
validate(value) {
|
|
2255
|
+
return value === true;
|
|
2228
2256
|
}
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2257
|
+
};
|
|
2258
|
+
|
|
2259
|
+
// src/schema/formats/block/align.ts
|
|
2260
|
+
var VALID_ALIGN_TYPES = ["left", "center", "right", "justify"];
|
|
2261
|
+
var alignFormat = {
|
|
2262
|
+
name: "align",
|
|
2263
|
+
scope: "block",
|
|
2264
|
+
normalize(value) {
|
|
2265
|
+
return value.toLowerCase();
|
|
2266
|
+
},
|
|
2267
|
+
validate(value) {
|
|
2268
|
+
return VALID_ALIGN_TYPES.includes(value);
|
|
2232
2269
|
}
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
rest = rest.slice(0, hashIdx);
|
|
2270
|
+
};
|
|
2271
|
+
|
|
2272
|
+
// src/schema/formats/block/blockquote.ts
|
|
2273
|
+
var blockquoteFormat = {
|
|
2274
|
+
name: "blockquote",
|
|
2275
|
+
scope: "block",
|
|
2276
|
+
validate(value) {
|
|
2277
|
+
return value === true;
|
|
2242
2278
|
}
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2279
|
+
};
|
|
2280
|
+
|
|
2281
|
+
// src/schema/formats/block/code-block.ts
|
|
2282
|
+
var codeBlockFormat = {
|
|
2283
|
+
name: "code-block",
|
|
2284
|
+
scope: "block",
|
|
2285
|
+
normalize(value) {
|
|
2286
|
+
if (typeof value === "string") {
|
|
2287
|
+
return value.toLowerCase().trim();
|
|
2288
|
+
}
|
|
2289
|
+
return value;
|
|
2290
|
+
},
|
|
2291
|
+
validate(value) {
|
|
2292
|
+
if (value === true) {
|
|
2293
|
+
return true;
|
|
2294
|
+
}
|
|
2295
|
+
if (typeof value === "string" && value.length > 0) {
|
|
2296
|
+
return true;
|
|
2297
|
+
}
|
|
2298
|
+
return false;
|
|
2248
2299
|
}
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
var CODE_WIDGET_IFRAME_ALLOW = "accelerometer; camera; encrypted-media; geolocation; gyroscope; microphone; midi; payment; usb; xr-spatial-tracking; cross-origin-isolated";
|
|
2261
|
-
function renderEmbedIframeIsolationAttrs(context, kind) {
|
|
2262
|
-
const opts = context?.embed;
|
|
2263
|
-
const parts = [];
|
|
2264
|
-
if (kind === "codeWidget" && opts?.crossOriginIsolated) {
|
|
2265
|
-
parts.push(`allow="${CODE_WIDGET_IFRAME_ALLOW}"`);
|
|
2300
|
+
};
|
|
2301
|
+
|
|
2302
|
+
// src/schema/formats/block/header.ts
|
|
2303
|
+
var headerFormat = {
|
|
2304
|
+
name: "header",
|
|
2305
|
+
scope: "block",
|
|
2306
|
+
normalize(value) {
|
|
2307
|
+
return Math.max(1, Math.min(6, Math.floor(value)));
|
|
2308
|
+
},
|
|
2309
|
+
validate(value) {
|
|
2310
|
+
return Number.isInteger(value) && value >= 1 && value <= 6;
|
|
2266
2311
|
}
|
|
2267
|
-
|
|
2268
|
-
|
|
2312
|
+
};
|
|
2313
|
+
|
|
2314
|
+
// src/schema/formats/block/header-id.ts
|
|
2315
|
+
var headerIdFormat = {
|
|
2316
|
+
name: "header-id",
|
|
2317
|
+
scope: "block",
|
|
2318
|
+
normalize(value) {
|
|
2319
|
+
return String(value).trim().toLowerCase();
|
|
2320
|
+
},
|
|
2321
|
+
validate(value) {
|
|
2322
|
+
if (typeof value !== "string") return false;
|
|
2323
|
+
const trimmed = value.trim();
|
|
2324
|
+
return trimmed.length > 0 && !/\s/.test(trimmed);
|
|
2269
2325
|
}
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2326
|
+
};
|
|
2327
|
+
|
|
2328
|
+
// src/schema/formats/block/indent.ts
|
|
2329
|
+
var MAX_INDENT = 8;
|
|
2330
|
+
var indentFormat = {
|
|
2331
|
+
name: "indent",
|
|
2332
|
+
scope: "block",
|
|
2333
|
+
normalize(value) {
|
|
2334
|
+
return Math.max(0, Math.min(MAX_INDENT, Math.floor(value)));
|
|
2335
|
+
},
|
|
2336
|
+
validate(value) {
|
|
2337
|
+
return Number.isInteger(value) && value >= 0 && value <= MAX_INDENT;
|
|
2277
2338
|
}
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2339
|
+
};
|
|
2340
|
+
|
|
2341
|
+
// src/schema/formats/block/list.ts
|
|
2342
|
+
var VALID_LIST_TYPES = ["ordered", "bullet", "checked", "unchecked"];
|
|
2343
|
+
var listFormat = {
|
|
2344
|
+
name: "list",
|
|
2345
|
+
scope: "block",
|
|
2346
|
+
normalize(value) {
|
|
2347
|
+
return value.toLowerCase();
|
|
2348
|
+
},
|
|
2349
|
+
validate(value) {
|
|
2350
|
+
return VALID_LIST_TYPES.includes(value);
|
|
2281
2351
|
}
|
|
2282
|
-
|
|
2283
|
-
|
|
2352
|
+
};
|
|
2353
|
+
|
|
2354
|
+
// src/schema/formats/block/table-row.ts
|
|
2355
|
+
var tableRowFormat = {
|
|
2356
|
+
name: "table-row",
|
|
2357
|
+
scope: "block",
|
|
2358
|
+
validate(value) {
|
|
2359
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 0;
|
|
2284
2360
|
}
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2361
|
+
};
|
|
2362
|
+
|
|
2363
|
+
// src/schema/formats/block/table-col.ts
|
|
2364
|
+
var tableColFormat = {
|
|
2365
|
+
name: "table-col",
|
|
2366
|
+
scope: "block",
|
|
2367
|
+
validate(value) {
|
|
2368
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 0;
|
|
2288
2369
|
}
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2370
|
+
};
|
|
2371
|
+
|
|
2372
|
+
// src/schema/formats/block/table-header.ts
|
|
2373
|
+
var tableHeaderFormat = {
|
|
2374
|
+
name: "table-header",
|
|
2375
|
+
scope: "block",
|
|
2376
|
+
validate(value) {
|
|
2377
|
+
return value === true;
|
|
2294
2378
|
}
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2379
|
+
};
|
|
2380
|
+
|
|
2381
|
+
// src/schema/formats/block/table-col-align.ts
|
|
2382
|
+
var VALID_ALIGNS = ["left", "center", "right"];
|
|
2383
|
+
var tableColAlignFormat = {
|
|
2384
|
+
name: "table-col-align",
|
|
2385
|
+
scope: "block",
|
|
2386
|
+
normalize(value) {
|
|
2387
|
+
return value.toLowerCase();
|
|
2388
|
+
},
|
|
2389
|
+
validate(value) {
|
|
2390
|
+
return VALID_ALIGNS.includes(value);
|
|
2298
2391
|
}
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2392
|
+
};
|
|
2393
|
+
|
|
2394
|
+
// src/schema/formats/embed/block.ts
|
|
2395
|
+
var blockFormat = {
|
|
2396
|
+
name: "block",
|
|
2397
|
+
scope: "embed",
|
|
2398
|
+
validate(value) {
|
|
2399
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) && typeof value.type === "string" && value.type.length > 0;
|
|
2302
2400
|
}
|
|
2303
|
-
|
|
2304
|
-
}
|
|
2401
|
+
};
|
|
2305
2402
|
|
|
2306
2403
|
// src/schema/formats/embed/codeWidget.ts
|
|
2307
2404
|
var codeWidgetFormat = {
|
|
@@ -3856,6 +3953,10 @@ function htmlToDelta(html, options = {}) {
|
|
|
3856
3953
|
processBoxElement(node);
|
|
3857
3954
|
return;
|
|
3858
3955
|
}
|
|
3956
|
+
if (node.hasAttribute("data-scrider-ext-table")) {
|
|
3957
|
+
processExtTableHostElement(node);
|
|
3958
|
+
return;
|
|
3959
|
+
}
|
|
3859
3960
|
}
|
|
3860
3961
|
if (tagName === "p" || tagName === "div") {
|
|
3861
3962
|
processDefaultBlock(node);
|
|
@@ -4199,27 +4300,43 @@ function htmlToDelta(html, options = {}) {
|
|
|
4199
4300
|
}
|
|
4200
4301
|
processChildren(element);
|
|
4201
4302
|
}
|
|
4202
|
-
function
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
|
|
4206
|
-
|
|
4207
|
-
|
|
4208
|
-
|
|
4209
|
-
|
|
4210
|
-
if (!innerHtml) return [{ insert: "\n" }];
|
|
4211
|
-
return htmlToDelta(innerHtml, options).ops;
|
|
4212
|
-
}
|
|
4213
|
-
};
|
|
4214
|
-
const data = tableHandler.fromHtml(table, blockContext);
|
|
4215
|
-
if (data) {
|
|
4216
|
-
flushText();
|
|
4217
|
-
delta.insert({ block: data });
|
|
4218
|
-
delta.insert("\n");
|
|
4219
|
-
atLineStart = true;
|
|
4220
|
-
return;
|
|
4303
|
+
function processExtTableHostElement(host) {
|
|
4304
|
+
let table = null;
|
|
4305
|
+
const children2 = host.childNodes;
|
|
4306
|
+
for (let i = 0; i < children2.length; i++) {
|
|
4307
|
+
const child = children2[i];
|
|
4308
|
+
if (child && isElement(child) && child.tagName.toLowerCase() === "table") {
|
|
4309
|
+
table = child;
|
|
4310
|
+
break;
|
|
4221
4311
|
}
|
|
4222
4312
|
}
|
|
4313
|
+
if (table && insertExtendedTableBlock(table, host)) return;
|
|
4314
|
+
processDefaultBlock(host);
|
|
4315
|
+
}
|
|
4316
|
+
function makeTableBlockParseContext() {
|
|
4317
|
+
return {
|
|
4318
|
+
registry: void 0,
|
|
4319
|
+
parseElement: (el) => {
|
|
4320
|
+
const innerHtml = el.innerHTML ?? "";
|
|
4321
|
+
if (!innerHtml) return [{ insert: "\n" }];
|
|
4322
|
+
return htmlToDelta(innerHtml, options).ops;
|
|
4323
|
+
}
|
|
4324
|
+
};
|
|
4325
|
+
}
|
|
4326
|
+
function insertExtendedTableBlock(table, host) {
|
|
4327
|
+
const tableHandler = options.blockHandlers?.get("table");
|
|
4328
|
+
if (!tableHandler) return false;
|
|
4329
|
+
let data = tableHandler.fromHtml(table, makeTableBlockParseContext());
|
|
4330
|
+
if (!data) return false;
|
|
4331
|
+
if (host) data = enrichTableBlockFromHost(data, host);
|
|
4332
|
+
flushText();
|
|
4333
|
+
delta.insert({ block: data });
|
|
4334
|
+
delta.insert("\n");
|
|
4335
|
+
atLineStart = true;
|
|
4336
|
+
return true;
|
|
4337
|
+
}
|
|
4338
|
+
function processTableElement(table) {
|
|
4339
|
+
if (insertExtendedTableBlock(table, null)) return;
|
|
4223
4340
|
let rowIdx = 0;
|
|
4224
4341
|
const tableChildren = table.childNodes;
|
|
4225
4342
|
for (let i = 0; i < tableChildren.length; i++) {
|