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 * jQuery Once Plugin v1.2 368 * http://plugins.jquery.com/project/once 369 * 370 * Dual licensed under the MIT and GPL licenses: 371 * http://www.opensource.org/licenses/mit-license.php 372 * http://www.gnu.org/licenses/gpl.html 373 */ 374 375(function ($) { 376 var cache = {}, uuid = 0; 377 378 /** 379 * Filters elements by whether they have not yet been processed. 380 * 381 * @param id 382 * (Optional) If this is a string, then it will be used as the CSS class 383 * name that is applied to the elements for determining whether it has 384 * already been processed. The elements will get a class in the form of 385 * "id-processed". 386 * 387 * If the id parameter is a function, it will be passed off to the fn 388 * parameter and the id will become a unique identifier, represented as a 389 * number. 390 * 391 * When the id is neither a string or a function, it becomes a unique 392 * identifier, depicted as a number. The element's class will then be 393 * represented in the form of "jquery-once-#-processed". 394 * 395 * Take note that the id must be valid for usage as an element's class name. 396 * @param fn 397 * (Optional) If given, this function will be called for each element that 398 * has not yet been processed. The function's return value follows the same 399 * logic as $.each(). Returning true will continue to the next matched 400 * element in the set, while returning false will entirely break the 401 * iteration. 402 */ 403 $.fn.once = function (id, fn) { 404 if (typeof id != 'string') { 405 // Generate a numeric ID if the id passed can't be used as a CSS class. 406 if (!(id in cache)) { 407 cache[id] = ++uuid; 408 } 409 // When the fn parameter is not passed, we interpret it from the id. 410 if (!fn) { 411 fn = id; 412 } 413 id = 'jquery-once-' + cache[id]; 414 } 415 // Remove elements from the set that have already been processed. 416 var name = id + '-processed'; 417 var elements = this.not('.' + name).addClass(name); 418 419 return $.isFunction(fn) ? elements.each(fn) : elements; 420 }; 421 422 /** 423 * Filters elements that have been processed once already. 424 * 425 * @param id 426 * A required string representing the name of the class which should be used 427 * when filtering the elements. This only filters elements that have already 428 * been processed by the once function. The id should be the same id that 429 * was originally passed to the once() function. 430 * @param fn 431 * (Optional) If given, this function will be called for each element that 432 * has not yet been processed. The function's return value follows the same 433 * logic as $.each(). Returning true will continue to the next matched 434 * element in the set, while returning false will entirely break the 435 * iteration. 436 */ 437 $.fn.removeOnce = function (id, fn) { 438 var name = id + '-processed'; 439 var elements = this.filter('.' + name).removeClass(name); 440 441 return $.isFunction(fn) ? elements.each(fn) : elements; 442 }; 443})(jQuery); 444;/*})'"*/;/*})'"*/ 445var Drupal = Drupal || { 'settings': {}, 'behaviors': {}, 'locale': {} }; 446 447// Allow other JavaScript libraries to use $. 448jQuery.noConflict(); 449 450(function ($) { 451 452/** 453 * Override jQuery.fn.init to guard against XSS attacks. 454 * 455 * See http://bugs.jquery.com/ticket/9521 456 */ 457var jquery_init = $.fn.init; 458$.fn.init = function (selector, context, rootjQuery) { 459 // If the string contains a "#" before a "<", treat it as invali
459d HTML. 460 if (selector && typeof selector === 'string') { 461 var hash_position = selector.indexOf('#'); 462 if (hash_position >= 0) { 463 var bracket_position = selector.indexOf('<'); 464 if (bracket_position > hash_position) { 465 throw 'Syntax error, unrecognized expression: ' + selector; 466 } 467 } 468 } 469 return jquery_init.call(this, selector, context, rootjQuery); 470}; 471$.fn.init.prototype = jquery_init.prototype; 472 473/** 474 * Pre-filter Ajax requests to guard against XSS attacks. 475 * 476 * See https://github.com/jquery/jquery/issues/2432 477 */ 478if ($.ajaxPrefilter) { 479 // For newer versions of jQuery, use an Ajax prefilter to prevent 480 // auto-executing script tags from untrusted domains. This is similar to the 481 // fix that is built in to jQuery 3.0 and higher. 482 $.ajaxPrefilter(function (s) { 483 if (s.crossDomain) { 484 s.contents.script = false; 485 } 486 }); 487} 488else if ($.httpData) { 489 // For the version of jQuery that ships with Drupal core, override 490 // jQuery.httpData to prevent auto-detecting "script" data types from 491 // untrusted domains. 492 var jquery_httpData = $.httpData; 493 $.httpData = function (xhr, type, s) { 494 // @todo Consider backporting code from newer jQuery versions to check for 495 // a cross-domain request here, rather than using Drupal.urlIsLocal() to 496 // block scripts from all URLs that are not on the same site. 497 if (!type && !Drupal.urlIsLocal(s.url)) { 498 var content_type = xhr.getResponseHeader('content-type') || ''; 499 if (content_type.indexOf('javascript') >= 0) { 500 // Default to a safe data type. 501 type = 'text'; 502 } 503 } 504 return jquery_httpData.call(this, xhr, type, s); 505 }; 506 $.httpData.prototype = jquery_httpData.prototype; 507} 508 509/** 510 * Attach all registered behaviors to a page element. 511 * 512 * Behaviors are event-triggered actions that attach to page elements, enhancing 513 * default non-JavaScript UIs. Behaviors are registered in the Drupal.behaviors 514 * object using the method 'attach' and optionally also 'detach' as follows: 515 * @code 516 * Drupal.behaviors.behaviorName = { 517 * attach: function (context, settings) { 518 * ... 519 * }, 520 * detach: function (context, settings, trigger) { 521 * ... 522 * } 523 * }; 524 * @endcode 525 * 526 * Drupal.attachBehaviors is added below to the jQuery ready event and so 527 * runs on initial page load. Developers implementing AHAH/Ajax in their 528 * solutions should also call this function after new page content has been 529 * loaded, feeding in an element to be processed, in order to attach all 530 * behaviors to the new content. 531 * 532 * Behaviors should use 533 * @code 534 * $(selector).once('behavior-name', function () { 535 * ... 536 * }); 537 * @endcode 538 * to ensure the behavior is attached only once to a given element. (Doing so 539 * enables the reprocessing of given elements, which may be needed on occasion 540 * despite the ability to limit behavior attachment to a particular element.) 541 * 542 * @param context 543 * An element to attach behaviors to. If none is given, the document element 544 * is used. 545 * @param settings 546 * An object containing settings for the current context. If none given, the 547 * global Drupal.settings object is used. 548 */ 549Drupal.attachBehaviors = function (context, settings) { 550 context = context || document; 551 settings = settings || Drupal.settings; 552 // Execute all of them. 553 $.each(Drupal.behaviors, function () { 554 if ($.isFunction(this.attach)) { 555 this.attach(context, settings); 556 } 557 }); 558}; 559 560/** 561 * Detach registered behaviors from a page element. 562 * 563 * Developers implementing AHAH/Ajax in their solutions should call this 564 * function before page content is about to be removed, feeding in an element 565 * to be processed, in order to allow special behaviors to detach from the 566 * content. 567 * 568 * Such implementations should look for the class name that was added in their 569 * corresponding Drupal.behaviors.behaviorName.attach implementation, i.e. 570 * behaviorName-processed, to ensure the behavior is detached only from 571 * previously processed elements. 572 * 573 * @param context 574 * An element to detach behaviors from. If none is given, the document element 575 * is used. 576 * @param settings
577 * An object containing settings for the current context. If none given, the 578 * global Drupal.settings object is used. 579 * @param trigger 580 * A string containing what's causing the behaviors to be detached. The 581 * possible triggers are: 582 * - unload: (default) The context element is being removed from the DOM. 583 * - move: The element is about to be moved within the DOM (for example, 584 * during a tabledrag row swap). After the move is completed, 585 * Drupal.attachBehaviors() is called, so that the behavior can undo 586 * whatever it did in response to the move. Many behaviors won't need to 587 * do anything simply in response to the element being moved, but because 588 * IFRAME elements reload their "src" when being moved within the DOM, 589 * behaviors bound to IFRAME elements (like WYSIWYG editors) may need to 590 * take some action. 591 * - serialize: When an Ajax form is submitted, this is called with the 592 * form as the context. This provides every behavior within the form an 593 * opportunity to ensure that the field elements have correct content 594 * in them before the form is serialized. The canonical use-case is so 595 * that WYSIWYG editors can update the hidden textarea to which they are 596 * bound. 597 * 598 * @see Drupal.attachBehaviors 599 */ 600Drupal.detachBehaviors = function (context, settings, trigger) { 601 context = context || document; 602 settings = settings || Drupal.settings; 603 trigger = trigger || 'unload'; 604 // Execute all of them. 605 $.each(Drupal.behaviors, function () { 606 if ($.isFunction(this.detach)) { 607 this.detach(context, settings, trigger); 608 } 609 }); 610}; 611 612/** 613 * Encode special characters in a plain-text string for display as HTML. 614 * 615 * @ingroup sanitization 616 */ 617Drupal.checkPlain = function (str) { 618 var character, regex, 619 replace = { '&': '&', "'": ''', '"': '"', '<': '<', '>': '>' }; 620 str = String(str); 621 for (character in replace) { 622 if (replace.hasOwnProperty(character)) { 623 regex = new RegExp(character, 'g'); 624 str = str.replace(regex, replace[character]); 625 } 626 } 627 return str; 628}; 629 630/** 631 * Replace placeholders with sanitized values in a string. 632 * 633 * @param str 634 * A string with placeholders. 635 * @param args 636 * An object of replacements pairs to make. Incidences of any key in this 637 * array are replaced with the corresponding value. Based on the first 638 * character of the key, the value is escaped and/or themed: 639 * - !variable: inserted as is 640 * - @variable: escape plain text to HTML (Drupal.checkPlain) 641 * - %variable: escape text and theme as a placeholder for user-submitted 642 * content (checkPlain + Drupal.theme('placeholder')) 643 * 644 * @see Drupal.t() 645 * @ingroup sanitization 646 */ 647Drupal.formatString = function(str, args) { 648 // Transform arguments before inserting them. 649 for (var key in args) { 650 if (args.hasOwnProperty(key)) { 651 switch (key.charAt(0)) { 652 // Escaped only. 653 case '@': 654 args[key] = Drupal.checkPlain(args[key]); 655 break; 656 // Pass-through. 657 case '!': 658 break; 659 // Escaped and placeholder. 660 default: 661 args[key] = Drupal.theme('placeholder', args[key]); 662 break; 663 } 664 } 665 } 666 667 return Drupal.stringReplace(str, args, null); 668}; 669 670/** 671 * Replace substring. 672 * 673 * The longest keys will be tried first. Once a substring has been replaced, 674 * its new value will not be searched again. 675 * 676 * @param {String} str 677 * A string with placeholders. 678 * @param {Object} args 679 * Key-value pairs. 680 * @param {Array|null} keys 681 * Array of keys from the "args". Internal use only. 682 * 683 * @return {String} 684 * Returns the replaced string. 685 */ 686Drupal.stringReplace = function (str, args, keys) { 687 if (str.length === 0) { 688 return str; 689 } 690 691 // If the array of keys is not passed then collect the keys from the args. 692 if (!$.isArray(keys)) { 693 keys = []; 694 for (var k in args) { 695 if (args.hasOwnProperty(k)) { 696 keys.push(k); 697 } 698 } 699 700 // Order the keys by the character length. The shortest one is the first. 701 keys.sort(function (a, b) { return a.length - b.length; }); 702 } 703 704 if (keys.length === 0) { 705 return str; 706 } 707 708 // Take next longest one from the end. 709 var key = keys.pop(); 710 var fragments = str.split(key); 711 712 if (keys.length) { 713 for (var i = 0; i < fragments.length; i++) { 714 // Process each fragment with a copy of remaining keys. 715 fragments[i] = Drupal.stringReplace(fragments[i], args, keys.slice(0)); 716 } 717 } 718 719 return fragments.join(args[key]); 720}; 721 722/** 723 * Translate strings to the page language or a given language. 724 * 725 * See the documentation of the server-side t() function for further details. 726 * 727 * @param str 728 * A string containing the English string to translate. 729 * @param args 730 * An object of replacements pairs to make after translation. Incidences 731 * of any key in this array are replaced with the corresponding value. 732 * See Drupal.formatString(). 733 * 734 * @param options 735 * - 'context' (defaults to the empty context): The context the source string 736 * belongs to. 737 * 738 * @return 739 * The translated string. 740 */ 741Drupal.t = function (str, args, options) { 742 options = options || {}; 743 options.context = options.context || ''; 744 745 // Fetch the localized version of the string. 746 if (Drupal.locale.strings && Drupal.locale.strings[options.context] && Drupal.locale.strings[options.context][str]) { 747 str = Drupal.locale.strings[options.context][str]; 748 } 749 750 if (args) { 751 str = Drupal.formatString(str, args); 752 } 753 return str; 754}; 755 756/** 757 * Format a string containing a count of items. 758 * 759 * This function ensures that the string is pluralized correctly. Since Drupal.t() is 760 * called by this function, make sure not to pass already-localized strings to it. 761 * 762 * See the documentation of the server-side format_plural() function for further details. 763 * 764 * @param count 765 * The item count to display. 766 * @param singular 767 * The string for the singular case. Please make sure it is clear this is 768 * singular, to ease translation (e.g. use "1 new comment" instead of "1 new"). 769 * Do not use @count in the singular string. 770 * @param plural 771 * The string for the plural case. Please make sure it is clear this is plural, 772 * to ease translation. Use @count in place of the item count, as in "@count 773 * new comments". 774 * @param args 775 * An object of replacements pairs to make after translation. Incidences 776 * of any key in this array are replaced with the corresponding value. 777 * See Drupal.formatString(). 778 * Note that you do not need to include @count in this array.
779 * This replacement is done automatically for the plural case. 780 * @param options 781 * The options to pass to the Drupal.t() function. 782 * @return 783 * A translated string. 784 */ 785Drupal.formatPlural = function (count, singular, plural, args, options) { 786 args = args || {}; 787 args['@count'] = count; 788 // Determine the index of the plural form. 789 var index = Drupal.locale.pluralFormula ? Drupal.locale.pluralFormula(args['@count']) : ((args['@count'] == 1) ? 0 : 1); 790 791 if (index == 0) { 792 return Drupal.t(singular, args, options); 793 } 794 else if (index == 1) { 795 return Drupal.t(plural, args, options); 796 } 797 else { 798 args['@count[' + index + ']'] = args['@count']; 799 delete args['@count']; 800 return Drupal.t(plural.replace('@count', '@count[' + index + ']'), args, options); 801 } 802}; 803 804/** 805 * Returns the passed in URL as an absolute URL. 806 * 807 * @param url 808 * The URL string to be normalized to an absolute URL. 809 * 810 * @return 811 * The normalized, absolute URL. 812 * 813 * @see https://github.com/angular/angular.js/blob/v1.4.4/src/ng/urlUtils.js 814 * @see https://grack.com/blog/2009/11/17/absolutizing-url-in-javascript 815 * @see https://github.com/jquery/jquery-ui/blob/1.11.4/ui/tabs.js#L53 816 */ 817Drupal.absoluteUrl = function (url) { 818 var urlParsingNode = document.createElement('a'); 819 820 // Decode the URL first; this is required by IE <= 6. Decoding non-UTF-8 821 // strings may throw an exception. 822 try { 823 url = decodeURIComponent(url); 824 } catch (e) {} 825 826 urlParsingNode.setAttribute('href', url); 827 828 // IE <= 7 normalizes the URL when assigned to the anchor node similar to 829 // the other browsers. 830 return urlParsingNode.cloneNode(false).href; 831}; 832 833/** 834 * Returns true if the URL is within Drupal's base path. 835 * 836 * @param url 837 * The URL string to be tested. 838 * 839 * @return 840 * Boolean true if local. 841 * 842 * @see https://github.com/jquery/jquery-ui/blob/1.11.4/ui/tabs.js#L58 843 */ 844Drupal.urlIsLocal = function (url) { 845 // Always use browser-derived absolute URLs in the comparison, to avoid 846 // attempts to break out of the base path using directory traversal. 847 var absoluteUrl = Drupal.absoluteUrl(url); 848 var protocol = location.protocol; 849 850 // Consider URLs that match this site's base URL but use HTTPS instead of HTTP 851 // as local as well. 852 if (protocol === 'http:' && absoluteUrl.indexOf('https:') === 0) { 853 protocol = 'https:'; 854 } 855 var baseUrl = protocol + '//' + location.host + Drupal.settings.basePath.slice(0, -1); 856 857 // Decoding non-UTF-8 strings may throw an exception. 858 try { 859 absoluteUrl = decodeURIComponent(absoluteUrl); 860 } catch (e) {} 861 try { 862 baseUrl = decodeURIComponent(baseUrl); 863 } catch (e) {} 864 865 // The given URL matches the site's base URL, or has a path under the site's 866 // base URL. 867 return absoluteUrl === baseUrl || absoluteUrl.indexOf(baseUrl + '/') === 0; 868}; 869 870/** 871 * Sanitizes a URL for use with jQuery.ajax(). 872 * 873 * @param url 874 * The URL string to be sanitized. 875 * 876 * @return 877 * The sanitized URL. 878 */ 879Drupal.sanitizeAjaxUrl = function (url) { 880 var regex = /\=\?(&|$)/; 881 while (url.match(regex)) { 882 url = url.replace(regex, ''); 883 } 884 return url; 885} 886 887/** 888 * Generate the themed representation of a Drupal object. 889 * 890 * All requests for themed output must go through this function. It examines 891 * the request and routes it to the appropriate theme function. If the current 892 * theme does not provide an override function, the generic theme function is 893 * called. 894 * 895 * For example, to retrieve the HTML for text that should be emphasized and 896 * displayed as a placeholder inside a sentence, call 897 * Drupal.theme('placeholder', text). 898 * 899 * @param func 900 * The name of the theme function to call. 901 * @param ... 902 * Additional arguments to pass along to the theme function. 903 * @return 904 * Any data the theme function returns. This could be a plain HTML string, 905 * but also a complex object. 906 */ 907Drupal.theme = function (func) { 908 var args = Array.prototype.slice.apply(arguments, [1]); 909 910 return (Drupal.theme[func] || Drupal.theme.prototype[func]).apply(this, args); 911}; 912 913/** 914 * Freeze the current body height (as minimum height). Used to prevent 915 * unnecessary upwards scrolling when doing DOM manipulations. 916 */ 917Drupal.freezeHeight = function () { 918 Drupal.unfreezeHeight(); 919 $('<div id="freeze-height"></div>').css({ 920 position: 'absolute', 921 top: '0px', 922 left: '0px', 923 width: '1px', 924 height: $('body').css('height') 925 }).appendTo('body'); 926}; 927 928/** 929 * Unfreeze the body height. 930 */ 931Drupal.unfreezeHeight = function () { 932 $('#freeze-height').remove(); 933}; 934 935/** 936 * Encodes a Drupal path for use in a URL. 937 * 938 * For aesthetic reasons slashes are not escaped. 939 */ 940Drupal.encodePath = function (item, uri) { 941 uri = uri || location.href; 942 return encodeURIComponent(item).replace(/%2F/g, '/'); 943}; 944 945/** 946 * Get the text selection in a textarea. 947 */ 948Drupal.getSelection = function (element) { 949 if (typeof element.selectionStart != 'number' && document.selection) { 950 // The current selection. 951 var range1 = document.selection.createRange(); 952 var range2 = range1.duplicate(); 953 // Select all text. 954 range2.moveToElementText(element); 955 // Now move 'dummy' end point to end point of original range.
956 range2.setEndPoint('EndToEnd', range1); 957 // Now we can calculate start and end points. 958 var start = range2.text.length - range1.text.length; 959 var end = start + range1.text.length; 960 return { 'start': start, 'end': end }; 961 } 962 return { 'start': element.selectionStart, 'end': element.selectionEnd }; 963}; 964 965/** 966 * Add a global variable which determines if the window is being unloaded. 967 * 968 * This is primarily used by Drupal.displayAjaxError(). 969 */ 970Drupal.beforeUnloadCalled = false; 971$(window).bind('beforeunload pagehide', function () { 972 Drupal.beforeUnloadCalled = true; 973}); 974 975/** 976 * Displays a JavaScript error from an Ajax response when appropriate to do so. 977 */ 978Drupal.displayAjaxError = function (message) { 979 // Skip displaying the message if the user deliberately aborted (for example, 980 // by reloading the page or navigating to a different page) while the Ajax 981 // request was still ongoing. See, for example, the discussion at 982 // http://stackoverflow.com/questions/699941/handle-ajax-error-when-a-user-clicks-refresh. 983 if (!Drupal.beforeUnloadCalled) { 984 alert(message); 985 } 986}; 987 988/** 989 * Build an error message from an Ajax response. 990 */ 991Drupal.ajaxError = function (xmlhttp, uri, customMessage) { 992 var statusCode, statusText, pathText, responseText, readyStateText, message; 993 if (xmlhttp.status) { 994 statusCode = "\n" + Drupal.t("An AJAX HTTP error occurred.") + "\n" + Drupal.t("HTTP Result Code: !status", {'!status': xmlhttp.status}); 995 } 996 else { 997 statusCode = "\n" + Drupal.t("An AJAX HTTP request terminated abnormally."); 998 } 999 statusCode += "\n" + Drupal.t("Debugging information follows."); 1000 pathText = "\n" + Drupal.t("Path: !uri", {'!uri': uri} ); 1001 statusText = ''; 1002 // In some cases, when statusCode == 0, xmlhttp.statusText may not be defined. 1003 // Unfortunately, testing for it with typeof, etc, doesn't seem to catch that 1004 // and the test causes an exception. So we need to catch the exception here. 1005 try { 1006 statusText = "\n" + Drupal.t("StatusText: !statusText", {'!statusText': $.trim(xmlhttp.statusText)}); 1007 } 1008 catch (e) {} 1009 1010 responseText = ''; 1011 // Again, we don't have a way to know for sure whether accessing 1012 // xmlhttp.responseText is going to throw an exception. So we'll catch it. 1013 try { 1014 responseText = "\n" + Drupal.t("ResponseText: !responseText", {'!responseText': $.trim(xmlhttp.responseText) } ); 1015 } catch (e) {} 1016 1017 // Make the responseText more readable by stripping HTML tags and newlines. 1018 responseText = responseText.replace(/<("[^"]*"|'[^']*'|[^'">])*>/gi,""); 1019 responseText = responseText.replace(/[\n]+\s+/g,"\n"); 1020 1021 // We don't need readyState except for status == 0. 1022 readyStateText = xmlhttp.status == 0 ? ("\n" + Drupal.t("ReadyState: !readyState", {'!readyState': xmlhttp.readyState})) : ""; 1023 1024 // Additional message beyond what the xmlhttp object provides. 1025 customMessage = customMessage ? ("\n" + Drupal.t("CustomMessage: !customMessage", {'!customMessage': customMessage})) : ""; 1026 1027 message = statusCode + pathText + statusText + customMessage + responseText + readyStateText; 1028 return message; 1029}; 1030 1031// Class indicating that JS is enabled; used for styling purpose. 1032$('html').addClass('js'); 1033 1034$(function () { 1035 if (Drupal.settings.setHasJsCookie === 1) { 1036 // 'js enabled' cookie. 1037 document.cookie = 'has_js=1; path=/; SameSite=Lax'; 1038 } 1039}); 1040 1041/** 1042 * Additions to jQuery.support. 1043 */ 1044$(function () { 1045 /** 1046 * Boolean indicating whether or not position:fixed is supported. 1047 */ 1048 if (jQuery.support.positionFixed === undefined) { 1049 var el = $('<div style="position:fixed; top:10px" />').appendTo(document.body); 1050 jQuery.support.positionFixed = el[0].offsetTop === 10; 1051 el.remove(); 1052 } 1053}); 1054 1055//Attach all behaviors. 1056$(function () { 1057 Drupal.attachBehaviors(document, Drupal.settings); 1058}); 1059 1060/** 1061 * The default themes. 1062 */ 1063Drupal.theme.prototype = { 1064 1065 /** 1066 * Formats text for emphasized display in a placeholder inside a sentence. 1067 * 1068 * @param str 1069 * The text to format (plain-text). 1070 * @return 1071 * The formatted text (html). 1072 */ 1073 placeholder: function (str) { 1074 return '<em class="placeholder">' + Drupal.checkPlain(str) + '</em>'; 1075 } 1076}; 1077 1078})(jQuery); 1079;/*})'"*/;/*})'"*/ 1080/** 1081 * Workaround for deprecated $.browser which was removed in jQuery 1.9 1082 * @see https://api.jquery.com/jquery.browser/ 1083 */ 1084(function ($) { 1085 if ($.browser===undefined) { 1086 $.browser={}; 1087 $.browser.msie=false; 1088 $.browser.version=0; 1089 if (navigator.userAgent.match(/MSIE ([0-9]+)\./)) { 1090 $.browser.msie=true; 1091 $.browser.version=RegExp.$1; 1092 } 1093 } 1094})(jQuery); 1095;/*})'"*/;/*})'"*/
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.