drupal/core/misc/displace.js

176 lines
5.4 KiB
JavaScript
Raw Normal View History

/**
* Manages elements that can offset the size of the viewport.
*/
(function ($, Drupal, debounce) {
"use strict";
var offsets = {
top: 0,
right: 0,
bottom: 0,
left: 0
};
/**
* Registers a resize handler on the window.
*/
Drupal.behaviors.drupalDisplace = {
attach: function () {
// Mark this behavior as processed on the first pass.
if (this.displaceProcessed) {
return;
}
this.displaceProcessed = true;
$(window).on('resize.drupalDisplace', debounce(displace, 200));
}
};
/**
* Informs listeners of the current offset dimensions.
*
* @param {boolean} broadcast
* (optional) When true or undefined, causes the recalculated offsets values to be
* broadcast to listeners.
*
* @return {object}
* An object whose keys are the for sides an element -- top, right, bottom
* and left. The value of each key is the viewport displacement distance for
* that edge.
*/
function displace (broadcast) {
offsets = Drupal.displace.offsets = calculateOffsets();
if (typeof broadcast === 'undefined' || broadcast) {
$(document).trigger('drupalViewportOffsetChange', offsets);
}
return offsets;
}
/**
* Determines the viewport offsets.
*
* @return {object}
* An object whose keys are the for sides an element -- top, right, bottom
* and left. The value of each key is the viewport displacement distance for
* that edge.
*/
function calculateOffsets () {
return {
top: calculateOffset('top'),
right: calculateOffset('right'),
bottom: calculateOffset('bottom'),
left: calculateOffset('left')
};
}
/**
* Gets a specific edge's offset.
*
* Any element with the attribute data-offset-{edge} e.g. data-offset-top will
* be considered in the viewport offset calculations. If the attribute has a
* numeric value, that value will be used. If no value is provided, one will
* be calculated using the element's dimensions and placement.
*
* @param {string} edge
* The name of the edge to calculate. Can be 'top', 'right',
* 'bottom' or 'left'.
*
* @return {number}
* The viewport displacement distance for the requested edge.
*/
function calculateOffset (edge) {
var edgeOffset = 0;
var displacingElements = document.querySelectorAll('[data-offset-' + edge + ']');
for (var i = 0, n = displacingElements.length; i < n; i++) {
var el = displacingElements[i];
// If the element is not visble, do consider its dimensions.
if (el.style.display === 'none') {
continue;
}
// If the offset data attribute contains a displacing value, use it.
var displacement = parseInt(el.getAttribute('data-offset-' + edge), 10);
// If the element's offset data attribute exits
// but is not a valid number then get the displacement
// dimensions directly from the element.
if (isNaN(displacement)) {
displacement = getRawOffset(el, edge);
}
// If the displacement value is larger than the current value for this
// edge, use the displacement value.
edgeOffset = Math.max(edgeOffset, displacement);
}
return edgeOffset;
}
/**
* Calculates displacement for element based on its dimensions and placement.
*
* @param {jQuery} $el
* The jQuery element whose dimensions and placement will be measured.
*
* @param {string} edge
* The name of the edge of the viewport that the element is associated
* with.
*
* @return {number}
* The viewport displacement distance for the requested edge.
*/
function getRawOffset (el, edge) {
var $el = $(el);
var documentElement = document.documentElement;
var displacement = 0;
var horizontal = (edge === 'left' || edge === 'right');
// Get the offset of the element itself.
var placement = $el.offset()[ horizontal ? 'left' : 'top'];
// Subtract scroll distance from placement to get the distance
// to the edge of the viewport.
placement -= window['scroll' + (horizontal ? 'X' : 'Y')] || document.documentElement['scroll' + (horizontal) ? 'Left' : 'Top'] || 0;
// Find the displacement value according to the edge.
switch (edge) {
// Left and top elements displace as a sum of their own offset value
// plus their size.
case 'top':
// Total displacment is the sum of the elements placement and size.
displacement = placement + $el.outerHeight();
break;
case 'left':
// Total displacment is the sum of the elements placement and size.
displacement = placement + $el.outerWidth();
break;
// Right and bottom elements displace according to their left and
// top offset. Their size isn't important.
case 'bottom':
displacement = documentElement.clientHeight - placement;
break;
case 'right':
displacement = documentElement.clientWidth - placement;
break;
default:
displacement = 0;
}
return displacement;
}
/**
* Assign the displace function to a property of the Drupal global object.
*/
Drupal.displace = displace;
$.extend(Drupal.displace, {
/**
* Expose offsets to other scripts to avoid having to recalculate offsets
*/
offsets: offsets,
/**
* Expose method to compute a single edge offsets.
*/
calculateOffset: calculateOffset
});
})(jQuery, Drupal, Drupal.debounce);