1(function () { 2 'use strict'; 3 4 function BBMForms(options) { 5 if (!options) { 6 throw new Error('No options passed to BBMForms.'); 7 } 8 if (!options.formId) { 9 throw new Error('No formId option passed to BBMForms.'); 10 } 11 if (!options.hostName) { 12 options.hostName = window.location.hostname; 13 } 14 15 //override default options with given options 16 this.options = bbmFormsJQ.extend({}, BBMForms.defaultOptions, options); 17 18 //Use default if empty string 19 if (!this.options.origin) 20 this.options.origin = BBMForms.defaultOptions.origin; 21 if (!this.options.title) { 22 var title = document.getElementsByTagName('title'); 23 if (title.length > 0) this.options.title = title[0].innerText.trim(); 24 else this.options.title = BBMForms.defaultOptions.title; //empty 25 } 26 27 this.options.title = this.options.title.trim(); //remove whitespaces 28 29 this.userId = getCookie(BBMForms.constants.userIdCookieName); 30 31 this.userSsoId = this.options.userSsoId ? this.options.userSsoId : null; 32 33 this.userSsoEmail = this.options.userSsoEmail 34 ? this.options.userSsoEmail 35 : null; 36 37 var enableCapture = BBMForms.defaultOptions.enableMetadataCapture; 38 if (typeof options.enableMetadataCapture != 'undefined') 39 enableCapture = options.enableMetadataCapture; 40 this.options.enableMetadataCapture = enableCapture; 41 if (enableCapture) { 42 var paramList = BBMForms.defaultOptions.metadataParamList; 43 if (typeof options.metadataParamList != 'undefined') 44 paramList = options.metadataParamList; 45 46 var prefixList = BBMForms.defaultOptions.metadataPrefixList; 47 if (typeof options.metadataPrefixList != 'undefined') 48 prefixList = options.metadataPrefixList; 49 50 bbmFormsJQ.each(prefixList, function (i) { 51 var parameters = getUrlParameterByPrefix(prefixList[i]); 52 if (parameters.length > 0) { 53 bbmFormsJQ.each(parameters, function (index, value) { 54 if (bbmFormsJQ.inArray(value, paramList) < 0) { 55 paramList.push(value); 56 } 57 }); 58 } 59 }); 60 61 this.options.metadataParamList = paramList; 62 } 63 } 64 65 function getOwnPropertyNameInsensitive(obj, value) { 66 for (var prop in obj) { 67 if (prop.toLowerCase() === value.toLowerCase()) { 68 return prop; 69 } 70 } 71 return null; 72 } 73 74 function decodeHtml(str) { 75 var $txt = bbmFormsJQ('<textarea/>'); 76 return $txt.html($txt.html(str).text()).text(); 77 } 78 79 function setCookie(cname, cvalue, exdays, hostName) { 80 var d = new Date(); 81 d.setTime(d.getTime() + exdays * 24 * 60 * 60 * 1000); 82 var expires = ';expires=' + d.toUTCString(); 83 84 var domain = ''; 85 if (hostName) { 86 var hnArr = hostName.split('.'); 87 if (hnArr.length > 1) { 88 var hn = hnArr.reverse(); 89 domain = ';domain=.' + hn[1] + '.' + hn[0]; 90 } else { 91 domain = ';domain=' + hostName; 92 } 93 } 94 95 document.cookie = cname + '=' + cvalue + expires + domain + ';path=/'; 96 } 97 98 function getCookie(cname) { 99 var name = cname + '='; 100 var decodedCookie = decodeURIComponent(document.cookie); 101 var ca = decodedCookie.split(';'); 102 for (var i = 0; i < ca.length; i++) { 103 var c = ca[i]; 104 while (c.charAt(0) == ' ') { 105 c = c.substring(1); 106 } 107 if (c.indexOf(name) == 0) { 108 return c.substring(name.length, c.length); 109 } 110 } 111 return ''; 112 } 113 114 function postFormData( 115 formData, 116 callbackPassThrough, 117 callbackDisplayForm, 118 callbackError 119 ) { 120 bbmFormsJQ.ajax({ 121 type: 'post', 122 timeout: 300000, //5 minutes to allow for slow connection large file uploads set to 200MB max per request. 123 url: BBMForms.constants.apiPath + '/lead', 124 data: formData, 125 dataFilter: function () {}, //Empty out dataFilter that may exist. 126 processData: false, 127 contentType: false, 128 success: function (data, status, xhr) { 129 if (!data && (xhr.responseJSON || xhr.responseText)) 130 data = xhr.responseJSON 131 ? xhr.responseJSON 132 : JSON.parse(xhr.responseText); 133 134 callbackPassThrough(data); 135 }, 136 error: function (xhr, status, error) { 137 if (xhr.status === 200 && (xhr.responseJSON || xhr.responseText)) { 138 //When responseJSON is undefined and fails to parse it comes here. Handle as passthrough. 139 var data = xhr.responseJSON 140 ? xhr.responseJSON 141 : JSON.parse(xhr.responseText); 142 callbackPassThrough(data); 143 } else if ( 144 (xhr.status === 400 || xhr.status === 403) && 145 (xhr.responseJSON || xhr.responseText) 146 ) { 147 //Cannot pass through for having either incomplete or invalid fields. 148 var data = xhr.responseJSON 149 ? xhr.responseJSON 150 : JSON.parse(xhr.responseText); 151 data.stepToDisplay = parseInt(data.stepToDisplay); 152 callbackDisplayForm(data); 153 } else { 154 console.log('Failed to post data. ' + error); 155 callbackError('Error: Failed to post form data.'); 156 } 157 }, 158 }); 159 } 160
161 function postSerializedForm( 162 formData, 163 callbackPassThrough, 164 callbackDisplayForm, 165 callbackError 166 ) { 167 bbmFormsJQ.ajax({ 168 type: 'post', 169 url: BBMForms.constants.apiPath + '/lead', 170 data: formData, 171 dataFilter: function () {}, //Empty out dataFilter that may exist. 172 success: function (data, status, xhr) { 173 if (!data && (xhr.responseJSON || xhr.responseText)) 174 data = xhr.responseJSON 175 ? xhr.responseJSON 176 : JSON.parse(xhr.responseText); 177 178 callbackPassThrough(data); 179 }, 180 error: function (xhr, status, error) { 181 if (xhr.status === 200 && (xhr.responseJSON || xhr.responseText)) { 182 //When responseJSON is undefined and fails to parse it comes here. Handle as passthrough. 183 var data = xhr.responseJSON 184 ? xhr.responseJSON 185 : JSON.parse(xhr.responseText); 186 callbackPassThrough(data); 187 } else if ( 188 (xhr.status === 400 || xhr.status === 403) && 189 (xhr.responseJSON || xhr.responseText) 190 ) { 191 //Cannot pass through for having either incomplete or invalid fields. 192 var data = xhr.responseJSON 193 ? xhr.responseJSON 194 : JSON.parse(xhr.responseText); 195 data.stepToDisplay = parseInt(data.stepToDisplay); 196 callbackDisplayForm(data); 197 } else { 198 console.log('Failed to post data. ' + error); 199 callbackError('Error: Failed to post form data.'); 200 } 201 }, 202 }); 203 } 204 205 function postBypassFormData( 206 formData, 207 callbackPassThrough, 208 callbackDisplayBypass, 209 callbackDisplayForm, 210 callbackError 211 ) { 212 bbmFormsJQ.ajax({ 213 type: 'post', 214 url: BBMForms.constants.apiPath + '/lead/check', 215 data: formData, 216 dataFilter: function () {}, //Empty out dataFilter that may exist. 217 processData: false, 218 contentType: false, 219 success: function (data, status, xhr) { 220 if (!data && (xhr.responseJSON || xhr.responseText)) 221 data = xhr.responseJSON 222 ? xhr.responseJSON 223 : JSON.parse(xhr.responseText); 224 225 callbackPassThrough(data); 226 }, 227 error: function (xhr, status, error) { 228 if (xhr.status === 200 && (xhr.responseJSON || xhr.responseText)) { 229 //When responseJSON is undefined and fails to parse it comes here. Handle as passthrough. 230 var data = xhr.responseJSON 231 ? xhr.responseJSON 232 : JSON.parse(xhr.responseText); 233 callbackPassThrough(data); 234 } else if (xhr.status === 400 || xhr.status === 404) { 235 callbackDisplayForm(xhr.responseText); 236 } else if (xhr.status === 403) { 237 callbackDisplayBypass(xhr.responseText); 238 } else { 239 console.log('Failed to post data. ' + error); 240 callbackError('Error: Failed to post form data.'); 241 } 242 }, 243 }); 244 } 245 246 function postBypassSerializedForm( 247 formData, 248 callbackPassThrough, 249 callbackDisplayBypass, 250 callbackDisplayForm, 251 callbackError 252 ) { 253 bbmFormsJQ.ajax({ 254 type: 'post', 255 url: BBMForms.constants.apiPath + '/lead/check', 256 data: formData, 257 dataFilter: function () {}, //Empty out dataFilter that may exist. 258 success: function (data, status, xhr) { 259 if (!data && (xhr.responseJSON || xhr.responseText)) 260 data = xhr.responseJSON 261 ? xhr.responseJSON 262 : JSON.parse(xhr.responseText); 263 264 callbackPassThrough(data); 265 }, 266 error: function (xhr, status, error) { 267 if (xhr.status === 200 && (xhr.responseJSON || xhr.responseText)) { 268 //When responseJSON is undefined and fails to parse it comes here. Handle as passthrough. 269 var data = xhr.responseJSON 270 ? xhr.responseJSON 271 : JSON.parse(xhr.responseText); 272 callbackPassThrough(data); 273 } else if (xhr.status === 400 || xhr.status === 404) { 274 callbackDisplayForm(xhr.responseText); 275 } else if (xhr.status === 403) { 276 callbackDisplayBypass(xhr.responseText); 277 } else { 278 console.log('Failed to post data. ' + error); 279 callbackError('Error: Failed to post form data.'); 280 } 281 }, 282 }); 283 } 284 285 function getForm(reqData, callbackPassThrough, callbackGated, callbackError) { 286 bbmFormsJQ.ajax({ 287 type: 'get', 288 url: BBMForms.constants.apiPath + '/lead', 289 data: reqData, 290 dataFilter: function () {}, //Empty out dataFilter that may exist. 291 success: function (data, status, xhr) { 292 if (!data && (xhr.responseJSON || xhr.responseText)) 293 data = xhr.responseJSON 294 ? xhr.responseJSON 295 : JSON.parse(xhr.responseText); 296 297 callbackPassThrough(data); 298 }, 299 error: function (xhr, status, error) { 300 if (xhr.status === 200 && (xhr.responseJSON || xhr.responseText)) { 301 //When responseJSON is undefined and fails to parse it comes here. Handle as passthrough. 302 var data = xhr.responseJSON 303 ? xhr.responseJSON 304 : JSON.parse(xhr.responseText); 305 callbackPassThrough(data); 306 } else if (xhr.status === 404) { 307 //Form is not found 308 callbackError('Not Found: The requested form is not found.'); 309 } else if ( 310 xhr.status === 403 && 311 (xhr.responseJSON || xhr.responseText) 312 ) { 313 //Cannot pass through for having incomplete fields. 314 var data = xhr.responseJSON 315 ? xhr.responseJSON 316 : JSON.parse(xhr.responseText); 317 data.stepToDisplay = parseInt(data.stepToDisplay); 318 callbackGated(data); 319 } else { 320 console.log('Get form error: ' + error); 321 callbackError('Error: Failed to retrieve the requested form.'); 322 } 323 }, 324 }); 325 } 326 327 function postResource( 328 reqData, 329 callbackPassThrough, 330 callbackGated,
331 callbackError 332 ) { 333 bbmFormsJQ.ajax({ 334 type: 'post', 335 url: BBMForms.constants.apiPath + '/resource', 336 data: reqData, 337 dataFilter: function () {}, //Empty out dataFilter that may exist. 338 success: function (data, status, xhr) { 339 if (!data && (xhr.responseJSON || xhr.responseText)) 340 data = xhr.responseJSON 341 ? xhr.responseJSON 342 : JSON.parse(xhr.responseText); 343 344 callbackPassThrough(data); 345 }, 346 error: function (xhr, status, error) { 347 if (xhr.status === 200 && (xhr.responseJSON || xhr.responseText)) { 348 //When responseJSON is undefined and fails to parse it comes here. Handle as passthrough. 349 var data = xhr.responseJSON 350 ? xhr.responseJSON 351 : JSON.parse(xhr.responseText); 352 callbackPassThrough(data); 353 } else if (xhr.status === 404) { 354 //Form is not found 355 callbackError('Not Found: The requested form is not found.'); 356 } else if ( 357 xhr.status === 403 && 358 (xhr.responseJSON || xhr.responseText) 359 ) { 360 //Cannot pass through for having incomplete fields. 361 var data = xhr.responseJSON 362 ? xhr.responseJSON 363 : JSON.parse(xhr.responseText); 364 data.stepToDisplay = parseInt(data.stepToDisplay); 365 callbackGated(data); 366 } else { 367 console.log('Get form error: ' + error); 368 callbackError('Error: Failed to retrieve the requested form.'); 369 } 370 }, 371 }); 372 } 373 374 function getBootstrapColumnClasses(fieldSize) { 375 switch (fieldSize) { 376 case 'short': 377 return 'col-xs-12 col-sm-3 col-md-3'; 378 case 'long': 379 return 'col-xs-12 col-sm-9 col-md-9'; 380 case 'full': 381 return 'col-xs-12 col-sm-12 col-md-12'; 382 case 'medium': 383 default: 384 return 'col-xs-12 col-sm-6 col-lg-6'; 385 } 386 } 387 388 function getBootstrapFieldClasses(fieldSize, independentLine) { 389 if (independentLine && fieldSize !== 'full') { 390 return getBootstrapColumnClasses(fieldSize); 391 } else { 392 return ''; 393 } 394 } 395 396 function createFieldBlock(properties, includeLabel) { 397 var $fieldBlock = bbmFormsJQ('<div/>') 398 .addClass(BBMForms.constants.fieldBlockCssClass) 399 .addClass(properties.fieldType); 400 if (properties.independentLine === true) { 401 $fieldBlock.addClass('full').addClass(getBootstrapColumnClasses('full')); 402 } else { 403 $fieldBlock 404 .addClass(properties.fieldSize) 405 .addClass(getBootstrapColumnClasses(properties.fieldSize)); 406 } 407 408 includeLabel = typeof includeLabel !== 'undefined' ? includeLabel : true; 409 if (includeLabel) { 410 var required = properties.required 411 ? " <sup class='" + 412 BBMForms.constants.requiredTextCssClass + 413 "'>" + 414 BBMForms.constants.requiredText + 415 '</sup>' 416 : ''; 417 var $label = bbmFormsJQ('<label/>', { 418 for: properties.clientProperties.clientId, 419 }).html(properties.label + required); 420 $fieldBlock.append($label); 421 } 422 423 if (properties.instructionText) { 424 var $instTxt = bbmFormsJQ('<p></p>') 425 .text(properties.instructionText) 426 .addClass(BBMForms.constants.instructionTextCssClass); 427 $fieldBlock.append($instTxt); 428 } 429 430 return $fieldBlock; 431 } 432 433 function createPaymentBlock(properties, includeLabel) { 434 var $fieldBlock = bbmFormsJQ('<div/>').addClass('row cart-page'); 435 436 // hide required text 437 bbmFormsJQ('.instructionText').html(''); 438 439 //Add Payment class to steps wrapper 440 var $stepsWrapper = bbmFormsJQ('.steps_wrapper'); 441 $stepsWrapper.addClass('payment'); 442 443 if (properties.instructionText) { 444 var $instTxt = bbmFormsJQ('<p></p>') 445 .text(properties.instructionText) 446 .addClass(BBMForms.constants.instructionTextCssClass); 447 $fieldBlock.append($instTxt); 448 } 449 450 return $fieldBlock; 451 } 452 453 function createInputFieldList(properties, inputType) { 454 var $inputListWrap = bbmFormsJQ('<div/>'); 455 456 var columnsCssClass = ''; 457 if (properties.columns) { 458 switch (properties.columns.toLowerCase()) { 459 case 'two': 460 columnsCssClass = 'col-xs-12 col-sm-6 col-md-6'; 461 break; 462 case 'three': 463 columnsCssClass = 'col-xs-12 col-sm-6 col-md-4'; 464 break; 465 case 'four': 466 columnsCssClass = 'col-xs-12 col-sm-4 col-md-3'; 467 break; 468 } 469 } 470 471 var listIndex = 0; 472 bbmFormsJQ.each(properties.values, function (index, item) { 473 listIndex = index; 474 var inputId = properties.clientProperties.clientId + '_' + listIndex; 475 var value = item.value ? item.value : item.option; 476 var $inputWrap = bbmFormsJQ('<div/>') 477 .addClass(BBMForms.constants.columnCssClass) 478 .addClass(columnsCssClass); 479 if ((inputType == 'radio' || inputType == 'checkbox') && item.image) { 480 //item description requires a different set up (for Detailed Checkboxes) 481 if (!item.description) { 482 $inputWrap.append( 483 bbmFormsJQ('<label/>', { for: inputId }) 484 .addClass(BBMForms.constants.imageLabelCssClass) 485 .append( 486 bbmFormsJQ('<img/>', { src: item.image, alt: item.option }) 487 ) 488 ); 489 } 490 } 491 $inputWrap.append( 492 bbmFormsJQ('<input/>', { 493 id: inputId, 494 name: properties.externalId, 495 'data-fieldname': properties.name, 496 type: inputType, 497 }).val(value) 498 ); 499 500 //If auto-progression is enabled, auto-click the next btn 501 if ( 502 properties.required && 503 inputType == 'radio' && 504 properties.enableAutoProgress && 505 properties.$enableAutoProgressBtn 506 ) { 507 $inputWrap.change(function () { 508 properties.$enableAutoProgressBtn.click(); 509 }); 510 } 511 512 //Detailed Checkbox 513 if (item.description) { 514 $inputWrap 515 .append( 516 bbmFormsJQ('<label/>', { 517 for: inputId, 518 class: BBMForms.constants.detailedLabelCssClass, 519 }).text(item.option) 520 ) 521 .addClass(BBMForms.constants.detailedCheckboxCssClass) 522 .append( 523 bbmFormsJQ('<div/>') 524 .addClass(BBMForms.constants.detailedDescriptionCssClass) 525 .html(item.description) 526 ); 527 528 if (item.image) { 529 $inputWrap.append( 530 bbmFormsJQ('<img/>', { 531 src: item.image, 532 alt: item.option, 533 }).addClass(BBMForms.constants.detailedImageCssClass) 534 ); 535 } 536 537 $inputWrap.change(function () { 538 bbmFormsJQ(this).toggleClass('selected'); 539 }); 540 } else { 541 //Default Checkbox/Radio button 542 $inputWrap.append( 543 bbmFormsJQ('<label/>', { for: inputId }).text(item.option) 544 ); 545 } 546 547 $inputListWrap.append($inputWrap); 548 }); 549 550 var fieldIdOther = 551 properties.clientProperties.clientId + 552 BBMForms.constants.otherFieldIdSuffix; 553 if (properties.other === true) { 554 listIndex++; 555 var inputId = properties.clientProperties.clientId + '_' + listIndex; 556 var $inputWrap = bbmFormsJQ('<div/>') 557 .addClass(BBMForms.constants.columnCssClass) 558 .addClass(columnsCssClass); 559 var $otherInput = bbmFormsJQ('<input/>', { 560 id: inputId, 561 name: properties.externalId, 562 type: inputType, 563 }).val(BBMForms.constants.otherText); 564 $inputWrap.append($otherInput); 565 $inputWrap.append( 566 bbmFormsJQ('<label/>', { for: inputId }).text( 567 BBMForms.constants.otherText 568 ) 569 ); 570 $inputListWrap.append($inputWrap); 571 572 //Create clientId to access on submit 573 properties.clientProperties.otherFieldClientId = fieldIdOther; 574 575 var $otherBox = bbmFormsJQ('<div/>') 576 .addClass(BBMForms.constants.otherContainerCssClass) 577 .hide(); 578 var $otherInstTxt = bbmFormsJQ('<p/>').text( 579 BBMForms.constants.otherExplanationText 580 ); 581 var $otherField = bbmFormsJQ('<input/>', { 582 id: fieldIdOther, 583 name: properties.externalId, 584 type: 'text', 585 }) 586 .addClass(properties.fieldSize) 587 .addClass( 588 getBootstrapFieldClasses( 589 BBMForms.constants.otherFieldSize, 590 properties.independentLine 591 ) 592 ) 593 .prop('disabled', true); 594 $otherBox.append($otherInstTxt).append($otherField); 595 $inputListWrap.append($otherBox); 596 597 $inputListWrap 598 .find("input[name='" + properties.externalId + "']") 599 .change(function () { 600 if ( 601 $inputListWrap.find("input[id='" + inputId + "']").prop('checked') 602 ) { 603 $otherField.prop('disabled', false); 604 $otherBox.show(); 605 } else { 606 $otherBox.hide(); 607 $otherField.prop('disabled', true); 608 $otherField.val(''); 609 } 610 }); 611 } 612 613 return $inputListWrap; 614 } 615 616 function createPaymentInputFieldList(properties) { 617 var $inputListWrap = bbmFormsJQ('<div/>'); 618 619 bbmFormsJQ.each(properties.values, function (index, item) { 620 var inputId = properties.clientProperties.clientId + '_' + index; 621 622 //Divs wraps for shopping cart 623 var $inputWrap = bbmFormsJQ('<div/>', { 624 'data-title': item.label, 625 }).addClass('item lineItem'); 626 var $blockWrap = bbmFormsJQ('<div/>').addClass('col-10 col-xs-10'); 627 var $row = bbmFormsJQ('<div/>').addClass('row'); 628 $row.append( 629 bbmFormsJQ('<input/>', { 630 id: inputId, 631 name: properties.externalId, 632 'data-fieldname': properties.name, 633 type: 'checkbox', 634 class: 'col-1 col-xs-1 col-sm-2', 635 }).val(item.value) 636 ); 637 var price = parseFloat(item.cost); 638 if (isNaN(price)) { 639 price = 0; 640 } 641 var $priceWrap = bbmFormsJQ('<div/>', { 'data-value': item.cost }) 642 .addClass('col-2 col-xs-2 pricing') 643 .text(item.costLabel + price.toFixed(2)); 644 645 if (item.image) { 646 var $imageWrap = bbmFormsJQ('<div/>').addClass( 647 'col-sm-2 col-0 col-xs-0 hide-mobile' 648 ); 649 $imageWrap.append( 650 bbmFormsJQ('<img/>', { src: item.image, alt: item.label }) 651 ); 652 $row.append($imageWrap); 653 } 654 655 var $descriptionWrap = bbmFormsJQ('<div/>').addClass( 656 'col-10 col-xs-10 col-sm-8' 657 ); 658 $descriptionWrap.append(bbmFormsJQ('<h3/>').text(item.label)); 659 660 if (item.description) { 661 $descriptionWrap.append(bbmFormsJQ('<p/>').text(item.description)); 662 } 663 664 //Build: append in the correct order 665 $row.append($descriptionWrap); 666 $blockWrap.append($row); 667 $inputWrap.append($blockWrap); 668 $inputWrap.append($priceWrap); 669 670 $inputWrap.change(updatePaymentCart); 671 672 $inputListWrap.append($inputWrap); 673 }); 674 675 return $inputListWrap; 676 } 677 678 function updatePaymentCart() { 679 let $cartSummary = bbmFormsJQ('.cart-lineItems').html(''); 680 let numTotal = 0.0; 681 let itemTotal = 0; 682 bbmFormsJQ('.lineItem').each(function () {
683 let lineItem = bbmFormsJQ(this).find('input:checked'); 684 if (lineItem.length > 0) { 685 itemTotal++; 686 let priceElement = bbmFormsJQ(this).find('.pricing'); 687 let lineItemTitle = bbmFormsJQ(this).attr('data-title'); 688 let price = parseFloat(priceElement.attr('data-value')); 689 if (isNaN(price)) { 690 price = 0; 691 } 692 numTotal += price; 693 694 //add it to the shopping cart right page as well 695 let $cartItem = bbmFormsJQ('<div/>').addClass( 696 'cart-item row no-gutters' 697 ); 698 let $cartTitle = bbmFormsJQ('<h3/>') 699 .addClass('col-7 col-xs-7') 700 .text(lineItemTitle); 701 let $cartPrice = bbmFormsJQ('<div/>') 702 .addClass('col-5 col-xs-5 pricing') 703 .text('$' + price.toFixed(2)); 704 $cartItem.append($cartTitle); 705 $cartItem.append($cartPrice); 706 $cartSummary.append($cartItem); 707 } 708 }); 709 710 var $checkoutBtn = bbmFormsJQ('.checkoutBtn'); 711 if (itemTotal > 0) { 712 $checkoutBtn.text(BBMForms.constants.paymentCheckoutBtnText); 713 $checkoutBtn.prop('disabled', false); 714 } else { 715 $checkoutBtn.text(BBMForms.constants.paymentCheckoutBtnDisableText); 716 $checkoutBtn.prop('disabled', true); 717 } 718 719 bbmFormsJQ('.cart-num').text(itemTotal + ' Added'); 720 bbmFormsJQ('.total-pricing').text( 721 BBMForms.defaultOptions.costLabel + numTotal.toFixed(2) 722 ); 723 } 724 725 function createSingleLineFormBlock( 726 properties, 727 inputType, 728 userMetadata, 729 attributes 730 ) { 731 var $fieldBlock = createFieldBlock(properties); 732 733 var $input = bbmFormsJQ('<input/>', { 734 id: properties.clientProperties.clientId, 735 name: properties.externalId, 736 'data-fieldname': properties.name, 737 type: inputType, 738 }) 739 .addClass(properties.fieldSize) 740 .addClass( 741 getBootstrapFieldClasses( 742 properties.fieldSize, 743 properties.independentLine 744 ) 745 ); 746 if (properties.characterLimit) { 747 $input.attr('maxlength', properties.characterLimit); 748 } 749 if (properties.fieldMasking) { 750 $input.attr('placeholder', properties.fieldMasking); 751 } 752 if (attributes) { 753 $input.attr(attributes); 754 } 755 $fieldBlock.append($input); 756 757 return $fieldBlock; 758 } 759 760 function createFileFormBlock(properties) { 761 var $fieldBlock = createFieldBlock(properties); 762 763 //Previously uploaded file name will be displayed during prePopulateAndValidate(); 764 var $input = bbmFormsJQ('<input/>', { 765 id: properties.clientProperties.clientId, 766 name: properties.externalId, 767 'data-fieldname': properties.name, 768 type: 'file', 769 }) 770 .addClass(properties.fieldSize) 771 .addClass( 772 getBootstrapFieldClasses( 773 properties.fieldSize, 774 properties.independentLine 775 ) 776 ); 777 if (properties.fileTypes) { 778 $input.attr('accept', properties.fileTypes); 779 } 780 $fieldBlock.append($input); 781 782 var $labelBtn = bbmFormsJQ('<label/>', { 783 for: properties.clientProperties.clientId, 784 }) 785 .addClass(BBMForms.constants.fileButtonCssClass) 786 .text(BBMForms.constants.fileButtonText) 787 .attr('style', 'background-color:#'.concat(properties.buttonColor, ';')); 788 $fieldBlock.append($labelBtn); 789 790 var $labelFileName = bbmFormsJQ('<span/>').addClass( 791 BBMForms.constants.fileNameCssClass 792 ); 793 $fieldBlock.append($labelFileName); 794 795 //Show selected file name 796 $input.change(function () { 797 if (this.files && this.files.length > 0) { 798 var fn = this.files[0].name; 799 $labelFileName.text(fn); 800 } else { 801 $labelFileName.text(''); 802 } 803 }); 804 805 return $fieldBlock; 806 } 807 808 function createParagraphTextFormBlock(properties, userMetadata) { 809 var $fieldBlock = createFieldBlock(properties); 810 var numRows = properties.rows 811 ? properties.rows 812 : BBMForms.constants.textareaDefaultRows; 813 814 var $input = bbmFormsJQ('<textarea/>', { 815 id: properties.clientProperties.clientId, 816 name: properties.externalId, 817 'data-fieldname': properties.name, 818 rows: numRows, 819 }) 820 .addClass(properties.fieldSize) 821 .addClass( 822 getBootstrapFieldClasses( 823 properties.fieldSize, 824 properties.independentLine 825 ) 826 ); 827 if (properties.characterLimit) { 828 $input.attr('maxlength', properties.characterLimit); 829 } 830 $fieldBlock.append($input); 831 832 return $fieldBlock; 833 } 834 835 function createMultipleChoiceFormBlock(properties, userMetadata) { 836 var $fieldBlock = createFieldBlock(properties); 837 838 var inputType = 'radio'; 839 var $inputList = createInputFieldList(properties, inputType); 840 $fieldBlock.append($inputList); 841 842 //This hidden field is added so in cases where the radio buttons is not required, this will submit an empty value 843 var $inputHidden = bbmFormsJQ('<input/>', { 844 type: 'hidden', 845 name: properties.externalId, 846 id: 847 properties.clientProperties.clientId + 848 '_hidden' + 849 BBMForms.constants.multipleRadioButtonNameSuffix, 850 }); 851 $fieldBlock.append($inputHidden); 852 853 return $fieldBlock; 854 } 855 856 function createCheckboxesFormBlock(properties, userMetadata) { 857 var $fieldBlock = createFieldBlock(properties); 858 859 var inputType = 'checkbox'; 860 var $inputList = createInputFieldList(properties, inputType); 861 $fieldBlock.append($inputList); 862 863 //This hidden field is added so in cases where the checkbox is not required, this will submit an empty value 864 var $inputHidden = bbmFormsJQ('<input/>', { 865 type: 'hidden', 866 name: properties.externalId, 867 id: 868 properties.clientProperties.clientId + 869 '_hidden' + 870 BBMForms.constants.multipleCheckboxNameSuffix, 871 }); 872 $fieldBlock.append($inputHidden); 873 874 return $fieldBlock; 875 } 876 877 function createDropdownMenuFormBlock(properties, userMetadata) { 878 var $fieldBlock = createFieldBlock(properties); 879 880 var $select = bbmFormsJQ('<select/>', { 881 id: properties.clientProperties.clientId, 882 name: properties.externalId, 883 'data-fieldname': properties.name, 884 }) 885 .addClass(properties.fieldSize) 886 .addClass( 887 getBootstrapFieldClasses( 888 properties.fieldSize, 889 properties.independentLine 890 ) 891 ); 892 893 //If auto-progression is enabled, auto-click the next btn 894 if ( 895 properties.required && 896 properties.enableAutoProgress && 897 properties.$enableAutoProgressBtn 898 ) { 899 $select.change(function () { 900 //Keep user on current step so they can fill out the other text box 901 if (this.value === BBMForms.constants.otherText) { 902 return; 903 } 904 905 properties.$enableAutoProgressBtn.click(); 906 }); 907 } 908 909 $select.append(bbmFormsJQ('<option/>').text('- Select -').val('')); 910 bbmFormsJQ.each(properties.values, function (index, item) { 911 var value = item.value ? item.value : item.option; 912 $select.append(bbmFormsJQ('<option/>').text(item.option).val(value)); 913 }); 914 $fieldBlock.append($select); 915 916 var fieldIdOther = 917 properties.clientProperties.clientId + 918 BBMForms.constants.otherFieldIdSuffix; 919 if (properties.other === true) { 920 $select.append( 921 bbmFormsJQ('<option/>') 922 .text(BBMForms.constants.otherText) 923 .val(BBMForms.constants.otherText) 924 ); 925 926 //Create clientId to access on submit 927 properties.clientProperties.otherFieldClientId = fieldIdOther; 928 929 var $otherBox = bbmFormsJQ('<div/>') 930 .addClass(BBMForms.constants.otherContainerCssClass) 931 .hide(); 932 var $otherInstTxt = bbmFormsJQ('<p/>').text( 933 BBMForms.constants.otherExplanationText 934 ); 935 var $otherField = bbmFormsJQ('<input/>', { 936 id: fieldIdOther, 937 name: properties.externalId, 938 type: 'text', 939 }) 940 .addClass(properties.fieldSize) 941 .addClass( 942 getBootstrapFieldClasses( 943 BBMForms.constants.otherFieldSize, 944 properties.independentLine 945 ) 946 ) 947 .prop('disabled', true); 948 $otherBox.append($otherInstTxt).append($otherField); 949 $fieldBlock.append($otherBox); 950 951 $select.change(function () { 952 var $otherOption = bbmFormsJQ(this).find( 953 "option[value='" + BBMForms.constants.otherText + "']" 954 ); 955 if ($otherOption.is(':selected')) { 956 $otherField.prop('disabled', false); 957 $otherBox.show(); 958 } else { 959 $otherBox.hide(); 960 $otherField.prop('disabled', true); 961 } 962 }); 963 } 964 965 return $fieldBlock; 966 } 967 968 function createConsentCheckboxFormBlock(properties) { 969 var $fieldBlock = createFieldBlock(properties, false); 970 971 var $consentColumn = bbmFormsJQ('<div/>').addClass( 972 BBMForms.constants.columnCssClass 973 ); 974 975 //This input is just to interact with the user. Actual value sent to the api is inputHidden below. This is because when unchecked the field is not sent at all. 976 var $input = bbmFormsJQ('<input/>', { 977 id: properties.clientProperties.clientId, 978 name: 979 properties.externalId + BBMForms.constants.consentCheckboxNameSuffix, 980 'data-fieldname': properties.name, 981 type: 'checkbox', 982 }) 983 .addClass(properties.fieldSize) 984 .addClass( 985 getBootstrapFieldClasses( 986 properties.fieldSize, 987 properties.independentLine 988 ) 989 ); 990 $consentColumn.append($input); 991 992 //This hidden field is always there with a value. See updateFormDataForConsentCheckBox. 993 var $inputHidden = bbmFormsJQ('<input/>', { 994 type: 'hidden', 995 name: properties.externalId, 996 id: properties.clientProperties.clientId + '_hidden', 997 }); 998 $consentColumn.append($inputHidden); 999 1000 var consentHtml = decodeHtml(properties.label); 1001 $consentColumn.append(consentHtml); 1002 1003 $fieldBlock.append($consentColumn); 1004 1005 return $fieldBlock; 1006 } 1007 1008 function createSectionDividerFormBlock(properties) { 1009 var $fieldBlock = createFieldBlock(properties, false); 1010 1011 var $dividerBlock = bbmFormsJQ('<div/>').addClass( 1012 BBMForms.constants.columnCssClass 1013 ); 1014 1015 if (properties.showTopDividerLine) { 1016 $dividerBlock.append("<div class='line-divider col-xs-12'>"); 1017 } 1018 1019 var descriptionHtml = decodeHtml(properties.description); 1020 $dividerBlock.append(descriptionHtml); 1021 1022 if (properties.showBottomDividerLine) { 1023 $dividerBlock.append("<div class='line-divider col-xs-12'>"); 1024 } 1025 1026 $fieldBlock.append($dividerBlock); 1027 1028 return $fieldBlock; 1029 } 1030 1031 function createSingleLineZipCode(properties, userMetadata) { 1032 var $fieldBlock = createFieldBlock(properties); 1033 1034 var $input = bbmFormsJQ('<input/>', { 1035 id: properties.clientProperties.clientId, 1036 name: properties.externalId, 1037 'data-fieldname': properties.name, 1038 type: 'text', 1039 }) 1040 .addClass(properties.fieldSize) 1041 .addClass( 1042 getBootstrapFieldClasses( 1043 properties.fieldSize, 1044 properties.independentLine 1045 ) 1046 ); 1047 $input.attr('maxlength', '5'); 1048 $input.attr('pattern', '[0-9]*'); 1049 $fieldBlock.append($input); 1050 1051 return $fieldBlock; 1052 } 1053 1054 function createPaymentCart(properties, userMetadata) { 1055 var $fieldBlock = createPaymentBlock(properties); 1056 var $leftSide = bbmFormsJQ('<div/>') 1057 .addClass('col-12 col-xs-12 col-md-9') 1058 .append(createPaymentInputFieldList(properties)); 1059 $fieldBlock.append($leftSide); 1060 1061 //Mobile 1062 var $rightSideMobile = bbmFormsJQ('<div/>').addClass( 1063 'col-12 cold-xs-12 col-lg-3 cart-mobile' 1064 ); 1065 $rightSideMobile.append(
1066 bbmFormsJQ('<span/>') 1067 .addClass(BBMForms.constants.paymentCartMobileTotalCssClass) 1068 .text('0 ' + BBMForms.constants.paymentCartMobileTotalText) 1069 ); 1070 $fieldBlock.append($rightSideMobile); 1071 1072 //Desktop 1073 var $rightSideDesktop = bbmFormsJQ('<div/>') 1074 .addClass('col-12 col-xs-12 col-md-3 cart-desktop') 1075 .append(bbmFormsJQ('<h4/>').text(BBMForms.constants.paymentCartHeader)); 1076 $rightSideDesktop.append(bbmFormsJQ('<div/>').addClass('cart-lineItems')); 1077 1078 //Line Items 1079 var $lineItem = bbmFormsJQ('<div/>').addClass('cart-items'); 1080 $rightSideDesktop.append($lineItem); 1081 1082 var $totalWrap = bbmFormsJQ('<div/>').addClass('row no-gutters'); 1083 $totalWrap.append( 1084 bbmFormsJQ('<div/>') 1085 .addClass( 1086 'col-6 col-xs-6 ' + BBMForms.constants.paymentCartTotalCssClass 1087 ) 1088 .text('Total') 1089 ); 1090 $totalWrap.append( 1091 bbmFormsJQ('<div/>') 1092 .addClass( 1093 'col-6 col-xs-6 text-right ' + 1094 BBMForms.constants.paymentCartTotalPriceCssClass 1095 ) 1096 .text('$0.00') 1097 ); 1098 1099 $rightSideDesktop.append($totalWrap); 1100 $fieldBlock.append($rightSideDesktop); 1101 1102 //pre-populate field value 1103 var preVals; 1104 if ( 1105 bbmFormsJQ.isArray(properties.fieldValues) && 1106 properties.fieldValues.length 1107 ) { 1108 preVals = properties.fieldValues; 1109 } 1110 1111 //If no values prefilled from api 1112 else if (properties.name) { 1113 //#1 Url prefill 1114 var values = getUrlParameter(properties.name); 1115 if (values.length) { 1116 preVals = values; 1117 } 1118 //#2 User metadata prefill 1119 else if (userMetadata) { 1120 var propName = getOwnPropertyNameInsensitive( 1121 userMetadata, 1122 properties.name 1123 ); 1124 if (propName) { 1125 var vals = userMetadata[propName]; 1126 if (vals) { 1127 if (bbmFormsJQ.isArray(vals) && vals.length) { 1128 preVals = vals; 1129 } else { 1130 preVals = [vals]; 1131 } 1132 } 1133 } 1134 } 1135 } 1136 if (bbmFormsJQ.isArray(preVals) && preVals.length) { 1137 bbmFormsJQ('.cart-page').ready(updatePaymentCart); 1138 //lowercase values for case-insensitive matching 1139 var lowerPreVals = bbmFormsJQ.map(preVals, function (n, i) { 1140 return n.toLowerCase(); 1141 }); 1142 var matchingValues = $leftSide 1143 .find('input[type=checkbox]') 1144 .filter(function () { 1145 var $option = bbmFormsJQ(this); 1146 var optionVal = $option.attr('value').toLowerCase(); 1147 var matchingVal = ''; 1148 bbmFormsJQ.each(lowerPreVals, function (index, value) { 1149 if (value == optionVal) { 1150 matchingVal = value; 1151 //select the matching option 1152 $option.prop('checked', true).trigger('change'); 1153 } 1154 }); 1155 return matchingVal.length > 0; 1156 }) 1157 .map(function () { 1158 return bbmFormsJQ(this).attr('value').toLowerCase(); 1159 }); 1160 } 1161 1162 //This hidden field is added so in cases where the checkbox is not required, this will submit an empty value 1163 var $inputHidden = bbmFormsJQ('<input/>', { 1164 type: 'hidden', 1165 name: properties.externalId, 1166 id: 1167 properties.clientProperties.clientId + 1168 '_hidden' + 1169 BBMForms.constants.multipleCheckboxNameSuffix, 1170 }); 1171 $fieldBlock.append($inputHidden); 1172 1173 //Validation 1174 if (properties.validationError) { 1175 $fieldBlock.append( 1176 bbmFormsJQ('<span/>') 1177 .text(properties.validationError) 1178 .addClass(BBMForms.constants.errorMsgCssClass) 1179 ); 1180 } 1181 1182 bbmFormsJQ('.buttons').hide(); 1183 1184 return $fieldBlock; 1185 } 1186 1187 function configureConditionalDisplay(self, properties) { 1188 //Handle IConditionalDisplayField 1189 if ( 1190 properties.conditionalDisplayBaseFieldId && 1191 properties.conditionalDisplayValues && 1192 bbmFormsJQ.isArray(properties.conditionalDisplayValues) && 1193 properties.conditionalDisplayValues.length 1194 ) { 1195 var $field = 1196 properties.fieldType == 'MultipleChoice' || 1197 properties.fieldType == 'Checkboxes' 1198 ? bbmFormsJQ("input[name='" + properties.externalId + "']") 1199 : bbmFormsJQ('#' + properties.clientProperties.clientId); 1200 var $fieldBlock = $field.closest( 1201 '.' + BBMForms.constants.fieldBlockCssClass 1202 ); 1203 if ($fieldBlock) { 1204 properties.display = false;
1205 bbmFormsJQ.each(self.formData.form.steps, function (index, step) { 1206 var baseFieldArr = bbmFormsJQ.grep(step, function (item) { 1207 return item.externalId === properties.conditionalDisplayBaseFieldId; 1208 }); 1209 if (baseFieldArr && baseFieldArr.length) { 1210 var baseField = baseFieldArr[0]; 1211 var $baseField = 1212 baseField.fieldType == 'MultipleChoice' || 1213 baseField.fieldType == 'Checkboxes' 1214 ? bbmFormsJQ("input[name='" + baseField.externalId + "']") 1215 : bbmFormsJQ('#' + baseField.clientProperties.clientId); 1216 var findIndexArr = bbmFormsJQ.grep( 1217 BBMForms.conditionalDisplayChangeEvents, 1218 function (item) { 1219 return ( 1220 item.baseFieldId == 1221 properties.conditionalDisplayBaseFieldId && 1222 item.conditionalFieldId == properties.externalId 1223 ); 1224 } 1225 ); 1226 if ($baseField && (!findIndexArr || findIndexArr.length == 0)) { 1227 $baseField.change(function () { 1228 updateFormDataFromInputForSingleField(baseField); 1229 configureConditionalDisplay(self, properties); 1230 }); 1231 var log = { 1232 baseFieldId: properties.conditionalDisplayBaseFieldId, 1233 conditionalFieldId: properties.externalId, 1234 }; 1235 BBMForms.conditionalDisplayChangeEvents.push(log); 1236 } 1237 //use checkvalues to determine display if base field is in previous step as we may not have submitted value in the same session. 1238 var checkValues = 1239 baseField.stepNumber < properties.stepNumber 1240 ? baseField.fieldCheckValues 1241 : baseField.fieldValues; 1242 if (checkValues && checkValues.length > 0) { 1243 var intersection = checkValues.filter(function (value) { 1244 return ( 1245 properties.conditionalDisplayValues 1246 .map(function (x) { 1247 return x.value; 1248 }) 1249 .indexOf(value) >= 0 1250 ); 1251 }); 1252 if (intersection && intersection.length > 0) { 1253 properties.display = true; 1254 return false; //break 1255 } 1256 } 1257 } 1258 }); 1259 if (properties.display) $fieldBlock.show(); 1260 else $fieldBlock.hide(); 1261 } 1262 } else if ( 1263 typeof properties.fieldRules != 'undefined' && 1264 properties.fieldRules.length 1265 ) { 1266 var $field = 1267 properties.fieldType == 'MultipleChoice' || 1268 properties.fieldType == 'Checkboxes' 1269 ? bbmFormsJQ("input[name='" + properties.externalId + "']") 1270 : bbmFormsJQ('#' + properties.clientProperties.clientId); 1271 var $fieldBlock = $field.closest( 1272 '.' + BBMForms.constants.fieldBlockCssClass 1273 ); 1274 if ($fieldBlock) { 1275 properties.display = false; 1276 bbmFormsJQ.each(properties.fieldRules, function (index, rule) { 1277 if ( 1278 typeof rule !== 'undefined' && 1279 typeof rule.fieldID !== 'undefined' && 1280 typeof rule.options !== 'undefined' && 1281 rule.options.length > 0 1282 ) { 1283 bbmFormsJQ.each(self.formData.form.steps, function (index, step) { 1284 var baseFieldArr = bbmFormsJQ.grep(step, function (item) { 1285 return item.externalId === rule.fieldID; 1286 }); 1287 if (baseFieldArr && baseFieldArr.length) { 1288 var baseField = baseFieldArr[0]; 1289 var $baseField = 1290 baseField.fieldType == 'MultipleChoice' || 1291 baseField.fieldType == 'Checkboxes' 1292 ? bbmFormsJQ("input[name='" + baseField.externalId + "']") 1293 : bbmFormsJQ('#' + baseField.clientProperties.clientId); 1294 var findIndexArr = bbmFormsJQ.grep( 1295 BBMForms.conditionalDisplayChangeEvents, 1296 function (item) { 1297 return ( 1298 item.baseFieldId == rule.fieldID && 1299 item.conditionalFieldId == properties.externalId 1300 ); 1301 } 1302 ); 1303 if ($baseField && (!findIndexArr || findIndexArr.length == 0)) { 1304 $baseField.change(function () { 1305 updateFormDataFromInputForSingleField(baseField); 1306 configureConditionalDisplay(self, properties); 1307 }); 1308 var log = { 1309 baseFieldId: rule.fieldID, 1310 conditionalFieldId: properties.externalId, 1311 }; 1312 BBMForms.conditionalDisplayChangeEvents.push(log); 1313 } 1314 //use checkvalues to determine display if base field is in previous step as we may not have submitted value in the same session. 1315 var checkValues = 1316 baseField.stepNumber < properties.stepNumber 1317 ? baseField.fieldCheckValues 1318 : baseField.fieldValues; 1319 if (checkValues && checkValues.length > 0) { 1320 var intersection = checkValues.filter(function (value) { 1321 return ( 1322 rule.options 1323 .filter(function (option) { 1324 return option.fieldDisplay == true; 1325 }) 1326 .map(function (x) { 1327 return x.fieldValue; 1328 }) 1329 .indexOf(value) >= 0 1330 ); 1331 }); 1332 if (intersection && intersection.length > 0) { 1333 properties.display = true; 1334 return false; //break 1335 } 1336 } 1337 } 1338 }); 1339 } 1340 }); 1341 if (properties.display) $fieldBlock.show(); 1342 else $fieldBlock.hide(); 1343 } 1344 } 1345 } 1346 1347 function prePopulateAndValidate(self, properties) { 1348 let userMetadata = self.options.userMetadata; 1349 switch (properties.fieldType) { 1350 case 'SingleLineText': 1351 case 'SingleLineEmailAddress': 1352 case 'SingleLineURL': 1353 case 'SingleLineDate': 1354 case 'SingleLineNumber': 1355 case 'SingleLinePhoneNumber': 1356 case 'ParagraphText': 1357 case 'SingleLineZipCode': { 1358 let $input = bbmFormsJQ('#' + properties.clientProperties.clientId); 1359 let $fieldBlock = $input.closest( 1360 '.' + BBMForms.constants.fieldBlockCssClass 1361 ); 1362 1363 //pre-populate field value 1364 let preVal; 1365 if ( 1366 bbmFormsJQ.isArray(properties.fieldValues) && 1367 properties.fieldValues.length && 1368 properties.fieldValues[0] 1369 ) { 1370 preVal = properties.fieldValues[0]; 1371 } 1372 //If no values prefilled from api 1373 else if (properties.name) { 1374 //#1 Url prefill 1375 let values = getUrlParameter(properties.name); 1376 if (values.length) { 1377 preVal = values[0]; 1378 } 1379 //#2 User metadata prefill 1380 else if (userMetadata) {
1381 let propName = getOwnPropertyNameInsensitive( 1382 userMetadata, 1383 properties.name 1384 ); 1385 if (propName) { 1386 let preVals = userMetadata[propName]; 1387 if (bbmFormsJQ.isArray(preVals)) preVal = preVals[0]; 1388 else preVal = preVals; 1389 } 1390 } 1391 } 1392 if (preVal) { 1393 $input.val(preVal); 1394 } 1395 1396 //Validation 1397 if (properties.validationError) { 1398 $input.addClass(BBMForms.constants.errorFieldCssClass); 1399 $fieldBlock.append( 1400 bbmFormsJQ('<span/>') 1401 .text(properties.validationError) 1402 .addClass(BBMForms.constants.errorMsgCssClass) 1403 ); 1404 } 1405 1406 break; 1407 } 1408 case 'FileUpload': { 1409 let $input = bbmFormsJQ('#' + properties.clientProperties.clientId); 1410 let $fieldBlock = $input.closest( 1411 '.' + BBMForms.constants.fieldBlockCssClass 1412 ); 1413 1414 //pre-populate field value 1415 //If previously uploaded file is found, it will display the file name and not display the upload input field. 1416 if ( 1417 bbmFormsJQ.isArray(properties.fieldValues) && 1418 properties.fieldValues.length && 1419 properties.fieldValues[0] 1420 ) { 1421 let fileUrl = properties.fieldValues[0]; 1422 let fileName = fileUrl.split('/').pop(); 1423 let $fileTxt = bbmFormsJQ('<p></p>') 1424 .text('Uploaded file: ') 1425 .addClass(BBMForms.constants.instructionTextCssClass) 1426 .append( 1427 bbmFormsJQ('<a/>', { href: fileUrl, target: '_blank' }).text( 1428 fileName 1429 ) 1430 ); 1431 $fieldBlock.append($fileTxt); 1432 } 1433 1434 //Validation 1435 if (properties.validationError) { 1436 $input.addClass(BBMForms.constants.errorFieldCssClass); 1437 $fieldBlock.append( 1438 bbmFormsJQ('<span/>') 1439 .text(properties.validationError) 1440 .addClass(BBMForms.constants.errorMsgCssClass) 1441 ); 1442 } 1443 1444 break; 1445 } 1446 case 'MultipleChoice': { 1447 let $input = bbmFormsJQ("input[name='" + properties.externalId + "']"); 1448 let $fieldBlock = $input.closest( 1449 '.' + BBMForms.constants.fieldBlockCssClass 1450 ); 1451 let inputType = 'radio'; 1452 1453 //pre-populate field value 1454 var preVal; 1455 if ( 1456 bbmFormsJQ.isArray(properties.fieldValues) && 1457 properties.fieldValues.length && 1458 properties.fieldValues[0] 1459 ) { 1460 preVal = properties.fieldValues[0]; 1461 } 1462 //If no values prefilled from api 1463 else if (properties.name) { 1464 //#1 Url prefill 1465 var values = getUrlParameter(properties.name); 1466 if (values.length) { 1467 preVal = values[0]; 1468 } 1469 //#2 User metadata prefill 1470 else if (userMetadata) { 1471 var propName = getOwnPropertyNameInsensitive( 1472 userMetadata, 1473 properties.name 1474 ); 1475 if (propName) { 1476 var preVals = userMetadata[propName]; 1477 if (bbmFormsJQ.isArray(preVals)) preVal = preVals[0]; 1478 else preVal = preVals; 1479 } 1480 } 1481 } 1482 if (preVal) { 1483 preVal = preVal.replace('"', '\\"').replace("'", "\\'"); 1484 var lowerPreVal = preVal.toLowerCase(); 1485 var matchingValue = $fieldBlock 1486 .find("input[type='" + inputType + "']") 1487 .filter(function () { 1488 var $option = bbmFormsJQ(this); 1489 var optionVal = $option.attr('value').toLowerCase(); 1490 var matchingVal = ''; 1491 if (lowerPreVal == optionVal) { 1492 matchingVal = optionVal; 1493 //select the matching option 1494 $option.prop('checked', true).trigger('change'); 1495 } 1496 return matchingVal.length > 0; 1497 }) 1498 .map(function () { 1499 return bbmFormsJQ(this).attr('value').toLowerCase(); 1500 }); 1501 if (properties.other === true && matchingValue.length == 0) { 1502 //if other option is there set non-matching value to the other text box. 1503 $fieldBlock 1504 .find( 1505 "input[type='" + 1506 inputType + 1507 "'][value='" + 1508 BBMForms.constants.otherText + 1509 "']" 1510 ) 1511 .prop('checked', true) 1512 .trigger('change'); 1513 $fieldBlock 1514 .find('input#' + properties.clientProperties.otherFieldClientId) 1515 .val(preVal) 1516 .prop('disabled', false); 1517 $fieldBlock 1518 .find('input#' + properties.clientProperties.otherFieldClientId) 1519 .closest('.' + BBMForms.constants.otherContainerCssClass) 1520 .show(); 1521 } 1522 } 1523 1524 //Validation 1525 if (properties.validationError) { 1526 $fieldBlock.append(
1527 bbmFormsJQ('<span/>') 1528 .text(properties.validationError) 1529 .addClass(BBMForms.constants.errorMsgCssClass) 1530 ); 1531 } 1532 1533 break; 1534 } 1535 case 'Checkboxes': { 1536 let $input = bbmFormsJQ("input[name='" + properties.externalId + "']"); 1537 let $fieldBlock = $input.closest( 1538 '.' + BBMForms.constants.fieldBlockCssClass 1539 ); 1540 let inputType = 'checkbox'; 1541 1542 //pre-populate field value 1543 let preVals; 1544 if ( 1545 bbmFormsJQ.isArray(properties.fieldValues) && 1546 properties.fieldValues.length 1547 ) { 1548 preVals = properties.fieldValues; 1549 } 1550 //If no values prefilled from api 1551 else if (properties.name) { 1552 //#1 Url prefill 1553 let values = getUrlParameter(properties.name); 1554 if (values.length) { 1555 preVals = values; 1556 } 1557 //#2 User metadata prefill 1558 else if (userMetadata) { 1559 let propName = getOwnPropertyNameInsensitive( 1560 userMetadata, 1561 properties.name 1562 ); 1563 if (propName) { 1564 let vals = userMetadata[propName]; 1565 if (vals) { 1566 if (bbmFormsJQ.isArray(vals) && vals.length) { 1567 preVals = vals; 1568 } else { 1569 preVals = [vals]; 1570 } 1571 } 1572 } 1573 } 1574 } 1575 if (bbmFormsJQ.isArray(preVals) && preVals.length) { 1576 //lowercase values for case-insensitive matching 1577 let lowerPreVals = bbmFormsJQ.map(preVals, function (n, i) { 1578 return n.toLowerCase(); 1579 }); 1580 let matchingValues = $fieldBlock 1581 .find("input[type='" + inputType + "']") 1582 .filter(function () { 1583 let $option = bbmFormsJQ(this); 1584 let optionVal = $option.attr('value').toLowerCase(); 1585 let matchingVal = ''; 1586 bbmFormsJQ.each(lowerPreVals, function (index, value) { 1587 if (value == optionVal) { 1588 matchingVal = value; 1589 //select the matching option 1590 $option.prop('checked', true).trigger('change'); 1591 if ($option.parent().hasClass('detail-checkbox')) { 1592 $option.parent().addClass('selected'); 1593 } 1594 } 1595 }); 1596 return matchingVal.length > 0; 1597 }) 1598 .map(function () { 1599 return bbmFormsJQ(this).attr('value').toLowerCase(); 1600 }); 1601 if (properties.other === true) { 1602 //if other option is there set non-matching value to the other text box. 1603 let nonMatchingValues = lowerPreVals.filter(function (v) { 1604 return bbmFormsJQ.inArray(v, matchingValues) < 0; 1605 }); 1606 if (nonMatchingValues.length) { 1607 let value = nonMatchingValues[0] 1608 .replace('"', '\\"') 1609 .replace("'", "\\'"); 1610 $fieldBlock 1611 .find( 1612 "input[type='" + 1613 inputType + 1614 "'][value='" + 1615 BBMForms.constants.otherText + 1616 "']" 1617 ) 1618 .prop('checked', true) 1619 .trigger('change'); 1620 $fieldBlock 1621 .find('input#' + properties.clientProperties.otherFieldClientId) 1622 .val(value) 1623 .prop('disabled', false); 1624 $fieldBlock 1625 .find('input#' + properties.clientProperties.otherFieldClientId) 1626 .closest('.' + BBMForms.constants.otherContainerCssClass) 1627 .show(); 1628 } 1629 } 1630 } 1631 1632 //Validation 1633 if (properties.validationError) { 1634 $fieldBlock.append( 1635 bbmFormsJQ('<span/>') 1636 .text(properties.validationError) 1637 .addClass(BBMForms.constants.errorMsgCssClass) 1638 ); 1639 } 1640 1641 break; 1642 } 1643 case 'DropdownMenu': { 1644 let $input = bbmFormsJQ('#' + properties.clientProperties.clientId); 1645 let $fieldBlock = $input.closest( 1646 '.' + BBMForms.constants.fieldBlockCssClass 1647 ); 1648 1649 //pre-populate field value 1650 let preVals; 1651 if ( 1652 bbmFormsJQ.isArray(properties.fieldValues) && 1653 properties.fieldValues.length 1654 ) { 1655 preVals = properties.fieldValues; 1656 } 1657 //If no values prefilled from api 1658 else if (properties.name) { 1659 //#1 Url prefill 1660 let values = getUrlParameter(properties.name); 1661 if (values.length) { 1662 preVals = values; 1663 } 1664 //#2 User metadata prefill 1665 else if (userMetadata) {
1666 let propName = getOwnPropertyNameInsensitive( 1667 userMetadata, 1668 properties.name 1669 ); 1670 if (propName) { 1671 let vals = userMetadata[propName]; 1672 if (vals) { 1673 if (bbmFormsJQ.isArray(vals) && vals.length) { 1674 preVals = vals; 1675 } else { 1676 preVals = [vals]; 1677 } 1678 } 1679 } 1680 } 1681 } 1682 if (bbmFormsJQ.isArray(preVals) && preVals.length) { 1683 //lowercase values for case-insensitive matching 1684 let lowerPreVals = bbmFormsJQ.map(preVals, function (n, i) { 1685 return n.toLowerCase(); 1686 }); 1687 let matchingValues = $input 1688 .find('option') 1689 .filter(function () { 1690 let $option = bbmFormsJQ(this); 1691 let optionVal = $option.attr('value').toLowerCase(); 1692 let matchingVal = ''; 1693 bbmFormsJQ.each(lowerPreVals, function (index, value) { 1694 if (value == optionVal) { 1695 matchingVal = value; 1696 //select the matching option 1697 $option.prop('selected', true).trigger('change'); 1698 } 1699 }); 1700 return matchingVal.length > 0; 1701 }) 1702 .map(function () { 1703 return bbmFormsJQ(this).attr('value').toLowerCase(); 1704 }); 1705 if (properties.other === true) { 1706 //if other option is there set non-matching value to the other text box. 1707 let nonMatchingValues = lowerPreVals.filter(function (v) { 1708 return bbmFormsJQ.inArray(v, matchingValues) < 0; 1709 }); 1710 if (nonMatchingValues.length) { 1711 let value = nonMatchingValues[0] 1712 .replace('"', '\\"') 1713 .replace("'", "\\'"); 1714 $input 1715 .find("option[value='" + BBMForms.constants.otherText + "']") 1716 .prop('selected', true) 1717 .trigger('change'); 1718 $fieldBlock 1719 .find('input#' + properties.clientProperties.otherFieldClientId) 1720 .val(value) 1721 .prop('disabled', false); 1722 $fieldBlock 1723 .find('input#' + properties.clientProperties.otherFieldClientId) 1724 .closest('.' + BBMForms.constants.otherContainerCssClass) 1725 .show(); 1726 } 1727 } 1728 } 1729 1730 //Validation 1731 if (properties.validationError) { 1732 $input.addClass(BBMForms.constants.errorFieldCssClass); 1733 $fieldBlock.append( 1734 bbmFormsJQ('<span/>') 1735 .text(properties.validationError) 1736 .addClass(BBMForms.constants.errorMsgCssClass) 1737 ); 1738 } 1739 1740 break; 1741 } 1742 case 'ConsentCheckbox': { 1743 let $input = bbmFormsJQ('#' + properties.clientProperties.clientId); 1744 let $fieldBlock = $input.closest( 1745 '.' + BBMForms.constants.fieldBlockCssClass 1746 ); 1747 1748 //pre-populate field value 1749 if ( 1750 bbmFormsJQ.isArray(properties.fieldValues) && 1751 properties.fieldValues.length && 1752 properties.fieldValues[0] && 1753 properties.fieldValues[0].toUpperCase() === 'YES' 1754 ) { 1755 $input.prop('checked', true).trigger('change'); 1756 } 1757 1758 //Validation 1759 if (properties.validationError) { 1760 $fieldBlock.append( 1761 bbmFormsJQ('<span/>') 1762 .text(properties.validationError) 1763 .addClass(BBMForms.constants.errorMsgCssClass) 1764 ); 1765 } 1766 1767 break; 1768 } 1769 } 1770 } 1771 1772 function createProgressIndicator(progressType, stepNum, stepsCount) { 1773 switch (progressType) { 1774 case 'Steps': { 1775 var $progInd = bbmFormsJQ('<ul/>') 1776 .addClass(BBMForms.constants.progressIndicatorCssClass) 1777 .addClass('align-center padding-bottom-30'); 1778 for (var i = 0; i < stepsCount; i++) { 1779 var sn = i + 1; 1780 var $step = bbmFormsJQ('<li/>').text( 1781 BBMForms.constants.stepText + ' ' + sn 1782 ); 1783 if (sn == stepNum) { 1784 $step.addClass(BBMForms.constants.progressCurrentStepCssClass); 1785 } 1786 $progInd.append($step); 1787 } 1788 return $progInd; 1789 } 1790 case 'Bar': { 1791 var percentage = Math.round(((stepNum - 1) / stepsCount) * 100); 1792 var $progInd = bbmFormsJQ('<div/>') 1793 .addClass(BBMForms.constants.progressIndicatorCssClass) 1794 .addClass('col-xs-8 col-sm-4 col-md-4 col-center padding-bottom-20'); 1795 var $container = bbmFormsJQ('<div/>', { 1796 role: 'progressbar', 1797 'aria-valuenow': percentage, 1798 'aria-valuemin': 0, 1799 'aria-valuemax': 100, 1800 }) 1801 .addClass(BBMForms.constants.progressBarContainerCssClass) 1802 .addClass('margin-bottom-10'); 1803 var $bar = bbmFormsJQ('<div/>') 1804 .addClass(BBMForms.constants.progressBarCssClass) 1805 .css('width', percentage + '%'); 1806 $container.append($bar); 1807 $progInd.append($container);
1808 $progInd.append(bbmFormsJQ('<span/>').text(percentage + '%')); 1809 return $progInd; 1810 } 1811 case 'StepsWithProgressBar': { 1812 var percentage = Math.round(((stepNum - 1) / stepsCount) * 100); 1813 var $progInd = bbmFormsJQ('<div/>') 1814 .addClass(BBMForms.constants.progressIndicatorCssClass) 1815 .addClass('col-xs-8 col-sm-4 col-md-4 col-center padding-bottom-20'); 1816 var $container = bbmFormsJQ('<div/>', { 1817 role: 'progressbar', 1818 'aria-valuenow': percentage, 1819 'aria-valuemin': 0, 1820 'aria-valuemax': 100, 1821 }) 1822 .addClass(BBMForms.constants.progressBarContainerCssClass) 1823 .addClass('margin-bottom-10'); 1824 var $bar = bbmFormsJQ('<div/>') 1825 .addClass(BBMForms.constants.progressBarCssClass) 1826 .css('width', percentage + '%'); 1827 $container.append($bar); 1828 $progInd.append($container); 1829 $progInd.append( 1830 bbmFormsJQ('<span/>').text('Step ' + stepNum + ' of ' + stepsCount) 1831 ); 1832 return $progInd; 1833 } 1834 } 1835 } 1836 1837 function updateFormDataForSingleValue(properties) { 1838 properties.fieldValues = []; 1839 var value = bbmFormsJQ('#' + properties.clientProperties.clientId).val(); 1840 if (value) { 1841 properties.fieldValues.push(value); 1842 } 1843 } 1844 1845 function updateFormDataForFile(properties) { 1846 properties.fieldValues = []; 1847 if (bbmFormsJQ('#' + properties.clientProperties.clientId)[0]) { 1848 var value = bbmFormsJQ('#' + properties.clientProperties.clientId)[0] 1849 .files[0]; 1850 if (value) { 1851 properties.fieldValues.push(value); 1852 } 1853 } 1854 } 1855 1856 function updateFormDataForDropDown(properties) { 1857 properties.fieldValues = []; 1858 var value = bbmFormsJQ('#' + properties.clientProperties.clientId).val(); 1859 1860 //Use other field value if "other" is selected from dropdown 1861 if (properties.other === true && value === BBMForms.constants.otherText) { 1862 value = bbmFormsJQ( 1863 '#' + properties.clientProperties.otherFieldClientId 1864 ).val(); 1865 } 1866 1867 if (value) { 1868 properties.fieldValues.push(value); 1869 } 1870 } 1871 1872 function updateFormDataForRadio(properties) { 1873 properties.fieldValues = []; 1874 var value = bbmFormsJQ( 1875 "input[type='radio'][name='" + properties.externalId + "']:checked" 1876 ).val(); 1877 1878 //Use other field value if "other" is selected from dropdown 1879 if (properties.other === true && value === BBMForms.constants.otherText) { 1880 value = bbmFormsJQ( 1881 '#' + properties.clientProperties.otherFieldClientId 1882 ).val(); 1883 } 1884 1885 if (value) { 1886 properties.fieldValues.push(value); 1887 } 1888 } 1889 1890 function updateFormDataForCheckBox(properties) { 1891 properties.fieldValues = []; 1892 bbmFormsJQ( 1893 "input[type='checkbox'][name='" + properties.externalId + "']:checked" 1894 ).each(function () { 1895 var value = bbmFormsJQ(this).val(); 1896 1897 //Use other field value if "other" is selected from dropdown 1898 if (properties.other === true && value === BBMForms.constants.otherText) { 1899 value = bbmFormsJQ( 1900 '#' + properties.clientProperties.otherFieldClientId 1901 ).val(); 1902 } 1903 1904 if (value) { 1905 properties.fieldValues.push(value); 1906 } 1907 }); 1908 } 1909 1910 function updateFormDataForConsentCheckBox(properties) { 1911 properties.fieldValues = []; 1912 var $field = bbmFormsJQ('#' + properties.clientProperties.clientId); 1913 var $fieldHidden = bbmFormsJQ( 1914 '#' + properties.clientProperties.clientId + '_hidden' 1915 ); 1916 var checked = $field.prop('checked'); 1917 var $fieldBlock = $field.closest( 1918 '.' + BBMForms.constants.fieldBlockCssClass 1919 ); 1920 var value = ''; 1921 if ($fieldBlock.is(':visible')) { 1922 value = checked ? 'Yes' : 'No'; 1923 } 1924 properties.fieldValues.push(value); 1925 $fieldHidden.val(value); 1926 } 1927 1928 function displayBypass(self, errMsg) { 1929 self.$formBypass.empty(); 1930 self.$formBypass.append( 1931 bbmFormsJQ('<p/>') 1932 .css('text-align', 'right') 1933 .append( 1934 bbmFormsJQ('<a/>') 1935 .attr('href', 'javascript:void(0)') 1936 .text('Back') 1937 .click(function () {
1938 self.display(); 1939 }) 1940 ) 1941 ); 1942 if (self.formData.form.advancedOptions.formBypassDescription) { 1943 self.$formBypass.append( 1944 bbmFormsJQ('<p/>') 1945 .css('text-align', 'left') 1946 .text(self.formData.form.advancedOptions.formBypassDescription) 1947 ); 1948 } 1949 1950 self.$errorMessage.empty().hide(); 1951 1952 if (!self.formData.form.advancedOptions.formBypassFieldId) { 1953 self.$errorMessage 1954 .html(bbmFormsJQ('<p/>').text('Unique field is undefined.')) 1955 .show(); 1956 return; 1957 } 1958 1959 var bypassField = findFieldByExternalId( 1960 self.formData.form.steps, 1961 self.formData.form.advancedOptions.formBypassFieldId 1962 ); 1963 if (!bypassField) { 1964 self.$errorMessage 1965 .html(bbmFormsJQ('<p/>').text('Unique field is not found.')) 1966 .show(); 1967 return; 1968 } 1969 1970 self.$progressIndicatorContainer.html(''); 1971 1972 if (errMsg) { 1973 self.$errorMessage.html(bbmFormsJQ('<p/>').text(errMsg)).show(); 1974 } 1975 1976 //Empty out all steps. 1977 self.$stepsWrapper.empty(); 1978 1979 //Create new step container 1980 var stepContainerId = self.$form.attr('id') + '_bypass'; 1981 self.$currentStepContainer = bbmFormsJQ('<div/>', { 1982 id: stepContainerId, 1983 }).addClass(BBMForms.constants.stepContainerCssClass); 1984 self.$stepsWrapper.append(self.$currentStepContainer); 1985 1986 addField(self.$currentStepContainer, bypassField); 1987 1988 var $buttonsContainer = bbmFormsJQ('<div/>') 1989 .addClass(BBMForms.constants.buttonsContainerCssClass) 1990 .addClass(getBootstrapColumnClasses('full')); 1991 self.$currentStepContainer.append($buttonsContainer); 1992 1993 //If this is a multi-step form with more steps to go, show the continue button. 1994 var nextBtnTxt = self.formData.form.advancedOptions.actionButtonText 1995 ? self.formData.form.advancedOptions.actionButtonText 1996 : BBMForms.constants.submitButtonTextDefault; 1997 var nextBtnColor = self.formData.form.advancedOptions.buttonColor 1998 ? self.formData.form.advancedOptions.buttonColor 1999 : BBMForms.constants.submitButtonColorDefault; 2000 var $nextBtn = bbmFormsJQ('<button/>') 2001 .text(nextBtnTxt) 2002 .attr('title', nextBtnTxt) 2003 .addClass(BBMForms.constants.nextButtonCssClass) 2004 .attr('style', 'background-color:#'.concat(nextBtnColor, ';')) 2005 .click(function (e) { 2006 e.preventDefault(); 2007 2008 //Disable button to prevent multiple clicks. 2009 var $allBtns = $buttonsContainer.find('button').prop('disabled', true); 2010 var $btn = bbmFormsJQ(this).text('Processing...'); 2011 bypassForm(self); 2012 }); 2013 $buttonsContainer.append($nextBtn); 2014 2015 if (self.formData.form.advancedOptions.disclaimer) { 2016 //wysiwyg 2017 var disclaimerHtml = decodeHtml( 2018 self.formData.form.advancedOptions.disclaimer 2019 ); 2020 var $disclaimer = bbmFormsJQ('<div/>') 2021 .addClass(BBMForms.constants.disclaimerCssClass) 2022 .addClass(getBootstrapColumnClasses('full')) 2023 .html(disclaimerHtml); 2024 self.$currentStepContainer.append($disclaimer); 2025 } 2026 2027 if (self.options.callbackBypassStepLoaded) { 2028 self.options.callbackBypassStepLoaded(); 2029 } 2030 } 2031 2032 function findFieldByExternalId(steps, externalId) { 2033 for (var i = 0, lenI = steps.length; i < lenI; i++) { 2034 for (var j = 0, lenJ = steps[i].length; j < lenJ; j++) { 2035 if (steps[i][j].externalId == externalId) return steps[i][j]; 2036 } 2037 } 2038 return null; 2039 } 2040 2041 function addField(self, item) { 2042 var $container = self.$currentStepContainer; 2043 var $fieldBlock; 2044 switch (item.fieldType) { 2045 case 'SingleLineText': 2046 $fieldBlock = createSingleLineFormBlock( 2047 item, 2048 'text', 2049 self.options.userMetadata 2050 ); 2051 break; 2052 case 'SingleLineEmailAddress': 2053 $fieldBlock = createSingleLineFormBlock( 2054 item, 2055 'email', 2056 self.options.userMetadata 2057 ); 2058 break; 2059 case 'SingleLineURL': 2060 $fieldBlock = createSingleLineFormBlock( 2061 item, 2062 'url', 2063 self.options.userMetadata 2064 ); 2065 break; 2066 case 'SingleLineDate': 2067 $fieldBlock = createSingleLineFormBlock( 2068 item, 2069 'date', 2070 self.options.userMetadata 2071 ); 2072 break; 2073 case 'SingleLineNumber': 2074 $fieldBlock = createSingleLineFormBlock( 2075 item, 2076 'number', 2077 self.options.userMetadata, 2078 { min: 0, step: 1 } 2079 ); 2080 break; 2081 case 'SingleLinePhoneNumber': 2082 $fieldBlock = createSingleLineFormBlock( 2083 item, 2084 'tel', 2085 self.options.userMetadata 2086 ); 2087 break; 2088 case 'FileUpload': 2089 $fieldBlock = createFileFormBlock(item); 2090 break; 2091 case 'ParagraphText': 2092 $fieldBlock = createParagraphTextFormBlock( 2093 item, 2094 self.options.userMetadata 2095 ); 2096 break; 2097 case 'MultipleChoice': 2098 $fieldBlock = createMultipleChoiceFormBlock( 2099 item, 2100 self.options.userMetadata 2101 ); 2102 break; 2103 case 'Checkboxes': 2104 $fieldBlock = createCheckboxesFormBlock( 2105 item, 2106 self.options.userMetadata 2107 ); 2108 break; 2109 case 'DropdownMenu': 2110 $fieldBlock = createDropdownMenuFormBlock( 2111 item, 2112 self.options.userMetadata 2113 ); 2114 break; 2115 case 'ConsentCheckbox': 2116 $fieldBlock = createConsentCheckboxFormBlock(item); 2117 break; 2118 case 'SectionDivider': 2119 $fieldBlock = createSectionDividerFormBlock(item); 2120 break; 2121 case 'SingleLineZipCode': 2122 $fieldBlock = createSingleLineZipCode(item, self.options.userMetadata); 2123 break; 2124 case 'FieldPayment': 2125 $fieldBlock = createPaymentCart(item, self.options.userMetadata); 2126 //Remove row class to display shopping cart properly 2127 $container.closest('form').parent().removeClass('row'); 2128 break; 2129 } 2130 if ($fieldBlock) { 2131 if (item.display == false) $fieldBlock.hide(); 2132 $container.append($fieldBlock); 2133 } 2134 } 2135 2136 function displayLoadingStep(self, stepToDisplay, isLastStep) { 2137 if (self.formData.form.advancedOptions.stepTransition == null) { 2138 return false;
2139 } 2140 2141 //Don't display if going back a step 2142 if (self.$loadingIndicatorContainer.hasClass('goback')) { 2143 self.$loadingIndicatorContainer.removeClass('goback'); 2144 return false; 2145 } 2146 2147 //Don't display custom loading message if refreshed page or revisiting page 2148 if (self.$loadingIndicatorContainer.hasClass('firstload')) { 2149 self.$loadingIndicatorContainer.removeClass('firstload'); 2150 return false; 2151 } 2152 2153 //Don't load on the first step 2154 if (stepToDisplay <= 1) { 2155 return false; 2156 } 2157 2158 var loadingMessage = 2159 self.formData.form.advancedOptions.stepTransition[stepToDisplay - 1]; 2160 if (!loadingMessage) { 2161 return false; 2162 } 2163 2164 //Hide the form contents 2165 var formId = BBMForms.constants.formNamePrefix + self.element.id; 2166 bbmFormsJQ('#' + formId).css('display', 'none'); 2167 2168 var $loadingMessageBlock = bbmFormsJQ('<p/>').text(loadingMessage); 2169 var $loadingImageBlock = bbmFormsJQ('<div/>') 2170 .addClass('loading-gif') 2171 .append('<img src=' + BBMForms.constants.loadingImagePath + ' />'); 2172 self.$loadingIndicatorContainer.html($loadingMessageBlock); 2173 self.$loadingIndicatorContainer.append($loadingImageBlock); 2174 2175 var transitionTime = BBMForms.defaultOptions.loadingTime; 2176 if ( 2177 self.formData.form.advancedOptions.stepTransitionTime != null && 2178 self.formData.form.advancedOptions.stepTransitionTime > 0 2179 ) 2180 transitionTime = 2181 self.formData.form.advancedOptions.stepTransitionTime * 1000; //convert to ms 2182 2183 let hasReturnUrl = 2184 self.options.returnUrl || 2185 self.formData.form.advancedOptions.confirmationActionURL; 2186 if (isLastStep && hasReturnUrl) { 2187 let returnUrl = self.options.returnUrl 2188 ? self.options.returnUrl 2189 : self.formData.form.advancedOptions.confirmationActionURL; 2190 setTimeout( 2191 returnFromLoadingStep.bind(null, self, returnUrl), 2192 transitionTime 2193 ); 2194 } else { 2195 setTimeout(hideLoadingStep.bind(null, self), transitionTime); 2196 } 2197 2198 return true; 2199 } 2200 2201 function hideLoadingStep(self) { 2202 self.$loadingIndicatorContainer.empty(); 2203 var formId = BBMForms.constants.formNamePrefix + self.element.id; 2204 bbmFormsJQ('#' + formId).css('display', ''); 2205 } 2206 2207 function returnFromLoadingStep(self, returnUrl) { 2208 if (returnUrl) { 2209 if (self.options.callbackPostPassThrough) { 2210 let leadRecordsCopy = null; 2211 if ( 2212 self.formData.hasOwnProperty('leadRecords') && 2213 typeof self.formData.leadRecords != 'undefined' 2214 ) { 2215 leadRecordsCopy = Object.assign({}, self.formData.leadRecords); 2216 } 2217 //let leadRecordsFullCopy = null; 2218 //if (self.formData.hasOwnProperty('leadRecordsFull') && typeof self.formData.leadRecordsFull != "undefined") { 2219 // leadRecordsFullCopy = Object.assign({}, self.formData.leadRecordsFull); 2220 //} 2221 let formDataCopy = Object.assign({}, self.formData); 2222 //leadrecords are being returned as separate param so remove from formData which is in the same return params set 2223 if (formDataCopy.hasOwnProperty('leadRecords')) 2224 delete formDataCopy.leadRecords; 2225 if (formDataCopy.hasOwnProperty('leadRecordsFull')) 2226 delete formDataCopy.leadRecordsFull; 2227 2228 self.options.callbackPostPassThrough( 2229 leadRecordsCopy, 2230 null, 2231 null, 2232 formDataCopy 2233 ); 2234 } 2235 2236 window.location = returnUrl; 2237 } 2238 } 2239 2240 function displayStep(self, stepToDisplay, errMsg) { 2241 self.$progressIndicatorContainer.html( 2242 createProgressIndicator( 2243 self.formData.form.advancedOptions.showProgress, 2244 stepToDisplay, 2245 self.formData.form.steps.length 2246 ) 2247 ); 2248 2249 //Display validation error messages 2250 self.$errorMessage.empty().hide(); 2251 var displayError = errMsg != undefined; 2252 if (errMsg) { 2253 self.$errorMessage.append(bbmFormsJQ('<p/>').text(errMsg)).show(); 2254 } 2255 if (self.formData.form.validationError) { 2256 var msgHtml = decodeHtml(self.formData.form.validationError); 2257 self.$errorMessage.append(msgHtml).show(); 2258 displayError = true; 2259 } 2260 2261 //If no error, display loading step 2262 if (!displayError) displayLoadingStep(self, stepToDisplay); 2263 2264 //Empty out all steps. 2265 self.$stepsWrapper.empty(); 2266 2267 //Create new step container 2268 var stepContainerId = self.$form.attr('id') + '_' + stepToDisplay; 2269 self.$currentStepContainer = bbmFormsJQ('<div/>', { 2270 id: stepContainerId, 2271 }).addClass(BBMForms.constants.stepContainerCssClass); 2272 self.$currentStepContainer.append( 2273 bbmFormsJQ('<input/>', { type: 'hidden', name: 'stepToDisplay' }).val( 2274 stepToDisplay 2275 ) 2276 ); 2277 self.$stepsWrapper.append(self.$currentStepContainer); 2278 2279 //If this is a multi-step form with more steps to go, show the continue button. 2280 var isLastStep = stepToDisplay == self.formData.form.steps.length; 2281 2282 var $buttonsContainer = bbmFormsJQ('<div/>') 2283 .addClass(BBMForms.constants.buttonsContainerCssClass) 2284 .addClass(getBootstrapColumnClasses('full')); 2285 var nextBtnTxt = isLastStep 2286 ? self.formData.form.advancedOptions.actionButtonText 2287 ? self.formData.form.advancedOptions.actionButtonText 2288 : BBMForms.constants.submitButtonTextDefault 2289 : BBMForms.constants.nextStepButtonText; 2290 var nextBtnColor = self.formData.form.advancedOptions.buttonColor 2291 ? self.formData.form.advancedOptions.buttonColor 2292 : BBMForms.constants.submitButtonColorDefault; 2293 var $nextBtn = bbmFormsJQ('<button/>') 2294 .text(nextBtnTxt) 2295 .attr('title', nextBtnTxt) 2296 .addClass(BBMForms.constants.nextButtonCssClass) 2297 .attr('style', 'background-color:#'.concat(nextBtnColor, ';')); 2298 2299 if (self.options.enableAutoProgress == true) { 2300 self.options.$nextBtn = $nextBtn; 2301 } 2302 2303 $nextBtn.click(function (e) { 2304 e.preventDefault(); 2305 2306 //Disable button to prevent multiple clicks.
2307 var $allBtns = $buttonsContainer.find('button').prop('disabled', true); 2308 var $btn = bbmFormsJQ(this).text('Processing...'); 2309 continueForm(self); 2310 }); 2311 2312 var stepIndex = stepToDisplay - 1; 2313 var isPaymentStep = false; 2314 var paymentFieldId = 0; 2315 bbmFormsJQ.each( 2316 self.formData.form.steps[stepIndex], 2317 function (index, item) { 2318 //If a step is a single field that only accepts a single selection answer, enable auto-progress 2319 if ( 2320 self.options.enableAutoProgress == true && 2321 self.formData.form.steps[stepIndex].length == 1 && 2322 !isLastStep && 2323 (self.formData.form.steps[stepIndex][0].fieldType == 2324 'MultipleChoice' || 2325 self.formData.form.steps[stepIndex][0].fieldType == 'DropdownMenu') 2326 ) { 2327 //assign auto progression properties 2328 item.enableAutoProgress = true; 2329 item.$enableAutoProgressBtn = self.options.$nextBtn; 2330 } 2331 if (item.fieldType == 'FileUpload') { 2332 item.buttonColor = nextBtnColor; 2333 } 2334 addField(self, item); 2335 2336 if (item.fieldType == 'FieldPayment') { 2337 isPaymentStep = true; 2338 paymentFieldId = item.externalId; 2339 } 2340 } 2341 ); 2342 2343 //Conditional Display 2344 BBMForms.conditionalDisplayChangeEvents = []; 2345 bbmFormsJQ.each( 2346 self.formData.form.steps[stepIndex], 2347 function (index, item) { 2348 configureConditionalDisplay(self, item); 2349 } 2350 ); 2351 2352 bbmFormsJQ.each( 2353 self.formData.form.steps[stepIndex], 2354 function (index, item) { 2355 prePopulateAndValidate(self, item); 2356 } 2357 ); 2358
2359 //Append the Consent Text before submit button 2360 if ( 2361 (isLastStep || self.formData.form.isConditionalForm) && 2362 self.formData.form.consentLevelText 2363 ) { 2364 var consentHtml = decodeHtml(self.formData.form.consentLevelText); 2365 var $consentLevel = bbmFormsJQ('<div/>') 2366 .addClass(BBMForms.constants.disclaimerCssClass) 2367 .addClass(getBootstrapColumnClasses('full')) 2368 .html(consentHtml); 2369 self.$currentStepContainer.append($consentLevel); 2370 } 2371 2372 //Append buttons container after the steps fields have been added 2373 self.$currentStepContainer.append($buttonsContainer); 2374 2375 //Hide submit button for payment step but allow user to go back and update stuff 2376 //the CHECKOUT button should replace this submit button 2377 if (!isPaymentStep) { 2378 $buttonsContainer.append($nextBtn); 2379 } else { 2380 var $checkoutBtn = bbmFormsJQ('<button/>') 2381 .addClass('btn btn-primary btn-block next-button mt-4 checkoutBtn') 2382 .text(BBMForms.constants.paymentCheckoutBtnDisableText) 2383 .attr('title', BBMForms.constants.paymentCheckoutBtnText); 2384 $checkoutBtn.prop('disabled', true); //disable button unless there is at least one checkbox checked off 2385 var $mobileCheckoutBtn = bbmFormsJQ('<button/>') 2386 .addClass('next-button btn btn-primary btn-primary-large checkoutBtn') 2387 .text(BBMForms.constants.paymentCheckoutBtnDisableText) 2388 .attr('title', BBMForms.constants.paymentCheckoutBtnText); 2389 $mobileCheckoutBtn.prop('disabled', true); 2390 2391 $checkoutBtn.click(function (e) { 2392 e.preventDefault(); 2393 2394 //Disable button to prevent multiple clicks. 2395 $buttonsContainer.find('button').prop('disabled', true); 2396 bbmFormsJQ(this).text('Processing...'); 2397 continueToPayments(self, paymentFieldId); 2398 }); 2399 $mobileCheckoutBtn.click(function (e) { 2400 e.preventDefault(); 2401 2402 //Disable button to prevent multiple clicks. 2403 $buttonsContainer.find('button').prop('disabled', true); 2404 bbmFormsJQ(this).text('Processing...'); 2405 continueToPayments(self, paymentFieldId); 2406 }); 2407 var $cartContainer = bbmFormsJQ('.cart-desktop'); 2408 $cartContainer.append($checkoutBtn); 2409 bbmFormsJQ('.cart-mobile').append($mobileCheckoutBtn); 2410 } 2411 2412 var canGoBack = stepToDisplay > 1; 2413 if (canGoBack) { 2414 //Back button to show to the right so it can drop to next line on small width 2415 var backBtnTxt = BBMForms.constants.previousStepButtonText; 2416 var $backBtn = bbmFormsJQ('<button/>') 2417 .text(backBtnTxt) 2418 .addClass(BBMForms.constants.previousButtonCssClass) 2419 .data('text', backBtnTxt) 2420 .click(function (e) { 2421 e.preventDefault(); 2422 //Disable button to prevent multiple clicks. 2423 var $allBtns = $buttonsContainer 2424 .find('button') 2425 .prop('disabled', true); 2426 var $btn = bbmFormsJQ(this).text('Processing...'); 2427 backToStep(self, stepToDisplay - 1); 2428 }); 2429 $buttonsContainer.append($backBtn); 2430 } 2431 2432 if (isLastStep && self.formData.form.advancedOptions.disclaimer) { 2433 //wysiwyg 2434 var disclaimerHtml = decodeHtml( 2435 self.formData.form.advancedOptions.disclaimer 2436 ); 2437 var $disclaimer = bbmFormsJQ('<div/>') 2438 .addClass(BBMForms.constants.disclaimerCssClass) 2439 .addClass(getBootstrapColumnClasses('full')) 2440 .html(disclaimerHtml); 2441 self.$currentStepContainer.append($disclaimer); 2442 } 2443 } 2444 2445 function updateFieldClientProperties(self) { 2446 bbmFormsJQ.each(self.formData.form.steps, function (index, step) { 2447 bbmFormsJQ.each(step, function (index, item) { 2448 //set clientId 2449 var fieldId = 2450 BBMForms.constants.fieldIdPrefix + self.element.id + item.externalId; 2451 //Create client-only properties to keep information needed for client use only. This will be deleted when posting. 2452 item.clientProperties = item.clientProperties || {}; 2453 //Create clientId to access on submit 2454 item.clientProperties.clientId = fieldId; 2455 }); 2456 }); 2457 } 2458 2459 function updateFormDataFromInput(self) { 2460 //Go through form fields and update formData field values. 2461 var stepIndex = self.formData.stepToDisplay - 1; 2462 bbmFormsJQ.each( 2463 self.formData.form.steps[stepIndex], 2464 function (index, item) { 2465 updateFormDataFromInputForSingleField(item); 2466 } 2467 ); 2468 } 2469 2470 function updateFormDataFromInputForSingleField(item) { 2471 switch (item.fieldType) { 2472 case 'SingleLineText': 2473 case 'SingleLineEmailAddress': 2474 case 'SingleLineURL': 2475 case 'SingleLineDate': 2476 case 'SingleLineNumber': 2477 case 'ParagraphText': 2478 updateFormDataForSingleValue(item); 2479 break; 2480 case 'FileUpload': 2481 updateFormDataForFile(item); 2482 break; 2483 case 'DropdownMenu': 2484 updateFormDataForDropDown(item); 2485 break; 2486 case 'MultipleChoice': 2487 updateFormDataForRadio(item); 2488 break; 2489 case 'Checkboxes': 2490 updateFormDataForCheckBox(item); 2491 break; 2492 case 'ConsentCheckbox': 2493 updateFormDataForConsentCheckBox(item); 2494 break; 2495 } 2496 } 2497 2498 function createFormData(self) { 2499 //set/update metadata before submiting the form. 2500 setMetaData(self); 2501 2502 //Clean up other fields. 2503 2504 //Do not send the empty dropdown value for the first option "- select -" if Other input field is filled. 2505 var selectToRemove = []; 2506 self.$form 2507 .find( 2508 "input:enabled[id$='" + BBMForms.constants.otherFieldIdSuffix + "']" 2509 ) 2510 .closest('.' + BBMForms.constants.fieldBlockCssClass) 2511 .find('select') 2512 .each(function () { 2513 if ( 2514 bbmFormsJQ(this).find('option:selected').length == 1 && 2515 bbmFormsJQ(this).find('option:selected')[0].value == '' 2516 ) { 2517 selectToRemove.push(bbmFormsJQ(this)); 2518 } 2519 }); 2520 bbmFormsJQ.each(selectToRemove, function (index, $item) { 2521 $item.remove(); 2522 }); 2523 //If "Other" is selected/checked, the other input field value is sent regardless of it being empty or not. 2524 2525 //Remove hidden checkbox field if there are values selected so the empty value does not get sent with the actual values 2526 //Note!! This should be performed before below where we do the removal of the Other checkbox if other input field is shown.
2527 self.$form 2528 .find( 2529 "input[id$='" + 2530 '_hidden' + 2531 BBMForms.constants.multipleCheckboxNameSuffix + 2532 "']" 2533 ) 2534 .each(function () { 2535 if ( 2536 self.$form.find("input[name$='" + this.name + "']:checked").length > 0 2537 ) 2538 this.parentElement.removeChild(this); //workaround for IE11 2539 }); 2540 2541 //Remove hidden radiobutton field if there are values selected so the empty value does not get sent with the actual values 2542 //Note!! This should be performed before below where we do the removal of the Other radiobutton if other input field is shown. 2543 self.$form 2544 .find( 2545 "input[id$='" + 2546 '_hidden' + 2547 BBMForms.constants.multipleRadioButtonNameSuffix + 2548 "']" 2549 ) 2550 .each(function () { 2551 if ( 2552 self.$form.find("input[name$='" + this.name + "']:checked").length > 0 2553 ) 2554 this.parentElement.removeChild(this); //workaround for IE11 2555 }); 2556 2557 //Do not send the "Other" dropdown/checkbox/radio value if Other input field is shown.
2558 self.$form 2559 .find( 2560 "input:enabled[id$='" + BBMForms.constants.otherFieldIdSuffix + "']" 2561 ) 2562 .closest('.' + BBMForms.constants.fieldBlockCssClass) 2563 .find('select') 2564 .remove(); 2565 self.$form 2566 .find( 2567 "input:enabled[id$='" + BBMForms.constants.otherFieldIdSuffix + "']" 2568 ) 2569 .closest('.' + BBMForms.constants.fieldBlockCssClass) 2570 .find( 2571 "input[type='checkbox'][value='" + BBMForms.constants.otherText + "']" 2572 ) 2573 .remove(); 2574 self.$form 2575 .find( 2576 "input:enabled[id$='" + BBMForms.constants.otherFieldIdSuffix + "']" 2577 ) 2578 .closest('.' + BBMForms.constants.fieldBlockCssClass) 2579 .find("input[type='radio'][value='" + BBMForms.constants.otherText + "']") 2580 .remove(); 2581 2582 //If "Other" is not selected/checked, remove the other input field so the empty value does not get sent. 2583 self.$form 2584 .find( 2585 "input:disabled[id$='" + BBMForms.constants.otherFieldIdSuffix + "']" 2586 ) 2587 .remove(); 2588 2589 //Remove consent checkboxes. Hidden field has the actual value. 2590 self.$form 2591 .find( 2592 "input[name$='" + BBMForms.constants.consentCheckboxNameSuffix + "']" 2593 ) 2594 .remove(); 2595 2596 //Remove display false fields except consent to prevent causing required/validation errors on the server. 2597 //ConsentCheckbox is an exception and is not removed because we need its data (the fact that it was not displayed to the user with empty value) stored on the server. 2598 var stepIndex = self.formData.stepToDisplay - 1; 2599 bbmFormsJQ.each( 2600 self.formData.form.steps[stepIndex], 2601 function (index, item) { 2602 if (item.fieldType != 'ConsentCheckbox' && item.display == false) { 2603 self.$form.find("[name='" + item.externalId + "']").remove(); 2604 } 2605 } 2606 ); 2607 2608 return self.$form; 2609 } 2610 2611 function continueForm(self) { 2612 //Empty out and hide error message 2613 self.$errorMessage.empty().hide(); 2614 2615 //Update formData object with user input data 2616 updateFormDataFromInput(self); 2617 2618 //Post this.formData to api endpoint and validate 2619 var $fData = createFormData(self); 2620 2621 if ( 2622 window.FormData && 2623 navigator.userAgent.indexOf('MSIE') < 0 && 2624 navigator.appVersion.indexOf('Trident/') < 0 2625 ) { 2626 var fData = new FormData($fData[0]); 2627 postFormData( 2628 fData, 2629 function (data) { 2630 var stepToDisplay = self.formData.stepToDisplay + 1; 2631 self.formData = data; 2632 2633 updateFieldClientProperties(self); 2634 2635 var displayLoading = displayLoadingStep(self, stepToDisplay, true); 2636 2637 //if returnUrl exist, need to defer the return for the loading transition to complete 2638 let hasReturnUrl = 2639 self.options.returnUrl || 2640 self.formData.form.advancedOptions.confirmationActionURL; 2641 var deferReturn = hasReturnUrl && displayLoading; 2642 2643 //Pass through. We have a completed lead already. 2644 handleCompletion(self, deferReturn); 2645 }, 2646 function (data) { 2647 self.formData = data; 2648 2649 updateFieldClientProperties(self); 2650 2651 //Incomplete lead. Show form. 2652 displayStep(self, self.formData.stepToDisplay); 2653 2654 if (self.options.callbackNextStepLoaded) { 2655 self.options.callbackNextStepLoaded(self.formData.stepToDisplay); 2656 } 2657 2658 scrollUpToTop(self); 2659 }, 2660 function (errorMessage) { 2661 //Failed to post. 2662 displayStep(self, self.formData.stepToDisplay); 2663 2664 if (self.options.callbackPostError) { 2665 self.options.callbackPostError(self.formData.stepToDisplay); 2666 } 2667 2668 self.$errorMessage.html(bbmFormsJQ('<p/>').text(errorMessage)).show(); 2669 scrollUpToTop(self); 2670 } 2671 ); 2672 } else { 2673 var fData = $fData.serialize(); 2674 postSerializedForm( 2675 fData, 2676 function (data) { 2677 var stepToDisplay = self.formData.stepToDisplay + 1; 2678 self.formData = data; 2679 2680 updateFieldClientProperties(self); 2681 2682 var displayLoading = displayLoadingStep(self, stepToDisplay, true); 2683 2684 //if returnUrl exist, need to defer the return for the loading transition to complete 2685 let hasReturnUrl = 2686 self.options.returnUrl || 2687 self.formData.form.advancedOptions.confirmationActionURL; 2688 var deferReturn = hasReturnUrl && displayLoading; 2689 2690 //Pass through. We have a completed lead already. 2691 handleCompletion(self, deferReturn); 2692 }, 2693 function (data) { 2694 self.formData = data; 2695 2696 updateFieldClientProperties(self); 2697 2698 //Incomplete lead. Show form. 2699 displayStep(self, self.formData.stepToDisplay); 2700 2701 if (self.options.callbackNextStepLoaded) { 2702 self.options.callbackNextStepLoaded(self.formData.stepToDisplay); 2703 } 2704 2705 scrollUpToTop(self); 2706 }, 2707 function (errorMessage) { 2708 //Failed to post. 2709 displayStep(self, self.formData.stepToDisplay); 2710 2711 if (self.options.callbackPostError) { 2712 self.options.callbackPostError(self.formData.stepToDisplay); 2713 } 2714 2715 self.$errorMessage.html(bbmFormsJQ('<p/>').text(errorMessage)).show(); 2716 scrollUpToTop(self); 2717 } 2718 ); 2719 } 2720 } 2721 2722 function continueToPayments(self, paymentExternalFieldId) { 2723 //Empty out and hide error message 2724 self.$errorMessage.empty().hide(); 2725 2726 //Update formData object with user input data 2727 updateFormDataFromInput(self); 2728 2729 //Post this.formData to api endpoint and validate 2730 var $fData = createFormData(self); 2731 2732 //Posted information 2733 if ( 2734 window.FormData && 2735 navigator.userAgent.indexOf('MSIE') < 0 && 2736 navigator.appVersion.indexOf('Trident/') < 0 2737 ) { 2738 var fData = new FormData($fData[0]); 2739 postFormData( 2740 fData, 2741 function (data) { 2742 var stepToDisplay = self.formData.stepToDisplay + 1; 2743 self.formData = data; 2744 2745 updateFieldClientProperties(self); 2746 2747 var displayLoading = displayLoadingStep(self, stepToDisplay, true); 2748 2749 //if returnUrl exist, need to defer the return for the loading transition to complete 2750 let hasReturnUrl = 2751 self.options.returnUrl || 2752 self.formData.form.advancedOptions.confirmationActionURL; 2753 var deferReturn = hasReturnUrl && displayLoading; 2754 2755 //Pass through. We have a completed lead already. 2756 handleCompletion(self, deferReturn); 2757 }, 2758 function (data) { 2759 var params = { 2760 formId: data.form.externalId, 2761 sessionId: data.sessionId, 2762 userSsoId: data.userSsoId, 2763 returnUrl: encodeURIComponent(window.location.href), 2764 }; 2765 window.location = 2766 BBMForms.constants.secureCheckoutUrl + 2767 '?' + 2768 bbmFormsJQ.param(params); 2769 }, 2770 function (errorMessage) { 2771 displayStep(self, self.formData.stepToDisplay); 2772 self.$errorMessage.html(bbmFormsJQ('<p/>').text(errorMessage)).show(); 2773 scrollUpToTop(self); 2774 } 2775 ); 2776 } else {
2777 var fData = $fData.serialize(); 2778 postSerializedForm( 2779 fData, 2780 function (data) { 2781 var stepToDisplay = self.formData.stepToDisplay + 1; 2782 self.formData = data; 2783 2784 updateFieldClientProperties(self); 2785 2786 var displayLoading = displayLoadingStep(self, stepToDisplay, true); 2787 2788 let hasReturnUrl = 2789 self.options.returnUrl || 2790 self.formData.form.advancedOptions.confirmationActionURL; 2791 var deferReturn = hasReturnUrl && displayLoading; 2792 2793 handleCompletion(self, deferReturn); 2794 }, 2795 function (data) { 2796 var params = { 2797 redirectUrl: window.location.href, 2798 userUID: data.userId, 2799 formID: data.form.externalId, 2800 sessionID: data.sessionId, 2801 fieldID: paymentExternalFieldId, 2802 }; 2803 window.location = 2804 BBMForms.constants.secureCheckoutUrl + 2805 '?' + 2806 bbmFormsJQ.param(params); 2807 }, 2808 function (errorMessage) { 2809 displayStep(self, self.formData.stepToDisplay); 2810 self.$errorMessage.html(bbmFormsJQ('<p/>').text(errorMessage)).show(); 2811 scrollUpToTop(self); 2812 } 2813 ); 2814 } 2815 } 2816 2817 function bypassForm(self) { 2818 //Update formData object with user input data 2819 updateFormDataFromInput(self); 2820 2821 //Post this.formData to api endpoint and validate 2822 var $fData = createFormData(self); 2823 2824 if (window.FormData) { 2825 var fData = new FormData($fData[0]); 2826 postBypassFormData( 2827 fData, 2828 function (data) { 2829 self.formData = data; 2830 2831 updateFieldClientProperties(self); 2832 2833 //Store userId in cookie. Update with userId retrieved from previously completed form. 2834 setCookie( 2835 BBMForms.constants.userIdCookieName, 2836 self.formData.userId, 2837 self.options.userIdCookieExpireDays, 2838 self.options.hostName 2839 ); 2840 2841 //Pass through. We have a completed lead already. 2842 handleCompletion(self); 2843 }, 2844 function (errorMessage) { 2845 //Bypass failed. Display bypass again. 2846 displayBypass(self, errorMessage); 2847 scrollUpToTop(self); 2848 }, 2849 function (errorMessage) { 2850 //Bypass failed due to missing form/lead. Display form. 2851 self.display(errorMessage); 2852 scrollUpToTop(self); 2853 }, 2854 function (errorMessage) { 2855 //Failed to post. 2856 displayBypass(self, errorMessage); 2857 scrollUpToTop(self); 2858 } 2859 ); 2860 } else { 2861 var fData = $fData.serialize(); 2862 postBypassSerializedForm( 2863 fData, 2864 function (data) { 2865 self.formData = data; 2866 2867 updateFieldClientProperties(self); 2868 2869 //Pass through. We have a completed lead already. 2870 handleCompletion(self); 2871 }, 2872 function (errorMessage) { 2873 //Bypass failed. Display bypass again. 2874 displayBypass(self, errorMessage); 2875 scrollUpToTop(self); 2876 }, 2877 function (errorMessage) { 2878 //Bypass failed due to missing form/lead. Display form. 2879 self.display(errorMessage); 2880 scrollUpToTop(self); 2881 }, 2882 function (errorMessage) { 2883 //Failed to post. 2884 displayBypass(self, errorMessage); 2885 scrollUpToTop(self); 2886 } 2887 ); 2888 } 2889 } 2890 2891 function backToStep(self, stepToDisplay) { 2892 //Update formData object with user input data 2893 updateFormDataFromInput(self); 2894 2895 //check if step back is empty 2896 // Conditional rules may return display false fields 2897 for (var i = stepToDisplay - 1; i >= 0; i--) { 2898 if ( 2899 self.formData.form.steps[i].filter(o => o.display == true).length > 0 2900 ) { 2901 stepToDisplay = i + 1; 2902 break; 2903 } 2904 } 2905 self.formData.stepToDisplay = stepToDisplay; 2906 2907 //assign a class to indicating its going back to skip loading screen 2908 if (stepToDisplay > 1) self.$loadingIndicatorContainer.addClass('goback'); 2909 2910 //No posting. No validation. Just go back. 2911 displayStep(self, stepToDisplay); 2912 2913 if (self.options.callbackPrevStepLoaded) { 2914 self.options.callbackPrevStepLoaded(stepToDisplay); 2915 } 2916 2917 scrollUpToTop(self); 2918 } 2919 2920 function handleCompletion(self, deferReturn) { 2921 deactivateConfirmExit(); 2922 2923 let leadRecordsCopy = null; 2924 if ( 2925 self.formData.hasOwnProperty('leadRecords') && 2926 typeof self.formData.leadRecords != 'undefined' 2927 ) { 2928 leadRecordsCopy = Object.assign({}, self.formData.leadRecords); 2929 } 2930 let leadRecordsFullCopy = null; 2931 if ( 2932 self.formData.hasOwnProperty('leadRecordsFull') && 2933 typeof self.formData.leadRecordsFull != 'undefined' 2934 ) { 2935 leadRecordsFullCopy = Object.assign({}, self.formData.leadRecordsFull); 2936 } 2937 let formDataCopy = Object.assign({}, self.formData); 2938 //leadrecords are being returned as separate param so remove from formData which is in the same return params set 2939 if (formDataCopy.hasOwnProperty('leadRecords')) 2940 delete formDataCopy.leadRecords; 2941 if (formDataCopy.hasOwnProperty('leadRecordsFull')) 2942 delete formDataCopy.leadRecordsFull; 2943 2944 //window.onSubmitBBMForms 2945 var onSubmitFunc = getOnSubmitFunc(self); 2946 2947 //if deferReturn=true, then skip the return step (this is only for loading transition cases) 2948 let hasReturnUrl = 2949 self.options.returnUrl || 2950 self.formData.form.advancedOptions.confirmationActionURL; 2951 if (hasReturnUrl && !deferReturn) { 2952 if (self.options.callbackPostPassThrough) { 2953 self.options.callbackPostPassThrough( 2954 leadRecordsCopy, 2955 null, 2956 leadRecordsFullCopy, 2957 formDataCopy 2958 ); 2959 } 2960 2961 let returnUrl = self.options.returnUrl 2962 ? self.options.returnUrl 2963 : self.formData.form.advancedOptions.confirmationActionURL; 2964 if (onSubmitFunc) { 2965 try { 2966 onSubmitFunc(leadRecordsCopy, formDataCopy.form, function () { 2967 window.location = returnUrl; 2968 }); 2969 } catch { 2970 window.location = returnUrl; 2971 } 2972 } else { 2973 window.location = returnUrl; 2974 } 2975 } else { 2976 if (self.options.callbackNextStepLoaded) { 2977 self.options.callbackNextStepLoaded('Completion Message'); 2978 } 2979 2980 if (self.$formInstructions) { 2981 self.$formInstructions.empty(); 2982 } 2983 if (self.$progressIndicatorContainer) { 2984 self.$progressIndicatorContainer.empty(); 2985 } 2986 2987 var msgHtml = self.formData.form.advancedOptions.confirmationMessage 2988 ? decodeHtml(self.formData.form.advancedOptions.confirmationMessage) 2989 : bbmFormsJQ('<p/>').text( 2990 BBMForms.constants.confirmationMessageDefault 2991 ); 2992 var msgBlock = bbmFormsJQ('<div/>') 2993 .addClass('col-xs-12 col-sm-12 col-md-12 padding-bottom-30') 2994 .addClass(BBMForms.constants.messageContainerCssClass) 2995 .append(msgHtml); 2996 self.$stepsWrapper.html(msgBlock); 2997 2998 if (self.options.scrollToTopUponCompletion) { 2999 scrollUpToTop(self); 3000 } 3001 3002 if (self.options.callbackPostPassThrough) { 3003 self.options.callbackPostPassThrough( 3004 leadRecordsCopy, 3005 null, 3006 leadRecordsFullCopy, 3007 formDataCopy 3008 ); 3009 } 3010 3011 if (onSubmitFunc) { 3012 onSubmitFunc(leadRecordsCopy, formDataCopy.form); 3013 } 3014 } 3015 } 3016 3017 function handlePreviouslyCompleted(self, leadExist) { 3018 deactivateConfirmExit(); 3019 self.$element.empty(); 3020 //leadExist, in combination with Cookie'd Consent (Revisit Bypass) and force input for payment field to show completed form 3021 var isRevisit = 3022 leadExist ||
3023 (self.formData.leadFormExternalId && 3024 self.formData.leadFormExternalId === self.formData.form.externalId); 3025 3026 var stepType = "Cookie'd Visit Consent"; 3027 var visitType = isRevisit ? 'Revisit' : "Cookie'd Visit"; 3028 3029 //check revisit bypass 3030 if (isRevisit && self.formData.form.cookieConsentRevisitBypass) { 3031 let leadRecordsCopy = null; 3032 if ( 3033 self.formData.hasOwnProperty('leadRecords') && 3034 typeof self.formData.leadRecords != 'undefined' 3035 ) { 3036 leadRecordsCopy = Object.assign({}, self.formData.leadRecords); 3037 } 3038 //let leadRecordsFullCopy = null; 3039 //if (self.formData.hasOwnProperty('leadRecordsFull') && typeof self.formData.leadRecordsFull != "undefined") { 3040 // leadRecordsFullCopy = Object.assign({}, self.formData.leadRecordsFull); 3041 //} 3042 let formDataCopy = Object.assign({}, self.formData); 3043 //leadrecords are being returned as separate param so remove from formData which is in the same return params set 3044 if (formDataCopy.hasOwnProperty('leadRecords')) 3045 delete formDataCopy.leadRecords; 3046 if (formDataCopy.hasOwnProperty('leadRecordsFull')) 3047 delete formDataCopy.leadRecordsFull; 3048 3049 let hasReturnUrl = 3050 self.options.returnUrl || 3051 self.formData.form.advancedOptions.confirmationActionURL; 3052 if (hasReturnUrl) { 3053 stepType = 'Revisit Redirect'; 3054 3055 if (self.options.callbackFormLoaded) { 3056 self.options.callbackFormLoaded(stepType); 3057 } 3058 3059 if (self.options.callbackGetPassThrough) { 3060 self.options.callbackGetPassThrough( 3061 leadRecordsCopy, 3062 visitType, 3063 null, 3064 formDataCopy 3065 ); 3066 } 3067 3068 let returnUrl = self.options.returnUrl 3069 ? self.options.returnUrl 3070 : self.formData.form.advancedOptions.confirmationActionURL; 3071 window.location = returnUrl; 3072 } else { 3073 //Pass through. We have a completed lead already. Show the download button. 3074 stepType = 'Completion Message'; 3075 3076 if (self.options.callbackFormLoaded) { 3077 self.options.callbackFormLoaded(stepType); 3078 } 3079 3080 self.$element.empty(); 3081 var msgHtml = self.formData.form.advancedOptions 3082 .confirmationRevisitMessage 3083 ? decodeHtml( 3084 self.formData.form.advancedOptions.confirmationRevisitMessage 3085 ) 3086 : ''; 3087 if (msgHtml != '') { 3088 var msgBlock = bbmFormsJQ('<div/>') 3089 .addClass('col-xs-12 col-sm-12 col-md-12 padding-bottom-30') 3090 .addClass(BBMForms.constants.messageContainerCssClass) 3091 .append(msgHtml); 3092 self.$element.append(msgBlock); 3093 } 3094 3095 if (self.options.callbackGetPassThrough) { 3096 self.options.callbackGetPassThrough( 3097 leadRecordsCopy, 3098 visitType, 3099 null, 3100 formDataCopy 3101 ); 3102 } 3103 } 3104 } else { 3105 //window.onSubmitBBMForms 3106 var onSubmitFunc = getOnSubmitFunc(self); 3107 3108 if (self.options.callbackFormLoaded) { 3109 self.options.callbackFormLoaded(stepType); 3110 } 3111 3112 //Show cookie'd consent and button below. 3113 if (self.formData.form.cookieConsentText) { 3114 var consentHtml = decodeHtml(self.formData.form.cookieConsentText); 3115 var $consentBlock = bbmFormsJQ('<div/>') 3116 .addClass(BBMForms.constants.disclaimerCssClass) 3117 .addClass(getBootstrapColumnClasses('full')) 3118 .addClass('margin-bottom-10') 3119 .html(consentHtml); 3120 self.$element.append($consentBlock); 3121 } 3122 3123 var actionText = self.formData.form.advancedOptions.confirmationActionText 3124 ? self.formData.form.advancedOptions.confirmationActionText 3125 : BBMForms.constants.confirmationActionButtonTextDefault; 3126 var $buttonsContainer = bbmFormsJQ('<div/>') 3127 .addClass(BBMForms.constants.buttonsContainerCssClass) 3128 .addClass(getBootstrapColumnClasses('full')); 3129 var $downloadField = bbmFormsJQ('<div/>') 3130 .addClass('full') 3131 .addClass(BBMForms.constants.fieldBlockCssClass) 3132 .addClass(getBootstrapColumnClasses('full')); 3133 var nextBtnColor = self.formData.form.advancedOptions.buttonColor 3134 ? self.formData.form.advancedOptions.buttonColor 3135 : BBMForms.constants.submitButtonColorDefault; 3136 3137 var formMetaData = getMetaData(self); 3138 3139 var $button = bbmFormsJQ('<a/>') 3140 .attr('href', 'javascript:void(0)') 3141 .text(actionText) 3142 .attr('style', 'background-color:#'.concat(nextBtnColor, ';')) 3143 .addClass(BBMForms.constants.buttonCssClass) 3144 .addClass(BBMForms.constants.nextButtonCssClass) 3145 .click(function () { 3146 //On Click, we should make a resource request so the lead is generated. 3147 var reqData = { 3148 formId: self.options.formId, 3149 userId: self.userId, 3150 userSsoId: self.userSsoId, 3151 userSsoEmail: self.userSsoEmail, 3152 ip: self.options.userIpAddress, 3153 origin: self.options.origin, 3154 title: self.options.title, 3155 formmetadata: JSON.stringify(formMetaData), 3156 }; 3157 postResource( 3158 reqData, 3159 function (data) { 3160 self.formData = data; 3161 3162 updateFieldClientProperties(self); 3163 3164 let leadRecordsCopy = null; 3165 if ( 3166 self.formData.hasOwnProperty('leadRecords') && 3167 typeof self.formData.leadRecords != 'undefined' 3168 ) { 3169 leadRecordsCopy = Object.assign({}, self.formData.leadRecords); 3170 } 3171 let leadRecordsFullCopy = null; 3172 if ( 3173 self.formData.hasOwnProperty('leadRecordsFull') && 3174 typeof self.formData.leadRecordsFull != 'undefined' 3175 ) { 3176 leadRecordsFullCopy = Object.assign( 3177 {}, 3178 self.formData.leadRecordsFull 3179 ); 3180 } 3181 let formDataCopy = Object.assign({}, self.formData); 3182 //leadrecords are being returned as separate param so remove from formData which is in the same return params set 3183 if (formDataCopy.hasOwnProperty('leadRecords')) 3184 delete formDataCopy.leadRecords; 3185 if (formDataCopy.hasOwnProperty('leadRecordsFull')) 3186 delete formDataCopy.leadRecordsFull; 3187 3188 let hasReturnUrl = 3189 self.options.returnUrl || 3190 self.formData.form.advancedOptions.confirmationActionURL; 3191 if (hasReturnUrl) { 3192 if (self.options.callbackPostPassThrough) { 3193 self.options.callbackPostPassThrough( 3194 leadRecordsCopy, 3195 visitType, 3196 leadRecordsFullCopy, 3197 formDataCopy 3198 ); 3199 } 3200 3201 let returnUrl = self.options.returnUrl 3202 ? self.options.returnUrl 3203 : self.formData.form.advancedOptions.confirmationActionURL; 3204 if (onSubmitFunc) { 3205 try { 3206 onSubmitFunc( 3207 leadRecordsCopy, 3208 formDataCopy.form, 3209 function () { 3210 window.location = returnUrl; 3211 } 3212 ); 3213 } catch { 3214 window.location = returnUrl; 3215 } 3216 } else { 3217 window.location = returnUrl; 3218 } 3219 } else { 3220 //Pass through. We have a completed lead already. Show the download button. 3221 if (self.options.callbackNextStepLoaded) { 3222 self.options.callbackNextStepLoaded('Completion Message'); 3223 } 3224 3225 self.$element.empty(); 3226 var msgHtml = self.formData.form.advancedOptions 3227 .confirmationMessage 3228 ? decodeHtml( 3229 self.formData.form.advancedOptions.confirmationMessage 3230 ) 3231 : bbmFormsJQ('<p/>').text( 3232 BBMForms.constants.confirmationMessageDefault 3233 ); 3234 var msgBlock = bbmFormsJQ('<div/>') 3235 .addClass('col-xs-12 col-sm-12 col-md-12 padding-bottom-30') 3236 .addClass(BBMForms.constants.messageContainerCssClass) 3237 .append(msgHtml); 3238 self.$element.append(msgBlock); 3239 3240 if (self.options.callbackPostPassThrough) { 3241 self.options.callbackPostPassThrough( 3242 leadRecordsCopy, 3243 visitType, 3244 leadRecordsFullCopy, 3245 formDataCopy 3246 ); 3247 } 3248 3249 if (onSubmitFunc) { 3250 onSubmitFunc(leadRecordsCopy, formDataCopy.form); 3251 } 3252 } 3253 }, 3254 function (data) { 3255 self.formData = data; 3256 3257 updateFieldClientProperties(self); 3258 3259 //Incomplete lead. Show form.
3260 self.display(); 3261 }, 3262 function (errorMessage) { 3263 self.$element.html(bbmFormsJQ('<p/>').text(errorMessage)); 3264 } 3265 ); 3266 }); 3267 3268 var $downloadColumn = bbmFormsJQ('<div/>') 3269 .addClass(BBMForms.constants.columnCssClass) 3270 .append($button); 3271 self.$element.append( 3272 $buttonsContainer.append($downloadField.append($downloadColumn)) 3273 ); 3274 } 3275 } 3276 3277 function getOnSubmitFunc(self) { 3278 window.onSubmitBBMForms = 3279 window.onSubmitBBMForms && Array.isArray(window.onSubmitBBMForms) 3280 ? window.onSubmitBBMForms 3281 : []; 3282 var onSubmitFunc = null; 3283 var onSubmitIndex = window.onSubmitBBMForms.findIndex( 3284 item => 3285 item.hasOwnProperty('formId') && 3286 item.formId === self.formData.form.externalId 3287 ); 3288 if (onSubmitIndex !== -1) { 3289 var onSubmitItem = window.onSubmitBBMForms.splice(onSubmitIndex, 1)[0]; //get array item while removing it from array 3290 if ( 3291 onSubmitItem && 3292 onSubmitItem.hasOwnProperty('onSubmit') && 3293 typeof onSubmitItem.onSubmit === 'function' 3294 ) { 3295 onSubmitFunc = onSubmitItem.onSubmit; 3296 } 3297 } 3298 return onSubmitFunc; 3299 } 3300 3301 function scrollUpToTop(self) { 3302 bbmFormsJQ('html, body').animate( 3303 { 3304 scrollTop: self.$element.offset().top, 3305 }, 3306 500 3307 ); 3308 } 3309 3310 function getUrlParameter(name) { 3311 let results = []; 3312 let searchParams = new URLSearchParams(location.search); 3313 for (let key of searchParams.keys()) { 3314 if (key.trim().toLowerCase() == name.trim().toLowerCase()) { 3315 results.push( 3316 decodeURIComponent(searchParams.get(key).replace(/\+/g, ' ').trim()) 3317 ); 3318 } 3319 } 3320 return results; 3321 } 3322 3323 function getUrlParameterByPrefix(prefix) { 3324 let results = []; 3325 let searchParams = new URLSearchParams(location.search); 3326 for (let key of searchParams.keys()) { 3327 if (key.trim().toLowerCase().startsWith(prefix.trim().toLowerCase())) { 3328 results.push(decodeURIComponent(key.trim().replace(/\+/g, ' ').trim())); 3329 } 3330 } 3331 return results; 3332 } 3333 3334 function activateConfirmExit(isInteracted) { 3335 window.onbeforeunload = function (e) { 3336 confirmExit(e, isInteracted); 3337 }; 3338 } 3339 3340 function deactivateConfirmExit() { 3341 window.onbeforeunload = null; 3342 } 3343 3344 function confirmExit(e, isInteracted) { 3345 if (isInteracted) { 3346 e.preventDefault(); 3347 e.returnValue = ''; 3348 } 3349 //return true; 3350 } 3351 3352 function getMetaData(self) { 3353 //Metadata defined on the form from FormsCMS. 3354 self.formData.form.formMetadata = self.formData.form.formMetadata || {}; //Default to empty object if not defined. 3355 3356 //1. 3357 //Create metadata with FormsCMS defined metadata (self.formData.form.formMetadata) as base and append embed script defined metadata (self.options.formMetadata) 3358 //embed script defined metadata overwrite FormsCMS defined metadata if both have same metadata key. 3359 var formMetaData = bbmFormsJQ.extend( 3360 {}, 3361 self.formData.form.formMetadata, 3362 self.options.formMetadata 3363 ); 3364 3365 //2. 3366 //If 3rd party script defined metadata exists this gets appended to the above result being used as base. 3367 //3rd party script will overwrite the above result if same metadata key found. 3368 if ( 3369 typeof window.BBMFormsMetaData !== 'undefined' && 3370 bbmFormsJQ.isArray(window.BBMFormsMetaData) 3371 ) { 3372 bbmFormsJQ.each(window.BBMFormsMetaData, function (index, data) { 3373 if (typeof data === 'object') { 3374 formMetaData = bbmFormsJQ.extend({}, formMetaData, data); 3375 } 3376 }); 3377 } 3378 3379 //3. 3380 //If URL metadata capture is enabled then append metadata from URL querystring. 3381 if (self.options.enableMetadataCapture) { 3382 //Check to see if already defined metadata keys are there in the URL querystring. 3383 //Overwrite existing metadata properties if same key found in URL querystring. 3384 bbmFormsJQ.each(formMetaData, function (key, value) { 3385 var result = getUrlParameter(key); 3386 if (result.length > 0) { 3387 formMetaData[key] = result[0]; 3388 } 3389 }); 3390 3391 //Append metadata from URL querystring from the defined prefixes and specific params. 3392 bbmFormsJQ.each(self.options.metadataParamList, function (i) { 3393 var key = self.options.metadataParamList[i]; 3394 var result = getUrlParameter(key); 3395 if (result.length > 0) { 3396 formMetaData[key] = result[0]; 3397 } 3398 }); 3399 } 3400 3401 return formMetaData; 3402 } 3403 3404 function setMetaData(self) { 3405 var metaData = getMetaData(self); 3406 var metaDataFieldName = 'formMetadata'; 3407 self.$form.find("input[name$='" + metaDataFieldName + "']").remove(); 3408 self.$form.append( 3409 bbmFormsJQ('<input/>', { type: 'hidden', name: metaDataFieldName }).val( 3410 JSON.stringify(metaData) 3411 ) 3412 ); 3413 } 3414 3415 var onArrayPush = function (arr, callback) { 3416 arr.push = function (e) { 3417 Array.prototype.push.call(arr, e); 3418 callback(arr); 3419 }; 3420 }; 3421 3422 BBMForms.prototype.display = function (errMsg) { 3423 if (!this.options.element) { 3424 throw new Error('No element option passed to BBMForms.'); 3425 } 3426 3427 this.element = this.options.element; 3428 this.$element = bbmFormsJQ(this.element); 3429 this.$element.empty(); //clear 3430 3431 //Create container 3432 var $row = bbmFormsJQ('<div/>'); 3433 this.$element.append( 3434 bbmFormsJQ('<div/>') 3435 .addClass(BBMForms.constants.formContainerCssClass) 3436 .append(bbmFormsJQ('<div/>').addClass('container').append($row)) 3437 ); 3438 this.$element = $row; 3439 3440 //Show loading message while get form is in progress... 3441 this.$element.html("<p style='text-align:center;'>Loading...</p>"); 3442 3443 //Add formId to array of displayed forms to be used for blueconic form capture 3444 window.displayedBBMForms = window.displayedBBMForms ?? []; 3445 if (window.displayedBBMForms.indexOf(this.options.formId) === -1) { 3446 window.displayedBBMForms.push(this.options.formId); 3447 } 3448 3449 var reqData = { 3450 formId: this.options.formId, 3451 userId: this.userId, 3452 userSsoId: this.userSsoId, 3453 userSsoEmail: this.userSsoEmail, 3454 getFullLeadRecordsBackOnSuccess: 3455 this.options.getFullLeadRecordsBackOnSuccess, 3456 allowPrefill: this.options.allowPrefill, 3457 ip: this.options.userIpAddress, 3458 origin: this.options.origin, 3459 title: this.options.title, 3460 formmetadata: JSON.stringify(this.options.formMetadata), 3461 }; 3462 var self = this; 3463 getForm( 3464 reqData, 3465 function (data) { 3466 self.formData = data; 3467 3468 updateFieldClientProperties(self); 3469 3470 //Pass through. We have a completed lead already. Show the download button. 3471 handlePreviouslyCompleted(self); 3472 }, 3473 function (data) { 3474 //Incomplete lead. Show form. 3475 3476 self.formData = data; 3477 3478 updateFieldClientProperties(self); 3479 3480 //leadCompleted in query string sent from success payment, to skip form data and show completed form message 3481 var sp = getUrlParameter('leadCompleted'); 3482 if (sp == 'true') { 3483 handlePreviouslyCompleted(self, true); 3484 return; 3485 } 3486 3487 if (self.options.callbackFormLoading) { 3488 self.options.callbackFormLoading(); 3489 } 3490 3491 //Store userId in cookie.
3492 setCookie( 3493 BBMForms.constants.userIdCookieName, 3494 self.formData.userId, 3495 self.options.userIdCookieExpireDays, 3496 self.options.hostName 3497 ); 3498 3499 //Clean out loading message. 3500 self.$element.empty(); 3501 3502 self.userId = self.formData.userId; 3503 self.userSsoId = self.formData.userSsoId; 3504 self.userSsoEmail = self.formData.userSsoEmail; 3505 self.sessionId = self.formData.sessionId; 3506 self.options.enableAutoProgress = self.formData.form.enableAutoProgress; 3507 self.options.enableConfirmExit = self.formData.form.enableConfirmExit; 3508 3509 //Header 3510 if ( 3511 self.formData.form.advancedOptions.headline || 3512 self.formData.form.advancedOptions.description 3513 ) { 3514 var $header = bbmFormsJQ('<header/>').addClass( 3515 'col-md-8 col-lg-9 col-center align-center padding-bottom-30' 3516 ); 3517 3518 if (self.formData.form.advancedOptions.headline) { 3519 //text 3520 var $formHead = bbmFormsJQ('<h2/>').text( 3521 self.formData.form.advancedOptions.headline 3522 ); 3523 $header.append($formHead); 3524 } 3525 3526 if (self.formData.form.advancedOptions.description) { 3527 //wysiwyg 3528 var descHtml = decodeHtml( 3529 self.formData.form.advancedOptions.description 3530 ); 3531 var $formDesc = bbmFormsJQ('<div/>') 3532 .addClass('description') 3533 .html(descHtml); 3534 $header.append($formDesc); 3535 } 3536 3537 self.$element.append( 3538 bbmFormsJQ('<div/>') 3539 .addClass('row justify-content-center') 3540 .append($header) 3541 ); 3542 } 3543 3544 if (self.formData.form.steps) { 3545 //Progress indicator 3546 self.$progressIndicatorContainer = bbmFormsJQ('<div/>').addClass( 3547 'row justify-content-center' 3548 ); 3549 self.$element.append(self.$progressIndicatorContainer); 3550 3551 //Loading indicator 3552 self.$loadingIndicatorContainer = bbmFormsJQ('<div/>').addClass( 3553 'row justify-content-center loading firstload' 3554 ); 3555 self.$element.append(self.$loadingIndicatorContainer); 3556 3557 //Error message container 3558 self.$errorMessage = bbmFormsJQ('<div/>') 3559 .addClass(BBMForms.constants.errorMsgCssClass) 3560 .addClass('padding-bottom-30') 3561 .hide(); 3562 self.$element.append( 3563 bbmFormsJQ('<div/>') 3564 .addClass('row justify-content-center') 3565 .append( 3566 bbmFormsJQ('<div/>') 3567 .addClass('col-xs-12 col-sm-12 col-md-12') 3568 .append(self.$errorMessage) 3569 ) 3570 ); 3571 3572 //Create form element 3573 var formClientId = 3574 BBMForms.constants.formNamePrefix + self.element.id; 3575 self.$form = bbmFormsJQ('<form/>', { 3576 id: formClientId, 3577 name: formClientId, 3578 method: 'post', 3579 enctype: 'multipart/form-data', 3580 }); 3581 self.$element.append( 3582 bbmFormsJQ('<div/>') 3583 .addClass('row justify-content-center') 3584 .append(self.$form) 3585 ); 3586 3587 //Hidden fields 3588 self.$form.append( 3589 bbmFormsJQ('<input/>', { type: 'hidden', name: 'formId' }).val( 3590 self.options.formId 3591 ) 3592 ); 3593 self.$form.append( 3594 bbmFormsJQ('<input/>', { type: 'hidden', name: 'userId' }).val( 3595 self.userId 3596 ) 3597 ); 3598 self.$form.append( 3599 bbmFormsJQ('<input/>', { type: 'hidden', name: 'userSsoId' }).val( 3600 self.userSsoId 3601 ) 3602 ); 3603 self.$form.append( 3604 bbmFormsJQ('<input/>', { 3605 type: 'hidden', 3606 name: 'userSsoEmail', 3607 }).val(self.userSsoEmail) 3608 ); 3609 self.$form.append( 3610 bbmFormsJQ('<input/>', { 3611 type: 'hidden', 3612 name: 'getFullLeadRecordsBackOnSuccess', 3613 }).val(self.options.getFullLeadRecordsBackOnSuccess) 3614 ); 3615 self.$form.append( 3616 bbmFormsJQ('<input/>', { type: 'hidden', name: 'sessionId' }).val( 3617 self.sessionId 3618 ) 3619 ); 3620 self.$form.append( 3621 bbmFormsJQ('<input/>', { type: 'hidden', name: 'ip' }).val( 3622 self.options.userIpAddress 3623 ) 3624 ); 3625 self.$form.append( 3626 bbmFormsJQ('<input/>', { type: 'hidden', name: 'origin' }).val( 3627 self.options.origin 3628 ) 3629 ); 3630 self.$form.append( 3631 bbmFormsJQ('<input/>', { type: 'hidden', name: 'title' }).val( 3632 self.options.title 3633 ) 3634 ); 3635 3636 //Form instructions 3637 self.$formInstructions = bbmFormsJQ('<div/>').addClass( 3638 'col-xs-12 col-sm-12 col-md-12' 3639 ); 3640 self.$form.append(self.$formInstructions); 3641 3642 //Form bypass 3643 if ( 3644 self.formData.form.advancedOptions.formBypass && 3645 self.formData.form.advancedOptions.formBypassLinkText 3646 ) { 3647 self.$formBypass = bbmFormsJQ('<p/>') 3648 .addClass('padding-bottom-10') 3649 .css('text-align', 'right') 3650 .append( 3651 bbmFormsJQ('<a/>') 3652 .attr('href', 'javascript:void(0);') 3653 .text(self.formData.form.advancedOptions.formBypassLinkText) 3654 .click(function (e) { 3655 e.preventDefault(); 3656 displayBypass(self); 3657 }) 3658 ); 3659 self.$formInstructions.append(self.$formBypass); 3660 } 3661 3662 //Required note 3663 self.$formInstructions.append( 3664 bbmFormsJQ('<p/>') 3665 .addClass(BBMForms.constants.instructionTextCssClass) 3666 .addClass('padding-bottom-20') 3667 .css('text-align', 'right') 3668 .text('Required ') 3669 .append( 3670 bbmFormsJQ('<sup/>') 3671 .addClass(BBMForms.constants.requiredTextCssClass) 3672 .text(BBMForms.constants.requiredText) 3673 ) 3674 ); 3675 3676 //Steps wrapper 3677 self.$stepsWrapper = bbmFormsJQ('<div/>').addClass( 3678 BBMForms.constants.stepsWrapperCssClass 3679 ); 3680 self.$form.append(self.$stepsWrapper); 3681 3682 //Display step 3683 displayStep(self, self.formData.stepToDisplay, errMsg); 3684 3685 if (self.options.callbackFormLoaded) { 3686 self.options.callbackFormLoaded(self.formData.stepToDisplay); 3687 } 3688 } 3689 3690 if (self.options.enableConfirmExit) { 3691 var form = document.getElementById( 3692 'bbmforms_' + data.form.externalId 3693 ); 3694 //event listener added to very end. Earlier placements may result in form variable being undefined as the form may not yet be present on DOM 3695 form.addEventListener('change', function () { 3696 //when event listen detects input value changes on form, pass interacted as true. 3697 activateConfirmExit(true); 3698 }); 3699 3700 //when form first gets created, pass in false because it has yet to be interacted with. 3701 activateConfirmExit(false); 3702 } 3703 }, 3704 function (errorMessage) { 3705 self.$element.html(bbmFormsJQ('<p/>').text(errorMessage)); 3706 } 3707 ); 3708 }; 3709 3710 BBMForms.prototype.redirect = function () { 3711 if (!this.options.formUrl) { 3712 throw new Error('No formUrl option passed to BBMForms.'); 3713 } 3714 3715 var reqData = { 3716 formId: this.options.formId, 3717 userId: this.userId, 3718 userSsoId: this.userSsoId, 3719 userSsoEmail: this.userSsoEmail, 3720 ip: this.options.userIpAddress, 3721 origin: this.options.origin, 3722 title: this.options.title, 3723 formmetadata: JSON.stringify(this.options.formMetadata), 3724 }; 3725 var self = this; 3726 //Should do post-resource since the redirect function is for getting access to the resource where it should create a lead. 3727 postResource( 3728 reqData, 3729 function (data) { 3730 self.formData = data; 3731 3732 updateFieldClientProperties(self); 3733 3734 let leadRecordsCopy = null; 3735 if ( 3736 self.formData.hasOwnProperty('leadRecords') && 3737 typeof self.formData.leadRecords != 'undefined' 3738 ) { 3739 leadRecordsCopy = Object.assign({}, self.formData.leadRecords); 3740 } 3741 //let leadRecordsFullCopy = null; 3742 //if (self.formData.hasOwnProperty('leadRecordsFull') && typeof self.formData.leadRecordsFull != "undefined") { 3743 // leadRecordsFullCopy = Object.assign({}, self.formData.leadRecordsFull); 3744 //} 3745 let formDataCopy = Object.assign({}, self.formData); 3746 //leadrecords are being returned as separate param so remove from formData which is in the same return params set 3747 if (formDataCopy.hasOwnProperty('leadRecords')) 3748 delete formDataCopy.leadRecords; 3749 if (formDataCopy.hasOwnProperty('leadRecordsFull')) 3750 delete formDataCopy.leadRecordsFull; 3751 3752 //Pass through. We have a completed lead already. 3753 if (self.options.callbackGetPassThrough) { 3754 self.options.callbackGetPassThrough( 3755 leadRecordsCopy, 3756 null, 3757 null, 3758 formDataCopy 3759 ); 3760 } 3761 window.location = self.options.returnUrl; 3762 }, 3763 function (data) { 3764 //Incomplete lead. Back to the form page. 3765 window.location = self.options.formUrl; 3766 }, 3767 function (errorMessage) { 3768 throw new Error(errorMessage); 3769 } 3770 ); 3771 }; 3772 3773 BBMForms.constants = { 3774 //apiPath: "http://localhost:54217/api", 3775 apiPath: 'https://leaddata.bobitstudios.com/v2/api', 3776 userIdCookieName: 'BBMForms_UserID', //keep this name as it's shared name on other sites 3777 formNamePrefix: 'bbmforms_form_', 3778 fieldIdPrefix: 'bbmforms_field_', 3779 otherFieldIdSuffix: '_other', 3780 errorMsgCssClass: 'errorMsg', 3781 errorFieldCssClass: 'error', 3782 instructionTextCssClass: 'instructionText', 3783 requiredTextCssClass: 'required', 3784 requiredText: '*',
3785 optionalTextCssClass: 'optional', 3786 optionalText: '(Optional)', 3787 columnCssClass: 'column', 3788 otherText: 'Other', 3789 otherExplanationText: 'if other, please specify:', 3790 stepText: 'Step', 3791 progressIndicatorCssClass: 'progressIndicator', 3792 progressCurrentStepCssClass: 'current', 3793 progressBarContainerCssClass: 'progress', 3794 progressBarCssClass: 'bar', 3795 stepsWrapperCssClass: 'steps_wrapper', 3796 stepContainerCssClass: 'step', 3797 textareaDefaultRows: 6, 3798 buttonsContainerCssClass: 'buttons', 3799 previousStepButtonText: 'Back', 3800 nextStepButtonText: 'Continue', 3801 submitButtonTextDefault: 'Submit', 3802 confirmationActionButtonTextDefault: 'Download', 3803 submitButtonColorDefault: '3C549E', 3804 confirmationMessageDefault: 'Thank you.', 3805 previousButtonCssClass: 'btn-secondary', 3806 buttonCssClass: 'button', 3807 nextButtonCssClass: 'btn-primary', 3808 submitButtonCssClass: 'btn-primary', 3809 formContainerCssClass: 'bbmform', 3810 fieldBlockCssClass: 'field', 3811 otherContainerCssClass: 'other-info', 3812 otherFieldSize: 'medium', 3813 fileButtonCssClass: 'file-button', 3814 fileNameCssClass: 'file-name', 3815 fileButtonText: 'Browse', 3816 disclaimerCssClass: 'disclaimer', 3817 consentCheckboxNameSuffix: '_consent', 3818 multipleCheckboxNameSuffix: '_checkbox', 3819 multipleRadioButtonNameSuffix: '_radiobutton', 3820 imageLabelCssClass: 'image-label', 3821 messageContainerCssClass: 'message-container', 3822 loadingImagePath: '//staticfiles.bobitstudios.com/content/loading.gif', 3823 detailedCheckboxCssClass: 'detail-checkbox', 3824 detailedDescriptionCssClass: 'detail-text', 3825 detailedImageCssClass: 'detail-image', 3826 detailedLabelCssClass: 'detail-label', 3827 3828 paymentCartTotalCssClass: 'total', 3829 paymentCartMobileTotalCssClass: 'cart-num', 3830 paymentCartMobileTotalText: 'Added', 3831 paymentCartTotalPriceCssClass: 'total-pricing', 3832 paymentCartHeader: 'Cart Summary', 3833 paymentCheckoutBtnText: 'CHECK OUT', 3834 paymentCheckoutBtnDisableText: 'SELECT ONE', 3835 secureCheckoutUrl: 'https://bobitpayments.com', 3836 //secureCheckoutUrl: "http://localhost:64791" 3837 }; 3838 3839 BBMForms.defaultOptions = { 3840 formId: 0, 3841 userIdCookieExpireDays: 60, 3842 hostName: '', 3843 returnUrl: '', 3844 formUrl: '', 3845 userIpAddress: '', 3846 origin: window.location.href, //Default to current url 3847 title: '', //Default to current page title when bbmform gets initialized 3848 callbackGetPassThrough: null, 3849 callbackPostPassThrough: null, 3850 callbackFormLoading: null, 3851 callbackFormLoaded: null, 3852 callbackNextStepLoaded: null, 3853 callbackPrevStepLoaded: null, 3854 callbackBypassStepLoaded: null, 3855 callbackPostError: null, 3856 scrollToTopUponCompletion: true, 3857 enableAutoProgress: false, //this will always be overrided by what the form returns 3858 buttonColor: '3c549d', 3859 $nextBtn: null, 3860 enableConfirmExit: false, 3861 formMetadata: {}, 3862 userMetadata: {}, 3863 enableMetadataCapture: true, //Enable/disable metadata capture from url by setting false 3864 metadataParamList: [], //pull specific parameters from the url that may not be included in the metadata: default is empty 3865 metadataPrefixList: ['utm_'], //default pulls all parameters that contain "utm_" 3866 loadingTime: 3000, 3867 costLabel: '$', 3868 getFullLeadRecordsBackOnSuccess: false, 3869 allowPrefill: false, 3870 }; 3871 3872 BBMForms.conditionalDisplayChangeEvents = []; 3873 3874 BBMForms.run = function () { 3875 var scriptJQuery = document.createElement('script'); 3876 scriptJQuery.type = 'text/javascript'; 3877 scriptJQuery.src = 3878 '//ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js'; 3879 scriptJQuery.onload = function () { 3880 window.bbmFormsJQ = jQuery.noConflict(true); 3881 if (typeof window.onLoadBBMForms !== 'undefined') { 3882 if (bbmFormsJQ.isArray(window.onLoadBBMForms)) { 3883 var onLoadFunc; 3884 onArrayPush(window.onLoadBBMForms, function () { 3885 while ( 3886 (onLoadFunc = window.onLoadBBMForms.pop()) !== null && 3887 typeof onLoadFunc === 'function' 3888 ) { 3889 onLoadFunc(); 3890 } 3891 }); 3892 while ( 3893 (onLoadFunc = window.onLoadBBMForms.pop()) !== null && 3894 typeof onLoadFunc === 'function' 3895 ) { 3896 onLoadFunc(); 3897 } 3898 } else if (typeof window.onLoadBBMForms === 'function') { 3899 window.onLoadBBMForms(); 3900 } 3901 } 3902 }; 3903 document.head.appendChild(scriptJQuery); 3904 }; 3905 3906 BBMForms.getQueryStringValue = function (name) { 3907 var results = getUrlParameter(name); 3908 if (results.length > 0) return results[0]; 3909 3910 return ''; 3911 }; 3912 3913 window.BBMForms = BBMForms; 3914 3915 BBMForms.run(); 3916})();
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.