1var Drupal = Drupal || { 'settings': {}, 'behaviors': {}, 'locale': {} }; 2 3// Allow other JavaScript libraries to use $. 4jQuery.noConflict(); 5 6(function ($) { 7 8/** 9 * Override jQuery.fn.init to guard against XSS attacks. 10 * 11 * See http://bugs.jquery.com/ticket/9521 12 */ 13var jquery_init = $.fn.init; 14$.fn.init = function (selector, context, rootjQuery) { 15 // If the string contains a "#" before a "<", treat it as invalid HTML. 16 if (selector && typeof selector === 'string') { 17 var hash_position = selector.indexOf('#'); 18 if (hash_position >= 0) { 19 var bracket_position = selector.indexOf('<'); 20 if (bracket_position > hash_position) { 21 throw 'Syntax error, unrecognized expression: ' + selector; 22 } 23 } 24 } 25 return jquery_init.call(this, selector, context, rootjQuery); 26}; 27$.fn.init.prototype = jquery_init.prototype; 28 29/** 30 * Pre-filter Ajax requests to guard against XSS attacks. 31 * 32 * See https://github.com/jquery/jquery/issues/2432 33 */ 34if ($.ajaxPrefilter) { 35 // For newer versions of jQuery, use an Ajax prefilter to prevent 36 // auto-executing script tags from untrusted domains. This is similar to the 37 // fix that is built in to jQuery 3.0 and higher. 38 $.ajaxPrefilter(function (s) { 39 if (s.crossDomain) { 40 s.contents.script = false; 41 } 42 }); 43} 44else if ($.httpData) { 45 // For the version of jQuery that ships with Drupal core, override 46 // jQuery.httpData to prevent auto-detecting "script" data types from 47 // untrusted domains. 48 var jquery_httpData = $.httpData; 49 $.httpData = function (xhr, type, s) { 50 // @todo Consider backporting code from newer jQuery versions to check for 51 // a cross-domain request here, rather than using Drupal.urlIsLocal() to 52 // block scripts from all URLs that are not on the same site. 53 if (!type && !Drupal.urlIsLocal(s.url)) { 54 var content_type = xhr.getResponseHeader('content-type') || ''; 55 if (content_type.indexOf('javascript') >= 0) { 56 // Default to a safe data type. 57 type = 'text'; 58 } 59 } 60 return jquery_httpData.call(this, xhr, type, s); 61 }; 62 $.httpData.prototype = jquery_httpData.prototype; 63} 64 65/** 66 * Attach all registered behaviors to a page element. 67 * 68 * Behaviors are event-triggered actions that attach to page elements, enhancing 69 * default non-JavaScript UIs. Behaviors are registered in the Drupal.behaviors 70 * object using the method 'attach' and optionally also 'detach' as follows: 71 * @code 72 * Drupal.behaviors.behaviorName = { 73 * attach: function (context, settings) { 74 * ... 75 * }, 76 * detach: function (context, settings, trigger) { 77 * ... 78 * } 79 * }; 80 * @endcode 81 * 82 * Drupal.attachBehaviors is added below to the jQuery ready event and so 83 * runs on initial page load. Developers implementing AHAH/Ajax in their 84 * solutions should also call this function after new page content has been 85 * loaded, feeding in an element to be processed, in order to attach all 86 * behaviors to the new content. 87 * 88 * Behaviors should use 89 * @code 90 * $(selector).once('behavior-name', function () { 91 * ... 92 * }); 93 * @endcode 94 * to ensure the behavior is attached only once to a given element. (Doing so 95 * enables the reprocessing of given elements, which may be needed on occasion 96 * despite the ability to limit behavior attachment to a particular element.) 97 * 98 * @param context 99 * An element to attach behaviors to. If none is given, the document element 100 * is used. 101 * @param settings 102 * An object containing settings for the current context. If none given, the 103 * global Drupal.settings object is used. 104 */ 105Drupal.attachBehaviors = function (context, settings) { 106 context = context || document; 107 settings = settings || Drupal.settings; 108 // Execute all of them. 109 $.each(Drupal.behaviors, function () { 110 if ($.isFunction(this.attach)) { 111 this.attach(context, settings); 112 } 113 }); 114}; 115 116/** 117 * Detach registered behaviors from a page element. 118 * 119 * Developers implementing AHAH/Ajax in their solutions should call this 120 * function before page content is about to be removed, feeding in an element 121 * to be processed, in order to allow special behaviors to detach from the 122 * content. 123 * 124 * Such implementations should look for the class name that was added in their 125 * corresponding Drupal.behaviors.behaviorName.attach implementation, i.e. 126 * behaviorName-processed, to ensure the behavior is detached only from 127 * previously processed elements. 128 * 129 * @param context 130 * An element to detach behaviors from. If none is given, the document element 131 * is used. 132 * @param settings
133 * An object containing settings for the current context. If none given, the 134 * global Drupal.settings object is used. 135 * @param trigger 136 * A string containing what's causing the behaviors to be detached. The 137 * possible triggers are: 138 * - unload: (default) The context element is being removed from the DOM. 139 * - move: The element is about to be moved within the DOM (for example, 140 * during a tabledrag row swap). After the move is completed, 141 * Drupal.attachBehaviors() is called, so that the behavior can undo 142 * whatever it did in response to the move. Many behaviors won't need to 143 * do anything simply in response to the element being moved, but because 144 * IFRAME elements reload their "src" when being moved within the DOM, 145 * behaviors bound to IFRAME elements (like WYSIWYG editors) may need to 146 * take some action. 147 * - serialize: When an Ajax form is submitted, this is called with the 148 * form as the context. This provides every behavior within the form an 149 * opportunity to ensure that the field elements have correct content 150 * in them before the form is serialized. The canonical use-case is so 151 * that WYSIWYG editors can update the hidden textarea to which they are 152 * bound. 153 * 154 * @see Drupal.attachBehaviors 155 */ 156Drupal.detachBehaviors = function (context, settings, trigger) { 157 context = context || document; 158 settings = settings || Drupal.settings; 159 trigger = trigger || 'unload'; 160 // Execute all of them. 161 $.each(Drupal.behaviors, function () { 162 if ($.isFunction(this.detach)) { 163 this.detach(context, settings, trigger); 164 } 165 }); 166}; 167 168/** 169 * Encode special characters in a plain-text string for display as HTML. 170 * 171 * @ingroup sanitization 172 */ 173Drupal.checkPlain = function (str) { 174 var character, regex, 175 replace = { '&': '&', "'": ''', '"': '"', '<': '<', '>': '>' }; 176 str = String(str); 177 for (character in replace) { 178 if (replace.hasOwnProperty(character)) { 179 regex = new RegExp(character, 'g'); 180 str = str.replace(regex, replace[character]); 181 } 182 } 183 return str; 184}; 185 186/** 187 * Replace placeholders with sanitized values in a string. 188 * 189 * @param str 190 * A string with placeholders. 191 * @param args 192 * An object of replacements pairs to make. Incidences of any key in this 193 * array are replaced with the corresponding value. Based on the first 194 * character of the key, the value is escaped and/or themed: 195 * - !variable: inserted as is 196 * - @variable: escape plain text to HTML (Drupal.checkPlain) 197 * - %variable: escape text and theme as a placeholder for user-submitted 198 * content (checkPlain + Drupal.theme('placeholder')) 199 * 200 * @see Drupal.t() 201 * @ingroup sanitization 202 */ 203Drupal.formatString = function(str, args) { 204 // Transform arguments before inserting them. 205 for (var key in args) { 206 if (args.hasOwnProperty(key)) { 207 switch (key.charAt(0)) { 208 // Escaped only. 209 case '@': 210 args[key] = Drupal.checkPlain(args[key]); 211 break; 212 // Pass-through. 213 case '!': 214 break; 215 // Escaped and placeholder. 216 default: 217 args[key] = Drupal.theme('placeholder', args[key]); 218 break; 219 } 220 } 221 } 222 223 return Drupal.stringReplace(str, args, null); 224}; 225 226/** 227 * Replace substring. 228 * 229 * The longest keys will be tried first. Once a substring has been replaced, 230 * its new value will not be searched again. 231 * 232 * @param {String} str 233 * A string with placeholders. 234 * @param {Object} args 235 * Key-value pairs. 236 * @param {Array|null} keys 237 * Array of keys from the "args". Internal use only. 238 * 239 * @return {String} 240 * Returns the replaced string. 241 */ 242Drupal.stringReplace = function (str, args, keys) { 243 if (str.length === 0) { 244 return str; 245 } 246 247 // If the array of keys is not passed then collect the keys from the args. 248 if (!$.isArray(keys)) { 249 keys = []; 250 for (var k in args) { 251 if (args.hasOwnProperty(k)) { 252 keys.push(k); 253 } 254 } 255 256 // Order the keys by the character length. The shortest one is the first. 257 keys.sort(function (a, b) { return a.length - b.length; }); 258 } 259 260 if (keys.length === 0) { 261 return str; 262 } 263 264 // Take next longest one from the end. 265 var key = keys.pop(); 266 var fragments = str.split(key); 267 268 if (keys.length) { 269 for (var i = 0; i < fragments.length; i++) { 270 // Process each fragment with a copy of remaining keys. 271 fragments[i] = Drupal.stringReplace(fragments[i], args, keys.slice(0)); 272 } 273 } 274 275 return fragments.join(args[key]); 276}; 277 278/** 279 * Translate strings to the page language or a given language. 280 * 281 * See the documentation of the server-side t() function for further details. 282 * 283 * @param str 284 * A string containing the English string to translate. 285 * @param args 286 * An object of replacements pairs to make after translation. Incidences 287 * of any key in this array are replaced with the corresponding value. 288 * See Drupal.formatString(). 289 * 290 * @param options 291 * - 'context' (defaults to the empty context): The context the source string 292 * belongs to. 293 * 294 * @return 295 * The translated string. 296 */ 297Drupal.t = function (str, args, options) { 298 options = options || {}; 299 options.context = options.context || ''; 300 301 // Fetch the localized version of the string. 302 if (Drupal.locale.strings && Drupal.locale.strings[options.context] && Drupal.locale.strings[options.context][str]) { 303 str = Drupal.locale.strings[options.context][str]; 304 } 305 306 if (args) { 307 str = Drupal.formatString(str, args); 308 } 309 return str; 310}; 311 312/** 313 * Format a string containing a count of items. 314 * 315 * This function ensures that the string is pluralized correctly. Since Drupal.t() is 316 * called by this function, make sure not to pass already-localized strings to it. 317 * 318 * See the documentation of the server-side format_plural() function for further details. 319 * 320 * @param count 321 * The item count to display. 322 * @param singular 323 * The string for the singular case. Please make sure it is clear this is 324 * singular, to ease translation (e.g. use "1 new comment" instead of "1 new"). 325 * Do not use @count in the singular string. 326 * @param plural 327 * The string for the plural case. Please make sure it is clear this is plural, 328 * to ease translation. Use @count in place of the item count, as in "@count 329 * new comments". 330 * @param args 331 * An object of replacements pairs to make after translation. Incidences 332 * of any key in this array are replaced with the corresponding value. 333 * See Drupal.formatString(). 334 * Note that you do not need to include @count in this array.
335 * This replacement is done automatically for the plural case. 336 * @param options 337 * The options to pass to the Drupal.t() function. 338 * @return 339 * A translated string. 340 */ 341Drupal.formatPlural = function (count, singular, plural, args, options) { 342 args = args || {}; 343 args['@count'] = count; 344 // Determine the index of the plural form. 345 var index = Drupal.locale.pluralFormula ? Drupal.locale.pluralFormula(args['@count']) : ((args['@count'] == 1) ? 0 : 1); 346 347 if (index == 0) { 348 return Drupal.t(singular, args, options); 349 } 350 else if (index == 1) { 351 return Drupal.t(plural, args, options); 352 } 353 else { 354 args['@count[' + index + ']'] = args['@count']; 355 delete args['@count']; 356 return Drupal.t(plural.replace('@count', '@count[' + index + ']'), args, options); 357 } 358}; 359 360/** 361 * Returns the passed in URL as an absolute URL. 362 * 363 * @param url 364 * The URL string to be normalized to an absolute URL. 365 * 366 * @return 367 * The normalized, absolute URL. 368 * 369 * @see https://github.com/angular/angular.js/blob/v1.4.4/src/ng/urlUtils.js 370 * @see https://grack.com/blog/2009/11/17/absolutizing-url-in-javascript 371 * @see https://github.com/jquery/jquery-ui/blob/1.11.4/ui/tabs.js#L53 372 */ 373Drupal.absoluteUrl = function (url) { 374 var urlParsingNode = document.createElement('a'); 375 376 // Decode the URL first; this is required by IE <= 6. Decoding non-UTF-8 377 // strings may throw an exception. 378 try { 379 url = decodeURIComponent(url); 380 } catch (e) {} 381 382 urlParsingNode.setAttribute('href', url); 383 384 // IE <= 7 normalizes the URL when assigned to the anchor node similar to 385 // the other browsers. 386 return urlParsingNode.cloneNode(false).href; 387}; 388 389/** 390 * Returns true if the URL is within Drupal's base path. 391 * 392 * @param url 393 * The URL string to be tested. 394 * 395 * @return 396 * Boolean true if local. 397 * 398 * @see https://github.com/jquery/jquery-ui/blob/1.11.4/ui/tabs.js#L58 399 */ 400Drupal.urlIsLocal = function (url) { 401 // Always use browser-derived absolute URLs in the comparison, to avoid 402 // attempts to break out of the base path using directory traversal. 403 var absoluteUrl = Drupal.absoluteUrl(url); 404 var protocol = location.protocol; 405 406 // Consider URLs that match this site's base URL but use HTTPS instead of HTTP 407 // as local as well. 408 if (protocol === 'http:' && absoluteUrl.indexOf('https:') === 0) { 409 protocol = 'https:'; 410 } 411 var baseUrl = protocol + '//' + location.host + Drupal.settings.basePath.slice(0, -1); 412 413 // Decoding non-UTF-8 strings may throw an exception. 414 try { 415 absoluteUrl = decodeURIComponent(absoluteUrl); 416 } catch (e) {} 417 try { 418 baseUrl = decodeURIComponent(baseUrl); 419 } catch (e) {} 420 421 // The given URL matches the site's base URL, or has a path under the site's 422 // base URL. 423 return absoluteUrl === baseUrl || absoluteUrl.indexOf(baseUrl + '/') === 0; 424}; 425 426/** 427 * Generate the themed representation of a Drupal object. 428 * 429 * All requests for themed output must go through this function. It examines 430 * the request and routes it to the appropriate theme function. If the current 431 * theme does not provide an override function, the generic theme function is 432 * called. 433 * 434 * For example, to retrieve the HTML for text that should be emphasized and 435 * displayed as a placeholder inside a sentence, call 436 * Drupal.theme('placeholder', text). 437 * 438 * @param func 439 * The name of the theme function to call. 440 * @param ... 441 * Additional arguments to pass along to the theme function. 442 * @return 443 * Any data the theme function returns. This could be a plain HTML string, 444 * but also a complex object. 445 */ 446Drupal.theme = function (func) { 447 var args = Array.prototype.slice.apply(arguments, [1]); 448 449 return (Drupal.theme[func] || Drupal.theme.prototype[func]).apply(this, args); 450}; 451 452/** 453 * Freeze the current body height (as minimum height). Used to prevent 454 * unnecessary upwards scrolling when doing DOM manipulations. 455 */ 456Drupal.freezeHeight = function () { 457 Drupal.unfreezeHeight(); 458 $('<div id="freeze-height"></div>').css({ 459 position: 'absolute', 460 top: '0px', 461 left: '0px', 462 width: '1px', 463 height: $('body').css('height') 464 }).appendTo('body'); 465}; 466 467/** 468 * Unfreeze the body height. 469 */ 470Drupal.unfreezeHeight = function () { 471 $('#freeze-height').remove(); 472}; 473 474/** 475 * Encodes a Drupal path for use in a URL. 476 * 477 * For aesthetic reasons slashes are not escaped. 478 */ 479Drupal.encodePath = function (item, uri) { 480 uri = uri || location.href; 481 return encodeURIComponent(item).replace(/%2F/g, '/'); 482}; 483 484/** 485 * Get the text selection in a textarea. 486 */ 487Drupal.getSelection = function (element) { 488 if (typeof element.selectionStart != 'number' && document.selection) { 489 // The current selection. 490 var range1 = document.selection.createRange(); 491 var range2 = range1.duplicate(); 492 // Select all text. 493 range2.moveToElementText(element); 494 // Now move 'dummy' end point to end point of original range.
495 range2.setEndPoint('EndToEnd', range1); 496 // Now we can calculate start and end points. 497 var start = range2.text.length - range1.text.length; 498 var end = start + range1.text.length; 499 return { 'start': start, 'end': end }; 500 } 501 return { 'start': element.selectionStart, 'end': element.selectionEnd }; 502}; 503 504/** 505 * Add a global variable which determines if the window is being unloaded. 506 * 507 * This is primarily used by Drupal.displayAjaxError(). 508 */ 509Drupal.beforeUnloadCalled = false; 510$(window).bind('beforeunload pagehide', function () { 511 Drupal.beforeUnloadCalled = true; 512}); 513 514/** 515 * Displays a JavaScript error from an Ajax response when appropriate to do so. 516 */ 517Drupal.displayAjaxError = function (message) { 518 // Skip displaying the message if the user deliberately aborted (for example, 519 // by reloading the page or navigating to a different page) while the Ajax 520 // request was still ongoing. See, for example, the discussion at 521 // http://stackoverflow.com/questions/699941/handle-ajax-error-when-a-user-clicks-refresh. 522 if (!Drupal.beforeUnloadCalled) { 523 alert(message); 524 } 525}; 526 527/** 528 * Build an error message from an Ajax response. 529 */ 530Drupal.ajaxError = function (xmlhttp, uri, customMessage) { 531 var statusCode, statusText, pathText, responseText, readyStateText, message; 532 if (xmlhttp.status) { 533 statusCode = "\n" + Drupal.t("An AJAX HTTP error occurred.") + "\n" + Drupal.t("HTTP Result Code: !status", {'!status': xmlhttp.status}); 534 } 535 else { 536 statusCode = "\n" + Drupal.t("An AJAX HTTP request terminated abnormally."); 537 } 538 statusCode += "\n" + Drupal.t("Debugging information follows."); 539 pathText = "\n" + Drupal.t("Path: !uri", {'!uri': uri} ); 540 statusText = ''; 541 // In some cases, when statusCode == 0, xmlhttp.statusText may not be defined. 542 // Unfortunately, testing for it with typeof, etc, doesn't seem to catch that 543 // and the test causes an exception. So we need to catch the exception here. 544 try { 545 statusText = "\n" + Drupal.t("StatusText: !statusText", {'!statusText': $.trim(xmlhttp.statusText)}); 546 } 547 catch (e) {} 548 549 responseText = ''; 550 // Again, we don't have a way to know for sure whether accessing 551 // xmlhttp.responseText is going to throw an exception. So we'll catch it. 552 try { 553 responseText = "\n" + Drupal.t("ResponseText: !responseText", {'!responseText': $.trim(xmlhttp.responseText) } ); 554 } catch (e) {} 555 556 // Make the responseText more readable by stripping HTML tags and newlines. 557 responseText = responseText.replace(/<("[^"]*"|'[^']*'|[^'">])*>/gi,""); 558 responseText = responseText.replace(/[\n]+\s+/g,"\n"); 559 560 // We don't need readyState except for status == 0. 561 readyStateText = xmlhttp.status == 0 ? ("\n" + Drupal.t("ReadyState: !readyState", {'!readyState': xmlhttp.readyState})) : ""; 562 563 // Additional message beyond what the xmlhttp object provides. 564 customMessage = customMessage ? ("\n" + Drupal.t("CustomMessage: !customMessage", {'!customMessage': customMessage})) : ""; 565 566 message = statusCode + pathText + statusText + customMessage + responseText + readyStateText; 567 return message; 568}; 569 570// Class indicating that JS is enabled; used for styling purpose. 571$('html').addClass('js'); 572 573// 'js enabled' cookie. 574document.cookie = 'has_js=1; path=/'; 575 576/** 577 * Additions to jQuery.support. 578 */ 579$(function () { 580 /** 581 * Boolean indicating whether or not position:fixed is supported. 582 */ 583 if (jQuery.support.positionFixed === undefined) { 584 var el = $('<div style="position:fixed; top:10px" />').appendTo(document.body); 585 jQuery.support.positionFixed = el[0].offsetTop === 10; 586 el.remove(); 587 } 588}); 589 590//Attach all behaviors. 591$(function () { 592 Drupal.attachBehaviors(document, Drupal.settings); 593}); 594 595/** 596 * The default themes. 597 */ 598Drupal.theme.prototype = { 599 600 /** 601 * Formats text for emphasized display in a placeholder inside a sentence. 602 * 603 * @param str 604 * The text to format (plain-text). 605 * @return 606 * The formatted text (html). 607 */ 608 placeholder: function (str) { 609 return '<em class="placeholder">' + Drupal.checkPlain(str) + '</em>'; 610 } 611}; 612 613})(jQuery);
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.