diff --git a/lib/editor/tiny/amd/build/defaults.min.js.map b/lib/editor/tiny/amd/build/defaults.min.js.map index 9a9f0a80fbd68..6b02ee7531bfd 100644 --- a/lib/editor/tiny/amd/build/defaults.min.js.map +++ b/lib/editor/tiny/amd/build/defaults.min.js.map @@ -1 +1 @@ -{"version":3,"file":"defaults.min.js","sources":["../src/defaults.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/* eslint-disable max-len, */\n\n/**\n * TinyMCE Editor Upstream defaults.\n *\n * @module editor_tiny/editor\n * @copyright 2022 Andrew Lyons \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n/**\n * The upstream defaults for the TinyMCE Menu.\n *\n * This value is defined in the TinyMCE documentation, but not exported anywhere useful.\n * https://www.tiny.cloud/docs/tinymce/6/menus-configuration-options/#menu\n *\n * @returns {Object}\n */\nexport const getDefaultMenu = () => {\n return {\n file: {title: 'File', items: 'newdocument restoredraft | preview | export print | deleteallconversations'},\n edit: {title: 'Edit', items: 'undo redo | cut copy paste pastetext | selectall | searchreplace'},\n view: {title: 'View', items: 'code | visualaid visualchars visualblocks | spellchecker | preview fullscreen | showcomments'},\n insert: {title: 'Insert', items: 'image link media addcomment pageembed template codesample inserttable | charmap emoticons hr | pagebreak nonbreaking anchor tableofcontents | insertdatetime'},\n format: {title: 'Format', items: 'bold italic underline strikethrough superscript subscript codeformat | styles blocks fontfamily fontsize align lineheight | forecolor backcolor | language | removeformat'},\n tools: {title: 'Tools', items: 'spellchecker spellcheckerlanguage | a11ycheck code wordcount'},\n table: {title: 'Table', items: 'inserttable | cell row column | advtablesort | tableprops deletetable'},\n help: {title: 'Help', items: 'help'}\n };\n};\n\n/**\n * The default toolbar configuration to use.\n *\n * This is based upon the default value used if no toolbar is specified.\n *\n * https://www.tiny.cloud/docs/tinymce/6/menus-configuration-options/#menu\n *\n * @returns {Object}\n */\nexport const getDefaultToolbar = () => {\n return [\n {\n name: 'content',\n items: [],\n },\n {\n name: 'formatting',\n items: [\n 'bold',\n 'italic',\n ],\n },\n {\n name: 'history',\n items: [\n 'undo',\n 'redo',\n ],\n },\n {\n name: 'alignment',\n items: [\n 'alignleft',\n 'aligncenter',\n 'alignright',\n 'alignjustify',\n ],\n },\n {\n name: 'indentation',\n items: [\n 'outdent',\n 'indent',\n ],\n },\n {\n name: 'lists',\n items: [\n 'bullist',\n 'numlist',\n ],\n },\n {\n name: 'comments',\n items: ['addcomment'],\n },\n ];\n};\n\n/**\n * The default quickbars_insert_toolbar configuration to use.\n *\n * This is based upon the default value used if no toolbar is specified.\n *\n * https://www.tiny.cloud/docs/tinymce/6/quickbars/#quickbars_selection_toolbar\n *\n * @returns {string}\n */\nexport const getDefaultQuickbarsSelectionToolbar = () => 'bold italic | quicklink h2 h3 blockquote';\n\n/**\n * The default quickbars_insert_toolbar configuration to use.\n *\n * This is based upon the default value used if no toolbar is specified.\n *\n * https://www.tiny.cloud/docs/tinymce/6/quickbars/#quickbars_insert_toolbar\n *\n * @returns {string}\n */\nexport const getDefaultQuickbarsInsertToolbar = () => 'quickimage quicktable';\n\n/**\n * The default quickbars_insert_toolbar configuration to use.\n *\n * This is based upon the default value used if no toolbar is specified.\n *\n * https://www.tiny.cloud/docs/tinymce/6/quickbars/#quickbars_image_toolbar\n *\n * @returns {string}\n */\nexport const getDefaultQuickbarsImageToolbar = () => 'alignleft aligncenter alignright';\n\n/**\n * Get the default configuration provided by TinyMCE.\n *\n * @returns {object}\n */\nexport const getDefaultConfiguration = () => ({\n // Toolbar configuration.\n // https://www.tiny.cloud/docs/tinymce/6/toolbar-configuration-options/\n // TODO: Move this configuration to a passed-in option.\n // eslint-disable-next-line camelcase\n toolbar_mode: 'sliding',\n toolbar: getDefaultToolbar(),\n\n // Quickbars Selection Toolbar configuration.\n // https://www.tiny.cloud/docs/tinymce/6/quickbars/#quickbars_selection_toolbar\n // eslint-disable-next-line camelcase\n quickbars_selection_toolbar: getDefaultQuickbarsSelectionToolbar(),\n\n // Quickbars Select Toolbar configuration.\n // https://www.tiny.cloud/docs/tinymce/6/quickbars/#quickbars_insert_toolbar\n // eslint-disable-next-line camelcase\n quickbars_insert_toolbar: getDefaultQuickbarsInsertToolbar(),\n\n // Quickbars Image Toolbar configuration.\n // https://www.tiny.cloud/docs/tinymce/6/quickbars/#quickbars_image_toolbar\n // eslint-disable-next-line camelcase\n quickbars_image_toolbar: getDefaultQuickbarsImageToolbar(),\n\n\n // Menu configuration.\n // https://www.tiny.cloud/docs/tinymce/6/menus-configuration-options/\n // TODO: Move this configuration to a passed-in option.\n menu: getDefaultMenu(),\n\n // TODO Add mobile configuration.\n // Mobile configuration.\n // https://www.tiny.cloud/docs/tinymce/6/tinymce-for-mobile/\n // This will include mobile-specific toolbar, and menu options.\n\n // Skins\n skin: 'oxide',\n});\n"],"names":["getDefaultMenu","file","title","items","edit","view","insert","format","tools","table","help","getDefaultToolbar","name","getDefaultQuickbarsSelectionToolbar","getDefaultQuickbarsInsertToolbar","getDefaultQuickbarsImageToolbar","toolbar_mode","toolbar","quickbars_selection_toolbar","quickbars_insert_toolbar","quickbars_image_toolbar","menu","skin"],"mappings":";;;;;;;;MAiCaA,eAAiB,KACnB,CACHC,KAAM,CAACC,MAAO,OAAQC,MAAO,8EAC7BC,KAAM,CAACF,MAAO,OAAQC,MAAO,oEAC7BE,KAAM,CAACH,MAAO,OAAQC,MAAO,gGAC7BG,OAAQ,CAACJ,MAAO,SAAUC,MAAO,gKACjCI,OAAQ,CAACL,MAAO,SAAUC,MAAO,6KACjCK,MAAO,CAACN,MAAO,QAASC,MAAO,gEAC/BM,MAAO,CAACP,MAAO,QAASC,MAAO,yEAC/BO,KAAM,CAACR,MAAO,OAAQC,MAAO,uDAaxBQ,kBAAoB,IACtB,CACH,CACIC,KAAM,UACNT,MAAO,IAEX,CACIS,KAAM,aACNT,MAAO,CACH,OACA,WAGR,CACIS,KAAM,UACNT,MAAO,CACH,OACA,SAGR,CACIS,KAAM,YACNT,MAAO,CACH,YACA,cACA,aACA,iBAGR,CACIS,KAAM,cACNT,MAAO,CACH,UACA,WAGR,CACIS,KAAM,QACNT,MAAO,CACH,UACA,YAGR,CACIS,KAAM,WACNT,MAAO,CAAC,mEAcPU,oCAAsC,IAAM,kIAW5CC,iCAAmC,IAAM,yGAWzCC,gCAAkC,IAAM,6IAOd,MAKnCC,aAAc,UACdC,QA7FO,CACH,CACIL,KAAM,UACNT,MAAO,IAEX,CACIS,KAAM,aACNT,MAAO,CACH,OACA,WAGR,CACIS,KAAM,UACNT,MAAO,CACH,OACA,SAGR,CACIS,KAAM,YACNT,MAAO,CACH,YACA,cACA,aACA,iBAGR,CACIS,KAAM,cACNT,MAAO,CACH,UACA,WAGR,CACIS,KAAM,QACNT,MAAO,CACH,UACA,YAGR,CACIS,KAAM,WACNT,MAAO,CAAC,gBAsDhBe,4BAxCqD,2CA6CrDC,yBAlCkD,wBAuClDC,wBA5BiD,mCAkCjDC,KAxIO,CACHpB,KAAM,CAACC,MAAO,OAAQC,MAAO,8EAC7BC,KAAM,CAACF,MAAO,OAAQC,MAAO,oEAC7BE,KAAM,CAACH,MAAO,OAAQC,MAAO,gGAC7BG,OAAQ,CAACJ,MAAO,SAAUC,MAAO,gKACjCI,OAAQ,CAACL,MAAO,SAAUC,MAAO,6KACjCK,MAAO,CAACN,MAAO,QAASC,MAAO,gEAC/BM,MAAO,CAACP,MAAO,QAASC,MAAO,yEAC/BO,KAAM,CAACR,MAAO,OAAQC,MAAO,SAwIjCmB,KAAM"} \ No newline at end of file +{"version":3,"file":"defaults.min.js","sources":["../src/defaults.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/* eslint-disable max-len, */\n\n/**\n * TinyMCE Editor Upstream defaults.\n *\n * @module editor_tiny/editor\n * @copyright 2022 Andrew Lyons \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n/**\n * The upstream defaults for the TinyMCE Menu.\n *\n * This value is defined in the TinyMCE documentation, but not exported anywhere useful.\n * https://www.tiny.cloud/docs/tinymce/6/menus-configuration-options/#menu\n *\n * @returns {Object}\n */\nexport const getDefaultMenu = () => {\n return {\n file: {title: 'File', items: 'newdocument restoredraft | preview | export print | deleteallconversations'},\n edit: {title: 'Edit', items: 'undo redo | cut copy paste pastetext | selectall | searchreplace'},\n view: {title: 'View', items: 'code | visualaid visualchars visualblocks | spellchecker | preview fullscreen | showcomments'},\n insert: {title: 'Insert', items: 'image link media addcomment pageembed template codesample inserttable | charmap emoticons hr | pagebreak nonbreaking anchor tableofcontents | insertdatetime'},\n format: {title: 'Format', items: 'bold italic underline strikethrough superscript subscript codeformat | styles blocks fontfamily fontsize align lineheight | forecolor backcolor | language | removeformat'},\n tools: {title: 'Tools', items: 'spellchecker spellcheckerlanguage | a11ycheck code wordcount'},\n table: {title: 'Table', items: 'inserttable | cell row column | advtablesort | tableprops deletetable'},\n help: {title: 'Help', items: 'help'}\n };\n};\n\n/**\n * The default toolbar configuration to use.\n *\n * This is based upon the default value used if no toolbar is specified.\n *\n * https://www.tiny.cloud/docs/tinymce/6/menus-configuration-options/#menu\n *\n * @returns {Object}\n */\nexport const getDefaultToolbar = () => {\n return [\n {\n name: 'content',\n items: [],\n },\n {\n name: 'formatting',\n items: [\n 'bold',\n 'italic',\n ],\n },\n {\n name: 'history',\n items: [\n 'undo',\n 'redo',\n ],\n },\n {\n name: 'alignment',\n items: [\n 'alignleft',\n 'aligncenter',\n 'alignright',\n 'alignjustify',\n ],\n },\n {\n name: 'indentation',\n items: [\n 'outdent',\n 'indent',\n ],\n },\n {\n name: 'lists',\n items: [\n 'bullist',\n 'numlist',\n ],\n },\n {\n name: 'comments',\n items: ['addcomment'],\n },\n ];\n};\n\n/**\n * The default quickbars_insert_toolbar configuration to use.\n *\n * This is based upon the default value used if no toolbar is specified.\n *\n * https://www.tiny.cloud/docs/tinymce/6/quickbars/#quickbars_selection_toolbar\n *\n * @returns {string}\n */\nexport const getDefaultQuickbarsSelectionToolbar = () => 'bold italic | quicklink h2 h3 blockquote';\n\n/**\n * The default quickbars_insert_toolbar configuration to use.\n *\n * This is based upon the default value used if no toolbar is specified.\n *\n * https://www.tiny.cloud/docs/tinymce/6/quickbars/#quickbars_insert_toolbar\n *\n * @returns {string}\n */\nexport const getDefaultQuickbarsInsertToolbar = () => 'quickimage quicktable';\n\n/**\n * The default quickbars_insert_toolbar configuration to use.\n *\n * This is based upon the default value used if no toolbar is specified.\n *\n * https://www.tiny.cloud/docs/tinymce/6/quickbars/#quickbars_image_toolbar\n *\n * @returns {string}\n */\nexport const getDefaultQuickbarsImageToolbar = () => 'alignleft aligncenter alignright';\n\n/**\n * Get the default configuration provided by TinyMCE.\n *\n * @returns {object}\n */\nexport const getDefaultConfiguration = () => ({\n // Toolbar configuration.\n // https://www.tiny.cloud/docs/tinymce/6/toolbar-configuration-options/\n // TODO: Move this configuration to a passed-in option.\n // eslint-disable-next-line camelcase\n toolbar_mode: 'sliding',\n toolbar: getDefaultToolbar(),\n\n // Quickbars Selection Toolbar configuration.\n // https://www.tiny.cloud/docs/tinymce/6/quickbars/#quickbars_selection_toolbar\n // eslint-disable-next-line camelcase\n quickbars_selection_toolbar: getDefaultQuickbarsSelectionToolbar(),\n\n // Quickbars Select Toolbar configuration.\n // https://www.tiny.cloud/docs/tinymce/6/quickbars/#quickbars_insert_toolbar\n // eslint-disable-next-line camelcase\n quickbars_insert_toolbar: getDefaultQuickbarsInsertToolbar(),\n\n // Quickbars Image Toolbar configuration.\n // https://www.tiny.cloud/docs/tinymce/6/quickbars/#quickbars_image_toolbar\n // eslint-disable-next-line camelcase\n quickbars_image_toolbar: getDefaultQuickbarsImageToolbar(),\n\n\n // Menu configuration.\n // https://www.tiny.cloud/docs/tinymce/6/menus-configuration-options/\n // TODO: Move this configuration to a passed-in option.\n menu: getDefaultMenu(),\n\n // Mobile configuration.\n // At this time we will use the default TinyMCE mobile configuration.\n // https://www.tiny.cloud/docs/tinymce/6/tinymce-for-mobile/\n\n // Skins\n skin: 'oxide',\n});\n"],"names":["getDefaultMenu","file","title","items","edit","view","insert","format","tools","table","help","getDefaultToolbar","name","getDefaultQuickbarsSelectionToolbar","getDefaultQuickbarsInsertToolbar","getDefaultQuickbarsImageToolbar","toolbar_mode","toolbar","quickbars_selection_toolbar","quickbars_insert_toolbar","quickbars_image_toolbar","menu","skin"],"mappings":";;;;;;;;MAiCaA,eAAiB,KACnB,CACHC,KAAM,CAACC,MAAO,OAAQC,MAAO,8EAC7BC,KAAM,CAACF,MAAO,OAAQC,MAAO,oEAC7BE,KAAM,CAACH,MAAO,OAAQC,MAAO,gGAC7BG,OAAQ,CAACJ,MAAO,SAAUC,MAAO,gKACjCI,OAAQ,CAACL,MAAO,SAAUC,MAAO,6KACjCK,MAAO,CAACN,MAAO,QAASC,MAAO,gEAC/BM,MAAO,CAACP,MAAO,QAASC,MAAO,yEAC/BO,KAAM,CAACR,MAAO,OAAQC,MAAO,uDAaxBQ,kBAAoB,IACtB,CACH,CACIC,KAAM,UACNT,MAAO,IAEX,CACIS,KAAM,aACNT,MAAO,CACH,OACA,WAGR,CACIS,KAAM,UACNT,MAAO,CACH,OACA,SAGR,CACIS,KAAM,YACNT,MAAO,CACH,YACA,cACA,aACA,iBAGR,CACIS,KAAM,cACNT,MAAO,CACH,UACA,WAGR,CACIS,KAAM,QACNT,MAAO,CACH,UACA,YAGR,CACIS,KAAM,WACNT,MAAO,CAAC,mEAcPU,oCAAsC,IAAM,kIAW5CC,iCAAmC,IAAM,yGAWzCC,gCAAkC,IAAM,6IAOd,MAKnCC,aAAc,UACdC,QA7FO,CACH,CACIL,KAAM,UACNT,MAAO,IAEX,CACIS,KAAM,aACNT,MAAO,CACH,OACA,WAGR,CACIS,KAAM,UACNT,MAAO,CACH,OACA,SAGR,CACIS,KAAM,YACNT,MAAO,CACH,YACA,cACA,aACA,iBAGR,CACIS,KAAM,cACNT,MAAO,CACH,UACA,WAGR,CACIS,KAAM,QACNT,MAAO,CACH,UACA,YAGR,CACIS,KAAM,WACNT,MAAO,CAAC,gBAsDhBe,4BAxCqD,2CA6CrDC,yBAlCkD,wBAuClDC,wBA5BiD,mCAkCjDC,KAxIO,CACHpB,KAAM,CAACC,MAAO,OAAQC,MAAO,8EAC7BC,KAAM,CAACF,MAAO,OAAQC,MAAO,oEAC7BE,KAAM,CAACH,MAAO,OAAQC,MAAO,gGAC7BG,OAAQ,CAACJ,MAAO,SAAUC,MAAO,gKACjCI,OAAQ,CAACL,MAAO,SAAUC,MAAO,6KACjCK,MAAO,CAACN,MAAO,QAASC,MAAO,gEAC/BM,MAAO,CAACP,MAAO,QAASC,MAAO,yEAC/BO,KAAM,CAACR,MAAO,OAAQC,MAAO,SAuIjCmB,KAAM"} \ No newline at end of file diff --git a/lib/editor/tiny/amd/build/editor.min.js b/lib/editor/tiny/amd/build/editor.min.js index 8d739f2f001af..6bf3f55e93db4 100644 --- a/lib/editor/tiny/amd/build/editor.min.js +++ b/lib/editor/tiny/amd/build/editor.min.js @@ -1,3 +1,3 @@ -define("editor_tiny/editor",["exports","core/pending","./defaults","./loader","./options"],(function(_exports,_pending,_defaults,_loader,Options){var obj;Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.setupForTarget=_exports.setupForElementId=_exports.getInstanceForElementId=_exports.getInstanceForElement=_exports.getAllInstances=_exports.configureDefaultEditor=void 0,_pending=(obj=_pending)&&obj.__esModule?obj:{default:obj},Options=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}(Options);var _systemImportTransformerGlobalIdentifier="undefined"!=typeof window?window:"undefined"!=typeof self?self:"undefined"!=typeof global?global:{};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)}const instanceMap=new Map;let defaultOptions={};const importPluginList=async pluginList=>{const pluginHandlers=await Promise.all(pluginList.map((pluginPath=>-1===pluginPath.indexOf("/")?Promise.resolve(pluginPath):"function"==typeof _systemImportTransformerGlobalIdentifier.define&&_systemImportTransformerGlobalIdentifier.define.amd?new Promise((function(resolve,reject){_systemImportTransformerGlobalIdentifier.require([pluginPath],resolve,reject)})):"undefined"!=typeof module&&module.exports&&"undefined"!=typeof require||"undefined"!=typeof module&&module.component&&_systemImportTransformerGlobalIdentifier.require&&"component"===_systemImportTransformerGlobalIdentifier.require.loader?Promise.resolve(require(pluginPath)):Promise.resolve(_systemImportTransformerGlobalIdentifier[pluginPath])))),pluginNames=pluginHandlers.map((pluginConfig=>"string"==typeof pluginConfig?pluginConfig:Array.isArray(pluginConfig)?pluginConfig[0]:null)).filter((value=>value));return{pluginNames:pluginNames,pluginConfig:pluginHandlers.map((pluginConfig=>Array.isArray(pluginConfig)?pluginConfig[1]:null)).filter((value=>value))}};_exports.getAllInstances=()=>new Map(instanceMap.entries());_exports.getInstanceForElementId=elementId=>getInstanceForElement(document.getElementById(elementId));const getInstanceForElement=element=>{const instance=instanceMap.get(element);if(!instance||!instance.removed)return instance;instanceMap.remove(element)};_exports.getInstanceForElement=getInstanceForElement;_exports.setupForElementId=_ref=>{let{elementId:elementId,options:options}=_ref;const target=document.getElementById(elementId);return setupForTarget(target,options)};(async()=>{const lang=document.querySelector("html").lang,[tinyMCE,langData]=await Promise.all([(0,_loader.getTinyMCE)(),(language=lang,fetch("".concat(M.cfg.wwwroot,"/lib/editor/tiny/lang.php/").concat(M.cfg.langrev,"/").concat(language)).then((response=>response.json())))]);var language;tinyMCE.addI18n(lang,langData)})();const getPlugins=function(){let{plugins:plugins=null}=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return plugins||(defaultOptions.plugins?defaultOptions.plugins:{})},getEditorConfiguration=(target,tinyMCE,options,pluginValues)=>{const{pluginNames:pluginNames,pluginConfig:pluginConfig}=pluginValues,instanceConfig=((target,tinyMCE,options,plugins)=>{const lang=document.querySelector("html").lang;return Object.assign({},(0,_defaults.getDefaultConfiguration)(),{base_url:_loader.baseUrl,target:target,language:lang,content_css:[options.css],convert_urls:!1,a11y_advanced_options:!0,quickbars_insert_toolbar:"",plugins:[...plugins],skin:"oxide",promotion:!1,branding:options.branding,setup:editor=>{Options.register(editor,options)}})})(target,0,options,pluginNames);return instanceConfig.menu.file&&(instanceConfig.menu.file.items=""),pluginConfig.filter((pluginConfig=>"function"==typeof pluginConfig.configure)).forEach((pluginConfig=>{Object.assign(instanceConfig,pluginConfig.configure(instanceConfig,options))})),Object.assign(instanceConfig,Options.getInitialPluginConfiguration(options)),instanceConfig},setupForTarget=async function(target){let options=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};const instance=getInstanceForElement(target);if(instance)return Promise.resolve(instance);const pendingPromise=new _pending.default("editor_tiny/editor:setupForTarget"),plugins=getPlugins(options),[tinyMCE,pluginValues]=await Promise.all([(0,_loader.getTinyMCE)(),importPluginList(Object.keys(plugins))]),existingEditor=tinyMCE.EditorManager.get(target.id);if(existingEditor){if(existingEditor.targetElm.closest("body")){if(existingEditor.targetElm===target)return pendingPromise.resolve(),Promise.resolve(existingEditor);throw pendingPromise.resolve(),new Error("TinyMCE instance already exists for different target with same ID")}existingEditor.destroy()}const instanceConfig=getEditorConfiguration(target,0,options,pluginValues),[editor]=await tinyMCE.init(instanceConfig);return instanceMap.set(target,editor),editor.on("remove",(_ref2=>{let{target:target}=_ref2;instanceMap.delete(target.targetElm)})),pendingPromise.resolve(),editor};_exports.setupForTarget=setupForTarget;_exports.configureDefaultEditor=function(){let options=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};defaultOptions=options}})); +define("editor_tiny/editor",["exports","core/pending","./defaults","./loader","./options"],(function(_exports,_pending,_defaults,_loader,Options){var obj;Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.setupForTarget=_exports.setupForElementId=_exports.getInstanceForElementId=_exports.getInstanceForElement=_exports.getAllInstances=_exports.configureDefaultEditor=void 0,_pending=(obj=_pending)&&obj.__esModule?obj:{default:obj},Options=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}(Options);var _systemImportTransformerGlobalIdentifier="undefined"!=typeof window?window:"undefined"!=typeof self?self:"undefined"!=typeof global?global:{};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)}const instanceMap=new Map;let defaultOptions={};const importPluginList=async pluginList=>{const pluginHandlers=await Promise.all(pluginList.map((pluginPath=>-1===pluginPath.indexOf("/")?Promise.resolve(pluginPath):"function"==typeof _systemImportTransformerGlobalIdentifier.define&&_systemImportTransformerGlobalIdentifier.define.amd?new Promise((function(resolve,reject){_systemImportTransformerGlobalIdentifier.require([pluginPath],resolve,reject)})):"undefined"!=typeof module&&module.exports&&"undefined"!=typeof require||"undefined"!=typeof module&&module.component&&_systemImportTransformerGlobalIdentifier.require&&"component"===_systemImportTransformerGlobalIdentifier.require.loader?Promise.resolve(require(pluginPath)):Promise.resolve(_systemImportTransformerGlobalIdentifier[pluginPath])))),pluginNames=pluginHandlers.map((pluginConfig=>"string"==typeof pluginConfig?pluginConfig:Array.isArray(pluginConfig)?pluginConfig[0]:null)).filter((value=>value));return{pluginNames:pluginNames,pluginConfig:pluginHandlers.map((pluginConfig=>Array.isArray(pluginConfig)?pluginConfig[1]:null)).filter((value=>value))}};_exports.getAllInstances=()=>new Map(instanceMap.entries());_exports.getInstanceForElementId=elementId=>getInstanceForElement(document.getElementById(elementId));const getInstanceForElement=element=>{const instance=instanceMap.get(element);if(!instance||!instance.removed)return instance;instanceMap.remove(element)};_exports.getInstanceForElement=getInstanceForElement;_exports.setupForElementId=_ref=>{let{elementId:elementId,options:options}=_ref;const target=document.getElementById(elementId);return setupForTarget(target,options)};(async()=>{const lang=document.querySelector("html").lang,[tinyMCE,langData]=await Promise.all([(0,_loader.getTinyMCE)(),(language=lang,fetch("".concat(M.cfg.wwwroot,"/lib/editor/tiny/lang.php/").concat(M.cfg.langrev,"/").concat(language)).then((response=>response.json())))]);var language;tinyMCE.addI18n(lang,langData)})();const getPlugins=function(){let{plugins:plugins=null}=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return plugins||(defaultOptions.plugins?defaultOptions.plugins:{})},getEditorConfiguration=(target,tinyMCE,options,pluginValues)=>{const{pluginNames:pluginNames,pluginConfig:pluginConfig}=pluginValues,instanceConfig=((target,tinyMCE,options,plugins)=>{const lang=document.querySelector("html").lang;return Object.assign({},(0,_defaults.getDefaultConfiguration)(),{base_url:_loader.baseUrl,target:target,language:lang,content_css:[options.css],convert_urls:!1,a11y_advanced_options:!0,quickbars_insert_toolbar:"",block_formats:"Paragraph=p; Heading 3= h3; Heading 4= h4; Heading 5= h5; Heading 6= h6;",plugins:[...plugins],skin:"oxide",promotion:!1,branding:options.branding,setup:editor=>{Options.register(editor,options)}})})(target,0,options,pluginNames);return instanceConfig.menu.file&&(instanceConfig.menu.file.items=""),instanceConfig.menu.format&&(instanceConfig.menu.format.items=instanceConfig.menu.format.items.replace(/forecolor ?/,"").replace(/backcolor ?/,"").replace(/fontfamily ?/,"").replace(/fontsize ?/,"").replace(/styles ?/,"").replaceAll(/\| *\|/g,"|")),pluginConfig.filter((pluginConfig=>"function"==typeof pluginConfig.configure)).forEach((pluginConfig=>{Object.assign(instanceConfig,pluginConfig.configure(instanceConfig,options))})),Object.assign(instanceConfig,Options.getInitialPluginConfiguration(options)),instanceConfig},setupForTarget=async function(target){let options=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};const instance=getInstanceForElement(target);if(instance)return Promise.resolve(instance);const pendingPromise=new _pending.default("editor_tiny/editor:setupForTarget"),plugins=getPlugins(options),[tinyMCE,pluginValues]=await Promise.all([(0,_loader.getTinyMCE)(),importPluginList(Object.keys(plugins))]),existingEditor=tinyMCE.EditorManager.get(target.id);if(existingEditor){if(existingEditor.targetElm.closest("body")){if(existingEditor.targetElm===target)return pendingPromise.resolve(),Promise.resolve(existingEditor);throw pendingPromise.resolve(),new Error("TinyMCE instance already exists for different target with same ID")}existingEditor.destroy()}const instanceConfig=getEditorConfiguration(target,0,options,pluginValues),[editor]=await tinyMCE.init(instanceConfig);return instanceMap.set(target,editor),editor.on("remove",(_ref2=>{let{target:target}=_ref2;instanceMap.delete(target.targetElm)})),pendingPromise.resolve(),editor};_exports.setupForTarget=setupForTarget;_exports.configureDefaultEditor=function(){let options=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};defaultOptions=options}})); //# sourceMappingURL=editor.min.js.map \ No newline at end of file diff --git a/lib/editor/tiny/amd/build/editor.min.js.map b/lib/editor/tiny/amd/build/editor.min.js.map index 1782654e3d3e8..a6cdccbe38752 100644 --- a/lib/editor/tiny/amd/build/editor.min.js.map +++ b/lib/editor/tiny/amd/build/editor.min.js.map @@ -1 +1 @@ -{"version":3,"file":"editor.min.js","sources":["../src/editor.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 * TinyMCE Editor Manager.\n *\n * @module editor_tiny/editor\n * @copyright 2022 Andrew Lyons \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Pending from 'core/pending';\nimport {getDefaultConfiguration} from './defaults';\nimport {getTinyMCE, baseUrl} from './loader';\nimport * as Options from './options';\n\n/**\n * Storage for the TinyMCE instances on the page.\n * @type {Map}\n */\nconst instanceMap = new Map();\n\n/**\n * The default editor configuration.\n * @type {Object}\n */\nlet defaultOptions = {};\n\n/**\n * Require the modules for the named set of TinyMCE plugins.\n *\n * @param {string[]} pluginList The list of plugins\n * @return {Promise[]} A matching set of Promises relating to the requested plugins\n */\nconst importPluginList = async(pluginList) => {\n // Fetch all of the plugins from the list of plugins.\n // If a plugin contains a '/' then it is assumed to be a Moodle AMD module to import.\n const pluginHandlers = await Promise.all(pluginList.map(pluginPath => {\n if (pluginPath.indexOf('/') === -1) {\n // A standard TinyMCE Plugin.\n return Promise.resolve(pluginPath);\n }\n\n return import(pluginPath);\n }));\n\n // Normalise the plugin data to a list of plugin names.\n // Two formats are supported:\n // - a string; and\n // - an array whose first element is the plugin name, and the second element is the plugin configuration.\n const pluginNames = pluginHandlers.map((pluginConfig) => {\n if (typeof pluginConfig === 'string') {\n return pluginConfig;\n }\n if (Array.isArray(pluginConfig)) {\n return pluginConfig[0];\n }\n return null;\n }).filter((value) => value);\n\n // Fetch the list of pluginConfig handlers.\n const pluginConfig = pluginHandlers.map((pluginConfig) => {\n if (Array.isArray(pluginConfig)) {\n return pluginConfig[1];\n }\n return null;\n }).filter((value) => value);\n\n return {\n pluginNames,\n pluginConfig,\n };\n};\n\n/**\n * Fetch the language data for the specified language.\n *\n * @param {string} language The language identifier\n * @returns {object}\n */\nconst fetchLanguage = (language) => fetch(\n `${M.cfg.wwwroot}/lib/editor/tiny/lang.php/${M.cfg.langrev}/${language}`\n).then(response => response.json());\n\n/**\n * Get a list of all Editors in a Map, keyed by the DOM Node that the Editor is associated with.\n *\n * @returns {Map}\n */\nexport const getAllInstances = () => new Map(instanceMap.entries());\n\n/**\n * Get the TinyMCE instance for the specified Node ID.\n *\n * @param {string} elementId\n * @returns {TinyMCE|undefined}\n */\nexport const getInstanceForElementId = elementId => getInstanceForElement(document.getElementById(elementId));\n\n/*\n * Get the TinyMCE instance for the specified HTMLElement.\n *\n * @param {HTMLElement} element\n * @returns {TinyMCE|undefined}\n */\nexport const getInstanceForElement = element => {\n const instance = instanceMap.get(element);\n if (instance && instance.removed) {\n instanceMap.remove(element);\n return undefined;\n }\n return instance;\n};\n\n/**\n * Set up TinyMCE for the selector at the specified HTML Node id.\n *\n * @param {object} config The configuration required to setup the editor\n * @param {string} config.elementId The HTML Node ID\n * @param {Object} config.options The editor plugin configuration\n * @return {Promise} The TinyMCE instance\n */\nexport const setupForElementId = ({elementId, options}) => {\n const target = document.getElementById(elementId);\n return setupForTarget(target, options);\n};\n\n/**\n * Initialise the page with standard TinyMCE requirements.\n *\n * Currently this includes the language taken from the HTML lang property.\n */\nconst initialisePage = async() => {\n const lang = document.querySelector('html').lang;\n\n const [tinyMCE, langData] = await Promise.all([getTinyMCE(), fetchLanguage(lang)]);\n tinyMCE.addI18n(lang, langData);\n};\ninitialisePage();\n\n/**\n * Get the list of plugins to load for the specified configuration.\n *\n * If the specified configuration does not include a plugin configuration, then return the default configuration.\n *\n * @param {object} options\n * @param {array} [options.plugins=null] The plugin list\n * @returns {object}\n */\nconst getPlugins = ({plugins = null} = {}) => {\n if (plugins) {\n return plugins;\n }\n\n if (defaultOptions.plugins) {\n return defaultOptions.plugins;\n }\n\n return {};\n};\n\n/**\n * Get the standard configuration for the specified options.\n *\n * @param {Node} target\n * @param {tinyMCE} tinyMCE\n * @param {object} options\n * @param {Array} plugins\n * @returns {object}\n */\nconst getStandardConfig = (target, tinyMCE, options, plugins) => {\n const lang = document.querySelector('html').lang;\n\n return Object.assign({}, getDefaultConfiguration(), {\n // eslint-disable-next-line camelcase\n base_url: baseUrl,\n\n // Set the editor target.\n // https://www.tiny.cloud/docs/tinymce/6/editor-important-options/#target\n target,\n\n // Set the language.\n // https://www.tiny.cloud/docs/tinymce/6/ui-localization/#language\n // eslint-disable-next-line camelcase\n language: lang,\n\n // Load the editor stylesheet into the editor iframe.\n // https://www.tiny.cloud/docs/tinymce/6/add-css-options/\n // eslint-disable-next-line camelcase\n content_css: [\n options.css,\n ],\n\n // Do not convert URLs to relative URLs.\n // https://www.tiny.cloud/docs/tinymce/6/url-handling/#convert_urls\n // eslint-disable-next-line camelcase\n convert_urls: false,\n\n // Enabled 'advanced' a11y options.\n // This includes allowing role=\"presentation\" from the image uploader.\n // https://www.tiny.cloud/docs/tinymce/6/accessibility/\n // eslint-disable-next-line camelcase\n a11y_advanced_options: true,\n\n // Disable quickbars entirely.\n // The UI is not ideal and we'll wait for it to improve in future before we enable it in Moodle.\n // eslint-disable-next-line camelcase\n quickbars_insert_toolbar: '',\n\n // The list of plugins to include in the instance.\n // https://www.tiny.cloud/docs/tinymce/6/editor-important-options/#plugins\n plugins: [\n ...plugins,\n ],\n\n // TODO Add mobile configuration.\n // Mobile configuration.\n // https://www.tiny.cloud/docs/tinymce/6/tinymce-for-mobile/\n // This will include mobile-specific toolbar, and menu options.\n\n // Skins\n skin: 'oxide',\n\n // Remove the \"Upgrade\" link for Tiny.\n // https://www.tiny.cloud/docs/tinymce/6/editor-premium-upgrade-promotion/\n promotion: false,\n\n // Allow the administrator to disable branding.\n // https://www.tiny.cloud/docs/tinymce/6/statusbar-configuration-options/#branding\n branding: options.branding,\n\n setup: (editor) => {\n Options.register(editor, options);\n },\n });\n};\n\n/**\n * Fetch the TinyMCE configuration for this editor instance.\n *\n * @param {HTMLElement} target\n * @param {TinyMCE} tinyMCE The TinyMCE API\n * @param {Object} options The editor plugin configuration\n * @param {object} pluginValues\n * @param {object} pluginValues.pluginConfig The list of plugin configuration\n * @param {object} pluginValues.pluginNames The list of plugins to load\n * @returns {object} The TinyMCE Configuration\n */\nconst getEditorConfiguration = (target, tinyMCE, options, pluginValues) => {\n const {\n pluginNames,\n pluginConfig,\n } = pluginValues;\n\n // Allow plugins to modify the configuration.\n // This seems a little strange, but we must double-process the config slightly.\n\n // First we fetch the standard configuration.\n const instanceConfig = getStandardConfig(target, tinyMCE, options, pluginNames);\n\n // Next we make any standard changes.\n // Here we remove the file menu, as it doesn't offer any useful functionality.\n // We only empty the items list so that a plugin may choose to add to it themselves later if they wish.\n if (instanceConfig.menu.file) {\n instanceConfig.menu.file.items = '';\n }\n\n // Next we call the `configure` function for any plugin which defines it.\n // We pass the current instanceConfig in here, to allow them to make certain changes to the global configuration.\n // For example, to add themselves to any menu, toolbar, and so on.\n // Any plugin which wishes to have configuration options must register those options here.\n pluginConfig.filter((pluginConfig) => typeof pluginConfig.configure === 'function').forEach((pluginConfig) => {\n Object.assign(instanceConfig, pluginConfig.configure(instanceConfig, options));\n });\n\n // Next we convert the plugin configuration into a format that TinyMCE understands.\n Object.assign(instanceConfig, Options.getInitialPluginConfiguration(options));\n\n return instanceConfig;\n};\n\n/**\n * Set up TinyMCE for the HTML Element.\n *\n * @param {HTMLElement} target\n * @param {Object} [options={}] The editor plugin configuration\n * @return {Promise} The TinyMCE instance\n */\nexport const setupForTarget = async(target, options = {}) => {\n const instance = getInstanceForElement(target);\n if (instance) {\n return Promise.resolve(instance);\n }\n\n // Register a new pending promise to ensure that Behat waits for the editor setup to complete before continuing.\n const pendingPromise = new Pending('editor_tiny/editor:setupForTarget');\n\n // Get the list of plugins.\n const plugins = getPlugins(options);\n\n // Fetch the tinyMCE API, and instantiate the plugins.\n const [tinyMCE, pluginValues] = await Promise.all([\n getTinyMCE(),\n importPluginList(Object.keys(plugins)),\n ]);\n\n // TinyMCE uses the element ID as a map key internally, even if the target has changed.\n // In the case where we have an editor in a modal form which has been detached from the DOM, but the editor not removed,\n // we need to manually destroy the editor.\n // We could theoretically do this with a Mutation Observer, but in some cases the Node may be moved,\n // or added back elsewhere in the DOM.\n const existingEditor = tinyMCE.EditorManager.get(target.id);\n if (existingEditor) {\n if (existingEditor.targetElm.closest('body')) {\n if (existingEditor.targetElm === target) {\n pendingPromise.resolve();\n return Promise.resolve(existingEditor);\n } else {\n pendingPromise.resolve();\n throw new Error('TinyMCE instance already exists for different target with same ID');\n }\n } else {\n existingEditor.destroy();\n }\n }\n\n // Get the editor configuration for this editor.\n const instanceConfig = getEditorConfiguration(target, tinyMCE, options, pluginValues);\n\n // Initialise the editor instance for the given configuration.\n // At this point any plugin which has configuration options registered will have them applied for this instance.\n const [editor] = await tinyMCE.init(instanceConfig);\n\n // Store the editor instance in the instanceMap and register a listener on removal to remove it from the map.\n instanceMap.set(target, editor);\n editor.on('remove', ({target}) => {\n // Handle removal of the editor from the map on destruction.\n instanceMap.delete(target.targetElm);\n });\n\n pendingPromise.resolve();\n return editor;\n};\n\n/**\n * Set the default editor configuration.\n *\n * This configuration is used when an editor is initialised without any configuration.\n *\n * @param {object} [options={}]\n */\nexport const configureDefaultEditor = (options = {}) => {\n defaultOptions = options;\n};\n"],"names":["instanceMap","Map","defaultOptions","importPluginList","async","pluginHandlers","Promise","all","pluginList","map","pluginPath","indexOf","resolve","pluginNames","pluginConfig","Array","isArray","filter","value","entries","elementId","getInstanceForElement","document","getElementById","element","instance","get","removed","remove","_ref","options","target","setupForTarget","lang","querySelector","tinyMCE","langData","language","fetch","M","cfg","wwwroot","langrev","then","response","json","addI18n","initialisePage","getPlugins","plugins","getEditorConfiguration","pluginValues","instanceConfig","Object","assign","base_url","baseUrl","content_css","css","convert_urls","a11y_advanced_options","quickbars_insert_toolbar","skin","promotion","branding","setup","editor","Options","register","getStandardConfig","menu","file","items","configure","forEach","getInitialPluginConfiguration","pendingPromise","Pending","keys","existingEditor","EditorManager","id","targetElm","closest","Error","destroy","init","set","on","_ref2","delete"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"editor.min.js","sources":["../src/editor.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 * TinyMCE Editor Manager.\n *\n * @module editor_tiny/editor\n * @copyright 2022 Andrew Lyons \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Pending from 'core/pending';\nimport {getDefaultConfiguration} from './defaults';\nimport {getTinyMCE, baseUrl} from './loader';\nimport * as Options from './options';\n\n/**\n * Storage for the TinyMCE instances on the page.\n * @type {Map}\n */\nconst instanceMap = new Map();\n\n/**\n * The default editor configuration.\n * @type {Object}\n */\nlet defaultOptions = {};\n\n/**\n * Require the modules for the named set of TinyMCE plugins.\n *\n * @param {string[]} pluginList The list of plugins\n * @return {Promise[]} A matching set of Promises relating to the requested plugins\n */\nconst importPluginList = async(pluginList) => {\n // Fetch all of the plugins from the list of plugins.\n // If a plugin contains a '/' then it is assumed to be a Moodle AMD module to import.\n const pluginHandlers = await Promise.all(pluginList.map(pluginPath => {\n if (pluginPath.indexOf('/') === -1) {\n // A standard TinyMCE Plugin.\n return Promise.resolve(pluginPath);\n }\n\n return import(pluginPath);\n }));\n\n // Normalise the plugin data to a list of plugin names.\n // Two formats are supported:\n // - a string; and\n // - an array whose first element is the plugin name, and the second element is the plugin configuration.\n const pluginNames = pluginHandlers.map((pluginConfig) => {\n if (typeof pluginConfig === 'string') {\n return pluginConfig;\n }\n if (Array.isArray(pluginConfig)) {\n return pluginConfig[0];\n }\n return null;\n }).filter((value) => value);\n\n // Fetch the list of pluginConfig handlers.\n const pluginConfig = pluginHandlers.map((pluginConfig) => {\n if (Array.isArray(pluginConfig)) {\n return pluginConfig[1];\n }\n return null;\n }).filter((value) => value);\n\n return {\n pluginNames,\n pluginConfig,\n };\n};\n\n/**\n * Fetch the language data for the specified language.\n *\n * @param {string} language The language identifier\n * @returns {object}\n */\nconst fetchLanguage = (language) => fetch(\n `${M.cfg.wwwroot}/lib/editor/tiny/lang.php/${M.cfg.langrev}/${language}`\n).then(response => response.json());\n\n/**\n * Get a list of all Editors in a Map, keyed by the DOM Node that the Editor is associated with.\n *\n * @returns {Map}\n */\nexport const getAllInstances = () => new Map(instanceMap.entries());\n\n/**\n * Get the TinyMCE instance for the specified Node ID.\n *\n * @param {string} elementId\n * @returns {TinyMCE|undefined}\n */\nexport const getInstanceForElementId = elementId => getInstanceForElement(document.getElementById(elementId));\n\n/*\n * Get the TinyMCE instance for the specified HTMLElement.\n *\n * @param {HTMLElement} element\n * @returns {TinyMCE|undefined}\n */\nexport const getInstanceForElement = element => {\n const instance = instanceMap.get(element);\n if (instance && instance.removed) {\n instanceMap.remove(element);\n return undefined;\n }\n return instance;\n};\n\n/**\n * Set up TinyMCE for the selector at the specified HTML Node id.\n *\n * @param {object} config The configuration required to setup the editor\n * @param {string} config.elementId The HTML Node ID\n * @param {Object} config.options The editor plugin configuration\n * @return {Promise} The TinyMCE instance\n */\nexport const setupForElementId = ({elementId, options}) => {\n const target = document.getElementById(elementId);\n return setupForTarget(target, options);\n};\n\n/**\n * Initialise the page with standard TinyMCE requirements.\n *\n * Currently this includes the language taken from the HTML lang property.\n */\nconst initialisePage = async() => {\n const lang = document.querySelector('html').lang;\n\n const [tinyMCE, langData] = await Promise.all([getTinyMCE(), fetchLanguage(lang)]);\n tinyMCE.addI18n(lang, langData);\n};\ninitialisePage();\n\n/**\n * Get the list of plugins to load for the specified configuration.\n *\n * If the specified configuration does not include a plugin configuration, then return the default configuration.\n *\n * @param {object} options\n * @param {array} [options.plugins=null] The plugin list\n * @returns {object}\n */\nconst getPlugins = ({plugins = null} = {}) => {\n if (plugins) {\n return plugins;\n }\n\n if (defaultOptions.plugins) {\n return defaultOptions.plugins;\n }\n\n return {};\n};\n\n/**\n * Get the standard configuration for the specified options.\n *\n * @param {Node} target\n * @param {tinyMCE} tinyMCE\n * @param {object} options\n * @param {Array} plugins\n * @returns {object}\n */\nconst getStandardConfig = (target, tinyMCE, options, plugins) => {\n const lang = document.querySelector('html').lang;\n\n return Object.assign({}, getDefaultConfiguration(), {\n // eslint-disable-next-line camelcase\n base_url: baseUrl,\n\n // Set the editor target.\n // https://www.tiny.cloud/docs/tinymce/6/editor-important-options/#target\n target,\n\n // Set the language.\n // https://www.tiny.cloud/docs/tinymce/6/ui-localization/#language\n // eslint-disable-next-line camelcase\n language: lang,\n\n // Load the editor stylesheet into the editor iframe.\n // https://www.tiny.cloud/docs/tinymce/6/add-css-options/\n // eslint-disable-next-line camelcase\n content_css: [\n options.css,\n ],\n\n // Do not convert URLs to relative URLs.\n // https://www.tiny.cloud/docs/tinymce/6/url-handling/#convert_urls\n // eslint-disable-next-line camelcase\n convert_urls: false,\n\n // Enabled 'advanced' a11y options.\n // This includes allowing role=\"presentation\" from the image uploader.\n // https://www.tiny.cloud/docs/tinymce/6/accessibility/\n // eslint-disable-next-line camelcase\n a11y_advanced_options: true,\n\n // Disable quickbars entirely.\n // The UI is not ideal and we'll wait for it to improve in future before we enable it in Moodle.\n // eslint-disable-next-line camelcase\n quickbars_insert_toolbar: '',\n\n // Disable some of the standard paragraph levels.\n // https://www.tiny.cloud/docs/tinymce/6/user-formatting-options/#block_formats\n // eslint-disable-next-line camelcase\n block_formats: 'Paragraph=p; Heading 3= h3; Heading 4= h4; Heading 5= h5; Heading 6= h6;',\n\n // The list of plugins to include in the instance.\n // https://www.tiny.cloud/docs/tinymce/6/editor-important-options/#plugins\n plugins: [\n ...plugins,\n ],\n\n // Skins\n skin: 'oxide',\n\n // Remove the \"Upgrade\" link for Tiny.\n // https://www.tiny.cloud/docs/tinymce/6/editor-premium-upgrade-promotion/\n promotion: false,\n\n // Allow the administrator to disable branding.\n // https://www.tiny.cloud/docs/tinymce/6/statusbar-configuration-options/#branding\n branding: options.branding,\n\n setup: (editor) => {\n Options.register(editor, options);\n },\n });\n};\n\n/**\n * Fetch the TinyMCE configuration for this editor instance.\n *\n * @param {HTMLElement} target\n * @param {TinyMCE} tinyMCE The TinyMCE API\n * @param {Object} options The editor plugin configuration\n * @param {object} pluginValues\n * @param {object} pluginValues.pluginConfig The list of plugin configuration\n * @param {object} pluginValues.pluginNames The list of plugins to load\n * @returns {object} The TinyMCE Configuration\n */\nconst getEditorConfiguration = (target, tinyMCE, options, pluginValues) => {\n const {\n pluginNames,\n pluginConfig,\n } = pluginValues;\n\n // Allow plugins to modify the configuration.\n // This seems a little strange, but we must double-process the config slightly.\n\n // First we fetch the standard configuration.\n const instanceConfig = getStandardConfig(target, tinyMCE, options, pluginNames);\n\n // Next we make any standard changes.\n // Here we remove the file menu, as it doesn't offer any useful functionality.\n // We only empty the items list so that a plugin may choose to add to it themselves later if they wish.\n if (instanceConfig.menu.file) {\n instanceConfig.menu.file.items = '';\n }\n\n // We disable the styles, backcolor, and forecolor plugins from the format menu.\n // These are not useful for Moodle and we don't want to encourage their use.\n if (instanceConfig.menu.format) {\n instanceConfig.menu.format.items = instanceConfig.menu.format.items\n // Remove forecolor and backcolor.\n .replace(/forecolor ?/, '')\n .replace(/backcolor ?/, '')\n\n // Remove fontfamily for now.\n .replace(/fontfamily ?/, '')\n\n // Remove fontsize for now.\n .replace(/fontsize ?/, '')\n\n // Remove styles - it just duplicates the format menu in a way which does not respect configuration\n .replace(/styles ?/, '')\n\n // Remove any duplicate separators.\n .replaceAll(/\\| *\\|/g, '|');\n }\n\n // Next we call the `configure` function for any plugin which defines it.\n // We pass the current instanceConfig in here, to allow them to make certain changes to the global configuration.\n // For example, to add themselves to any menu, toolbar, and so on.\n // Any plugin which wishes to have configuration options must register those options here.\n pluginConfig.filter((pluginConfig) => typeof pluginConfig.configure === 'function').forEach((pluginConfig) => {\n Object.assign(instanceConfig, pluginConfig.configure(instanceConfig, options));\n });\n\n // Next we convert the plugin configuration into a format that TinyMCE understands.\n Object.assign(instanceConfig, Options.getInitialPluginConfiguration(options));\n\n return instanceConfig;\n};\n\n/**\n * Set up TinyMCE for the HTML Element.\n *\n * @param {HTMLElement} target\n * @param {Object} [options={}] The editor plugin configuration\n * @return {Promise} The TinyMCE instance\n */\nexport const setupForTarget = async(target, options = {}) => {\n const instance = getInstanceForElement(target);\n if (instance) {\n return Promise.resolve(instance);\n }\n\n // Register a new pending promise to ensure that Behat waits for the editor setup to complete before continuing.\n const pendingPromise = new Pending('editor_tiny/editor:setupForTarget');\n\n // Get the list of plugins.\n const plugins = getPlugins(options);\n\n // Fetch the tinyMCE API, and instantiate the plugins.\n const [tinyMCE, pluginValues] = await Promise.all([\n getTinyMCE(),\n importPluginList(Object.keys(plugins)),\n ]);\n\n // TinyMCE uses the element ID as a map key internally, even if the target has changed.\n // In the case where we have an editor in a modal form which has been detached from the DOM, but the editor not removed,\n // we need to manually destroy the editor.\n // We could theoretically do this with a Mutation Observer, but in some cases the Node may be moved,\n // or added back elsewhere in the DOM.\n const existingEditor = tinyMCE.EditorManager.get(target.id);\n if (existingEditor) {\n if (existingEditor.targetElm.closest('body')) {\n if (existingEditor.targetElm === target) {\n pendingPromise.resolve();\n return Promise.resolve(existingEditor);\n } else {\n pendingPromise.resolve();\n throw new Error('TinyMCE instance already exists for different target with same ID');\n }\n } else {\n existingEditor.destroy();\n }\n }\n\n // Get the editor configuration for this editor.\n const instanceConfig = getEditorConfiguration(target, tinyMCE, options, pluginValues);\n\n // Initialise the editor instance for the given configuration.\n // At this point any plugin which has configuration options registered will have them applied for this instance.\n const [editor] = await tinyMCE.init(instanceConfig);\n\n // Store the editor instance in the instanceMap and register a listener on removal to remove it from the map.\n instanceMap.set(target, editor);\n editor.on('remove', ({target}) => {\n // Handle removal of the editor from the map on destruction.\n instanceMap.delete(target.targetElm);\n });\n\n pendingPromise.resolve();\n return editor;\n};\n\n/**\n * Set the default editor configuration.\n *\n * This configuration is used when an editor is initialised without any configuration.\n *\n * @param {object} [options={}]\n */\nexport const configureDefaultEditor = (options = {}) => {\n defaultOptions = options;\n};\n"],"names":["instanceMap","Map","defaultOptions","importPluginList","async","pluginHandlers","Promise","all","pluginList","map","pluginPath","indexOf","resolve","pluginNames","pluginConfig","Array","isArray","filter","value","entries","elementId","getInstanceForElement","document","getElementById","element","instance","get","removed","remove","_ref","options","target","setupForTarget","lang","querySelector","tinyMCE","langData","language","fetch","M","cfg","wwwroot","langrev","then","response","json","addI18n","initialisePage","getPlugins","plugins","getEditorConfiguration","pluginValues","instanceConfig","Object","assign","base_url","baseUrl","content_css","css","convert_urls","a11y_advanced_options","quickbars_insert_toolbar","block_formats","skin","promotion","branding","setup","editor","Options","register","getStandardConfig","menu","file","items","format","replace","replaceAll","configure","forEach","getInitialPluginConfiguration","pendingPromise","Pending","keys","existingEditor","EditorManager","id","targetElm","closest","Error","destroy","init","set","on","_ref2","delete"],"mappings":"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"} \ No newline at end of file diff --git a/lib/editor/tiny/amd/src/defaults.js b/lib/editor/tiny/amd/src/defaults.js index 8b1d3b93bf821..627bde4b461ea 100644 --- a/lib/editor/tiny/amd/src/defaults.js +++ b/lib/editor/tiny/amd/src/defaults.js @@ -170,10 +170,9 @@ export const getDefaultConfiguration = () => ({ // TODO: Move this configuration to a passed-in option. menu: getDefaultMenu(), - // TODO Add mobile configuration. // Mobile configuration. + // At this time we will use the default TinyMCE mobile configuration. // https://www.tiny.cloud/docs/tinymce/6/tinymce-for-mobile/ - // This will include mobile-specific toolbar, and menu options. // Skins skin: 'oxide', diff --git a/lib/editor/tiny/amd/src/editor.js b/lib/editor/tiny/amd/src/editor.js index c8d320b9b212b..cd71277b28c79 100644 --- a/lib/editor/tiny/amd/src/editor.js +++ b/lib/editor/tiny/amd/src/editor.js @@ -219,17 +219,17 @@ const getStandardConfig = (target, tinyMCE, options, plugins) => { // eslint-disable-next-line camelcase quickbars_insert_toolbar: '', + // Disable some of the standard paragraph levels. + // https://www.tiny.cloud/docs/tinymce/6/user-formatting-options/#block_formats + // eslint-disable-next-line camelcase + block_formats: 'Paragraph=p; Heading 3= h3; Heading 4= h4; Heading 5= h5; Heading 6= h6;', + // The list of plugins to include in the instance. // https://www.tiny.cloud/docs/tinymce/6/editor-important-options/#plugins plugins: [ ...plugins, ], - // TODO Add mobile configuration. - // Mobile configuration. - // https://www.tiny.cloud/docs/tinymce/6/tinymce-for-mobile/ - // This will include mobile-specific toolbar, and menu options. - // Skins skin: 'oxide', @@ -277,6 +277,27 @@ const getEditorConfiguration = (target, tinyMCE, options, pluginValues) => { instanceConfig.menu.file.items = ''; } + // We disable the styles, backcolor, and forecolor plugins from the format menu. + // These are not useful for Moodle and we don't want to encourage their use. + if (instanceConfig.menu.format) { + instanceConfig.menu.format.items = instanceConfig.menu.format.items + // Remove forecolor and backcolor. + .replace(/forecolor ?/, '') + .replace(/backcolor ?/, '') + + // Remove fontfamily for now. + .replace(/fontfamily ?/, '') + + // Remove fontsize for now. + .replace(/fontsize ?/, '') + + // Remove styles - it just duplicates the format menu in a way which does not respect configuration + .replace(/styles ?/, '') + + // Remove any duplicate separators. + .replaceAll(/\| *\|/g, '|'); + } + // Next we call the `configure` function for any plugin which defines it. // We pass the current instanceConfig in here, to allow them to make certain changes to the global configuration. // For example, to add themselves to any menu, toolbar, and so on.