Move into nested docroot
This commit is contained in:
parent
83a0d3a149
commit
c8b70abde9
13405 changed files with 0 additions and 0 deletions
web/core/modules/contextual/js
256
web/core/modules/contextual/js/contextual.js
Normal file
256
web/core/modules/contextual/js/contextual.js
Normal file
|
@ -0,0 +1,256 @@
|
|||
/**
|
||||
* @file
|
||||
* Attaches behaviors for the Contextual module.
|
||||
*/
|
||||
|
||||
(function ($, Drupal, drupalSettings, _, Backbone, JSON, storage) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var options = $.extend(drupalSettings.contextual,
|
||||
// Merge strings on top of drupalSettings so that they are not mutable.
|
||||
{
|
||||
strings: {
|
||||
open: Drupal.t('Open'),
|
||||
close: Drupal.t('Close')
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Clear the cached contextual links whenever the current user's set of
|
||||
// permissions changes.
|
||||
var cachedPermissionsHash = storage.getItem('Drupal.contextual.permissionsHash');
|
||||
var permissionsHash = drupalSettings.user.permissionsHash;
|
||||
if (cachedPermissionsHash !== permissionsHash) {
|
||||
if (typeof permissionsHash === 'string') {
|
||||
_.chain(storage).keys().each(function (key) {
|
||||
if (key.substring(0, 18) === 'Drupal.contextual.') {
|
||||
storage.removeItem(key);
|
||||
}
|
||||
});
|
||||
}
|
||||
storage.setItem('Drupal.contextual.permissionsHash', permissionsHash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a contextual link: updates its DOM, sets up model and views.
|
||||
*
|
||||
* @param {jQuery} $contextual
|
||||
* A contextual links placeholder DOM element, containing the actual
|
||||
* contextual links as rendered by the server.
|
||||
* @param {string} html
|
||||
* The server-side rendered HTML for this contextual link.
|
||||
*/
|
||||
function initContextual($contextual, html) {
|
||||
var $region = $contextual.closest('.contextual-region');
|
||||
var contextual = Drupal.contextual;
|
||||
|
||||
$contextual
|
||||
// Update the placeholder to contain its rendered contextual links.
|
||||
.html(html)
|
||||
// Use the placeholder as a wrapper with a specific class to provide
|
||||
// positioning and behavior attachment context.
|
||||
.addClass('contextual')
|
||||
// Ensure a trigger element exists before the actual contextual links.
|
||||
.prepend(Drupal.theme('contextualTrigger'));
|
||||
|
||||
// Set the destination parameter on each of the contextual links.
|
||||
var destination = 'destination=' + Drupal.encodePath(drupalSettings.path.currentPath);
|
||||
$contextual.find('.contextual-links a').each(function () {
|
||||
var url = this.getAttribute('href');
|
||||
var glue = (url.indexOf('?') === -1) ? '?' : '&';
|
||||
this.setAttribute('href', url + glue + destination);
|
||||
});
|
||||
|
||||
// Create a model and the appropriate views.
|
||||
var model = new contextual.StateModel({
|
||||
title: $region.find('h2').eq(0).text().trim()
|
||||
});
|
||||
var viewOptions = $.extend({el: $contextual, model: model}, options);
|
||||
contextual.views.push({
|
||||
visual: new contextual.VisualView(viewOptions),
|
||||
aural: new contextual.AuralView(viewOptions),
|
||||
keyboard: new contextual.KeyboardView(viewOptions)
|
||||
});
|
||||
contextual.regionViews.push(new contextual.RegionView(
|
||||
$.extend({el: $region, model: model}, options))
|
||||
);
|
||||
|
||||
// Add the model to the collection. This must happen after the views have
|
||||
// been associated with it, otherwise collection change event handlers can't
|
||||
// trigger the model change event handler in its views.
|
||||
contextual.collection.add(model);
|
||||
|
||||
// Let other JavaScript react to the adding of a new contextual link.
|
||||
$(document).trigger('drupalContextualLinkAdded', {
|
||||
$el: $contextual,
|
||||
$region: $region,
|
||||
model: model
|
||||
});
|
||||
|
||||
// Fix visual collisions between contextual link triggers.
|
||||
adjustIfNestedAndOverlapping($contextual);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a contextual link is nested & overlapping, if so: adjusts it.
|
||||
*
|
||||
* This only deals with two levels of nesting; deeper levels are not touched.
|
||||
*
|
||||
* @param {jQuery} $contextual
|
||||
* A contextual links placeholder DOM element, containing the actual
|
||||
* contextual links as rendered by the server.
|
||||
*/
|
||||
function adjustIfNestedAndOverlapping($contextual) {
|
||||
var $contextuals = $contextual
|
||||
// @todo confirm that .closest() is not sufficient
|
||||
.parents('.contextual-region').eq(-1)
|
||||
.find('.contextual');
|
||||
|
||||
// Early-return when there's no nesting.
|
||||
if ($contextuals.length === 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the two contextual links overlap, then we move the second one.
|
||||
var firstTop = $contextuals.eq(0).offset().top;
|
||||
var secondTop = $contextuals.eq(1).offset().top;
|
||||
if (firstTop === secondTop) {
|
||||
var $nestedContextual = $contextuals.eq(1);
|
||||
|
||||
// Retrieve height of nested contextual link.
|
||||
var height = 0;
|
||||
var $trigger = $nestedContextual.find('.trigger');
|
||||
// Elements with the .visually-hidden class have no dimensions, so this
|
||||
// class must be temporarily removed to the calculate the height.
|
||||
$trigger.removeClass('visually-hidden');
|
||||
height = $nestedContextual.height();
|
||||
$trigger.addClass('visually-hidden');
|
||||
|
||||
// Adjust nested contextual link's position.
|
||||
$nestedContextual.css({top: $nestedContextual.position().top + height});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches outline behavior for regions associated with contextual links.
|
||||
*
|
||||
* Events
|
||||
* Contextual triggers an event that can be used by other scripts.
|
||||
* - drupalContextualLinkAdded: Triggered when a contextual link is added.
|
||||
*
|
||||
* @type {Drupal~behavior}
|
||||
*
|
||||
* @prop {Drupal~behaviorAttach} attach
|
||||
* Attaches the outline behavior to the right context.
|
||||
*/
|
||||
Drupal.behaviors.contextual = {
|
||||
attach: function (context) {
|
||||
var $context = $(context);
|
||||
|
||||
// Find all contextual links placeholders, if any.
|
||||
var $placeholders = $context.find('[data-contextual-id]').once('contextual-render');
|
||||
if ($placeholders.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Collect the IDs for all contextual links placeholders.
|
||||
var ids = [];
|
||||
$placeholders.each(function () {
|
||||
ids.push($(this).attr('data-contextual-id'));
|
||||
});
|
||||
|
||||
// Update all contextual links placeholders whose HTML is cached.
|
||||
var uncachedIDs = _.filter(ids, function initIfCached(contextualID) {
|
||||
var html = storage.getItem('Drupal.contextual.' + contextualID);
|
||||
if (html !== null) {
|
||||
// Initialize after the current execution cycle, to make the AJAX
|
||||
// request for retrieving the uncached contextual links as soon as
|
||||
// possible, but also to ensure that other Drupal behaviors have had
|
||||
// the chance to set up an event listener on the Backbone collection
|
||||
// Drupal.contextual.collection.
|
||||
window.setTimeout(function () {
|
||||
initContextual($context.find('[data-contextual-id="' + contextualID + '"]'), html);
|
||||
});
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
// Perform an AJAX request to let the server render the contextual links
|
||||
// for each of the placeholders.
|
||||
if (uncachedIDs.length > 0) {
|
||||
$.ajax({
|
||||
url: Drupal.url('contextual/render'),
|
||||
type: 'POST',
|
||||
data: {'ids[]': uncachedIDs},
|
||||
dataType: 'json',
|
||||
success: function (results) {
|
||||
_.each(results, function (html, contextualID) {
|
||||
// Store the metadata.
|
||||
storage.setItem('Drupal.contextual.' + contextualID, html);
|
||||
// If the rendered contextual links are empty, then the current
|
||||
// user does not have permission to access the associated links:
|
||||
// don't render anything.
|
||||
if (html.length > 0) {
|
||||
// Update the placeholders to contain its rendered contextual
|
||||
// links. Usually there will only be one placeholder, but it's
|
||||
// possible for multiple identical placeholders exist on the
|
||||
// page (probably because the same content appears more than
|
||||
// once).
|
||||
$placeholders = $context.find('[data-contextual-id="' + contextualID + '"]');
|
||||
|
||||
// Initialize the contextual links.
|
||||
for (var i = 0; i < $placeholders.length; i++) {
|
||||
initContextual($placeholders.eq(i), html);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Namespace for contextual related functionality.
|
||||
*
|
||||
* @namespace
|
||||
*/
|
||||
Drupal.contextual = {
|
||||
|
||||
/**
|
||||
* The {@link Drupal.contextual.View} instances associated with each list
|
||||
* element of contextual links.
|
||||
*
|
||||
* @type {Array}
|
||||
*/
|
||||
views: [],
|
||||
|
||||
/**
|
||||
* The {@link Drupal.contextual.RegionView} instances associated with each
|
||||
* contextual region element.
|
||||
*
|
||||
* @type {Array}
|
||||
*/
|
||||
regionViews: []
|
||||
};
|
||||
|
||||
/**
|
||||
* A Backbone.Collection of {@link Drupal.contextual.StateModel} instances.
|
||||
*
|
||||
* @type {Backbone.Collection}
|
||||
*/
|
||||
Drupal.contextual.collection = new Backbone.Collection([], {model: Drupal.contextual.StateModel});
|
||||
|
||||
/**
|
||||
* A trigger is an interactive element often bound to a click handler.
|
||||
*
|
||||
* @return {string}
|
||||
* A string representing a DOM fragment.
|
||||
*/
|
||||
Drupal.theme.contextualTrigger = function () {
|
||||
return '<button class="trigger visually-hidden focusable" type="button"></button>';
|
||||
};
|
||||
|
||||
})(jQuery, Drupal, drupalSettings, _, Backbone, window.JSON, window.sessionStorage);
|
Reference in a new issue