diff --git a/amd/build/ui.min.js b/amd/build/ui.min.js index 5721910..4b34376 100644 --- a/amd/build/ui.min.js +++ b/amd/build/ui.min.js @@ -9,6 +9,6 @@ define("tiny_multilang2/ui",["exports","./options"],(function(_exports,_options2 * @copyright 2015 onwards Iñaki Arenaza & Mondragon Unibertsitatea * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -const spanClass="multilang-begin mceNonEditable",spanFixedAttrs='{mlang %lang}',spanMultilangEnd=spanFixedAttrs.replace("begin","end")+">{mlang}",isNull=a=>null==a;let _isSubmit=!1;const _options={},addVisualStyling=function(ed){let content=ed.getContent();if(-1!==content.indexOf(spanClass))return content;let intermediateReplacements=[];const replaceHelper=function(m0,m1,p,c){const textBefore=c.substring(0,p),textAfter=c.substring(p+m0.length);return textBefore.lastIndexOf("<")")&&(m0=m1?spanMultilangBegin.replace(new RegExp("%lang","g"),m1):spanMultilangEnd),intermediateReplacements.push(m0),"".concat(textBefore,"___~~").concat(intermediateReplacements.length,"~~___").concat(textAfter)};for(;;){const m=content.match(new RegExp("{\\s*mlang(\\s+([^}]+?))?\\s*}","i"));if(!m)break;content=replaceHelper(m[0],m[2],m.index,m.input)}for(let i=0;i').concat(innerHTML,"");for(end of(ed.dom.setOuterHTML(span,newHTML),toRemove))ed.dom.remove(end)}}}else ed.dom.setOuterHTML(span,span.innerHTML.toLowerCase())}))},getHighlightNodeFromSelect=function(ed,search){let span;return ed.dom.getParents(ed.selection.getStart(),(elm=>{if(!isNull(elm.classList)){const pair="begin"===search?"end":"begin";if(elm.classList.contains("multilang-"+pair)){span=elm;do{span="begin"===search?span.previousSibling:span.nextSibling}while(!isNull(span)&&(isNull(span.classList)||!span.classList.contains("multilang-"+search)))}else elm.classList.contains("multilang-"+search)&&(span=elm)}})),span},hideContentToolbar=function(el){for(;!isNull(el);){if(el.nodeType===Node.ELEMENT_NODE&&!isNull(el.getAttribute("class"))&&-1!=el.getAttribute("class").indexOf("tox-pop-"))return void(el.style.display="none");el=el.parentNode}};_exports.onInit=function(ed,options){Object.keys(options).forEach((function(key){_options[key]=options[key]})),ed.setContent(addVisualStyling(ed)),(0,_options2.isContentToHighlight)(ed)&&ed.dom.addStyle((0,_options2.getHighlightCss)(ed))};_exports.onBeforeGetContent=function(ed,content){if(!isNull(content.source_view)&&!0===content.source_view){const onClose=function(ed){ed.off("close",onClose),ed.setContent(addVisualStyling(ed))};new MutationObserver(((mutations,obs)=>{const viewSrcModal=document.querySelector('[data-region="modal"]');viewSrcModal&&(viewSrcModal.addEventListener("click",(event=>{const{action:action}=event.target.dataset;["cancel","save","hide"].includes(action)&&onClose(ed)})),obs.disconnect())})).observe(document.body,{childList:!0,subtree:!0}),removeVisualStyling(ed)}};_exports.onFocus=function(ed){_isSubmit&&(ed.setContent(addVisualStyling(ed),{no_events:!0}),_isSubmit=!1)};_exports.onSubmit=function(ed){removeVisualStyling(ed),_isSubmit=!0};_exports.onDelete=function(ed,event){if(event.isComposing||isNull(event.clientX)&&46!==event.keyCode&&8!==event.keyCode)return;if(!isNull(event.clientX)&&(event.target.nodeType!==Node.ELEMENT_NODE||"path"!==event.target.nodeName&&"svg"!==event.target.nodeName))return;const begin=getHighlightNodeFromSelect(ed,"begin"),end=getHighlightNodeFromSelect(ed,"end");isNull(begin)||isNull(end)||(event.preventDefault(),ed.dom.remove(begin),ed.dom.remove(end),isNull(event.clientX)||hideContentToolbar(event.target),cleanupBogus(ed))};const cleanupBogus=function(ed){for(const span of ed.dom.select('span[class*="multilang"')){const p=span.parentElement;!isNull(p.classList)&&p.classList.contains("mce-offscreen-selection")&&ed.dom.remove(p)}};_exports.applyLanguage=function(ed,iso,event){if(isNull(iso))return;if("remove"===iso){return void ed.contentDocument.body.querySelectorAll(".multilang-begin, .multilang-end").forEach((element=>{ed.dom.remove(element)}))}const regexLang=/%lang/g;let text=ed.selection.getContent();if(""===text.toString().replace(/^\s+/,"").replace(/\s+$/,"")){if(!isNull(event))return void hideContentToolbar(event.target);let newtext=spanMultilangBegin.replace(regexLang,iso)+" "+spanMultilangEnd;return(0,_options2.mlangFilterExists)(ed)||(newtext=newtext.replaceAll("mceNonEditable","mceNonEditable fallback")),void ed.insertContent(newtext)}isNull(event)||hideContentToolbar(event.target);const span=getHighlightNodeFromSelect(ed,"begin");if(!isNull(span)){let replacement=spanMultilangBegin.replace(regexLang,iso);return span.classList.contains("fallback")&&(replacement=replacement.replace("mceNonEditable","mceNonEditable fallback")),ed.dom.setOuterHTML(span,replacement),void cleanupBogus(ed)}if(-1!==text.indexOf("multilang-begin")||-1!==text.indexOf("multilang-end"))return void ed.notificationManager.open({text:_options.langInSelectionErrMsg,type:"error"});const block=function(text){let result={el:null,cnt:0};const body=(new DOMParser).parseFromString(text,"text/html").body;if(body.firstChild.nodeType!==Node.ELEMENT_NODE)return result;const blockTags=["address","article","aside","blockquote","dd","div","dl","dt","figcaption","h1","h2","h3","h4","h5","h6","li","ol","p","pre","section","tfoot","ul"];for(let i=0;i{mlang %lang}',spanMultilangEnd=spanFixedAttrs.replace("begin","end")+">{mlang}",isNull=a=>null==a;let _isSubmit=!1;const _options={},addVisualStyling=function(ed){let content=ed.getContent();if(-1!==content.indexOf(spanClass))return content;let intermediateReplacements=[];const replaceHelper=function(m0,m1,p,c){const textBefore=c.substring(0,p),textAfter=c.substring(p+m0.length);return textBefore.lastIndexOf("<")")&&(m0=m1?spanMultilangBegin.replace(new RegExp("%lang","g"),m1):spanMultilangEnd),intermediateReplacements.push(m0),"".concat(textBefore,"___~~").concat(intermediateReplacements.length,"~~___").concat(textAfter)};for(;;){const m=content.match(new RegExp("{\\s*mlang(\\s+([^}]+?))?\\s*}","i"));if(!m)break;content=replaceHelper(m[0],m[2],m.index,m.input)}for(let i=0;i').concat(innerHTML,"");for(end of(ed.dom.setOuterHTML(span,newHTML),toRemove))ed.dom.remove(end)}}}else ed.dom.setOuterHTML(span,span.innerHTML.toLowerCase())}))},getHighlightNodeFromSelect=function(ed,search){let span;return ed.dom.getParents(ed.selection.getStart(),(elm=>{if(!isNull(elm.classList)){const pair="begin"===search?"end":"begin";if(elm.classList.contains("multilang-"+pair)){span=elm;do{span="begin"===search?span.previousSibling:span.nextSibling}while(!isNull(span)&&(isNull(span.classList)||!span.classList.contains("multilang-"+search)))}else elm.classList.contains("multilang-"+search)&&(span=elm)}})),span},hideContentToolbar=function(el){for(;!isNull(el);){if(el.nodeType===Node.ELEMENT_NODE&&!isNull(el.getAttribute("class"))&&-1!=el.getAttribute("class").indexOf("tox-pop-"))return void(el.style.display="none");el=el.parentNode}};_exports.onInit=function(ed,options){Object.keys(options).forEach((function(key){_options[key]=options[key]})),ed.setContent(addVisualStyling(ed)),(0,_options2.isContentToHighlight)(ed)&&ed.dom.addStyle((0,_options2.getHighlightCss)(ed))};_exports.onBeforeGetContent=function(ed,content){if(!isNull(content.source_view)&&!0===content.source_view){const onClose=function(ed){ed.off("close",onClose),ed.setContent(addVisualStyling(ed))};new MutationObserver(((mutations,obs)=>{const viewSrcModal=document.querySelector('[data-region="modal"]');if(viewSrcModal)return viewSrcModal.addEventListener("click",(event=>{const{action:action}=event.target.dataset;["cancel","save","hide"].includes(action)&&onClose(ed)})),void obs.disconnect();document.querySelector(".tox-dialog-wrap")&&(ed.on("CloseWindow",(()=>{onClose(ed)})),obs.disconnect())})).observe(document.body,{childList:!0,subtree:!0}),removeVisualStyling(ed)}};_exports.onFocus=function(ed){_isSubmit&&(ed.setContent(addVisualStyling(ed),{no_events:!0}),_isSubmit=!1)};_exports.onSubmit=function(ed){removeVisualStyling(ed),_isSubmit=!0};_exports.onDelete=function(ed,event){if(event.isComposing||isNull(event.clientX)&&46!==event.keyCode&&8!==event.keyCode)return;if(!isNull(event.clientX)&&(event.target.nodeType!==Node.ELEMENT_NODE||"path"!==event.target.nodeName&&"svg"!==event.target.nodeName))return;const begin=getHighlightNodeFromSelect(ed,"begin"),end=getHighlightNodeFromSelect(ed,"end");isNull(begin)||isNull(end)||(event.preventDefault(),ed.dom.remove(begin),ed.dom.remove(end),isNull(event.clientX)||hideContentToolbar(event.target),cleanupBogus(ed))};const cleanupBogus=function(ed){for(const span of ed.dom.select('span[class*="multilang"')){const p=span.parentElement;!isNull(p.classList)&&p.classList.contains("mce-offscreen-selection")&&ed.dom.remove(p)}};_exports.applyLanguage=function(ed,iso,event){if(isNull(iso))return;if("remove"===iso){return void ed.contentDocument.body.querySelectorAll(".multilang-begin, .multilang-end").forEach((element=>{ed.dom.remove(element)}))}const regexLang=/%lang/g;let text=ed.selection.getContent();if(""===text.toString().replace(/^\s+/,"").replace(/\s+$/,"")){if(!isNull(event))return void hideContentToolbar(event.target);let newtext=spanMultilangBegin.replace(regexLang,iso)+" "+spanMultilangEnd;return(0,_options2.mlangFilterExists)(ed)||(newtext=newtext.replaceAll("mceNonEditable","mceNonEditable fallback")),void ed.insertContent(newtext)}isNull(event)||hideContentToolbar(event.target);const span=getHighlightNodeFromSelect(ed,"begin");if(!isNull(span)){let replacement=spanMultilangBegin.replace(regexLang,iso);return span.classList.contains("fallback")&&(replacement=replacement.replace("mceNonEditable","mceNonEditable fallback")),ed.dom.setOuterHTML(span,replacement),void cleanupBogus(ed)}if(-1!==text.indexOf("multilang-begin")||-1!==text.indexOf("multilang-end"))return void ed.notificationManager.open({text:_options.langInSelectionErrMsg,type:"error"});const block=function(text){let result={el:null,cnt:0};const body=(new DOMParser).parseFromString(text,"text/html").body;if(body.firstChild.nodeType!==Node.ELEMENT_NODE)return result;const blockTags=["address","article","aside","blockquote","dd","div","dl","dt","figcaption","h1","h2","h3","h4","h5","h6","li","ol","p","pre","section","tfoot","ul"];for(let i=0;i.\n\n/**\n * Commands for the plugin logic of the Moodle tiny_multilang2 plugin.\n *\n * @module tiny_multilang2\n * @author Iñaki Arenaza \n * @author Stephan Robotta \n * @author Tai Le Tan \n * @copyright 2015 onwards Iñaki Arenaza & Mondragon Unibertsitatea\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {getHighlightCss, isContentToHighlight, mlangFilterExists} from './options';\n\n// This class inside a identified the {mlang} tag that is encapsulated in a span.\nconst spanClass = 'multilang-begin mceNonEditable';\n// This is the element with the data attribute.\nconst spanFixedAttrs = '{mlang %lang}';\n// The end span doesn't need information about the used language.\nconst spanMultilangEnd = spanFixedAttrs.replace('begin', 'end') + '>{mlang}';\n// Helper functions\nconst trim = v => v.toString().replace(/^\\s+/, '').replace(/\\s+$/, '');\nconst isNull = a => a === null || a === undefined;\n\n/**\n * Marker to remember that the submit button was hit.\n * @type {boolean}\n * @private\n */\nlet _isSubmit = false;\n\n/**\n * @type {object}\n * @private\n */\nconst _options = {};\n\n/**\n * Convert {mlang xx} and {mlang} strings to spans, so we can style them visually.\n * Remove superflous whitespace while at it.\n * @param {tinymce.Editor} ed\n * @return {string}\n */\nconst addVisualStyling = function(ed) {\n\n let content = ed.getContent();\n\n // Do not use a variable whether text is already highlighted, do a check for the existing class\n // because this is safe for many tiny element windows at one page.\n if (content.indexOf(spanClass) !== -1) {\n return content;\n }\n\n let intermediateReplacements = [];\n // Helper function to check wether te matching {mland} is inside a html node or not.\n const replaceHelper = function(m0, m1, p, c) {\n // Check if we are inside a html node by checking where the next > and < are.\n const textBefore = c.substring(0, p);\n const textAfter = c.substring(p + m0.length);\n const open = textBefore.lastIndexOf('<');\n const close = textBefore.lastIndexOf('>');\n // If open < close we are outside a html node and can modify the original content\n if (open < close) {\n if (!m1) {\n m0 = spanMultilangEnd;\n } else {\n m0 = spanMultilangBegin.replace(new RegExp('%lang', 'g'), m1);\n }\n } // If open > close we are inside a html node and do not modify the original content.\n // Store the matched {mlang} tag in the replacements array.\n intermediateReplacements.push(m0);\n return `${textBefore}___~~${intermediateReplacements.length}~~___${textAfter}`;\n };\n // First look for any {mlang} tags in the content string and do a preg_replace with the corresponding\n // tag that encapsulated the {mlang} tag so that the {mlang} is highlighted.\n // eslint-disable-next-line no-constant-condition\n while (1) {\n const m = content.match(new RegExp('{\\\\s*mlang(\\\\s+([^}]+?))?\\\\s*}', 'i'));\n if (!m) {\n break;\n }\n content = replaceHelper(m[0], m[2], m.index, m.input);\n }\n // Revert all placeholders back to the original {mlang} tags.\n for (let i = 0; i < intermediateReplacements.length; i++) {\n content = content.replace(`___~~${i + 1}~~___`, intermediateReplacements[i]);\n }\n\n // Check for the traditional tags, in case these were used as well in the text.\n // Any tag must be replaced with a {mlang XX}\n // and the corresponding closing must be replaced by {mlang}.\n // To handle this, we must convert the string into a DOMDocument so that any span.multilang tag can be searched\n // and replaced.\n const dom = new DOMParser();\n const doc = dom.parseFromString(content, 'text/html');\n if (doc.children.length === 0) { // Should not happen, but anyway, keep the check.\n return content;\n }\n const nodes = doc.querySelectorAll('span.multilang');\n if (nodes.length === 0) {\n return content;\n }\n for (const span of nodes) {\n const newSpan = spanMultilangBegin\n .replace(new RegExp('%lang', 'g'), span.getAttribute('lang'))\n .replace('mceNonEditable', 'mceNonEditable fallback')\n + span.innerHTML\n + spanMultilangEnd\n .replace('mceNonEditable', 'mceNonEditable fallback');\n // Insert the replacement string after the span tag itself by converting it into a html fragment.\n span.insertAdjacentHTML('afterend', newSpan);\n // Once the new tags are placed at the correct position, we can remove the original span tag.\n span.remove();\n }\n // Convert the DOMDocument into a string again.\n return doc.getElementsByTagName('body')[0].innerHTML;\n};\n\n/**\n * Remove the spans we added in _add_visual_styling() to leave only the {mlang xx} and {mlang} tags.\n * Also make sure we lowercase the multilang 'tags'\n * @param {tinymce.Editor} ed\n */\nconst removeVisualStyling = function(ed) {\n ['begin', 'end'].forEach(function(t) {\n for (const span of ed.dom.select('span.multilang-' + t)) {\n if (t === 'begin' && span.classList.contains('fallback')) {\n // This placeholder tag was created from an oldstyle tag.\n let innerHTML = '';\n let end = span;\n let toRemove = [];\n // Search the corresponding closing tag.\n while (end) {\n end = end.nextSibling;\n if (isNull(end)) { // Got a parent that does not exist. Stop here.\n break;\n }\n if (!isNull(end.classList) && end.classList.contains('multilang-end')) {\n // We found the multilang-end node, that needs to be removed, and also, we can stop here.\n toRemove.push(end);\n break;\n }\n // Sibling inside the tags need to be preserved, but moved to the innerHTML of the real\n // span tag. Therefore, collect the node content as string and remember the real nodes\n // to remove them later.\n if (end.nodeType === 3) {\n innerHTML += end.nodeValue;\n } else if (end.nodeType === 1) {\n innerHTML += end.outerHTML;\n }\n toRemove.push(end);\n }\n if (!isNull(end)) {\n // Extract the language from the {mlang XX} tag.\n const lang = span.innerHTML.match(new RegExp('{\\\\s*mlang\\\\s+([^}]+?)\\\\s*}', 'i'));\n if (lang) {\n /* Do not add the dir attribute as it breaks the Moodle language filter.\n // Right to left default languages.\n const rtlLanguages = getRTLLanguages();\n const langCode = lang[1];\n // Add dir=\"rtl\" to the html tag any time the overall document direction is right-to-left.\n const dir = rtlLanguages.includes(langCode) ? 'rtl' : 'ltr';\n // Do not add the dir attribute as it breaks the Moodle language filter.\n const newHTML = `${innerHTML}`;\n */\n const newHTML = `${innerHTML}`;\n ed.dom.setOuterHTML(span, newHTML);\n // And remove the other siblings.\n for (end of toRemove) {\n ed.dom.remove(end);\n }\n }\n }\n } else {\n // Normal placeholder tag, just restore the innerHTML that is {mlang XX} or {mlang}-\n ed.dom.setOuterHTML(span, span.innerHTML.toLowerCase());\n }\n }\n });\n};\n\n/**\n * At the current selection lookup for the current node. If we are inside a special span that encapsulates\n * the {lang} tag, then look for the corresponding opening or closing tag, depending on what's set in the\n * search param.\n * @param {tinymce.Editor} ed\n * @param {string} search\n * @return {Node|null} The encapsulating span tag if found.\n */\nconst getHighlightNodeFromSelect = function(ed, search) {\n let span;\n ed.dom.getParents(ed.selection.getStart(), elm => {\n // Are we in a span that highlights the lang tag.\n if (!isNull(elm.classList)) {\n // If we are on an opening/closing lang tag, we need to search for the corresponding opening/closing tag.\n const pair = search === 'begin' ? 'end' : 'begin';\n if (elm.classList.contains('multilang-' + pair)) {\n span = elm;\n do {\n // If we look for begin, go back siblings, otherwise look fnext siblings until end is found.\n span = search === 'begin' ? span.previousSibling : span.nextSibling;\n } while (!isNull(span) && (isNull(span.classList) || !span.classList.contains('multilang-' + search)));\n } else if (elm.classList.contains('multilang-' + search)) {\n // We are already on the correct tag we search for\n span = elm;\n }\n }\n });\n return span;\n};\n\n/**\n * From the given text (that is derived from a selection) we try to check if we have block elements selected and\n * in case yes, how many.\n * Return an object with:\n * el: the first block element node from the string\n * cnt: number of block elements found on the first level\n * In case the text fragment is not valid parsable HTML, then null and 0 is returned.\n * @param {string} text\n * @return {object}\n */\nconst getBlockElement = function(text) {\n let result = {el: null, cnt: 0};\n const dom = new DOMParser();\n const body = dom.parseFromString(text, 'text/html').body;\n // If the children nodes start with no block element, then just quit here.\n if (body.firstChild.nodeType !== Node.ELEMENT_NODE) {\n return result;\n }\n // These are not all block elements, we check for some only where the lang tags should be placed inside.\n const blockTags = ['address', 'article', 'aside', 'blockquote',\n 'dd', 'div', 'dl', 'dt', 'figcaption', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',\n 'li', 'ol', 'p', 'pre', 'section', 'tfoot', 'ul'];\n for (let i = 0; i < body.children.length; i++) {\n if (body.children[i].nodeType !== Node.ELEMENT_NODE) {\n continue;\n }\n if (blockTags.indexOf(body.children[i].tagName.toString().toLowerCase()) !== -1) {\n result.cnt += 1;\n if (isNull(result.el)) {\n result.el = body.children[i];\n }\n }\n }\n return result;\n};\n\n/**\n * Check for the parent hierarchy elements, if there's a context toolbar container, then hide it.\n * @param {Node} el\n */\nconst hideContentToolbar = function(el) {\n while (!isNull(el)) {\n if (el.nodeType === Node.ELEMENT_NODE &&\n !isNull(el.getAttribute('class')) &&\n el.getAttribute('class').indexOf('tox-pop-') != -1\n ) {\n el.style.display = 'none';\n return;\n }\n el = el.parentNode;\n }\n};\n\n/**\n * When loading the editor for the first time, add the spans for highlighting the lang tags.\n * These are highlighted with the appropriate css only.\n * In addition pass some options to the plugin instance.\n * @param {tinymce.Editor} ed\n * @param {object} options\n */\nconst onInit = function(ed, options) {\n Object.keys(options).forEach(function(key) {\n _options[key] = options[key];\n });\n ed.setContent(addVisualStyling(ed));\n if (isContentToHighlight(ed)) {\n ed.dom.addStyle(getHighlightCss(ed));\n }\n};\n\n/**\n * When the source code view dialogue is show, we must remove the highlight spans from the editor content\n * and also add them again when the dialogue is closed.\n * @param {tinymce.Editor} ed\n * @param {object} content\n */\nconst onBeforeGetContent = function(ed, content) {\n if (!isNull(content.source_view) && content.source_view === true) {\n // If the user clicks on 'Cancel' or the close button on the html\n // source code dialog view, make sure we re-add the visual styling.\n const onClose = function(ed) {\n ed.off('close', onClose);\n ed.setContent(addVisualStyling(ed));\n };\n const observer = new MutationObserver((mutations, obs) => {\n const viewSrcModal = document.querySelector('[data-region=\"modal\"]');\n if (viewSrcModal) {\n viewSrcModal.addEventListener('click', (event) => {\n const {action} = event.target.dataset;\n if (['cancel', 'save', 'hide'].includes(action)) {\n onClose(ed);\n }\n });\n // Stop observing once the modal is found.\n obs.disconnect();\n }\n });\n observer.observe(document.body, {childList: true, subtree: true});\n removeVisualStyling(ed);\n }\n};\n\n/**\n * When the submit button is hit, the marker spans are removed. However, if there's an error\n * in saving the content (via ajax) the editor remains with the cleaned content. Therefore,\n * we need to add the marker span elements once again when the user tries to change the content\n * of the editor.\n * @param {tinymce.Editor} ed\n */\nconst onFocus = function(ed) {\n if (_isSubmit) {\n // eslint-disable-next-line camelcase\n ed.setContent(addVisualStyling(ed), {no_events: true});\n _isSubmit = false;\n }\n};\n\n/**\n * Fires when the form containing the editor is submitted. Remove all the marker span elements.\n * @param {tinymce.Editor} ed\n */\nconst onSubmit = function(ed) {\n removeVisualStyling(ed);\n _isSubmit = true;\n};\n\n/**\n * Check for key press when something is deleted. If that happens inside a highlight span\n * tag, then remove this tag and the corresponding that open/closes this lang tag.\n * @param {tinymce.Editor} ed\n * @param {Object} event\n */\nconst onDelete = function(ed, event) {\n // We are not in composing mode, have not clicked and key or was not pressed.\n if (event.isComposing || (isNull(event.clientX) && event.keyCode !== 46 && event.keyCode !== 8)) {\n return;\n }\n // In case we clicked, check that we clicked an icon (this must have been the trash icon in the context menu).\n if (!isNull(event.clientX) &&\n (event.target.nodeType !== Node.ELEMENT_NODE || (event.target.nodeName !== 'path' && event.target.nodeName !== 'svg'))) {\n return;\n }\n // Conditions match either key or was pressed, or an click on an svg icon was done.\n // Check if we are inside a span for the language tag.\n const begin = getHighlightNodeFromSelect(ed, 'begin');\n const end = getHighlightNodeFromSelect(ed, 'end');\n // Only if both, start and end tags are found, then delete the nodes here and prevent the default handling\n // because the stuff to be deleted is already gone.\n if (!isNull(begin) && !isNull(end)) {\n event.preventDefault();\n ed.dom.remove(begin);\n ed.dom.remove(end);\n if (!isNull(event.clientX)) {\n hideContentToolbar(event.target);\n }\n cleanupBogus(ed);\n }\n};\n\n/**\n * In the tinyMCE of Moodle 4.1 and 4.2 some leftovers from the element selection can be seen when the source code\n * is displayed. Remove these. Apparently 4.3 does not have this problem anymore.\n * @param {tinymce.Editor} ed\n */\nconst cleanupBogus = function(ed) {\n for (const span of ed.dom.select('span[class*=\"multilang\"')) {\n const p = span.parentElement;\n if (!isNull(p.classList) && p.classList.contains('mce-offscreen-selection')) {\n ed.dom.remove(p);\n }\n }\n};\n\n/**\n * The action when a language icon or menu entry is clicked. This adds the {mlang} tags at the current content\n * position or around the selection.\n * @param {tinymce.Editor} ed\n * @param {string} iso\n * @param {Event} event\n */\nconst applyLanguage = function(ed, iso, event) {\n if (isNull(iso)) {\n return;\n }\n if (iso === \"remove\") {\n const elements = ed.contentDocument.body;\n // Find all elements with the class \"multilang-begin\" or \"multilang-end\".\n const multiLangElements = elements.querySelectorAll('.multilang-begin, .multilang-end');\n multiLangElements.forEach(element => {\n ed.dom.remove(element);\n });\n return;\n }\n const regexLang = /%lang/g;\n let text = ed.selection.getContent();\n // Selection is empty, just insert the lang opening and closing tag\n // together with a space where the user may add the content.\n if (trim(text) === '') {\n // Event is set when the context menu was hit, here the editor lost the previously selected node. Therfore,\n // don't do anything.\n if (!isNull(event)) {\n hideContentToolbar(event.target);\n return;\n }\n let newtext = spanMultilangBegin.replace(regexLang, iso) + ' ' + spanMultilangEnd;\n if (!mlangFilterExists(ed)) {\n // No mlang filter, add the fallback class to the highlight spans so that these are translated\n // to the standard elements.\n newtext = newtext.replaceAll('mceNonEditable', 'mceNonEditable fallback');\n }\n ed.insertContent(newtext);\n return;\n }\n // Hide context toolbar, because at any subsequent call the node is not selected anymore.\n if (!isNull(event)) {\n hideContentToolbar(event.target);\n }\n // No matter if we have syntax highlighting enabled or not, the spans around the language tags exist\n // in the WYSIWYG mode. So check if we are on a special span that encapsulates the language tags. Search\n // for the start span tag.\n const span = getHighlightNodeFromSelect(ed, 'begin');\n // If we have a span, then it's the opening tag, and we just replace this one with the new iso.\n if (!isNull(span)) {\n let replacement = spanMultilangBegin.replace(regexLang, iso);\n if (span.classList.contains('fallback')) {\n replacement = replacement.replace('mceNonEditable', 'mceNonEditable fallback');\n }\n ed.dom.setOuterHTML(span, replacement);\n cleanupBogus(ed);\n return;\n }\n // Check if we have language tags inside the selection:\n if (text.indexOf('multilang-begin') !== -1 || text.indexOf('multilang-end') !== -1) {\n ed.notificationManager.open({\n text: _options.langInSelectionErrMsg,\n type: 'error',\n });\n return;\n }\n const block = getBlockElement(text);\n if (!isNull(block.el)) {\n if (block.cnt === 1) {\n // We have a block element selected, such as a hX or p tag. Then keep this tag and place the\n // language tags inside but around the content of the block element.\n let newtext = spanMultilangBegin.replace(regexLang, iso) + block.el.innerHTML + spanMultilangEnd;\n if (!mlangFilterExists(ed)) { // No mlang filter, add the fallback class to the highlight spans.\n newtext = newtext.replaceAll('mceNonEditable', 'mceNonEditable fallback');\n }\n block.el.innerHTML = newtext;\n ed.selection.setContent(block.el.outerHTML);\n return;\n }\n if (!mlangFilterExists(ed)) {\n ed.notificationManager.open({\n text: _options.multipleBlocksErrMsg,\n type: 'error',\n });\n return;\n }\n }\n // Not inside a lang tag, insert a new opening and closing tag with the selection inside.\n let newtext = spanMultilangBegin.replace(regexLang, iso) + text + spanMultilangEnd;\n if (!mlangFilterExists(ed)) { // No mlang filter, add the fallback class to the highlight spans.\n newtext = newtext.replaceAll('mceNonEditable', 'mceNonEditable fallback');\n }\n ed.selection.setContent(newtext);\n};\n\nexport {\n onInit,\n onBeforeGetContent,\n onFocus,\n onSubmit,\n onDelete,\n applyLanguage\n};\n"],"names":["spanClass","spanFixedAttrs","spanMultilangBegin","spanMultilangEnd","replace","isNull","a","_isSubmit","_options","addVisualStyling","ed","content","getContent","indexOf","intermediateReplacements","replaceHelper","m0","m1","p","c","textBefore","substring","textAfter","length","lastIndexOf","RegExp","push","m","match","index","input","i","doc","DOMParser","parseFromString","children","nodes","querySelectorAll","span","newSpan","getAttribute","innerHTML","insertAdjacentHTML","remove","getElementsByTagName","removeVisualStyling","forEach","t","dom","select","classList","contains","end","toRemove","nextSibling","nodeType","nodeValue","outerHTML","lang","newHTML","setOuterHTML","toLowerCase","getHighlightNodeFromSelect","search","getParents","selection","getStart","elm","pair","previousSibling","hideContentToolbar","el","Node","ELEMENT_NODE","style","display","parentNode","options","Object","keys","key","setContent","addStyle","source_view","onClose","off","MutationObserver","mutations","obs","viewSrcModal","document","querySelector","addEventListener","event","action","target","dataset","includes","disconnect","observe","body","childList","subtree","no_events","isComposing","clientX","keyCode","nodeName","begin","preventDefault","cleanupBogus","parentElement","iso","contentDocument","element","regexLang","text","toString","newtext","replaceAll","insertContent","replacement","notificationManager","open","langInSelectionErrMsg","type","block","result","cnt","firstChild","blockTags","tagName","getBlockElement","multipleBlocksErrMsg"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"ui.min.js","sources":["../src/ui.js"],"sourcesContent":["// This file is part of Moodle - https://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 * Commands for the plugin logic of the Moodle tiny_multilang2 plugin.\n *\n * @module tiny_multilang2\n * @author Iñaki Arenaza \n * @author Stephan Robotta \n * @author Tai Le Tan \n * @copyright 2015 onwards Iñaki Arenaza & Mondragon Unibertsitatea\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {getHighlightCss, isContentToHighlight, mlangFilterExists} from './options';\n\n// This class inside a identified the {mlang} tag that is encapsulated in a span.\nconst spanClass = 'multilang-begin mceNonEditable';\n// This is the element with the data attribute.\nconst spanFixedAttrs = '{mlang %lang}';\n// The end span doesn't need information about the used language.\nconst spanMultilangEnd = spanFixedAttrs.replace('begin', 'end') + '>{mlang}';\n// Helper functions\nconst trim = v => v.toString().replace(/^\\s+/, '').replace(/\\s+$/, '');\nconst isNull = a => a === null || a === undefined;\n\n/**\n * Marker to remember that the submit button was hit.\n * @type {boolean}\n * @private\n */\nlet _isSubmit = false;\n\n/**\n * @type {object}\n * @private\n */\nconst _options = {};\n\n/**\n * Convert {mlang xx} and {mlang} strings to spans, so we can style them visually.\n * Remove superflous whitespace while at it.\n * @param {tinymce.Editor} ed\n * @return {string}\n */\nconst addVisualStyling = function(ed) {\n\n let content = ed.getContent();\n\n // Do not use a variable whether text is already highlighted, do a check for the existing class\n // because this is safe for many tiny element windows at one page.\n if (content.indexOf(spanClass) !== -1) {\n return content;\n }\n\n let intermediateReplacements = [];\n // Helper function to check wether te matching {mland} is inside a html node or not.\n const replaceHelper = function(m0, m1, p, c) {\n // Check if we are inside a html node by checking where the next > and < are.\n const textBefore = c.substring(0, p);\n const textAfter = c.substring(p + m0.length);\n const open = textBefore.lastIndexOf('<');\n const close = textBefore.lastIndexOf('>');\n // If open < close we are outside a html node and can modify the original content\n if (open < close) {\n if (!m1) {\n m0 = spanMultilangEnd;\n } else {\n m0 = spanMultilangBegin.replace(new RegExp('%lang', 'g'), m1);\n }\n } // If open > close we are inside a html node and do not modify the original content.\n // Store the matched {mlang} tag in the replacements array.\n intermediateReplacements.push(m0);\n return `${textBefore}___~~${intermediateReplacements.length}~~___${textAfter}`;\n };\n // First look for any {mlang} tags in the content string and do a preg_replace with the corresponding\n // tag that encapsulated the {mlang} tag so that the {mlang} is highlighted.\n // eslint-disable-next-line no-constant-condition\n while (1) {\n const m = content.match(new RegExp('{\\\\s*mlang(\\\\s+([^}]+?))?\\\\s*}', 'i'));\n if (!m) {\n break;\n }\n content = replaceHelper(m[0], m[2], m.index, m.input);\n }\n // Revert all placeholders back to the original {mlang} tags.\n for (let i = 0; i < intermediateReplacements.length; i++) {\n content = content.replace(`___~~${i + 1}~~___`, intermediateReplacements[i]);\n }\n\n // Check for the traditional tags, in case these were used as well in the text.\n // Any tag must be replaced with a {mlang XX}\n // and the corresponding closing must be replaced by {mlang}.\n // To handle this, we must convert the string into a DOMDocument so that any span.multilang tag can be searched\n // and replaced.\n const dom = new DOMParser();\n const doc = dom.parseFromString(content, 'text/html');\n if (doc.children.length === 0) { // Should not happen, but anyway, keep the check.\n return content;\n }\n const nodes = doc.querySelectorAll('span.multilang');\n if (nodes.length === 0) {\n return content;\n }\n for (const span of nodes) {\n const newSpan = spanMultilangBegin\n .replace(new RegExp('%lang', 'g'), span.getAttribute('lang'))\n .replace('mceNonEditable', 'mceNonEditable fallback')\n + span.innerHTML\n + spanMultilangEnd\n .replace('mceNonEditable', 'mceNonEditable fallback');\n // Insert the replacement string after the span tag itself by converting it into a html fragment.\n span.insertAdjacentHTML('afterend', newSpan);\n // Once the new tags are placed at the correct position, we can remove the original span tag.\n span.remove();\n }\n // Convert the DOMDocument into a string again.\n return doc.getElementsByTagName('body')[0].innerHTML;\n};\n\n/**\n * Remove the spans we added in _add_visual_styling() to leave only the {mlang xx} and {mlang} tags.\n * Also make sure we lowercase the multilang 'tags'\n * @param {tinymce.Editor} ed\n */\nconst removeVisualStyling = function(ed) {\n ['begin', 'end'].forEach(function(t) {\n for (const span of ed.dom.select('span.multilang-' + t)) {\n if (t === 'begin' && span.classList.contains('fallback')) {\n // This placeholder tag was created from an oldstyle tag.\n let innerHTML = '';\n let end = span;\n let toRemove = [];\n // Search the corresponding closing tag.\n while (end) {\n end = end.nextSibling;\n if (isNull(end)) { // Got a parent that does not exist. Stop here.\n break;\n }\n if (!isNull(end.classList) && end.classList.contains('multilang-end')) {\n // We found the multilang-end node, that needs to be removed, and also, we can stop here.\n toRemove.push(end);\n break;\n }\n // Sibling inside the tags need to be preserved, but moved to the innerHTML of the real\n // span tag. Therefore, collect the node content as string and remember the real nodes\n // to remove them later.\n if (end.nodeType === 3) {\n innerHTML += end.nodeValue;\n } else if (end.nodeType === 1) {\n innerHTML += end.outerHTML;\n }\n toRemove.push(end);\n }\n if (!isNull(end)) {\n // Extract the language from the {mlang XX} tag.\n const lang = span.innerHTML.match(new RegExp('{\\\\s*mlang\\\\s+([^}]+?)\\\\s*}', 'i'));\n if (lang) {\n /* Do not add the dir attribute as it breaks the Moodle language filter.\n // Right to left default languages.\n const rtlLanguages = getRTLLanguages();\n const langCode = lang[1];\n // Add dir=\"rtl\" to the html tag any time the overall document direction is right-to-left.\n const dir = rtlLanguages.includes(langCode) ? 'rtl' : 'ltr';\n // Do not add the dir attribute as it breaks the Moodle language filter.\n const newHTML = `${innerHTML}`;\n */\n const newHTML = `${innerHTML}`;\n ed.dom.setOuterHTML(span, newHTML);\n // And remove the other siblings.\n for (end of toRemove) {\n ed.dom.remove(end);\n }\n }\n }\n } else {\n // Normal placeholder tag, just restore the innerHTML that is {mlang XX} or {mlang}-\n ed.dom.setOuterHTML(span, span.innerHTML.toLowerCase());\n }\n }\n });\n};\n\n/**\n * At the current selection lookup for the current node. If we are inside a special span that encapsulates\n * the {lang} tag, then look for the corresponding opening or closing tag, depending on what's set in the\n * search param.\n * @param {tinymce.Editor} ed\n * @param {string} search\n * @return {Node|null} The encapsulating span tag if found.\n */\nconst getHighlightNodeFromSelect = function(ed, search) {\n let span;\n ed.dom.getParents(ed.selection.getStart(), elm => {\n // Are we in a span that highlights the lang tag.\n if (!isNull(elm.classList)) {\n // If we are on an opening/closing lang tag, we need to search for the corresponding opening/closing tag.\n const pair = search === 'begin' ? 'end' : 'begin';\n if (elm.classList.contains('multilang-' + pair)) {\n span = elm;\n do {\n // If we look for begin, go back siblings, otherwise look fnext siblings until end is found.\n span = search === 'begin' ? span.previousSibling : span.nextSibling;\n } while (!isNull(span) && (isNull(span.classList) || !span.classList.contains('multilang-' + search)));\n } else if (elm.classList.contains('multilang-' + search)) {\n // We are already on the correct tag we search for\n span = elm;\n }\n }\n });\n return span;\n};\n\n/**\n * From the given text (that is derived from a selection) we try to check if we have block elements selected and\n * in case yes, how many.\n * Return an object with:\n * el: the first block element node from the string\n * cnt: number of block elements found on the first level\n * In case the text fragment is not valid parsable HTML, then null and 0 is returned.\n * @param {string} text\n * @return {object}\n */\nconst getBlockElement = function(text) {\n let result = {el: null, cnt: 0};\n const dom = new DOMParser();\n const body = dom.parseFromString(text, 'text/html').body;\n // If the children nodes start with no block element, then just quit here.\n if (body.firstChild.nodeType !== Node.ELEMENT_NODE) {\n return result;\n }\n // These are not all block elements, we check for some only where the lang tags should be placed inside.\n const blockTags = ['address', 'article', 'aside', 'blockquote',\n 'dd', 'div', 'dl', 'dt', 'figcaption', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',\n 'li', 'ol', 'p', 'pre', 'section', 'tfoot', 'ul'];\n for (let i = 0; i < body.children.length; i++) {\n if (body.children[i].nodeType !== Node.ELEMENT_NODE) {\n continue;\n }\n if (blockTags.indexOf(body.children[i].tagName.toString().toLowerCase()) !== -1) {\n result.cnt += 1;\n if (isNull(result.el)) {\n result.el = body.children[i];\n }\n }\n }\n return result;\n};\n\n/**\n * Check for the parent hierarchy elements, if there's a context toolbar container, then hide it.\n * @param {Node} el\n */\nconst hideContentToolbar = function(el) {\n while (!isNull(el)) {\n if (el.nodeType === Node.ELEMENT_NODE &&\n !isNull(el.getAttribute('class')) &&\n el.getAttribute('class').indexOf('tox-pop-') != -1\n ) {\n el.style.display = 'none';\n return;\n }\n el = el.parentNode;\n }\n};\n\n/**\n * When loading the editor for the first time, add the spans for highlighting the lang tags.\n * These are highlighted with the appropriate css only.\n * In addition pass some options to the plugin instance.\n * @param {tinymce.Editor} ed\n * @param {object} options\n */\nconst onInit = function(ed, options) {\n Object.keys(options).forEach(function(key) {\n _options[key] = options[key];\n });\n ed.setContent(addVisualStyling(ed));\n if (isContentToHighlight(ed)) {\n ed.dom.addStyle(getHighlightCss(ed));\n }\n};\n\n/**\n * When the source code view dialogue is show, we must remove the highlight spans from the editor content\n * and also add them again when the dialogue is closed.\n * @param {tinymce.Editor} ed\n * @param {object} content\n */\nconst onBeforeGetContent = function(ed, content) {\n if (!isNull(content.source_view) && content.source_view === true) {\n // If the user clicks on 'Cancel' or the close button on the html\n // source code dialog view, make sure we re-add the visual styling.\n const onClose = function(ed) {\n ed.off('close', onClose);\n ed.setContent(addVisualStyling(ed));\n };\n const observer = new MutationObserver((mutations, obs) => {\n const viewSrcModal = document.querySelector('[data-region=\"modal\"]');\n if (viewSrcModal) {\n viewSrcModal.addEventListener('click', (event) => {\n const {action} = event.target.dataset;\n if (['cancel', 'save', 'hide'].includes(action)) {\n onClose(ed);\n }\n });\n // Stop observing once the modal is found.\n obs.disconnect();\n return;\n }\n const tinyMceModal = document.querySelector('.tox-dialog-wrap');\n if (tinyMceModal) {\n ed.on('CloseWindow', () => {\n onClose(ed);\n });\n obs.disconnect();\n }\n });\n observer.observe(document.body, {childList: true, subtree: true});\n removeVisualStyling(ed);\n }\n};\n\n/**\n * When the submit button is hit, the marker spans are removed. However, if there's an error\n * in saving the content (via ajax) the editor remains with the cleaned content. Therefore,\n * we need to add the marker span elements once again when the user tries to change the content\n * of the editor.\n * @param {tinymce.Editor} ed\n */\nconst onFocus = function(ed) {\n if (_isSubmit) {\n // eslint-disable-next-line camelcase\n ed.setContent(addVisualStyling(ed), {no_events: true});\n _isSubmit = false;\n }\n};\n\n/**\n * Fires when the form containing the editor is submitted. Remove all the marker span elements.\n * @param {tinymce.Editor} ed\n */\nconst onSubmit = function(ed) {\n removeVisualStyling(ed);\n _isSubmit = true;\n};\n\n/**\n * Check for key press when something is deleted. If that happens inside a highlight span\n * tag, then remove this tag and the corresponding that open/closes this lang tag.\n * @param {tinymce.Editor} ed\n * @param {Object} event\n */\nconst onDelete = function(ed, event) {\n // We are not in composing mode, have not clicked and key or was not pressed.\n if (event.isComposing || (isNull(event.clientX) && event.keyCode !== 46 && event.keyCode !== 8)) {\n return;\n }\n // In case we clicked, check that we clicked an icon (this must have been the trash icon in the context menu).\n if (!isNull(event.clientX) &&\n (event.target.nodeType !== Node.ELEMENT_NODE || (event.target.nodeName !== 'path' && event.target.nodeName !== 'svg'))) {\n return;\n }\n // Conditions match either key or was pressed, or an click on an svg icon was done.\n // Check if we are inside a span for the language tag.\n const begin = getHighlightNodeFromSelect(ed, 'begin');\n const end = getHighlightNodeFromSelect(ed, 'end');\n // Only if both, start and end tags are found, then delete the nodes here and prevent the default handling\n // because the stuff to be deleted is already gone.\n if (!isNull(begin) && !isNull(end)) {\n event.preventDefault();\n ed.dom.remove(begin);\n ed.dom.remove(end);\n if (!isNull(event.clientX)) {\n hideContentToolbar(event.target);\n }\n cleanupBogus(ed);\n }\n};\n\n/**\n * In the tinyMCE of Moodle 4.1 and 4.2 some leftovers from the element selection can be seen when the source code\n * is displayed. Remove these. Apparently 4.3 does not have this problem anymore.\n * @param {tinymce.Editor} ed\n */\nconst cleanupBogus = function(ed) {\n for (const span of ed.dom.select('span[class*=\"multilang\"')) {\n const p = span.parentElement;\n if (!isNull(p.classList) && p.classList.contains('mce-offscreen-selection')) {\n ed.dom.remove(p);\n }\n }\n};\n\n/**\n * The action when a language icon or menu entry is clicked. This adds the {mlang} tags at the current content\n * position or around the selection.\n * @param {tinymce.Editor} ed\n * @param {string} iso\n * @param {Event} event\n */\nconst applyLanguage = function(ed, iso, event) {\n if (isNull(iso)) {\n return;\n }\n if (iso === \"remove\") {\n const elements = ed.contentDocument.body;\n // Find all elements with the class \"multilang-begin\" or \"multilang-end\".\n const multiLangElements = elements.querySelectorAll('.multilang-begin, .multilang-end');\n multiLangElements.forEach(element => {\n ed.dom.remove(element);\n });\n return;\n }\n const regexLang = /%lang/g;\n let text = ed.selection.getContent();\n // Selection is empty, just insert the lang opening and closing tag\n // together with a space where the user may add the content.\n if (trim(text) === '') {\n // Event is set when the context menu was hit, here the editor lost the previously selected node. Therfore,\n // don't do anything.\n if (!isNull(event)) {\n hideContentToolbar(event.target);\n return;\n }\n let newtext = spanMultilangBegin.replace(regexLang, iso) + ' ' + spanMultilangEnd;\n if (!mlangFilterExists(ed)) {\n // No mlang filter, add the fallback class to the highlight spans so that these are translated\n // to the standard elements.\n newtext = newtext.replaceAll('mceNonEditable', 'mceNonEditable fallback');\n }\n ed.insertContent(newtext);\n return;\n }\n // Hide context toolbar, because at any subsequent call the node is not selected anymore.\n if (!isNull(event)) {\n hideContentToolbar(event.target);\n }\n // No matter if we have syntax highlighting enabled or not, the spans around the language tags exist\n // in the WYSIWYG mode. So check if we are on a special span that encapsulates the language tags. Search\n // for the start span tag.\n const span = getHighlightNodeFromSelect(ed, 'begin');\n // If we have a span, then it's the opening tag, and we just replace this one with the new iso.\n if (!isNull(span)) {\n let replacement = spanMultilangBegin.replace(regexLang, iso);\n if (span.classList.contains('fallback')) {\n replacement = replacement.replace('mceNonEditable', 'mceNonEditable fallback');\n }\n ed.dom.setOuterHTML(span, replacement);\n cleanupBogus(ed);\n return;\n }\n // Check if we have language tags inside the selection:\n if (text.indexOf('multilang-begin') !== -1 || text.indexOf('multilang-end') !== -1) {\n ed.notificationManager.open({\n text: _options.langInSelectionErrMsg,\n type: 'error',\n });\n return;\n }\n const block = getBlockElement(text);\n if (!isNull(block.el)) {\n if (block.cnt === 1) {\n // We have a block element selected, such as a hX or p tag. Then keep this tag and place the\n // language tags inside but around the content of the block element.\n let newtext = spanMultilangBegin.replace(regexLang, iso) + block.el.innerHTML + spanMultilangEnd;\n if (!mlangFilterExists(ed)) { // No mlang filter, add the fallback class to the highlight spans.\n newtext = newtext.replaceAll('mceNonEditable', 'mceNonEditable fallback');\n }\n block.el.innerHTML = newtext;\n ed.selection.setContent(block.el.outerHTML);\n return;\n }\n if (!mlangFilterExists(ed)) {\n ed.notificationManager.open({\n text: _options.multipleBlocksErrMsg,\n type: 'error',\n });\n return;\n }\n }\n // Not inside a lang tag, insert a new opening and closing tag with the selection inside.\n let newtext = spanMultilangBegin.replace(regexLang, iso) + text + spanMultilangEnd;\n if (!mlangFilterExists(ed)) { // No mlang filter, add the fallback class to the highlight spans.\n newtext = newtext.replaceAll('mceNonEditable', 'mceNonEditable fallback');\n }\n ed.selection.setContent(newtext);\n};\n\nexport {\n onInit,\n onBeforeGetContent,\n onFocus,\n onSubmit,\n onDelete,\n applyLanguage\n};\n"],"names":["spanClass","spanFixedAttrs","spanMultilangBegin","spanMultilangEnd","replace","isNull","a","_isSubmit","_options","addVisualStyling","ed","content","getContent","indexOf","intermediateReplacements","replaceHelper","m0","m1","p","c","textBefore","substring","textAfter","length","lastIndexOf","RegExp","push","m","match","index","input","i","doc","DOMParser","parseFromString","children","nodes","querySelectorAll","span","newSpan","getAttribute","innerHTML","insertAdjacentHTML","remove","getElementsByTagName","removeVisualStyling","forEach","t","dom","select","classList","contains","end","toRemove","nextSibling","nodeType","nodeValue","outerHTML","lang","newHTML","setOuterHTML","toLowerCase","getHighlightNodeFromSelect","search","getParents","selection","getStart","elm","pair","previousSibling","hideContentToolbar","el","Node","ELEMENT_NODE","style","display","parentNode","options","Object","keys","key","setContent","addStyle","source_view","onClose","off","MutationObserver","mutations","obs","viewSrcModal","document","querySelector","addEventListener","event","action","target","dataset","includes","disconnect","on","observe","body","childList","subtree","no_events","isComposing","clientX","keyCode","nodeName","begin","preventDefault","cleanupBogus","parentElement","iso","contentDocument","element","regexLang","text","toString","newtext","replaceAll","insertContent","replacement","notificationManager","open","langInSelectionErrMsg","type","block","result","cnt","firstChild","blockTags","tagName","getBlockElement","multipleBlocksErrMsg"],"mappings":"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"} \ No newline at end of file diff --git a/amd/src/ui.js b/amd/src/ui.js index 4598746..804bf03 100644 --- a/amd/src/ui.js +++ b/amd/src/ui.js @@ -320,6 +320,14 @@ const onBeforeGetContent = function(ed, content) { }); // Stop observing once the modal is found. obs.disconnect(); + return; + } + const tinyMceModal = document.querySelector('.tox-dialog-wrap'); + if (tinyMceModal) { + ed.on('CloseWindow', () => { + onClose(ed); + }); + obs.disconnect(); } }); observer.observe(document.body, {childList: true, subtree: true});