Backup of db before drupal security update
[yaffs-website] / web / core / misc / displace.js
1 /**
2  * @file
3  * Manages elements that can offset the size of the viewport.
4  *
5  * Measures and reports viewport offset dimensions from elements like the
6  * toolbar that can potentially displace the positioning of other elements.
7  */
8
9 /**
10  * @typedef {object} Drupal~displaceOffset
11  *
12  * @prop {number} top
13  * @prop {number} left
14  * @prop {number} right
15  * @prop {number} bottom
16  */
17
18 /**
19  * Triggers when layout of the page changes.
20  *
21  * This is used to position fixed element on the page during page resize and
22  * Toolbar toggling.
23  *
24  * @event drupalViewportOffsetChange
25  */
26
27 (function ($, Drupal, debounce) {
28
29   'use strict';
30
31   /**
32    * @name Drupal.displace.offsets
33    *
34    * @type {Drupal~displaceOffset}
35    */
36   var offsets = {
37     top: 0,
38     right: 0,
39     bottom: 0,
40     left: 0
41   };
42
43   /**
44    * Registers a resize handler on the window.
45    *
46    * @type {Drupal~behavior}
47    */
48   Drupal.behaviors.drupalDisplace = {
49     attach: function () {
50       // Mark this behavior as processed on the first pass.
51       if (this.displaceProcessed) {
52         return;
53       }
54       this.displaceProcessed = true;
55
56       $(window).on('resize.drupalDisplace', debounce(displace, 200));
57     }
58   };
59
60   /**
61    * Informs listeners of the current offset dimensions.
62    *
63    * @function Drupal.displace
64    *
65    * @prop {Drupal~displaceOffset} offsets
66    *
67    * @param {bool} [broadcast]
68    *   When true or undefined, causes the recalculated offsets values to be
69    *   broadcast to listeners.
70    *
71    * @return {Drupal~displaceOffset}
72    *   An object whose keys are the for sides an element -- top, right, bottom
73    *   and left. The value of each key is the viewport displacement distance for
74    *   that edge.
75    *
76    * @fires event:drupalViewportOffsetChange
77    */
78   function displace(broadcast) {
79     offsets = Drupal.displace.offsets = calculateOffsets();
80     if (typeof broadcast === 'undefined' || broadcast) {
81       $(document).trigger('drupalViewportOffsetChange', offsets);
82     }
83     return offsets;
84   }
85
86   /**
87    * Determines the viewport offsets.
88    *
89    * @return {Drupal~displaceOffset}
90    *   An object whose keys are the for sides an element -- top, right, bottom
91    *   and left. The value of each key is the viewport displacement distance for
92    *   that edge.
93    */
94   function calculateOffsets() {
95     return {
96       top: calculateOffset('top'),
97       right: calculateOffset('right'),
98       bottom: calculateOffset('bottom'),
99       left: calculateOffset('left')
100     };
101   }
102
103   /**
104    * Gets a specific edge's offset.
105    *
106    * Any element with the attribute data-offset-{edge} e.g. data-offset-top will
107    * be considered in the viewport offset calculations. If the attribute has a
108    * numeric value, that value will be used. If no value is provided, one will
109    * be calculated using the element's dimensions and placement.
110    *
111    * @function Drupal.displace.calculateOffset
112    *
113    * @param {string} edge
114    *   The name of the edge to calculate. Can be 'top', 'right',
115    *   'bottom' or 'left'.
116    *
117    * @return {number}
118    *   The viewport displacement distance for the requested edge.
119    */
120   function calculateOffset(edge) {
121     var edgeOffset = 0;
122     var displacingElements = document.querySelectorAll('[data-offset-' + edge + ']');
123     var n = displacingElements.length;
124     for (var i = 0; i < n; i++) {
125       var el = displacingElements[i];
126       // If the element is not visible, do consider its dimensions.
127       if (el.style.display === 'none') {
128         continue;
129       }
130       // If the offset data attribute contains a displacing value, use it.
131       var displacement = parseInt(el.getAttribute('data-offset-' + edge), 10);
132       // If the element's offset data attribute exits
133       // but is not a valid number then get the displacement
134       // dimensions directly from the element.
135       if (isNaN(displacement)) {
136         displacement = getRawOffset(el, edge);
137       }
138       // If the displacement value is larger than the current value for this
139       // edge, use the displacement value.
140       edgeOffset = Math.max(edgeOffset, displacement);
141     }
142
143     return edgeOffset;
144   }
145
146   /**
147    * Calculates displacement for element based on its dimensions and placement.
148    *
149    * @param {HTMLElement} el
150    *   The jQuery element whose dimensions and placement will be measured.
151    *
152    * @param {string} edge
153    *   The name of the edge of the viewport that the element is associated
154    *   with.
155    *
156    * @return {number}
157    *   The viewport displacement distance for the requested edge.
158    */
159   function getRawOffset(el, edge) {
160     var $el = $(el);
161     var documentElement = document.documentElement;
162     var displacement = 0;
163     var horizontal = (edge === 'left' || edge === 'right');
164     // Get the offset of the element itself.
165     var placement = $el.offset()[horizontal ? 'left' : 'top'];
166     // Subtract scroll distance from placement to get the distance
167     // to the edge of the viewport.
168     placement -= window['scroll' + (horizontal ? 'X' : 'Y')] || document.documentElement['scroll' + (horizontal ? 'Left' : 'Top')] || 0;
169     // Find the displacement value according to the edge.
170     switch (edge) {
171       // Left and top elements displace as a sum of their own offset value
172       // plus their size.
173       case 'top':
174         // Total displacement is the sum of the elements placement and size.
175         displacement = placement + $el.outerHeight();
176         break;
177
178       case 'left':
179         // Total displacement is the sum of the elements placement and size.
180         displacement = placement + $el.outerWidth();
181         break;
182
183       // Right and bottom elements displace according to their left and
184       // top offset. Their size isn't important.
185       case 'bottom':
186         displacement = documentElement.clientHeight - placement;
187         break;
188
189       case 'right':
190         displacement = documentElement.clientWidth - placement;
191         break;
192
193       default:
194         displacement = 0;
195     }
196     return displacement;
197   }
198
199   /**
200    * Assign the displace function to a property of the Drupal global object.
201    *
202    * @ignore
203    */
204   Drupal.displace = displace;
205   $.extend(Drupal.displace, {
206
207     /**
208      * Expose offsets to other scripts to avoid having to recalculate offsets.
209      *
210      * @ignore
211      */
212     offsets: offsets,
213
214     /**
215      * Expose method to compute a single edge offsets.
216      *
217      * @ignore
218      */
219     calculateOffset: calculateOffset
220   });
221
222 })(jQuery, Drupal, Drupal.debounce);