annotate core/modules/quickedit/js/quickedit.js @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents 129ea1e6d783
children
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@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);