2015-08-17 17:00:26 -07:00
/ * *
* @ file
* Manages elements that can offset the size of the viewport .
*
* Measures and reports viewport offset dimensions from elements like the
* toolbar that can potentially displace the positioning of other elements .
* /
/ * *
* @ typedef { object } Drupal ~ displaceOffset
*
* @ prop { number } top
* @ prop { number } left
* @ prop { number } right
* @ prop { number } bottom
* /
/ * *
* Triggers when layout of the page changes .
*
* This is used to position fixed element on the page during page resize and
* Toolbar toggling .
*
* @ event drupalViewportOffsetChange
* /
( function ( $ , Drupal , debounce ) {
2015-10-21 21:44:50 -07:00
'use strict' ;
2015-08-17 17:00:26 -07:00
/ * *
* @ name Drupal . displace . offsets
*
* @ type { Drupal ~ displaceOffset }
* /
var offsets = {
top : 0 ,
right : 0 ,
bottom : 0 ,
left : 0
} ;
/ * *
* Registers a resize handler on the window .
*
* @ type { Drupal ~ behavior }
* /
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 .
*
* @ function Drupal . displace
*
* @ prop { Drupal ~ displaceOffset } offsets
*
* @ param { bool } [ broadcast ]
* When true or undefined , causes the recalculated offsets values to be
* broadcast to listeners .
*
* @ return { Drupal ~ displaceOffset }
* 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 .
*
* @ fires event : drupalViewportOffsetChange
* /
function displace ( broadcast ) {
offsets = Drupal . displace . offsets = calculateOffsets ( ) ;
if ( typeof broadcast === 'undefined' || broadcast ) {
$ ( document ) . trigger ( 'drupalViewportOffsetChange' , offsets ) ;
}
return offsets ;
}
/ * *
* Determines the viewport offsets .
*
* @ return { Drupal ~ displaceOffset }
* 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 .
*
* @ function Drupal . displace . calculateOffset
*
* @ 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 + ']' ) ;
var n = displacingElements . length ;
for ( var i = 0 ; i < n ; i ++ ) {
var el = displacingElements [ i ] ;
// If the element is not visible, 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 { HTMLElement } 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 displacement is the sum of the elements placement and size.
displacement = placement + $el . outerHeight ( ) ;
break ;
case 'left' :
// Total displacement 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 .
*
* @ ignore
* /
Drupal . displace = displace ;
$ . extend ( Drupal . displace , {
/ * *
* Expose offsets to other scripts to avoid having to recalculate offsets .
*
* @ ignore
* /
offsets : offsets ,
/ * *
* Expose method to compute a single edge offsets .
*
* @ ignore
* /
calculateOffset : calculateOffset
} ) ;
} ) ( jQuery , Drupal , Drupal . debounce ) ;