1/** 2* DO NOT EDIT THIS FILE. 3* See the following change record for more information, 4* https://www.drupal.org/node/2815083 5* @preserve 6**/ 7 8Drupal.debounce = function (func, wait, immediate) { 9 var timeout = void 0; 10 var result = void 0; 11 return function () { 12 for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { 13 args[_key] = arguments[_key]; 14 } 15 16 var context = this; 17 var later = function later() { 18 timeout = null; 19 if (!immediate) { 20 result = func.apply(context, args); 21 } 22 }; 23 var callNow = immediate && !timeout; 24 clearTimeout(timeout); 25 timeout = setTimeout(later, wait); 26 if (callNow) { 27 result = func.apply(context, args); 28 } 29 return result; 30 }; 31};; 32/** 33* DO NOT EDIT THIS FILE. 34* See the following change record for more information, 35* https://www.drupal.org/node/2815083 36* @preserve 37**/ 38 39(function ($, Drupal, debounce) { 40 $.fn.drupalGetSummary = function () { 41 var callback = this.data('summaryCallback'); 42 return this[0] && callback ? $.trim(callback(this[0])) : ''; 43 }; 44 45 $.fn.drupalSetSummary = function (callback) { 46 var self = this; 47 48 if (typeof callback !== 'function') { 49 var val = callback; 50 callback = function callback() { 51 return val; 52 }; 53 } 54 55 return this.data('summaryCallback', callback).off('formUpdated.summary').on('formUpdated.summary', function () { 56 self.trigger('summaryUpdated'); 57 }).trigger('summaryUpdated'); 58 }; 59 60 Drupal.behaviors.formSingleSubmit = { 61 attach: function attach() { 62 function onFormSubmit(e) { 63 var $form = $(e.currentTarget); 64 var formValues = $form.serialize(); 65 var previousValues = $form.attr('data-drupal-form-submit-last'); 66 if (previousValues === formValues) { 67 e.preventDefault(); 68 } else { 69 $form.attr('data-drupal-form-submit-last', formValues); 70 } 71 } 72 73 $('body').once('form-single-submit').on('submit.singleSubmit', 'form:not([method~="GET"])', onFormSubmit); 74 } 75 }; 76 77 function triggerFormUpdated(element) { 78 $(element).trigger('formUpdated'); 79 } 80 81 function fieldsList(form) { 82 var $fieldList = $(form).find('[name]').map(function (index, element) { 83 return element.getAttribute('id'); 84 }); 85 86 return $.makeArray($fieldList); 87 } 88 89 Drupal.behaviors.formUpdated = { 90 attach: function attach(context) { 91 var $context = $(context); 92 var contextIsForm = $context.is('form'); 93 var $forms = (contextIsForm ? $context : $context.find('form')).once('form-updated'); 94 var formFields = void 0; 95 96 if ($forms.length) { 97 $.makeArray($forms).forEach(function (form) { 98 var events = 'change.formUpdated input.formUpdated '; 99 var eventHandler = debounce(function (event) { 100 triggerFormUpdated(event.target); 101 }, 300); 102 formFields = fieldsList(form).join(','); 103 104 form.setAttribute('data-drupal-form-fields', formFields); 105 $(form).on(events, eventHandler); 106 }); 107 } 108 109 if (contextIsForm) { 110 formFields = fieldsList(context).join(','); 111 112 var currentFields = $(context).attr('data-drupal-form-fields'); 113 114 if (formFields !== currentFields) { 115 triggerFormUpdated(context); 116 } 117 } 118 }, 119 detach: function detach(context, settings, trigger) { 120 var $context = $(context); 121 var contextIsForm = $context.is('form'); 122 if (trigger === 'unload') { 123 var $forms = (contextIsForm ? $context : $context.find('form')).removeOnce('form-updated'); 124 if ($forms.length) { 125 $.makeArray($forms).forEach(function (form) { 126 form.removeAttribute('data-drupal-form-fields'); 127 $(form).off('.formUpdated'); 128 }); 129 } 130 } 131 } 132 }; 133 134 Drupal.behaviors.fillUserInfoFromBrowser = { 135 attach: function attach(context, settings) { 136 var userInfo = ['name', 'mail', 'homepage']; 137 var $forms = $('[data-user-info-from-browser]').once('user-info-from-browser'); 138 if ($forms.length) { 139 userInfo.forEach(function (info) { 140 var $element = $forms.find('[name=' + info + ']'); 141 var browserData = localStorage.getItem('Drupal.visitor.' + info); 142 var emptyOrDefault = $element.val() === '' || $element.attr('data-drupal-default-value') === $element.val(); 143 if ($element.length && emptyOrDefault && browserData) { 144 $element.val(browserData); 145 } 146 }); 147 } 148 $forms.on('submit', function () { 149 userInfo.forEach(function (info) { 150 var $element = $forms.find('[name=' + info + ']'); 151 if ($element.length) { 152 localStorage.setItem('Drupal.visitor.' + info, $element.val()); 153 } 154 }); 155 }); 156 } 157 }; 158 159 var handleFragmentLinkClickOrHashChange = function handleFragmentLinkClickOrHashChange(e) { 160 var url = void 0; 161 if (e.type === 'click') { 162 url = e.currentTarget.location ? e.currentTarget.location : e.currentTarget; 163 } else { 164 url = window.location; 165 } 166 var hash = url.hash.substr(1); 167 if (hash) { 168 var $target = $('#' + hash); 169 $('body').trigger('formFragmentLinkClickOrHashChange', [$target]); 170 171 setTimeout(function () { 172 return $target.trigger('focus'); 173 }, 300); 174 } 175 }; 176 177 var debouncedHandleFragmentLinkClickOrHashChange = debounce(handleFragmentLinkClickOrHashChange, 300, true); 178 179 $(window).on('hashchange.form-fragment', debouncedHandleFragmentLinkClickOrHashChange); 180 181 $(document).on('click.form-fragment', 'a[href*="#"]', debouncedHandleFragmentLinkClickOrHashChange); 182})(jQuery, Drupal, Drupal.debounce);; 183/** 184 * @file 185 * Webform behaviors. 186 */ 187 188(function ($, Drupal) { 189 190 'use strict'; 191 192 // Trigger Drupal's attaching of behaviors after the page is 193 // completely loaded. 194 // @see https://stackoverflow.com/questions/37838430/detect-if-page-is-load-from-back-button 195 // @see https://stackoverflow.com/questions/20899274/how-to-refresh-page-on-back-button-click/20899422#20899422 196 var isChrome = (/chrom(e|ium)/.test(window.navigator.userAgent.toLowerCase())); 197 if (isChrome) { 198 // Track back button in navigation. 199 // @see https://stackoverflow.com/questions/37838430/detect-if-page-is-load-from-back-button 200 var backButton = false;
201 if (window.performance) { 202 var navEntries = window.performance.getEntriesByType('navigation'); 203 if (navEntries.length > 0 && navEntries[0].type === 'back_forward') { 204 backButton = true; 205 } 206 else if (window.performance.navigation 207 && window.performance.navigation.type === window.performance.navigation.TYPE_BACK_FORWARD) { 208 backButton = true; 209 } 210 } 211 212 // If the back button is pressed, delay Drupal's attaching of behaviors. 213 if (backButton) { 214 var attachBehaviors = Drupal.attachBehaviors; 215 Drupal.attachBehaviors = function (context, settings) { 216 setTimeout(function (context, settings) { 217 attachBehaviors(context, settings); 218 }, 300); 219 }; 220 } 221 } 222 223})(jQuery, Drupal); 224; 225/** 226* DO NOT EDIT THIS FILE. 227* See the following change record for more information, 228* https://www.drupal.org/node/2815083 229* @preserve 230**/ 231 232(function ($, Drupal) { 233 var states = { 234 postponed: [] 235 }; 236 237 Drupal.states = states; 238 239 function invert(a, invertState) { 240 return invertState && typeof a !== 'undefined' ? !a : a; 241 } 242 243 function _compare2(a, b) { 244 if (a === b) { 245 return typeof a === 'undefined' ? a : true; 246 } 247 248 return typeof a === 'undefined' || typeof b === 'undefined'; 249 } 250 251 function ternary(a, b) { 252 if (typeof a === 'undefined') { 253 return b; 254 } 255 if (typeof b === 'undefined') { 256 return a; 257 } 258 259 return a && b; 260 } 261 262 Drupal.behaviors.states = { 263 attach: function attach(context, settings) { 264 var $states = $(context).find('[data-drupal-states]'); 265 var il = $states.length; 266 267 var _loop = function _loop(i) { 268 var config = JSON.parse($states[i].getAttribute('data-drupal-states')); 269 Object.keys(config || {}).forEach(function (state) { 270 new states.Dependent({ 271 element: $($states[i]), 272 state: states.State.sanitize(state), 273 constraints: config[state] 274 }); 275 }); 276 }; 277 278 for (var i = 0; i < il; i++) { 279 _loop(i); 280 } 281 282 while (states.postponed.length) { 283 states.postponed.shift()(); 284 } 285 } 286 }; 287 288 states.Dependent = function (args) { 289 var _this = this; 290 291 $.extend(this, { values: {}, oldValue: null }, args); 292 293 this.dependees = this.getDependees(); 294 Object.keys(this.dependees || {}).forEach(function (selector) { 295 _this.initializeDependee(selector, _this.dependees[selector]); 296 }); 297 }; 298 299 states.Dependent.comparisons = { 300 RegExp: function RegExp(reference, value) { 301 return reference.test(value); 302 }, 303 Function: function Function(reference, value) { 304 return reference(value); 305 }, 306 Number: function Number(reference, value) { 307 return typeof value === 'string' ? _compare2(reference.toString(), value) : _compare2(reference, value); 308 } 309 }; 310 311 states.Dependent.prototype = { 312 initializeDependee: function initializeDependee(selector, dependeeStates) { 313 var _this2 = this; 314 315 this.values[selector] = {}; 316 317 Object.keys(dependeeStates).forEach(function (i) { 318 var state = dependeeStates[i]; 319 320 if ($.inArray(state, dependeeStates) === -1) { 321 return; 322 } 323 324 state = states.State.sanitize(state); 325 326 _this2.values[selector][state.name] = null; 327 328 $(selector).on('state:' + state, { selector: selector, state: state }, function (e) { 329 _this2.update(e.data.selector, e.data.state, e.value); 330 }); 331 332 new states.Trigger({ selector: selector, state: state }); 333 }); 334 }, 335 compare: function compare(reference, selector, state) { 336 var value = this.values[selector][state.name]; 337 if (reference.constructor.name in states.Dependent.comparisons) { 338 return states.Dependent.comparisons[reference.constructor.name](reference, value); 339 } 340 341 return _compare2(reference, value); 342 }, 343 update: function update(selector, state, value) { 344 if (value !== this.values[selector][state.name]) { 345 this.values[selector][state.name] = value; 346 this.reevaluate(); 347 } 348 }, 349 reevaluate: function reevaluate() { 350 var value = this.verifyConstraints(this.constraints); 351 352 if (value !== this.oldValue) { 353 this.oldValue = value; 354 355 value = invert(value, this.state.invert); 356 357 this.element.trigger({ 358 type: 'state:' + this.state, 359 value: value, 360 trigger: true 361 }); 362 } 363 }, 364 verifyConstraints: function verifyConstraints(constraints, selector) { 365 var result = void 0; 366 if ($.isArray(constraints)) { 367 var hasXor = $.inArray('xor', constraints) === -1; 368 var len = constraints.length; 369 for (var i = 0; i < len; i++) { 370 if (constraints[i] !== 'xor') { 371 var constraint = this.checkConstraints(constraints[i], selector, i); 372 373 if (constraint && (hasXor || result)) { 374 return hasXor; 375 } 376 result = result || constraint; 377 } 378 } 379 } else if ($.isPlainObject(constraints)) { 380 for (var n in constraints) { 381 if (constraints.hasOwnProperty(n)) { 382 result = ternary(result, this.checkConstraints(constraints[n], selector, n)); 383 384 if (result === false) { 385 return false;
386 } 387 } 388 } 389 } 390 return result; 391 }, 392 checkConstraints: function checkConstraints(value, selector, state) { 393 if (typeof state !== 'string' || /[0-9]/.test(state[0])) { 394 state = null; 395 } else if (typeof selector === 'undefined') { 396 selector = state; 397 state = null; 398 } 399 400 if (state !== null) { 401 state = states.State.sanitize(state); 402 return invert(this.compare(value, selector, state), state.invert); 403 } 404 405 return this.verifyConstraints(value, selector); 406 }, 407 getDependees: function getDependees() { 408 var cache = {}; 409 410 var _compare = this.compare; 411 this.compare = function (reference, selector, state) { 412 (cache[selector] || (cache[selector] = [])).push(state.name); 413 }; 414 415 this.verifyConstraints(this.constraints); 416 417 this.compare = _compare; 418 419 return cache; 420 } 421 }; 422 423 states.Trigger = function (args) { 424 $.extend(this, args); 425 426 if (this.state in states.Trigger.states) { 427 this.element = $(this.selector); 428 429 if (!this.element.data('trigger:' + this.state)) { 430 this.initialize(); 431 } 432 } 433 }; 434 435 states.Trigger.prototype = { 436 initialize: function initialize() { 437 var _this3 = this; 438 439 var trigger = states.Trigger.states[this.state]; 440 441 if (typeof trigger === 'function') { 442 trigger.call(window, this.element); 443 } else { 444 Object.keys(trigger || {}).forEach(function (event) { 445 _this3.defaultTrigger(event, trigger[event]); 446 }); 447 } 448 449 this.element.data('trigger:' + this.state, true); 450 }, 451 defaultTrigger: function defaultTrigger(event, valueFn) { 452 var oldValue = valueFn.call(this.element); 453 454 this.element.on(event, $.proxy(function (e) { 455 var value = valueFn.call(this.element, e); 456 457 if (oldValue !== value) { 458 this.element.trigger({ 459 type: 'state:' + this.state, 460 value: value, 461 oldValue: oldValue 462 }); 463 oldValue = value; 464 } 465 }, this)); 466 467 states.postponed.push($.proxy(function () { 468 this.element.trigger({ 469 type: 'state:' + this.state, 470 value: oldValue, 471 oldValue: null 472 }); 473 }, this)); 474 } 475 }; 476 477 states.Trigger.states = { 478 empty: { 479 keyup: function keyup() { 480 return this.val() === ''; 481 } 482 }, 483 484 checked: { 485 change: function change() { 486 var checked = false; 487 this.each(function () { 488 checked = $(this).prop('checked'); 489 490 return !checked; 491 }); 492 return checked; 493 } 494 }, 495 496 value: { 497 keyup: function keyup() { 498 if (this.length > 1) { 499 return this.filter(':checked').val() || false; 500 } 501 return this.val(); 502 }, 503 change: function change() { 504 if (this.length > 1) { 505 return this.filter(':checked').val() || false; 506 } 507 return this.val(); 508 } 509 }, 510 511 collapsed: { 512 collapsed: function collapsed(e) { 513 return typeof e !== 'undefined' && 'value' in e ? e.value : !this.is('[open]'); 514 } 515 } 516 }; 517 518 states.State = function (state) { 519 this.pristine = state; 520 this.name = state; 521 522 var process = true; 523 do { 524 while (this.name.charAt(0) === '!') { 525 this.name = this.name.substring(1); 526 this.invert = !this.invert; 527 } 528 529 if (this.name in states.State.aliases) { 530 this.name = states.State.aliases[this.name]; 531 } else { 532 process = false; 533 } 534 } while (process); 535 }; 536 537 states.State.sanitize = function (state) { 538 if (state instanceof states.State) { 539 return state; 540 } 541 542 return new states.State(state); 543 }; 544 545 states.State.aliases = { 546 enabled: '!disabled', 547 invisible: '!visible', 548 invalid: '!valid', 549 untouched: '!touched', 550 optional: '!required', 551 filled: '!empty', 552 unchecked: '!checked', 553 irrelevant: '!relevant', 554 expanded: '!collapsed', 555 open: '!collapsed', 556 closed: 'collapsed', 557 readwrite: '!readonly' 558 }; 559 560 states.State.prototype = { 561 invert: false, 562 563 toString: function toString() { 564 return this.name; 565 } 566 }; 567 568 var $document = $(document); 569 $document.on('state:disabled', function (e) { 570 if (e.trigger) { 571 $(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); 572 } 573 }); 574 575 $document.on('state:required', function (e) { 576 if (e.trigger) { 577 if (e.value) { 578 var label = 'label' + (e.target.id ? '[for=' + e.target.id + ']' : ''); 579 var $label = $(e.target).attr({ required: 'required', 'aria-required': 'true' }).closest('.js-form-item, .js-form-wrapper').find(label); 580 581 if (!$label.hasClass('js-form-required').length) { 582 $label.addClass('js-form-required form-required'); 583 } 584 } else { 585 $(e.target).removeAttr('required aria-required').closest('.js-form-item, .js-form-wrapper').find('label.js-form-required').removeClass('js-form-required form-required'); 586 } 587 } 588 }); 589 590 $document.on('state:visible', function (e) { 591 if (e.trigger) { 592 $(e.target).closest('.js-form-item, .js-form-submit, .js-form-wrapper').toggle(e.value); 593 } 594 }); 595 596 $document.on('state:checked', function (e) { 597 if (e.trigger) { 598 $(e.target).prop('checked', e.value); 599 } 600 }); 601 602 $document.on('state:collapsed', function (e) { 603 if (e.trigger) { 604 if ($(e.target).is('[open]') === e.value) { 605 $(e.target).find('> summary').trigger('click'); 606 } 607 } 608 }); 609})(jQuery, Drupal);; 610/** 611 * @file 612 * JavaScript behaviors for custom webform #states. 613 */ 614 615(function ($, Drupal) { 616 617 'use strict'; 618 619 Drupal.webform = Drupal.webform || {}; 620 Drupal.webform.states = Drupal.webform.states || {}; 621 Drupal.webform.states.slideDown = Drupal.webform.states.slideDown || {}; 622 Drupal.webform.states.slideDown.duration = 'slow'; 623 Drupal.webform.states.slideUp = Drupal.webform.states.slideUp || {}; 624 Drupal.webform.states.slideUp.duration = 'fast'; 625 626 /* ************************************************************************ */ 627 // jQuery functions. 628 /* ************************************************************************ */ 629 630 /** 631 * Check if an element has a specified data attribute. 632 * 633 * @param {string} data 634 * The data attribute name. 635 * 636 * @return {boolean} 637 * TRUE if an element has a specified data attribute. 638 */ 639 $.fn.hasData = function (data) { 640 return (typeof this.data(data) !== 'undefined'); 641 }; 642 643 /**
644 * Check if element is within the webform or not. 645 * 646 * @return {boolean} 647 * TRUE if element is within the webform. 648 */ 649 $.fn.isWebform = function () { 650 return $(this).closest('form.webform-submission-form, form[id^="webform"], form[data-is-webform]').length ? true : false; 651 }; 652 653 /** 654 * Check if element is to be treated as a webform element. 655 * 656 * @return {boolean} 657 * TRUE if element is to be treated as a webform element. 658 */ 659 $.fn.isWebformElement = function () { 660 return ($(this).isWebform() || $(this).closest('[data-is-webform-element]').length) ? true : false; 661 }; 662 663 /* ************************************************************************ */ 664 // Trigger. 665 /* ************************************************************************ */ 666 667 // The change event is triggered by cut-n-paste and select menus. 668 // Issue #2445271: #states element empty check not triggered on mouse 669 // based paste. 670 // @see https://www.drupal.org/node/2445271 671 Drupal.states.Trigger.states.empty.change = function change() { 672 return this.val() === ''; 673 }; 674 675 /* ************************************************************************ */ 676 // Dependents. 677 /* ************************************************************************ */ 678 679 680 // Apply solution included in #1962800 patch. 681 // Issue #1962800: Form #states not working with literal integers as 682 // values in IE11. 683 // @see https://www.drupal.org/project/drupal/issues/1962800 684 // @see https://www.drupal.org/files/issues/core-states-not-working-with-integers-ie11_1962800_46.patch 685 // 686 // This issue causes pattern, less than, and greater than support to break. 687 // @see https://www.drupal.org/project/webform/issues/2981724 688 var states = Drupal.states; 689 Drupal.states.Dependent.prototype.compare = function compare(reference, selector, state) { 690 var value = this.values[selector][state.name]; 691 692 var name = reference.constructor.name; 693 if (!name) { 694 name = $.type(reference); 695 696 name = name.charAt(0).toUpperCase() + name.slice(1); 697 } 698 if (name in states.Dependent.comparisons) { 699 return states.Dependent.comparisons[name](reference, value); 700 } 701 702 if (reference.constructor.name in states.Dependent.comparisons) { 703 return states.Dependent.comparisons[reference.constructor.name](reference, value); 704 } 705 706 return _compare2(reference, value); 707 }; 708 function _compare2(a, b) { 709 if (a === b) { 710 return typeof a === 'undefined' ? a : true; 711 } 712 713 return typeof a === 'undefined' || typeof b === 'undefined'; 714 } 715 716 // Adds pattern, less than, and greater than support to #state API. 717 // @see http://drupalsun.com/julia-evans/2012/03/09/extending-form-api-states-regular-expressions 718 Drupal.states.Dependent.comparisons.Object = function (reference, value) { 719 if ('pattern' in reference) { 720 return (new RegExp(reference['pattern'])).test(value); 721 } 722 else if ('!pattern' in reference) { 723 return !((new RegExp(reference['!pattern'])).test(value)); 724 } 725 else if ('less' in reference) { 726 return (value !== '' && parseFloat(reference['less']) > parseFloat(value)); 727 } 728 else if ('less_equal' in reference) { 729 return (value !== '' && parseFloat(reference['less_equal']) >= parseFloat(value)); 730 } 731 else if ('greater' in reference) { 732 return (value !== '' && parseFloat(reference['greater']) < parseFloat(value)); 733 } 734 else if ('greater_equal' in reference) { 735 return (value !== '' && parseFloat(reference['greater_equal']) <= parseFloat(value)); 736 } 737 else if ('between' in reference || '!between' in reference) { 738 if (value === '') { 739 return false; 740 } 741 742 var between = reference['between'] || reference['!between']; 743 var betweenParts = between.split(':'); 744 var greater = betweenParts[0]; 745 var less = (typeof betweenParts[1] !== 'undefined') ? betweenParts[1] : null; 746 var isGreaterThan = (greater === null || greater === '' || parseFloat(value) >= parseFloat(greater)); 747 var isLessThan = (less === null || less === '' || parseFloat(value) <= parseFloat(less)); 748 var result = (isGreaterThan && isLessThan); 749 return (reference['!between']) ? !result : result; 750 } 751 else { 752 return reference.indexOf(value) !== false; 753 } 754 }; 755 756 /* ************************************************************************ */ 757 // States events. 758 /* ************************************************************************ */ 759 760 var $document = $(document); 761 762 $document.on('state:required', function (e) { 763 if (e.trigger && $(e.target).isWebformElement()) { 764 var $target = $(e.target); 765 // Fix #required file upload. 766 // @see Issue #2860529: Conditional required File upload field don't work. 767 toggleRequired($target.find('input[type="file"]'), e.value); 768 769 // Fix #required for radios and likert. 770 // @see Issue #2856795: If radio buttons are required but not filled form is nevertheless submitted. 771 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')) { 772 $target.toggleClass('required', e.value); 773 toggleRequired($target.find('input[type="radio"]'), e.value); 774 } 775 776 // Fix #required for checkboxes. 777 // @see Issue #2938414: Checkboxes don't support #states required. 778 // @see checkboxRequiredhandler 779 if ($target.is('.js-form-type-checkboxes, .js-form-type-webform-checkboxes-other, .js-webform-type-checkboxes, .js-webform-type-webform-checkboxes-other')) { 780 $target.toggleClass('required', e.value); 781 var $checkboxes = $target.find('input[type="checkbox"]'); 782 if (e.value) { 783 // Add event handler. 784 $checkboxes.on('click', statesCheckboxesRequiredEventHandler); 785 // Initialize and add required attribute. 786 checkboxesRequired($target); 787 } 788 else { 789 // Remove event handler. 790 $checkboxes.off('click', statesCheckboxesRequiredEventHandler); 791 // Remove required attribute. 792 toggleRequired($checkboxes, false); 793 } 794 } 795 796 // Fix #required for tableselect. 797 // @see Issue #3212581: Table select does not trigger client side validation 798 if ($target.is('.js-webform-tableselect')) { 799 $target.toggleClass('required', e.value); 800 var isMultiple = $target.is('[multiple]'); 801 if (isMultiple) { 802 // Checkboxes. 803 var $tbody = $target.find('tbody'); 804 var $checkboxes = $tbody.find('input[type="checkbox"]'); 805 copyRequireMessage($target, $checkboxes); 806 if (e.value) { 807 $checkboxes.on('click change', statesCheckboxesRequiredEventHandler); 808 checkboxesRequired($tbody); 809 } 810 else { 811 $checkboxes.off('click change ', statesCheckboxesRequiredEventHandler); 812 toggleRequired($tbody, false); 813 } 814 } 815 else { 816 // Radios. 817 var $radios = $target.find('input[type="radio"]'); 818 copyRequireMessage($target, $radios); 819 toggleRequired($radios, e.value); 820 } 821 } 822 823 // Fix required label for elements without the for attribute. 824 // @see Issue #3145300: Conditional Visible Select Other not working. 825 if ($target.is('.js-form-type-webform-select-other, .js-webform-type-webform-select-other')) { 826 var $select = $target.find('select'); 827 toggleRequired($select, e.value); 828 copyRequireMessage($target, $select); 829 } 830 if ($target.find('> label:not([for])').length) { 831 $target.find('> label').toggleClass('js-form-required form-required', e.value); 832 } 833 834 // Fix required label for checkboxes and radios. 835 // @see Issue #2938414: Checkboxes don't support #states required 836 // @see Issue #2731991: Setting required on radios marks all options required. 837 // @see Issue #2856315: Conditional Logic - Requiring Radios in a Fieldset. 838 // Fix #required for fieldsets. 839 // @see Issue #2977569: Hidden fieldsets that become visible with conditional logic cannot be made required. 840 if ($target.is('.js-webform-type-radios, .js-webform-type-checkboxes, fieldset')) { 841 $target.find('legend span.fieldset-legend:not(.visually-hidden)').toggleClass('js-form-required form-required', e.value); 842 } 843 844 // Issue #2986017: Fieldsets shouldn't have required attribute. 845 if ($target.is('fieldset')) { 846 $target.removeAttr('required aria-required'); 847 } 848 } 849 }); 850 851 $document.on('state:checked', function (e) { 852 if (e.trigger) { 853 $(e.target).trigger('change'); 854 } 855 }); 856 857 $document.on('state:readonly', function (e) { 858 if (e.trigger && $(e.target).isWebformElement()) { 859 $(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); 860 861 // Trigger webform:readonly. 862 $(e.target).trigger('webform:readonly') 863 .find('select, input, textarea, button').trigger('webform:readonly'); 864 } 865 }); 866 867 $document.on('state:visible state:visible-slide', function (e) { 868 if (e.trigger && $(e.target).isWebformElement()) { 869 if (e.value) { 870 $(':input', e.target).addBack().each(function () { 871 restoreValueAndRequired(this); 872 triggerEventHandlers(this); 873 }); 874 } 875 else { 876 // @see https://www.sitepoint.com/jquery-function-clear-form-data/ 877 $(':input', e.target).addBack().each(function () { 878 backupValueAndRequired(this); 879 clearValueAndRequired(this); 880 triggerEventHandlers(this); 881 }); 882 } 883 } 884 }); 885 886 $document.on('state:visible-slide', function (e) { 887 if (e.trigger && $(e.target).isWebformElement()) { 888 var effect = e.value ? 'slideDown' : 'slideUp'; 889 var duration = Drupal.webform.states[effect].duration; 890 $(e.target).closest('.js-form-item, .js-form-submit, .js-form-wrapper')[effect](duration); 891 } 892 }); 893 Drupal.states.State.aliases['invisible-slide'] = '!visible-slide'; 894 895 $document.on('state:disabled', function (e) { 896 if (e.trigger && $(e.target).isWebformElement()) { 897 // Make sure disabled property is set before triggering webform:disabled. 898 // Copied from: core/misc/states.js 899 $(e.target) 900 .prop('disabled', e.value) 901 .closest('.js-form-item, .js-form-submit, .js-form-wrapper').toggleClass('form-disabled', e.value) 902 .find('select, input, textarea, button').prop('disabled', e.value); 903 904 // Never disable hidden file[fids] because the existing values will 905 // be completely lost when the webform is submitted. 906 var fileElements = $(e.target) 907 .find(':input[type="hidden"][name$="[fids]"]'); 908 if (fileElements.length) { 909 // Remove 'disabled' attribute from fieldset which will block 910 // all disabled elements from being submitted. 911 if ($(e.target).is('fieldset')) { 912 $(e.target).prop('disabled', false); 913 } 914 fileElements.removeAttr('disabled'); 915 } 916 917 // Trigger webform:disabled. 918 $(e.target).trigger('webform:disabled') 919 .find('select, input, textarea, button').trigger('webform:disabled'); 920 } 921 }); 922 923 /* ************************************************************************ */ 924 // Behaviors. 925 /* ************************************************************************ */ 926 927 /** 928 * Adds HTML5 validation to required checkboxes. 929 * 930 * @type {Drupal~behavior} 931 * 932 * @see https://www.drupal.org/project/webform/issues/3068998 933 */ 934 Drupal.behaviors.webformCheckboxesRequired = { 935 attach: function (context) { 936 $('.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) 937 .once('webform-checkboxes-required') 938 .each(function () { 939 var $element = $(this); 940 $element.find('input[type="checkbox"]').on('click', statesCheckboxesRequiredEventHandler); 941 setTimeout(function () {checkboxesRequired($element);}); 942 }); 943 } 944 }; 945 946 /** 947 * Adds HTML5 validation to required radios. 948 * 949 * @type {Drupal~behavior} 950 * 951 * @see https://www.drupal.org/project/webform/issues/2856795 952 */ 953 Drupal.behaviors.webformRadiosRequired = { 954 attach: function (context) { 955 $('.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) 956 .once('webform-radios-required') 957 .each(function () { 958 var $element = $(this); 959 setTimeout(function () {radiosRequired($element);}); 960 }); 961 } 962 }; 963 964 /** 965 * Adds HTML5 validation to required table select. 966 * 967 * @type {Drupal~behavior} 968 * 969 * @see https://www.drupal.org/project/webform/issues/2856795 970 */ 971 Drupal.behaviors.webformTableSelectRequired = { 972 attach: function (context) { 973 $('.js-webform-tableselect.required', context) 974 .once('webform-tableselect-required') 975 .each(function () { 976 var $element = $(this); 977 var $tbody = $element.find('tbody'); 978 var isMultiple = $element.is('[multiple]'); 979 980 if (isMultiple) { 981 // Check all checkbox triggers checkbox 'change' event on 982 // select and deselect all. 983 // @see Drupal.tableSelect 984 $tbody.find('input[type="checkbox"]').on('click change', function () { 985 checkboxesRequired($tbody); 986 }); 987 } 988 989 setTimeout(function () { 990 isMultiple ? checkboxesRequired($tbody) : radiosRequired($element); 991 }); 992 }); 993 } 994 }; 995 996 /** 997 * Add HTML5 multiple checkboxes required validation. 998 * 999 * @param {jQuery} $element
1000 * An jQuery object containing HTML5 radios. 1001 * 1002 * @see https://stackoverflow.com/a/37825072/145846 1003 */ 1004 function checkboxesRequired($element) { 1005 var $firstCheckbox = $element.find('input[type="checkbox"]').first(); 1006 var isChecked = $element.find('input[type="checkbox"]').is(':checked'); 1007 toggleRequired($firstCheckbox, !isChecked); 1008 copyRequireMessage($element, $firstCheckbox); 1009 } 1010 1011 /** 1012 * Add HTML5 radios required validation. 1013 * 1014 * @param {jQuery} $element 1015 * An jQuery object containing HTML5 radios. 1016 * 1017 * @see https://www.drupal.org/project/webform/issues/2856795 1018 */ 1019 function radiosRequired($element) { 1020 var $radios = $element.find('input[type="radio"]'); 1021 var isRequired = $element.hasClass('required'); 1022 toggleRequired($radios, isRequired); 1023 copyRequireMessage($element, $radios); 1024 } 1025 1026 /* ************************************************************************ */ 1027 // Event handlers. 1028 /* ************************************************************************ */ 1029 1030 /** 1031 * Trigger #states API HTML5 multiple checkboxes required validation. 1032 * 1033 * @see https://stackoverflow.com/a/37825072/145846 1034 */ 1035 function statesCheckboxesRequiredEventHandler() { 1036 var $element = $(this).closest('.js-webform-type-checkboxes, .js-webform-type-webform-checkboxes-other'); 1037 checkboxesRequired($element); 1038 } 1039 1040 /** 1041 * Trigger an input's event handlers. 1042 * 1043 * @param {element} input 1044 * An input. 1045 */ 1046 function triggerEventHandlers(input) { 1047 var $input = $(input); 1048 var type = input.type; 1049 var tag = input.tagName.toLowerCase(); 1050 // Add 'webform.states' as extra parameter to event handlers. 1051 // @see Drupal.behaviors.webformUnsaved 1052 var extraParameters = ['webform.states']; 1053 if (type === 'checkbox' || type === 'radio') { 1054 $input 1055 .trigger('change', extraParameters) 1056 .trigger('blur', extraParameters); 1057 } 1058 else if (tag === 'select') { 1059 // Do not trigger the onchange event for Address element's country code 1060 // when it is initialized. 1061 // @see \Drupal\address\Element\Country 1062 if ($input.closest('.webform-type-address').length) { 1063 if (!$input.data('webform-states-address-initialized') 1064 && $input.attr('autocomplete') === 'country' 1065 && $input.val() === $input.find("option[selected]").attr('value')) { 1066 return; 1067 } 1068 $input.data('webform-states-address-initialized', true); 1069 } 1070 1071 $input 1072 .trigger('change', extraParameters) 1073 .trigger('blur', extraParameters); 1074 } 1075 else if (type !== 'submit' && type !== 'button' && type !== 'file') { 1076 // Make sure input mask is removed and then reset when value is restored. 1077 // @see https://www.drupal.org/project/webform/issues/3124155 1078 // @see https://www.drupal.org/project/webform/issues/3202795 1079 var hasInputMask = ($.fn.inputmask && $input.hasClass('js-webform-input-mask')); 1080 hasInputMask && $input.inputmask('remove'); 1081 1082 $input 1083 .trigger('input', extraParameters) 1084 .trigger('change', extraParameters) 1085 .trigger('keydown', extraParameters) 1086 .trigger('keyup', extraParameters) 1087 .trigger('blur', extraParameters); 1088 1089 hasInputMask && $input.inputmask(); 1090 } 1091 } 1092 1093 /* ************************************************************************ */ 1094 // Backup and restore value functions. 1095 /* ************************************************************************ */ 1096 1097 /** 1098 * Backup an input's current value and required attribute 1099 * 1100 * @param {element} input 1101 * An input. 1102 */ 1103 function backupValueAndRequired(input) { 1104 var $input = $(input); 1105 var type = input.type; 1106 var tag = input.tagName.toLowerCase(); // Normalize case. 1107 1108 // Backup required. 1109 if ($input.prop('required') && !$input.hasData('webform-required')) { 1110 $input.data('webform-required', true); 1111 } 1112 1113 // Backup value. 1114 if (!$input.hasData('webform-value')) { 1115 if (type === 'checkbox' || type === 'radio') { 1116 $input.data('webform-value', $input.prop('checked')); 1117 } 1118 else if (tag === 'select') { 1119 var values = []; 1120 $input.find('option:selected').each(function (i, option) { 1121 values[i] = option.value; 1122 }); 1123 $input.data('webform-value', values); 1124 } 1125 else if (type !== 'submit' && type !== 'button') { 1126 $input.data('webform-value', input.value); 1127 } 1128 } 1129 } 1130 1131 /** 1132 * Restore an input's value and required attribute. 1133 * 1134 * @param {element} input 1135 * An input. 1136 */ 1137 function restoreValueAndRequired(input) { 1138 var $input = $(input); 1139 1140 // Restore value. 1141 var value = $input.data('webform-value'); 1142 if (typeof value !== 'undefined') { 1143 var type = input.type; 1144 var tag = input.tagName.toLowerCase(); // Normalize case. 1145 1146 if (type === 'checkbox' || type === 'radio') { 1147 $input.prop('checked', value); 1148 } 1149 else if (tag === 'select') { 1150 $.each(value, function (i, option_value) { 1151 // Prevent "Syntax error, unrecognized expression" error by 1152 // escaping single quotes. 1153 // @see https://forum.jquery.com/topic/escape-characters-prior-to-using-selector 1154 option_value = option_value.replace(/'/g, "\\\'"); 1155 $input.find("option[value='" + option_value + "']").prop('selected', true); 1156 }); 1157 } 1158 else if (type !== 'submit' && type !== 'button') { 1159 input.value = value; 1160 } 1161 $input.removeData('webform-value'); 1162 } 1163 1164 // Restore required. 1165 var required = $input.data('webform-required'); 1166 if (typeof required !== 'undefined') { 1167 if (required) { 1168 $input.prop('required', true); 1169 } 1170 $input.removeData('webform-required'); 1171 } 1172 } 1173 1174 /** 1175 * Clear an input's value and required attributes. 1176 * 1177 * @param {element} input 1178 * An input. 1179 */ 1180 function clearValueAndRequired(input) { 1181 var $input = $(input); 1182 1183 // Check for #states no clear attribute.
1184 // @see https://css-tricks.com/snippets/jquery/make-an-jquery-hasattr/ 1185 if ($input.closest('[data-webform-states-no-clear]').length) { 1186 return; 1187 } 1188 1189 // Clear value. 1190 var type = input.type; 1191 var tag = input.tagName.toLowerCase(); // Normalize case. 1192 if (type === 'checkbox' || type === 'radio') { 1193 $input.prop('checked', false); 1194 } 1195 else if (tag === 'select') { 1196 if ($input.find('option[value=""]').length) { 1197 $input.val(''); 1198 } 1199 else { 1200 input.selectedIndex = -1; 1201 } 1202 } 1203 else if (type !== 'submit' && type !== 'button') { 1204 input.value = (type === 'color') ? '#000000' : ''; 1205 } 1206 1207 // Clear required. 1208 $input.prop('required', false); 1209 } 1210 1211 /* ************************************************************************ */ 1212 // Helper functions. 1213 /* ************************************************************************ */ 1214 1215 /** 1216 * Toggle an input's required attributes. 1217 * 1218 * @param {element} $input 1219 * An input. 1220 * @param {boolean} required 1221 * Is input required. 1222 */ 1223 function toggleRequired($input, required) { 1224 var isCheckboxOrRadio = ($input.attr('type') === 'radio' || $input.attr('type') === 'checkbox'); 1225 if (required) { 1226 if (isCheckboxOrRadio) { 1227 $input.attr({'required': 'required'}); 1228 } 1229 else { 1230 $input.attr({'required': 'required', 'aria-required': 'true'}); 1231 } 1232 } 1233 else { 1234 if (isCheckboxOrRadio) { 1235 $input.removeAttr('required'); 1236 } 1237 else { 1238 $input.removeAttr('required aria-required'); 1239 } 1240 } 1241 } 1242 1243 /** 1244 * Copy the clientside_validation.module's message. 1245 * 1246 * @param {jQuery} $source 1247 * The source element. 1248 * @param {jQuery} $destination 1249 * The destination element. 1250 */ 1251 function copyRequireMessage($source, $destination) { 1252 if ($source.attr('data-msg-required')) { 1253 $destination.attr('data-msg-required', $source.attr('data-msg-required')); 1254 } 1255 } 1256 1257})(jQuery, Drupal); 1258; 1259/** 1260 * @file 1261 * JavaScript behaviors for webforms. 1262 */ 1263 1264(function ($, Drupal) { 1265 1266 'use strict'; 1267 1268 /** 1269 * Remove single submit event listener. 1270 * 1271 * @type {Drupal~behavior} 1272 * 1273 * @prop {Drupal~behaviorAttach} attach 1274 * Attaches the behavior for removing single submit event listener. 1275 * 1276 * @see Drupal.behaviors.formSingleSubmit 1277 */ 1278 Drupal.behaviors.webformRemoveFormSingleSubmit = { 1279 attach: function attach() { 1280 function onFormSubmit(e) { 1281 var $form = $(e.currentTarget); 1282 $form.removeAttr('data-drupal-form-submit-last'); 1283 } 1284 $('body') 1285 .once('webform-single-submit') 1286 .on('submit.singleSubmit', 'form.webform-remove-single-submit', onFormSubmit); 1287 } 1288 }; 1289 1290 /** 1291 * Prevent webform autosubmit on wizard pages. 1292 * 1293 * @type {Drupal~behavior} 1294 * 1295 * @prop {Drupal~behaviorAttach} attach 1296 * Attaches the behavior for disabling webform autosubmit. 1297 * Wizard pages need to be progressed with the Previous or Next buttons, 1298 * not by pressing Enter. 1299 */ 1300 Drupal.behaviors.webformDisableAutoSubmit = { 1301 attach: function (context) { 1302 // Not using context so that inputs loaded via Ajax will have autosubmit 1303 // disabled. 1304 // @see http://stackoverflow.com/questions/11235622/jquery-disable-form-submit-on-enter 1305 $('.js-webform-disable-autosubmit input') 1306 .not(':button, :submit, :reset, :image, :file') 1307 .once('webform-disable-autosubmit') 1308 .on('keyup keypress', function (e) { 1309 if (e.which === 13) { 1310 e.preventDefault(); 1311 return false; 1312 } 1313 }); 1314 } 1315 }; 1316 1317 /** 1318 * Custom required and pattern validation error messages. 1319 * 1320 * @type {Drupal~behavior} 1321 * 1322 * @prop {Drupal~behaviorAttach} attach 1323 * Attaches the behavior for the webform custom required and pattern 1324 * validation error messages. 1325 * 1326 * @see http://stackoverflow.com/questions/5272433/html5-form-required-attribute-set-custom-validation-message 1327 **/ 1328 Drupal.behaviors.webformRequiredError = { 1329 attach: function (context) { 1330 $(context).find(':input[data-webform-required-error], :input[data-webform-pattern-error]').once('webform-required-error') 1331 .on('invalid', function () { 1332 this.setCustomValidity(''); 1333 if (this.valid) { 1334 return; 1335 } 1336 1337 if (this.validity.patternMismatch && $(this).attr('data-webform-pattern-error')) { 1338 this.setCustomValidity($(this).attr('data-webform-pattern-error')); 1339 } 1340 else if (this.validity.valueMissing && $(this).attr('data-webform-required-error')) { 1341 this.setCustomValidity($(this).attr('data-webform-required-error')); 1342 } 1343 }) 1344 .on('input change', function () { 1345 // Find all related elements by name and reset custom validity. 1346 // This specifically applies to required radios and checkboxes. 1347 var name = $(this).attr('name'); 1348 $(this.form).find(':input[name="' + name + '"]').each(function () { 1349 this.setCustomValidity(''); 1350 }); 1351 }); 1352 } 1353 }; 1354 1355 // When #state:required is triggered we need to reset the target elements 1356 // custom validity. 1357 $(document).on('state:required', function (e) { 1358 $(e.target).filter('[data-webform-required-error]') 1359 .each(function () {this.setCustomValidity('');}); 1360 }); 1361 1362})(jQuery, Drupal); 1363; 1364/** 1365* DO NOT EDIT THIS FILE. 1366* See the following change record for more information, 1367* https://www.drupal.org/node/2815083 1368* @preserve 1369**/ 1370 1371(function (Drupal, debounce) { 1372 var liveElement = void 0; 1373 var announcements = []; 1374 1375 Drupal.behaviors.drupalAnnounce = { 1376 attach: function attach(context) { 1377 if (!liveElement) { 1378 liveElement = document.createElement('div'); 1379 liveElement.id = 'drupal-live-announce'; 1380 liveElement.className = 'visually-hidden'; 1381 liveElement.setAttribute('aria-live', 'polite'); 1382 liveElement.setAttribute('aria-busy', 'false'); 1383 document.body.appendChild(liveElement); 1384 } 1385 } 1386 }; 1387 1388 function announce() { 1389 var text = []; 1390 var priority = 'polite'; 1391 var announcement = void 0; 1392 1393 var il = announcements.length; 1394 for (var i = 0; i < il; i++) { 1395 announcement = announcements.pop(); 1396 text.unshift(announcement.text); 1397 1398 if (announcement.priority === 'assertive') { 1399 priority = 'assertive'; 1400 } 1401 } 1402 1403 if (text.length) { 1404 liveElement.innerHTML = ''; 1405 1406 liveElement.setAttribute('aria-busy', 'true'); 1407 1408 liveElement.setAttribute('aria-live', priority); 1409 1410 liveElement.innerHTML = text.join('\n'); 1411 1412 liveElement.setAttribute('aria-busy', 'false'); 1413 } 1414 } 1415 1416 Drupal.announce = function (text, priority) { 1417 announcements.push({ 1418 text: text, 1419 priority: priority 1420 }); 1421 1422 return debounce(announce, 200)(); 1423 }; 1424})(Drupal, Drupal.debounce);; 1425/** 1426 * @file 1427 * JavaScript behaviors for details element. 1428 */ 1429 1430(function ($, Drupal) { 1431 1432 'use strict'; 1433 1434 Drupal.webform = Drupal.webform || {}; 1435 Drupal.webform.detailsToggle = Drupal.webform.detailsToggle || {}; 1436 Drupal.webform.detailsToggle.options = Drupal.webform.detailsToggle.options || {}; 1437 1438 /** 1439 * Attach handler to toggle details open/close state. 1440 * 1441 * @type {Drupal~behavior} 1442 */ 1443 Drupal.behaviors.webformDetailsToggle = { 1444 attach: function (context) { 1445 $('.js-webform-details-toggle', context).once('webform-details-toggle').each(function () { 1446 var $form = $(this); 1447 var $tabs = $form.find('.webform-tabs'); 1448 1449 // Get only the main details elements and ignore all nested details. 1450 var selector = ($tabs.length) ? '.webform-tab' : '.js-webform-details-toggle, .webform-elements'; 1451 var $details = $form.find('details').filter(function () { 1452 var $parents = $(this).parentsUntil(selector); 1453 return ($parents.find('details').length === 0); 1454 }); 1455 1456 // Toggle is only useful when there are two or more details elements. 1457 if ($details.length < 2) { 1458 return; 1459 } 1460 1461 var options = $.extend({ 1462 button: '<button type="button" class="webform-details-toggle-state"></button>' 1463 }, Drupal.webform.detailsToggle.options); 1464 1465 // Create toggle buttons. 1466 var $toggle = $(options.button) 1467 .attr('title', Drupal.t('Toggle details widget state.')) 1468 .on('click', function (e) { 1469 // Get details that are not vertical tabs pane. 1470 var $details = $form.find('details:not(.vertical-tabs__pane)'); 1471 var open; 1472 if (Drupal.webform.detailsToggle.isFormDetailsOpen($form)) { 1473 $details.removeAttr('open'); 1474 open = 0; 1475 } 1476 else { 1477 $details.attr('open', 'open'); 1478 open = 1; 1479 } 1480 Drupal.webform.detailsToggle.setDetailsToggleLabel($form); 1481 1482 // Set the saved states for all the details elements. 1483 // @see webform.element.details.save.js 1484 if (Drupal.webformDetailsSaveGetName) { 1485 $details.each(function () { 1486 // Note: Drupal.webformDetailsSaveGetName checks if localStorage 1487 // exists and is enabled. 1488 // @see webform.element.details.save.js 1489 var name = Drupal.webformDetailsSaveGetName($(this)); 1490 if (name) {
1491 localStorage.setItem(name, open); 1492 } 1493 }); 1494 } 1495 }) 1496 .wrap('<div class="webform-details-toggle-state-wrapper"></div>') 1497 .parent(); 1498 1499 if ($tabs.length) { 1500 // Add toggle state before the tabs. 1501 $tabs.find('.item-list:first-child').eq(0).before($toggle); 1502 } 1503 else { 1504 // Add toggle state link to first details element. 1505 $details.eq(0).before($toggle); 1506 } 1507 1508 Drupal.webform.detailsToggle.setDetailsToggleLabel($form); 1509 }); 1510 } 1511 }; 1512 1513 /** 1514 * Determine if a webform's details are all opened. 1515 * 1516 * @param {jQuery} $form 1517 * A webform. 1518 * 1519 * @return {boolean} 1520 * TRUE if a webform's details are all opened. 1521 */ 1522 Drupal.webform.detailsToggle.isFormDetailsOpen = function ($form) { 1523 return ($form.find('details[open]').length === $form.find('details').length); 1524 }; 1525 1526 /** 1527 * Set a webform's details toggle state widget label. 1528 * 1529 * @param {jQuery} $form 1530 * A webform. 1531 */ 1532 Drupal.webform.detailsToggle.setDetailsToggleLabel = function ($form) { 1533 var isOpen = Drupal.webform.detailsToggle.isFormDetailsOpen($form); 1534 1535 var label = (isOpen) ? Drupal.t('Collapse all') : Drupal.t('Expand all'); 1536 $form.find('.webform-details-toggle-state').html(label); 1537 1538 var text = (isOpen) ? Drupal.t('All details have been expanded.') : Drupal.t('All details have been collapsed.'); 1539 Drupal.announce(text); 1540 }; 1541 1542})(jQuery, Drupal); 1543; 1544/** 1545* DO NOT EDIT THIS FILE. 1546* See the following change record for more information, 1547* https://www.drupal.org/node/2815083 1548* @preserve 1549**/ 1550 1551(function ($, Drupal) { 1552 Drupal.behaviors.detailsAria = { 1553 attach: function attach() { 1554 $('body').once('detailsAria').on('click.detailsAria', 'summary', function (event) { 1555 var $summary = $(event.currentTarget); 1556 var open = $(event.currentTarget.parentNode).attr('open') === 'open' ? 'false' : 'true'; 1557 1558 $summary.attr({ 1559 'aria-expanded': open, 1560 'aria-pressed': open 1561 }); 1562 }); 1563 } 1564 }; 1565})(jQuery, Drupal);; 1566/** 1567* DO NOT EDIT THIS FILE. 1568* See the following change record for more information, 1569* https://www.drupal.org/node/2815083 1570* @preserve 1571**/ 1572 1573(function ($, Modernizr, Drupal) { 1574 function CollapsibleDetails(node) { 1575 this.$node = $(node); 1576 this.$node.data('details', this); 1577 1578 var anchor = window.location.hash && window.location.hash !== '#' ? ', ' + window.location.hash : ''; 1579 if (this.$node.find('.error' + anchor).length) { 1580 this.$node.attr('open', true); 1581 } 1582 1583 this.setupSummary(); 1584 1585 this.setupLegend(); 1586 } 1587 1588 $.extend(CollapsibleDetails, { 1589 instances: [] 1590 }); 1591 1592 $.extend(CollapsibleDetails.prototype, { 1593 setupSummary: function setupSummary() { 1594 this.$summary = $('<span class="summary"></span>'); 1595 this.$node.on('summaryUpdated', $.proxy(this.onSummaryUpdated, this)).trigger('summaryUpdated'); 1596 }, 1597 setupLegend: function setupLegend() { 1598 var $legend = this.$node.find('> summary'); 1599 1600 $('<span class="details-summary-prefix visually-hidden"></span>').append(this.$node.attr('open') ? Drupal.t('Hide') : Drupal.t('Show')).prependTo($legend).after(document.createTextNode(' ')); 1601 1602 $('<a class="details-title"></a>').attr('href', '#' + this.$node.attr('id')).prepend($legend.contents()).appendTo($legend); 1603 1604 $legend.append(this.$summary).on('click', $.proxy(this.onLegendClick, this)); 1605 }, 1606 onLegendClick: function onLegendClick(e) { 1607 this.toggle(); 1608 e.preventDefault(); 1609 }, 1610 onSummaryUpdated: function onSummaryUpdated() { 1611 var text = $.trim(this.$node.drupalGetSummary()); 1612 this.$summary.html(text ? ' (' + text + ')' : ''); 1613 }, 1614 toggle: function toggle() { 1615 var _this = this; 1616 1617 var isOpen = !!this.$node.attr('open'); 1618 var $summaryPrefix = this.$node.find('> summary span.details-summary-prefix'); 1619 if (isOpen) { 1620 $summaryPrefix.html(Drupal.t('Show')); 1621 } else { 1622 $summaryPrefix.html(Drupal.t('Hide')); 1623 } 1624 1625 setTimeout(function () { 1626 _this.$node.attr('open', !isOpen); 1627 }, 0); 1628 } 1629 }); 1630 1631 Drupal.behaviors.collapse = { 1632 attach: function attach(context) { 1633 if (Modernizr.details) { 1634 return; 1635 } 1636 var $collapsibleDetails = $(context).find('details').once('collapse').addClass('collapse-processed'); 1637 if ($collapsibleDetails.length) { 1638 for (var i = 0; i < $collapsibleDetails.length; i++) { 1639 CollapsibleDetails.instances.push(new CollapsibleDetails($collapsibleDetails[i])); 1640 } 1641 } 1642 } 1643 }; 1644 1645 var handleFragmentLinkClickOrHashChange = function handleFragmentLinkClickOrHashChange(e, $target) { 1646 $target.parents('details').not('[open]').find('> summary').trigger('click'); 1647 }; 1648 1649 $('body').on('formFragmentLinkClickOrHashChange.details', handleFragmentLinkClickOrHashChange); 1650 1651 Drupal.CollapsibleDetails = CollapsibleDetails; 1652})(jQuery, Modernizr, Drupal);; 1653/** 1654 * @file 1655 * JavaScript behaviors for select menu. 1656 */ 1657 1658(function ($, Drupal) { 1659 1660 'use strict'; 1661 1662 /** 1663 * Disable select menu options using JavaScript. 1664 * 1665 * @type {Drupal~behavior} 1666 */ 1667 Drupal.behaviors.webformSelectOptionsDisabled = { 1668 attach: function (context) { 1669 $('select[data-webform-select-options-disabled]', context).once('webform-select-options-disabled').each(function () { 1670 var $select = $(this); 1671 var disabled = $select.attr('data-webform-select-options-disabled').split(/\s*,\s*/); 1672 $select.find('option').filter(function isDisabled() { 1673 return ($.inArray(this.value, disabled) !== -1); 1674 }).attr('disabled', 'disabled'); 1675 }); 1676 } 1677 }; 1678 1679 1680})(jQuery, Drupal); 1681; 1682/** 1683 * @file 1684 * Javascript for the Geojson Google Maps. 1685 * 1686 * @source: https://github.com/JasonSanford/GeoJSON-to-Google-Maps 1687 */ 1688 1689(function ($, Drupal) { 1690 1691 'use strict'; 1692 1693 Drupal.googleGeoJson = function (geojson, options) { 1694 1695 let _geometryToGoogleMaps = function (geojsonGeometry, opts, geojsonProperties) { 1696 1697 let googleObj; 1698 let bounds; 1699 let paths = []; 1700 let path = []; 1701 let i; 1702 let j; 1703 let k; 1704 let coord; 1705 let ll; 1706 1707 switch (geojsonGeometry.type) { 1708 case 'Point': 1709 opts.position = new google.maps.LatLng(geojsonGeometry.coordinates[1], geojsonGeometry.coordinates[0]); 1710 bounds = new google.maps.LatLngBounds(); 1711 bounds.extend(opts.position); 1712 googleObj = new google.maps.Marker(opts); 1713 googleObj.set('bounds', bounds); 1714 if (geojsonProperties) { 1715 googleObj.set('geojsonProperties', geojsonProperties); 1716 } 1717 break; 1718 1719 case 'MultiPoint': 1720 googleObj = []; 1721 bounds = new google.maps.LatLngBounds(); 1722 for (i = 0; i < geojsonGeometry.coordinates.length; i++) { 1723 opts.position = new google.maps.LatLng(geojsonGeometry.coordinates[i][1], geojsonGeometry.coordinates[i][0]); 1724 bounds.extend(opts.position); 1725 googleObj.push(new google.maps.Marker(opts)); 1726 } 1727 if (geojsonProperties) { 1728 for (k = 0; k < googleObj.length; k++) { 1729 googleObj[k].set('geojsonProperties', geojsonProperties); 1730 } 1731 } 1732 for (k = 0; k < googleObj.length; k++) { 1733 googleObj[k].set('bounds', bounds); 1734 } 1735 break; 1736 1737 case 'LineString': 1738 bounds = new google.maps.LatLngBounds(); 1739 path = []; 1740 for (i = 0; i < geojsonGeometry.coordinates.length; i++) { 1741 coord = geojsonGeometry.coordinates[i]; 1742 ll = new google.maps.LatLng(coord[1], coord[0]); 1743 bounds.extend(ll); 1744 path.push(ll); 1745 } 1746 opts.path = path; 1747 googleObj = new google.maps.Polyline(opts); 1748 googleObj.set('bounds', bounds); 1749 if (geojsonProperties) { 1750 googleObj.set('geojsonProperties', geojsonProperties); 1751 } 1752 break; 1753 1754 case 'MultiLineString': 1755 googleObj = []; 1756 bounds = new google.maps.LatLngBounds(); 1757 for (i = 0; i < geojsonGeometry.coordinates.length; i++) { 1758 path = []; 1759 for (j = 0; j < geojsonGeometry.coordinates[i].length; j++) { 1760 coord = geojsonGeometry.coordinates[i][j]; 1761 ll = new google.maps.LatLng(coord[1], coord[0]); 1762 bounds.extend(ll); 1763 path.push(ll); 1764 } 1765 opts.path = path; 1766 googleObj.push(new google.maps.Polyline(opts)); 1767 } 1768 if (geojsonProperties) { 1769 for (k = 0; k < googleObj.length; k++) { 1770 googleObj[k].set('geojsonProperties', geojsonProperties); 1771 } 1772 } 1773 for (k = 0; k < googleObj.length; k++) { 1774 googleObj[k].set('bounds', bounds); 1775 } 1776 break; 1777 1778 case 'Polygon': 1779 paths = []; 1780 bounds = new google.maps.LatLngBounds(); 1781 for (i = 0; i < geojsonGeometry.coordinates.length; i++) { 1782 path = []; 1783 for (j = 0; j < geojsonGeometry.coordinates[i].length; j++) { 1784 ll = new google.maps.LatLng(geojsonGeometry.coordinates[i][j][1], geojsonGeometry.coordinates[i][j][0]); 1785 bounds.extend(ll); 1786 path.push(ll); 1787 } 1788 paths.push(path); 1789 } 1790 opts.paths = paths; 1791 googleObj = new google.maps.Polygon(opts); 1792 googleObj.set('bounds', bounds); 1793 if (geojsonProperties) { 1794 googleObj.set('geojsonProperties', geojsonProperties); 1795 } 1796 break; 1797 1798 case 'MultiPolygon': 1799 googleObj = []; 1800 bounds = new google.maps.LatLngBounds(); 1801 for (i = 0; i < geojsonGeometry.coordinates.length; i++) { 1802 paths = []; 1803 for (j = 0; j < geojsonGeometry.coordinates[i].length; j++) { 1804 path = []; 1805 for (k = 0; k < geojsonGeometry.coordinates[i][j].length; k++) { 1806 ll = new google.maps.LatLng(geojsonGeometry.coordinates[i][j][k][1], geojsonGeometry.coordinates[i][j][k][0]); 1807 bounds.extend(ll); 1808 path.push(ll); 1809 } 1810 paths.push(path); 1811 } 1812 opts.paths = paths; 1813 googleObj.push(new google.maps.Polygon(opts)); 1814 } 1815 if (geojsonProperties) { 1816 for (k = 0; k < googleObj.length; k++) { 1817 googleObj[k].set('geojsonProperties', geojsonProperties); 1818 } 1819 } 1820 for (k = 0; k < googleObj.length; k++) { 1821 googleObj[k].set('bounds', bounds); 1822 } 1823 break; 1824 1825 case 'GeometryCollection': 1826 googleObj = []; 1827 if (!geojsonGeometry.geometries) { 1828 googleObj = _error('Invalid GeoJSON object: GeometryCollection object missing \'geometries\' member.'); 1829 } 1830 else { 1831 for (i = 0; i < geojsonGeometry.geometries.length; i++) { 1832 googleObj.push(_geometryToGoogleMaps(geojsonGeometry.geometries[i], opts, geojsonProperties || null)); 1833 } 1834 } 1835 break; 1836 1837 default: 1838 googleObj = _error('Invalid GeoJSON object: Geometry object must be one of \'Point\', \'LineString\', \'Polygon\' or \'MultiPolygon\'.'); 1839 } 1840 1841 return googleObj; 1842 1843 }; 1844 1845 let _error = function (message) { 1846 1847 return { 1848 type: 'Error', 1849 message: message 1850 }; 1851 1852 }; 1853 1854 let obj; 1855 1856 let opts = options || {}; 1857 1858 switch (geojson.type) { 1859 case 'FeatureCollection': 1860 if (!geojson.features) { 1861 obj = _error('Invalid GeoJSON object: FeatureCollection object missing \'features\' member.'); 1862 } 1863 else { 1864 obj = []; 1865 for (let y = 0; y < geojson.features.length; y++) { 1866 obj.push(_geometryToGoogleMaps(geojson.features[y].geometry, opts, geojson.features[y].properties)); 1867 } 1868 } 1869 break; 1870 1871 case 'GeometryCollection': 1872 if (!geojson.geometries) { 1873 obj = _error('Invalid GeoJSON object: GeometryCollection object missing \'geometries\' member.'); 1874 } 1875 else { 1876 obj = []; 1877 for (let z = 0; z < geojson.geometries.length; z++) { 1878 obj.push(_geometryToGoogleMaps(geojson.geometries[z], opts, geojson.geometries[z].properties)); 1879 } 1880 } 1881 break; 1882 1883 case 'Feature': 1884 if (!(geojson.properties && geojson.geometry)) { 1885 obj = _error('Invalid GeoJSON object: Feature object missing \'properties\' or \'geometry\' member.'); 1886 } 1887 else { 1888 obj = _geometryToGoogleMaps(geojson.geometry, opts, geojson.properties); 1889 } 1890 break; 1891 1892 case 'Point': case 'MultiPoint': case 'LineString': case 'MultiLineString': case 'Polygon': case 'MultiPolygon': 1893 obj = geojson.coordinates 1894 ? obj = _geometryToGoogleMaps(geojson, opts, geojson.properties) 1895 : _error('Invalid GeoJSON object: Geometry object missing \'coordinates\' member.'); 1896 break; 1897 1898 default: 1899 obj = _error('Invalid GeoJSON object: GeoJSON object must be one of \'Point\', \'LineString\', \'Polygon\', \'MultiPolygon\', \'Feature\', \'FeatureCollection\' or \'GeometryCollection\'.'); 1900 1901 } 1902 1903 return obj; 1904 }; 1905 1906})(jQuery, Drupal); 1907; 1908/** 1909 * @file 1910 * Javascript for the Geofield Google Map formatter. 1911 */ 1912 1913(function ($, Drupal) { 1914 1915 'use strict'; 1916 1917 /** 1918 * The Geofield Google Map formatter behavior. 1919 * 1920 * @type {Drupal~behavior} 1921 * 1922 * @prop {Drupal~behaviorAttach} attach 1923 * Attaches the Geofield Google Map formatter behavior. 1924 */ 1925 Drupal.behaviors.geofieldGoogleMap = { 1926 attach: function (context, drupalSettings) { 1927 1928 function loadMap(mapId) { 1929 // Check if the Map container really exists and hasn't been yet 1930 // initialized. 1931 if (drupalSettings['geofield_google_map'][mapId] && !Drupal.geoFieldMapFormatter.map_data[mapId]) { 1932 1933 let map_settings = drupalSettings['geofield_google_map'][mapId]['map_settings']; 1934 let data = drupalSettings['geofield_google_map'][mapId]['data']; 1935 1936 // Set the map_data[mapid] settings. 1937 Drupal.geoFieldMapFormatter.map_data[mapId] = map_settings; 1938 1939 // Load before the GMap Library, if needed. 1940 Drupal.geoFieldMapFormatter.loadGoogle(mapId, map_settings.gmap_api_key, map_settings.map_additional_libraries, function () { 1941 if (!document.getElementById(mapId)) { 1942 return; 1943 } 1944 Drupal.geoFieldMapFormatter.map_initialize(mapId, map_settings, data, context); 1945 }); 1946 } 1947 } 1948 1949 if (drupalSettings['geofield_google_map']) { 1950 1951 // If the IntersectionObserver API is available, create an observer to load the map when it enters the viewport 1952 // It will be used to handle map loading instead of displaying the map on page load. 1953 let mapObserver = null; 1954 if ('IntersectionObserver' in window){ 1955 mapObserver = new IntersectionObserver(function (entries, observer) { 1956 for(var i = 0; i < entries.length; i++) { 1957 if(entries[i].isIntersecting){ 1958 const mapId = entries[i].target.id; 1959 loadMap(mapId); 1960 } 1961 } 1962 }); 1963 } 1964 1965 $(context).find('.geofield-google-map').once('geofield-processed').each(function (index, element) { 1966 const mapId = $(element).attr('id'); 1967 if (drupalSettings['geofield_google_map'][mapId]) { 1968 const map_settings = drupalSettings['geofield_google_map'][mapId]['map_settings']; 1969 if (mapObserver && map_settings['map_lazy_load']['lazy_load']) { 1970 mapObserver.observe(element) 1971 } else { 1972 loadMap(mapId); 1973 } 1974 } 1975 }); 1976 } 1977 } 1978 }; 1979 1980 Drupal.geoFieldMapFormatter = { 1981 1982 map_start: { 1983 center: {lat: 41.85, lng: -87.65}, 1984 zoom: 18 1985 }, 1986 1987 map_data: {}, 1988 1989 // Google Maps are loaded lazily. In some situations load_google() is 1990 // called twice, which results in "You have included the Google Maps API 1991 // multiple times on this page. This may cause unexpected errors." errors. 1992 // This flag will prevent repeat $.getScript() calls. 1993 maps_api
1993_loading: false, 1994 1995 /** 1996 * Returns the re-coded google maps api language parameter, from html lang attribute. 1997 * 1998 * @param {string} html_language - The language id string 1999 * 2000 * @return {string} - The transformed language id string 2001 */ 2002 googleMapsLanguage: function (html_language) { 2003 switch (html_language) { 2004 case 'zh-hans': 2005 html_language = 'zh-CN'; 2006 break; 2007 2008 case 'zh-hant': 2009 html_language = 'zh-TW'; 2010 break; 2011 } 2012 return html_language; 2013 }, 2014 2015 /** 2016 * Provides the callback that is called when maps loads. 2017 */ 2018 googleCallback: function () { 2019 let self = this; 2020 // Wait until the window load event to try to use the maps library. 2021 $(document).ready(function (e) { 2022 _.each(self.googleCallbacks, function (callback) { 2023 callback.callback(); 2024 }); 2025 self.googleCallbacks = []; 2026 }); 2027 }, 2028 2029 /** 2030 * Adds a callback that will be called once the maps library is loaded. 2031 * 2032 * @param {string} callback - The callback 2033 */ 2034 addCallback: function (callback) { 2035 let self = this; 2036 // Ensure callbacks array. 2037 self.googleCallbacks = self.googleCallbacks || []; 2038 self.googleCallbacks.push({callback: callback}); 2039 }, 2040 2041 /** 2042 * Load Google Maps library. 2043 * 2044 * @param {string} mapid - The map id 2045 * @param {string} gmap_api_key - The gmap api key 2046 * @param {array} additional_libraries - Additional Libraries list 2047 * @param {string} callback - the Callback function name 2048 */ 2049 loadGoogle: function (mapid, gmap_api_key, additional_libraries, callback) { 2050 let self = this; 2051 let html_language = $('html').attr('lang') || 'en'; 2052 2053 // Add the callback. 2054 self.addCallback(callback); 2055 2056 // Check for google maps. 2057 if (typeof google === 'undefined' || typeof google.maps === 'undefined') { 2058 if (self.maps_api_loading === true) { 2059 return; 2060 } 2061 2062 self.maps_api_loading = true; 2063 // Google maps isn't loaded so lazy load google maps. 2064 // Default script path. 2065 let scriptPath = self.map_data[mapid]['gmap_api_localization'] + '?v=3.exp&sensor=false&language=' + self.googleMapsLanguage(html_language); 2066 2067 // If a Google API key is set, use it. 2068 if (gmap_api_key) { 2069 scriptPath += '&key=' + gmap_api_key; 2070 } 2071 2072 if (additional_libraries) { 2073 let libraries = []; 2074 for (let library in additional_libraries) { 2075 if (additional_libraries.hasOwnProperty(library)) { 2076 libraries.push(library); 2077 } 2078 } 2079 scriptPath += '&libraries=' + libraries.join(); 2080 } 2081 2082 $.getScript(scriptPath) 2083 .done(function () { 2084 self.maps_api_loading = false; 2085 self.googleCallback(); 2086 }); 2087 2088 } 2089 else { 2090 // Google maps loaded. Run callback. 2091 self.googleCallback(); 2092 } 2093 }, 2094 2095 checkImage: function (imageSrc, setIcon, logError) { 2096 let img = new Image(); 2097 img.src = imageSrc; 2098 img.onload = setIcon; 2099 img.onerror = logError; 2100 }, 2101 2102 place_feature: function (feature, mapid) { 2103 let self = this; 2104 let icon_image = null; 2105 2106 // Override and set icon image with geojsonProperties.icon, if set as not 2107 // null/empty. 2108 if (feature.geojsonProperties.icon && feature.geojsonProperties.icon.length > 0) { 2109 icon_image = feature.geojsonProperties.icon; 2110 } 2111 2112 // Define the OverlappingMarkerSpiderfier flag. 2113 let oms = self.map_data[mapid].oms ? self.map_data[mapid].oms : null; 2114 2115 // Set the personalized Icon Image, if set. 2116 if (feature.setIcon && icon_image && icon_image.length > 0) { 2117 self.checkImage(icon_image, 2118 // Success loading image. 2119 function () { 2120 feature.setIcon(icon_image); 2121 }); 2122 } 2123 2124 let map = self.map_data[mapid].map; 2125 2126 // Add a default Tooltip on the title geojsonProperty, if existing. 2127 if (feature.setTitle && feature.geojsonProperties.tooltip) { 2128 feature.setTitle(feature.geojsonProperties.tooltip); 2129 } 2130 2131 // If the feature is a Point, make it a Marker and extend the Map bounds. 2132 if (feature.getPosition) { 2133 if (oms) { 2134 self.map_data[mapid].oms.addMarker(feature); 2135 } 2136 else {
2137 feature.setMap(map); 2138 } 2139 2140 // Generate the markers object index based on entity id (and geofield 2141 // cardinality), and add the marker to the markers object. 2142 let entity_id = feature['geojsonProperties']['entity_id']; 2143 if (self.map_data[mapid].geofield_cardinality && self.map_data[mapid].geofield_cardinality !== 1) { 2144 let i = 0; 2145 while (self.map_data[mapid].markers[entity_id + '-' + i]) { 2146 i++; 2147 } 2148 self.map_data[mapid].markers[entity_id + '-' + i] = feature; 2149 } 2150 else { 2151 self.map_data[mapid].markers[entity_id] = feature; 2152 } 2153 2154 self.map_data[mapid].map_bounds.extend(feature.getPosition()); 2155 2156 // Check for eventual simple or OverlappingMarkerSpiderfier click 2157 // Listener. 2158 let clickListener = oms ? 'spider_click' : 'click'; 2159 google.maps.event.addListener(feature, clickListener, function () { 2160 self.infowindow_open(mapid, feature); 2161 }); 2162 } 2163 2164 // If the feature is a Polyline or a Polygon, add to the Map and extend 2165 // the Map bounds. 2166 if (feature.getPath) { 2167 let feature_options = feature.geojsonProperties.path_options ? JSON.parse(feature.geojsonProperties.path_options) : {}; 2168 feature.setOptions(feature_options); 2169 feature.setMap(map); 2170 let path = feature.getPath(); 2171 path.forEach(function (element) { 2172 self.map_data[mapid].map_bounds.extend(element); 2173 }); 2174 google.maps.event.addListener(feature, 'click', function (event) { 2175 self.infowindow_open(mapid, feature, event.latLng); 2176 }); 2177 } 2178 2179 }, 2180 2181 // Closes and open the Map Infowindow at the input feature. 2182 infowindow_open: function (mapid, feature, anchor) { 2183 let self = this; 2184 let map = self.map_data[mapid].map; 2185 let properties = feature.get('geojsonProperties'); 2186 if (feature.setTitle && properties && properties.title) { 2187 feature.setTitle(properties.title); 2188 } 2189 map.infowindow.close(); 2190 if (properties.description) { 2191 map.infowindow.setContent(properties.description); 2192 2193 // Note: if the feature is a Marker (and not a Polyline/Polygon) its 2194 // extensions will override the infowindow anchor position, in the map. 2195 // infowindow.open method. 2196 map.infowindow.setPosition(anchor); 2197 setTimeout(function () { 2198 map.infowindow.open(map, feature); 2199 }, 200); 2200 } 2201 }, 2202 2203 map_refresh: function (mapid) { 2204 let self = this; 2205 setTimeout(function () { 2206 google.maps.event.trigger(self.map_data[mapid].map, 'resize'); 2207 }, 10); 2208 }, 2209 2210 // Init Geofield Google Map and its functions. 2211 map_initialize: function (mapid, map_settings, data, context) { 2212 let self = this; 2213 $.noConflict(); 2214 2215 // If google and google.maps have been defined. 2216 if (google && google.maps) { 2217 let styledMapType; 2218 2219 let mapOptions = { 2220 center: map_settings.map_center ? new google.maps.LatLng(map_settings.map_center.lat, map_settings.map_center.lon) : new google.maps.LatLng(42, 12.5), 2221 zoom: map_settings.map_zoom_and_pan.zoom.initial ? parseInt(map_settings.map_zoom_and_pan.zoom.initial) : 8, 2222 minZoom: map_settings.map_zoom_and_pan.zoom.min ? parseInt(map_settings.map_zoom_and_pan.zoom.min) : 1, 2223 maxZoom: map_settings.map_zoom_and_pan.zoom.max ? parseInt(map_settings.map_zoom_and_pan.zoom.max) : 20, 2224 gestureHandling: map_settings.map_zoom_and_pan.gestureHandling || 'auto', 2225 mapTypeId: map_settings.map_controls.map_type_id || 'roadmap' 2226 }; 2227 2228 // Manage the old scrollwheel & draggable settings (deprecated by google maps api). 2229 if (!map_settings.map_zoom_and_pan.scrollwheel && !map_settings.map_zoom_and_pan.gestureHandling) { 2230 mapOptions.scrollwheel = false; 2231 } 2232 if (!map_settings.map_zoom_and_pan.draggable && !map_settings.map_zoom_and_pan.gestureHandling) { 2233 mapOptions.draggable = false; 2234 } 2235 2236 if (map_settings.map_controls.disable_default_ui) { 2237 mapOptions.disableDefaultUI = map_settings.map_controls.disable_default_ui; 2238 } 2239 else { 2240 // Implement Custom Style Map, if Set. 2241 if (map_settings.custom_style_map && map_settings.custom_style_map.custom_style_control && map_settings.custom_style_map.custom_style_name.length > 0 && map_settings.custom_style_map.custom_style_options.length > 0) { 2242 let customMapStyleName = map_settings.custom_style_map.custom_style_name; 2243 let customMapStyle = JSON.parse(map_settings.custom_style_map.custom_style_options); 2244 styledMapType = new google.maps.StyledMapType(customMapStyle, {name: customMapStyleName}); 2245 map_settings.map_controls.map_type_control_options_type_ids.push('custom_styled_map'); 2246 } 2247 2248 mapOptions.zoomControl = !!map_settings.map_controls.zoom_control; 2249 mapOptions.mapTypeControl = !!map_settings.map_controls.map_type_control; 2250 mapOptions.mapTypeControlOptions = { 2251 mapTypeIds: map_settings.map_controls.map_type_control_options_type_ids ? map_settings.map_controls.map_type_control_options_type_ids : ['roadmap', 'satellite', 'hybrid'], 2252 position: google.maps.ControlPosition.TOP_LEFT 2253 }; 2254 mapOptions.scaleControl = !!map_settings.map_controls.scale_control; 2255 mapOptions.streetViewControl = !!map_settings.map_controls.street_view_control; 2256 mapOptions.fullscreenControl = !!map_settings.map_controls.fullscreen_control; 2257 } 2258 2259 // Add map_additional_options if any. 2260 if (map_settings.map_additional_options.length > 0) { 2261 let additionalOptions = JSON.parse(map_settings.map_additional_options); 2262 // Transforms additionalOptions "true", "false" values into true & 2263 // false. 2264 for (let prop in additionalOptions) { 2265 if (additionalOptions.hasOwnProperty(prop)) { 2266 if (additionalOptions[prop] === 'true') { 2267 additionalOptions[prop] = true; 2268 } 2269 if (additionalOptions[prop] === 'false') { 2270 additionalOptions[prop] = false;
2271 } 2272 } 2273 } 2274 // Merge mapOptions with additionalOptions. 2275 $.extend(mapOptions, additionalOptions); 2276 } 2277 2278 // Define the Geofield Google Map. 2279 let map = new google.maps.Map(document.getElementById(mapid), mapOptions); 2280 2281 // Add the Map Reset Control, if set. 2282 if (map_settings.map_zoom_and_pan.map_reset) { 2283 let mapResetControlPosition = map_settings.map_zoom_and_pan.map_reset_position || 'TOP_RIGHT'; 2284 2285 // Create the DIV to hold the control and call the mapResetControl() 2286 // constructor passing in this DIV. 2287 let mapResetControlDiv = document.createElement('div'); 2288 mapResetControlDiv.style.zIndex = "10"; 2289 mapResetControlDiv.index = 1; 2290 new self.map_reset_control(mapResetControlDiv, mapid); 2291 map.controls[google.maps.ControlPosition[mapResetControlPosition]].push(mapResetControlDiv); 2292 } 2293 2294 if (Drupal.geoFieldMapGeocoder && map_settings.map_geocoder.control) { 2295 let mapGeocoderControlPosition = map_settings.map_geocoder.settings.position || 'TOP_RIGHT'; 2296 let mapGeocoderControlDiv = document.createElement('div'); 2297 Drupal.geoFieldMapFormatter.map_data[mapid].geocoder_control = new Drupal.geoFieldMapGeocoder.map_control(mapGeocoderControlDiv, mapid); 2298 mapGeocoderControlDiv.index = 1; 2299 map.controls[google.maps.ControlPosition[mapGeocoderControlPosition]].push(Drupal.geoFieldMapFormatter.map_data[mapid].geocoder_control); 2300 Drupal.geoFieldMapGeocoder.map_control_autocomplete(mapid, map_settings.map_geocoder.settings, $(Drupal.geoFieldMapFormatter.map_data[mapid].geocoder_control), 'formatter', 'gmap'); 2301 } 2302 2303 // If defined a Custom Map Style, associate the styled map with 2304 // custom_styled_map MapTypeId and set it to display. 2305 if (styledMapType) { 2306 map.mapTypes.set('custom_styled_map', styledMapType); 2307 // Set Custom Map Style to Default, if requested. 2308 if (map_settings.custom_style_map && map_settings.custom_style_map.custom_style_default) { 2309 map.setMapTypeId('custom_styled_map'); 2310 } 2311 } 2312 2313 // Define a mapid self property, so other code can interact with it. 2314 self.map_data[mapid].map = map; 2315 self.map_data[mapid].map_options = mapOptions; 2316 self.map_data[mapid].features = data.features; 2317 self.map_data[mapid].markers = {}; 2318 2319 // Define the MapBounds property. 2320 self.map_data[mapid].map_bounds = new google.maps.LatLngBounds(); 2321 2322 // Set the zoom force and center property for the map. 2323 self.map_data[mapid].zoom_force = !!map_settings.map_zoom_and_pan.zoom.force; 2324 self.map_data[mapid].center_force = !!map_settings.map_center.center_force; 2325 2326 // Parse the Geojson data into Google Maps Locations. 2327 let features = data.features && data.features.length > 0 ? Drupal.googleGeoJson(data) : null; 2328 2329 if (features && features.length > 0 && (!features.type || features.type !== 'Error')) { 2330 2331 /** 2332 * Implement OverlappingMarkerSpiderfier if its control set true. 2333 */ 2334 if (map_settings.map_oms && map_settings.map_oms.map_oms_control && OverlappingMarkerSpiderfier) { 2335 let omsOptions = map_settings.map_oms.map_oms_options.length > 0 ? JSON.parse(map_settings.map_oms.map_oms_options) : { 2336 markersWontMove: true, 2337 markersWontHide: true, 2338 basicFormatEvents: true, 2339 keepSpiderfied: true 2340 }; 2341 self.map_data[mapid].oms = new OverlappingMarkerSpiderfier(map, omsOptions); 2342 } 2343 2344 map.infowindow = new google.maps.InfoWindow({ 2345 content: '' 2346 }); 2347 2348 // If the map.infowindow is defined, add an event listener for the 2349 // Ajax Infowindow Popup. 2350 google.maps.event.addListener(map.infowindow, 'domready', function () { 2351 let element = document.createElement('div'); 2352 element.innerHTML = map.infowindow.getContent().trim(); 2353 let content = $('[data-geofield-google-map-ajax-popup]', element); 2354 if (content.length) { 2355 let url = content.data('geofield-google-map-ajax-popup'); 2356 Drupal.ajax({url: url}).execute(); 2357 } 2358 // Attach drupal behaviors on new content. 2359 $(element).each(function () { 2360 Drupal.attachBehaviors(this, drupalSettings); 2361 }) 2362 }); 2363 2364 if (features.setMap) { 2365 self.place_feature(features, mapid); 2366 } 2367 else { 2368 for (let i in features) { 2369 if (features[i].setMap) { 2370 self.place_feature(features[i], mapid); 2371 } 2372 else { 2373 for (let j in features[i]) { 2374 if (features[i][j].setMap) { 2375 self.place_feature(features[i][j], mapid); 2376 } 2377 } 2378 } 2379 } 2380 } 2381 2382 // Implement Markeclustering, if more than 1 marker on the map, 2383 // and the markercluster option is set to true. 2384 if (typeof MarkerClusterer !== 'undefined' && map_settings.map_markercluster.markercluster_control) { 2385 2386 let markeclusterOption = { 2387 imagePath: 'https://developers.google.com/maps/documentation/javascript/examples/markerclusterer/m' 2388 }; 2389 2390 // Add markercluster_additional_options if any. 2391 if (map_settings.map_markercluster.markercluster_additional_options.length > 0) { 2392 let markeclusterAdditionalOptions = JSON.parse(map_settings.map_markercluster.markercluster_additional_options); 2393 // Merge markeclusterOption with markeclusterAdditionalOptions. 2394 $.extend(markeclusterOption, markeclusterAdditionalOptions); 2395 } 2396
2397 // Define a markerCluster property, so other code can interact with 2398 // it. 2399 let markerCluster = []; 2400 let keys = Object.keys(self.map_data[mapid].markers); 2401 for (let k = 0; k < keys.length; k++) { 2402 markerCluster.push(self.map_data[mapid].markers[keys[k]]); 2403 } 2404 self.map_data[mapid].markerCluster = new MarkerClusterer(map, markerCluster, markeclusterOption); 2405 } 2406 2407 // If the Map Initial State is defined by MapBounds, 2408 // and the map center is not forced. 2409 if (!self.mapBoundsAreNull(self.map_data[mapid].map_bounds) && !self.map_data[mapid].center_force) { 2410 map.fitBounds(self.map_data[mapid].map_bounds); 2411 } 2412 // Else if the Map Initial State is defined by Markers in the exact 2413 // same locations,. 2414 else if (self.map_data[mapid].markers.constructor === Object && 2415 self.mapBoundsAreNull(self.map_data[mapid].map_bounds) && 2416 // And the map center is not forced. 2417 !self.map_data[mapid].center_force) { 2418 map.setCenter(self.map_data[mapid].markers[Object.keys(self.map_data[mapid].markers)[0]].getPosition()); 2419 } 2420 2421 } 2422 2423 google.maps.event.addListenerOnce(map, 'bounds_changed', function () { 2424 // Force the Map Zoom if requested. 2425 if (self.map_data[mapid].zoom_force) { 2426 self.map_data[mapid].map.setZoom(self.map_data[mapid].map_options.zoom); 2427 } 2428 }); 2429 2430 // At the beginning (once) ... 2431 google.maps.event.addListenerOnce(map, 'idle', function () { 2432 2433 // Open the Feature infowindow, if so set. 2434 if (self.map_data[mapid].map_marker_and_infowindow.force_open && parseInt(self.map_data[mapid].map_marker_and_infowindow.force_open) === 1) { 2435 // map.setCenter(features[0].getPosition()); 2436 self.infowindow_open(mapid, features[0]); 2437 } 2438 2439 // In case of map initial position not forced, and zooFiner not 2440 // null/neutral, adapt the Map Zoom and the Start Zoom accordingly. 2441 if (!self.map_data[mapid].zoom_force && self.map_data[mapid].map_zoom_and_pan.zoom.finer && self.map_data[mapid].map_zoom_and_pan.zoom.finer !== 0) { 2442 map.setOptions({ 2443 zoom: map.getZoom() + parseInt(self.map_data[mapid].map_zoom_and_pan.zoom.finer) 2444 }); 2445 } 2446 2447 // Update map initial state after everything is settled. 2448 self.map_set_start_state(mapid, map.getCenter(), map.getZoom()); 2449 2450 // Trigger a custom event on Geofield Map initialized, with mapid. 2451 $(context).trigger('geofieldMapInit', mapid); 2452 }); 2453 2454 } 2455 }, 2456 2457 mapBoundsAreNull: function (mapBounds) { 2458 let north_east = mapBounds.getNorthEast();
2459 let south_west = mapBounds.getSouthWest(); 2460 return north_east.toString() === south_west.toString(); 2461 2462 }, 2463 2464 map_set_start_state: function (mapid, center, zoom) { 2465 let self = this; 2466 self.map_data[mapid].map_start_center = center; 2467 self.map_data[mapid].map_start_zoom = zoom; 2468 }, 2469 2470 map_reset_control: function (controlDiv, mapid) { 2471 // Set CSS for the control border. 2472 let controlUI = document.createElement('div'); 2473 controlUI.style.backgroundColor = '#fff'; 2474 controlUI.style.boxShadow = 'rgba(0,0,0,.3) 0px 1px 4px -1px'; 2475 controlUI.style.cursor = 'pointer'; 2476 controlUI.title = Drupal.t('Click to reset the map to its initial state'); 2477 controlUI.style.margin = '10px'; 2478 controlUI.style.position = 'relative'; 2479 controlUI.id = 'geofield-map--' + mapid + '--reset-control'; 2480 controlDiv.appendChild(controlUI); 2481 2482 // Set CSS for the control interior. 2483 let controlText = document.createElement('div'); 2484 controlText.style.position = 'relative'; 2485 controlText.innerHTML = Drupal.t('Reset Map'); 2486 controlText.style.padding = '0 17px'; 2487 controlText.style.display = 'table-cell'; 2488 controlText.style.height = '40px'; 2489 controlText.style.fontSize = '18px'; 2490 controlText.style.color = 'rgb(86,86,86)'; 2491 controlText.style.textAlign = 'center'; 2492 controlText.style.verticalAlign = 'middle'; 2493 controlUI.appendChild(controlText); 2494 2495 // Setup the click event listeners: simply set the map to Chicago. 2496 controlUI.addEventListener('click', function () { 2497 Drupal.geoFieldMapFormatter.map_data[mapid].map.setCenter(Drupal.geoFieldMapFormatter.map_data[mapid].map_start_center); 2498 Drupal.geoFieldMapFormatter.map_data[mapid].map.setZoom(Drupal.geoFieldMapFormatter.map_data[mapid].map_start_zoom); 2499 }); 2500 return controlUI; 2501 } 2502 2503 }; 2504 2505})(jQuery, Drupal); 2506;
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.