@@ -842,6 +853,316 @@
+
+
{
+ libreCmsAudioNode: {
+ setLibreCmsAudio: (attributes?: { language: string }) => ReturnType;
+ };
+ }
+}
+
+export const AudioNode = Node.create({
+ name: "libreCmsAudioNode",
+
+ content: "inline*",
+
+ marks: "",
+
+ group: "block",
+
+ code: false,
+
+ defining: true,
+
+ addAttributes() {
+ return {
+ align: {
+ parseHTML: (element) => {
+ if (element.hasAttribute("data-align")) {
+ return element.getAttribute("data-align");
+ } else {
+ return "center";
+ }
+ },
+ },
+ altText: {
+ parseHTML: (element) => {
+ const audioElem = element.querySelector("audio");
+ if (audioElem) {
+ return audioElem.textContent;
+ } else {
+ return "";
+ }
+ },
+ },
+ figCaption: {
+ parseHTML: (element) => {
+ const figCaptionElem = element.querySelector("figcaption");
+ if (figCaptionElem) {
+ return figCaptionElem.innerHTML;
+ } else {
+ return "";
+ }
+ },
+ },
+ audioSrc: {
+ parseHTML: (element) => {
+ const audioElem = element.querySelector("audio");
+ if (audioElem) {
+ return audioElem.src;
+ } else {
+ return "";
+ }
+ },
+ },
+ };
+ },
+
+ addCommands() {
+ return {
+ setLibreCmsAudio:
+ (attributes) =>
+ ({ commands }) => {
+ return commands.setNode("libreCmsAudioNode", attributes);
+ },
+ };
+ },
+
+ addNodeView() {
+ return ({
+ editor,
+ node,
+ getPos,
+ HTMLAttributes,
+ decorations,
+ extension,
+ }) => {
+ const templateNode = document.querySelector(
+ "#librecms-audio-node-view"
+ );
+ if (!templateNode) {
+ const errorMsg = document.createElement("div");
+ errorMsg.classList.add("alert", "alert-danger");
+ errorMsg.textContent = "Failed to create audio node view.";
+
+ return errorMsg;
+ }
+
+ const dom = document.createElement("div");
+ dom.classList.add("librecms-audio-node-view", "p-2");
+ if (!node.attrs.align) {
+ node.attrs.align = "center";
+ }
+ dom.classList.add(`librecms-audio-node-align-${node.attrs.align}`);
+ const template = templateNode as HTMLTemplateElement;
+ const nodeView = template.content.cloneNode(true) as HTMLElement;
+ const audioElem = nodeView.querySelector("audio");
+ const dialogIdNr = Math.floor(Math.random() * 1000000000);
+
+ const selectDialogId = `librecms-image-audio-select-audio-dialog-${dialogIdNr}`;
+ const selectDialogTitleId = `librecms-audio-node-select-audio-dialog-${dialogIdNr}-title`;
+ const selectButtonElem = nodeView.querySelector(
+ ".select-audio-button"
+ );
+ if (selectButtonElem) {
+ selectButtonElem.setAttribute(
+ "data-target",
+ `#${selectDialogId}`
+ );
+ }
+ const selectDialogElem = nodeView.querySelector(
+ ".modal.select-audio-dialog"
+ );
+ if (selectDialogElem) {
+ selectDialogElem.id = selectDialogId;
+ selectDialogElem.setAttribute(
+ "aria-labelledby",
+ selectDialogTitleId
+ );
+
+ const selectDialogTitleElem =
+ selectDialogElem.querySelector(".modal-title");
+ if (selectDialogTitleElem) {
+ selectDialogTitleElem.id = selectDialogTitleId;
+ }
+
+ const selectDialogIds =
+ selectDialogElem.querySelectorAll("*[id]");
+ selectDialogIds.forEach((elemWithId) => {
+ elemWithId.id = `${elemWithId.id}-${dialogIdNr}`;
+ });
+
+ const selectDialogLabels =
+ selectDialogElem.querySelectorAll("*[for]");
+ selectDialogLabels.forEach((label) => {
+ label.setAttribute(
+ "for",
+ `${label.getAttribute("for")}-${dialogIdNr}`
+ );
+ });
+
+ const describedElems = selectDialogElem.querySelectorAll(
+ "*[aria-describedby]"
+ );
+ describedElems.forEach((describedElem) => {
+ describedElem.setAttribute(
+ "aria-describedby",
+ `${describedElem.getAttribute(
+ "aria-describedby"
+ )}-${dialogIdNr}`
+ );
+ });
+ }
+
+ const settingsDialogId = `librecms-audio-node-view-settings-dialog-${dialogIdNr}`;
+ const settingsDialogTitleId = `${settingsDialogId}-title`;
+ const settingsButtonElem = nodeView.querySelector(
+ ".audio-settings-button"
+ );
+ if (settingsButtonElem) {
+ settingsButtonElem.setAttribute(
+ "data-target",
+ `#${settingsDialogId}`
+ );
+ }
+ const settingsDialogElem = nodeView.querySelector(
+ ".modal.audio-settings-dialog"
+ );
+ if (settingsDialogElem) {
+ settingsDialogElem.id = settingsDialogId;
+ settingsDialogElem.setAttribute(
+ "aria-labelledby",
+ settingsDialogTitleId
+ );
+
+ const settingsDialogTitleElem =
+ settingsDialogElem.querySelector(".modal-title");
+ if (settingsDialogTitleElem) {
+ settingsDialogTitleElem.id = settingsDialogTitleId;
+ }
+
+ const settingDialogIds =
+ settingsDialogElem.querySelectorAll("*[id]");
+ for (let i = 0; i < settingDialogIds.length; i++) {
+ const elemWithId = settingDialogIds.item(i);
+ elemWithId.id = `${elemWithId.id}-${dialogIdNr}`;
+ }
+ const settingDialogLabels =
+ settingsDialogElem.querySelectorAll("*[for]");
+ for (let i = 0; i < settingDialogLabels.length; i++) {
+ const label = settingDialogLabels.item(
+ i
+ ) as HTMLLabelElement;
+ label.setAttribute(
+ "for",
+ `${label.getAttribute("for")}-${dialogIdNr}`
+ );
+ }
+ const describedElems = settingsDialogElem.querySelectorAll(
+ "*[aria-describedby]"
+ );
+ for (let i = 0; i < describedElems.length; i++) {
+ const describedElem = describedElems.item(i);
+ describedElem.setAttribute(
+ "aria-describedby",
+ `${describedElem.getAttribute(
+ "aria-describedby"
+ )}-${dialogIdNr}`
+ );
+ }
+
+ const submitButton = settingsDialogElem.querySelector(
+ "button.audio-settings-dialog-save"
+ );
+
+ const altTextInput = settingsDialogElem.querySelector(
+ `input#alttext-${dialogIdNr}`
+ );
+ const alignSelect = settingsDialogElem.querySelector(
+ `select#align-${dialogIdNr}`
+ );
+
+ if (altTextInput) {
+ (altTextInput as HTMLInputElement).value =
+ node.attrs.altText;
+ } else {
+ console.warn("Input for alt text not found.");
+ }
+
+ if (alignSelect) {
+ const optionElems = alignSelect.querySelectorAll("option");
+ for (let i = 0; i < optionElems.length; i++) {
+ const optionElem = optionElems.item(
+ i
+ ) as HTMLOptionElement;
+ if (optionElem.value === node.attrs.align) {
+ optionElem.selected = true;
+ }
+ }
+ } else {
+ console.warn("Select for image alignment not found.");
+ }
+
+ if (submitButton) {
+ submitButton.addEventListener("click", (event) => {
+ const inputElem = altTextInput as HTMLInputElement;
+ if (altTextInput) {
+ node.attrs.altText = inputElem.value;
+ if (audioElem) {
+ audioElem.textContent = inputElem.value;
+ }
+ } else {
+ console.warn("Input for alt text not found.");
+ }
+
+ if (alignSelect) {
+ const selectElem = alignSelect as HTMLSelectElement;
+ node.attrs.align =
+ selectElem.selectedOptions.item(0)?.value;
+ ["floatleft", "center", "floatright"].forEach(
+ (align) =>
+ dom.classList.remove(
+ `librecms-audio-node-align-${align}`
+ )
+ );
+ dom.classList.add(
+ `librecms-audio-node-align-${node.attrs.align}`
+ );
+ } else {
+ console.warn(
+ "Select for audio alignment not found."
+ );
+ }
+ });
+ } else {
+ console.warn(
+ "Submit button for image settings dialog not found."
+ );
+ }
+ }
+
+ const figCaptionElem = nodeView.querySelector("figcaption");
+ if (node.attrs.figCaption !== "" && figCaptionElem) {
+ figCaptionElem.innerHTML = node.attrs.figCaption;
+ }
+
+ dom.appendChild(nodeView);
+
+ if (selectButtonElem) {
+ if (audioElem) {
+ selectButtonElem.addEventListener("click", (event) =>
+ loadAudioAssets(event, node, audioElem)
+ );
+ } else {
+ console.error("audio elem not found.");
+ }
+ }
+
+ return {
+ dom,
+ };
+ };
+ },
+
+ parseHTML() {
+ return [
+ {
+ tag: "figure[data-type=librecms-audio-node]",
+ },
+ ];
+ },
+
+ renderHTML({ node, HTMLAttributes }) {
+ return [
+ "figure",
+ mergeAttributes({
+ "data-align": node.attrs.align,
+ "data-type": "librecms-image-node",
+ }),
+ [
+ "audio",
+ {
+ src: node.attrs.audioSrc,
+ textContent: node.attrs.altText,
+ },
+ ],
+ ["figcaption", [node.attrs.figCaption]],
+ ];
+ },
+});
+
+function loadAudioAssets(
+ event: Event,
+ node: ProsemirrorNode
,
+ audioElem: HTMLAudioElement
+) {
+ const eventTarget = event.currentTarget as HTMLElement;
+ const editorElem = document.querySelector(".cms-editor");
+ if (!editorElem) {
+ return;
+ }
+ const baseUrl = editorElem.getAttribute("data-baseUrl");
+ const contentSection = editorElem.getAttribute("data-contentsection");
+ const dialogId = eventTarget.getAttribute("data-target");
+ if (!dialogId) {
+ console.error("data-target attribute is missing.");
+ return;
+ }
+ const dialog = document.querySelector(dialogId);
+ if (!dialog) {
+ console.error("dialog element not found is missing.");
+ return;
+ }
+ const rowTemplateResult = document.querySelector(
+ "#librecms-audio-node-view-row"
+ );
+ if (!rowTemplateResult) {
+ console.error("template for result row not found.");
+ return;
+ }
+ const rowTemplate = rowTemplateResult as HTMLTemplateElement;
+ const table = dialog.querySelector("table");
+ if (!table) {
+ console.error("result table not found.");
+ return;
+ }
+ const tableBody = table.querySelector("tbody");
+ if (!tableBody) {
+ console.error("table body not found.");
+ return;
+ }
+ tableBody.innerHTML = "";
+ const fetchUrl = `/content-sections/${contentSection}/assets?type=org.librecms.assets.AudioAsset,org.librecms.assets.ExternalAudioAsset`;
+ fetch(fetchUrl)
+ .then((response) => {
+ if (response.ok) {
+ response
+ .json()
+ .then((data) => {
+ const audioAssets = data as [];
+ for (const audioAsset of audioAssets) {
+ const row = rowTemplate.content.cloneNode(
+ true
+ ) as Element;
+ const colName = row.querySelector(".col-name");
+ const colType = row.querySelector(".col-type");
+ const selectButton =
+ row.querySelector(".col-action button");
+
+ if (colName) {
+ colName.textContent = audioAsset["name"];
+ }
+ if (colType) {
+ colType.textContent = audioAsset["type"];
+ }
+
+ if (selectButton) {
+ selectButton.setAttribute(
+ "data-audiouuid",
+ audioAsset["uuid"]
+ );
+
+ selectButton.addEventListener(
+ "click",
+ (event) => {
+ const audioUrl = `/content-sections/info/audio/uuid-${audioAsset["uuid"]}`;
+ node.attrs.imgSrc = audioUrl;
+ if (audioElem) {
+ audioElem.src = audioUrl;
+ } else {
+ console.error(
+ "img element not found."
+ );
+ }
+ }
+ );
+ }
+
+ tableBody.appendChild(row);
+ }
+ })
+ .catch((error) => {
+ console.error(error);
+ });
+ } else {
+ console.error(
+ `Error. Status: ${response.status}. Status Text: ${response.statusText}`
+ );
+ }
+ })
+ .catch((error) => {
+ console.error(error);
+ });
+}
diff --git a/ccm-cms/src/main/typescript/content-sections/cms-editor/audio-node/index.ts b/ccm-cms/src/main/typescript/content-sections/cms-editor/audio-node/index.ts
new file mode 100644
index 000000000..4d22fab8f
--- /dev/null
+++ b/ccm-cms/src/main/typescript/content-sections/cms-editor/audio-node/index.ts
@@ -0,0 +1,5 @@
+import { AudioNode } from "./audio-node";
+
+export * from "./audio-node";
+
+export default AudioNode;
\ No newline at end of file
diff --git a/ccm-cms/src/main/typescript/content-sections/cms-editor/image-node/image-node.ts b/ccm-cms/src/main/typescript/content-sections/cms-editor/image-node/image-node.ts
index f5600904d..ae47e4793 100644
--- a/ccm-cms/src/main/typescript/content-sections/cms-editor/image-node/image-node.ts
+++ b/ccm-cms/src/main/typescript/content-sections/cms-editor/image-node/image-node.ts
@@ -111,8 +111,7 @@ export const ImageNode = Node.create({
);
if (!templateNode) {
const errorMsg = document.createElement("div");
- errorMsg.classList.add("alert");
- errorMsg.classList.add("alert-danger");
+ errorMsg.classList.add("alert", "alert-danger");
errorMsg.textContent = "Failed to create image node view.";
return errorMsg;
@@ -148,8 +147,8 @@ export const ImageNode = Node.create({
const imgElem = nodeView.querySelector("img");
const dialogIdNr = Math.floor(Math.random() * 1000000000);
- const selectDialogId = `librecms-image-node-select-image-dialog-title-${dialogIdNr}`;
- const selectDialogTitleId = `librecms-image-node-select-image-dialog-title-${dialogIdNr}-title`;
+ const selectDialogId = `librecms-image-node-select-image-dialog-${dialogIdNr}`;
+ const selectDialogTitleId = `librecms-image-node-select-image-dialog-${dialogIdNr}-title`;
const selectButtonElem = nodeView.querySelector(
".select-image-button"
);
@@ -180,6 +179,7 @@ export const ImageNode = Node.create({
selectDialogIds.forEach((elemWithId) => {
elemWithId.id = `${elemWithId.id}-${dialogIdNr}`;
});
+
const selectDialogLabels =
selectDialogElem.querySelectorAll("*[for]");
selectDialogLabels.forEach((label) => {
@@ -383,24 +383,6 @@ export const ImageNode = Node.create({
"Submit button for image settings dialog not found."
);
}
-
- // const removeImageButton = nodeView.querySelector(
- // ".image-remove-button"
- // );
- // if (removeImageButton) {
- // removeImageButton.addEventListener("click", (event) => {
- // if (typeof getPos === "number") {
- // const from = getPos as number;
- // const range: Range = {
- // from,
- // to: node.nodeSize,
- // };
- // editor.chain().focus().deleteRange(range);
- // }
- // });
- // } else {
- // console.error("removeImageButton not found.");
- // }
}
const figCaptionElem = nodeView.querySelector("figcaption");
@@ -452,7 +434,7 @@ export const ImageNode = Node.create({
src: node.attrs.imgSrc,
},
],
- ["figcaption"],
+ ["figcaption", [node.attrs.figCaption]],
];
},
});
@@ -462,7 +444,7 @@ function loadImages(
node: ProsemirrorNode,
imgElem: HTMLImageElement
) {
- console.log("Loading images...");
+ // console.log("Loading images...");
const eventTarget = event.currentTarget as HTMLElement;
const editorElem = document.querySelector(".cms-editor");
if (!editorElem) {
@@ -520,7 +502,7 @@ function loadImages(
colName.textContent = image["name"];
}
if (colType) {
- colType.textContent = image["name"];
+ colType.textContent = image["type"];
}
if (selectButton) {
diff --git a/ccm-cms/src/main/typescript/content-sections/cms-editor/video-node/index.ts b/ccm-cms/src/main/typescript/content-sections/cms-editor/video-node/index.ts
new file mode 100644
index 000000000..b1dfd2dab
--- /dev/null
+++ b/ccm-cms/src/main/typescript/content-sections/cms-editor/video-node/index.ts
@@ -0,0 +1,5 @@
+import { VideoNode } from "./video-node";
+
+export * from "./video-node";
+
+export default VideoNode;
\ No newline at end of file
diff --git a/ccm-cms/src/main/typescript/content-sections/cms-editor/video-node/video-node.ts b/ccm-cms/src/main/typescript/content-sections/cms-editor/video-node/video-node.ts
new file mode 100644
index 000000000..43a807f01
--- /dev/null
+++ b/ccm-cms/src/main/typescript/content-sections/cms-editor/video-node/video-node.ts
@@ -0,0 +1,493 @@
+import { Node, nodeInputRule, mergeAttributes, Range } from "@tiptap/core";
+import { merge } from "jquery";
+import { Node as ProsemirrorNode } from "prosemirror-model";
+
+declare module "@tiptap/core" {
+ interface Commands {
+ libreCmsVideoNode: {
+ setLibreCmsVideo: (attributes?: { language: string }) => ReturnType;
+ };
+ }
+}
+
+export const VideoNode = Node.create({
+ name: "libreCmsVideoNode",
+
+ content: "inline*",
+
+ marks: "",
+
+ group: "block",
+
+ code: false,
+
+ defining: true,
+
+ addAttributes() {
+ return {
+ align: {
+ parseHTML: (element) => {
+ if (element.hasAttribute("data-align")) {
+ return element.getAttribute("data-align");
+ } else {
+ return "center";
+ }
+ },
+ },
+ altText: {
+ parseHTML: (element) => {
+ const videoElem = element.querySelector("video");
+ if (videoElem) {
+ return videoElem.textContent;
+ } else {
+ return "";
+ }
+ },
+ },
+ figCaption: {
+ parseHTML: (element) => {
+ const figCaptionElem = element.querySelector("figcaption");
+ if (figCaptionElem) {
+ return figCaptionElem.innerHTML;
+ } else {
+ return "";
+ }
+ },
+ },
+ videoSrc: {
+ parseHTML: (element) => {
+ const videoElem = element.querySelector("video");
+ if (videoElem) {
+ return videoElem.src;
+ } else {
+ return "";
+ }
+ },
+ },
+ size: {
+ parseHTML: (element) => {
+ if (element.hasAttribute("data-size")) {
+ return element.getAttribute("data-size");
+ } else {
+ return "50";
+ }
+ },
+ },
+ };
+ },
+
+ addCommands() {
+ return {
+ setLibreCmsVideo:
+ (attributes) =>
+ ({ commands }) => {
+ return commands.setNode("libreCmsVideoNode", attributes);
+ },
+ };
+ },
+
+ addNodeView() {
+ return ({
+ editor,
+ node,
+ getPos,
+ HTMLAttributes,
+ decorations,
+ extension,
+ }) => {
+ const templateNode = document.querySelector(
+ "#librecms-video-node-view"
+ );
+ if (!templateNode) {
+ const errorMsg = document.createElement("div");
+ errorMsg.classList.add("alert alert-danger");
+ errorMsg.textContent = "Failed to create video node view.";
+ }
+
+ const dom = document.createElement("div");
+ dom.classList.add("librecms-video-node-view", "p-2");
+ if (!node.attrs.size) {
+ node.attrs.size = "50";
+ }
+ dom.classList.add(`librecms-video-node-width-${node.attrs.size}`);
+ if (!node.attrs.align) {
+ node.attrs.align = "center";
+ }
+ dom.classList.add(`librecms-video-node-align-${node.attrs.align}`);
+
+ const template = templateNode as HTMLTemplateElement;
+ const nodeView = template.content.cloneNode(true) as HTMLElement;
+ const videoElem = nodeView.querySelector("video");
+ const dialogIdNr = Math.floor(Math.random() * 1000000000);
+
+ const selectDialogId = `librecms-video-node-select-video-dialog-title-${dialogIdNr}`;
+ const selectDialogTitleId = `librecms-video-node-select-video-dialog-${dialogIdNr}-title`;
+ const selectButtonElem = nodeView.querySelector(
+ ".select-video-button"
+ );
+ if (selectButtonElem) {
+ selectButtonElem.setAttribute(
+ "data-target",
+ `#${selectDialogId}`
+ );
+ }
+ const selectDialogElem = nodeView.querySelector(
+ ".modal.select-video-dialog"
+ );
+ if (selectDialogElem) {
+ selectDialogElem.id = selectDialogId;
+ selectDialogElem.setAttribute(
+ "aria-labelledby",
+ selectDialogTitleId
+ );
+
+ const selectDialogTitleElem =
+ selectDialogElem.querySelector(".modal-title");
+ if (selectDialogTitleElem) {
+ selectDialogTitleElem.id = selectDialogTitleId;
+ }
+
+ const selectDialogIds =
+ selectDialogElem.querySelectorAll("*[id]");
+ selectDialogIds.forEach((elemWithId) => {
+ elemWithId.id = `${elemWithId.id}-${dialogIdNr}`;
+ });
+
+ const selectDialogLabels =
+ selectDialogElem.querySelectorAll("*[for]");
+ selectDialogLabels.forEach((label) => {
+ label.setAttribute(
+ "for",
+ `${label.getAttribute("for")}-${dialogIdNr}`
+ );
+ });
+
+ const describedElems = selectDialogElem.querySelectorAll(
+ "*[aria-describedby]"
+ );
+ describedElems.forEach((describedElem) => {
+ describedElem.setAttribute(
+ "aria-describedby",
+ `${describedElem.getAttribute(
+ "aria-describedby"
+ )}-${dialogIdNr}}`
+ );
+ });
+ }
+
+ const settingsDialogId = `librecms-video-node-view-settings-dialog-${dialogIdNr}`;
+ const settingsDialogTitleId = `${settingsDialogId}-title`;
+ const settingsButtonElem = nodeView.querySelector(
+ ".video-settings-button"
+ );
+ if (settingsButtonElem) {
+ settingsButtonElem.setAttribute(
+ "data-target",
+ `#${settingsDialogId}`
+ );
+ }
+ const settingsDialogElem = nodeView.querySelector(
+ ".modal.video-settings-dialog"
+ );
+ if (settingsDialogElem) {
+ settingsDialogElem.id = settingsDialogId;
+ settingsDialogElem.setAttribute(
+ "aria-labelledby",
+ settingsDialogTitleId
+ );
+
+ const settingsDialogTitleElem =
+ settingsDialogElem.querySelector(".modal-title");
+ if (settingsDialogTitleElem) {
+ settingsDialogTitleElem.id = settingsDialogTitleId;
+ }
+
+ const settingDialogIds =
+ settingsDialogElem.querySelectorAll("*[id]");
+ for (let i = 0; i < settingDialogIds.length; i++) {
+ const elemWithId = settingDialogIds.item(i);
+ elemWithId.id = `${elemWithId.id}-${dialogIdNr}`;
+ }
+ const settingDialogLabels =
+ settingsDialogElem.querySelectorAll("*[for]");
+ for (let i = 0; i < settingDialogLabels.length; i++) {
+ const label = settingDialogLabels.item(
+ i
+ ) as HTMLLabelElement;
+ label.setAttribute(
+ "for",
+ `${label.getAttribute("for")}-${dialogIdNr}`
+ );
+ }
+ const describedElems = settingsDialogElem.querySelectorAll(
+ "*[aria-describedby]"
+ );
+ for (let i = 0; i < describedElems.length; i++) {
+ const describedElem = describedElems.item(i);
+ describedElem.setAttribute(
+ "aria-describedby",
+ `${describedElem.getAttribute(
+ "aria-describedby"
+ )}-${dialogIdNr}`
+ );
+ }
+
+ const submitButton = settingsDialogElem.querySelector(
+ "button.video-settings-dialog-save"
+ );
+
+ const altTextInput = settingsDialogElem.querySelector(
+ `input#alttext-${dialogIdNr}`
+ );
+ const alignSelect = settingsDialogElem.querySelector(
+ `select#align-${dialogIdNr}`
+ );
+
+ const sizeSelect = settingsDialogElem.querySelector(
+ `select#size-${dialogIdNr}`
+ );
+
+ if (altTextInput) {
+ (altTextInput as HTMLInputElement).value =
+ node.attrs.altText;
+ } else {
+ console.warn("Input for alt text not found.");
+ }
+
+ if (alignSelect) {
+ const optionElems = alignSelect.querySelectorAll("option");
+ for (let i = 0; i < optionElems.length; i++) {
+ const optionElem = optionElems.item(
+ i
+ ) as HTMLOptionElement;
+ if (optionElem.value === node.attrs.align) {
+ optionElem.selected = true;
+ }
+ }
+ } else {
+ console.warn("Select for video alignment not found.");
+ }
+
+ if (sizeSelect) {
+ const optionElems = sizeSelect.querySelectorAll("option");
+ for (let i = 0; i < optionElems.length; i++) {
+ const optionElem = optionElems.item(
+ i
+ ) as HTMLOptionElement;
+ if (optionElem.value === node.attrs.size) {
+ optionElem.selected = true;
+ }
+ }
+ } else {
+ console.warn("Select for video size not found.");
+ }
+
+ if (submitButton) {
+ submitButton.addEventListener("click", (event) => {
+ const inputElem = altTextInput as HTMLInputElement;
+ if (altTextInput) {
+ node.attrs.altText = inputElem.value;
+ if (videoElem) {
+ videoElem.textContent = inputElem.value;
+ }
+ } else {
+ console.warn("Input for alt text not found.");
+ }
+
+ if (alignSelect) {
+ const selectElem = alignSelect as HTMLSelectElement;
+ node.attrs.align =
+ selectElem.selectedOptions.item(0)?.value;
+ ["floatleft", "center", "floatright"].forEach(
+ (align) =>
+ dom.classList.remove(
+ `librecms-video-node-align-${align}`
+ )
+ );
+ dom.classList.add(
+ `librecms-video-node-align-${node.attrs.align}`
+ );
+ } else {
+ console.warn(
+ "Select for video alignment not found."
+ );
+ }
+
+ if (sizeSelect) {
+ const selectElem = sizeSelect as HTMLSelectElement;
+ node.attrs.size =
+ selectElem.selectedOptions.item(0)?.value;
+ ["25", "33", "50", "66", "75", "100"].forEach(
+ (size) =>
+ dom.classList.remove(
+ `librecms-video-node-width-${size}`
+ )
+ );
+ dom.classList.add(
+ `librecms-video-node-width-${node.attrs.size}`
+ );
+ } else {
+ console.warn("Select for video size not found.");
+ }
+ });
+ } else {
+ console.warn(
+ "Submit button for video settings dialog not found."
+ );
+ }
+ }
+
+ const figCaptionElem = nodeView.querySelector("figcaption");
+ if (node.attrs.figCaption !== "" && figCaptionElem) {
+ figCaptionElem.innerHTML = node.attrs.figCaption;
+ }
+
+ dom.appendChild(nodeView);
+
+ if (selectButtonElem) {
+ if (videoElem) {
+ selectButtonElem.addEventListener("click", (event) =>
+ loadVideos(event, node, videoElem)
+ );
+ } else {
+ console.error("video elem not found.");
+ }
+ }
+
+ return {
+ dom,
+ };
+ };
+ },
+
+ parseHTML() {
+ return [
+ {
+ tag: "figure[data-type=librecms-video-node]",
+ },
+ ];
+ },
+
+ renderHTML({ node, HTMLAttributes }) {
+ return [
+ "figure",
+ mergeAttributes({
+ "data-align": node.attrs.align,
+ "data-size": node.attrs.size,
+ "data-type": "librecms-video-node",
+ }),
+ [
+ "video",
+ {
+ src: node.attrs.videoSrc,
+ textContent: node.attrs.altText,
+ },
+ ],
+ ["figcaption", [node.attrs.figCaption]],
+ ];
+ },
+});
+
+function loadVideos(
+ event: Event,
+ node: ProsemirrorNode,
+ videoElem: HTMLVideoElement
+) {
+ const eventTarget = event.currentTarget as HTMLElement;
+ const editorElem = document.querySelector(".cms-editor");
+ if (!editorElem) {
+ return;
+ }
+ const baseUrl = editorElem.getAttribute("data-baseUrl");
+ const contentSection = editorElem.getAttribute("data-contentsection");
+ const dialogId = eventTarget.getAttribute("data-target");
+ if (!dialogId) {
+ console.error("data-target attribute is missing.");
+ return;
+ }
+ const dialog = document.querySelector(dialogId);
+ if (!dialog) {
+ console.error("dialog element not found is missing.");
+ return;
+ }
+ const rowTemplateResult = document.querySelector(
+ "#librecms-video-node-view-row"
+ );
+ if (!rowTemplateResult) {
+ console.error("template for result row not found.");
+ return;
+ }
+ const rowTemplate = rowTemplateResult as HTMLTemplateElement;
+ const table = dialog.querySelector("table");
+ if (!table) {
+ console.error("result table not found.");
+ return;
+ }
+ const tableBody = table.querySelector("tbody");
+ if (!tableBody) {
+ console.error("table body not found.");
+ return;
+ }
+ tableBody.innerHTML = "";
+ const fetchUrl = `/content-sections/${contentSection}/assets?type=org.librecms.assets.VideoAsset,org.librecms.assets.ExternalVideoAsset`;
+ fetch(fetchUrl)
+ .then((response) => {
+ if (response.ok) {
+ response
+ .json()
+ .then((data) => {
+ const videos = data as [];
+ for (const video of videos) {
+ const row = rowTemplate.content.cloneNode(
+ true
+ ) as Element;
+ const colName = row.querySelector(".col-name");
+ const colType = row.querySelector(".col-type");
+ const selectButton =
+ row.querySelector(".col-action button");
+
+ if (colName) {
+ colName.textContent = video["name"];
+ }
+ if (colType) {
+ colType.textContent = video["type"];
+ }
+
+ if (selectButton) {
+ selectButton.setAttribute(
+ "data-videouuid",
+ video["uuid"]
+ );
+
+ selectButton.addEventListener(
+ "click",
+ (event) => {
+ const videoUrl = `/content-sections/info/videos/uuid-${video["uuid"]}`;
+ node.attrs.videoSrc = videoUrl;
+ if (videoElem) {
+ videoElem.src = videoUrl;
+ } else {
+ console.error(
+ "video element not found."
+ );
+ }
+ }
+ );
+ }
+
+ tableBody.appendChild(row);
+ }
+ })
+ .catch((error) => {
+ console.error(error);
+ });
+ } else {
+ console.error(
+ `Error. Status: ${response.status}. Status Text: ${response.statusText}`
+ );
+ }
+ })
+ .catch((error) => {
+ console.error(error);
+ });
+}