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') || $element.is('select')) { 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 // Remove any message set. 2121 this.progress.element.parent().find('.message').remove(); 2122 } 2123 2124 // Invoke the original success handler. 2125 return success.apply(this, [response, status]); 2126 }; 2127 2128})(jQuery, this, Drupal, drupalSettings); 2129; 2130/** 2131* DO NOT EDIT THIS FILE. 2132* See the following change record for more information, 2133* https://www.drupal.org/node/2815083 2134* @preserve 2135**/ 2136(function ($, Drupal, _ref) { 2137 var isTabbable = _ref.isTabbable; 2138 $.extend($.expr[':'], { 2139 tabbable: function tabbable(element) { 2140 Drupal.deprecationError({
2141 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' 2142 }); 2143 if (element.tagName === 'SUMMARY' || element.tagName === 'DETAILS') { 2144 var tabIndex = element.getAttribute('tabIndex'); 2145 if (tabIndex === null || tabIndex < 0) { 2146 return false; 2147 } 2148 } 2149 return isTabbable(element); 2150 } 2151 }); 2152})(jQuery, Drupal, window.tabbable);;
vendor: 327 bytes, lines 2152-2153
2152 2153(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','
2153GTM-KMLH4SZ
vendor: 3 bytes, line 2153
2153');
2153; 2154// Custom scripts file 2155 2156(function ($) { 2157 2158 'use strict'; 2159 2160 // Generic function that runs on window resize. 2161 function resizeStuff() { 2162 2163 } 2164 2165 // Runs function once on window resize. 2166 var TO = false; 2167 $(window).resize(function () { 2168 if (TO !== false) { 2169 clearTimeout(TO); 2170 } 2171 2172 // 200 is time in miliseconds. 2173 TO = setTimeout(resizeStuff, 200); 2174 }).resize(); 2175 2176 // Count function for the infographic component types 2177 $('.count').each(function () { 2178 $(this).prop('Counter',0).animate({ 2179 Counter: $(this).text() 2180 }, { 2181 duration: 3000, 2182 easing: 'swing', 2183 step: function (now) { 2184 $(this).text(Math.ceil(now).toLocaleString('en')); 2185 } 2186 }); 2187 }); 2188 $("#closeit").click(function() { 2189 $("#shoutbar").slideUp(); 2190 }); 2191 // $(document).ready(function(){$("#openit").click(function(){$("#shoutbar").effect("bounce","slow");$("#openit").slideUp()})}); 2192 // $(document).ready(function(){$("#shoutbar").effect("bounce","slow");$("#closeit").click(function(){$("#shoutbar").slideUp();$("#openit").slideDown()})}); 2193})(jQuery);; 2194$ = jQuery; 2195$(function(){ 2196 // Add classes to header elements so they can shrink when the page scrolls 2197 var shrinkHeader = 0; 2198 var glitchHeader = 150; 2199 $(window).scroll(function() { 2200 var scroll = getCurrentScroll(); 2201 if ( scroll >= glitchHeader ) { 2202 $('header.header').addClass('shrink'); 2203 $('.site-logo__grid').addClass('shrink'); 2204 // $('.region-midline').addClass('shrink'); 2205 // $('.page-wrapper').addClass('shrink'); 2206 // $('body.user-logged-in header.header').removeClass('shrink'); 2207 // $('body.user-logged-in .site-logo__grid').removeClass('shrink'); 2208 // $('body.user-logged-in .region-midline').removeClass('shrink'); 2209 } 2210 else if ( scroll <= shrinkHeader ) { 2211 $('header.header').removeClass('shrink'); 2212 $('.site-logo__grid').removeClass('shrink'); 2213 // $('.region-midline').removeClass('shrink'); 2214 // $('.page-wrapper').removeClass('shrink'); 2215 } 2216 document.onscroll = scroll; 2217 }); 2218 function getCurrentScroll() { 2219 // console.log(scroll); 2220 return window.pageYOffset || document.documentElement.scrollTop; 2221 } 2222 2223 // Add a click function for the ... icon in the footer of the sliding navigation 2224 $("a#toggle").click(function() 2225 { 2226 $(".mm-search").slideToggle(); 2227 $(".mm-searchfield").slideToggle(); 2228 $(".mmenu_tsesearch").slideToggle(); 2229 $(".mmenu_tsecoop").slideToggle(); 2230 return false; 2231 }); 2232 2233 // Create a clone of the menu, right next to original. 2234 // Clone is used to sync up elements when the header becomes fixed after scrolling 50 pixels vertically down the page 2235 // $('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(); 2236 // 2237 // scrollIntervalID = setInterval(stickIt, 10); 2238 // 2239 // function stickIt() { 2240 // 2241 // var orgElementPos = $('.original').offset(); 2242 // orgElementTop = orgElementPos.top; 2243 // 2244 // if ($(window).scrollTop() >= (orgElementTop)) { 2245 // // scrolled past the original position; now only show the cloned, sticky element. 2246 // 2247 // // Cloned element should always have same left position and width as original element. 2248 // orgElement = $('.original'); 2249 // coordsOrgElement = orgElement.offset(); 2250 // leftOrgElement = coordsOrgElement.left; 2251 // widthOrgElement = orgElement.css('width'); 2252 // $('.cloned').css('left',leftOrgElement+'px').css('top',0).css('width',widthOrgElement).show(); 2253 // $('.original').css('visibility','hidden'); 2254 // } else { 2255 // // not scrolled past the menu; only show the original menu. 2256 // $('.cloned').hide(); 2257 // $('.original').css('visibility','visible'); 2258 // } 2259 // } 2260}); 2261 2262// function rotateCard(btn){ 2263// var $card = $(btn).closest('.card-container'); 2264// console.log($card); 2265// if($card.hasClass('hover')){ 2266// $card.removeClass('hover'); 2267// } else { 2268// $card.addClass('hover'); 2269// } 2270// } 2271 2272function rotateCard(btn){ 2273 var $card = $(btn).closest('.card-container'); 2274 console.log($card); 2275 if($card.hasClass('hover')){ 2276 $card.removeClass('hover'); 2277 } else { 2278 $card.addClass('hover'); 2279 } 2280} 2281 2282// (function ($, undefined) { 2283// function rotateCard(btn){ 2284// var
2285// $card = $(btn).closest('.card-container'), 2286// $toggle = $card.hasClass('hover'); 2287// $card.toggleClass(!$toggle, 'hover'); 2288// } 2289// })(jQuery);; 2290/** 2291 * Due to the construction of the responsive_menu module, there exists no 2292 * API or method of injecting navbar information 2293 */ 2294(function ($, undefined) { 2295 'use strict'; 2296 /** 2297 * Provides the off-canvas menu. 2298 * 2299 * @type {Drupal~behavior} 2300 * 2301 * @prop {Drupal~behaviorAttach} attach 2302 * Attaches the behavior for the off-canvas menu. 2303 */ 2304 Drupal.behaviors.responsive_menu_mmenu = { 2305 attach: function (context) { 2306 $('body').once('cwb-responsive-menu-mmenu').each(function () { 2307 const mmenuId = '#off-canvas'; 2308 const offCanvas = document.querySelector(mmenuId); 2309 const settings = drupalSettings.responsive_menu; 2310 const position = settings.position, 2311 theme = settings.theme, 2312 pagedim = settings.pagedim; 2313 2314 const options = { 2315 extensions: [ 2316 theme, 2317 'fx-menu-slide', 2318 position === 'left' ? 'position-left' : 'position-right' 2319 ], 2320 keyboardNavigation: { 2321 enable: true, 2322 enhance: true, 2323 }, 2324 drag: { 2325 open: settings.drag 2326 }, 2327 navbars: [ 2328 { 2329 "position": "bottom", 2330 "content": [ 2331 "<a class='glyphicon glyphicon-option-horizontal' href='#' id='toggle' aria-hidden='true'></a>", 2332 "searchfield", 2333 "<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>", 2334 "<div class='mmenu_tsecoop'>Touchstone Energy® Cooperatives</div></div>" 2335 ] 2336 } 2337 ] 2338 }; 2339 2340 if (pagedim !== 'none') { 2341 options.extensions.push(pagedim); 2342 } 2343 2344 const config = { 2345 classNames: { 2346 selected: 'menu-item--active-trail' 2347 } 2348 }; 2349 2350 // Allow the settings and options to be extended or overridden. 2351 if (typeof settings.custom !== 'undefined') { 2352 if (typeof settings.custom.options !== 'undefined') { 2353 extend(options, drupalSettings.responsive_menu.custom.options); 2354 } 2355 if (typeof drupalSettings.responsive_menu.custom.config !== 'undefined') { 2356 extend(config, drupalSettings.responsive_menu.custom.config); 2357 } 2358 } 2359 2360 // Set up the off canvas menu. 2361 const mmenu = new Mmenu(mmenuId, options, config); 2362 2363 // Due to a rendering issue with Chrome the page needs the viewport 2364 // metatag to have a value including initial-scale=1.0 otherwise it 2365 // won't render properly. 2366 // @see issue #3153145 2367 const mmenuApi = mmenu.API; 2368 const viewports = document.getElementsByName('viewport'); 2369 2370 if (viewports.length !== 0 && settings.modifyViewport) { 2371 const viewportMeta = viewports[0]; 2372 const defaultViewport = viewports[0].content; 2373 const staticViewport = "width=device-width, initial-scale=1.0, minimum-scale=1.0"; 2374 2375 mmenuApi.bind('open:start', function () { 2376 viewportMeta.setAttribute('content', staticViewport); 2377 }); 2378 mmenuApi.bind('close:start', function () { 2379 viewportMeta.setAttribute('content', defaultViewport); 2380 }); 2381 } 2382 }); 2383 } 2384 }; 2385})(jQuery); 2386; 2387var timeOut; 2388function scrollToTop() { 2389 if (document.body.scrollTop!=0 || document.documentElement.scrollTop!=0){ 2390 window.scrollBy(0,-50); 2391 timeOut=setTimeout('scrollToTop()',10); 2392 } 2393 else clearTimeout(timeOut); 2394}; 2395(function ($) { 2396 2397 'use strict'; 2398 2399 Drupal.behaviors.timeline = { 2400 attach: function (context, settings) { 2401 2402 var $timeline_block = $('.cd-timeline-block'); 2403 2404 $('main .cd-timeline-block', context).once('timeline').each(function(){ 2405 console.log($(this)); 2406 2407 //hide any timeline blocks that are outside of the viewport 2408 if($(this).offset().top > $(window).scrollTop()+$(window).height()*0.75) { 2409 $(this).find('.cd-timeline-img, .cd-timeline-content').addClass('is-hidden'); 2410 } 2411 2412 //on scrolling, show/animate the timeline blocks as they enter the viewport 2413 $(window).on('scroll', function(){ 2414 $timeline_block.each(function(){ 2415 if( $(this).offset().top <= $(window).scrollTop()+$(window).height()*0.75 && $(this).find('.cd-timeline-img').hasClass('is-hidden') ) { 2416 $(this).find('.cd-timeline-img, .cd-timeline-content').removeClass('is-hidden').addClass('bounce-in'); 2417 } 2418 }); 2419 }); 2420 }); 2421 } 2422 }; 2423})(jQuery);; 2424/** 2425 * @file 2426 * External links js file. 2427 */ 2428 2429(function ($, Drupal, drupalSettings) { 2430 2431 'use strict'; 2432 2433 Drupal.extlink = Drupal.extlink || {}; 2434 2435 Drupal.extlink.attach = function (context, drupalSettings) { 2436 if (typeof drupalSettings.data === 'undefined' || !drupalSettings.data.hasOwnProperty('extlink')) { 2437 return; 2438 } 2439 2440 // Define the jQuery method (either 'append' or 'prepend') of placing the 2441 // icon, defaults to 'append'. 2442 var extIconPlacement = 'append'; 2443 if (drupalSettings.data.extlink.extIconPlacement && drupalSettings.data.extlink.extIconPlacement != '0') { 2444 extIconPlacement = drupalSettings.data.extlink.extIconPlacement; 2445 } 2446 2447 // Strip the host name down, removing ports, subdomains, or www. 2448 var pattern = /^(([^\/:]+?\.)*)([^\.:]{1,})((\.[a-z0-9]{1,253})*)(:[0-9]{1,5})?$/; 2449 var host = window.location.host.replace(pattern, '$2$3$6'); 2450 var subdomain = window.location.host.replace(host, ''); 2451 2452 // Determine what subdomains are considered internal. 2453 var subdomains; 2454 if (drupalSettings.data.extlink.extSubdomains) { 2455 subdomains = '([^/]*\\.)?'; 2456 } 2457 else if (subdomain === 'www.' || subdomain === '') { 2458 subdomains = '(www\\.)?'; 2459 } 2460 else { 2461 subdomains = subdomain.replace('.', '\\.'); 2462 } 2463 2464 // Whitelisted domains. 2465 var whitelistedDomains = false;
2466 if (drupalSettings.data.extlink.whitelistedDomains) { 2467 whitelistedDomains = []; 2468 for (var i = 0; i < drupalSettings.data.extlink.whitelistedDomains.length; i++) { 2469 whitelistedDomains.push(new RegExp('^https?:\\/\\/' + drupalSettings.data.extlink.whitelistedDomains[i].replace(/(\r\n|\n|\r)/gm,'') + '.*$', 'i')); 2470 } 2471 } 2472 2473 // Build regular expressions that define an internal link. 2474 var internal_link = new RegExp('^https?://([^@]*@)?' + subdomains + host, 'i'); 2475 2476 // Extra internal link matching. 2477 var extInclude = false; 2478 if (drupalSettings.data.extlink.extInclude) { 2479 extInclude = new RegExp(drupalSettings.data.extlink.extInclude.replace(/\\/, '\\'), 'i'); 2480 } 2481 2482 // Extra external link matching. 2483 var extExclude = false; 2484 if (drupalSettings.data.extlink.extExclude) { 2485 extExclude = new RegExp(drupalSettings.data.extlink.extExclude.replace(/\\/, '\\'), 'i'); 2486 } 2487 2488 // Extra external link CSS selector exclusion. 2489 var extCssExclude = false; 2490 if (drupalSettings.data.extlink.extCssExclude) { 2491 extCssExclude = drupalSettings.data.extlink.extCssExclude; 2492 } 2493 2494 // Extra external link CSS selector explicit. 2495 var extCssExplicit = false; 2496 if (drupalSettings.data.extlink.extCssExplicit) { 2497 extCssExplicit = drupalSettings.data.extlink.extCssExplicit; 2498 } 2499 2500 // Find all links which are NOT internal and begin with http as opposed 2501 // to ftp://, javascript:, etc. other kinds of links. 2502 // When operating on the 'this' variable, the host has been appended to 2503 // all links by the browser, even local ones. 2504 // In jQuery 1.1 and higher, we'd use a filter method here, but it is not 2505 // available in jQuery 1.0 (Drupal 5 default). 2506 var external_links = []; 2507 var mailto_links = []; 2508 $('a:not([data-extlink]), area:not([data-extlink])', context).each(function (el) { 2509 try { 2510 var url = ''; 2511 if (typeof this.href == 'string') { 2512 url = this.href.toLowerCase(); 2513 } 2514 // Handle SVG links (xlink:href). 2515 else if (typeof this.href == 'object') { 2516 url = this.href.baseVal; 2517 } 2518 if (url.indexOf('http') === 0 2519 && ((!internal_link.test(url) && !(extExclude && extExclude.test(url))) || (extInclude && extInclude.test(url))) 2520 && !(extCssExclude && $(this).is(extCssExclude)) 2521 && !(extCssExclude && $(this).parents(extCssExclude).length > 0) 2522 && !(extCssExplicit && $(this).parents(extCssExplicit).length < 1)) { 2523 var match = false; 2524 if (whitelistedDomains) { 2525 for (var i = 0; i < whitelistedDomains.length; i++) { 2526 if (whitelistedDomains[i].test(url)) { 2527 match = true; 2528 break; 2529 } 2530 } 2531 } 2532 if (!match) { 2533 external_links.push(this); 2534 } 2535 } 2536 // Do not include area tags with begin with mailto: (this prohibits 2537 // icons from being added to image-maps). 2538 else if (this.tagName !== 'AREA' 2539 && url.indexOf('mailto:') === 0 2540 && !(extCssExclude && $(this).parents(extCssExclude).length > 0) 2541 && !(extCssExplicit && $(this).parents(extCssExplicit).length < 1)) { 2542 mailto_links.push(this); 2543 } 2544 } 2545 // IE7 throws errors often when dealing with irregular links, such as: 2546 // <a href="node/10"></a> Empty tags. 2547 // <a href="http://user:[email protected]">example</a> User:pass syntax. 2548 catch (error) { 2549 return false; 2550 } 2551 }); 2552 2553 if (drupalSettings.data.extlink.extClass !== '0' && drupalSettings.data.extlink.extClass !== '') { 2554 Drupal.extlink.applyClassAndSpan(external_links, drupalSettings.data.extlink.extClass, extIconPlacement); 2555 } 2556 2557 if (drupalSettings.data.extlink.mailtoClass !== '0' && drupalSettings.data.extlink.mailtoClass !== '') { 2558 Drupal.extlink.applyClassAndSpan(mailto_links, drupalSettings.data.extlink.mailtoClass, extIconPlacement); 2559 } 2560 2561 if (drupalSettings.data.extlink.extTarget) { 2562 // Apply the target attribute to all links. 2563 $(external_links).filter(function () { 2564 // Filter out links with target set if option specified. 2565 return !(drupalSettings.data.extlink.extTargetNoOverride && $(this).is('a[target]')); 2566 }).attr({target: '_blank'}); 2567 2568 // Add noopener rel attribute to combat phishing. 2569 $(external_links).attr('rel', function (i, val) { 2570 // If no rel attribute is present, create one with the value noopener. 2571 if (val === null || typeof val === 'undefined') { 2572 return 'noopener'; 2573 }
2574 // Check to see if rel contains noopener. Add what doesn't exist. 2575 if (val.indexOf('noopener') > -1) { 2576 if (val.indexOf('noopener') === -1) { 2577 return val + ' noopener'; 2578 } 2579 // Noopener exists. Nothing needs to be added. 2580 else { 2581 return val; 2582 } 2583 } 2584 // Else, append noopener to val. 2585 else { 2586 return val + ' noopener'; 2587 } 2588 }); 2589 } 2590 2591 if (drupalSettings.data.extlink.extNofollow) { 2592 $(external_links).attr('rel', function (i, val) { 2593 // When the link does not have a rel attribute set it to 'nofollow'. 2594 if (val === null || typeof val === 'undefined') { 2595 return 'nofollow'; 2596 } 2597 var target = 'nofollow'; 2598 // Change the target, if not overriding follow. 2599 if (drupalSettings.data.extlink.extFollowNoOverride) { 2600 target = 'follow'; 2601 } 2602 if (val.indexOf(target) === -1) { 2603 return val + ' nofollow'; 2604 } 2605 return val; 2606 }); 2607 } 2608 2609 if (drupalSettings.data.extlink.extNoreferrer) { 2610 $(external_links).attr('rel', function (i, val) { 2611 // When the link does not have a rel attribute set it to 'noreferrer'. 2612 if (val === null || typeof val === 'undefined') { 2613 return 'noreferrer'; 2614 } 2615 if (val.indexOf('noreferrer') === -1) { 2616 return val + ' noreferrer'; 2617 } 2618 return val; 2619 }); 2620 } 2621 2622 Drupal.extlink = Drupal.extlink || {}; 2623 2624 // Set up default click function for the external links popup. This should be 2625 // overridden by modules wanting to alter the popup. 2626 Drupal.extlink.popupClickHandler = Drupal.extlink.popupClickHandler || function () { 2627 if (drupalSettings.data.extlink.extAlert) { 2628 return confirm(drupalSettings.data.extlink.extAlertText); 2629 } 2630 }; 2631 2632 $(external_links).off("click.extlink"); 2633 $(external_links).on("click.extlink", function (e) { 2634 return Drupal.extlink.popupClickHandler(e, this); 2635 }); 2636 }; 2637 2638 /** 2639 * Apply a class and a trailing <span> to all links not containing images. 2640 * 2641 * @param {object[]} links 2642 * An array of DOM elements representing the links. 2643 * @param {string} class_name 2644 * The class to apply to the links. 2645 * @param {string} icon_placement 2646 * 'append' or 'prepend' the icon to the link. 2647 */ 2648 Drupal.extlink.applyClassAndSpan = function (links, class_name, icon_placement) { 2649 var $links_to_process; 2650 if (drupalSettings.data.extlink.extImgClass) { 2651 $links_to_process = $(links); 2652 } 2653 else { 2654 var links_with_images = $(links).find('img, svg').parents('a'); 2655 $links_to_process = $(links).not(links_with_images); 2656 } 2657 2658 if (class_name !== '0') { 2659 $links_to_process.addClass(class_name); 2660 } 2661 2662 // Add data-extlink attribute. 2663 $links_to_process.attr('data-extlink', ''); 2664 2665 var i; 2666 var length = $links_to_process.length; 2667 for (i = 0; i < length; i++) { 2668 var $link = $($links_to_process[i]); 2669 if (drupalSettings.data.extlink.extUseFontAwesome) { 2670 if (class_name === drupalSettings.data.extlink.mailtoClass) { 2671 $link[icon_placement]('<span class="fa-' + class_name + ' extlink"><span class="' + drupalSettings.data.extlink.extFaMailtoClasses + '" aria-label="' + drupalSettings.data.extlink.mailtoLabel + '"></span></span>'); 2672 } 2673 else { 2674 $link[icon_placement]('<span class="fa-' + class_name + ' extlink"><span class="' + drupalSettings.data.extlink.extFaLinkClasses + '" aria-label="' + drupalSettings.data.extlink.extLabel + '"></span></span>'); 2675 } 2676 } 2677 else { 2678 if (class_name === drupalSettings.data.extlink.mailtoClass) { 2679 $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>'); 2680 } 2681 else { 2682 $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>'); 2683 } 2684 } 2685 } 2686 }; 2687 2688 Drupal.behaviors.extlink = Drupal.behaviors.extlink || {}; 2689 Drupal.behaviors.extlink.attach = function (context, drupalSettings) { 2690 // Backwards compatibility, for the benefit of modules overriding extlink 2691 // functionality by defining an "extlinkAttach" global function. 2692 if (typeof extlinkAttach === 'function') { 2693 extlinkAttach(context); 2694 } 2695 else { 2696 Drupal.extlink.attach(context, drupalSettings); 2697 } 2698 }; 2699 2700})(jQuery, Drupal, drupalSettings); 2701; 2702/** 2703 * @file 2704 * Webform behaviors. 2705 */ 2706 2707(function ($, Drupal) { 2708 2709 'use strict'; 2710 2711 // Trigger Drupal's attaching of behaviors after the page is 2712 // completely loaded. 2713 // @see https://stackoverflow.com/questions/37838430/detect-if-page-is-load-from-back-button 2714 // @see https://stackoverflow.com/questions/20899274/how-to-refresh-page-on-back-button-click/20899422#20899422 2715 var isChrome = (/chrom(e|ium)/.test(window.navigator.userAgent.toLowerCase())); 2716 if (isChrome) { 2717 // Track back button in navigation. 2718 // @see https://stackoverflow.com/questions/37838430/detect-if-page-is-load-from-back-button 2719 var backButton = false;
2720 if (window.performance) { 2721 var navEntries = window.performance.getEntriesByType('navigation'); 2722 if (navEntries.length > 0 && navEntries[0].type === 'back_forward') { 2723 backButton = true; 2724 } 2725 else if (window.performance.navigation 2726 && window.performance.navigation.type === window.performance.navigation.TYPE_BACK_FORWARD) { 2727 backButton = true; 2728 } 2729 } 2730 2731 // If the back button is pressed, delay Drupal's attaching of behaviors. 2732 if (backButton) { 2733 var attachBehaviors = Drupal.attachBehaviors; 2734 Drupal.attachBehaviors = function (context, settings) { 2735 setTimeout(function (context, settings) { 2736 attachBehaviors(context, settings); 2737 }, 300); 2738 }; 2739 } 2740 } 2741 2742})(jQuery, Drupal); 2743; 2744/** 2745* DO NOT EDIT THIS FILE. 2746* See the following change record for more information, 2747* https://www.drupal.org/node/2815083 2748* @preserve 2749**/ 2750(function ($, Drupal) { 2751 var states = { 2752 postponed: [] 2753 }; 2754 Drupal.states = states; 2755 function invert(a, invertState) { 2756 return invertState && typeof a !== 'undefined' ? !a : a; 2757 } 2758 function _compare2(a, b) { 2759 if (a === b) { 2760 return typeof a === 'undefined' ? a : true; 2761 } 2762 return typeof a === 'undefined' || typeof b === 'undefined'; 2763 } 2764 function ternary(a, b) { 2765 if (typeof a === 'undefined') { 2766 return b; 2767 } 2768 if (typeof b === 'undefined') { 2769 return a; 2770 } 2771 return a && b; 2772 } 2773 Drupal.behaviors.states = { 2774 attach: function attach(context, settings) { 2775 var $states = $(context).find('[data-drupal-states]'); 2776 var il = $states.length; 2777 var _loop = function _loop(i) { 2778 var config = JSON.parse($states[i].getAttribute('data-drupal-states')); 2779 Object.keys(config || {}).forEach(function (state) { 2780 new states.Dependent({ 2781 element: $($states[i]), 2782 state: states.State.sanitize(state), 2783 constraints: config[state] 2784 }); 2785 }); 2786 }; 2787 for (var i = 0; i < il; i++) { 2788 _loop(i); 2789 } 2790 while (states.postponed.length) { 2791 states.postponed.shift()(); 2792 } 2793 } 2794 }; 2795 states.Dependent = function (args) { 2796 var _this = this; 2797 $.extend(this, { 2798 values: {}, 2799 oldValue: null 2800 }, args); 2801 this.dependees = this.getDependees(); 2802 Object.keys(this.dependees || {}).forEach(function (selector) { 2803 _this.initializeDependee(selector, _this.dependees[selector]); 2804 }); 2805 }; 2806 states.Dependent.comparisons = { 2807 RegExp: function RegExp(reference, value) { 2808 return reference.test(value); 2809 }, 2810 Function: function Function(reference, value) { 2811 return reference(value); 2812 }, 2813 Number: function Number(reference, value) { 2814 return typeof value === 'string' ? _compare2(reference.toString(), value) : _compare2(reference, value); 2815 } 2816 }; 2817 states.Dependent.prototype = { 2818 initializeDependee: function initializeDependee(selector, dependeeStates) { 2819 var _this2 = this; 2820 this.values[selector] = {}; 2821 Object.keys(dependeeStates).forEach(function (i) { 2822 var state = dependeeStates[i]; 2823 if ($.inArray(state, dependeeStates) === -1) { 2824 return; 2825 } 2826 state = states.State.sanitize(state); 2827 _this2.values[selector][state.name] = null; 2828 $(selector).on("state:".concat(state), { 2829 selector: selector, 2830 state: state 2831 }, function (e) { 2832 _this2.update(e.data.selector, e.data.state, e.value); 2833 }); 2834 new states.Trigger({ 2835 selector: selector, 2836 state: state 2837 }); 2838 }); 2839 }, 2840 compare: function compare(reference, selector, state) { 2841 var value = this.values[selector][state.name]; 2842 if (reference.constructor.name in states.Dependent.comparisons) { 2843 return states.Dependent.comparisons[reference.constructor.name](reference, value); 2844 } 2845 return _compare2(reference, value); 2846 }, 2847 update: function update(selector, state, value) { 2848 if (value !== this.values[selector][state.name]) { 2849 this.values[selector][state.name] = value; 2850 this.reevaluate(); 2851 } 2852 }, 2853 reevaluate: function reevaluate() { 2854 var value = this.verifyConstraints(this.constraints); 2855 if (value !== this.oldValue) { 2856 this.oldValue = value; 2857 value = invert(value, this.state.invert); 2858 this.element.trigger({ 2859 type: "state:".concat(this.state), 2860 value: value, 2861 trigger: true 2862 }); 2863 } 2864 }, 2865 verifyConstraints: function verifyConstraints(constraints, selector) { 2866 var result; 2867 if ($.isArray(constraints)) { 2868 var hasXor = $.inArray('xor', constraints) === -1; 2869 var len = constraints.length; 2870 for (var i = 0; i < len; i++) { 2871 if (constraints[i] !== 'xor') { 2872 var constraint = this.checkConstraints(constraints[i], selector, i); 2873 if (constraint && (hasXor || result)) { 2874 return hasXor; 2875 } 2876 result = result || constraint; 2877 } 2878 } 2879 } else if ($.isPlainObject(constraints)) { 2880 for (var n in constraints) { 2881 if (constraints.hasOwnProperty(n)) { 2882 result = ternary(result, this.checkConstraints(constraints[n], selector, n)); 2883 if (result === false) { 2884 return false;
2885 } 2886 } 2887 } 2888 } 2889 return result; 2890 }, 2891 checkConstraints: function checkConstraints(value, selector, state) { 2892 if (typeof state !== 'string' || /[0-9]/.test(state[0])) { 2893 state = null; 2894 } else if (typeof selector === 'undefined') { 2895 selector = state; 2896 state = null; 2897 } 2898 if (state !== null) { 2899 state = states.State.sanitize(state); 2900 return invert(this.compare(value, selector, state), state.invert); 2901 } 2902 return this.verifyConstraints(value, selector); 2903 }, 2904 getDependees: function getDependees() { 2905 var cache = {}; 2906 var _compare = this.compare; 2907 this.compare = function (reference, selector, state) { 2908 (cache[selector] || (cache[selector] = [])).push(state.name); 2909 }; 2910 this.verifyConstraints(this.constraints); 2911 this.compare = _compare; 2912 return cache; 2913 } 2914 }; 2915 states.Trigger = function (args) { 2916 $.extend(this, args); 2917 if (this.state in states.Trigger.states) { 2918 this.element = $(this.selector); 2919 if (!this.element.data("trigger:".concat(this.state))) { 2920 this.initialize(); 2921 } 2922 } 2923 }; 2924 states.Trigger.prototype = { 2925 initialize: function initialize() { 2926 var _this3 = this; 2927 var trigger = states.Trigger.states[this.state]; 2928 if (typeof trigger === 'function') { 2929 trigger.call(window, this.element); 2930 } else { 2931 Object.keys(trigger || {}).forEach(function (event) { 2932 _this3.defaultTrigger(event, trigger[event]); 2933 }); 2934 } 2935 this.element.data("trigger:".concat(this.state), true); 2936 }, 2937 defaultTrigger: function defaultTrigger(event, valueFn) { 2938 var oldValue = valueFn.call(this.element); 2939 this.element.on(event, $.proxy(function (e) { 2940 var value = valueFn.call(this.element, e); 2941 if (oldValue !== value) { 2942 this.element.trigger({ 2943 type: "state:".concat(this.state), 2944 value: value, 2945 oldValue: oldValue 2946 }); 2947 oldValue = value; 2948 } 2949 }, this)); 2950 states.postponed.push($.proxy(function () { 2951 this.element.trigger({ 2952 type: "state:".concat(this.state), 2953 value: oldValue, 2954 oldValue: null 2955 }); 2956 }, this)); 2957 } 2958 }; 2959 states.Trigger.states = { 2960 empty: { 2961 keyup: function keyup() { 2962 return this.val() === ''; 2963 }, 2964 change: function change() { 2965 return this.val() === ''; 2966 } 2967 }, 2968 checked: { 2969 change: function change() { 2970 var checked = false; 2971 this.each(function () { 2972 checked = $(this).prop('checked'); 2973 return !checked; 2974 }); 2975 return checked; 2976 } 2977 }, 2978 value: { 2979 keyup: function keyup() { 2980 if (this.length > 1) { 2981 return this.filter(':checked').val() || false; 2982 } 2983 return this.val(); 2984 }, 2985 change: function change() { 2986 if (this.length > 1) { 2987 return this.filter(':checked').val() || false; 2988 } 2989 return this.val(); 2990 } 2991 }, 2992 collapsed: { 2993 collapsed: function collapsed(e) { 2994 return typeof e !== 'undefined' && 'value' in e ? e.value : !this.is('[open]'); 2995 } 2996 } 2997 }; 2998 states.State = function (state) { 2999 this.pristine = state; 3000 this.name = state; 3001 var process = true; 3002 do { 3003 while (this.name.charAt(0) === '!') { 3004 this.name = this.name.substring(1); 3005 this.invert = !this.invert; 3006 } 3007 if (this.name in states.State.aliases) { 3008 this.name = states.State.aliases[this.name]; 3009 } else { 3010 process = false; 3011 } 3012 } while (process); 3013 }; 3014 states.State.sanitize = function (state) { 3015 if (state instanceof states.State) { 3016 return state; 3017 } 3018 return new states.State(state); 3019 }; 3020 states.State.aliases = { 3021 enabled: '!disabled', 3022 invisible: '!visible', 3023 invalid: '!valid', 3024 untouched: '!touched', 3025 optional: '!required', 3026 filled: '!empty', 3027 unchecked: '!checked', 3028 irrelevant: '!relevant', 3029 expanded: '!collapsed', 3030 open: '!collapsed', 3031 closed: 'collapsed', 3032 readwrite: '!readonly' 3033 }; 3034 states.State.prototype = { 3035 invert: false, 3036 toString: function toString() { 3037 return this.name; 3038 } 3039 }; 3040 var $document = $(document); 3041 $document.on('state:disabled', function (e) { 3042 if (e.trigger) { 3043 $(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); 3044 } 3045 }); 3046 $document.on('state:required', function (e) { 3047 if (e.trigger) { 3048 if (e.value) { 3049 var label = "label".concat(e.target.id ? "[for=".concat(e.target.id, "]") : ''); 3050 var $label = $(e.target).attr({ 3051 required: 'required', 3052 'aria-required': 'true' 3053 }).closest('.js-form-item, .js-form-wrapper').find(label); 3054 if (!$label.hasClass('js-form-required').length) { 3055 $label.addClass('js-form-required form-required'); 3056 } 3057 } else { 3058 $(e.target).removeAttr('required aria-required').closest('.js-form-item, .js-form-wrapper').find('label.js-form-required').removeClass('js-form-required form-required'); 3059 } 3060 } 3061 }); 3062 $document.on('state:visible', function (e) { 3063 if (e.trigger) { 3064 $(e.target).closest('.js-form-item, .js-form-submit, .js-form-wrapper').toggle(e.value); 3065 } 3066 }); 3067 $document.on('state:checked', function (e) { 3068 if (e.trigger) { 3069 $(e.target).closest('.js-form-item, .js-form-wrapper').find('input').prop('checked', e.value).trigger('change'); 3070 } 3071 }); 3072 $document.on('state:collapsed', function (e) { 3073 if (e.trigger) { 3074 if ($(e.target).is('[open]') === e.value) { 3075 $(e.target).find('> summary').trigger('click'); 3076 } 3077 } 3078 }); 3079})(jQuery, Drupal);; 3080/** 3081 * @file 3082 * Extends core/misc/states.js. 3083 */ 3084(function($) { 3085 3086 // Unbind core state.js from document first so we can then override below. 3087 $(document).unbind('state:disabled'); 3088 3089 /** 3090 * Global state change handlers. These are bound to "document" to cover all 3091 * elements whose state changes. Events sent to elements within the page 3092 * bubble up to these handlers. We use this system so that themes and modules 3093 * can override these state change handlers for particular parts of a page. 3094 */ 3095 $(document).bind('state:disabled', function(e) { 3096 // Only act when this change was triggered by a dependency and not by the 3097 // element monitoring itself. 3098 if (e.trigger) { 3099 $(e.target) 3100 .attr('disabled', e.value) 3101 .closest('.form-item, .form-submit, .form-wrapper').toggleClass('form-disabled', e.value) 3102 .find(':input').attr('disabled', e.value); 3103 3104 // Note: WebKit nightlies don't reflect that change correctly. 3105 // See https://bugs.webkit.org/show_bug.cgi?id=23789 3106 } 3107 }); 3108 3109})(jQuery); 3110; 3111/** 3112 * @file 3113 * JavaScript behaviors for custom webform #states. 3114 */ 3115 3116(function ($, Drupal, once) { 3117 3118 'use strict'; 3119 3120 Drupal.webform = Drupal.webform || {}; 3121 Drupal.webform.states = Drupal.webform.states || {}; 3122 Drupal.webform.states.slideDown = Drupal.webform.states.slideDown || {}; 3123 Drupal.webform.states.slideDown.duration = 'slow'; 3124 Drupal.webform.states.slideUp = Drupal.webform.states.slideUp || {}; 3125 Drupal.webform.states.slideUp.duration = 'fast'; 3126 3127 /* ************************************************************************ */ 3128 // jQuery functions. 3129 /* ************************************************************************ */ 3130 3131 /** 3132 * Check if an element has a specified data attribute. 3133 * 3134 * @param {string} data 3135 * The data attribute name. 3136 * 3137 * @return {boolean} 3138 * TRUE if an element has a specified data attribute. 3139 */ 3140 $.fn.hasData = function (data) { 3141 return (typeof this.data(data) !== 'undefined'); 3142 }; 3143 3144 /**
3145 * Check if element is within the webform or not. 3146 * 3147 * @return {boolean} 3148 * TRUE if element is within the webform. 3149 */ 3150 $.fn.isWebform = function () { 3151 return $(this).closest('form.webform-submission-form, form[id^="webform"], form[data-is-webform]').length ? true : false; 3152 }; 3153 3154 /** 3155 * Check if element is to be treated as a webform element. 3156 * 3157 * @return {boolean} 3158 * TRUE if element is to be treated as a webform element. 3159 */ 3160 $.fn.isWebformElement = function () { 3161 return ($(this).isWebform() || $(this).closest('[data-is-webform-element]').length) ? true : false; 3162 }; 3163 3164 /* ************************************************************************ */ 3165 // Trigger. 3166 /* ************************************************************************ */ 3167 3168 // The change event is triggered by cut-n-paste and select menus. 3169 // Issue #2445271: #states element empty check not triggered on mouse 3170 // based paste. 3171 // @see https://www.drupal.org/node/2445271 3172 Drupal.states.Trigger.states.empty.change = function change() { 3173 return this.val() === ''; 3174 }; 3175 3176 /* ************************************************************************ */ 3177 // Dependents. 3178 /* ************************************************************************ */ 3179 3180 // Apply solution included in #1962800 patch. 3181 // Issue #1962800: Form #states not working with literal integers as 3182 // values in IE11. 3183 // @see https://www.drupal.org/project/drupal/issues/1962800 3184 // @see https://www.drupal.org/files/issues/core-states-not-working-with-integers-ie11_1962800_46.patch 3185 // 3186 // This issue causes pattern, less than, and greater than support to break. 3187 // @see https://www.drupal.org/project/webform/issues/2981724 3188 var states = Drupal.states; 3189 Drupal.states.Dependent.prototype.compare = function compare(reference, selector, state) { 3190 var value = this.values[selector][state.name]; 3191 3192 var name = reference.constructor.name; 3193 if (!name) { 3194 name = $.type(reference); 3195 3196 name = name.charAt(0).toUpperCase() + name.slice(1); 3197 } 3198 if (name in states.Dependent.comparisons) { 3199 return states.Dependent.comparisons[name](reference, value); 3200 } 3201 3202 if (reference.constructor.name in states.Dependent.comparisons) { 3203 return states.Dependent.comparisons[reference.constructor.name](reference, value); 3204 } 3205 3206 return _compare2(reference, value); 3207 }; 3208 function _compare2(a, b) { 3209 if (a === b) { 3210 return typeof a === 'undefined' ? a : true; 3211 } 3212 3213 return typeof a === 'undefined' || typeof b === 'undefined'; 3214 } 3215 3216 // Adds pattern, less than, and greater than support to #state API. 3217 // @see http://drupalsun.com/julia-evans/2012/03/09/extending-form-api-states-regular-expressions 3218 Drupal.states.Dependent.comparisons.Object = function (reference, value) { 3219 if ('pattern' in reference) { 3220 return (new RegExp(reference['pattern'])).test(value); 3221 } 3222 else if ('!pattern' in reference) { 3223 return !((new RegExp(reference['!pattern'])).test(value)); 3224 } 3225 else if ('less' in reference) { 3226 return (value !== '' && parseFloat(reference['less']) > parseFloat(value)); 3227 } 3228 else if ('less_equal' in reference) { 3229 return (value !== '' && parseFloat(reference['less_equal']) >= parseFloat(value)); 3230 } 3231 else if ('greater' in reference) { 3232 return (value !== '' && parseFloat(reference['greater']) < parseFloat(value)); 3233 } 3234 else if ('greater_equal' in reference) { 3235 return (value !== '' && parseFloat(reference['greater_equal']) <= parseFloat(value)); 3236 } 3237 else if ('between' in reference || '!between' in reference) { 3238 if (value === '') { 3239 return false; 3240 } 3241 3242 var between = reference['between'] || reference['!between']; 3243 var betweenParts = between.split(':'); 3244 var greater = betweenParts[0]; 3245 var less = (typeof betweenParts[1] !== 'undefined') ? betweenParts[1] : null; 3246 var isGreaterThan = (greater === null || greater === '' || parseFloat(value) >= parseFloat(greater)); 3247 var isLessThan = (less === null || less === '' || parseFloat(value) <= parseFloat(less)); 3248 var result = (isGreaterThan && isLessThan); 3249 return (reference['!between']) ? !result : result; 3250 } 3251 else { 3252 return reference.indexOf(value) !== false; 3253 } 3254 }; 3255 3256 /* ************************************************************************ */ 3257 // States events. 3258 /* ************************************************************************ */ 3259 3260 var $document = $(document); 3261 3262 $document.on('state:required', function (e) { 3263 if (e.trigger && $(e.target).isWebformElement()) { 3264 var $target = $(e.target); 3265 // Fix #required file upload. 3266 // @see Issue #2860529: Conditional required File upload field don't work. 3267 toggleRequired($target.find('input[type="file"]'), e.value); 3268 3269 // Fix #required for radios and likert. 3270 // @see Issue #2856795: If radio buttons are required but not filled form is nevertheless submitted. 3271 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')) { 3272 $target.toggleClass('required', e.value); 3273 toggleRequired($target.find('input[type="radio"]'), e.value); 3274 } 3275 3276 // Fix #required for checkboxes. 3277 // @see Issue #2938414: Checkboxes don't support #states required. 3278 // @see checkboxRequiredhandler 3279 if ($target.is('.js-form-type-checkboxes, .js-form-type-webform-checkboxes-other, .js-webform-type-checkboxes, .js-webform-type-webform-checkboxes-other')) { 3280 $target.toggleClass('required', e.value); 3281 var $checkboxes = $target.find('input[type="checkbox"]'); 3282 if (e.value) { 3283 // Add event handler. 3284 $checkboxes.on('click', statesCheckboxesRequiredEventHandler); 3285 // Initialize and add required attribute. 3286 checkboxesRequired($target); 3287 } 3288 else { 3289 // Remove event handler. 3290 $checkboxes.off('click', statesCheckboxesRequiredEventHandler); 3291 // Remove required attribute. 3292 toggleRequired($checkboxes, false); 3293 } 3294 } 3295 3296 // Fix #required for tableselect. 3297 // @see Issue #3212581: Table select does not trigger client side validation 3298 if ($target.is('.js-webform-tableselect')) { 3299 $target.toggleClass('required', e.value); 3300 var isMultiple = $target.is('[multiple]'); 3301 if (isMultiple) { 3302 // Checkboxes. 3303 var $tbody = $target.find('tbody'); 3304 var $checkboxes = $tbody.find('input[type="checkbox"]'); 3305 copyRequireMessage($target, $checkboxes); 3306 if (e.value) { 3307 $checkboxes.on('click change', statesCheckboxesRequiredEventHandler); 3308 checkboxesRequired($tbody); 3309 } 3310 else { 3311 $checkboxes.off('click change ', statesCheckboxesRequiredEventHandler); 3312 toggleRequired($tbody, false); 3313 } 3314 } 3315 else { 3316 // Radios. 3317 var $radios = $target.find('input[type="radio"]'); 3318 copyRequireMessage($target, $radios); 3319 toggleRequired($radios, e.value); 3320 } 3321 } 3322 3323 // Fix required label for elements without the for attribute. 3324 // @see Issue #3145300: Conditional Visible Select Other not working. 3325 if ($target.is('.js-form-type-webform-select-other, .js-webform-type-webform-select-other')) { 3326 var $select = $target.find('select'); 3327 toggleRequired($select, e.value); 3328 copyRequireMessage($target, $select); 3329 } 3330 if ($target.find('> label:not([for])').length) { 3331 $target.find('> label').toggleClass('js-form-required form-required', e.value); 3332 } 3333 3334 // Fix required label for checkboxes and radios. 3335 // @see Issue #2938414: Checkboxes don't support #states required 3336 // @see Issue #2731991: Setting required on radios marks all options required. 3337 // @see Issue #2856315: Conditional Logic - Requiring Radios in a Fieldset. 3338 // Fix #required for fieldsets. 3339 // @see Issue #2977569: Hidden fieldsets that become visible with conditional logic cannot be made required. 3340 if ($target.is('.js-webform-type-radios, .js-webform-type-checkboxes, fieldset')) { 3341 $target.find('legend span.fieldset-legend:not(.visually-hidden),legend span.fieldset__label:not(.visually-hidden)').toggleClass('js-form-required form-required', e.value); 3342 } 3343 3344 // Issue #2986017: Fieldsets shouldn't have required attribute. 3345 if ($target.is('fieldset')) { 3346 $target.removeAttr('required aria-required'); 3347 } 3348 } 3349 }); 3350 3351 $document.on('state:checked', function (e) { 3352 if (e.trigger) { 3353 $(e.target).trigger('change'); 3354 } 3355 }); 3356 3357 $document.on('state:readonly', function (e) { 3358 if (e.trigger && $(e.target).isWebformElement()) { 3359 $(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); 3360 3361 // Trigger webform:readonly. 3362 $(e.target).trigger('webform:readonly') 3363 .find('select, input, textarea, button').trigger('webform:readonly'); 3364 } 3365 }); 3366 3367 $document.on('state:visible state:visible-slide', function (e) { 3368 if (e.trigger && $(e.target).isWebformElement()) { 3369 if (e.value) { 3370 $(':input', e.target).addBack().each(function () { 3371 restoreValueAndRequired(this); 3372 triggerEventHandlers(this); 3373 }); 3374 } 3375 else { 3376 // @see https://www.sitepoint.com/jquery-function-clear-form-data/ 3377 $(':input', e.target).addBack().each(function () { 3378 backupValueAndRequired(this); 3379 clearValueAndRequired(this); 3380 triggerEventHandlers(this); 3381 }); 3382 } 3383 } 3384 }); 3385 3386 $document.on('state:visible-slide', function (e) { 3387 if (e.trigger && $(e.target).isWebformElement()) { 3388 var effect = e.value ? 'slideDown' : 'slideUp'; 3389 var duration = Drupal.webform.states[effect].duration; 3390 $(e.target).closest('.js-form-item, .js-form-submit, .js-form-wrapper')[effect](duration); 3391 } 3392 }); 3393 Drupal.states.State.aliases['invisible-slide'] = '!visible-slide'; 3394 3395 $document.on('state:disabled', function (e) { 3396 if (e.trigger && $(e.target).isWebformElement()) { 3397 // Make sure disabled property is set before triggering webform:disabled. 3398 // Copied from: core/misc/states.js 3399 $(e.target) 3400 .prop('disabled', e.value) 3401 .closest('.js-form-item, .js-form-submit, .js-form-wrapper').toggleClass('form-disabled', e.value) 3402 .find('select, input, textarea, button').prop('disabled', e.value); 3403 3404 // Never disable hidden file[fids] because the existing values will 3405 // be completely lost when the webform is submitted. 3406 var fileElements = $(e.target) 3407 .find(':input[type="hidden"][name$="[fids]"]'); 3408 if (fileElements.length) { 3409 // Remove 'disabled' attribute from fieldset which will block 3410 // all disabled elements from being submitted. 3411 if ($(e.target).is('fieldset')) { 3412 $(e.target).prop('disabled', false); 3413 } 3414 fileElements.removeAttr('disabled'); 3415 } 3416 3417 // Trigger webform:disabled. 3418 $(e.target).trigger('webform:disabled') 3419 .find('select, input, textarea, button').trigger('webform:disabled'); 3420 } 3421 }); 3422 3423 /* ************************************************************************ */ 3424 // Behaviors. 3425 /* ************************************************************************ */ 3426 3427 /** 3428 * Adds HTML5 validation to required checkboxes. 3429 * 3430 * @type {Drupal~behavior} 3431 * 3432 * @see https://www.drupal.org/project/webform/issues/3068998 3433 */ 3434 Drupal.behaviors.webformCheckboxesRequired = { 3435 attach: function (context) { 3436 $(once('webform-checkboxes-required', '.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)) 3437 .each(function () { 3438 var $element = $(this); 3439 $element.find('input[type="checkbox"]').on('click', statesCheckboxesRequiredEventHandler); 3440 setTimeout(function () {checkboxesRequired($element);}); 3441 }); 3442 } 3443 }; 3444 3445 /** 3446 * Adds HTML5 validation to required radios. 3447 * 3448 * @type {Drupal~behavior} 3449 * 3450 * @see https://www.drupal.org/project/webform/issues/2856795 3451 */ 3452 Drupal.behaviors.webformRadiosRequired = { 3453 attach: function (context) { 3454 $(once('webform-radios-required', '.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)) 3455 .each(function () { 3456 var $element = $(this); 3457 setTimeout(function () {radiosRequired($element);}); 3458 }); 3459 } 3460 }; 3461 3462 /** 3463 * Adds HTML5 validation to required table select. 3464 * 3465 * @type {Drupal~behavior} 3466 * 3467 * @see https://www.drupal.org/project/webform/issues/2856795 3468 */ 3469 Drupal.behaviors.webformTableSelectRequired = { 3470 attach: function (context) { 3471 $(once('webform-tableselect-required','.js-webform-tableselect.required', context)) 3472 .each(function () { 3473 var $element = $(this); 3474 var $tbody = $element.find('tbody'); 3475 var isMultiple = $element.is('[multiple]'); 3476 3477 if (isMultiple) { 3478 // Check all checkbox triggers checkbox 'change' event on 3479 // select and deselect all. 3480 // @see Drupal.tableSelect 3481 $tbody.find('input[type="checkbox"]').on('click change', function () { 3482 checkboxesRequired($tbody); 3483 }); 3484 } 3485 3486 setTimeout(function () { 3487 isMultiple ? checkboxesRequired($tbody) : radiosRequired($element); 3488 }); 3489 }); 3490 } 3491 }; 3492 3493 /** 3494 * Add HTML5 multiple checkboxes required validation. 3495 * 3496 * @param {jQuery} $element
3497 * An jQuery object containing HTML5 radios. 3498 * 3499 * @see https://stackoverflow.com/a/37825072/145846 3500 */ 3501 function checkboxesRequired($element) { 3502 var $firstCheckbox = $element.find('input[type="checkbox"]').first(); 3503 var isChecked = $element.find('input[type="checkbox"]').is(':checked'); 3504 toggleRequired($firstCheckbox, !isChecked); 3505 copyRequireMessage($element, $firstCheckbox); 3506 } 3507 3508 /** 3509 * Add HTML5 radios required validation. 3510 * 3511 * @param {jQuery} $element 3512 * An jQuery object containing HTML5 radios. 3513 * 3514 * @see https://www.drupal.org/project/webform/issues/2856795 3515 */ 3516 function radiosRequired($element) { 3517 var $radios = $element.find('input[type="radio"]'); 3518 var isRequired = $element.hasClass('required'); 3519 toggleRequired($radios, isRequired); 3520 copyRequireMessage($element, $radios); 3521 } 3522 3523 /* ************************************************************************ */ 3524 // Event handlers. 3525 /* ************************************************************************ */ 3526 3527 /** 3528 * Trigger #states API HTML5 multiple checkboxes required validation. 3529 * 3530 * @see https://stackoverflow.com/a/37825072/145846 3531 */ 3532 function statesCheckboxesRequiredEventHandler() { 3533 var $element = $(this).closest('.js-webform-type-checkboxes, .js-webform-type-webform-checkboxes-other'); 3534 checkboxesRequired($element); 3535 } 3536 3537 /** 3538 * Trigger an input's event handlers. 3539 * 3540 * @param {element} input 3541 * An input. 3542 */ 3543 function triggerEventHandlers(input) { 3544 var $input = $(input); 3545 var type = input.type; 3546 var tag = input.tagName.toLowerCase(); 3547 // Add 'webform.states' as extra parameter to event handlers. 3548 // @see Drupal.behaviors.webformUnsaved 3549 var extraParameters = ['webform.states']; 3550 if (type === 'checkbox' || type === 'radio') { 3551 $input 3552 .trigger('change', extraParameters) 3553 .trigger('blur', extraParameters); 3554 } 3555 else if (tag === 'select') { 3556 // Do not trigger the onchange event for Address element's country code 3557 // when it is initialized. 3558 // @see \Drupal\address\Element\Country 3559 if ($input.closest('.webform-type-address').length) { 3560 if (!$input.data('webform-states-address-initialized') 3561 && $input.attr('autocomplete') === 'country' 3562 && $input.val() === $input.find("option[selected]").attr('value')) { 3563 return; 3564 } 3565 $input.data('webform-states-address-initialized', true); 3566 } 3567 3568 $input 3569 .trigger('change', extraParameters) 3570 .trigger('blur', extraParameters); 3571 } 3572 else if (type !== 'submit' && type !== 'button' && type !== 'file') { 3573 // Make sure input mask is removed and then reset when value is restored. 3574 // @see https://www.drupal.org/project/webform/issues/3124155 3575 // @see https://www.drupal.org/project/webform/issues/3202795 3576 var hasInputMask = ($.fn.inputmask && $input.hasClass('js-webform-input-mask')); 3577 hasInputMask && $input.inputmask('remove'); 3578 3579 $input 3580 .trigger('input', extraParameters) 3581 .trigger('change', extraParameters) 3582 .trigger('keydown', extraParameters) 3583 .trigger('keyup', extraParameters) 3584 .trigger('blur', extraParameters); 3585 3586 hasInputMask && $input.inputmask(); 3587 } 3588 } 3589 3590 /* ************************************************************************ */ 3591 // Backup and restore value functions. 3592 /* ************************************************************************ */ 3593 3594 /** 3595 * Backup an input's current value and required attribute 3596 * 3597 * @param {element} input 3598 * An input. 3599 */ 3600 function backupValueAndRequired(input) { 3601 var $input = $(input); 3602 var type = input.type; 3603 var tag = input.tagName.toLowerCase(); // Normalize case. 3604 3605 // Backup required. 3606 if ($input.prop('required') && !$input.hasData('webform-required')) { 3607 $input.data('webform-required', true); 3608 } 3609 3610 // Backup value. 3611 if (!$input.hasData('webform-value')) { 3612 if (type === 'checkbox' || type === 'radio') { 3613 $input.data('webform-value', $input.prop('checked')); 3614 } 3615 else if (tag === 'select') { 3616 var values = []; 3617 $input.find('option:selected').each(function (i, option) { 3618 values[i] = option.value; 3619 }); 3620 $input.data('webform-value', values); 3621 } 3622 else if (type !== 'submit' && type !== 'button') { 3623 $input.data('webform-value', input.value); 3624 } 3625 } 3626 } 3627 3628 /** 3629 * Restore an input's value and required attribute. 3630 * 3631 * @param {element} input 3632 * An input. 3633 */ 3634 function restoreValueAndRequired(input) { 3635 var $input = $(input); 3636 3637 // Restore value. 3638 var value = $input.data('webform-value'); 3639 if (typeof value !== 'undefined') { 3640 var type = input.type; 3641 var tag = input.tagName.toLowerCase(); // Normalize case. 3642 3643 if (type === 'checkbox' || type === 'radio') { 3644 $input.prop('checked', value); 3645 } 3646 else if (tag === 'select') { 3647 $.each(value, function (i, option_value) { 3648 // Prevent "Syntax error, unrecognized expression" error by 3649 // escaping single quotes. 3650 // @see https://forum.jquery.com/topic/escape-characters-prior-to-using-selector 3651 option_value = option_value.replace(/'/g, "\\\'"); 3652 $input.find("option[value='" + option_value + "']").prop('selected', true); 3653 }); 3654 } 3655 else if (type !== 'submit' && type !== 'button') { 3656 input.value = value; 3657 } 3658 $input.removeData('webform-value'); 3659 } 3660 3661 // Restore required. 3662 var required = $input.data('webform-required'); 3663 if (typeof required !== 'undefined') { 3664 if (required) { 3665 $input.prop('required', true); 3666 } 3667 $input.removeData('webform-required'); 3668 } 3669 } 3670 3671 /** 3672 * Clear an input's value and required attributes. 3673 * 3674 * @param {element} input 3675 * An input. 3676 */ 3677 function clearValueAndRequired(input) { 3678 var $input = $(input); 3679 3680 // Check for #states no clear attribute.
3681 // @see https://css-tricks.com/snippets/jquery/make-an-jquery-hasattr/ 3682 if ($input.closest('[data-webform-states-no-clear]').length) { 3683 return; 3684 } 3685 3686 // Clear value. 3687 var type = input.type; 3688 var tag = input.tagName.toLowerCase(); // Normalize case. 3689 if (type === 'checkbox' || type === 'radio') { 3690 $input.prop('checked', false); 3691 } 3692 else if (tag === 'select') { 3693 if ($input.find('option[value=""]').length) { 3694 $input.val(''); 3695 } 3696 else { 3697 input.selectedIndex = -1; 3698 } 3699 } 3700 else if (type !== 'submit' && type !== 'button') { 3701 input.value = (type === 'color') ? '#000000' : ''; 3702 } 3703 3704 // Clear required. 3705 $input.prop('required', false); 3706 } 3707 3708 /* ************************************************************************ */ 3709 // Helper functions. 3710 /* ************************************************************************ */ 3711 3712 /** 3713 * Toggle an input's required attributes. 3714 * 3715 * @param {element} $input 3716 * An input. 3717 * @param {boolean} required 3718 * Is input required. 3719 */ 3720 function toggleRequired($input, required) { 3721 var isCheckboxOrRadio = ($input.attr('type') === 'radio' || $input.attr('type') === 'checkbox'); 3722 if (required) { 3723 if (isCheckboxOrRadio) { 3724 $input.attr({'required': 'required'}); 3725 } 3726 else { 3727 $input.attr({'required': 'required', 'aria-required': 'true'}); 3728 } 3729 } 3730 else { 3731 if (isCheckboxOrRadio) { 3732 $input.removeAttr('required'); 3733 } 3734 else { 3735 $input.removeAttr('required aria-required'); 3736 } 3737 } 3738 } 3739 3740 /** 3741 * Copy the clientside_validation.module's message. 3742 * 3743 * @param {jQuery} $source 3744 * The source element. 3745 * @param {jQuery} $destination 3746 * The destination element. 3747 */ 3748 function copyRequireMessage($source, $destination) { 3749 if ($source.attr('data-msg-required')) { 3750 $destination.attr('data-msg-required', $source.attr('data-msg-required')); 3751 } 3752 } 3753 3754})(jQuery, Drupal, once); 3755; 3756/** 3757 * @file 3758 * JavaScript behaviors for custom webform #states. 3759 */ 3760 3761(function ($, Drupal) { 3762 3763 'use strict'; 3764 3765 $(document).on('state:required', function (e) { 3766 if (e.trigger && $(e.target).isWebform()) { 3767 var $target = $(e.target); 3768 3769 // @see Issue #2856315: Conditional Logic - Requiring Radios in a Fieldset. 3770 // Fix #required for fieldsets. 3771 if ($target.is('.js-form-wrapper.panel')) { 3772 if (e.value) { 3773 $target.find('.panel-heading .panel-title').addClass('js-form-required form-required'); 3774 } 3775 else { 3776 $target.find('.panel-heading .panel-title').removeClass('js-form-required form-required'); 3777 } 3778 } 3779 3780 } 3781 }); 3782 3783})(jQuery, Drupal); 3784;
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.