diff --git a/lib/editor/tiny/plugins/media/amd/build/embed/embedhandler.min.js b/lib/editor/tiny/plugins/media/amd/build/embed/embedhandler.min.js index 6ae05e9d96b..17761bd3cb9 100644 --- a/lib/editor/tiny/plugins/media/amd/build/embed/embedhandler.min.js +++ b/lib/editor/tiny/plugins/media/amd/build/embed/embedhandler.min.js @@ -1,3 +1,3 @@ -define("tiny_media/embed/embedhandler",["exports","../selectors","./embedinsert","../helpers","core/modal_events","editor_tiny/utils","./embedhelpers","core/templates","./embedthumbnailinsert"],(function(_exports,_selectors,_embedinsert,_helpers,ModalEvents,_utils,_embedhelpers,_templates,_embedthumbnailinsert){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.EmbedHandler=void 0,_selectors=_interopRequireDefault(_selectors),ModalEvents=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(ModalEvents),_templates=_interopRequireDefault(_templates);_exports.EmbedHandler=class{constructor(_data){var _this=this;_defineProperty(this,"loadTemplatePromise",(templateContext=>{templateContext.elementid=this.editor.id,templateContext.bodyTemplate=_selectors.default.EMBED.template.body.insertMediaBody,templateContext.footerTemplate=_selectors.default.EMBED.template.footer.insertMediaFooter,templateContext.selector=_selectors.default.EMBED.type,Promise.all([(0,_helpers.body)(templateContext,this.root),(0,_helpers.footer)(templateContext,this.root)]).then((()=>{new _embedinsert.EmbedInsert(this).init()})).catch((error=>{window.console.log(error)}))})),_defineProperty(this,"loadInsertThumbnailTemplatePromise",(async(templateContext,root,mediaData)=>{Promise.all([(0,_helpers.body)(templateContext,root.root),(0,_helpers.footer)(templateContext,root.root)]).then((()=>{this.currentModal.insertMediaModal||(this.currentModal.insertMediaModal=this.currentModal),root.uploadThumbnailModal&&(this.currentModal.uploadThumbnailModal=root.uploadThumbnailModal),this.thumbnailModalRoot=root.root,new _embedthumbnailinsert.EmbedThumbnailInsert(this).init(mediaData)})).catch((error=>{window.console.log(error)}))})),_defineProperty(this,"loadMediaDetails",(async(embedPreview,templateContext)=>{Promise.all([(0,_helpers.body)(templateContext,this.root),(0,_helpers.footer)(templateContext,this.root)]).then((()=>{embedPreview.init()})).catch((error=>{(0,_helpers.stopMediaLoading)(this.root,_selectors.default.EMBED.type),window.console.log(error)}))})),_defineProperty(this,"resetUploadForm",(function(){let isMediaInsert=!(arguments.length>0&&void 0!==arguments[0])||arguments[0];isMediaInsert?(_this.newMediaLink=!1,_this.fetchedMediaLinkTitle=null,_this.resetCurrentMediaData(),_this.loadTemplatePromise((0,_embedhelpers.insertMediaTemplateContext)(_this))):_this.loadInsertThumbnailTemplatePromise((0,_embedhelpers.insertMediaThumbnailTemplateContext)(_this),{root:_this.thumbnailModalRoot},_this.mediaData)})),_defineProperty(this,"getMediumProperties",(()=>{var _medium$getAttribute;const medium=this.selectedMedia;if(!medium)return null;const tracks={subtitles:[],captions:[],descriptions:[],chapters:[],metadata:[]},sources=[];medium.querySelectorAll("track").forEach((track=>{tracks[track.getAttribute("kind")].push({src:track.getAttribute("src"),srclang:track.getAttribute("srclang"),label:track.getAttribute("label"),defaultTrack:(0,_embedhelpers.hasAudioVideoAttr)(track,"default")})})),medium.querySelectorAll("source").forEach((source=>{sources.push(source.src)}));const title=null!==(_medium$getAttribute=medium.getAttribute("title"))&&void 0!==_medium$getAttribute?_medium$getAttribute:medium.textContent;return{type:this.mediaType,sources:sources,poster:medium.getAttribute("poster"),title:!!title&&title.trim(),width:medium.getAttribute("width"),height:medium.getAttribute("height"),autoplay:(0,_embedhelpers.hasAudioVideoAttr)(medium,"autoplay"),loop:(0,_embedhelpers.hasAudioVideoAttr)(medium,"loop"),muted:(0,_embedhelpers.hasAudioVideoAttr)(medium,"muted"),controls:(0,_embedhelpers.hasAudioVideoAttr)(medium,"controls"),tracks:tracks}})),_defineProperty(this,"getCurrentEmbedData",(()=>{const properties=this.getMediumProperties();if(!properties||this.newMediaLink)return{media:{}};const processedProperties={};return processedProperties.media=properties,processedProperties.link=!1,processedProperties})),_defineProperty(this,"getHelpStrings",(async()=>(this.helpStrings||(this.helpStrings=await(0,_embedhelpers.getHelpStrings)()),this.helpStrings))),_defineProperty(this,"getTemplateContext",(async data=>{const languages=(0,_embedhelpers.prepareMoodleLang)(this.editor),helpIcons=Array.from(Object.entries(await this.getHelpStrings())).forEach((_ref=>{let[key,text]=_ref;data["".concat(key.toLowerCase(),"helpicon")]={text:text}}));return Object.assign({},{elementid:this.editor.getElement().id,showFilePickerTrack:this.canShowFilePickerTrack,langsInstalled:languages.installed,langsAvailable:languages.available,media:!0,isUpdating:this.isUpdating},data,helpIcons)})),_defineProperty(this,"getMediaTemplateContext",(async function(){let data=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null;return data?data.hasOwnProperty("isUpdating")?_this.isUpdating=data.isUpdating:_this.isUpdating=Object.keys(data).length>1:data=Object.assign({},_this.getCurrentEmbedData()),await _this.getTemplateContext(data)})),_defineProperty(this,"loadMediaPreview",(url=>{new _embedinsert.EmbedInsert(this).loadMediaPreview(url)})),_defineProperty(this,"resetCurrentMediaData",(()=>{this.media={},this.mediaType=null,this.selectedMedia=null})),_defineProperty(this,"getFilePickerTypeFromElement",(element=>element.closest(_selectors.default.EMBED.elements.posterSource)?"image":element.closest(_selectors.default.EMBED.elements.trackSource)?"subtitle":"media")),_defineProperty(this,"getTrackTypeFromTabPane",(tabPane=>tabPane.getAttribute("data-track-kind"))),_defineProperty(this,"clickHandler",(async e=>{const element=e.target;if(element.closest(_selectors.default.EMBED.actions.mediaBrowser)){e.preventDefault();const fpType=this.getFilePickerTypeFromElement(element),params=await(0,_utils.displayFilepicker)(this.editor,fpType);this.trackFilePickerCallback(params,element,fpType)}e.target.closest(_selectors.default.EMBED.actions.addUrl)&&((0,_helpers.startMediaLoading)(this.root,_selectors.default.EMBED.type),this.urlChanged());element.closest(_selectors.default.EMBED.elements.track+" .addcomponent")&&(e.preventDefault(),this.addTrackComponent(element));element.closest(_selectors.default.EMBED.elements.track+" .removecomponent")&&(e.preventDefault(),this.removeTrackComponent(element));const trackDefaultAction=element.closest(_selectors.default.EMBED.elements.trackDefault);if(trackDefaultAction&&trackDefaultAction.checked){const getKind=el=>this.getTrackTypeFromTabPane(el.parentElement.closest(".tab-pane"));element.parentElement.closest(".tab-content").querySelectorAll(_selectors.default.EMBED.elements.trackDefault).forEach((select=>{select!==element&&getKind(element)===getKind(select)&&(select.checked=!1)}))}})),_defineProperty(this,"getMediaHTML",(form=>{this.mediaType=this.root.querySelector(_selectors.default.EMBED.elements.mediaPreviewContainer).dataset.mediaType;const tabContent=form.querySelector(".tab-content");return this["getMediaHTML"+this.mediaType[0].toUpperCase()+this.mediaType.substr(1)](tabContent)})),_defineProperty(this,"getMediaHTMLVideo",(tab=>{const details=document.querySelector(_selectors.default.EMBED.elements.mediaDetailsBody),context=this.getContextForMediaHTML(tab,details);context.width=details.querySelector(_selectors.default.EMBED.elements.width).value||!1,context.height=details.querySelector(_selectors.default.EMBED.elements.height).value||!1;const mediaPreviewContainer=details.querySelector(_selectors.default.EMBED.elements.mediaPreviewContainer);return context.poster=mediaPreviewContainer.dataset.mediaPoster||!1,context.sources?_templates.default.renderForPromise("tiny_media/embed/embed_media_video",context):""})),_defineProperty(this,"getMediaHTMLAudio",(tab=>{const details=document.querySelector(_selectors.default.EMBED.elements.mediaDetailsBody),context=this.getContextForMediaHTML(tab,details);return context.sources.length?_templates.default.renderForPromise("tiny_media/embed/embed_media_audio",context):""})),_defineProperty(this,"getContextForMediaHTML",((tab,details)=>{var _mediaPreviewContaine;const tracks=Array.from(tab.querySelectorAll(_selectors.default.EMBED.elements.track)).map((track=>({track:track.querySelector(_selectors.default.EMBED.elements.trackSource+" "+_selectors.default.EMBED.elements.url).value,kind:this.getTrackTypeFromTabPane(track.closest(".tab-pane")),label:track.querySelector(_selectors.default.EMBED.elements.trackLabel).value||track.querySelector(_selectors.default.EMBED.elements.trackLang).value,srclang:track.querySelector(_selectors.default.EMBED.elements.trackLang).value,defaultTrack:track.querySelector(_selectors.default.EMBED.elements.trackDefault).checked?"true":null}))).filter((track=>!!track.track)),mediaPreviewContainer=details.querySelector(_selectors.default.EMBED.elements.mediaPreviewContainer);let sources=null!==(_mediaPreviewContaine=mediaPreviewContainer.dataset.originalUrl)&&void 0!==_mediaPreviewContaine?_mediaPreviewContaine:null;this.alternativeSources&&(this.alternativeSources[0]=sources,sources=this.alternativeSources);const title=details.querySelector(_selectors.default.EMBED.elements.title).value;mediaPreviewContainer.removeAttribute("data-original-url");const templateContext={sources:sources,tracks:tracks,showControls:details.querySelector(_selectors.default.EMBED.elements.mediaControl).checked,autoplay:details.querySelector(_selectors.default.EMBED.elements.mediaAutoplay).checked,muted:details.querySelector(_selectors.default.EMBED.elements.mediaMute).checked,loop:details.querySelector(_selectors.default.EMBED.elements.mediaLoop).checked,title:""!==title&&title.trim()};return"link"===this.mediaType&&(templateContext.description=Array.isArray(sources)?sources[0]:sources),templateContext})),_defineProperty(this,"handleDialogueSubmission",(async(event,modal)=>{const{html:html}=await this.getMediaHTML(modal.getRoot()[0]);html&&(this.isUpdating?(this.selectedMedia.outerHTML=html,this.isUpdating=!1):this.editor.insertContent(html))})),_defineProperty(this,"registerEventListeners",(async()=>{this.canShowFilePickerTrack&&this.root.addEventListener("click",this.clickHandler.bind(this)),this.root.addEventListener("input",(e=>{const urlEle=e.target.closest(_selectors.default.EMBED.elements.fromUrl);urlEle&&this.toggleUrlButton(urlEle,this.root)})),this.modalRoot.on(ModalEvents.hidden,(()=>{this.currentModal.destroy()})),this.modalRoot.on(ModalEvents.save,this.handleDialogueSubmission.bind(this))})),(0,_helpers.setPropertiesFromData)(this,_data)}urlChanged(){const url=this.root.querySelector(_selectors.default.EMBED.elements.fromUrl).value;url&&url!==this.currentUrl&&(this.mediaType=null,this.newMediaLink=!0,this.loadMediaPreview(url))}trackFilePickerCallback(params,element,fpType){if(""!==params.url){if(element.closest(".tab-pane")){if(element.closest(_selectors.default.EMBED.elements.source).querySelector(_selectors.default.EMBED.elements.url).value=params.url,"subtitle"===fpType){const subtitleLang=params.file.split(".vtt")[0].split("-").slice(-1)[0],langObj=(0,_embedhelpers.getMoodleLangObj)(subtitleLang,this.editor);if(langObj){const track=element.closest(_selectors.default.EMBED.elements.track);track.querySelector(_selectors.default.EMBED.elements.trackLabel).value=langObj.lang.trim(),track.querySelector(_selectors.default.EMBED.elements.trackLang).value=langObj.code}}}else this.newFileUpload=!0,this.resetCurrentMediaData(),this.loadMediaPreview(params.url)}}addTrackComponent(element){const trackElement=element.closest(_selectors.default.EMBED.elements.track),clone=trackElement.cloneNode(!0);trackElement.querySelector(".removecomponent-wrapper").classList.remove("hidden"),trackElement.querySelector(".addcomponent-wrapper").classList.add("hidden"),trackElement.parentNode.insertBefore(clone,trackElement.nextSibling)}removeTrackComponent(element){element.closest(_selectors.default.EMBED.elements.track).remove()}toggleUrlButton(input,root){const url=input.value;root.querySelector(_selectors.default.EMBED.actions.addUrl).disabled=!(""!==url&&(0,_helpers.isValidUrl)(url))}getMediaHTMLLink(){var _document$querySelect;const mediaPreviewContainer=document.querySelector(_selectors.default.EMBED.elements.mediaPreviewContainer),context={name:null!==(_document$querySelect=document.querySelector(_selectors.default.EMBED.elements.title).value)&&void 0!==_document$querySelect?_document$querySelect:mediaPreviewContainer.dataset.originalUrl,url:mediaPreviewContainer.dataset.originalUrl||!1};return context.url?_templates.default.renderForPromise("tiny_media/embed/embed_media_link",context):""}}})); +define("tiny_media/embed/embedhandler",["exports","../selectors","./embedinsert","../helpers","core/modal_events","editor_tiny/utils","./embedhelpers","core/templates","./embedthumbnailinsert"],(function(_exports,_selectors,_embedinsert,_helpers,ModalEvents,_utils,_embedhelpers,_templates,_embedthumbnailinsert){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.EmbedHandler=void 0,_selectors=_interopRequireDefault(_selectors),ModalEvents=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(ModalEvents),_templates=_interopRequireDefault(_templates);_exports.EmbedHandler=class{constructor(_data){var _this=this;_defineProperty(this,"loadTemplatePromise",(templateContext=>{templateContext.elementid=this.editor.id,templateContext.bodyTemplate=_selectors.default.EMBED.template.body.insertMediaBody,templateContext.footerTemplate=_selectors.default.EMBED.template.footer.insertMediaFooter,templateContext.selector=_selectors.default.EMBED.type,Promise.all([(0,_helpers.body)(templateContext,this.root),(0,_helpers.footer)(templateContext,this.root)]).then((()=>{new _embedinsert.EmbedInsert(this).init()})).catch((error=>{window.console.log(error)}))})),_defineProperty(this,"loadInsertThumbnailTemplatePromise",(async(templateContext,root,mediaData)=>{Promise.all([(0,_helpers.body)(templateContext,root.root),(0,_helpers.footer)(templateContext,root.root)]).then((()=>{this.currentModal.insertMediaModal||(this.currentModal.insertMediaModal=this.currentModal),root.uploadThumbnailModal&&(this.currentModal.uploadThumbnailModal=root.uploadThumbnailModal),this.thumbnailModalRoot=root.root,new _embedthumbnailinsert.EmbedThumbnailInsert(this).init(mediaData)})).catch((error=>{window.console.log(error)}))})),_defineProperty(this,"loadMediaDetails",(async(embedPreview,templateContext)=>{Promise.all([(0,_helpers.body)(templateContext,this.root),(0,_helpers.footer)(templateContext,this.root)]).then((()=>{embedPreview.init()})).catch((error=>{(0,_helpers.stopMediaLoading)(this.root,_selectors.default.EMBED.type),window.console.log(error)}))})),_defineProperty(this,"resetUploadForm",(function(){let isMediaInsert=!(arguments.length>0&&void 0!==arguments[0])||arguments[0];isMediaInsert?(_this.newMediaLink=!1,_this.fetchedMediaLinkTitle=null,_this.resetCurrentMediaData(),_this.loadTemplatePromise((0,_embedhelpers.insertMediaTemplateContext)(_this))):_this.loadInsertThumbnailTemplatePromise((0,_embedhelpers.insertMediaThumbnailTemplateContext)(_this),{root:_this.thumbnailModalRoot},_this.mediaData)})),_defineProperty(this,"getMediaProperties",(()=>{var _media$getAttribute;const media=this.selectedMedia;if(!media)return null;const tracks={subtitles:[],captions:[],descriptions:[],chapters:[],metadata:[]},sources=[];media.querySelectorAll("track").forEach((track=>{tracks[track.getAttribute("kind")].push({src:track.getAttribute("src"),srclang:track.getAttribute("srclang"),label:track.getAttribute("label"),defaultTrack:(0,_embedhelpers.hasAudioVideoAttr)(track,"default")})})),media.querySelectorAll("source").forEach((source=>{sources.push(source.src)}));const title=null!==(_media$getAttribute=media.getAttribute("title"))&&void 0!==_media$getAttribute?_media$getAttribute:media.textContent;return{type:this.mediaType,sources:sources,poster:media.getAttribute("poster"),title:!!title&&title.trim(),width:media.getAttribute("width"),height:media.getAttribute("height"),autoplay:(0,_embedhelpers.hasAudioVideoAttr)(media,"autoplay"),loop:(0,_embedhelpers.hasAudioVideoAttr)(media,"loop"),muted:(0,_embedhelpers.hasAudioVideoAttr)(media,"muted"),controls:(0,_embedhelpers.hasAudioVideoAttr)(media,"controls"),tracks:tracks}})),_defineProperty(this,"getCurrentEmbedData",(()=>{const properties=this.getMediaProperties();if(!properties||this.newMediaLink)return{media:{}};const processedProperties={};return processedProperties.media=properties,processedProperties.link=!1,processedProperties})),_defineProperty(this,"getHelpStrings",(async()=>(this.helpStrings||(this.helpStrings=await(0,_embedhelpers.getHelpStrings)()),this.helpStrings))),_defineProperty(this,"getTemplateContext",(async data=>{const languages=(0,_embedhelpers.prepareMoodleLang)(this.editor),helpIcons=Array.from(Object.entries(await this.getHelpStrings())).forEach((_ref=>{let[key,text]=_ref;data["".concat(key.toLowerCase(),"helpicon")]={text:text}}));return Object.assign({},{elementid:this.editor.getElement().id,showFilePickerTrack:this.canShowFilePickerTrack,langsInstalled:languages.installed,langsAvailable:languages.available,media:!0,isUpdating:this.isUpdating},data,helpIcons)})),_defineProperty(this,"getMediaTemplateContext",(async function(){let data=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null;return data?data.hasOwnProperty("isUpdating")?_this.isUpdating=data.isUpdating:_this.isUpdating=Object.keys(data).length>1:data=Object.assign({},_this.getCurrentEmbedData()),await _this.getTemplateContext(data)})),_defineProperty(this,"loadMediaPreview",(url=>{new _embedinsert.EmbedInsert(this).loadMediaPreview(url)})),_defineProperty(this,"resetCurrentMediaData",(()=>{this.media={},this.mediaType=null,this.selectedMedia=null})),_defineProperty(this,"getFilePickerTypeFromElement",(element=>element.closest(_selectors.default.EMBED.elements.posterSource)?"image":element.closest(_selectors.default.EMBED.elements.trackSource)?"subtitle":"media")),_defineProperty(this,"getTrackTypeFromTabPane",(tabPane=>tabPane.getAttribute("data-track-kind"))),_defineProperty(this,"clickHandler",(async e=>{const element=e.target;if(element.closest(_selectors.default.EMBED.actions.mediaBrowser)){e.preventDefault();const fpType=this.getFilePickerTypeFromElement(element),params=await(0,_utils.displayFilepicker)(this.editor,fpType);this.trackFilePickerCallback(params,element,fpType)}e.target.closest(_selectors.default.EMBED.actions.addUrl)&&((0,_helpers.startMediaLoading)(this.root,_selectors.default.EMBED.type),this.urlChanged());element.closest(_selectors.default.EMBED.elements.track+" .addcomponent")&&(e.preventDefault(),this.addTrackComponent(element));element.closest(_selectors.default.EMBED.elements.track+" .removecomponent")&&(e.preventDefault(),this.removeTrackComponent(element));const trackDefaultAction=element.closest(_selectors.default.EMBED.elements.trackDefault);if(trackDefaultAction&&trackDefaultAction.checked){const getKind=el=>this.getTrackTypeFromTabPane(el.parentElement.closest(".tab-pane"));element.parentElement.closest(".tab-content").querySelectorAll(_selectors.default.EMBED.elements.trackDefault).forEach((select=>{select!==element&&getKind(element)===getKind(select)&&(select.checked=!1)}))}})),_defineProperty(this,"getMediaHTML",(form=>{this.mediaType=this.root.querySelector(_selectors.default.EMBED.elements.mediaPreviewContainer).dataset.mediaType;const tabContent=form.querySelector(".tab-content");return this["getMediaHTML"+this.mediaType[0].toUpperCase()+this.mediaType.substr(1)](tabContent)})),_defineProperty(this,"getMediaHTMLVideo",(tab=>{const details=document.querySelector(_selectors.default.EMBED.elements.mediaDetailsBody),context=this.getContextForMediaHTML(tab,details);context.width=details.querySelector(_selectors.default.EMBED.elements.width).value||!1,context.height=details.querySelector(_selectors.default.EMBED.elements.height).value||!1;const mediaPreviewContainer=details.querySelector(_selectors.default.EMBED.elements.mediaPreviewContainer);return context.poster=mediaPreviewContainer.dataset.mediaPoster||!1,context.sources?_templates.default.renderForPromise("tiny_media/embed/embed_media_video",context):""})),_defineProperty(this,"getMediaHTMLAudio",(tab=>{const details=document.querySelector(_selectors.default.EMBED.elements.mediaDetailsBody),context=this.getContextForMediaHTML(tab,details);return context.sources.length?_templates.default.renderForPromise("tiny_media/embed/embed_media_audio",context):""})),_defineProperty(this,"getContextForMediaHTML",((tab,details)=>{var _mediaPreviewContaine;const tracks=Array.from(tab.querySelectorAll(_selectors.default.EMBED.elements.track)).map((track=>({track:track.querySelector(_selectors.default.EMBED.elements.trackSource+" "+_selectors.default.EMBED.elements.url).value,kind:this.getTrackTypeFromTabPane(track.closest(".tab-pane")),label:track.querySelector(_selectors.default.EMBED.elements.trackLabel).value||track.querySelector(_selectors.default.EMBED.elements.trackLang).value,srclang:track.querySelector(_selectors.default.EMBED.elements.trackLang).value,defaultTrack:track.querySelector(_selectors.default.EMBED.elements.trackDefault).checked?"true":null}))).filter((track=>!!track.track)),mediaPreviewContainer=details.querySelector(_selectors.default.EMBED.elements.mediaPreviewContainer);let sources=null!==(_mediaPreviewContaine=mediaPreviewContainer.dataset.originalUrl)&&void 0!==_mediaPreviewContaine?_mediaPreviewContaine:null;this.alternativeSources&&(this.alternativeSources[0]=sources,sources=this.alternativeSources);const title=details.querySelector(_selectors.default.EMBED.elements.title).value;mediaPreviewContainer.removeAttribute("data-original-url");const templateContext={sources:sources,tracks:tracks,showControls:details.querySelector(_selectors.default.EMBED.elements.mediaControl).checked,autoplay:details.querySelector(_selectors.default.EMBED.elements.mediaAutoplay).checked,muted:details.querySelector(_selectors.default.EMBED.elements.mediaMute).checked,loop:details.querySelector(_selectors.default.EMBED.elements.mediaLoop).checked,title:""!==title&&title.trim()};return"link"===this.mediaType&&(templateContext.description=Array.isArray(sources)?sources[0]:sources),templateContext})),_defineProperty(this,"handleDialogueSubmission",(async(event,modal)=>{const{html:html}=await this.getMediaHTML(modal.getRoot()[0]);html&&(this.isUpdating?(this.selectedMedia.outerHTML=html,this.isUpdating=!1):this.editor.insertContent(html))})),_defineProperty(this,"registerEventListeners",(async()=>{this.canShowFilePickerTrack&&this.root.addEventListener("click",this.clickHandler.bind(this)),this.root.addEventListener("input",(e=>{const urlEle=e.target.closest(_selectors.default.EMBED.elements.fromUrl);urlEle&&this.toggleUrlButton(urlEle,this.root)})),this.modalRoot.on(ModalEvents.hidden,(()=>{this.currentModal.destroy()})),this.modalRoot.on(ModalEvents.save,this.handleDialogueSubmission.bind(this))})),(0,_helpers.setPropertiesFromData)(this,_data)}urlChanged(){const url=this.root.querySelector(_selectors.default.EMBED.elements.fromUrl).value;url&&url!==this.currentUrl&&(this.mediaType=null,this.newMediaLink=!0,this.loadMediaPreview(url))}trackFilePickerCallback(params,element,fpType){if(""!==params.url){if(element.closest(".tab-pane")){if(element.closest(_selectors.default.EMBED.elements.source).querySelector(_selectors.default.EMBED.elements.url).value=params.url,"subtitle"===fpType){const subtitleLang=params.file.split(".vtt")[0].split("-").slice(-1)[0],langObj=(0,_embedhelpers.getMoodleLangObj)(subtitleLang,this.editor);if(langObj){const track=element.closest(_selectors.default.EMBED.elements.track);track.querySelector(_selectors.default.EMBED.elements.trackLabel).value=langObj.lang.trim(),track.querySelector(_selectors.default.EMBED.elements.trackLang).value=langObj.code}}}else this.newFileUpload=!0,this.resetCurrentMediaData(),this.loadMediaPreview(params.url)}}addTrackComponent(element){const trackElement=element.closest(_selectors.default.EMBED.elements.track),clone=trackElement.cloneNode(!0);trackElement.querySelector(".removecomponent-wrapper").classList.remove("hidden"),trackElement.querySelector(".addcomponent-wrapper").classList.add("hidden"),trackElement.parentNode.insertBefore(clone,trackElement.nextSibling)}removeTrackComponent(element){element.closest(_selectors.default.EMBED.elements.track).remove()}toggleUrlButton(input,root){const url=input.value;root.querySelector(_selectors.default.EMBED.actions.addUrl).disabled=!(""!==url&&(0,_helpers.isValidUrl)(url))}getMediaHTMLLink(){var _document$querySelect;const mediaPreviewContainer=document.querySelector(_selectors.default.EMBED.elements.mediaPreviewContainer),context={name:null!==(_document$querySelect=document.querySelector(_selectors.default.EMBED.elements.title).value)&&void 0!==_document$querySelect?_document$querySelect:mediaPreviewContainer.dataset.originalUrl,url:mediaPreviewContainer.dataset.originalUrl||!1};return context.url?_templates.default.renderForPromise("tiny_media/embed/embed_media_link",context):""}}})); //# sourceMappingURL=embedhandler.min.js.map \ No newline at end of file diff --git a/lib/editor/tiny/plugins/media/amd/build/embed/embedhandler.min.js.map b/lib/editor/tiny/plugins/media/amd/build/embed/embedhandler.min.js.map index 5fc400cd792..b33c2abcc28 100644 --- a/lib/editor/tiny/plugins/media/amd/build/embed/embedhandler.min.js.map +++ b/lib/editor/tiny/plugins/media/amd/build/embed/embedhandler.min.js.map @@ -1 +1 @@ -{"version":3,"file":"embedhandler.min.js","sources":["../../src/embed/embedhandler.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 .\n\n/**\n * Tiny media plugin embed handler class.\n *\n * This handles anything that embed requires like:\n * - Calling the media preview in embedPreview.\n * - Loading the embed insert.\n * - Getting selected media data.\n * - Handles url and repository uploads.\n * - Reset embed insert when embed preview is deleted.\n * - Handles media embedding into tiny and etc.\n *\n * @module tiny_media/embed/embedhandler\n * @copyright 2024 Stevani Andolo \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Selectors from \"../selectors\";\nimport {EmbedInsert} from './embedinsert';\nimport {\n body,\n footer,\n setPropertiesFromData,\n isValidUrl,\n stopMediaLoading,\n startMediaLoading,\n} from '../helpers';\nimport * as ModalEvents from 'core/modal_events';\nimport {displayFilepicker} from 'editor_tiny/utils';\nimport {\n insertMediaTemplateContext,\n getHelpStrings,\n prepareMoodleLang,\n getMoodleLangObj,\n hasAudioVideoAttr,\n insertMediaThumbnailTemplateContext,\n} from \"./embedhelpers\";\nimport Templates from 'core/templates';\nimport {EmbedThumbnailInsert} from './embedthumbnailinsert';\n\nexport class EmbedHandler {\n\n constructor(data) {\n setPropertiesFromData(this, data); // Creates dynamic properties based on \"data\" param.\n }\n\n /**\n * Load the media insert dialogue.\n *\n * @param {object} templateContext Object template context\n */\n loadTemplatePromise = (templateContext) => {\n templateContext.elementid = this.editor.id;\n templateContext.bodyTemplate = Selectors.EMBED.template.body.insertMediaBody;\n templateContext.footerTemplate = Selectors.EMBED.template.footer.insertMediaFooter;\n templateContext.selector = Selectors.EMBED.type;\n\n Promise.all([body(templateContext, this.root), footer(templateContext, this.root)])\n .then(() => {\n (new EmbedInsert(this)).init();\n return;\n })\n .catch(error => {\n window.console.log(error);\n });\n };\n\n /**\n * Load the media thumbnail insert dialogue.\n *\n * @param {object} templateContext Object template context\n * @param {HTMLElement} root\n * @param {object} mediaData\n */\n loadInsertThumbnailTemplatePromise = async(templateContext, root, mediaData) => {\n Promise.all([body(templateContext, root.root), footer(templateContext, root.root)])\n .then(() => {\n if (!this.currentModal.insertMediaModal) {\n this.currentModal.insertMediaModal = this.currentModal;\n }\n\n if (root.uploadThumbnailModal) {\n this.currentModal.uploadThumbnailModal = root.uploadThumbnailModal;\n }\n\n this.thumbnailModalRoot = root.root;\n (new EmbedThumbnailInsert(this)).init(mediaData);\n return;\n })\n .catch(error => {\n window.console.log(error);\n });\n };\n\n /**\n * Loads the media preview dialogue.\n *\n * @param {object} embedPreview Object of embedPreview\n * @param {object} templateContext Object of template context\n */\n loadMediaDetails = async(embedPreview, templateContext) => {\n Promise.all([body(templateContext, this.root), footer(templateContext, this.root)])\n .then(() => {\n embedPreview.init();\n return;\n })\n .catch(error => {\n stopMediaLoading(this.root, Selectors.EMBED.type);\n window.console.log(error);\n });\n };\n\n /**\n * Reset the media/thumbnail insert modal form.\n *\n * @param {boolean} isMediaInsert Is current state media insert or thumbnail insert?\n */\n resetUploadForm = (isMediaInsert = true) => {\n if (isMediaInsert) {\n this.newMediaLink = false;\n this.fetchedMediaLinkTitle = null;\n this.resetCurrentMediaData();\n this.loadTemplatePromise(insertMediaTemplateContext(this));\n } else {\n this.loadInsertThumbnailTemplatePromise(\n insertMediaThumbnailTemplateContext(this), // Get template context for creating media thumbnail.\n {root: this.thumbnailModalRoot}, // Required root elements.\n this.mediaData // Get current media data.\n );\n }\n };\n\n /**\n * Get selected media data.\n *\n * @returns {null|object}\n */\n getMediumProperties = () => {\n const medium = this.selectedMedia;\n if (!medium) {\n return null;\n }\n\n const tracks = {\n subtitles: [],\n captions: [],\n descriptions: [],\n chapters: [],\n metadata: []\n };\n const sources = [];\n\n medium.querySelectorAll('track').forEach((track) => {\n tracks[track.getAttribute('kind')].push({\n src: track.getAttribute('src'),\n srclang: track.getAttribute('srclang'),\n label: track.getAttribute('label'),\n defaultTrack: hasAudioVideoAttr(track, 'default')\n });\n });\n\n medium.querySelectorAll('source').forEach((source) => {\n sources.push(source.src);\n });\n const title = medium.getAttribute('title') ?? medium.textContent;\n\n return {\n type: this.mediaType,\n sources,\n poster: medium.getAttribute('poster'),\n title: title ? title.trim() : false,\n width: medium.getAttribute('width'),\n height: medium.getAttribute('height'),\n autoplay: hasAudioVideoAttr(medium, 'autoplay'),\n loop: hasAudioVideoAttr(medium, 'loop'),\n muted: hasAudioVideoAttr(medium, 'muted'),\n controls: hasAudioVideoAttr(medium, 'controls'),\n tracks,\n };\n };\n\n /**\n * Get selected media data.\n *\n * @returns {object}\n */\n getCurrentEmbedData = () => {\n const properties = this.getMediumProperties();\n if (!properties || this.newMediaLink) {\n return {media: {}};\n }\n\n const processedProperties = {};\n processedProperties.media = properties;\n processedProperties.link = false;\n\n return processedProperties;\n };\n\n /**\n * Get help strings for media subtitles and captions.\n *\n * @returns {null|object}\n */\n getHelpStrings = async() => {\n if (!this.helpStrings) {\n this.helpStrings = await getHelpStrings();\n }\n\n return this.helpStrings;\n };\n\n /**\n * Set template context for insert media dialogue.\n *\n * @param {object} data Object of media data\n * @returns {object}\n */\n getTemplateContext = async(data) => {\n const languages = prepareMoodleLang(this.editor);\n const helpIcons = Array.from(Object.entries(await this.getHelpStrings())).forEach(([key, text]) => {\n data[`${key.toLowerCase()}helpicon`] = {text};\n });\n\n return Object.assign({}, {\n elementid: this.editor.getElement().id,\n showFilePickerTrack: this.canShowFilePickerTrack,\n langsInstalled: languages.installed,\n langsAvailable: languages.available,\n media: true,\n isUpdating: this.isUpdating,\n }, data, helpIcons);\n };\n\n /**\n * Set and get media template context.\n *\n * @param {null|object} data Null or object of media data\n * @returns {Promise} A promise that resolves template context.\n */\n getMediaTemplateContext = async(data = null) => {\n if (!data) {\n data = Object.assign({}, this.getCurrentEmbedData());\n } else {\n if (data.hasOwnProperty('isUpdating')) {\n this.isUpdating = data.isUpdating;\n } else {\n this.isUpdating = Object.keys(data).length > 1;\n }\n }\n return await this.getTemplateContext(data);\n };\n\n /**\n * Handles changes in the media URL input field and loads a preview of the media if the URL has changed.\n */\n urlChanged() {\n const url = this.root.querySelector(Selectors.EMBED.elements.fromUrl).value;\n if (url && url !== this.currentUrl) {\n // Set to null on new url change.\n this.mediaType = null;\n\n // Flag as new media link insert.\n this.newMediaLink = true;\n this.loadMediaPreview(url);\n }\n }\n\n /**\n * Load the media preview dialogue.\n *\n * @param {string} url String of media url\n */\n loadMediaPreview = (url) => {\n (new EmbedInsert(this)).loadMediaPreview(url);\n };\n\n /**\n * Callback for file picker that previews the media or add the captions and subtitles.\n *\n * @param {object} params Object of media url and etc\n * @param {html} element Selected element.\n * @param {string} fpType Caption type.\n */\n trackFilePickerCallback(params, element, fpType) {\n if (params.url !== '') {\n const tabPane = element.closest('.tab-pane');\n if (tabPane) {\n element.closest(Selectors.EMBED.elements.source).querySelector(Selectors.EMBED.elements.url).value = params.url;\n\n if (fpType === 'subtitle') {\n // If the file is subtitle file. We need to match the language and label for that file.\n const subtitleLang = params.file.split('.vtt')[0].split('-').slice(-1)[0];\n const langObj = getMoodleLangObj(subtitleLang, this.editor);\n if (langObj) {\n const track = element.closest(Selectors.EMBED.elements.track);\n track.querySelector(Selectors.EMBED.elements.trackLabel).value = langObj.lang.trim();\n track.querySelector(Selectors.EMBED.elements.trackLang).value = langObj.code;\n }\n }\n } else {\n // Flag as new file upload.\n this.newFileUpload = true;\n this.resetCurrentMediaData();\n this.loadMediaPreview(params.url);\n }\n }\n }\n\n /**\n * Reset current media data.\n */\n resetCurrentMediaData = () => {\n // Reset the value of the following props.\n this.media = {};\n this.mediaType = null;\n this.selectedMedia = null;\n };\n\n /**\n * Add new html track element.\n *\n * @param {html} element\n */\n addTrackComponent(element) {\n const trackElement = element.closest(Selectors.EMBED.elements.track);\n const clone = trackElement.cloneNode(true);\n\n trackElement.querySelector('.removecomponent-wrapper').classList.remove('hidden');\n trackElement.querySelector('.addcomponent-wrapper').classList.add('hidden');\n trackElement.parentNode.insertBefore(clone, trackElement.nextSibling);\n }\n\n /**\n * Remove added html track element.\n *\n * @param {html} element\n */\n removeTrackComponent(element) {\n const sourceElement = element.closest(Selectors.EMBED.elements.track);\n sourceElement.remove();\n }\n\n /**\n * Get picker type based on the selected element.\n *\n * @param {html} element Selected element\n * @returns {string}\n */\n getFilePickerTypeFromElement = (element) => {\n if (element.closest(Selectors.EMBED.elements.posterSource)) {\n return 'image';\n }\n if (element.closest(Selectors.EMBED.elements.trackSource)) {\n return 'subtitle';\n }\n\n return 'media';\n };\n\n /**\n * Get captions/subtitles type.\n *\n * @param {html} tabPane\n * @returns {string}\n */\n getTrackTypeFromTabPane = (tabPane) => {\n return tabPane.getAttribute('data-track-kind');\n };\n\n /**\n * Handle click events.\n *\n * @param {html} e Selected element\n */\n clickHandler = async(e) => {\n const element = e.target;\n\n // Handle repository browsing.\n const mediaBrowser = element.closest(Selectors.EMBED.actions.mediaBrowser);\n if (mediaBrowser) {\n e.preventDefault();\n const fpType = this.getFilePickerTypeFromElement(element);\n const params = await displayFilepicker(this.editor, fpType);\n this.trackFilePickerCallback(params, element, fpType);\n }\n\n // Handles add media url.\n const addUrlEle = e.target.closest(Selectors.EMBED.actions.addUrl);\n if (addUrlEle) {\n startMediaLoading(this.root, Selectors.EMBED.type);\n this.urlChanged();\n }\n\n // Handles adding tracks.\n const addComponentTrackAction = element.closest(Selectors.EMBED.elements.track + ' .addcomponent');\n if (addComponentTrackAction) {\n e.preventDefault();\n this.addTrackComponent(element);\n }\n\n // Handles removing added tracks.\n const removeComponentTrackAction = element.closest(Selectors.EMBED.elements.track + ' .removecomponent');\n if (removeComponentTrackAction) {\n e.preventDefault();\n this.removeTrackComponent(element);\n }\n\n // Only allow one track per tab to be selected as \"default\".\n const trackDefaultAction = element.closest(Selectors.EMBED.elements.trackDefault);\n if (trackDefaultAction && trackDefaultAction.checked) {\n const getKind = (el) => this.getTrackTypeFromTabPane(el.parentElement.closest('.tab-pane'));\n\n element.parentElement\n .closest('.tab-content')\n .querySelectorAll(Selectors.EMBED.elements.trackDefault)\n .forEach((select) => {\n if (select !== element && getKind(element) === getKind(select)) {\n select.checked = false;\n }\n });\n }\n };\n\n /**\n * Enables or disables the URL-related buttons in the footer based on the current URL and input value.\n *\n * @param {html} input Url input field\n * @param {object} root\n */\n toggleUrlButton(input, root) {\n const url = input.value;\n const addUrl = root.querySelector(Selectors.EMBED.actions.addUrl);\n addUrl.disabled = !(url !== \"\" && isValidUrl(url));\n }\n\n /**\n * Get media html to be inserted or updated into tiny.\n *\n * @param {html} form Selected element\n * @returns {string} String of html\n */\n getMediaHTML = (form) => {\n this.mediaType = this.root.querySelector(Selectors.EMBED.elements.mediaPreviewContainer).dataset.mediaType;\n const tabContent = form.querySelector('.tab-content');\n const callback = 'getMediaHTML' + this.mediaType[0].toUpperCase() + this.mediaType.substr(1);\n return this[callback](tabContent);\n };\n\n /**\n * Get media as link.\n *\n * @returns {string} String of html.\n */\n getMediaHTMLLink() {\n const mediaPreviewContainer = document.querySelector(Selectors.EMBED.elements.mediaPreviewContainer);\n const context = {\n name: document.querySelector(Selectors.EMBED.elements.title).value ?? mediaPreviewContainer.dataset.originalUrl,\n url: mediaPreviewContainer.dataset.originalUrl || false\n };\n\n return context.url ? Templates.renderForPromise('tiny_media/embed/embed_media_link', context) : '';\n }\n\n /**\n * Get media as video.\n *\n * @param {html} tab Selected element\n * @returns {string} String of html.\n */\n getMediaHTMLVideo = (tab) => {\n const details = document.querySelector(Selectors.EMBED.elements.mediaDetailsBody);\n const context = this.getContextForMediaHTML(tab, details);\n context.width = details.querySelector(Selectors.EMBED.elements.width).value || false;\n context.height = details.querySelector(Selectors.EMBED.elements.height).value || false;\n\n const mediaPreviewContainer = details.querySelector(Selectors.EMBED.elements.mediaPreviewContainer);\n context.poster = mediaPreviewContainer.dataset.mediaPoster || false;\n return context.sources ? Templates.renderForPromise('tiny_media/embed/embed_media_video', context) : '';\n };\n\n /**\n * Get media as audio.\n *\n * @param {html} tab Selected element\n * @returns {string} String of html.\n */\n getMediaHTMLAudio = (tab) => {\n const details = document.querySelector(Selectors.EMBED.elements.mediaDetailsBody);\n const context = this.getContextForMediaHTML(tab, details);\n return context.sources.length ? Templates.renderForPromise('tiny_media/embed/embed_media_audio', context) : '';\n };\n\n /**\n * Get previewed media data.\n *\n * @param {html} tab Selected element\n * @param {html} details Selected element\n * @returns {object}\n */\n getContextForMediaHTML = (tab, details) => {\n const tracks = Array.from(tab.querySelectorAll(Selectors.EMBED.elements.track)).map(track => ({\n track: track.querySelector(Selectors.EMBED.elements.trackSource + ' ' + Selectors.EMBED.elements.url).value,\n kind: this.getTrackTypeFromTabPane(track.closest('.tab-pane')),\n label: track.querySelector(Selectors.EMBED.elements.trackLabel).value ||\n track.querySelector(Selectors.EMBED.elements.trackLang).value,\n srclang: track.querySelector(Selectors.EMBED.elements.trackLang).value,\n defaultTrack: track.querySelector(Selectors.EMBED.elements.trackDefault).checked ? \"true\" : null\n })).filter((track) => !!track.track);\n\n const mediaPreviewContainer = details.querySelector(Selectors.EMBED.elements.mediaPreviewContainer);\n let sources = mediaPreviewContainer.dataset.originalUrl ?? null;\n\n // Let's check if media has more than one sources.\n if (this.alternativeSources) {\n // Always update the first item in this.alternativeSources to the new one.\n this.alternativeSources[0] = sources;\n // Override the sources to have all the updated sources.\n sources = this.alternativeSources;\n }\n\n const title = details.querySelector(Selectors.EMBED.elements.title).value;\n // Remove data-original-url attribute once it's extracted.\n mediaPreviewContainer.removeAttribute('data-original-url');\n\n const templateContext = {\n sources,\n tracks,\n showControls: details.querySelector(Selectors.EMBED.elements.mediaControl).checked,\n autoplay: details.querySelector(Selectors.EMBED.elements.mediaAutoplay).checked,\n muted: details.querySelector(Selectors.EMBED.elements.mediaMute).checked,\n loop: details.querySelector(Selectors.EMBED.elements.mediaLoop).checked,\n title: title !== '' ? title.trim() : false,\n };\n\n // Add description prop to templateContext if media type is \"link\".\n if (this.mediaType === 'link') {\n // Let's form an alternative title.\n templateContext.description = Array.isArray(sources) ? sources[0] : sources;\n }\n\n return templateContext;\n };\n\n /**\n * Handle the insert/update media in tiny editor.\n *\n * @param {event} event\n * @param {object} modal Object of current modal\n */\n handleDialogueSubmission = async(event, modal) => {\n const {html} = await this.getMediaHTML(modal.getRoot()[0]);\n if (html) {\n if (this.isUpdating) {\n this.selectedMedia.outerHTML = html;\n this.isUpdating = false;\n } else {\n this.editor.insertContent(html);\n }\n }\n };\n\n /**\n * Register insert media modal elements' events.\n */\n registerEventListeners = async() => {\n // Handles click events for insert media modal.\n if (this.canShowFilePickerTrack) {\n this.root.addEventListener('click', this.clickHandler.bind(this));\n }\n\n // Handles media adding using url input.\n this.root.addEventListener('input', (e) => {\n const urlEle = e.target.closest(Selectors.EMBED.elements.fromUrl);\n if (urlEle) {\n this.toggleUrlButton(urlEle, this.root);\n }\n });\n\n // Destroy created modal when it's closed.\n this.modalRoot.on(ModalEvents.hidden, () => {\n this.currentModal.destroy();\n });\n\n // Handles media insert to editor.\n this.modalRoot.on(ModalEvents.save, this.handleDialogueSubmission.bind(this));\n };\n}\n"],"names":["constructor","data","templateContext","elementid","this","editor","id","bodyTemplate","Selectors","EMBED","template","body","insertMediaBody","footerTemplate","footer","insertMediaFooter","selector","type","Promise","all","root","then","EmbedInsert","init","catch","error","window","console","log","async","mediaData","currentModal","insertMediaModal","uploadThumbnailModal","thumbnailModalRoot","EmbedThumbnailInsert","embedPreview","isMediaInsert","_this","newMediaLink","fetchedMediaLinkTitle","resetCurrentMediaData","loadTemplatePromise","loadInsertThumbnailTemplatePromise","medium","selectedMedia","tracks","subtitles","captions","descriptions","chapters","metadata","sources","querySelectorAll","forEach","track","getAttribute","push","src","srclang","label","defaultTrack","source","title","textContent","mediaType","poster","trim","width","height","autoplay","loop","muted","controls","properties","getMediumProperties","media","processedProperties","link","helpStrings","languages","helpIcons","Array","from","Object","entries","getHelpStrings","_ref","key","text","toLowerCase","assign","getElement","showFilePickerTrack","canShowFilePickerTrack","langsInstalled","installed","langsAvailable","available","isUpdating","hasOwnProperty","keys","length","getCurrentEmbedData","getTemplateContext","url","loadMediaPreview","element","closest","elements","posterSource","trackSource","tabPane","e","target","actions","mediaBrowser","preventDefault","fpType","getFilePickerTypeFromElement","params","trackFilePickerCallback","addUrl","urlChanged","addTrackComponent","removeTrackComponent","trackDefaultAction","trackDefault","checked","getKind","el","getTrackTypeFromTabPane","parentElement","select","form","querySelector","mediaPreviewContainer","dataset","tabContent","toUpperCase","substr","tab","details","document","mediaDetailsBody","context","getContextForMediaHTML","value","mediaPoster","Templates","renderForPromise","map","kind","trackLabel","trackLang","filter","originalUrl","alternativeSources","removeAttribute","showControls","mediaControl","mediaAutoplay","mediaMute","mediaLoop","description","isArray","event","modal","html","getMediaHTML","getRoot","outerHTML","insertContent","addEventListener","clickHandler","bind","urlEle","fromUrl","toggleUrlButton","modalRoot","on","ModalEvents","hidden","destroy","save","handleDialogueSubmission","currentUrl","subtitleLang","file","split","slice","langObj","lang","code","newFileUpload","trackElement","clone","cloneNode","classList","remove","add","parentNode","insertBefore","nextSibling","input","disabled","getMediaHTMLLink","name"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"embedhandler.min.js","sources":["../../src/embed/embedhandler.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 .\n\n/**\n * Tiny media plugin embed handler class.\n *\n * This handles anything that embed requires like:\n * - Calling the media preview in embedPreview.\n * - Loading the embed insert.\n * - Getting selected media data.\n * - Handles url and repository uploads.\n * - Reset embed insert when embed preview is deleted.\n * - Handles media embedding into tiny and etc.\n *\n * @module tiny_media/embed/embedhandler\n * @copyright 2024 Stevani Andolo \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Selectors from \"../selectors\";\nimport {EmbedInsert} from './embedinsert';\nimport {\n body,\n footer,\n setPropertiesFromData,\n isValidUrl,\n stopMediaLoading,\n startMediaLoading,\n} from '../helpers';\nimport * as ModalEvents from 'core/modal_events';\nimport {displayFilepicker} from 'editor_tiny/utils';\nimport {\n insertMediaTemplateContext,\n getHelpStrings,\n prepareMoodleLang,\n getMoodleLangObj,\n hasAudioVideoAttr,\n insertMediaThumbnailTemplateContext,\n} from \"./embedhelpers\";\nimport Templates from 'core/templates';\nimport {EmbedThumbnailInsert} from './embedthumbnailinsert';\n\nexport class EmbedHandler {\n\n constructor(data) {\n setPropertiesFromData(this, data); // Creates dynamic properties based on \"data\" param.\n }\n\n /**\n * Load the media insert dialogue.\n *\n * @param {object} templateContext Object template context\n */\n loadTemplatePromise = (templateContext) => {\n templateContext.elementid = this.editor.id;\n templateContext.bodyTemplate = Selectors.EMBED.template.body.insertMediaBody;\n templateContext.footerTemplate = Selectors.EMBED.template.footer.insertMediaFooter;\n templateContext.selector = Selectors.EMBED.type;\n\n Promise.all([body(templateContext, this.root), footer(templateContext, this.root)])\n .then(() => {\n (new EmbedInsert(this)).init();\n return;\n })\n .catch(error => {\n window.console.log(error);\n });\n };\n\n /**\n * Load the media thumbnail insert dialogue.\n *\n * @param {object} templateContext Object template context\n * @param {HTMLElement} root\n * @param {object} mediaData\n */\n loadInsertThumbnailTemplatePromise = async(templateContext, root, mediaData) => {\n Promise.all([body(templateContext, root.root), footer(templateContext, root.root)])\n .then(() => {\n if (!this.currentModal.insertMediaModal) {\n this.currentModal.insertMediaModal = this.currentModal;\n }\n\n if (root.uploadThumbnailModal) {\n this.currentModal.uploadThumbnailModal = root.uploadThumbnailModal;\n }\n\n this.thumbnailModalRoot = root.root;\n (new EmbedThumbnailInsert(this)).init(mediaData);\n return;\n })\n .catch(error => {\n window.console.log(error);\n });\n };\n\n /**\n * Loads the media preview dialogue.\n *\n * @param {object} embedPreview Object of embedPreview\n * @param {object} templateContext Object of template context\n */\n loadMediaDetails = async(embedPreview, templateContext) => {\n Promise.all([body(templateContext, this.root), footer(templateContext, this.root)])\n .then(() => {\n embedPreview.init();\n return;\n })\n .catch(error => {\n stopMediaLoading(this.root, Selectors.EMBED.type);\n window.console.log(error);\n });\n };\n\n /**\n * Reset the media/thumbnail insert modal form.\n *\n * @param {boolean} isMediaInsert Is current state media insert or thumbnail insert?\n */\n resetUploadForm = (isMediaInsert = true) => {\n if (isMediaInsert) {\n this.newMediaLink = false;\n this.fetchedMediaLinkTitle = null;\n this.resetCurrentMediaData();\n this.loadTemplatePromise(insertMediaTemplateContext(this));\n } else {\n this.loadInsertThumbnailTemplatePromise(\n insertMediaThumbnailTemplateContext(this), // Get template context for creating media thumbnail.\n {root: this.thumbnailModalRoot}, // Required root elements.\n this.mediaData // Get current media data.\n );\n }\n };\n\n /**\n * Get selected media data.\n *\n * @returns {null|object}\n */\n getMediaProperties = () => {\n const media = this.selectedMedia;\n if (!media) {\n return null;\n }\n\n const tracks = {\n subtitles: [],\n captions: [],\n descriptions: [],\n chapters: [],\n metadata: []\n };\n const sources = [];\n\n media.querySelectorAll('track').forEach((track) => {\n tracks[track.getAttribute('kind')].push({\n src: track.getAttribute('src'),\n srclang: track.getAttribute('srclang'),\n label: track.getAttribute('label'),\n defaultTrack: hasAudioVideoAttr(track, 'default')\n });\n });\n\n media.querySelectorAll('source').forEach((source) => {\n sources.push(source.src);\n });\n const title = media.getAttribute('title') ?? media.textContent;\n\n return {\n type: this.mediaType,\n sources,\n poster: media.getAttribute('poster'),\n title: title ? title.trim() : false,\n width: media.getAttribute('width'),\n height: media.getAttribute('height'),\n autoplay: hasAudioVideoAttr(media, 'autoplay'),\n loop: hasAudioVideoAttr(media, 'loop'),\n muted: hasAudioVideoAttr(media, 'muted'),\n controls: hasAudioVideoAttr(media, 'controls'),\n tracks,\n };\n };\n\n /**\n * Get selected media data.\n *\n * @returns {object}\n */\n getCurrentEmbedData = () => {\n const properties = this.getMediaProperties();\n if (!properties || this.newMediaLink) {\n return {media: {}};\n }\n\n const processedProperties = {};\n processedProperties.media = properties;\n processedProperties.link = false;\n\n return processedProperties;\n };\n\n /**\n * Get help strings for media subtitles and captions.\n *\n * @returns {null|object}\n */\n getHelpStrings = async() => {\n if (!this.helpStrings) {\n this.helpStrings = await getHelpStrings();\n }\n\n return this.helpStrings;\n };\n\n /**\n * Set template context for insert media dialogue.\n *\n * @param {object} data Object of media data\n * @returns {object}\n */\n getTemplateContext = async(data) => {\n const languages = prepareMoodleLang(this.editor);\n const helpIcons = Array.from(Object.entries(await this.getHelpStrings())).forEach(([key, text]) => {\n data[`${key.toLowerCase()}helpicon`] = {text};\n });\n\n return Object.assign({}, {\n elementid: this.editor.getElement().id,\n showFilePickerTrack: this.canShowFilePickerTrack,\n langsInstalled: languages.installed,\n langsAvailable: languages.available,\n media: true,\n isUpdating: this.isUpdating,\n }, data, helpIcons);\n };\n\n /**\n * Set and get media template context.\n *\n * @param {null|object} data Null or object of media data\n * @returns {Promise} A promise that resolves template context.\n */\n getMediaTemplateContext = async(data = null) => {\n if (!data) {\n data = Object.assign({}, this.getCurrentEmbedData());\n } else {\n if (data.hasOwnProperty('isUpdating')) {\n this.isUpdating = data.isUpdating;\n } else {\n this.isUpdating = Object.keys(data).length > 1;\n }\n }\n return await this.getTemplateContext(data);\n };\n\n /**\n * Handles changes in the media URL input field and loads a preview of the media if the URL has changed.\n */\n urlChanged() {\n const url = this.root.querySelector(Selectors.EMBED.elements.fromUrl).value;\n if (url && url !== this.currentUrl) {\n // Set to null on new url change.\n this.mediaType = null;\n\n // Flag as new media link insert.\n this.newMediaLink = true;\n this.loadMediaPreview(url);\n }\n }\n\n /**\n * Load the media preview dialogue.\n *\n * @param {string} url String of media url\n */\n loadMediaPreview = (url) => {\n (new EmbedInsert(this)).loadMediaPreview(url);\n };\n\n /**\n * Callback for file picker that previews the media or add the captions and subtitles.\n *\n * @param {object} params Object of media url and etc\n * @param {html} element Selected element.\n * @param {string} fpType Caption type.\n */\n trackFilePickerCallback(params, element, fpType) {\n if (params.url !== '') {\n const tabPane = element.closest('.tab-pane');\n if (tabPane) {\n element.closest(Selectors.EMBED.elements.source).querySelector(Selectors.EMBED.elements.url).value = params.url;\n\n if (fpType === 'subtitle') {\n // If the file is subtitle file. We need to match the language and label for that file.\n const subtitleLang = params.file.split('.vtt')[0].split('-').slice(-1)[0];\n const langObj = getMoodleLangObj(subtitleLang, this.editor);\n if (langObj) {\n const track = element.closest(Selectors.EMBED.elements.track);\n track.querySelector(Selectors.EMBED.elements.trackLabel).value = langObj.lang.trim();\n track.querySelector(Selectors.EMBED.elements.trackLang).value = langObj.code;\n }\n }\n } else {\n // Flag as new file upload.\n this.newFileUpload = true;\n this.resetCurrentMediaData();\n this.loadMediaPreview(params.url);\n }\n }\n }\n\n /**\n * Reset current media data.\n */\n resetCurrentMediaData = () => {\n // Reset the value of the following props.\n this.media = {};\n this.mediaType = null;\n this.selectedMedia = null;\n };\n\n /**\n * Add new html track element.\n *\n * @param {html} element\n */\n addTrackComponent(element) {\n const trackElement = element.closest(Selectors.EMBED.elements.track);\n const clone = trackElement.cloneNode(true);\n\n trackElement.querySelector('.removecomponent-wrapper').classList.remove('hidden');\n trackElement.querySelector('.addcomponent-wrapper').classList.add('hidden');\n trackElement.parentNode.insertBefore(clone, trackElement.nextSibling);\n }\n\n /**\n * Remove added html track element.\n *\n * @param {html} element\n */\n removeTrackComponent(element) {\n const sourceElement = element.closest(Selectors.EMBED.elements.track);\n sourceElement.remove();\n }\n\n /**\n * Get picker type based on the selected element.\n *\n * @param {html} element Selected element\n * @returns {string}\n */\n getFilePickerTypeFromElement = (element) => {\n if (element.closest(Selectors.EMBED.elements.posterSource)) {\n return 'image';\n }\n if (element.closest(Selectors.EMBED.elements.trackSource)) {\n return 'subtitle';\n }\n\n return 'media';\n };\n\n /**\n * Get captions/subtitles type.\n *\n * @param {html} tabPane\n * @returns {string}\n */\n getTrackTypeFromTabPane = (tabPane) => {\n return tabPane.getAttribute('data-track-kind');\n };\n\n /**\n * Handle click events.\n *\n * @param {html} e Selected element\n */\n clickHandler = async(e) => {\n const element = e.target;\n\n // Handle repository browsing.\n const mediaBrowser = element.closest(Selectors.EMBED.actions.mediaBrowser);\n if (mediaBrowser) {\n e.preventDefault();\n const fpType = this.getFilePickerTypeFromElement(element);\n const params = await displayFilepicker(this.editor, fpType);\n this.trackFilePickerCallback(params, element, fpType);\n }\n\n // Handles add media url.\n const addUrlEle = e.target.closest(Selectors.EMBED.actions.addUrl);\n if (addUrlEle) {\n startMediaLoading(this.root, Selectors.EMBED.type);\n this.urlChanged();\n }\n\n // Handles adding tracks.\n const addComponentTrackAction = element.closest(Selectors.EMBED.elements.track + ' .addcomponent');\n if (addComponentTrackAction) {\n e.preventDefault();\n this.addTrackComponent(element);\n }\n\n // Handles removing added tracks.\n const removeComponentTrackAction = element.closest(Selectors.EMBED.elements.track + ' .removecomponent');\n if (removeComponentTrackAction) {\n e.preventDefault();\n this.removeTrackComponent(element);\n }\n\n // Only allow one track per tab to be selected as \"default\".\n const trackDefaultAction = element.closest(Selectors.EMBED.elements.trackDefault);\n if (trackDefaultAction && trackDefaultAction.checked) {\n const getKind = (el) => this.getTrackTypeFromTabPane(el.parentElement.closest('.tab-pane'));\n\n element.parentElement\n .closest('.tab-content')\n .querySelectorAll(Selectors.EMBED.elements.trackDefault)\n .forEach((select) => {\n if (select !== element && getKind(element) === getKind(select)) {\n select.checked = false;\n }\n });\n }\n };\n\n /**\n * Enables or disables the URL-related buttons in the footer based on the current URL and input value.\n *\n * @param {html} input Url input field\n * @param {object} root\n */\n toggleUrlButton(input, root) {\n const url = input.value;\n const addUrl = root.querySelector(Selectors.EMBED.actions.addUrl);\n addUrl.disabled = !(url !== \"\" && isValidUrl(url));\n }\n\n /**\n * Get media html to be inserted or updated into tiny.\n *\n * @param {html} form Selected element\n * @returns {string} String of html\n */\n getMediaHTML = (form) => {\n this.mediaType = this.root.querySelector(Selectors.EMBED.elements.mediaPreviewContainer).dataset.mediaType;\n const tabContent = form.querySelector('.tab-content');\n const callback = 'getMediaHTML' + this.mediaType[0].toUpperCase() + this.mediaType.substr(1);\n return this[callback](tabContent);\n };\n\n /**\n * Get media as link.\n *\n * @returns {string} String of html.\n */\n getMediaHTMLLink() {\n const mediaPreviewContainer = document.querySelector(Selectors.EMBED.elements.mediaPreviewContainer);\n const context = {\n name: document.querySelector(Selectors.EMBED.elements.title).value ?? mediaPreviewContainer.dataset.originalUrl,\n url: mediaPreviewContainer.dataset.originalUrl || false\n };\n\n return context.url ? Templates.renderForPromise('tiny_media/embed/embed_media_link', context) : '';\n }\n\n /**\n * Get media as video.\n *\n * @param {html} tab Selected element\n * @returns {string} String of html.\n */\n getMediaHTMLVideo = (tab) => {\n const details = document.querySelector(Selectors.EMBED.elements.mediaDetailsBody);\n const context = this.getContextForMediaHTML(tab, details);\n context.width = details.querySelector(Selectors.EMBED.elements.width).value || false;\n context.height = details.querySelector(Selectors.EMBED.elements.height).value || false;\n\n const mediaPreviewContainer = details.querySelector(Selectors.EMBED.elements.mediaPreviewContainer);\n context.poster = mediaPreviewContainer.dataset.mediaPoster || false;\n return context.sources ? Templates.renderForPromise('tiny_media/embed/embed_media_video', context) : '';\n };\n\n /**\n * Get media as audio.\n *\n * @param {html} tab Selected element\n * @returns {string} String of html.\n */\n getMediaHTMLAudio = (tab) => {\n const details = document.querySelector(Selectors.EMBED.elements.mediaDetailsBody);\n const context = this.getContextForMediaHTML(tab, details);\n return context.sources.length ? Templates.renderForPromise('tiny_media/embed/embed_media_audio', context) : '';\n };\n\n /**\n * Get previewed media data.\n *\n * @param {html} tab Selected element\n * @param {html} details Selected element\n * @returns {object}\n */\n getContextForMediaHTML = (tab, details) => {\n const tracks = Array.from(tab.querySelectorAll(Selectors.EMBED.elements.track)).map(track => ({\n track: track.querySelector(Selectors.EMBED.elements.trackSource + ' ' + Selectors.EMBED.elements.url).value,\n kind: this.getTrackTypeFromTabPane(track.closest('.tab-pane')),\n label: track.querySelector(Selectors.EMBED.elements.trackLabel).value ||\n track.querySelector(Selectors.EMBED.elements.trackLang).value,\n srclang: track.querySelector(Selectors.EMBED.elements.trackLang).value,\n defaultTrack: track.querySelector(Selectors.EMBED.elements.trackDefault).checked ? \"true\" : null\n })).filter((track) => !!track.track);\n\n const mediaPreviewContainer = details.querySelector(Selectors.EMBED.elements.mediaPreviewContainer);\n let sources = mediaPreviewContainer.dataset.originalUrl ?? null;\n\n // Let's check if media has more than one sources.\n if (this.alternativeSources) {\n // Always update the first item in this.alternativeSources to the new one.\n this.alternativeSources[0] = sources;\n // Override the sources to have all the updated sources.\n sources = this.alternativeSources;\n }\n\n const title = details.querySelector(Selectors.EMBED.elements.title).value;\n // Remove data-original-url attribute once it's extracted.\n mediaPreviewContainer.removeAttribute('data-original-url');\n\n const templateContext = {\n sources,\n tracks,\n showControls: details.querySelector(Selectors.EMBED.elements.mediaControl).checked,\n autoplay: details.querySelector(Selectors.EMBED.elements.mediaAutoplay).checked,\n muted: details.querySelector(Selectors.EMBED.elements.mediaMute).checked,\n loop: details.querySelector(Selectors.EMBED.elements.mediaLoop).checked,\n title: title !== '' ? title.trim() : false,\n };\n\n // Add description prop to templateContext if media type is \"link\".\n if (this.mediaType === 'link') {\n // Let's form an alternative title.\n templateContext.description = Array.isArray(sources) ? sources[0] : sources;\n }\n\n return templateContext;\n };\n\n /**\n * Handle the insert/update media in tiny editor.\n *\n * @param {event} event\n * @param {object} modal Object of current modal\n */\n handleDialogueSubmission = async(event, modal) => {\n const {html} = await this.getMediaHTML(modal.getRoot()[0]);\n if (html) {\n if (this.isUpdating) {\n this.selectedMedia.outerHTML = html;\n this.isUpdating = false;\n } else {\n this.editor.insertContent(html);\n }\n }\n };\n\n /**\n * Register insert media modal elements' events.\n */\n registerEventListeners = async() => {\n // Handles click events for insert media modal.\n if (this.canShowFilePickerTrack) {\n this.root.addEventListener('click', this.clickHandler.bind(this));\n }\n\n // Handles media adding using url input.\n this.root.addEventListener('input', (e) => {\n const urlEle = e.target.closest(Selectors.EMBED.elements.fromUrl);\n if (urlEle) {\n this.toggleUrlButton(urlEle, this.root);\n }\n });\n\n // Destroy created modal when it's closed.\n this.modalRoot.on(ModalEvents.hidden, () => {\n this.currentModal.destroy();\n });\n\n // Handles media insert to editor.\n this.modalRoot.on(ModalEvents.save, this.handleDialogueSubmission.bind(this));\n };\n}\n"],"names":["constructor","data","templateContext","elementid","this","editor","id","bodyTemplate","Selectors","EMBED","template","body","insertMediaBody","footerTemplate","footer","insertMediaFooter","selector","type","Promise","all","root","then","EmbedInsert","init","catch","error","window","console","log","async","mediaData","currentModal","insertMediaModal","uploadThumbnailModal","thumbnailModalRoot","EmbedThumbnailInsert","embedPreview","isMediaInsert","_this","newMediaLink","fetchedMediaLinkTitle","resetCurrentMediaData","loadTemplatePromise","loadInsertThumbnailTemplatePromise","media","selectedMedia","tracks","subtitles","captions","descriptions","chapters","metadata","sources","querySelectorAll","forEach","track","getAttribute","push","src","srclang","label","defaultTrack","source","title","textContent","mediaType","poster","trim","width","height","autoplay","loop","muted","controls","properties","getMediaProperties","processedProperties","link","helpStrings","languages","helpIcons","Array","from","Object","entries","getHelpStrings","_ref","key","text","toLowerCase","assign","getElement","showFilePickerTrack","canShowFilePickerTrack","langsInstalled","installed","langsAvailable","available","isUpdating","hasOwnProperty","keys","length","getCurrentEmbedData","getTemplateContext","url","loadMediaPreview","element","closest","elements","posterSource","trackSource","tabPane","e","target","actions","mediaBrowser","preventDefault","fpType","getFilePickerTypeFromElement","params","trackFilePickerCallback","addUrl","urlChanged","addTrackComponent","removeTrackComponent","trackDefaultAction","trackDefault","checked","getKind","el","getTrackTypeFromTabPane","parentElement","select","form","querySelector","mediaPreviewContainer","dataset","tabContent","toUpperCase","substr","tab","details","document","mediaDetailsBody","context","getContextForMediaHTML","value","mediaPoster","Templates","renderForPromise","map","kind","trackLabel","trackLang","filter","originalUrl","alternativeSources","removeAttribute","showControls","mediaControl","mediaAutoplay","mediaMute","mediaLoop","description","isArray","event","modal","html","getMediaHTML","getRoot","outerHTML","insertContent","addEventListener","clickHandler","bind","urlEle","fromUrl","toggleUrlButton","modalRoot","on","ModalEvents","hidden","destroy","save","handleDialogueSubmission","currentUrl","subtitleLang","file","split","slice","langObj","lang","code","newFileUpload","trackElement","clone","cloneNode","classList","remove","add","parentNode","insertBefore","nextSibling","input","disabled","getMediaHTMLLink","name"],"mappings":"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"} \ No newline at end of file diff --git a/lib/editor/tiny/plugins/media/amd/src/embed/embedhandler.js b/lib/editor/tiny/plugins/media/amd/src/embed/embedhandler.js index ca9f180d5ae..335008491ec 100644 --- a/lib/editor/tiny/plugins/media/amd/src/embed/embedhandler.js +++ b/lib/editor/tiny/plugins/media/amd/src/embed/embedhandler.js @@ -149,9 +149,9 @@ export class EmbedHandler { * * @returns {null|object} */ - getMediumProperties = () => { - const medium = this.selectedMedia; - if (!medium) { + getMediaProperties = () => { + const media = this.selectedMedia; + if (!media) { return null; } @@ -164,7 +164,7 @@ export class EmbedHandler { }; const sources = []; - medium.querySelectorAll('track').forEach((track) => { + media.querySelectorAll('track').forEach((track) => { tracks[track.getAttribute('kind')].push({ src: track.getAttribute('src'), srclang: track.getAttribute('srclang'), @@ -173,22 +173,22 @@ export class EmbedHandler { }); }); - medium.querySelectorAll('source').forEach((source) => { + media.querySelectorAll('source').forEach((source) => { sources.push(source.src); }); - const title = medium.getAttribute('title') ?? medium.textContent; + const title = media.getAttribute('title') ?? media.textContent; return { type: this.mediaType, sources, - poster: medium.getAttribute('poster'), + poster: media.getAttribute('poster'), title: title ? title.trim() : false, - width: medium.getAttribute('width'), - height: medium.getAttribute('height'), - autoplay: hasAudioVideoAttr(medium, 'autoplay'), - loop: hasAudioVideoAttr(medium, 'loop'), - muted: hasAudioVideoAttr(medium, 'muted'), - controls: hasAudioVideoAttr(medium, 'controls'), + width: media.getAttribute('width'), + height: media.getAttribute('height'), + autoplay: hasAudioVideoAttr(media, 'autoplay'), + loop: hasAudioVideoAttr(media, 'loop'), + muted: hasAudioVideoAttr(media, 'muted'), + controls: hasAudioVideoAttr(media, 'controls'), tracks, }; }; @@ -199,7 +199,7 @@ export class EmbedHandler { * @returns {object} */ getCurrentEmbedData = () => { - const properties = this.getMediumProperties(); + const properties = this.getMediaProperties(); if (!properties || this.newMediaLink) { return {media: {}}; } diff --git a/lib/editor/tiny/plugins/media/tests/behat/audio.feature b/lib/editor/tiny/plugins/media/tests/behat/audio.feature deleted file mode 100644 index c0bb8b3ec42..00000000000 --- a/lib/editor/tiny/plugins/media/tests/behat/audio.feature +++ /dev/null @@ -1,130 +0,0 @@ -@editor @editor_tiny @tiny_media @javascript -Feature: Use the TinyMCE editor to upload an audio - In order to work with audios - As a user - I need to be able to upload and manipulate audios - - Scenario: Clicking on the Audio button in the TinyMCE editor opens the audio dialog - Given I log in as "admin" - And I open my profile in edit mode - When I click on the "Multimedia" button for the "Description" TinyMCE editor - Then "Insert media" "dialogue" should exist - - Scenario: Browsing repositories in the TinyMCE editor shows the FilePicker - Given I log in as "admin" - And I open my profile in edit mode - When I click on the "Multimedia" button for the "Description" TinyMCE editor - And I click on "Browse repositories" "button" in the "Insert media" "dialogue" - Then "File picker" "dialogue" should exist - - @_file_upload - Scenario: Browsing repositories in the TinyMCE editor shows the FilePicker and add audio file - Given I log in as "admin" - When I open my profile in edit mode - And I click on the "Multimedia" button for the "Description" TinyMCE editor - And I click on "Browse repositories" "button" - And I upload "/lib/editor/tiny/tests/behat/fixtures/audio-sample.mp3" to the file picker for TinyMCE - And I click on "Save" "button" - Then I select the "audio" element in position "1" of the "Description" TinyMCE editor - - @_file_upload - # The following scenario covers: - # 1. Preview uploaded audio. - # 2. Delete previewed audio. - Scenario: Preview audio before embedding it into TinyMCE editor - Given I log in as "admin" - When I open my profile in edit mode - And I click on the "Multimedia" button for the "Description" TinyMCE editor - Then "Insert media" "dialogue" should exist - And I click on "Browse repositories" "button" in the "Insert media" "dialogue" - And "File picker" "dialogue" should exist - And I upload "/lib/editor/tiny/tests/behat/fixtures/audio-sample.mp3" to the file picker for TinyMCE - # Preview inserted audio. - And "Media details" "dialogue" should exist - And "tiny-insert-media" "region" should not exist in the "Media details" "dialogue" - And "tiny-media-details-body" "region" should exist in the "Media details" "dialogue" - And the field "Media title" in the "Media details" "dialogue" matches value "audio-sample" - And "Delete media" "button" should exist in the "Media details" "dialogue" - # Add custom thumbnail button should not exist for audio type. - And "Add custom thumbnail" "button" should not exist in the "Media details" "dialogue" - # Media controls. - And the field "Show controls" in the "Media details" "dialogue" matches value "1" - And the field "Autoplay" in the "Media details" "dialogue" matches value "0" - And the field "Muted" in the "Media details" "dialogue" matches value "0" - And the field "Loop" in the "Media details" "dialogue" matches value "0" - # Size configuration should not exist for audio type. - And "Original size" "radio" should not exist in the "Media details" "dialogue" - And "Custom size" "radio" should not exist in the "Media details" "dialogue" - # Subtitles, captions, description, tracks and metadata button for video/audio. - And "Subtitles and captions" "link" should exist in the "Media details" "dialogue" - # Let's delete the previwed audio. - And I click on "Delete media" "button" in the "Media details" "dialogue" - And "Delete media" "dialogue" should exist - And "Delete" "button" should exist in the "Delete media" "dialogue" - And I click on "Delete" "button" in the "Delete media" "dialogue" - And "Insert media" "dialogue" should exist - And "tiny-insert-media" "region" should exist - - @_file_upload - Scenario: Add subtitles to an audio before embedding it into TinyMCE editor - Given I log in as "admin" - When I open my profile in edit mode - And I click on the "Multimedia" button for the "Description" TinyMCE editor - Then "Insert media" "dialogue" should exist - And I click on "Browse repositories" "button" in the "Insert media" "dialogue" - And "File picker" "dialogue" should exist - And I upload "/lib/editor/tiny/tests/behat/fixtures/audio-sample.mp3" to the file picker for TinyMCE - And "Media details" "dialogue" should exist - And "tiny-insert-media" "region" should not exist in the "Media details" "dialogue" - And "tiny-media-details-body" "region" should exist in the "Media details" "dialogue" - # Add media subtitles. - And I click on "Subtitles and captions" "link" in the "Media details" "dialogue" - And I click on "Browse repositories..." "button" in the "Media details" "dialogue" - And "File picker" "dialogue" should exist - And I upload "/lib/editor/tiny/tests/behat/fixtures/subtitle-sample.vtt" to the file picker for TinyMCE - And "File picker" "dialogue" should not exist - And I set the field "Language" in the "Media details" "dialogue" to "en" - And I set the field "Label" in the "Media details" "dialogue" to "Subtitle sample" - And I click on "Save" "button" in the "Media details" "dialogue" - And I select the "audio" element in position "1" of the "Description" TinyMCE editor - And I click on the "Multimedia" button for the "Description" TinyMCE editor - And "Media details" "dialogue" should exist - And I click on "Subtitles and captions" "link" in the "Media details" "dialogue" - And the field "Subtitle track URL" in the "Media details" "dialogue" does not match value "" - And the field "Language" in the "Media details" "dialogue" matches value "en" - And the field "Label" in the "Media details" "dialogue" matches value "Subtitle sample" - - @_file_upload - Scenario: Insert and update audio in the TinyMCE editor - Given I log in as "admin" - When I open my profile in edit mode - And I click on the "Multimedia" button for the "Description" TinyMCE editor - Then "Insert media" "dialogue" should exist - And I click on "Browse repositories" "button" in the "Insert media" "dialogue" - And "File picker" "dialogue" should exist - And I upload "/lib/editor/tiny/tests/behat/fixtures/audio-sample.mp3" to the file picker for TinyMCE - # Preview inserted audio. - And "Media details" "dialogue" should exist - And "tiny-insert-media" "region" should not exist in the "Media details" "dialogue" - And "tiny-media-details-body" "region" should exist in the "Media details" "dialogue" - And the field "Media title" in the "Media details" "dialogue" matches value "audio-sample" - And "Delete media" "button" should exist in the "Media details" "dialogue" - # Embed media into tiny and preview it by selecting it from tiny. - And I click on "Save" "button" in the "Media details" "dialogue" - And I select the "audio" element in position "1" of the "Description" TinyMCE editor - And I click on the "Multimedia" button for the "Description" TinyMCE editor - And "Media details" "dialogue" should exist - # Let's update audio data. - And I set the field "Media title" in the "Media details" "dialogue" to "Test title" - And I click on "Autoplay" "checkbox" in the "Media details" "dialogue" - And I click on "Muted" "checkbox" in the "Media details" "dialogue" - And I click on "Loop" "checkbox" in the "Media details" "dialogue" - And I click on "Save" "button" in the "Media details" "dialogue" - # Let's preview and check the updated data. - And I select the "audio" element in position "1" of the "Description" TinyMCE editor - And I click on the "Multimedia" button for the "Description" TinyMCE editor - And "Media details" "dialogue" should exist - And the field "Media title" in the "Media details" "dialogue" matches value "Test title" - And the field "Autoplay" in the "Media details" "dialogue" matches value "1" - And the field "Muted" in the "Media details" "dialogue" matches value "1" - And the field "Loop" in the "Media details" "dialogue" matches value "1" diff --git a/lib/editor/tiny/plugins/media/tests/behat/link.feature b/lib/editor/tiny/plugins/media/tests/behat/link.feature deleted file mode 100644 index 9b4f237f02d..00000000000 --- a/lib/editor/tiny/plugins/media/tests/behat/link.feature +++ /dev/null @@ -1,74 +0,0 @@ -@editor @editor_tiny @tiny_media @javascript -Feature: Use the TinyMCE editor to upload a media link - In order to work with media links - As a user - I need to be able to upload and manipulate media links - - Scenario: Clicking on the media button in the TinyMCE editor opens the dialog - Given I log in as "admin" - When I open my profile in edit mode - And I click on the "Multimedia" button for the "Description" TinyMCE editor - Then "Insert media" "dialogue" should exist - - # The following scenario covers: - # 1. Add YouTube link. - # 3. Preview embedded YouTube link from tiny. - # 4. Embed YouTube link as a hyperlink. - # 5. Preview embedded YouTube link from tiny. - Scenario: Embed YouTube link as a hyperlink - Given I log in as "admin" - When I open my profile in edit mode - And I click on the "Multimedia" button for the "Description" TinyMCE editor - Then "Insert media" "dialogue" should exist - # Add audio link using "Or add via URL" input. - And I set the field "Or add via URL" to "https://www.youtube.com/watch?v=-fPTdIruJUU" - And I click on "Add" "button" in the "Insert media" "dialogue" - # Preview inserted link. - And "Media details" "dialogue" should exist - And "tiny-insert-media" "region" should not exist in the "Media details" "dialogue" - And "tiny-media-details-body" "region" should exist in the "Media details" "dialogue" - And the field "Media title" in the "Media details" "dialogue" matches value "https://www.youtube.com/watch?v=-fPTdIruJUU" - And "Add custom thumbnail" "button" should not exist in the "Media details" "dialogue" - And "Show controls" "checkbox" should not exist in the "Media details" "dialogue" - And "Autoplay" "checkbox" should not exist in the "Media details" "dialogue" - And "Muted" "checkbox" should not exist in the "Media details" "dialogue" - And "Loop" "checkbox" should not exist in the "Media details" "dialogue" - # Embed link as video into tiny and preview it by selecting it from tiny. - And I click on "Save" "button" - And I select the "a" element in position "0" of the "Description" TinyMCE editor - And I click on the "Multimedia" button for the "Description" TinyMCE editor - And "Media details" "dialogue" should exist - And the field "Media title" in the "Media details" "dialogue" matches value "https://www.youtube.com/watch?v=-fPTdIruJUU" - And "Show controls" "checkbox" should not exist in the "Media details" "dialogue" - And "Autoplay" "checkbox" should not exist in the "Media details" "dialogue" - And "Muted" "checkbox" should not exist in the "Media details" "dialogue" - And "Loop" "checkbox" should not exist in the "Media details" "dialogue" - And I set the field "Media title" in the "Media details" "dialogue" to "https://www.youtube.com/watch?v=-fPTdIruJUU" - And I click on "Save" "button" in the "Media details" "dialogue" - And I select the "a" element in position "0" of the "Description" TinyMCE editor - And I click on the "Multimedia" button for the "Description" TinyMCE editor - And "Media details" "dialogue" should exist - And the field "Media title" in the "Media details" "dialogue" matches value "https://www.youtube.com/watch?v=-fPTdIruJUU" - # Delete previewed link. - And I click on "Delete media" "button" in the "Media details" "dialogue" - And "Delete media" "dialogue" should exist - And "Delete" "button" should exist in the "Delete media" "dialogue" - And I click on "Delete" "button" in the "Delete media" "dialogue" - And "Insert media" "dialogue" should exist - And "tiny-insert-media" "region" should exist in the "Insert media" "dialogue" - # Let's add the same YouTube link. - And I set the field "Or add via URL" to "https://www.youtube.com/watch?v=-fPTdIruJUU" - And I click on "Add" "button" in the "Insert media" "dialogue" - # Preview inserted media link. - And "Media details" "dialogue" should exist - And "tiny-insert-media" "region" should not exist in the "Media details" "dialogue" - And "tiny-media-details-body" "region" should exist in the "Media details" "dialogue" - And the field "Media title" in the "Media details" "dialogue" matches value "https://www.youtube.com/watch?v=-fPTdIruJUU" - And "Add custom thumbnail" "button" should not exist in the "Media details" "dialogue" - And "Show controls" "checkbox" should not exist in the "Media details" "dialogue" - And "Autoplay" "checkbox" should not exist in the "Media details" "dialogue" - And "Muted" "checkbox" should not exist in the "Media details" "dialogue" - And "Loop" "checkbox" should not exist in the "Media details" "dialogue" - And "Original size" "radio" should not exist in the "Media details" "dialogue" - And "Custom size" "radio" should not exist in the "Media details" "dialogue" - And I click on "Save" "button" in the "Media details" "dialogue" diff --git a/lib/editor/tiny/plugins/media/tests/behat/media.feature b/lib/editor/tiny/plugins/media/tests/behat/media.feature new file mode 100644 index 00000000000..c3d27a21f3a --- /dev/null +++ b/lib/editor/tiny/plugins/media/tests/behat/media.feature @@ -0,0 +1,136 @@ +@editor @editor_tiny @tiny_media @javascript +Feature: Use the TinyMCE editor to upload a media file + In order to work with media files + As a user + I need to be able to upload and change settings of media files + + Background: + Given I log in as "admin" + And I open my profile in edit mode + And I click on the "Multimedia" button for the "Description" TinyMCE editor + + Scenario: Clicking on the media button in the TinyMCE editor opens the insert media dialog + Then "Insert media" "dialogue" should exist + And "Browse repositories" "button" should exist in the "Insert media" "dialogue" + And I click on "Browse repositories" "button" in the "Insert media" "dialogue" + And "File picker" "dialogue" should exist + + @_file_upload + Scenario Outline: Insert and update media in the TinyMCE editor + Given I click on "Browse repositories" "button" + When I upload "/lib/editor/tiny/tests/behat/fixtures/" to the file picker for TinyMCE + Then "Media details" "dialogue" should exist + And "Media title" "field" should exist in the "Media details" "dialogue" + And "Add custom thumbnail" "button" in the "Media details" "dialogue" + And "Subtitles and captions" "link" should exist in the "Media details" "dialogue" + And the field "Media title" in the "Media details" "dialogue" matches value "" + And the field "Show controls" in the "Media details" "dialogue" matches value "1" + And the field "Autoplay" in the "Media details" "dialogue" matches value "0" + And the field "Muted" in the "Media details" "dialogue" matches value "0" + And the field "Loop" in the "Media details" "dialogue" matches value "0" + And + And + And I click on "Delete media" "button" in the "Media details" "dialogue" + And "Delete media" "dialogue" should exist + And I click on "Delete" "button" in the "Delete media" "dialogue" + And "Insert media" "dialogue" should exist + And I click on "Browse repositories" "button" + And I upload "/lib/editor/tiny/tests/behat/fixtures/" to the file picker for TinyMCE + And I click on "Overwrite" "button" + And I set the field "Media title" to "" + And I set the field "Autoplay" to "1" + And I set the field "Muted" to "1" + And I set the field "Loop" to "1" + And + And + And I click on "Subtitles and captions" "link" in the "Media details" "dialogue" + And I click on "Browse repositories..." "button" in the "Media details" "dialogue" + And I upload "/lib/editor/tiny/tests/behat/fixtures/" to the file picker for TinyMCE + And I set the field "Language" in the "Media details" "dialogue" to "" + And I set the field "Label" in the "Media details" "dialogue" to "" + And I click on "Save" "button" in the "Media details" "dialogue" + And I switch to the "Description" TinyMCE editor iframe + And "//*[contains(@data-id, 'id_description_editor')]//[@title='' and @autoplay='autoplay' and @loop='loop' and @muted='true' and @controls='controls']" "xpath_element" should exist + And "//*[contains(@data-id, 'id_description_editor')]////source[contains(@src, '')]" "xpath_element" should exist + And "//*[contains(@data-id, 'id_description_editor')]////track[contains(@src, '') and @kind='subtitles' and contains(@srclang, '') and @label='']" "xpath_element" should exist + And I switch to the main frame + And I select the "video" element in position "1" of the "Description" TinyMCE editor + And I click on the "Multimedia" button for the "Description" TinyMCE editor + And the field "Media title" in the "Media details" "dialogue" matches value "" + And the field "Show controls" in the "Media details" "dialogue" matches value "1" + And the field "Autoplay" in the "Media details" "dialogue" matches value "1" + And the field "Muted" in the "Media details" "dialogue" matches value "1" + And the field "Loop" in the "Media details" "dialogue" matches value "1" + And + And + And I click on "Subtitles and captions" "link" in the "Media details" "dialogue" + And the field "Subtitle track URL" in the "Media details" "dialogue" does not match value "" + And the field "Language" in the "Media details" "dialogue" matches value "" + And the field "Label" in the "Media details" "dialogue" matches value "" + + Examples: + | mediatype | fixturefile | fixturetitle | newfixturetitle | subtitlefile | subtitletitle | subtitlelangcode | subtitlelang | thumbnailaction | originalsizeverify | customsizeverify1 | customsizeverify2 | customsizeverify3 | customsizestep | customsizeaction | + | video | moodle-logo.mp4 | moodle-logo | Moodle LMS Logo | subtitle-sample.vtt | Subtitle sample for video | en | English | should exist | the field "Original size" in the "Media details" "dialogue" matches value "1" | the field "Custom size" in the "Media details" "dialogue" matches value "0" | the field "Custom size" in the "Media details" "dialogue" matches value "1" | the field "Width" in the "Media details" "dialogue" matches value "300" | I click on "Custom size" "radio" in the "Media details" "dialogue" | I set the field "Width" in the "Media details" "dialogue" to "300" | + | audio | audio-sample.mp3 | audio-sample | Sample Audio File | subtitle-sample.vtt | Subtitle sample for audio | fr | French | should not exist | "Original size" "field" should not exist in the "Media details" "dialogue" | "Custom size" "field" should not exist in the "Media details" "dialogue" | "Custom size" "field" should not exist in the "Media details" "dialogue" | "Width" "field" should not exist in the "Media details" "dialogue" | "Original size" "field" should not exist in the "Media details" "dialogue" | "Custom size" "field" should not exist in the "Media details" "dialogue" | + + @_file_upload + Scenario: Add custom thumbnail to a video in TinyMCE editor + Given I click on "Browse repositories" "button" in the "Insert media" "dialogue" + And I upload "/lib/editor/tiny/tests/behat/fixtures/moodle-logo.mp4" to the file picker for TinyMCE + When I click on "Add custom thumbnail" "button" in the "Media details" "dialogue" + Then "Insert media thumbnail" "dialogue" should exist + And I click on "Browse repositories" "button" in the "Insert media thumbnail" "dialogue" + And I upload "lib/editor/tiny/tests/behat/fixtures/moodle-logo.png" to the file picker for TinyMCE + And "Media thumbnail" "dialogue" should exist + And "tiny-media-thumbnail-preview" "region" should exist in the "Media thumbnail" "dialogue" + And I click on "Delete media thumbnail" "button" in the "Media thumbnail" "dialogue" + And "Delete media thumbnail" "dialogue" should exist + And I click on "Delete" "button" in the "Delete media thumbnail" "dialogue" + And I click on "Browse repositories" "button" in the "Insert media thumbnail" "dialogue" + And I upload "lib/editor/tiny/tests/behat/fixtures/moodle-logo.png" to the file picker for TinyMCE + And I click on "Overwrite" "button" in the "File exists" "dialogue" + And I click on "Next" "button" in the "Media thumbnail" "dialogue" + But "Add custom thumbnail" "button" should not exist in the "Media details" "dialogue" + And "Change thumbnail" "button" should exist in the "Media details" "dialogue" + And I click on "Delete thumbnail" "button" in the "Media details" "dialogue" + And I click on "Delete" "button" in the "Delete thumbnail" "dialogue" + And "Media details" "dialogue" should exist + And "Change thumbnail" "button" should not exist in the "Media details" "dialogue" + And "Delete thumbnail" "button" should not exist in the "Media details" "dialogue" + And I click on "Add custom thumbnail" "button" in the "Media details" "dialogue" + And "Insert media thumbnail" "dialogue" should exist + And I click on "Browse repositories" "button" in the "Insert media thumbnail" "dialogue" + And I upload "lib/editor/tiny/tests/behat/fixtures/moodle-logo.png" to the file picker for TinyMCE + And I click on "Overwrite" "button" in the "File exists" "dialogue" + And I click on "Next" "button" in the "Media thumbnail" "dialogue" + And I click on "Save" "button" in the "Media details" "dialogue" + And I switch to the "Description" TinyMCE editor iframe + And "//*[contains(@data-id, 'id_description_editor')]//video[contains(@poster, 'moodle-logo.png')]" "xpath_element" should exist + + Scenario: Embed external video link - External video service + Given the "mediaplugin" filter is "on" + And I enable "youtube" "media" plugin + And I disable "videojs" "media" plugin + And I log in as "admin" + And I open my profile in edit mode + And I click on the "Multimedia" button for the "Description" TinyMCE editor + When I set the field "Or add via URL" to "https://www.youtube.com/watch?v=JeimE8Wz6e4" + And I click on "Add" "button" in the "Insert media" "dialogue" + Then "Media details" "dialogue" should exist + And "Media title" "field" should exist in the "Media details" "dialogue" + And "Show controls" "field" should not exist in the "Media details" "dialogue" + And "Autoplay" "field" should not exist in the "Media details" "dialogue" + And "Muted" "field" should not exist in the "Media details" "dialogue" + And "Loop" "field" should not exist in the "Media details" "dialogue" + And the field "Media title" in the "Media details" "dialogue" matches value "https://www.youtube.com/watch?v=JeimE8Wz6e4" + And I set the field "Media title" to "Hey, that is pretty good!" + And I click on "Save" "button" in the "Media details" "dialogue" + And I switch to the "Description" TinyMCE editor iframe + And "//*[contains(@data-id, 'id_description_editor')]//a[@class='external-media-provider' and @href='https://www.youtube.com/watch?v=JeimE8Wz6e4' and normalize-space(text())='Hey, that is pretty good!']" "xpath_element" should exist + And I switch to the main frame + And I select the "a" element in position "0" of the "Description" TinyMCE editor + And I click on the "Multimedia" button for the "Description" TinyMCE editor + And the field "Media title" in the "Media details" "dialogue" matches value "Hey, that is pretty good!" + And I click on "Cancel" "button" in the "Media details" "dialogue" + And I press "Update profile" + And "//span[contains(@class, 'mediaplugin_youtube')]//iframe[@title='Hey, that is pretty good!']" "xpath_element" should exist diff --git a/lib/editor/tiny/plugins/media/tests/behat/video.feature b/lib/editor/tiny/plugins/media/tests/behat/video.feature deleted file mode 100644 index 1a47609dcf1..00000000000 --- a/lib/editor/tiny/plugins/media/tests/behat/video.feature +++ /dev/null @@ -1,199 +0,0 @@ -@editor @editor_tiny @tiny_media @javascript -Feature: Use the TinyMCE editor to upload a video - In order to work with videos - As a user - I need to be able to upload and manipulate videos - - Scenario: Clicking on the Video button in the TinyMCE editor opens the video dialog - Given I log in as "admin" - And I open my profile in edit mode - When I click on the "Multimedia" button for the "Description" TinyMCE editor - Then "Insert media" "dialogue" should exist - - Scenario: Browsing repositories in the TinyMCE editor shows the FilePicker - Given I log in as "admin" - And I open my profile in edit mode - When I click on the "Multimedia" button for the "Description" TinyMCE editor - And I click on "Browse repositories" "button" in the "Insert media" "dialogue" - Then "File picker" "dialogue" should exist - - @_file_upload - Scenario: Browsing repositories in the TinyMCE editor shows the FilePicker and upload video file - Given I log in as "admin" - When I open my profile in edit mode - And I click on the "Multimedia" button for the "Description" TinyMCE editor - And I click on "Browse repositories" "button" - And I upload "/lib/editor/tiny/tests/behat/fixtures/moodle-logo.mp4" to the file picker for TinyMCE - And I click on "Save" "button" - Then I select the "video" element in position "1" of the "Description" TinyMCE editor - - @_file_upload - # The following scenario covers: - # 1. Preview uploaded video. - # 2. Delete previewed video. - Scenario: Preview video before embedding it into TinyMCE editor - Given I log in as "admin" - When I open my profile in edit mode - And I click on the "Multimedia" button for the "Description" TinyMCE editor - Then "Insert media" "dialogue" should exist - And I click on "Browse repositories" "button" in the "Insert media" "dialogue" - And "File picker" "dialogue" should exist - And I upload "/lib/editor/tiny/tests/behat/fixtures/moodle-logo.mp4" to the file picker for TinyMCE - # Preview inserted video. - And "Media details" "dialogue" should exist - And "tiny-insert-media" "region" should not exist in the "Media details" "dialogue" - And "tiny-media-details-body" "region" should exist in the "Media details" "dialogue" - And the field "Media title" in the "Media details" "dialogue" matches value "moodle-logo" - # Delete media preview button. - And "Delete media" "button" should exist in the "Media details" "dialogue" - # Only video has add custom thumbnail button. - And "Add custom thumbnail" "button" should exist in the "Media details" "dialogue" - # Media controls. - And the field "Show controls" in the "Media details" "dialogue" matches value "1" - And the field "Autoplay" in the "Media details" "dialogue" matches value "0" - And the field "Muted" in the "Media details" "dialogue" matches value "0" - And the field "Loop" in the "Media details" "dialogue" matches value "0" - # Only video has size configuration. - And the field "Original size" in the "Media details" "dialogue" matches value "1" - And the field "Custom size" in the "Media details" "dialogue" matches value "0" - # Subtitles, captions, description, tracks and metadata button for video/audio. - And "Subtitles and captions" "link" should exist in the "Media details" "dialogue" - # Let's delete the previwed video. - And I click on "Delete media" "button" in the "Media details" "dialogue" - And "Delete media" "dialogue" should exist - And "Delete" "button" should exist in the "Delete media" "dialogue" - And I click on "Delete" "button" in the "Delete media" "dialogue" - And "Insert media" "dialogue" should exist - And "tiny-insert-media" "region" should exist - - @_file_upload - # The following screnario covers: - # 1. Add video custom thumbnail. - # 2. Preview uploaded custom thumbnail. - # 3. Delete previewed thumbnail. - # 4. Delete added thumbnail. - Scenario: Add custom thumbnail from local storage to a video - Given I log in as "admin" - When I open my profile in edit mode - And I click on the "Multimedia" button for the "Description" TinyMCE editor - Then "Insert media" "dialogue" should exist - And I click on "Browse repositories" "button" in the "Insert media" "dialogue" - And "File picker" "dialogue" should exist - And I upload "/lib/editor/tiny/tests/behat/fixtures/moodle-logo.mp4" to the file picker for TinyMCE - And "Media details" "dialogue" should exist - And "tiny-insert-media" "region" should not exist in the "Media details" "dialogue" - And "tiny-media-details-body" "region" should exist in the "Media details" "dialogue" - # Add custom thumbnail. - And "Add custom thumbnail" "button" should exist in the "Media details" "dialogue" - And I click on "Add custom thumbnail" "button" in the "Media details" "dialogue" - And "Insert media thumbnail" "dialogue" should exist - # And "tiny-insert-media" "region" should exist in the "Insert media thumbnail" "dialogue" - And I click on "Browse repositories" "button" in the "Insert media thumbnail" "dialogue" - And "File picker" "dialogue" should exist - And I upload "lib/editor/tiny/tests/behat/fixtures/moodle-logo.png" to the file picker for TinyMCE - # Delete previewed thumbnail. - And "Media thumbnail" "dialogue" should exist - And "tiny-media-thumbnail-preview" "region" should exist in the "Media thumbnail" "dialogue" - And "Delete media thumbnail" "button" should exist in the "Media thumbnail" "dialogue" - And I click on "Delete media thumbnail" "button" in the "Media thumbnail" "dialogue" - And "Delete media thumbnail" "dialogue" should exist - And "Delete" "button" should exist in the "Delete media thumbnail" "dialogue" - And I click on "Delete" "button" in the "Delete media thumbnail" "dialogue" - # Add back the same thumbnail link again. - And "Insert media thumbnail" "dialogue" should exist - # And "tiny-insert-media" "region" should exist in the "Insert media thumbnail" "dialogue" - And I click on "Browse repositories" "button" in the "Insert media thumbnail" "dialogue" - And "File picker" "dialogue" should exist - And I upload "lib/editor/tiny/tests/behat/fixtures/moodle-logo.png" to the file picker for TinyMCE - # Same file upload will cause "File exists" dialogue to display. - And "File exists" "dialogue" should exist - And I click on "Overwrite" "button" in the "File exists" "dialogue" - And "Media thumbnail" "dialogue" should exist - And "tiny-media-thumbnail-preview-footer" "region" should exist in the "Media thumbnail" "dialogue" - And "Next" "button" should exist in the "Media thumbnail" "dialogue" - And I click on "Next" "button" in the "Media thumbnail" "dialogue" - And "Media details" "dialogue" should exist - And "tiny-insert-media" "region" should not exist in the "Media details" "dialogue" - And "tiny-media-details-body" "region" should exist in the "Media details" "dialogue" - # Now "Add custom thumbnail" button should not exist. - And "Add custom thumbnail" "button" should not exist in the "Media details" "dialogue" - # And Change and Delete thumbnail buttons now exist. - And "Change thumbnail" "button" should exist in the "Media details" "dialogue" - And "Delete thumbnail" "button" should exist in the "Media details" "dialogue" - # Delete added thumbnail. - And I click on "Delete thumbnail" "button" in the "Media details" "dialogue" - And "Delete thumbnail" "dialogue" should exist - And "Delete" "button" should exist in the "Delete thumbnail" "dialogue" - And I click on "Delete" "button" in the "Delete thumbnail" "dialogue" - And "Media details" "dialogue" should exist - And "tiny-insert-media" "region" should not exist in the "Media details" "dialogue" - And "tiny-media-details-body" "region" should exist in the "Media details" "dialogue" - # After the added thumbnail is deleted, both Change and Delete thumbnial buttons should not exist anymore. - And "Change thumbnail" "button" should not exist in the "Media details" "dialogue" - And "Delete thumbnail" "button" should not exist in the "Media details" "dialogue" - # After the added thumbnail is deleted, "Add custom thubmnail" should exist again. - And "Add custom thumbnail" "button" should exist in the "Media details" "dialogue" - - @_file_upload - Scenario: Add subtitles to video before embedding it into TinyMCE editor - Given I log in as "admin" - When I open my profile in edit mode - And I click on the "Multimedia" button for the "Description" TinyMCE editor - Then "Insert media" "dialogue" should exist - And I click on "Browse repositories" "button" in the "Insert media" "dialogue" - And "File picker" "dialogue" should exist - And I upload "/lib/editor/tiny/tests/behat/fixtures/moodle-logo.mp4" to the file picker for TinyMCE - And "Media details" "dialogue" should exist - And "tiny-insert-media" "region" should not exist in the "Media details" "dialogue" - And "tiny-media-details-body" "region" should exist in the "Media details" "dialogue" - # Add media subtitles. - And I click on "Subtitles and captions" "link" in the "Media details" "dialogue" - And I click on "Browse repositories..." "button" in the "Media details" "dialogue" - And "File picker" "dialogue" should exist - And I upload "/lib/editor/tiny/tests/behat/fixtures/subtitle-sample.vtt" to the file picker for TinyMCE - And "File picker" "dialogue" should not exist - And I set the field "Language" in the "Media details" "dialogue" to "en" - And I set the field "Label" in the "Media details" "dialogue" to "Subtitle sample" - And I click on "Save" "button" in the "Media details" "dialogue" - And I select the "video" element in position "1" of the "Description" TinyMCE editor - And I click on the "Multimedia" button for the "Description" TinyMCE editor - And "Media details" "dialogue" should exist - And I click on "Subtitles and captions" "link" in the "Media details" "dialogue" - And the field "Subtitle track URL" in the "Media details" "dialogue" does not match value "" - And the field "Language" in the "Media details" "dialogue" matches value "en" - And the field "Label" in the "Media details" "dialogue" matches value "Subtitle sample" - - @_file_upload - Scenario: Insert and update video in the TinyMCE editor - Given I log in as "admin" - When I open my profile in edit mode - And I click on the "Multimedia" button for the "Description" TinyMCE editor - Then "Insert media" "dialogue" should exist - And I click on "Browse repositories" "button" - And "File picker" "dialogue" should exist - And I upload "/lib/editor/tiny/tests/behat/fixtures/moodle-logo.mp4" to the file picker for TinyMCE - And "Media details" "dialogue" should exist - And I click on "Save" "button" in the "Media details" "dialogue" - And I select the "video" element in position "1" of the "Description" TinyMCE editor - And I click on the "Multimedia" button for the "Description" TinyMCE editor - And "Media details" "dialogue" should exist - And "tiny-insert-media" "region" should not exist in the "Media details" "dialogue" - And "tiny-media-details-body" "region" should exist in the "Media details" "dialogue" - And I set the field "Media title" in the "Media details" "dialogue" to "Test title" - And I click on "Autoplay" "checkbox" in the "Media details" "dialogue" - And I click on "Muted" "checkbox" in the "Media details" "dialogue" - And I click on "Loop" "checkbox" in the "Media details" "dialogue" - And the field "Original size" in the "Media details" "dialogue" matches value "1" - And the field "Custom size" in the "Media details" "dialogue" matches value "0" - And I click on "Custom size" "radio" in the "Media details" "dialogue" - And I set the field "Width" in the "Media details" "dialogue" to "300" - And I click on "Save" "button" in the "Media details" "dialogue" - And I select the "video" element in position "1" of the "Description" TinyMCE editor - And I click on the "Multimedia" button for the "Description" TinyMCE editor - And "Media details" "dialogue" should exist - And the field "Media title" in the "Media details" "dialogue" matches value "Test title" - And the field "Autoplay" in the "Media details" "dialogue" matches value "1" - And the field "Muted" in the "Media details" "dialogue" matches value "1" - And the field "Loop" in the "Media details" "dialogue" matches value "1" - And the field "Custom size" in the "Media details" "dialogue" matches value "1" - And the field "Width" in the "Media details" "dialogue" matches value "300"