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