diff --git a/core/assets/vendor/ckeditor/build-config.js b/core/assets/vendor/ckeditor/build-config.js index c4eda91..6b09b91 100644 --- a/core/assets/vendor/ckeditor/build-config.js +++ b/core/assets/vendor/ckeditor/build-config.js @@ -92,6 +92,7 @@ var CKBUILDER_CONFIG = { 'sharedspace' : 1, 'sourcedialog' : 1, 'widget' : 1, - 'image2' : 1 + 'image2' : 1, + 'uploadwidget' : 1 } }; diff --git a/core/modules/ckeditor/ckeditor.libraries.yml b/core/modules/ckeditor/ckeditor.libraries.yml index 902cb99..68ea65b 100644 --- a/core/modules/ckeditor/ckeditor.libraries.yml +++ b/core/modules/ckeditor/ckeditor.libraries.yml @@ -15,6 +15,12 @@ drupal.ckeditor: # Ensure to run after core/matchmedia. - core/matchmedia +drupal.ckeditor.plugins.drupalimage.drag-and-drop-uploads: + version: VERSION + css: + component: + css/plugins/drupalimage/ckeditor.drag-and-drop-uploads.css: {} + drupal.ckeditor.plugins.drupalimagecaption: version: VERSION css: diff --git a/core/modules/ckeditor/ckeditor.module b/core/modules/ckeditor/ckeditor.module index 115afe2..54f4793 100644 --- a/core/modules/ckeditor/ckeditor.module +++ b/core/modules/ckeditor/ckeditor.module @@ -65,6 +65,9 @@ function ckeditor_ckeditor_css_alter(array &$css, Editor $editor) { if ($editor->getFilterFormat()->filters('filter_caption')->status) { $css[] = drupal_get_path('module', 'filter') . '/css/filter.caption.css'; } + + // Add the drupalimage drag-and-drop CSS to enhance UX. + $css[] = drupal_get_path('module', 'ckeditor') . '/css/plugins/drupalimage/ckeditor.drag-and-drop-uploads.css'; } /** diff --git a/core/modules/ckeditor/css/plugins/drupalimage/ckeditor.drag-and-drop-uploads.css b/core/modules/ckeditor/css/plugins/drupalimage/ckeditor.drag-and-drop-uploads.css new file mode 100644 index 0000000..cc29f91 --- /dev/null +++ b/core/modules/ckeditor/css/plugins/drupalimage/ckeditor.drag-and-drop-uploads.css @@ -0,0 +1,6 @@ +/** + * Needed in CKEditor, to enhance the UX of the drag-and-drop image uploading. + */ +img[data-cke-drupalimage-preview] { + opacity: 0.3; +} diff --git a/core/modules/ckeditor/js/ckeditor.js b/core/modules/ckeditor/js/ckeditor.js index 497393f..a63744d 100644 --- a/core/modules/ckeditor/js/ckeditor.js +++ b/core/modules/ckeditor/js/ckeditor.js @@ -215,6 +215,7 @@ // Add a consistent dialog class. var classes = dialogSettings.dialogClass ? dialogSettings.dialogClass.split(' ') : []; + classes.push('ckeditor-drupal-dialog'); classes.push('ui-dialog--narrow'); dialogSettings.dialogClass = classes.join(' '); dialogSettings.autoResize = window.matchMedia('(min-width: 600px)').matches; @@ -248,7 +249,7 @@ }; // Moves the dialog to the top of the CKEDITOR stack. - $(window).on('dialogcreate', function (e, dialog, $element, settings) { + $(window).on('dialogcreate', function (e, dialog) { $('.ui-dialog--narrow').css('zIndex', CKEDITOR.config.baseFloatZIndex + 1); }); @@ -258,6 +259,13 @@ $(this).remove(); }); }); + // @fires event:editor:dialogopen + $(window).on('dialog:aftercreate', function (e, dialog, $element, settings) { + if (!$element.parent().hasClass('ckeditor-drupal-dialog')) { + return; + } + $(window).trigger('editor:dialogopen', [dialog, $element]); + }); // Respond to dialogs that are saved, sending data back to CKEditor. $(window).on('editor:dialogsave', function (e, values) { @@ -267,10 +275,16 @@ }); // Respond to dialogs that are closed, removing the current save handler. + // @fires event:editor:dialogclose $(window).on('dialog:afterclose', function (e, dialog, $element) { + if (!$element.parent().hasClass('ckeditor-drupal-dialog')) { + return; + } + if (Drupal.ckeditor.saveCallback) { Drupal.ckeditor.saveCallback = null; } + $(window).trigger('editor:dialogclose', [dialog, $element]); }); // Set the CKEditor cache-busting string to the same value as Drupal. diff --git a/core/modules/ckeditor/js/plugins/drupalimage/plugin.js b/core/modules/ckeditor/js/plugins/drupalimage/plugin.js index 0025664..5f34daa 100644 --- a/core/modules/ckeditor/js/plugins/drupalimage/plugin.js +++ b/core/modules/ckeditor/js/plugins/drupalimage/plugin.js @@ -18,7 +18,7 @@ 'use strict'; CKEDITOR.plugins.add('drupalimage', { - requires: 'image2', + requires: 'image2,uploadwidget', icons: 'drupalimage', hidpi: true, @@ -267,6 +267,61 @@ } }); + var fileTools = CKEDITOR.fileTools; + fileTools.addUploadWidget(editor, 'fileReader', { + loadMethod: 'load', + supportedTypes: /image\/(gif|png|jpg|jpeg)/, + fileToElement: function () { + var el = new CKEDITOR.dom.element('span'); + el.setText('Loading...'); + return el; + }, + onLoaded: function (loader) { + var el = new CKEDITOR.dom.element('img'); + el.setAttribute('data-cke-drupalimage-preview', true); + el.setAttribute('src', loader.data); + this.replaceWith(el.getOuterHtml()); + } + }); + + editor.on('paste', function (evt) { + var file = evt.data.dataTransfer.getFile(0); + + if (CKEDITOR.fileTools.isTypeSupported(file, /image\/(gif|png|jpg|jpeg)/)) { + var loader = editor.uploadRepository.create(file); + loader.load(); + + editor.execCommand('image'); + + $(window).one('editor:dialogopen', function (e, dialog, $element, settings) { + var form = $element.find('form.editor-image-dialog'); + var formData = new FormData(); + var uploadButtonId = '#' + form.find('[data-drupal-selector=edit-fid-upload-button]').attr('id'); + + var ajaxObject = getAjaxObject(uploadButtonId); + ajaxObject.beforeSerialize = function (element_settings, options) { + formData.append('files[fid]', file); + $.each(this.$form.serializeArray(), function (k, v) { + formData[v.name] = v.value; + formData.append(v.name, v.value); + }); + options.data = formData; + options.contentType = false; + options.processData = false; + }; + + ajaxObject.execute(); + }); + + $(window).one('editor:dialogclose', function (e, dialog, $element, settings) { + var preview = editor.document.findOne('[data-cke-drupalimage-preview]'); + if (preview) { + preview.remove(); + } + }); + } + }); + // Register the toolbar button. if (editor.ui.addButton) { editor.ui.addButton('DrupalImage', { @@ -363,6 +418,24 @@ return null; } + /** + * Gets a Drupal AJAX instance by selector id. + * + * @param {string} id + * The selector id. + * @returns {Object} + * The Drupal AJAX instance, or null. + */ + function getAjaxObject(id) { + var ajaxInstance = null; + $.each(Drupal.ajax.instances, function (k, v) { + if (v !== null && v.selector === id) { + return ajaxInstance = Drupal.ajax.instances[k]; + } + }); + return ajaxInstance; + } + // Expose an API for other plugins to interact with drupalimage widgets. CKEDITOR.plugins.drupalimage = { getFocusedWidget: getFocusedWidget diff --git a/core/modules/ckeditor/src/Plugin/Editor/CKEditor.php b/core/modules/ckeditor/src/Plugin/Editor/CKEditor.php index 170305d..641e607 100644 --- a/core/modules/ckeditor/src/Plugin/Editor/CKEditor.php +++ b/core/modules/ckeditor/src/Plugin/Editor/CKEditor.php @@ -365,6 +365,7 @@ public function getLangcodes() { public function getLibraries(Editor $editor) { $libraries = array( 'ckeditor/drupal.ckeditor', + 'ckeditor/drupal.ckeditor.plugins.drupalimage.drag-and-drop-uploads', ); // Get the required libraries for any enabled plugins.