PageSourceSearch

https://www.blm.gov/modules/contrib/extlink/js/extlink.js?tlu9fw

js blm.gov collected 2026-09-24 06:23:29 UTC 24,325 bytes, 584 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 (typeof drupalSettings.data === 'undefined' || !drupalSettings.data.hasOwnProperty('extlink')) {
11      return;
12    }
13
14    // Define the method (either 'append' or 'prepend') of placing the
15    // icon, defaults to 'append'.
16    let extIconPlacement = 'append';
17    if (drupalSettings.data.extlink.extIconPlacement && drupalSettings.data.extlink.extIconPlacement !== '0') {
18      extIconPlacement = drupalSettings.data.extlink.extIconPlacement;
19    }
20
21    // Strip the host name down, removing ports, subdomains, or www.
22    const pattern = /^(([^:]+?\.)*)([^.:]+)((\.[a-z0-9]{1,253})*)(:[0-9]{1,5})?$/;
23    const host = window.location.host.replace(pattern, '$2$3$6');
24    const subdomain = window.location.host.replace(host, '');
25
26    // Determine what subdomains are considered internal.
27    let subdomains;
28    if (drupalSettings.data.extlink.extSubdomains) {
29      subdomains = '([^/]*\\.)?';
30    } else if (subdomain === 'www.' || subdomain === '') {
31      subdomains = '(www\\.)?';
32    } else {
33      subdomains = subdomain.replace('.', '\\.');
34    }
35
36    // Allowed domains.
37    let allowedDomains = false;
38    if (drupalSettings.data.extlink.allowedDomains) {
39      allowedDomains = [];
40      for (let i = 0; i < drupalSettings.data.extlink.allowedDomains.length; i++) {
41        allowedDomains.push(new RegExp(`^https?:\\/\\/${drupalSettings.data.extlink.allowedDomains[i].replace(/(\r\n|\n|\r)/gm, '')}.*$`, 'i'));
42      }
43    }
44
45    // Build regular expressions that define an internal link.
46    const internalLink = new RegExp(`^https?://([^@]*@)?${subdomains}${host}`, 'i');
47
48    // Extra internal link matching.
49    let extInclude = false;
50    if (drupalSettings.data.extlink.extInclude) {
51      extInclude = new RegExp(drupalSettings.data.extlink.extInclude.replace(/\\/, '\\'), 'i');
52    }
53
54    // Extra external link matching.
55    let extExclude = false;
56    if (drupalSettings.data.extlink.extExclude) {
57      extExclude = new RegExp(drupalSettings.data.extlink.extExclude.replace(/\\/, '\\'), 'i');
58    }
59
60    // Extra external link matching for excluding noreferrer.
61    let extExcludeNoreferrer = false;
62    if (drupalSettings.data.extlink.extExcludeNoreferrer) {
63      extExcludeNoreferrer = new RegExp(drupalSettings.data.extlink.extExcludeNoreferrer.replace(/\\/, '\\'), 'i');
64    }
65
66    // Extra external link CSS selector exclusion.
67    let extCssExclude = false;
68    if (drupalSettings.data.extlink.extCssExclude) {
69      extCssExclude = drupalSettings.data.extlink.extCssExclude;
70    }
71
72    // Extra external link CSS selector inclusion.
73    let extCssInclude = false;
74    if (drupalSettings.data.extlink.extCssInclude) {
75      extCssInclude = drupalSettings.data.extlink.extCssInclude;
76    }
77
78    // Extra external link CSS selector explicit.
79    let extCssExplicit = false;
80    if (drupalSettings.data.extlink.extCssExplicit) {
81      extCssExplicit = drupalSettings.data.extlink.extCssExplicit;
82    }
83
84    // Find all links which are NOT internal and begin with http as opposed
85    // to ftp://, javascript:, etc. other kinds of links.
86    // When operating on the 'this' variable, the host has been appended to
87    // all links by the browser, even local ones.
88    const externalLinks = [];
89    const mailtoLinks = [];
90    const telLinks = [];
91    const extlinks = context.querySelectorAll('a:not([data-extlink]), area:not([data-extlink])');
92    extlinks.forEach((el) => {
93      try {
94        let url = '';
95        if (typeof el.href === 'string') {
96          url = el.href.toLowerCase();
97        }
98        // Handle SVG links (xlink:href).
99        else if (typeof el.href === 'object') {
100          url = el.href.baseVal;
101        }
102        const isExtCssIncluded = extCssInclude && (el.matches(extCssInclude) || el.closest(extCssInclude));
103        if (
104          url.indexOf('http') === 0 &&
105          ((!internalLink.test(url) && !(extExclude && extExclude.test(url))) || (extInclude && extInclude.test(url)) || isExtCssIncluded) &&
106          !(extCssExclude && el.matches(extCssExclude)) &&
107          !(extCssExclude && el.closest(extCssExclude)) &&
108          !(extCssExplicit && !el.closest(extCssExplicit))
109        ) {
110          let match = false;
111          if (!isExtCssIncluded && allowedDomains) {
112            for (let i = 0; i < allowedDomains.length; i++) {
113              if (allowedDomains[i].test(url)) {
114                match = true;
115                break;
116              }
117            }
118          }
119          if (!match) {
120            externalLinks.push(el);
121          }
122        }
123        // Do not include area tags with begin with mailto: (this prohibits
124        // icons from being added to image-maps).
125        else if (el.tagName !== 'AREA' && !(extCssExclude && el.closest(extCssExclude)) && !(extCssExplicit && !el.closest(extCssExplicit))) {
126          if (url.indexOf('mailto:') === 0) {
127            mailtoLinks.push(el);
128          } else if (url.indexOf('tel:') === 0) {
129            telLinks.push(el);
130          }
131        }
132      } catch (error) {
133        // IE7 throws errors often when dealing with irregular links, such as:
134        // <a href="node/10"></a> Empty tags.
135        // <a href="http://user:[email protected]">example</a> User:pass syntax.
136        return false;
137      }
138    });
139
140    const hasExtIcon = drupalSettings.data.extlink.extClass !== '0' && drupalSettings.data.extlink.extClass !== '';
141    const hasAdditionalExtClasses = drupalSettings.data.extlink.extAdditionalLinkClasses !== '';
142    Drupal.extlink.applyClassAndSpan(externalLinks, 'ext', hasExtIcon ? extIconPlacement : null);
143    if (hasAdditionalExtClasses) {
144      Drupal.extlink.applyClassAndSpan(externalLinks, drupalSettings.data.extlink.extAdditionalLinkClasses, null);
145    }
146
147    const hasMailtoClass = drupalSettings.data.extlink.mailtoClass !== '0' && drupalSettings.data.extlink.mailtoClass !== '';
148    const hasAdditionalMailtoClasses = drupalSettings.data.extlink.extAdditionalMailtoClasses !== '';
149    if (hasMailtoClass) {
150      Drupal.extlink.applyClassAndSpan(mailtoLinks, drupalSettings.data.extlink.mailtoClass, extIconPlacement);
151    }
152    if (hasAdditionalMailtoClasses) {
153      Drupal.extlink.applyClassAndSpan(mailtoLinks, drupalSettings.data.extlink.extAdditionalMailtoClasses, null);
154    }
155
156    const hasTelClass = drupalSettings.data.extlink.telClass !== '0' && drupalSettings.data.extlink.telClass !== '';
157    const hasAdditionalTelClasses = drupalSettings.data.extlink.extAdditionalTelClasses !== '0' && drupalSettings.data.extlink.extAdditionalTelClasses !== '';
158    if (hasTelClass) {
159      Drupal.extlink.applyClassAndSpan(telLinks, drupalSettings.data.extlink.telClass, extIconPlacement);
160    }
161    if (hasAdditionalTelClasses) {
162      Drupal.extlink.applyClassAndSpan(mailtoLinks, drupalSettings.data.extlink.extAdditionalTelClasses, null);
163    }
164
165    if (drupalSettings.data.extlink.extTarget) {
166      // Add target attr to open link in a new tab if not set.
167      externalLinks.forEach((link, i) => {
168        if (!(drupalSettings.data.extlink.extTargetNoOverride && link.matches('a[target]'))) {
169          externalLinks[i].setAttribute('target', '_blank');
170        }
171      });
172
173      // Add noopener rel attribute to combat phishing.
174      externalLinks.forEach((link, i) => {
175        const val = link.getAttribute('rel');
176        // If no rel attribute is present, create one with the value noopener.
177        if (val === null || typeof val === 'undefined') {
178          externalLinks[i].setAttribute('rel', 'noopener');
179          return;
180        }
181        // Check to see if rel contains noopener. Add what doesn't exist.
182        if (val.indexOf('noopener') > -1) {
183          if (val.indexOf('noopener') === -1) {
184            // Add noopener.
185            externalLinks[i].setAttribute('rel', `${val} noopener`);
186          } else {
187            // Noopener exists. Nothing needs to be added.
188          }
189        }
190        // Else, append noopener to val.
191        else {
192          // Add noopener.
193          externalLinks[i].setAttribute('rel', `${val} noopener`);
194        }
195      });
196    }
197
198    if (drupalSettings.data.extlink.extNofollow) {
199      externalLinks.forEach((link, i) => {
200        const val = link.getAttribute('rel');
201        // When the link does not have a rel attribute set it to 'nofollow'.
202        if (val === null || typeof val === 'undefined') {
203          externalLinks[i].setAttribute('rel', 'nofollow');
204          return;
205        }
206        let target = 'nofollow';
207        // Change the target, if not overriding follow.
208        if (drupalSettings.data.extlink.extFollowNoOverride) {
209          target = 'follow';
210        }
211        if (val.indexOf(target) === -1) {
212          // Add nofollow.
213          externalLinks[i].setAttribute('rel', `${val} nofollow`);
214        }
215      });
216    }
217
218    if (drupalSettings.data.extlink.extTitleNoOverride === false) {
219      // Set the title attribute of all external links that opens in a new window.
220      externalLinks.forEach((link, i) => {
221        const oldTitle = link.getAttribute('title');
222        let newTitle = '';
223
224        if (drupalSettings.data.extlink.extTargetAppendNewWindowDisplay) {
225          newTitle = drupalSettings.data.extlink.extTargetAppendNewWindowLabel;
226        }
227        // Determine new title based on drupalSettings extTarget configuration.
228        if (oldTitle !== null) {
229          if (Drupal.extlink.hasNewWindowText(oldTitle)) {
230            return;
231          }
232          newTitle = Drupal.extlink.combineLabels(oldTitle, newTitle);
233        }
234        if (newTitle) {
235          externalLinks[i].setAttribute('title', newTitle);
236        }
237      });
238    }
239
240    if (drupalSettings.data.extlink.extNoreferrer) {
241      externalLinks.forEach((link, i) => {
242        // Don't add 'noreferrer' if exclude noreferrer option is set and the
243        // link matches the entered pattern to exclude 'noreferrer' tag.
244        if (drupalSettings.data.extlink.extExcludeNoreferrer && extExcludeNoreferrer.test(link.getAttribute('href'))) {
245          return;
246        }
247
248        const val = link.getAttribute('rel');
249        // When the link does not have a rel attribute set it to 'noreferrer'.
250        if (val === null || typeof val === 'undefined') {
251          externalLinks[i].setAttribute('rel', 'noreferrer');
252          return;
253        }
254        // Add noreferrer.
255        externalLinks[i].setAttribute('rel', `${val} noreferrer`);
256      });
257    }
258
259    /* eslint:disable:no-empty */
260    Drupal.extlink = Drupal.extlink || {};
261
262    // Set up default click function for the external links popup. This should be
263    // overridden by modules wanting to alter the popup.
264    Drupal.extlink.popupClickHandler =
265      Drupal.extlink.popupClickHandler ||
266      (() => {
267        if (drupalSettings.data.extlink.extAlert) {
268          // eslint-disable-next-line no-restricted-globals
269          return confirm(drupalSettings.data.extlink.extAlertText);
270        }
271      });
272
273    const _that = this;
274    Drupal.extlink.handleClick = function (event) {
275      const shouldNavigate = Drupal.extlink.popupClickHandler.call(_that, event);
276      if (typeof shouldNavigate !== 'undefined' && !shouldNavigate) {
277        // Prevent navigation if the user clicks "Cancel".
278        event.preventDefault();
279      }
280    };
281
282    externalLinks.forEach((val, i) => {
283      externalLinks[i].removeEventListener('click', Drupal.extlink.handleClick);
284      externalLinks[i].addEventListener('click', Drupal.extlink.handleClick);
285    });
286  };
287
288  /**
289   * Check if the label already has 'new window' text.
290   *
291   *
292   * @returns boolean
293   * @param label
294   */
295  Drupal.extlink.hasNewWindowText = function (label) {
296    return label.toLowerCase().indexOf(Drupal.t('new window')) !== -1;
297  };
298
299  /**
300   * Combine two labels.
301   *
302   * Combine labels in a readable manner, taking into account if the label
303   * uses parenthesis or not. For examples,
304   *
305   *   1. "A" + "B" => "A, B"
306   *   2. "A" + "(B)" => "A (B)"
307   *   3. "(A)" + "B" => "B (A)"
308   *   4. "(A)" + "(B)" => "(A, B)"
309   *
310   *
311   * @returns string
312   * @param labelA
313   * @param labelB
314   */
315  Drupal.extlink.combineLabels = function (labelA, labelB) {
316    labelA = labelA || '';
317    labelB = labelB || '';
318
319    // If either label is empty, return the other without adding a comma
320    if (!labelA.trim()) return labelB;
321    if (!labelB.trim()) return labelA;
322
323    const labelANoParens = labelA.trim().replace('(', '').replace(')', '');
324    const labelBNoParens = labelB.trim().replace('(', '').replace(')', '');
325    if (labelA === labelANoParens) {
326      if (labelB === labelBNoParens) {
327        // This is Example 1 above: "A" + "B" => "A, B"
328        return `${labelA}, ${labelB}`;
329      }
330
331      // This is Example 2 above: "A" + "(B)" => "A (B)"
332      return `${labelA} ${labelB}`;
333    }
334
335    if (labelB === labelBNoParens) {
336      // This is Example 3 above: "(A)" + "B" => "B (A)"
337      return `${labelB} ${labelA}`;
338    }
339
340    // This is Example 4 above: "(A)" + "(B)" => "(A, B)"
341    return `(${labelANoParens}, ${labelBNoParens})`;
342  };
343
344  /**
345   * Apply a class and a trailing <span> to all links not containing images.
346   *
347   * @param {object[]} links
348   *   An array of DOM elements representing the links.
349   * @param {string} className
350   *   The class to apply to the links.
351   * @param {string} iconPlacement
352   *   'append' or 'prepend' the icon to the link.
353   */
354  Drupal.extlink.applyClassAndSpan = (links, className, iconPlacement) => {
355    let linksToProcess;
356    if (drupalSettings.data.extlink.extImgClass) {
357      linksToProcess = links;
358    } else {
359      // Only text links.
360      linksToProcess = links.filter((link) => {
361        return link.querySelector('img, svg') === null;
362      });
363    }
364
365    for (let i = 0; i < linksToProcess.length; i++) {
366      if (className !== '0') {
367        linksToProcess[i].classList.add(className);
368      }
369
370      // Additional classes:
371      if (className === drupalSettings.data.extlink.mailtoClass && drupalSettings.data.extlink.extAdditionalMailtoClasse
371s) {
372        // Is mail link:
373        linksToProcess[i].classList.add(drupalSettings.data.extlink.extAdditionalMailtoClasses);
374      } else if (className === drupalSettings.data.extlink.telClass && drupalSettings.data.extlink.extAdditionalTelClasses) {
375        // Is regular external link:
376        linksToProcess[i].classList.add(drupalSettings.data.extlink.extAdditionalTelClasses);
377      } else if (drupalSettings.data.extlink.extAdditionalLinkClasses) {
378        // Is regular external link:
379        linksToProcess[i].classList.add(drupalSettings.data.extlink.extAdditionalLinkClasses);
380      }
381
382      // Add data-extlink attribute.
383      linksToProcess[i].setAttribute('data-extlink', '');
384
385      // Add icon.
386      if (iconPlacement) {
387        let link = linksToProcess[i];
388
389        // Prevent appended icons from wrapping lines.
390        if (drupalSettings.data.extlink.extPreventOrphan && iconPlacement === 'append') {
391          // Find the last word in the link.
392          let lastTextNode = link.lastChild;
393          let trailingWhitespace = null;
394          let parentNode = link;
395          while (lastTextNode) {
396            if (lastTextNode.lastChild) {
397              // Last node was not text; step down into child node.
398              parentNode = lastTextNode;
399              lastTextNode = lastTextNode.lastChild;
400            } else if (lastTextNode.nodeName === '#text' && parentNode.lastElementChild && lastTextNode.textContent.trim().length === 0) {
401              // Last node was text, but it was whitespace. Step back into previous node.
402              trailingWhitespace = lastTextNode;
403              parentNode = parentNode.lastElementChild;
404              lastTextNode = parentNode.lastChild;
405            } else {
406              // Last node was null or valid text.
407              break;
408            }
409          }
410          if (lastTextNode && lastTextNode.nodeName === '#text' && lastTextNode.textContent.length > 0) {
411            const lastText = lastTextNode.textContent;
412            const lastWordRegex = new RegExp(/\S+\s*$/, 'g');
413            const lastWord = lastText.match(lastWordRegex);
414            if (lastWord !== null) {
415              // Wrap the last word in a span.
416              const breakPreventer = document.createElement('span');
417              breakPreventer.classList.add('extlink-nobreak');
418              breakPreventer.textContent = lastWord[0];
419              if (trailingWhitespace) {
420                trailingWhitespace.textContent = '';
421                breakPreventer.append(trailingWhitespace.textContent);
422              }
423              lastTextNode.textContent = lastText.substring(0, lastText.length - lastWord[0].length);
424              lastTextNode.parentNode.append(breakPreventer);
425              // Insert the icon into the span rather than the link.
426              link = breakPreventer;
427            }
428          }
429        }
430
431        // Create an icon element.
432        let iconElement;
433        if (drupalSettings.data.extlink.extUseFontAwesome) {
434          iconElement = document.createElement('span');
435          iconElement.setAttribute('class', `fa-${className} extlink`);
436          if (className === drupalSettings.data.extlink.mailtoClass) {
437            if (drupalSettings.data.extlink.mailtoLabel) {
438              link.ariaLabel = drupalSettings.data.extlink.mailtoLabel;
439            }
440            iconElement.innerHTML = Drupal.theme('extlink_fa_mailto', drupalSettings, iconPlacement);
441          } else if (className === drupalSettings.data.extlink.extClass) {
442            if (drupalSettings.data.extlink.extLabel) {
443              link.ariaLabel = drupalSettings.data.extlink.extLabel;
444            }
445            iconElement.innerHTML = Drupal.theme('extlink_fa_extlink', drupalSettings, iconPlacement);
446          } else if (className === drupalSettings.data.extlink.telClass) {
447            if (drupalSettings.data.extlink.telLabel) {
448              link.ariaLabel = drupalSettings.data.extlink.telLabel;
449            }
450            iconElement.innerHTML = Drupal.theme('extlink_fa_tel', drupalSettings, iconPlacement);
451          }
452        } else {
453          iconElement = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
454          iconElement.setAttribute('focusable', 'false');
455          iconElement.setAttribute('width', '1em');
456          iconElement.setAttribute('height', '1em');
457          iconElement.classList.add(className);
458          iconElement.setAttribute('data-extlink-placement', iconPlacement);
459          if (className === drupalSettings.data.extlink.mailtoClass) {
460            iconElement = Drupal.theme('extlink_mailto', iconElement, drupalSettings);
461          } else if (className === drupalSettings.data.extlink.extClass) {
462            iconElement = Drupal.theme('extlink_extlink', iconElement, drupalSettings);
463          } else if (className === drupalSettings.data.extlink.telClass) {
464            iconElement = Drupal.theme('extlink_tel', iconElement, drupalSettings);
465          }
466        }
467        iconElement.setAttribute('role', 'img');
468        iconElement.setAttribute('aria-hidden', drupalSettings.data.extlink.extHideIcons);
469        link[iconPlacement](iconElement);
470      }
471    }
472  };
473
474  /**
475   * Theme function for a Font Awesome mailto icon.
476   *
477   * @param {object} drupalSettings
478   *   Settings object used to construct the markup.
479   * @param {string} iconPlacement
480   *   The class to apply to a link.
481   *
482   * @return {string}
483   *   HTML string of the Font AweSome mailto link icon.
484   */
485  Drupal.theme.extlink_fa_mailto = function (drupalSettings, iconPlacement) {
486    return `<span class="${drupalSettings.data.extlink.extFaMailtoClasse
486s}" data-extlink-placement="${iconPlacement}"></span>`;
487  };
488
489  /**
490   * Theme function for a Font Awesome external link icon.
491   *
492   * @param {object} drupalSettings
493   *   Settings object used to construct the markup.
494   * @param {string} iconPlacement
495   *   The class to apply to a link.
496   *
497   * @return {string}
498   *   HTML string of the Font AweSome external link icon.
499   */
500  Drupal.theme.extlink_fa_extlink = function (drupalSettings, iconPlacement) {
501    return `<span class="${drupalSettings.data.extlink.extFaLinkClasses}" data-extlink-placement="${iconPlacement}"></span>`;
502  };
503
504  /**
505   * Theme function for a Font Awesome telephone icon.
506   *
507   * @param {object} drupalSettings
508   *   Settings object used to construct the markup.
509   * @param {string} iconPlacement
510   *   The class to apply to a link.
511   *
512   * @return {string}
513   *   HTML string of the Font AweSome telephone icon.
514   */
515  Drupal.theme.extlink_fa_tel = function (drupalSettings, iconPlacement) {
516    return `<span class="${drupalSettings.data.extlink.extFaTelClasses}" data-extlink-placement="${iconPlacement}"></span>`;
517  };
518
519  /**
520   * Theme function for a mailto icon.
521   *
522   * @param {object} iconElement
523   *   The current iconElement being altered.
524   * @param {object} drupalSettings
525   *   Settings object used to construct the markup.
526   *
527   * @return {object}
528   *   The altered iconElement.
529   */
530  Drupal.theme.extlink_mailto = function (iconElement, drupalSettings) {
531    iconElement.setAttribute('aria-label', drupalSettings.data.extlink.mailtoLabel);
532    iconElement.setAttribute('viewBox', '0 10 70 20');
533    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"/>`;
534    return iconElement;
535  };
536
537  /**
538   * Theme function for an external link icon.
539   *
540   * @param {object} iconElement
541   *   The current iconElement being altered.
542   * @param {object} drupalSettings
543   *   Settings object used to construct the markup.
544   *
545   * @return {object}
546   *   The altered iconElement.
547   */
548  Drupal.theme.extlink_extlink = function (iconElement, drupalSettings) {
549    iconElement.setAttribute('aria-label', drupalSettings.data.extlink.extLabel);
550    iconElement.setAttribute('viewBox', '0 0 80 40');
551    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"/>`;
552    return iconElement;
553  };
554
555  /**
556   * Theme function for an external telephone icon.
557   *
558   * @param {object} iconElement
559   *   The current iconElement being altered.
560   * @param {object} drupalSettings
561   *   Settings object used to construct the markup.
562   *
563   * @return {object}
564   *   The altered iconElement.
565   */
566  Drupal.theme.extlink_tel = function (iconElement, drupalSettings) {
567    iconElement.setAttribute('aria-label', drupalSettings.data.extlink.telLabel);
568    iconElement.setAttribute('viewBox', '0 0 181.352 181.352');
569    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"/>`;
570    return iconElement;
571  };
572
573  Drupal.behaviors.extlink = Drupal.behaviors.extlink || {};
574  Drupal.behaviors.extlink.attach = (context, drupalSettings) => {
575    // Backwards compatibility, for the benefit of modules overriding extlink
576    // functionality by defining an "extlinkAttach" global function.
577    if (typeof extlinkAttach === 'function') {
578      // eslint-disable-next-line no-undef
579      extlinkAttach(context);
580    } else {
581      Drupal.extlink.attach(context, drupalSettings);
582    }
583  };
584})(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.