1/** 2 * For jQuery versions less than 3.4.0, this replaces the jQuery.extend 3 * function with the one from jQuery 3.4.0, slightly modified (documented 4 * below) to be compatible with older jQuery versions and browsers. 5 * 6 * This provides the Object.prototype pollution vulnerability fix to Drupal 7 * installations running older jQuery versions, including the versions shipped 8 * with Drupal core and https://www.drupal.org/project/jquery_update. 9 * 10 * @see https://github.com/jquery/jquery/pull/4333 11 */ 12 13(function (jQuery) { 14 15// Do not override jQuery.extend() if the jQuery version is already >=3.4.0. 16var versionParts = jQuery.fn.jquery.split('.'); 17var majorVersion = parseInt(versionParts[0]); 18var minorVersion = parseInt(versionParts[1]); 19var patchVersion = parseInt(versionParts[2]); 20var isPreReleaseVersion = (patchVersion.toString() !== versionParts[2]); 21if ( 22 (majorVersion > 3) || 23 (majorVersion === 3 && minorVersion > 4) || 24 (majorVersion === 3 && minorVersion === 4 && patchVersion > 0) || 25 (majorVersion === 3 && minorVersion === 4 && patchVersion === 0 && !isPreReleaseVersion) 26) { 27 return; 28} 29 30/** 31 * This is almost verbatim copied from jQuery 3.4.0. 32 * 33 * Only two minor changes have been made: 34 * - The call to isFunction() is changed to jQuery.isFunction(). 35 * - The two calls to Array.isArray() is changed to jQuery.isArray(). 36 * 37 * The above two changes ensure compatibility with all older jQuery versions 38 * (1.4.4 - 3.3.1) and older browser versions (e.g., IE8). 39 */ 40jQuery.extend = jQuery.fn.extend = function() { 41 var options, name, src, copy, copyIsArray, clone, 42 target = arguments[ 0 ] || {}, 43 i = 1, 44 length = arguments.length, 45 deep = false; 46 47 // Handle a deep copy situation 48 if ( typeof target === "boolean" ) { 49 deep = target; 50 51 // Skip the boolean and the target 52 target = arguments[ i ] || {}; 53 i++; 54 } 55 56 // Handle case when target is a string or something (possible in deep copy) 57 if ( typeof target !== "object" && !jQuery.isFunction( target ) ) { 58 target = {}; 59 } 60 61 // Extend jQuery itself if only one argument is passed 62 if ( i === length ) { 63 target = this; 64 i--; 65 } 66 67 for ( ; i < length; i++ ) { 68 69 // Only deal with non-null/undefined values 70 if ( ( options = arguments[ i ] ) != null ) { 71 72 // Extend the base object 73 for ( name in options ) { 74 copy = options[ name ]; 75 76 // Prevent Object.prototype pollution 77 // Prevent never-ending loop 78 if ( name === "__proto__" || target === copy ) { 79 continue; 80 } 81 82 // Recurse if we're merging plain objects or arrays 83 if ( deep && copy && ( jQuery.isPlainObject( copy ) || 84 ( copyIsArray = jQuery.isArray( copy ) ) ) ) { 85 src = target[ name ]; 86 87 // Ensure proper type for the source value 88 if ( copyIsArray && !jQuery.isArray( src ) ) { 89 clone = []; 90 } else if ( !copyIsArray && !jQuery.isPlainObject( src ) ) { 91 clone = {}; 92 } else { 93 clone = src; 94 } 95 copyIsArray = false; 96 97 // Never move original objects, clone them 98 target[ name ] = jQuery.extend( deep, clone, copy ); 99 100 // Don't bring in undefined values 101 } else if ( copy !== undefined ) { 102 target[ name ] = copy; 103 } 104 } 105 } 106 } 107 108 // Return the modified object 109 return target; 110}; 111 112})(jQuery); 113; 114/** 115 * For jQuery versions less than 3.5.0, this replaces the jQuery.htmlPrefilter() 116 * function with one that fixes these security vulnerabilities while also 117 * retaining the pre-3.5.0 behavior where it's safe to do so. 118 * - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-11022 119 * - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-11023 120 * 121 * Additionally, for jQuery versions that do not have a jQuery.htmlPrefilter() 122 * function (1.x prior to 1.12 and 2.x prior to 2.2), this adds it, and 123 * extends the functions that need to call it to do so. 124 * 125 * Drupal core's jQuery version is 1.4.4, but jQuery Update can provide a 126 * different version, so this covers all versions between 1.4.4 and 3.4.1. 127 * The GitHub links in the code comments below link to jQuery 1.5 code, be
127cause 128 * 1.4.4 isn't on GitHub, but the referenced code didn't change from 1.4.4 to 129 * 1.5. 130 */ 131 132(function (jQuery) { 133 134 // Parts of this backport differ by jQuery version. 135 var versionParts = jQuery.fn.jquery.split('.'); 136 var majorVersion = parseInt(versionParts[0]); 137 var minorVersion = parseInt(versionParts[1]); 138 139 // No backport is needed if we're already on jQuery 3.5 or higher. 140 if ( (majorVersion > 3) || (majorVersion === 3 && minorVersion >= 5) ) { 141 return; 142 } 143 144 // Prior to jQuery 3.5, jQuery converted XHTML-style self-closing tags to 145 // their XML equivalent: e.g., "<div />" to "<div></div>". This is 146 // problematic for several reasons, including that it's vulnerable to XSS 147 // attacks. However, since this was jQuery's behavior for many years, many 148 // Drupal modules and jQuery plugins may be relying on it. Therefore, we 149 // preserve that behavior, but for a limited set of tags only, that we believe 150 // to not be vulnerable. This is the set of HTML tags that satisfy all of the 151 // following conditions: 152 // - In DOMPurify's list of HTML tags. If an HTML tag isn't safe enough to 153 // appear in that list, then we don't want to mess with it here either. 154 // @see https://github.com/cure53/DOMPurify/blob/2.0.11/dist/purify.js#L128 155 // - A normal element (not a void, template, text, or foreign element). 156 // @see https://html.spec.whatwg.org/multipage/syntax.html#elements-2 157 // - An element that is still defined by the current HTML specification 158 // (not a deprecated element), because we do not want to rely on how 159 // browsers parse deprecated elements. 160 // @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element 161 // - Not 'html', 'head', or 'body', because this pseudo-XHTML expansion is 162 // designed for fragments, not entire documents. 163 // - Not 'colgroup', because due to an idiosyncrasy of jQuery's original 164 // regular expression, it didn't match on colgroup, and we don't want to 165 // introduce a behavior change for that. 166 var selfClosingTagsToReplace = [ 167 'a', 'abbr', 'address', 'article', 'aside', 'audio', 'b', 'bdi', 'bdo', 168 'blockquote', 'button', 'canvas', 'caption', 'cite', 'code', 'data', 169 'datalist', 'dd', 'del', 'details', 'dfn', 'div', 'dl', 'dt', 'em', 170 'fieldset', 'figcaption', 'figure', 'footer', 'form', 'h1', 'h2', 'h3', 171 'h4', 'h5', 'h6', 'header', 'hgroup', 'i', 'ins', 'kbd', 'label', 'legend', 172 'li', 'main', 'map', 'mark', 'menu', 'meter', 'nav', 'ol', 'optgroup', 173 'option', 'output', 'p', 'picture', 'pre', 'progress', 'q', 'rp', 'rt', 174 'ruby', 's', 'samp', 'section', 'select', 'small', 'source', 'span', 175 'strong', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'tfoot', 'th', 176 'thead', 'time', 'tr', 'u', 'ul', 'var', 'video' 177 ]; 178 179 // Define regular expressions for <TAG/> and <TAG ATTRIBUTES/>. Doing this as 180 // two expressions makes it easier to target <a/> without also targeting 181 // every tag that starts with "a". 182 var xhtmlRegExpGroup = '(' + selfClosingTagsToReplace.join('|') + ')'; 183 var whitespace = '[\\x20\\t\\r\\n\\f]'; 184 var rxhtmlTagWithoutSpaceOrAttributes = new RegExp('<' + xhtmlRegExpGroup + '\\/>', 'gi'); 185 var rxhtmlTagWithSpaceAndMaybeAttributes = new RegExp('<' + xhtmlRegExpGroup + '(' + whitespace + '[^>]*)\\/>', 'gi'); 186 187 // jQuery 3.5 also fixed a vulnerability for when </select> appears within 188 // an <option> or <optgroup>, but it did that in local code that we can't 189 // backport directly. Instead, we filter such cases out. To do so, we need to 190 // determine when jQuery would otherwise invoke the vulnerable code, which it 191 // uses this regular expression to determine. The regular expression changed 192 // for version 3.0.0 and changed again for 3.4.0. 193 // @see https://github.com/jquery/jquery/blob/1.5/jquery.js#L4958 194 // @see https://github.com/jquery/jquery/blob/3.0.0/dist/jquery.js#L4584 195 // @see https://github.com/jquery/jquery/blob/3.4.0/dist/jquery.js#L4712 196 var rtagName; 197 if (majorVersion < 3) { 198 rtagName = /<([\w:]+)/; 199 } 200 else if (minorVersion < 4) { 201 rtagName = /<([a-z][^\/\0>\x20\t\r\n\f]+)/i; 202 } 203 else { 204 rtagName = /<([a-z][^\/\0>\x20\t\r\n\f]*)/i; 205 } 206 207 // The regular expression that jQuery uses to determine which self-closing 208 // tags to expand to open and close tags. This is vulnerable, because it 209 // matches all tag names except the few excluded ones. We only use this 210 // expression for determining vulnerability. The expression changed for 211 // version 3, but we only need to check for vulnerability in versions 1 and 2, 212 // so we use the expression from those versions. 213 // @see https://github.com/jquery/jquery/blob/1.5/jquery.js#L4957 214 var rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi; 215 216 jQuery.extend({ 217 htmlPrefilter: function (html) { 218 // This is how jQuery determines the first tag in the HTML. 219 // @see https://github.com/jquery/jquery/blob/1.5/jquery.js#L5521 220 var tag = ( rtagName.exec( html ) || [ "", "" ] )[ 1 ].toLowerCase(); 221
222 // It is not valid HTML for <option> or <optgroup> to have <select> as 223 // either a descendant or sibling, and attempts to inject one can cause 224 // XSS on jQuery versions before 3.5. Since this is invalid HTML and a 225 // possible XSS attack, reject the entire string. 226 // @see https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-11023 227 if ((tag === 'option' || tag === 'optgroup') && html.match(/<\/?select/i)) { 228 html = ''; 229 } 230 231 // Retain jQuery's prior to 3.5 conversion of pseudo-XHTML, but for only 232 // the tags in the `selfClosingTagsToReplace` list defined above. 233 // @see https://github.com/jquery/jquery/blob/1.5/jquery.js#L5518 234 // @see https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-11022 235 html = html.replace(rxhtmlTagWithoutSpaceOrAttributes, "<$1></$1>"); 236 html = html.replace(rxhtmlTagWithSpaceAndMaybeAttributes, "<$1$2></$1>"); 237 238 // Prior to jQuery 1.12 and 2.2, this function gets called (via code later 239 // in this file) in addition to, rather than instead of, the unsafe 240 // expansion of self-closing tags (including ones not in the list above). 241 // We can't prevent that unsafe expansion from running, so instead we 242 // check to make sure that it doesn't affect the DOM returned by the 243 // browser's parsing logic. If it does affect it, then it's vulnerable to 244 // XSS, so we reject the entire string. 245 if ( (majorVersion === 1 && minorVersion < 12) || (majorVersion === 2 && minorVersion < 2) ) { 246 var htmlRisky = html.replace(rxhtmlTag, "<$1></$2>"); 247 if (htmlRisky !== html) { 248 // Even though htmlRisky and html are different strings, they might 249 // represent the same HTML structure once parsed, in which case, 250 // htmlRisky is actually safe. We can ask the browser to parse both 251 // to find out, but the browser can't parse table fragments (e.g., a 252 // root-level "<td>"), so we need to wrap them. We just need this 253 // technique to work on all supported browsers; we don't need to 254 // copy from the specific jQuery version we're using. 255 // @see https://github.com/jquery/jquery/blob/3.5.1/dist/jquery.js#L4939 256 var wrapMap = { 257 thead: [ 1, "<table>", "</table>" ], 258 col: [ 2, "<table><colgroup>", "</colgroup></table>" ], 259 tr: [ 2, "<table><tbody>", "</tbody></table>" ], 260 td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ], 261 }; 262 wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; 263 wrapMap.th = wrapMap.td; 264 265 // Function to wrap HTML into something that a browser can parse. 266 // @see https://github.com/jquery/jquery/blob/3.5.1/dist/jquery.js#L5032 267 var getWrappedHtml = function (html) { 268 var wrap = wrapMap[tag]; 269 if (wrap) { 270 html = wrap[1] + html + wrap[2]; 271 } 272 return html; 273 }; 274 275 // Function to return canonical HTML after parsing it. This parses 276 // only; it doesn't execute scripts. 277 // @see https://github.com/jquery/jquery-migrate/blob/3.3.0/src/jquery/manipulation.js#L5 278 var getParsedHtml = function (html) { 279 var doc = window.document.implementation.createHTMLDocument( "" ); 280 doc.body.innerHTML = html; 281 return doc.body ? doc.body.innerHTML : ''; 282 }; 283 284 // If the browser couldn't parse either one successfully, or if 285 // htmlRisky parses differently than html, then html is vulnerable, 286 // so reject it. 287 var htmlParsed = getParsedHtml(getWrappedHtml(html)); 288 var htmlRiskyParsed = getParsedHtml(getWrappedHtml(htmlRisky)); 289 if (htmlRiskyParsed === '' || htmlParsed === '' || (htmlRiskyParsed !== htmlParsed)) { 290 html = ''; 291 } 292 } 293 } 294 295 return html; 296 } 297 }); 298 299 // Prior to jQuery 1.12 and 2.2, jQuery.clean(), jQuery.buildFragment(), and 300 // jQuery.fn.html() did not call jQuery.htmlPrefilter(), so we add that. 301 if ( (majorVersion === 1 && minorVersion < 12) || (majorVersion === 2 && minorVersion < 2) ) { 302 // Filter the HTML coming into jQuery.fn.html(). 303 var fnOriginalHtml = jQuery.fn.html; 304 jQuery.fn.extend({ 305 // @see https://github.com/jquery/jquery/blob/1.5/jquery.js#L5147 306 html: function (value) { 307 if (typeof value === "string") { 308 value = jQuery.htmlPrefilter(value); 309 } 310 // .html() can be called as a setter (with an argument) or as a getter 311 // (without an argument), so invoke fnOriginalHtml() the same way that 312 // we were invoked. 313 return fnOriginalHtml.apply(this, arguments.length ? [value] : []); 314 } 315 }); 316 317 // The regular expression that jQuery uses to determine if a string is HTML. 318 // Used by both clean() and buildFragment(). 319 // @see https://github.com/jquery/jquery/blob/1.5/jquery.js#L4960
320 var rhtml = /<|&#?\w+;/; 321 322 // Filter HTML coming into: 323 // - jQuery.clean() for versions prior to 1.9. 324 // - jQuery.buildFragment() for 1.9 and above. 325 // 326 // The looping constructs in the two functions might be essentially 327 // identical, but they're each expressed here in the way that most closely 328 // matches their original expression in jQuery, so that we filter all of 329 // the items and only the items that jQuery will treat as HTML strings. 330 if (majorVersion === 1 && minorVersion < 9) { 331 var originalClean = jQuery.clean; 332 jQuery.extend({ 333 // @see https://github.com/jquery/jquery/blob/1.5/jquery.js#L5493 334 'clean': function (elems, context, fragment, scripts) { 335 for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) { 336 if ( typeof elem === "string" && rhtml.test( elem ) ) { 337 elems[i] = elem = jQuery.htmlPrefilter(elem); 338 } 339 } 340 return originalClean.call(this, elems, context, fragment, scripts); 341 } 342 }); 343 } 344 else { 345 var originalBuildFragment = jQuery.buildFragment; 346 jQuery.extend({ 347 // @see https://github.com/jquery/jquery/blob/1.9.0/jquery.js#L6419 348 'buildFragment': function (elems, context, scripts, selection) { 349 var l = elems.length; 350 for ( var i = 0; i < l; i++ ) { 351 var elem = elems[i]; 352 if (elem || elem === 0) { 353 if ( jQuery.type( elem ) !== "object" && rhtml.test( elem ) ) { 354 elems[i] = elem = jQuery.htmlPrefilter(elem); 355 } 356 } 357 } 358 return originalBuildFragment.call(this, elems, context, scripts, selection); 359 } 360 }); 361 } 362 } 363 364})(jQuery); 365; 366 367/** 368 * jQuery Once Plugin v1.2 369 * http://plugins.jquery.com/project/once 370 * 371 * Dual licensed under the MIT and GPL licenses: 372 * http://www.opensource.org/licenses/mit-license.php 373 * http://www.gnu.org/licenses/gpl.html 374 */ 375 376(function ($) { 377 var cache = {}, uuid = 0; 378 379 /** 380 * Filters elements by whether they have not yet been processed. 381 * 382 * @param id 383 * (Optional) If this is a string, then it will be used as the CSS class 384 * name that is applied to the elements for determining whether it has 385 * already been processed. The elements will get a class in the form of 386 * "id-processed". 387 * 388 * If the id parameter is a function, it will be passed off to the fn 389 * parameter and the id will become a unique identifier, represented as a 390 * number. 391 * 392 * When the id is neither a string or a function, it becomes a unique 393 * identifier, depicted as a number. The element's class will then be 394 * represented in the form of "jquery-once-#-processed". 395 * 396 * Take note that the id must be valid for usage as an element's class name. 397 * @param fn 398 * (Optional) If given, this function will be called for each element that 399 * has not yet been processed. The function's return value follows the same 400 * logic as $.each(). Returning true will continue to the next matched 401 * element in the set, while returning false will entirely break the 402 * iteration. 403 */ 404 $.fn.once = function (id, fn) { 405 if (typeof id != 'string') { 406 // Generate a numeric ID if the id passed can't be used as a CSS class. 407 if (!(id in cache)) { 408 cache[id] = ++uuid; 409 } 410 // When the fn parameter is not passed, we interpret it from the id. 411 if (!fn) { 412 fn = id; 413 } 414 id = 'jquery-once-' + cache[id]; 415 } 416 // Remove elements from the set that have already been processed. 417 var name = id + '-processed'; 418 var elements = this.not('.' + name).addClass(name); 419 420 return $.isFunction(fn) ? elements.each(fn) : elements; 421 }; 422 423 /** 424 * Filters elements that have been processed once already. 425 * 426 * @param id 427 * A required string representing the name of the class which should be used 428 * when filtering the elements. This only filters elements that have already 429 * been processed by the once function. The id should be the same id that 430 * was originally passed to the once() function. 431 * @param fn 432 * (Optional) If given, this function will be called for each element that 433 * has not yet been processed. The function's return value follows the same 434 * logic as $.each(). Returning true will continue to the next matched 435 * element in the set, while returning false will entirely break the 436 * iteration. 437 */ 438 $.fn.removeOnce = function (id, fn) { 439 var name = id + '-processed'; 440 var elements = this.filter('.' + name).removeClass(name); 441 442 return $.isFunction(fn) ? elements.each(fn) : elements; 443 }; 444})(jQuery); 445; 446 447var Drupal = Drupal || { 'settings': {}, 'behaviors': {}, 'locale': {} }; 448 449// Allow other JavaScript libraries to use $. 450jQuery.noConflict(); 451 452(function ($) { 453 454/** 455 * Override jQuery.fn.init to guard against XSS attacks. 456 * 457 * See http://bugs.jquery.com/ticket/9521 458 */ 459var jquery_init = $.fn.init; 460$.fn.init = function (selector, context, rootjQuery) { 461 // If the string contains a "#" before a "<", treat it as invali
461d HTML. 462 if (selector && typeof selector === 'string') { 463 var hash_position = selector.indexOf('#'); 464 if (hash_position >= 0) { 465 var bracket_position = selector.indexOf('<'); 466 if (bracket_position > hash_position) { 467 throw 'Syntax error, unrecognized expression: ' + selector; 468 } 469 } 470 } 471 return jquery_init.call(this, selector, context, rootjQuery); 472}; 473$.fn.init.prototype = jquery_init.prototype; 474 475/** 476 * Pre-filter Ajax requests to guard against XSS attacks. 477 * 478 * See https://github.com/jquery/jquery/issues/2432 479 */ 480if ($.ajaxPrefilter) { 481 // For newer versions of jQuery, use an Ajax prefilter to prevent 482 // auto-executing script tags from untrusted domains. This is similar to the 483 // fix that is built in to jQuery 3.0 and higher. 484 $.ajaxPrefilter(function (s) { 485 if (s.crossDomain) { 486 s.contents.script = false; 487 } 488 }); 489} 490else if ($.httpData) { 491 // For the version of jQuery that ships with Drupal core, override 492 // jQuery.httpData to prevent auto-detecting "script" data types from 493 // untrusted domains. 494 var jquery_httpData = $.httpData; 495 $.httpData = function (xhr, type, s) { 496 // @todo Consider backporting code from newer jQuery versions to check for 497 // a cross-domain request here, rather than using Drupal.urlIsLocal() to 498 // block scripts from all URLs that are not on the same site. 499 if (!type && !Drupal.urlIsLocal(s.url)) { 500 var content_type = xhr.getResponseHeader('content-type') || ''; 501 if (content_type.indexOf('javascript') >= 0) { 502 // Default to a safe data type. 503 type = 'text'; 504 } 505 } 506 return jquery_httpData.call(this, xhr, type, s); 507 }; 508 $.httpData.prototype = jquery_httpData.prototype; 509} 510 511/** 512 * Attach all registered behaviors to a page element. 513 * 514 * Behaviors are event-triggered actions that attach to page elements, enhancing 515 * default non-JavaScript UIs. Behaviors are registered in the Drupal.behaviors 516 * object using the method 'attach' and optionally also 'detach' as follows: 517 * @code 518 * Drupal.behaviors.behaviorName = { 519 * attach: function (context, settings) { 520 * ... 521 * }, 522 * detach: function (context, settings, trigger) { 523 * ... 524 * } 525 * }; 526 * @endcode 527 * 528 * Drupal.attachBehaviors is added below to the jQuery ready event and so 529 * runs on initial page load. Developers implementing AHAH/Ajax in their 530 * solutions should also call this function after new page content has been 531 * loaded, feeding in an element to be processed, in order to attach all 532 * behaviors to the new content. 533 * 534 * Behaviors should use 535 * @code 536 * $(selector).once('behavior-name', function () { 537 * ... 538 * }); 539 * @endcode 540 * to ensure the behavior is attached only once to a given element. (Doing so 541 * enables the reprocessing of given elements, which may be needed on occasion 542 * despite the ability to limit behavior attachment to a particular element.) 543 * 544 * @param context 545 * An element to attach behaviors to. If none is given, the document element 546 * is used. 547 * @param settings 548 * An object containing settings for the current context. If none given, the 549 * global Drupal.settings object is used. 550 */ 551Drupal.attachBehaviors = function (context, settings) { 552 context = context || document; 553 settings = settings || Drupal.settings; 554 // Execute all of them. 555 $.each(Drupal.behaviors, function () { 556 if ($.isFunction(this.attach)) { 557 this.attach(context, settings); 558 } 559 }); 560}; 561 562/** 563 * Detach registered behaviors from a page element. 564 * 565 * Developers implementing AHAH/Ajax in their solutions should call this 566 * function before page content is about to be removed, feeding in an element 567 * to be processed, in order to allow special behaviors to detach from the 568 * content. 569 * 570 * Such implementations should look for the class name that was added in their 571 * corresponding Drupal.behaviors.behaviorName.attach implementation, i.e. 572 * behaviorName-processed, to ensure the behavior is detached only from 573 * previously processed elements. 574 * 575 * @param context 576 * An element to detach behaviors from. If none is given, the document element 577 * is used. 578 * @param settings
579 * An object containing settings for the current context. If none given, the 580 * global Drupal.settings object is used. 581 * @param trigger 582 * A string containing what's causing the behaviors to be detached. The 583 * possible triggers are: 584 * - unload: (default) The context element is being removed from the DOM. 585 * - move: The element is about to be moved within the DOM (for example, 586 * during a tabledrag row swap). After the move is completed, 587 * Drupal.attachBehaviors() is called, so that the behavior can undo 588 * whatever it did in response to the move. Many behaviors won't need to 589 * do anything simply in response to the element being moved, but because 590 * IFRAME elements reload their "src" when being moved within the DOM, 591 * behaviors bound to IFRAME elements (like WYSIWYG editors) may need to 592 * take some action. 593 * - serialize: When an Ajax form is submitted, this is called with the 594 * form as the context. This provides every behavior within the form an 595 * opportunity to ensure that the field elements have correct content 596 * in them before the form is serialized. The canonical use-case is so 597 * that WYSIWYG editors can update the hidden textarea to which they are 598 * bound. 599 * 600 * @see Drupal.attachBehaviors 601 */ 602Drupal.detachBehaviors = function (context, settings, trigger) { 603 context = context || document; 604 settings = settings || Drupal.settings; 605 trigger = trigger || 'unload'; 606 // Execute all of them. 607 $.each(Drupal.behaviors, function () { 608 if ($.isFunction(this.detach)) { 609 this.detach(context, settings, trigger); 610 } 611 }); 612}; 613 614/** 615 * Encode special characters in a plain-text string for display as HTML. 616 * 617 * @ingroup sanitization 618 */ 619Drupal.checkPlain = function (str) { 620 var character, regex, 621 replace = { '&': '&', "'": ''', '"': '"', '<': '<', '>': '>' }; 622 str = String(str); 623 for (character in replace) { 624 if (replace.hasOwnProperty(character)) { 625 regex = new RegExp(character, 'g'); 626 str = str.replace(regex, replace[character]); 627 } 628 } 629 return str; 630}; 631 632/** 633 * Replace placeholders with sanitized values in a string. 634 * 635 * @param str 636 * A string with placeholders. 637 * @param args 638 * An object of replacements pairs to make. Incidences of any key in this 639 * array are replaced with the corresponding value. Based on the first 640 * character of the key, the value is escaped and/or themed: 641 * - !variable: inserted as is 642 * - @variable: escape plain text to HTML (Drupal.checkPlain) 643 * - %variable: escape text and theme as a placeholder for user-submitted 644 * content (checkPlain + Drupal.theme('placeholder')) 645 * 646 * @see Drupal.t() 647 * @ingroup sanitization 648 */ 649Drupal.formatString = function(str, args) { 650 // Transform arguments before inserting them. 651 for (var key in args) { 652 if (args.hasOwnProperty(key)) { 653 switch (key.charAt(0)) { 654 // Escaped only. 655 case '@': 656 args[key] = Drupal.checkPlain(args[key]); 657 break; 658 // Pass-through. 659 case '!': 660 break; 661 // Escaped and placeholder. 662 default: 663 args[key] = Drupal.theme('placeholder', args[key]); 664 break; 665 } 666 } 667 } 668 669 return Drupal.stringReplace(str, args, null); 670}; 671 672/** 673 * Replace substring. 674 * 675 * The longest keys will be tried first. Once a substring has been replaced, 676 * its new value will not be searched again. 677 * 678 * @param {String} str 679 * A string with placeholders. 680 * @param {Object} args 681 * Key-value pairs. 682 * @param {Array|null} keys 683 * Array of keys from the "args". Internal use only. 684 * 685 * @return {String} 686 * Returns the replaced string. 687 */ 688Drupal.stringReplace = function (str, args, keys) { 689 if (str.length === 0) { 690 return str; 691 } 692 693 // If the array of keys is not passed then collect the keys from the args. 694 if (!$.isArray(keys)) { 695 keys = []; 696 for (var k in args) { 697 if (args.hasOwnProperty(k)) { 698 keys.push(k); 699 } 700 } 701 702 // Order the keys by the character length. The shortest one is the first. 703 keys.sort(function (a, b) { return a.length - b.length; }); 704 } 705 706 if (keys.length === 0) { 707 return str; 708 } 709 710 // Take next longest one from the end. 711 var key = keys.pop(); 712 var fragments = str.split(key); 713 714 if (keys.length) { 715 for (var i = 0; i < fragments.length; i++) { 716 // Process each fragment with a copy of remaining keys. 717 fragments[i] = Drupal.stringReplace(fragments[i], args, keys.slice(0)); 718 } 719 } 720 721 return fragments.join(args[key]); 722}; 723 724/** 725 * Translate strings to the page language or a given language. 726 * 727 * See the documentation of the server-side t() function for further details. 728 * 729 * @param str 730 * A string containing the English string to translate. 731 * @param args 732 * An object of replacements pairs to make after translation. Incidences 733 * of any key in this array are replaced with the corresponding value. 734 * See Drupal.formatString(). 735 * 736 * @param options 737 * - 'context' (defaults to the empty context): The context the source string 738 * belongs to. 739 * 740 * @return 741 * The translated string. 742 */ 743Drupal.t = function (str, args, options) { 744 options = options || {}; 745 options.context = options.context || ''; 746 747 // Fetch the localized version of the string. 748 if (Drupal.locale.strings && Drupal.locale.strings[options.context] && Drupal.locale.strings[options.context][str]) { 749 str = Drupal.locale.strings[options.context][str]; 750 } 751 752 if (args) { 753 str = Drupal.formatString(str, args); 754 } 755 return str; 756}; 757 758/** 759 * Format a string containing a count of items. 760 * 761 * This function ensures that the string is pluralized correctly. Since Drupal.t() is 762 * called by this function, make sure not to pass already-localized strings to it. 763 * 764 * See the documentation of the server-side format_plural() function for further details. 765 * 766 * @param count 767 * The item count to display. 768 * @param singular 769 * The string for the singular case. Please make sure it is clear this is 770 * singular, to ease translation (e.g. use "1 new comment" instead of "1 new"). 771 * Do not use @count in the singular string. 772 * @param plural 773 * The string for the plural case. Please make sure it is clear this is plural, 774 * to ease translation. Use @count in place of the item count, as in "@count 775 * new comments". 776 * @param args 777 * An object of replacements pairs to make after translation. Incidences 778 * of any key in this array are replaced with the corresponding value. 779 * See Drupal.formatString(). 780 * Note that you do not need to include @count in this array.
781 * This replacement is done automatically for the plural case. 782 * @param options 783 * The options to pass to the Drupal.t() function. 784 * @return 785 * A translated string. 786 */ 787Drupal.formatPlural = function (count, singular, plural, args, options) { 788 args = args || {}; 789 args['@count'] = count; 790 // Determine the index of the plural form. 791 var index = Drupal.locale.pluralFormula ? Drupal.locale.pluralFormula(args['@count']) : ((args['@count'] == 1) ? 0 : 1); 792 793 if (index == 0) { 794 return Drupal.t(singular, args, options); 795 } 796 else if (index == 1) { 797 return Drupal.t(plural, args, options); 798 } 799 else { 800 args['@count[' + index + ']'] = args['@count']; 801 delete args['@count']; 802 return Drupal.t(plural.replace('@count', '@count[' + index + ']'), args, options); 803 } 804}; 805 806/** 807 * Returns the passed in URL as an absolute URL. 808 * 809 * @param url 810 * The URL string to be normalized to an absolute URL. 811 * 812 * @return 813 * The normalized, absolute URL. 814 * 815 * @see https://github.com/angular/angular.js/blob/v1.4.4/src/ng/urlUtils.js 816 * @see https://grack.com/blog/2009/11/17/absolutizing-url-in-javascript 817 * @see https://github.com/jquery/jquery-ui/blob/1.11.4/ui/tabs.js#L53 818 */ 819Drupal.absoluteUrl = function (url) { 820 var urlParsingNode = document.createElement('a'); 821 822 // Decode the URL first; this is required by IE <= 6. Decoding non-UTF-8 823 // strings may throw an exception. 824 try { 825 url = decodeURIComponent(url); 826 } catch (e) {} 827 828 urlParsingNode.setAttribute('href', url); 829 830 // IE <= 7 normalizes the URL when assigned to the anchor node similar to 831 // the other browsers. 832 return urlParsingNode.cloneNode(false).href; 833}; 834 835/** 836 * Returns true if the URL is within Drupal's base path. 837 * 838 * @param url 839 * The URL string to be tested. 840 * 841 * @return 842 * Boolean true if local. 843 * 844 * @see https://github.com/jquery/jquery-ui/blob/1.11.4/ui/tabs.js#L58 845 */ 846Drupal.urlIsLocal = function (url) { 847 // Always use browser-derived absolute URLs in the comparison, to avoid 848 // attempts to break out of the base path using directory traversal. 849 var absoluteUrl = Drupal.absoluteUrl(url); 850 var protocol = location.protocol; 851 852 // Consider URLs that match this site's base URL but use HTTPS instead of HTTP 853 // as local as well. 854 if (protocol === 'http:' && absoluteUrl.indexOf('https:') === 0) { 855 protocol = 'https:'; 856 } 857 var baseUrl = protocol + '//' + location.host + Drupal.settings.basePath.slice(0, -1); 858 859 // Decoding non-UTF-8 strings may throw an exception. 860 try { 861 absoluteUrl = decodeURIComponent(absoluteUrl); 862 } catch (e) {} 863 try { 864 baseUrl = decodeURIComponent(baseUrl); 865 } catch (e) {} 866 867 // The given URL matches the site's base URL, or has a path under the site's 868 // base URL. 869 return absoluteUrl === baseUrl || absoluteUrl.indexOf(baseUrl + '/') === 0; 870}; 871 872/** 873 * Sanitizes a URL for use with jQuery.ajax(). 874 * 875 * @param url 876 * The URL string to be sanitized. 877 * 878 * @return 879 * The sanitized URL. 880 */ 881Drupal.sanitizeAjaxUrl = function (url) { 882 var regex = /\=\?(&|$)/; 883 while (url.match(regex)) { 884 url = url.replace(regex, ''); 885 } 886 return url; 887} 888 889/** 890 * Generate the themed representation of a Drupal object. 891 * 892 * All requests for themed output must go through this function. It examines 893 * the request and routes it to the appropriate theme function. If the current 894 * theme does not provide an override function, the generic theme function is 895 * called. 896 * 897 * For example, to retrieve the HTML for text that should be emphasized and 898 * displayed as a placeholder inside a sentence, call 899 * Drupal.theme('placeholder', text). 900 * 901 * @param func 902 * The name of the theme function to call. 903 * @param ... 904 * Additional arguments to pass along to the theme function. 905 * @return 906 * Any data the theme function returns. This could be a plain HTML string, 907 * but also a complex object. 908 */ 909Drupal.theme = function (func) { 910 var args = Array.prototype.slice.apply(arguments, [1]); 911 912 return (Drupal.theme[func] || Drupal.theme.prototype[func]).apply(this, args); 913}; 914 915/** 916 * Freeze the current body height (as minimum height). Used to prevent 917 * unnecessary upwards scrolling when doing DOM manipulations. 918 */ 919Drupal.freezeHeight = function () { 920 Drupal.unfreezeHeight(); 921 $('<div id="freeze-height"></div>').css({ 922 position: 'absolute', 923 top: '0px', 924 left: '0px', 925 width: '1px', 926 height: $('body').css('height') 927 }).appendTo('body'); 928}; 929 930/** 931 * Unfreeze the body height. 932 */ 933Drupal.unfreezeHeight = function () { 934 $('#freeze-height').remove(); 935}; 936 937/** 938 * Encodes a Drupal path for use in a URL. 939 * 940 * For aesthetic reasons slashes are not escaped. 941 */ 942Drupal.encodePath = function (item, uri) { 943 uri = uri || location.href; 944 return encodeURIComponent(item).replace(/%2F/g, '/'); 945}; 946 947/** 948 * Get the text selection in a textarea. 949 */ 950Drupal.getSelection = function (element) { 951 if (typeof element.selectionStart != 'number' && document.selection) { 952 // The current selection. 953 var range1 = document.selection.createRange(); 954 var range2 = range1.duplicate(); 955 // Select all text. 956 range2.moveToElementText(element); 957 // Now move 'dummy' end point to end point of original range.
958 range2.setEndPoint('EndToEnd', range1); 959 // Now we can calculate start and end points. 960 var start = range2.text.length - range1.text.length; 961 var end = start + range1.text.length; 962 return { 'start': start, 'end': end }; 963 } 964 return { 'start': element.selectionStart, 'end': element.selectionEnd }; 965}; 966 967/** 968 * Add a global variable which determines if the window is being unloaded. 969 * 970 * This is primarily used by Drupal.displayAjaxError(). 971 */ 972Drupal.beforeUnloadCalled = false; 973$(window).bind('beforeunload pagehide', function () { 974 Drupal.beforeUnloadCalled = true; 975}); 976 977/** 978 * Displays a JavaScript error from an Ajax response when appropriate to do so. 979 */ 980Drupal.displayAjaxError = function (message) { 981 // Skip displaying the message if the user deliberately aborted (for example, 982 // by reloading the page or navigating to a different page) while the Ajax 983 // request was still ongoing. See, for example, the discussion at 984 // http://stackoverflow.com/questions/699941/handle-ajax-error-when-a-user-clicks-refresh. 985 if (!Drupal.beforeUnloadCalled) { 986 alert(message); 987 } 988}; 989 990/** 991 * Build an error message from an Ajax response. 992 */ 993Drupal.ajaxError = function (xmlhttp, uri, customMessage) { 994 var statusCode, statusText, pathText, responseText, readyStateText, message; 995 if (xmlhttp.status) { 996 statusCode = "\n" + Drupal.t("An AJAX HTTP error occurred.") + "\n" + Drupal.t("HTTP Result Code: !status", {'!status': xmlhttp.status}); 997 } 998 else { 999 statusCode = "\n" + Drupal.t("An AJAX HTTP request terminated abnormally."); 1000 } 1001 statusCode += "\n" + Drupal.t("Debugging information follows."); 1002 pathText = "\n" + Drupal.t("Path: !uri", {'!uri': uri} ); 1003 statusText = ''; 1004 // In some cases, when statusCode == 0, xmlhttp.statusText may not be defined. 1005 // Unfortunately, testing for it with typeof, etc, doesn't seem to catch that 1006 // and the test causes an exception. So we need to catch the exception here. 1007 try { 1008 statusText = "\n" + Drupal.t("StatusText: !statusText", {'!statusText': $.trim(xmlhttp.statusText)}); 1009 } 1010 catch (e) {} 1011 1012 responseText = ''; 1013 // Again, we don't have a way to know for sure whether accessing 1014 // xmlhttp.responseText is going to throw an exception. So we'll catch it. 1015 try { 1016 responseText = "\n" + Drupal.t("ResponseText: !responseText", {'!responseText': $.trim(xmlhttp.responseText) } ); 1017 } catch (e) {} 1018 1019 // Make the responseText more readable by stripping HTML tags and newlines. 1020 responseText = responseText.replace(/<("[^"]*"|'[^']*'|[^'">])*>/gi,""); 1021 responseText = responseText.replace(/[\n]+\s+/g,"\n"); 1022 1023 // We don't need readyState except for status == 0. 1024 readyStateText = xmlhttp.status == 0 ? ("\n" + Drupal.t("ReadyState: !readyState", {'!readyState': xmlhttp.readyState})) : ""; 1025 1026 // Additional message beyond what the xmlhttp object provides. 1027 customMessage = customMessage ? ("\n" + Drupal.t("CustomMessage: !customMessage", {'!customMessage': customMessage})) : ""; 1028 1029 message = statusCode + pathText + statusText + customMessage + responseText + readyStateText; 1030 return message; 1031}; 1032 1033// Class indicating that JS is enabled; used for styling purpose. 1034$('html').addClass('js'); 1035 1036$(function () { 1037 if (Drupal.settings.setHasJsCookie === 1) { 1038 // 'js enabled' cookie. 1039 document.cookie = 'has_js=1; path=/; SameSite=Lax'; 1040 } 1041}); 1042 1043/** 1044 * Additions to jQuery.support. 1045 */ 1046$(function () { 1047 /** 1048 * Boolean indicating whether or not position:fixed is supported. 1049 */ 1050 if (jQuery.support.positionFixed === undefined) { 1051 var el = $('<div style="position:fixed; top:10px" />').appendTo(document.body); 1052 jQuery.support.positionFixed = el[0].offsetTop === 10; 1053 el.remove(); 1054 } 1055}); 1056 1057//Attach all behaviors. 1058$(function () { 1059 Drupal.attachBehaviors(document, Drupal.settings); 1060}); 1061 1062/** 1063 * The default themes. 1064 */ 1065Drupal.theme.prototype = { 1066 1067 /** 1068 * Formats text for emphasized display in a placeholder inside a sentence. 1069 * 1070 * @param str 1071 * The text to format (plain-text). 1072 * @return 1073 * The formatted text (html). 1074 */ 1075 placeholder: function (str) { 1076 return '<em class="placeholder">' + Drupal.checkPlain(str) + '</em>'; 1077 } 1078}; 1079 1080})(jQuery); 1081; 1082/** 1083 * Workaround for deprecated $.browser which was removed in jQuery 1.9 1084 * @see https://api.jquery.com/jquery.browser/ 1085 */ 1086(function ($) { 1087 if ($.browser===undefined) { 1088 $.browser={}; 1089 $.browser.msie=false; 1090 $.browser.version=0; 1091 if (navigator.userAgent.match(/MSIE ([0-9]+)\./)) { 1092 $.browser.msie=true; 1093 $.browser.version=RegExp.$1; 1094 } 1095 } 1096})(jQuery); 1097;
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.