2 * DO NOT EDIT THIS FILE.
3 * See the following change record for more information,
4 * https://www.drupal.org/node/2815083
8 (function ($, _, Backbone, Drupal, drupalSettings, JSON, storage) {
9 var options = $.extend(drupalSettings.quickedit, {
11 quickEdit: Drupal.t('Quick edit')
15 var fieldsMetadataQueue = [];
17 var fieldsAvailableQueue = [];
19 var contextualLinksQueue = [];
21 var entityInstancesTracker = {};
23 Drupal.behaviors.quickedit = {
24 attach: function attach(context) {
25 $('body').once('quickedit-init').each(initQuickEdit);
27 var $fields = $(context).find('[data-quickedit-field-id]').once('quickedit');
28 if ($fields.length === 0) {
32 $(context).find('[data-quickedit-entity-id]').once('quickedit').each(function (index, entityElement) {
33 processEntity(entityElement);
36 $fields.each(function (index, fieldElement) {
37 processField(fieldElement);
40 contextualLinksQueue = _.filter(contextualLinksQueue, function (contextualLink) {
41 return !initializeEntityContextualLink(contextualLink);
44 fetchMissingMetadata(function (fieldElementsWithFreshMetadata) {
45 _.each(fieldElementsWithFreshMetadata, processField);
47 contextualLinksQueue = _.filter(contextualLinksQueue, function (contextualLink) {
48 return !initializeEntityContextualLink(contextualLink);
52 detach: function detach(context, settings, trigger) {
53 if (trigger === 'unload') {
54 deleteContainedModelsAndQueues($(context));
71 has: function has(fieldID) {
72 return storage.getItem(this._prefixFieldID(fieldID)) !== null;
74 add: function add(fieldID, metadata) {
75 storage.setItem(this._prefixFieldID(fieldID), JSON.stringify(metadata));
77 get: function get(fieldID, key) {
78 var metadata = JSON.parse(storage.getItem(this._prefixFieldID(fieldID)));
79 return typeof key === 'undefined' ? metadata : metadata[key];
81 _prefixFieldID: function _prefixFieldID(fieldID) {
82 return 'Drupal.quickedit.metadata.' + fieldID;
84 _unprefixFieldID: function _unprefixFieldID(fieldID) {
85 return fieldID.substring(26);
87 intersection: function intersection(fieldIDs) {
88 var prefixedFieldIDs = _.map(fieldIDs, this._prefixFieldID);
89 var intersection = _.intersection(prefixedFieldIDs, _.keys(sessionStorage));
90 return _.map(intersection, this._unprefixFieldID);
95 var permissionsHashKey = Drupal.quickedit.metadata._prefixFieldID('permissionsHash');
96 var permissionsHashValue = storage.getItem(permissionsHashKey);
97 var permissionsHash = drupalSettings.user.permissionsHash;
98 if (permissionsHashValue !== permissionsHash) {
99 if (typeof permissionsHash === 'string') {
100 _.chain(storage).keys().each(function (key) {
101 if (key.substring(0, 26) === 'Drupal.quickedit.metadata.') {
102 storage.removeItem(key);
106 storage.setItem(permissionsHashKey, permissionsHash);
109 $(document).on('drupalContextualLinkAdded', function (event, data) {
110 if (data.$region.is('[data-quickedit-entity-id]')) {
111 if (!data.$region.is('[data-quickedit-entity-instance-id]')) {
112 data.$region.once('quickedit');
113 processEntity(data.$region.get(0));
115 var contextualLink = {
116 entityID: data.$region.attr('data-quickedit-entity-id'),
117 entityInstanceID: data.$region.attr('data-quickedit-entity-instance-id'),
119 region: data.$region[0]
122 if (!initializeEntityContextualLink(contextualLink)) {
123 contextualLinksQueue.push(contextualLink);
128 function extractEntityID(fieldID) {
129 return fieldID.split('/').slice(0, 2).join('/');
132 function initQuickEdit(bodyElement) {
133 Drupal.quickedit.collections.entities = new Drupal.quickedit.EntityCollection();
134 Drupal.quickedit.collections.fields = new Drupal.quickedit.FieldCollection();
136 Drupal.quickedit.app = new Drupal.quickedit.AppView({
138 model: new Drupal.quickedit.AppModel(),
139 entitiesCollection: Drupal.quickedit.collections.entities,
140 fieldsCollection: Drupal.quickedit.collections.fields
144 function processEntity(entityElement) {
145 var entityID = entityElement.getAttribute('data-quickedit-entity-id');
146 if (!entityInstancesTracker.hasOwnProperty(entityID)) {
147 entityInstancesTracker[entityID] = 0;
149 entityInstancesTracker[entityID]++;
152 var entityInstanceID = entityInstancesTracker[entityID];
153 entityElement.setAttribute('data-quickedit-entity-instance-id', entityInstanceID);
156 function processField(fieldElement) {
157 var metadata = Drupal.quickedit.metadata;
158 var fieldID = fieldElement.getAttribute('data-quickedit-field-id');
159 var entityID = extractEntityID(fieldID);
161 var entityElementSelector = '[data-quickedit-entity-id="' + entityID + '"]';
162 var $entityElement = $(entityElementSelector);
164 if (!$entityElement.length) {
165 throw '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.';
167 var entityElement = $(fieldElement).closest($entityElement);
169 if (entityElement.length === 0) {
170 var $lowestCommonParent = $entityElement.parents().has(fieldElement).first();
171 entityElement = $lowestCommonParent.find($entityElement);
173 var entityInstanceID = entityElement.get(0).getAttribute('data-quickedit-entity-instance-id');
175 if (!metadata.has(fieldID)) {
176 fieldsMetadataQueue.push({
180 entityInstanceID: entityInstanceID
185 if (metadata.get(fieldID, 'access') !== true) {
189 if (Drupal.quickedit.collections.entities.findWhere({ entityID: entityID, entityInstanceID: entityInstanceID })) {
190 initializeField(fieldElement, fieldID, entityID, entityInstanceID);
192 fieldsAvailableQueue.push({ el: fieldElement, fieldID: fieldID, entityID: entityID, entityInstanceID: entityInstanceID });
196 function initializeField(fieldElement, fieldID, entityID, entityInstanceID) {
197 var entity = Drupal.quickedit.collections.entities.findWhere({
199 entityInstanceID: entityInstanceID
202 $(fieldElement).addClass('quickedit-field');
204 var field = new Drupal.quickedit.FieldModel({
207 id: fieldID + '[' + entity.get('entityInstanceID') + ']',
209 metadata: Drupal.quickedit.metadata.get(fieldID),
210 acceptStateChange: _.bind(Drupal.quickedit.app.acceptEditorStateChange, Drupal.quickedit.app)
213 Drupal.quickedit.collections.fields.add(field);
216 function fetchMissingMetadata(callback) {
217 if (fieldsMetadataQueue.length) {
218 var fieldIDs = _.pluck(fieldsMetadataQueue, 'fieldID');
219 var fieldElementsWithoutMetadata = _.pluck(fieldsMetadataQueue, 'el');
220 var entityIDs = _.uniq(_.pluck(fieldsMetadataQueue, 'entityID'), true);
222 entityIDs = _.difference(entityIDs, Drupal.quickedit.metadata.intersection(entityIDs));
223 fieldsMetadataQueue = [];
226 url: Drupal.url('quickedit/metadata'),
229 'fields[]': fieldIDs,
230 'entities[]': entityIDs
233 success: function success(results) {
234 _.each(results, function (fieldMetadata, fieldID) {
235 Drupal.quickedit.metadata.add(fieldID, fieldMetadata);
238 callback(fieldElementsWithoutMetadata);
244 function loadMissingEditors(callback) {
245 var loadedEditors = _.keys(Drupal.quickedit.editors);
246 var missingEditors = [];
247 Drupal.quickedit.collections.fields.each(function (fieldModel) {
248 var metadata = Drupal.quickedit.metadata.get(fieldModel.get('fieldID'));
249 if (metadata.access && _.indexOf(loadedEditors, metadata.editor) === -1) {
250 missingEditors.push(metadata.editor);
252 Drupal.quickedit.editors[metadata.editor] = false;
255 missingEditors = _.uniq(missingEditors);
256 if (missingEditors.length === 0) {
261 var loadEditorsAjax = Drupal.ajax({
262 url: Drupal.url('quickedit/attachments'),
263 submit: { 'editors[]': missingEditors }
266 var realInsert = Drupal.AjaxCommands.prototype.insert;
267 loadEditorsAjax.commands.insert = function (ajax, response, status) {
269 realInsert(ajax, response, status);
272 loadEditorsAjax.execute();
275 function initializeEntityContextualLink(contextualLink) {
276 var metadata = Drupal.quickedit.metadata;
278 function hasFieldWithPermission(fieldIDs) {
279 for (var i = 0; i < fieldIDs.length; i++) {
280 var fieldID = fieldIDs[i];
281 if (metadata.get(fieldID, 'access') === true) {
288 function allMetadataExists(fieldIDs) {
289 return fieldIDs.length === metadata.intersection(fieldIDs).length;
292 var fields = _.where(fieldsAvailableQueue, {
293 entityID: contextualLink.entityID,
294 entityInstanceID: contextualLink.entityInstanceID
296 var fieldIDs = _.pluck(fields, 'fieldID');
298 if (fieldIDs.length === 0) {
300 } else if (hasFieldWithPermission(fieldIDs)) {
301 var entityModel = new Drupal.quickedit.EntityModel({
302 el: contextualLink.region,
303 entityID: contextualLink.entityID,
304 entityInstanceID: contextualLink.entityInstanceID,
305 id: contextualLink.entityID + '[' + contextualLink.entityInstanceID + ']',
306 label: Drupal.quickedit.metadata.get(contextualLink.entityID, 'label')
308 Drupal.quickedit.collections.entities.add(entityModel);
310 var entityDecorationView = new Drupal.quickedit.EntityDecorationView({
311 el: contextualLink.region,
314 entityModel.set('entityDecorationView', entityDecorationView);
316 _.each(fields, function (field) {
317 initializeField(field.el, field.fieldID, contextualLink.entityID, contextualLink.entityInstanceID);
319 fieldsAvailableQueue = _.difference(fieldsAvailableQueue, fields);
321 var initContextualLink = _.once(function () {
322 var $links = $(contextualLink.el).find('.contextual-links');
323 var contextualLinkView = new Drupal.quickedit.ContextualLinkView($.extend({
324 el: $('<li class="quickedit"><a href="" role="button" aria-pressed="false"></a></li>').prependTo($links),
326 appModel: Drupal.quickedit.app.model
328 entityModel.set('contextualLinkView', contextualLinkView);
331 loadMissingEditors(initContextualLink);
334 } else if (allMetadataExists(fieldIDs)) {
341 function deleteContainedModelsAndQueues($context) {
342 $context.find('[data-quickedit-entity-id]').addBack('[data-quickedit-entity-id]').each(function (index, entityElement) {
343 var entityModel = Drupal.quickedit.collections.entities.findWhere({ el: entityElement });
345 var contextualLinkView = entityModel.get('contextualLinkView');
346 contextualLinkView.undelegateEvents();
347 contextualLinkView.remove();
349 entityModel.get('entityDecorationView').remove();
351 entityModel.destroy();
354 function hasOtherRegion(contextualLink) {
355 return contextualLink.region !== entityElement;
358 contextualLinksQueue = _.filter(contextualLinksQueue, hasOtherRegion);
361 $context.find('[data-quickedit-field-id]').addBack('[data-quickedit-field-id]').each(function (index, fieldElement) {
362 Drupal.quickedit.collections.fields.chain().filter(function (fieldModel) {
363 return fieldModel.get('el') === fieldElement;
364 }).invoke('destroy');
366 function hasOtherFieldElement(field) {
367 return field.el !== fieldElement;
370 fieldsMetadataQueue = _.filter(fieldsMetadataQueue, hasOtherFieldElement);
371 fieldsAvailableQueue = _.filter(fieldsAvailableQueue, hasOtherFieldElement);
374 })(jQuery, _, Backbone, Drupal, drupalSettings, window.JSON, window.sessionStorage);