Merge branch 'MDL-75966-master' of https://github.com/andrewnicols/moodle
This commit is contained in:
@@ -331,14 +331,14 @@ Feature: Perform basic calendar functionality
|
||||
And I click on "My own user event" "link"
|
||||
Then I should see "User event"
|
||||
And I should not see "Group 1"
|
||||
And I click on "Edit" "button"
|
||||
And I click on "Edit" "button" in the "My own user event" "dialogue"
|
||||
And I set the following fields to these values:
|
||||
| Event title | Site event |
|
||||
| Type of event | site |
|
||||
And I press "Save"
|
||||
And I click on "Site event" "link"
|
||||
And I should see "Site event"
|
||||
And I click on "Edit" "button"
|
||||
And I click on "Edit" "button" in the "Site event" "dialogue"
|
||||
And I set the following fields to these values:
|
||||
| Event title | Course 1 event |
|
||||
| Type of event | course |
|
||||
@@ -347,7 +347,7 @@ Feature: Perform basic calendar functionality
|
||||
And I press "Save"
|
||||
And I click on "Course 1 event" "link"
|
||||
And I should see "Course event"
|
||||
And I click on "Edit" "button"
|
||||
And I click on "Edit" "button" in the "Course 1 event" "dialogue"
|
||||
And I set the following fields to these values:
|
||||
| Event title | Category event |
|
||||
| Type of event | category |
|
||||
|
||||
@@ -1861,7 +1861,7 @@ class core_plugin_manager {
|
||||
),
|
||||
|
||||
'editor' => array(
|
||||
'atto', 'textarea', 'tinymce'
|
||||
'atto', 'textarea', 'tiny', 'tinymce'
|
||||
),
|
||||
|
||||
'enrol' => array(
|
||||
@@ -2028,6 +2028,15 @@ class core_plugin_manager {
|
||||
'objectives'
|
||||
),
|
||||
|
||||
'tiny' => [
|
||||
'accessibilitychecker',
|
||||
'autosave',
|
||||
'equation',
|
||||
'h5p',
|
||||
'media',
|
||||
'recordrtc',
|
||||
],
|
||||
|
||||
'tinymce' => array(
|
||||
'ctrlhelp', 'managefiles', 'moodleemoticon', 'moodleimage',
|
||||
'moodlemedia', 'moodlenolink', 'pdw', 'spellchecker', 'wrap'
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
define("editor_tiny/defaults",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.getDefaultToolbar=_exports.getDefaultQuickbarsSelectionToolbar=_exports.getDefaultQuickbarsInsertToolbar=_exports.getDefaultQuickbarsImageToolbar=_exports.getDefaultMenu=_exports.getDefaultConfiguration=void 0;
|
||||
/**
|
||||
* TinyMCE Editor Upstream defaults.
|
||||
*
|
||||
* @module editor_tiny/editor
|
||||
* @copyright 2022 Andrew Lyons <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
const getDefaultMenu=()=>({file:{title:"File",items:"newdocument restoredraft | preview | export print | deleteallconversations"},edit:{title:"Edit",items:"undo redo | cut copy paste pastetext | selectall | searchreplace"},view:{title:"View",items:"code | visualaid visualchars visualblocks | spellchecker | preview fullscreen | showcomments"},insert:{title:"Insert",items:"image link media addcomment pageembed template codesample inserttable | charmap emoticons hr | pagebreak nonbreaking anchor tableofcontents | insertdatetime"},format:{title:"Format",items:"bold italic underline strikethrough superscript subscript codeformat | styles blocks fontfamily fontsize align lineheight | forecolor backcolor | language | removeformat"},tools:{title:"Tools",items:"spellchecker spellcheckerlanguage | a11ycheck code wordcount"},table:{title:"Table",items:"inserttable | cell row column | advtablesort | tableprops deletetable"},help:{title:"Help",items:"help"}});_exports.getDefaultMenu=getDefaultMenu;const getDefaultToolbar=()=>[{name:"history",items:["undo","redo"]},{name:"formatting",items:["bold","italic"]},{name:"alignment",items:["alignleft","aligncenter","alignright","alignjustify"]},{name:"indentation",items:["outdent","indent"]},{name:"lists",items:["bullist","numlist"]},{name:"comments",items:["addcomment"]}];_exports.getDefaultToolbar=getDefaultToolbar;const getDefaultQuickbarsSelectionToolbar=()=>"bold italic | quicklink h2 h3 blockquote";_exports.getDefaultQuickbarsSelectionToolbar=getDefaultQuickbarsSelectionToolbar;const getDefaultQuickbarsInsertToolbar=()=>"quickimage quicktable";_exports.getDefaultQuickbarsInsertToolbar=getDefaultQuickbarsInsertToolbar;const getDefaultQuickbarsImageToolbar=()=>"alignleft aligncenter alignright";_exports.getDefaultQuickbarsImageToolbar=getDefaultQuickbarsImageToolbar;_exports.getDefaultConfiguration=()=>({toolbar_mode:"sliding",toolbar:[{name:"history",items:["undo","redo"]},{name:"formatting",items:["bold","italic"]},{name:"alignment",items:["alignleft","aligncenter","alignright","alignjustify"]},{name:"indentation",items:["outdent","indent"]},{name:"lists",items:["bullist","numlist"]},{name:"comments",items:["addcomment"]}],quickbars_selection_toolbar:"bold italic | quicklink h2 h3 blockquote",quickbars_insert_toolbar:"quickimage quicktable",quickbars_image_toolbar:"alignleft aligncenter alignright",menu:{file:{title:"File",items:"newdocument restoredraft | preview | export print | deleteallconversations"},edit:{title:"Edit",items:"undo redo | cut copy paste pastetext | selectall | searchreplace"},view:{title:"View",items:"code | visualaid visualchars visualblocks | spellchecker | preview fullscreen | showcomments"},insert:{title:"Insert",items:"image link media addcomment pageembed template codesample inserttable | charmap emoticons hr | pagebreak nonbreaking anchor tableofcontents | insertdatetime"},format:{title:"Format",items:"bold italic underline strikethrough superscript subscript codeformat | styles blocks fontfamily fontsize align lineheight | forecolor backcolor | language | removeformat"},tools:{title:"Tools",items:"spellchecker spellcheckerlanguage | a11ycheck code wordcount"},table:{title:"Table",items:"inserttable | cell row column | advtablesort | tableprops deletetable"},help:{title:"Help",items:"help"}},skin:"oxide"})}));
|
||||
|
||||
//# sourceMappingURL=defaults.min.js.map
|
||||
File diff suppressed because one or more lines are too long
+3
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+11
@@ -0,0 +1,11 @@
|
||||
define("editor_tiny/loader",["exports","core/config"],(function(_exports,Config){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)}
|
||||
/**
|
||||
* Tiny Loader for Moodle
|
||||
*
|
||||
* @module editor_tiny/loader
|
||||
* @copyright 2022 Andrew Lyons <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
let tinyMCEPromise;Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.getTinyMCE=_exports.baseUrl=void 0,Config=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}(Config);const baseUrl="".concat(Config.wwwroot,"/lib/editor/tiny/loader.php/").concat(M.cfg.jsrev);_exports.baseUrl=baseUrl;_exports.getTinyMCE=()=>tinyMCEPromise||(tinyMCEPromise=new Promise(((resolve,reject)=>{const head=document.querySelector("head");let script=head.querySelector('script[data-tinymce="tinymce"]');script&&resolve(window.tinyMCE),script=document.createElement("script"),script.dataset.tinymce="tinymce",script.src="".concat(baseUrl,"/tinymce.js"),script.async=!0,script.addEventListener("load",(()=>{resolve(window.tinyMCE)}),!1),script.addEventListener("error",(err=>{reject(err)}),!1),head.append(script)})),tinyMCEPromise)}));
|
||||
|
||||
//# sourceMappingURL=loader.min.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"loader.min.js","sources":["../src/loader.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Tiny Loader for Moodle\n *\n * @module editor_tiny/loader\n * @copyright 2022 Andrew Lyons <[email protected]>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nlet tinyMCEPromise;\n\nimport * as Config from 'core/config';\n\nexport const baseUrl = `${Config.wwwroot}/lib/editor/tiny/loader.php/${M.cfg.jsrev}`;\n\n/**\n * Get the TinyMCE API Object.\n *\n * @returns {Promise<TinyMCE>} The TinyMCE API Object\n */\nexport const getTinyMCE = () => {\n if (tinyMCEPromise) {\n return tinyMCEPromise;\n }\n\n tinyMCEPromise = new Promise((resolve, reject) => {\n const head = document.querySelector('head');\n let script = head.querySelector('script[data-tinymce=\"tinymce\"]');\n if (script) {\n resolve(window.tinyMCE);\n }\n\n script = document.createElement('script');\n script.dataset.tinymce = 'tinymce';\n script.src = `${baseUrl}/tinymce.js`;\n script.async = true;\n\n script.addEventListener('load', () => {\n resolve(window.tinyMCE);\n }, false);\n\n script.addEventListener('error', (err) => {\n reject(err);\n }, false);\n\n head.append(script);\n });\n\n return tinyMCEPromise;\n};\n"],"names":["tinyMCEPromise","baseUrl","Config","wwwroot","M","cfg","jsrev","Promise","resolve","reject","head","document","querySelector","script","window","tinyMCE","createElement","dataset","tinymce","src","async","addEventListener","err","append"],"mappings":";;;;;;;;IAuBIA,qxBAISC,kBAAaC,OAAOC,+CAAsCC,EAAEC,IAAIC,oDAOnD,IAClBN,iBAIJA,eAAiB,IAAIO,SAAQ,CAACC,QAASC,gBAC7BC,KAAOC,SAASC,cAAc,YAChCC,OAASH,KAAKE,cAAc,kCAC5BC,QACAL,QAAQM,OAAOC,SAGnBF,OAASF,SAASK,cAAc,UAChCH,OAAOI,QAAQC,QAAU,UACzBL,OAAOM,cAASlB,uBAChBY,OAAOO,OAAQ,EAEfP,OAAOQ,iBAAiB,QAAQ,KAC5Bb,QAAQM,OAAOC,YAChB,GAEHF,OAAOQ,iBAAiB,SAAUC,MAC9Bb,OAAOa,QACR,GAEHZ,KAAKa,OAAOV,WAGTb"}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
define("editor_tiny/options",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.register=_exports.getPluginOptionName=_exports.getMoodleLang=_exports.getInitialPluginConfiguration=_exports.getFilepickers=_exports.getFilePicker=_exports.getDraftItemId=_exports.getCurrentLanguage=_exports.getContextId=void 0;_exports.register=(editor,options)=>{const registerOption=editor.options.register,setOption=editor.options.set;registerOption("moodle:contextid",{processor:"number",default:0}),setOption("moodle:contextid",options.context),registerOption("moodle:filepickers",{processor:"object",default:{}}),setOption("moodle:filepickers",options.filepicker),registerOption("moodle:draftitemid",{processor:"number",default:0}),setOption("moodle:draftitemid",options.draftitemid),registerOption("moodle:currentLanguage",{processor:"string",default:"en"}),setOption("moodle:currentLanguage",options.currentLanguage),registerOption("moodle:language",{processor:"object",default:{}}),setOption("moodle:language",options.language)};_exports.getContextId=editor=>editor.options.get("moodle:contextid");_exports.getDraftItemId=editor=>editor.options.get("moodle:draftitemid");const getFilepickers=editor=>editor.options.get("moodle:filepickers");_exports.getFilepickers=getFilepickers;_exports.getFilePicker=(editor,type)=>getFilepickers(editor)[type];_exports.getMoodleLang=editor=>editor.options.get("moodle:language");_exports.getCurrentLanguage=editor=>editor.options.get("moodle:currentLanguage");_exports.getInitialPluginConfiguration=options=>{const config={};return Object.entries(options.plugins).forEach((_ref=>{var _pluginConfig$config;let[pluginName,pluginConfig]=_ref;Object.entries(null!==(_pluginConfig$config=pluginConfig.config)&&void 0!==_pluginConfig$config?_pluginConfig$config:{}).forEach((_ref2=>{let[optionName,value]=_ref2;config[getPluginOptionName(pluginName,optionName)]=value}))})),config};const getPluginOptionName=(pluginName,optionName)=>"".concat(pluginName,":").concat(optionName);_exports.getPluginOptionName=getPluginOptionName}));
|
||||
|
||||
//# sourceMappingURL=options.min.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"options.min.js","sources":["../src/options.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Option helper for TinyMCE Editor Manager.\n *\n * @module editor_tiny/options\n * @copyright 2022 Andrew Lyons <[email protected]>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nconst optionContextId = 'moodle:contextid';\nconst optionDraftItemId = 'moodle:draftitemid';\nconst filePickers = 'moodle:filepickers';\nconst optionsMoodleLang = 'moodle:language';\nconst currentLanguage = 'moodle:currentLanguage';\n\nexport const register = (editor, options) => {\n const registerOption = editor.options.register;\n const setOption = editor.options.set;\n\n registerOption(optionContextId, {\n processor: 'number',\n \"default\": 0,\n });\n setOption(optionContextId, options.context);\n\n registerOption(filePickers, {\n processor: 'object',\n \"default\": {},\n });\n setOption(filePickers, options.filepicker);\n\n registerOption(optionDraftItemId, {\n processor: 'number',\n \"default\": 0,\n });\n setOption(optionDraftItemId, options.draftitemid);\n\n registerOption(currentLanguage, {\n processor: 'string',\n \"default\": 'en',\n });\n setOption(currentLanguage, options.currentLanguage);\n\n // This is primarily used by the media plugin, but it may be re-used elsewhere so is included here as it is large.\n registerOption(optionsMoodleLang, {\n processor: 'object',\n \"default\": {},\n });\n setOption(optionsMoodleLang, options.language);\n};\n\nexport const getContextId = (editor) => editor.options.get(optionContextId);\nexport const getDraftItemId = (editor) => editor.options.get(optionDraftItemId);\nexport const getFilepickers = (editor) => editor.options.get(filePickers);\nexport const getFilePicker = (editor, type) => getFilepickers(editor)[type];\nexport const getMoodleLang = (editor) => editor.options.get(optionsMoodleLang);\nexport const getCurrentLanguage = (editor) => editor.options.get(currentLanguage);\n\n/**\n * Get a set of namespaced options for all defined plugins.\n *\n * @param {object} options\n * @returns {object}\n */\nexport const getInitialPluginConfiguration = (options) => {\n const config = {};\n\n Object.entries(options.plugins).forEach(([pluginName, pluginConfig]) => {\n const values = Object.entries(pluginConfig.config ?? {});\n values.forEach(([optionName, value]) => {\n config[getPluginOptionName(pluginName, optionName)] = value;\n });\n });\n\n return config;\n};\n\n/**\n * Get the namespaced option name for a plugin.\n *\n * @param {string} pluginName\n * @param {string} optionName\n * @returns {string}\n */\nexport const getPluginOptionName = (pluginName, optionName) => `${pluginName}:${optionName}`;\n"],"names":["editor","options","registerOption","register","setOption","set","processor","context","filepicker","draftitemid","currentLanguage","language","get","getFilepickers","type","config","Object","entries","plugins","forEach","_ref","pluginName","pluginConfig","_ref2","optionName","value","getPluginOptionName"],"mappings":"oXA6BwB,CAACA,OAAQC,iBACvBC,eAAiBF,OAAOC,QAAQE,SAChCC,UAAYJ,OAAOC,QAAQI,IAEjCH,eAVoB,mBAUY,CAC5BI,UAAW,iBACA,IAEfF,UAdoB,mBAcOH,QAAQM,SAEnCL,eAdgB,qBAcY,CACxBI,UAAW,iBACA,KAEfF,UAlBgB,qBAkBOH,QAAQO,YAE/BN,eArBsB,qBAqBY,CAC9BI,UAAW,iBACA,IAEfF,UAzBsB,qBAyBOH,QAAQQ,aAErCP,eAxBoB,yBAwBY,CAC5BI,UAAW,iBACA,OAEfF,UA5BoB,yBA4BOH,QAAQS,iBAGnCR,eAhCsB,kBAgCY,CAC9BI,UAAW,iBACA,KAEfF,UApCsB,kBAoCOH,QAAQU,iCAGZX,QAAWA,OAAOC,QAAQW,IA1C/B,4CA2COZ,QAAWA,OAAOC,QAAQW,IA1C/B,4BA2CbC,eAAkBb,QAAWA,OAAOC,QAAQW,IA1CrC,oFA2CS,CAACZ,OAAQc,OAASD,eAAeb,QAAQc,6BACxCd,QAAWA,OAAOC,QAAQW,IA3C9B,+CA4CSZ,QAAWA,OAAOC,QAAQW,IA3CrC,iEAmDsBX,gBACpCc,OAAS,UAEfC,OAAOC,QAAQhB,QAAQiB,SAASC,SAAQC,oCAAEC,WAAYC,mBACnCN,OAAOC,qCAAQK,aAAaP,4DAAU,IAC9CI,SAAQI,YAAEC,WAAYC,aACzBV,OAAOW,oBAAoBL,WAAYG,aAAeC,YAIvDV,cAUEW,oBAAsB,CAACL,WAAYG,uBAAkBH,uBAAcG"}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
define("editor_tiny/uploader",["exports","core_form/events","editor_tiny/options"],(function(_exports,_events,_options){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0;return _exports.default=(editor,filePickerType,blob,fileName,progress)=>new Promise(((resolve,reject)=>{var _options$savepath;(0,_events.notifyUploadStarted)(editor.targetElm.id);const xhr=new XMLHttpRequest;xhr.upload.addEventListener("progress",(e=>{progress(e.loaded/e.total*100)})),xhr.addEventListener("load",(()=>{if(403===xhr.status)return void reject({message:"HTTP error: ".concat(xhr.status),remove:!0});if(xhr.status<200||xhr.status>=300)return void reject("HTTP Error: ".concat(xhr.status));const response=JSON.parse(xhr.responseText);if(!response)return void reject("Invalid JSON: ".concat(xhr.responseText));let location;(0,_events.notifyUploadCompleted)(editor.targetElm.id),response.url?location=response.url:response.event&&"fileexists"===response.event&&response.newfile&&(location=response.newfile.url),location&&"string"==typeof location?resolve(location):reject("Unable to handle file result: ".concat(xhr.responseText))})),xhr.addEventListener("error",(()=>{reject({message:"Upload failed due to an XHR transport error. Code: ".concat(xhr.status),remove:!0})}));const formData=new FormData,options=(0,_options.getFilePicker)(editor,filePickerType);formData.append("repo_upload_file",blob,fileName),formData.append("itemid",options.itemid),Object.values(options.repositories).some((repository=>"upload"===repository.type&&(formData.append("repo_id",repository.id),!0))),formData.append("env",options.env),formData.append("sesskey",M.cfg.sesskey),formData.append("client_id",options.client_id),formData.append("savepath",null!==(_options$savepath=options.savepath)&&void 0!==_options$savepath?_options$savepath:"/"),formData.append("ctx_id",options.context.id),xhr.open("POST","".concat(M.cfg.wwwroot,"/repository/repository_ajax.php?action=upload"),!0),xhr.send(formData)})),_exports.default}));
|
||||
|
||||
//# sourceMappingURL=uploader.min.js.map
|
||||
File diff suppressed because one or more lines are too long
+3
@@ -0,0 +1,3 @@
|
||||
define("editor_tiny/utils",["exports","core/templates","./options","core/str"],(function(_exports,_templates,_options,_str){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.getPluginMetadata=_exports.getImagePath=_exports.getDocumentationLink=_exports.getButtonImage=_exports.ensureEditorIsValid=_exports.displayFilepicker=_exports.addToolbarSection=_exports.addToolbarButtons=_exports.addToolbarButton=_exports.addQuickbarsToolbarItem=_exports.addMenubarItem=_exports.addContextmenuItem=void 0;const getImagePath=function(identifier){let component=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"editor_tiny";return Promise.resolve(M.util.image_url(identifier,component))};_exports.getImagePath=getImagePath;_exports.getButtonImage=async function(identifier){let component=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"editor_tiny";return(0,_templates.renderForPromise)("editor_tiny/toolbar_button",{image:await getImagePath(identifier,component)})};_exports.displayFilepicker=(editor,filetype)=>new Promise(((resolve,reject)=>{const configuration=(0,_options.getFilePicker)(editor,filetype);if(configuration){const options={...configuration,formcallback:resolve};M.core_filepicker.show(Y,options)}else reject("Unknown filetype ".concat(filetype))}));_exports.addToolbarButton=(toolbar,section,button)=>{if(!toolbar)return[{name:section,items:[button]}];return JSON.parse(JSON.stringify(toolbar)).map((item=>(item.name===section&&item.items.push(button),item)))};_exports.addToolbarButtons=(toolbar,section,buttons)=>{if(!toolbar)return[{name:section,items:buttons}];return JSON.parse(JSON.stringify(toolbar)).map((item=>(item.name===section&&buttons.forEach((button=>item.items.push(button))),item)))};_exports.addToolbarSection=function(toolbar,name,relativeTo){let append=!(arguments.length>3&&void 0!==arguments[3])||arguments[3];const newSection={name:name,items:[]},sectionInserted=toolbar.some(((section,index)=>section.name===relativeTo&&(append?toolbar.splice(index+1,0,newSection):toolbar.splice(index,0,newSection),!0)));return sectionInserted||(append?toolbar.push(newSection):toolbar.unshift(newSection)),toolbar};_exports.addMenubarItem=(menubar,section,menuitem)=>{if(!menubar){({})[section]={title:section,items:menuitem}}const mutatedMenubar=JSON.parse(JSON.stringify(menubar));return Array.from(Object.entries(mutatedMenubar)).forEach((_ref=>{let[name,menu]=_ref;name===section&&(menu.items="".concat(menu.items," ").concat(menuitem))})),mutatedMenubar};_exports.addContextmenuItem=function(contextmenu){const contextmenuItems=(null!=contextmenu?contextmenu:"").split(" ");for(var _len=arguments.length,menuitems=new Array(_len>1?_len-1:0),_key=1;_key<_len;_key++)menuitems[_key-1]=arguments[_key];return contextmenuItems.concat(menuitems).filter((item=>""!==item)).join(" ")};_exports.addQuickbarsToolbarItem=function(toolbar){return toolbar};const getDocumentationLink=pluginName=>"https://docs.moodle.org/en/editor_tiny/".concat(pluginName);_exports.getDocumentationLink=getDocumentationLink;_exports.getPluginMetadata=async function(component,pluginName){let url=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;const name=await(0,_str.get_string)("helplinktext",component);return{getMetadata:()=>({name:name,url:null!=url?url:getDocumentationLink(pluginName)})}};_exports.ensureEditorIsValid=editor=>{var _editor$targetElm;return null!=editor&&null!==(_editor$targetElm=editor.targetElm)&&void 0!==_editor$targetElm&&_editor$targetElm.closest("body")?editor:(editor.destroy(),null)}}));
|
||||
|
||||
//# sourceMappingURL=utils.min.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,175 @@
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/* eslint-disable max-len, */
|
||||
|
||||
/**
|
||||
* TinyMCE Editor Upstream defaults.
|
||||
*
|
||||
* @module editor_tiny/editor
|
||||
* @copyright 2022 Andrew Lyons <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
/**
|
||||
* The upstream defaults for the TinyMCE Menu.
|
||||
*
|
||||
* This value is defined in the TinyMCE documentation, but not exported anywhere useful.
|
||||
* https://www.tiny.cloud/docs/tinymce/6/menus-configuration-options/#menu
|
||||
*
|
||||
* @returns {Object}
|
||||
*/
|
||||
export const getDefaultMenu = () => {
|
||||
return {
|
||||
file: {title: 'File', items: 'newdocument restoredraft | preview | export print | deleteallconversations'},
|
||||
edit: {title: 'Edit', items: 'undo redo | cut copy paste pastetext | selectall | searchreplace'},
|
||||
view: {title: 'View', items: 'code | visualaid visualchars visualblocks | spellchecker | preview fullscreen | showcomments'},
|
||||
insert: {title: 'Insert', items: 'image link media addcomment pageembed template codesample inserttable | charmap emoticons hr | pagebreak nonbreaking anchor tableofcontents | insertdatetime'},
|
||||
format: {title: 'Format', items: 'bold italic underline strikethrough superscript subscript codeformat | styles blocks fontfamily fontsize align lineheight | forecolor backcolor | language | removeformat'},
|
||||
tools: {title: 'Tools', items: 'spellchecker spellcheckerlanguage | a11ycheck code wordcount'},
|
||||
table: {title: 'Table', items: 'inserttable | cell row column | advtablesort | tableprops deletetable'},
|
||||
help: {title: 'Help', items: 'help'}
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* The default toolbar configuration to use.
|
||||
*
|
||||
* This is based upon the default value used if no toolbar is specified.
|
||||
*
|
||||
* https://www.tiny.cloud/docs/tinymce/6/menus-configuration-options/#menu
|
||||
*
|
||||
* @returns {Object}
|
||||
*/
|
||||
export const getDefaultToolbar = () => {
|
||||
return [
|
||||
{
|
||||
name: 'history',
|
||||
items: [
|
||||
'undo',
|
||||
'redo',
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'formatting',
|
||||
items: [
|
||||
'bold',
|
||||
'italic',
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'alignment',
|
||||
items: [
|
||||
'alignleft',
|
||||
'aligncenter',
|
||||
'alignright',
|
||||
'alignjustify',
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'indentation',
|
||||
items: [
|
||||
'outdent',
|
||||
'indent',
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'lists',
|
||||
items: [
|
||||
'bullist',
|
||||
'numlist',
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'comments',
|
||||
items: ['addcomment'],
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
/**
|
||||
* The default quickbars_insert_toolbar configuration to use.
|
||||
*
|
||||
* This is based upon the default value used if no toolbar is specified.
|
||||
*
|
||||
* https://www.tiny.cloud/docs/tinymce/6/quickbars/#quickbars_selection_toolbar
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
export const getDefaultQuickbarsSelectionToolbar = () => 'bold italic | quicklink h2 h3 blockquote';
|
||||
|
||||
/**
|
||||
* The default quickbars_insert_toolbar configuration to use.
|
||||
*
|
||||
* This is based upon the default value used if no toolbar is specified.
|
||||
*
|
||||
* https://www.tiny.cloud/docs/tinymce/6/quickbars/#quickbars_insert_toolbar
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
export const getDefaultQuickbarsInsertToolbar = () => 'quickimage quicktable';
|
||||
|
||||
/**
|
||||
* The default quickbars_insert_toolbar configuration to use.
|
||||
*
|
||||
* This is based upon the default value used if no toolbar is specified.
|
||||
*
|
||||
* https://www.tiny.cloud/docs/tinymce/6/quickbars/#quickbars_image_toolbar
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
export const getDefaultQuickbarsImageToolbar = () => 'alignleft aligncenter alignright';
|
||||
|
||||
/**
|
||||
* Get the default configuration provided by TinyMCE.
|
||||
*
|
||||
* @returns {object}
|
||||
*/
|
||||
export const getDefaultConfiguration = () => ({
|
||||
// Toolbar configuration.
|
||||
// https://www.tiny.cloud/docs/tinymce/6/toolbar-configuration-options/
|
||||
// TODO: Move this configuration to a passed-in option.
|
||||
// eslint-disable-next-line camelcase
|
||||
toolbar_mode: 'sliding',
|
||||
toolbar: getDefaultToolbar(),
|
||||
|
||||
// Quickbars Selection Toolbar configuration.
|
||||
// https://www.tiny.cloud/docs/tinymce/6/quickbars/#quickbars_selection_toolbar
|
||||
// eslint-disable-next-line camelcase
|
||||
quickbars_selection_toolbar: getDefaultQuickbarsSelectionToolbar(),
|
||||
|
||||
// Quickbars Select Toolbar configuration.
|
||||
// https://www.tiny.cloud/docs/tinymce/6/quickbars/#quickbars_insert_toolbar
|
||||
// eslint-disable-next-line camelcase
|
||||
quickbars_insert_toolbar: getDefaultQuickbarsInsertToolbar(),
|
||||
|
||||
// Quickbars Image Toolbar configuration.
|
||||
// https://www.tiny.cloud/docs/tinymce/6/quickbars/#quickbars_image_toolbar
|
||||
// eslint-disable-next-line camelcase
|
||||
quickbars_image_toolbar: getDefaultQuickbarsImageToolbar(),
|
||||
|
||||
|
||||
// Menu configuration.
|
||||
// https://www.tiny.cloud/docs/tinymce/6/menus-configuration-options/
|
||||
// TODO: Move this configuration to a passed-in option.
|
||||
menu: getDefaultMenu(),
|
||||
|
||||
// Mobile configuration.
|
||||
// At this time we will use the default TinyMCE mobile configuration.
|
||||
// https://www.tiny.cloud/docs/tinymce/6/tinymce-for-mobile/
|
||||
|
||||
// Skins
|
||||
skin: 'oxide',
|
||||
});
|
||||
@@ -0,0 +1,394 @@
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* TinyMCE Editor Manager.
|
||||
*
|
||||
* @module editor_tiny/editor
|
||||
* @copyright 2022 Andrew Lyons <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
import Pending from 'core/pending';
|
||||
import {getDefaultConfiguration} from './defaults';
|
||||
import {getTinyMCE, baseUrl} from './loader';
|
||||
import * as Options from './options';
|
||||
import {addToolbarButton, addToolbarSection} from './utils';
|
||||
|
||||
/**
|
||||
* Storage for the TinyMCE instances on the page.
|
||||
* @type {Map}
|
||||
*/
|
||||
const instanceMap = new Map();
|
||||
|
||||
/**
|
||||
* The default editor configuration.
|
||||
* @type {Object}
|
||||
*/
|
||||
let defaultOptions = {};
|
||||
|
||||
/**
|
||||
* Require the modules for the named set of TinyMCE plugins.
|
||||
*
|
||||
* @param {string[]} pluginList The list of plugins
|
||||
* @return {Promise[]} A matching set of Promises relating to the requested plugins
|
||||
*/
|
||||
const importPluginList = async(pluginList) => {
|
||||
// Fetch all of the plugins from the list of plugins.
|
||||
// If a plugin contains a '/' then it is assumed to be a Moodle AMD module to import.
|
||||
const pluginHandlers = await Promise.all(pluginList.map(pluginPath => {
|
||||
if (pluginPath.indexOf('/') === -1) {
|
||||
// A standard TinyMCE Plugin.
|
||||
return Promise.resolve(pluginPath);
|
||||
}
|
||||
|
||||
return import(pluginPath);
|
||||
}));
|
||||
|
||||
// Normalise the plugin data to a list of plugin names.
|
||||
// Two formats are supported:
|
||||
// - a string; and
|
||||
// - an array whose first element is the plugin name, and the second element is the plugin configuration.
|
||||
const pluginNames = pluginHandlers.map((pluginConfig) => {
|
||||
if (typeof pluginConfig === 'string') {
|
||||
return pluginConfig;
|
||||
}
|
||||
if (Array.isArray(pluginConfig)) {
|
||||
return pluginConfig[0];
|
||||
}
|
||||
return null;
|
||||
}).filter((value) => value);
|
||||
|
||||
// Fetch the list of pluginConfig handlers.
|
||||
const pluginConfig = pluginHandlers.map((pluginConfig) => {
|
||||
if (Array.isArray(pluginConfig)) {
|
||||
return pluginConfig[1];
|
||||
}
|
||||
return null;
|
||||
}).filter((value) => value);
|
||||
|
||||
return {
|
||||
pluginNames,
|
||||
pluginConfig,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Fetch the language data for the specified language.
|
||||
*
|
||||
* @param {string} language The language identifier
|
||||
* @returns {object}
|
||||
*/
|
||||
const fetchLanguage = (language) => fetch(
|
||||
`${M.cfg.wwwroot}/lib/editor/tiny/lang.php/${M.cfg.langrev}/${language}`
|
||||
).then(response => response.json());
|
||||
|
||||
/**
|
||||
* Get a list of all Editors in a Map, keyed by the DOM Node that the Editor is associated with.
|
||||
*
|
||||
* @returns {Map<Node, Editor>}
|
||||
*/
|
||||
export const getAllInstances = () => new Map(instanceMap.entries());
|
||||
|
||||
/**
|
||||
* Get the TinyMCE instance for the specified Node ID.
|
||||
*
|
||||
* @param {string} elementId
|
||||
* @returns {TinyMCE|undefined}
|
||||
*/
|
||||
export const getInstanceForElementId = elementId => getInstanceForElement(document.getElementById(elementId));
|
||||
|
||||
/*
|
||||
* Get the TinyMCE instance for the specified HTMLElement.
|
||||
*
|
||||
* @param {HTMLElement} element
|
||||
* @returns {TinyMCE|undefined}
|
||||
*/
|
||||
export const getInstanceForElement = element => {
|
||||
const instance = instanceMap.get(element);
|
||||
if (instance && instance.removed) {
|
||||
instanceMap.remove(element);
|
||||
return undefined;
|
||||
}
|
||||
return instance;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set up TinyMCE for the selector at the specified HTML Node id.
|
||||
*
|
||||
* @param {object} config The configuration required to setup the editor
|
||||
* @param {string} config.elementId The HTML Node ID
|
||||
* @param {Object} config.options The editor plugin configuration
|
||||
* @return {Promise<TinyMCE>} The TinyMCE instance
|
||||
*/
|
||||
export const setupForElementId = ({elementId, options}) => {
|
||||
const target = document.getElementById(elementId);
|
||||
return setupForTarget(target, options);
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialise the page with standard TinyMCE requirements.
|
||||
*
|
||||
* Currently this includes the language taken from the HTML lang property.
|
||||
*/
|
||||
const initialisePage = async() => {
|
||||
const lang = document.querySelector('html').lang;
|
||||
|
||||
const [tinyMCE, langData] = await Promise.all([getTinyMCE(), fetchLanguage(lang)]);
|
||||
tinyMCE.addI18n(lang, langData);
|
||||
};
|
||||
initialisePage();
|
||||
|
||||
/**
|
||||
* Get the list of plugins to load for the specified configuration.
|
||||
*
|
||||
* If the specified configuration does not include a plugin configuration, then return the default configuration.
|
||||
*
|
||||
* @param {object} options
|
||||
* @param {array} [options.plugins=null] The plugin list
|
||||
* @returns {object}
|
||||
*/
|
||||
const getPlugins = ({plugins = null} = {}) => {
|
||||
if (plugins) {
|
||||
return plugins;
|
||||
}
|
||||
|
||||
if (defaultOptions.plugins) {
|
||||
return defaultOptions.plugins;
|
||||
}
|
||||
|
||||
return {};
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the standard configuration for the specified options.
|
||||
*
|
||||
* @param {Node} target
|
||||
* @param {tinyMCE} tinyMCE
|
||||
* @param {object} options
|
||||
* @param {Array} plugins
|
||||
* @returns {object}
|
||||
*/
|
||||
const getStandardConfig = (target, tinyMCE, options, plugins) => {
|
||||
const lang = document.querySelector('html').lang;
|
||||
|
||||
const config = Object.assign({}, getDefaultConfiguration(), {
|
||||
// eslint-disable-next-line camelcase
|
||||
base_url: baseUrl,
|
||||
|
||||
// Set the editor target.
|
||||
// https://www.tiny.cloud/docs/tinymce/6/editor-important-options/#target
|
||||
target,
|
||||
|
||||
// Set the language.
|
||||
// https://www.tiny.cloud/docs/tinymce/6/ui-localization/#language
|
||||
// eslint-disable-next-line camelcase
|
||||
language: lang,
|
||||
|
||||
// Load the editor stylesheet into the editor iframe.
|
||||
// https://www.tiny.cloud/docs/tinymce/6/add-css-options/
|
||||
// eslint-disable-next-line camelcase
|
||||
content_css: [
|
||||
options.css,
|
||||
],
|
||||
|
||||
// Do not convert URLs to relative URLs.
|
||||
// https://www.tiny.cloud/docs/tinymce/6/url-handling/#convert_urls
|
||||
// eslint-disable-next-line camelcase
|
||||
convert_urls: false,
|
||||
|
||||
// Enabled 'advanced' a11y options.
|
||||
// This includes allowing role="presentation" from the image uploader.
|
||||
// https://www.tiny.cloud/docs/tinymce/6/accessibility/
|
||||
// eslint-disable-next-line camelcase
|
||||
a11y_advanced_options: true,
|
||||
|
||||
// Disable quickbars entirely.
|
||||
// The UI is not ideal and we'll wait for it to improve in future before we enable it in Moodle.
|
||||
// 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,
|
||||
],
|
||||
|
||||
// Skins
|
||||
skin: 'oxide',
|
||||
|
||||
// Remove the "Upgrade" link for Tiny.
|
||||
// https://www.tiny.cloud/docs/tinymce/6/editor-premium-upgrade-promotion/
|
||||
promotion: false,
|
||||
|
||||
// Allow the administrator to disable branding.
|
||||
// https://www.tiny.cloud/docs/tinymce/6/statusbar-configuration-options/#branding
|
||||
branding: options.branding,
|
||||
|
||||
setup: (editor) => {
|
||||
Options.register(editor, options);
|
||||
},
|
||||
});
|
||||
|
||||
config.toolbar = addToolbarSection(config.toolbar, 'content', 'formatting', true);
|
||||
config.toolbar = addToolbarButton(config.toolbar, 'content', 'link');
|
||||
|
||||
return config;
|
||||
};
|
||||
|
||||
/**
|
||||
* Fetch the TinyMCE configuration for this editor instance.
|
||||
*
|
||||
* @param {HTMLElement} target
|
||||
* @param {TinyMCE} tinyMCE The TinyMCE API
|
||||
* @param {Object} options The editor plugin configuration
|
||||
* @param {object} pluginValues
|
||||
* @param {object} pluginValues.pluginConfig The list of plugin configuration
|
||||
* @param {object} pluginValues.pluginNames The list of plugins to load
|
||||
* @returns {object} The TinyMCE Configuration
|
||||
*/
|
||||
const getEditorConfiguration = (target, tinyMCE, options, pluginValues) => {
|
||||
const {
|
||||
pluginNames,
|
||||
pluginConfig,
|
||||
} = pluginValues;
|
||||
|
||||
// Allow plugins to modify the configuration.
|
||||
// This seems a little strange, but we must double-process the config slightly.
|
||||
|
||||
// First we fetch the standard configuration.
|
||||
const instanceConfig = getStandardConfig(target, tinyMCE, options, pluginNames);
|
||||
|
||||
// Next we make any standard changes.
|
||||
// Here we remove the file menu, as it doesn't offer any useful functionality.
|
||||
// We only empty the items list so that a plugin may choose to add to it themselves later if they wish.
|
||||
if (instanceConfig.menu.file) {
|
||||
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.
|
||||
// Any plugin which wishes to have configuration options must register those options here.
|
||||
pluginConfig.filter((pluginConfig) => typeof pluginConfig.configure === 'function').forEach((pluginConfig) => {
|
||||
const pluginInstanceOverride = pluginConfig.configure(instanceConfig, options);
|
||||
Object.assign(instanceConfig, pluginInstanceOverride);
|
||||
});
|
||||
|
||||
// Next we convert the plugin configuration into a format that TinyMCE understands.
|
||||
Object.assign(instanceConfig, Options.getInitialPluginConfiguration(options));
|
||||
|
||||
return instanceConfig;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set up TinyMCE for the HTML Element.
|
||||
*
|
||||
* @param {HTMLElement} target
|
||||
* @param {Object} [options={}] The editor plugin configuration
|
||||
* @return {Promise<TinyMCE>} The TinyMCE instance
|
||||
*/
|
||||
export const setupForTarget = async(target, options = {}) => {
|
||||
const instance = getInstanceForElement(target);
|
||||
if (instance) {
|
||||
return Promise.resolve(instance);
|
||||
}
|
||||
|
||||
// Register a new pending promise to ensure that Behat waits for the editor setup to complete before continuing.
|
||||
const pendingPromise = new Pending('editor_tiny/editor:setupForTarget');
|
||||
|
||||
// Get the list of plugins.
|
||||
const plugins = getPlugins(options);
|
||||
|
||||
// Fetch the tinyMCE API, and instantiate the plugins.
|
||||
const [tinyMCE, pluginValues] = await Promise.all([
|
||||
getTinyMCE(),
|
||||
importPluginList(Object.keys(plugins)),
|
||||
]);
|
||||
|
||||
// TinyMCE uses the element ID as a map key internally, even if the target has changed.
|
||||
// In the case where we have an editor in a modal form which has been detached from the DOM, but the editor not removed,
|
||||
// we need to manually destroy the editor.
|
||||
// We could theoretically do this with a Mutation Observer, but in some cases the Node may be moved,
|
||||
// or added back elsewhere in the DOM.
|
||||
const existingEditor = tinyMCE.EditorManager.get(target.id);
|
||||
if (existingEditor) {
|
||||
if (existingEditor.targetElm.closest('body')) {
|
||||
if (existingEditor.targetElm === target) {
|
||||
pendingPromise.resolve();
|
||||
return Promise.resolve(existingEditor);
|
||||
} else {
|
||||
pendingPromise.resolve();
|
||||
throw new Error('TinyMCE instance already exists for different target with same ID');
|
||||
}
|
||||
} else {
|
||||
existingEditor.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
// Get the editor configuration for this editor.
|
||||
const instanceConfig = getEditorConfiguration(target, tinyMCE, options, pluginValues);
|
||||
|
||||
// Initialise the editor instance for the given configuration.
|
||||
// At this point any plugin which has configuration options registered will have them applied for this instance.
|
||||
const [editor] = await tinyMCE.init(instanceConfig);
|
||||
|
||||
// Store the editor instance in the instanceMap and register a listener on removal to remove it from the map.
|
||||
instanceMap.set(target, editor);
|
||||
editor.on('remove', ({target}) => {
|
||||
// Handle removal of the editor from the map on destruction.
|
||||
instanceMap.delete(target.targetElm);
|
||||
});
|
||||
|
||||
pendingPromise.resolve();
|
||||
return editor;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the default editor configuration.
|
||||
*
|
||||
* This configuration is used when an editor is initialised without any configuration.
|
||||
*
|
||||
* @param {object} [options={}]
|
||||
*/
|
||||
export const configureDefaultEditor = (options = {}) => {
|
||||
defaultOptions = options;
|
||||
};
|
||||
@@ -0,0 +1,64 @@
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Tiny Loader for Moodle
|
||||
*
|
||||
* @module editor_tiny/loader
|
||||
* @copyright 2022 Andrew Lyons <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
let tinyMCEPromise;
|
||||
|
||||
import * as Config from 'core/config';
|
||||
|
||||
export const baseUrl = `${Config.wwwroot}/lib/editor/tiny/loader.php/${M.cfg.jsrev}`;
|
||||
|
||||
/**
|
||||
* Get the TinyMCE API Object.
|
||||
*
|
||||
* @returns {Promise<TinyMCE>} The TinyMCE API Object
|
||||
*/
|
||||
export const getTinyMCE = () => {
|
||||
if (tinyMCEPromise) {
|
||||
return tinyMCEPromise;
|
||||
}
|
||||
|
||||
tinyMCEPromise = new Promise((resolve, reject) => {
|
||||
const head = document.querySelector('head');
|
||||
let script = head.querySelector('script[data-tinymce="tinymce"]');
|
||||
if (script) {
|
||||
resolve(window.tinyMCE);
|
||||
}
|
||||
|
||||
script = document.createElement('script');
|
||||
script.dataset.tinymce = 'tinymce';
|
||||
script.src = `${baseUrl}/tinymce.js`;
|
||||
script.async = true;
|
||||
|
||||
script.addEventListener('load', () => {
|
||||
resolve(window.tinyMCE);
|
||||
}, false);
|
||||
|
||||
script.addEventListener('error', (err) => {
|
||||
reject(err);
|
||||
}, false);
|
||||
|
||||
head.append(script);
|
||||
});
|
||||
|
||||
return tinyMCEPromise;
|
||||
};
|
||||
@@ -0,0 +1,99 @@
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Option helper for TinyMCE Editor Manager.
|
||||
*
|
||||
* @module editor_tiny/options
|
||||
* @copyright 2022 Andrew Lyons <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
const optionContextId = 'moodle:contextid';
|
||||
const optionDraftItemId = 'moodle:draftitemid';
|
||||
const filePickers = 'moodle:filepickers';
|
||||
const optionsMoodleLang = 'moodle:language';
|
||||
const currentLanguage = 'moodle:currentLanguage';
|
||||
|
||||
export const register = (editor, options) => {
|
||||
const registerOption = editor.options.register;
|
||||
const setOption = editor.options.set;
|
||||
|
||||
registerOption(optionContextId, {
|
||||
processor: 'number',
|
||||
"default": 0,
|
||||
});
|
||||
setOption(optionContextId, options.context);
|
||||
|
||||
registerOption(filePickers, {
|
||||
processor: 'object',
|
||||
"default": {},
|
||||
});
|
||||
setOption(filePickers, options.filepicker);
|
||||
|
||||
registerOption(optionDraftItemId, {
|
||||
processor: 'number',
|
||||
"default": 0,
|
||||
});
|
||||
setOption(optionDraftItemId, options.draftitemid);
|
||||
|
||||
registerOption(currentLanguage, {
|
||||
processor: 'string',
|
||||
"default": 'en',
|
||||
});
|
||||
setOption(currentLanguage, options.currentLanguage);
|
||||
|
||||
// This is primarily used by the media plugin, but it may be re-used elsewhere so is included here as it is large.
|
||||
registerOption(optionsMoodleLang, {
|
||||
processor: 'object',
|
||||
"default": {},
|
||||
});
|
||||
setOption(optionsMoodleLang, options.language);
|
||||
};
|
||||
|
||||
export const getContextId = (editor) => editor.options.get(optionContextId);
|
||||
export const getDraftItemId = (editor) => editor.options.get(optionDraftItemId);
|
||||
export const getFilepickers = (editor) => editor.options.get(filePickers);
|
||||
export const getFilePicker = (editor, type) => getFilepickers(editor)[type];
|
||||
export const getMoodleLang = (editor) => editor.options.get(optionsMoodleLang);
|
||||
export const getCurrentLanguage = (editor) => editor.options.get(currentLanguage);
|
||||
|
||||
/**
|
||||
* Get a set of namespaced options for all defined plugins.
|
||||
*
|
||||
* @param {object} options
|
||||
* @returns {object}
|
||||
*/
|
||||
export const getInitialPluginConfiguration = (options) => {
|
||||
const config = {};
|
||||
|
||||
Object.entries(options.plugins).forEach(([pluginName, pluginConfig]) => {
|
||||
const values = Object.entries(pluginConfig.config ?? {});
|
||||
values.forEach(([optionName, value]) => {
|
||||
config[getPluginOptionName(pluginName, optionName)] = value;
|
||||
});
|
||||
});
|
||||
|
||||
return config;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the namespaced option name for a plugin.
|
||||
*
|
||||
* @param {string} pluginName
|
||||
* @param {string} optionName
|
||||
* @returns {string}
|
||||
*/
|
||||
export const getPluginOptionName = (pluginName, optionName) => `${pluginName}:${optionName}`;
|
||||
@@ -0,0 +1,110 @@
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Tiny Media plugin for Moodle.
|
||||
*
|
||||
* @module editor_tiny/uploade
|
||||
* @copyright 2022 Andrew Lyons <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
import {
|
||||
notifyUploadStarted,
|
||||
notifyUploadCompleted,
|
||||
} from 'core_form/events';
|
||||
import {getFilePicker} from 'editor_tiny/options';
|
||||
|
||||
// This image uploader is based on advice given at:
|
||||
// https://www.tiny.cloud/docs/tinymce/6/upload-images/
|
||||
export default (editor, filePickerType, blob, fileName, progress) => new Promise((resolve, reject) => {
|
||||
notifyUploadStarted(editor.targetElm.id);
|
||||
|
||||
const xhr = new XMLHttpRequest();
|
||||
|
||||
// Add the progress handler.
|
||||
xhr.upload.addEventListener('progress', (e) => {
|
||||
progress(e.loaded / e.total * 100);
|
||||
});
|
||||
|
||||
xhr.addEventListener('load', () => {
|
||||
if (xhr.status === 403) {
|
||||
reject({
|
||||
message: `HTTP error: ${xhr.status}`,
|
||||
remove: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (xhr.status < 200 || xhr.status >= 300) {
|
||||
reject(`HTTP Error: ${xhr.status}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const response = JSON.parse(xhr.responseText);
|
||||
|
||||
if (!response) {
|
||||
reject(`Invalid JSON: ${xhr.responseText}`);
|
||||
return;
|
||||
}
|
||||
|
||||
notifyUploadCompleted(editor.targetElm.id);
|
||||
|
||||
let location;
|
||||
if (response.url) {
|
||||
location = response.url;
|
||||
} else if (response.event && response.event === 'fileexists' && response.newfile) {
|
||||
// A file with this name is already in use here - rename to avoid conflict.
|
||||
// Chances are, it's a different image (stored in a different folder on the user's computer).
|
||||
// If the user wants to reuse an existing image, they can copy/paste it within the editor.
|
||||
location = response.newfile.url;
|
||||
}
|
||||
|
||||
if (location && typeof location === 'string') {
|
||||
resolve(location);
|
||||
return;
|
||||
}
|
||||
|
||||
reject(`Unable to handle file result: ${xhr.responseText}`);
|
||||
});
|
||||
|
||||
xhr.addEventListener('error', () => {
|
||||
reject({
|
||||
message: `Upload failed due to an XHR transport error. Code: ${xhr.status}`,
|
||||
remove: true,
|
||||
});
|
||||
});
|
||||
|
||||
const formData = new FormData();
|
||||
const options = getFilePicker(editor, filePickerType);
|
||||
|
||||
formData.append('repo_upload_file', blob, fileName);
|
||||
formData.append('itemid', options.itemid);
|
||||
Object.values(options.repositories).some((repository) => {
|
||||
if (repository.type === 'upload') {
|
||||
formData.append('repo_id', repository.id);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
formData.append('env', options.env);
|
||||
formData.append('sesskey', M.cfg.sesskey);
|
||||
formData.append('client_id', options.client_id);
|
||||
formData.append('savepath', options.savepath ?? '/');
|
||||
formData.append('ctx_id', options.context.id);
|
||||
|
||||
xhr.open('POST', `${M.cfg.wwwroot}/repository/repository_ajax.php?action=upload`, true);
|
||||
xhr.send(formData);
|
||||
});
|
||||
@@ -0,0 +1,247 @@
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import {renderForPromise} from 'core/templates';
|
||||
import {getFilePicker} from './options';
|
||||
import {get_string as getString} from 'core/str';
|
||||
|
||||
/**
|
||||
* Get the image path for the specified image.
|
||||
*
|
||||
* @param {string} identifier The name of the image
|
||||
* @param {string} component The component name
|
||||
* @return {string} The image URL path
|
||||
*/
|
||||
export const getImagePath = (identifier, component = 'editor_tiny') => Promise.resolve(M.util.image_url(identifier, component));
|
||||
|
||||
export const getButtonImage = async(identifier, component = 'editor_tiny') => renderForPromise('editor_tiny/toolbar_button', {
|
||||
image: await getImagePath(identifier, component),
|
||||
});
|
||||
|
||||
/**
|
||||
* Helper to display a filepicker and return a Promise.
|
||||
*
|
||||
* The Promise will resolve when a file is selected, or reject if the file type is not found.
|
||||
*
|
||||
* @param {TinyMCE} editor
|
||||
* @param {string} filetype
|
||||
* @returns {Promise<object>} The file object returned by the filepicker
|
||||
*/
|
||||
export const displayFilepicker = (editor, filetype) => new Promise((resolve, reject) => {
|
||||
const configuration = getFilePicker(editor, filetype);
|
||||
if (configuration) {
|
||||
const options = {
|
||||
...configuration,
|
||||
formcallback: resolve,
|
||||
};
|
||||
M.core_filepicker.show(Y, options);
|
||||
return;
|
||||
}
|
||||
reject(`Unknown filetype ${filetype}`);
|
||||
});
|
||||
|
||||
/**
|
||||
* Given a TinyMCE Toolbar configuration, add the specified button to the named section.
|
||||
*
|
||||
* @param {object} toolbar
|
||||
* @param {string} section
|
||||
* @param {string} button
|
||||
* @returns {object} The toolbar configuration
|
||||
*/
|
||||
export const addToolbarButton = (toolbar, section, button) => {
|
||||
if (!toolbar) {
|
||||
return [{
|
||||
name: section,
|
||||
items: [button],
|
||||
}];
|
||||
}
|
||||
|
||||
const mutatedToolbar = JSON.parse(JSON.stringify(toolbar));
|
||||
return mutatedToolbar.map((item) => {
|
||||
if (item.name === section) {
|
||||
item.items.push(button);
|
||||
}
|
||||
|
||||
return item;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Given a TinyMCE Toolbar configuration, add the specified buttons to the named section.
|
||||
*
|
||||
* @param {object} toolbar
|
||||
* @param {string} section
|
||||
* @param {Array} buttons
|
||||
* @returns {object} The toolbar configuration
|
||||
*/
|
||||
export const addToolbarButtons = (toolbar, section, buttons) => {
|
||||
if (!toolbar) {
|
||||
return [{
|
||||
name: section,
|
||||
items: buttons,
|
||||
}];
|
||||
}
|
||||
|
||||
const mutatedToolbar = JSON.parse(JSON.stringify(toolbar));
|
||||
return mutatedToolbar.map((item) => {
|
||||
if (item.name === section) {
|
||||
buttons.forEach(button => item.items.push(button));
|
||||
}
|
||||
|
||||
return item;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Insert a new section into the toolbar.
|
||||
*
|
||||
* @param {array} toolbar The TinyMCE.editor.settings.toolbar configuration
|
||||
* @param {string} name The new section name to add
|
||||
* @param {string} relativeTo Insert relative to this section name
|
||||
* @param {boolean} append Append or Prepend
|
||||
* @returns {array}
|
||||
*/
|
||||
export const addToolbarSection = (toolbar, name, relativeTo, append = true) => {
|
||||
const newSection = {
|
||||
name,
|
||||
items: [],
|
||||
};
|
||||
const sectionInserted = toolbar.some((section, index) => {
|
||||
if (section.name === relativeTo) {
|
||||
if (append) {
|
||||
toolbar.splice(index + 1, 0, newSection);
|
||||
} else {
|
||||
toolbar.splice(index, 0, newSection);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
if (!sectionInserted) {
|
||||
// Relative section not found.
|
||||
if (append) {
|
||||
toolbar.push(newSection);
|
||||
} else {
|
||||
toolbar.unshift(newSection);
|
||||
}
|
||||
}
|
||||
|
||||
return toolbar;
|
||||
};
|
||||
|
||||
/**
|
||||
* Given a TinyMCE Menubar configuration, add the specified button to the named section.
|
||||
*
|
||||
* @param {object} menubar
|
||||
* @param {string} section
|
||||
* @param {string} menuitem
|
||||
* @returns {object}
|
||||
*/
|
||||
export const addMenubarItem = (menubar, section, menuitem) => {
|
||||
if (!menubar) {
|
||||
const emptyMenubar = {};
|
||||
emptyMenubar[section] = {
|
||||
title: section,
|
||||
items: menuitem,
|
||||
};
|
||||
}
|
||||
|
||||
const mutatedMenubar = JSON.parse(JSON.stringify(menubar));
|
||||
Array.from(Object.entries(mutatedMenubar)).forEach(([name, menu]) => {
|
||||
if (name === section) {
|
||||
menu.items = `${menu.items} ${menuitem}`;
|
||||
}
|
||||
});
|
||||
|
||||
return mutatedMenubar;
|
||||
};
|
||||
|
||||
/**
|
||||
* Given a TinyMCE contextmenu configuration, add the specified button to the end.
|
||||
*
|
||||
* @param {string} contextmenu
|
||||
* @param {string[]} menuitems
|
||||
* @returns {string}
|
||||
*/
|
||||
export const addContextmenuItem = (contextmenu, ...menuitems) => {
|
||||
const contextmenuItems = (contextmenu ?? '').split(' ');
|
||||
|
||||
return contextmenuItems
|
||||
.concat(menuitems)
|
||||
.filter((item) => item !== '')
|
||||
.join(' ');
|
||||
};
|
||||
|
||||
/**
|
||||
* Given a TinyMCE quickbars configuration, add items to the meun.
|
||||
*
|
||||
* @param {string} toolbar
|
||||
* @param {string[]} menuitems
|
||||
* @returns {string}
|
||||
*/
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
export const addQuickbarsToolbarItem = (toolbar, ...menuitems) => {
|
||||
// For the moment we have disabled use of this menu.
|
||||
// The configuration is left in place to allow plugins to declare that they would like to support it in the future.
|
||||
return toolbar;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the link to the user documentation for the named plugin.
|
||||
*
|
||||
* @param {string} pluginName
|
||||
* @returns {string}
|
||||
*/
|
||||
export const getDocumentationLink = (pluginName) => `https://docs.moodle.org/en/editor_tiny/${pluginName}`;
|
||||
|
||||
/**
|
||||
* Get the default plugin metadata for the named plugin.
|
||||
* If no URL is provided, then a URL is generated pointing to the standard Moodle Documentation.
|
||||
*
|
||||
* @param {string} component The component name
|
||||
* @param {string} pluginName The plugin name
|
||||
* @param {string|null} [url=null] An optional URL to the plugin documentation
|
||||
* @returns {object}
|
||||
*/
|
||||
export const getPluginMetadata = async(component, pluginName, url = null) => {
|
||||
const name = await getString('helplinktext', component);
|
||||
return {
|
||||
getMetadata: () => ({
|
||||
name,
|
||||
url: url ?? getDocumentationLink(pluginName),
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Ensure that the editor is still in the DOM, removing it if it is not.
|
||||
*
|
||||
* @param {TinyMCE} editor
|
||||
* @returns {TinyMCE|null}
|
||||
*/
|
||||
export const ensureEditorIsValid = (editor) => {
|
||||
// TinyMCE uses the element ID as a map key internally, even if the target has changed.
|
||||
// In cases such as where an editor is in a modal form which has been detached from the DOM, but the editor not removed,
|
||||
// we need to manually destroy the editor.
|
||||
// We could theoretically do this with a Mutation Observer, but in some cases the Node may be moved,
|
||||
// or added back elsewhere in the DOM.
|
||||
if (!editor?.targetElm?.closest('body')) {
|
||||
editor.destroy();
|
||||
return null;
|
||||
}
|
||||
|
||||
return editor;
|
||||
};
|
||||
@@ -0,0 +1,221 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace editor_tiny;
|
||||
|
||||
/**
|
||||
* Tiny Editor.
|
||||
*
|
||||
* @package editor_tiny
|
||||
* @copyright 2021 Andrew Lyons <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class editor extends \texteditor {
|
||||
|
||||
/** @var manager The Tiny Manager instace */
|
||||
protected $manager;
|
||||
|
||||
/** @var \stdClass|null The default configuration to use if none is provided */
|
||||
protected static $defaultconfiguration = null;
|
||||
|
||||
/**
|
||||
* Instantiate the new editor instance.
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->manager = new manager();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default configuration for the editor.
|
||||
*
|
||||
* @param manager $manager The editor manager
|
||||
*/
|
||||
public static function set_default_configuration(manager $manager): void {
|
||||
global $PAGE;
|
||||
|
||||
if (self::is_default_configuration_set()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$context = $PAGE->context;
|
||||
|
||||
$config = (object) [
|
||||
'css' => $PAGE->theme->editor_css_url()->out(false),
|
||||
'context' => $context->id,
|
||||
'plugins' => $manager->get_plugin_configuration($context, [], []),
|
||||
];
|
||||
|
||||
$config = json_encode($config);
|
||||
$inlinejs = <<<EOF
|
||||
M.util.js_pending('editor_tiny/editor:defaultConfiguration');
|
||||
require(['editor_tiny/editor'], (Tiny) => {
|
||||
Tiny.configureDefaultEditor({$config});
|
||||
M.util.js_complete('editor_tiny/editor:defaultConfiguration');
|
||||
});
|
||||
EOF;
|
||||
|
||||
$PAGE->requires->js_amd_inline($inlinejs);
|
||||
|
||||
self::$defaultconfiguration = $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the current defautl configuration.
|
||||
*
|
||||
* @return \stdClass|null The default configuration or null if not set.
|
||||
*/
|
||||
public static function get_default_configuration(): ?\stdClass {
|
||||
return self::$defaultconfiguration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the default configuration.
|
||||
*/
|
||||
public static function reset_default_configuration(): void {
|
||||
self::$defaultconfiguration = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the default configuration is set.
|
||||
*
|
||||
* @return bool True if the default configuration is set.
|
||||
*/
|
||||
public static function is_default_configuration_set(): bool {
|
||||
return !empty(self::$defaultconfiguration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the current browser supported by this editor?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function supported_by_browser() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* List of supported text field formats.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_supported_formats() {
|
||||
return [
|
||||
FORMAT_HTML => FORMAT_HTML,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns text format preferred by this editor.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function get_preferred_format() {
|
||||
return FORMAT_HTML;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this editor support picking from repositories?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function supports_repositories() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this editor for given element.
|
||||
*
|
||||
* @param string $elementid
|
||||
* @param array $options
|
||||
* @param null $fpoptions
|
||||
*/
|
||||
public function use_editor($elementid, array $options = null, $fpoptions = null) {
|
||||
global $PAGE;
|
||||
|
||||
// Ensure that the default configuration is set.
|
||||
self::set_default_configuration($this->manager);
|
||||
|
||||
if ($fpoptions === null) {
|
||||
$fpoptions = [];
|
||||
}
|
||||
|
||||
$context = $PAGE->context;
|
||||
|
||||
if (isset($options['context']) && ($options['context'] instanceof \context)) {
|
||||
// A different context was provided.
|
||||
// Use that instead.
|
||||
$context = $options['context'];
|
||||
}
|
||||
|
||||
// Generate the configuration for this editor.
|
||||
$siteconfig = get_config('editor_tiny');
|
||||
$config = (object) [
|
||||
// The URL to the CSS file for the editor.
|
||||
'css' => $PAGE->theme->editor_css_url()->out(false),
|
||||
|
||||
// The current context for this page or editor.
|
||||
'context' => $context->id,
|
||||
|
||||
// File picker options.
|
||||
'filepicker' => $fpoptions,
|
||||
|
||||
'currentLanguage' => current_language(),
|
||||
|
||||
'branding' => property_exists($siteconfig, 'branding') ? !empty($siteconfig->branding) : true,
|
||||
|
||||
// Language options.
|
||||
'language' => [
|
||||
'currentlang' => current_language(),
|
||||
'installed' => get_string_manager()->get_list_of_translations(true),
|
||||
'available' => get_string_manager()->get_list_of_languages()
|
||||
],
|
||||
|
||||
// Plugin configuration.
|
||||
'plugins' => $this->manager->get_plugin_configuration($context, $options, $fpoptions, $this),
|
||||
];
|
||||
|
||||
foreach ($fpoptions as $fp) {
|
||||
// Guess the draftitemid for the editor.
|
||||
// Note: This is the best we can do at the moment.
|
||||
if (!empty($fp->itemid)) {
|
||||
$config->draftitemid = $fp->itemid;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$configoptions = json_encode(convert_to_array($config));
|
||||
|
||||
// Note: This is not ideal but the editor does not have control over any HTML output.
|
||||
// The Editor API only allows you to run JavaScript.
|
||||
// In the future we will extend the editor API to allow it to generate the textarea, or attributes to use in the
|
||||
// textarea or its wrapper.
|
||||
// For now we cannot use the `js_call_amd()` API call because it warns if the parameters passed exceed a
|
||||
// relatively low character limit.
|
||||
$config = json_encode($config);
|
||||
$inlinejs = <<<EOF
|
||||
M.util.js_pending('editor_tiny/editor');
|
||||
require(['editor_tiny/editor'], (Tiny) => {
|
||||
Tiny.setupForElementId({
|
||||
elementId: "${elementid}",
|
||||
options: ${configoptions},
|
||||
});
|
||||
M.util.js_complete('editor_tiny/editor');
|
||||
});
|
||||
EOF;
|
||||
|
||||
$PAGE->requires->js_amd_inline($inlinejs);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,583 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace editor_tiny;
|
||||
|
||||
use context;
|
||||
|
||||
/**
|
||||
* Tiny Editor Plugin manager.
|
||||
*
|
||||
* @package editor_tiny
|
||||
* @copyright 2021 Andrew Lyons <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class manager {
|
||||
|
||||
/**
|
||||
* Get the configuration for all plugins.
|
||||
*
|
||||
* @param context $context The context that the editor is used within
|
||||
* @param array $options The options passed in when requesting the editor
|
||||
* @param array $fpoptions The filepicker options passed in when requesting the editor
|
||||
* @param editor $editor The editor instance in which the plugin is initialised
|
||||
*/
|
||||
public function get_plugin_configuration(
|
||||
context $context,
|
||||
array $options = [],
|
||||
array $fpoptions = [],
|
||||
?editor $editor = null
|
||||
): array {
|
||||
$disabledplugins = $this->get_disabled_plugins();
|
||||
|
||||
// Get the list of plugins.
|
||||
// Note: Disabled plugins are already removed from this list.
|
||||
$plugins = $this->get_shipped_plugins();
|
||||
|
||||
// Fetch configuration for Moodle plugins.
|
||||
$moodleplugins = \core_component::get_plugin_list_with_class('tiny', 'plugininfo');
|
||||
foreach ($moodleplugins as $plugin => $classname) {
|
||||
if (in_array($plugin, $disabledplugins) || in_array("{$plugin}/plugin", $disabledplugins)) {
|
||||
// Skip getting data for disabled plugins.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!is_a($classname, plugin::class, true)) {
|
||||
// Skip plugins that do not implement the plugin interface.
|
||||
debugging("Plugin {$plugin} does not implement the plugin interface", DEBUG_DEVELOPER);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$classname::is_enabled($context, $options, $fpoptions, $editor)) {
|
||||
// This plugin has disabled itself for some reason.
|
||||
// This is typical for media plugins where there is no file storage.
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get the plugin information, which includes the list of buttons, menu items, and configuration.
|
||||
$plugininfo = $classname::get_plugin_info(
|
||||
$context,
|
||||
$options,
|
||||
$fpoptions,
|
||||
$editor
|
||||
);
|
||||
|
||||
if (!empty($config)) {
|
||||
$plugininfo['config'] = $config;
|
||||
}
|
||||
|
||||
// We suffix the plugin name for Moodle plugins with /plugin to avoid conflicts with Tiny plugins.
|
||||
$plugins["{$plugin}/plugin"] = $plugininfo;
|
||||
}
|
||||
|
||||
return $plugins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of the buttons provided by this plugin.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
protected function get_tinymce_buttons(): array {
|
||||
// The following list is defined at:
|
||||
// https://www.tiny.cloud/docs/advanced/available-toolbar-buttons/#thecoretoolbarbuttons.
|
||||
return [
|
||||
// These are always available, without requiring additional plugins.
|
||||
'aligncenter',
|
||||
'alignjustify',
|
||||
'alignleft',
|
||||
'alignnone',
|
||||
'alignright',
|
||||
'blockquote',
|
||||
'backcolor',
|
||||
'bold',
|
||||
'copy',
|
||||
'cut',
|
||||
'fontselect',
|
||||
'fontsizeselect',
|
||||
'forecolor',
|
||||
'formatselect',
|
||||
'h1',
|
||||
'h2',
|
||||
'h3',
|
||||
'h4',
|
||||
'h5',
|
||||
'h6',
|
||||
'indent',
|
||||
'italic',
|
||||
'language',
|
||||
'lineheight',
|
||||
'newdocument',
|
||||
'outdent',
|
||||
'paste',
|
||||
'redo',
|
||||
'remove',
|
||||
'removeformat',
|
||||
'selectall',
|
||||
'strikethrough',
|
||||
'styleselect',
|
||||
'subscript',
|
||||
'superscript',
|
||||
'underline',
|
||||
'undo',
|
||||
'visualaid',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of the menu items provided by this plugin.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
protected function get_tinymce_menuitems(): array {
|
||||
// The following list is defined at:
|
||||
// https://www.tiny.cloud/docs/advanced/available-menu-items/#thecoremenuitems.
|
||||
return [
|
||||
'align' => 'format',
|
||||
'backcolor' => 'format',
|
||||
'blockformats' => 'format',
|
||||
'bold' => 'format',
|
||||
'codeformat' => 'format',
|
||||
'copy' => 'copy',
|
||||
'cut' => 'copy',
|
||||
'forecolor' => 'format',
|
||||
'formats' => 'format',
|
||||
'fontformats' => 'format',
|
||||
'fontsizes' => 'format',
|
||||
'italic' => 'format',
|
||||
'language' => 'format',
|
||||
'lineheight' => 'format',
|
||||
'newdocument' => 'file',
|
||||
'paste' => 'copy',
|
||||
'redo' => 'copy',
|
||||
'removeformat' => 'format',
|
||||
'selectall' => 'edit',
|
||||
'strikethrough' => 'format',
|
||||
'subscript' => 'format',
|
||||
'superscript' => 'format',
|
||||
'underline' => 'format',
|
||||
'undo' => 'copy',
|
||||
'visualaid' => 'view',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of all available plugins, including both TinyMCE shipped, and Moodle add-onis.
|
||||
*
|
||||
* Each plugin is returned as an array element containing:
|
||||
* - a list of buttons (if applicable); and
|
||||
* - a list of menuitems (if applicable).
|
||||
*
|
||||
* Note: Not all plugins include buttons, and not all plugins include menuitems.
|
||||
* These array keys are optional.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_available_plugins(): array {
|
||||
$plugins = $this->get_shipped_plugins();
|
||||
$plugins += $this->get_moodle_plugins();
|
||||
|
||||
$disabledplugins = $this->get_disabled_plugins();
|
||||
$plugins = array_filter($plugins, function ($plugin) use ($disabledplugins) {
|
||||
return !in_array($plugin, $disabledplugins);
|
||||
}, ARRAY_FILTER_USE_KEY);
|
||||
|
||||
return $plugins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of all available plugins built into TinyMCE and not shipped as separate Moodle plugins.
|
||||
*
|
||||
* Each plugin is returned as an array element containing:
|
||||
* - a list of buttons (if applicable); and
|
||||
* - a list of menuitems (if applicable).
|
||||
*
|
||||
* Note: Not all plugins include buttons, and not all plugins include menuitems.
|
||||
* These array keys are optional.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_shipped_plugins(): array {
|
||||
$plugins = $this->get_tinymce_plugins();
|
||||
if ($this->premium_plugins_enabled()) {
|
||||
$plugins += $this->get_premium_plugins();
|
||||
}
|
||||
|
||||
$disabledplugins = $this->get_disabled_plugins();
|
||||
return array_filter($plugins, function($plugin) use ($disabledplugins) {
|
||||
return !in_array($plugin, $disabledplugins);
|
||||
}, ARRAY_FILTER_USE_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of the core plugins with their button, and menuitem, configuration.
|
||||
*
|
||||
* @return array[]
|
||||
*/
|
||||
protected function get_tinymce_plugins(): array {
|
||||
// The following list is defined at:
|
||||
// https://www.tiny.cloud/docs/advanced/available-toolbar-buttons/#thecoretoolbarbuttons.
|
||||
return [
|
||||
'anchor' => [
|
||||
'buttons' => [
|
||||
'anchor',
|
||||
],
|
||||
'menuitems' => [
|
||||
'anchor' => 'insert',
|
||||
],
|
||||
],
|
||||
'autosave' => [
|
||||
'buttons' => [
|
||||
'restoredraft',
|
||||
],
|
||||
'menuitems' => [
|
||||
'restoredraft' => 'file',
|
||||
],
|
||||
],
|
||||
'charmap' => [
|
||||
'buttons' => [
|
||||
'charmap',
|
||||
],
|
||||
'menuitems' => [
|
||||
'charmap' => 'insert',
|
||||
],
|
||||
],
|
||||
'code' => [
|
||||
'buttons' => [
|
||||
'code',
|
||||
],
|
||||
'menuitems' => [
|
||||
'code' => 'view',
|
||||
],
|
||||
],
|
||||
'codesample' => [
|
||||
'buttons' => [
|
||||
'codesample',
|
||||
],
|
||||
'menutiems' => [
|
||||
'codesample' => 'insert',
|
||||
],
|
||||
],
|
||||
'directionality' => [
|
||||
'buttons' => [
|
||||
'ltr',
|
||||
'rtl',
|
||||
],
|
||||
],
|
||||
'emoticons' => [
|
||||
'buttons' => [
|
||||
'emoticons',
|
||||
],
|
||||
'menuitems' => [
|
||||
'emoticons' => 'insert',
|
||||
],
|
||||
],
|
||||
'fullscreen' => [
|
||||
'buttons' => [
|
||||
'fullscreen',
|
||||
],
|
||||
'menuitems' => [
|
||||
'fullscreen' => 'view',
|
||||
],
|
||||
],
|
||||
'help' => [
|
||||
'buttons' => [
|
||||
'help',
|
||||
],
|
||||
'menuitems' => [
|
||||
'help' => 'help',
|
||||
],
|
||||
],
|
||||
'image' => [
|
||||
'buttons' => [
|
||||
'image',
|
||||
],
|
||||
'menuitems' => [
|
||||
'image' => 'insert',
|
||||
],
|
||||
],
|
||||
'insertdatetime' => [
|
||||
'buttons' => [
|
||||
'insertdatetime',
|
||||
],
|
||||
'menuitems' => [
|
||||
'insertdatetime' => 'insert',
|
||||
],
|
||||
],
|
||||
'link' => [
|
||||
'buttons' => [
|
||||
'link',
|
||||
'openlink',
|
||||
'unlink',
|
||||
],
|
||||
'menuitems' => [
|
||||
'link' => 'insert',
|
||||
],
|
||||
],
|
||||
'lists' => [
|
||||
'buttons' => [
|
||||
'bullist',
|
||||
'numlist',
|
||||
],
|
||||
],
|
||||
'media' => [
|
||||
'buttons' => [
|
||||
'media',
|
||||
],
|
||||
'menuitems' => [
|
||||
'media' => 'insert',
|
||||
],
|
||||
],
|
||||
'nonbreaking' => [
|
||||
'buttons' => [
|
||||
'nonbreaking',
|
||||
],
|
||||
'menuitems' => [
|
||||
'nonbreaking' => 'insert',
|
||||
],
|
||||
],
|
||||
'pagebreak' => [
|
||||
'buttons' => [
|
||||
'pagebreak',
|
||||
],
|
||||
'menuitems' => [
|
||||
'pagebreak' => 'insert',
|
||||
],
|
||||
],
|
||||
'preview' => [
|
||||
'buttons' => [
|
||||
'preview',
|
||||
],
|
||||
'menuitems' => [
|
||||
'preview' => 'file',
|
||||
],
|
||||
],
|
||||
'quickbars' => [
|
||||
'buttons' => [
|
||||
'quickimage',
|
||||
'quicklink',
|
||||
'quicktable',
|
||||
],
|
||||
],
|
||||
'save' => [
|
||||
'buttons' => [
|
||||
'cancel',
|
||||
'save',
|
||||
],
|
||||
],
|
||||
'searchreplace' => [
|
||||
'buttons' => [
|
||||
'searchreplace',
|
||||
],
|
||||
'menuitems' => [
|
||||
'searchreplace' => 'edit',
|
||||
],
|
||||
],
|
||||
'table' => [
|
||||
'buttons' => [
|
||||
'table',
|
||||
'tablecellprops',
|
||||
'tablecopyrow',
|
||||
'tablecutrow',
|
||||
'tabledelete',
|
||||
'tabledeletecol',
|
||||
'tabledeleterow',
|
||||
'tableinsertdialog',
|
||||
'tableinsertcolafter',
|
||||
'tableinsertcolbefore',
|
||||
'tableinsertrowafter',
|
||||
'tableinsertrowbefore',
|
||||
'tablemergecells',
|
||||
'tablepasterowafter',
|
||||
'tablepasterowbefore',
|
||||
'tableprops',
|
||||
'tablerowprops',
|
||||
'tablesplitcells',
|
||||
'tableclass',
|
||||
'tablecellclass',
|
||||
'tablecellvalign',
|
||||
'tablecellborderwidth',
|
||||
'tablecellborderstyle',
|
||||
'tablecaption',
|
||||
'tablecellbackgroundcolor',
|
||||
'tablecellbordercolor',
|
||||
'tablerowheader',
|
||||
'tablecolheader',
|
||||
],
|
||||
'menuitems' => [
|
||||
'inserttable' => 'table',
|
||||
'tableprops' => 'table',
|
||||
'deletetable' => 'table',
|
||||
'cell' => 'table',
|
||||
'tablemergecells' => 'table',
|
||||
'tablesplitcells' => 'table',
|
||||
'tablecellprops' => 'table',
|
||||
'column' => 'table',
|
||||
'tableinsertcolumnbefore' => 'table',
|
||||
'tableinsertcolumnafter' => 'table',
|
||||
'tablecutcolumn' => 'table',
|
||||
'tablecopycolumn' => 'table',
|
||||
'tablepastecolumnbefore' => 'table',
|
||||
'tablepastecolumnafter' => 'table',
|
||||
'tabledeletecolumn' => 'table',
|
||||
'row' => 'table',
|
||||
'tableinsertrowbefore' => 'table',
|
||||
'tableinsertrowafter' => 'table',
|
||||
'tablecutrow' => 'table',
|
||||
'tablecopyrow' => 'table',
|
||||
'tablepasterowbefore' => 'table',
|
||||
'tablepasterowafter' => 'table',
|
||||
'tablerowprops' => 'table',
|
||||
'tabledeleterow' => 'table',
|
||||
],
|
||||
],
|
||||
'template' => [
|
||||
'buttons' => [
|
||||
'template',
|
||||
],
|
||||
'menuitems' => [
|
||||
'template' => 'insert',
|
||||
],
|
||||
],
|
||||
'visualblocks' => [
|
||||
'buttons' => [
|
||||
'visualblocks',
|
||||
],
|
||||
'menuitems' => [
|
||||
'visualblocks' => 'view',
|
||||
],
|
||||
],
|
||||
'visualchars' => [
|
||||
'buttons' => [
|
||||
'visualchars',
|
||||
],
|
||||
'menuitems' => [
|
||||
'visualchars' => 'view',
|
||||
],
|
||||
],
|
||||
'wordcount' => [
|
||||
'buttons' => [
|
||||
'wordcount',
|
||||
],
|
||||
'menuitems' => [
|
||||
'wordcount' => 'tools',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of the disabled plugins.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
protected function get_disabled_plugins(): array {
|
||||
return [
|
||||
// Disable the image and media plugins.
|
||||
// These are not generally compatible with Moodle.
|
||||
'image',
|
||||
'media',
|
||||
|
||||
// Use the Moodle autosave plugin instead.
|
||||
'autosave',
|
||||
|
||||
// Disable the Template plugin for now.
|
||||
'template',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of the Moodle plugins with their button, and menuitem, configuration.
|
||||
*
|
||||
* @return array[]
|
||||
*/
|
||||
protected function get_moodle_plugins(): array {
|
||||
$plugins = \core_component::get_plugin_list_with_class('tiny', 'plugininfo');
|
||||
|
||||
$pluginconfig = [];
|
||||
foreach ($plugins as $pluginname => $classname) {
|
||||
if (!is_a($classname, plugin::class, true)) {
|
||||
continue;
|
||||
}
|
||||
// Module name => [buttons, menuitems].
|
||||
$pluginconfig["{$pluginname}/plugin"] = $classname::get_plugin_info();
|
||||
}
|
||||
|
||||
return $pluginconfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether premium plugins are configured and enabled.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function premium_plugins_enabled(): bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of the Tiny Premium plugins with their button, and menuitem, configuration.
|
||||
*
|
||||
* Note: This only includes _compatible_ premium plugins.
|
||||
* Some premium plugins *may not* be compatible with Moodle, and some may require additional configuration.
|
||||
*
|
||||
* @return array[]
|
||||
*/
|
||||
protected function get_premium_plugins(): array {
|
||||
return [
|
||||
'a11ycheck' => [
|
||||
'buttons' => [
|
||||
'a11ycheck',
|
||||
],
|
||||
'menuitems' => [
|
||||
'a11ycheck',
|
||||
],
|
||||
],
|
||||
'advcode' => [
|
||||
'buttons' => [
|
||||
'code',
|
||||
],
|
||||
'menuitems' => [
|
||||
'code',
|
||||
],
|
||||
],
|
||||
'footnotes' => [
|
||||
'buttons' => [
|
||||
'footnotes',
|
||||
'footnotesupdate',
|
||||
],
|
||||
'menuitems' => [
|
||||
'footnotes',
|
||||
'footnotesupdate',
|
||||
],
|
||||
],
|
||||
'mergetags' => [
|
||||
'buttons' => [
|
||||
'mergetags',
|
||||
],
|
||||
'menuitems' => [
|
||||
'mergetags',
|
||||
],
|
||||
],
|
||||
'autocorrect' => [
|
||||
'menuitems' => [
|
||||
'autocorrect',
|
||||
'capitalization',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace editor_tiny;
|
||||
|
||||
use context;
|
||||
|
||||
/**
|
||||
* Tiny Editor Plugin class.
|
||||
*
|
||||
* This class must be implemented by any Moodle plugin adding TinyMCE features.
|
||||
*
|
||||
* It should optionally implement the following interfaces:
|
||||
* - plugin_with_buttons: to add buttons to the TinyMCE toolbar
|
||||
* - plugin_with_menuitems
|
||||
* - plugin_with_configuration: to add configuration to the TinyMCE editor
|
||||
*
|
||||
* @package editor_tiny
|
||||
* @copyright 2021 Andrew Lyons <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
abstract class plugin {
|
||||
/**
|
||||
* Whether the plugin is enabled
|
||||
*
|
||||
* @param context $context The context that the editor is used within
|
||||
* @param array $options The options passed in when requesting the editor
|
||||
* @param array $fpoptions The filepicker options passed in when requesting the editor
|
||||
* @param editor $editor The editor instance in which the plugin is initialised
|
||||
* @return boolean
|
||||
*/
|
||||
public static function is_enabled(
|
||||
context $context,
|
||||
array $options,
|
||||
array $fpoptions,
|
||||
?editor $editor = null
|
||||
): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the plugin information for the plugin.
|
||||
*
|
||||
* @param context $context The context that the editor is used within
|
||||
* @param array $options The options passed in when requesting the editor
|
||||
* @param array $fpoptions The filepicker options passed in when requesting the editor
|
||||
* @param editor $editor The editor instance in which the plugin is initialised
|
||||
* @return array
|
||||
*/
|
||||
final public static function get_plugin_info(
|
||||
context $context,
|
||||
array $options,
|
||||
array $fpoptions,
|
||||
?editor $editor = null
|
||||
): array {
|
||||
$plugindata = [];
|
||||
|
||||
if (is_a(static::class, plugin_with_buttons::class, true)) {
|
||||
$plugindata['buttons'] = static::get_available_buttons();
|
||||
}
|
||||
|
||||
if (is_a(static::class, plugin_with_menuitems::class, true)) {
|
||||
$plugindata['menuitems'] = static::get_available_menuitems();
|
||||
}
|
||||
|
||||
if (is_a(static::class, plugin_with_configuration::class, true)) {
|
||||
$plugindata['config'] = static::get_plugin_configuration_for_context($context, $options, $fpoptions, $editor);
|
||||
}
|
||||
|
||||
return $plugindata;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace editor_tiny;
|
||||
|
||||
/**
|
||||
* Tiny Editor.
|
||||
*
|
||||
* @package editor_tiny
|
||||
* @copyright 2021 Andrew Lyons <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
interface plugin_with_buttons {
|
||||
/**
|
||||
* Get a list of the buttons provided by this plugin.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public static function get_available_buttons(): array;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace editor_tiny;
|
||||
|
||||
use context;
|
||||
|
||||
/**
|
||||
* An interface representing a plugin with menu items.
|
||||
*
|
||||
* @package editor_tiny
|
||||
* @copyright 2021 Andrew Lyons <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
interface plugin_with_configuration {
|
||||
/**
|
||||
* Get a list of the menu items provided by this plugin.
|
||||
*
|
||||
* @param context $context The context that the editor is used within
|
||||
* @param array $options The options passed in when requesting the editor
|
||||
* @param array $fpoptions The filepicker options passed in when requesting the editor
|
||||
* @param editor $editor The editor instance in which the plugin is initialised
|
||||
* @return array
|
||||
*/
|
||||
public static function get_plugin_configuration_for_context(
|
||||
context $context,
|
||||
array $options,
|
||||
array $fpoptions,
|
||||
?editor $editor = null
|
||||
): array;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace editor_tiny;
|
||||
|
||||
/**
|
||||
* An interface representing a plugin with menu items.
|
||||
*
|
||||
* @package editor_tiny
|
||||
* @copyright 2021 Andrew Lyons <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
interface plugin_with_menuitems {
|
||||
/**
|
||||
* Get a list of the menu items provided by this plugin.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public static function get_available_menuitems(): array;
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace editor_tiny\plugininfo;
|
||||
|
||||
use moodle_url;
|
||||
|
||||
/**
|
||||
* Subplugin info class.
|
||||
*
|
||||
* @package editor_tiny
|
||||
* @copyright 2022 Andrew Lyons <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class tiny extends \core\plugininfo\base {
|
||||
|
||||
/**
|
||||
* These subplugins can be uninstalled.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_uninstall_allowed(): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return URL used for management of plugins of this type.
|
||||
*
|
||||
* @return moodle_url
|
||||
*/
|
||||
public static function get_manage_url(): moodle_url {
|
||||
return new moodle_url('/admin/settings.php', [
|
||||
'section' => 'editosettingstiny',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Include the settings.php file from subplugins if provided.
|
||||
*
|
||||
* This is a copy of very similar implementations from various other subplugin areas.
|
||||
*
|
||||
* @param \part_of_admin_tree $adminroot
|
||||
* @param string $parentnodename
|
||||
* @param bool $hassiteconfig whether the current user has moodle/site:config capability
|
||||
*/
|
||||
public function load_settings(\part_of_admin_tree $adminroot, $parentnodename, $hassiteconfig): void {
|
||||
// In case settings.php wants to refer to them.
|
||||
global $CFG, $USER, $DB, $OUTPUT, $PAGE;
|
||||
|
||||
$ADMIN = $adminroot; // May be used in settings.php.
|
||||
$plugininfo = $this; // Also can be used inside settings.php.
|
||||
|
||||
if (!$this->is_installed_and_upgraded()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$hassiteconfig || !file_exists($this->full_path('settings.php'))) {
|
||||
return;
|
||||
}
|
||||
|
||||
$section = $this->get_settings_section_name();
|
||||
$settings = new \admin_settingpage(
|
||||
$section,
|
||||
$this->displayname,
|
||||
'moodle/site:config',
|
||||
$this->is_enabled() === false
|
||||
);
|
||||
|
||||
// This may also set $settings to null.
|
||||
include($this->full_path('settings.php'));
|
||||
|
||||
if ($settings) {
|
||||
$ADMIN->add($parentnodename, $settings);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the settings section name.
|
||||
* This is used to get the setting links in the Tiny sub-plugins table.
|
||||
*
|
||||
* @return null|string the settings section name.
|
||||
*/
|
||||
public function get_settings_section_name(): ?string {
|
||||
return "tiny_{$this->name}_settings";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace editor_tiny\privacy;
|
||||
|
||||
/**
|
||||
* Privacy Subsystem implementation for the TinyMCE Editor.
|
||||
*
|
||||
* @package editor_tiny
|
||||
* @copyright 2022 Andrew Lyons <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class provider implements \core_privacy\local\metadata\null_provider {
|
||||
|
||||
/**
|
||||
* Return the langstring identifier for the reason that no privacy provider needs to be implemented for this plugin.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_reason(): string {
|
||||
return 'privacy:reason';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"plugintypes": {
|
||||
"tiny": "lib/editor/tiny/plugins"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
body.mce-content-body {
|
||||
margin: revert;
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
# TinyMCE
|
||||
|
||||
The world's #1 open source rich text editor.
|
||||
|
||||
Used and trusted by millions of developers, TinyMCE is the world’s most customizable, scalable, and flexible rich text editor. We’ve helped launch the likes of Atlassian, Medium, Evernote (and lots more that we can’t tell you), by empowering them to create exceptional content and experiences for their users.
|
||||
|
||||
With more than 350M+ downloads every year, we’re also one of the most trusted enterprise-grade open source HTML editors on the internet. There’s currently more than 100M+ products worldwide, powered by Tiny. As a high powered WYSIWYG editor, TinyMCE is built to scale, designed to innovate, and thrives on delivering results to difficult edge-cases.
|
||||
|
||||
You can access a [full featured demo of TinyMCE](https://www.tiny.cloud/docs/tinymce/6/premium-full-featured/) in the docs on the TinyMCE website.
|
||||
|
||||
<p align="center">
|
||||
<img alt="Screenshot of the TinyMCE Editor" src="https://www.tiny.cloud/storage/github-readme-images/tinymce-editor-6x.png"\>
|
||||
</p>
|
||||
|
||||
## Get started with TinyMCE
|
||||
|
||||
Getting started with the TinyMCE rich text editor is easy, and for simple configurations can be done in less than 5 minutes.
|
||||
|
||||
[TinyMCE Cloud Deployment Quick Start Guide](https://www.tiny.cloud/docs/tinymce/6/cloud-quick-start/)
|
||||
|
||||
[TinyMCE Self-hosted Deployment Guide](https://www.tiny.cloud/docs/tinymce/6/npm-projects/)
|
||||
|
||||
TinyMCE provides a range of configuration options that allow you to integrate it into your application. Start customizing with a [basic setup](https://www.tiny.cloud/docs/tinymce/6/basic-setup/).
|
||||
|
||||
Configure it for one of three modes of editing:
|
||||
|
||||
- [TinyMCE classic editing mode](https://www.tiny.cloud/docs/tinymce/6/use-tinymce-classic/).
|
||||
- [TinyMCE inline editing mode](https://www.tiny.cloud/docs/tinymce/6/use-tinymce-inline/).
|
||||
- [TinyMCE distraction-free editing mode](https://www.tiny.cloud/docs/tinymce/6/use-tinymce-distraction-free/).
|
||||
|
||||
## Features
|
||||
|
||||
### Integration
|
||||
|
||||
TinyMCE is easily integrated into your projects with the help of components such as:
|
||||
|
||||
- [tinymce-react](https://github.com/tinymce/tinymce-react)
|
||||
- [tinymce-vue](https://github.com/tinymce/tinymce-vue)
|
||||
- [tinymce-angular](https://github.com/tinymce/tinymce-angular)
|
||||
|
||||
With over 29 integrations, and 400+ APIs, see the TinyMCE docs for a full list of editor [integrations](https://www.tiny.cloud/docs/tinymce/6/integrations/).
|
||||
|
||||
### Customization
|
||||
|
||||
It is easy to [configure the UI](https://www.tiny.cloud/docs/tinymce/6/customize-ui/) of your rich text editor to match the design of your site, product or application. Due to its flexibility, you can [configure the editor](https://www.tiny.cloud/docs/tinymce/6/basic-setup/) with as much or as little functionality as you like, depending on your requirements.
|
||||
|
||||
With [50+ powerful plugins available](https://www.tiny.cloud/tinymce/features/), and content editable as the basis of TinyMCE, adding additional functionality is as simple as including a single line of code.
|
||||
|
||||
Realizing the full power of most plugins requires only a few lines more.
|
||||
|
||||
### Extensibility
|
||||
|
||||
Sometimes your editor requirements can be quite unique, and you need the freedom and flexibility to innovate. Thanks to TinyMCE being open source, you can view the source code and develop your own extensions for custom functionality to meet your own requirements.
|
||||
|
||||
The TinyMCE [API](https://www.tiny.cloud/docs/tinymce/6/apis/tinymce.root/) is exposed to make it easier for you to write custom functionality that fits within the existing framework of TinyMCE [UI components](https://www.tiny.cloud/docs/tinymce/6/custom-ui-components/).
|
||||
|
||||
### Extended Features and Support
|
||||
|
||||
For the professional software teams that require more in-depth efficiency, compliance or collaborative features built to enterprise-grade standards, please [get in touch with our team](https://www.tiny.cloud/contact/).
|
||||
|
||||
Tiny also offers dedicated SLAs and support for professional development teams.
|
||||
|
||||
## Compiling and contributing
|
||||
|
||||
In 2019 the decision was made to transition our codebase to a monorepo. For information on compiling and contributing, see: [contribution guidelines](https://github.com/tinymce/tinymce/blob/master/CONTRIBUTING.md).
|
||||
|
||||
As an open source product, we encourage and support the active development of our software.
|
||||
|
||||
## Want more information?
|
||||
|
||||
Visit the [TinyMCE website](https://tiny.cloud/) and check out the [TinyMCE documentation](https://www.tiny.cloud/docs/).
|
||||
@@ -0,0 +1,184 @@
|
||||
tinymce.IconManager.add('default', {
|
||||
icons: {
|
||||
'accessibility-check': '<svg width="24" height="24"><path d="M12 2a2 2 0 0 1 2 2 2 2 0 0 1-2 2 2 2 0 0 1-2-2c0-1.1.9-2 2-2Zm8 7h-5v12c0 .6-.4 1-1 1a1 1 0 0 1-1-1v-5c0-.6-.4-1-1-1a1 1 0 0 0-1 1v5c0 .6-.4 1-1 1a1 1 0 0 1-1-1V9H4a1 1 0 1 1 0-2h16c.6 0 1 .4 1 1s-.4 1-1 1Z" fill-rule="nonzero"/></svg>',
|
||||
'action-next': '<svg width="24" height="24"><path fill-rule="nonzero" d="M5.7 7.3a1 1 0 0 0-1.4 1.4l7.7 7.7 7.7-7.7a1 1 0 1 0-1.4-1.4L12 13.6 5.7 7.3Z"/></svg>',
|
||||
'action-prev': '<svg width="24" height="24"><path fill-rule="nonzero" d="M18.3 15.7a1 1 0 0 0 1.4-1.4L12 6.6l-7.7 7.7a1 1 0 0 0 1.4 1.4L12 9.4l6.3 6.3Z"/></svg>',
|
||||
'addtag': '<svg width="24" height="24"><path fill-rule="evenodd" clip-rule="evenodd" d="M15 5a2 2 0 0 1 1.6.8L21 12l-4.4 6.2a2 2 0 0 1-1.6.8h-3v-2h3l3.5-5L15 7H5v3H3V7c0-1.1.9-2 2-2h10Z"/><path fill-rule="evenodd" clip-rule="evenodd" d="M6 12a1 1 0 0 0-1 1v2H3a1 1 0 1 0 0 2h2v2a1 1 0 1 0 2 0v-2h2a1 1 0 1 0 0-2H7v-2c0-.6-.4-1-1-1Z"/></svg>',
|
||||
'align-center': '<svg width="24" height="24"><path d="M5 5h14c.6 0 1 .4 1 1s-.4 1-1 1H5a1 1 0 1 1 0-2Zm3 4h8c.6 0 1 .4 1 1s-.4 1-1 1H8a1 1 0 1 1 0-2Zm0 8h8c.6 0 1 .4 1 1s-.4 1-1 1H8a1 1 0 0 1 0-2Zm-3-4h14c.6 0 1 .4 1 1s-.4 1-1 1H5a1 1 0 0 1 0-2Z" fill-rule="evenodd"/></svg>',
|
||||
'align-justify': '<svg width="24" height="24"><path d="M5 5h14c.6 0 1 .4 1 1s-.4 1-1 1H5a1 1 0 1 1 0-2Zm0 4h14c.6 0 1 .4 1 1s-.4 1-1 1H5a1 1 0 1 1 0-2Zm0 4h14c.6 0 1 .4 1 1s-.4 1-1 1H5a1 1 0 0 1 0-2Zm0 4h14c.6 0 1 .4 1 1s-.4 1-1 1H5a1 1 0 0 1 0-2Z" fill-rule="evenodd"/></svg>',
|
||||
'align-left': '<svg width="24" height="24"><path d="M5 5h14c.6 0 1 .4 1 1s-.4 1-1 1H5a1 1 0 1 1 0-2Zm0 4h8c.6 0 1 .4 1 1s-.4 1-1 1H5a1 1 0 1 1 0-2Zm0 8h8c.6 0 1 .4 1 1s-.4 1-1 1H5a1 1 0 0 1 0-2Zm0-4h14c.6 0 1 .4 1 1s-.4 1-1 1H5a1 1 0 0 1 0-2Z" fill-rule="evenodd"/></svg>',
|
||||
'align-none': '<svg width="24" height="24"><path d="M14.2 5 13 7H5a1 1 0 1 1 0-2h9.2Zm4 0h.8a1 1 0 0 1 0 2h-2l1.2-2Zm-6.4 4-1.2 2H5a1 1 0 0 1 0-2h6.8Zm4 0H19a1 1 0 0 1 0 2h-4.4l1.2-2Zm-6.4 4-1.2 2H5a1 1 0 0 1 0-2h4.4Zm4 0H19a1 1 0 0 1 0 2h-6.8l1.2-2ZM7 17l-1.2 2H5a1 1 0 0 1 0-2h2Zm4 0h8a1 1 0 0 1 0 2H9.8l1.2-2Zm5.2-13.5 1.3.7-9.7 16.3-1.3-.7 9.7-16.3Z" fill-rule="evenodd"/></svg>',
|
||||
'align-right': '<svg width="24" height="24"><path d="M5 5h14c.6 0 1 .4 1 1s-.4 1-1 1H5a1 1 0 1 1 0-2Zm6 4h8c.6 0 1 .4 1 1s-.4 1-1 1h-8a1 1 0 0 1 0-2Zm0 8h8c.6 0 1 .4 1 1s-.4 1-1 1h-8a1 1 0 0 1 0-2Zm-6-4h14c.6 0 1 .4 1 1s-.4 1-1 1H5a1 1 0 0 1 0-2Z" fill-rule="evenodd"/></svg>',
|
||||
'arrow-left': '<svg width="24" height="24"><path d="m5.6 13 12 6a1 1 0 0 0 1.4-1V6a1 1 0 0 0-1.4-.9l-12 6a1 1 0 0 0 0 1.8Z" fill-rule="evenodd"/></svg>',
|
||||
'arrow-right': '<svg width="24" height="24"><path d="m18.5 13-12 6A1 1 0 0 1 5 18V6a1 1 0 0 1 1.4-.9l12 6a1 1 0 0 1 0 1.8Z" fill-rule="evenodd"/></svg>',
|
||||
'bold': '<svg width="24" height="24"><path d="M7.8 19c-.3 0-.5 0-.6-.2l-.2-.5V5.7c0-.2 0-.4.2-.5l.6-.2h5c1.5 0 2.7.3 3.5 1 .7.6 1.1 1.4 1.1 2.5a3 3 0 0 1-.6 1.9c-.4.6-1 1-1.6 1.2.4.1.9.3 1.3.6s.8.7 1 1.2c.4.4.5 1 .5 1.6 0 1.3-.4 2.3-1.3 3-.8.7-2.1 1-3.8 1H7.8Zm5-8.3c.6 0 1.2-.1 1.6-.5.4-.3.6-.7.6-1.3 0-1.1-.8-1.7-2.3-1.7H9.3v3.5h3.4Zm.5 6c.7 0 1.3-.1 1.7-.4.4-.4.6-.9.6-1.5s-.2-1-.7-1.4c-.4-.3-1-.4-2-.4H9.4v3.8h4Z" fill-rule="evenodd"/></svg>',
|
||||
'bookmark': '<svg width="24" height="24"><path d="M6 4v17l6-4 6 4V4c0-.6-.4-1-1-1H7a1 1 0 0 0-1 1Z" fill-rule="nonzero"/></svg>',
|
||||
'border-style': '<svg width="24" height="24"><g fill-rule="evenodd"><rect width="18" height="2" x="3" y="6" rx="1"/><rect width="2.8" height="2" x="3" y="16" rx="1"/><rect width="2.8" height="2" x="6.8" y="16" rx="1"/><rect width="2.8" height="2" x="10.6" y="16" rx="1"/><rect width="2.8" height="2" x="14.4" y="16" rx="1"/><rect width="2.8" height="2" x="18.2" y="16" rx="1"/><rect width="8" height="2" x="3" y="11" rx="1"/><rect width="8" height="2" x="13" y="11" rx="1"/></g></svg>',
|
||||
'border-width': '<svg width="24" height="24"><g fill-rule="evenodd"><rect width="18" height="5" x="3" y="5" rx="1"/><rect width="18" height="3.5" x="3" y="11.5" rx="1"/><rect width="18" height="2" x="3" y="17" rx="1"/></g></svg>',
|
||||
'brightness': '<svg width="24" height="24"><path d="M12 17c.3 0 .5.1.7.3.2.2.3.4.3.7v1c0 .3-.1.5-.3.7a1 1 0 0 1-.7.3 1 1 0 0 1-.7-.3 1 1 0 0 1-.3-.7v-1c0-.3.1-.5.3-.7.2-.2.4-.3.7-.3Zm0-10a1 1 0 0 1-.7-.3A1 1 0 0 1 11 6V5c0-.3.1-.5.3-.7.2-.2.4-.3.7-.3.3 0 .5.1.7.3.2.2.3.4.3.7v1c0 .3-.1.5-.3.7a1 1 0 0 1-.7.3Zm7 4c.3 0 .5.1.7.3.2.2.3.4.3.7 0 .3-.1.5-.3.7a1 1 0 0 1-.7.3h-1a1 1 0 0 1-.7-.3 1 1 0 0 1-.3-.7c0-.3.1-.5.3-.7.2-.2.4-.3.7-.3h1ZM7 12c0 .3-.1.5-.3.7a1 1 0 0 1-.7.3H5a1 1 0 0 1-.7-.3A1 1 0 0 1 4 12c0-.3.1-.5.3-.7.2-.2.4-.3.7-.3h1c.3 0 .5.1.7.3.2.2.3.4.3.7Zm10 3.5.7.8c.2.1.3.4.3.6 0 .3-.1.6-.3.8a1 1 0 0 1-.8.3 1 1 0 0 1-.6-.3l-.8-.7a1 1 0 0 1-.3-.8c0-.2.1-.5.3-.7a1 1 0 0 1 1.4 0Zm-10-7-.7-.8a1 1 0 0 1-.3-.6c0-.3.1-.6.3-.8.2-.2.5-.3.8-.3.2 0 .5.1.7.3l.7.7c.2.2.3.5.3.8 0 .2-.1.5-.3.7a1 1 0 0 1-.7.3 1 1 0 0 1-.8-.3Zm10 0a1 1 0 0 1-.8.3 1 1 0 0 1-.7-.3 1 1 0 0 1-.3-.7c0-.3.1-.6.3-.8l.8-.7c.1-.2.4-.3.6-.3.3 0 .6.1.8.3.2.2.3.5.3.8 0 .2-.1.5-.3.7l-.7.7Zm-10 7c.2-.2.5-.3.8-.3.2 0 .5.1.7.3a1 1 0 0 1 0 1.4l-.8.8a1 1 0 0 1-.6.3 1 1 0 0 1-.8-.3 1 1 0 0 1-.3-.8c0-.2.1-.5.3-.6l.7-.8ZM12 8a4 4 0 0 1 3.7 2.4 4 4 0 0 1 0 3.2A4 4 0 0 1 12 16a4 4 0 0 1-3.7-2.4 4 4 0 0 1 0-3.2A4 4 0 0 1 12 8Zm0 6.5c.7 0 1.3-.2 1.8-.7.5-.5.7-1.1.7-1.8s-.2-1.3-.7-1.8c-.5-.5-1.1-.7-1.8-.7s-1.3.2-1.8.7c-.5.5-.7 1.1-.7 1.8s.2 1.3.7 1.8c.5.5 1.1.7 1.8.7Z" fill-rule="evenodd"/></svg>',
|
||||
'browse': '<svg width="24" height="24"><path d="M19 4a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2h-4v-2h4V8H5v10h4v2H5a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2h14Zm-8 9.4-2.3 2.3a1 1 0 1 1-1.4-1.4l4-4a1 1 0 0 1 1.4 0l4 4a1 1 0 0 1-1.4 1.4L13 13.4V20a1 1 0 0 1-2 0v-6.6Z" fill-rule="nonzero"/></svg>',
|
||||
'cancel': '<svg width="24" height="24"><path d="M12 4.6a7.4 7.4 0 1 1 0 14.8 7.4 7.4 0 0 1 0-14.8ZM12 3a9 9 0 1 0 0 18 9 9 0 0 0 0-18Zm0 8L14.8 8l1 1.1-2.7 2.8 2.7 2.7-1.1 1.1-2.7-2.7-2.7 2.7-1-1.1 2.6-2.7-2.7-2.7 1-1.1 2.8 2.7Z" fill-rule="nonzero"/></svg>',
|
||||
'cell-background-color': '<svg width="24" height="24"><path d="m15.7 2 1.6 1.6-2.7 2.6 5.9 5.8c.7.7.7 1.7 0 2.4l-6.3 6.1a1.7 1.7 0 0 1-2.4 0l-6.3-6.1c-.7-.7-.7-1.7 0-2.4L15.7 2ZM18 12l-4.5-4L9 12h9ZM4 16s2 2.4 2 3.8C6 21 5.1 22 4 22s-2-1-2-2.2C2 18.4 4 16 4 16Z"/></svg>',
|
||||
'cell-border-color': '<svg width="24" height="24"><g fill-rule="evenodd"><path fill-rule="nonzero" d="M5 13v5h2v2H5a2 2 0 0 1-2-2v-5h2zm8-7V4h6a2 2 0 0 1 2 2h-8z" opacity=".2"/><path fill-rule="nonzero" d="M13 4v2H5v7H3V6c0-1.1.9-2 2-2h8zm-2.6 14.1.1-.1.1.1.2.3.2.2.2.2c.4.6.8 1.2.8 1.7 0 .8-.7 1.5-1.5 1.5S9 21.3 9 20.5c0-.5.4-1.1.8-1.7l.2-.2.2-.2.2-.3z"/><path d="m13 11-2 2H5v-2h6V6h2z"/><path fill-rule="nonzero" d="m18.4 8 1 1-1.8 1.9 4 4c.5.4.5 1.1 0 1.6l-4.3 4.2a1.2 1.2 0 0 1-1.6 0l-4.4-4.2c-.4-.5-.4-1.2 0-1.7l7-6.8Zm1.6 7-3-3-3 3h6Z"/></g></svg>',
|
||||
'change-case': '<svg width="24" height="24"><path d="M18.4 18.2v-.6c-.5.8-1.3 1.2-2.4 1.2-2.2 0-3.3-1.6-3.3-4.8 0-3.1 1-4.7 3.3-4.7 1.1 0 1.8.3 2.4 1.1v-.6c0-.5.4-.8.8-.8s.8.3.8.8v8.4c0 .5-.4.8-.8.8a.8.8 0 0 1-.8-.8zm-2-7.4c-1.3 0-1.8.9-1.8 3.2 0 2.4.5 3.3 1.7 3.3 1.3 0 1.8-.9 1.8-3.2 0-2.4-.5-3.3-1.7-3.3zM10 15.7H5.5l-.8 2.6a1 1 0 0 1-1 .7h-.2a.7.7 0 0 1-.7-1l4-12a1 1 0 0 1 2 0l4 12a.7.7 0 0 1-.8 1h-.2a1 1 0 0 1-1-.7l-.8-2.6zm-.3-1.5-2-6.5-1.9 6.5h3.9z" fill-rule="evenodd"/></svg>',
|
||||
'character-count': '<svg width="24" height="24"><path d="M4 11.5h16v1H4v-1Zm4.8-6.8V10H7.7V5.8h-1v-1h2ZM11 8.3V9h2v1h-3V7.7l2-1v-.9h-2v-1h3v2.4l-2 1Zm6.3-3.4V10h-3.1V9h2.1V8h-2.1V6.8h2.1v-1h-2.1v-1h3.1ZM5.8 16.4c0-.5.2-.8.5-1 .2-.2.6-.3 1.2-.3l.8.1c.2 0 .4.2.5.3l.4.4v2.8l.2.3H8.2V18.7l-.6.3H7c-.4 0-.7 0-1-.2a1 1 0 0 1-.3-.9c0-.3 0-.6.3-.8.3-.2.7-.4 1.2-.4l.6-.2h.3v-.2l-.1-.2a.8.8 0 0 0-.5-.1 1 1 0 0 0-.4 0l-.3.4h-1Zm2.3.8h-.2l-.2.1-.4.1a1 1 0 0 0-.4.2l-.2.2.1.3.5.1h.4l.4-.4v-.6Zm2-3.4h1.2v1.7l.5-.3h.5c.5 0 .9.1 1.2.5.3.4.5.8.5 1.4 0 .6-.2 1.1-.5 1.5-.3.4-.7.6-1.3.6l-.6-.1-.4-.4v.4h-1.1v-5.4Zm1.1 3.3c0 .3 0 .6.2.8a.7.7 0 0 0 1.2 0l.2-.8c0-.4 0-.6-.2-.8a.7.7 0 0 0-.6-.3l-.6.3-.2.8Zm6.1-.5c0-.2 0-.3-.2-.4a.8.8 0 0 0-.5-.2c-.3 0-.5.1-.6.3l-.2.9c0 .3 0 .6.2.8.1.2.3.3.6.3.2 0 .4 0 .5-.2l.2-.4h1.1c0 .5-.3.8-.6 1.1a2 2 0 0 1-1.3.4c-.5 0-1-.2-1.3-.6a2 2 0 0 1-.5-1.4c0-.6.1-1.1.5-1.5.3-.4.8-.5 1.4-.5.5 0 1 0 1.2.3.4.3.5.7.5 1.2h-1v-.1Z" fill-rule="evenodd"/></svg>',
|
||||
'checklist-rtl': '<svg width="24" height="24"><path d="M5 17h8c.6 0 1 .4 1 1s-.4 1-1 1H5a1 1 0 0 1 0-2zm0-6h8c.6 0 1 .4 1 1s-.4 1-1 1H5a1 1 0 0 1 0-2zm0-6h8c.6 0 1 .4 1 1s-.4 1-1 1H5a1 1 0 1 1 0-2zm14.2 11c.2-.4.6-.5.9-.3.3.2.4.6.2 1L18 20c-.2.3-.7.4-1 0l-1.3-1.3a.7.7 0 0 1 0-1c.3-.2.7-.2 1 0l.7.9 1.7-2.8zm0-6c.2-.4.6-.5.9-.3.3.2.4.6.2 1L18 14c-.2.3-.7.4-1 0l-1.3-1.3a.7.7 0 0 1 0-1c.3-.2.7-.2 1 0l.7.9 1.7-2.8zm0-6c.2-.4.6-.5.9-.3.3.2.4.6.2 1L18 8c-.2.3-.7.4-1 0l-1.3-1.3a.7.7 0 0 1 0-1c.3-.2.7-.2 1 0l.7.9 1.7-2.8z" fill-rule="evenodd"/></svg>',
|
||||
'checklist': '<svg width="24" height="24"><path d="M11 17h8c.6 0 1 .4 1 1s-.4 1-1 1h-8a1 1 0 0 1 0-2Zm0-6h8c.6 0 1 .4 1 1s-.4 1-1 1h-8a1 1 0 0 1 0-2Zm0-6h8a1 1 0 0 1 0 2h-8a1 1 0 0 1 0-2ZM7.2 16c.2-.4.6-.5.9-.3.3.2.4.6.2 1L6 20c-.2.3-.7.4-1 0l-1.3-1.3a.7.7 0 0 1 0-1c.3-.2.7-.2 1 0l.7.9 1.7-2.8Zm0-6c.2-.4.6-.5.9-.3.3.2.4.6.2 1L6 14c-.2.3-.7.4-1 0l-1.3-1.3a.7.7 0 0 1 0-1c.3-.2.7-.2 1 0l.7.9 1.7-2.8Zm0-6c.2-.4.6-.5.9-.3.3.2.4.6.2 1L6 8c-.2.3-.7.4-1 0L3.8 6.9a.7.7 0 0 1 0-1c.3-.2.7-.2 1 0l.7.9 1.7-2.8Z" fill-rule="evenodd"/></svg>',
|
||||
'checkmark': '<svg width="24" height="24"><path d="M18.2 5.4a1 1 0 0 1 1.6 1.2l-8 12a1 1 0 0 1-1.5.1l-5-5a1 1 0 1 1 1.4-1.4l4.1 4.1 7.4-11Z" fill-rule="nonzero"/></svg>',
|
||||
'chevron-down': '<svg width="10" height="10"><path d="M8.7 2.2c.3-.3.8-.3 1 0 .4.4.4.9 0 1.2L5.7 7.8c-.3.3-.9.3-1.2 0L.2 3.4a.8.8 0 0 1 0-1.2c.3-.3.8-.3 1.1 0L5 6l3.7-3.8Z" fill-rule="nonzero"/></svg>',
|
||||
'chevron-left': '<svg width="10" height="10"><path d="M7.8 1.3 4 5l3.8 3.7c.3.3.3.8 0 1-.4.4-.9.4-1.2 0L2.2 5.7a.8.8 0 0 1 0-1.2L6.6.2C7 0 7.4 0 7.8.2c.3.3.3.8 0 1.1Z" fill-rule="nonzero"/></svg>',
|
||||
'chevron-right': '<svg width="10" height="10"><path d="M2.2 1.3a.8.8 0 0 1 0-1c.4-.4.9-.4 1.2 0l4.4 4.1c.3.4.3.9 0 1.2L3.4 9.8c-.3.3-.8.3-1.2 0a.8.8 0 0 1 0-1.1L6 5 2.2 1.3Z" fill-rule="nonzero"/></svg>',
|
||||
'chevron-up': '<svg width="10" height="10"><path d="M8.7 7.8 5 4 1.3 7.8c-.3.3-.8.3-1 0a.8.8 0 0 1 0-1.2l4.1-4.4c.3-.3.9-.3 1.2 0l4.2 4.4c.3.3.3.9 0 1.2-.3.3-.8.3-1.1 0Z" fill-rule="nonzero"/></svg>',
|
||||
'close': '<svg width="24" height="24"><path d="M17.3 8.2 13.4 12l3.9 3.8a1 1 0 0 1-1.5 1.5L12 13.4l-3.8 3.9a1 1 0 0 1-1.5-1.5l3.9-3.8-3.9-3.8a1 1 0 0 1 1.5-1.5l3.8 3.9 3.8-3.9a1 1 0 0 1 1.5 1.5Z" fill-rule="evenodd"/></svg>',
|
||||
'code-sample': '<svg width="24" height="26"><path d="M7.1 11a2.8 2.8 0 0 1-.8 2 2.8 2.8 0 0 1 .8 2v1.7c0 .3.1.6.4.8.2.3.5.4.8.4.3 0 .4.2.4.4v.8c0 .2-.1.4-.4.4-.7 0-1.4-.3-2-.8-.5-.6-.8-1.3-.8-2V15c0-.3-.1-.6-.4-.8-.2-.3-.5-.4-.8-.4a.4.4 0 0 1-.4-.4v-.8c0-.2.2-.4.4-.4.3 0 .6-.1.8-.4.3-.2.4-.5.4-.8V9.3c0-.7.3-1.4.8-2 .6-.5 1.3-.8 2-.8.3 0 .4.2.4.4v.8c0 .2-.1.4-.4.4-.3 0-.6.1-.8.4-.3.2-.4.5-.4.8V11Zm9.8 0V9.3c0-.3-.1-.6-.4-.8-.2-.3-.5-.4-.8-.4a.4.4 0 0 1-.4-.4V7c0-.2.1-.4.4-.4.7 0 1.4.3 2 .8.5.6.8 1.3.8 2V11c0 .3.1.6.4.8.2.3.5.4.8.4.2 0 .4.2.4.4v.8c0 .2-.2.4-.4.4-.3 0-.6.1-.8.4-.3.2-.4.5-.4.8v1.7c0 .7-.3 1.4-.8 2-.6.5-1.3.8-2 .8a.4.4 0 0 1-.4-.4v-.8c0-.2.1-.4.4-.4.3 0 .6-.1.8-.4.3-.2.4-.5.4-.8V15a2.8 2.8 0 0 1 .8-2 2.8 2.8 0 0 1-.8-2Zm-3.3-.4c0 .4-.1.8-.5 1.1-.3.3-.7.5-1.1.5-.4 0-.8-.2-1.1-.5-.4-.3-.5-.7-.5-1.1 0-.5.1-.9.5-1.2.3-.3.7-.4 1.1-.4.4 0 .8.1 1.1.4.4.3.5.7.5 1.2ZM12 13c.4 0 .8.1 1.1.5.4.3.5.7.5 1.1 0 1-.1 1.6-.5 2a3 3 0 0 1-1.1 1c-.4.3-.8.4-1.1.4a.5.5 0 0 1-.5-.5V17a3 3 0 0 0 1-.2l.6-.6c-.6 0-1-.2-1.3-.5-.2-.3-.3-.7-.3-1 0-.5.1-1 .5-1.2.3-.4.7-.5 1.1-.5Z" fill-rule="evenodd"/></svg>',
|
||||
'color-levels': '<svg width="24" height="24"><path d="M17.5 11.4A9 9 0 0 1 18 14c0 .5 0 1-.2 1.4 0 .4-.3.9-.5 1.3a6.2 6.2 0 0 1-3.7 3 5.7 5.7 0 0 1-3.2 0A5.9 5.9 0 0 1 7.6 18a6.2 6.2 0 0 1-1.4-2.6 6.7 6.7 0 0 1 0-2.8c0-.4.1-.9.3-1.3a13.6 13.6 0 0 1 2.3-4A20 20 0 0 1 12 4a26.4 26.4 0 0 1 3.2 3.4 18.2 18.2 0 0 1 2.3 4Zm-2 4.5c.4-.7.5-1.4.5-2a7.3 7.3 0 0 0-1-3.2c.2.6.2 1.2.2 1.9a4.5 4.5 0 0 1-1.3 3 5.3 5.3 0 0 1-2.3 1.5 4.9 4.9 0 0 1-2 .1 4.3 4.3 0 0 0 2.4.8 4 4 0 0 0 2-.6 4 4 0 0 0 1.5-1.5Z" fill-rule="evenodd"/></svg>',
|
||||
'color-picker': '<svg width="24" height="24"><path d="M12 3a9 9 0 0 0 0 18 1.5 1.5 0 0 0 1.1-2.5c-.2-.3-.4-.6-.4-1 0-.8.7-1.5 1.5-1.5H16a5 5 0 0 0 5-5c0-4.4-4-8-9-8Zm-5.5 9a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3Zm3-4a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3Zm5 0a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3Zm3 4a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3Z" fill-rule="nonzero"/></svg>',
|
||||
'color-swatch-remove-color': '<svg width="24" height="24"><path stroke="#000" stroke-width="2" d="M21 3 3 21" fill-rule="evenodd"/></svg>',
|
||||
'color-swatch': '<svg width="24" height="24"><rect x="3" y="3" width="18" height="18" rx="1" fill-rule="evenodd"/></svg>',
|
||||
'comment-add': '<svg width="24" height="24"><g fill-rule="nonzero"><path d="m9 19 3-2h7c.6 0 1-.4 1-1V6c0-.6-.4-1-1-1H5a1 1 0 0 0-1 1v10c0 .6.4 1 1 1h4v2Zm-2 4v-4H5a3 3 0 0 1-3-3V6a3 3 0 0 1 3-3h14a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3h-6.4L7 23Z"/><path d="M13 10h2a1 1 0 0 1 0 2h-2v2a1 1 0 0 1-2 0v-2H9a1 1 0 0 1 0-2h2V8a1 1 0 0 1 2 0v2Z"/></g></svg>',
|
||||
'comment': '<svg width="24" height="24"><path fill-rule="nonzero" d="m9 19 3-2h7c.6 0 1-.4 1-1V6c0-.6-.4-1-1-1H5a1 1 0 0 0-1 1v10c0 .6.4 1 1 1h4v2Zm-2 4v-4H5a3 3 0 0 1-3-3V6a3 3 0 0 1 3-3h14a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3h-6.4L7 23Z"/></svg>',
|
||||
'contrast': '<svg width="24" height="24"><path d="M12 4a7.8 7.8 0 0 1 5.7 2.3A8 8 0 1 1 12 4Zm-6 8a6 6 0 0 0 6 6V6a6 6 0 0 0-6 6Z" fill-rule="evenodd"/></svg>',
|
||||
'copy': '<svg width="24" height="24"><path d="M16 3H6a2 2 0 0 0-2 2v11h2V5h10V3Zm1 4a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2h-7a2 2 0 0 1-2-2V9c0-1.2.9-2 2-2h7Zm0 12V9h-7v10h7Z" fill-rule="nonzero"/></svg>',
|
||||
'crop': '<svg width="24" height="24"><path d="M17 8v7h2c.6 0 1 .4 1 1s-.4 1-1 1h-2v2c0 .6-.4 1-1 1a1 1 0 0 1-1-1v-2H7V9H5a1 1 0 1 1 0-2h2V5c0-.6.4-1 1-1s1 .4 1 1v2h7l3-3 1 1-3 3ZM9 9v5l5-5H9Zm1 6h5v-5l-5 5Z" fill-rule="evenodd"/></svg>',
|
||||
'cut-column': '<svg width="24" height="24"><path fill-rule="evenodd" d="M7.2 4.5c.9 0 1.6.4 2.2 1A3.7 3.7 0 0 1 10.5 8v.5l1 1 4-4 1-.5a3.3 3.3 0 0 1 2 0c.4 0 .7.3 1 .5L17 8h4v13h-6V10l-1.5 1.5.5.5v4l-2.5-2.5-1 1v.5c0 .4 0 .8-.3 1.2-.2.5-.4.9-.8 1.2-.6.7-1.3 1-2.2 1-.8.2-1.5 0-2-.6l-.5-.8-.2-1c0-.4 0-.8.3-1.2A3.9 3.9 0 0 1 7 12.7c.5-.2 1-.3 1.5-.2l1-1-1-1c-.5 0-1 0-1.5-.2-.5-.1-1-.4-1.4-.9-.4-.3-.6-.7-.8-1.2L4.5 7c0-.4 0-.7.2-1 0-.3.3-.6.5-.8.5-.5 1.2-.8 2-.7Zm12.3 5h-3v10h3v-10ZM8 13.8h-.3l-.4.2a2.8 2.8 0 0 0-.7.4v.1a2.8 2.8 0 0 0-.6.8l-.1.4v.7l.2.5.5.2h.7a2.6 2.6 0 0 0 .8-.3 2.4 2.4 0 0 0 .7-.7 2.5 2.5 0 0 0 .3-.8 1.5 1.5 0 0 0 0-.8 1 1 0 0 0-.2-.4 1 1 0 0 0-.5-.2H8Zm3.5-3.7c-.4 0-.7.1-1 .4-.3.3-.4.6-.4 1s.1.7.4 1c.3.3.6.4 1 .4s.7-.1 1-.4c.3-.3.4-.6.4-1s-.1-.7-.4-1c-.3-.3-.6-.4-1-.4ZM7 5.8h-.4a1 1 0 0 0-.5.3 1 1 0 0 0-.2.5v.7a2.5 2.5 0 0 0 .3.8l.2.3h.1l.4.4.4.2.4.1h.7L9 9l.2-.4a1.6 1.6 0 0 0 0-.8 2.6 2.6 0 0 0-.3-.8A2.5 2.5 0 0 0 7.7 6l-.4-.1H7Z"/></svg>',
|
||||
'cut-row': '<svg width="24" height="24"><path fill-rule="evenodd" d="M22 3v5H9l3 3 2-2h4l-4 4 1 1h.5c.4 0 .8 0 1.2.3.5.2.9.4 1.2.8.7.6 1 1.3 1 2.2.2.8 0 1.5-.6 2l-.8.5-1 .2c-.4 0-.8 0-1.2-.3a3.9 3.9 0 0 1-2.1-2.2c-.2-.5-.3-1-.2-1.5l-1-1-1 1c0 .5 0 1-.2 1.5-.1.5-.4 1-.9 1.4-.3.4-.7.6-1.2.8l-1.2.3c-.4 0-.7 0-1-.2-.3 0-.6-.3-.8-.5-.5-.5-.8-1.2-.7-2 0-.9.4-1.6 1-2.2A3.7 3.7 0 0 1 8.6 14H9l1-1-4-4-.5-1a3.3 3.3 0 0 1 0-2c0-.4.3-.7.5-1l2 2V3h14ZM8.5 15.3h-.3a2.6 2.6 0 0 0-.8.4 2.5 2.5 0 0 0-.9 1.1l-.1.4v.7l.2.5.5.2h.7a2.5 2.5 0 0 0 .8-.3L9 18V18l.4-.4.2-.4.1-.4v-.7a1 1 0 0 0-.2-.5 1 1 0 0 0-.4-.2h-.5Zm7 0H15a1 1 0 0 0-.4.3 1 1 0 0 0-.2.5 1.5 1.5 0 0 0 0 .7v.4a2.8 2.8 0 0 0 .5.7h.1a2.8 2.8 0 0 0 .8.6l.4.1h.7l.5-.2.2-.5v-.7a2.6 2.6 0 0 0-.3-.8 2.4 2.4 0 0 0-.7-.7 2.5 2.5 0 0 0-.8-.3h-.3ZM12 11.6c-.4 0-.7.1-1 .4-.3.3-.4.6-.4 1s.1.7.4 1c.3.3.6.4 1 .4s.7-.1 1-.4c.3-.3.4-.6.4-1s-.1-.7-.4-1c-.3-.3-.6-.4-1-.4Zm8.5-7.1h-11v2h11v-2Z"/></svg>',
|
||||
'cut': '<svg width="24" height="24"><path d="M18 15c.6.7 1 1.4 1 2.3 0 .8-.2 1.5-.7 2l-.8.5-1 .2c-.4 0-.8 0-1.2-.3a3.9 3.9 0 0 1-2.1-2.2c-.2-.5-.3-1-.2-1.5l-1-1-1 1c0 .5 0 1-.2 1.5-.1.5-.4 1-.9 1.4-.3.4-.7.6-1.2.8l-1.2.3c-.4 0-.7 0-1-.2-.3 0-.6-.3-.8-.5-.5-.5-.8-1.2-.7-2 0-.9.4-1.6 1-2.2A3.7 3.7 0 0 1 8.6 14H9l1-1-4-4-.5-1a3.3 3.3 0 0 1 0-2c0-.4.3-.7.5-1l6 6 6-6 .5 1a3.3 3.3 0 0 1 0 2c0 .4-.3.7-.5 1l-4 4 1 1h.5c.4 0 .8 0 1.2.3.5.2.9.4 1.2.8Zm-8.5 2.2.1-.4v-.7a1 1 0 0 0-.2-.5 1 1 0 0 0-.4-.2 1.6 1.6 0 0 0-.8 0 2.6 2.6 0 0 0-.8.3 2.5 2.5 0 0 0-.9 1.1l-.1.4v.7l.2.5.5.2h.7a2.5 2.5 0 0 0 .8-.3 2.8 2.8 0 0 0 1-1Zm2.5-2.8c.4 0 .7-.1 1-.4.3-.3.4-.6.4-1s-.1-.7-.4-1c-.3-.3-.6-.4-1-.4s-.7.1-1 .4c-.3.3-.4.6-.4 1s.1.7.4 1c.3.3.6.4 1 .4Zm5.4 4 .2-.5v-.7a2.6 2.6 0 0 0-.3-.8 2.4 2.4 0 0 0-.7-.7 2.5 2.5 0 0 0-.8-.3 1.5 1.5 0 0 0-.8 0 1 1 0 0 0-.4.2 1 1 0 0 0-.2.5 1.5 1.5 0 0 0 0 .7v.4l.3.4.3.4a2.8 2.8 0 0 0 .8.5l.4.1h.7l.5-.2Z" fill-rule="evenodd"/></svg>',
|
||||
'document-properties': '<svg width="24" height="24"><path d="M14.4 3H7a2 2 0 0 0-2 2v14c0 1.1.9 2 2 2h10a2 2 0 0 0 2-2V7.6L14.4 3ZM17 19H7V5h6v4h4v10Z" fill-rule="nonzero"/></svg>',
|
||||
'drag': '<svg width="24" height="24"><path d="M13 5h2v2h-2V5Zm0 4h2v2h-2V9ZM9 9h2v2H9V9Zm4 4h2v2h-2v-2Zm-4 0h2v2H9v-2Zm0 4h2v2H9v-2Zm4 0h2v2h-2v-2ZM9 5h2v2H9V5Z" fill-rule="evenodd"/></svg>',
|
||||
'duplicate-column': '<svg width="24" height="24"><path d="M17 6v16h-7V6h7Zm-2 2h-3v12h3V8Zm-2-6v2H8v15H6V2h7Z"/></svg>',
|
||||
'duplicate-row': '<svg width="24" height="24"><path d="M22 11v7H6v-7h16Zm-2 2H8v3h12v-3Zm-1-6v2H4v5H2V7h17Z"/></svg>',
|
||||
'duplicate': '<svg width="24" height="24"><g fill-rule="nonzero"><path d="M16 3v2H6v11H4V5c0-1.1.9-2 2-2h10Zm3 8h-2V9h-7v10h9a2 2 0 0 1-2 2h-7a2 2 0 0 1-2-2V9c0-1.2.9-2 2-2h7a2 2 0 0 1 2 2v2Z"/><path d="M17 14h1a1 1 0 0 1 0 2h-1v1a1 1 0 0 1-2 0v-1h-1a1 1 0 0 1 0-2h1v-1a1 1 0 0 1 2 0v1Z"/></g></svg>',
|
||||
'edit-block': '<svg width="24" height="24"><path fill-rule="nonzero" d="m19.8 8.8-9.4 9.4c-.2.2-.5.4-.9.4l-5.4 1.2 1.2-5.4.5-.8 9.4-9.4c.7-.7 1.8-.7 2.5 0l2.1 2.1c.7.7.7 1.8 0 2.5Zm-2-.2 1-.9v-.3l-2.2-2.2a.3.3 0 0 0-.3 0l-1 1L18 8.5Zm-1 1-2.5-2.4-6 6 2.5 2.5 6-6Zm-7 7.1-2.6-2.4-.3.3-.1.2-.7 3 3.1-.6h.1l.4-.5Z"/></svg>',
|
||||
'edit-image': '<svg width="24" height="24"><path d="M18 16h2V7a2 2 0 0 0-2-2H7v2h11v9ZM6 17h15a1 1 0 0 1 0 2h-1v1a1 1 0 0 1-2 0v-1H6a2 2 0 0 1-2-2V7H3a1 1 0 1 1 0-2h1V4a1 1 0 1 1 2 0v13Zm3-5.3 1.3 2 3-4.7 3.7 6H7l2-3.3Z" fill-rule="nonzero"/></svg>',
|
||||
'embed-page': '<svg width="24" height="24"><path d="M19 6V5H5v14h2A13 13 0 0 1 19 6Zm0 1.4c-.8.8-1.6 2.4-2.2 4.6H19V7.4Zm0 5.6h-2.4c-.4 1.8-.6 3.8-.6 6h3v-6Zm-4 6c0-2.2.2-4.2.6-6H13c-.7 1.8-1.1 3.8-1.1 6h3Zm-4 0c0-2.2.4-4.2 1-6H9.6A12 12 0 0 0 8 19h3ZM4 3h16c.6 0 1 .4 1 1v16c0 .6-.4 1-1 1H4a1 1 0 0 1-1-1V4c0-.6.4-1 1-1Zm11.8 9c.4-1.9 1-3.4 1.8-4.5a9.2 9.2 0 0 0-4 4.5h2.2Zm-3.4 0a12 12 0 0 1 2.8-4 12 12 0 0 0-5 4h2.2Z" fill-rule="nonzero"/></svg>',
|
||||
'embed': '<svg width="24" height="24"><path d="M4 3h16c.6 0 1 .4 1 1v16c0 .6-.4 1-1 1H4a1 1 0 0 1-1-1V4c0-.6.4-1 1-1Zm1 2v14h14V5H5Zm4.8 2.6 5.6 4a.5.5 0 0 1 0 .8l-5.6 4A.5.5 0 0 1 9 16V8a.5.5 0 0 1 .8-.4Z" fill-rule="nonzero"/></svg>',
|
||||
'emoji': '<svg width="24" height="24"><path d="M9 11c.6 0 1-.4 1-1s-.4-1-1-1a1 1 0 0 0-1 1c0 .6.4 1 1 1Zm6 0c.6 0 1-.4 1-1s-.4-1-1-1a1 1 0 0 0-1 1c0 .6.4 1 1 1Zm-3 5.5c2.1 0 4-1.5 4.4-3.5H7.6c.5 2 2.3 3.5 4.4 3.5ZM12 4a8 8 0 1 0 0 16 8 8 0 0 0 0-16Zm0 14.5a6.5 6.5 0 1 1 0-13 6.5 6.5 0 0 1 0 13Z" fill-rule="nonzero"/></svg>',
|
||||
'export': '<svg width="24" height="24"><g fill-rule="nonzero"><path d="M14.4 3 18 7v1h-5V5H7v14h9a1 1 0 0 1 2 0c0 1-.8 2-1.9 2H7c-1 0-2-.8-2-1.9V5c0-1 .8-2 1.9-2h7.5Z"/><path d="M18.1 12c.5 0 .9.4.9 1 0 .5-.3 1-.8 1h-7.3c-.5 0-.9-.4-.9-1 0-.5.3-1 .8-1h7.3Z"/><path d="M16.4 9.2a1 1 0 0 1 1.4.2l2.4 3.6-2.4 3.6a1 1 0 0 1-1.7-1v-.2l1.7-2.4-1.6-2.4a1 1 0 0 1 .2-1.4Z"/></g></svg>',
|
||||
'fill': '<svg width="24" height="26"><path d="m16.6 12-9-9-1.4 1.4 2.4 2.4-5.2 5.1c-.5.6-.5 1.6 0 2.2L9 19.6a1.5 1.5 0 0 0 2.2 0l5.5-5.5c.5-.6.5-1.6 0-2.2ZM5.2 13 10 8.2l4.8 4.8H5.2ZM19 14.5s-2 2.2-2 3.5c0 1.1.9 2 2 2a2 2 0 0 0 2-2c0-1.3-2-3.5-2-3.5Z" fill-rule="nonzero"/></svg>',
|
||||
'flip-horizontally': '<svg width="24" height="24"><path d="M14 19h2v-2h-2v2Zm4-8h2V9h-2v2ZM4 7v10c0 1.1.9 2 2 2h3v-2H6V7h3V5H6a2 2 0 0 0-2 2Zm14-2v2h2a2 2 0 0 0-2-2Zm-7 16h2V3h-2v18Zm7-6h2v-2h-2v2Zm-4-8h2V5h-2v2Zm4 12a2 2 0 0 0 2-2h-2v2Z" fill-rule="nonzero"/></svg>',
|
||||
'flip-vertically': '<svg width="24" height="24"><path d="M5 14v2h2v-2H5Zm8 4v2h2v-2h-2Zm4-14H7a2 2 0 0 0-2 2v3h2V6h10v3h2V6a2 2 0 0 0-2-2Zm2 14h-2v2a2 2 0 0 0 2-2ZM3 11v2h18v-2H3Zm6 7v2h2v-2H9Zm8-4v2h2v-2h-2ZM5 18c0 1.1.9 2 2 2v-2H5Z" fill-rule="nonzero"/></svg>',
|
||||
'footnote': '<svg width="24" height="24"><path d="M19 13c.6 0 1 .4 1 1s-.4 1-1 1H5a1 1 0 1 1 0-2h14Z"/><path fill-rule="evenodd" clip-rule="evenodd" d="M19 4v6h-1V5h-1.5V4h2.6Z"/><path d="M12 18c.6 0 1 .4 1 1s-.4 1-1 1H5a1 1 0 1 1 0-2h7ZM14 8c.6 0 1 .4 1 1s-.4 1-1 1H5a1 1 0 0 1 0-2h9Z"/></svg>',
|
||||
'format-painter': '<svg width="24" height="24"><path d="M18 5V4c0-.5-.4-1-1-1H5a1 1 0 0 0-1 1v4c0 .6.5 1 1 1h12c.6 0 1-.4 1-1V7h1v4H9v9c0 .6.4 1 1 1h2c.6 0 1-.4 1-1v-7h8V5h-3Z" fill-rule="nonzero"/></svg>',
|
||||
'format': '<svg width="24" height="24"><path fill-rule="evenodd" d="M17 5a1 1 0 0 1 0 2h-4v11a1 1 0 0 1-2 0V7H7a1 1 0 1 1 0-2h10Z"/></svg>',
|
||||
'fullscreen': '<svg width="24" height="24"><path d="m15.3 10-1.2-1.3 2.9-3h-2.3a.9.9 0 1 1 0-1.7H19c.5 0 .9.4.9.9v4.4a.9.9 0 1 1-1.8 0V7l-2.9 3Zm0 4 3 3v-2.3a.9.9 0 1 1 1.7 0V19c0 .5-.4.9-.9.9h-4.4a.9.9 0 1 1 0-1.8H17l-3-2.9 1.3-1.2ZM10 15.4l-2.9 3h2.3a.9.9 0 1 1 0 1.7H5a.9.9 0 0 1-.9-.9v-4.4a.9.9 0 1 1 1.8 0V17l2.9-3 1.2 1.3ZM8.7 10 5.7 7v2.3a.9.9 0 0 1-1.7 0V5c0-.5.4-.9.9-.9h4.4a.9.9 0 0 1 0 1.8H7l3 2.9-1.3 1.2Z" fill-rule="nonzero"/></svg>',
|
||||
'gallery': '<svg width="24" height="24"><path fill-rule="nonzero" d="m5 15.7 2.3-2.2c.3-.3.7-.3 1 0L11 16l5.1-5c.3-.4.8-.4 1 0l2 1.9V8H5v7.7ZM5 18V19h3l1.8-1.9-2-2L5 17.9Zm14-3-2.5-2.4-6.4 6.5H19v-4ZM4 6h16c.6 0 1 .4 1 1v13c0 .6-.4 1-1 1H4a1 1 0 0 1-1-1V7c0-.6.4-1 1-1Zm6 7a2 2 0 1 1 0-4 2 2 0 0 1 0 4ZM4.5 4h15a.5.5 0 1 1 0 1h-15a.5.5 0 0 1 0-1Zm2-2h11a.5.5 0 1 1 0 1h-11a.5.5 0 0 1 0-1Z"/></svg>',
|
||||
'gamma': '<svg width="24" height="24"><path d="M4 3h16c.6 0 1 .4 1 1v16c0 .6-.4 1-1 1H4a1 1 0 0 1-1-1V4c0-.6.4-1 1-1Zm1 2v14h14V5H5Zm6.5 11.8V14L9.2 8.7a5.1 5.1 0 0 0-.4-.8l-.1-.2H8v-1l.3-.1.3-.1h.7a1 1 0 0 1 .6.5l.1.3a8.5 8.5 0 0 1 .3.6l1.9 4.6 2-5.2a1 1 0 0 1 1-.6.5.5 0 0 1 .5.6L13 14v2.8a.7.7 0 0 1-1.4 0Z" fill-rule="nonzero"/></svg>',
|
||||
'help': '<svg width="24" height="24"><g fill-rule="evenodd"><path d="M12 5.5a6.5 6.5 0 0 0-6 9 6.3 6.3 0 0 0 1.4 2l1 1a6.3 6.3 0 0 0 3.6 1 6.5 6.5 0 0 0 6-9 6.3 6.3 0 0 0-1.4-2l-1-1a6.3 6.3 0 0 0-3.6-1ZM12 4a7.8 7.8 0 0 1 5.7 2.3A8 8 0 1 1 12 4Z"/><path d="M9.6 9.7a.7.7 0 0 1-.7-.8c0-1.1 1.5-1.8 3.2-1.8 1.8 0 3.2.8 3.2 2.4 0 1.4-.4 2.1-1.5 2.8-.2 0-.3.1-.3.2a2 2 0 0 0-.8.8.8.8 0 0 1-1.4-.6c.3-.7.8-1 1.3-1.5l.4-.2c.7-.4.8-.6.8-1.5 0-.5-.6-.9-1.7-.9-.5 0-1 .1-1.4.3-.2 0-.3.1-.3.2v-.2c0 .4-.4.8-.8.8Z" fill-rule="nonzero"/><circle cx="12" cy="16" r="1"/></g></svg>',
|
||||
'highlight-bg-color': '<svg width="24" height="24"><g fill-rule="evenodd"><path id="tox-icon-highlight-bg-color__color" d="M3 18h18v3H3z"/><path fill-rule="nonzero" d="M7.7 16.7H3l3.3-3.3-.7-.8L10.2 8l4 4.1-4 4.2c-.2.2-.6.2-.8 0l-.6-.7-1.1 1.1zm5-7.5L11 7.4l3-2.9a2 2 0 0 1 2.6 0L18 6c.7.7.7 2 0 2.7l-2.9 2.9-1.8-1.8-.5-.6"/></g></svg>',
|
||||
'home': '<svg width="24" height="24"><path fill-rule="nonzero" d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"/></svg>',
|
||||
'horizontal-rule': '<svg width="24" height="24"><path d="M4 11h16v2H4z" fill-rule="evenodd"/></svg>',
|
||||
'image-options': '<svg width="24" height="24"><path d="M6 10a2 2 0 0 0-2 2c0 1.1.9 2 2 2a2 2 0 0 0 2-2 2 2 0 0 0-2-2Zm12 0a2 2 0 0 0-2 2c0 1.1.9 2 2 2a2 2 0 0 0 2-2 2 2 0 0 0-2-2Zm-6 0a2 2 0 0 0-2 2c0 1.1.9 2 2 2a2 2 0 0 0 2-2 2 2 0 0 0-2-2Z" fill-rule="nonzero"/></svg>',
|
||||
'image': '<svg width="24" height="24"><path d="m5 15.7 3.3-3.2c.3-.3.7-.3 1 0L12 15l4.1-4c.3-.4.8-.4 1 0l2 1.9V5H5v10.7ZM5 18V19h3l2.8-2.9-2-2L5 17.9Zm14-3-2.5-2.4-6.4 6.5H19v-4ZM4 3h16c.6 0 1 .4 1 1v16c0 .6-.4 1-1 1H4a1 1 0 0 1-1-1V4c0-.6.4-1 1-1Zm6 8a2 2 0 1 0 0-4 2 2 0 0 0 0 4Z" fill-rule="nonzero"/></svg>',
|
||||
'indent': '<svg width="24" height="24"><path d="M7 5h12c.6 0 1 .4 1 1s-.4 1-1 1H7a1 1 0 1 1 0-2Zm5 4h7c.6 0 1 .4 1 1s-.4 1-1 1h-7a1 1 0 0 1 0-2Zm0 4h7c.6 0 1 .4 1 1s-.4 1-1 1h-7a1 1 0 0 1 0-2Zm-5 4h12a1 1 0 0 1 0 2H7a1 1 0 0 1 0-2Zm-2.6-3.8L6.2 12l-1.8-1.2a1 1 0 0 1 1.2-1.6l3 2a1 1 0 0 1 0 1.6l-3 2a1 1 0 1 1-1.2-1.6Z" fill-rule="evenodd"/></svg>',
|
||||
'info': '<svg width="24" height="24"><path d="M12 4a7.8 7.8 0 0 1 5.7 2.3A8 8 0 1 1 12 4Zm-1 3v2h2V7h-2Zm3 10v-1h-1v-5h-3v1h1v4h-1v1h4Z" fill-rule="evenodd"/></svg>',
|
||||
'insert-character': '<svg width="24" height="24"><path d="M15 18h4l1-2v4h-6v-3.3l1.4-1a6 6 0 0 0 1.8-2.9 6.3 6.3 0 0 0-.1-4.1 5.8 5.8 0 0 0-3-3.2c-.6-.3-1.3-.5-2.1-.5a5.1 5.1 0 0 0-3.9 1.8 6.3 6.3 0 0 0-1.3 6 6.2 6.2 0 0 0 1.8 3l1.4.9V20H4v-4l1 2h4v-.5l-2-1L5.4 15A6.5 6.5 0 0 1 4 11c0-1 .2-1.9.6-2.7A7 7 0 0 1 6.3 6C7.1 5.4 8 5 9 4.5c1-.3 2-.5 3.1-.5a8.8 8.8 0 0 1 5.7 2 7 7 0 0 1 1.7 2.3 6 6 0 0 1 .2 4.8c-.2.7-.6 1.3-1 1.9a7.6 7.6 0 0 1-3.6 2.5v.5Z" fill-rule="evenodd"/></svg>',
|
||||
'insert-time': '<svg width="24" height="24"><g fill-rule="nonzero"><path d="M12 19a7 7 0 1 0 0-14 7 7 0 0 0 0 14Zm0 2a9 9 0 1 1 0-18 9 9 0 0 1 0 18Z"/><path d="M16 12h-3V7c0-.6-.4-1-1-1a1 1 0 0 0-1 1v7h5c.6 0 1-.4 1-1s-.4-1-1-1Z"/></g></svg>',
|
||||
'invert': '<svg width="24" height="24"><path d="M18 19.3 16.5 18a5.8 5.8 0 0 1-3.1 1.9 6.1 6.1 0 0 1-5.5-1.6A5.8 5.8 0 0 1 6 14v-.3l.1-1.2A13.9 13.9 0 0 1 7.7 9l-3-3 .7-.8 2.8 2.9 9 8.9 1.5 1.6-.7.6Zm0-5.5v.3l-.1 1.1-.4 1-1.2-1.2a4.3 4.3 0 0 0 .2-1v-.2c0-.4 0-.8-.2-1.3l-.5-1.4a14.8 14.8 0 0 0-3-4.2L12 6a26.1 26.1 0 0 0-2.2 2.5l-1-1a20.9 20.9 0 0 1 2.9-3.3L12 4l1 .8a22.2 22.2 0 0 1 4 5.4c.6 1.2 1 2.4 1 3.6Z" fill-rule="evenodd"/></svg>',
|
||||
'italic': '<svg width="24" height="24"><path d="m16.7 4.7-.1.9h-.3c-.6 0-1 0-1.4.3-.3.3-.4.6-.5 1.1l-2.1 9.8v.6c0 .5.4.8 1.4.8h.2l-.2.8H8l.2-.8h.2c1.1 0 1.8-.5 2-1.5l2-9.8.1-.5c0-.6-.4-.8-1.4-.8h-.3l.2-.9h5.8Z" fill-rule="evenodd"/></svg>',
|
||||
'language': '<svg width="24" height="24"><path d="M12 3a9 9 0 1 1 0 18 9 9 0 0 1 0-18Zm4.3 13.3c-.5 1-1.2 2-2 2.9a7.5 7.5 0 0 0 3.2-2.1l-.2-.2a6 6 0 0 0-1-.6Zm-8.6 0c-.5.2-.9.5-1.2.8.9 1 2 1.7 3.2 2a10 10 0 0 1-2-2.8Zm3.6-.8c-.8 0-1.6.1-2.2.3.5 1 1.2 1.9 2.1 2.7Zm1.5 0v3c.9-.8 1.6-1.7 2.1-2.7-.6-.2-1.4-.3-2.1-.3Zm-6-2.7H4.5c.2 1 .5 2.1 1 3h.3l1.3-1a10 10 0 0 1-.3-2Zm12.7 0h-2.3c0 .7-.1 1.4-.3 2l1.6 1.1c.5-1 .9-2 1-3.1Zm-3.8 0h-3V14c1 0 2 .1 2.7.4.2-.5.3-1 .3-1.6Zm-4.4 0h-3l.3 1.6c.8-.3 1.7-.4 2.7-.4v-1.3Zm-5.5-5c-.7 1-1.1 2.2-1.3 3.5h2.3c0-1 .2-1.8.5-2.6l-1.5-1Zm2.9 1.4v.1c-.2.6-.4 1.3-.4 2h3V9.4c-1 0-1.8-.1-2.6-.3Zm6.6 0h-.1l-2.4.3v1.8h3l-.5-2.1Zm3-1.4-.3.1-1.3.8c.3.8.5 1.6.5 2.6h2.3a7.5 7.5 0 0 0-1.3-3.5Zm-9 0 2 .2V5.5a9 9 0 0 0-2 2.2Zm3.5-2.3V8c.6 0 1.3 0 1.9-.2a9 9 0 0 0-2-2.3Zm-3-.7h-.1c-1.1.4-2.1 1-3 1.8l1.2.7a10 10 0 0 1 1.9-2.5Zm4.4 0 .1.1a10 10 0 0 1 1.8 2.4l1.1-.7a7.5 7.5 0 0 0-3-1.8Z"/></svg>',
|
||||
'line-height': '<svg width="24" height="24"><path d="M21 5a1 1 0 0 1 .1 2H13a1 1 0 0 1-.1-2H21zm0 4a1 1 0 0 1 .1 2H13a1 1 0 0 1-.1-2H21zm0 4a1 1 0 0 1 .1 2H13a1 1 0 0 1-.1-2H21zm0 4a1 1 0 0 1 .1 2H13a1 1 0 0 1-.1-2H21zM7 3.6l3.7 3.7a1 1 0 0 1-1.3 1.5h-.1L8 7.3v9.2l1.3-1.3a1 1 0 0 1 1.3 0h.1c.4.4.4 1 0 1.3v.1L7 20.4l-3.7-3.7a1 1 0 0 1 1.3-1.5h.1L6 16.7V7.4L4.7 8.7a1 1 0 0 1-1.3 0h-.1a1 1 0 0 1 0-1.3v-.1L7 3.6z"/></svg>',
|
||||
'line': '<svg width="24" height="24"><path d="m15 9-8 8H4v-3l8-8 3 3Zm1-1-3-3 1-1h1c-.2 0 0 0 0 0l2 2s0 .2 0 0v1l-1 1ZM4 18h16v2H4v-2Z" fill-rule="evenodd"/></svg>',
|
||||
'link': '<svg width="24" height="24"><path d="M6.2 12.3a1 1 0 0 1 1.4 1.4l-2 2a2 2 0 1 0 2.6 2.8l4.8-4.8a1 1 0 0 0 0-1.4 1 1 0 1 1 1.4-1.3 2.9 2.9 0 0 1 0 4L9.6 20a3.9 3.9 0 0 1-5.5-5.5l2-2Zm11.6-.6a1 1 0 0 1-1.4-1.4l2-2a2 2 0 1 0-2.6-2.8L11 10.3a1 1 0 0 0 0 1.4A1 1 0 1 1 9.6 13a2.9 2.9 0 0 1 0-4L14.4 4a3.9 3.9 0 0 1 5.5 5.5l-2 2Z" fill-rule="nonzero"/></svg>',
|
||||
'list-bull-circle': '<svg width="48" height="48"><g fill-rule="evenodd"><path d="M11 16a2 2 0 1 0 0-4 2 2 0 0 0 0 4Zm0 1a3 3 0 1 1 0-6 3 3 0 0 1 0 6ZM11 26a2 2 0 1 0 0-4 2 2 0 0 0 0 4Zm0 1a3 3 0 1 1 0-6 3 3 0 0 1 0 6ZM11 36a2 2 0 1 0 0-4 2 2 0 0 0 0 4Zm0 1a3 3 0 1 1 0-6 3 3 0 0 1 0 6Z" fill-rule="nonzero"/><path opacity=".2" d="M18 12h22v4H18zM18 22h22v4H18zM18 32h22v4H18z"/></g></svg>',
|
||||
'list-bull-default': '<svg width="48" height="48"><g fill-rule="evenodd"><circle cx="11" cy="14" r="3"/><circle cx="11" cy="24" r="3"/><circle cx="11" cy="34" r="3"/><path opacity=".2" d="M18 12h22v4H18zM18 22h22v4H18zM18 32h22v4H18z"/></g></svg>',
|
||||
'list-bull-square': '<svg width="48" height="48"><g fill-rule="evenodd"><path d="M8 11h6v6H8zM8 21h6v6H8zM8 31h6v6H8z"/><path opacity=".2" d="M18 12h22v4H18zM18 22h22v4H18zM18 32h22v4H18z"/></g></svg>',
|
||||
'list-num-default-rtl': '<svg width="48" height="48"><g fill-rule="evenodd"><path opacity=".2" d="M8 12h22v4H8zM8 22h22v4H8zM8 32h22v4H8z"/><path d="M37.4 17v-4.8h-.1l-1.5 1v-1.1l1.6-1.1h1.2v6zM33.3 17.1c-.5 0-.8-.3-.8-.7 0-.4.3-.7.8-.7.4 0 .7.3.7.7 0 .4-.3.7-.7.7zm1.7 5.7c0-1.2 1-2 2.2-2 1.3 0 2.1.8 2.1 1.8 0 .7-.3 1.2-1.3 2.2l-1.2 1v.2h2.6v1h-4.3v-.9l2-1.9c.8-.8 1-1.1 1-1.5 0-.5-.4-.8-1-.8-.5 0-.9.3-.9.9H35zm-1.7 4.3c-.5 0-.8-.3-.8-.7 0-.4.3-.7.8-.7.4 0 .7.3.7.7 0 .4-.3.7-.7.7zm3.2 7.3v-1h.7c.6 0 1-.3 1-.8 0-.4-.4-.7-1-.7s-1 .3-1 .8H35c0-1.1 1-1.8 2.2-1.8 1.2 0 2.1.6 2.1 1.6 0 .7-.4 1.2-1 1.3v.1c.7.1 1.3.7 1.3 1.4 0 1-1 1.9-2.4 1.9-1.3 0-2.2-.8-2.3-2h1.2c0 .6.5 1 1.1 1 .6 0 1-.4 1-1 0-.5-.3-.8-1-.8h-.7zm-3.3 2.7c-.4 0-.7-.3-.7-.7 0-.4.3-.7.7-.7.5 0 .8.3.8.7 0 .4-.3.7-.8.7z"/></g></svg>',
|
||||
'list-num-default': '<svg width="48" height="48"><g fill-rule="evenodd"><path opacity=".2" d="M18 12h22v4H18zM18 22h22v4H18zM18 32h22v4H18z"/><path d="M10 17v-4.8l-1.5 1v-1.1l1.6-1h1.2V17h-1.2Zm3.6.1c-.4 0-.7-.3-.7-.7 0-.4.3-.7.7-.7.5 0 .7.3.7.7 0 .4-.2.7-.7.7Zm-5 5.7c0-1.2.8-2 2.1-2s2.1.8 2.1 1.8c0 .7-.3 1.2-1.4 2.2l-1.1 1v.2h2.6v1H8.6v-.9l2-1.9c.8-.8 1-1.1 1-1.5 0-.5-.4-.8-1-.8-.5 0-.9.3-.9.9H8.5Zm6.3 4.3c-.5 0-.7-.3-.7-.7 0-.4.2-.7.7-.7.4 0 .7.3.7.7 0 .4-.3.7-.7.7ZM10 34.4v-1h.7c.6 0 1-.3 1-.8 0-.4-.4-.7-1-.7s-1 .3-1 .8H8.6c0-1.1 1-1.8 2.2-1.8 1.3 0 2.1.6 2.1 1.6 0 .7-.4 1.2-1 1.3v.1c.8.1 1.3.7 1.3 1.4 0 1-1 1.9-2.4 1.9-1.3 0-2.2-.8-2.3-2h1.2c0 .6.5 1 1.1 1 .7 0 1-.4 1-1 0-.5-.3-.8-1-.8h-.7Zm4.7 2.7c-.4 0-.7-.3-.7-.7 0-.4.3-.7.7-.7.5 0 .8.3.8.7 0 .4-.3.7-.8.7Z"/></g></svg>',
|
||||
'list-num-lower-alpha-rtl': '<svg width="48" height="48"><g fill-rule="evenodd"><path opacity=".2" d="M8 12h22v4H8zM8 22h22v4H8zM8 32h22v4H8z"/><path d="M36.5 16c-.9 0-1.5-.5-1.5-1.3s.6-1.3 1.8-1.4h1v-.4c0-.4-.2-.6-.7-.6-.4 0-.7.1-.8.4h-1.1c0-.8.8-1.4 2-1.4S39 12 39 13V16h-1.2v-.6c-.3.4-.8.7-1.4.7Zm.4-.8c.6 0 1-.4 1-.9V14h-1c-.5.1-.7.3-.7.6 0 .4.3.6.7.6ZM33.1 16.1c-.4 0-.7-.3-.7-.7 0-.4.3-.7.7-.7.5 0 .8.3.8.7 0 .4-.3.7-.8.7ZM37.7 26c-.7 0-1.2-.2-1.5-.7v.7H35v-6.3h1.2v2.5c.3-.5.8-.9 1.5-.9 1.1 0 1.8 1 1.8 2.4 0 1.5-.7 2.4-1.8 2.4Zm-.5-3.6c-.6 0-1 .5-1 1.3s.4 1.4 1 1.4c.7 0 1-.6 1-1.4 0-.8-.3-1.3-1-1.3ZM33.2 26.1c-.4 0-.7-.3-.7-.7 0-.4.3-.7.7-.7.5 0 .8.3.8.7 0 .4-.3.7-.8.7zm6 7h-1c-.1-.5-.4-.8-1-.8s-1 .5-1 1.4c0 1 .4 1.4 1 1.4.5 0 .9-.2 1-.7h1c0 1-.8 1.7-2 1.7-1.4 0-2.2-.9-2.2-2.4s.8-2.4 2.2-2.4c1.2 0 2 .7 2 1.7zm-6.1 3c-.5 0-.7-.3-.7-.7 0-.4.2-.7.7-.7.4 0 .7.3.7.7 0 .4-.3.7-.7.7z"/></g></svg>',
|
||||
'list-num-lower-alpha': '<svg width="48" height="48"><g fill-rule="evenodd"><path opacity=".2" d="M18 12h22v4H18zM18 22h22v4H18zM18 32h22v4H18z"/><path d="M10.3 15.2c.5 0 1-.4 1-.9V14h-1c-.5.1-.8.3-.8.6 0 .4.3.6.8.6Zm-.4.9c-1 0-1.5-.6-1.5-1.4 0-.8.6-1.3 1.7-1.4h1.1v-.4c0-.4-.2-.6-.7-.6-.5 0-.8.1-.9.4h-1c0-.8.8-1.4 2-1.4 1.1 0 1.8.6 1.8 1.6V16h-1.1v-.6h-.1c-.2.4-.7.7-1.3.7Zm4.6 0c-.5 0-.7-.3-.7-.7 0-.4.2-.7.7-.7.4 0 .7.3.7.7 0 .4-.3.7-.7.7Zm-3.2 10c-.6 0-1.2-.3-1.4-.8v.7H8.5v-6.3H10v2.5c.3-.5.8-.9 1.4-.9 1.2 0 1.9 1 1.9 2.4 0 1.5-.7 2.4-1.9 2.4Zm-.4-3.7c-.7 0-1 .5-1 1.3s.3 1.4 1 1.4c.6 0 1-.6 1-1.4 0-.8-.4-1.3-1-1.3Zm4 3.7c-.5 0-.7-.3-.7-.7 0-.4.2-.7.7-.7.4 0 .7.3.7.7 0 .4-.3.7-.7.7Zm-2.2 7h-1.2c0-.5-.4-.8-.9-.8-.6 0-1 .5-1 1.4 0 1 .4 1.4 1 1.4.5 0 .8-.2 1-.7h1c0 1-.8 1.7-2 1.7-1.4 0-2.2-.9-2.2-2.4s.8-2.4 2.2-2.4c1.2 0 2 .7 2 1.7Zm1.8 3c-.5 0-.8-.3-.8-.7 0-.4.3-.7.8-.7.4 0 .7.3.7.7 0 .4-.3.7-.7.7Z"/></g></svg>',
|
||||
'list-num-lower-greek-rtl': '<svg width="48" height="48"><g fill-rule="evenodd"><path opacity=".2" d="M8 12h22v4H8zM8 22h22v4H8zM8 32h22v4H8z"/><path d="M37.4 16c-1.2 0-2-.8-2-2.3 0-1.5.8-2.4 2-2.4.6 0 1 .4 1.3 1v-.9H40v3.2c0 .4.1.5.4.5h.2v.9h-.6c-.6 0-1-.2-1-.7h-.2c-.2.4-.7.8-1.3.8Zm.3-1c.6 0 1-.5 1-1.3s-.4-1.3-1-1.3-1 .5-1 1.3.4 1.4 1 1.4ZM33.3 16.1c-.5 0-.8-.3-.8-.7 0-.4.3-.7.8-.7.4 0 .7.3.7.7 0 .4-.3.7-.7.7ZM36 21.9c0-1.5.8-2.3 2.1-2.3 1.2 0 2 .6 2 1.6 0 .6-.3 1-.9 1.3.9.3 1.3.8 1.3 1.7 0 1.2-.7 1.9-1.8 1.9-.6 0-1.1-.3-1.4-.8v2.2H36V22Zm1.8 1.2v-1h.3c.5 0 .9-.2.9-.7 0-.5-.3-.8-.9-.8-.5 0-.8.3-.8 1v2.2c0 .8.4 1.3 1 1.3s1-.4 1-1-.4-1-1.2-1h-.3ZM33.3 26.1c-.5 0-.8-.3-.8-.7 0-.4.3-.7.8-.7.4 0 .7.3.7.7 0 .4-.3.7-.7.7ZM37.1 34.6 34.8 30h1.4l1.7 3.5 1.7-3.5h1.1l-2.2 4.6v.1c.5.8.7 1.4.7 1.8 0 .4-.2.8-.4 1-.2.2-.6.3-1 .3-.9 0-1.3-.4-1.3-1.2 0-.5.2-1 .5-1.7l.1-.2Zm.7 1a2 2 0 0 0-.4.9c0 .3.1.4.4.4.3 0 .4-.1.4-.4 0-.2-.1-.6-.4-1ZM33.3 36.1c-.5 0-.8-.3-.8-.7 0-.4.3-.7.8-.7.4 0 .7.3.7.7 0 .4-.3.7-.7.7Z"/></g></svg>',
|
||||
'list-num-lower-greek': '<svg width="48" height="48"><g fill-rule="evenodd"><path opacity=".2" d="M18 12h22v4H18zM18 22h22v4H18zM18 32h22v4H18z"/><path d="M10.5 15c.7 0 1-.5 1-1.3s-.3-1.3-1-1.3c-.5 0-.9.5-.9 1.3s.4 1.4 1 1.4Zm-.3 1c-1.1 0-1.8-.8-1.8-2.3 0-1.5.7-2.4 1.8-2.4.7 0 1.1.4 1.3 1h.1v-.9h1.2v3.2c0 .4.1.5.4.5h.2v.9h-.6c-.6 0-1-.2-1.1-.7h-.1c-.2.4-.7.8-1.4.8Zm5 .1c-.5 0-.8-.3-.8-.7 0-.4.3-.7.7-.7.5 0 .8.3.8.7 0 .4-.3.7-.8.7Zm-4.9 7v-1h.3c.6 0 1-.2 1-.7 0-.5-.4-.8-1-.8-.5 0-.8.3-.8 1v2.2c0 .8.4 1.3 1.1 1.3.6 0 1-.4 1-1s-.5-1-1.3-1h-.3ZM8.6 22c0-1.5.7-2.3 2-2.3 1.2 0 2 .6 2 1.6 0 .6-.3 1-.8 1.3.8.3 1.3.8 1.3 1.7 0 1.2-.8 1.9-1.9 1.9-.6 0-1.1-.3-1.3-.8v2.2H8.5V22Zm6.2 4.2c-.4 0-.7-.3-.7-.7 0-.4.3-.7.7-.7.5 0 .7.3.7.7 0 .4-.2.7-.7.7Zm-4.5 8.5L8 30h1.4l1.7 3.5 1.7-3.5h1.1l-2.2 4.6v.1c.5.8.7 1.4.7 1.8 0 .4-.1.8-.4 1-.2.2-.6.3-1 .3-.9 0-1.3-.4-1.3-1.2 0-.5.2-1 .5-1.7l.1-.2Zm.7 1a2 2 0 0 0-.4.9c0 .3.1.4.4.4.3 0 .4-.1.4-.4 0-.2-.1-.6-.4-1Zm4.5.5c-.5 0-.8-.3-.8-.7 0-.4.3-.7.8-.7.4 0 .7.3.7.7 0 .4-.3.7-.7.7Z"/></g></svg>',
|
||||
'list-num-lower-roman-rtl': '<svg width="48" height="48"><g fill-rule="evenodd"><path opacity=".2" d="M8 12h22v4H8zM8 22h22v4H8zM8 32h22v4H8z"/><path d="M32.9 16v-1.2h-1.3V16H33Zm0 10v-1.2h-1.3V26H33Zm0 10v-1.2h-1.3V36H33Z"/><path fill-rule="nonzero" d="M36 21h-1.5v5H36zM36 31h-1.5v5H36zM39 21h-1.5v5H39zM39 31h-1.5v5H39zM42 31h-1.5v5H42zM36 11h-1.5v5H36zM36 19h-1.5v1H36zM36 29h-1.5v1H36zM39 19h-1.5v1H39zM39 29h-1.5v1H39zM42 29h-1.5v1H42zM36 9h-1.5v1H36z"/></g></svg>',
|
||||
'list-num-lower-roman': '<svg width="48" height="48"><g fill-rule="evenodd"><path opacity=".2" d="M18 12h22v4H18zM18 22h22v4H18zM18 32h22v4H18z"/><path d="M15.1 16v-1.2h1.3V16H15Zm0 10v-1.2h1.3V26H15Zm0 10v-1.2h1.3V36H15Z"/><path fill-rule="nonzero" d="M12 21h1.5v5H12zM12 31h1.5v5H12zM9 21h1.5v5H9zM9 31h1.5v5H9zM6 31h1.5v5H6zM12 11h1.5v5H12zM12 19h1.5v1H12zM12 29h1.5v1H12zM9 19h1.5v1H9zM9 29h1.5v1H9zM6 29h1.5v1H6zM12 9h1.5v1H12z"/></g></svg>',
|
||||
'list-num-upper-alpha-rtl': '<svg width="48" height="48"><g fill-rule="evenodd"><path opacity=".2" d="M8 12h22v4H8zM8 22h22v4H8zM8 32h22v4H8z"/><path d="m39.3 17-.5-1.4h-2l-.5 1.4H35l2-6h1.6l2 6h-1.3Zm-1.6-4.7-.7 2.3h1.6l-.8-2.3ZM33.4 17c-.4 0-.7-.3-.7-.7 0-.4.3-.7.7-.7.5 0 .7.3.7.7 0 .4-.2.7-.7.7Zm4.7 9.9h-2.7v-6H38c1.2 0 1.9.6 1.9 1.5 0 .6-.5 1.2-1 1.3.7.1 1.3.7 1.3 1.5 0 1-.8 1.7-2 1.7Zm-1.4-5v1.5h1c.6 0 1-.3 1-.8 0-.4-.4-.7-1-.7h-1Zm0 4h1.1c.7 0 1.1-.3 1.1-.8 0-.6-.4-.9-1.1-.9h-1.1V26ZM33 27.1c-.5 0-.8-.3-.8-.7 0-.4.3-.7.8-.7.4 0 .7.3.7.7 0 .4-.3.7-.7.7Zm4.9 10c-1.8 0-2.8-1.1-2.8-3.1s1-3.1 2.8-3.1c1.4 0 2.5.9 2.6 2.2h-1.3c0-.7-.6-1.1-1.3-1.1-1 0-1.6.7-1.6 2s.6 2 1.6 2c.7 0 1.2-.4 1.4-1h1.2c-.1 1.3-1.2 2.2-2.6 2.2Zm-4.5 0c-.5 0-.8-.3-.8-.7 0-.4.3-.7.8-.7.4 0 .7.3.7.7 0 .4-.3.7-.7.7Z"/></g></svg>',
|
||||
'list-num-upper-alpha': '<svg width="48" height="48"><g fill-rule="evenodd"><path opacity=".2" d="M18 12h22v4H18zM18 22h22v4H18zM18 32h22v4H18z"/><path d="m12.6 17-.5-1.4h-2L9.5 17H8.3l2-6H12l2 6h-1.3ZM11 12.3l-.7 2.3h1.6l-.8-2.3Zm4.7 4.8c-.4 0-.7-.3-.7-.7 0-.4.3-.7.7-.7.5 0 .7.3.7.7 0 .4-.2.7-.7.7ZM11.4 27H8.7v-6h2.6c1.2 0 1.9.6 1.9 1.5 0 .6-.5 1.2-1 1.3.7.1 1.3.7 1.3 1.5 0 1-.8 1.7-2 1.7ZM10 22v1.5h1c.6 0 1-.3 1-.8 0-.4-.4-.7-1-.7h-1Zm0 4H11c.7 0 1.1-.3 1.1-.8 0-.6-.4-.9-1.1-.9H10V26Zm5.4 1.1c-.5 0-.8-.3-.8-.7 0-.4.3-.7.8-.7.4 0 .7.3.7.7 0 .4-.3.7-.7.7Zm-4.1 10c-1.8 0-2.8-1.1-2.8-3.1s1-3.1 2.8-3.1c1.4 0 2.5.9 2.6 2.2h-1.3c0-.7-.6-1.1-1.3-1.1-1 0-1.6.7-1.6 2s.6 2 1.6 2c.7 0 1.2-.4 1.4-1h1.2c-.1 1.3-1.2 2.2-2.6 2.2Zm4.5 0c-.5 0-.8-.3-.8-.7 0-.4.3-.7.8-.7.4 0 .7.3.7.7 0 .4-.3.7-.7.7Z"/></g></svg>',
|
||||
'list-num-upper-roman-rtl': '<svg width="48" height="48"><g fill-rule="evenodd"><path opacity=".2" d="M8 12h22v4H8zM8 22h22v4H8zM8 32h22v4H8z"/><path d="M31.6 17v-1.2H33V17h-1.3Zm0 10v-1.2H33V27h-1.3Zm0 10v-1.2H33V37h-1.3Z"/><path fill-rule="nonzero" d="M34.5 20H36v7h-1.5zM34.5 30H36v7h-1.5zM37.5 20H39v7h-1.5zM37.5 30H39v7h-1.5zM40.5 30H42v7h-1.5zM34.5 10H36v7h-1.5z"/></g></svg>',
|
||||
'list-num-upper-roman': '<svg width="48" height="48"><g fill-rule="evenodd"><path opacity=".2" d="M18 12h22v4H18zM18 22h22v4H18zM18 32h22v4H18z"/><path d="M15.1 17v-1.2h1.3V17H15Zm0 10v-1.2h1.3V27H15Zm0 10v-1.2h1.3V37H15Z"/><path fill-rule="nonzero" d="M12 20h1.5v7H12zM12 30h1.5v7H12zM9 20h1.5v7H9zM9 30h1.5v7H9zM6 30h1.5v7H6zM12 10h1.5v7H12z"/></g></svg>',
|
||||
'lock': '<svg width="24" height="24"><path d="M16.3 11c.2 0 .3 0 .5.2l.2.6v7.4c0 .3 0 .4-.2.6l-.6.2H7.8c-.3 0-.4 0-.6-.2a.7.7 0 0 1-.2-.6v-7.4c0-.3 0-.4.2-.6l.5-.2H8V8c0-.8.3-1.5.9-2.1.6-.6 1.3-.9 2.1-.9h2c.8 0 1.5.3 2.1.9.6.6.9 1.3.9 2.1v3h.3ZM10 8v3h4V8a1 1 0 0 0-.3-.7A1 1 0 0 0 13 7h-2a1 1 0 0 0-.7.3 1 1 0 0 0-.3.7Z" fill-rule="evenodd"/></svg>',
|
||||
'ltr': '<svg width="24" height="24"><path d="M11 5h7a1 1 0 0 1 0 2h-1v11a1 1 0 0 1-2 0V7h-2v11a1 1 0 0 1-2 0v-6c-.5 0-1 0-1.4-.3A3.4 3.4 0 0 1 7.8 10a3.3 3.3 0 0 1 0-2.8 3.4 3.4 0 0 1 1.8-1.8L11 5ZM4.4 16.2 6.2 15l-1.8-1.2a1 1 0 0 1 1.2-1.6l3 2a1 1 0 0 1 0 1.6l-3 2a1 1 0 1 1-1.2-1.6Z" fill-rule="evenodd"/></svg>',
|
||||
'more-drawer': '<svg width="24" height="24"><path d="M6 10a2 2 0 0 0-2 2c0 1.1.9 2 2 2a2 2 0 0 0 2-2 2 2 0 0 0-2-2Zm12 0a2 2 0 0 0-2 2c0 1.1.9 2 2 2a2 2 0 0 0 2-2 2 2 0 0 0-2-2Zm-6 0a2 2 0 0 0-2 2c0 1.1.9 2 2 2a2 2 0 0 0 2-2 2 2 0 0 0-2-2Z" fill-rule="nonzero"/></svg>',
|
||||
'new-document': '<svg width="24" height="24"><path d="M14.4 3H7a2 2 0 0 0-2 2v14c0 1.1.9 2 2 2h10a2 2 0 0 0 2-2V7.6L14.4 3ZM17 19H7V5h6v4h4v10Z" fill-rule="nonzero"/></svg>',
|
||||
'new-tab': '<svg width="24" height="24"><path d="m15 13 2-2v8H5V7h8l-2 2H7v8h8v-4Zm4-8v5.5l-2-2-5.6 5.5H10v-1.4L15.5 7l-2-2H19Z" fill-rule="evenodd"/></svg>',
|
||||
'non-breaking': '<svg width="24" height="24"><path d="M11 11H8a1 1 0 1 1 0-2h3V6c0-.6.4-1 1-1s1 .4 1 1v3h3c.6 0 1 .4 1 1s-.4 1-1 1h-3v3c0 .6-.4 1-1 1a1 1 0 0 1-1-1v-3Zm10 4v5H3v-5c0-.6.4-1 1-1s1 .4 1 1v3h14v-3c0-.6.4-1 1-1s1 .4 1 1Z" fill-rule="evenodd"/></svg>',
|
||||
'notice': '<svg width="24" height="24"><path d="M17.8 9.8 15.4 4 20 8.5v7L15.5 20h-7L4 15.5v-7L8.5 4h7l2.3 5.8Zm0 0 2.2 5.7-2.3-5.8ZM13 17v-2h-2v2h2Zm0-4V7h-2v6h2Z" fill-rule="evenodd"/></svg>',
|
||||
'ordered-list-rtl': '<svg width="24" height="24"><path d="M6 17h8a1 1 0 0 1 0 2H6a1 1 0 0 1 0-2Zm0-6h8a1 1 0 0 1 0 2H6a1 1 0 0 1 0-2Zm0-6h8a1 1 0 0 1 0 2H6a1 1 0 1 1 0-2Zm13-1v3.5a.5.5 0 1 1-1 0V5h-.5a.5.5 0 1 1 0-1H19Zm-1 8.8.2.2h1.3a.5.5 0 1 1 0 1h-1.6a1 1 0 0 1-.9-1V13c0-.4.3-.8.6-1l1.2-.4.2-.3a.2.2 0 0 0-.2-.2h-1.3a.5.5 0 0 1-.5-.5c0-.3.2-.5.5-.5h1.6c.5 0 .9.4.9 1v.1c0 .4-.3.8-.6 1l-1.2.4-.2.3Zm2 4.2v2c0 .6-.4 1-1 1h-1.5a.5.5 0 0 1 0-1h1.2a.3.3 0 1 0 0-.6h-1.3a.4.4 0 1 1 0-.8h1.3a.3.3 0 0 0 0-.6h-1.2a.5.5 0 1 1 0-1H19c.6 0 1 .4 1 1Z" fill-rule="evenodd"/></svg>',
|
||||
'ordered-list': '<svg width="24" height="24"><path d="M10 17h8c.6 0 1 .4 1 1s-.4 1-1 1h-8a1 1 0 0 1 0-2Zm0-6h8c.6 0 1 .4 1 1s-.4 1-1 1h-8a1 1 0 0 1 0-2Zm0-6h8c.6 0 1 .4 1 1s-.4 1-1 1h-8a1 1 0 1 1 0-2ZM6 4v3.5c0 .3-.2.5-.5.5a.5.5 0 0 1-.5-.5V5h-.5a.5.5 0 0 1 0-1H6Zm-1 8.8.2.2h1.3c.3 0 .5.2.5.5s-.2.5-.5.5H4.9a1 1 0 0 1-.9-1V13c0-.4.3-.8.6-1l1.2-.4.2-.3a.2.2 0 0 0-.2-.2H4.5a.5.5 0 0 1-.5-.5c0-.3.2-.5.5-.5h1.6c.5 0 .9.4.9 1v.1c0 .4-.3.8-.6 1l-1.2.4-.2.3ZM7 17v2c0 .6-.4 1-1 1H4.5a.5.5 0 0 1 0-1h1.2c.2 0 .3-.1.3-.3 0-.2-.1-.3-.3-.3H4.4a.4.4 0 1 1 0-.8h1.3c.2 0 .3-.1.3-.3 0-.2-.1-.3-.3-.3H4.5a.5.5 0 1 1 0-1H6c.6 0 1 .4 1 1Z" fill-rule="evenodd"/></svg>',
|
||||
'orientation': '<svg width="24" height="24"><path d="M7.3 6.4 1 13l6.4 6.5 6.5-6.5-6.5-6.5ZM3.7 13l3.6-3.7L11 13l-3.7 3.7-3.6-3.7ZM12 6l2.8 2.7c.3.3.3.8 0 1-.3.4-.9.4-1.2 0L9.2 5.7a.8.8 0 0 1 0-1.2L13.6.2c.3-.3.9-.3 1.2 0 .3.3.3.8 0 1.1L12 4h1a9 9 0 1 1-4.3 16.9l1.5-1.5A7 7 0 1 0 13 6h-1Z" fill-rule="nonzero"/></svg>',
|
||||
'outdent': '<svg width="24" height="24"><path d="M7 5h12c.6 0 1 .4 1 1s-.4 1-1 1H7a1 1 0 1 1 0-2Zm5 4h7c.6 0 1 .4 1 1s-.4 1-1 1h-7a1 1 0 0 1 0-2Zm0 4h7c.6 0 1 .4 1 1s-.4 1-1 1h-7a1 1 0 0 1 0-2Zm-5 4h12a1 1 0 0 1 0 2H7a1 1 0 0 1 0-2Zm1.6-3.8a1 1 0 0 1-1.2 1.6l-3-2a1 1 0 0 1 0-1.6l3-2a1 1 0 0 1 1.2 1.6L6.8 12l1.8 1.2Z" fill-rule="evenodd"/></svg>',
|
||||
'page-break': '<svg width="24" height="24"><g fill-rule="evenodd"><path d="M5 11c.6 0 1 .4 1 1s-.4 1-1 1a1 1 0 0 1 0-2Zm3 0h1c.6 0 1 .4 1 1s-.4 1-1 1H8a1 1 0 0 1 0-2Zm4 0c.6 0 1 .4 1 1s-.4 1-1 1a1 1 0 0 1 0-2Zm3 0h1c.6 0 1 .4 1 1s-.4 1-1 1h-1a1 1 0 0 1 0-2Zm4 0c.6 0 1 .4 1 1s-.4 1-1 1a1 1 0 0 1 0-2ZM7 3v5h10V3c0-.6.4-1 1-1s1 .4 1 1v7H5V3c0-.6.4-1 1-1s1 .4 1 1ZM6 22a1 1 0 0 1-1-1v-7h14v7c0 .6-.4 1-1 1a1 1 0 0 1-1-1v-5H7v5c0 .6-.4 1-1 1Z"/></g></svg>',
|
||||
'paragraph': '<svg width="24" height="24"><path fill-rule="evenodd" d="M10 5h7a1 1 0 0 1 0 2h-1v11a1 1 0 0 1-2 0V7h-2v11a1 1 0 0 1-2 0v-6c-.5 0-1 0-1.4-.3A3.4 3.4 0 0 1 6.8 10a3.3 3.3 0 0 1 0-2.8 3.4 3.4 0 0 1 1.8-1.8L10 5Z"/></svg>',
|
||||
'paste-column-after': '<svg width="24" height="24"><path fill-rule="evenodd" d="M12 1a3 3 0 0 1 2.8 2H18c1 0 2 .8 2 1.9V7h-2V5h-2v1c0 .6-.4 1-1 1H9a1 1 0 0 1-1-1V5H6v13h7v2H6c-1 0-2-.8-2-1.9V5c0-1 .8-2 1.9-2H9.2A3 3 0 0 1 12 1Zm8 7v12h-6V8h6Zm-1.5 1.5h-3v9h3v-9ZM12 3a1 1 0 1 0 0 2 1 1 0 0 0 0-2Z"/></svg>',
|
||||
'paste-column-before': '<svg width="24" height="24"><path fill-rule="evenodd" d="M12 1a3 3 0 0 1 2.8 2H18c1 0 2 .8 2 1.9V18c0 1-.8 2-1.9 2H11v-2h7V5h-2v1c0 .6-.4 1-1 1H9a1 1 0 0 1-1-1V5H6v2H4V5c0-1 .8-2 1.9-2H9.2A3 3 0 0 1 12 1Zm-2 7v12H4V8h6ZM8.5 9.5h-3v9h3v-9ZM12 3a1 1 0 1 0 0 2 1 1 0 0 0 0-2Z"/></svg>',
|
||||
'paste-row-after': '<svg width="24" height="24"><path fill-rule="evenodd" d="M12 1a3 3 0 0 1 2.8 2H18c1 0 2 .8 2 1.9V11h-2V5h-2v1c0 .6-.4 1-1 1H9a1 1 0 0 1-1-1V5H6v13h14c0 1-.8 2-1.9 2H6c-1 0-2-.8-2-1.9V5c0-1 .8-2 1.9-2H9.2A3 3 0 0 1 12 1Zm10 11v5H8v-5h14Zm-1.5 1.5h-11v2h11v-2ZM12 3a1 1 0 1 0 0 2 1 1 0 0 0 0-2Z"/></svg>',
|
||||
'paste-row-before': '<svg width="24" height="24"><path fill-rule="evenodd" d="M12 1a3 3 0 0 1 2.8 2H18c1 0 2 .8 2 1.9V7h-2V5h-2v1c0 .6-.4 1-1 1H9a1 1 0 0 1-1-1V5H6v13h12v-4h2v4c0 1-.8 2-1.9 2H6c-1 0-2-.8-2-1.9V5c0-1 .8-2 1.9-2H9.2A3 3 0 0 1 12 1Zm10 7v5H8V8h14Zm-1.5 1.5h-11v2h11v-2ZM12 3a1 1 0 1 0 0 2 1 1 0 0 0 0-2Z"/></svg>',
|
||||
'paste-text': '<svg width="24" height="24"><path d="M18 9V5h-2v1c0 .6-.4 1-1 1H9a1 1 0 0 1-1-1V5H6v13h3V9h9ZM9 20H6a2 2 0 0 1-2-2V5c0-1.1.9-2 2-2h3.2A3 3 0 0 1 12 1a3 3 0 0 1 2.8 2H18a2 2 0 0 1 2 2v4h1v12H9v-1Zm1.5-9.5v9h9v-9h-9ZM12 3a1 1 0 0 0-1 1c0 .5.4 1 1 1s1-.5 1-1-.4-1-1-1Zm0 9h6v2h-.5l-.5-1h-1v4h.8v1h-3.6v-1h.8v-4h-1l-.5 1H12v-2Z" fill-rule="nonzero"/></svg>',
|
||||
'paste': '<svg width="24" height="24"><path d="M18 9V5h-2v1c0 .6-.4 1-1 1H9a1 1 0 0 1-1-1V5H6v13h3V9h9ZM9 20H6a2 2 0 0 1-2-2V5c0-1.1.9-2 2-2h3.2A3 3 0 0 1 12 1a3 3 0 0 1 2.8 2H18a2 2 0 0 1 2 2v4h1v12H9v-1Zm1.5-9.5v9h9v-9h-9ZM12 3a1 1 0 0 0-1 1c0 .5.4 1 1 1s1-.5 1-1-.4-1-1-1Z" fill-rule="nonzero"/></svg>',
|
||||
'permanent-pen': '<svg width="24" height="24"><path d="M10.5 17.5 8 20H3v-3l3.5-3.5a2 2 0 0 1 0-3L14 3l1 1-7.3 7.3a1 1 0 0 0 0 1.4l3.6 3.6c.4.4 1 .4 1.4 0L20 9l1 1-7.6 7.6a2 2 0 0 1-2.8 0l-.1-.1Z" fill-rule="nonzero"/></svg>',
|
||||
'plus': '<svg width="24" height="24"><path d="M12 4c.5 0 1 .4 1 .9V11h6a1 1 0 0 1 .1 2H13v6a1 1 0 0 1-2 .1V13H5a1 1 0 0 1-.1-2H11V5c0-.6.4-1 1-1Z"/></svg>',
|
||||
'preferences': '<svg width="24" height="24"><path d="m20.1 13.5-1.9.2a5.8 5.8 0 0 1-.6 1.5l1.2 1.5c.4.4.3 1 0 1.4l-.7.7a1 1 0 0 1-1.4 0l-1.5-1.2a6.2 6.2 0 0 1-1.5.6l-.2 1.9c0 .5-.5.9-1 .9h-1a1 1 0 0 1-1-.9l-.2-1.9a5.8 5.8 0 0 1-1.5-.6l-1.5 1.2a1 1 0 0 1-1.4 0l-.7-.7a1 1 0 0 1 0-1.4l1.2-1.5a6.2 6.2 0 0 1-.6-1.5l-1.9-.2a1 1 0 0 1-.9-1v-1c0-.5.4-1 .9-1l1.9-.2a5.8 5.8 0 0 1 .6-1.5L5.2 7.3a1 1 0 0 1 0-1.4l.7-.7a1 1 0 0 1 1.4 0l1.5 1.2a6.2 6.2 0 0 1 1.5-.6l.2-1.9c0-.5.5-.9 1-.9h1c.5 0 1 .4 1 .9l.2 1.9a5.8 5.8 0 0 1 1.5.6l1.5-1.2a1 1 0 0 1 1.4 0l.7.7c.3.4.4 1 0 1.4l-1.2 1.5a6.2 6.2 0 0 1 .6 1.5l1.9.2c.5 0 .9.5.9 1v1c0 .5-.4 1-.9 1ZM12 15a3 3 0 1 0 0-6 3 3 0 0 0 0 6Z" fill-rule="evenodd"/></svg>',
|
||||
'preview': '<svg width="24" height="24"><path d="M3.5 12.5c.5.8 1.1 1.6 1.8 2.3 2 2 4.2 3.2 6.7 3.2s4.7-1.2 6.7-3.2a16.2 16.2 0 0 0 2.1-2.8 15.7 15.7 0 0 0-2.1-2.8c-2-2-4.2-3.2-6.7-3.2a9.3 9.3 0 0 0-6.7 3.2A16.2 16.2 0 0 0 3.2 12c0 .2.2.3.3.5Zm-2.4-1 .7-1.2L4 7.8C6.2 5.4 8.9 4 12 4c3 0 5.8 1.4 8.1 3.8a18.2 18.2 0 0 1 2.8 3.7v1l-.7 1.2-2.1 2.5c-2.3 2.4-5 3.8-8.1 3.8-3 0-5.8-1.4-8.1-3.8a18.2 18.2 0 0 1-2.8-3.7 1 1 0 0 1 0-1Zm12-3.3a2 2 0 1 0 2.7 2.6 4 4 0 1 1-2.6-2.6Z" fill-rule="nonzero"/></svg>',
|
||||
'print': '<svg width="24" height="24"><path d="M18 8H6a3 3 0 0 0-3 3v6h2v3h14v-3h2v-6a3 3 0 0 0-3-3Zm-1 10H7v-4h10v4Zm.5-5c-.8 0-1.5-.7-1.5-1.5s.7-1.5 1.5-1.5 1.5.7 1.5 1.5-.7 1.5-1.5 1.5Zm.5-8H6v2h12V5Z" fill-rule="nonzero"/></svg>',
|
||||
'quote': '<svg width="24" height="24"><path d="M7.5 17h.9c.4 0 .7-.2.9-.6L11 13V8c0-.6-.4-1-1-1H6a1 1 0 0 0-1 1v4c0 .6.4 1 1 1h2l-1.3 2.7a1 1 0 0 0 .8 1.3Zm8 0h.9c.4 0 .7-.2.9-.6L19 13V8c0-.6-.4-1-1-1h-4a1 1 0 0 0-1 1v4c0 .6.4 1 1 1h2l-1.3 2.7a1 1 0 0 0 .8 1.3Z" fill-rule="nonzero"/></svg>',
|
||||
'redo': '<svg width="24" height="24"><path d="M17.6 10H12c-2.8 0-4.4 1.4-4.9 3.5-.4 2 .3 4 1.4 4.6a1 1 0 1 1-1 1.8c-2-1.2-2.9-4.1-2.3-6.8.6-3 3-5.1 6.8-5.1h5.6l-3.3-3.3a1 1 0 1 1 1.4-1.4l5 5a1 1 0 0 1 0 1.4l-5 5a1 1 0 0 1-1.4-1.4l3.3-3.3Z" fill-rule="nonzero"/></svg>',
|
||||
'reload': '<svg width="24" height="24"><g fill-rule="nonzero"><path d="m5 22.1-1.2-4.7v-.2a1 1 0 0 1 1-1l5 .4a1 1 0 1 1-.2 2l-2.2-.2a7.8 7.8 0 0 0 8.4.2 7.5 7.5 0 0 0 3.5-6.4 1 1 0 1 1 2 0 9.5 9.5 0 0 1-4.5 8 9.9 9.9 0 0 1-10.2 0l.4 1.4a1 1 0 1 1-2 .5ZM13.6 7.4c0-.5.5-1 1-.9l2.8.2a8 8 0 0 0-9.5-1 7.5 7.5 0 0 0-3.6 7 1 1 0 0 1-2 0 9.5 9.5 0 0 1 4.5-8.6 10 10 0 0 1 10.9.3l-.3-1a1 1 0 0 1 2-.5l1.1 4.8a1 1 0 0 1-1 1.2l-5-.4a1 1 0 0 1-.9-1Z"/></g></svg>',
|
||||
'remove-formatting': '<svg width="24" height="24"><path d="M13.2 6a1 1 0 0 1 0 .2l-2.6 10a1 1 0 0 1-1 .8h-.2a.8.8 0 0 1-.8-1l2.6-10H8a1 1 0 1 1 0-2h9a1 1 0 0 1 0 2h-3.8ZM5 18h7a1 1 0 0 1 0 2H5a1 1 0 0 1 0-2Zm13 1.5L16.5 18 15 19.5a.7.7 0 0 1-1-1l1.5-1.5-1.5-1.5a.7.7 0 0 1 1-1l1.5 1.5 1.5-1.5a.7.7 0 0 1 1 1L17.5 17l1.5 1.5a.7.7 0 0 1-1 1Z" fill-rule="evenodd"/></svg>',
|
||||
'remove': '<svg width="24" height="24"><path d="M16 7h3a1 1 0 0 1 0 2h-1v9a3 3 0 0 1-3 3H9a3 3 0 0 1-3-3V9H5a1 1 0 1 1 0-2h3V6a3 3 0 0 1 3-3h2a3 3 0 0 1 3 3v1Zm-2 0V6c0-.6-.4-1-1-1h-2a1 1 0 0 0-1 1v1h4Zm2 2H8v9c0 .6.4 1 1 1h6c.6 0 1-.4 1-1V9Zm-7 3a1 1 0 0 1 2 0v4a1 1 0 0 1-2 0v-4Zm4 0a1 1 0 0 1 2 0v4a1 1 0 0 1-2 0v-4Z" fill-rule="nonzero"/></svg>',
|
||||
'resize-handle': '<svg width="10" height="10"><g fill-rule="nonzero"><path d="M8.1 1.1A.5.5 0 1 1 9 2l-7 7A.5.5 0 1 1 1 8l7-7ZM8.1 5.1A.5.5 0 1 1 9 6l-3 3A.5.5 0 1 1 5 8l3-3Z"/></g></svg>',
|
||||
'resize': '<svg width="24" height="24"><path d="M4 5c0-.3.1-.5.3-.7.2-.2.4-.3.7-.3h6c.3 0 .5.1.7.3.2.2.3.4.3.7 0 .3-.1.5-.3.7a1 1 0 0 1-.7.3H7.4L18 16.6V13c0-.3.1-.5.3-.7.2-.2.4-.3.7-.3.3 0 .5.1.7.3.2.2.3.4.3.7v6c0 .3-.1.5-.3.7a1 1 0 0 1-.7.3h-6a1 1 0 0 1-.7-.3 1 1 0 0 1-.3-.7c0-.3.1-.5.3-.7.2-.2.4-.3.7-.3h3.6L6 7.4V11c0 .3-.1.5-.3.7a1 1 0 0 1-.7.3 1 1 0 0 1-.7-.3A1 1 0 0 1 4 11V5Z" fill-rule="evenodd"/></svg>',
|
||||
'restore-draft': '<svg width="24" height="24"><g fill-rule="evenodd"><path d="M17 13c0 .6-.4 1-1 1h-4V8c0-.6.4-1 1-1s1 .4 1 1v4h2c.6 0 1 .4 1 1Z"/><path d="M4.7 10H9a1 1 0 0 1 0 2H3a1 1 0 0 1-1-1V5a1 1 0 1 1 2 0v3l2.5-2.4a9.2 9.2 0 0 1 10.8-1.5A9 9 0 0 1 13.4 21c-2.4.1-4.7-.7-6.5-2.2a1 1 0 1 1 1.3-1.5 7.2 7.2 0 0 0 11.6-3.7 7 7 0 0 0-3.5-7.7A7.2 7.2 0 0 0 8 7L4.7 10Z" fill-rule="nonzero"/></g></svg>',
|
||||
'rotate-left': '<svg width="24" height="24"><path d="M4.7 10H9a1 1 0 0 1 0 2H3a1 1 0 0 1-1-1V5a1 1 0 1 1 2 0v3l2.5-2.4a9.2 9.2 0 0 1 10.8-1.5A9 9 0 0 1 13.4 21c-2.4.1-4.7-.7-6.5-2.2a1 1 0 1 1 1.3-1.5 7.2 7.2 0 0 0 11.6-3.7 7 7 0 0 0-3.5-7.7A7.2 7.2 0 0 0 8 7L4.7 10Z" fill-rule="nonzero"/></svg>',
|
||||
'rotate-right': '<svg width="24" height="24"><path d="M20 8V5a1 1 0 0 1 2 0v6c0 .6-.4 1-1 1h-6a1 1 0 0 1 0-2h4.3L16 7A7.2 7.2 0 0 0 7.7 6a7 7 0 0 0 3 13.1c1.9.1 3.7-.5 5-1.7a1 1 0 0 1 1.4 1.5A9.2 9.2 0 0 1 2.2 14c-.9-3.9 1-8 4.5-9.9 3.5-1.9 8-1.3 10.8 1.5L20 8Z" fill-rule="nonzero"/></svg>',
|
||||
'rtl': '<svg width="24" height="24"><path d="M8 5h8v2h-2v12h-2V7h-2v12H8v-7c-.5 0-1 0-1.4-.3A3.4 3.4 0 0 1 4.8 10a3.3 3.3 0 0 1 0-2.8 3.4 3.4 0 0 1 1.8-1.8L8 5Zm12 11.2a1 1 0 1 1-1 1.6l-3-2a1 1 0 0 1 0-1.6l3-2a1 1 0 1 1 1 1.6L18.4 15l1.8 1.2Z" fill-rule="evenodd"/></svg>',
|
||||
'save': '<svg width="24" height="24"><path d="M5 16h14a2 2 0 0 1 2 2v2a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-2c0-1.1.9-2 2-2Zm0 2v2h14v-2H5Zm10 0h2v2h-2v-2Zm-4-6.4L8.7 9.3a1 1 0 1 0-1.4 1.4l4 4c.4.4 1 .4 1.4 0l4-4a1 1 0 1 0-1.4-1.4L13 11.6V4a1 1 0 0 0-2 0v7.6Z" fill-rule="nonzero"/></svg>',
|
||||
'search': '<svg width="24" height="24"><path d="M16 17.3a8 8 0 1 1 1.4-1.4l4.3 4.4a1 1 0 0 1-1.4 1.4l-4.4-4.3Zm-5-.3a6 6 0 1 0 0-12 6 6 0 0 0 0 12Z" fill-rule="nonzero"/></svg>',
|
||||
'select-all': '<svg width="24" height="24"><path d="M3 5h2V3a2 2 0 0 0-2 2Zm0 8h2v-2H3v2Zm4 8h2v-2H7v2ZM3 9h2V7H3v2Zm10-6h-2v2h2V3Zm6 0v2h2a2 2 0 0 0-2-2ZM5 21v-2H3c0 1.1.9 2 2 2Zm-2-4h2v-2H3v2ZM9 3H7v2h2V3Zm2 18h2v-2h-2v2Zm8-8h2v-2h-2v2Zm0 8a2 2 0 0 0 2-2h-2v2Zm0-12h2V7h-2v2Zm0 8h2v-2h-2v2Zm-4 4h2v-2h-2v2Zm0-16h2V3h-2v2ZM7 17h10V7H7v10Zm2-8h6v6H9V9Z" fill-rule="nonzero"/></svg>',
|
||||
'selected': '<svg width="24" height="24"><path fill-rule="nonzero" d="M6 4h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2Zm3.6 10.9L7 12.3a.7.7 0 0 0-1 1L9.6 17 18 8.6a.7.7 0 0 0 0-1 .7.7 0 0 0-1 0l-7.4 7.3Z"/></svg>',
|
||||
'settings': '<svg width="24" height="24"><path d="M11 6h8c.6 0 1 .4 1 1s-.4 1-1 1h-8v.3c0 .2 0 .3-.2.5l-.6.2H7.8c-.3 0-.4 0-.6-.2a.7.7 0 0 1-.2-.6V8H5a1 1 0 1 1 0-2h2v-.3c0-.2 0-.3.2-.5l.5-.2h2.5c.3 0 .4 0 .6.2l.2.5V6ZM8 8h2V6H8v2Zm9 2.8v.2h2c.6 0 1 .4 1 1s-.4 1-1 1h-2v.3c0 .2 0 .3-.2.5l-.6.2h-2.4c-.3 0-.4 0-.6-.2a.7.7 0 0 1-.2-.6V13H5a1 1 0 0 1 0-2h8v-.3c0-.2 0-.3.2-.5l.6-.2h2.4c.3 0 .4 0 .6.2l.2.6ZM14 13h2v-2h-2v2Zm-3 2.8v.2h8c.6 0 1 .4 1 1s-.4 1-1 1h-8v.3c0 .2 0 .3-.2.5l-.6.2H7.8c-.3 0-.4 0-.6-.2a.7.7 0 0 1-.2-.6V18H5a1 1 0 0 1 0-2h2v-.3c0-.2 0-.3.2-.5l.5-.2h2.5c.3 0 .4 0 .6.2l.2.6ZM8 18h2v-2H8v2Z" fill-rule="evenodd"/></svg>',
|
||||
'sharpen': '<svg width="24" height="24"><path d="m16 6 4 4-8 9-8-9 4-4h8Zm-4 10.2 5.5-6.2-.1-.1H12v-.3h5.1l-.2-.2H12V9h4.6l-.2-.2H12v-.3h4.1l-.2-.2H12V8h3.6l-.2-.2H8.7L6.5 10l.1.1H12v.3H6.9l.2.2H12v.3H7.3l.2.2H12v.3H7.7l.3.2h4v.3H8.2l.2.2H12v.3H8.6l.3.2H12v.3H9l.3.2H12v.3H9.5l.2.2H12v.3h-2l.2.2H12v.3h-1.6l.2.2H12v.3h-1.1l.2.2h.9v.3h-.7l.2.2h.5v.3h-.3l.3.2Z" fill-rule="evenodd"/></svg>',
|
||||
'sourcecode': '<svg width="24" height="24"><g fill-rule="nonzero"><path d="M9.8 15.7c.3.3.3.8 0 1-.3.4-.9.4-1.2 0l-4.4-4.1a.8.8 0 0 1 0-1.2l4.4-4.2c.3-.3.9-.3 1.2 0 .3.3.3.8 0 1.1L6 12l3.8 3.7ZM14.2 15.7c-.3.3-.3.8 0 1 .4.4.9.4 1.2 0l4.4-4.1c.3-.3.3-.9 0-1.2l-4.4-4.2a.8.8 0 0 0-1.2 0c-.3.3-.3.8 0 1.1L18 12l-3.8 3.7Z"/></g></svg>',
|
||||
'spell-check': '<svg width="24" height="24"><path d="M6 8v3H5V5c0-.3.1-.5.3-.7.2-.2.4-.3.7-.3h2c.3 0 .5.1.7.3.2.2.3.4.3.7v6H8V8H6Zm0-3v2h2V5H6Zm13 0h-3v5h3v1h-3a1 1 0 0 1-.7-.3 1 1 0 0 1-.3-.7V5c0-.3.1-.5.3-.7.2-.2.4-.3.7-.3h3v1Zm-5 1.5-.1.7c-.1.2-.3.3-.6.3.3 0 .5.1.6.3l.1.7V10c0 .3-.1.5-.3.7a1 1 0 0 1-.7.3h-3V4h3c.3 0 .5.1.7.3.2.2.3.4.3.7v1.5ZM13 10V8h-2v2h2Zm0-3V5h-2v2h2Zm3 5 1 1-6.5 7L7 15.5l1.3-1 2.2 2.2L16 12Z" fill-rule="evenodd"/></svg>',
|
||||
'strike-through': '<svg width="24" height="24"><g fill-rule="evenodd"><path d="M15.6 8.5c-.5-.7-1-1.1-1.3-1.3-.6-.4-1.3-.6-2-.6-2.7 0-2.8 1.7-2.8 2.1 0 1.6 1.8 2 3.2 2.3 4.4.9 4.6 2.8 4.6 3.9 0 1.4-.7 4.1-5 4.1A6.2 6.2 0 0 1 7 16.4l1.5-1.1c.4.6 1.6 2 3.7 2 1.6 0 2.5-.4 3-1.2.4-.8.3-2-.8-2.6-.7-.4-1.6-.7-2.9-1-1-.2-3.9-.8-3.9-3.6C7.6 6 10.3 5 12.4 5c2.9 0 4.2 1.6 4.7 2.4l-1.5 1.1Z"/><path d="M5 11h14a1 1 0 0 1 0 2H5a1 1 0 0 1 0-2Z" fill-rule="nonzero"/></g></svg>',
|
||||
'subscript': '<svg width="24" height="24"><path d="m10.4 10 4.6 4.6-1.4 1.4L9 11.4 4.4 16 3 14.6 7.6 10 3 5.4 4.4 4 9 8.6 13.6 4 15 5.4 10.4 10ZM21 19h-5v-1l1-.8 1.7-1.6c.3-.4.5-.8.5-1.2 0-.3 0-.6-.2-.7-.2-.2-.5-.3-.9-.3a2 2 0 0 0-.8.2l-.7.3-.4-1.1 1-.6 1.2-.2c.8 0 1.4.3 1.8.7.4.4.6.9.6 1.5s-.2 1.1-.5 1.6a8 8 0 0 1-1.3 1.3l-.6.6h2.6V19Z" fill-rule="nonzero"/></svg>',
|
||||
'superscript': '<svg width="24" height="24"><path d="M15 9.4 10.4 14l4.6 4.6-1.4 1.4L9 15.4 4.4 20 3 18.6 7.6 14 3 9.4 4.4 8 9 12.6 13.6 8 15 9.4Zm5.9 1.6h-5v-1l1-.8 1.7-1.6c.3-.5.5-.9.5-1.3 0-.3 0-.5-.2-.7-.2-.2-.5-.3-.9-.3l-.8.2-.7.4-.4-1.2c.2-.2.5-.4 1-.5.3-.2.8-.2 1.2-.2.8 0 1.4.2 1.8.6.4.4.6 1 .6 1.6 0 .5-.2 1-.5 1.5l-1.3 1.4-.6.5h2.6V11Z" fill-rule="nonzero"/></svg>',
|
||||
'table-caption': '<svg width="24" height="24"><g fill-rule="nonzero"><rect width="12" height="2" x="3" y="4" rx="1"/><path d="M19 8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-8c0-1.1.9-2 2-2h14ZM5 15v3h6v-3H5Zm14 0h-6v3h6v-3Zm0-5h-6v3h6v-3ZM5 13h6v-3H5v3Z"/></g></svg>',
|
||||
'table-cell-classes': '<svg width="24" height="24"><g fill-rule="evenodd"><path fill-rule="nonzero" d="M13 4v9H3V6c0-1.1.9-2 2-2h8Zm-2 2H5v5h6V6Z"/><path fill-rule="nonzero" d="M13 4h6a2 2 0 0 1 2 2v7h-8v-2h6V6h-6V4Z" opacity=".2"/><path d="m18 20-2.6 1.6.7-3-2.4-2 3.1-.2 1.2-2.9 1.2 2.9 3.1.2-2.4 2 .7 3z"/><path fill-rule="nonzero" d="M3 13v5c0 1.1.9 2 2 2h8v-7h-2v5H5v-5H3Z" opacity=".2"/></g></svg>',
|
||||
'table-cell-properties': '<svg width="24" height="24"><path fill-rule="nonzero" d="M19 4a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2h14Zm-8 9H5v5h6v-5Zm8 0h-6v5h6v-5Zm-8-7H5v5h6V6Z"/></svg>',
|
||||
'table-cell-select-all': '<svg width="24" height="24"><g fill-rule="evenodd"><path fill-rule="nonzero" d="M19 4a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2h14Zm0 2H5v12h14V6Z"/><path d="M13 6v5h6v2h-6v5h-2v-5H5v-2h6V6h2Z" opacity=".2"/></g></svg>',
|
||||
'table-cell-select-inner': '<svg width="24" height="24"><g fill-rule="evenodd"><path fill-rule="nonzero" d="M19 4a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2h14Zm0 2H5v12h14V6Z" opacity=".2"/><path d="M13 6v5h6v2h-6v5h-2v-5H5v-2h6V6h2Z"/></g></svg>',
|
||||
'table-classes': '<svg width="24" height="24"><g fill-rule="evenodd"><path fill-rule="nonzero" d="M19 4a2 2 0 0 1 2 2v7h-8v7H5a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2h14Zm-8 9H5v5h6v-5Zm8-7h-6v5h6V6Zm-8 0H5v5h6V6Z"/><path d="m18 20-2.6 1.6.7-3-2.4-2 3.1-.2 1.2-2.9 1.2 2.9 3.1.2-2.4 2 .7 3z"/></g></svg>',
|
||||
'table-delete-column': '<svg width="24" height="24"><path fill-rule="nonzero" d="M19 4a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2h14Zm-4 4h-2V6h-2v2H9V6H5v12h4v-2h2v2h2v-2h2v2h4V6h-4v2Zm.3.5 1 1.2-3 2.3 3 2.3-1 1.2L12 13l-3.3 2.6-1-1.2 3-2.3-3-2.3 1-1.2L12 11l3.3-2.5Z"/></svg>',
|
||||
'table-delete-row': '<svg width="24" height="24"><path fill-rule="nonzero" d="M19 4a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2h14Zm0 2H5v3h2.5v2H5v2h2.5v2H5v3h14v-3h-2.5v-2H19v-2h-2.5V9H19V6Zm-4.7 1.8 1.2 1L13 12l2.6 3.3-1.2 1-2.3-3-2.3 3-1.2-1L11 12 8.5 8.7l1.2-1 2.3 3 2.3-3Z"/></svg>',
|
||||
'table-delete-table': '<svg width="24" height="24"><g fill-rule="nonzero"><path d="M19 4a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2h14ZM5 6v12h14V6H5Z"/><path d="m14.4 8.6 1.1 1-2.4 2.4 2.4 2.4-1.1 1.1-2.4-2.4-2.4 2.4-1-1.1 2.3-2.4-2.3-2.4 1-1 2.4 2.3z"/></g></svg>',
|
||||
'table-insert-column-after': '<svg width="24" height="24"><path fill-rule="nonzero" d="M20 4c.6 0 1 .4 1 1v2a1 1 0 0 1-2 0V6h-8v12h8v-1a1 1 0 0 1 2 0v2c0 .5-.4 1-.9 1H5a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2h15ZM9 13H5v5h4v-5Zm7-5c.5 0 1 .4 1 .9V11h2a1 1 0 0 1 .1 2H17v2a1 1 0 0 1-2 .1V13h-2a1 1 0 0 1-.1-2H15V9c0-.6.4-1 1-1ZM9 6H5v5h4V6Z"/></svg>',
|
||||
'table-insert-column-before': '<svg width="24" height="24"><path fill-rule="nonzero" d="M19 4a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H4a1 1 0 0 1-1-1v-2a1 1 0 0 1 2 0v1h8V6H5v1a1 1 0 1 1-2 0V5c0-.6.4-1 1-1h15Zm0 9h-4v5h4v-5ZM8 8c.5 0 1 .4 1 .9V11h2a1 1 0 0 1 .1 2H9v2a1 1 0 0 1-2 .1V13H5a1 1 0 0 1-.1-2H7V9c0-.6.4-1 1-1Zm11-2h-4v5h4V6Z"/></svg>',
|
||||
'table-insert-row-above': '<svg width="24" height="24"><path fill-rule="nonzero" d="M6 4a1 1 0 1 1 0 2H5v6h14V6h-1a1 1 0 0 1 0-2h2c.6 0 1 .4 1 1v13a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5c0-.6.4-1 1-1h2Zm5 10H5v4h6v-4Zm8 0h-6v4h6v-4ZM12 3c.5 0 1 .4 1 .9V6h2a1 1 0 0 1 0 2h-2v2a1 1 0 0 1-2 .1V8H9a1 1 0 0 1 0-2h2V4c0-.6.4-1 1-1Z"/></svg>',
|
||||
'table-insert-row-after': '<svg width="24" height="24"><path fill-rule="nonzero" d="M12 13c.5 0 1 .4 1 .9V16h2a1 1 0 0 1 .1 2H13v2a1 1 0 0 1-2 .1V18H9a1 1 0 0 1-.1-2H11v-2c0-.6.4-1 1-1Zm6 7a1 1 0 0 1 0-2h1v-6H5v6h1a1 1 0 0 1 0 2H4a1 1 0 0 1-1-1V6c0-1.1.9-2 2-2h14a2 2 0 0 1 2 2v13c0 .5-.4 1-.9 1H18ZM11 6H5v4h6V6Zm8 0h-6v4h6V6Z"/></svg>',
|
||||
'table-left-header': '<svg width="24" height="24"><path d="M19 4a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2h14Zm0 9h-4v5h4v-5Zm-6 0H9v5h4v-5Zm0-7H9v5h4V6Zm6 0h-4v5h4V6Z"/></svg>',
|
||||
'table-merge-cells': '<svg width="24" height="24"><path fill-rule="nonzero" d="M19 4a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2h14ZM5 15.5V18h3v-2.5H5Zm14-5h-9V18h9v-7.5ZM19 6h-4v2.5h4V6ZM8 6H5v2.5h3V6Zm5 0h-3v2.5h3V6Zm-8 7.5h3v-3H5v3Z"/></svg>',
|
||||
'table-row-numbering-rtl': '<svg width="24" height="24"><path d="M6 4a2 2 0 0 0-2 2v13c0 1.1.9 2 2 2h12a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2H6Zm0 12h8v3H6v-3Zm11 0c.6 0 1 .4 1 1v1a1 1 0 0 1-2 0v-1c0-.6.4-1 1-1ZM6 11h8v3H6v-3Zm11 0c.6 0 1 .4 1 1v1a1 1 0 0 1-2 0v-1c0-.6.4-1 1-1ZM6 6h8v3H6V6Zm11 0c.6 0 1 .4 1 1v1a1 1 0 1 1-2 0V7c0-.6.4-1 1-1Z"/></svg>',
|
||||
'table-row-numbering': '<svg width="24" height="24"><path d="M18 4a2 2 0 0 1 2 2v13a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2h12Zm0 12h-8v3h8v-3ZM7 16a1 1 0 0 0-1 1v1a1 1 0 0 0 2 0v-1c0-.6-.4-1-1-1Zm11-5h-8v3h8v-3ZM7 11a1 1 0 0 0-1 1v1a1 1 0 0 0 2 0v-1c0-.6-.4-1-1-1Zm11-5h-8v3h8V6ZM7 6a1 1 0 0 0-1 1v1a1 1 0 1 0 2 0V7c0-.6-.4-1-1-1Z"/></svg>',
|
||||
'table-row-properties': '<svg width="24" height="24"><path fill-rule="nonzero" d="M19 4a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2h14ZM5 15v3h6v-3H5Zm14 0h-6v3h6v-3Zm0-9h-6v3h6V6ZM5 9h6V6H5v3Z"/></svg>',
|
||||
'table-split-cells': '<svg width="24" height="24"><path fill-rule="nonzero" d="M19 4a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2h14ZM8 15.5H5V18h3v-2.5Zm11-5h-9V18h9v-7.5Zm-2.5 1 1 1-2 2 2 2-1 1-2-2-2 2-1-1 2-2-2-2 1-1 2 2 2-2Zm-8.5-1H5v3h3v-3ZM19 6h-4v2.5h4V6ZM8 6H5v2.5h3V6Zm5 0h-3v2.5h3V6Z"/></svg>',
|
||||
'table-top-header': '<svg width="24" height="24"><path d="M19 4a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2h14Zm-8 11H5v3h6v-3Zm8 0h-6v3h6v-3Zm0-5h-6v3h6v-3ZM5 13h6v-3H5v3Z"/></svg>',
|
||||
'table': '<svg width="24" height="24"><path fill-rule="nonzero" d="M19 4a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2h14ZM5 14v4h6v-4H5Zm14 0h-6v4h6v-4Zm0-6h-6v4h6V8ZM5 12h6V8H5v4Z"/></svg>',
|
||||
'template': '<svg width="24" height="24"><path d="M19 19v-1H5v1h14ZM9 16v-4a5 5 0 1 1 6 0v4h4a2 2 0 0 1 2 2v3H3v-3c0-1.1.9-2 2-2h4Zm4 0v-5l.8-.6a3 3 0 1 0-3.6 0l.8.6v5h2Z" fill-rule="nonzero"/></svg>',
|
||||
'temporary-placeholder': '<svg width="24" height="24"><g fill-rule="evenodd"><path d="M9 7.6V6h2.5V4.5a.5.5 0 1 1 1 0V6H15v1.6a8 8 0 1 1-6 0Zm-2.6 5.3a.5.5 0 0 0 .3.6c.3 0 .6 0 .6-.3l.1-.2a5 5 0 0 1 3.3-2.8c.3-.1.4-.4.4-.6-.1-.3-.4-.5-.6-.4a6 6 0 0 0-4.1 3.7Z"/><circle cx="14" cy="4" r="1"/><circle cx="12" cy="2" r="1"/><circle cx="10" cy="4" r="1"/></g></svg>',
|
||||
'text-color': '<svg width="24" height="24"><g fill-rule="evenodd"><path id="tox-icon-text-color__color" d="M3 18h18v3H3z"/><path d="M8.7 16h-.8a.5.5 0 0 1-.5-.6l2.7-9c.1-.3.3-.4.5-.4h2.8c.2 0 .4.1.5.4l2.7 9a.5.5 0 0 1-.5.6h-.8a.5.5 0 0 1-.4-.4l-.7-2.2c0-.3-.3-.4-.5-.4h-3.4c-.2 0-.4.1-.5.4l-.7 2.2c0 .3-.2.4-.4.4Zm2.6-7.6-.6 2a.5.5 0 0 0 .5.6h1.6a.5.5 0 0 0 .5-.6l-.6-2c0-.3-.3-.4-.5-.4h-.4c-.2 0-.4.1-.5.4Z"/></g></svg>',
|
||||
'toc': '<svg width="24" height="24"><path d="M5 5c.6 0 1 .4 1 1s-.4 1-1 1a1 1 0 1 1 0-2Zm3 0h11c.6 0 1 .4 1 1s-.4 1-1 1H8a1 1 0 1 1 0-2Zm-3 8c.6 0 1 .4 1 1s-.4 1-1 1a1 1 0 0 1 0-2Zm3 0h11c.6 0 1 .4 1 1s-.4 1-1 1H8a1 1 0 0 1 0-2Zm0-4c.6 0 1 .4 1 1s-.4 1-1 1a1 1 0 1 1 0-2Zm3 0h8c.6 0 1 .4 1 1s-.4 1-1 1h-8a1 1 0 0 1 0-2Zm-3 8c.6 0 1 .4 1 1s-.4 1-1 1a1 1 0 0 1 0-2Zm3 0h8c.6 0 1 .4 1 1s-.4 1-1 1h-8a1 1 0 0 1 0-2Z" fill-rule="evenodd"/></svg>',
|
||||
'translate': '<svg width="24" height="24"><path d="m12.7 14.3-.3.7-.4.7-2.2-2.2-3.1 3c-.3.4-.8.4-1 0a.7.7 0 0 1 0-1l3.1-3A12.4 12.4 0 0 1 6.7 9H8a10.1 10.1 0 0 0 1.7 2.4c.5-.5 1-1.1 1.4-1.8l.9-2H4.7a.7.7 0 1 1 0-1.5h4.4v-.7c0-.4.3-.8.7-.8.4 0 .7.4.7.8v.7H15c.4 0 .8.3.8.7 0 .4-.4.8-.8.8h-1.4a12.3 12.3 0 0 1-1 2.4 13.5 13.5 0 0 1-1.7 2.3l1.9 1.8Zm4.3-3 2.7 7.3a.5.5 0 0 1-.4.7 1 1 0 0 1-1-.7l-.6-1.5h-3.4l-.6 1.5a1 1 0 0 1-1 .7.5.5 0 0 1-.4-.7l2.7-7.4a1 1 0 0 1 2 0Zm-2.2 4.4h2.4L16 12.5l-1.2 3.2Z" fill-rule="evenodd"/></svg>',
|
||||
'underline': '<svg width="24" height="24"><path d="M16 5c.6 0 1 .4 1 1v5.5a4 4 0 0 1-.4 1.8l-1 1.4a5.3 5.3 0 0 1-5.5 1 5 5 0 0 1-1.6-1c-.5-.4-.8-.9-1.1-1.4a4 4 0 0 1-.4-1.8V6c0-.6.4-1 1-1s1 .4 1 1v5.5c0 .3 0 .6.2 1l.6.7a3.3 3.3 0 0 0 2.2.8 3.4 3.4 0 0 0 2.2-.8c.3-.2.4-.5.6-.8l.2-.9V6c0-.6.4-1 1-1ZM8 17h8c.6 0 1 .4 1 1s-.4 1-1 1H8a1 1 0 0 1 0-2Z" fill-rule="evenodd"/></svg>',
|
||||
'undo': '<svg width="24" height="24"><path d="M6.4 8H12c3.7 0 6.2 2 6.8 5.1.6 2.7-.4 5.6-2.3 6.8a1 1 0 0 1-1-1.8c1.1-.6 1.8-2.7 1.4-4.6-.5-2.1-2.1-3.5-4.9-3.5H6.4l3.3 3.3a1 1 0 1 1-1.4 1.4l-5-5a1 1 0 0 1 0-1.4l5-5a1 1 0 0 1 1.4 1.4L6.4 8Z" fill-rule="nonzero"/></svg>',
|
||||
'unlink': '<svg width="24" height="24"><path d="M6.2 12.3a1 1 0 0 1 1.4 1.4l-2 2a2 2 0 1 0 2.6 2.8l4.8-4.8a1 1 0 0 0 0-1.4 1 1 0 1 1 1.4-1.3 2.9 2.9 0 0 1 0 4L9.6 20a3.9 3.9 0 0 1-5.5-5.5l2-2Zm11.6-.6a1 1 0 0 1-1.4-1.4l2.1-2a2 2 0 1 0-2.7-2.8L11 10.3a1 1 0 0 0 0 1.4A1 1 0 1 1 9.6 13a2.9 2.9 0 0 1 0-4L14.4 4a3.9 3.9 0 0 1 5.5 5.5l-2 2ZM7.6 6.3a.8.8 0 0 1-1 1.1L3.3 4.2a.7.7 0 1 1 1-1l3.2 3.1ZM5.1 8.6a.8.8 0 0 1 0 1.5H3a.8.8 0 0 1 0-1.5H5Zm5-3.5a.8.8 0 0 1-1.5 0V3a.8.8 0 0 1 1.5 0V5Zm6 11.8a.8.8 0 0 1 1-1l3.2 3.2a.8.8 0 0 1-1 1L16 17Zm-2.2 2a.8.8 0 0 1 1.5 0V21a.8.8 0 0 1-1.5 0V19Zm5-3.5a.7.7 0 1 1 0-1.5H21a.8.8 0 0 1 0 1.5H19Z" fill-rule="nonzero"/></svg>',
|
||||
'unlock': '<svg width="24" height="24"><path d="M16 5c.8 0 1.5.3 2.1.9.6.6.9 1.3.9 2.1v3h-2V8a1 1 0 0 0-.3-.7A1 1 0 0 0 16 7h-2a1 1 0 0 0-.7.3 1 1 0 0 0-.3.7v3h.3c.2 0 .3 0 .5.2l.2.6v7.4c0 .3 0 .4-.2.6l-.6.2H4.8c-.3 0-.4 0-.6-.2a.7.7 0 0 1-.2-.6v-7.4c0-.3 0-.4.2-.6l.5-.2H11V8c0-.8.3-1.5.9-2.1.6-.6 1.3-.9 2.1-.9h2Z" fill-rule="evenodd"/></svg>',
|
||||
'unordered-list': '<svg width="24" height="24"><path d="M11 5h8c.6 0 1 .4 1 1s-.4 1-1 1h-8a1 1 0 0 1 0-2Zm0 6h8c.6 0 1 .4 1 1s-.4 1-1 1h-8a1 1 0 0 1 0-2Zm0 6h8c.6 0 1 .4 1 1s-.4 1-1 1h-8a1 1 0 0 1 0-2ZM4.5 6c0-.4.1-.8.4-1 .3-.4.7-.5 1.1-.5.4 0 .8.1 1 .4.4.3.5.7.5 1.1 0 .4-.1.8-.4 1-.3.4-.7.5-1.1.5-.4 0-.8-.1-1-.4-.4-.3-.5-.7-.5-1.1Zm0 6c0-.4.1-.8.4-1 .3-.4.7-.5 1.1-.5.4 0 .8.1 1 .4.4.3.5.7.5 1.1 0 .4-.1.8-.4 1-.3.4-.7.5-1.1.5-.4 0-.8-.1-1-.4-.4-.3-.5-.7-.5-1.1Zm0 6c0-.4.1-.8.4-1 .3-.4.7-.5 1.1-.5.4 0 .8.1 1 .4.4.3.5.7.5 1.1 0 .4-.1.8-.4 1-.3.4-.7.5-1.1.5-.4 0-.8-.1-1-.4-.4-.3-.5-.7-.5-1.1Z" fill-rule="evenodd"/></svg>',
|
||||
'unselected': '<svg width="24" height="24"><path fill-rule="nonzero" d="M6 4h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2Zm0 1a1 1 0 0 0-1 1v12c0 .6.4 1 1 1h12c.6 0 1-.4 1-1V6c0-.6-.4-1-1-1H6Z"/></svg>',
|
||||
'upload': '<svg width="24" height="24"><path d="M18 19v-2a1 1 0 0 1 2 0v3c0 .6-.4 1-1 1H5a1 1 0 0 1-1-1v-3a1 1 0 0 1 2 0v2h12ZM11 6.4 8.7 8.7a1 1 0 0 1-1.4-1.4l4-4a1 1 0 0 1 1.4 0l4 4a1 1 0 1 1-1.4 1.4L13 6.4V16a1 1 0 0 1-2 0V6.4Z" fill-rule="nonzero"/></svg>',
|
||||
'user': '<svg width="24" height="24"><path d="M12 24a12 12 0 1 1 0-24 12 12 0 0 1 0 24Zm-8.7-5.3a11 11 0 0 0 17.4 0C19.4 16.3 14.6 15 12 15c-2.6 0-7.4 1.3-8.7 3.7ZM12 13c2.2 0 4-2 4-4.5S14.2 4 12 4 8 6 8 8.5 9.8 13 12 13Z" fill-rule="nonzero"/></svg>',
|
||||
'vertical-align': '<svg width="24" height="24"><g fill-rule="nonzero"><rect width="18" height="2" x="3" y="11" rx="1"/><path d="M12 2c.6 0 1 .4 1 1v4l2-1.3a1 1 0 0 1 1.2 1.5l-.1.1-4.1 3-4-3a1 1 0 0 1 1-1.7l2 1.5V3c0-.6.4-1 1-1zm0 11.8 4 2.9a1 1 0 0 1-1 1.7l-2-1.5V21c0 .5-.4 1-.9 1H12a1 1 0 0 1-1-1v-4l-2 1.3a1 1 0 0 1-1.2-.1l-.1-.1a1 1 0 0 1 .1-1.3l.1-.1 4.1-3z"/></g></svg>',
|
||||
'visualblocks': '<svg width="24" height="24"><path d="M9 19v2H7v-2h2Zm-4 0v2a2 2 0 0 1-2-2h2Zm8 0v2h-2v-2h2Zm8 0a2 2 0 0 1-2 2v-2h2Zm-4 0v2h-2v-2h2ZM15 7a1 1 0 0 1 0 2v7a1 1 0 0 1-2 0V9h-1v7a1 1 0 0 1-2 0v-4a2.5 2.5 0 0 1-.2-5H15ZM5 15v2H3v-2h2Zm16 0v2h-2v-2h2ZM5 11v2H3v-2h2Zm16 0v2h-2v-2h2ZM5 7v2H3V7h2Zm16 0v2h-2V7h2ZM5 3v2H3c0-1.1.9-2 2-2Zm8 0v2h-2V3h2Zm6 0a2 2 0 0 1 2 2h-2V3ZM9 3v2H7V3h2Zm8 0v2h-2V3h2Z" fill-rule="evenodd"/></svg>',
|
||||
'visualchars': '<svg width="24" height="24"><path d="M10 5h7a1 1 0 0 1 0 2h-1v11a1 1 0 0 1-2 0V7h-2v11a1 1 0 0 1-2 0v-6c-.5 0-1 0-1.4-.3A3.4 3.4 0 0 1 6.8 10a3.3 3.3 0 0 1 0-2.8 3.4 3.4 0 0 1 1.8-1.8L10 5Z" fill-rule="evenodd"/></svg>',
|
||||
'warning': '<svg width="24" height="24"><path d="M19.8 18.3c.2.5.3.9 0 1.2-.1.3-.5.5-1 .5H5.2c-.5 0-.9-.2-1-.5-.3-.3-.2-.7 0-1.2L11 4.7l.5-.5.5-.2c.2 0 .3 0 .5.2.2 0 .3.3.5.5l6.8 13.6ZM12 18c.3 0 .5-.1.7-.3.2-.2.3-.4.3-.7a1 1 0 0 0-.3-.7 1 1 0 0 0-.7-.3 1 1 0 0 0-.7.3 1 1 0 0 0-.3.7c0 .3.1.5.3.7.2.2.4.3.7.3Zm.7-3 .3-4a1 1 0 0 0-.3-.7 1 1 0 0 0-.7-.3 1 1 0 0 0-.7.3 1 1 0 0 0-.3.7l.3 4h1.4Z" fill-rule="evenodd"/></svg>',
|
||||
'zoom-in': '<svg width="24" height="24"><path d="M16 17.3a8 8 0 1 1 1.4-1.4l4.3 4.4a1 1 0 0 1-1.4 1.4l-4.4-4.3Zm-5-.3a6 6 0 1 0 0-12 6 6 0 0 0 0 12Zm-1-9a1 1 0 0 1 2 0v6a1 1 0 0 1-2 0V8Zm-2 4a1 1 0 0 1 0-2h6a1 1 0 0 1 0 2H8Z" fill-rule="nonzero"/></svg>',
|
||||
'zoom-out': '<svg width="24" height="24"><path d="M16 17.3a8 8 0 1 1 1.4-1.4l4.3 4.4a1 1 0 0 1-1.4 1.4l-4.4-4.3Zm-5-.3a6 6 0 1 0 0-12 6 6 0 0 0 0 12Zm-3-5a1 1 0 0 1 0-2h6a1 1 0 0 1 0 2H8Z" fill-rule="nonzero"/></svg>',
|
||||
}
|
||||
});
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,3 @@
|
||||
This is where language files should be placed.
|
||||
|
||||
Please DO NOT translate these directly use this service: https://www.transifex.com/projects/p/tinymce/
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Ephox Corporation DBA Tiny Technologies, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -0,0 +1,253 @@
|
||||
/**
|
||||
* TinyMCE version 6.2.0 (2022-09-08)
|
||||
*/
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
var global$1 = tinymce.util.Tools.resolve('tinymce.PluginManager');
|
||||
|
||||
const applyListFormat = (editor, listName, styleValue) => {
|
||||
const cmd = listName === 'UL' ? 'InsertUnorderedList' : 'InsertOrderedList';
|
||||
editor.execCommand(cmd, false, styleValue === false ? null : { 'list-style-type': styleValue });
|
||||
};
|
||||
|
||||
const register$2 = editor => {
|
||||
editor.addCommand('ApplyUnorderedListStyle', (ui, value) => {
|
||||
applyListFormat(editor, 'UL', value['list-style-type']);
|
||||
});
|
||||
editor.addCommand('ApplyOrderedListStyle', (ui, value) => {
|
||||
applyListFormat(editor, 'OL', value['list-style-type']);
|
||||
});
|
||||
};
|
||||
|
||||
const option = name => editor => editor.options.get(name);
|
||||
const register$1 = editor => {
|
||||
const registerOption = editor.options.register;
|
||||
registerOption('advlist_number_styles', {
|
||||
processor: 'string[]',
|
||||
default: 'default,lower-alpha,lower-greek,lower-roman,upper-alpha,upper-roman'.split(',')
|
||||
});
|
||||
registerOption('advlist_bullet_styles', {
|
||||
processor: 'string[]',
|
||||
default: 'default,circle,square'.split(',')
|
||||
});
|
||||
};
|
||||
const getNumberStyles = option('advlist_number_styles');
|
||||
const getBulletStyles = option('advlist_bullet_styles');
|
||||
|
||||
const isNullable = a => a === null || a === undefined;
|
||||
const isNonNullable = a => !isNullable(a);
|
||||
|
||||
var global = tinymce.util.Tools.resolve('tinymce.util.Tools');
|
||||
|
||||
class Optional {
|
||||
constructor(tag, value) {
|
||||
this.tag = tag;
|
||||
this.value = value;
|
||||
}
|
||||
static some(value) {
|
||||
return new Optional(true, value);
|
||||
}
|
||||
static none() {
|
||||
return Optional.singletonNone;
|
||||
}
|
||||
fold(onNone, onSome) {
|
||||
if (this.tag) {
|
||||
return onSome(this.value);
|
||||
} else {
|
||||
return onNone();
|
||||
}
|
||||
}
|
||||
isSome() {
|
||||
return this.tag;
|
||||
}
|
||||
isNone() {
|
||||
return !this.tag;
|
||||
}
|
||||
map(mapper) {
|
||||
if (this.tag) {
|
||||
return Optional.some(mapper(this.value));
|
||||
} else {
|
||||
return Optional.none();
|
||||
}
|
||||
}
|
||||
bind(binder) {
|
||||
if (this.tag) {
|
||||
return binder(this.value);
|
||||
} else {
|
||||
return Optional.none();
|
||||
}
|
||||
}
|
||||
exists(predicate) {
|
||||
return this.tag && predicate(this.value);
|
||||
}
|
||||
forall(predicate) {
|
||||
return !this.tag || predicate(this.value);
|
||||
}
|
||||
filter(predicate) {
|
||||
if (!this.tag || predicate(this.value)) {
|
||||
return this;
|
||||
} else {
|
||||
return Optional.none();
|
||||
}
|
||||
}
|
||||
getOr(replacement) {
|
||||
return this.tag ? this.value : replacement;
|
||||
}
|
||||
or(replacement) {
|
||||
return this.tag ? this : replacement;
|
||||
}
|
||||
getOrThunk(thunk) {
|
||||
return this.tag ? this.value : thunk();
|
||||
}
|
||||
orThunk(thunk) {
|
||||
return this.tag ? this : thunk();
|
||||
}
|
||||
getOrDie(message) {
|
||||
if (!this.tag) {
|
||||
throw new Error(message ?? 'Called getOrDie on None');
|
||||
} else {
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
static from(value) {
|
||||
return isNonNullable(value) ? Optional.some(value) : Optional.none();
|
||||
}
|
||||
getOrNull() {
|
||||
return this.tag ? this.value : null;
|
||||
}
|
||||
getOrUndefined() {
|
||||
return this.value;
|
||||
}
|
||||
each(worker) {
|
||||
if (this.tag) {
|
||||
worker(this.value);
|
||||
}
|
||||
}
|
||||
toArray() {
|
||||
return this.tag ? [this.value] : [];
|
||||
}
|
||||
toString() {
|
||||
return this.tag ? `some(${ this.value })` : 'none()';
|
||||
}
|
||||
}
|
||||
Optional.singletonNone = new Optional(false);
|
||||
|
||||
const isChildOfBody = (editor, elm) => {
|
||||
return editor.dom.isChildOf(elm, editor.getBody());
|
||||
};
|
||||
const isTableCellNode = node => {
|
||||
return isNonNullable(node) && /^(TH|TD)$/.test(node.nodeName);
|
||||
};
|
||||
const isListNode = editor => node => {
|
||||
return isNonNullable(node) && /^(OL|UL|DL)$/.test(node.nodeName) && isChildOfBody(editor, node);
|
||||
};
|
||||
const getSelectedStyleType = editor => {
|
||||
const listElm = editor.dom.getParent(editor.selection.getNode(), 'ol,ul');
|
||||
const style = editor.dom.getStyle(listElm, 'listStyleType');
|
||||
return Optional.from(style);
|
||||
};
|
||||
const isWithinNonEditable = (editor, element) => element !== null && editor.dom.getContentEditableParent(element) === 'false';
|
||||
const isWithinNonEditableList = (editor, element) => {
|
||||
const parentList = editor.dom.getParent(element, 'ol,ul,dl');
|
||||
return isWithinNonEditable(editor, parentList);
|
||||
};
|
||||
|
||||
const findIndex = (list, predicate) => {
|
||||
for (let index = 0; index < list.length; index++) {
|
||||
const element = list[index];
|
||||
if (predicate(element)) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
const styleValueToText = styleValue => {
|
||||
return styleValue.replace(/\-/g, ' ').replace(/\b\w/g, chr => {
|
||||
return chr.toUpperCase();
|
||||
});
|
||||
};
|
||||
const normalizeStyleValue = styleValue => isNullable(styleValue) || styleValue === 'default' ? '' : styleValue;
|
||||
const isWithinList = (editor, e, nodeName) => {
|
||||
const tableCellIndex = findIndex(e.parents, isTableCellNode);
|
||||
const parents = tableCellIndex !== -1 ? e.parents.slice(0, tableCellIndex) : e.parents;
|
||||
const lists = global.grep(parents, isListNode(editor));
|
||||
return lists.length > 0 && lists[0].nodeName === nodeName;
|
||||
};
|
||||
const makeSetupHandler = (editor, nodeName) => api => {
|
||||
const nodeChangeHandler = e => {
|
||||
api.setActive(isWithinList(editor, e, nodeName));
|
||||
api.setEnabled(!isWithinNonEditableList(editor, e.element));
|
||||
};
|
||||
editor.on('NodeChange', nodeChangeHandler);
|
||||
return () => editor.off('NodeChange', nodeChangeHandler);
|
||||
};
|
||||
const addSplitButton = (editor, id, tooltip, cmd, nodeName, styles) => {
|
||||
editor.ui.registry.addSplitButton(id, {
|
||||
tooltip,
|
||||
icon: nodeName === 'OL' ? 'ordered-list' : 'unordered-list',
|
||||
presets: 'listpreview',
|
||||
columns: 3,
|
||||
fetch: callback => {
|
||||
const items = global.map(styles, styleValue => {
|
||||
const iconStyle = nodeName === 'OL' ? 'num' : 'bull';
|
||||
const iconName = styleValue === 'disc' || styleValue === 'decimal' ? 'default' : styleValue;
|
||||
const itemValue = normalizeStyleValue(styleValue);
|
||||
const displayText = styleValueToText(styleValue);
|
||||
return {
|
||||
type: 'choiceitem',
|
||||
value: itemValue,
|
||||
icon: 'list-' + iconStyle + '-' + iconName,
|
||||
text: displayText
|
||||
};
|
||||
});
|
||||
callback(items);
|
||||
},
|
||||
onAction: () => editor.execCommand(cmd),
|
||||
onItemAction: (_splitButtonApi, value) => {
|
||||
applyListFormat(editor, nodeName, value);
|
||||
},
|
||||
select: value => {
|
||||
const listStyleType = getSelectedStyleType(editor);
|
||||
return listStyleType.map(listStyle => value === listStyle).getOr(false);
|
||||
},
|
||||
onSetup: makeSetupHandler(editor, nodeName)
|
||||
});
|
||||
};
|
||||
const addButton = (editor, id, tooltip, cmd, nodeName, styleValue) => {
|
||||
editor.ui.registry.addToggleButton(id, {
|
||||
active: false,
|
||||
tooltip,
|
||||
icon: nodeName === 'OL' ? 'ordered-list' : 'unordered-list',
|
||||
onSetup: makeSetupHandler(editor, nodeName),
|
||||
onAction: () => editor.queryCommandState(cmd) || styleValue === '' ? editor.execCommand(cmd) : applyListFormat(editor, nodeName, styleValue)
|
||||
});
|
||||
};
|
||||
const addControl = (editor, id, tooltip, cmd, nodeName, styles) => {
|
||||
if (styles.length > 1) {
|
||||
addSplitButton(editor, id, tooltip, cmd, nodeName, styles);
|
||||
} else {
|
||||
addButton(editor, id, tooltip, cmd, nodeName, normalizeStyleValue(styles[0]));
|
||||
}
|
||||
};
|
||||
const register = editor => {
|
||||
addControl(editor, 'numlist', 'Numbered list', 'InsertOrderedList', 'OL', getNumberStyles(editor));
|
||||
addControl(editor, 'bullist', 'Bullet list', 'InsertUnorderedList', 'UL', getBulletStyles(editor));
|
||||
};
|
||||
|
||||
var Plugin = () => {
|
||||
global$1.add('advlist', editor => {
|
||||
if (editor.hasPlugin('lists')) {
|
||||
register$1(editor);
|
||||
register(editor);
|
||||
register$2(editor);
|
||||
} else {
|
||||
console.error('Please use the Lists plugin together with the Advanced List plugin.');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Plugin();
|
||||
|
||||
})();
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* TinyMCE version 6.2.0 (2022-09-08)
|
||||
*/
|
||||
!function(){"use strict";var t=tinymce.util.Tools.resolve("tinymce.PluginManager");const e=(t,e,r)=>{const s="UL"===e?"InsertUnorderedList":"InsertOrderedList";t.execCommand(s,!1,!1===r?null:{"list-style-type":r})},r=t=>e=>e.options.get(t),s=r("advlist_number_styles"),n=r("advlist_bullet_styles"),l=t=>null==t,i=t=>!l(t);var o=tinymce.util.Tools.resolve("tinymce.util.Tools");class a{constructor(t,e){this.tag=t,this.value=e}static some(t){return new a(!0,t)}static none(){return a.singletonNone}fold(t,e){return this.tag?e(this.value):t()}isSome(){return this.tag}isNone(){return!this.tag}map(t){return this.tag?a.some(t(this.value)):a.none()}bind(t){return this.tag?t(this.value):a.none()}exists(t){return this.tag&&t(this.value)}forall(t){return!this.tag||t(this.value)}filter(t){return!this.tag||t(this.value)?this:a.none()}getOr(t){return this.tag?this.value:t}or(t){return this.tag?this:t}getOrThunk(t){return this.tag?this.value:t()}orThunk(t){return this.tag?this:t()}getOrDie(t){if(this.tag)return this.value;throw new Error(t??"Called getOrDie on None")}static from(t){return i(t)?a.some(t):a.none()}getOrNull(){return this.tag?this.value:null}getOrUndefined(){return this.value}each(t){this.tag&&t(this.value)}toArray(){return this.tag?[this.value]:[]}toString(){return this.tag?`some(${this.value})`:"none()"}}a.singletonNone=new a(!1);const u=t=>i(t)&&/^(TH|TD)$/.test(t.nodeName),d=t=>l(t)||"default"===t?"":t,g=(t,e)=>r=>{const s=s=>{r.setActive(((t,e,r)=>{const s=((t,e)=>{for(let r=0;r<t.length;r++)if(e(t[r]))return r;return-1})(e.parents,u),n=-1!==s?e.parents.slice(0,s):e.parents,l=o.grep(n,(t=>e=>i(e)&&/^(OL|UL|DL)$/.test(e.nodeName)&&((t,e)=>t.dom.isChildOf(e,t.getBody()))(t,e))(t));return l.length>0&&l[0].nodeName===r})(t,s,e)),r.setEnabled(!((t,e)=>{const r=t.dom.getParent(e,"ol,ul,dl");return((t,e)=>null!==e&&"false"===t.dom.getContentEditableParent(e))(t,r)})(t,s.element))};return t.on("NodeChange",s),()=>t.off("NodeChange",s)},h=(t,r,s,n,l,i)=>{i.length>1?((t,r,s,n,l,i)=>{t.ui.registry.addSplitButton(r,{tooltip:s,icon:"OL"===l?"ordered-list":"unordered-list",presets:"listpreview",columns:3,fetch:t=>{t(o.map(i,(t=>{const e="OL"===l?"num":"bull",r="disc"===t||"decimal"===t?"default":t,s=d(t),n=(t=>t.replace(/\-/g," ").replace(/\b\w/g,(t=>t.toUpperCase())))(t);return{type:"choiceitem",value:s,icon:"list-"+e+"-"+r,text:n}})))},onAction:()=>t.execCommand(n),onItemAction:(r,s)=>{e(t,l,s)},select:e=>{const r=(t=>{const e=t.dom.getParent(t.selection.getNode(),"ol,ul"),r=t.dom.getStyle(e,"listStyleType");return a.from(r)})(t);return r.map((t=>e===t)).getOr(!1)},onSetup:g(t,l)})})(t,r,s,n,l,i):((t,r,s,n,l,i)=>{t.ui.registry.addToggleButton(r,{active:!1,tooltip:s,icon:"OL"===l?"ordered-list":"unordered-list",onSetup:g(t,l),onAction:()=>t.queryCommandState(n)||""===i?t.execCommand(n):e(t,l,i)})})(t,r,s,n,l,d(i[0]))};t.add("advlist",(t=>{t.hasPlugin("lists")?((t=>{const e=t.options.register;e("advlist_number_styles",{processor:"string[]",default:"default,lower-alpha,lower-greek,lower-roman,upper-alpha,upper-roman".split(",")}),e("advlist_bullet_styles",{processor:"string[]",default:"default,circle,square".split(",")})})(t),(t=>{h(t,"numlist","Numbered list","InsertOrderedList","OL",s(t)),h(t,"bullist","Bullet list","InsertUnorderedList","UL",n(t))})(t),(t=>{t.addCommand("ApplyUnorderedListStyle",((r,s)=>{e(t,"UL",s["list-style-type"])})),t.addCommand("ApplyOrderedListStyle",((r,s)=>{e(t,"OL",s["list-style-type"])}))})(t)):console.error("Please use the Lists plugin together with the Advanced List plugin.")}))}();
|
||||
@@ -0,0 +1,196 @@
|
||||
/**
|
||||
* TinyMCE version 6.2.0 (2022-09-08)
|
||||
*/
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
var global$2 = tinymce.util.Tools.resolve('tinymce.PluginManager');
|
||||
|
||||
var global$1 = tinymce.util.Tools.resolve('tinymce.dom.RangeUtils');
|
||||
|
||||
var global = tinymce.util.Tools.resolve('tinymce.util.Tools');
|
||||
|
||||
const option = name => editor => editor.options.get(name);
|
||||
const register$2 = editor => {
|
||||
const registerOption = editor.options.register;
|
||||
registerOption('allow_html_in_named_anchor', {
|
||||
processor: 'boolean',
|
||||
default: false
|
||||
});
|
||||
};
|
||||
const allowHtmlInNamedAnchor = option('allow_html_in_named_anchor');
|
||||
|
||||
const namedAnchorSelector = 'a:not([href])';
|
||||
const isEmptyString = str => !str;
|
||||
const getIdFromAnchor = elm => {
|
||||
const id = elm.getAttribute('id') || elm.getAttribute('name');
|
||||
return id || '';
|
||||
};
|
||||
const isAnchor = elm => elm.nodeName.toLowerCase() === 'a';
|
||||
const isNamedAnchor = elm => isAnchor(elm) && !elm.getAttribute('href') && getIdFromAnchor(elm) !== '';
|
||||
const isEmptyNamedAnchor = elm => isNamedAnchor(elm) && !elm.firstChild;
|
||||
|
||||
const removeEmptyNamedAnchorsInSelection = editor => {
|
||||
const dom = editor.dom;
|
||||
global$1(dom).walk(editor.selection.getRng(), nodes => {
|
||||
global.each(nodes, node => {
|
||||
if (isEmptyNamedAnchor(node)) {
|
||||
dom.remove(node, false);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
const isValidId = id => /^[A-Za-z][A-Za-z0-9\-:._]*$/.test(id);
|
||||
const getNamedAnchor = editor => editor.dom.getParent(editor.selection.getStart(), namedAnchorSelector);
|
||||
const getId = editor => {
|
||||
const anchor = getNamedAnchor(editor);
|
||||
if (anchor) {
|
||||
return getIdFromAnchor(anchor);
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
};
|
||||
const createAnchor = (editor, id) => {
|
||||
editor.undoManager.transact(() => {
|
||||
if (!allowHtmlInNamedAnchor(editor)) {
|
||||
editor.selection.collapse(true);
|
||||
}
|
||||
if (editor.selection.isCollapsed()) {
|
||||
editor.insertContent(editor.dom.createHTML('a', { id }));
|
||||
} else {
|
||||
removeEmptyNamedAnchorsInSelection(editor);
|
||||
editor.formatter.remove('namedAnchor', undefined, undefined, true);
|
||||
editor.formatter.apply('namedAnchor', { value: id });
|
||||
editor.addVisual();
|
||||
}
|
||||
});
|
||||
};
|
||||
const updateAnchor = (editor, id, anchorElement) => {
|
||||
anchorElement.removeAttribute('name');
|
||||
anchorElement.id = id;
|
||||
editor.addVisual();
|
||||
editor.undoManager.add();
|
||||
};
|
||||
const insert = (editor, id) => {
|
||||
const anchor = getNamedAnchor(editor);
|
||||
if (anchor) {
|
||||
updateAnchor(editor, id, anchor);
|
||||
} else {
|
||||
createAnchor(editor, id);
|
||||
}
|
||||
editor.focus();
|
||||
};
|
||||
|
||||
const insertAnchor = (editor, newId) => {
|
||||
if (!isValidId(newId)) {
|
||||
editor.windowManager.alert('ID should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.');
|
||||
return false;
|
||||
} else {
|
||||
insert(editor, newId);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
const open = editor => {
|
||||
const currentId = getId(editor);
|
||||
editor.windowManager.open({
|
||||
title: 'Anchor',
|
||||
size: 'normal',
|
||||
body: {
|
||||
type: 'panel',
|
||||
items: [{
|
||||
name: 'id',
|
||||
type: 'input',
|
||||
label: 'ID',
|
||||
placeholder: 'example'
|
||||
}]
|
||||
},
|
||||
buttons: [
|
||||
{
|
||||
type: 'cancel',
|
||||
name: 'cancel',
|
||||
text: 'Cancel'
|
||||
},
|
||||
{
|
||||
type: 'submit',
|
||||
name: 'save',
|
||||
text: 'Save',
|
||||
primary: true
|
||||
}
|
||||
],
|
||||
initialData: { id: currentId },
|
||||
onSubmit: api => {
|
||||
if (insertAnchor(editor, api.getData().id)) {
|
||||
api.close();
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const register$1 = editor => {
|
||||
editor.addCommand('mceAnchor', () => {
|
||||
open(editor);
|
||||
});
|
||||
};
|
||||
|
||||
const isNamedAnchorNode = node => isEmptyString(node.attr('href')) && !isEmptyString(node.attr('id') || node.attr('name'));
|
||||
const isEmptyNamedAnchorNode = node => isNamedAnchorNode(node) && !node.firstChild;
|
||||
const setContentEditable = state => nodes => {
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
const node = nodes[i];
|
||||
if (isEmptyNamedAnchorNode(node)) {
|
||||
node.attr('contenteditable', state);
|
||||
}
|
||||
}
|
||||
};
|
||||
const setup = editor => {
|
||||
editor.on('PreInit', () => {
|
||||
editor.parser.addNodeFilter('a', setContentEditable('false'));
|
||||
editor.serializer.addNodeFilter('a', setContentEditable(null));
|
||||
});
|
||||
};
|
||||
|
||||
const registerFormats = editor => {
|
||||
editor.formatter.register('namedAnchor', {
|
||||
inline: 'a',
|
||||
selector: namedAnchorSelector,
|
||||
remove: 'all',
|
||||
split: true,
|
||||
deep: true,
|
||||
attributes: { id: '%value' },
|
||||
onmatch: (node, _fmt, _itemName) => {
|
||||
return isNamedAnchor(node);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const register = editor => {
|
||||
const onAction = () => editor.execCommand('mceAnchor');
|
||||
editor.ui.registry.addToggleButton('anchor', {
|
||||
icon: 'bookmark',
|
||||
tooltip: 'Anchor',
|
||||
onAction,
|
||||
onSetup: buttonApi => editor.selection.selectorChangedWithUnbind('a:not([href])', buttonApi.setActive).unbind
|
||||
});
|
||||
editor.ui.registry.addMenuItem('anchor', {
|
||||
icon: 'bookmark',
|
||||
text: 'Anchor...',
|
||||
onAction
|
||||
});
|
||||
};
|
||||
|
||||
var Plugin = () => {
|
||||
global$2.add('anchor', editor => {
|
||||
register$2(editor);
|
||||
setup(editor);
|
||||
register$1(editor);
|
||||
register(editor);
|
||||
editor.on('PreInit', () => {
|
||||
registerFormats(editor);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
Plugin();
|
||||
|
||||
})();
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* TinyMCE version 6.2.0 (2022-09-08)
|
||||
*/
|
||||
!function(){"use strict";var e=tinymce.util.Tools.resolve("tinymce.PluginManager"),t=tinymce.util.Tools.resolve("tinymce.dom.RangeUtils"),o=tinymce.util.Tools.resolve("tinymce.util.Tools");const n=("allow_html_in_named_anchor",e=>e.options.get("allow_html_in_named_anchor"));const a="a:not([href])",r=e=>!e,i=e=>e.getAttribute("id")||e.getAttribute("name")||"",l=e=>(e=>"a"===e.nodeName.toLowerCase())(e)&&!e.getAttribute("href")&&""!==i(e),s=e=>e.dom.getParent(e.selection.getStart(),a),d=(e,a)=>{const r=s(e);r?((e,t,o)=>{o.removeAttribute("name"),o.id=t,e.addVisual(),e.undoManager.add()})(e,a,r):((e,a)=>{e.undoManager.transact((()=>{n(e)||e.selection.collapse(!0),e.selection.isCollapsed()?e.insertContent(e.dom.createHTML("a",{id:a})):((e=>{const n=e.dom;t(n).walk(e.selection.getRng(),(e=>{o.each(e,(e=>{var t;l(t=e)&&!t.firstChild&&n.remove(e,!1)}))}))})(e),e.formatter.remove("namedAnchor",void 0,void 0,!0),e.formatter.apply("namedAnchor",{value:a}),e.addVisual())}))})(e,a),e.focus()},c=e=>(e=>r(e.attr("href"))&&!r(e.attr("id")||e.attr("name")))(e)&&!e.firstChild,m=e=>t=>{for(let o=0;o<t.length;o++){const n=t[o];c(n)&&n.attr("contenteditable",e)}};e.add("anchor",(e=>{(e=>{(0,e.options.register)("allow_html_in_named_anchor",{processor:"boolean",default:!1})})(e),(e=>{e.on("PreInit",(()=>{e.parser.addNodeFilter("a",m("false")),e.serializer.addNodeFilter("a",m(null))}))})(e),(e=>{e.addCommand("mceAnchor",(()=>{(e=>{const t=(e=>{const t=s(e);return t?i(t):""})(e);e.windowManager.open({title:"Anchor",size:"normal",body:{type:"panel",items:[{name:"id",type:"input",label:"ID",placeholder:"example"}]},buttons:[{type:"cancel",name:"cancel",text:"Cancel"},{type:"submit",name:"save",text:"Save",primary:!0}],initialData:{id:t},onSubmit:t=>{((e,t)=>/^[A-Za-z][A-Za-z0-9\-:._]*$/.test(t)?(d(e,t),!0):(e.windowManager.alert("ID should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores."),!1))(e,t.getData().id)&&t.close()}})})(e)}))})(e),(e=>{const t=()=>e.execCommand("mceAnchor");e.ui.registry.addToggleButton("anchor",{icon:"bookmark",tooltip:"Anchor",onAction:t,onSetup:t=>e.selection.selectorChangedWithUnbind("a:not([href])",t.setActive).unbind}),e.ui.registry.addMenuItem("anchor",{icon:"bookmark",text:"Anchor...",onAction:t})})(e),e.on("PreInit",(()=>{(e=>{e.formatter.register("namedAnchor",{inline:"a",selector:a,remove:"all",split:!0,deep:!0,attributes:{id:"%value"},onmatch:(e,t,o)=>l(e)})})(e)}))}))}();
|
||||
@@ -0,0 +1,226 @@
|
||||
/**
|
||||
* TinyMCE version 6.2.0 (2022-09-08)
|
||||
*/
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
var global$1 = tinymce.util.Tools.resolve('tinymce.PluginManager');
|
||||
|
||||
const link = () => /(?:[A-Za-z][A-Za-z\d.+-]{0,14}:\/\/(?:[-.~*+=!&;:'%@?^${}(),\w]+@)?|www\.|[-;:&=+$,.\w]+@)[A-Za-z\d-]+(?:\.[A-Za-z\d-]+)*(?::\d+)?(?:\/(?:[-.~*+=!;:'%@$(),\/\w]*[-~*+=%@$()\/\w])?)?(?:\?(?:[-.~*+=!&;:'%@?^${}(),\/\w]+))?(?:#(?:[-.~*+=!&;:'%@?^${}(),\/\w]+))?/g;
|
||||
|
||||
const option = name => editor => editor.options.get(name);
|
||||
const register = editor => {
|
||||
const registerOption = editor.options.register;
|
||||
registerOption('autolink_pattern', {
|
||||
processor: 'regexp',
|
||||
default: new RegExp('^' + link().source + '$', 'i')
|
||||
});
|
||||
registerOption('link_default_target', { processor: 'string' });
|
||||
registerOption('link_default_protocol', {
|
||||
processor: 'string',
|
||||
default: 'https'
|
||||
});
|
||||
};
|
||||
const getAutoLinkPattern = option('autolink_pattern');
|
||||
const getDefaultLinkTarget = option('link_default_target');
|
||||
const getDefaultLinkProtocol = option('link_default_protocol');
|
||||
const allowUnsafeLinkTarget = option('allow_unsafe_link_target');
|
||||
|
||||
const hasProto = (v, constructor, predicate) => {
|
||||
if (predicate(v, constructor.prototype)) {
|
||||
return true;
|
||||
} else {
|
||||
return v.constructor?.name === constructor.name;
|
||||
}
|
||||
};
|
||||
const typeOf = x => {
|
||||
const t = typeof x;
|
||||
if (x === null) {
|
||||
return 'null';
|
||||
} else if (t === 'object' && Array.isArray(x)) {
|
||||
return 'array';
|
||||
} else if (t === 'object' && hasProto(x, String, (o, proto) => proto.isPrototypeOf(o))) {
|
||||
return 'string';
|
||||
} else {
|
||||
return t;
|
||||
}
|
||||
};
|
||||
const isType = type => value => typeOf(value) === type;
|
||||
const eq = t => a => t === a;
|
||||
const isString = isType('string');
|
||||
const isUndefined = eq(undefined);
|
||||
const isNullable = a => a === null || a === undefined;
|
||||
const isNonNullable = a => !isNullable(a);
|
||||
|
||||
const not = f => t => !f(t);
|
||||
|
||||
const hasOwnProperty = Object.hasOwnProperty;
|
||||
const has = (obj, key) => hasOwnProperty.call(obj, key);
|
||||
|
||||
const checkRange = (str, substr, start) => substr === '' || str.length >= substr.length && str.substr(start, start + substr.length) === substr;
|
||||
const contains = (str, substr, start = 0, end) => {
|
||||
const idx = str.indexOf(substr, start);
|
||||
if (idx !== -1) {
|
||||
return isUndefined(end) ? true : idx + substr.length <= end;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
const startsWith = (str, prefix) => {
|
||||
return checkRange(str, prefix, 0);
|
||||
};
|
||||
|
||||
const zeroWidth = '\uFEFF';
|
||||
const isZwsp = char => char === zeroWidth;
|
||||
const removeZwsp = s => s.replace(/\uFEFF/g, '');
|
||||
|
||||
var global = tinymce.util.Tools.resolve('tinymce.dom.TextSeeker');
|
||||
|
||||
const isTextNode = node => node.nodeType === 3;
|
||||
const isElement = node => node.nodeType === 1;
|
||||
const isBracketOrSpace = char => /^[(\[{ \u00a0]$/.test(char);
|
||||
const hasProtocol = url => /^([A-Za-z][A-Za-z\d.+-]*:\/\/)|mailto:/.test(url);
|
||||
const isPunctuation = char => /[?!,.;:]/.test(char);
|
||||
const findChar = (text, index, predicate) => {
|
||||
for (let i = index - 1; i >= 0; i--) {
|
||||
const char = text.charAt(i);
|
||||
if (!isZwsp(char) && predicate(char)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
const freefallRtl = (container, offset) => {
|
||||
let tempNode = container;
|
||||
let tempOffset = offset;
|
||||
while (isElement(tempNode) && tempNode.childNodes[tempOffset]) {
|
||||
tempNode = tempNode.childNodes[tempOffset];
|
||||
tempOffset = isTextNode(tempNode) ? tempNode.data.length : tempNode.childNodes.length;
|
||||
}
|
||||
return {
|
||||
container: tempNode,
|
||||
offset: tempOffset
|
||||
};
|
||||
};
|
||||
|
||||
const parseCurrentLine = (editor, offset) => {
|
||||
const voidElements = editor.schema.getVoidElements();
|
||||
const autoLinkPattern = getAutoLinkPattern(editor);
|
||||
const {dom, selection} = editor;
|
||||
if (dom.getParent(selection.getNode(), 'a[href]') !== null) {
|
||||
return null;
|
||||
}
|
||||
const rng = selection.getRng();
|
||||
const textSeeker = global(dom, node => {
|
||||
return dom.isBlock(node) || has(voidElements, node.nodeName.toLowerCase()) || dom.getContentEditable(node) === 'false';
|
||||
});
|
||||
const {
|
||||
container: endContainer,
|
||||
offset: endOffset
|
||||
} = freefallRtl(rng.endContainer, rng.endOffset);
|
||||
const root = dom.getParent(endContainer, dom.isBlock) ?? dom.getRoot();
|
||||
const endSpot = textSeeker.backwards(endContainer, endOffset + offset, (node, offset) => {
|
||||
const text = node.data;
|
||||
const idx = findChar(text, offset, not(isBracketOrSpace));
|
||||
return idx === -1 || isPunctuation(text[idx]) ? idx : idx + 1;
|
||||
}, root);
|
||||
if (!endSpot) {
|
||||
return null;
|
||||
}
|
||||
let lastTextNode = endSpot.container;
|
||||
const startSpot = textSeeker.backwards(endSpot.container, endSpot.offset, (node, offset) => {
|
||||
lastTextNode = node;
|
||||
const idx = findChar(node.data, offset, isBracketOrSpace);
|
||||
return idx === -1 ? idx : idx + 1;
|
||||
}, root);
|
||||
const newRng = dom.createRng();
|
||||
if (!startSpot) {
|
||||
newRng.setStart(lastTextNode, 0);
|
||||
} else {
|
||||
newRng.setStart(startSpot.container, startSpot.offset);
|
||||
}
|
||||
newRng.setEnd(endSpot.container, endSpot.offset);
|
||||
const rngText = removeZwsp(newRng.toString());
|
||||
const matches = rngText.match(autoLinkPattern);
|
||||
if (matches) {
|
||||
let url = matches[0];
|
||||
if (startsWith(url, 'www.')) {
|
||||
const protocol = getDefaultLinkProtocol(editor);
|
||||
url = protocol + '://' + url;
|
||||
} else if (contains(url, '@') && !hasProtocol(url)) {
|
||||
url = 'mailto:' + url;
|
||||
}
|
||||
return {
|
||||
rng: newRng,
|
||||
url
|
||||
};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
const convertToLink = (editor, result) => {
|
||||
const {dom, selection} = editor;
|
||||
const {rng, url} = result;
|
||||
const bookmark = selection.getBookmark();
|
||||
selection.setRng(rng);
|
||||
const command = 'createlink';
|
||||
const args = {
|
||||
command,
|
||||
ui: false,
|
||||
value: url
|
||||
};
|
||||
const beforeExecEvent = editor.dispatch('BeforeExecCommand', args);
|
||||
if (!beforeExecEvent.isDefaultPrevented()) {
|
||||
editor.getDoc().execCommand(command, false, url);
|
||||
editor.dispatch('ExecCommand', args);
|
||||
const defaultLinkTarget = getDefaultLinkTarget(editor);
|
||||
if (isString(defaultLinkTarget)) {
|
||||
const anchor = selection.getNode();
|
||||
dom.setAttrib(anchor, 'target', defaultLinkTarget);
|
||||
if (defaultLinkTarget === '_blank' && !allowUnsafeLinkTarget(editor)) {
|
||||
dom.setAttrib(anchor, 'rel', 'noopener');
|
||||
}
|
||||
}
|
||||
}
|
||||
selection.moveToBookmark(bookmark);
|
||||
editor.nodeChanged();
|
||||
};
|
||||
const handleSpacebar = editor => {
|
||||
const result = parseCurrentLine(editor, -1);
|
||||
if (isNonNullable(result)) {
|
||||
convertToLink(editor, result);
|
||||
}
|
||||
};
|
||||
const handleBracket = handleSpacebar;
|
||||
const handleEnter = editor => {
|
||||
const result = parseCurrentLine(editor, 0);
|
||||
if (isNonNullable(result)) {
|
||||
convertToLink(editor, result);
|
||||
}
|
||||
};
|
||||
const setup = editor => {
|
||||
editor.on('keydown', e => {
|
||||
if (e.keyCode === 13 && !e.isDefaultPrevented()) {
|
||||
handleEnter(editor);
|
||||
}
|
||||
});
|
||||
editor.on('keyup', e => {
|
||||
if (e.keyCode === 32) {
|
||||
handleSpacebar(editor);
|
||||
} else if (e.keyCode === 48 && e.shiftKey || e.keyCode === 221) {
|
||||
handleBracket(editor);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var Plugin = () => {
|
||||
global$1.add('autolink', editor => {
|
||||
register(editor);
|
||||
setup(editor);
|
||||
});
|
||||
};
|
||||
|
||||
Plugin();
|
||||
|
||||
})();
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* TinyMCE version 6.2.0 (2022-09-08)
|
||||
*/
|
||||
!function(){"use strict";var e=tinymce.util.Tools.resolve("tinymce.PluginManager");const t=e=>t=>t.options.get(e),n=t("autolink_pattern"),o=t("link_default_target"),r=t("link_default_protocol"),a=t("allow_unsafe_link_target"),s=("string",e=>"string"===(e=>{const t=typeof e;return null===e?"null":"object"===t&&Array.isArray(e)?"array":"object"===t&&(n=o=e,(r=String).prototype.isPrototypeOf(n)||o.constructor?.name===r.name)?"string":t;var n,o,r})(e));const l=(void 0,e=>undefined===e);const c=e=>!(e=>null==e)(e),i=Object.hasOwnProperty,d=e=>"\ufeff"===e;var u=tinymce.util.Tools.resolve("tinymce.dom.TextSeeker");const f=e=>/^[(\[{ \u00a0]$/.test(e),g=(e,t,n)=>{for(let o=t-1;o>=0;o--){const t=e.charAt(o);if(!d(t)&&n(t))return o}return-1},m=(e,t)=>{const o=e.schema.getVoidElements(),a=n(e),{dom:s,selection:c}=e;if(null!==s.getParent(c.getNode(),"a[href]"))return null;const d=c.getRng(),m=u(s,(e=>{return s.isBlock(e)||(t=o,n=e.nodeName.toLowerCase(),i.call(t,n))||"false"===s.getContentEditable(e);var t,n})),{container:k,offset:p}=((e,t)=>{let n=e,o=t;for(;1===n.nodeType&&n.childNodes[o];)n=n.childNodes[o],o=3===n.nodeType?n.data.length:n.childNodes.length;return{container:n,offset:o}})(d.endContainer,d.endOffset),y=s.getParent(k,s.isBlock)??s.getRoot(),h=m.backwards(k,p+t,((e,t)=>{const n=e.data,o=g(n,t,(r=f,e=>!r(e)));var r,a;return-1===o||(a=n[o],/[?!,.;:]/.test(a))?o:o+1}),y);if(!h)return null;let w=h.container;const _=m.backwards(h.container,h.offset,((e,t)=>{w=e;const n=g(e.data,t,f);return-1===n?n:n+1}),y),v=s.createRng();_?v.setStart(_.container,_.offset):v.setStart(w,0),v.setEnd(h.container,h.offset);const A=v.toString().replace(/\uFEFF/g,"").match(a);if(A){let t=A[0];return b="www.",(C=t).length>=b.length&&C.substr(0,0+b.length)===b?t=r(e)+"://"+t:((e,t,n=0,o)=>{const r=e.indexOf(t,n);return-1!==r&&(!!l(o)||r+t.length<=o)})(t,"@")&&!(e=>/^([A-Za-z][A-Za-z\d.+-]*:\/\/)|mailto:/.test(e))(t)&&(t="mailto:"+t),{rng:v,url:t}}var C,b;return null},k=(e,t)=>{const{dom:n,selection:r}=e,{rng:l,url:c}=t,i=r.getBookmark();r.setRng(l);const d="createlink",u={command:d,ui:!1,value:c};if(!e.dispatch("BeforeExecCommand",u).isDefaultPrevented()){e.getDoc().execCommand(d,!1,c),e.dispatch("ExecCommand",u);const t=o(e);if(s(t)){const o=r.getNode();n.setAttrib(o,"target",t),"_blank"!==t||a(e)||n.setAttrib(o,"rel","noopener")}}r.moveToBookmark(i),e.nodeChanged()},p=e=>{const t=m(e,-1);c(t)&&k(e,t)},y=p;e.add("autolink",(e=>{(e=>{const t=e.options.register;t("autolink_pattern",{processor:"regexp",default:new RegExp("^"+/(?:[A-Za-z][A-Za-z\d.+-]{0,14}:\/\/(?:[-.~*+=!&;:'%@?^${}(),\w]+@)?|www\.|[-;:&=+$,.\w]+@)[A-Za-z\d-]+(?:\.[A-Za-z\d-]+)*(?::\d+)?(?:\/(?:[-.~*+=!;:'%@$(),\/\w]*[-~*+=%@$()\/\w])?)?(?:\?(?:[-.~*+=!&;:'%@?^${}(),\/\w]+))?(?:#(?:[-.~*+=!&;:'%@?^${}(),\/\w]+))?/g.source+"$","i")}),t("link_default_target",{processor:"string"}),t("link_default_protocol",{processor:"string",default:"https"})})(e),(e=>{e.on("keydown",(t=>{13!==t.keyCode||t.isDefaultPrevented()||(e=>{const t=m(e,0);c(t)&&k(e,t)})(e)})),e.on("keyup",(t=>{32===t.keyCode?p(e):(48===t.keyCode&&t.shiftKey||221===t.keyCode)&&y(e)}))})(e)}))}();
|
||||
@@ -0,0 +1,155 @@
|
||||
/**
|
||||
* TinyMCE version 6.2.0 (2022-09-08)
|
||||
*/
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
const Cell = initial => {
|
||||
let value = initial;
|
||||
const get = () => {
|
||||
return value;
|
||||
};
|
||||
const set = v => {
|
||||
value = v;
|
||||
};
|
||||
return {
|
||||
get,
|
||||
set
|
||||
};
|
||||
};
|
||||
|
||||
var global$1 = tinymce.util.Tools.resolve('tinymce.PluginManager');
|
||||
|
||||
var global = tinymce.util.Tools.resolve('tinymce.Env');
|
||||
|
||||
const fireResizeEditor = editor => editor.dispatch('ResizeEditor');
|
||||
|
||||
const option = name => editor => editor.options.get(name);
|
||||
const register$1 = editor => {
|
||||
const registerOption = editor.options.register;
|
||||
registerOption('autoresize_overflow_padding', {
|
||||
processor: 'number',
|
||||
default: 1
|
||||
});
|
||||
registerOption('autoresize_bottom_margin', {
|
||||
processor: 'number',
|
||||
default: 50
|
||||
});
|
||||
};
|
||||
const getMinHeight = option('min_height');
|
||||
const getMaxHeight = option('max_height');
|
||||
const getAutoResizeOverflowPadding = option('autoresize_overflow_padding');
|
||||
const getAutoResizeBottomMargin = option('autoresize_bottom_margin');
|
||||
|
||||
const isFullscreen = editor => editor.plugins.fullscreen && editor.plugins.fullscreen.isFullscreen();
|
||||
const toggleScrolling = (editor, state) => {
|
||||
const body = editor.getBody();
|
||||
if (body) {
|
||||
body.style.overflowY = state ? '' : 'hidden';
|
||||
if (!state) {
|
||||
body.scrollTop = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
const parseCssValueToInt = (dom, elm, name, computed) => {
|
||||
const value = parseInt(dom.getStyle(elm, name, computed) ?? '', 10);
|
||||
return isNaN(value) ? 0 : value;
|
||||
};
|
||||
const shouldScrollIntoView = trigger => {
|
||||
if (trigger?.type.toLowerCase() === 'setcontent') {
|
||||
const setContentEvent = trigger;
|
||||
return setContentEvent.selection === true || setContentEvent.paste === true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
const resize = (editor, oldSize, trigger) => {
|
||||
const dom = editor.dom;
|
||||
const doc = editor.getDoc();
|
||||
if (!doc) {
|
||||
return;
|
||||
}
|
||||
if (isFullscreen(editor)) {
|
||||
toggleScrolling(editor, true);
|
||||
return;
|
||||
}
|
||||
const docEle = doc.documentElement;
|
||||
const resizeBottomMargin = getAutoResizeBottomMargin(editor);
|
||||
const minHeight = getMinHeight(editor) ?? editor.getElement().offsetHeight;
|
||||
let resizeHeight = minHeight;
|
||||
const marginTop = parseCssValueToInt(dom, docEle, 'margin-top', true);
|
||||
const marginBottom = parseCssValueToInt(dom, docEle, 'margin-bottom', true);
|
||||
let contentHeight = docEle.offsetHeight + marginTop + marginBottom + resizeBottomMargin;
|
||||
if (contentHeight < 0) {
|
||||
contentHeight = 0;
|
||||
}
|
||||
const containerHeight = editor.getContainer().offsetHeight;
|
||||
const contentAreaHeight = editor.getContentAreaContainer().offsetHeight;
|
||||
const chromeHeight = containerHeight - contentAreaHeight;
|
||||
if (contentHeight + chromeHeight > minHeight) {
|
||||
resizeHeight = contentHeight + chromeHeight;
|
||||
}
|
||||
const maxHeight = getMaxHeight(editor);
|
||||
if (maxHeight && resizeHeight > maxHeight) {
|
||||
resizeHeight = maxHeight;
|
||||
toggleScrolling(editor, true);
|
||||
} else {
|
||||
toggleScrolling(editor, false);
|
||||
}
|
||||
if (resizeHeight !== oldSize.get()) {
|
||||
const deltaSize = resizeHeight - oldSize.get();
|
||||
dom.setStyle(editor.getContainer(), 'height', resizeHeight + 'px');
|
||||
oldSize.set(resizeHeight);
|
||||
fireResizeEditor(editor);
|
||||
if (global.browser.isSafari() && (global.os.isMacOS() || global.os.isiOS())) {
|
||||
const win = editor.getWin();
|
||||
win.scrollTo(win.pageXOffset, win.pageYOffset);
|
||||
}
|
||||
if (editor.hasFocus() && shouldScrollIntoView(trigger)) {
|
||||
editor.selection.scrollIntoView();
|
||||
}
|
||||
if ((global.browser.isSafari() || global.browser.isChromium()) && deltaSize < 0) {
|
||||
resize(editor, oldSize, trigger);
|
||||
}
|
||||
}
|
||||
};
|
||||
const setup = (editor, oldSize) => {
|
||||
editor.on('init', () => {
|
||||
const overflowPadding = getAutoResizeOverflowPadding(editor);
|
||||
const dom = editor.dom;
|
||||
dom.setStyles(editor.getDoc().documentElement, { height: 'auto' });
|
||||
dom.setStyles(editor.getBody(), {
|
||||
'paddingLeft': overflowPadding,
|
||||
'paddingRight': overflowPadding,
|
||||
'min-height': 0
|
||||
});
|
||||
});
|
||||
editor.on('NodeChange SetContent keyup FullscreenStateChanged ResizeContent', e => {
|
||||
resize(editor, oldSize, e);
|
||||
});
|
||||
};
|
||||
|
||||
const register = (editor, oldSize) => {
|
||||
editor.addCommand('mceAutoResize', () => {
|
||||
resize(editor, oldSize);
|
||||
});
|
||||
};
|
||||
|
||||
var Plugin = () => {
|
||||
global$1.add('autoresize', editor => {
|
||||
register$1(editor);
|
||||
if (!editor.options.isSet('resize')) {
|
||||
editor.options.set('resize', false);
|
||||
}
|
||||
if (!editor.inline) {
|
||||
const oldSize = Cell(0);
|
||||
register(editor, oldSize);
|
||||
setup(editor, oldSize);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Plugin();
|
||||
|
||||
})();
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* TinyMCE version 6.2.0 (2022-09-08)
|
||||
*/
|
||||
!function(){"use strict";var e=tinymce.util.Tools.resolve("tinymce.PluginManager"),t=tinymce.util.Tools.resolve("tinymce.Env");const o=e=>t=>t.options.get(e),s=o("min_height"),n=o("max_height"),i=o("autoresize_overflow_padding"),r=o("autoresize_bottom_margin"),a=(e,t)=>{const o=e.getBody();o&&(o.style.overflowY=t?"":"hidden",t||(o.scrollTop=0))},l=(e,t,o,s)=>{const n=parseInt(e.getStyle(t,o,s)??"",10);return isNaN(n)?0:n},g=(e,o,i)=>{const c=e.dom,u=e.getDoc();if(!u)return;if((e=>e.plugins.fullscreen&&e.plugins.fullscreen.isFullscreen())(e))return void a(e,!0);const f=u.documentElement,d=r(e),m=s(e)??e.getElement().offsetHeight;let p=m;const h=l(c,f,"margin-top",!0),y=l(c,f,"margin-bottom",!0);let C=f.offsetHeight+h+y+d;C<0&&(C=0);const S=e.getContainer().offsetHeight-e.getContentAreaContainer().offsetHeight;C+S>m&&(p=C+S);const z=n(e);if(z&&p>z?(p=z,a(e,!0)):a(e,!1),p!==o.get()){const s=p-o.get();if(c.setStyle(e.getContainer(),"height",p+"px"),o.set(p),(e=>{e.dispatch("ResizeEditor")})(e),t.browser.isSafari()&&(t.os.isMacOS()||t.os.isiOS())){const t=e.getWin();t.scrollTo(t.pageXOffset,t.pageYOffset)}e.hasFocus()&&(e=>{if("setcontent"===e?.type.toLowerCase()){const t=e;return!0===t.selection||!0===t.paste}return!1})(i)&&e.selection.scrollIntoView(),(t.browser.isSafari()||t.browser.isChromium())&&s<0&&g(e,o,i)}};e.add("autoresize",(e=>{if((e=>{const t=e.options.register;t("autoresize_overflow_padding",{processor:"number",default:1}),t("autoresize_bottom_margin",{processor:"number",default:50})})(e),e.options.isSet("resize")||e.options.set("resize",!1),!e.inline){const t=(e=>{let t=0;return{get:()=>t,set:e=>{t=e}}})();((e,t)=>{e.addCommand("mceAutoResize",(()=>{g(e,t)}))})(e,t),((e,t)=>{e.on("init",(()=>{const t=i(e),o=e.dom;o.setStyles(e.getDoc().documentElement,{height:"auto"}),o.setStyles(e.getBody(),{paddingLeft:t,paddingRight:t,"min-height":0})})),e.on("NodeChange SetContent keyup FullscreenStateChanged ResizeContent",(o=>{g(e,t,o)}))})(e,t)}}))}();
|
||||
@@ -0,0 +1,230 @@
|
||||
/**
|
||||
* TinyMCE version 6.2.0 (2022-09-08)
|
||||
*/
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
var global$4 = tinymce.util.Tools.resolve('tinymce.PluginManager');
|
||||
|
||||
const hasProto = (v, constructor, predicate) => {
|
||||
if (predicate(v, constructor.prototype)) {
|
||||
return true;
|
||||
} else {
|
||||
return v.constructor?.name === constructor.name;
|
||||
}
|
||||
};
|
||||
const typeOf = x => {
|
||||
const t = typeof x;
|
||||
if (x === null) {
|
||||
return 'null';
|
||||
} else if (t === 'object' && Array.isArray(x)) {
|
||||
return 'array';
|
||||
} else if (t === 'object' && hasProto(x, String, (o, proto) => proto.isPrototypeOf(o))) {
|
||||
return 'string';
|
||||
} else {
|
||||
return t;
|
||||
}
|
||||
};
|
||||
const isType = type => value => typeOf(value) === type;
|
||||
const eq = t => a => t === a;
|
||||
const isString = isType('string');
|
||||
const isUndefined = eq(undefined);
|
||||
|
||||
var global$3 = tinymce.util.Tools.resolve('tinymce.util.Delay');
|
||||
|
||||
var global$2 = tinymce.util.Tools.resolve('tinymce.util.LocalStorage');
|
||||
|
||||
var global$1 = tinymce.util.Tools.resolve('tinymce.util.Tools');
|
||||
|
||||
const fireRestoreDraft = editor => editor.dispatch('RestoreDraft');
|
||||
const fireStoreDraft = editor => editor.dispatch('StoreDraft');
|
||||
const fireRemoveDraft = editor => editor.dispatch('RemoveDraft');
|
||||
|
||||
const parse = timeString => {
|
||||
const multiples = {
|
||||
s: 1000,
|
||||
m: 60000
|
||||
};
|
||||
const parsedTime = /^(\d+)([ms]?)$/.exec(timeString);
|
||||
return (parsedTime && parsedTime[2] ? multiples[parsedTime[2]] : 1) * parseInt(timeString, 10);
|
||||
};
|
||||
|
||||
const option = name => editor => editor.options.get(name);
|
||||
const register$1 = editor => {
|
||||
const registerOption = editor.options.register;
|
||||
const timeProcessor = value => {
|
||||
const valid = isString(value);
|
||||
if (valid) {
|
||||
return {
|
||||
value: parse(value),
|
||||
valid
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
valid: false,
|
||||
message: 'Must be a string.'
|
||||
};
|
||||
}
|
||||
};
|
||||
registerOption('autosave_ask_before_unload', {
|
||||
processor: 'boolean',
|
||||
default: true
|
||||
});
|
||||
registerOption('autosave_prefix', {
|
||||
processor: 'string',
|
||||
default: 'tinymce-autosave-{path}{query}{hash}-{id}-'
|
||||
});
|
||||
registerOption('autosave_restore_when_empty', {
|
||||
processor: 'boolean',
|
||||
default: false
|
||||
});
|
||||
registerOption('autosave_interval', {
|
||||
processor: timeProcessor,
|
||||
default: '30s'
|
||||
});
|
||||
registerOption('autosave_retention', {
|
||||
processor: timeProcessor,
|
||||
default: '20m'
|
||||
});
|
||||
};
|
||||
const shouldAskBeforeUnload = option('autosave_ask_before_unload');
|
||||
const shouldRestoreWhenEmpty = option('autosave_restore_when_empty');
|
||||
const getAutoSaveInterval = option('autosave_interval');
|
||||
const getAutoSaveRetention = option('autosave_retention');
|
||||
const getAutoSavePrefix = editor => {
|
||||
const location = document.location;
|
||||
return editor.options.get('autosave_prefix').replace(/{path}/g, location.pathname).replace(/{query}/g, location.search).replace(/{hash}/g, location.hash).replace(/{id}/g, editor.id);
|
||||
};
|
||||
|
||||
const isEmpty = (editor, html) => {
|
||||
if (isUndefined(html)) {
|
||||
return editor.dom.isEmpty(editor.getBody());
|
||||
} else {
|
||||
const trimmedHtml = global$1.trim(html);
|
||||
if (trimmedHtml === '') {
|
||||
return true;
|
||||
} else {
|
||||
const fragment = new DOMParser().parseFromString(trimmedHtml, 'text/html');
|
||||
return editor.dom.isEmpty(fragment);
|
||||
}
|
||||
}
|
||||
};
|
||||
const hasDraft = editor => {
|
||||
const time = parseInt(global$2.getItem(getAutoSavePrefix(editor) + 'time') ?? '0', 10) || 0;
|
||||
if (new Date().getTime() - time > getAutoSaveRetention(editor)) {
|
||||
removeDraft(editor, false);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
const removeDraft = (editor, fire) => {
|
||||
const prefix = getAutoSavePrefix(editor);
|
||||
global$2.removeItem(prefix + 'draft');
|
||||
global$2.removeItem(prefix + 'time');
|
||||
if (fire !== false) {
|
||||
fireRemoveDraft(editor);
|
||||
}
|
||||
};
|
||||
const storeDraft = editor => {
|
||||
const prefix = getAutoSavePrefix(editor);
|
||||
if (!isEmpty(editor) && editor.isDirty()) {
|
||||
global$2.setItem(prefix + 'draft', editor.getContent({
|
||||
format: 'raw',
|
||||
no_events: true
|
||||
}));
|
||||
global$2.setItem(prefix + 'time', new Date().getTime().toString());
|
||||
fireStoreDraft(editor);
|
||||
}
|
||||
};
|
||||
const restoreDraft = editor => {
|
||||
const prefix = getAutoSavePrefix(editor);
|
||||
if (hasDraft(editor)) {
|
||||
editor.setContent(global$2.getItem(prefix + 'draft') ?? '', { format: 'raw' });
|
||||
fireRestoreDraft(editor);
|
||||
}
|
||||
};
|
||||
const startStoreDraft = editor => {
|
||||
const interval = getAutoSaveInterval(editor);
|
||||
global$3.setEditorInterval(editor, () => {
|
||||
storeDraft(editor);
|
||||
}, interval);
|
||||
};
|
||||
const restoreLastDraft = editor => {
|
||||
editor.undoManager.transact(() => {
|
||||
restoreDraft(editor);
|
||||
removeDraft(editor);
|
||||
});
|
||||
editor.focus();
|
||||
};
|
||||
|
||||
const get = editor => ({
|
||||
hasDraft: () => hasDraft(editor),
|
||||
storeDraft: () => storeDraft(editor),
|
||||
restoreDraft: () => restoreDraft(editor),
|
||||
removeDraft: fire => removeDraft(editor, fire),
|
||||
isEmpty: html => isEmpty(editor, html)
|
||||
});
|
||||
|
||||
var global = tinymce.util.Tools.resolve('tinymce.EditorManager');
|
||||
|
||||
const setup = editor => {
|
||||
editor.editorManager.on('BeforeUnload', e => {
|
||||
let msg;
|
||||
global$1.each(global.get(), editor => {
|
||||
if (editor.plugins.autosave) {
|
||||
editor.plugins.autosave.storeDraft();
|
||||
}
|
||||
if (!msg && editor.isDirty() && shouldAskBeforeUnload(editor)) {
|
||||
msg = editor.translate('You have unsaved changes are you sure you want to navigate away?');
|
||||
}
|
||||
});
|
||||
if (msg) {
|
||||
e.preventDefault();
|
||||
e.returnValue = msg;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const makeSetupHandler = editor => api => {
|
||||
api.setEnabled(hasDraft(editor));
|
||||
const editorEventCallback = () => api.setEnabled(hasDraft(editor));
|
||||
editor.on('StoreDraft RestoreDraft RemoveDraft', editorEventCallback);
|
||||
return () => editor.off('StoreDraft RestoreDraft RemoveDraft', editorEventCallback);
|
||||
};
|
||||
const register = editor => {
|
||||
startStoreDraft(editor);
|
||||
const onAction = () => {
|
||||
restoreLastDraft(editor);
|
||||
};
|
||||
editor.ui.registry.addButton('restoredraft', {
|
||||
tooltip: 'Restore last draft',
|
||||
icon: 'restore-draft',
|
||||
onAction,
|
||||
onSetup: makeSetupHandler(editor)
|
||||
});
|
||||
editor.ui.registry.addMenuItem('restoredraft', {
|
||||
text: 'Restore last draft',
|
||||
icon: 'restore-draft',
|
||||
onAction,
|
||||
onSetup: makeSetupHandler(editor)
|
||||
});
|
||||
};
|
||||
|
||||
var Plugin = () => {
|
||||
global$4.add('autosave', editor => {
|
||||
register$1(editor);
|
||||
setup(editor);
|
||||
register(editor);
|
||||
editor.on('init', () => {
|
||||
if (shouldRestoreWhenEmpty(editor) && editor.dom.isEmpty(editor.getBody())) {
|
||||
restoreDraft(editor);
|
||||
}
|
||||
});
|
||||
return get(editor);
|
||||
});
|
||||
};
|
||||
|
||||
Plugin();
|
||||
|
||||
})();
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* TinyMCE version 6.2.0 (2022-09-08)
|
||||
*/
|
||||
!function(){"use strict";var t=tinymce.util.Tools.resolve("tinymce.PluginManager");const e=("string",t=>"string"===(t=>{const e=typeof t;return null===t?"null":"object"===e&&Array.isArray(t)?"array":"object"===e&&(r=o=t,(a=String).prototype.isPrototypeOf(r)||o.constructor?.name===a.name)?"string":e;var r,o,a})(t));const r=(void 0,t=>undefined===t);var o=tinymce.util.Tools.resolve("tinymce.util.Delay"),a=tinymce.util.Tools.resolve("tinymce.util.LocalStorage"),s=tinymce.util.Tools.resolve("tinymce.util.Tools");const n=t=>{const e=/^(\d+)([ms]?)$/.exec(t);return(e&&e[2]?{s:1e3,m:6e4}[e[2]]:1)*parseInt(t,10)},i=t=>e=>e.options.get(t),u=i("autosave_ask_before_unload"),c=i("autosave_restore_when_empty"),l=i("autosave_interval"),m=i("autosave_retention"),d=t=>{const e=document.location;return t.options.get("autosave_prefix").replace(/{path}/g,e.pathname).replace(/{query}/g,e.search).replace(/{hash}/g,e.hash).replace(/{id}/g,t.id)},f=(t,e)=>{if(r(e))return t.dom.isEmpty(t.getBody());{const r=s.trim(e);if(""===r)return!0;{const e=(new DOMParser).parseFromString(r,"text/html");return t.dom.isEmpty(e)}}},v=t=>{const e=parseInt(a.getItem(d(t)+"time")??"0",10)||0;return!((new Date).getTime()-e>m(t)&&(p(t,!1),1))},p=(t,e)=>{const r=d(t);a.removeItem(r+"draft"),a.removeItem(r+"time"),!1!==e&&(t=>{t.dispatch("RemoveDraft")})(t)},g=t=>{const e=d(t);!f(t)&&t.isDirty()&&(a.setItem(e+"draft",t.getContent({format:"raw",no_events:!0})),a.setItem(e+"time",(new Date).getTime().toString()),(t=>{t.dispatch("StoreDraft")})(t))},y=t=>{const e=d(t);v(t)&&(t.setContent(a.getItem(e+"draft")??"",{format:"raw"}),(t=>{t.dispatch("RestoreDraft")})(t))};var D=tinymce.util.Tools.resolve("tinymce.EditorManager");const h=t=>e=>{e.setEnabled(v(t));const r=()=>e.setEnabled(v(t));return t.on("StoreDraft RestoreDraft RemoveDraft",r),()=>t.off("StoreDraft RestoreDraft RemoveDraft",r)};t.add("autosave",(t=>((t=>{const r=t.options.register,o=t=>{const r=e(t);return r?{value:n(t),valid:r}:{valid:!1,message:"Must be a string."}};r("autosave_ask_before_unload",{processor:"boolean",default:!0}),r("autosave_prefix",{processor:"string",default:"tinymce-autosave-{path}{query}{hash}-{id}-"}),r("autosave_restore_when_empty",{processor:"boolean",default:!1}),r("autosave_interval",{processor:o,default:"30s"}),r("autosave_retention",{processor:o,default:"20m"})})(t),(t=>{t.editorManager.on("BeforeUnload",(t=>{let e;s.each(D.get(),(t=>{t.plugins.autosave&&t.plugins.autosave.storeDraft(),!e&&t.isDirty()&&u(t)&&(e=t.translate("You have unsaved changes are you sure you want to navigate away?"))})),e&&(t.preventDefault(),t.returnValue=e)}))})(t),(t=>{(t=>{const e=l(t);o.setEditorInterval(t,(()=>{g(t)}),e)})(t);const e=()=>{(t=>{t.undoManager.transact((()=>{y(t),p(t)})),t.focus()})(t)};t.ui.registry.addButton("restoredraft",{tooltip:"Restore last draft",icon:"restore-draft",onAction:e,onSetup:h(t)}),t.ui.registry.addMenuItem("restoredraft",{text:"Restore last draft",icon:"restore-draft",onAction:e,onSetup:h(t)})})(t),t.on("init",(()=>{c(t)&&t.dom.isEmpty(t.getBody())&&y(t)})),(t=>({hasDraft:()=>v(t),storeDraft:()=>g(t),restoreDraft:()=>y(t),removeDraft:e=>p(t,e),isEmpty:e=>f(t,e)}))(t))))}();
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* TinyMCE version 6.2.0 (2022-09-08)
|
||||
*/
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
var global = tinymce.util.Tools.resolve('tinymce.PluginManager');
|
||||
|
||||
const setContent = (editor, html) => {
|
||||
editor.focus();
|
||||
editor.undoManager.transact(() => {
|
||||
editor.setContent(html);
|
||||
});
|
||||
editor.selection.setCursorLocation();
|
||||
editor.nodeChanged();
|
||||
};
|
||||
const getContent = editor => {
|
||||
return editor.getContent({ source_view: true });
|
||||
};
|
||||
|
||||
const open = editor => {
|
||||
const editorContent = getContent(editor);
|
||||
editor.windowManager.open({
|
||||
title: 'Source Code',
|
||||
size: 'large',
|
||||
body: {
|
||||
type: 'panel',
|
||||
items: [{
|
||||
type: 'textarea',
|
||||
name: 'code'
|
||||
}]
|
||||
},
|
||||
buttons: [
|
||||
{
|
||||
type: 'cancel',
|
||||
name: 'cancel',
|
||||
text: 'Cancel'
|
||||
},
|
||||
{
|
||||
type: 'submit',
|
||||
name: 'save',
|
||||
text: 'Save',
|
||||
primary: true
|
||||
}
|
||||
],
|
||||
initialData: { code: editorContent },
|
||||
onSubmit: api => {
|
||||
setContent(editor, api.getData().code);
|
||||
api.close();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const register$1 = editor => {
|
||||
editor.addCommand('mceCodeEditor', () => {
|
||||
open(editor);
|
||||
});
|
||||
};
|
||||
|
||||
const register = editor => {
|
||||
const onAction = () => editor.execCommand('mceCodeEditor');
|
||||
editor.ui.registry.addButton('code', {
|
||||
icon: 'sourcecode',
|
||||
tooltip: 'Source code',
|
||||
onAction
|
||||
});
|
||||
editor.ui.registry.addMenuItem('code', {
|
||||
icon: 'sourcecode',
|
||||
text: 'Source code',
|
||||
onAction
|
||||
});
|
||||
};
|
||||
|
||||
var Plugin = () => {
|
||||
global.add('code', editor => {
|
||||
register$1(editor);
|
||||
register(editor);
|
||||
return {};
|
||||
});
|
||||
};
|
||||
|
||||
Plugin();
|
||||
|
||||
})();
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* TinyMCE version 6.2.0 (2022-09-08)
|
||||
*/
|
||||
!function(){"use strict";tinymce.util.Tools.resolve("tinymce.PluginManager").add("code",(e=>((e=>{e.addCommand("mceCodeEditor",(()=>{(e=>{const o=(e=>e.getContent({source_view:!0}))(e);e.windowManager.open({title:"Source Code",size:"large",body:{type:"panel",items:[{type:"textarea",name:"code"}]},buttons:[{type:"cancel",name:"cancel",text:"Cancel"},{type:"submit",name:"save",text:"Save",primary:!0}],initialData:{code:o},onSubmit:o=>{((e,o)=>{e.focus(),e.undoManager.transact((()=>{e.setContent(o)})),e.selection.setCursorLocation(),e.nodeChanged()})(e,o.getData().code),o.close()}})})(e)}))})(e),(e=>{const o=()=>e.execCommand("mceCodeEditor");e.ui.registry.addButton("code",{icon:"sourcecode",tooltip:"Source code",onAction:o}),e.ui.registry.addMenuItem("code",{icon:"sourcecode",text:"Source code",onAction:o})})(e),{})))}();
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -0,0 +1,383 @@
|
||||
/**
|
||||
* TinyMCE version 6.2.0 (2022-09-08)
|
||||
*/
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
var global = tinymce.util.Tools.resolve('tinymce.PluginManager');
|
||||
|
||||
const hasProto = (v, constructor, predicate) => {
|
||||
if (predicate(v, constructor.prototype)) {
|
||||
return true;
|
||||
} else {
|
||||
return v.constructor?.name === constructor.name;
|
||||
}
|
||||
};
|
||||
const typeOf = x => {
|
||||
const t = typeof x;
|
||||
if (x === null) {
|
||||
return 'null';
|
||||
} else if (t === 'object' && Array.isArray(x)) {
|
||||
return 'array';
|
||||
} else if (t === 'object' && hasProto(x, String, (o, proto) => proto.isPrototypeOf(o))) {
|
||||
return 'string';
|
||||
} else {
|
||||
return t;
|
||||
}
|
||||
};
|
||||
const isType$1 = type => value => typeOf(value) === type;
|
||||
const isSimpleType = type => value => typeof value === type;
|
||||
const isString = isType$1('string');
|
||||
const isBoolean = isSimpleType('boolean');
|
||||
const isNullable = a => a === null || a === undefined;
|
||||
const isNonNullable = a => !isNullable(a);
|
||||
const isFunction = isSimpleType('function');
|
||||
const isNumber = isSimpleType('number');
|
||||
|
||||
const compose1 = (fbc, fab) => a => fbc(fab(a));
|
||||
const constant = value => {
|
||||
return () => {
|
||||
return value;
|
||||
};
|
||||
};
|
||||
const never = constant(false);
|
||||
|
||||
class Optional {
|
||||
constructor(tag, value) {
|
||||
this.tag = tag;
|
||||
this.value = value;
|
||||
}
|
||||
static some(value) {
|
||||
return new Optional(true, value);
|
||||
}
|
||||
static none() {
|
||||
return Optional.singletonNone;
|
||||
}
|
||||
fold(onNone, onSome) {
|
||||
if (this.tag) {
|
||||
return onSome(this.value);
|
||||
} else {
|
||||
return onNone();
|
||||
}
|
||||
}
|
||||
isSome() {
|
||||
return this.tag;
|
||||
}
|
||||
isNone() {
|
||||
return !this.tag;
|
||||
}
|
||||
map(mapper) {
|
||||
if (this.tag) {
|
||||
return Optional.some(mapper(this.value));
|
||||
} else {
|
||||
return Optional.none();
|
||||
}
|
||||
}
|
||||
bind(binder) {
|
||||
if (this.tag) {
|
||||
return binder(this.value);
|
||||
} else {
|
||||
return Optional.none();
|
||||
}
|
||||
}
|
||||
exists(predicate) {
|
||||
return this.tag && predicate(this.value);
|
||||
}
|
||||
forall(predicate) {
|
||||
return !this.tag || predicate(this.value);
|
||||
}
|
||||
filter(predicate) {
|
||||
if (!this.tag || predicate(this.value)) {
|
||||
return this;
|
||||
} else {
|
||||
return Optional.none();
|
||||
}
|
||||
}
|
||||
getOr(replacement) {
|
||||
return this.tag ? this.value : replacement;
|
||||
}
|
||||
or(replacement) {
|
||||
return this.tag ? this : replacement;
|
||||
}
|
||||
getOrThunk(thunk) {
|
||||
return this.tag ? this.value : thunk();
|
||||
}
|
||||
orThunk(thunk) {
|
||||
return this.tag ? this : thunk();
|
||||
}
|
||||
getOrDie(message) {
|
||||
if (!this.tag) {
|
||||
throw new Error(message ?? 'Called getOrDie on None');
|
||||
} else {
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
static from(value) {
|
||||
return isNonNullable(value) ? Optional.some(value) : Optional.none();
|
||||
}
|
||||
getOrNull() {
|
||||
return this.tag ? this.value : null;
|
||||
}
|
||||
getOrUndefined() {
|
||||
return this.value;
|
||||
}
|
||||
each(worker) {
|
||||
if (this.tag) {
|
||||
worker(this.value);
|
||||
}
|
||||
}
|
||||
toArray() {
|
||||
return this.tag ? [this.value] : [];
|
||||
}
|
||||
toString() {
|
||||
return this.tag ? `some(${ this.value })` : 'none()';
|
||||
}
|
||||
}
|
||||
Optional.singletonNone = new Optional(false);
|
||||
|
||||
const map = (xs, f) => {
|
||||
const len = xs.length;
|
||||
const r = new Array(len);
|
||||
for (let i = 0; i < len; i++) {
|
||||
const x = xs[i];
|
||||
r[i] = f(x, i);
|
||||
}
|
||||
return r;
|
||||
};
|
||||
const each = (xs, f) => {
|
||||
for (let i = 0, len = xs.length; i < len; i++) {
|
||||
const x = xs[i];
|
||||
f(x, i);
|
||||
}
|
||||
};
|
||||
const filter = (xs, pred) => {
|
||||
const r = [];
|
||||
for (let i = 0, len = xs.length; i < len; i++) {
|
||||
const x = xs[i];
|
||||
if (pred(x, i)) {
|
||||
r.push(x);
|
||||
}
|
||||
}
|
||||
return r;
|
||||
};
|
||||
|
||||
const DOCUMENT = 9;
|
||||
const DOCUMENT_FRAGMENT = 11;
|
||||
const ELEMENT = 1;
|
||||
const TEXT = 3;
|
||||
|
||||
const fromHtml = (html, scope) => {
|
||||
const doc = scope || document;
|
||||
const div = doc.createElement('div');
|
||||
div.innerHTML = html;
|
||||
if (!div.hasChildNodes() || div.childNodes.length > 1) {
|
||||
const message = 'HTML does not have a single root node';
|
||||
console.error(message, html);
|
||||
throw new Error(message);
|
||||
}
|
||||
return fromDom(div.childNodes[0]);
|
||||
};
|
||||
const fromTag = (tag, scope) => {
|
||||
const doc = scope || document;
|
||||
const node = doc.createElement(tag);
|
||||
return fromDom(node);
|
||||
};
|
||||
const fromText = (text, scope) => {
|
||||
const doc = scope || document;
|
||||
const node = doc.createTextNode(text);
|
||||
return fromDom(node);
|
||||
};
|
||||
const fromDom = node => {
|
||||
if (node === null || node === undefined) {
|
||||
throw new Error('Node cannot be null or undefined');
|
||||
}
|
||||
return { dom: node };
|
||||
};
|
||||
const fromPoint = (docElm, x, y) => Optional.from(docElm.dom.elementFromPoint(x, y)).map(fromDom);
|
||||
const SugarElement = {
|
||||
fromHtml,
|
||||
fromTag,
|
||||
fromText,
|
||||
fromDom,
|
||||
fromPoint
|
||||
};
|
||||
|
||||
const is = (element, selector) => {
|
||||
const dom = element.dom;
|
||||
if (dom.nodeType !== ELEMENT) {
|
||||
return false;
|
||||
} else {
|
||||
const elem = dom;
|
||||
if (elem.matches !== undefined) {
|
||||
return elem.matches(selector);
|
||||
} else if (elem.msMatchesSelector !== undefined) {
|
||||
return elem.msMatchesSelector(selector);
|
||||
} else if (elem.webkitMatchesSelector !== undefined) {
|
||||
return elem.webkitMatchesSelector(selector);
|
||||
} else if (elem.mozMatchesSelector !== undefined) {
|
||||
return elem.mozMatchesSelector(selector);
|
||||
} else {
|
||||
throw new Error('Browser lacks native selectors');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
typeof window !== 'undefined' ? window : Function('return this;')();
|
||||
|
||||
const name = element => {
|
||||
const r = element.dom.nodeName;
|
||||
return r.toLowerCase();
|
||||
};
|
||||
const type = element => element.dom.nodeType;
|
||||
const isType = t => element => type(element) === t;
|
||||
const isElement = isType(ELEMENT);
|
||||
const isText = isType(TEXT);
|
||||
const isDocument = isType(DOCUMENT);
|
||||
const isDocumentFragment = isType(DOCUMENT_FRAGMENT);
|
||||
const isTag = tag => e => isElement(e) && name(e) === tag;
|
||||
|
||||
const owner = element => SugarElement.fromDom(element.dom.ownerDocument);
|
||||
const documentOrOwner = dos => isDocument(dos) ? dos : owner(dos);
|
||||
const parent = element => Optional.from(element.dom.parentNode).map(SugarElement.fromDom);
|
||||
const children$2 = element => map(element.dom.childNodes, SugarElement.fromDom);
|
||||
|
||||
const rawSet = (dom, key, value) => {
|
||||
if (isString(value) || isBoolean(value) || isNumber(value)) {
|
||||
dom.setAttribute(key, value + '');
|
||||
} else {
|
||||
console.error('Invalid call to Attribute.set. Key ', key, ':: Value ', value, ':: Element ', dom);
|
||||
throw new Error('Attribute value was not simple');
|
||||
}
|
||||
};
|
||||
const set = (element, key, value) => {
|
||||
rawSet(element.dom, key, value);
|
||||
};
|
||||
const remove = (element, key) => {
|
||||
element.dom.removeAttribute(key);
|
||||
};
|
||||
|
||||
const isShadowRoot = dos => isDocumentFragment(dos) && isNonNullable(dos.dom.host);
|
||||
const supported = isFunction(Element.prototype.attachShadow) && isFunction(Node.prototype.getRootNode);
|
||||
const getRootNode = supported ? e => SugarElement.fromDom(e.dom.getRootNode()) : documentOrOwner;
|
||||
const getShadowRoot = e => {
|
||||
const r = getRootNode(e);
|
||||
return isShadowRoot(r) ? Optional.some(r) : Optional.none();
|
||||
};
|
||||
const getShadowHost = e => SugarElement.fromDom(e.dom.host);
|
||||
|
||||
const inBody = element => {
|
||||
const dom = isText(element) ? element.dom.parentNode : element.dom;
|
||||
if (dom === undefined || dom === null || dom.ownerDocument === null) {
|
||||
return false;
|
||||
}
|
||||
const doc = dom.ownerDocument;
|
||||
return getShadowRoot(SugarElement.fromDom(dom)).fold(() => doc.body.contains(dom), compose1(inBody, getShadowHost));
|
||||
};
|
||||
|
||||
const ancestor$1 = (scope, predicate, isRoot) => {
|
||||
let element = scope.dom;
|
||||
const stop = isFunction(isRoot) ? isRoot : never;
|
||||
while (element.parentNode) {
|
||||
element = element.parentNode;
|
||||
const el = SugarElement.fromDom(element);
|
||||
if (predicate(el)) {
|
||||
return Optional.some(el);
|
||||
} else if (stop(el)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Optional.none();
|
||||
};
|
||||
|
||||
const ancestor = (scope, selector, isRoot) => ancestor$1(scope, e => is(e, selector), isRoot);
|
||||
|
||||
const isSupported = dom => dom.style !== undefined && isFunction(dom.style.getPropertyValue);
|
||||
|
||||
const get = (element, property) => {
|
||||
const dom = element.dom;
|
||||
const styles = window.getComputedStyle(dom);
|
||||
const r = styles.getPropertyValue(property);
|
||||
return r === '' && !inBody(element) ? getUnsafeProperty(dom, property) : r;
|
||||
};
|
||||
const getUnsafeProperty = (dom, property) => isSupported(dom) ? dom.style.getPropertyValue(property) : '';
|
||||
|
||||
const getDirection = element => get(element, 'direction') === 'rtl' ? 'rtl' : 'ltr';
|
||||
|
||||
const children$1 = (scope, predicate) => filter(children$2(scope), predicate);
|
||||
|
||||
const children = (scope, selector) => children$1(scope, e => is(e, selector));
|
||||
|
||||
const getParentElement = element => parent(element).filter(isElement);
|
||||
const getNormalizedBlock = (element, isListItem) => {
|
||||
const normalizedElement = isListItem ? ancestor(element, 'ol,ul') : Optional.some(element);
|
||||
return normalizedElement.getOr(element);
|
||||
};
|
||||
const isListItem = isTag('li');
|
||||
const setDir = (editor, dir) => {
|
||||
const selectedBlocks = editor.selection.getSelectedBlocks();
|
||||
if (selectedBlocks.length > 0) {
|
||||
each(selectedBlocks, block => {
|
||||
const blockElement = SugarElement.fromDom(block);
|
||||
const isBlockElementListItem = isListItem(blockElement);
|
||||
const normalizedBlock = getNormalizedBlock(blockElement, isBlockElementListItem);
|
||||
const normalizedBlockParent = getParentElement(normalizedBlock);
|
||||
normalizedBlockParent.each(parent => {
|
||||
const parentDirection = getDirection(parent);
|
||||
if (parentDirection !== dir) {
|
||||
set(normalizedBlock, 'dir', dir);
|
||||
} else if (getDirection(normalizedBlock) !== dir) {
|
||||
remove(normalizedBlock, 'dir');
|
||||
}
|
||||
if (isBlockElementListItem) {
|
||||
const listItems = children(normalizedBlock, 'li[dir]');
|
||||
each(listItems, listItem => remove(listItem, 'dir'));
|
||||
}
|
||||
});
|
||||
});
|
||||
editor.nodeChanged();
|
||||
}
|
||||
};
|
||||
|
||||
const register$1 = editor => {
|
||||
editor.addCommand('mceDirectionLTR', () => {
|
||||
setDir(editor, 'ltr');
|
||||
});
|
||||
editor.addCommand('mceDirectionRTL', () => {
|
||||
setDir(editor, 'rtl');
|
||||
});
|
||||
};
|
||||
|
||||
const getNodeChangeHandler = (editor, dir) => api => {
|
||||
const nodeChangeHandler = e => {
|
||||
const element = SugarElement.fromDom(e.element);
|
||||
api.setActive(getDirection(element) === dir);
|
||||
};
|
||||
editor.on('NodeChange', nodeChangeHandler);
|
||||
return () => editor.off('NodeChange', nodeChangeHandler);
|
||||
};
|
||||
const register = editor => {
|
||||
editor.ui.registry.addToggleButton('ltr', {
|
||||
tooltip: 'Left to right',
|
||||
icon: 'ltr',
|
||||
onAction: () => editor.execCommand('mceDirectionLTR'),
|
||||
onSetup: getNodeChangeHandler(editor, 'ltr')
|
||||
});
|
||||
editor.ui.registry.addToggleButton('rtl', {
|
||||
tooltip: 'Right to left',
|
||||
icon: 'rtl',
|
||||
onAction: () => editor.execCommand('mceDirectionRTL'),
|
||||
onSetup: getNodeChangeHandler(editor, 'rtl')
|
||||
});
|
||||
};
|
||||
|
||||
var Plugin = () => {
|
||||
global.add('directionality', editor => {
|
||||
register$1(editor);
|
||||
register(editor);
|
||||
});
|
||||
};
|
||||
|
||||
Plugin();
|
||||
|
||||
})();
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* TinyMCE version 6.2.0 (2022-09-08)
|
||||
*/
|
||||
!function(){"use strict";var t=tinymce.util.Tools.resolve("tinymce.PluginManager");const e=t=>e=>typeof e===t,o=t=>"string"===(t=>{const e=typeof t;return null===t?"null":"object"===e&&Array.isArray(t)?"array":"object"===e&&(o=r=t,(n=String).prototype.isPrototypeOf(o)||r.constructor?.name===n.name)?"string":e;var o,r,n})(t),r=e("boolean"),n=t=>!(t=>null==t)(t),i=e("function"),s=e("number"),l=(!1,()=>false);class a{constructor(t,e){this.tag=t,this.value=e}static some(t){return new a(!0,t)}static none(){return a.singletonNone}fold(t,e){return this.tag?e(this.value):t()}isSome(){return this.tag}isNone(){return!this.tag}map(t){return this.tag?a.some(t(this.value)):a.none()}bind(t){return this.tag?t(this.value):a.none()}exists(t){return this.tag&&t(this.value)}forall(t){return!this.tag||t(this.value)}filter(t){return!this.tag||t(this.value)?this:a.none()}getOr(t){return this.tag?this.value:t}or(t){return this.tag?this:t}getOrThunk(t){return this.tag?this.value:t()}orThunk(t){return this.tag?this:t()}getOrDie(t){if(this.tag)return this.value;throw new Error(t??"Called getOrDie on None")}static from(t){return n(t)?a.some(t):a.none()}getOrNull(){return this.tag?this.value:null}getOrUndefined(){return this.value}each(t){this.tag&&t(this.value)}toArray(){return this.tag?[this.value]:[]}toString(){return this.tag?`some(${this.value})`:"none()"}}a.singletonNone=new a(!1);const u=(t,e)=>{for(let o=0,r=t.length;o<r;o++)e(t[o],o)},c=t=>{if(null==t)throw new Error("Node cannot be null or undefined");return{dom:t}},d=c,h=(t,e)=>{const o=t.dom;if(1!==o.nodeType)return!1;{const t=o;if(void 0!==t.matches)return t.matches(e);if(void 0!==t.msMatchesSelector)return t.msMatchesSelector(e);if(void 0!==t.webkitMatchesSelector)return t.webkitMatchesSelector(e);if(void 0!==t.mozMatchesSelector)return t.mozMatchesSelector(e);throw new Error("Browser lacks native selectors")}};"undefined"!=typeof window?window:Function("return this;")();const m=t=>e=>(t=>t.dom.nodeType)(e)===t,g=m(1),f=m(3),v=m(9),p=m(11),y=(t,e)=>{t.dom.removeAttribute(e)},w=i(Element.prototype.attachShadow)&&i(Node.prototype.getRootNode)?t=>d(t.dom.getRootNode()):t=>v(t)?t:d(t.dom.ownerDocument),N=t=>d(t.dom.host),b=t=>{const e=f(t)?t.dom.parentNode:t.dom;if(null==e||null===e.ownerDocument)return!1;const o=e.ownerDocument;return(t=>{const e=w(t);return p(o=e)&&n(o.dom.host)?a.some(e):a.none();var o})(d(e)).fold((()=>o.body.contains(e)),(r=b,i=N,t=>r(i(t))));var r,i},S=t=>"rtl"===((t,e)=>{const o=t.dom,r=window.getComputedStyle(o).getPropertyValue(e);return""!==r||b(t)?r:((t,e)=>(t=>void 0!==t.style&&i(t.style.getPropertyValue))(t)?t.style.getPropertyValue(e):"")(o,e)})(t,"direction")?"rtl":"ltr",A=(t,e)=>((t,o)=>((t,e)=>{const o=[];for(let r=0,n=t.length;r<n;r++){const n=t[r];e(n,r)&&o.push(n)}return o})(((t,e)=>{const o=t.length,r=new Array(o);for(let n=0;n<o;n++){const o=t[n];r[n]=e(o,n)}return r})(t.dom.childNodes,d),(t=>h(t,e))))(t),T=("li",t=>g(t)&&"li"===t.dom.nodeName.toLowerCase());const C=(t,e)=>{const n=t.selection.getSelectedBlocks();n.length>0&&(u(n,(t=>{const n=d(t),c=T(n),m=((t,e)=>{return(e?(o=t,r="ol,ul",((t,e,o)=>{let n=t.dom;const s=i(o)?o:l;for(;n.parentNode;){n=n.parentNode;const t=d(n);if(h(t,r))return a.some(t);if(s(t))break}return a.none()})(o,0,n)):a.some(t)).getOr(t);var o,r,n})(n,c);var f;(f=m,(t=>a.from(t.dom.parentNode).map(d))(f).filter(g)).each((t=>{if(S(t)!==e?((t,e,n)=>{((t,e,n)=>{if(!(o(n)||r(n)||s(n)))throw console.error("Invalid call to Attribute.set. Key ",e,":: Value ",n,":: Element ",t),new Error("Attribute value was not simple");t.setAttribute(e,n+"")})(t.dom,e,n)})(m,"dir",e):S(m)!==e&&y(m,"dir"),c){const t=A(m,"li[dir]");u(t,(t=>y(t,"dir")))}}))})),t.nodeChanged())},D=(t,e)=>o=>{const r=t=>{const r=d(t.element);o.setActive(S(r)===e)};return t.on("NodeChange",r),()=>t.off("NodeChange",r)};t.add("directionality",(t=>{(t=>{t.addCommand("mceDirectionLTR",(()=>{C(t,"ltr")})),t.addCommand("mceDirectionRTL",(()=>{C(t,"rtl")}))})(t),(t=>{t.ui.registry.addToggleButton("ltr",{tooltip:"Left to right",icon:"ltr",onAction:()=>t.execCommand("mceDirectionLTR"),onSetup:D(t,"ltr")}),t.ui.registry.addToggleButton("rtl",{tooltip:"Right to left",icon:"rtl",onAction:()=>t.execCommand("mceDirectionRTL"),onSetup:D(t,"rtl")})})(t)}))}();
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,583 @@
|
||||
/**
|
||||
* TinyMCE version 6.2.0 (2022-09-08)
|
||||
*/
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
var global$1 = tinymce.util.Tools.resolve('tinymce.PluginManager');
|
||||
|
||||
const eq = t => a => t === a;
|
||||
const isNull = eq(null);
|
||||
const isUndefined = eq(undefined);
|
||||
const isNullable = a => a === null || a === undefined;
|
||||
const isNonNullable = a => !isNullable(a);
|
||||
|
||||
const noop = () => {
|
||||
};
|
||||
const constant = value => {
|
||||
return () => {
|
||||
return value;
|
||||
};
|
||||
};
|
||||
const never = constant(false);
|
||||
|
||||
class Optional {
|
||||
constructor(tag, value) {
|
||||
this.tag = tag;
|
||||
this.value = value;
|
||||
}
|
||||
static some(value) {
|
||||
return new Optional(true, value);
|
||||
}
|
||||
static none() {
|
||||
return Optional.singletonNone;
|
||||
}
|
||||
fold(onNone, onSome) {
|
||||
if (this.tag) {
|
||||
return onSome(this.value);
|
||||
} else {
|
||||
return onNone();
|
||||
}
|
||||
}
|
||||
isSome() {
|
||||
return this.tag;
|
||||
}
|
||||
isNone() {
|
||||
return !this.tag;
|
||||
}
|
||||
map(mapper) {
|
||||
if (this.tag) {
|
||||
return Optional.some(mapper(this.value));
|
||||
} else {
|
||||
return Optional.none();
|
||||
}
|
||||
}
|
||||
bind(binder) {
|
||||
if (this.tag) {
|
||||
return binder(this.value);
|
||||
} else {
|
||||
return Optional.none();
|
||||
}
|
||||
}
|
||||
exists(predicate) {
|
||||
return this.tag && predicate(this.value);
|
||||
}
|
||||
forall(predicate) {
|
||||
return !this.tag || predicate(this.value);
|
||||
}
|
||||
filter(predicate) {
|
||||
if (!this.tag || predicate(this.value)) {
|
||||
return this;
|
||||
} else {
|
||||
return Optional.none();
|
||||
}
|
||||
}
|
||||
getOr(replacement) {
|
||||
return this.tag ? this.value : replacement;
|
||||
}
|
||||
or(replacement) {
|
||||
return this.tag ? this : replacement;
|
||||
}
|
||||
getOrThunk(thunk) {
|
||||
return this.tag ? this.value : thunk();
|
||||
}
|
||||
orThunk(thunk) {
|
||||
return this.tag ? this : thunk();
|
||||
}
|
||||
getOrDie(message) {
|
||||
if (!this.tag) {
|
||||
throw new Error(message ?? 'Called getOrDie on None');
|
||||
} else {
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
static from(value) {
|
||||
return isNonNullable(value) ? Optional.some(value) : Optional.none();
|
||||
}
|
||||
getOrNull() {
|
||||
return this.tag ? this.value : null;
|
||||
}
|
||||
getOrUndefined() {
|
||||
return this.value;
|
||||
}
|
||||
each(worker) {
|
||||
if (this.tag) {
|
||||
worker(this.value);
|
||||
}
|
||||
}
|
||||
toArray() {
|
||||
return this.tag ? [this.value] : [];
|
||||
}
|
||||
toString() {
|
||||
return this.tag ? `some(${ this.value })` : 'none()';
|
||||
}
|
||||
}
|
||||
Optional.singletonNone = new Optional(false);
|
||||
|
||||
const exists = (xs, pred) => {
|
||||
for (let i = 0, len = xs.length; i < len; i++) {
|
||||
const x = xs[i];
|
||||
if (pred(x, i)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
const map$1 = (xs, f) => {
|
||||
const len = xs.length;
|
||||
const r = new Array(len);
|
||||
for (let i = 0; i < len; i++) {
|
||||
const x = xs[i];
|
||||
r[i] = f(x, i);
|
||||
}
|
||||
return r;
|
||||
};
|
||||
const each$1 = (xs, f) => {
|
||||
for (let i = 0, len = xs.length; i < len; i++) {
|
||||
const x = xs[i];
|
||||
f(x, i);
|
||||
}
|
||||
};
|
||||
|
||||
const Cell = initial => {
|
||||
let value = initial;
|
||||
const get = () => {
|
||||
return value;
|
||||
};
|
||||
const set = v => {
|
||||
value = v;
|
||||
};
|
||||
return {
|
||||
get,
|
||||
set
|
||||
};
|
||||
};
|
||||
|
||||
const last = (fn, rate) => {
|
||||
let timer = null;
|
||||
const cancel = () => {
|
||||
if (!isNull(timer)) {
|
||||
clearTimeout(timer);
|
||||
timer = null;
|
||||
}
|
||||
};
|
||||
const throttle = (...args) => {
|
||||
cancel();
|
||||
timer = setTimeout(() => {
|
||||
timer = null;
|
||||
fn.apply(null, args);
|
||||
}, rate);
|
||||
};
|
||||
return {
|
||||
cancel,
|
||||
throttle
|
||||
};
|
||||
};
|
||||
|
||||
const insertEmoticon = (editor, ch) => {
|
||||
editor.insertContent(ch);
|
||||
};
|
||||
|
||||
const keys = Object.keys;
|
||||
const hasOwnProperty = Object.hasOwnProperty;
|
||||
const each = (obj, f) => {
|
||||
const props = keys(obj);
|
||||
for (let k = 0, len = props.length; k < len; k++) {
|
||||
const i = props[k];
|
||||
const x = obj[i];
|
||||
f(x, i);
|
||||
}
|
||||
};
|
||||
const map = (obj, f) => {
|
||||
return tupleMap(obj, (x, i) => ({
|
||||
k: i,
|
||||
v: f(x, i)
|
||||
}));
|
||||
};
|
||||
const tupleMap = (obj, f) => {
|
||||
const r = {};
|
||||
each(obj, (x, i) => {
|
||||
const tuple = f(x, i);
|
||||
r[tuple.k] = tuple.v;
|
||||
});
|
||||
return r;
|
||||
};
|
||||
const has = (obj, key) => hasOwnProperty.call(obj, key);
|
||||
|
||||
const shallow = (old, nu) => {
|
||||
return nu;
|
||||
};
|
||||
const baseMerge = merger => {
|
||||
return (...objects) => {
|
||||
if (objects.length === 0) {
|
||||
throw new Error(`Can't merge zero objects`);
|
||||
}
|
||||
const ret = {};
|
||||
for (let j = 0; j < objects.length; j++) {
|
||||
const curObject = objects[j];
|
||||
for (const key in curObject) {
|
||||
if (has(curObject, key)) {
|
||||
ret[key] = merger(ret[key], curObject[key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
};
|
||||
const merge = baseMerge(shallow);
|
||||
|
||||
const singleton = doRevoke => {
|
||||
const subject = Cell(Optional.none());
|
||||
const revoke = () => subject.get().each(doRevoke);
|
||||
const clear = () => {
|
||||
revoke();
|
||||
subject.set(Optional.none());
|
||||
};
|
||||
const isSet = () => subject.get().isSome();
|
||||
const get = () => subject.get();
|
||||
const set = s => {
|
||||
revoke();
|
||||
subject.set(Optional.some(s));
|
||||
};
|
||||
return {
|
||||
clear,
|
||||
isSet,
|
||||
get,
|
||||
set
|
||||
};
|
||||
};
|
||||
const value = () => {
|
||||
const subject = singleton(noop);
|
||||
const on = f => subject.get().each(f);
|
||||
return {
|
||||
...subject,
|
||||
on
|
||||
};
|
||||
};
|
||||
|
||||
const checkRange = (str, substr, start) => substr === '' || str.length >= substr.length && str.substr(start, start + substr.length) === substr;
|
||||
const contains = (str, substr, start = 0, end) => {
|
||||
const idx = str.indexOf(substr, start);
|
||||
if (idx !== -1) {
|
||||
return isUndefined(end) ? true : idx + substr.length <= end;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
const startsWith = (str, prefix) => {
|
||||
return checkRange(str, prefix, 0);
|
||||
};
|
||||
|
||||
var global = tinymce.util.Tools.resolve('tinymce.Resource');
|
||||
|
||||
const DEFAULT_ID = 'tinymce.plugins.emoticons';
|
||||
const option = name => editor => editor.options.get(name);
|
||||
const register$2 = (editor, pluginUrl) => {
|
||||
const registerOption = editor.options.register;
|
||||
registerOption('emoticons_database', {
|
||||
processor: 'string',
|
||||
default: 'emojis'
|
||||
});
|
||||
registerOption('emoticons_database_url', {
|
||||
processor: 'string',
|
||||
default: `${ pluginUrl }/js/${ getEmojiDatabase(editor) }${ editor.suffix }.js`
|
||||
});
|
||||
registerOption('emoticons_database_id', {
|
||||
processor: 'string',
|
||||
default: DEFAULT_ID
|
||||
});
|
||||
registerOption('emoticons_append', {
|
||||
processor: 'object',
|
||||
default: {}
|
||||
});
|
||||
registerOption('emoticons_images_url', {
|
||||
processor: 'string',
|
||||
default: 'https://twemoji.maxcdn.com/v/13.0.1/72x72/'
|
||||
});
|
||||
};
|
||||
const getEmojiDatabase = option('emoticons_database');
|
||||
const getEmojiDatabaseUrl = option('emoticons_database_url');
|
||||
const getEmojiDatabaseId = option('emoticons_database_id');
|
||||
const getAppendedEmoji = option('emoticons_append');
|
||||
const getEmojiImageUrl = option('emoticons_images_url');
|
||||
|
||||
const ALL_CATEGORY = 'All';
|
||||
const categoryNameMap = {
|
||||
symbols: 'Symbols',
|
||||
people: 'People',
|
||||
animals_and_nature: 'Animals and Nature',
|
||||
food_and_drink: 'Food and Drink',
|
||||
activity: 'Activity',
|
||||
travel_and_places: 'Travel and Places',
|
||||
objects: 'Objects',
|
||||
flags: 'Flags',
|
||||
user: 'User Defined'
|
||||
};
|
||||
const translateCategory = (categories, name) => has(categories, name) ? categories[name] : name;
|
||||
const getUserDefinedEmoji = editor => {
|
||||
const userDefinedEmoticons = getAppendedEmoji(editor);
|
||||
return map(userDefinedEmoticons, value => ({
|
||||
keywords: [],
|
||||
category: 'user',
|
||||
...value
|
||||
}));
|
||||
};
|
||||
const initDatabase = (editor, databaseUrl, databaseId) => {
|
||||
const categories = value();
|
||||
const all = value();
|
||||
const emojiImagesUrl = getEmojiImageUrl(editor);
|
||||
const getEmoji = lib => {
|
||||
if (startsWith(lib.char, '<img')) {
|
||||
return lib.char.replace(/src="([^"]+)"/, (match, url) => `src="${ emojiImagesUrl }${ url }"`);
|
||||
} else {
|
||||
return lib.char;
|
||||
}
|
||||
};
|
||||
const processEmojis = emojis => {
|
||||
const cats = {};
|
||||
const everything = [];
|
||||
each(emojis, (lib, title) => {
|
||||
const entry = {
|
||||
title,
|
||||
keywords: lib.keywords,
|
||||
char: getEmoji(lib),
|
||||
category: translateCategory(categoryNameMap, lib.category)
|
||||
};
|
||||
const current = cats[entry.category] !== undefined ? cats[entry.category] : [];
|
||||
cats[entry.category] = current.concat([entry]);
|
||||
everything.push(entry);
|
||||
});
|
||||
categories.set(cats);
|
||||
all.set(everything);
|
||||
};
|
||||
editor.on('init', () => {
|
||||
global.load(databaseId, databaseUrl).then(emojis => {
|
||||
const userEmojis = getUserDefinedEmoji(editor);
|
||||
processEmojis(merge(emojis, userEmojis));
|
||||
}, err => {
|
||||
console.log(`Failed to load emojis: ${ err }`);
|
||||
categories.set({});
|
||||
all.set([]);
|
||||
});
|
||||
});
|
||||
const listCategory = category => {
|
||||
if (category === ALL_CATEGORY) {
|
||||
return listAll();
|
||||
}
|
||||
return categories.get().bind(cats => Optional.from(cats[category])).getOr([]);
|
||||
};
|
||||
const listAll = () => all.get().getOr([]);
|
||||
const listCategories = () => [ALL_CATEGORY].concat(keys(categories.get().getOr({})));
|
||||
const waitForLoad = () => {
|
||||
if (hasLoaded()) {
|
||||
return Promise.resolve(true);
|
||||
} else {
|
||||
return new Promise((resolve, reject) => {
|
||||
let numRetries = 15;
|
||||
const interval = setInterval(() => {
|
||||
if (hasLoaded()) {
|
||||
clearInterval(interval);
|
||||
resolve(true);
|
||||
} else {
|
||||
numRetries--;
|
||||
if (numRetries < 0) {
|
||||
console.log('Could not load emojis from url: ' + databaseUrl);
|
||||
clearInterval(interval);
|
||||
reject(false);
|
||||
}
|
||||
}
|
||||
}, 100);
|
||||
});
|
||||
}
|
||||
};
|
||||
const hasLoaded = () => categories.isSet() && all.isSet();
|
||||
return {
|
||||
listCategories,
|
||||
hasLoaded,
|
||||
waitForLoad,
|
||||
listAll,
|
||||
listCategory
|
||||
};
|
||||
};
|
||||
|
||||
const emojiMatches = (emoji, lowerCasePattern) => contains(emoji.title.toLowerCase(), lowerCasePattern) || exists(emoji.keywords, k => contains(k.toLowerCase(), lowerCasePattern));
|
||||
const emojisFrom = (list, pattern, maxResults) => {
|
||||
const matches = [];
|
||||
const lowerCasePattern = pattern.toLowerCase();
|
||||
const reachedLimit = maxResults.fold(() => never, max => size => size >= max);
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
if (pattern.length === 0 || emojiMatches(list[i], lowerCasePattern)) {
|
||||
matches.push({
|
||||
value: list[i].char,
|
||||
text: list[i].title,
|
||||
icon: list[i].char
|
||||
});
|
||||
if (reachedLimit(matches.length)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return matches;
|
||||
};
|
||||
|
||||
const patternName = 'pattern';
|
||||
const open = (editor, database) => {
|
||||
const initialState = {
|
||||
pattern: '',
|
||||
results: emojisFrom(database.listAll(), '', Optional.some(300))
|
||||
};
|
||||
const currentTab = Cell(ALL_CATEGORY);
|
||||
const scan = dialogApi => {
|
||||
const dialogData = dialogApi.getData();
|
||||
const category = currentTab.get();
|
||||
const candidates = database.listCategory(category);
|
||||
const results = emojisFrom(candidates, dialogData[patternName], category === ALL_CATEGORY ? Optional.some(300) : Optional.none());
|
||||
dialogApi.setData({ results });
|
||||
};
|
||||
const updateFilter = last(dialogApi => {
|
||||
scan(dialogApi);
|
||||
}, 200);
|
||||
const searchField = {
|
||||
label: 'Search',
|
||||
type: 'input',
|
||||
name: patternName
|
||||
};
|
||||
const resultsField = {
|
||||
type: 'collection',
|
||||
name: 'results'
|
||||
};
|
||||
const getInitialState = () => {
|
||||
const body = {
|
||||
type: 'tabpanel',
|
||||
tabs: map$1(database.listCategories(), cat => ({
|
||||
title: cat,
|
||||
name: cat,
|
||||
items: [
|
||||
searchField,
|
||||
resultsField
|
||||
]
|
||||
}))
|
||||
};
|
||||
return {
|
||||
title: 'Emojis',
|
||||
size: 'normal',
|
||||
body,
|
||||
initialData: initialState,
|
||||
onTabChange: (dialogApi, details) => {
|
||||
currentTab.set(details.newTabName);
|
||||
updateFilter.throttle(dialogApi);
|
||||
},
|
||||
onChange: updateFilter.throttle,
|
||||
onAction: (dialogApi, actionData) => {
|
||||
if (actionData.name === 'results') {
|
||||
insertEmoticon(editor, actionData.value);
|
||||
dialogApi.close();
|
||||
}
|
||||
},
|
||||
buttons: [{
|
||||
type: 'cancel',
|
||||
text: 'Close',
|
||||
primary: true
|
||||
}]
|
||||
};
|
||||
};
|
||||
const dialogApi = editor.windowManager.open(getInitialState());
|
||||
dialogApi.focus(patternName);
|
||||
if (!database.hasLoaded()) {
|
||||
dialogApi.block('Loading emojis...');
|
||||
database.waitForLoad().then(() => {
|
||||
dialogApi.redial(getInitialState());
|
||||
updateFilter.throttle(dialogApi);
|
||||
dialogApi.focus(patternName);
|
||||
dialogApi.unblock();
|
||||
}).catch(_err => {
|
||||
dialogApi.redial({
|
||||
title: 'Emojis',
|
||||
body: {
|
||||
type: 'panel',
|
||||
items: [{
|
||||
type: 'alertbanner',
|
||||
level: 'error',
|
||||
icon: 'warning',
|
||||
text: 'Could not load emojis'
|
||||
}]
|
||||
},
|
||||
buttons: [{
|
||||
type: 'cancel',
|
||||
text: 'Close',
|
||||
primary: true
|
||||
}],
|
||||
initialData: {
|
||||
pattern: '',
|
||||
results: []
|
||||
}
|
||||
});
|
||||
dialogApi.focus(patternName);
|
||||
dialogApi.unblock();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const register$1 = (editor, database) => {
|
||||
editor.addCommand('mceEmoticons', () => open(editor, database));
|
||||
};
|
||||
|
||||
const setup = editor => {
|
||||
editor.on('PreInit', () => {
|
||||
editor.parser.addAttributeFilter('data-emoticon', nodes => {
|
||||
each$1(nodes, node => {
|
||||
node.attr('data-mce-resize', 'false');
|
||||
node.attr('data-mce-placeholder', '1');
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const init = (editor, database) => {
|
||||
editor.ui.registry.addAutocompleter('emoticons', {
|
||||
trigger: ':',
|
||||
columns: 'auto',
|
||||
minChars: 2,
|
||||
fetch: (pattern, maxResults) => database.waitForLoad().then(() => {
|
||||
const candidates = database.listAll();
|
||||
return emojisFrom(candidates, pattern, Optional.some(maxResults));
|
||||
}),
|
||||
onAction: (autocompleteApi, rng, value) => {
|
||||
editor.selection.setRng(rng);
|
||||
editor.insertContent(value);
|
||||
autocompleteApi.hide();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const register = editor => {
|
||||
const onAction = () => editor.execCommand('mceEmoticons');
|
||||
editor.ui.registry.addButton('emoticons', {
|
||||
tooltip: 'Emojis',
|
||||
icon: 'emoji',
|
||||
onAction
|
||||
});
|
||||
editor.ui.registry.addMenuItem('emoticons', {
|
||||
text: 'Emojis...',
|
||||
icon: 'emoji',
|
||||
onAction
|
||||
});
|
||||
};
|
||||
|
||||
var Plugin = () => {
|
||||
global$1.add('emoticons', (editor, pluginUrl) => {
|
||||
register$2(editor, pluginUrl);
|
||||
const databaseUrl = getEmojiDatabaseUrl(editor);
|
||||
const databaseId = getEmojiDatabaseId(editor);
|
||||
const database = initDatabase(editor, databaseUrl, databaseId);
|
||||
register$1(editor, database);
|
||||
register(editor);
|
||||
init(editor, database);
|
||||
setup(editor);
|
||||
});
|
||||
};
|
||||
|
||||
Plugin();
|
||||
|
||||
})();
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -0,0 +1,918 @@
|
||||
/**
|
||||
* TinyMCE version 6.2.0 (2022-09-08)
|
||||
*/
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
const Cell = initial => {
|
||||
let value = initial;
|
||||
const get = () => {
|
||||
return value;
|
||||
};
|
||||
const set = v => {
|
||||
value = v;
|
||||
};
|
||||
return {
|
||||
get,
|
||||
set
|
||||
};
|
||||
};
|
||||
|
||||
var global$3 = tinymce.util.Tools.resolve('tinymce.PluginManager');
|
||||
|
||||
let unique = 0;
|
||||
const generate = prefix => {
|
||||
const date = new Date();
|
||||
const time = date.getTime();
|
||||
const random = Math.floor(Math.random() * 1000000000);
|
||||
unique++;
|
||||
return prefix + '_' + random + unique + String(time);
|
||||
};
|
||||
|
||||
const get$1 = customTabs => {
|
||||
const addTab = spec => {
|
||||
const name = spec.name ?? generate('tab-name');
|
||||
const currentCustomTabs = customTabs.get();
|
||||
currentCustomTabs[name] = spec;
|
||||
customTabs.set(currentCustomTabs);
|
||||
};
|
||||
return { addTab };
|
||||
};
|
||||
|
||||
const register$2 = (editor, dialogOpener) => {
|
||||
editor.addCommand('mceHelp', dialogOpener);
|
||||
};
|
||||
|
||||
const option = name => editor => editor.options.get(name);
|
||||
const register$1 = editor => {
|
||||
const registerOption = editor.options.register;
|
||||
registerOption('help_tabs', { processor: 'array' });
|
||||
};
|
||||
const getHelpTabs = option('help_tabs');
|
||||
const getForcedPlugins = option('forced_plugins');
|
||||
|
||||
const register = (editor, dialogOpener) => {
|
||||
editor.ui.registry.addButton('help', {
|
||||
icon: 'help',
|
||||
tooltip: 'Help',
|
||||
onAction: dialogOpener
|
||||
});
|
||||
editor.ui.registry.addMenuItem('help', {
|
||||
text: 'Help',
|
||||
icon: 'help',
|
||||
shortcut: 'Alt+0',
|
||||
onAction: dialogOpener
|
||||
});
|
||||
};
|
||||
|
||||
const hasProto = (v, constructor, predicate) => {
|
||||
if (predicate(v, constructor.prototype)) {
|
||||
return true;
|
||||
} else {
|
||||
return v.constructor?.name === constructor.name;
|
||||
}
|
||||
};
|
||||
const typeOf = x => {
|
||||
const t = typeof x;
|
||||
if (x === null) {
|
||||
return 'null';
|
||||
} else if (t === 'object' && Array.isArray(x)) {
|
||||
return 'array';
|
||||
} else if (t === 'object' && hasProto(x, String, (o, proto) => proto.isPrototypeOf(o))) {
|
||||
return 'string';
|
||||
} else {
|
||||
return t;
|
||||
}
|
||||
};
|
||||
const isType = type => value => typeOf(value) === type;
|
||||
const isSimpleType = type => value => typeof value === type;
|
||||
const eq = t => a => t === a;
|
||||
const isString = isType('string');
|
||||
const isUndefined = eq(undefined);
|
||||
const isNullable = a => a === null || a === undefined;
|
||||
const isNonNullable = a => !isNullable(a);
|
||||
const isFunction = isSimpleType('function');
|
||||
|
||||
const constant = value => {
|
||||
return () => {
|
||||
return value;
|
||||
};
|
||||
};
|
||||
const never = constant(false);
|
||||
|
||||
class Optional {
|
||||
constructor(tag, value) {
|
||||
this.tag = tag;
|
||||
this.value = value;
|
||||
}
|
||||
static some(value) {
|
||||
return new Optional(true, value);
|
||||
}
|
||||
static none() {
|
||||
return Optional.singletonNone;
|
||||
}
|
||||
fold(onNone, onSome) {
|
||||
if (this.tag) {
|
||||
return onSome(this.value);
|
||||
} else {
|
||||
return onNone();
|
||||
}
|
||||
}
|
||||
isSome() {
|
||||
return this.tag;
|
||||
}
|
||||
isNone() {
|
||||
return !this.tag;
|
||||
}
|
||||
map(mapper) {
|
||||
if (this.tag) {
|
||||
return Optional.some(mapper(this.value));
|
||||
} else {
|
||||
return Optional.none();
|
||||
}
|
||||
}
|
||||
bind(binder) {
|
||||
if (this.tag) {
|
||||
return binder(this.value);
|
||||
} else {
|
||||
return Optional.none();
|
||||
}
|
||||
}
|
||||
exists(predicate) {
|
||||
return this.tag && predicate(this.value);
|
||||
}
|
||||
forall(predicate) {
|
||||
return !this.tag || predicate(this.value);
|
||||
}
|
||||
filter(predicate) {
|
||||
if (!this.tag || predicate(this.value)) {
|
||||
return this;
|
||||
} else {
|
||||
return Optional.none();
|
||||
}
|
||||
}
|
||||
getOr(replacement) {
|
||||
return this.tag ? this.value : replacement;
|
||||
}
|
||||
or(replacement) {
|
||||
return this.tag ? this : replacement;
|
||||
}
|
||||
getOrThunk(thunk) {
|
||||
return this.tag ? this.value : thunk();
|
||||
}
|
||||
orThunk(thunk) {
|
||||
return this.tag ? this : thunk();
|
||||
}
|
||||
getOrDie(message) {
|
||||
if (!this.tag) {
|
||||
throw new Error(message ?? 'Called getOrDie on None');
|
||||
} else {
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
static from(value) {
|
||||
return isNonNullable(value) ? Optional.some(value) : Optional.none();
|
||||
}
|
||||
getOrNull() {
|
||||
return this.tag ? this.value : null;
|
||||
}
|
||||
getOrUndefined() {
|
||||
return this.value;
|
||||
}
|
||||
each(worker) {
|
||||
if (this.tag) {
|
||||
worker(this.value);
|
||||
}
|
||||
}
|
||||
toArray() {
|
||||
return this.tag ? [this.value] : [];
|
||||
}
|
||||
toString() {
|
||||
return this.tag ? `some(${ this.value })` : 'none()';
|
||||
}
|
||||
}
|
||||
Optional.singletonNone = new Optional(false);
|
||||
|
||||
const nativeSlice = Array.prototype.slice;
|
||||
const nativeIndexOf = Array.prototype.indexOf;
|
||||
const rawIndexOf = (ts, t) => nativeIndexOf.call(ts, t);
|
||||
const contains = (xs, x) => rawIndexOf(xs, x) > -1;
|
||||
const map = (xs, f) => {
|
||||
const len = xs.length;
|
||||
const r = new Array(len);
|
||||
for (let i = 0; i < len; i++) {
|
||||
const x = xs[i];
|
||||
r[i] = f(x, i);
|
||||
}
|
||||
return r;
|
||||
};
|
||||
const filter = (xs, pred) => {
|
||||
const r = [];
|
||||
for (let i = 0, len = xs.length; i < len; i++) {
|
||||
const x = xs[i];
|
||||
if (pred(x, i)) {
|
||||
r.push(x);
|
||||
}
|
||||
}
|
||||
return r;
|
||||
};
|
||||
const findUntil = (xs, pred, until) => {
|
||||
for (let i = 0, len = xs.length; i < len; i++) {
|
||||
const x = xs[i];
|
||||
if (pred(x, i)) {
|
||||
return Optional.some(x);
|
||||
} else if (until(x, i)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Optional.none();
|
||||
};
|
||||
const find = (xs, pred) => {
|
||||
return findUntil(xs, pred, never);
|
||||
};
|
||||
const sort = (xs, comparator) => {
|
||||
const copy = nativeSlice.call(xs, 0);
|
||||
copy.sort(comparator);
|
||||
return copy;
|
||||
};
|
||||
|
||||
const keys = Object.keys;
|
||||
const hasOwnProperty = Object.hasOwnProperty;
|
||||
const get = (obj, key) => {
|
||||
return has(obj, key) ? Optional.from(obj[key]) : Optional.none();
|
||||
};
|
||||
const has = (obj, key) => hasOwnProperty.call(obj, key);
|
||||
|
||||
const cat = arr => {
|
||||
const r = [];
|
||||
const push = x => {
|
||||
r.push(x);
|
||||
};
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
arr[i].each(push);
|
||||
}
|
||||
return r;
|
||||
};
|
||||
|
||||
const description = `<h1>Editor UI keyboard navigation</h1>
|
||||
|
||||
<h2>Activating keyboard navigation</h2>
|
||||
|
||||
<p>The sections of the outer UI of the editor - the menubar, toolbar, sidebar and footer - are all keyboard navigable. As such, there are multiple ways to activate keyboard navigation:</p>
|
||||
<ul>
|
||||
<li>Focus the menubar: Alt + F9 (Windows) or ⌥F9 (MacOS)</li>
|
||||
<li>Focus the toolbar: Alt + F10 (Windows) or ⌥F10 (MacOS)</li>
|
||||
<li>Focus the footer: Alt + F11 (Windows) or ⌥F11 (MacOS)</li>
|
||||
</ul>
|
||||
|
||||
<p>Focusing the menubar or toolbar will start keyboard navigation at the first item in the menubar or toolbar, which will be highlighted with a gray background. Focusing the footer will start keyboard navigation at the first item in the element path, which will be highlighted with an underline. </p>
|
||||
|
||||
<h2>Moving between UI sections</h2>
|
||||
|
||||
<p>When keyboard navigation is active, pressing tab will move the focus to the next major section of the UI, where applicable. These sections are:</p>
|
||||
<ul>
|
||||
<li>the menubar</li>
|
||||
<li>each group of the toolbar </li>
|
||||
<li>the sidebar</li>
|
||||
<li>the element path in the footer </li>
|
||||
<li>the wordcount toggle button in the footer </li>
|
||||
<li>the branding link in the footer </li>
|
||||
<li>the editor resize handle in the footer</li>
|
||||
</ul>
|
||||
|
||||
<p>Pressing shift + tab will move backwards through the same sections, except when moving from the footer to the toolbar. Focusing the element path then pressing shift + tab will move focus to the first toolbar group, not the last.</p>
|
||||
|
||||
<h2>Moving within UI sections</h2>
|
||||
|
||||
<p>Keyboard navigation within UI sections can usually be achieved using the left and right arrow keys. This includes:</p>
|
||||
<ul>
|
||||
<li>moving between menus in the menubar</li>
|
||||
<li>moving between buttons in a toolbar group</li>
|
||||
<li>moving between items in the element path</li>
|
||||
</ul>
|
||||
|
||||
<p>In all these UI sections, keyboard navigation will cycle within the section. For example, focusing the last button in a toolbar group then pressing right arrow will move focus to the first item in the same toolbar group. </p>
|
||||
|
||||
<h1>Executing buttons</h1>
|
||||
|
||||
<p>To execute a button, navigate the selection to the desired button and hit space or enter.</p>
|
||||
|
||||
<h1>Opening, navigating and closing menus</h1>
|
||||
|
||||
<p>When focusing a menubar button or a toolbar button with a menu, pressing space, enter or down arrow will open the menu. When the menu opens the first item will be selected. To move up or down the menu, press the up or down arrow key respectively. This is the same for submenus, which can also be opened and closed using the left and right arrow keys.</p>
|
||||
|
||||
<p>To close any active menu, hit the escape key. When a menu is closed the selection will be restored to its previous selection. This also works for closing submenus.</p>
|
||||
|
||||
<h1>Context toolbars and menus</h1>
|
||||
|
||||
<p>To focus an open context toolbar such as the table context toolbar, press Ctrl + F9 (Windows) or ⌃F9 (MacOS).</p>
|
||||
|
||||
<p>Context toolbar navigation is the same as toolbar navigation, and context menu navigation is the same as standard menu navigation.</p>
|
||||
|
||||
<h1>Dialog navigation</h1>
|
||||
|
||||
<p>There are two types of dialog UIs in TinyMCE: tabbed dialogs and non-tabbed dialogs.</p>
|
||||
|
||||
<p>When a non-tabbed dialog is opened, the first interactive component in the dialog will be focused. Users can navigate between interactive components by pressing tab. This includes any footer buttons. Navigation will cycle back to the first dialog component if tab is pressed while focusing the last component in the dialog. Pressing shift + tab will navigate backwards.</p>
|
||||
|
||||
<p>When a tabbed dialog is opened, the first button in the tab menu is focused. Pressing tab will navigate to the first interactive component in that tab, and will cycle through the tab\u2019s components, the footer buttons, then back to the tab button. To switch to another tab, focus the tab button for the current tab, then use the arrow keys to cycle through the tab buttons.</p>`;
|
||||
const tab$3 = () => {
|
||||
const body = {
|
||||
type: 'htmlpanel',
|
||||
presets: 'document',
|
||||
html: description
|
||||
};
|
||||
return {
|
||||
name: 'keyboardnav',
|
||||
title: 'Keyboard Navigation',
|
||||
items: [body]
|
||||
};
|
||||
};
|
||||
|
||||
var global$2 = tinymce.util.Tools.resolve('tinymce.Env');
|
||||
|
||||
const convertText = source => {
|
||||
const isMac = global$2.os.isMacOS() || global$2.os.isiOS();
|
||||
const mac = {
|
||||
alt: '⌥',
|
||||
ctrl: '⌃',
|
||||
shift: '⇧',
|
||||
meta: '⌘',
|
||||
access: '⌃⌥'
|
||||
};
|
||||
const other = {
|
||||
meta: 'Ctrl ',
|
||||
access: 'Shift + Alt '
|
||||
};
|
||||
const replace = isMac ? mac : other;
|
||||
const shortcut = source.split('+');
|
||||
const updated = map(shortcut, segment => {
|
||||
const search = segment.toLowerCase().trim();
|
||||
return has(replace, search) ? replace[search] : segment;
|
||||
});
|
||||
return isMac ? updated.join('').replace(/\s/, '') : updated.join('+');
|
||||
};
|
||||
|
||||
const shortcuts = [
|
||||
{
|
||||
shortcuts: ['Meta + B'],
|
||||
action: 'Bold'
|
||||
},
|
||||
{
|
||||
shortcuts: ['Meta + I'],
|
||||
action: 'Italic'
|
||||
},
|
||||
{
|
||||
shortcuts: ['Meta + U'],
|
||||
action: 'Underline'
|
||||
},
|
||||
{
|
||||
shortcuts: ['Meta + A'],
|
||||
action: 'Select all'
|
||||
},
|
||||
{
|
||||
shortcuts: [
|
||||
'Meta + Y',
|
||||
'Meta + Shift + Z'
|
||||
],
|
||||
action: 'Redo'
|
||||
},
|
||||
{
|
||||
shortcuts: ['Meta + Z'],
|
||||
action: 'Undo'
|
||||
},
|
||||
{
|
||||
shortcuts: ['Access + 1'],
|
||||
action: 'Heading 1'
|
||||
},
|
||||
{
|
||||
shortcuts: ['Access + 2'],
|
||||
action: 'Heading 2'
|
||||
},
|
||||
{
|
||||
shortcuts: ['Access + 3'],
|
||||
action: 'Heading 3'
|
||||
},
|
||||
{
|
||||
shortcuts: ['Access + 4'],
|
||||
action: 'Heading 4'
|
||||
},
|
||||
{
|
||||
shortcuts: ['Access + 5'],
|
||||
action: 'Heading 5'
|
||||
},
|
||||
{
|
||||
shortcuts: ['Access + 6'],
|
||||
action: 'Heading 6'
|
||||
},
|
||||
{
|
||||
shortcuts: ['Access + 7'],
|
||||
action: 'Paragraph'
|
||||
},
|
||||
{
|
||||
shortcuts: ['Access + 8'],
|
||||
action: 'Div'
|
||||
},
|
||||
{
|
||||
shortcuts: ['Access + 9'],
|
||||
action: 'Address'
|
||||
},
|
||||
{
|
||||
shortcuts: ['Alt + 0'],
|
||||
action: 'Open help dialog'
|
||||
},
|
||||
{
|
||||
shortcuts: ['Alt + F9'],
|
||||
action: 'Focus to menubar'
|
||||
},
|
||||
{
|
||||
shortcuts: ['Alt + F10'],
|
||||
action: 'Focus to toolbar'
|
||||
},
|
||||
{
|
||||
shortcuts: ['Alt + F11'],
|
||||
action: 'Focus to element path'
|
||||
},
|
||||
{
|
||||
shortcuts: ['Ctrl + F9'],
|
||||
action: 'Focus to contextual toolbar'
|
||||
},
|
||||
{
|
||||
shortcuts: ['Shift + Enter'],
|
||||
action: 'Open popup menu for split buttons'
|
||||
},
|
||||
{
|
||||
shortcuts: ['Meta + K'],
|
||||
action: 'Insert link (if link plugin activated)'
|
||||
},
|
||||
{
|
||||
shortcuts: ['Meta + S'],
|
||||
action: 'Save (if save plugin activated)'
|
||||
},
|
||||
{
|
||||
shortcuts: ['Meta + F'],
|
||||
action: 'Find (if searchreplace plugin activated)'
|
||||
},
|
||||
{
|
||||
shortcuts: ['Meta + Shift + F'],
|
||||
action: 'Switch to or from fullscreen mode'
|
||||
}
|
||||
];
|
||||
|
||||
const tab$2 = () => {
|
||||
const shortcutList = map(shortcuts, shortcut => {
|
||||
const shortcutText = map(shortcut.shortcuts, convertText).join(' or ');
|
||||
return [
|
||||
shortcut.action,
|
||||
shortcutText
|
||||
];
|
||||
});
|
||||
const tablePanel = {
|
||||
type: 'table',
|
||||
header: [
|
||||
'Action',
|
||||
'Shortcut'
|
||||
],
|
||||
cells: shortcutList
|
||||
};
|
||||
return {
|
||||
name: 'shortcuts',
|
||||
title: 'Handy Shortcuts',
|
||||
items: [tablePanel]
|
||||
};
|
||||
};
|
||||
|
||||
var global$1 = tinymce.util.Tools.resolve('tinymce.util.I18n');
|
||||
|
||||
const urls = map([
|
||||
{
|
||||
key: 'advlist',
|
||||
name: 'Advanced List'
|
||||
},
|
||||
{
|
||||
key: 'anchor',
|
||||
name: 'Anchor'
|
||||
},
|
||||
{
|
||||
key: 'autolink',
|
||||
name: 'Autolink'
|
||||
},
|
||||
{
|
||||
key: 'autoresize',
|
||||
name: 'Autoresize'
|
||||
},
|
||||
{
|
||||
key: 'autosave',
|
||||
name: 'Autosave'
|
||||
},
|
||||
{
|
||||
key: 'charmap',
|
||||
name: 'Character Map'
|
||||
},
|
||||
{
|
||||
key: 'code',
|
||||
name: 'Code'
|
||||
},
|
||||
{
|
||||
key: 'codesample',
|
||||
name: 'Code Sample'
|
||||
},
|
||||
{
|
||||
key: 'colorpicker',
|
||||
name: 'Color Picker'
|
||||
},
|
||||
{
|
||||
key: 'directionality',
|
||||
name: 'Directionality'
|
||||
},
|
||||
{
|
||||
key: 'emoticons',
|
||||
name: 'Emoticons'
|
||||
},
|
||||
{
|
||||
key: 'fullscreen',
|
||||
name: 'Full Screen'
|
||||
},
|
||||
{
|
||||
key: 'help',
|
||||
name: 'Help'
|
||||
},
|
||||
{
|
||||
key: 'image',
|
||||
name: 'Image'
|
||||
},
|
||||
{
|
||||
key: 'importcss',
|
||||
name: 'Import CSS'
|
||||
},
|
||||
{
|
||||
key: 'insertdatetime',
|
||||
name: 'Insert Date/Time'
|
||||
},
|
||||
{
|
||||
key: 'link',
|
||||
name: 'Link'
|
||||
},
|
||||
{
|
||||
key: 'lists',
|
||||
name: 'Lists'
|
||||
},
|
||||
{
|
||||
key: 'media',
|
||||
name: 'Media'
|
||||
},
|
||||
{
|
||||
key: 'nonbreaking',
|
||||
name: 'Nonbreaking'
|
||||
},
|
||||
{
|
||||
key: 'pagebreak',
|
||||
name: 'Page Break'
|
||||
},
|
||||
{
|
||||
key: 'preview',
|
||||
name: 'Preview'
|
||||
},
|
||||
{
|
||||
key: 'quickbars',
|
||||
name: 'Quick Toolbars'
|
||||
},
|
||||
{
|
||||
key: 'save',
|
||||
name: 'Save'
|
||||
},
|
||||
{
|
||||
key: 'searchreplace',
|
||||
name: 'Search and Replace'
|
||||
},
|
||||
{
|
||||
key: 'table',
|
||||
name: 'Table'
|
||||
},
|
||||
{
|
||||
key: 'template',
|
||||
name: 'Template'
|
||||
},
|
||||
{
|
||||
key: 'textcolor',
|
||||
name: 'Text Color'
|
||||
},
|
||||
{
|
||||
key: 'visualblocks',
|
||||
name: 'Visual Blocks'
|
||||
},
|
||||
{
|
||||
key: 'visualchars',
|
||||
name: 'Visual Characters'
|
||||
},
|
||||
{
|
||||
key: 'wordcount',
|
||||
name: 'Word Count'
|
||||
},
|
||||
{
|
||||
key: 'a11ychecker',
|
||||
name: 'Accessibility Checker',
|
||||
type: 'premium'
|
||||
},
|
||||
{
|
||||
key: 'advcode',
|
||||
name: 'Advanced Code Editor',
|
||||
type: 'premium'
|
||||
},
|
||||
{
|
||||
key: 'advtable',
|
||||
name: 'Advanced Tables',
|
||||
type: 'premium'
|
||||
},
|
||||
{
|
||||
key: 'casechange',
|
||||
name: 'Case Change',
|
||||
type: 'premium'
|
||||
},
|
||||
{
|
||||
key: 'checklist',
|
||||
name: 'Checklist',
|
||||
type: 'premium'
|
||||
},
|
||||
{
|
||||
key: 'editimage',
|
||||
name: 'Enhanced Image Editing',
|
||||
type: 'premium'
|
||||
},
|
||||
{
|
||||
key: 'footnotes',
|
||||
name: 'Footnotes',
|
||||
type: 'premium'
|
||||
},
|
||||
{
|
||||
key: 'mediaembed',
|
||||
name: 'Enhanced Media Embed',
|
||||
type: 'premium',
|
||||
slug: 'introduction-to-mediaembed'
|
||||
},
|
||||
{
|
||||
key: 'export',
|
||||
name: 'Export',
|
||||
type: 'premium'
|
||||
},
|
||||
{
|
||||
key: 'formatpainter',
|
||||
name: 'Format Painter',
|
||||
type: 'premium'
|
||||
},
|
||||
{
|
||||
key: 'linkchecker',
|
||||
name: 'Link Checker',
|
||||
type: 'premium'
|
||||
},
|
||||
{
|
||||
key: 'mentions',
|
||||
name: 'Mentions',
|
||||
type: 'premium'
|
||||
},
|
||||
{
|
||||
key: 'mergetags',
|
||||
name: 'Merge Tags',
|
||||
type: 'premium'
|
||||
},
|
||||
{
|
||||
key: 'pageembed',
|
||||
name: 'Page Embed',
|
||||
type: 'premium'
|
||||
},
|
||||
{
|
||||
key: 'permanentpen',
|
||||
name: 'Permanent Pen',
|
||||
type: 'premium'
|
||||
},
|
||||
{
|
||||
key: 'powerpaste',
|
||||
name: 'PowerPaste',
|
||||
type: 'premium',
|
||||
slug: 'introduction-to-powerpaste'
|
||||
},
|
||||
{
|
||||
key: 'rtc',
|
||||
name: 'Real-Time Collaboration',
|
||||
type: 'premium',
|
||||
slug: 'rtc-introduction'
|
||||
},
|
||||
{
|
||||
key: 'tinymcespellchecker',
|
||||
name: 'Spell Checker Pro',
|
||||
type: 'premium',
|
||||
slug: 'introduction-to-tiny-spellchecker'
|
||||
},
|
||||
{
|
||||
key: 'autocorrect',
|
||||
name: 'Spelling Autocorrect',
|
||||
type: 'premium'
|
||||
},
|
||||
{
|
||||
key: 'tinycomments',
|
||||
name: 'Tiny Comments',
|
||||
type: 'premium',
|
||||
slug: 'introduction-to-tiny-comments'
|
||||
},
|
||||
{
|
||||
key: 'tinydrive',
|
||||
name: 'Tiny Drive',
|
||||
type: 'premium',
|
||||
slug: 'tinydrive-introduction'
|
||||
},
|
||||
{
|
||||
key: 'tableofcontents',
|
||||
name: 'Table of Contents',
|
||||
type: 'premium'
|
||||
}
|
||||
], item => ({
|
||||
...item,
|
||||
type: item.type || 'opensource',
|
||||
slug: item.slug || item.key
|
||||
}));
|
||||
|
||||
const tab$1 = editor => {
|
||||
const availablePlugins = () => {
|
||||
const premiumPlugins = filter(urls, ({type}) => {
|
||||
return type === 'premium';
|
||||
});
|
||||
const sortedPremiumPlugins = sort(map(premiumPlugins, p => p.name), (s1, s2) => s1.localeCompare(s2));
|
||||
const premiumPluginList = map(sortedPremiumPlugins, pluginName => `<li>${ pluginName }</li>`).join('');
|
||||
return '<div data-mce-tabstop="1" tabindex="-1">' + '<p><b>' + global$1.translate('Premium plugins:') + '</b></p>' + '<ul>' + premiumPluginList + '<li class="tox-help__more-link" "><a href="https://www.tiny.cloud/pricing/?utm_campaign=editor_referral&utm_medium=help_dialog&utm_source=tinymce" rel="noopener" target="_blank">' + global$1.translate('Learn more...') + '</a></li>' + '</ul>' + '</div>';
|
||||
};
|
||||
const makeLink = p => `<a href="${ p.url }" target="_blank" rel="noopener">${ p.name }</a>`;
|
||||
const identifyUnknownPlugin = (editor, key) => {
|
||||
const getMetadata = editor.plugins[key].getMetadata;
|
||||
if (isFunction(getMetadata)) {
|
||||
const metadata = getMetadata();
|
||||
return {
|
||||
name: metadata.name,
|
||||
html: makeLink(metadata)
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
name: key,
|
||||
html: key
|
||||
};
|
||||
}
|
||||
};
|
||||
const getPluginData = (editor, key) => find(urls, x => {
|
||||
return x.key === key;
|
||||
}).fold(() => {
|
||||
return identifyUnknownPlugin(editor, key);
|
||||
}, x => {
|
||||
const name = x.type === 'premium' ? `${ x.name }*` : x.name;
|
||||
const html = makeLink({
|
||||
name,
|
||||
url: `https://www.tiny.cloud/docs/tinymce/6/${ x.slug }/`
|
||||
});
|
||||
return {
|
||||
name,
|
||||
html
|
||||
};
|
||||
});
|
||||
const getPluginKeys = editor => {
|
||||
const keys$1 = keys(editor.plugins);
|
||||
const forcedPlugins = getForcedPlugins(editor);
|
||||
return isUndefined(forcedPlugins) ? keys$1 : filter(keys$1, k => !contains(forcedPlugins, k));
|
||||
};
|
||||
const pluginLister = editor => {
|
||||
const pluginKeys = getPluginKeys(editor);
|
||||
const sortedPluginData = sort(map(pluginKeys, k => getPluginData(editor, k)), (pd1, pd2) => pd1.name.localeCompare(pd2.name));
|
||||
const pluginLis = map(sortedPluginData, key => {
|
||||
return '<li>' + key.html + '</li>';
|
||||
});
|
||||
const count = pluginLis.length;
|
||||
const pluginsString = pluginLis.join('');
|
||||
const html = '<p><b>' + global$1.translate([
|
||||
'Plugins installed ({0}):',
|
||||
count
|
||||
]) + '</b></p>' + '<ul>' + pluginsString + '</ul>';
|
||||
return html;
|
||||
};
|
||||
const installedPlugins = editor => {
|
||||
if (editor == null) {
|
||||
return '';
|
||||
}
|
||||
return '<div data-mce-tabstop="1" tabindex="-1">' + pluginLister(editor) + '</div>';
|
||||
};
|
||||
const htmlPanel = {
|
||||
type: 'htmlpanel',
|
||||
presets: 'document',
|
||||
html: [
|
||||
installedPlugins(editor),
|
||||
availablePlugins()
|
||||
].join('')
|
||||
};
|
||||
return {
|
||||
name: 'plugins',
|
||||
title: 'Plugins',
|
||||
items: [htmlPanel]
|
||||
};
|
||||
};
|
||||
|
||||
var global = tinymce.util.Tools.resolve('tinymce.EditorManager');
|
||||
|
||||
const tab = () => {
|
||||
const getVersion = (major, minor) => major.indexOf('@') === 0 ? 'X.X.X' : major + '.' + minor;
|
||||
const version = getVersion(global.majorVersion, global.minorVersion);
|
||||
const changeLogLink = '<a href="https://www.tiny.cloud/docs/tinymce/6/changelog/?utm_campaign=editor_referral&utm_medium=help_dialog&utm_source=tinymce" rel="noopener" target="_blank">TinyMCE ' + version + '</a>';
|
||||
const htmlPanel = {
|
||||
type: 'htmlpanel',
|
||||
html: '<p>' + global$1.translate([
|
||||
'You are using {0}',
|
||||
changeLogLink
|
||||
]) + '</p>',
|
||||
presets: 'document'
|
||||
};
|
||||
return {
|
||||
name: 'versions',
|
||||
title: 'Version',
|
||||
items: [htmlPanel]
|
||||
};
|
||||
};
|
||||
|
||||
const parseHelpTabsSetting = (tabsFromSettings, tabs) => {
|
||||
const newTabs = {};
|
||||
const names = map(tabsFromSettings, t => {
|
||||
if (isString(t)) {
|
||||
if (has(tabs, t)) {
|
||||
newTabs[t] = tabs[t];
|
||||
}
|
||||
return t;
|
||||
} else {
|
||||
const name = t.name ?? generate('tab-name');
|
||||
newTabs[name] = t;
|
||||
return name;
|
||||
}
|
||||
});
|
||||
return {
|
||||
tabs: newTabs,
|
||||
names
|
||||
};
|
||||
};
|
||||
const getNamesFromTabs = tabs => {
|
||||
const names = keys(tabs);
|
||||
const idx = names.indexOf('versions');
|
||||
if (idx !== -1) {
|
||||
names.splice(idx, 1);
|
||||
names.push('versions');
|
||||
}
|
||||
return {
|
||||
tabs,
|
||||
names
|
||||
};
|
||||
};
|
||||
const parseCustomTabs = (editor, customTabs) => {
|
||||
const shortcuts = tab$2();
|
||||
const nav = tab$3();
|
||||
const plugins = tab$1(editor);
|
||||
const versions = tab();
|
||||
const tabs = {
|
||||
[shortcuts.name]: shortcuts,
|
||||
[nav.name]: nav,
|
||||
[plugins.name]: plugins,
|
||||
[versions.name]: versions,
|
||||
...customTabs.get()
|
||||
};
|
||||
return Optional.from(getHelpTabs(editor)).fold(() => getNamesFromTabs(tabs), tabsFromSettings => parseHelpTabsSetting(tabsFromSettings, tabs));
|
||||
};
|
||||
const init = (editor, customTabs) => () => {
|
||||
const {tabs, names} = parseCustomTabs(editor, customTabs);
|
||||
const foundTabs = map(names, name => get(tabs, name));
|
||||
const dialogTabs = cat(foundTabs);
|
||||
const body = {
|
||||
type: 'tabpanel',
|
||||
tabs: dialogTabs
|
||||
};
|
||||
editor.windowManager.open({
|
||||
title: 'Help',
|
||||
size: 'medium',
|
||||
body,
|
||||
buttons: [{
|
||||
type: 'cancel',
|
||||
name: 'close',
|
||||
text: 'Close',
|
||||
primary: true
|
||||
}],
|
||||
initialData: {}
|
||||
});
|
||||
};
|
||||
|
||||
var Plugin = () => {
|
||||
global$3.add('help', editor => {
|
||||
const customTabs = Cell({});
|
||||
const api = get$1(customTabs);
|
||||
register$1(editor);
|
||||
const dialogOpener = init(editor, customTabs);
|
||||
register(editor, dialogOpener);
|
||||
register$2(editor, dialogOpener);
|
||||
editor.shortcuts.add('Alt+0', 'Open help dialog', 'mceHelp');
|
||||
return api;
|
||||
});
|
||||
};
|
||||
|
||||
Plugin();
|
||||
|
||||
})();
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -0,0 +1,343 @@
|
||||
/**
|
||||
* TinyMCE version 6.2.0 (2022-09-08)
|
||||
*/
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
var global$4 = tinymce.util.Tools.resolve('tinymce.PluginManager');
|
||||
|
||||
const hasProto = (v, constructor, predicate) => {
|
||||
if (predicate(v, constructor.prototype)) {
|
||||
return true;
|
||||
} else {
|
||||
return v.constructor?.name === constructor.name;
|
||||
}
|
||||
};
|
||||
const typeOf = x => {
|
||||
const t = typeof x;
|
||||
if (x === null) {
|
||||
return 'null';
|
||||
} else if (t === 'object' && Array.isArray(x)) {
|
||||
return 'array';
|
||||
} else if (t === 'object' && hasProto(x, String, (o, proto) => proto.isPrototypeOf(o))) {
|
||||
return 'string';
|
||||
} else {
|
||||
return t;
|
||||
}
|
||||
};
|
||||
const isType = type => value => typeOf(value) === type;
|
||||
const isSimpleType = type => value => typeof value === type;
|
||||
const isString = isType('string');
|
||||
const isObject = isType('object');
|
||||
const isArray = isType('array');
|
||||
const isFunction = isSimpleType('function');
|
||||
|
||||
var global$3 = tinymce.util.Tools.resolve('tinymce.dom.DOMUtils');
|
||||
|
||||
var global$2 = tinymce.util.Tools.resolve('tinymce.EditorManager');
|
||||
|
||||
var global$1 = tinymce.util.Tools.resolve('tinymce.Env');
|
||||
|
||||
var global = tinymce.util.Tools.resolve('tinymce.util.Tools');
|
||||
|
||||
const option = name => editor => editor.options.get(name);
|
||||
const register = editor => {
|
||||
const registerOption = editor.options.register;
|
||||
const filterProcessor = value => isString(value) || isFunction(value) || isObject(value);
|
||||
registerOption('importcss_merge_classes', {
|
||||
processor: 'boolean',
|
||||
default: true
|
||||
});
|
||||
registerOption('importcss_exclusive', {
|
||||
processor: 'boolean',
|
||||
default: true
|
||||
});
|
||||
registerOption('importcss_selector_converter', { processor: 'function' });
|
||||
registerOption('importcss_selector_filter', { processor: filterProcessor });
|
||||
registerOption('importcss_file_filter', { processor: filterProcessor });
|
||||
registerOption('importcss_groups', { processor: 'object[]' });
|
||||
registerOption('importcss_append', {
|
||||
processor: 'boolean',
|
||||
default: false
|
||||
});
|
||||
};
|
||||
const shouldMergeClasses = option('importcss_merge_classes');
|
||||
const shouldImportExclusive = option('importcss_exclusive');
|
||||
const getSelectorConverter = option('importcss_selector_converter');
|
||||
const getSelectorFilter = option('importcss_selector_filter');
|
||||
const getCssGroups = option('importcss_groups');
|
||||
const shouldAppend = option('importcss_append');
|
||||
const getFileFilter = option('importcss_file_filter');
|
||||
const getSkin = option('skin');
|
||||
const getSkinUrl = option('skin_url');
|
||||
|
||||
const nativePush = Array.prototype.push;
|
||||
const map = (xs, f) => {
|
||||
const len = xs.length;
|
||||
const r = new Array(len);
|
||||
for (let i = 0; i < len; i++) {
|
||||
const x = xs[i];
|
||||
r[i] = f(x, i);
|
||||
}
|
||||
return r;
|
||||
};
|
||||
const flatten = xs => {
|
||||
const r = [];
|
||||
for (let i = 0, len = xs.length; i < len; ++i) {
|
||||
if (!isArray(xs[i])) {
|
||||
throw new Error('Arr.flatten item ' + i + ' was not an array, input: ' + xs);
|
||||
}
|
||||
nativePush.apply(r, xs[i]);
|
||||
}
|
||||
return r;
|
||||
};
|
||||
const bind = (xs, f) => flatten(map(xs, f));
|
||||
|
||||
const generate = () => {
|
||||
const ungroupedOrder = [];
|
||||
const groupOrder = [];
|
||||
const groups = {};
|
||||
const addItemToGroup = (groupTitle, itemInfo) => {
|
||||
if (groups[groupTitle]) {
|
||||
groups[groupTitle].push(itemInfo);
|
||||
} else {
|
||||
groupOrder.push(groupTitle);
|
||||
groups[groupTitle] = [itemInfo];
|
||||
}
|
||||
};
|
||||
const addItem = itemInfo => {
|
||||
ungroupedOrder.push(itemInfo);
|
||||
};
|
||||
const toFormats = () => {
|
||||
const groupItems = bind(groupOrder, g => {
|
||||
const items = groups[g];
|
||||
return items.length === 0 ? [] : [{
|
||||
title: g,
|
||||
items
|
||||
}];
|
||||
});
|
||||
return groupItems.concat(ungroupedOrder);
|
||||
};
|
||||
return {
|
||||
addItemToGroup,
|
||||
addItem,
|
||||
toFormats
|
||||
};
|
||||
};
|
||||
|
||||
const internalEditorStyle = /^\.(?:ephox|tiny-pageembed|mce)(?:[.-]+\w+)+$/;
|
||||
const removeCacheSuffix = url => {
|
||||
const cacheSuffix = global$1.cacheSuffix;
|
||||
if (isString(url)) {
|
||||
url = url.replace('?' + cacheSuffix, '').replace('&' + cacheSuffix, '');
|
||||
}
|
||||
return url;
|
||||
};
|
||||
const isSkinContentCss = (editor, href) => {
|
||||
const skin = getSkin(editor);
|
||||
if (skin) {
|
||||
const skinUrlBase = getSkinUrl(editor);
|
||||
const skinUrl = skinUrlBase ? editor.documentBaseURI.toAbsolute(skinUrlBase) : global$2.baseURL + '/skins/ui/' + skin;
|
||||
const contentSkinUrlPart = global$2.baseURL + '/skins/content/';
|
||||
return href === skinUrl + '/content' + (editor.inline ? '.inline' : '') + '.min.css' || href.indexOf(contentSkinUrlPart) !== -1;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
const compileFilter = filter => {
|
||||
if (isString(filter)) {
|
||||
return value => {
|
||||
return value.indexOf(filter) !== -1;
|
||||
};
|
||||
} else if (filter instanceof RegExp) {
|
||||
return value => {
|
||||
return filter.test(value);
|
||||
};
|
||||
}
|
||||
return filter;
|
||||
};
|
||||
const isCssImportRule = rule => rule.styleSheet;
|
||||
const isCssPageRule = rule => rule.selectorText;
|
||||
const getSelectors = (editor, doc, fileFilter) => {
|
||||
const selectors = [];
|
||||
const contentCSSUrls = {};
|
||||
const append = (styleSheet, imported) => {
|
||||
let href = styleSheet.href;
|
||||
let rules;
|
||||
href = removeCacheSuffix(href);
|
||||
if (!href || fileFilter && !fileFilter(href, imported) || isSkinContentCss(editor, href)) {
|
||||
return;
|
||||
}
|
||||
global.each(styleSheet.imports, styleSheet => {
|
||||
append(styleSheet, true);
|
||||
});
|
||||
try {
|
||||
rules = styleSheet.cssRules || styleSheet.rules;
|
||||
} catch (e) {
|
||||
}
|
||||
global.each(rules, cssRule => {
|
||||
if (isCssImportRule(cssRule)) {
|
||||
append(cssRule.styleSheet, true);
|
||||
} else if (isCssPageRule(cssRule)) {
|
||||
global.each(cssRule.selectorText.split(','), selector => {
|
||||
selectors.push(global.trim(selector));
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
global.each(editor.contentCSS, url => {
|
||||
contentCSSUrls[url] = true;
|
||||
});
|
||||
if (!fileFilter) {
|
||||
fileFilter = (href, imported) => {
|
||||
return imported || contentCSSUrls[href];
|
||||
};
|
||||
}
|
||||
try {
|
||||
global.each(doc.styleSheets, styleSheet => {
|
||||
append(styleSheet);
|
||||
});
|
||||
} catch (e) {
|
||||
}
|
||||
return selectors;
|
||||
};
|
||||
const defaultConvertSelectorToFormat = (editor, selectorText) => {
|
||||
let format = {};
|
||||
const selector = /^(?:([a-z0-9\-_]+))?(\.[a-z0-9_\-\.]+)$/i.exec(selectorText);
|
||||
if (!selector) {
|
||||
return;
|
||||
}
|
||||
const elementName = selector[1];
|
||||
const classes = selector[2].substr(1).split('.').join(' ');
|
||||
const inlineSelectorElements = global.makeMap('a,img');
|
||||
if (selector[1]) {
|
||||
format = { title: selectorText };
|
||||
if (editor.schema.getTextBlockElements()[elementName]) {
|
||||
format.block = elementName;
|
||||
} else if (editor.schema.getBlockElements()[elementName] || inlineSelectorElements[elementName.toLowerCase()]) {
|
||||
format.selector = elementName;
|
||||
} else {
|
||||
format.inline = elementName;
|
||||
}
|
||||
} else if (selector[2]) {
|
||||
format = {
|
||||
inline: 'span',
|
||||
title: selectorText.substr(1),
|
||||
classes
|
||||
};
|
||||
}
|
||||
if (shouldMergeClasses(editor)) {
|
||||
format.classes = classes;
|
||||
} else {
|
||||
format.attributes = { class: classes };
|
||||
}
|
||||
return format;
|
||||
};
|
||||
const getGroupsBySelector = (groups, selector) => {
|
||||
return global.grep(groups, group => {
|
||||
return !group.filter || group.filter(selector);
|
||||
});
|
||||
};
|
||||
const compileUserDefinedGroups = groups => {
|
||||
return global.map(groups, group => {
|
||||
return global.extend({}, group, {
|
||||
original: group,
|
||||
selectors: {},
|
||||
filter: compileFilter(group.filter)
|
||||
});
|
||||
});
|
||||
};
|
||||
const isExclusiveMode = (editor, group) => {
|
||||
return group === null || shouldImportExclusive(editor);
|
||||
};
|
||||
const isUniqueSelector = (editor, selector, group, globallyUniqueSelectors) => {
|
||||
return !(isExclusiveMode(editor, group) ? selector in globallyUniqueSelectors : selector in group.selectors);
|
||||
};
|
||||
const markUniqueSelector = (editor, selector, group, globallyUniqueSelectors) => {
|
||||
if (isExclusiveMode(editor, group)) {
|
||||
globallyUniqueSelectors[selector] = true;
|
||||
} else {
|
||||
group.selectors[selector] = true;
|
||||
}
|
||||
};
|
||||
const convertSelectorToFormat = (editor, plugin, selector, group) => {
|
||||
let selectorConverter;
|
||||
const converter = getSelectorConverter(editor);
|
||||
if (group && group.selector_converter) {
|
||||
selectorConverter = group.selector_converter;
|
||||
} else if (converter) {
|
||||
selectorConverter = converter;
|
||||
} else {
|
||||
selectorConverter = () => {
|
||||
return defaultConvertSelectorToFormat(editor, selector);
|
||||
};
|
||||
}
|
||||
return selectorConverter.call(plugin, selector, group);
|
||||
};
|
||||
const setup = editor => {
|
||||
editor.on('init', () => {
|
||||
const model = generate();
|
||||
const globallyUniqueSelectors = {};
|
||||
const selectorFilter = compileFilter(getSelectorFilter(editor));
|
||||
const groups = compileUserDefinedGroups(getCssGroups(editor));
|
||||
const processSelector = (selector, group) => {
|
||||
if (isUniqueSelector(editor, selector, group, globallyUniqueSelectors)) {
|
||||
markUniqueSelector(editor, selector, group, globallyUniqueSelectors);
|
||||
const format = convertSelectorToFormat(editor, editor.plugins.importcss, selector, group);
|
||||
if (format) {
|
||||
const formatName = format.name || global$3.DOM.uniqueId();
|
||||
editor.formatter.register(formatName, format);
|
||||
return {
|
||||
title: format.title,
|
||||
format: formatName
|
||||
};
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
global.each(getSelectors(editor, editor.getDoc(), compileFilter(getFileFilter(editor))), selector => {
|
||||
if (!internalEditorStyle.test(selector)) {
|
||||
if (!selectorFilter || selectorFilter(selector)) {
|
||||
const selectorGroups = getGroupsBySelector(groups, selector);
|
||||
if (selectorGroups.length > 0) {
|
||||
global.each(selectorGroups, group => {
|
||||
const menuItem = processSelector(selector, group);
|
||||
if (menuItem) {
|
||||
model.addItemToGroup(group.title, menuItem);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
const menuItem = processSelector(selector, null);
|
||||
if (menuItem) {
|
||||
model.addItem(menuItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
const items = model.toFormats();
|
||||
editor.dispatch('addStyleModifications', {
|
||||
items,
|
||||
replace: !shouldAppend(editor)
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const get = editor => {
|
||||
const convertSelectorToFormat = selectorText => {
|
||||
return defaultConvertSelectorToFormat(editor, selectorText);
|
||||
};
|
||||
return { convertSelectorToFormat };
|
||||
};
|
||||
|
||||
var Plugin = () => {
|
||||
global$4.add('importcss', editor => {
|
||||
register(editor);
|
||||
setup(editor);
|
||||
return get(editor);
|
||||
});
|
||||
};
|
||||
|
||||
Plugin();
|
||||
|
||||
})();
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* TinyMCE version 6.2.0 (2022-09-08)
|
||||
*/
|
||||
!function(){"use strict";var e=tinymce.util.Tools.resolve("tinymce.PluginManager");const t=e=>t=>(e=>{const t=typeof e;return null===e?"null":"object"===t&&Array.isArray(e)?"array":"object"===t&&(s=r=e,(o=String).prototype.isPrototypeOf(s)||r.constructor?.name===o.name)?"string":t;var s,r,o})(t)===e,s=t("string"),r=t("object"),o=t("array"),n=("function",e=>"function"==typeof e);var c=tinymce.util.Tools.resolve("tinymce.dom.DOMUtils"),i=tinymce.util.Tools.resolve("tinymce.EditorManager"),l=tinymce.util.Tools.resolve("tinymce.Env"),a=tinymce.util.Tools.resolve("tinymce.util.Tools");const p=e=>t=>t.options.get(e),u=p("importcss_merge_classes"),m=p("importcss_exclusive"),f=p("importcss_selector_converter"),y=p("importcss_selector_filter"),h=p("importcss_groups"),d=p("importcss_append"),_=p("importcss_file_filter"),g=p("skin"),v=p("skin_url"),b=Array.prototype.push,x=/^\.(?:ephox|tiny-pageembed|mce)(?:[.-]+\w+)+$/,T=e=>s(e)?t=>-1!==t.indexOf(e):e instanceof RegExp?t=>e.test(t):e,S=(e,t)=>{let s={};const r=/^(?:([a-z0-9\-_]+))?(\.[a-z0-9_\-\.]+)$/i.exec(t);if(!r)return;const o=r[1],n=r[2].substr(1).split(".").join(" "),c=a.makeMap("a,img");return r[1]?(s={title:t},e.schema.getTextBlockElements()[o]?s.block=o:e.schema.getBlockElements()[o]||c[o.toLowerCase()]?s.selector=o:s.inline=o):r[2]&&(s={inline:"span",title:t.substr(1),classes:n}),u(e)?s.classes=n:s.attributes={class:n},s},k=(e,t)=>null===t||m(e),w=e=>{e.on("init",(()=>{const t=(()=>{const e=[],t=[],s={};return{addItemToGroup:(e,r)=>{s[e]?s[e].push(r):(t.push(e),s[e]=[r])},addItem:t=>{e.push(t)},toFormats:()=>{return(r=t,n=e=>{const t=s[e];return 0===t.length?[]:[{title:e,items:t}]},(e=>{const t=[];for(let s=0,r=e.length;s<r;++s){if(!o(e[s]))throw new Error("Arr.flatten item "+s+" was not an array, input: "+e);b.apply(t,e[s])}return t})(((e,t)=>{const s=e.length,r=new Array(s);for(let o=0;o<s;o++){const s=e[o];r[o]=t(s,o)}return r})(r,n))).concat(e);var r,n}}})(),r={},n=T(y(e)),p=(e=>a.map(e,(e=>a.extend({},e,{original:e,selectors:{},filter:T(e.filter)}))))(h(e)),u=(t,s)=>{if(((e,t,s,r)=>!(k(e,s)?t in r:t in s.selectors))(e,t,s,r)){((e,t,s,r)=>{k(e,s)?r[t]=!0:s.selectors[t]=!0})(e,t,s,r);const o=((e,t,s,r)=>{let o;const n=f(e);return o=r&&r.selector_converter?r.selector_converter:n||(()=>S(e,s)),o.call(t,s,r)})(e,e.plugins.importcss,t,s);if(o){const t=o.name||c.DOM.uniqueId();return e.formatter.register(t,o),{title:o.title,format:t}}}return null};a.each(((e,t,r)=>{const o=[],n={},c=(t,n)=>{let p,u=t.href;if(u=(e=>{const t=l.cacheSuffix;return s(e)&&(e=e.replace("?"+t,"").replace("&"+t,"")),e})(u),u&&(!r||r(u,n))&&!((e,t)=>{const s=g(e);if(s){const r=v(e),o=r?e.documentBaseURI.toAbsolute(r):i.baseURL+"/skins/ui/"+s,n=i.baseURL+"/skins/content/";return t===o+"/content"+(e.inline?".inline":"")+".min.css"||-1!==t.indexOf(n)}return!1})(e,u)){a.each(t.imports,(e=>{c(e,!0)}));try{p=t.cssRules||t.rules}catch(e){}a.each(p,(e=>{e.styleSheet?c(e.styleSheet,!0):e.selectorText&&a.each(e.selectorText.split(","),(e=>{o.push(a.trim(e))}))}))}};a.each(e.contentCSS,(e=>{n[e]=!0})),r||(r=(e,t)=>t||n[e]);try{a.each(t.styleSheets,(e=>{c(e)}))}catch(e){}return o})(e,e.getDoc(),T(_(e))),(e=>{if(!x.test(e)&&(!n||n(e))){const s=((e,t)=>a.grep(e,(e=>!e.filter||e.filter(t))))(p,e);if(s.length>0)a.each(s,(s=>{const r=u(e,s);r&&t.addItemToGroup(s.title,r)}));else{const s=u(e,null);s&&t.addItem(s)}}}));const m=t.toFormats();e.dispatch("addStyleModifications",{items:m,replace:!d(e)})}))};e.add("importcss",(e=>((e=>{const t=e.options.register,o=e=>s(e)||n(e)||r(e);t("importcss_merge_classes",{processor:"boolean",default:!0}),t("importcss_exclusive",{processor:"boolean",default:!0}),t("importcss_selector_converter",{processor:"function"}),t("importcss_selector_filter",{processor:o}),t("importcss_file_filter",{processor:o}),t("importcss_groups",{processor:"object[]"}),t("importcss_append",{processor:"boolean",default:!1})})(e),w(e),(e=>({convertSelectorToFormat:t=>S(e,t)}))(e))))}();
|
||||
@@ -0,0 +1,175 @@
|
||||
/**
|
||||
* TinyMCE version 6.2.0 (2022-09-08)
|
||||
*/
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
var global$1 = tinymce.util.Tools.resolve('tinymce.PluginManager');
|
||||
|
||||
const option = name => editor => editor.options.get(name);
|
||||
const register$2 = editor => {
|
||||
const registerOption = editor.options.register;
|
||||
registerOption('insertdatetime_dateformat', {
|
||||
processor: 'string',
|
||||
default: editor.translate('%Y-%m-%d')
|
||||
});
|
||||
registerOption('insertdatetime_timeformat', {
|
||||
processor: 'string',
|
||||
default: editor.translate('%H:%M:%S')
|
||||
});
|
||||
registerOption('insertdatetime_formats', {
|
||||
processor: 'string[]',
|
||||
default: [
|
||||
'%H:%M:%S',
|
||||
'%Y-%m-%d',
|
||||
'%I:%M:%S %p',
|
||||
'%D'
|
||||
]
|
||||
});
|
||||
registerOption('insertdatetime_element', {
|
||||
processor: 'boolean',
|
||||
default: false
|
||||
});
|
||||
};
|
||||
const getDateFormat = option('insertdatetime_dateformat');
|
||||
const getTimeFormat = option('insertdatetime_timeformat');
|
||||
const getFormats = option('insertdatetime_formats');
|
||||
const shouldInsertTimeElement = option('insertdatetime_element');
|
||||
const getDefaultDateTime = editor => {
|
||||
const formats = getFormats(editor);
|
||||
return formats.length > 0 ? formats[0] : getTimeFormat(editor);
|
||||
};
|
||||
|
||||
const daysShort = 'Sun Mon Tue Wed Thu Fri Sat Sun'.split(' ');
|
||||
const daysLong = 'Sunday Monday Tuesday Wednesday Thursday Friday Saturday Sunday'.split(' ');
|
||||
const monthsShort = 'Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec'.split(' ');
|
||||
const monthsLong = 'January February March April May June July August September October November December'.split(' ');
|
||||
const addZeros = (value, len) => {
|
||||
value = '' + value;
|
||||
if (value.length < len) {
|
||||
for (let i = 0; i < len - value.length; i++) {
|
||||
value = '0' + value;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
};
|
||||
const getDateTime = (editor, fmt, date = new Date()) => {
|
||||
fmt = fmt.replace('%D', '%m/%d/%Y');
|
||||
fmt = fmt.replace('%r', '%I:%M:%S %p');
|
||||
fmt = fmt.replace('%Y', '' + date.getFullYear());
|
||||
fmt = fmt.replace('%y', '' + date.getYear());
|
||||
fmt = fmt.replace('%m', addZeros(date.getMonth() + 1, 2));
|
||||
fmt = fmt.replace('%d', addZeros(date.getDate(), 2));
|
||||
fmt = fmt.replace('%H', '' + addZeros(date.getHours(), 2));
|
||||
fmt = fmt.replace('%M', '' + addZeros(date.getMinutes(), 2));
|
||||
fmt = fmt.replace('%S', '' + addZeros(date.getSeconds(), 2));
|
||||
fmt = fmt.replace('%I', '' + ((date.getHours() + 11) % 12 + 1));
|
||||
fmt = fmt.replace('%p', '' + (date.getHours() < 12 ? 'AM' : 'PM'));
|
||||
fmt = fmt.replace('%B', '' + editor.translate(monthsLong[date.getMonth()]));
|
||||
fmt = fmt.replace('%b', '' + editor.translate(monthsShort[date.getMonth()]));
|
||||
fmt = fmt.replace('%A', '' + editor.translate(daysLong[date.getDay()]));
|
||||
fmt = fmt.replace('%a', '' + editor.translate(daysShort[date.getDay()]));
|
||||
fmt = fmt.replace('%%', '%');
|
||||
return fmt;
|
||||
};
|
||||
const updateElement = (editor, timeElm, computerTime, userTime) => {
|
||||
const newTimeElm = editor.dom.create('time', { datetime: computerTime }, userTime);
|
||||
editor.dom.replace(newTimeElm, timeElm);
|
||||
editor.selection.select(newTimeElm, true);
|
||||
editor.selection.collapse(false);
|
||||
};
|
||||
const insertDateTime = (editor, format) => {
|
||||
if (shouldInsertTimeElement(editor)) {
|
||||
const userTime = getDateTime(editor, format);
|
||||
let computerTime;
|
||||
if (/%[HMSIp]/.test(format)) {
|
||||
computerTime = getDateTime(editor, '%Y-%m-%dT%H:%M');
|
||||
} else {
|
||||
computerTime = getDateTime(editor, '%Y-%m-%d');
|
||||
}
|
||||
const timeElm = editor.dom.getParent(editor.selection.getStart(), 'time');
|
||||
if (timeElm) {
|
||||
updateElement(editor, timeElm, computerTime, userTime);
|
||||
} else {
|
||||
editor.insertContent('<time datetime="' + computerTime + '">' + userTime + '</time>');
|
||||
}
|
||||
} else {
|
||||
editor.insertContent(getDateTime(editor, format));
|
||||
}
|
||||
};
|
||||
|
||||
const register$1 = editor => {
|
||||
editor.addCommand('mceInsertDate', (_ui, value) => {
|
||||
insertDateTime(editor, value ?? getDateFormat(editor));
|
||||
});
|
||||
editor.addCommand('mceInsertTime', (_ui, value) => {
|
||||
insertDateTime(editor, value ?? getTimeFormat(editor));
|
||||
});
|
||||
};
|
||||
|
||||
const Cell = initial => {
|
||||
let value = initial;
|
||||
const get = () => {
|
||||
return value;
|
||||
};
|
||||
const set = v => {
|
||||
value = v;
|
||||
};
|
||||
return {
|
||||
get,
|
||||
set
|
||||
};
|
||||
};
|
||||
|
||||
var global = tinymce.util.Tools.resolve('tinymce.util.Tools');
|
||||
|
||||
const register = editor => {
|
||||
const formats = getFormats(editor);
|
||||
const defaultFormat = Cell(getDefaultDateTime(editor));
|
||||
const insertDateTime = format => editor.execCommand('mceInsertDate', false, format);
|
||||
editor.ui.registry.addSplitButton('insertdatetime', {
|
||||
icon: 'insert-time',
|
||||
tooltip: 'Insert date/time',
|
||||
select: value => value === defaultFormat.get(),
|
||||
fetch: done => {
|
||||
done(global.map(formats, format => ({
|
||||
type: 'choiceitem',
|
||||
text: getDateTime(editor, format),
|
||||
value: format
|
||||
})));
|
||||
},
|
||||
onAction: _api => {
|
||||
insertDateTime(defaultFormat.get());
|
||||
},
|
||||
onItemAction: (_api, value) => {
|
||||
defaultFormat.set(value);
|
||||
insertDateTime(value);
|
||||
}
|
||||
});
|
||||
const makeMenuItemHandler = format => () => {
|
||||
defaultFormat.set(format);
|
||||
insertDateTime(format);
|
||||
};
|
||||
editor.ui.registry.addNestedMenuItem('insertdatetime', {
|
||||
icon: 'insert-time',
|
||||
text: 'Date/time',
|
||||
getSubmenuItems: () => global.map(formats, format => ({
|
||||
type: 'menuitem',
|
||||
text: getDateTime(editor, format),
|
||||
onAction: makeMenuItemHandler(format)
|
||||
}))
|
||||
});
|
||||
};
|
||||
|
||||
var Plugin = () => {
|
||||
global$1.add('insertdatetime', editor => {
|
||||
register$2(editor);
|
||||
register$1(editor);
|
||||
register(editor);
|
||||
});
|
||||
};
|
||||
|
||||
Plugin();
|
||||
|
||||
})();
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* TinyMCE version 6.2.0 (2022-09-08)
|
||||
*/
|
||||
!function(){"use strict";var e=tinymce.util.Tools.resolve("tinymce.PluginManager");const t=e=>t=>t.options.get(e),a=t("insertdatetime_dateformat"),r=t("insertdatetime_timeformat"),n=t("insertdatetime_formats"),s=t("insertdatetime_element"),i="Sun Mon Tue Wed Thu Fri Sat Sun".split(" "),o="Sunday Monday Tuesday Wednesday Thursday Friday Saturday Sunday".split(" "),m="Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" "),l="January February March April May June July August September October November December".split(" "),c=(e,t)=>{if((e=""+e).length<t)for(let a=0;a<t-e.length;a++)e="0"+e;return e},d=(e,t,a=new Date)=>(t=(t=(t=(t=(t=(t=(t=(t=(t=(t=(t=(t=(t=(t=(t=t.replace("%D","%m/%d/%Y")).replace("%r","%I:%M:%S %p")).replace("%Y",""+a.getFullYear())).replace("%y",""+a.getYear())).replace("%m",c(a.getMonth()+1,2))).replace("%d",c(a.getDate(),2))).replace("%H",""+c(a.getHours(),2))).replace("%M",""+c(a.getMinutes(),2))).replace("%S",""+c(a.getSeconds(),2))).replace("%I",""+((a.getHours()+11)%12+1))).replace("%p",a.getHours()<12?"AM":"PM")).replace("%B",""+e.translate(l[a.getMonth()]))).replace("%b",""+e.translate(m[a.getMonth()]))).replace("%A",""+e.translate(o[a.getDay()]))).replace("%a",""+e.translate(i[a.getDay()]))).replace("%%","%"),u=(e,t)=>{if(s(e)){const a=d(e,t);let r;r=/%[HMSIp]/.test(t)?d(e,"%Y-%m-%dT%H:%M"):d(e,"%Y-%m-%d");const n=e.dom.getParent(e.selection.getStart(),"time");n?((e,t,a,r)=>{const n=e.dom.create("time",{datetime:a},r);e.dom.replace(n,t),e.selection.select(n,!0),e.selection.collapse(!1)})(e,n,r,a):e.insertContent('<time datetime="'+r+'">'+a+"</time>")}else e.insertContent(d(e,t))};var p=tinymce.util.Tools.resolve("tinymce.util.Tools");e.add("insertdatetime",(e=>{(e=>{const t=e.options.register;t("insertdatetime_dateformat",{processor:"string",default:e.translate("%Y-%m-%d")}),t("insertdatetime_timeformat",{processor:"string",default:e.translate("%H:%M:%S")}),t("insertdatetime_formats",{processor:"string[]",default:["%H:%M:%S","%Y-%m-%d","%I:%M:%S %p","%D"]}),t("insertdatetime_element",{processor:"boolean",default:!1})})(e),(e=>{e.addCommand("mceInsertDate",((t,r)=>{u(e,r??a(e))})),e.addCommand("mceInsertTime",((t,a)=>{u(e,a??r(e))}))})(e),(e=>{const t=n(e),a=(e=>{let t=e;return{get:()=>t,set:e=>{t=e}}})((e=>{const t=n(e);return t.length>0?t[0]:r(e)})(e)),s=t=>e.execCommand("mceInsertDate",!1,t);e.ui.registry.addSplitButton("insertdatetime",{icon:"insert-time",tooltip:"Insert date/time",select:e=>e===a.get(),fetch:a=>{a(p.map(t,(t=>({type:"choiceitem",text:d(e,t),value:t}))))},onAction:e=>{s(a.get())},onItemAction:(e,t)=>{a.set(t),s(t)}});const i=e=>()=>{a.set(e),s(e)};e.ui.registry.addNestedMenuItem("insertdatetime",{icon:"insert-time",text:"Date/time",getSubmenuItems:()=>p.map(t,(t=>({type:"menuitem",text:d(e,t),onAction:i(t)})))})})(e)}))}();
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -0,0 +1,111 @@
|
||||
/**
|
||||
* TinyMCE version 6.2.0 (2022-09-08)
|
||||
*/
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
var global$1 = tinymce.util.Tools.resolve('tinymce.PluginManager');
|
||||
|
||||
const isSimpleType = type => value => typeof value === type;
|
||||
const isBoolean = isSimpleType('boolean');
|
||||
const isNumber = isSimpleType('number');
|
||||
|
||||
const option = name => editor => editor.options.get(name);
|
||||
const register$2 = editor => {
|
||||
const registerOption = editor.options.register;
|
||||
registerOption('nonbreaking_force_tab', {
|
||||
processor: value => {
|
||||
if (isBoolean(value)) {
|
||||
return {
|
||||
value: value ? 3 : 0,
|
||||
valid: true
|
||||
};
|
||||
} else if (isNumber(value)) {
|
||||
return {
|
||||
value,
|
||||
valid: true
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
valid: false,
|
||||
message: 'Must be a boolean or number.'
|
||||
};
|
||||
}
|
||||
},
|
||||
default: false
|
||||
});
|
||||
registerOption('nonbreaking_wrap', {
|
||||
processor: 'boolean',
|
||||
default: true
|
||||
});
|
||||
};
|
||||
const getKeyboardSpaces = option('nonbreaking_force_tab');
|
||||
const wrapNbsps = option('nonbreaking_wrap');
|
||||
|
||||
const stringRepeat = (string, repeats) => {
|
||||
let str = '';
|
||||
for (let index = 0; index < repeats; index++) {
|
||||
str += string;
|
||||
}
|
||||
return str;
|
||||
};
|
||||
const isVisualCharsEnabled = editor => editor.plugins.visualchars ? editor.plugins.visualchars.isEnabled() : false;
|
||||
const insertNbsp = (editor, times) => {
|
||||
const classes = () => isVisualCharsEnabled(editor) ? 'mce-nbsp-wrap mce-nbsp' : 'mce-nbsp-wrap';
|
||||
const nbspSpan = () => `<span class="${ classes() }" contenteditable="false">${ stringRepeat(' ', times) }</span>`;
|
||||
const shouldWrap = wrapNbsps(editor);
|
||||
const html = shouldWrap || editor.plugins.visualchars ? nbspSpan() : stringRepeat(' ', times);
|
||||
editor.undoManager.transact(() => editor.insertContent(html));
|
||||
};
|
||||
|
||||
const register$1 = editor => {
|
||||
editor.addCommand('mceNonBreaking', () => {
|
||||
insertNbsp(editor, 1);
|
||||
});
|
||||
};
|
||||
|
||||
var global = tinymce.util.Tools.resolve('tinymce.util.VK');
|
||||
|
||||
const setup = editor => {
|
||||
const spaces = getKeyboardSpaces(editor);
|
||||
if (spaces > 0) {
|
||||
editor.on('keydown', e => {
|
||||
if (e.keyCode === global.TAB && !e.isDefaultPrevented()) {
|
||||
if (e.shiftKey) {
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
e.stopImmediatePropagation();
|
||||
insertNbsp(editor, spaces);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const register = editor => {
|
||||
const onAction = () => editor.execCommand('mceNonBreaking');
|
||||
editor.ui.registry.addButton('nonbreaking', {
|
||||
icon: 'non-breaking',
|
||||
tooltip: 'Nonbreaking space',
|
||||
onAction
|
||||
});
|
||||
editor.ui.registry.addMenuItem('nonbreaking', {
|
||||
icon: 'non-breaking',
|
||||
text: 'Nonbreaking space',
|
||||
onAction
|
||||
});
|
||||
};
|
||||
|
||||
var Plugin = () => {
|
||||
global$1.add('nonbreaking', editor => {
|
||||
register$2(editor);
|
||||
register$1(editor);
|
||||
register(editor);
|
||||
setup(editor);
|
||||
});
|
||||
};
|
||||
|
||||
Plugin();
|
||||
|
||||
})();
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* TinyMCE version 6.2.0 (2022-09-08)
|
||||
*/
|
||||
!function(){"use strict";var n=tinymce.util.Tools.resolve("tinymce.PluginManager");const e=n=>e=>typeof e===n,a=e("boolean"),o=e("number"),t=n=>e=>e.options.get(n),i=t("nonbreaking_force_tab"),r=t("nonbreaking_wrap"),s=(n,e)=>{let a="";for(let o=0;o<e;o++)a+=n;return a},c=(n,e)=>{const a=r(n)||n.plugins.visualchars?`<span class="${(n=>!!n.plugins.visualchars&&n.plugins.visualchars.isEnabled())(n)?"mce-nbsp-wrap mce-nbsp":"mce-nbsp-wrap"}" contenteditable="false">${s(" ",e)}</span>`:s(" ",e);n.undoManager.transact((()=>n.insertContent(a)))};var l=tinymce.util.Tools.resolve("tinymce.util.VK");n.add("nonbreaking",(n=>{(n=>{const e=n.options.register;e("nonbreaking_force_tab",{processor:n=>a(n)?{value:n?3:0,valid:!0}:o(n)?{value:n,valid:!0}:{valid:!1,message:"Must be a boolean or number."},default:!1}),e("nonbreaking_wrap",{processor:"boolean",default:!0})})(n),(n=>{n.addCommand("mceNonBreaking",(()=>{c(n,1)}))})(n),(n=>{const e=()=>n.execCommand("mceNonBreaking");n.ui.registry.addButton("nonbreaking",{icon:"non-breaking",tooltip:"Nonbreaking space",onAction:e}),n.ui.registry.addMenuItem("nonbreaking",{icon:"non-breaking",text:"Nonbreaking space",onAction:e})})(n),(n=>{const e=i(n);e>0&&n.on("keydown",(a=>{if(a.keyCode===l.TAB&&!a.isDefaultPrevented()){if(a.shiftKey)return;a.preventDefault(),a.stopImmediatePropagation(),c(n,e)}}))})(n)}))}();
|
||||
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
* TinyMCE version 6.2.0 (2022-09-08)
|
||||
*/
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
var global$1 = tinymce.util.Tools.resolve('tinymce.PluginManager');
|
||||
|
||||
var global = tinymce.util.Tools.resolve('tinymce.Env');
|
||||
|
||||
const option = name => editor => editor.options.get(name);
|
||||
const register$2 = editor => {
|
||||
const registerOption = editor.options.register;
|
||||
registerOption('pagebreak_separator', {
|
||||
processor: 'string',
|
||||
default: '<!-- pagebreak -->'
|
||||
});
|
||||
registerOption('pagebreak_split_block', {
|
||||
processor: 'boolean',
|
||||
default: false
|
||||
});
|
||||
};
|
||||
const getSeparatorHtml = option('pagebreak_separator');
|
||||
const shouldSplitBlock = option('pagebreak_split_block');
|
||||
|
||||
const pageBreakClass = 'mce-pagebreak';
|
||||
const getPlaceholderHtml = shouldSplitBlock => {
|
||||
const html = `<img src="${ global.transparentSrc }" class="${ pageBreakClass }" data-mce-resize="false" data-mce-placeholder />`;
|
||||
return shouldSplitBlock ? `<p>${ html }</p>` : html;
|
||||
};
|
||||
const setup$1 = editor => {
|
||||
const separatorHtml = getSeparatorHtml(editor);
|
||||
const shouldSplitBlock$1 = () => shouldSplitBlock(editor);
|
||||
const pageBreakSeparatorRegExp = new RegExp(separatorHtml.replace(/[\?\.\*\[\]\(\)\{\}\+\^\$\:]/g, a => {
|
||||
return '\\' + a;
|
||||
}), 'gi');
|
||||
editor.on('BeforeSetContent', e => {
|
||||
e.content = e.content.replace(pageBreakSeparatorRegExp, getPlaceholderHtml(shouldSplitBlock$1()));
|
||||
});
|
||||
editor.on('PreInit', () => {
|
||||
editor.serializer.addNodeFilter('img', nodes => {
|
||||
let i = nodes.length, node, className;
|
||||
while (i--) {
|
||||
node = nodes[i];
|
||||
className = node.attr('class');
|
||||
if (className && className.indexOf(pageBreakClass) !== -1) {
|
||||
const parentNode = node.parent;
|
||||
if (parentNode && editor.schema.getBlockElements()[parentNode.name] && shouldSplitBlock$1()) {
|
||||
parentNode.type = 3;
|
||||
parentNode.value = separatorHtml;
|
||||
parentNode.raw = true;
|
||||
node.remove();
|
||||
continue;
|
||||
}
|
||||
node.type = 3;
|
||||
node.value = separatorHtml;
|
||||
node.raw = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const register$1 = editor => {
|
||||
editor.addCommand('mcePageBreak', () => {
|
||||
editor.insertContent(getPlaceholderHtml(shouldSplitBlock(editor)));
|
||||
});
|
||||
};
|
||||
|
||||
const setup = editor => {
|
||||
editor.on('ResolveName', e => {
|
||||
if (e.target.nodeName === 'IMG' && editor.dom.hasClass(e.target, pageBreakClass)) {
|
||||
e.name = 'pagebreak';
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const register = editor => {
|
||||
const onAction = () => editor.execCommand('mcePageBreak');
|
||||
editor.ui.registry.addButton('pagebreak', {
|
||||
icon: 'page-break',
|
||||
tooltip: 'Page break',
|
||||
onAction
|
||||
});
|
||||
editor.ui.registry.addMenuItem('pagebreak', {
|
||||
text: 'Page break',
|
||||
icon: 'page-break',
|
||||
onAction
|
||||
});
|
||||
};
|
||||
|
||||
var Plugin = () => {
|
||||
global$1.add('pagebreak', editor => {
|
||||
register$2(editor);
|
||||
register$1(editor);
|
||||
register(editor);
|
||||
setup$1(editor);
|
||||
setup(editor);
|
||||
});
|
||||
};
|
||||
|
||||
Plugin();
|
||||
|
||||
})();
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* TinyMCE version 6.2.0 (2022-09-08)
|
||||
*/
|
||||
!function(){"use strict";var e=tinymce.util.Tools.resolve("tinymce.PluginManager"),a=tinymce.util.Tools.resolve("tinymce.Env");const t=e=>a=>a.options.get(e),r=t("pagebreak_separator"),n=t("pagebreak_split_block"),o="mce-pagebreak",s=e=>{const t=`<img src="${a.transparentSrc}" class="mce-pagebreak" data-mce-resize="false" data-mce-placeholder />`;return e?`<p>${t}</p>`:t};e.add("pagebreak",(e=>{(e=>{const a=e.options.register;a("pagebreak_separator",{processor:"string",default:"\x3c!-- pagebreak --\x3e"}),a("pagebreak_split_block",{processor:"boolean",default:!1})})(e),(e=>{e.addCommand("mcePageBreak",(()=>{e.insertContent(s(n(e)))}))})(e),(e=>{const a=()=>e.execCommand("mcePageBreak");e.ui.registry.addButton("pagebreak",{icon:"page-break",tooltip:"Page break",onAction:a}),e.ui.registry.addMenuItem("pagebreak",{text:"Page break",icon:"page-break",onAction:a})})(e),(e=>{const a=r(e),t=()=>n(e),c=new RegExp(a.replace(/[\?\.\*\[\]\(\)\{\}\+\^\$\:]/g,(e=>"\\"+e)),"gi");e.on("BeforeSetContent",(e=>{e.content=e.content.replace(c,s(t()))})),e.on("PreInit",(()=>{e.serializer.addNodeFilter("img",(r=>{let n,s,c=r.length;for(;c--;)if(n=r[c],s=n.attr("class"),s&&-1!==s.indexOf(o)){const r=n.parent;if(r&&e.schema.getBlockElements()[r.name]&&t()){r.type=3,r.value=a,r.raw=!0,n.remove();continue}n.type=3,n.value=a,n.raw=!0}}))}))})(e),(e=>{e.on("ResolveName",(a=>{"IMG"===a.target.nodeName&&e.dom.hasClass(a.target,o)&&(a.name="pagebreak")}))})(e)}))}();
|
||||
@@ -0,0 +1,96 @@
|
||||
/**
|
||||
* TinyMCE version 6.2.0 (2022-09-08)
|
||||
*/
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
var global$2 = tinymce.util.Tools.resolve('tinymce.PluginManager');
|
||||
|
||||
var global$1 = tinymce.util.Tools.resolve('tinymce.Env');
|
||||
|
||||
var global = tinymce.util.Tools.resolve('tinymce.util.Tools');
|
||||
|
||||
const option = name => editor => editor.options.get(name);
|
||||
const getContentStyle = option('content_style');
|
||||
const shouldUseContentCssCors = option('content_css_cors');
|
||||
const getBodyClass = option('body_class');
|
||||
const getBodyId = option('body_id');
|
||||
|
||||
const getPreviewHtml = editor => {
|
||||
let headHtml = '';
|
||||
const encode = editor.dom.encode;
|
||||
const contentStyle = getContentStyle(editor) ?? '';
|
||||
headHtml += '<base href="' + encode(editor.documentBaseURI.getURI()) + '">';
|
||||
const cors = shouldUseContentCssCors(editor) ? ' crossorigin="anonymous"' : '';
|
||||
global.each(editor.contentCSS, url => {
|
||||
headHtml += '<link type="text/css" rel="stylesheet" href="' + encode(editor.documentBaseURI.toAbsolute(url)) + '"' + cors + '>';
|
||||
});
|
||||
if (contentStyle) {
|
||||
headHtml += '<style type="text/css">' + contentStyle + '</style>';
|
||||
}
|
||||
const bodyId = getBodyId(editor);
|
||||
const bodyClass = getBodyClass(editor);
|
||||
const isMetaKeyPressed = global$1.os.isMacOS() || global$1.os.isiOS() ? 'e.metaKey' : 'e.ctrlKey && !e.altKey';
|
||||
const preventClicksOnLinksScript = '<script>' + 'document.addEventListener && document.addEventListener("click", function(e) {' + 'for (var elm = e.target; elm; elm = elm.parentNode) {' + 'if (elm.nodeName === "A" && !(' + isMetaKeyPressed + ')) {' + 'e.preventDefault();' + '}' + '}' + '}, false);' + '</script> ';
|
||||
const directionality = editor.getBody().dir;
|
||||
const dirAttr = directionality ? ' dir="' + encode(directionality) + '"' : '';
|
||||
const previewHtml = '<!DOCTYPE html>' + '<html>' + '<head>' + headHtml + '</head>' + '<body id="' + encode(bodyId) + '" class="mce-content-body ' + encode(bodyClass) + '"' + dirAttr + '>' + editor.getContent() + preventClicksOnLinksScript + '</body>' + '</html>';
|
||||
return previewHtml;
|
||||
};
|
||||
|
||||
const open = editor => {
|
||||
const content = getPreviewHtml(editor);
|
||||
const dataApi = editor.windowManager.open({
|
||||
title: 'Preview',
|
||||
size: 'large',
|
||||
body: {
|
||||
type: 'panel',
|
||||
items: [{
|
||||
name: 'preview',
|
||||
type: 'iframe',
|
||||
sandboxed: true,
|
||||
transparent: false
|
||||
}]
|
||||
},
|
||||
buttons: [{
|
||||
type: 'cancel',
|
||||
name: 'close',
|
||||
text: 'Close',
|
||||
primary: true
|
||||
}],
|
||||
initialData: { preview: content }
|
||||
});
|
||||
dataApi.focus('close');
|
||||
};
|
||||
|
||||
const register$1 = editor => {
|
||||
editor.addCommand('mcePreview', () => {
|
||||
open(editor);
|
||||
});
|
||||
};
|
||||
|
||||
const register = editor => {
|
||||
const onAction = () => editor.execCommand('mcePreview');
|
||||
editor.ui.registry.addButton('preview', {
|
||||
icon: 'preview',
|
||||
tooltip: 'Preview',
|
||||
onAction
|
||||
});
|
||||
editor.ui.registry.addMenuItem('preview', {
|
||||
icon: 'preview',
|
||||
text: 'Preview',
|
||||
onAction
|
||||
});
|
||||
};
|
||||
|
||||
var Plugin = () => {
|
||||
global$2.add('preview', editor => {
|
||||
register$1(editor);
|
||||
register(editor);
|
||||
});
|
||||
};
|
||||
|
||||
Plugin();
|
||||
|
||||
})();
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* TinyMCE version 6.2.0 (2022-09-08)
|
||||
*/
|
||||
!function(){"use strict";var e=tinymce.util.Tools.resolve("tinymce.PluginManager"),t=tinymce.util.Tools.resolve("tinymce.Env"),o=tinymce.util.Tools.resolve("tinymce.util.Tools");const n=e=>t=>t.options.get(e),i=n("content_style"),s=n("content_css_cors"),c=n("body_class"),r=n("body_id");e.add("preview",(e=>{(e=>{e.addCommand("mcePreview",(()=>{(e=>{const n=(e=>{let n="";const a=e.dom.encode,l=i(e)??"";n+='<base href="'+a(e.documentBaseURI.getURI())+'">';const d=s(e)?' crossorigin="anonymous"':"";o.each(e.contentCSS,(t=>{n+='<link type="text/css" rel="stylesheet" href="'+a(e.documentBaseURI.toAbsolute(t))+'"'+d+">"})),l&&(n+='<style type="text/css">'+l+"</style>");const m=r(e),y=c(e),p='<script>document.addEventListener && document.addEventListener("click", function(e) {for (var elm = e.target; elm; elm = elm.parentNode) {if (elm.nodeName === "A" && !('+(t.os.isMacOS()||t.os.isiOS()?"e.metaKey":"e.ctrlKey && !e.altKey")+")) {e.preventDefault();}}}, false);<\/script> ",u=e.getBody().dir,v=u?' dir="'+a(u)+'"':"";return"<!DOCTYPE html><html><head>"+n+'</head><body id="'+a(m)+'" class="mce-content-body '+a(y)+'"'+v+">"+e.getContent()+p+"</body></html>"})(e);e.windowManager.open({title:"Preview",size:"large",body:{type:"panel",items:[{name:"preview",type:"iframe",sandboxed:!0,transparent:!1}]},buttons:[{type:"cancel",name:"close",text:"Close",primary:!0}],initialData:{preview:n}}).focus("close")})(e)}))})(e),(e=>{const t=()=>e.execCommand("mcePreview");e.ui.registry.addButton("preview",{icon:"preview",tooltip:"Preview",onAction:t}),e.ui.registry.addMenuItem("preview",{icon:"preview",text:"Preview",onAction:t})})(e)}))}();
|
||||
@@ -0,0 +1,420 @@
|
||||
/**
|
||||
* TinyMCE version 6.2.0 (2022-09-08)
|
||||
*/
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
var global$2 = tinymce.util.Tools.resolve('tinymce.PluginManager');
|
||||
|
||||
const hasProto = (v, constructor, predicate) => {
|
||||
if (predicate(v, constructor.prototype)) {
|
||||
return true;
|
||||
} else {
|
||||
return v.constructor?.name === constructor.name;
|
||||
}
|
||||
};
|
||||
const typeOf = x => {
|
||||
const t = typeof x;
|
||||
if (x === null) {
|
||||
return 'null';
|
||||
} else if (t === 'object' && Array.isArray(x)) {
|
||||
return 'array';
|
||||
} else if (t === 'object' && hasProto(x, String, (o, proto) => proto.isPrototypeOf(o))) {
|
||||
return 'string';
|
||||
} else {
|
||||
return t;
|
||||
}
|
||||
};
|
||||
const isType = type => value => typeOf(value) === type;
|
||||
const isSimpleType = type => value => typeof value === type;
|
||||
const isString = isType('string');
|
||||
const isBoolean = isSimpleType('boolean');
|
||||
const isNullable = a => a === null || a === undefined;
|
||||
const isNonNullable = a => !isNullable(a);
|
||||
const isFunction = isSimpleType('function');
|
||||
|
||||
const option = name => editor => editor.options.get(name);
|
||||
const register = editor => {
|
||||
const registerOption = editor.options.register;
|
||||
const toolbarProcessor = defaultValue => value => {
|
||||
const valid = isBoolean(value) || isString(value);
|
||||
if (valid) {
|
||||
if (isBoolean(value)) {
|
||||
return {
|
||||
value: value ? defaultValue : '',
|
||||
valid
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
value: value.trim(),
|
||||
valid
|
||||
};
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
valid: false,
|
||||
message: 'Must be a boolean or string.'
|
||||
};
|
||||
}
|
||||
};
|
||||
const defaultSelectionToolbar = 'bold italic | quicklink h2 h3 blockquote';
|
||||
registerOption('quickbars_selection_toolbar', {
|
||||
processor: toolbarProcessor(defaultSelectionToolbar),
|
||||
default: defaultSelectionToolbar
|
||||
});
|
||||
const defaultInsertToolbar = 'quickimage quicktable';
|
||||
registerOption('quickbars_insert_toolbar', {
|
||||
processor: toolbarProcessor(defaultInsertToolbar),
|
||||
default: defaultInsertToolbar
|
||||
});
|
||||
const defaultImageToolbar = 'alignleft aligncenter alignright';
|
||||
registerOption('quickbars_image_toolbar', {
|
||||
processor: toolbarProcessor(defaultImageToolbar),
|
||||
default: defaultImageToolbar
|
||||
});
|
||||
};
|
||||
const getTextSelectionToolbarItems = option('quickbars_selection_toolbar');
|
||||
const getInsertToolbarItems = option('quickbars_insert_toolbar');
|
||||
const getImageToolbarItems = option('quickbars_image_toolbar');
|
||||
|
||||
let unique = 0;
|
||||
const generate = prefix => {
|
||||
const date = new Date();
|
||||
const time = date.getTime();
|
||||
const random = Math.floor(Math.random() * 1000000000);
|
||||
unique++;
|
||||
return prefix + '_' + random + unique + String(time);
|
||||
};
|
||||
|
||||
const insertTable = (editor, columns, rows) => {
|
||||
editor.execCommand('mceInsertTable', false, {
|
||||
rows,
|
||||
columns
|
||||
});
|
||||
};
|
||||
const insertBlob = (editor, base64, blob) => {
|
||||
const blobCache = editor.editorUpload.blobCache;
|
||||
const blobInfo = blobCache.create(generate('mceu'), blob, base64);
|
||||
blobCache.add(blobInfo);
|
||||
editor.insertContent(editor.dom.createHTML('img', { src: blobInfo.blobUri() }));
|
||||
};
|
||||
|
||||
const blobToBase64 = blob => {
|
||||
return new Promise(resolve => {
|
||||
const reader = new FileReader();
|
||||
reader.onloadend = () => {
|
||||
resolve(reader.result.split(',')[1]);
|
||||
};
|
||||
reader.readAsDataURL(blob);
|
||||
});
|
||||
};
|
||||
|
||||
var global$1 = tinymce.util.Tools.resolve('tinymce.Env');
|
||||
|
||||
var global = tinymce.util.Tools.resolve('tinymce.util.Delay');
|
||||
|
||||
const pickFile = editor => new Promise(resolve => {
|
||||
const fileInput = document.createElement('input');
|
||||
fileInput.type = 'file';
|
||||
fileInput.accept = 'image/*';
|
||||
fileInput.style.position = 'fixed';
|
||||
fileInput.style.left = '0';
|
||||
fileInput.style.top = '0';
|
||||
fileInput.style.opacity = '0.001';
|
||||
document.body.appendChild(fileInput);
|
||||
const changeHandler = e => {
|
||||
resolve(Array.prototype.slice.call(e.target.files));
|
||||
};
|
||||
fileInput.addEventListener('change', changeHandler);
|
||||
const cancelHandler = e => {
|
||||
const cleanup = () => {
|
||||
resolve([]);
|
||||
fileInput.parentNode?.removeChild(fileInput);
|
||||
};
|
||||
if (global$1.os.isAndroid() && e.type !== 'remove') {
|
||||
global.setEditorTimeout(editor, cleanup, 0);
|
||||
} else {
|
||||
cleanup();
|
||||
}
|
||||
editor.off('focusin remove', cancelHandler);
|
||||
};
|
||||
editor.on('focusin remove', cancelHandler);
|
||||
fileInput.click();
|
||||
});
|
||||
|
||||
const setupButtons = editor => {
|
||||
editor.ui.registry.addButton('quickimage', {
|
||||
icon: 'image',
|
||||
tooltip: 'Insert image',
|
||||
onAction: () => {
|
||||
pickFile(editor).then(files => {
|
||||
if (files.length > 0) {
|
||||
const blob = files[0];
|
||||
blobToBase64(blob).then(base64 => {
|
||||
insertBlob(editor, base64, blob);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
editor.ui.registry.addButton('quicktable', {
|
||||
icon: 'table',
|
||||
tooltip: 'Insert table',
|
||||
onAction: () => {
|
||||
insertTable(editor, 2, 2);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const constant = value => {
|
||||
return () => {
|
||||
return value;
|
||||
};
|
||||
};
|
||||
const never = constant(false);
|
||||
|
||||
class Optional {
|
||||
constructor(tag, value) {
|
||||
this.tag = tag;
|
||||
this.value = value;
|
||||
}
|
||||
static some(value) {
|
||||
return new Optional(true, value);
|
||||
}
|
||||
static none() {
|
||||
return Optional.singletonNone;
|
||||
}
|
||||
fold(onNone, onSome) {
|
||||
if (this.tag) {
|
||||
return onSome(this.value);
|
||||
} else {
|
||||
return onNone();
|
||||
}
|
||||
}
|
||||
isSome() {
|
||||
return this.tag;
|
||||
}
|
||||
isNone() {
|
||||
return !this.tag;
|
||||
}
|
||||
map(mapper) {
|
||||
if (this.tag) {
|
||||
return Optional.some(mapper(this.value));
|
||||
} else {
|
||||
return Optional.none();
|
||||
}
|
||||
}
|
||||
bind(binder) {
|
||||
if (this.tag) {
|
||||
return binder(this.value);
|
||||
} else {
|
||||
return Optional.none();
|
||||
}
|
||||
}
|
||||
exists(predicate) {
|
||||
return this.tag && predicate(this.value);
|
||||
}
|
||||
forall(predicate) {
|
||||
return !this.tag || predicate(this.value);
|
||||
}
|
||||
filter(predicate) {
|
||||
if (!this.tag || predicate(this.value)) {
|
||||
return this;
|
||||
} else {
|
||||
return Optional.none();
|
||||
}
|
||||
}
|
||||
getOr(replacement) {
|
||||
return this.tag ? this.value : replacement;
|
||||
}
|
||||
or(replacement) {
|
||||
return this.tag ? this : replacement;
|
||||
}
|
||||
getOrThunk(thunk) {
|
||||
return this.tag ? this.value : thunk();
|
||||
}
|
||||
orThunk(thunk) {
|
||||
return this.tag ? this : thunk();
|
||||
}
|
||||
getOrDie(message) {
|
||||
if (!this.tag) {
|
||||
throw new Error(message ?? 'Called getOrDie on None');
|
||||
} else {
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
static from(value) {
|
||||
return isNonNullable(value) ? Optional.some(value) : Optional.none();
|
||||
}
|
||||
getOrNull() {
|
||||
return this.tag ? this.value : null;
|
||||
}
|
||||
getOrUndefined() {
|
||||
return this.value;
|
||||
}
|
||||
each(worker) {
|
||||
if (this.tag) {
|
||||
worker(this.value);
|
||||
}
|
||||
}
|
||||
toArray() {
|
||||
return this.tag ? [this.value] : [];
|
||||
}
|
||||
toString() {
|
||||
return this.tag ? `some(${ this.value })` : 'none()';
|
||||
}
|
||||
}
|
||||
Optional.singletonNone = new Optional(false);
|
||||
|
||||
var ClosestOrAncestor = (is, ancestor, scope, a, isRoot) => {
|
||||
if (is(scope, a)) {
|
||||
return Optional.some(scope);
|
||||
} else if (isFunction(isRoot) && isRoot(scope)) {
|
||||
return Optional.none();
|
||||
} else {
|
||||
return ancestor(scope, a, isRoot);
|
||||
}
|
||||
};
|
||||
|
||||
const ELEMENT = 1;
|
||||
|
||||
const fromHtml = (html, scope) => {
|
||||
const doc = scope || document;
|
||||
const div = doc.createElement('div');
|
||||
div.innerHTML = html;
|
||||
if (!div.hasChildNodes() || div.childNodes.length > 1) {
|
||||
const message = 'HTML does not have a single root node';
|
||||
console.error(message, html);
|
||||
throw new Error(message);
|
||||
}
|
||||
return fromDom(div.childNodes[0]);
|
||||
};
|
||||
const fromTag = (tag, scope) => {
|
||||
const doc = scope || document;
|
||||
const node = doc.createElement(tag);
|
||||
return fromDom(node);
|
||||
};
|
||||
const fromText = (text, scope) => {
|
||||
const doc = scope || document;
|
||||
const node = doc.createTextNode(text);
|
||||
return fromDom(node);
|
||||
};
|
||||
const fromDom = node => {
|
||||
if (node === null || node === undefined) {
|
||||
throw new Error('Node cannot be null or undefined');
|
||||
}
|
||||
return { dom: node };
|
||||
};
|
||||
const fromPoint = (docElm, x, y) => Optional.from(docElm.dom.elementFromPoint(x, y)).map(fromDom);
|
||||
const SugarElement = {
|
||||
fromHtml,
|
||||
fromTag,
|
||||
fromText,
|
||||
fromDom,
|
||||
fromPoint
|
||||
};
|
||||
|
||||
const is = (element, selector) => {
|
||||
const dom = element.dom;
|
||||
if (dom.nodeType !== ELEMENT) {
|
||||
return false;
|
||||
} else {
|
||||
const elem = dom;
|
||||
if (elem.matches !== undefined) {
|
||||
return elem.matches(selector);
|
||||
} else if (elem.msMatchesSelector !== undefined) {
|
||||
return elem.msMatchesSelector(selector);
|
||||
} else if (elem.webkitMatchesSelector !== undefined) {
|
||||
return elem.webkitMatchesSelector(selector);
|
||||
} else if (elem.mozMatchesSelector !== undefined) {
|
||||
return elem.mozMatchesSelector(selector);
|
||||
} else {
|
||||
throw new Error('Browser lacks native selectors');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
typeof window !== 'undefined' ? window : Function('return this;')();
|
||||
|
||||
const name = element => {
|
||||
const r = element.dom.nodeName;
|
||||
return r.toLowerCase();
|
||||
};
|
||||
|
||||
const ancestor$1 = (scope, predicate, isRoot) => {
|
||||
let element = scope.dom;
|
||||
const stop = isFunction(isRoot) ? isRoot : never;
|
||||
while (element.parentNode) {
|
||||
element = element.parentNode;
|
||||
const el = SugarElement.fromDom(element);
|
||||
if (predicate(el)) {
|
||||
return Optional.some(el);
|
||||
} else if (stop(el)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Optional.none();
|
||||
};
|
||||
const closest$1 = (scope, predicate, isRoot) => {
|
||||
const is = (s, test) => test(s);
|
||||
return ClosestOrAncestor(is, ancestor$1, scope, predicate, isRoot);
|
||||
};
|
||||
|
||||
const ancestor = (scope, selector, isRoot) => ancestor$1(scope, e => is(e, selector), isRoot);
|
||||
const closest = (scope, selector, isRoot) => {
|
||||
const is$1 = (element, selector) => is(element, selector);
|
||||
return ClosestOrAncestor(is$1, ancestor, scope, selector, isRoot);
|
||||
};
|
||||
|
||||
const addToEditor$1 = editor => {
|
||||
const insertToolbarItems = getInsertToolbarItems(editor);
|
||||
if (insertToolbarItems.length > 0) {
|
||||
editor.ui.registry.addContextToolbar('quickblock', {
|
||||
predicate: node => {
|
||||
const sugarNode = SugarElement.fromDom(node);
|
||||
const textBlockElementsMap = editor.schema.getTextBlockElements();
|
||||
const isRoot = elem => elem.dom === editor.getBody();
|
||||
return closest(sugarNode, 'table', isRoot).fold(() => closest$1(sugarNode, elem => name(elem) in textBlockElementsMap && editor.dom.isEmpty(elem.dom), isRoot).isSome(), never);
|
||||
},
|
||||
items: insertToolbarItems,
|
||||
position: 'line',
|
||||
scope: 'editor'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const addToEditor = editor => {
|
||||
const isEditable = node => editor.dom.getContentEditableParent(node) !== 'false';
|
||||
const isImage = node => node.nodeName === 'IMG' || node.nodeName === 'FIGURE' && /image/i.test(node.className);
|
||||
const imageToolbarItems = getImageToolbarItems(editor);
|
||||
if (imageToolbarItems.length > 0) {
|
||||
editor.ui.registry.addContextToolbar('imageselection', {
|
||||
predicate: isImage,
|
||||
items: imageToolbarItems,
|
||||
position: 'node'
|
||||
});
|
||||
}
|
||||
const textToolbarItems = getTextSelectionToolbarItems(editor);
|
||||
if (textToolbarItems.length > 0) {
|
||||
editor.ui.registry.addContextToolbar('textselection', {
|
||||
predicate: node => !isImage(node) && !editor.selection.isCollapsed() && isEditable(node),
|
||||
items: textToolbarItems,
|
||||
position: 'selection',
|
||||
scope: 'editor'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
var Plugin = () => {
|
||||
global$2.add('quickbars', editor => {
|
||||
register(editor);
|
||||
setupButtons(editor);
|
||||
addToEditor$1(editor);
|
||||
addToEditor(editor);
|
||||
});
|
||||
};
|
||||
|
||||
Plugin();
|
||||
|
||||
})();
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* TinyMCE version 6.2.0 (2022-09-08)
|
||||
*/
|
||||
!function(){"use strict";var e=tinymce.util.Tools.resolve("tinymce.PluginManager");const t=e=>t=>typeof t===e,o=("string",e=>"string"===(e=>{const t=typeof e;return null===e?"null":"object"===t&&Array.isArray(e)?"array":"object"===t&&(o=n=e,(r=String).prototype.isPrototypeOf(o)||n.constructor?.name===r.name)?"string":t;var o,n,r})(e));const n=t("boolean"),r=t("function"),i=e=>t=>t.options.get(e),s=i("quickbars_selection_toolbar"),a=i("quickbars_insert_toolbar"),l=i("quickbars_image_toolbar");let c=0;var u=tinymce.util.Tools.resolve("tinymce.Env"),d=tinymce.util.Tools.resolve("tinymce.util.Delay");const m=e=>{e.ui.registry.addButton("quickimage",{icon:"image",tooltip:"Insert image",onAction:()=>{(e=>new Promise((t=>{const o=document.createElement("input");o.type="file",o.accept="image/*",o.style.position="fixed",o.style.left="0",o.style.top="0",o.style.opacity="0.001",document.body.appendChild(o),o.addEventListener("change",(e=>{t(Array.prototype.slice.call(e.target.files))}));const n=r=>{const i=()=>{t([]),o.parentNode?.removeChild(o)};u.os.isAndroid()&&"remove"!==r.type?d.setEditorTimeout(e,i,0):i(),e.off("focusin remove",n)};e.on("focusin remove",n),o.click()})))(e).then((t=>{if(t.length>0){const o=t[0];(e=>new Promise((t=>{const o=new FileReader;o.onloadend=()=>{t(o.result.split(",")[1])},o.readAsDataURL(e)})))(o).then((t=>{((e,t,o)=>{const n=e.editorUpload.blobCache,r=n.create((e=>{const t=(new Date).getTime(),o=Math.floor(1e9*Math.random());return c++,"mceu_"+o+c+String(t)})(),o,t);n.add(r),e.insertContent(e.dom.createHTML("img",{src:r.blobUri()}))})(e,t,o)}))}}))}}),e.ui.registry.addButton("quicktable",{icon:"table",tooltip:"Insert table",onAction:()=>{((e,t,o)=>{e.execCommand("mceInsertTable",!1,{rows:2,columns:2})})(e)}})},g=(!1,()=>false);class h{constructor(e,t){this.tag=e,this.value=t}static some(e){return new h(!0,e)}static none(){return h.singletonNone}fold(e,t){return this.tag?t(this.value):e()}isSome(){return this.tag}isNone(){return!this.tag}map(e){return this.tag?h.some(e(this.value)):h.none()}bind(e){return this.tag?e(this.value):h.none()}exists(e){return this.tag&&e(this.value)}forall(e){return!this.tag||e(this.value)}filter(e){return!this.tag||e(this.value)?this:h.none()}getOr(e){return this.tag?this.value:e}or(e){return this.tag?this:e}getOrThunk(e){return this.tag?this.value:e()}orThunk(e){return this.tag?this:e()}getOrDie(e){if(this.tag)return this.value;throw new Error(e??"Called getOrDie on None")}static from(e){return null==e?h.none():h.some(e)}getOrNull(){return this.tag?this.value:null}getOrUndefined(){return this.value}each(e){this.tag&&e(this.value)}toArray(){return this.tag?[this.value]:[]}toString(){return this.tag?`some(${this.value})`:"none()"}}h.singletonNone=new h(!1);var b=(e,t,o,n,i)=>e(o,n)?h.some(o):r(i)&&i(o)?h.none():t(o,n,i);const p=e=>{if(null==e)throw new Error("Node cannot be null or undefined");return{dom:e}},f=p,v=(e,t)=>{const o=e.dom;if(1!==o.nodeType)return!1;{const e=o;if(void 0!==e.matches)return e.matches(t);if(void 0!==e.msMatchesSelector)return e.msMatchesSelector(t);if(void 0!==e.webkitMatchesSelector)return e.webkitMatchesSelector(t);if(void 0!==e.mozMatchesSelector)return e.mozMatchesSelector(t);throw new Error("Browser lacks native selectors")}};"undefined"!=typeof window?window:Function("return this;")();const y=(e,t,o)=>{let n=e.dom;const i=r(o)?o:g;for(;n.parentNode;){n=n.parentNode;const e=f(n);if(t(e))return h.some(e);if(i(e))break}return h.none()},k=(e,t,o)=>y(e,(e=>v(e,t)),o),w=e=>{const t=a(e);t.length>0&&e.ui.registry.addContextToolbar("quickblock",{predicate:t=>{const o=f(t),n=e.schema.getTextBlockElements(),r=t=>t.dom===e.getBody();return((e,t,o)=>b(((e,t)=>v(e,t)),k,e,"table",o))(o,0,r).fold((()=>((t,o,r)=>b(((e,t)=>t(e)),y,t,(t=>t.dom.nodeName.toLowerCase()in n&&e.dom.isEmpty(t.dom)),r))(o,0,r).isSome()),g)},items:t,position:"line",scope:"editor"})};e.add("quickbars",(e=>{(e=>{const t=e.options.register,r=e=>t=>{const r=n(t)||o(t);return r?n(t)?{value:t?e:"",valid:r}:{value:t.trim(),valid:r}:{valid:!1,message:"Must be a boolean or string."}},i="bold italic | quicklink h2 h3 blockquote";t("quickbars_selection_toolbar",{processor:r(i),default:i});const s="quickimage quicktable";t("quickbars_insert_toolbar",{processor:r(s),default:s});const a="alignleft aligncenter alignright";t("quickbars_image_toolbar",{processor:r(a),default:a})})(e),m(e),w(e),(e=>{const t=e=>"IMG"===e.nodeName||"FIGURE"===e.nodeName&&/image/i.test(e.className),o=l(e);o.length>0&&e.ui.registry.addContextToolbar("imageselection",{predicate:t,items:o,position:"node"});const n=s(e);n.length>0&&e.ui.registry.addContextToolbar("textselection",{predicate:o=>!t(o)&&!e.selection.isCollapsed()&&(t=>"false"!==e.dom.getContentEditableParent(t))(o),items:n,position:"selection",scope:"editor"})})(e)}))}();
|
||||
@@ -0,0 +1,118 @@
|
||||
/**
|
||||
* TinyMCE version 6.2.0 (2022-09-08)
|
||||
*/
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
var global$2 = tinymce.util.Tools.resolve('tinymce.PluginManager');
|
||||
|
||||
const isSimpleType = type => value => typeof value === type;
|
||||
const isFunction = isSimpleType('function');
|
||||
|
||||
var global$1 = tinymce.util.Tools.resolve('tinymce.dom.DOMUtils');
|
||||
|
||||
var global = tinymce.util.Tools.resolve('tinymce.util.Tools');
|
||||
|
||||
const option = name => editor => editor.options.get(name);
|
||||
const register$2 = editor => {
|
||||
const registerOption = editor.options.register;
|
||||
registerOption('save_enablewhendirty', {
|
||||
processor: 'boolean',
|
||||
default: true
|
||||
});
|
||||
registerOption('save_onsavecallback', { processor: 'function' });
|
||||
registerOption('save_oncancelcallback', { processor: 'function' });
|
||||
};
|
||||
const enableWhenDirty = option('save_enablewhendirty');
|
||||
const getOnSaveCallback = option('save_onsavecallback');
|
||||
const getOnCancelCallback = option('save_oncancelcallback');
|
||||
|
||||
const displayErrorMessage = (editor, message) => {
|
||||
editor.notificationManager.open({
|
||||
text: message,
|
||||
type: 'error'
|
||||
});
|
||||
};
|
||||
const save = editor => {
|
||||
const formObj = global$1.DOM.getParent(editor.id, 'form');
|
||||
if (enableWhenDirty(editor) && !editor.isDirty()) {
|
||||
return;
|
||||
}
|
||||
editor.save();
|
||||
const onSaveCallback = getOnSaveCallback(editor);
|
||||
if (isFunction(onSaveCallback)) {
|
||||
onSaveCallback.call(editor, editor);
|
||||
editor.nodeChanged();
|
||||
return;
|
||||
}
|
||||
if (formObj) {
|
||||
editor.setDirty(false);
|
||||
if (!formObj.onsubmit || formObj.onsubmit()) {
|
||||
if (typeof formObj.submit === 'function') {
|
||||
formObj.submit();
|
||||
} else {
|
||||
displayErrorMessage(editor, 'Error: Form submit field collision.');
|
||||
}
|
||||
}
|
||||
editor.nodeChanged();
|
||||
} else {
|
||||
displayErrorMessage(editor, 'Error: No form element found.');
|
||||
}
|
||||
};
|
||||
const cancel = editor => {
|
||||
const h = global.trim(editor.startContent);
|
||||
const onCancelCallback = getOnCancelCallback(editor);
|
||||
if (isFunction(onCancelCallback)) {
|
||||
onCancelCallback.call(editor, editor);
|
||||
return;
|
||||
}
|
||||
editor.resetContent(h);
|
||||
};
|
||||
|
||||
const register$1 = editor => {
|
||||
editor.addCommand('mceSave', () => {
|
||||
save(editor);
|
||||
});
|
||||
editor.addCommand('mceCancel', () => {
|
||||
cancel(editor);
|
||||
});
|
||||
};
|
||||
|
||||
const stateToggle = editor => api => {
|
||||
const handler = () => {
|
||||
api.setEnabled(!enableWhenDirty(editor) || editor.isDirty());
|
||||
};
|
||||
handler();
|
||||
editor.on('NodeChange dirty', handler);
|
||||
return () => editor.off('NodeChange dirty', handler);
|
||||
};
|
||||
const register = editor => {
|
||||
editor.ui.registry.addButton('save', {
|
||||
icon: 'save',
|
||||
tooltip: 'Save',
|
||||
enabled: false,
|
||||
onAction: () => editor.execCommand('mceSave'),
|
||||
onSetup: stateToggle(editor)
|
||||
});
|
||||
editor.ui.registry.addButton('cancel', {
|
||||
icon: 'cancel',
|
||||
tooltip: 'Cancel',
|
||||
enabled: false,
|
||||
onAction: () => editor.execCommand('mceCancel'),
|
||||
onSetup: stateToggle(editor)
|
||||
});
|
||||
editor.addShortcut('Meta+S', '', 'mceSave');
|
||||
};
|
||||
|
||||
var Plugin = () => {
|
||||
global$2.add('save', editor => {
|
||||
register$2(editor);
|
||||
register(editor);
|
||||
register$1(editor);
|
||||
});
|
||||
};
|
||||
|
||||
Plugin();
|
||||
|
||||
})();
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* TinyMCE version 6.2.0 (2022-09-08)
|
||||
*/
|
||||
!function(){"use strict";var e=tinymce.util.Tools.resolve("tinymce.PluginManager");const n=("function",e=>"function"==typeof e);var o=tinymce.util.Tools.resolve("tinymce.dom.DOMUtils"),t=tinymce.util.Tools.resolve("tinymce.util.Tools");const a=e=>n=>n.options.get(e),c=a("save_enablewhendirty"),i=a("save_onsavecallback"),s=a("save_oncancelcallback"),r=(e,n)=>{e.notificationManager.open({text:n,type:"error"})},l=e=>n=>{const o=()=>{n.setEnabled(!c(e)||e.isDirty())};return o(),e.on("NodeChange dirty",o),()=>e.off("NodeChange dirty",o)};e.add("save",(e=>{(e=>{const n=e.options.register;n("save_enablewhendirty",{processor:"boolean",default:!0}),n("save_onsavecallback",{processor:"function"}),n("save_oncancelcallback",{processor:"function"})})(e),(e=>{e.ui.registry.addButton("save",{icon:"save",tooltip:"Save",enabled:!1,onAction:()=>e.execCommand("mceSave"),onSetup:l(e)}),e.ui.registry.addButton("cancel",{icon:"cancel",tooltip:"Cancel",enabled:!1,onAction:()=>e.execCommand("mceCancel"),onSetup:l(e)}),e.addShortcut("Meta+S","","mceSave")})(e),(e=>{e.addCommand("mceSave",(()=>{(e=>{const t=o.DOM.getParent(e.id,"form");if(c(e)&&!e.isDirty())return;e.save();const a=i(e);if(n(a))return a.call(e,e),void e.nodeChanged();t?(e.setDirty(!1),t.onsubmit&&!t.onsubmit()||("function"==typeof t.submit?t.submit():r(e,"Error: Form submit field collision.")),e.nodeChanged()):r(e,"Error: No form element found.")})(e)})),e.addCommand("mceCancel",(()=>{(e=>{const o=t.trim(e.startContent),a=s(e);n(a)?a.call(e,e):e.resetContent(o)})(e)}))})(e)}))}();
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -0,0 +1,549 @@
|
||||
/**
|
||||
* TinyMCE version 6.2.0 (2022-09-08)
|
||||
*/
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
var global$2 = tinymce.util.Tools.resolve('tinymce.PluginManager');
|
||||
|
||||
const hasProto = (v, constructor, predicate) => {
|
||||
if (predicate(v, constructor.prototype)) {
|
||||
return true;
|
||||
} else {
|
||||
return v.constructor?.name === constructor.name;
|
||||
}
|
||||
};
|
||||
const typeOf = x => {
|
||||
const t = typeof x;
|
||||
if (x === null) {
|
||||
return 'null';
|
||||
} else if (t === 'object' && Array.isArray(x)) {
|
||||
return 'array';
|
||||
} else if (t === 'object' && hasProto(x, String, (o, proto) => proto.isPrototypeOf(o))) {
|
||||
return 'string';
|
||||
} else {
|
||||
return t;
|
||||
}
|
||||
};
|
||||
const isType = type => value => typeOf(value) === type;
|
||||
const isSimpleType = type => value => typeof value === type;
|
||||
const isString = isType('string');
|
||||
const isObject = isType('object');
|
||||
const isArray = isType('array');
|
||||
const isNullable = a => a === null || a === undefined;
|
||||
const isNonNullable = a => !isNullable(a);
|
||||
const isFunction = isSimpleType('function');
|
||||
const isArrayOf = (value, pred) => {
|
||||
if (isArray(value)) {
|
||||
for (let i = 0, len = value.length; i < len; ++i) {
|
||||
if (!pred(value[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const constant = value => {
|
||||
return () => {
|
||||
return value;
|
||||
};
|
||||
};
|
||||
function curry(fn, ...initialArgs) {
|
||||
return (...restArgs) => {
|
||||
const all = initialArgs.concat(restArgs);
|
||||
return fn.apply(null, all);
|
||||
};
|
||||
}
|
||||
const never = constant(false);
|
||||
|
||||
const escape = text => text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
|
||||
var global$1 = tinymce.util.Tools.resolve('tinymce.util.Tools');
|
||||
|
||||
const option = name => editor => editor.options.get(name);
|
||||
const register$2 = editor => {
|
||||
const registerOption = editor.options.register;
|
||||
registerOption('template_cdate_classes', {
|
||||
processor: 'string',
|
||||
default: 'cdate'
|
||||
});
|
||||
registerOption('template_mdate_classes', {
|
||||
processor: 'string',
|
||||
default: 'mdate'
|
||||
});
|
||||
registerOption('template_selected_content_classes', {
|
||||
processor: 'string',
|
||||
default: 'selcontent'
|
||||
});
|
||||
registerOption('template_preview_replace_values', { processor: 'object' });
|
||||
registerOption('template_replace_values', { processor: 'object' });
|
||||
registerOption('templates', {
|
||||
processor: value => isString(value) || isArrayOf(value, isObject) || isFunction(value),
|
||||
default: []
|
||||
});
|
||||
registerOption('template_cdate_format', {
|
||||
processor: 'string',
|
||||
default: editor.translate('%Y-%m-%d')
|
||||
});
|
||||
registerOption('template_mdate_format', {
|
||||
processor: 'string',
|
||||
default: editor.translate('%Y-%m-%d')
|
||||
});
|
||||
};
|
||||
const getCreationDateClasses = option('template_cdate_classes');
|
||||
const getModificationDateClasses = option('template_mdate_classes');
|
||||
const getSelectedContentClasses = option('template_selected_content_classes');
|
||||
const getPreviewReplaceValues = option('template_preview_replace_values');
|
||||
const getTemplateReplaceValues = option('template_replace_values');
|
||||
const getTemplates = option('templates');
|
||||
const getCdateFormat = option('template_cdate_format');
|
||||
const getMdateFormat = option('template_mdate_format');
|
||||
const getContentStyle = option('content_style');
|
||||
const shouldUseContentCssCors = option('content_css_cors');
|
||||
const getBodyClass = option('body_class');
|
||||
|
||||
const addZeros = (value, len) => {
|
||||
value = '' + value;
|
||||
if (value.length < len) {
|
||||
for (let i = 0; i < len - value.length; i++) {
|
||||
value = '0' + value;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
};
|
||||
const getDateTime = (editor, fmt, date = new Date()) => {
|
||||
const daysShort = 'Sun Mon Tue Wed Thu Fri Sat Sun'.split(' ');
|
||||
const daysLong = 'Sunday Monday Tuesday Wednesday Thursday Friday Saturday Sunday'.split(' ');
|
||||
const monthsShort = 'Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec'.split(' ');
|
||||
const monthsLong = 'January February March April May June July August September October November December'.split(' ');
|
||||
fmt = fmt.replace('%D', '%m/%d/%Y');
|
||||
fmt = fmt.replace('%r', '%I:%M:%S %p');
|
||||
fmt = fmt.replace('%Y', '' + date.getFullYear());
|
||||
fmt = fmt.replace('%y', '' + date.getYear());
|
||||
fmt = fmt.replace('%m', addZeros(date.getMonth() + 1, 2));
|
||||
fmt = fmt.replace('%d', addZeros(date.getDate(), 2));
|
||||
fmt = fmt.replace('%H', '' + addZeros(date.getHours(), 2));
|
||||
fmt = fmt.replace('%M', '' + addZeros(date.getMinutes(), 2));
|
||||
fmt = fmt.replace('%S', '' + addZeros(date.getSeconds(), 2));
|
||||
fmt = fmt.replace('%I', '' + ((date.getHours() + 11) % 12 + 1));
|
||||
fmt = fmt.replace('%p', '' + (date.getHours() < 12 ? 'AM' : 'PM'));
|
||||
fmt = fmt.replace('%B', '' + editor.translate(monthsLong[date.getMonth()]));
|
||||
fmt = fmt.replace('%b', '' + editor.translate(monthsShort[date.getMonth()]));
|
||||
fmt = fmt.replace('%A', '' + editor.translate(daysLong[date.getDay()]));
|
||||
fmt = fmt.replace('%a', '' + editor.translate(daysShort[date.getDay()]));
|
||||
fmt = fmt.replace('%%', '%');
|
||||
return fmt;
|
||||
};
|
||||
|
||||
class Optional {
|
||||
constructor(tag, value) {
|
||||
this.tag = tag;
|
||||
this.value = value;
|
||||
}
|
||||
static some(value) {
|
||||
return new Optional(true, value);
|
||||
}
|
||||
static none() {
|
||||
return Optional.singletonNone;
|
||||
}
|
||||
fold(onNone, onSome) {
|
||||
if (this.tag) {
|
||||
return onSome(this.value);
|
||||
} else {
|
||||
return onNone();
|
||||
}
|
||||
}
|
||||
isSome() {
|
||||
return this.tag;
|
||||
}
|
||||
isNone() {
|
||||
return !this.tag;
|
||||
}
|
||||
map(mapper) {
|
||||
if (this.tag) {
|
||||
return Optional.some(mapper(this.value));
|
||||
} else {
|
||||
return Optional.none();
|
||||
}
|
||||
}
|
||||
bind(binder) {
|
||||
if (this.tag) {
|
||||
return binder(this.value);
|
||||
} else {
|
||||
return Optional.none();
|
||||
}
|
||||
}
|
||||
exists(predicate) {
|
||||
return this.tag && predicate(this.value);
|
||||
}
|
||||
forall(predicate) {
|
||||
return !this.tag || predicate(this.value);
|
||||
}
|
||||
filter(predicate) {
|
||||
if (!this.tag || predicate(this.value)) {
|
||||
return this;
|
||||
} else {
|
||||
return Optional.none();
|
||||
}
|
||||
}
|
||||
getOr(replacement) {
|
||||
return this.tag ? this.value : replacement;
|
||||
}
|
||||
or(replacement) {
|
||||
return this.tag ? this : replacement;
|
||||
}
|
||||
getOrThunk(thunk) {
|
||||
return this.tag ? this.value : thunk();
|
||||
}
|
||||
orThunk(thunk) {
|
||||
return this.tag ? this : thunk();
|
||||
}
|
||||
getOrDie(message) {
|
||||
if (!this.tag) {
|
||||
throw new Error(message ?? 'Called getOrDie on None');
|
||||
} else {
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
static from(value) {
|
||||
return isNonNullable(value) ? Optional.some(value) : Optional.none();
|
||||
}
|
||||
getOrNull() {
|
||||
return this.tag ? this.value : null;
|
||||
}
|
||||
getOrUndefined() {
|
||||
return this.value;
|
||||
}
|
||||
each(worker) {
|
||||
if (this.tag) {
|
||||
worker(this.value);
|
||||
}
|
||||
}
|
||||
toArray() {
|
||||
return this.tag ? [this.value] : [];
|
||||
}
|
||||
toString() {
|
||||
return this.tag ? `some(${ this.value })` : 'none()';
|
||||
}
|
||||
}
|
||||
Optional.singletonNone = new Optional(false);
|
||||
|
||||
const exists = (xs, pred) => {
|
||||
for (let i = 0, len = xs.length; i < len; i++) {
|
||||
const x = xs[i];
|
||||
if (pred(x, i)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
const map = (xs, f) => {
|
||||
const len = xs.length;
|
||||
const r = new Array(len);
|
||||
for (let i = 0; i < len; i++) {
|
||||
const x = xs[i];
|
||||
r[i] = f(x, i);
|
||||
}
|
||||
return r;
|
||||
};
|
||||
const findUntil = (xs, pred, until) => {
|
||||
for (let i = 0, len = xs.length; i < len; i++) {
|
||||
const x = xs[i];
|
||||
if (pred(x, i)) {
|
||||
return Optional.some(x);
|
||||
} else if (until(x, i)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Optional.none();
|
||||
};
|
||||
const find = (xs, pred) => {
|
||||
return findUntil(xs, pred, never);
|
||||
};
|
||||
|
||||
const hasOwnProperty = Object.hasOwnProperty;
|
||||
const get = (obj, key) => {
|
||||
return has(obj, key) ? Optional.from(obj[key]) : Optional.none();
|
||||
};
|
||||
const has = (obj, key) => hasOwnProperty.call(obj, key);
|
||||
|
||||
const entitiesAttr = {
|
||||
'"': '"',
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
'&': '&',
|
||||
'\'': '''
|
||||
};
|
||||
const htmlEscape = html => html.replace(/["'<>&]/g, match => get(entitiesAttr, match).getOr(match));
|
||||
const hasAnyClasses = (dom, n, classes) => exists(classes.split(/\s+/), c => dom.hasClass(n, c));
|
||||
|
||||
const createTemplateList = (editor, callback) => {
|
||||
return () => {
|
||||
const templateList = getTemplates(editor);
|
||||
if (isFunction(templateList)) {
|
||||
templateList(callback);
|
||||
} else if (isString(templateList)) {
|
||||
fetch(templateList).then(res => {
|
||||
if (res.ok) {
|
||||
res.json().then(callback);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
callback(templateList);
|
||||
}
|
||||
};
|
||||
};
|
||||
const replaceTemplateValues = (html, templateValues) => {
|
||||
global$1.each(templateValues, (v, k) => {
|
||||
if (isFunction(v)) {
|
||||
v = v(k);
|
||||
}
|
||||
html = html.replace(new RegExp('\\{\\$' + escape(k) + '\\}', 'g'), v);
|
||||
});
|
||||
return html;
|
||||
};
|
||||
const replaceVals = (editor, scope) => {
|
||||
const dom = editor.dom, vl = getTemplateReplaceValues(editor);
|
||||
global$1.each(dom.select('*', scope), e => {
|
||||
global$1.each(vl, (v, k) => {
|
||||
if (dom.hasClass(e, k)) {
|
||||
if (isFunction(v)) {
|
||||
v(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
const insertTemplate = (editor, _ui, html) => {
|
||||
const dom = editor.dom;
|
||||
const sel = editor.selection.getContent();
|
||||
html = replaceTemplateValues(html, getTemplateReplaceValues(editor));
|
||||
let el = dom.create('div', {}, html);
|
||||
const n = dom.select('.mceTmpl', el);
|
||||
if (n && n.length > 0) {
|
||||
el = dom.create('div');
|
||||
el.appendChild(n[0].cloneNode(true));
|
||||
}
|
||||
global$1.each(dom.select('*', el), n => {
|
||||
if (hasAnyClasses(dom, n, getCreationDateClasses(editor))) {
|
||||
n.innerHTML = getDateTime(editor, getCdateFormat(editor));
|
||||
}
|
||||
if (hasAnyClasses(dom, n, getModificationDateClasses(editor))) {
|
||||
n.innerHTML = getDateTime(editor, getMdateFormat(editor));
|
||||
}
|
||||
if (hasAnyClasses(dom, n, getSelectedContentClasses(editor))) {
|
||||
n.innerHTML = sel;
|
||||
}
|
||||
});
|
||||
replaceVals(editor, el);
|
||||
editor.execCommand('mceInsertContent', false, el.innerHTML);
|
||||
editor.addVisual();
|
||||
};
|
||||
|
||||
var global = tinymce.util.Tools.resolve('tinymce.Env');
|
||||
|
||||
const getPreviewContent = (editor, html) => {
|
||||
if (html.indexOf('<html>') === -1) {
|
||||
let contentCssEntries = '';
|
||||
const contentStyle = getContentStyle(editor) ?? '';
|
||||
const cors = shouldUseContentCssCors(editor) ? ' crossorigin="anonymous"' : '';
|
||||
global$1.each(editor.contentCSS, url => {
|
||||
contentCssEntries += '<link type="text/css" rel="stylesheet" href="' + editor.documentBaseURI.toAbsolute(url) + '"' + cors + '>';
|
||||
});
|
||||
if (contentStyle) {
|
||||
contentCssEntries += '<style type="text/css">' + contentStyle + '</style>';
|
||||
}
|
||||
const bodyClass = getBodyClass(editor);
|
||||
const encode = editor.dom.encode;
|
||||
const isMetaKeyPressed = global.os.isMacOS() || global.os.isiOS() ? 'e.metaKey' : 'e.ctrlKey && !e.altKey';
|
||||
const preventClicksOnLinksScript = '<script>' + 'document.addEventListener && document.addEventListener("click", function(e) {' + 'for (var elm = e.target; elm; elm = elm.parentNode) {' + 'if (elm.nodeName === "A" && !(' + isMetaKeyPressed + ')) {' + 'e.preventDefault();' + '}' + '}' + '}, false);' + '</script> ';
|
||||
const directionality = editor.getBody().dir;
|
||||
const dirAttr = directionality ? ' dir="' + encode(directionality) + '"' : '';
|
||||
html = '<!DOCTYPE html>' + '<html>' + '<head>' + '<base href="' + encode(editor.documentBaseURI.getURI()) + '">' + contentCssEntries + preventClicksOnLinksScript + '</head>' + '<body class="' + encode(bodyClass) + '"' + dirAttr + '>' + html + '</body>' + '</html>';
|
||||
}
|
||||
return replaceTemplateValues(html, getPreviewReplaceValues(editor));
|
||||
};
|
||||
const open = (editor, templateList) => {
|
||||
const createTemplates = () => {
|
||||
if (!templateList || templateList.length === 0) {
|
||||
const message = editor.translate('No templates defined.');
|
||||
editor.notificationManager.open({
|
||||
text: message,
|
||||
type: 'info'
|
||||
});
|
||||
return Optional.none();
|
||||
}
|
||||
return Optional.from(global$1.map(templateList, (template, index) => {
|
||||
const isUrlTemplate = t => t.url !== undefined;
|
||||
return {
|
||||
selected: index === 0,
|
||||
text: template.title,
|
||||
value: {
|
||||
url: isUrlTemplate(template) ? Optional.from(template.url) : Optional.none(),
|
||||
content: !isUrlTemplate(template) ? Optional.from(template.content) : Optional.none(),
|
||||
description: template.description
|
||||
}
|
||||
};
|
||||
}));
|
||||
};
|
||||
const createSelectBoxItems = templates => map(templates, t => ({
|
||||
text: t.text,
|
||||
value: t.text
|
||||
}));
|
||||
const findTemplate = (templates, templateTitle) => find(templates, t => t.text === templateTitle);
|
||||
const loadFailedAlert = api => {
|
||||
editor.windowManager.alert('Could not load the specified template.', () => api.focus('template'));
|
||||
};
|
||||
const getTemplateContent = t => t.value.url.fold(() => Promise.resolve(t.value.content.getOr('')), url => fetch(url).then(res => res.ok ? res.text() : Promise.reject()));
|
||||
const onChange = (templates, updateDialog) => (api, change) => {
|
||||
if (change.name === 'template') {
|
||||
const newTemplateTitle = api.getData().template;
|
||||
findTemplate(templates, newTemplateTitle).each(t => {
|
||||
api.block('Loading...');
|
||||
getTemplateContent(t).then(previewHtml => {
|
||||
updateDialog(api, t, previewHtml);
|
||||
}).catch(() => {
|
||||
updateDialog(api, t, '');
|
||||
api.setEnabled('save', false);
|
||||
loadFailedAlert(api);
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
const onSubmit = templates => api => {
|
||||
const data = api.getData();
|
||||
findTemplate(templates, data.template).each(t => {
|
||||
getTemplateContent(t).then(previewHtml => {
|
||||
editor.execCommand('mceInsertTemplate', false, previewHtml);
|
||||
api.close();
|
||||
}).catch(() => {
|
||||
api.setEnabled('save', false);
|
||||
loadFailedAlert(api);
|
||||
});
|
||||
});
|
||||
};
|
||||
const openDialog = templates => {
|
||||
const selectBoxItems = createSelectBoxItems(templates);
|
||||
const buildDialogSpec = (bodyItems, initialData) => ({
|
||||
title: 'Insert Template',
|
||||
size: 'large',
|
||||
body: {
|
||||
type: 'panel',
|
||||
items: bodyItems
|
||||
},
|
||||
initialData,
|
||||
buttons: [
|
||||
{
|
||||
type: 'cancel',
|
||||
name: 'cancel',
|
||||
text: 'Cancel'
|
||||
},
|
||||
{
|
||||
type: 'submit',
|
||||
name: 'save',
|
||||
text: 'Save',
|
||||
primary: true
|
||||
}
|
||||
],
|
||||
onSubmit: onSubmit(templates),
|
||||
onChange: onChange(templates, updateDialog)
|
||||
});
|
||||
const updateDialog = (dialogApi, template, previewHtml) => {
|
||||
const content = getPreviewContent(editor, previewHtml);
|
||||
const bodyItems = [
|
||||
{
|
||||
type: 'selectbox',
|
||||
name: 'template',
|
||||
label: 'Templates',
|
||||
items: selectBoxItems
|
||||
},
|
||||
{
|
||||
type: 'htmlpanel',
|
||||
html: `<p aria-live="polite">${ htmlEscape(template.value.description) }</p>`
|
||||
},
|
||||
{
|
||||
label: 'Preview',
|
||||
type: 'iframe',
|
||||
name: 'preview',
|
||||
sandboxed: false,
|
||||
transparent: false
|
||||
}
|
||||
];
|
||||
const initialData = {
|
||||
template: template.text,
|
||||
preview: content
|
||||
};
|
||||
dialogApi.unblock();
|
||||
dialogApi.redial(buildDialogSpec(bodyItems, initialData));
|
||||
dialogApi.focus('template');
|
||||
};
|
||||
const dialogApi = editor.windowManager.open(buildDialogSpec([], {
|
||||
template: '',
|
||||
preview: ''
|
||||
}));
|
||||
dialogApi.block('Loading...');
|
||||
getTemplateContent(templates[0]).then(previewHtml => {
|
||||
updateDialog(dialogApi, templates[0], previewHtml);
|
||||
}).catch(() => {
|
||||
updateDialog(dialogApi, templates[0], '');
|
||||
dialogApi.setEnabled('save', false);
|
||||
loadFailedAlert(dialogApi);
|
||||
});
|
||||
};
|
||||
const optTemplates = createTemplates();
|
||||
optTemplates.each(openDialog);
|
||||
};
|
||||
|
||||
const showDialog = editor => templates => {
|
||||
open(editor, templates);
|
||||
};
|
||||
const register$1 = editor => {
|
||||
editor.addCommand('mceInsertTemplate', curry(insertTemplate, editor));
|
||||
editor.addCommand('mceTemplate', createTemplateList(editor, showDialog(editor)));
|
||||
};
|
||||
|
||||
const setup = editor => {
|
||||
editor.on('PreProcess', o => {
|
||||
const dom = editor.dom, dateFormat = getMdateFormat(editor);
|
||||
global$1.each(dom.select('div', o.node), e => {
|
||||
if (dom.hasClass(e, 'mceTmpl')) {
|
||||
global$1.each(dom.select('*', e), e => {
|
||||
if (hasAnyClasses(dom, e, getModificationDateClasses(editor))) {
|
||||
e.innerHTML = getDateTime(editor, dateFormat);
|
||||
}
|
||||
});
|
||||
replaceVals(editor, e);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const register = editor => {
|
||||
const onAction = () => editor.execCommand('mceTemplate');
|
||||
editor.ui.registry.addButton('template', {
|
||||
icon: 'template',
|
||||
tooltip: 'Insert template',
|
||||
onAction
|
||||
});
|
||||
editor.ui.registry.addMenuItem('template', {
|
||||
icon: 'template',
|
||||
text: 'Insert template...',
|
||||
onAction
|
||||
});
|
||||
};
|
||||
|
||||
var Plugin = () => {
|
||||
global$2.add('template', editor => {
|
||||
register$2(editor);
|
||||
register(editor);
|
||||
register$1(editor);
|
||||
setup(editor);
|
||||
});
|
||||
};
|
||||
|
||||
Plugin();
|
||||
|
||||
})();
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,98 @@
|
||||
/**
|
||||
* TinyMCE version 6.2.0 (2022-09-08)
|
||||
*/
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
const Cell = initial => {
|
||||
let value = initial;
|
||||
const get = () => {
|
||||
return value;
|
||||
};
|
||||
const set = v => {
|
||||
value = v;
|
||||
};
|
||||
return {
|
||||
get,
|
||||
set
|
||||
};
|
||||
};
|
||||
|
||||
var global = tinymce.util.Tools.resolve('tinymce.PluginManager');
|
||||
|
||||
const fireVisualBlocks = (editor, state) => {
|
||||
editor.dispatch('VisualBlocks', { state });
|
||||
};
|
||||
|
||||
const toggleVisualBlocks = (editor, pluginUrl, enabledState) => {
|
||||
const dom = editor.dom;
|
||||
dom.toggleClass(editor.getBody(), 'mce-visualblocks');
|
||||
enabledState.set(!enabledState.get());
|
||||
fireVisualBlocks(editor, enabledState.get());
|
||||
};
|
||||
|
||||
const register$2 = (editor, pluginUrl, enabledState) => {
|
||||
editor.addCommand('mceVisualBlocks', () => {
|
||||
toggleVisualBlocks(editor, pluginUrl, enabledState);
|
||||
});
|
||||
};
|
||||
|
||||
const option = name => editor => editor.options.get(name);
|
||||
const register$1 = editor => {
|
||||
const registerOption = editor.options.register;
|
||||
registerOption('visualblocks_default_state', {
|
||||
processor: 'boolean',
|
||||
default: false
|
||||
});
|
||||
};
|
||||
const isEnabledByDefault = option('visualblocks_default_state');
|
||||
|
||||
const setup = (editor, pluginUrl, enabledState) => {
|
||||
editor.on('PreviewFormats AfterPreviewFormats', e => {
|
||||
if (enabledState.get()) {
|
||||
editor.dom.toggleClass(editor.getBody(), 'mce-visualblocks', e.type === 'afterpreviewformats');
|
||||
}
|
||||
});
|
||||
editor.on('init', () => {
|
||||
if (isEnabledByDefault(editor)) {
|
||||
toggleVisualBlocks(editor, pluginUrl, enabledState);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const toggleActiveState = (editor, enabledState) => api => {
|
||||
api.setActive(enabledState.get());
|
||||
const editorEventCallback = e => api.setActive(e.state);
|
||||
editor.on('VisualBlocks', editorEventCallback);
|
||||
return () => editor.off('VisualBlocks', editorEventCallback);
|
||||
};
|
||||
const register = (editor, enabledState) => {
|
||||
const onAction = () => editor.execCommand('mceVisualBlocks');
|
||||
editor.ui.registry.addToggleButton('visualblocks', {
|
||||
icon: 'visualblocks',
|
||||
tooltip: 'Show blocks',
|
||||
onAction,
|
||||
onSetup: toggleActiveState(editor, enabledState)
|
||||
});
|
||||
editor.ui.registry.addToggleMenuItem('visualblocks', {
|
||||
text: 'Show blocks',
|
||||
icon: 'visualblocks',
|
||||
onAction,
|
||||
onSetup: toggleActiveState(editor, enabledState)
|
||||
});
|
||||
};
|
||||
|
||||
var Plugin = () => {
|
||||
global.add('visualblocks', (editor, pluginUrl) => {
|
||||
register$1(editor);
|
||||
const enabledState = Cell(false);
|
||||
register$2(editor, pluginUrl, enabledState);
|
||||
register(editor, enabledState);
|
||||
setup(editor, pluginUrl, enabledState);
|
||||
});
|
||||
};
|
||||
|
||||
Plugin();
|
||||
|
||||
})();
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* TinyMCE version 6.2.0 (2022-09-08)
|
||||
*/
|
||||
!function(){"use strict";var t=tinymce.util.Tools.resolve("tinymce.PluginManager");const s=(t,s,o)=>{t.dom.toggleClass(t.getBody(),"mce-visualblocks"),o.set(!o.get()),((t,s)=>{t.dispatch("VisualBlocks",{state:s})})(t,o.get())},o=("visualblocks_default_state",t=>t.options.get("visualblocks_default_state"));const e=(t,s)=>o=>{o.setActive(s.get());const e=t=>o.setActive(t.state);return t.on("VisualBlocks",e),()=>t.off("VisualBlocks",e)};t.add("visualblocks",((t,l)=>{(t=>{(0,t.options.register)("visualblocks_default_state",{processor:"boolean",default:!1})})(t);const a=(t=>{let s=!1;return{get:()=>s,set:t=>{s=t}}})();((t,o,e)=>{t.addCommand("mceVisualBlocks",(()=>{s(t,0,e)}))})(t,0,a),((t,s)=>{const o=()=>t.execCommand("mceVisualBlocks");t.ui.registry.addToggleButton("visualblocks",{icon:"visualblocks",tooltip:"Show blocks",onAction:o,onSetup:e(t,s)}),t.ui.registry.addToggleMenuItem("visualblocks",{text:"Show blocks",icon:"visualblocks",onAction:o,onSetup:e(t,s)})})(t,a),((t,e,l)=>{t.on("PreviewFormats AfterPreviewFormats",(s=>{l.get()&&t.dom.toggleClass(t.getBody(),"mce-visualblocks","afterpreviewformats"===s.type)})),t.on("init",(()=>{o(t)&&s(t,0,l)}))})(t,0,a)}))}();
|
||||
@@ -0,0 +1,507 @@
|
||||
/**
|
||||
* TinyMCE version 6.2.0 (2022-09-08)
|
||||
*/
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
const Cell = initial => {
|
||||
let value = initial;
|
||||
const get = () => {
|
||||
return value;
|
||||
};
|
||||
const set = v => {
|
||||
value = v;
|
||||
};
|
||||
return {
|
||||
get,
|
||||
set
|
||||
};
|
||||
};
|
||||
|
||||
var global = tinymce.util.Tools.resolve('tinymce.PluginManager');
|
||||
|
||||
const get$2 = toggleState => {
|
||||
const isEnabled = () => {
|
||||
return toggleState.get();
|
||||
};
|
||||
return { isEnabled };
|
||||
};
|
||||
|
||||
const fireVisualChars = (editor, state) => {
|
||||
return editor.dispatch('VisualChars', { state });
|
||||
};
|
||||
|
||||
const hasProto = (v, constructor, predicate) => {
|
||||
if (predicate(v, constructor.prototype)) {
|
||||
return true;
|
||||
} else {
|
||||
return v.constructor?.name === constructor.name;
|
||||
}
|
||||
};
|
||||
const typeOf = x => {
|
||||
const t = typeof x;
|
||||
if (x === null) {
|
||||
return 'null';
|
||||
} else if (t === 'object' && Array.isArray(x)) {
|
||||
return 'array';
|
||||
} else if (t === 'object' && hasProto(x, String, (o, proto) => proto.isPrototypeOf(o))) {
|
||||
return 'string';
|
||||
} else {
|
||||
return t;
|
||||
}
|
||||
};
|
||||
const isType$1 = type => value => typeOf(value) === type;
|
||||
const isSimpleType = type => value => typeof value === type;
|
||||
const eq = t => a => t === a;
|
||||
const isString = isType$1('string');
|
||||
const isNull = eq(null);
|
||||
const isBoolean = isSimpleType('boolean');
|
||||
const isNullable = a => a === null || a === undefined;
|
||||
const isNonNullable = a => !isNullable(a);
|
||||
const isNumber = isSimpleType('number');
|
||||
|
||||
class Optional {
|
||||
constructor(tag, value) {
|
||||
this.tag = tag;
|
||||
this.value = value;
|
||||
}
|
||||
static some(value) {
|
||||
return new Optional(true, value);
|
||||
}
|
||||
static none() {
|
||||
return Optional.singletonNone;
|
||||
}
|
||||
fold(onNone, onSome) {
|
||||
if (this.tag) {
|
||||
return onSome(this.value);
|
||||
} else {
|
||||
return onNone();
|
||||
}
|
||||
}
|
||||
isSome() {
|
||||
return this.tag;
|
||||
}
|
||||
isNone() {
|
||||
return !this.tag;
|
||||
}
|
||||
map(mapper) {
|
||||
if (this.tag) {
|
||||
return Optional.some(mapper(this.value));
|
||||
} else {
|
||||
return Optional.none();
|
||||
}
|
||||
}
|
||||
bind(binder) {
|
||||
if (this.tag) {
|
||||
return binder(this.value);
|
||||
} else {
|
||||
return Optional.none();
|
||||
}
|
||||
}
|
||||
exists(predicate) {
|
||||
return this.tag && predicate(this.value);
|
||||
}
|
||||
forall(predicate) {
|
||||
return !this.tag || predicate(this.value);
|
||||
}
|
||||
filter(predicate) {
|
||||
if (!this.tag || predicate(this.value)) {
|
||||
return this;
|
||||
} else {
|
||||
return Optional.none();
|
||||
}
|
||||
}
|
||||
getOr(replacement) {
|
||||
return this.tag ? this.value : replacement;
|
||||
}
|
||||
or(replacement) {
|
||||
return this.tag ? this : replacement;
|
||||
}
|
||||
getOrThunk(thunk) {
|
||||
return this.tag ? this.value : thunk();
|
||||
}
|
||||
orThunk(thunk) {
|
||||
return this.tag ? this : thunk();
|
||||
}
|
||||
getOrDie(message) {
|
||||
if (!this.tag) {
|
||||
throw new Error(message ?? 'Called getOrDie on None');
|
||||
} else {
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
static from(value) {
|
||||
return isNonNullable(value) ? Optional.some(value) : Optional.none();
|
||||
}
|
||||
getOrNull() {
|
||||
return this.tag ? this.value : null;
|
||||
}
|
||||
getOrUndefined() {
|
||||
return this.value;
|
||||
}
|
||||
each(worker) {
|
||||
if (this.tag) {
|
||||
worker(this.value);
|
||||
}
|
||||
}
|
||||
toArray() {
|
||||
return this.tag ? [this.value] : [];
|
||||
}
|
||||
toString() {
|
||||
return this.tag ? `some(${ this.value })` : 'none()';
|
||||
}
|
||||
}
|
||||
Optional.singletonNone = new Optional(false);
|
||||
|
||||
const map = (xs, f) => {
|
||||
const len = xs.length;
|
||||
const r = new Array(len);
|
||||
for (let i = 0; i < len; i++) {
|
||||
const x = xs[i];
|
||||
r[i] = f(x, i);
|
||||
}
|
||||
return r;
|
||||
};
|
||||
const each$1 = (xs, f) => {
|
||||
for (let i = 0, len = xs.length; i < len; i++) {
|
||||
const x = xs[i];
|
||||
f(x, i);
|
||||
}
|
||||
};
|
||||
const filter = (xs, pred) => {
|
||||
const r = [];
|
||||
for (let i = 0, len = xs.length; i < len; i++) {
|
||||
const x = xs[i];
|
||||
if (pred(x, i)) {
|
||||
r.push(x);
|
||||
}
|
||||
}
|
||||
return r;
|
||||
};
|
||||
|
||||
const keys = Object.keys;
|
||||
const each = (obj, f) => {
|
||||
const props = keys(obj);
|
||||
for (let k = 0, len = props.length; k < len; k++) {
|
||||
const i = props[k];
|
||||
const x = obj[i];
|
||||
f(x, i);
|
||||
}
|
||||
};
|
||||
|
||||
typeof window !== 'undefined' ? window : Function('return this;')();
|
||||
|
||||
const TEXT = 3;
|
||||
|
||||
const type = element => element.dom.nodeType;
|
||||
const value = element => element.dom.nodeValue;
|
||||
const isType = t => element => type(element) === t;
|
||||
const isText = isType(TEXT);
|
||||
|
||||
const rawSet = (dom, key, value) => {
|
||||
if (isString(value) || isBoolean(value) || isNumber(value)) {
|
||||
dom.setAttribute(key, value + '');
|
||||
} else {
|
||||
console.error('Invalid call to Attribute.set. Key ', key, ':: Value ', value, ':: Element ', dom);
|
||||
throw new Error('Attribute value was not simple');
|
||||
}
|
||||
};
|
||||
const set = (element, key, value) => {
|
||||
rawSet(element.dom, key, value);
|
||||
};
|
||||
const get$1 = (element, key) => {
|
||||
const v = element.dom.getAttribute(key);
|
||||
return v === null ? undefined : v;
|
||||
};
|
||||
const remove$3 = (element, key) => {
|
||||
element.dom.removeAttribute(key);
|
||||
};
|
||||
|
||||
const read = (element, attr) => {
|
||||
const value = get$1(element, attr);
|
||||
return value === undefined || value === '' ? [] : value.split(' ');
|
||||
};
|
||||
const add$2 = (element, attr, id) => {
|
||||
const old = read(element, attr);
|
||||
const nu = old.concat([id]);
|
||||
set(element, attr, nu.join(' '));
|
||||
return true;
|
||||
};
|
||||
const remove$2 = (element, attr, id) => {
|
||||
const nu = filter(read(element, attr), v => v !== id);
|
||||
if (nu.length > 0) {
|
||||
set(element, attr, nu.join(' '));
|
||||
} else {
|
||||
remove$3(element, attr);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const supports = element => element.dom.classList !== undefined;
|
||||
const get = element => read(element, 'class');
|
||||
const add$1 = (element, clazz) => add$2(element, 'class', clazz);
|
||||
const remove$1 = (element, clazz) => remove$2(element, 'class', clazz);
|
||||
|
||||
const add = (element, clazz) => {
|
||||
if (supports(element)) {
|
||||
element.dom.classList.add(clazz);
|
||||
} else {
|
||||
add$1(element, clazz);
|
||||
}
|
||||
};
|
||||
const cleanClass = element => {
|
||||
const classList = supports(element) ? element.dom.classList : get(element);
|
||||
if (classList.length === 0) {
|
||||
remove$3(element, 'class');
|
||||
}
|
||||
};
|
||||
const remove = (element, clazz) => {
|
||||
if (supports(element)) {
|
||||
const classList = element.dom.classList;
|
||||
classList.remove(clazz);
|
||||
} else {
|
||||
remove$1(element, clazz);
|
||||
}
|
||||
cleanClass(element);
|
||||
};
|
||||
|
||||
const fromHtml = (html, scope) => {
|
||||
const doc = scope || document;
|
||||
const div = doc.createElement('div');
|
||||
div.innerHTML = html;
|
||||
if (!div.hasChildNodes() || div.childNodes.length > 1) {
|
||||
const message = 'HTML does not have a single root node';
|
||||
console.error(message, html);
|
||||
throw new Error(message);
|
||||
}
|
||||
return fromDom(div.childNodes[0]);
|
||||
};
|
||||
const fromTag = (tag, scope) => {
|
||||
const doc = scope || document;
|
||||
const node = doc.createElement(tag);
|
||||
return fromDom(node);
|
||||
};
|
||||
const fromText = (text, scope) => {
|
||||
const doc = scope || document;
|
||||
const node = doc.createTextNode(text);
|
||||
return fromDom(node);
|
||||
};
|
||||
const fromDom = node => {
|
||||
if (node === null || node === undefined) {
|
||||
throw new Error('Node cannot be null or undefined');
|
||||
}
|
||||
return { dom: node };
|
||||
};
|
||||
const fromPoint = (docElm, x, y) => Optional.from(docElm.dom.elementFromPoint(x, y)).map(fromDom);
|
||||
const SugarElement = {
|
||||
fromHtml,
|
||||
fromTag,
|
||||
fromText,
|
||||
fromDom,
|
||||
fromPoint
|
||||
};
|
||||
|
||||
const charMap = {
|
||||
'\xA0': 'nbsp',
|
||||
'\xAD': 'shy'
|
||||
};
|
||||
const charMapToRegExp = (charMap, global) => {
|
||||
let regExp = '';
|
||||
each(charMap, (_value, key) => {
|
||||
regExp += key;
|
||||
});
|
||||
return new RegExp('[' + regExp + ']', global ? 'g' : '');
|
||||
};
|
||||
const charMapToSelector = charMap => {
|
||||
let selector = '';
|
||||
each(charMap, value => {
|
||||
if (selector) {
|
||||
selector += ',';
|
||||
}
|
||||
selector += 'span.mce-' + value;
|
||||
});
|
||||
return selector;
|
||||
};
|
||||
const regExp = charMapToRegExp(charMap);
|
||||
const regExpGlobal = charMapToRegExp(charMap, true);
|
||||
const selector = charMapToSelector(charMap);
|
||||
const nbspClass = 'mce-nbsp';
|
||||
|
||||
const wrapCharWithSpan = value => '<span data-mce-bogus="1" class="mce-' + charMap[value] + '">' + value + '</span>';
|
||||
|
||||
const isMatch = n => {
|
||||
const value$1 = value(n);
|
||||
return isText(n) && isString(value$1) && regExp.test(value$1);
|
||||
};
|
||||
const filterDescendants = (scope, predicate) => {
|
||||
let result = [];
|
||||
const dom = scope.dom;
|
||||
const children = map(dom.childNodes, SugarElement.fromDom);
|
||||
each$1(children, x => {
|
||||
if (predicate(x)) {
|
||||
result = result.concat([x]);
|
||||
}
|
||||
result = result.concat(filterDescendants(x, predicate));
|
||||
});
|
||||
return result;
|
||||
};
|
||||
const findParentElm = (elm, rootElm) => {
|
||||
while (elm.parentNode) {
|
||||
if (elm.parentNode === rootElm) {
|
||||
return rootElm;
|
||||
}
|
||||
elm = elm.parentNode;
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
const replaceWithSpans = text => text.replace(regExpGlobal, wrapCharWithSpan);
|
||||
|
||||
const isWrappedNbsp = node => node.nodeName.toLowerCase() === 'span' && node.classList.contains('mce-nbsp-wrap');
|
||||
const show = (editor, rootElm) => {
|
||||
const dom = editor.dom;
|
||||
const nodeList = filterDescendants(SugarElement.fromDom(rootElm), isMatch);
|
||||
each$1(nodeList, n => {
|
||||
const parent = n.dom.parentNode;
|
||||
if (isWrappedNbsp(parent)) {
|
||||
add(SugarElement.fromDom(parent), nbspClass);
|
||||
} else {
|
||||
const withSpans = replaceWithSpans(dom.encode(value(n) ?? ''));
|
||||
const div = dom.create('div', {}, withSpans);
|
||||
let node;
|
||||
while (node = div.lastChild) {
|
||||
dom.insertAfter(node, n.dom);
|
||||
}
|
||||
editor.dom.remove(n.dom);
|
||||
}
|
||||
});
|
||||
};
|
||||
const hide = (editor, rootElm) => {
|
||||
const nodeList = editor.dom.select(selector, rootElm);
|
||||
each$1(nodeList, node => {
|
||||
if (isWrappedNbsp(node)) {
|
||||
remove(SugarElement.fromDom(node), nbspClass);
|
||||
} else {
|
||||
editor.dom.remove(node, true);
|
||||
}
|
||||
});
|
||||
};
|
||||
const toggle = editor => {
|
||||
const body = editor.getBody();
|
||||
const bookmark = editor.selection.getBookmark();
|
||||
let parentNode = findParentElm(editor.selection.getNode(), body);
|
||||
parentNode = parentNode !== undefined ? parentNode : body;
|
||||
hide(editor, parentNode);
|
||||
show(editor, parentNode);
|
||||
editor.selection.moveToBookmark(bookmark);
|
||||
};
|
||||
|
||||
const applyVisualChars = (editor, toggleState) => {
|
||||
fireVisualChars(editor, toggleState.get());
|
||||
const body = editor.getBody();
|
||||
if (toggleState.get() === true) {
|
||||
show(editor, body);
|
||||
} else {
|
||||
hide(editor, body);
|
||||
}
|
||||
};
|
||||
const toggleVisualChars = (editor, toggleState) => {
|
||||
toggleState.set(!toggleState.get());
|
||||
const bookmark = editor.selection.getBookmark();
|
||||
applyVisualChars(editor, toggleState);
|
||||
editor.selection.moveToBookmark(bookmark);
|
||||
};
|
||||
|
||||
const register$2 = (editor, toggleState) => {
|
||||
editor.addCommand('mceVisualChars', () => {
|
||||
toggleVisualChars(editor, toggleState);
|
||||
});
|
||||
};
|
||||
|
||||
const option = name => editor => editor.options.get(name);
|
||||
const register$1 = editor => {
|
||||
const registerOption = editor.options.register;
|
||||
registerOption('visualchars_default_state', {
|
||||
processor: 'boolean',
|
||||
default: false
|
||||
});
|
||||
};
|
||||
const isEnabledByDefault = option('visualchars_default_state');
|
||||
|
||||
const setup$1 = (editor, toggleState) => {
|
||||
editor.on('init', () => {
|
||||
applyVisualChars(editor, toggleState);
|
||||
});
|
||||
};
|
||||
|
||||
const first = (fn, rate) => {
|
||||
let timer = null;
|
||||
const cancel = () => {
|
||||
if (!isNull(timer)) {
|
||||
clearTimeout(timer);
|
||||
timer = null;
|
||||
}
|
||||
};
|
||||
const throttle = (...args) => {
|
||||
if (isNull(timer)) {
|
||||
timer = setTimeout(() => {
|
||||
timer = null;
|
||||
fn.apply(null, args);
|
||||
}, rate);
|
||||
}
|
||||
};
|
||||
return {
|
||||
cancel,
|
||||
throttle
|
||||
};
|
||||
};
|
||||
|
||||
const setup = (editor, toggleState) => {
|
||||
const debouncedToggle = first(() => {
|
||||
toggle(editor);
|
||||
}, 300);
|
||||
editor.on('keydown', e => {
|
||||
if (toggleState.get() === true) {
|
||||
e.keyCode === 13 ? toggle(editor) : debouncedToggle.throttle();
|
||||
}
|
||||
});
|
||||
editor.on('remove', debouncedToggle.cancel);
|
||||
};
|
||||
|
||||
const toggleActiveState = (editor, enabledStated) => api => {
|
||||
api.setActive(enabledStated.get());
|
||||
const editorEventCallback = e => api.setActive(e.state);
|
||||
editor.on('VisualChars', editorEventCallback);
|
||||
return () => editor.off('VisualChars', editorEventCallback);
|
||||
};
|
||||
const register = (editor, toggleState) => {
|
||||
const onAction = () => editor.execCommand('mceVisualChars');
|
||||
editor.ui.registry.addToggleButton('visualchars', {
|
||||
tooltip: 'Show invisible characters',
|
||||
icon: 'visualchars',
|
||||
onAction,
|
||||
onSetup: toggleActiveState(editor, toggleState)
|
||||
});
|
||||
editor.ui.registry.addToggleMenuItem('visualchars', {
|
||||
text: 'Show invisible characters',
|
||||
icon: 'visualchars',
|
||||
onAction,
|
||||
onSetup: toggleActiveState(editor, toggleState)
|
||||
});
|
||||
};
|
||||
|
||||
var Plugin = () => {
|
||||
global.add('visualchars', editor => {
|
||||
register$1(editor);
|
||||
const toggleState = Cell(isEnabledByDefault(editor));
|
||||
register$2(editor, toggleState);
|
||||
register(editor, toggleState);
|
||||
setup(editor, toggleState);
|
||||
setup$1(editor, toggleState);
|
||||
return get$2(toggleState);
|
||||
});
|
||||
};
|
||||
|
||||
Plugin();
|
||||
|
||||
})();
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,405 @@
|
||||
/**
|
||||
* TinyMCE version 6.2.0 (2022-09-08)
|
||||
*/
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
var global$2 = tinymce.util.Tools.resolve('tinymce.PluginManager');
|
||||
|
||||
const eq = t => a => t === a;
|
||||
const isNull = eq(null);
|
||||
|
||||
const identity = x => {
|
||||
return x;
|
||||
};
|
||||
|
||||
const zeroWidth = '\uFEFF';
|
||||
const removeZwsp$1 = s => s.replace(/\uFEFF/g, '');
|
||||
|
||||
const map = (xs, f) => {
|
||||
const len = xs.length;
|
||||
const r = new Array(len);
|
||||
for (let i = 0; i < len; i++) {
|
||||
const x = xs[i];
|
||||
r[i] = f(x, i);
|
||||
}
|
||||
return r;
|
||||
};
|
||||
|
||||
const punctuationStr = '[!-#%-*,-\\/:;?@\\[-\\]_{}\xA1\xAB\xB7\xBB\xBF;\xB7\u055A-\u055F\u0589\u058A\u05BE\u05C0\u05C3\u05C6\u05F3\u05F4\u0609\u060A\u060C\u060D\u061B\u061E\u061F\u066A-\u066D\u06D4\u0700-\u070D\u07F7-\u07F9\u0830-\u083E\u085E\u0964\u0965\u0970\u0DF4\u0E4F\u0E5A\u0E5B\u0F04-\u0F12\u0F3A-\u0F3D\u0F85\u0FD0-\u0FD4\u0FD9\u0FDA\u104A-\u104F\u10FB\u1361-\u1368\u1400\u166D\u166E\u169B\u169C\u16EB-\u16ED\u1735\u1736\u17D4-\u17D6\u17D8-\u17DA\u1800-\u180A\u1944\u1945\u1A1E\u1A1F\u1AA0-\u1AA6\u1AA8-\u1AAD\u1B5A-\u1B60\u1BFC-\u1BFF\u1C3B-\u1C3F\u1C7E\u1C7F\u1CD3\u2010-\u2027\u2030-\u2043\u2045-\u2051\u2053-\u205E\u207D\u207E\u208D\u208E\u3008\u3009\u2768-\u2775\u27C5\u27C6\u27E6-\u27EF\u2983-\u2998\u29D8-\u29DB\u29FC\u29FD\u2CF9-\u2CFC\u2CFE\u2CFF\u2D70\u2E00-\u2E2E\u2E30\u2E31\u3001-\u3003\u3008-\u3011\u3014-\u301F\u3030\u303D\u30A0\u30FB\uA4FE\uA4FF\uA60D-\uA60F\uA673\uA67E\uA6F2-\uA6F7\uA874-\uA877\uA8CE\uA8CF\uA8F8-\uA8FA\uA92E\uA92F\uA95F\uA9C1-\uA9CD\uA9DE\uA9DF\uAA5C-\uAA5F\uAADE\uAADF\uABEB\uFD3E\uFD3F\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE61\uFE63\uFE68\uFE6A\uFE6B\uFF01-\uFF03\uFF05-\uFF0A\uFF0C-\uFF0F\uFF1A\uFF1B\uFF1F\uFF20\uFF3B-\uFF3D\uff3f\uFF5B\uFF5D\uFF5F-\uFF65]';
|
||||
const regExps = {
|
||||
aletter: '[A-Za-z\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05d0-\u05ea\u05f0-\u05F3\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097f\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d\u0c58\u0c59\u0c60\u0c61\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d60\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u10a0-\u10c5\u10d0-\u10fa\u10fc\u1100-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191c\u1a00-\u1a16\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bc0-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1ce9-\u1cec\u1cee-\u1cf1\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u24B6-\u24E9\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2d00-\u2d25\u2d30-\u2d65\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2e2f\u3005\u303b\u303c\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua697\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790\ua791\ua7a0-\ua7a9\ua7fa-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uffa0-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc]',
|
||||
midnumlet: `[-'\\.\u2018\u2019\u2024\uFE52\uFF07\uFF0E]`,
|
||||
midletter: '[:\xB7\xB7\u05F4\u2027\uFE13\uFE55\uFF1A]',
|
||||
midnum: '[\xB1+*/,;;\u0589\u060C\u060D\u066C\u07F8\u2044\uFE10\uFE14\uFE50\uFE54\uFF0C\uFF1B]',
|
||||
numeric: '[0-9\u0660-\u0669\u066B\u06f0-\u06f9\u07c0-\u07c9\u0966-\u096f\u09e6-\u09ef\u0a66-\u0a6f\u0ae6-\u0aef\u0b66-\u0b6f\u0be6-\u0bef\u0c66-\u0c6f\u0ce6-\u0cef\u0d66-\u0d6f\u0e50-\u0e59\u0ed0-\u0ed9\u0f20-\u0f29\u1040-\u1049\u1090-\u1099\u17e0-\u17e9\u1810-\u1819\u1946-\u194f\u19d0-\u19d9\u1a80-\u1a89\u1a90-\u1a99\u1b50-\u1b59\u1bb0-\u1bb9\u1c40-\u1c49\u1c50-\u1c59\ua620-\ua629\ua8d0-\ua8d9\ua900-\ua909\ua9d0-\ua9d9\uaa50-\uaa59\uabf0-\uabf9]',
|
||||
cr: '\\r',
|
||||
lf: '\\n',
|
||||
newline: '[\x0B\f\x85\u2028\u2029]',
|
||||
extend: '[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065f\u0670\u06d6-\u06dc\u06df-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0859-\u085b\u0900-\u0903\u093a-\u093c\u093e-\u094f\u0951-\u0957\u0962\u0963\u0981-\u0983\u09bc\u09be-\u09c4\u09c7\u09c8\u09cb-\u09cd\u09d7\u09e2\u09e3\u0a01-\u0a03\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81-\u0a83\u0abc\u0abe-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ae2\u0ae3\u0b01-\u0b03\u0b3c\u0b3e-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd7\u0c01-\u0c03\u0c3e-\u0c44\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0c82\u0c83\u0cbc\u0cbe-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d02\u0d03\u0d3e-\u0d44\u0d46-\u0d48\u0d4a-\u0d4d\u0d57\u0d62\u0d63\u0d82\u0d83\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0df2\u0df3\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f3e\u0f3f\u0f71-\u0f84\u0f86\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u102b-\u103e\u1056-\u1059\u105e-\u1060\u1062-\u1064\u1067-\u106d\u1071-\u1074\u1082-\u108d\u108f\u109a-\u109d\u135d-\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b6-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u192b\u1930-\u193b\u19b0-\u19c0\u19c8\u19c9\u1a17-\u1a1b\u1a55-\u1a5e\u1a60-\u1a7c\u1a7f\u1b00-\u1b04\u1b34-\u1b44\u1b6b-\u1b73\u1b80-\u1b82\u1ba1-\u1baa\u1be6-\u1bf3\u1c24-\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce8\u1ced\u1cf2\u1dc0-\u1de6\u1dfc-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2d7f\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\uA672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua823-\ua827\ua880\ua881\ua8b4-\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua953\ua980-\ua983\ua9b3-\ua9c0\uaa29-\uaa36\uaa43\uaa4c\uaa4d\uaa7b\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe3-\uabea\uabec\uabed\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]',
|
||||
format: '[\xAD\u0600-\u0603\u06DD\u070F\u17b4\u17b5\u200E\u200F\u202A-\u202E\u2060-\u2064\u206A-\u206F\uFEFF\uFFF9-\uFFFB]',
|
||||
katakana: '[\u3031-\u3035\u309B\u309C\u30A0-\u30fa\u30fc-\u30ff\u31f0-\u31ff\u32D0-\u32FE\u3300-\u3357\uff66-\uff9d]',
|
||||
extendnumlet: '[=_\u203f\u2040\u2054\ufe33\ufe34\ufe4d-\ufe4f\uff3f\u2200-\u22FF<>]',
|
||||
punctuation: punctuationStr
|
||||
};
|
||||
const characterIndices = {
|
||||
ALETTER: 0,
|
||||
MIDNUMLET: 1,
|
||||
MIDLETTER: 2,
|
||||
MIDNUM: 3,
|
||||
NUMERIC: 4,
|
||||
CR: 5,
|
||||
LF: 6,
|
||||
NEWLINE: 7,
|
||||
EXTEND: 8,
|
||||
FORMAT: 9,
|
||||
KATAKANA: 10,
|
||||
EXTENDNUMLET: 11,
|
||||
AT: 12,
|
||||
OTHER: 13
|
||||
};
|
||||
const SETS$1 = [
|
||||
new RegExp(regExps.aletter),
|
||||
new RegExp(regExps.midnumlet),
|
||||
new RegExp(regExps.midletter),
|
||||
new RegExp(regExps.midnum),
|
||||
new RegExp(regExps.numeric),
|
||||
new RegExp(regExps.cr),
|
||||
new RegExp(regExps.lf),
|
||||
new RegExp(regExps.newline),
|
||||
new RegExp(regExps.extend),
|
||||
new RegExp(regExps.format),
|
||||
new RegExp(regExps.katakana),
|
||||
new RegExp(regExps.extendnumlet),
|
||||
new RegExp('@')
|
||||
];
|
||||
const EMPTY_STRING$1 = '';
|
||||
const PUNCTUATION$1 = new RegExp('^' + regExps.punctuation + '$');
|
||||
const WHITESPACE$1 = /^\s+$/;
|
||||
|
||||
const SETS = SETS$1;
|
||||
const OTHER = characterIndices.OTHER;
|
||||
const getType = char => {
|
||||
let type = OTHER;
|
||||
const setsLength = SETS.length;
|
||||
for (let j = 0; j < setsLength; ++j) {
|
||||
const set = SETS[j];
|
||||
if (set && set.test(char)) {
|
||||
type = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return type;
|
||||
};
|
||||
const memoize = func => {
|
||||
const cache = {};
|
||||
return char => {
|
||||
if (cache[char]) {
|
||||
return cache[char];
|
||||
} else {
|
||||
const result = func(char);
|
||||
cache[char] = result;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
};
|
||||
const classify = characters => {
|
||||
const memoized = memoize(getType);
|
||||
return map(characters, memoized);
|
||||
};
|
||||
|
||||
const isWordBoundary = (map, index) => {
|
||||
const type = map[index];
|
||||
const nextType = map[index + 1];
|
||||
if (index < 0 || index > map.length - 1 && index !== 0) {
|
||||
return false;
|
||||
}
|
||||
if (type === characterIndices.ALETTER && nextType === characterIndices.ALETTER) {
|
||||
return false;
|
||||
}
|
||||
const nextNextType = map[index + 2];
|
||||
if (type === characterIndices.ALETTER && (nextType === characterIndices.MIDLETTER || nextType === characterIndices.MIDNUMLET || nextType === characterIndices.AT) && nextNextType === characterIndices.ALETTER) {
|
||||
return false;
|
||||
}
|
||||
const prevType = map[index - 1];
|
||||
if ((type === characterIndices.MIDLETTER || type === characterIndices.MIDNUMLET || nextType === characterIndices.AT) && nextType === characterIndices.ALETTER && prevType === characterIndices.ALETTER) {
|
||||
return false;
|
||||
}
|
||||
if ((type === characterIndices.NUMERIC || type === characterIndices.ALETTER) && (nextType === characterIndices.NUMERIC || nextType === characterIndices.ALETTER)) {
|
||||
return false;
|
||||
}
|
||||
if ((type === characterIndices.MIDNUM || type === characterIndices.MIDNUMLET) && nextType === characterIndices.NUMERIC && prevType === characterIndices.NUMERIC) {
|
||||
return false;
|
||||
}
|
||||
if (type === characterIndices.NUMERIC && (nextType === characterIndices.MIDNUM || nextType === characterIndices.MIDNUMLET) && nextNextType === characterIndices.NUMERIC) {
|
||||
return false;
|
||||
}
|
||||
if (type === characterIndices.EXTEND || type === characterIndices.FORMAT || prevType === characterIndices.EXTEND || prevType === characterIndices.FORMAT || nextType === characterIndices.EXTEND || nextType === characterIndices.FORMAT) {
|
||||
return false;
|
||||
}
|
||||
if (type === characterIndices.CR && nextType === characterIndices.LF) {
|
||||
return false;
|
||||
}
|
||||
if (type === characterIndices.NEWLINE || type === characterIndices.CR || type === characterIndices.LF) {
|
||||
return true;
|
||||
}
|
||||
if (nextType === characterIndices.NEWLINE || nextType === characterIndices.CR || nextType === characterIndices.LF) {
|
||||
return true;
|
||||
}
|
||||
if (type === characterIndices.KATAKANA && nextType === characterIndices.KATAKANA) {
|
||||
return false;
|
||||
}
|
||||
if (nextType === characterIndices.EXTENDNUMLET && (type === characterIndices.ALETTER || type === characterIndices.NUMERIC || type === characterIndices.KATAKANA || type === characterIndices.EXTENDNUMLET)) {
|
||||
return false;
|
||||
}
|
||||
if (type === characterIndices.EXTENDNUMLET && (nextType === characterIndices.ALETTER || nextType === characterIndices.NUMERIC || nextType === characterIndices.KATAKANA)) {
|
||||
return false;
|
||||
}
|
||||
if (type === characterIndices.AT) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
const EMPTY_STRING = EMPTY_STRING$1;
|
||||
const WHITESPACE = WHITESPACE$1;
|
||||
const PUNCTUATION = PUNCTUATION$1;
|
||||
const isProtocol = str => str === 'http' || str === 'https';
|
||||
const findWordEnd = (characters, startIndex) => {
|
||||
let i;
|
||||
for (i = startIndex; i < characters.length; i++) {
|
||||
if (WHITESPACE.test(characters[i])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
};
|
||||
const findUrlEnd = (characters, startIndex) => {
|
||||
const endIndex = findWordEnd(characters, startIndex + 1);
|
||||
const peakedWord = characters.slice(startIndex + 1, endIndex).join(EMPTY_STRING);
|
||||
return peakedWord.substr(0, 3) === '://' ? endIndex : startIndex;
|
||||
};
|
||||
const findWords = (chars, sChars, characterMap, options) => {
|
||||
const words = [];
|
||||
let word = [];
|
||||
for (let i = 0; i < characterMap.length; ++i) {
|
||||
word.push(chars[i]);
|
||||
if (isWordBoundary(characterMap, i)) {
|
||||
const ch = sChars[i];
|
||||
if ((options.includeWhitespace || !WHITESPACE.test(ch)) && (options.includePunctuation || !PUNCTUATION.test(ch))) {
|
||||
const startOfWord = i - word.length + 1;
|
||||
const endOfWord = i + 1;
|
||||
const str = sChars.slice(startOfWord, endOfWord).join(EMPTY_STRING);
|
||||
if (isProtocol(str)) {
|
||||
const endOfUrl = findUrlEnd(sChars, i);
|
||||
const url = chars.slice(endOfWord, endOfUrl);
|
||||
Array.prototype.push.apply(word, url);
|
||||
i = endOfUrl;
|
||||
}
|
||||
words.push(word);
|
||||
}
|
||||
word = [];
|
||||
}
|
||||
}
|
||||
return words;
|
||||
};
|
||||
const getDefaultOptions = () => ({
|
||||
includeWhitespace: false,
|
||||
includePunctuation: false
|
||||
});
|
||||
const getWords$1 = (chars, extract, options) => {
|
||||
options = {
|
||||
...getDefaultOptions(),
|
||||
...options
|
||||
};
|
||||
const filteredChars = [];
|
||||
const extractedChars = [];
|
||||
for (let i = 0; i < chars.length; i++) {
|
||||
const ch = extract(chars[i]);
|
||||
if (ch !== zeroWidth) {
|
||||
filteredChars.push(chars[i]);
|
||||
extractedChars.push(ch);
|
||||
}
|
||||
}
|
||||
const characterMap = classify(extractedChars);
|
||||
return findWords(filteredChars, extractedChars, characterMap, options);
|
||||
};
|
||||
|
||||
const getWords = getWords$1;
|
||||
|
||||
var global$1 = tinymce.util.Tools.resolve('tinymce.dom.TreeWalker');
|
||||
|
||||
const getText = (node, schema) => {
|
||||
const blockElements = schema.getBlockElements();
|
||||
const voidElements = schema.getVoidElements();
|
||||
const isNewline = node => blockElements[node.nodeName] || voidElements[node.nodeName];
|
||||
const textBlocks = [];
|
||||
let txt = '';
|
||||
const treeWalker = new global$1(node, node);
|
||||
let tempNode;
|
||||
while (tempNode = treeWalker.next()) {
|
||||
if (tempNode.nodeType === 3) {
|
||||
txt += removeZwsp$1(tempNode.data);
|
||||
} else if (isNewline(tempNode) && txt.length) {
|
||||
textBlocks.push(txt);
|
||||
txt = '';
|
||||
}
|
||||
}
|
||||
if (txt.length) {
|
||||
textBlocks.push(txt);
|
||||
}
|
||||
return textBlocks;
|
||||
};
|
||||
|
||||
const removeZwsp = text => text.replace(/\u200B/g, '');
|
||||
const strLen = str => str.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g, '_').length;
|
||||
const countWords = (node, schema) => {
|
||||
const text = removeZwsp(getText(node, schema).join('\n'));
|
||||
return getWords(text.split(''), identity).length;
|
||||
};
|
||||
const countCharacters = (node, schema) => {
|
||||
const text = getText(node, schema).join('');
|
||||
return strLen(text);
|
||||
};
|
||||
const countCharactersWithoutSpaces = (node, schema) => {
|
||||
const text = getText(node, schema).join('').replace(/\s/g, '');
|
||||
return strLen(text);
|
||||
};
|
||||
|
||||
const createBodyCounter = (editor, count) => () => count(editor.getBody(), editor.schema);
|
||||
const createSelectionCounter = (editor, count) => () => count(editor.selection.getRng().cloneContents(), editor.schema);
|
||||
const createBodyWordCounter = editor => createBodyCounter(editor, countWords);
|
||||
const get = editor => ({
|
||||
body: {
|
||||
getWordCount: createBodyWordCounter(editor),
|
||||
getCharacterCount: createBodyCounter(editor, countCharacters),
|
||||
getCharacterCountWithoutSpaces: createBodyCounter(editor, countCharactersWithoutSpaces)
|
||||
},
|
||||
selection: {
|
||||
getWordCount: createSelectionCounter(editor, countWords),
|
||||
getCharacterCount: createSelectionCounter(editor, countCharacters),
|
||||
getCharacterCountWithoutSpaces: createSelectionCounter(editor, countCharactersWithoutSpaces)
|
||||
},
|
||||
getCount: createBodyWordCounter(editor)
|
||||
});
|
||||
|
||||
const open = (editor, api) => {
|
||||
editor.windowManager.open({
|
||||
title: 'Word Count',
|
||||
body: {
|
||||
type: 'panel',
|
||||
items: [{
|
||||
type: 'table',
|
||||
header: [
|
||||
'Count',
|
||||
'Document',
|
||||
'Selection'
|
||||
],
|
||||
cells: [
|
||||
[
|
||||
'Words',
|
||||
String(api.body.getWordCount()),
|
||||
String(api.selection.getWordCount())
|
||||
],
|
||||
[
|
||||
'Characters (no spaces)',
|
||||
String(api.body.getCharacterCountWithoutSpaces()),
|
||||
String(api.selection.getCharacterCountWithoutSpaces())
|
||||
],
|
||||
[
|
||||
'Characters',
|
||||
String(api.body.getCharacterCount()),
|
||||
String(api.selection.getCharacterCount())
|
||||
]
|
||||
]
|
||||
}]
|
||||
},
|
||||
buttons: [{
|
||||
type: 'cancel',
|
||||
name: 'close',
|
||||
text: 'Close',
|
||||
primary: true
|
||||
}]
|
||||
});
|
||||
};
|
||||
|
||||
const register$1 = (editor, api) => {
|
||||
editor.addCommand('mceWordCount', () => open(editor, api));
|
||||
};
|
||||
|
||||
const first = (fn, rate) => {
|
||||
let timer = null;
|
||||
const cancel = () => {
|
||||
if (!isNull(timer)) {
|
||||
clearTimeout(timer);
|
||||
timer = null;
|
||||
}
|
||||
};
|
||||
const throttle = (...args) => {
|
||||
if (isNull(timer)) {
|
||||
timer = setTimeout(() => {
|
||||
timer = null;
|
||||
fn.apply(null, args);
|
||||
}, rate);
|
||||
}
|
||||
};
|
||||
return {
|
||||
cancel,
|
||||
throttle
|
||||
};
|
||||
};
|
||||
|
||||
var global = tinymce.util.Tools.resolve('tinymce.util.Delay');
|
||||
|
||||
const fireWordCountUpdate = (editor, api) => {
|
||||
editor.dispatch('wordCountUpdate', {
|
||||
wordCount: {
|
||||
words: api.body.getWordCount(),
|
||||
characters: api.body.getCharacterCount(),
|
||||
charactersWithoutSpaces: api.body.getCharacterCountWithoutSpaces()
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const updateCount = (editor, api) => {
|
||||
fireWordCountUpdate(editor, api);
|
||||
};
|
||||
const setup = (editor, api, delay) => {
|
||||
const debouncedUpdate = first(() => updateCount(editor, api), delay);
|
||||
editor.on('init', () => {
|
||||
updateCount(editor, api);
|
||||
global.setEditorTimeout(editor, () => {
|
||||
editor.on('SetContent BeforeAddUndo Undo Redo ViewUpdate keyup', debouncedUpdate.throttle);
|
||||
}, 0);
|
||||
editor.on('remove', debouncedUpdate.cancel);
|
||||
});
|
||||
};
|
||||
|
||||
const register = editor => {
|
||||
const onAction = () => editor.execCommand('mceWordCount');
|
||||
editor.ui.registry.addButton('wordcount', {
|
||||
tooltip: 'Word count',
|
||||
icon: 'character-count',
|
||||
onAction
|
||||
});
|
||||
editor.ui.registry.addMenuItem('wordcount', {
|
||||
text: 'Word count',
|
||||
icon: 'character-count',
|
||||
onAction
|
||||
});
|
||||
};
|
||||
|
||||
var Plugin = (delay = 300) => {
|
||||
global$2.add('wordcount', editor => {
|
||||
const api = get(editor);
|
||||
register$1(editor, api);
|
||||
register(editor);
|
||||
setup(editor, api, delay);
|
||||
return api;
|
||||
});
|
||||
};
|
||||
|
||||
Plugin();
|
||||
|
||||
})();
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,66 @@
|
||||
body {
|
||||
background-color: #222f3e;
|
||||
color: #fff;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||
line-height: 1.4;
|
||||
margin: 1rem;
|
||||
}
|
||||
a {
|
||||
color: #4099ff;
|
||||
}
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
/* Apply a default padding if legacy cellpadding attribute is missing */
|
||||
table:not([cellpadding]) th,
|
||||
table:not([cellpadding]) td {
|
||||
padding: 0.4rem;
|
||||
}
|
||||
/* Set default table styles if a table has a positive border attribute
|
||||
and no inline css */
|
||||
table[border]:not([border="0"]):not([style*="border-width"]) th,
|
||||
table[border]:not([border="0"]):not([style*="border-width"]) td {
|
||||
border-width: 1px;
|
||||
}
|
||||
/* Set default table styles if a table has a positive border attribute
|
||||
and no inline css */
|
||||
table[border]:not([border="0"]):not([style*="border-style"]) th,
|
||||
table[border]:not([border="0"]):not([style*="border-style"]) td {
|
||||
border-style: solid;
|
||||
}
|
||||
/* Set default table styles if a table has a positive border attribute
|
||||
and no inline css */
|
||||
table[border]:not([border="0"]):not([style*="border-color"]) th,
|
||||
table[border]:not([border="0"]):not([style*="border-color"]) td {
|
||||
border-color: #6d737b;
|
||||
}
|
||||
figure {
|
||||
display: table;
|
||||
margin: 1rem auto;
|
||||
}
|
||||
figure figcaption {
|
||||
color: #8a8f97;
|
||||
display: block;
|
||||
margin-top: 0.25rem;
|
||||
text-align: center;
|
||||
}
|
||||
hr {
|
||||
border-color: #6d737b;
|
||||
border-style: solid;
|
||||
border-width: 1px 0 0 0;
|
||||
}
|
||||
code {
|
||||
background-color: #6d737b;
|
||||
border-radius: 3px;
|
||||
padding: 0.1rem 0.2rem;
|
||||
}
|
||||
.mce-content-body:not([dir=rtl]) blockquote {
|
||||
border-left: 2px solid #6d737b;
|
||||
margin-left: 1.5rem;
|
||||
padding-left: 1rem;
|
||||
}
|
||||
.mce-content-body[dir=rtl] blockquote {
|
||||
border-right: 2px solid #6d737b;
|
||||
margin-right: 1.5rem;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user