summaryrefslogtreecommitdiffstatshomepage
path: root/core/modules/contextual/js/contextual.es6.js
diff options
context:
space:
mode:
authorAlex Pott <alex.a.pott@googlemail.com>2017-05-19 23:12:53 +0100
committerAlex Pott <alex.a.pott@googlemail.com>2017-05-19 23:12:53 +0100
commit8287017e034bc323dec1d86b3f37a804aa082d2d (patch)
treeebd8ff908859b0e1cc4319392da94e8f68033321 /core/modules/contextual/js/contextual.es6.js
parent9a0e9a649ac8078ce6e5f6089749a1115bdda06b (diff)
downloaddrupal-8287017e034bc323dec1d86b3f37a804aa082d2d.tar.gz
drupal-8287017e034bc323dec1d86b3f37a804aa082d2d.zip
Issue #2818825 by drpal, nod_, droplet, cilefen: Rename all JS files to *.es6.js and compile them
Diffstat (limited to 'core/modules/contextual/js/contextual.es6.js')
-rw-r--r--core/modules/contextual/js/contextual.es6.js256
1 files changed, 256 insertions, 0 deletions
diff --git a/core/modules/contextual/js/contextual.es6.js b/core/modules/contextual/js/contextual.es6.js
new file mode 100644
index 000000000000..558ea105cc2f
--- /dev/null
+++ b/core/modules/contextual/js/contextual.es6.js
@@ -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 && html.length) {
+ // 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);