PageSourceSearch

https://www.trade.gov/modules/contrib/extlink/js/extlink.js?tlu5k9

js trade.gov collected 2026-09-24 06:44:12 UTC 28,820 bytes, 811 lines download raw bytes

1/**
2 * @file
3 * External links js file.
4 */
5
6((Drupal, drupalSettings) => {
7  Drupal.extlink = Drupal.extlink || {};
8
9  Drupal.extlink.attach = (context, drupalSettings) => {
10    if (
11      typeof drupalSettings.data === 'undefined' ||
12      !drupalSettings.data.hasOwnProperty('extlink')
13    ) {
14      return;
15    }
16
17    // Define the method (either 'append' or 'prepend') of placing the
18    // icon, defaults to 'append'.
19    let extIconPlacement = 'append';
20    if (
21      drupalSettings.data.extlink.extIconPlacement &&
22      drupalSettings.data.extlink.extIconPlacement !== '0'
23    ) {
24      extIconPlacement = drupalSettings.data.extlink.extIconPlacement;
25    }
26
27    // Strip the host name down, removing ports, subdomains, or www.
28    const pattern =
29      /^(([^:]+?\.)*)([^.:]+)((\.[a-z0-9]{1,253})*)(:[0-9]{1,5})?$/;
30    const host = window.location.host.replace(pattern, '$2$3$6');
31    const subdomain = window.location.host.replace(host, '');
32
33    // Determine what subdomains are considered internal.
34    let subdomains;
35    if (drupalSettings.data.extlink.extSubdomains) {
36      subdomains = '([^/]*\\.)?';
37    } else if (subdomain === 'www.' || subdomain === '') {
38      subdomains = '(www\\.)?';
39    } else {
40      subdomains = subdomain.replace('.', '\\.');
41    }
42
43    // Allowed domains.
44    let allowedDomains = false;
45    if (drupalSettings.data.extlink.allowedDomains) {
46      allowedDomains = [];
47      for (
48        let i = 0;
49        i < drupalSettings.data.extlink.allowedDomains.length;
50        i++
51      ) {
52        allowedDomains.push(
53          new RegExp(
54            `^https?:\\/\\/${drupalSettings.data.extlink.allowedDomains[i].replace(/(\r\n|\n|\r)/gm, '')}.*$`,
55            'i',
56          ),
57        );
58      }
59    }
60
61    // Build regular expressions that define an internal link.
62    const internalLink = new RegExp(
63      `^https?://([^@]*@)?${subdomains}${host}`,
64      'i',
65    );
66
67    // Extra internal link matching.
68    let extInclude = false;
69    if (drupalSettings.data.extlink.extInclude) {
70      extInclude = new RegExp(
71        drupalSettings.data.extlink.extInclude.replace(/\\/, '\\'),
72        'i',
73      );
74    }
75
76    // Extra external link matching.
77    let extExclude = false;
78    if (drupalSettings.data.extlink.extExclude) {
79      extExclude = new RegExp(
80        drupalSettings.data.extlink.extExclude.replace(/\\/, '\\'),
81        'i',
82      );
83    }
84
85    // Extra external link matching for excluding noreferrer.
86    let extExcludeNoreferrer = false;
87    if (drupalSettings.data.extlink.extExcludeNoreferrer) {
88      extExcludeNoreferrer = new RegExp(
89        drupalSettings.data.extlink.extExcludeNoreferrer.replace(/\\/, '\\'),
90        'i',
91      );
92    }
93
94    // Extra external link CSS selector exclusion.
95    let extCssExclude = false;
96    if (drupalSettings.data.extlink.extCssExclude) {
97      extCssExclude = drupalSettings.data.extlink.extCssExclude;
98    }
99
100    // Extra external link CSS selector inclusion.
101    let extCssInclude = false;
102    if (drupalSettings.data.extlink.extCssInclude) {
103      extCssInclude = drupalSettings.data.extlink.extCssInclude;
104    }
105
106    // Extra external link CSS selector explicit.
107    let extCssExplicit = false;
108    if (drupalSettings.data.extlink.extCssExplicit) {
109      extCssExplicit = drupalSettings.data.extlink.extCssExplicit;
110    }
111
112    // Find all links which are NOT internal and begin with http as opposed
113    // to ftp://, javascript:, etc. other kinds of links.
114    // When operating on the 'this' variable, the host has been appended to
115    // all links by the browser, even local ones.
116    const externalLinks = [];
117    const mailtoLinks = [];
118    const telLinks = [];
119    const extlinks = context.querySelectorAll(
120      'a:not([data-extlink]), area:not([data-extlink])',
121    );
122    extlinks.forEach((el) => {
123      try {
124        let url = '';
125        if (typeof el.href === 'string') {
126          url = el.href.toLowerCase();
127        }
128        // Handle SVG links (xlink:href).
129        else if (typeof el.href === 'object') {
130          url = el.href.baseVal;
131        }
132        const isExtCssIncluded =
133          extCssInclude &&
134          (el.matches(extCssInclude) || el.closest(extCssInclude));
135        if (
136          url.indexOf('http') === 0 &&
137          ((!internalLink.test(url) && !(extExclude && extExclude.test(url))) ||
138            (extInclude && extInclude.test(url)) ||
139            isExtCssIncluded) &&
140          !(extCssExclude && el.matches(extCssExclude)) &&
141          !(extCssExclude && el.closest(extCssExclude)) &&
142          !(extCssExplicit && !el.closest(extCssExplicit))
143        ) {
144          let match = false;
145          if (!isExtCssIncluded && allowedDomains) {
146            for (let i = 0; i < allowedDomains.length; i++) {
147              if (allowedDomains[i].test(url)) {
148                match = true;
149                break;
150              }
151            }
152          }
153          if (!match) {
154            externalLinks.push(el);
155          }
156        }
157        // Do not include area tags with begin with mailto: (this prohibits
158        // icons from being added to image-maps).
159        else if (
160          el.tagName !== 'AREA' &&
161          !(extCssExclude && el.closest(extCssExclude)) &&
162          !(extCssExplicit && !el.closest(extCssExplicit))
163        ) {
164          if (url.indexOf('mailto:') === 0) {
165            mailtoLinks.push(el);
166          } else if (url.indexOf('tel:') === 0) {
167            telLinks.push(el);
168          }
169        }
170      } catch (error) {
171        // IE7 throws errors often when dealing with irregular links, such as:
172        // <a href="node/10"></a> Empty tags.
173        // <a href="http://user:[email protected]">example</a> User:pass syntax.
174        return false;
175      }
176    });
177
178    const hasExtIcon =
179      drupalSettings.data.extlink.extClass !== '0' &&
180      drupalSettings.data.extlink.extClass !== '';
181    const hasAdditionalExtClasses =
182      drupalSettings.data.extlink.extAdditionalLinkClasses !== '';
183    Drupal.extlink.applyClassAndSpan(
184      externalLinks,
185      'ext',
186      hasExtIcon ? extIconPlacement : null,
187    );
188    if (hasAdditionalExtClasses) {
189      Drupal.extlink.applyClassAndSpan(
190        externalLinks,
191        drupalSettings.data.extlink.extAdditionalLinkClasses,
192        null,
193      );
194    }
195
196    const hasMailtoClass =
197      drupalSettings.data.extlink.mailtoClass !== '0' &&
198      drupalSettings.data.extlink.mailtoClass !== '';
199    const hasAdditionalMailtoClasses =
200      drupalSettings.data.extlink.extAdditionalMailtoClasses !== '';
201    if (hasMailtoClass) {
202      Drupal.extlink.applyClassAndSpan(
203        mailtoLinks,
204        drupalSettings.data.extlink.mailtoClass,
205        extIconPlacement,
206      );
207    }
208    if (hasAdditionalMailtoClasses) {
209      Drupal.extlink.applyClassAndSpan(
210        mailtoLinks,
211        drupalSettings.data.extlink.extAdditionalMailtoClasses,
212        null,
213      );
214    }
215
216    const hasTelClass =
217      drupalSettings.data.extlink.telClass !== '0' &&
218      drupalSettings.data.extlink.telClass !== '';
219    const hasAdditionalTelClasses =
220      drupalSettings.data.extlink.extAdditionalTelClasses !== '0' &&
221      drupalSettings.data.extlink.extAdditionalTelClasses !== '';
222    if (hasTelClass) {
223      Drupal.extlink.applyClassAndSpan(
224        telLinks,
225        drupalSettings.data.extlink.telClass,
226        extIconPlacement,
227      );
228    }
229    if (hasAdditionalTelClasses) {
230      Drupal.extlink.applyClassAndSpan(
231        mailtoLinks,
232        drupalSettings.data.extlink.extAdditionalTelClasses,
233        null,
234      );
235    }
236
237    if (drupalSettings.data.extlink.extTarget) {
238      // Add target attr to open link in a new tab if not set.
239      externalLinks.forEach((link, i) => {
240        if (
241          !(
242            drupalSettings.data.extlink.extTargetNoOverride &&
243            link.matches('a[target]')
244          )
245        ) {
246          externalLinks[i].setAttribute('target', '_blank');
247        }
248      });
249
250      // Add noopener rel attribute to combat phishing.
251      externalLinks.forEach((link, i) => {
252        const val = link.getAttribute('rel');
253        // If no rel attribute is present, create one with the value noopener.
254        if (val === null || typeof val === 'undefined') {
255          externalLinks[i].setAttribute('rel', 'noopener');
256          return;
257        }
258        // Check to see if rel contains noopener. Add what doesn't exist.
259        if (val.indexOf('noopener') > -1) {
260          if (val.indexOf('noopener') === -1) {
261            // Add noopener.
262            externalLinks[i].setAttribute('rel', `${val} noopener`);
263          } else {
264            // Noopener exists. Nothing needs to be added.
265          }
266        }
267        // Else, append noopener to val.
268        else {
269          // Add noopener.
270          externalLinks[i].setAttribute('rel', `${val} noopener`);
271        }
272      });
273    }
274
275    if (drupalSettings.data.extlink.extNofollow) {
276      externalLinks.forEach((link, i) => {
277        const val = link.getAttribute('rel');
278        // When the link does not have a rel attribute set it to 'nofollow'.
279        if (val === null || typeof val === 'undefined') {
280          externalLinks[i].setAttribute('rel', 'nofollow');
281          return;
282        }
283        let target = 'nofollow';
284        // Change the target, if not overriding follow.
285        if (drupalSettings.data.extlink.extFollowNoOverride) {
286          target = 'follow';
287        }
288        if (val.indexOf(target) === -1) {
289          // Add nofollow.
290          externalLinks[i].setAttribute('rel', `${val} nofollow`);
291        }
292      });
293    }
294
295    if (drupalSettings.data.extlink.extTitleNoOverride === false) {
296      // Set the title attribute of all external links that opens in a new window.
297      externalLinks.forEach((link, i) => {
298        const oldTitle = link.getAttribute('title');
299        let newTitle = '';
300
301        if (
302          drupalSettings.data.extlink.extTarget &&
303          drupalSettings.data.extlink.extTargetAppendNewWindowDisplay
304        ) {
305          newTitle = drupalSettings.data.extlink.extTargetAppendNewWindowLabel;
306        }
307        // Determine new title based on drupalSettings extTarget configuration.
308        if (oldTitle !== null) {
309          if (Drupal.extlink.hasNewWindowText(oldTitle)) {
310            return;
311          }
312          newTitle = Drupal.extlink.combineLabels(oldTitle, newTitle);
313        }
314        if (newTitle) {
315          externalLinks[i].setAttribute('title', newTitle);
316        }
317      });
318    }
319
320    if (drupalSettings.data.extlink.extNoreferrer) {
321      externalLinks.forEach((link, i) => {
322        // Don't add 'noreferrer' if exclude noreferrer option is set and the
323        // link matches the entered pattern to exclude 'noreferrer' tag.
324        if (
325          drupalSettings.data.extlink.extExcludeNoreferrer &&
326          extExcludeNoreferrer.test(link.getAttribute('href'))
327        ) {
328          return;
329        }
330
331        const val = link.getAttribute('rel');
332        // When the link does not have a rel attribute set it to 'noreferrer'.
333        if (val === null || typeof val === 'undefined') {
334          externalLinks[i].setAttribute('rel', 'noreferrer');
335          return;
336        }
337        // Add noreferrer.
338        externalLinks[i].setAttribute('rel', `${val} noreferrer`);
339      });
340    }
341
342    /* eslint:disable:no-empty */
343    Drupal.extlink = Drupal.extlink || {};
344
345    // Set up default click function for the external links popup. This should be
346    // overridden by modules wanting to alter the popup.
347    Drupal.extlink.popupClickHandler =
348      Drupal.extlink.popupClickHandler ||
349      (() => {
350        if (drupalSettings.data.extlink.extAlert) {
351          // eslint-disable-next-line no-restricted-globals
352          return confirm(drupalSettings.data.extlink.extAlertText);
353        }
354      });
355
356    const _that = this;
357    Drupal.extlink.handleClick = function (event) {
358      const shouldNavigate = Drupal.extlink.popupClickHandler.call(
359        _that,
360        event,
361      );
362      if (typeof shouldNavigate !== 'undefined' && !shouldNavigate) {
363        // Prevent navigation if the user clicks "Cancel".
364        event.preventDefault();
365      }
366    };
367
368    externalLinks.forEach((val, i) => {
369      externalLinks[i].removeEventListener('click', Drupal.extlink.handleClick);
370      externalLinks[i].addEventListener('click', Drupal.extlink.handleClick);
371    });
372  };
373
374  /**
375   * Check if the label already has 'new window' text.
376   *
377   *
378   * @returns boolean
379   * @param label
380   */
381  Drupal.extlink.hasNewWindowText = function (label) {
382    return label.toLowerCase().indexOf(Drupal.t('new window')) !== -1;
383  };
384
385  /**
386   * Check if element is mostly text like element.
387   *
388   * We consider element to be text like if it starts with text, ends with text
389   * and in between can only have simple html elements - elements that do not
390   * have inner elements.
391   *
392   * @param element
393   *   HTML element.
394   * @returns {boolean}
395   *   true if this element is mostly text, false other way.
396   */
397  Drupal.extlink.isTextLikeElement = function (element) {
398    let startsWithText = false;
399    let endsWithText = false;
400
401    for (let i = 0; i < element.childNodes.length; i++) {
402      const child = element.childNodes[i];
403
404      if (child.nodeType === Node.TEXT_NODE) {
405        const trimmedText = child.textContent.trim();
406        if (trimmedText.length > 0) {
407          if (!startsWithText) {
408            startsWithText = true;
409          }
410          // Any text node after an element is an ending text.
411          endsWithText = true;
412        }
413      } else if (child.nodeType === Node.ELEMENT_NODE) {
414        endsWithText = false;
415
416        // If the element has any children, it’s not considered a text like
417        // link.
418        if (child.children.length > 0) {
419          return false;
420        }
421      }
422    }
423
424    return startsWithText && endsWithText;
425  };
426
427  /**
428   * Combine two labels.
429   *
430   * Combine labels in a readable manner, taking into account if the label
431   * uses parenthesis or not. For examples,
432   *
433   *   1. "A" + "B" => "A, B"
434   *   2. "A" + "(B)" => "A (B)"
435   *   3. "(A)" + "B" => "B (A)"
436   *   4. "(A)" + "(B)" => "(A, B)"
437   *
438   *
439   * @returns string
440   * @param labelA
441   * @param labelB
442   */
443  Drupal.extlink.combineLabels = function (labelA, labelB) {
444    labelA = labelA || '';
445    labelB = labelB || '';
446
447    // If either label is empty, return the other without adding a comma
448    if (!labelA.trim()) return labelB;
449    if (!labelB.trim()) return labelA;
450
451    const labelANoParens = labelA.trim().replace('(', '').replace(')', '');
452    const labelBNoParens = labelB.trim().replace('(', '').replace(')', '');
453    if (labelA === labelANoParens) {
454      if (labelB === labelBNoParens) {
455        // This is Example 1 above: "A" + "B" => "A, B"
456        return `${labelA}, ${labelB}`;
457      }
458
459      // This is Example 2 above: "A" + "(B)" => "A (B)"
460      return `${labelA} ${labelB}`;
461    }
462
463    if (labelB === labelBNoParens) {
464      // This is Example 3 above: "(A)" + "B" => "B (A)"
465      return `${labelB} ${labelA}`;
466    }
467
468    // This is Example 4 above: "(A)" + "(B)" => "(A, B)"
469    return `(${labelANoParens}, ${labelBNoParens})`;
470  };
471
472  /**
473   * Apply a class and a trailing <span> to all links not containing images.
474   *
475   * @param {object[]} links
476   *   An array of DOM elements representing the links.
477   * @param {string} className
478   *   The class to apply to the links.
479   * @param {string} iconPlacement
480   *   'append' or 'prepend' the icon to the link.
481   */
482  Drupal.extlink.applyClassAndSpan = (links, className, iconPlacement) => {
483    let linksToProcess;
484    if (drupalSettings.data.extlink.extImgClass) {
485      linksToProcess = links;
486    } else {
487      // Only text links.
488      linksToProcess = links.filter((link) => {
489        return link.querySelector('img, svg') === null;
490      });
491    }
492
493    for (let i = 0; i < linksToProcess.length; i++) {
494      if (className !== '0') {
495        linksToProcess[i].classList.add(className);
496      }
497
498      // Additional classes:
499      if (
500        className === drupalSettings.data.extlink.mailtoClass &&
501        drupalSettings.data.extlink.extAdditionalMailtoClasses
502      ) {
503        // Is mail link:
504        linksToProcess[i].classList.add(
505          drupalSettings.data.extlink.extAdditionalMailtoClasses,
506        );
507      } else if (
508        className === drupalSettings.data.extlink.telClass &&
509        drupalSettings.data.extlink.extAdditionalTelClasses
510      ) {
511        // Is regular external link:
512        linksToProcess[i].classList.add(
513          drupalSettings.data.extlink.extAdditionalTelClasses,
514        );
515      } else if (drupalSettings.data.extlink.extAdditionalLinkClasses) {
516        // Is regular external link:
517        linksToProcess[i].classList.add(
518          drupalSettings.data.extlink.extAdditionalLinkClasses,
519        );
520      }
521
522      // Add data-extlink attribute.
523      linksToProcess[i].setAttribute('data-extlink', '');
524
525      // Add icon.
526      if (iconPlacement) {
527        let link = linksToProcess[i];
528
529        // Prevent appended icons from wrapping lines.
530        if (
531          drupalSettings.data.extlink.extPreventOrphan &&
532          iconPlacement === 'append'
533        ) {
534          let preventOrphan = true;
535
536          if (
537            drupalSettings.data.extlink.extPreventOrphanTextLike &&
538            !Drupal.extlink.isTextLikeElement(link)
539          ) {
540            preventOrphan = false;
541          }
542
543          if (preventOrphan) {
544            // Find the last word in the link.
545            let lastTextNode = link.lastChild;
546            let trailingWhitespace = null;
547            let parentNode = link;
548            while (lastTextNode) {
549              if (lastTextNode.lastChild) {
550                // Last node was not text; step down into child node.
551                parentNode = lastTextNode;
552                lastTextNode = lastTextNode.lastChild;
553              } else if (
554                lastTextNode.nodeName === '#text' &&
555                parentNode.lastElementChild &&
556                lastTextNode.textContent.trim().length === 0
557              ) {
558                // Last node was text, but it was whitespace. Step back into previous node.
559                trailingWhitespace = lastTextNode;
560                parentNode = parentNode.lastElementChild;
561                lastTextNode = parentNode.lastChild;
562              } else {
563                // Last node was null or valid text.
564                break;
565              }
566            }
567            if (
568              lastTextNode &&
569              lastTextNode.nodeName === '#text' &&
570              lastTextNode.textContent.length > 0
571            ) {
572              const lastText = lastTextNode.textContent;
573              const lastWordRegex = new RegExp(/\S+\s*$/, 'g');
574              const lastWord = lastText.match(lastWordRegex);
575              if (lastWord !== null) {
576                // Wrap the last word in a span.
577                const breakPreventer = document.createElement('span');
578                breakPreventer.classList.add('extlink-nobreak');
579                breakPreventer.textContent = lastWord[0];
580                if (trailingWhitespace) {
581                  trailingWhitespace.textContent = '';
582                  breakPreventer.append(trailingWhitespace.textContent);
583                }
584                lastTextNode.textContent = lastText.substring(
585                  0,
586                  lastText.length - lastWord[0].length,
587                );
588                lastTextNode.parentNode.append(breakPreventer);
589                // Insert the icon into the span rather than the link.
590                link = breakPreventer;
591              }
592            }
593          }
594        }
595
596        // Create an icon element.
597        let iconElement;
598        if (drupalSettings.data.extlink.extUseFontAwesome) {
599          iconElement = document.createElement('span');
600          iconElement.setAttribute('class', `fa-${className} extlink`);
601          if (className === drupalSettings.data.extlink.mailtoClass) {
602            if (drupalSettings.data.extlink.mailtoLabel) {
603              link.ariaLabel = drupalSettings.data.extlink.mailtoLabel;
604            }
605            iconElement.innerHTML = Drupal.theme(
606              'extlink_fa_mailto',
607              drupalSettings,
608              iconPlacement,
609            );
610          } else if (className === drupalSettings.data.extlink.extClass) {
611            if (drupalSettings.data.extlink.extLabel) {
612              link.ariaLabel = drupalSettings.data.extlink.extLabel;
613            }
614            iconElement.innerHTML = Drupal.theme(
615              'extlink_fa_extlink',
616              drupalSettings,
617              iconPlacement,
618            );
619          } else if (className === drupalSettings.data.extlink.telClass) {
620            if (drupalSettings.data.extlink.telLabel) {
621              link.ariaLabel = drupalSettings.data.extlink.telLabel;
622            }
623            iconElement.innerHTML = Drupal.theme(
624              'extlink_fa_tel',
625              drupalSettings,
626              iconPlacement,
627            );
628          }
629        } else if (drupalSettings.data.extlink.extUseIcon) {
630          iconElement = document.createElement('span');
631          iconElement.setAttribute('class', `extlink`);
632          iconElement.setAttribute('data-extlink-placement', iconPlacement);
633          if (className === drupalSettings.data.extlink.mailtoClass) {
634            if (drupalSettings.data.extlink.mailtoLabel) {
635              link.ariaLabel = drupalSettings.data.extlink.mailtoLabel;
636            }
637            iconElement.innerHTML = drupalSettings.data.extlink.extMailIcon;
638            iconElement.setAttribute('data-extlink-icon-type', 'mailto');
639          } else if (className === drupalSettings.data.extlink.extClass) {
640            if (drupalSettings.data.extlink.extLabel) {
641              link.ariaLabel = drupalSettings.data.extlink.extLabel;
642            }
643            iconElement.innerHTML = drupalSettings.data.extlink.extLinkIcon;
644            iconElement.setAttribute('data-extlink-icon-type', 'link');
645          } else if (className === drupalSettings.data.extlink.telClass) {
646            if (drupalSettings.data.extlink.telLabel) {
647              link.ariaLabel = drupalSettings.data.extlink.telLabel;
648            }
649            iconElement.innerHTML = drupalSettings.data.extlink.extTelIcon;
650            iconElement.setAttribute('data-extlink-icon-type', 'tel');
651          }
652        } else {
653          iconElement = document.createElementNS(
654            'http://www.w3.org/2000/svg',
655            'svg',
656          );
657          iconElement.setAttribute('focusable', 'false');
658          iconElement.setAttribute('width', '1em');
659          iconElement.setAttribute('height', '1em');
660          iconElement.classList.add(className);
661          iconElement.setAttribute('data-extlink-placement', iconPlacement);
662          if (className === drupalSettings.data.extlink.mailtoClass) {
663            iconElement = Drupal.theme(
664              'extlink_mailto',
665              iconElement,
666              drupalSettings,
667            );
668          } else if (className === drupalSettings.data.extlink.extClass) {
669            iconElement = Drupal.theme(
670              'extlink_extlink',
671              iconElement,
672              drupalSettings,
673            );
674          } else if (className === drupalSettings.data.extlink.telClass) {
675            iconElement = Drupal.theme(
676              'extlink_tel',
677              iconElement,
678              drupalSettings,
679            );
680          }
681        }
682        iconElement.setAttribute('role', 'img');
683        iconElement.setAttribute(
684          'aria-hidden',
685          drupalSettings.data.extlink.extHideIcons,
686        );
687        link[iconPlacement](iconElement);
688      }
689    }
690  };
691
692  /**
693   * Theme function for a Font Awesome mailto icon.
694   *
695   * @param {object} drupalSettings
696   *   Settings object used to construct the markup.
697   * @param {string} iconPlacement
698   *   The class to apply to a link.
699   *
700   * @return {string}
701   *   HTML string of the Font AweSome mailto link icon.
702   */
703  Drupal.theme.extlink_fa_mailto = function (drupalSettings, iconPlacement) {
704    return `<span class="${drupalSettings.data.extlink.extFaMailtoClasse
704s}" data-extlink-placement="${iconPlacement}"></span>`;
705  };
706
707  /**
708   * Theme function for a Font Awesome external link icon.
709   *
710   * @param {object} drupalSettings
711   *   Settings object used to construct the markup.
712   * @param {string} iconPlacement
713   *   The class to apply to a link.
714   *
715   * @return {string}
716   *   HTML string of the Font AweSome external link icon.
717   */
718  Drupal.theme.extlink_fa_extlink = function (drupalSettings, iconPlacement) {
719    return `<span class="${drupalSettings.data.extlink.extFaLinkClasses}" data-extlink-placement="${iconPlacement}"></span>`;
720  };
721
722  /**
723   * Theme function for a Font Awesome telephone icon.
724   *
725   * @param {object} drupalSettings
726   *   Settings object used to construct the markup.
727   * @param {string} iconPlacement
728   *   The class to apply to a link.
729   *
730   * @return {string}
731   *   HTML string of the Font AweSome telephone icon.
732   */
733  Drupal.theme.extlink_fa_tel = function (drupalSettings, iconPlacement) {
734    return `<span class="${drupalSettings.data.extlink.extFaTelClasses}" data-extlink-placement="${iconPlacement}"></span>`;
735  };
736
737  /**
738   * Theme function for a mailto icon.
739   *
740   * @param {object} iconElement
741   *   The current iconElement being altered.
742   * @param {object} drupalSettings
743   *   Settings object used to construct the markup.
744   *
745   * @return {object}
746   *   The altered iconElement.
747   */
748  Drupal.theme.extlink_mailto = function (iconElement, drupalSettings) {
749    iconElement.setAttribute(
750      'aria-label',
751      drupalSettings.data.extlink.mailtoLabel,
752    );
753    iconElement.setAttribute('viewBox', '0 10 70 20');
754    iconElement.innerHTML = `<title>${drupalSettings.data.extlink.mailtoLabel}</title><path d="M56 14H8c-1.1 0-2 0.9-2 2v32c0 1.1 0.9 2 2 2h48c1.1 0 2-0.9 2-2V16C58 14.9 57.1 14 56 14zM50.5 18L32 33.4 13.5 18H50.5zM10 46V20.3l20.7 17.3C31.1 37.8 31.5 38 32 38s0.9-0.2 1.3-0.5L54 20.3V46H10z"/>`;
755    return iconElement;
756  };
757
758  /**
759   * Theme function for an external link icon.
760   *
761   * @param {object} iconElement
762   *   The current iconElement being altered.
763   * @param {object} drupalSettings
764   *   Settings object used to construct the markup.
765   *
766   * @return {object}
767   *   The altered iconElement.
768   */
769  Drupal.theme.extlink_extlink = function (iconElement, drupalSettings) {
770    iconElement.setAttribute(
771      'aria-label',
772      drupalSettings.data.extlink.extLabel,
773    );
774    iconElement.setAttribute('viewBox', '0 0 80 40');
775    iconElement.innerHTML = `<title>${drupalSettings.data.extlink.extLabel}</title><path d="M48 26c-1.1 0-2 0.9-2 2v26H10V18h26c1.1 0 2-0.9 2-2s-0.9-2-2-2H8c-1.1 0-2 0.9-2 2v40c0 1.1 0.9 2 2 2h40c1.1 0 2-0.9 2-2V28C50 26.9 49.1 26 48 26z"/><path d="M56 6H44c-1.1 0-2 0.9-2 2s0.9 2 2 2h7.2L30.6 30.6c-0.8 0.8-0.8 2 0 2.8C31 33.8 31.5 34 32 34s1-0.2 1.4-0.6L54 12.8V20c0 1.1 0.9 2 2 2s2-0.9 2-2V8C58 6.9 57.1 6 56 6z"/>`;
776    return iconElement;
777  };
778
779  /**
780   * Theme function for an external telephone icon.
781   *
782   * @param {object} iconElement
783   *   The current iconElement being altered.
784   * @param {object} drupalSettings
785   *   Settings object used to construct the markup.
786   *
787   * @return {object}
788   *   The altered iconElement.
789   */
790  Drupal.theme.extlink_tel = function (iconElement, drupalSettings) {
791    iconElement.setAttribute(
792      'aria-label',
793      drupalSettings.data.extlink.telLabel,
794    );
795    iconElement.setAttribute('viewBox', '0 0 181.352 181.352');
796    iconElement.innerHTML = `<title>${drupalSettings.data.extlink.telLabel}</title><path xmlns="http://www.w3.org/2000/svg" d="M169.393,167.37l-14.919,9.848c-9.604,6.614-50.531,14.049-106.211-53.404C-5.415,58.873,9.934,22.86,17.134,14.555L29.523,1.678c2.921-2.491,7.328-2.198,9.839,0.811l32.583,38.543l0.02,0.02c2.384,2.824,2.306,7.22-0.83,9.868v0.029l-14.44,10.415c-5.716,5.667-0.733,14.587,5.11,23.204l27.786,32.808c12.926,12.477,20.009,18.241,26.194,14.118l12.008-13.395c2.941-2.472,7.328-2.169,9.839,0.821l32.603,38.543v0.02C172.607,160.316,172.519,164.703,169.393,167.37z"/>`;
797    return iconElement;
798  };
799
800  Drupal.behaviors.extlink = Drupal.behaviors.extlink || {};
801  Drupal.behaviors.extlink.attach = (context, drupalSettings) => {
802    // Backwards compatibility, for the benefit of modules overriding extlink
803    // functionality by defining an "extlinkAttach" global function.
804    if (typeof extlinkAttach === 'function') {
805      // eslint-disable-next-line no-undef
806      extlinkAttach(context);
807    } else {
808      Drupal.extlink.attach(context, drupalSettings);
809    }
810  };
811})(Drupal, drupalSettings);

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.