annotate core/modules/quickedit/js/models/EntityModel.js @ 0:4c8ae668cc8c

Initial import (non-working)
author Chris Cannam
date Wed, 29 Nov 2017 16:09:58 +0000
parents
children 1fec387a4317
rev   line source
Chris@0 1 /**
Chris@0 2 * DO NOT EDIT THIS FILE.
Chris@0 3 * See the following change record for more information,
Chris@0 4 * https://www.drupal.org/node/2815083
Chris@0 5 * @preserve
Chris@0 6 **/
Chris@0 7
Chris@0 8 (function (_, $, Backbone, Drupal) {
Chris@0 9 Drupal.quickedit.EntityModel = Drupal.quickedit.BaseModel.extend({
Chris@0 10 defaults: {
Chris@0 11 el: null,
Chris@0 12
Chris@0 13 entityID: null,
Chris@0 14
Chris@0 15 entityInstanceID: null,
Chris@0 16
Chris@0 17 id: null,
Chris@0 18
Chris@0 19 label: null,
Chris@0 20
Chris@0 21 fields: null,
Chris@0 22
Chris@0 23 isActive: false,
Chris@0 24
Chris@0 25 inTempStore: false,
Chris@0 26
Chris@0 27 isDirty: false,
Chris@0 28
Chris@0 29 isCommitting: false,
Chris@0 30
Chris@0 31 state: 'closed',
Chris@0 32
Chris@0 33 fieldsInTempStore: [],
Chris@0 34
Chris@0 35 reload: false
Chris@0 36 },
Chris@0 37
Chris@0 38 initialize: function initialize() {
Chris@0 39 this.set('fields', new Drupal.quickedit.FieldCollection());
Chris@0 40
Chris@0 41 this.listenTo(this, 'change:state', this.stateChange);
Chris@0 42
Chris@0 43 this.listenTo(this.get('fields'), 'change:state', this.fieldStateChange);
Chris@0 44
Chris@0 45 Drupal.quickedit.BaseModel.prototype.initialize.call(this);
Chris@0 46 },
Chris@0 47 stateChange: function stateChange(entityModel, state, options) {
Chris@0 48 var to = state;
Chris@0 49 switch (to) {
Chris@0 50 case 'closed':
Chris@0 51 this.set({
Chris@0 52 isActive: false,
Chris@0 53 inTempStore: false,
Chris@0 54 isDirty: false
Chris@0 55 });
Chris@0 56 break;
Chris@0 57
Chris@0 58 case 'launching':
Chris@0 59 break;
Chris@0 60
Chris@0 61 case 'opening':
Chris@0 62 entityModel.get('fields').each(function (fieldModel) {
Chris@0 63 fieldModel.set('state', 'candidate', options);
Chris@0 64 });
Chris@0 65 break;
Chris@0 66
Chris@0 67 case 'opened':
Chris@0 68 this.set('isActive', true);
Chris@0 69 break;
Chris@0 70
Chris@0 71 case 'committing':
Chris@0 72 var fields = this.get('fields');
Chris@0 73
Chris@0 74 fields.chain().filter(function (fieldModel) {
Chris@0 75 return _.intersection([fieldModel.get('state')], ['active']).length;
Chris@0 76 }).each(function (fieldModel) {
Chris@0 77 fieldModel.set('state', 'candidate');
Chris@0 78 });
Chris@0 79
Chris@0 80 fields.chain().filter(function (fieldModel) {
Chris@0 81 return _.intersection([fieldModel.get('state')], Drupal.quickedit.app.changedFieldStates).length;
Chris@0 82 }).each(function (fieldModel) {
Chris@0 83 fieldModel.set('state', 'saving');
Chris@0 84 });
Chris@0 85 break;
Chris@0 86
Chris@0 87 case 'deactivating':
Chris@0 88 var changedFields = this.get('fields').filter(function (fieldModel) {
Chris@0 89 return _.intersection([fieldModel.get('state')], ['changed', 'invalid']).length;
Chris@0 90 });
Chris@0 91
Chris@0 92 if ((changedFields.length || this.get('fieldsInTempStore').length) && !options.saved && !options.confirmed) {
Chris@0 93 this.set('state', 'opened', { confirming: true });
Chris@0 94
Chris@0 95 _.defer(function () {
Chris@0 96 Drupal.quickedit.app.confirmEntityDeactivation(entityModel);
Chris@0 97 });
Chris@0 98 } else {
Chris@0 99 var invalidFields = this.get('fields').filter(function (fieldModel) {
Chris@0 100 return _.intersection([fieldModel.get('state')], ['invalid']).length;
Chris@0 101 });
Chris@0 102
Chris@0 103 entityModel.set('reload', this.get('fieldsInTempStore').length || invalidFields.length);
Chris@0 104
Chris@0 105 entityModel.get('fields').each(function (fieldModel) {
Chris@0 106 if (_.intersection([fieldModel.get('state')], ['candidate', 'highlighted']).length) {
Chris@0 107 fieldModel.trigger('change:state', fieldModel, fieldModel.get('state'), options);
Chris@0 108 } else {
Chris@0 109 fieldModel.set('state', 'candidate', options);
Chris@0 110 }
Chris@0 111 });
Chris@0 112 }
Chris@0 113 break;
Chris@0 114
Chris@0 115 case 'closing':
Chris@0 116 options.reason = 'stop';
Chris@0 117 this.get('fields').each(function (fieldModel) {
Chris@0 118 fieldModel.set({
Chris@0 119 inTempStore: false,
Chris@0 120 state: 'inactive'
Chris@0 121 }, options);
Chris@0 122 });
Chris@0 123 break;
Chris@0 124 }
Chris@0 125 },
Chris@0 126 _updateInTempStoreAttributes: function _updateInTempStoreAttributes(entityModel, fieldModel) {
Chris@0 127 var current = fieldModel.get('state');
Chris@0 128 var previous = fieldModel.previous('state');
Chris@0 129 var fieldsInTempStore = entityModel.get('fieldsInTempStore');
Chris@0 130
Chris@0 131 if (current === 'saved') {
Chris@0 132 entityModel.set('inTempStore', true);
Chris@0 133
Chris@0 134 fieldModel.set('inTempStore', true);
Chris@0 135
Chris@0 136 fieldsInTempStore.push(fieldModel.get('fieldID'));
Chris@0 137 fieldsInTempStore = _.uniq(fieldsInTempStore);
Chris@0 138 entityModel.set('fieldsInTempStore', fieldsInTempStore);
Chris@0 139 } else if (current === 'candidate' && previous === 'inactive') {
Chris@0 140 fieldModel.set('inTempStore', _.intersection([fieldModel.get('fieldID')], fieldsInTempStore).length > 0);
Chris@0 141 }
Chris@0 142 },
Chris@0 143 fieldStateChange: function fieldStateChange(fieldModel, state) {
Chris@0 144 var entityModel = this;
Chris@0 145 var fieldState = state;
Chris@0 146
Chris@0 147 switch (this.get('state')) {
Chris@0 148 case 'closed':
Chris@0 149 case 'launching':
Chris@0 150 break;
Chris@0 151
Chris@0 152 case 'opening':
Chris@0 153 _.defer(function () {
Chris@0 154 entityModel.set('state', 'opened', {
Chris@0 155 'accept-field-states': Drupal.quickedit.app.readyFieldStates
Chris@0 156 });
Chris@0 157 });
Chris@0 158 break;
Chris@0 159
Chris@0 160 case 'opened':
Chris@0 161 if (fieldState === 'changed') {
Chris@0 162 entityModel.set('isDirty', true);
Chris@0 163 } else {
Chris@0 164 this._updateInTempStoreAttributes(entityModel, fieldModel);
Chris@0 165 }
Chris@0 166 break;
Chris@0 167
Chris@0 168 case 'committing':
Chris@0 169 if (fieldState === 'invalid') {
Chris@0 170 _.defer(function () {
Chris@0 171 entityModel.set('state', 'opened', { reason: 'invalid' });
Chris@0 172 });
Chris@0 173 } else {
Chris@0 174 this._updateInTempStoreAttributes(entityModel, fieldModel);
Chris@0 175 }
Chris@0 176
Chris@0 177 var options = {
Chris@0 178 'accept-field-states': Drupal.quickedit.app.readyFieldStates
Chris@0 179 };
Chris@0 180 if (entityModel.set('isCommitting', true, options)) {
Chris@0 181 entityModel.save({
Chris@0 182 success: function success() {
Chris@0 183 entityModel.set({
Chris@0 184 state: 'deactivating',
Chris@0 185 isCommitting: false
Chris@0 186 }, { saved: true });
Chris@0 187 },
Chris@0 188 error: function error() {
Chris@0 189 entityModel.set('isCommitting', false);
Chris@0 190
Chris@0 191 entityModel.set('state', 'opened', { reason: 'networkerror' });
Chris@0 192
Chris@0 193 var message = Drupal.t('Your changes to <q>@entity-title</q> could not be saved, either due to a website problem or a network connection problem.<br>Please try again.', { '@entity-title': entityModel.get('label') });
Chris@0 194 Drupal.quickedit.util.networkErrorModal(Drupal.t('Network problem!'), message);
Chris@0 195 }
Chris@0 196 });
Chris@0 197 }
Chris@0 198 break;
Chris@0 199
Chris@0 200 case 'deactivating':
Chris@0 201 _.defer(function () {
Chris@0 202 entityModel.set('state', 'closing', {
Chris@0 203 'accept-field-states': Drupal.quickedit.app.readyFieldStates
Chris@0 204 });
Chris@0 205 });
Chris@0 206 break;
Chris@0 207
Chris@0 208 case 'closing':
Chris@0 209 _.defer(function () {
Chris@0 210 entityModel.set('state', 'closed', {
Chris@0 211 'accept-field-states': ['inactive']
Chris@0 212 });
Chris@0 213 });
Chris@0 214 break;
Chris@0 215 }
Chris@0 216 },
Chris@0 217 save: function save(options) {
Chris@0 218 var entityModel = this;
Chris@0 219
Chris@0 220 var entitySaverAjax = Drupal.ajax({
Chris@0 221 url: Drupal.url('quickedit/entity/' + entityModel.get('entityID')),
Chris@0 222 error: function error() {
Chris@0 223 options.error.call(entityModel);
Chris@0 224 }
Chris@0 225 });
Chris@0 226
Chris@0 227 entitySaverAjax.commands.quickeditEntitySaved = function (ajax, response, status) {
Chris@0 228 entityModel.get('fields').each(function (fieldModel) {
Chris@0 229 fieldModel.set('inTempStore', false);
Chris@0 230 });
Chris@0 231 entityModel.set('inTempStore', false);
Chris@0 232 entityModel.set('fieldsInTempStore', []);
Chris@0 233
Chris@0 234 if (options.success) {
Chris@0 235 options.success.call(entityModel);
Chris@0 236 }
Chris@0 237 };
Chris@0 238
Chris@0 239 entitySaverAjax.execute();
Chris@0 240 },
Chris@0 241 validate: function validate(attrs, options) {
Chris@0 242 var acceptedFieldStates = options['accept-field-states'] || [];
Chris@0 243
Chris@0 244 var currentState = this.get('state');
Chris@0 245 var nextState = attrs.state;
Chris@0 246 if (currentState !== nextState) {
Chris@0 247 if (_.indexOf(this.constructor.states, nextState) === -1) {
Chris@0 248 return '"' + nextState + '" is an invalid state';
Chris@0 249 }
Chris@0 250
Chris@0 251 if (!this._acceptStateChange(currentState, nextState, options)) {
Chris@0 252 return 'state change not accepted';
Chris@0 253 } else if (!this._fieldsHaveAcceptableStates(acceptedFieldStates)) {
Chris@0 254 return 'state change not accepted because fields are not in acceptable state';
Chris@0 255 }
Chris@0 256 }
Chris@0 257
Chris@0 258 var currentIsCommitting = this.get('isCommitting');
Chris@0 259 var nextIsCommitting = attrs.isCommitting;
Chris@0 260 if (currentIsCommitting === false && nextIsCommitting === true) {
Chris@0 261 if (!this._fieldsHaveAcceptableStates(acceptedFieldStates)) {
Chris@0 262 return 'isCommitting change not accepted because fields are not in acceptable state';
Chris@0 263 }
Chris@0 264 } else if (currentIsCommitting === true && nextIsCommitting === true) {
Chris@0 265 return 'isCommitting is a mutex, hence only changes are allowed';
Chris@0 266 }
Chris@0 267 },
Chris@0 268 _acceptStateChange: function _acceptStateChange(from, to, context) {
Chris@0 269 var accept = true;
Chris@0 270
Chris@0 271 if (!this.constructor.followsStateSequence(from, to)) {
Chris@0 272 accept = false;
Chris@0 273
Chris@0 274 if (from === 'closing' && to === 'closed') {
Chris@0 275 accept = true;
Chris@0 276 } else if (from === 'committing' && to === 'opened' && context.reason && (context.reason === 'invalid' || context.reason === 'networkerror')) {
Chris@0 277 accept = true;
Chris@0 278 } else if (from === 'deactivating' && to === 'opened' && context.confirming) {
Chris@0 279 accept = true;
Chris@0 280 } else if (from === 'opened' && to === 'deactivating' && context.confirmed) {
Chris@0 281 accept = true;
Chris@0 282 }
Chris@0 283 }
Chris@0 284
Chris@0 285 return accept;
Chris@0 286 },
Chris@0 287 _fieldsHaveAcceptableStates: function _fieldsHaveAcceptableStates(acceptedFieldStates) {
Chris@0 288 var accept = true;
Chris@0 289
Chris@0 290 if (acceptedFieldStates.length > 0) {
Chris@0 291 var fieldStates = this.get('fields').pluck('state') || [];
Chris@0 292
Chris@0 293 if (_.difference(fieldStates, acceptedFieldStates).length) {
Chris@0 294 accept = false;
Chris@0 295 }
Chris@0 296 }
Chris@0 297
Chris@0 298 return accept;
Chris@0 299 },
Chris@0 300 destroy: function destroy(options) {
Chris@0 301 Drupal.quickedit.BaseModel.prototype.destroy.call(this, options);
Chris@0 302
Chris@0 303 this.stopListening();
Chris@0 304
Chris@0 305 this.get('fields').reset();
Chris@0 306 },
Chris@0 307 sync: function sync() {}
Chris@0 308 }, {
Chris@0 309 states: ['closed', 'launching', 'opening', 'opened', 'committing', 'deactivating', 'closing'],
Chris@0 310
Chris@0 311 followsStateSequence: function followsStateSequence(from, to) {
Chris@0 312 return _.indexOf(this.states, from) < _.indexOf(this.states, to);
Chris@0 313 }
Chris@0 314 });
Chris@0 315
Chris@0 316 Drupal.quickedit.EntityCollection = Backbone.Collection.extend({
Chris@0 317 model: Drupal.quickedit.EntityModel
Chris@0 318 });
Chris@0 319 })(_, jQuery, Backbone, Drupal);