drupal/core/modules/quickedit/js/quickedit.js

386 lines
12 KiB
JavaScript

/**
* DO NOT EDIT THIS FILE.
* See the following change record for more information,
* https://www.drupal.org/node/2815083
* @preserve
**/
(function ($, _, Backbone, Drupal, drupalSettings, JSON, storage) {
const options = $.extend(drupalSettings.quickedit, {
strings: {
quickEdit: Drupal.t('Quick edit')
}
});
let fieldsMetadataQueue = [];
let fieldsAvailableQueue = [];
let contextualLinksQueue = [];
const entityInstancesTracker = {};
function initQuickEdit(bodyElement) {
Drupal.quickedit.collections.entities = new Drupal.quickedit.EntityCollection();
Drupal.quickedit.collections.fields = new Drupal.quickedit.FieldCollection();
Drupal.quickedit.app = new Drupal.quickedit.AppView({
el: bodyElement,
model: new Drupal.quickedit.AppModel(),
entitiesCollection: Drupal.quickedit.collections.entities,
fieldsCollection: Drupal.quickedit.collections.fields
});
}
function processEntity(entityElement) {
const entityID = entityElement.getAttribute('data-quickedit-entity-id');
if (!entityInstancesTracker.hasOwnProperty(entityID)) {
entityInstancesTracker[entityID] = 0;
} else {
entityInstancesTracker[entityID]++;
}
const entityInstanceID = entityInstancesTracker[entityID];
entityElement.setAttribute('data-quickedit-entity-instance-id', entityInstanceID);
}
function initializeField(fieldElement, fieldID, entityID, entityInstanceID) {
const entity = Drupal.quickedit.collections.entities.findWhere({
entityID,
entityInstanceID
});
$(fieldElement).addClass('quickedit-field');
const field = new Drupal.quickedit.FieldModel({
el: fieldElement,
fieldID,
id: `${fieldID}[${entity.get('entityInstanceID')}]`,
entity,
metadata: Drupal.quickedit.metadata.get(fieldID),
acceptStateChange: _.bind(Drupal.quickedit.app.acceptEditorStateChange, Drupal.quickedit.app)
});
Drupal.quickedit.collections.fields.add(field);
}
function loadMissingEditors(callback) {
const loadedEditors = _.keys(Drupal.quickedit.editors);
let missingEditors = [];
Drupal.quickedit.collections.fields.each(fieldModel => {
const metadata = Drupal.quickedit.metadata.get(fieldModel.get('fieldID'));
if (metadata.access && _.indexOf(loadedEditors, metadata.editor) === -1) {
missingEditors.push(metadata.editor);
Drupal.quickedit.editors[metadata.editor] = false;
}
});
missingEditors = _.uniq(missingEditors);
if (missingEditors.length === 0) {
callback();
return;
}
const loadEditorsAjax = Drupal.ajax({
url: Drupal.url('quickedit/attachments'),
submit: {
'editors[]': missingEditors
}
});
const realInsert = Drupal.AjaxCommands.prototype.insert;
loadEditorsAjax.commands.insert = function (ajax, response, status) {
_.defer(callback);
realInsert(ajax, response, status);
};
loadEditorsAjax.execute();
}
function initializeEntityContextualLink(contextualLink) {
const metadata = Drupal.quickedit.metadata;
function hasFieldWithPermission(fieldIDs) {
for (let i = 0; i < fieldIDs.length; i++) {
const fieldID = fieldIDs[i];
if (metadata.get(fieldID, 'access') === true) {
return true;
}
}
return false;
}
function allMetadataExists(fieldIDs) {
return fieldIDs.length === metadata.intersection(fieldIDs).length;
}
const fields = _.where(fieldsAvailableQueue, {
entityID: contextualLink.entityID,
entityInstanceID: contextualLink.entityInstanceID
});
const fieldIDs = _.pluck(fields, 'fieldID');
if (fieldIDs.length === 0) {
return false;
}
if (hasFieldWithPermission(fieldIDs)) {
const entityModel = new Drupal.quickedit.EntityModel({
el: contextualLink.region,
entityID: contextualLink.entityID,
entityInstanceID: contextualLink.entityInstanceID,
id: `${contextualLink.entityID}[${contextualLink.entityInstanceID}]`,
label: Drupal.quickedit.metadata.get(contextualLink.entityID, 'label')
});
Drupal.quickedit.collections.entities.add(entityModel);
const entityDecorationView = new Drupal.quickedit.EntityDecorationView({
el: contextualLink.region,
model: entityModel
});
entityModel.set('entityDecorationView', entityDecorationView);
_.each(fields, field => {
initializeField(field.el, field.fieldID, contextualLink.entityID, contextualLink.entityInstanceID);
});
fieldsAvailableQueue = _.difference(fieldsAvailableQueue, fields);
const initContextualLink = _.once(() => {
const $links = $(contextualLink.el).find('.contextual-links');
const contextualLinkView = new Drupal.quickedit.ContextualLinkView($.extend({
el: $('<li class="quickedit"><a href="" role="button" aria-pressed="false"></a></li>').prependTo($links),
model: entityModel,
appModel: Drupal.quickedit.app.model
}, options));
entityModel.set('contextualLinkView', contextualLinkView);
});
loadMissingEditors(initContextualLink);
return true;
}
if (allMetadataExists(fieldIDs)) {
return true;
}
return false;
}
function extractEntityID(fieldID) {
return fieldID.split('/').slice(0, 2).join('/');
}
function processField(fieldElement) {
const metadata = Drupal.quickedit.metadata;
const fieldID = fieldElement.getAttribute('data-quickedit-field-id');
const entityID = extractEntityID(fieldID);
const entityElementSelector = `[data-quickedit-entity-id="${entityID}"]`;
const $entityElement = $(entityElementSelector);
if (!$entityElement.length) {
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.`);
}
let entityElement = $(fieldElement).closest($entityElement);
if (entityElement.length === 0) {
const $lowestCommonParent = $entityElement.parents().has(fieldElement).first();
entityElement = $lowestCommonParent.find($entityElement);
}
const entityInstanceID = entityElement.get(0).getAttribute('data-quickedit-entity-instance-id');
if (!metadata.has(fieldID)) {
fieldsMetadataQueue.push({
el: fieldElement,
fieldID,
entityID,
entityInstanceID
});
return;
}
if (metadata.get(fieldID, 'access') !== true) {
return;
}
if (Drupal.quickedit.collections.entities.findWhere({
entityID,
entityInstanceID
})) {
initializeField(fieldElement, fieldID, entityID, entityInstanceID);
} else {
fieldsAvailableQueue.push({
el: fieldElement,
fieldID,
entityID,
entityInstanceID
});
}
}
function deleteContainedModelsAndQueues($context) {
$context.find('[data-quickedit-entity-id]').addBack('[data-quickedit-entity-id]').each((index, entityElement) => {
const entityModel = Drupal.quickedit.collections.entities.findWhere({
el: entityElement
});
if (entityModel) {
const contextualLinkView = entityModel.get('contextualLinkView');
contextualLinkView.undelegateEvents();
contextualLinkView.remove();
entityModel.get('entityDecorationView').remove();
entityModel.destroy();
}
function hasOtherRegion(contextualLink) {
return contextualLink.region !== entityElement;
}
contextualLinksQueue = _.filter(contextualLinksQueue, hasOtherRegion);
});
$context.find('[data-quickedit-field-id]').addBack('[data-quickedit-field-id]').each((index, fieldElement) => {
Drupal.quickedit.collections.fields.chain().filter(fieldModel => fieldModel.get('el') === fieldElement).invoke('destroy');
function hasOtherFieldElement(field) {
return field.el !== fieldElement;
}
fieldsMetadataQueue = _.filter(fieldsMetadataQueue, hasOtherFieldElement);
fieldsAvailableQueue = _.filter(fieldsAvailableQueue, hasOtherFieldElement);
});
}
function fetchMissingMetadata(callback) {
if (fieldsMetadataQueue.length) {
const fieldIDs = _.pluck(fieldsMetadataQueue, 'fieldID');
const fieldElementsWithoutMetadata = _.pluck(fieldsMetadataQueue, 'el');
let entityIDs = _.uniq(_.pluck(fieldsMetadataQueue, 'entityID'), true);
entityIDs = _.difference(entityIDs, Drupal.quickedit.metadata.intersection(entityIDs));
fieldsMetadataQueue = [];
$.ajax({
url: Drupal.url('quickedit/metadata'),
type: 'POST',
data: {
'fields[]': fieldIDs,
'entities[]': entityIDs
},
dataType: 'json',
success(results) {
_.each(results, (fieldMetadata, fieldID) => {
Drupal.quickedit.metadata.add(fieldID, fieldMetadata);
});
callback(fieldElementsWithoutMetadata);
}
});
}
}
Drupal.behaviors.quickedit = {
attach(context) {
once('quickedit-init', 'body').forEach(initQuickEdit);
const fields = once('quickedit', '[data-quickedit-field-id]', context);
if (fields.length === 0) {
return;
}
once('quickedit', '[data-quickedit-entity-id]', context).forEach(processEntity);
fields.forEach(processField);
contextualLinksQueue = _.filter(contextualLinksQueue, contextualLink => !initializeEntityContextualLink(contextualLink));
fetchMissingMetadata(fieldElementsWithFreshMetadata => {
_.each(fieldElementsWithFreshMetadata, processField);
contextualLinksQueue = _.filter(contextualLinksQueue, contextualLink => !initializeEntityContextualLink(contextualLink));
});
},
detach(context, settings, trigger) {
if (trigger === 'unload') {
deleteContainedModelsAndQueues($(context));
}
}
};
Drupal.quickedit = {
app: null,
collections: {
entities: null,
fields: null
},
editors: {},
metadata: {
has(fieldID) {
return storage.getItem(this._prefixFieldID(fieldID)) !== null;
},
add(fieldID, metadata) {
storage.setItem(this._prefixFieldID(fieldID), JSON.stringify(metadata));
},
get(fieldID, key) {
const metadata = JSON.parse(storage.getItem(this._prefixFieldID(fieldID)));
return typeof key === 'undefined' ? metadata : metadata[key];
},
_prefixFieldID(fieldID) {
return `Drupal.quickedit.metadata.${fieldID}`;
},
_unprefixFieldID(fieldID) {
return fieldID.substring(26);
},
intersection(fieldIDs) {
const prefixedFieldIDs = _.map(fieldIDs, this._prefixFieldID);
const intersection = _.intersection(prefixedFieldIDs, _.keys(sessionStorage));
return _.map(intersection, this._unprefixFieldID);
}
}
};
const permissionsHashKey = Drupal.quickedit.metadata._prefixFieldID('permissionsHash');
const permissionsHashValue = storage.getItem(permissionsHashKey);
const permissionsHash = drupalSettings.user.permissionsHash;
if (permissionsHashValue !== permissionsHash) {
if (typeof permissionsHash === 'string') {
_.chain(storage).keys().each(key => {
if (key.substring(0, 26) === 'Drupal.quickedit.metadata.') {
storage.removeItem(key);
}
});
}
storage.setItem(permissionsHashKey, permissionsHash);
}
$(document).on('drupalContextualLinkAdded', (event, data) => {
if (data.$region.is('[data-quickedit-entity-id]')) {
if (!data.$region.is('[data-quickedit-entity-instance-id]')) {
once('quickedit', data.$region);
processEntity(data.$region.get(0));
}
const contextualLink = {
entityID: data.$region.attr('data-quickedit-entity-id'),
entityInstanceID: data.$region.attr('data-quickedit-entity-instance-id'),
el: data.$el[0],
region: data.$region[0]
};
if (!initializeEntityContextualLink(contextualLink)) {
contextualLinksQueue.push(contextualLink);
}
}
});
})(jQuery, _, Backbone, Drupal, drupalSettings, window.JSON, window.sessionStorage);