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, drupalSettings, JSON, storage) {
|
Chris@0
|
9 var options = $.extend(drupalSettings.quickedit, {
|
Chris@0
|
10 strings: {
|
Chris@0
|
11 quickEdit: Drupal.t('Quick edit')
|
Chris@0
|
12 }
|
Chris@0
|
13 });
|
Chris@0
|
14
|
Chris@0
|
15 var fieldsMetadataQueue = [];
|
Chris@0
|
16
|
Chris@0
|
17 var fieldsAvailableQueue = [];
|
Chris@0
|
18
|
Chris@0
|
19 var contextualLinksQueue = [];
|
Chris@0
|
20
|
Chris@0
|
21 var entityInstancesTracker = {};
|
Chris@0
|
22
|
Chris@17
|
23 function initQuickEdit(bodyElement) {
|
Chris@17
|
24 Drupal.quickedit.collections.entities = new Drupal.quickedit.EntityCollection();
|
Chris@17
|
25 Drupal.quickedit.collections.fields = new Drupal.quickedit.FieldCollection();
|
Chris@17
|
26
|
Chris@17
|
27 Drupal.quickedit.app = new Drupal.quickedit.AppView({
|
Chris@17
|
28 el: bodyElement,
|
Chris@17
|
29 model: new Drupal.quickedit.AppModel(),
|
Chris@17
|
30 entitiesCollection: Drupal.quickedit.collections.entities,
|
Chris@17
|
31 fieldsCollection: Drupal.quickedit.collections.fields
|
Chris@17
|
32 });
|
Chris@17
|
33 }
|
Chris@17
|
34
|
Chris@17
|
35 function processEntity(entityElement) {
|
Chris@17
|
36 var entityID = entityElement.getAttribute('data-quickedit-entity-id');
|
Chris@17
|
37 if (!entityInstancesTracker.hasOwnProperty(entityID)) {
|
Chris@17
|
38 entityInstancesTracker[entityID] = 0;
|
Chris@17
|
39 } else {
|
Chris@17
|
40 entityInstancesTracker[entityID]++;
|
Chris@17
|
41 }
|
Chris@17
|
42
|
Chris@17
|
43 var entityInstanceID = entityInstancesTracker[entityID];
|
Chris@17
|
44 entityElement.setAttribute('data-quickedit-entity-instance-id', entityInstanceID);
|
Chris@17
|
45 }
|
Chris@17
|
46
|
Chris@17
|
47 function initializeField(fieldElement, fieldID, entityID, entityInstanceID) {
|
Chris@17
|
48 var entity = Drupal.quickedit.collections.entities.findWhere({
|
Chris@17
|
49 entityID: entityID,
|
Chris@17
|
50 entityInstanceID: entityInstanceID
|
Chris@17
|
51 });
|
Chris@17
|
52
|
Chris@17
|
53 $(fieldElement).addClass('quickedit-field');
|
Chris@17
|
54
|
Chris@17
|
55 var field = new Drupal.quickedit.FieldModel({
|
Chris@17
|
56 el: fieldElement,
|
Chris@17
|
57 fieldID: fieldID,
|
Chris@17
|
58 id: fieldID + '[' + entity.get('entityInstanceID') + ']',
|
Chris@17
|
59 entity: entity,
|
Chris@17
|
60 metadata: Drupal.quickedit.metadata.get(fieldID),
|
Chris@17
|
61 acceptStateChange: _.bind(Drupal.quickedit.app.acceptEditorStateChange, Drupal.quickedit.app)
|
Chris@17
|
62 });
|
Chris@17
|
63
|
Chris@17
|
64 Drupal.quickedit.collections.fields.add(field);
|
Chris@17
|
65 }
|
Chris@17
|
66
|
Chris@17
|
67 function loadMissingEditors(callback) {
|
Chris@17
|
68 var loadedEditors = _.keys(Drupal.quickedit.editors);
|
Chris@17
|
69 var missingEditors = [];
|
Chris@17
|
70 Drupal.quickedit.collections.fields.each(function (fieldModel) {
|
Chris@17
|
71 var metadata = Drupal.quickedit.metadata.get(fieldModel.get('fieldID'));
|
Chris@17
|
72 if (metadata.access && _.indexOf(loadedEditors, metadata.editor) === -1) {
|
Chris@17
|
73 missingEditors.push(metadata.editor);
|
Chris@17
|
74
|
Chris@17
|
75 Drupal.quickedit.editors[metadata.editor] = false;
|
Chris@17
|
76 }
|
Chris@17
|
77 });
|
Chris@17
|
78 missingEditors = _.uniq(missingEditors);
|
Chris@17
|
79 if (missingEditors.length === 0) {
|
Chris@17
|
80 callback();
|
Chris@17
|
81 return;
|
Chris@17
|
82 }
|
Chris@17
|
83
|
Chris@17
|
84 var loadEditorsAjax = Drupal.ajax({
|
Chris@17
|
85 url: Drupal.url('quickedit/attachments'),
|
Chris@17
|
86 submit: { 'editors[]': missingEditors }
|
Chris@17
|
87 });
|
Chris@17
|
88
|
Chris@17
|
89 var realInsert = Drupal.AjaxCommands.prototype.insert;
|
Chris@17
|
90 loadEditorsAjax.commands.insert = function (ajax, response, status) {
|
Chris@17
|
91 _.defer(callback);
|
Chris@17
|
92 realInsert(ajax, response, status);
|
Chris@17
|
93 };
|
Chris@17
|
94
|
Chris@17
|
95 loadEditorsAjax.execute();
|
Chris@17
|
96 }
|
Chris@17
|
97
|
Chris@17
|
98 function initializeEntityContextualLink(contextualLink) {
|
Chris@17
|
99 var metadata = Drupal.quickedit.metadata;
|
Chris@17
|
100
|
Chris@17
|
101 function hasFieldWithPermission(fieldIDs) {
|
Chris@17
|
102 for (var i = 0; i < fieldIDs.length; i++) {
|
Chris@17
|
103 var fieldID = fieldIDs[i];
|
Chris@17
|
104 if (metadata.get(fieldID, 'access') === true) {
|
Chris@17
|
105 return true;
|
Chris@17
|
106 }
|
Chris@17
|
107 }
|
Chris@17
|
108 return false;
|
Chris@17
|
109 }
|
Chris@17
|
110
|
Chris@17
|
111 function allMetadataExists(fieldIDs) {
|
Chris@17
|
112 return fieldIDs.length === metadata.intersection(fieldIDs).length;
|
Chris@17
|
113 }
|
Chris@17
|
114
|
Chris@17
|
115 var fields = _.where(fieldsAvailableQueue, {
|
Chris@17
|
116 entityID: contextualLink.entityID,
|
Chris@17
|
117 entityInstanceID: contextualLink.entityInstanceID
|
Chris@17
|
118 });
|
Chris@17
|
119 var fieldIDs = _.pluck(fields, 'fieldID');
|
Chris@17
|
120
|
Chris@17
|
121 if (fieldIDs.length === 0) {
|
Chris@17
|
122 return false;
|
Chris@17
|
123 }
|
Chris@17
|
124
|
Chris@17
|
125 if (hasFieldWithPermission(fieldIDs)) {
|
Chris@17
|
126 var entityModel = new Drupal.quickedit.EntityModel({
|
Chris@17
|
127 el: contextualLink.region,
|
Chris@17
|
128 entityID: contextualLink.entityID,
|
Chris@17
|
129 entityInstanceID: contextualLink.entityInstanceID,
|
Chris@17
|
130 id: contextualLink.entityID + '[' + contextualLink.entityInstanceID + ']',
|
Chris@17
|
131 label: Drupal.quickedit.metadata.get(contextualLink.entityID, 'label')
|
Chris@17
|
132 });
|
Chris@17
|
133 Drupal.quickedit.collections.entities.add(entityModel);
|
Chris@17
|
134
|
Chris@17
|
135 var entityDecorationView = new Drupal.quickedit.EntityDecorationView({
|
Chris@17
|
136 el: contextualLink.region,
|
Chris@17
|
137 model: entityModel
|
Chris@17
|
138 });
|
Chris@17
|
139 entityModel.set('entityDecorationView', entityDecorationView);
|
Chris@17
|
140
|
Chris@17
|
141 _.each(fields, function (field) {
|
Chris@17
|
142 initializeField(field.el, field.fieldID, contextualLink.entityID, contextualLink.entityInstanceID);
|
Chris@17
|
143 });
|
Chris@17
|
144 fieldsAvailableQueue = _.difference(fieldsAvailableQueue, fields);
|
Chris@17
|
145
|
Chris@17
|
146 var initContextualLink = _.once(function () {
|
Chris@17
|
147 var $links = $(contextualLink.el).find('.contextual-links');
|
Chris@17
|
148 var contextualLinkView = new Drupal.quickedit.ContextualLinkView($.extend({
|
Chris@17
|
149 el: $('<li class="quickedit"><a href="" role="button" aria-pressed="false"></a></li>').prependTo($links),
|
Chris@17
|
150 model: entityModel,
|
Chris@17
|
151 appModel: Drupal.quickedit.app.model
|
Chris@17
|
152 }, options));
|
Chris@17
|
153 entityModel.set('contextualLinkView', contextualLinkView);
|
Chris@17
|
154 });
|
Chris@17
|
155
|
Chris@17
|
156 loadMissingEditors(initContextualLink);
|
Chris@17
|
157
|
Chris@17
|
158 return true;
|
Chris@17
|
159 }
|
Chris@17
|
160
|
Chris@17
|
161 if (allMetadataExists(fieldIDs)) {
|
Chris@17
|
162 return true;
|
Chris@17
|
163 }
|
Chris@17
|
164
|
Chris@17
|
165 return false;
|
Chris@17
|
166 }
|
Chris@17
|
167
|
Chris@17
|
168 function extractEntityID(fieldID) {
|
Chris@17
|
169 return fieldID.split('/').slice(0, 2).join('/');
|
Chris@17
|
170 }
|
Chris@17
|
171
|
Chris@17
|
172 function processField(fieldElement) {
|
Chris@17
|
173 var metadata = Drupal.quickedit.metadata;
|
Chris@17
|
174 var fieldID = fieldElement.getAttribute('data-quickedit-field-id');
|
Chris@17
|
175 var entityID = extractEntityID(fieldID);
|
Chris@17
|
176
|
Chris@17
|
177 var entityElementSelector = '[data-quickedit-entity-id="' + entityID + '"]';
|
Chris@17
|
178 var $entityElement = $(entityElementSelector);
|
Chris@17
|
179
|
Chris@17
|
180 if (!$entityElement.length) {
|
Chris@17
|
181 throw new Error('Quick Edit could not associate the rendered entity field markup (with [data-quickedit-field-id="' + fieldID + '"]) with the corresponding rendered entity markup: no parent DOM node found with [data-quickedit-entity-id="' + entityID + '"]. This is typically caused by the theme\'s template for this entity type forgetting to print the attributes.');
|
Chris@17
|
182 }
|
Chris@17
|
183 var entityElement = $(fieldElement).closest($entityElement);
|
Chris@17
|
184
|
Chris@17
|
185 if (entityElement.length === 0) {
|
Chris@17
|
186 var $lowestCommonParent = $entityElement.parents().has(fieldElement).first();
|
Chris@17
|
187 entityElement = $lowestCommonParent.find($entityElement);
|
Chris@17
|
188 }
|
Chris@17
|
189 var entityInstanceID = entityElement.get(0).getAttribute('data-quickedit-entity-instance-id');
|
Chris@17
|
190
|
Chris@17
|
191 if (!metadata.has(fieldID)) {
|
Chris@17
|
192 fieldsMetadataQueue.push({
|
Chris@17
|
193 el: fieldElement,
|
Chris@17
|
194 fieldID: fieldID,
|
Chris@17
|
195 entityID: entityID,
|
Chris@17
|
196 entityInstanceID: entityInstanceID
|
Chris@17
|
197 });
|
Chris@17
|
198 return;
|
Chris@17
|
199 }
|
Chris@17
|
200
|
Chris@17
|
201 if (metadata.get(fieldID, 'access') !== true) {
|
Chris@17
|
202 return;
|
Chris@17
|
203 }
|
Chris@17
|
204
|
Chris@17
|
205 if (Drupal.quickedit.collections.entities.findWhere({
|
Chris@17
|
206 entityID: entityID,
|
Chris@17
|
207 entityInstanceID: entityInstanceID
|
Chris@17
|
208 })) {
|
Chris@17
|
209 initializeField(fieldElement, fieldID, entityID, entityInstanceID);
|
Chris@17
|
210 } else {
|
Chris@17
|
211 fieldsAvailableQueue.push({
|
Chris@17
|
212 el: fieldElement,
|
Chris@17
|
213 fieldID: fieldID,
|
Chris@17
|
214 entityID: entityID,
|
Chris@17
|
215 entityInstanceID: entityInstanceID
|
Chris@17
|
216 });
|
Chris@17
|
217 }
|
Chris@17
|
218 }
|
Chris@17
|
219
|
Chris@17
|
220 function deleteContainedModelsAndQueues($context) {
|
Chris@17
|
221 $context.find('[data-quickedit-entity-id]').addBack('[data-quickedit-entity-id]').each(function (index, entityElement) {
|
Chris@17
|
222 var entityModel = Drupal.quickedit.collections.entities.findWhere({
|
Chris@17
|
223 el: entityElement
|
Chris@17
|
224 });
|
Chris@17
|
225 if (entityModel) {
|
Chris@17
|
226 var contextualLinkView = entityModel.get('contextualLinkView');
|
Chris@17
|
227 contextualLinkView.undelegateEvents();
|
Chris@17
|
228 contextualLinkView.remove();
|
Chris@17
|
229
|
Chris@17
|
230 entityModel.get('entityDecorationView').remove();
|
Chris@17
|
231
|
Chris@17
|
232 entityModel.destroy();
|
Chris@17
|
233 }
|
Chris@17
|
234
|
Chris@17
|
235 function hasOtherRegion(contextualLink) {
|
Chris@17
|
236 return contextualLink.region !== entityElement;
|
Chris@17
|
237 }
|
Chris@17
|
238
|
Chris@17
|
239 contextualLinksQueue = _.filter(contextualLinksQueue, hasOtherRegion);
|
Chris@17
|
240 });
|
Chris@17
|
241
|
Chris@17
|
242 $context.find('[data-quickedit-field-id]').addBack('[data-quickedit-field-id]').each(function (index, fieldElement) {
|
Chris@17
|
243 Drupal.quickedit.collections.fields.chain().filter(function (fieldModel) {
|
Chris@17
|
244 return fieldModel.get('el') === fieldElement;
|
Chris@17
|
245 }).invoke('destroy');
|
Chris@17
|
246
|
Chris@17
|
247 function hasOtherFieldElement(field) {
|
Chris@17
|
248 return field.el !== fieldElement;
|
Chris@17
|
249 }
|
Chris@17
|
250
|
Chris@17
|
251 fieldsMetadataQueue = _.filter(fieldsMetadataQueue, hasOtherFieldElement);
|
Chris@17
|
252 fieldsAvailableQueue = _.filter(fieldsAvailableQueue, hasOtherFieldElement);
|
Chris@17
|
253 });
|
Chris@17
|
254 }
|
Chris@17
|
255
|
Chris@17
|
256 function fetchMissingMetadata(callback) {
|
Chris@17
|
257 if (fieldsMetadataQueue.length) {
|
Chris@17
|
258 var fieldIDs = _.pluck(fieldsMetadataQueue, 'fieldID');
|
Chris@17
|
259 var fieldElementsWithoutMetadata = _.pluck(fieldsMetadataQueue, 'el');
|
Chris@17
|
260 var entityIDs = _.uniq(_.pluck(fieldsMetadataQueue, 'entityID'), true);
|
Chris@17
|
261
|
Chris@17
|
262 entityIDs = _.difference(entityIDs, Drupal.quickedit.metadata.intersection(entityIDs));
|
Chris@17
|
263 fieldsMetadataQueue = [];
|
Chris@17
|
264
|
Chris@17
|
265 $.ajax({
|
Chris@17
|
266 url: Drupal.url('quickedit/metadata'),
|
Chris@17
|
267 type: 'POST',
|
Chris@17
|
268 data: {
|
Chris@17
|
269 'fields[]': fieldIDs,
|
Chris@17
|
270 'entities[]': entityIDs
|
Chris@17
|
271 },
|
Chris@17
|
272 dataType: 'json',
|
Chris@17
|
273 success: function success(results) {
|
Chris@17
|
274 _.each(results, function (fieldMetadata, fieldID) {
|
Chris@17
|
275 Drupal.quickedit.metadata.add(fieldID, fieldMetadata);
|
Chris@17
|
276 });
|
Chris@17
|
277
|
Chris@17
|
278 callback(fieldElementsWithoutMetadata);
|
Chris@17
|
279 }
|
Chris@17
|
280 });
|
Chris@17
|
281 }
|
Chris@17
|
282 }
|
Chris@17
|
283
|
Chris@0
|
284 Drupal.behaviors.quickedit = {
|
Chris@0
|
285 attach: function attach(context) {
|
Chris@0
|
286 $('body').once('quickedit-init').each(initQuickEdit);
|
Chris@0
|
287
|
Chris@0
|
288 var $fields = $(context).find('[data-quickedit-field-id]').once('quickedit');
|
Chris@0
|
289 if ($fields.length === 0) {
|
Chris@0
|
290 return;
|
Chris@0
|
291 }
|
Chris@0
|
292
|
Chris@0
|
293 $(context).find('[data-quickedit-entity-id]').once('quickedit').each(function (index, entityElement) {
|
Chris@0
|
294 processEntity(entityElement);
|
Chris@0
|
295 });
|
Chris@0
|
296
|
Chris@0
|
297 $fields.each(function (index, fieldElement) {
|
Chris@0
|
298 processField(fieldElement);
|
Chris@0
|
299 });
|
Chris@0
|
300
|
Chris@0
|
301 contextualLinksQueue = _.filter(contextualLinksQueue, function (contextualLink) {
|
Chris@0
|
302 return !initializeEntityContextualLink(contextualLink);
|
Chris@0
|
303 });
|
Chris@0
|
304
|
Chris@0
|
305 fetchMissingMetadata(function (fieldElementsWithFreshMetadata) {
|
Chris@0
|
306 _.each(fieldElementsWithFreshMetadata, processField);
|
Chris@0
|
307
|
Chris@0
|
308 contextualLinksQueue = _.filter(contextualLinksQueue, function (contextualLink) {
|
Chris@0
|
309 return !initializeEntityContextualLink(contextualLink);
|
Chris@0
|
310 });
|
Chris@0
|
311 });
|
Chris@0
|
312 },
|
Chris@0
|
313 detach: function detach(context, settings, trigger) {
|
Chris@0
|
314 if (trigger === 'unload') {
|
Chris@0
|
315 deleteContainedModelsAndQueues($(context));
|
Chris@0
|
316 }
|
Chris@0
|
317 }
|
Chris@0
|
318 };
|
Chris@0
|
319
|
Chris@0
|
320 Drupal.quickedit = {
|
Chris@0
|
321 app: null,
|
Chris@0
|
322
|
Chris@0
|
323 collections: {
|
Chris@0
|
324 entities: null,
|
Chris@0
|
325
|
Chris@0
|
326 fields: null
|
Chris@0
|
327 },
|
Chris@0
|
328
|
Chris@0
|
329 editors: {},
|
Chris@0
|
330
|
Chris@0
|
331 metadata: {
|
Chris@0
|
332 has: function has(fieldID) {
|
Chris@0
|
333 return storage.getItem(this._prefixFieldID(fieldID)) !== null;
|
Chris@0
|
334 },
|
Chris@0
|
335 add: function add(fieldID, metadata) {
|
Chris@0
|
336 storage.setItem(this._prefixFieldID(fieldID), JSON.stringify(metadata));
|
Chris@0
|
337 },
|
Chris@0
|
338 get: function get(fieldID, key) {
|
Chris@0
|
339 var metadata = JSON.parse(storage.getItem(this._prefixFieldID(fieldID)));
|
Chris@0
|
340 return typeof key === 'undefined' ? metadata : metadata[key];
|
Chris@0
|
341 },
|
Chris@0
|
342 _prefixFieldID: function _prefixFieldID(fieldID) {
|
Chris@0
|
343 return 'Drupal.quickedit.metadata.' + fieldID;
|
Chris@0
|
344 },
|
Chris@0
|
345 _unprefixFieldID: function _unprefixFieldID(fieldID) {
|
Chris@0
|
346 return fieldID.substring(26);
|
Chris@0
|
347 },
|
Chris@0
|
348 intersection: function intersection(fieldIDs) {
|
Chris@0
|
349 var prefixedFieldIDs = _.map(fieldIDs, this._prefixFieldID);
|
Chris@0
|
350 var intersection = _.intersection(prefixedFieldIDs, _.keys(sessionStorage));
|
Chris@0
|
351 return _.map(intersection, this._unprefixFieldID);
|
Chris@0
|
352 }
|
Chris@0
|
353 }
|
Chris@0
|
354 };
|
Chris@0
|
355
|
Chris@0
|
356 var permissionsHashKey = Drupal.quickedit.metadata._prefixFieldID('permissionsHash');
|
Chris@0
|
357 var permissionsHashValue = storage.getItem(permissionsHashKey);
|
Chris@0
|
358 var permissionsHash = drupalSettings.user.permissionsHash;
|
Chris@0
|
359 if (permissionsHashValue !== permissionsHash) {
|
Chris@0
|
360 if (typeof permissionsHash === 'string') {
|
Chris@0
|
361 _.chain(storage).keys().each(function (key) {
|
Chris@0
|
362 if (key.substring(0, 26) === 'Drupal.quickedit.metadata.') {
|
Chris@0
|
363 storage.removeItem(key);
|
Chris@0
|
364 }
|
Chris@0
|
365 });
|
Chris@0
|
366 }
|
Chris@0
|
367 storage.setItem(permissionsHashKey, permissionsHash);
|
Chris@0
|
368 }
|
Chris@0
|
369
|
Chris@0
|
370 $(document).on('drupalContextualLinkAdded', function (event, data) {
|
Chris@0
|
371 if (data.$region.is('[data-quickedit-entity-id]')) {
|
Chris@0
|
372 if (!data.$region.is('[data-quickedit-entity-instance-id]')) {
|
Chris@0
|
373 data.$region.once('quickedit');
|
Chris@0
|
374 processEntity(data.$region.get(0));
|
Chris@0
|
375 }
|
Chris@0
|
376 var contextualLink = {
|
Chris@0
|
377 entityID: data.$region.attr('data-quickedit-entity-id'),
|
Chris@0
|
378 entityInstanceID: data.$region.attr('data-quickedit-entity-instance-id'),
|
Chris@0
|
379 el: data.$el[0],
|
Chris@0
|
380 region: data.$region[0]
|
Chris@0
|
381 };
|
Chris@0
|
382
|
Chris@0
|
383 if (!initializeEntityContextualLink(contextualLink)) {
|
Chris@0
|
384 contextualLinksQueue.push(contextualLink);
|
Chris@0
|
385 }
|
Chris@0
|
386 }
|
Chris@0
|
387 });
|
Chris@0
|
388 })(jQuery, _, Backbone, Drupal, drupalSettings, window.JSON, window.sessionStorage); |