Chris@0: /**
Chris@0: * DO NOT EDIT THIS FILE.
Chris@0: * See the following change record for more information,
Chris@0: * https://www.drupal.org/node/2815083
Chris@0: * @preserve
Chris@0: **/
Chris@0:
Chris@0: (function (_, $, Backbone, Drupal) {
Chris@0: Drupal.quickedit.EntityModel = Drupal.quickedit.BaseModel.extend({
Chris@0: defaults: {
Chris@0: el: null,
Chris@0:
Chris@0: entityID: null,
Chris@0:
Chris@0: entityInstanceID: null,
Chris@0:
Chris@0: id: null,
Chris@0:
Chris@0: label: null,
Chris@0:
Chris@0: fields: null,
Chris@0:
Chris@0: isActive: false,
Chris@0:
Chris@0: inTempStore: false,
Chris@0:
Chris@0: isDirty: false,
Chris@0:
Chris@0: isCommitting: false,
Chris@0:
Chris@0: state: 'closed',
Chris@0:
Chris@0: fieldsInTempStore: [],
Chris@0:
Chris@0: reload: false
Chris@0: },
Chris@0:
Chris@0: initialize: function initialize() {
Chris@0: this.set('fields', new Drupal.quickedit.FieldCollection());
Chris@0:
Chris@0: this.listenTo(this, 'change:state', this.stateChange);
Chris@0:
Chris@0: this.listenTo(this.get('fields'), 'change:state', this.fieldStateChange);
Chris@0:
Chris@0: Drupal.quickedit.BaseModel.prototype.initialize.call(this);
Chris@0: },
Chris@0: stateChange: function stateChange(entityModel, state, options) {
Chris@0: var to = state;
Chris@0: switch (to) {
Chris@0: case 'closed':
Chris@0: this.set({
Chris@0: isActive: false,
Chris@0: inTempStore: false,
Chris@0: isDirty: false
Chris@0: });
Chris@0: break;
Chris@0:
Chris@0: case 'launching':
Chris@0: break;
Chris@0:
Chris@0: case 'opening':
Chris@0: entityModel.get('fields').each(function (fieldModel) {
Chris@0: fieldModel.set('state', 'candidate', options);
Chris@0: });
Chris@0: break;
Chris@0:
Chris@0: case 'opened':
Chris@0: this.set('isActive', true);
Chris@0: break;
Chris@0:
Chris@0: case 'committing':
Chris@14: {
Chris@14: var fields = this.get('fields');
Chris@0:
Chris@14: fields.chain().filter(function (fieldModel) {
Chris@14: return _.intersection([fieldModel.get('state')], ['active']).length;
Chris@14: }).each(function (fieldModel) {
Chris@14: fieldModel.set('state', 'candidate');
Chris@14: });
Chris@0:
Chris@14: fields.chain().filter(function (fieldModel) {
Chris@14: return _.intersection([fieldModel.get('state')], Drupal.quickedit.app.changedFieldStates).length;
Chris@14: }).each(function (fieldModel) {
Chris@14: fieldModel.set('state', 'saving');
Chris@14: });
Chris@14: break;
Chris@14: }
Chris@0:
Chris@0: case 'deactivating':
Chris@14: {
Chris@14: var changedFields = this.get('fields').filter(function (fieldModel) {
Chris@14: return _.intersection([fieldModel.get('state')], ['changed', 'invalid']).length;
Chris@0: });
Chris@0:
Chris@14: if ((changedFields.length || this.get('fieldsInTempStore').length) && !options.saved && !options.confirmed) {
Chris@14: this.set('state', 'opened', { confirming: true });
Chris@0:
Chris@14: _.defer(function () {
Chris@14: Drupal.quickedit.app.confirmEntityDeactivation(entityModel);
Chris@14: });
Chris@14: } else {
Chris@14: var invalidFields = this.get('fields').filter(function (fieldModel) {
Chris@14: return _.intersection([fieldModel.get('state')], ['invalid']).length;
Chris@14: });
Chris@14:
Chris@14: entityModel.set('reload', this.get('fieldsInTempStore').length || invalidFields.length);
Chris@14:
Chris@14: entityModel.get('fields').each(function (fieldModel) {
Chris@14: if (_.intersection([fieldModel.get('state')], ['candidate', 'highlighted']).length) {
Chris@14: fieldModel.trigger('change:state', fieldModel, fieldModel.get('state'), options);
Chris@14: } else {
Chris@14: fieldModel.set('state', 'candidate', options);
Chris@14: }
Chris@14: });
Chris@14: }
Chris@14: break;
Chris@0: }
Chris@0:
Chris@0: case 'closing':
Chris@0: options.reason = 'stop';
Chris@0: this.get('fields').each(function (fieldModel) {
Chris@0: fieldModel.set({
Chris@0: inTempStore: false,
Chris@0: state: 'inactive'
Chris@0: }, options);
Chris@0: });
Chris@0: break;
Chris@0: }
Chris@0: },
Chris@0: _updateInTempStoreAttributes: function _updateInTempStoreAttributes(entityModel, fieldModel) {
Chris@0: var current = fieldModel.get('state');
Chris@0: var previous = fieldModel.previous('state');
Chris@0: var fieldsInTempStore = entityModel.get('fieldsInTempStore');
Chris@0:
Chris@0: if (current === 'saved') {
Chris@0: entityModel.set('inTempStore', true);
Chris@0:
Chris@0: fieldModel.set('inTempStore', true);
Chris@0:
Chris@0: fieldsInTempStore.push(fieldModel.get('fieldID'));
Chris@0: fieldsInTempStore = _.uniq(fieldsInTempStore);
Chris@0: entityModel.set('fieldsInTempStore', fieldsInTempStore);
Chris@0: } else if (current === 'candidate' && previous === 'inactive') {
Chris@0: fieldModel.set('inTempStore', _.intersection([fieldModel.get('fieldID')], fieldsInTempStore).length > 0);
Chris@0: }
Chris@0: },
Chris@0: fieldStateChange: function fieldStateChange(fieldModel, state) {
Chris@0: var entityModel = this;
Chris@0: var fieldState = state;
Chris@0:
Chris@0: switch (this.get('state')) {
Chris@0: case 'closed':
Chris@0: case 'launching':
Chris@0: break;
Chris@0:
Chris@0: case 'opening':
Chris@0: _.defer(function () {
Chris@0: entityModel.set('state', 'opened', {
Chris@0: 'accept-field-states': Drupal.quickedit.app.readyFieldStates
Chris@0: });
Chris@0: });
Chris@0: break;
Chris@0:
Chris@0: case 'opened':
Chris@0: if (fieldState === 'changed') {
Chris@0: entityModel.set('isDirty', true);
Chris@0: } else {
Chris@0: this._updateInTempStoreAttributes(entityModel, fieldModel);
Chris@0: }
Chris@0: break;
Chris@0:
Chris@0: case 'committing':
Chris@14: {
Chris@14: if (fieldState === 'invalid') {
Chris@14: _.defer(function () {
Chris@14: entityModel.set('state', 'opened', { reason: 'invalid' });
Chris@14: });
Chris@14: } else {
Chris@14: this._updateInTempStoreAttributes(entityModel, fieldModel);
Chris@14: }
Chris@14:
Chris@14: var options = {
Chris@14: 'accept-field-states': Drupal.quickedit.app.readyFieldStates
Chris@14: };
Chris@14: if (entityModel.set('isCommitting', true, options)) {
Chris@14: entityModel.save({
Chris@14: success: function success() {
Chris@14: entityModel.set({
Chris@14: state: 'deactivating',
Chris@14: isCommitting: false
Chris@14: }, { saved: true });
Chris@14: },
Chris@14: error: function error() {
Chris@14: entityModel.set('isCommitting', false);
Chris@14:
Chris@17: entityModel.set('state', 'opened', {
Chris@17: reason: 'networkerror'
Chris@17: });
Chris@14:
Chris@14: var message = Drupal.t('Your changes to @entity-title
could not be saved, either due to a website problem or a network connection problem.
Please try again.', { '@entity-title': entityModel.get('label') });
Chris@14: Drupal.quickedit.util.networkErrorModal(Drupal.t('Network problem!'), message);
Chris@14: }
Chris@14: });
Chris@14: }
Chris@14: break;
Chris@0: }
Chris@0:
Chris@0: case 'deactivating':
Chris@0: _.defer(function () {
Chris@0: entityModel.set('state', 'closing', {
Chris@0: 'accept-field-states': Drupal.quickedit.app.readyFieldStates
Chris@0: });
Chris@0: });
Chris@0: break;
Chris@0:
Chris@0: case 'closing':
Chris@0: _.defer(function () {
Chris@0: entityModel.set('state', 'closed', {
Chris@0: 'accept-field-states': ['inactive']
Chris@0: });
Chris@0: });
Chris@0: break;
Chris@0: }
Chris@0: },
Chris@0: save: function save(options) {
Chris@0: var entityModel = this;
Chris@0:
Chris@0: var entitySaverAjax = Drupal.ajax({
Chris@0: url: Drupal.url('quickedit/entity/' + entityModel.get('entityID')),
Chris@0: error: function error() {
Chris@0: options.error.call(entityModel);
Chris@0: }
Chris@0: });
Chris@0:
Chris@0: entitySaverAjax.commands.quickeditEntitySaved = function (ajax, response, status) {
Chris@0: entityModel.get('fields').each(function (fieldModel) {
Chris@0: fieldModel.set('inTempStore', false);
Chris@0: });
Chris@0: entityModel.set('inTempStore', false);
Chris@0: entityModel.set('fieldsInTempStore', []);
Chris@0:
Chris@0: if (options.success) {
Chris@0: options.success.call(entityModel);
Chris@0: }
Chris@0: };
Chris@0:
Chris@0: entitySaverAjax.execute();
Chris@0: },
Chris@0: validate: function validate(attrs, options) {
Chris@0: var acceptedFieldStates = options['accept-field-states'] || [];
Chris@0:
Chris@0: var currentState = this.get('state');
Chris@0: var nextState = attrs.state;
Chris@0: if (currentState !== nextState) {
Chris@0: if (_.indexOf(this.constructor.states, nextState) === -1) {
Chris@0: return '"' + nextState + '" is an invalid state';
Chris@0: }
Chris@0:
Chris@0: if (!this._acceptStateChange(currentState, nextState, options)) {
Chris@0: return 'state change not accepted';
Chris@17: }
Chris@17:
Chris@17: if (!this._fieldsHaveAcceptableStates(acceptedFieldStates)) {
Chris@17: return 'state change not accepted because fields are not in acceptable state';
Chris@17: }
Chris@0: }
Chris@0:
Chris@0: var currentIsCommitting = this.get('isCommitting');
Chris@0: var nextIsCommitting = attrs.isCommitting;
Chris@0: if (currentIsCommitting === false && nextIsCommitting === true) {
Chris@0: if (!this._fieldsHaveAcceptableStates(acceptedFieldStates)) {
Chris@0: return 'isCommitting change not accepted because fields are not in acceptable state';
Chris@0: }
Chris@0: } else if (currentIsCommitting === true && nextIsCommitting === true) {
Chris@0: return 'isCommitting is a mutex, hence only changes are allowed';
Chris@0: }
Chris@0: },
Chris@0: _acceptStateChange: function _acceptStateChange(from, to, context) {
Chris@0: var accept = true;
Chris@0:
Chris@0: if (!this.constructor.followsStateSequence(from, to)) {
Chris@0: accept = false;
Chris@0:
Chris@0: if (from === 'closing' && to === 'closed') {
Chris@0: accept = true;
Chris@0: } else if (from === 'committing' && to === 'opened' && context.reason && (context.reason === 'invalid' || context.reason === 'networkerror')) {
Chris@0: accept = true;
Chris@0: } else if (from === 'deactivating' && to === 'opened' && context.confirming) {
Chris@0: accept = true;
Chris@0: } else if (from === 'opened' && to === 'deactivating' && context.confirmed) {
Chris@0: accept = true;
Chris@0: }
Chris@0: }
Chris@0:
Chris@0: return accept;
Chris@0: },
Chris@0: _fieldsHaveAcceptableStates: function _fieldsHaveAcceptableStates(acceptedFieldStates) {
Chris@0: var accept = true;
Chris@0:
Chris@0: if (acceptedFieldStates.length > 0) {
Chris@0: var fieldStates = this.get('fields').pluck('state') || [];
Chris@0:
Chris@0: if (_.difference(fieldStates, acceptedFieldStates).length) {
Chris@0: accept = false;
Chris@0: }
Chris@0: }
Chris@0:
Chris@0: return accept;
Chris@0: },
Chris@0: destroy: function destroy(options) {
Chris@0: Drupal.quickedit.BaseModel.prototype.destroy.call(this, options);
Chris@0:
Chris@0: this.stopListening();
Chris@0:
Chris@0: this.get('fields').reset();
Chris@0: },
Chris@0: sync: function sync() {}
Chris@0: }, {
Chris@0: states: ['closed', 'launching', 'opening', 'opened', 'committing', 'deactivating', 'closing'],
Chris@0:
Chris@0: followsStateSequence: function followsStateSequence(from, to) {
Chris@0: return _.indexOf(this.states, from) < _.indexOf(this.states, to);
Chris@0: }
Chris@0: });
Chris@0:
Chris@0: Drupal.quickedit.EntityCollection = Backbone.Collection.extend({
Chris@0: model: Drupal.quickedit.EntityModel
Chris@0: });
Chris@0: })(_, jQuery, Backbone, Drupal);