1if (typeof formidable_dropzone === "undefined") var formidable_dropzone = []; 2 3(function ($) { 4 5 if ($.fn.Dropzone) { 6 Dropzone.autoDiscover = false; 7 } 8 9 "use strict"; 10 11 $.fn.tagName = function() { 12 return this.prop('tagName').toLowerCase(); 13 }; 14 15 $.fn.formidable = function (settings) { 16 17 var timeout = ''; 18 var formidable = this; 19 var dropzone = 0; 20 var debug = false; 21 22 $.extend(formidable, { 23 24 settings: $.extend({ 25 26 // Do callback on load 27 // Vars: method|function 28 onloadCallback: function() {}, 29 30 // Set locale 31 // Vars: string --> 'en', 'fr', 'nl', ... 32 locale: 'en', 33 34 // Show errors on top of form or underneath each element 35 // Vars: string -> 'form', 'element' 36 errors: 'element', 37 38 // Add additional classes to each error message 39 // Vars: string 40 errorsAdditionalClass: '', 41 42 // Do callback on errors 43 // Vars: method|function 44 errorsCallback: function() {}, 45 46 // Hide form on successful submission 47 // Vars: bolean 48 successHideForm: false, 49 50 // Add additional classes to each error message 51 // Vars: string 52 successAdditionalClass: '', 53 54 // Scroll to top on success 55 // Vars: bolean 56 scrollToTop: true, 57 58 // Scroll animation time 59 // Vars: int 60 scrollTime: 200, 61 62 // Scroll offset from top 63 // Vars: int 64 scrollOffset: 0, 65 66 // Do callback on success 67 // Vars: method|function 68 successCallback: function() {}, 69 70 // Dependencies for the form. 71 // Should be loaded through the controller 72 // Vars: json string 73 dependencies: '', 74 75 // Step variables 76 // Vars: array 77 steps: { 78 /* Appearance */ 79 headerTag: "h3", 80 bodyTag: "section", 81 contentContainerTag: "div", 82 actionContainerTag: "div", 83 stepsContainerTag: "div", 84 cssClass: "has-steps", 85 stepsOrientation: 0, 86 87 /* Templates */ 88 titleTemplate: '<span class="number">#index#.</span> #title#', 89 loadingTemplate: '<span class="spinner"></span> #text#', 90 91 /* Behaviour */ 92 autoFocus: false, 93 enableAllSteps: false, 94 enableKeyNavigation: false, 95 enablePagination: false, 96 suppressPaginationOnFocus: true, 97 enableContentCache: true,
98 enableCancelButton: false, 99 enableFinishButton: false, 100 preloadContent: false, 101 showFinishButtonAlways: false, 102 forceMoveForward: false, 103 saveState: false, 104 startIndex: 0, 105 106 /* Transition Effects */ 107 transitionEffect: 'fade', 108 transitionEffectSpeed: 200, 109 110 /* Events */ 111 onStepChanging: function (event, currentIndex, newIndex) { return true; }, 112 onStepChanged: function (event, currentIndex, priorIndex) { }}, 113 onCanceled: function (event) { }, 114 onFinishing: function (event, currentIndex) { return true; }, 115 onFinished: function (event, currentIndex) { }, 116 117 /* Labels */ 118 labels: { 119 cancel: "Cancel", 120 current: "current step:", 121 pagination: "Pagination", 122 finish: "Finish", 123 next: "Next", 124 previous: "Previous", 125 loading: "Loading ..." 126 } 127 128 }, settings), 129 130 init: function() 131 { 132 // initialize formObj 133 this.formObj = $('form', this); 134 135 // set locale 136 this.setLocale(); 137 138 // add honeypot 139 this.addHoneypot(); 140 141 // add resolution 142 this.setResolution(); 143 144 // enable steps 145 this.enableSteps(); 146 147 // enable masking 148 this.enableMasking(); 149 150 // enable counter 151 this.enableCounter(); 152 153 // enable ranges 154 this.enableRanges(); 155 156 // enable sigantures 157 this.enableSignatures(); 158 159 // enable others for options 160 this.enableOthers(); 161 162 // enable dropzones 163 this.enableDropzones(); 164 165 // load dependencies 166 this.loadDependencies(); 167 168 // do callback 169 this.doCallbackOnload(); 170 171 // on submit! 172 this.formObj.off('submit').on('submit', this.formSubmit.bind(this)); 173 }, 174 175 formSubmit: function(e) 176 { 177 // stop form from submitting 178 e.preventDefault(); 179 180 // capture data 181 var formData = this.captureData(); 182 183 // disable fields 184 this.startProcess(); 185 186 return $.ajax({ 187 url: this.formObj.attr('action'), 188 data: formData, 189 method: 'post', 190 dataType: 'json', 191 192 // when using formdata 193 contentType: false, 194 processData: false, 195 cache: false, 196 197 beforeSend: this.beforeFormSend.bind(this), 198 error: this.formSubmitError.bind(this), 199 success: this.formSubmitSuccess.bind(this), 200 complete: this.formSubmitComplete.bind(this) 201 }); 202 }, 203 204 beforeFormSend: function() 205 { 206 formidable.removeClass('was-validated'); 207 formidable.find(':input').removeClass('is-invalid is-valid'); 208 formidable.find('.invalid-feedback, .valid-feedback').remove(); 209 formidable.find('[data-formidable-message]').empty().hide(); 210 }, 211 212 formSubmitError: function(response) 213 { 214 var data = response.responseJSON; 215 216 if (data.message) { 217 this.formMessage(data.message); 218 } 219 220 if (data.errors) { 221 var self = this; 222 var formMessages = []; 223 224 // support both error formats: 225 // keyed: { field_handle: ["message", ...] } 226 // ErrorList: [ { field: { element: "field_handle" }, message: "message" } ] 227 $.each(data.errors, function(key, err) { 228 var handle = null; 229 var message = null; 230 231 if (err !== null && typeof err === 'object' && typeof err.message !== 'undefined') { 232 message = err.message; 233 if (err.field && err.field.element) { 234 handle = err.field.element; 235 } 236 } else { 237 handle = key; 238 message = $.isArray(err) ? err.join('<br />') : err; 239 } 240 241 if (self.settings.errors == 'form' || !handle || handle == 'form') { 242 formMessages.push(message); 243 244 if (handle) { 245 var holder = $('[data-formidable-handle="'+handle+'"]', formidable); 246 if (typeof handle === 'object') { 247 holder = handle; 248 } 249 holder.find(':input:not(.no-valid)').addClass('is-invalid'); 250 } 251 252 } else { 253 self.elementMessage(handle, message); 254 } 255 }); 256 257 if (formMessages.length) { 258 self.formMessage({type: 'error', message: formMessages}); 259 } 260 } 261 }, 262 263 formSubmitSuccess: function(data) 264 { 265 266 if (data.redirect) { 267 268 // make sure to start process 269 this.startProcess(); 270 271 window.location.href = data.redirect; 272 return; 273 } 274 275 if (data.message) {
276 this.formMessage({type: 'success', message: data.message}); 277 } 278 }, 279 280 formSubmitSuccessStep: function(data) 281 { 282 // set as validated 283 this.setFormValidated(data.step); 284 285 // reload ccm_token 286 this.resetToken(data.ccm_token); 287 288 // refresh elements 289 this.resetRecaptcha(); 290 291 // enable fields 292 this.stopProcess(); 293 294 // scroll to top of form 295 this.scrollToTop(); 296 }, 297 298 formValidateCurrentStep: function(noError) 299 { 300 // capture data 301 var formData = this.captureData(); 302 303 // add current step data 304 formData.append('validate_formidable_step', $('[data-formidable-step].current', formidable).data('formidable-step')); 305 306 // disable fields 307 this.startProcess(); 308 309 return $.ajax({ 310 url: this.formObj.attr('action'), 311 data: formData, 312 method: 'post', 313 dataType: 'json', 314 // make sure this isn't async. 315 async: false, 316 317 // when using formdata 318 contentType: false, 319 processData: false, 320 cache: false, 321 322 beforeSend: this.beforeFormSend.bind(this), 323 error: !!noError?function(){}:this.formSubmitError.bind(this), 324 success: this.formSubmitSuccessStep.bind(this), 325 complete: this.stopProcess.bind(this), 326 }); 327 }, 328 329 formClearCurrentStep: function() 330 { 331 $(':input', $('[data-formidable-step].current', formidable)).each(function(i, field) { 332 $(field).removeClass('is-valid no-valid is-invalid is-invalid'); 333 if ($(field).attr('type') == 'radio' || $(field).attr('type') == 'checkbox') { 334 $(field).prop('checked', false); 335 $(field).closest('label').removeClass('active'); 336 } 337 else $(field).val(''); 338 }); 339 this.formValidateCurrentStep(true); // true for no errors 340 }, 341 342 formSubmitComplete: function(response, status) 343 { 344 var data = response.responseJSON; 345 346 // set as validated 347 this.setFormValidated(); 348 349 // reload ccm_token 350 this.resetToken(data.ccm_token); 351 352 // refresh elements 353 this.resetRecaptcha(); 354 355 // enable fields 356 this.stopProcess(); 357 358 // scroll to top of form 359 this.scrollToTop(); 360 361 // reload dependencies 362 this.reloadDependencies(); 363 364 // do callback 365 if (status == 'error') { 366 this.doCallbackError(response); 367 } 368 if (status == 'success') { 369 370 // clear form 371 this.resetForm(); 372 373 // hide form 374 this.hideForm(); 375 376 // data layer (enhanced) 377 this.doEnhancedConversion(response); 378 379 // do callback 380 this.doCallbackSuccess(response); 381 } 382 }, 383 384 captureData: function() 385 { 386 // before we capture all data, we need to process the textarea (wysiwyg) 387 if (typeof(CKEDITOR) != 'undefined') { 388 for(var i in CKEDITOR.instances) { 389 CKEDITOR.instances[i].updateElement(); 390 } 391 } 392 // now grab all the data. 393 var formData = new FormData(this.formObj[0]); 394 return formData; 395 }, 396 397 setLocale: function() 398 { 399 // set locale for moment.js 400 if (window.moment) { 401 moment.locale(this.settings.locale.substr(0, 2)); 402 } 403 }, 404 405 setResolution: function() 406 { 407 var resolution = $('<input>').attr({ 408 'type': 'hidden', 409 'name': 'resolution', 410 'id': 'resolution', 411 'value': screen.width + 'x' + screen.height 412 }); 413 this.formObj.prepend(resolution); 414 }, 415 416 addHoneypot: function() 417 { 418 var website_url = $('<input>').attr({ 419 'type': 'text', 420 'name': 'website_url', 421 'id': 'website_url', 422 'value': '' 423 }).attr('style', 'display:none'); 424 this.formObj.prepend(website_url); 425 426 var homepage = $('<input>').attr({ 427 'type': 'text', 428 'name': 'homepage', 429 'id': 'homepage', 430 'value': '' 431 }).attr('style', 'display:none'); 432 this.formObj.prepend(homepage); 433 }, 434 435 enableSteps: function() 436 { 437 if ($.fn.steps) { 438 if (this.formObj.find('[data-formidable-step]').length <= 0) { 439 return; 440 } 441 442 this.settings.steps.onStepChanging = function (e, currentStep, newStep) 443 { 444 // Always allow previous action even if the current form is not val
444id! 445 if (newStep < currentStep) { 446 this.formClearCurrentStep(); 447 this.scrollToTop(1); 448 return true; 449 } 450 451 // now handle the next step! 452 var promise = this.formValidateCurrentStep(); 453 if (promise.responseJSON.success) { 454 this.stopProcess(); 455 this.scrollToTop(1); 456 return true; 457 } 458 return false; 459 460 }.bind(this); 461 462 this.settings.steps.onFinishing = function (e, currentStep) 463 { 464 // now handle the next step! 465 var promise = this.formValidateCurrentStep(); 466 if (promise.responseJSON.success) { 467 this.formSubmitSuccess(promise.responseJSON); 468 this.formSubmitComplete(promise, 'success'); 469 return true; 470 } 471 return false; 472 473 }.bind(this); 474 475 var steps = this.formObj.steps(this.settings.steps); 476 477 this.formObj.find('[data-formidable-step-buttons] [data-formidable-step-previous]').on('click', function() { 478 steps.steps('previous'); 479 }); 480 this.formObj.find('[data-formidable-step-buttons] [data-formidable-step-next]').on('click', function() { 481 if ($('section[data-formidable-step]:last').data('formidable-step') != $(this).closest('section[data-formidable-step]').data('formidable-step')) { 482 return steps.steps('next'); 483 } 484 steps.steps('finish'); 485 }); 486 } 487 }, 488 489 enableMasking: function() 490 { 491 // mask for "normal" fields 492 if ($.fn.mask) { 493 $('[data-mask]').each(function(i, f) { 494 $(f).mask($(f).attr('data-mask')); 495 }); 496 } 497 // mask / placeholder for selectboxes 498 $('[data-placeholder]').each(function(i, f) { 499 var attr = {disabled: true, hidden: true, selected: true}; 500 if ($(f).attr('multiple')) { 501 attr = {disabled: true, selected: false}; 502 } 503 $(f).find('option:first').prop(attr); 504 }) 505 }, 506 507 enableCounter: function() 508 { 509 $('[data-range-type]', formidable).each(function(i, f) { 510 var typ = $(f).attr('data-range-type'); 511 var min = parseFloat($(f).attr('data-range-min')); 512 var max = parseFloat($(f).attr('data-range-max')); 513 var range = '<div data-range><span data-count></span>/<span data-max>'+max+'</span></div>'; 514 $(f).wrapAll('<div data-formidable-countable>').after(range); 515 var tag = $(f).tagName().toLowerCase(); 516 // for select, checkbox and radio... 517 if ($.inArray(tag, ['select']) > -1 || $.inArray($(f).attr('type'), ['radio', 'checkbox']) > -1) { 518 $(f).on('change', function() { 519 var range = $(f).closest('[data-formidable-countable]').find('[data-count]'); 520 var checked = parseFloat($(":selected", $(f)).length); 521 range.text(checked); 522 $(f).find(':not(:selected)').prop('disabled', false); 523 if (checked >= max) { 524 $(f).find(':not(:selected)').prop('disabled', true); 525 } 526 }).trigger('change'); 527 } 528 // for number field 529 else if ($.inArray($(f).attr('type'), ['number']) > -1) { 530 var range = $(f).closest('[data-formidable-countable]').find('[data-count]'); 531 range.text(min); 532 } 533 // for file field 534 else if ($.inArray(typ, ['files']) > -1) { 535 $('[data-additional-file]', $(f)).on('click', function() { 536 $('.file.hide:first', $(f)).removeClass('hide'); 537 if ($('.file.hide', $(f)).length <= 0) { 538 $('[data-additional-file]', $(f)).prop('disabled', true); 539 } 540 }); 541 } 542 // for others 543 else { 544 if ($.fn.simplyCountable) { 545 $(f).simplyCountable({counter: $('[data-count]', $(f).closest('[data-formidable-countable]')), countType: typ, maxCount: max}); 546 } 547 } 548 }); 549 }, 550 551 enableRanges: function() 552 { 553 $('input[type="range"]', formidable).each(function(i, f) {
554 var span = $('[data-range-value="'+$(this).attr('id')+'"]'); 555 span.html($(f).val()); 556 557 $(f).on('input', function() { 558 span.html($(f).val()); 559 }); 560 }); 561 }, 562 563 enableSignatures: function() 564 { 565 $('[data-signature-type]', formidable).each(function(i, f) { 566 var container = $(f); 567 var signature = $('div.signature', container); 568 var textarea = $('textarea', container); 569 570 var options = $.extend({ 571 'width': 'ratio', 572 'height': 'ratio', 573 'sizeRatio': 2, // only used when height = 'ratio', 574 'color': '#000', 575 'background-color': '#fff', 576 'decor-color': '#eee', 577 'lineWidth': 0, 578 'minFatFingerCompensation': -10, 579 'showUndoButton': false, 580 'readOnly': false, 581 'data': [], 582 'signatureLine': false, 583 'afterload': function() {} 584 }, JSON.parse('{'+container.data('options').replace(/\'/g, '"')+'}')); 585 586 var signature = signature.jSignature(options).on('change', function() { 587 textarea.text("data:" + $(this).jSignature("getData", "svgbase64").join(",") ); 588 }); 589 590 // Add image if already there! 591 // Disabled for now. It's not nice to edit a signature... 592 // if (textarea.text().length > 0) signature.jSignature("setData", textarea.text()); 593 594 $('[data-signature="clear"]', container).on('click', function (e) { 595 if (!container.is(':disabled')) { 596 signature.jSignature('reset'); 597 textarea.text(''); 598 } 599 }); 600 }); 601 }, 602 603 // enable dropzones 604 enableDropzones: function() 605 { 606 $('.formidable-dropzone', formidable).each(function(i, e) { 607 $('div.dropzone-box', $(e)).formidableDropzone(this); 608 }.bind(this)); 609 }, 610 611 enableOthers: function() 612 { 613 $('[data-option-other]', formidable).each(function(i, f) { 614 var element = $(f).find('[type="checkbox"], [type="radio"], select'); 615 element.on('change', function() { 616 var value = element.val(); 617 if ($.inArray(element.attr('type'), ['radio', 'checkbox']) > -1) { 618 value = []; 619 $(f).find(':is(:checked)').each(function(i, e) { 620 value.push($(e).val()); 621 }); 622 } 623 var other = $(f).find('div.option-other'); 624 if ($.inArray('other_value', $.isArray(value)?value:[value]) > -1) { 625 other.addClass('enabled').find(':input').prop({disabled: false}); 626 } 627 else { 628 other.removeClass('enabled').find(':input').val('').prop({disabled: true}); 629 } 630 }).trigger('change'); 631 }) 632 }, 633 634 // run dependencies 635 loadDependencies: function() 636 { 637 $.each(this.settings.dependencies.selectors, function(d, dependency) { 638 $(dependency.selector, formidable).on('change keyup', function() { 639 clearTimeout(timeout); 640 timeout = setTimeout(formidable.checkDependencyRule(dependency.id), 200); 641 }).trigger('change'); 642 }); 643 }, 644
645 // reload dependencies 646 reloadDependencies: function() 647 { 648 this.settings.dependencies.reload = true; 649 $.each(this.settings.dependencies.selectors, function(d, dependency) { 650 formidable.checkDependencyRule(dependency.id); 651 }); 652 this.settings.dependencies.reload = false; 653 }, 654 655 // resolve a dependency selector to the actual :input element(s), 656 // also when the handle is on a wrapper (e.g. checkbox/Accept elements) 657 getDependencyInput: function(handle) 658 { 659 var elements = $(handle, formidable); 660 var input = elements.filter(':input'); 661 elements.not(':input').each(function(i, element) { 662 input = input.add($(element).find(':input')); 663 }); 664 return input; 665 }, 666 667 checkDependencyRule: function(id) 668 { 669 for (let rs = 0; rs < this.settings.dependencies.rules.length; rs++) { 670 671 var rules = this.settings.dependencies.rules[rs]; 672 673 // skip rules... 674 if ($.inArray(rules.id, id) <= -1) { 675 continue; 676 } 677 678 var selector = $(rules.selector, formidable); 679 680 var inverse = false; 681 682 // first go back to basics to it's "default" state. 683 // example: if show is action, default is hidden. 684 for (let r = 0; r < rules.rules.length; r++) { 685 var rule = rules.rules[r]; 686 for (let a = 0; a < rule.actions.length; a++) { 687 var action = rule.actions[a]; 688 if (this.settings.dependencies.reload && $.inArray(action[0], ['disable']) <= -1) { 689 continue; 690 } 691 switch (action[0]) { 692 case 'show': 693 selector.prop('disabled', true).closest('[data-formidable-handle]').hide(); 694 break; 695 case 'hide': 696 selector.prop('disabled', false).closest('[data-formidable-handle]').show(); 697 inverse = true; 698 break; 699 case 'disable': 700 selector.prop('disabled', false); 701 break; 702 case 'enable': 703 selector.prop('disabled', true); 704 inverse = true; 705 break; 706 case 'class': 707 formidable.removeElementClass(selector, action[1]); 708 break; 709 case 'value': 710 formidable.setElementValue(selector, ''); 711 break; 712 } 713 } 714 } 715 716 for (let r = 0; r < rules.rules.length; r++) { 717 718 var rule = rules.rules[r]; 719 720 if (debug) console.log('---- RuleNo: '+r+' ----'); 721 if (debug) console.log('Selector: '+rules.selector); 722 723 var match = true; 724 725 for (let s = 0; s < rule.selectors.length; s++) { 726 727 var line = rule.selectors[s]; 728 729 var input = formidable.getDependencyInput(line.handle); 730 if (!input.length) { 731 continue; 732 } 733 734 var type = input.eq(0).tagName(); 735 if (type == 'input') { 736 type = input.eq(0).attr('type'); 737 } 738 739 var value = []; 740 switch (type) { 741 case 'select': 742 input.each(function(i, field) { 743 var fieldValue = $(field).val(); 744 if ($.isArray(fieldValue)) { 745 $.each(fieldValue, function(v, item) { 746 if (item) { 747 value.push(String(item).toLowerCase()); 748 } 749 }); 750 } 751 else if (fieldValue) { 752 value.push(String(fieldValue).toLowerCase()); 753 } 754 }); 755 break; 756 case 'checkbox': 757 case 'radio': 758 $.each(input.filter(':checked'), function(c, check) { 759 value[c] = String($(check).val()).toLowerCase(); 760 }); 761 break; 762 default: 763 if (input.eq(0).val()) { 764 value[0] = String(input.eq(0).val()).toLowerCase(); 765 } 766 break; 767 } 768 769 if (debug) console.log('Rule selector: '+line.handle); 770 if (debug) console.log('Value: '+(value.length?value:'(empty)')+' ('+value.length+')'); 771 772 for (let c = 0; c < line.conditions.length; c++) { 773 774 var cond = line.conditions[c]; 775 var compare_value = cond.value?cond.value.toLowerCase():''; 776 777 if (debug) console.log('---- ConditionRuleNo: '+c+' ----'); 778 779 // change comparison to empty / not empty for any and non; 780 if (compare_value == 'any') cond.comparison = 'not_empty'; 781 if (compare_value == 'non') cond.comparison = 'empty'; 782 783 if (debug) console.log('Condition: '+cond.c
783omparison); 784 if (debug) console.log('Compare to: '+compare_value); 785 786 switch (cond.comparison) { 787 case 'empty': 788 // not empty is false 789 if (value.length > 0) match = false; 790 break; 791 case 'not_empty': 792 // empty is false 793 if (value.length <= 0) match = false; 794 break; 795 case 'equals': 796 // not equals is false 797 if (compare_value == 'any' && value.length <= 0) match = false; 798 else if (compare_value == 'non' && value.length > 0) match = false; 799 else if ($.inArray(compare_value, value) <= -1) match = false; 800 break; 801 case 'not_equals': 802 // equals is false 803 if ($.inArray(compare_value, value) > -1) match = false; 804 break; 805 case 'contains': 806 // not contains is false 807 for (let i = 0; i < value.length; i++) { 808 if (value[i].search(compare_value) <= -1) { 809 match = false; 810 break; 811 } 812 } 813 break; 814 case 'not_contains': 815 // contains is false 816 for (let i = 0; i < value.length; i++) { 817 if (value[i].search(compare_value) >= -1) { 818 match = false; 819 break; 820 } 821 } 822 break; 823 } 824 } 825 } 826 827 if (debug) console.log('Match: '+(match?'yes':'no')); 828 829 if (inverse) { 830 if (debug) console.log('Inversed?: '+(inverse?'yes':'no')); 831 match = !match; 832 } 833 834 if (!match) { 835 continue; 836 } 837 838 for (let a = 0; a < rule.actions.length; a++) { 839 var action = rule.actions[a]; 840 if (this.settings.dependencies.reload && $.inArray(action[0], ['disable']) <= -1) { 841 continue; 842 } 843 if (debug) console.log('Action '+action[0]); 844 switch (action[0]) { 845 case 'show': 846 if (inverse) { 847 selector.prop('disabled', true).closest('[data-formidable-handle]').hide(); 848 break; 849 } 850 selector.prop('disabled', false).closest('[data-formidable-handle]').show(); 851 break; 852 case 'hide': 853 if (inverse) { 854 selector.prop('disabled', false).closest('[data-formidable-handle]').show(); 855 break; 856 } 857 selector.prop('disabled', true).closest('[data-formidable-handle]').hide(); 858 break; 859 case 'disable': 860 if (inverse) { 861 selector.prop('disabled', false); 862 break; 863 } 864 selector.prop('disabled', true); 865 break; 866 case 'enable': 867 if (inverse) { 868 selector.prop('disabled', true); 869 break; 870 } 871 selector.prop('disabled', false); 872 break; 873 case 'class': 874 formidable.addElementClass(selector, action[1]); 875 if (debug) console.log('Class: '+action[1]); 876 break; 877 case 'value': 878 formidable.setElementValue(selector, action[1]); 879 if (debug) console.log('Value: '+action[1]); 880 break; 881 case 'clear': 882 formidable.setElementValue(selector); 883 break; 884 } 885 } 886 if (debug) console.log('------------------'); 887 } 888 } 889 }, 890 891 // set field value 892 setElementValue: function(selector, value) 893 { 894 var type = selector.tagName(); 895 if (type == 'input') { 896 type = selector.attr('type'); 897 } 898 switch (type) { 899 case 'select': 900 selector.prop('selectedIndex', 0); 901 if (value.length) { 902 selector.val(value); 903 } 904 break; 905 case 'checkbox': 906 case 'radio': 907 selector.prop('checked', false); 908 if (value.length) { 909 for (let i = 0; i < selector.length; i++) { 910 var s = selector[i]; 911 $(s).prop('checked', false); 912 if ($(s).val() == value) $(s).prop('checked', true); 913 }; 914 } 915 break; 916 default: 917 selector.val(value); 918 break; 919 } 920 selector.trigger('change'); 921 }, 922 923 // add field class
924 addElementClass: function(selector, className, todo) 925 { 926 selector.addClass(className); 927 }, 928 929 // remove field class 930 removeElementClass: function(selector, className, todo) 931 { 932 selector.removeClass(className); 933 }, 934 935 // if form is validated 936 setFormValidated: function(step) 937 { 938 // Disabled for now because i don't want to use :valid or :invalid 939 // formidable.addClass('was-validated'); 940 if (!!step) { 941 $('[data-formidable-step="' + step + '"]', formidable).find(':input:not(.is-invalid):not(.no-valid)').addClass('is-valid'); 942 return; 943 } 944 $('[data-formidable-type]', formidable).find(':input:not(.is-invalid):not(.no-valid)').addClass('is-valid'); 945 }, 946 947 // start 'busy' process 948 startProcess: function() 949 { 950 formidable.addClass('processing').find(':input').attr('disabled', true); 951 }, 952 953 // stop 'busy' process 954 stopProcess: function() 955 { 956 formidable.removeClass('processing').find(':input').attr('disabled', false); 957 }, 958 959 setSendable: function(sendable) 960 { 961 this.sendable = sendable; 962 }, 963 964 getSendable: function() 965 { 966 return this.sendable; 967 }, 968 969 // hide form 970 hideForm: function() 971 { 972 if (!!this.settings.successHideForm) { 973 this.formObj.remove(); 974 } 975 }, 976 977 resetForm: function() 978 { 979 $('[data-formidable-type]', formidable).find(':input').removeClass('is-valid no-valid is-invalid is-invalid'); 980 $('div.invalid-feedback, div.valid-feedback', formidable).remove(); 981 982 // reset signatures canvasses 983 $('[data-signature-type]', formidable).each(function(i, f) { 984 $('div.signature', $(f)).jSignature('reset'); 985 }); 986 987 this.formObj.trigger('reset'); 988 }, 989 990 resetRecaptcha: function() 991 { 992 // reset recaptcha 993 if (typeof refresh_recaptcha !== "undefined") { 994 refresh_recaptcha(); 995 } 996 }, 997 998 // callback called onload of the form 999 doCallbackOnload: function() 1000 { 1001 if (!!this.settings.onloadCallback && $.isFunction(this.settings.onloadCallback)) { 1002 this.settings.onloadCallback.call(this, formidable); 1003 } 1004 }, 1005 1006 // callback called when errors occure on submit 1007 doCallbackError: function(response) 1008 { 1009 if (this.settings.errorsCallback && $.isFunction(this.settings.errorsCallback)) { 1010 this.settings.errorsCallback.call(this, this, response); 1011 } 1012 }, 1013 1014 // callback called when form is successfully submitted 1015 doCallbackSuccess: function(response) 1016 { 1017 if (this.settings.successCallback && $.isFunction(this.settings.successCallback)) { 1018 this.settings.successCallback.call(this, this, response); 1019 } 1020 }, 1021 1022 doEnhancedConversion: function(response) 1023 { 1024 var data = response.responseJSON; 1025 if (data.enhanced_conversion) { 1026 window.dataLayer = window.dataLayer || []; 1027 dataLayer.push($.parseJSON(data.enhanced_conversion)); 1028 } 1029 }, 1030 1031 // reload token data in form 1032 resetToken: function(ccm_token) 1033 { 1034 $('[id="ccm_token"]', formidable).val(ccm_token); 1035 }, 1036 1037 // move to top 1038 scrollToTop: function() 1039 { 1040 if (!!this.settings.scrollToTop) { 1041 $('html, body').animate({ 1042 scrollTop: formidable.offset().top + this.settings.scrollOffset 1043 }, this.settings.scrollTime); 1044 } 1045 }, 1046 1047 // show message (notifications and errors) on top of form 1048 formMessage: function(message) 1049 { 1050 var type = 'error'; 1051 if (!!message.type) { 1052 type = message.type; 1053 } 1054 if (!!message.message) { 1055 message = message.message; 1056 } 1057 if ($.isArray(message)) { 1058 message = message.join('<br />'); 1059 } 1060 1061 var holder = $('[data-formidable-message]', formidable); 1062 holder.attr('class', '').html(message); 1063 if (type == 'error') { 1064 holder.addClass('alert alert-danger'); 1065 if (this.settings.errorsAdditionalClass) { 1066 holder.addClass(this.settings.errorsAdditionalClass); 1067 } 1068 } 1069 if (type == 'info') { 1070 holder.addClass('alert alert-info'); 1071 } 1072 if (type == 'success') { 1073 holder.addClass('alert alert-success'); 1074 if (this.settings.successAdditionalClass) { 1075 holder.addClass(this.settings.successAdditionalClass); 1076 } 1077 } 1078 holder.show(); 1079 }, 1080 1081 // show message (erros) after the specific element 1082 elementMessage: function(handle, message) 1083 { 1084 var type = 'error'; 1085 if (!!message.type) { 1086 type = message.type; 1087 } 1088 if (!!message.message) { 1089 message = message.message; 1090 } 1091 if ($.isArray(message)) { 1092 message = message.join('<br />'); 1093 } 1094 1095 var holder = $('[data-formidable-handle="'+handle+'"]', formidable); 1096 if (typeof handle === 'object') { 1097 holder = handle; 1098 } 1099 1100 var elements = holder.find(':input:not(.no-valid)'); 1101 if (type == 'error') { 1102 elements.addClass('is-invalid'); 1103 if (this.settings.errorsAdditionalClass) { 1104 elements.addClass(this.settings.errorsAdditionalClass); 1105 } 1106 } 1107 else { 1108 elements.addClass('is-valid'); 1109 if (this.settings.successAdditionalClass) { 1110 elements.addClass(this.settings.successAdditionalClass); 1111 } 1112 } 1113 1114 // show message with element 1115 if (this.settings.errors == 'element') { 1116 if (message.length > 0) { 1117 var msg = $('<div>'); 1118 msg.html(message); 1119 if (type == 'error') { 1120 msg.addClass('invalid-feedback'); 1121 if (this.settings.errorsAdditionalClass) { 1122 msg.addClass(this.settings.errorsAdditionalClass); 1123 } 1124 } 1125 else { 1126 msg.addClass('valid-feedback'); 1127 if (this.settings.successAdditionalClass) { 1128 msg.addClass(this.settings.successAdditionalClass); 1129 } 1130 } 1131 holder.append(msg); 1132 } 1133 } 1134 }, 1135 1136 getFormObject: function() 1137 { 1138 return this.formObj; 1139 }, 1140 1141 addDropzoneUpload: function() 1142 { 1143 dropzone++; 1144 this.startProcess(); 1145 }, 1146 1147 removeDropzoneUpload: function() 1148 { 1149 dropzone--; 1150 if (dropzone <= 0) { 1151 this.stopProcess(); 1152 } 1153 } 1154 1155 }); 1156
1157 formidable.init(); 1158 1159 return formidable; 1160 }; 1161 1162 1163 $.fn.formidableDropzone = function (formidable) { 1164 1165 var dropzone = this; 1166 1167 $.extend(dropzone, { 1168 1169 settings: $.extend({ 1170 1171 // set max uploaded files 1172 maxFiles: 10, 1173 1174 // set accepted extensions 1175 acceptedFiles: '.jpg,.jpeg,.png,.doc,.docx,.xls,.xlsx,.pdf', 1176 1177 // set max uploaded filesize 1178 maxFilesize: 5 // MB 1179 1180 }), 1181 1182 formidable: formidable, 1183 dropzoneObj: null, 1184 1185 init: function() 1186 { 1187 this.dropzoneDiv = $(this); 1188 this.formObj = this.formidable.getFormObject(); 1189 1190 this.settings.action = this.formObj.attr('action');; 1191 1192 if (this.dropzoneDiv.data('range-max')) { 1193 this.settings.maxFiles = this.dropzoneDiv.data('range-max'); 1194 } 1195 if (this.dropzoneDiv.data('extensions')) { 1196 this.settings.acceptedFiles = this.dropzoneDiv.data('extensions'); 1197 } 1198 if (this.dropzoneDiv.data('filesize')) { 1199 this.settings.maxFilesize = this.dropzoneDiv.data('filesize'); 1200 } 1201 1202 // do callback 1203 this.doDropzone(); 1204 }, 1205 1206 doDropzone: function() 1207 { 1208 this.dropzoneDiv.dropzone({ 1209 url: this.settings.action.replace(/submit/, 'upload'), 1210 maxFiles: this.settings.maxFiles, 1211 acceptedFiles: this.settings.acceptedFiles, 1212 maxFilesize: this.settings.maxFilesize, 1213 createImageThumbnails: true, 1214 autoProcessQueue: true, 1215 uploadMultiple: false, 1216 addRemoveLinks: false, 1217 previewTemplate: this.dropzoneDiv.next('[id^="dropzoneTemplate"]').html(), 1218 init: this.initialize.bind(this), 1219 sending: this.sending.bind(this), 1220 addedfiles: this.add.bind(this), 1221 removedfile: this.removeFile.bind(this), 1222 processing: this.processing.bind(this), 1223 error: this.error.bind(this), 1224 success: this.success.bind(this), 1225 complete: this.complete.bind(this) 1226 }); 1227 this.dropzoneObj = this.dropzoneDiv.get(0).dropzone; 1228 1229 this.mocks(); 1230 }, 1231 1232 initialize: function() 1233 { 1234 1235 }, 1236 1237 sending: function(file, json, formData) 1238 { 1239 formData.append('formID', $('[id="formID"]', this.formidable.getFormObject()).val()); 1240 formData.append('bID', $('[id="bID"]', this.formidable.getFormObject()).val()); 1241 formData.append('ccm_token', $('[id="ccm_token"]', this.formidable.getFormObject()).val()); 1242 formData.append('handle', this.dropzoneDiv.data('element-handle')); 1243 }, 1244 1245 add: function(file) 1246 { 1247 this.removeErrors(); 1248 }, 1249 1250 processing: function(file) 1251 { 1252 this.formidable.addDropzoneUpload(); 1253 this.removeErrors(); 1254 }, 1255 1256 error: function(file, message) 1257 { 1258 this.formidable.stopProcess(); 1259 this.dropzoneObj.removeFile(file); 1260 this.formidable.elementMessage(this.dropzoneDiv.parent(), message); 1261 }, 1262 1263 removeFile: function(file) 1264 { 1265 var response = false; 1266 1267 if (file.xhr && file.xhr.responseText.length > 0) { 1268 response = $.parseJSON(file.xhr.responseText); 1269 } 1270 1271 if (!!response) { 1272 1273 this.doRemoveFile(response.file); 1274 } 1275 1276 // do native dropzone remove 1277 file.previewElement.remove(); 1278 1279 if ($('.dropzone-item', this.dropzoneDiv).length <= 0) { 1280 this.dropzoneDiv.removeClass('dz-started'); 1281 } 1282 }, 1283 1284 doRemoveFile: function(file) 1285 { 1286 var formData = new FormData(); 1287 1288 formData.append('formID', $('[id="formID"]', this.formidable.getFormObject()).val()); 1289 formData.append('bID', $('[id="bID"]', this.formidable.getFormObject()).val()); 1290 formData.append('ccm_token', $('[id="ccm_token"]', this.formidable.getFormObject()).val()); 1291 formData.append('handle', this.dropzoneDiv.data('element-handle')); 1292 formData.append('file', file); 1293 1294 $.ajax({ 1295 url: this.settings.action.replace(/submit/, 'delete'), 1296 data: formData, 1297 method: 'post', 1298 dataType: 'json', 1299 1300 // when using formdata 1301 contentType: false, 1302 processData: false, 1303 cache: false, 1304 1305 error: this.removeFileError.bind(this), 1306 success: this.removeFileComplete.bind(this) 1307 }); 1308 }, 1309 1310 removeFileComplete: function(response) 1311 { 1312 $('[type="hidden"][value="'+response.file+'"]', this.dropzoneDiv).remove(); 1313 }, 1314 1315 removeFileError: function(response) 1316 { 1317 var data = response.responseJSON; 1318 1319 if (data.errors) { 1320 // set all errors 1321 if (this.settings.errors == 'form') { 1322 var errors = []; 1323 $.each(data.errors, function(handle, err) { 1324 if ($.isArray(err)) { 1325 err = err.join('<br />'); 1326 } 1327 errors.push(err); 1328 }); 1329 this.formidable.formMessage({type: 'error', message: errors}); 1330 } 1331 else { 1332 $.each(data.errors, function(handle, message) { 1333 this.formidable.elementMessage(this.dropzoneDiv.parent(), message); 1334 }.bind(this)); 1335 } 1336 } 1337 }, 1338 1339 removeErrors: function() 1340 {
1341 $('.invalid-feedback:not(:last-child)', this.dropzoneDiv.parent()).remove(); 1342 }, 1343 1344 removeAllErrors: function() 1345 { 1346 $('.invalid-feedback', this.dropzoneDiv.parent()).remove(); 1347 }, 1348 1349 removeNonExistingThumbnail: function() 1350 { 1351 $('[data-dz-thumbnail]', this.dropzoneDiv).each(function(i, row) { 1352 if (!$(row).attr('href') || $(row).attr('href').length <= 0) { 1353 $(row).parent().remove(); 1354 } 1355 }); 1356 }, 1357 1358 success: function(file, json) 1359 { 1360 this.removeNonExistingThumbnail(); 1361 1362 /* append to object */ 1363 var input = $('<input>').attr({ 1364 'type': 'hidden', 1365 'name': this.dropzoneDiv.data('element-handle')+'[]', 1366 'value': json.file 1367 }); 1368 $('.dropzone-item:last-child', this.dropzoneDiv).prepend(input); 1369 }, 1370 1371 complete: function(file) 1372 { 1373 this.formidable.removeDropzoneUpload(); 1374 }, 1375 1376 mocks: function() 1377 { 1378 var files = this.dropzoneDiv.data('files') || []; 1379 $.each(files, function(i, file) { 1380 1381 var mock = { 1382 name: file.name, 1383 size: file.size, 1384 accepted: true, 1385 status: Dropzone.ADDED, 1386 } 1387 this.dropzoneObj.files.push(mock); 1388 // resizeThumbnail = false (5th arg) so Dropzone does not build/load a preview image 1389 this.dropzoneObj.displayExistingFile(mock, null, null, null, false); 1390 1391 this.removeNonExistingThumbnail(); 1392 1393 var input = $('<input>').attr({ 1394 'type': 'hidden', 1395 'name': this.dropzoneDiv.data('element-handle')+'[]', 1396 'value': mock.name 1397 }); 1398 1399 $('.dropzone-item:last-child', this.dropzoneDiv).prepend(input); 1400 $('.dropzone-item:last-child', this.dropzoneDiv).find('[data-dz-remove]').on('click', function() { 1401 this.doRemoveFile(mock.name); 1402 }.bind(this)); 1403 1404 }.bind(this)); 1405 } 1406 1407 }); 1408 1409 dropzone.init(); 1410 1411 return dropzone; 1412 }; 1413 1414}(jQuery));
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.