comparison 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
comparison
equal deleted inserted replaced
-1:000000000000 0:4c8ae668cc8c
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);