AutorÃa | Ultima modificación | Ver Log |
{"version":3,"file":"embedpreview.min.js","sources":["../../src/embed/embedpreview.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Tiny media plugin embed preview and details class.\n *\n * This handles the embed file/url preview before embedding them into tiny editor.\n *\n * @module tiny_media/embed/embedpreview\n * @copyright 20
24 Stevani Andolo <stevani@hotmail.com.au>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Selectors from '../selectors';\nimport {component} from '../common';\nimport {getString} from 'core/str';\nimport {\n sourceTypeChecked,\n getFileName,\n setPropertiesFromData,\n showElements,\n stopMediaLoading,\n hideElements,\n} from '../helpers';\nimport {EmbedHandler} from './embedhandler';\nimport {MediaBase} from '../mediabase';\nimport Notification from 'core/notification';\nimport EmbedModal from '../embedmodal';\nimport {\n getEmbeddedMediaDetails,\n insertMediaThumbnailTemplateContext,\n fetchPreview,\n} from './embedhelpers';\nimport {notifyFilterContentUpdated} from 'core_filters/events';\n\nexport class EmbedPreview extends MediaBase {\n\n // Selector type for \"EMBED\".\n selectorType = Selectors.EMBED.type;\n\n // Fixed aspect ratio used for external media providers.\n linkMediaAspectRatio = 1.78;\n\n constructor(data) {\
n super();\n setPropertiesFromData(this, data); // Creates dynamic properties based on \"data\" param.\n }\n\n /**\n * Init the media details preview.\n */\n init = async() => {\n this.currentModal.setTitle(getString('mediadetails', component));\n sourceTypeChecked({\n fetchedTitle: this.fetchedMediaLinkTitle ?? null,\n source: this.originalUrl,\n root: this.root,\n urlSelector: Selectors.EMBED.elements.fromUrl,\n fileNameSelector: Selectors.EMBED.elements.fileNameLabel,\n });\n this.setMediaSourceAndPoster();\n this.registerMediaDetailsEventListeners(this.currentModal);\n };\n\n /**\n * Sets media source and thumbnail for the video.\n */\n setMediaSourceAndPoster = async() => {\n const box = this.root.querySelector(Selectors.EMBED.elements.previewBox);\n const previewArea = document.querySelector(Selectors.EMBED.elements.mediaPreviewContainer);\n prev
iewArea.setAttribute('data-original-url', this.originalUrl);\n\n // Previewing existing media could be a link one.\n // Or, new media added using url input and mediaType is neither video or audio.\n if (this.mediaType === 'link' || (this.newMediaLink && !['video', 'audio'].includes(this.mediaType))) {\n previewArea.setAttribute('data-media-type', 'link');\n previewArea.innerHTML = await fetchPreview(this.originalUrl, this.contextId);\n notifyFilterContentUpdated(previewArea);\n } else if (this.mediaType === 'video') {\n const video = document.createElement('video');\n video.src = this.originalUrl;\n\n // Media url can be played using html video.\n video.addEventListener('loadedmetadata', () => {\n const videoHeight = video.videoHeight;\n const videoWidth = video.videoWidth;\n const widthProportion = (videoWidth - videoHeight);\n const isLandscape
= widthProportion > 0;\n\n // Store dimensions of the raw video.\n this.mediaDimensions = {\n width: videoWidth,\n height: videoHeight,\n };\n\n // Set the media preview based on the media dimensions.\n if (isLandscape) {\n video.width = box.offsetWidth;\n } else {\n video.height = box.offsetHeight;\n }\n\n const height = this.root.querySelector(Selectors.EMBED.elements.height);\n const width = this.root.querySelector(Selectors.EMBED.elements.width);\n\n if (height.value === '' && width.value === '') {\n height.value = videoHeight;\n width.value = videoWidth;\n }\n\n // Size checking and adjustment.\n if (videoHeight === parseInt(height.value) && videoWidth === parseInt(width.value)) {\n
this.currentWidth = this.mediaDimensions.width;\n this.currentHeight = this.mediaDimensions.height;\n this.sizeChecked('original');\n } else {\n this.currentWidth = parseInt(width.value);\n this.currentHeight = parseInt(height.value);\n this.sizeChecked('custom');\n }\n });\n\n video.controls = true;\n if (this.media.poster) {\n previewArea.setAttribute('data-media-poster', this.media.poster);\n if (!video.classList.contains('w-100')) {\n video.classList.add('w-100');\n }\n video.poster = this.media.poster;\n }\n video.load();\n\n previewArea.setAttribute('data-media-type', 'video');\n previewArea.innerHTML = video.outerHTML;\n notifyFilterContentUpdated(previewArea);\n } else if (this.mediaType === 'audio') {\n
const audio = document.createElement('audio');\n audio.src = this.originalUrl;\n audio.controls = true;\n audio.load();\n\n previewArea.setAttribute('data-media-type', 'audio');\n previewArea.innerHTML = audio.outerHTML;\n notifyFilterContentUpdated(previewArea);\n } else {\n // Show warning notification.\n const urlWarningLabelEle = this.root.querySelector(Selectors.EMBED.elements.urlWarning);\n urlWarningLabelEle.innerHTML = await getString('medianotavailabledesc', component, this.originalUrl);\n showElements(Selectors.EMBED.elements.urlWarning, this.root);\n\n // Stop the spinner.\n stopMediaLoading(this.root, Selectors.EMBED.type);\n\n // Reset the upload form.\n (new EmbedHandler(this)).resetUploadForm();\n return;\n }\n\n // Stop the loader and display back the body template when the media is loaded.\n
stopMediaLoading(this.root, Selectors.EMBED.type);\n showElements(Selectors.EMBED.elements.mediaDetailsBody, this.root);\n\n // Set the media name/title.\n this.root.querySelector(Selectors.EMBED.elements.title).value = this.setMediaTitle();\n };\n\n /**\n * Set media name/title.\n *\n * @returns {string}\n */\n setMediaTitle = () => {\n // Getting and setting up media title/name.\n let fileName = null;\n if (['video', 'audio'].includes(this.mediaType)) {\n fileName = getFileName(this.originalUrl); // Get original filename.\n } else if (this.fetchedMediaLinkTitle) {\n fileName = this.fetchedMediaLinkTitle;\n } else {\n fileName = this.originalUrl;\n }\n\n if (this.isUpdating) {\n if (!this.newMediaLink) {\n fileName = this.mediaTitle; // Title from the selected media.\n }\n }\n\n return fileName;\n };\n\n /**\n * Deletes
the media after confirming with the user and loads the insert media page.\n */\n deleteMedia = () => {\n Notification.deleteCancelPromise(\n getString('deletemedia', component),\n getString('deletemediawarning', component),\n ).then(() => {\n // Reset media upload form.\n (new EmbedHandler(this)).resetUploadForm();\n\n // Delete any selected media mediaData.\n delete this.mediaData;\n return;\n }).catch(error => {\n window.console.log(error);\n });\n };\n\n /**\n * Delete embedded media thumbnail.\n */\n deleteEmbeddedThumbnail = () => {\n Notification.deleteCancelPromise(\n getString('deleteembeddedthumbnail', component),\n getString('deleteembeddedthumbnailwarning', component),\n ).then(async() => {\n if (this.mediaType === 'video') {\n const video = this.root.querySelector('video');\n if (vi
deo) {\n video.removeAttribute('poster');\n const preview = this.root.querySelector(Selectors.EMBED.elements.mediaPreviewContainer);\n preview.removeAttribute('data-media-poster');\n }\n }\n\n const deleteCustomThumbnail = this.root.querySelector(Selectors.EMBED.actions.deleteCustomThumbnail);\n deleteCustomThumbnail.remove();\n\n const uploadCustomThumbnail = this.root.querySelector(Selectors.EMBED.actions.uploadCustomThumbnail);\n uploadCustomThumbnail.textContent = await getString('uploadthumbnail', component);\n return;\n }).catch(error => {\n window.console.log(error);\n });\n };\n\n /**\n * Shows the insert thumbnail dialogue.\n */\n showUploadThumbnail = async() => {\n const uploadThumbnailModal = await EmbedModal.create({\n large: true,\n templateContext: {elementid: this.editor.getElement().id}
,\n });\n const root = uploadThumbnailModal.getRoot()[0];\n\n // Get selected media metadata.\n const mediaData = getEmbeddedMediaDetails(this);\n mediaData.isUpdating = this.isUpdating;\n\n const embedHandler = new EmbedHandler(this);\n embedHandler.loadInsertThumbnailTemplatePromise(\n insertMediaThumbnailTemplateContext(this), // Get template context for creating media thumbnail.\n {root, uploadThumbnailModal}, // Required root elements.\n await embedHandler.getMediaTemplateContext(mediaData) // Get current media data.\n );\n };\n\n /**\n * Only registers event listeners for new loaded elements in embed preview modal.\n */\n registerMediaDetailsEventListeners = async() => {\n // Handle the original size when selected.\n const sizeOriginalEle = this.root.querySelector(Selectors.EMBED.elements.sizeOriginal);\n if (sizeOriginalEle) {\n sizeOriginalEle.addEventListener('cha
nge', () => {\n this.sizeChecked('original');\n });\n }\n\n // Handle the custom size when selected.\n const sizeCustomEle = this.root.querySelector(Selectors.EMBED.elements.sizeCustom);\n if (sizeCustomEle) {\n sizeCustomEle.addEventListener('change', () => {\n this.sizeChecked('custom');\n });\n }\n\n const widthEle = this.root.querySelector(Selectors.EMBED.elements.width);\n const heightEle = this.root.querySelector(Selectors.EMBED.elements.height);\n\n // Handle the custom with size when inputted.\n if (widthEle) {\n widthEle.addEventListener('input', () => {\n if (this.mediaType === 'link') {\n // Let's apply the 16:9 aspect ratio if it's a link media type.\n heightEle.value = Math.round(widthEle.value / this.linkMediaAspectRatio);\n } else {\n // Avoid empty value.\n
widthEle.value = widthEle.value === \"\" ? 0 : Number(widthEle.value);\n this.autoAdjustSize();\n }\n });\n }\n\n // Handle the custom height size when inputted.\n if (heightEle) {\n heightEle.addEventListener('input', () => {\n if (this.mediaType === 'link') {\n // Let's apply the 16:9 aspect ratio if it's a link media type.\n widthEle.value = Math.round(heightEle.value * this.linkMediaAspectRatio);\n } else {\n // Avoid empty value.\n heightEle.value = heightEle.value === \"\" ? 0 : Number(heightEle.value);\n this.autoAdjustSize(true);\n }\n });\n }\n\n // Handle media preview delete.\n const deleteMedia = this.root.querySelector(Selectors.EMBED.actions.deleteMedia);\n if (deleteMedia) {\n deleteMedia.addEventListener('click', (e) => {\n
e.preventDefault();\n this.deleteMedia();\n });\n }\n\n // Show subtitles and captions settings.\n const showSubtitleCaption = this.root.querySelector(Selectors.EMBED.actions.showSubtitleCaption);\n if (showSubtitleCaption) {\n showSubtitleCaption.addEventListener('click', (e) => {\n e.preventDefault();\n hideElements([\n Selectors.EMBED.actions.showSubtitleCaption,\n Selectors.EMBED.actions.cancelMediaDetails,\n Selectors.EMBED.elements.mediaDetailsBody,\n ], this.root);\n showElements([\n Selectors.EMBED.actions.backToMediaDetails,\n Selectors.EMBED.elements.mediaSubtitleCaptionBody,\n ], this.root);\n });\n }\n\n // Back to media preview.\n const backToMediaDetails = this.root.querySelector(Selectors.EMBED.actions.backToMediaDetai
ls);\n if (backToMediaDetails) {\n backToMediaDetails.addEventListener('click', () => {\n hideElements([\n Selectors.EMBED.actions.backToMediaDetails,\n Selectors.EMBED.elements.mediaSubtitleCaptionBody,\n ], this.root);\n showElements([\n Selectors.EMBED.actions.showSubtitleCaption,\n Selectors.EMBED.actions.cancelMediaDetails,\n Selectors.EMBED.elements.mediaDetailsBody,\n ], this.root);\n });\n }\n\n // Handles upload media thumbnail.\n const uploadCustomThumbnail = this.root.querySelector(Selectors.EMBED.actions.uploadCustomThumbnail);\n if (uploadCustomThumbnail) {\n uploadCustomThumbnail.addEventListener('click', () => {\n this.showUploadThumbnail();\n });\n }\n\n // Handles delete media thumbnail.\n const deleteCustomThumbnail = this.ro
ot.querySelector(Selectors.EMBED.actions.deleteCustomThumbnail);\n if (deleteCustomThumbnail) {\n deleteCustomThumbnail.addEventListener('click', () => {\n this.deleteEmbeddedThumbnail();\n });\n }\n\n // Handles language track selection.\n const langTracks = this.root.querySelectorAll(Selectors.EMBED.elements.trackLang);\n if (langTracks) {\n langTracks.forEach((dropdown) => {\n const defaultVal = dropdown.getAttribute('data-value');\n if (defaultVal) {\n Array.from(dropdown.options).some(option => {\n // Check if srclang in track is a language code like \"en\"\n // or language name like \"English\" prior to MDL-85159.\n if (option.dataset.languageCode === defaultVal || option.value === defaultVal) {\n option.selected = true;\n return true;\n
}\n return false;\n });\n }\n });\n }\n };\n}\n"],"names":["EmbedPreview","MediaBase","constructor","data","Selectors","EMBED","type","async","currentModal","setTitle","component","fetchedTitle","this","fetchedMediaLinkTitle","source","originalUrl","root","urlSelector","elements","fromUrl","fileNameSelector","fileNameLabel","setMediaSourceAndPoster","registerMediaDetailsEventListeners","box","querySelector","previewBox","previewArea","document","mediaPreviewContainer","setAttribute","mediaType","newMediaLink","includes","innerHTML","contextId","video","createElement","src","addEventListener","videoHeight","videoWidth","isLandscape","mediaDimensions","width","height","offsetWidth","offsetHeight","value","parseInt","currentWidth","currentHeight","sizeChecked","controls","media","poster","classList","contains","add","load","outerHTML","urlWarning","EmbedHandler","resetUploadForm","audio","mediaDetailsBody","title","setMediaTi
tle","fileName","isUpdating","mediaTitle","deleteCancelPromise","then","mediaData","catch","error","window","console","log","removeAttribute","actions","deleteCustomThumbnail","remove","uploadCustomThumbnail","textContent","uploadThumbnailModal","EmbedModal","create","large","templateContext","elementid","editor","getElement","id","getRoot","embedHandler","loadInsertThumbnailTemplatePromise","getMediaTemplateContext","sizeOriginalEle","sizeOriginal","sizeCustomEle","sizeCustom","widthEle","heightEle","Math","round","linkMediaAspectRatio","Number","autoAdjustSize","deleteMedia","e","preventDefault","showSubtitleCaption","cancelMediaDetails","backToMediaDetails","mediaSubtitleCaptionBody","showUploadThumbnail","deleteEmbeddedThumbnail","langTracks","querySelectorAll","trackLang","forEach","dropdown","defaultVal","getAttribute","Array","from","options","some","option","dataset","languageCode","selected"],"mappings":"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"}