Mercurial > hg > isophonics-drupal-site
comparison core/modules/quickedit/js/models/FieldModel.es6.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 * @file | |
3 * A Backbone Model for the state of an in-place editable field in the DOM. | |
4 */ | |
5 | |
6 (function (_, Backbone, Drupal) { | |
7 Drupal.quickedit.FieldModel = Drupal.quickedit.BaseModel.extend(/** @lends Drupal.quickedit.FieldModel# */{ | |
8 | |
9 /** | |
10 * @type {object} | |
11 */ | |
12 defaults: /** @lends Drupal.quickedit.FieldModel# */{ | |
13 | |
14 /** | |
15 * The DOM element that represents this field. It may seem bizarre to have | |
16 * a DOM element in a Backbone Model, but we need to be able to map fields | |
17 * in the DOM to FieldModels in memory. | |
18 */ | |
19 el: null, | |
20 | |
21 /** | |
22 * A field ID, of the form | |
23 * `<entity type>/<id>/<field name>/<language>/<view mode>` | |
24 * | |
25 * @example | |
26 * "node/1/field_tags/und/full" | |
27 */ | |
28 fieldID: null, | |
29 | |
30 /** | |
31 * The unique ID of this field within its entity instance on the page, of | |
32 * the form `<entity type>/<id>/<field name>/<language>/<view | |
33 * mode>[entity instance ID]`. | |
34 * | |
35 * @example | |
36 * "node/1/field_tags/und/full[0]" | |
37 */ | |
38 id: null, | |
39 | |
40 /** | |
41 * A {@link Drupal.quickedit.EntityModel}. Its "fields" attribute, which | |
42 * is a FieldCollection, is automatically updated to include this | |
43 * FieldModel. | |
44 */ | |
45 entity: null, | |
46 | |
47 /** | |
48 * This field's metadata as returned by the | |
49 * QuickEditController::metadata(). | |
50 */ | |
51 metadata: null, | |
52 | |
53 /** | |
54 * Callback function for validating changes between states. Receives the | |
55 * previous state, new state, context, and a callback. | |
56 */ | |
57 acceptStateChange: null, | |
58 | |
59 /** | |
60 * A logical field ID, of the form | |
61 * `<entity type>/<id>/<field name>/<language>`, i.e. the fieldID without | |
62 * the view mode, to be able to identify other instances of the same | |
63 * field on the page but rendered in a different view mode. | |
64 * | |
65 * @example | |
66 * "node/1/field_tags/und". | |
67 */ | |
68 logicalFieldID: null, | |
69 | |
70 // The attributes below are stateful. The ones above will never change | |
71 // during the life of a FieldModel instance. | |
72 | |
73 /** | |
74 * In-place editing state of this field. Defaults to the initial state. | |
75 * Possible values: {@link Drupal.quickedit.FieldModel.states}. | |
76 */ | |
77 state: 'inactive', | |
78 | |
79 /** | |
80 * The field is currently in the 'changed' state or one of the following | |
81 * states in which the field is still changed. | |
82 */ | |
83 isChanged: false, | |
84 | |
85 /** | |
86 * Is tracked by the EntityModel, is mirrored here solely for decorative | |
87 * purposes: so that FieldDecorationView.renderChanged() can react to it. | |
88 */ | |
89 inTempStore: false, | |
90 | |
91 /** | |
92 * The full HTML representation of this field (with the element that has | |
93 * the data-quickedit-field-id as the outer element). Used to propagate | |
94 * changes from this field to other instances of the same field storage. | |
95 */ | |
96 html: null, | |
97 | |
98 /** | |
99 * An object containing the full HTML representations (values) of other | |
100 * view modes (keys) of this field, for other instances of this field | |
101 * displayed in a different view mode. | |
102 */ | |
103 htmlForOtherViewModes: null, | |
104 }, | |
105 | |
106 /** | |
107 * State of an in-place editable field in the DOM. | |
108 * | |
109 * @constructs | |
110 * | |
111 * @augments Drupal.quickedit.BaseModel | |
112 * | |
113 * @param {object} options | |
114 * Options for the field model. | |
115 */ | |
116 initialize(options) { | |
117 // Store the original full HTML representation of this field. | |
118 this.set('html', options.el.outerHTML); | |
119 | |
120 // Enlist field automatically in the associated entity's field collection. | |
121 this.get('entity').get('fields').add(this); | |
122 | |
123 // Automatically generate the logical field ID. | |
124 this.set('logicalFieldID', this.get('fieldID').split('/').slice(0, 4).join('/')); | |
125 | |
126 // Call Drupal.quickedit.BaseModel's initialize() method. | |
127 Drupal.quickedit.BaseModel.prototype.initialize.call(this, options); | |
128 }, | |
129 | |
130 /** | |
131 * Destroys the field model. | |
132 * | |
133 * @param {object} options | |
134 * Options for the field model. | |
135 */ | |
136 destroy(options) { | |
137 if (this.get('state') !== 'inactive') { | |
138 throw new Error('FieldModel cannot be destroyed if it is not inactive state.'); | |
139 } | |
140 Drupal.quickedit.BaseModel.prototype.destroy.call(this, options); | |
141 }, | |
142 | |
143 /** | |
144 * @inheritdoc | |
145 */ | |
146 sync() { | |
147 // We don't use REST updates to sync. | |
148 | |
149 }, | |
150 | |
151 /** | |
152 * Validate function for the field model. | |
153 * | |
154 * @param {object} attrs | |
155 * The attributes changes in the save or set call. | |
156 * @param {object} options | |
157 * An object with the following option: | |
158 * @param {string} [options.reason] | |
159 * A string that conveys a particular reason to allow for an exceptional | |
160 * state change. | |
161 * @param {Array} options.accept-field-states | |
162 * An array of strings that represent field states that the entities must | |
163 * be in to validate. For example, if `accept-field-states` is | |
164 * `['candidate', 'highlighted']`, then all the fields of the entity must | |
165 * be in either of these two states for the save or set call to | |
166 * validate and proceed. | |
167 * | |
168 * @return {string} | |
169 * A string to say something about the state of the field model. | |
170 */ | |
171 validate(attrs, options) { | |
172 const current = this.get('state'); | |
173 const next = attrs.state; | |
174 if (current !== next) { | |
175 // Ensure it's a valid state. | |
176 if (_.indexOf(this.constructor.states, next) === -1) { | |
177 return `"${next}" is an invalid state`; | |
178 } | |
179 // Check if the acceptStateChange callback accepts it. | |
180 if (!this.get('acceptStateChange')(current, next, options, this)) { | |
181 return 'state change not accepted'; | |
182 } | |
183 } | |
184 }, | |
185 | |
186 /** | |
187 * Extracts the entity ID from this field's ID. | |
188 * | |
189 * @return {string} | |
190 * An entity ID: a string of the format `<entity type>/<id>`. | |
191 */ | |
192 getEntityID() { | |
193 return this.get('fieldID').split('/').slice(0, 2).join('/'); | |
194 }, | |
195 | |
196 /** | |
197 * Extracts the view mode ID from this field's ID. | |
198 * | |
199 * @return {string} | |
200 * A view mode ID. | |
201 */ | |
202 getViewMode() { | |
203 return this.get('fieldID').split('/').pop(); | |
204 }, | |
205 | |
206 /** | |
207 * Find other instances of this field with different view modes. | |
208 * | |
209 * @return {Array} | |
210 * An array containing view mode IDs. | |
211 */ | |
212 findOtherViewModes() { | |
213 const currentField = this; | |
214 const otherViewModes = []; | |
215 Drupal.quickedit.collections.fields | |
216 // Find all instances of fields that display the same logical field | |
217 // (same entity, same field, just a different instance and maybe a | |
218 // different view mode). | |
219 .where({ logicalFieldID: currentField.get('logicalFieldID') }) | |
220 .forEach((field) => { | |
221 // Ignore the current field. | |
222 if (field === currentField) { | |
223 | |
224 } | |
225 // Also ignore other fields with the same view mode. | |
226 else if (field.get('fieldID') === currentField.get('fieldID')) { | |
227 | |
228 } | |
229 else { | |
230 otherViewModes.push(field.getViewMode()); | |
231 } | |
232 }); | |
233 return otherViewModes; | |
234 }, | |
235 | |
236 }, /** @lends Drupal.quickedit.FieldModel */{ | |
237 | |
238 /** | |
239 * Sequence of all possible states a field can be in during quickediting. | |
240 * | |
241 * @type {Array.<string>} | |
242 */ | |
243 states: [ | |
244 // The field associated with this FieldModel is linked to an EntityModel; | |
245 // the user can choose to start in-place editing that entity (and | |
246 // consequently this field). No in-place editor (EditorView) is associated | |
247 // with this field, because this field is not being in-place edited. | |
248 // This is both the initial (not yet in-place editing) and the end state | |
249 // (finished in-place editing). | |
250 'inactive', | |
251 // The user is in-place editing this entity, and this field is a | |
252 // candidate | |
253 // for in-place editing. In-place editor should not | |
254 // - Trigger: user. | |
255 // - Guarantees: entity is ready, in-place editor (EditorView) is | |
256 // associated with the field. | |
257 // - Expected behavior: visual indicators | |
258 // around the field indicate it is available for in-place editing, no | |
259 // in-place editor presented yet. | |
260 'candidate', | |
261 // User is highlighting this field. | |
262 // - Trigger: user. | |
263 // - Guarantees: see 'candidate'. | |
264 // - Expected behavior: visual indicators to convey highlighting, in-place | |
265 // editing toolbar shows field's label. | |
266 'highlighted', | |
267 // User has activated the in-place editing of this field; in-place editor | |
268 // is activating. | |
269 // - Trigger: user. | |
270 // - Guarantees: see 'candidate'. | |
271 // - Expected behavior: loading indicator, in-place editor is loading | |
272 // remote data (e.g. retrieve form from back-end). Upon retrieval of | |
273 // remote data, the in-place editor transitions the field's state to | |
274 // 'active'. | |
275 'activating', | |
276 // In-place editor has finished loading remote data; ready for use. | |
277 // - Trigger: in-place editor. | |
278 // - Guarantees: see 'candidate'. | |
279 // - Expected behavior: in-place editor for the field is ready for use. | |
280 'active', | |
281 // User has modified values in the in-place editor. | |
282 // - Trigger: user. | |
283 // - Guarantees: see 'candidate', plus in-place editor is ready for use. | |
284 // - Expected behavior: visual indicator of change. | |
285 'changed', | |
286 // User is saving changed field data in in-place editor to | |
287 // PrivateTempStore. The save mechanism of the in-place editor is called. | |
288 // - Trigger: user. | |
289 // - Guarantees: see 'candidate' and 'active'. | |
290 // - Expected behavior: saving indicator, in-place editor is saving field | |
291 // data into PrivateTempStore. Upon successful saving (without | |
292 // validation errors), the in-place editor transitions the field's state | |
293 // to 'saved', but to 'invalid' upon failed saving (with validation | |
294 // errors). | |
295 'saving', | |
296 // In-place editor has successfully saved the changed field. | |
297 // - Trigger: in-place editor. | |
298 // - Guarantees: see 'candidate' and 'active'. | |
299 // - Expected behavior: transition back to 'candidate' state because the | |
300 // deed is done. Then: 1) transition to 'inactive' to allow the field | |
301 // to be rerendered, 2) destroy the FieldModel (which also destroys | |
302 // attached views like the EditorView), 3) replace the existing field | |
303 // HTML with the existing HTML and 4) attach behaviors again so that the | |
304 // field becomes available again for in-place editing. | |
305 'saved', | |
306 // In-place editor has failed to saved the changed field: there were | |
307 // validation errors. | |
308 // - Trigger: in-place editor. | |
309 // - Guarantees: see 'candidate' and 'active'. | |
310 // - Expected behavior: remain in 'invalid' state, let the user make more | |
311 // changes so that he can save it again, without validation errors. | |
312 'invalid', | |
313 ], | |
314 | |
315 /** | |
316 * Indicates whether the 'from' state comes before the 'to' state. | |
317 * | |
318 * @param {string} from | |
319 * One of {@link Drupal.quickedit.FieldModel.states}. | |
320 * @param {string} to | |
321 * One of {@link Drupal.quickedit.FieldModel.states}. | |
322 * | |
323 * @return {bool} | |
324 * Whether the 'from' state comes before the 'to' state. | |
325 */ | |
326 followsStateSequence(from, to) { | |
327 return _.indexOf(this.states, from) < _.indexOf(this.states, to); | |
328 }, | |
329 | |
330 }); | |
331 | |
332 /** | |
333 * @constructor | |
334 * | |
335 * @augments Backbone.Collection | |
336 */ | |
337 Drupal.quickedit.FieldCollection = Backbone.Collection.extend(/** @lends Drupal.quickedit.FieldCollection */{ | |
338 | |
339 /** | |
340 * @type {Drupal.quickedit.FieldModel} | |
341 */ | |
342 model: Drupal.quickedit.FieldModel, | |
343 }); | |
344 }(_, Backbone, Drupal)); |