Updated Drupal to 8.6. This goes with the following updates because it's possible...
[yaffs-website] / web / core / modules / image / js / editors / image.es6.js
index 3621ff97aca27632ef8f66e645e3d4ffc8f19718..b43f75f0147f029a34c50b41ec3812d5551e6e06 100644 (file)
  * Drag+drop based in-place editor for images.
  */
 
-(function ($, _, Drupal) {
-  Drupal.quickedit.editors.image = Drupal.quickedit.EditorView.extend(/** @lends Drupal.quickedit.editors.image# */{
-
-    /**
-     * @constructs
-     *
-     * @augments Drupal.quickedit.EditorView
-     *
-     * @param {object} options
-     *   Options for the image editor.
-     */
-    initialize(options) {
-      Drupal.quickedit.EditorView.prototype.initialize.call(this, options);
-      // Set our original value to our current HTML (for reverting).
-      this.model.set('originalValue', this.$el.html().trim());
-      // $.val() callback function for copying input from our custom form to
-      // the Quick Edit Field Form.
-      this.model.set('currentValue', function (index, value) {
-        const matches = $(this).attr('name').match(/(alt|title)]$/);
-        if (matches) {
-          const name = matches[1];
-          const $toolgroup = $(`#${options.fieldModel.toolbarView.getMainWysiwygToolgroupId()}`);
-          const $input = $toolgroup.find(`.quickedit-image-field-info input[name="${name}"]`);
-          if ($input.length) {
-            return $input.val();
-          }
-        }
-      });
-    },
-
-    /**
-     * @inheritdoc
-     *
-     * @param {Drupal.quickedit.FieldModel} fieldModel
-     *   The field model that holds the state.
-     * @param {string} state
-     *   The state to change to.
-     * @param {object} options
-     *   State options, if needed by the state change.
-     */
-    stateChange(fieldModel, state, options) {
-      const from = fieldModel.previous('state');
-      switch (state) {
-        case 'inactive':
-          break;
-
-        case 'candidate':
-          if (from !== 'inactive') {
-            this.$el.find('.quickedit-image-dropzone').remove();
-            this.$el.removeClass('quickedit-image-element');
-          }
-          if (from === 'invalid') {
-            this.removeValidationErrors();
+(function($, _, Drupal) {
+  Drupal.quickedit.editors.image = Drupal.quickedit.EditorView.extend(
+    /** @lends Drupal.quickedit.editors.image# */ {
+      /**
+       * @constructs
+       *
+       * @augments Drupal.quickedit.EditorView
+       *
+       * @param {object} options
+       *   Options for the image editor.
+       */
+      initialize(options) {
+        Drupal.quickedit.EditorView.prototype.initialize.call(this, options);
+        // Set our original value to our current HTML (for reverting).
+        this.model.set('originalValue', this.$el.html().trim());
+        // $.val() callback function for copying input from our custom form to
+        // the Quick Edit Field Form.
+        this.model.set('currentValue', function(index, value) {
+          const matches = $(this)
+            .attr('name')
+            .match(/(alt|title)]$/);
+          if (matches) {
+            const name = matches[1];
+            const $toolgroup = $(
+              `#${options.fieldModel.toolbarView.getMainWysiwygToolgroupId()}`,
+            );
+            const $input = $toolgroup.find(
+              `.quickedit-image-field-info input[name="${name}"]`,
+            );
+            if ($input.length) {
+              return $input.val();
+            }
           }
-          break;
-
-        case 'highlighted':
-          break;
+        });
+      },
+
+      /**
+       * @inheritdoc
+       *
+       * @param {Drupal.quickedit.FieldModel} fieldModel
+       *   The field model that holds the state.
+       * @param {string} state
+       *   The state to change to.
+       * @param {object} options
+       *   State options, if needed by the state change.
+       */
+      stateChange(fieldModel, state, options) {
+        const from = fieldModel.previous('state');
+        switch (state) {
+          case 'inactive':
+            break;
+
+          case 'candidate':
+            if (from !== 'inactive') {
+              this.$el.find('.quickedit-image-dropzone').remove();
+              this.$el.removeClass('quickedit-image-element');
+            }
+            if (from === 'invalid') {
+              this.removeValidationErrors();
+            }
+            break;
 
-        case 'activating':
-          // Defer updating the field model until the current state change has
-          // propagated, to not trigger a nested state change event.
-          _.defer(() => {
-            fieldModel.set('state', 'active');
-          });
-          break;
+          case 'highlighted':
+            break;
 
-        case 'active': {
-          const self = this;
+          case 'activating':
+            // Defer updating the field model until the current state change has
+            // propagated, to not trigger a nested state change event.
+            _.defer(() => {
+              fieldModel.set('state', 'active');
+            });
+            break;
 
-          // Indicate that this element is being edited by Quick Edit Image.
-          this.$el.addClass('quickedit-image-element');
+          case 'active': {
+            const self = this;
 
-          // Render our initial dropzone element. Once the user reverts changes
-          // or saves a new image, this element is removed.
-          const $dropzone = this.renderDropzone('upload', Drupal.t('Drop file here or click to upload'));
+            // Indicate that this element is being edited by Quick Edit Image.
+            this.$el.addClass('quickedit-image-element');
 
-          $dropzone.on('dragenter', function (e) {
-            $(this).addClass('hover');
-          });
-          $dropzone.on('dragleave', function (e) {
-            $(this).removeClass('hover');
-          });
+            // Render our initial dropzone element. Once the user reverts changes
+            // or saves a new image, this element is removed.
+            const $dropzone = this.renderDropzone(
+              'upload',
+              Drupal.t('Drop file here or click to upload'),
+            );
 
-          $dropzone.on('drop', function (e) {
-            // Only respond when a file is dropped (could be another element).
-            if (e.originalEvent.dataTransfer && e.originalEvent.dataTransfer.files.length) {
+            $dropzone.on('dragenter', function(e) {
+              $(this).addClass('hover');
+            });
+            $dropzone.on('dragleave', function(e) {
               $(this).removeClass('hover');
-              self.uploadImage(e.originalEvent.dataTransfer.files[0]);
-            }
-          });
-
-          $dropzone.on('click', (e) => {
-            // Create an <input> element without appending it to the DOM, and
-            // trigger a click event. This is the easiest way to arbitrarily
-            // open the browser's upload dialog.
-            $('<input type="file">')
-              .trigger('click')
-              .on('change', function () {
-                if (this.files.length) {
-                  self.uploadImage(this.files[0]);
-                }
-              });
-          });
+            });
 
-          // Prevent the browser's default behavior when dragging files onto
-          // the document (usually opens them in the same tab).
-          $dropzone.on('dragover dragenter dragleave drop click', (e) => {
-            e.preventDefault();
-            e.stopPropagation();
-          });
+            $dropzone.on('drop', function(e) {
+              // Only respond when a file is dropped (could be another element).
+              if (
+                e.originalEvent.dataTransfer &&
+                e.originalEvent.dataTransfer.files.length
+              ) {
+                $(this).removeClass('hover');
+                self.uploadImage(e.originalEvent.dataTransfer.files[0]);
+              }
+            });
 
-          this.renderToolbar(fieldModel);
-          break;
-        }
+            $dropzone.on('click', e => {
+              // Create an <input> element without appending it to the DOM, and
+              // trigger a click event. This is the easiest way to arbitrarily
+              // open the browser's upload dialog.
+              $('<input type="file">')
+                .trigger('click')
+                .on('change', function() {
+                  if (this.files.length) {
+                    self.uploadImage(this.files[0]);
+                  }
+                });
+            });
 
-        case 'changed':
-          break;
+            // Prevent the browser's default behavior when dragging files onto
+            // the document (usually opens them in the same tab).
+            $dropzone.on('dragover dragenter dragleave drop click', e => {
+              e.preventDefault();
+              e.stopPropagation();
+            });
 
-        case 'saving':
-          if (from === 'invalid') {
-            this.removeValidationErrors();
+            this.renderToolbar(fieldModel);
+            break;
           }
 
-          this.save(options);
-          break;
+          case 'changed':
+            break;
 
-        case 'saved':
-          break;
+          case 'saving':
+            if (from === 'invalid') {
+              this.removeValidationErrors();
+            }
 
-        case 'invalid':
-          this.showValidationErrors();
-          break;
-      }
-    },
+            this.save(options);
+            break;
 
-    /**
-     * Validates/uploads a given file.
-     *
-     * @param {File} file
-     *   The file to upload.
-     */
-    uploadImage(file) {
-      // Indicate loading by adding a special class to our icon.
-      this.renderDropzone('upload loading', Drupal.t('Uploading <i>@file</i>…', { '@file': file.name }));
-
-      // Build a valid URL for our endpoint.
-      const fieldID = this.fieldModel.get('fieldID');
-      const url = Drupal.quickedit.util.buildUrl(fieldID, Drupal.url('quickedit/image/upload/!entity_type/!id/!field_name/!langcode/!view_mode'));
-
-      // Construct form data that our endpoint can consume.
-      const data = new FormData();
-      data.append('files[image]', file);
-
-      // Construct a POST request to our endpoint.
-      const self = this;
-      this.ajax({
-        type: 'POST',
-        url,
-        data,
-        success(response) {
-          const $el = $(self.fieldModel.get('el'));
-          // Indicate that the field has changed - this enables the
-          // "Save" button.
-          self.fieldModel.set('state', 'changed');
-          self.fieldModel.get('entity').set('inTempStore', true);
-          self.removeValidationErrors();
-
-          // Replace our html with the new image. If we replaced our entire
-          // element with data.html, we would have to implement complicated logic
-          // like what's in Drupal.quickedit.AppView.renderUpdatedField.
-          const $content = $(response.html).closest('[data-quickedit-field-id]').children();
-          $el.empty().append($content);
-        },
-      });
-    },
+          case 'saved':
+            break;
 
-    /**
-     * Utility function to make an AJAX request to the server.
-     *
-     * In addition to formatting the correct request, this also handles error
-     * codes and messages by displaying them visually inline with the image.
-     *
-     * Drupal.ajax is not called here as the Form API is unused by this
-     * in-place editor, and our JSON requests/responses try to be
-     * editor-agnostic. Ideally similar logic and routes could be used by
-     * modules like CKEditor for drag+drop file uploads as well.
-     *
-     * @param {object} options
-     *   Ajax options.
-     * @param {string} options.type
-     *   The type of request (i.e. GET, POST, PUT, DELETE, etc.)
-     * @param {string} options.url
-     *   The URL for the request.
-     * @param {*} options.data
-     *   The data to send to the server.
-     * @param {function} options.success
-     *   A callback function used when a request is successful, without errors.
-     */
-    ajax(options) {
-      const defaultOptions = {
-        context: this,
-        dataType: 'json',
-        cache: false,
-        contentType: false,
-        processData: false,
-        error() {
-          this.renderDropzone('error', Drupal.t('A server error has occurred.'));
-        },
-      };
-
-      const ajaxOptions = $.extend(defaultOptions, options);
-      const successCallback = ajaxOptions.success;
-
-      // Handle the success callback.
-      ajaxOptions.success = function (response) {
-        if (response.main_error) {
-          this.renderDropzone('error', response.main_error);
-          if (response.errors.length) {
-            this.model.set('validationErrors', response.errors);
-          }
-          this.showValidationErrors();
-        }
-        else {
-          successCallback(response);
+          case 'invalid':
+            this.showValidationErrors();
+            break;
         }
-      };
-
-      $.ajax(ajaxOptions);
-    },
-
-    /**
-     * Renders our toolbar form for editing metadata.
-     *
-     * @param {Drupal.quickedit.FieldModel} fieldModel
-     *   The current Field Model.
-     */
-    renderToolbar(fieldModel) {
-      const $toolgroup = $(`#${fieldModel.toolbarView.getMainWysiwygToolgroupId()}`);
-      let $toolbar = $toolgroup.find('.quickedit-image-field-info');
-      if ($toolbar.length === 0) {
-        // Perform an AJAX request for extra image info (alt/title).
-        const fieldID = fieldModel.get('fieldID');
-        const url = Drupal.quickedit.util.buildUrl(fieldID, Drupal.url('quickedit/image/info/!entity_type/!id/!field_name/!langcode/!view_mode'));
+      },
+
+      /**
+       * Validates/uploads a given file.
+       *
+       * @param {File} file
+       *   The file to upload.
+       */
+      uploadImage(file) {
+        // Indicate loading by adding a special class to our icon.
+        this.renderDropzone(
+          'upload loading',
+          Drupal.t('Uploading <i>@file</i>…', { '@file': file.name }),
+        );
+
+        // Build a valid URL for our endpoint.
+        const fieldID = this.fieldModel.get('fieldID');
+        const url = Drupal.quickedit.util.buildUrl(
+          fieldID,
+          Drupal.url(
+            'quickedit/image/upload/!entity_type/!id/!field_name/!langcode/!view_mode',
+          ),
+        );
+
+        // Construct form data that our endpoint can consume.
+        const data = new FormData();
+        data.append('files[image]', file);
+
+        // Construct a POST request to our endpoint.
         const self = this;
-        self.ajax({
-          type: 'GET',
+        this.ajax({
+          type: 'POST',
           url,
+          data,
           success(response) {
-            $toolbar = $(Drupal.theme.quickeditImageToolbar(response));
-            $toolgroup.append($toolbar);
-            $toolbar.on('keyup paste', () => {
-              fieldModel.set('state', 'changed');
-            });
-            // Re-position the toolbar, which could have changed size.
-            fieldModel.get('entity').toolbarView.position();
+            const $el = $(self.fieldModel.get('el'));
+            // Indicate that the field has changed - this enables the
+            // "Save" button.
+            self.fieldModel.set('state', 'changed');
+            self.fieldModel.get('entity').set('inTempStore', true);
+            self.removeValidationErrors();
+
+            // Replace our html with the new image. If we replaced our entire
+            // element with data.html, we would have to implement complicated logic
+            // like what's in Drupal.quickedit.AppView.renderUpdatedField.
+            const $content = $(response.html)
+              .closest('[data-quickedit-field-id]')
+              .children();
+            $el.empty().append($content);
           },
         });
-      }
-    },
-
-    /**
-     * Renders our dropzone element.
-     *
-     * @param {string} state
-     *   The current state of our editor. Only used for visual styling.
-     * @param {string} text
-     *   The text to display in the dropzone area.
-     *
-     * @return {jQuery}
-     *   The rendered dropzone.
-     */
-    renderDropzone(state, text) {
-      let $dropzone = this.$el.find('.quickedit-image-dropzone');
-      // If the element already exists, modify its contents.
-      if ($dropzone.length) {
-        $dropzone
-          .removeClass('upload error hover loading')
-          .addClass(`.quickedit-image-dropzone ${state}`)
-          .children('.quickedit-image-text')
-            .html(text);
-      }
-      else {
-        $dropzone = $(Drupal.theme('quickeditImageDropzone', {
-          state,
-          text,
-        }));
-        this.$el.append($dropzone);
-      }
-
-      return $dropzone;
-    },
-
-    /**
-     * @inheritdoc
-     */
-    revert() {
-      this.$el.html(this.model.get('originalValue'));
-    },
+      },
+
+      /**
+       * Utility function to make an AJAX request to the server.
+       *
+       * In addition to formatting the correct request, this also handles error
+       * codes and messages by displaying them visually inline with the image.
+       *
+       * Drupal.ajax is not called here as the Form API is unused by this
+       * in-place editor, and our JSON requests/responses try to be
+       * editor-agnostic. Ideally similar logic and routes could be used by
+       * modules like CKEditor for drag+drop file uploads as well.
+       *
+       * @param {object} options
+       *   Ajax options.
+       * @param {string} options.type
+       *   The type of request (i.e. GET, POST, PUT, DELETE, etc.)
+       * @param {string} options.url
+       *   The URL for the request.
+       * @param {*} options.data
+       *   The data to send to the server.
+       * @param {function} options.success
+       *   A callback function used when a request is successful, without errors.
+       */
+      ajax(options) {
+        const defaultOptions = {
+          context: this,
+          dataType: 'json',
+          cache: false,
+          contentType: false,
+          processData: false,
+          error() {
+            this.renderDropzone(
+              'error',
+              Drupal.t('A server error has occurred.'),
+            );
+          },
+        };
 
-    /**
-     * @inheritdoc
-     */
-    getQuickEditUISettings() {
-      return { padding: false, unifiedToolbar: true, fullWidthToolbar: true, popup: false };
-    },
+        const ajaxOptions = $.extend(defaultOptions, options);
+        const successCallback = ajaxOptions.success;
 
-    /**
-     * @inheritdoc
-     */
-    showValidationErrors() {
-      const errors = Drupal.theme('quickeditImageErrors', {
-        errors: this.model.get('validationErrors'),
-      });
-      $(`#${this.fieldModel.toolbarView.getMainWysiwygToolgroupId()}`)
-        .append(errors);
-      this.getEditedElement()
-        .addClass('quickedit-validation-error');
-      // Re-position the toolbar, which could have changed size.
-      this.fieldModel.get('entity').toolbarView.position();
-    },
+        // Handle the success callback.
+        ajaxOptions.success = function(response) {
+          if (response.main_error) {
+            this.renderDropzone('error', response.main_error);
+            if (response.errors.length) {
+              this.model.set('validationErrors', response.errors);
+            }
+            this.showValidationErrors();
+          } else {
+            successCallback(response);
+          }
+        };
+
+        $.ajax(ajaxOptions);
+      },
+
+      /**
+       * Renders our toolbar form for editing metadata.
+       *
+       * @param {Drupal.quickedit.FieldModel} fieldModel
+       *   The current Field Model.
+       */
+      renderToolbar(fieldModel) {
+        const $toolgroup = $(
+          `#${fieldModel.toolbarView.getMainWysiwygToolgroupId()}`,
+        );
+        let $toolbar = $toolgroup.find('.quickedit-image-field-info');
+        if ($toolbar.length === 0) {
+          // Perform an AJAX request for extra image info (alt/title).
+          const fieldID = fieldModel.get('fieldID');
+          const url = Drupal.quickedit.util.buildUrl(
+            fieldID,
+            Drupal.url(
+              'quickedit/image/info/!entity_type/!id/!field_name/!langcode/!view_mode',
+            ),
+          );
+          const self = this;
+          self.ajax({
+            type: 'GET',
+            url,
+            success(response) {
+              $toolbar = $(Drupal.theme.quickeditImageToolbar(response));
+              $toolgroup.append($toolbar);
+              $toolbar.on('keyup paste', () => {
+                fieldModel.set('state', 'changed');
+              });
+              // Re-position the toolbar, which could have changed size.
+              fieldModel.get('entity').toolbarView.position();
+            },
+          });
+        }
+      },
+
+      /**
+       * Renders our dropzone element.
+       *
+       * @param {string} state
+       *   The current state of our editor. Only used for visual styling.
+       * @param {string} text
+       *   The text to display in the dropzone area.
+       *
+       * @return {jQuery}
+       *   The rendered dropzone.
+       */
+      renderDropzone(state, text) {
+        let $dropzone = this.$el.find('.quickedit-image-dropzone');
+        // If the element already exists, modify its contents.
+        if ($dropzone.length) {
+          $dropzone
+            .removeClass('upload error hover loading')
+            .addClass(`.quickedit-image-dropzone ${state}`)
+            .children('.quickedit-image-text')
+            .html(text);
+        } else {
+          $dropzone = $(
+            Drupal.theme('quickeditImageDropzone', {
+              state,
+              text,
+            }),
+          );
+          this.$el.append($dropzone);
+        }
 
-    /**
-     * @inheritdoc
-     */
-    removeValidationErrors() {
-      $(`#${this.fieldModel.toolbarView.getMainWysiwygToolgroupId()}`)
-        .find('.quickedit-image-errors').remove();
-      this.getEditedElement()
-        .removeClass('quickedit-validation-error');
+        return $dropzone;
+      },
+
+      /**
+       * @inheritdoc
+       */
+      revert() {
+        this.$el.html(this.model.get('originalValue'));
+      },
+
+      /**
+       * @inheritdoc
+       */
+      getQuickEditUISettings() {
+        return {
+          padding: false,
+          unifiedToolbar: true,
+          fullWidthToolbar: true,
+          popup: false,
+        };
+      },
+
+      /**
+       * @inheritdoc
+       */
+      showValidationErrors() {
+        const errors = Drupal.theme('quickeditImageErrors', {
+          errors: this.model.get('validationErrors'),
+        });
+        $(`#${this.fieldModel.toolbarView.getMainWysiwygToolgroupId()}`).append(
+          errors,
+        );
+        this.getEditedElement().addClass('quickedit-validation-error');
+        // Re-position the toolbar, which could have changed size.
+        this.fieldModel.get('entity').toolbarView.position();
+      },
+
+      /**
+       * @inheritdoc
+       */
+      removeValidationErrors() {
+        $(`#${this.fieldModel.toolbarView.getMainWysiwygToolgroupId()}`)
+          .find('.quickedit-image-errors')
+          .remove();
+        this.getEditedElement().removeClass('quickedit-validation-error');
+      },
     },
-
-  });
-}(jQuery, _, Drupal));
+  );
+})(jQuery, _, Drupal);