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