/** * 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);