1/** 2 * @file 3 * Drupal Bootstrap object. 4 */ 5 6/** 7 * All Drupal Bootstrap JavaScript APIs are contained in this namespace. 8 * 9 * @namespace 10 */ 11(function (_, $, Drupal, drupalSettings) { 12 'use strict'; 13 14 var Bootstrap = { 15 processedOnce: {}, 16 settings: drupalSettings.bootstrap || {} 17 }; 18 19 /** 20 * Wraps Drupal.checkPlain() to ensure value passed isn't empty. 21 * 22 * Encodes special characters in a plain-text string for display as HTML. 23 * 24 * @param {string} str 25 * The string to be encoded. 26 * 27 * @return {string} 28 * The encoded string. 29 * 30 * @ingroup sanitization 31 */ 32 Bootstrap.checkPlain = function (str) { 33 return str && Drupal.checkPlain(str) || ''; 34 }; 35 36 /** 37 * Creates a jQuery plugin. 38 * 39 * @param {String} id 40 * A jQuery plugin identifier located in $.fn. 41 * @param {Function} plugin 42 * A constructor function used to initialize the for the jQuery plugin. 43 * @param {Boolean} [noConflict] 44 * Flag indicating whether or not to create a ".noConflict()" helper method 45 * for the plugin. 46 */ 47 Bootstrap.createPlugin = function (id, plugin, noConflict) { 48 // Immediately return if plugin doesn't exist. 49 if ($.fn[id] !== void 0) { 50 return this.fatal('Specified jQuery plugin identifier already exists: @id. Use Drupal.bootstrap.replacePlugin() instead.', {'@id': id}); 51 } 52 53 // Immediately return if plugin isn't a function. 54 if (typeof plugin !== 'function') { 55 return this.fatal('You must provide a constructor function to create a jQuery plugin "@id": @plugin', {'@id': id, '@plugin': plugin}); 56 } 57 58 // Add a ".noConflict()" helper method. 59 this.pluginNoConflict(id, plugin, noConflict); 60 61 $.fn[id] = plugin; 62 }; 63 64 /** 65 * Diff object properties. 66 * 67 * @param {...Object} objects 68 * Two or more objects. The first object will be used to return properties 69 * values. 70 * 71 * @return {Object} 72 * Returns the properties of the first passed object that are not present 73 * in all other passed objects. 74 */ 75 Bootstrap.diffObjects = function (objects) { 76 var args = Array.prototype.slice.call(arguments); 77 return _.pick(args[0], _.difference.apply(_, _.map(args, function (obj) { 78 return Object.keys(obj); 79 }))); 80 }; 81 82 /** 83 * Map of supported events by regular expression. 84 * 85 * @type {Object<Event|MouseEvent|KeyboardEvent|TouchEvent,RegExp>} 86 */ 87 Bootstrap.eventMap = { 88 Event: /^(?:load|unload|abort|error|select|change|submit|reset|focus|blur|resize|scroll)$/, 89 MouseEvent: /^(?:click|dblclick|mouse(?:down|enter|leave|up|over|move|out))$/, 90 KeyboardEvent: /^(?:key(?:down|press|up))$/, 91 TouchEvent: /^(?:touch(?:start|end|move|cancel))$/ 92 }; 93 94 /** 95 * Extends a jQuery Plugin. 96 * 97 * @param {String} id 98 * A jQuery plugin identifier located in $.fn. 99 * @param {Function} callback 100 * A constructor function used to initialize the for the jQuery plugin. 101 * 102 * @return {Function|Boolean} 103 * The jQuery plugin constructor or FALSE if the plugin does not exist. 104 */ 105 Bootstrap.extendPlugin = function (id, callback) { 106 // Immediately return if plugin doesn't exist. 107 if (typeof $.fn[id] !== 'function') { 108 return this.fatal('Specified jQuery plugin identifier does not exist: @id', {'@id': id}); 109 } 110 111 // Immediately return if callback isn't a function. 112 if (typeof callback !== 'function') { 113 return this.fatal('You must provide a callback function to extend the jQuery plugin "@id": @callback', {'@id': id, '@callback': callback}); 114 } 115 116 // Determine existing plugin constructor. 117 var constructor = $.fn[id] && $.fn[id].Constructor || $.fn[id]; 118 var plugin = callback.apply(constructor, [this.settings]); 119 if (!$.isPlainObject(plugin)) { 120 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}); 121 } 122 123 this.wrapPluginConstructor(constructor, plugin, true); 124 125 return $.fn[id]; 126 }; 127 128 Bootstrap.superWrapper = function (parent, fn) { 129 return function () { 130 var previousSuper = this.super; 131 this.super = parent; 132 var ret = fn.apply(this, arguments); 133 if (previousSuper) { 134 this.super = previousSuper; 135 } 136 else { 137 delete this.super; 138 } 139 return ret; 140 }; 141 }; 142 143 /** 144 * Provide a helper method for displaying when something is went wrong. 145 * 146 * @param {String} message 147 * The message to display. 148 * @param {Object} [args] 149 * An arguments to use in message. 150 * 151 * @return {Boolean} 152 * Always returns FALSE. 153 */
154 Bootstrap.fatal = function (message, args) { 155 if (this.settings.dev && console.warn) { 156 for (var name in args) { 157 if (args.hasOwnProperty(name) && typeof args[name] === 'object') { 158 args[name] = JSON.stringify(args[name]); 159 } 160 } 161 Drupal.throwError(new Error(Drupal.formatString(message, args))); 162 } 163 return false; 164 }; 165 166 /** 167 * Intersects object properties. 168 * 169 * @param {...Object} objects 170 * Two or more objects. The first object will be used to return properties 171 * values. 172 * 173 * @return {Object} 174 * Returns the properties of first passed object that intersects with all 175 * other passed objects. 176 */ 177 Bootstrap.intersectObjects = function (objects) { 178 var args = Array.prototype.slice.call(arguments); 179 return _.pick(args[0], _.intersection.apply(_, _.map(args, function (obj) { 180 return Object.keys(obj); 181 }))); 182 }; 183 184 /** 185 * Normalizes an object's values. 186 * 187 * @param {Object} obj 188 * The object to normalize. 189 * 190 * @return {Object} 191 * The normalized object. 192 */ 193 Bootstrap.normalizeObject = function (obj) { 194 if (!$.isPlainObject(obj)) { 195 return obj; 196 } 197 198 for (var k in obj) { 199 if (typeof obj[k] === 'string') { 200 if (obj[k] === 'true') { 201 obj[k] = true; 202 } 203 else if (obj[k] === 'false') { 204 obj[k] = false; 205 } 206 else if (obj[k].match(/^[\d-.]$/)) { 207 obj[k] = parseFloat(obj[k]); 208 } 209 } 210 else if ($.isPlainObject(obj[k])) { 211 obj[k] = Bootstrap.normalizeObject(obj[k]); 212 } 213 } 214 215 return obj; 216 }; 217 218 /** 219 * An object based once plugin (similar to jquery.once, but without the DOM). 220 * 221 * @param {String} id 222 * A unique identifier. 223 * @param {Function} callback 224 * The callback to invoke if the identifier has not yet been seen. 225 * 226 * @return {Bootstrap} 227 */ 228 Bootstrap.once = function (id, callback) { 229 // Immediately return if identifier has already been processed. 230 if (this.processedOnce[id]) { 231 return this; 232 } 233 callback.call(this, this.settings); 234 this.processedOnce[id] = true; 235 return this; 236 }; 237 238 /** 239 * Provide jQuery UI like ability to get/set options for Bootstrap plugins. 240 * 241 * @param {string|object} key 242 * A string value of the option to set, can be dot like to a nested key. 243 * An object of key/value pairs. 244 * @param {*} [value] 245 * (optional) A value to set for key. 246 * 247 * @returns {*} 248 * - Returns nothing if key is an object or both key and value parameters 249 * were provided to set an option. 250 * - Returns the a value for a specific setting if key was provided. 251 * - Returns an object of key/value pairs of all the options if no key or 252 * value parameter was provided. 253 * 254 * @see https://github.com/jquery/jquery-ui/blob/master/ui/widget.js 255 */ 256 Bootstrap.option = function (key, value) { 257 var options = $.isPlainObject(key) ? $.extend({}, key) : {}; 258 259 // Get all options (clone so it doesn't reference the internal object). 260 if (arguments.length === 0) { 261 return $.extend({}, this.options); 262 } 263 264 // Get/set single option. 265 if (typeof key === "string") { 266 // Handle nested keys in dot notation. 267 // e.g., "foo.bar" => { foo: { bar: true } } 268 var parts = key.split('.'); 269 key = parts.shift(); 270 var obj = options; 271 if (parts.length) { 272 for (var i = 0; i < parts.length - 1; i++) { 273 obj[parts[i]] = obj[parts[i]] || {}; 274 obj = obj[parts[i]]; 275 } 276 key = parts.pop(); 277 } 278 279 // Get. 280 if (arguments.length === 1) { 281 return obj[key] === void 0 ? null : obj[key]; 282 } 283 284 // Set. 285 obj[key] = value; 286 } 287 288 // Set multiple options. 289 $.extend(true, this.options, options); 290 }; 291 292 /** 293 * Adds a ".noConflict()" helper method if needed. 294 * 295 * @param {String} id 296 * A jQuery plugin identifier located in $.fn. 297 * @param {Function} plugin 298 * @param {Function} plugin 299 * A constructor function used to initialize the for the jQuery plugin. 300 * @param {Boolean} [noConflict]
301 * Flag indicating whether or not to create a ".noConflict()" helper method 302 * for the plugin. 303 */ 304 Bootstrap.pluginNoConflict = function (id, plugin, noConflict) { 305 if (plugin.noConflict === void 0 && (noConflict === void 0 || noConflict)) { 306 var old = $.fn[id]; 307 plugin.noConflict = function () { 308 $.fn[id] = old; 309 return this; 310 }; 311 } 312 }; 313 314 /** 315 * Replaces a Bootstrap jQuery plugin definition. 316 * 317 * @param {String} id 318 * A jQuery plugin identifier located in $.fn. 319 * @param {Function} callback 320 * A callback function that is immediately invoked and must return a 321 * function that will be used as the plugin constructor. 322 * @param {Boolean} [noConflict] 323 * Flag indicating whether or not to create a ".noConflict()" helper method 324 * for the plugin. 325 */ 326 Bootstrap.replacePlugin = function (id, callback, noConflict) { 327 // Immediately return if plugin doesn't exist. 328 if (typeof $.fn[id] !== 'function') { 329 return this.fatal('Specified jQuery plugin identifier does not exist: @id', {'@id': id}); 330 } 331 332 // Immediately return if callback isn't a function. 333 if (typeof callback !== 'function') { 334 return this.fatal('You must provide a valid callback function to replace a jQuery plugin: @callback', {'@callback': callback}); 335 } 336 337 // Determine existing plugin constructor. 338 var constructor = $.fn[id] && $.fn[id].Constructor || $.fn[id]; 339 var plugin = callback.apply(constructor, [this.settings]); 340 341 // Immediately return if plugin isn't a function. 342 if (typeof plugin !== 'function') { 343 return this.fatal('Returned value from callback is not a usable function to replace a jQuery plugin "@id": @plugin', {'@id': id, '@plugin': plugin}); 344 } 345 346 this.wrapPluginConstructor(constructor, plugin); 347 348 // Add a ".noConflict()" helper method. 349 this.pluginNoConflict(id, plugin, noConflict); 350 351 $.fn[id] = plugin; 352 }; 353 354 /** 355 * Simulates a native event on an element in the browser. 356 * 357 * Note: This is a fairly complete modern implementation. If things aren't 358 * working quite the way you intend (in older browsers), you may wish to use 359 * the jQuery.simulate plugin. If it's available, this method will defer to 360 * that plugin. 361 * 362 * @see https://github.com/jquery/jquery-simulate 363 * 364 * @param {HTMLElement|jQuery} element 365 * A DOM element to dispatch event on. Note: this may be a jQuery object, 366 * however be aware that this will trigger the same event for each element 367 * inside the jQuery collection; use with caution. 368 * @param {String|String[]} type 369 * The type(s) of event to simulate. 370 * @param {Object} [options] 371 * An object of options to pass to the event constructor. Typically, if 372 * an event is being proxied, you should just pass the original event 373 * object here. This allows, if the browser supports it, to be a truly 374 * simulated event. 375 * 376 * @return {Boolean} 377 * The return value is false if event is cancelable and at least one of the 378 * event handlers which handled this event called Event.preventDefault(). 379 * Otherwise it returns true. 380 */ 381 Bootstrap.simulate = function (element, type, options) { 382 // Handle jQuery object wrappers so it triggers on each element. 383 var ret = true; 384 if (element instanceof $) { 385 element.each(function () { 386 if (!Bootstrap.simulate(this, type, options)) { 387 ret = false;
388 } 389 }); 390 return ret; 391 } 392 393 if (!(element instanceof HTMLElement)) { 394 this.fatal('Passed element must be an instance of HTMLElement, got "@type" instead.', { 395 '@type': typeof element, 396 }); 397 } 398 399 // Defer to the jQuery.simulate plugin, if it's available. 400 if (typeof $.simulate === 'function') { 401 new $.simulate(element, type, options); 402 return true; 403 } 404 405 var event; 406 var ctor; 407 var types = [].concat(type); 408 for (var i = 0, l = types.length; i < l; i++) { 409 type = types[i]; 410 for (var name in this.eventMap) { 411 if (this.eventMap[name].test(type)) { 412 ctor = name; 413 break; 414 } 415 } 416 if (!ctor) { 417 throw new SyntaxError('Only rudimentary HTMLEvents, KeyboardEvents and MouseEvents are supported: ' + type); 418 } 419 var opts = {bubbles: true, cancelable: true}; 420 if (ctor === 'KeyboardEvent' || ctor === 'MouseEvent') { 421 $.extend(opts, {ctrlKey: !1, altKey: !1, shiftKey: !1, metaKey: !1}); 422 } 423 if (ctor === 'MouseEvent') { 424 $.extend(opts, {button: 0, pointerX: 0, pointerY: 0, view: window}); 425 } 426 if (options) { 427 $.extend(opts, options); 428 } 429 if (typeof window[ctor] === 'function') { 430 event = new window[ctor](type, opts); 431 if (!element.dispatchEvent(event)) { 432 ret = false;
433 } 434 } 435 else if (document.createEvent) { 436 event = document.createEvent(ctor); 437 event.initEvent(type, opts.bubbles, opts.cancelable); 438 if (!element.dispatchEvent(event)) { 439 ret = false; 440 } 441 } 442 else if (typeof element.fireEvent === 'function') { 443 event = $.extend(document.createEventObject(), opts); 444 if (!element.fireEvent('on' + type, event)) { 445 ret = false; 446 } 447 } 448 else if (typeof element[type]) { 449 element[type](); 450 } 451 } 452 return ret; 453 }; 454 455 /** 456 * Strips HTML and returns just text. 457 * 458 * @param {String|Element|jQuery} html 459 * A string of HTML content, an Element DOM object or a jQuery object. 460 * 461 * @return {String} 462 * The text without HTML tags. 463 * 464 * @todo Replace with http://locutus.io/php/strings/strip_tags/ 465 */ 466 Bootstrap.stripHtml = function (html) { 467 if (html instanceof $) { 468 html = html.html(); 469 } 470 else if (html instanceof Element) { 471 html = html.innerHTML; 472 } 473 var tmp = document.createElement('DIV'); 474 tmp.innerHTML = html; 475 return (tmp.textContent || tmp.innerText || '').replace(/^[\s\n\t]*|[\s\n\t]*$/, ''); 476 }; 477 478 /** 479 * Provide a helper method for displaying when something is unsupported. 480 * 481 * @param {String} type 482 * The type of unsupported object, e.g. method or option. 483 * @param {String} name 484 * The name of the unsupported object. 485 * @param {*} [value] 486 * The value of the unsupported object. 487 */ 488 Bootstrap.unsupported = function (type, name, value) { 489 Bootstrap.warn('Unsupported by Drupal Bootstrap: (@type) @name -> @value', { 490 '@type': type, 491 '@name': name, 492 '@value': typeof value === 'object' ? JSON.stringify(value) : value 493 }); 494 }; 495 496 /** 497 * Provide a helper method to display a warning. 498 * 499 * @param {String} message 500 * The message to display. 501 * @param {Object} [args] 502 * Arguments to use as replacements in Drupal.formatString. 503 */ 504 Bootstrap.warn = function (message, args) { 505 if (this.settings.dev && console.warn) { 506 console.warn(Drupal.formatString(message, args)); 507 } 508 }; 509 510 /** 511 * Wraps a plugin with common functionality. 512 * 513 * @param {Function} constructor 514 * A plugin constructor being wrapped. 515 * @param {Object|Function} plugin 516 * The plugin being wrapped. 517 * @param {Boolean} [extend = false] 518 * Whether to add super extensibility. 519 */ 520 Bootstrap.wrapPluginConstructor = function (constructor, plugin, extend) { 521 var proto = constructor.prototype; 522 523 // Add a jQuery UI like option getter/setter method. 524 var option = this.option; 525 if (proto.option === void(0)) { 526 proto.option = function () { 527 return option.apply(this, arguments); 528 }; 529 } 530 531 if (extend) { 532 // Handle prototype properties separately. 533 if (plugin.prototype !== void 0) { 534 for (var key in plugin.prototype) { 535 if (!plugin.prototype.hasOwnProperty(key)) continue; 536 var value = plugin.prototype[key]; 537 if (typeof value === 'function') { 538 proto[key] = this.superWrapper(proto[key] || function () {}, value); 539 } 540 else { 541 proto[key] = $.isPlainObject(value) ? $.extend(true, {}, proto[key], value) : value; 542 } 543 } 544 } 545 delete plugin.prototype; 546 547 // Handle static properties. 548 for (key in plugin) { 549 if (!plugin.hasOwnProperty(key)) continue; 550 value = plugin[key]; 551 if (typeof value === 'function') { 552 constructor[key] = this.superWrapper(constructor[key] || function () {}, value); 553 } 554 else { 555 constructor[key] = $.isPlainObject(value) ? $.extend(true, {}, constructor[key], value) : value; 556 } 557 } 558 } 559 }; 560 561 /** 562 * Add Bootstrap to the global Drupal object. 563 * 564 * @type {Bootstrap} 565 */ 566 Drupal.bootstrap = Drupal.bootstrap || Bootstrap; 567 568})(window._, window.jQuery, window.Drupal, window.drupalSettings); 569; 570(function ($, _) { 571 572 /** 573 * @class Attributes 574 * 575 * Modifies attributes. 576 * 577 * @param {Object|Attributes} attributes 578 * An object to initialize attributes with. 579 */ 580 var Attributes = function (attributes) { 581 this.data = {}; 582 this.data['class'] = [];
583 this.merge(attributes); 584 }; 585 586 /** 587 * Renders the attributes object as a string to inject into an HTML element. 588 * 589 * @return {String} 590 * A rendered string suitable for inclusion in HTML markup. 591 */ 592 Attributes.prototype.toString = function () { 593 var output = ''; 594 var name, value; 595 var checkPlain = function (str) { 596 return str && str.toString().replace(/&/g, '&').replace(/"/g, '"').replace(/</g, '<').replace(/>/g, '>') || ''; 597 }; 598 var data = this.getData(); 599 for (name in data) { 600 if (!data.hasOwnProperty(name)) continue; 601 value = data[name]; 602 if (_.isFunction(value)) value = value(); 603 if (_.isObject(value)) value = _.values(value); 604 if (_.isArray(value)) value = value.join(' '); 605 output += ' ' + checkPlain(name) + '="' + checkPlain(value) + '"'; 606 } 607 return output; 608 }; 609 610 /** 611 * Renders the Attributes object as a plain object. 612 * 613 * @return {Object} 614 * A plain object suitable for inclusion in DOM elements. 615 */ 616 Attributes.prototype.toPlainObject = function () { 617 var object = {}; 618 var name, value; 619 var data = this.getData(); 620 for (name in data) { 621 if (!data.hasOwnProperty(name)) continue; 622 value = data[name]; 623 if (_.isFunction(value)) value = value(); 624 if (_.isObject(value)) value = _.values(value); 625 if (_.isArray(value)) value = value.join(' '); 626 object[name] = value; 627 } 628 return object; 629 }; 630 631 /** 632 * Add class(es) to the array. 633 * 634 * @param {string|Array} value 635 * An individual class or an array of classes to add. 636 * 637 * @return {Attributes} 638 * 639 * @chainable 640 */ 641 Attributes.prototype.addClass = function (value) { 642 var args = Array.prototype.slice.call(arguments); 643 this.data['class'] = this.sanitizeClasses(this.data['class'].concat(args)); 644 return this; 645 }; 646 647 /** 648 * Returns whether the requested attribute exists. 649 * 650 * @param {string} name 651 * An attribute name to check. 652 * 653 * @return {boolean} 654 * TRUE or FALSE 655 */ 656 Attributes.prototype.exists = function (name) { 657 return this.data[name] !== void(0) && this.data[name] !== null; 658 }; 659 660 /** 661 * Retrieve a specific attribute from the array. 662 * 663 * @param {string} name 664 * The specific attribute to retrieve. 665 * @param {*} defaultValue 666 * (optional) The default value to set if the attribute does not exist. 667 * 668 * @return {*} 669 * A specific attribute value, passed by reference. 670 */ 671 Attributes.prototype.get = function (name, defaultValue) { 672 if (!this.exists(name)) this.data[name] = defaultValue; 673 return this.data[name]; 674 }; 675 676 /** 677 * Retrieves a cloned copy of the internal attributes data object. 678 * 679 * @return {Object} 680 */ 681 Attributes.prototype.getData = function () { 682 return _.extend({}, this.data); 683 }; 684 685 /** 686 * Retrieves classes from the array. 687 * 688 * @return {Array} 689 * The classes array. 690 */ 691 Attributes.prototype.getClasses = function () { 692 return this.get('class', []); 693 }; 694 695 /** 696 * Indicates whether a class is present in the array. 697 * 698 * @param {string|Array} className 699 * The class(es) to search for. 700 * 701 * @return {boolean} 702 * TRUE or FALSE 703 */ 704 Attributes.prototype.hasClass = function (className) { 705 className = this.sanitizeClasses(Array.prototype.slice.call(arguments)); 706 var classes = this.getClasses(); 707 for (var i = 0, l = className.length; i < l; i++) { 708 // If one of the classes fails, immediately return false. 709 if (_.indexOf(classes, className[i]) === -1) { 710 return false; 711 } 712 } 713 return true; 714 }; 715 716 /** 717 * Merges multiple values into the array. 718 * 719 * @param {Attributes|Node|jQuery|Object} object 720 * An Attributes object with existing data, a Node DOM element, a jQuery 721 * instance or a plain object where the key is the attribute name and the 722 * value is the attribute value. 723 * @param {boolean} [recursive] 724 * Flag determining whether or not to recursively merge key/value pairs. 725 * 726 * @return {Attributes} 727 * 728 * @chainable 729 */ 730 Attributes.prototype.merge = function (object, recursive) { 731 // Immediately return if there is nothing to merge. 732 if (!object) { 733 return this; 734 } 735 736 // Get attributes from a jQuery element. 737 if (object instanceof $) { 738 object = object[0]; 739 } 740 741 // Get attributes from a DOM element. 742 if (object instanceof Node) { 743 object = Array.prototype.slice.call(object.attributes).reduce(function (attributes, attribute) { 744 attributes[attribute.name] = attribute.value; 745 return attributes; 746 }, {}); 747 } 748 // Get attributes from an Attributes instance. 749 else if (object instanceof Attributes) { 750 object = object.getData(); 751 } 752 // Otherwise, clone the object. 753 else { 754 object = _.extend({}, object); 755 } 756 757 // By this point, there should be a valid plain object. 758 if (!$.isPlainObject(object)) { 759 setTimeout(function () { 760 throw new Error('Passed object is not supported: ' + object); 761 }); 762 return this; 763 } 764 765 // Handle classes separately. 766 if (object && object['class'] !== void 0) { 767 this.addClass(object['class']); 768 delete object['class']; 769 } 770 771 if (recursive === void 0 || recursive) { 772 this.data = $.extend(true, {}, this.data, object); 773 } 774 else { 775 this.data = $.extend({}, this.data, object); 776 } 777 778 return this; 779 }; 780 781 /** 782 * Removes an attribute from the array. 783 * 784 * @param {string} name 785 * The name of the attribute to remove. 786 * 787 * @return {Attributes} 788 * 789 * @chainable 790 */ 791 Attributes.prototype.remove = function (name) { 792 if (this.exists(name)) delete this.data[name]; 793 return this; 794 }; 795 796 /** 797 * Removes a class from the attributes array. 798 * 799 * @param {...string|Array} className 800 * An individual class or an array of classes to remove. 801 * 802 * @return {Attributes} 803 * 804 * @chainable 805 */ 806 Attributes.prototype.removeClass = function (className) { 807 var remove = this.sanitizeClasses(Array.prototype.slice.apply(arguments)); 808 this.data['class'] = _.without(this.getClasses(), remove); 809 return this; 810 }; 811 812 /** 813 * Replaces a class in the attributes array. 814 * 815 * @param {string} oldValue 816 * The old class to remove. 817 * @param {string} newValue 818 * The new class. It will not be added if the old class does not exist. 819 * 820 * @return {Attributes} 821 * 822 * @chainable 823 */ 824 Attributes.prototype.replaceClass = function (oldValue, newValue) { 825 var classes = this.getClasses(); 826 var i = _.indexOf(this.sanitizeClasses(oldValue), classes); 827 if (i >= 0) { 828 classes[i] = newValue; 829 this.set('class', classes); 830 } 831 return this; 832 }; 833 834 /** 835 * Ensures classes are flattened into a single is an array and sanitized. 836 * 837 * @param {...String|Array} classes 838 * The class or classes to sanitize. 839 * 840 * @return {Array} 841 * A sanitized array of classes. 842 */ 843 Attributes.prototype.sanitizeClasses = function (classes) { 844 return _.chain(Array.prototype.slice.call(arguments)) 845 // Flatten in case there's a mix of strings and arrays. 846 .flatten() 847 848 // Split classes that may have been added with a space as a separator. 849 .map(function (string) { 850 return string.split(' '); 851 }) 852 853 // Flatten again since it was just split into arrays. 854 .flatten() 855 856 // Filter out empty items. 857 .filter() 858
859 // Clean the class to ensure it's a valid class name. 860 .map(function (value) { 861 return Attributes.cleanClass(value); 862 }) 863 864 // Ensure classes are unique. 865 .uniq() 866 867 // Retrieve the final value. 868 .value(); 869 }; 870 871 /** 872 * Sets an attribute on the array. 873 * 874 * @param {string} name 875 * The name of the attribute to set. 876 * @param {*} value 877 * The value of the attribute to set. 878 * 879 * @return {Attributes} 880 * 881 * @chainable 882 */ 883 Attributes.prototype.set = function (name, value) { 884 var obj = $.isPlainObject(name) ? name : {}; 885 if (typeof name === 'string') { 886 obj[name] = value; 887 } 888 return this.merge(obj); 889 }; 890 891 /** 892 * Prepares a string for use as a CSS identifier (element, class, or ID name). 893 * 894 * Note: this is essentially a direct copy from 895 * \Drupal\Component\Utility\Html::cleanCssIdentifier 896 * 897 * @param {string} identifier 898 * The identifier to clean. 899 * @param {Object} [filter] 900 * An object of string replacements to use on the identifier. 901 * 902 * @return {string} 903 * The cleaned identifier. 904 */ 905 Attributes.cleanClass = function (identifier, filter) { 906 filter = filter || { 907 ' ': '-', 908 '_': '-', 909 '/': '-', 910 '[': '-', 911 ']': '' 912 }; 913 914 identifier = identifier.toLowerCase(); 915 916 if (filter['__'] === void 0) { 917 identifier = identifier.replace('__', '#DOUBLE_UNDERSCORE#'); 918 } 919 920 identifier = identifier.replace(Object.keys(filter), Object.keys(filter).map(function(key) { return filter[key]; })); 921 922 if (filter['__'] === void 0) { 923 identifier = identifier.replace('#DOUBLE_UNDERSCORE#', '__'); 924 } 925
926 identifier = identifier.replace(/[^\u002D\u0030-\u0039\u0041-\u005A\u005F\u0061-\u007A\u00A1-\uFFFF]/g, ''); 927 identifier = identifier.replace(['/^[0-9]/', '/^(-[0-9])|^(--)/'], ['_', '__']); 928 929 return identifier; 930 }; 931 932 /** 933 * Creates an Attributes instance. 934 * 935 * @param {object|Attributes} [attributes] 936 * An object to initialize attributes with. 937 * 938 * @return {Attributes} 939 * An Attributes instance. 940 * 941 * @constructor 942 */ 943 Attributes.create = function (attributes) { 944 return new Attributes(attributes); 945 }; 946 947 window.Attributes = Attributes; 948 949})(window.jQuery, window._); 950; 951/** 952 * @file 953 * Theme hooks for the Drupal Bootstrap base theme. 954 */ 955(function ($, Drupal, Bootstrap, Attributes) { 956 957 /** 958 * Fallback for theming an icon if the Icon API module is not installed. 959 */ 960 if (!Drupal.icon) Drupal.icon = { bundles: {} }; 961 if (!Drupal.theme.icon || Drupal.theme.prototype.icon) { 962 $.extend(Drupal.theme, /** @lends Drupal.theme */ { 963 /** 964 * Renders an icon. 965 * 966 * @param {string} bundle 967 * The bundle which the icon belongs to. 968 * @param {string} icon 969 * The name of the icon to render. 970 * @param {object|Attributes} [attributes] 971 * An object of attributes to also apply to the icon. 972 * 973 * @returns {string} 974 */ 975 icon: function (bundle, icon, attributes) { 976 if (!Drupal.icon.bundles[bundle]) return ''; 977 attributes = Attributes.create(attributes).addClass('icon').set('aria-hidden', 'true'); 978 icon = Drupal.icon.bundles[bundle](icon, attributes); 979 return '<span' + attributes + '></span>'; 980 } 981 }); 982 } 983 984 /** 985 * Callback for modifying an icon in the "bootstrap" icon bundle. 986 * 987 * @param {string} icon 988 * The icon being rendered. 989 * @param {Attributes} attributes 990 * Attributes object for the icon. 991 */ 992 Drupal.icon.bundles.bootstrap = function (icon, attributes) { 993 attributes.addClass(['glyphicon', 'glyphicon-' + icon]); 994 }; 995 996 /** 997 * Add necessary theming hooks. 998 */ 999 $.extend(Drupal.theme, /** @lends Drupal.theme */ { 1000 1001 /** 1002 * Renders a Bootstrap AJAX glyphicon throbber. 1003 * 1004 * @returns {string} 1005 */ 1006 ajaxThrobber: function () { 1007 return Drupal.theme('bootstrapIcon', 'refresh', {'class': ['ajax-throbber', 'glyphicon-spin'] }); 1008 }, 1009 1010 /** 1011 * Renders a button element. 1012 * 1013 * @param {object|Attributes} attributes 1014 * An object of attributes to apply to the button. If it contains one of: 1015 * - value: The label of the button. 1016 * - context: The context type of Bootstrap button, can be one of: 1017 * - default 1018 * - primary 1019 * - success 1020 * - info 1021 * - warning 1022 * - danger 1023 * - link 1024 * 1025 * @returns {string} 1026 */ 1027 button: function (attributes) { 1028 attributes = Attributes.create(attributes).addClass('btn'); 1029 var context = attributes.get('context', 'default'); 1030 var label = attributes.get('value', ''); 1031 attributes.remove('context').remove('value'); 1032 if (!attributes.hasClass(['btn-default', 'btn-primary', 'btn-success', 'btn-info', 'btn-warning', 'btn-danger', 'btn-link'])) { 1033 attributes.addClass('btn-' + Bootstrap.checkPlain(context)); 1034 } 1035 1036 // Attempt to, intelligently, provide a default button "type". 1037 if (!attributes.exists('type')) { 1038 attributes.set('type', attributes.hasClass('form-submit') ? 'submit' : 'button'); 1039 } 1040 1041 return '<button' + attributes + '>' + label + '</button>'; 1042 }, 1043 1044 /** 1045 * Alias for "button" theme hook. 1046 * 1047 * @param {object|Attributes} attributes 1048 * An object of attributes to apply to the button. 1049 * 1050 * @see Drupal.theme.button() 1051 * 1052 * @returns {string} 1053 */ 1054 btn: function (attributes) { 1055 return Drupal.theme('button', attributes); 1056 }, 1057 1058 /** 1059 * Renders a button block element. 1060 * 1061 * @param {object|Attributes} attributes 1062 * An object of attributes to apply to the button. 1063 * 1064 * @see Drupal.theme.button() 1065 * 1066 * @returns {string} 1067 */ 1068 'btn-block': function (attributes) { 1069 return Drupal.theme('button', Attributes.create(attributes).addClass('btn-block')); 1070 }, 1071 1072 /**
1073 * Renders a large button element. 1074 * 1075 * @param {object|Attributes} attributes 1076 * An object of attributes to apply to the button. 1077 * 1078 * @see Drupal.theme.button() 1079 * 1080 * @returns {string} 1081 */ 1082 'btn-lg': function (attributes) { 1083 return Drupal.theme('button', Attributes.create(attributes).addClass('btn-lg')); 1084 }, 1085 1086 /** 1087 * Renders a small button element. 1088 * 1089 * @param {object|Attributes} attributes 1090 * An object of attributes to apply to the button. 1091 * 1092 * @see Drupal.theme.button() 1093 * 1094 * @returns {string} 1095 */ 1096 'btn-sm': function (attributes) { 1097 return Drupal.theme('button', Attributes.create(attributes).addClass('btn-sm')); 1098 }, 1099 1100 /** 1101 * Renders an extra small button element. 1102 * 1103 * @param {object|Attributes} attributes 1104 * An object of attributes to apply to the button. 1105 * 1106 * @see Drupal.theme.button() 1107 * 1108 * @returns {string} 1109 */ 1110 'btn-xs': function (attributes) { 1111 return Drupal.theme('button', Attributes.create(attributes).addClass('btn-xs')); 1112 }, 1113 1114 /** 1115 * Renders a glyphicon. 1116 * 1117 * @param {string} name 1118 * The name of the glyphicon. 1119 * @param {object|Attributes} [attributes] 1120 * An object of attributes to apply to the icon. 1121 * 1122 * @returns {string} 1123 */ 1124 bootstrapIcon: function (name, attributes) { 1125 return Drupal.theme('icon', 'bootstrap', name, attributes); 1126 } 1127 1128 }); 1129 1130})(window.jQuery, window.Drupal, window.Drupal.bootstrap, window.Attributes); 1131; 1132/** 1133* DO NOT EDIT THIS FILE. 1134* See the following change record for more information, 1135* https://www.drupal.org/node/2815083 1136* @preserve 1137**/ 1138 1139(function ($, Drupal) { 1140 var states = { 1141 postponed: [] 1142 }; 1143 1144 Drupal.states = states; 1145 1146 function invert(a, invertState) { 1147 return invertState && typeof a !== 'undefined' ? !a : a; 1148 } 1149 1150 function _compare2(a, b) { 1151 if (a === b) { 1152 return typeof a === 'undefined' ? a : true; 1153 } 1154 1155 return typeof a === 'undefined' || typeof b === 'undefined'; 1156 } 1157 1158 function ternary(a, b) { 1159 if (typeof a === 'undefined') { 1160 return b; 1161 } 1162 if (typeof b === 'undefined') { 1163 return a; 1164 } 1165 1166 return a && b; 1167 } 1168 1169 Drupal.behaviors.states = { 1170 attach: function attach(context, settings) { 1171 var $states = $(context).find('[data-drupal-states]'); 1172 var il = $states.length; 1173 1174 var _loop = function _loop(i) { 1175 var config = JSON.parse($states[i].getAttribute('data-drupal-states')); 1176 Object.keys(config || {}).forEach(function (state) { 1177 new states.Dependent({ 1178 element: $($states[i]), 1179 state: states.State.sanitize(state), 1180 constraints: config[state] 1181 }); 1182 }); 1183 }; 1184 1185 for (var i = 0; i < il; i++) { 1186 _loop(i); 1187 } 1188 1189 while (states.postponed.length) { 1190 states.postponed.shift()(); 1191 } 1192 } 1193 }; 1194 1195 states.Dependent = function (args) { 1196 var _this = this; 1197 1198 $.extend(this, { values: {}, oldValue: null }, args); 1199 1200 this.dependees = this.getDependees(); 1201 Object.keys(this.dependees || {}).forEach(function (selector) { 1202 _this.initializeDependee(selector, _this.dependees[selector]); 1203 }); 1204 }; 1205 1206 states.Dependent.comparisons = { 1207 RegExp: function RegExp(reference, value) { 1208 return reference.test(value); 1209 }, 1210 Function: function Function(reference, value) { 1211 return reference(value); 1212 }, 1213 Number: function Number(reference, value) { 1214 return typeof value === 'string' ? _compare2(reference.toString(), value) : _compare2(reference, value); 1215 } 1216 }; 1217 1218 states.Dependent.prototype = { 1219 initializeDependee: function initializeDependee(selector, dependeeStates) { 1220 var _this2 = this; 1221 1222 this.values[selector] = {}; 1223 1224 Object.keys(dependeeStates).forEach(function (i) { 1225 var state = dependeeStates[i]; 1226 1227 if ($.inArray(state, dependeeStates) === -1) { 1228 return; 1229 } 1230 1231 state = states.State.sanitize(state); 1232 1233 _this2.values[selector][state.name] = null; 1234 1235 $(selector).on('state:' + state, { selector: selector, state: state }, function (e) { 1236 _this2.update(e.data.selector, e.data.state, e.value); 1237 }); 1238 1239 new states.Trigger({ selector: selector, state: state }); 1240 }); 1241 }, 1242 compare: function compare(reference, selector, state) { 1243 var value = this.values[selector][state.name]; 1244 if (reference.constructor.name in states.Dependent.comparisons) { 1245 return states.Dependent.comparisons[reference.constructor.name](reference, value); 1246 } 1247 1248 return _compare2(reference, value); 1249 }, 1250 update: function update(selector, state, value) { 1251 if (value !== this.values[selector][state.name]) { 1252 this.values[selector][state.name] = value; 1253 this.reevaluate(); 1254 } 1255 }, 1256 reevaluate: function reevaluate() { 1257 var value = this.verifyConstraints(this.constraints); 1258 1259 if (value !== this.oldValue) { 1260 this.oldValue = value; 1261 1262 value = invert(value, this.state.invert); 1263 1264 this.element.trigger({ 1265 type: 'state:' + this.state, 1266 value: value, 1267 trigger: true 1268 }); 1269 } 1270 }, 1271 verifyConstraints: function verifyConstraints(constraints, selector) { 1272 var result = void 0; 1273 if ($.isArray(constraints)) { 1274 var hasXor = $.inArray('xor', constraints) === -1; 1275 var len = constraints.length; 1276 for (var i = 0; i < len; i++) { 1277 if (constraints[i] !== 'xor') { 1278 var constraint = this.checkConstraints(constraints[i], selector, i); 1279 1280 if (constraint && (hasXor || result)) { 1281 return hasXor; 1282 } 1283 result = result || constraint; 1284 } 1285 } 1286 } else if ($.isPlainObject(constraints)) { 1287 for (var n in constraints) { 1288 if (constraints.hasOwnProperty(n)) { 1289 result = ternary(result, this.checkConstraints(constraints[n], selector, n)); 1290 1291 if (result === false) { 1292 return false;
1293 } 1294 } 1295 } 1296 } 1297 return result; 1298 }, 1299 checkConstraints: function checkConstraints(value, selector, state) { 1300 if (typeof state !== 'string' || /[0-9]/.test(state[0])) { 1301 state = null; 1302 } else if (typeof selector === 'undefined') { 1303 selector = state; 1304 state = null; 1305 } 1306 1307 if (state !== null) { 1308 state = states.State.sanitize(state); 1309 return invert(this.compare(value, selector, state), state.invert); 1310 } 1311 1312 return this.verifyConstraints(value, selector); 1313 }, 1314 getDependees: function getDependees() { 1315 var cache = {}; 1316 1317 var _compare = this.compare; 1318 this.compare = function (reference, selector, state) { 1319 (cache[selector] || (cache[selector] = [])).push(state.name); 1320 }; 1321 1322 this.verifyConstraints(this.constraints); 1323 1324 this.compare = _compare; 1325 1326 return cache; 1327 } 1328 }; 1329 1330 states.Trigger = function (args) { 1331 $.extend(this, args); 1332 1333 if (this.state in states.Trigger.states) { 1334 this.element = $(this.selector); 1335 1336 if (!this.element.data('trigger:' + this.state)) { 1337 this.initialize(); 1338 } 1339 } 1340 }; 1341 1342 states.Trigger.prototype = { 1343 initialize: function initialize() { 1344 var _this3 = this; 1345 1346 var trigger = states.Trigger.states[this.state]; 1347 1348 if (typeof trigger === 'function') { 1349 trigger.call(window, this.element); 1350 } else { 1351 Object.keys(trigger || {}).forEach(function (event) { 1352 _this3.defaultTrigger(event, trigger[event]); 1353 }); 1354 } 1355 1356 this.element.data('trigger:' + this.state, true); 1357 }, 1358 defaultTrigger: function defaultTrigger(event, valueFn) { 1359 var oldValue = valueFn.call(this.element); 1360 1361 this.element.on(event, $.proxy(function (e) { 1362 var value = valueFn.call(this.element, e); 1363 1364 if (oldValue !== value) { 1365 this.element.trigger({ 1366 type: 'state:' + this.state, 1367 value: value, 1368 oldValue: oldValue 1369 }); 1370 oldValue = value; 1371 } 1372 }, this)); 1373 1374 states.postponed.push($.proxy(function () { 1375 this.element.trigger({ 1376 type: 'state:' + this.state, 1377 value: oldValue, 1378 oldValue: null 1379 }); 1380 }, this)); 1381 } 1382 }; 1383 1384 states.Trigger.states = { 1385 empty: { 1386 keyup: function keyup() { 1387 return this.val() === ''; 1388 } 1389 }, 1390 1391 checked: { 1392 change: function change() { 1393 var checked = false; 1394 this.each(function () { 1395 checked = $(this).prop('checked'); 1396 1397 return !checked; 1398 }); 1399 return checked; 1400 } 1401 }, 1402 1403 value: { 1404 keyup: function keyup() { 1405 if (this.length > 1) { 1406 return this.filter(':checked').val() || false; 1407 } 1408 return this.val(); 1409 }, 1410 change: function change() { 1411 if (this.length > 1) { 1412 return this.filter(':checked').val() || false; 1413 } 1414 return this.val(); 1415 } 1416 }, 1417 1418 collapsed: { 1419 collapsed: function collapsed(e) { 1420 return typeof e !== 'undefined' && 'value' in e ? e.value : !this.is('[open]'); 1421 } 1422 } 1423 }; 1424 1425 states.State = function (state) { 1426 this.pristine = state; 1427 this.name = state; 1428 1429 var process = true; 1430 do { 1431 while (this.name.charAt(0) === '!') { 1432 this.name = this.name.substring(1); 1433 this.invert = !this.invert; 1434 } 1435 1436 if (this.name in states.State.aliases) { 1437 this.name = states.State.aliases[this.name]; 1438 } else { 1439 process = false; 1440 } 1441 } while (process); 1442 }; 1443 1444 states.State.sanitize = function (state) { 1445 if (state instanceof states.State) { 1446 return state; 1447 } 1448 1449 return new states.State(state); 1450 }; 1451 1452 states.State.aliases = { 1453 enabled: '!disabled', 1454 invisible: '!visible', 1455 invalid: '!valid', 1456 untouched: '!touched', 1457 optional: '!required', 1458 filled: '!empty', 1459 unchecked: '!checked', 1460 irrelevant: '!relevant', 1461 expanded: '!collapsed', 1462 open: '!collapsed', 1463 closed: 'collapsed', 1464 readwrite: '!readonly' 1465 }; 1466 1467 states.State.prototype = { 1468 invert: false, 1469 1470 toString: function toString() { 1471 return this.name; 1472 } 1473 }; 1474 1475 var $document = $(document); 1476 $document.on('state:disabled', function (e) { 1477 if (e.trigger) { 1478 $(e.target).prop('disabled', e.value).closest('.js-form-item, .js-form-submit, .js-form-wrapper').toggleClass('form-disabled', e.value).find('select, input, textarea').prop('disabled', e.value); 1479 } 1480 }); 1481 1482 $document.on('state:required', function (e) { 1483 if (e.trigger) { 1484 if (e.value) { 1485 var label = 'label' + (e.target.id ? '[for=' + e.target.id + ']' : ''); 1486 var $label = $(e.target).attr({ required: 'required', 'aria-required': 'aria-required' }).closest('.js-form-item, .js-form-wrapper').find(label); 1487 1488 if (!$label.hasClass('js-form-required').length) { 1489 $label.addClass('js-form-required form-required'); 1490 } 1491 } else { 1492 $(e.target).removeAttr('required aria-required').closest('.js-form-item, .js-form-wrapper').find('label.js-form-required').removeClass('js-form-required form-required'); 1493 } 1494 } 1495 }); 1496 1497 $document.on('state:visible', function (e) { 1498 if (e.trigger) { 1499 $(e.target).closest('.js-form-item, .js-form-submit, .js-form-wrapper').toggle(e.value); 1500 } 1501 }); 1502 1503 $document.on('state:checked', function (e) { 1504 if (e.trigger) { 1505 $(e.target).prop('checked', e.value); 1506 } 1507 }); 1508 1509 $document.on('state:collapsed', function (e) { 1510 if (e.trigger) { 1511 if ($(e.target).is('[open]') === e.value) { 1512 $(e.target).find('> summary').trigger('click'); 1513 } 1514 } 1515 }); 1516})(jQuery, Drupal);; 1517/** 1518 * @file 1519 * Extends core/misc/states.js. 1520 */ 1521(function($) { 1522 1523 // Unbind core state.js from document first so we can then override below. 1524 $(document).unbind('state:disabled'); 1525 1526 /** 1527 * Global state change handlers. These are bound to "document" to cover all 1528 * elements whose state changes. Events sent to elements within the page 1529 * bubble up to these handlers. We use this system so that themes and modules 1530 * can override these state change handlers for particular parts of a page. 1531 */ 1532 $(document).bind('state:disabled', function(e) { 1533 // Only act when this change was triggered by a dependency and not by the 1534 // element monitoring itself. 1535 if (e.trigger) { 1536 $(e.target) 1537 .attr('disabled', e.value) 1538 .closest('.form-item, .form-submit, .form-wrapper').toggleClass('form-disabled', e.value) 1539 .find(':input').attr('disabled', e.value); 1540 1541 // Note: WebKit nightlies don't reflect that change correctly. 1542 // See https://bugs.webkit.org/show_bug.cgi?id=23789 1543 } 1544 }); 1545 1546})(jQuery); 1547; 1548/** 1549 * @file 1550 * JavaScript behaviors for custom webform #states. 1551 */ 1552 1553(function ($, Drupal) { 1554 1555 'use strict'; 1556 1557 Drupal.webform = Drupal.webform || {}; 1558 Drupal.webform.states = Drupal.webform.states || {}; 1559 Drupal.webform.states.slideDown = Drupal.webform.states.slideDown || {}; 1560 Drupal.webform.states.slideDown.duration = 'slow'; 1561 Drupal.webform.states.slideUp = Drupal.webform.states.slideUp || {}; 1562 Drupal.webform.states.slideUp.duration = 'fast'; 1563 1564 /** 1565 * Check if an element has a specified data attribute. 1566 * 1567 * @param {string} data 1568 * The data attribute name. 1569 * 1570 * @return {boolean} 1571 * TRUE if an element has a specified data attribute. 1572 */ 1573 $.fn.hasData = function (data) { 1574 return (typeof this.data(data) !== 'undefined'); 1575 }; 1576 1577 /**
1578 * Check if element is within the webform or not. 1579 * 1580 * @return {boolean} 1581 * TRUE if element is within the webform. 1582 */ 1583 $.fn.isWebform = function () { 1584 return $(this).closest('form[id^="webform"]').length ? true : false; 1585 }; 1586 1587 // The change event is triggered by cut-n-paste and select menus. 1588 // Issue #2445271: #states element empty check not triggered on mouse 1589 // based paste. 1590 // @see https://www.drupal.org/node/2445271 1591 Drupal.states.Trigger.states.empty.change = function change() { 1592 return this.val() === ''; 1593 }; 1594 1595 // Apply solution included in #1962800 patch. 1596 // Issue #1962800: Form #states not working with literal integers as 1597 // values in IE11. 1598 // @see https://www.drupal.org/project/drupal/issues/1962800 1599 // @see https://www.drupal.org/files/issues/core-states-not-working-with-integers-ie11_1962800_46.patch 1600 // 1601 // This issue causes pattern, less than, and greater than support to break. 1602 // @see https://www.drupal.org/project/webform/issues/2981724 1603 var states = Drupal.states; 1604 Drupal.states.Dependent.prototype.compare = function compare(reference, selector, state) { 1605 var value = this.values[selector][state.name]; 1606 1607 var name = reference.constructor.name; 1608 if (!name) { 1609 name = $.type(reference); 1610 1611 name = name.charAt(0).toUpperCase() + name.slice(1); 1612 } 1613 if (name in states.Dependent.comparisons) { 1614 return states.Dependent.comparisons[name](reference, value); 1615 } 1616 1617 if (reference.constructor.name in states.Dependent.comparisons) { 1618 return states.Dependent.comparisons[reference.constructor.name](reference, value); 1619 } 1620 1621 return _compare2(reference, value); 1622 }; 1623 function _compare2(a, b) { 1624 if (a === b) { 1625 return typeof a === 'undefined' ? a : true; 1626 } 1627 1628 return typeof a === 'undefined' || typeof b === 'undefined'; 1629 } 1630 1631 // Adds pattern, less than, and greater than support to #state API. 1632 // @see http://drupalsun.com/julia-evans/2012/03/09/extending-form-api-states-regular-expressions 1633 Drupal.states.Dependent.comparisons.Object = function (reference, value) { 1634 if ('pattern' in reference) { 1635 return (new RegExp(reference['pattern'])).test(value); 1636 } 1637 else if ('!pattern' in reference) { 1638 return !((new RegExp(reference['!pattern'])).test(value)); 1639 } 1640 else if ('less' in reference) { 1641 return (value !== '' && parseFloat(reference['less']) > parseFloat(value)); 1642 } 1643 else if ('greater' in reference) { 1644 return (value !== '' && parseFloat(reference['greater']) < parseFloat(value)); 1645 } 1646 else { 1647 return reference.indexOf(value) !== false; 1648 } 1649 }; 1650 1651 var $document = $(document); 1652 1653 $document.on('state:required', function (e) { 1654 if (e.trigger && $(e.target).isWebform()) { 1655 var $target = $(e.target); 1656 // Fix #required file upload. 1657 // @see Issue #2860529: Conditional required File upload field don't work. 1658 if (e.value) { 1659 $target.find('input[type="file"]').attr({'required': 'required', 'aria-required': 'true'}); 1660 } 1661 else { 1662 $target.find('input[type="file"]').removeAttr('required aria-required'); 1663 } 1664 1665 // Fix required label for checkboxes and radios. 1666 // @see Issue #2938414: Checkboxes don't support #states required 1667 // @see Issue #2731991: Setting required on radios marks all options required. 1668 // @see Issue #2856315: Conditional Logic - Requiring Radios in a Fieldset. 1669 // Fix #required for fieldsets. 1670 // @see Issue #2977569: Hidden fieldsets that become visible with conditional logic cannot be made required. 1671 if ($target.is('.js-webform-type-radios, .js-webform-type-checkboxes, fieldset')) { 1672 if (e.value) { 1673 $target.find('legend span.fieldset-legend:not(.visually-hidden)').addClass('js-form-required form-required'); 1674 } 1675 else { 1676 $target.find('legend span.fieldset-legend:not(.visually-hidden)').removeClass('js-form-required form-required'); 1677 } 1678 } 1679 1680 // Fix #required for radios. 1681 // @see Issue #2856795: If radio buttons are required but not filled form is nevertheless submitted. 1682 if ($target.is('.js-webform-type-radios, .js-form-type-webform-radios-other')) { 1683 if (e.value) { 1684 $target.find('input[type="radio"]').attr({'required': 'required', 'aria-required': 'true'}); 1685 } 1686 else { 1687 $target.find('input[type="radio"]').removeAttr('required aria-required'); 1688 } 1689 } 1690 1691 // Fix #required for checkboxes. 1692 // @see Issue #2938414: Checkboxes don't support #states required. 1693 // @see checkboxRequiredhandler 1694 if ($target.is('.js-webform-type-checkboxes, .js-form-type-webform-checkboxes-other')) { 1695 var $checkboxes = $target.find('input[type="checkbox"]'); 1696 if (e.value) { 1697 // Bind the event handler and add custom HTML5 required validation 1698 // to all checkboxes. 1699 $checkboxes.bind('click', checkboxRequiredhandler); 1700 if (!$checkboxes.is(':checked')) { 1701 $checkboxes.attr({'required': 'required', 'aria-required': 'true'}); 1702 } 1703 } 1704 else { 1705 // Remove custom HTML5 required validation from all checkboxes 1706 // and unbind the event handler. 1707 $checkboxes 1708 .removeAttr('required aria-required') 1709 .unbind('click', checkboxRequiredhandler); 1710 } 1711 } 1712 1713 // Issue #2986017: Fieldsets shouldn't have required attribute. 1714 if ($target.is('fieldset')) { 1715 $target.removeAttr('required aria-required'); 1716 } 1717 } 1718 1719 }); 1720 1721 $document.on('state:readonly', function (e) { 1722 if (e.trigger && $(e.target).isWebform()) { 1723 $(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); 1724 } 1725 }); 1726 1727 $document.on('state:visible state:visible-slide', function (e) { 1728 if (e.trigger && $(e.target).isWebform()) { 1729 if (e.value) { 1730 $(':input', e.target).addBack().each(function () { 1731 restoreValueAndRequired(this); 1732 triggerEventHandlers(this); 1733 }); 1734 } 1735 else { 1736 // @see https://www.sitepoint.com/jquery-function-clear-form-data/ 1737 $(':input', e.target).addBack().each(function () { 1738 backupValueAndRequired(this); 1739 clearValueAndRequired(this); 1740 triggerEventHandlers(this); 1741 }); 1742 } 1743 } 1744 }); 1745 1746 $document.bind('state:visible-slide', function (e) { 1747 if (e.trigger && $(e.target).isWebform()) { 1748 var effect = e.value ? 'slideDown' : 'slideUp'; 1749 var duration = Drupal.webform.states[effect].duration; 1750 $(e.target).closest('.js-form-item, .js-form-submit, .js-form-wrapper')[effect](duration); 1751 } 1752 }); 1753 Drupal.states.State.aliases['invisible-slide'] = '!visible-slide'; 1754 1755 $document.on('state:disabled', function (e) { 1756 if (e.trigger && $(e.target).isWebform()) { 1757 // Make sure disabled property is set before triggering webform:disabled. 1758 // Copied from: core/misc/states.js 1759 $(e.target) 1760 .prop('disabled', e.value) 1761 .closest('.js-form-item, .js-form-submit, .js-form-wrapper').toggleClass('form-disabled', e.value) 1762 .find('select, input, textarea, button').prop('disabled', e.value); 1763 1764 // Trigger webform:disabled. 1765 $(e.target).trigger('webform:disabled') 1766 .find('select, input, textarea, button').trigger('webform:disabled'); 1767 } 1768 }); 1769 1770 /** 1771 * Trigger custom HTML5 multiple checkboxes validation. 1772 * 1773 * @see https://stackoverflow.com/a/37825072/145846 1774 */ 1775 function checkboxRequiredhandler() { 1776 var $checkboxes = $(this).closest('.js-webform-type-checkboxes, .js-form-type-webform-checkboxes-other').find('input[type="checkbox"]'); 1777 if ($checkboxes.is(':checked')) { 1778 $checkboxes.removeAttr('required aria-required'); 1779 } 1780 else { 1781 $checkboxes.attr({'required': 'required', 'aria-required': 'true'}); 1782 } 1783 } 1784 1785 /** 1786 * Trigger an input's event handlers. 1787 * 1788 * @param {element} input 1789 * An input. 1790 */ 1791 function triggerEventHandlers(input) { 1792 var $input = $(input); 1793 var type = input.type; 1794 var tag = input.tagName.toLowerCase(); 1795 // Add 'webform.states' as extra parameter to event handlers. 1796 // @see Drupal.behaviors.webformUnsaved 1797 var extraParameters = ['webform.states']; 1798 if (type === 'checkbox' || type === 'radio') { 1799 $input 1800 .trigger('change', extraParameters) 1801 .trigger('blur', extraParameters); 1802 } 1803 else if (tag === 'select') { 1804 $input 1805 .trigger('change', extraParameters) 1806 .trigger('blur', extraParameters); 1807 } 1808 else if (type !== 'submit' && type !== 'button' && type !== 'file') { 1809 $input 1810 .trigger('input', extraParameters) 1811 .trigger('change', extraParameters) 1812 .trigger('keydown', extraParameters) 1813 .trigger('keyup', extraParameters) 1814 .trigger('blur', extraParameters); 1815 } 1816 } 1817 1818 /** 1819 * Backup an input's current value and required attribute 1820 * 1821 * @param {element} input 1822 * An input. 1823 */ 1824 function backupValueAndRequired(input) { 1825 var $input = $(input); 1826 var type = input.type; 1827 var tag = input.tagName.toLowerCase(); // Normalize case. 1828 1829 // Backup required. 1830 if ($input.prop('required') && !$input.hasData('webform-required')) { 1831 $input.data('webform-required', true); 1832 } 1833 1834 // Backup value. 1835 if (!$input.hasData('webform-value')) { 1836 if (type === 'checkbox' || type === 'radio') { 1837 $input.data('webform-value', $input.prop('checked')); 1838 } 1839 else if (tag === 'select') { 1840 var values = []; 1841 $input.find('option:selected').each(function (i, option) { 1842 values[i] = option.value; 1843 }); 1844 $input.data('webform-value', values); 1845 } 1846 else if (type !== 'submit' && type !== 'button') { 1847 $input.data('webform-value', input.value); 1848 } 1849 } 1850 } 1851 1852 /** 1853 * Restore an input's value and required attribute. 1854 * 1855 * @param {element} input 1856 * An input. 1857 */ 1858 function restoreValueAndRequired(input) { 1859 var $input = $(input); 1860 1861 // Restore value. 1862 var value = $input.data('webform-value'); 1863 if (typeof value !== 'undefined') { 1864 var type = input.type; 1865 var tag = input.tagName.toLowerCase(); // Normalize case. 1866 1867 if (type === 'checkbox' || type === 'radio') { 1868 $input.prop('checked', value); 1869 } 1870 else if (tag === 'select') { 1871 $.each(value, function (i, option_value) { 1872 $input.find("option[value='" + option_value + "']").prop('selected', true); 1873 }); 1874 } 1875 else if (type !== 'submit' && type !== 'button') { 1876 input.value = value; 1877 } 1878 $input.removeData('webform-value'); 1879 } 1880 1881 // Restore required. 1882 var required = $input.data('webform-required'); 1883 if (typeof required !== 'undefined') { 1884 if (required) { 1885 $input.prop('required', true); 1886 } 1887 $input.removeData('webform-required'); 1888 } 1889 } 1890 1891 /** 1892 * Clear an input's value and required attributes. 1893 * 1894 * @param {element} input 1895 * An input. 1896 */ 1897 function clearValueAndRequired(input) { 1898 var $input = $(input); 1899 1900 // Check for #states no clear attribute.
1901 // @see https://css-tricks.com/snippets/jquery/make-an-jquery-hasattr/ 1902 if ($input.closest('[data-webform-states-no-clear]').length) { 1903 return; 1904 } 1905 1906 // Clear value. 1907 var type = input.type; 1908 var tag = input.tagName.toLowerCase(); // Normalize case. 1909 if (type === 'checkbox' || type === 'radio') { 1910 $input.prop('checked', false); 1911 } 1912 else if (tag === 'select') { 1913 if ($input.find('option[value=""]').length) { 1914 $input.val(''); 1915 } 1916 else { 1917 input.selectedIndex = -1; 1918 } 1919 } 1920 else if (type !== 'submit' && type !== 'button') { 1921 input.value = (type === 'color') ? '#000000' : ''; 1922 } 1923 1924 // Clear required. 1925 $input.prop('required', false); 1926 } 1927 1928})(jQuery, Drupal); 1929; 1930/** 1931 * @file 1932 * JavaScript behaviors for custom webform #states. 1933 */ 1934 1935(function ($, Drupal) { 1936 1937 'use strict'; 1938 1939 $(document).on('state:required', function (e) { 1940 if (e.trigger && $(e.target).isWebform()) { 1941 var $target = $(e.target); 1942 1943 // @see Issue #2856315: Conditional Logic - Requiring Radios in a Fieldset. 1944 // Fix #required for fieldsets. 1945 if ($target.is('.js-form-wrapper.panel')) { 1946 if (e.value) { 1947 $target.find('.panel-heading .panel-title').addClass('js-form-required form-required'); 1948 } 1949 else { 1950 $target.find('.panel-heading .panel-title').removeClass('js-form-required form-required'); 1951 } 1952 } 1953 1954 } 1955 }); 1956 1957})(jQuery, Drupal); 1958; 1959/** 1960 * @file 1961 * Bootstrap Popovers. 1962 */ 1963 1964var Drupal = Drupal || {}; 1965 1966(function ($, Drupal, Bootstrap) { 1967 "use strict"; 1968 1969 var $document = $(document); 1970 1971 /** 1972 * Extend the Bootstrap Popover plugin constructor class. 1973 */ 1974 Bootstrap.extendPlugin('popover', function (settings) { 1975 return { 1976 DEFAULTS: { 1977 animation: !!settings.popover_animation, 1978 autoClose: !!settings.popover_auto_close, 1979 enabled: settings.popover_enabled, 1980 html: !!settings.popover_html, 1981 placement: settings.popover_placement, 1982 selector: settings.popover_selector, 1983 trigger: settings.popover_trigger, 1984 title: settings.popover_title, 1985 content: settings.popover_content, 1986 delay: parseInt(settings.popover_delay, 10), 1987 container: settings.popover_container 1988 } 1989 }; 1990 }); 1991 1992 /** 1993 * Bootstrap Popovers. 1994 * 1995 * @todo This should really be properly delegated if selector option is set. 1996 */ 1997 Drupal.behaviors.bootstrapPopovers = { 1998 $activePopover: null, 1999 attach: function (context) { 2000 // Immediately return if popovers are not available. 2001 if (!$.fn.popover || !$.fn.popover.Constructor.DEFAULTS.enabled) { 2002 return; 2003 } 2004 2005 var _this = this; 2006 2007 $document 2008 .on('show.bs.popover', '[data-toggle=popover]', function () { 2009 var $trigger = $(this); 2010 var popover = $trigger.data('bs.popover'); 2011 2012 // Only keep track of clicked triggers that we're manually handling. 2013 if (popover.options.originalTrigger === 'click') { 2014 if (_this.$activePopover && _this.getOption('autoClose') && !_this.$activePopover.is($trigger)) { 2015 _this.$activePopover.popover('hide'); 2016 } 2017 _this.$activePopover = $trigger; 2018 } 2019 }) 2020 // Unfortunately, :focusable is only made available when using jQuery 2021 // UI. While this would be the most semantic pseudo selector to use 2022 // here, jQuery UI may not always be loaded. Instead, just use :visible 2023 // here as this just needs some sort of selector here. This activates 2024 // delegate binding to elements in jQuery so it can work it's bubbling 2025 // focus magic since elements don't really propagate their focus events. 2026 // @see https://www.drupal.org/project/bootstrap/issues/3013236 2027 .on('focus.bs.popover', ':visible', function (e) { 2028 var $target = $(e.target); 2029 if (_this.$activePopover && _this.getOption('autoClose') && !_this.$activePopover.is($target) && !$target.closest('.popover.in')[0]) { 2030 _this.$activePopover.popover('hide'); 2031 _this.$activePopover = null; 2032 } 2033 }) 2034 .on('click.bs.popover', function (e) { 2035 var $target = $(e.target); 2036 if (_this.$activePopover && _this.getOption('autoClose') && !$target.is('[data-toggle=popover]') && !$target.closest('.popover.in')[0]) { 2037 _this.$activePopover.popover('hide'); 2038 _this.$activePopover = null; 2039 } 2040 }) 2041 .on('keyup.bs.popover', function (e) { 2042 if (_this.$activePopover && _this.getOption('autoClose') && e.which === 27) { 2043 _this.$activePopover.popover('hide'); 2044 _this.$activePopover = null; 2045 } 2046 }) 2047 ; 2048 2049 var elements = $(context).find('[data-toggle=popover]').toArray(); 2050 for (var i = 0; i < elements.length; i++) { 2051 var $element = $(elements[i]); 2052 var options = $.extend({}, $.fn.popover.Constructor.DEFAULTS, $element.data()); 2053 2054 // Store the original trigger. 2055 options.originalTrigger = options.trigger; 2056 2057 // If the trigger is "click", then we'll handle it manually here. 2058 if (options.trigger === 'click') { 2059 options.trigger = 'manual'; 2060 } 2061 2062 // Retrieve content from a target element. 2063 var target = options.target || $element.is('a[href^="#"]') && $element.attr('href'); 2064 var $target = $document.find(target).clone(); 2065 if (!options.content && $target[0]) { 2066 $target.removeClass('visually-hidden hidden').removeAttr('aria-hidden'); 2067 options.content = $target.wrap('<div/>').parent()[options.html ? 'html' : 'text']() || ''; 2068 } 2069 2070 // Initialize the popover. 2071 $element.popover(options); 2072 2073 // Handle clicks manually. 2074 if (options.originalTrigger === 'click') { 2075 // To ensure the element is bound multiple times, remove any 2076 // previously set event handler before adding another one. 2077 $element 2078 .off('click.drupal.bootstrap.popover') 2079 .on('click.drupal.bootstrap.popover', function (e) { 2080 $(this).popover('toggle'); 2081 e.preventDefault(); 2082 e.stopPropagation(); 2083 }) 2084 ; 2085 } 2086 } 2087 }, 2088 detach: function (context) { 2089 // Immediately return if popovers are not available. 2090 if (!$.fn.popover || !$.fn.popover.Constructor.DEFAULTS.enabled) { 2091 return; 2092 } 2093 2094 // Destroy all popovers. 2095 $(context).find('[data-toggle="popover"]') 2096 .off('click.drupal.bootstrap.popover') 2097 .popover('destroy') 2098 ; 2099 }, 2100 getOption: function(name, defaultValue, element) { 2101 var $element = element ? $(element) : this.$activePopover; 2102 var options = $.extend(true, {}, $.fn.popover.Constructor.DEFAULTS, ($element && $element.data('bs.popover') || {}).options); 2103 if (options[name] !== void 0) { 2104 return options[name]; 2105 } 2106 return defaultValue !== void 0 ? defaultValue : void 0; 2107 } 2108 }; 2109 2110})(window.jQuery, window.Drupal, window.Drupal.bootstrap); 2111;
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.