@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.js
CHANGED
|
@@ -361,7 +361,272 @@ function isTextNode(node) {
|
|
|
361
361
|
return node.nodeType === NODE_TYPE.TEXT_NODE;
|
|
362
362
|
}
|
|
363
363
|
|
|
364
|
+
// src/conversion/html/config.ts
|
|
365
|
+
var INLINE_FORMAT_TAGS = {
|
|
366
|
+
link: "a",
|
|
367
|
+
bold: "strong",
|
|
368
|
+
italic: "em",
|
|
369
|
+
underline: "u",
|
|
370
|
+
strike: "s",
|
|
371
|
+
subscript: "sub",
|
|
372
|
+
superscript: "sup",
|
|
373
|
+
code: "code",
|
|
374
|
+
mark: "mark",
|
|
375
|
+
kbd: "kbd"
|
|
376
|
+
};
|
|
377
|
+
var INLINE_FORMAT_ORDER = [
|
|
378
|
+
"link",
|
|
379
|
+
"bold",
|
|
380
|
+
"italic",
|
|
381
|
+
"underline",
|
|
382
|
+
"strike",
|
|
383
|
+
"subscript",
|
|
384
|
+
"superscript",
|
|
385
|
+
"code",
|
|
386
|
+
"mark",
|
|
387
|
+
"kbd"
|
|
388
|
+
];
|
|
389
|
+
var INLINE_STYLE_FORMATS = {
|
|
390
|
+
color: "color",
|
|
391
|
+
background: "background-color",
|
|
392
|
+
font: "font-family",
|
|
393
|
+
size: "font-size"
|
|
394
|
+
};
|
|
395
|
+
var BLOCK_FORMAT_TAGS = {
|
|
396
|
+
header: (value) => `h${String(value)}`,
|
|
397
|
+
blockquote: "blockquote",
|
|
398
|
+
"code-block": "pre",
|
|
399
|
+
list: "li"
|
|
400
|
+
// Wrapped in ul/ol based on list type
|
|
401
|
+
};
|
|
402
|
+
var LIST_WRAPPER_TAGS = {
|
|
403
|
+
ordered: "ol",
|
|
404
|
+
bullet: "ul",
|
|
405
|
+
checked: "ul",
|
|
406
|
+
unchecked: "ul"
|
|
407
|
+
};
|
|
408
|
+
var EMBED_RENDERERS = {
|
|
409
|
+
image: (value, attrs) => {
|
|
410
|
+
const src = typeof value === "string" ? value : "";
|
|
411
|
+
const altVal = attrs?.alt;
|
|
412
|
+
const widthVal = attrs?.width;
|
|
413
|
+
const heightVal = attrs?.height;
|
|
414
|
+
const floatVal = attrs?.float;
|
|
415
|
+
const alt = altVal != null && (typeof altVal === "string" || typeof altVal === "number") ? ` alt="${escapeHtml(String(altVal))}"` : "";
|
|
416
|
+
const width = widthVal != null && (typeof widthVal === "string" || typeof widthVal === "number") ? ` width="${String(widthVal)}"` : "";
|
|
417
|
+
const height = heightVal != null && (typeof heightVal === "string" || typeof heightVal === "number") ? ` height="${String(heightVal)}"` : "";
|
|
418
|
+
const float = floatVal != null && typeof floatVal === "string" && floatVal !== "none" ? ` data-float="${escapeHtml(floatVal)}"` : "";
|
|
419
|
+
return `<img src="${escapeHtml(src)}"${alt}${width}${height}${float}>`;
|
|
420
|
+
},
|
|
421
|
+
video: (value, attrs, context) => {
|
|
422
|
+
const src = typeof value === "string" ? value : "";
|
|
423
|
+
const floatVal = attrs?.float;
|
|
424
|
+
const widthVal = attrs?.width;
|
|
425
|
+
const heightVal = attrs?.height;
|
|
426
|
+
const float = floatVal != null && typeof floatVal === "string" && floatVal !== "none" ? ` data-float="${escapeHtml(floatVal)}"` : "";
|
|
427
|
+
const styles = [];
|
|
428
|
+
if (widthVal != null && (typeof widthVal === "string" || typeof widthVal === "number")) {
|
|
429
|
+
const w = String(widthVal);
|
|
430
|
+
if (w && w !== "auto") styles.push(`width: ${/^\d+$/.test(w) ? w + "px" : w}`);
|
|
431
|
+
}
|
|
432
|
+
if (heightVal != null && (typeof heightVal === "string" || typeof heightVal === "number")) {
|
|
433
|
+
const h = String(heightVal);
|
|
434
|
+
if (h && h !== "auto") styles.push(`height: ${/^\d+$/.test(h) ? h + "px" : h}`);
|
|
435
|
+
}
|
|
436
|
+
const style = styles.length > 0 ? ` style="${styles.join("; ")}"` : "";
|
|
437
|
+
const embedSrc = toVideoEmbedUrl(src);
|
|
438
|
+
if (embedSrc) {
|
|
439
|
+
return `<iframe src="${escapeHtml(embedSrc)}" frameborder="0" allowfullscreen${renderEmbedIframeIsolationAttrs(context, "video")}${float}${style}></iframe>`;
|
|
440
|
+
}
|
|
441
|
+
return `<video src="${escapeHtml(src)}" controls${float}${style}></video>`;
|
|
442
|
+
},
|
|
443
|
+
codeWidget: (value, attrs, context) => {
|
|
444
|
+
const src = typeof value === "string" ? value : "";
|
|
445
|
+
const floatVal = attrs?.float;
|
|
446
|
+
const widthVal = attrs?.width;
|
|
447
|
+
const heightVal = attrs?.height;
|
|
448
|
+
const float = floatVal != null && typeof floatVal === "string" && floatVal !== "none" ? ` data-float="${escapeHtml(floatVal)}"` : "";
|
|
449
|
+
const styles = [];
|
|
450
|
+
if (widthVal != null && (typeof widthVal === "string" || typeof widthVal === "number")) {
|
|
451
|
+
const w = String(widthVal);
|
|
452
|
+
if (w && w !== "auto") styles.push(`width: ${/^\d+$/.test(w) ? w + "px" : w}`);
|
|
453
|
+
}
|
|
454
|
+
if (heightVal != null && (typeof heightVal === "string" || typeof heightVal === "number")) {
|
|
455
|
+
const h = String(heightVal);
|
|
456
|
+
if (h && h !== "auto") styles.push(`height: ${/^\d+$/.test(h) ? h + "px" : h}`);
|
|
457
|
+
}
|
|
458
|
+
const style = styles.length > 0 ? ` style="${styles.join("; ")}"` : "";
|
|
459
|
+
const embedSrc = toCodeWidgetEmbedUrl(src);
|
|
460
|
+
return `<iframe data-code-widget src="${escapeHtml(embedSrc)}" frameborder="0" allowfullscreen${renderEmbedIframeIsolationAttrs(context, "codeWidget")}${float}${style}></iframe>`;
|
|
461
|
+
},
|
|
462
|
+
formula: (value) => {
|
|
463
|
+
const latex = typeof value === "string" ? value : "";
|
|
464
|
+
return `<span class="formula" data-formula="${escapeHtml(latex)}">${escapeHtml(latex)}</span>`;
|
|
465
|
+
},
|
|
466
|
+
diagram: (value) => {
|
|
467
|
+
const source = typeof value === "string" ? value : "";
|
|
468
|
+
return `<span class="diagram" data-diagram="${escapeHtml(source)}">${escapeHtml(source)}</span>`;
|
|
469
|
+
},
|
|
470
|
+
drawio: (value, attrs) => {
|
|
471
|
+
const src = typeof value === "string" ? value : "";
|
|
472
|
+
const altVal = attrs?.alt;
|
|
473
|
+
const alt = altVal != null && (typeof altVal === "string" || typeof altVal === "number") ? ` data-alt="${escapeHtml(String(altVal))}"` : "";
|
|
474
|
+
return `<span class="drawio" data-drawio-src="${escapeHtml(src)}"${alt}></span>`;
|
|
475
|
+
},
|
|
476
|
+
"footnote-ref": (value) => {
|
|
477
|
+
const id = typeof value === "string" ? value : String(value);
|
|
478
|
+
return `<sup class="footnote-ref"><a href="#fn-${escapeHtml(id)}" id="fnref-${escapeHtml(id)}">[${escapeHtml(id)}]</a></sup>`;
|
|
479
|
+
},
|
|
480
|
+
divider: () => "<hr>",
|
|
481
|
+
// Soft line break (Shift+Enter equivalent). Emitted with an explicit
|
|
482
|
+
// `data-scrider-embed` marker so that html-to-delta can distinguish this
|
|
483
|
+
// embed from the placeholder `<br>` that appears inside an empty
|
|
484
|
+
// paragraph (`<p><br></p>`) without relying solely on positional
|
|
485
|
+
// heuristics. See `soft-break.ts` for the format definition.
|
|
486
|
+
softBreak: () => "<br data-scrider-embed>"
|
|
487
|
+
};
|
|
488
|
+
var TAG_TO_INLINE_FORMAT = {
|
|
489
|
+
strong: { format: "bold", value: true },
|
|
490
|
+
b: { format: "bold", value: true },
|
|
491
|
+
em: { format: "italic", value: true },
|
|
492
|
+
i: { format: "italic", value: true },
|
|
493
|
+
u: { format: "underline", value: true },
|
|
494
|
+
ins: { format: "underline", value: true },
|
|
495
|
+
s: { format: "strike", value: true },
|
|
496
|
+
strike: { format: "strike", value: true },
|
|
497
|
+
del: { format: "strike", value: true },
|
|
498
|
+
sub: { format: "subscript", value: true },
|
|
499
|
+
sup: { format: "superscript", value: true },
|
|
500
|
+
code: { format: "code", value: true },
|
|
501
|
+
mark: { format: "mark", value: true },
|
|
502
|
+
kbd: { format: "kbd", value: true }
|
|
503
|
+
};
|
|
504
|
+
var TAG_TO_BLOCK_FORMAT = {
|
|
505
|
+
h1: { format: "header", value: 1 },
|
|
506
|
+
h2: { format: "header", value: 2 },
|
|
507
|
+
h3: { format: "header", value: 3 },
|
|
508
|
+
h4: { format: "header", value: 4 },
|
|
509
|
+
h5: { format: "header", value: 5 },
|
|
510
|
+
h6: { format: "header", value: 6 },
|
|
511
|
+
blockquote: { format: "blockquote", value: true },
|
|
512
|
+
pre: { format: "code-block", value: true }
|
|
513
|
+
};
|
|
514
|
+
var CSS_ALIGN_TO_FORMAT = {
|
|
515
|
+
left: "left",
|
|
516
|
+
center: "center",
|
|
517
|
+
right: "right",
|
|
518
|
+
justify: "justify"
|
|
519
|
+
};
|
|
520
|
+
function escapeHtml(text) {
|
|
521
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
522
|
+
}
|
|
523
|
+
function unescapeHtml(text) {
|
|
524
|
+
return text.replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/&/g, "&");
|
|
525
|
+
}
|
|
526
|
+
function toVideoEmbedUrl(url) {
|
|
527
|
+
if (url.includes("youtube.com/embed") || url.includes("player.vimeo.com") || url.includes("dailymotion.com/embed") || url.includes("video_ext.php") || url.includes("rutube.ru/play/embed")) {
|
|
528
|
+
return url;
|
|
529
|
+
}
|
|
530
|
+
const ytMatch = url.match(/youtube\.com\/watch\?v=([\w-]+)/);
|
|
531
|
+
if (ytMatch) {
|
|
532
|
+
return `https://www.youtube.com/embed/${ytMatch[1]}`;
|
|
533
|
+
}
|
|
534
|
+
const ytShortMatch = url.match(/youtu\.be\/([\w-]+)/);
|
|
535
|
+
if (ytShortMatch) {
|
|
536
|
+
return `https://www.youtube.com/embed/${ytShortMatch[1]}`;
|
|
537
|
+
}
|
|
538
|
+
const rtMatch = url.match(/rutube\.ru\/video\/([\w]+)/);
|
|
539
|
+
if (rtMatch) {
|
|
540
|
+
return `https://rutube.ru/play/embed/${rtMatch[1]}`;
|
|
541
|
+
}
|
|
542
|
+
return null;
|
|
543
|
+
}
|
|
544
|
+
function fromVideoEmbedUrl(embedUrl) {
|
|
545
|
+
const ytMatch = embedUrl.match(/youtube\.com\/embed\/([\w-]+)/);
|
|
546
|
+
if (ytMatch) {
|
|
547
|
+
return `https://www.youtube.com/watch?v=${ytMatch[1]}`;
|
|
548
|
+
}
|
|
549
|
+
const rtMatch = embedUrl.match(/rutube\.ru\/play\/embed\/([\w]+)/);
|
|
550
|
+
if (rtMatch) {
|
|
551
|
+
return `https://rutube.ru/video/${rtMatch[1]}/`;
|
|
552
|
+
}
|
|
553
|
+
return embedUrl;
|
|
554
|
+
}
|
|
555
|
+
function splitUrl(url) {
|
|
556
|
+
let rest = url;
|
|
557
|
+
let hash = "";
|
|
558
|
+
const hashIdx = rest.indexOf("#");
|
|
559
|
+
if (hashIdx >= 0) {
|
|
560
|
+
hash = rest.slice(hashIdx);
|
|
561
|
+
rest = rest.slice(0, hashIdx);
|
|
562
|
+
}
|
|
563
|
+
let query = "";
|
|
564
|
+
const qIdx = rest.indexOf("?");
|
|
565
|
+
if (qIdx >= 0) {
|
|
566
|
+
query = rest.slice(qIdx);
|
|
567
|
+
rest = rest.slice(0, qIdx);
|
|
568
|
+
}
|
|
569
|
+
return { base: rest, query, hash };
|
|
570
|
+
}
|
|
571
|
+
function hasQueryParam(url, key) {
|
|
572
|
+
const { query } = splitUrl(url);
|
|
573
|
+
return new RegExp(`[?&]${key}=`, "i").test(query);
|
|
574
|
+
}
|
|
575
|
+
function appendQueryParam(url, key, value) {
|
|
576
|
+
const { base, query, hash } = splitUrl(url);
|
|
577
|
+
const next = query ? `${query}&${key}=${value}` : `?${key}=${value}`;
|
|
578
|
+
return `${base}${next}${hash}`;
|
|
579
|
+
}
|
|
580
|
+
var CODE_WIDGET_IFRAME_ALLOW = "accelerometer; camera; encrypted-media; geolocation; gyroscope; microphone; midi; payment; usb; xr-spatial-tracking; cross-origin-isolated";
|
|
581
|
+
function renderEmbedIframeIsolationAttrs(context, kind) {
|
|
582
|
+
const opts = context?.embed;
|
|
583
|
+
const parts = [];
|
|
584
|
+
if (kind === "codeWidget" && opts?.crossOriginIsolated) {
|
|
585
|
+
parts.push(`allow="${CODE_WIDGET_IFRAME_ALLOW}"`);
|
|
586
|
+
}
|
|
587
|
+
if (opts?.credentialless) {
|
|
588
|
+
parts.push("credentialless");
|
|
589
|
+
}
|
|
590
|
+
return parts.length ? ` ${parts.join(" ")}` : "";
|
|
591
|
+
}
|
|
592
|
+
function toCodeWidgetEmbedUrl(url) {
|
|
593
|
+
const u = typeof url === "string" ? url.trim() : "";
|
|
594
|
+
if (!u) return "";
|
|
595
|
+
if (/(?:\/\/|^)(?:[\w-]+\.)*stackblitz\.com\//i.test(u)) {
|
|
596
|
+
return hasQueryParam(u, "embed") ? u : appendQueryParam(u, "embed", "1");
|
|
597
|
+
}
|
|
598
|
+
if (/(?:\/\/|^)(?:[\w-]+\.)*codesandbox\.io\//i.test(u)) {
|
|
599
|
+
if (/codesandbox\.io\/embed\//i.test(u)) return u;
|
|
600
|
+
return u.replace(/codesandbox\.io\/s\//i, "codesandbox.io/embed/");
|
|
601
|
+
}
|
|
602
|
+
if (/(?:\/\/|^)(?:[\w-]+\.)*replit\.com\//i.test(u)) {
|
|
603
|
+
return hasQueryParam(u, "embed") ? u : appendQueryParam(u, "embed", "true");
|
|
604
|
+
}
|
|
605
|
+
if (/(?:\/\/|^)(?:[\w-]+\.)*codepen\.io\//i.test(u)) {
|
|
606
|
+
if (/codepen\.io\/[^/]+\/embed\//i.test(u)) return u;
|
|
607
|
+
return u.replace(/(codepen\.io\/[^/]+)\/pen\//i, "$1/embed/");
|
|
608
|
+
}
|
|
609
|
+
if (/(?:\/\/|^)(?:[\w-]+\.)*jsfiddle\.net\//i.test(u)) {
|
|
610
|
+
const { base, query, hash } = splitUrl(u);
|
|
611
|
+
if (/\/embedded(?:\/|$)/i.test(base)) return u;
|
|
612
|
+
const trimmed = base.replace(/\/+$/, "");
|
|
613
|
+
return `${trimmed}/embedded/${query}${hash}`;
|
|
614
|
+
}
|
|
615
|
+
if (/(?:\/\/|^)(?:[\w-]+\.)*trinket\.io\//i.test(u)) {
|
|
616
|
+
if (/trinket\.io\/embed\//i.test(u)) return u;
|
|
617
|
+
return u.replace(/trinket\.io\//i, "trinket.io/embed/");
|
|
618
|
+
}
|
|
619
|
+
if (/(?:\/\/|^)(?:[\w-]+\.)*onecompiler\.com\//i.test(u)) {
|
|
620
|
+
if (/onecompiler\.com\/embed\//i.test(u)) return u;
|
|
621
|
+
return u.replace(/onecompiler\.com\//i, "onecompiler.com/embed/");
|
|
622
|
+
}
|
|
623
|
+
return u;
|
|
624
|
+
}
|
|
625
|
+
|
|
364
626
|
// src/schema/blocks/table.ts
|
|
627
|
+
var EXT_TABLE_HOST_ATTR = "data-scrider-ext-table";
|
|
628
|
+
var EXT_TABLE_HOST_CLASS = "scrider-ext-table-host";
|
|
629
|
+
var VALID_TABLE_BLOCK_FLOATS = ["left", "center", "right"];
|
|
365
630
|
var VALID_CELL_HORIZONTAL_ALIGNS = [
|
|
366
631
|
"left",
|
|
367
632
|
"center",
|
|
@@ -466,6 +731,46 @@ function promoteAlignFromCellOps(cell) {
|
|
|
466
731
|
}
|
|
467
732
|
return cell;
|
|
468
733
|
}
|
|
734
|
+
function parseVerticalAlign(value) {
|
|
735
|
+
const normalized = value?.trim().toLowerCase();
|
|
736
|
+
if (!normalized) return null;
|
|
737
|
+
if (normalized === "top" || normalized === "baseline" || normalized === "text-top") return "top";
|
|
738
|
+
if (normalized === "middle" || normalized === "center") return "middle";
|
|
739
|
+
if (normalized === "bottom" || normalized === "text-bottom") return "bottom";
|
|
740
|
+
return null;
|
|
741
|
+
}
|
|
742
|
+
function extractInlineVerticalAlign(element) {
|
|
743
|
+
const verticalAlign = element.style?.getPropertyValue?.("vertical-align");
|
|
744
|
+
const fromStyle = parseVerticalAlign(verticalAlign);
|
|
745
|
+
if (fromStyle) return fromStyle;
|
|
746
|
+
const style = element.getAttribute("style") || "";
|
|
747
|
+
const match = style.match(/vertical-align:\s*(top|middle|bottom|center|baseline|text-top|text-bottom)/i);
|
|
748
|
+
if (match?.[1]) {
|
|
749
|
+
const parsed = parseVerticalAlign(match[1]);
|
|
750
|
+
if (parsed) return parsed;
|
|
751
|
+
}
|
|
752
|
+
const msoMatch = style.match(/mso-vertical-align:\s*(top|middle|bottom|center)/i);
|
|
753
|
+
if (msoMatch?.[1]) {
|
|
754
|
+
const parsed = parseVerticalAlign(msoMatch[1]);
|
|
755
|
+
if (parsed) return parsed;
|
|
756
|
+
}
|
|
757
|
+
return null;
|
|
758
|
+
}
|
|
759
|
+
function extractCellVAlign(cell) {
|
|
760
|
+
const direct = extractInlineVerticalAlign(cell);
|
|
761
|
+
if (direct) return direct;
|
|
762
|
+
const children = cell.childNodes;
|
|
763
|
+
for (let i = 0; i < children.length; i++) {
|
|
764
|
+
const child = children[i];
|
|
765
|
+
if (!child || !isElement(child)) continue;
|
|
766
|
+
const tag = child.tagName.toLowerCase();
|
|
767
|
+
if (tag === "p" || tag === "div") {
|
|
768
|
+
const vAlign = extractInlineVerticalAlign(child);
|
|
769
|
+
if (vAlign) return vAlign;
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
return null;
|
|
773
|
+
}
|
|
469
774
|
function renderExtendedRow(data, row, cols, defaultCellTag, context, pretty) {
|
|
470
775
|
const nl = pretty ? "\n" : "";
|
|
471
776
|
const ind = (level) => pretty ? " ".repeat(level) : "";
|
|
@@ -487,14 +792,17 @@ function renderExtendedRow(data, row, cols, defaultCellTag, context, pretty) {
|
|
|
487
792
|
}
|
|
488
793
|
const colDefault = data.colAligns?.[c] ?? "left";
|
|
489
794
|
const effectiveAlign = resolveCellHorizontalAlign(cell, c, data.colAligns);
|
|
490
|
-
|
|
795
|
+
const styleParts = [];
|
|
491
796
|
if (effectiveAlign !== "left") {
|
|
492
|
-
|
|
797
|
+
styleParts.push(`text-align: ${effectiveAlign}`);
|
|
493
798
|
} else if (cell.align === "left" && colDefault !== "left") {
|
|
494
|
-
|
|
799
|
+
styleParts.push("text-align: left");
|
|
800
|
+
}
|
|
801
|
+
if (cell.vAlign !== void 0 && cell.vAlign !== "top") {
|
|
802
|
+
styleParts.push(`vertical-align: ${cell.vAlign}`);
|
|
495
803
|
}
|
|
496
|
-
if (
|
|
497
|
-
attrs.push(`style="${
|
|
804
|
+
if (styleParts.length > 0) {
|
|
805
|
+
attrs.push(`style="${styleParts.join("; ")}"`);
|
|
498
806
|
}
|
|
499
807
|
const attrStr = attrs.length > 0 ? " " + attrs.join(" ") : "";
|
|
500
808
|
let content = "";
|
|
@@ -506,6 +814,46 @@ function renderExtendedRow(data, row, cols, defaultCellTag, context, pretty) {
|
|
|
506
814
|
html += `${ind(2)}</tr>${nl}`;
|
|
507
815
|
return html;
|
|
508
816
|
}
|
|
817
|
+
function parseTableBlockFloat(value) {
|
|
818
|
+
if (value === "left" || value === "center" || value === "right") return value;
|
|
819
|
+
return void 0;
|
|
820
|
+
}
|
|
821
|
+
function extractHostBlockWidthPx(host) {
|
|
822
|
+
const style = host.getAttribute("style") || "";
|
|
823
|
+
const widthMatch = style.match(/(?:^|;\s*)width:\s*([\d.]+)px/i);
|
|
824
|
+
if (!widthMatch?.[1]) return void 0;
|
|
825
|
+
const n = parseFloat(widthMatch[1]);
|
|
826
|
+
if (!Number.isFinite(n) || n <= 0) return void 0;
|
|
827
|
+
return Math.round(n);
|
|
828
|
+
}
|
|
829
|
+
function enrichTableBlockFromHost(data, host) {
|
|
830
|
+
const float = parseTableBlockFloat(host.getAttribute("data-float"));
|
|
831
|
+
const width = extractHostBlockWidthPx(host);
|
|
832
|
+
if (float == null && width == null) return data;
|
|
833
|
+
const next = { ...data };
|
|
834
|
+
if (float != null) next.float = float;
|
|
835
|
+
if (width != null) next.width = width;
|
|
836
|
+
return next;
|
|
837
|
+
}
|
|
838
|
+
function tableNeedsHostWrapper(data) {
|
|
839
|
+
return data.float != null && VALID_TABLE_BLOCK_FLOATS.includes(data.float) || data.width != null && data.width > 0;
|
|
840
|
+
}
|
|
841
|
+
function renderTableHostWrapperOpen(data, pretty) {
|
|
842
|
+
const nl = pretty ? "\n" : "";
|
|
843
|
+
const ind = pretty ? " " : "";
|
|
844
|
+
const attrs = [`class="${EXT_TABLE_HOST_CLASS}"`, EXT_TABLE_HOST_ATTR];
|
|
845
|
+
if (data.float) attrs.push(`data-float="${escapeHtml(data.float)}"`);
|
|
846
|
+
const styleParts = [];
|
|
847
|
+
if (data.width != null && data.width > 0) {
|
|
848
|
+
styleParts.push(`width: ${Math.round(data.width)}px`);
|
|
849
|
+
styleParts.push("max-width: 100%");
|
|
850
|
+
}
|
|
851
|
+
if (styleParts.length > 0) attrs.push(`style="${styleParts.join("; ")}"`);
|
|
852
|
+
return `<div ${attrs.join(" ")}>${nl}${ind}`;
|
|
853
|
+
}
|
|
854
|
+
function renderTableHostWrapperClose(pretty) {
|
|
855
|
+
return pretty ? "</div>\n" : "</div>";
|
|
856
|
+
}
|
|
509
857
|
function isGfmCompatible(data) {
|
|
510
858
|
if (data.colWidths && data.colWidths.some((w) => w > 0)) {
|
|
511
859
|
return false;
|
|
@@ -513,8 +861,17 @@ function isGfmCompatible(data) {
|
|
|
513
861
|
if (data.rowHeights && data.rowHeights.some((h) => h > 0)) {
|
|
514
862
|
return false;
|
|
515
863
|
}
|
|
864
|
+
if (data.width != null && data.width > 0) {
|
|
865
|
+
return false;
|
|
866
|
+
}
|
|
867
|
+
if (data.float != null) {
|
|
868
|
+
return false;
|
|
869
|
+
}
|
|
870
|
+
for (const cell of Object.values(data.cells)) {
|
|
871
|
+
if (cell !== null && cell.align !== void 0) return false;
|
|
872
|
+
}
|
|
516
873
|
for (const cell of Object.values(data.cells)) {
|
|
517
|
-
if (cell !== null && cell.
|
|
874
|
+
if (cell !== null && cell.vAlign !== void 0) return false;
|
|
518
875
|
}
|
|
519
876
|
for (const cell of Object.values(data.cells)) {
|
|
520
877
|
if (cell === null) return false;
|
|
@@ -729,6 +1086,10 @@ function parseTableElement(table, context) {
|
|
|
729
1086
|
delete withoutAlign.align;
|
|
730
1087
|
cellData = withoutAlign;
|
|
731
1088
|
}
|
|
1089
|
+
const htmlVAlign = extractCellVAlign(cell);
|
|
1090
|
+
if (htmlVAlign && htmlVAlign !== "top") {
|
|
1091
|
+
cellData = { ...cellData, vAlign: htmlVAlign };
|
|
1092
|
+
}
|
|
732
1093
|
const cellKey = `${rowIdx}:${colIdx}`;
|
|
733
1094
|
cells[cellKey] = cellData;
|
|
734
1095
|
rawAligns[cellKey] = effectiveAlign;
|
|
@@ -858,6 +1219,12 @@ var tableBlockHandler = {
|
|
|
858
1219
|
if (typeof h !== "number" || h < 0) return false;
|
|
859
1220
|
}
|
|
860
1221
|
}
|
|
1222
|
+
if (data.width !== void 0) {
|
|
1223
|
+
if (typeof data.width !== "number" || data.width <= 0) return false;
|
|
1224
|
+
}
|
|
1225
|
+
if (data.float !== void 0 && !VALID_TABLE_BLOCK_FLOATS.includes(data.float)) {
|
|
1226
|
+
return false;
|
|
1227
|
+
}
|
|
861
1228
|
if (data.colAligns !== void 0) {
|
|
862
1229
|
if (!Array.isArray(data.colAligns) || data.colAligns.length !== cols) {
|
|
863
1230
|
return false;
|
|
@@ -909,6 +1276,9 @@ var tableBlockHandler = {
|
|
|
909
1276
|
html += `${ind(1)}</tbody>${nl}`;
|
|
910
1277
|
}
|
|
911
1278
|
html += `</table>`;
|
|
1279
|
+
if (tableNeedsHostWrapper(data)) {
|
|
1280
|
+
return renderTableHostWrapperOpen(data, pretty) + html + renderTableHostWrapperClose(pretty);
|
|
1281
|
+
}
|
|
912
1282
|
return html;
|
|
913
1283
|
},
|
|
914
1284
|
fromHtml(element, context) {
|
|
@@ -1714,544 +2084,282 @@ var boldFormat = {
|
|
|
1714
2084
|
return value === true;
|
|
1715
2085
|
}
|
|
1716
2086
|
};
|
|
1717
|
-
|
|
1718
|
-
// src/schema/formats/inline/code.ts
|
|
1719
|
-
var codeFormat = {
|
|
1720
|
-
name: "code",
|
|
1721
|
-
scope: "inline",
|
|
1722
|
-
validate(value) {
|
|
1723
|
-
return value === true;
|
|
1724
|
-
}
|
|
1725
|
-
};
|
|
1726
|
-
|
|
1727
|
-
// src/schema/formats/inline/color.ts
|
|
1728
|
-
var colorFormat = {
|
|
1729
|
-
name: "color",
|
|
1730
|
-
scope: "inline",
|
|
1731
|
-
normalize(value) {
|
|
1732
|
-
return toHexColor(value);
|
|
1733
|
-
},
|
|
1734
|
-
validate(value) {
|
|
1735
|
-
return typeof value === "string" && isValidColor(value);
|
|
1736
|
-
}
|
|
1737
|
-
};
|
|
1738
|
-
|
|
1739
|
-
// src/schema/formats/inline/font.ts
|
|
1740
|
-
var fontFormat = {
|
|
1741
|
-
name: "font",
|
|
1742
|
-
scope: "inline",
|
|
1743
|
-
validate(value) {
|
|
1744
|
-
return typeof value === "string" && value.length > 0;
|
|
1745
|
-
}
|
|
1746
|
-
};
|
|
1747
|
-
|
|
1748
|
-
// src/schema/formats/inline/italic.ts
|
|
1749
|
-
var italicFormat = {
|
|
1750
|
-
name: "italic",
|
|
1751
|
-
scope: "inline",
|
|
1752
|
-
validate(value) {
|
|
1753
|
-
return value === true;
|
|
1754
|
-
}
|
|
1755
|
-
};
|
|
1756
|
-
|
|
1757
|
-
// src/schema/formats/inline/kbd.ts
|
|
1758
|
-
var kbdFormat = {
|
|
1759
|
-
name: "kbd",
|
|
1760
|
-
scope: "inline",
|
|
1761
|
-
validate(value) {
|
|
1762
|
-
return value === true;
|
|
1763
|
-
}
|
|
1764
|
-
};
|
|
1765
|
-
|
|
1766
|
-
// src/schema/formats/inline/link.ts
|
|
1767
|
-
var linkFormat = {
|
|
1768
|
-
name: "link",
|
|
1769
|
-
scope: "inline",
|
|
1770
|
-
normalize(value) {
|
|
1771
|
-
return value.trim();
|
|
1772
|
-
},
|
|
1773
|
-
validate(value) {
|
|
1774
|
-
if (typeof value !== "string" || value.length === 0) {
|
|
1775
|
-
return false;
|
|
1776
|
-
}
|
|
1777
|
-
const trimmed = value.trim();
|
|
1778
|
-
if (trimmed.startsWith("/") || trimmed.startsWith("./") || trimmed.startsWith("../")) {
|
|
1779
|
-
return true;
|
|
1780
|
-
}
|
|
1781
|
-
if (trimmed.startsWith("//")) {
|
|
1782
|
-
return true;
|
|
1783
|
-
}
|
|
1784
|
-
if (trimmed.startsWith("mailto:") || trimmed.startsWith("tel:")) {
|
|
1785
|
-
return true;
|
|
1786
|
-
}
|
|
1787
|
-
try {
|
|
1788
|
-
const url = new URL(trimmed);
|
|
1789
|
-
return url.protocol === "http:" || url.protocol === "https:";
|
|
1790
|
-
} catch {
|
|
1791
|
-
return false;
|
|
1792
|
-
}
|
|
1793
|
-
}
|
|
1794
|
-
};
|
|
1795
|
-
|
|
1796
|
-
// src/schema/formats/inline/mark.ts
|
|
1797
|
-
var markFormat = {
|
|
1798
|
-
name: "mark",
|
|
1799
|
-
scope: "inline",
|
|
1800
|
-
validate(value) {
|
|
1801
|
-
return value === true;
|
|
1802
|
-
}
|
|
1803
|
-
};
|
|
1804
|
-
|
|
1805
|
-
// src/schema/formats/inline/size.ts
|
|
1806
|
-
var sizeFormat = {
|
|
1807
|
-
name: "size",
|
|
1808
|
-
scope: "inline",
|
|
1809
|
-
validate(value) {
|
|
1810
|
-
return typeof value === "string" && value.length > 0;
|
|
1811
|
-
}
|
|
1812
|
-
};
|
|
1813
|
-
|
|
1814
|
-
// src/schema/formats/inline/strike.ts
|
|
1815
|
-
var strikeFormat = {
|
|
1816
|
-
name: "strike",
|
|
1817
|
-
scope: "inline",
|
|
1818
|
-
validate(value) {
|
|
1819
|
-
return value === true;
|
|
1820
|
-
}
|
|
1821
|
-
};
|
|
1822
|
-
|
|
1823
|
-
// src/schema/formats/inline/subscript.ts
|
|
1824
|
-
var subscriptFormat = {
|
|
1825
|
-
name: "subscript",
|
|
1826
|
-
scope: "inline",
|
|
1827
|
-
validate(value) {
|
|
1828
|
-
return value === true;
|
|
1829
|
-
}
|
|
1830
|
-
};
|
|
1831
|
-
|
|
1832
|
-
// src/schema/formats/inline/superscript.ts
|
|
1833
|
-
var superscriptFormat = {
|
|
1834
|
-
name: "superscript",
|
|
1835
|
-
scope: "inline",
|
|
1836
|
-
validate(value) {
|
|
1837
|
-
return value === true;
|
|
1838
|
-
}
|
|
1839
|
-
};
|
|
1840
|
-
|
|
1841
|
-
// src/schema/formats/inline/underline.ts
|
|
1842
|
-
var underlineFormat = {
|
|
1843
|
-
name: "underline",
|
|
1844
|
-
scope: "inline",
|
|
1845
|
-
validate(value) {
|
|
1846
|
-
return value === true;
|
|
1847
|
-
}
|
|
1848
|
-
};
|
|
1849
|
-
|
|
1850
|
-
// src/schema/formats/block/align.ts
|
|
1851
|
-
var VALID_ALIGN_TYPES = ["left", "center", "right", "justify"];
|
|
1852
|
-
var alignFormat = {
|
|
1853
|
-
name: "align",
|
|
1854
|
-
scope: "block",
|
|
1855
|
-
normalize(value) {
|
|
1856
|
-
return value.toLowerCase();
|
|
1857
|
-
},
|
|
1858
|
-
validate(value) {
|
|
1859
|
-
return VALID_ALIGN_TYPES.includes(value);
|
|
1860
|
-
}
|
|
1861
|
-
};
|
|
1862
|
-
|
|
1863
|
-
// src/schema/formats/block/blockquote.ts
|
|
1864
|
-
var blockquoteFormat = {
|
|
1865
|
-
name: "blockquote",
|
|
1866
|
-
scope: "block",
|
|
1867
|
-
validate(value) {
|
|
1868
|
-
return value === true;
|
|
1869
|
-
}
|
|
1870
|
-
};
|
|
1871
|
-
|
|
1872
|
-
// src/schema/formats/block/code-block.ts
|
|
1873
|
-
var codeBlockFormat = {
|
|
1874
|
-
name: "code-block",
|
|
1875
|
-
scope: "block",
|
|
1876
|
-
normalize(value) {
|
|
1877
|
-
if (typeof value === "string") {
|
|
1878
|
-
return value.toLowerCase().trim();
|
|
1879
|
-
}
|
|
1880
|
-
return value;
|
|
1881
|
-
},
|
|
1882
|
-
validate(value) {
|
|
1883
|
-
if (value === true) {
|
|
1884
|
-
return true;
|
|
1885
|
-
}
|
|
1886
|
-
if (typeof value === "string" && value.length > 0) {
|
|
1887
|
-
return true;
|
|
1888
|
-
}
|
|
1889
|
-
return false;
|
|
1890
|
-
}
|
|
1891
|
-
};
|
|
1892
|
-
|
|
1893
|
-
// src/schema/formats/block/header.ts
|
|
1894
|
-
var headerFormat = {
|
|
1895
|
-
name: "header",
|
|
1896
|
-
scope: "block",
|
|
1897
|
-
normalize(value) {
|
|
1898
|
-
return Math.max(1, Math.min(6, Math.floor(value)));
|
|
1899
|
-
},
|
|
1900
|
-
validate(value) {
|
|
1901
|
-
return Number.isInteger(value) && value >= 1 && value <= 6;
|
|
1902
|
-
}
|
|
1903
|
-
};
|
|
1904
|
-
|
|
1905
|
-
// src/schema/formats/block/header-id.ts
|
|
1906
|
-
var headerIdFormat = {
|
|
1907
|
-
name: "header-id",
|
|
1908
|
-
scope: "block",
|
|
1909
|
-
normalize(value) {
|
|
1910
|
-
return String(value).trim().toLowerCase();
|
|
1911
|
-
},
|
|
1912
|
-
validate(value) {
|
|
1913
|
-
if (typeof value !== "string") return false;
|
|
1914
|
-
const trimmed = value.trim();
|
|
1915
|
-
return trimmed.length > 0 && !/\s/.test(trimmed);
|
|
1916
|
-
}
|
|
1917
|
-
};
|
|
1918
|
-
|
|
1919
|
-
// src/schema/formats/block/indent.ts
|
|
1920
|
-
var MAX_INDENT = 8;
|
|
1921
|
-
var indentFormat = {
|
|
1922
|
-
name: "indent",
|
|
1923
|
-
scope: "block",
|
|
1924
|
-
normalize(value) {
|
|
1925
|
-
return Math.max(0, Math.min(MAX_INDENT, Math.floor(value)));
|
|
1926
|
-
},
|
|
1927
|
-
validate(value) {
|
|
1928
|
-
return Number.isInteger(value) && value >= 0 && value <= MAX_INDENT;
|
|
1929
|
-
}
|
|
1930
|
-
};
|
|
1931
|
-
|
|
1932
|
-
// src/schema/formats/block/list.ts
|
|
1933
|
-
var VALID_LIST_TYPES = ["ordered", "bullet", "checked", "unchecked"];
|
|
1934
|
-
var listFormat = {
|
|
1935
|
-
name: "list",
|
|
1936
|
-
scope: "block",
|
|
1937
|
-
normalize(value) {
|
|
1938
|
-
return value.toLowerCase();
|
|
1939
|
-
},
|
|
1940
|
-
validate(value) {
|
|
1941
|
-
return VALID_LIST_TYPES.includes(value);
|
|
1942
|
-
}
|
|
1943
|
-
};
|
|
1944
|
-
|
|
1945
|
-
// src/schema/formats/block/table-row.ts
|
|
1946
|
-
var tableRowFormat = {
|
|
1947
|
-
name: "table-row",
|
|
1948
|
-
scope: "block",
|
|
1949
|
-
validate(value) {
|
|
1950
|
-
return typeof value === "number" && Number.isInteger(value) && value >= 0;
|
|
1951
|
-
}
|
|
1952
|
-
};
|
|
1953
|
-
|
|
1954
|
-
// src/schema/formats/block/table-col.ts
|
|
1955
|
-
var tableColFormat = {
|
|
1956
|
-
name: "table-col",
|
|
1957
|
-
scope: "block",
|
|
1958
|
-
validate(value) {
|
|
1959
|
-
return typeof value === "number" && Number.isInteger(value) && value >= 0;
|
|
1960
|
-
}
|
|
1961
|
-
};
|
|
1962
|
-
|
|
1963
|
-
// src/schema/formats/block/table-header.ts
|
|
1964
|
-
var tableHeaderFormat = {
|
|
1965
|
-
name: "table-header",
|
|
1966
|
-
scope: "block",
|
|
2087
|
+
|
|
2088
|
+
// src/schema/formats/inline/code.ts
|
|
2089
|
+
var codeFormat = {
|
|
2090
|
+
name: "code",
|
|
2091
|
+
scope: "inline",
|
|
1967
2092
|
validate(value) {
|
|
1968
2093
|
return value === true;
|
|
1969
2094
|
}
|
|
1970
2095
|
};
|
|
1971
2096
|
|
|
1972
|
-
// src/schema/formats/
|
|
1973
|
-
var
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
scope: "block",
|
|
2097
|
+
// src/schema/formats/inline/color.ts
|
|
2098
|
+
var colorFormat = {
|
|
2099
|
+
name: "color",
|
|
2100
|
+
scope: "inline",
|
|
1977
2101
|
normalize(value) {
|
|
1978
|
-
return value
|
|
2102
|
+
return toHexColor(value);
|
|
1979
2103
|
},
|
|
1980
2104
|
validate(value) {
|
|
1981
|
-
return
|
|
2105
|
+
return typeof value === "string" && isValidColor(value);
|
|
1982
2106
|
}
|
|
1983
2107
|
};
|
|
1984
2108
|
|
|
1985
|
-
// src/schema/formats/
|
|
1986
|
-
var
|
|
1987
|
-
name: "
|
|
1988
|
-
scope: "
|
|
2109
|
+
// src/schema/formats/inline/font.ts
|
|
2110
|
+
var fontFormat = {
|
|
2111
|
+
name: "font",
|
|
2112
|
+
scope: "inline",
|
|
1989
2113
|
validate(value) {
|
|
1990
|
-
return typeof value === "
|
|
2114
|
+
return typeof value === "string" && value.length > 0;
|
|
1991
2115
|
}
|
|
1992
2116
|
};
|
|
1993
2117
|
|
|
1994
|
-
// src/
|
|
1995
|
-
var
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
subscript: "sub",
|
|
2002
|
-
superscript: "sup",
|
|
2003
|
-
code: "code",
|
|
2004
|
-
mark: "mark",
|
|
2005
|
-
kbd: "kbd"
|
|
2006
|
-
};
|
|
2007
|
-
var INLINE_FORMAT_ORDER = [
|
|
2008
|
-
"link",
|
|
2009
|
-
"bold",
|
|
2010
|
-
"italic",
|
|
2011
|
-
"underline",
|
|
2012
|
-
"strike",
|
|
2013
|
-
"subscript",
|
|
2014
|
-
"superscript",
|
|
2015
|
-
"code",
|
|
2016
|
-
"mark",
|
|
2017
|
-
"kbd"
|
|
2018
|
-
];
|
|
2019
|
-
var INLINE_STYLE_FORMATS = {
|
|
2020
|
-
color: "color",
|
|
2021
|
-
background: "background-color",
|
|
2022
|
-
font: "font-family",
|
|
2023
|
-
size: "font-size"
|
|
2024
|
-
};
|
|
2025
|
-
var BLOCK_FORMAT_TAGS = {
|
|
2026
|
-
header: (value) => `h${String(value)}`,
|
|
2027
|
-
blockquote: "blockquote",
|
|
2028
|
-
"code-block": "pre",
|
|
2029
|
-
list: "li"
|
|
2030
|
-
// Wrapped in ul/ol based on list type
|
|
2118
|
+
// src/schema/formats/inline/italic.ts
|
|
2119
|
+
var italicFormat = {
|
|
2120
|
+
name: "italic",
|
|
2121
|
+
scope: "inline",
|
|
2122
|
+
validate(value) {
|
|
2123
|
+
return value === true;
|
|
2124
|
+
}
|
|
2031
2125
|
};
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2126
|
+
|
|
2127
|
+
// src/schema/formats/inline/kbd.ts
|
|
2128
|
+
var kbdFormat = {
|
|
2129
|
+
name: "kbd",
|
|
2130
|
+
scope: "inline",
|
|
2131
|
+
validate(value) {
|
|
2132
|
+
return value === true;
|
|
2133
|
+
}
|
|
2037
2134
|
};
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
const alt = altVal != null && (typeof altVal === "string" || typeof altVal === "number") ? ` alt="${escapeHtml(String(altVal))}"` : "";
|
|
2046
|
-
const width = widthVal != null && (typeof widthVal === "string" || typeof widthVal === "number") ? ` width="${String(widthVal)}"` : "";
|
|
2047
|
-
const height = heightVal != null && (typeof heightVal === "string" || typeof heightVal === "number") ? ` height="${String(heightVal)}"` : "";
|
|
2048
|
-
const float = floatVal != null && typeof floatVal === "string" && floatVal !== "none" ? ` data-float="${escapeHtml(floatVal)}"` : "";
|
|
2049
|
-
return `<img src="${escapeHtml(src)}"${alt}${width}${height}${float}>`;
|
|
2135
|
+
|
|
2136
|
+
// src/schema/formats/inline/link.ts
|
|
2137
|
+
var linkFormat = {
|
|
2138
|
+
name: "link",
|
|
2139
|
+
scope: "inline",
|
|
2140
|
+
normalize(value) {
|
|
2141
|
+
return value.trim();
|
|
2050
2142
|
},
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
const widthVal = attrs?.width;
|
|
2055
|
-
const heightVal = attrs?.height;
|
|
2056
|
-
const float = floatVal != null && typeof floatVal === "string" && floatVal !== "none" ? ` data-float="${escapeHtml(floatVal)}"` : "";
|
|
2057
|
-
const styles = [];
|
|
2058
|
-
if (widthVal != null && (typeof widthVal === "string" || typeof widthVal === "number")) {
|
|
2059
|
-
const w = String(widthVal);
|
|
2060
|
-
if (w && w !== "auto") styles.push(`width: ${/^\d+$/.test(w) ? w + "px" : w}`);
|
|
2143
|
+
validate(value) {
|
|
2144
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
2145
|
+
return false;
|
|
2061
2146
|
}
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2147
|
+
const trimmed = value.trim();
|
|
2148
|
+
if (trimmed.startsWith("/") || trimmed.startsWith("./") || trimmed.startsWith("../")) {
|
|
2149
|
+
return true;
|
|
2065
2150
|
}
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
if (embedSrc) {
|
|
2069
|
-
return `<iframe src="${escapeHtml(embedSrc)}" frameborder="0" allowfullscreen${renderEmbedIframeIsolationAttrs(context, "video")}${float}${style}></iframe>`;
|
|
2151
|
+
if (trimmed.startsWith("//")) {
|
|
2152
|
+
return true;
|
|
2070
2153
|
}
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
codeWidget: (value, attrs, context) => {
|
|
2074
|
-
const src = typeof value === "string" ? value : "";
|
|
2075
|
-
const floatVal = attrs?.float;
|
|
2076
|
-
const widthVal = attrs?.width;
|
|
2077
|
-
const heightVal = attrs?.height;
|
|
2078
|
-
const float = floatVal != null && typeof floatVal === "string" && floatVal !== "none" ? ` data-float="${escapeHtml(floatVal)}"` : "";
|
|
2079
|
-
const styles = [];
|
|
2080
|
-
if (widthVal != null && (typeof widthVal === "string" || typeof widthVal === "number")) {
|
|
2081
|
-
const w = String(widthVal);
|
|
2082
|
-
if (w && w !== "auto") styles.push(`width: ${/^\d+$/.test(w) ? w + "px" : w}`);
|
|
2154
|
+
if (trimmed.startsWith("mailto:") || trimmed.startsWith("tel:")) {
|
|
2155
|
+
return true;
|
|
2083
2156
|
}
|
|
2084
|
-
|
|
2085
|
-
const
|
|
2086
|
-
|
|
2157
|
+
try {
|
|
2158
|
+
const url = new URL(trimmed);
|
|
2159
|
+
return url.protocol === "http:" || url.protocol === "https:";
|
|
2160
|
+
} catch {
|
|
2161
|
+
return false;
|
|
2087
2162
|
}
|
|
2088
|
-
|
|
2089
|
-
const embedSrc = toCodeWidgetEmbedUrl(src);
|
|
2090
|
-
return `<iframe data-code-widget src="${escapeHtml(embedSrc)}" frameborder="0" allowfullscreen${renderEmbedIframeIsolationAttrs(context, "codeWidget")}${float}${style}></iframe>`;
|
|
2091
|
-
},
|
|
2092
|
-
formula: (value) => {
|
|
2093
|
-
const latex = typeof value === "string" ? value : "";
|
|
2094
|
-
return `<span class="formula" data-formula="${escapeHtml(latex)}">${escapeHtml(latex)}</span>`;
|
|
2095
|
-
},
|
|
2096
|
-
diagram: (value) => {
|
|
2097
|
-
const source = typeof value === "string" ? value : "";
|
|
2098
|
-
return `<span class="diagram" data-diagram="${escapeHtml(source)}">${escapeHtml(source)}</span>`;
|
|
2099
|
-
},
|
|
2100
|
-
drawio: (value, attrs) => {
|
|
2101
|
-
const src = typeof value === "string" ? value : "";
|
|
2102
|
-
const altVal = attrs?.alt;
|
|
2103
|
-
const alt = altVal != null && (typeof altVal === "string" || typeof altVal === "number") ? ` data-alt="${escapeHtml(String(altVal))}"` : "";
|
|
2104
|
-
return `<span class="drawio" data-drawio-src="${escapeHtml(src)}"${alt}></span>`;
|
|
2105
|
-
},
|
|
2106
|
-
"footnote-ref": (value) => {
|
|
2107
|
-
const id = typeof value === "string" ? value : String(value);
|
|
2108
|
-
return `<sup class="footnote-ref"><a href="#fn-${escapeHtml(id)}" id="fnref-${escapeHtml(id)}">[${escapeHtml(id)}]</a></sup>`;
|
|
2109
|
-
},
|
|
2110
|
-
divider: () => "<hr>",
|
|
2111
|
-
// Soft line break (Shift+Enter equivalent). Emitted with an explicit
|
|
2112
|
-
// `data-scrider-embed` marker so that html-to-delta can distinguish this
|
|
2113
|
-
// embed from the placeholder `<br>` that appears inside an empty
|
|
2114
|
-
// paragraph (`<p><br></p>`) without relying solely on positional
|
|
2115
|
-
// heuristics. See `soft-break.ts` for the format definition.
|
|
2116
|
-
softBreak: () => "<br data-scrider-embed>"
|
|
2117
|
-
};
|
|
2118
|
-
var TAG_TO_INLINE_FORMAT = {
|
|
2119
|
-
strong: { format: "bold", value: true },
|
|
2120
|
-
b: { format: "bold", value: true },
|
|
2121
|
-
em: { format: "italic", value: true },
|
|
2122
|
-
i: { format: "italic", value: true },
|
|
2123
|
-
u: { format: "underline", value: true },
|
|
2124
|
-
ins: { format: "underline", value: true },
|
|
2125
|
-
s: { format: "strike", value: true },
|
|
2126
|
-
strike: { format: "strike", value: true },
|
|
2127
|
-
del: { format: "strike", value: true },
|
|
2128
|
-
sub: { format: "subscript", value: true },
|
|
2129
|
-
sup: { format: "superscript", value: true },
|
|
2130
|
-
code: { format: "code", value: true },
|
|
2131
|
-
mark: { format: "mark", value: true },
|
|
2132
|
-
kbd: { format: "kbd", value: true }
|
|
2133
|
-
};
|
|
2134
|
-
var TAG_TO_BLOCK_FORMAT = {
|
|
2135
|
-
h1: { format: "header", value: 1 },
|
|
2136
|
-
h2: { format: "header", value: 2 },
|
|
2137
|
-
h3: { format: "header", value: 3 },
|
|
2138
|
-
h4: { format: "header", value: 4 },
|
|
2139
|
-
h5: { format: "header", value: 5 },
|
|
2140
|
-
h6: { format: "header", value: 6 },
|
|
2141
|
-
blockquote: { format: "blockquote", value: true },
|
|
2142
|
-
pre: { format: "code-block", value: true }
|
|
2163
|
+
}
|
|
2143
2164
|
};
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2165
|
+
|
|
2166
|
+
// src/schema/formats/inline/mark.ts
|
|
2167
|
+
var markFormat = {
|
|
2168
|
+
name: "mark",
|
|
2169
|
+
scope: "inline",
|
|
2170
|
+
validate(value) {
|
|
2171
|
+
return value === true;
|
|
2172
|
+
}
|
|
2149
2173
|
};
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
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")) {
|
|
2158
|
-
return url;
|
|
2174
|
+
|
|
2175
|
+
// src/schema/formats/inline/size.ts
|
|
2176
|
+
var sizeFormat = {
|
|
2177
|
+
name: "size",
|
|
2178
|
+
scope: "inline",
|
|
2179
|
+
validate(value) {
|
|
2180
|
+
return typeof value === "string" && value.length > 0;
|
|
2159
2181
|
}
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2182
|
+
};
|
|
2183
|
+
|
|
2184
|
+
// src/schema/formats/inline/strike.ts
|
|
2185
|
+
var strikeFormat = {
|
|
2186
|
+
name: "strike",
|
|
2187
|
+
scope: "inline",
|
|
2188
|
+
validate(value) {
|
|
2189
|
+
return value === true;
|
|
2163
2190
|
}
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2191
|
+
};
|
|
2192
|
+
|
|
2193
|
+
// src/schema/formats/inline/subscript.ts
|
|
2194
|
+
var subscriptFormat = {
|
|
2195
|
+
name: "subscript",
|
|
2196
|
+
scope: "inline",
|
|
2197
|
+
validate(value) {
|
|
2198
|
+
return value === true;
|
|
2167
2199
|
}
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2200
|
+
};
|
|
2201
|
+
|
|
2202
|
+
// src/schema/formats/inline/superscript.ts
|
|
2203
|
+
var superscriptFormat = {
|
|
2204
|
+
name: "superscript",
|
|
2205
|
+
scope: "inline",
|
|
2206
|
+
validate(value) {
|
|
2207
|
+
return value === true;
|
|
2171
2208
|
}
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2209
|
+
};
|
|
2210
|
+
|
|
2211
|
+
// src/schema/formats/inline/underline.ts
|
|
2212
|
+
var underlineFormat = {
|
|
2213
|
+
name: "underline",
|
|
2214
|
+
scope: "inline",
|
|
2215
|
+
validate(value) {
|
|
2216
|
+
return value === true;
|
|
2178
2217
|
}
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2218
|
+
};
|
|
2219
|
+
|
|
2220
|
+
// src/schema/formats/block/align.ts
|
|
2221
|
+
var VALID_ALIGN_TYPES = ["left", "center", "right", "justify"];
|
|
2222
|
+
var alignFormat = {
|
|
2223
|
+
name: "align",
|
|
2224
|
+
scope: "block",
|
|
2225
|
+
normalize(value) {
|
|
2226
|
+
return value.toLowerCase();
|
|
2227
|
+
},
|
|
2228
|
+
validate(value) {
|
|
2229
|
+
return VALID_ALIGN_TYPES.includes(value);
|
|
2182
2230
|
}
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
rest = rest.slice(0, hashIdx);
|
|
2231
|
+
};
|
|
2232
|
+
|
|
2233
|
+
// src/schema/formats/block/blockquote.ts
|
|
2234
|
+
var blockquoteFormat = {
|
|
2235
|
+
name: "blockquote",
|
|
2236
|
+
scope: "block",
|
|
2237
|
+
validate(value) {
|
|
2238
|
+
return value === true;
|
|
2192
2239
|
}
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2240
|
+
};
|
|
2241
|
+
|
|
2242
|
+
// src/schema/formats/block/code-block.ts
|
|
2243
|
+
var codeBlockFormat = {
|
|
2244
|
+
name: "code-block",
|
|
2245
|
+
scope: "block",
|
|
2246
|
+
normalize(value) {
|
|
2247
|
+
if (typeof value === "string") {
|
|
2248
|
+
return value.toLowerCase().trim();
|
|
2249
|
+
}
|
|
2250
|
+
return value;
|
|
2251
|
+
},
|
|
2252
|
+
validate(value) {
|
|
2253
|
+
if (value === true) {
|
|
2254
|
+
return true;
|
|
2255
|
+
}
|
|
2256
|
+
if (typeof value === "string" && value.length > 0) {
|
|
2257
|
+
return true;
|
|
2258
|
+
}
|
|
2259
|
+
return false;
|
|
2198
2260
|
}
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
var CODE_WIDGET_IFRAME_ALLOW = "accelerometer; camera; encrypted-media; geolocation; gyroscope; microphone; midi; payment; usb; xr-spatial-tracking; cross-origin-isolated";
|
|
2211
|
-
function renderEmbedIframeIsolationAttrs(context, kind) {
|
|
2212
|
-
const opts = context?.embed;
|
|
2213
|
-
const parts = [];
|
|
2214
|
-
if (kind === "codeWidget" && opts?.crossOriginIsolated) {
|
|
2215
|
-
parts.push(`allow="${CODE_WIDGET_IFRAME_ALLOW}"`);
|
|
2261
|
+
};
|
|
2262
|
+
|
|
2263
|
+
// src/schema/formats/block/header.ts
|
|
2264
|
+
var headerFormat = {
|
|
2265
|
+
name: "header",
|
|
2266
|
+
scope: "block",
|
|
2267
|
+
normalize(value) {
|
|
2268
|
+
return Math.max(1, Math.min(6, Math.floor(value)));
|
|
2269
|
+
},
|
|
2270
|
+
validate(value) {
|
|
2271
|
+
return Number.isInteger(value) && value >= 1 && value <= 6;
|
|
2216
2272
|
}
|
|
2217
|
-
|
|
2218
|
-
|
|
2273
|
+
};
|
|
2274
|
+
|
|
2275
|
+
// src/schema/formats/block/header-id.ts
|
|
2276
|
+
var headerIdFormat = {
|
|
2277
|
+
name: "header-id",
|
|
2278
|
+
scope: "block",
|
|
2279
|
+
normalize(value) {
|
|
2280
|
+
return String(value).trim().toLowerCase();
|
|
2281
|
+
},
|
|
2282
|
+
validate(value) {
|
|
2283
|
+
if (typeof value !== "string") return false;
|
|
2284
|
+
const trimmed = value.trim();
|
|
2285
|
+
return trimmed.length > 0 && !/\s/.test(trimmed);
|
|
2219
2286
|
}
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2287
|
+
};
|
|
2288
|
+
|
|
2289
|
+
// src/schema/formats/block/indent.ts
|
|
2290
|
+
var MAX_INDENT = 8;
|
|
2291
|
+
var indentFormat = {
|
|
2292
|
+
name: "indent",
|
|
2293
|
+
scope: "block",
|
|
2294
|
+
normalize(value) {
|
|
2295
|
+
return Math.max(0, Math.min(MAX_INDENT, Math.floor(value)));
|
|
2296
|
+
},
|
|
2297
|
+
validate(value) {
|
|
2298
|
+
return Number.isInteger(value) && value >= 0 && value <= MAX_INDENT;
|
|
2227
2299
|
}
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2300
|
+
};
|
|
2301
|
+
|
|
2302
|
+
// src/schema/formats/block/list.ts
|
|
2303
|
+
var VALID_LIST_TYPES = ["ordered", "bullet", "checked", "unchecked"];
|
|
2304
|
+
var listFormat = {
|
|
2305
|
+
name: "list",
|
|
2306
|
+
scope: "block",
|
|
2307
|
+
normalize(value) {
|
|
2308
|
+
return value.toLowerCase();
|
|
2309
|
+
},
|
|
2310
|
+
validate(value) {
|
|
2311
|
+
return VALID_LIST_TYPES.includes(value);
|
|
2231
2312
|
}
|
|
2232
|
-
|
|
2233
|
-
|
|
2313
|
+
};
|
|
2314
|
+
|
|
2315
|
+
// src/schema/formats/block/table-row.ts
|
|
2316
|
+
var tableRowFormat = {
|
|
2317
|
+
name: "table-row",
|
|
2318
|
+
scope: "block",
|
|
2319
|
+
validate(value) {
|
|
2320
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 0;
|
|
2234
2321
|
}
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2322
|
+
};
|
|
2323
|
+
|
|
2324
|
+
// src/schema/formats/block/table-col.ts
|
|
2325
|
+
var tableColFormat = {
|
|
2326
|
+
name: "table-col",
|
|
2327
|
+
scope: "block",
|
|
2328
|
+
validate(value) {
|
|
2329
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 0;
|
|
2238
2330
|
}
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2331
|
+
};
|
|
2332
|
+
|
|
2333
|
+
// src/schema/formats/block/table-header.ts
|
|
2334
|
+
var tableHeaderFormat = {
|
|
2335
|
+
name: "table-header",
|
|
2336
|
+
scope: "block",
|
|
2337
|
+
validate(value) {
|
|
2338
|
+
return value === true;
|
|
2244
2339
|
}
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2340
|
+
};
|
|
2341
|
+
|
|
2342
|
+
// src/schema/formats/block/table-col-align.ts
|
|
2343
|
+
var VALID_ALIGNS = ["left", "center", "right"];
|
|
2344
|
+
var tableColAlignFormat = {
|
|
2345
|
+
name: "table-col-align",
|
|
2346
|
+
scope: "block",
|
|
2347
|
+
normalize(value) {
|
|
2348
|
+
return value.toLowerCase();
|
|
2349
|
+
},
|
|
2350
|
+
validate(value) {
|
|
2351
|
+
return VALID_ALIGNS.includes(value);
|
|
2248
2352
|
}
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2353
|
+
};
|
|
2354
|
+
|
|
2355
|
+
// src/schema/formats/embed/block.ts
|
|
2356
|
+
var blockFormat = {
|
|
2357
|
+
name: "block",
|
|
2358
|
+
scope: "embed",
|
|
2359
|
+
validate(value) {
|
|
2360
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) && typeof value.type === "string" && value.type.length > 0;
|
|
2252
2361
|
}
|
|
2253
|
-
|
|
2254
|
-
}
|
|
2362
|
+
};
|
|
2255
2363
|
|
|
2256
2364
|
// src/schema/formats/embed/codeWidget.ts
|
|
2257
2365
|
var codeWidgetFormat = {
|
|
@@ -3806,6 +3914,10 @@ function htmlToDelta(html, options = {}) {
|
|
|
3806
3914
|
processBoxElement(node);
|
|
3807
3915
|
return;
|
|
3808
3916
|
}
|
|
3917
|
+
if (node.hasAttribute("data-scrider-ext-table")) {
|
|
3918
|
+
processExtTableHostElement(node);
|
|
3919
|
+
return;
|
|
3920
|
+
}
|
|
3809
3921
|
}
|
|
3810
3922
|
if (tagName === "p" || tagName === "div") {
|
|
3811
3923
|
processDefaultBlock(node);
|
|
@@ -4149,27 +4261,43 @@ function htmlToDelta(html, options = {}) {
|
|
|
4149
4261
|
}
|
|
4150
4262
|
processChildren(element);
|
|
4151
4263
|
}
|
|
4152
|
-
function
|
|
4153
|
-
|
|
4154
|
-
|
|
4155
|
-
|
|
4156
|
-
|
|
4157
|
-
|
|
4158
|
-
|
|
4159
|
-
|
|
4160
|
-
if (!innerHtml) return [{ insert: "\n" }];
|
|
4161
|
-
return htmlToDelta(innerHtml, options).ops;
|
|
4162
|
-
}
|
|
4163
|
-
};
|
|
4164
|
-
const data = tableHandler.fromHtml(table, blockContext);
|
|
4165
|
-
if (data) {
|
|
4166
|
-
flushText();
|
|
4167
|
-
delta.insert({ block: data });
|
|
4168
|
-
delta.insert("\n");
|
|
4169
|
-
atLineStart = true;
|
|
4170
|
-
return;
|
|
4264
|
+
function processExtTableHostElement(host) {
|
|
4265
|
+
let table = null;
|
|
4266
|
+
const children2 = host.childNodes;
|
|
4267
|
+
for (let i = 0; i < children2.length; i++) {
|
|
4268
|
+
const child = children2[i];
|
|
4269
|
+
if (child && isElement(child) && child.tagName.toLowerCase() === "table") {
|
|
4270
|
+
table = child;
|
|
4271
|
+
break;
|
|
4171
4272
|
}
|
|
4172
4273
|
}
|
|
4274
|
+
if (table && insertExtendedTableBlock(table, host)) return;
|
|
4275
|
+
processDefaultBlock(host);
|
|
4276
|
+
}
|
|
4277
|
+
function makeTableBlockParseContext() {
|
|
4278
|
+
return {
|
|
4279
|
+
registry: void 0,
|
|
4280
|
+
parseElement: (el) => {
|
|
4281
|
+
const innerHtml = el.innerHTML ?? "";
|
|
4282
|
+
if (!innerHtml) return [{ insert: "\n" }];
|
|
4283
|
+
return htmlToDelta(innerHtml, options).ops;
|
|
4284
|
+
}
|
|
4285
|
+
};
|
|
4286
|
+
}
|
|
4287
|
+
function insertExtendedTableBlock(table, host) {
|
|
4288
|
+
const tableHandler = options.blockHandlers?.get("table");
|
|
4289
|
+
if (!tableHandler) return false;
|
|
4290
|
+
let data = tableHandler.fromHtml(table, makeTableBlockParseContext());
|
|
4291
|
+
if (!data) return false;
|
|
4292
|
+
if (host) data = enrichTableBlockFromHost(data, host);
|
|
4293
|
+
flushText();
|
|
4294
|
+
delta.insert({ block: data });
|
|
4295
|
+
delta.insert("\n");
|
|
4296
|
+
atLineStart = true;
|
|
4297
|
+
return true;
|
|
4298
|
+
}
|
|
4299
|
+
function processTableElement(table) {
|
|
4300
|
+
if (insertExtendedTableBlock(table, null)) return;
|
|
4173
4301
|
let rowIdx = 0;
|
|
4174
4302
|
const tableChildren = table.childNodes;
|
|
4175
4303
|
for (let i = 0; i < tableChildren.length; i++) {
|