PageSourceSearch

https://www.natcouncil.com/sites/default/files/js/optimized/js_7SL…NNBy6sFKF5jcM6Qs3qqkEmREIuMvSk.js?v=10.5.10

js natcouncil.com collected 2026-09-29 18:18:36 UTC 22,803 bytes, 635 lines download raw bytes

1/* Source and licensing information for the line(s) below can be found at https://natcouncil-website.glueuphosting.com/core/misc/position.js. */
2/**
3 * @file
4 * A modified version of jQuery UI position.
5 *
6 * Per jQuery UI's public domain license, it is permissible to run modified
7 * versions of their code. This file offers the same functionality as what is
8 * provided by jQuery UI position, but refactored to meet Drupal coding
9 * standards, and restructured so it extends jQuery core instead of jQuery UI.
10 *
11 * This is provided to support pre-existing code that expects the jQuery
12 * position API.
13 *
14 * @see https://github.com/jquery/jquery-ui/blob/1.12.1/LICENSE.txt
15 * @see https://raw.githubusercontent.com/jquery/jquery-ui/1.12.1/ui/position.js
16 */
17
18/**
19 * This provides ported version of jQuery UI position, refactored to not depend
20 * on jQuery UI and to meet Drupal JavaScript coding standards. Functionality
21 * and usage is identical. It positions an element relative to another. The
22 * `position()` function can be called by any jQuery object. Additional details
23 * on using `position()` are provided in this file in the docblock for
24 * $.fn.position.
25 */
26(($) => {
27  let cachedScrollbarWidth = null;
28  const { max, abs } = Math;
29  const regexHorizontal = /left|center|right/;
30  const regexVertical = /top|center|bottom/;
31  const regexOffset = /[+-]\d+(\.[\d]+)?%?/;
32  const regexPosition = /^\w+/;
33  const _position = $.fn.position;
34
35  function getOffsets(offsets, width, height) {
36    return [
37      parseFloat(offsets[0]) *
38        (typeof offsets[0] === 'string' && offsets[0].endsWith('%')
39          ? width / 100
40          : 1),
41      parseFloat(offsets[1]) *
42        (typeof offsets[1] === 'string' && offsets[1].endsWith('%')
43          ? height / 100
44          : 1),
45    ];
46  }
47
48  function parseCss(element, property) {
49    return parseInt(window.getComputedStyle(element)[property], 10) || 0;
50  }
51
52  function getDimensions(elem) {
53    const raw = elem[0];
54    if (raw.nodeType === 9) {
55      return {
56        width: elem.width(),
57        height: elem.height(),
58        offset: { top: 0, left: 0 },
59      };
60    }
61    if (!!raw && raw === raw.window) {
62      return {
63        width: elem.width(),
64        height: elem.height(),
65        offset: { top: elem.scrollTop(), left: elem.scrollLeft() },
66      };
67    }
68    if (raw.preventDefault) {
69      return {
70        width: 0,
71        height: 0,
72        offset: { top: raw.pageY, left: raw.pageX },
73      };
74    }
75    return {
76      width: elem.outerWidth(),
77      height: elem.outerHeight(),
78      offset: elem.offset(),
79    };
80  }
81
82  const collisions = {
83    fit: {
84      left(position, data) {
85        const { within } = data;
86        const withinOffset = within.isWindow
87          ? within.scrollLeft
88          : within.offset.left;
89        const outerWidth = within.width;
90        const collisionPosLeft =
91          position.left - data.collisionPosition.marginLeft;
92        const overLeft = withinOffset - collisionPosLeft;
93        const overRight =
94          collisionPosLeft + data.collisionWidth - outerWidth - withinOffset;
95        let newOverRight;
96
97        // Element is wider than within
98        if (data.collisionWidth > outerWidth) {
99          // Element is initially over the left side of within
100          if (overLeft > 0 && overRight <= 0) {
101            newOverRight =
102              position.left +
103              overLeft +
104              data.collisionWidth -
105              outerWidth -
106              withinOffset;
107            position.left += overLeft - newOverRight;
108
109            // Element is initially over right side of within
110          } else if (overRight > 0 && overLeft <= 0) {
111            position.left = withinOffset;
112
113            // Element is initially over both left and right sides of within
114          } else if (overLeft > overRight) {
115            position.left = withinOffset + outerWidth - data.collisionWidth;
116          } else {
117            position.left = withinOffset;
118          }
119
120          // Too far left -> align with left edge
121        } else if (overLeft > 0) {
122          position.left += overLeft;
123
124          // Too far right -> align with right edge
125        } else if (overRight > 0) {
126          position.left -= overRight;
127
128          // Adjust based on position and margin
129        } else {
130          position.left = max(position.left - collisionPosLeft, position.left);
131        }
132      },
133      top(position, data) {
134        const { within } = data;
135        const withinOffset = within.isWindow
136          ? within.scrollTop
137          : within.offset.top;
138        const outerHeight = data.within.height;
139        const collisionPosTop = position.top - data.collisionPosition.marginTop;
140        const overTop = withinOffset - collisionPosTop;
141        const overBottom =
142          collisionPosTop + data.collisionHeight - outerHeight - withinOffset;
143        let newOverBottom;
144
145        // Element is taller than within
146        if (data.collisionHeight > outerHeight) {
147          // Element is initially over the top of within
148          if (overTop > 0 && overBottom <= 0) {
149            newOverBottom =
150              position.top +
151              overTop +
152              data.collisionHeight -
153              outerHeight -
154              withinOffset;
155            position.top += overTop - newOverBottom;
156
157            // Element is initially over bottom of within
158          } else if (overBottom > 0 && overTop <= 0) {
159            position.top = withinOffset;
160
161            // Element is initially over both top and bottom of within
162          } else if (overTop > overBottom) {
163            position.top = withinOffset + outerHeight - data.collisionHeight;
164          } else {
165            position.top = withinOffset;
166          }
167
168          // Too far up -> align with top
169        } else if (overTop > 0) {
170          position.top += overTop;
171
172          // Too far down -> align with bottom edge
173        } else if (overBottom > 0) {
174          position.top -= overBottom;
175
176          // Adjust based on position and margin
177        } else {
178          position.top = max(position.top - collisionPosTop, position.top);
179        }
180      },
181    },
182    flip: {
183      left(position, data) {
184        const { within } = data;
185        const withinOffset = within.offset.left + within.scrollLeft;
186        const outerWidth = within.width;
187        const offsetLeft = within.isWindow
188          ? within.scrollLeft
189          : within.offset.left;
190        const collisionPosLeft =
191          position.left - data.collisionPosition.marginLeft;
192        const overLeft = collisionPosLeft - offsetLeft;
193        const overRight =
194          collisionPosLeft + data.collisionWidth - outerWidth - offsetLeft;
195        const myOffset =
196          // eslint-disable-next-line no-nested-ternary
197          data.my[0] === 'left'
198            ? -data.elemWidth
199            : data.my[0] === 'right'
200              ? data.elemWidth
201              : 0;
202        const atOffset =
203          // eslint-disable-next-line no-nested-ternary
204          data.at[0] === 'left'
205            ? data.targetWidth
206            : data.at[0] === 'right'
207              ? -data.targetWidth
208              : 0;
209        const offset = -2 * data.offset[0];
210        let newOverRight;
211        let newOverLeft;
212
213        if (overLeft < 0) {
214          newOverRight =
215            position.left +
216            myOffset +
217            atOffset +
218            offset +
219            data.collisionWidth -
220            outerWidth -
221            withinOffset;
222          if (newOverRight < 0 || newOverRight < abs(overLeft)) {
223            position.left += myOffset + atOffset + offset;
224          }
225        } else if (overRight > 0) {
226          newOverLeft =
227            position.left -
228            data.collisionPosition.marginLeft +
229            myOffset +
230            atOffset +
231            offset -
232            offsetLeft;
233          if (newOverLeft > 0 || abs(newOverLeft) < overRight) {
234            position.left += myOffset + atOffset + offset;
235          }
236        }
237      },
238      top(position, data) {
239        const { within } = data;
240        const withinOffset = within.offset.top + within.scrollTop;
241        const outerHeight = within.height;
242        const offsetTop = within.isWindow
243          ? within.scrollTop
244          : within.offset.top;
245        const collisionPosTop = position.top - data.collisionPosition.marginTop;
246        const overTop = collisionPosTop - offsetTop;
247        const overBottom =
248          collisionPosTop + data.collisionHeight - outerHeight - offsetTop;
249        const top = data.my[1] === 'top';
250        // eslint-disable-next-line no-nested-ternary
251        const myOffset = top
252          ? -data.elemHeight
253          : data.my[1] === 'bottom'
254            ? data.elemHeight
255            : 0;
256        const atOffset =
257          // eslint-disable-next-line no-nested-ternary
258          data.at[1] === 'top'
259            ? data.targetHeight
260            : data.at[1] === 'bottom'
261              ? -data.targetHeight
262              : 0;
263        const offset = -2 * data.offset[1];
264        let newOverTop;
265        let newOverBottom;
266        if (overTop < 0) {
267          newOverBottom =
268            position.top +
269            myOffset +
270            atOffset +
271            offset +
272            data.collisionHeight -
273            outerHeight -
274            withinOffset;
275          if (newOverBottom < 0 || newOverBottom < abs(overTop)) {
276            position.top += myOffset + atOffset + offset;
277          }
278        } else if (overBottom > 0) {
279          newOverTop =
280            position.top -
281            data.collisionPosition.marginTop +
282            myOffset +
283            atOffset +
284            offset -
285            offsetTop;
286          if (newOverTop > 0 || abs(newOverTop) < overBottom) {
287            position.top += myOffset + atOffset + offset;
288          }
289        }
290      },
291    },
292    flipfit: {
293      left(...args) {
294        collisions.flip.left.apply(this, args);
295        collisions.fit.left.apply(this, args);
296      },
297      top(...args) {
298        collisions.flip.top.apply(this, args);
299        collisions.fit.top.apply(this, args);
300      },
301    },
302  };
303
304  $.position = {
305    scrollbarWidth() {
306      if (cachedScrollbarWidth !== undefined) {
307        return cachedScrollbarWidth;
308      }
309      const div = $(
310        '<div ' +
311          "style='display:block;position:absolute;width:50px;height:50px;overflow:hidden;'>" +
312          "<div style='height:100px;width:auto;'></div></div>",
313      );
314      const innerDiv = div.children()[0];
315
316      $('body').append(div);
317      const w1 = innerDiv.offsetWidth;
318      div[0].style.overflow = 'scroll';
319
320      let w2 = innerDiv.offsetWidth;
321
322      if (w1 === w2) {
323        w2 = div[0].clientWidth;
324      }
325
326      div.remove();
327      cachedScrollbarWidth = w1 - w2;
328      return cachedScrollbarWidth;
329    },
330    getScrollInfo(within) {
331      const overflowX =
332        within.isWindow || within.isDocument
333          ? ''
334          : window.getComputedStyle(within.element[0])['overflow-x'];
335      const overflowY =
336        within.isWindow || within.isDocument
337          ? ''
338          : window.getComputedStyle(within.element[0])['overflow-y'];
339      const hasOverflowX =
340        overflowX === 'scroll' ||
341        (overflowX === 'auto' && within.width < within.element[0].scrollWidth);
342      const hasOverflowY =
343        overflowY === 'scroll' ||
344        (overflowY === 'auto' &&
345          within.height < within.element[0].scrollHeight);
346      return {
347        width: hasOverflowY ? $.position.scrollbarWidth() : 0,
348        height: hasOverflowX ? $.position.scrollbarWidth() : 0,
349      };
350    },
351    getWithinInfo(element) {
352      const withinElement = $(element || window);
353      const isWindow =
354        !!withinElement[0] && withinElement[0] === withinElement[0].window;
355      const isDocument = !!withinElement[0] && withinElement[0].nodeType === 9;
356      const hasOffset = !isWindow && !isDocument;
357      return {
358        element: withinElement,
359        isWindow,
360        isDocument,
361        offset: hasOffset ? $(element).offset() : { left: 0, top: 0 },
362        scrollLeft: withinElement.scrollLeft(),
363        scrollTop: withinElement.scrollTop(),
364        width: withinElement.outerWidth(),
365        height: withinElement.outerHeight(),
366      };
367    },
368  };
369
370  // eslint-disable-next-line func-names
371  /**
372   * Positions an element relative to another.
373   *
374   * The following documentation is originally from
375   * {@link https://api.jqueryui.com/position/}.
376   *
377   * @param {Object} options - the options object.
378   * @param {string} options.my - Defines which position on the element being
379   *   positioned to align with the target element: "horizontal vertical"
380   *   alignment. A single value such as "right" will be normalized to "right
381   *   center", "top" will be normalized to "center top" (following CSS
382   *   convention). Acceptable horizontal values: "left", "center", "right".
383   *   Acceptable vertical values: "top", "center", "bottom". Example: "left
384   *   top" or "center center". Each dimension can also contain offsets, in
385   *   pixels or percent, e.g., "right+10 top-25%". Percentage offsets are
386   *   relative to the element being positioned. Default value is "center".
387   * @param {string} options.at - Defines which position on the target element
388   *   to align the positioned element against: "horizontal vertical" alignment.
389   *   See the `my` option for full details on possible values. Percentage
390   *   offsets are relative to the target element. Default value is "center".
391   * @param {string|Element|jQuery|Event|null} options.of - Which element to
392   *   position against. If you provide a selector or jQuery object, the first
393   *   matching element will be used. If you provide an event object, the pageX
394   *   and pageY properties will be used. Example: "#top-menu". Default value is
395   *   null.
396   * @param {string} options.collision - When the positioned element overflows
397   *   the window in some direction, move it to an alternative position. Similar
398   *   to `my` and `at`, this accepts a single value or a pair for
399   *   horizontal/vertical, e.g., "flip", "fit", "fit flip", "fit none". Default
400   *   value is "flip". The options work as follows:
401   *   - "flip": Flips the element to the opposite side of the target and the
402   *     collision detection is run again to see if it will fit. Whichever side
403   *     allows more of the element to be visible will be used.
404   *   - "fit": Shift the element away from the edge of the window.
405   *   - "flipfit": First applies the flip logic, placing the element on
406   *     whichever side allows more of the element to be visible. Then the fit
407   *     logic is applied to ensure as much of the element is visible as
408   *     possible.
409   *     "none": Does not apply any collision detection.
410   * @param {function|null} options.using - When specified, the actual property
411   *   setting is delegated to this callback. Receives two parameters: The first
412   *   is a hash of top and left values for the position that should be set and
413   *   can be forwarded to .css() or .animate().The second provides feedback
414   *   about the position and dimensions of both elements, as well as
415   *   calculations to their relative position. Both target and element have
416   *   these properties: element, left, top, width, height. In addition, there's
417   *   horizontal, vertical and important, providing twelve potential directions
418   *   like { horizontal: "center", vertical: "left", important: "horizontal" }.
419   *   Default value is null.
420   * @param {string|Element|jQuery} options.within - Element to position within,
421   *   affecting collision detection. If you provide a selector or jQuery
422   *   object, the first matching element will be used. Default value is window.
423   *
424   * @return {jQuery}
425   *  The jQuery object that called called this function.
426   */
427  $.fn.position = function (options) {
428    if (!options || !options.of) {
429      // eslint-disable-next-line prefer-rest-params
430      return _position.apply(this, arguments);
431    }
432
433    // Make a copy, we don't want to modify arguments
434    options = $.extend({}, options);
435
436    const within = $.position.getWithinInfo(options.within);
437    const scrollInfo = $.position.getScrollInfo(within);
438    const collision = (options.collision || 'flip').split(' ');
439    const offsets = {};
440
441    // Make sure string options are treated a
441s CSS selectors
442    const target =
443      typeof options.of === 'string'
444        ? $(document).find(options.of)
445        : $(options.of);
446    const dimensions = getDimensions(target);
447    const targetWidth = dimensions.width;
448    const targetHeight = dimensions.height;
449    const targetOffset = dimensions.offset;
450
451    if (target[0].preventDefault) {
452      // Force left top to allow flipping
453      options.at = 'left top';
454    }
455
456    // Clone to reuse original targetOffset later
457    const basePosition = $.extend({}, targetOffset);
458
459    // Force my and at to have valid horizontal and vertical positions
460    // if a value is missing or invalid, it will be converted to center
461    // eslint-disable-next-line func-names
462    $.each(['my', 'at'], function () {
463      let pos = (options[this] || '').split(' ');
464
465      if (pos.length === 1) {
466        // eslint-disable-next-line no-nested-ternary
467        pos = regexHorizontal.test(pos[0])
468          ? pos.concat(['center'])
469          : regexVertical.test(pos[0])
470            ? ['center'].concat(pos)
471            : ['center', 'center'];
472      }
473      pos[0] = regexHorizontal.test(pos[0]) ? pos[0] : 'center';
474      pos[1] = regexVertical.test(pos[1]) ? pos[1] : 'center';
475
476      // Calculate offsets
477      const horizontalOffset = regexOffset.exec(pos[0]);
478      const verticalOffset = regexOffset.exec(pos[1]);
479      offsets[this] = [
480        horizontalOffset ? horizontalOffset[0] : 0,
481        verticalOffset ? verticalOffset[0] : 0,
482      ];
483
484      // Reduce to just the positions without the offsets
485      options[this] = [
486        regexPosition.exec(pos[0])[0],
487        regexPosition.exec(pos[1])[0],
488      ];
489    });
490
491    // Normalize collision option
492    if (collision.length === 1) {
493      // eslint-disable-next-line prefer-destructuring
494      collision[1] = collision[0];
495    }
496
497    if (options.at[0] === 'right') {
498      basePosition.left += targetWidth;
499    } else if (options.at[0] === 'center') {
500      basePosition.left += targetWidth / 2;
501    }
502
503    if (options.at[1] === 'bottom') {
504      basePosition.top += targetHeight;
505    } else if (options.at[1] === 'center') {
506      basePosition.top += targetHeight / 2;
507    }
508
509    const atOffset = getOffsets(offsets.at, targetWidth, targetHeight);
510    basePosition.left += atOffset[0];
511    basePosition.top += atOffset[1];
512
513    // eslint-disable-next-line func-names
514    return this.each(function () {
515      let using;
516      const elem = $(this);
517      const elemWidth = elem.outerWidth();
518      const elemHeight = elem.outerHeight();
519      const marginLeft = parseCss(this, 'marginLeft');
520      const marginTop = parseCss(this, 'marginTop');
521      const collisionWidth =
522        elemWidth +
523        marginLeft +
524        parseCss(this, 'marginRight') +
525        scrollInfo.width;
526      const collisionHeight =
527        elemHeight +
528        marginTop +
529        parseCss(this, 'marginBottom') +
530        scrollInfo.height;
531      const position = $.extend({}, basePosition);
532      const myOffset = getOffsets(
533        offsets.my,
534        elem.outerWidth(),
535        elem.outerHeight(),
536      );
537
538      if (options.my[0] === 'right') {
539        position.left -= elemWidth;
540      } else if (options.my[0] === 'center') {
541        position.left -= elemWidth / 2;
542      }
543
544      if (options.my[1] === 'bottom') {
545        position.top -= elemHeight;
546      } else if (options.my[1] === 'center') {
547        position.top -= elemHeight / 2;
548      }
549
550      position.left += myOffset[0];
551      position.top += myOffset[1];
552
553      const collisionPosition = {
554        marginLeft,
555        marginTop,
556      };
557
558      // eslint-disable-next-line func-names
559      $.each(['left', 'top'], function (i, dir) {
560        if (collisions[collision[i]]) {
561          collisions[collision[i]][dir](position, {
562            targetWidth,
563            targetHeight,
564            elemWidth,
565            elemHeight,
566            collisionPosition,
567            collisionWidth,
568            collisionHeight,
569            offset: [atOffset[0] + myOffset[0], atOffset[1] + myOffset[1]],
570            my: options.my,
571            at: options.at,
572            within,
573            elem,
574          });
575        }
576      });
577
578      if (options.using) {
579        // Adds feedback as second argument to using callback, if present
580        // eslint-disable-next-line func-names
581        using = function (props) {
582          const left = targetOffset.left - position.left;
583          const right = left + targetWidth - elemWidth;
584          const top = targetOffset.top - position.top;
585          const bottom = top + targetHeight - elemHeight;
586          const feedback = {
587            target: {
588              element: target,
589              left: targetOffset.left,
590              top: targetOffset.top,
591              width: targetWidth,
592              height: targetHeight,
593            },
594            element: {
595              element: elem,
596              left: position.left,
597              top: position.top,
598              width: elemWidth,
599              height: elemHeight,
600            },
601            // eslint-disable-next-line no-nested-ternary
602            horizontal: right < 0 ? 'left' : left > 0 ? 'right' : 'center',
603            // eslint-disable-next-line no-nested-ternary
604            vertical: bottom < 0 ? 'top' : top > 0 ? 'bottom' : 'middle',
605          };
606          if (targetWidth < elemWidth && abs(left + right) < targetWidth) {
607            feedback.horizontal = 'center';
608          }
609          if (targetHeight < elemHeight && abs(top + bottom) < targetHeight) {
610            feedback.vertical = 'middle';
611          }
612          if (max(abs(left), abs(right)) > max(abs(top), abs(bottom))) {
613            feedback.important = 'horizontal';
614          } else {
615            feedback.important = 'vertical';
616          }
617          options.using.call(this, props, feedback);
618        };
619      }
620
621      elem.offset($.extend(position, { using }));
622    });
623  };
624
625  // Although $.ui.position is not built to be called directly, some legacy code
626  // may have checks for the presence of $.ui.position, which can be used to
627  // confirm the presence of jQuery UI position's API, as opposed to the more
628  // limited version provided by jQuery.
629  if (!$.hasOwnProperty('ui')) {
630    $.ui = {};
631  }
632  $.ui.position = collisions;
633})(jQuery);
634
635/* Source and licensing information for the above line(s) can be found at https://natcouncil-website.glueuphosting.com/core/misc/position.js. */

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.