@bendyline/squisq 2.11.16 → 2.11.18

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.
Files changed (37) hide show
  1. package/dist/{Doc-Bd2kdzuy.d.ts → Doc-B6TYHYGc.d.ts} +41 -2
  2. package/dist/{ImageEditDoc-C3HOltpW.d.ts → ImageEditDoc-CN_3Ysj5.d.ts} +1 -1
  3. package/dist/chunk-3XEABCT3.js +114 -0
  4. package/dist/{chunk-WMXQDP2C.js → chunk-7IGKCHDP.js} +352 -17
  5. package/dist/{chunk-C3NAY5YP.js → chunk-A5QJJZ5S.js} +8 -5
  6. package/dist/{chunk-HVJ6VFDX.js → chunk-GB7OFYUK.js} +4 -0
  7. package/dist/{chunk-NC6LD6D3.js → chunk-J4ACZBO4.js} +50 -8
  8. package/dist/{chunk-ANH2OZ2M.js → chunk-LJZHC6GN.js} +146 -11
  9. package/dist/{chunk-IFJPPI63.js → chunk-NM747LUO.js} +829 -298
  10. package/dist/{chunk-GDHXT5Q7.js → chunk-PGWYC3S2.js} +2 -2
  11. package/dist/{chunk-X7BNDUNI.js → chunk-Q6ELOAOQ.js} +383 -1
  12. package/dist/{chunk-IRVXZUVU.js → chunk-RYO4QW6R.js} +2 -4
  13. package/dist/{chunk-TMOHAFYE.js → chunk-WSFXV5BT.js} +17 -0
  14. package/dist/doc/index.d.ts +35 -24
  15. package/dist/doc/index.js +13 -7
  16. package/dist/fence/index.d.ts +1 -1
  17. package/dist/generate/index.d.ts +1 -1
  18. package/dist/imageEdit/index.d.ts +3 -3
  19. package/dist/index.d.ts +7 -7
  20. package/dist/index.js +88 -34
  21. package/dist/jsonForm/index.d.ts +1 -1
  22. package/dist/jsonForm/index.js +1 -1
  23. package/dist/markdown/index.d.ts +170 -4
  24. package/dist/markdown/index.js +24 -4
  25. package/dist/{materializePageSection-FRv9whYy.d.ts → materializePageSection-IYXU3tDN.d.ts} +6 -1
  26. package/dist/mediaEdit/index.d.ts +2 -2
  27. package/dist/narration/index.d.ts +1 -1
  28. package/dist/narration/index.js +10 -9
  29. package/dist/recommend/index.js +2 -2
  30. package/dist/schemas/index.d.ts +4 -4
  31. package/dist/schemas/index.js +1 -1
  32. package/dist/{themeLibrary-BbF3EKWM.d.ts → themeLibrary-_46saKYO.d.ts} +1 -1
  33. package/dist/timing/index.d.ts +1 -1
  34. package/dist/transform/index.d.ts +84 -4
  35. package/dist/transform/index.js +38 -8
  36. package/package.json +1 -1
  37. package/dist/chunk-5W7F46OO.js +0 -337
@@ -6,11 +6,11 @@ import {
6
6
  quoteAttrValue,
7
7
  splitKeyValueToken,
8
8
  tokenizeAttrTokens
9
- } from "./chunk-TMOHAFYE.js";
9
+ } from "./chunk-WSFXV5BT.js";
10
10
  import {
11
11
  parseFrontmatter,
12
12
  parseHtmlToNodes
13
- } from "./chunk-X7BNDUNI.js";
13
+ } from "./chunk-Q6ELOAOQ.js";
14
14
  import {
15
15
  resolveIcon
16
16
  } from "./chunk-7N4G32LG.js";
@@ -685,6 +685,378 @@ function plainTextFromInlineHtml(html) {
685
685
  return walk(parseHtmlToNodes(html)).replace(/\u00A0/g, " ").replace(/\n{3,}/g, "\n\n").trim();
686
686
  }
