@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.cjs
CHANGED
|
@@ -491,7 +491,272 @@ function isTextNode(node) {
|
|
|
491
491
|
return node.nodeType === NODE_TYPE.TEXT_NODE;
|
|
492
492
|
}
|
|
493
493
|
|
|
494
|
+
// src/conversion/html/config.ts
|
|
495
|
+
var INLINE_FORMAT_TAGS = {
|
|
496
|
+
link: "a",
|
|
497
|
+
bold: "strong",
|
|
498
|
+
italic: "em",
|
|
499
|
+
underline: "u",
|
|
500
|
+
strike: "s",
|
|
501
|
+
subscript: "sub",
|
|
502
|
+
superscript: "sup",
|
|
503
|
+
code: "code",
|
|
504
|
+
mark: "mark",
|
|
505
|
+
kbd: "kbd"
|
|
506
|
+
};
|
|
507
|
+
var INLINE_FORMAT_ORDER = [
|
|
508
|
+
"link",
|
|
509
|
+
"bold",
|
|
510
|
+
"italic",
|
|
511
|
+
"underline",
|
|
512
|
+
"strike",
|
|
513
|
+
"subscript",
|
|
514
|
+
"superscript",
|
|
515
|
+
"code",
|
|
516
|
+
"mark",
|
|
517
|
+
"kbd"
|
|
518
|
+
];
|
|
519
|
+
var INLINE_STYLE_FORMATS = {
|
|
520
|
+
color: "color",
|
|
521
|
+
background: "background-color",
|
|
522
|
+
font: "font-family",
|
|
523
|
+
size: "font-size"
|
|
524
|
+
};
|
|
525
|
+
var BLOCK_FORMAT_TAGS = {
|
|
526
|
+
header: (value) => `h${String(value)}`,
|
|
527
|
+
blockquote: "blockquote",
|
|
528
|
+
"code-block": "pre",
|
|
529
|
+
list: "li"
|
|
530
|
+
// Wrapped in ul/ol based on list type
|
|
531
|
+
};
|
|
532
|
+
var LIST_WRAPPER_TAGS = {
|
|
533
|
+
ordered: "ol",
|
|
534
|
+
bullet: "ul",
|
|
535
|
+
checked: "ul",
|
|
536
|
+
unchecked: "ul"
|
|
537
|
+
};
|
|
538
|
+
var EMBED_RENDERERS = {
|
|
539
|
+
image: (value, attrs) => {
|
|
540
|
+
const src = typeof value === "string" ? value : "";
|
|
541
|
+
const altVal = attrs?.alt;
|
|
542
|
+
const widthVal = attrs?.width;
|
|
543
|
+
const heightVal = attrs?.height;
|
|
544
|
+
const floatVal = attrs?.float;
|
|
545
|
+
const alt = altVal != null && (typeof altVal === "string" || typeof altVal === "number") ? ` alt="${escapeHtml(String(altVal))}"` : "";
|
|
546
|
+
const width = widthVal != null && (typeof widthVal === "string" || typeof widthVal === "number") ? ` width="${String(widthVal)}"` : "";
|
|
547
|
+
const height = heightVal != null && (typeof heightVal === "string" || typeof heightVal === "number") ? ` height="${String(heightVal)}"` : "";
|
|
548
|
+
const float = floatVal != null && typeof floatVal === "string" && floatVal !== "none" ? ` data-float="${escapeHtml(floatVal)}"` : "";
|
|
549
|
+
return `<img src="${escapeHtml(src)}"${alt}${width}${height}${float}>`;
|
|
550
|
+
},
|
|
551
|
+
video: (value, attrs, context) => {
|
|
552
|
+
const src = typeof value === "string" ? value : "";
|
|
553
|
+
const floatVal = attrs?.float;
|
|
554
|
+
const widthVal = attrs?.width;
|
|
555
|
+
const heightVal = attrs?.height;
|
|
556
|
+
const float = floatVal != null && typeof floatVal === "string" && floatVal !== "none" ? ` data-float="${escapeHtml(floatVal)}"` : "";
|
|
557
|
+
const styles = [];
|
|
558
|
+
if (widthVal != null && (typeof widthVal === "string" || typeof widthVal === "number")) {
|
|
559
|
+
const w = String(widthVal);
|
|
560
|
+
if (w && w !== "auto") styles.push(`width: ${/^\d+$/.test(w) ? w + "px" : w}`);
|
|
561
|
+
}
|
|
562
|
+
if (heightVal != null && (typeof heightVal === "string" || typeof heightVal === "number")) {
|
|
563
|
+
const h = String(heightVal);
|
|
564
|
+
if (h && h !== "auto") styles.push(`height: ${/^\d+$/.test(h) ? h + "px" : h}`);
|
|
565
|
+
}
|
|
566
|
+
const style = styles.length > 0 ? ` style="${styles.join("; ")}"` : "";
|
|
567
|
+
const embedSrc = toVideoEmbedUrl(src);
|
|
568
|
+
if (embedSrc) {
|
|
569
|
+
return `<iframe src="${escapeHtml(embedSrc)}" frameborder="0" allowfullscreen${renderEmbedIframeIsolationAttrs(context, "video")}${float}${style}></iframe>`;
|
|
570
|
+
}
|
|
571
|
+
return `<video src="${escapeHtml(src)}" controls${float}${style}></video>`;
|
|
572
|
+
},
|
|
573
|
+
codeWidget: (value, attrs, context) => {
|
|
574
|
+
const src = typeof value === "string" ? value : "";
|
|
575
|
+
const floatVal = attrs?.float;
|
|
576
|
+
const widthVal = attrs?.width;
|
|
577
|
+
const heightVal = attrs?.height;
|
|
578
|
+
const float = floatVal != null && typeof floatVal === "string" && floatVal !== "none" ? ` data-float="${escapeHtml(floatVal)}"` : "";
|
|
579
|
+
const styles = [];
|
|
580
|
+
if (widthVal != null && (typeof widthVal === "string" || typeof widthVal === "number")) {
|
|
581
|
+
const w = String(widthVal);
|
|
582
|
+
if (w && w !== "auto") styles.push(`width: ${/^\d+$/.test(w) ? w + "px" : w}`);
|
|
583
|
+
}
|
|
584
|
+
if (heightVal != null && (typeof heightVal === "string" || typeof heightVal === "number")) {
|
|
585
|
+
const h = String(heightVal);
|
|
586
|
+
if (h && h !== "auto") styles.push(`height: ${/^\d+$/.test(h) ? h + "px" : h}`);
|
|
587
|
+
}
|
|
588
|
+
const style = styles.length > 0 ? ` style="${styles.join("; ")}"` : "";
|
|
589
|
+
const embedSrc = toCodeWidgetEmbedUrl(src);
|
|
590
|
+
return `<iframe data-code-widget src="${escapeHtml(embedSrc)}" frameborder="0" allowfullscreen${renderEmbedIframeIsolationAttrs(context, "codeWidget")}${float}${style}></iframe>`;
|
|
591
|
+
},
|
|
592
|
+
formula: (value) => {
|
|
593
|
+
const latex = typeof value === "string" ? value : "";
|
|
594
|
+
return `<span class="formula" data-formula="${escapeHtml(latex)}">${escapeHtml(latex)}</span>`;
|
|
595
|
+
},
|
|
596
|
+
diagram: (value) => {
|
|
597
|
+
const source = typeof value === "string" ? value : "";
|
|
598
|
+
return `<span class="diagram" data-diagram="${escapeHtml(source)}">${escapeHtml(source)}</span>`;
|
|
599
|
+
},
|
|
600
|
+
drawio: (value, attrs) => {
|
|
601
|
+
const src = typeof value === "string" ? value : "";
|
|
602
|
+
const altVal = attrs?.alt;
|
|
603
|
+
const alt = altVal != null && (typeof altVal === "string" || typeof altVal === "number") ? ` data-alt="${escapeHtml(String(altVal))}"` : "";
|
|
604
|
+
return `<span class="drawio" data-drawio-src="${escapeHtml(src)}"${alt}></span>`;
|
|
605
|
+
},
|
|
606
|
+
"footnote-ref": (value) => {
|
|
607
|
+
const id = typeof value === "string" ? value : String(value);
|
|
608
|
+
return `<sup class="footnote-ref"><a href="#fn-${escapeHtml(id)}" id="fnref-${escapeHtml(id)}">[${escapeHtml(id)}]</a></sup>`;
|
|
609
|
+
},
|
|
610
|
+
divider: () => "<hr>",
|
|
611
|
+
// Soft line break (Shift+Enter equivalent). Emitted with an explicit
|
|
612
|
+
// `data-scrider-embed` marker so that html-to-delta can distinguish this
|
|
613
|
+
// embed from the placeholder `<br>` that appears inside an empty
|
|
614
|
+
// paragraph (`<p><br></p>`) without relying solely on positional
|
|
615
|
+
// heuristics. See `soft-break.ts` for the format definition.
|
|
616
|
+
softBreak: () => "<br data-scrider-embed>"
|
|
617
|
+
};
|
|
618
|
+
var TAG_TO_INLINE_FORMAT = {
|
|
619
|
+
strong: { format: "bold", value: true },
|
|
620
|
+
b: { format: "bold", value: true },
|
|
621
|
+
em: { format: "italic", value: true },
|
|
622
|
+
i: { format: "italic", value: true },
|
|
623
|
+
u: { format: "underline", value: true },
|
|
624
|
+
ins: { format: "underline", value: true },
|
|
625
|
+
s: { format: "strike", value: true },
|
|
626
|
+
strike: { format: "strike", value: true },
|
|
627
|
+
del: { format: "strike", value: true },
|
|
628
|
+
sub: { format: "subscript", value: true },
|
|
629
|
+
sup: { format: "superscript", value: true },
|
|
630
|
+
code: { format: "code", value: true },
|
|
631
|
+
mark: { format: "mark", value: true },
|
|
632
|
+
kbd: { format: "kbd", value: true }
|
|
633
|
+
};
|
|
634
|
+
var TAG_TO_BLOCK_FORMAT = {
|
|
635
|
+
h1: { format: "header", value: 1 },
|
|
636
|
+
h2: { format: "header", value: 2 },
|
|
637
|
+
h3: { format: "header", value: 3 },
|
|
638
|
+
h4: { format: "header", value: 4 },
|
|
639
|
+
h5: { format: "header", value: 5 },
|
|
640
|
+
h6: { format: "header", value: 6 },
|
|
641
|
+
blockquote: { format: "blockquote", value: true },
|
|
642
|
+
pre: { format: "code-block", value: true }
|
|
643
|
+
};
|
|
644
|
+
var CSS_ALIGN_TO_FORMAT = {
|
|
645
|
+
left: "left",
|
|
646
|
+
center: "center",
|
|
647
|
+
right: "right",
|
|
648
|
+
justify: "justify"
|
|
649
|
+
};
|
|
650
|
+
function escapeHtml(text) {
|
|
651
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
652
|
+
}
|
|
653
|
+
function unescapeHtml(text) {
|
|
654
|
+
return text.replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/&/g, "&");
|
|
655
|
+
}
|
|
656
|
+
function toVideoEmbedUrl(url) {
|
|
657
|
+
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")) {
|
|
658
|
+
return url;
|
|
659
|
+
}
|
|
660
|
+
const ytMatch = url.match(/youtube\.com\/watch\?v=([\w-]+)/);
|
|
661
|
+
if (ytMatch) {
|
|
662
|
+
return `https://www.youtube.com/embed/${ytMatch[1]}`;
|
|
663
|
+
}
|
|
664
|
+
const ytShortMatch = url.match(/youtu\.be\/([\w-]+)/);
|
|
665
|
+
if (ytShortMatch) {
|
|
666
|
+
return `https://www.youtube.com/embed/${ytShortMatch[1]}`;
|
|
667
|
+
}
|
|
668
|
+
const rtMatch = url.match(/rutube\.ru\/video\/([\w]+)/);
|
|
669
|
+
if (rtMatch) {
|
|
670
|
+
return `https://rutube.ru/play/embed/${rtMatch[1]}`;
|
|
671
|
+
}
|
|
672
|
+
return null;
|
|
673
|
+
}
|
|
674
|
+
function fromVideoEmbedUrl(embedUrl) {
|
|
675
|
+
const ytMatch = embedUrl.match(/youtube\.com\/embed\/([\w-]+)/);
|
|
676
|
+
if (ytMatch) {
|
|
677
|
+
return `https://www.youtube.com/watch?v=${ytMatch[1]}`;
|
|
678
|
+
}
|
|
679
|
+
const rtMatch = embedUrl.match(/rutube\.ru\/play\/embed\/([\w]+)/);
|
|
680
|
+
if (rtMatch) {
|
|
681
|
+
return `https://rutube.ru/video/${rtMatch[1]}/`;
|
|
682
|
+
}
|
|
683
|
+
return embedUrl;
|
|
684
|
+
}
|
|
685
|
+
function splitUrl(url) {
|
|
686
|
+
let rest = url;
|
|
687
|
+
let hash = "";
|
|
688
|
+
const hashIdx = rest.indexOf("#");
|
|
689
|
+
if (hashIdx >= 0) {
|
|
690
|
+
hash = rest.slice(hashIdx);
|
|
691
|
+
rest = rest.slice(0, hashIdx);
|
|
692
|
+
}
|
|
693
|
+
let query = "";
|
|
694
|
+
const qIdx = rest.indexOf("?");
|
|
695
|
+
if (qIdx >= 0) {
|
|
696
|
+
query = rest.slice(qIdx);
|
|
697
|
+
rest = rest.slice(0, qIdx);
|
|
698
|
+
}
|
|
699
|
+
return { base: rest, query, hash };
|
|
700
|
+
}
|
|
701
|
+
function hasQueryParam(url, key) {
|
|
702
|
+
const { query } = splitUrl(url);
|
|
703
|
+
return new RegExp(`[?&]${key}=`, "i").test(query);
|
|
704
|
+
}
|
|
705
|
+
function appendQueryParam(url, key, value) {
|
|
706
|
+
const { base, query, hash } = splitUrl(url);
|
|
707
|
+
const next = query ? `${query}&${key}=${value}` : `?${key}=${value}`;
|
|
708
|
+
return `${base}${next}${hash}`;
|
|
709
|
+
}
|
|
710
|
+
var CODE_WIDGET_IFRAME_ALLOW = "accelerometer; camera; encrypted-media; geolocation; gyroscope; microphone; midi; payment; usb; xr-spatial-tracking; cross-origin-isolated";
|
|
711
|
+
function renderEmbedIframeIsolationAttrs(context, kind) {
|
|
712
|
+
const opts = context?.embed;
|
|
713
|
+
const parts = [];
|
|
714
|
+
if (kind === "codeWidget" && opts?.crossOriginIsolated) {
|
|
715
|
+
parts.push(`allow="${CODE_WIDGET_IFRAME_ALLOW}"`);
|
|
716
|
+
}
|
|
717
|
+
if (opts?.credentialless) {
|
|
718
|
+
parts.push("credentialless");
|
|
719
|
+
}
|
|
720
|
+
return parts.length ? ` ${parts.join(" ")}` : "";
|
|
721
|
+
}
|
|
722
|
+
function toCodeWidgetEmbedUrl(url) {
|
|
723
|
+
const u = typeof url === "string" ? url.trim() : "";
|
|
724
|
+
if (!u) return "";
|
|
725
|
+
if (/(?:\/\/|^)(?:[\w-]+\.)*stackblitz\.com\//i.test(u)) {
|
|
726
|
+
return hasQueryParam(u, "embed") ? u : appendQueryParam(u, "embed", "1");
|
|
727
|
+
}
|
|
728
|
+
if (/(?:\/\/|^)(?:[\w-]+\.)*codesandbox\.io\//i.test(u)) {
|
|
729
|
+
if (/codesandbox\.io\/embed\//i.test(u)) return u;
|
|
730
|
+
return u.replace(/codesandbox\.io\/s\//i, "codesandbox.io/embed/");
|
|
731
|
+
}
|
|
732
|
+
if (/(?:\/\/|^)(?:[\w-]+\.)*replit\.com\//i.test(u)) {
|
|
733
|
+
return hasQueryParam(u, "embed") ? u : appendQueryParam(u, "embed", "true");
|
|
734
|
+
}
|
|
735
|
+
if (/(?:\/\/|^)(?:[\w-]+\.)*codepen\.io\//i.test(u)) {
|
|
736
|
+
if (/codepen\.io\/[^/]+\/embed\//i.test(u)) return u;
|
|
737
|
+
return u.replace(/(codepen\.io\/[^/]+)\/pen\//i, "$1/embed/");
|
|
738
|
+
}
|
|
739
|
+
if (/(?:\/\/|^)(?:[\w-]+\.)*jsfiddle\.net\//i.test(u)) {
|
|
740
|
+
const { base, query, hash } = splitUrl(u);
|
|
741
|
+
if (/\/embedded(?:\/|$)/i.test(base)) return u;
|
|
742
|
+
const trimmed = base.replace(/\/+$/, "");
|
|
743
|
+
return `${trimmed}/embedded/${query}${hash}`;
|
|
744
|
+
}
|
|
745
|
+
if (/(?:\/\/|^)(?:[\w-]+\.)*trinket\.io\//i.test(u)) {
|
|
746
|
+
if (/trinket\.io\/embed\//i.test(u)) return u;
|
|
747
|
+
return u.replace(/trinket\.io\//i, "trinket.io/embed/");
|
|
748
|
+
}
|
|
749
|
+
if (/(?:\/\/|^)(?:[\w-]+\.)*onecompiler\.com\//i.test(u)) {
|
|
750
|
+
if (/onecompiler\.com\/embed\//i.test(u)) return u;
|
|
751
|
+
return u.replace(/onecompiler\.com\//i, "onecompiler.com/embed/");
|
|
752
|
+
}
|
|
753
|
+
return u;
|
|
754
|
+
}
|
|
755
|
+
|
|
494
756
|
// src/schema/blocks/table.ts
|
|
757
|
+
var EXT_TABLE_HOST_ATTR = "data-scrider-ext-table";
|
|
758
|
+
var EXT_TABLE_HOST_CLASS = "scrider-ext-table-host";
|
|
759
|
+
var VALID_TABLE_BLOCK_FLOATS = ["left", "center", "right"];
|
|
495
760
|
var VALID_CELL_HORIZONTAL_ALIGNS = [
|
|
496
761
|
"left",
|
|
497
762
|
"center",
|
|
@@ -518,6 +783,18 @@ function getGridDimensions(cells) {
|
|
|
518
783
|
if (maxRow < 0 || maxCol < 0) return null;
|
|
519
784
|
return [maxRow + 1, maxCol + 1];
|
|
520
785
|
}
|
|
786
|
+
function rowspanCrossesHeaderBoundary(cells, headerRows) {
|
|
787
|
+
if (headerRows <= 0) return false;
|
|
788
|
+
for (const [key, cell] of Object.entries(cells)) {
|
|
789
|
+
if (cell === null) continue;
|
|
790
|
+
const parsed = parseCellKey(key);
|
|
791
|
+
if (!parsed) continue;
|
|
792
|
+
const [row] = parsed;
|
|
793
|
+
const rowspan = cell.rowspan ?? 1;
|
|
794
|
+
if (row < headerRows && row + rowspan > headerRows) return true;
|
|
795
|
+
}
|
|
796
|
+
return false;
|
|
797
|
+
}
|
|
521
798
|
function isValidCellData(cell) {
|
|
522
799
|
if (typeof cell !== "object" || cell === null || !Array.isArray(cell.ops) || cell.ops.length === 0) {
|
|
523
800
|
return false;
|
|
@@ -679,6 +956,46 @@ function renderExtendedRow(data, row, cols, defaultCellTag, context, pretty) {
|
|
|
679
956
|
html += `${ind(2)}</tr>${nl}`;
|
|
680
957
|
return html;
|
|
681
958
|
}
|
|
959
|
+
function parseTableBlockFloat(value) {
|
|
960
|
+
if (value === "left" || value === "center" || value === "right") return value;
|
|
961
|
+
return void 0;
|
|
962
|
+
}
|
|
963
|
+
function extractHostBlockWidthPx(host) {
|
|
964
|
+
const style = host.getAttribute("style") || "";
|
|
965
|
+
const widthMatch = style.match(/(?:^|;\s*)width:\s*([\d.]+)px/i);
|
|
966
|
+
if (!widthMatch?.[1]) return void 0;
|
|
967
|
+
const n = parseFloat(widthMatch[1]);
|
|
968
|
+
if (!Number.isFinite(n) || n <= 0) return void 0;
|
|
969
|
+
return Math.round(n);
|
|
970
|
+
}
|
|
971
|
+
function enrichTableBlockFromHost(data, host) {
|
|
972
|
+
const float = parseTableBlockFloat(host.getAttribute("data-float"));
|
|
973
|
+
const width = extractHostBlockWidthPx(host);
|
|
974
|
+
if (float == null && width == null) return data;
|
|
975
|
+
const next = { ...data };
|
|
976
|
+
if (float != null) next.float = float;
|
|
977
|
+
if (width != null) next.width = width;
|
|
978
|
+
return next;
|
|
979
|
+
}
|
|
980
|
+
function tableNeedsHostWrapper(data) {
|
|
981
|
+
return data.float != null && VALID_TABLE_BLOCK_FLOATS.includes(data.float) || data.width != null && data.width > 0;
|
|
982
|
+
}
|
|
983
|
+
function renderTableHostWrapperOpen(data, pretty) {
|
|
984
|
+
const nl = pretty ? "\n" : "";
|
|
985
|
+
const ind = pretty ? " " : "";
|
|
986
|
+
const attrs = [`class="${EXT_TABLE_HOST_CLASS}"`, EXT_TABLE_HOST_ATTR];
|
|
987
|
+
if (data.float) attrs.push(`data-float="${escapeHtml(data.float)}"`);
|
|
988
|
+
const styleParts = [];
|
|
989
|
+
if (data.width != null && data.width > 0) {
|
|
990
|
+
styleParts.push(`width: ${Math.round(data.width)}px`);
|
|
991
|
+
styleParts.push("max-width: 100%");
|
|
992
|
+
}
|
|
993
|
+
if (styleParts.length > 0) attrs.push(`style="${styleParts.join("; ")}"`);
|
|
994
|
+
return `<div ${attrs.join(" ")}>${nl}${ind}`;
|
|
995
|
+
}
|
|
996
|
+
function renderTableHostWrapperClose(pretty) {
|
|
997
|
+
return pretty ? "</div>\n" : "</div>";
|
|
998
|
+
}
|
|
682
999
|
function isGfmCompatible(data) {
|
|
683
1000
|
if (data.colWidths && data.colWidths.some((w) => w > 0)) {
|
|
684
1001
|
return false;
|
|
@@ -686,6 +1003,12 @@ function isGfmCompatible(data) {
|
|
|
686
1003
|
if (data.rowHeights && data.rowHeights.some((h) => h > 0)) {
|
|
687
1004
|
return false;
|
|
688
1005
|
}
|
|
1006
|
+
if (data.width != null && data.width > 0) {
|
|
1007
|
+
return false;
|
|
1008
|
+
}
|
|
1009
|
+
if (data.float != null) {
|
|
1010
|
+
return false;
|
|
1011
|
+
}
|
|
689
1012
|
for (const cell of Object.values(data.cells)) {
|
|
690
1013
|
if (cell !== null && cell.align !== void 0) return false;
|
|
691
1014
|
}
|
|
@@ -772,7 +1095,27 @@ function collectTableRows(table) {
|
|
|
772
1095
|
rows.push(section);
|
|
773
1096
|
}
|
|
774
1097
|
}
|
|
775
|
-
return { rows, headerRowCount };
|
|
1098
|
+
return { rows, headerRowCount };
|
|
1099
|
+
}
|
|
1100
|
+
function inferHeaderRowsFromThPrefix(rows) {
|
|
1101
|
+
let count = 0;
|
|
1102
|
+
for (const row of rows) {
|
|
1103
|
+
let hasCell = false;
|
|
1104
|
+
let allTh = true;
|
|
1105
|
+
const cellChildren = row.childNodes;
|
|
1106
|
+
for (let i = 0; i < cellChildren.length; i++) {
|
|
1107
|
+
const cell = cellChildren[i];
|
|
1108
|
+
if (!cell || !isElement(cell)) continue;
|
|
1109
|
+
const tag = cell.tagName.toLowerCase();
|
|
1110
|
+
if (tag !== "td" && tag !== "th") continue;
|
|
1111
|
+
hasCell = true;
|
|
1112
|
+
if (tag !== "th") allTh = false;
|
|
1113
|
+
}
|
|
1114
|
+
if (!hasCell) continue;
|
|
1115
|
+
if (!allTh) break;
|
|
1116
|
+
count++;
|
|
1117
|
+
}
|
|
1118
|
+
return count;
|
|
776
1119
|
}
|
|
777
1120
|
function extractColWidths(table) {
|
|
778
1121
|
const children = table.childNodes;
|
|
@@ -861,7 +1204,13 @@ function extractInlineHorizontalAlign(element) {
|
|
|
861
1204
|
return null;
|
|
862
1205
|
}
|
|
863
1206
|
function parseTableElement(table, context) {
|
|
864
|
-
const
|
|
1207
|
+
const collected = collectTableRows(table);
|
|
1208
|
+
const { rows } = collected;
|
|
1209
|
+
let headerRowCount = collected.headerRowCount;
|
|
1210
|
+
if (headerRowCount === 0) {
|
|
1211
|
+
const inferred = inferHeaderRowsFromThPrefix(rows);
|
|
1212
|
+
if (inferred > 0) headerRowCount = inferred;
|
|
1213
|
+
}
|
|
865
1214
|
if (rows.length === 0) return null;
|
|
866
1215
|
const cells = {};
|
|
867
1216
|
const colAligns = [];
|
|
@@ -1038,6 +1387,12 @@ var tableBlockHandler = {
|
|
|
1038
1387
|
if (typeof h !== "number" || h < 0) return false;
|
|
1039
1388
|
}
|
|
1040
1389
|
}
|
|
1390
|
+
if (data.width !== void 0) {
|
|
1391
|
+
if (typeof data.width !== "number" || data.width <= 0) return false;
|
|
1392
|
+
}
|
|
1393
|
+
if (data.float !== void 0 && !VALID_TABLE_BLOCK_FLOATS.includes(data.float)) {
|
|
1394
|
+
return false;
|
|
1395
|
+
}
|
|
1041
1396
|
if (data.colAligns !== void 0) {
|
|
1042
1397
|
if (!Array.isArray(data.colAligns) || data.colAligns.length !== cols) {
|
|
1043
1398
|
return false;
|
|
@@ -1073,14 +1428,15 @@ var tableBlockHandler = {
|
|
|
1073
1428
|
}
|
|
1074
1429
|
html += `${ind(1)}</colgroup>${nl}`;
|
|
1075
1430
|
}
|
|
1076
|
-
|
|
1431
|
+
const splitHeaderSection = headerRows > 0 && !rowspanCrossesHeaderBoundary(data.cells, headerRows);
|
|
1432
|
+
if (splitHeaderSection) {
|
|
1077
1433
|
html += `${ind(1)}<thead>${nl}`;
|
|
1078
1434
|
for (let r = 0; r < headerRows; r++) {
|
|
1079
1435
|
html += renderExtendedRow(data, r, cols, "th", context, pretty);
|
|
1080
1436
|
}
|
|
1081
1437
|
html += `${ind(1)}</thead>${nl}`;
|
|
1082
1438
|
}
|
|
1083
|
-
const bodyStart = headerRows;
|
|
1439
|
+
const bodyStart = splitHeaderSection ? headerRows : 0;
|
|
1084
1440
|
if (bodyStart < rows) {
|
|
1085
1441
|
html += `${ind(1)}<tbody>${nl}`;
|
|
1086
1442
|
for (let r = bodyStart; r < rows; r++) {
|
|
@@ -1089,6 +1445,9 @@ var tableBlockHandler = {
|
|
|
1089
1445
|
html += `${ind(1)}</tbody>${nl}`;
|
|
1090
1446
|
}
|
|
1091
1447
|
html += `</table>`;
|
|
1448
|
+
if (tableNeedsHostWrapper(data)) {
|
|
1449
|
+
return renderTableHostWrapperOpen(data, pretty) + html + renderTableHostWrapperClose(pretty);
|
|
1450
|
+
}
|
|
1092
1451
|
return html;
|
|
1093
1452
|
},
|
|
1094
1453
|
fromHtml(element, context) {
|
|
@@ -1894,544 +2253,282 @@ var boldFormat = {
|
|
|
1894
2253
|
return value === true;
|
|
1895
2254
|
}
|
|
1896
2255
|
};
|
|
1897
|
-
|
|
1898
|
-
// src/schema/formats/inline/code.ts
|
|
1899
|
-
var codeFormat = {
|
|
1900
|
-
name: "code",
|
|
1901
|
-
scope: "inline",
|
|
1902
|
-
validate(value) {
|
|
1903
|
-
return value === true;
|
|
1904
|
-
}
|
|
1905
|
-
};
|
|
1906
|
-
|
|
1907
|
-
// src/schema/formats/inline/color.ts
|
|
1908
|
-
var colorFormat = {
|
|
1909
|
-
name: "color",
|
|
1910
|
-
scope: "inline",
|
|
1911
|
-
normalize(value) {
|
|
1912
|
-
return toHexColor(value);
|
|
1913
|
-
},
|
|
1914
|
-
validate(value) {
|
|
1915
|
-
return typeof value === "string" && isValidColor(value);
|
|
1916
|
-
}
|
|
1917
|
-
};
|
|
1918
|
-
|
|
1919
|
-
// src/schema/formats/inline/font.ts
|
|
1920
|
-
var fontFormat = {
|
|
1921
|
-
name: "font",
|
|
1922
|
-
scope: "inline",
|
|
1923
|
-
validate(value) {
|
|
1924
|
-
return typeof value === "string" && value.length > 0;
|
|
1925
|
-
}
|
|
1926
|
-
};
|
|
1927
|
-
|
|
1928
|
-
// src/schema/formats/inline/italic.ts
|
|
1929
|
-
var italicFormat = {
|
|
1930
|
-
name: "italic",
|
|
1931
|
-
scope: "inline",
|
|
1932
|
-
validate(value) {
|
|
1933
|
-
return value === true;
|
|
1934
|
-
}
|
|
1935
|
-
};
|
|
1936
|
-
|
|
1937
|
-
// src/schema/formats/inline/kbd.ts
|
|
1938
|
-
var kbdFormat = {
|
|
1939
|
-
name: "kbd",
|
|
1940
|
-
scope: "inline",
|
|
1941
|
-
validate(value) {
|
|
1942
|
-
return value === true;
|
|
1943
|
-
}
|
|
1944
|
-
};
|
|
1945
|
-
|
|
1946
|
-
// src/schema/formats/inline/link.ts
|
|
1947
|
-
var linkFormat = {
|
|
1948
|
-
name: "link",
|
|
1949
|
-
scope: "inline",
|
|
1950
|
-
normalize(value) {
|
|
1951
|
-
return value.trim();
|
|
1952
|
-
},
|
|
1953
|
-
validate(value) {
|
|
1954
|
-
if (typeof value !== "string" || value.length === 0) {
|
|
1955
|
-
return false;
|
|
1956
|
-
}
|
|
1957
|
-
const trimmed = value.trim();
|
|
1958
|
-
if (trimmed.startsWith("/") || trimmed.startsWith("./") || trimmed.startsWith("../")) {
|
|
1959
|
-
return true;
|
|
1960
|
-
}
|
|
1961
|
-
if (trimmed.startsWith("//")) {
|
|
1962
|
-
return true;
|
|
1963
|
-
}
|
|
1964
|
-
if (trimmed.startsWith("mailto:") || trimmed.startsWith("tel:")) {
|
|
1965
|
-
return true;
|
|
1966
|
-
}
|
|
1967
|
-
try {
|
|
1968
|
-
const url = new URL(trimmed);
|
|
1969
|
-
return url.protocol === "http:" || url.protocol === "https:";
|
|
1970
|
-
} catch {
|
|
1971
|
-
return false;
|
|
1972
|
-
}
|
|
1973
|
-
}
|
|
1974
|
-
};
|
|
1975
|
-
|
|
1976
|
-
// src/schema/formats/inline/mark.ts
|
|
1977
|
-
var markFormat = {
|
|
1978
|
-
name: "mark",
|
|
1979
|
-
scope: "inline",
|
|
1980
|
-
validate(value) {
|
|
1981
|
-
return value === true;
|
|
1982
|
-
}
|
|
1983
|
-
};
|
|
1984
|
-
|
|
1985
|
-
// src/schema/formats/inline/size.ts
|
|
1986
|
-
var sizeFormat = {
|
|
1987
|
-
name: "size",
|
|
1988
|
-
scope: "inline",
|
|
1989
|
-
validate(value) {
|
|
1990
|
-
return typeof value === "string" && value.length > 0;
|
|
1991
|
-
}
|
|
1992
|
-
};
|
|
1993
|
-
|
|
1994
|
-
// src/schema/formats/inline/strike.ts
|
|
1995
|
-
var strikeFormat = {
|
|
1996
|
-
name: "strike",
|
|
1997
|
-
scope: "inline",
|
|
1998
|
-
validate(value) {
|
|
1999
|
-
return value === true;
|
|
2000
|
-
}
|
|
2001
|
-
};
|
|
2002
|
-
|
|
2003
|
-
// src/schema/formats/inline/subscript.ts
|
|
2004
|
-
var subscriptFormat = {
|
|
2005
|
-
name: "subscript",
|
|
2006
|
-
scope: "inline",
|
|
2007
|
-
validate(value) {
|
|
2008
|
-
return value === true;
|
|
2009
|
-
}
|
|
2010
|
-
};
|
|
2011
|
-
|
|
2012
|
-
// src/schema/formats/inline/superscript.ts
|
|
2013
|
-
var superscriptFormat = {
|
|
2014
|
-
name: "superscript",
|
|
2015
|
-
scope: "inline",
|
|
2016
|
-
validate(value) {
|
|
2017
|
-
return value === true;
|
|
2018
|
-
}
|
|
2019
|
-
};
|
|
2020
|
-
|
|
2021
|
-
// src/schema/formats/inline/underline.ts
|
|
2022
|
-
var underlineFormat = {
|
|
2023
|
-
name: "underline",
|
|
2024
|
-
scope: "inline",
|
|
2025
|
-
validate(value) {
|
|
2026
|
-
return value === true;
|
|
2027
|
-
}
|
|
2028
|
-
};
|
|
2029
|
-
|
|
2030
|
-
// src/schema/formats/block/align.ts
|
|
2031
|
-
var VALID_ALIGN_TYPES = ["left", "center", "right", "justify"];
|
|
2032
|
-
var alignFormat = {
|
|
2033
|
-
name: "align",
|
|
2034
|
-
scope: "block",
|
|
2035
|
-
normalize(value) {
|
|
2036
|
-
return value.toLowerCase();
|
|
2037
|
-
},
|
|
2038
|
-
validate(value) {
|
|
2039
|
-
return VALID_ALIGN_TYPES.includes(value);
|
|
2040
|
-
}
|
|
2041
|
-
};
|
|
2042
|
-
|
|
2043
|
-
// src/schema/formats/block/blockquote.ts
|
|
2044
|
-
var blockquoteFormat = {
|
|
2045
|
-
name: "blockquote",
|
|
2046
|
-
scope: "block",
|
|
2047
|
-
validate(value) {
|
|
2048
|
-
return value === true;
|
|
2049
|
-
}
|
|
2050
|
-
};
|
|
2051
|
-
|
|
2052
|
-
// src/schema/formats/block/code-block.ts
|
|
2053
|
-
var codeBlockFormat = {
|
|
2054
|
-
name: "code-block",
|
|
2055
|
-
scope: "block",
|
|
2056
|
-
normalize(value) {
|
|
2057
|
-
if (typeof value === "string") {
|
|
2058
|
-
return value.toLowerCase().trim();
|
|
2059
|
-
}
|
|
2060
|
-
return value;
|
|
2061
|
-
},
|
|
2062
|
-
validate(value) {
|
|
2063
|
-
if (value === true) {
|
|
2064
|
-
return true;
|
|
2065
|
-
}
|
|
2066
|
-
if (typeof value === "string" && value.length > 0) {
|
|
2067
|
-
return true;
|
|
2068
|
-
}
|
|
2069
|
-
return false;
|
|
2070
|
-
}
|
|
2071
|
-
};
|
|
2072
|
-
|
|
2073
|
-
// src/schema/formats/block/header.ts
|
|
2074
|
-
var headerFormat = {
|
|
2075
|
-
name: "header",
|
|
2076
|
-
scope: "block",
|
|
2077
|
-
normalize(value) {
|
|
2078
|
-
return Math.max(1, Math.min(6, Math.floor(value)));
|
|
2079
|
-
},
|
|
2080
|
-
validate(value) {
|
|
2081
|
-
return Number.isInteger(value) && value >= 1 && value <= 6;
|
|
2082
|
-
}
|
|
2083
|
-
};
|
|
2084
|
-
|
|
2085
|
-
// src/schema/formats/block/header-id.ts
|
|
2086
|
-
var headerIdFormat = {
|
|
2087
|
-
name: "header-id",
|
|
2088
|
-
scope: "block",
|
|
2089
|
-
normalize(value) {
|
|
2090
|
-
return String(value).trim().toLowerCase();
|
|
2091
|
-
},
|
|
2092
|
-
validate(value) {
|
|
2093
|
-
if (typeof value !== "string") return false;
|
|
2094
|
-
const trimmed = value.trim();
|
|
2095
|
-
return trimmed.length > 0 && !/\s/.test(trimmed);
|
|
2096
|
-
}
|
|
2097
|
-
};
|
|
2098
|
-
|
|
2099
|
-
// src/schema/formats/block/indent.ts
|
|
2100
|
-
var MAX_INDENT = 8;
|
|
2101
|
-
var indentFormat = {
|
|
2102
|
-
name: "indent",
|
|
2103
|
-
scope: "block",
|
|
2104
|
-
normalize(value) {
|
|
2105
|
-
return Math.max(0, Math.min(MAX_INDENT, Math.floor(value)));
|
|
2106
|
-
},
|
|
2107
|
-
validate(value) {
|
|
2108
|
-
return Number.isInteger(value) && value >= 0 && value <= MAX_INDENT;
|
|
2109
|
-
}
|
|
2110
|
-
};
|
|
2111
|
-
|
|
2112
|
-
// src/schema/formats/block/list.ts
|
|
2113
|
-
var VALID_LIST_TYPES = ["ordered", "bullet", "checked", "unchecked"];
|
|
2114
|
-
var listFormat = {
|
|
2115
|
-
name: "list",
|
|
2116
|
-
scope: "block",
|
|
2117
|
-
normalize(value) {
|
|
2118
|
-
return value.toLowerCase();
|
|
2119
|
-
},
|
|
2120
|
-
validate(value) {
|
|
2121
|
-
return VALID_LIST_TYPES.includes(value);
|
|
2122
|
-
}
|
|
2123
|
-
};
|
|
2124
|
-
|
|
2125
|
-
// src/schema/formats/block/table-row.ts
|
|
2126
|
-
var tableRowFormat = {
|
|
2127
|
-
name: "table-row",
|
|
2128
|
-
scope: "block",
|
|
2129
|
-
validate(value) {
|
|
2130
|
-
return typeof value === "number" && Number.isInteger(value) && value >= 0;
|
|
2131
|
-
}
|
|
2132
|
-
};
|
|
2133
|
-
|
|
2134
|
-
// src/schema/formats/block/table-col.ts
|
|
2135
|
-
var tableColFormat = {
|
|
2136
|
-
name: "table-col",
|
|
2137
|
-
scope: "block",
|
|
2138
|
-
validate(value) {
|
|
2139
|
-
return typeof value === "number" && Number.isInteger(value) && value >= 0;
|
|
2140
|
-
}
|
|
2141
|
-
};
|
|
2142
|
-
|
|
2143
|
-
// src/schema/formats/block/table-header.ts
|
|
2144
|
-
var tableHeaderFormat = {
|
|
2145
|
-
name: "table-header",
|
|
2146
|
-
scope: "block",
|
|
2256
|
+
|
|
2257
|
+
// src/schema/formats/inline/code.ts
|
|
2258
|
+
var codeFormat = {
|
|
2259
|
+
name: "code",
|
|
2260
|
+
scope: "inline",
|
|
2147
2261
|
validate(value) {
|
|
2148
2262
|
return value === true;
|
|
2149
2263
|
}
|
|
2150
2264
|
};
|
|
2151
2265
|
|
|
2152
|
-
// src/schema/formats/
|
|
2153
|
-
var
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
scope: "block",
|
|
2266
|
+
// src/schema/formats/inline/color.ts
|
|
2267
|
+
var colorFormat = {
|
|
2268
|
+
name: "color",
|
|
2269
|
+
scope: "inline",
|
|
2157
2270
|
normalize(value) {
|
|
2158
|
-
return value
|
|
2271
|
+
return toHexColor(value);
|
|
2159
2272
|
},
|
|
2160
2273
|
validate(value) {
|
|
2161
|
-
return
|
|
2274
|
+
return typeof value === "string" && isValidColor(value);
|
|
2162
2275
|
}
|
|
2163
2276
|
};
|
|
2164
2277
|
|
|
2165
|
-
// src/schema/formats/
|
|
2166
|
-
var
|
|
2167
|
-
name: "
|
|
2168
|
-
scope: "
|
|
2278
|
+
// src/schema/formats/inline/font.ts
|
|
2279
|
+
var fontFormat = {
|
|
2280
|
+
name: "font",
|
|
2281
|
+
scope: "inline",
|
|
2169
2282
|
validate(value) {
|
|
2170
|
-
return typeof value === "
|
|
2283
|
+
return typeof value === "string" && value.length > 0;
|
|
2171
2284
|
}
|
|
2172
2285
|
};
|
|
2173
2286
|
|
|
2174
|
-
// src/
|
|
2175
|
-
var
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
subscript: "sub",
|
|
2182
|
-
superscript: "sup",
|
|
2183
|
-
code: "code",
|
|
2184
|
-
mark: "mark",
|
|
2185
|
-
kbd: "kbd"
|
|
2186
|
-
};
|
|
2187
|
-
var INLINE_FORMAT_ORDER = [
|
|
2188
|
-
"link",
|
|
2189
|
-
"bold",
|
|
2190
|
-
"italic",
|
|
2191
|
-
"underline",
|
|
2192
|
-
"strike",
|
|
2193
|
-
"subscript",
|
|
2194
|
-
"superscript",
|
|
2195
|
-
"code",
|
|
2196
|
-
"mark",
|
|
2197
|
-
"kbd"
|
|
2198
|
-
];
|
|
2199
|
-
var INLINE_STYLE_FORMATS = {
|
|
2200
|
-
color: "color",
|
|
2201
|
-
background: "background-color",
|
|
2202
|
-
font: "font-family",
|
|
2203
|
-
size: "font-size"
|
|
2204
|
-
};
|
|
2205
|
-
var BLOCK_FORMAT_TAGS = {
|
|
2206
|
-
header: (value) => `h${String(value)}`,
|
|
2207
|
-
blockquote: "blockquote",
|
|
2208
|
-
"code-block": "pre",
|
|
2209
|
-
list: "li"
|
|
2210
|
-
// Wrapped in ul/ol based on list type
|
|
2287
|
+
// src/schema/formats/inline/italic.ts
|
|
2288
|
+
var italicFormat = {
|
|
2289
|
+
name: "italic",
|
|
2290
|
+
scope: "inline",
|
|
2291
|
+
validate(value) {
|
|
2292
|
+
return value === true;
|
|
2293
|
+
}
|
|
2211
2294
|
};
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2295
|
+
|
|
2296
|
+
// src/schema/formats/inline/kbd.ts
|
|
2297
|
+
var kbdFormat = {
|
|
2298
|
+
name: "kbd",
|
|
2299
|
+
scope: "inline",
|
|
2300
|
+
validate(value) {
|
|
2301
|
+
return value === true;
|
|
2302
|
+
}
|
|
2217
2303
|
};
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
const alt = altVal != null && (typeof altVal === "string" || typeof altVal === "number") ? ` alt="${escapeHtml(String(altVal))}"` : "";
|
|
2226
|
-
const width = widthVal != null && (typeof widthVal === "string" || typeof widthVal === "number") ? ` width="${String(widthVal)}"` : "";
|
|
2227
|
-
const height = heightVal != null && (typeof heightVal === "string" || typeof heightVal === "number") ? ` height="${String(heightVal)}"` : "";
|
|
2228
|
-
const float = floatVal != null && typeof floatVal === "string" && floatVal !== "none" ? ` data-float="${escapeHtml(floatVal)}"` : "";
|
|
2229
|
-
return `<img src="${escapeHtml(src)}"${alt}${width}${height}${float}>`;
|
|
2304
|
+
|
|
2305
|
+
// src/schema/formats/inline/link.ts
|
|
2306
|
+
var linkFormat = {
|
|
2307
|
+
name: "link",
|
|
2308
|
+
scope: "inline",
|
|
2309
|
+
normalize(value) {
|
|
2310
|
+
return value.trim();
|
|
2230
2311
|
},
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
const widthVal = attrs?.width;
|
|
2235
|
-
const heightVal = attrs?.height;
|
|
2236
|
-
const float = floatVal != null && typeof floatVal === "string" && floatVal !== "none" ? ` data-float="${escapeHtml(floatVal)}"` : "";
|
|
2237
|
-
const styles = [];
|
|
2238
|
-
if (widthVal != null && (typeof widthVal === "string" || typeof widthVal === "number")) {
|
|
2239
|
-
const w = String(widthVal);
|
|
2240
|
-
if (w && w !== "auto") styles.push(`width: ${/^\d+$/.test(w) ? w + "px" : w}`);
|
|
2312
|
+
validate(value) {
|
|
2313
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
2314
|
+
return false;
|
|
2241
2315
|
}
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2316
|
+
const trimmed = value.trim();
|
|
2317
|
+
if (trimmed.startsWith("/") || trimmed.startsWith("./") || trimmed.startsWith("../")) {
|
|
2318
|
+
return true;
|
|
2245
2319
|
}
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
if (embedSrc) {
|
|
2249
|
-
return `<iframe src="${escapeHtml(embedSrc)}" frameborder="0" allowfullscreen${renderEmbedIframeIsolationAttrs(context, "video")}${float}${style}></iframe>`;
|
|
2320
|
+
if (trimmed.startsWith("//")) {
|
|
2321
|
+
return true;
|
|
2250
2322
|
}
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
codeWidget: (value, attrs, context) => {
|
|
2254
|
-
const src = typeof value === "string" ? value : "";
|
|
2255
|
-
const floatVal = attrs?.float;
|
|
2256
|
-
const widthVal = attrs?.width;
|
|
2257
|
-
const heightVal = attrs?.height;
|
|
2258
|
-
const float = floatVal != null && typeof floatVal === "string" && floatVal !== "none" ? ` data-float="${escapeHtml(floatVal)}"` : "";
|
|
2259
|
-
const styles = [];
|
|
2260
|
-
if (widthVal != null && (typeof widthVal === "string" || typeof widthVal === "number")) {
|
|
2261
|
-
const w = String(widthVal);
|
|
2262
|
-
if (w && w !== "auto") styles.push(`width: ${/^\d+$/.test(w) ? w + "px" : w}`);
|
|
2323
|
+
if (trimmed.startsWith("mailto:") || trimmed.startsWith("tel:")) {
|
|
2324
|
+
return true;
|
|
2263
2325
|
}
|
|
2264
|
-
|
|
2265
|
-
const
|
|
2266
|
-
|
|
2326
|
+
try {
|
|
2327
|
+
const url = new URL(trimmed);
|
|
2328
|
+
return url.protocol === "http:" || url.protocol === "https:";
|
|
2329
|
+
} catch {
|
|
2330
|
+
return false;
|
|
2267
2331
|
}
|
|
2268
|
-
|
|
2269
|
-
const embedSrc = toCodeWidgetEmbedUrl(src);
|
|
2270
|
-
return `<iframe data-code-widget src="${escapeHtml(embedSrc)}" frameborder="0" allowfullscreen${renderEmbedIframeIsolationAttrs(context, "codeWidget")}${float}${style}></iframe>`;
|
|
2271
|
-
},
|
|
2272
|
-
formula: (value) => {
|
|
2273
|
-
const latex = typeof value === "string" ? value : "";
|
|
2274
|
-
return `<span class="formula" data-formula="${escapeHtml(latex)}">${escapeHtml(latex)}</span>`;
|
|
2275
|
-
},
|
|
2276
|
-
diagram: (value) => {
|
|
2277
|
-
const source = typeof value === "string" ? value : "";
|
|
2278
|
-
return `<span class="diagram" data-diagram="${escapeHtml(source)}">${escapeHtml(source)}</span>`;
|
|
2279
|
-
},
|
|
2280
|
-
drawio: (value, attrs) => {
|
|
2281
|
-
const src = typeof value === "string" ? value : "";
|
|
2282
|
-
const altVal = attrs?.alt;
|
|
2283
|
-
const alt = altVal != null && (typeof altVal === "string" || typeof altVal === "number") ? ` data-alt="${escapeHtml(String(altVal))}"` : "";
|
|
2284
|
-
return `<span class="drawio" data-drawio-src="${escapeHtml(src)}"${alt}></span>`;
|
|
2285
|
-
},
|
|
2286
|
-
"footnote-ref": (value) => {
|
|
2287
|
-
const id = typeof value === "string" ? value : String(value);
|
|
2288
|
-
return `<sup class="footnote-ref"><a href="#fn-${escapeHtml(id)}" id="fnref-${escapeHtml(id)}">[${escapeHtml(id)}]</a></sup>`;
|
|
2289
|
-
},
|
|
2290
|
-
divider: () => "<hr>",
|
|
2291
|
-
// Soft line break (Shift+Enter equivalent). Emitted with an explicit
|
|
2292
|
-
// `data-scrider-embed` marker so that html-to-delta can distinguish this
|
|
2293
|
-
// embed from the placeholder `<br>` that appears inside an empty
|
|
2294
|
-
// paragraph (`<p><br></p>`) without relying solely on positional
|
|
2295
|
-
// heuristics. See `soft-break.ts` for the format definition.
|
|
2296
|
-
softBreak: () => "<br data-scrider-embed>"
|
|
2297
|
-
};
|
|
2298
|
-
var TAG_TO_INLINE_FORMAT = {
|
|
2299
|
-
strong: { format: "bold", value: true },
|
|
2300
|
-
b: { format: "bold", value: true },
|
|
2301
|
-
em: { format: "italic", value: true },
|
|
2302
|
-
i: { format: "italic", value: true },
|
|
2303
|
-
u: { format: "underline", value: true },
|
|
2304
|
-
ins: { format: "underline", value: true },
|
|
2305
|
-
s: { format: "strike", value: true },
|
|
2306
|
-
strike: { format: "strike", value: true },
|
|
2307
|
-
del: { format: "strike", value: true },
|
|
2308
|
-
sub: { format: "subscript", value: true },
|
|
2309
|
-
sup: { format: "superscript", value: true },
|
|
2310
|
-
code: { format: "code", value: true },
|
|
2311
|
-
mark: { format: "mark", value: true },
|
|
2312
|
-
kbd: { format: "kbd", value: true }
|
|
2313
|
-
};
|
|
2314
|
-
var TAG_TO_BLOCK_FORMAT = {
|
|
2315
|
-
h1: { format: "header", value: 1 },
|
|
2316
|
-
h2: { format: "header", value: 2 },
|
|
2317
|
-
h3: { format: "header", value: 3 },
|
|
2318
|
-
h4: { format: "header", value: 4 },
|
|
2319
|
-
h5: { format: "header", value: 5 },
|
|
2320
|
-
h6: { format: "header", value: 6 },
|
|
2321
|
-
blockquote: { format: "blockquote", value: true },
|
|
2322
|
-
pre: { format: "code-block", value: true }
|
|
2332
|
+
}
|
|
2323
2333
|
};
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2334
|
+
|
|
2335
|
+
// src/schema/formats/inline/mark.ts
|
|
2336
|
+
var markFormat = {
|
|
2337
|
+
name: "mark",
|
|
2338
|
+
scope: "inline",
|
|
2339
|
+
validate(value) {
|
|
2340
|
+
return value === true;
|
|
2341
|
+
}
|
|
2329
2342
|
};
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
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")) {
|
|
2338
|
-
return url;
|
|
2343
|
+
|
|
2344
|
+
// src/schema/formats/inline/size.ts
|
|
2345
|
+
var sizeFormat = {
|
|
2346
|
+
name: "size",
|
|
2347
|
+
scope: "inline",
|
|
2348
|
+
validate(value) {
|
|
2349
|
+
return typeof value === "string" && value.length > 0;
|
|
2339
2350
|
}
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2351
|
+
};
|
|
2352
|
+
|
|
2353
|
+
// src/schema/formats/inline/strike.ts
|
|
2354
|
+
var strikeFormat = {
|
|
2355
|
+
name: "strike",
|
|
2356
|
+
scope: "inline",
|
|
2357
|
+
validate(value) {
|
|
2358
|
+
return value === true;
|
|
2343
2359
|
}
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2360
|
+
};
|
|
2361
|
+
|
|
2362
|
+
// src/schema/formats/inline/subscript.ts
|
|
2363
|
+
var subscriptFormat = {
|
|
2364
|
+
name: "subscript",
|
|
2365
|
+
scope: "inline",
|
|
2366
|
+
validate(value) {
|
|
2367
|
+
return value === true;
|
|
2347
2368
|
}
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2369
|
+
};
|
|
2370
|
+
|
|
2371
|
+
// src/schema/formats/inline/superscript.ts
|
|
2372
|
+
var superscriptFormat = {
|
|
2373
|
+
name: "superscript",
|
|
2374
|
+
scope: "inline",
|
|
2375
|
+
validate(value) {
|
|
2376
|
+
return value === true;
|
|
2351
2377
|
}
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2378
|
+
};
|
|
2379
|
+
|
|
2380
|
+
// src/schema/formats/inline/underline.ts
|
|
2381
|
+
var underlineFormat = {
|
|
2382
|
+
name: "underline",
|
|
2383
|
+
scope: "inline",
|
|
2384
|
+
validate(value) {
|
|
2385
|
+
return value === true;
|
|
2358
2386
|
}
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2387
|
+
};
|
|
2388
|
+
|
|
2389
|
+
// src/schema/formats/block/align.ts
|
|
2390
|
+
var VALID_ALIGN_TYPES = ["left", "center", "right", "justify"];
|
|
2391
|
+
var alignFormat = {
|
|
2392
|
+
name: "align",
|
|
2393
|
+
scope: "block",
|
|
2394
|
+
normalize(value) {
|
|
2395
|
+
return value.toLowerCase();
|
|
2396
|
+
},
|
|
2397
|
+
validate(value) {
|
|
2398
|
+
return VALID_ALIGN_TYPES.includes(value);
|
|
2362
2399
|
}
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
rest = rest.slice(0, hashIdx);
|
|
2400
|
+
};
|
|
2401
|
+
|
|
2402
|
+
// src/schema/formats/block/blockquote.ts
|
|
2403
|
+
var blockquoteFormat = {
|
|
2404
|
+
name: "blockquote",
|
|
2405
|
+
scope: "block",
|
|
2406
|
+
validate(value) {
|
|
2407
|
+
return value === true;
|
|
2372
2408
|
}
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2409
|
+
};
|
|
2410
|
+
|
|
2411
|
+
// src/schema/formats/block/code-block.ts
|
|
2412
|
+
var codeBlockFormat = {
|
|
2413
|
+
name: "code-block",
|
|
2414
|
+
scope: "block",
|
|
2415
|
+
normalize(value) {
|
|
2416
|
+
if (typeof value === "string") {
|
|
2417
|
+
return value.toLowerCase().trim();
|
|
2418
|
+
}
|
|
2419
|
+
return value;
|
|
2420
|
+
},
|
|
2421
|
+
validate(value) {
|
|
2422
|
+
if (value === true) {
|
|
2423
|
+
return true;
|
|
2424
|
+
}
|
|
2425
|
+
if (typeof value === "string" && value.length > 0) {
|
|
2426
|
+
return true;
|
|
2427
|
+
}
|
|
2428
|
+
return false;
|
|
2378
2429
|
}
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
var CODE_WIDGET_IFRAME_ALLOW = "accelerometer; camera; encrypted-media; geolocation; gyroscope; microphone; midi; payment; usb; xr-spatial-tracking; cross-origin-isolated";
|
|
2391
|
-
function renderEmbedIframeIsolationAttrs(context, kind) {
|
|
2392
|
-
const opts = context?.embed;
|
|
2393
|
-
const parts = [];
|
|
2394
|
-
if (kind === "codeWidget" && opts?.crossOriginIsolated) {
|
|
2395
|
-
parts.push(`allow="${CODE_WIDGET_IFRAME_ALLOW}"`);
|
|
2430
|
+
};
|
|
2431
|
+
|
|
2432
|
+
// src/schema/formats/block/header.ts
|
|
2433
|
+
var headerFormat = {
|
|
2434
|
+
name: "header",
|
|
2435
|
+
scope: "block",
|
|
2436
|
+
normalize(value) {
|
|
2437
|
+
return Math.max(1, Math.min(6, Math.floor(value)));
|
|
2438
|
+
},
|
|
2439
|
+
validate(value) {
|
|
2440
|
+
return Number.isInteger(value) && value >= 1 && value <= 6;
|
|
2396
2441
|
}
|
|
2397
|
-
|
|
2398
|
-
|
|
2442
|
+
};
|
|
2443
|
+
|
|
2444
|
+
// src/schema/formats/block/header-id.ts
|
|
2445
|
+
var headerIdFormat = {
|
|
2446
|
+
name: "header-id",
|
|
2447
|
+
scope: "block",
|
|
2448
|
+
normalize(value) {
|
|
2449
|
+
return String(value).trim().toLowerCase();
|
|
2450
|
+
},
|
|
2451
|
+
validate(value) {
|
|
2452
|
+
if (typeof value !== "string") return false;
|
|
2453
|
+
const trimmed = value.trim();
|
|
2454
|
+
return trimmed.length > 0 && !/\s/.test(trimmed);
|
|
2399
2455
|
}
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2456
|
+
};
|
|
2457
|
+
|
|
2458
|
+
// src/schema/formats/block/indent.ts
|
|
2459
|
+
var MAX_INDENT = 8;
|
|
2460
|
+
var indentFormat = {
|
|
2461
|
+
name: "indent",
|
|
2462
|
+
scope: "block",
|
|
2463
|
+
normalize(value) {
|
|
2464
|
+
return Math.max(0, Math.min(MAX_INDENT, Math.floor(value)));
|
|
2465
|
+
},
|
|
2466
|
+
validate(value) {
|
|
2467
|
+
return Number.isInteger(value) && value >= 0 && value <= MAX_INDENT;
|
|
2407
2468
|
}
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2469
|
+
};
|
|
2470
|
+
|
|
2471
|
+
// src/schema/formats/block/list.ts
|
|
2472
|
+
var VALID_LIST_TYPES = ["ordered", "bullet", "checked", "unchecked"];
|
|
2473
|
+
var listFormat = {
|
|
2474
|
+
name: "list",
|
|
2475
|
+
scope: "block",
|
|
2476
|
+
normalize(value) {
|
|
2477
|
+
return value.toLowerCase();
|
|
2478
|
+
},
|
|
2479
|
+
validate(value) {
|
|
2480
|
+
return VALID_LIST_TYPES.includes(value);
|
|
2411
2481
|
}
|
|
2412
|
-
|
|
2413
|
-
|
|
2482
|
+
};
|
|
2483
|
+
|
|
2484
|
+
// src/schema/formats/block/table-row.ts
|
|
2485
|
+
var tableRowFormat = {
|
|
2486
|
+
name: "table-row",
|
|
2487
|
+
scope: "block",
|
|
2488
|
+
validate(value) {
|
|
2489
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 0;
|
|
2414
2490
|
}
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2491
|
+
};
|
|
2492
|
+
|
|
2493
|
+
// src/schema/formats/block/table-col.ts
|
|
2494
|
+
var tableColFormat = {
|
|
2495
|
+
name: "table-col",
|
|
2496
|
+
scope: "block",
|
|
2497
|
+
validate(value) {
|
|
2498
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 0;
|
|
2418
2499
|
}
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2500
|
+
};
|
|
2501
|
+
|
|
2502
|
+
// src/schema/formats/block/table-header.ts
|
|
2503
|
+
var tableHeaderFormat = {
|
|
2504
|
+
name: "table-header",
|
|
2505
|
+
scope: "block",
|
|
2506
|
+
validate(value) {
|
|
2507
|
+
return value === true;
|
|
2424
2508
|
}
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2509
|
+
};
|
|
2510
|
+
|
|
2511
|
+
// src/schema/formats/block/table-col-align.ts
|
|
2512
|
+
var VALID_ALIGNS = ["left", "center", "right"];
|
|
2513
|
+
var tableColAlignFormat = {
|
|
2514
|
+
name: "table-col-align",
|
|
2515
|
+
scope: "block",
|
|
2516
|
+
normalize(value) {
|
|
2517
|
+
return value.toLowerCase();
|
|
2518
|
+
},
|
|
2519
|
+
validate(value) {
|
|
2520
|
+
return VALID_ALIGNS.includes(value);
|
|
2428
2521
|
}
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2522
|
+
};
|
|
2523
|
+
|
|
2524
|
+
// src/schema/formats/embed/block.ts
|
|
2525
|
+
var blockFormat = {
|
|
2526
|
+
name: "block",
|
|
2527
|
+
scope: "embed",
|
|
2528
|
+
validate(value) {
|
|
2529
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) && typeof value.type === "string" && value.type.length > 0;
|
|
2432
2530
|
}
|
|
2433
|
-
|
|
2434
|
-
}
|
|
2531
|
+
};
|
|
2435
2532
|
|
|
2436
2533
|
// src/schema/formats/embed/codeWidget.ts
|
|
2437
2534
|
var codeWidgetFormat = {
|
|
@@ -3986,6 +4083,10 @@ function htmlToDelta(html, options = {}) {
|
|
|
3986
4083
|
processBoxElement(node);
|
|
3987
4084
|
return;
|
|
3988
4085
|
}
|
|
4086
|
+
if (node.hasAttribute("data-scrider-ext-table")) {
|
|
4087
|
+
processExtTableHostElement(node);
|
|
4088
|
+
return;
|
|
4089
|
+
}
|
|
3989
4090
|
}
|
|
3990
4091
|
if (tagName === "p" || tagName === "div") {
|
|
3991
4092
|
processDefaultBlock(node);
|
|
@@ -4329,27 +4430,43 @@ function htmlToDelta(html, options = {}) {
|
|
|
4329
4430
|
}
|
|
4330
4431
|
processChildren(element);
|
|
4331
4432
|
}
|
|
4332
|
-
function
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
if (!innerHtml) return [{ insert: "\n" }];
|
|
4341
|
-
return htmlToDelta(innerHtml, options).ops;
|
|
4342
|
-
}
|
|
4343
|
-
};
|
|
4344
|
-
const data = tableHandler.fromHtml(table, blockContext);
|
|
4345
|
-
if (data) {
|
|
4346
|
-
flushText();
|
|
4347
|
-
delta.insert({ block: data });
|
|
4348
|
-
delta.insert("\n");
|
|
4349
|
-
atLineStart = true;
|
|
4350
|
-
return;
|
|
4433
|
+
function processExtTableHostElement(host) {
|
|
4434
|
+
let table = null;
|
|
4435
|
+
const children2 = host.childNodes;
|
|
4436
|
+
for (let i = 0; i < children2.length; i++) {
|
|
4437
|
+
const child = children2[i];
|
|
4438
|
+
if (child && isElement(child) && child.tagName.toLowerCase() === "table") {
|
|
4439
|
+
table = child;
|
|
4440
|
+
break;
|
|
4351
4441
|
}
|
|
4352
4442
|
}
|
|
4443
|
+
if (table && insertExtendedTableBlock(table, host)) return;
|
|
4444
|
+
processDefaultBlock(host);
|
|
4445
|
+
}
|
|
4446
|
+
function makeTableBlockParseContext() {
|
|
4447
|
+
return {
|
|
4448
|
+
registry: void 0,
|
|
4449
|
+
parseElement: (el) => {
|
|
4450
|
+
const innerHtml = el.innerHTML ?? "";
|
|
4451
|
+
if (!innerHtml) return [{ insert: "\n" }];
|
|
4452
|
+
return htmlToDelta(innerHtml, options).ops;
|
|
4453
|
+
}
|
|
4454
|
+
};
|
|
4455
|
+
}
|
|
4456
|
+
function insertExtendedTableBlock(table, host) {
|
|
4457
|
+
const tableHandler = options.blockHandlers?.get("table");
|
|
4458
|
+
if (!tableHandler) return false;
|
|
4459
|
+
let data = tableHandler.fromHtml(table, makeTableBlockParseContext());
|
|
4460
|
+
if (!data) return false;
|
|
4461
|
+
if (host) data = enrichTableBlockFromHost(data, host);
|
|
4462
|
+
flushText();
|
|
4463
|
+
delta.insert({ block: data });
|
|
4464
|
+
delta.insert("\n");
|
|
4465
|
+
atLineStart = true;
|
|
4466
|
+
return true;
|
|
4467
|
+
}
|
|
4468
|
+
function processTableElement(table) {
|
|
4469
|
+
if (insertExtendedTableBlock(table, null)) return;
|
|
4353
4470
|
let rowIdx = 0;
|
|
4354
4471
|
const tableChildren = table.childNodes;
|
|
4355
4472
|
for (let i = 0; i < tableChildren.length; i++) {
|