1 2/** 3 * Override jQuery.fn.init to guard against XSS attacks. 4 * 5 * See http://bugs.jquery.com/ticket/9521 6 */ 7(function () { 8 var jquery_init = jQuery.fn.init; 9 jQuery.fn.init = function (selector, context, rootjQuery) { 10 // If the string contains a "#" before a "<", treat it as invalid HTML. 11 if (selector && typeof selector === 'string') { 12 var hash_position = selector.indexOf('#'); 13 if (hash_position >= 0) { 14 var bracket_position = selector.indexOf('<'); 15 if (bracket_position > hash_position) { 16 throw 'Syntax error, unrecognized expression: ' + selector; 17 } 18 } 19 } 20 return jquery_init.call(this, selector, context, rootjQuery); 21 }; 22 jQuery.fn.init.prototype = jquery_init.prototype; 23 24 /** 25 * Pre-filter Ajax requests to guard against XSS attacks. 26 * 27 * See https://github.com/jquery/jquery/issues/2432 28 */ 29 if ($.ajaxPrefilter) { 30 // For newer versions of jQuery, use an Ajax prefilter to prevent 31 // auto-executing script tags from untrusted domains. This is similar to the 32 // fix that is built in to jQuery 3.0 and higher. 33 $.ajaxPrefilter(function (s) { 34 if (s.crossDomain) { 35 s.contents.script = false; 36 } 37 }); 38 } 39 else if ($.httpData) { 40 // For the version of jQuery that ships with Drupal core, override 41 // jQuery.httpData to prevent auto-detecting "script" data types from 42 // untrusted domains. 43 var jquery_httpData = $.httpData; 44 $.httpData = function (xhr, type, s) { 45 // @todo Consider backporting code from newer jQuery versions to check for 46 // a cross-domain request here, rather than using Drupal.urlIsLocal() to 47 // block scripts from all URLs that are not on the same site. 48 if (!type && (!s || !Drupal.urlIsLocal(s.url))) { 49 var content_type = xhr.getResponseHeader('content-type') || ''; 50 if (content_type.indexOf('javascript') >= 0) { 51 // Default to a safe data type. 52 type = 'text'; 53 } 54 } 55 return jquery_httpData.call(this, xhr, type, s); 56 }; 57 $.httpData.prototype = jquery_httpData.prototype; 58 } 59})(); 60 61var Drupal = Drupal || { 'settings': {}, 'behaviors': {}, 'themes': {}, 'locale': {} }; 62 63/** 64 * Set the variable that indicates if JavaScript behaviors should be applied 65 */ 66Drupal.jsEnabled = document.getElementsByTagName && document.createElement && document.createTextNode && document.documentElement && document.getElementById; 67 68/** 69 * Attach all registered behaviors to a page element. 70 * 71 * Behaviors are event-triggered actions that attach to page elements, enhancing 72 * default non-Javascript UIs. Behaviors are registered in the Drupal.behaviors 73 * object as follows: 74 * @code 75 * Drupal.behaviors.behaviorName = function () { 76 * ... 77 * }; 78 * @endcode 79 * 80 * Drupal.attachBehaviors is added below to the jQuery ready event and so 81 * runs on initial page load. Developers implementing AHAH/AJAX in their 82 * solutions should also call this function after new page content has been 83 * loaded, feeding in an element to be processed, in order to attach all 84 * behaviors to the new content. 85 * 86 * Behaviors should use a class in the form behaviorName-processed to ensure 87 * the behavior is attached only once to a given element. (Doing so enables 88 * the reprocessing of given elements, which may be needed on occasion despite 89 * the ability to limit behavior attachment to a particular element.) 90 * 91 * @param context 92 * An element to attach behaviors to. If none is given, the document element 93 * is used. 94 */ 95Drupal.attachBehaviors = function(context) { 96 context = context || document; 97 if (Drupal.jsEnabled) { 98 // Execute all of them. 99 jQuery.each(Drupal.behaviors, function() { 100 this(context); 101 }); 102 } 103}; 104 105/** 106 * Encode special characters in a plain-text string for display as HTML. 107 */ 108Drupal.checkPlain = function(str) { 109 str = String(str); 110 var replace = { '&': '&', "'": ''', '"': '"', '<': '<', '>': '>' }; 111 for (var character in replace) { 112 var regex = new RegExp(character, 'g'); 113 str = str.replace(regex, replace[character]); 114 } 115 return str; 116}; 117 118/** 119 * Translate strings to the page language or a given language. 120 * 121 * See the documentation of the server-side t() function for further details. 122 * 123 * @param str 124 * A string containing the English string to translate. 125 * @param args 126 * An object of replacements pairs to make after translation. Incidences 127 * of any key in this array are replaced with the corresponding value. 128 * Based on the first character of the key, the value is escaped and/or themed: 129 * - !variable: inserted as is 130 * - @variable: escape plain text to HTML (Drupal.checkPlain) 131 * - %variable: escape text and theme as a placeholder for user-submitted 132 * content (checkPlain + Drupal.theme('placeholder')) 133 * @return 134 * The translated string. 135 */ 136Drupal.t = function(str, args) { 137 // Fetch the localized version of the string. 138 if (Drupal.locale.strings && Drupal.locale.strings[str]) { 139 str = Drupal.locale.strings[str]; 140 } 141 142 if (args) { 143 // Transform arguments before inserting them 144 for (var key in args) { 145 switch (key.charAt(0)) { 146 // Escaped only 147 case '@': 148 args[key] = Drupal.checkPlain(args[key]); 149 break; 150 // Pass-through 151 case '!': 152 break; 153 // Escaped and placeholder 154 case '%': 155 default: 156 args[key] = Drupal.theme('placeholder', args[key]); 157 break; 158 } 159 str = str.replace(key, args[key]); 160 } 161 } 162 return str; 163}; 164 165/** 166 * Format a string containing a count of items. 167 * 168 * This function ensures that the string is pluralized correctly. Since Drupal.t() is 169 * called by this function, make sure not to pass already-localized strings to it. 170 * 171 * See the documentation of the server-side format_plural() function for further details. 172 * 173 * @param count 174 * The item count to display. 175 * @param singular 176 * The string for the singular case. Please make sure it is clear this is 177 * singular, to ease translation (e.g. use "1 new comment" instead of "1 new"). 178 * Do not use @count in the singular string. 179 * @param plural 180 * The string for the plural case. Please make sure it is clear this is plural, 181 * to ease translation. Use @count in place of the item count, as in "@count 182 * new comments". 183 * @param args 184 * An object of replacements pairs to make after translation. Incidences 185 * of any key in this array are replaced with the corresponding value. 186 * Based on the first character of the key, the value is escaped and/or themed: 187 * - !variable: inserted as is 188 * - @variable: escape plain text to HTML (Drupal.checkPlain) 189 * - %variable: escape text and theme as a placeholder for user-submitted 190 * content (checkPlain + Drupal.theme('placeholder')) 191 * Note that you do not need to include @count in this array.
192 * This replacement is done automatically for the plural case. 193 * @return 194 * A translated string. 195 */ 196Drupal.formatPlural = function(count, singular, plural, args) { 197 var args = args || {}; 198 args['@count'] = count; 199 // Determine the index of the plural form. 200 var index = Drupal.locale.pluralFormula ? Drupal.locale.pluralFormula(args['@count']) : ((args['@count'] == 1) ? 0 : 1); 201 202 if (index == 0) { 203 return Drupal.t(singular, args); 204 } 205 else if (index == 1) { 206 return Drupal.t(plural, args); 207 } 208 else { 209 args['@count['+ index +']'] = args['@count']; 210 delete args['@count']; 211 return Drupal.t(plural.replace('@count', '@count['+ index +']'), args); 212 } 213}; 214 215/** 216 * Returns the passed in URL as an absolute URL. 217 * 218 * @param url 219 * The URL string to be normalized to an absolute URL. 220 * 221 * @return 222 * The normalized, absolute URL. 223 * 224 * @see https://github.com/angular/angular.js/blob/v1.4.4/src/ng/urlUtils.js 225 * @see https://grack.com/blog/2009/11/17/absolutizing-url-in-javascript 226 * @see https://github.com/jquery/jquery-ui/blob/1.11.4/ui/tabs.js#L53 227 */ 228Drupal.absoluteUrl = function (url) { 229 var urlParsingNode = document.createElement('a'); 230 231 // Decode the URL first; this is required by IE <= 6. Decoding non-UTF-8 232 // strings may throw an exception. 233 try { 234 url = decodeURIComponent(url); 235 } catch (e) {} 236 237 urlParsingNode.setAttribute('href', url); 238 239 // IE <= 7 normalizes the URL when assigned to the anchor node similar to 240 // the other browsers. 241 return urlParsingNode.cloneNode(false).href; 242}; 243 244/** 245 * Returns true if the URL is within Drupal's base path. 246 * 247 * @param url 248 * The URL string to be tested. 249 * 250 * @return 251 * Boolean true if local. 252 * 253 * @see https://github.com/jquery/jquery-ui/blob/1.11.4/ui/tabs.js#L58 254 */ 255Drupal.urlIsLocal = function (url) { 256 // Always use browser-derived absolute URLs in the comparison, to avoid 257 // attempts to break out of the base path using directory traversal. 258 var absoluteUrl = Drupal.absoluteUrl(url); 259 var protocol = location.protocol; 260 261 // Consider URLs that match this site's base URL but use HTTPS instead of HTTP 262 // as local as well. 263 if (protocol === 'http:' && absoluteUrl.indexOf('https:') === 0) { 264 protocol = 'https:'; 265 } 266 var baseUrl = protocol + '//' + location.host + Drupal.settings.basePath.slice(0, -1); 267 268 // Decoding non-UTF-8 strings may throw an exception. 269 try { 270 absoluteUrl = decodeURIComponent(absoluteUrl); 271 } catch (e) {} 272 try { 273 baseUrl = decodeURIComponent(baseUrl); 274 } catch (e) {} 275 276 // The given URL matches the site's base URL, or has a path under the site's 277 // base URL. 278 return absoluteUrl === baseUrl || absoluteUrl.indexOf(baseUrl + '/') === 0; 279}; 280 281/** 282 * Generate the themed representation of a Drupal object. 283 * 284 * All requests for themed output must go through this function. It examines 285 * the request and routes it to the appropriate theme function. If the current 286 * theme does not provide an override function, the generic theme function is 287 * called. 288 * 289 * For example, to retrieve the HTML that is output by theme_placeholder(text), 290 * call Drupal.theme('placeholder', text). 291 * 292 * @param func 293 * The name of the theme function to call. 294 * @param ... 295 * Additional arguments to pass along to the theme function. 296 * @return 297 * Any data the theme function returns. This could be a plain HTML string, 298 * but also a complex object. 299 */ 300Drupal.theme = function(func) { 301 for (var i = 1, args = []; i < arguments.length; i++) { 302 args.push(arguments[i]); 303 } 304 305 return (Drupal.theme[func] || Drupal.theme.prototype[func]).apply(this, args); 306}; 307 308/** 309 * Parse a JSON response. 310 * 311 * The result is either the JSON object, or an object with 'status' 0 and 'data' an error message. 312 */ 313Drupal.parseJson = function (data) { 314 if ((data.substring(0, 1) != '{') && (data.substring(0, 1) != '[')) { 315 return { status: 0, data: data.length ? data : Drupal.t('Unspecified error') }; 316 } 317 return eval('(' + data + ');'); 318}; 319 320/** 321 * Freeze the current body height (as minimum height). Used to prevent 322 * unnecessary upwards scrolling when doing DOM manipulations. 323 */ 324Drupal.freezeHeight = function () { 325 Drupal.unfreezeHeight(); 326 var div = document.createElement('div'); 327 $(div).css({ 328 position: 'absolute', 329 top: '0px', 330 left: '0px', 331 width: '1px', 332 height: $('body').css('height') 333 }).attr('id', 'freeze-height'); 334 $('body').append(div); 335}; 336 337/** 338 * Unfreeze the body height 339 */ 340Drupal.unfreezeHeight = function () { 341 $('#freeze-height').remove(); 342}; 343 344/** 345 * Wrapper around encodeURIComponent() which avoids Apache quirks (equivalent of 346 * drupal_urlencode() in PHP). This function should only be used on paths, not 347 * on query string arguments. 348 */ 349Drupal.encodeURIComponent = function (item, uri) { 350 uri = uri || location.href; 351 item = encodeURIComponent(item).replace(/%2F/g, '/'); 352 return (uri.indexOf('?q=') != -1) ? item : item.replace(/%26/g, '%2526').replace(/%23/g, '%2523').replace(/\/\//g, '/%252F'); 353}; 354 355/** 356 * Get the text selection in a textarea. 357 */ 358Drupal.getSelection = function (element) { 359 if (typeof(element.selectionStart) != 'number' && document.selection) { 360 // The current selection 361 var range1 = document.selection.createRange(); 362 var range2 = range1.duplicate(); 363 // Select all text. 364 range2.moveToElementText(element); 365 // Now move 'dummy' end point to end point of original range.
366 range2.setEndPoint('EndToEnd', range1); 367 // Now we can calculate start and end points. 368 var start = range2.text.length - range1.text.length; 369 var end = start + range1.text.length; 370 return { 'start': start, 'end': end }; 371 } 372 return { 'start': element.selectionStart, 'end': element.selectionEnd }; 373}; 374 375/** 376 * Build an error message from ahah response. 377 */ 378Drupal.ahahError = function(xmlhttp, uri) { 379 if (xmlhttp.status == 200) { 380 if (jQuery.trim($(xmlhttp.responseText).text())) { 381 var message = Drupal.t("An error occurred. \n@uri\n@text", {'@uri': uri, '@text': xmlhttp.responseText }); 382 } 383 else { 384 var message = Drupal.t("An error occurred. \n@uri\n(no information available).", {'@uri': uri, '@text': xmlhttp.responseText }); 385 } 386 } 387 else { 388 var message = Drupal.t("An HTTP error @status occurred. \n@uri", {'@uri': uri, '@status': xmlhttp.status }); 389 } 390 return message; 391} 392 393// Global Killswitch on the <html> element 394if (Drupal.jsEnabled) { 395 // Global Killswitch on the <html> element 396 $(document.documentElement).addClass('js'); 397 // 'js enabled' cookie 398 document.cookie = 'has_js=1; path=/'; 399 // Attach all behaviors. 400 $(document).ready(function() { 401 Drupal.attachBehaviors(this); 402 }); 403} 404 405/** 406 * The default themes. 407 */ 408Drupal.theme.prototype = { 409 410 /** 411 * Formats text for emphasized display in a placeholder inside a sentence. 412 * 413 * @param str 414 * The text to format (plain-text). 415 * @return 416 * The formatted text (html). 417 */ 418 placeholder: function(str) { 419 return '<em>' + Drupal.checkPlain(str) + '</em>'; 420 } 421};
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.