@scrider/formatter 1.8.3 → 1.8.5
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 +649 -521
- 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 +649 -521
- 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",
|
|
@@ -596,6 +861,46 @@ function promoteAlignFromCellOps(cell) {
|
|
|
596
861
|
}
|
|
597
862
|
return cell;
|
|
598
863
|
}
|
|
864
|
+
function parseVerticalAlign(value) {
|
|
865
|
+
const normalized = value?.trim().toLowerCase();
|
|
866
|
+
if (!normalized) return null;
|
|
867
|
+
if (normalized === "top" || normalized === "baseline" || normalized === "text-top") return "top";
|
|
868
|
+
if (normalized === "middle" || normalized === "center") return "middle";
|
|
869
|
+
if (normalized === "bottom" || normalized === "text-bottom") return "bottom";
|
|
870
|
+
return null;
|
|
871
|
+
}
|
|
872
|
+
function extractInlineVerticalAlign(element) {
|
|
873
|
+
const verticalAlign = element.style?.getPropertyValue?.("vertical-align");
|
|
874
|
+
const fromStyle = parseVerticalAlign(verticalAlign);
|
|
875
|
+
if (fromStyle) return fromStyle;
|
|
876
|
+
const style = element.getAttribute("style") || "";
|
|
877
|
+
const match = style.match(/vertical-align:\s*(top|middle|bottom|center|baseline|text-top|text-bottom)/i);
|
|
878
|
+
if (match?.[1]) {
|
|
879
|
+
const parsed = parseVerticalAlign(match[1]);
|
|
880
|
+
if (parsed) return parsed;
|
|
881
|
+
}
|
|
882
|
+
const msoMatch = style.match(/mso-vertical-align:\s*(top|middle|bottom|center)/i);
|
|
883
|
+
if (msoMatch?.[1]) {
|
|
884
|
+
const parsed = parseVerticalAlign(msoMatch[1]);
|
|
885
|
+
if (parsed) return parsed;
|
|
886
|
+
}
|
|
887
|
+
return null;
|
|
888
|
+
}
|
|
889
|
+
function extractCellVAlign(cell) {
|
|
890
|
+
const direct = extractInlineVerticalAlign(cell);
|
|
891
|
+
if (direct) return direct;
|
|
892
|
+
const children = cell.childNodes;
|
|
893
|
+
for (let i = 0; i < children.length; i++) {
|
|
894
|
+
const child = children[i];
|
|
895
|
+
if (!child || !isElement(child)) continue;
|
|
896
|
+
const tag = child.tagName.toLowerCase();
|
|
897
|
+
if (tag === "p" || tag === "div") {
|
|
898
|
+
const vAlign = extractInlineVerticalAlign(child);
|
|
899
|
+
if (vAlign) return vAlign;
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
return null;
|
|
903
|
+
}
|
|
599
904
|
function renderExtendedRow(data, row, cols, defaultCellTag, context, pretty) {
|
|
600
905
|
const nl = pretty ? "\n" : "";
|
|
601
906
|
const ind = (level) => pretty ? " ".repeat(level) : "";
|
|
@@ -617,14 +922,17 @@ function renderExtendedRow(data, row, cols, defaultCellTag, context, pretty) {
|
|
|
617
922
|
}
|
|
618
923
|
const colDefault = data.colAligns?.[c] ?? "left";
|
|
619
924
|
const effectiveAlign = resolveCellHorizontalAlign(cell, c, data.colAligns);
|
|
620
|
-
|
|
925
|
+
const styleParts = [];
|
|
621
926
|
if (effectiveAlign !== "left") {
|
|
622
|
-
|
|
927
|
+
styleParts.push(`text-align: ${effectiveAlign}`);
|
|
623
928
|
} else if (cell.align === "left" && colDefault !== "left") {
|
|
624
|
-
|
|
929
|
+
styleParts.push("text-align: left");
|
|
930
|
+
}
|
|
931
|
+
if (cell.vAlign !== void 0 && cell.vAlign !== "top") {
|
|
932
|
+
styleParts.push(`vertical-align: ${cell.vAlign}`);
|
|
625
933
|
}
|
|
626
|
-
if (
|
|
627
|
-
attrs.push(`style="${
|
|
934
|
+
if (styleParts.length > 0) {
|
|
935
|
+
attrs.push(`style="${styleParts.join("; ")}"`);
|
|
628
936
|
}
|
|
629
937
|
const attrStr = attrs.length > 0 ? " " + attrs.join(" ") : "";
|
|
630
938
|
let content = "";
|
|
@@ -636,6 +944,46 @@ function renderExtendedRow(data, row, cols, defaultCellTag, context, pretty) {
|
|
|
636
944
|
html += `${ind(2)}</tr>${nl}`;
|
|
637
945
|
return html;
|
|
638
946
|
}
|
|
947
|
+
function parseTableBlockFloat(value) {
|
|
948
|
+
if (value === "left" || value === "center" || value === "right") return value;
|
|
949
|
+
return void 0;
|
|
950
|
+
}
|
|
951
|
+
function extractHostBlockWidthPx(host) {
|
|
952
|
+
const style = host.getAttribute("style") || "";
|
|
953
|
+
const widthMatch = style.match(/(?:^|;\s*)width:\s*([\d.]+)px/i);
|
|
954
|
+
if (!widthMatch?.[1]) return void 0;
|
|
955
|
+
const n = parseFloat(widthMatch[1]);
|
|
956
|
+
if (!Number.isFinite(n) || n <= 0) return void 0;
|
|
957
|
+
return Math.round(n);
|
|
958
|
+
}
|
|
959
|
+
function enrichTableBlockFromHost(data, host) {
|
|
960
|
+
const float = parseTableBlockFloat(host.getAttribute("data-float"));
|
|
961
|
+
const width = extractHostBlockWidthPx(host);
|
|
962
|
+
if (float == null && width == null) return data;
|
|
963
|
+
const next = { ...data };
|
|
964
|
+
if (float != null) next.float = float;
|
|
965
|
+
if (width != null) next.width = width;
|
|
966
|
+
return next;
|
|
967
|
+
}
|
|
968
|
+
function tableNeedsHostWrapper(data) {
|
|
969
|
+
return data.float != null && VALID_TABLE_BLOCK_FLOATS.includes(data.float) || data.width != null && data.width > 0;
|
|
970
|
+
}
|
|
971
|
+
function renderTableHostWrapperOpen(data, pretty) {
|
|
972
|
+
const nl = pretty ? "\n" : "";
|
|
973
|
+
const ind = pretty ? " " : "";
|
|
974
|
+
const attrs = [`class="${EXT_TABLE_HOST_CLASS}"`, EXT_TABLE_HOST_ATTR];
|
|
975
|
+
if (data.float) attrs.push(`data-float="${escapeHtml(data.float)}"`);
|
|
976
|
+
const styleParts = [];
|
|
977
|
+
if (data.width != null && data.width > 0) {
|
|
978
|
+
styleParts.push(`width: ${Math.round(data.width)}px`);
|
|
979
|
+
styleParts.push("max-width: 100%");
|
|
980
|
+
}
|
|
981
|
+
if (styleParts.length > 0) attrs.push(`style="${styleParts.join("; ")}"`);
|
|
982
|
+
return `<div ${attrs.join(" ")}>${nl}${ind}`;
|
|
983
|
+
}
|
|
984
|
+
function renderTableHostWrapperClose(pretty) {
|
|
985
|
+
return pretty ? "</div>\n" : "</div>";
|
|
986
|
+
}
|
|
639
987
|
function isGfmCompatible(data) {
|
|
640
988
|
if (data.colWidths && data.colWidths.some((w) => w > 0)) {
|
|
641
989
|
return false;
|
|
@@ -643,8 +991,17 @@ function isGfmCompatible(data) {
|
|
|
643
991
|
if (data.rowHeights && data.rowHeights.some((h) => h > 0)) {
|
|
644
992
|
return false;
|
|
645
993
|
}
|
|
994
|
+
if (data.width != null && data.width > 0) {
|
|
995
|
+
return false;
|
|
996
|
+
}
|
|
997
|
+
if (data.float != null) {
|
|
998
|
+
return false;
|
|
999
|
+
}
|
|
1000
|
+
for (const cell of Object.values(data.cells)) {
|
|
1001
|
+
if (cell !== null && cell.align !== void 0) return false;
|
|
1002
|
+
}
|
|
646
1003
|
for (const cell of Object.values(data.cells)) {
|
|
647
|
-
if (cell !== null && cell.
|
|
1004
|
+
if (cell !== null && cell.vAlign !== void 0) return false;
|
|
648
1005
|
}
|
|
649
1006
|
for (const cell of Object.values(data.cells)) {
|
|
650
1007
|
if (cell === null) return false;
|
|
@@ -859,6 +1216,10 @@ function parseTableElement(table, context) {
|
|
|
859
1216
|
delete withoutAlign.align;
|
|
860
1217
|
cellData = withoutAlign;
|
|
861
1218
|
}
|
|
1219
|
+
const htmlVAlign = extractCellVAlign(cell);
|
|
1220
|
+
if (htmlVAlign && htmlVAlign !== "top") {
|
|
1221
|
+
cellData = { ...cellData, vAlign: htmlVAlign };
|
|
1222
|
+
}
|
|
862
1223
|
const cellKey = `${rowIdx}:${colIdx}`;
|
|
863
1224
|
cells[cellKey] = cellData;
|
|
864
1225
|
rawAligns[cellKey] = effectiveAlign;
|
|
@@ -988,6 +1349,12 @@ var tableBlockHandler = {
|
|
|
988
1349
|
if (typeof h !== "number" || h < 0) return false;
|
|
989
1350
|
}
|
|
990
1351
|
}
|
|
1352
|
+
if (data.width !== void 0) {
|
|
1353
|
+
if (typeof data.width !== "number" || data.width <= 0) return false;
|
|
1354
|
+
}
|
|
1355
|
+
if (data.float !== void 0 && !VALID_TABLE_BLOCK_FLOATS.includes(data.float)) {
|
|
1356
|
+
return false;
|
|
1357
|
+
}
|
|
991
1358
|
if (data.colAligns !== void 0) {
|
|
992
1359
|
if (!Array.isArray(data.colAligns) || data.colAligns.length !== cols) {
|
|
993
1360
|
return false;
|
|
@@ -1039,6 +1406,9 @@ var tableBlockHandler = {
|
|
|
1039
1406
|
html += `${ind(1)}</tbody>${nl}`;
|
|
1040
1407
|
}
|
|
1041
1408
|
html += `</table>`;
|
|
1409
|
+
if (tableNeedsHostWrapper(data)) {
|
|
1410
|
+
return renderTableHostWrapperOpen(data, pretty) + html + renderTableHostWrapperClose(pretty);
|
|
1411
|
+
}
|
|
1042
1412
|
return html;
|
|
1043
1413
|
},
|
|
1044
1414
|
fromHtml(element, context) {
|
|
@@ -1844,544 +2214,282 @@ var boldFormat = {
|
|
|
1844
2214
|
return value === true;
|
|
1845
2215
|
}
|
|
1846
2216
|
};
|
|
1847
|
-
|
|
1848
|
-
// src/schema/formats/inline/code.ts
|
|
1849
|
-
var codeFormat = {
|
|
1850
|
-
name: "code",
|
|
1851
|
-
scope: "inline",
|
|
1852
|
-
validate(value) {
|
|
1853
|
-
return value === true;
|
|
1854
|
-
}
|
|
1855
|
-
};
|
|
1856
|
-
|
|
1857
|
-
// src/schema/formats/inline/color.ts
|
|
1858
|
-
var colorFormat = {
|
|
1859
|
-
name: "color",
|
|
1860
|
-
scope: "inline",
|
|
1861
|
-
normalize(value) {
|
|
1862
|
-
return toHexColor(value);
|
|
1863
|
-
},
|
|
1864
|
-
validate(value) {
|
|
1865
|
-
return typeof value === "string" && isValidColor(value);
|
|
1866
|
-
}
|
|
1867
|
-
};
|
|
1868
|
-
|
|
1869
|
-
// src/schema/formats/inline/font.ts
|
|
1870
|
-
var fontFormat = {
|
|
1871
|
-
name: "font",
|
|
1872
|
-
scope: "inline",
|
|
1873
|
-
validate(value) {
|
|
1874
|
-
return typeof value === "string" && value.length > 0;
|
|
1875
|
-
}
|
|
1876
|
-
};
|
|
1877
|
-
|
|
1878
|
-
// src/schema/formats/inline/italic.ts
|
|
1879
|
-
var italicFormat = {
|
|
1880
|
-
name: "italic",
|
|
1881
|
-
scope: "inline",
|
|
1882
|
-
validate(value) {
|
|
1883
|
-
return value === true;
|
|
1884
|
-
}
|
|
1885
|
-
};
|
|
1886
|
-
|
|
1887
|
-
// src/schema/formats/inline/kbd.ts
|
|
1888
|
-
var kbdFormat = {
|
|
1889
|
-
name: "kbd",
|
|
1890
|
-
scope: "inline",
|
|
1891
|
-
validate(value) {
|
|
1892
|
-
return value === true;
|
|
1893
|
-
}
|
|
1894
|
-
};
|
|
1895
|
-
|
|
1896
|
-
// src/schema/formats/inline/link.ts
|
|
1897
|
-
var linkFormat = {
|
|
1898
|
-
name: "link",
|
|
1899
|
-
scope: "inline",
|
|
1900
|
-
normalize(value) {
|
|
1901
|
-
return value.trim();
|
|
1902
|
-
},
|
|
1903
|
-
validate(value) {
|
|
1904
|
-
if (typeof value !== "string" || value.length === 0) {
|
|
1905
|
-
return false;
|
|
1906
|
-
}
|
|
1907
|
-
const trimmed = value.trim();
|
|
1908
|
-
if (trimmed.startsWith("/") || trimmed.startsWith("./") || trimmed.startsWith("../")) {
|
|
1909
|
-
return true;
|
|
1910
|
-
}
|
|
1911
|
-
if (trimmed.startsWith("//")) {
|
|
1912
|
-
return true;
|
|
1913
|
-
}
|
|
1914
|
-
if (trimmed.startsWith("mailto:") || trimmed.startsWith("tel:")) {
|
|
1915
|
-
return true;
|
|
1916
|
-
}
|
|
1917
|
-
try {
|
|
1918
|
-
const url = new URL(trimmed);
|
|
1919
|
-
return url.protocol === "http:" || url.protocol === "https:";
|
|
1920
|
-
} catch {
|
|
1921
|
-
return false;
|
|
1922
|
-
}
|
|
1923
|
-
}
|
|
1924
|
-
};
|
|
1925
|
-
|
|
1926
|
-
// src/schema/formats/inline/mark.ts
|
|
1927
|
-
var markFormat = {
|
|
1928
|
-
name: "mark",
|
|
1929
|
-
scope: "inline",
|
|
1930
|
-
validate(value) {
|
|
1931
|
-
return value === true;
|
|
1932
|
-
}
|
|
1933
|
-
};
|
|
1934
|
-
|
|
1935
|
-
// src/schema/formats/inline/size.ts
|
|
1936
|
-
var sizeFormat = {
|
|
1937
|
-
name: "size",
|
|
1938
|
-
scope: "inline",
|
|
1939
|
-
validate(value) {
|
|
1940
|
-
return typeof value === "string" && value.length > 0;
|
|
1941
|
-
}
|
|
1942
|
-
};
|
|
1943
|
-
|
|
1944
|
-
// src/schema/formats/inline/strike.ts
|
|
1945
|
-
var strikeFormat = {
|
|
1946
|
-
name: "strike",
|
|
1947
|
-
scope: "inline",
|
|
1948
|
-
validate(value) {
|
|
1949
|
-
return value === true;
|
|
1950
|
-
}
|
|
1951
|
-
};
|
|
1952
|
-
|
|
1953
|
-
// src/schema/formats/inline/subscript.ts
|
|
1954
|
-
var subscriptFormat = {
|
|
1955
|
-
name: "subscript",
|
|
1956
|
-
scope: "inline",
|
|
1957
|
-
validate(value) {
|
|
1958
|
-
return value === true;
|
|
1959
|
-
}
|
|
1960
|
-
};
|
|
1961
|
-
|
|
1962
|
-
// src/schema/formats/inline/superscript.ts
|
|
1963
|
-
var superscriptFormat = {
|
|
1964
|
-
name: "superscript",
|
|
1965
|
-
scope: "inline",
|
|
1966
|
-
validate(value) {
|
|
1967
|
-
return value === true;
|
|
1968
|
-
}
|
|
1969
|
-
};
|
|
1970
|
-
|
|
1971
|
-
// src/schema/formats/inline/underline.ts
|
|
1972
|
-
var underlineFormat = {
|
|
1973
|
-
name: "underline",
|
|
1974
|
-
scope: "inline",
|
|
1975
|
-
validate(value) {
|
|
1976
|
-
return value === true;
|
|
1977
|
-
}
|
|
1978
|
-
};
|
|
1979
|
-
|
|
1980
|
-
// src/schema/formats/block/align.ts
|
|
1981
|
-
var VALID_ALIGN_TYPES = ["left", "center", "right", "justify"];
|
|
1982
|
-
var alignFormat = {
|
|
1983
|
-
name: "align",
|
|
1984
|
-
scope: "block",
|
|
1985
|
-
normalize(value) {
|
|
1986
|
-
return value.toLowerCase();
|
|
1987
|
-
},
|
|
1988
|
-
validate(value) {
|
|
1989
|
-
return VALID_ALIGN_TYPES.includes(value);
|
|
1990
|
-
}
|
|
1991
|
-
};
|
|
1992
|
-
|
|
1993
|
-
// src/schema/formats/block/blockquote.ts
|
|
1994
|
-
var blockquoteFormat = {
|
|
1995
|
-
name: "blockquote",
|
|
1996
|
-
scope: "block",
|
|
1997
|
-
validate(value) {
|
|
1998
|
-
return value === true;
|
|
1999
|
-
}
|
|
2000
|
-
};
|
|
2001
|
-
|
|
2002
|
-
// src/schema/formats/block/code-block.ts
|
|
2003
|
-
var codeBlockFormat = {
|
|
2004
|
-
name: "code-block",
|
|
2005
|
-
scope: "block",
|
|
2006
|
-
normalize(value) {
|
|
2007
|
-
if (typeof value === "string") {
|
|
2008
|
-
return value.toLowerCase().trim();
|
|
2009
|
-
}
|
|
2010
|
-
return value;
|
|
2011
|
-
},
|
|
2012
|
-
validate(value) {
|
|
2013
|
-
if (value === true) {
|
|
2014
|
-
return true;
|
|
2015
|
-
}
|
|
2016
|
-
if (typeof value === "string" && value.length > 0) {
|
|
2017
|
-
return true;
|
|
2018
|
-
}
|
|
2019
|
-
return false;
|
|
2020
|
-
}
|
|
2021
|
-
};
|
|
2022
|
-
|
|
2023
|
-
// src/schema/formats/block/header.ts
|
|
2024
|
-
var headerFormat = {
|
|
2025
|
-
name: "header",
|
|
2026
|
-
scope: "block",
|
|
2027
|
-
normalize(value) {
|
|
2028
|
-
return Math.max(1, Math.min(6, Math.floor(value)));
|
|
2029
|
-
},
|
|
2030
|
-
validate(value) {
|
|
2031
|
-
return Number.isInteger(value) && value >= 1 && value <= 6;
|
|
2032
|
-
}
|
|
2033
|
-
};
|
|
2034
|
-
|
|
2035
|
-
// src/schema/formats/block/header-id.ts
|
|
2036
|
-
var headerIdFormat = {
|
|
2037
|
-
name: "header-id",
|
|
2038
|
-
scope: "block",
|
|
2039
|
-
normalize(value) {
|
|
2040
|
-
return String(value).trim().toLowerCase();
|
|
2041
|
-
},
|
|
2042
|
-
validate(value) {
|
|
2043
|
-
if (typeof value !== "string") return false;
|
|
2044
|
-
const trimmed = value.trim();
|
|
2045
|
-
return trimmed.length > 0 && !/\s/.test(trimmed);
|
|
2046
|
-
}
|
|
2047
|
-
};
|
|
2048
|
-
|
|
2049
|
-
// src/schema/formats/block/indent.ts
|
|
2050
|
-
var MAX_INDENT = 8;
|
|
2051
|
-
var indentFormat = {
|
|
2052
|
-
name: "indent",
|
|
2053
|
-
scope: "block",
|
|
2054
|
-
normalize(value) {
|
|
2055
|
-
return Math.max(0, Math.min(MAX_INDENT, Math.floor(value)));
|
|
2056
|
-
},
|
|
2057
|
-
validate(value) {
|
|
2058
|
-
return Number.isInteger(value) && value >= 0 && value <= MAX_INDENT;
|
|
2059
|
-
}
|
|
2060
|
-
};
|
|
2061
|
-
|
|
2062
|
-
// src/schema/formats/block/list.ts
|
|
2063
|
-
var VALID_LIST_TYPES = ["ordered", "bullet", "checked", "unchecked"];
|
|
2064
|
-
var listFormat = {
|
|
2065
|
-
name: "list",
|
|
2066
|
-
scope: "block",
|
|
2067
|
-
normalize(value) {
|
|
2068
|
-
return value.toLowerCase();
|
|
2069
|
-
},
|
|
2070
|
-
validate(value) {
|
|
2071
|
-
return VALID_LIST_TYPES.includes(value);
|
|
2072
|
-
}
|
|
2073
|
-
};
|
|
2074
|
-
|
|
2075
|
-
// src/schema/formats/block/table-row.ts
|
|
2076
|
-
var tableRowFormat = {
|
|
2077
|
-
name: "table-row",
|
|
2078
|
-
scope: "block",
|
|
2079
|
-
validate(value) {
|
|
2080
|
-
return typeof value === "number" && Number.isInteger(value) && value >= 0;
|
|
2081
|
-
}
|
|
2082
|
-
};
|
|
2083
|
-
|
|
2084
|
-
// src/schema/formats/block/table-col.ts
|
|
2085
|
-
var tableColFormat = {
|
|
2086
|
-
name: "table-col",
|
|
2087
|
-
scope: "block",
|
|
2088
|
-
validate(value) {
|
|
2089
|
-
return typeof value === "number" && Number.isInteger(value) && value >= 0;
|
|
2090
|
-
}
|
|
2091
|
-
};
|
|
2092
|
-
|
|
2093
|
-
// src/schema/formats/block/table-header.ts
|
|
2094
|
-
var tableHeaderFormat = {
|
|
2095
|
-
name: "table-header",
|
|
2096
|
-
scope: "block",
|
|
2217
|
+
|
|
2218
|
+
// src/schema/formats/inline/code.ts
|
|
2219
|
+
var codeFormat = {
|
|
2220
|
+
name: "code",
|
|
2221
|
+
scope: "inline",
|
|
2097
2222
|
validate(value) {
|
|
2098
2223
|
return value === true;
|
|
2099
2224
|
}
|
|
2100
2225
|
};
|
|
2101
2226
|
|
|
2102
|
-
// src/schema/formats/
|
|
2103
|
-
var
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
scope: "block",
|
|
2227
|
+
// src/schema/formats/inline/color.ts
|
|
2228
|
+
var colorFormat = {
|
|
2229
|
+
name: "color",
|
|
2230
|
+
scope: "inline",
|
|
2107
2231
|
normalize(value) {
|
|
2108
|
-
return value
|
|
2232
|
+
return toHexColor(value);
|
|
2109
2233
|
},
|
|
2110
2234
|
validate(value) {
|
|
2111
|
-
return
|
|
2235
|
+
return typeof value === "string" && isValidColor(value);
|
|
2112
2236
|
}
|
|
2113
2237
|
};
|
|
2114
2238
|
|
|
2115
|
-
// src/schema/formats/
|
|
2116
|
-
var
|
|
2117
|
-
name: "
|
|
2118
|
-
scope: "
|
|
2239
|
+
// src/schema/formats/inline/font.ts
|
|
2240
|
+
var fontFormat = {
|
|
2241
|
+
name: "font",
|
|
2242
|
+
scope: "inline",
|
|
2119
2243
|
validate(value) {
|
|
2120
|
-
return typeof value === "
|
|
2244
|
+
return typeof value === "string" && value.length > 0;
|
|
2121
2245
|
}
|
|
2122
2246
|
};
|
|
2123
2247
|
|
|
2124
|
-
// src/
|
|
2125
|
-
var
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
subscript: "sub",
|
|
2132
|
-
superscript: "sup",
|
|
2133
|
-
code: "code",
|
|
2134
|
-
mark: "mark",
|
|
2135
|
-
kbd: "kbd"
|
|
2136
|
-
};
|
|
2137
|
-
var INLINE_FORMAT_ORDER = [
|
|
2138
|
-
"link",
|
|
2139
|
-
"bold",
|
|
2140
|
-
"italic",
|
|
2141
|
-
"underline",
|
|
2142
|
-
"strike",
|
|
2143
|
-
"subscript",
|
|
2144
|
-
"superscript",
|
|
2145
|
-
"code",
|
|
2146
|
-
"mark",
|
|
2147
|
-
"kbd"
|
|
2148
|
-
];
|
|
2149
|
-
var INLINE_STYLE_FORMATS = {
|
|
2150
|
-
color: "color",
|
|
2151
|
-
background: "background-color",
|
|
2152
|
-
font: "font-family",
|
|
2153
|
-
size: "font-size"
|
|
2154
|
-
};
|
|
2155
|
-
var BLOCK_FORMAT_TAGS = {
|
|
2156
|
-
header: (value) => `h${String(value)}`,
|
|
2157
|
-
blockquote: "blockquote",
|
|
2158
|
-
"code-block": "pre",
|
|
2159
|
-
list: "li"
|
|
2160
|
-
// Wrapped in ul/ol based on list type
|
|
2248
|
+
// src/schema/formats/inline/italic.ts
|
|
2249
|
+
var italicFormat = {
|
|
2250
|
+
name: "italic",
|
|
2251
|
+
scope: "inline",
|
|
2252
|
+
validate(value) {
|
|
2253
|
+
return value === true;
|
|
2254
|
+
}
|
|
2161
2255
|
};
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2256
|
+
|
|
2257
|
+
// src/schema/formats/inline/kbd.ts
|
|
2258
|
+
var kbdFormat = {
|
|
2259
|
+
name: "kbd",
|
|
2260
|
+
scope: "inline",
|
|
2261
|
+
validate(value) {
|
|
2262
|
+
return value === true;
|
|
2263
|
+
}
|
|
2167
2264
|
};
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
const alt = altVal != null && (typeof altVal === "string" || typeof altVal === "number") ? ` alt="${escapeHtml(String(altVal))}"` : "";
|
|
2176
|
-
const width = widthVal != null && (typeof widthVal === "string" || typeof widthVal === "number") ? ` width="${String(widthVal)}"` : "";
|
|
2177
|
-
const height = heightVal != null && (typeof heightVal === "string" || typeof heightVal === "number") ? ` height="${String(heightVal)}"` : "";
|
|
2178
|
-
const float = floatVal != null && typeof floatVal === "string" && floatVal !== "none" ? ` data-float="${escapeHtml(floatVal)}"` : "";
|
|
2179
|
-
return `<img src="${escapeHtml(src)}"${alt}${width}${height}${float}>`;
|
|
2265
|
+
|
|
2266
|
+
// src/schema/formats/inline/link.ts
|
|
2267
|
+
var linkFormat = {
|
|
2268
|
+
name: "link",
|
|
2269
|
+
scope: "inline",
|
|
2270
|
+
normalize(value) {
|
|
2271
|
+
return value.trim();
|
|
2180
2272
|
},
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
const widthVal = attrs?.width;
|
|
2185
|
-
const heightVal = attrs?.height;
|
|
2186
|
-
const float = floatVal != null && typeof floatVal === "string" && floatVal !== "none" ? ` data-float="${escapeHtml(floatVal)}"` : "";
|
|
2187
|
-
const styles = [];
|
|
2188
|
-
if (widthVal != null && (typeof widthVal === "string" || typeof widthVal === "number")) {
|
|
2189
|
-
const w = String(widthVal);
|
|
2190
|
-
if (w && w !== "auto") styles.push(`width: ${/^\d+$/.test(w) ? w + "px" : w}`);
|
|
2273
|
+
validate(value) {
|
|
2274
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
2275
|
+
return false;
|
|
2191
2276
|
}
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2277
|
+
const trimmed = value.trim();
|
|
2278
|
+
if (trimmed.startsWith("/") || trimmed.startsWith("./") || trimmed.startsWith("../")) {
|
|
2279
|
+
return true;
|
|
2195
2280
|
}
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
if (embedSrc) {
|
|
2199
|
-
return `<iframe src="${escapeHtml(embedSrc)}" frameborder="0" allowfullscreen${renderEmbedIframeIsolationAttrs(context, "video")}${float}${style}></iframe>`;
|
|
2281
|
+
if (trimmed.startsWith("//")) {
|
|
2282
|
+
return true;
|
|
2200
2283
|
}
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
codeWidget: (value, attrs, context) => {
|
|
2204
|
-
const src = typeof value === "string" ? value : "";
|
|
2205
|
-
const floatVal = attrs?.float;
|
|
2206
|
-
const widthVal = attrs?.width;
|
|
2207
|
-
const heightVal = attrs?.height;
|
|
2208
|
-
const float = floatVal != null && typeof floatVal === "string" && floatVal !== "none" ? ` data-float="${escapeHtml(floatVal)}"` : "";
|
|
2209
|
-
const styles = [];
|
|
2210
|
-
if (widthVal != null && (typeof widthVal === "string" || typeof widthVal === "number")) {
|
|
2211
|
-
const w = String(widthVal);
|
|
2212
|
-
if (w && w !== "auto") styles.push(`width: ${/^\d+$/.test(w) ? w + "px" : w}`);
|
|
2284
|
+
if (trimmed.startsWith("mailto:") || trimmed.startsWith("tel:")) {
|
|
2285
|
+
return true;
|
|
2213
2286
|
}
|
|
2214
|
-
|
|
2215
|
-
const
|
|
2216
|
-
|
|
2287
|
+
try {
|
|
2288
|
+
const url = new URL(trimmed);
|
|
2289
|
+
return url.protocol === "http:" || url.protocol === "https:";
|
|
2290
|
+
} catch {
|
|
2291
|
+
return false;
|
|
2217
2292
|
}
|
|
2218
|
-
|
|
2219
|
-
const embedSrc = toCodeWidgetEmbedUrl(src);
|
|
2220
|
-
return `<iframe data-code-widget src="${escapeHtml(embedSrc)}" frameborder="0" allowfullscreen${renderEmbedIframeIsolationAttrs(context, "codeWidget")}${float}${style}></iframe>`;
|
|
2221
|
-
},
|
|
2222
|
-
formula: (value) => {
|
|
2223
|
-
const latex = typeof value === "string" ? value : "";
|
|
2224
|
-
return `<span class="formula" data-formula="${escapeHtml(latex)}">${escapeHtml(latex)}</span>`;
|
|
2225
|
-
},
|
|
2226
|
-
diagram: (value) => {
|
|
2227
|
-
const source = typeof value === "string" ? value : "";
|
|
2228
|
-
return `<span class="diagram" data-diagram="${escapeHtml(source)}">${escapeHtml(source)}</span>`;
|
|
2229
|
-
},
|
|
2230
|
-
drawio: (value, attrs) => {
|
|
2231
|
-
const src = typeof value === "string" ? value : "";
|
|
2232
|
-
const altVal = attrs?.alt;
|
|
2233
|
-
const alt = altVal != null && (typeof altVal === "string" || typeof altVal === "number") ? ` data-alt="${escapeHtml(String(altVal))}"` : "";
|
|
2234
|
-
return `<span class="drawio" data-drawio-src="${escapeHtml(src)}"${alt}></span>`;
|
|
2235
|
-
},
|
|
2236
|
-
"footnote-ref": (value) => {
|
|
2237
|
-
const id = typeof value === "string" ? value : String(value);
|
|
2238
|
-
return `<sup class="footnote-ref"><a href="#fn-${escapeHtml(id)}" id="fnref-${escapeHtml(id)}">[${escapeHtml(id)}]</a></sup>`;
|
|
2239
|
-
},
|
|
2240
|
-
divider: () => "<hr>",
|
|
2241
|
-
// Soft line break (Shift+Enter equivalent). Emitted with an explicit
|
|
2242
|
-
// `data-scrider-embed` marker so that html-to-delta can distinguish this
|
|
2243
|
-
// embed from the placeholder `<br>` that appears inside an empty
|
|
2244
|
-
// paragraph (`<p><br></p>`) without relying solely on positional
|
|
2245
|
-
// heuristics. See `soft-break.ts` for the format definition.
|
|
2246
|
-
softBreak: () => "<br data-scrider-embed>"
|
|
2247
|
-
};
|
|
2248
|
-
var TAG_TO_INLINE_FORMAT = {
|
|
2249
|
-
strong: { format: "bold", value: true },
|
|
2250
|
-
b: { format: "bold", value: true },
|
|
2251
|
-
em: { format: "italic", value: true },
|
|
2252
|
-
i: { format: "italic", value: true },
|
|
2253
|
-
u: { format: "underline", value: true },
|
|
2254
|
-
ins: { format: "underline", value: true },
|
|
2255
|
-
s: { format: "strike", value: true },
|
|
2256
|
-
strike: { format: "strike", value: true },
|
|
2257
|
-
del: { format: "strike", value: true },
|
|
2258
|
-
sub: { format: "subscript", value: true },
|
|
2259
|
-
sup: { format: "superscript", value: true },
|
|
2260
|
-
code: { format: "code", value: true },
|
|
2261
|
-
mark: { format: "mark", value: true },
|
|
2262
|
-
kbd: { format: "kbd", value: true }
|
|
2263
|
-
};
|
|
2264
|
-
var TAG_TO_BLOCK_FORMAT = {
|
|
2265
|
-
h1: { format: "header", value: 1 },
|
|
2266
|
-
h2: { format: "header", value: 2 },
|
|
2267
|
-
h3: { format: "header", value: 3 },
|
|
2268
|
-
h4: { format: "header", value: 4 },
|
|
2269
|
-
h5: { format: "header", value: 5 },
|
|
2270
|
-
h6: { format: "header", value: 6 },
|
|
2271
|
-
blockquote: { format: "blockquote", value: true },
|
|
2272
|
-
pre: { format: "code-block", value: true }
|
|
2293
|
+
}
|
|
2273
2294
|
};
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2295
|
+
|
|
2296
|
+
// src/schema/formats/inline/mark.ts
|
|
2297
|
+
var markFormat = {
|
|
2298
|
+
name: "mark",
|
|
2299
|
+
scope: "inline",
|
|
2300
|
+
validate(value) {
|
|
2301
|
+
return value === true;
|
|
2302
|
+
}
|
|
2279
2303
|
};
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
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")) {
|
|
2288
|
-
return url;
|
|
2304
|
+
|
|
2305
|
+
// src/schema/formats/inline/size.ts
|
|
2306
|
+
var sizeFormat = {
|
|
2307
|
+
name: "size",
|
|
2308
|
+
scope: "inline",
|
|
2309
|
+
validate(value) {
|
|
2310
|
+
return typeof value === "string" && value.length > 0;
|
|
2289
2311
|
}
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2312
|
+
};
|
|
2313
|
+
|
|
2314
|
+
// src/schema/formats/inline/strike.ts
|
|
2315
|
+
var strikeFormat = {
|
|
2316
|
+
name: "strike",
|
|
2317
|
+
scope: "inline",
|
|
2318
|
+
validate(value) {
|
|
2319
|
+
return value === true;
|
|
2293
2320
|
}
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2321
|
+
};
|
|
2322
|
+
|
|
2323
|
+
// src/schema/formats/inline/subscript.ts
|
|
2324
|
+
var subscriptFormat = {
|
|
2325
|
+
name: "subscript",
|
|
2326
|
+
scope: "inline",
|
|
2327
|
+
validate(value) {
|
|
2328
|
+
return value === true;
|
|
2297
2329
|
}
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2330
|
+
};
|
|
2331
|
+
|
|
2332
|
+
// src/schema/formats/inline/superscript.ts
|
|
2333
|
+
var superscriptFormat = {
|
|
2334
|
+
name: "superscript",
|
|
2335
|
+
scope: "inline",
|
|
2336
|
+
validate(value) {
|
|
2337
|
+
return value === true;
|
|
2301
2338
|
}
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2339
|
+
};
|
|
2340
|
+
|
|
2341
|
+
// src/schema/formats/inline/underline.ts
|
|
2342
|
+
var underlineFormat = {
|
|
2343
|
+
name: "underline",
|
|
2344
|
+
scope: "inline",
|
|
2345
|
+
validate(value) {
|
|
2346
|
+
return value === true;
|
|
2308
2347
|
}
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2348
|
+
};
|
|
2349
|
+
|
|
2350
|
+
// src/schema/formats/block/align.ts
|
|
2351
|
+
var VALID_ALIGN_TYPES = ["left", "center", "right", "justify"];
|
|
2352
|
+
var alignFormat = {
|
|
2353
|
+
name: "align",
|
|
2354
|
+
scope: "block",
|
|
2355
|
+
normalize(value) {
|
|
2356
|
+
return value.toLowerCase();
|
|
2357
|
+
},
|
|
2358
|
+
validate(value) {
|
|
2359
|
+
return VALID_ALIGN_TYPES.includes(value);
|
|
2312
2360
|
}
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
rest = rest.slice(0, hashIdx);
|
|
2361
|
+
};
|
|
2362
|
+
|
|
2363
|
+
// src/schema/formats/block/blockquote.ts
|
|
2364
|
+
var blockquoteFormat = {
|
|
2365
|
+
name: "blockquote",
|
|
2366
|
+
scope: "block",
|
|
2367
|
+
validate(value) {
|
|
2368
|
+
return value === true;
|
|
2322
2369
|
}
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2370
|
+
};
|
|
2371
|
+
|
|
2372
|
+
// src/schema/formats/block/code-block.ts
|
|
2373
|
+
var codeBlockFormat = {
|
|
2374
|
+
name: "code-block",
|
|
2375
|
+
scope: "block",
|
|
2376
|
+
normalize(value) {
|
|
2377
|
+
if (typeof value === "string") {
|
|
2378
|
+
return value.toLowerCase().trim();
|
|
2379
|
+
}
|
|
2380
|
+
return value;
|
|
2381
|
+
},
|
|
2382
|
+
validate(value) {
|
|
2383
|
+
if (value === true) {
|
|
2384
|
+
return true;
|
|
2385
|
+
}
|
|
2386
|
+
if (typeof value === "string" && value.length > 0) {
|
|
2387
|
+
return true;
|
|
2388
|
+
}
|
|
2389
|
+
return false;
|
|
2328
2390
|
}
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
var CODE_WIDGET_IFRAME_ALLOW = "accelerometer; camera; encrypted-media; geolocation; gyroscope; microphone; midi; payment; usb; xr-spatial-tracking; cross-origin-isolated";
|
|
2341
|
-
function renderEmbedIframeIsolationAttrs(context, kind) {
|
|
2342
|
-
const opts = context?.embed;
|
|
2343
|
-
const parts = [];
|
|
2344
|
-
if (kind === "codeWidget" && opts?.crossOriginIsolated) {
|
|
2345
|
-
parts.push(`allow="${CODE_WIDGET_IFRAME_ALLOW}"`);
|
|
2391
|
+
};
|
|
2392
|
+
|
|
2393
|
+
// src/schema/formats/block/header.ts
|
|
2394
|
+
var headerFormat = {
|
|
2395
|
+
name: "header",
|
|
2396
|
+
scope: "block",
|
|
2397
|
+
normalize(value) {
|
|
2398
|
+
return Math.max(1, Math.min(6, Math.floor(value)));
|
|
2399
|
+
},
|
|
2400
|
+
validate(value) {
|
|
2401
|
+
return Number.isInteger(value) && value >= 1 && value <= 6;
|
|
2346
2402
|
}
|
|
2347
|
-
|
|
2348
|
-
|
|
2403
|
+
};
|
|
2404
|
+
|
|
2405
|
+
// src/schema/formats/block/header-id.ts
|
|
2406
|
+
var headerIdFormat = {
|
|
2407
|
+
name: "header-id",
|
|
2408
|
+
scope: "block",
|
|
2409
|
+
normalize(value) {
|
|
2410
|
+
return String(value).trim().toLowerCase();
|
|
2411
|
+
},
|
|
2412
|
+
validate(value) {
|
|
2413
|
+
if (typeof value !== "string") return false;
|
|
2414
|
+
const trimmed = value.trim();
|
|
2415
|
+
return trimmed.length > 0 && !/\s/.test(trimmed);
|
|
2349
2416
|
}
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2417
|
+
};
|
|
2418
|
+
|
|
2419
|
+
// src/schema/formats/block/indent.ts
|
|
2420
|
+
var MAX_INDENT = 8;
|
|
2421
|
+
var indentFormat = {
|
|
2422
|
+
name: "indent",
|
|
2423
|
+
scope: "block",
|
|
2424
|
+
normalize(value) {
|
|
2425
|
+
return Math.max(0, Math.min(MAX_INDENT, Math.floor(value)));
|
|
2426
|
+
},
|
|
2427
|
+
validate(value) {
|
|
2428
|
+
return Number.isInteger(value) && value >= 0 && value <= MAX_INDENT;
|
|
2357
2429
|
}
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2430
|
+
};
|
|
2431
|
+
|
|
2432
|
+
// src/schema/formats/block/list.ts
|
|
2433
|
+
var VALID_LIST_TYPES = ["ordered", "bullet", "checked", "unchecked"];
|
|
2434
|
+
var listFormat = {
|
|
2435
|
+
name: "list",
|
|
2436
|
+
scope: "block",
|
|
2437
|
+
normalize(value) {
|
|
2438
|
+
return value.toLowerCase();
|
|
2439
|
+
},
|
|
2440
|
+
validate(value) {
|
|
2441
|
+
return VALID_LIST_TYPES.includes(value);
|
|
2361
2442
|
}
|
|
2362
|
-
|
|
2363
|
-
|
|
2443
|
+
};
|
|
2444
|
+
|
|
2445
|
+
// src/schema/formats/block/table-row.ts
|
|
2446
|
+
var tableRowFormat = {
|
|
2447
|
+
name: "table-row",
|
|
2448
|
+
scope: "block",
|
|
2449
|
+
validate(value) {
|
|
2450
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 0;
|
|
2364
2451
|
}
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2452
|
+
};
|
|
2453
|
+
|
|
2454
|
+
// src/schema/formats/block/table-col.ts
|
|
2455
|
+
var tableColFormat = {
|
|
2456
|
+
name: "table-col",
|
|
2457
|
+
scope: "block",
|
|
2458
|
+
validate(value) {
|
|
2459
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 0;
|
|
2368
2460
|
}
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2461
|
+
};
|
|
2462
|
+
|
|
2463
|
+
// src/schema/formats/block/table-header.ts
|
|
2464
|
+
var tableHeaderFormat = {
|
|
2465
|
+
name: "table-header",
|
|
2466
|
+
scope: "block",
|
|
2467
|
+
validate(value) {
|
|
2468
|
+
return value === true;
|
|
2374
2469
|
}
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2470
|
+
};
|
|
2471
|
+
|
|
2472
|
+
// src/schema/formats/block/table-col-align.ts
|
|
2473
|
+
var VALID_ALIGNS = ["left", "center", "right"];
|
|
2474
|
+
var tableColAlignFormat = {
|
|
2475
|
+
name: "table-col-align",
|
|
2476
|
+
scope: "block",
|
|
2477
|
+
normalize(value) {
|
|
2478
|
+
return value.toLowerCase();
|
|
2479
|
+
},
|
|
2480
|
+
validate(value) {
|
|
2481
|
+
return VALID_ALIGNS.includes(value);
|
|
2378
2482
|
}
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2483
|
+
};
|
|
2484
|
+
|
|
2485
|
+
// src/schema/formats/embed/block.ts
|
|
2486
|
+
var blockFormat = {
|
|
2487
|
+
name: "block",
|
|
2488
|
+
scope: "embed",
|
|
2489
|
+
validate(value) {
|
|
2490
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) && typeof value.type === "string" && value.type.length > 0;
|
|
2382
2491
|
}
|
|
2383
|
-
|
|
2384
|
-
}
|
|
2492
|
+
};
|
|
2385
2493
|
|
|
2386
2494
|
// src/schema/formats/embed/codeWidget.ts
|
|
2387
2495
|
var codeWidgetFormat = {
|
|
@@ -3936,6 +4044,10 @@ function htmlToDelta(html, options = {}) {
|
|
|
3936
4044
|
processBoxElement(node);
|
|
3937
4045
|
return;
|
|
3938
4046
|
}
|
|
4047
|
+
if (node.hasAttribute("data-scrider-ext-table")) {
|
|
4048
|
+
processExtTableHostElement(node);
|
|
4049
|
+
return;
|
|
4050
|
+
}
|
|
3939
4051
|
}
|
|
3940
4052
|
if (tagName === "p" || tagName === "div") {
|
|
3941
4053
|
processDefaultBlock(node);
|
|
@@ -4279,27 +4391,43 @@ function htmlToDelta(html, options = {}) {
|
|
|
4279
4391
|
}
|
|
4280
4392
|
processChildren(element);
|
|
4281
4393
|
}
|
|
4282
|
-
function
|
|
4283
|
-
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
if (!innerHtml) return [{ insert: "\n" }];
|
|
4291
|
-
return htmlToDelta(innerHtml, options).ops;
|
|
4292
|
-
}
|
|
4293
|
-
};
|
|
4294
|
-
const data = tableHandler.fromHtml(table, blockContext);
|
|
4295
|
-
if (data) {
|
|
4296
|
-
flushText();
|
|
4297
|
-
delta.insert({ block: data });
|
|
4298
|
-
delta.insert("\n");
|
|
4299
|
-
atLineStart = true;
|
|
4300
|
-
return;
|
|
4394
|
+
function processExtTableHostElement(host) {
|
|
4395
|
+
let table = null;
|
|
4396
|
+
const children2 = host.childNodes;
|
|
4397
|
+
for (let i = 0; i < children2.length; i++) {
|
|
4398
|
+
const child = children2[i];
|
|
4399
|
+
if (child && isElement(child) && child.tagName.toLowerCase() === "table") {
|
|
4400
|
+
table = child;
|
|
4401
|
+
break;
|
|
4301
4402
|
}
|
|
4302
4403
|
}
|
|
4404
|
+
if (table && insertExtendedTableBlock(table, host)) return;
|
|
4405
|
+
processDefaultBlock(host);
|
|
4406
|
+
}
|
|
4407
|
+
function makeTableBlockParseContext() {
|
|
4408
|
+
return {
|
|
4409
|
+
registry: void 0,
|
|
4410
|
+
parseElement: (el) => {
|
|
4411
|
+
const innerHtml = el.innerHTML ?? "";
|
|
4412
|
+
if (!innerHtml) return [{ insert: "\n" }];
|
|
4413
|
+
return htmlToDelta(innerHtml, options).ops;
|
|
4414
|
+
}
|
|
4415
|
+
};
|
|
4416
|
+
}
|
|
4417
|
+
function insertExtendedTableBlock(table, host) {
|
|
4418
|
+
const tableHandler = options.blockHandlers?.get("table");
|
|
4419
|
+
if (!tableHandler) return false;
|
|
4420
|
+
let data = tableHandler.fromHtml(table, makeTableBlockParseContext());
|
|
4421
|
+
if (!data) return false;
|
|
4422
|
+
if (host) data = enrichTableBlockFromHost(data, host);
|
|
4423
|
+
flushText();
|
|
4424
|
+
delta.insert({ block: data });
|
|
4425
|
+
delta.insert("\n");
|
|
4426
|
+
atLineStart = true;
|
|
4427
|
+
return true;
|
|
4428
|
+
}
|
|
4429
|
+
function processTableElement(table) {
|
|
4430
|
+
if (insertExtendedTableBlock(table, null)) return;
|
|
4303
4431
|
let rowIdx = 0;
|
|
4304
4432
|
const tableChildren = table.childNodes;
|
|
4305
4433
|
for (let i = 0; i < tableChildren.length; i++) {
|