annotate core/modules/quickedit/js/quickedit.js @ 15:e200cb7efeb3

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