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