diff --git a/ccm-cms/src/main/resources/META-INF/resources/components/librecms/cmsEditor.xhtml b/ccm-cms/src/main/resources/META-INF/resources/components/librecms/cmsEditor.xhtml index 3a0acb30a..33c701d78 100644 --- a/ccm-cms/src/main/resources/META-INF/resources/components/librecms/cmsEditor.xhtml +++ b/ccm-cms/src/main/resources/META-INF/resources/components/librecms/cmsEditor.xhtml @@ -664,9 +664,20 @@ class="btn btn-outline-dark tiptap-insert-image" title="#{CmsAdminMessages['cms_editor.buttons.media.insert_image']}" type="button"> - + + + + + + + + +
{ + 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); + }); +}