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