687
687
 
688
+ // src/markdown/mediaReference.ts
689
+ var VIDEO_EXTENSIONS = /* @__PURE__ */ new Set(["mp4", "m4v", "mov", "webm", "ogv"]);
690
+ var AUDIO_EXTENSIONS = /* @__PURE__ */ new Set(["mp3", "m4a", "aac", "wav", "ogg", "oga", "opus", "flac"]);
691
+ var CONTAINER_EXTENSIONS = /* @__PURE__ */ new Set(["webm", "mp4"]);
692
+ function mediaKindForUrl(url) {
693
+ const path = url.split(/[?#]/, 1)[0] ?? "";
694
+ const name = path.slice(path.lastIndexOf("/") + 1);
695
+ const dot = name.lastIndexOf(".");
696
+ if (dot <= 0) return null;
697
+ const ext = name.slice(dot + 1).toLowerCase();
698
+ if (AUDIO_EXTENSIONS.has(ext)) return "audio";
699
+ if (CONTAINER_EXTENSIONS.has(ext) && /^(?:\.\/)?audio\//i.test(path)) return "audio";
700
+ if (VIDEO_EXTENSIONS.has(ext)) return "video";
701
+ return null;
702
+ }
703
+
704
+ // src/markdown/videoEmbed.ts
705
+ var VIDEO_EMBED_PROVIDERS = [
706
+ { id: "youtube", name: "YouTube", example: "https://www.youtube.com/watch?v=dQw4w9WgXcQ" },
707
+ { id: "vimeo", name: "Vimeo", example: "https://vimeo.com/76979871" },
708
+ {
709
+ id: "loom",
710
+ name: "Loom",
711
+ example: "https://www.loom.com/share/0281766fa2d04bb788eaf19e65135184"
712
+ },
713
+ { id: "dailymotion", name: "Dailymotion", example: "https://www.dailymotion.com/video/x7tgad0" },
714
+ { id: "wistia", name: "Wistia", example: "https://home.wistia.com/medias/e4a27b971d" }
715
+ ];
716
+ var PROVIDER_NAMES = Object.fromEntries(
717
+ VIDEO_EMBED_PROVIDERS.map((p) => [p.id, p.name])
718
+ );
719
+ var LANDSCAPE = 16 / 9;
720
+ var PORTRAIT = 9 / 16;
721
+ function unescapeMarkdown(value) {
722
+ return value.replace(/\\([!-/:-@[-`{-~])/g, "$1");
723
+ }
724
+ function toUrl(input) {
725
+ let text = unescapeMarkdown(input.trim());
726
+ if (text.startsWith("<") && text.endsWith(">")) text = text.slice(1, -1).trim();
727
+ if (!text || /\s/.test(text)) return null;
728
+ if (text.startsWith("//")) text = `https:${text}`;
729
+ else if (!/^[a-z][a-z0-9+.-]*:/i.test(text)) text = `https://${text}`;
730
+ let url;
731
+ try {
732
+ url = new URL(text);
733
+ } catch {
734
+ return null;
735
+ }
736
+ if (url.protocol !== "https:" && url.protocol !== "http:") return null;
737
+ if (url.username || url.password || url.port) return null;
738
+ return url;
739
+ }
740
+ function baseHost(url) {
741
+ return url.hostname.toLowerCase().replace(/^(?:www|m)\./, "");
742
+ }
743
+ function pathSegments(url) {
744
+ return url.pathname.split("/").filter(Boolean).map((segment) => {
745
+ try {
746
+ return decodeURIComponent(segment);
747
+ } catch {
748
+ return segment;
749
+ }
750
+ });
751
+ }
752
+ function fragmentParam(url, key) {
753
+ const hash = url.hash.replace(/^#/, "");
754
+ if (!hash) return null;
755
+ return new URLSearchParams(hash).get(key);
756
+ }
757
+ var HMS_RE = /^(?:(\d+)h)?(?:(\d+)m)?(?:(\d+)s?)?$/;
758
+ var CLOCK_RE = /^(?:(\d+):)?(\d{1,2}):(\d{2})$/;
759
+ function parseVideoStartTime(raw) {
760
+ if (!raw) return void 0;
761
+ const value = raw.trim().toLowerCase();
762
+ if (!value) return void 0;
763
+ let total = null;
764
+ const hms = HMS_RE.exec(value);
765
+ if (hms && (hms[1] || hms[2] || hms[3])) {
766
+ total = Number(hms[1] ?? 0) * 3600 + Number(hms[2] ?? 0) * 60 + Number(hms[3] ?? 0);
767
+ } else {
768
+ const clock = CLOCK_RE.exec(value);
769
+ const minutes = Number(clock?.[2]);
770
+ const seconds = Number(clock?.[3]);
771
+ if (clock && seconds < 60 && (clock[1] === void 0 || minutes < 60)) {
772
+ total = Number(clock[1] ?? 0) * 3600 + minutes * 60 + seconds;
773
+ }
774
+ }
775
+ if (total === null || !Number.isFinite(total) || total <= 0) return void 0;
776
+ return Math.floor(total);
777
+ }
778
+ function withQuery(base, params) {
779
+ const query = params.filter((entry) => entry[1] !== void 0).map(([key, value]) => `${key}=${encodeURIComponent(value)}`).join("&");
780
+ return query ? `${base}?${query}` : base;
781
+ }
782
+ function build(provider, fields) {
783
+ const embed = {
784
+ provider,
785
+ providerName: PROVIDER_NAMES[provider],
786
+ id: fields.id,
787
+ embedUrl: fields.embedUrl,
788
+ watchUrl: fields.watchUrl,
789
+ aspectRatio: fields.aspectRatio ?? LANDSCAPE
790
+ };
791
+ if (fields.playlistId) embed.playlistId = fields.playlistId;
792
+ if (fields.startSeconds) embed.startSeconds = fields.startSeconds;
793
+ if (fields.thumbnailUrl) embed.thumbnailUrl = fields.thumbnailUrl;
794
+ return embed;
795
+ }
796
+ var YOUTUBE_HOSTS = /* @__PURE__ */ new Set(["youtube.com", "music.youtube.com", "youtube-nocookie.com"]);
797
+ var YOUTUBE_ID_RE = /^[A-Za-z0-9_-]{11}$/;
798
+ var YOUTUBE_LIST_RE = /^[A-Za-z0-9_-]{2,80}$/;
799
+ var YOUTUBE_ID_PATHS = /* @__PURE__ */ new Set(["embed", "v", "e", "live", "shorts"]);
800
+ var YOUTUBE_EMBED_ORIGIN = "https://www.youtube-nocookie.com";
801
+ function parseYouTube(url) {
802
+ const host = baseHost(url);
803
+ const segments = pathSegments(url);
804
+ const params = url.searchParams;
805
+ let id = null;
806
+ let shorts = false;
807
+ if (host === "youtu.be") {
808
+ id = segments[0] ?? null;
809
+ } else if (YOUTUBE_HOSTS.has(host)) {
810
+ const [head, second] = segments;
811
+ if (head === "watch") id = params.get("v");
812
+ else if (head && YOUTUBE_ID_PATHS.has(head) && second !== "videoseries") {
813
+ id = second ?? null;
814
+ shorts = head === "shorts";
815
+ } else if (head !== "playlist" && !(head === "embed" && second === "videoseries")) {
816
+ return null;
817
+ }
818
+ } else {
819
+ return null;
820
+ }
821
+ if (id !== null && !YOUTUBE_ID_RE.test(id)) return null;
822
+ const list = params.get("list");
823
+ const playlistId = list && YOUTUBE_LIST_RE.test(list) ? list : void 0;
824
+ if (!id && !playlistId) return null;
825
+ const startSeconds = parseVideoStartTime(
826
+ params.get("t") ?? params.get("start") ?? fragmentParam(url, "t")
827
+ );
828
+ const start = startSeconds ? String(startSeconds) : void 0;
829
+ if (!id) {
830
+ return build("youtube", {
831
+ id: playlistId,
832
+ playlistId,
833
+ embedUrl: withQuery(`${YOUTUBE_EMBED_ORIGIN}/embed/videoseries`, [["list", playlistId]]),
834
+ watchUrl: withQuery("https://www.youtube.com/playlist", [["list", playlistId]])
835
+ });
836
+ }
837
+ return build("youtube", {
838
+ id,
839
+ playlistId,
840
+ startSeconds,
841
+ embedUrl: withQuery(`${YOUTUBE_EMBED_ORIGIN}/embed/${id}`, [
842
+ ["list", playlistId],
843
+ ["start", start]
844
+ ]),
845
+ watchUrl: shorts ? `https://www.youtube.com/shorts/${id}` : withQuery("https://www.youtube.com/watch", [
846
+ ["v", id],
847
+ ["list", playlistId],
848
+ ["t", start && `${start}s`]
849
+ ]),
850
+ thumbnailUrl: `https://i.ytimg.com/vi/${id}/hqdefault.jpg`,
851
+ aspectRatio: shorts ? PORTRAIT : LANDSCAPE
852
+ });
853
+ }
854
+ var VIMEO_ID_RE = /^\d{1,12}$/;
855
+ var VIMEO_HASH_RE = /^[0-9a-f]{6,24}$/i;
856
+ function vimeoIdFromSegments(segments) {
857
+ const [a, b, c, d] = segments;
858
+ if (a && VIMEO_ID_RE.test(a)) {
859
+ return { id: a, ...b && VIMEO_HASH_RE.test(b) ? { hash: b } : {} };
860
+ }
861
+ if (a === "video" && b && VIMEO_ID_RE.test(b)) return { id: b };
862
+ if (a === "channels" && c && VIMEO_ID_RE.test(c)) return { id: c };
863
+ if (a === "groups" && c === "videos" && d && VIMEO_ID_RE.test(d)) return { id: d };
864
+ if ((a === "album" || a === "showcase") && c === "video" && d && VIMEO_ID_RE.test(d)) {
865
+ return { id: d };
866
+ }
867
+ return null;
868
+ }
869
+ function parseVimeo(url) {
870
+ const host = baseHost(url);
871
+ if (host !== "vimeo.com" && host !== "player.vimeo.com") return null;
872
+ const found = vimeoIdFromSegments(pathSegments(url));
873
+ if (!found) return null;
874
+ const queryHash = url.searchParams.get("h");
875
+ const hash = found.hash ?? (queryHash && VIMEO_HASH_RE.test(queryHash) ? queryHash : void 0);
876
+ const startSeconds = parseVideoStartTime(
877
+ fragmentParam(url, "t") ?? url.searchParams.get("t") ?? url.searchParams.get("start")
878
+ );
879
+ const timeFragment = startSeconds ? `#t=${startSeconds}s` : "";
880
+ return build("vimeo", {
881
+ id: found.id,
882
+ startSeconds,
883
+ embedUrl: withQuery(`https://player.vimeo.com/video/${found.id}`, [["h", hash]]) + timeFragment,
884
+ watchUrl: `https://vimeo.com/${found.id}${hash ? `/${hash}` : ""}${timeFragment}`
885
+ });
886
+ }
887
+ var LOOM_ID_RE = /(?:^|-)([0-9a-f]{32})$/i;
888
+ function parseLoom(url) {
889
+ if (baseHost(url) !== "loom.com") return null;
890
+ const [head, second] = pathSegments(url);
891
+ if (head !== "share" && head !== "embed" || !second) return null;
892
+ const match = LOOM_ID_RE.exec(second);
893
+ if (!match) return null;
894
+ const id = match[1].toLowerCase();
895
+ return build("loom", {
896
+ id,
897
+ embedUrl: `https://www.loom.com/embed/${id}`,
898
+ watchUrl: `https://www.loom.com/share/${id}`
899
+ });
900
+ }
901
+ var DAILYMOTION_ID_RE = /^[A-Za-z0-9]{5,20}$/;
902
+ function parseDailymotion(url) {
903
+ const host = baseHost(url);
904
+ const segments = pathSegments(url);
905
+ let raw = null;
906
+ if (host === "dai.ly") raw = segments[0] ?? null;
907
+ else if (host === "dailymotion.com") {
908
+ if (segments[0] === "video") raw = segments[1] ?? null;
909
+ else if (segments[0] === "embed" && segments[1] === "video") raw = segments[2] ?? null;
910
+ else return null;
911
+ } else if (host === "geo.dailymotion.com") {
912
+ raw = url.searchParams.get("video");
913
+ } else {
914
+ return null;
915
+ }
916
+ const id = raw?.split("_")[0];
917
+ if (!id || !DAILYMOTION_ID_RE.test(id)) return null;
918
+ const startSeconds = parseVideoStartTime(url.searchParams.get("start"));
919
+ const start = startSeconds ? String(startSeconds) : void 0;
920
+ return build("dailymotion", {
921
+ id,
922
+ startSeconds,
923
+ embedUrl: withQuery(`https://www.dailymotion.com/embed/video/${id}`, [["start", start]]),
924
+ watchUrl: withQuery(`https://www.dailymotion.com/video/${id}`, [["start", start]]),
925
+ thumbnailUrl: `https://www.dailymotion.com/thumbnail/video/${id}`
926
+ });
927
+ }
928
+ var WISTIA_ID_RE = /^[a-z0-9]{10}$/;
929
+ var WISTIA_ACCOUNT_HOST_RE = /^[a-z0-9-]{1,63}\.wistia\.com$/;
930
+ function parseWistia(url) {
931
+ const host = url.hostname.toLowerCase();
932
+ const fastHost = host === "fast.wistia.net" || host === "fast.wistia.com";
933
+ if (!fastHost && !WISTIA_ACCOUNT_HOST_RE.test(host)) return null;
934
+ const segments = pathSegments(url);
935
+ let raw;
936
+ if (segments[0] === "medias") raw = segments[1];
937
+ else if (segments[0] === "embed" && (segments[1] === "iframe" || segments[1] === "medias")) {
938
+ raw = segments[2];
939
+ }
940
+ const id = raw?.replace(/\.jsonp$/, "");
941
+ if (!id || !WISTIA_ID_RE.test(id)) return null;
942
+ const embedUrl = `https://fast.wistia.net/embed/iframe/${id}`;
943
+ return build("wistia", {
944
+ id,
945
+ embedUrl,
946
+ // A media page lives on the account's own subdomain; the fast host only
947
+ // serves the player, which doubles as a viewable page.
948
+ watchUrl: fastHost ? embedUrl : `https://${host}/medias/${id}`
949
+ });
950
+ }
951
+ var PARSERS = [
952
+ parseYouTube,
953
+ parseVimeo,
954
+ parseLoom,
955
+ parseDailymotion,
956
+ parseWistia
957
+ ];
958
+ function parseVideoEmbedUrl(input) {
959
+ if (typeof input !== "string") return null;
960
+ const url = toUrl(input);
961
+ if (!url) return null;
962
+ for (const parse of PARSERS) {
963
+ const embed = parse(url);
964
+ if (embed) return embed;
965
+ }
966
+ return null;
967
+ }
968
+ function isVideoEmbedUrl(input) {
969
+ return parseVideoEmbedUrl(input) !== null;
970
+ }
971
+ var VIDEO_EMBED_IFRAME_ALLOW = "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen";
972
+ function videoEmbedIframeAttributes(embed, title) {
973
+ return {
974
+ src: embed.embedUrl,
975
+ title: title?.trim() || `${embed.providerName} video`,
976
+ allow: VIDEO_EMBED_IFRAME_ALLOW,
977
+ allowfullscreen: "",
978
+ referrerpolicy: "strict-origin-when-cross-origin",
979
+ loading: "lazy"
980
+ };
981
+ }
982
+ var GENERIC_PLAYER_TITLE_RE = /^(?:[\w .-]+[ -])?(?:video[ -])?player$/i;
983
+ function attributeOf(element, name) {
984
+ for (const [key, value] of Object.entries(element.attributes)) {
985
+ if (key.toLowerCase() === name) return value;
986
+ }
987
+ return void 0;
988
+ }
989
+ function soleIframe(nodes) {
990
+ let found = null;
991
+ let disqualified = false;
992
+ const visit = (list) => {
993
+ for (const node of list) {
994
+ if (disqualified) return;
995
+ if (node.type === "htmlText") {
996
+ if (node.value.trim()) disqualified = true;
997
+ continue;
998
+ }
999
+ if (node.type !== "htmlElement") continue;
1000
+ const tag = node.tagName.toLowerCase();
1001
+ if (tag === "iframe") {
1002
+ if (found) disqualified = true;
1003
+ found = node;
1004
+ continue;
1005
+ }
1006
+ if (tag === "script" || tag === "style" || tag === "noscript") continue;
1007
+ visit(node.children);
1008
+ }
1009
+ };
1010
+ visit(nodes);
1011
+ return disqualified ? null : found;
1012
+ }
1013
+ function iframeEmbed(element) {
1014
+ const embed = parseVideoEmbedUrl(attributeOf(element, "src"));
1015
+ if (!embed) return null;
1016
+ const title = attributeOf(element, "title")?.trim() ?? "";
1017
+ const generic = GENERIC_PLAYER_TITLE_RE.test(title) || title.toLowerCase() === `${embed.providerName} video`.toLowerCase();
1018
+ return { embed, title: title && !generic ? title : null };
1019
+ }
1020
+ function videoEmbedFromIframe(element) {
1021
+ return element.tagName.toLowerCase() === "iframe" ? iframeEmbed(element) : null;
1022
+ }
1023
+ function parseVideoEmbedInput(input) {
1024
+ if (typeof input !== "string") return null;
1025
+ const text = input.trim();
1026
+ if (!text) return null;
1027
+ if (/^<(?!https?:)/i.test(text)) {
1028
+ if (!/<iframe\b/i.test(text)) return null;
1029
+ const iframe = soleIframe(parseHtmlToNodes(text));
1030
+ return iframe ? iframeEmbed(iframe) : null;
1031
+ }
1032
+ const embed = parseVideoEmbedUrl(text);
1033
+ return embed ? { embed, title: null } : null;
1034
+ }
1035
+ function isBlankInline(node) {
1036
+ return node.type === "text" && !node.value.trim() || node.type === "htmlInline" && node.htmlChildren.length === 0;
1037
+ }
1038
+ function findBlockVideoEmbed(node) {
1039
+ if (node.type === "htmlBlock") {
1040
+ const iframe = soleIframe(node.htmlChildren);
1041
+ const found = iframe ? iframeEmbed(iframe) : null;
1042
+ return found ? { ...found, form: "iframe" } : null;
1043
+ }
1044
+ if (node.type !== "paragraph") return null;
1045
+ const text = extractPlainText(node).trim();
1046
+ if (text && !/\s/.test(text)) {
1047
+ const bare = parseVideoEmbedUrl(text);
1048
+ if (bare) return { embed: bare, title: null, form: "url" };
1049
+ }
1050
+ const significant = node.children.filter((child) => !isBlankInline(child));
1051
+ if (significant.length !== 1 || significant[0].type !== "link") return null;
1052
+ const link = significant[0];
1053
+ const embed = parseVideoEmbedUrl(link.url);
1054
+ if (!embed) return null;
1055
+ const label = extractPlainText(link).trim();
1056
+ const title = label && !isVideoEmbedUrl(label) && label !== link.url ? label : null;
1057
+ return { embed, title, form: title ? "link" : "url" };
1058
+ }
1059
+
688
1060
  export {
689
1061
  parseHtmlToNodes,
690
1062
  stringifyHtmlNodes,
@@ -702,5 +1074,15 @@ export {
702
1074
  createDocument,
703
1075
  inferDocumentTitle,
704
1076
  readFrontmatterThemeId,
705
- plainTextFromInlineHtml
1077
+ plainTextFromInlineHtml,
1078
+ mediaKindForUrl,
1079
+ VIDEO_EMBED_PROVIDERS,
1080
+ parseVideoStartTime,
1081
+ parseVideoEmbedUrl,
1082
+ isVideoEmbedUrl,
1083
+ VIDEO_EMBED_IFRAME_ALLOW,
1084
+ videoEmbedIframeAttributes,
1085
+ videoEmbedFromIframe,
1086
+ parseVideoEmbedInput,
1087
+ findBlockVideoEmbed
706
1088
  };
@@ -6,11 +6,9 @@ import {
6
6
  import {
7
7
  buildNarrationScript,
8
8
  expectedSyllablesAt,
9
+ flattenRenderableBlocks,
9
10
  wordPosAtExpectedSyllables
10
- } from "./chunk-5W7F46OO.js";
11
- import {
12
- flattenRenderableBlocks
13
- } from "./chunk-WMXQDP2C.js";
11
+ } from "./chunk-7IGKCHDP.js";
14
12
 
15
13
  // src/narration/types.ts
16
14
  var DEFAULT_FEATURE_CONFIG = Object.freeze({
@@ -1,3 +1,7 @@
1
+ import {
2
+ videoEmbedFromIframe,
3
+ videoEmbedIframeAttributes
4
+ } from "./chunk-Q6ELOAOQ.js";
1
5
  import {
2
6
  TRANSITION_DIRECTIONS,
3
7
  TRANSITION_TYPES,
@@ -402,6 +406,7 @@ function sanitizeHtmlNode(node) {
402
406
  }
403
407
  function sanitizeHtmlElement(node) {
404
408
  const tag = node.tagName.toLowerCase();
409
+ if (tag === "iframe") return sanitizeVideoEmbedIframe(node);
405
410
  if (DROP_WITH_CONTENT_TAGS.has(tag)) return [];
406
411
  const children = sanitizeHtmlNodes(node.children);
407
412
  if (!SAFE_TAGS.has(tag)) return children;
@@ -415,6 +420,18 @@ function sanitizeHtmlElement(node) {
415
420
  }
416
421
  ];
417
422
  }
423
+ function sanitizeVideoEmbedIframe(node) {
424
+ const found = videoEmbedFromIframe(node);
425
+ if (!found) return [];
426
+ const attributes = videoEmbedIframeAttributes(found.embed, found.title);
427
+ for (const [rawName, value] of Object.entries(node.attributes)) {
428
+ const name = rawName.toLowerCase();
429
+ if ((name === "width" || name === "height") && isNonNegativeInteger(value)) {
430
+ attributes[name] = value.trim();
431
+ }
432
+ }
433
+ return [{ type: "htmlElement", tagName: "iframe", attributes, children: [], selfClosing: false }];
434
+ }
418
435
  function sanitizeAttrs(tag, attrs) {
419
436
  const out = {};
420
437
  for (const [rawName, rawValue] of Object.entries(attrs)) {