diff --git a/lib/editor/tiny/plugins/media/amd/build/embed.min.js b/lib/editor/tiny/plugins/media/amd/build/embed.min.js index 4acf88fb946..cf8c0e86c3e 100644 --- a/lib/editor/tiny/plugins/media/amd/build/embed.min.js +++ b/lib/editor/tiny/plugins/media/amd/build/embed.min.js @@ -1,3 +1,3 @@ -define("tiny_media/embed",["exports","core/templates","core/str","core/modal_factory","core/modal_events","editor_tiny/utils","editor_tiny/options","./common","./embedmodal","./selectors","./options"],(function(_exports,_templates,_str,ModalFactory,ModalEvents,_utils,_options,_common,_embedmodal,_selectors,_options2){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 _interopRequireWildcard(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]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}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.MediaEmbed=void 0,_templates=_interopRequireDefault(_templates),ModalFactory=_interopRequireWildcard(ModalFactory),ModalEvents=_interopRequireWildcard(ModalEvents),_embedmodal=_interopRequireDefault(_embedmodal),_selectors=_interopRequireDefault(_selectors);_exports.MediaEmbed=class{constructor(editor){_defineProperty(this,"editor",null),_defineProperty(this,"canShowFilePicker",!1),_defineProperty(this,"helpStrings",null),_defineProperty(this,"isUpdating",!1),_defineProperty(this,"selectedMedia",null);const permissions=(0,_options2.getEmbedPermissions)(editor);this.canShowFilePicker=permissions.filepicker,this.editor=editor}async getHelpStrings(){if(!this.helpStrings){const[addSource,tracks,subtitles,captions,descriptions,chapters,metadata]=await(0,_str.get_strings)(["addsource_help","tracks_help","subtitles_help","captions_help","descriptions_help","chapters_help","metadata_help"].map((key=>({key:key,component:_common.component}))));this.helpStrings={addSource:addSource,tracks:tracks,subtitles:subtitles,captions:captions,descriptions:descriptions,chapters:chapters,metadata:metadata}}return this.helpStrings}async getTemplateContext(data){const languages=this.prepareMoodleLang(),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,showfilepicker:this.canShowFilePicker,langsinstalled:languages.installed,langsavailable:languages.available,link:!0,video:!1,audio:!1,isupdating:this.isUpdating},data,helpIcons)}async displayDialogue(){this.selectedMedia=this.getSelectedMedia();const data=Object.assign({},this.getCurrentEmbedData());this.isUpdating=0!==Object.keys(data).length;const modal=await ModalFactory.create({type:_embedmodal.default.TYPE,title:(0,_str.get_string)("createmedia","tiny_media"),templateContext:await this.getTemplateContext(data),removeOnClose:!0,large:!0});this.currentModal=modal,await this.registerEventListeners(modal),modal.show()}getCurrentEmbedData(){const properties=this.getMediumProperties();if(!properties)return{};const processedProperties={};return processedProperties[properties.type.toLowerCase()]=properties,processedProperties.link=!1,processedProperties}getSelectedMedia(){const mediaElm=this.editor.selection.getNode();return mediaElm?"video"===mediaElm.nodeName.toLowerCase()||"audio"===mediaElm.nodeName.toLowerCase()?mediaElm:mediaElm.querySelector("video")?mediaElm.querySelector("video"):mediaElm.querySelector("audio")?mediaElm.querySelector("audio"):null:null}getMediumProperties(){const boolAttr=(elem,attr)=>elem.hasAttribute(attr)&&(elem.getAttribute(attr)||""===elem.getAttribute(attr)),tracks={subtitles:[],captions:[],descriptions:[],chapters:[],metadata:[]},sources=[],medium=this.selectedMedia;return medium?(medium.querySelectorAll("track").forEach((track=>{tracks[track.getAttribute("kind")].push({src:track.getAttribute("src"),srclang:track.getAttribute("srclang"),label:track.getAttribute("label"),defaultTrack:boolAttr(track,"default")})})),medium.querySelectorAll("source").forEach((source=>{sources.push(source.src)})),{type:"video"===medium.nodeName.toLowerCase()?_selectors.default.EMBED.mediaTypes.video:_selectors.default.EMBED.mediaTypes.audio,sources:sources,poster:medium.getAttribute("poster"),title:medium.getAttribute("title"),width:medium.getAttribute("width"),height:medium.getAttribute("height"),autoplay:boolAttr(medium,"autoplay"),loop:boolAttr(medium,"loop"),muted:boolAttr(medium,"muted"),controls:boolAttr(medium,"controls"),tracks:tracks}):null}prepareMoodleLang(){const moodleLangs=(0,_options.getMoodleLang)(this.editor),currentLanguage=(0,_options.getCurrentLanguage)(this.editor);return{installed:Object.entries(moodleLangs.installed).map((_ref2=>{let[lang,code]=_ref2;return{lang:lang,code:code,default:lang===currentLanguage}})),available:Object.entries(moodleLangs.available).map((_ref3=>{let[lang,code]=_ref3;return{lang:lang,code:code,default:lang===currentLanguage}}))}}getMoodleLangObj(subtitleLang){const{available:available}=(0,_options.getMoodleLang)(this.editor);return available[subtitleLang]?{lang:subtitleLang,code:available[subtitleLang]}:null}filePickerCallback(params,element,fpType){if(""!==params.url){const tabPane=element.closest(".tab-pane");if(element.closest(_selectors.default.EMBED.elements.source).querySelector(_selectors.default.EMBED.elements.url).value=params.url,tabPane.id===this.editor.getElement().id+"_"+_selectors.default.EMBED.mediaTypes.link.toLowerCase()&&(tabPane.querySelector(_selectors.default.EMBED.elements.name).value=params.file),"subtitle"===fpType){const subtitleLang=params.file.split(".vtt")[0].split("-").slice(-1)[0],langObj=this.getMoodleLangObj(subtitleLang);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}}}}addMediaSourceComponent(element,callback){const sourceElement=element.closest(_selectors.default.EMBED.elements.source+_selectors.default.EMBED.elements.mediaSource),clone=sourceElement.cloneNode(!0);sourceElement.querySelector(".removecomponent-wrapper").classList.remove("hidden"),sourceElement.querySelector(".addcomponent-wrapper").classList.add("hidden"),sourceElement.parentNode.insertBefore(clone,sourceElement.nextSibling),callback&&callback(clone)}removeMediaSourceComponent(element){element.closest(_selectors.default.EMBED.elements.source+_selectors.default.EMBED.elements.mediaSource).remove()}addTrackComponent(element,callback){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),callback&&callback(clone)}removeTrackComponent(element){element.closest(_selectors.default.EMBED.elements.track).remove()}getMediumTypeFromTabPane(tabPane){return tabPane.getAttribute("data-medium-type")}getTrackTypeFromTabPane(tabPane){return tabPane.getAttribute("data-track-kind")}getMediaHTML(form){const mediumType=this.getMediumTypeFromTabPane(form.querySelector(".root.tab-content > .tab-pane.active")),tabContent=form.querySelector(_selectors.default.EMBED.elements[mediumType.toLowerCase()+"Pane"]);return this["getMediaHTML"+mediumType[0].toUpperCase()+mediumType.substr(1)](tabContent)}getMediaHTMLLink(tab){const context={url:tab.querySelector(_selectors.default.EMBED.elements.url).value,name:tab.querySelector(_selectors.default.EMBED.elements.name).value||!1};return context.url?_templates.default.renderForPromise("tiny_media/embed_media_link",context):""}getMediaHTMLVideo(tab){const context=this.getContextForMediaHTML(tab);return context.width=tab.querySelector(_selectors.default.EMBED.elements.width).value||!1,context.height=tab.querySelector(_selectors.default.EMBED.elements.height).value||!1,context.poster=tab.querySelector("".concat(_selectors.default.EMBED.elements.posterSource," ").concat(_selectors.default.EMBED.elements.url)).value||!1,context.sources.length?_templates.default.renderForPromise("tiny_media/embed_media_video",context):""}getMediaHTMLAudio(tab){const context=this.getContextForMediaHTML(tab);return context.sources.length?_templates.default.renderForPromise("tiny_media/embed_media_audio",context):""}getContextForMediaHTML(tab){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));return{sources:Array.from(tab.querySelectorAll(_selectors.default.EMBED.elements.mediaSource+" "+_selectors.default.EMBED.elements.url)).filter((source=>!!source.value)).map((source=>source.value)),description:tab.querySelector(_selectors.default.EMBED.elements.mediaSource+" "+_selectors.default.EMBED.elements.url).value||!1,tracks:tracks,showControls:tab.querySelector(_selectors.default.EMBED.elements.mediaControl).checked,autoplay:tab.querySelector(_selectors.default.EMBED.elements.mediaAutoplay).checked,muted:tab.querySelector(_selectors.default.EMBED.elements.mediaMute).checked,loop:tab.querySelector(_selectors.default.EMBED.elements.mediaLoop).checked,title:tab.querySelector(_selectors.default.EMBED.elements.title).value||!1}}getFilepickerTypeFromElement(element){return element.closest(_selectors.default.EMBED.elements.posterSource)?"image":element.closest(_selectors.default.EMBED.elements.trackSource)?"subtitle":"media"}async clickHandler(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.filePickerCallback(params,element,fpType)}element.closest(_selectors.default.EMBED.elements.mediaSource+" .addcomponent")&&(e.preventDefault(),this.addMediaSourceComponent(element));element.closest(_selectors.default.EMBED.elements.mediaSource+" .removecomponent")&&(e.preventDefault(),this.removeMediaSourceComponent(element));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(".root.tab-content").querySelectorAll(_selectors.default.EMBED.elements.trackDefault).forEach((select=>{select!==element&&getKind(element)===getKind(select)&&(select.checked=!1)}))}}async handleDialogueSubmission(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))}async registerEventListeners(modal){await modal.getBody();const $root=modal.getRoot(),root=$root[0];this.canShowFilePicker&&root.addEventListener("click",this.clickHandler.bind(this)),$root.on(ModalEvents.save,this.handleDialogueSubmission.bind(this)),$root.on(ModalEvents.hidden,(()=>{this.currentModal.destroy()})),$root.on(ModalEvents.shown,(()=>{root.querySelectorAll(_selectors.default.EMBED.elements.trackLang).forEach((dropdown=>{const defaultVal=dropdown.getAttribute("data-value");defaultVal&&(dropdown.value=defaultVal)}))}))}}})); +define("tiny_media/embed",["exports","core/templates","core/str","core/modal_factory","core/modal_events","editor_tiny/utils","editor_tiny/options","./common","./embedmodal","./selectors","./options"],(function(_exports,_templates,_str,ModalFactory,ModalEvents,_utils,_options,_common,_embedmodal,_selectors,_options2){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 _interopRequireWildcard(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]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}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.MediaEmbed=void 0,_templates=_interopRequireDefault(_templates),ModalFactory=_interopRequireWildcard(ModalFactory),ModalEvents=_interopRequireWildcard(ModalEvents),_embedmodal=_interopRequireDefault(_embedmodal),_selectors=_interopRequireDefault(_selectors);_exports.MediaEmbed=class{constructor(editor){_defineProperty(this,"editor",null),_defineProperty(this,"canShowFilePicker",!1),_defineProperty(this,"canShowFilePickerPoster",!1),_defineProperty(this,"canShowFilePickerTrack",!1),_defineProperty(this,"helpStrings",null),_defineProperty(this,"isUpdating",!1),_defineProperty(this,"selectedMedia",null);const permissions=(0,_options2.getEmbedPermissions)(editor);this.canShowFilePicker=permissions.filepicker&&void 0!==(0,_options.getFilePicker)(editor,"media"),this.canShowFilePickerPoster=permissions.filepicker&&void 0!==(0,_options.getFilePicker)(editor,"image"),this.canShowFilePickerTrack=permissions.filepicker&&void 0!==(0,_options.getFilePicker)(editor,"subtitle"),this.editor=editor}async getHelpStrings(){if(!this.helpStrings){const[addSource,tracks,subtitles,captions,descriptions,chapters,metadata]=await(0,_str.get_strings)(["addsource_help","tracks_help","subtitles_help","captions_help","descriptions_help","chapters_help","metadata_help"].map((key=>({key:key,component:_common.component}))));this.helpStrings={addSource:addSource,tracks:tracks,subtitles:subtitles,captions:captions,descriptions:descriptions,chapters:chapters,metadata:metadata}}return this.helpStrings}async getTemplateContext(data){const languages=this.prepareMoodleLang(),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,showfilepicker:this.canShowFilePicker,showfilepickerposter:this.canShowFilePickerPoster,showfilepickertrack:this.canShowFilePickerTrack,langsinstalled:languages.installed,langsavailable:languages.available,link:!0,video:!1,audio:!1,isupdating:this.isUpdating},data,helpIcons)}async displayDialogue(){this.selectedMedia=this.getSelectedMedia();const data=Object.assign({},this.getCurrentEmbedData());this.isUpdating=0!==Object.keys(data).length;const modal=await ModalFactory.create({type:_embedmodal.default.TYPE,title:(0,_str.get_string)("createmedia","tiny_media"),templateContext:await this.getTemplateContext(data),removeOnClose:!0,large:!0});this.currentModal=modal,await this.registerEventListeners(modal),modal.show()}getCurrentEmbedData(){const properties=this.getMediumProperties();if(!properties)return{};const processedProperties={};return processedProperties[properties.type.toLowerCase()]=properties,processedProperties.link=!1,processedProperties}getSelectedMedia(){const mediaElm=this.editor.selection.getNode();return mediaElm?"video"===mediaElm.nodeName.toLowerCase()||"audio"===mediaElm.nodeName.toLowerCase()?mediaElm:mediaElm.querySelector("video")?mediaElm.querySelector("video"):mediaElm.querySelector("audio")?mediaElm.querySelector("audio"):null:null}getMediumProperties(){const boolAttr=(elem,attr)=>elem.hasAttribute(attr)&&(elem.getAttribute(attr)||""===elem.getAttribute(attr)),tracks={subtitles:[],captions:[],descriptions:[],chapters:[],metadata:[]},sources=[],medium=this.selectedMedia;return medium?(medium.querySelectorAll("track").forEach((track=>{tracks[track.getAttribute("kind")].push({src:track.getAttribute("src"),srclang:track.getAttribute("srclang"),label:track.getAttribute("label"),defaultTrack:boolAttr(track,"default")})})),medium.querySelectorAll("source").forEach((source=>{sources.push(source.src)})),{type:"video"===medium.nodeName.toLowerCase()?_selectors.default.EMBED.mediaTypes.video:_selectors.default.EMBED.mediaTypes.audio,sources:sources,poster:medium.getAttribute("poster"),title:medium.getAttribute("title"),width:medium.getAttribute("width"),height:medium.getAttribute("height"),autoplay:boolAttr(medium,"autoplay"),loop:boolAttr(medium,"loop"),muted:boolAttr(medium,"muted"),controls:boolAttr(medium,"controls"),tracks:tracks}):null}prepareMoodleLang(){const moodleLangs=(0,_options.getMoodleLang)(this.editor),currentLanguage=(0,_options.getCurrentLanguage)(this.editor);return{installed:Object.entries(moodleLangs.installed).map((_ref2=>{let[lang,code]=_ref2;return{lang:lang,code:code,default:lang===currentLanguage}})),available:Object.entries(moodleLangs.available).map((_ref3=>{let[lang,code]=_ref3;return{lang:lang,code:code,default:lang===currentLanguage}}))}}getMoodleLangObj(subtitleLang){const{available:available}=(0,_options.getMoodleLang)(this.editor);return available[subtitleLang]?{lang:subtitleLang,code:available[subtitleLang]}:null}filePickerCallback(params,element,fpType){if(""!==params.url){const tabPane=element.closest(".tab-pane");if(element.closest(_selectors.default.EMBED.elements.source).querySelector(_selectors.default.EMBED.elements.url).value=params.url,tabPane.id===this.editor.getElement().id+"_"+_selectors.default.EMBED.mediaTypes.link.toLowerCase()&&(tabPane.querySelector(_selectors.default.EMBED.elements.name).value=params.file),"subtitle"===fpType){const subtitleLang=params.file.split(".vtt")[0].split("-").slice(-1)[0],langObj=this.getMoodleLangObj(subtitleLang);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}}}}addMediaSourceComponent(element,callback){const sourceElement=element.closest(_selectors.default.EMBED.elements.source+_selectors.default.EMBED.elements.mediaSource),clone=sourceElement.cloneNode(!0);sourceElement.querySelector(".removecomponent-wrapper").classList.remove("hidden"),sourceElement.querySelector(".addcomponent-wrapper").classList.add("hidden"),sourceElement.parentNode.insertBefore(clone,sourceElement.nextSibling),callback&&callback(clone)}removeMediaSourceComponent(element){element.closest(_selectors.default.EMBED.elements.source+_selectors.default.EMBED.elements.mediaSource).remove()}addTrackComponent(element,callback){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),callback&&callback(clone)}removeTrackComponent(element){element.closest(_selectors.default.EMBED.elements.track).remove()}getMediumTypeFromTabPane(tabPane){return tabPane.getAttribute("data-medium-type")}getTrackTypeFromTabPane(tabPane){return tabPane.getAttribute("data-track-kind")}getMediaHTML(form){const mediumType=this.getMediumTypeFromTabPane(form.querySelector(".root.tab-content > .tab-pane.active")),tabContent=form.querySelector(_selectors.default.EMBED.elements[mediumType.toLowerCase()+"Pane"]);return this["getMediaHTML"+mediumType[0].toUpperCase()+mediumType.substr(1)](tabContent)}getMediaHTMLLink(tab){const context={url:tab.querySelector(_selectors.default.EMBED.elements.url).value,name:tab.querySelector(_selectors.default.EMBED.elements.name).value||!1};return context.url?_templates.default.renderForPromise("tiny_media/embed_media_link",context):""}getMediaHTMLVideo(tab){const context=this.getContextForMediaHTML(tab);return context.width=tab.querySelector(_selectors.default.EMBED.elements.width).value||!1,context.height=tab.querySelector(_selectors.default.EMBED.elements.height).value||!1,context.poster=tab.querySelector("".concat(_selectors.default.EMBED.elements.posterSource," ").concat(_selectors.default.EMBED.elements.url)).value||!1,context.sources.length?_templates.default.renderForPromise("tiny_media/embed_media_video",context):""}getMediaHTMLAudio(tab){const context=this.getContextForMediaHTML(tab);return context.sources.length?_templates.default.renderForPromise("tiny_media/embed_media_audio",context):""}getContextForMediaHTML(tab){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));return{sources:Array.from(tab.querySelectorAll(_selectors.default.EMBED.elements.mediaSource+" "+_selectors.default.EMBED.elements.url)).filter((source=>!!source.value)).map((source=>source.value)),description:tab.querySelector(_selectors.default.EMBED.elements.mediaSource+" "+_selectors.default.EMBED.elements.url).value||!1,tracks:tracks,showControls:tab.querySelector(_selectors.default.EMBED.elements.mediaControl).checked,autoplay:tab.querySelector(_selectors.default.EMBED.elements.mediaAutoplay).checked,muted:tab.querySelector(_selectors.default.EMBED.elements.mediaMute).checked,loop:tab.querySelector(_selectors.default.EMBED.elements.mediaLoop).checked,title:tab.querySelector(_selectors.default.EMBED.elements.title).value||!1}}getFilepickerTypeFromElement(element){return element.closest(_selectors.default.EMBED.elements.posterSource)?"image":element.closest(_selectors.default.EMBED.elements.trackSource)?"subtitle":"media"}async clickHandler(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.filePickerCallback(params,element,fpType)}element.closest(_selectors.default.EMBED.elements.mediaSource+" .addcomponent")&&(e.preventDefault(),this.addMediaSourceComponent(element));element.closest(_selectors.default.EMBED.elements.mediaSource+" .removecomponent")&&(e.preventDefault(),this.removeMediaSourceComponent(element));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(".root.tab-content").querySelectorAll(_selectors.default.EMBED.elements.trackDefault).forEach((select=>{select!==element&&getKind(element)===getKind(select)&&(select.checked=!1)}))}}async handleDialogueSubmission(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))}async registerEventListeners(modal){await modal.getBody();const $root=modal.getRoot(),root=$root[0];(this.canShowFilePicker||this.canShowFilePickerPoster||this.canShowFilePickerTrack)&&root.addEventListener("click",this.clickHandler.bind(this)),$root.on(ModalEvents.save,this.handleDialogueSubmission.bind(this)),$root.on(ModalEvents.hidden,(()=>{this.currentModal.destroy()})),$root.on(ModalEvents.shown,(()=>{root.querySelectorAll(_selectors.default.EMBED.elements.trackLang).forEach((dropdown=>{const defaultVal=dropdown.getAttribute("data-value");defaultVal&&(dropdown.value=defaultVal)}))}))}}})); //# sourceMappingURL=embed.min.js.map \ No newline at end of file diff --git a/lib/editor/tiny/plugins/media/amd/build/embed.min.js.map b/lib/editor/tiny/plugins/media/amd/build/embed.min.js.map index d85d27f14ad..55de9737df0 100644 --- a/lib/editor/tiny/plugins/media/amd/build/embed.min.js.map +++ b/lib/editor/tiny/plugins/media/amd/build/embed.min.js.map @@ -1 +1 @@ -{"version":3,"file":"embed.min.js","sources":["../src/embed.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 class for Moodle.\n *\n * @module tiny_media/embed\n * @copyright 2022 Huong Nguyen \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Templates from 'core/templates';\nimport {\n get_string as getString,\n get_strings as getStrings,\n} from 'core/str';\nimport * as ModalFactory from 'core/modal_factory';\nimport * as ModalEvents from 'core/modal_events';\nimport {displayFilepicker} from 'editor_tiny/utils';\nimport {getCurrentLanguage, getMoodleLang} from 'editor_tiny/options';\nimport {component} from \"./common\";\nimport Modal from './embedmodal';\nimport Selectors from './selectors';\nimport {getEmbedPermissions} from './options';\n\nexport const MediaEmbed = class {\n editor = null;\n canShowFilePicker = false;\n\n /**\n * @property {Object} The names of the alignment options.\n */\n helpStrings = null;\n\n /**\n * @property {boolean} Indicate that the user is updating the media or not.\n */\n isUpdating = false;\n\n /**\n * @property {Object} The currently selected media.\n */\n selectedMedia = null;\n\n constructor(editor) {\n const permissions = getEmbedPermissions(editor);\n this.canShowFilePicker = permissions.filepicker;\n this.editor = editor;\n }\n\n async getHelpStrings() {\n if (!this.helpStrings) {\n const [addSource, tracks, subtitles, captions, descriptions, chapters, metadata] = await getStrings([\n 'addsource_help',\n 'tracks_help',\n 'subtitles_help',\n 'captions_help',\n 'descriptions_help',\n 'chapters_help',\n 'metadata_help',\n ].map((key) => ({\n key,\n component,\n })));\n\n this.helpStrings = {addSource, tracks, subtitles, captions, descriptions, chapters, metadata};\n }\n\n return this.helpStrings;\n }\n\n async getTemplateContext(data) {\n const languages = this.prepareMoodleLang();\n\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 showfilepicker: this.canShowFilePicker,\n langsinstalled: languages.installed,\n langsavailable: languages.available,\n link: true,\n video: false,\n audio: false,\n isupdating: this.isUpdating,\n }, data, helpIcons);\n }\n\n async displayDialogue() {\n this.selectedMedia = this.getSelectedMedia();\n const data = Object.assign({}, this.getCurrentEmbedData());\n this.isUpdating = Object.keys(data).length !== 0;\n\n const modal = await ModalFactory.create({\n type: Modal.TYPE,\n title: getString('createmedia', 'tiny_media'),\n templateContext: await this.getTemplateContext(data),\n removeOnClose: true,\n large: true,\n });\n\n this.currentModal = modal;\n await this.registerEventListeners(modal);\n modal.show();\n }\n\n getCurrentEmbedData() {\n const properties = this.getMediumProperties();\n if (!properties) {\n return {};\n }\n\n const processedProperties = {};\n processedProperties[properties.type.toLowerCase()] = properties;\n processedProperties.link = false;\n\n return processedProperties;\n }\n\n getSelectedMedia() {\n const mediaElm = this.editor.selection.getNode();\n\n if (!mediaElm) {\n return null;\n }\n\n if (mediaElm.nodeName.toLowerCase() === 'video' || mediaElm.nodeName.toLowerCase() === 'audio') {\n return mediaElm;\n }\n\n if (mediaElm.querySelector('video')) {\n return mediaElm.querySelector('video');\n }\n\n if (mediaElm.querySelector('audio')) {\n return mediaElm.querySelector('audio');\n }\n\n return null;\n }\n\n getMediumProperties() {\n const boolAttr = (elem, attr) => {\n // As explained in MDL-64175, some OS (like Ubuntu), are removing the value for these attributes.\n // So in order to check if attr=\"true\", we need to check if the attribute exists and if the value is empty or true.\n return (elem.hasAttribute(attr) && (elem.getAttribute(attr) || elem.getAttribute(attr) === ''));\n };\n\n const tracks = {\n subtitles: [],\n captions: [],\n descriptions: [],\n chapters: [],\n metadata: []\n };\n const sources = [];\n\n const medium = this.selectedMedia;\n if (!medium) {\n return null;\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: boolAttr(track, 'default')\n });\n });\n\n medium.querySelectorAll('source').forEach((source) => {\n sources.push(source.src);\n });\n\n return {\n type: medium.nodeName.toLowerCase() === 'video' ? Selectors.EMBED.mediaTypes.video : Selectors.EMBED.mediaTypes.audio,\n sources,\n poster: medium.getAttribute('poster'),\n title: medium.getAttribute('title'),\n width: medium.getAttribute('width'),\n height: medium.getAttribute('height'),\n autoplay: boolAttr(medium, 'autoplay'),\n loop: boolAttr(medium, 'loop'),\n muted: boolAttr(medium, 'muted'),\n controls: boolAttr(medium, 'controls'),\n tracks,\n };\n }\n\n prepareMoodleLang() {\n const moodleLangs = getMoodleLang(this.editor);\n const currentLanguage = getCurrentLanguage(this.editor);\n\n const installed = Object.entries(moodleLangs.installed).map(([lang, code]) => ({\n lang,\n code,\n \"default\": lang === currentLanguage,\n }));\n\n const available = Object.entries(moodleLangs.available).map(([lang, code]) => ({\n lang,\n code,\n \"default\": lang === currentLanguage,\n }));\n\n return {\n installed,\n available,\n };\n }\n\n getMoodleLangObj(subtitleLang) {\n const {available} = getMoodleLang(this.editor);\n\n if (available[subtitleLang]) {\n return {\n lang: subtitleLang,\n code: available[subtitleLang],\n };\n }\n\n return null;\n }\n\n filePickerCallback(params, element, fpType) {\n if (params.url !== '') {\n const tabPane = element.closest('.tab-pane');\n element.closest(Selectors.EMBED.elements.source).querySelector(Selectors.EMBED.elements.url).value = params.url;\n\n if (tabPane.id === this.editor.getElement().id + '_' + Selectors.EMBED.mediaTypes.link.toLowerCase()) {\n tabPane.querySelector(Selectors.EMBED.elements.name).value = params.file;\n }\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 = this.getMoodleLangObj(subtitleLang);\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 }\n }\n\n addMediaSourceComponent(element, callback) {\n const sourceElement = element.closest(Selectors.EMBED.elements.source + Selectors.EMBED.elements.mediaSource);\n const clone = sourceElement.cloneNode(true);\n\n sourceElement.querySelector('.removecomponent-wrapper').classList.remove('hidden');\n sourceElement.querySelector('.addcomponent-wrapper').classList.add('hidden');\n\n sourceElement.parentNode.insertBefore(clone, sourceElement.nextSibling);\n\n if (callback) {\n callback(clone);\n }\n }\n\n removeMediaSourceComponent(element) {\n const sourceElement = element.closest(Selectors.EMBED.elements.source + Selectors.EMBED.elements.mediaSource);\n sourceElement.remove();\n }\n\n addTrackComponent(element, callback) {\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\n trackElement.parentNode.insertBefore(clone, trackElement.nextSibling);\n\n if (callback) {\n callback(clone);\n }\n }\n\n removeTrackComponent(element) {\n const sourceElement = element.closest(Selectors.EMBED.elements.track);\n sourceElement.remove();\n }\n\n getMediumTypeFromTabPane(tabPane) {\n return tabPane.getAttribute('data-medium-type');\n }\n\n getTrackTypeFromTabPane(tabPane) {\n return tabPane.getAttribute('data-track-kind');\n }\n\n getMediaHTML(form) {\n const mediumType = this.getMediumTypeFromTabPane(form.querySelector('.root.tab-content > .tab-pane.active'));\n const tabContent = form.querySelector(Selectors.EMBED.elements[mediumType.toLowerCase() + 'Pane']);\n\n return this['getMediaHTML' + mediumType[0].toUpperCase() + mediumType.substr(1)](tabContent);\n }\n\n getMediaHTMLLink(tab) {\n const context = {\n url: tab.querySelector(Selectors.EMBED.elements.url).value,\n name: tab.querySelector(Selectors.EMBED.elements.name).value || false\n };\n\n return context.url ? Templates.renderForPromise('tiny_media/embed_media_link', context) : '';\n }\n\n getMediaHTMLVideo(tab) {\n const context = this.getContextForMediaHTML(tab);\n context.width = tab.querySelector(Selectors.EMBED.elements.width).value || false;\n context.height = tab.querySelector(Selectors.EMBED.elements.height).value || false;\n context.poster = tab.querySelector(\n `${Selectors.EMBED.elements.posterSource} ${Selectors.EMBED.elements.url}`\n ).value || false;\n\n return context.sources.length ? Templates.renderForPromise('tiny_media/embed_media_video', context) : '';\n }\n\n getMediaHTMLAudio(tab) {\n const context = this.getContextForMediaHTML(tab);\n\n return context.sources.length ? Templates.renderForPromise('tiny_media/embed_media_audio', context) : '';\n }\n\n getContextForMediaHTML(tab) {\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 sources = Array.from(tab.querySelectorAll(Selectors.EMBED.elements.mediaSource + ' '\n + Selectors.EMBED.elements.url))\n .filter((source) => !!source.value)\n .map((source) => source.value);\n\n return {\n sources,\n description: tab.querySelector(Selectors.EMBED.elements.mediaSource + ' '\n + Selectors.EMBED.elements.url).value || false,\n tracks,\n showControls: tab.querySelector(Selectors.EMBED.elements.mediaControl).checked,\n autoplay: tab.querySelector(Selectors.EMBED.elements.mediaAutoplay).checked,\n muted: tab.querySelector(Selectors.EMBED.elements.mediaMute).checked,\n loop: tab.querySelector(Selectors.EMBED.elements.mediaLoop).checked,\n title: tab.querySelector(Selectors.EMBED.elements.title).value || false\n };\n }\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 async clickHandler(e) {\n const element = e.target;\n\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.filePickerCallback(params, element, fpType);\n }\n\n const addComponentSourceAction = element.closest(Selectors.EMBED.elements.mediaSource + ' .addcomponent');\n if (addComponentSourceAction) {\n e.preventDefault();\n this.addMediaSourceComponent(element);\n }\n\n const removeComponentSourceAction = element.closest(Selectors.EMBED.elements.mediaSource + ' .removecomponent');\n if (removeComponentSourceAction) {\n e.preventDefault();\n this.removeMediaSourceComponent(element);\n }\n\n const addComponentTrackAction = element.closest(Selectors.EMBED.elements.track + ' .addcomponent');\n if (addComponentTrackAction) {\n e.preventDefault();\n this.addTrackComponent(element);\n }\n\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('.root.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 async handleDialogueSubmission(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 async registerEventListeners(modal) {\n await modal.getBody();\n const $root = modal.getRoot();\n const root = $root[0];\n if (this.canShowFilePicker) {\n root.addEventListener('click', this.clickHandler.bind(this));\n }\n\n $root.on(ModalEvents.save, this.handleDialogueSubmission.bind(this));\n $root.on(ModalEvents.hidden, () => {\n this.currentModal.destroy();\n });\n $root.on(ModalEvents.shown, () => {\n root.querySelectorAll(Selectors.EMBED.elements.trackLang).forEach((dropdown) => {\n const defaultVal = dropdown.getAttribute('data-value');\n if (defaultVal) {\n dropdown.value = defaultVal;\n }\n });\n });\n }\n};\n"],"names":["constructor","editor","permissions","canShowFilePicker","filepicker","this","helpStrings","addSource","tracks","subtitles","captions","descriptions","chapters","metadata","map","key","component","data","languages","prepareMoodleLang","helpIcons","Array","from","Object","entries","getHelpStrings","forEach","_ref","text","toLowerCase","assign","elementid","getElement","id","showfilepicker","langsinstalled","installed","langsavailable","available","link","video","audio","isupdating","isUpdating","selectedMedia","getSelectedMedia","getCurrentEmbedData","keys","length","modal","ModalFactory","create","type","Modal","TYPE","title","templateContext","getTemplateContext","removeOnClose","large","currentModal","registerEventListeners","show","properties","getMediumProperties","processedProperties","mediaElm","selection","getNode","nodeName","querySelector","boolAttr","elem","attr","hasAttribute","getAttribute","sources","medium","querySelectorAll","track","push","src","srclang","label","defaultTrack","source","Selectors","EMBED","mediaTypes","poster","width","height","autoplay","loop","muted","controls","moodleLangs","currentLanguage","_ref2","lang","code","_ref3","getMoodleLangObj","subtitleLang","filePickerCallback","params","element","fpType","url","tabPane","closest","elements","value","name","file","split","slice","langObj","trackLabel","trim","trackLang","addMediaSourceComponent","callback","sourceElement","mediaSource","clone","cloneNode","classList","remove","add","parentNode","insertBefore","nextSibling","removeMediaSourceComponent","addTrackComponent","trackElement","removeTrackComponent","getMediumTypeFromTabPane","getTrackTypeFromTabPane","getMediaHTML","form","mediumType","tabContent","toUpperCase","substr","getMediaHTMLLink","tab","context","Templates","renderForPromise","getMediaHTMLVideo","getContextForMediaHTML","posterSource","getMediaHTMLAudio","trackSource","kind","trackDefault","checked","filter","description","showControls","mediaControl","mediaAutoplay","mediaMute","mediaLoop","getFilepickerTypeFromElement","e","target","actions","mediaBrowser","preventDefault","trackDefaultAction","getKind","el","parentElement","select","event","html","getRoot","outerHTML","insertContent","getBody","$root","root","addEventListener","clickHandler","bind","on","ModalEvents","save","handleDialogueSubmission","hidden","destroy","shown","dropdown","defaultVal"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"embed.min.js","sources":["../src/embed.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 class for Moodle.\n *\n * @module tiny_media/embed\n * @copyright 2022 Huong Nguyen \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Templates from 'core/templates';\nimport {\n get_string as getString,\n get_strings as getStrings,\n} from 'core/str';\nimport * as ModalFactory from 'core/modal_factory';\nimport * as ModalEvents from 'core/modal_events';\nimport {displayFilepicker} from 'editor_tiny/utils';\nimport {getCurrentLanguage, getMoodleLang} from 'editor_tiny/options';\nimport {component} from \"./common\";\nimport Modal from './embedmodal';\nimport Selectors from './selectors';\nimport {getEmbedPermissions} from './options';\nimport {getFilePicker} from 'editor_tiny/options';\n\nexport const MediaEmbed = class {\n editor = null;\n canShowFilePicker = false;\n canShowFilePickerPoster = false;\n canShowFilePickerTrack = false;\n\n /**\n * @property {Object} The names of the alignment options.\n */\n helpStrings = null;\n\n /**\n * @property {boolean} Indicate that the user is updating the media or not.\n */\n isUpdating = false;\n\n /**\n * @property {Object} The currently selected media.\n */\n selectedMedia = null;\n\n constructor(editor) {\n const permissions = getEmbedPermissions(editor);\n\n // Indicates whether the file picker can be shown.\n this.canShowFilePicker = permissions.filepicker && (typeof getFilePicker(editor, 'media') !== 'undefined');\n this.canShowFilePickerPoster = permissions.filepicker && (typeof getFilePicker(editor, 'image') !== 'undefined');\n this.canShowFilePickerTrack = permissions.filepicker && (typeof getFilePicker(editor, 'subtitle') !== 'undefined');\n\n this.editor = editor;\n }\n\n async getHelpStrings() {\n if (!this.helpStrings) {\n const [addSource, tracks, subtitles, captions, descriptions, chapters, metadata] = await getStrings([\n 'addsource_help',\n 'tracks_help',\n 'subtitles_help',\n 'captions_help',\n 'descriptions_help',\n 'chapters_help',\n 'metadata_help',\n ].map((key) => ({\n key,\n component,\n })));\n\n this.helpStrings = {addSource, tracks, subtitles, captions, descriptions, chapters, metadata};\n }\n\n return this.helpStrings;\n }\n\n async getTemplateContext(data) {\n const languages = this.prepareMoodleLang();\n\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 showfilepicker: this.canShowFilePicker,\n showfilepickerposter: this.canShowFilePickerPoster,\n showfilepickertrack: this.canShowFilePickerTrack,\n langsinstalled: languages.installed,\n langsavailable: languages.available,\n link: true,\n video: false,\n audio: false,\n isupdating: this.isUpdating,\n }, data, helpIcons);\n }\n\n async displayDialogue() {\n this.selectedMedia = this.getSelectedMedia();\n const data = Object.assign({}, this.getCurrentEmbedData());\n this.isUpdating = Object.keys(data).length !== 0;\n\n const modal = await ModalFactory.create({\n type: Modal.TYPE,\n title: getString('createmedia', 'tiny_media'),\n templateContext: await this.getTemplateContext(data),\n removeOnClose: true,\n large: true,\n });\n\n this.currentModal = modal;\n await this.registerEventListeners(modal);\n modal.show();\n }\n\n getCurrentEmbedData() {\n const properties = this.getMediumProperties();\n if (!properties) {\n return {};\n }\n\n const processedProperties = {};\n processedProperties[properties.type.toLowerCase()] = properties;\n processedProperties.link = false;\n\n return processedProperties;\n }\n\n getSelectedMedia() {\n const mediaElm = this.editor.selection.getNode();\n\n if (!mediaElm) {\n return null;\n }\n\n if (mediaElm.nodeName.toLowerCase() === 'video' || mediaElm.nodeName.toLowerCase() === 'audio') {\n return mediaElm;\n }\n\n if (mediaElm.querySelector('video')) {\n return mediaElm.querySelector('video');\n }\n\n if (mediaElm.querySelector('audio')) {\n return mediaElm.querySelector('audio');\n }\n\n return null;\n }\n\n getMediumProperties() {\n const boolAttr = (elem, attr) => {\n // As explained in MDL-64175, some OS (like Ubuntu), are removing the value for these attributes.\n // So in order to check if attr=\"true\", we need to check if the attribute exists and if the value is empty or true.\n return (elem.hasAttribute(attr) && (elem.getAttribute(attr) || elem.getAttribute(attr) === ''));\n };\n\n const tracks = {\n subtitles: [],\n captions: [],\n descriptions: [],\n chapters: [],\n metadata: []\n };\n const sources = [];\n\n const medium = this.selectedMedia;\n if (!medium) {\n return null;\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: boolAttr(track, 'default')\n });\n });\n\n medium.querySelectorAll('source').forEach((source) => {\n sources.push(source.src);\n });\n\n return {\n type: medium.nodeName.toLowerCase() === 'video' ? Selectors.EMBED.mediaTypes.video : Selectors.EMBED.mediaTypes.audio,\n sources,\n poster: medium.getAttribute('poster'),\n title: medium.getAttribute('title'),\n width: medium.getAttribute('width'),\n height: medium.getAttribute('height'),\n autoplay: boolAttr(medium, 'autoplay'),\n loop: boolAttr(medium, 'loop'),\n muted: boolAttr(medium, 'muted'),\n controls: boolAttr(medium, 'controls'),\n tracks,\n };\n }\n\n prepareMoodleLang() {\n const moodleLangs = getMoodleLang(this.editor);\n const currentLanguage = getCurrentLanguage(this.editor);\n\n const installed = Object.entries(moodleLangs.installed).map(([lang, code]) => ({\n lang,\n code,\n \"default\": lang === currentLanguage,\n }));\n\n const available = Object.entries(moodleLangs.available).map(([lang, code]) => ({\n lang,\n code,\n \"default\": lang === currentLanguage,\n }));\n\n return {\n installed,\n available,\n };\n }\n\n getMoodleLangObj(subtitleLang) {\n const {available} = getMoodleLang(this.editor);\n\n if (available[subtitleLang]) {\n return {\n lang: subtitleLang,\n code: available[subtitleLang],\n };\n }\n\n return null;\n }\n\n filePickerCallback(params, element, fpType) {\n if (params.url !== '') {\n const tabPane = element.closest('.tab-pane');\n element.closest(Selectors.EMBED.elements.source).querySelector(Selectors.EMBED.elements.url).value = params.url;\n\n if (tabPane.id === this.editor.getElement().id + '_' + Selectors.EMBED.mediaTypes.link.toLowerCase()) {\n tabPane.querySelector(Selectors.EMBED.elements.name).value = params.file;\n }\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 = this.getMoodleLangObj(subtitleLang);\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 }\n }\n\n addMediaSourceComponent(element, callback) {\n const sourceElement = element.closest(Selectors.EMBED.elements.source + Selectors.EMBED.elements.mediaSource);\n const clone = sourceElement.cloneNode(true);\n\n sourceElement.querySelector('.removecomponent-wrapper').classList.remove('hidden');\n sourceElement.querySelector('.addcomponent-wrapper').classList.add('hidden');\n\n sourceElement.parentNode.insertBefore(clone, sourceElement.nextSibling);\n\n if (callback) {\n callback(clone);\n }\n }\n\n removeMediaSourceComponent(element) {\n const sourceElement = element.closest(Selectors.EMBED.elements.source + Selectors.EMBED.elements.mediaSource);\n sourceElement.remove();\n }\n\n addTrackComponent(element, callback) {\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\n trackElement.parentNode.insertBefore(clone, trackElement.nextSibling);\n\n if (callback) {\n callback(clone);\n }\n }\n\n removeTrackComponent(element) {\n const sourceElement = element.closest(Selectors.EMBED.elements.track);\n sourceElement.remove();\n }\n\n getMediumTypeFromTabPane(tabPane) {\n return tabPane.getAttribute('data-medium-type');\n }\n\n getTrackTypeFromTabPane(tabPane) {\n return tabPane.getAttribute('data-track-kind');\n }\n\n getMediaHTML(form) {\n const mediumType = this.getMediumTypeFromTabPane(form.querySelector('.root.tab-content > .tab-pane.active'));\n const tabContent = form.querySelector(Selectors.EMBED.elements[mediumType.toLowerCase() + 'Pane']);\n\n return this['getMediaHTML' + mediumType[0].toUpperCase() + mediumType.substr(1)](tabContent);\n }\n\n getMediaHTMLLink(tab) {\n const context = {\n url: tab.querySelector(Selectors.EMBED.elements.url).value,\n name: tab.querySelector(Selectors.EMBED.elements.name).value || false\n };\n\n return context.url ? Templates.renderForPromise('tiny_media/embed_media_link', context) : '';\n }\n\n getMediaHTMLVideo(tab) {\n const context = this.getContextForMediaHTML(tab);\n context.width = tab.querySelector(Selectors.EMBED.elements.width).value || false;\n context.height = tab.querySelector(Selectors.EMBED.elements.height).value || false;\n context.poster = tab.querySelector(\n `${Selectors.EMBED.elements.posterSource} ${Selectors.EMBED.elements.url}`\n ).value || false;\n\n return context.sources.length ? Templates.renderForPromise('tiny_media/embed_media_video', context) : '';\n }\n\n getMediaHTMLAudio(tab) {\n const context = this.getContextForMediaHTML(tab);\n\n return context.sources.length ? Templates.renderForPromise('tiny_media/embed_media_audio', context) : '';\n }\n\n getContextForMediaHTML(tab) {\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 sources = Array.from(tab.querySelectorAll(Selectors.EMBED.elements.mediaSource + ' '\n + Selectors.EMBED.elements.url))\n .filter((source) => !!source.value)\n .map((source) => source.value);\n\n return {\n sources,\n description: tab.querySelector(Selectors.EMBED.elements.mediaSource + ' '\n + Selectors.EMBED.elements.url).value || false,\n tracks,\n showControls: tab.querySelector(Selectors.EMBED.elements.mediaControl).checked,\n autoplay: tab.querySelector(Selectors.EMBED.elements.mediaAutoplay).checked,\n muted: tab.querySelector(Selectors.EMBED.elements.mediaMute).checked,\n loop: tab.querySelector(Selectors.EMBED.elements.mediaLoop).checked,\n title: tab.querySelector(Selectors.EMBED.elements.title).value || false\n };\n }\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 async clickHandler(e) {\n const element = e.target;\n\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.filePickerCallback(params, element, fpType);\n }\n\n const addComponentSourceAction = element.closest(Selectors.EMBED.elements.mediaSource + ' .addcomponent');\n if (addComponentSourceAction) {\n e.preventDefault();\n this.addMediaSourceComponent(element);\n }\n\n const removeComponentSourceAction = element.closest(Selectors.EMBED.elements.mediaSource + ' .removecomponent');\n if (removeComponentSourceAction) {\n e.preventDefault();\n this.removeMediaSourceComponent(element);\n }\n\n const addComponentTrackAction = element.closest(Selectors.EMBED.elements.track + ' .addcomponent');\n if (addComponentTrackAction) {\n e.preventDefault();\n this.addTrackComponent(element);\n }\n\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('.root.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 async handleDialogueSubmission(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 async registerEventListeners(modal) {\n await modal.getBody();\n const $root = modal.getRoot();\n const root = $root[0];\n if (this.canShowFilePicker || this.canShowFilePickerPoster || this.canShowFilePickerTrack) {\n root.addEventListener('click', this.clickHandler.bind(this));\n }\n\n $root.on(ModalEvents.save, this.handleDialogueSubmission.bind(this));\n $root.on(ModalEvents.hidden, () => {\n this.currentModal.destroy();\n });\n $root.on(ModalEvents.shown, () => {\n root.querySelectorAll(Selectors.EMBED.elements.trackLang).forEach((dropdown) => {\n const defaultVal = dropdown.getAttribute('data-value');\n if (defaultVal) {\n dropdown.value = defaultVal;\n }\n });\n });\n }\n};\n"],"names":["constructor","editor","permissions","canShowFilePicker","filepicker","canShowFilePickerPoster","canShowFilePickerTrack","this","helpStrings","addSource","tracks","subtitles","captions","descriptions","chapters","metadata","map","key","component","data","languages","prepareMoodleLang","helpIcons","Array","from","Object","entries","getHelpStrings","forEach","_ref","text","toLowerCase","assign","elementid","getElement","id","showfilepicker","showfilepickerposter","showfilepickertrack","langsinstalled","installed","langsavailable","available","link","video","audio","isupdating","isUpdating","selectedMedia","getSelectedMedia","getCurrentEmbedData","keys","length","modal","ModalFactory","create","type","Modal","TYPE","title","templateContext","getTemplateContext","removeOnClose","large","currentModal","registerEventListeners","show","properties","getMediumProperties","processedProperties","mediaElm","selection","getNode","nodeName","querySelector","boolAttr","elem","attr","hasAttribute","getAttribute","sources","medium","querySelectorAll","track","push","src","srclang","label","defaultTrack","source","Selectors","EMBED","mediaTypes","poster","width","height","autoplay","loop","muted","controls","moodleLangs","currentLanguage","_ref2","lang","code","_ref3","getMoodleLangObj","subtitleLang","filePickerCallback","params","element","fpType","url","tabPane","closest","elements","value","name","file","split","slice","langObj","trackLabel","trim","trackLang","addMediaSourceComponent","callback","sourceElement","mediaSource","clone","cloneNode","classList","remove","add","parentNode","insertBefore","nextSibling","removeMediaSourceComponent","addTrackComponent","trackElement","removeTrackComponent","getMediumTypeFromTabPane","getTrackTypeFromTabPane","getMediaHTML","form","mediumType","tabContent","toUpperCase","substr","getMediaHTMLLink","tab","context","Templates","renderForPromise","getMediaHTMLVideo","getContextForMediaHTML","posterSource","getMediaHTMLAudio","trackSource","kind","trackDefault","checked","filter","description","showControls","mediaControl","mediaAutoplay","mediaMute","mediaLoop","getFilepickerTypeFromElement","e","target","actions","mediaBrowser","preventDefault","trackDefaultAction","getKind","el","parentElement","select","event","html","getRoot","outerHTML","insertContent","getBody","$root","root","addEventListener","clickHandler","bind","on","ModalEvents","save","handleDialogueSubmission","hidden","destroy","shown","dropdown","defaultVal"],"mappings":"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"} \ No newline at end of file diff --git a/lib/editor/tiny/plugins/media/amd/build/image.min.js b/lib/editor/tiny/plugins/media/amd/build/image.min.js index 0569571f5b3..7c2017ad4b3 100644 --- a/lib/editor/tiny/plugins/media/amd/build/image.min.js +++ b/lib/editor/tiny/plugins/media/amd/build/image.min.js @@ -1,3 +1,3 @@ -define("tiny_media/image",["exports","core/templates","core/str","core/modal_factory","core/pending","editor_tiny/utils","./selectors","./imagemodal","./options","./common"],(function(_exports,_templates,_str,ModalFactory,_pending,_utils,_selectors,_imagemodal,_options,_common){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.MediaImage=void 0,_templates=_interopRequireDefault(_templates),ModalFactory=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}(ModalFactory),_pending=_interopRequireDefault(_pending),_selectors=_interopRequireDefault(_selectors),_imagemodal=_interopRequireDefault(_imagemodal);_exports.MediaImage=class{constructor(editor){_defineProperty(this,"DEFAULTS",{WIDTH:160,HEIGHT:160}),_defineProperty(this,"form",null),_defineProperty(this,"rawImageDimensions",null),_defineProperty(this,"canShowFilePicker",!1),_defineProperty(this,"editor",null),_defineProperty(this,"currentModal",null),_defineProperty(this,"selectedImage",null),_defineProperty(this,"imageAlignment",null);const permissions=(0,_options.getImagePermissions)(editor);this.canShowFilePicker=permissions.filepicker,this.editor=editor}async displayDialogue(){this.rawImageDimensions=null;const currentImageData=await this.getCurrentImageData(),modal=await ModalFactory.create({type:_imagemodal.default.TYPE,title:(0,_str.get_string)("imageproperties","tiny_media"),templateContext:await this.getTemplateContext(currentImageData),removeOnClose:!0,large:!0});this.currentModal=modal,currentImageData&¤tImageData.src&&this.loadPreviewImage(currentImageData.src),await this.registerEventListeners(modal),modal.show()}async getAlignmentTitles(){if(!this.alignmentTitles){const[top,middle,bottom]=await(0,_str.get_strings)(["alignment_top","alignment_middle","alignment_bottom"].map((key=>({key:key,component:_common.component}))));this.alignmentTitles={top:top,middle:middle,bottom:bottom}}return this.alignmentTitles}async getImageAlignment(){let selected=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"";const titles=await this.getAlignmentTitles(),alignments=[{text:titles.top,value:"align-top"},{text:titles.middle,value:"align-middle"},{text:titles.bottom,value:"align-bottom"}];return selected&&alignments.forEach(((alignment,index,array)=>{alignment.value===selected&&(array[index].selected=!0)})),alignments}async getTemplateContext(data){return{elementid:this.editor.id,showfilepicker:this.canShowFilePicker,alignoptions:await this.getImageAlignment(),...data}}async getCurrentImageData(){const selectedImageProperties=this.getSelectedImageProperties();if(!selectedImageProperties)return{};const properties={...selectedImageProperties};return properties.align&&(properties.alignoptions=await this.getImageAlignment(properties.align)),properties.src&&(properties.haspreview=!0),properties.alt||(properties.presentation=!0),properties}filePickerCallback(params,self){if(params.url){self.form.querySelector(_selectors.default.IMAGE.elements.url).value=params.url,self.form.querySelector(_selectors.default.IMAGE.elements.width).value="",self.form.querySelector(_selectors.default.IMAGE.elements.height).value="",self.loadPreviewImage(params.url)}}storeImageDimensions(image){this.rawImageDimensions={width:image.width||this.DEFAULTS.WIDTH,height:image.height||this.DEFAULTS.HEIGHT};const currentWidth=(element=>(""===element.value&&(element.value=this.rawImageDimensions.width),element.value))(this.form.querySelector(_selectors.default.IMAGE.elements.width)),currentHeight=(element=>(""===element.value&&(element.value=this.rawImageDimensions.height),element.value))(this.form.querySelector(_selectors.default.IMAGE.elements.height)),preview=this.form.querySelector(_selectors.default.IMAGE.elements.preview);preview.setAttribute("src",image.src),preview.style.display="inline";const constrain=this.form.querySelector(_selectors.default.IMAGE.elements.constrain);if(this.isPercentageValue(currentWidth)&&this.isPercentageValue(currentHeight))constrain.checked=currentWidth===currentHeight;else if(0===image.width||0===image.height)constrain.disabled="disabled";else{const widthRatio=Math.round(1e3*parseInt(currentWidth,10)/image.width),heightRatio=Math.round(1e3*parseInt(currentHeight,10)/image.height);constrain.checked=widthRatio===heightRatio}}loadPreviewImage(url){const image=new Image;image.addEventListener("error",(()=>{this.form.querySelector(_selectors.default.IMAGE.elements.preview).style.display="none"})),image.addEventListener("load",(()=>{this.storeImageDimensions(image),this.autoAdjustSize()})),image.src=url}urlChanged(){const input=this.form.querySelector(_selectors.default.IMAGE.elements.url);input.value&&this.loadPreviewImage(input.value)}hasErrorUrlField(){const urlError=""===this.form.querySelector(_selectors.default.IMAGE.elements.url).value;return this.toggleVisibility(_selectors.default.IMAGE.elements.urlWarning,urlError),this.toggleAriaInvalid([_selectors.default.IMAGE.elements.url],urlError),urlError}hasErrorAltField(){const alt=this.form.querySelector(_selectors.default.IMAGE.elements.alt).value,presentation=this.form.querySelector(_selectors.default.IMAGE.elements.presentation).checked,imageAltError=""===alt&&!presentation;return this.toggleVisibility(_selectors.default.IMAGE.elements.altWarning,imageAltError),this.toggleAriaInvalid([_selectors.default.IMAGE.elements.alt,_selectors.default.IMAGE.elements.presentation],imageAltError),imageAltError}toggleVisibility(selector,predicate){this.form.querySelectorAll(selector).forEach((element=>{element.style.display=predicate?"block":"none"}))}toggleAriaInvalid(selectors,predicate){selectors.forEach((selector=>{this.form.querySelectorAll(selector).forEach((element=>element.setAttribute("aria-invalid",predicate)))}))}getAlignmentClass(alignment){return alignment}updateWarning(){const urlError=this.hasErrorUrlField(),imageAltError=this.hasErrorAltField();return urlError||imageAltError}getImageContext(){if(this.updateWarning())return null;const classList=[];this.form.querySelector(_selectors.default.IMAGE.elements.constrain).value&&classList.push(_selectors.default.IMAGE.styles.responsive);const alignment=this.getAlignmentClass(this.form.querySelector(_selectors.default.IMAGE.elements.alignment).value);return classList.push(alignment),{url:this.form.querySelector(_selectors.default.IMAGE.elements.url).value,alt:this.form.querySelector(_selectors.default.IMAGE.elements.alt).value,width:this.form.querySelector(_selectors.default.IMAGE.elements.width).value,height:this.form.querySelector(_selectors.default.IMAGE.elements.height).value,presentation:this.form.querySelector(_selectors.default.IMAGE.elements.presentation).checked,customStyle:this.form.querySelector(_selectors.default.IMAGE.elements.customStyle).value,classlist:classList.join(" ")}}setImage(){const pendingPromise=new _pending.default("tiny_media:setImage");if(""===this.form.querySelector(_selectors.default.IMAGE.elements.url).value)return;if(this.updateWarning())return void pendingPromise.resolve();const width=this.form.querySelector(_selectors.default.IMAGE.elements.width).value;if(!this.isPercentageValue(width)&&isNaN(parseInt(width,10)))return this.form.querySelector(_selectors.default.IMAGE.elements.width).focus(),void pendingPromise.resolve();const height=this.form.querySelector(_selectors.default.IMAGE.elements.height).value;if(!this.isPercentageValue(height)&&isNaN(parseInt(height,10)))return this.form.querySelector(_selectors.default.IMAGE.elements.height).focus(),void pendingPromise.resolve();_templates.default.render("tiny_media/image",this.getImageContext()).then((html=>(this.editor.insertContent(html),this.currentModal.destroy(),pendingPromise.resolve(),html))).catch()}handleKeyupCharacterCount(){const alt=this.form.querySelector(_selectors.default.IMAGE.elements.alt).value;this.form.querySelector("#currentcount").innerHTML=alt.length}autoAdjustSize(){let forceHeight=arguments.length>0&&void 0!==arguments[0]&&arguments[0];if(!this.rawImageDimensions)return;const widthField=this.form.querySelector(_selectors.default.IMAGE.elements.width),heightField=this.form.querySelector(_selectors.default.IMAGE.elements.height),normalizeFieldData=fieldData=>(fieldData.isPercentageValue=!!this.isPercentageValue(fieldData.field.value),fieldData.isPercentageValue?(fieldData.percentValue=parseInt(fieldData.field.value,10),fieldData.pixelSize=this.rawImageDimensions[fieldData.type]/100*fieldData.percentValue):(fieldData.pixelSize=parseInt(fieldData.field.value,10),fieldData.percentValue=fieldData.pixelSize/this.rawImageDimensions[fieldData.type]*100),fieldData),imagePreview=this.form.querySelector(_selectors.default.IMAGE.elements.preview);imagePreview.style.width="",imagePreview.style.height="";const constrainField=this.form.querySelector(_selectors.default.IMAGE.elements.constrain),keyField=(()=>{const currentValue=forceHeight?{field:heightField,type:"height"}:{field:widthField,type:"width"};return""===currentValue.field.value&&(currentValue.field.value=this.rawImageDimensions[currentValue.type]),normalizeFieldData(currentValue)})(),relativeField=normalizeFieldData(forceHeight?{field:widthField,type:"width"}:{field:heightField,type:"height"});constrainField.checked&&(keyField.isPercentageValue?(relativeField.field.value=keyField.field.value,relativeField.percentValue=keyField.percentValue):(relativeField.pixelSize=Math.round(keyField.pixelSize/this.rawImageDimensions[keyField.type]*this.rawImageDimensions[relativeField.type]),relativeField.field.value=relativeField.pixelSize)),function(image,keyField,relativeField){let forceHeight=arguments.length>3&&void 0!==arguments[3]&&arguments[3];const getStyleValue=field=>field.isPercentageValue?"".concat(field.percentValue,"%"):"".concat(field.pixelSize,"px");forceHeight?"width"===keyField.type?image.style.width=getStyleValue(keyField):image.style.width=getStyleValue(relativeField):(image.style[keyField.type]=getStyleValue(keyField),image.style[relativeField.type]=getStyleValue(relativeField))}(imagePreview,keyField,relativeField,!!constrainField.checked)}getSelectedImageProperties(){const image=this.getSelectedImage();if(!image)return this.selectedImage=null,null;const properties={src:null,alt:null,width:null,height:null,align:"",presentation:!1};this.removeLegacyAlignment(image),this.selectedImage=image,properties.customStyle=image.style.cssText;const width=(image=>this.isPercentageValue(String(image.width))?image.width:parseInt(image.width,10))(image);0!==width&&(properties.width=width);const height=(image=>this.isPercentageValue(String(image.height))?image.height:parseInt(image.height,10))(image);0!==height&&(properties.height=height);const alignment=this.getAlignmentProperties(image,properties);return alignment&&(properties.align=alignment.value),properties.src=image.getAttribute("src"),properties.alt=image.getAttribute("alt")||"",properties.presentation="presentation"===image.getAttribute("role"),properties}removeLegacyAlignment(imageNode){return imageNode.style.margin?(_selectors.default.IMAGE.alignments.some((alignment=>{if(imageNode.style[alignment.name]!==alignment.value)return!1;const normalisedNode=document.createElement("div");return normalisedNode.style.margin=alignment.margin,imageNode.style.margin===normalisedNode.style.margin&&(imageNode.classList.add(this.getAlignmentClass(alignment.value)),imageNode.style[alignment.name]=null,imageNode.style.margin=null,!0)})),imageNode):imageNode}getAlignmentProperties(image){const currentAlignment=_selectors.default.IMAGE.alignments.find((alignment=>!!image.classList.contains(this.getAlignmentClass(alignment.value))||!!alignment.legacyValues&&alignment.legacyValues.some((legacyValue=>image.classList.contains(legacyValue)))));return currentAlignment||_selectors.default.IMAGE.alignments.find((alignment=>alignment.isDefault))}getSelectedImage(){const imgElm=this.editor.selection.getNode(),figureElm=this.editor.dom.getParent(imgElm,"figure.image");return figureElm?this.editor.dom.select("img",figureElm)[0]:imgElm&&("IMG"!==imgElm.nodeName.toUpperCase()||this.isPlaceholderImage(imgElm))?null:imgElm}isPlaceholderImage(imgElm){return"IMG"===imgElm.nodeName.toUpperCase()&&(imgElm.hasAttribute("data-mce-object")||imgElm.hasAttribute("data-mce-placeholder"))}isPercentageValue(value){return value.match(/\d+%/)}async registerEventListeners(modal){await modal.getBody();const root=modal.getRoot()[0];this.form=root.querySelector(_selectors.default.IMAGE.elements.form),root.addEventListener("click",(e=>{const submitAction=e.target.closest(_selectors.default.IMAGE.actions.submit),imageBrowserAction=e.target.closest(_selectors.default.IMAGE.actions.imageBrowser);submitAction&&(e.preventDefault(),this.setImage()),imageBrowserAction&&this.canShowFilePicker&&(e.preventDefault(),(0,_utils.displayFilepicker)(this.editor,"image").then((params=>{this.filePickerCallback(params,this)})).catch())})),root.addEventListener("change",(e=>{e.target.closest(_selectors.default.IMAGE.elements.url)&&this.hasErrorUrlField();e.target.closest(_selectors.default.IMAGE.elements.presentation)&&this.hasErrorAltField();e.target.closest(_selectors.default.IMAGE.elements.constrain)&&this.autoAdjustSize(!0)})),root.addEventListener("blur",(e=>{if(e.target.nodeType===Node.ELEMENT_NODE){e.target.closest(_selectors.default.IMAGE.elements.url)&&this.urlChanged();e.target.closest(_selectors.default.IMAGE.elements.alt)&&this.hasErrorAltField();e.target.closest(_selectors.default.IMAGE.elements.width)&&this.autoAdjustSize();e.target.closest(_selectors.default.IMAGE.elements.height)&&this.autoAdjustSize(!0)}}),!0),root.addEventListener("keyup",(e=>{e.target.closest(_selectors.default.IMAGE.elements.alt)&&this.handleKeyupCharacterCount()}))}}})); +define("tiny_media/image",["exports","core/templates","core/str","core/modal_factory","core/pending","editor_tiny/utils","./selectors","./imagemodal","./options","./common","editor_tiny/options"],(function(_exports,_templates,_str,ModalFactory,_pending,_utils,_selectors,_imagemodal,_options,_common,_options2){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.MediaImage=void 0,_templates=_interopRequireDefault(_templates),ModalFactory=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}(ModalFactory),_pending=_interopRequireDefault(_pending),_selectors=_interopRequireDefault(_selectors),_imagemodal=_interopRequireDefault(_imagemodal);_exports.MediaImage=class{constructor(editor){_defineProperty(this,"DEFAULTS",{WIDTH:160,HEIGHT:160}),_defineProperty(this,"form",null),_defineProperty(this,"rawImageDimensions",null),_defineProperty(this,"canShowFilePicker",!1),_defineProperty(this,"editor",null),_defineProperty(this,"currentModal",null),_defineProperty(this,"selectedImage",null),_defineProperty(this,"imageAlignment",null);const permissions=(0,_options.getImagePermissions)(editor);this.canShowFilePicker=permissions.filepicker&&void 0!==(0,_options2.getFilePicker)(editor,"image"),this.editor=editor}async displayDialogue(){this.rawImageDimensions=null;const currentImageData=await this.getCurrentImageData(),modal=await ModalFactory.create({type:_imagemodal.default.TYPE,title:(0,_str.get_string)("imageproperties","tiny_media"),templateContext:await this.getTemplateContext(currentImageData),removeOnClose:!0,large:!0});this.currentModal=modal,currentImageData&¤tImageData.src&&this.loadPreviewImage(currentImageData.src),await this.registerEventListeners(modal),modal.show()}async getAlignmentTitles(){if(!this.alignmentTitles){const[top,middle,bottom]=await(0,_str.get_strings)(["alignment_top","alignment_middle","alignment_bottom"].map((key=>({key:key,component:_common.component}))));this.alignmentTitles={top:top,middle:middle,bottom:bottom}}return this.alignmentTitles}async getImageAlignment(){let selected=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"";const titles=await this.getAlignmentTitles(),alignments=[{text:titles.top,value:"align-top"},{text:titles.middle,value:"align-middle"},{text:titles.bottom,value:"align-bottom"}];return selected&&alignments.forEach(((alignment,index,array)=>{alignment.value===selected&&(array[index].selected=!0)})),alignments}async getTemplateContext(data){return{elementid:this.editor.id,showfilepicker:this.canShowFilePicker,alignoptions:await this.getImageAlignment(),...data}}async getCurrentImageData(){const selectedImageProperties=this.getSelectedImageProperties();if(!selectedImageProperties)return{};const properties={...selectedImageProperties};return properties.align&&(properties.alignoptions=await this.getImageAlignment(properties.align)),properties.src&&(properties.haspreview=!0),properties.alt||(properties.presentation=!0),properties}filePickerCallback(params,self){if(params.url){self.form.querySelector(_selectors.default.IMAGE.elements.url).value=params.url,self.form.querySelector(_selectors.default.IMAGE.elements.width).value="",self.form.querySelector(_selectors.default.IMAGE.elements.height).value="",self.loadPreviewImage(params.url)}}storeImageDimensions(image){this.rawImageDimensions={width:image.width||this.DEFAULTS.WIDTH,height:image.height||this.DEFAULTS.HEIGHT};const currentWidth=(element=>(""===element.value&&(element.value=this.rawImageDimensions.width),element.value))(this.form.querySelector(_selectors.default.IMAGE.elements.width)),currentHeight=(element=>(""===element.value&&(element.value=this.rawImageDimensions.height),element.value))(this.form.querySelector(_selectors.default.IMAGE.elements.height)),preview=this.form.querySelector(_selectors.default.IMAGE.elements.preview);preview.setAttribute("src",image.src),preview.style.display="inline";const constrain=this.form.querySelector(_selectors.default.IMAGE.elements.constrain);if(this.isPercentageValue(currentWidth)&&this.isPercentageValue(currentHeight))constrain.checked=currentWidth===currentHeight;else if(0===image.width||0===image.height)constrain.disabled="disabled";else{const widthRatio=Math.round(1e3*parseInt(currentWidth,10)/image.width),heightRatio=Math.round(1e3*parseInt(currentHeight,10)/image.height);constrain.checked=widthRatio===heightRatio}}loadPreviewImage(url){const image=new Image;image.addEventListener("error",(()=>{this.form.querySelector(_selectors.default.IMAGE.elements.preview).style.display="none"})),image.addEventListener("load",(()=>{this.storeImageDimensions(image),this.autoAdjustSize()})),image.src=url}urlChanged(){const input=this.form.querySelector(_selectors.default.IMAGE.elements.url);input.value&&this.loadPreviewImage(input.value)}hasErrorUrlField(){const urlError=""===this.form.querySelector(_selectors.default.IMAGE.elements.url).value;return this.toggleVisibility(_selectors.default.IMAGE.elements.urlWarning,urlError),this.toggleAriaInvalid([_selectors.default.IMAGE.elements.url],urlError),urlError}hasErrorAltField(){const alt=this.form.querySelector(_selectors.default.IMAGE.elements.alt).value,presentation=this.form.querySelector(_selectors.default.IMAGE.elements.presentation).checked,imageAltError=""===alt&&!presentation;return this.toggleVisibility(_selectors.default.IMAGE.elements.altWarning,imageAltError),this.toggleAriaInvalid([_selectors.default.IMAGE.elements.alt,_selectors.default.IMAGE.elements.presentation],imageAltError),imageAltError}toggleVisibility(selector,predicate){this.form.querySelectorAll(selector).forEach((element=>{element.style.display=predicate?"block":"none"}))}toggleAriaInvalid(selectors,predicate){selectors.forEach((selector=>{this.form.querySelectorAll(selector).forEach((element=>element.setAttribute("aria-invalid",predicate)))}))}getAlignmentClass(alignment){return alignment}updateWarning(){const urlError=this.hasErrorUrlField(),imageAltError=this.hasErrorAltField();return urlError||imageAltError}getImageContext(){if(this.updateWarning())return null;const classList=[];this.form.querySelector(_selectors.default.IMAGE.elements.constrain).value&&classList.push(_selectors.default.IMAGE.styles.responsive);const alignment=this.getAlignmentClass(this.form.querySelector(_selectors.default.IMAGE.elements.alignment).value);return classList.push(alignment),{url:this.form.querySelector(_selectors.default.IMAGE.elements.url).value,alt:this.form.querySelector(_selectors.default.IMAGE.elements.alt).value,width:this.form.querySelector(_selectors.default.IMAGE.elements.width).value,height:this.form.querySelector(_selectors.default.IMAGE.elements.height).value,presentation:this.form.querySelector(_selectors.default.IMAGE.elements.presentation).checked,customStyle:this.form.querySelector(_selectors.default.IMAGE.elements.customStyle).value,classlist:classList.join(" ")}}setImage(){const pendingPromise=new _pending.default("tiny_media:setImage");if(""===this.form.querySelector(_selectors.default.IMAGE.elements.url).value)return;if(this.updateWarning())return void pendingPromise.resolve();const width=this.form.querySelector(_selectors.default.IMAGE.elements.width).value;if(!this.isPercentageValue(width)&&isNaN(parseInt(width,10)))return this.form.querySelector(_selectors.default.IMAGE.elements.width).focus(),void pendingPromise.resolve();const height=this.form.querySelector(_selectors.default.IMAGE.elements.height).value;if(!this.isPercentageValue(height)&&isNaN(parseInt(height,10)))return this.form.querySelector(_selectors.default.IMAGE.elements.height).focus(),void pendingPromise.resolve();_templates.default.render("tiny_media/image",this.getImageContext()).then((html=>(this.editor.insertContent(html),this.currentModal.destroy(),pendingPromise.resolve(),html))).catch()}handleKeyupCharacterCount(){const alt=this.form.querySelector(_selectors.default.IMAGE.elements.alt).value;this.form.querySelector("#currentcount").innerHTML=alt.length}autoAdjustSize(){let forceHeight=arguments.length>0&&void 0!==arguments[0]&&arguments[0];if(!this.rawImageDimensions)return;const widthField=this.form.querySelector(_selectors.default.IMAGE.elements.width),heightField=this.form.querySelector(_selectors.default.IMAGE.elements.height),normalizeFieldData=fieldData=>(fieldData.isPercentageValue=!!this.isPercentageValue(fieldData.field.value),fieldData.isPercentageValue?(fieldData.percentValue=parseInt(fieldData.field.value,10),fieldData.pixelSize=this.rawImageDimensions[fieldData.type]/100*fieldData.percentValue):(fieldData.pixelSize=parseInt(fieldData.field.value,10),fieldData.percentValue=fieldData.pixelSize/this.rawImageDimensions[fieldData.type]*100),fieldData),imagePreview=this.form.querySelector(_selectors.default.IMAGE.elements.preview);imagePreview.style.width="",imagePreview.style.height="";const constrainField=this.form.querySelector(_selectors.default.IMAGE.elements.constrain),keyField=(()=>{const currentValue=forceHeight?{field:heightField,type:"height"}:{field:widthField,type:"width"};return""===currentValue.field.value&&(currentValue.field.value=this.rawImageDimensions[currentValue.type]),normalizeFieldData(currentValue)})(),relativeField=normalizeFieldData(forceHeight?{field:widthField,type:"width"}:{field:heightField,type:"height"});constrainField.checked&&(keyField.isPercentageValue?(relativeField.field.value=keyField.field.value,relativeField.percentValue=keyField.percentValue):(relativeField.pixelSize=Math.round(keyField.pixelSize/this.rawImageDimensions[keyField.type]*this.rawImageDimensions[relativeField.type]),relativeField.field.value=relativeField.pixelSize)),function(image,keyField,relativeField){let forceHeight=arguments.length>3&&void 0!==arguments[3]&&arguments[3];const getStyleValue=field=>field.isPercentageValue?"".concat(field.percentValue,"%"):"".concat(field.pixelSize,"px");forceHeight?"width"===keyField.type?image.style.width=getStyleValue(keyField):image.style.width=getStyleValue(relativeField):(image.style[keyField.type]=getStyleValue(keyField),image.style[relativeField.type]=getStyleValue(relativeField))}(imagePreview,keyField,relativeField,!!constrainField.checked)}getSelectedImageProperties(){const image=this.getSelectedImage();if(!image)return this.selectedImage=null,null;const properties={src:null,alt:null,width:null,height:null,align:"",presentation:!1};this.removeLegacyAlignment(image),this.selectedImage=image,properties.customStyle=image.style.cssText;const width=(image=>this.isPercentageValue(String(image.width))?image.width:parseInt(image.width,10))(image);0!==width&&(properties.width=width);const height=(image=>this.isPercentageValue(String(image.height))?image.height:parseInt(image.height,10))(image);0!==height&&(properties.height=height);const alignment=this.getAlignmentProperties(image,properties);return alignment&&(properties.align=alignment.value),properties.src=image.getAttribute("src"),properties.alt=image.getAttribute("alt")||"",properties.presentation="presentation"===image.getAttribute("role"),properties}removeLegacyAlignment(imageNode){return imageNode.style.margin?(_selectors.default.IMAGE.alignments.some((alignment=>{if(imageNode.style[alignment.name]!==alignment.value)return!1;const normalisedNode=document.createElement("div");return normalisedNode.style.margin=alignment.margin,imageNode.style.margin===normalisedNode.style.margin&&(imageNode.classList.add(this.getAlignmentClass(alignment.value)),imageNode.style[alignment.name]=null,imageNode.style.margin=null,!0)})),imageNode):imageNode}getAlignmentProperties(image){const currentAlignment=_selectors.default.IMAGE.alignments.find((alignment=>!!image.classList.contains(this.getAlignmentClass(alignment.value))||!!alignment.legacyValues&&alignment.legacyValues.some((legacyValue=>image.classList.contains(legacyValue)))));return currentAlignment||_selectors.default.IMAGE.alignments.find((alignment=>alignment.isDefault))}getSelectedImage(){const imgElm=this.editor.selection.getNode(),figureElm=this.editor.dom.getParent(imgElm,"figure.image");return figureElm?this.editor.dom.select("img",figureElm)[0]:imgElm&&("IMG"!==imgElm.nodeName.toUpperCase()||this.isPlaceholderImage(imgElm))?null:imgElm}isPlaceholderImage(imgElm){return"IMG"===imgElm.nodeName.toUpperCase()&&(imgElm.hasAttribute("data-mce-object")||imgElm.hasAttribute("data-mce-placeholder"))}isPercentageValue(value){return value.match(/\d+%/)}async registerEventListeners(modal){await modal.getBody();const root=modal.getRoot()[0];this.form=root.querySelector(_selectors.default.IMAGE.elements.form),root.addEventListener("click",(e=>{const submitAction=e.target.closest(_selectors.default.IMAGE.actions.submit),imageBrowserAction=e.target.closest(_selectors.default.IMAGE.actions.imageBrowser);submitAction&&(e.preventDefault(),this.setImage()),imageBrowserAction&&this.canShowFilePicker&&(e.preventDefault(),(0,_utils.displayFilepicker)(this.editor,"image").then((params=>{this.filePickerCallback(params,this)})).catch())})),root.addEventListener("change",(e=>{e.target.closest(_selectors.default.IMAGE.elements.url)&&this.hasErrorUrlField();e.target.closest(_selectors.default.IMAGE.elements.presentation)&&this.hasErrorAltField();e.target.closest(_selectors.default.IMAGE.elements.constrain)&&this.autoAdjustSize(!0)})),root.addEventListener("blur",(e=>{if(e.target.nodeType===Node.ELEMENT_NODE){e.target.closest(_selectors.default.IMAGE.elements.url)&&this.urlChanged();e.target.closest(_selectors.default.IMAGE.elements.alt)&&this.hasErrorAltField();e.target.closest(_selectors.default.IMAGE.elements.width)&&this.autoAdjustSize();e.target.closest(_selectors.default.IMAGE.elements.height)&&this.autoAdjustSize(!0)}}),!0),root.addEventListener("keyup",(e=>{e.target.closest(_selectors.default.IMAGE.elements.alt)&&this.handleKeyupCharacterCount()}))}}})); //# sourceMappingURL=image.min.js.map \ No newline at end of file diff --git a/lib/editor/tiny/plugins/media/amd/build/image.min.js.map b/lib/editor/tiny/plugins/media/amd/build/image.min.js.map index 137734d8a5f..54a3775d98d 100644 --- a/lib/editor/tiny/plugins/media/amd/build/image.min.js.map +++ b/lib/editor/tiny/plugins/media/amd/build/image.min.js.map @@ -1 +1 @@ -{"version":3,"file":"image.min.js","sources":["../src/image.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 Image class for Moodle.\n *\n * @module tiny_media/image\n * @copyright 2022 Huong Nguyen \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Templates from 'core/templates';\nimport {get_string as getString, get_strings as getStrings} from 'core/str';\nimport * as ModalFactory from 'core/modal_factory';\nimport Pending from 'core/pending';\nimport {displayFilepicker} from 'editor_tiny/utils';\nimport Selectors from './selectors';\nimport Modal from './imagemodal';\nimport {getImagePermissions} from './options';\nimport {component} from \"./common\";\n\nexport const MediaImage = class {\n\n DEFAULTS = {\n WIDTH: 160,\n HEIGHT: 160,\n };\n\n form = null;\n rawImageDimensions = null;\n canShowFilePicker = false;\n editor = null;\n currentModal = null;\n selectedImage = null;\n imageAlignment = null;\n\n constructor(editor) {\n const permissions = getImagePermissions(editor);\n this.canShowFilePicker = permissions.filepicker;\n this.editor = editor;\n }\n\n async displayDialogue() {\n // Reset the image dimensions.\n this.rawImageDimensions = null;\n\n const currentImageData = await this.getCurrentImageData();\n const modal = await ModalFactory.create({\n type: Modal.TYPE,\n title: getString('imageproperties', 'tiny_media'),\n templateContext: await this.getTemplateContext(currentImageData),\n removeOnClose: true,\n large: true,\n });\n\n this.currentModal = modal;\n if (currentImageData && currentImageData.src) {\n this.loadPreviewImage(currentImageData.src);\n }\n\n await this.registerEventListeners(modal);\n modal.show();\n }\n\n async getAlignmentTitles() {\n if (!this.alignmentTitles) {\n const [top, middle, bottom] = await getStrings([\n 'alignment_top',\n 'alignment_middle',\n 'alignment_bottom',\n ].map((key) => ({key, component})));\n\n this.alignmentTitles = {\n top,\n middle,\n bottom,\n };\n }\n\n return this.alignmentTitles;\n }\n\n async getImageAlignment(selected = '') {\n const titles = await this.getAlignmentTitles();\n const alignments = [\n {\n text: titles.top,\n value: 'align-top',\n },\n {\n text: titles.middle,\n value: 'align-middle',\n },\n {\n text: titles.bottom,\n value: 'align-bottom',\n },\n ];\n\n if (selected) {\n alignments.forEach((alignment, index, array) => {\n if (alignment.value === selected) {\n array[index].selected = true;\n }\n });\n }\n\n return alignments;\n }\n\n async getTemplateContext(data) {\n return {\n elementid: this.editor.id,\n showfilepicker: this.canShowFilePicker,\n alignoptions: await this.getImageAlignment(),\n ...data,\n };\n }\n\n async getCurrentImageData() {\n const selectedImageProperties = this.getSelectedImageProperties();\n if (!selectedImageProperties) {\n return {};\n }\n\n const properties = {...selectedImageProperties};\n if (properties.align) {\n properties.alignoptions = await this.getImageAlignment(properties.align);\n }\n\n if (properties.src) {\n properties.haspreview = true;\n }\n\n if (!properties.alt) {\n properties.presentation = true;\n }\n\n return properties;\n }\n\n filePickerCallback(params, self) {\n if (params.url) {\n const input = self.form.querySelector(Selectors.IMAGE.elements.url);\n input.value = params.url;\n\n // Auto set the width and height.\n self.form.querySelector(Selectors.IMAGE.elements.width).value = '';\n self.form.querySelector(Selectors.IMAGE.elements.height).value = '';\n\n // Load the preview image.\n self.loadPreviewImage(params.url);\n }\n }\n\n storeImageDimensions(image) {\n // Store dimensions of the raw image, falling back to defaults for images without dimensions (e.g. SVG).\n this.rawImageDimensions = {\n width: image.width || this.DEFAULTS.WIDTH,\n height: image.height || this.DEFAULTS.HEIGHT,\n };\n\n const getCurrentWidth = (element) => {\n if (element.value === '') {\n element.value = this.rawImageDimensions.width;\n }\n return element.value;\n };\n const getCurrentHeight = (element) => {\n if (element.value === '') {\n element.value = this.rawImageDimensions.height;\n }\n return element.value;\n };\n\n const widthInput = this.form.querySelector(Selectors.IMAGE.elements.width);\n const currentWidth = getCurrentWidth(widthInput);\n\n const heightInput = this.form.querySelector(Selectors.IMAGE.elements.height);\n const currentHeight = getCurrentHeight(heightInput);\n\n const preview = this.form.querySelector(Selectors.IMAGE.elements.preview);\n preview.setAttribute('src', image.src);\n preview.style.display = 'inline';\n\n const constrain = this.form.querySelector(Selectors.IMAGE.elements.constrain);\n if (this.isPercentageValue(currentWidth) && this.isPercentageValue(currentHeight)) {\n constrain.checked = currentWidth === currentHeight;\n } else if (image.width === 0 || image.height === 0) {\n // If we don't have both dimensions of the image, we can't auto-size it, so disable control.\n constrain.disabled = 'disabled';\n } else {\n // This is the same as comparing to 3 decimal places.\n const widthRatio = Math.round(1000 * parseInt(currentWidth, 10) / image.width);\n const heightRatio = Math.round(1000 * parseInt(currentHeight, 10) / image.height);\n constrain.checked = widthRatio === heightRatio;\n }\n }\n\n loadPreviewImage(url) {\n const image = new Image();\n\n image.addEventListener('error', () => {\n const preview = this.form.querySelector(Selectors.IMAGE.elements.preview);\n preview.style.display = 'none';\n });\n image.addEventListener('load', () => {\n this.storeImageDimensions(image);\n this.autoAdjustSize();\n });\n\n image.src = url;\n }\n\n urlChanged() {\n const input = this.form.querySelector(Selectors.IMAGE.elements.url);\n\n if (input.value) {\n // Load the preview image.\n this.loadPreviewImage(input.value);\n }\n }\n\n hasErrorUrlField() {\n const url = this.form.querySelector(Selectors.IMAGE.elements.url).value;\n const urlError = url === '';\n this.toggleVisibility(Selectors.IMAGE.elements.urlWarning, urlError);\n this.toggleAriaInvalid([Selectors.IMAGE.elements.url], urlError);\n\n return urlError;\n }\n\n hasErrorAltField() {\n const alt = this.form.querySelector(Selectors.IMAGE.elements.alt).value;\n const presentation = this.form.querySelector(Selectors.IMAGE.elements.presentation).checked;\n const imageAltError = alt === '' && !presentation;\n this.toggleVisibility(Selectors.IMAGE.elements.altWarning, imageAltError);\n this.toggleAriaInvalid([Selectors.IMAGE.elements.alt, Selectors.IMAGE.elements.presentation], imageAltError);\n\n return imageAltError;\n }\n\n toggleVisibility(selector, predicate) {\n const elements = this.form.querySelectorAll(selector);\n elements.forEach((element) => {\n element.style.display = predicate ? 'block' : 'none';\n });\n }\n\n toggleAriaInvalid(selectors, predicate) {\n selectors.forEach((selector) => {\n const elements = this.form.querySelectorAll(selector);\n elements.forEach((element) => element.setAttribute('aria-invalid', predicate));\n });\n }\n\n getAlignmentClass(alignment) {\n return alignment;\n }\n\n updateWarning() {\n const urlError = this.hasErrorUrlField();\n const imageAltError = this.hasErrorAltField();\n\n return urlError || imageAltError;\n }\n\n getImageContext() {\n // Check if there are any accessibility issues.\n if (this.updateWarning()) {\n return null;\n }\n\n const classList = [];\n\n const constrain = this.form.querySelector(Selectors.IMAGE.elements.constrain).value;\n if (constrain) {\n classList.push(Selectors.IMAGE.styles.responsive);\n }\n\n // Add the alignment class for the image.\n const alignment = this.getAlignmentClass(this.form.querySelector(Selectors.IMAGE.elements.alignment).value);\n classList.push(alignment);\n\n return {\n url: this.form.querySelector(Selectors.IMAGE.elements.url).value,\n alt: this.form.querySelector(Selectors.IMAGE.elements.alt).value,\n width: this.form.querySelector(Selectors.IMAGE.elements.width).value,\n height: this.form.querySelector(Selectors.IMAGE.elements.height).value,\n presentation: this.form.querySelector(Selectors.IMAGE.elements.presentation).checked,\n customStyle: this.form.querySelector(Selectors.IMAGE.elements.customStyle).value,\n classlist: classList.join(' '),\n };\n }\n\n setImage() {\n const pendingPromise = new Pending('tiny_media:setImage');\n const url = this.form.querySelector(Selectors.IMAGE.elements.url).value;\n if (url === '') {\n return;\n }\n\n // Check if there are any accessibility issues.\n if (this.updateWarning()) {\n pendingPromise.resolve();\n return;\n }\n\n // Check for invalid width or height.\n const width = this.form.querySelector(Selectors.IMAGE.elements.width).value;\n if (!this.isPercentageValue(width) && isNaN(parseInt(width, 10))) {\n this.form.querySelector(Selectors.IMAGE.elements.width).focus();\n pendingPromise.resolve();\n return;\n }\n\n const height = this.form.querySelector(Selectors.IMAGE.elements.height).value;\n if (!this.isPercentageValue(height) && isNaN(parseInt(height, 10))) {\n this.form.querySelector(Selectors.IMAGE.elements.height).focus();\n pendingPromise.resolve();\n return;\n }\n\n Templates.render('tiny_media/image', this.getImageContext())\n .then((html) => {\n this.editor.insertContent(html);\n this.currentModal.destroy();\n pendingPromise.resolve();\n\n return html;\n })\n .catch();\n }\n\n handleKeyupCharacterCount() {\n const alt = this.form.querySelector(Selectors.IMAGE.elements.alt).value;\n const current = this.form.querySelector('#currentcount');\n current.innerHTML = alt.length;\n }\n\n autoAdjustSize(forceHeight = false) {\n // If we do not know the image size, do not do anything.\n if (!this.rawImageDimensions) {\n return;\n }\n\n const widthField = this.form.querySelector(Selectors.IMAGE.elements.width);\n const heightField = this.form.querySelector(Selectors.IMAGE.elements.height);\n const normalizeFieldData = (fieldData) => {\n fieldData.isPercentageValue = !!this.isPercentageValue(fieldData.field.value);\n if (fieldData.isPercentageValue) {\n fieldData.percentValue = parseInt(fieldData.field.value, 10);\n fieldData.pixelSize = this.rawImageDimensions[fieldData.type] / 100 * fieldData.percentValue;\n } else {\n fieldData.pixelSize = parseInt(fieldData.field.value, 10);\n fieldData.percentValue = fieldData.pixelSize / this.rawImageDimensions[fieldData.type] * 100;\n }\n\n return fieldData;\n };\n\n const getKeyField = () => {\n const getValue = () => {\n if (forceHeight) {\n return {\n field: heightField,\n type: 'height',\n };\n } else {\n return {\n field: widthField,\n type: 'width',\n };\n }\n };\n\n const currentValue = getValue();\n if (currentValue.field.value === '') {\n currentValue.field.value = this.rawImageDimensions[currentValue.type];\n }\n\n return normalizeFieldData(currentValue);\n };\n\n const getRelativeField = () => {\n if (forceHeight) {\n return normalizeFieldData({\n field: widthField,\n type: 'width',\n });\n } else {\n return normalizeFieldData({\n field: heightField,\n type: 'height',\n });\n }\n };\n\n\n const setImageDimensions = (image, keyField, relativeField, forceHeight = false) => {\n const getStyleValue = (field) => field.isPercentageValue ? `${field.percentValue}%` : `${field.pixelSize}px`;\n\n // If the values are constrained, then only update the width.\n if (forceHeight) {\n if (keyField.type === 'width') {\n image.style.width = getStyleValue(keyField);\n } else {\n image.style.width = getStyleValue(relativeField);\n }\n } else {\n image.style[keyField.type] = getStyleValue(keyField);\n image.style[relativeField.type] = getStyleValue(relativeField);\n }\n };\n\n const imagePreview = this.form.querySelector(Selectors.IMAGE.elements.preview);\n // Clear the existing preview sizes.\n imagePreview.style.width = '';\n imagePreview.style.height = '';\n\n // Now update with the new values.\n const constrainField = this.form.querySelector(Selectors.IMAGE.elements.constrain);\n const keyField = getKeyField();\n const relativeField = getRelativeField();\n if (constrainField.checked) {\n // We are keeping the image in proportion.\n // Calculate the size for the relative field.\n if (keyField.isPercentageValue) {\n // In proportion, so the percentages are the same.\n relativeField.field.value = keyField.field.value;\n relativeField.percentValue = keyField.percentValue;\n } else {\n relativeField.pixelSize = Math.round(\n keyField.pixelSize / this.rawImageDimensions[keyField.type] * this.rawImageDimensions[relativeField.type]\n );\n relativeField.field.value = relativeField.pixelSize;\n }\n }\n setImageDimensions(imagePreview, keyField, relativeField, !!constrainField.checked);\n }\n\n getSelectedImageProperties() {\n const image = this.getSelectedImage();\n if (!image) {\n this.selectedImage = null;\n return null;\n }\n\n const properties = {\n src: null,\n alt: null,\n width: null,\n height: null,\n align: '',\n presentation: false,\n };\n\n const getImageHeight = (image) => {\n if (!this.isPercentageValue(String(image.height))) {\n return parseInt(image.height, 10);\n }\n\n return image.height;\n };\n\n const getImageWidth = (image) => {\n if (!this.isPercentageValue(String(image.width))) {\n return parseInt(image.width, 10);\n }\n\n return image.width;\n };\n\n // Get the current selection.\n this.removeLegacyAlignment(image);\n this.selectedImage = image;\n\n properties.customStyle = image.style.cssText;\n\n const width = getImageWidth(image);\n if (width !== 0) {\n properties.width = width;\n }\n\n const height = getImageHeight(image);\n if (height !== 0) {\n properties.height = height;\n }\n\n const alignment = this.getAlignmentProperties(image, properties);\n if (alignment) {\n properties.align = alignment.value;\n }\n\n properties.src = image.getAttribute('src');\n properties.alt = image.getAttribute('alt') || '';\n properties.presentation = (image.getAttribute('role') === 'presentation');\n\n return properties;\n }\n\n removeLegacyAlignment(imageNode) {\n if (!imageNode.style.margin) {\n // There is no margin therefore this cannot match any known alignments.\n return imageNode;\n }\n\n Selectors.IMAGE.alignments.some(alignment => {\n if (imageNode.style[alignment.name] !== alignment.value) {\n // The name/value do not match. Skip.\n return false;\n }\n const normalisedNode = document.createElement('div');\n normalisedNode.style.margin = alignment.margin;\n if (imageNode.style.margin !== normalisedNode.style.margin) {\n // The margin does not match.\n return false;\n }\n\n imageNode.classList.add(this.getAlignmentClass(alignment.value));\n imageNode.style[alignment.name] = null;\n imageNode.style.margin = null;\n\n return true;\n });\n\n return imageNode;\n }\n\n getAlignmentProperties(image) {\n const currentAlignment = Selectors.IMAGE.alignments.find((alignment) => {\n if (image.classList.contains(this.getAlignmentClass(alignment.value))) {\n return true;\n }\n\n if (alignment.legacyValues) {\n return alignment.legacyValues.some((legacyValue) => image.classList.contains(legacyValue));\n }\n\n return false;\n });\n if (currentAlignment) {\n return currentAlignment;\n }\n\n return Selectors.IMAGE.alignments.find((alignment) => alignment.isDefault);\n }\n\n getSelectedImage() {\n const imgElm = this.editor.selection.getNode();\n const figureElm = this.editor.dom.getParent(imgElm, 'figure.image');\n if (figureElm) {\n return this.editor.dom.select('img', figureElm)[0];\n }\n\n if (imgElm && (imgElm.nodeName.toUpperCase() !== 'IMG' || this.isPlaceholderImage(imgElm))) {\n return null;\n }\n return imgElm;\n }\n\n isPlaceholderImage(imgElm) {\n if (imgElm.nodeName.toUpperCase() !== 'IMG') {\n return false;\n }\n\n return (imgElm.hasAttribute('data-mce-object') || imgElm.hasAttribute('data-mce-placeholder'));\n }\n\n isPercentageValue(value) {\n return value.match(/\\d+%/);\n }\n\n async registerEventListeners(modal) {\n await modal.getBody();\n const root = modal.getRoot()[0];\n\n this.form = root.querySelector(Selectors.IMAGE.elements.form);\n root.addEventListener('click', (e) => {\n const submitAction = e.target.closest(Selectors.IMAGE.actions.submit);\n const imageBrowserAction = e.target.closest(Selectors.IMAGE.actions.imageBrowser);\n if (submitAction) {\n e.preventDefault();\n this.setImage();\n }\n if (imageBrowserAction && this.canShowFilePicker) {\n e.preventDefault();\n displayFilepicker(this.editor, 'image').then((params) => {\n const self = this;\n this.filePickerCallback(params, self);\n\n return;\n }).catch();\n }\n });\n\n root.addEventListener('change', (e) => {\n const urlEle = e.target.closest(Selectors.IMAGE.elements.url);\n if (urlEle) {\n this.hasErrorUrlField();\n }\n\n const presentationEle = e.target.closest(Selectors.IMAGE.elements.presentation);\n if (presentationEle) {\n this.hasErrorAltField();\n }\n\n const constrainEle = e.target.closest(Selectors.IMAGE.elements.constrain);\n if (constrainEle) {\n this.autoAdjustSize(true);\n }\n });\n\n root.addEventListener('blur', (e) => {\n if (e.target.nodeType === Node.ELEMENT_NODE) {\n const urlEle = e.target.closest(Selectors.IMAGE.elements.url);\n if (urlEle) {\n this.urlChanged();\n }\n\n const altEle = e.target.closest(Selectors.IMAGE.elements.alt);\n if (altEle) {\n this.hasErrorAltField();\n }\n\n const widthEle = e.target.closest(Selectors.IMAGE.elements.width);\n if (widthEle) {\n this.autoAdjustSize();\n }\n\n const heightEle = e.target.closest(Selectors.IMAGE.elements.height);\n if (heightEle) {\n this.autoAdjustSize(true);\n }\n }\n }, true);\n\n // Character count.\n root.addEventListener('keyup', (e) => {\n const altEle = e.target.closest(Selectors.IMAGE.elements.alt);\n if (altEle) {\n this.handleKeyupCharacterCount();\n }\n });\n }\n};\n"],"names":["constructor","editor","WIDTH","HEIGHT","permissions","canShowFilePicker","filepicker","rawImageDimensions","currentImageData","this","getCurrentImageData","modal","ModalFactory","create","type","Modal","TYPE","title","templateContext","getTemplateContext","removeOnClose","large","currentModal","src","loadPreviewImage","registerEventListeners","show","alignmentTitles","top","middle","bottom","map","key","component","selected","titles","getAlignmentTitles","alignments","text","value","forEach","alignment","index","array","data","elementid","id","showfilepicker","alignoptions","getImageAlignment","selectedImageProperties","getSelectedImageProperties","properties","align","haspreview","alt","presentation","filePickerCallback","params","self","url","form","querySelector","Selectors","IMAGE","elements","width","height","storeImageDimensions","image","DEFAULTS","currentWidth","element","getCurrentWidth","currentHeight","getCurrentHeight","preview","setAttribute","style","display","constrain","isPercentageValue","checked","disabled","widthRatio","Math","round","parseInt","heightRatio","Image","addEventListener","autoAdjustSize","urlChanged","input","hasErrorUrlField","urlError","toggleVisibility","urlWarning","toggleAriaInvalid","hasErrorAltField","imageAltError","altWarning","selector","predicate","querySelectorAll","selectors","getAlignmentClass","updateWarning","getImageContext","classList","push","styles","responsive","customStyle","classlist","join","setImage","pendingPromise","Pending","resolve","isNaN","focus","render","then","html","insertContent","destroy","catch","handleKeyupCharacterCount","innerHTML","length","forceHeight","widthField","heightField","normalizeFieldData","fieldData","field","percentValue","pixelSize","imagePreview","constrainField","keyField","currentValue","getKeyField","relativeField","getStyleValue","setImageDimensions","getSelectedImage","selectedImage","removeLegacyAlignment","cssText","String","getImageWidth","getImageHeight","getAlignmentProperties","getAttribute","imageNode","margin","some","name","normalisedNode","document","createElement","add","currentAlignment","find","contains","legacyValues","legacyValue","isDefault","imgElm","selection","getNode","figureElm","dom","getParent","select","nodeName","toUpperCase","isPlaceholderImage","hasAttribute","match","getBody","root","getRoot","e","submitAction","target","closest","actions","submit","imageBrowserAction","imageBrowser","preventDefault","nodeType","Node","ELEMENT_NODE"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"image.min.js","sources":["../src/image.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 Image class for Moodle.\n *\n * @module tiny_media/image\n * @copyright 2022 Huong Nguyen \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Templates from 'core/templates';\nimport {get_string as getString, get_strings as getStrings} from 'core/str';\nimport * as ModalFactory from 'core/modal_factory';\nimport Pending from 'core/pending';\nimport {displayFilepicker} from 'editor_tiny/utils';\nimport Selectors from './selectors';\nimport Modal from './imagemodal';\nimport {getImagePermissions} from './options';\nimport {component} from \"./common\";\nimport {getFilePicker} from 'editor_tiny/options';\n\nexport const MediaImage = class {\n\n DEFAULTS = {\n WIDTH: 160,\n HEIGHT: 160,\n };\n\n form = null;\n rawImageDimensions = null;\n canShowFilePicker = false;\n editor = null;\n currentModal = null;\n selectedImage = null;\n imageAlignment = null;\n\n constructor(editor) {\n const permissions = getImagePermissions(editor);\n\n // Indicates whether the file picker can be shown.\n this.canShowFilePicker = permissions.filepicker && (typeof getFilePicker(editor, 'image') !== 'undefined');\n\n this.editor = editor;\n }\n\n async displayDialogue() {\n // Reset the image dimensions.\n this.rawImageDimensions = null;\n\n const currentImageData = await this.getCurrentImageData();\n const modal = await ModalFactory.create({\n type: Modal.TYPE,\n title: getString('imageproperties', 'tiny_media'),\n templateContext: await this.getTemplateContext(currentImageData),\n removeOnClose: true,\n large: true,\n });\n\n this.currentModal = modal;\n if (currentImageData && currentImageData.src) {\n this.loadPreviewImage(currentImageData.src);\n }\n\n await this.registerEventListeners(modal);\n modal.show();\n }\n\n async getAlignmentTitles() {\n if (!this.alignmentTitles) {\n const [top, middle, bottom] = await getStrings([\n 'alignment_top',\n 'alignment_middle',\n 'alignment_bottom',\n ].map((key) => ({key, component})));\n\n this.alignmentTitles = {\n top,\n middle,\n bottom,\n };\n }\n\n return this.alignmentTitles;\n }\n\n async getImageAlignment(selected = '') {\n const titles = await this.getAlignmentTitles();\n const alignments = [\n {\n text: titles.top,\n value: 'align-top',\n },\n {\n text: titles.middle,\n value: 'align-middle',\n },\n {\n text: titles.bottom,\n value: 'align-bottom',\n },\n ];\n\n if (selected) {\n alignments.forEach((alignment, index, array) => {\n if (alignment.value === selected) {\n array[index].selected = true;\n }\n });\n }\n\n return alignments;\n }\n\n async getTemplateContext(data) {\n return {\n elementid: this.editor.id,\n showfilepicker: this.canShowFilePicker,\n alignoptions: await this.getImageAlignment(),\n ...data,\n };\n }\n\n async getCurrentImageData() {\n const selectedImageProperties = this.getSelectedImageProperties();\n if (!selectedImageProperties) {\n return {};\n }\n\n const properties = {...selectedImageProperties};\n if (properties.align) {\n properties.alignoptions = await this.getImageAlignment(properties.align);\n }\n\n if (properties.src) {\n properties.haspreview = true;\n }\n\n if (!properties.alt) {\n properties.presentation = true;\n }\n\n return properties;\n }\n\n filePickerCallback(params, self) {\n if (params.url) {\n const input = self.form.querySelector(Selectors.IMAGE.elements.url);\n input.value = params.url;\n\n // Auto set the width and height.\n self.form.querySelector(Selectors.IMAGE.elements.width).value = '';\n self.form.querySelector(Selectors.IMAGE.elements.height).value = '';\n\n // Load the preview image.\n self.loadPreviewImage(params.url);\n }\n }\n\n storeImageDimensions(image) {\n // Store dimensions of the raw image, falling back to defaults for images without dimensions (e.g. SVG).\n this.rawImageDimensions = {\n width: image.width || this.DEFAULTS.WIDTH,\n height: image.height || this.DEFAULTS.HEIGHT,\n };\n\n const getCurrentWidth = (element) => {\n if (element.value === '') {\n element.value = this.rawImageDimensions.width;\n }\n return element.value;\n };\n const getCurrentHeight = (element) => {\n if (element.value === '') {\n element.value = this.rawImageDimensions.height;\n }\n return element.value;\n };\n\n const widthInput = this.form.querySelector(Selectors.IMAGE.elements.width);\n const currentWidth = getCurrentWidth(widthInput);\n\n const heightInput = this.form.querySelector(Selectors.IMAGE.elements.height);\n const currentHeight = getCurrentHeight(heightInput);\n\n const preview = this.form.querySelector(Selectors.IMAGE.elements.preview);\n preview.setAttribute('src', image.src);\n preview.style.display = 'inline';\n\n const constrain = this.form.querySelector(Selectors.IMAGE.elements.constrain);\n if (this.isPercentageValue(currentWidth) && this.isPercentageValue(currentHeight)) {\n constrain.checked = currentWidth === currentHeight;\n } else if (image.width === 0 || image.height === 0) {\n // If we don't have both dimensions of the image, we can't auto-size it, so disable control.\n constrain.disabled = 'disabled';\n } else {\n // This is the same as comparing to 3 decimal places.\n const widthRatio = Math.round(1000 * parseInt(currentWidth, 10) / image.width);\n const heightRatio = Math.round(1000 * parseInt(currentHeight, 10) / image.height);\n constrain.checked = widthRatio === heightRatio;\n }\n }\n\n loadPreviewImage(url) {\n const image = new Image();\n\n image.addEventListener('error', () => {\n const preview = this.form.querySelector(Selectors.IMAGE.elements.preview);\n preview.style.display = 'none';\n });\n image.addEventListener('load', () => {\n this.storeImageDimensions(image);\n this.autoAdjustSize();\n });\n\n image.src = url;\n }\n\n urlChanged() {\n const input = this.form.querySelector(Selectors.IMAGE.elements.url);\n\n if (input.value) {\n // Load the preview image.\n this.loadPreviewImage(input.value);\n }\n }\n\n hasErrorUrlField() {\n const url = this.form.querySelector(Selectors.IMAGE.elements.url).value;\n const urlError = url === '';\n this.toggleVisibility(Selectors.IMAGE.elements.urlWarning, urlError);\n this.toggleAriaInvalid([Selectors.IMAGE.elements.url], urlError);\n\n return urlError;\n }\n\n hasErrorAltField() {\n const alt = this.form.querySelector(Selectors.IMAGE.elements.alt).value;\n const presentation = this.form.querySelector(Selectors.IMAGE.elements.presentation).checked;\n const imageAltError = alt === '' && !presentation;\n this.toggleVisibility(Selectors.IMAGE.elements.altWarning, imageAltError);\n this.toggleAriaInvalid([Selectors.IMAGE.elements.alt, Selectors.IMAGE.elements.presentation], imageAltError);\n\n return imageAltError;\n }\n\n toggleVisibility(selector, predicate) {\n const elements = this.form.querySelectorAll(selector);\n elements.forEach((element) => {\n element.style.display = predicate ? 'block' : 'none';\n });\n }\n\n toggleAriaInvalid(selectors, predicate) {\n selectors.forEach((selector) => {\n const elements = this.form.querySelectorAll(selector);\n elements.forEach((element) => element.setAttribute('aria-invalid', predicate));\n });\n }\n\n getAlignmentClass(alignment) {\n return alignment;\n }\n\n updateWarning() {\n const urlError = this.hasErrorUrlField();\n const imageAltError = this.hasErrorAltField();\n\n return urlError || imageAltError;\n }\n\n getImageContext() {\n // Check if there are any accessibility issues.\n if (this.updateWarning()) {\n return null;\n }\n\n const classList = [];\n\n const constrain = this.form.querySelector(Selectors.IMAGE.elements.constrain).value;\n if (constrain) {\n classList.push(Selectors.IMAGE.styles.responsive);\n }\n\n // Add the alignment class for the image.\n const alignment = this.getAlignmentClass(this.form.querySelector(Selectors.IMAGE.elements.alignment).value);\n classList.push(alignment);\n\n return {\n url: this.form.querySelector(Selectors.IMAGE.elements.url).value,\n alt: this.form.querySelector(Selectors.IMAGE.elements.alt).value,\n width: this.form.querySelector(Selectors.IMAGE.elements.width).value,\n height: this.form.querySelector(Selectors.IMAGE.elements.height).value,\n presentation: this.form.querySelector(Selectors.IMAGE.elements.presentation).checked,\n customStyle: this.form.querySelector(Selectors.IMAGE.elements.customStyle).value,\n classlist: classList.join(' '),\n };\n }\n\n setImage() {\n const pendingPromise = new Pending('tiny_media:setImage');\n const url = this.form.querySelector(Selectors.IMAGE.elements.url).value;\n if (url === '') {\n return;\n }\n\n // Check if there are any accessibility issues.\n if (this.updateWarning()) {\n pendingPromise.resolve();\n return;\n }\n\n // Check for invalid width or height.\n const width = this.form.querySelector(Selectors.IMAGE.elements.width).value;\n if (!this.isPercentageValue(width) && isNaN(parseInt(width, 10))) {\n this.form.querySelector(Selectors.IMAGE.elements.width).focus();\n pendingPromise.resolve();\n return;\n }\n\n const height = this.form.querySelector(Selectors.IMAGE.elements.height).value;\n if (!this.isPercentageValue(height) && isNaN(parseInt(height, 10))) {\n this.form.querySelector(Selectors.IMAGE.elements.height).focus();\n pendingPromise.resolve();\n return;\n }\n\n Templates.render('tiny_media/image', this.getImageContext())\n .then((html) => {\n this.editor.insertContent(html);\n this.currentModal.destroy();\n pendingPromise.resolve();\n\n return html;\n })\n .catch();\n }\n\n handleKeyupCharacterCount() {\n const alt = this.form.querySelector(Selectors.IMAGE.elements.alt).value;\n const current = this.form.querySelector('#currentcount');\n current.innerHTML = alt.length;\n }\n\n autoAdjustSize(forceHeight = false) {\n // If we do not know the image size, do not do anything.\n if (!this.rawImageDimensions) {\n return;\n }\n\n const widthField = this.form.querySelector(Selectors.IMAGE.elements.width);\n const heightField = this.form.querySelector(Selectors.IMAGE.elements.height);\n const normalizeFieldData = (fieldData) => {\n fieldData.isPercentageValue = !!this.isPercentageValue(fieldData.field.value);\n if (fieldData.isPercentageValue) {\n fieldData.percentValue = parseInt(fieldData.field.value, 10);\n fieldData.pixelSize = this.rawImageDimensions[fieldData.type] / 100 * fieldData.percentValue;\n } else {\n fieldData.pixelSize = parseInt(fieldData.field.value, 10);\n fieldData.percentValue = fieldData.pixelSize / this.rawImageDimensions[fieldData.type] * 100;\n }\n\n return fieldData;\n };\n\n const getKeyField = () => {\n const getValue = () => {\n if (forceHeight) {\n return {\n field: heightField,\n type: 'height',\n };\n } else {\n return {\n field: widthField,\n type: 'width',\n };\n }\n };\n\n const currentValue = getValue();\n if (currentValue.field.value === '') {\n currentValue.field.value = this.rawImageDimensions[currentValue.type];\n }\n\n return normalizeFieldData(currentValue);\n };\n\n const getRelativeField = () => {\n if (forceHeight) {\n return normalizeFieldData({\n field: widthField,\n type: 'width',\n });\n } else {\n return normalizeFieldData({\n field: heightField,\n type: 'height',\n });\n }\n };\n\n\n const setImageDimensions = (image, keyField, relativeField, forceHeight = false) => {\n const getStyleValue = (field) => field.isPercentageValue ? `${field.percentValue}%` : `${field.pixelSize}px`;\n\n // If the values are constrained, then only update the width.\n if (forceHeight) {\n if (keyField.type === 'width') {\n image.style.width = getStyleValue(keyField);\n } else {\n image.style.width = getStyleValue(relativeField);\n }\n } else {\n image.style[keyField.type] = getStyleValue(keyField);\n image.style[relativeField.type] = getStyleValue(relativeField);\n }\n };\n\n const imagePreview = this.form.querySelector(Selectors.IMAGE.elements.preview);\n // Clear the existing preview sizes.\n imagePreview.style.width = '';\n imagePreview.style.height = '';\n\n // Now update with the new values.\n const constrainField = this.form.querySelector(Selectors.IMAGE.elements.constrain);\n const keyField = getKeyField();\n const relativeField = getRelativeField();\n if (constrainField.checked) {\n // We are keeping the image in proportion.\n // Calculate the size for the relative field.\n if (keyField.isPercentageValue) {\n // In proportion, so the percentages are the same.\n relativeField.field.value = keyField.field.value;\n relativeField.percentValue = keyField.percentValue;\n } else {\n relativeField.pixelSize = Math.round(\n keyField.pixelSize / this.rawImageDimensions[keyField.type] * this.rawImageDimensions[relativeField.type]\n );\n relativeField.field.value = relativeField.pixelSize;\n }\n }\n setImageDimensions(imagePreview, keyField, relativeField, !!constrainField.checked);\n }\n\n getSelectedImageProperties() {\n const image = this.getSelectedImage();\n if (!image) {\n this.selectedImage = null;\n return null;\n }\n\n const properties = {\n src: null,\n alt: null,\n width: null,\n height: null,\n align: '',\n presentation: false,\n };\n\n const getImageHeight = (image) => {\n if (!this.isPercentageValue(String(image.height))) {\n return parseInt(image.height, 10);\n }\n\n return image.height;\n };\n\n const getImageWidth = (image) => {\n if (!this.isPercentageValue(String(image.width))) {\n return parseInt(image.width, 10);\n }\n\n return image.width;\n };\n\n // Get the current selection.\n this.removeLegacyAlignment(image);\n this.selectedImage = image;\n\n properties.customStyle = image.style.cssText;\n\n const width = getImageWidth(image);\n if (width !== 0) {\n properties.width = width;\n }\n\n const height = getImageHeight(image);\n if (height !== 0) {\n properties.height = height;\n }\n\n const alignment = this.getAlignmentProperties(image, properties);\n if (alignment) {\n properties.align = alignment.value;\n }\n\n properties.src = image.getAttribute('src');\n properties.alt = image.getAttribute('alt') || '';\n properties.presentation = (image.getAttribute('role') === 'presentation');\n\n return properties;\n }\n\n removeLegacyAlignment(imageNode) {\n if (!imageNode.style.margin) {\n // There is no margin therefore this cannot match any known alignments.\n return imageNode;\n }\n\n Selectors.IMAGE.alignments.some(alignment => {\n if (imageNode.style[alignment.name] !== alignment.value) {\n // The name/value do not match. Skip.\n return false;\n }\n const normalisedNode = document.createElement('div');\n normalisedNode.style.margin = alignment.margin;\n if (imageNode.style.margin !== normalisedNode.style.margin) {\n // The margin does not match.\n return false;\n }\n\n imageNode.classList.add(this.getAlignmentClass(alignment.value));\n imageNode.style[alignment.name] = null;\n imageNode.style.margin = null;\n\n return true;\n });\n\n return imageNode;\n }\n\n getAlignmentProperties(image) {\n const currentAlignment = Selectors.IMAGE.alignments.find((alignment) => {\n if (image.classList.contains(this.getAlignmentClass(alignment.value))) {\n return true;\n }\n\n if (alignment.legacyValues) {\n return alignment.legacyValues.some((legacyValue) => image.classList.contains(legacyValue));\n }\n\n return false;\n });\n if (currentAlignment) {\n return currentAlignment;\n }\n\n return Selectors.IMAGE.alignments.find((alignment) => alignment.isDefault);\n }\n\n getSelectedImage() {\n const imgElm = this.editor.selection.getNode();\n const figureElm = this.editor.dom.getParent(imgElm, 'figure.image');\n if (figureElm) {\n return this.editor.dom.select('img', figureElm)[0];\n }\n\n if (imgElm && (imgElm.nodeName.toUpperCase() !== 'IMG' || this.isPlaceholderImage(imgElm))) {\n return null;\n }\n return imgElm;\n }\n\n isPlaceholderImage(imgElm) {\n if (imgElm.nodeName.toUpperCase() !== 'IMG') {\n return false;\n }\n\n return (imgElm.hasAttribute('data-mce-object') || imgElm.hasAttribute('data-mce-placeholder'));\n }\n\n isPercentageValue(value) {\n return value.match(/\\d+%/);\n }\n\n async registerEventListeners(modal) {\n await modal.getBody();\n const root = modal.getRoot()[0];\n\n this.form = root.querySelector(Selectors.IMAGE.elements.form);\n root.addEventListener('click', (e) => {\n const submitAction = e.target.closest(Selectors.IMAGE.actions.submit);\n const imageBrowserAction = e.target.closest(Selectors.IMAGE.actions.imageBrowser);\n if (submitAction) {\n e.preventDefault();\n this.setImage();\n }\n if (imageBrowserAction && this.canShowFilePicker) {\n e.preventDefault();\n displayFilepicker(this.editor, 'image').then((params) => {\n const self = this;\n this.filePickerCallback(params, self);\n\n return;\n }).catch();\n }\n });\n\n root.addEventListener('change', (e) => {\n const urlEle = e.target.closest(Selectors.IMAGE.elements.url);\n if (urlEle) {\n this.hasErrorUrlField();\n }\n\n const presentationEle = e.target.closest(Selectors.IMAGE.elements.presentation);\n if (presentationEle) {\n this.hasErrorAltField();\n }\n\n const constrainEle = e.target.closest(Selectors.IMAGE.elements.constrain);\n if (constrainEle) {\n this.autoAdjustSize(true);\n }\n });\n\n root.addEventListener('blur', (e) => {\n if (e.target.nodeType === Node.ELEMENT_NODE) {\n const urlEle = e.target.closest(Selectors.IMAGE.elements.url);\n if (urlEle) {\n this.urlChanged();\n }\n\n const altEle = e.target.closest(Selectors.IMAGE.elements.alt);\n if (altEle) {\n this.hasErrorAltField();\n }\n\n const widthEle = e.target.closest(Selectors.IMAGE.elements.width);\n if (widthEle) {\n this.autoAdjustSize();\n }\n\n const heightEle = e.target.closest(Selectors.IMAGE.elements.height);\n if (heightEle) {\n this.autoAdjustSize(true);\n }\n }\n }, true);\n\n // Character count.\n root.addEventListener('keyup', (e) => {\n const altEle = e.target.closest(Selectors.IMAGE.elements.alt);\n if (altEle) {\n this.handleKeyupCharacterCount();\n }\n });\n }\n};\n"],"names":["constructor","editor","WIDTH","HEIGHT","permissions","canShowFilePicker","filepicker","rawImageDimensions","currentImageData","this","getCurrentImageData","modal","ModalFactory","create","type","Modal","TYPE","title","templateContext","getTemplateContext","removeOnClose","large","currentModal","src","loadPreviewImage","registerEventListeners","show","alignmentTitles","top","middle","bottom","map","key","component","selected","titles","getAlignmentTitles","alignments","text","value","forEach","alignment","index","array","data","elementid","id","showfilepicker","alignoptions","getImageAlignment","selectedImageProperties","getSelectedImageProperties","properties","align","haspreview","alt","presentation","filePickerCallback","params","self","url","form","querySelector","Selectors","IMAGE","elements","width","height","storeImageDimensions","image","DEFAULTS","currentWidth","element","getCurrentWidth","currentHeight","getCurrentHeight","preview","setAttribute","style","display","constrain","isPercentageValue","checked","disabled","widthRatio","Math","round","parseInt","heightRatio","Image","addEventListener","autoAdjustSize","urlChanged","input","hasErrorUrlField","urlError","toggleVisibility","urlWarning","toggleAriaInvalid","hasErrorAltField","imageAltError","altWarning","selector","predicate","querySelectorAll","selectors","getAlignmentClass","updateWarning","getImageContext","classList","push","styles","responsive","customStyle","classlist","join","setImage","pendingPromise","Pending","resolve","isNaN","focus","render","then","html","insertContent","destroy","catch","handleKeyupCharacterCount","innerHTML","length","forceHeight","widthField","heightField","normalizeFieldData","fieldData","field","percentValue","pixelSize","imagePreview","constrainField","keyField","currentValue","getKeyField","relativeField","getStyleValue","setImageDimensions","getSelectedImage","selectedImage","removeLegacyAlignment","cssText","String","getImageWidth","getImageHeight","getAlignmentProperties","getAttribute","imageNode","margin","some","name","normalisedNode","document","createElement","add","currentAlignment","find","contains","legacyValues","legacyValue","isDefault","imgElm","selection","getNode","figureElm","dom","getParent","select","nodeName","toUpperCase","isPlaceholderImage","hasAttribute","match","getBody","root","getRoot","e","submitAction","target","closest","actions","submit","imageBrowserAction","imageBrowser","preventDefault","nodeType","Node","ELEMENT_NODE"],"mappings":"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"} \ No newline at end of file diff --git a/lib/editor/tiny/plugins/media/amd/src/embed.js b/lib/editor/tiny/plugins/media/amd/src/embed.js index be0a7488195..8781d13120c 100644 --- a/lib/editor/tiny/plugins/media/amd/src/embed.js +++ b/lib/editor/tiny/plugins/media/amd/src/embed.js @@ -34,10 +34,13 @@ import {component} from "./common"; import Modal from './embedmodal'; import Selectors from './selectors'; import {getEmbedPermissions} from './options'; +import {getFilePicker} from 'editor_tiny/options'; export const MediaEmbed = class { editor = null; canShowFilePicker = false; + canShowFilePickerPoster = false; + canShowFilePickerTrack = false; /** * @property {Object} The names of the alignment options. @@ -56,7 +59,12 @@ export const MediaEmbed = class { constructor(editor) { const permissions = getEmbedPermissions(editor); - this.canShowFilePicker = permissions.filepicker; + + // Indicates whether the file picker can be shown. + this.canShowFilePicker = permissions.filepicker && (typeof getFilePicker(editor, 'media') !== 'undefined'); + this.canShowFilePickerPoster = permissions.filepicker && (typeof getFilePicker(editor, 'image') !== 'undefined'); + this.canShowFilePickerTrack = permissions.filepicker && (typeof getFilePicker(editor, 'subtitle') !== 'undefined'); + this.editor = editor; } @@ -91,6 +99,8 @@ export const MediaEmbed = class { return Object.assign({}, { elementid: this.editor.getElement().id, showfilepicker: this.canShowFilePicker, + showfilepickerposter: this.canShowFilePickerPoster, + showfilepickertrack: this.canShowFilePickerTrack, langsinstalled: languages.installed, langsavailable: languages.available, link: true, @@ -443,7 +453,7 @@ export const MediaEmbed = class { await modal.getBody(); const $root = modal.getRoot(); const root = $root[0]; - if (this.canShowFilePicker) { + if (this.canShowFilePicker || this.canShowFilePickerPoster || this.canShowFilePickerTrack) { root.addEventListener('click', this.clickHandler.bind(this)); } diff --git a/lib/editor/tiny/plugins/media/amd/src/image.js b/lib/editor/tiny/plugins/media/amd/src/image.js index ee1a2525ba9..77a16be5f44 100644 --- a/lib/editor/tiny/plugins/media/amd/src/image.js +++ b/lib/editor/tiny/plugins/media/amd/src/image.js @@ -30,6 +30,7 @@ import Selectors from './selectors'; import Modal from './imagemodal'; import {getImagePermissions} from './options'; import {component} from "./common"; +import {getFilePicker} from 'editor_tiny/options'; export const MediaImage = class { @@ -48,7 +49,10 @@ export const MediaImage = class { constructor(editor) { const permissions = getImagePermissions(editor); - this.canShowFilePicker = permissions.filepicker; + + // Indicates whether the file picker can be shown. + this.canShowFilePicker = permissions.filepicker && (typeof getFilePicker(editor, 'image') !== 'undefined'); + this.editor = editor; } diff --git a/lib/editor/tiny/plugins/media/templates/embed_media_modal_audio.mustache b/lib/editor/tiny/plugins/media/templates/embed_media_modal_audio.mustache index 1b95c93ff29..13a4c9e22d2 100644 --- a/lib/editor/tiny/plugins/media/templates/embed_media_modal_audio.mustache +++ b/lib/editor/tiny/plugins/media/templates/embed_media_modal_audio.mustache @@ -32,9 +32,11 @@
+ {{#showfilepicker}} + {{/showfilepicker}}
@@ -60,9 +62,11 @@
+ {{#showfilepicker}} + {{/showfilepicker}}
@@ -248,9 +252,11 @@
+ {{#showfilepickertrack}} - - + + + {{/showfilepickertrack}}
@@ -298,9 +304,11 @@
+ {{#showfilepickertrack}} - - + + + {{/showfilepickertrack}}
@@ -356,9 +364,11 @@
+ {{#showfilepickertrack}} - - + + + {{/showfilepickertrack}}
@@ -406,9 +416,11 @@
+ {{#showfilepickertrack}} - - + + + {{/showfilepickertrack}}
@@ -464,9 +476,11 @@
+ {{#showfilepickertrack}} - - + + + {{/showfilepickertrack}}
@@ -514,9 +528,11 @@
+ {{#showfilepickertrack}} - - + + + {{/showfilepickertrack}}
@@ -572,9 +588,11 @@
+ {{#showfilepickertrack}} - - + + + {{/showfilepickertrack}}
@@ -622,9 +640,11 @@
+ {{#showfilepickertrack}} - - + + + {{/showfilepickertrack}}
@@ -681,9 +701,11 @@
+ {{#showfilepickertrack}} - - + + + {{/showfilepickertrack}}
@@ -731,9 +753,11 @@
+ {{#showfilepickertrack}} - - + + + {{/showfilepickertrack}}
diff --git a/lib/editor/tiny/plugins/media/templates/embed_media_modal_link.mustache b/lib/editor/tiny/plugins/media/templates/embed_media_modal_link.mustache index 6f28995b1b8..1aa2da611f1 100644 --- a/lib/editor/tiny/plugins/media/templates/embed_media_modal_link.mustache +++ b/lib/editor/tiny/plugins/media/templates/embed_media_modal_link.mustache @@ -31,9 +31,11 @@
+ {{#showfilepicker}} + {{/showfilepicker}}
diff --git a/lib/editor/tiny/plugins/media/templates/embed_media_modal_video.mustache b/lib/editor/tiny/plugins/media/templates/embed_media_modal_video.mustache index 928e6e385b8..d646059bf54 100644 --- a/lib/editor/tiny/plugins/media/templates/embed_media_modal_video.mustache +++ b/lib/editor/tiny/plugins/media/templates/embed_media_modal_video.mustache @@ -32,9 +32,11 @@
+ {{#showfilepicker}} + {{/showfilepicker}}
@@ -60,9 +62,11 @@
+ {{#showfilepicker}} - - + + + {{/showfilepicker}}
@@ -133,9 +137,11 @@
+ {{#showfilepickerposter}} - - + + + {{/showfilepickerposter}}
@@ -276,9 +282,11 @@
+ {{#showfilepickertrack}} - - + + + {{/showfilepickertrack}}
@@ -326,9 +334,11 @@
+ {{#showfilepickertrack}} - - + + + {{/showfilepickertrack}}
@@ -384,9 +394,11 @@
+ {{#showfilepickertrack}} - - + + + {{/showfilepickertrack}}
@@ -434,9 +446,11 @@
+ {{#showfilepickertrack}} - - + + + {{/showfilepickertrack}}
@@ -492,9 +506,11 @@
+ {{#showfilepickertrack}} - - + + + {{/showfilepickertrack}}
@@ -542,9 +558,11 @@
+ {{#showfilepickertrack}} - - + + + {{/showfilepickertrack}}
@@ -600,9 +618,11 @@
+ {{#showfilepickertrack}} - - + + + {{/showfilepickertrack}}
@@ -650,9 +670,11 @@
+ {{#showfilepickertrack}} - - + + + {{/showfilepickertrack}}
@@ -709,9 +731,11 @@
+ {{#showfilepickertrack}} - - + + + {{/showfilepickertrack}}
@@ -759,9 +783,11 @@
+ {{#showfilepickertrack}} - - + + + {{/showfilepickertrack}}
diff --git a/lib/editor/tiny/plugins/media/templates/insert_image_modal.mustache b/lib/editor/tiny/plugins/media/templates/insert_image_modal.mustache index 22241b4d917..a45d08ed38b 100644 --- a/lib/editor/tiny/plugins/media/templates/insert_image_modal.mustache +++ b/lib/editor/tiny/plugins/media/templates/insert_image_modal.mustache @@ -48,7 +48,7 @@ {{^showfilepicker}}
- +
{{/showfilepicker}}