1/** 2 * @file 3 */ 4 5(function ($, Drupal, drupalSettings) { 6 7 'use strict'; 8 9 Drupal.extlink = Drupal.extlink || {}; 10 11 Drupal.extlink.attach = function (context, drupalSettings) { 12 if (!drupalSettings.data.hasOwnProperty('extlink')) { 13 return; 14 } 15 16 // Strip the host name down, removing ports, subdomains, or www. 17 var pattern = /^(([^\/:]+?\.)*)([^\.:]{1,})((\.[a-z0-9]{1,253})*)(:[0-9]{1,5})?$/; 18 var host = window.location.host.replace(pattern, '$2$3'); 19 var subdomain = window.location.host.replace(host, ''); 20 21 // Determine what subdomains are considered internal. 22 var subdomains; 23 if (drupalSettings.data.extlink.extSubdomains) { 24 subdomains = '([^/]*\\.)?'; 25 } 26 else if (subdomain === 'www.' || subdomain === '') { 27 subdomains = '(www\\.)?'; 28 } 29 else { 30 subdomains = subdomain.replace('.', '\\.'); 31 } 32 33 // Build regular expressions that define an internal link. 34 var internal_link = new RegExp('^https?://' + subdomains + host, 'i'); 35 36 // Extra internal link matching. 37 var extInclude = false; 38 if (drupalSettings.data.extlink.extInclude) { 39 extInclude = new RegExp(drupalSettings.data.extlink.extInclude.replace(/\\/, '\\'), 'i'); 40 } 41 42 // Extra external link matching. 43 var extExclude = false; 44 if (drupalSettings.data.extlink.extExclude) { 45 extExclude = new RegExp(drupalSettings.data.extlink.extExclude.replace(/\\/, '\\'), 'i'); 46 } 47 48 // Extra external link CSS selector exclusion. 49 var extCssExclude = false; 50 if (drupalSettings.data.extlink.extCssExclude) { 51 extCssExclude = drupalSettings.data.extlink.extCssExclude; 52 } 53 54 // Extra external link CSS selector explicit. 55 var extCssExplicit = false; 56 if (drupalSettings.data.extlink.extCssExplicit) { 57 extCssExplicit = drupalSettings.data.extlink.extCssExplicit; 58 } 59 60 // Find all links which are NOT internal and begin with http as opposed 61 // to ftp://, javascript:, etc. other kinds of links. 62 // When operating on the 'this' variable, the host has been appended to 63 // all links by the browser, even local ones. 64 // In jQuery 1.1 and higher, we'd use a filter method here, but it is not 65 // available in jQuery 1.0 (Drupal 5 default). 66 var external_links = []; 67 var mailto_links = []; 68 $('a:not(.' + drupalSettings.data.extlink.extClass + ', .' + drupalSettings.data.extlink.mailtoClass + '), area:not(.' + drupalSettings.data.extlink.extClass + ', .' + drupalSettings.data.extlink.mailtoClass + ')', context).each(function (el) { 69 try { 70 var url = ''; 71 if (typeof this.href == 'string') { 72 url = this.href.toLowerCase(); 73 } 74 // Handle SVG links (xlink:href). 75 else if (typeof this.href == 'object') { 76 url = this.href.baseVal; 77 } 78 if (url.indexOf('http') === 0 79 && ((!internal_link.test(url) && !(extExclude && extExclude.test(url))) || (extInclude && extInclude.test(url))) 80 && !(extCssExclude && $(this).is(extCssExclude)) 81 && !(extCssExclude && $(this).parents(extCssExclude).length > 0) 82 && !(extCssExplicit && $(this).parents(extCssExplicit).length < 1)) { 83 external_links.push(this); 84 } 85 // Do not include area tags with begin with mailto: (this prohibits 86 // icons from being added to image-maps). 87 else if (this.tagName !== 'AREA' 88 && url.indexOf('mailto:') === 0 89 && !(extCssExclude && $(this).parents(extCssExclude).length > 0) 90 && !(extCssExplicit && $(this).parents(extCssExplicit).length < 1)) { 91 mailto_links.push(this); 92 } 93 } 94 // IE7 throws errors often when dealing with irregular links, such as: 95 // <a href="node/10"></a> Empty tags. 96 // <a href="http://user:[email protected]">example</a> User:pass syntax. 97 catch (error) { 98 return false; 99 } 100 }); 101 102 if (drupalSettings.data.extlink.extClass !== '0' && drupalSettings.data.extlink.extClass !== '') { 103 Drupal.extlink.applyClassAndSpan(external_links, drupalSettings.data.extlink.extClass); 104 } 105 106 if (drupalSettings.data.extlink.mailtoClass !== '0' && drupalSettings.data.extlink.mailtoClass !== '') { 107 Drupal.extlink.applyClassAndSpan(mailto_links, drupalSettings.data.extlink.mailtoClass); 108 } 109 110 if (drupalSettings.data.extlink.extTarget) { 111 // Apply the target attribute to all links. 112 $(external_links).filter(function () { 113 // Filter out links with target set if option specified. 114 return !(drupalSettings.data.extlink.extTargetNoOverride && $(this).is('a[target]')); 115 }).attr({ target: '_blank' }); 116
117 // Add noopener and noreferrer rel attributes to combat phishing. 118 $(external_links).attr('rel', function (i, val) { 119 // If no rel attribute is present, create one with the values noopener and noreferrer. 120 if (val === null || typeof val === 'undefined') { 121 return 'noopener noreferrer'; 122 } 123 // Check to see if rel contains noopener or noreferrer. Add what doesn't exist. 124 if (val.indexOf('noopener') > -1 || val.indexOf('noreferrer') > -1) { 125 if (val.indexOf('noopener') === -1) { 126 return val + ' noopener'; 127 } 128 if (val.indexOf('noreferrer') === -1) { 129 return val + ' noreferrer'; 130 } 131 // Both noopener and noreferrer exist. Nothing needs to be added. 132 else { 133 return val; 134 } 135 } 136 // Else, append noopener and noreferrer to val. 137 else { 138 return val + ' noopener noreferrer'; 139 } 140 }); 141 } 142 143 if (drupalSettings.data.extlink.extNofollow) { 144 $(external_links).attr('rel', function (i, val) { 145 // when the link does not have a rel attribute set it to 'nofollow'. 146 if (val === null || typeof val === 'undefined') { 147 return 'nofollow'; 148 } 149 var target = 'nofollow'; 150 // Change the target, if not overriding follow. 151 if (drupalSettings.data.extlink.extFollowNoOverride) { 152 target = 'follow'; 153 } 154 if (val.indexOf(target) === -1) { 155 return val + ' nofollow'; 156 } 157 return val; 158 }); 159 } 160 161 Drupal.extlink = Drupal.extlink || {}; 162 163 // Set up default click function for the external links popup. This should be 164 // overridden by modules wanting to alter the popup. 165 Drupal.extlink.popupClickHandler = Drupal.extlink.popupClickHandler || function () { 166 if (drupalSettings.data.extlink.extAlert) { 167 return confirm(drupalSettings.data.extlink.extAlertText); 168 } 169 }; 170 171 $(external_links).click(function (e) { 172 return Drupal.extlink.popupClickHandler(e, this); 173 }); 174 }; 175 176 /** 177 * Apply a class and a trailing <span> to all links not containing images. 178 * 179 * @param {object[]} links 180 * An array of DOM elements representing the links. 181 * @param {string} class_name 182 * The class to apply to the links. 183 */ 184 Drupal.extlink.applyClassAndSpan = function (links, class_name) { 185 var $links_to_process; 186 if (drupalSettings.data.extlink.extImgClass) { 187 $links_to_process = $(links); 188 } 189 else { 190 var links_with_images = $(links).find('img').parents('a'); 191 $links_to_process = $(links).not(links_with_images); 192 } 193 if (class_name !== '0') { 194 $links_to_process.addClass(class_name); 195 } 196 var i; 197 var length = $links_to_process.length; 198 for (i = 0; i < length; i++) { 199 var $link = $($links_to_process[i]); 200 if (class_name === drupalSettings.data.extlink.mailtoClass) { 201 $link.append('<span class="' + class_name + '" aria-label="' + drupalSettings.data.extlink.mailtoLabel + '"></span>'); 202 } 203 else { 204 $link.append('<span class="' + class_name + '" aria-label="' + drupalSettings.data.extlink.extLabel + '"></span>'); 205 } 206 } 207 }; 208 209 Drupal.behaviors.extlink = Drupal.behaviors.extlink || {}; 210 Drupal.behaviors.extlink.attach = function (context, drupalSettings) { 211 // Backwards compatibility, for the benefit of modules overriding extlink 212 // functionality by defining an "extlinkAttach" global function. 213 if (typeof extlinkAttach === 'function') { 214 extlinkAttach(context); 215 } 216 else { 217 Drupal.extlink.attach(context, drupalSettings); 218 } 219 }; 220 221})(jQuery, 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.