1+function(i,t,e){"use strict";var s={size:{min:.1,max:10,default:1},shrink:{min:.2,max:1,default:.8},regrow:{min:.2,max:1,default:.8},minRows:{min:1,max:99,default:1},maxRows:{min:1,max:99,default:2},prefixReserve:{min:0,max:.8,default:.3},prefixHide:{min:.1,max:10,default:2},alignRight:{type:"boolean",default:!1}},n=function(){var i=t.fn.jquery.split(".");return 1==i[0]&&i[1]<8}();i.rrssbConfigAll=function(i){t(".rrssb").each(function(){t(this).rrssbConfig(i)})},t.fn.rrssbConfig=function(i){if(!t(this).data("settings")||i){var e={};for(var n in s)e[n]=s[n].default,i&&("boolean"==s[n].type?e[n]=Boolean(i[n]):isNaN(parseFloat(i[n]))||(e[n]=Math.min(s[n].max,Math.max(s[n].min,i[n]))));t(this).data("settings",e),h.call(this)}};var r,a=function(){t(".rrssb").each(h)},h=function(){var i=t(this).data("orig");i||(i=function(){var i=t(".rrssb-prefix",this),e=i.length?i.clone().css({visibility:"hidden","white-space":"nowrap",display:"inline"}).appendTo(this):null;t("ul").contents().filter(function(){return this.nodeType===Node.TEXT_NODE}).remove();var s={width:0,buttons:0,height:t("li",this).innerHeight(),fontSize:parseFloat(t(this).css("font-size")),prefixWidth:e?e.innerWidth():0};return t("li",this).each(function(){s.width=Math.max(s.width,t(this).innerWidth()),s.buttons++}),t(this).data("orig",s),e&&e.remove(),s}.call(this));var e=t(this).data("settings"),s=i.width*e.size,r=i.buttons,a=t(this).innerWidth()-1,h=a<s*e.shrink?"":s;n?t("li",this).width(h):t("li",this).innerWidth(h);var o=a/e.shrink,l=i.prefixWidth*e.size;l>o*e.prefixHide?(l=0,t(".rrssb-prefix",this).css("display","none")):t(".rrssb-prefix",this).css("display","");var c=l<=a*e.prefixReserve?o-l:o,f=Math.floor(c/s);f*e.maxRows<r?(t(this).addClass("no-label"),s=i.height*e.size,f=Math.max(1,Math.floor(c/s))):t(this).removeClass("no-label");var d=e.minRows>1?Math.max(1,Math.ceil(r/(e.minRows-1))-1):r;f=Math.min(f,d),f=Math.ceil(r/Math.ceil(r/f)),d=Math.ceil(r/Math.ceil(r/d));var u=Math.floor(1e4/f)/100;t("li",this).css("max-width",u+"%");var m=s*f+l;m>o&&(m-=l,l=0);var p=f<d?e.regrow:1,g=Math.min(p,a/m),b=i.fontSize*e.size*g*.98;if(t(this).css("font-size",b+"px"),l){var x=Math.floor(1e4*l/m)/100;t(".rrssb-buttons",this).css("padding-left",x+"%"),t(".rrssb-prefix",this).css("position","absolute");var v=Math.ceil(r/f)*i.height/i.fontSize;t(".rrssb-prefix",this).css("line-height",v+"em")}else t(".rrssb-buttons",this).css("padding-left",""),t(".rrssb-prefix",this).css("position",""),t(".rrssb-prefix",this).css("line-height","");var w=e.alignRight?"padding-left":"padding-right";if(g>=.999*p){var M=Math.floor(1e4*(a-m*g*1.02)/a)/100;t(this).css(w,M+"%")}else t(this).css(w,"")},o=function(i){r&&clearTimeout(r),r=setTimeout(a,i||200)};t(document).ready(function(){t(".rrssb-buttons a.popup").click(function(e){!function(t,e,s,n){var r=void 0!==i.screenLeft?i.screenLeft:screen.left,a=void 0!==i.screenTop?i.screenTop:screen.top,h=(i.innerWidth?i.innerWidth:document.documentElement.clientWidth?document.documentElement.clientWidth:screen.width)/2-s/2+r,o=(i.innerHeight?i.innerHeight:document.documentElement.clientHeight?document.documentElement.clientHeight:screen.height)/3-n/3+a,l=i.open(t,e,"scrollbars=yes, width="+s+", height="+n+", top="+o+", left="+h);l&&l.focus&&l.focus()}(t(this).attr("href"),t(this).find(".rrssb-text").html(),580,470),e.preventDefault()}),t(i).resize(o),t(document).ready(function(){rrssbConfigAll()})})}(window,jQuery);; 2/** 3 * @file 4 * The RRSSB Drupal Behavior to configure settings. 5 */ 6 7(function ($) { 8 'use strict'; 9 10 Drupal.behaviors.rrssb = { 11 attach: function (context, settings) { 12 for (var buttonSet in settings.rrssb) { 13 $('.rrssb-bs-' + buttonSet).each(function(){ 14 $(this).rrssbConfig(settings.rrssb[buttonSet]); 15 }); 16 } 17 } 18 }; 19})(jQuery); 20; 21/** 22* DO NOT EDIT THIS FILE. 23* See the following change record for more information, 24* https://www.drupal.org/node/2815083 25* @preserve 26**/ 27 28Drupal.debounce = function (func, wait, immediate) { 29 var timeout = void 0; 30 var result = void 0; 31 return function () { 32 for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { 33 args[_key] = arguments[_key]; 34 } 35 36 var context = this; 37 var later = function later() { 38 timeout = null; 39 if (!immediate) {
40 result = func.apply(context, args); 41 } 42 }; 43 var callNow = immediate && !timeout; 44 clearTimeout(timeout); 45 timeout = setTimeout(later, wait); 46 if (callNow) { 47 result = func.apply(context, args); 48 } 49 return result; 50 }; 51};; 52/** 53* DO NOT EDIT THIS FILE. 54* See the following change record for more information, 55* https://www.drupal.org/node/2815083 56* @preserve 57**/ 58 59(function ($, Drupal, debounce) { 60 $.fn.drupalGetSummary = function () { 61 var callback = this.data('summaryCallback'); 62 return this[0] && callback ? $.trim(callback(this[0])) : ''; 63 }; 64 65 $.fn.drupalSetSummary = function (callback) { 66 var self = this; 67 68 if (typeof callback !== 'function') { 69 var val = callback; 70 callback = function callback() { 71 return val; 72 }; 73 } 74 75 return this.data('summaryCallback', callback).off('formUpdated.summary').on('formUpdated.summary', function () { 76 self.trigger('summaryUpdated'); 77 }).trigger('summaryUpdated'); 78 }; 79 80 Drupal.behaviors.formSingleSubmit = { 81 attach: function attach() { 82 function onFormSubmit(e) { 83 var $form = $(e.currentTarget); 84 var formValues = $form.serialize(); 85 var previousValues = $form.attr('data-drupal-form-submit-last'); 86 if (previousValues === formValues) { 87 e.preventDefault(); 88 } else { 89 $form.attr('data-drupal-form-submit-last', formValues); 90 } 91 } 92 93 $('body').once('form-single-submit').on('submit.singleSubmit', 'form:not([method~="GET"])', onFormSubmit); 94 } 95 }; 96 97 function triggerFormUpdated(element) { 98 $(element).trigger('formUpdated'); 99 } 100 101 function fieldsList(form) { 102 var $fieldList = $(form).find('[name]').map(function (index, element) { 103 return element.getAttribute('id'); 104 }); 105 106 return $.makeArray($fieldList); 107 } 108 109 Drupal.behaviors.formUpdated = { 110 attach: function attach(context) { 111 var $context = $(context); 112 var contextIsForm = $context.is('form'); 113 var $forms = (contextIsForm ? $context : $context.find('form')).once('form-updated'); 114 var formFields = void 0; 115 116 if ($forms.length) { 117 $.makeArray($forms).forEach(function (form) { 118 var events = 'change.formUpdated input.formUpdated '; 119 var eventHandler = debounce(function (event) { 120 triggerFormUpdated(event.target); 121 }, 300); 122 formFields = fieldsList(form).join(','); 123 124 form.setAttribute('data-drupal-form-fields', formFields); 125 $(form).on(events, eventHandler); 126 }); 127 } 128 129 if (contextIsForm) { 130 formFields = fieldsList(context).join(','); 131 132 var currentFields = $(context).attr('data-drupal-form-fields'); 133 134 if (formFields !== currentFields) { 135 triggerFormUpdated(context); 136 } 137 } 138 }, 139 detach: function detach(context, settings, trigger) { 140 var $context = $(context); 141 var contextIsForm = $context.is('form'); 142 if (trigger === 'unload') { 143 var $forms = (contextIsForm ? $context : $context.find('form')).removeOnce('form-updated'); 144 if ($forms.length) { 145 $.makeArray($forms).forEach(function (form) { 146 form.removeAttribute('data-drupal-form-fields'); 147 $(form).off('.formUpdated'); 148 }); 149 } 150 } 151 } 152 }; 153 154 Drupal.behaviors.fillUserInfoFromBrowser = { 155 attach: function attach(context, settings) { 156 var userInfo = ['name', 'mail', 'homepage']; 157 var $forms = $('[data-user-info-from-browser]').once('user-info-from-browser'); 158 if ($forms.length) { 159 userInfo.forEach(function (info) { 160 var $element = $forms.find('[name=' + info + ']'); 161 var browserData = localStorage.getItem('Drupal.visitor.' + info); 162 var emptyOrDefault = $element.val() === '' || $element.attr('data-drupal-default-value') === $element.val(); 163 if ($element.length && emptyOrDefault && browserData) { 164 $element.val(browserData); 165 } 166 }); 167 } 168 $forms.on('submit', function () { 169 userInfo.forEach(function (info) { 170 var $element = $forms.find('[name=' + info + ']'); 171 if ($element.length) { 172 localStorage.setItem('Drupal.visitor.' + info, $element.val()); 173 } 174 }); 175 }); 176 } 177 }; 178 179 var handleFragmentLinkClickOrHashChange = function handleFragmentLinkClickOrHashChange(e) { 180 var url = void 0; 181 if (e.type === 'click') { 182 url = e.currentTarget.location ? e.currentTarget.location : e.currentTarget; 183 } else { 184 url = window.location; 185 } 186 var hash = url.hash.substr(1); 187 if (hash) { 188 var $target = $('#' + hash); 189 $('body').trigger('formFragmentLinkClickOrHashChange', [$target]); 190 191 setTimeout(function () { 192 return $target.trigger('focus'); 193 }, 300); 194 } 195 }; 196 197 var debouncedHandleFragmentLinkClickOrHashChange = debounce(handleFragmentLinkClickOrHashChange, 300, true); 198 199 $(window).on('hashchange.form-fragment', debouncedHandleFragmentLinkClickOrHashChange); 200 201 $(document).on('click.form-fragment', 'a[href*="#"]', debouncedHandleFragmentLinkClickOrHashChange); 202})(jQuery, Drupal, Drupal.debounce);; 203/** 204 * @file 205 * Webform behaviors. 206 */ 207 208(function ($, Drupal) { 209 210 'use strict'; 211 212 // Trigger Drupal's attaching of behaviors after the page is 213 // completely loaded. 214 // @see https://stackoverflow.com/questions/37838430/detect-if-page-is-load-from-back-button 215 // @see https://stackoverflow.com/questions/20899274/how-to-refresh-page-on-back-button-click/20899422#20899422 216 var isChrome = (/chrom(e|ium)/.test(window.navigator.userAgent.toLowerCase())); 217 if (isChrome) { 218 // Track back button in navigation. 219 // @see https://stackoverflow.com/questions/37838430/detect-if-page-is-load-from-back-button 220 var backButton = false;
221 if (window.performance) { 222 var navEntries = window.performance.getEntriesByType('navigation'); 223 if (navEntries.length > 0 && navEntries[0].type === 'back_forward') { 224 backButton = true; 225 } 226 else if (window.performance.navigation 227 && window.performance.navigation.type === window.performance.navigation.TYPE_BACK_FORWARD) { 228 backButton = true; 229 } 230 } 231 232 // If the back button is pressed, delay Drupal's attaching of behaviors. 233 if (backButton) { 234 var attachBehaviors = Drupal.attachBehaviors; 235 Drupal.attachBehaviors = function (context, settings) { 236 setTimeout(function (context, settings) { 237 attachBehaviors(context, settings); 238 }, 300); 239 }; 240 } 241 } 242 243})(jQuery, Drupal); 244; 245/** 246* DO NOT EDIT THIS FILE. 247* See the following change record for more information, 248* https://www.drupal.org/node/2815083 249* @preserve 250**/ 251 252(function ($, Drupal) { 253 var states = { 254 postponed: [] 255 }; 256 257 Drupal.states = states; 258 259 function invert(a, invertState) { 260 return invertState && typeof a !== 'undefined' ? !a : a; 261 } 262 263 function _compare2(a, b) { 264 if (a === b) { 265 return typeof a === 'undefined' ? a : true; 266 } 267 268 return typeof a === 'undefined' || typeof b === 'undefined'; 269 } 270 271 function ternary(a, b) { 272 if (typeof a === 'undefined') { 273 return b; 274 } 275 if (typeof b === 'undefined') { 276 return a; 277 } 278 279 return a && b; 280 } 281 282 Drupal.behaviors.states = { 283 attach: function attach(context, settings) { 284 var $states = $(context).find('[data-drupal-states]'); 285 var il = $states.length; 286 287 var _loop = function _loop(i) { 288 var config = JSON.parse($states[i].getAttribute('data-drupal-states')); 289 Object.keys(config || {}).forEach(function (state) { 290 new states.Dependent({ 291 element: $($states[i]), 292 state: states.State.sanitize(state), 293 constraints: config[state] 294 }); 295 }); 296 }; 297 298 for (var i = 0; i < il; i++) { 299 _loop(i); 300 } 301 302 while (states.postponed.length) { 303 states.postponed.shift()(); 304 } 305 } 306 }; 307 308 states.Dependent = function (args) { 309 var _this = this; 310 311 $.extend(this, { values: {}, oldValue: null }, args); 312 313 this.dependees = this.getDependees(); 314 Object.keys(this.dependees || {}).forEach(function (selector) { 315 _this.initializeDependee(selector, _this.dependees[selector]); 316 }); 317 }; 318 319 states.Dependent.comparisons = { 320 RegExp: function RegExp(reference, value) { 321 return reference.test(value); 322 }, 323 Function: function Function(reference, value) { 324 return reference(value); 325 }, 326 Number: function Number(reference, value) { 327 return typeof value === 'string' ? _compare2(reference.toString(), value) : _compare2(reference, value); 328 } 329 }; 330 331 states.Dependent.prototype = { 332 initializeDependee: function initializeDependee(selector, dependeeStates) { 333 var _this2 = this; 334 335 this.values[selector] = {}; 336 337 Object.keys(dependeeStates).forEach(function (i) { 338 var state = dependeeStates[i]; 339 340 if ($.inArray(state, dependeeStates) === -1) { 341 return; 342 } 343 344 state = states.State.sanitize(state); 345 346 _this2.values[selector][state.name] = null; 347 348 $(selector).on('state:' + state, { selector: selector, state: state }, function (e) { 349 _this2.update(e.data.selector, e.data.state, e.value); 350 }); 351 352 new states.Trigger({ selector: selector, state: state }); 353 }); 354 }, 355 compare: function compare(reference, selector, state) { 356 var value = this.values[selector][state.name]; 357 if (reference.constructor.name in states.Dependent.comparisons) { 358 return states.Dependent.comparisons[reference.constructor.name](reference, value); 359 } 360 361 return _compare2(reference, value); 362 }, 363 update: function update(selector, state, value) { 364 if (value !== this.values[selector][state.name]) { 365 this.values[selector][state.name] = value; 366 this.reevaluate(); 367 } 368 }, 369 reevaluate: function reevaluate() { 370 var value = this.verifyConstraints(this.constraints); 371 372 if (value !== this.oldValue) { 373 this.oldValue = value; 374 375 value = invert(value, this.state.invert); 376 377 this.element.trigger({ 378 type: 'state:' + this.state, 379 value: value, 380 trigger: true 381 }); 382 } 383 }, 384 verifyConstraints: function verifyConstraints(constraints, selector) { 385 var result = void 0; 386 if ($.isArray(constraints)) { 387 var hasXor = $.inArray('xor', constraints) === -1; 388 var len = constraints.length; 389 for (var i = 0; i < len; i++) { 390 if (constraints[i] !== 'xor') { 391 var constraint = this.checkConstraints(constraints[i], selector, i); 392 393 if (constraint && (hasXor || result)) { 394 return hasXor; 395 } 396 result = result || constraint; 397 } 398 } 399 } else if ($.isPlainObject(constraints)) { 400 for (var n in constraints) { 401 if (constraints.hasOwnProperty(n)) { 402 result = ternary(result, this.checkConstraints(constraints[n], selector, n)); 403 404 if (result === false) { 405 return false;
406 } 407 } 408 } 409 } 410 return result; 411 }, 412 checkConstraints: function checkConstraints(value, selector, state) { 413 if (typeof state !== 'string' || /[0-9]/.test(state[0])) { 414 state = null; 415 } else if (typeof selector === 'undefined') { 416 selector = state; 417 state = null; 418 } 419 420 if (state !== null) { 421 state = states.State.sanitize(state); 422 return invert(this.compare(value, selector, state), state.invert); 423 } 424 425 return this.verifyConstraints(value, selector); 426 }, 427 getDependees: function getDependees() { 428 var cache = {}; 429 430 var _compare = this.compare; 431 this.compare = function (reference, selector, state) { 432 (cache[selector] || (cache[selector] = [])).push(state.name); 433 }; 434 435 this.verifyConstraints(this.constraints); 436 437 this.compare = _compare; 438 439 return cache; 440 } 441 }; 442 443 states.Trigger = function (args) { 444 $.extend(this, args); 445 446 if (this.state in states.Trigger.states) { 447 this.element = $(this.selector); 448 449 if (!this.element.data('trigger:' + this.state)) { 450 this.initialize(); 451 } 452 } 453 }; 454 455 states.Trigger.prototype = { 456 initialize: function initialize() { 457 var _this3 = this; 458 459 var trigger = states.Trigger.states[this.state]; 460 461 if (typeof trigger === 'function') { 462 trigger.call(window, this.element); 463 } else { 464 Object.keys(trigger || {}).forEach(function (event) { 465 _this3.defaultTrigger(event, trigger[event]); 466 }); 467 } 468 469 this.element.data('trigger:' + this.state, true); 470 }, 471 defaultTrigger: function defaultTrigger(event, valueFn) { 472 var oldValue = valueFn.call(this.element); 473 474 this.element.on(event, $.proxy(function (e) { 475 var value = valueFn.call(this.element, e); 476 477 if (oldValue !== value) { 478 this.element.trigger({ 479 type: 'state:' + this.state, 480 value: value, 481 oldValue: oldValue 482 }); 483 oldValue = value; 484 } 485 }, this)); 486 487 states.postponed.push($.proxy(function () { 488 this.element.trigger({ 489 type: 'state:' + this.state, 490 value: oldValue, 491 oldValue: null 492 }); 493 }, this)); 494 } 495 }; 496 497 states.Trigger.states = { 498 empty: { 499 keyup: function keyup() { 500 return this.val() === ''; 501 } 502 }, 503 504 checked: { 505 change: function change() { 506 var checked = false; 507 this.each(function () { 508 checked = $(this).prop('checked'); 509 510 return !checked; 511 }); 512 return checked; 513 } 514 }, 515 516 value: { 517 keyup: function keyup() { 518 if (this.length > 1) { 519 return this.filter(':checked').val() || false; 520 } 521 return this.val(); 522 }, 523 change: function change() { 524 if (this.length > 1) { 525 return this.filter(':checked').val() || false; 526 } 527 return this.val(); 528 } 529 }, 530 531 collapsed: { 532 collapsed: function collapsed(e) { 533 return typeof e !== 'undefined' && 'value' in e ? e.value : !this.is('[open]'); 534 } 535 } 536 }; 537 538 states.State = function (state) { 539 this.pristine = state; 540 this.name = state; 541 542 var process = true; 543 do { 544 while (this.name.charAt(0) === '!') { 545 this.name = this.name.substring(1); 546 this.invert = !this.invert; 547 } 548 549 if (this.name in states.State.aliases) { 550 this.name = states.State.aliases[this.name]; 551 } else { 552 process = false; 553 } 554 } while (process); 555 }; 556 557 states.State.sanitize = function (state) { 558 if (state instanceof states.State) { 559 return state; 560 } 561 562 return new states.State(state); 563 }; 564 565 states.State.aliases = { 566 enabled: '!disabled', 567 invisible: '!visible', 568 invalid: '!valid', 569 untouched: '!touched', 570 optional: '!required', 571 filled: '!empty', 572 unchecked: '!checked', 573 irrelevant: '!relevant', 574 expanded: '!collapsed', 575 open: '!collapsed', 576 closed: 'collapsed', 577 readwrite: '!readonly' 578 }; 579 580 states.State.prototype = { 581 invert: false, 582 583 toString: function toString() { 584 return this.name; 585 } 586 }; 587 588 var $document = $(document); 589 $document.on('state:disabled', function (e) { 590 if (e.trigger) { 591 $(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); 592 } 593 }); 594 595 $document.on('state:required', function (e) { 596 if (e.trigger) { 597 if (e.value) { 598 var label = 'label' + (e.target.id ? '[for=' + e.target.id + ']' : ''); 599 var $label = $(e.target).attr({ required: 'required', 'aria-required': 'true' }).closest('.js-form-item, .js-form-wrapper').find(label); 600 601 if (!$label.hasClass('js-form-required').length) { 602 $label.addClass('js-form-required form-required'); 603 } 604 } else { 605 $(e.target).removeAttr('required aria-required').closest('.js-form-item, .js-form-wrapper').find('label.js-form-required').removeClass('js-form-required form-required'); 606 } 607 } 608 }); 609 610 $document.on('state:visible', function (e) { 611 if (e.trigger) { 612 $(e.target).closest('.js-form-item, .js-form-submit, .js-form-wrapper').toggle(e.value); 613 } 614 }); 615 616 $document.on('state:checked', function (e) { 617 if (e.trigger) { 618 $(e.target).prop('checked', e.value); 619 } 620 }); 621 622 $document.on('state:collapsed', function (e) { 623 if (e.trigger) { 624 if ($(e.target).is('[open]') === e.value) { 625 $(e.target).find('> summary').trigger('click'); 626 } 627 } 628 }); 629})(jQuery, Drupal);; 630/** 631 * @file 632 * JavaScript behaviors for custom webform #states. 633 */ 634 635(function ($, Drupal) { 636 637 'use strict'; 638 639 Drupal.webform = Drupal.webform || {}; 640 Drupal.webform.states = Drupal.webform.states || {}; 641 Drupal.webform.states.slideDown = Drupal.webform.states.slideDown || {}; 642 Drupal.webform.states.slideDown.duration = 'slow'; 643 Drupal.webform.states.slideUp = Drupal.webform.states.slideUp || {}; 644 Drupal.webform.states.slideUp.duration = 'fast'; 645 646 /* ************************************************************************ */ 647 // jQuery functions. 648 /* ************************************************************************ */ 649 650 /** 651 * Check if an element has a specified data attribute. 652 * 653 * @param {string} data 654 * The data attribute name. 655 * 656 * @return {boolean} 657 * TRUE if an element has a specified data attribute. 658 */ 659 $.fn.hasData = function (data) { 660 return (typeof this.data(data) !== 'undefined'); 661 }; 662 663 /**
664 * Check if element is within the webform or not. 665 * 666 * @return {boolean} 667 * TRUE if element is within the webform. 668 */ 669 $.fn.isWebform = function () { 670 return $(this).closest('form.webform-submission-form, form[id^="webform"], form[data-is-webform]').length ? true : false; 671 }; 672 673 /** 674 * Check if element is to be treated as a webform element. 675 * 676 * @return {boolean} 677 * TRUE if element is to be treated as a webform element. 678 */ 679 $.fn.isWebformElement = function () { 680 return ($(this).isWebform() || $(this).closest('[data-is-webform-element]').length) ? true : false; 681 }; 682 683 /* ************************************************************************ */ 684 // Trigger. 685 /* ************************************************************************ */ 686 687 // The change event is triggered by cut-n-paste and select menus. 688 // Issue #2445271: #states element empty check not triggered on mouse 689 // based paste. 690 // @see https://www.drupal.org/node/2445271 691 Drupal.states.Trigger.states.empty.change = function change() { 692 return this.val() === ''; 693 }; 694 695 /* ************************************************************************ */ 696 // Dependents. 697 /* ************************************************************************ */ 698 699 700 // Apply solution included in #1962800 patch. 701 // Issue #1962800: Form #states not working with literal integers as 702 // values in IE11. 703 // @see https://www.drupal.org/project/drupal/issues/1962800 704 // @see https://www.drupal.org/files/issues/core-states-not-working-with-integers-ie11_1962800_46.patch 705 // 706 // This issue causes pattern, less than, and greater than support to break. 707 // @see https://www.drupal.org/project/webform/issues/2981724 708 var states = Drupal.states; 709 Drupal.states.Dependent.prototype.compare = function compare(reference, selector, state) { 710 var value = this.values[selector][state.name]; 711 712 var name = reference.constructor.name; 713 if (!name) { 714 name = $.type(reference); 715 716 name = name.charAt(0).toUpperCase() + name.slice(1); 717 } 718 if (name in states.Dependent.comparisons) { 719 return states.Dependent.comparisons[name](reference, value); 720 } 721 722 if (reference.constructor.name in states.Dependent.comparisons) { 723 return states.Dependent.comparisons[reference.constructor.name](reference, value); 724 } 725 726 return _compare2(reference, value); 727 }; 728 function _compare2(a, b) { 729 if (a === b) { 730 return typeof a === 'undefined' ? a : true; 731 } 732 733 return typeof a === 'undefined' || typeof b === 'undefined'; 734 } 735 736 // Adds pattern, less than, and greater than support to #state API. 737 // @see http://drupalsun.com/julia-evans/2012/03/09/extending-form-api-states-regular-expressions 738 Drupal.states.Dependent.comparisons.Object = function (reference, value) { 739 if ('pattern' in reference) { 740 return (new RegExp(reference['pattern'])).test(value); 741 } 742 else if ('!pattern' in reference) { 743 return !((new RegExp(reference['!pattern'])).test(value)); 744 } 745 else if ('less' in reference) { 746 return (value !== '' && parseFloat(reference['less']) > parseFloat(value)); 747 } 748 else if ('less_equal' in reference) { 749 return (value !== '' && parseFloat(reference['less_equal']) >= parseFloat(value)); 750 } 751 else if ('greater' in reference) { 752 return (value !== '' && parseFloat(reference['greater']) < parseFloat(value)); 753 } 754 else if ('greater_equal' in reference) { 755 return (value !== '' && parseFloat(reference['greater_equal']) <= parseFloat(value)); 756 } 757 else if ('between' in reference || '!between' in reference) { 758 if (value === '') { 759 return false; 760 } 761 762 var between = reference['between'] || reference['!between']; 763 var betweenParts = between.split(':'); 764 var greater = betweenParts[0]; 765 var less = (typeof betweenParts[1] !== 'undefined') ? betweenParts[1] : null; 766 var isGreaterThan = (greater === null || greater === '' || parseFloat(value) >= parseFloat(greater)); 767 var isLessThan = (less === null || less === '' || parseFloat(value) <= parseFloat(less)); 768 var result = (isGreaterThan && isLessThan); 769 return (reference['!between']) ? !result : result; 770 } 771 else { 772 return reference.indexOf(value) !== false; 773 } 774 }; 775 776 /* ************************************************************************ */ 777 // States events. 778 /* ************************************************************************ */ 779 780 var $document = $(document); 781 782 $document.on('state:required', function (e) { 783 if (e.trigger && $(e.target).isWebformElement()) { 784 var $target = $(e.target); 785 // Fix #required file upload. 786 // @see Issue #2860529: Conditional required File upload field don't work. 787 toggleRequired($target.find('input[type="file"]'), e.value); 788 789 // Fix #required for radios and likert. 790 // @see Issue #2856795: If radio buttons are required but not filled form is nevertheless submitted. 791 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')) { 792 $target.toggleClass('required', e.value); 793 toggleRequired($target.find('input[type="radio"]'), e.value); 794 } 795 796 // Fix #required for checkboxes. 797 // @see Issue #2938414: Checkboxes don't support #states required. 798 // @see checkboxRequiredhandler 799 if ($target.is('.js-form-type-checkboxes, .js-form-type-webform-checkboxes-other, .js-webform-type-checkboxes, .js-webform-type-webform-checkboxes-other')) { 800 $target.toggleClass('required', e.value); 801 var $checkboxes = $target.find('input[type="checkbox"]'); 802 if (e.value) { 803 // Add event handler. 804 $checkboxes.on('click', statesCheckboxesRequiredEventHandler); 805 // Initialize and add required attribute. 806 checkboxesRequired($target); 807 } 808 else { 809 // Remove event handler. 810 $checkboxes.off('click', statesCheckboxesRequiredEventHandler); 811 // Remove required attribute. 812 toggleRequired($checkboxes, false); 813 } 814 } 815 816 // Fix #required for tableselect. 817 // @see Issue #3212581: Table select does not trigger client side validation 818 if ($target.is('.js-webform-tableselect')) { 819 $target.toggleClass('required', e.value); 820 var isMultiple = $target.is('[multiple]'); 821 if (isMultiple) { 822 // Checkboxes. 823 var $tbody = $target.find('tbody'); 824 var $checkboxes = $tbody.find('input[type="checkbox"]'); 825 copyRequireMessage($target, $checkboxes); 826 if (e.value) { 827 $checkboxes.on('click change', statesCheckboxesRequiredEventHandler); 828 checkboxesRequired($tbody); 829 } 830 else { 831 $checkboxes.off('click change ', statesCheckboxesRequiredEventHandler); 832 toggleRequired($tbody, false); 833 } 834 } 835 else { 836 // Radios. 837 var $radios = $target.find('input[type="radio"]'); 838 copyRequireMessage($target, $radios); 839 toggleRequired($radios, e.value); 840 } 841 } 842 843 // Fix required label for elements without the for attribute. 844 // @see Issue #3145300: Conditional Visible Select Other not working. 845 if ($target.is('.js-form-type-webform-select-other, .js-webform-type-webform-select-other')) { 846 var $select = $target.find('select'); 847 toggleRequired($select, e.value); 848 copyRequireMessage($target, $select); 849 } 850 if ($target.find('> label:not([for])').length) { 851 $target.find('> label').toggleClass('js-form-required form-required', e.value); 852 } 853 854 // Fix required label for checkboxes and radios. 855 // @see Issue #2938414: Checkboxes don't support #states required 856 // @see Issue #2731991: Setting required on radios marks all options required. 857 // @see Issue #2856315: Conditional Logic - Requiring Radios in a Fieldset. 858 // Fix #required for fieldsets. 859 // @see Issue #2977569: Hidden fieldsets that become visible with conditional logic cannot be made required. 860 if ($target.is('.js-webform-type-radios, .js-webform-type-checkboxes, fieldset')) { 861 $target.find('legend span.fieldset-legend:not(.visually-hidden)').toggleClass('js-form-required form-required', e.value); 862 } 863 864 // Issue #2986017: Fieldsets shouldn't have required attribute. 865 if ($target.is('fieldset')) { 866 $target.removeAttr('required aria-required'); 867 } 868 } 869 }); 870 871 $document.on('state:checked', function (e) { 872 if (e.trigger) { 873 $(e.target).trigger('change'); 874 } 875 }); 876 877 $document.on('state:readonly', function (e) { 878 if (e.trigger && $(e.target).isWebformElement()) { 879 $(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); 880 881 // Trigger webform:readonly. 882 $(e.target).trigger('webform:readonly') 883 .find('select, input, textarea, button').trigger('webform:readonly'); 884 } 885 }); 886 887 $document.on('state:visible state:visible-slide', function (e) { 888 if (e.trigger && $(e.target).isWebformElement()) { 889 if (e.value) { 890 $(':input', e.target).addBack().each(function () { 891 restoreValueAndRequired(this); 892 triggerEventHandlers(this); 893 }); 894 } 895 else { 896 // @see https://www.sitepoint.com/jquery-function-clear-form-data/ 897 $(':input', e.target).addBack().each(function () { 898 backupValueAndRequired(this); 899 clearValueAndRequired(this); 900 triggerEventHandlers(this); 901 }); 902 } 903 } 904 }); 905 906 $document.on('state:visible-slide', function (e) { 907 if (e.trigger && $(e.target).isWebformElement()) { 908 var effect = e.value ? 'slideDown' : 'slideUp'; 909 var duration = Drupal.webform.states[effect].duration; 910 $(e.target).closest('.js-form-item, .js-form-submit, .js-form-wrapper')[effect](duration); 911 } 912 }); 913 Drupal.states.State.aliases['invisible-slide'] = '!visible-slide'; 914 915 $document.on('state:disabled', function (e) { 916 if (e.trigger && $(e.target).isWebformElement()) { 917 // Make sure disabled property is set before triggering webform:disabled. 918 // Copied from: core/misc/states.js 919 $(e.target) 920 .prop('disabled', e.value) 921 .closest('.js-form-item, .js-form-submit, .js-form-wrapper').toggleClass('form-disabled', e.value) 922 .find('select, input, textarea, button').prop('disabled', e.value); 923 924 // Never disable hidden file[fids] because the existing values will 925 // be completely lost when the webform is submitted. 926 var fileElements = $(e.target) 927 .find(':input[type="hidden"][name$="[fids]"]'); 928 if (fileElements.length) { 929 // Remove 'disabled' attribute from fieldset which will block 930 // all disabled elements from being submitted. 931 if ($(e.target).is('fieldset')) { 932 $(e.target).prop('disabled', false); 933 } 934 fileElements.removeAttr('disabled'); 935 } 936 937 // Trigger webform:disabled. 938 $(e.target).trigger('webform:disabled') 939 .find('select, input, textarea, button').trigger('webform:disabled'); 940 } 941 }); 942 943 /* ************************************************************************ */ 944 // Behaviors. 945 /* ************************************************************************ */ 946 947 /** 948 * Adds HTML5 validation to required checkboxes. 949 * 950 * @type {Drupal~behavior} 951 * 952 * @see https://www.drupal.org/project/webform/issues/3068998 953 */ 954 Drupal.behaviors.webformCheckboxesRequired = { 955 attach: function (context) { 956 $('.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) 957 .once('webform-checkboxes-required') 958 .each(function () { 959 var $element = $(this); 960 $element.find('input[type="checkbox"]').on('click', statesCheckboxesRequiredEventHandler); 961 setTimeout(function () {checkboxesRequired($element);}); 962 }); 963 } 964 }; 965 966 /** 967 * Adds HTML5 validation to required radios. 968 * 969 * @type {Drupal~behavior} 970 * 971 * @see https://www.drupal.org/project/webform/issues/2856795 972 */ 973 Drupal.behaviors.webformRadiosRequired = { 974 attach: function (context) { 975 $('.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) 976 .once('webform-radios-required') 977 .each(function () { 978 var $element = $(this); 979 setTimeout(function () {radiosRequired($element);}); 980 }); 981 } 982 }; 983 984 /** 985 * Adds HTML5 validation to required table select. 986 * 987 * @type {Drupal~behavior} 988 * 989 * @see https://www.drupal.org/project/webform/issues/2856795 990 */ 991 Drupal.behaviors.webformTableSelectRequired = { 992 attach: function (context) { 993 $('.js-webform-tableselect.required', context) 994 .once('webform-tableselect-required') 995 .each(function () { 996 var $element = $(this); 997 var $tbody = $element.find('tbody'); 998 var isMultiple = $element.is('[multiple]'); 999 1000 if (isMultiple) { 1001 // Check all checkbox triggers checkbox 'change' event on 1002 // select and deselect all. 1003 // @see Drupal.tableSelect 1004 $tbody.find('input[type="checkbox"]').on('click change', function () { 1005 checkboxesRequired($tbody); 1006 }); 1007 } 1008 1009 setTimeout(function () { 1010 isMultiple ? checkboxesRequired($tbody) : radiosRequired($element); 1011 }); 1012 }); 1013 } 1014 }; 1015 1016 /** 1017 * Add HTML5 multiple checkboxes required validation. 1018 * 1019 * @param {jQuery} $element
1020 * An jQuery object containing HTML5 radios. 1021 * 1022 * @see https://stackoverflow.com/a/37825072/145846 1023 */ 1024 function checkboxesRequired($element) { 1025 var $firstCheckbox = $element.find('input[type="checkbox"]').first(); 1026 var isChecked = $element.find('input[type="checkbox"]').is(':checked'); 1027 toggleRequired($firstCheckbox, !isChecked); 1028 copyRequireMessage($element, $firstCheckbox); 1029 } 1030 1031 /** 1032 * Add HTML5 radios required validation. 1033 * 1034 * @param {jQuery} $element 1035 * An jQuery object containing HTML5 radios. 1036 * 1037 * @see https://www.drupal.org/project/webform/issues/2856795 1038 */ 1039 function radiosRequired($element) { 1040 var $radios = $element.find('input[type="radio"]'); 1041 var isRequired = $element.hasClass('required'); 1042 toggleRequired($radios, isRequired); 1043 copyRequireMessage($element, $radios); 1044 } 1045 1046 /* ************************************************************************ */ 1047 // Event handlers. 1048 /* ************************************************************************ */ 1049 1050 /** 1051 * Trigger #states API HTML5 multiple checkboxes required validation. 1052 * 1053 * @see https://stackoverflow.com/a/37825072/145846 1054 */ 1055 function statesCheckboxesRequiredEventHandler() { 1056 var $element = $(this).closest('.js-webform-type-checkboxes, .js-webform-type-webform-checkboxes-other'); 1057 checkboxesRequired($element); 1058 } 1059 1060 /** 1061 * Trigger an input's event handlers. 1062 * 1063 * @param {element} input 1064 * An input. 1065 */ 1066 function triggerEventHandlers(input) { 1067 var $input = $(input); 1068 var type = input.type; 1069 var tag = input.tagName.toLowerCase(); 1070 // Add 'webform.states' as extra parameter to event handlers. 1071 // @see Drupal.behaviors.webformUnsaved 1072 var extraParameters = ['webform.states']; 1073 if (type === 'checkbox' || type === 'radio') { 1074 $input 1075 .trigger('change', extraParameters) 1076 .trigger('blur', extraParameters); 1077 } 1078 else if (tag === 'select') { 1079 // Do not trigger the onchange event for Address element's country code 1080 // when it is initialized. 1081 // @see \Drupal\address\Element\Country 1082 if ($input.closest('.webform-type-address').length) { 1083 if (!$input.data('webform-states-address-initialized') 1084 && $input.attr('autocomplete') === 'country' 1085 && $input.val() === $input.find("option[selected]").attr('value')) { 1086 return; 1087 } 1088 $input.data('webform-states-address-initialized', true); 1089 } 1090 1091 $input 1092 .trigger('change', extraParameters) 1093 .trigger('blur', extraParameters); 1094 } 1095 else if (type !== 'submit' && type !== 'button' && type !== 'file') { 1096 // Make sure input mask is removed and then reset when value is restored. 1097 // @see https://www.drupal.org/project/webform/issues/3124155 1098 // @see https://www.drupal.org/project/webform/issues/3202795 1099 var hasInputMask = ($.fn.inputmask && $input.hasClass('js-webform-input-mask')); 1100 hasInputMask && $input.inputmask('remove'); 1101 1102 $input 1103 .trigger('input', extraParameters) 1104 .trigger('change', extraParameters) 1105 .trigger('keydown', extraParameters) 1106 .trigger('keyup', extraParameters) 1107 .trigger('blur', extraParameters); 1108 1109 hasInputMask && $input.inputmask(); 1110 } 1111 } 1112 1113 /* ************************************************************************ */ 1114 // Backup and restore value functions. 1115 /* ************************************************************************ */ 1116 1117 /** 1118 * Backup an input's current value and required attribute 1119 * 1120 * @param {element} input 1121 * An input. 1122 */ 1123 function backupValueAndRequired(input) { 1124 var $input = $(input); 1125 var type = input.type; 1126 var tag = input.tagName.toLowerCase(); // Normalize case. 1127 1128 // Backup required. 1129 if ($input.prop('required') && !$input.hasData('webform-required')) { 1130 $input.data('webform-required', true); 1131 } 1132 1133 // Backup value. 1134 if (!$input.hasData('webform-value')) { 1135 if (type === 'checkbox' || type === 'radio') { 1136 $input.data('webform-value', $input.prop('checked')); 1137 } 1138 else if (tag === 'select') { 1139 var values = []; 1140 $input.find('option:selected').each(function (i, option) { 1141 values[i] = option.value; 1142 }); 1143 $input.data('webform-value', values); 1144 } 1145 else if (type !== 'submit' && type !== 'button') { 1146 $input.data('webform-value', input.value); 1147 } 1148 } 1149 } 1150 1151 /** 1152 * Restore an input's value and required attribute. 1153 * 1154 * @param {element} input 1155 * An input. 1156 */ 1157 function restoreValueAndRequired(input) { 1158 var $input = $(input); 1159 1160 // Restore value. 1161 var value = $input.data('webform-value'); 1162 if (typeof value !== 'undefined') { 1163 var type = input.type; 1164 var tag = input.tagName.toLowerCase(); // Normalize case. 1165 1166 if (type === 'checkbox' || type === 'radio') { 1167 $input.prop('checked', value); 1168 } 1169 else if (tag === 'select') { 1170 $.each(value, function (i, option_value) { 1171 // Prevent "Syntax error, unrecognized expression" error by 1172 // escaping single quotes. 1173 // @see https://forum.jquery.com/topic/escape-characters-prior-to-using-selector 1174 option_value = option_value.replace(/'/g, "\\\'"); 1175 $input.find("option[value='" + option_value + "']").prop('selected', true); 1176 }); 1177 } 1178 else if (type !== 'submit' && type !== 'button') { 1179 input.value = value; 1180 } 1181 $input.removeData('webform-value'); 1182 } 1183 1184 // Restore required. 1185 var required = $input.data('webform-required'); 1186 if (typeof required !== 'undefined') { 1187 if (required) { 1188 $input.prop('required', true); 1189 } 1190 $input.removeData('webform-required'); 1191 } 1192 } 1193 1194 /** 1195 * Clear an input's value and required attributes. 1196 * 1197 * @param {element} input 1198 * An input. 1199 */ 1200 function clearValueAndRequired(input) { 1201 var $input = $(input); 1202 1203 // Check for #states no clear attribute.
1204 // @see https://css-tricks.com/snippets/jquery/make-an-jquery-hasattr/ 1205 if ($input.closest('[data-webform-states-no-clear]').length) { 1206 return; 1207 } 1208 1209 // Clear value. 1210 var type = input.type; 1211 var tag = input.tagName.toLowerCase(); // Normalize case. 1212 if (type === 'checkbox' || type === 'radio') { 1213 $input.prop('checked', false); 1214 } 1215 else if (tag === 'select') { 1216 if ($input.find('option[value=""]').length) { 1217 $input.val(''); 1218 } 1219 else { 1220 input.selectedIndex = -1; 1221 } 1222 } 1223 else if (type !== 'submit' && type !== 'button') { 1224 input.value = (type === 'color') ? '#000000' : ''; 1225 } 1226 1227 // Clear required. 1228 $input.prop('required', false); 1229 } 1230 1231 /* ************************************************************************ */ 1232 // Helper functions. 1233 /* ************************************************************************ */ 1234 1235 /** 1236 * Toggle an input's required attributes. 1237 * 1238 * @param {element} $input 1239 * An input. 1240 * @param {boolean} required 1241 * Is input required. 1242 */ 1243 function toggleRequired($input, required) { 1244 var isCheckboxOrRadio = ($input.attr('type') === 'radio' || $input.attr('type') === 'checkbox'); 1245 if (required) { 1246 if (isCheckboxOrRadio) { 1247 $input.attr({'required': 'required'}); 1248 } 1249 else { 1250 $input.attr({'required': 'required', 'aria-required': 'true'}); 1251 } 1252 } 1253 else { 1254 if (isCheckboxOrRadio) { 1255 $input.removeAttr('required'); 1256 } 1257 else { 1258 $input.removeAttr('required aria-required'); 1259 } 1260 } 1261 } 1262 1263 /** 1264 * Copy the clientside_validation.module's message. 1265 * 1266 * @param {jQuery} $source 1267 * The source element. 1268 * @param {jQuery} $destination 1269 * The destination element. 1270 */ 1271 function copyRequireMessage($source, $destination) { 1272 if ($source.attr('data-msg-required')) { 1273 $destination.attr('data-msg-required', $source.attr('data-msg-required')); 1274 } 1275 } 1276 1277})(jQuery, Drupal); 1278; 1279/** 1280 * @file 1281 * JavaScript behaviors for webforms. 1282 */ 1283 1284(function ($, Drupal) { 1285 1286 'use strict'; 1287 1288 /** 1289 * Remove single submit event listener. 1290 * 1291 * @type {Drupal~behavior} 1292 * 1293 * @prop {Drupal~behaviorAttach} attach 1294 * Attaches the behavior for removing single submit event listener. 1295 * 1296 * @see Drupal.behaviors.formSingleSubmit 1297 */ 1298 Drupal.behaviors.webformRemoveFormSingleSubmit = { 1299 attach: function attach() { 1300 function onFormSubmit(e) { 1301 var $form = $(e.currentTarget); 1302 $form.removeAttr('data-drupal-form-submit-last'); 1303 } 1304 $('body') 1305 .once('webform-single-submit') 1306 .on('submit.singleSubmit', 'form.webform-remove-single-submit', onFormSubmit); 1307 } 1308 }; 1309 1310 /** 1311 * Prevent webform autosubmit on wizard pages. 1312 * 1313 * @type {Drupal~behavior} 1314 * 1315 * @prop {Drupal~behaviorAttach} attach 1316 * Attaches the behavior for disabling webform autosubmit. 1317 * Wizard pages need to be progressed with the Previous or Next buttons, 1318 * not by pressing Enter. 1319 */ 1320 Drupal.behaviors.webformDisableAutoSubmit = { 1321 attach: function (context) { 1322 // Not using context so that inputs loaded via Ajax will have autosubmit 1323 // disabled. 1324 // @see http://stackoverflow.com/questions/11235622/jquery-disable-form-submit-on-enter 1325 $('.js-webform-disable-autosubmit input') 1326 .not(':button, :submit, :reset, :image, :file') 1327 .once('webform-disable-autosubmit') 1328 .on('keyup keypress', function (e) { 1329 if (e.which === 13) { 1330 e.preventDefault(); 1331 return false; 1332 } 1333 }); 1334 } 1335 }; 1336 1337 /** 1338 * Custom required and pattern validation error messages. 1339 * 1340 * @type {Drupal~behavior} 1341 * 1342 * @prop {Drupal~behaviorAttach} attach 1343 * Attaches the behavior for the webform custom required and pattern 1344 * validation error messages. 1345 * 1346 * @see http://stackoverflow.com/questions/5272433/html5-form-required-attribute-set-custom-validation-message 1347 **/ 1348 Drupal.behaviors.webformRequiredError = { 1349 attach: function (context) { 1350 $(context).find(':input[data-webform-required-error], :input[data-webform-pattern-error]').once('webform-required-error') 1351 .on('invalid', function () { 1352 this.setCustomValidity(''); 1353 if (this.valid) { 1354 return; 1355 } 1356 1357 if (this.validity.patternMismatch && $(this).attr('data-webform-pattern-error')) { 1358 this.setCustomValidity($(this).attr('data-webform-pattern-error')); 1359 } 1360 else if (this.validity.valueMissing && $(this).attr('data-webform-required-error')) { 1361 this.setCustomValidity($(this).attr('data-webform-required-error')); 1362 } 1363 }) 1364 .on('input change', function () { 1365 // Find all related elements by name and reset custom validity. 1366 // This specifically applies to required radios and checkboxes. 1367 var name = $(this).attr('name'); 1368 $(this.form).find(':input[name="' + name + '"]').each(function () { 1369 this.setCustomValidity(''); 1370 }); 1371 }); 1372 } 1373 }; 1374 1375 // When #state:required is triggered we need to reset the target elements 1376 // custom validity. 1377 $(document).on('state:required', function (e) { 1378 $(e.target).filter('[data-webform-required-error]') 1379 .each(function () {this.setCustomValidity('');}); 1380 }); 1381 1382})(jQuery, Drupal); 1383; 1384/** 1385* DO NOT EDIT THIS FILE. 1386* See the following change record for more information, 1387* https://www.drupal.org/node/2815083 1388* @preserve 1389**/ 1390 1391(function (Drupal, debounce) { 1392 var liveElement = void 0; 1393 var announcements = []; 1394 1395 Drupal.behaviors.drupalAnnounce = { 1396 attach: function attach(context) { 1397 if (!liveElement) { 1398 liveElement = document.createElement('div'); 1399 liveElement.id = 'drupal-live-announce'; 1400 liveElement.className = 'visually-hidden'; 1401 liveElement.setAttribute('aria-live', 'polite'); 1402 liveElement.setAttribute('aria-busy', 'false'); 1403 document.body.appendChild(liveElement); 1404 } 1405 } 1406 }; 1407 1408 function announce() { 1409 var text = []; 1410 var priority = 'polite'; 1411 var announcement = void 0; 1412 1413 var il = announcements.length; 1414 for (var i = 0; i < il; i++) { 1415 announcement = announcements.pop(); 1416 text.unshift(announcement.text); 1417 1418 if (announcement.priority === 'assertive') { 1419 priority = 'assertive'; 1420 } 1421 } 1422 1423 if (text.length) { 1424 liveElement.innerHTML = ''; 1425 1426 liveElement.setAttribute('aria-busy', 'true'); 1427 1428 liveElement.setAttribute('aria-live', priority); 1429 1430 liveElement.innerHTML = text.join('\n'); 1431 1432 liveElement.setAttribute('aria-busy', 'false'); 1433 } 1434 } 1435 1436 Drupal.announce = function (text, priority) { 1437 announcements.push({ 1438 text: text, 1439 priority: priority 1440 }); 1441 1442 return debounce(announce, 200)(); 1443 }; 1444})(Drupal, Drupal.debounce);; 1445/** 1446 * @file 1447 * JavaScript behaviors for details element. 1448 */ 1449 1450(function ($, Drupal) { 1451 1452 'use strict'; 1453 1454 Drupal.webform = Drupal.webform || {}; 1455 Drupal.webform.detailsToggle = Drupal.webform.detailsToggle || {}; 1456 Drupal.webform.detailsToggle.options = Drupal.webform.detailsToggle.options || {}; 1457 1458 /** 1459 * Attach handler to toggle details open/close state. 1460 * 1461 * @type {Drupal~behavior} 1462 */ 1463 Drupal.behaviors.webformDetailsToggle = { 1464 attach: function (context) { 1465 $('.js-webform-details-toggle', context).once('webform-details-toggle').each(function () { 1466 var $form = $(this); 1467 var $tabs = $form.find('.webform-tabs'); 1468 1469 // Get only the main details elements and ignore all nested details. 1470 var selector = ($tabs.length) ? '.webform-tab' : '.js-webform-details-toggle, .webform-elements'; 1471 var $details = $form.find('details').filter(function () { 1472 var $parents = $(this).parentsUntil(selector); 1473 return ($parents.find('details').length === 0); 1474 }); 1475 1476 // Toggle is only useful when there are two or more details elements. 1477 if ($details.length < 2) { 1478 return; 1479 } 1480 1481 var options = $.extend({ 1482 button: '<button type="button" class="webform-details-toggle-state"></button>' 1483 }, Drupal.webform.detailsToggle.options); 1484 1485 // Create toggle buttons. 1486 var $toggle = $(options.button) 1487 .attr('title', Drupal.t('Toggle details widget state.')) 1488 .on('click', function (e) { 1489 // Get details that are not vertical tabs pane. 1490 var $details = $form.find('details:not(.vertical-tabs__pane)'); 1491 var open; 1492 if (Drupal.webform.detailsToggle.isFormDetailsOpen($form)) { 1493 $details.removeAttr('open'); 1494 open = 0; 1495 } 1496 else { 1497 $details.attr('open', 'open'); 1498 open = 1; 1499 } 1500 Drupal.webform.detailsToggle.setDetailsToggleLabel($form); 1501 1502 // Set the saved states for all the details elements. 1503 // @see webform.element.details.save.js 1504 if (Drupal.webformDetailsSaveGetName) { 1505 $details.each(function () { 1506 // Note: Drupal.webformDetailsSaveGetName checks if localStorage 1507 // exists and is enabled. 1508 // @see webform.element.details.save.js 1509 var name = Drupal.webformDetailsSaveGetName($(this)); 1510 if (name) {
1511 localStorage.setItem(name, open); 1512 } 1513 }); 1514 } 1515 }) 1516 .wrap('<div class="webform-details-toggle-state-wrapper"></div>') 1517 .parent(); 1518 1519 if ($tabs.length) { 1520 // Add toggle state before the tabs. 1521 $tabs.find('.item-list:first-child').eq(0).before($toggle); 1522 } 1523 else { 1524 // Add toggle state link to first details element. 1525 $details.eq(0).before($toggle); 1526 } 1527 1528 Drupal.webform.detailsToggle.setDetailsToggleLabel($form); 1529 }); 1530 } 1531 }; 1532 1533 /** 1534 * Determine if a webform's details are all opened. 1535 * 1536 * @param {jQuery} $form 1537 * A webform. 1538 * 1539 * @return {boolean} 1540 * TRUE if a webform's details are all opened. 1541 */ 1542 Drupal.webform.detailsToggle.isFormDetailsOpen = function ($form) { 1543 return ($form.find('details[open]').length === $form.find('details').length); 1544 }; 1545 1546 /** 1547 * Set a webform's details toggle state widget label. 1548 * 1549 * @param {jQuery} $form 1550 * A webform. 1551 */ 1552 Drupal.webform.detailsToggle.setDetailsToggleLabel = function ($form) { 1553 var isOpen = Drupal.webform.detailsToggle.isFormDetailsOpen($form); 1554 1555 var label = (isOpen) ? Drupal.t('Collapse all') : Drupal.t('Expand all'); 1556 $form.find('.webform-details-toggle-state').html(label); 1557 1558 var text = (isOpen) ? Drupal.t('All details have been expanded.') : Drupal.t('All details have been collapsed.'); 1559 Drupal.announce(text); 1560 }; 1561 1562})(jQuery, Drupal); 1563; 1564/** 1565* DO NOT EDIT THIS FILE. 1566* See the following change record for more information, 1567* https://www.drupal.org/node/2815083 1568* @preserve 1569**/ 1570 1571(function ($, Drupal) { 1572 Drupal.behaviors.detailsAria = { 1573 attach: function attach() { 1574 $('body').once('detailsAria').on('click.detailsAria', 'summary', function (event) { 1575 var $summary = $(event.currentTarget); 1576 var open = $(event.currentTarget.parentNode).attr('open') === 'open' ? 'false' : 'true'; 1577 1578 $summary.attr({ 1579 'aria-expanded': open, 1580 'aria-pressed': open 1581 }); 1582 }); 1583 } 1584 }; 1585})(jQuery, Drupal);; 1586/** 1587* DO NOT EDIT THIS FILE. 1588* See the following change record for more information, 1589* https://www.drupal.org/node/2815083 1590* @preserve 1591**/ 1592 1593(function ($, Modernizr, Drupal) { 1594 function CollapsibleDetails(node) { 1595 this.$node = $(node); 1596 this.$node.data('details', this); 1597 1598 var anchor = window.location.hash && window.location.hash !== '#' ? ', ' + window.location.hash : ''; 1599 if (this.$node.find('.error' + anchor).length) { 1600 this.$node.attr('open', true); 1601 } 1602 1603 this.setupSummary(); 1604 1605 this.setupLegend(); 1606 } 1607 1608 $.extend(CollapsibleDetails, { 1609 instances: [] 1610 }); 1611 1612 $.extend(CollapsibleDetails.prototype, { 1613 setupSummary: function setupSummary() { 1614 this.$summary = $('<span class="summary"></span>'); 1615 this.$node.on('summaryUpdated', $.proxy(this.onSummaryUpdated, this)).trigger('summaryUpdated'); 1616 }, 1617 setupLegend: function setupLegend() { 1618 var $legend = this.$node.find('> summary'); 1619 1620 $('<span class="details-summary-prefix visually-hidden"></span>').append(this.$node.attr('open') ? Drupal.t('Hide') : Drupal.t('Show')).prependTo($legend).after(document.createTextNode(' ')); 1621 1622 $('<a class="details-title"></a>').attr('href', '#' + this.$node.attr('id')).prepend($legend.contents()).appendTo($legend); 1623 1624 $legend.append(this.$summary).on('click', $.proxy(this.onLegendClick, this)); 1625 }, 1626 onLegendClick: function onLegendClick(e) { 1627 this.toggle(); 1628 e.preventDefault(); 1629 }, 1630 onSummaryUpdated: function onSummaryUpdated() { 1631 var text = $.trim(this.$node.drupalGetSummary()); 1632 this.$summary.html(text ? ' (' + text + ')' : ''); 1633 }, 1634 toggle: function toggle() { 1635 var _this = this; 1636 1637 var isOpen = !!this.$node.attr('open'); 1638 var $summaryPrefix = this.$node.find('> summary span.details-summary-prefix'); 1639 if (isOpen) { 1640 $summaryPrefix.html(Drupal.t('Show')); 1641 } else { 1642 $summaryPrefix.html(Drupal.t('Hide')); 1643 } 1644 1645 setTimeout(function () { 1646 _this.$node.attr('open', !isOpen); 1647 }, 0); 1648 } 1649 }); 1650 1651 Drupal.behaviors.collapse = { 1652 attach: function attach(context) { 1653 if (Modernizr.details) { 1654 return; 1655 } 1656 var $collapsibleDetails = $(context).find('details').once('collapse').addClass('collapse-processed'); 1657 if ($collapsibleDetails.length) { 1658 for (var i = 0; i < $collapsibleDetails.length; i++) { 1659 CollapsibleDetails.instances.push(new CollapsibleDetails($collapsibleDetails[i])); 1660 } 1661 } 1662 } 1663 }; 1664 1665 var handleFragmentLinkClickOrHashChange = function handleFragmentLinkClickOrHashChange(e, $target) { 1666 $target.parents('details').not('[open]').find('> summary').trigger('click'); 1667 }; 1668 1669 $('body').on('formFragmentLinkClickOrHashChange.details', handleFragmentLinkClickOrHashChange); 1670 1671 Drupal.CollapsibleDetails = CollapsibleDetails; 1672})(jQuery, Modernizr, Drupal);; 1673/*! 1674 * Signature Pad v2.3.0 1675 * https://github.com/szimek/signature_pad 1676 * 1677 * Copyright 2017 Szymon Nowak 1678 * Released under the MIT license 1679 * 1680 * The main idea and some parts of the code (e.g. drawing variable width Bézier curve) are taken from: 1681 * http://corner.squareup.com/2012/07/smoother-signatures.html 1682 * 1683 * Implementation of interpolation using cubic Bézier curves is taken from: 1684 * http://benknowscode.wordpress.com/2012/09/14/path-interpolation-using-cubic-bezier-and-control-point-estimation-in-javascript 1685 * 1686 * Algorithm for approximated length of a Bézier curve is taken from: 1687 * http://www.lemoda.net/maths/bezier-length/index.html 1688 * 1689 */ 1690 1691(function (global, factory) { 1692 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : 1693 typeof define === 'function' && define.amd ? define(factory) : 1694 (global.SignaturePad = factory()); 1695}(this, (function () { 'use strict'; 1696 1697function Point(x, y, time) { 1698 this.x = x; 1699 this.y = y; 1700 this.time = time || new Date().getTime(); 1701} 1702 1703Point.prototype.velocityFrom = function (start) { 1704 return this.time !== start.time ? this.distanceTo(start) / (this.time - start.time) : 1; 1705}; 1706 1707Point.prototype.distanceTo = function (start) { 1708 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2)); 1709}; 1710 1711Point.prototype.equals = function (other) { 1712 return this.x === other.x && this.y === other.y && this.time === other.time; 1713}; 1714 1715function Bezier(startPoint, control1, control2, endPoint) { 1716 this.startPoint = startPoint; 1717 this.control1 = control1; 1718 this.control2 = control2; 1719 this.endPoint = endPoint; 1720} 1721 1722// Returns approximated length. 1723Bezier.prototype.length = function () { 1724 var steps = 10; 1725 var length = 0; 1726 var px = void 0; 1727 var py = void 0; 1728 1729 for (var i = 0; i <= steps; i += 1) { 1730 var t = i / steps; 1731 var cx = this._point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x); 1732 var cy = this._point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y); 1733 if (i > 0) { 1734 var xdiff = cx - px; 1735 var ydiff = cy - py; 1736 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff); 1737 } 1738 px = cx; 1739 py = cy; 1740 } 1741 1742 return length; 1743}; 1744 1745/* eslint-disable no-multi-spaces, space-in-parens */ 1746Bezier.prototype._point = function (t, start, c1, c2, end) { 1747 return start * (1.0 - t) * (1.0 - t) * (1.0 - t) + 3.0 * c1 * (1.0 - t) * (1.0 - t) * t + 3.0 * c2 * (1.0 - t) * t * t + end * t * t * t; 1748}; 1749 1750/* eslint-disable */ 1751 1752// http://stackoverflow.com/a/27078401/815507 1753function throttle(func, wait, options) { 1754 var context, args, result; 1755 var timeout = null; 1756 var previous = 0; 1757 if (!options) options = {}; 1758 var later = function later() { 1759 previous = options.leading === false ? 0 : Date.now(); 1760 timeout = null;
vendor: 1,009 bytes, lines 1761-1793
1761 result = func.apply(context, args); 1762 if (!timeout) context = args = null; 1763 }; 1764 return function () { 1765 var now = Date.now(); 1766 if (!previous && options.leading === false) previous = now; 1767 var remaining = wait - (now - previous); 1768 context = this; 1769 args = arguments; 1770 if (remaining <= 0 || remaining > wait) { 1771 if (timeout) { 1772 clearTimeout(timeout); 1773 timeout = null; 1774 } 1775 previous = now; 1776 result = func.apply(context, args); 1777 if (!timeout) context = args = null; 1778 } else if (!timeout && options.trailing !== false) { 1779 timeout = setTimeout(later, remaining); 1780 } 1781 return result; 1782 }; 1783} 1784 1785function SignaturePad(canvas, options) { 1786 var self = this; 1787 var opts = options || {}; 1788 1789 this.velocityFilterWeight = opts.velocityFilterWeight || 0.7; 1790 this.minWidth = opts.minWidth || 0.5; 1791 this.maxWidth = opts.maxWidth || 2.5; 1792 this.throttle = 'throttle' in opts ? opts.throttle : 16; // in miliseconds 1793 this.minDistance = opts.minDistance ||
vendor: 10,574 bytes, lines 1793-2167
1793 5; 1794 1795 if (this.throttle) { 1796 this._strokeMoveUpdate = throttle(SignaturePad.prototype._strokeUpdate, this.throttle); 1797 } else { 1798 this._strokeMoveUpdate = SignaturePad.prototype._strokeUpdate; 1799 } 1800 1801 this.dotSize = opts.dotSize || function () { 1802 return (this.minWidth + this.maxWidth) / 2; 1803 }; 1804 this.penColor = opts.penColor || 'black'; 1805 this.backgroundColor = opts.backgroundColor || 'rgba(0,0,0,0)'; 1806 this.onBegin = opts.onBegin; 1807 this.onEnd = opts.onEnd; 1808 1809 this._canvas = canvas; 1810 this._ctx = canvas.getContext('2d'); 1811 this.clear(); 1812 1813 // We need add these inline so they are available to unbind while still having 1814 // access to 'self' we could use _.bind but it's not worth adding a dependency. 1815 this._handleMouseDown = function (event) { 1816 if (event.which === 1) { 1817 self._mouseButtonDown = true; 1818 self._strokeBegin(event); 1819 } 1820 }; 1821 1822 this._handleMouseMove = function (event) { 1823 if (self._mouseButtonDown) { 1824 self._strokeMoveUpdate(event); 1825 } 1826 }; 1827 1828 this._handleMouseUp = function (event) { 1829 if (event.which === 1 && self._mouseButtonDown) { 1830 self._mouseButtonDown = false; 1831 self._strokeEnd(event); 1832 } 1833 }; 1834 1835 this._handleTouchStart = function (event) { 1836 if (event.targetTouches.length === 1) { 1837 var touch = event.changedTouches[0]; 1838 self._strokeBegin(touch); 1839 } 1840 }; 1841 1842 this._handleTouchMove = function (event) { 1843 // Prevent scrolling. 1844 event.preventDefault(); 1845 1846 var touch = event.targetTouches[0]; 1847 self._strokeMoveUpdate(touch); 1848 }; 1849 1850 this._handleTouchEnd = function (event) { 1851 var wasCanvasTouched = event.target === self._canvas; 1852 if (wasCanvasTouched) { 1853 event.preventDefault(); 1854 self._strokeEnd(event); 1855 } 1856 }; 1857 1858 // Enable mouse and touch event handlers 1859 this.on(); 1860} 1861 1862// Public methods 1863SignaturePad.prototype.clear = function () { 1864 var ctx = this._ctx; 1865 var canvas = this._canvas; 1866 1867 ctx.fillStyle = this.backgroundColor; 1868 ctx.clearRect(0, 0, canvas.width, canvas.height); 1869 ctx.fillRect(0, 0, canvas.width, canvas.height); 1870 1871 this._data = []; 1872 this._reset(); 1873 this._isEmpty = true; 1874}; 1875 1876SignaturePad.prototype.fromDataURL = function (dataUrl) { 1877 var _this = this; 1878 1879 var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 1880 1881 var image = new Image(); 1882 var ratio = options.ratio || window.devicePixelRatio || 1; 1883 var width = options.width || this._canvas.width / ratio; 1884 var height = options.height || this._canvas.height / ratio; 1885 1886 this._reset(); 1887 image.src = dataUrl; 1888 image.onload = function () { 1889 _this._ctx.drawImage(image, 0, 0, width, height); 1890 }; 1891 this._isEmpty = false; 1892}; 1893 1894SignaturePad.prototype.toDataURL = function (type) { 1895 var _canvas; 1896 1897 switch (type) { 1898 case 'image/svg+xml': 1899 return this._toSVG(); 1900 default: 1901 for (var _len = arguments.length, options = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { 1902 options[_key - 1] = arguments[_key]; 1903 } 1904 1905 return (_canvas = this._canvas).toDataURL.apply(_canvas, [type].concat(options)); 1906 } 1907}; 1908 1909SignaturePad.prototype.on = function () { 1910 this._handleMouseEvents(); 1911 this._handleTouchEvents(); 1912}; 1913 1914SignaturePad.prototype.off = function () { 1915 this._canvas.removeEventListener('mousedown', this._handleMouseDown); 1916 this._canvas.removeEventListener('mousemove', this._handleMouseMove); 1917 document.removeEventListener('mouseup', this._handleMouseUp); 1918 1919 this._canvas.removeEventListener('touchstart', this._handleTouchStart); 1920 this._canvas.removeEventListener('touchmove', this._handleTouchMove); 1921 this._canvas.removeEventListener('touchend', this._handleTouchEnd); 1922}; 1923 1924SignaturePad.prototype.isEmpty = function () { 1925 return this._isEmpty; 1926}; 1927 1928// Private methods 1929SignaturePad.prototype._strokeBegin = function (event) { 1930 this._data.push([]); 1931 this._reset(); 1932 this._strokeUpdate(event); 1933 1934 if (typeof this.onBegin === 'function') { 1935 this.onBegin(event); 1936 } 1937}; 1938 1939SignaturePad.prototype._strokeUpdate = function (event) { 1940 var x = event.clientX; 1941 var y = event.clientY; 1942 1943 var point = this._createPoint(x, y); 1944 var lastPointGroup = this._data[this._data.length - 1]; 1945 var lastPoint = lastPointGroup && lastPointGroup[lastPointGroup.length - 1]; 1946 var isLastPointTooClose = lastPoint && point.distanceTo(lastPoint) < this.minDistance; 1947 1948 // Skip this point if it's too close to the previous one 1949 if (!(lastPoint && isLastPointTooClose)) { 1950 var _addPoint = this._addPoint(point), 1951 curve = _addPoint.curve, 1952 widths = _addPoint.widths; 1953 1954 if (curve && widths) { 1955 this._drawCurve(curve, widths.start, widths.end); 1956 } 1957 1958 this._data[this._data.length - 1].push({ 1959 x: point.x, 1960 y: point.y, 1961 time: point.time, 1962 color: this.penColor 1963 }); 1964 } 1965}; 1966 1967SignaturePad.prototype._strokeEnd = function (event) { 1968 var canDrawCurve = this.points.length > 2; 1969 var point = this.points[0]; // Point instance 1970 1971 if (!canDrawCurve && point) { 1972 this._drawDot(point); 1973 } 1974 1975 if (point) { 1976 var lastPointGroup = this._data[this._data.length - 1]; 1977 var lastPoint = lastPointGroup[lastPointGroup.length - 1]; // plain object 1978 1979 // When drawing a dot, there's only one point in a group, so without this check 1980 // such group would end up with exactly the same 2 points. 1981 if (!point.equals(lastPoint)) { 1982 lastPointGroup.push({ 1983 x: point.x, 1984 y: point.y, 1985 time: point.time, 1986 color: this.penColor 1987 }); 1988 } 1989 } 1990 1991 if (typeof this.onEnd === 'function') { 1992 this.onEnd(event); 1993 } 1994}; 1995 1996SignaturePad.prototype._handleMouseEvents = function () { 1997 this._mouseButtonDown = false; 1998 1999 this._canvas.addEventListener('mousedown', this._handleMouseDown); 2000 this._canvas.addEventListener('mousemove', this._handleMouseMove); 2001 document.addEventListener('mouseup', this._handleMouseUp); 2002}; 2003 2004SignaturePad.prototype._handleTouchEvents = function () { 2005 // Pass touch events to canvas element on mobile IE11 and Edge. 2006 this._canvas.style.msTouchAction = 'none'; 2007 this._canvas.style.touchAction = 'none'; 2008 2009 this._canvas.addEventListener('touchstart', this._handleTouchStart); 2010 this._canvas.addEventListener('touchmove', this._handleTouchMove); 2011 this._canvas.addEventListener('touchend', this._handleTouchEnd); 2012}; 2013 2014SignaturePad.prototype._reset = function () { 2015 this.points = []; 2016 this._lastVelocity = 0; 2017 this._lastWidth = (this.minWidth + this.maxWidth) / 2; 2018 this._ctx.fillStyle = this.penColor; 2019}; 2020 2021SignaturePad.prototype._createPoint = function (x, y, time) { 2022 var rect = this._canvas.getBoundingClientRect(); 2023 2024 return new Point(x - rect.left, y - rect.top, time || new Date().getTime()); 2025}; 2026 2027SignaturePad.prototype._addPoint = function (point) { 2028 var points = this.points; 2029 var tmp = void 0; 2030 2031 points.push(point); 2032 2033 if (points.length > 2) { 2034 // To reduce the initial lag make it work with 3 points 2035 // by copying the first point to the beginning. 2036 if (points.length === 3) points.unshift(points[0]); 2037 2038 tmp = this._calculateCurveControlPoints(points[0], points[1], points[2]); 2039 var c2 = tmp.c2; 2040 tmp = this._calculateCurveControlPoints(points[1], points[2], points[3]); 2041 var c3 = tmp.c1; 2042 var curve = new Bezier(points[1], c2, c3, points[2]); 2043 var widths = this._calculateCurveWidths(curve); 2044 2045 // Remove the first element from the list, 2046 // so that we always have no more than 4 points in points array. 2047 points.shift(); 2048 2049 return { curve: curve, widths: widths }; 2050 } 2051 2052 return {}; 2053}; 2054 2055SignaturePad.prototype._calculateCurveControlPoints = function (s1, s2, s3) { 2056 var dx1 = s1.x - s2.x; 2057 var dy1 = s1.y - s2.y; 2058 var dx2 = s2.x - s3.x; 2059 var dy2 = s2.y - s3.y; 2060 2061 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 }; 2062 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 }; 2063 2064 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1); 2065 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2); 2066 2067 var dxm = m1.x - m2.x; 2068 var dym = m1.y - m2.y; 2069 2070 var k = l2 / (l1 + l2); 2071 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k }; 2072 2073 var tx = s2.x - cm.x; 2074 var ty = s2.y - cm.y; 2075 2076 return { 2077 c1: new Point(m1.x + tx, m1.y + ty), 2078 c2: new Point(m2.x + tx, m2.y + ty) 2079 }; 2080}; 2081 2082SignaturePad.prototype._calculateCurveWidths = function (curve) { 2083 var startPoint = curve.startPoint; 2084 var endPoint = curve.endPoint; 2085 var widths = { start: null, end: null }; 2086 2087 var velocity = this.velocityFilterWeight * endPoint.velocityFrom(startPoint) + (1 - this.velocityFilterWeight) * this._lastVelocity; 2088 2089 var newWidth = this._strokeWidth(velocity); 2090 2091 widths.start = this._lastWidth; 2092 widths.end = newWidth; 2093 2094 this._lastVelocity = velocity; 2095 this._lastWidth = newWidth; 2096 2097 return widths; 2098}; 2099 2100SignaturePad.prototype._strokeWidth = function (velocity) { 2101 return Math.max(this.maxWidth / (velocity + 1), this.minWidth); 2102}; 2103 2104SignaturePad.prototype._drawPoint = function (x, y, size) { 2105 var ctx = this._ctx; 2106 2107 ctx.moveTo(x, y); 2108 ctx.arc(x, y, size, 0, 2 * Math.PI, false); 2109 this._isEmpty = false; 2110}; 2111 2112SignaturePad.prototype._drawCurve = function (curve, startWidth, endWidth) { 2113 var ctx = this._ctx; 2114 var widthDelta = endWidth - startWidth; 2115 var drawSteps = Math.floor(curve.length()); 2116 2117 ctx.beginPath(); 2118 2119 for (var i = 0; i < drawSteps; i += 1) { 2120 // Calculate the Bezier (x, y) coordinate for this step. 2121 var t = i / drawSteps; 2122 var tt = t * t; 2123 var ttt = tt * t; 2124 var u = 1 - t; 2125 var uu = u * u; 2126 var uuu = uu * u; 2127 2128 var x = uuu * curve.startPoint.x; 2129 x += 3 * uu * t * curve.control1.x; 2130 x += 3 * u * tt * curve.control2.x; 2131 x += ttt * curve.endPoint.x; 2132 2133 var y = uuu * curve.startPoint.y; 2134 y += 3 * uu * t * curve.control1.y; 2135 y += 3 * u * tt * curve.control2.y; 2136 y += ttt * curve.endPoint.y; 2137 2138 var width = startWidth + ttt * widthDelta; 2139 this._drawPoint(x, y, width); 2140 } 2141 2142 ctx.closePath(); 2143 ctx.fill(); 2144}; 2145 2146SignaturePad.prototype._drawDot = function (point) { 2147 var ctx = this._ctx; 2148 var width = typeof this.dotSize === 'function' ? this.dotSize() : this.dotSize; 2149 2150 ctx.beginPath(); 2151 this._drawPoint(point.x, point.y, width); 2152 ctx.closePath(); 2153 ctx.fill(); 2154}; 2155 2156SignaturePad.prototype._fromData = function (pointGroups, drawCurve, drawDot) { 2157 for (var i = 0; i < pointGroups.length; i += 1) { 2158 var group = pointGroups[i]; 2159 2160 if (group.length > 1) { 2161 for (var j = 0; j < group.length; j += 1) { 2162 var rawPoint = group[j]; 2163 var point = new Point(rawPoint.x, rawPoint.y, rawPoint.time); 2164 var color = rawPoint.color; 2165 2166 if (j === 0) { 2167 // First point in a group. Nothing to draw yet.
2168 this._reset(); 2169 this._addPoint(point); 2170 } else if (j !== group.length - 1) { 2171 // Middle point in a group. 2172 var _addPoint2 = this._addPoint(point), 2173 curve = _addPoint2.curve, 2174 widths = _addPoint2.widths; 2175 2176 if (curve && widths) { 2177 drawCurve(curve, widths, color); 2178 } 2179 } else { 2180 // Last point in a group. Do nothing. 2181 } 2182 } 2183 } else { 2184 this._reset(); 2185 var _rawPoint = group[0]; 2186 drawDot(_rawPoint); 2187 } 2188 } 2189}; 2190 2191SignaturePad.prototype._toSVG = function () { 2192 var _this2 = this; 2193 2194 var pointGroups = this._data; 2195 var canvas = this._canvas; 2196 var ratio = Math.max(window.devicePixelRatio || 1, 1); 2197 var minX = 0; 2198 var minY = 0; 2199 var maxX = canvas.width / ratio; 2200 var maxY = canvas.height / ratio; 2201 var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); 2202 2203 svg.setAttributeNS(null, 'width', canvas.width); 2204 svg.setAttributeNS(null, 'height', canvas.height); 2205 2206 this._fromData(pointGroups, function (curve, widths, color) { 2207 var path = document.createElement('path'); 2208 2209 // Need to check curve for NaN values, these pop up when drawing 2210 // lines on the canvas that are not continuous. E.g. Sharp corners 2211 // or stopping mid-stroke and than continuing without lifting mouse. 2212 if (!isNaN(curve.control1.x) && !isNaN(curve.control1.y) && !isNaN(curve.control2.x) && !isNaN(curve.control2.y)) { 2213 var attr = 'M ' + curve.startPoint.x.toFixed(3) + ',' + curve.startPoint.y.toFixed(3) + ' ' + ('C ' + curve.control1.x.toFixed(3) + ',' + curve.control1.y.toFixed(3) + ' ') + (curve.control2.x.toFixed(3) + ',' + curve.control2.y.toFixed(3) + ' ') + (curve.endPoint.x.toFixed(3) + ',' + curve.endPoint.y.toFixed(3)); 2214 2215 path.setAttribute('d', attr); 2216 path.setAttribute('stroke-width', (widths.end * 2.25).toFixed(3)); 2217 path.setAttribute('stroke', color); 2218 path.setAttribute('fill', 'none'); 2219 path.setAttribute('stroke-linecap', 'round'); 2220 2221 svg.appendChild(path); 2222 } 2223 }, function (rawPoint) { 2224 var circle = document.createElement('circle'); 2225 var dotSize = typeof _this2.dotSize === 'function' ? _this2.dotSize() : _this2.dotSize; 2226 circle.setAttribute('r', dotSize); 2227 circle.setAttribute('cx', rawPoint.x); 2228 circle.setAttribute('cy', rawPoint.y); 2229 circle.setAttribute('fill', rawPoint.color); 2230 2231 svg.appendChild(circle); 2232 }); 2233 2234 var prefix = 'data:image/svg+xml;base64,'; 2235 var header = '<svg' + ' xmlns="http://www.w3.org/2000/svg"' + ' xmlns:xlink="http://www.w3.org/1999/xlink"' + (' viewBox="' + minX + ' ' + minY + ' ' + maxX + ' ' + maxY + '"') + (' width="' + maxX + '"') + (' height="' + maxY + '"') + '>'; 2236 var body = svg.innerHTML; 2237 2238 // IE hack for missing innerHTML property on SVGElement 2239 if (body === undefined) { 2240 var dummy = document.createElement('dummy'); 2241 var nodes = svg.childNodes; 2242 dummy.innerHTML = ''; 2243 2244 for (var i = 0; i < nodes.length; i += 1) { 2245 dummy.appendChild(nodes[i].cloneNode(true)); 2246 } 2247 2248 body = dummy.innerHTML; 2249 } 2250 2251 var footer = '</svg>'; 2252 var data = header + body + footer; 2253 2254 return prefix + btoa(data); 2255}; 2256 2257SignaturePad.prototype.fromData = function (pointGroups) { 2258 var _this3 = this; 2259 2260 this.clear(); 2261 2262 this._fromData(pointGroups, function (curve, widths) { 2263 return _this3._drawCurve(curve, widths.start, widths.end); 2264 }, function (rawPoint) { 2265 return _this3._drawDot(rawPoint); 2266 }); 2267 2268 this._data = pointGroups; 2269}; 2270 2271SignaturePad.prototype.toData = function () { 2272 return this._data; 2273}; 2274 2275return SignaturePad; 2276 2277}))); 2278; 2279/** 2280 * @file 2281 * JavaScript behaviors for signature pad integration. 2282 */ 2283 2284(function ($, Drupal, debounce) { 2285 2286 'use strict'; 2287 2288 // @see https://github.com/szimek/signature_pad#options 2289 Drupal.webform = Drupal.webform || {}; 2290 Drupal.webform.signaturePad = Drupal.webform.signaturePad || {}; 2291 Drupal.webform.signaturePad.options = Drupal.webform.signaturePad.options || {}; 2292 2293 /** 2294 * Initialize signature element. 2295 * 2296 * @type {Drupal~behavior} 2297 */ 2298 Drupal.behaviors.webformSignature = { 2299 attach: function (context) { 2300 if (!window.SignaturePad) { 2301 return; 2302 } 2303 2304 $(context).find('input.js-webform-signature').once('webform-signature').each(function () { 2305 var $input = $(this); 2306 var value = $input.val(); 2307 var $wrapper = $input.parent(); 2308 var $canvas = $wrapper.find('canvas'); 2309 var $button = $wrapper.find(':button, :submit'); 2310 var canvas = $canvas[0]; 2311 2312 var refresh = function () { 2313 // Set dimensions. 2314 $canvas.attr('width', $wrapper.width()); 2315 $canvas.attr('height', $wrapper.width() / 3); 2316 // Set signature. 2317 signaturePad.clear(); 2318 var value = $input.val(); 2319 if (value) { 2320 signaturePad.fromDataURL(value); 2321 } 2322 }; 2323 2324 // Initialize signature canvas. 2325 var options = $.extend({ 2326 onEnd: function () { 2327 $input.val(signaturePad.toDataURL()); 2328 } 2329 }, Drupal.webform.signaturePad.options); 2330 var signaturePad = new SignaturePad(canvas, options); 2331 2332 // Disable the signature pad when input is disabled or readonly. 2333 if ($input.is(':disabled') || $input.is('[readonly]')) { 2334 signaturePad.off(); 2335 $button.hide(); 2336 } 2337 2338 // Set resize handler. 2339 $(window).on('resize', debounce(refresh, 10)); 2340 2341 // Set reset handler. 2342 $button.on('click', function () { 2343 signaturePad.clear(); 2344 $input.val(''); 2345 $(this).trigger('blur'); 2346 return false;
2347 }); 2348 2349 // Input onchange clears signature pad if value is empty. 2350 // Onchange events handlers are triggered when a webform is 2351 // hidden or shown. 2352 // @see webform.states.js 2353 // @see triggerEventHandlers() 2354 $input.on('change', function () { 2355 setTimeout(refresh, 1); 2356 }); 2357 2358 // Turn signature pad off/on when the input 2359 // is disabled/readonly/enabled. 2360 // @see webform.states.js 2361 $input.on('webform:disabled webform:readonly', function () { 2362 if ($input.is(':disabled') || $input.is('[readonly]')) { 2363 signaturePad.off(); 2364 $button.hide(); 2365 } 2366 else { 2367 signaturePad.on(); 2368 $button.show(); 2369 } 2370 }); 2371 2372 // Make sure that the signature pad is initialized. 2373 setTimeout(refresh, 1); 2374 }); 2375 } 2376 }; 2377 2378})(jQuery, Drupal, Drupal.debounce); 2379; 2380/** 2381 * @file 2382 * JavaScript behaviors for select menu. 2383 */ 2384 2385(function ($, Drupal) { 2386 2387 'use strict'; 2388 2389 /** 2390 * Disable select menu options using JavaScript. 2391 * 2392 * @type {Drupal~behavior} 2393 */ 2394 Drupal.behaviors.webformSelectOptionsDisabled = { 2395 attach: function (context) { 2396 $('select[data-webform-select-options-disabled]', context).once('webform-select-options-disabled').each(function () { 2397 var $select = $(this); 2398 var disabled = $select.attr('data-webform-select-options-disabled').split(/\s*,\s*/); 2399 $select.find('option').filter(function isDisabled() { 2400 return ($.inArray(this.value, disabled) !== -1); 2401 }).attr('disabled', 'disabled'); 2402 }); 2403 } 2404 }; 2405 2406 2407})(jQuery, Drupal); 2408;
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.