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