Updated Drupal to 8.6. This goes with the following updates because it's possible...
[yaffs-website] / web / core / modules / quickedit / js / models / FieldModel.es6.js
index 0c01cc30240678fb10049246bcf2330d35effd5f..fbb510cf7fdfa81b0af46273f50b093577c831e1 100644 (file)
  * A Backbone Model for the state of an in-place editable field in the DOM.
  */
 
-(function (_, Backbone, Drupal) {
-  Drupal.quickedit.FieldModel = Drupal.quickedit.BaseModel.extend(/** @lends Drupal.quickedit.FieldModel# */{
-
-    /**
-     * @type {object}
-     */
-    defaults: /** @lends Drupal.quickedit.FieldModel# */{
-
+(function(_, Backbone, Drupal) {
+  Drupal.quickedit.FieldModel = Drupal.quickedit.BaseModel.extend(
+    /** @lends Drupal.quickedit.FieldModel# */ {
       /**
-       * The DOM element that represents this field. It may seem bizarre to have
-       * a DOM element in a Backbone Model, but we need to be able to map fields
-       * in the DOM to FieldModels in memory.
+       * @type {object}
        */
-      el: null,
+      defaults: /** @lends Drupal.quickedit.FieldModel# */ {
+        /**
+         * The DOM element that represents this field. It may seem bizarre to have
+         * a DOM element in a Backbone Model, but we need to be able to map fields
+         * in the DOM to FieldModels in memory.
+         */
+        el: null,
+
+        /**
+         * A field ID, of the form
+         * `<entity type>/<id>/<field name>/<language>/<view mode>`
+         *
+         * @example
+         * "node/1/field_tags/und/full"
+         */
+        fieldID: null,
+
+        /**
+         * The unique ID of this field within its entity instance on the page, of
+         * the form `<entity type>/<id>/<field name>/<language>/<view
+         * mode>[entity instance ID]`.
+         *
+         * @example
+         * "node/1/field_tags/und/full[0]"
+         */
+        id: null,
+
+        /**
+         * A {@link Drupal.quickedit.EntityModel}. Its "fields" attribute, which
+         * is a FieldCollection, is automatically updated to include this
+         * FieldModel.
+         */
+        entity: null,
+
+        /**
+         * This field's metadata as returned by the
+         * QuickEditController::metadata().
+         */
+        metadata: null,
+
+        /**
+         * Callback function for validating changes between states. Receives the
+         * previous state, new state, context, and a callback.
+         */
+        acceptStateChange: null,
+
+        /**
+         * A logical field ID, of the form
+         * `<entity type>/<id>/<field name>/<language>`, i.e. the fieldID without
+         * the view mode, to be able to identify other instances of the same
+         * field on the page but rendered in a different view mode.
+         *
+         * @example
+         * "node/1/field_tags/und".
+         */
+        logicalFieldID: null,
+
+        // The attributes below are stateful. The ones above will never change
+        // during the life of a FieldModel instance.
+
+        /**
+         * In-place editing state of this field. Defaults to the initial state.
+         * Possible values: {@link Drupal.quickedit.FieldModel.states}.
+         */
+        state: 'inactive',
+
+        /**
+         * The field is currently in the 'changed' state or one of the following
+         * states in which the field is still changed.
+         */
+        isChanged: false,
+
+        /**
+         * Is tracked by the EntityModel, is mirrored here solely for decorative
+         * purposes: so that FieldDecorationView.renderChanged() can react to it.
+         */
+        inTempStore: false,
+
+        /**
+         * The full HTML representation of this field (with the element that has
+         * the data-quickedit-field-id as the outer element). Used to propagate
+         * changes from this field to other instances of the same field storage.
+         */
+        html: null,
+
+        /**
+         * An object containing the full HTML representations (values) of other
+         * view modes (keys) of this field, for other instances of this field
+         * displayed in a different view mode.
+         */
+        htmlForOtherViewModes: null,
+      },
 
       /**
-       * A field ID, of the form
-       * `<entity type>/<id>/<field name>/<language>/<view mode>`
+       * State of an in-place editable field in the DOM.
        *
-       * @example
-       * "node/1/field_tags/und/full"
-       */
-      fieldID: null,
-
-      /**
-       * The unique ID of this field within its entity instance on the page, of
-       * the form `<entity type>/<id>/<field name>/<language>/<view
-       * mode>[entity instance ID]`.
+       * @constructs
        *
-       * @example
-       * "node/1/field_tags/und/full[0]"
-       */
-      id: null,
-
-      /**
-       * A {@link Drupal.quickedit.EntityModel}. Its "fields" attribute, which
-       * is a FieldCollection, is automatically updated to include this
-       * FieldModel.
+       * @augments Drupal.quickedit.BaseModel
+       *
+       * @param {object} options
+       *   Options for the field model.
        */
-      entity: null,
+      initialize(options) {
+        // Store the original full HTML representation of this field.
+        this.set('html', options.el.outerHTML);
+
+        // Enlist field automatically in the associated entity's field collection.
+        this.get('entity')
+          .get('fields')
+          .add(this);
+
+        // Automatically generate the logical field ID.
+        this.set(
+          'logicalFieldID',
+          this.get('fieldID')
+            .split('/')
+            .slice(0, 4)
+            .join('/'),
+        );
+
+        // Call Drupal.quickedit.BaseModel's initialize() method.
+        Drupal.quickedit.BaseModel.prototype.initialize.call(this, options);
+      },
 
       /**
-       * This field's metadata as returned by the
-       * QuickEditController::metadata().
+       * Destroys the field model.
+       *
+       * @param {object} options
+       *   Options for the field model.
        */
-      metadata: null,
+      destroy(options) {
+        if (this.get('state') !== 'inactive') {
+          throw new Error(
+            'FieldModel cannot be destroyed if it is not inactive state.',
+          );
+        }
+        Drupal.quickedit.BaseModel.prototype.destroy.call(this, options);
+      },
 
       /**
-       * Callback function for validating changes between states. Receives the
-       * previous state, new state, context, and a callback.
+       * @inheritdoc
        */
-      acceptStateChange: null,
+      sync() {
+        // We don't use REST updates to sync.
+      },
 
       /**
-       * A logical field ID, of the form
-       * `<entity type>/<id>/<field name>/<language>`, i.e. the fieldID without
-       * the view mode, to be able to identify other instances of the same
-       * field on the page but rendered in a different view mode.
+       * Validate function for the field model.
        *
-       * @example
-       * "node/1/field_tags/und".
+       * @param {object} attrs
+       *   The attributes changes in the save or set call.
+       * @param {object} options
+       *   An object with the following option:
+       * @param {string} [options.reason]
+       *   A string that conveys a particular reason to allow for an exceptional
+       *   state change.
+       * @param {Array} options.accept-field-states
+       *   An array of strings that represent field states that the entities must
+       *   be in to validate. For example, if `accept-field-states` is
+       *   `['candidate', 'highlighted']`, then all the fields of the entity must
+       *   be in either of these two states for the save or set call to
+       *   validate and proceed.
+       *
+       * @return {string}
+       *   A string to say something about the state of the field model.
        */
-      logicalFieldID: null,
-
-      // The attributes below are stateful. The ones above will never change
-      // during the life of a FieldModel instance.
+      validate(attrs, options) {
+        const current = this.get('state');
+        const next = attrs.state;
+        if (current !== next) {
+          // Ensure it's a valid state.
+          if (_.indexOf(this.constructor.states, next) === -1) {
+            return `"${next}" is an invalid state`;
+          }
+          // Check if the acceptStateChange callback accepts it.
+          if (!this.get('acceptStateChange')(current, next, options, this)) {
+            return 'state change not accepted';
+          }
+        }
+      },
 
       /**
-       * In-place editing state of this field. Defaults to the initial state.
-       * Possible values: {@link Drupal.quickedit.FieldModel.states}.
+       * Extracts the entity ID from this field's ID.
+       *
+       * @return {string}
+       *   An entity ID: a string of the format `<entity type>/<id>`.
        */
-      state: 'inactive',
+      getEntityID() {
+        return this.get('fieldID')
+          .split('/')
+          .slice(0, 2)
+          .join('/');
+      },
 
       /**
-       * The field is currently in the 'changed' state or one of the following
-       * states in which the field is still changed.
+       * Extracts the view mode ID from this field's ID.
+       *
+       * @return {string}
+       *   A view mode ID.
        */
-      isChanged: false,
+      getViewMode() {
+        return this.get('fieldID')
+          .split('/')
+          .pop();
+      },
 
       /**
-       * Is tracked by the EntityModel, is mirrored here solely for decorative
-       * purposes: so that FieldDecorationView.renderChanged() can react to it.
+       * Find other instances of this field with different view modes.
+       *
+       * @return {Array}
+       *   An array containing view mode IDs.
        */
-      inTempStore: false,
-
+      findOtherViewModes() {
+        const currentField = this;
+        const otherViewModes = [];
+        Drupal.quickedit.collections.fields
+          // Find all instances of fields that display the same logical field
+          // (same entity, same field, just a different instance and maybe a
+          // different view mode).
+          .where({ logicalFieldID: currentField.get('logicalFieldID') })
+          .forEach(field => {
+            // Ignore the current field and other fields with the same view mode.
+            if (
+              field !== currentField &&
+              field.get('fieldID') !== currentField.get('fieldID')
+            ) {
+              otherViewModes.push(field.getViewMode());
+            }
+          });
+        return otherViewModes;
+      },
+    },
+    /** @lends Drupal.quickedit.FieldModel */ {
       /**
-       * The full HTML representation of this field (with the element that has
-       * the data-quickedit-field-id as the outer element). Used to propagate
-       * changes from this field to other instances of the same field storage.
+       * Sequence of all possible states a field can be in during quickediting.
+       *
+       * @type {Array.<string>}
        */
-      html: null,
+      states: [
+        // The field associated with this FieldModel is linked to an EntityModel;
+        // the user can choose to start in-place editing that entity (and
+        // consequently this field). No in-place editor (EditorView) is associated
+        // with this field, because this field is not being in-place edited.
+        // This is both the initial (not yet in-place editing) and the end state
+        // (finished in-place editing).
+        'inactive',
+        // The user is in-place editing this entity, and this field is a
+        // candidate
+        // for in-place editing. In-place editor should not
+        // - Trigger: user.
+        // - Guarantees: entity is ready, in-place editor (EditorView) is
+        //   associated with the field.
+        // - Expected behavior: visual indicators
+        //   around the field indicate it is available for in-place editing, no
+        //   in-place editor presented yet.
+        'candidate',
+        // User is highlighting this field.
+        // - Trigger: user.
+        // - Guarantees: see 'candidate'.
+        // - Expected behavior: visual indicators to convey highlighting, in-place
+        //   editing toolbar shows field's label.
+        'highlighted',
+        // User has activated the in-place editing of this field; in-place editor
+        // is activating.
+        // - Trigger: user.
+        // - Guarantees: see 'candidate'.
+        // - Expected behavior: loading indicator, in-place editor is loading
+        //   remote data (e.g. retrieve form from back-end). Upon retrieval of
+        //   remote data, the in-place editor transitions the field's state to
+        //   'active'.
+        'activating',
+        // In-place editor has finished loading remote data; ready for use.
+        // - Trigger: in-place editor.
+        // - Guarantees: see 'candidate'.
+        // - Expected behavior: in-place editor for the field is ready for use.
+        'active',
+        // User has modified values in the in-place editor.
+        // - Trigger: user.
+        // - Guarantees: see 'candidate', plus in-place editor is ready for use.
+        // - Expected behavior: visual indicator of change.
+        'changed',
+        // User is saving changed field data in in-place editor to
+        // PrivateTempStore. The save mechanism of the in-place editor is called.
+        // - Trigger: user.
+        // - Guarantees: see 'candidate' and 'active'.
+        // - Expected behavior: saving indicator, in-place editor is saving field
+        //   data into PrivateTempStore. Upon successful saving (without
+        //   validation errors), the in-place editor transitions the field's state
+        //   to 'saved', but to 'invalid' upon failed saving (with validation
+        //   errors).
+        'saving',
+        // In-place editor has successfully saved the changed field.
+        // - Trigger: in-place editor.
+        // - Guarantees: see 'candidate' and 'active'.
+        // - Expected behavior: transition back to 'candidate' state because the
+        //   deed is done. Then: 1) transition to 'inactive' to allow the field
+        //   to be rerendered, 2) destroy the FieldModel (which also destroys
+        //   attached views like the EditorView), 3) replace the existing field
+        //   HTML with the existing HTML and 4) attach behaviors again so that the
+        //   field becomes available again for in-place editing.
+        'saved',
+        // In-place editor has failed to saved the changed field: there were
+        // validation errors.
+        // - Trigger: in-place editor.
+        // - Guarantees: see 'candidate' and 'active'.
+        // - Expected behavior: remain in 'invalid' state, let the user make more
+        //   changes so that he can save it again, without validation errors.
+        'invalid',
+      ],
 
       /**
-       * An object containing the full HTML representations (values) of other
-       * view modes (keys) of this field, for other instances of this field
-       * displayed in a different view mode.
+       * Indicates whether the 'from' state comes before the 'to' state.
+       *
+       * @param {string} from
+       *   One of {@link Drupal.quickedit.FieldModel.states}.
+       * @param {string} to
+       *   One of {@link Drupal.quickedit.FieldModel.states}.
+       *
+       * @return {bool}
+       *   Whether the 'from' state comes before the 'to' state.
        */
-      htmlForOtherViewModes: null,
-    },
-
-    /**
-     * State of an in-place editable field in the DOM.
-     *
-     * @constructs
-     *
-     * @augments Drupal.quickedit.BaseModel
-     *
-     * @param {object} options
-     *   Options for the field model.
-     */
-    initialize(options) {
-      // Store the original full HTML representation of this field.
-      this.set('html', options.el.outerHTML);
-
-      // Enlist field automatically in the associated entity's field collection.
-      this.get('entity').get('fields').add(this);
-
-      // Automatically generate the logical field ID.
-      this.set('logicalFieldID', this.get('fieldID').split('/').slice(0, 4).join('/'));
-
-      // Call Drupal.quickedit.BaseModel's initialize() method.
-      Drupal.quickedit.BaseModel.prototype.initialize.call(this, options);
-    },
-
-    /**
-     * Destroys the field model.
-     *
-     * @param {object} options
-     *   Options for the field model.
-     */
-    destroy(options) {
-      if (this.get('state') !== 'inactive') {
-        throw new Error('FieldModel cannot be destroyed if it is not inactive state.');
-      }
-      Drupal.quickedit.BaseModel.prototype.destroy.call(this, options);
-    },
-
-    /**
-     * @inheritdoc
-     */
-    sync() {
-      // We don't use REST updates to sync.
-
+      followsStateSequence(from, to) {
+        return _.indexOf(this.states, from) < _.indexOf(this.states, to);
+      },
     },
-
-    /**
-     * Validate function for the field model.
-     *
-     * @param {object} attrs
-     *   The attributes changes in the save or set call.
-     * @param {object} options
-     *   An object with the following option:
-     * @param {string} [options.reason]
-     *   A string that conveys a particular reason to allow for an exceptional
-     *   state change.
-     * @param {Array} options.accept-field-states
-     *   An array of strings that represent field states that the entities must
-     *   be in to validate. For example, if `accept-field-states` is
-     *   `['candidate', 'highlighted']`, then all the fields of the entity must
-     *   be in either of these two states for the save or set call to
-     *   validate and proceed.
-     *
-     * @return {string}
-     *   A string to say something about the state of the field model.
-     */
-    validate(attrs, options) {
-      const current = this.get('state');
-      const next = attrs.state;
-      if (current !== next) {
-        // Ensure it's a valid state.
-        if (_.indexOf(this.constructor.states, next) === -1) {
-          return `"${next}" is an invalid state`;
-        }
-        // Check if the acceptStateChange callback accepts it.
-        if (!this.get('acceptStateChange')(current, next, options, this)) {
-          return 'state change not accepted';
-        }
-      }
-    },
-
-    /**
-     * Extracts the entity ID from this field's ID.
-     *
-     * @return {string}
-     *   An entity ID: a string of the format `<entity type>/<id>`.
-     */
-    getEntityID() {
-      return this.get('fieldID').split('/').slice(0, 2).join('/');
-    },
-
-    /**
-     * Extracts the view mode ID from this field's ID.
-     *
-     * @return {string}
-     *   A view mode ID.
-     */
-    getViewMode() {
-      return this.get('fieldID').split('/').pop();
-    },
-
-    /**
-     * Find other instances of this field with different view modes.
-     *
-     * @return {Array}
-     *   An array containing view mode IDs.
-     */
-    findOtherViewModes() {
-      const currentField = this;
-      const otherViewModes = [];
-      Drupal.quickedit.collections.fields
-        // Find all instances of fields that display the same logical field
-        // (same entity, same field, just a different instance and maybe a
-        // different view mode).
-        .where({ logicalFieldID: currentField.get('logicalFieldID') })
-        .forEach((field) => {
-          // Ignore the current field and other fields with the same view mode.
-          if (field !== currentField && field.get('fieldID') !== currentField.get('fieldID')) {
-            otherViewModes.push(field.getViewMode());
-          }
-        });
-      return otherViewModes;
-    },
-
-  }, /** @lends Drupal.quickedit.FieldModel */{
-
-    /**
-     * Sequence of all possible states a field can be in during quickediting.
-     *
-     * @type {Array.<string>}
-     */
-    states: [
-      // The field associated with this FieldModel is linked to an EntityModel;
-      // the user can choose to start in-place editing that entity (and
-      // consequently this field). No in-place editor (EditorView) is associated
-      // with this field, because this field is not being in-place edited.
-      // This is both the initial (not yet in-place editing) and the end state
-      // (finished in-place editing).
-      'inactive',
-      // The user is in-place editing this entity, and this field is a
-      // candidate
-      // for in-place editing. In-place editor should not
-      // - Trigger: user.
-      // - Guarantees: entity is ready, in-place editor (EditorView) is
-      //   associated with the field.
-      // - Expected behavior: visual indicators
-      //   around the field indicate it is available for in-place editing, no
-      //   in-place editor presented yet.
-      'candidate',
-      // User is highlighting this field.
-      // - Trigger: user.
-      // - Guarantees: see 'candidate'.
-      // - Expected behavior: visual indicators to convey highlighting, in-place
-      //   editing toolbar shows field's label.
-      'highlighted',
-      // User has activated the in-place editing of this field; in-place editor
-      // is activating.
-      // - Trigger: user.
-      // - Guarantees: see 'candidate'.
-      // - Expected behavior: loading indicator, in-place editor is loading
-      //   remote data (e.g. retrieve form from back-end). Upon retrieval of
-      //   remote data, the in-place editor transitions the field's state to
-      //   'active'.
-      'activating',
-      // In-place editor has finished loading remote data; ready for use.
-      // - Trigger: in-place editor.
-      // - Guarantees: see 'candidate'.
-      // - Expected behavior: in-place editor for the field is ready for use.
-      'active',
-      // User has modified values in the in-place editor.
-      // - Trigger: user.
-      // - Guarantees: see 'candidate', plus in-place editor is ready for use.
-      // - Expected behavior: visual indicator of change.
-      'changed',
-      // User is saving changed field data in in-place editor to
-      // PrivateTempStore. The save mechanism of the in-place editor is called.
-      // - Trigger: user.
-      // - Guarantees: see 'candidate' and 'active'.
-      // - Expected behavior: saving indicator, in-place editor is saving field
-      //   data into PrivateTempStore. Upon successful saving (without
-      //   validation errors), the in-place editor transitions the field's state
-      //   to 'saved', but to 'invalid' upon failed saving (with validation
-      //   errors).
-      'saving',
-      // In-place editor has successfully saved the changed field.
-      // - Trigger: in-place editor.
-      // - Guarantees: see 'candidate' and 'active'.
-      // - Expected behavior: transition back to 'candidate' state because the
-      //   deed is done. Then: 1) transition to 'inactive' to allow the field
-      //   to be rerendered, 2) destroy the FieldModel (which also destroys
-      //   attached views like the EditorView), 3) replace the existing field
-      //   HTML with the existing HTML and 4) attach behaviors again so that the
-      //   field becomes available again for in-place editing.
-      'saved',
-      // In-place editor has failed to saved the changed field: there were
-      // validation errors.
-      // - Trigger: in-place editor.
-      // - Guarantees: see 'candidate' and 'active'.
-      // - Expected behavior: remain in 'invalid' state, let the user make more
-      //   changes so that he can save it again, without validation errors.
-      'invalid',
-    ],
-
-    /**
-     * Indicates whether the 'from' state comes before the 'to' state.
-     *
-     * @param {string} from
-     *   One of {@link Drupal.quickedit.FieldModel.states}.
-     * @param {string} to
-     *   One of {@link Drupal.quickedit.FieldModel.states}.
-     *
-     * @return {bool}
-     *   Whether the 'from' state comes before the 'to' state.
-     */
-    followsStateSequence(from, to) {
-      return _.indexOf(this.states, from) < _.indexOf(this.states, to);
-    },
-
-  });
+  );
 
   /**
    * @constructor
    *
    * @augments Backbone.Collection
    */
-  Drupal.quickedit.FieldCollection = Backbone.Collection.extend(/** @lends Drupal.quickedit.FieldCollection */{
-
-    /**
-     * @type {Drupal.quickedit.FieldModel}
-     */
-    model: Drupal.quickedit.FieldModel,
-  });
-}(_, Backbone, Drupal));
+  Drupal.quickedit.FieldCollection = Backbone.Collection.extend(
+    /** @lends Drupal.quickedit.FieldCollection */ {
+      /**
+       * @type {Drupal.quickedit.FieldModel}
+       */
+      model: Drupal.quickedit.FieldModel,
+    },
+  );
+})(_, Backbone, Drupal);