diff --git a/.doclintrc b/.doclintrc new file mode 100644 index 00000000..a6b65270 --- /dev/null +++ b/.doclintrc @@ -0,0 +1 @@ +docs/en/ diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..d633fdac --- /dev/null +++ b/.editorconfig @@ -0,0 +1,31 @@ +# For more information about the properties used in +# this file, please see the EditorConfig documentation: +# http://editorconfig.org/ + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false + +[*.{yml,js,json,css,scss,eslintrc,feature}] +indent_size = 2 +indent_style = space + +# Don't perform any clean-up on thirdparty files + +[client/tinymce_lang/**] +trim_trailing_whitespace = false +insert_final_newline = false + +[client/src/tinymce/**] +trim_trailing_whitespace = false +insert_final_newline = false + +[composer.json] +indent_size = 4 diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 00000000..e50df28e --- /dev/null +++ b/.eslintignore @@ -0,0 +1,9 @@ +# Ignore dist files +client/dist/ + +# Ignore vendor +node_modules/ + +# Ignore language files (auto-generated) +client/lang/ +client/tinymce_lang/ diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 00000000..4b81cffb --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@silverstripe/eslint-config/.eslintrc'); diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..2004851c --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,10 @@ +name: CI + +on: + push: + pull_request: + workflow_dispatch: + +jobs: + ci: + uses: silverstripe/gha-ci/.github/workflows/ci.yml@v1 diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..bed15206 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +.sass-cache +/vendor/ +/node_modules/ +/**/*.js.map +/**/*.css.map +yarn-error.log +composer.lock diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 00000000..3c032078 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +18 diff --git a/.stylelintignore b/.stylelintignore new file mode 100644 index 00000000..9e682ee8 --- /dev/null +++ b/.stylelintignore @@ -0,0 +1 @@ +client/src/tinymce/* diff --git a/.stylelintrc.js b/.stylelintrc.js new file mode 100644 index 00000000..ef7353b7 --- /dev/null +++ b/.stylelintrc.js @@ -0,0 +1 @@ +module.exports = require('@silverstripe/eslint-config/.stylelintrc'); diff --git a/_config.php b/_config.php new file mode 100644 index 00000000..07bce953 --- /dev/null +++ b/_config.php @@ -0,0 +1,64 @@ +setOptions([ + 'friendly_name' => 'Default CMS', + 'priority' => '50', + 'skin' => 'silverstripe', + 'contextmenu' => "searchreplace | sslink anchor ssmedia ssembed inserttable | cell row column deletetable", + 'use_native_selects' => false, + ]); + $editorConfig->insertButtonsAfter('table', 'anchor'); + + // Prepare list of plugins to enable + $moduleManifest = ModuleLoader::inst()->getManifest(); + $module = $moduleManifest->getModule('silverstripe/htmleditor-tinymce'); + $plugins = []; + + // Add link plugins if silverstripe/admin is installed. + // The JS in these relies on some of the admin code e.g. modals. + if ($moduleManifest->moduleExists('silverstripe/admin')) { + $plugins += [ + 'sslink' => $module->getResource('client/dist/js/TinyMCE_sslink.js'), + 'sslinkexternal' => $module->getResource('client/dist/js/TinyMCE_sslink-external.js'), + 'sslinkemail' => $module->getResource('client/dist/js/TinyMCE_sslink-email.js'), + ]; + // Move anchor button to be after the link button + $editorConfig->removeButtons('anchor'); + $editorConfig->insertButtonsAfter('sslink', 'anchor'); + } + + // Add plugins for managing assets if silverstripe/asset-admin is installed + if ($moduleManifest->moduleExists('silverstripe/asset-admin')) { + $plugins += [ + 'ssmedia' => $module->getResource('client/dist/js/TinyMCE_ssmedia.js'), + 'ssembed' => $module->getResource('client/dist/js/TinyMCE_ssembed.js'), + 'sslinkfile' => $module->getResource('client/dist/js/TinyMCE_sslink-file.js'), + ]; + $editorConfig->insertButtonsAfter('table', 'ssmedia'); + $editorConfig->insertButtonsAfter('ssmedia', 'ssembed'); + } + + // Add internal link plugins if silverstripe/cms is installed + if ($moduleManifest->moduleExists('silverstripe/cms')) { + $plugins += [ + 'sslinkinternal' => $module->getResource('client/dist/js/TinyMCE_sslink-internal.js'), + 'sslinkanchor' => $module->getResource('client/dist/js/TinyMCE_sslink-anchor.js'), + ]; + } + + if (!empty($plugins)) { + $editorConfig->enablePlugins($plugins); + } +}); diff --git a/_config/config.yml b/_config/config.yml new file mode 100644 index 00000000..ba156f5e --- /dev/null +++ b/_config/config.yml @@ -0,0 +1,11 @@ +--- +Name: tinymce-config +--- +SilverStripe\Forms\HTMLEditor\HTMLEditorConfig: + default_config_definitions: + cms: + configClass: 'SilverStripe\TinyMCE\TinyMCEConfig' + +SilverStripe\Admin\LeftAndMain: + extra_requirements_javascript: + - 'silverstripe/htmleditor-tinymce: client/dist/js/bundle.js' diff --git a/_config/injector.yml b/_config/injector.yml new file mode 100644 index 00000000..8bbc4f46 --- /dev/null +++ b/_config/injector.yml @@ -0,0 +1,11 @@ +--- +Name: tinymce-injector +--- +SilverStripe\Core\Injector\Injector: + SilverStripe\Forms\HTMLEditor\HTMLEditorConfig: + class: SilverStripe\TinyMCE\TinyMCEConfig + SilverStripe\TinyMCE\TinyMCEScriptGenerator: '%$SilverStripe\TinyMCE\TinyMCECombinedGenerator' + SilverStripe\TinyMCE\TinyMCECombinedGenerator: + class: SilverStripe\TinyMCE\TinyMCECombinedGenerator + properties: + AssetHandler: '%$SilverStripe\Assets\Storage\GeneratedAssetHandler' diff --git a/babel.config.json b/babel.config.json new file mode 100644 index 00000000..4f06b0cd --- /dev/null +++ b/babel.config.json @@ -0,0 +1,6 @@ +{ + "presets": [ + "@babel/preset-env", + "@babel/preset-react" + ] +} diff --git a/behat.yml b/behat.yml new file mode 100644 index 00000000..cce34c5b --- /dev/null +++ b/behat.yml @@ -0,0 +1,32 @@ +# Run TinyMCE behat tests with this command +# ========================================================================= # +# chromedriver +# vendor/bin/behat @htmleditor-tinymce +# ========================================================================= # +default: + suites: + htmleditor-tinymce: + paths: + - '%paths.modules.htmleditor-tinymce%/tests/behat/features' + contexts: + - SilverStripe\Framework\Tests\Behaviour\FeatureContext + - SilverStripe\Framework\Tests\Behaviour\CmsFormsContext + - SilverStripe\Framework\Tests\Behaviour\CmsUiContext + - SilverStripe\BehatExtension\Context\BasicContext + - SilverStripe\BehatExtension\Context\LoginContext + - SilverStripe\CMS\Tests\Behaviour\AnchorContext + - + SilverStripe\AssetAdmin\Tests\Behat\Context\FixtureContext: + - "%paths.modules.htmleditor-tinymce%/tests/behat/files/" + + extensions: + SilverStripe\BehatExtension\MinkExtension: + default_session: facebook_web_driver + javascript_session: facebook_web_driver + facebook_web_driver: + browser: chrome + wd_host: "http://127.0.0.1:9515" #chromedriver port + + SilverStripe\BehatExtension\Extension: + screenshot_path: '%paths.base%/artifacts/screenshots' + bootstrap_file: vendor/silverstripe/framework/tests/behat/serve-bootstrap.php diff --git a/client/dist/js/TinyMCE_ssembed.js b/client/dist/js/TinyMCE_ssembed.js new file mode 100644 index 00000000..3df467ee --- /dev/null +++ b/client/dist/js/TinyMCE_ssembed.js @@ -0,0 +1 @@ +!function(){"use strict";var e={145:function(e){e.exports=ReactDomClient},207:function(e){e.exports=Injector},502:function(e){e.exports=ShortcodeSerialiser},594:function(e){e.exports=React},669:function(e){e.exports=jQuery},815:function(e){e.exports=i18n},984:function(e){e.exports=InsertEmbedModal}},t={};function n(r){var o=t[r];if(void 0!==o)return o.exports;var i=t[r]={exports:{}};return e[r](i,i.exports,n),i.exports}var r=u(n(669)),o=u(n(594)),i=n(145),a=n(207),d=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=c(t);if(n&&n.has(e))return n.get(e);var r={__proto__:null},o=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var i in e)if("default"!==i&&{}.hasOwnProperty.call(e,i)){var a=o?Object.getOwnPropertyDescriptor(e,i):null;a&&(a.get||a.set)?Object.defineProperty(r,i,a):r[i]=e[i]}return r.default=e,n&&n.set(e,r),r}(n(502)),s=u(n(984)),l=u(n(815));function c(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(c=function(e){return e?n:t})(e)}function u(e){return e&&e.__esModule?e:{default:e}}const p=(0,a.loadComponent)(s.default),h='div[data-shortcode="embed"]';(()=>{const e=e=>{const t=l.default._t("TinyMCE.INSERT_VIA_URL","Insert media via URL"),n=l.default._t("TinyMCE.EDIT_MEDIA","Edit media"),o=l.default._t("TinyMCE.DELETE_MEDIA","Delete media"),i=l.default._t("TinyMCE.MEDIA","Media");return e.addCommand("ssembed",(()=>{(0,r.default)(`#${e.id}`).entwine("ss").openEmbedDialog()})),e.addCommand("ssembed-delete",(()=>{const t=e.selection.getNode();e.dom.is(t,h)?t.remove():e.dom.is(t.parentNode,h)?t.parentNode.remove():console.error({error:"Unexpected selection - expected embed",selectedNode:t})})),e.ui.registry.addButton("ssembed",{tooltip:t,icon:"embed",onAction:()=>e.execCommand("ssembed"),stateSelector:h}),e.ui.registry.addMenuItem("ssembed",{text:i,icon:"embed",onAction:()=>e.execCommand("ssembed")}),e.ui.registry.addButton("ssembededit",{tooltip:n,icon:"edit-block",onAction:()=>e.execCommand("ssembed")}),e.ui.registry.addButton("ssembeddelete",{tooltip:o,icon:"remove",onAction:()=>e.execCommand("ssembed-delete")}),e.ui.registry.addContextToolbar("ssembed",{predicate:t=>e.dom.is(t,h),position:"node",scope:"node",items:"alignleft aligncenter alignright | ssembededit ssembeddelete"}),e.on("BeforeExecCommand",(t=>{const n=t.command,r=t.ui,o=t.value;"mceMedia"===n&&(t.preventDefault(),e.execCommand("ssembed",r,o))})),e.on("GetContent",(e=>{const t=(0,r.default)(`
${e.content}
`);t.find(h).each((function(){const e=(0,r.default)(this),t=e.find("img.placeholder");if(0===t.length)return e.removeAttr("data-url"),void e.removeAttr("data-shortcode");const n=e.find(".caption").text(),o=parseInt(t.attr("width"),10),i=parseInt(t.attr("height"),10),a=e.data("url"),s=(0,d.sanitiseShortCodeProperties)({url:a,thumbnail:t.prop("src"),class:e.prop("class"),width:isNaN(o)?null:o,height:isNaN(i)?null:i,caption:n}),l=d.default.serialise({name:"embed",properties:s,wrapped:!0,content:s.url});e.replaceWith(l)})),e.content=t.html()})),e.on("BeforeSetContent",(e=>{let t=e.content,n=d.default.match("embed",!0,t);for(;n;){const e=n.properties,o=(0,r.default)("
").attr("data-url",e.url||n.content).attr("data-shortcode","embed").addClass(e.class).addClass("ss-htmleditorfield-file embed"),i=(0,r.default)("").attr("src",e.thumbnail).addClass("placeholder");if(e.width&&i.attr("width",e.width),e.height&&i.attr("height",e.height),o.append(i),e.caption){const t=(0,r.default)("

").addClass("caption").text(e.caption);o.append(t)}t=t.replace(n.original,(0,r.default)("

").append(o).html()),n=d.default.match("embed",!0,t)}e.content=t})),{getMetadata(){return{name:"Silverstripe Embed",url:"https://docs.silverstripe.org/en/4/developer_guides/forms/field_types/htmleditorfield"}}}};tinymce.PluginManager.add("ssembed",(t=>e(t)))})(),r.default.entwine("ss",(e=>{e(".js-injector-boot #insert-embed-react__dialog-wrapper").entwine({Element:null,Data:{},ReactRoot:null,onunmatch(){this._clearModal()},_clearModal(){const e=this.getReactRoot();e&&(e.unmount(),this.setReactRoot(null))},open(){this._renderModal(!0)},close(){this.setData({}),this._renderModal(!1)},_renderModal(e){var t=this;const n=this.getOriginalAttributes();let r=this.getReactRoot();r||(r=(0,i.createRoot)(this[0])),r.render(o.default.createElement(p,{isOpen:e,onCreate:function(){return t._handleCreate(...arguments)},onInsert:function(){return t._handleInsert(...arguments)},onClosed:()=>this.close(),onLoadingError:function(){return t._handleLoadingError(...arguments)},bodyClassName:"modal__dialog",className:"insert-embed-react__dialog-wrapper",fileAttributes:n})),this.setReactRoot(r)},_handleLoadingError(){this.setData({}),this.open()},_handleInsert(e){const t=this.getData();this.setData(Object.assign({Url:t.Url},e)),this.insertRemote(),this.close()},_handleCreate(e){this.setData(Object.assign({},this.getData(),e)),this.open()},getOriginalAttributes(){const t=this.getData(),n=this.getElement();if(!n)return t;const r=e(n.getEditor().getSelectedNode());if(!r.length)return t;const o=r.closest(h).add(r.filter(h));if(!o.length)return t;const i=o.find("img.placeholder");if(0===i.length)return t;const a=o.find(".caption").text(),d=parseInt(i.width(),10),s=parseInt(i.height(),10);return{Url:o.data("url")||t.Url,CaptionText:a,PreviewUrl:i.attr("src"),Width:isNaN(d)?null:d,Height:isNaN(s)?null:s,Placement:this.findPosition(o.prop("class"))}},findPosition(e){if("string"!=typeof e)return"";const t=e.split(" ");return["leftAlone","center","rightAlone","left","right"].find((e=>t.indexOf(e)>-1))},insertRemote(){const t=this.getElement();if(!t)return!1;const n=t.getEditor();if(!n)return!1;const o=this.getData(),i=(0,r.default)("
").attr("data-url",o.Url).attr("data-shortcode","embed").addClass(o.Placement).addClass("ss-htmleditorfield-file embed"),a=(0,r.default)("").attr("src",o.PreviewUrl).addClass("placeholder");if(o.Width&&a.attr("width",o.Width),o.Height&&a.attr("height",o.Height),i.append(a),o.CaptionText){const e=(0,r.default)("

").addClass("caption").text(o.CaptionText);i.append(e)}const d=e(n.getSelectedNode());let s=e(null);return d.length&&(s=d.filter(h),0===s.length&&(s=d.closest(h)),0===s.length&&(s=d.filter("img.placeholder"))),s.length?s.replaceWith(i):(n.repaint(),n.insertContent(e("

").append(i.clone()).html(),{skip_undo:1})),n.addUndo(),n.repaint(),!0}})}))}(); \ No newline at end of file diff --git a/client/dist/js/TinyMCE_sslink-anchor.js b/client/dist/js/TinyMCE_sslink-anchor.js new file mode 100644 index 00000000..6adf0105 --- /dev/null +++ b/client/dist/js/TinyMCE_sslink-anchor.js @@ -0,0 +1 @@ +!function(){"use strict";var t={40:function(t){t.exports=ReactRedux},127:function(t){t.exports=InsertLinkModal},145:function(t){t.exports=ReactDomClient},207:function(t){t.exports=Injector},502:function(t){t.exports=ShortcodeSerialiser},594:function(t){t.exports=React},669:function(t){t.exports=jQuery},671:function(t,e){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;const n=(t,e)=>t?`${t}.${e}`:e;window.ss=window.ss||{},window.ss.tinymceactions=window.ss.tinymceactions||new class{constructor(){this.actions={},this.editorCommandsToUrlTestsMap={},this.defaultCommand="sslinkexternal"}addAction(t,e,i){const o=e.priority||50,r=n(i,t),s=this.getActions(t,i,!0);return e.type="menuitem",e.hasOwnProperty("onclick")&&(e.onAction=e.onclick,delete e.onclick),s.find((t=>e.text===t.text))||(this.actions[r]=[...this.getActions(t,i,!1),{...e,priority:o}]),this}getActions(t,e){let i=e&&!(!(arguments.length>2&&void 0!==arguments[2])||arguments[2])||!this.actions[t]?[]:this.actions[t];const o=n(e,t);return e&&this.actions[o]&&(i=[...i,...this.actions[o]]),i}getSortedActions(t,e){let n=!(arguments.length>2&&void 0!==arguments[2])||arguments[2];return this.getActions(t,e,n).sort(((t,e)=>{const n=e.priority-t.priority;return n||(t.text.toLocaleLowerCase()>e.text.toLocaleLowerCase()?1:-1)}))}addCommandWithUrlTest(t,e){return this.editorCommandsToUrlTestsMap[t]=e,this}setDefaultCommand(t){return this.defaultCommand=t,this}getDefaultCommand(){return this.defaultCommand}getEditorCommandFromUrl(t){let e=this.getDefaultCommand();const n=Object.keys(this.editorCommandsToUrlTestsMap).find((e=>this.editorCommandsToUrlTestsMap[e]&&this.editorCommandsToUrlTestsMap[e].test(t)));return n&&(e=n),e}};e.default=window.ss.tinymceactions},815:function(t){t.exports=i18n},918:function(t){t.exports=AnchorSelectorActions}},e={};function n(i){var o=e[i];if(void 0!==o)return o.exports;var r=e[i]={exports:{}};return t[i](r,r.exports,n),r.exports}!function(){var t=u(n(815)),e=u(n(671)),i=u(n(594)),o=n(145),r=n(40),s=u(n(669)),a=u(n(502)),d=n(127),l=n(207),c=n(918);function u(t){return t&&t.__esModule?t:{default:t}}const m="sslinkanchor",h={init(n){e.default.addAction("sslink",{text:t.default._t("TinyMCE.LINKLABEL_ANCHOR","Anchor on a page"),onAction:t=>t.execCommand(m),priority:60},n.getParam("editorIdentifier")).addCommandWithUrlTest(m,/^\[sitetree_link.+]#[^#\]]+$/),n.addCommand(m,(()=>{const t=(0,s.default)(`#${n.id}`).entwine("ss"),e=Number((0,s.default)("#Form_EditForm_ID").val()||0),i=(0,s.default)(n.getBody()).find("[id],[name]").toArray().map((t=>t.id||t.name));ss.store.dispatch((0,c.updatedCurrentField)(e,i,n.id)),t.openLinkAnchorDialog()}))}},p="insert-link__dialog-wrapper--anchor",f=(0,l.provideInjector)((0,d.createInsertLinkModal)("SilverStripe\\Admin\\LeftAndMain","editorAnchorLink"));s.default.entwine("ss",(e=>{e("textarea.htmleditor").entwine({openLinkAnchorDialog(){let t=e(`#${p}`);t.length||(t=e(`
`),e("body").append(t)),t.addClass("insert-link__dialog-wrapper"),t.setElement(this),t.open()}}),e(`#${p}`).entwine({ReactRoot:null,renderModal(n){var s=this;const a=ss.store,d=this.getOriginalAttributes(),l=this.getRequireLinkText(),c=Number(e("#Form_EditForm_ID").val()||0);let u=this.getReactRoot();u||(u=(0,o.createRoot)(this[0]),this.setReactRoot(u)),u.render(i.default.createElement(r.Provider,{store:a},i.default.createElement(f,{isOpen:n,onInsert:function(){return s.handleInsert(...arguments)},onClosed:()=>this.close(),title:t.default._t("TinyMCE.LINK_ANCHOR","Link to an anchor on a page"),bodyClassName:"modal__dialog",className:"insert-link__dialog-wrapper--anchor",fileAttributes:d,identifier:"Admin.InsertLinkAnchorModal",requireLinkText:l,currentPageID:c})))},buildAttributes(t){return{href:`${a.default.serialise({name:"sitetree_link",properties:{id:t.PageID}},!0)}${t.Anchor&&t.Anchor.length?`#${t.Anchor}`:""}`,target:t.TargetBlank?"_blank":"",title:t.Description}},getOriginalAttributes(){const t=this.getElement().getEditor(),n=e(t.getSelectedNode()),i=(n.attr("href")||"").split("#");if(!i[0])return{};const o=a.default.match("sitetree_link",!1,i[0]);return o?{PageID:o.properties.id?parseInt(o.properties.id,10):0,Anchor:i[1]||"",Description:n.attr("title"),TargetBlank:!!n.attr("target")}:{}}})})),tinymce.PluginManager.add(m,(t=>h.init(t)))}()}(); \ No newline at end of file diff --git a/client/dist/js/TinyMCE_sslink-email.js b/client/dist/js/TinyMCE_sslink-email.js new file mode 100644 index 00000000..07e68d7c --- /dev/null +++ b/client/dist/js/TinyMCE_sslink-email.js @@ -0,0 +1 @@ +!function(){"use strict";var t={127:function(t){t.exports=InsertLinkModal},145:function(t){t.exports=ReactDomClient},207:function(t){t.exports=Injector},594:function(t){t.exports=React},669:function(t){t.exports=jQuery},671:function(t,e){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;const i=(t,e)=>t?`${t}.${e}`:e;window.ss=window.ss||{},window.ss.tinymceactions=window.ss.tinymceactions||new class{constructor(){this.actions={},this.editorCommandsToUrlTestsMap={},this.defaultCommand="sslinkexternal"}addAction(t,e,n){const o=e.priority||50,s=i(n,t),r=this.getActions(t,n,!0);return e.type="menuitem",e.hasOwnProperty("onclick")&&(e.onAction=e.onclick,delete e.onclick),r.find((t=>e.text===t.text))||(this.actions[s]=[...this.getActions(t,n,!1),{...e,priority:o}]),this}getActions(t,e){let n=e&&!(!(arguments.length>2&&void 0!==arguments[2])||arguments[2])||!this.actions[t]?[]:this.actions[t];const o=i(e,t);return e&&this.actions[o]&&(n=[...n,...this.actions[o]]),n}getSortedActions(t,e){let i=!(arguments.length>2&&void 0!==arguments[2])||arguments[2];return this.getActions(t,e,i).sort(((t,e)=>{const i=e.priority-t.priority;return i||(t.text.toLocaleLowerCase()>e.text.toLocaleLowerCase()?1:-1)}))}addCommandWithUrlTest(t,e){return this.editorCommandsToUrlTestsMap[t]=e,this}setDefaultCommand(t){return this.defaultCommand=t,this}getDefaultCommand(){return this.defaultCommand}getEditorCommandFromUrl(t){let e=this.getDefaultCommand();const i=Object.keys(this.editorCommandsToUrlTestsMap).find((e=>this.editorCommandsToUrlTestsMap[e]&&this.editorCommandsToUrlTestsMap[e].test(t)));return i&&(e=i),e}};e.default=window.ss.tinymceactions},815:function(t){t.exports=i18n}},e={};function i(n){var o=e[n];if(void 0!==o)return o.exports;var s=e[n]={exports:{}};return t[n](s,s.exports,i),s.exports}!function(){var t=l(i(815)),e=l(i(671)),n=l(i(594)),o=i(145),s=l(i(669)),r=i(127),a=i(207);function l(t){return t&&t.__esModule?t:{default:t}}const d="sslinkemail",c={init(i){e.default.addAction("sslink",{text:t.default._t("TinyMCE.LINKLABEL_EMAIL","Link to email address"),onAction:t=>t.execCommand(d),priority:51},i.getParam("editorIdentifier")).addCommandWithUrlTest(d,/^mailto:/),i.addCommand(d,(()=>{window.jQuery(`#${i.id}`).entwine("ss").openLinkEmailDialog()}))}},m="insert-link__dialog-wrapper--email",u=(0,a.loadComponent)((0,r.createInsertLinkModal)("SilverStripe\\Admin\\LeftAndMain","EditorEmailLink"));s.default.entwine("ss",(e=>{e("textarea.htmleditor").entwine({openLinkEmailDialog(){let t=e(`#${m}`);t.length||(t=e(`
`),e("body").append(t)),t.addClass("insert-link__dialog-wrapper"),t.setElement(this),t.open()}}),e(`#${m}`).entwine({ReactRoot:null,renderModal(e){var i=this;const s=this.getOriginalAttributes(),r=this.getRequireLinkText();let a=this.getReactRoot();a||(a=(0,o.createRoot)(this[0]),this.setReactRoot(a)),a.render(n.default.createElement(u,{isOpen:e,onInsert:function(){return i.handleInsert(...arguments)},onClosed:()=>this.close(),title:t.default._t("TinyMCE.LINK_EMAIL","Insert email link"),bodyClassName:"modal__dialog",className:"insert-link__dialog-wrapper--email",fileAttributes:s,identifier:"Admin.InsertLinkEmailModal",requireLinkText:r}))},getOriginalAttributes(){const t=this.getElement().getEditor(),i=e(t.getSelectedNode()),n=(i.attr("href")||"").split("?");let o=n[0].replace(/^mailto:/,"").split("?")[0];o.match(/.+@.+\..+/)||(o="");const s=n[1]?n[1].match(/subject=([^&]+)/):"";return{Link:o,Subject:s?decodeURIComponent(s[1]):"",Description:i.attr("title")}},buildAttributes(t){const e=this._super(t);let i="",n=e.href.replace(/^mailto:/,"").split("?")[0];return n.match(/.+@.+\..+/)||(n=""),n&&(i=`mailto:${n}`),i&&t.Subject&&(i=`${i}?subject=${encodeURIComponent(t.Subject)}`),e.href=i,delete e.target,e}})})),tinymce.PluginManager.add(d,(t=>c.init(t)))}()}(); \ No newline at end of file diff --git a/client/dist/js/TinyMCE_sslink-external.js b/client/dist/js/TinyMCE_sslink-external.js new file mode 100644 index 00000000..b1e87f61 --- /dev/null +++ b/client/dist/js/TinyMCE_sslink-external.js @@ -0,0 +1 @@ +!function(){"use strict";var t={127:function(t){t.exports=InsertLinkModal},145:function(t){t.exports=ReactDomClient},207:function(t){t.exports=Injector},594:function(t){t.exports=React},669:function(t){t.exports=jQuery},671:function(t,e){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;const n=(t,e)=>t?`${t}.${e}`:e;window.ss=window.ss||{},window.ss.tinymceactions=window.ss.tinymceactions||new class{constructor(){this.actions={},this.editorCommandsToUrlTestsMap={},this.defaultCommand="sslinkexternal"}addAction(t,e,i){const o=e.priority||50,s=n(i,t),r=this.getActions(t,i,!0);return e.type="menuitem",e.hasOwnProperty("onclick")&&(e.onAction=e.onclick,delete e.onclick),r.find((t=>e.text===t.text))||(this.actions[s]=[...this.getActions(t,i,!1),{...e,priority:o}]),this}getActions(t,e){let i=e&&!(!(arguments.length>2&&void 0!==arguments[2])||arguments[2])||!this.actions[t]?[]:this.actions[t];const o=n(e,t);return e&&this.actions[o]&&(i=[...i,...this.actions[o]]),i}getSortedActions(t,e){let n=!(arguments.length>2&&void 0!==arguments[2])||arguments[2];return this.getActions(t,e,n).sort(((t,e)=>{const n=e.priority-t.priority;return n||(t.text.toLocaleLowerCase()>e.text.toLocaleLowerCase()?1:-1)}))}addCommandWithUrlTest(t,e){return this.editorCommandsToUrlTestsMap[t]=e,this}setDefaultCommand(t){return this.defaultCommand=t,this}getDefaultCommand(){return this.defaultCommand}getEditorCommandFromUrl(t){let e=this.getDefaultCommand();const n=Object.keys(this.editorCommandsToUrlTestsMap).find((e=>this.editorCommandsToUrlTestsMap[e]&&this.editorCommandsToUrlTestsMap[e].test(t)));return n&&(e=n),e}};e.default=window.ss.tinymceactions},815:function(t){t.exports=i18n}},e={};function n(i){var o=e[i];if(void 0!==o)return o.exports;var s=e[i]={exports:{}};return t[i](s,s.exports,n),s.exports}!function(){var t=l(n(815)),e=l(n(671)),i=l(n(594)),o=n(145),s=l(n(669)),r=n(127),a=n(207);function l(t){return t&&t.__esModule?t:{default:t}}const d={init(n){e.default.addAction("sslink",{text:t.default._t("TinyMCE.LINKLABEL_EXTERNALURL","Link to external URL"),onAction:t=>t.execCommand("sslinkexternal"),priority:70},n.getParam("editorIdentifier")),n.addCommand("sslinkexternal",(()=>{window.jQuery(`#${n.id}`).entwine("ss").openLinkExternalDialog()}))}},c="insert-link__dialog-wrapper--external",u=(0,a.loadComponent)((0,r.createInsertLinkModal)("SilverStripe\\Admin\\LeftAndMain","EditorExternalLink"));s.default.entwine("ss",(e=>{e("textarea.htmleditor").entwine({openLinkExternalDialog(){let t=e(`#${c}`);t.length||(t=e(`
`),e("body").append(t)),t.addClass("insert-link__dialog-wrapper"),t.setElement(this),t.open()}}),e(`#${c}`).entwine({ReactRoot:null,renderModal(e){var n=this;const s=this.getOriginalAttributes(),r=this.getRequireLinkText();let a=this.getReactRoot();a||(a=(0,o.createRoot)(this[0]),this.setReactRoot(a)),a.render(i.default.createElement(u,{isOpen:e,onInsert:function(){return n.handleInsert(...arguments)},onClosed:()=>this.close(),title:t.default._t("TinyMCE.LINK_EXTERNAL","Insert external link"),bodyClassName:"modal__dialog",className:"insert-link__dialog-wrapper--external",fileAttributes:s,identifier:"Admin.InsertLinkExternalModal",requireLinkText:r}))},buildAttributes(t){const e=this._super(t);let n=e.href;return n.match(/:\/\//)||(n=`${window.location.protocol}//${n}`),n=n.replace(/.*:\/\/(#.*)$/,"$1"),n.match(/:\/\/$/)&&(n=""),e.href=n,e}})})),tinymce.PluginManager.add("sslinkexternal",(t=>d.init(t)))}()}(); \ No newline at end of file diff --git a/client/dist/js/TinyMCE_sslink-file.js b/client/dist/js/TinyMCE_sslink-file.js new file mode 100644 index 00000000..7d6491e1 --- /dev/null +++ b/client/dist/js/TinyMCE_sslink-file.js @@ -0,0 +1 @@ +!function(){"use strict";var t={64:function(t){t.exports=AssetAdminModalActions},145:function(t){t.exports=ReactDomClient},207:function(t){t.exports=Injector},502:function(t){t.exports=ShortcodeSerialiser},594:function(t){t.exports=React},643:function(t){t.exports=InsertMediaModal},669:function(t){t.exports=jQuery},671:function(t,e){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;const n=(t,e)=>t?`${t}.${e}`:e;window.ss=window.ss||{},window.ss.tinymceactions=window.ss.tinymceactions||new class{constructor(){this.actions={},this.editorCommandsToUrlTestsMap={},this.defaultCommand="sslinkexternal"}addAction(t,e,i){const r=e.priority||50,o=n(i,t),s=this.getActions(t,i,!0);return e.type="menuitem",e.hasOwnProperty("onclick")&&(e.onAction=e.onclick,delete e.onclick),s.find((t=>e.text===t.text))||(this.actions[o]=[...this.getActions(t,i,!1),{...e,priority:r}]),this}getActions(t,e){let i=e&&!(!(arguments.length>2&&void 0!==arguments[2])||arguments[2])||!this.actions[t]?[]:this.actions[t];const r=n(e,t);return e&&this.actions[r]&&(i=[...i,...this.actions[r]]),i}getSortedActions(t,e){let n=!(arguments.length>2&&void 0!==arguments[2])||arguments[2];return this.getActions(t,e,n).sort(((t,e)=>{const n=e.priority-t.priority;return n||(t.text.toLocaleLowerCase()>e.text.toLocaleLowerCase()?1:-1)}))}addCommandWithUrlTest(t,e){return this.editorCommandsToUrlTestsMap[t]=e,this}setDefaultCommand(t){return this.defaultCommand=t,this}getDefaultCommand(){return this.defaultCommand}getEditorCommandFromUrl(t){let e=this.getDefaultCommand();const n=Object.keys(this.editorCommandsToUrlTestsMap).find((e=>this.editorCommandsToUrlTestsMap[e]&&this.editorCommandsToUrlTestsMap[e].test(t)));return n&&(e=n),e}};e.default=window.ss.tinymceactions},815:function(t){t.exports=i18n}},e={};function n(i){var r=e[i];if(void 0!==r)return r.exports;var o=e[i]={exports:{}};return t[i](o,o.exports,n),o.exports}!function(){var t=f(n(815)),e=f(n(671)),i=f(n(594)),r=n(145),o=f(n(669)),s=f(n(502)),a=f(n(643)),l=u(n(207)),d=u(n(64));function c(t){if("function"!=typeof WeakMap)return null;var e=new WeakMap,n=new WeakMap;return(c=function(t){return t?n:e})(t)}function u(t,e){if(!e&&t&&t.__esModule)return t;if(null===t||"object"!=typeof t&&"function"!=typeof t)return{default:t};var n=c(e);if(n&&n.has(t))return n.get(t);var i={__proto__:null},r=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var o in t)if("default"!==o&&{}.hasOwnProperty.call(t,o)){var s=r?Object.getOwnPropertyDescriptor(t,o):null;s&&(s.get||s.set)?Object.defineProperty(i,o,s):i[o]=t[o]}return i.default=t,n&&n.set(t,i),i}function f(t){return t&&t.__esModule?t:{default:t}}const p="sslinkfile",m={init(n){e.default.addAction("sslink",{text:t.default._t("TinyMCE.LINKLABEL_FILE","Link to a file"),onAction:t=>t.execCommand(p),priority:80},n.getParam("editorIdentifier")).addCommandWithUrlTest(p,/^\[file_link/),n.addCommand(p,(()=>{(0,o.default)(`#${n.id}`).entwine("ss").openLinkFileDialog()}))}},h="insert-link__dialog-wrapper--file",g=(0,l.loadComponent)(a.default);o.default.entwine("ss",(t=>{t("textarea.htmleditor").entwine({openLinkFileDialog(){let e=t(`#${h}`);e.length||(e=t(`
`),t("body").append(e)),e.addClass("insert-link__dialog-wrapper"),e.setElement(this),e.open()}}),t(`.js-injector-boot #${h}`).entwine({ReactRoot:null,renderModal(t){var e=this;const{dispatch:n}=l.default.reducer.store;n(d.initFormStack("insert-link","admin"));const o=this.getOriginalAttributes(),s=this.getFolderId(),a=this.getRequireLinkText();let c=this.getReactRoot();c||(c=(0,r.createRoot)(this[0]),this.setReactRoot(c)),c.render(i.default.createElement(g,{isOpen:t,type:"insert-link",folderId:s,onInsert:function(){return e.handleInsert(...arguments)},onClosed:()=>{n(d.reset()),this.close()},title:!1,bodyClassName:"modal__dialog",className:"insert-link__dialog-wrapper--internal",fileAttributes:o,requireLinkText:a}))},buildAttributes(t){return{href:`${s.default.serialise({name:"file_link",properties:{id:t.ID}},!0)}${t.Anchor&&t.Anchor.length?`#${t.Anchor}`:""}`,target:t.TargetBlank?"_blank":"",title:t.Description}},getFolderId(){const t=this.getElement();if(!t)return null;const e=Number(t.data("config").upload_folder_id);return isNaN(e)?null:e},getOriginalAttributes(){const e=this.getElement().getEditor(),n=t(e.getSelectedNode()),i=(n.attr("href")||"").split("#");if(!i[0])return{};const r=s.default.match("file_link",!1,i[0]);return r?{ID:r.properties.id?parseInt(r.properties.id,10):0,Anchor:i[1]||"",Description:n.attr("title"),TargetBlank:!!n.attr("target")}:{}}})})),tinymce.PluginManager.add(p,(t=>m.init(t)))}()}(); \ No newline at end of file diff --git a/client/dist/js/TinyMCE_sslink-internal.js b/client/dist/js/TinyMCE_sslink-internal.js new file mode 100644 index 00000000..5681fe11 --- /dev/null +++ b/client/dist/js/TinyMCE_sslink-internal.js @@ -0,0 +1 @@ +!function(){"use strict";var t={40:function(t){t.exports=ReactRedux},127:function(t){t.exports=InsertLinkModal},145:function(t){t.exports=ReactDomClient},207:function(t){t.exports=Injector},502:function(t){t.exports=ShortcodeSerialiser},594:function(t){t.exports=React},669:function(t){t.exports=jQuery},671:function(t,e){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;const n=(t,e)=>t?`${t}.${e}`:e;window.ss=window.ss||{},window.ss.tinymceactions=window.ss.tinymceactions||new class{constructor(){this.actions={},this.editorCommandsToUrlTestsMap={},this.defaultCommand="sslinkexternal"}addAction(t,e,i){const r=e.priority||50,o=n(i,t),s=this.getActions(t,i,!0);return e.type="menuitem",e.hasOwnProperty("onclick")&&(e.onAction=e.onclick,delete e.onclick),s.find((t=>e.text===t.text))||(this.actions[o]=[...this.getActions(t,i,!1),{...e,priority:r}]),this}getActions(t,e){let i=e&&!(!(arguments.length>2&&void 0!==arguments[2])||arguments[2])||!this.actions[t]?[]:this.actions[t];const r=n(e,t);return e&&this.actions[r]&&(i=[...i,...this.actions[r]]),i}getSortedActions(t,e){let n=!(arguments.length>2&&void 0!==arguments[2])||arguments[2];return this.getActions(t,e,n).sort(((t,e)=>{const n=e.priority-t.priority;return n||(t.text.toLocaleLowerCase()>e.text.toLocaleLowerCase()?1:-1)}))}addCommandWithUrlTest(t,e){return this.editorCommandsToUrlTestsMap[t]=e,this}setDefaultCommand(t){return this.defaultCommand=t,this}getDefaultCommand(){return this.defaultCommand}getEditorCommandFromUrl(t){let e=this.getDefaultCommand();const n=Object.keys(this.editorCommandsToUrlTestsMap).find((e=>this.editorCommandsToUrlTestsMap[e]&&this.editorCommandsToUrlTestsMap[e].test(t)));return n&&(e=n),e}};e.default=window.ss.tinymceactions},815:function(t){t.exports=i18n}},e={};function n(i){var r=e[i];if(void 0!==r)return r.exports;var o=e[i]={exports:{}};return t[i](o,o.exports,n),o.exports}!function(){var t=c(n(815)),e=c(n(671)),i=c(n(594)),r=n(145),o=n(40),s=c(n(669)),a=c(n(502)),d=n(127),l=n(207);function c(t){return t&&t.__esModule?t:{default:t}}const u="sslinkinternal",m={init(n){e.default.addAction("sslink",{text:t.default._t("TinyMCE.LINKLABEL_PAGE","Page on this site"),onAction:t=>t.execCommand(u),priority:90},n.getParam("editorIdentifier")).addCommandWithUrlTest(u,/^\[sitetree_link.+]$/),n.addCommand(u,(()=>{(0,s.default)(`#${n.id}`).entwine("ss").openLinkInternalDialog()}))}},p="insert-link__dialog-wrapper--internal",h=(0,l.provideInjector)((0,d.createInsertLinkModal)("SilverStripe\\Admin\\LeftAndMain","editorInternalLink"));s.default.entwine("ss",(e=>{e("textarea.htmleditor").entwine({openLinkInternalDialog(){let t=e(`#${p}`);t.length||(t=e(`
`),e("body").append(t)),t.addClass("insert-link__dialog-wrapper"),t.setElement(this),t.open()}}),e(`#${p}`).entwine({ReactRoot:null,renderModal(e){var n=this;const s=ss.store,a=this.getOriginalAttributes(),d=this.getRequireLinkText();let l=this.getReactRoot();l||(l=(0,r.createRoot)(this[0]),this.setReactRoot(l)),l.render(i.default.createElement(o.Provider,{store:s},i.default.createElement(h,{isOpen:e,onInsert:function(){return n.handleInsert(...arguments)},onClosed:()=>this.close(),title:t.default._t("TinyMCE.LINK_PAGE","Link to a page"),bodyClassName:"modal__dialog",className:"insert-link__dialog-wrapper--internal",fileAttributes:a,identifier:"Admin.InsertLinkInternalModal",requireLinkText:d})))},buildAttributes(t){return{href:`${a.default.serialise({name:"sitetree_link",properties:{id:t.PageID}},!0)}${t.Anchor&&t.Anchor.length?`#${t.Anchor}`:""}`,target:t.TargetBlank?"_blank":"",title:t.Description}},getOriginalAttributes(){const t=this.getElement().getEditor(),n=e(t.getSelectedNode()),i=(n.attr("href")||"").split("#");if(!i[0])return{};const r=a.default.match("sitetree_link",!1,i[0]);return r?{PageID:r.properties.id?parseInt(r.properties.id,10):0,Anchor:i[1]||"",Description:n.attr("title"),TargetBlank:!!n.attr("target")}:{}}})})),tinymce.PluginManager.add(u,(t=>m.init(t)))}()}(); \ No newline at end of file diff --git a/client/dist/js/TinyMCE_sslink.js b/client/dist/js/TinyMCE_sslink.js new file mode 100644 index 00000000..ff8a0ccb --- /dev/null +++ b/client/dist/js/TinyMCE_sslink.js @@ -0,0 +1 @@ +!function(){"use strict";var t={502:function(t){t.exports=ShortcodeSerialiser},669:function(t){t.exports=jQuery},671:function(t,e){Object.defineProperty(e,"__esModule",{value:!0}),e.default=void 0;const n=(t,e)=>t?`${t}.${e}`:e;window.ss=window.ss||{},window.ss.tinymceactions=window.ss.tinymceactions||new class{constructor(){this.actions={},this.editorCommandsToUrlTestsMap={},this.defaultCommand="sslinkexternal"}addAction(t,e,i){const o=e.priority||50,s=n(i,t),r=this.getActions(t,i,!0);return e.type="menuitem",e.hasOwnProperty("onclick")&&(e.onAction=e.onclick,delete e.onclick),r.find((t=>e.text===t.text))||(this.actions[s]=[...this.getActions(t,i,!1),{...e,priority:o}]),this}getActions(t,e){let i=e&&!(!(arguments.length>2&&void 0!==arguments[2])||arguments[2])||!this.actions[t]?[]:this.actions[t];const o=n(e,t);return e&&this.actions[o]&&(i=[...i,...this.actions[o]]),i}getSortedActions(t,e){let n=!(arguments.length>2&&void 0!==arguments[2])||arguments[2];return this.getActions(t,e,n).sort(((t,e)=>{const n=e.priority-t.priority;return n||(t.text.toLocaleLowerCase()>e.text.toLocaleLowerCase()?1:-1)}))}addCommandWithUrlTest(t,e){return this.editorCommandsToUrlTestsMap[t]=e,this}setDefaultCommand(t){return this.defaultCommand=t,this}getDefaultCommand(){return this.defaultCommand}getEditorCommandFromUrl(t){let e=this.getDefaultCommand();const n=Object.keys(this.editorCommandsToUrlTestsMap).find((e=>this.editorCommandsToUrlTestsMap[e]&&this.editorCommandsToUrlTestsMap[e].test(t)));return n&&(e=n),e}};e.default=window.ss.tinymceactions},815:function(t){t.exports=i18n}},e={};function n(i){var o=e[i];if(void 0!==o)return o.exports;var s=e[i]={exports:{}};return t[i](s,s.exports,n),s.exports}!function(){var t=s(n(671)),e=s(n(669)),i=n(502),o=s(n(815));function s(t){return t&&t.__esModule?t:{default:t}}const r={init(n){function i(){return t.default.getSortedActions("sslink",n.getParam("editorIdentifier"),!0).map((t=>Object.assign({},t,{onAction:()=>t.onAction(n)})))}const s=navigator.platform.toUpperCase().includes("MAC")?"⌘":"Ctrl",r=o.default._t("TinyMCE.INSERT_LINK","Insert link"),a=o.default.inject(o.default._t("TinyMCE.INSERT_LINK_WITH_SHORTCUT","Insert link {shortcut}"),{shortcut:`[${s}+K]`});return n.addShortcut("Meta+k","Open link menu",(()=>{(0,e.default)(`[aria-label^="${r}"] > button`,n.container).first().click()})),n.ui.registry.addMenuButton("sslink",{icon:"link",tooltip:a,fetch:t=>t(i())}),n.ui.registry.addNestedMenuItem("sslink",{icon:"link",text:r,getSubmenuItems:i}),n.ui.registry.addButton("sslink-edit",{text:o.default._t("TinyMCE.EDIT_LINK","Edit link"),onAction:function(){const i=(0,e.default)(tinymce.activeEditor.selection.getNode()).closest("a");tinymce.activeEditor.selection.select(i[0]);const o=tinymce.activeEditor.selection.getNode().getAttribute("href");o&&n.execCommand(t.default.getEditorCommandFromUrl(o))}}),n.ui.registry.addButton("sslink-remove",{text:o.default._t("TinyMCE.REMOVE_LINK","Remove link"),onAction:()=>this.handleRemoveLinkClick(n)}),n.ui.registry.addContextToolbar("sslink",{predicate:t=>n.dom.is(t,"a[href]"),position:"node",scope:"node",items:"sslink-edit sslink-remove"}),{getMetadata(){return{name:"Silverstripe Link",url:"https://docs.silverstripe.org/en/4/developer_guides/forms/field_types/htmleditorfield"}}}},handleRemoveLinkClick(t){const e=t.execCommand("unlink"),n=t.selection.getNode();return n&&void 0!==n.normalize&&n.normalize(),e}};e.default.entwine("ss",(t=>{t(".insert-link__dialog-wrapper").entwine({Element:null,Data:{},Bookmark:null,onunmatch(){this._clearModal()},_clearModal(){const t=this.getReactRoot();t&&(t.unmount(),this.setReactRoot(null))},open(){const t=this.getElement().getEditor().getInstance();this.setBookmark(t.selection.getBookmark(2,!0)),this.renderModal(!0)},close(){this.setData({}),this.renderModal(!1)},renderModal(){},checkNodeMatches(t,e){return t===e||1===e.children.length&&t===e.children[0]},linkCanWrapSelection(t,e){const n=t.getSelection()||"",i=e.getNode();if(n)return""!==n.trim();const o=document.createElement(i.nodeName);if(o.textContent="Check the outer HTML",o.outerHTML.includes("Check the outer HTML"))return!1;if(this.checkNodeMatches(i,e.getSel().focusNode)&&this.checkNodeMatches(i,e.getSel().anchorNode)){if(1===tinymce.activeEditor.dom.createFragment(`${i.outerHTML}`).childNodes.length)return!0}return!1},getRequireLinkText(){const e=this.getElement().getEditor();let n=e.getInstance().selection;const i=t(n.getNode()).closest("a");e.getInstance().selection.select(i[0]),n=e.getInstance().selection;const o=this.linkCanWrapSelection(e,n);return"A"!==n.getNode().tagName&&!o},handleInsert(t){this.getElement().getEditor().getInstance().selection.moveToBookmark(this.getBookmark());const e=this.buildAttributes(t),n=(0,i.createHTMLSanitiser)()(t.Text);return this.insertLinkInEditor(e,n),this.close(),Promise.resolve()},buildAttributes(t){let{Anchor:e,Link:n,TargetBlank:i,Description:o}=t,s=e&&e.length?`#${e}`:"";s=s.replace(/^#+/,"#");return{href:`${n}${s}`,target:i?"_blank":"",title:o}},insertLinkInEditor(t,e){const n=this.getElement().getEditor();n.insertLink(t,null,e),n.addUndo(),n.repaint();const i=n.getInstance().selection;setTimeout((()=>i&&i.collapse()),0)},getOriginalAttributes(){const e=this.getElement().getEditor(),n=t(e.getSelectedNode()),i=(n.attr("href")||"").split("#");return{Link:i[0]||"",Anchor:i[1]||"",Description:n.attr("title"),TargetBlank:!!n.attr("target")}}})})),tinymce.PluginManager.add("sslink",(t=>r.init(t)))}()}(); \ No newline at end of file diff --git a/client/dist/js/TinyMCE_ssmedia.js b/client/dist/js/TinyMCE_ssmedia.js new file mode 100644 index 00000000..2acc3b27 --- /dev/null +++ b/client/dist/js/TinyMCE_ssmedia.js @@ -0,0 +1 @@ +!function(){"use strict";var t={64:function(t){t.exports=AssetAdminModalActions},145:function(t){t.exports=ReactDomClient},207:function(t){t.exports=Injector},502:function(t){t.exports=ShortcodeSerialiser},594:function(t){t.exports=React},643:function(t){t.exports=InsertMediaModal},669:function(t){t.exports=jQuery},759:function(t,e){Object.defineProperty(e,"__esModule",{value:!0}),e.imageSizePresetButtons=function(t,e){return e.map((e=>function(t,e){const{name:i,width:n,text:a}=e,r=`ssmedia${i}`;t.on("init",(()=>{t.formatter.register(r,{selector:"img",attributes:{width:n?n.toString():""}})}));const o=()=>{let e=t.selection.getNode();return"IMG"!==e.tagName&&e.children.item("img")&&(e=e.children.item("img")),e&&"IMG"===e.tagName?e:void 0},s=t=>{const e=o();t.disabled(!(!e||!n)&&e.naturalWidth{if(t.formatter.match(r))return!0;const e=o();if(!n&&e){const t=e.getAttribute("width");return!t||t.toString()===e.naturalWidth.toString()}return!1},l=e=>{const i=e.target,a=()=>{i.active(d())};if(t.on("NodeChange",(()=>{s(i),a()})),s(i),t.formatter&&(t.formatter.formatChanged(r,a),d())){t.formatter.apply(r);const e=o();e&&e.setAttribute("width",n||e.naturalWidth)}},c=()=>{const e=o();e&&(e.removeAttribute("height"),e.removeAttribute("width"),t.formatter.apply(r),n?e.setAttribute("height",e.clientHeight):(e.setAttribute("width",e.naturalWidth),e.setAttribute("height",e.naturalHeight)))};return t.ui.registry.addButton(r,{text:a,onAction:c,onPostRender:l}),r}(t,e)))}},815:function(t){t.exports=i18n}},e={};function i(n){var a=e[n];if(void 0!==a)return a.exports;var r=e[n]={exports:{}};return t[n](r,r.exports,i),r.exports}var n=p(i(669)),a=p(i(815)),r=p(i(594)),o=i(145),s=g(i(207)),d=p(i(643)),l=g(i(502)),c=g(i(64)),u=i(759);function m(t){if("function"!=typeof WeakMap)return null;var e=new WeakMap,i=new WeakMap;return(m=function(t){return t?i:e})(t)}function g(t,e){if(!e&&t&&t.__esModule)return t;if(null===t||"object"!=typeof t&&"function"!=typeof t)return{default:t};var i=m(e);if(i&&i.has(t))return i.get(t);var n={__proto__:null},a=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var r in t)if("default"!==r&&{}.hasOwnProperty.call(t,r)){var o=a?Object.getOwnPropertyDescriptor(t,r):null;o&&(o.get||o.set)?Object.defineProperty(n,r,o):n[r]=t[r]}return n.default=t,i&&i.set(t,n),n}function p(t){return t&&t.__esModule?t:{default:t}}const f=(0,s.loadComponent)(d.default),h='img[data-shortcode="image"]';(()=>{const t={init(t){const e=a.default._t("TinyMCE.INSERT_FROM_FILES","Insert from Files"),i=a.default._t("TinyMCE.EDIT_IMAGE","Edit image"),r=a.default._t("TinyMCE.DELETE_IMAGE","Delete image"),o=a.default._t("TinyMCE.FILE","File");t.addCommand("ssmedia",(()=>{(0,n.default)(`#${t.id}`).entwine("ss").openMediaDialog()})),t.addCommand("ssmedia-delete",(()=>{const e=t.selection.getNode();t.dom.is(e,h)?e.remove():console.error({error:"Unexpected selection - expected image",selectedNode:e})})),t.ui.registry.addButton("ssmedia",{tooltip:e,icon:"image",onAction:()=>t.execCommand("ssmedia"),stateSelector:h}),t.ui.registry.addMenuItem("ssmedia",{text:o,icon:"image",onAction:()=>t.execCommand("ssmedia")}),t.ui.registry.addButton("ssmediaedit",{tooltip:i,icon:"edit-block",onAction:()=>t.execCommand("ssmedia")}),t.ui.registry.addButton("ssmediadelete",{tooltip:r,icon:"remove",onAction:()=>t.execCommand("ssmedia-delete")});const s=t.getParam("image_size_presets");let d=[];return s&&(d=(0,u.imageSizePresetButtons)(t,s)),t.ui.registry.addContextToolbar("ssmedia",{predicate:e=>t.dom.is(e,h),position:"node",scope:"node",items:`${d.join(" ")} | ssmediaedit ssmediadelete`}),t.on("BeforeExecCommand",(e=>{const i=e.command,n=e.ui,a=e.value;"mceEditImage"!==i&&"mceImage"!==i||(e.preventDefault(),t.execCommand("ssmedia",n,a))})),t.on("GetContent",(t=>{const e=(0,n.default)(`
${t.content}
`);e.find(h).add(e.filter(h)).each((function(){const t=(0,n.default)(this),e={src:t.attr("src"),id:t.data("id"),width:t.attr("width"),height:t.attr("height"),class:t.attr("class"),title:t.attr("title"),alt:t.attr("alt"),loading:t.data("loading")},i=l.default.serialise({name:"image",properties:(0,l.sanitiseShortCodeProperties)(e),wrapped:!1});t.replaceWith(i)})),t.content="",e.each((function(){void 0!==this.innerHTML&&(t.content+=this.innerHTML)}))})),t.on("BeforeSetContent",(t=>{let e=t.content,i=l.default.match("image",!1,e);for(;i;){const t=i.properties,a=(0,n.default)("").attr(Object.assign({},t,{id:void 0,"data-id":t.id,"data-shortcode":"image","data-loading":t.loading})).addClass("ss-htmleditorfield-file image");e=e.replace(i.original,(0,n.default)("
").append(a).html()),i=l.default.match("image",!1,e)}t.content=e})),{getMetadata(){return{name:"Silverstripe Media",url:"https://docs.silverstripe.org/en/4/developer_guides/forms/field_types/htmleditorfield"}}}}};tinymce.PluginManager.add("ssmedia",(e=>t.init(e)))})(),n.default.entwine("ss",(t=>{t(".js-injector-boot #insert-media-react__dialog-wrapper").entwine({Element:null,Data:{},ReactRoot:null,onunmatch(){this._clearModal()},_clearModal(){const t=this.getReactRoot();t&&(t.unmount(),this.setReactRoot(null))},open(){const{dispatch:t}=s.default.reducer.store;t(c.initFormStack("insert-media","admin"));const e=tinymce.activeEditor.getParam("image_size_presets");t(c.defineImageSizePresets(e)),this._renderModal(!0)},close(){const{dispatch:t}=s.default.reducer.store;t(c.reset()),this._renderModal(!1)},_renderModal(t){var e=this;const{url:i,...n}=this.getOriginalAttributes(),a=n.hasOwnProperty("ID")&&null!==n.ID,s=this.getFolderId(),d=this.getElement().getEditor(),l=d.getInstance().selection,c=d.getSelection(),u=l.getNode().tagName,m="A"!==u&&("IMG"===u||""===c.trim());let g=this.getReactRoot();g||(g=(0,o.createRoot)(this[0])),g.render(r.default.createElement(f,{title:!1,isOpen:t,folderId:s,onInsert:function(){return e._handleInsert(...arguments)},onClosed:()=>this.close(),bodyClassName:"modal__dialog",className:"insert-media-react__dialog-wrapper",requireLinkText:m,fileAttributes:n,fileSelected:a})),this.setReactRoot(g)},_handleInsert(t,e){let i=!1;this.setData(Object.assign({},t,e));try{let t=null;if(t=e?e.category:"image","image"===t)i=this.insertImage();else i=this.insertFile()}catch(t){this.statusMessage(t,"bad")}return i&&this.close(),Promise.resolve()},getFolderId(){const t=this.getElement();if(!t)return null;const e=Number(t.data("config").upload_folder_id);return isNaN(e)?null:e},getOriginalAttributes(){const e=this.getElement();if(!e)return{};const i=e.getEditor().getSelectedNode();if(!i)return{};const n=t(i),a=(n.attr("href")||"").split("#");if(a[0]){const t=l.default.match("file_link",!1,a[0]);if(t)return{ID:t.properties.id?parseInt(t.properties.id,10):0,Anchor:a[1]||"",Description:n.attr("title"),TargetBlank:!!n.attr("target")}}const r=n.parent(".captionImage").find(".caption"),o={url:n.attr("src"),AltText:n.attr("alt"),Width:n.attr("width"),Height:n.attr("height"),Loading:n.attr("data-loading"),TitleTooltip:n.attr("title"),Alignment:this.findPosition(n.attr("class")),Caption:r.text(),ID:n.attr("data-id")};return["Width","Height","ID"].forEach((t=>{o[t]="string"==typeof o[t]?parseInt(o[t],10):null})),o},findPosition(t){if("string"!=typeof t)return"";const e=t.split(" ");return["leftAlone","center","rightAlone","left","right"].find((t=>e.indexOf(t)>-1))},getAttributes(){const t=this.getData();return{src:t.url,alt:t.AltText,width:t.Width,height:t.Height,title:t.TitleTooltip,class:t.Alignment,"data-id":t.ID,"data-shortcode":"image","data-loading":t.Loading}},getExtraData(){const t=this.getData();return{CaptionText:t&&t.Caption}},insertFile(){const e=this.getData(),i=this.getElement().getEditor(),n=t(i.getSelectedNode()),a=l.default.serialise({name:"file_link",properties:{id:e.ID}},!0);let r=i.getSelection()||e.Text||e.filename;n.is("a")&&n.html()&&(r="");const o={href:a,target:e.TargetBlank?"_blank":"",title:e.Description};if(n.is("img")){r=e.Text||e.filename;const a=t("").attr(o).text(r);n.replaceWith(a),i.addUndo(),i.repaint()}else this.insertLinkInEditor(o,r);return!0},insertImage(){const e=this.getElement();if(!e)return!1;const i=e.getEditor();if(!i)return!1;const n=t(i.getSelectedNode()),a=this.getAttributes(),r=this.getExtraData();let o=n&&n.is("img,a")?n:null;o&&o.parent().is(".captionImage")&&(o=o.parent());const s=n&&n.is("img")?n:t("");s.attr(a).addClass("ss-htmleditorfield-file image");let d=s.parent(".captionImage"),l=d.find(".caption");r.CaptionText?(d.length||(d=t("
")),d.attr("class",`captionImage ${a.class}`).removeAttr("data-mce-style").width(a.width),l.length||(l=t('

').appendTo(d)),l.attr("class",`caption ${a.class}`).text(r.CaptionText)):(d=null,l=null);const c=d||s;return o&&o.not(c).length&&o.replaceWith(c),d&&d.prepend(s),o||(i.repaint(),i.insertContent(t("
").append(c).html(),{skip_undo:1})),i.addUndo(),i.repaint(),!0},statusMessage(e,i){const a=t("
").text(e).html();n.default.noticeAdd({text:a,type:i,stayTime:5e3,inEffect:{left:"0",opacity:"show"}})}})}))}(); \ No newline at end of file diff --git a/client/dist/js/bundle.js b/client/dist/js/bundle.js new file mode 100644 index 00000000..1c5bbdbb --- /dev/null +++ b/client/dist/js/bundle.js @@ -0,0 +1 @@ +(function(){var __webpack_modules__={38:function(e,t,n){"use strict";var r,o=(r=n(121))&&r.__esModule?r:{default:r};document.addEventListener("DOMContentLoaded",(()=>{(0,o.default)()}))},121:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=i(n(207)),o=i(n(517));function i(e){return e&&e.__esModule?e:{default:e}}t.default=()=>{r.default.component.registerMany({TinyMceHtmlEditorField:o.default})}},160:function(e,t,n){"use strict";e.exports=function(){if("object"==typeof globalThis)return globalThis;var e;try{e=this||new Function("return this")()}catch(e){if("object"==typeof window)return window;if("object"==typeof self)return self;if(void 0!==n.g)return n.g}return e}()},207:function(e){"use strict";e.exports=Injector},227:function(__unused_webpack_module,__unused_webpack_exports,__webpack_require__){"use strict";var _jquery=_interopRequireDefault(__webpack_require__(669)),_lodash=_interopRequireDefault(__webpack_require__(912));function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}const ss=void 0!==window.ss?window.ss:{};ss.editorWrappers.tinyMCE=function(){let editorID;return{init(e){editorID=e,this.create()},destroy(){tinymce.EditorManager.execCommand("mceRemoveEditor",!1,editorID)},getInstance(){return tinymce.EditorManager.get(editorID)},onopen(){},onclose(){},setHeight(e,t){if(void 0!==t&&e.target&&e.target.iframeElement){e.target.iframeElement.height=""!==t?"auto":t;const n=e.target.iframeElement.closest(".tox-sidebar-wrap");n&&(n.style.height=t)}},getRawConfig(){const e=`#${editorID}`;return(0,_jquery.default)(e).data("config")},getConfig(){const e=this.getRawConfig(),t=e.row_height?e.row_height:void 0,n=this;return e.selector=`#${editorID}`,e.setup=function(e){e.on("change",(()=>{n.save()})),e.on("init",(e=>{n.setHeight(e,t)})),e.on("ResizeEditor",(e=>{n.setHeight(e,"")}))},e},save(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};const t=this.getInstance();t.save(),e.silent||((0,_jquery.default)(t.getElement()).trigger("change"),t.getElement().dispatchEvent(new Event("input",{bubbles:!0})))},create(){let e,t,n;function r(r){const o=r.target;!e||(new Date-e)/100>500?(n=(0,_jquery.default)(o).scrollTop(),(0,_jquery.default)(".mce-floatpanel").css("opacity","0")):window.clearTimeout(t),e=new Date,t=window.setTimeout((()=>function(t,n){const r=(0,_jquery.default)(t).scrollTop();(0,_jquery.default)(".mce-floatpanel").each(((e,t)=>{const o=parseFloat(t.style.top);(0,_jquery.default)(t).css("top",o-(r-n)+"px")})),(0,_jquery.default)(".mce-floatpanel").css("opacity","1"),e=void 0}(o,n)),500)}const o=this.getConfig();void 0!==o.baseURL&&(tinymce.EditorManager.baseURL=o.baseURL),o.skin=o.skin||"silverstripe",tinymce.init(o).then((e=>{if(e.length>0&&e[0].container){(0,_jquery.default)(e[0].container).closest(".panel--scrollable").on("scroll",(e=>r(e)))}}))},repaint(){},isDirty(){return this.getInstance().isDirty()},prepValueForChangeTracker(e){const t=_jquery.default.extend({forced_root_block:"p"},this.getRawConfig()),n=tinymce.html.Serializer(t),r=tinymce.html.DomParser(t);return n.serialize(r.parse(e))},getContent(){return this.getInstance().getContent()},getSelection(){return this.getInstance().selection.getSel().toString()||""},selectByCssSelector(e){const t=this.getInstance().getDoc(),n=t.getSelection(),r=new Range;let o=!1;(0,_jquery.default)(t).find(e).each((function(){o||(r.selectNode(this),n.removeAllRanges(),n.addRange(r),o=!0)}))},getDOM(){return this.getInstance().getElement()},getContainer(){return this.getInstance().getContainer()},getSelectedNode(){return this.getInstance().selection.getNode()},selectNode(e){this.getInstance().selection.select(e)},setContent(e,t){this.getInstance().setContent(e,t)},insertContent(e,t){this.getInstance().insertContent(e,t)},replaceContent(e,t){this.getInstance().execCommand("mceReplaceContent",!1,e,t)},insertLink(e,t,n){if(n){n=n.replaceAll("<","<").replaceAll(">",">");const t=this.getInstance().dom.create("a",e,n);this.getInstance().selection.setNode(t)}else this.getInstance().execCommand("mceInsertLink",!1,e,t)},removeLink(){this.getInstance().execCommand("unlink",!1)},cleanLink(href,node){const settings=this.getConfig,cb=settings.urlconverter_callback,cu=tinyMCE.settings.convert_urls;return cb&&(href=eval(`${cb}(href, node, true);`)),cu&&href.match(new RegExp(`^${(0,_lodash.default)(tinyMCE.settings.document_base_url)}(.*)$`))&&(href=RegExp.$1),href.match(/^javascript:\s*mctmp/)&&(href=""),href},createBookmark(){return this.getInstance().selection.getBookmark()},moveToBookmark(e){this.getInstance().selection.moveToBookmark(e),this.getInstance().focus()},blur(){this.getInstance().selection.collapse()},addUndo(){this.getInstance().undoManager.add()}}},ss.editorWrappers.default=ss.editorWrappers.tinyMCE},256:function(e,t,n){var r=n(671),o=n(160);void 0===o.TinyMCEActionRegistrar&&(o.TinyMCEActionRegistrar=r),e.exports=r},517:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.Component=void 0;var r=c(n(594)),o=c(n(921)),i=n(556),s=c(n(623));function c(e){return e&&e.__esModule?e:{default:e}}class a extends i.Component{constructor(e){super(e),this.state={isReady:!e.data.editorjs},this.inputRef=null,this.handleReady=this.handleReady.bind(this)}getInputProps(){return{...super.getInputProps(),...this.props.data.attributes,innerRef:e=>{this.inputRef=e}}}getEditorElement(){return document.getElementById(this.getInputProps().id)}getEditor(){return window.TinyMCE&&window.TinyMCE.get(this.getInputProps().id)}handleReady(){!window.TinyMCE&&window.tinymce&&(window.TinyMCE=window.tinymce),this.setState({isReady:!0})}registerChangeListener(){const e=this.getEditorElement();this.getEditor().on("change keyup",(()=>{super.handleChange({target:e})}))}renderDependencyScript(){return window.tinymce||window.TinyMCE?(setTimeout((()=>{this.handleReady()}),0),null):r.default.createElement(o.default,{url:this.props.data.editorjs,onLoad:this.handleReady})}render(){return this.state.isReady?super.render():this.renderDependencyScript()}componentDidUpdate(e,t){const{isReady:n}=this.state;if(!n)return;n!==t.isReady&&setTimeout((()=>{const{document:e,jQuery:t}=window,n=t?t.Event("EntwineElementsAdded"):new Event("noop"),r=this.getEditorElement();n.targets=[r],t&&t(e).triggerHandler(n),this.registerChangeListener()}),1);const{value:r}=this.props;if(r!==e.value){const e=new Event("change",{bubbles:!0});e.simulated=!0,e.value=r,this.inputRef.dispatchEvent(e)}}componentWillUnmount(){if(!this.state.isReady)return;const{document:e,jQuery:t}=window,n=t?t.Event("EntwineElementsRemoved"):new Event("noop"),r=this.getEditorElement(),o=this.getEditor();o&&o.save(),n.targets=[r],super.handleChange({target:r}),t&&t(e).triggerHandler(n)}}t.Component=a;t.default=(0,s.default)(a)},556:function(e){"use strict";e.exports=TextField},594:function(e){"use strict";e.exports=React},623:function(e){"use strict";e.exports=FieldHolder},669:function(e){"use strict";e.exports=jQuery},671:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;const n=(e,t)=>e?`${e}.${t}`:t;window.ss=window.ss||{},window.ss.tinymceactions=window.ss.tinymceactions||new class{constructor(){this.actions={},this.editorCommandsToUrlTestsMap={},this.defaultCommand="sslinkexternal"}addAction(e,t,r){const o=t.priority||50,i=n(r,e),s=this.getActions(e,r,!0);return t.type="menuitem",t.hasOwnProperty("onclick")&&(t.onAction=t.onclick,delete t.onclick),s.find((e=>t.text===e.text))||(this.actions[i]=[...this.getActions(e,r,!1),{...t,priority:o}]),this}getActions(e,t){let r=t&&!(!(arguments.length>2&&void 0!==arguments[2])||arguments[2])||!this.actions[e]?[]:this.actions[e];const o=n(t,e);return t&&this.actions[o]&&(r=[...r,...this.actions[o]]),r}getSortedActions(e,t){let n=!(arguments.length>2&&void 0!==arguments[2])||arguments[2];return this.getActions(e,t,n).sort(((e,t)=>{const n=t.priority-e.priority;return n||(e.text.toLocaleLowerCase()>t.text.toLocaleLowerCase()?1:-1)}))}addCommandWithUrlTest(e,t){return this.editorCommandsToUrlTestsMap[e]=t,this}setDefaultCommand(e){return this.defaultCommand=e,this}getDefaultCommand(){return this.defaultCommand}getEditorCommandFromUrl(e){let t=this.getDefaultCommand();const n=Object.keys(this.editorCommandsToUrlTestsMap).find((t=>this.editorCommandsToUrlTestsMap[t]&&this.editorCommandsToUrlTestsMap[t].test(e)));return n&&(t=n),t}};t.default=window.ss.tinymceactions},912:function(e,t,n){var r="[object Symbol]",o=/[\\^$.*+?()[\]{}|]/g,i=RegExp(o.source),s="object"==typeof n.g&&n.g&&n.g.Object===Object&&n.g,c="object"==typeof self&&self&&self.Object===Object&&self,a=s||c||Function("return this")(),u=Object.prototype.toString,d=a.Symbol,l=d?d.prototype:void 0,p=l?l.toString:void 0;function f(e){if("string"==typeof e)return e;if(function(e){return"symbol"==typeof e||function(e){return!!e&&"object"==typeof e}(e)&&u.call(e)==r}(e))return p?p.call(e):"";var t=e+"";return"0"==t&&1/e==-1/0?"-0":t}e.exports=function(e){var t;return(e=null==(t=e)?"":f(t))&&i.test(e)?e.replace(o,"\\$&"):e}},921:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r,o=function(){function e(e,t){for(var n=0;n` and `` elements will be converted to more restrictive alternatives, namely `` for image MIME types, `