1/** 2 * @file 3 * Drupal Bootstrap object. 4 */ 5 6/** 7 * All Drupal Bootstrap JavaScript APIs are contained in this namespace. 8 * 9 * @param {underscore} _ 10 * @param {jQuery} $ 11 * @param {Drupal} Drupal 12 * @param {drupalSettings} drupalSettings 13 */ 14(function (_, $, Drupal, drupalSettings) { 15 'use strict'; 16 17 /** 18 * @typedef Drupal.bootstrap 19 */ 20 var Bootstrap = { 21 processedOnce: {}, 22 settings: drupalSettings.bootstrap || {} 23 }; 24 25 /** 26 * Wraps Drupal.checkPlain() to ensure value passed isn't empty. 27 * 28 * Encodes special characters in a plain-text string for display as HTML. 29 * 30 * @param {string} str 31 * The string to be encoded. 32 * 33 * @return {string} 34 * The encoded string. 35 * 36 * @ingroup sanitization 37 */ 38 Bootstrap.checkPlain = function (str) { 39 return str && Drupal.checkPlain(str) || ''; 40 }; 41 42 /** 43 * Creates a jQuery plugin. 44 * 45 * @param {String} id 46 * A jQuery plugin identifier located in $.fn. 47 * @param {Function} plugin 48 * A constructor function used to initialize the for the jQuery plugin. 49 * @param {Boolean} [noConflict] 50 * Flag indicating whether or not to create a ".noConflict()" helper method 51 * for the plugin. 52 */ 53 Bootstrap.createPlugin = function (id, plugin, noConflict) { 54 // Immediately return if plugin doesn't exist. 55 if ($.fn[id] !== void 0) { 56 return this.fatal('Specified jQuery plugin identifier already exists: @id. Use Drupal.bootstrap.replacePlugin() instead.', {'@id': id}); 57 } 58 59 // Immediately return if plugin isn't a function. 60 if (typeof plugin !== 'function') { 61 return this.fatal('You must provide a constructor function to create a jQuery plugin "@id": @plugin', {'@id': id, '@plugin': plugin}); 62 } 63 64 // Add a ".noConflict()" helper method. 65 this.pluginNoConflict(id, plugin, noConflict); 66 67 $.fn[id] = plugin; 68 }; 69 70 /** 71 * Diff object properties. 72 * 73 * @param {...Object} objects 74 * Two or more objects. The first object will be used to return properties 75 * values. 76 * 77 * @return {Object} 78 * Returns the properties of the first passed object that are not present 79 * in all other passed objects. 80 */ 81 Bootstrap.diffObjects = function (objects) { 82 var args = Array.prototype.slice.call(arguments); 83 return _.pick(args[0], _.difference.apply(_, _.map(args, function (obj) { 84 return Object.keys(obj); 85 }))); 86 }; 87 88 /** 89 * Map of supported events by regular expression. 90 * 91 * @type {Object<Event|MouseEvent|KeyboardEvent|TouchEvent,RegExp>} 92 */ 93 Bootstrap.eventMap = { 94 Event: /^(?:load|unload|abort|error|select|change|submit|reset|focus|blur|resize|scroll)$/, 95 MouseEvent: /^(?:click|dblclick|mouse(?:down|enter|leave|up|over|move|out))$/, 96 KeyboardEvent: /^(?:key(?:down|press|up))$/, 97 TouchEvent: /^(?:touch(?:start|end|move|cancel))$/ 98 }; 99 100 /** 101 * Extends a jQuery Plugin. 102 * 103 * @param {String} id 104 * A jQuery plugin identifier located in $.fn. 105 * @param {Function} callback 106 * A constructor function used to initialize the for the jQuery plugin. 107 * 108 * @return {Function|Boolean} 109 * The jQuery plugin constructor or FALSE if the plugin does not exist. 110 */ 111 Bootstrap.extendPlugin = function (id, callback) { 112 // Immediately return if plugin doesn't exist. 113 if (typeof $.fn[id] !== 'function') { 114 return this.fatal('Specified jQuery plugin identifier does not exist: @id', {'@id': id}); 115 } 116 117 // Immediately return if callback isn't a function. 118 if (typeof callback !== 'function') { 119 return this.fatal('You must provide a callback function to extend the jQuery plugin "@id": @callback', {'@id': id, '@callback': callback}); 120 } 121 122 // Determine existing plugin constructor. 123 var constructor = $.fn[id] && $.fn[id].Constructor || $.fn[id]; 124 var plugin = callback.apply(constructor, [this.settings]); 125 if (!$.isPlainObject(plugin)) { 126 return this.fatal('Returned value from callback is not a plain object that can be used to extend the jQuery plugin "@id": @obj', {'@obj': plugin}); 127 } 128 129 this.wrapPluginConstructor(constructor, plugin, true); 130 131 return $.fn[id]; 132 }; 133 134 Bootstrap.superWrapper = function (parent, fn) { 135 return function () { 136 var previousSuper = this.super; 137 this.super = parent; 138 var ret = fn.apply(this, arguments); 139 if (previousSuper) { 140 this.super = previousSuper; 141 } 142 else { 143 delete this.super; 144 } 145 return ret; 146 }; 147 }; 148 149 /** 150 * Provide a helper method for displaying when something is went wrong. 151 * 152 * @param {String} message 153 * The message to display. 154 * @param {Object} [args] 155 * An arguments to use in message. 156 * 157 * @return {Boolean} 158 * Always returns FALSE. 159 */
160 Bootstrap.fatal = function (message, args) { 161 if (this.settings.dev && console.warn) { 162 for (var name in args) { 163 if (args.hasOwnProperty(name) && typeof args[name] === 'object') { 164 args[name] = JSON.stringify(args[name]); 165 } 166 } 167 Drupal.throwError(new Error(Drupal.formatString(message, args))); 168 } 169 return false; 170 }; 171 172 /** 173 * Intersects object properties. 174 * 175 * @param {...Object} objects 176 * Two or more objects. The first object will be used to return properties 177 * values. 178 * 179 * @return {Object} 180 * Returns the properties of first passed object that intersects with all 181 * other passed objects. 182 */ 183 Bootstrap.intersectObjects = function (objects) { 184 var args = Array.prototype.slice.call(arguments); 185 return _.pick(args[0], _.intersection.apply(_, _.map(args, function (obj) { 186 return Object.keys(obj); 187 }))); 188 }; 189 190 /** 191 * Normalizes an object's values. 192 * 193 * @param {Object} obj 194 * The object to normalize. 195 * 196 * @return {Object} 197 * The normalized object. 198 */ 199 Bootstrap.normalizeObject = function (obj) { 200 if (!$.isPlainObject(obj)) { 201 return obj; 202 } 203 204 for (var k in obj) { 205 if (typeof obj[k] === 'string') { 206 if (obj[k] === 'true') { 207 obj[k] = true; 208 } 209 else if (obj[k] === 'false') { 210 obj[k] = false; 211 } 212 else if (obj[k].match(/^[\d-.]$/)) { 213 obj[k] = parseFloat(obj[k]); 214 } 215 } 216 else if ($.isPlainObject(obj[k])) { 217 obj[k] = Bootstrap.normalizeObject(obj[k]); 218 } 219 } 220 221 return obj; 222 }; 223 224 /** 225 * An object based once plugin (similar to jquery.once, but without the DOM). 226 * 227 * @param {String} id 228 * A unique identifier. 229 * @param {Function} callback 230 * The callback to invoke if the identifier has not yet been seen. 231 * 232 * @return {Bootstrap} 233 */ 234 Bootstrap.once = function (id, callback) { 235 // Immediately return if identifier has already been processed. 236 if (this.processedOnce[id]) { 237 return this; 238 } 239 callback.call(this, this.settings); 240 this.processedOnce[id] = true; 241 return this; 242 }; 243 244 /** 245 * Provide jQuery UI like ability to get/set options for Bootstrap plugins. 246 * 247 * @param {string|object} key 248 * A string value of the option to set, can be dot like to a nested key. 249 * An object of key/value pairs. 250 * @param {*} [value] 251 * (optional) A value to set for key. 252 * 253 * @returns {*} 254 * - Returns nothing if key is an object or both key and value parameters 255 * were provided to set an option. 256 * - Returns the a value for a specific setting if key was provided. 257 * - Returns an object of key/value pairs of all the options if no key or 258 * value parameter was provided. 259 * 260 * @see https://github.com/jquery/jquery-ui/blob/master/ui/widget.js 261 */ 262 Bootstrap.option = function (key, value) { 263 var options = $.isPlainObject(key) ? $.extend({}, key) : {}; 264 265 // Get all options (clone so it doesn't reference the internal object). 266 if (arguments.length === 0) { 267 return $.extend({}, this.options); 268 } 269 270 // Get/set single option. 271 if (typeof key === "string") { 272 // Handle nested keys in dot notation. 273 // e.g., "foo.bar" => { foo: { bar: true } } 274 var parts = key.split('.'); 275 key = parts.shift(); 276 var obj = options; 277 if (parts.length) { 278 for (var i = 0; i < parts.length - 1; i++) { 279 obj[parts[i]] = obj[parts[i]] || {}; 280 obj = obj[parts[i]]; 281 } 282 key = parts.pop(); 283 } 284 285 // Get. 286 if (arguments.length === 1) { 287 return obj[key] === void 0 ? null : obj[key]; 288 } 289 290 // Set. 291 obj[key] = value; 292 } 293 294 // Set multiple options. 295 $.extend(true, this.options, options); 296 }; 297 298 /** 299 * Adds a ".noConflict()" helper method if needed. 300 * 301 * @param {String} id 302 * A jQuery plugin identifier located in $.fn. 303 * @param {Function} plugin 304 * @param {Function} plugin 305 * A constructor function used to initialize the for the jQuery plugin. 306 * @param {Boolean} [noConflict]
307 * Flag indicating whether or not to create a ".noConflict()" helper method 308 * for the plugin. 309 */ 310 Bootstrap.pluginNoConflict = function (id, plugin, noConflict) { 311 if (plugin.noConflict === void 0 && (noConflict === void 0 || noConflict)) { 312 var old = $.fn[id]; 313 plugin.noConflict = function () { 314 $.fn[id] = old; 315 return this; 316 }; 317 } 318 }; 319 320 /** 321 * Creates a handler that relays to another event name. 322 * 323 * @param {HTMLElement|jQuery} target 324 * A target element. 325 * @param {String} name 326 * The name of the event to trigger. 327 * @param {Boolean} [stopPropagation=true] 328 * Flag indicating whether to stop the propagation of the event, defaults 329 * to true. 330 * 331 * @return {Function} 332 * An even handler callback function. 333 */ 334 Bootstrap.relayEvent = function (target, name, stopPropagation) { 335 return function (e) { 336 if (stopPropagation === void 0 || stopPropagation) { 337 e.stopPropagation(); 338 } 339 var $target = $(target); 340 var parts = name.split('.').filter(Boolean); 341 var type = parts.shift(); 342 e.target = $target[0]; 343 e.currentTarget = $target[0]; 344 e.namespace = parts.join('.'); 345 e.type = type; 346 $target.trigger(e); 347 }; 348 }; 349 350 /** 351 * Replaces a Bootstrap jQuery plugin definition. 352 * 353 * @param {String} id 354 * A jQuery plugin identifier located in $.fn. 355 * @param {Function} callback 356 * A callback function that is immediately invoked and must return a 357 * function that will be used as the plugin constructor. 358 * @param {Boolean} [noConflict] 359 * Flag indicating whether or not to create a ".noConflict()" helper method 360 * for the plugin. 361 */ 362 Bootstrap.replacePlugin = function (id, callback, noConflict) { 363 // Immediately return if plugin doesn't exist. 364 if (typeof $.fn[id] !== 'function') { 365 return this.fatal('Specified jQuery plugin identifier does not exist: @id', {'@id': id}); 366 } 367 368 // Immediately return if callback isn't a function. 369 if (typeof callback !== 'function') { 370 return this.fatal('You must provide a valid callback function to replace a jQuery plugin: @callback', {'@callback': callback}); 371 } 372 373 // Determine existing plugin constructor. 374 var constructor = $.fn[id] && $.fn[id].Constructor || $.fn[id]; 375 var plugin = callback.apply(constructor, [this.settings]); 376 377 // Immediately return if plugin isn't a function. 378 if (typeof plugin !== 'function') { 379 return this.fatal('Returned value from callback is not a usable function to replace a jQuery plugin "@id": @plugin', {'@id': id, '@plugin': plugin}); 380 } 381 382 this.wrapPluginConstructor(constructor, plugin); 383 384 // Add a ".noConflict()" helper method. 385 this.pluginNoConflict(id, plugin, noConflict); 386 387 $.fn[id] = plugin; 388 }; 389 390 /** 391 * Simulates a native event on an element in the browser. 392 * 393 * Note: This is a fairly complete modern implementation. If things aren't 394 * working quite the way you intend (in older browsers), you may wish to use 395 * the jQuery.simulate plugin. If it's available, this method will defer to 396 * that plugin. 397 * 398 * @see https://github.com/jquery/jquery-simulate 399 * 400 * @param {HTMLElement|jQuery} element 401 * A DOM element to dispatch event on. Note: this may be a jQuery object, 402 * however be aware that this will trigger the same event for each element 403 * inside the jQuery collection; use with caution. 404 * @param {String|String[]} type 405 * The type(s) of event to simulate. 406 * @param {Object} [options] 407 * An object of options to pass to the event constructor. Typically, if 408 * an event is being proxied, you should just pass the original event 409 * object here. This allows, if the browser supports it, to be a truly 410 * simulated event. 411 * 412 * @return {Boolean} 413 * The return value is false if event is cancelable and at least one of the 414 * event handlers which handled this event called Event.preventDefault(). 415 * Otherwise it returns true. 416 */ 417 Bootstrap.simulate = function (element, type, options) { 418 // Handle jQuery object wrappers so it triggers on each element. 419 var ret = true; 420 if (element instanceof $) { 421 element.each(function () { 422 if (!Bootstrap.simulate(this, type, options)) { 423 ret = false;
424 } 425 }); 426 return ret; 427 } 428 429 if (!(element instanceof HTMLElement)) { 430 this.fatal('Passed element must be an instance of HTMLElement, got "@type" instead.', { 431 '@type': typeof element, 432 }); 433 } 434 435 // Defer to the jQuery.simulate plugin, if it's available. 436 if (typeof $.simulate === 'function') { 437 new $.simulate(element, type, options); 438 return true; 439 } 440 441 var event; 442 var ctor; 443 var types = [].concat(type); 444 for (var i = 0, l = types.length; i < l; i++) { 445 type = types[i]; 446 for (var name in this.eventMap) { 447 if (this.eventMap[name].test(type)) { 448 ctor = name; 449 break; 450 } 451 } 452 if (!ctor) { 453 throw new SyntaxError('Only rudimentary HTMLEvents, KeyboardEvents and MouseEvents are supported: ' + type); 454 } 455 var opts = {bubbles: true, cancelable: true}; 456 if (ctor === 'KeyboardEvent' || ctor === 'MouseEvent') { 457 $.extend(opts, {ctrlKey: !1, altKey: !1, shiftKey: !1, metaKey: !1}); 458 } 459 if (ctor === 'MouseEvent') { 460 $.extend(opts, {button: 0, pointerX: 0, pointerY: 0, view: window}); 461 } 462 if (options) { 463 $.extend(opts, options); 464 } 465 if (typeof window[ctor] === 'function') { 466 event = new window[ctor](type, opts); 467 if (!element.dispatchEvent(event)) { 468 ret = false;
469 } 470 } 471 else if (document.createEvent) { 472 event = document.createEvent(ctor); 473 event.initEvent(type, opts.bubbles, opts.cancelable); 474 if (!element.dispatchEvent(event)) { 475 ret = false; 476 } 477 } 478 else if (typeof element.fireEvent === 'function') { 479 event = $.extend(document.createEventObject(), opts); 480 if (!element.fireEvent('on' + type, event)) { 481 ret = false; 482 } 483 } 484 else if (typeof element[type]) { 485 element[type](); 486 } 487 } 488 return ret; 489 }; 490 491 /** 492 * Strips HTML and returns just text. 493 * 494 * @param {String|Element|jQuery} html 495 * A string of HTML content, an Element DOM object or a jQuery object. 496 * 497 * @return {String} 498 * The text without HTML tags. 499 * 500 * @todo Replace with http://locutus.io/php/strings/strip_tags/ 501 */ 502 Bootstrap.stripHtml = function (html) { 503 if (html instanceof $) { 504 html = html.html(); 505 } 506 else if (html instanceof Element) { 507 html = html.innerHTML; 508 } 509 var tmp = document.createElement('DIV'); 510 tmp.innerHTML = html; 511 return (tmp.textContent || tmp.innerText || '').replace(/^[\s\n\t]*|[\s\n\t]*$/, ''); 512 }; 513 514 /** 515 * Provide a helper method for displaying when something is unsupported. 516 * 517 * @param {String} type 518 * The type of unsupported object, e.g. method or option. 519 * @param {String} name 520 * The name of the unsupported object. 521 * @param {*} [value] 522 * The value of the unsupported object. 523 */ 524 Bootstrap.unsupported = function (type, name, value) { 525 Bootstrap.warn('Unsupported by Drupal Bootstrap: (@type) @name -> @value', { 526 '@type': type, 527 '@name': name, 528 '@value': typeof value === 'object' ? JSON.stringify(value) : value 529 }); 530 }; 531 532 /** 533 * Provide a helper method to display a warning. 534 * 535 * @param {String} message 536 * The message to display. 537 * @param {Object} [args] 538 * Arguments to use as replacements in Drupal.formatString. 539 */ 540 Bootstrap.warn = function (message, args) { 541 if (this.settings.dev && console.warn) { 542 console.warn(Drupal.formatString(message, args)); 543 } 544 }; 545 546 /** 547 * Wraps a plugin with common functionality. 548 * 549 * @param {Function} constructor 550 * A plugin constructor being wrapped. 551 * @param {Object|Function} plugin 552 * The plugin being wrapped. 553 * @param {Boolean} [extend = false] 554 * Whether to add super extensibility. 555 */ 556 Bootstrap.wrapPluginConstructor = function (constructor, plugin, extend) { 557 var proto = constructor.prototype; 558 559 // Add a jQuery UI like option getter/setter method. 560 var option = this.option; 561 if (proto.option === void(0)) { 562 proto.option = function () { 563 return option.apply(this, arguments); 564 }; 565 } 566 567 if (extend) { 568 // Handle prototype properties separately. 569 if (plugin.prototype !== void 0) { 570 for (var key in plugin.prototype) { 571 if (!plugin.prototype.hasOwnProperty(key)) continue; 572 var value = plugin.prototype[key]; 573 if (typeof value === 'function') { 574 proto[key] = this.superWrapper(proto[key] || function () {}, value); 575 } 576 else { 577 proto[key] = $.isPlainObject(value) ? $.extend(true, {}, proto[key], value) : value; 578 } 579 } 580 } 581 delete plugin.prototype; 582 583 // Handle static properties. 584 for (key in plugin) { 585 if (!plugin.hasOwnProperty(key)) continue; 586 value = plugin[key]; 587 if (typeof value === 'function') { 588 constructor[key] = this.superWrapper(constructor[key] || function () {}, value); 589 } 590 else { 591 constructor[key] = $.isPlainObject(value) ? $.extend(true, {}, constructor[key], value) : value; 592 } 593 } 594 } 595 }; 596 597 // Add Bootstrap to the global Drupal object. 598 Drupal.bootstrap = Drupal.bootstrap || Bootstrap; 599 600})(window._, window.jQuery, window.Drupal, window.drupalSettings); 601; 602(function ($, _) { 603 604 /** 605 * @class Attributes 606 * 607 * Modifies attributes. 608 * 609 * @param {Object|Attributes} attributes 610 * An object to initialize attributes with. 611 */ 612 var Attributes = function (attributes) { 613 this.data = {}; 614 this.data['class'] = [];
615 this.merge(attributes); 616 }; 617 618 /** 619 * Renders the attributes object as a string to inject into an HTML element. 620 * 621 * @return {String} 622 * A rendered string suitable for inclusion in HTML markup. 623 */ 624 Attributes.prototype.toString = function () { 625 var output = ''; 626 var name, value; 627 var checkPlain = function (str) { 628 return str && str.toString().replace(/&/g, '&').replace(/"/g, '"').replace(/</g, '<').replace(/>/g, '>') || ''; 629 }; 630 var data = this.getData(); 631 for (name in data) { 632 if (!data.hasOwnProperty(name)) continue; 633 value = data[name]; 634 if (_.isFunction(value)) value = value(); 635 if (_.isObject(value)) value = _.values(value); 636 if (_.isArray(value)) value = value.join(' '); 637 output += ' ' + checkPlain(name) + '="' + checkPlain(value) + '"'; 638 } 639 return output; 640 }; 641 642 /** 643 * Renders the Attributes object as a plain object. 644 * 645 * @return {Object} 646 * A plain object suitable for inclusion in DOM elements. 647 */ 648 Attributes.prototype.toPlainObject = function () { 649 var object = {}; 650 var name, value; 651 var data = this.getData(); 652 for (name in data) { 653 if (!data.hasOwnProperty(name)) continue; 654 value = data[name]; 655 if (_.isFunction(value)) value = value(); 656 if (_.isObject(value)) value = _.values(value); 657 if (_.isArray(value)) value = value.join(' '); 658 object[name] = value; 659 } 660 return object; 661 }; 662 663 /** 664 * Add class(es) to the array. 665 * 666 * @param {string|Array} value 667 * An individual class or an array of classes to add. 668 * 669 * @return {Attributes} 670 * 671 * @chainable 672 */ 673 Attributes.prototype.addClass = function (value) { 674 var args = Array.prototype.slice.call(arguments); 675 this.data['class'] = this.sanitizeClasses(this.data['class'].concat(args)); 676 return this; 677 }; 678 679 /** 680 * Returns whether the requested attribute exists. 681 * 682 * @param {string} name 683 * An attribute name to check. 684 * 685 * @return {boolean} 686 * TRUE or FALSE 687 */ 688 Attributes.prototype.exists = function (name) { 689 return this.data[name] !== void(0) && this.data[name] !== null; 690 }; 691 692 /** 693 * Retrieve a specific attribute from the array. 694 * 695 * @param {string} name 696 * The specific attribute to retrieve. 697 * @param {*} defaultValue 698 * (optional) The default value to set if the attribute does not exist. 699 * 700 * @return {*} 701 * A specific attribute value, passed by reference. 702 */ 703 Attributes.prototype.get = function (name, defaultValue) { 704 if (!this.exists(name)) this.data[name] = defaultValue; 705 return this.data[name]; 706 }; 707 708 /** 709 * Retrieves a cloned copy of the internal attributes data object. 710 * 711 * @return {Object} 712 */ 713 Attributes.prototype.getData = function () { 714 return _.extend({}, this.data); 715 }; 716 717 /** 718 * Retrieves classes from the array. 719 * 720 * @return {Array} 721 * The classes array. 722 */ 723 Attributes.prototype.getClasses = function () { 724 return this.get('class', []); 725 }; 726 727 /** 728 * Indicates whether a class is present in the array. 729 * 730 * @param {string|Array} className 731 * The class(es) to search for. 732 * 733 * @return {boolean} 734 * TRUE or FALSE 735 */ 736 Attributes.prototype.hasClass = function (className) { 737 className = this.sanitizeClasses(Array.prototype.slice.call(arguments)); 738 var classes = this.getClasses(); 739 for (var i = 0, l = className.length; i < l; i++) { 740 // If one of the classes fails, immediately return false. 741 if (_.indexOf(classes, className[i]) === -1) { 742 return false; 743 } 744 } 745 return true; 746 }; 747 748 /** 749 * Merges multiple values into the array. 750 * 751 * @param {Attributes|Node|jQuery|Object} object 752 * An Attributes object with existing data, a Node DOM element, a jQuery 753 * instance or a plain object where the key is the attribute name and the 754 * value is the attribute value. 755 * @param {boolean} [recursive] 756 * Flag determining whether or not to recursively merge key/value pairs. 757 * 758 * @return {Attributes} 759 * 760 * @chainable 761 */ 762 Attributes.prototype.merge = function (object, recursive) { 763 // Immediately return if there is nothing to merge. 764 if (!object) { 765 return this; 766 } 767 768 // Get attributes from a jQuery element. 769 if (object instanceof $) { 770 object = object[0]; 771 } 772 773 // Get attributes from a DOM element. 774 if (object instanceof Node) { 775 object = Array.prototype.slice.call(object.attributes).reduce(function (attributes, attribute) { 776 attributes[attribute.name] = attribute.value; 777 return attributes; 778 }, {}); 779 } 780 // Get attributes from an Attributes instance. 781 else if (object instanceof Attributes) { 782 object = object.getData(); 783 } 784 // Otherwise, clone the object. 785 else { 786 object = _.extend({}, object); 787 } 788 789 // By this point, there should be a valid plain object. 790 if (!$.isPlainObject(object)) { 791 setTimeout(function () { 792 throw new Error('Passed object is not supported: ' + object); 793 }); 794 return this; 795 } 796 797 // Handle classes separately. 798 if (object && object['class'] !== void 0) { 799 this.addClass(object['class']); 800 delete object['class']; 801 } 802 803 if (recursive === void 0 || recursive) { 804 this.data = $.extend(true, {}, this.data, object); 805 } 806 else { 807 this.data = $.extend({}, this.data, object); 808 } 809 810 return this; 811 }; 812 813 /** 814 * Removes an attribute from the array. 815 * 816 * @param {string} name 817 * The name of the attribute to remove. 818 * 819 * @return {Attributes} 820 * 821 * @chainable 822 */ 823 Attributes.prototype.remove = function (name) { 824 if (this.exists(name)) delete this.data[name]; 825 return this; 826 }; 827 828 /** 829 * Removes a class from the attributes array. 830 * 831 * @param {...string|Array} className 832 * An individual class or an array of classes to remove. 833 * 834 * @return {Attributes} 835 * 836 * @chainable 837 */ 838 Attributes.prototype.removeClass = function (className) { 839 var remove = this.sanitizeClasses(Array.prototype.slice.apply(arguments)); 840 this.data['class'] = _.without(this.getClasses(), remove); 841 return this; 842 }; 843 844 /** 845 * Replaces a class in the attributes array. 846 * 847 * @param {string} oldValue 848 * The old class to remove. 849 * @param {string} newValue 850 * The new class. It will not be added if the old class does not exist. 851 * 852 * @return {Attributes} 853 * 854 * @chainable 855 */ 856 Attributes.prototype.replaceClass = function (oldValue, newValue) { 857 var classes = this.getClasses(); 858 var i = _.indexOf(this.sanitizeClasses(oldValue), classes); 859 if (i >= 0) { 860 classes[i] = newValue; 861 this.set('class', classes); 862 } 863 return this; 864 }; 865 866 /** 867 * Ensures classes are flattened into a single is an array and sanitized. 868 * 869 * @param {...String|Array} classes 870 * The class or classes to sanitize. 871 * 872 * @return {Array} 873 * A sanitized array of classes. 874 */ 875 Attributes.prototype.sanitizeClasses = function (classes) { 876 return _.chain(Array.prototype.slice.call(arguments)) 877 // Flatten in case there's a mix of strings and arrays. 878 .flatten() 879 880 // Split classes that may have been added with a space as a separator. 881 .map(function (string) { 882 return string.split(' '); 883 }) 884 885 // Flatten again since it was just split into arrays. 886 .flatten() 887 888 // Filter out empty items. 889 .filter() 890
891 // Clean the class to ensure it's a valid class name. 892 .map(function (value) { 893 return Attributes.cleanClass(value); 894 }) 895 896 // Ensure classes are unique. 897 .uniq() 898 899 // Retrieve the final value. 900 .value(); 901 }; 902 903 /** 904 * Sets an attribute on the array. 905 * 906 * @param {string} name 907 * The name of the attribute to set. 908 * @param {*} value 909 * The value of the attribute to set. 910 * 911 * @return {Attributes} 912 * 913 * @chainable 914 */ 915 Attributes.prototype.set = function (name, value) { 916 var obj = $.isPlainObject(name) ? name : {}; 917 if (typeof name === 'string') { 918 obj[name] = value; 919 } 920 return this.merge(obj); 921 }; 922 923 /** 924 * Prepares a string for use as a CSS identifier (element, class, or ID name). 925 * 926 * Note: this is essentially a direct copy from 927 * \Drupal\Component\Utility\Html::cleanCssIdentifier 928 * 929 * @param {string} identifier 930 * The identifier to clean. 931 * @param {Object} [filter] 932 * An object of string replacements to use on the identifier. 933 * 934 * @return {string} 935 * The cleaned identifier. 936 */ 937 Attributes.cleanClass = function (identifier, filter) { 938 filter = filter || { 939 ' ': '-', 940 '_': '-', 941 '/': '-', 942 '[': '-', 943 ']': '' 944 }; 945 946 identifier = identifier.toLowerCase(); 947 948 if (filter['__'] === void 0) { 949 identifier = identifier.replace('__', '#DOUBLE_UNDERSCORE#'); 950 } 951 952 identifier = identifier.replace(Object.keys(filter), Object.keys(filter).map(function(key) { return filter[key]; })); 953 954 if (filter['__'] === void 0) { 955 identifier = identifier.replace('#DOUBLE_UNDERSCORE#', '__'); 956 } 957
958 identifier = identifier.replace(/[^\u002D\u0030-\u0039\u0041-\u005A\u005F\u0061-\u007A\u00A1-\uFFFF]/g, ''); 959 identifier = identifier.replace(['/^[0-9]/', '/^(-[0-9])|^(--)/'], ['_', '__']); 960 961 return identifier; 962 }; 963 964 /** 965 * Creates an Attributes instance. 966 * 967 * @param {object|Attributes} [attributes] 968 * An object to initialize attributes with. 969 * 970 * @return {Attributes} 971 * An Attributes instance. 972 * 973 * @constructor 974 */ 975 Attributes.create = function (attributes) { 976 return new Attributes(attributes); 977 }; 978 979 window.Attributes = Attributes; 980 981})(window.jQuery, window._); 982; 983/** 984 * @file 985 * Theme hooks for the Drupal Bootstrap base theme. 986 */ 987(function ($, Drupal, Bootstrap, Attributes) { 988 989 /** 990 * Fallback for theming an icon if the Icon API module is not installed. 991 */ 992 if (!Drupal.icon) Drupal.icon = { bundles: {} }; 993 if (!Drupal.theme.icon || Drupal.theme.prototype.icon) { 994 $.extend(Drupal.theme, /** @lends Drupal.theme */ { 995 /** 996 * Renders an icon. 997 * 998 * @param {string} bundle 999 * The bundle which the icon belongs to. 1000 * @param {string} icon 1001 * The name of the icon to render. 1002 * @param {object|Attributes} [attributes] 1003 * An object of attributes to also apply to the icon. 1004 * 1005 * @returns {string} 1006 */ 1007 icon: function (bundle, icon, attributes) { 1008 if (!Drupal.icon.bundles[bundle]) return ''; 1009 attributes = Attributes.create(attributes).addClass('icon').set('aria-hidden', 'true'); 1010 icon = Drupal.icon.bundles[bundle](icon, attributes); 1011 return '<span' + attributes + '></span>'; 1012 } 1013 }); 1014 } 1015 1016 /** 1017 * Callback for modifying an icon in the "bootstrap" icon bundle. 1018 * 1019 * @param {string} icon 1020 * The icon being rendered. 1021 * @param {Attributes} attributes 1022 * Attributes object for the icon. 1023 */ 1024 Drupal.icon.bundles.bootstrap = function (icon, attributes) { 1025 attributes.addClass(['glyphicon', 'glyphicon-' + icon]); 1026 }; 1027 1028 /** 1029 * Add necessary theming hooks. 1030 */ 1031 $.extend(Drupal.theme, /** @lends Drupal.theme */ { 1032 1033 /** 1034 * Renders a Bootstrap AJAX glyphicon throbber. 1035 * 1036 * @returns {string} 1037 */ 1038 ajaxThrobber: function () { 1039 return Drupal.theme('bootstrapIcon', 'refresh', {'class': ['ajax-throbber', 'glyphicon-spin'] }); 1040 }, 1041 1042 /** 1043 * Renders a button element. 1044 * 1045 * @param {object|Attributes} attributes 1046 * An object of attributes to apply to the button. If it contains one of: 1047 * - value: The label of the button. 1048 * - context: The context type of Bootstrap button, can be one of: 1049 * - default 1050 * - primary 1051 * - success 1052 * - info 1053 * - warning 1054 * - danger 1055 * - link 1056 * 1057 * @returns {string} 1058 */ 1059 button: function (attributes) { 1060 attributes = Attributes.create(attributes).addClass('btn'); 1061 var context = attributes.get('context', 'default'); 1062 var label = attributes.get('value', ''); 1063 attributes.remove('context').remove('value'); 1064 if (!attributes.hasClass(['btn-default', 'btn-primary', 'btn-success', 'btn-info', 'btn-warning', 'btn-danger', 'btn-link'])) { 1065 attributes.addClass('btn-' + Bootstrap.checkPlain(context)); 1066 } 1067 1068 // Attempt to, intelligently, provide a default button "type". 1069 if (!attributes.exists('type')) { 1070 attributes.set('type', attributes.hasClass('form-submit') ? 'submit' : 'button'); 1071 } 1072 1073 return '<button' + attributes + '>' + label + '</button>'; 1074 }, 1075 1076 /** 1077 * Alias for "button" theme hook. 1078 * 1079 * @param {object|Attributes} attributes 1080 * An object of attributes to apply to the button. 1081 * 1082 * @see Drupal.theme.button() 1083 * 1084 * @returns {string} 1085 */ 1086 btn: function (attributes) { 1087 return Drupal.theme('button', attributes); 1088 }, 1089 1090 /** 1091 * Renders a button block element. 1092 * 1093 * @param {object|Attributes} attributes 1094 * An object of attributes to apply to the button. 1095 * 1096 * @see Drupal.theme.button() 1097 * 1098 * @returns {string} 1099 */ 1100 'btn-block': function (attributes) { 1101 return Drupal.theme('button', Attributes.create(attributes).addClass('btn-block')); 1102 }, 1103 1104 /**
1105 * Renders a large button element. 1106 * 1107 * @param {object|Attributes} attributes 1108 * An object of attributes to apply to the button. 1109 * 1110 * @see Drupal.theme.button() 1111 * 1112 * @returns {string} 1113 */ 1114 'btn-lg': function (attributes) { 1115 return Drupal.theme('button', Attributes.create(attributes).addClass('btn-lg')); 1116 }, 1117 1118 /** 1119 * Renders a small button element. 1120 * 1121 * @param {object|Attributes} attributes 1122 * An object of attributes to apply to the button. 1123 * 1124 * @see Drupal.theme.button() 1125 * 1126 * @returns {string} 1127 */ 1128 'btn-sm': function (attributes) { 1129 return Drupal.theme('button', Attributes.create(attributes).addClass('btn-sm')); 1130 }, 1131 1132 /** 1133 * Renders an extra small button element. 1134 * 1135 * @param {object|Attributes} attributes 1136 * An object of attributes to apply to the button. 1137 * 1138 * @see Drupal.theme.button() 1139 * 1140 * @returns {string} 1141 */ 1142 'btn-xs': function (attributes) { 1143 return Drupal.theme('button', Attributes.create(attributes).addClass('btn-xs')); 1144 }, 1145 1146 /** 1147 * Renders a glyphicon. 1148 * 1149 * @param {string} name 1150 * The name of the glyphicon. 1151 * @param {object|Attributes} [attributes] 1152 * An object of attributes to apply to the icon. 1153 * 1154 * @returns {string} 1155 */ 1156 bootstrapIcon: function (name, attributes) { 1157 return Drupal.theme('icon', 'bootstrap', name, attributes); 1158 } 1159 1160 }); 1161 1162})(window.jQuery, window.Drupal, window.Drupal.bootstrap, window.Attributes); 1163; 1164/** 1165* DO NOT EDIT THIS FILE. 1166* See the following change record for more information, 1167* https://www.drupal.org/node/2815083 1168* @preserve 1169**/ 1170(function ($, Drupal) { 1171 Drupal.theme.progressBar = function (id) { 1172 return "<div id=\"".concat(id, "\" class=\"progress\" aria-live=\"polite\">") + '<div class="progress__label"> </div>' + '<div class="progress__track"><div class="progress__bar"></div></div>' + '<div class="progress__percentage"></div>' + '<div class="progress__description"> </div>' + '</div>'; 1173 }; 1174 Drupal.ProgressBar = function (id, updateCallback, method, errorCallback) { 1175 this.id = id; 1176 this.method = method || 'GET'; 1177 this.updateCallback = updateCallback; 1178 this.errorCallback = errorCallback; 1179 this.element = $(Drupal.theme('progressBar', id)); 1180 }; 1181 $.extend(Drupal.ProgressBar.prototype, { 1182 setProgress: function setProgress(percentage, message, label) { 1183 if (percentage >= 0 && percentage <= 100) { 1184 $(this.element).find('div.progress__bar').css('width', "".concat(percentage, "%")); 1185 $(this.element).find('div.progress__percentage').html("".concat(percentage, "%")); 1186 } 1187 $('div.progress__description', this.element).html(message); 1188 $('div.progress__label', this.element).html(label); 1189 if (this.updateCallback) { 1190 this.updateCallback(percentage, message, this); 1191 } 1192 }, 1193 startMonitoring: function startMonitoring(uri, delay) { 1194 this.delay = delay; 1195 this.uri = uri; 1196 this.sendPing(); 1197 }, 1198 stopMonitoring: function stopMonitoring() { 1199 clearTimeout(this.timer); 1200 this.uri = null; 1201 }, 1202 sendPing: function sendPing() { 1203 if (this.timer) { 1204 clearTimeout(this.timer); 1205 } 1206 if (this.uri) { 1207 var pb = this; 1208 var uri = this.uri; 1209 if (uri.indexOf('?') === -1) { 1210 uri += '?'; 1211 } else { 1212 uri += '&'; 1213 } 1214 uri += '_format=json'; 1215 $.ajax({ 1216 type: this.method, 1217 url: uri, 1218 data: '', 1219 dataType: 'json', 1220 success: function success(progress) { 1221 if (progress.status === 0) { 1222 pb.displayError(progress.data); 1223 return; 1224 } 1225 pb.setProgress(progress.percentage, progress.message, progress.label); 1226 pb.timer = setTimeout(function () { 1227 pb.sendPing(); 1228 }, pb.delay); 1229 }, 1230 error: function error(xmlhttp) { 1231 var e = new Drupal.AjaxError(xmlhttp, pb.uri); 1232 pb.displayError("<pre>".concat(e.message, "</pre>")); 1233 } 1234 }); 1235 } 1236 }, 1237 displayError: function displayError(string) { 1238 var error = $('<div class="messages messages--error"></div>').html(string); 1239 $(this.element).before(error).hide(); 1240 if (this.errorCallback) { 1241 this.errorCallback(this); 1242 } 1243 } 1244 }); 1245})(jQuery, Drupal);; 1246/** 1247 * @file 1248 * Extends methods from core/misc/progress.js. 1249 */ 1250 1251(function ($, Drupal) { 1252 1253 'use strict'; 1254 1255 /** 1256 * Theme function for the progress bar. 1257 * 1258 * @param {string} id 1259 * 1260 * @return {string} 1261 * The HTML for the progress bar. 1262 */ 1263 Drupal.theme.progressBar = function (id) { 1264 return '<div class="progress-wrapper" aria-live="polite">' + 1265 '<div class="message"></div>'+ 1266 '<div id ="' + id + '" class="progress progress-striped active">' + 1267 '<div class="progress-bar" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0">' + 1268 '<span class="percentage"></span>' + 1269 '</div>' + 1270 '</div>' + 1271 '<div class="progress-label"></div>' + 1272 '</div>'; 1273 }; 1274 1275 $.extend(Drupal.ProgressBar.prototype, /** @lends Drupal.ProgressBar */{ 1276 1277 /** 1278 * Set the percentage and status message for the progressbar. 1279 * 1280 * @param {number} percentage 1281 * @param {string} message 1282 * @param {string} label 1283 */ 1284 setProgress: function (percentage, message, label) { 1285 if (percentage >= 0 && percentage <= 100) {
1286 $(this.element).find('.progress-bar').css('width', percentage + '%').attr('aria-valuenow', percentage); 1287 $(this.element).find('.percentage').html(percentage + '%'); 1288 } 1289 if (message) { 1290 // Remove the unnecessary whitespace at the end of the message. 1291 message = message.replace(/<br\/> |\s*$/, ''); 1292 1293 $('.message', this.element).html(message); 1294 } 1295 if (label) { 1296 $('.progress-label', this.element).html(label); 1297 } 1298 if (this.updateCallback) { 1299 this.updateCallback(percentage, message, this); 1300 } 1301 }, 1302 1303 /** 1304 * Display errors on the page. 1305 * 1306 * @param {string} string 1307 */ 1308 displayError: function (string) { 1309 var error = $('<div class="alert alert-block alert-error"><button class="close" data-dismiss="alert">×</button><h4>' + Drupal.t('Error message') + '</h4></div>').append(string); 1310 $(this.element).before(error).hide(); 1311 1312 if (this.errorCallback) { 1313 this.errorCallback(this); 1314 } 1315 } 1316 }); 1317 1318})(jQuery, Drupal); 1319; 1320/** 1321* DO NOT EDIT THIS FILE. 1322* See the following change record for more information, 1323* https://www.drupal.org/node/2815083 1324* @preserve 1325**/ 1326(function ($, once) { 1327 var deprecatedMessageSuffix = "is deprecated in Drupal 9.3.0 and will be removed in Drupal 10.0.0. Use the core/once library instead. See https://www.drupal.org/node/3158256"; 1328 var originalJQOnce = $.fn.once; 1329 var originalJQRemoveOnce = $.fn.removeOnce; 1330 $.fn.once = function jQueryOnce(id) { 1331 Drupal.deprecationError({ 1332 message: "jQuery.once() ".concat(deprecatedMessageSuffix) 1333 }); 1334 return originalJQOnce.apply(this, [id]); 1335 }; 1336 $.fn.removeOnce = function jQueryRemoveOnce(id) { 1337 Drupal.deprecationError({ 1338 message: "jQuery.removeOnce() ".concat(deprecatedMessageSuffix) 1339 }); 1340 return originalJQRemoveOnce.apply(this, [id]); 1341 }; 1342 var drupalOnce = once; 1343 function augmentedOnce(id, selector, context) { 1344 originalJQOnce.apply($(selector, context), [id]); 1345 return drupalOnce(id, selector, context); 1346 } 1347 function remove(id, selector, context) { 1348 originalJQRemoveOnce.apply($(selector, context), [id]); 1349 return drupalOnce.remove(id, selector, context); 1350 } 1351 window.once = Object.assign(augmentedOnce, drupalOnce, { 1352 remove: remove 1353 }); 1354})(jQuery, once);; 1355loadjs=function(){var h=function(){},c={},u={},f={};function o(e,n){if(e){var r=f[e];if(u[e]=n,r)for(;r.length;)r[0](e,n),r.splice(0,1)}}function l(e,n){e.call&&(e={success:e}),n.length?(e.error||h)(n):(e.success||h)(e)}function d(r,t,s,i){var c,o,e=document,n=s.async,u=(s.numRetries||0)+1,f=s.before||h,l=r.replace(/[\?|#].*$/,""),a=r.replace(/^(css|img)!/,"");i=i||0,/(^css!|\.css$)/.test(l)?((o=e.createElement("link")).rel="stylesheet",o.href=a,(c="hideFocus"in o)&&o.relList&&(c=0,o.rel="preload",o.as="style")):/(^img!|\.(png|gif|jpg|svg|webp)$)/.test(l)?(o=e.createElement("img")).src=a:((o=e.createElement("script")).src=r,o.async=void 0===n||n),!(o.onload=o.onerror=o.onbeforeload=function(e){var n=e.type[0];if(c)try{o.sheet.cssText.length||(n="e")}catch(e){18!=e.code&&(n="e")}if("e"==n){if((i+=1)<u)return d(r,t,s,i)}else if("preload"==o.rel&&"style"==o.as)return o.rel="stylesheet";t(r,n,e.defaultPrevented)})!==f(r,o)&&e.head.appendChild(o)}function r(e,n,r){var t,s;if(n&&n.trim&&(t=n),s=(t?r:n)||{},t){if(t in c)throw"LoadJS";c[t]=!0}function i(n,r){!function(e,t,n){var r,s,i=(e=e.push?e:[e]).length,c=i,o=[];for(r=function(e,n,r){if("e"==n&&o.push(e),"b"==n){if(!r)return;o.push(e)}--i||t(o)},s=0;s<c;s++)d(e[s],r,n)}(e,function(e){l(s,e),n&&l({success:n,error:r},e),o(t,e)},s)}if(s.returnPromise)return new Promise(i);i()}return r.ready=function(e,n){return function(e,r){e=e.push?e:[e];var n,t,s,i=[],c=e.length,o=c;
1355for(n=function(e,n){n.length&&i.push(e),--o||r(i)};c--;)t=e[c],(s=u[t])?n(t,s):(f[t]=f[t]||[]).push(n)}(e,function(e){l(n,e)}),r},r.done=function(e){o(e,[])},r.reset=function(){c={},u={},f={}},r.isDefined=function(e){return e in c},r}();; 1356/** 1357* DO NOT EDIT THIS FILE. 1358* See the following change record for more information, 1359* https://www.drupal.org/node/2815083 1360* @preserve 1361**/ 1362function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); } 1363function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } 1364function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } 1365function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); } 1366function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); } 1367function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } 1368(function ($, window, Drupal, drupalSettings, loadjs, _ref) { 1369 var isFocusable = _ref.isFocusable, 1370 tabbable = _ref.tabbable; 1371 Drupal.behaviors.AJAX = { 1372 attach: function attach(context, settings) { 1373 function loadAjaxBehavior(base) { 1374 var elementSettings = settings.ajax[base]; 1375 if (typeof elementSettings.selector === 'undefined') { 1376 elementSettings.selector = "#".concat(base); 1377 } 1378 once('drupal-ajax', $(elementSettings.selector)).forEach(function (el) { 1379 elementSettings.element = el; 1380 elementSettings.base = base; 1381 Drupal.ajax(elementSettings); 1382 }); 1383 } 1384 Object.keys(settings.ajax || {}).forEach(function (base) { 1385 return loadAjaxBehavior(base); 1386 }); 1387 Drupal.ajax.bindAjaxLinks(document.body); 1388 once('ajax', '.use-ajax-submit').forEach(function (el) { 1389 var elementSettings = {}; 1390 elementSettings.url = $(el.form).attr('action'); 1391 elementSettings.setClick = true; 1392 elementSettings.event = 'click'; 1393 elementSettings.progress = { 1394 type: 'throbber' 1395 }; 1396 elementSettings.base = el.id; 1397 elementSettings.element = el; 1398 Drupal.ajax(elementSettings); 1399 }); 1400 }, 1401 detach: function detach(context, settings, trigger) { 1402 if (trigger === 'unload') { 1403 Drupal.ajax.expired().forEach(function (instance) { 1404 Drupal.ajax.instances[instance.instanceIndex] = null; 1405 }); 1406 } 1407 } 1408 }; 1409 Drupal.AjaxError = function (xmlhttp, uri, customMessage) { 1410 var statusCode; 1411 var statusText; 1412 var responseText; 1413 if (xmlhttp.status) { 1414 statusCode = "\n".concat(Drupal.t('An AJAX HTTP error occurred.'), "\n").concat(Drupal.t('HTTP Result Code: !status', { 1415 '!status': xmlhttp.status 1416 })); 1417 } else { 1418 statusCode = "\n".concat(Drupal.t('An AJAX HTTP request terminated abnormally.')); 1419 } 1420 statusCode += "\n".concat(Drupal.t('Debugging information follows.')); 1421 var pathText = "\n".concat(Drupal.t('Path: !uri', { 1422 '!uri': uri 1423 })); 1424 statusText = ''; 1425 try { 1426 statusText = "\n".concat(Drupal.t('StatusText: !statusText', { 1427 '!statusText': xmlhttp.statusText.trim() 1428 })); 1429 } catch (e) {} 1430 responseText = ''; 1431 try { 1432 responseText = "\n".concat(Drupal.t('ResponseText: !responseText', { 1433 '!responseText': xmlhttp.responseText.trim() 1434 })); 1435 } catch (e) {} 1436 responseText = responseText.replace(/<("[^"]*"|'[^']*'|[^'">])*>/gi, ''); 1437 responseText = responseText.replace(/[\n]+\s+/g, '\n'); 1438 var readyStateText = xmlhttp.status === 0 ? "\n".concat(Drupal.t('ReadyState: !readyState', { 1439 '!readyState': xmlhttp.readyState 1440 })) : ''; 1441 customMessage = customMessage ? "\n".concat(Drupal.t('CustomMessage: !customMessage', { 1442 '!customMessage': customMessage 1443 })) : ''; 1444 this.message = statusCode + pathText + statusText + customMessage + responseText + readyStateText; 1445 this.name = 'AjaxError'; 1446 }; 1447 Drupal.AjaxError.prototype = new Error(); 1448 Drupal.AjaxError.prototype.constructor = Drupal.AjaxError; 1449 Drupal.ajax = function (settings) { 1450 if (arguments.length !== 1) { 1451 throw new Error('Drupal.ajax() function must be called with one configuration object only'); 1452 } 1453 var base = settings.base || false; 1454 var element = settings.element || false; 1455 delete settings.base; 1456 delete settings.element; 1457 if (!settings.progress && !element) { 1458 settings.progress = false;
1459 } 1460 var ajax = new Drupal.Ajax(base, element, settings); 1461 ajax.instanceIndex = Drupal.ajax.instances.length; 1462 Drupal.ajax.instances.push(ajax); 1463 return ajax; 1464 }; 1465 Drupal.ajax.instances = []; 1466 Drupal.ajax.expired = function () { 1467 return Drupal.ajax.instances.filter(function (instance) { 1468 return instance && instance.element !== false && !document.body.contains(instance.element); 1469 }); 1470 }; 1471 Drupal.ajax.bindAjaxLinks = function (element) { 1472 once('ajax', '.use-ajax', element).forEach(function (ajaxLink) { 1473 var $linkElement = $(ajaxLink); 1474 var elementSettings = { 1475 progress: { 1476 type: 'throbber' 1477 }, 1478 dialogType: $linkElement.data('dialog-type'), 1479 dialog: $linkElement.data('dialog-options'), 1480 dialogRenderer: $linkElement.data('dialog-renderer'), 1481 base: $linkElement.attr('id'), 1482 element: ajaxLink 1483 }; 1484 var href = $linkElement.attr('href'); 1485 if (href) { 1486 elementSettings.url = href; 1487 elementSettings.event = 'click'; 1488 } 1489 Drupal.ajax(elementSettings); 1490 }); 1491 }; 1492 Drupal.Ajax = function (base, element, elementSettings) { 1493 var defaults = { 1494 event: element ? 'mousedown' : null, 1495 keypress: true, 1496 selector: base ? "#".concat(base) : null, 1497 effect: 'none', 1498 speed: 'none', 1499 method: 'replaceWith', 1500 progress: { 1501 type: 'throbber', 1502 message: Drupal.t('Please wait...') 1503 }, 1504 submit: { 1505 js: true 1506 } 1507 }; 1508 $.extend(this, defaults, elementSettings); 1509 this.commands = new Drupal.AjaxCommands(); 1510 this.instanceIndex = false; 1511 if (this.wrapper) { 1512 this.wrapper = "#".concat(this.wrapper); 1513 } 1514 this.element = element; 1515 this.element_settings = elementSettings; 1516 this.elementSettings = elementSettings; 1517 if (this.element && this.element.form) { 1518 this.$form = $(this.element.form); 1519 } 1520 if (!this.url) { 1521 var $element = $(this.element); 1522 if ($element.is('a')) { 1523 this.url = $element.attr('href'); 1524 } else if (this.element && element.form) { 1525 this.url = this.$form.attr('action'); 1526 } 1527 } 1528 var originalUrl = this.url; 1529 this.url = this.url.replace(/\/nojs(\/|$|\?|#)/, '/ajax$1'); 1530 if (drupalSettings.ajaxTrustedUrl[originalUrl]) { 1531 drupalSettings.ajaxTrustedUrl[this.url] = true; 1532 } 1533 var ajax = this; 1534 ajax.options = { 1535 url: ajax.url, 1536 data: ajax.submit, 1537 isInProgress: function isInProgress() { 1538 return ajax.ajaxing; 1539 }, 1540 beforeSerialize: function beforeSerialize(elementSettings, options) { 1541 return ajax.beforeSerialize(elementSettings, options); 1542 }, 1543 beforeSubmit: function beforeSubmit(formValues, elementSettings, options) { 1544 ajax.ajaxing = true; 1545 return ajax.beforeSubmit(formValues, elementSettings, options); 1546 }, 1547 beforeSend: function beforeSend(xmlhttprequest, options) { 1548 ajax.ajaxing = true; 1549 return ajax.beforeSend(xmlhttprequest, options); 1550 }, 1551 success: function success(response, status, xmlhttprequest) { 1552 var _this = this; 1553 if (typeof response === 'string') { 1554 response = $.parseJSON(response); 1555 } 1556 if (response !== null && !drupalSettings.ajaxTrustedUrl[ajax.url]) { 1557 if (xmlhttprequest.getResponseHeader('X-Drupal-Ajax-Token') !== '1') { 1558 var customMessage = Drupal.t('The response failed verification so will not be processed.'); 1559 return ajax.error(xmlhttprequest, ajax.url, customMessage); 1560 } 1561 } 1562 return Promise.resolve(ajax.success(response, status)).then(function () { 1563 ajax.ajaxing = false; 1564 $(document).trigger('ajaxSuccess', [xmlhttprequest, _this]); 1565 $(document).trigger('ajaxComplete', [xmlhttprequest, _this]); 1566 if (--$.active === 0) { 1567 $(document).trigger('ajaxStop'); 1568 } 1569 }); 1570 }, 1571 error: function error(xmlhttprequest, status, _error) { 1572 ajax.ajaxing = false; 1573 }, 1574 complete: function complete(xmlhttprequest, status) { 1575 if (status === 'error' || status === 'parsererror') { 1576 return ajax.error(xmlhttprequest, ajax.url); 1577 } 1578 }, 1579 dataType: 'json', 1580 jsonp: false, 1581 type: 'POST' 1582 }; 1583 if (elementSettings.dialog) { 1584 ajax.options.data.dialogOptions = elementSettings.dialog; 1585 } 1586 if (ajax.options.url.indexOf('?') === -1) { 1587 ajax.options.url += '?'; 1588 } else { 1589 ajax.options.url += '&'; 1590 } 1591 var wrapper = "drupal_".concat(elementSettings.dialogType || 'ajax'); 1592 if (elementSettings.dialogRenderer) { 1593 wrapper += ".".concat(elementSettings.dialogRenderer); 1594 } 1595 ajax.options.url += "".concat(Drupal.ajax.WRAPPER_FORMAT, "=").concat(wrapper); 1596 $(ajax.element).on(elementSettings.event, function (event) { 1597 if (!drupalSettings.ajaxTrustedUrl[ajax.url] && !Drupal.url.isLocal(ajax.url)) { 1598 throw new Error(Drupal.t('The callback URL is not local and n
1598ot trusted: !url', { 1599 '!url': ajax.url 1600 })); 1601 } 1602 return ajax.eventResponse(this, event); 1603 }); 1604 if (elementSettings.keypress) { 1605 $(ajax.element).on('keypress', function (event) { 1606 return ajax.keypressResponse(this, event); 1607 }); 1608 } 1609 if (elementSettings.prevent) { 1610 $(ajax.element).on(elementSettings.prevent, false); 1611 } 1612 }; 1613 Drupal.ajax.WRAPPER_FORMAT = '_wrapper_format'; 1614 Drupal.Ajax.AJAX_REQUEST_PARAMETER = '_drupal_ajax'; 1615 Drupal.Ajax.prototype.execute = function () { 1616 if (this.ajaxing) { 1617 return; 1618 } 1619 try { 1620 this.beforeSerialize(this.element, this.options); 1621 return $.ajax(this.options); 1622 } catch (e) { 1623 this.ajaxing = false; 1624 window.alert("An error occurred while attempting to process ".concat(this.options.url, ": ").concat(e.message)); 1625 return $.Deferred().reject(); 1626 } 1627 }; 1628 Drupal.Ajax.prototype.keypressResponse = function (element, event) { 1629 var ajax = this; 1630 if (event.which === 13 || event.which === 32 && element.type !== 'text' && element.type !== 'textarea' && element.type !== 'tel' && element.type !== 'number') { 1631 event.preventDefault(); 1632 event.stopPropagation(); 1633 $(element).trigger(ajax.elementSettings.event); 1634 } 1635 }; 1636 Drupal.Ajax.prototype.eventResponse = function (element, event) { 1637 event.preventDefault(); 1638 event.stopPropagation(); 1639 var ajax = this; 1640 if (ajax.ajaxing) { 1641 return; 1642 } 1643 try { 1644 if (ajax.$form) { 1645 if (ajax.setClick) { 1646 element.form.clk = element; 1647 } 1648 ajax.$form.ajaxSubmit(ajax.options); 1649 } else { 1650 ajax.beforeSerialize(ajax.element, ajax.options); 1651 $.ajax(ajax.options); 1652 } 1653 } catch (e) { 1654 ajax.ajaxing = false; 1655 window.alert("An error occurred while attempting to process ".concat(ajax.options.url, ": ").concat(e.message)); 1656 } 1657 }; 1658 Drupal.Ajax.prototype.beforeSerialize = function (element, options) { 1659 if (this.$form && document.body.contains(this.$form.get(0))) { 1660 var settings = this.settings || drupalSettings; 1661 Drupal.detachBehaviors(this.$form.get(0), settings, 'serialize'); 1662 } 1663 options.data[Drupal.Ajax.AJAX_REQUEST_PARAMETER] = 1; 1664 var pageState = drupalSettings.ajaxPageState; 1665 options.data['ajax_page_state[theme]'] = pageState.theme; 1666 options.data['ajax_page_state[theme_token]'] = pageState.theme_token; 1667 options.data['ajax_page_state[libraries]'] = pageState.libraries; 1668 }; 1669 Drupal.Ajax.prototype.beforeSubmit = function (formValues, element, options) {}; 1670 Drupal.Ajax.prototype.beforeSend = function (xmlhttprequest, options) { 1671 if (this.$form) { 1672 options.extraData = options.extraData || {}; 1673 options.extraData.ajax_iframe_upload = '1'; 1674 var v = $.fieldValue(this.element); 1675 if (v !== null) { 1676 options.extraData[this.element.name] = v; 1677 } 1678 } 1679 $(this.element).prop('disabled', true); 1680 if (!this.progress || !this.progress.type) { 1681 return; 1682 } 1683 var progressIndicatorMethod = "setProgressIndicator".concat(this.progress.type.slice(0, 1).toUpperCase()).concat(this.progress.type.slice(1).toLowerCase()); 1684 if (progressIndicatorMethod in this && typeof this[progressIndicatorMethod] === 'function') { 1685 this[progressIndicatorMethod].call(this); 1686 } 1687 }; 1688 Drupal.theme.ajaxProgressThrobber = function (message) { 1689 var messageMarkup = typeof message === 'string' ? Drupal.theme('ajaxProgressMessage', message) : ''; 1690 var throbber = '<div class="throbber"> </div>'; 1691 return "<div class=\"ajax-progress ajax-progress-throbber\">".concat(throbber).concat(messageMarkup, "</div>"); 1692 }; 1693 Drupal.theme.ajaxProgressIndicatorFullscreen = function () { 1694 return '<div class="ajax-progress ajax-progress-fullscreen"> </div>'; 1695 }; 1696 Drupal.theme.ajaxProgressMessage = function (message) { 1697 return "<div class=\"message\">".concat(message, "</div>"); 1698 }; 1699 Drupal.theme.ajaxProgressBar = function ($element) { 1700 return $('<div class="ajax-progress ajax-progress-bar"></div>').append($element); 1701 }; 1702 Drupal.Ajax.prototype.setProgressIndicatorBar = function () { 1703 var progressBar = new Drupal.ProgressBar("ajax-progress-".concat(this.element.i
1703d), $.noop, this.progress.method, $.noop); 1704 if (this.progress.message) { 1705 progressBar.setProgress(-1, this.progress.message); 1706 } 1707 if (this.progress.url) { 1708 progressBar.startMonitoring(this.progress.url, this.progress.interval || 1500); 1709 } 1710 this.progress.element = $(Drupal.theme('ajaxProgressBar', progressBar.element)); 1711 this.progress.object = progressBar; 1712 $(this.element).after(this.progress.element); 1713 }; 1714 Drupal.Ajax.prototype.setProgressIndicatorThrobber = function () { 1715 this.progress.element = $(Drupal.theme('ajaxProgressThrobber', this.progress.message)); 1716 $(this.element).after(this.progress.element); 1717 }; 1718 Drupal.Ajax.prototype.setProgressIndicatorFullscreen = function () { 1719 this.progress.element = $(Drupal.theme('ajaxProgressIndicatorFullscreen')); 1720 $('body').append(this.progress.element); 1721 }; 1722 Drupal.Ajax.prototype.commandExecutionQueue = function (response, status) { 1723 var _this2 = this; 1724 var ajaxCommands = this.commands; 1725 return Object.keys(response || {}).reduce(function (executionQueue, key) { 1726 return executionQueue.then(function () { 1727 var command = response[key].command; 1728 if (command && ajaxCommands[command]) { 1729 return ajaxCommands[command](_this2, response[key], status); 1730 } 1731 }); 1732 }, Promise.resolve()); 1733 }; 1734 Drupal.Ajax.prototype.success = function (response, status) { 1735 var _this3 = this; 1736 if (this.progress.element) { 1737 $(this.progress.element).remove(); 1738 } 1739 if (this.progress.object) { 1740 this.progress.object.stopMonitoring(); 1741 } 1742 $(this.element).prop('disabled', false); 1743 var elementParents = $(this.element).parents('[data-drupal-selector]').addBack().toArray(); 1744 var focusChanged = Object.keys(response || {}).some(function (key) { 1745 var _response$key = response[key], 1746 command = _response$key.command, 1747 method = _response$key.method; 1748 return command === 'focusFirst' || command === 'invoke' && method === 'focus'; 1749 }); 1750 return this.commandExecutionQueue(response, status).then(function () { 1751 if (!focusChanged && _this3.element && !$(_this3.element).data('disable-refocus')) { 1752 var target = false;
1753 for (var n = elementParents.length - 1; !target && n >= 0; n--) { 1754 target = document.querySelector("[data-drupal-selector=\"".concat(elementParents[n].getAttribute('data-drupal-selector'), "\"]")); 1755 } 1756 if (target) { 1757 $(target).trigger('focus'); 1758 } 1759 } 1760 if (_this3.$form && document.body.contains(_this3.$form.get(0))) { 1761 var settings = _this3.settings || drupalSettings; 1762 Drupal.attachBehaviors(_this3.$form.get(0), settings); 1763 } 1764 _this3.settings = null; 1765 }).catch(function (error) { 1766 return console.error(Drupal.t('An error occurred during the execution of the Ajax response: !error', { 1767 '!error': error 1768 })); 1769 }); 1770 }; 1771 Drupal.Ajax.prototype.getEffect = function (response) { 1772 var type = response.effect || this.effect; 1773 var speed = response.speed || this.speed; 1774 var effect = {}; 1775 if (type === 'none') { 1776 effect.showEffect = 'show'; 1777 effect.hideEffect = 'hide'; 1778 effect.showSpeed = ''; 1779 } else if (type === 'fade') { 1780 effect.showEffect = 'fadeIn'; 1781 effect.hideEffect = 'fadeOut'; 1782 effect.showSpeed = speed; 1783 } else { 1784 effect.showEffect = "".concat(type, "Toggle"); 1785 effect.hideEffect = "".concat(type, "Toggle"); 1786 effect.showSpeed = speed; 1787 } 1788 return effect; 1789 }; 1790 Drupal.Ajax.prototype.error = function (xmlhttprequest, uri, customMessage) { 1791 if (this.progress.element) { 1792 $(this.progress.element).remove(); 1793 } 1794 if (this.progress.object) { 1795 this.progress.object.stopMonitoring(); 1796 } 1797 $(this.wrapper).show(); 1798 $(this.element).prop('disabled', false); 1799 if (this.$form && document.body.contains(this.$form.get(0))) { 1800 var settings = this.settings || drupalSettings; 1801 Drupal.attachBehaviors(this.$form.get(0), settings); 1802 } 1803 throw new Drupal.AjaxError(xmlhttprequest, uri, customMessage); 1804 }; 1805 Drupal.theme.ajaxWrapperNewContent = function ($newContent, ajax, response) { 1806 return (response.effect || ajax.effect) !== 'none' && $newContent.filter(function (i) { 1807 return !($newContent[i].nodeName === '#comment' || $newContent[i].nodeName === '#text' && /^(\s|\n|\r)*$/.test($newContent[i].textContent)); 1808 }).length > 1 ? Drupal.theme('ajaxWrapperMultipleRootElements', $newContent) : $newContent; 1809 }; 1810 Drupal.theme.ajaxWrapperMultipleRootElements = function ($elements) { 1811 return $('<div></div>').append($elements); 1812 }; 1813 Drupal.AjaxCommands = function () {}; 1814 Drupal.AjaxCommands.prototype = { 1815 insert: function insert(ajax, response) { 1816 var $wrapper = response.selector ? $(response.selector) : $(ajax.wrapper); 1817 var method = response.method || ajax.method; 1818 var effect = ajax.getEffect(response); 1819 var settings = response.settings || ajax.settings || drupalSettings; 1820 var $newContent = $($.parseHTML(response.data, document, true)); 1821 $newContent = Drupal.theme('ajaxWrapperNewContent', $newContent, ajax, response); 1822 switch (method) { 1823 case 'html': 1824 case 'replaceWith': 1825 case 'replaceAll': 1826 case 'empty': 1827 case 'remove': 1828 Drupal.detachBehaviors($wrapper.get(0), settings); 1829 break; 1830 default: 1831 break; 1832 } 1833 $wrapper[method]($newContent); 1834 if (effect.showEffect !== 'show') { 1835 $newContent.hide(); 1836 } 1837 var $ajaxNewContent = $newContent.find('.ajax-new-content'); 1838 if ($ajaxNewContent.length) { 1839 $ajaxNewContent.hide(); 1840 $newContent.show(); 1841 $ajaxNewContent[effect.showEffect](effect.showSpeed); 1842 } else if (effect.showEffect !== 'show') { 1843 $newContent[effect.showEffect](effect.showSpeed); 1844 } 1845 if ($newContent.parents('html').length) { 1846 $newContent.each(function (index, element) { 1847 if (element.nodeType === Node.ELEMENT_NODE) { 1848 Drupal.attachBehaviors(element, settings); 1849 } 1850 }); 1851 } 1852 }, 1853 remove: function remove(ajax, response, status) { 1854 var settings = response.settings || ajax.settings || drupalSettings; 1855 $(response.selector).each(function () { 1856 Drupal.detachBehaviors(this, settings); 1857 }).remove(); 1858 }, 1859 changed: function changed(ajax, response, status) { 1860 var $element = $(response.selector); 1861 if (!$element.hasClass('ajax-changed')) { 1862 $element.addClass('ajax-changed'); 1863 if (response.asterisk) { 1864 $element.find(response.asterisk).append(" <abbr class=\"ajax-changed\" title=\"".concat(Drupal.t('Changed'), "\">*</abbr> ")); 1865 } 1866 } 1867 }, 1868 alert: function alert(ajax, response, status) { 1869 window.alert(response.text); 1870 }, 1871 announce: function announce(ajax, response) { 1872 if (response.priority) { 1873 Drupal.announce(response.text, response.priority); 1874 } else { 1875 Drupal.announce(response.text); 1876 } 1877 }, 1878 redirect: function redirect(ajax, response, status) { 1879 window.location = response.url; 1880 }, 1881 css: function css(ajax, response, status) { 1882 $(response.selector).css(response.argument); 1883 }, 1884 settings: function settings(ajax, response, status) { 1885 var ajaxSettings = drupalSettings.ajax; 1886 if (ajaxSettings) {
1887 Drupal.ajax.expired().forEach(function (instance) { 1888 if (instance.selector) { 1889 var selector = instance.selector.replace('#', ''); 1890 if (selector in ajaxSettings) { 1891 delete ajaxSettings[selector]; 1892 } 1893 } 1894 }); 1895 } 1896 if (response.merge) { 1897 $.extend(true, drupalSettings, response.settings); 1898 } else { 1899 ajax.settings = response.settings; 1900 } 1901 }, 1902 data: function data(ajax, response, status) { 1903 $(response.selector).data(response.name, response.value); 1904 }, 1905 focusFirst: function focusFirst(ajax, response, status) { 1906 var focusChanged = false; 1907 var container = document.querySelector(response.selector); 1908 if (container) { 1909 var tabbableElements = tabbable(container); 1910 if (tabbableElements.length) { 1911 tabbableElements[0].focus(); 1912 focusChanged = true; 1913 } else if (isFocusable(container)) { 1914 container.focus(); 1915 focusChanged = true; 1916 } 1917 } 1918 if (ajax.hasOwnProperty('element') && !focusChanged) { 1919 ajax.element.focus(); 1920 } 1921 }, 1922 invoke: function invoke(ajax, response, status) { 1923 var $element = $(response.selector); 1924 $element[response.method].apply($element, _toConsumableArray(response.args)); 1925 }, 1926 restripe: function restripe(ajax, response, status) { 1927 $(response.selector).find('> tbody > tr:visible, > tr:visible').removeClass('odd even').filter(':even').addClass('odd').end().filter(':odd').addClass('even'); 1928 }, 1929 update_build_id: function update_build_id(ajax, response, status) { 1930 document.querySelectorAll("input[name=\"form_build_id\"][value=\"".concat(response.old, "\"]")).forEach(function (item) { 1931 item.value = response.new; 1932 }); 1933 }, 1934 add_css: function add_css(ajax, response, status) { 1935 $('head').prepend(response.data); 1936 }, 1937 message: function message(ajax, response) { 1938 var messages = new Drupal.Message(document.querySelector(response.messageWrapperQuerySelector)); 1939 if (response.clearPrevious) { 1940 messages.clear(); 1941 } 1942 messages.add(response.message, response.messageOptions); 1943 }, 1944 add_js: function add_js(ajax, response, status) { 1945 var parentEl = document.querySelector(response.selector || 'body'); 1946 var settings = ajax.settings || drupalSettings; 1947 var allUniqueBundleIds = response.data.map(function (script) { 1948 var uniqueBundleId = script.src + ajax.instanceIndex; 1949 loadjs(script.src, uniqueBundleId, { 1950 async: false, 1951 before: function before(path, scriptEl) { 1952 Object.keys(script).forEach(function (attributeKey) { 1953 scriptEl.setAttribute(attributeKey, script[attributeKey]); 1954 }); 1955 parentEl.appendChild(scriptEl); 1956 return false; 1957 } 1958 }); 1959 return uniqueBundleId; 1960 }); 1961 return new Promise(function (resolve, reject) { 1962 loadjs.ready(allUniqueBundleIds, { 1963 success: function success() { 1964 Drupal.attachBehaviors(parentEl, settings); 1965 resolve(); 1966 }, 1967 error: function error(depsNotFound) { 1968 var message = Drupal.t("The following files could not be loaded: @dependencies", { 1969 '@dependencies': depsNotFound.join(', ') 1970 }); 1971 reject(message); 1972 } 1973 }); 1974 }); 1975 } 1976 }; 1977 var stopEvent = function stopEvent(xhr, settings) { 1978 return xhr.getResponseHeader('X-Drupal-Ajax-Token') === '1' && settings.isInProgress && settings.isInProgress(); 1979 }; 1980 $.extend(true, $.event.special, { 1981 ajaxSuccess: { 1982 trigger: function trigger(event, xhr, settings) { 1983 if (stopEvent(xhr, settings)) { 1984 return false; 1985 } 1986 } 1987 }, 1988 ajaxComplete: { 1989 trigger: function trigger(event, xhr, settings) { 1990 if (stopEvent(xhr, settings)) { 1991 $.active++; 1992 return false; 1993 } 1994 } 1995 } 1996 }); 1997})(jQuery, window, Drupal, drupalSettings, loadjs, window.tabbable);; 1998/** 1999 * @file 2000 * Extends methods from core/misc/ajax.js. 2001 */ 2002 2003(function ($, window, Drupal, drupalSettings) { 2004 2005 /** 2006 * Attempts to find the closest glyphicon progress indicator. 2007 * 2008 * @param {jQuery|Element} element 2009 * A DOM element. 2010 * 2011 * @returns {jQuery} 2012 * A jQuery object. 2013 */ 2014 Drupal.Ajax.prototype.findGlyphicon = function (element) { 2015 return $(element).closest('.form-item').find('.ajax-progress.glyphicon') 2016 }; 2017 2018 /** 2019 * Starts the spinning of the glyphicon progress indicator. 2020 * 2021 * @param {jQuery|Element} element 2022 * A DOM element. 2023 * @param {string} [message] 2024 * An optional message to display (tooltip) for the progress. 2025 * 2026 * @returns {jQuery} 2027 * A jQuery object. 2028 */ 2029 Drupal.Ajax.prototype.glyphiconStart = function (element, message) { 2030 var $glyphicon = this.findGlyphicon(element); 2031 if ($glyphicon[0]) { 2032 $glyphicon.addClass('glyphicon-spin'); 2033 2034 // Add any message as a tooltip to the glyphicon. 2035 if ($.fn.tooltip && drupalSettings.bootstrap.tooltip_enabled) { 2036 $glyphicon 2037 .removeAttr('data-toggle') 2038 .removeAttr('data-original-title') 2039 .removeAttr('title') 2040 .tooltip('destroy') 2041 ; 2042 2043 if (message) { 2044 $glyphicon.attr('data-toggle', 'tooltip').attr('title', message).tooltip(); 2045 } 2046 } 2047 2048 // Append a message for screen readers. 2049 if (message) { 2050 $glyphicon.parent().append('<div class="sr-only message">' + message + '</div>'); 2051 } 2052 } 2053 return $glyphicon; 2054 }; 2055 2056 /** 2057 * Stop the spinning of a glyphicon progress indicator. 2058 * 2059 * @param {jQuery|Element} element 2060 * A DOM element. 2061 */ 2062 Drupal.Ajax.prototype.glyphiconStop = function (element) { 2063 var $glyphicon = this.findGlyphicon(element); 2064 if ($glyphicon[0]) { 2065 $glyphicon.removeClass('glyphicon-spin'); 2066 if ($.fn.tooltip && drupalSettings.bootstrap.tooltip_enabled) { 2067 $glyphicon 2068 .removeAttr('data-toggle') 2069 .removeAttr('data-original-title') 2070 .removeAttr('title') 2071 .tooltip('destroy') 2072 ; 2073 } 2074 } 2075 }; 2076 2077 /** 2078 * Sets the throbber progress indicator. 2079 */ 2080 Drupal.Ajax.prototype.setProgressIndicatorThrobber = function () { 2081 var $element = $(this.element); 2082 2083 // Find an existing glyphicon progress indicator. 2084 var $glyphicon = this.glyphiconStart($element, this.progress.message); 2085 if ($glyphicon[0]) { 2086 this.progress.element = $glyphicon.parent(); 2087 this.progress.glyphicon = true; 2088 return; 2089 } 2090 2091 // Otherwise, add a glyphicon throbber after the element. 2092 if (!this.progress.element) { 2093 this.progress.element = $(Drupal.theme('ajaxThrobber')); 2094 } 2095 if (this.progress.message) { 2096 this.progress.element.after('<div class="message">' + this.progress.message + '</div>'); 2097 } 2098 2099 // If element is an input DOM element type (not :input), append after. 2100 if ($element.is('input')) { 2101 $element.after(this.progress.element); 2102 } 2103 // Otherwise append the throbber inside the element. 2104 else { 2105 $element.append(this.progress.element); 2106 } 2107 }; 2108 2109 2110 /** 2111 * Handler for the form redirection completion. 2112 * 2113 * @param {Array.<Drupal.AjaxCommands~commandDefinition>} response 2114 * @param {number} status 2115 */ 2116 var success = Drupal.Ajax.prototype.success; 2117 Drupal.Ajax.prototype.success = function (response, status) { 2118 if (this.progress.element) { 2119 2120 // Stop a glyphicon throbber. 2121 if (this.progress.glyphicon) { 2122 this.glyphiconStop(this.progress.element); 2123 } 2124 // Remove the progress element. 2125 else { 2126 this.progress.element.remove(); 2127 } 2128 2129 // Remove any message set. 2130 this.progress.element.parent().find('.message').remove(); 2131 } 2132 2133 // Invoke the original success handler. 2134 return success.apply(this, [response, status]); 2135 }; 2136 2137})(jQuery, this, Drupal, drupalSettings); 2138; 2139/** 2140* DO NOT EDIT THIS FILE. 2141* See the following change record for more information, 2142* https://www.drupal.org/node/2815083 2143* @preserve 2144**/ 2145(function ($, Drupal, _ref) { 2146 var isTabbable = _ref.isTabbable; 2147 $.extend($.expr[':'], { 2148 tabbable: function tabbable(element) { 2149 Drupal.deprecationError({
2150 message: 'The :tabbable selector is deprecated in Drupal 9.2.0 and will be removed in Drupal 11.0.0. Use the core/tabbable library instead. See https://www.drupal.org/node/3183730' 2151 }); 2152 if (element.tagName === 'SUMMARY' || element.tagName === 'DETAILS') { 2153 var tabIndex = element.getAttribute('tabIndex'); 2154 if (tabIndex === null || tabIndex < 0) { 2155 return false; 2156 } 2157 } 2158 return isTabbable(element); 2159 } 2160 }); 2161})(jQuery, Drupal, window.tabbable);;
vendor: 327 bytes, lines 2161-2162
2161 2162(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start': new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0], j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src= 'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);})(window,document,'script','dataLayer','
2162GTM-KMLH4SZ
vendor: 3 bytes, line 2162
2162');
2162; 2163// Custom scripts file 2164 2165(function ($) { 2166 2167 'use strict'; 2168 2169 // Generic function that runs on window resize. 2170 function resizeStuff() { 2171 2172 } 2173 2174 // Runs function once on window resize. 2175 var TO = false; 2176 $(window).resize(function () { 2177 if (TO !== false) { 2178 clearTimeout(TO); 2179 } 2180 2181 // 200 is time in miliseconds. 2182 TO = setTimeout(resizeStuff, 200); 2183 }).resize(); 2184 2185 // Count function for the infographic component types 2186 $('.count').each(function () { 2187 $(this).prop('Counter',0).animate({ 2188 Counter: $(this).text() 2189 }, { 2190 duration: 3000, 2191 easing: 'swing', 2192 step: function (now) { 2193 $(this).text(Math.ceil(now).toLocaleString('en')); 2194 } 2195 }); 2196 }); 2197 $("#closeit").click(function() { 2198 $("#shoutbar").slideUp(); 2199 }); 2200 // $(document).ready(function(){$("#openit").click(function(){$("#shoutbar").effect("bounce","slow");$("#openit").slideUp()})}); 2201 // $(document).ready(function(){$("#shoutbar").effect("bounce","slow");$("#closeit").click(function(){$("#shoutbar").slideUp();$("#openit").slideDown()})}); 2202})(jQuery);; 2203$ = jQuery; 2204$(function(){ 2205 // Add classes to header elements so they can shrink when the page scrolls 2206 var shrinkHeader = 40; 2207 var glitchHeader = 50; 2208 $(window).scroll(function() { 2209 var scroll = getCurrentScroll(); 2210 if ( scroll >= glitchHeader ) { 2211 $('header.header').addClass('shrink'); 2212 $('.site-logo__grid').addClass('shrink'); 2213 $('.region-midline').addClass('shrink'); 2214 $('.page-wrapper').addClass('shrink'); 2215 $('body.user-logged-in header.header').removeClass('shrink'); 2216 $('body.user-logged-in .site-logo__grid').removeClass('shrink'); 2217 $('body.user-logged-in .region-midline').removeClass('shrink'); 2218 } 2219 // else if ( scroll <= shrinkHeader ) { 2220 // $('.page-wrapper').removeClass('shrink'); 2221 // $('header.header').removeClass('shrink'); 2222 // $('.site-logo__grid').removeClass('shrink'); 2223 // $('.region-midline').removeClass('shrink'); 2224 // } 2225 document.onscroll = scroll; 2226 }); 2227 function getCurrentScroll() { 2228 // console.log(scroll); 2229 return window.pageYOffset || document.documentElement.scrollTop; 2230 } 2231 2232 // Add a click function for the ... icon in the footer of the sliding navigation 2233 $("a#toggle").click(function() 2234 { 2235 $(".mm-searchfield").slideToggle(); 2236 $(".mmenu_tsesearch").slideToggle(); 2237 $(".mmenu_tsecoop").slideToggle(); 2238 return false; 2239 }); 2240 2241 // Create a clone of the menu, right next to original. 2242 // Clone is used to sync up elements when the header becomes fixed after scrolling 50 pixels vertically down the page 2243 // $('header.header').addClass('original').clone().insertAfter('.header').addClass('cloned').css('position','fixed').css('top','0').css('margin-top','0').css('z-index','500').removeClass('original').hide(); 2244 // 2245 // scrollIntervalID = setInterval(stickIt, 10); 2246 // 2247 // function stickIt() { 2248 // 2249 // var orgElementPos = $('.original').offset(); 2250 // orgElementTop = orgElementPos.top; 2251 // 2252 // if ($(window).scrollTop() >= (orgElementTop)) { 2253 // // scrolled past the original position; now only show the cloned, sticky element. 2254 // 2255 // // Cloned element should always have same left position and width as original element. 2256 // orgElement = $('.original'); 2257 // coordsOrgElement = orgElement.offset(); 2258 // leftOrgElement = coordsOrgElement.left; 2259 // widthOrgElement = orgElement.css('width'); 2260 // $('.cloned').css('left',leftOrgElement+'px').css('top',0).css('width',widthOrgElement).show(); 2261 // $('.original').css('visibility','hidden'); 2262 // } else { 2263 // // not scrolled past the menu; only show the original menu. 2264 // $('.cloned').hide(); 2265 // $('.original').css('visibility','visible'); 2266 // } 2267 // } 2268}); 2269 2270function rotateCard(btn){ 2271 var $card = $(btn).closest('.card-container'); 2272 console.log($card); 2273 if($card.hasClass('hover')){ 2274 $card.removeClass('hover'); 2275 } else { 2276 $card.addClass('hover'); 2277 } 2278}; 2279/** 2280 * Due to the construction of the responsive_menu module, there exists no 2281 * API or method of injecting navbar information 2282 */ 2283(function ($, undefined) { 2284 'use strict'; 2285 /** 2286 * Provides the off-canvas menu. 2287 * 2288 * @type {Drupal~behavior} 2289 * 2290 * @prop {Drupal~behaviorAttach} attach 2291 * Attaches the behavior for the off-canvas menu. 2292 */ 2293 Drupal.behaviors.responsive_menu_mmenu = { 2294 attach: function (context) { 2295 $('body').once('cwb-responsive-menu-mmenu').each(function () { 2296 const mmenuId = '#off-canvas'; 2297 const offCanvas = document.querySelector(mmenuId); 2298 2299 const settings = drupalSettings.responsive_menu; 2300 const position = settings.position, 2301 theme = settings.theme, 2302 pagedim = settings.pagedim; 2303 2304 const options = { 2305 extensions: [ 2306 theme, 2307 'fx-menu-slide', 2308 position === 'left' ? 'position-left' : 'position-right' 2309 ], 2310 keyboardNavigation: { 2311 enable: true, 2312 enhance: true, 2313 }, 2314 drag: { 2315 open: settings.drag 2316 }, 2317 navbars: [ 2318 { 2319 "position": "bottom", 2320 "content": [ 2321 "<a class='glyphicon glyphicon-option-horizontal' href='#' id='toggle' aria-hidden='true'></a>", 2322 "searchfield", 2323 "<div class='mmenu_tsesearch'><p>Can't find what you are looking for?</p><p>Use our instant search to find relevant pages.</p></div>", 2324 "<div class='mmenu_tsecoop'>Touchstone Energy® Cooperatives</div></div>" 2325 ] 2326 } 2327 ] 2328 }; 2329 2330 if (pagedim !== 'none') { 2331 options.extensions.push(pagedim); 2332 } 2333 2334 const config = { 2335 classNames: { 2336 selected: 'menu-item--active-trail' 2337 } 2338 }; 2339 2340 // Allow the settings and options to be extended or overridden. 2341 if (typeof settings.custom !== 'undefined') { 2342 if (typeof settings.custom.options !== 'undefined') { 2343 extend(options, drupalSettings.responsive_menu.custom.options); 2344 } 2345 if (typeof drupalSettings.responsive_menu.custom.config !== 'undefined') { 2346 extend(config, drupalSettings.responsive_menu.custom.config); 2347 } 2348 } 2349 2350 // Set up the off canvas menu. 2351 const mmenu = new Mmenu(mmenuId, options, config); 2352 2353 // Due to a rendering issue with Chrome the page needs the viewport 2354 // metatag to have a value including initial-scale=1.0 otherwise it 2355 // won't render properly. 2356 // @see issue #3153145 2357 const mmenuApi = mmenu.API; 2358 const viewports = document.getElementsByName('viewport'); 2359 2360 if (viewports.length !== 0 && settings.modifyViewport) { 2361 const viewportMeta = viewports[0];
2362 const defaultViewport = viewports[0].content; 2363 const staticViewport = "width=device-width, initial-scale=1.0, minimum-scale=1.0"; 2364 2365 mmenuApi.bind('open:start', function () { 2366 viewportMeta.setAttribute('content', staticViewport); 2367 }); 2368 mmenuApi.bind('close:start', function () { 2369 viewportMeta.setAttribute('content', defaultViewport); 2370 }); 2371 } 2372 }); 2373 } 2374 }; 2375})(jQuery); 2376; 2377(function ($) { 2378 2379 'use strict'; 2380 2381 Drupal.behaviors.timeline = { 2382 attach: function (context, settings) { 2383 2384 var $timeline_block = $('.cd-timeline-block'); 2385 2386 $('main .cd-timeline-block', context).once('timeline').each(function(){ 2387 console.log($(this)); 2388 2389 //hide any timeline blocks that are outside of the viewport 2390 if($(this).offset().top > $(window).scrollTop()+$(window).height()*0.75) { 2391 $(this).find('.cd-timeline-img, .cd-timeline-content').addClass('is-hidden'); 2392 } 2393 2394 //on scrolling, show/animate the timeline blocks as they enter the viewport 2395 $(window).on('scroll', function(){ 2396 $timeline_block.each(function(){ 2397 if( $(this).offset().top <= $(window).scrollTop()+$(window).height()*0.75 && $(this).find('.cd-timeline-img').hasClass('is-hidden') ) { 2398 $(this).find('.cd-timeline-img, .cd-timeline-content').removeClass('is-hidden').addClass('bounce-in'); 2399 } 2400 }); 2401 }); 2402 }); 2403 } 2404 }; 2405})(jQuery);; 2406(function ($, undefined) { 2407 $(document).ready(function () { 2408 var 2409 selectors = { 2410 tabGroups: '.paragraph--type--bp-accordion', 2411 tabLinks: '.panel-title', 2412 } 2413 ; 2414 // Iterate through each accordion group... 2415 $(selectors.tabGroups).each(function () { 2416 var 2417 tabGroup = this 2418 tabId = tabGroup.id 2419 ; 2420 // Iterate through all links in accordion group... 2421 counter = 0; 2422 $(selectors.tabLinks, tabGroup).each(function () { 2423 2424 /** 2425 * Get this tab's href, which matches the ID of one of the panes 2426 * in this accordion group. Craft a new, unique href/id based on 2427 * the current group's id, then re-assign href and ID accordingly. 2428 * This ensures that each accordion group gets a uniquely named 2429 * set of matching accordions/panes per group. 2430 */ 2431 2432 var 2433 href = $('> a', this).attr('href'), 2434 newHref = tabId+'-'+counter, 2435 linkedContent = $(href, tabGroup) 2436 ; 2437 $('> a', this).attr('href', '#'+newHref) 2438 $(linkedContent).each(function () { 2439 this.id = newHref; 2440 }) 2441 counter++; 2442 }); 2443 }); 2444 }); 2445})(jQuery);; 2446var timeOut; 2447function scrollToTop() { 2448 if (document.body.scrollTop!=0 || document.documentElement.scrollTop!=0){ 2449 window.scrollBy(0,-50); 2450 timeOut=setTimeout('scrollToTop()',10); 2451 } 2452 else clearTimeout(timeOut); 2453}; 2454/** 2455 * @file 2456 * External links js file. 2457 */ 2458 2459(function ($, Drupal, drupalSettings) { 2460 2461 'use strict'; 2462 2463 Drupal.extlink = Drupal.extlink || {}; 2464 2465 Drupal.extlink.attach = function (context, drupalSettings) { 2466 if (typeof drupalSettings.data === 'undefined' || !drupalSettings.data.hasOwnProperty('extlink')) { 2467 return; 2468 } 2469 2470 // Define the jQuery method (either 'append' or 'prepend') of placing the 2471 // icon, defaults to 'append'. 2472 var extIconPlacement = 'append'; 2473 if (drupalSettings.data.extlink.extIconPlacement && drupalSettings.data.extlink.extIconPlacement != '0') { 2474 extIconPlacement = drupalSettings.data.extlink.extIconPlacement; 2475 } 2476 2477 // Strip the host name down, removing ports, subdomains, or www. 2478 var pattern = /^(([^\/:]+?\.)*)([^\.:]{1,})((\.[a-z0-9]{1,253})*)(:[0-9]{1,5})?$/; 2479 var host = window.location.host.replace(pattern, '$2$3$6'); 2480 var subdomain = window.location.host.replace(host, ''); 2481 2482 // Determine what subdomains are considered internal. 2483 var subdomains; 2484 if (drupalSettings.data.extlink.extSubdomains) { 2485 subdomains = '([^/]*\\.)?'; 2486 } 2487 else if (subdomain === 'www.' || subdomain === '') { 2488 subdomains = '(www\\.)?'; 2489 } 2490 else { 2491 subdomains = subdomain.replace('.', '\\.'); 2492 } 2493 2494 // Whitelisted domains. 2495 var whitelistedDomains = false;
2496 if (drupalSettings.data.extlink.whitelistedDomains) { 2497 whitelistedDomains = []; 2498 for (var i = 0; i < drupalSettings.data.extlink.whitelistedDomains.length; i++) { 2499 whitelistedDomains.push(new RegExp('^https?:\\/\\/' + drupalSettings.data.extlink.whitelistedDomains[i].replace(/(\r\n|\n|\r)/gm,'') + '.*$', 'i')); 2500 } 2501 } 2502 2503 // Build regular expressions that define an internal link. 2504 var internal_link = new RegExp('^https?://([^@]*@)?' + subdomains + host, 'i'); 2505 2506 // Extra internal link matching. 2507 var extInclude = false; 2508 if (drupalSettings.data.extlink.extInclude) { 2509 extInclude = new RegExp(drupalSettings.data.extlink.extInclude.replace(/\\/, '\\'), 'i'); 2510 } 2511 2512 // Extra external link matching. 2513 var extExclude = false; 2514 if (drupalSettings.data.extlink.extExclude) { 2515 extExclude = new RegExp(drupalSettings.data.extlink.extExclude.replace(/\\/, '\\'), 'i'); 2516 } 2517 2518 // Extra external link CSS selector exclusion. 2519 var extCssExclude = false; 2520 if (drupalSettings.data.extlink.extCssExclude) { 2521 extCssExclude = drupalSettings.data.extlink.extCssExclude; 2522 } 2523 2524 // Extra external link CSS selector explicit. 2525 var extCssExplicit = false; 2526 if (drupalSettings.data.extlink.extCssExplicit) { 2527 extCssExplicit = drupalSettings.data.extlink.extCssExplicit; 2528 } 2529 2530 // Find all links which are NOT internal and begin with http as opposed 2531 // to ftp://, javascript:, etc. other kinds of links. 2532 // When operating on the 'this' variable, the host has been appended to 2533 // all links by the browser, even local ones. 2534 // In jQuery 1.1 and higher, we'd use a filter method here, but it is not 2535 // available in jQuery 1.0 (Drupal 5 default). 2536 var external_links = []; 2537 var mailto_links = []; 2538 $('a:not([data-extlink]), area:not([data-extlink])', context).each(function (el) { 2539 try { 2540 var url = ''; 2541 if (typeof this.href == 'string') { 2542 url = this.href.toLowerCase(); 2543 } 2544 // Handle SVG links (xlink:href). 2545 else if (typeof this.href == 'object') { 2546 url = this.href.baseVal; 2547 } 2548 if (url.indexOf('http') === 0 2549 && ((!internal_link.test(url) && !(extExclude && extExclude.test(url))) || (extInclude && extInclude.test(url))) 2550 && !(extCssExclude && $(this).is(extCssExclude)) 2551 && !(extCssExclude && $(this).parents(extCssExclude).length > 0) 2552 && !(extCssExplicit && $(this).parents(extCssExplicit).length < 1)) { 2553 var match = false; 2554 if (whitelistedDomains) { 2555 for (var i = 0; i < whitelistedDomains.length; i++) { 2556 if (whitelistedDomains[i].test(url)) { 2557 match = true; 2558 break; 2559 } 2560 } 2561 } 2562 if (!match) { 2563 external_links.push(this); 2564 } 2565 } 2566 // Do not include area tags with begin with mailto: (this prohibits 2567 // icons from being added to image-maps). 2568 else if (this.tagName !== 'AREA' 2569 && url.indexOf('mailto:') === 0 2570 && !(extCssExclude && $(this).parents(extCssExclude).length > 0) 2571 && !(extCssExplicit && $(this).parents(extCssExplicit).length < 1)) { 2572 mailto_links.push(this); 2573 } 2574 } 2575 // IE7 throws errors often when dealing with irregular links, such as: 2576 // <a href="node/10"></a> Empty tags. 2577 // <a href="http://user:[email protected]">example</a> User:pass syntax. 2578 catch (error) { 2579 return false; 2580 } 2581 }); 2582 2583 if (drupalSettings.data.extlink.extClass !== '0' && drupalSettings.data.extlink.extClass !== '') { 2584 Drupal.extlink.applyClassAndSpan(external_links, drupalSettings.data.extlink.extClass, extIconPlacement); 2585 } 2586 2587 if (drupalSettings.data.extlink.mailtoClass !== '0' && drupalSettings.data.extlink.mailtoClass !== '') { 2588 Drupal.extlink.applyClassAndSpan(mailto_links, drupalSettings.data.extlink.mailtoClass, extIconPlacement); 2589 } 2590 2591 if (drupalSettings.data.extlink.extTarget) { 2592 // Apply the target attribute to all links. 2593 $(external_links).filter(function () { 2594 // Filter out links with target set if option specified. 2595 return !(drupalSettings.data.extlink.extTargetNoOverride && $(this).is('a[target]')); 2596 }).attr({target: '_blank'}); 2597 2598 // Add noopener rel attribute to combat phishing. 2599 $(external_links).attr('rel', function (i, val) { 2600 // If no rel attribute is present, create one with the value noopener. 2601 if (val === null || typeof val === 'undefined') { 2602 return 'noopener'; 2603 }
2604 // Check to see if rel contains noopener. Add what doesn't exist. 2605 if (val.indexOf('noopener') > -1) { 2606 if (val.indexOf('noopener') === -1) { 2607 return val + ' noopener'; 2608 } 2609 // Noopener exists. Nothing needs to be added. 2610 else { 2611 return val; 2612 } 2613 } 2614 // Else, append noopener to val. 2615 else { 2616 return val + ' noopener'; 2617 } 2618 }); 2619 } 2620 2621 if (drupalSettings.data.extlink.extNofollow) { 2622 $(external_links).attr('rel', function (i, val) { 2623 // When the link does not have a rel attribute set it to 'nofollow'. 2624 if (val === null || typeof val === 'undefined') { 2625 return 'nofollow'; 2626 } 2627 var target = 'nofollow'; 2628 // Change the target, if not overriding follow. 2629 if (drupalSettings.data.extlink.extFollowNoOverride) { 2630 target = 'follow'; 2631 } 2632 if (val.indexOf(target) === -1) { 2633 return val + ' nofollow'; 2634 } 2635 return val; 2636 }); 2637 } 2638 2639 if (drupalSettings.data.extlink.extNoreferrer) { 2640 $(external_links).attr('rel', function (i, val) { 2641 // When the link does not have a rel attribute set it to 'noreferrer'. 2642 if (val === null || typeof val === 'undefined') { 2643 return 'noreferrer'; 2644 } 2645 if (val.indexOf('noreferrer') === -1) { 2646 return val + ' noreferrer'; 2647 } 2648 return val; 2649 }); 2650 } 2651 2652 Drupal.extlink = Drupal.extlink || {}; 2653 2654 // Set up default click function for the external links popup. This should be 2655 // overridden by modules wanting to alter the popup. 2656 Drupal.extlink.popupClickHandler = Drupal.extlink.popupClickHandler || function () { 2657 if (drupalSettings.data.extlink.extAlert) { 2658 return confirm(drupalSettings.data.extlink.extAlertText); 2659 } 2660 }; 2661 2662 $(external_links).off("click.extlink"); 2663 $(external_links).on("click.extlink", function (e) { 2664 return Drupal.extlink.popupClickHandler(e, this); 2665 }); 2666 }; 2667 2668 /** 2669 * Apply a class and a trailing <span> to all links not containing images. 2670 * 2671 * @param {object[]} links 2672 * An array of DOM elements representing the links. 2673 * @param {string} class_name 2674 * The class to apply to the links. 2675 * @param {string} icon_placement 2676 * 'append' or 'prepend' the icon to the link. 2677 */ 2678 Drupal.extlink.applyClassAndSpan = function (links, class_name, icon_placement) { 2679 var $links_to_process; 2680 if (drupalSettings.data.extlink.extImgClass) { 2681 $links_to_process = $(links); 2682 } 2683 else { 2684 var links_with_images = $(links).find('img, svg').parents('a'); 2685 $links_to_process = $(links).not(links_with_images); 2686 } 2687 2688 if (class_name !== '0') { 2689 $links_to_process.addClass(class_name); 2690 } 2691 2692 // Add data-extlink attribute. 2693 $links_to_process.attr('data-extlink', ''); 2694 2695 var i; 2696 var length = $links_to_process.length; 2697 for (i = 0; i < length; i++) { 2698 var $link = $($links_to_process[i]); 2699 if (drupalSettings.data.extlink.extUseFontAwesome) { 2700 if (class_name === drupalSettings.data.extlink.mailtoClass) { 2701 $link[icon_placement]('<span class="fa-' + class_name + ' extlink"><span class="' + drupalSettings.data.extlink.extFaMailtoClasses + '" aria-label="' + drupalSettings.data.extlink.mailtoLabel + '"></span></span>'); 2702 } 2703 else { 2704 $link[icon_placement]('<span class="fa-' + class_name + ' extlink"><span class="' + drupalSettings.data.extlink.extFaLinkClasses + '" aria-label="' + drupalSettings.data.extlink.extLabel + '"></span></span>'); 2705 } 2706 } 2707 else { 2708 if (class_name === drupalSettings.data.extlink.mailtoClass) { 2709 $link[icon_placement]('<svg focusable="false" class="' + class_name + '" role="img" aria-label="' + drupalSettings.data.extlink.mailtoLabel + '" xmlns="http://www.w3.org/2000/svg" viewBox="0 10 70 20"><metadata><sfw xmlns="http://ns.adobe.com/SaveForWeb/1.0/"><sliceSourceBounds y="-8160" x="-8165" width="16389" height="16384" bottomLeftOrigin="true"/><optimizationSettings><targetSettings targetSettingsID="0" fileFormat="PNG24Format"><PNG24Format transparency="true" filtered="false" interlaced="false" noMatteColor="false" matteColor="#FFFFFF"/></targetSettings></optimizationSettings></sfw></metadata><title>' + drupalSettings.data.extlink.mailtoLabel + '</title><path d="M56 14H8c-1.1 0-2 0.9-2 2v32c0 1.1 0.9 2 2 2h48c1.1 0 2-0.9 2-2V16C58 14.9 57.1 14 56 14zM50.5 18L32 33.4 13.5 18H50.5zM10 46V20.3l20.7 17.3C31.1 37.8 31.5 38 32 38s0.9-0.2 1.3-0.5L54 20.3V46H10z"/></svg>'); 2710 } 2711 else { 2712 $link[icon_placement]('<svg focusable="false" class="' + class_name + '" role="img" aria-label="' + drupalSettings.data.extlink.extLabel + '" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 80 40"><metadata><sfw xmlns="http://ns.adobe.com/SaveForWeb/1.0/"><sliceSourceBounds y="-8160" x="-8165" width="16389" height="16384" bottomLeftOrigin="true"/><optimizationSettings><targetSettings targetSettingsID="0" fileFormat="PNG24Format"><PNG24Format transparency="true" filtered="false" interlaced="false" noMatteColor="false" matteColor="#FFFFFF"/></targetSettings></optimizationSettings></sfw></metadata><title>' + drupalSettings.data.extlink.extLabel + '</title><path d="M48 26c-1.1 0-2 0.9-2 2v26H10V18h26c1.1 0 2-0.9 2-2s-0.9-2-2-2H8c-1.1 0-2 0.9-2 2v40c0 1.1 0.9 2 2 2h40c1.1 0 2-0.9 2-2V28C50 26.9 49.1 26 48 26z"/><path d="M56 6H44c-1.1 0-2 0.9-2 2s0.9 2 2 2h7.2L30.6 30.6c-0.8 0.8-0.8 2 0 2.8C31 33.8 31.5 34 32 34s1-0.2 1.4-0.6L54 12.8V20c0 1.1 0.9 2 2 2s2-0.9 2-2V8C58 6.9 57.1 6 56 6z"/></svg>'); 2713 } 2714 } 2715 } 2716 }; 2717 2718 Drupal.behaviors.extlink = Drupal.behaviors.extlink || {}; 2719 Drupal.behaviors.extlink.attach = function (context, drupalSettings) { 2720 // Backwards compatibility, for the benefit of modules overriding extlink 2721 // functionality by defining an "extlinkAttach" global function. 2722 if (typeof extlinkAttach === 'function') { 2723 extlinkAttach(context); 2724 } 2725 else { 2726 Drupal.extlink.attach(context, drupalSettings); 2727 } 2728 }; 2729 2730})(jQuery, Drupal, drupalSettings); 2731; 2732/** 2733 * @file 2734 * Webform behaviors. 2735 */ 2736 2737(function ($, Drupal) { 2738 2739 'use strict'; 2740 2741 // Trigger Drupal's attaching of behaviors after the page is 2742 // completely loaded. 2743 // @see https://stackoverflow.com/questions/37838430/detect-if-page-is-load-from-back-button 2744 // @see https://stackoverflow.com/questions/20899274/how-to-refresh-page-on-back-button-click/20899422#20899422 2745 var isChrome = (/chrom(e|ium)/.test(window.navigator.userAgent.toLowerCase())); 2746 if (isChrome) { 2747 // Track back button in navigation. 2748 // @see https://stackoverflow.com/questions/37838430/detect-if-page-is-load-from-back-button 2749 var backButton = false;
2750 if (window.performance) { 2751 var navEntries = window.performance.getEntriesByType('navigation'); 2752 if (navEntries.length > 0 && navEntries[0].type === 'back_forward') { 2753 backButton = true; 2754 } 2755 else if (window.performance.navigation 2756 && window.performance.navigation.type === window.performance.navigation.TYPE_BACK_FORWARD) { 2757 backButton = true; 2758 } 2759 } 2760 2761 // If the back button is pressed, delay Drupal's attaching of behaviors. 2762 if (backButton) { 2763 var attachBehaviors = Drupal.attachBehaviors; 2764 Drupal.attachBehaviors = function (context, settings) { 2765 setTimeout(function (context, settings) { 2766 attachBehaviors(context, settings); 2767 }, 300); 2768 }; 2769 } 2770 } 2771 2772})(jQuery, Drupal); 2773; 2774/** 2775* DO NOT EDIT THIS FILE. 2776* See the following change record for more information, 2777* https://www.drupal.org/node/2815083 2778* @preserve 2779**/ 2780(function ($, Drupal) { 2781 var states = { 2782 postponed: [] 2783 }; 2784 Drupal.states = states; 2785 function invert(a, invertState) { 2786 return invertState && typeof a !== 'undefined' ? !a : a; 2787 } 2788 function _compare2(a, b) { 2789 if (a === b) { 2790 return typeof a === 'undefined' ? a : true; 2791 } 2792 return typeof a === 'undefined' || typeof b === 'undefined'; 2793 } 2794 function ternary(a, b) { 2795 if (typeof a === 'undefined') { 2796 return b; 2797 } 2798 if (typeof b === 'undefined') { 2799 return a; 2800 } 2801 return a && b; 2802 } 2803 Drupal.behaviors.states = { 2804 attach: function attach(context, settings) { 2805 var $states = $(context).find('[data-drupal-states]'); 2806 var il = $states.length; 2807 var _loop = function _loop(i) { 2808 var config = JSON.parse($states[i].getAttribute('data-drupal-states')); 2809 Object.keys(config || {}).forEach(function (state) { 2810 new states.Dependent({ 2811 element: $($states[i]), 2812 state: states.State.sanitize(state), 2813 constraints: config[state] 2814 }); 2815 }); 2816 }; 2817 for (var i = 0; i < il; i++) { 2818 _loop(i); 2819 } 2820 while (states.postponed.length) { 2821 states.postponed.shift()(); 2822 } 2823 } 2824 }; 2825 states.Dependent = function (args) { 2826 var _this = this; 2827 $.extend(this, { 2828 values: {}, 2829 oldValue: null 2830 }, args); 2831 this.dependees = this.getDependees(); 2832 Object.keys(this.dependees || {}).forEach(function (selector) { 2833 _this.initializeDependee(selector, _this.dependees[selector]); 2834 }); 2835 }; 2836 states.Dependent.comparisons = { 2837 RegExp: function RegExp(reference, value) { 2838 return reference.test(value); 2839 }, 2840 Function: function Function(reference, value) { 2841 return reference(value); 2842 }, 2843 Number: function Number(reference, value) { 2844 return typeof value === 'string' ? _compare2(reference.toString(), value) : _compare2(reference, value); 2845 } 2846 }; 2847 states.Dependent.prototype = { 2848 initializeDependee: function initializeDependee(selector, dependeeStates) { 2849 var _this2 = this; 2850 this.values[selector] = {}; 2851 Object.keys(dependeeStates).forEach(function (i) { 2852 var state = dependeeStates[i]; 2853 if ($.inArray(state, dependeeStates) === -1) { 2854 return; 2855 } 2856 state = states.State.sanitize(state); 2857 _this2.values[selector][state.name] = null; 2858 $(selector).on("state:".concat(state), { 2859 selector: selector, 2860 state: state 2861 }, function (e) { 2862 _this2.update(e.data.selector, e.data.state, e.value); 2863 }); 2864 new states.Trigger({ 2865 selector: selector, 2866 state: state 2867 }); 2868 }); 2869 }, 2870 compare: function compare(reference, selector, state) { 2871 var value = this.values[selector][state.name]; 2872 if (reference.constructor.name in states.Dependent.comparisons) { 2873 return states.Dependent.comparisons[reference.constructor.name](reference, value); 2874 } 2875 return _compare2(reference, value); 2876 }, 2877 update: function update(selector, state, value) { 2878 if (value !== this.values[selector][state.name]) { 2879 this.values[selector][state.name] = value; 2880 this.reevaluate(); 2881 } 2882 }, 2883 reevaluate: function reevaluate() { 2884 var value = this.verifyConstraints(this.constraints); 2885 if (value !== this.oldValue) { 2886 this.oldValue = value; 2887 value = invert(value, this.state.invert); 2888 this.element.trigger({ 2889 type: "state:".concat(this.state), 2890 value: value, 2891 trigger: true 2892 }); 2893 } 2894 }, 2895 verifyConstraints: function verifyConstraints(constraints, selector) { 2896 var result; 2897 if ($.isArray(constraints)) { 2898 var hasXor = $.inArray('xor', constraints) === -1; 2899 var len = constraints.length; 2900 for (var i = 0; i < len; i++) { 2901 if (constraints[i] !== 'xor') { 2902 var constraint = this.checkConstraints(constraints[i], selector, i); 2903 if (constraint && (hasXor || result)) { 2904 return hasXor; 2905 } 2906 result = result || constraint; 2907 } 2908 } 2909 } else if ($.isPlainObject(constraints)) { 2910 for (var n in constraints) { 2911 if (constraints.hasOwnProperty(n)) { 2912 result = ternary(result, this.checkConstraints(constraints[n], selector, n)); 2913 if (result === false) { 2914 return false;
2915 } 2916 } 2917 } 2918 } 2919 return result; 2920 }, 2921 checkConstraints: function checkConstraints(value, selector, state) { 2922 if (typeof state !== 'string' || /[0-9]/.test(state[0])) { 2923 state = null; 2924 } else if (typeof selector === 'undefined') { 2925 selector = state; 2926 state = null; 2927 } 2928 if (state !== null) { 2929 state = states.State.sanitize(state); 2930 return invert(this.compare(value, selector, state), state.invert); 2931 } 2932 return this.verifyConstraints(value, selector); 2933 }, 2934 getDependees: function getDependees() { 2935 var cache = {}; 2936 var _compare = this.compare; 2937 this.compare = function (reference, selector, state) { 2938 (cache[selector] || (cache[selector] = [])).push(state.name); 2939 }; 2940 this.verifyConstraints(this.constraints); 2941 this.compare = _compare; 2942 return cache; 2943 } 2944 }; 2945 states.Trigger = function (args) { 2946 $.extend(this, args); 2947 if (this.state in states.Trigger.states) { 2948 this.element = $(this.selector); 2949 if (!this.element.data("trigger:".concat(this.state))) { 2950 this.initialize(); 2951 } 2952 } 2953 }; 2954 states.Trigger.prototype = { 2955 initialize: function initialize() { 2956 var _this3 = this; 2957 var trigger = states.Trigger.states[this.state]; 2958 if (typeof trigger === 'function') { 2959 trigger.call(window, this.element); 2960 } else { 2961 Object.keys(trigger || {}).forEach(function (event) { 2962 _this3.defaultTrigger(event, trigger[event]); 2963 }); 2964 } 2965 this.element.data("trigger:".concat(this.state), true); 2966 }, 2967 defaultTrigger: function defaultTrigger(event, valueFn) { 2968 var oldValue = valueFn.call(this.element); 2969 this.element.on(event, $.proxy(function (e) { 2970 var value = valueFn.call(this.element, e); 2971 if (oldValue !== value) { 2972 this.element.trigger({ 2973 type: "state:".concat(this.state), 2974 value: value, 2975 oldValue: oldValue 2976 }); 2977 oldValue = value; 2978 } 2979 }, this)); 2980 states.postponed.push($.proxy(function () { 2981 this.element.trigger({ 2982 type: "state:".concat(this.state), 2983 value: oldValue, 2984 oldValue: null 2985 }); 2986 }, this)); 2987 } 2988 }; 2989 states.Trigger.states = { 2990 empty: { 2991 keyup: function keyup() { 2992 return this.val() === ''; 2993 }, 2994 change: function change() { 2995 return this.val() === ''; 2996 } 2997 }, 2998 checked: { 2999 change: function change() { 3000 var checked = false; 3001 this.each(function () { 3002 checked = $(this).prop('checked'); 3003 return !checked; 3004 }); 3005 return checked; 3006 } 3007 }, 3008 value: { 3009 keyup: function keyup() { 3010 if (this.length > 1) { 3011 return this.filter(':checked').val() || false; 3012 } 3013 return this.val(); 3014 }, 3015 change: function change() { 3016 if (this.length > 1) { 3017 return this.filter(':checked').val() || false; 3018 } 3019 return this.val(); 3020 } 3021 }, 3022 collapsed: { 3023 collapsed: function collapsed(e) { 3024 return typeof e !== 'undefined' && 'value' in e ? e.value : !this.is('[open]'); 3025 } 3026 } 3027 }; 3028 states.State = function (state) { 3029 this.pristine = state; 3030 this.name = state; 3031 var process = true; 3032 do { 3033 while (this.name.charAt(0) === '!') { 3034 this.name = this.name.substring(1); 3035 this.invert = !this.invert; 3036 } 3037 if (this.name in states.State.aliases) { 3038 this.name = states.State.aliases[this.name]; 3039 } else { 3040 process = false; 3041 } 3042 } while (process); 3043 }; 3044 states.State.sanitize = function (state) { 3045 if (state instanceof states.State) { 3046 return state; 3047 } 3048 return new states.State(state); 3049 }; 3050 states.State.aliases = { 3051 enabled: '!disabled', 3052 invisible: '!visible', 3053 invalid: '!valid', 3054 untouched: '!touched', 3055 optional: '!required', 3056 filled: '!empty', 3057 unchecked: '!checked', 3058 irrelevant: '!relevant', 3059 expanded: '!collapsed', 3060 open: '!collapsed', 3061 closed: 'collapsed', 3062 readwrite: '!readonly' 3063 }; 3064 states.State.prototype = { 3065 invert: false, 3066 toString: function toString() { 3067 return this.name; 3068 } 3069 }; 3070 var $document = $(document); 3071 $document.on('state:disabled', function (e) { 3072 if (e.trigger) { 3073 $(e.target).closest('.js-form-item, .js-form-submit, .js-form-wrapper').toggleClass('form-disabled', e.value).find('select, input, textarea').prop('disabled', e.value); 3074 } 3075 }); 3076 $document.on('state:required', function (e) { 3077 if (e.trigger) { 3078 if (e.value) { 3079 var label = "label".concat(e.target.id ? "[for=".concat(e.target.id, "]") : ''); 3080 var $label = $(e.target).attr({ 3081 required: 'required', 3082 'aria-required': 'true' 3083 }).closest('.js-form-item, .js-form-wrapper').find(label); 3084 if (!$label.hasClass('js-form-required').length) { 3085 $label.addClass('js-form-required form-required'); 3086 } 3087 } else { 3088 $(e.target).removeAttr('required aria-required').closest('.js-form-item, .js-form-wrapper').find('label.js-form-required').removeClass('js-form-required form-required'); 3089 } 3090 } 3091 }); 3092 $document.on('state:visible', function (e) { 3093 if (e.trigger) { 3094 $(e.target).closest('.js-form-item, .js-form-submit, .js-form-wrapper').toggle(e.value); 3095 } 3096 }); 3097 $document.on('state:checked', function (e) { 3098 if (e.trigger) { 3099 $(e.target).closest('.js-form-item, .js-form-wrapper').find('input').prop('checked', e.value).trigger('change'); 3100 } 3101 }); 3102 $document.on('state:collapsed', function (e) { 3103 if (e.trigger) { 3104 if ($(e.target).is('[open]') === e.value) { 3105 $(e.target).find('> summary').trigger('click'); 3106 } 3107 } 3108 }); 3109})(jQuery, Drupal);; 3110/** 3111 * @file 3112 * Extends core/misc/states.js. 3113 */ 3114(function($) { 3115 3116 // Unbind core state.js from document first so we can then override below. 3117 $(document).unbind('state:disabled'); 3118 3119 /** 3120 * Global state change handlers. These are bound to "document" to cover all 3121 * elements whose state changes. Events sent to elements within the page 3122 * bubble up to these handlers. We use this system so that themes and modules 3123 * can override these state change handlers for particular parts of a page. 3124 */ 3125 $(document).bind('state:disabled', function(e) { 3126 // Only act when this change was triggered by a dependency and not by the 3127 // element monitoring itself. 3128 if (e.trigger) { 3129 $(e.target) 3130 .attr('disabled', e.value) 3131 .closest('.form-item, .form-submit, .form-wrapper').toggleClass('form-disabled', e.value) 3132 .find(':input').attr('disabled', e.value); 3133 3134 // Note: WebKit nightlies don't reflect that change correctly. 3135 // See https://bugs.webkit.org/show_bug.cgi?id=23789 3136 } 3137 }); 3138 3139})(jQuery); 3140; 3141/** 3142 * @file 3143 * JavaScript behaviors for custom webform #states. 3144 */ 3145 3146(function ($, Drupal) { 3147 3148 'use strict'; 3149 3150 Drupal.webform = Drupal.webform || {}; 3151 Drupal.webform.states = Drupal.webform.states || {}; 3152 Drupal.webform.states.slideDown = Drupal.webform.states.slideDown || {}; 3153 Drupal.webform.states.slideDown.duration = 'slow'; 3154 Drupal.webform.states.slideUp = Drupal.webform.states.slideUp || {}; 3155 Drupal.webform.states.slideUp.duration = 'fast'; 3156 3157 /* ************************************************************************ */ 3158 // jQuery functions. 3159 /* ************************************************************************ */ 3160 3161 /** 3162 * Check if an element has a specified data attribute. 3163 * 3164 * @param {string} data 3165 * The data attribute name. 3166 * 3167 * @return {boolean} 3168 * TRUE if an element has a specified data attribute. 3169 */ 3170 $.fn.hasData = function (data) { 3171 return (typeof this.data(data) !== 'undefined'); 3172 }; 3173 3174 /**
3175 * Check if element is within the webform or not. 3176 * 3177 * @return {boolean} 3178 * TRUE if element is within the webform. 3179 */ 3180 $.fn.isWebform = function () { 3181 return $(this).closest('form.webform-submission-form, form[id^="webform"], form[data-is-webform]').length ? true : false; 3182 }; 3183 3184 /** 3185 * Check if element is to be treated as a webform element. 3186 * 3187 * @return {boolean} 3188 * TRUE if element is to be treated as a webform element. 3189 */ 3190 $.fn.isWebformElement = function () { 3191 return ($(this).isWebform() || $(this).closest('[data-is-webform-element]').length) ? true : false; 3192 }; 3193 3194 /* ************************************************************************ */ 3195 // Trigger. 3196 /* ************************************************************************ */ 3197 3198 // The change event is triggered by cut-n-paste and select menus. 3199 // Issue #2445271: #states element empty check not triggered on mouse 3200 // based paste. 3201 // @see https://www.drupal.org/node/2445271 3202 Drupal.states.Trigger.states.empty.change = function change() { 3203 return this.val() === ''; 3204 }; 3205 3206 /* ************************************************************************ */ 3207 // Dependents. 3208 /* ************************************************************************ */ 3209 3210 // Apply solution included in #1962800 patch. 3211 // Issue #1962800: Form #states not working with literal integers as 3212 // values in IE11. 3213 // @see https://www.drupal.org/project/drupal/issues/1962800 3214 // @see https://www.drupal.org/files/issues/core-states-not-working-with-integers-ie11_1962800_46.patch 3215 // 3216 // This issue causes pattern, less than, and greater than support to break. 3217 // @see https://www.drupal.org/project/webform/issues/2981724 3218 var states = Drupal.states; 3219 Drupal.states.Dependent.prototype.compare = function compare(reference, selector, state) { 3220 var value = this.values[selector][state.name]; 3221 3222 var name = reference.constructor.name; 3223 if (!name) { 3224 name = $.type(reference); 3225 3226 name = name.charAt(0).toUpperCase() + name.slice(1); 3227 } 3228 if (name in states.Dependent.comparisons) { 3229 return states.Dependent.comparisons[name](reference, value); 3230 } 3231 3232 if (reference.constructor.name in states.Dependent.comparisons) { 3233 return states.Dependent.comparisons[reference.constructor.name](reference, value); 3234 } 3235 3236 return _compare2(reference, value); 3237 }; 3238 function _compare2(a, b) { 3239 if (a === b) { 3240 return typeof a === 'undefined' ? a : true; 3241 } 3242 3243 return typeof a === 'undefined' || typeof b === 'undefined'; 3244 } 3245 3246 // Adds pattern, less than, and greater than support to #state API. 3247 // @see http://drupalsun.com/julia-evans/2012/03/09/extending-form-api-states-regular-expressions 3248 Drupal.states.Dependent.comparisons.Object = function (reference, value) { 3249 if ('pattern' in reference) { 3250 return (new RegExp(reference['pattern'])).test(value); 3251 } 3252 else if ('!pattern' in reference) { 3253 return !((new RegExp(reference['!pattern'])).test(value)); 3254 } 3255 else if ('less' in reference) { 3256 return (value !== '' && parseFloat(reference['less']) > parseFloat(value)); 3257 } 3258 else if ('less_equal' in reference) { 3259 return (value !== '' && parseFloat(reference['less_equal']) >= parseFloat(value)); 3260 } 3261 else if ('greater' in reference) { 3262 return (value !== '' && parseFloat(reference['greater']) < parseFloat(value)); 3263 } 3264 else if ('greater_equal' in reference) { 3265 return (value !== '' && parseFloat(reference['greater_equal']) <= parseFloat(value)); 3266 } 3267 else if ('between' in reference || '!between' in reference) { 3268 if (value === '') { 3269 return false; 3270 } 3271 3272 var between = reference['between'] || reference['!between']; 3273 var betweenParts = between.split(':'); 3274 var greater = betweenParts[0]; 3275 var less = (typeof betweenParts[1] !== 'undefined') ? betweenParts[1] : null; 3276 var isGreaterThan = (greater === null || greater === '' || parseFloat(value) >= parseFloat(greater)); 3277 var isLessThan = (less === null || less === '' || parseFloat(value) <= parseFloat(less)); 3278 var result = (isGreaterThan && isLessThan); 3279 return (reference['!between']) ? !result : result; 3280 } 3281 else { 3282 return reference.indexOf(value) !== false; 3283 } 3284 }; 3285 3286 /* ************************************************************************ */ 3287 // States events. 3288 /* ************************************************************************ */ 3289 3290 var $document = $(document); 3291 3292 $document.on('state:required', function (e) { 3293 if (e.trigger && $(e.target).isWebformElement()) { 3294 var $target = $(e.target); 3295 // Fix #required file upload. 3296 // @see Issue #2860529: Conditional required File upload field don't work. 3297 toggleRequired($target.find('input[type="file"]'), e.value); 3298 3299 // Fix #required for radios and likert. 3300 // @see Issue #2856795: If radio buttons are required but not filled form is nevertheless submitted. 3301 if ($target.is('.js-form-type-radios, .js-form-type-webform-radios-other, .js-webform-type-radios, .js-webform-type-webform-radios-other, .js-webform-type-webform-entity-radios, .webform-likert-table')) { 3302 $target.toggleClass('required', e.value); 3303 toggleRequired($target.find('input[type="radio"]'), e.value); 3304 } 3305 3306 // Fix #required for checkboxes. 3307 // @see Issue #2938414: Checkboxes don't support #states required. 3308 // @see checkboxRequiredhandler 3309 if ($target.is('.js-form-type-checkboxes, .js-form-type-webform-checkboxes-other, .js-webform-type-checkboxes, .js-webform-type-webform-checkboxes-other')) { 3310 $target.toggleClass('required', e.value); 3311 var $checkboxes = $target.find('input[type="checkbox"]'); 3312 if (e.value) { 3313 // Add event handler. 3314 $checkboxes.on('click', statesCheckboxesRequiredEventHandler); 3315 // Initialize and add required attribute. 3316 checkboxesRequired($target); 3317 } 3318 else { 3319 // Remove event handler. 3320 $checkboxes.off('click', statesCheckboxesRequiredEventHandler); 3321 // Remove required attribute. 3322 toggleRequired($checkboxes, false); 3323 } 3324 } 3325 3326 // Fix #required for tableselect. 3327 // @see Issue #3212581: Table select does not trigger client side validation 3328 if ($target.is('.js-webform-tableselect')) { 3329 $target.toggleClass('required', e.value); 3330 var isMultiple = $target.is('[multiple]'); 3331 if (isMultiple) { 3332 // Checkboxes. 3333 var $tbody = $target.find('tbody'); 3334 var $checkboxes = $tbody.find('input[type="checkbox"]'); 3335 copyRequireMessage($target, $checkboxes); 3336 if (e.value) { 3337 $checkboxes.on('click change', statesCheckboxesRequiredEventHandler); 3338 checkboxesRequired($tbody); 3339 } 3340 else { 3341 $checkboxes.off('click change ', statesCheckboxesRequiredEventHandler); 3342 toggleRequired($tbody, false); 3343 } 3344 } 3345 else { 3346 // Radios. 3347 var $radios = $target.find('input[type="radio"]'); 3348 copyRequireMessage($target, $radios); 3349 toggleRequired($radios, e.value); 3350 } 3351 } 3352 3353 // Fix required label for elements without the for attribute. 3354 // @see Issue #3145300: Conditional Visible Select Other not working. 3355 if ($target.is('.js-form-type-webform-select-other, .js-webform-type-webform-select-other')) { 3356 var $select = $target.find('select'); 3357 toggleRequired($select, e.value); 3358 copyRequireMessage($target, $select); 3359 } 3360 if ($target.find('> label:not([for])').length) { 3361 $target.find('> label').toggleClass('js-form-required form-required', e.value); 3362 } 3363 3364 // Fix required label for checkboxes and radios. 3365 // @see Issue #2938414: Checkboxes don't support #states required 3366 // @see Issue #2731991: Setting required on radios marks all options required. 3367 // @see Issue #2856315: Conditional Logic - Requiring Radios in a Fieldset. 3368 // Fix #required for fieldsets. 3369 // @see Issue #2977569: Hidden fieldsets that become visible with conditional logic cannot be made required. 3370 if ($target.is('.js-webform-type-radios, .js-webform-type-checkboxes, fieldset')) { 3371 $target.find('legend span.fieldset-legend:not(.visually-hidden)').toggleClass('js-form-required form-required', e.value); 3372 } 3373 3374 // Issue #2986017: Fieldsets shouldn't have required attribute. 3375 if ($target.is('fieldset')) { 3376 $target.removeAttr('required aria-required'); 3377 } 3378 } 3379 }); 3380 3381 $document.on('state:checked', function (e) { 3382 if (e.trigger) { 3383 $(e.target).trigger('change'); 3384 } 3385 }); 3386 3387 $document.on('state:readonly', function (e) { 3388 if (e.trigger && $(e.target).isWebformElement()) { 3389 $(e.target).prop('readonly', e.value).closest('.js-form-item, .js-form-wrapper').toggleClass('webform-readonly', e.value).find('input, textarea').prop('readonly', e.value); 3390 3391 // Trigger webform:readonly. 3392 $(e.target).trigger('webform:readonly') 3393 .find('select, input, textarea, button').trigger('webform:readonly'); 3394 } 3395 }); 3396 3397 $document.on('state:visible state:visible-slide', function (e) { 3398 if (e.trigger && $(e.target).isWebformElement()) { 3399 if (e.value) { 3400 $(':input', e.target).addBack().each(function () { 3401 restoreValueAndRequired(this); 3402 triggerEventHandlers(this); 3403 }); 3404 } 3405 else { 3406 // @see https://www.sitepoint.com/jquery-function-clear-form-data/ 3407 $(':input', e.target).addBack().each(function () { 3408 backupValueAndRequired(this); 3409 clearValueAndRequired(this); 3410 triggerEventHandlers(this); 3411 }); 3412 } 3413 } 3414 }); 3415 3416 $document.on('state:visible-slide', function (e) { 3417 if (e.trigger && $(e.target).isWebformElement()) { 3418 var effect = e.value ? 'slideDown' : 'slideUp'; 3419 var duration = Drupal.webform.states[effect].duration; 3420 $(e.target).closest('.js-form-item, .js-form-submit, .js-form-wrapper')[effect](duration); 3421 } 3422 }); 3423 Drupal.states.State.aliases['invisible-slide'] = '!visible-slide'; 3424 3425 $document.on('state:disabled', function (e) { 3426 if (e.trigger && $(e.target).isWebformElement()) { 3427 // Make sure disabled property is set before triggering webform:disabled. 3428 // Copied from: core/misc/states.js 3429 $(e.target) 3430 .prop('disabled', e.value) 3431 .closest('.js-form-item, .js-form-submit, .js-form-wrapper').toggleClass('form-disabled', e.value) 3432 .find('select, input, textarea, button').prop('disabled', e.value); 3433 3434 // Never disable hidden file[fids] because the existing values will 3435 // be completely lost when the webform is submitted. 3436 var fileElements = $(e.target) 3437 .find(':input[type="hidden"][name$="[fids]"]'); 3438 if (fileElements.length) { 3439 // Remove 'disabled' attribute from fieldset which will block 3440 // all disabled elements from being submitted. 3441 if ($(e.target).is('fieldset')) { 3442 $(e.target).prop('disabled', false); 3443 } 3444 fileElements.removeAttr('disabled'); 3445 } 3446 3447 // Trigger webform:disabled. 3448 $(e.target).trigger('webform:disabled') 3449 .find('select, input, textarea, button').trigger('webform:disabled'); 3450 } 3451 }); 3452 3453 /* ************************************************************************ */ 3454 // Behaviors. 3455 /* ************************************************************************ */ 3456 3457 /** 3458 * Adds HTML5 validation to required checkboxes. 3459 * 3460 * @type {Drupal~behavior} 3461 * 3462 * @see https://www.drupal.org/project/webform/issues/3068998 3463 */ 3464 Drupal.behaviors.webformCheckboxesRequired = { 3465 attach: function (context) { 3466 $('.js-form-type-checkboxes.required, .js-form-type-webform-checkboxes-other.required, .js-webform-type-checkboxes.required, .js-webform-type-webform-checkboxes-other.required, .js-webform-type-webform-radios-other.checkboxes', context) 3467 .once('webform-checkboxes-required') 3468 .each(function () { 3469 var $element = $(this); 3470 $element.find('input[type="checkbox"]').on('click', statesCheckboxesRequiredEventHandler); 3471 setTimeout(function () {checkboxesRequired($element);}); 3472 }); 3473 } 3474 }; 3475 3476 /** 3477 * Adds HTML5 validation to required radios. 3478 * 3479 * @type {Drupal~behavior} 3480 * 3481 * @see https://www.drupal.org/project/webform/issues/2856795 3482 */ 3483 Drupal.behaviors.webformRadiosRequired = { 3484 attach: function (context) { 3485 $('.js-form-type-radios, .js-form-type-webform-radios-other, .js-webform-type-radios, .js-webform-type-webform-radios-other, .js-webform-type-webform-entity-radios, .js-webform-type-webform-scale', context) 3486 .once('webform-radios-required') 3487 .each(function () { 3488 var $element = $(this); 3489 setTimeout(function () {radiosRequired($element);}); 3490 }); 3491 } 3492 }; 3493 3494 /** 3495 * Adds HTML5 validation to required table select. 3496 * 3497 * @type {Drupal~behavior} 3498 * 3499 * @see https://www.drupal.org/project/webform/issues/2856795 3500 */ 3501 Drupal.behaviors.webformTableSelectRequired = { 3502 attach: function (context) { 3503 $('.js-webform-tableselect.required', context) 3504 .once('webform-tableselect-required') 3505 .each(function () { 3506 var $element = $(this); 3507 var $tbody = $element.find('tbody'); 3508 var isMultiple = $element.is('[multiple]'); 3509 3510 if (isMultiple) { 3511 // Check all checkbox triggers checkbox 'change' event on 3512 // select and deselect all. 3513 // @see Drupal.tableSelect 3514 $tbody.find('input[type="checkbox"]').on('click change', function () { 3515 checkboxesRequired($tbody); 3516 }); 3517 } 3518 3519 setTimeout(function () { 3520 isMultiple ? checkboxesRequired($tbody) : radiosRequired($element); 3521 }); 3522 }); 3523 } 3524 }; 3525 3526 /** 3527 * Add HTML5 multiple checkboxes required validation. 3528 * 3529 * @param {jQuery} $element
3530 * An jQuery object containing HTML5 radios. 3531 * 3532 * @see https://stackoverflow.com/a/37825072/145846 3533 */ 3534 function checkboxesRequired($element) { 3535 var $firstCheckbox = $element.find('input[type="checkbox"]').first(); 3536 var isChecked = $element.find('input[type="checkbox"]').is(':checked'); 3537 toggleRequired($firstCheckbox, !isChecked); 3538 copyRequireMessage($element, $firstCheckbox); 3539 } 3540 3541 /** 3542 * Add HTML5 radios required validation. 3543 * 3544 * @param {jQuery} $element 3545 * An jQuery object containing HTML5 radios. 3546 * 3547 * @see https://www.drupal.org/project/webform/issues/2856795 3548 */ 3549 function radiosRequired($element) { 3550 var $radios = $element.find('input[type="radio"]'); 3551 var isRequired = $element.hasClass('required'); 3552 toggleRequired($radios, isRequired); 3553 copyRequireMessage($element, $radios); 3554 } 3555 3556 /* ************************************************************************ */ 3557 // Event handlers. 3558 /* ************************************************************************ */ 3559 3560 /** 3561 * Trigger #states API HTML5 multiple checkboxes required validation. 3562 * 3563 * @see https://stackoverflow.com/a/37825072/145846 3564 */ 3565 function statesCheckboxesRequiredEventHandler() { 3566 var $element = $(this).closest('.js-webform-type-checkboxes, .js-webform-type-webform-checkboxes-other'); 3567 checkboxesRequired($element); 3568 } 3569 3570 /** 3571 * Trigger an input's event handlers. 3572 * 3573 * @param {element} input 3574 * An input. 3575 */ 3576 function triggerEventHandlers(input) { 3577 var $input = $(input); 3578 var type = input.type; 3579 var tag = input.tagName.toLowerCase(); 3580 // Add 'webform.states' as extra parameter to event handlers. 3581 // @see Drupal.behaviors.webformUnsaved 3582 var extraParameters = ['webform.states']; 3583 if (type === 'checkbox' || type === 'radio') { 3584 $input 3585 .trigger('change', extraParameters) 3586 .trigger('blur', extraParameters); 3587 } 3588 else if (tag === 'select') { 3589 // Do not trigger the onchange event for Address element's country code 3590 // when it is initialized. 3591 // @see \Drupal\address\Element\Country 3592 if ($input.closest('.webform-type-address').length) { 3593 if (!$input.data('webform-states-address-initialized') 3594 && $input.attr('autocomplete') === 'country' 3595 && $input.val() === $input.find("option[selected]").attr('value')) { 3596 return; 3597 } 3598 $input.data('webform-states-address-initialized', true); 3599 } 3600 3601 $input 3602 .trigger('change', extraParameters) 3603 .trigger('blur', extraParameters); 3604 } 3605 else if (type !== 'submit' && type !== 'button' && type !== 'file') { 3606 // Make sure input mask is removed and then reset when value is restored. 3607 // @see https://www.drupal.org/project/webform/issues/3124155 3608 // @see https://www.drupal.org/project/webform/issues/3202795 3609 var hasInputMask = ($.fn.inputmask && $input.hasClass('js-webform-input-mask')); 3610 hasInputMask && $input.inputmask('remove'); 3611 3612 $input 3613 .trigger('input', extraParameters) 3614 .trigger('change', extraParameters) 3615 .trigger('keydown', extraParameters) 3616 .trigger('keyup', extraParameters) 3617 .trigger('blur', extraParameters); 3618 3619 hasInputMask && $input.inputmask(); 3620 } 3621 } 3622 3623 /* ************************************************************************ */ 3624 // Backup and restore value functions. 3625 /* ************************************************************************ */ 3626 3627 /** 3628 * Backup an input's current value and required attribute 3629 * 3630 * @param {element} input 3631 * An input. 3632 */ 3633 function backupValueAndRequired(input) { 3634 var $input = $(input); 3635 var type = input.type; 3636 var tag = input.tagName.toLowerCase(); // Normalize case. 3637 3638 // Backup required. 3639 if ($input.prop('required') && !$input.hasData('webform-required')) { 3640 $input.data('webform-required', true); 3641 } 3642 3643 // Backup value. 3644 if (!$input.hasData('webform-value')) { 3645 if (type === 'checkbox' || type === 'radio') { 3646 $input.data('webform-value', $input.prop('checked')); 3647 } 3648 else if (tag === 'select') { 3649 var values = []; 3650 $input.find('option:selected').each(function (i, option) { 3651 values[i] = option.value; 3652 }); 3653 $input.data('webform-value', values); 3654 } 3655 else if (type !== 'submit' && type !== 'button') { 3656 $input.data('webform-value', input.value); 3657 } 3658 } 3659 } 3660 3661 /** 3662 * Restore an input's value and required attribute. 3663 * 3664 * @param {element} input 3665 * An input. 3666 */ 3667 function restoreValueAndRequired(input) { 3668 var $input = $(input); 3669 3670 // Restore value. 3671 var value = $input.data('webform-value'); 3672 if (typeof value !== 'undefined') { 3673 var type = input.type; 3674 var tag = input.tagName.toLowerCase(); // Normalize case. 3675 3676 if (type === 'checkbox' || type === 'radio') { 3677 $input.prop('checked', value); 3678 } 3679 else if (tag === 'select') { 3680 $.each(value, function (i, option_value) { 3681 // Prevent "Syntax error, unrecognized expression" error by 3682 // escaping single quotes. 3683 // @see https://forum.jquery.com/topic/escape-characters-prior-to-using-selector 3684 option_value = option_value.replace(/'/g, "\\\'"); 3685 $input.find("option[value='" + option_value + "']").prop('selected', true); 3686 }); 3687 } 3688 else if (type !== 'submit' && type !== 'button') { 3689 input.value = value; 3690 } 3691 $input.removeData('webform-value'); 3692 } 3693 3694 // Restore required. 3695 var required = $input.data('webform-required'); 3696 if (typeof required !== 'undefined') { 3697 if (required) { 3698 $input.prop('required', true); 3699 } 3700 $input.removeData('webform-required'); 3701 } 3702 } 3703 3704 /** 3705 * Clear an input's value and required attributes. 3706 * 3707 * @param {element} input 3708 * An input. 3709 */ 3710 function clearValueAndRequired(input) { 3711 var $input = $(input); 3712 3713 // Check for #states no clear attribute.
3714 // @see https://css-tricks.com/snippets/jquery/make-an-jquery-hasattr/ 3715 if ($input.closest('[data-webform-states-no-clear]').length) { 3716 return; 3717 } 3718 3719 // Clear value. 3720 var type = input.type; 3721 var tag = input.tagName.toLowerCase(); // Normalize case. 3722 if (type === 'checkbox' || type === 'radio') { 3723 $input.prop('checked', false); 3724 } 3725 else if (tag === 'select') { 3726 if ($input.find('option[value=""]').length) { 3727 $input.val(''); 3728 } 3729 else { 3730 input.selectedIndex = -1; 3731 } 3732 } 3733 else if (type !== 'submit' && type !== 'button') { 3734 input.value = (type === 'color') ? '#000000' : ''; 3735 } 3736 3737 // Clear required. 3738 $input.prop('required', false); 3739 } 3740 3741 /* ************************************************************************ */ 3742 // Helper functions. 3743 /* ************************************************************************ */ 3744 3745 /** 3746 * Toggle an input's required attributes. 3747 * 3748 * @param {element} $input 3749 * An input. 3750 * @param {boolean} required 3751 * Is input required. 3752 */ 3753 function toggleRequired($input, required) { 3754 var isCheckboxOrRadio = ($input.attr('type') === 'radio' || $input.attr('type') === 'checkbox'); 3755 if (required) { 3756 if (isCheckboxOrRadio) { 3757 $input.attr({'required': 'required'}); 3758 } 3759 else { 3760 $input.attr({'required': 'required', 'aria-required': 'true'}); 3761 } 3762 } 3763 else { 3764 if (isCheckboxOrRadio) { 3765 $input.removeAttr('required'); 3766 } 3767 else { 3768 $input.removeAttr('required aria-required'); 3769 } 3770 } 3771 } 3772 3773 /** 3774 * Copy the clientside_validation.module's message. 3775 * 3776 * @param {jQuery} $source 3777 * The source element. 3778 * @param {jQuery} $destination 3779 * The destination element. 3780 */ 3781 function copyRequireMessage($source, $destination) { 3782 if ($source.attr('data-msg-required')) { 3783 $destination.attr('data-msg-required', $source.attr('data-msg-required')); 3784 } 3785 } 3786 3787})(jQuery, Drupal); 3788; 3789/** 3790 * @file 3791 * JavaScript behaviors for custom webform #states. 3792 */ 3793 3794(function ($, Drupal) { 3795 3796 'use strict'; 3797 3798 $(document).on('state:required', function (e) { 3799 if (e.trigger && $(e.target).isWebform()) { 3800 var $target = $(e.target); 3801 3802 // @see Issue #2856315: Conditional Logic - Requiring Radios in a Fieldset. 3803 // Fix #required for fieldsets. 3804 if ($target.is('.js-form-wrapper.panel')) { 3805 if (e.value) { 3806 $target.find('.panel-heading .panel-title').addClass('js-form-required form-required'); 3807 } 3808 else { 3809 $target.find('.panel-heading .panel-title').removeClass('js-form-required form-required'); 3810 } 3811 } 3812 3813 } 3814 }); 3815 3816})(jQuery, Drupal); 3817;
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.