X-Git-Url: http://www.aleph1.co.uk/gitweb/?a=blobdiff_plain;ds=sidebyside;f=web%2Fcore%2Fmodules%2Fckeditor%2Fjs%2Fckeditor.es6.js;fp=web%2Fcore%2Fmodules%2Fckeditor%2Fjs%2Fckeditor.es6.js;h=21f6e4bb451d928a61d2a4e3351b88a69a3ce9f8;hb=9917807b03b64faf00f6a1f29dcb6eafc454efa5;hp=0000000000000000000000000000000000000000;hpb=aea91e65e895364e460983b890e295aa5d5540a5;p=yaffs-website diff --git a/web/core/modules/ckeditor/js/ckeditor.es6.js b/web/core/modules/ckeditor/js/ckeditor.es6.js new file mode 100644 index 000000000..21f6e4bb4 --- /dev/null +++ b/web/core/modules/ckeditor/js/ckeditor.es6.js @@ -0,0 +1,345 @@ +/** + * @file + * CKEditor implementation of {@link Drupal.editors} API. + */ + +(function (Drupal, debounce, CKEDITOR, $, displace, AjaxCommands) { + /** + * @namespace + */ + Drupal.editors.ckeditor = { + + /** + * Editor attach callback. + * + * @param {HTMLElement} element + * The element to attach the editor to. + * @param {string} format + * The text format for the editor. + * + * @return {bool} + * Whether the call to `CKEDITOR.replace()` created an editor or not. + */ + attach(element, format) { + this._loadExternalPlugins(format); + // Also pass settings that are Drupal-specific. + format.editorSettings.drupal = { + format: format.format, + }; + + // Set a title on the CKEditor instance that includes the text field's + // label so that screen readers say something that is understandable + // for end users. + const label = $(`label[for=${element.getAttribute('id')}]`).html(); + format.editorSettings.title = Drupal.t('Rich Text Editor, !label field', { '!label': label }); + + return !!CKEDITOR.replace(element, format.editorSettings); + }, + + /** + * Editor detach callback. + * + * @param {HTMLElement} element + * The element to detach the editor from. + * @param {string} format + * The text format used for the editor. + * @param {string} trigger + * The event trigger for the detach. + * + * @return {bool} + * Whether the call to `CKEDITOR.dom.element.get(element).getEditor()` + * found an editor or not. + */ + detach(element, format, trigger) { + const editor = CKEDITOR.dom.element.get(element).getEditor(); + if (editor) { + if (trigger === 'serialize') { + editor.updateElement(); + } + else { + editor.destroy(); + element.removeAttribute('contentEditable'); + } + } + return !!editor; + }, + + /** + * Reacts on a change in the editor element. + * + * @param {HTMLElement} element + * The element where the change occured. + * @param {function} callback + * Callback called with the value of the editor. + * + * @return {bool} + * Whether the call to `CKEDITOR.dom.element.get(element).getEditor()` + * found an editor or not. + */ + onChange(element, callback) { + const editor = CKEDITOR.dom.element.get(element).getEditor(); + if (editor) { + editor.on('change', debounce(() => { + callback(editor.getData()); + }, 400)); + + // A temporary workaround to control scrollbar appearance when using + // autoGrow event to control editor's height. + // @todo Remove when http://dev.ckeditor.com/ticket/12120 is fixed. + editor.on('mode', () => { + const editable = editor.editable(); + if (!editable.isInline()) { + editor.on('autoGrow', (evt) => { + const doc = evt.editor.document; + const scrollable = CKEDITOR.env.quirks ? doc.getBody() : doc.getDocumentElement(); + + if (scrollable.$.scrollHeight < scrollable.$.clientHeight) { + scrollable.setStyle('overflow-y', 'hidden'); + } + else { + scrollable.removeStyle('overflow-y'); + } + }, null, null, 10000); + } + }); + } + return !!editor; + }, + + /** + * Attaches an inline editor to a DOM element. + * + * @param {HTMLElement} element + * The element to attach the editor to. + * @param {object} format + * The text format used in the editor. + * @param {string} [mainToolbarId] + * The id attribute for the main editor toolbar, if any. + * @param {string} [floatedToolbarId] + * The id attribute for the floated editor toolbar, if any. + * + * @return {bool} + * Whether the call to `CKEDITOR.replace()` created an editor or not. + */ + attachInlineEditor(element, format, mainToolbarId, floatedToolbarId) { + this._loadExternalPlugins(format); + // Also pass settings that are Drupal-specific. + format.editorSettings.drupal = { + format: format.format, + }; + + const settings = $.extend(true, {}, format.editorSettings); + + // If a toolbar is already provided for "true WYSIWYG" (in-place editing), + // then use that toolbar instead: override the default settings to render + // CKEditor UI's top toolbar into mainToolbar, and don't render the bottom + // toolbar at all. (CKEditor doesn't need a floated toolbar.) + if (mainToolbarId) { + const settingsOverride = { + extraPlugins: 'sharedspace', + removePlugins: 'floatingspace,elementspath', + sharedSpaces: { + top: mainToolbarId, + }, + }; + + // Find the "Source" button, if any, and replace it with "Sourcedialog". + // (The 'sourcearea' plugin only works in CKEditor's iframe mode.) + let sourceButtonFound = false; + for (let i = 0; !sourceButtonFound && i < settings.toolbar.length; i++) { + if (settings.toolbar[i] !== '/') { + for (let j = 0; !sourceButtonFound && j < settings.toolbar[i].items.length; j++) { + if (settings.toolbar[i].items[j] === 'Source') { + sourceButtonFound = true; + // Swap sourcearea's "Source" button for sourcedialog's. + settings.toolbar[i].items[j] = 'Sourcedialog'; + settingsOverride.extraPlugins += ',sourcedialog'; + settingsOverride.removePlugins += ',sourcearea'; + } + } + } + } + + settings.extraPlugins += `,${settingsOverride.extraPlugins}`; + settings.removePlugins += `,${settingsOverride.removePlugins}`; + settings.sharedSpaces = settingsOverride.sharedSpaces; + } + + // CKEditor requires an element to already have the contentEditable + // attribute set to "true", otherwise it won't attach an inline editor. + element.setAttribute('contentEditable', 'true'); + + return !!CKEDITOR.inline(element, settings); + }, + + /** + * Loads the required external plugins for the editor. + * + * @param {object} format + * The text format used in the editor. + */ + _loadExternalPlugins(format) { + const externalPlugins = format.editorSettings.drupalExternalPlugins; + // Register and load additional CKEditor plugins as necessary. + if (externalPlugins) { + for (const pluginName in externalPlugins) { + if (externalPlugins.hasOwnProperty(pluginName)) { + CKEDITOR.plugins.addExternal(pluginName, externalPlugins[pluginName], ''); + } + } + delete format.editorSettings.drupalExternalPlugins; + } + }, + + }; + + Drupal.ckeditor = { + + /** + * Variable storing the current dialog's save callback. + * + * @type {?function} + */ + saveCallback: null, + + /** + * Open a dialog for a Drupal-based plugin. + * + * This dynamically loads jQuery UI (if necessary) using the Drupal AJAX + * framework, then opens a dialog at the specified Drupal path. + * + * @param {CKEditor} editor + * The CKEditor instance that is opening the dialog. + * @param {string} url + * The URL that contains the contents of the dialog. + * @param {object} existingValues + * Existing values that will be sent via POST to the url for the dialog + * contents. + * @param {function} saveCallback + * A function to be called upon saving the dialog. + * @param {object} dialogSettings + * An object containing settings to be passed to the jQuery UI. + */ + openDialog(editor, url, existingValues, saveCallback, dialogSettings) { + // Locate a suitable place to display our loading indicator. + let $target = $(editor.container.$); + if (editor.elementMode === CKEDITOR.ELEMENT_MODE_REPLACE) { + $target = $target.find('.cke_contents'); + } + + // Remove any previous loading indicator. + $target.css('position', 'relative').find('.ckeditor-dialog-loading').remove(); + + // Add a consistent dialog class. + const classes = dialogSettings.dialogClass ? dialogSettings.dialogClass.split(' ') : []; + classes.push('ui-dialog--narrow'); + dialogSettings.dialogClass = classes.join(' '); + dialogSettings.autoResize = window.matchMedia('(min-width: 600px)').matches; + dialogSettings.width = 'auto'; + + // Add a "Loadingâ¦" message, hide it underneath the CKEditor toolbar, + // create a Drupal.Ajax instance to load the dialog and trigger it. + const $content = $(`