1//IMPORTANT 2//Read Before Editing 3//******************* 4/* 5This file powers core UI functionality that is shared for all web applications. 6No Framework dependencies (e.g. JQuery) should be referenced in this file to ensure the application works without third-party UI dependencies. 7This file is ALWAYS included and contains APP-SPECIFIC reusable functions only. 8 9NOTE: the difference between this and [app name].lib.js is that this is a library contains functionality that is shared for all web apps 10NOTE: the difference between this and app.ui.js is that this is a library file for reusable functions 11 12*/ 13//******************* 14 15//framwork UI helpers 16//NOTICE: NO JQUERY or other third-party dependencies 17 18'use strict'; 19 20/*App Core Enums*/ 21 22const WALLET_TYPES = { 23 NONE: 0, 24 GOOGLE_PAY: 1, 25 APPLE_PAY: 2 26} 27 28const THIRD_PARTY_PAYMENT_PLATFORMS = { 29 None: 0, 30 PayPal: 1, 31 GooglePay: 2, 32 ApplePay: 3, 33 DAFPay: 4 34}; 35 36const FORM_VERSIONS = { 37 LEGACY: 1, 38 CURRENT: 2 39}; 40 41const FORM_RENDER_VIEWS = { 42 Unknown: 0, 43 LandingPage: 1, 44 ConfirmationPage: 2, 45 ErrorPage: 3, 46 SuccessPage: 4 47}; 48 49const FORM_TYPES = { 50 None: 0, 51 Donation: 1, 52 Event_Registration: 2, 53 Volunteer: 3, 54 Enrollment: 4, 55 Contact: 5, 56 Job_Application: 6, 57 Other: 7, 58 Auction: 8, 59 Grassroots_Event_Application: 9, 60 Polls_Surveys: 570, 61 Advocacy: 575, 62 Acquisition: 578, 63 Sustainer_Activation: 603, 64 Membership_Activation: 615, 65 Pledge: 617, 66 Case_Intake: 630, 67 Membership: 665, 68 Volunteer_Time_Entry: 719, 69 Solicitor_Notes: 729 70}; 71 72const FORM_SECTION_TYPES = { 73 Custom: 0, 74 Contact_Info: 1, 75 Donation: 2, 76 Event_Application: 7, 77 Event_Ticket: 6, 78 Payment: 4, 79 Payment_Info: 11, 80 Security: 5, 81 Tribute: 3, 82 Volunteer_Time: 12, 83 Free_Gift: 13 84}; 85 86const FORM_SYSTEM_FIELD_RENDER_TYPES = { 87 Undefined: 0, 88 Hidden: 5, 89 DropDown: 2, 90 RadioButton: 3, 91 RadioButtonImages: 4, 92 TextBox: 6, 93 CheckBox: 1 94}; 95 96const IN_KIND_ASSET_CLASSES = { 97 NONE: 0, 98 PROPERTY: 1, 99 STOCKS: 2, 100 AUTOMOBILES: 3, 101 OTHER: 4, 102 FOOD: 5 103}; 104 105const TRIBUTE_TYPES = { 106 HONOR: 1, 107 MEMORY: 2, 108 BEHALF: 3 109}; 110 111const OpportunityStates = { 112 Unknown: 0, 113 InProgress: 1, 114 Successful: 2, 115 Failed: 3 116}; 117 118/*App Core Utility Functions*/ 119 120function getPageHostName() { 121 122 // CORS iFrame use case 123 if (window.location.ancestorOrigins && window.location.ancestorOrigins.length > 0) { 124 try { 125 return new URL(window.location.ancestorOrigins[0]).hostname; 126 } 127 catch (err) {} 128 } 129 130 // same-origin case 131 try { 132 if (window.parent && window.parent.location && window.parent.location.hostname) 133 return window.parent.location.hostname; 134 } 135 catch (err) {} 136 137 return ''; 138} 139 140function initializePublicAppFormExpirationDateTextBoxes() { 141 142 const allFields = document.querySelectorAll('.ce-publicAppFormContainers .ce-formFieldSets .ce-formFieldRows input[type="text"].ce-creditCardExpirationDateTextBoxes'); 143 144 for (let x = 0;x < allFields.length;x++) { 145 146 const domElement = allFields[x]; 147 148 if (isBlank(domElement.tryGetAttributeValue('placeholder'))) 149 domElement.setAttribute('placeholder', 'MM/YY'); 150 151 let currentDataValidationAttrVal = domElement.tryGetAttributeValue('data-validation'); 152 153 if (currentDataValidationAttrVal == null) 154 currentDataValidationAttrVal = ''; 155 156 if (isBlank(currentDataValidationAttrVal)) 157 currentDataValidationAttrVal = 'required,creditcardexpirationdate'; 158 else if (!currentDataValidationAttrVal.containsValue('creditcardexpirationdate')) { 159 creditcardexpirationdate = creditcardexpirationdate.delimitedListAdd('required'); 160 creditcardexpirationdate = creditcardexpirationdate.delimitedListAdd('creditcardexpirationdate'); 161 } 162 163 domElement.setAttribute('data-validation', currentDataValidationAttrVal); 164 165 domElement.addEventListener('keyup', function (e) {handleMMYYCreditCardExpirationInputChange(domElement, e);}); 166 167 } 168 169} 170 171function toggleFieldLabelVisiblityBasedOnValue(fld) { 172 173 if (fld != null) { 174 175 //console.log('toggle label visiblity for field: ' + fld.id); 176 177 const fldLbl = getLabelForField(fld); 178 if (fldLbl != null) { 179 180 if (fldLbl.hasClassName('ce-alwaysVisibleLabels')) 181 fldLbl.unGhost(); 182 else if (isBlank(fld.getValue())) 183 fldLbl.ghost(); 184 else 185 fldLbl.unGhost(); 186 187 } 188 189 } 190 191} 192 193function initializePublicAppFormDynamicLabel(domElement, fldLbl) { 194 195 if (domElement != null && fldLbl != null && !fldLbl.hasClassName('ce-formFieldNonDynamicLabels')) { 196 197 const fsElem = domElement.firstParentTagByType('fieldset'); 198 if (fsElem != null) 199 fsElem.addClassName('ce-hasDynamicLabels'); 200 201 domElement.parentElement.addClassName('ce-dynamicLabelContainers'); 202 203 domElement.addValueChangeListener(function (elem) {toggleFieldLabelVisiblityBasedOnValue(elem);}); 204 205 toggleFieldLabelVisiblityBasedOnValue(domElement); 206 207 //set placeholder from label if not set 208 const lblTxt = getFieldLabelText(fldLbl); 209 210 if (!isBlank(lblTxt) && isBlank(domElement.tryGetAttributeValue('placeholder'))) 211 domElement.setAttribute('placeholder', 'Enter ' + lblTxt); 212 213 } 214 215} 216 217function initializePublicAppFormDynamicLabels() { 218 219 const allFields = document.querySelectorAll('.ce-publicAppFormContainers:not(ce-noDynamicLabels) .ce-formFieldSets:not
219(ce-noDynamicLabels) .ce-formFieldRows:not(ce-noDynamicLabels) input[type="text"],.ce-publicAppFormContainers:not(ce-noDynamicLabels) .ce-formFieldSets:not(ce-noDynamicLabels) .ce-formFieldRows:not(ce-noDynamicLabels) input[type="password"],.ce-publicAppFormContainers:not(ce-noDynamicLabels) .ce-formFieldSets:not(ce-noDynamicLabels) .ce-formFieldRows:not(ce-noDynamicLabels) select'); 220 221 for (let x = 0;x < allFields.length;x++) { 222 223 const domElement = allFields[x]; 224 225 const fldLbl = getLabelForField(domElement); 226 227 initializePublicAppFormDynamicLabel(domElement, fldLbl); 228 229 } 230 231} 232 233function toggleDependentButtonBasedOnFieldValues(depBtnElem) { 234 235 if (depBtnElem != null) { 236 237 const csvFieldDeps = depBtnElem.tryGetCsvAttribute('data-dependentFormFieldDomIdsCsv'); 238 if (csvFieldDeps.length > 0) 239 toggleButtonVisiblityBasedOnDependentFieldValues(document.getElementById(csvFieldDeps[0])); 240 241 } 242 243} 244 245function toggleButtonVisiblityBasedOnDependentFieldValues(depFldElem) { 246 247 if (depFldElem != null) { 248 249 const fldButtonDomIdsCsv = depFldElem.tryGetCsvAttribute('data-dependentButtonDomIdsCsv'); 250 251 //console.log('toggle dep button for field: ' + depFldElem.id + '(' + fldButtonDomIdsCsv.length + ')'); 252 253 for (let x = 0;x < fldButtonDomIdsCsv.length;x++) { 254 255 const dependentBtnElem = document.getElementById(fldButtonDomIdsCsv[x]); 256 257 //console.log('get button: ' + fldButtonDomIdsCsv[x] + '(' + dependentBtnElem + ')'); 258 259 if (dependentBtnElem != null) { 260 261 let areAnyFieldsUnpopulated = false; 262 263 const btnDepFldIdsCsv = dependentBtnElem.tryGetCsvAttribute('data-dependentFormFieldDomIdsCsv'); 264 265 for (let y = 0;y < btnDepFldIdsCsv.length;y++) { 266 267 const inputFldElem = document.getElementById(btnDepFldIdsCsv[y]); 268 269 if (inputFldElem != null && isBlank(inputFldElem.getValue())) { 270 areAnyFieldsUnpopulated = true; 271 break; 272 } 273 274 } 275 276 if (areAnyFieldsUnpopulated) 277 dependentBtnElem.vanish(); 278 else 279 dependentBtnElem.show(); 280 281 } 282 283 } 284 285 } 286 287} 288 289function initializePublicAppFormFieldDependentButtons() { 290 291 //wire up depdendent buttons 292 const dependentButtons = document.querySelectorAll('button[data-dependentFormFieldDomIdsCsv],input[data-dependentFormFieldDomIdsCsv]'); 293 294 //console.log('dependentButtons.length: ' + dependentButtons.length); 295 296 for (let x = 0;x < dependentButtons.length;x++) { 297 298 const btnDomId = dependentButtons[x].id; 299
300 //console.log('initialize dependent button: ' + btnDomId); 301 302 if (!isBlank(btnDomId)) { 303 304 const depFldIds = dependentButtons[x].tryGetCsvAttribute('data-dependentFormFieldDomIdsCsv'); 305 306 let lastDependentFieldElem = null; 307 308 for (let y = 0;y < depFldIds.length;y++) { 309 310 //console.log('initialize dependent field id: ' + depFldIds[y]); 311 312 const lastDependentFieldElem = document.getElementById(depFldIds[y]); 313 if (lastDependentFieldElem != null) { 314 315 let curCsv = lastDependentFieldElem.tryGetAttributeValue('data-dependentButtonDomIdsCsv'); 316 317 if (curCsv == null) 318 curCsv = ''; 319 320 curCsv = curCsv.listAdd(btnDomId); 321 322 lastDependentFieldElem.setAttribute('data-dependentButtonDomIdsCsv', curCsv); 323 324 lastDependentFieldElem.addEventListener('input', function () {toggleButtonVisiblityBasedOnDependentFieldValues(this);}); 325 326 } 327 328 if (lastDependentFieldElem != null) 329 toggleButtonVisiblityBasedOnDependentFieldValues(lastDependentFieldElem); 330 331 } 332 333 } 334 else 335 console.log('unable to initialize dependent button (dom id required)'); 336 337 } 338 339} 340 341function initializePublicAppFormValidatedFields() { 342 343 const allFields = document.querySelectorAll('.ce-publicAppFormContainers .ce-formFieldSets .ce-formFieldRows input[type="text"],.ce-publicAppFormContainers .ce-formFieldSets .ce-formFieldRows input[type="checkbox"],.ce-publicAppFormContainers .ce-formFieldSets .ce-formFieldRows input[type="password"],.ce-publicAppFormContainers .ce-formFieldSets .ce-formFieldRows textarea,.ce-publicAppFormContainers .ce-formFieldSets .ce-formFieldRows select'); //,span.checkbox-set,div.searchableDropDownListWithListBuilderContainers 344 345 for (let x = 0;x < allFields.length;x++) { 346 347 const domElement = allFields[x]; 348 349 if (!isBlank(domElement.getAttribute('data-validation'))) { 350
351 const formFieldType = domElement.getInputFieldType(); 352 if (formFieldType != FormInputTypes.Unknown) { 353 354 //revalidate if there's a change 355 switch (formFieldType) { 356 case FormInputTypes.TextBox: 357 case FormInputTypes.Password: 358 case FormInputTypes.TextArea: 359 domElement.addEventListener('keyup', function () {this.validate();}); 360 break; 361 case FormInputTypes.CheckBox: 362 domElement.addEventListener('click', function () {this.validate();}); 363 break; 364 case FormInputTypes.DropDownList: 365 domElement.addEventListener('change', function () {this.validate();}); 366 break; 367 } 368 369 } 370 371 } 372 373 } 374 375} 376 377function getFormItemQuantityInputValue(itemQuantityInputElem) { 378 379 let retVal = 0; 380 381 if (itemQuantityInputElem != null) { 382 383 if (itemQuantityInputElem.tagName.toLowerCase() == 'input' && itemQuantityInputElem.getAttribute('type') == 'checkbox') { 384 385 if (itemQuantityInputElem.checked) 386 retVal = 1; 387 388 } 389 else if (!isBlank(itemQuantityInputElem.getValue())) 390 retVal = itemQuantityInputElem.getValue().toString().toIntZeroIfInvalid(); 391 392 } 393 394 return retVal; 395 396} 397 398function getFormTicketQuantityInputValue(ticketQuantityInputElem) { 399 400 return getFormItemQuantityInputValue(ticketQuantityInputElem); 401 402} 403 404function getFormTicketQuantityInputMaxValue(ticketQuantityInputElem) { 405 406 let retVal = 0; 407 408 if (ticketQuantityInputElem != null) { 409 410 let maxValRaw = ''; 411 412 if (ticketQuantityInputElem.tagName.toLowerCase() == 'input' && ticketQuantityInputElem.getAttribute('type') == 'checkbox') 413 maxValRaw = ticketQuantityInputElem.parentNode.tryGetAttributeValue('data-maxvalue'); 414 else 415 maxValRaw = ticketQuantityInputElem.tryGetAttributeValue('data-maxvalue'); 416 417 if (!isBlank(maxValRaw)) 418 retVal = maxValRaw.toString().toIntZeroIfInvalid(); 419 420 } 421 422 return retVal; 423 424} 425 426function getFormTicketQuantityInputTicketId(ticketQuantityInputElem) { 427 428 let retVal = 0; 429 430 if (ticketQuantityInputElem != null) { 431 432 let maxValRaw = ''; 433 434 if (ticketQuantityInputElem.tagName.toLowerCase() == 'input' && ticketQuantityInputElem.getAttribute('type') == 'checkbox') 435 maxValRaw = ticketQuantityInputElem.parentNode.tryGetAttributeValue('data-ticket-id'); 436 else 437 maxValRaw = ticketQuantityInputElem.tryGetAttributeValue('data-ticket-id'); 438 439 if (!isBlank(maxValRaw)) 440 retVal = maxValRaw.toString().toIntZeroIfInvalid(); 441 442 } 443 444 return retVal; 445 446} 447 448function roundEndUserSuggestedDonationAmount(amt) { 449 450 let retVal = 0.00; 451 452 if (!isBlank(amt) && amt.toString().toDecimalZeroIfInvalid() > 0) { 453 454 let decVal = amt.toString().toDecimalZeroIfInvalid(); 455 456 retVal = (Math.round(decVal / 5) * 5); 457 458 ////hard rule - don't ask for more than $500 on a donation form 459 //if (decVal >= 1000) 460 // retVal = Math.round(decVal / 100) * 100; //retVal = 900; 461 //else if (decVal > 100) 462 // retVal = Math.round(decVal / 50) * 50; 463 //else if (decVal > 10) 464 // retVal = Math.ceil(decVal / 10) * 10; //deliberately roundind up 465 //else 466 // retVal = Math.ceil(decVal / 5) * 5; //deliberately roundind up 467 468 } 469 470 if (retVal < 5) 471 retVal = 5; 472 473 return retVal; 474 475} 476 477function getEndUserSuggestedDonationIncrementFromAmount(amt) { 478 479 let retVal = 0.00; 480 481 if (!isBlank(amt) && amt.toString().toDecimalZeroIfInvalid() > 0) { 482 483 let decVal = amt.toString().toDecimalZeroIfInvalid(); 484 485 //hard rule - don't ask for more than $500 on a donation form 486 if (decVal > 500) 487 retVal = 100; 488 else if (decVal > 100) 489 retVal = 50; 490 else if (decVal > 10) 491 retVal = 10; 492 else 493 retVal = 5; 494 495 } 496 497 return retVal; 498 499} 500 501function getEndUserSuggestionAskAmountsFromMinimum(amt, numberOfOptions, seedMultiplier) { 502 503 let retVal = []; 504 505 if (numberOfOptions <= 0) 506 numberOfOptions = 5; 507 508 let tmpAmt = amt; 509 510 if (seedMultiplier > 0 && !isBlank(amt) && amt.toString().toDecimalZeroIfInvalid() > 0) 511 tmpAmt = (tmpAmt * seedMultiplier); 512 513 let decVal = roundEndUserSuggestedDonationAmount(tmpAmt); 514 515 if (decVal > 0) { 516 517 let incVal = getEndUserSuggestedDonationIncrementFromAmount(decVal); 518 519 retVal.push(decVal); 520 521 if (numberOfOptions > 1) 522 for (let x = 0;x < (numberOfOptions - 1);x++) { 523 524 decVal = decVal + incVal; 525 526 retVal.push(decVal); 527 528 } 529 530 } 531 532 return retVal; 533 534} 535 536function getEndUserSuggestionAskAmountsFromMaximum(amt, numberOfOptions, seedMultiplier) { 537 538 let retVal = []; 539 540 if (numberOfOptions <= 0) 541 numberOfOptions = 5; 542 543 let tmpAmt = amt; 544 545 if (seedMultiplier > 0 && !isBlank(amt) && amt.toString().toDecimalZeroIfInvalid() > 0) 546 tmpAmt = (tmpAmt * seedMultiplier); 547 548 let decVal = roundEndUserSuggestedDonationAmount(tmpAmt); 549 550 if (decVal > 0) { 551 552 let incVal = getEndUserSuggestedDonationIncrementFromAmount(decVal); // * numberOfOptions 553 554 if ((decVal - (incVal * numberOfOptions)) <= 0) 555 decVal = ((incVal * numberOfOptions)); // + 5); 556 557 retVal.push(decVal); 558 559 if (numberOfOptions > 1) 560 for (let x = 0;x < (numberOfOptions - 1);x++) { 561 562 decVal = decVal - incVal; 563 564 retVal.push(decVal); 565 566 } 567 568 retVal = retVal.reverse(); 569 570 } 571 572 return retVal; 573 574} 575 576function ValidatePromoCode(promoCode, paymentAmount, selectedTickets, form_id, successCallBack, errorCallBack) { 577 578 const params = { 579 "ajaxParams": { 580 "PromoCode": promoCode, 581 "PaymentAmount": paymentAmount, 582 "Form_Id": form_id, 583 "SelectedTickets": selectedTickets 584 } 585 }; 586 587 App.PostJsonAsync('/api/PublicWebAppUIAjaxEndpoint/ValidatePromoCode', 588 JSON.stringify(params), 589 function (result) { 590 591 //console.log('validate promo code: ' + result); 592 593 if (typeof successCallBack == 'function') 594 successCallBack(result); 595 596 }, 597 function (error) { 598 599 //console.log('error: ' + error); 600 601 if (typeof errorCallBack == 'function') 602 errorCallBack(error); 603 604 }); 605 606} 607 608function LookupPostalCodeLocationInfo(postalCode, successCallBack, errorCallBack) { 609 610 const params = { 611 "postalCode": postalCode 612 }; 613 614 App.PostJsonAsync('/api/PublicWebAppUIAjaxEndpoint/GetPostalCodeLocationInfo', 615 JSON.stringify(params), 616 function (result) { 617 618 //console.log('postal code: ' + result); 619 620 if (typeof successCallBack == 'function') 621 successCallBack(result); 622 623 }, 624 function (error) { 625 626 //console.log('error: ' + error); 627 628 if (typeof errorCallBack == 'function') 629 errorCallBack(error); 630 631 }); 632 633} 634 635 636/*App Core Helper Apis*/ 637 638function getVerticalScollingPanelApi(scrollElemId, scrollEntryCssSelector, minimumNumberOfEntries, scrollStartDelayMilliseconds) { 639 640 const thisApi = {}; 641 642 thisApi.ScrollElemId = scrollElemId; 643 644 thisApi.ScrollEntryCssSelector = scrollEntryCssSelector; 645 646 thisApi.ScrollStartDelayMilliseconds = scrollStartDelayMilliseconds; 647 thisApi.MinimumNumberOfEntries = minimumNumberOfEntries; 648 649 const _logger = getConsoleLogApi({ "LogSourceName": 'CE Vertical Scrolling Panel Api' });
650 651 thisApi.RegisterInitializedCallBack = function (callBackFunction) { 652 653 CallBackManager.add('verticalScollingPanelApiInitializedCallBack', callBackFunction, null); 654 655 }; 656 657 thisApi.FireInitializedEvent = function () { 658 659 CallBackManager.fire('verticalScollingPanelApiInitializedCallBack', {}, null); 660 661 }; 662 663 //Initialize 664 thisApi.Initialize = function () { 665 666 _logger.Log('initialize started'); 667 668 if (!isBlank(thisApi.ScrollElemId) && document.getElementById(thisApi.ScrollElemId) != null) { 669 670 //add auto-scroll class to parent 671 document.getElementById(thisApi.ScrollElemId).parentElement.addClassName('autoScrollingWidgetBoxes'); 672 673 //try to set to first child if possible 674 if (isBlank(thisApi.ScrollEntryCssSelector)) { 675 676 //_logger.Log('verticalScollingPanelApi ScrollElemId id ' + document.getElementById(thisApi.ScrollElemId).id); 677 //_logger.Log('verticalScollingPanelApi first child tag ' + document.getElementById(thisApi.ScrollElemId).firstElementChild .TagName); 678 679 if (document.getElementById(thisApi.ScrollElemId).firstElementChild != null && !isBlank(document.getElementById(thisApi.ScrollElemId).firstElementChild.tagName)) { 680 681 let childTgName = document.getElementById(thisApi.ScrollElemId).firstElementChild.tagName; 682 683 _logger.Log('verticalScollingPanelApi auto-configured scroll entry selector to first child tag ' + childTgName + ' (user parameter to specify entry tag selector)'); 684 685 thisApi.ScrollEntryCssSelector = childTgName; 686 687 //special handling case for tables, where there may be a tbody tag 688 if (childTgName.toLowerCase() == 'tbody' && document.getElementById(thisApi.ScrollElemId).firstElementChild.firstElementChild != null && !isBlank(document.getElementById(thisApi.ScrollElemId).firstElementChild.firstElementChild.tagName)) { 689 690 _logger.Log('verticalScollingPanelApi detected tbody child and found better aut-configure scroll entry selector ' + document.getElementById(thisApi.ScrollElemId).firstElementChild.firstElementChild.tagName + ' (user parameter to specify entry tag selector)'); 691 692 thisApi.ScrollEntryCssSelector = document.getElementById(thisApi.ScrollElemId).firstElementChild.firstElementChild.tagName; 693 694 } 695 696 } 697 698 } 699 700 if (!isBlank(thisApi.ScrollEntryCssSelector)) { 701 702 initializeVerticalScrollingElement(document.getElementById(thisApi.ScrollElemId), thisApi.ScrollEntryCssSelector, thisApi.MinimumNumberOfEntries, thisApi.ScrollStartDelayMilliseconds); 703 704 thisApi.FireInitializedEvent(); 705 706 } 707 else 708 _logger.Log('unable to initialize verticalScollingPanelApi becuase scroll entry CSS selector is missing'); 709 710 } 711 else 712 _logger.Log('unable to initialize verticalScollingPanelApi becuase scroll element id ' + thisApi.ScrollElemId + ' is invalid'); 713 714 }; 715 716 return thisApi; 717 718} 719 720function getListPopBoxPanelApi(containerElemId, listEntriesArray, listDataAjaxUrl, popTimerSeconds, addClassNames) { 721 722 const thisApi = {}; 723 724 thisApi.ContainerElemId = containerElemId; 725 726 thisApi.ListEntriesArray = listEntriesArray; 727 728 thisApi.ListDataAjaxUrl = listDataAjaxUrl; //future 729 730 thisApi.PopTimerMilliSeconds = 10000; //default to 10 seconds 731 732 if (popTimerSeconds > 0) 733 thisApi.PopTimerMilliSeconds = (popTimerSeconds * 1000); 734 735 thisApi.AddClassNames = addClassNames; 736 737 const _logger = getConsoleLogApi({ "LogSourceName": 'CE List Pop Box Panel Api' }); 738 739 let _currentEntryIndex = -1; 740 741 thisApi.RegisterInitializedCallBack = function (callBackFunction) { 742 743 CallBackManager.add('listPopBoxPanelApiInitializedCallBack', callBackFunction, null); 744 745 }; 746 747 thisApi.FireInitializedEvent = function () { 748 749 CallBackManager.fire('listPopBoxPanelApiInitializedCallBack', {}, null); 750 751 }; 752 753 thisApi.GetContainerElement = function () { return document.getElementById(thisApi.ContainerElemId); }; 754 755 thisApi.GetEntriesContainerElement = function () { 756 757 let retVal = null; 758 759 if (thisApi.GetContainerElement() != null) 760 retVal = thisApi.GetContainerElement().querySelector('div.onlineListWidgetPopBoxes'); 761 762 return retVal; 763 764 }; 765 766 thisApi.Show = function () { 767 768 if (thisApi.GetContainerElement() != null) 769 thisApi.GetContainerElement().show(); 770 771 }; 772 773 thisApi.Hide = function () { 774 775 if (thisApi.GetContainerElement() != null) 776 thisApi.GetContainerElement().vanish(); 777 778 }; 779 780 thisApi.Pause = function () { 781 782 if (thisApi.GetContainerElement() != null) 783 thisApi.GetContainerElement().setAttribute('data-updates-paused', '1'); 784 785 }; 786 787 thisApi.UnPause = function () { 788 789 if (thisApi.GetContainerElement() != null) 790 thisApi.GetContainerElement().setAttribute('data-updates-paused', '0'); 791 792 }; 793 794 thisApi.Stop = function () { 795 796 if (thisApi.GetContainerElement() != null) 797 thisApi.GetContainerElement().setAttribute('data-updates-stopped', '1'); 798 799 thisApi.Hide(); 800 801 }; 802 803 const _processUpdates = function () { 804 805 //this kills all updates becuase call back never fires resart 806 let isStopped = thisApi.GetContainerElement().tryGetTrueFalseAttribute('data-updates-stopped'); 807 if (!isStopped) { 808 809 let isPaused = thisApi.GetContainerElement().tryGetTrueFalseAttribute('data-updates-paused'); 810 if (!isPaused) { 811 812 //always clear first to allow for visual delay 813 thisApi.GetEntriesContainerElement().innerHTML = ''; 814 thisApi.Hide(); 815
816 _logger.Log('process pop box entry finished index ' + _currentEntryIndex + ' clear entry and wait before showing new entry'); 817 818 setTimeout( 819 function () { 820 821 _currentEntryIndex++; 822 823 if (_currentEntryIndex >= thisApi.ListEntriesArray.length) 824 _currentEntryIndex = 0; // start over 825 826 let newEntryHtml = thisApi.ListEntriesArray[_currentEntryIndex]; 827 828 _logger.Log('process pop box entry ' + _currentEntryIndex + ' of ' + thisApi.ListEntriesArray.length + ' after ' + thisApi.PopTimerMilliSeconds + ' millisecond delay'); 829 830 thisApi.GetEntriesContainerElement().innerHTML = newEntryHtml; 831 thisApi.Show(); 832 833 setTimeout(function () { _processUpdates() }, thisApi.PopTimerMilliSeconds); 834 835 }, 836 thisApi.PopTimerMilliSeconds); //reuse timer for both waits for now 837 838 } 839 else 840 setTimeout(function () { _processUpdates() }, thisApi.PopTimerMilliSeconds); 841 842 } 843 else 844 thisApi.Hide(); //belt & suspenders 845 846 }; 847 848 //Initialize 849 thisApi.Initialize = function () { 850 851 _logger.Log('initialize started'); 852 853 if (thisApi.GetContainerElement() != null) { 854 855 thisApi.Hide(); //belt & suspenders 856 857 //try to set to first child if possible 858 if ((thisApi.ListEntriesArray != null && thisApi.ListEntriesArray.length > 0) || !isBlank(thisApi.ListDataAjaxUrl)) { 859 860 thisApi.GetContainerElement().addEventListener('mouseover', function () { thisApi.Pause(); }); 861 thisApi.GetContainerElement().addEventListener('mouseout', function () { thisApi.UnPause(); }); 862 863 //add entries element if not popualted 864 let entriesElem = thisApi.GetEntriesContainerElement(); 865 if (entriesElem == null) { 866 867 _logger.Log('initialize getListPopBoxPanelApi adding required DOM elements'); 868 869 //add entries 870 let entriesElem = document.createElement('div'); 871 entriesElem.addClassName('onlineListWidgetPopBoxes'); 872 873 if (!isBlank(thisApi.AddClassNames)) 874 entriesElem.addClassNames(thisApi.AddClassNames); 875 876 thisApi.GetContainerElement().appendChild(entriesElem); 877 878 //add close icon 879 let closeIconCnt = document.createElement('div'); 880 881 closeIconCnt.addClassNames('onlineListWidgetBoxOffsetCloseIconContainers boxPositionBottomLeft'); 882 883 if (!isBlank(thisApi.AddClassNames)) 884 closeIconCnt.addClassNames(thisApi.AddClassNames); 885 886 //close circle 887 let closeIconCrcl = document.createElement('div'); 888 889 closeIconCrcl.addEventListener('click', function () { thisApi.Hide(); }); 890 891 closeIconCrcl.addClassNames('onlineListWidgetBoxOffsetCloseIconCircles'); 892 893 if (!isBlank(thisApi.AddClassNames)) 894 closeIconCrcl.addClassNames(thisApi.AddClassNames); 895 896 //close text 897 let closeIconTxt = document.createElement('div'); 898 899 closeIconTxt.addClassNames('onlineListWidgetBoxOffsetCloseIconText'); 900 901 if (!isBlank(thisApi.AddClassNames)) 902 closeIconTxt.addClassNames(thisApi.AddClassNames); 903 904 closeIconTxt.innerText = 'X'; 905 906 closeIconCrcl.appendChild(closeIconTxt); 907 908 closeIconCnt.appendChild(closeIconCrcl); 909 910 thisApi.GetContainerElement().appendChild(closeIconCnt); 911 912 } 913 914 _processUpdates(); 915 916 thisApi.FireInitializedEvent(); 917 918 } 919 else
920 _logger.Log('unable to initialize getListPopBoxPanelApi becuase data array is missing or emptry and no real-time ajax URL is available'); 921 922 } 923 else 924 _logger.Log('unable to initialize getListPopBoxPanelApi becuase container element id ' + thisApi.ContainerElemId + ' is invalid'); 925 926 }; 927 928 return thisApi; 929 930} 931 932function getEndUserSuggestedDonationAskAmountsApi(seedAmount, numberOfOptions, seedMultiplier) { 933 934 const thisApi = {}; 935 936 thisApi.SeedAmount = seedAmount; 937 thisApi.NumberOfOptions = numberOfOptions; 938 939 thisApi.SeedMultiplier = 1; 940 941 if (seedMultiplier > 0) 942 thisApi.SeedMultiplier = seedMultiplier; 943 944 thisApi.GenerateAmountsFromSeed = function () { return getEndUserSuggestionAskAmountsFromMinimum(thisApi.SeedAmount, thisApi.NumberOfOptions, thisApi.SeedMultiplier); }; 945 thisApi.GenerateAmountsToSeed = function () { return getEndUserSuggestionAskAmountsFromMaximum(thisApi.SeedAmount, thisApi.NumberOfOptions, thisApi.SeedMultiplier); }; 946 947 return thisApi; 948 949} 950 951function getAddAdditionalContactsApi(config) { 952 953 const thisApi = {}; 954 955 //thisApi.Type = config.Type; //if needed (i.e., registraiton verus membership) 956 957 thisApi.AutoInitialize = (config.AutoInitialize != null) ? config.AutoInitialize : true; 958 959 thisApi.ItemQuantityFieldDomId = config.ItemQuantityFieldDomId; 960 961 thisApi.RelatedElementContainerDomId = config.RelatedElementContainerDomId; 962 963 thisApi.WidgetContainerDomId = config.WidgetContainerDomId; 964 965 thisApi.MaximumContactsPerItem = (config.MaximumContactsPerItem != null) ? config.MaximumContactsPerItem : 0; 966 thisApi.MaximumContacts = (config.MaximumContacts != null) ? config.MaximumContacts : 0; 967 968 let _getMaximumContactsFunction = null; 969 970 if (config.GetMaximumContactsFunction != null && typeof config.GetMaximumContactsFunction == 'function') 971 _getMaximumContactsFunction = config.GetMaximumContactsFunction; 972 973 thisApi.EmailAddressRequired = (config.EmailAddressRequired != null) ? config.EmailAddressRequired : false; 974 thisApi.PhoneNumberRequired = (config.PhoneNumberRequired != null) ? config.PhoneNumberRequired : false; 975 976 thisApi.TitleHtml = config.TitleHtml; 977 978 thisApi.AddContactButtonText = (!isBlank(config.AddContactButtonText)) ? config.AddContactButtonText : 'Add Another'; 979 980 thisApi.AddFirstContactRow = (config.AddFirstContactRow != null) ? config.AddFirstContactRow : true; 981 982 thisApi.RelatedElementReference_Id = (config.RelatedElementReference_Id != null) ? config.RelatedElementReference_Id : 0; 983 984 thisApi.HiddenFieldDomId = config.HiddenFieldDomId; 985 986 const _logger = getConsoleLogApi({ "LogSourceName": 'CE Add More Contacts Api' }); 987 988 thisApi.GetItemQuantityFieldElement = function () { return document.getElementById(thisApi.ItemQuantityFieldDomId); }; 989 990 thisApi.GetItemQuantity = function () { 991 992 let retVal = 0; 993 994 const itemQuantElem = thisApi.GetItemQuantityFieldElement(); 995 996 if (itemQuantElem != null) 997 retVal = getFormItemQuantityInputValue(itemQuantElem); 998 else 999 retVal = 1; // if quanity selector not defined, hard-code 1 (e.g. membership sign up - always 1 no matter what unlike tickets) 1000 1001 return retVal; 1002 1003 }; 1004 1005 thisApi.GetRelatedElementContainerElement = function () { return document.getElementById(thisApi.RelatedElementContainerDomId); }; 1006 1007 thisApi.GetWidgetContainerElement = function () { 1008 1009 let contElem = document.getElementById(thisApi.WidgetContainerDomId); 1010 1011 if (contElem == null) 1012 contElem = thisApi.GetAddContactsWidgetElement(); //fall back (legacy forms mostly) 1013 1014 return contElem; 1015 1016 }; 1017 1018 thisApi.Show = function () { if (thisApi.GetWidgetContainerElement() != null) thisApi.GetWidgetContainerElement().show(); }; 1019 thisApi.Hide = function () { if (thisApi.GetWidgetContainerElement() != null) thisApi.GetWidgetContainerElement().vanish(); }; 1020 1021 const _getUniqueDomIdPrefix = function () { return thisApi.RelatedElementContainerDomId + '_' + thisApi.WidgetContainerDomId; }; 1022 1023 thisApi.GetAddContactsWidgetElement = function () { return document.getElementById(_getUniqueDomIdPrefix() + '_additionalContactsContainer'); }; 1024 1025 thisApi.GetContactRowsContainerElement = function () { return document.getElementById(_getUniqueDomIdPrefix() + '_additionalContactsContactRowsContainer'); }; 1026 1027 thisApi.GetAddContactButtonElement = function () { return document.getElementById(_getUniqueDomIdPrefix() + '_addAn
1027otherButton'); }; 1028 1029 thisApi.GetAdditionalContactRows = function () { return thisApi.GetContactRowsContainerElement().querySelectorAll('.additionalContactsContactRows'); } 1030 thisApi.GetAdditionalContactRowCount = function () { return thisApi.GetContactRowsContainerElement().querySelectorAll('.additionalContactsContactRows').length; } 1031 1032 thisApi.HandleItemQuantityChanged = function () { 1033 1034 const contElem = thisApi.GetWidgetContainerElement(); 1035 if (contElem != null) { 1036 1037 contElem.vanish(); 1038 1039 const itemQuant = thisApi.GetItemQuantity(); 1040 if (itemQuant > 0) { 1041 1042 const maxContactsAllowed = _getNumberOfAllowedContacts(); 1043 1044 //make sure we don't have a dynamic evaluation (function) 1045 const maxDynamic = (_getMaximumContactsFunction != null && typeof _getMaximumContactsFunction == 'function'); 1046 1047 //only show the container if we can allow any contacts 1048 //NOTE: this might be over complicated, but idea is to possibly allow unlimited via non-dynamic config at some point - can be simplified if its not worth it and we want to force a max per item even if its 1 (5.28.26) 1049 if (maxContactsAllowed > 0 || !maxDynamic) { 1050 1051 contElem.show(); 1052 1053 _refreshAdditionalContactRowsBasedOnMax(); 1054 1055 } 1056 1057 } 1058 1059 } 1060 1061 _toggleAddGuestButton(); 1062 1063 }; 1064 1065 const _refreshAdditionalContactRowsBasedOnMax = function () { 1066 1067 const maxContactsAllowed = _getNumberOfAllowedContacts(); 1068 1069 let currentCount = thisApi.GetAdditionalContactRowCount(); 1070 while (currentCount > 0 && currentCount > maxContactsAllowed) { 1071 1072 //remove last 1073 let rows = thisApi.GetAdditionalContactRows(); 1074 if (rows != null && rows.length > 0) 1075 thisApi.TryRemoveContactRow(getLastArrayEntry(rows), false); 1076 1077 currentCount = thisApi.GetAdditionalContactRowCount(); 1078 1079 } 1080 1081 }; 1082 1083 thisApi.SetMaximumContactsPerItem = function (newMax) { 1084 1085 thisApi.MaximumContactsPerItem = newMax; 1086 1087 let maxContactsAllowed = _getNumberOfAllowedContacts(); 1088 1089 if (maxContactsAllowed > 0) { 1090 1091 _refreshAdditionalContactRowsBasedOnMax(); 1092 1093 _toggleAddGuestButton(); 1094 1095 } 1096 1097 }; 1098 1099 thisApi.ValidateRows = function () { 1100 1101 let retVal = true; 1102 1103 let contactRows = thisApi.GetAdditionalContactRows(); 1104 for (let x = 0; x < contactRows.length; x++) { 1105 1106 let firstNameElem = contactRows[x].querySelector('.additionalContactsContactFirstNames'); 1107 firstNameElem.validationSucceeded(); 1108 if (isBlank(firstNameElem.getValue())) { 1109 retVal = false; 1110 firstNameElem.validationFailed(); 1111 } 1112 1113 let lastNameElem = contactRows[x].querySelector('.additionalContactsContactLastNames'); 1114 lastNameElem.validationSucceeded(); 1115 if (isBlank(lastNameElem.getValue())) { 1116 retVal = false; 1117 lastNameElem.validationFailed(); 1118 } 1119 1120 let emailElem = contactRows[x].querySelector('.additionalContactsContactEmailAddresses'); 1121 emailElem.validationSucceeded(); 1122 if (thisApi.EmailAddressRequired && isBlank(emailElem.getValue())) { 1123 retVal = false; 1124 emailElem.validationFailed(); 1125 } 1126 1127 if (!isBlank(emailElem.getValue()) && !isValidEmailAddress(emailElem.getValue())) { 1128 retVal = false; 1129 emailElem.validationFailed(); 1130 } 1131 1132 let phoneElem = contactRows[x].querySelector('.additionalContactsContactPhoneNumbers'); 1133 phoneElem.validationSucceeded(); 1134 if (thisApi.PhoneNumberRequired && isBlank(phoneElem.getValue())) { 1135 retVal = false; 1136 phoneElem.validationFailed(); 1137 } 1138 1139 } 1140 1141 return retVal; 1142 1143 } 1144 1145 thisApi.GetContactDetails = function () { 1146 1147 let retVal = []; 1148 1149 let contactRows = thisApi.GetAdditionalContactRows(); 1150 for (let x = 0; x < contactRows.length; x++) { 1151 1152 let cnt = {}; 1153 1154 cnt.FirstName = contactRows[x].querySelector('.additionalContactsContactFirstNames').getValue(); 1155 cnt.LastName = contactRows[x].querySelector('.additionalContactsContactLastNames').getValue(); 1156 cnt.EmailAddress = contactRows[x].querySelector('.additionalContactsContactEmailAddresses').getValue(); 1157 cnt.PhoneNumber = contactRows[x].querySelector('.additionalContactsContactPhoneNumbers').getValue(); 1158 1159 retVal.push(cnt); 1160 1161 } 1162 1163 return retVal; 1164 1165 }; 1166 1167 thisApi.GetSerializedContactDetails = function () { 1168 1169 let cntArr = thisApi.GetContactDetails(); 1170 1171 return JSON.stringify(cntArr); 1172 1173 }; 1174 1175 thisApi.WriteSerializedContactDetailsToHiddenField = function () { 1176 1177 if (!isBlank(thisApi.HiddenFieldDomId) && document.getElementById(thisApi.HiddenFieldDomId) != null) { 1178 1179 let serCnt = thisApi.GetSerializedContactDetails(); 1180 1181 document.getElementById(thisApi.HiddenFieldDomId).setValue(serCnt); 1182 1183 } 1184 1185 }; 1186 1187 thisApi.RegisterInitializedCallBack = function (callBackFunction) { 1188 1189 CallBackManager.add(_getUniqueDomIdPrefix() + '_addAdditionalContactsApiInitializedCallBack', callBackFunction, null); 1190 1191 }; 1192 1193 const _fireInitializedEvent = function () { 1194 1195 let returnObj = {}; 1196 returnObj.RelatedElementContainerDomId = thisApi.RelatedElementContainerDomId; //note can have multiple per page 1197 1198 CallBackManager.fire(_getUniqueDomIdPrefix() + '_addAdditionalContactsApiInitializedCallBack', returnObj, null); 1199 1200 }; 1201 1202 const _getNumberOfAllowedContacts = function () { 1203 1204 const curQty = thisApi.GetItemQuantity();
1205 1206 let retVal = (curQty * thisApi.MaximumContactsPerItem); 1207 1208 if (thisApi.MaximumContacts > 0 && retVal > thisApi.MaximumContacts) 1209 retVal = thisApi.MaximumContacts; 1210 1211 //console.log(thisApi.WidgetContainerDomId + ' - _getNumberOfAllowedContacts curQty: ' + curQty); 1212 1213 if (_getMaximumContactsFunction != null && typeof _getMaximumContactsFunction == 'function') 1214 retVal = _getMaximumContactsFunction(curQty); 1215 1216 return retVal; 1217 1218 }; 1219 1220 const _toggleAddGuestButton = function () { 1221 1222 //check for add another 1223 const addAnotherBtn = thisApi.GetAddContactButtonElement(); 1224 1225 const maxContactsAllowed = _getNumberOfAllowedContacts(); 1226 1227 //make sure we don't have a dynamic evaluation (function) 1228 const maxDynamic = (_getMaximumContactsFunction != null && typeof _getMaximumContactsFunction == 'function'); 1229 1230 const currentUIRowCount = thisApi.GetAdditionalContactRowCount(); 1231 1232 addAnotherBtn.vanish(); 1233 if ((maxContactsAllowed <= 0 && !maxDynamic) || currentUIRowCount < maxContactsAllowed) 1234 addAnotherBtn.show(); 1235 1236 }; 1237 1238 thisApi.TryRemoveContactRow = function (rowParentElem, callToggleAddAnotherButton) { 1239 1240 const rowEntryRowsPrntElem = thisApi.GetContactRowsContainerElement(); 1241 if (rowEntryRowsPrntElem != null && rowParentElem != null) 1242 rowParentElem.remove(); 1243 1244 if(callToggleAddAnotherButton) 1245 _toggleAddGuestButton(); 1246 1247 }; 1248 1249 thisApi.AddContactRow = function () { 1250 1251 let rowEntryRowsPrntElem = thisApi.GetContactRowsContainerElement(); 1252 if (rowEntryRowsPrntElem != null) { 1253 1254 let entryRow = document.createElement('div'); 1255 entryRow.addClassName('additionalContactsContactRows'); 1256 1257 //add first name 1258 let firstNameElem = document.createElement('input'); 1259 firstNameElem.setAttribute('type', 'text'); 1260 firstNameElem.addClassName('additionalContactsContactFirstNames'); 1261 firstNameElem.setAttribute('placeholder', 'First Name'); 1262 1263 entryRow.appendChild(firstNameElem); 1264 1265 //add last name 1266 let lastNameElem = document.createElement('input'); 1267 lastNameElem.setAttribute('type', 'text'); 1268 lastNameElem.addClassName('additionalContactsContactLastNames'); 1269 lastNameElem.setAttribute('placeholder', 'Last Name'); 1270 1271 entryRow.appendChild(lastNameElem); 1272 1273 //add email 1274 let emailElem = document.createElement('input'); 1275 emailElem.setAttribute('type', 'text'); 1276 emailElem.addClassName('additionalContactsContactEmailAddresses'); 1277 emailElem.setAttribute('placeholder', 'Email'); 1278 1279 entryRow.appendChild(emailElem); 1280 1281 //add phone 1282 let phoneElem = document.createElement('input'); 1283 phoneElem.setAttribute('type', 'text'); 1284 phoneElem.addClassName('additionalContactsContactPhoneNumbers'); 1285 phoneElem.setAttribute('placeholder', 'Phone'); 1286 1287 entryRow.appendChild(phoneElem); 1288 1289 //if (thisApi.GetAdditionalContactRowCount() > 0) { 1290 1291 let removeBtn = document.createElement('input'); 1292 removeBtn.setAttribute('type', 'button'); 1293 removeBtn.setAttribute('value', 'Remove'); 1294 removeBtn.addEventListener('click', function () { thisApi.TryRemoveContactRow(this.parentNode, true); }); 1295 1296 entryRow.appendChild(removeBtn); 1297 1298 //} 1299 1300 rowEntryRowsPrntElem.appendChild(entryRow); 1301 1302 _toggleAddGuestButton(); 1303 1304 } 1305 1306 }; 1307 1308 let _initialized = false; 1309 1310 //Initialize 1311 thisApi.Initialize = function () { 1312
1313 _logger.Log('initialize started (' + _getUniqueDomIdPrefix() + ')'); 1314 1315 if (!_initialized) { 1316 1317 let parentElem = document.getElementById(thisApi.WidgetContainerDomId); 1318 1319 if (parentElem == null) 1320 parentElem = thisApi.GetRelatedElementContainerElement(); //fall back (legacy forms mostly) 1321 1322 if (parentElem != null) { 1323 1324 let additionalContactsContainer = document.createElement('div'); 1325 additionalContactsContainer.id = _getUniqueDomIdPrefix() + '_additionalContactsContainer'; 1326 additionalContactsContainer.addClassName('additionalContactsContainers'); 1327 1328 if (!isBlank(thisApi.TitleHtml)) { 1329 1330 let additionalContactsContactsTitleContainer = document.createElement('div'); 1331 additionalContactsContactsTitleContainer.id = _getUniqueDomIdPrefix() + '_additionalContactsContactTitleContainer'; 1332 additionalContactsContactsTitleContainer.addClassName('additionalContactsContactsTitleContainers'); 1333 1334 additionalContactsContactsTitleContainer.innerHTML = thisApi.TitleHtml; 1335 1336 additionalContactsContainer.appendChild(additionalContactsContactsTitleContainer); 1337 1338 } 1339 1340 let additionalContactsContactRowsContainer = document.createElement('div'); 1341 additionalContactsContactRowsContainer.id = _getUniqueDomIdPrefix() + '_additionalContactsContactRowsContainer'; 1342 additionalContactsContactRowsContainer.addClassName('additionalContactsContactRowsContainers'); 1343 1344 additionalContactsContainer.appendChild(additionalContactsContactRowsContainer); 1345 1346 //add button 1347 let addBtn = document.createElement('input'); 1348 addBtn.id = _getUniqueDomIdPrefix() + '_addAnotherButton'; 1349 addBtn.setAttribute('type', 'button'); 1350 addBtn.setAttribute('value', thisApi.AddContactButtonText); 1351 addBtn.addEventListener('click', function () { thisApi.AddContactRow(); }); 1352 1353 additionalContactsContainer.appendChild(addBtn); 1354 1355 parentElem.appendChild(additionalContactsContainer); 1356 1357 //add first row 1358 if(thisApi.AddFirstContactRow) 1359 thisApi.AddContactRow(); 1360 1361 //wire up quanity changed event 1362 let qtyInput = thisApi.GetItemQuantityFieldElement(); 1363 if (qtyInput != null) { 1364 1365 if(qtyInput.tagName.toLowerCase() == 'input' && qtyInput.getAttribute('type') == 'text') 1366 qtyInput.addEventListener('input', function () { thisApi.HandleItemQuantityChanged(); }); 1367 else 1368 qtyInput.addEventListener('change', function () { thisApi.HandleItemQuantityChanged(); }); 1369 1370 } 1371 1372 thisApi.HandleItemQuantityChanged(); 1373 1374 _initialized = true; 1375 1376 } 1377 else 1378 _logger.Log('invalid parent element for additional contact api (' + _getUniqueDomIdPrefix() + ')'); 1379 1380 } 1381 else 1382 _logger.Log('skipping additional contacts api initialization (' + _getUniqueDomIdPrefix() + ') (already initialized)'); 1383 1384 _fireInitializedEvent(); 1385 1386 }; 1387 1388 if (thisApi.AutoInitialize) { 1389 1390 document.addEventListener('DOMContentLoaded', function () { thisApi.Initialize(); }); 1391 1392 } 1393 1394 return thisApi; 1395 1396} 1397 1398function getCheckOutItemForMeOrSomeoneElseApi(config) { 1399 1400 const thisApi = {}; 1401 1402 //thisApi.Type = config.Type; //if needed (i.e., registraiton verus membership) 1403 1404 thisApi.AutoInitialize = (config.AutoInitialize != null) ? config.AutoInitialize : true; 1405 1406 thisApi.ItemQuantityFieldDomId = config.ItemQuantityFieldDomId;
1407 1408 thisApi.RelatedElementContainerDomId = config.RelatedElementContainerDomId; 1409 1410 thisApi.WidgetContainerDomId = config.WidgetContainerDomId; 1411 1412 thisApi.EmailAddressRequired = (config.EmailAddressRequired != null) ? config.EmailAddressRequired : false; 1413 thisApi.PhoneNumberRequired = (config.PhoneNumberRequired != null) ? config.PhoneNumberRequired : false; 1414 1415 thisApi.ToggleLableCheckedText = (config.ToggleLableCheckedText != null) ? config.ToggleLableCheckedText : 'for me'; 1416 thisApi.ToggleLableNotCheckedText = (config.ToggleLableNotCheckedText != null) ? config.ToggleLableNotCheckedText : 'someone else'; 1417 1418 thisApi.RelatedElementReference_Id = (config.RelatedElementReference_Id != null) ? config.RelatedElementReference_Id : 0; 1419 1420 thisApi.HiddenFieldDomId = config.HiddenFieldDomId; 1421 1422 const _logger = getConsoleLogApi({ "LogSourceName": 'CE Me Or Someone Else Api' }); 1423 1424 thisApi.GetItemQuantityFieldElement = function () { return document.getElementById(thisApi.ItemQuantityFieldDomId); }; 1425 1426 thisApi.GetRelatedElementContainerElement = function () { return document.getElementById(thisApi.RelatedElementContainerDomId); }; 1427 1428 thisApi.GetWidgetContainerElement = function () { 1429 1430 let contElem = document.getElementById(thisApi.WidgetContainerDomId); 1431 1432 if (contElem == null) 1433 contElem = thisApi.GetAddContactsWidgetElement(); //fall back (legacy forms mostly) 1434 1435 return contElem; 1436 1437 }; 1438 1439 thisApi.GetAddContactsWidgetElement = function () { return document.getElementById(thisApi.RelatedElementContainerDomId + '_forMeOrSomeoneElseContainer'); }; 1440 1441 thisApi.GetContactInfoContainerElement = function () { return document.getElementById(thisApi.RelatedElementContainerDomId + '_forMeOrSomeoneElseContactInfoContainer'); }; 1442 1443 thisApi.GetToggleCheckboxElement = function () { return document.getElementById(thisApi.RelatedElementContainerDomId + '_forMeOrSomeoneElseToggleCheckBox'); }; 1444 thisApi.GetToggleCheckboxLabelElement = function () { return document.getElementById(thisApi.RelatedElementContainerDomId + '_forMeOrSomeoneElseToggleCheckBoxLabel'); }; 1445 1446 thisApi.HandleItemQuantityChanged = function () { 1447 1448 let wigContainerElem = thisApi.GetWidgetContainerElement(); 1449 if (wigContainerElem != null) { 1450 1451 wigContainerElem.vanish(); 1452 1453 let itemQuantElem = thisApi.GetItemQuantityFieldElement(); 1454 1455 let itemQuant = getFormItemQuantityInputValue(itemQuantElem); 1456 1457 if (itemQuant > 0) 1458 wigContainerElem.show(); 1459 1460 } 1461 1462 }; 1463 1464 thisApi.HandleToggleCheckBoxChanged = function () { 1465 1466 let cbElem = thisApi.GetToggleCheckboxElement(); 1467 let lblElem = thisApi.GetToggleCheckboxLabelElement(); 1468 1469 let contactInfoContainerElem = thisApi.GetContactInfoContainerElement(); 1470 1471 if (cbElem != null && lblElem != null && contactInfoContainerElem != null) { 1472 1473 if (cbElem.checked) { 1474 contactInfoContainerElem.vanish(); 1475 lblElem.innerHTML = thisApi.ToggleLableCheckedText; 1476 } 1477 else 1478 { 1479 contactInfoContainerElem.show(); 1480 lblElem.innerHTML = thisApi.ToggleLableNotCheckedText; 1481 } 1482 1483 } 1484 1485 }; 1486 1487 thisApi.ValidateContactInfo = function () { 1488 1489 let retVal = true; 1490 1491 let cbElem = thisApi.GetToggleCheckboxElement(); 1492 let contactInfoContainer = thisApi.GetContactInfoContainerElement(); 1493 1494 //only validate if checked 1495 if (cbElem != null && !cbElem.checked && contactInfoContainer != null) { 1496 1497 let firstNameElem = contactInfoContainer.querySelector('.forMeOrSomeoneElseContactFirstNames'); 1498 firstNameElem.validationSucceeded(); 1499 if (isBlank(firstNameElem.getValue())){ 1500 retVal = false; 1501 firstNameElem.validationFailed(); 1502 } 1503 1504 let lastNameElem = contactInfoContainer.querySelector('.forMeOrSomeoneElseContactLastNames'); 1505 lastNameElem.validationSucceeded(); 1506 if (isBlank(lastNameElem.getValue())){ 1507 retVal = false; 1508 lastNameElem.validationFailed(); 1509 } 1510 1511 let emailElem = contactInfoContainer.querySelector('.forMeOrSomeoneElseContactEmailAddresses'); 1512 emailElem.validationSucceeded(); 1513 if (thisApi.EmailAddressRequired && isBlank(emailElem.getValue())){ 1514 retVal = false; 1515 emailElem.validationFailed(); 1516 } 1517 1518 if (!isBlank(emailElem.getValue()) && !isValidEmailAddress(emailElem.getValue())){ 1519 retVal = false; 1520 emailElem.validationFailed(); 1521 } 1522 1523 let phoneElem = contactInfoContainer.querySelector('.forMeOrSomeoneElseContactPhoneNumbers'); 1524 phoneElem.validationSucceeded(); 1525 if (thisApi.PhoneNumberRequired && isBlank(phoneElem.getValue())){ 1526 retVal = false; 1527 phoneElem.validationFailed(); 1528 } 1529 1530 } 1531 1532 return retVal; 1533 1534 } 1535 1536 thisApi.GetContactDetails = function () { 1537 1538 let retVal = {}; 1539 1540 let cbElem = thisApi.GetToggleCheckboxElement(); 1541 let contactInfoContainer = thisApi.GetContactInfoContainerElement(); 1542 1543 if (cbElem != null && contactInfoContainer != null) { 1544 1545 retVal.FirstName = ''; 1546 retVal.LastName = ''; 1547 retVal.EmailAddress = ''; 1548 retVal.PhoneNumber = ''; 1549 1550 if (!cbElem.checked) { 1551 retVal.FirstName = contactInfoContainer.querySelector('.forMeOrSomeoneElseContactFirstNames').getValue(); 1552 retVal.LastName = contactInfoContainer.querySelector('.forMeOrSomeoneElseContactLastNames').getValue(); 1553 retVal.EmailAddress = contactInfoContainer.querySelector('.forMeOrSomeoneElseContactEmailAddresses').getValue(); 1554 retVal.PhoneNumber = contactInfoContainer.querySelector('.forMeOrSomeoneElseContactPhoneNumbers').getValue(); 1555 } 1556 1557 } 1558 1559 return retVal; 1560 1561 }; 1562 1563 thisApi.GetSerializedContactDetails = function () { 1564 1565 let cntArr = thisApi.GetContactDetails(); 1566 1567 return JSON.stringify(cntArr); 1568 1569 }; 1570 1571 thisApi.WriteSerializedContactDetailsToHiddenField = function () { 1572 1573 if (!isBlank(thisApi.HiddenFieldDomId) && document.getElementById(thisApi.HiddenFieldDomId) != null) { 1574 1575 let serCnt = thisApi.GetSerializedContactDetails(); 1576 1577 document.getElementById(thisApi.HiddenFieldDomId).setValue(serCnt); 1578 1579 } 1580 1581 }; 1582 1583 thisApi.RegisterInitializedCallBack = function (callBackFunction) { 1584 1585 CallBackManager.add('forMeOrSomeoneElseApiInitializedCallBack', callBackFunction, null); 1586 1587 }; 1588 1589 const _fireInitializedEvent = function () { 1590 1591 let returnObj = {}; 1592 returnObj.RelatedElementContainerDomId = thisApi.RelatedElementContainerDomId; //note can have multiple per page 1593 1594 CallBackManager.fire('forMeOrSomeoneElseApiInitializedCallBack', returnObj, null); 1595 1596 }; 1597 1598 let _initialized = false; 1599 1600 //Initialize 1601 thisApi.Initialize = function () { 1602
1603 _logger.Log('initialize started (' + thisApi.RelatedElementContainerDomId + ')'); 1604 1605 if (!_initialized) { 1606 1607 let parentElem = document.getElementById(thisApi.WidgetContainerDomId); 1608 1609 if (parentElem == null) 1610 parentElem = thisApi.GetRelatedElementContainerElement(); //fall back (legacy forms mostly) 1611 1612 if (parentElem != null) { 1613 1614 let forMeOrSomeoneElseContainer = document.createElement('div'); 1615 forMeOrSomeoneElseContainer.id = thisApi.RelatedElementContainerDomId + '_forMeOrSomeoneElseContainer'; 1616 forMeOrSomeoneElseContainer.addClassName('forMeOrSomeoneElseContainers'); 1617 1618 //add checkbox 1619 let cbInput = document.createElement('input'); 1620 cbInput.setAttribute('type', 'checkbox'); 1621 cbInput.id = thisApi.RelatedElementContainerDomId + '_forMeOrSomeoneElseToggleCheckBox'; 1622 cbInput.checked = true; 1623 1624 cbInput.addEventListener('change', function () { thisApi.HandleToggleCheckBoxChanged(); }); 1625 1626 forMeOrSomeoneElseContainer.appendChild(cbInput); 1627 1628 //add label 1629 let lbl = document.createElement('label'); 1630 lbl.id = thisApi.RelatedElementContainerDomId + '_forMeOrSomeoneElseToggleCheckBoxLabel'; 1631 lbl.addClassName('forMeOrSomeoneElseToggleCheckBoxLabels'); 1632 lbl.innerHTML = thisApi.ToggleLableCheckedText; 1633 1634 forMeOrSomeoneElseContainer.appendChild(lbl); 1635 1636 let forMeOrSomeoneElseContactInfoContainer = document.createElement('div'); 1637 forMeOrSomeoneElseContactInfoContainer.id = thisApi.RelatedElementContainerDomId + '_forMeOrSomeoneElseContactInfoContainer'; 1638 forMeOrSomeoneElseContactInfoContainer.addClassName('forMeOrSomeoneElseContactInfoContainers'); 1639 1640 //add first name 1641 let firstNameElem = document.createElement('input'); 1642 firstNameElem.setAttribute('type', 'text'); 1643 firstNameElem.addClassName('forMeOrSomeoneElseContactFirstNames'); 1644 firstNameElem.setAttribute('placeholder', 'First Name'); 1645 1646 forMeOrSomeoneElseContactInfoContainer.appendChild(firstNameElem); 1647 1648 //add last name 1649 let lastNameElem = document.createElement('input'); 1650 lastNameElem.setAttribute('type', 'text'); 1651 lastNameElem.addClassName('forMeOrSomeoneElseContactLastNames'); 1652 lastNameElem.setAttribute('placeholder', 'Last Name'); 1653 1654 forMeOrSomeoneElseContactInfoContainer.appendChild(lastNameElem); 1655 1656 //add email 1657 let emailElem = document.createElement('input'); 1658 emailElem.setAttribute('type', 'text'); 1659 emailElem.addClassName('forMeOrSomeoneElseContactEmailAddresses'); 1660 emailElem.setAttribute('placeholder', 'Email'); 1661 1662 forMeOrSomeoneElseContactInfoContainer.appendChild(emailElem); 1663 1664 //add phone 1665 let phoneElem = document.createElement('input'); 1666 phoneElem.setAttribute('type', 'text'); 1667 phoneElem.addClassName('forMeOrSomeoneElseContactPhoneNumbers'); 1668 phoneElem.setAttribute('placeholder', 'Phone'); 1669 1670 forMeOrSomeoneElseContactInfoContainer.appendChild(phoneElem); 1671 1672 forMeOrSomeoneElseContainer.appendChild(forMeOrSomeoneElseContactInfoContainer); 1673 1674 parentElem.appendChild(forMeOrSomeoneElseContainer); 1675 1676 thisApi.HandleToggleCheckBoxChanged(); 1677 1678 //wire up quanity changed event 1679 let qtyInput = thisApi.GetItemQuantityFieldElement(); 1680 if (qtyInput != null) { 1681 1682 if(qtyInput.tagName.toLowerCase() == 'input' && qtyInput.getAttribute('type') == 'text') 1683 qtyInput.addEventListener('input', function () { thisApi.HandleItemQuantityChanged(); }); 1684 else 1685 qtyInput.addEventListener('change', function () { thisApi.HandleItemQuantityChanged(); }); 1686 1687 } 1688 1689 thisApi.HandleItemQuantityChanged(); 1690 1691 _initialized = true; 1692 1693 } 1694 else
1695 _logger.Log('invalid parent element for me or someone else api (' + thisApi.RelatedElementContainerDomId + ')'); 1696 1697 } 1698 else 1699 _logger.Log('skipping for me or someone else api initialization (' + thisApi.RelatedElementContainerDomId + ') (already initialized)'); 1700 1701 _fireInitializedEvent(); 1702 1703 }; 1704 1705 if (thisApi.AutoInitialize) { 1706 1707 document.addEventListener('DOMContentLoaded', function () { thisApi.Initialize(); }); 1708 1709 } 1710 1711 return thisApi; 1712 1713} 1714 1715function getStateProvincesApi() { 1716 1717 const thisApi = {}; 1718 1719 const _logger = getConsoleLogApi({ "LogSourceName": 'CE State/Provinces Api' }); 1720 1721 const _isStateMetaDataAvailableInContext = function () { return (typeof stateMap != 'undefined' && stateMap != null); }; 1722 1723 if (!_isStateMetaDataAvailableInContext()) 1724 _logger.LogWarning('states variable not presentat at time of initialization (ensure js file is included or variable is loaded on page when used)'); 1725 1726 const _getStateEntryById = function (state_id) { 1727 1728 let retVal = null; 1729 1730 if (state_id > 0) 1731 for (let x = 0; x < stateMap.length; x++) { 1732 1733 for (let y = 0; y < stateMap[x].length; y++) 1734 if (stateMap[x][y].stateID == state_id) { 1735 retVal = stateMap[x][y]; 1736 break; 1737 } 1738 1739 if (retVal != null) 1740 break; 1741 1742 } 1743 1744 return retVal; 1745 1746 }; 1747 1748 thisApi.GetStateAbbreviationById = function (state_id) { 1749 1750 let retVal = ''; 1751 1752 let st = _getStateEntryById(state_id); 1753 if (st != null) 1754 retVal = st.stateAbbreviation; 1755 1756 return retVal; 1757 1758 }; 1759 1760 thisApi.GetStateNameById = function (state_id) { 1761 1762 let retVal = ''; 1763 1764 let st = _getStateEntryById(state_id); 1765 if (st != null) 1766 retVal = st.stateName; 1767 1768 return retVal; 1769 1770 }; 1771 1772 const _getCountryStateEntries = function (country_id) { 1773 1774 let retVal = null; 1775 1776 if (country_id > 0) 1777 retVal = stateMap[country_id]; 1778 1779 return retVal; 1780 1781 }; 1782 1783 thisApi.GetCountryStateIdByNameOrAbbreviation = function (country_id, stateNameOrAbbreviation) { 1784 1785 let retVal = 0; 1786 1787 if (country_id > 0 && !isBlank(stateNameOrAbbreviation)) { 1788 1789 let cnt = _getCountryStateEntries(country_id); 1790 if (cnt != null) { 1791 1792 for (let x = 0; x < cnt.length; x++) 1793 if (cnt[x].stateName == stateNameOrAbbreviation || cnt[x].stateAbbreviation == stateNameOrAbbreviation) { 1794 retVal = cnt[x].stateID; 1795 break; 1796 } 1797 1798 } 1799 1800 } 1801 1802 return retVal; 1803 1804 }; 1805 1806 thisApi.RepopulateStatesDropDown = function (stateElem, country_id, addBlankOption = false) { 1807 1808 clearOptions(stateElem); 1809 1810 const cntStates = _getCountryStateEntries(country_id); 1811 1812 if (addBlankOption) 1813 stateElem.addDropDownOption(0, '', true); 1814 1815 if (cntStates != null) { 1816 1817 for (let x = 0; x < cntStates.length; x++) 1818 stateElem.addDropDownOption(cntStates[x].stateID,cntStates[x].stateAbbreviation); 1819 1820 } 1821 1822 if (stateElem.options.length > 0) 1823 stateElem.show(); 1824 else 1825 stateElem.vanish(); 1826 1827 }; 1828 1829 return thisApi; 1830 1831} 1832 1833function getCountriesApi() { 1834 1835 const thisApi = {}; 1836 1837 const _logger = getConsoleLogApi({ "LogSourceName": 'CE Countries Api' }); 1838 1839 const _isCountryMetaDataAvailableInContext = function () { return (typeof countryMap != 'undefined' && countryMap != null); }; 1840 1841 if (!_isCountryMetaDataAvailableInContext()) 1842 _logger.LogWarning('countries variable not present at time of initialization (ensure js file is included or variable is loaded on page when used)'); 1843 1844 thisApi.GetCountryCodeById = function (country_id) { 1845 1846 let retVal = ''; 1847 1848 if (country_id > 0) 1849 retVal = countryMap[country_id].countryCode; 1850 1851 return retVal; 1852 1853 }; 1854 1855 thisApi.IsPostalCodeRequiredById = function (country_id) { 1856 1857 let retVal = false; 1858 1859 if (country_id > 0) 1860 retVal = countryMap[country_id].PostalCodeRequired; 1861 1862 return retVal; 1863 1864 }; 1865 1866 thisApi.GetCountryCodeByIdOrUSA = function (country_id) { 1867 1868 let retVal = thisApi.GetCountryCodeById(country_id); 1869 1870 if (isBlank(retVal)) 1871 retVal = 'US'; 1872 1873 return retVal; 1874 1875 }; 1876 1877 thisApi.GetCountryIdByCodeOrAlias = function (countryCodeOrAlias) { 1878 1879 let retVal = 0; 1880 1881 if (!isBlank(countryCodeOrAlias)) { 1882 1883 for (let id in countryMap) 1884 if (countryMap[id].countryCode == countryCodeOrAlias || countryMap[id].countryAlias == countryCodeOrAlias) { 1885 retVal = id; 1886 break; 1887 } 1888 1889 } 1890 1891 return retVal; 1892 1893 }; 1894 1895 thisApi.GetCountryIdByCodeOrAliasOrUsa = function (countryCodeOrAlias) { 1896 1897 let retVal = 0; 1898 1899 retVal = thisApi.GetCountryIdByCodeOrAlias(countryCodeOrAlias); 1900 1901 if (retVal <= 0) 1902 retVal = Countries.USA; 1903 1904 return retVal; 1905 1906 }; 1907 1908 thisApi.PopulateCountriesDropDown = function (countriesElem) { 1909 1910 clearOptions(countriesElem); //paranoia 1911 1912 if (_isCountryMetaDataAvailableInContext()) { 1913
1914 Object.keys(countryMap).forEach(function(key) { 1915 const value = countryMap[key]; 1916 countriesElem.addDropDownOption(value.countryID,value.countryName); 1917 }); 1918 1919 } 1920 1921 if (countriesElem.options.length > 0) 1922 countriesElem.show(); 1923 else 1924 countriesElem.vanish(); 1925 1926 countriesElem.setDropDownValueOrFirst(Countries.USA); //default to USA for now 1927 1928 }; 1929 1930 return thisApi; 1931 1932} 1933 1934function getFormAddressApi(config) { 1935 1936 const thisApi = {}; 1937 1938 const _autoInitialize = (config.AutoInitialize != null) ? config.AutoInitialize : true; 1939 1940 const _required = (config.Required != null) ? config.Required : false; 1941 1942 const _applyStyles = (config.ApplyStyles != null) ? config.ApplyStyles : true; 1943 const _addBlankStateOption = (config.AddBlankStateOption != null) ? config.AddBlankStateOption : false; 1944 1945 const _formOrContainerDomId = config.FormOrContainerDomId; 1946 1947 const _street1FieldDomId = config.Street1FieldDomId; 1948 const _street2FieldDomId = config.Street2FieldDomId; 1949 const _cityFieldDomId = config.CityFieldDomId; 1950 const _stateProvinceFieldDomId = config.StateProvinceFieldDomId; 1951 const _postalCodeFieldDomId = config.PostalCodeFieldDomId; 1952 const _countryCodeFieldDomId = config.CountryFieldDomId; 1953 1954 const _cityStatePostalCodeLabelDomId = (config.CityStatePostalCodeLabelDomId != null) ? config.CityStatePostalCodeLabelDomId : null; 1955 1956 const _logger = getConsoleLogApi({ "LogSourceName": 'CE Form Address Api' }); 1957 1958 const _getFormOrContainerElement = function () { return document.getElementById(_formOrContainerDomId); }; 1959 1960 const _getStreet1FieldElement = function () { return document.getElementById(_street1FieldDomId); }; 1961 1962 Object.defineProperty(thisApi, 'Street1FieldElement', { 1963 get: function() { 1964 return _getStreet1FieldElement(); 1965 } 1966 }); 1967 1968 Object.defineProperty(thisApi, 'Street1', { 1969 get: function() { 1970 return thisApi.Street1FieldElement.getValue(); 1971 }, 1972 set: function(value) { 1973 thisApi.Street1FieldElement.setValue(value); 1974 } 1975 }); 1976 1977 const _getStreet2FieldElement = function () { return document.getElementById(_street2FieldDomId); }; 1978 1979 Object.defineProperty(thisApi, 'Street2FieldElement', { 1980 get: function() { 1981 return _getStreet2FieldElement(); 1982 } 1983 }); 1984 1985 Object.defineProperty(thisApi, 'Street2', { 1986 get: function() { 1987 return thisApi.Street2FieldElement.getValue(); 1988 }, 1989 set: function(value) { 1990 thisApi.Street2FieldElement.setValue(value); 1991 } 1992 }); 1993 1994 const _getCityFieldElement = function () { return document.getElementById(_cityFieldDomId); }; 1995 1996 Object.defineProperty(thisApi, 'CityFieldElement', { 1997 get: function() { 1998 return _getCityFieldElement(); 1999 } 2000 }); 2001 2002 Object.defineProperty(thisApi, 'City', { 2003 get: function() { 2004 return thisApi.CityFieldElement.getValue(); 2005 }, 2006 set: function(value) { 2007 thisApi.CityFieldElement.setValue(value); 2008 } 2009 }); 2010 2011 const _getStateProvinceFieldElement = function () { return document.getElementById(_stateProvinceFieldDomId); }; 2012 2013 Object.defineProperty(thisApi, 'StateProvinceFieldElement', { 2014 get: function() { 2015 return _getStateProvinceFieldElement(); 2016 } 2017 }); 2018 2019 Object.defineProperty(thisApi, 'StateProvince_Id', { 2020 get: function() { 2021 return thisApi.StateProvinceFieldElement.getValue().toIntZeroIfInvalid(); 2022 }, 2023 set: function(value) { 2024 thisApi.StateProvinceFieldElement.setDropDownValueOrFirst(value.toIntZeroIfInvalid()); 2025 } 2026 }); 2027 2028 const _getPostalCodeFieldElement = function () { return document.getElementById(_postalCodeFieldDomId); }; 2029 2030 Object.defineProperty(thisApi, 'PostalCodeFieldElement', { 2031 get: function() { 2032 return _getPostalCodeFieldElement(); 2033 } 2034 });
2035 2036 Object.defineProperty(thisApi, 'PostalCode', { 2037 get: function() { 2038 return thisApi.PostalCodeFieldElement.getValue(); 2039 }, 2040 set: function(value) { 2041 thisApi.PostalCodeFieldElement.setValue(value); 2042 } 2043 }); 2044 2045 const _getCountryFieldElement = function () { return document.getElementById(_countryCodeFieldDomId); }; 2046 2047 Object.defineProperty(thisApi, 'CountryFieldElement', { 2048 get: function() { 2049 return _getCountryFieldElement(); 2050 } 2051 }); 2052 2053 Object.defineProperty(thisApi, 'Country_Id', { 2054 get: function() { 2055 return thisApi.CountryFieldElement.getValue().toIntZeroIfInvalid(); 2056 }, 2057 set: function(value) { 2058 thisApi.CountryFieldElement.setDropDownValueOrFirst(value.toIntZeroIfInvalid()); 2059 } 2060 }); 2061 2062 const _getCityStatePostalCodeLabeElement = function () { 2063 2064 let retVal = null; 2065 2066 if(!isBlank(_cityStatePostalCodeLabelDomId)) 2067 retVal = document.getElementById(_cityStatePostalCodeLabelDomId); 2068 2069 return retVal; 2070 2071 }; 2072 2073 thisApi.MakeRequired = function () { 2074 2075 _getStreet1FieldElement().setAttribute('data-validation', 'required'); 2076 _getCityFieldElement().setAttribute('data-validation', 'required'); 2077 _getCountryFieldElement().setAttribute('data-validation', 'required,integer,positive'); 2078 2079 }; 2080 2081 thisApi.MakeNotRequired = function () { 2082 2083 _getStreet1FieldElement().removeAttribute('data-validation'); 2084 _getCityFieldElement().removeAttribute('data-validation'); 2085 _getCountryFieldElement().removeAttribute('data-validation'); 2086 2087 }; 2088 2089 const _handleCountryChange = function () { 2090 2091 const cntId = _getCountryFieldElement().getValue().toIntZeroIfInvalid(); 2092 2093 App.StateProvinces.RepopulateStatesDropDown(_getStateProvinceFieldElement(), cntId, _addBlankStateOption); 2094 2095 if (_required) { 2096 2097 const postalCodeReq = App.Countries.IsPostalCodeRequiredById(cntId); 2098 2099 if (postalCodeReq) 2100 _getPostalCodeFieldElement().setAttribute('data-validation', 'required'); 2101 else 2102 _getPostalCodeFieldElement().setAttribute('data-validation', ''); 2103 2104 } 2105 2106 if (_getCityStatePostalCodeLabeElement() != null) { 2107 2108 //set label 2109 let lbl = 'City'; 2110 2111 if (_getStateProvinceFieldElement().options.length > 0) 2112 lbl += ', State/Province'; 2113 2114 lbl += ', Postal Code, Country'; 2115 2116 _getCityStatePostalCodeLabeElement().innerHTML = lbl; 2117 2118 } 2119 2120 }; 2121 2122 let _initialized = false; 2123 2124 //Initialize 2125 thisApi.Initialize = function () { 2126 2127 _logger.Log('initialize started (' + _formOrContainerDomId + ')'); 2128 2129 if (!_initialized) { 2130 2131 if (_getFormOrContainerElement() != null) { 2132 2133 if (_getStreet1FieldElement() != null && _getStreet2FieldElement() != null && _getCityFieldElement() != null && _getStateProvinceFieldElement() != null && _getPostalCodeFieldElement() != null && _getCountryFieldElement() != null) { 2134 2135 App.Countries.PopulateCountriesDropDown(_getCountryFieldElement()); 2136 2137 _handleCountryChange(); 2138 2139 _getCountryFieldElement().addEventListener('change', function () { _handleCountryChange(); }); 2140 2141 //wire up validation if not present 2142 if (_required) 2143 thisApi.MakeRequired(); 2144 2145 if (_applyStyles) { 2146 _getStreet1FieldElement().setAttribute('maxlength', 100); 2147 _getStreet1FieldElement().setAttribute('placeholder', 'Street Address'); 2148 2149 _getStreet2FieldElement().setAttribute('maxlength', 100); 2150 _getStreet2FieldElement().setAttribute('placeholder', 'Suite or Unit #'); 2151 2152 _getCityFieldElement().setAttribute('maxlength', 50); 2153 _getCityFieldElement().setAttribute('style', 'width:120px;'); 2154 _getCityFieldElement().setAttribute('placeholder', 'City'); 2155 2156 //_getStateProvinceFieldElement() 2157 2158 _getPostalCodeFieldElement().setAttribute('maxlength', 20); 2159 _getPostalCodeFieldElement().setAttribute('style', 'width:95px;'); 2160 _getPostalCodeFieldElement().setAttribute('placeholder', 'Postal Code'); 2161 2162 //_getCountryFieldElement() 2163 } 2164 2165 _initialized = true; 2166 2167 } 2168 else
2169 _logger.Log('invalid or missing form fields (' + _formOrContainerDomId + ')'); 2170 2171 } 2172 else 2173 _logger.Log('invalid or missing form or container element dom id (' + _formOrContainerDomId + ')'); 2174 2175 } 2176 else 2177 _logger.Log('skipping form address fields api initialization (' + _formOrContainerDomId + ') (already initialized)'); 2178 2179 //_fireInitializedEvent(); 2180 2181 }; 2182 2183 if (_autoInitialize) { 2184 2185 document.addEventListener('DOMContentLoaded', function () { thisApi.Initialize(); }); 2186 2187 } 2188 2189 return thisApi; 2190 2191} 2192 2193function getAmountTextBoxPlusMinusButtonsApi(config) { 2194 2195 const thisApi = {}; 2196 2197 thisApi.TextBoxDomId = config.TextBoxDomId; 2198 thisApi.MinimumValue = config.MinimumValue != null ? config.MinimumValue : 0; 2199 thisApi.MaximumValue = config.MaximumValue != null ? config.MaximumValue : 0; 2200 2201 const _logger = getConsoleLogApi({"LogSourceName": 'CE Amount Textbox Plus Minus Buttons Api'}); 2202 2203 const _tryGetTextBoxElement = function () {return document.getElementById(thisApi.TextBoxDomId);}; 2204 2205 const _tryGetAmountOrZero = function(){ 2206 2207 let retVal = 0; 2208 2209 const tbElem = _tryGetTextBoxElement(); 2210 if (tbElem != null) 2211 retVal = tbElem.getValue().toIntZeroIfInvalid(); 2212 2213 return retVal; 2214 2215 }; 2216 2217 const _tryGetMinusButtonElement = function () { 2218 2219 let retVal = null; 2220 2221 const tbElem = _tryGetTextBoxElement(); 2222 if (tbElem != null) 2223 retVal = tbElem.parentElement.querySelector('.ce-textBoxMinusButtons'); 2224 2225 return retVal; 2226 2227 }; 2228 2229 const _tryGetPlusButtonElement = function () { 2230 2231 let retVal = null; 2232 2233 const tbElem = _tryGetTextBoxElement(); 2234 if (tbElem != null) 2235 retVal = tbElem.parentElement.querySelector('.ce-textBoxPlusButtons'); 2236 2237 return retVal; 2238 2239 }; 2240 2241 const _generateFormFieldButton = function (domId, clickCallbackFunction, toolTip) { 2242 2243 let retVal = document.createElement('button'); 2244 retVal.id = domId; 2245 retVal.setAttribute('type', 'button'); 2246 2247 if (!isBlank(toolTip)) 2248 retVal.setAttribute('title', toolTip); 2249 2250 retVal.addClassName('ce-formFieldButtons ce-iconOnlyButtons ce-noThemeStyles'); 2251 2252 if (typeof clickCallbackFunction == 'function') 2253 retVal.addEventListener('click', function () {clickCallbackFunction(this);}); 2254 2255 let cntElem = document.createElement('span'); 2256 2257 cntElem.addClassName('ce-formFieldButtonContentContainers'); 2258 2259 retVal.appendChild(cntElem); 2260 2261 return retVal; 2262 2263 }; 2264 2265 const _handleReduceAmountClick = function () { 2266 2267 const tbElem = _tryGetTextBoxElement(); 2268 if (tbElem != null) { 2269 2270 let rawVal = tbElem.getValue(); 2271 let curVal = rawVal.toIntZeroIfInvalid(); 2272 //console.log('curVal: ' + curVal); 2273 2274 if (curVal < thisApi.MinimumValue) 2275 curVal = thisApi.MinimumValue; 2276 2277 if (curVal > thisApi.MinimumValue) 2278 curVal--; 2279 2280 //console.log('rawVal: ' + rawVal); 2281 //console.log('curVal: ' + curVal); 2282 2283 if (rawVal.trim() != curVal.toString().trim()) { 2284 tbElem.setValue(curVal); 2285 //_internalHandleAmountChanged(); //TODO: figure out why addInputElementValueChangeListener isn't working and remove 2286 //_fireAmountChangedEvent(); 2287 } 2288 2289 } 2290 2291 }; 2292 2293 const _handleIncreaseAmountClick = function () { 2294 2295 const tbElem = _tryGetTextBoxElement(); 2296 if (tbElem != null) { 2297 2298 let rawVal = tbElem.getValue(); 2299 let curVal = rawVal.toIntZeroIfInvalid(); 2300 //console.log('curVal: ' + curVal); 2301 2302 if (thisApi.MaximumValue <= 0 || curVal < thisApi.MaximumValue) 2303 curVal++; 2304 2305 //console.log('rawVal: ' + rawVal); 2306 //console.log('curVal: ' + curVal); 2307 2308 if (rawVal.trim() != curVal.toString().trim()) { 2309 tbElem.setValue(curVal); 2310 //_internalHandleAmountChanged(); //TODO: figure out why addInputElementValueChangeListener isn't working and remove 2311 //_fireAmountChangedEvent(); 2312 } 2313 2314 } 2315 2316 }; 2317 2318 thisApi.RegisterAmountChangedCallBack = function (callBackFunction) { 2319 CallBackManager.add('amountTextBoxPlusMinusButtonsApi_' + thisApi.TextBoxDomId + '_amountChangedCallBack', callBackFunction, null); 2320 }; 2321 2322 const _fireAmountChangedEvent = function () {
2323 const cbParams = {"TextBoxElement": _tryGetTextBoxElement()} 2324 CallBackManager.fire('amountTextBoxPlusMinusButtonsApi_' + thisApi.TextBoxDomId + '_amountChangedCallBack', cbParams, null); 2325 }; 2326 2327 thisApi.RegisterInitializedCallBack = function (callBackFunction) { 2328 CallBackManager.add('amountTextBoxPlusMinusButtonsApi_' + thisApi.TextBoxDomId + '_initializedCallBack', callBackFunction, null); 2329 }; 2330 2331 const _fireInitializedEvent = function () { 2332 CallBackManager.fire('amountTextBoxPlusMinusButtonsApi_' + thisApi.TextBoxDomId + '_initializedCallBack', {}, null); 2333 }; 2334 2335 const _refreshUIWhenAmountChanges = function(){ 2336 2337 const amt = _tryGetAmountOrZero(); 2338 2339 const decreaseBtn = _tryGetMinusButtonElement(); 2340 2341 decreaseBtn.show(); 2342 if (amt <= 0 || amt <= thisApi.MinimumValue) decreaseBtn.vanish(); 2343 2344 const increaseBtn = _tryGetPlusButtonElement(); 2345 2346 increaseBtn.show(); 2347 if (thisApi.MaximumValue > 0 && amt >= thisApi.MaximumValue) increaseBtn.vanish(); 2348 2349 }; 2350 2351 const _internalHandleAmountChanged = function () { 2352 2353 _refreshUIWhenAmountChanges(); 2354 2355 _fireAmountChangedEvent(); 2356 2357 }; 2358 2359 //Initialize 2360 thisApi.Initialize = function () { 2361 2362 _logger.Log('initialize started (' + thisApi.TextBoxDomId + ')'); 2363 2364 const tbElem = _tryGetTextBoxElement(); 2365 2366 if (tbElem == null) { 2367 _logger.Log('invalid textbox element (abort initialization)'); 2368 return; 2369 } 2370 2371 const minusBtnElem = _tryGetMinusButtonElement(); 2372 2373 if (minusBtnElem != null) { 2374 _logger.Log('minus button already present (abort initialization)'); 2375 return; 2376 } 2377 2378 const plusBtnElem = _tryGetPlusButtonElement(); 2379 2380 if (plusBtnElem != null) { 2381 _logger.Log('plus button already present (abort initialization)'); 2382 return; 2383 } 2384 2385 const tbElemContainer = tbElem.parentElement; 2386 2387 if (tbElemContainer == null) { 2388 _logger.Log('invalid textbox parent element (abort initialization)'); 2389 return; 2390 } 2391 2392 tbElem.addClassName('ce-amountWithPlusMinusButtonInputs'); 2393 tbElem.parentElement.addClassName('ce-amountWithPlusMinusButtonInputContainers'); 2394 2395 const buttonsWrapperElem = document.createElement('div'); 2396 buttonsWrapperElem.addClassName('ce-adjacentFormFieldButtonContainers'); 2397 2398 //add minus button 2399 const tikMinusButton = _generateFormFieldButton(thisApi.TextBoxDomId + '_reduceAmountButton', function () {_handleReduceAmountClick();}, 'reduce amount'); 2400 tikMinusButton.addClassName('ce-textBoxMinusButtons'); 2401 tikMinusButton.firstElementChild.addClassNames('fa fa-minus'); 2402 //tikMinusButton.setAttribute('data-textboxDomId', thisApi.TextBoxDomId); 2403 buttonsWrapperElem.appendChild(tikMinusButton); 2404 2405 const tikPlusButton = _generateFormFieldButton(thisApi.TextBoxDomId + '_increaseAmountButton', function () {_handleIncreaseAmountClick();}, 'increase amount'); 2406 tikPlusButton.addClassName('ce-textBoxPlusButtons'); 2407 tikPlusButton.firstElementChild.addClassNames('fa fa-plus'); 2408 //tikPlusButton.setAttribute('data-textboxDomId', thisApi.TextBoxDomId); 2409 buttonsWrapperElem.appendChild(tikPlusButton); 2410 2411 tbElemContainer.appendChild(buttonsWrapperElem); 2412 2413 addInputElementValueChangeListener(tbElem, function () { _internalHandleAmountChanged(); }); 2414 2415 if (typeof config.AmountChangedCallBack == 'function') 2416 thisApi.RegisterAmountChangedCallBack(config.AmountChangedCallBack); 2417 2418 _refreshUIWhenAmountChanges(); 2419 2420 _fireInitializedEvent(); 2421 2422 }; 2423 2424 return thisApi; 2425 2426} 2427 2428function getInKindTypesApi() { 2429 2430 const thisApi = {}; 2431 2432 const _logger = getConsoleLogApi({ "LogSourceName": 'CE In-Kind Types Api' }); 2433 2434 const _inKindTypeDataAvailableInContext = (typeof WebFormContext != 'undefined' && WebFormContext.InKindTypeClassMap != 'undefined'); 2435 2436 if (!_inKindTypeDataAvailableInContext) 2437 _logger.LogWarning('InKindTypeClassMap variable not present (ensure variable is published on page)'); 2438 2439 thisApi.GetClassIdByTypeId = function (type_id) { 2440 2441 let retVal = ''; 2442 2443 if (type_id > 0) 2444 retVal = WebFormContext.InKindTypeClassMap[type_id]; 2445 2446 return retVal; 2447 2448 }; 2449 2450 return thisApi; 2451 2452} 2453 2454function getCurrencyApi() { 2455 2456 const thisApi = {}; 2457 2458 const _logger = getConsoleLogApi({ "LogSourceName": 'CE Currency Api' });
2459 2460 const _currenciesAvailableInContext = (typeof currencies != 'undefined'); 2461 2462 if (!_currenciesAvailableInContext) 2463 _logger.LogWarning('currencies variable not present (ensure js file is included or variable is published on page)'); 2464 2465 thisApi.GetCurrencyIdByCode = function(currencyCode){ 2466 2467 let retVal = 0; 2468 2469 if (!isBlank(currencyCode)) { 2470 2471 for (let x = 0; x < currencies.length; x++) 2472 if (currencies[x].CurrencyCode == currencyCode) { 2473 retVal = currencies[x].Id; 2474 break; 2475 } 2476 2477 } 2478 2479 return retVal; 2480 2481 } 2482 2483 thisApi.GetCurrencyIdByCodeOrUSD = function(currencyCode){ 2484 2485 let retVal = thisApi.GetCurrencyIdByCode(currencyCode); 2486 2487 if (retVal <= 0) 2488 retVal = Currencies.USD; 2489 2490 return retVal; 2491 2492 } 2493 2494 thisApi.GetCurrencyCodeByIdOrUSD = function (currency_id) { 2495 2496 let retVal = ''; 2497 2498 if (currency_id <= 0) 2499 currency_id = Currencies.USD; 2500 2501 for (let x = 0; x < currencies.length; x++) 2502 if (currencies[x].Id == currency_id) { 2503 retVal = currencies[x].CurrencyCode; 2504 break; 2505 } 2506 2507 return retVal; 2508 2509 }; 2510 2511 thisApi.GetCurrencySymbolByIdOrUSD = function (currency_id) { 2512 2513 let retVal = ''; 2514 2515 if (currency_id <= 0) 2516 currency_id = Currencies.USD; 2517 2518 for (let x = 0; x < currencies.length; x++) 2519 if (currencies[x].Id == currency_id) { 2520 retVal = currencies[x].Symbol; 2521 break; 2522 } 2523 2524 return retVal; 2525 2526 }; 2527 2528 return thisApi; 2529 2530} 2531 2532function getSingleLineAddressFormFieldApi(config) { 2533 2534 const thisApi = {}; 2535 2536 thisApi.AutoInitialize = (config.AutoInitialize != null) ? config.AutoInitialize : true; 2537 2538 //Two use cases: eitehr map dom element and API will set values or leave them unnassigned and listen for the validation successed event to implement custom logic 2539 thisApi.SingleLineAddressTextBoxDomId = config.SingleLineAddressTextBoxDomId; 2540 thisApi.AddressStreet1TextBoxDomId = config.AddressStreet1TextBoxDomId; 2541 thisApi.AddressStreet2TextBoxDomId = config.AddressStreet2TextBoxDomId; 2542 thisApi.AddressCityTextBoxDomId = config.AddressCityTextBoxDomId; 2543 thisApi.AddressStateProvinceDropDownDomId = config.AddressStateProvinceDropDownDomId; 2544 thisApi.AddressPostalCodeTextBoxDomId = config.AddressPostalCodeTextBoxDomId; 2545 thisApi.AddressCountryDropDownDomId = config.AddressCountryDropDownDomId; 2546 //thisApi.PlaceAutocompleteElement = config.PlaceAutocompleteElement; //New GOOGLE Maps API 2547 2548 //TODO: add show/hide element for error toggle? 2549 2550 const _logger = getConsoleLogApi({ "LogSourceName": 'CE Single Line Address Form Field API (' + thisApi.SingleLineAddressTextBoxDomId + ')' }); 2551 2552 const _getSingleLineAddressTextBoxElement = function () { return document.getElementById(thisApi.SingleLineAddressTextBoxDomId); }; 2553
2554 Object.defineProperty(thisApi, 'SingleLineAddressTextBox', { 2555 get: function () { 2556 return _getSingleLineAddressTextBoxElement(); 2557 } 2558 }); 2559 2560 const _getAddressStreet1TextBoxElement = function () { return document.getElementById(thisApi.AddressStreet1TextBoxDomId); }; 2561 2562 Object.defineProperty(thisApi, 'AddressStreet1TextBoxElement', { 2563 get: function () { 2564 return _getAddressStreet1TextBoxElement(); 2565 } 2566 }); 2567 2568 const _getAddressStreet2TextBoxElement = function () { return document.getElementById(thisApi.AddressStreet2TextBoxDomId); }; 2569 2570 Object.defineProperty(thisApi, 'AddressStreet2TextBoxElement', { 2571 get: function () { 2572 return _getAddressStreet2TextBoxElement(); 2573 } 2574 }); 2575 2576 const _getAddressCityTextBoxElement = function () { return document.getElementById(thisApi.AddressCityTextBoxDomId); }; 2577 2578 Object.defineProperty(thisApi, 'AddressCityTextBoxElement', { 2579 get: function () { 2580 return _getAddressCityTextBoxElement(); 2581 } 2582 }); 2583 2584 const _getAddressStateProvinceDropDownElement = function () { return document.getElementById(thisApi.AddressStateProvinceDropDownDomId); }; 2585
2586 Object.defineProperty(thisApi, 'AddressStateProvinceDropDownElement', { 2587 get: function () { 2588 return _getAddressStateProvinceDropDownElement(); 2589 } 2590 }); 2591 2592 const _getAddressPostalCodeTextBoxElement = function () { return document.getElementById(thisApi.AddressPostalCodeTextBoxDomId); }; 2593 2594 Object.defineProperty(thisApi, 'AddressPostalCodeTextBoxElement', { 2595 get: function () { 2596 return _getAddressPostalCodeTextBoxElement(); 2597 } 2598 }); 2599 2600 const _getAddressCountryDropDownElement = function () { return document.getElementById(thisApi.AddressCountryDropDownDomId); }; 2601 2602 Object.defineProperty(thisApi, 'AddressCountryDropDownElement', { 2603 get: function () { 2604 return _getAddressCountryDropDownElement(); 2605 } 2606 }); 2607 2608 thisApi.RegisterInitializedCallBack = function (callBackFunction) { 2609 CallBackManager.add('singleLineAddressFormField_' + thisApi.PostalCodeEntryTextBoxDomId + '_initializedCallBack', callBackFunction, null); 2610 }; 2611 2612 const _fireInitializedEvent = function () { 2613 CallBackManager.fire('singleLineAddressFormField_' + thisApi.PostalCodeEntryTextBoxDomId + '_initializedCallBack', {}, null); 2614 }; 2615 2616 thisApi.RegisterAddressSelectedCallBack = function (callBackFunction) { 2617 CallBackManager.add('singleLineAddressFormField_' + thisApi.PostalCodeEntryTextBoxDomId + '_addressSelectedCallBack', callBackFunction, null); 2618 }; 2619 2620 const _fireAddressSelectedEvent = function (result) { 2621 2622 let cb = {}; 2623 cb.Result = result; 2624 2625 CallBackManager.fire('singleLineAddressFormField_' + thisApi.PostalCodeEntryTextBoxDomId + '_addressSelectedCallBack', cb, null); 2626 2627 }; 2628 2629 thisApi.RegisterGoogleMapsIntegrationFailureCallBack = function (callBackFunction) { 2630 CallBackManager.add('singleLineAddressFormField_' + thisApi.PostalCodeEntryTextBoxDomId + '_GoogleMapsIntegrationFailureCallBack', callBackFunction, null); 2631 }; 2632 2633 const _fireGoogleMapsIntegrationFailureEvent = function () { 2634 CallBackManager.fire('singleLineAddressFormField_' + thisApi.PostalCodeEntryTextBoxDomId + '_GoogleMapsIntegrationFailureCallBack', {}, null); 2635 }; 2636 2637 //thisApi.RegisterAddressLookupFailedCallBack = function (callBackFunction) { 2638 // CallBackManager.add('singleLineAddressFormField_' + thisApi.PostalCodeEntryTextBoxDomId + '_addressLookupFailedCallBack', callBackFunction, null); 2639 //}; 2640 // 2641 //const _fireAddressLookupFailedEvent = function () { 2642 // CallBackManager.fire('singleLineAddressFormField_' + thisApi.PostalCodeEntryTextBoxDomId + '_addressLookupFailedCallBack', {}, null); 2643 //}; 2644 2645 const _handleSingleLineAddressSelected = function (selectedPlace) { 2646 2647 //comment out following line for new Google Maps API 2648 selectedPlace = _googleAutoCompleteApi.getPlace(); 2649 2650 _logger.Log('single line address selected ' + selectedPlace.formatted_address); 2651
2652 //_logger.Log('single line address selected ' + selectedPlace.formattedAddress); //New Google Maps API 2653 2654 let street1Formatted = parseGoogleAddressLine1Short(selectedPlace); 2655 let city = parseGoogleCityShortName(selectedPlace); 2656 let state = parseGoogleStateShortName(selectedPlace); 2657 let zip = parseGooglePostalCodeLongName(selectedPlace); 2658 let country = parseGoogleCountryShortName(selectedPlace); 2659 2660 //_logger.Log('street1Formatted: ' + street1Formatted); 2661 //_logger.Log('city: ' + city); 2662 //_logger.Log('state: ' + state); 2663 //_logger.Log('zip: ' + zip); 2664 //_logger.Log('country: ' + country); 2665 2666 //todo: wire up 2667 if (thisApi.AddressStreet1TextBoxElement != null) 2668 thisApi.AddressStreet1TextBoxElement.setValue(street1Formatted); 2669 2670 //if (thisApi.AddressStreet2TextBoxElement != null) 2671 // thisApi.AddressStreet2TextBoxElement.setValue(''); 2672 2673 if (thisApi.AddressCityTextBoxElement != null) 2674 thisApi.AddressCityTextBoxElement.setValue(city); 2675 2676 if (thisApi.AddressStateProvinceDropDownElement != null) 2677 thisApi.AddressStateProvinceDropDownElement.setValue(''); 2678 2679 if (thisApi.AddressPostalCodeTextBoxElement != null) 2680 thisApi.AddressPostalCodeTextBoxElement.setValue(zip); 2681 2682 if (thisApi.AddressCountryDropDownElement != null) 2683 thisApi.AddressCountryDropDownElement.setValue(''); 2684 2685 //todo: add show/hide toggle error handler 2686 2687 _fireAddressSelectedEvent(selectedPlace); 2688 2689 }; 2690 2691 const _printGoogleMapsSetupInstructions = function () { 2692 2693 console.log( 2694 '%cGoogle Maps Address Lookup — Setup Required', 2695 'color: #d93025; font-weight: bold; font-size: 14px;' 2696 ); 2697 2698 console.log( 2699 '%cYour Google Maps address lookup is not working. Follow these steps to fix it:', 2700 'font-weight: bold;' 2701 ); 2702 2703 console.log('1. Go to the Google Cloud Console: https://console.cloud.google.com/'); 2704 console.log('2. Sign in with the account that owns your API key.'); 2705 console.log('3. Make sure the correct project is selected in the top dropdown.'); 2706 console.log('4. In the left menu, go to APIs & Services -> Library.'); 2707 console.log('5. Search for "Places API" (not "Places API (New)") and click Enable.'); 2708 console.log('6. Confirm billing is enabled for the project under the Billing section.'); 2709 console.log('7. Wait 2-5 minutes, then refresh this page.'); 2710 2711 console.log( 2712 '%cIf it still does not work, also check:', 2713 'font-weight: bold;' 2714 ); 2715 2716 console.log('- API key restrictions: under APIs & Services -> Credentials, confirm "Places API" is allowed.'); 2717 console.log('- Website restrictions: confirm your domain is listed under HTTP referrer restrictions.'); 2718 2719 }; 2720 2721 const _handleGoogleMapsIntegrationFailure = function (reasonMessage) { 2722 2723 let genMessage = 'Google Maps integration failed'; 2724 2725 if (!isBlank(reasonMessage)) 2726 genMessage += '(' + reasonMessage + ')'; 2727 2728 console.error(genMessage); 2729 2730 _printGoogleMapsSetupInstructions(); 2731 2732 _fireGoogleMapsIntegrationFailureEvent(); 2733 2734 }; 2735 2736 let _googleAutoCompleteApi = null; 2737 2738 let _initialized = false; 2739 2740 //Initialize 2741 thisApi.Initialize = function () { 2742 2743 _logger.Log('initialize started'); 2744 2745 if (!_initialized) { 2746 2747 if (thisApi.SingleLineAddressTextBoxDomId != null) { 2748 2749 //LEGACY GOOGLE MAPS API 2750 2751 // 1. Guard against the API not being loaded at all 2752 if (typeof google != 'undefined' && google.maps && google.maps.places) { 2753 2754 // 2. Catch synchronous construction errors 2755 try { 2756 2757 _googleAutoCompleteApi = new google.maps.places.Autocomplete(thisApi.SingleLineAddressTextBox, { types: ['address'], fields: ['formatted_address', 'geometry', 'address_components'] }); 2758 2759 } 2760 catch (constructionError) 2761 { 2762 _handleGoogleMapsIntegrationFailure(constructionError.message); 2763 } 2764 2765 // 3. Wrap the listener callback 2766 _googleAutoCompleteApi.addListener('place_changed', 2767 function () { 2768 2769 try { 2770 _handleSingleLineAddressSelected(); 2771 } 2772 catch (handlerError) 2773 { 2774 _handleGoogleMapsIntegrationFailure(handlerError.message); 2775 } 2776 2777 }); 2778 2779 } 2780 else 2781 _handleGoogleMapsIntegrationFailure('Google Maps Places library did not load.'); 2782
2783 //LEGACY GOOGLE MAPS API 2784 2785 /* 2786 New Goggle Maps API 2787 _googleAutoCompleteApi = thisApi.PlaceAutocompleteElement; 2788 2789 _googleAutoCompleteApi.addEventListener('gmp-select', async (event) => { 2790 const place = event.placePrediction.toPlace(); 2791 2792 await place.fetchFields({ 2793 fields: ['formattedAddress', 'location', 'addressComponents', 'displayName'] 2794 }); 2795 2796 _handleSingleLineAddressSelected(place); 2797 2798 }); 2799 2800 //legacy fallback 2801 _googleAutoCompleteApi.addEventListener('gmp-placeselect', async (event) => { 2802 const place = event.placePrediction.toPlace(); 2803 console.log("Selected Place:", place); 2804 });*/ 2805 2806 _initialized = true; 2807 2808 } 2809 else 2810 _logger.Log('missing single line address field for postal code form field api'); 2811 2812 } 2813 else 2814 _logger.Log('skipping single line address form field api initialization (already initialized)'); 2815 2816 _fireInitializedEvent(); 2817 2818 }; 2819 2820 if (thisApi.AutoInitialize) { 2821 2822 document.addEventListener('DOMContentLoaded', function () { thisApi.Initialize(); }); 2823 2824 } 2825 2826 return thisApi; 2827 2828} 2829 2830function getPostalCodeLookupFormFieldApi(config) { 2831 2832 const thisApi = {}; 2833 2834 thisApi.AutoInitialize = (config.AutoInitialize != null) ? config.AutoInitialize : true; 2835 2836 //Two use cases: eitehr map dom elemeent and API will set values or leave them unnassigned and listen for the validation successed event to implement custom logic 2837 thisApi.PostalCodeEntryTextBoxDomId = config.PostalCodeEntryTextBoxDomId; 2838 thisApi.StateProvinceDropDownDomId = config.StateProvinceDropDownDomId; 2839 thisApi.CityTextBoxDomId = config.CityTextBoxDomId; 2840 thisApi.CountryDropDownDomId = config.CountryDropDownDomId; 2841 2842 const _logger = getConsoleLogApi({ "LogSourceName": 'CE Postal Code Form Field API' }); 2843 2844 const _getPostalCodeEntryTextBoxElement = function () { return document.getElementById(thisApi.PostalCodeEntryTextBoxDomId); }; 2845
2846 Object.defineProperty(thisApi, 'PostalCodeEntryTextBoxElement', { 2847 get: function () { 2848 return _getPostalCodeEntryTextBoxElement(); 2849 } 2850 }); 2851 2852 const _getStateProvinceDropDownElement = function () { return document.getElementById(thisApi.StateProvinceDropDownDomId); }; 2853 2854 Object.defineProperty(thisApi, 'StateProvinceDropDownElement', { 2855 get: function () { 2856 return _getStateProvinceDropDownElement(); 2857 } 2858 }); 2859 2860 const _getCityTextBoxElement = function () { return document.getElementById(thisApi.CityTextBoxDomId); }; 2861 2862 Object.defineProperty(thisApi, 'CityTextBoxElement', { 2863 get: function () { 2864 return _getCityTextBoxElement(); 2865 } 2866 }); 2867 2868 const _getCountryDropDownElement = function () { return document.getElementById(thisApi.CountryDropDownDomId); }; 2869 2870 Object.defineProperty(thisApi, 'CountryDropDownElement', { 2871 get: function () { 2872 return _getCountryDropDownElement(); 2873 } 2874 }); 2875 2876 thisApi.RegisterInitializedCallBack = function (callBackFunction) { 2877 CallBackManager.add('postalCodeLookupFormField_' + thisApi.PostalCodeEntryTextBoxDomId + '_initializedCallBack', callBackFunction, null); 2878 }; 2879 2880 const _fireInitializedEvent = function () { 2881 CallBackManager.fire('postalCodeLookupFormField_' + thisApi.PostalCodeEntryTextBoxDomId + '_initializedCallBack', {}, null); 2882 }; 2883 2884 thisApi.RegisterLookupSuccessfulCallBack = function (callBackFunction) { 2885 CallBackManager.add('postalCodeLookupFormField_' + thisApi.PostalCodeEntryTextBoxDomId + '_lookupSuccessfulCallBack', callBackFunction, null); 2886 }; 2887 2888 const _fireLookupSuccessfulEvent = function (result) { 2889 2890 let cb = {}; 2891 cb.Result = result; 2892 2893 CallBackManager.fire('postalCodeLookupFormField_' + thisApi.PostalCodeEntryTextBoxDomId + '_lookupSuccessfulCallBack', cb, null); 2894 2895 }; 2896 2897 thisApi.RegisterLookupFailedCallBack = function (callBackFunction) { 2898 CallBackManager.add('postalCodeLookupFormField_' + thisApi.PostalCodeEntryTextBoxDomId + '_lookupFailedCallBack', callBackFunction, null); 2899 }; 2900 2901 const _fireLookupFailedEvent = function () { 2902 CallBackManager.fire('postalCodeLookupFormField_' + thisApi.PostalCodeEntryTextBoxDomId + '_lookupFailedCallBack', {}, null); 2903 }; 2904 2905 const _lookupPostalCode = function (successCallBack, errorCallBack) { 2906 2907 let postalCode = thisApi.PostalCodeEntryTextBoxElement.getValue(); 2908 2909 LookupPostalCodeLocationInfo(postalCode, 2910 function (result) { 2911 2912 if (result.Success) { 2913 2914 if(thisApi.CityTextBoxElement != null && thisApi.CityTextBoxElement.getValue() != result.City) 2915 thisApi.CityTextBoxElement.setValue(result.City); 2916 2917 if (thisApi.StateProvinceDropDownElement != null && thisApi.StateProvinceDropDownElement.getValue() != result.State_Id) 2918 thisApi.StateProvinceDropDownElement.setValue(result.State_Id); 2919 2920 if (thisApi.CountryDropDownElement != null && thisApi.CountryDropDownElement.getValue() != result.Country_Id) 2921 thisApi.StateProvinceDropDownElement.setValue(result.Country_Id); 2922 2923 if (typeof successCallBack == 'function') 2924 successCallBack(); 2925 2926 _fireLookupSuccessfulEvent(result); 2927 2928 } 2929 else { 2930 2931 _logger.Log('error: ' + result.ErrorMessage); 2932 2933 if (typeof errorCallBack == 'function') 2934 errorCallBack(); 2935 2936 _fireLookupFailedEvent(); 2937 2938 } 2939 2940 }, 2941 function (error) { 2942 2943 _logger.Log('error: ' + error); 2944 2945 if (typeof errorCallBack == 'function') 2946 errorCallBack(); 2947 2948 _fireLookupFailedEvent(); 2949 2950 }); 2951 2952 }; 2953 2954 let _initialized = false; 2955 2956 //Initialize 2957 thisApi.Initialize = function () { 2958
2959 _logger.Log('initialize started (' + thisApi.PostalCodeEntryTextBoxDomId + ')'); 2960 2961 if (!_initialized) { 2962 2963 if (thisApi.PostalCodeEntryTextBoxElement != null) { // && thisApi.StateProvinceDropDownElement != null && thisApi.CityTextBoxElement 2964 2965 thisApi.PostalCodeEntryTextBoxElement.addEventListener('blur', function () { _lookupPostalCode() }); 2966 2967 _initialized = true; 2968 2969 } 2970 else 2971 _logger.Log('missing postal code entry field for postal code form field api (' + thisApi.PostalCodeEntryTextBoxDomId + ')'); 2972 2973 } 2974 else 2975 _logger.Log('skipping postal code form field api initialization (' + thisApi.PostalCodeEntryTextBoxDomId + ') (already initialized)'); 2976 2977 _fireInitializedEvent(); 2978 2979 }; 2980 2981 if (thisApi.AutoInitialize) { 2982 2983 document.addEventListener('DOMContentLoaded', function () { thisApi.Initialize(); }); 2984 2985 } 2986 2987 return thisApi; 2988 2989} 2990 2991function getPromoCodeFormFieldApi(config) { 2992 2993 const thisApi = {}; 2994 2995 thisApi.AutoInitialize = (config.AutoInitialize != null) ? config.AutoInitialize : true; 2996 2997 thisApi.PromoEntryTextBoxDomId = config.PromoEntryTextBoxDomId; 2998 2999 thisApi.GetTotalAmountFunction = config.GetTotalAmountFunction; 3000 thisApi.GetSelectedTicketQuantitiesFunction = config.GetSelectedTicketQuantitiesFunction; 3001 3002 thisApi.ValidatePromoButtonDomId = (config.ValidatePromoButtonDomId != null) ? config.ValidatePromoButtonDomId : null; 3003 3004 thisApi.Form_Id = (config.Form_Id != null) ? config.Form_Id : 0; 3005 3006 const _logger = getConsoleLogApi({ "LogSourceName": 'CE Promo Code Api' }); 3007 3008 thisApi.ValidateResponse = null; 3009 3010 thisApi.GetPromoEntryTextBoxElement = function () { return document.getElementById(thisApi.PromoEntryTextBoxDomId); }; 3011
3012 Object.defineProperty(thisApi, 'PromoEntryTextBoxElement', { 3013 get: function () { 3014 return thisApi.GetPromoEntryTextBoxElement(); 3015 } 3016 }); 3017 3018 thisApi.GetValidatePromoButtonElement = function () { return document.getElementById(thisApi.ValidatePromoButtonDomId); }; 3019 3020 Object.defineProperty(thisApi, 'ValidatePromoButtonElement', { 3021 get: function () { 3022 return thisApi.GetValidatePromoButtonElement(); 3023 } 3024 }); 3025 3026 thisApi.GetValidatePromoMessageContainerElement = function () { 3027 3028 let retVal = null; 3029 3030 if (thisApi.ValidatePromoButtonElement != null) 3031 retVal = thisApi.ValidatePromoButtonElement.parentElement.querySelector('.validatePromoCodeResultMessages'); 3032 3033 return retVal; 3034 3035 }; 3036 3037 Object.defineProperty(thisApi, 'ValidatePromoMessageContainerElement', { 3038 get: function () { 3039 return thisApi.GetValidatePromoMessageContainerElement(); 3040 } 3041 }); 3042 3043 const _clearMessages = function () { 3044 3045 if (thisApi.ValidatePromoMessageContainerElement != null) { 3046 thisApi.ValidatePromoMessageContainerElement.removeClassName('error'); 3047 thisApi.ValidatePromoMessageContainerElement.removeClassName('success'); 3048 thisApi.ValidatePromoMessageContainerElement.innerText = ''; 3049 thisApi.ValidatePromoMessageContainerElement.vanish(); 3050 } 3051 3052 }; 3053 3054 const _setSuccessMessage = function (message) { 3055 3056 _clearMessages(); 3057 3058 if (thisApi.ValidatePromoMessageContainerElement != null) { 3059 thisApi.ValidatePromoMessageContainerElement.addClassName('success'); 3060 thisApi.ValidatePromoMessageContainerElement.innerText = message; 3061 thisApi.ValidatePromoMessageContainerElement.show(); 3062 } 3063 3064 }; 3065 3066 const _setErrorMessage = function (message) { 3067 3068 _clearMessages(); 3069 3070 if (thisApi.ValidatePromoMessageContainerElement != null) { 3071 thisApi.ValidatePromoMessageContainerElement.addClassName('error'); 3072 thisApi.ValidatePromoMessageContainerElement.innerText = message; 3073 thisApi.ValidatePromoMessageContainerElement.show(); 3074 } 3075 3076 }; 3077 3078 thisApi.RegisterInitializedCallBack = function (callBackFunction) { 3079 CallBackManager.add('promo_' + thisApi.PromoEntryTextBoxDomId + '_initializedCallBack', callBackFunction, null); 3080 }; 3081 3082 const _fireInitializedEvent = function () { 3083 CallBackManager.fire('promo_' + thisApi.PromoEntryTextBoxDomId + '_initializedCallBack', {}, null); 3084 }; 3085 3086 thisApi.RegisterValidationSuccessfulChangedCallBack = function (callBackFunction) { 3087 CallBackManager.add('promo_' + thisApi.PromoEntryTextBoxDomId + '_validationSuccessfulCallBack', callBackFunction, null); 3088 }; 3089 3090 const _fireValidationSuccessfulEvent = function () { 3091 CallBackManager.fire('promo_' + thisApi.PromoEntryTextBoxDomId + '_validationSuccessfulCallBack', {}, null); 3092 }; 3093 3094 thisApi.RegisterValidationFailedChangedCallBack = function (callBackFunction) { 3095 CallBackManager.add('promo_' + thisApi.PromoEntryTextBoxDomId + '_validationFailedCallBack', callBackFunction, null); 3096 }; 3097 3098 const _fireValidationFailedEvent = function () { 3099 CallBackManager.fire('promo_' + thisApi.PromoEntryTextBoxDomId + '_validationFailedCallBack', {}, null); 3100 }; 3101 3102 thisApi.ValidateResponse = null; 3103 3104 thisApi.GetDiscountAmount = function () { 3105 3106 let retVal = 0; 3107 3108 if (thisApi.ValidateResponse != null && thisApi.ValidateResponse.Success) 3109 retVal = thisApi.ValidateResponse.DiscountAmount; 3110 3111 return retVal; 3112 3113 }; 3114 3115 thisApi.GetTicketsDiscountAmount = function () { 3116 3117 let retVal = 0; 3118 3119 if (thisApi.ValidateResponse != null && thisApi.ValidateResponse.Success) 3120 retVal = thisApi.ValidateResponse.TicketsAppliedDiscount; 3121 3122 return retVal; 3123 3124 }; 3125 3126 thisApi.GetNonTicketDiscountAmount = function () { 3127 3128 let retVal = 0; 3129 3130 if (thisApi.ValidateResponse != null && thisApi.ValidateResponse.Success) 3131 retVal = thisApi.ValidateResponse.NonTicketAppliedDiscount; 3132 3133 return retVal; 3134 3135 }; 3136 3137 thisApi.Validate = function (successCallBack, errorCallBack) { _validateCode(successCallBack, errorCallBack); }; 3138 3139 const _validateCode = function (successCallBack, errorCallBack) { 3140 3141 let promoCode = thisApi.PromoEntryTextBoxElement.getValue(); 3142 let amt = thisApi.GetTotalAmountFunction(); 3143 let tix = thisApi.GetSelectedTicketQuantitiesFunction(); 3144 3145 _clearMessages();
3146 3147 thisApi.PromoEntryTextBoxElement.validationSucceeded(); 3148 3149 ValidatePromoCode(promoCode, amt, tix, thisApi.Form_Id, 3150 function (result) { 3151 3152 thisApi.ValidateResponse = result; 3153 3154 if (result.Success) 3155 _setSuccessMessage('promo code applied'); 3156 else 3157 { 3158 thisApi.PromoEntryTextBoxElement.validationFailed(); 3159 _setErrorMessage(result.ErrorMessage); 3160 } 3161 3162 if (typeof successCallBack == 'function') 3163 successCallBack(); 3164 3165 _fireValidationSuccessfulEvent(); 3166 3167 }, 3168 function (error) { 3169 3170 _logger.Log('error: ' + error); 3171 3172 thisApi.PromoEntryTextBoxElement.validationFailed(); 3173 3174 if (typeof errorCallBack == 'function') 3175 errorCallBack(); 3176 3177 _fireValidationFailedEvent(); 3178 3179 }); 3180 3181 }; 3182 3183 let _initialized = false; 3184 3185 //Initialize 3186 thisApi.Initialize = function () { 3187 3188 _logger.Log('initialize started (' + thisApi.PromoEntryTextBoxDomId + ')'); 3189 3190 if (!_initialized) { 3191 3192 if (thisApi.PromoEntryTextBoxElement != null) { 3193 3194 if (thisApi.ValidatePromoButtonElement == null) { 3195 3196 thisApi.PromoEntryTextBoxElement.setAttribute('placeholder','enter promo code'); 3197 3198 thisApi.ValidatePromoButtonDomId = thisApi.PromoEntryTextBoxDomId + '_validateBtn'; 3199 3200 let btn = document.createElement('button'); 3201 btn.setAttribute('type', 'button'); 3202 btn.id = thisApi.ValidatePromoButtonDomId; 3203 btn.innerHTML = 'apply'; 3204 btn.addEventListener('click', function () { _validateCode() }); 3205 3206 thisApi.PromoEntryTextBoxElement.insertAdjacentElement('afterend', btn); 3207 3208 let messCont = document.createElement('span'); 3209 messCont.addClassName('validatePromoCodeResultMessages'); 3210 3211 messCont.vanish(); 3212 3213 btn.insertAdjacentElement('afterend', messCont); 3214 3215 } 3216 3217 _initialized = true; 3218 3219 } 3220 else 3221 _logger.Log('invalid text box element for promo code form field api (' + thisApi.PromoEntryTextBoxDomId + ')'); 3222 3223 } 3224 else 3225 _logger.Log('skipping promo code form field api initialization (' + thisApi.PromoEntryTextBoxDomId + ') (already initialized)'); 3226 3227 _fireInitializedEvent(); 3228 3229 }; 3230 3231 if (thisApi.AutoInitialize) { 3232 3233 document.addEventListener('DOMContentLoaded', function () { thisApi.Initialize(); }); 3234 3235 } 3236 3237 return thisApi; 3238 3239} 3240 3241function getLiveActivityStreamWidgetApi(config) { 3242 3243 const thisApi = {}; 3244 3245 thisApi.MainContainerElementDomId = config.MainContainerElementDomId; 3246 3247 if(config.Title != null) 3248 thisApi.Title = config.Title; 3249 3250 thisApi.MaxItems = 50; 3251 3252 if (config != null && config.MaxItems > 0) 3253 thisApi.MaxItems = config.MaxItems; 3254 3255 const _logger = getConsoleLogApi({ "LogSourceName": 'CE Live Activity Stream Widget Api' }); 3256 3257 const _getMainContainerElement = function () { return document.getElementById(thisApi.MainContainerElementDomId); }; 3258 3259 const _seenIds = {}; 3260 3261 const _avatarMap = {}; 3262 3263 const _avatars = [ 3264 { bg: '#e1f5ee', text: '#0f6e56' }, 3265 { bg: '#e6f1fb', text: '#185fa5' }, 3266 { bg: '#eeedfe', text: '#3c3489' }, 3267 { bg: '#faeeda', text: '#854f0b' }, 3268 { bg: '#faece7', text: '#993c1d' }, 3269 { bg: '#fbeaf0', text: '#993556' }, 3270 ]; 3271 3272 const _avatarFor = function (name) { 3273 3274 if (!_avatarMap[name]) { 3275 var i = Object.keys(_avatarMap).length % _avatars.length; 3276 _avatarMap[name] = _avatars[i]; 3277 } 3278 3279 return _avatarMap[name]; 3280 3281 }; 3282 3283 const _initials = function(name) { 3284 const parts = name.trim().split(/\s+/); 3285 return parts.length >= 2 ? parts[0][0] + parts[parts.length - 1][0] : name.slice(0, 2); 3286 } 3287 3288 const _escHtml = function (s) { 3289 return String(s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"'); 3290 }; 3291 3292 const _timeLabel = function () { 3293 const now = new Date(); 3294 const h = now.getHours(), m = now.getMinutes(); 3295 const ampm = h >= 12 ? 'pm' : 'am'; 3296 h = h % 12 || 12; 3297 return 'Today at ' + h + ':' + (m < 10 ? '0' : '') + m + ' ' + ampm; 3298 }; 3299 3300 const _addFeedItem = function(item) { 3301 3302 if (item.id) { 3303 if (_seenIds[item.id]) return; 3304 _seenIds[item.id] = true; 3305 } 3306 3307 const list = document.getElementById('liveActivityFeedPanelListContainer'); 3308 const empty = document.getElementById('liveActivityFeedPanelListFeedEmptyEntry'); 3309 if (empty) empty.remove(); 3310 3311 const av = _avatarFor(item.name); 3312 const li = document.createElement('li'); 3313 li.className = 'feed-item'; 3314 3315 const amountHtml = item.amount 3316 ? ' <span class="feed-amount">
3316$' + Number(item.amount).toLocaleString() + '</span>' 3317 : ''; 3318 3319 li.innerHTML = 3320 '<div class="feed-avatar" style="background:' + av.bg + ';color:' + av.text + '">' + 3321 _initials(item.name) + 3322 '</div>' + 3323 '<div class="feed-body">' + 3324 '<div class="feed-text"><strong>' + _escHtml(item.name) + '</strong> ' + _escHtml(item.text) + amountHtml + '</div>' + 3325 '<div class="feed-time">' + _escHtml(item.time || _timeLabel()) + '</div>' + 3326 '</div>'; 3327 3328 list.insertBefore(li, list.firstChild); 3329 3330 const items = list.querySelectorAll('.feed-item'); 3331 if (items.length > thisApi.MaxItems) 3332 list.removeChild(items[items.length - 1]); 3333 3334 } 3335 3336 thisApi.AddItem = function(item){ _addFeedItem(item); }; 3337 3338 thisApi.RegisterInitializedCallBack = function (callBackFunction) { 3339 CallBackManager.add('liveActivityStreamWidgetApiInitializedCallBack', callBackFunction, null); 3340 }; 3341 3342 let _fireInitializedEvent = function () { 3343 CallBackManager.fire('liveActivityStreamWidgetApiInitializedCallBack', {}, null); 3344 }; 3345 3346 //Initialize 3347 thisApi.Initialize = function () { 3348 3349 if (document.getElementById('liveActivityFeedPanel') != null) { 3350 _logger.Log('initialize completed (abort)'); 3351 return; 3352 } 3353 3354 _logger.Log('initialize started'); 3355 3356 const mainContainerElement = _getMainContainerElement(); 3357 if (mainContainerElement != null) { 3358 3359 const widgetContainerElem = document.createElement('div'); 3360 widgetContainerElem.id = 'liveActivityFeedPanel'; 3361 3362 const headerElem = document.createElement('div'); 3363 headerElem.id = 'liveActivityFeedPanelHeader'; 3364 3365 const headerTitleElem = document.createElement('span'); 3366 headerTitleElem.id = 'liveActivityFeedPanelTitle'; 3367 headerTitleElem.innerHTML = thisApi.Title; 3368 3369 headerElem.appendChild(headerTitleElem); 3370 3371 const headerLiveElem = document.createElement('span'); 3372 headerLiveElem.id = 'liveActivityFeedPanelTopRightLiveCallOut'; 3373 3374 const headerDotElem = document.createElement('span'); 3375 headerDotElem.id = 'liveActivityFeedPanelTopRightLiveDot'; 3376 headerLiveElem.appendChild(headerDotElem); 3377 3378 headerLiveElem.appendChild(document.createTextNode(' Live')); 3379 3380 headerElem.appendChild(headerLiveElem); 3381 3382 widgetContainerElem.appendChild(headerElem); 3383 3384 const feedContainerElem = document.createElement('div'); 3385 feedContainerElem.id = 'liveActivityFeedPanelListContainer'; 3386 3387 const waitContElem = document.createElement('li'); 3388 waitContElem.id = 'liveActivityFeedPanelListFeedEmptyEntry'; 3389 waitContElem.innerHTML = 'Waiting for activity...'; 3390 3391 feedContainerElem.appendChild(waitContElem); 3392 3393 widgetContainerElem.appendChild(feedContainerElem); 3394 3395 mainContainerElement.appendChild(widgetContainerElem); 3396 3397 _fireInitializedEvent(); 3398 3399 } 3400 else 3401 _logger.Log('unable to initialize Live Activity Stream Widget becuase no container is available'); 3402 3403 }; 3404 3405 return thisApi; 3406 3407} 3408 3409function getThermometerWidgetApi(config) { 3410 3411 const thisApi = {}; 3412 3413 thisApi.MainContainerElementDomId = config.MainContainerElementDomId; 3414 3415 if(config.Title != null) 3416 thisApi.Title = config.Title; 3417 3418 if(config.SubTitle != null) 3419 thisApi.SubTitle = config.SubTitle; 3420 3421 let _meterFillHexColor = '7DC242'; //TODO: allow configuration 3422 3423 let _goalRevenue = 0; 3424 let _amountRaised = 0; 3425 3426 const _logger = getConsoleLogApi({ "LogSourceName": 'CE Thermometer Widget Api' }); 3427 3428 const _getMainContainerElement = function () { return document.getElementById(thisApi.MainContainerElementDomId); }; 3429 3430 const _refreshTitle = function () { 3431 3432 const titleElem = document.getElementById('thermometerPanelTitle'); 3433 if (titleElem != null) { 3434
3435 titleElem.innerHTML = thisApi.Title; 3436 3437 //always show for now due to page layout - TBD 3438 //if (!isBlank(thisApi.Title)) 3439 // titleElem.show(); 3440 //else 3441 // titleElem.vanish(); 3442 3443 } 3444 3445 }; 3446 3447 thisApi.SetTitle = function (value) { thisApi.Title = value; _refreshTitle(); }; 3448 3449 const _refreshSubTitle = function () { 3450 3451 const subTitleElem = document.getElementById('thermometerPanelSubTitle'); 3452 if (subTitleElem != null) { 3453 3454 subTitleElem.innerHTML = thisApi.SubTitle; 3455 3456 //always show for now due to page layout - TBD 3457 //if (!isBlank(thisApi.SubTitle)) 3458 // subTitleElem.show(); 3459 //else 3460 // subTitleElem.vanish(); 3461 3462 } 3463 3464 }; 3465 3466 thisApi.SetSubTitle = function (value) { thisApi.SubTitle = value; _refreshSubTitle(); }; 3467 3468 thisApi.SetGoal = function (amt,suppressRefresh) { _goalRevenue = amt; if(!suppressRefresh) _refreshThermometerSvg(); }; 3469 3470 thisApi.SetAmount = function (amt,suppressRefresh) { _amountRaised = amt; if(!suppressRefresh) _refreshThermometerSvg(); }; 3471 3472 const _TX = 31; 3473 const _TW = 28; 3474 const _TT = 20; 3475 const _TH = 280; 3476 const _TB = 300; 3477 const _BCY = 332; 3478 const _BR = 24; 3479 3480 const _fmt = function (n) { 3481 if (n >= 1000000) return '$' + (n / 1000000).toFixed(n % 1000000 === 0 ? 0 : 1) + 'M'; 3482 if (n >= 10000) return '$' + (n / 1000).toFixed(n % 1000 === 0 ? 0 : 1) + 'K'; 3483 return '$' + Math.round(n).toLocaleString(); 3484 }; 3485 3486 const _fmtFull = function (n) { return '$' + Math.round(n).toLocaleString(); }; 3487 3488 //revisit 3489 //const _generateMeterColorByPercent = function (p) { 3490 // if (p >= 1.0) return '#1D9E75'; 3491 // if (p >= 0.75) return '#378ADD'; 3492 // if (p >= 0.50) return '#7F77DD'; 3493 // if (p >= 0.25) return '#BA7517'; 3494 // return '#D85A30'; 3495 //}; 3496 3497 function _refreshThermometerSvg() { 3498 3499 const goal = _goalRevenue; 3500 const raised = _amountRaised; 3501 3502 const pct = goal > 0 ? Math.min(raised / goal, 1) : 0; 3503 3504 const col = '#' + _meterFillHexColor; // _generateMeterColorByPercent(pct); 3505 3506 const pi = Math.round(pct * 100); 3507 3508 document.getElementById('thermometerTopStatRaisedAmount').textContent = _fmtFull(raised); 3509 document.getElementById('thermometerTopStatGoalAmount').textContent = _fmtFull(goal); 3510 document.getElementById('thermometerTopStatRemainingAmount').textContent = _fmtFull(Math.max(goal - raised, 0)); 3511 3512 const footCallOut = document.getElementById('thermometerFooter'); 3513 footCallOut.textContent = pct >= 1 ? '🎉 Goal reached!' : pi + '% of goal reached'; 3514 footCallOut.className = (pct >= 1 ? 'done' : ''); 3515 3516 const fh = pct * _TH; 3517 const fy = _TB - fh; 3518 3519 let svg = '<defs><clipPath id="tc"><rect x="' + _TX + '" y="' + _TT + '" width="' + _TW + '" height="' + _TH + '" rx="14"/></clipPath></defs>'; 3520 svg += '<rect x="' + _TX + '" y="' + _TT + '" width="' + _TW + '" height="' + _TH + '" rx="14" fill="#f0efe9" stroke="#d8d7d0" stroke-width="1"/>'; 3521 svg += '<circle cx="45" cy="' + _BCY + '" r="' + _BR + '" fill="#f0efe9" stroke="#d8d7d0" stroke-width="1"/>'; 3522 3523 if (fh > 0) 3524 svg += '<rect x="' + _TX + '" y="' + fy + '" width="' + _TW + '" height="' + (fh + 2) + '" clip-path="url(#tc)" fill="' + col + '"/>'; 3525 3526 svg += '<circle cx="45" cy="' + _BCY + '" r="' + (_BR - 5) + '" fill="' + col + '"/>'; 3527 3528 [0.25, 0.5, 0.75, 1.0].forEach(function (m) { 3529 3530 const my = _TB - m * _TH; 3531 const hit = pct >= m - 0.001; 3532 const full = m === 1.0; 3533 3534 svg += '<line x1="' + (_TX - 4) + '" y1="' + my + '" x2="' + (_TX + _TW + 4) + '" y2="' + my + '" stroke="' + (hit ? col : '#ccc') + '" stroke-width="' + (full ? 2 : 1) + '" stroke-dasharray="' + (full ? '' : '3,2') + '"/>'; 3535 svg += '<text x="' + (_TX + _TW + 8) + '" y="' + (my + 4) + '" font-size="9" font-family="monospace" fill="' + (hit ? col : '#aaa') + '">' + Math.round(m * 100) + '%</text>'; 3536 3537 }); 3538 3539 if (pct > 0.07) 3540 svg += '<text x="45" y="' + Math.max(fy + 14, _TT + 14) + '" text-anchor="middle" font-size="8" font-family="monospace" font-weight="500" fill="white">' + pi + '%</text>'; 3541 3542 document.getElementById('thermometer').innerHTML = svg; 3543 3544 const labelsEl = document.getElementById('thermometerLeftLabelsContainer'); 3545 labelsEl.innerHTML = '';
3546 [goal, goal * 0.75, goal * 0.5, goal * 0.25, 0].forEach(function (v) { 3547 const d = document.createElement('div'); 3548 d.className = 'thermo-label'; 3549 d.textContent = _fmt(Math.round(v)); 3550 labelsEl.appendChild(d); 3551 }); 3552 3553 //hide goal dependent UI 3554 if (goal > 0) { 3555 3556 document.getElementById('thermometerTopStatGoalAmount').parentElement.show(); 3557 document.getElementById('thermometerTopStatRemainingAmount').parentElement.show(); 3558 document.getElementById('thermometerContainer').show(); 3559 document.getElementById('thermometerFooter').show(); 3560 3561 } 3562 else { 3563 3564 document.getElementById('thermometerTopStatGoalAmount').parentElement.vanish(); 3565 document.getElementById('thermometerTopStatRemainingAmount').parentElement.vanish(); 3566 document.getElementById('thermometerContainer').vanish(); 3567 document.getElementById('thermometerFooter').vanish(); 3568 3569 } 3570 3571 } 3572 3573 thisApi.Reload = function () { _refreshThermometerSvg(); }; 3574 3575 thisApi.RegisterInitializedCallBack = function (callBackFunction) { 3576 CallBackManager.add('thermometerWidgetApiInitializedCallBack', callBackFunction, null); 3577 }; 3578 3579 let _fireInitializedEvent = function () { 3580 CallBackManager.fire('thermometerWidgetApiInitializedCallBack', {}, null); 3581 }; 3582 3583 const _addTopKpiElem = function (containerElem, domId, label) { 3584 3585 const kpiContainerElem = document.createElement('div'); 3586 kpiContainerElem.addClassName('thermo-top-stat'); 3587 3588 const lblElem = document.createElement('div'); 3589 lblElem.addClassName('thermo-top-stat-lbl'); 3590 lblElem.innerHTML = label //todo: make configurable 3591 3592 kpiContainerElem.appendChild(lblElem); 3593 3594 const valElem = document.createElement('div'); 3595 valElem.id = domId; 3596 valElem.addClassName('thermo-top-stat-val'); 3597 3598 kpiContainerElem.appendChild(valElem); 3599 3600 containerElem.appendChild(kpiContainerElem); 3601 3602 }; 3603 3604 //Initialize 3605 thisApi.Initialize = function () { 3606 3607 if (document.getElementById('thermometerPanel') != null) { 3608 _logger.Log('initialize completed (abort)'); 3609 return; 3610 } 3611 3612 _logger.Log('initialize started'); 3613 3614 const mainContainerElement = _getMainContainerElement(); 3615 if (mainContainerElement != null) { 3616 3617 const thermContainerElem = document.createElement('div'); 3618 thermContainerElem.id = 'thermometerPanel'; 3619 3620 const titleElem = document.createElement('div'); 3621 titleElem.id = 'thermometerPanelTitle'; 3622 titleElem.innerHTML = '...'; 3623 3624 thermContainerElem.appendChild(titleElem); 3625 3626 const subTitleElem = document.createElement('div'); 3627 subTitleElem.id = 'thermometerPanelSubTitle'; 3628 subTitleElem.innerHTML = '...'; 3629 3630 thermContainerElem.appendChild(subTitleElem); 3631 3632 const topStatsContainerElem = document.createElement('div'); 3633 topStatsContainerElem.id = 'thermometerPanelTopStatsContainer'; 3634 3635 _addTopKpiElem(topStatsContainerElem, 'thermometerTopStatRaisedAmount', 'Raised'); //todo: make label configurable 3636 _addTopKpiElem(topStatsContainerElem, 'thermometerTopStatGoalAmount', 'Goal'); //todo: make label configurable 3637 _addTopKpiElem(topStatsContainerElem, 'thermometerTopStatRemainingAmount', 'Remaining'); //todo: make label configurable 3638 3639 thermContainerElem.appendChild(topStatsContainerElem); 3640 3641 const thermometerContainerElem = document.createElement('div'); 3642 thermometerContainerElem.id = 'thermometerContainer'; 3643 3644 const termLeftContElem = document.createElement('div');
3645 termLeftContElem.id = 'thermometerLeftLabelsContainer'; 3646 thermometerContainerElem.appendChild(termLeftContElem); 3647 3648 const svgElem = createSvgElement(); 3649 svgElem.id = 'thermometer'; 3650 svgElem.setAttribute('width', '90'); 3651 svgElem.setAttribute('height', '360'); 3652 svgElem.setAttribute('viewBox', '0 0 90 360'); 3653 //svgElem.setAttribute('xmlns', 'http://www.w3.org/2000/svg'); 3654 thermometerContainerElem.appendChild(svgElem); 3655 3656 thermContainerElem.appendChild(thermometerContainerElem); 3657 3658 const thermometerFooterElem = document.createElement('div'); 3659 thermometerFooterElem.id = 'thermometerFooter'; 3660 3661 thermContainerElem.appendChild(thermometerFooterElem); 3662 3663 mainContainerElement.appendChild(thermContainerElem); 3664 3665 //start init UI api calls 3666 thisApi.SetTitle(thisApi.Title); 3667 thisApi.SetSubTitle(thisApi.SubTitle); 3668 3669 if(config.Goal != null && config.Goal > 0) 3670 thisApi.SetGoal(config.Goal, true); 3671 3672 if(config.Amount != null && config.Amount > 0) 3673 thisApi.SetAmount(config.Amount, true); 3674 3675 _refreshThermometerSvg(); 3676 3677 _fireInitializedEvent(); 3678 3679 } 3680 else 3681 _logger.Log('unable to initialize Thermometer Widget Api becuase no container is available'); 3682 3683 }; 3684 3685 return thisApi; 3686 3687} 3688 3689function getRequestValidationApi(config) { 3690 3691 const thisApi = {}; 3692 3693 thisApi.CurrentRequestToken = config.CurrentRequestToken; 3694 3695 thisApi.HeaderKeyName = config.HeaderKeyName; 3696 thisApi.FormHiddenFieldId = config.FormHiddenFieldId; 3697 thisApi.HoneypotHeaderKeyName = config.HoneypotHeaderKeyName; 3698 thisApi.HoneypotFormHiddenFieldId = config.HoneypotFormHiddenFieldId; 3699 3700 let _refreshProcessing = false; 3701 3702 const _logger = getConsoleLogApi({ "LogSourceName": 'CE Request Validation Api' }); 3703 3704 const _refreshToken = function (successCallBack, errorCallBack) { 3705 3706 let isTabVisible = true; 3707 3708 //extra defensive in case older browsers do not support 3709 try { 3710 isTabVisible = (document.visibilityState == "visible"); 3711 } 3712 catch (err) { 3713 _logger.Log('check tab visiblity failed: ' + err); 3714 } 3715 3716 if (isTabVisible) { 3717 3718 _logger.Log('refresh token called'); 3719 3720 if (!_refreshProcessing) { 3721 3722 _refreshProcessing = true; 3723 3724 //showProcessingMessage(); //removed (6.4.2026) 3725 3726 try { 3727 3728 getUrlWithSessionAsync('/api/PublicWebAppUIAjaxEndpoint/GenerateRequestToken', 3729 function (result) { 3730 3731 //hideProcessingMessage(); //removed (6.4.2026) 3732 3733 _setToken(result); 3734 3735 _refreshProcessing = false; 3736 3737 if (typeof successCallBack == 'function') 3738 successCallBack(); 3739 3740 }, 3741 function (error) { 3742 3743 //hideProcessingMessage(); //removed (6.4.2026) 3744 3745 _logger.Log('error: ' + error); 3746 3747 _refreshProcessing = false; 3748 3749 if (typeof errorCallBack == 'function') 3750 errorCallBack(); 3751 3752 }); 3753 3754 } 3755 catch (err) { 3756 3757 _logger.Log('request validation api unable to refresh request token (check endpoint and client APIs): ' + err); 3758 3759 _refreshProcessing = false; 3760 3761 if (typeof errorCallBack == 'function') 3762 errorCallBack(); 3763 3764 } 3765 3766 } 3767 else 3768 { 3769 3770 _logger.Log('refresh token request skipped due to active refresh processing'); 3771 3772 if (typeof errorCallBack == 'function') 3773 errorCallBack(); 3774 3775 } 3776 3777 } 3778 else 3779 { 3780
3781 _logger.Log('skipping refresh becuase tab is not visible'); 3782 3783 if (typeof errorCallBack == 'function') 3784 errorCallBack(); 3785 3786 } 3787 3788 }; 3789 3790 thisApi.RefreshToken = function (successCallBack, errorCallBack) { _refreshToken(successCallBack, errorCallBack); }; 3791 3792 const _setToken = function (token) { 3793 3794 thisApi.CurrentRequestToken = token; 3795 3796 let currentHiddenField = document.getElementById(thisApi.FormHiddenFieldId); 3797 if (currentHiddenField != null) 3798 currentHiddenField.setValue(token); 3799 3800 _logger.Log('request token updated'); // to ' + token); 3801 3802 }; 3803 3804 const _getHoneyPotValue = function () { 3805 3806 let retVal = ''; 3807 3808 let currentHiddenField = document.getElementById(thisApi.HoneypotFormHiddenFieldId); 3809 if (currentHiddenField != null) 3810 retVal = currentHiddenField.value; 3811 3812 return retVal; 3813 3814 }; 3815 3816 thisApi.GetHoneyPotValue = function () { return _getHoneyPotValue(); }; 3817 3818 //Initialize 3819 thisApi.Initialize = function () { 3820 3821 _logger.Log('initialize started'); 3822 3823 //find form to append hidden field to 3824 let formElem = getDotNetPostbackFormOrFirstFormElement(); 3825 3826 //inject hidden field into DOM for form post based behavior 3827 let currentHiddenField = document.getElementById(thisApi.FormHiddenFieldId); 3828 if (currentHiddenField == null && formElem != null) { 3829 3830 //base validation field 3831 let hf = document.createElement('input'); 3832 hf.setAttribute('type', 'hidden'); 3833 hf.setAttribute('id', thisApi.FormHiddenFieldId); 3834 hf.setAttribute('name', thisApi.FormHiddenFieldId); 3835 hf.setValue(thisApi.CurrentRequestToken); 3836 3837 formElem.appendChild(hf); 3838 3839 } 3840 3841 //add honeypot field 3842 let hpFieldElem = document.getElementById(thisApi.HoneypotFormHiddenFieldId); 3843 if (hpFieldElem == null) { 3844 3845 //browsers ignore autocomplete no for some fields. In order to stop the browser from auto-populating our honeypot field, create a second field for the browser to populate instead 3846 let hp_hf_un = document.createElement('input'); 3847 hp_hf_un.setAttribute('type', 'text'); 3848 hp_hf_un.setAttribute('id', 'ahp_username'); 3849 hp_hf_un.setAttribute('name', 'ahp_username'); 3850 hp_hf_un.setAttribute('class', 'coreContactUserNameEntryFields'); 3851 hp_hf_un.setAttribute('tabindex', '-1'); 3852 hp_hf_un.setAttribute('autocomplete', 'yes'); 3853 hp_hf_un.setAttribute('style', 'display:none;'); 3854 3855 //always add honeypot, even if there is no dot net form 3856 if (formElem != null) 3857 formElem.appendChild(hp_hf_un); 3858 else 3859 document.body.appendChild(hp_hf_un); 3860 3861 let hp_hf = document.createElement('input'); 3862 hp_hf.setAttribute('type', 'text'); 3863 hp_hf.setAttribute('id', thisApi.HoneypotFormHiddenFieldId); 3864 hp_hf.setAttribute('name', thisApi.HoneypotFormHiddenFieldId); 3865 hp_hf.setAttribute('class', 'coreContactEmailEntryFields'); 3866 hp_hf.setAttribute('tabindex', '-1'); 3867 hp_hf.setAttribute('autocomplete', 'off'); 3868 hp_hf.setAttribute('style', 'display:none;'); 3869 3870 //always add honeypot, even if there is no dot net form 3871 if (formElem != null) 3872 formElem.appendChild(hp_hf); 3873 else 3874 document.body.appendChild(hp_hf); 3875 3876 } 3877 3878 //ajax behavior will rely on headers 3879 3880 // Refresh token every 5 minutes 3881 setInterval(function () { _refreshToken(); }, (300 * 1000)); 3882 3883 // Add the visibility listener 3884 try { 3885 3886 document.addEventListener('visibilitychange', function () { 3887 if (document.visibilityState === 'visible') { 3888 _logger.Log('Tab became visible, refreshing token immediately...'); 3889 _refreshToken(); 3890 } 3891 }); 3892 3893 } catch (err) {
3894 _logger.Log('visibility listener failed: ' + err); 3895 } 3896 3897 }; 3898 3899 return thisApi; 3900 3901} 3902 3903function getGoogleCaptchaApi(config) { 3904 3905 const thisApi = {}; 3906 3907 thisApi.GoogleCaptchaApiKey = config.GoogleCaptchaApiKey; 3908 thisApi.GoogleCaptchaAvailable = false; 3909 3910 thisApi.HeaderKeyName = config.HeaderKeyName; 3911 thisApi.FormHiddenFieldId = config.FormHiddenFieldId; 3912 3913 thisApi.CurrentCaptchaToken = ''; 3914 3915 const _logger = getConsoleLogApi({ "LogSourceName": 'CE Captcha Api' }); 3916 3917 //Events 3918 thisApi.RegisterInitializedCallBack = function (callBackFunction) { 3919 CallBackManager.add('googleCaptchaInitializedCallBack', callBackFunction, null); 3920 }; 3921 3922 const _fireInitializedEvent = function () { 3923 CallBackManager.fire('googleCaptchaInitializedCallBack', {}, null); 3924 }; 3925 3926 let _captchaRefreshing = false; 3927 3928 const _refreshCaptchaToken = function (successCallBack, errorCallBack) { 3929 3930 if (thisApi.GoogleCaptchaAvailable) { 3931 3932 if (!_captchaRefreshing) { 3933 3934 _captchaRefreshing = true; 3935 3936 grecaptcha.execute(thisApi.GoogleCaptchaApiKey, { action: "submit" }).then(function (token) { 3937 3938 _logger.Log('captcha token refreshed'); //: ' + token); 3939 3940 _captchaRefreshing = false; 3941 3942 thisApi.CurrentCaptchaToken = token; 3943 3944 let currentHiddenField = document.getElementById(thisApi.FormHiddenFieldId); 3945 if (currentHiddenField != null) 3946 currentHiddenField.setValue(token); 3947 3948 if (typeof successCallBack == 'function') 3949 successCallBack(); 3950 3951 }).catch(function (error) { 3952 3953 console.error('error refreshing CAPTCHA:', error); 3954 3955 _captchaRefreshing = false; 3956 3957 if (typeof errorCallBack == 'function') 3958 errorCallBack(); 3959 3960 }); 3961 3962 //not supported by google api 3963 //.finally(function () { 3964 // 3965 // _logger.Log('CAPTCHA token request finished.'); // This will run no matter what (whether success or failure) 3966 // 3967 // _captchaRefreshing = false; 3968 // 3969 // if (typeof anyCallBack == 'function') 3970 // anyCallBack(); 3971 // 3972 //}); 3973 3974 } 3975 else { 3976 3977 _logger.Log('CAPTCHA token request skipped (already running)'); 3978 3979 if (typeof errorCallBack == 'function') 3980 errorCallBack(); 3981 3982 } 3983 3984 } 3985 else 3986 { 3987 3988 _logger.Log('cannot refresh Google Captcha token becuase API is not available'); 3989 3990 if (typeof errorCallBack == 'function') 3991 errorCallBack(); 3992 3993 } 3994 3995 }; 3996 3997 thisApi.RefreshCaptchaToken = function (successCallBack, errorCallBack) { _refreshCaptchaToken(successCallBack, errorCallBack); }; 3998 3999 thisApi.HidePageIcon = function () { 4000 4001 const pgIconElem = document.querySelector('.grecaptcha-badge'); 4002 if (pgIconElem != null) 4003 pgIconElem.vanish(); 4004 4005 }; 4006 4007 //Initialize 4008 thisApi.Initialize = function () { 4009 4010 _logger.Log('initialize started'); 4011
4012 //_logger.Log('try init captcha: ' + thisApi.GoogleCaptchaApiKey); 4013 if (!isBlank(thisApi.GoogleCaptchaApiKey)) { 4014 4015 if (grecaptcha) { 4016 4017 thisApi.GoogleCaptchaAvailable = true; 4018 4019 //inject hidden field into DOM for form post based behavior 4020 let currentHiddenField = document.getElementById(thisApi.FormHiddenFieldId); 4021 if (currentHiddenField == null) { 4022 4023 //find form to append hidden field to 4024 let formElem = getDotNetPostbackFormOrFirstFormElement(); 4025 4026 if (formElem != null) { 4027 4028 //base validation field 4029 let hf = document.createElement('input'); 4030 hf.setAttribute('type', 'hidden'); 4031 hf.setAttribute('id', thisApi.FormHiddenFieldId); 4032 hf.setAttribute('name', thisApi.FormHiddenFieldId); 4033 //hf.setValue(''); 4034 4035 formElem.appendChild(hf); 4036 4037 } 4038 4039 } 4040 4041 // Refresh token every 90 seconds (tokens expire every 2 mintutes) 4042 setInterval(function () { _refreshCaptchaToken(); }, (90 * 1000)); 4043 4044 _refreshCaptchaToken(function () { _fireInitializedEvent(); }, function () { _fireInitializedEvent(); }); 4045 4046 } 4047 else 4048 _logger.Log('unable to initialize Google Captcha API becuase no API key available'); 4049 4050 } 4051 else 4052 _logger.Log('unable to initialize Google Captcha API becuase no API key available'); 4053 4054 }; 4055 4056 return thisApi; 4057 4058} 4059 4060function getOneClickCheckOutApi(config) { 4061 4062 const thisApi = {}; 4063 4064 thisApi.AutoInitialize = (config.AutoInitialize != null) ? config.AutoInitialize : true; 4065 4066 thisApi.ParentElementDomId = config.ParentElementDomId; 4067 thisApi.AddAfterElementDomId = config.AddAfterElementDomId; 4068 4069 thisApi.Contact_Id = config.Contact_Id; 4070 thisApi.PaymentProfile_Id = config.PaymentProfile_Id; 4071 thisApi.PaymentProfileDescription = config.PaymentProfileDescription; 4072 4073 thisApi.FormRowCssClass = config.FormRowCssClass; 4074 4075 const _logger = getConsoleLogApi({ "LogSourceName": 'CE One Click Check Out API' }); 4076 4077 let _oneClickCheckOutSelectedByUser = false; 4078 4079 const _getParentElement = function () { return document.getElementById(thisApi.ParentElementDomId); }; 4080
4081 Object.defineProperty(thisApi, 'ParentElement', { 4082 get: function () { 4083 return _getParentElement(); 4084 } 4085 }); 4086 4087 const _getAddAfterElement = function () { return document.getElementById(thisApi.AddAfterElementDomId); }; 4088 4089 Object.defineProperty(thisApi, 'AddAfterElement', { 4090 get: function () { 4091 return _getAddAfterElement(); 4092 } 4093 }); 4094 4095 Object.defineProperty(thisApi, 'EnabledByUser', { 4096 get: function () { 4097 return _oneClickCheckOutSelectedByUser; 4098 } 4099 }); 4100 4101 thisApi.RegisterInitializedCallBack = function (callBackFunction) { 4102 CallBackManager.add('oneClickCheckOutInitializedCallBack', callBackFunction, null); 4103 }; 4104 4105 const _fireInitializedEvent = function () { 4106 CallBackManager.fire('oneClickCheckOutInitializedCallBack', {}, null); 4107 }; 4108 4109 thisApi.RegisterHideAllOptionsCallBack = function (callBackFunction) { 4110 CallBackManager.add('oneClickCheckOutHideAllOptionsCallBack', callBackFunction, null); 4111 }; 4112 4113 const _fireHideAllOptionsEvent = function () { 4114 CallBackManager.fire('oneClickCheckOutHideAllOptionsCallBack', {}, null); 4115 }; 4116 4117 thisApi.RegisterDefaultStateShownCallBack = function (callBackFunction) { 4118 CallBackManager.add('oneClickCheckOutDefaultStateShownCallBack', callBackFunction, null); 4119 }; 4120 4121 const _fireDefaultStateShownEvent = function () { 4122 CallBackManager.fire('oneClickCheckOutDefaultStateShownCallBack', {}, null); 4123 }; 4124 4125 thisApi.RegisterUseAccountSelectedCallBack = function (callBackFunction) { 4126 CallBackManager.add('oneClickCheckOutUseAccountSelectedCallBack', callBackFunction, null); 4127 }; 4128 4129 const _fireUseAccountSelectedEvent = function () { 4130 CallBackManager.fire('oneClickCheckOutUseAccountSelectedCallBack', {}, null); 4131 }; 4132 4133 thisApi.RegisterDontUseCallBack = function (callBackFunction) { 4134 CallBackManager.add('oneClickCheckOutDontUseCallBack', callBackFunction, null); 4135 }; 4136 4137 const _fireDontUseEvent = function () { 4138 CallBackManager.fire('oneClickCheckOutDontUseCallBack', {}, null); 4139 }; 4140 4141 thisApi.RegisterForgetInfoCallBack = function (callBackFunction) { 4142 CallBackManager.add('oneClickCheckOutForgetInfoCallBack', callBackFunction, null); 4143 }; 4144 4145 const _fireForgetInfoEvent = function () { 4146 CallBackManager.fire('oneClickCheckOutForgetInfoCallBack', {}, null); 4147 }; 4148 4149 thisApi.RegisterDisabledCallBack = function (callBackFunction) { 4150 CallBackManager.add('oneClickCheckOutDisableCallBack', callBackFunction, null); 4151 }; 4152 4153 const _fireDisabledEvent = function () { 4154 CallBackManager.fire('oneClickCheckOutDisableCallBack', {}, null); 4155 }; 4156 4157 const _setOneClickHideAll = function () { 4158 4159 if (_initialized) { 4160 4161 document.getElementById('oneClickTopRow').vanish(); 4162 4163 document.getElementById('oneClickCheckoutMessagesContainer').vanish(); 4164 document.getElementById('oneClickCheckoutInitialQuestion').vanish(); 4165 document.getElementById('oneClickCheckoutAllSetMessage').vanish(); 4166 document.getElementById('oneClickCheckoutUndoUseButton').vanish(); 4167 4168 document.getElementById('oneClickBottomRow').vanish(); 4169 4170 document.getElementById('oneClickCheckoutContainer').vanish(); 4171 document.getElementById('oneClickCheckoutUseProfileButton').vanish(); 4172 document.getElementById('oneClickCheckoutDoNotUseButton').vanish(); 4173 document.getElementById('oneClickCheckoutForgetButton').vanish(); 4174 4175 _fireHideAllOptionsEvent(); 4176 4177 } 4178 4179 }; 4180 4181 const _setOneClickDefaultState = function () { 4182 4183 if (_initialized) { 4184 4185 _setOneClickHideAll(); 4186 4187 _oneClickCheckOutSelectedByUser = false; 4188 4189 document.getElementById('oneClickTopRow').show(); 4190 4191 document.getElementById('oneClickCheckoutMessagesContainer').show(); 4192 document.getElementById('oneClickCheckoutInitialQuestion').show(); 4193 4194 document.getElementById('oneClickBottomRow').show(); 4195 4196 document.getElementById('oneClickCheckoutContainer').show(); 4197 document.getElementById('oneClickCheckoutUseProfileButton').show(); 4198 document.getElementById('oneClickCheckoutDoNotUseButton').show(); 4199 document.getElementById('oneClickCheckoutForgetButton').show(); 4200 4201 _fireDefaultStateShownEvent(); 4202 4203 } 4204 4205 }; 4206
4207 const _setOneClickUseAccountState = function () { 4208 4209 if (_initialized) { 4210 4211 _setOneClickHideAll(); 4212 4213 _oneClickCheckOutSelectedByUser = true; 4214 4215 document.getElementById('oneClickTopRow').show(); 4216 4217 document.getElementById('oneClickCheckoutMessagesContainer').show(); 4218 document.getElementById('oneClickCheckoutAllSetMessage').show(); 4219 4220 _fireUseAccountSelectedEvent(); 4221 4222 } 4223 4224 }; 4225 4226 const _setOneClickDoNotUseState = function () { 4227 4228 if (_initialized) { 4229 4230 _setOneClickHideAll(); 4231 4232 _oneClickCheckOutSelectedByUser = false; 4233 4234 document.getElementById('oneClickTopRow').show(); 4235 4236 document.getElementById('oneClickCheckoutMessagesContainer').show(); 4237 document.getElementById('oneClickCheckoutUndoUseButton').show(); 4238 4239 _fireDontUseEvent(); 4240 4241 } 4242 4243 }; 4244 4245 const _setOneClickForgetMyInfoState = function () { 4246 4247 if (_initialized) { 4248 4249 _oneClickCheckOutSelectedByUser = false; 4250 4251 _oneClickOptContactOut(); 4252 4253 _setOneClickHideAll(); 4254 4255 _fireForgetInfoEvent(); 4256 4257 } 4258 4259 }; 4260 4261 const _handleOneClickUseAccountClick = function () { _setOneClickUseAccountState(); }; 4262 const _handleOneClickDoNotUseClick = function () { _setOneClickDoNotUseState(); }; 4263 const _handleOneClickChangedMindClick = function () { _setOneClickDefaultState(); }; 4264 const _handleForgetMyInfoClick = function () { _setOneClickForgetMyInfoState(); }; 4265 4266 const _getOnclickOptOutKeyName = function () { let retVal = ''; if (thisApi.Contact_Id > 0) retVal = 'contact_' + thisApi.Contact_Id + '_oneClickedOptedOut'; return retVal; }; 4267 4268 const _isContactOptedOutOfOneClick = function () { return App.LocalStorage.GetValueOrFalse(_getOnclickOptOutKeyName()); }; 4269 4270 const _oneClickOptContactOut = function () { if (thisApi.Contact_Id > 0) App.LocalStorage.Save(_getOnclickOptOutKeyName(), true); }; 4271 const _oneClickOptContactIn = function (contact_id) { thisApi.Contact_Id = contact_id; if (thisApi.Contact_Id > 0) App.LocalStorage.Save(_getOnclickOptOutKeyName(), false); }; 4272 4273 const _getOneClickTempDisableKeyName = function () { let retVal = ''; if (thisApi.Contact_Id > 0) retVal = 'contact_' + thisApi.Contact_Id + '_oneClickedTemporarilyDisabled'; return retVal; }; 4274 4275 const _disableTemporarily = function () { 4276 4277 if (_initialized && thisApi.Contact_Id > 0) { 4278 4279 _oneClickCheckOutSelectedByUser = false; 4280 4281 App.LocalStorage.SaveForOneHour(_getOneClickTempDisableKeyName(), true); 4282 4283 _setOneClickHideAll(); 4284 4285 _fireDisabledEvent(); 4286 4287 } 4288 4289 }; 4290 4291 thisApi.DisableTemporarily = function () { _disableTemporarily(); }; 4292 4293 const _enable = function () { 4294 4295 if (_initialized && thisApi.Contact_Id > 0) 4296 App.LocalStorage.Save(_getOneClickTempDisableKeyName(), false); 4297 4298 }; 4299 4300 thisApi.Enable = function () { _enable(); }; 4301 4302 const _isDisabled = function () { 4303 4304 let retVal = false; 4305 4306 if (thisApi.Contact_Id > 0) 4307 retVal = App.LocalStorage.GetValueOrFalse(_getOneClickTempDisableKeyName()); 4308 4309 return retVal; 4310 4311 }; 4312 4313 let _initialized = false; 4314 4315 thisApi.SetOneClickForgetMyInfoState = function () { _setOneClickForgetMyInfoState(); }; 4316 4317 //Initialize 4318 thisApi.Initialize = function () { 4319 4320 _logger.Log('initialize started'); 4321 4322 if (!_initialized) { 4323 4324 if (thisApi.ParentElement != null && thisApi.AddAfterElement != null) { 4325 4326 _logger.Log('init one-click check out for contact ' + thisApi.Contact_Id + ' with profile ' + thisApi.PaymentProfile_Id + ' (' + thisApi.PaymentProfileDescription + ')'); 4327 4328 if (thisApi.Contact_Id > 0 && thisApi.PaymentProfile_Id > 0 && !isBlank(thisApi.PaymentProfileDescription)) { 4329 4330 //check for opt out 4331 let optedOut = _isContactOptedOutOfOneClick(); 4332 if (!optedOut) { 4333 4334 let tempDisabled = _isDisabled(); 4335 if (!tempDisabled) { 4336 4337 //build UI 4338 _initialized = true; 4339 4340 //allow reinitialization 4341 if (document.getElementById('oneClickBottomRow') == null) { 4342 4343 let oneClickBottomRow = createDomElement('div', 'oneClickBottomRow', thisApi.FormRowCssClass); 4344 4345 let onClickCont = createDomElement('div', 'oneClickCheckoutContainer', 'ce-oneClickCheckoutContainers'); 4346 4347 onClickCont.addClickInputButton('loading...', function () { _handleOneClickUseA
4347ccountClick(); }, 'oneClickCheckoutUseProfileButton'); 4348 onClickCont.addClickInputButton('Use Another Method of Payment', function () { _handleOneClickDoNotUseClick(); }, 'oneClickCheckoutDoNotUseButton'); 4349 4350 onClickCont.addHtmlClickLink('Don\'t show this again', function () { _handleForgetMyInfoClick(); }, 'oneClickCheckoutForgetButton'); 4351 4352 oneClickBottomRow.appendChild(onClickCont); 4353 4354 thisApi.ParentElement.insertAfterElement(oneClickBottomRow, thisApi.AddAfterElement); 4355 4356 let oneClickTopRow = createDomElement('div', 'oneClickTopRow', thisApi.FormRowCssClass); 4357 4358 let onClickMessCont = createDomElement('div', 'oneClickCheckoutMessagesContainer'); 4359 4360 onClickMessCont.addText('Use your payment info from last time?', 'oneClickCheckoutInitialQuestion'); 4361 4362 let allSetMessage = document.createElement('div'); 4363 allSetMessage.id = 'oneClickCheckoutAllSetMessage'; 4364 4365 allSetMessage.addText('You\'re all set! Click button below to pay. '); 4366 allSetMessage.addAnonymousClickLink('Click here', function () { _handleOneClickChangedMindClick(); }); 4367 allSetMessage.addText(' to use another payment method.'); 4368 4369 onClickMessCont.appendChild(allSetMessage); 4370 4371 onClickMessCont.addHtmlClickLink('Click here to use your payment info from last time.', function () { _handleOneClickChangedMindClick(); }, 'oneClickCheckoutUndoUseButton'); 4372 4373 oneClickTopRow.appendChild(onClickMessCont); 4374 4375 thisApi.ParentElement.insertAfterElement(oneClickTopRow, thisApi.AddAfterElement); 4376 4377 } 4378 4379 document.getElementById('oneClickCheckoutUseProfileButton').setAttribute('value', 'Yes - Use ' + thisApi.PaymentProfileDescription); 4380 4381 _setOneClickDefaultState(); 4382 4383 } 4384 else 4385 _logger.Log('one click disabled for 1 hour for visitor'); 4386 4387 } 4388 else 4389 _logger.Log('visitor opted out of one click'); 4390 4391 } 4392 else 4393 _logger.Log('visitor context invalid'); 4394 4395 4396 _initialized = true; 4397 4398 } 4399 else 4400 _logger.Log('missing required parent element and insert after element for one click check out api'); 4401 4402 } 4403 else 4404 _logger.Log('skipping one click check out api initialization (already initialized)'); 4405 4406 _fireInitializedEvent(); 4407 4408 }; 4409 4410 if (thisApi.AutoInitialize) { 4411 4412 document.addEventListener('DOMContentLoaded', function () { thisApi.Initialize(); }); 4413 4414 } 4415 4416 return thisApi; 4417 4418} 4419 4420function getPageShoppingCartCallOutApi(config) { 4421 4422 const thisApi = {}; 4423 4424 thisApi.Mode = 'Floating'; //no config right now (v1) 4425 4426 thisApi.ShoppingCartUrl = config.ShoppingCartUrl; 4427 thisApi.ShoppingCartItemCount = (config.ShoppingCartItemCount != null) ? config.ShoppingCartItemCount : 0; 4428 4429 const _logger = getConsoleLogApi({"LogSourceName": 'CE Shopping Cart Call Out Api' }); 4430 4431 const _tryGetCtaLinkElem = function () { return document.querySelector('.ce-floatingCartCta'); }; 4432 4433 thisApi.Show = function () {if (_tryGetCtaLinkElem() != null) _tryGetCtaLinkElem().show();}; 4434 thisApi.Hide = function () {if (_tryGetCtaLinkElem() != null) _tryGetCtaLinkElem().vanish();}; 4435 4436 thisApi.RegisterInitializedCallBack = function (callBackFunction) { 4437 4438 CallBackManager.add('pageShoppingCartCallOutApiInitializedCallBack', callBackFunction, null); 4439 4440 }; 4441 4442 const _fireInitializedEvent = function () { 4443 4444 CallBackManager.fire('pageShoppingCartCallOutApiInitializedCallBack', {}, null); 4445 4446 }; 4447 4448 //Initialize 4449 thisApi.Initialize = function () { 4450
4451 _logger.Log('initialize shopping cart call out started'); 4452 4453 if (_tryGetCtaLinkElem() != null) { 4454 _logger.Log('no body in dom (abort initialization)'); 4455 return; 4456 } 4457 4458 if (isBlank(thisApi.ShoppingCartUrl)) { 4459 _logger.Log('shopping cart url required (abort initialization)'); 4460 return; 4461 } 4462 4463 if (_tryGetCtaLinkElem() != null) { 4464 _logger.Log('shopping cart call out already present in dom (abort initialization)'); 4465 return; 4466 } 4467 4468 const mainLinkElem = document.createElement('a'); 4469 mainLinkElem.href = thisApi.ShoppingCartUrl; 4470 mainLinkElem.addClassName('ce-floatingCartCta'); 4471 4472 const svgElem = createSvgElement(); 4473 svgElem.addClassName('ce-floatingCartCtaIcon'); 4474 svgElem.setAttribute('viewBox', '0 0 24 24'); 4475 4476 const svgPathElem = createSvgPathElement(); 4477 svgPathElem.setAttribute('d', 4478 'M7 18c-1.1 0-1.99.9-1.99 2S5.9 22 7 22s2-.9 2-2-.9-2-2-2z ' + 4479 'M17 18c-1.1 0-1.99.9-1.99 2S15.9 22 17 22s2-.9 2-2-.9-2-2-2z ' + 4480 'M7.17 14.75l.03-.12.9-1.63h7.45c.75 0 1.41-.41 1.75-1.03 ' + 4481 'L21.16 5H6.21l-.94-2H2v2h2l3.6 7.59-1.35 2.44 ' + 4482 'C6.09 15.32 6 15.65 6 16c0 1.1.9 2 2 2h12v-2H8.42 ' + 4483 'c-.14 0-.25-.11-.25-.25z'); 4484 4485 svgElem.appendChild(svgPathElem); 4486 4487 mainLinkElem.appendChild(svgElem); 4488 4489 const countIconElem = document.createElement('span'); 4490 countIconElem.addClassName('ce-floatingCartCtaCount'); 4491 4492 countIconElem.innerText = formatNumber(thisApi.ShoppingCartItemCount); 4493 4494 mainLinkElem.appendChild(countIconElem); 4495 4496 document.body.appendChild(mainLinkElem); 4497 4498 if (thisApi.ShoppingCartItemCount <= 0) 4499 thisApi.Hide(); 4500 4501 _fireInitializedEvent(); 4502 4503 }; 4504 4505 return thisApi; 4506 4507} 4508 4509function getShoppingCartProductApi(config) { 4510 4511 const thisApi = {}; 4512 4513 const _product = config.Product; 4514 4515 const _productId = _product.Product_Id || 0; 4516 4517 const _productContainerDomId = config.ProductContainerDomId; 4518 4519 const _variantDataArray = _product.VariantDataArray || []; 4520 4521 const _customFields = _product.CustomFields || []; 4522 4523 const _attributes = _product.Attributes || []; 4524 4525 const _basePrice = _product.BasePrice || 0; 4526 4527 const _baseInventory = _product.BaseInventory || 0; 4528 4529 const _galleryImages = _product.GalleryImages || []; 4530 4531 const _siteInventoryConfig = config.SiteInventoryConfig || {}; 4532 4533 const _logger = getConsoleLogApi({"LogSourceName": 'CE Shopping Cart Product Api'}); 4534 4535 Object.defineProperty(thisApi, 'Product_Id', { 4536 get: function () { 4537 return _productId; 4538 } 4539 }); 4540 4541 const _tryGetProductContainerElem = function () {return document.getElementById(_productContainerDomId);}; 4542
4543 Object.defineProperty(thisApi, 'ContainerDomElement', { 4544 get: function () { 4545 return _tryGetProductContainerElem(); 4546 } 4547 }); 4548 4549 const _getDisabledDropDownElements = function () {return _tryGetProductContainerElem().querySelectorAll('[data-noOptionsAvailable="true"]'); }; 4550 4551 Object.defineProperty(thisApi, 'DisabledDropDownDomElements', { 4552 get: function () { 4553 return _getDisabledDropDownElements(); 4554 } 4555 }); 4556 4557 Object.defineProperty(thisApi, 'HasAvailableProducts', { 4558 get: function () { 4559 return !_tryGetProductContainerElem().hasClassName('.ce-productHasNoAvailableOptions'); 4560 } 4561 }); 4562 4563 const _getQuantityElem = function () {return _tryGetProductContainerElem().querySelector('.ce-addToCartQuantities');}; 4564 4565 Object.defineProperty(thisApi, 'QuantityDomElement', { 4566 get: function () { 4567 return _getQuantityElem(); 4568 } 4569 }); 4570 4571 const _getCustomerSetPriceElem = function () {return _tryGetProductContainerElem().querySelector('.ce-addToCartCustomerSetPriceInputs');}; 4572
4573 Object.defineProperty(thisApi, 'SetPriceDomElement', { 4574 get: function () { 4575 return _getCustomerSetPriceElem(); 4576 } 4577 }); 4578 4579 const _getCustomFieldElems = function (customField_id) {return _tryGetProductContainerElem().querySelector('.ce-shoppingCartCustomFields');}; 4580 4581 Object.defineProperty(thisApi, 'CustomFieldDomElements', { 4582 get: function () { 4583 return _getCustomFieldElems(); 4584 } 4585 }); 4586 4587 const _getCustomFieldElem = function (customField_id) {return _tryGetProductContainerElem().querySelector('.ce-shoppingCartCustomFields[data-custom-field-id="' + customField_id + '"]');}; 4588 4589 const _getCustomFieldOptionElems = function (customField_id) {return _tryGetProductContainerElem().querySelectorAll('.ce-customFieldOption[data-custom-field-id="' + customField_id + '"]');}; 4590 4591 const _getAttrDropDownElem = function (attribute_id) {return _tryGetProductContainerElem().querySelector('.ce-attrDropDown[data-attribute-id="' + attribute_id + '"]');}; 4592 4593 const _getAttrDropDownElems = function () {return _tryGetProductContainerElem().querySelectorAll('.ce-attrDropDown');}; 4594
4595 Object.defineProperty(thisApi, 'AttributeDropDownDomElements', { 4596 get: function () { 4597 return _getAttrDropDownElems(); 4598 } 4599 }); 4600 4601 const _getGroupIdHiddenInputElem = function () {return _tryGetProductContainerElem().querySelector('[name="avGroupID"]');}; 4602 4603 const _getPriceDisplayElem = function () {return document.getElementById('priceDisp' + _productId);}; 4604 4605 const _getComparePriceDisplayElem = function () {return document.getElementById('comparePrice' + _productId);}; 4606 4607 const _getInventoryMessageElem = function () {return document.getElementById('inventoryDisp' + _productId);}; 4608 4609 const _getProductImageElem = function () {return document.getElementById('prodDetailImg-' + _productId);}; 4610 4611 const _getProductImageLinkElem = function () {return document.getElementById('prodDetailImghyperlink-' + _productId);}; 4612 4613 const _getGalleryThumbnailElems = function () {return _tryGetProductContainerElem().querySelectorAll('.ce-galleryThumbnail');}; 4614 4615 const _getGalleryViewImageElem = function () {return _tryGetProductContainerElem().querySelector('#prod_viewImage');}; 4616 4617 const _getGalleryViewImageLinkElem = function () {return _tryGetProductContainerElem().querySelector('#prod_viewImageLink');}; 4618 4619 const _getAddToCartButtonCellElem = function () {return _tryGetProductContainerElem().querySelector('.ce-formSectionFooterButtonRows');}; 4620 4621 const _getAddToCartButtonElem = function () {return _tryGetProductContainerElem().querySelector('.ce-addToCartButton');}; 4622 4623 let _currentGroupId = null; 4624 4625 const _setCurrentGroupId = function (groupId) { 4626 _currentGroupId = groupId; 4627 const groupIdElem = _getGroupIdHiddenInputElem(); 4628 if (groupIdElem != null) groupIdElem.value = groupId; 4629 }; 4630 4631 const _getVariantCount = function () {return _variantDataArray.length;}; 4632
4633 Object.defineProperty(thisApi, 'VariantCount', { 4634 get: function () { 4635 return _getVariantCount(); 4636 } 4637 }); 4638 4639 Object.defineProperty(thisApi, 'Variant_Id', { 4640 get: function () { 4641 return (_currentGroupId != null) ? _currentGroupId : 0; 4642 } 4643 }); 4644 4645 const _tryGetQuantityValue = function () { 4646 4647 let retVal = 0; 4648 4649 const qtyElem = _getQuantityElem(); 4650 4651 if (qtyElem != null) 4652 retVal = qtyElem.getValue().toIntZeroIfInvalid(); 4653 4654 return retVal; 4655 4656 }; 4657 4658 Object.defineProperty(thisApi, 'Quantity', { 4659 get: function () {return _tryGetQuantityValue();} 4660 }); 4661 4662 const _tryGetCustomerSetPriceAmount = function () { 4663 4664 let retVal = 0.00; 4665 4666 const setPriceElem = _getCustomerSetPriceElem(); 4667 if (setPriceElem != null) 4668 retVal = setPriceElem.getValue().toDecimalZeroIfInvalid(); 4669 4670 return retVal; 4671 4672 }; 4673 4674 Object.defineProperty(thisApi, 'CustomerSetPrice', { 4675 get: function () {return _tryGetCustomerSetPriceAmount();} 4676 }); 4677 4678 const _getCustomFieldValue = function (field) { 4679 4680 if (field.FieldTypeId == 1 || field.FieldTypeId == 4) { 4681 const fieldElem = _getCustomFieldElem(field.FieldId); 4682 return (fieldElem != null) ? fieldElem.value : ''; 4683 } 4684 4685 const optionElems = _getCustomFieldOptionElems(field.FieldId); 4686 const checkedValues = []; 4687 const len = optionElems.length; 4688 4689 for (let x = 0;x < len;x++) 4690 if (optionElems[x].checked) 4691 checkedValues.push(optionElems[x].value); 4692 4693 return checkedValues.join(','); 4694 4695 }; 4696
4697 Object.defineProperty(thisApi, 'CustomFields', { 4698 get: function () { 4699 4700 const result = []; 4701 4702 _customFields.forEach(function (field) { 4703 result.push({Id: field.FieldId, Value: _getCustomFieldValue(field)}); 4704 }); 4705 4706 return result; 4707 4708 } 4709 }); 4710 4711 const _setDropDownsByGroup = function (arrObj, refGroupId) { 4712 4713 if (arrObj.length > 0) { 4714 4715 for (let x = 0;x < arrObj.length;++x) { 4716 4717 const attrValArr = arrObj[x][0].toString().split('|'); 4718 const attrArr = arrObj[x][4].toString().split(','); 4719 4720 if (arrObj[x][3] == refGroupId) { 4721 4722 for (let y = 0;y < attrArr.length;++y) { 4723 4724 const ddObj = _getAttrDropDownElem(attrArr[y]); 4725 4726 if (ddObj != null) { 4727 4728 for (let z = 0;z < ddObj.length;++z) { 4729 4730 for (let i = 0;i < attrValArr.length;++i) { 4731 4732 if (ddObj.options[z].value == attrValArr[i]) 4733 ddObj.selectedIndex = z; 4734 4735 } 4736 4737 } 4738 4739 } 4740 4741 } 4742 4743 } 4744 4745 } 4746 4747 } 4748 4749 }; 4750 4751 const _setDropDownByAttrValId = function (attributeId, attrValId) { 4752 4753 const ddObj = _getAttrDropDownElem(attributeId); 4754 4755 for (let x = 0;x < ddObj.length;++x) { 4756 4757 if (ddObj.options[x].value == attrValId) 4758 ddObj.selectedIndex = x; 4759 4760 } 4761 4762 }; 4763 4764 const _getSelectedAttrValue = function (attributeId) { 4765 4766 const ddObj = _getAttrDropDownElem(attributeId); 4767 4768 _logger.Log('_getSelectedAttrValue for id ' + attributeId + ' (elem: ' + ddObj + ')'); 4769 4770 if (ddObj != null) 4771 _logger.Log('ddObj.selectedIndex: ' + ddObj.selectedIndex); 4772 4773 if (ddObj == null || ddObj.selectedIndex < 0) 4774 return null; 4775 4776 if (ddObj.getAttribute('data-noOptionsAvailable') == 'true') 4777 return null; 4778 4779 return ddObj.options[ddObj.selectedIndex].value; 4780 4781 }; 4782 4783 const _updateProductImageForSelection = function () { 4784 4785 _attributes.forEach(function (attribute) { 4786 4787 const selectedValueId = _getSelectedAttrValue(attribute.Id); 4788 4789 if (selectedValueId == null) 4790 return; 4791 4792 let matchedValue = null; 4793 4794 for (let x = 0;x < attribute.Values.length;x++) { 4795 4796 if (attribute.Values[x].Id == selectedValueId) { 4797 matchedValue = attribute.Values[x]; 4798 break; 4799 } 4800 4801 } 4802 4803 if (matchedValue != null && matchedValue.Image) { 4804 4805 const imgElem = _getProductImageElem(); 4806 4807 if (imgElem != null) { 4808 4809 let newSrc = imgElem.src.replace(/\/[^\/]*$/, '/' + matchedValue.Image); 4810 newSrc = newSrc.replace('/_thumbs/', '/_images/').replace('/_hires/', '/_images/'); 4811 imgElem.src = newSrc; 4812 4813 const linkElem = _getProductImageLinkElem(); 4814 if (linkElem != null) linkElem.href = newSrc; 4815 4816 } 4817 4818 } 4819 4820 }); 4821 4822 }; 4823 4824 const _findMatchingVariantIndex = function () { 4825 4826 let matchIndex = 0; 4827 4828 if (_attributes.length === 0) 4829 return matchIndex; 4830 4831 for (let x = 0;x < _variantDataArray.length;++x) { 4832 4833 const innerArr = _variantDataArray[x][0].split('|'); 4834 let matches = 0; 4835 4836 for (let y = 0;y < innerArr.length;++y) { 4837 4838 for (let z = 0;z < _attributes.length;z++) { 4839 4840 if (innerArr[y] == _getSelectedAttrValue(_attributes[z].Id)) 4841 matches = matches + 1; 4842 4843 } 4844 4845 } 4846 4847 if (matches == innerArr.length) 4848 matchIndex = x; 4849 4850 } 4851 4852 return matchIndex; 4853 4854 }; 4855 4856 const _getPrice = function () { 4857 4858 let retVal = _basePrice; 4859 4860 const matchIndex = _findMatchingVariantIndex(); 4861 4862 if (_variantDataArray.length > 0) 4863 retVal = (_basePrice + _variantDataArray[matchIndex][1]); 4864 4865 return retVal; 4866 4867 }; 4868 4869 //Object.defineProperty(thisApi, 'Price', { 4870 // get: function () { 4871 // return _getPrice(); 4872 // } 4873 //}); 4874 4875 const _updatePrice = function (obj) { 4876 4877 const matchIndex = _findMatchingVariantIndex(); 4878 4879 //console.log('update price FIRED!!!!!!'); 4880 4881 _updateProductImageForSelection(); 4882
4883 //_logger.Log('UpdatePrice matchIndex: ' + matchIndex); 4884 4885 if (_variantDataArray.length > 0) 4886 _setCurrentGroupId(_variantDataArray[matchIndex][3]); 4887 else 4888 _setCurrentGroupId(0); 4889 4890 if (_variantDataArray.length > 0) { 4891 4892 const inventory = _variantDataArray[matchIndex][2]; 4893 const basePrice = _basePrice + _variantDataArray[matchIndex][1]; 4894 4895 const priceElem = _getPriceDisplayElem(); 4896 if (priceElem != null) priceElem.innerHTML = '$' + basePrice.toFixed(2); 4897 4898 const compareElem = _getComparePriceDisplayElem(); 4899 if (compareElem != null) { 4900 4901 if (_variantDataArray[matchIndex][5] > 0) { 4902 const compPrice = _variantDataArray[matchIndex][5]; 4903 compareElem.innerHTML = '$' + compPrice.toFixed(2); 4904 } 4905 else 4906 compareElem.innerHTML = ''; 4907 4908 } 4909 4910 let thisInvValueTemplate = _variantDataArray[matchIndex][6]; 4911 let thisInvValueTemplateSO = _variantDataArray[matchIndex][7]; 4912 4913 if (isBlank(thisInvValueTemplate)) 4914 thisInvValueTemplate = _siteInventoryConfig.DispTemplate || '^*^INVVAL^*^'; 4915 4916 if (isBlank(thisInvValueTemplateSO)) 4917 thisInvValueTemplateSO = _siteInventoryConfig.DispTemplateSO || '^*^INVVAL^*^'; 4918 4919 _logger.Log('UpdatePrice show inv template: ' + thisInvValueTemplate); 4920 _logger.Log('UpdatePrice sold out template: ' + thisInvValueTemplateSO); 4921 4922 const invElem = _getInventoryMessageElem(); 4923 4924 if (invElem != null) { 4925 4926 if (_siteInventoryConfig.TrackingEnabled && _product.UseInventory && _siteInventoryConfig.GlShowInventory && _product.ShowInventory) 4927 invElem.innerHTML = inventory > 0 ? thisInvValueTemplate.replace('^*^INVVAL^*^', inventory.toString()) : thisInvValueTemplateSO.replace('^*^INVVAL^*^', inventory.toString()); 4928 else if (_siteInventoryConfig.TrackingEnabled && _product.UseInventory) 4929 invElem.innerHTML = inventory > 0 ? '' : 'SOLD OUT'; 4930 4931 } 4932 4933 if (_product.UseInventory) { 4934 4935 const qtyElem = _getQuantityElem(); 4936 const addToCartCellElem = _getAddToCartButtonCellElem(); 4937 4938 if (qtyElem != null) { 4939 4940 const curQty = qtyElem.getValue().toIntZeroIfInvalid(); 4941 4942 const origConfiguredMaxAllowed = qtyElem.tryGetIntegerAttribute('data-configuredOriginalMaximumNumericValue'); 4943 4944 let setMaxAllowedVal = inventory; 4945 4946 if (origConfiguredMaxAllowed > 0 && inventory > origConfiguredMaxAllowed) 4947 setMaxAllowedVal = origConfiguredMaxAllowed; 4948 4949 qtyElem.setAttribute('data-validationMaximumNumericValue', setMaxAllowedVal); 4950 4951 if (inventory > 0) 4952 qtyElem.enable(); 4953 else 4954 qtyElem.disable(); 4955 4956 } 4957 4958 } 4959 4960 const variantStatus = _variantDataArray[matchIndex][11]; 4961 4962 _logger.Log('UpdatePrice variantStatus: ' + variantStatus); 4963 4964 if (variantStatus == 0) { 4965 4966 let inactiveDisplayText = _variantDataArray[matchIndex][12]; 4967 4968 if (isBlank(inactiveDisplayText)) 4969 inactiveDisplayText = 'Unavailable'; 4970 4971 _logger.Log('UpdatePrice called by: ' + (obj != null ? obj.name : '(none)')); 4972 4973 if (priceElem != null && obj != null && obj.name != null && obj.name.length > 0) 4974 priceElem.innerHTML = (inactiveDisplayText != null) ? inactiveDisplayText : ''; 4975 4976 if (compareElem != null) compareElem.innerHTML = ''; 4977 if (invElem != null) invElem.innerHTML = ''; 4978 4979 } 4980 4981 } 4982 4983 }; 4984 4985 const _setAttributeValue = function (attributeId, attrValId) { 4986 4987 const ddObj = _getAttrDropDownElem(attributeId); 4988 4989 if (ddObj == null) return; 4990 4991 _setDropDownByAttrValId(attributeId, attrValId); 4992 4993 ddObj.dispatchEvent(new Event('change', {bubbles: true})); 4994 4995 }; 4996 4997 const _updateAttrDropdownsForSku = function (sku) { 4998 4999 for (let x = 0;x < _variantDataArray.length;++x) { 5000 5001 if (_variantDataArray[x][3] == sku) { 5002 5003 const attrIds = _variantDataArray[x][4].toString().split(','); 5004 const attrValIds = _variantDataArray[x][0].toString().split('|'); 5005 5006 for (let y = 0;y < attrIds.length;y++) { 5007 5008 const ddObj = _getAttrDropDownElem(attrIds[y]); 5009 5010 if (ddObj != null && attrValIds[y] != null) { 5011 ddObj.value = attrValIds[y];
5012 ddObj.dispatchEvent(new Event('change', {bubbles: true})); 5013 } 5014 5015 } 5016 5017 } 5018 5019 } 5020 5021 }; 5022 5023 const _swapGalleryImage = function (thumbnailElem) { 5024 5025 if (thumbnailElem == null) return; 5026 5027 const passId = thumbnailElem.id; 5028 5029 for (let x = 0;x < _galleryImages.length;x++) { 5030 5031 const galleryImage = _galleryImages[x]; 5032 const thisImgObj = document.getElementById(galleryImage.ElemId); 5033 5034 if (thisImgObj == null) continue; 5035 5036 if (thisImgObj.id == passId) { 5037 5038 const viewImgLink = _getGalleryViewImageLinkElem(); 5039 const viewImgObj = _getGalleryViewImageElem(); 5040 5041 if (galleryImage.Inventory > 0 || galleryImage.SoldOutMediumImageUrl.length == 0 || galleryImage.SoldOutLargeImageUrl.length == 0) { 5042 if (viewImgLink != null) viewImgLink.href = galleryImage.LargeImageUrl; 5043 if (viewImgObj != null) viewImgObj.src = galleryImage.MediumImageUrl; 5044 } 5045 else { 5046 if (viewImgLink != null) viewImgLink.href = galleryImage.SoldOutLargeImageUrl; 5047 if (viewImgObj != null) viewImgObj.src = galleryImage.SoldOutMediumImageUrl; 5048 } 5049 5050 thisImgObj.classList.add('ce-galleryThumbnailActive'); 5051 5052 } 5053 else { 5054 thisImgObj.classList.remove('ce-galleryThumbnailActive'); 5055 } 5056 5057 } 5058 5059 const attributeId = thumbnailElem.getAttribute('data-attribute-id'); 5060 const attrValId = thumbnailElem.getAttribute('data-attr-val-id'); 5061 const groupId = thumbnailElem.getAttribute('data-group-id'); 5062 5063 if (attributeId && attrValId) 5064 _setAttributeValue(parseInt(attributeId), attrValId); 5065 else if (groupId) 5066 _updateAttrDropdownsForSku(parseInt(groupId)); 5067 5068 }; 5069 5070 const _updateAttributeOptionAvailability = function (useInventory) { 5071 5072 let diabledDropDownCnt = 0; 5073 5074 _attributes.forEach(function (attribute) { 5075 5076 const ddObj = _getAttrDropDownElem(attribute.Id); 5077 5078 if (ddObj == null) return; 5079 5080 for (let x = 0;x < ddObj.options.length;x++) { 5081 5082 if (ddObj.options[x].getAttribute('data-noOptionsPlaceholder') == 'true') continue; 5083 5084 const optionValue = ddObj.options[x].value; 5085 let attributeOutOfStock = true; 5086 5087 for (let y = 0;y < _variantDataArray.length;++y) { 5088 5089 const innerArr = _variantDataArray[y][0].toString().split('|'); 5090 const variantExistsInDropdown = innerArr.indexOf(optionValue) !== -1; 5091 5092 if (variantExistsInDropdown) { 5093 5094 const rowInventory = _variantDataArray[y][2]; 5095 const rowStatus = _variantDataArray[y][11]; 5096 5097 if (!((useInventory && rowInventory <= 0) || rowStatus == 0)) 5098 attributeOutOfStock = false; 5099 5100 } 5101 5102 } 5103 5104 if (attributeOutOfStock) { 5105 ddObj.options[x].style.display = 'none'; 5106 ddObj.options[x].setAttribute('disabled', 'disabled'); 5107 } 5108 else { 5109 ddObj.options[x].style.display = ''; 5110 ddObj.options[x].removeAttribute('disabled'); 5111 } 5112 5113 } 5114 5115 let foundAvailable = false; 5116 5117 for (let x = 0;x < ddObj.options.length;x++) { 5118 5119 if (ddObj.options[x].getAttribute('data-noOptionsPlaceholder') == 'true') continue; 5120 5121 if (ddObj.options[x].style.display != 'none') { 5122 ddObj.selectedIndex = x; 5123 foundAvailable = true; 5124 break; 5125 } 5126 5127 } 5128 5129 let placeholderOption = null; 5130 5131 for (let x = 0;x < ddObj.options.length;x++) { 5132 if (ddObj.options[x].getAttribute('data-noOptionsPlaceholder')
5132== 'true') { 5133 placeholderOption = ddObj.options[x]; 5134 break; 5135 } 5136 } 5137 5138 if (!foundAvailable) { 5139 5140 if (placeholderOption == null) { 5141 placeholderOption = document.createElement('option'); 5142 placeholderOption.setAttribute('data-noOptionsPlaceholder', 'true'); 5143 placeholderOption.value = ''; 5144 placeholderOption.text = 'No options available'; 5145 ddObj.insertBefore(placeholderOption, ddObj.options[0] || null); 5146 } 5147 5148 placeholderOption.style.display = ''; 5149 placeholderOption.removeAttribute('disabled'); 5150 ddObj.selectedIndex = placeholderOption.index; 5151 ddObj.setAttribute('disabled', 'disabled'); 5152 ddObj.setAttribute('data-noOptionsAvailable', 'true'); 5153 5154 diabledDropDownCnt++; 5155 5156 } 5157 else { 5158 5159 ddObj.removeAttribute('disabled'); 5160 ddObj.removeAttribute('data-noOptionsAvailable'); 5161 5162 if (placeholderOption != null) { 5163 placeholderOption.style.display = 'none'; 5164 placeholderOption.setAttribute('disabled', 'disabled'); 5165 } 5166 5167 } 5168 5169 }); 5170 5171 if (diabledDropDownCnt > 0) 5172 _tryGetProductContainerElem().addClassName('ce-productHasNoAvailableOptions'); 5173 else 5174 _tryGetProductContainerElem().removeClassName('ce-productHasNoAvailableOptions'); 5175 5176 }; 5177 5178 const _validateProductConfiguration = function () { 5179 5180 let carryOn = true; 5181 5182 for (let x = 0;x < _attributes.length;x++) { 5183 5184 const ddObj = _getAttrDropDownElem(_attributes[x].Id); 5185 5186 if (ddObj != null) { 5187 5188 ddObj.validationSucceeded(); 5189 5190 if (ddObj.getAttribute('data-noOptionsAvailable') == 'true') { 5191 carryOn = false; 5192 ddObj.validationFailed(); 5193 } 5194 5195 } 5196 5197 } 5198 5199 //let prodErrorDesc = _product.Name; 5200 //if(isBlank(prodErrorDesc)) 5201 // prodErrorDesc = 'Selection'; 5202 5203 //check status of variant 5204 if (carryOn && _variantDataArray.length > 0 && _currentGroupId != null) { 5205 5206 for (let x = 0;x < _variantDataArray.length;++x) { 5207 5208 if (_variantDataArray[x][3] == _currentGroupId && parseInt(_variantDataArray[x][11]) == 0) { 5209 carryOn = false; 5210 App.UI.Messages.SetError('Selection is currently not available'); 5211 } 5212 5213 } 5214 5215 } 5216 5217 //check inventory on variant or base 5218 if (carryOn && _siteInventoryConfig.TrackingEnabled && _product.UseInventory) { 5219 5220 const qtyElem = _getQuantityElem(); 5221 const quantVal = (qtyElem != null) ? parseInt(qtyElem.value) : 0; 5222 5223 if (_variantDataArray.length > 0 && _currentGroupId != null) 5224 { 5225 5226 for (let x = 0;x < _variantDataArray.length;++x) { 5227 5228 if (_variantDataArray[x][3] == _currentGroupId) { 5229 5230 const rowInventory = parseInt(_variantDataArray[x][2]); 5231 5232 if (rowInventory <= 0) { 5233 carryOn = false; 5234 App.UI.Messages.SetError('Selection is currently sold out'); 5235 } 5236 else if (quantVal > rowInventory) { 5237 carryOn = false; 5238 App.UI.Messages.SetError('Only ' + rowInventory + ' remaining'); 5239 } 5240 5241 } 5242 5243 } 5244 5245 } 5246 else 5247 { 5248 5249 if (_baseInventory <= 0) { 5250 carryOn = false; 5251 App.UI.Messages.SetError('Selection is currently sold out'); 5252 } 5253 else if (quantVal > _baseInventory) { 5254 carryOn = false; 5255 App.UI.Messages.SetError('Only ' + _baseInventory + ' remaining'); 5256 } 5257 5258 } 5259 5260 } 5261 5262 if (!carryOn) 5263 return carryOn; 5264 5265 _customFields.forEach(function (field) { 5266 5267 if (!carryOn) return; 5268 5269 if (field.FieldTypeId == 2 || field.FieldTypeId == 3) { 5270 5271 if (field.Required) { 5272 5273 const optionElems = _getCustomFieldOptionElems(field.FieldId); 5274 let anyChecked = false; 5275 5276 if (optionElems != null) { 5277 5278 for (let x = 0;x < optionElems.length;x++) 5279 optionElems[x].validationSucceeded(); 5280 5281 for (let x = 0;x < optionElems.length;x++) { 5282 if (optionElems[x].checked) { 5283 anyChecked = true; 5284 break; 5285 } 5286 } 5287 5288 if (!anyChecked) 5289 for (let x = 0;x < optionElems.length;x++) 5290 optionElems[x].validationFailed(); 5291 5292 } 5293 5294 if (!anyChecked) { 5295 5296 carryOn = false; 5297 5298 if (!isBlank(field.ClientSideErrorMessage)) 5299 App.UI.Messages.SetError(field.ClientSideErrorMessage); 5300 5301 } 5302 5303 } 5304 5305 } 5306 5307 }); 5308 5309 return carryOn; 5310 5311 }; 5312 5313 const _generateAjaxPayloadParameters = function () { 5314 5315 const unitPrice = _getPrice(); 5316 5317 var params = { 5318 "Product_Id": _productId, 5319 "Variant_Id": thisApi.Variant_Id, 5320 "Quantity": thisApi.Quantity, 5321 "CustomerSetPrice": thisApi.CustomerSetPrice, 5322 "CustomFields": thisApi.CustomFields, 5323 "UnitPrice": unitPrice, 5324 "TotalPrice": (thisApi.Quantity * unitPrice) 5325 }; 5326 5327 return params; 5328 5329 }; 5330
5331 Object.defineProperty(thisApi, 'SelectionDetails', { 5332 get: function () { 5333 return _generateAjaxPayloadParameters(); 5334 } 5335 }); 5336 5337 const _validateProductForm = function () { 5338 5339 let validated = App.UI.Validation.ValidateForm(_tryGetProductContainerElem()); //NOTE: ValidateForm calls clear messages 5340 5341 if (validated) 5342 validated = _validateProductConfiguration(); 5343 5344 return validated; 5345 5346 }; 5347 5348 thisApi.Validate = function () {return _validateProductForm();}; 5349 5350 const _submitAddToCart = function () { 5351 5352 const validated = _validateProductForm(); 5353 5354 if (!validated) 5355 return; 5356 5357 App.UI.Messages.ClearMessages(); 5358 5359 var params = _generateAjaxPayloadParameters(); 5360 5361 App.PostJsonAsync('/api/CmsAppAjaxEndpoint/AddProductToCart', 5362 JSON.stringify(params), 5363 function (result) { 5364 5365 if (result != null && result.Success) 5366 App.Navigation.GoToUrl(gl_shoppingCartUrl); //document.location = gl_shoppingCartUrl; 5367 else 5368 App.UI.Messages.SetError(result.ErrorMessage); 5369 5370 }); 5371 5372 }; 5373 5374 thisApi.RegisterInitializedCallBack = function (callBackFunction) { 5375 CallBackManager.add('shoppingCartProduct' + _productId + 'ApiInitializedCallBack', callBackFunction, null); 5376 }; 5377 5378 const _fireInitializedEvent = function () { 5379 5380 const cbParams = {}; 5381 cbParams.Product_Id = _productId; 5382 5383 CallBackManager.fire('shoppingCartProduct' + _productId + 'ApiInitializedCallBack', cbParams, null); 5384 5385 }; 5386 5387 thisApi.Initialize = function () { 5388 5389 _logger.Log('initialize product ' + _productId + ' started'); 5390 5391 if (_productId <= 0) { 5392 _logger.Log('product id required (abort initialization)'); 5393 return; 5394 } 5395 5396 if (_tryGetProductContainerElem() == null) { 5397 _logger.Log('invalid form element (abort initialization)'); 5398 return; 5399 } 5400 5401 if (_product == null) { 5402 _logger.Log('product config required (abort initialization)'); 5403 return; 5404 } 5405
5406 //_logger.Log('initiailze attribute change events for ' + _attributes.length + ' attributes'); 5407 5408 _attributes.forEach(function (attribute) { 5409 5410 const ddElem = _getAttrDropDownElem(attribute.Id); 5411 5412 _logger.Log('initiailze attribute change event for ' + ddElem.id); 5413 if (ddElem != null) 5414 ddElem.addEventListener('change', function () {_updatePrice(ddElem);}); 5415 5416 }); 5417 5418 const qtyElem = _getQuantityElem(); 5419 if (qtyElem != null) 5420 qtyElem.addEventListener('change', function () {_updatePrice(qtyElem);}); 5421 5422 const addToCartButtonElem = _getAddToCartButtonElem(); 5423 if (addToCartButtonElem != null) 5424 addToCartButtonElem.addEventListener('click', function () {_submitAddToCart();}); 5425 5426 const galleryThumbnailElems = _getGalleryThumbnailElems(); 5427 if (galleryThumbnailElems != null) 5428 galleryThumbnailElems.forEach(function (thumbElem) { 5429 thumbElem.addEventListener('click', function (evt) { 5430 if (evt != null && evt.preventDefault) evt.preventDefault(); 5431 _swapGalleryImage(thumbElem); 5432 }); 5433 }); 5434 5435 _updateAttributeOptionAvailability(_product.UseInventory); 5436 5437 _updatePrice(null); 5438 5439 _fireInitializedEvent(); 5440 5441 }; 5442 5443 return thisApi; 5444 5445} 5446 5447/*App Core Payment Plug-In Apis*/ 5448 5449function getPayPalApi(config) { 5450 5451 const thisApi = {}; 5452 5453 thisApi.PayPalEnabled = config.PayPalEnabled; 5454 5455 thisApi.PayPalLegacyButtonContainerDomId = config.PayPalLegacyButtonContainerDomId; 5456 thisApi.PayPalNewButtonContainerDomId = config.PayPalNewButtonContainerDomId; 5457 thisApi.PayPalPaymentTypeLabelDomId = config.PayPalPaymentTypeLabelDomId; 5458 5459 thisApi.PayPalMerchantGatewayId = config.PayPalMerchantGatewayId; 5460 thisApi.PayPalAccountMode = config.PayPalAccountMode; 5461 5462 thisApi.CharityEngineAppRecordId = config.CharityEngineAppRecordId; 5463 5464 thisApi.PaypalVenmoCheckOutEnabled = config.PaypalVenmoCheckOutEnabled; 5465 5466 thisApi.GetChargeAmountFunction = config.GetChargeAmountFunction; 5467 thisApi.GetRecurringFrequencyIdFunction = config.GetRecurringFrequencyIdFunction; 5468 thisApi.GetCurrencyIdFunction = config.GetCurrencyIdFunction; 5469 5470 thisApi.UIValidationCallBackFunction = config.UIValidationCallBackFunction; 5471 5472 const _logger = getConsoleLogApi({ "LogSourceName": 'CE PayPal Api' }); 5473 5474 thisApi.RegisterInitializedCallBack = function (callBackFunction) { 5475 5476 CallBackManager.add('payPalApiInitializedCallBack', callBackFunction, null); 5477 5478 }; 5479 5480 thisApi.FireInitializedEvent = function () { 5481 5482 CallBackManager.fire('payPalApiInitializedCallBack', {}, null); 5483 5484 }; 5485 5486 thisApi.RegisterTokenReceivedCallBack = function (callBackFunction) { 5487 5488 CallBackManager.add('payPalApiTokenReceivedCallBack', callBackFunction, null); 5489 5490 }; 5491 5492 thisApi.FireTokenReceivedEvent = function (callBackParams) { 5493 5494 CallBackManager.fire('payPalApiTokenReceivedCallBack', callBackParams, null); 5495 5496 }; 5497 5498 thisApi.RegisterPaymentAuthorizedCallBack = function (callBackFunction) { 5499 5500 CallBackManager.add('payPalApiPaymentAuthorizedCallBack', callBackFunction, null); 5501 5502 }; 5503 5504 thisApi.FirePaymentAuthorizedEvent = function (callBackParams) { 5505 5506 CallBackManager.fire('payPalApiPaymentAuthorizedCallBack', callBackParams, null); 5507 5508 }; 5509 5510 thisApi.IsPayPalApiAvailable = function () { 5511 return (window.paypal); 5512 } 5513 5514 thisApi.GetButtonsContainerElement = function () { 5515 5516 let btnElem = document.querySelector('div.paypal-button-container'); 5517 5518 return btnElem; 5519 5520 }; 5521 5522 thisApi.GetButtonElement = function () { return thisApi.GetButtonsContainerElement; }; //backwards compatibility (to be removed) 5523 5524 thisApi.GetLegacyButtonElement = function () { 5525 5526 let btnElem = document.getElementById(thisApi.PayPalLegacyButtonContainerDomId); 5527 5528 return btnElem; 5529 5530 }; 5531 5532 thisApi.GetNewButtonElement = function () { 5533 5534 let btnElem = document.getElementById(thisApi.PayPalNewButtonContainerDomId); 5535 5536 return btnElem; 5537 5538 }; 5539 5540 thisApi.GetPaymentTypeLabelElement = function () { 5541 5542 let btnElem = document.getElementById(thisApi.PayPalPaymentTypeLabelDomId); 5543 5544 return btnElem; 5545 5546 }; 5547 5548 thisApi.RefreshButton = function () { thisApi.RefreshButtonIfShown(); }; 5549 5550 thisApi.RefreshButtonIfShown = function () { 5551 5552 if (thisApi.GetButtonsContainerElement() != null) { 5553 5554 if (thisApi.GetButtonsContainerElement().isShown()) 5555 thisApi.ShowButton(); 5556 5557 } 5558 5559 }; 5560 5561 thisApi.ShowButton = function () { 5562 5563 if (thisApi.GetButtonsContainerElement() != null) { 5564 5565 //always keep hidden if paypal is not enabled 5566 thisApi.GetButtonsContainerElement().vanish(); 5567 if(thisApi.PayPalEnabled) 5568 thisApi.GetButtonsContainerElement().show(); 5569 5570 } 5571 5572 //temporary solution to show/hide the old versus new API 5573 if (thisApi.GetLegacyButtonElement() != null) 5574 thisApi.GetLegacyButtonElement().vanish(); 5575 5576 if (thisApi.GetNewButtonElement() != null) 5577 thisApi.GetNewButtonElement().vanish(); 5578
5579 _logger.Log('ShowButton Frequency: ' + thisApi.GetRecurringFrequencyIdFunction()); 5580 5581 //show the old API if its a recurring payment or if we have the new check out disabled 5582 if (thisApi.GetRecurringFrequencyIdFunction() > 0 || !thisApi.PaypalVenmoCheckOutEnabled) { 5583 5584 _logger.Log('show recurring buttons'); 5585 5586 if (thisApi.GetLegacyButtonElement() != null) 5587 thisApi.GetLegacyButtonElement().show(); 5588 5589 } 5590 else { 5591 5592 _logger.Log('show one-time payment buttons'); 5593 5594 if (thisApi.GetNewButtonElement() != null) 5595 thisApi.GetNewButtonElement().show(); 5596 5597 } 5598 5599 }; 5600 5601 thisApi.HideButton = function () { 5602 5603 if (thisApi.GetButtonsContainerElement() != null) 5604 thisApi.GetButtonsContainerElement().vanish(); 5605 5606 }; 5607 5608 //Initialize 5609 thisApi.Initialize = function () { 5610 5611 _logger.Log('initialize started'); 5612 5613 _logger.Log('initialize legacy express check out front-end API'); 5614 5615 let ppMode = 'production'; 5616 if (!isBlank(thisApi.PayPalAccountMode)) 5617 ppMode = thisApi.PayPalAccountMode; 5618 5619 paypal.Button.render({ 5620 env: ppMode, 5621 style: { 5622 size: 'medium', 5623 color: 'blue', 5624 shape: 'rect', 5625 label: 'paypal', 5626 tagline: 'false' 5627 }, 5628 payment: function (resolve, reject) { 5629 5630 _logger.Log('Initialize paypal payment'); 5631 5632 let uiValidated = true; 5633 5634 if (typeof (thisApi.UIValidationCallBackFunction) == 'function') 5635 uiValidated = thisApi.UIValidationCallBackFunction(); 5636 5637 if (uiValidated) { 5638 5639 return new paypal.Promise(function (resolve, reject) { 5640 5641 let enablePayPalRec = 0; 5642 if (thisApi.GetRecurringFrequencyIdFunction() > 0) 5643 enablePayPalRec = 1; 5644 5645 let presentmentCurrencyId = 0; 5646 if (typeof (thisApi.GetCurrencyIdFunction) == 'function') 5647 presentmentCurrencyId = thisApi.GetCurrencyIdFunction(); 5648 5649 const params = { 5650 "ajaxParams": { 5651 "PaypalGatewayId": thisApi.PayPalMerchantGatewayId, 5652 "PaymentAmount": thisApi.GetChargeAmountFunction(), 5653 "EnableRecurringBilling": enablePayPalRec, 5654 "AppTypeId": 0, //deprecated (6.9.2023) 5655 "AppId": thisApi.CharityEngineAppRecordId, 5656 "PresentmentCurrencyId": presentmentCurrencyId 5657 } 5658 }; 5659 5660 _logger.Log('get authorization token params: ' + JSON.stringify(params)); 5661 5662 App.BackgroundPostJsonAsync('/api/PublicWebAppUIAjaxEndpoint/GetPaypalAuthorizationToken', 5663 JSON.stringify(params), 5664 function (result) { 5665 5666 _logger.Log('result: ' + JSON.stringify(result)); 5667 5668 if (result != null && result.Success) { 5669 5670 let cbParam = {}; 5671 cbParam.ApiVersion = 1; 5672 cbParam.PaymentSourceId = 1; 5673 cbParam.PayPalToken = result.PaypalToken; 5674 cbParam.ChargeAmount = result.ChargeAmount; 5675 5676 thisApi.FireTokenReceivedEvent(cbParam); 5677 5678 resolve(result.PaypalToken); 5679 5680 } 5681 else 5682 reject(result.ErrorMessage); 5683 5684 }, 5685 function (error) {
5686 _logger.Log('error: ' + error); 5687 reject(error); 5688 }); 5689 5690 }); 5691 5692 } 5693 else { 5694 5695 _logger.Log('Abort paypal payment becuase UI validation failed'); 5696 5697 return new paypal.Promise(function (resolve, reject) { reject('Required fields missing'); }); 5698 5699 } 5700 5701 }, 5702 onAuthorize: function (data) { 5703 5704 let cbParam = {}; 5705 cbParam.PayPalGatewayPayerId = data.payerID; 5706 5707 thisApi.FirePaymentAuthorizedEvent(cbParam); // handlePaypalPaymentAuthorized(); 5708 5709 }, 5710 onCancel: function (data, actions) { 5711 _logger.Log('paypal payment cancelled'); 5712 }, 5713 onError: function (err) { 5714 console.error('paypal payment error', err); 5715 } 5716 }, '#' + thisApi.PayPalLegacyButtonContainerDomId); 5717 5718 if (thisApi.PaypalVenmoCheckOutEnabled) { 5719 5720 if (document.getElementById(thisApi.PayPalNewButtonContainerDomId) != null) { 5721 5722 _logger.Log('initialize latest check-out front-end API (paypal/venmo)'); 5723 5724 //document.getElementById(thisApi.PayPalNewButtonContainerDomId).innerHTML = '<input type="button" value="New Paypal Button" onclick="alert(\'implment new paypal button\')" />'; 5725 5726 //todo: implement new paypal button intialization 5727 const ppVmConfig = { 5728 style: { 5729 layout: 'horizontal', 5730 color: 'blue', 5731 shape: 'rect', 5732 label: 'paypal', 5733 height: 40, 5734 tagline: false, 5735 label: 'pay' 5736 }, 5737 onInit(data, actions) { 5738 5739 _logger.Log('new paypal button init event'); 5740 5741 }, 5742 onClick() { 5743 5744 _logger.Log('new paypal button click event'); 5745 5746 }, 5747 createOrder() { 5748 5749 _logger.Log('new paypal button create order event'); 5750 5751 let uiValidated = true; 5752 5753 if (typeof (thisApi.UIValidationCallBackFunction) == 'function') 5754 uiValidated = thisApi.UIValidationCallBackFunction(); 5755 5756 let presentmentCurrencyId = 0; 5757 if (typeof (thisApi.GetCurrencyIdFunction) == 'function') 5758 presentmentCurrencyId = thisApi.GetCurrencyIdFunction(); 5759 5760 if (uiValidated) { 5761 5762 return new paypal.Promise(function (resolve, reject) { 5763 5764 const params = { 5765 "ajaxParams": { 5766 "PayPalGatewayId": thisApi.PayPalMerchantGatewayId, 5767 "PaymentAmount": thisApi.GetChargeAmountFunction(), 5768 "AppId": thisApi.CharityEngineAppRecordId, 5769 "PresentmentCurrencyId": presentmentCurrencyId 5770 } 5771 }; 5772 5773 _logger.Log('start payment params: ' + JSON.stringify(params)); 5774 5775 App.BackgroundPostJsonAsync('/api/PublicWebAppUIAjaxEndpoint/StartPayPalPayment', 5776 JSON.stringify(params), 5777 function (result) { 5778 5779 _logger.Log('result: ' + JSON.stringify(result)); 5780 5781 if (result != null && result.Success) { 5782 5783 let cbParam = {}; 5784 cbParam.ApiVersion = 2; 5785 cbParam.PaymentSourceId = result.PayPalPaymentSourceId;
5786 cbParam.PayPalToken = result.PayPalOrderId; 5787 cbParam.ChargeAmount = result.ChargeAmount; 5788 5789 thisApi.FireTokenReceivedEvent(cbParam); 5790 5791 resolve(result.PayPalOrderId); 5792 5793 } 5794 else 5795 reject(result.ErrorMessage); 5796 5797 }, 5798 function (error) { 5799 _logger.Log('error: ' + error); 5800 reject(error); 5801 }); 5802 5803 }); 5804 5805 } 5806 else { 5807 5808 _logger.Log('Abort paypal payment becuase UI validation failed'); 5809 5810 return new paypal.Promise(function (resolve, reject) { reject('Required fields missing'); }); 5811 5812 } 5813 5814 }, 5815 onApprove(data) { 5816 5817 _logger.Log('new paypal button create order event'); 5818 5819 _logger.Log('new paypal sdk approve data: ' + JSON.stringify(data)); 5820 5821 let cbParam = {}; 5822 cbParam.PayPalGatewayPayerId = data.payerID; 5823 5824 thisApi.FirePaymentAuthorizedEvent(cbParam); 5825 5826 } 5827 }; 5828 5829 //ppVmConfig.fundingSource = paypal.FUNDING.PAYPAL; //forces popup 5830 5831 paypal.Buttons(ppVmConfig).render('#' + thisApi.PayPalNewButtonContainerDomId); 5832 5833 } 5834 5835 if (thisApi.GetRecurringFrequencyIdFunction() <= 0 && thisApi.GetPaymentTypeLabelElement() != null) 5836 thisApi.GetPaymentTypeLabelElement().innerHTML = "PayPal/Venmo"; 5837 5838 } 5839 else 5840 _logger.Log('skipping latest PayPal/Venmo check-out front-end initialization (configuration)'); 5841 5842 thisApi.FireInitializedEvent(); 5843 5844 }; 5845 5846 return thisApi; 5847 5848} 5849 5850function getGooglePayApi(config) { 5851 5852 const thisApi = {}; 5853 5854 thisApi.GooglePayEnabled = config.GooglePayEnabled; 5855 5856 thisApi.GooglePayDivId = config.GooglePayDivId; 5857 thisApi.GooglePayButtonContainerId = config.GooglePayButtonContainerId; 5858 thisApi.GooglePayEnvironment = config.GooglePayEnvironment; 5859 thisApi.GooglePayGateway = config.GooglePayGateway; 5860 thisApi.GooglePayGatewayMerchantID = config.GooglePayGatewayMerchantID; 5861 5862 thisApi.GooglePayMerchantID = config.GooglePayMerchantID; 5863 thisApi.OrganizationName = config.OrganizationName; 5864 5865 //UI interaction call backs 5866 thisApi.GetChargeAmountFunction = config.GetChargeAmountFunction; 5867 thisApi.GetRecurringAmountFunction = config.GetRecurringAmountFunction; 5868 thisApi.GetCurrencyCodeFunction = config.GetCurrencyCodeFunction; 5869 thisApi.GetCountryCodeFunction = config.GetCountryCodeFunction; 5870 5871 thisApi.UIValidationCallBackFunction = config.UIValidationCallBackFunction; 5872 5873 const _logger = getConsoleLogApi({ "LogSourceName": 'CE GooglePay Api' }); 5874 5875 //https://developers.google.com/pay/api/web/reference/request-objects#PaymentDataRequest 5876 let baseRequest = { 5877 apiVersion: 2, 5878 apiVersionMinor: 0 5879 }; 5880 5881 //https://developers.google.com/pay/api/web/reference/request-objects#CardParameters 5882 let baseCardPaymentMethod = { 5883 type: 'CARD', 5884 parameters: { 5885 allowedAuthMethods: ["PAN_ONLY"], //, "CRYPTOGRAM_3DS" NOTE: removed 3DS until we can verify GooglePay processor support and make configurable for one-time only (10.4.2023) 5886 allowedCardNetworks: ["AMEX", "DISCOVER", "INTERAC", "JCB", "MASTERCARD", "VISA"] 5887 } 5888 }; 5889 5890 //https://developers.google.com/pay/api/web/reference/request-objects#CardParameters 5891 let cardPaymentMethod = Object.assign( 5892 {}, 5893 baseCardPaymentMethod, 5894 { 5895 tokenizationSpecification: { 5896 type: 'PAYMENT_GATEWAY', 5897 parameters: { 5898 'gateway': thisApi.GooglePayGateway, 5899 'gatewayMerchantId': thisApi.GooglePayGatewayMerchantID 5900 } 5901 } 5902 } 5903 ); 5904 5905 let googlePayClient = null; 5906 5907 thisApi.GetGooglePaymentsClient = function () { // function getGooglePaymentsClient() { 5908 5909 if (googlePayClient == null) 5910 googlePayClient = new google.payments.api.PaymentsClient({ environment: thisApi.GooglePayEnvironment }); //https://developers.google.com/pay/api/web/reference/client#PaymentsClient 5911 5912 return googlePayClient; 5913 5914 } 5915 5916 thisApi.RegisterInitializedCallBack = function (callBackFunction) { 5917 5918 CallBackManager.add('googlePayApiInitializedCallBack', callBackFunction, null); 5919 5920 }; 5921 5922 thisApi.FireInitializedEvent = function () { 5923 5924 CallBackManager.fire('googlePayApiInitializedCallBack', {}, null); 5925 5926 }; 5927 5928 thisApi.RegisterButtonAddedCallBack = function (callBackFunction) { 5929 5930 CallBackManager.add('googlePayApiButtonAddedCallBack', callBackFunction, null); 5931 5932 }; 5933 5934 thisApi.FireButtonAddedEvent = function () { 5935 5936 CallBackManager.fire('googlePayApiButtonAddedCallBack', {}, null); 5937 5938 }; 5939 5940 thisApi.RegisterPaymentAuthorizedCallBack = function (callBackFunction) { 5941 5942 CallBackManager.add('googlePayApiPaymentAuthorizedCallBack', callBackFunction, null); 5943 5944 }; 5945 5946 thisApi.FirePaymentAuthorizedEvent = function (callBackParams) { 5947 5948 CallBackManager.fire('googlePayApiPaymentAuthorizedCallBack', callBackParams, null); 5949 5950 }; 5951 5952 thisApi.IsGooglePayApiAvailable = function () { 5953 return (window.google && google.payments); 5954 } 5955 5956 thisApi.GetContainerDivElement = function () { 5957 5958 let divCntElem = null; 5959 5960 if (!isBlank(thisApi.GooglePayDivId)) 5961 divCntElem = document.getElementById(thisApi.GooglePayDivId); 5962 5963 return divCntElem; 5964 5965 }; 5966 5967 thisApi.GetButtonContainerElement = function () { 5968 5969 let btnCntElem = null; 5970 5971 if (!isBlank(thisApi.GooglePayButtonContainerId)) 5972 btnCntElem = document.getElementById(thisApi.GooglePayButtonContainerId); 5973 5974 return btnCntElem; 5975 5976 }; 5977 5978 thisApi.GetButtonElement = function () { 5979 5980 let btnElem = document.querySelector('button.gpay-button'); 5981 5982 return btnElem; 5983 5984 }; 5985 5986 thisApi.ShowButton = function () { 5987 5988 if (thisApi.GetContainerDivElement() != null) 5989 thisApi.GetContainerDivElement().show(); 5990 5991 if (thisApi.GetButtonElement() != null) 5992 thisApi.GetButtonElement().show(); 5993 5994 }; 5995 5996 thisApi.HideButton = function () { 5997 5998 if (thisApi.GetContainerDivElement() != null) 5999 thisApi.GetContainerDivElement().vanish(); 6000 6001 if (thisApi.GetButtonElement() != null) 6002 thisApi.GetButtonElement().vanish(); 6003 6004 }; 6005 6006 thisApi.GetGoogleIsReadyToPayRequest = function () { 6007 6008 return Object.assign( 6009 {}, 6010 baseRequest, 6011 { 6012 allowedPaymentMethods: [baseCardPaymentMethod] 6013 } 6014 ); 6015 6016 }; 6017 6018 thisApi.GetChargeAmount = function () { 6019 6020 let chargeAmt = 0.00; 6021 6022 if (typeof (thisApi.GetChargeAmountFunction) == 'function') 6023 chargeAmt = thisApi.GetChargeAmountFunction(); 6024 6025 return chargeAmt; 6026 6027 }; 6028 6029 thisApi.GetRecurringAmount = function () { 6030 6031 let recurringAmt = 0.00; 6032 6033 if (typeof (thisApi.GetRecurringAmountFunction) == 'function') 6034 recurringAmt = thisApi.GetRecurringAmountFunction(); 6035 6036 return recurringAmt; 6037 6038 }; 6039 6040 //Initialize 6041 thisApi.Initialize = function () { 6042
6043 _logger.Log('initialize started'); 6044 6045 let btnCntElem = thisApi.GetButtonContainerElement(); 6046 6047 if (btnCntElem != null) { 6048 6049 // prefetchGooglePaymentData(); // @todo prefetch payment data to improve performance after confirming site functionality 6050 6051 let gpc = thisApi.GetGooglePaymentsClient(); 6052 6053 gpc.isReadyToPay(thisApi.GetGoogleIsReadyToPayRequest()) 6054 .then(function (response) { 6055 6056 _logger.Log('initialize GooglePay button returned result: ' + response.result); 6057 6058 if (response.result) { 6059 6060 _logger.Log('add GooglePay button to DOM'); 6061 6062 let gpButton = 6063 gpc.createButton({ 6064 buttonType: 'donate', 6065 onClick: function () { 6066 6067 _logger.Log('GooglePay native button clicked'); 6068 6069 let uiValidated = true; 6070 6071 if (typeof (thisApi.UIValidationCallBackFunction) == 'function') 6072 uiValidated = thisApi.UIValidationCallBackFunction(); 6073 6074 if (uiValidated) { 6075 6076 _logger.Log('Hangle GooglePay button click using host: ' + gl_ce_pageHostName); 6077 6078 let paymentDataRequest = Object.assign({}, baseRequest); 6079 6080 paymentDataRequest.allowedPaymentMethods = [cardPaymentMethod]; 6081 6082 //https://developers.google.com/pay/api/web/guides/test-and-deploy/integration-checklist 6083 paymentDataRequest.merchantInfo = { 6084 merchantId: thisApi.GooglePayMerchantID, 6085 merchantName: thisApi.OrganizationName, 6086 merchantOrigin: gl_ce_pageHostName 6087 }; 6088 6089 let totalAmount = thisApi.GetChargeAmount(); 6090 6091 if (totalAmount <= 0 && thisApi.GetRecurringAmount() > 0) 6092 totalAmount = thisApi.GetRecurringAmount(); 6093 6094 paymentDataRequest.transactionInfo = { 6095 countryCode: thisApi.GetCountryCodeFunction(), 6096 currencyCode: thisApi.GetCurrencyCodeFunction(), 6097 totalPriceStatus: 'FINAL', 6098 totalPrice: totalAmount.toString() 6099 }; 6100 6101 gpc.loadPaymentData(paymentDataRequest) 6102 .then(function (paymentData) { 6103 6104 // handle the response 6105 let paymentToken = paymentData.paymentMethodData.tokenizationData.token; 6106 let payloadToken = JSON.parse(paymentToken); 6107 6108 let params = { 6109 "ajaxParams": { 6110 "signature": payloadToken.signature, 6111 "protocolVersion": payloadToken.protocolVersion, 6112 "signedMessage": payloadToken.signedMessage, 6113 "intermediateSigningKey": payloadToken.intermediateSigningKey 6114 } 6115 }; 6116 6117 App.BackgroundPostJsonAsync('/api/PublicWebAppUIAjaxEndpoint/GetGooglePayDecrytedPayoadToken', 6118 JSON.stringify(params), 6119 function (result) { 6120 6121 _logger.Log
6121('result: ' + JSON.stringify(result)); 6122 6123 if (result != null && result.Success) { 6124 6125 let paymentData = JSON.parse(result.DecrypedMessage); 6126 6127 _logger.Log(paymentData) 6128 6129 thisApi.FirePaymentAuthorizedEvent(paymentData); //handleGooglePayPaymentAuthorized(paymentData); 6130 6131 } 6132 else 6133 _logger.Log('result: ' + JSON.stringify(result)); 6134 6135 }, 6136 function (error) { 6137 _logger.Log('error: ' + error); 6138 }); 6139 6140 }) 6141 .catch(function (err) { 6142 console.error(err); 6143 }); 6144 6145 } 6146 else 6147 _logger.Log('GooglePay native button click aborted due to UI validation failure'); 6148 6149 }, 6150 allowedPaymentMethods: [baseCardPaymentMethod] 6151 }); 6152 6153 if (beginsWith(thisApi.GooglePayButtonContainerId, 'fieldset_')) { 6154 6155 let buttonContainer = document.createElement("div"); 6156 buttonContainer.id = 'googlePayButtonContainer'; 6157 buttonContainer.appendChild(gpButton); 6158 6159 btnCntElem.appendChild(buttonContainer); 6160 6161 } 6162 else 6163 btnCntElem.appendChild(gpButton); 6164 6165 thisApi.FireButtonAddedEvent(); //tryInitiateGooglePayThemeCallBack(); 6166 6167 } 6168 else 6169 _logger.Log('skip adding GooglePay button due to failed ready request'); 6170 6171 }) 6172 .catch(function (err) { 6173 console.error('initialize GooglePay button failed with error: ' + err); // show error in developer console for debugging 6174 }); 6175 6176 } 6177 6178 thisApi.FireInitializedEvent(); 6179 6180 }; 6181 6182 return thisApi; 6183 6184} 6185 6186function getApplePayApi(config) { 6187 6188 const thisApi = {}; 6189 6190 thisApi.ApplePayEnabled = config.ApplePayEnabled; 6191 6192 thisApi.ApplePayButtonDomId = 'ceApplePay'; 6193 6194 thisApi.ApplePayDivId = config.ApplePayDivId; 6195 thisApi.ApplePayButtonContainerId = config.ApplePayButtonContainerId; 6196 6197 thisApi.OrganizationName = config.OrganizationName; 6198 thisApi.UserCenterUrl = config.UserCenterUrl; 6199 6200 //UI interaction call backs 6201 thisApi.GetChargeAmountFunction = config.GetChargeAmountFunction; 6202 thisApi.GetRecurringAmountFunction = config.GetRecurringAmountFunction; 6203 thisApi.GetCurrencyCodeFunction = config.GetCurrencyCodeFunction; 6204 thisApi.GetCountryCodeFunction = config.GetCountryCodeFunction; 6205 6206 thisApi.UIValidationCallBackFunction = config.UIValidationCallBackFunction; 6207 6208 const _logger = getConsoleLogApi({ "LogSourceName": 'CE ApplePay Api' }); 6209 6210 thisApi.RegisterInitializedCallBack = function (callBackFunction) { 6211 6212 CallBackManager.add('applePayApiInitializedCallBack', callBackFunction, null); 6213 6214 }; 6215 6216 thisApi.FireInitializedEvent = function () { 6217 6218 CallBackManager.fire('applePayApiInitializedCallBack', {}, null); 6219 6220 }; 6221 6222 thisApi.RegisterPaymentAuthorizedCallBack = function (callBackFunction) { 6223 6224 CallBackManager.add('applePayApiPaymentAuthorizedCallBack', callBackFunction, null); 6225 6226 }; 6227 6228 thisApi.FirePaymentAuthorizedEvent = function (callBackParams) { 6229 6230 CallBackManager.fire('applePayApiPaymentAuthorizedCallBack', callBackParams, null); 6231 6232 }; 6233 6234 thisApi.IsApplePayApiAvailable = function () { 6235 6236 let retVal = false 6237 6238 try { 6239 if (window.top.ApplePaySession && window.top.ApplePaySession.canMakePayments() && thisApi.GetButtonElement() != null) 6240 retVal = true; 6241 } 6242 catch (err) {
6243 _logger.Log('IsApplePayApiAvailable error: ' + err.message); 6244 } 6245 6246 return retVal; 6247 6248 } 6249 6250 thisApi.GetButtonElement = function () { 6251 6252 let btnElem = null; 6253 6254 if (!isBlank(thisApi.ApplePayButtonDomId)) 6255 btnElem = document.getElementById(thisApi.ApplePayButtonDomId); 6256 6257 return btnElem; 6258 6259 }; 6260 6261 thisApi.GetContainerDivElement = function () { 6262 6263 let divCntElem = null; 6264 6265 if (!isBlank(thisApi.ApplePayDivId)) 6266 divCntElem = document.getElementById(thisApi.ApplePayDivId); 6267 6268 return divCntElem; 6269 6270 }; 6271 6272 thisApi.GetButtonContainerElement = function () { 6273 6274 let btnCntElem = null; 6275 6276 if (!isBlank(thisApi.ApplePayButtonContainerId)) 6277 btnCntElem = document.getElementById(thisApi.ApplePayButtonContainerId); 6278 6279 return btnCntElem; 6280 6281 }; 6282 6283 thisApi.ShowButton = function () { 6284 6285 if (thisApi.GetContainerDivElement() != null) 6286 thisApi.GetContainerDivElement().show(); 6287 6288 if (thisApi.GetButtonElement() != null) 6289 thisApi.GetButtonElement().show(); 6290 6291 }; 6292 6293 thisApi.HideButton = function () { 6294 6295 if (thisApi.GetContainerDivElement() != null) 6296 thisApi.GetContainerDivElement().vanish(); 6297 6298 if (thisApi.GetButtonElement() != null) 6299 thisApi.GetButtonElement().vanish(); 6300 6301 }; 6302 6303 thisApi.GetChargeAmount = function () { 6304 6305 let chargeAmt = 0.00; 6306 6307 if (typeof (thisApi.GetChargeAmountFunction) == 'function') 6308 chargeAmt = thisApi.GetChargeAmountFunction(); 6309 6310 return chargeAmt; 6311 6312 }; 6313 6314 thisApi.GetRecurringAmount = function () { 6315 6316 let recurringAmt = 0.00; 6317 6318 if (typeof (thisApi.GetRecurringAmountFunction) == 'function') 6319 recurringAmt = thisApi.GetRecurringAmountFunction(); 6320 6321 return recurringAmt; 6322 6323 }; 6324 6325 thisApi.GetApplePayTransactionObject = function () { 6326 6327 let apto = {}; 6328 6329 apto.countryCode = thisApi.GetCountryCodeFunction(); 6330 apto.currencyCode = thisApi.GetCurrencyCodeFunction(); 6331 apto.merchantCapabilities = ["supports3DS"]; 6332 apto.supportedNetworks = ["visa", "masterCard", "amex", "discover"]; 6333 6334 let chargeAmt = thisApi.GetChargeAmount(); 6335 if (chargeAmt > 0) { 6336 6337 _logger.Log('populate ApplePay charge amount transaction info (' + chargeAmt + ')'); 6338 6339 apto.total = { 6340 "label": thisApi.OrganizationName, 6341 "type": "final", 6342 "amount": chargeAmt 6343 }; 6344 6345 } 6346 else 6347 _logger.Log('no charge amount for ApplePay transaction info'); 6348 6349 let recurringAmt = thisApi.GetRecurringAmount(); 6350 if (recurringAmt > 0) { 6351 6352 _logger.Log('populate ApplePay recurring transaction info (' + recurringAmt + ')'); 6353 6354 apto.recurringPaymentRequest = { 6355 "paymentDescription": "Recurring donation.", 6356 "regularBilling": { 6357 "label": "Recurring", 6358 "amount": recurringAmt, 6359 "paymentTiming": "recurring", 6360 "recurringPaymentStartDate": getDateTimeISO() 6361 }, 6362 "billingAgreement": "You agree to allow our organization to bill your ApplePay account on a recurring basis for the amount you selected until you cancel the donation.", 6363 "managementURL": thisApi.UserCenterUrl //todo: needs to be client-specific 6364 //"tokenNotificationURL": "" //https://developer.apple.com/documentation/apple_pay_on_the_web/applepayrecurringpaymentrequest/3955958-tokennotificationurl 6365 }; 6366 6367 } 6368 else 6369 _logger.Log('no recurring amount for ApplePay transaction info'); 6370 6371 //if (typeof (callBackFunction) == 'function') 6372 // callBackFunction({}); 6373 6374 return apto; 6375 6376 }; 6377 6378 //Initialize 6379 thisApi.Initialize = function () { 6380 6381 _logger.Log('initialize started'); 6382 6383 let btnCntElem = thisApi.GetButtonContainerElement(); 6384 6385 if (btnCntElem != null) { 6386 6387 //create apple pay button 6388 let appelepaybutton = document.createElement("div"); 6389 appelepaybutton.id = thisApi.ApplePayButtonDomId;
6390 appelepaybutton.className = "apple-pay-button apple-pay-button-text-donate"; 6391 appelepaybutton.lang = "en"; 6392 6393 // Set the onClick listener on the Apple Pay button 6394 appelepaybutton.addEventListener('click', function () { 6395 6396 _logger.Log('ApplePay button clicked'); 6397 6398 let uiValidated = true; 6399 6400 if (typeof (thisApi.UIValidationCallBackFunction) == 'function') 6401 uiValidated = thisApi.UIValidationCallBackFunction(); 6402 6403 if (uiValidated && window.frame && window.frame !== window.top) { 6404 6405 window.parent.postMessage({ type: 'ce_fireEmbeddedApplePayButtonClickedEvent' }, '*'); 6406 6407 } 6408 else if (uiValidated) { 6409 6410 let request = thisApi.GetApplePayTransactionObject(); 6411 6412 let applePaySession = new ApplePaySession(6, request); 6413 6414 // This is the first event that Apple triggers. Here you need to validate the Apple Pay Session from your Back-End 6415 applePaySession.onvalidatemerchant = function (event) { 6416 6417 _logger.Log('handle IOS merchant validation (' + event.validationURL + ')'); 6418 6419 let params = { 6420 "ajaxParams": { 6421 "appleUrl": event.validationURL, 6422 "initiativeContext": gl_ce_pageHostName 6423 } 6424 }; 6425 6426 App.BackgroundPostJsonAsync('/api/PublicWebAppUIAjaxEndpoint/ApplePayMerchantValidation', 6427 JSON.stringify(params), 6428 function (result) { 6429 6430 _logger.Log('ApplePayMerchantValidation result: ' + JSON.stringify(result)); 6431 6432 try { 6433 applePaySession.completeMerchantValidation(JSON.parse(result)); 6434 } 6435 catch (ex) { 6436 _logger.Log('ApplePaySession completeMerchantValidation exception: ' + ex); 6437 } 6438 6439 }, 6440 function (error) { 6441 _logger.Log('ApplePayMerchantValidation error: ' + error); 6442 }); 6443 6444 }; 6445 6446 applePaySession.onshippingcontactselected = function (event) { 6447 6448 _logger.Log('handle IOS shipping contact selected'); 6449 6450 // populate with the availbale shipping methods for the region (Apple will tell you the region). 6451 let shipping = {}; 6452 6453 if (config.shop.shop_localisation.countryCode == 'GB') 6454 shipping = { methods: config.shipping.GB_region }; 6455 else 6456 shipping = { methods: config.shipping.WORLDWIDE_region }; 6457 6458 let newTotal = { 6459 type: 'final', 6460 label: config.shop.shop_name, 6461 amount: (parseFloat(config.shop.product_price) + parseFloat(shipping.methods[0].amount)).toFixed(2) 6462 }; 6463 6464 let newLineItems = [ 6465 { 6466 type: 'final', 6467 label: 'Subtotal', 6468 amount: config.shop.product_price 6469 }, 6470 { 6471 type: 'final', 6472 label: shipping.methods[0].label, 6473 amount: shipping.methods[0].amount 6474 } 6475 ]; 6476 6477 applePaySession.completeShippingContactSelection( 6478 ApplePaySession.STATUS_SUCCESS, 6479 shipping.methods, 6480 newTotal, 6481 newLineItems 6482 ); 6483 6484 }; 6485 6486 applePaySession.onshippingmethodselected = function (event) { 6487
6488 _logger.Log('handle IOS shipping method selected'); 6489 6490 let newTotal = { 6491 type: 'final', 6492 label: config.shop.shop_name, 6493 amount: (parseFloat(config.shop.product_price) + parseFloat(event.shippingMethod.amount)).toFixed(2) 6494 }; 6495 6496 let newLineItems = [ 6497 { 6498 type: 'final', 6499 label: 'Subtotal', 6500 amount: config.shop.product_price 6501 }, 6502 { 6503 type: 'final', 6504 label: event.shippingMethod.label, 6505 amount: event.shippingMethod.amount 6506 } 6507 ]; 6508 6509 applePaySession.completeShippingMethodSelection( 6510 ApplePaySession.STATUS_SUCCESS, 6511 newTotal, 6512 newLineItems 6513 ); 6514 6515 }; 6516 6517 applePaySession.onpaymentauthorized = function (event) { 6518 6519 _logger.Log('handle IOS payment authorized'); 6520 6521 let payloadToken = event.payment.token; 6522 6523 let params = { 6524 "ajaxParams": { 6525 "paymentData": payloadToken.paymentData, 6526 "paymentMethod": payloadToken.paymentMethod, 6527 "transactionIdentifier": payloadToken.transactionIdentifier 6528 } 6529 }; 6530 6531 App.BackgroundPostJsonAsync('/api/PublicWebAppUIAjaxEndpoint/ApplePayDecrytedPayoadToken', 6532 JSON.stringify(params), 6533 function (result) { 6534 6535 _logger.Log('ApplePayDecrytedPayoadToken result: ' + result); 6536 6537 if (result != null && result.Success) { 6538 6539 _logger.Log('ApplePayDecrytedPayoadToken DecrypedMessage: ' + result.DecrypedMessage); 6540 6541 let apPaymentKeys = JSON.parse(result.DecrypedMessage); 6542 6543 _logger.Log('DecrypedMessage.applicationPrimaryAccountNumber: ' + apPaymentKeys.applicationPrimaryAccountNumber); 6544 _logger.Log('DecrypedMessage.applicationExpirationDate: ' + apPaymentKeys.applicationExpirationDate); 6545 _logger.Log('DecrypedMessage.paymentData: ' + apPaymentKeys.paymentData); 6546 _logger.Log('DecrypedMessage.paymentData.onlinePaymentCryptogram: ' + apPaymentKeys.paymentData.onlinePaymentCryptogram); 6547 _logger.Log('DecrypedMessage.paymentData.eciIndicator: ' + apPaymentKeys.paymentData.eciIndicator); 6548 6549 applePaySession.completePayment(ApplePaySession.STATUS_SUCCESS); 6550 6551 thisApi.HideButton(); 6552
6553 //_logger.Log('payloadToken.PaymentMethod.type: ' + payloadToken.PaymentMethod.type); 6554 //_logger.Log('payloadToken.PaymentMethod.displayName: ' + payloadToken.PaymentMethod.displayName); 6555 //_logger.Log('payloadToken.PaymentMethod.network: ' + payloadToken.PaymentMethod.network); 6556 6557 let cbParam = {}; 6558 cbParam.PaymentMethod = payloadToken.paymentMethod.type; 6559 cbParam.CardType = payloadToken.paymentMethod.network; 6560 cbParam.CardDisplayName = payloadToken.paymentMethod.displayName; 6561 6562 cbParam.CardType_Id = 0; 6563 6564 //parse card type id 6565 switch (cbParam.CardType.toLowerCase()) { 6566 case 'visa': 6567 cbParam.CardType_Id = 1; 6568 break; 6569 case 'mastercard': 6570 cbParam.CardType_Id = 2; 6571 break; 6572 case 'amex': 6573 cbParam.CardType_Id = 3; 6574 break; 6575 case 'discover': 6576 cbParam.CardType_Id = 4; 6577 break; 6578 } 6579 6580 _logger.Log('cbParam.CardType_Id: ' + cbParam.CardType_Id); 6581 6582 cbParam.ApplePayToken = apPaymentKeys.applicationPrimaryAccountNumber; 6583 6584 //may be necessary for test apple pay wallets -- currently they are returning null for the ECI indicator 6585 if (apPaymentKeys.paymentData.eciIndicator != null) { 6586 cbParam.ApplePayTokenEciIndicator = apPaymentKeys.paymentData.eciIndicator; 6587 } 6588 else { 6589 switch (cbParam.CardType.toLowerCase()) { 6590 case 'visa': 6591 cbParam.ApplePayTokenEciIndicator = 7; 6592 break; 6593 case 'mastercard': 6594 cbParam.ApplePayTokenEciIndicator = 5; 6595 break; 6596 case 'amex': 6597 cbParam.ApplePayTokenEciIndicator = 5; 6598 break; 6599 case 'discover': 6600 cbParam.ApplePayTokenEciIndicator = 5; 6601 break; 6602 } 6603 } 6604 6605 cbParam.ExpirationMonth = ''; 6606 cbParam.ExpirationYear = ''; 6607 6608 if (!isBlank(apPaymentKeys.applicationExpirationDate) && apPaymentKeys.applicationExpirationDate.length == 6) { 6609 cbParam.ExpirationMonth = apPaymentKeys.applicationExpirationDate.substring(2, 4); 6610 cbParam.ExpirationYear = apPaymentKeys.applicationExpirationDate.substring(0, 2); 6611 } 6612 6613 _logger.Log('cbParam.ExpirationMonth: ' + cbParam.ExpirationMonth); 6614 _logger.Log('cbParam.ExpirationYear: ' + cbParam.ExpirationYear); 6615 6616 cbParam.ApplePayCryptogram = apPaymentKeys.paymentData.onlinePaymentCryptogram; 6617 6618 thisApi.FirePaymentAuthorizedEvent(cbParam); 6619 6620 } 6621 else 6622 applePaySession.completePayment(ApplePaySession.STATUS_FAILURE); 6623 6624 }, 6625 function (error) {
6626 _logger.Log('ApplePayDecrytedPayoadToken error: ' + error); 6627 applePaySession.completePayment(ApplePaySession.STATUS_FAILURE); 6628 }); 6629 6630 }; 6631 6632 _logger.Log('start ApplePay session'); 6633 6634 //begin IOS SESSION 6635 applePaySession.begin(); 6636 6637 } 6638 else 6639 _logger.Log('ApplePay session skipped due to UI validation error'); 6640 6641 }); 6642 6643 if (beginsWith(thisApi.ApplePayButtonContainerId, 'fieldset_')) { 6644 6645 let applePayButtonContainer = document.createElement("div"); 6646 applePayButtonContainer.id = 'applePayButtonContainer'; 6647 applePayButtonContainer.appendChild(appelepaybutton); 6648 6649 btnCntElem.appendChild(applePayButtonContainer); 6650 6651 } 6652 else 6653 btnCntElem.appendChild(appelepaybutton); 6654 6655 //this is a bug - why is the inner API showing the button, shouldn't the consumer? 6656 if (thisApi.IsApplePayApiAvailable()) 6657 thisApi.ShowButton(); 6658 else 6659 thisApi.HideButton(); 6660 6661 } 6662 6663 thisApi.FireInitializedEvent(); 6664 6665 }; 6666 6667 return thisApi; 6668 6669} 6670 6671function getChariotDafPayApi(config) { 6672 6673 const thisApi = {}; 6674 6675 thisApi.DafPayEnabled = config.DafPayEnabled; 6676 thisApi.DafPayButtonContainerDomId = config.DafPayButtonContainerDomId; 6677 thisApi.DafPayClientConnectId = config.DafPayClientConnectId; 6678 thisApi.GetChargeAmountFunction = config.GetChargeAmountFunction; 6679 thisApi.GetRecurringFrequencyIdFunction = config.GetRecurringFrequencyIdFunction; 6680 thisApi.UIValidationCallBackFunction = config.UIValidationCallBackFunction; 6681 thisApi.GetContactInformationFunction = config.GetContactInformationFunction; 6682 6683 let dafPayButtonContainer = 'dafPayButtonContainer'; 6684 let dafPayButtonId = 'dafPayButton'; 6685 6686 const _logger = getConsoleLogApi({ "LogSourceName": 'CE DafPay Api' }); 6687 6688 thisApi.GetButtonElement = function () { 6689 return document.getElementById(dafPayButtonId); 6690 }; 6691 6692 thisApi.GetButtonMasterContainerElement = function () { 6693 6694 let btnElem = document.getElementById(thisApi.DafPayButtonContainerDomId); 6695 6696 return btnElem; 6697 6698 }; 6699 6700 thisApi.GetButtonContainerElement = function () { 6701 6702 let btnElem = document.getElementById(dafPayButtonContainer); 6703 6704 return btnElem; 6705 6706 }; 6707 6708 thisApi.RegisterInitializedCallBack = function (callBackFunction) { 6709 6710 CallBackManager.add('dafPayApiInitializedCallBack', callBackFunction, null); 6711 6712 }; 6713 6714 thisApi.FireInitializedEvent = function () { 6715 6716 CallBackManager.fire('dafPayApiInitializedCallBack', {}, null); 6717 6718 }; 6719 6720 thisApi.RegisterButtonClickedCallBack = function (callBackFunction) { 6721 6722 CallBackManager.add('dafPayApiButtonClickedCallBack', callBackFunction, null); 6723 6724 }; 6725 6726 thisApi.FireButtonClickedEvent = function (callBackParams) { 6727 6728 CallBackManager.fire('dafPayApiButtonClickedCallBack', callBackParams, null); 6729 6730 }; 6731 6732 thisApi.RegisterPaymentAuthorizedCallBack = function (callBackFunction) { 6733 6734 CallBackManager.add('dafPayApiPaymentAuthorizedCallBack', callBackFunction, null); 6735 6736 }; 6737 6738 thisApi.FirePaymentAuthorizedEvent = function (callBackParams) { 6739 6740 CallBackManager.fire('dafPayApiPaymentAuthorizedCallBack', callBackParams, null); 6741 6742 }; 6743 6744 thisApi.RegisterPaymentFailedCallBack = function (callBackFunction) { 6745 6746 CallBackManager.add('dafPayApiPaymentFailedCallBack', callBackFunction, null); 6747 6748 }; 6749 6750 thisApi.FirePaymentFailedEvent = function (callBackParams) { 6751 6752 CallBackManager.fire('dafPayApiPaymentFailedCallBack', callBackParams, null); 6753 6754 }; 6755 6756 thisApi.ShowButton = function () { 6757 6758 //if (thisApi.GetButtonMasterContainerElement() != null) 6759 // thisApi.GetButtonMasterContainerElement().show(); 6760 6761 if (thisApi.GetButtonContainerElement() != null) 6762 thisApi.GetButtonContainerElement().show(); 6763 6764 if (thisApi.GetButtonElement() != null) 6765 thisApi.GetButtonElement().show(); //this has no effect is it is a fake tag 6766 6767 }; 6768 6769 thisApi.HideButton = function () { 6770 6771 //if (thisApi.GetButtonMasterContainerElement() != null) 6772 // thisApi.GetButtonMasterContainerElement().vanish(); 6773 6774 if (thisApi.GetButtonContainerElement() != null) 6775 thisApi.GetButtonContainerElement().vanish(); 6776 6777 if (thisApi.GetButtonElement() != null) 6778 thisApi.GetButtonElement().vanish(); //this has no effect is it is a fake tag 6779 6780 }; 6781 6782 //Initialize 6783 thisApi.Initialize = function () { 6784
6785 _logger.Log('initialize started'); 6786 6787 let btnContainer = thisApi.GetButtonMasterContainerElement(); 6788 if (btnContainer != null) { 6789 6790 if (!isBlank(thisApi.DafPayClientConnectId)) { 6791 6792 _logger.Log('configure DafPay button for connect id ' + thisApi.DafPayClientConnectId + ' in ' + thisApi.DafPayButtonContainerDomId); 6793 6794 let dpBtnCont = document.createElement('div'); 6795 dpBtnCont.id = dafPayButtonContainer; 6796 dpBtnCont.setAttribute('style', 'display:none;'); //hide by default 6797 6798 //<chariot-connect id="chariot" cid="GENERATED_CONNECT_IDENTIFIER"></chariot-connect> 6799 let tag = document.createElement('chariot-connect'); 6800 tag.id = dafPayButtonId; 6801 tag.setAttribute('cid', thisApi.DafPayClientConnectId); 6802 tag.setAttribute('style', 'width:auto;margin:0 auto;'); 6803 6804 dpBtnCont.appendChild(tag); 6805 6806 btnContainer.appendChild(dpBtnCont); 6807 6808 let dpBtn = thisApi.GetButtonElement(); 6809 6810 if (dpBtn != null) { 6811 6812 //optional event if needed to detect initialization 6813 dpBtn.addEventListener('CHARIOT_INIT', () => { 6814 6815 _logger.Log('DafPay init event fired'); 6816 6817 }); 6818 6819 //this is the key listener that fires back when a user either completes the DafPay app 6820 dpBtn.addEventListener('CHARIOT_SUCCESS', ({ detail }) => { 6821 6822 _logger.Log('DafPay user completion fired: ' + JSON.stringify(detail)); 6823 6824 //process transaction 6825 6826 let cbParam = {}; 6827 cbParam.DafPayResponse = detail; 6828 6829 thisApi.FirePaymentAuthorizedEvent(cbParam); 6830 6831 }); 6832 6833 //this is the key listener that fires back when a user abandons the DafPay app 6834 dpBtn.addEventListener('CHARIOT_EXIT', ({ detail }) => { 6835 6836 _logger.Log('DafPay user abandon fired: ' + JSON.stringify(detail)); 6837 6838 //do nothing for now 6839 6840 let cbParam = {}; 6841 cbParam.DafPayResponse = detail; 6842 6843 thisApi.FirePaymentFailedEvent(cbParam); 6844 6845 }); 6846 6847 //consider loading optional donor info via call backs 6848 dpBtn.onDonationRequest(async () => { 6849 6850 _logger.Log('DafPay button clicked (check out starting)'); 6851 6852 thisApi.FireButtonClickedEvent({}); 6853 6854 let uiValidated = true; 6855 6856 if (typeof (thisApi.UIValidationCallBackFunction) == 'function') 6857 uiValidated = thisApi.UIValidationCallBackFunction(); 6858 6859 let amtRaw = thisApi.GetChargeAmountFunction(); 6860 6861 if (typeof amtRaw === 'string') 6862 amtRaw = Number(stripCurrencyFormatting(amtRaw)); 6863 6864 let retVal = { 6865 "amount": parseFloat(amtRaw.toFixed(2).replace('.', '')), 6866 "firstName": thisApi.GetContactInformationFunction().DonorFirstName(), 6867 "lastName": thisApi.GetContactInformationFunction().DonorLastName(), 6868 "email": thisApi.GetContactInformationFunction().DonorEmail(), 6869 "phone": thisApi.GetContactInformationFunction().DonorPhone(), 6870 "address": { 6871 "line1": thisApi.GetContactInformationFunction().DonorAddress1(), 6872 "line2": thisApi.GetContactInformationFunction().DonorAddress2(), 6873 "city": thisApi.GetContactInformationFunction().DonorCity(), 6874 "state": thisApi.GetContactInformationFunction().DonorState(), 6875 "postalCode": thisApi.GetContactInformationFunction().DonorZipCode() 6876 } 6877 }; 6878 6879 if (!uiValidated) 6880 retVal = false; 6881 6882 return retVal; //false will cancel the donation flow 6883 6884 }); 6885 6886 } 6887 else
6888 _logger.Log('CRITICAL EXCEPTION accessing DafPay button after initialization'); 6889 6890 } 6891 else 6892 _logger.Log('unable to initialize DafPay button because connect id is missing'); 6893 6894 } 6895 else 6896 _logger.Log('unable to initialize DafPay button because container is not valid: ' + thisApi.DafPayButtonContainerDomId); 6897 6898 thisApi.FireInitializedEvent(); 6899 6900 }; 6901 6902 return thisApi; 6903 6904} 6905 6906/*Web Forms Api*/ 6907 6908function getFormApi(config) { 6909 6910 const thisApi = {}; 6911 6912 thisApi.Form_Id = config.Form_Id; 6913 thisApi.FormDomId = config.FormDomId; 6914 thisApi.HeaderMessagesContainerDomId = config.HeaderMessagesContainerDomId; 6915 thisApi.SubmitButtonDomId = config.SubmitButtonDomId; 6916 //thisApi.BackButtonDomId = config.BackButtonDomId; 6917 thisApi.ButtonsContainerDomId = config.ButtonsContainerDomId; 6918 6919 thisApi.AutoInitialize = (config.AutoInitialize != null) ? config.AutoInitialize : true; 6920 6921 thisApi.PayPalApi = config.PayPalApi; 6922 thisApi.ApplePayApi = config.ApplePayApi; 6923 thisApi.GooglePayApi = config.GooglePayApi; 6924 thisApi.DafPayApi = config.DafPayApi; 6925 6926 thisApi.PromoCodeFieldApi = null; 6927 thisApi.ContactPostalCodeFieldApi = null; 6928 thisApi.ContactSingleLineAddressApi = null; 6929 thisApi.TributeSingleLineAddressApi = null; 6930 6931 thisApi.OneClickCheckOutApi = null; 6932 6933 thisApi.TitleContainerDomId = ''; 6934 thisApi.HeaderContainerDomId = ''; 6935 thisApi.FooterContentDomId = ''; 6936 6937 //Core Third-Party Payment processing tracking global variables 6938 //let _walletTypeId = 0; 6939 let _payPalTokenId = ''; 6940 let _payPalGatewayPayerId = ''; 6941 let _payPalAmountChargedResponse = ''; 6942 6943 let _walletToken = ''; 6944 let _walletCreditCardType_id = 0; 6945 let _walletEciIndicator = ''; 6946 let _tokenExpirationMonth = ''; 6947 let _tokenExpirationYear = ''; 6948 let _tokenCryptogram = ''; 6949 6950 let _dafPayWorkflowSessionId = ''; 6951 6952 const _logger = getConsoleLogApi({ "LogSourceName": 'CE Web Forms 2.0 Api' }); 6953 6954 //Core Form Meta Properties & Framework 6955 //****************************************** 6956 6957 let _trans_id = 0; 6958 Object.defineProperty(thisApi, 'Transaction_Id', { 6959 get: function () { 6960 return _trans_id; 6961 } 6962 }); 6963 6964 let _transAmount = 0.00; 6965 Object.defineProperty(thisApi, 'ProcessedPaymentAmount', { 6966 get: function () { 6967 return _transAmount; 6968 } 6969 }); 6970 6971 //IMPORTANT: need new theme API framework and it needs a contract to communicate to the form API 6972 thisApi.ThemeRunning = false; 6973 6974 Object.defineProperty(thisApi, 'Id', { 6975 get: function () { 6976 return Form_Id; 6977 } 6978 }); 6979 6980 const _getFormName = function () { return WebFormContext.Form.Name; }; 6981 Object.defineProperty(thisApi, 'Name', { 6982 get: function () { 6983 return _getFormName(); 6984 } 6985 }); 6986 6987 const _getFormElement = function () { return document.getElementById(thisApi.FormDomId); }; 6988 Object.defineProperty(thisApi, 'FormElement', { 6989 get: function () { 6990 return _getFormElement(); 6991 } 6992 }); 6993 6994 const _getHeaderMessagesContainer = function () { return document.getElementById(thisApi.HeaderMessagesContainerDomId); }; 6995 Object.defineProperty(thisApi, 'HeaderMessagesContainerElement', { 6996 get: function () { 6997 return _getHeaderMessagesContainer(); 6998 } 6999 }); 7000 7001 const _getTitleContainerDomIdElement = function () { return document.getElementById(thisApi.TitleContainerDomId); }; 7002 Object.defineProperty(thisApi, 'TitleContainerElement', { 7003 get: function () { 7004 return _getTitleContainerDomIdElement(); 7005 } 7006 }); 7007 7008 const _getHeaderContainerDomIdElement = function () { return document.getElementById(thisApi.HeaderContainerDomId); }; 7009 Object.defineProperty(thisApi, 'HeaderContainerElement', { 7010 get: function () { 7011 return _getHeaderContainerDomIdElement(); 7012 } 7013 });
7014 7015 const _getFooterContainerDomIdElement = function () { return document.getElementById(thisApi.FooterContentDomId); }; 7016 Object.defineProperty(thisApi, 'FooterContainerElement', { 7017 get: function () { 7018 return _getFooterContainerDomIdElement(); 7019 } 7020 }); 7021 7022 const _getButtonsContainerElement = function () { return document.getElementById(thisApi.ButtonsContainerDomId); }; 7023 Object.defineProperty(thisApi, 'ButtonsContainerElement', { 7024 get: function () { 7025 return _getButtonsContainerElement(); 7026 } 7027 }); 7028 7029 const _getSubmitButtonElement = function () { return document.getElementById(thisApi.SubmitButtonDomId); }; 7030 Object.defineProperty(thisApi, 'SubmitButtonElement', { 7031 get: function () { 7032 return _getSubmitButtonElement(); 7033 } 7034 }); 7035 7036 //const _getBackButtonElement = function () { return document.getElementById(thisApi.BackButtonDomId); }; 7037 //Object.defineProperty(thisApi, 'BackButtonElement', { 7038 // get: function () { 7039 // return _getBackButtonElement(); 7040 // } 7041 //}); 7042 7043 thisApi.ShowButtons = function () { thisApi.ButtonsContainerElement.show(); }; 7044 thisApi.HideButtons = function () { thisApi.ButtonsContainerElement.vanish(); }; 7045 7046 thisApi.ShowSubmitButton = function () { thisApi.SubmitButtonElement.show(); }; 7047 thisApi.HideSubmitButton = function () { thisApi.SubmitButtonElement.vanish(); }; 7048 7049 thisApi.SetSubmitButtonText = function (text) { thisApi.SubmitButtonElement.innerHTML = text; }; 7050 7051 //thisApi.ShowBackButton = function () { thisApi.BackButtonElement.show(); }; 7052 //thisApi.HideBackButton = function () { thisApi.BackButtonElement.vanish(); }; 7053 7054 const _generateButton = function(domId, text, onClickJavascript) { 7055 7056 let btn = document.createElement('button'); 7057 btn.setAttribute('type', 'button'); 7058 btn.id = domId; 7059 btn.innerHTML = text; 7060 btn.addEventListener('click', function () { onClickJavascript() }); 7061 7062 return btn; 7063 7064 }; 7065 7066 thisApi.AddButton = function (domId, text, onClickJavascript) { 7067 7068 let btn = _generateButton(domId, text, onClickJavascript); 7069 7070 thisApi.ButtonsContainerElement.appendChild(btn); 7071 7072 }; 7073 7074 thisApi.AddButtonRight = function (domId, text, onClickJavascript) { thisApi.AddButton(domId, text, onClickJavascript); }; 7075 7076 thisApi.AddButtonLeft = function (domId, text, onClickJavascript) { 7077 7078 let btn = _generateButton(domId, text, onClickJavascript); 7079 7080 thisApi.ButtonsContainerElement.insertFirstElement(btn); 7081 7082 }; 7083 7084 thisApi.ShowButtonByDomId = function (domId) { if (document.getElementById(domId) != null) document.getElementById(domId).show(); }; 7085 thisApi.HideButtonByDomId = function (domId) { if (document.getElementById(domId) != null) document.getElementById(domId).vanish(); }; 7086 7087 const _tryGetFormContextIdOrZero = function (configVal) { let retVal = 0; if (configVal != null) retVal = configVal; return retVal; }; 7088 7089 //CORE Section & Field APIs (interactions API) 7090 //******************************************** 7091 7092 const _getInitializedFieldApi = function (fieldLookupFunction, defaultGetReturnValueFunction, setValueCallbackFunction, fieldValidationFailedCallbackFunction) { 7093 7094 let retVal = null; 7095 7096 if (typeof fieldLookupFunction == 'function') { 7097 7098 for (let x = 0; x < _allFieldApis.length; x++) 7099 if (fieldLookupFunction(_allFieldApis[x].Configuration)) { 7100 retVal = _allFieldApis[x]; 7101 break; 7102 } 7103 7104 if (retVal != null) { 7105 7106 retVal.InitializeDefaultGetReturnValueFunction(defaultGetReturnValueFunction); 7107 retVal.InitializeSetValueCallBackFunction(setValueCallbackFunction); 7108 retVal.InitializeFieldValidationFailedCallBackFunction(fieldValidationFailedCallbackFunction); 7109 7110 } 7111 //this means the field isn't configured on the form - not an issue 7112 //else
7113 // _logger.Log('unable to get initialized form field api - check lookup function (CRITICAL ERROR)'); 7114 7115 } 7116 else 7117 _logger.Log('get initialized form field api requires lookup function (CRITICAL ERROR)'); 7118 7119 return retVal; 7120 7121 }; 7122 7123 const _getFormSectionApi = function (positionNumber, configuration) { 7124 7125 let secApi = {}; 7126 7127 secApi.PositionNumber = positionNumber; 7128 7129 secApi.Configuration = configuration; 7130 7131 let _getDomElement = function () { return document.getElementById(secApi.Configuration.DomId); }; 7132 7133 Object.defineProperty(secApi, 'DomElement', { 7134 get: function () { 7135 return _getDomElement(); 7136 } 7137 }); 7138 7139 const _getFormFields = function () { 7140 7141 let retVal = []; 7142 7143 for (let x = 0; x < _allFieldApis.length; x++) 7144 if (_allFieldApis[x].Configuration.Section.Id == secApi.Configuration.Id) 7145 retVal.push(_allFieldApis[x]); 7146 7147 return retVal; 7148 7149 }; 7150 7151 Object.defineProperty(secApi, 'Fields', { 7152 get: function () { 7153 return _getFormFields(); 7154 } 7155 }); 7156 7157 secApi.Hide = function () { 7158 7159 if (secApi.DomElement != null) { 7160 secApi.DomElement.vanish(); 7161 secApi.DomElement.addClassName('ce-validationDisabled'); 7162 } 7163 7164 }; 7165 7166 secApi.Show = function () { 7167 7168 if (secApi.DomElement != null) { 7169 secApi.DomElement.show(); 7170 secApi.DomElement.removeClassName('ce-validationDisabled'); 7171 } 7172 7173 }; 7174 7175 secApi.Validate = function () { 7176 7177 let valFailCount = 0; 7178 7179 _logger.Log('validate ' + secApi.Fields.length + ' fields'); 7180 7181 for (let x = 0; x < secApi.Fields.length; x++) { 7182 7183 let validated = secApi.Fields[x].Validate(); 7184 if (!validated) 7185 valFailCount++; 7186 7187 } 7188 7189 return (valFailCount == 0); 7190 7191 }; 7192 7193 return secApi; 7194 7195 }; 7196 7197 //Core Field APIS (baseline critical dependency setup) 7198 //**************************************************** 7199 7200 /*App Core Web Form Field Api*/ 7201 7202 const _getFormFieldApi = function (configuration) { 7203 7204 const ffApi = {}; 7205 7206 ffApi.Configuration = configuration; 7207 7208 ffApi.DefaultGetReturnValueFunction = null; 7209 7210 ffApi.InitializeDefaultGetReturnValueFunction = function (defaultGetReturnValueFunction) { 7211 7212 if (typeof defaultGetReturnValueFunction != 'undefined' && ffApi.DefaultGetReturnValueFunction == null) 7213 ffApi.DefaultGetReturnValueFunction = defaultGetReturnValueFunction; 7214 7215 }; 7216 7217 ffApi.SetValueCallbackFunction = null; 7218 7219 ffApi.InitializeSetValueCallBackFunction = function (setValueCallbackFunction) { 7220 7221 if (typeof setValueCallbackFunction != 'undefined' && ffApi.SetValueCallbackFunction == null) 7222 ffApi.SetValueCallbackFunction = setValueCallbackFunction; 7223 7224 }; 7225 7226 ffApi.FieldValidationFailedCallbackFunction = null; 7227 7228 ffApi.InitializeFieldValidationFailedCallBackFunction = function (fieldValidationFailedCallbackFunction) { 7229 7230 if (typeof fieldValidationFailedCallbackFunction != 'undefined' && ffApi.FieldValidationFailedCallbackFunction == null) 7231 ffApi.FieldValidationFailedCallbackFunction = fieldValidationFailedCallbackFunction; 7232 7233 }; 7234 7235 const _configuredDefaultValue = function () { 7236 7237 let retVal = ''; 7238 7239 if (ffApi.Configuration != null && !isBlank(ffApi.Configuration.DefaultValue)) 7240 retVal = ffApi.Configuration.DefaultValue.toIntZeroIfInvalid(); 7241 7242 return retVal; 7243 7244 }; 7245 7246 const _isPaymentMethodBillingInfoField = function () { 7247 return (ffApi.Configuration.DataMapping.BillingAddressStreet1 || ffApi.Configuration.DataMapping.BillingAddressStreet2 || ffApi.Configuration.DataMapping.BillingAddressCity || ffApi.Configuration.DataMapping.BillingAddressState || ffApi.Configuration.DataMapping.BillingAddressPostalCode || ffApi.Configuration.DataMapping.BillingAddressCountry); 7248 }; 7249 Object.defineProperty(ffApi, 'IsPaymentMethodBillingInformation', { 7250 get: function () { 7251 return _isPaymentMethodBillingInfoField(); 7252 } 7253 }); 7254 7255 const _isTributeRecipientAddressField = function () { 7256 return (ffApi.Configuration.Type.Id == FORM_FIELD_TYPES.TRIBUTE_ACK_ADDRESS_1_
7256SYSTEM || ffApi.Configuration.Type.Id == FORM_FIELD_TYPES.TRIBUTE_ACK_ADDRESS_2_SYSTEM || ffApi.Configuration.Type.Id == FORM_FIELD_TYPES.TRIBUTE_ACK_CITY_SYSTEM || ffApi.Configuration.Type.Id == FORM_FIELD_TYPES.TRIBUTE_ACK_STATE_SYSTEM || ffApi.Configuration.Type.Id == FORM_FIELD_TYPES.TRIBUTE_ACK_POSTAL_CODE_SYSTEM || ffApi.Configuration.Type.Id == FORM_FIELD_TYPES.TRIBUTE_ACK_COUNTRY_SYSTEM); 7257 }; 7258 Object.defineProperty(ffApi, 'IsTributeRecipientAddressInformation', { 7259 get: function () { 7260 return _isTributeRecipientAddressField(); 7261 } 7262 }); 7263 7264 const _isTributeRecipientContactInfoField = function () { 7265 return (_isTributeRecipientAddressField() || ffApi.Configuration.Type.Id == FORM_FIELD_TYPES.TRIBUTE_ACK_PHONE_NUMBER_SYSTEM || ffApi.Configuration.Type.Id == FORM_FIELD_TYPES.TRIBUTE_ACK_EMAIL_SYSTEM); 7266 }; 7267 Object.defineProperty(ffApi, 'IsTributeRecipientContactInformation', { 7268 get: function () { 7269 return _isTributeRecipientContactInfoField(); 7270 } 7271 }); 7272 7273 const _isTributeCardConfigurationField = function () { 7274 return (ffApi.Configuration.Type.Id == FORM_FIELD_TYPES.ECARD_SYSTEM || ffApi.Configuration.Type.Id == FORM_FIELD_TYPES.PRINT_CARD_SYSTEM || ffApi.Configuration.Type.Id == FORM_FIELD_TYPES.PRINT_CARD_COUNT_SYSTEM); 7275 }; 7276 Object.defineProperty(ffApi, 'IsTributeCardConfiguration', { 7277 get: function () { 7278 return _isTributeCardConfigurationField(); 7279 } 7280 }); 7281 7282 const _isContactAddressField = function () { 7283 return (ffApi.Configuration.DataMapping.DonorAddressStreet1 || ffApi.Configuration.DataMapping.DonorAddressStreet2 || ffApi.Configuration.DataMapping.DonorAddressCity || ffApi.Configuration.DataMapping.DonorAddressState || ffApi.Configuration.DataMapping.DonorAddressPostalCode || ffApi.Configuration.DataMapping.DonorAddressCountry); 7284 }; 7285 Object.defineProperty(ffApi, 'IsContactAddressField', { 7286 get: function () { 7287 return _isContactAddressField(); 7288 } 7289 }); 7290 7291 const _isEnabled = function () {return ffApi.Configuration.ShowOnLandingPage;}; 7292 Object.defineProperty(ffApi, 'Enabled', { 7293 get: function () { 7294 return _isEnabled(); 7295 } 7296 }); 7297 7298 Object.defineProperty(ffApi, 'DefaultValue', { 7299 get: function () { 7300 return _configuredDefaultValue(); 7301 } 7302 }); 7303 7304 Object.defineProperty(ffApi, 'DefaultIntegerValue', { 7305 get: function () { 7306 return _configuredDefaultValue().toIntZeroIfInvalid(); 7307 } 7308 }); 7309 7310 Object.defineProperty(ffApi, 'DefaultDecimalValue', { 7311 get: function () { 7312 return _configuredDefaultValue().toDecimalZeroIfInvalid(); 7313 } 7314 }); 7315 7316 const _tryGetFieldDomElement = function (field) { 7317 7318 let retVal = null; 7319 7320 if (field != null) 7321 retVal = document.getElementById(field.DomId); 7322 7323 return retVal; 7324 7325 } 7326 7327 const _getDomElement = function () {return _tryGetFieldDomElement(ffApi.Configuration);}; 7328 Object.defineProperty(ffApi, 'DomElement', { 7329 get: function () { 7330 return _getDomElement(); 7331 } 7332 }); 7333 7334 const _tryGetFieldElementValue = function (fieldElem) { 7335 7336 let retVal = ''; 7337 7338 if (fieldElem != null) { 7339 7340 try { 7341 retVal = fieldElem.getValue(); 7342 } 7343 catch { 7344 _logger.Log('failed to get value for field ' + ffApi.Configuration.DomId + ' (incompatible value field)'); 7345 } 7346 7347 } 7348 7349 return retVal; 7350 7351 } 7352 7353 const _getUnAlteredFieldValue = function () {return _tryGetFieldElementValue(ffApi.DomElement);}; 7354 7355 const _getValue = function () { 7356 7357 let retVal = _getUnAlteredFieldValue(); 7358 7359 if (typeof ffApi.DefaultGetReturnValueFunction == 'function') 7360 retVal = ffApi.DefaultGetReturnValueFunction(retVal); 7361 7362 return retVal; 7363 7364 }; 7365 Object.defineProperty(ffApi, 'Value', { 7366 get: function () { 7367 return _getValue(); 7368 } 7369 }); 7370 7371 Object.defineProperty(ffApi, 'HasValue', { 7372 get: function () { 7373 return isBlank(ffApi.Value); 7374 } 7375 }); 7376 7377 let _fileNameValue = null; 7378 7379 ffApi.SetFileNameValue = function (val) {_fileNameValue = val;}; 7380 ffApi.GetFileNameValue = function () {return _fileNameValue;}; 7381 7382 let _fileBase64Value = null; 7383 7384 ffApi.SetBase64Value = function (val) {_fileBase64Value = val;}; 7385 ffApi.GetBase64Value = function () {return _fileBase64Value;}; 7386 7387 const _tryGetFieldElementText = function (fieldElem) { 7388 7389 let retVal = ''; 7390 7391 if (fieldElem != null) 7392 retVal = fieldElem.getText(); 7393 7394 return retVal; 7395 7396 } 7397 7398 const _getText = function () {return _tryGetFieldElementText(ffApi.DomElement);}; 7399 Object.defineProperty(ffApi, 'Text', { 7400 get: function () { 7401 return _getText(); 7402 } 7403 }); 7404 7405 const _trySetFieldElementValue = function (fieldElem, val) { 7406 7407 if (fieldElem != null) { 7408 7409 if (fieldElem.hasClassName('ce-displayFields')) 7410 fieldElem.innerHTML = val; 7411 else 7412 fieldElem.setValue(val); 7413 7414 } 7415 7416 } 7417 7418 ffApi.SetValue = function (val) { 7419 7420 _trySetFieldElementValue(ffApi.DomElement, val); 7421 7422 if (typeof ffApi.SetValueCallbackFunction == 'function') 7423 ffApi.SetValueCallbackFunction(); 7424 7425 }; 7426 7427 const _getSection = function () { 7428 7429 let retVal = null; 7430 7431 for (let x = 0;x < _allSectionApis.length;x++) 7432 if (_allSectionApis[x].Configuration.Id == ffApi.Configuration.Section.Id) { 7433 retVal = _allSectionApis[x]; 7434 break; 7435 } 7436 7437 return retVal; 7438 7439 }; 7440 Object.defineProperty(ffApi, 'Section', { 7441 get: function () { 7442 return _getSection(); 7443 } 7444 }); 7445 7446 const _getSectionConfiguration = function () { 7447 7448 let retVal = null; 7449 7450 if (ffApi.Section != null) 7451 retVal = ffApi.Section.Configuration; 7452 //for (let x = 0; x < WebFormContext.Form.Sections.length; x++) 7453 // if (WebFormContext.Form.Sections[x].Id == ffApi.Configuration.Section.Id) { 7454 // retVal = WebFormContext.Form.Sections[x]; 7455 // break; 7456 // } 7457 7458 return retVal; 7459 7460 }; 7461 Object.defineProperty(ffApi, 'SectionConfiguration', { 7462 get: function () { 7463 return _getSectionConfiguration(); 7464 } 7465 }); 7466 7467 const _getSectionDomElement = function () { 7468 7469 let retVal = null; 7470 7471 if (ffApi.DomElement != null) 7472 retVal = ffApi.DomElement.firstParentWithClass('ce-formSections'); 7473 7474 return retVal; 7475 7476 }; 7477 Object.defineProperty(ffApi, 'SectionDomElement', { 7478 get: function () { 7479 return _getSectionDomElement(); 7480 } 7481 }); 7482 7483 const _getLabelDomElement = function () { 7484 7485 let retVal = null; 7486 7487 if (ffApi.DomElement != null) 7488 retVal = getLabelForField(ffApi.DomElement); 7489 7490 return retVal; 7491 7492 }; 7493 Object.defineProperty(ffApi, 'LabelDomElement', { 7494 get: function () { 7495 return _getLabelDomElement(); 7496 } 7497 }); 7498 7499 const _getLabelText = function () { 7500 7501 let retVal = null; 7502 7503 if (ffApi.LabelDomElement != null) 7504 retVal = ffApi.LabelDomElement.innerText; 7505 7506 return retVal; 7507 7508 }; 7509 Object.defineProperty(ffApi, 'LabelText', { 7510 get: function () { 7511 return _getLabelText(); 7512 } 7513 }); 7514 7515 const _getPlaceholderText = function () { 7516 7517 let retVal = null; 7518 7519 if (ffApi.DomElement != null && ffApi.DomElement.placeholder) 7520 retVal = ffApi.DomElement.placeholder; 7521 7522 7523 return retVal; 7524 7525 }; 7526 Object.defineProperty(ffApi, 'PlaceholderText', { 7527 get: function () { 7528 return _getPlaceholderText(); 7529 } 7530 }); 7531 7532 const _getWrapperDomElement = function () { 7533 7534 let retVal = null; 7535 7536 if (ffApi.DomElement != null) 7537 retVal = ffApi.DomElement.firstParentWithClass('ce-formFieldWrappers'); 7538 7539 return retVal; 7540 7541 }; 7542 Object.defineProperty(ffApi, 'WrapperDomElement', { 7543 get: function () { 7544 return _getWrapperDomElement(); 7545 } 7546 }); 7547 7548 const _getRowDomElement = function () { 7549 7550 let retVal = null; 7551 7552 if (ffApi.DomElement != null) 7553 retVal = ffApi.DomElement.firstParentWithClass('ce-formRows'); 7554 7555 return retVal; 7556 7557 }; 7558 Object.defineProperty(ffApi, 'RowDomElement', { 7559 get: function () { 7560 return _getRowDomElement(); 7561 } 7562 }); 7563 7564 //const _isHidden = function () { 7565 // 7566 // let retVal = false; 7567 // 7568 // if (ffApi.DomElement != null && ffApi.DomElement.firstParentWithClass('ce-formFieldContainers') != null && ffApi.DomElement.firstParentWithClass('ce-formFieldContainers').hasClassName('ce-validationDisabled')) 7569 // retVal = true; 7570 // 7571 // return retVal; 7572 // 7573 //}; 7574 //Object.defineProperty(ffApi, 'Hidden', { 7575 // get: function () { 7576 // return _isHidden(); 7577 // } 7578 //}); 7579 // 7580 //const _isVisible = function () { return !_isHidden(); }; 7581 //Object.defineProperty(ffApi, 'Visible', { 7582 // get: function () { 7583 // return _isVisible(); 7584 // } 7585 //}); 7586 7587 const _rowHasVisibleFields = function () {return (ffApi.RowDomElement.querySelectorAll('.ce-formFieldContainers:not([style*="display: none"])').length > 0);}; 7588 Object.defineProperty(ffApi, 'RowHasVisibleFields', { 7589 get: function () { 7590 return _rowHasVisibleFields(); 7591 } 7592 }); 7593 7594 const _hideRow = function (disableValidation = true) { 7595 ffApi.RowDomElement.vanish(); 7596 if (disableValidation) 7597 ffApi.RowDomElement.addClassName('ce-validationDisabled'); 7598 }; 7599 7600 const _showRow = function () { 7601 ffApi.RowDomElement.show(); 7602 ffApi.RowDomElement.removeClassName('ce-validationDisabled'); 7603 }; 7604 7605 ffApi.Hide = function (disableValidation = true) { 7606 7607 if (ffApi.DomElement != null && ffApi.DomElement.firstParentWithClass('ce-formFieldContainers') != null) { 7608 7609 ffApi.DomElement.firstParentWithClass('ce-formFieldContainers').vanish(); 7610 if (disableValidation) 7611 ffApi.DomElement.firstParentWithClass('ce-formFieldContainers').addClassName('ce-validationDisabled'); 7612 7613 if (!ffApi.RowHasVisibleFields) 7614 _hideRow(disableValidation); 7615 7616 } 7617 7618 }; 7619 7620 ffApi.Show = function () { 7621 7622 //NOTE: never show hidden form fields!!! 7623 if (ffApi.DomElement != null && ffApi.DomElement.firstParentWithClass('ce-formFieldContainers') != null && !ffApi.DomElement.hasParentWithClass('ce-emptyRows')) { 7624 7625 ffApi.DomElement.firstParentWithClass('ce-formFieldContainers').show(); 7626 ffApi.DomElement.firstParentWithClass('ce-formFieldContainers').removeClassName('ce-validationDisabled'); 7627 7628 _showRow(); 7629 7630 } 7631 7632 }; 7633 7634 ffApi.HideLabel = function () { 7635 7636 let lbl = ffApi.LabelDomElement; 7637 if (lbl != null) 7638 lbl.vanish(); 7639 7640 }; 7641 7642 ffApi.ShowLabel = function () { 7643 7644 let lbl = ffApi.LabelDomElement; 7645 if (lbl != null) 7646 lbl.show(); 7647 7648 }; 7649 7650 ffApi.HideOption = function (fieldElemOpt) { 7651 7652 if (fieldElemOpt != null && fieldElemOpt.firstParentWithClass('ce-formFieldOptionItems') != null) 7653 fieldElemOpt.firstParentWithClass('ce-formFieldOptionItems').vanish(); 7654 7655 }; 7656 7657 ffApi.ShowOption = function (fieldElemOpt) { 7658 7659 if (fieldElemOpt != null && fieldElemOpt.firstParentWithClass('ce-formFieldOptionItems') != null) 7660 fieldElemOpt.firstParentWithClass('ce-formFieldOptionItems').show(); 7661 7662 }; 7663 7664 const _getOptions = function () { 7665 7666 let retVal = []; 7667 7668 if (ffApi.DomElement != null) 7669 retVal = getMultiSelectInputOptionElements(ffApi.DomElement); 7670 7671 return retVal; 7672 7673 }; 7674 7675 Object.defineProperty(ffApi, 'Options', { 7676 get: function () { 7677 return _getOptions(); 7678 } 7679 }); 7680 7681 Object.defineProperty(ffApi, 'OptionCount', { 7682 get: function () { 7683 return ffApi.Options.length; 7684 } 7685 }); 7686 7687 Object.defineProperty(ffApi, 'HasOptions', { 7688 get: function () { 7689 return (ffApi.OptionCount > 0); 7690 } 7691 }); 7692 7693 const _getSelectedOptions = function () { 7694 7695 let retVal = []; 7696 7697 for (let x = 0;x < ffApi.Options.length;x++) 7698 if (ffApi.Options[x].checked) 7699 retVal.push(ffApi.Options[x]); 7700 7701 return retVal; 7702 7703 }; 7704 7705 Object.defineProperty(ffApi, 'SelectedOptions', { 7706 get: function () { 7707 return _getSelectedOptions(); 7708 } 7709 }); 7710 7711 const _getSelectedOptionValues = function () { 7712 7713 let retVal = []; 7714 7715 for (let x = 0;x < ffApi.SelectedOptions.length;x++) 7716 retVal.push(ffApi.SelectedOptions[x].getValue(true)); //we don't want true/falst here (ever!) 7717 7718 return retVal; 7719 7720 }; 7721 7722 Object.defineProperty(ffApi, 'SelectedOptionValues', { 7723 get: function () { 7724 return _getSelectedOptionValues();
7725 } 7726 }); 7727 7728 const _getSelectedOptionIdValues = function () { 7729 7730 let retVal = []; 7731 7732 for (let x = 0;x < ffApi.SelectedOptionValues.length;x++) { 7733 _logger.Log('_getSelectedOptionIdValues ' + x + ': ' + ffApi.SelectedOptionValues[x]); 7734 retVal.push(ffApi.SelectedOptionValues[x].toString().toIntZeroIfInvalid()); 7735 } 7736 7737 return retVal; 7738 7739 }; 7740 7741 Object.defineProperty(ffApi, 'SelectedOptionIdValues', { 7742 get: function () { 7743 return _getSelectedOptionIdValues(); 7744 } 7745 }); 7746 7747 const _getOptionValues = function () { 7748 7749 let retVal = []; 7750 7751 if (ffApi.OptionCount > 0) 7752 for (let x = 0;x < ffApi.OptionCount;x++) 7753 retVal.push(ffApi.Options[x].value); 7754 7755 return retVal; 7756 7757 }; 7758 7759 Object.defineProperty(ffApi, 'OptionValues', { 7760 get: function () { 7761 return _getOptionValues(); 7762 } 7763 }); 7764 7765 const _getOptionIdValues = function () { 7766 7767 let retVal = []; 7768 7769 for (let x = 0;x < ffApi.OptionValues.length;x++) 7770 retVal.push(ffApi.OptionValues[x].toIntZeroIfInvalid()); 7771 7772 return retVal; 7773 7774 }; 7775 7776 Object.defineProperty(ffApi, 'OptionIdValues', { 7777 get: function () { 7778 return _getOptionIdValues(); 7779 } 7780 }); 7781 7782 const _getFirstOption = function () { 7783 7784 let retVal = null; 7785 7786 let opts = _getOptions(); 7787 if (opts.length > 0) 7788 retVal = opts[0]; 7789 7790 return retVal; 7791 7792 }; 7793 7794 Object.defineProperty(ffApi, 'FirstOption', { 7795 get: function () { 7796 return _getFirstOption(); 7797 } 7798 }); 7799 7800 ffApi.SelectFirstOption = function () { 7801 7802 if (ffApi.FirstOption != null) 7803 ffApi.SetValue(ffApi.FirstOption.value.toIntZeroIfInvalid()); 7804 //else 7805 // _logger.Log('unable to set first option value (element not found on form)'); 7806 7807 }; 7808 7809 ffApi.GetOptionByValue = function (val) { 7810 7811 let retVal = null; 7812 7813 if (ffApi.DomElement != null) 7814 retVal = getMultiSelectInputOptionElementByValue(ffApi.DomElement, val); 7815 7816 return retVal; 7817 7818 }; 7819 7820 ffApi.HideOptionByValue = function (val) { 7821 7822 let opt = ffApi.GetOptionByValue(val); 7823 if (opt != null) 7824 ffApi.HideOption(opt) 7825 7826 }; 7827 7828 const _getSelectedOptionInput = function () { 7829 7830 let retVal = null; 7831 7832 if (ffApi.DomElement != null) 7833 retVal = getMultiSelectInputSelectedOptionElement(ffApi.DomElement); 7834 7835 return retVal; 7836 7837 }; 7838 7839 Object.defineProperty(ffApi, 'SelectedOptionInput', { 7840 get: function () { 7841 return _getSelectedOptionInput(); 7842 } 7843 }); 7844 7845 Object.defineProperty(ffApi, 'SelectedOptionDomElement', { 7846 get: function () { 7847 7848 let retVal = null; 7849 7850 let optInput = ffApi.SelectedOptionInput; 7851 if (optInput != null) 7852 retVal = optInput.firstParentOrSelfWithClass('ce-formFieldOptionItems') 7853 7854 return retVal; 7855 7856 } 7857 }); 7858 7859 const _validateRequiredField = function (field) { 7860 7861 let retVal = true; 7862
7863 //_logger.Log('domId: ' + field.DomId); 7864 //_logger.Log('field.DomElement: ' + fieldElem.outerHTML); 7865 field.DomElement.validationSucceeded(); 7866 7867 switch (field.Configuration.Type.Id) { 7868 case FORM_FIELD_TYPES.RADIO_BUTTON: 7869 case FORM_FIELD_TYPES.CHECK_BOX: 7870 7871 let anySelected = false; 7872 7873 let inputs = field.DomElement.querySelectorAll('input'); 7874 for (let x = 0;x < inputs.length;x++) { 7875 7876 inputs[x].validationSucceeded(); 7877 7878 if (inputs[x].checked) { 7879 anySelected = true; 7880 break; 7881 } 7882 7883 } 7884 7885 if (!anySelected) { 7886 retVal = false; 7887 field.DomElement.validationFailed(); //base container 7888 for (let x = 0;x < inputs.length;x++) inputs[x].validationFailed(); //individual inputs (not sure if necessary) 7889 } 7890 7891 break; 7892 default: 7893 7894 if (isBlank(field.DomElement.getValue())) { 7895 retVal = false; 7896 field.DomElement.validationFailed(); 7897 } 7898 7899 break; 7900 } 7901 7902 return retVal; 7903 7904 }; 7905 7906 const _validateEmailField = function (field) { 7907 7908 let retVal = true; 7909
7910 //_logger.Log('domId: ' + field.DomId); 7911 //_logger.Log('field.DomElement: ' + fieldElem.outerHTML); 7912 field.DomElement.validationSucceeded(); 7913 7914 let fldVal = field.DomElement.getValue(); 7915 if (!isBlank(fldVal) && !isValidEmailAddress(fldVal)) { 7916 retVal = false; 7917 field.DomElement.validationFailed(); //base container 7918 } 7919 7920 return retVal; 7921 7922 }; 7923 7924 const _validateCreditCardExpirationDateField = function (field) { 7925 7926 let retVal = true; 7927 7928 //_logger.Log('domId: ' + field.DomId); 7929 //_logger.Log('field.DomElement: ' + fieldElem.outerHTML); 7930 field.DomElement.validationSucceeded(); 7931 7932 let fldVal = field.DomElement.getValue(); 7933 //always required 7934 if (isBlank(fldVal) || !isExpirationDateValidMMYYFormat(fldVal) || !isMMYYExpirationDateValid(fldVal)) { 7935 retVal = false; 7936 field.DomElement.validationFailed(); //base container 7937 } 7938 7939 return retVal; 7940 7941 }; 7942 7943 const _validateAmountField = function (field) { 7944 7945 let retVal = true; 7946 7947 field.DomElement.validationSucceeded(); 7948 7949 const fldVal = field.DomElement.getValue(); 7950 7951 if (!isBlank(fldVal) && !/^(\d+\.?\d{0,2}|\.\d{1,2})$/.test(fldVal.toString().trim())) { 7952 retVal = false; 7953 field.DomElement.validationFailed(); 7954 } 7955 7956 return retVal; 7957 7958 }; 7959 7960 ffApi.Validate = function () { 7961 7962 let retVal = true; 7963 7964 if (ffApi.DomElement != null) { 7965 7966 if (!ffApi.DomElement.hasParentWithClass('ce-validationDisabled')) { 7967 7968 if (retVal && ffApi.Configuration.Validation.Required) 7969 retVal = _validateRequiredField(ffApi); 7970 7971 if (retVal && ffApi.Configuration.Validation.Email) 7972 retVal = _validateEmailField(ffApi); 7973 7974 if (retVal && ffApi.Configuration.Type.Id == FORM_FIELD_TYPES.CREDIT_CARD_EXPIRATION_DATE) 7975 retVal = _validateCreditCardExpirationDateField(ffApi); 7976 7977 if (retVal && _isAnyAmountInputField(ffApi.Configuration)) 7978 retVal = _validateAmountField(ffApi); 7979 7980 if (!retVal) { 7981 if (typeof ffApi.FieldValidationFailedCallbackFunction == 'function') 7982 ffApi.FieldValidationFailedCallbackFunction(); 7983 7984 _logger.Log('validation failed for field ' + ffApi.Configuration.Id + ' (' + ffApi.Configuration.Name + ')'); 7985 } 7986 7987 } 7988 else 7989 _logger.Log('validation disabled for field ' + ffApi.Configuration.Id + ' (' + ffApi.Configuration.Name + ')'); 7990 7991 } 7992 7993 //not sure this is needed for drop downs (ffApi.Configuration.Validation.Currency) 7994 7995 return retVal; 7996 7997 }; 7998 7999 return ffApi; 8000 8001}; 8002 8003 const _allSectionApis = []; 8004 8005 for (let x = 0; x < WebFormContext.Form.Sections.length; x++) { 8006 8007 let newApi = _getFormSectionApi(x, WebFormContext.Form.Sections[x]); 8008 8009 _allSectionApis.push(newApi); 8010 8011 } 8012 8013 const _allFieldApis = []; 8014 8015 for (let x = 0; x < WebFormContext.Form.Fields.length; x++) { 8016 8017 let newApi = _getFormFieldApi(WebFormContext.Form.Fields[x]); 8018 8019 _allFieldApis.push(newApi); 8020 8021 } 8022 8023 //Core UI & Framework 8024 //****************************************** 8025 //****************************************** 8026 8027 //CORE WebFormContext Helper APIs (ROWS) 8028 //************************************** 8029 8030 const _createFormLabel = function (domId, text) { 8031 8032 let retVal = document.createElement('label'); 8033 8034 if (!isBlank(domId)) { 8035 retVal.setAttribute('for', domId); 8036 retVal.innerText = text; 8037 } 8038 8039 return retVal; 8040 8041 }; 8042 8043 const _createFormRow = function (domId, classes) { 8044 8045 let classVal = 'ce-formRows'; 8046
8047 if (!isBlank(classes)) 8048 classVal += ' ' + classes; 8049 8050 return createDomElement('div', domId, classVal); 8051 8052 }; 8053 8054 const _createFormFieldContainer = function (domId, classes) { 8055 8056 let classVal = 'ce-formFieldContainers'; 8057 8058 if (!isBlank(classes)) 8059 classVal += ' ' + classes; 8060 8061 return createDomElement('div', domId, classVal); 8062 8063 }; 8064 8065 const _createFormFieldWrapper = function (domId, classes) { 8066 8067 let classVal = 'ce-formFieldWrappers'; 8068 8069 if (!isBlank(classes)) 8070 classVal += ' ' + classes; 8071 8072 return createDomElement('div', domId, classVal); 8073 8074 }; 8075 8076 const _createRowWithSingleDisplayElement = function (domId, classes, displayElementDomId, label, displayElementClasses) { 8077 8078 let row = _createFormRow(domId, classes); 8079 8080 let fldCont = _createFormFieldContainer(domId + '_container', ''); 8081 8082 if (!isBlank(label)) 8083 fldCont.appendChild(_createFormLabel(displayElementDomId,label)); 8084 8085 let displayElem = document.createElement('span'); 8086 displayElem.id = displayElementDomId; 8087 displayElem.addClassNames(displayElementClasses); 8088 8089 fldCont.appendChild(displayElem); 8090 8091 row.appendChild(fldCont); 8092 8093 return row; 8094 8095 }; 8096 8097 const _addRowWithSingleDisplayElement = function (sectionElement, domId, classes, displayElementDomId, label, displayElementClasses) { 8098 8099 if (sectionElement != null) { 8100 8101 let row = _createRowWithSingleDisplayElement(domId, classes, displayElementDomId, label, displayElementClasses); 8102 8103 sectionElement.appendChild(row); 8104 8105 } 8106 8107 }; 8108 8109 const _createRowWithSingleTextInput = function (domId, classes, inputDomId, label, inputClasses, placeHolder) { 8110 8111 let row = _createFormRow(domId, classes); 8112 8113 let fldCont = _createFormFieldContainer(domId + '_container', ''); 8114 8115 if (!isBlank(label)) 8116 fldCont.appendChild(_createFormLabel(inputDomId,label)); 8117 8118 fldCont.addTextBox(inputDomId, inputClasses, null, placeHolder); 8119 8120 row.appendChild(fldCont); 8121 8122 return row; 8123 8124 }; 8125 8126 const _addRowWithSingleTextInput = function (sectionElement, domId, classes, inputDomId, label, inputClasses, placeHolder) { 8127 8128 if (sectionElement != null) { 8129 8130 let row = _createRowWithSingleTextInput(domId, classes, inputDomId, label, inputClasses, placeHolder); 8131 8132 sectionElement.appendChild(row); 8133 8134 } 8135 8136 }; 8137 8138 const _createRowWithSingleCheckBoxOption = function (domId, classes, checkBoxDomId, checkBoxValue, checkBoxText, checkBoxSelected, clickJavascript) { 8139 8140 let row = _createFormRow(domId, classes); 8141 8142 let opts = []; 8143 opts.push(createMultiSelectInputOptionConfigJson(checkBoxValue, checkBoxText, checkBoxSelected)); 8144 8145 row.addCheckBoxList(checkBoxDomId, clickJavascript, opts); 8146 8147 return row; 8148 8149 }; 8150 8151 const _addRowWithSingleCheckBoxOption = function (sectionElement, domId, classes, checkBoxDomId, checkBoxValue, checkBoxText, checkBoxSelected, clickJavascript) { 8152 8153 if (sectionElement != null) { 8154 8155 let row = _createRowWithSingleCheckBoxOption(domId, classes, checkBoxDomId, checkBoxValue, checkBoxText, checkBoxSelected, clickJavascript); 8156 8157 sectionElement.appendChild(row); 8158 8159 } 8160 8161 }; 8162 8163 const _createRowWithRadioButtonList = function (domId, classes, radioButtonListDomId, label, optionsCsv, selectedOptionValue, clickJavascript) { 8164 8165 let row = _createFormRow(domId, classes); 8166 8167 let fldCont = _createFormFieldContainer(domId + '_container', ''); 8168 8169 if (!isBlank(label)) 8170 fldCont.appendChild(_createFormLabel(radioButtonListDomId,label)); 8171 8172 let fldWarp = _createFormFieldWrapper(); 8173 8174 fldWarp.addRadioButtonListFromCsv(radioButtonListDomId, clickJavascript, optionsCsv, selectedOptionValue, null, null, 'ce-formFieldOptionItems'); 8175 8176 fldCont.appendChild(fldWarp);
8177 8178 row.appendChild(fldCont); 8179 8180 return row; 8181 8182 }; 8183 8184 const _createRowWithText = function (domId, classes, text, contentDomId) { 8185 8186 let row = _createFormRow(domId, classes); 8187 8188 row.addText(text, contentDomId); 8189 8190 return row; 8191 8192 }; 8193 8194 const _addRowWithText = function (sectionElement, domId, classes, text, contentDomId) { 8195 8196 if (sectionElement != null) { 8197 8198 let row = _createRowWithText(domId, classes, text, contentDomId); 8199 8200 sectionElement.appendChild(row); 8201 8202 } 8203 8204 }; 8205 8206 const _createRowWithHtml = function (domId, classes, html, contentDomId) { 8207 8208 let row = _createFormRow(domId, classes); 8209 8210 row.addHtml(html, contentDomId); 8211 8212 return row; 8213 8214 }; 8215 8216 const _addRowWithHtml = function (sectionElement, domId, classes, html, contentDomId) { 8217 8218 if (sectionElement != null) { 8219 8220 let row = _createRowWithHtml(domId, classes, html, contentDomId); 8221 8222 sectionElement.appendChild(row); 8223 8224 } 8225 8226 }; 8227 8228 //CORE WebFormContext Helper APIs (GENERIC SECTIONS) 8229 //************************************************** 8230 8231 Object.defineProperty(thisApi, 'Sections', { 8232 get: function () { 8233 return _allSectionApis; 8234 } 8235 }); 8236 8237 const _getSectionById = function (section_id) { 8238 8239 let retVal = null; 8240 8241 for (let x = 0; x < _allSectionApis.length; x++) 8242 if (_allSectionApis[x].Configuration.Id == section_id) { 8243 retVal = _allSectionApis[x]; 8244 break; 8245 } 8246 8247 return retVal; 8248 8249 }; 8250 8251 const _getSectionByIndex = function (sectionIndexPosition) { 8252 8253 let retVal = null; 8254 8255 for (let x = 0; x < _allSectionApis.length; x++) 8256 if (_allSectionApis[x].PositionNumber == sectionIndexPosition) { 8257 retVal = _allSectionApis[x]; 8258 break; 8259 } 8260 8261 return retVal; 8262 8263 }; 8264 8265 const _tryHideSectionElement = function (sectionElem) { 8266 8267 if (sectionElem != null) { 8268 sectionElem.vanish(); 8269 sectionElem.addClassName('ce-validationDisabled'); 8270 } 8271 8272 }; 8273 8274 const _tryShowSectionElement = function (sectionElem) { 8275 8276 if (sectionElem != null) { 8277 sectionElem.show(); 8278 sectionElem.removeClassName('ce-validationDisabled'); 8279 } 8280 8281 }; 8282 8283 const _tryHideSection = function (sectionConfig) { _tryHideSectionElement(document.getElementById(sectionConfig.DomId)); }; 8284 const _tryShowSection = function (sectionConfig) { _tryShowSectionElement(document.getElementById(sectionConfig.DomId)); }; 8285 8286 const _trySetFieldValue = function (field, val) { if (field != null) field.SetValue(val); }; 8287 const _tryGetFieldValue = function (field) { let retVal = null; if (field != null) retVal = field.Value; return retVal; }; 8288 const _tryGetFieldText = function (field) { let retVal = null; if (field != null) retVal = field.Text; return retVal; }; 8289 8290 const _tryGetFieldValueOrZero = function (field) { let retVal = 0; if (field != null) retVal = field.Value; return retVal; }; 8291 const _tryGetFieldValueOrFalse = function (field) { let retVal = false; if (field != null) retVal = Boolean(field.Value); return retVal; }; 8292 const _tryGetFieldBoolValueOrNull = function (field) { let retVal = null; if (field != null) retVal = Boolean(field.Value); return retVal; }; 8293 const _tryGetFieldDateTimeValue = function (field) { let retVal = null; if (field != null && field.Value != null) retVal = field.Value.toString().toDateTimeOrNull(); return retVal; }; 8294 const _tryGetFieldDotNetDateTimeValue = function (field) { let retVal = null; if (field != null) retVal = formatDotNetJsonDate(field.Value); return retVal; }; 8295 8296 const _tryGetIdFieldValueOrZero = function (field) { let retVal = 0; if (field != null) retVal = field.Value; return retVal; }; 8297 8298 const _tryGetDomInputTextValueOrNull = function (domInput) { let retVal = null; if (domInput != null) retVal = domInput.getValue(); return retVal; }; 8299 const _tryGetDomInputIntegerValueOrZero = function (domInput) { let retVal = 0; if (domInput != null) retVal = domInput.getValue().toIntZeroIfInvalid(); return retVal; }; 8300 const _tryGetDomInputDecimalValueOrZero = function (domInput) { let retVal = 0; if (domInput != null) retVal = domInput.getValue().toDecimalZeroIfInvalid(); return retVal; }; 8301 8302 //CORE WebFormContext Helper APIs (SPECIFIC SECTIONS) 8303 //*************************************************** 8304 8305 const _getDonationAmountSectionConfig = function () { 8306 8307 let retVal = null; 8308 8309 if (WebFormContext.Form.AmountSectionConfig != null) 8310 retVal = WebFormContext.Form.AmountSectionConfig 8311 8312 return retVal; 8313 8314 }; 8315
8316 Object.defineProperty(thisApi, 'DonationAmountSectionConfig', { 8317 get: function () { 8318 return _getDonationAmountSectionConfig(); 8319 } 8320 }); 8321 8322 Object.defineProperty(thisApi, 'PaymentSection', { 8323 get: function () { return thisApi.PaymentMethodField.Section; } 8324 }) 8325 8326 // {"PaymentSectionConfig":{"EnableApplePaySubscriptions":false,"EnablePayPalSubscription":false,"EnablePayPalSubscriptions":false,"IncludeAcH":true,"IncludeCVV":false,"IncludeCreditCard":true,"IncludeDafPay":true,"IncludeNameOnCard":true,"IncludePayPal":true} 8327 const _getPaymentSectionConfig = function () { 8328 8329 let retVal = null; 8330 8331 if (WebFormContext.Form.PaymentSectionConfig != null) 8332 retVal = WebFormContext.Form.PaymentSectionConfig 8333 8334 return retVal; 8335 8336 }; 8337 8338 Object.defineProperty(thisApi, 'PaymentSectionConfig', { 8339 get: function () { 8340 return _getPaymentSectionConfig(); 8341 } 8342 }); 8343 8344 // {"TributeSectionConfig":{"EmailAcknowledgementLabel":"Email Announcement","EnableEmailAcknowledgement":true,"EnableMailAcknowledgement":true,"MailAcknowledgementLabel":"Mail Announcement","NoAcknowledgementLabel":"No Announcement"} 8345 const _getTributeSectionConfig = function () { 8346 8347 let retVal = null; 8348 8349 if (WebFormContext.Form.TributeSectionConfig != null) 8350 retVal = WebFormContext.Form.TributeSectionConfig 8351 8352 return retVal; 8353 8354 }; 8355
8356 Object.defineProperty(thisApi, 'TributeSectionConfig', { 8357 get: function () { 8358 return _getTributeSectionConfig(); 8359 } 8360 }); 8361 8362 //CORE WebFormContext Helper APIs (GENERIC FIELDS) 8363 //************************************************ 8364 8365 Object.defineProperty(thisApi, 'Fields', { 8366 get: function () { 8367 return _allFieldApis; 8368 } 8369 }); 8370 8371 const _getFieldById = function (field_id) { 8372 8373 let retVal = null; 8374 8375 for (let x = 0; x < _allFieldApis.length; x++) 8376 if (_allFieldApis[x].Configuration.Id == field_id) { 8377 retVal = _allFieldApis[x]; 8378 break; 8379 } 8380 8381 return retVal; 8382 8383 }; 8384 8385 const _getFieldByDomId = function (domId) { 8386 8387 let retVal = null; 8388 8389 if (!isBlank(domId)) { 8390 8391 for (let x = 0; x < _allFieldApis.length; x++) 8392 if (_allFieldApis[x].Configuration.DomId == domId) { 8393 retVal = _allFieldApis[x]; 8394 break; 8395 } 8396 8397 } 8398 8399 return retVal; 8400 8401 }; 8402 8403 const _getFieldElementFieldId = function (fieldOrChildElement) { 8404 8405 let retVal = 0; 8406 8407 if (fieldOrChildElement != null) 8408 retVal = fieldOrChildElement.tryGetIntegerAttribute('data-field-id'); 8409 8410 return retVal; 8411 8412 }; 8413 8414 const _tryHideField = function (field) { if (field != null) field.Hide(); }; 8415 const _tryShowField = function (field) { if (field != null) field.Show(); }; 8416 8417 const _tryShowFieldIfHasOptions = function (field) { if (field != null && field.HasOptions) field.Show(); }; 8418 8419 //CORE WebFormContext Helper APIs (SPECIFIC FIELDS) 8420 //************************************************* 8421 8422 //rule: ignore legacy mapped ticket quantity (we're going to derive membership info from type) 8423 const _isGenericAmountInputField = function (fieldConfig) { return (fieldConfig.DataMapping.TransactionAmount && fieldConfig.Type.Id != FORM_FIELD_TYPES.TICKET_QUANTITY_SYSTEM); }; 8424 8425 const _getGenericAmountInputFields = function () { 8426 8427 let retVal = []; 8428 8429 for (let x = 0; x < _allFieldApis.length; x++) 8430 if (_isGenericAmountInputField(_allFieldApis[x].Configuration)) { 8431 8432 _allFieldApis[x].InitializeDefaultGetReturnValueFunction(function (rawVal) { return rawVal.toDecimalZeroIfInvalid(); }); 8433 8434 //console.log('get generic input amount field API for ' + _allFieldApis[x].Configuration.DomId); 8435 //console.log('call back ' + _allFieldApis[x].SetValueCallbackFunction); 8436 8437 _allFieldApis[x].InitializeSetValueCallBackFunction(function () { _handlePaymentAmountChanged(); }); 8438 8439 retVal.push(_allFieldApis[x]); 8440 8441 } 8442 8443 return retVal; 8444 8445 }; 8446 Object.defineProperty(thisApi, 'GenericAmountInputFields', { get: function () { return _getGenericAmountInputFields(); } }); 8447 8448 const _getGenericAmountInputAmounts = function () { 8449 8450 let retVal = []; 8451 8452 for (let x = 0; x < thisApi.GenericAmountInputFields.length; x++) 8453 retVal.push(thisApi.GenericAmountInputFields[x].Value); 8454 8455 return retVal; 8456 8457 }; 8458
8459 Object.defineProperty(thisApi, 'GenericAmountInputAmounts', { get: function () { return _getGenericAmountInputAmounts(); } }); 8460 8461 const _getGenericAmountInputAmount = function () { 8462 8463 let retVal = 0.00; 8464 8465 for (let x = 0; x < thisApi.GenericAmountInputAmounts.length; x++) 8466 retVal += thisApi.GenericAmountInputAmounts[x]; 8467 8468 return retVal; 8469 8470 }; 8471 Object.defineProperty(thisApi, 'GenericAmountInputsAmount', { get: function () { return _getGenericAmountInputAmount(); } }); 8472 8473 Object.defineProperty(thisApi, 'FirstGenericAmountInputField', { get: function () { let retVal = null; if (thisApi.GenericAmountInputFields.length > 0) retVal = thisApi.GenericAmountInputFields[0]; return retVal; } }); 8474 Object.defineProperty(thisApi, 'FirstGenericAmountInputAmount', { get: function () { return _tryGetFieldValueOrZero(thisApi.FirstGenericAmountInputField); } }); 8475 8476 //deprecated in favor of FirstGenericAmountInputField to support transparent multi-other amounts with clear dividion of business logic between things like additional donation and membership payment (11.30.25) 8477 //thisApi.PaymentAmountField = _getInitializedFieldApi(_isGenericAmountInputField, function (rawVal) { return rawVal.toDecimalZeroIfInvalid(); }, function () { _handle_XXX_PaymentAmountChanged(); }); 8478 //Object.defineProperty(thisApi, 'PaymentAmount', { get: function () { return _tryGetFieldValueOrZero(thisApi.PaymentAmountField); } }); 8479 8480 const _isAnyAmountInputField = function (fieldConfig) { 8481 return ( 8482 (fieldConfig.DataMapping.TransactionAmount || 8483 fieldConfig.DataMapping.TaxDeductibleAmount || 8484 fieldConfig.DataMapping.RecurringAmount || 8485 fieldConfig.DataMapping.RecurringTaxDeductibleAmount || 8486 fieldConfig.DataMapping.RecurringMaxAmount) 8487 && fieldConfig.Type.Id != FORM_FIELD_TYPES.TICKET_QUANTITY_SYSTEM 8488 ); 8489 } 8490 8491 const _getGenericTaxDeductibleAmountInputFields = function () { 8492 8493 let retVal = []; 8494 8495 for (let x = 0; x < _allFieldApis.length; x++) 8496 if (_allFieldApis[x].Configuration.DataMapping.TaxDeductibleAmount && _allFieldApis[x].Configuration.Type.Id != FORM_FIELD_TYPES.TICKET_QUANTITY_SYSTEM) { 8497 8498 _allFieldApis[x].InitializeDefaultGetReturnValueFunction(function (rawVal) { return rawVal.toDecimalZeroIfInvalid(); }); 8499 //_allFieldApis[x].InitializeSetValueCallBackFunction(function () { _handle_XXX_PaymentAmountChanged(); }); 8500 8501 retVal.push(_allFieldApis[x]); 8502 8503 } 8504 8505 return retVal; 8506 8507 }; 8508 Object.defineProperty(thisApi, 'GenericTaxDeductibleAmountInputFields', { get: function () { return _getGenericTaxDeductibleAmountInputFields(); } }); 8509 8510 const _getGenericTaxDeductibleAmountInputAmounts = function () { 8511 8512 let retVal = []; 8513 8514 for (let x = 0; x < thisApi.GenericTaxDeductibleAmountInputFields.length; x++) 8515 retVal.push(thisApi.GenericTaxDeductibleAmountInputFields[x].Value); 8516 8517 return retVal; 8518 8519 }; 8520
8521 Object.defineProperty(thisApi, 'GenericTaxDeductibleAmountInputAmounts', { get: function () { return _getGenericTaxDeductibleAmountInputAmounts(); } }); 8522 8523 const _getGenericTaxDeductibleAmountInputAmount = function () { 8524 8525 let retVal = 0.00; 8526 8527 for (let x = 0; x < thisApi.GenericTaxDeductibleAmountInputAmounts.length; x++) 8528 retVal += thisApi.GenericTaxDeductibleAmountInputAmounts[x]; 8529 8530 return retVal; 8531 8532 }; 8533 Object.defineProperty(thisApi, 'GenericTaxDeductibleAmountInputsAmount', { get: function () { return _getGenericTaxDeductibleAmountInputAmount(); } }); 8534 8535 Object.defineProperty(thisApi, 'FirstGenericTaxDeductibleAmountInputField', { get: function () { let retVal = null; if (thisApi.GenericTaxDeductibleAmountInputFields.length > 0) retVal = thisApi.GenericTaxDeductibleAmountInputFields[0]; return retVal; } }); 8536 Object.defineProperty(thisApi, 'FirstGenericTaxDeductibleAmountInputAmount', { get: function () { return _tryGetFieldValueOrZero(thisApi.FirstGenericTaxDeductibleAmountInputField); } }); 8537 8538 //deprecated in favor of FirstGenericTaxDeductibleAmountInputField to support transparent multi-other amounts with clear dividion of business logic between things like additional donation and membership payment (11.30.25) 8539 //thisApi.PaymentTaxDeductibleAmountField = _getInitializedFieldApi(function (fieldConfig) { return fieldConfig.DataMapping.TaxDeductibleAmount; }, function (rawVal) { return rawVal.toDecimalZeroIfInvalid(); }); 8540 //Object.defineProperty(thisApi, 'PaymentTaxDeductibleAmount', { get: function () { return _tryGetFieldValueOrZero(thisApi.PaymentTaxDeductibleAmountField); } }); 8541 8542 const _isPaymentCurrencyField = function (fieldConfig) { return (fieldConfig.Type.Id == FORM_FIELD_TYPES.CURRENCY_SYSTEM); }; 8543 8544 thisApi.PaymentCurrencyField = _getInitializedFieldApi(_isPaymentCurrencyField, 8545 function (rawVal) { 8546 8547 let retVal = rawVal.toIntZeroIfInvalid(); 8548 8549 return retVal; 8550 8551 }, 8552 function () { _handleCurrencyChanged(); }); 8553 Object.defineProperty(thisApi, 'PaymentCurrencyId', { get: function () { return _tryGetFieldValueOrZero(thisApi.PaymentCurrencyField); } }); 8554 Object.defineProperty(thisApi, 'PaymentCurrencyCode', { get: function () { return _tryGetFieldText(thisApi.PaymentCurrencyField); } }); 8555 8556 thisApi.RecurringPaymentAmountField = _getInitializedFieldApi(function (fieldConfig) { return fieldConfig.DataMapping.RecurringAmount; }, function (rawVal) { return rawVal.toDecimalZeroIfInvalid(); }); //OVERRIDING ONE-TIME MAPPING, need better solution , function () { _handleRecurringPaymentAmountChanged(); } 8557 Object.defineProperty(thisApi, 'RecurringPaymentAmount', { get: function () { return thisApi.RecurringPaymentAmountField.Value; } }); 8558 8559 thisApi.RecurringPaymentTaxDeductibleAmountField = _getInitializedFieldApi(function (fieldConfig) { return fieldConfig.DataMapping.RecurringTaxDeductibleAmount; }, function (rawVal) { return rawVal.toDecimalZeroIfInvalid(); }); 8560 Object.defineProperty(thisApi, 'RecurringPaymentTaxDeductibleAmount', { get: function () { return _tryGetFieldValueOrZero(thisApi.RecurringPaymentTaxDeductibleAmountField); } }); 8561 8562 thisApi.RecurringPaymentStartDateField = _getInitializedFieldApi(function (fieldConfig) { return fieldConfig.DataMapping.RecurringStartDate; }, function (rawVal) { return rawVal.toDateTimeOrNull(); }); 8563 Object.defineProperty(thisApi, 'RecurringPaymentStartDate', { get: function () { return _tryGetFieldDateTimeValue(thisApi.RecurringPaymentStartDateField); } }); 8564 8565 Object.defineProperty(thisApi, 'RecurringPaymentStartDateAjaxFormat', { get: function () { return _tryGetFieldDotNetDateTimeValue(thisApi.RecurringPaymentStartDateField); } }); 8566 8567 thisApi.RecurringPaymentEndDateField = _getInitializedFieldApi(function (fieldConfig) { return fieldConfig.DataMapping.RecurringEndDate; }, function (rawVal) { return rawVal.toDateTimeOrNull(); }); 8568 Object.defineProperty(thisApi, 'RecurringPaymentEndDate', { get: function () { return _tryGetFieldDateTimeValue(thisApi.RecurringPaymentEndDateField); } }); 8569
8570 Object.defineProperty(thisApi, 'RecurringPaymentEndDateAjaxFormat', { get: function () { return _tryGetFieldDotNetDateTimeValue(thisApi.RecurringPaymentEndDateField); } }); 8571 8572 thisApi.RecurringMaxAmountField = _getInitializedFieldApi(function (fieldConfig) { return fieldConfig.DataMapping.RecurringMaxAmount; }, function (rawVal) { return rawVal.toDecimalZeroIfInvalid(); }); 8573 Object.defineProperty(thisApi, 'RecurringMaxAmount', { get: function () { return _tryGetFieldValueOrZero(thisApi.RecurringMaxAmountField); } }); 8574 8575 const _isRecurringFrequencyField = function (fieldConfig) { return fieldConfig.DataMapping.RecurringFrequency; }; 8576 8577 thisApi.RecurringPaymentFrequencyField = _getInitializedFieldApi(_isRecurringFrequencyField, function (rawVal) { return rawVal.toIntZeroIfInvalid(); }, function () { _handlePaymentFrequencyChanged(); }); 8578 Object.defineProperty(thisApi, 'RecurringPaymentFrequencyId', { get: function () { return _tryGetFieldValueOrZero(thisApi.RecurringPaymentFrequencyField); } }); 8579 8580 const _isPaymentMethodField = function (fieldConfig) { return fieldConfig.DataMapping.PaymentMethod; }; 8581 8582 thisApi.PaymentMethodField = _getInitializedFieldApi(_isPaymentMethodField, function (rawVal) { return rawVal.toIntZeroIfInvalid(); }, function () { _handlePaymentMethodChanged(); }); 8583 Object.defineProperty(thisApi, 'PaymentMethodId', { get: function () { return _tryGetFieldValueOrZero(thisApi.PaymentMethodField); } }); 8584 8585 const _getAvailablePaymentMethodIds = function () { 8586 8587 let retVal = []; 8588 8589 if (thisApi.PaymentMethodField != null) { 8590 8591 if (thisApi.PaymentMethodField.HasOptions) 8592 retVal = thisApi.PaymentMethodField.OptionIdValues; 8593 else 8594 retVal.push(thisApi.PaymentMethodField.Value); 8595 8596 } 8597 8598 return retVal; 8599 8600 }; 8601 Object.defineProperty(thisApi, 'AvailablePaymentMethodIds', { get: function () { return _getAvailablePaymentMethodIds(); } }); 8602 8603 thisApi.CreditCardTypeField = _getInitializedFieldApi(function (fieldConfig) { return (fieldConfig.Type.Id == FORM_FIELD_TYPES.CREDIT_CARD_TYPES_SYSTEM || fieldConfig.DataMapping.CreditCardType); }, function (rawVal) { return rawVal.toIntZeroIfInvalid(); }); 8604 8605 thisApi.CreditCardNumberField = _getInitializedFieldApi(function (fieldConfig) { return fieldConfig.DataMapping.CreditCardNumber; }); 8606 Object.defineProperty(thisApi, 'CreditCardNumber', { get: function () { return _tryGetFieldValue(thisApi.CreditCardNumberField); } }); 8607 8608 thisApi.CreditCardHolderNameField = _getInitializedFieldApi(function (fieldConfig) { return fieldConfig.DataMapping.CreditCardNameOnAccount; }); 8609 Object.defineProperty(thisApi, 'CreditCardHolderName', { get: function () { return _tryGetFieldValue(thisApi.CreditCardHolderNameField); } }); 8610 8611 thisApi.CreditCardExpirationDateField = _getInitializedFieldApi(function (fieldConfig) { return (fieldConfig.Type.Id == FORM_FIELD_TYPES.CREDIT_CARD_EXPIRATION_DATE); }); 8612 Object.defineProperty(thisApi, 'CreditCardExpirationDate', { get: function () { return _tryGetFieldValue(thisApi.CreditCardExpirationDateField); } }); 8613 8614 thisApi.CreditCardExpirationMonthField = _getInitializedFieldApi(function (fieldConfig) { return (fieldConfig.Type.Id == fieldConfig.DataMapping.CREDIT_CARD_EXP_MONTHS_SYSTEM || fieldConfig.DataMapping.CreditCardExpirationMonth); }); 8615 Object.defineProperty(thisApi, 'CreditCardExpirationMonth', { get: function () { return _tryGetFieldValue(thisApi.CreditCardExpirationMonthField); } }); 8616 8617 thisApi.CreditCardExpirationYearField = _getInitializedFieldApi(function (fieldConfig) { return (fieldConfig.Type.Id == fieldConfig.DataMapping.CREDIT_CARD_EXP_YEARS_SYSTEM || fieldConfig.DataMapping.CreditCardExpirationYear); }); 8618 Object.defineProperty(thisApi, 'CreditCardExpirationYear', { get: function () { return _tryGetFieldValue(thisApi.CreditCardExpirationYearField); } }); 8619 8620 thisApi.CreditCardSecurityCodeField = _getInitializedFieldApi(function (fieldConfig) { return fieldConfig.DataMapping.CreditCardSecurityCode; });
8621 Object.defineProperty(thisApi, 'CreditCardSecurityCode', { get: function () { return _tryGetFieldValue(thisApi.CreditCardSecurityCodeField); } }); 8622 8623 thisApi.BankAccountNumberField = _getInitializedFieldApi(function (fieldConfig) { return fieldConfig.DataMapping.BankAccountNumber; }); 8624 Object.defineProperty(thisApi, 'BankAccountNumber', { get: function () { return _tryGetFieldValue(thisApi.BankAccountNumberField); } }); 8625 8626 thisApi.BankAccountTypeField = _getInitializedFieldApi(function (fieldConfig) { return fieldConfig.DataMapping.BankAccountType; }); 8627 Object.defineProperty(thisApi, 'BankAccountTypeId', { get: function () { return _tryGetFieldValueOrZero(thisApi.BankAccountTypeField); } }); 8628 8629 thisApi.BankRoutingNumberField = _getInitializedFieldApi(function (fieldConfig) { return fieldConfig.DataMapping.BankRoutingNumber; }); 8630 Object.defineProperty(thisApi, 'BankRoutingNumber', { get: function () { return _tryGetFieldValue(thisApi.BankRoutingNumberField); } }); 8631 8632 thisApi.BankAccountNameOnAccountField = _getInitializedFieldApi(function (fieldConfig) { return fieldConfig.DataMapping.BankAccountNameOnAccount; }); 8633 Object.defineProperty(thisApi, 'BankAccountNameOnAccount', { get: function () { return _tryGetFieldValue(thisApi.BankAccountNameOnAccountField); } }); 8634 8635 thisApi.ACHTransactionTypeField = _getInitializedFieldApi(function (fieldConfig) { return fieldConfig.DataMapping.ACHTransactionType; }, function (rawVal) { return rawVal.toIntZeroIfInvalid(); }); 8636 thisApi.BankNameField = _getInitializedFieldApi(function (fieldConfig) { return fieldConfig.DataMapping.BankName; }); 8637 8638 //InKind 8639 thisApi.InKindTypeField = _getInitializedFieldApi(function (fieldConfig) { return fieldConfig.DataMapping.InKindType; }, function (rawVal) { return rawVal.toIntZeroIfInvalid(); }, function () { _handleInKindTypeChanged(); }); 8640 Object.defineProperty(thisApi, 'InKindTypeId', { get: function () { return _tryGetFieldValueOrZero(thisApi.InKindTypeField); } }); 8641 8642 const _tryGetInkindClassId = function () { 8643 8644 let retVal = IN_KIND_ASSET_CLASSES.NONE; 8645 8646 if (thisApi.InKindTypeId > 0) 8647 retVal = App.InKindTypes.GetClassIdByTypeId(thisApi.InKindTypeId); 8648 8649 return retVal; 8650 8651 }; 8652 8653 //IN_KIND_ASSET_CLASSES 8654 Object.defineProperty(thisApi, 'InKindTypeClassId', { get: function () { return _tryGetInkindClassId(); } }); 8655 8656 //Framework added 8657 Object.defineProperty(thisApi, 'InKindVehicleYear', { get: function () { return _tryGetDomInputIntegerValueOrZero(document.getElementById('inKindVehicleYear')); } }); 8658 Object.defineProperty(thisApi, 'InKindVehicleMake', { get: function () { return _tryGetDomInputTextValueOrNull(document.getElementById('inKindVehicleMake')); } }); 8659 Object.defineProperty(thisApi, 'InKindVehicleMileage', { get: function () { return _tryGetDomInputIntegerValueOrZero(document.getElementById('inKindeVehicleMileage')); } }); 8660 Object.defineProperty(thisApi, 'InKindVehicleVin', { get: function () { return _tryGetDomInputTextValueOrNull(document.getElementById('inKindVinNumber')); } }); 8661 8662 Object.defineProperty(thisApi, 'InKindStockTicker', { get: function () { return _tryGetDomInputTextValueOrNull(document.getElementById('inKindStockTicker')); } }); 8663 Object.defineProperty(thisApi, 'InKindStockCompany', { get: function () { return _tryGetDomInputTextValueOrNull(document.getElementById('inKindStockCompany')); } }); 8664 8665 Object.defineProperty(thisApi, 'InKindFoodWeight', { get: function () { return _tryGetDomInputDecimalValueOrZero(document.getElementById('inKindFoodWeight')); } }); 8666 8667 const _isContactTypeField = function (fieldConfig) { return (fieldConfig.Type.Id == FORM_FIELD_TYPES.CONTACT_TYPE_SYSTEM); }; 8668 8669 thisApi.ContactTypeField = _getInitializedFieldApi(_isContactTypeField, 8670 function (rawVal) { 8671 8672 let retVal = rawVal.toIntZeroIfInvalid(); 8673 8674 if (retVal <= 0) 8675 retVal = CONTACT_TYPES.Person; 8676 8677 return retVal; 8678 8679 }, 8680 function () { _handleContactTypeChanged(); }); 8681 Object.defineProperty(thisApi, 'ContactTypeId', { get: function () { let retVal = _tryGetFieldValueOrZero(thisApi.ContactTypeField); if (retVal <= 0) retVal = CONTACT_TYPES.Person; return retVal; } }); 8682 8683 thisApi.OrganizationNameField = _getInitializedFieldApi(function (fieldConfig) { return fieldConfig.DataMapping.OrganizationName; });
8684 Object.defineProperty(thisApi, 'OrganizationName', { get: function () { return _tryGetFieldValue(thisApi.OrganizationNameField); } }); 8685 8686 thisApi.ContactFirstNameField = _getInitializedFieldApi(function (fieldConfig) { return fieldConfig.DataMapping.DonorFirstName; }); 8687 Object.defineProperty(thisApi, 'ContactFirstName', { get: function () { return _tryGetFieldValue(thisApi.ContactFirstNameField); } }); 8688 8689 thisApi.ContactMiddleNameField = _getInitializedFieldApi(function (fieldConfig) { return fieldConfig.DataMapping.DonorMiddleName; }); 8690 Object.defineProperty(thisApi, 'ContactMiddleName', { get: function () { return _tryGetFieldValue(thisApi.ContactMiddleNameField); } }); 8691 8692 thisApi.ContactLastNameField = _getInitializedFieldApi(function (fieldConfig) { return fieldConfig.DataMapping.DonorLastName; }); 8693 Object.defineProperty(thisApi, 'ContactLastName', { get: function () { return _tryGetFieldValue(thisApi.ContactLastNameField); } }); 8694 8695 thisApi.ContactEmailAddressField = _getInitializedFieldApi(function (fieldConfig) { return fieldConfig.DataMapping.DonorEmailAddress; }); 8696 Object.defineProperty(thisApi, 'ContactEmailAddress', { get: function () { return _tryGetFieldValue(thisApi.ContactEmailAddressField); } }); 8697 8698 thisApi.ContactPhoneNumberField = _getInitializedFieldApi(function (fieldConfig) { return fieldConfig.DataMapping.DonorPhoneNumber; }); 8699 Object.defineProperty(thisApi, 'ContactPhoneNumber', { get: function () { return _tryGetFieldValue(thisApi.ContactPhoneNumberField); } }); 8700 8701 //framework injected 8702 const _tryGetContactAddressSingleLineEntryDomElement = function () { return document.getElementById('contactAddressSingleLineInput'); }; 8703 const _tryGetContactAddressSingleLineEntryValue = function () { return _tryGetDomInputTextValueOrNull(_tryGetContactAddressSingleLineEntryDomElement()); }; 8704 8705 thisApi.ContactAddressStreet1Field = _getInitializedFieldApi(function (fieldConfig) { return fieldConfig.DataMapping.DonorAddressStreet1; }, null, null, function() { _showAllContactAddressFields(); }); 8706 Object.defineProperty(thisApi, 'ContactAddressStreet1', { get: function () { return _tryGetFieldValue(thisApi.ContactAddressStreet1Field); } }); 8707 8708 thisApi.ContactAddressStreet2Field = _getInitializedFieldApi(function (fieldConfig) { return fieldConfig.DataMapping.DonorAddressStreet2; }, null, null, function () { _showAllContactAddressFields(); }); 8709 Object.defineProperty(thisApi, 'ContactAddressStreet2', { get: function () { return _tryGetFieldValue(thisApi.ContactAddressStreet2Field); } }); 8710 8711 thisApi.ContactAddressCityField = _getInitializedFieldApi(function (fieldConfig) { return fieldConfig.DataMapping.DonorAddressCity; }, null, null, function () { _showAllContactAddressFields(); }); 8712 Object.defineProperty(thisApi, 'ContactAddressCity', { get: function () { return _tryGetFieldValue(thisApi.ContactAddressCityField); } }); 8713 8714 thisApi.ContactAddressStateProvinceField = _getInitializedFieldApi(function (fieldConfig) { return fieldConfig.DataMapping.DonorAddressState; }, function (rawVal) { return rawVal.toIntZeroIfInvalid(); }, null, function () { _showAllContactAddressFields(); }); 8715 Object.defineProperty(thisApi, 'ContactAddressStateProvinceId', { get: function () { return _tryGetFieldValueOrZero(thisApi.ContactAddressStateProvinceField); } }); 8716 Object.defineProperty(thisApi, 'ContactAddressStateProvinceCode', { get: function () { return _tryGetFieldText(thisApi.ContactAddressStateProvinceField); } }); 8717 8718 thisApi.ContactAddressPostalCodeField = _getInitializedFieldApi(function (fieldConfig) { return fieldConfig.DataMapping.DonorAddressPostalCode; }, null, null, function () { _showAllContactAddressFields(); }); 8719 Object.defineProperty(thisApi, 'ContactAddressPostalCode', { get: function () { return _tryGetFieldValue(thisApi.ContactAddressPostalCodeField); } }); 8720 8721 thisApi.ContactAddressCountryField = _getInitializedFieldApi(function (fieldConfig) { return fieldConfig.DataMapping.DonorAddressCountry; }, function (rawVal) { return rawVal.toIntZeroIfInvalid(); }, null, function () { _showAllContactAddressFields(); }); 8722 Object.defineProperty(thisApi, 'ContactAddressCountryId', { get: function () { return _tryGetFieldValueOrZero(thisApi.ContactAddressCountryField); } }); 8723 Object.defineProperty(thisApi, 'ContactAddressCountryCode', { get: function () { return App.Countries.GetCountryCodeByIdOrUSA(_tryGetFieldValueOrZero(thisApi.ContactAddressCountryField)); } }); 8724 8725 thisApi.EmailOptInField = _getInitializedFieldApi(function (fieldConfig) { return fieldConfig.DataMapping.EmailOptIn; }); 8726 Object.defineProperty(thisApi, 'EmailOptIn', { get: function () { return _tryGetFieldBoolValueOrNull(thisApi.EmailOptInField); } }, function (rawVal) { return rawVal.toBoolFalseIfInvalid(); }); 8727 8728 thisApi.MailOptInField = _getInitializedFieldApi(function (fieldConfig) { return fieldConfig.DataMapping.MailOptIn; }); 8729 Object.defineProperty(thisApi, 'MailOptIn', { get: function () { return _tryGetFieldBoolValueOrNull(thisApi.MailOptInField); } }, function (rawVal) { return rawVal.toBoolFalseIfInvalid(); }); 8730 8731 thisApi.PhoneOptInField = _getInitializedFieldApi(function (fieldConfig) { return fieldConfig.DataMapping.PhoneOptIn; }); 8732 Object.defineProperty(thisApi, 'PhoneOptIn', { get: function () { return _tryGetFieldBoolValueOrNull(thisApi.PhoneOptInField); } }, function (rawVal) { return rawVal.toBoolFalseIfInvalid(); }); 8733 8734 thisApi.SmsOptInField = _getInitializedFieldApi(function (fieldConfig) { return fieldConfig.DataMapping.SmsOptIn; }); 8735 Object.defineProperty(thisApi, 'SmsOptIn', { get: function () { return _tryGetFieldBoolValueOrNull(thisApi.SmsOptInField); } }, function (rawVal) { return rawVal.toBoolFalseIfInvalid(); }); 8736 8737 const _getSelectedEmailOptInListIds = function () { 8738 8739 let retVal = []; 8740 8741 if (thisApi.EmailOptInListsField != null) { 8742 8743 if (thisApi.EmailOptInListsField.HasOptions) 8744 retVal = thisApi.EmailOptInListsField.SelectedOptionIdValues; 8745 else 8746 retVal.push(thisApi.EmailOptInListsField.Value); 8747 8748 } 8749 8750 return retVal; 8751 8752 }; 8753 Object.defineProperty(thisApi, 'SelectedEmailOptInListIds', { get: function () { return _getSelectedEmailOptInListIds(); } }); 8754 8755 thisApi.EmailOptInListsField = _getInitializedFieldApi(function (fieldConfig) { return (fieldConfig.Type.Id == FORM_FIELD_TYPES.EMAIL_OPTIN_LIST_SYSTEM); }, function (rawVal) { return rawVal.toIntZeroIfInvalid(); }); 8756 Object.defineProperty(thisApi, 'EmailOptInListId', { get: function () { return _tryGetFieldValueOrZero(thisApi.EmailOptInListsField); } }); 8757 8758 const _getSelectedVolunteerPostIds = function () { 8759 8760 let retVal = []; 8761 8762 if (thisApi.VolunteerPostsField != null) { 8763 8764 if (thisApi.VolunteerPostsField.HasOptions) 8765 retVal = thisApi.VolunteerPostsField.SelectedOptionIdValues; 8766 else 8767 retVal.push(thisApi.VolunteerPostsField.Value); 8768 8769 } 8770 8771 return retVal; 8772 8773 }; 8774 Object.defineProperty(thisApi, 'SelectedVolunteerPostIds', { get: function () { return _getSelectedVolunteerPostIds(); } }); 8775 8776 thisApi.VolunteerPostsField = _getInitializedFieldApi(function (fieldConfig) { return (fieldConfig.Type.Id == FORM_FIELD_TYPES.VOLUNTEER_POST_SELECTOR_NEW); }, function (rawVal) { return rawVal.toIntZeroIfInvalid(); }); 8777 Object.defineProperty(thisApi, 'VolunteerPostId', { get: function () { return _tryGetFieldValueOrZero(thisApi.VolunteerPostsField); } }); 8778 8779 const _getVolunteerTimeEntryFields = function () { 8780 8781 let retVal = []; 8782 8783 for (let x = 0; x < _allFieldApis.length; x++) 8784 if (_allFieldApis[x].Configuration.Type.Id == FORM_FIELD_TYPES.VOLUNTEER_POST_TIME_ENTRY) { 8785 8786 //time may be configured to hide entirely so was never rendered - nothing to wire up 8787 if (_allFieldApis[x].DomElement == null) 8788 continue; 8789 8790 //_allFieldApis[x].InitializeDefaultGetReturnValueFunction(function (rawVal) { return rawVal.toIntZeroIfInvalid(); }); 8791 //_allFieldApis[x].InitializeSetValueCallBackFunction(_handleVolunteerTimeEntryChanged); 8792 8793 retVal.push(_allFieldApis[x]); 8794 8795 } 8796 8797 return retVal; 8798 8799 }; 8800
8801 Object.defineProperty(thisApi, 'VolunteerTimeEntryFields', { 8802 get: function () { 8803 return _getVolunteerTimeEntryFields(); 8804 } 8805 }); 8806 8807 const _getSelectedCrmGroupIds = function () { 8808 8809 let retVal = []; 8810 8811 if (thisApi.CrmGroupsField != null) { 8812 8813 if (thisApi.CrmGroupsField.HasOptions) 8814 retVal = thisApi.CrmGroupsField.SelectedOptionIdValues; 8815 else 8816 retVal.push(thisApi.CrmGroupsField.Value); 8817 8818 } 8819 8820 return retVal; 8821 8822 }; 8823 Object.defineProperty(thisApi, 'SelectedCrmGroupIds', { get: function () { return _getSelectedCrmGroupIds(); } }); 8824 8825 thisApi.CrmGroupsField = _getInitializedFieldApi(function (fieldConfig) { return (fieldConfig.Type.Id == FORM_FIELD_TYPES.CRM_GROUP_SELECTOR); }, function (rawVal) { return rawVal.toIntZeroIfInvalid(); }); 8826 Object.defineProperty(thisApi, 'CrmGroupId', { get: function () { return _tryGetFieldValueOrZero(thisApi.CrmGroupsField); } }); 8827 8828 thisApi.BillingAddressStreet1Field = _getInitializedFieldApi(function (fieldConfig) { return fieldConfig.DataMapping.BillingAddressStreet1; }); 8829 Object.defineProperty(thisApi, 'BillingAddressStreet1', { get: function () { return _tryGetFieldValue(thisApi.BillingAddressStreet1Field); } }); 8830 8831 thisApi.BillingAddressStreet2Field = _getInitializedFieldApi(function (fieldConfig) { return fieldConfig.DataMapping.BillingAddressStreet2; }); 8832 Object.defineProperty(thisApi, 'BillingAddressStreet2', { get: function () { return _tryGetFieldValue(thisApi.BillingAddressStreet2Field); } }); 8833 8834 thisApi.BillingAddressCityField = _getInitializedFieldApi(function (fieldConfig) { return fieldConfig.DataMapping.BillingAddressCity; }); 8835 Object.defineProperty(thisApi, 'BillingAddressCity', { get: function () { return _tryGetFieldValue(thisApi.BillingAddressCityField); } }); 8836 8837 thisApi.BillingAddressStateProvinceField = _getInitializedFieldApi(function (fieldConfig) { return fieldConfig.DataMapping.BillingAddressState; }, function (rawVal) { return rawVal.toIntZeroIfInvalid(); }); 8838 Object.defineProperty(thisApi, 'BillingAddressStateProvinceCode', { get: function () { return _tryGetFieldText(thisApi.BillingAddressStateProvinceField); } }); 8839 8840 thisApi.BillingAddressPostalCodeField = _getInitializedFieldApi(function (fieldConfig) { return fieldConfig.DataMapping.BillingAddressPostalCode; }); 8841 Object.defineProperty(thisApi, 'BillingAddressPostalCode', { get: function () { return _tryGetFieldValue(thisApi.BillingAddressPostalCodeField); } }); 8842 8843 thisApi.BillingAddressCountryField = _getInitializedFieldApi(function (fieldConfig) { return fieldConfig.DataMapping.BillingAddressCountry; }, function (rawVal) { return rawVal.toIntZeroIfInvalid(); }); 8844 Object.defineProperty(thisApi, 'BillingAddressCountryCode', { get: function () { return App.Countries.GetCountryCodeByIdOrUSA(_tryGetFieldValueOrZero(thisApi.BillingAddressCountryField)); } }); 8845 8846 thisApi.FundField = _getInitializedFieldApi(function (fieldConfig) { return (fieldConfig.Type.Id == FORM_FIELD_TYPES.FUND_SYSTEM); }, function (rawVal) { return rawVal.toIntZeroIfInvalid(); }); 8847 Object.defineProperty(thisApi, 'FundId', { get: function () { return _tryGetFieldValueOrZero(thisApi.FundField); } }); 8848 8849 thisApi.GrassrootsEventField = _getInitializedFieldApi(function (fieldConfig) { return (fieldConfig.Type.Id == FORM_FIELD_TYPES.GRASSROOTS_EVENT_ID_SYSTEM); }, function (rawVal) { return rawVal.toIntZeroIfInvalid(); }); 8850 Object.defineProperty(thisApi, 'GrassrootsEventId', { get: function () { return _tryGetFieldValueOrZero(thisApi.GrassrootsEventField); } }); 8851 8852 thisApi.PromoCodeField = _getInitializedFieldApi(function (fieldConfig) { return (fieldConfig.Type.Id == FORM_FIELD_TYPES.PROMO_CODE_ADVANCED_SYSTEM); }); 8853 8854 thisApi.CommentField = _getInitializedFieldApi(function (fieldConfig) { return (fieldConfig.Type.Id == FORM_FIELD_TYPES.COMMENT_SYSTEM); }); 8855 Object.defineProperty(thisApi, 'Comment', { get: function () { return _tryGetFieldValue(thisApi.CommentField); } }); 8856 8857 thisApi.AnonymousField = _getInitializedFieldApi(function (fieldConfig) { return fieldConfig.DataMapping.Anonymous; }); 8858 Object.defineProperty(thisApi, 'Anonymous', { get: function () { return _tryGetFieldValueOrFalse(thisApi.AnonymousField); } }, function (rawVal) { return rawVal.toBoolFalseIfInvalid(); }); 8859 8860 const _getTicketQuantityFields = function () { 8861 8862 let retVal = []; 8863 8864 for (let x = 0; x < _allFieldApis.length; x++) 8865 if (_allFieldApis[x].Configuration.Type.Id == FORM_FIELD_TYPES.TICKET_QUANTITY_SYSTEM) { 8866 8867 //ticket may be configured to hide entirely (sold out / outside its sale window) and s
8867o was never rendered - nothing to wire up 8868 if (_allFieldApis[x].DomElement == null) 8869 continue; 8870 8871 //ticket is sold out or outside its sale window - rendered as a plain message instead of a quantity control (avoids reading .Value on a non-input element) 8872 if (_allFieldApis[x].DomElement.hasClassName('ce-ticketUnavailableMessages')) 8873 continue; 8874 8875 _allFieldApis[x].InitializeDefaultGetReturnValueFunction(function (rawVal) { return rawVal.toIntZeroIfInvalid(); }); 8876 _allFieldApis[x].InitializeSetValueCallBackFunction(_handleTicketQuantityChanged); 8877 8878 retVal.push(_allFieldApis[x]); 8879 8880 } 8881 8882 return retVal; 8883 8884 }; 8885 8886 Object.defineProperty(thisApi, 'TicketQuantityFields', { 8887 get: function () { 8888 return _getTicketQuantityFields(); 8889 } 8890 }); 8891 8892 const _getTicketSessionFieldsApis = function () { 8893 8894 let retVal = []; 8895 8896 for (let x = 0; x < _allFieldApis.length; x++) 8897 if (_allFieldApis[x].Configuration.Type.Id == FORM_FIELD_TYPES.TICKET_SESSION_SYSTEM && _allFieldApis[x].DomElement != null) { 8898 retVal.push(_allFieldApis[x]); 8899 break; 8900 } 8901 8902 return retVal; 8903 8904 }; 8905
8906 Object.defineProperty(thisApi, 'TicketSessionFields', { 8907 get: function () { 8908 return _getTicketSessionFieldsApis(); 8909 } 8910 }); 8911 8912 const _getTicketSubTotalFields = function () { 8913 8914 let retVal = []; 8915 8916 for (let x = 0; x < _allFieldApis.length; x++) 8917 if (_allFieldApis[x].Configuration.Type.Id == FORM_FIELD_TYPES.TICKET_SUBTOTAL_SYSTEM) { 8918 8919 //ticket may be configured to hide entirely (sold out / outside its sale window) and so was never rendered 8920 if (_allFieldApis[x].DomElement == null) 8921 continue; 8922 8923 _allFieldApis[x].InitializeDefaultGetReturnValueFunction(function (rawVal) { return rawVal.toDecimalZeroIfInvalid(); }); 8924 8925 retVal.push(_allFieldApis[x]); 8926 8927 } 8928 8929 return retVal; 8930 8931 }; 8932 8933 Object.defineProperty(thisApi, 'TicketSubTotalFields', { 8934 get: function () { 8935 return _getTicketSubTotalFields(); 8936 } 8937 }); 8938 8939 thisApi.DietaryRestrictionField = _getInitializedFieldApi(function (fieldConfig) { return fieldConfig.DataMapping.DietaryRestrictions; }); 8940 Object.defineProperty(thisApi, 'DietaryRestriction', { get: function () { return _tryGetFieldValue(thisApi.DietaryRestrictionField); } }); 8941 8942 thisApi.EmergencyContactNameField = _getInitializedFieldApi(function (fieldConfig) { return fieldConfig.DataMapping.EmergencyContactName; }); 8943 Object.defineProperty(thisApi, 'EmergencyContactName', { get: function () { return _tryGetFieldValue(thisApi.EmergencyContactNameField); } }); 8944 8945 thisApi.EmergencyContactPhoneNumberField = _getInitializedFieldApi(function (fieldConfig) { return fieldConfig.DataMapping.EmergencyContactPhoneNumber; }); 8946 Object.defineProperty(thisApi, 'EmergencyContactPhoneNumber', { get: function () { return _tryGetFieldValue(thisApi.EmergencyContactPhoneNumberField); } }); 8947 8948 thisApi.CaseTitleField = _getInitializedFieldApi(function (fieldConfig) { return fieldConfig.DataMapping.CaseTitle; }); 8949 Object.defineProperty(thisApi, 'CaseTitle', { get: function () { return _tryGetFieldValue(thisApi.CaseTitleField); } }); 8950 8951 thisApi.CaseStatusField = _getInitializedFieldApi(function (fieldConfig) { return fieldConfig.DataMapping.CaseStatus; }, function (rawVal) { return rawVal.toIntZeroIfInvalid(); }); 8952 Object.defineProperty(thisApi, 'CaseStatusId', { get: function () { return _tryGetFieldValueOrZero(thisApi.CaseStatusField); } }); 8953 8954 thisApi.CasePriorityField = _getInitializedFieldApi(function (fieldConfig) { return fieldConfig.DataMapping.CasePriority; }, function (rawVal) { return rawVal.toIntZeroIfInvalid(); }); 8955 Object.defineProperty(thisApi, 'CasePriorityId', { get: function () { return _tryGetFieldValueOrZero(thisApi.CasePriorityField); } }); 8956 8957 thisApi.CaseDescriptionField = _getInitializedFieldApi(function (fieldConfig) { return fieldConfig.DataMapping.CaseDescription; }); 8958 Object.defineProperty(thisApi, 'CaseDescription', { get: function () { return _tryGetFieldValue(thisApi.CaseDescriptionField); } }); 8959 8960 thisApi.FreeGiftSelectorField = _getInitializedFieldApi(function (fieldConfig) { return (fieldConfig.Type.Id == FORM_FIELD_TYPES.FREE_GIFT_SELECTOR); }); //not sure this will work , function (rawVal) { return rawVal.toIntZeroIfInvalid(); } - do we need a processing function for more complex types like this? 8961 Object.defineProperty(thisApi, 'FreeGiftSelectorProductId', { 8962 get: function () { 8963 8964 let retVal = 0; 8965 8966 if (thisApi.FreeGiftSelectorField != null && thisApi.FreeGiftSelectorField.DomElement.querySelector('input[type="radio"]:checked') != null) 8967 retVal = thisApi.FreeGiftSelectorField.DomElement.querySelector('input[type="radio"]:checked').value.toIntZeroIfInvalid(); 8968 8969 return retVal; 8970 8971 } 8972 }); 8973 8974 Object.defineProperty(thisApi, 'FreeGiftSelectorfulfillmentCenterId', { 8975 get: function () { 8976 8977 let retVal = 0; 8978 8979 if (thisApi.FreeGiftSelectorField != null) 8980 retVal = thisApi.FreeGiftSelectorField.DomElement.tryGetIntegerAttribute('data-fulfillment-center-id'); 8981 8982 return retVal; 8983 8984 } 8985 }); 8986 8987 thisApi.MembershipTypeField = _getInitializedFieldApi(function (fieldConfig) { return (fieldConfig.Type.Id == FORM_FIELD_TYPES.MEMBERSHIP_TYPES_SYSTEM); }, function (rawVal) { return rawVal.toIntZeroIfInvalid(); }, function () { _handleMembershipTypeChanged(); }); 8988 Object.defineProperty(thisApi, 'MembershipTypeId', { get: function () { return _tryGetFieldValueOrZero(thisApi.MembershipTypeField); } }); 8989 8990 //Framework added
8991 Object.defineProperty(thisApi, 'AutoRenewMembership', { get: function () { let retVal = false; if (document.getElementById('autoRenewMembershipCheckBox_0') != null) retVal = document.getElementById('autoRenewMembershipCheckBox_0').checked; return retVal; } }); 8992 8993 thisApi.PledgeTypeField = _getInitializedFieldApi(function (fieldConfig) { return (fieldConfig.Type.Id == FORM_FIELD_TYPES.PLEDGE_TYPES_SYSTEM); }, function (rawVal) { return rawVal.toIntZeroIfInvalid(); }); 8994 Object.defineProperty(thisApi, 'PledgeTypeId', { get: function () { return _tryGetFieldValueOrZero(thisApi.PledgeTypeField); } }); 8995 8996 thisApi.TributeField = _getInitializedFieldApi(function (fieldConfig) { return (fieldConfig.Type.Id == FORM_FIELD_TYPES.TRIBUTE_ID_SYSTEM); }, function (rawVal) { return rawVal.toIntZeroIfInvalid(); }); 8997 Object.defineProperty(thisApi, 'TributeId', { get: function () { return _tryGetFieldValueOrZero(thisApi.TributeField); } }); 8998 8999 thisApi.TributeTypeField = _getInitializedFieldApi(function (fieldConfig) { return (fieldConfig.Type.Id == FORM_FIELD_TYPES.TRIBUTE_TYPE_SYSTEM); }, function (rawVal) { return rawVal.toIntZeroIfInvalid(); }); 9000 Object.defineProperty(thisApi, 'TributeTypeId', { get: function () { return _tryGetFieldValueOrZero(thisApi.TributeTypeField); } }); 9001 9002 thisApi.TributeNameField = _getInitializedFieldApi(function (fieldConfig) { return (fieldConfig.Type.Id == FORM_FIELD_TYPES.TRIBUTE_NAME_SYSTEM); }); 9003 Object.defineProperty(thisApi, 'TributeName', { get: function () { return _tryGetFieldValue(thisApi.TributeNameField); } }); 9004 9005 thisApi.TributeDescriptionField = _getInitializedFieldApi(function (fieldConfig) { return (fieldConfig.Type.Id == FORM_FIELD_TYPES.TRIBUTE_DESCRIPTION_SYSTEM); }); 9006 Object.defineProperty(thisApi, 'TributeDescription', { get: function () { return _tryGetFieldValue(thisApi.TributeDescriptionField); } }); 9007 9008 //TRIBUTE_PEN_NAME_SYSTEM: 27, 9009 //TRIBUTE_ANONYMOUS_SYSTEM: 28, 9010 //TRIBUTE_START_DATE_SYSTEM: 29, 9011 //TRIBUTE_END_DATE_SYSTEM: 30, 9012 //TRIBUTE_GOAL_SYSTEM: 31, 9013 9014 thisApi.TributePersonalMessageField = _getInitializedFieldApi(function (fieldConfig) { return (fieldConfig.Type.Id == FORM_FIELD_TYPES.TRIBUTE_ACK_PERSONAL_MESSAGE_SYSTEM); }); 9015 Object.defineProperty(thisApi, 'TributePersonalMessage', { get: function () { return _tryGetFieldValue(thisApi.TributePersonalMessageField); } }); 9016 9017 //TRIBUTE_ACK_NAME_PREFIX__SYSTEM: 33, 9018 9019 thisApi.TributeRecipientFirstNameField = _getInitializedFieldApi(function (fieldConfig) { return (fieldConfig.Type.Id == FORM_FIELD_TYPES.TRIBUTE_ACK_FIRST_NAME_SYSTEM); }); 9020 Object.defineProperty(thisApi, 'TributeRecipientFirstName', { get: function () { return _tryGetFieldValue(thisApi.TributeRecipientFirstNameField); } }); 9021 9022 //TRIBUTE_ACK_MIDDLE_NAME_SYSTEM: 35, 9023 9024 thisApi.TributeRecipientLastNameField = _getInitializedFieldApi(function (fieldConfig) { return (fieldConfig.Type.Id == FORM_FIELD_TYPES.TRIBUTE_ACK_LAST_NAME_SYSTEM); }); 9025 Object.defineProperty(thisApi, 'TributeRecipientLastName', { get: function () { return _tryGetFieldValue(thisApi.TributeRecipientLastNameField); } }); 9026 9027 //TRIBUTE_ACK_NAME_SUFFIX_SYSTEM: 37, 9028 //TRIBUTE_ACK_TITLE_SYSTEM: 38, 9029 9030 const _tryGetTributeRecipientAddressSingleLineEntryDomElement = function () { return document.getElementById('tributeAddressSingleLineInput'); }; 9031 const _tryGetTributeRecipientAddressSingleLineEntryValue = function () { return _tryGetDomInputTextValueOrNull(_tryGetTributeRecipientAddressSingleLineEntryDomElement()); }; 9032 9033 thisApi.TributeRecipientAddressStreet1Field = _getInitializedFieldApi(function (fieldConfig) { return (fieldConfig.Type.Id == FORM_FIELD_TYPES.TRIBUTE_ACK_ADDRESS_1_SYSTEM); }); 9034 Object.defineProperty(thisApi, 'TributeRecipientAddressStreet1', { get: function () { return _tryGetFieldValue(thisApi.TributeRecipientAddressStreet1Field); } }); 9035 9036 thisApi.TributeRecipientAddressStreet2Field = _getInitializedFieldApi(function (fieldConfig) { return (fieldConfig.Type.Id == FORM_FIELD_TYPES.TRIBUTE_ACK_ADDRESS_2_SYSTEM); }); 9037 Object.defineProperty(thisApi, 'TributeRecipientAddressStreet2', { get: function () { return _tryGetFieldValue(thisApi.TributeRecipientAddressStreet2Field); } }); 9038 9039 //TRIBUTE_ACK_ADDRESS_3_SYSTEM: 43, 9040 //TRIBUTE_ACK_ADDRESS_4_SYSTEM: 44, 9041 9042 thisApi.TributeRecipientAddressCityField = _getInitializedFieldApi(function (fieldConfig) { return (fieldConfig.Type.Id == FORM_FIELD_TYPES.TRIBUTE_ACK_CITY_SYSTEM); }); 9043 Object.defineProperty(thisApi, 'TributeRecipientAddressCity', { get: function () { return _tryGetFieldValue(thisApi.TributeRecipientAddressCityField); } }); 9044 9045 thisApi.TributeRecipientAddressStateField = _getInitializedFieldApi(function (fieldConfig) { return (fieldConfig.Type.Id == FORM_FIELD_TYPES.TRIBUTE_ACK_STATE_SYSTEM); }); 9046 Object.defineProperty(thisApi, 'TributeRecipientAddressStateCode', { get: function () { return _tryGetFieldText(thisApi.TributeRecipientAddressStateField); } }); 9047 9048 thisApi.TributeRecipientAddressPostalCodeField = _getInitializedFieldApi(function (fieldConfig) { return (fieldConfig.Type.Id == FORM_FIELD_TYPES.TRIBUTE_ACK_POSTAL_CODE_SYSTEM); }); 9049 Object.defineProperty(thisApi, 'TributeRecipientAddressPostalCode', { get: function () { return _tryGetFieldValue(thisApi.TributeRecipientAddressPostalCodeField); } }); 9050 9051 thisApi.TributeRecipientAddressCountryField = _getInitializedFieldApi(function (fieldConfig) { return (fieldConfig.Type.Id == FORM_FIELD_TYPES.TRIBUTE_ACK_COUNTRY_SYSTEM); }); 9052 Object.defineProperty(thisApi, 'TributeRecipientAddressCountryCode', { get: function () { return _tryGetFieldText(thisApi.TributeRecipientAddressCountryField); } }); 9053 9054 thisApi.TributeRecipientPhoneNumberField = _getInitializedFieldApi(function (fieldConfig) { return (fieldConfig.Type.Id == FORM_FIELD_TYPES.TRIBUTE_ACK_PHONE_NUMBER_SYSTEM); }); 9055 Object.defineProperty(thisApi, 'TributeRecipientPhoneNumber', { get: function () { return _tryGetFieldValue(thisApi.TributeRecipientPhoneNumberField); } }); 9056 9057 thisApi.TributeRecipientEmailAddressField = _getInitializedFieldApi(function (fieldConfig) { return (fieldConfig.Type.Id == FORM_FIELD_TYPES.TRIBUTE_ACK_EMAIL_SYSTEM); }); 9058 Object.defineProperty(thisApi, 'TributeRecipientEmailAddress', { get: function () { return _tryGetFieldValue(thisApi.TributeRecipientEmailAddressField); } }); 9059 9060 thisApi.ECardField = _getInitializedFieldApi(function (fieldConfig) { return (fieldConfig.Type.Id == FORM_FIELD_TYPES.ECARD_SYSTEM); }, function (rawVal) { return rawVal.toIntZeroIfInvalid(); }); 9061 Object.defineProperty(thisApi, 'ECardId', { get: function () { return _tryGetFieldValueOrZero(thisApi.ECardField); } }); 9062 9063 thisApi.PrintCardField = _getInitializedFieldApi(function (fieldConfig) { return (fieldConfig.Type.Id == FORM_FIELD_TYPES.PRINT_CARD_SYSTEM); }, function (rawVal) { return rawVal.toIntZeroIfInvalid(); }); 9064 Object.defineProperty(thisApi, 'PrintCardId', { get: function () { return _tryGetFieldValueOrZero(thisApi.PrintCardField); } }); 9065 9066 thisApi.PrintCardCountField = _getInitializedFieldApi(function (fieldConfig) { return (fieldConfig.Type.Id == FORM_FIELD_TYPES.PRINT_CARD_COUNT_SYSTEM); }, function (rawVal) { return rawVal.toIntZeroIfInvalid(); }); 9067 Object.defineProperty(thisApi, 'PrintCardCount', { get: function () { return _tryGetFieldValueOrZero(thisApi.PrintCardCountField); } }); 9068 9069 thisApi.SolicitorField = _getInitializedFieldApi(function (fieldConfig) { return (fieldConfig.Type.Id == FORM_FIELD_TYPES.SOLICITOR); }, function (rawVal) { return rawVal.toIntZeroIfInvalid(); }); 9070 Object.defineProperty(thisApi, 'SolicitorId', { get: function () { return _tryGetFieldValueOrZero(thisApi.SolicitorField); } }); 9071 9072 thisApi.CustomCode1Field = _getInitializedFieldApi(function (fieldConfig) { return fieldConfig.DataMapping.CustomCode1; }); 9073 Object.defineProperty(thisApi, 'CustomCode1', { get: function () { return _tryGetFieldValue(thisApi.CustomCode1Field); } }); 9074 9075 thisApi.CustomCode2Field = _getInitializedFieldApi(function (fieldConfig) { return fieldConfig.DataMapping.CustomCode2; }); 9076 Object.defineProperty(thisApi, 'CustomCode2', { get: function () { return _tryGetFieldValue(thisApi.CustomCode2Field); } }); 9077 9078 thisApi.CustomCode3Field = _getInitializedFieldApi(function (fieldConfig) { return fieldConfig.DataMapping.CustomCode3; });
9079 Object.defineProperty(thisApi, 'CustomCode3', { get: function () { return _tryGetFieldValue(thisApi.CustomCode3Field); } }); 9080 9081 thisApi.CustomCode4Field = _getInitializedFieldApi(function (fieldConfig) { return fieldConfig.DataMapping.CustomCode4; }); 9082 Object.defineProperty(thisApi, 'CustomCode4', { get: function () { return _tryGetFieldValue(thisApi.CustomCode4Field); } }); 9083 9084 thisApi.CustomCode5Field = _getInitializedFieldApi(function (fieldConfig) { return fieldConfig.DataMapping.CustomCode5; }); 9085 Object.defineProperty(thisApi, 'CustomCode5', { get: function () { return _tryGetFieldValue(thisApi.CustomCode5Field); } }); 9086 9087 thisApi.CustomCode6Field = _getInitializedFieldApi(function (fieldConfig) { return fieldConfig.DataMapping.CustomCode6; }); 9088 Object.defineProperty(thisApi, 'CustomCode6', { get: function () { return _tryGetFieldValue(thisApi.CustomCode6Field); } }); 9089 9090 thisApi.CustomCode7Field = _getInitializedFieldApi(function (fieldConfig) { return fieldConfig.DataMapping.CustomCode7; }); 9091 Object.defineProperty(thisApi, 'CustomCode7', { get: function () { return _tryGetFieldValue(thisApi.CustomCode7Field); } }); 9092 9093 thisApi.CustomCode8Field = _getInitializedFieldApi(function (fieldConfig) { return fieldConfig.DataMapping.CustomCode8; }); 9094 Object.defineProperty(thisApi, 'CustomCode8', { get: function () { return _tryGetFieldValue(thisApi.CustomCode8Field); } }); 9095 9096 //Core Processing APIs (form & field settings) 9097 //******************************************** 9098 9099 //Core Processing APIs (ui helpers) 9100 9101 const _isDafPayPaymentMethodAvailable = function () { 9102 9103 let retVal = false; 9104 9105 for (let x = 0; x < thisApi.AvailablePaymentMethodIds.length; x++) 9106 if (thisApi.AvailablePaymentMethodIds[x] == PAYMENT_METHODS.SOFT_CREDIT) { 9107 retVal = true; 9108 break; 9109 } 9110 9111 if (retVal && thisApi.PaymentSectionConfig != null && !thisApi.PaymentSectionConfig.IncludeDafPay) 9112 retVal = false; 9113 9114 return retVal; 9115 9116 }; 9117 Object.defineProperty(thisApi, 'DafPayPaymentMethodAvailable', { get: function () { return _isDafPayPaymentMethodAvailable(); } }); 9118 9119 const _inKindPaymentMethodAvailable = function () { 9120 9121 let retVal = false; 9122 9123 for (let x = 0; x < thisApi.AvailablePaymentMethodIds.length; x++) 9124 if (thisApi.AvailablePaymentMethodIds[x] == PAYMENT_METHODS.IN_KIND) { 9125 retVal = true; 9126 break; 9127 } 9128 9129 return retVal; 9130 9131 }; 9132 9133 const _inkindOnlyPaymentForm = function () { 9134 return (thisApi.AvailablePaymentMethodIds.length == 1 && _inKindPaymentMethodAvailable()); 9135 }; 9136 9137 const _creditCardPaymentMethodAvailable = function () { 9138 9139 let retVal = false; 9140 9141 for (let x = 0; x < thisApi.AvailablePaymentMethodIds.length; x++) 9142 if (thisApi.AvailablePaymentMethodIds[x] == PAYMENT_METHODS.CREDIT_CARD) { 9143 retVal = true; 9144 break; 9145 } 9146 9147 return retVal; 9148 9149 }; 9150 9151 const _achPaymentMethodAvailable = function () { 9152 9153 let retVal = false; 9154 9155 for (let x = 0; x < thisApi.AvailablePaymentMethodIds.length; x++) 9156 if (thisApi.AvailablePaymentMethodIds[x] == PAYMENT_METHODS.CREDIT_CARD) { 9157 retVal = true; 9158 break; 9159 } 9160 9161 return retVal; 9162 9163 }; 9164 9165 const _payPalPaymentMethodAvailable = function () { 9166 9167 let retVal = false; 9168 9169 for (let x = 0; x < thisApi.AvailablePaymentMethodIds.length; x++) 9170 if (thisApi.AvailablePaymentMethodIds[x] == PAYMENT_METHODS.PAYPAL) { 9171 retVal = true; 9172 break; 9173 } 9174 9175 return retVal; 9176 9177 }; 9178 9179 const _oneClickCompatiblePaymentMethodsAvailable = function () { 9180 9181 let retVal = (_creditCardPaymentMethodAvailable() || _achPaymentMethodAvailable()); 9182 9183 return retVal; 9184 9185 }; 9186 9187 const _getFirstPaymentMethodBillingInfoField = function () { 9188 9189 let retVal = null; 9190 9191 for (let x = 0; x < _allFieldApis.length; x++) 9192 if (_allFieldApis[x].IsPaymentMethodBillingInformation) { 9193 retVal = _allFieldApis[x]; 9194 break; 9195 } 9196 9197 return retVal; 9198 9199 }; 9200 9201 const _getFirstTributeRecipientContactInfoField = function () { 9202 9203 let retVal = null; 9204 9205 for (let x = 0; x < _allFieldApis.length; x++) 9206 if (_allFieldApis[x].IsTributeRecipientContactInformation) { 9207 retVal = _allFieldApis[x]; 9208 break; 9209 } 9210 9211 return retVal; 9212 9213 }; 9214 9215 const _getFirstContactAddressField = function () { 9216 9217 let retVal = null; 9218 9219 for (let x = 0; x < _allFieldApis.length; x++) 9220 if (_allFieldApis[x].IsContactAddressField) { 9221 retVal = _allFieldApis[x]; 9222 break; 9223 } 9224 9225 return retVal; 9226 9227 }; 9228 9229 const _getFirstEnabledContactAddressField = function () { 9230 9231 let retVal = null; 9232 9233 for (let x = 0; x < _allFieldApis.length; x++) 9234 if (_allFieldApis[x].IsContactAddressField && _allFieldApis[x].Enabled) { 9235 retVal = _allFieldApis[x]; 9236 break; 9237 } 9238 9239 return retVal; 9240 9241 }; 9242 9243 const _getFileUploadFieldApis = function () { 9244 9245 let retVal = []; 9246 9247 for (let x = 0; x < _allFieldApis.length; x++) 9248 if (_allFieldApis[x].Configuration.Type.Id == FORM_FIELD_TYPES.FILE) 9249 retVal.push(_allFieldApis[x]); 9250 9251 return retVal; 9252 9253 }; 9254 9255 const _hideAllPaymentFields = function () { 9256 9257 _tryHideField(thisApi.CreditCardTypeField); 9258 _tryHideField(thisApi.CreditCardNumberField); 9259 _tryHideField(thisApi.CreditCardHolderNameField); 9260 _tryHideField(thisApi.CreditCardExpirationDateField); 9261 _tryHideField(thisApi.CreditCardExpirationMonthField); 9262 _tryHideField(thisApi.CreditCardExpirationYearField); 9263 _tryHideField(thisApi.CreditCardSecurityCodeField); 9264 9265 _tryHideField(thisApi.BankAccountNumberField); 9266 _tryHideField(thisApi.BankAccountTypeField); 9267 _tryHideField(thisApi.BankRoutingNumberField); 9268 _tryHideField(thisApi.BankAccountNameOnAccountField); 9269 _tryHideField(thisApi.ACHTransactionTypeField); 9270 _tryHideField(thisApi.BankNameField); 9271 9272 _tryHideInKindTypeDetailFields(); 9273 9274 if (document.getElementById('togglePaymentMethodBillingInfoContainer') != null) 9275 document.getElementById('togglePaymentMethodBillingInfoContainer').vanish(); 9276 9277 _tryHideField(thisApi.BillingAddressStreet1Field); 9278 _tryHideField(thisApi.BillingAddressStreet2Field); 9279 _tryHideField(thisApi.BillingAddressCityField); 9280 _tryHideField(thisApi.BillingAddressStateProvinceField); 9281 _tryHideField(thisApi.BillingAddressPostalCodeField); 9282 _tryHideField(thisApi.BillingAddressCountryField); 9283 9284 thisApi.HideSubmitButton(); 9285 9286 thisApi.PayPalApi.HideButton(); 9287 _tryHideGooglePayButton(); 9288 _tryHideApplePayButton(); 9289 thisApi.DafPayApi.HideButton(); 9290 9291 }; 9292 9293 const _calculateSelectedPaymentMethodfees = function () { 9294 9295 let retVal = 0.00; 9296 9297 let payMethId = thisApi.PaymentMethodId; 9298 9299 switch (payMethId) { 9300 case PAYMENT_METHODS.CREDIT_CARD: 9301 case PAYMENT_METHODS.ACH: 9302 case PAYMENT_METHODS.PAYPAL: 9303 case PAYMENT_METHODS.SOFT_CREDIT: 9304 case PAYMENT_METHODS.ONECLICK_DONATION: 9305 9306 let amt = thisApi.GetDiscountedAmount(); 9307 if (amt > 0) { 9308 9309 let feeRate = WebFormContext.Form.CoverFeeRate; 9310 //console.log('feeRate: ' + feeRate); 9311 if (feeRate > 0) 9312 retVal = (amt * (feeRate / 100)); 9313 9314 } 9315 9316 break; 9317 } 9318 9319 return retVal; 9320 9321 }; 9322 9323 const _calculateFees = function () { 9324 9325 let retVal = 0.00; 9326 9327 let cfCb = document.getElementById('coverFeesCheckBox'); 9328 if (cfCb != null && cfCb.getValue()) 9329 retVal = _calculateSelectedPaymentMethodfees(); 9330 9331 return retVal; 9332 9333 }; 9334 9335 const _getFeesFormatted = function () { 9336 9337 let amt = _calculateFees(); 9338 let sym = _getPaymentCurrencySymbol(); 9339 9340 return formatCurrency(amt, true, sym); 9341 9342 }; 9343 9344 const _getAmountFormatted = function (amt) { 9345 9346 let sym = _getPaymentCurrencySymbol(); 9347 9348 return formatCurrency(amt, true, sym); 9349 9350 }; 9351 9352 const _updateSubmitButtonText = function () { 9353 9354 let btnText = WebFormContext.Form.NextButtonLabel; 9355 9356 if (isBlank(btnText)) 9357 btnText += 'Submit'; 9358 9359 if (WebFormContext.Form.ShowAmountOnNextButton) { 9360 9361 let amt = thisApi.GetDiscountedAmount(); 9362
9363 _logger.Log('update button text called with orig amt: ' + amt); 9364 9365 if (amt > 0) { 9366 9367 let fees = _calculateFees(); 9368 9369 amt = amt + fees; 9370 9371 btnText += ' ' + _getAmountFormatted(amt); 9372 9373 let freq_id = _tryGetIdFieldValueOrZero(thisApi.RecurringPaymentFrequencyField); 9374 if (freq_id > 0) 9375 btnText += ' ' + getPaymentFrequencyDescriptionById(freq_id); 9376 9377 let donationAmt = thisApi.GenericAmountInputsAmount; 9378 9379 if (donationAmt > 0 && thisApi.GetPreDiscountAmount() > donationAmt) 9380 btnText += ' (includes ' + _getAmountFormatted(donationAmt) + ' donation)'; 9381 9382 } 9383 9384 } 9385 9386 thisApi.SetSubmitButtonText(btnText); 9387 9388 }; 9389 9390 const _updateFormAfterAmountImpactingChange = function () { 9391 9392 if (thisApi.PaymentMethodField != null) { 9393 9394 //check amount to see if payment section should display or not 9395 let amt = thisApi.GetDiscountedAmount(); 9396 9397 if (amt > 0) { 9398 9399 //special logic for forms like In-kind only, where amount is valid, but there are no fields. 9400 if (_inkindOnlyPaymentForm()) 9401 thisApi.PaymentMethodField.Section.Hide(); 9402 else { 9403 thisApi.PaymentMethodField.Section.Show(); 9404 _firePaymentMethodSectionShownEvent(); 9405 } 9406 9407 } 9408 else 9409 thisApi.PaymentMethodField.Section.Hide(); 9410 9411 } 9412 9413 _refreshCoverFeesWidget(); 9414 9415 _refreshFreeGiftSectionBasedOnAmountChange(); 9416 9417 _updateSubmitButtonText(); 9418 9419 }; 9420 9421 const _handlePaymentFrequencyChanged = function () { 9422 9423 _refreshPaymentAmountSectionForFormUpdates(); 9424 9425 _refreshFreeGiftSectionBasedOnAmountChange(); 9426 9427 _updateSubmitButtonText(); 9428 9429 _firePaymentRecurringFrequencyChangedEvent(); 9430 9431 }; 9432 9433 const _handlePaymentAmountChanged = function () { 9434 9435 _logger.Log('payment amount changed handler called'); 9436 9437 _refreshPaymentAmountSectionForFormUpdates(); 9438 9439 _updateFormAfterAmountImpactingChange(); 9440 9441 _firePaymentAmountChangedEvent(); 9442 9443 }; 9444 9445 const _handleRecurringPaymentAmountChanged = function () { 9446
9447 //_logger.Log('payment amount chaned handler called'); 9448 9449 //_refreshPaymentAmountSectionForFormUpdates(); 9450 // 9451 //_updateFormAfterAmountImpactingChange(); 9452 9453 _fireRecurringAmountChangedEvent(); 9454 9455 }; 9456 9457 let _lastCurrency_id = 0; 9458 9459 const _convertFormCurrency = function (fromCurrency_id, toCurrency_id) { 9460 9461 if (fromCurrency_id > 0 && toCurrency_id > 0 && fromCurrency_id != toCurrency_id) { 9462 9463 ////first update amount. 9464 //let transAmt = thisApi.Payment_XXX_Amount; 9465 // 9466 //if (transAmt > 0) { 9467 // 9468 // let newAmt = performCurrencyConversion(fromCurrency_id, transAmt, toCurrency_id, currencyMap, 2); 9469 // 9470 // if (newAmt != null) { 9471 // let newAmtRounded = roundEndUserSuggestedDonationAmount(newAmt); 9472 // thisApi.Payment_XXX_AmountField.SetValue(newAmtRounded); 9473 // } 9474 // else 9475 // _logger.Log('currency conversion failed'); 9476 // 9477 //} 9478 9479 for (let x = 0; x < thisApi.GenericAmountInputFields.length; x++) { 9480 9481 //first update amount. 9482 let transAmt = thisApi.GenericAmountInputFields[x].Value; 9483 9484 if (transAmt > 0) { 9485 9486 let newAmt = performCurrencyConversion(fromCurrency_id, transAmt, toCurrency_id, currencyMap, 2); 9487 9488 if (newAmt != null) { 9489 let newAmtRounded = roundEndUserSuggestedDonationAmount(newAmt); 9490 thisApi.GenericAmountInputFields[x].SetValue(newAmtRounded); 9491 } 9492 else 9493 _logger.Log('currency conversion failed'); 9494 9495 } 9496 9497 } 9498 9499 //if we have ask string, update them. 9500 9501 let newCurSym = _getPaymentCurrencySymbol(); 9502 9503 let asks = _getAmountAskStringInputs(); 9504 9505 for (let x = 0; x < asks.length; x++) { 9506 9507 let curAskAmt = asks[x].parentElement.tryGetDecimalAttribute('data-converted-amount'); 9508 9509 if (curAskAmt <= 0) 9510 curAskAmt = asks[x].value.toDecimalZeroIfInvalid(); 9511 9512 let newAskAmt = performCurrencyConversion(fromCurrency_id, curAskAmt, toCurrency_id, currencyMap, 2); 9513 if (newAskAmt != null) { 9514 9515 asks[x].parentElement.setAttribute('data-converted-amount', newAskAmt); 9516 9517 let newAskAmtRounded = roundEndUserSuggestedDonationAmount(newAskAmt); 9518 9519 asks[x].value = newAskAmtRounded; 9520 9521 let askBlockElem = asks[x].closest('.ce-amountAskStringButtonContainers'); 9522 9523 let lbl = getLabelForField(asks[x], askBlockElem); 9524 if (lbl != null) { 9525 9526 let lastSymb = lbl.getAttribute('data-currency-symbol'); 9527 9528 if (!isBlank(lastSymb)) { 9529 lbl.innerHTML = formatCurrency(newAskAmtRounded, false, newCurSym); 9530 lbl.setAttribute('data-currency-symbol', newCurSym) 9531 } 9532 9533 } 9534 9535 } 9536 else 9537 _logger.Log('currency conversion failed'); 9538 9539 } 9540 9541 } 9542 9543 }; 9544 9545 const _updateAllFieldCurrencyPrefixes = function () { 9546 9547 let sym = _getPaymentCurrencySymbol(); 9548 9549 let allPrefixes = document.querySelectorAll('.ce-formFieldCurrencyPrefixes'); 9550 for (let x = 0; x < allPrefixes.length; x++) 9551 allPrefixes[x].innerHTML = sym; 9552 9553 }; 9554 9555 const _handleCurrencyChanged = function () { 9556 9557 let currency_id = thisApi.PaymentCurrencyField.Value; 9558 9559 _logger.Log('currency changed to ' + currency_id); 9560 9561 if (currency_id > 0) { 9562 9563 _convertFormCurrency(_lastCurrency_id, currency_id); 9564 9565 _lastCurrency_id = currency_id; 9566 9567 } 9568 9569 _updateAllFieldCurrencyPrefixes(); 9570 9571 _refreshPaymentAmountSectionForFormUpdates();
9572 9573 _updateSubmitButtonText(); 9574 9575 _firePaymentCurrencyChangedEvent(); 9576 9577 }; 9578 9579 const _handlePaymentMethodChanged = function () { 9580 9581 //hide fields that are not applicable 9582 _hideAllPaymentFields(); 9583 9584 let payMethId = thisApi.PaymentMethodId; 9585 9586 _logger.Log('payment method changed to ' + payMethId); 9587 9588 _hideCoverFeesWidget(); 9589 9590 switch (payMethId) { 9591 case PAYMENT_METHODS.CREDIT_CARD: 9592 9593 _showCoverFeesWidget(); 9594 9595 let payWalletTypeId = _getPaymentWalletTypeId(); 9596 //_logger.Log('wallet: ' + payWalletTypeId); 9597 switch (payWalletTypeId) { 9598 case WALLET_TYPES.NONE: 9599 9600 _tryShowField(thisApi.CreditCardTypeField); 9601 _tryShowField(thisApi.CreditCardNumberField); 9602 _tryShowField(thisApi.CreditCardHolderNameField); 9603 _tryShowField(thisApi.CreditCardExpirationDateField); 9604 _tryShowField(thisApi.CreditCardExpirationMonthField); 9605 _tryShowField(thisApi.CreditCardExpirationYearField); 9606 _tryShowField(thisApi.CreditCardSecurityCodeField); 9607 9608 if (document.getElementById('togglePaymentMethodBillingInfoContainer') != null) 9609 document.getElementById('togglePaymentMethodBillingInfoContainer').show(); 9610 9611 _handleToggleBillingInfoClicked(); 9612 9613 ////TODO: do these need to be show/hidden via billing info different? 9614 //_tryShowField(thisApi.BillingAddressStreet1Field); 9615 //_tryShowField(thisApi.BillingAddressStreet2Field); 9616 //_tryShowField(thisApi.BillingAddressCityField); 9617 //_tryShowField(thisApi.BillingAddressStateProvinceField); 9618 //_tryShowField(thisApi.BillingAddressPostalCodeField); 9619 //_tryShowField(thisApi.BillingAddressCountryField); 9620 9621 thisApi.ShowSubmitButton(); 9622 9623 break;
9624 case WALLET_TYPES.GOOGLE_PAY: 9625 thisApi.GooglePayApi.ShowButton(); 9626 break; 9627 case WALLET_TYPES.APPLE_PAY: 9628 thisApi.ApplePayApi.ShowButton(); 9629 break; 9630 } 9631 9632 break; 9633 case PAYMENT_METHODS.ACH: 9634 9635 _showCoverFeesWidget(); 9636 9637 _tryShowField(thisApi.BankAccountNumberField); 9638 _tryShowField(thisApi.BankAccountTypeField); 9639 _tryShowField(thisApi.BankRoutingNumberField); 9640 _tryShowField(thisApi.BankAccountNameOnAccountField); 9641 _tryShowField(thisApi.ACHTransactionTypeField); 9642 _tryShowField(thisApi.BankNameField); 9643 9644 thisApi.ShowSubmitButton(); 9645 9646 break; 9647 case PAYMENT_METHODS.PAYPAL: 9648 _showCoverFeesWidget(); 9649 thisApi.PayPalApi.ShowButton(); 9650 break; 9651 case PAYMENT_METHODS.SOFT_CREDIT: 9652 _showCoverFeesWidget(); 9653 thisApi.DafPayApi.ShowButton(); 9654 break; 9655 case PAYMENT_METHODS.IN_KIND: 9656 9657 _refreshInKindFieldsByTypeSelected(); 9658 9659 thisApi.ShowSubmitButton(); 9660 9661 break; 9662 case PAYMENT_METHODS.NONE: 9663 case PAYMENT_METHODS.CHECK: 9664 case PAYMENT_METHODS.CASH: 9665 thisApi.ShowSubmitButton(); 9666 break; 9667 case PAYMENT_METHODS.ONECLICK_DONATION: 9668 9669 _showCoverFeesWidget(); 9670 9671 //LEGACY IDEA 9672 _setOneClickDefaultState(); 9673 9674 thisApi.ShowSubmitButton(); 9675 9676 break; 9677 } 9678 9679 _updateSubmitButtonText(); 9680 9681 _firePaymentMethodChangedEvent(); 9682 9683 }; 9684 9685 //Core Processing APIs (validation) 9686 9687 const _validateFieldById = function (field_id) { 9688 9689 let fld = _getFieldById(field_id); 9690 if (fld != null) 9691 fld.Validate(); //_validate_XXX_Field(fld); 9692 9693 }; 9694 9695 const _validateFieldElementRelative = function (fieldOrChildElement) { 9696 9697 if (fieldOrChildElement != null) { 9698 9699 let field_id = 0; 9700 9701 let actualFieldElem = fieldOrChildElement.firstParentOrSelfWithAttribute('data-field-id'); 9702 if (actualFieldElem != null) 9703 field_id = _getFieldElementFieldId(actualFieldElem); 9704 9705 if (field_id > 0) 9706 _validateFieldById(field_id); 9707 9708 } 9709 9710 }; 9711 9712 thisApi.ValidateSectionByPosition = function (sectionIndexPosition) { 9713 9714 let retVal = true; 9715 9716 const sec = _getSectionByIndex(sectionIndexPosition); 9717 if (sec != null) 9718 retVal = sec.Validate(); 9719 9720 return retVal; 9721 9722 }; 9723 9724 const _validateFormLevelValidationRules = function () { 9725 9726 const retVal = { validationResult: true, validationMessage: '' }; 9727 9728 //validate minimum charge amount 9729 const chargeAmt = thisApi.GetTransactionAmount(); 9730 if (retVal.validationResult && WebFormContext.Form.MinimumChargeAmount > chargeAmt) { 9731 retVal.validationResult = false; 9732 retVal.validationMessage = WebFormContext.Form.MinimumChargeAmountMessage; 9733 } 9734 9735 //validate volunteer post selection 9736 if (retVal.validationResult && thisApi.VolunteerPostsField != null && thisApi.SelectedVolunteerPostIds.length <= 0) { 9737 retVal.validationResult = false; 9738 retVal.validationMessage = 'Please select one or more volunteer posts'; 9739 } 9740 9741 9742 //validate opt-in list selection - if opt-in list options are present, one must be chosen 9743 if (retVal.validationResult && thisApi.EmailOptInListsField != null && thisApi.EmailOptInListsField.HasOptions && thisApi.SelectedEmailOptInListIds.length <= 0) { 9744 retVal.validationResult = false; 9745 retVal.validationMessage = 'Please select an email opt-in option'; 9746 } 9747 9748 //validate volunteer time selection 9749 if (retVal.validationResult && thisApi.VolunteerTimeEntryFields != null && thisApi.VolunteerTimeEntryFields.length > 0) { 9750 9751 let volunteerPostTimeEntryRows = document.querySelectorAll('.ce-volunteerPostTimeEntryFieldRows'); 9752 9753 let hasValidTimeEntry = false; 9754 let hasPartialTimeEntry = false; 9755 9756 for (let x = 0; x < volunteerPostTimeEntryRows.length; x++) { 9757 9758 let rowElem = volunteerPostTimeEntryRows[x]; 9759 9760 let startDate = rowElem.querySelector('.ce-volunteerPostTimeEntryStartDates').getValue().toDotNetAjaxDateTimeOrNull(); 9761 let endDate = rowElem.querySelector('.ce-volunteerPostTimeEntryEndDates').getValue().toDotNetAjaxDateTimeOrNull(); 9762 9763 if (startDate != null && endDate != null) 9764 hasValidTimeEntry = true; 9765 else if (startDate != null) 9766 hasPartialTimeEntry = true; 9767 else if (endDate != null) 9768 hasPartialTimeEntry = true; 9769 9770 } 9771 9772 if (!hasValidTimeEntry || hasPartialTimeEntry) { 9773 9774 retVal.validationResult = false; 9775 retVal.validationMessage = 'Please enter volunteer post start and end times'; 9776 9777 } 9778 9779 } 9780 9781 //validate if ticket quantities are on the form and the charge amount is $0, then ensure a t
9781icket was selected ($0 tickets) 9782 if (retVal.validationResult && thisApi.TicketQuantityFields.length > 0 && chargeAmt <= 0) { 9783 9784 let ticketsSelected = false; 9785 9786 for (let x = 0; x < thisApi.TicketQuantityFields.length; x++) 9787 if (thisApi.TicketQuantityFields[x].DomElement != null) { 9788 9789 if (thisApi.TicketQuantityFields[x].Value > 0) { 9790 ticketsSelected = true; 9791 break; 9792 } 9793 9794 } 9795 9796 if (!ticketsSelected) { 9797 retVal.validationResult = false; 9798 retVal.validationMessage = 'Please select at least one ticket or set a donation amount.'; 9799 } 9800 } 9801 9802 //validate ticket quantities don't exceed the max allowed (plus/minus buttons enforce this, but a manually typed value can bypass them) 9803 for (let x = 0; retVal.validationResult && x < thisApi.TicketQuantityFields.length; x++) 9804 if (thisApi.TicketQuantityFields[x].DomElement != null) { 9805 9806 const maxAllowed = thisApi.TicketQuantityFields[x].DomElement.tryGetIntegerAttribute('data-max-allowed'); 9807 9808 if (maxAllowed > 0 && thisApi.TicketQuantityFields[x].Value > maxAllowed) { 9809 retVal.validationResult = false; 9810 retVal.validationMessage = 'Please select ' + maxAllowed + ' or fewer of ' + getFieldLabelText(thisApi.TicketQuantityFields[x].LabelDomElement); 9811 } 9812 9813 } 9814 9815 //validate tickets that are sold out or outside their configured sale start/end date window aren't being purchased 9816 for (let x = 0; retVal.validationResult && x < thisApi.TicketQuantityFields.length; x++) 9817 if (thisApi.TicketQuantityFields[x].DomElement != null && !_isTicketPurchaseEnabled(thisApi.TicketQuantityFields[x]) && thisApi.TicketQuantityFields[x].Value > 0) { 9818 retVal.validationResult = false; 9819 retVal.validationMessage = getFieldLabelText(thisApi.TicketQuantityFields[x].LabelDomElement) + ' is not currently available for purchase'; 9820 } 9821 9822 //products 9823 if (typeof WebFormContext.Form.ProductWidgetApis != 'undefined' && WebFormContext.Form.ProductWidgetApis.length > 0) { 9824 9825 for (let x = 0;x < WebFormContext.Form.ProductWidgetApis.length;x++) { 9826 9827 if (WebFormContext.Form.ProductWidgetApis[x].SelectionDetails.Quantity > 0){ 9828 9829 const prodValResult = WebFormContext.Form.ProductWidgetApis[x].Validate(); 9830 9831 console.log('prodValResult: ' + prodValResult); 9832 9833 if (!prodValResult) { 9834 retVal.validationResult = false; 9835 retVal.validationMessage = 'Product selection not valid'; 9836 break; 9837 } 9838 9839 } 9840 9841 } 9842 9843 } 9844 9845 return retVal; 9846 9847 }; 9848 9849 let _formValidationSucceeded = true; 9850 thisApi.FormValidationMessage = ''; 9851 9852 //Form Validation 9853 thisApi.Validate = function () { 9854 9855 _formValidationSucceeded = true; 9856 thisApi.FormValidationMessage = ''; 9857 9858 //TODO: Add prevalidation Event 9859 9860 _logger.Log('validate ' + _allSectionApis.length + ' sections'); 9861 9862 for (let x = 0; x < _allSectionApis.length; x++) { 9863 9864 let secValidated = _allSectionApis[x].Validate(); 9865 if (!secValidated) 9866 _formValidationSucceeded = false; 9867 9868 } 9869 9870 if (_membershipAdditionalContactApi != null) { 9871 9872 let memSubMemValidated = _membershipAdditionalContactApi.ValidateRows(); 9873 if(!memSubMemValidated) 9874 _formValidationSucceeded = memSubMemValidated 9875 9876 } 9877 9878 //field level validation failed, show generic message 9879 if (!_formValidationSucceeded) 9880 thisApi.FormValidationMessage = 'Check all fields marked in red below'; 9881 9882 //check form level validation rules (ie min charge amount) 9883 let formLevelValidated = _validateFormLevelValidationRules();
9884 9885 if (!formLevelValidated.validationResult) { 9886 9887 _formValidationSucceeded = formLevelValidated.validationResult; 9888 9889 if (!isBlank(thisApi.FormValidationMessage)) 9890 thisApi.FormValidationMessage += '<br />'; 9891 9892 thisApi.FormValidationMessage += formLevelValidated.validationMessage; 9893 9894 } 9895 9896 //Should we have a valid and invalid event or just one saying validation has run? 9897 _fireFormValidatedEvent(); 9898 9899 return _formValidationSucceeded; 9900 9901 }; 9902 9903 //Core Processing Field Value Getters & Setters (simple one-to-one fields) 9904 9905 const _getPaymentCurrencySymbol = function () { return App.Currencies.GetCurrencySymbolByIdOrUSD(_tryGetIdFieldValueOrZero(thisApi.PaymentCurrencyField)); }; 9906 9907 const _getPaymentWalletTypeId = function () { 9908 9909 let retVal = WALLET_TYPES.NONE; 9910 9911 if (thisApi.PaymentMethodField != null && thisApi.PaymentMethodField.SelectedOptionDomElement != null) 9912 retVal = thisApi.PaymentMethodField.SelectedOptionDomElement.tryGetIntegerAttribute('data-wallet-type-id'); 9913 9914 return retVal; 9915 9916 }; 9917 9918 thisApi.GetPaymentCurrencySymbol = function () { return _getPaymentCurrencySymbol(); }; 9919 9920 thisApi.GetTicketQuantities = function () { 9921 9922 let retVal = []; 9923 9924 for (let x = 0; x < thisApi.TicketQuantityFields.length; x++) 9925 if (thisApi.TicketQuantityFields[x].DomElement != null) 9926 retVal.push({ "Ticket_Id": thisApi.TicketQuantityFields[x].DomElement.tryGetIntegerAttribute('data-ticket-id'), "Quantity": thisApi.TicketQuantityFields[x].Value }); 9927 9928 return retVal; 9929 9930 }; 9931 9932 thisApi.GetTicketCount = function () { 9933 9934 let retVal = 0; 9935 9936 for (let x = 0; x < thisApi.TicketQuantityFields.length; x++) 9937 if (thisApi.TicketQuantityFields[x].DomElement != null) 9938 retVal += thisApi.TicketQuantityFields[x].Value; 9939 9940 return retVal; 9941 9942 }; 9943 9944 thisApi.GetTicketGLCodeMapping = function () { 9945 9946 let retVal = []; 9947 9948 for (let x = 0; x < thisApi.TicketQuantityFields.length; x++) 9949 if (thisApi.TicketQuantityFields[x].DomElement != null && thisApi.TicketQuantityFields[x].Value > 0 && thisApi.TicketQuantityFields[x].DomElement.tryGetIntegerAttribute('data-ticket-glCode-id') > 0) { 9950 let glCodeId = thisApi.TicketQuantityFields[x].DomElement.tryGetIntegerAttribute('data-ticket-glCode-id'); 9951 9952 let index = retVal.findIndex(e => e.Id === glCodeId); 9953 9954 if (index > -1) 9955 retVal[index].Amount += (thisApi.TicketQuantityFields[x].Value * thisApi.TicketQuantityFields[x].DomElement.tryGetDecimalAttribute('data-ticket-price')); 9956 else 9957 retVal.push({ "Id": glCodeId, "Amount": (thisApi.TicketQuantityFields[x].Value * thisApi.TicketQuantityFields[x].DomElement.tryGetDecimalAttribute('data-ticket-price')) }); 9958 9959 } 9960 9961 return retVal; 9962 } 9963 9964 thisApi.GetRegistrationAmount = function () { 9965 9966 let retVal = 0; 9967 9968 for (let x = 0; x < thisApi.TicketQuantityFields.length; x++) 9969 if (thisApi.TicketQuantityFields[x].DomElement != null && thisApi.TicketQuantityFields[x].Value > 0) 9970 retVal += (thisApi.TicketQuantityFields[x].Value * thisApi.TicketQuantityFields[x].DomElement.tryGetDecimalAttribute('data-ticket-price')); 9971 9972 return retVal; 9973 9974 }; 9975 9976 thisApi.GetProductsAmount = function(){ 9977 9978 let retVal = 0.00; 9979 9980 if (typeof WebFormContext.Form.ProductWidgetApis != 'undefined' && WebFormContext.Form.ProductWidgetApis.length > 0) { 9981 9982 for (let x = 0;x < WebFormContext.Form.ProductWidgetApis.length;x++) 9983 if (WebFormContext.Form.ProductWidgetApis[x].SelectionDetails.Quantity > 0) 9984 retVal += (WebFormContext.Form.ProductWidgetApis[x].SelectionDetails.Quantity * WebFormContext.Form.ProductWidgetApis[x].SelectionDetails.UnitPrice); 9985 9986 } 9987 9988 return retVal; 9989 9990 }; 9991 9992 thisApi.GetRegistrationTaxDeductibleAmount = function () { 9993 9994 let retVal = 0; 9995 9996 for (let x = 0; x < thisApi.TicketQuantityFields.length; x++) 9997 if (thisApi.TicketQuantityFields[x].DomElement != null && thisApi.TicketQuantityFields[x].Value > 0) 9998 retVal += (thisApi.TicketQuantityFields[x].Value * thisApi.TicketQuantityFields[x].DomElement.tryGetDecimalAttribute('data-ticket-tax-deductible-amount')); 9999 10000 return retVal; 10001 10002 }; 10003 10004 thisApi.GetPreDiscountTaxDeductibleAmount = function () { 10005 10006 return (thisApi.GenericTaxDeductibleAmountInputsAmount + thisApi.GetRegistrationTaxDeductibleAmount() + _get
10006SelectedMembershipTaxDeductibleAmount()); 10007 10008 }; 10009 10010 thisApi.GetPreDiscountAmount = function () { 10011 10012 return (thisApi.GenericAmountInputsAmount + thisApi.GetRegistrationAmount() + thisApi.GetProductsAmount() + _getSelectedMembershipRate()); 10013 10014 }; 10015 10016 thisApi.GetPromoCodeApplicableTotalAmount = function () { 10017 10018 let regAmt = thisApi.GetRegistrationAmount(); 10019 let memAmt = _getSelectedMembershipRate(); 10020 10021 let retVal = (regAmt + memAmt); 10022 10023 //todo: do we need to add product amount to this? 10024 //we need special business rules to ensure amount is not a donation - we do not want to discount a donation 10025 //for now we will be somewhat restrictive and 10026 if (WebFormContext.Form.Type.Id != FORM_TYPES.Donation && WebFormContext.Form.Type.Id != FORM_TYPES.Event_Registration && WebFormContext.Form.Type.Id != FORM_TYPES.Membership && regAmt <= 0 && memAmt <= 0) 10027 retVal += thisApi.GenericAmountInputsAmount; 10028 10029 return retVal; 10030 10031 }; 10032 10033 thisApi.GetDiscountAmount = function () { 10034 10035 let retVal = 0.00; 10036 10037 if (thisApi.PromoCodeFieldApi != null) 10038 retVal = thisApi.PromoCodeFieldApi.GetDiscountAmount(); 10039 10040 return retVal; 10041 10042 }; 10043 10044 thisApi.GetTicketsDiscountAmount = function () { 10045 10046 let retVal = 0.00; 10047 10048 if (thisApi.PromoCodeFieldApi != null) 10049 retVal = thisApi.PromoCodeFieldApi.GetTicketsDiscountAmount(); 10050 10051 return retVal; 10052 10053 }; 10054 10055 thisApi.GetNonTicketDiscountAmount = function () { 10056 10057 let retVal = 0.00; 10058 10059 if (thisApi.PromoCodeFieldApi != null) 10060 retVal = thisApi.PromoCodeFieldApi.GetNonTicketDiscountAmount(); 10061 10062 return retVal; 10063 10064 }; 10065 10066 thisApi.GetDiscountedAmount = function () { 10067 10068 return (thisApi.GetPreDiscountAmount() - thisApi.GetDiscountAmount()); 10069 10070 }; 10071 10072 thisApi.GetTransactionAmount = function () { 10073 10074 let fees = _calculateFees(); 10075 10076 return (thisApi.GetDiscountedAmount() + fees); 10077 10078 }; 10079 10080 thisApi.GetRecurringTransactionAmount = function () { 10081 10082 let retVal = 0.00; 10083 10084 if (thisApi.RecurringPaymentFrequencyId > 0) { 10085 let fees = _calculateFees(); 10086 10087 retVal = thisApi.RecurringPaymentAmount + fees; 10088 } 10089 10090 return retVal; 10091 } 10092 10093 thisApi.GetTransactionTaxDeductibleAmount = function () { 10094 10095 let tdAmt = thisApi.GetPreDiscountTaxDeductibleAmount(); 10096 10097 let retVal = (tdAmt - thisApi.GetDiscountAmount()); 10098 10099 if (retVal < 0) 10100 retVal = 0; 10101 10102 return retVal; 10103 10104 }; 10105 10106 //CORE Processing - Form View Controllers 10107 10108 const _hideAllSections = function () { 10109 10110 for (let x = 0; x < _allSectionApis.length; x++) 10111 _allSectionApis[x].Hide(); 10112 10113 }; 10114 10115 const _updateFieldAndSectionVisibilityStateForView = function () { 10116 10117 switch (WebFormContext.RenderedViewNumber) { 10118 case FORM_RENDER_VIEWS.Unknown: 10119 case FORM_RENDER_VIEWS.LandingPage: 10120 10121 for (let x = 0; x < _allFieldApis.length; x++) 10122 if (_allFieldApis[x].Configuration.ShowOnLandingPage) { 10123 _logger.Log('show field ' + _allFieldApis[x].Configuration.DomId + ' based on landing page visiblity'); 10124 _allFieldApis[x].Show(); 10125 } 10126 else { 10127 _logger.Log('hide field ' + _allFieldApis[x].Configuration.DomId + ' based on landing page visiblity'); 10128 _allFieldApis[x].Hide(); 10129 } 10130 10131 for (let x = 0; x < _allSectionApis.length; x++) 10132 if (_allSectionApis[x].Configuration.ShowOnLandingPage) { 10133 _logger.Log('show section ' + _allSectionApis[x].Configuration.Id + ' based on landing page visiblity'); 10134 _allSectionApis[x].Show(); 10135 } 10136 else { 10137 _logger.Log('hide section ' + _allSectionApis[x].Configuration.Id + ' based on landing page visiblity'); 10138 _allSectionApis[x].Hide(); 10139 } 10140 10141 break; 10142 case FORM_RENDER_VIEWS.ConfirmationPage: 10143 10144 for (let x = 0; x < _allFieldApis.length; x++) 10145 if (_allFieldApis[x].Configuration.ShowOnConfirmationPage) 10146 _allFieldApis[x].Show(); 10147 else 10148 _allFieldApis[x].Hide(); 10149 10150 for (let x = 0; x < _allSectionApis.length; x++) 10151 if (_allSectionApis[x].Configuration.ShowOnConfirmationPage) 10152 _allSectionApis[x].Show(); 10153 else 10154 _allSectionApis[x].Hide(); 10155 10156 break; 10157 case FORM_RENDER_VIEWS.ErrorPage: 10158 10159 for (let x = 0; x < _allFieldApis.length; x++) 10160 if (_allFieldApis[x].Configuration.ShowOnErrorPage) 10161 _allFieldApis[x].Show(); 10162 else 10163 _allFieldApis[x].Hide(); 10164 10165 for (let x = 0; x < _allSectionApis.length; x++) 10166 if (_allSectionApis[x].Configuration.ShowOnErrorPage) 10167 _allSectionApis[x].Show(); 10168 else 10169 _allSectionApis[x].Hide(); 10170 10171 break; 10172 case FORM_RENDER_VIEWS.SuccessPage: 10173 10174 for (let x = 0; x < _allFieldApis.length; x++) 10175 if (_allFieldApis[x].Configuration.ShowOnSuccessPage) 10176 _allFieldApis[x].Show(); 10177 else 10178 _allFieldApis[x].Hide(); 10179 10180 for (let x = 0; x < _allSectionApis.length; x++) 10181 if (_allSectionApis[x].Configuration.ShowOnSuccessPage) 10182 _allSectionApis[x].Show(); 10183 else 10184 _allSectionApis[x].Hide(); 10185 10186 break; 10187 } 10188 10189 }; 10190 10191 const _setFormViewState = function (renderedViewNumber) { 10192 10193 thisApi.FormElement.removeClassName('ce-landingPageViews'); 10194 thisApi.FormElement.removeClassName('ce-confirmationPageViews'); 10195 thisApi.FormElement.removeClassName('ce-errorPageViews'); 10196 thisApi.FormElement.removeClassName('ce-successPageViews'); 10197 10198 WebFormContext.RenderedViewNumber = renderedViewNumber; // FORM_RENDER_VIEWS.LandingPage; 10199 10200 switch (WebFormContext.RenderedViewNumber) { 10201 case FORM_RENDER_VIEWS.LandingPage: 10202 WebFormContext.RenderedViewName = 'Landing Page'; 10203 thisApi.FormElement.addClassName('ce-landingPageViews'); 10204 break; 10205 case FORM_RENDER_VIEWS.ConfirmationPage: 10206 WebFormContext.RenderedViewName = 'Confirmation Page'; 10207 thisApi.FormElement.addClassName('ce-confirmationPageViews'); 10208 break; 10209 case FORM_RENDER_VIEWS.ErrorPage: 10210 WebFormContext.RenderedViewName = 'Error Page'; 10211 thisApi.FormElement.addClassName('ce-errorPageViews'); 10212 break; 10213 case FORM_RENDER_VIEWS.SuccessPage: 10214 WebFormContext.RenderedViewName = 'Success Page'; 10215 thisApi.FormElement.addClassName('ce-successPageViews'); 10216 break; 10217 } 10218 10219 10220 }; 10221 10222 const _clearHeaderMessageContent = function () { 10223 thisApi.HeaderMessagesContainerElement.removeClassName('error'); 10224 thisApi.HeaderMessagesContainerElement.removeClassName('success'); 10225 thisApi.HeaderMessagesContainerElement.removeClassName('warning'); 10226 _setHeaderMessageContent(); 10227 }; 10228 10229 const _setHeaderMessageContent = function (title, message) { 10230 10231 thisApi.HeaderMessagesContainerElement.innerHTML = ''; 10232 10233 if (!isBlank(title) || !isBlank(message)) { 10234 10235 thisApi.HeaderMessagesContainerElement.show(); 10236 10237 if (!isBlank(title)) 10238 thisApi.HeaderMessagesContainerElement.addHtml(title, 'headerDynamicMessageTitle'); 10239 10240 if (!isBlank(message)) 10241 thisApi.HeaderMessagesContainerElement.addHtml(message, 'headerDynamicMessageContent'); 10242 10243 scrollToTopOfWindow(); 10244 10245 } 10246 else 10247 thisApi.HeaderMessagesContainerElement.vanish(); 10248 10249 }; 10250 10251 const _setHeaderErrorContent = function (title, message) { 10252 thisApi.HeaderMessagesContainerElement.addClassName('error'); 10253 _setHeaderMessageContent(title, message); 10254 } 10255 10256 const _hideAllPageContentElements = function () { 10257 10258 if (thisApi.TitleContainerElement != null) 10259 thisApi.TitleContainerElement.vanish(); 10260 10261 if (thisApi.HeaderContainerElement != null) 10262 thisApi.HeaderContainerElement.vanish(); 10263 10264 if (thisApi.FooterContainerElement != null) 10265 thisApi.FooterContainerElement.vanish(); 10266 10267 }; 10268 10269 const _setPageContent = function (title, headerContent, footerContent) { 10270 10271 if (thisApi.TitleContainerElement != null) 10272 thisApi.TitleContainerElement.innerHTML = title; 10273 10274 if (thisApi.HeaderContainerElement != null) 10275 thisApi.HeaderContainerElement.innerHTML = headerContent; 10276 10277 if (thisApi.FooterContainerElement != null) 10278 thisApi.FooterContainerElement.innerHTML = footerContent; 10279 10280 }; 10281 10282 const _setDefaultView = function () { 10283 10284 _setFormViewState(FORM_RENDER_VIEWS.LandingPage); 10285 10286 _clearHeaderMessageContent(); 10287 10288 //moved to server side for token parsing 10289 //_setPageContent(WebFormContext.Form.DefaultViewContent.Title, WebFormContext.Form.DefaultViewContent.HeaderContent, WebFormContext.Form.DefaultViewContent.FooterContent); //Landing Page Content is Pre-Parsed 10290 10291 _updateFieldAndSectionVisibilityStateForView(); 10292 10293 _hideStartOverButton(); 10294 _hidePrintButton(); 10295 10296 }; 10297 10298 //TBD if we implement 10299 //const _setConfirmView = function (title, headerContent, footerContent) 10300 10301 const _setErrorView = function (error, errorMessage, pageTitle, headerContent, footerContent) { 10302 10303 _setFormViewState(FORM_RENDER_VIEWS.ErrorPage); 10304 10305 _setHeaderMessageContent(error, errorMessage); 10306 10307 //let's not change content for now, this migth be a deprecated
10307idea _setPageContent(pageTitle, headerContent, footerContent) 10308 10309 }; 10310 10311 const _getStartOverButton = function () { return document.getElementById('webFormStartOverButton'); }; 10312 10313 const _showStartOverButton = function () { _addStartOverButton(); document.getElementById('webFormStartOverButton').show(); }; 10314 const _hideStartOverButton = function () { if (document.getElementById('webFormStartOverButton') != null) document.getElementById('webFormStartOverButton').vanish(); }; 10315 10316 const _addStartOverButton = function () { 10317 10318 let sob = _getStartOverButton(); 10319 if (sob == null) 10320 thisApi.AddButton('webFormStartOverButton', 'Start Over', function () { reloadCurrentUrl(); }); 10321 10322 }; 10323 10324 const _getPrintButton = function () { return document.getElementById('webFormPrintButton'); }; 10325 10326 const _showPrintButton = function () { _addPrintButton(); document.getElementById('webFormPrintButton').show(); }; 10327 const _hidePrintButton = function () { if (document.getElementById('webFormPrintButton') != null) document.getElementById('webFormPrintButton').vanish(); }; 10328 10329 const _addPrintButton = function () { 10330 10331 let pb = _getPrintButton(); 10332 if (pb == null) 10333 thisApi.AddButton('webFormPrintButton', WebFormContext.Form.PrintButtonText, function () { printThisPage(); }); 10334 10335 }; 10336 10337 //TODO: we may not need this (might need to be returns from server as part of process transaction response) 10338 const _setSuccessView = function (title, successMessage, pageTitle, headerContent, footerContent) { 10339 10340 _setFormViewState(FORM_RENDER_VIEWS.SuccessPage); 10341 10342 //this is the system message 10343 _setHeaderMessageContent(title, successMessage); 10344 10345 //this is the configured page content 10346 _setPageContent(pageTitle, headerContent, footerContent) 10347 10348 _hideAllSections(); //new version hides fields on success // _updateFieldAndSectionVisibilityStateForView(); 10349 10350 thisApi.HideSubmitButton(); 10351 10352 _showStartOverButton(); 10353 10354 if (WebFormContext.Form.ShowPrintButtonOnSuccess) 10355 _showPrintButton(); 10356 10357 }; 10358 10359 //Third-Party Payment Method Handlers 10360 10361 const _handlePayPalTokenReceived = function (result) { 10362
10363 //_logger.Log('paypal token received: ' + JSON.stringify(result)); 10364 10365 thisApi.PaymentMethodField.SetValue(PAYMENT_METHODS.PAYPAL); 10366 10367 //_walletTypeId = WALLET_TYPES.NONE; 10368 10369 _payPalTokenId = result.PayPalToken; 10370 _payPalAmountChargedResponse = result.ChargeAmount; 10371 10372 }; 10373 10374 const _handlePaypalPaymentAuthorized = function (result) { 10375 10376 //_logger.Log('paypal payment authorized: ' + JSON.stringify(result)); 10377 10378 _payPalGatewayPayerId = result.PayPalGatewayPayerId; 10379 10380 thisApi.ProcessTransaction(); 10381 10382 }; 10383 10384 const _handleApplePayPaymentAuthorized = function (result) { 10385 10386 //_logger.Log('ApplePay payment authorized call back: ' + result); 10387 10388 //Pamyent method is already set to credit card at this point. This changes the payment method selection causing credit card field validation to occur 10389 //thisApi.PaymentMethodField.SetValue(PAYMENT_METHODS.CREDIT_CARD); 10390 10391 //_walletTypeId = WALLET_TYPES.APPLE_PAY; 10392 10393 _walletToken = result.ApplePayToken; 10394 10395 _walletCreditCardType_id = result.CardType_Id; 10396 _walletEciIndicator = result.ApplePayTokenEciIndicator; //required for first transaction 10397 10398 _tokenExpirationMonth = result.ExpirationMonth; 10399 _tokenExpirationYear = result.ExpirationYear; 10400 10401 _tokenCryptogram = result.ApplePayCryptogram; 10402 10403 thisApi.ProcessTransaction(); 10404 10405 }; 10406 10407 const _tryHideApplePayButton = function () { 10408 if (thisApi.ApplePayApi != null) thisApi.ApplePayApi.HideButton(); 10409 }; 10410 10411 const _tryHideGooglePayButton = function () { 10412 if (thisApi.GooglePayApi != null) thisApi.GooglePayApi.HideButton(); 10413 }; 10414 10415 thisApi.HandleGooglePayScriptLoaded = function () { 10416 10417 _logger.Log('google pay script loaded (handler started)'); 10418
10419 //_logger.Log('initialize googlepay API (' + (typeof thisApi.GooglePayApi) + ')'); 10420 if (thisApi.GooglePayApi != null && typeof thisApi.GooglePayApi != 'undefined' && thisApi.GooglePayApi.GooglePayEnabled) { 10421 _logger.Log('Initialize googlepay native button'); 10422 thisApi.GooglePayApi.RegisterPaymentAuthorizedCallBack(_handleGooglePayPaymentAuthorized); 10423 thisApi.GooglePayApi.Initialize(); 10424 } 10425 10426 _hideGooglePayOptionIfSdkNotLoaded(); 10427 10428 //if (thisApi.PaymentMethodField == null) 10429 // thisApi.GooglePayApi.HideButton(); 10430 10431 }; 10432 10433 const _handleGooglePayPaymentAuthorized = function (result) { 10434 10435 //_logger.Log('googlepay wallet token decrypted successfully'); 10436 10437 //Pamyent method is already set to credit card at this point. This changes the payment method selection causing credit card field validation to occur 10438 //thisApi.PaymentMethodField.SetValue(PAYMENT_METHODS.CREDIT_CARD); 10439 10440 //_walletTypeId = WALLET_TYPES.GOOG
10440LE_PAY; 10441 10442 _walletToken = result.paymentMethodDetails.pan; 10443 10444 _walletCreditCardType_id = 0; 10445 _walletEciIndicator = ''; //required for first transaction 10446 10447 _tokenExpirationMonth = result.paymentMethodDetails.expirationMonth; 10448 _tokenExpirationYear = result.paymentMethodDetails.expirationYear.toString().substr(-2); 10449 10450 _tokenCryptogram = ''; 10451 10452 thisApi.ProcessTransaction(); 10453 10454 }; 10455 10456 const _handleDafPayButtonClicked = function (result) { 10457 10458 //_logger.Log('capture DAFpay button clicked event'); 10459 10460 _dafPayWorkflowSessionId = ''; //always clear values at start 10461 10462 }; 10463 10464 const _handleDafPayPaymentAuthorized = function (result) { 10465 10466 //_logger.Log('DAFpay authorization call back: ' + JSON.stringify(result)); 10467 10468 if (!isBlank(result.DafPayResponse.workflowSessionId) && result.DafPayResponse.grantIntent != null && result.DafPayResponse.grantIntent.amount > 0) { 10469 10470 thisApi.PaymentMethodField.SetValue(PAYMENT_METHODS.SOFT_CREDIT); 10471 10472 //_walletTypeId = WALLET_TYPES.NONE; 10473 10474 let formattedDpAmount = Math.round(convertCentsToDollars(result.DafPayResponse.grantIntent.amount)); 10475 10476 //overwrite special for DafPay 10477 thisApi.FirstGenericAmountInputField.SetValue(formattedDpAmount); 10478 10479 _dafPayWorkflowSessionId = result.DafPayResponse.workflowSessionId; 10480 10481 thisApi.ProcessTransaction(); 10482 10483 } 10484 10485 //todo: reimplemnet for forms 10486 //else 10487 // showErrorStep('Unable to perform DAFpay check out. Please try another form of payment.', JSON.stringify(cbPresultaram
10487s)); 10488 10489 }; 10490 10491 const _handleDafPayPaymentAuthorizationFailed = function (result) { 10492 10493 //_logger.Log('web form capture DAFpay failed call back: ' + JSON.stringify(result)); 10494 10495 //todo: reimplemnet for forms 10496 //showErrorStep('Unable to perform DAFpay check out. Please try another form of payment.', JSON.stringify(result)); 10497 10498 }; 10499 10500 const _hideAllContactAddressFields = function () { 10501 10502 for (let x = 0; x < _allFieldApis.length; x++) 10503 if (_allFieldApis[x].IsContactAddressField) 10504 _allFieldApis[x].Hide(false); 10505 10506 let showDetLink = document.getElementById('contactAddressSingleLineEntryRowShowDetailsLink'); 10507 if (showDetLink != null) { 10508 10509 showDetLink.setAttribute('title', 'edit details'); 10510 10511 let icon = showDetLink.querySelector('.ce-formFieldEditButtonIcons'); 10512 if (icon != null) { 10513 10514 if (icon.hasClassName('fa')) { 10515 icon.removeClassName('fa-close'); 10516 icon.addClassName('fa-edit'); 10517 } 10518 else 10519 icon.innerText = 'add unit #'; 10520 10521 } 10522 10523 //showDetLink.innerText = 'edit'; //todo: future config to allow text versus icon - TBD 10524 10525 } 10526 10527 }; 10528 10529 const _showAllContactAddressFields = function () { 10530 10531 for (let x = 0; x < _allFieldApis.length; x++) 10532 if (_allFieldApis[x].IsContactAddressField) 10533 _allFieldApis[x].Show(); 10534 10535 let showDetLink = document.getElementById('contactAddressSingleLineEntryRowShowDetailsLink'); 10536 if (showDetLink != null) { 10537 10538 showDetLink.setAttribute('title', 'done editing'); 10539 10540 let icon = showDetLink.querySelector('.ce-formFieldEditButtonIcons'); 10541 if (icon != null) { 10542 10543 if (icon.hasClassName('fa')) { 10544 icon.removeClassName('fa-edit'); 10545 icon.addClassName('fa-close'); 10546 } 10547 else 10548 icon.innerText = 'done'; 10549 10550 } 10551 10552 //showDetLink.innerText = 'done'; //todo: future config to allow text versus icon - TBD 10553 10554 } 10555 10556 }; 10557 10558 const _handleContactSingleLineAddressShowDetailsClick = function () { 10559 10560 let showDetLink = document.getElementById('contactAddressSingleLineEntryRowShowDetailsLink'); 10561 if (showDetLink != null) { 10562 10563 let icon = showDetLink.querySelector('.ce-formFieldEditButtonIcons'); 10564 if (icon != null) { 10565 10566 if (icon.innerText.containsValue('done') || icon.hasClassName('fa-close')) 10567 _hideAllContactAddressFields(); 10568 else 10569 _showAllContactAddressFields(); 10570 10571 } 10572 10573 //todo: future config to allow text versus icon - TBD 10574 //if (showDetLink.innerText == 'edit') 10575 // _showAllContactAddressFields(); 10576 //else 10577 // _hideAllContactAddressFields(); 10578 10579 } 10580 10581 }; 10582 10583 const _handleContactSingleLineAddressIntegrationFailure = function () { 10584 10585 let showDetLink = document.getElementById('contactAddressSingleLineEntryRow'); 10586 if (showDetLink != null) { 10587 10588 showDetLink.vanish(); 10589 10590 _showAllContactAddressFields(); 10591 10592 } 10593 10594 }; 10595 10596 const _handleConactSingleLineAddressSelected = function (result) { 10597 10598 _logger.Log('contact single line address selected'); 10599 10600 let selectedPlace = result.Result; 10601 10602 let street1Formatted = parseGoogleAddressLine1Short(selectedPlace); 10603 let city = parseGoogleCityShortName(selectedPlace); 10604 let state = parseGoogleStateShortName(selectedPlace); 10605 let zip = parseGooglePostalCodeLongName(selectedPlace); 10606 let country = parseGoogleCountryShortName(selectedPlace); 10607 10608 _logger.Log('country: ' + country); 10609 10610 let country_id = App.Countries.GetCountryIdByCodeOrAliasOrUsa(country);
10611 _logger.Log('country_id: ' + country_id); 10612 10613 _logger.Log('street1Formatted: ' + street1Formatted); 10614 _logger.Log('city: ' + city); 10615 _logger.Log('state: ' + state); 10616 10617 let state_id = App.StateProvinces.GetCountryStateIdByNameOrAbbreviation(country_id, state); 10618 _logger.Log('state_id: ' + state_id); 10619 10620 _logger.Log('zip: ' + zip); 10621 10622 if (thisApi.ContactAddressStreet1Field != null && thisApi.ContactAddressStreet1Field.Value != street1Formatted) 10623 _trySetFieldValue(thisApi.ContactAddressStreet1Field, street1Formatted); 10624 10625 if (thisApi.ContactAddressCityField != null && thisApi.ContactAddressCityField.Value != city) 10626 _trySetFieldValue(thisApi.ContactAddressCityField, city); 10627 10628 //need to set country and update states/provinces before setting value 10629 if (thisApi.ContactAddressCountryField != null && thisApi.ContactAddressCountryField.Value != country_id) { 10630 _trySetFieldValue(thisApi.ContactAddressCountryField, country_id); 10631 App.StateProvinces.RepopulateStatesDropDown(thisApi.ContactAddressStateProvinceField.DomElement, country_id); 10632 } 10633 10634 if (thisApi.ContactAddressStateProvinceField != null && thisApi.ContactAddressStateProvinceField.Value != state_id) 10635 _trySetFieldValue(thisApi.ContactAddressStateProvinceField, state_id); 10636 10637 if (thisApi.ContactAddressPostalCodeField != null && thisApi.ContactAddressPostalCodeField.Value != zip) 10638 _trySetFieldValue(thisApi.ContactAddressPostalCodeField, zip); 10639 10640 //todo: this should be triggered if there's error conditions 10641 let badAddress = doesGoogleAddressRequireUserInteraction(selectedPlace); 10642 if (badAddress) 10643 _showAllContactAddressFields(); 10644 10645 }; 10646 10647 const _hideAllTributeAddressFields = function () { 10648 10649 for (let x = 0; x < _allFieldApis.length; x++) 10650 if (_allFieldApis[x].IsTributeRecipientAddressInformation) 10651 _allFieldApis[x].Hide(false); 10652 10653 let showDetLink = document.getElementById('tributeAddressSingleLineEntryRowShowDetailsLink'); 10654 if (showDetLink != null) { 10655 10656 showDetLink.setAttribute('title', 'edit details'); 10657 10658 let icon = showDetLink.querySelector('.ce-formFieldEditButtonIcons'); 10659 if (icon != null) { 10660 10661 if (icon.hasClassName('fa')) { 10662 icon.removeClassName('fa-close'); 10663 icon.addClassName('fa-edit'); 10664 } 10665 else 10666 icon.innerText = 'add unit #'; 10667 10668 } 10669 10670 //showDetLink.innerText = 'edit'; 10671 10672 } 10673 10674 }; 10675 10676 const _showAllTributeAddressFields = function () { 10677 10678 for (let x = 0; x < _allFieldApis.length; x++) 10679 if (_allFieldApis[x].IsTributeRecipientAddressInformation) 10680 _allFieldApis[x].Show(); 10681 10682 let showDetLink = document.getElementById('tributeAddressSingleLineEntryRowShowDetailsLink'); 10683 if (showDetLink != null) { 10684 10685 showDetLink.setAttribute('title', 'done editing'); 10686 10687 let icon = showDetLink.querySelector('.ce-formFieldEditButtonIcons'); 10688 if (icon != null) { 10689 10690 if (icon.hasClassName('fa')) { 10691 icon.removeClassName('fa-edit'); 10692 icon.addClassName('fa-close'); 10693 } 10694 else 10695 icon.innerText = 'done'; 10696 10697 } 10698 10699 //showDetLink.innerText = 'done'; //todo: future config to allow text versus icon - TBD 10700 10701 } 10702 10703 }; 10704 10705 const _handleTributeSingleLineAddressShowDetailsClick = function () { 10706 10707 let showDetLink = document.getElementById('tributeAddressSingleLineEntryRowShowDetailsLink'); 10708 if (showDetLink != null) { 10709 10710 let icon = showDetLink.querySelector('.ce-formFieldEditButtonIcons'); 10711 if (icon != null) { 10712 10713 if (icon.innerText.containsValue('done') || icon.hasClassName('fa-close')) 10714 _hideAllTributeAddressFields(); 10715 else 10716 _showAllTributeAddressFields(); 10717 10718 } 10719 10720 //todo: future config to allow text versus icon - TBD 10721 //if (showDetLink.innerText == 'edit') 10722 // _showAllTributeAddressFields(); 10723 //else 10724 // _hideAllTributeAddressFields(); 10725 10726 } 10727 10728 }; 10729 10730 const _handleTributeSingleLineAddressIntegrationFailure = function () { 10731
10732 let showDetLink = document.getElementById('tributeAddressSingleLineEntryRow'); 10733 if (showDetLink != null) { 10734 10735 showDetLink.vanish(); 10736 10737 _showAllContactAddressFields(); 10738 10739 } 10740 10741 }; 10742 10743 const _handleTributeSingleLineAddressSelected = function (result) { 10744 10745 _logger.Log('tribute single line address selected'); 10746 10747 let selectedPlace = result.Result; 10748 10749 let street1Formatted = parseGoogleAddressLine1Short(selectedPlace); 10750 let city = parseGoogleCityShortName(selectedPlace); 10751 let state = parseGoogleStateShortName(selectedPlace); 10752 let zip = parseGooglePostalCodeLongName(selectedPlace); 10753 let country = parseGoogleCountryShortName(selectedPlace); 10754 let country_id = App.Countries.GetCountryIdByCodeOrAliasOrUsa(country); 10755 let state_id = App.StateProvinces.GetCountryStateIdByNameOrAbbreviation(country_id, state); 10756 10757 if (thisApi.TributeRecipientAddressStreet1Field != null && thisApi.TributeRecipientAddressStreet1Field.Value != street1Formatted) 10758 _trySetFieldValue(thisApi.TributeRecipientAddressStreet1Field, street1Formatted); 10759 10760 if (thisApi.TributeRecipientAddressCityField != null && thisApi.TributeRecipientAddressCityField.Value != city) 10761 _trySetFieldValue(thisApi.TributeRecipientAddressCityField, city); 10762 10763 //need to set country and update states/provinces before setting value 10764 if (thisApi.TributeRecipientAddressCountryField != null && thisApi.TributeRecipientAddressCountryField.Value != country_id) { 10765 _trySetFieldValue(thisApi.TributeRecipientAddressCountryField, country_id); 10766 App.StateProvinces.RepopulateStatesDropDown(thisApi.TributeRecipientAddressStateField.DomElement, country_id); 10767 } 10768 10769 if (thisApi.TributeRecipientAddressStateField != null && thisApi.TributeRecipientAddressStateField.Value != state_id) 10770 _trySetFieldValue(thisApi.TributeRecipientAddressStateField, state_id); 10771 10772 if (thisApi.TributeRecipientAddressPostalCodeField != null && thisApi.TributeRecipientAddressPostalCodeField.Value != zip) 10773 _trySetFieldValue(thisApi.TributeRecipientAddressPostalCodeField, zip); 10774 10775 //todo: this should be triggered if there's error conditions 10776 let badAddress = doesGoogleAddressRequireUserInteraction(selectedPlace); 10777 if (badAddress) 10778 _showAllTributeAddressFields(); 10779 10780 }; 10781 10782 let _googleMapsFeaturesInitialized = false; 10783 10784 const _setFormFieldButtonText = function (btnElem, text) { btnElem.firstElementChild.innerText = text; }
10785 const _addFormFieldButtonIconCss = function (btnElem, iconCss) { btnElem.firstElementChild.addClassNames(iconCss); } 10786 const _setFormFieldButtonToolTip = function (btnElem, toolTip) { btnElem.setAttribute('title', toolTip); } 10787 10788 const _generateFormFieldButton = function (domId, clickCallbackFunction, toolTip) { 10789 10790 let retVal = document.createElement('button'); 10791 retVal.id = domId; 10792 retVal.setAttribute('type', 'button'); 10793 10794 if (!isBlank(toolTip)) 10795 _setFormFieldButtonToolTip(retVal, toolTip); 10796 10797 retVal.addClassName('ce-formFieldButtons'); 10798 10799 if (typeof clickCallbackFunction == 'function') 10800 retVal.addEventListener('click', function () { clickCallbackFunction(this); }); 10801 10802 let cntElem = document.createElement('span'); 10803 10804 cntElem.addClassName('ce-formFieldButtonContentContainers'); 10805 10806 retVal.appendChild(cntElem); 10807 10808 return retVal; 10809 10810 }; 10811 10812 const _generateFieldToggleEditButton = function (domId, renderAsIcon, clickCallbackFunction) { 10813 10814 let retVal = document.createElement('button'); 10815 retVal.id = domId; 10816 retVal.setAttribute('type', 'button'); 10817 retVal.setAttribute('title', 'edit details'); 10818 10819 retVal.addClassName('ce-formFieldEditButtons'); 10820 10821 if (typeof clickCallbackFunction == 'function') 10822 retVal.addEventListener('click', function () { clickCallbackFunction(); }); 10823 10824 let icon = document.createElement('span'); 10825 10826 icon.addClassName('ce-formFieldEditButtonIcons'); 10827 10828 if (renderAsIcon) 10829 icon.addClassNames('fa fa-edit'); 10830 else 10831 icon.innerText = 'edit'; 10832 10833 retVal.appendChild(icon); 10834 10835 return retVal; 10836 10837 }; 10838 10839 const _initializeGoogleMapsDependentFeatures = function () { 10840 10841 if (!_googleMapsFeaturesInitialized) { 10842 10843 _googleMapsFeaturesInitialized = true; 10844 10845 let firstContactInfoField = _getFirstEnabledContactAddressField(); 10846 if (firstContactInfoField != null) { 10847 10848 let row = _createFormRow('contactAddressSingleLineEntryRow', 'ce-addressSingleLineEntryRows'); 10849 10850 let fld = _createFormFieldContainer('contactAddressSingleLineEntryField'); 10851 10852 let fldWrapper = document.createElement('div'); 10853 fldWrapper.id = 'contactSingleLineAddressFieldWrapper'; 10854 fldWrapper.addClassNames('ce-formFieldWrappers ce-singleLineAddressFieldWrappers'); 10855 10856 /* 10857 New Google Maps API 10858 let contactSingleLineInput = new google.maps.places.PlaceAutocompleteElement(); 10859 //only available in BETA release of maps for now 10860 contactSingleLineInput.noClearButton = true; 10861 contactSingleLineInput.setAttribute('class', 'ce-googleMapsAutocomplete'); 10862 */ 10863 10864 //LEGACY GOOGLE MAPS API 10865 let contactSingleLineInput = document.createElement('input'); 10866 contactSingleLineInput.setAttribute('type', 'text'); 10867 //LEGACY GOOGLE MAPS API 10868 10869 contactSingleLineInput.id = 'contactAddressSingleLineInput'; 10870 contactSingleLineInput.setAttribute('placeholder', 'Start typing address...'); 10871 10872 let contactSingleLineInputLbl = document.createElement('label'); 10873 contactSingleLineInputLbl.setAttribute('for', contactSingleLineInput.id); 10874 contactSingleLineInputLbl.innerHTML = 'Address'; 10875 contactSingleLineInputLbl.addClassName('ce-formFieldLabels'); 10876 10877 fld.appendChild(contactSingleLineInputLbl); 10878 10879 fldWrapper.appendChild(contactSingleLineInput); 10880 10881 let editToggleBtn = _generateFieldToggleEditButton('contactAddressSingleLineEntryRowShowDetailsLink', false, function () { _handleContactSingleLineAddressShowDetailsClick(); }); 10882 10883 fldWrapper.appendChild(editToggleBtn); //fldWrapper.addHtmlClickLink('hide details', function () { _handleContactSingleLineAddressShowDetail
10883sClick(); }, 'contactAddressSingleLineEntryRowShowDetailsLink'); //todo: future config to allow text versus icon - TBD 10884 10885 fld.appendChild(fldWrapper); 10886 10887 row.appendChild(fld); 10888 10889 firstContactInfoField.RowDomElement.insertBeforeElement(row); 10890 10891 _hideAllContactAddressFields(); 10892 10893 thisApi.ContactSingleLineAddressApi = getSingleLineAddressFormFieldApi({ "AutoInitialize": false, "SingleLineAddressTextBoxDomId": contactSingleLineInput.id }); //New Google Maps API, "PlaceAutocompleteElement": contactSingleLineInput }); 10894 thisApi.ContactSingleLineAddressApi.RegisterAddressSelectedCallBack(_handleConactSingleLineAddressSelected); 10895 thisApi.ContactSingleLineAddressApi.RegisterGoogleMapsIntegrationFailureCallBack(_handleContactSingleLineAddressIntegrationFailure); 10896 thisApi.ContactSingleLineAddressApi.Initialize(); 10897 10898 _initializeDynamicLabelField(_tryGetContactAddressSingleLineEntryDomElement(),contactSingleLineInputLbl); 10899 10900 } 10901 else 10902 _logger.Log('skip contact single line address setup (not contact address fields)'); 10903 10904 let firstTributeContactInfField = _getFirstTributeRecipientContactInfoField(); 10905 if (firstTributeContactInfField != null) { 10906 10907 let row = _createFormRow('tributeAddressSingleLineEntryRow', 'ce-addressSingleLineEntryRows'); 10908 10909 let fld = _createFormFieldContainer('tributeAddressSingleLineEntryField'); 10910 10911 let fldWrapper = document.createElement('div'); 10912 fldWrapper.id = 'tributeSingleLineAddressFieldWrapper'; 10913 fldWrapper.addClassNames('ce-formFieldWrappers ce-singleLineAddressFieldWrappers'); 10914 10915 let tributeSingleLineInput = document.createElement('input'); 10916 tributeSingleLineInput.setAttribute('type', 'text'); 10917 tributeSingleLineInput.id = 'tributeAddressSingleLineInput'; 10918 tributeSingleLineInput.setAttribute('placeholder', 'Start typing address...'); 10919 10920 let tributeSingleLineInputLbl = document.createElement('label'); 10921 tributeSingleLineInputLbl.setAttribute('for', tributeSingleLineInput.id); 10922 tributeSingleLineInputLbl.innerHTML = 'Address'; 10923 tributeSingleLineInputLbl.addClassName('ce-formFieldLabels'); 10924 10925 fld.appendChild(tributeSingleLineInputLbl); 10926 10927 fldWrapper.appendChild(tributeSingleLineInput); 10928 10929 let editToggleBtn = _generateFieldToggleEditButton('tributeAddressSingleLineEntryRowShowDetailsLink', false, function () { _handleTributeSingleLineAddressShowDetailsClick(); }); 10930 10931 fldWrapper.appendChild(editToggleBtn); //fldWrapper.addHtmlClickLink('hide details', function () { _handleTributeSingleLineAddressShowDetailsClick(); }, 'tributeAddressSingleLineEntryRowShowDetailsLink'); 10932 10933 fld.appendChild(fldWrapper); 10934 10935 row.appendChild(fld); 10936 10937 firstTributeContactInfField.RowDomElement.insertBeforeElement(row); 10938 10939 _hideAllTributeAddressFields(); 10940 10941 thisApi.TributeSingleLineAddressApi = getSingleLineAddressFormFieldApi({ "AutoInitialize": false, "SingleLineAddressTextBoxDomId": tributeSingleLineInput.id }); 10942 thisApi.TributeSingleLineAddressApi.RegisterAddressSelectedCallBack(_handleTributeSingleLineAddressSelected); 10943 thisApi.TributeSingleLineAddressApi.RegisterGoogleMapsIntegrationFailureCallBack(_handleTributeSingleLineAddressIntegrationFailure); 10944 thisApi.TributeSingleLineAddressApi.Initialize(); 10945 10946 _initializeDynamicLabelField(_tryGetTributeRecipientAddressSingleLineEntryDomElement(),tributeSingleLineInputLbl); 10947 10948 } 10949 else 10950 _logger.Log('skip tribute single line address setup (not tribute address fields)'); 10951 10952 } 10953 10954 }; 10955 10956 const _googleMapsScriptLoadedHandler = function () { 10957
10958 _logger.Log('google maps location script loaded (handler started)'); 10959 10960 if (_initializeCompletedPreCallBacks) { 10961 10962 _logger.Log('fire google maps dependent feature setup rigth away (initialization completed)'); 10963 10964 _initializeGoogleMapsDependentFeatures(); 10965 10966 } 10967 else 10968 { 10969 10970 _logger.Log('register google maps dependent feature form ready call back (controlled timing & predictable results)'); 10971 10972 thisApi.RegisterFormReadyCallBack(function () { _initializeGoogleMapsDependentFeatures() }); 10973 10974 } 10975 10976 }; 10977 10978 thisApi.HandleGoogleMapsScriptLoaded = function () { _googleMapsScriptLoadedHandler(); }; 10979 10980 //CORE Processing APIs (process transactions) 10981 //******************************************* 10982 10983 thisApi.ProcessTransaction = function () { _processTransaction(); }; 10984 10985 const _resetExpiredRequestTokenAutoRetryCounter = function () { 10986 App.LocalStorage.Save('webFormExpiredTokenResubmitCount',0); //reset 10987 }; 10988 10989 const _processTransaction = function (userConfirmedNotDuplicate) { 10990 10991 let validationSucceeded = thisApi.Validate(); 10992 10993 _logger.Log('process transaction clicked (validation: ' + validationSucceeded + ')'); 10994 10995 _clearHeaderMessageContent(); 10996 10997 if (!validationSucceeded) 10998 _setHeaderErrorContent('Error', thisApi.FormValidationMessage); 10999 11000 if (validationSucceeded) { 11001 11002 //check for confirmation message needed 11003 let confirmProcess = true; 11004 if (!isBlank(WebFormContext.Form.NextButtonConfirmationMessage)) 11005 confirmProcess = confirm(WebFormContext.Form.NextButtonConfirmationMessage); 11006 11007 if (confirmProcess) { 11008 11009 let initiative_id = _tryGetFormContextIdOrZero(WebFormContext.Form.DefaultInitiative_Id); 11010 11011 let contextInitiative_id = _tryGetFormContextIdOrZero(WebFormContext.Form.ContextInitiative_Id); 11012 if (contextInitiative_id > 0) 11013 initiative_id = contextInitiative_id; 11014 11015 let audienceSegment_id = _tryGetFormContextIdOrZero(WebFormContext.Form.DefaultAudienceSegment_Id); 11016 11017 let contextAudienceSegment_id = _tryGetFormContextIdOrZero(WebFormContext.Form.ContextAudienceSegment_Id); 11018 if (contextAudienceSegment_id > 0) 11019 audienceSegment_id = contextAudienceSegment_id; 11020 11021 let glCode_id = _tryGetFormContextIdOrZero(WebFormContext.Form.GeneralLedgerCode_Id); 11022 11023 let defFund_id = _tryGetFormContextIdOrZero(WebFormContext.Form.DefaultFund_Id); 11024 let defRegFund_id = _tryGetFormContextIdOrZero(WebFormContext.Form.DefaultEventRegistrationFund_Id); 11025 11026 let donationFundToUse = defFund_id; 11027 11028 if (thisApi.FundId > 0) 11029 donationFundToUse = thisApi.FundId; 11030 11031 let transAmt = thisApi.GetTransactionAmount(); 11032 let donationAmt = thisApi.GenericAmountInputsAmount; 11033 11034 let coverFeesAmt = _calculateFees(); 11035 11036 let taxDeductibleAmt = thisApi.GetTransactionTaxDeductibleAmount(); 11037 11038 //TODO: Make fees being tax deductible configurable at the form or client level 11039 //No fees for DAFPay 11040 if (thisApi.PaymentMethodId != PAYMENT_METHODS.SOFT_CREDIT) 11041 taxDeductibleAmt += coverFeesAmt; 11042 11043 let fundsArray = []; 11044 11045 let tixCnt = thisApi.GetTicketCount(); 11046 if (tixCnt > 0 && defRegFund_id > 0 && donationFundToUse != defRegFund_id && transAmt > donationAmt) { 11047 11048 fundsArray.push({ "Id": defRegFund_id, "Amount": (transAmt - donationAmt) }); 11049 11050 if(donationFundToUse > 0) 11051 fundsArray.push({ "Id": donationFundToUse, "Amount": donationAmt }); 11052 11053 } 11054 else if (donationFundToUse > 0) 11055 fundsArray.push({ "Id": donationFundToUse, "Amount": transAmt }); 11056 11057 let glCodesArray = thisApi.GetTicketGLCodeMapping(); 11058 11059 if (glCode_id > 0 && donationAmt > 0) { 11060
11061 let glCodeIndex = glCodesArray.findIndex(e => e.Id === glCode_id); 11062 11063 if (glCodeIndex > -1) 11064 glCodesArray[glCodeIndex].Amount += donationAmt; 11065 else 11066 glCodesArray.push({ "Id": glCode_id, "Amount": donationAmt }); 11067 } 11068 11069 let solicitorContact_id = (thisApi.SolicitorId > 0) ? thisApi.SolicitorId : WebFormContext.Form.DefaultSolicitor_Id; 11070 11071 let transactionPayload = { 11072 "Form_Id": thisApi.Form_Id, 11073 "UserConfirmedNotDuplicate": userConfirmedNotDuplicate, 11074 "PaymentAmount": transAmt, 11075 "TaxDeductibleAmount": taxDeductibleAmt, 11076 "Currency": thisApi.PaymentCurrencyId, 11077 "DonationAmount": thisApi.GenericAmountInputsAmount, 11078 "CoverFeesAmount": coverFeesAmt, 11079 "Category_Id": _tryGetFormContextIdOrZero(WebFormContext.Form.Category_Id), 11080 "Contact": { 11081 "ContactType_Id": thisApi.ContactTypeId, 11082 "PersonInfo": { 11083 "FirstName": thisApi.ContactFirstName, 11084 "MiddleName": thisApi.ContactMiddleName, 11085 "LastName": thisApi.ContactLastName, 11086 "BirthDate": null, 11087 "Gender": null 11088 }, 11089 "OrganizationInfo": { 11090 "Name": thisApi.OrganizationName, 11091 "Type": null 11092 }, 11093 //"HouseholdInfo": { //maybe in future 11094 // "Name": null 11095 //}, 11096 "AddressStreet1": thisApi.ContactAddressStreet1, 11097 "AddressStreet2": thisApi.ContactAddressStreet2, 11098 "AddressCity": thisApi.ContactAddressCity, 11099 "AddressState": thisApi.ContactAddressStateProvinceCode, 11100 "AddressPostalCode": thisApi.ContactAddressPostalCode, 11101 "AddressCountry": thisApi.ContactAddressCountryCode, 11102 "EmailAddress": thisApi.ContactEmailAddress, 11103 "PhoneNumber": thisApi.ContactPhoneNumber, 11104 "FaxNumber": null, 11105 "SmsNumber": null, 11106 "MailOptIn": thisApi.MailOptIn, 11107 "EmailOptIn": thisApi.EmailOptIn, 11108 "PhoneOptIn": thisApi.PhoneOptIn, 11109 "FaxOptIn": null, 11110 "SmsOptIn": thisApi.SmsOptIn, 11111 "OptInListIds": thisApi.SelectedEmailOptInListIds, 11112 "GroupIds": thisApi.SelectedCrmGroupIds 11113 }, 11114 "MakeAnonymous": thisApi.Anonymous, 11115 "DisplayNameOnlineAs": null, 11116 "HideAmountOnline": false, 11117 "Comment": thisApi.Comment, 11118 "PaymentBillingInfo": { 11119 "AddressStreet1": thisApi.BillingAddressStreet1, 11120 "AddressStreet2": thisApi.BillingAddressStreet2, 11121 "AddressCity": thisApi.BillingAddressCity, 11122 "AddressStateProvince": thisApi.BillingAddressStateProvinceCode, 11123 "AddressPostalCode": thisApi.BillingAddressPostalCode, 11124 "AddressCountry": thisApi.BillingAddressCountryCode 11125 }, 11126 "Attribution": { 11127 "AudienceSegment_Id": audienceSegment_id, 11128 "Initiative_Id": initiative_id, 11129 "ResponseChannel_Id": _tryGetFormContextIdOrZero(WebFormContext.Form.ResponseChannel_Id), 11130 "Opportunity_Id": 0, 11131 "Event_Id": _tryGetFormContextIdOrZero(WebFormContext.Form.Event_Id), 11132 "GrassrootEvent_Id": thisApi.GrassrootsEventId, 11133 "PeerToPeerMicrosite_Id": 0, 11134 "PeerToPeerTeam_Id": 0, 11135 "PeerToPeerPage_Id": 0, 11136 "Tribute_Id": thisApi.TributeId, 11137 "Tribute": null, //populated below 11138 "Funds": fundsArray, 11139 "GeneralLedgerCodes": glCodesArray, 11140 "Solicitor": { 11141 "Contact_Id": solicitorContact_id 11142 }, 11143 "CustomCodes": { 11144 "Code1": thisApi.CustomCode1, 11145 "Code2": thisApi.CustomCode2, 11146 "Code3": thisApi.CustomCode3, 11147 "Code4": thisApi.CustomCode4, 11148 "Code5": thisApi.CustomCode5, 11149 "Code6": thisApi.CustomCode6, 11150 "Code7": thisApi.CustomCode7, 11151 "Code8": thisApi.CustomCode8 11152 } 11153 } 11154 }; 11155 11156 if (thisApi.FreeGiftSelectorProductId > 0) { 11157 transactionPayload.FreeGift = {}; 11158 transactionPayload.FreeGift.Product_Id = thisApi.FreeGiftSelectorProductId; 11159 transactionPayload.FreeGift.FulfillmentCenter_Id = thisApi.FreeGiftSelectorfulfillmentCenterId; 11160 } 11161 11162 //check for billing info same as contact info 11163 if (thisApi.PaymentMethodId == PAYMENT_METHODS.CREDIT_CARD) { 11164 11165 let isBillingInfoBlank = (isBlank(transactionPayload.PaymentBillingInfo.AddressStreet1) && isBlank(transactionPayload.PaymentBillingInfo.AddressCity) && isBlank(transactionPayload.PaymentBillingInfo.AddressPostalCode)); 11166 11167 if ((document.getElementById('togglePaymentMethodBillingInfoCheckBox') == null && isBillingInfoBlank) || document.getElementById('togglePaymentMethodBillingInfoCheckBox').getValue()) { 11168 11169 transactionPayload.PaymentBillingInfo.AddressStreet1 = transactionPayload.Contact.AddressStreet1; 11170 transactionPayload.PaymentBillingInfo.AddressStreet2 = transactionPayload.Contact.AddressStreet2; 11171 transactionPayload.PaymentBillingInfo.AddressCity = transactionPayload.Contact.AddressCity; 11172 transactionPayload.PaymentBillingInfo.AddressStateProvince = transactionPayload.Contact.AddressStateProvince; 11173 transactionPayload.PaymentBillingInfo.AddressPostalCode = transactionPayload.Contact.AddressPostalCode; 11174 transactionPayload.PaymentBillingInfo.AddressCountry = transactionPayload.Contact.AddressCountry; 11175 11176 } 11177 11178 } 11179 11180 let trbRecipRbl = document.getElementById('tributeRecipientToggle'); 11181 if (trbRecipRbl != null) { 11182 11183 let tribTpParam = TRIBUTE_TYPES.HONOR; 11184 11185 if (thisApi.TributeTypeId > 0) 11186 tribTpParam = thisApi.TributeTypeId 11187 11188 transactionPayload.Attribution.Tribute = {}; 11189 transactionPayload.Attribution.Tribute.Tribute_Id = 0; 11190 transactionPayload.Attribution.Tribute.Type_Id = tribTpParam; 11191 transactionPayload.Attribution.Tribute.TributeName = thisApi.TributeName; 11192 11193 //thisApi.TributeDescription 11194 11195 transactionPayload.Attribution.Tribute.Acknowedgees = []; 11196 11197 let ack = {}; 11198 ack.Message = thisApi.TributePersonalMessage; 11199 11200 ack.FirstName = thisApi.TributeRecipientFirstName; 11201 ack.LastName = thisApi.TributeRecipientLastName; 11202 11203 ack.ECard_Id = 0; 11204 ack.PrintCard_Id = 0; 11205 ack.PrintCardCount = 0; 11206 11207 let selVal = trbRecipRbl.getValue(); 11208 switch (selVal) { 11209 case 'mail': 11210 11211 ack.AddressStreet1 = thisApi.TributeRecipientAddressStreet1; 11212 ack.AddressStreet2 = thisApi.TributeRecipientAddressStreet2; 11213 ack.AddressCity = thisApi.TributeRecipientAddressCity; 11214 ack.AddressState = thisApi.TributeRecipientAddressStateCode; 11215 ack.AddressPostalCode = thisApi.TributeRecipientAddressPostalCode; 11216 ack.AddressCountry = thisApi.TributeRecipientAddressCountryCode; 11217 11218 ack.PrintCard_Id = thisApi.PrintCardId; 11219 ack.PrintCardCount = thisApi.PrintCardCount; 11220 11221 break; 11222 case 'email': 11223 11224 ack.EmailAddress = thisApi.TributeRecipientEmailAddress; 11225 11226 if(_isTributeEcardOptionSelected()) 11227 ack.ECard_Id = thisApi.ECardId; 11228 11229 break; 11230 case 'phone': 11231 //thisApi.TributeRecipientPhoneNumber 11232 break; 11233 } 11234 11235 transactionPayload.Attribution.Tribute.Acknowedgees.push(ack); 11236 11237 } 11238 11239 if (thisApi.PromoCodeFieldApi != null && thisApi.PromoCodeFieldApi.ValidateResponse != null && thisApi.PromoCodeFieldApi.ValidateResponse.Success) { 11240 11241 transactionPayload.PromoCode = {}; 11242 transactionPayload.PromoCode.Code = thisApi.PromoCodeFieldApi.ValidateResponse.PromoCode; 11243 transactionPayload.PromoCode.Promo_Id = thisApi.PromoCodeFieldApi.ValidateResponse.Promo_Id; 11244 transactionPayload.PromoCode.PromoCode_Id = thisApi.PromoCodeFieldApi.ValidateResponse.PromoCode_Id; 11245 transactionPayload.PromoCode.Discount = thisApi.GetDiscountAmount(); 11246 transactionPayload.PromoCode.RegistrationDiscount = thisApi.PromoCodeFieldApi.ValidateResponse.TicketsAppliedDiscount; 11247 transactionPayload.PromoCode.NonRegistrationDiscount = thisApi.PromoCodeFieldApi.ValidateResponse.NonTicketAppliedDiscount; 11248 11249 } 11250 11251 let relatedAccountMode = ''; 11252 11253 if (thisApi.MembershipTypeId > 0) 11254 relatedAccountMode = 'membership'; 11255 else if (WebFormContext.Form.Type.Id == FORM_TYPES.Pledge && thisApi.RecurringPaymentStartDateField != null) 11256 relatedAccountMode = 'pledge'; 11257 else if (thisApi.RecurringPaymentFrequencyId > 0) 11258 relatedAccountMode = 'sustainer'; 11259 11260 switch (relatedAccountMode) { 11261 case 'membership': 11262
11263 let membRate = _getSelectedMembershipRate(); 11264 let memTaxDeductibleAmt = _getSelectedMembershipTaxDeductibleAmount(); 11265 11266 transactionPayload.Membership = {}; 11267 transactionPayload.Membership.Type_Id = thisApi.MembershipTypeId; 11268 transactionPayload.Membership.Rate = membRate; 11269 transactionPayload.Membership.TaxDeductibleAmount = memTaxDeductibleAmt; 11270 11271 transactionPayload.Membership.PaymentPlanFrequency_Id = _getMembershipPaymentPlanFrequencyId(); 11272 11273 //need to consider implications of cover fees split with additional donation (handle server side for now) 11274 //transactionPayload.Membership.PaymentPlanAmount = membRate; 11275 //transactionPayload.Membership.PaymentPlanTaxDeductibleAmount = memTaxDeductibleAmt; 11276 11277 let memStartDate = thisApi.RecurringPaymentStartDate; 11278 if (memStartDate == null) 11279 memStartDate = new Date(); 11280 11281 transactionPayload.Membership.StartDate = formatDotNetJsonDate(memStartDate); 11282 11283 //annual memberships for now 11284 let durationDays = 365; 11285 11286 let dd = _getSelectedMembershipDurationDays(); 11287 if (dd > 0) 11288 durationDays = dd; 11289 11290 let memEndDate = null; 11291
11292 //_logger.Log('thisApi.AutoRenewMembership: ' + thisApi.AutoRenewMembership); 11293 if (!thisApi.AutoRenewMembership) { 11294 memEndDate = new Date(); 11295 memEndDate = memEndDate.setDate(memStartDate.getDate() + durationDays); 11296 transactionPayload.Membership.EndDate = formatDotNetJsonDate(memEndDate); 11297 } 11298 11299 //add sub memberships 11300 let allowSubMembers = getMultiSelectInputSelectedOptionBoolAttributeValue(thisApi.MembershipTypeField.DomElement, 'data-membership-allow-sub-members'); 11301 if (allowSubMembers) 11302 transactionPayload.Membership.SubMembers = _membershipAdditionalContactApi.GetContactDetails(); 11303 11304 //TODO: add custom installment if needed in future - e.g. Membership.Installment.push(); 11305 11306 break; 11307 case 'pledge': 11308 11309 transactionPayload.Pledge = {}; 11310 transactionPayload.Pledge.Type_Id = thisApi.PledgeTypeId; 11311 transactionPayload.Pledge.Amount = thisApi.FirstGenericAmountInputAmount + coverFeesAmt; 11312 transactionPayload.Pledge.TaxDeductibleAmount = thisApi.FirstGenericTaxDeductibleAmountInputAmount + coverFeesAmt; 11313 11314 transactionPayload.Pledge.Invoices = []; 11315 11316 let frstInvoice = {}; 11317 frstInvoice.DueDate = thisApi.RecurringPaymentStartDateAjaxFormat; 11318 frstInvoice.Amount = transactionPayload.Pledge.Amount; 11319 frstInvoice.TaxDeductibleAmount = transactionPayload.Pledge.TaxDeductibleAmount; 11320 11321 transactionPayload.Pledge.Invoices.push(frstInvoice); 11322 11323 //update payment amount based on installements or future date 11324 if (thisApi.RecurringPaymentStartDate != null && thisApi.RecurringPaymentStartDate > new Date()) { 11325 transactionPayload.PaymentAmount = 0.00; 11326 transactionPayload.TaxDeductibleAmount = 0.00; 11327 } 11328 11329 break; 11330 case 'sustainer': 11331 11332 let subsType_id = 0; //@webFormSettings.def_recurringSubscriptionType_id 11333 11334 transactionPayload.Sustainer = {}; 11335 transactionPayload.Sustainer.Type_Id = subsType_id; 11336 transactionPayload.Sustainer.Amount = thisApi.RecurringPaymentAmount + coverFeesAmt; 11337 transactionPayload.Sustainer.TaxDeductibleAmount = thisApi.RecurringPaymentTaxDeductibleAmount + coverFeesAmt; 11338 transactionPayload.Sustainer.Frequency_Id = thisApi.RecurringPaymentFrequencyId; 11339 transactionPayload.Sustainer.StartDate = thisApi.RecurringPaymentStartDateAjaxFormat; 11340 transactionPayload.Sustainer.EndDate = thisApi.RecurringPaymentEndDateAjaxFormat; 11341 transactionPayload.Sustainer.TotalAmountLimit = thisApi.RecurringMaxAmount; 11342 11343 //update payment amount based on installements or future date 11344 if (thisApi.RecurringPaymentStartDate != null && thisApi.RecurringPaymentStartDate > new Date()) { 11345 transactionPayload.PaymentAmount = 0.00; 11346 transactionPayload.TaxDeductibleAmount = 0.00; 11347 } 11348 11349 break; 11350 } 11351 11352 //add tickets 11353 //duplicated (used above) 11354 //let tixCnt = thisApi.GetTicketCount(); 11355 if (tixCnt > 0) { 11356 11357 transactionPayload.Registration = {}; 11358 transactionPayload.Registration.DietaryRestrictions = thisApi.DietaryRestriction; 11359 transactionPayload.Registration.EmergencyContactName = thisApi.EmergencyContactName; 11360 transactionPayload.Registration.EmergencyContactPhoneNumber = thisApi.EmergencyContactPhoneNumber; 11361 transactionPayload.Registration.SelectedTickets = []; 11362 11363 for (let x = 0; x < thisApi.TicketQuantityFields.length; x++) 11364 if (thisApi.TicketQuantityFields[x].DomElement != null && thisApi.TicketQuantityFields[x].Value > 0) { 11365 11366 let tick_id = thisApi.TicketQuantityFields[x].DomElement.tryGetIntegerAttribute('data-ticket-id'); 11367 11368 let newTick = {}; 11369 11370 newTick.Ticket_Id = tick_id; 11371 newTick.Quantity = thisApi.TicketQuantityFields[x].Value; 11372 newTick.Price = thisApi.TicketQuantityFields[x].DomElement.tryGetDecimalAttribute('data-ticket-price'); 11373 newTick.TaxDeductible = thisApi.TicketQuantityFields[x].DomElement.tryGetDecimalAttribute('data-ticket-tax-deductible-amount'); 11374 11375 newTick.RegistrationStatus_Id = 0; 11376 newTick.RegistrantCheckedIn = false; 11377 11378 newTick.Session_Id = 0; 11379 11380 if (thisApi.TicketSessionFields != null && thisApi.TicketSessionFields.length > 0) 11381 for (let y = 0; y < thisApi.TicketSessionFields.length; y++) 11382 if (thisApi.TicketSessionFields[y].DomElement.tryGetIntegerAttribute('data-ticket-id') == tick_id) { 11383 newTick.Session_Id = thisApi.TicketSessionFields[y].Value.toIntZeroIfInvalid(); 11384 break; 11385 } 11386 11387 newTick.DifferentRegistrantInfo = _getTicketMeOrSomeoneElseApi(tick_id)?.GetContactDetails(); 11388 11389 newTick.AdditionalRegistrants = _getMultipleTicketsOtherRegistrantsApi(tick_id)?.GetContactDetails(); 11390 11391 newTick.Guests = _getTicketAdditionalContactsApi(tick_id)?.GetContactDetails(); 11392 11393 transactionPayload.Registration.SelectedTickets.push(newTick); 11394 11395 } 11396 11397 } 11398 11399 //volunteer posts 11400 if (thisApi.SelectedVolunteerPostIds.length > 0) { 11401 11402 transactionPayload.VolunteerPostSignUpInfo = {}; 11403 11404 transactionPayload.VolunteerPostSignUpInfo.SelectedPostIds = thisApi.SelectedVolunteerPostIds; 11405 11406 } 11407 11408 //volunteer post time entry 11409 let volunteerPostTimeEntryWrappers = document.querySelectorAll('.ce-volunteerPostTimeEntryFieldRows'); 11410 if (volunteerPostTimeEntryWrappers.length > 0) { 11411 11412 transactionPayload.VolunteerTimeEntryInfo = []; 11413 11414 for (let x = 0; x < volunteerPostTimeEntryWrappers.length; x++) { 11415 11416 let rowElem = volunteerPostTimeEntryWrappers[x]; 11417 11418 let post_id = rowElem.tryGetIntegerAttribute('data-volunteer-post-id'); 11419 let startDate = rowElem.querySelector('.ce-volunteerPostTimeEntryStartDates').getValue().toDotNetAjaxDateTimeOrNull(); 11420 let endDate = rowElem.querySelector('.ce-volunteerPostTimeEntryEndDates').getValue().toDotNetAjaxDateTimeOrNull(); 11421 11422 if (post_id > 0 && startDate != null && endDate != null) { 11423 11424 let tei = {}; 11425 tei.VolunteerPost_Id = post_id; 11426 tei.StartTime = startDate; 11427 tei.EndTime = endDate; 11428 11429 transactionPayload.VolunteerTimeEntryInfo.push(tei); 11430 11431 } 11432 11433 } 11434 11435 } 11436 11437 //cases 11438 //WebFormContext.Form.Type.Id == FORM_TYPES.Case_Intake && 11439 if (thisApi.CaseTitleField != null || thisApi.CaseDescription != null) { 11440 11441 transactionPayload.CaseInfo = {}; 11442 11443 if (thisApi.CaseTitleField != null) 11444 transactionPayload.CaseInfo.Title = thisApi.CaseTitle; 11445 else 11446 transactionPayload.CaseInfo.Title = 'Online Case Submission'; 11447 11448 transactionPayload.CaseInfo.Status_Id = thisApi.CaseStatusId; 11449 transactionPayload.CaseInfo.Priority_Id = thisApi.CasePriorityId; 11450 transactionPayload.CaseInfo.Description = thisApi.CaseDescription; 11451 11452 } 11453 11454 //products 11455 if (typeof WebFormContext.Form.ProductWidgetApis != 'undefined' && WebFormContext.Form.ProductWidgetApis.length > 0) { 11456 11457 transactionPayload.Products = []; 11458 11459 for (let x = 0;x < WebFormContext.Form.ProductWidgetApis.length;x++) 11460 if(WebFormContext.Form.ProductWidgetApis[x].SelectionDetail
11460s.Quantity > 0){ 11461 11462 //console.log('SelectionDetails: ' + JSON.stringify(WebFormContext.Form.ProductWidgetApis[x].SelectionDetails)); 11463 11464 transactionPayload.Products.push(WebFormContext.Form.ProductWidgetApis[x].SelectionDetails); 11465 11466 } 11467 11468 } 11469 11470 //_logger.Log('build payload with _oneClickProfile_id: ' + thisApi.OneClickCheckOutApiPaymentProfile_Id); 11471 11472 if (thisApi.OneClickCheckOutApi != null && thisApi.OneClickCheckOutApi.EnabledByUser && thisApi.OneClickCheckOutApi.PaymentProfile_Id > 0) 11473 transactionPayload.OneClickProfile_Id = thisApi.OneClickCheckOutApi.PaymentProfile_Id; 11474 else { 11475 11476 _logger.Log('build payload with payment method: ' + thisApi.PaymentMethodId); 11477 11478 switch (thisApi.PaymentMethodId) { 11479 case PAYMENT_METHODS.CREDIT_CARD: 11480 11481 let payWalletTypeId = _getPaymentWalletTypeId(); 11482 switch (payWalletTypeId) { 11483 case WALLET_TYPES.NONE: 11484 11485 let expMonth = thisApi.CreditCardExpirationMonth; 11486 let expYear = thisApi.CreditCardExpirationYear; 11487 11488 const parsedExpDates = parseExpirationDateParts(thisApi.CreditCardExpirationDate); 11489 if (!isBlank(parsedExpDates.month) && !isBlank(parsedExpDates.year)) { 11490 expMonth = parsedExpDates.month; 11491 expYear = parsedExpDates.year; 11492 } 11493 11494 transactionPayload.CreditCardInfo = { 11495 "CardNumber": thisApi.CreditCardNumber, 11496 "ExpirationMonth": expMonth, 11497 "ExpirationYear": expYear, 11498 "NameOnCard": thisApi.CreditCardHolderName, 11499 "CVV": thisApi.CreditCardSecurityCode 11500 }; 11501 11502 break;
11503 case WALLET_TYPES.GOOGLE_PAY: 11504 11505 transactionPayload.ThirdPartyPaymentInfo = { 11506 "PlatformId": THIRD_PARTY_PAYMENT_PLATFORMS.GooglePay, 11507 "PlatformToken": _walletToken, 11508 "PlatformTokenExpirationMonth": _tokenExpirationMonth, 11509 "PlatformTokenExpirationYear": _tokenExpirationYear, 11510 "PlatformWalletCreditCardTypeId": _walletCreditCardType_id, //is this applicable to GooglePay? 11511 }; 11512 11513 break; 11514 case WALLET_TYPES.APPLE_PAY: 11515 11516 transactionPayload.ThirdPartyPaymentInfo = { 11517 "PlatformId": THIRD_PARTY_PAYMENT_PLATFORMS.ApplePay, 11518 "PlatformToken": _walletToken, 11519 "PlatformTokenExpirationMonth": _tokenExpirationMonth, 11520 "PlatformTokenExpirationYear": _tokenExpirationYear, 11521 "PlatformWalletCreditCardTypeId": _walletCreditCardType_id, 11522 "PlatformWalletEciIndicatorId": _walletEciIndicator, 11523 "PlatformTokenCryptoGram": _tokenCryptogram 11524 }; 11525 11526 break; 11527 } 11528 11529 break; 11530 case PAYMENT_METHODS.ACH: 11531 11532 transactionPayload.BankAccountInfo = { 11533 "BankAccountType": thisApi.BankAccountTypeId, 11534 "BankName": null, 11535 "BankCity": null, 11536 "BankStateProvince": null, 11537 "RoutingNumber": thisApi.BankRoutingNumber, 11538 "AccountNumber": thisApi.BankAccountNumber, 11539 "NameOnAccount": thisApi.BankAccountNameOnAccount 11540 }; 11541 11542 break; 11543 case PAYMENT_METHODS.PAYPAL: 11544 11545 transactionPayload.ThirdPartyPaymentInfo = { 11546 "PlatformId": THIRD_PARTY_PAYMENT_PLATFORMS.PayPal, 11547 "PlatformToken": _payPalTokenId, 11548 "PayerAccountId": _payPalGatewayPayerId, 11549 "ChargedAmount": _payPalAmountChargedResponse 11550 }; 11551 11552 break; 11553 case PAYMENT_METHODS.SOFT_CREDIT: 11554 11555 transactionPayload.ThirdPartyPaymentInfo = { 11556 "PlatformId": THIRD_PARTY_PAYMENT_PLATFORMS.DAFPay, 11557 "PlatformToken": _dafPayWorkflowSessionId 11558 }; 11559 11560 transactionPayload.PaymentAmount = thisApi.GetDiscountedAmount(); //no fees for DAFPay 11561 transactionPayload.CoverFeesAmount = 0.00; //no fees for DAFPay 11562 11563 break; 11564 case PAYMENT_METHODS.IN_KIND: 11565 11566 //NOTE: amount is used for value of asset (seamless) 11567 11568 if (thisApi.InKindTypeClassId > 0) { 11569 11570 _logger.Log('configure inkind details for class ' + thisApi.InKindTypeClassId); 11571 11572 transactionPayload.InKindGiftInfo = {}; 11573 transactionPayload.InKindGiftInfo.Type_Id = thisApi.InKindTypeId; 11574 transactionPayload.InKindGiftInfo.Quantity = 1; 11575 transactionPayload.InKindGiftInfo.Description = null; 11576 11577 switch (thisApi.InKindTypeClassId) { 11578 case IN_KIND_ASSET_CLASSES.AUTOMOBILES: 11579 11580 transactionPayload.InKindGiftInfo.VehicleInfo = {}; 11581 11582 transactionPayload.InKindGiftInfo.VehicleInfo.Year = thisApi.InKindVehicleYear; 11583 transactionPayload.InKindGiftInfo.VehicleInfo.Make = thisApi.InKindVehicleMake; 11584 transactionPayload.InKindGiftInfo.VehicleInfo.Mileage = thisApi.InKindVehicleMileage; 11585 transactionPayload.InKindGiftInfo.VehicleInfo.VIN = thisApi.InKindVehicleVin; 11586 11587 break; 11588 case IN_KIND_ASSET_CLASSES.STOCKS: 11589 11590 transactionPayload.InKindGiftInfo.StockBondInfo = {}; 11591 11592 transactionPayload.InKindGiftInfo.StockBondInfo.Ticker = thisApi.InKindStockTicker; 11593 transactionPayload.InKindGiftInfo.StockBondInfo.Company = thisApi.InKindStockCompany; 11594 11595 break; 11596 case IN_KIND_ASSET_CLASSES.FOOD: 11597 11598 transactionPayload.InKindGiftInfo.FoodInfo = {}; 11599 11600 transactionPayload.InKindGiftInfo.FoodInfo.WeightLbs = thisApi.InKindFoodWeight; 11601 11602 break; 11603 } 11604 11605 } 11606 else 11607 _logger.LogWarning('check out is set to in kind payment method but no class is present'); 11608 11609 break; 11610 case PAYMENT_METHODS.CASH: 11611 transactionPayload.IsCashPayment = true; 11612 break; 11613 case PAYMENT_METHODS.CHECK: 11614 transactionPayload.IsCheckPayment = true; 11615 break; 11616 case PAYMENT_METHODS.NONE: 11617 //nothing 11618 break; 11619 } 11620 11621 } 11622 11623 transactionPayload.Fields = []; 11624 11625 for (let x = 0; x < _allFieldApis.length; x++) { 11626 11627 let payloadField = {}; 11628 11629 //handle file upload fields 11630 if (_allFieldApis[x].Configuration.Type.Id == FORM_FIELD_TYPES.FILE) { 11631 11632 let fileVal = _allFieldApis[x].GetBase64Value(); 11633 if (!isBlank(fileVal)) 11634 { 11635 payloadField.Id = _allFieldApis[x].Configuration.Id; 11636 payloadField.FileName = _allFieldApis[x].GetFileNameValue(); 11637 payloadField.FileDataBase64 = fileVal; 11638 } 11639 11640 } 11641 else 11642 { 11643 11644 if (!isBlank(_allFieldApis[x].Value)) { 11645 payloadField.Id = _allFieldApis[x].Configuration.Id;
11646 payloadField.Value = _allFieldApis[x].Value; 11647 } 11648 11649 } 11650 11651 if(payloadField.Id > 0) 11652 transactionPayload.Fields.push(payloadField); 11653 11654 } 11655 11656 _clearHeaderMessageContent(); 11657 11658 _trans_id = 0; 11659 _transAmount = 0; 11660 11661 _saveLastFormFillContactInfo(); 11662 11663 //_logger.Log(JSON.stringify(transactionPayload)); 11664 11665 const processFunc = function () { 11666 11667 CE.APPS.ProcessWebFormTransaction({ "ajaxParams": transactionPayload }, 11668 function (result) { 11669 11670 if (result.Success && (!result.PaymentAttempted || result.PaymentSuccessful)) { 11671 11672 _trans_id = result.Transaction_Id; 11673 _transAmount = result.PaymentAmount; 11674 11675 _setSuccessView('', '', result.ResponsePageTitle, result.ResponseTopContent, result.ResponseBottomContent); 11676 11677 //check for double the donation 11678 if (!isBlank(WebFormContext.Form.DoubleTheDonationPublicKey) && window.doublethedonation && document.getElementById('dd-container') != null) { 11679 11680 document.getElementById('dd-container').show(); 11681 11682 let ddNowFormatted = nowFormattedMMDDYYYYHHMMSSAMPM() + ' EST'; 11683 11684 let ddPayLoad = { 11685 "360matchpro_public_key": WebFormContext.Form.DoubleTheDonationPublicKey, 11686 "partner_identifier": 'CHARI-O9zijvuqnBKeFA9o', 11687 "campaign": transactionPayload.Attribution.CustomCodes.Code1, 11688 "donation_identifier": result.Transaction_Id, 11689 "donation_amount": result.PaymentAmount, 11690 "donor_first_name": transactionPayload.Contact.PersonInfo.FirstName, 11691 "donor_last_name": transactionPayload.Contact.PersonInfo.LastName, 11692 "donor_email": transactionPayload.Contact.EmailAddress, 11693 "donor_address": { 11694 "zip": transactionPayload.Contact.AddressPostalCode, 11695 "city": transactionPayload.Contact.AddressCity, 11696 "state": transactionPayload.Contact.AddressState, 11697 "address1": transactionPayload.Contact.AddressStreet1, 11698 "address2": transactionPayload.Contact.AddressStreet1 11699 }, 11700 "donation_datetime": ddNowFormatted 11701 }; 11702 11703 //console.log('dd paylod: ' + JSON.stringify(ddPayLoad)); 11704 11705 doublethedonation.integrations.core.register_donation(ddPayLoad); 11706 11707 doublethedonation.plugin.load_config(); 11708 doublethedonation.plugin.set_donation_identifier(result.Transaction_Id); 11709 doublethedonation.plugin.set_donation_campaign("CharityEngine"); 11710 11711 let domain = doublethedonation.integrations.core.strip_domain(transactionPayload.Contact.EmailAddress); 11712 11713 doublethedonation.plugin.email_domain(domain); 11714 11715 } 11716 11717 if (thisApi.OneClickCheckOutApi != null) 11718 thisApi.OneClickCheckOutApi.Enable(); //re-enable after payment is successful 11719 11720 _resetExpiredRequestTokenAutoRetryCounter() 11721 11722 _firePaymentCompletedEvent(); 11723 11724 //call reinitialize cas 11725 if (result.Contact_Id > 0) 11726 reinitializeCas(result.Contact_Id, 11727 function () { 11728
11729 _logger.Log('post transaction cas reinitialization complete (' + result.Contact_Id + ')'); 11730 11731 }); 11732 11733 } 11734 else 11735 { 11736 11737 if (result.TokenExpired) { 11738 11739 let autoResubmitCnt = App.LocalStorage.GetIntegerValueOrZero('webFormExpiredTokenResubmitCount'); 11740 11741 _logger.Log('EXPIRED: ' + autoResubmitCnt); 11742 11743 if (autoResubmitCnt <= 0) { 11744 11745 autoResubmitCnt++; 11746 11747 App.LocalStorage.Save('webFormExpiredTokenResubmitCount', autoResubmitCnt); 11748 11749 _logger.Log('post expired token retry'); 11750 11751 //retry once 11752 if (WebFormContext.Form.CaptchaRequired) 11753 App.ProcessAjaxWithCaptchaFailover(processFunc, thisApi.ContactPhoneNumber, thisApi.ContactEmailAddress, 'Complete Transaction'); // captchaFailedPhoneNumber,captchaFailedEmail,captchaFailedButtonText); 11754 else 11755 processFunc(); 11756 11757 } 11758 else 11759 { 11760 _setErrorView('Error', 'Unable to process transaction. Please try again.', null, null, null); 11761 _resetExpiredRequestTokenAutoRetryCounter(); 11762 } 11763 11764 } 11765 else if (result.Duplicate) 11766 { 11767 11768 _setErrorView('This looks like a transaction you recently processed.', 'If this is not a duplicate, click the button below to process.', null, null, null); //we're not overriding content for now becuase it might cause confusion and undermine inspiration to try again (TBD) 11769 11770 let dupeGoButton = createClickInputButton('', '', '', 'Yes - Process Transaction', function () { _processTransaction(true); }); 11771 11772 document.getElementById('headerDynamicMessageContent').addLineBreak(); 11773 document.getElementById('headerDynamicMessageContent').appendChild(dupeGoButton); 11774 11775 _resetExpiredRequestTokenAutoRetryCounter(); 11776 11777 } 11778 else if (result.OneClickFailure) 11779 { 11780 _setErrorView('Error', 'Unable to process transaction. Please try again.', null, null, null); //we're not overriding content for now becuase it might cause confusion and undermine inspiration to try again (TBD) 11781 _resetExpiredRequestTokenAutoRetryCounter(); 11782 } 11783 else 11784 { 11785 _setErrorView('Error', result.ErrorMessage, result.ResponsePageTitle, null, null, null); //we're not overriding content for now becuase it might cause confusion and undermine inspiration to try again (TBD) 11786 _resetExpiredRequestTokenAutoRetryCounter(); 11787 } 11788 11789 //handle one click error or decline (in both cases temporarily disable to force alternate payment method) 11790 if (result.OneClickProfile_Id > 0 || result.OneClickFailure) 11791 thisApi.OneClickCheckOutApi.DisableTemporarily(); //special case to handle critical failure - disable for one hour and hide 11792 11793 } 11794 11795 }, 11796 function (error) { 11797 _setErrorView('Error', 'Unable to process transaction. Please try again.', null, null, null); //we're not overriding content for now becuase it might cause confusion and undermine inspiration to try again (TBD) 11798 }); 11799 11800 }; 11801 11802 if (WebFormContext.Form.CaptchaRequired) 11803 App.ProcessAjaxWithCaptchaFailover(processFunc, thisApi.ContactPhoneNumber, thisApi.ContactEmailAddress, 'Complete Transaction'); // captchaFailedPhoneNumber,captchaFailedEmail,captchaFailedButtonText); 11804 else 11805 processFunc(); 11806 11807 } 11808 11809 } 11810 11811 }; 11812 11813 const _handleSubmitClick = function () { thisApi.ProcessTransaction(); }; 11814 11815 //Events 11816 //****** 11817 11818 thisApi.RegisterFormInitializedCallBack = function (callBackFunction) { 11819 CallBackManager.add('formInitializedCallBack', callBackFunction, null); 11820 }; 11821 11822 const _fireFormInitializedEvent = function () { 11823 CallBackManager.fire('formInitializedCallBack', {}, null); 11824 }; 11825 11826 thisApi.RegisterFormReadyCallBack = function (callBackFunction) { 11827 CallBackManager.add('formReadyCallBack', callBackFunction, null); 11828 }; 11829 11830 const _fireFormReadyEvent = function () { 11831 CallBackManager.fire('formReadyCallBack', {}, null); 11832 }; 11833 11834 let _isThemeInitialized = false; 11835 11836 thisApi.RegisterThemeInitializedCallBack = function (callBackFunction) { 11837 11838 CallBackManager.add('form_' + thisApi.Form_Id + '_themeInitializedCallBack', callBackFunction, null); 11839 11840 }; 11841 11842 //special event to be fired in by theme (dependency inejction) 11843 thisApi.FireThemeInitializedEvent = function () { 11844 11845 _isThemeInitialized = true; 11846 11847 let cbParam = {}; 11848 cbParam.ThemeName = !isBlank(themeName) ? themeName : ''; 11849 cbParam.ThemeVersion = !isBlank(themeVersion) ? themeVersion : 0; 11850 11851 CallBackManager.fire('form_' + thisApi.Form_Id + '_themeInitializedCallBack', cbParam, null); 11852 11853 }; 11854 11855 thisApi.RegisterFormValidatedCallBack = function (callBackFunction) { 11856 CallBackManager.add('form_' + thisApi.Form_Id + '_validatedCallBack', callBackFunction, null); 11857 }; 11858 11859 const _fireFormValidatedEvent = function () { 11860 CallBackManager.fire('form_' + thisApi.Form_Id + '_validatedCallBack', {}, null); 11861 }; 11862 11863 thisApi.RegisterPaymentAmountChangedCallBack = function (callBackFunction) { 11864 CallBackManager.add('form_' + thisApi.Form_Id + '_paymentAmountChangedCallBack', callBackFunction, null); 11865 }; 11866 11867 const _firePaymentAmountChangedEvent = function () { 11868
11869 let cbParams = {}; 11870 cbParams.Amount = thisApi.GetTransactionAmount(); 11871 cbParams.GenericAmount = thisApi.GenericAmountInputsAmount; 11872 cbParams.MembershipAmount = _getSelectedMembershipRate(); 11873 cbParams.RegistrationAmount = thisApi.GetRegistrationAmount(); 11874 cbParams.ProductsAmount = thisApi.GetProductsAmount(); 11875 11876 CallBackManager.fire('form_' + thisApi.Form_Id + '_paymentAmountChangedCallBack', cbParams, null); 11877 11878 }; 11879 11880 thisApi.RegisterRecurringPaymentAmountChangedCallBack = function (callBackFunction) { 11881 CallBackManager.add('form_' + thisApi.Form_Id + '_recurringAmountChangedCallBack', callBackFunction, null); 11882 }; 11883 11884 const _fireRecurringAmountChangedEvent = function () { 11885 11886 let cbParam = {}; 11887 cbParam.Amount = thisApi.RecurringPaymentAmountField.Value; 11888 11889 CallBackManager.fire('form_' + thisApi.Form_Id + '_recurringAmountChangedCallBack', cbParam, null); 11890 11891 }; 11892 11893 thisApi.RegisterPaymentRecurringFrequencyChangedCallBack = function (callBackFunction) { 11894 CallBackManager.add('form_' + thisApi.Form_Id + '_paymentRecurringFrequencyChangedCallBack', callBackFunction, null); 11895 }; 11896 11897 const _firePaymentRecurringFrequencyChangedEvent = function () { 11898 11899 let cbParam = {}; 11900 cbParam.PaymentFrequency_Id = thisApi.RecurringPaymentFrequencyId; 11901 11902 CallBackManager.fire('form_' + thisApi.Form_Id + '_paymentRecurringFrequencyChangedCallBack', cbParam, null); 11903 11904 }; 11905 11906 thisApi.RegisterPaymentCurrencyChangedCallBack = function (callBackFunction) { 11907 CallBackManager.add('form_' + thisApi.Form_Id + '_paymentCurrencyChangedCallBack', callBackFunction, null); 11908 }; 11909 11910 const _firePaymentCurrencyChangedEvent = function () { 11911 11912 let cbParam = {}; 11913 cbParam.PaymentCurrency_Id = thisApi.PaymentCurrencyField.Value; 11914 11915 CallBackManager.fire('form_' + thisApi.Form_Id + '_paymentCurrencyChangedCallBack', cbParam, null); 11916 11917 }; 11918 11919 thisApi.RegisterPaymentMethodChangedCallBack = function (callBackFunction) { 11920 CallBackManager.add('form_' + thisApi.Form_Id + '_paymentMethodChangedCallBack', callBackFunction, null); 11921 }; 11922 11923 const _firePaymentMethodChangedEvent = function () { 11924 11925 let cbParam = {}; 11926 cbParam.PaymentMethod_Id = thisApi.PaymentMethodId; 11927 11928 CallBackManager.fire('form_' + thisApi.Form_Id + '_paymentMethodChangedCallBack', cbParam, null); 11929 11930 }; 11931 11932 thisApi.RegisterPaymentCompletedCallBack = function (callBackFunction) { 11933 CallBackManager.add('form_' + thisApi.Form_Id + '_paymentCompletedCallBack', callBackFunction, null); 11934 }; 11935 11936 const _firePaymentCompletedEvent = function () { 11937 11938 let cbParam = {}; 11939 cbParam.Transaction_Id = thisApi.Transaction_Id; 11940 cbParam.PaymentAmount = thisApi.ProcessedPaymentAmount; 11941 11942 CallBackManager.fire('form_' + thisApi.Form_Id + '_paymentCompletedCallBack', cbParam, null); 11943 11944 }; 11945 11946 thisApi.RegisterPaymentMethodSectionShownCallBack = function (callBackFunction) { 11947 CallBackManager.add('form_' + thisApi.Form_Id + '_paymentMethodSectionShownCallBack', callBackFunction, null); 11948 }; 11949 11950 const _firePaymentMethodSectionShownEvent = function () { 11951 11952 console.log('Fire Payment Method Shown Section Event'); 11953 11954 let cbParam = {}; 11955 cbParam.Section = thisApi.PaymentSection 11956 11957 CallBackManager.fire('form_' + thisApi.Form_Id + '_paymentMethodSectionShownCallBack', cbParam, null); 11958 11959 }; 11960 11961 thisApi.RegisterAskStringAmountsChangedCallBack = function (callBackFunction) { 11962 CallBackManager.add('form_' + thisApi.Form_Id + '_askStringAmountsChangedCallBack', callBackFunction, null); 11963 }; 11964 11965 const _fireAskStringAmountsChangedEvent = function () { 11966 CallBackManager.fire('form_' + thisApi.Form_Id + '_askStringAmountsChangedCallBack', {}, null); 11967 }; 11968 11969 //Startup Helpers 11970 //*************** 11971 11972 const _logCriticalCompatibilityIssue = function (message) { 11973
11974 _logger.Log('**********************************************'); 11975 _logger.Log('CRITICAL CONFIGURATION ISSUE DETECTED'); 11976 _logger.Log('configuration incompatible with web forms 2.0'); 11977 _logger.Log('see details below and fix form settings'); 11978 _logger.Log(message); 11979 _logger.Log('**********************************************'); 11980 11981 }; 11982 11983 const _criticalConfigurationMessages = []; 11984 11985 const _checkForCriticalConfigurationIssues = function () { 11986 11987 let retVal = false; 11988 11989 //we're allowing this 11990 //let mappedAmountFields = 0; 11991 //for (let x = 0; x < _allFieldApis.length; x++) 11992 // if (_allFieldApis[x].Configuration.DataMapping.TransactionAmount) 11993 // mappedAmountFields++; 11994 // 11995 //if (mappedAmountFields > 1) { 11996 // retVal = true; 11997 // _criticalConfigurationMessages.push('more than one form field mapped to amount (only one field can be mapped to amount and ' + mappedAmountFields + ' were found)'); 11998 // _logCriticalCompatibilityIssue('more than one form field mapped to amount (only one field can be mapped to amount and ' + mappedAmountFields + ' were found)'); 11999 //} 12000 12001 let mappedRecurringAmountFields = 0; 12002 for (let x = 0; x < _allFieldApis.length; x++) 12003 if (_allFieldApis[x].Configuration.DataMapping.RecurringAmount) 12004 mappedRecurringAmountFields++; 12005 12006 if (mappedRecurringAmountFields > 1) { 12007 retVal = true; 12008 _criticalConfigurationMessages.push('more than one form field mapped to recurring amount (only one field can be mapped to recurring amount and ' + mappedRecurringAmountFields + ' were found)'); 12009 _logCriticalCompatibilityIssue('more than one form field mapped to recurring amount (only one field can be mapped to recurring amount and ' + mappedRecurringAmountFields + ' were found)'); 12010 } 12011 12012 let mappedRecurringFrequencyFields = 0; 12013 for (let x = 0; x < _allFieldApis.length; x++) 12014 if (_allFieldApis[x].Configuration.DataMapping.RecurringFrequency) 12015 mappedRecurringFrequencyFields++; 12016 12017 if (mappedRecurringFrequencyFields > 1) { 12018 retVal = true; 12019 _criticalConfigurationMessages.push('more than one form field mapped to recurring frequency (only one field can be mapped to recurring frequency and ' + mappedRecurringFrequencyFields + ' were found)'); 12020 _logCriticalCompatibilityIssue('more than one form field mapped to recurring frequency (only one field can be mapped to recurring frequency and ' + mappedRecurringFrequencyFields + ' were found)'); 12021 } 12022 12023 let mappedCurrencyFields = 0; 12024 for (let x = 0; x < _allFieldApis.length; x++) 12025 if (_allFieldApis[x].Configuration.Type.Id == FORM_FIELD_TYPES.CURRENCY_SYSTEM) 12026 mappedCurrencyFields++; 12027 12028 if (mappedCurrencyFields > 1) { 12029 retVal = true; 12030 _criticalConfigurationMessages.push('more than one form field mapped to currency (only one field can be mapped to currency and ' + mappedCurrencyFields + ' were found)'); 12031 _logCriticalCompatibilityIssue('more than one form field mapped to currency (only one field can be mapped to currency and ' + mappedCurrencyFields + ' were found)'); 12032 } 12033 12034 if (thisApi.DafPayPaymentMethodAvailable && thisApi.FirstGenericAmountInputField == null) { 12035 retVal = true; 12036 _criticalConfigurationMessages.push('DAFPay configured but no generic amount input field on form (DAFPay requires at least one input field mapped to transaction amount)'); 12037 _logCriticalCompatibilityIssue('DAFPay configured but no generic amount input field on form (DAFPay requires at least one input field mapped to transaction amount)'); 12038 } 12039 12040 let mappedFreeGiftFields = 0; 12041 for (let x = 0; x < _allFieldApis.length; x++) 12042 if (_allFieldApis[x].Configuration.Type.Id == FORM_FIELD_TYPES.FREE_GIFT_SELECTOR) 12043 mappedFreeGiftFields++; 12044 12045 if (mappedFreeGiftFields > 1) { 12046 retVal = true; 12047 _criticalConfigurationMessages.push('more than one form field configured as free gift selector (only one free gift selector field allowed and ' + mappedFreeGiftFields + ' were found)'); 12048 _logCriticalCompatibilityIssue('more than one form field configured as free gift selector (only one free gift selector field allowed and ' + mappedFreeGiftFields + ' were found)'); 12049 } 12050 12051 return retVal; 12052 12053 }; 12054 12055 const _wireUpFieldValueChangeDetectionEvent = function (fieldConfig, callBackFunction) { 12056 12057 if (fieldConfig != null && callBackFunction != null && typeof callBackFunction == 'function') { 12058 12059 let fieldElem = document.getElementById(fieldConfig.DomId); 12060 if (fieldElem != null) { 12061 12062 switch (fieldConfig.Type.Id) { 12063 case FORM_FIELD_TYPES.RADIO_BUTTON: 12064 case FORM_FIELD_TYPES.CHECK_BOX: 12065 fieldElem.addEventListener('click', function () { callBackFunction(this); }); 12066 break; 12067 case FORM_FIELD_TYPES.TEXT: 12068 fieldElem.addEventListener('input', function () { callBackFunction(this); }); 12069 break; 12070 case FORM_FIELD_TYPES.DROP_DOWN: 12071 fieldElem.addEventListener('change', function () { callBackFunction(this); }); 12072 break; 12073 default: 12074 12075 switch (fieldConfig.SystemFieldRenderType_Id) { 12076 case FORM_SYSTEM_FIELD_RENDER_TYPES.DropDown: 12077 fieldElem.addEventListener('change', function () { callBackFunction(this); }); 12078 break; 12079 case FORM_SYSTEM_FIELD_RENDER_TYPES.RadioButton: 12080 case FORM_SYSTEM_FIELD_RENDER_TYPES.RadioButtonImages: 12081 case FORM_SYSTEM_FIELD_RENDER_TYPES.CheckBox: 12082 fieldElem.addEventListener('click', function () { callBackFunction(this); }); 12083 break; 12084 case FORM_SYSTEM_FIELD_RENDER_TYPES.TextBox: 12085 fieldElem.addEventListener('input', function () { callBackFunction(this); }); 12086 break; 12087 default: 12088 12089 let fldTp = tryGetInputFieldType(fieldElem); 12090 switch (fldTp) { 12091 case FormInputTypes.TextBox: 12092 case FormInputTypes.Password: 12093 case FormInputTypes.TextArea: 12094 fieldElem.addEventListener('input', function () { callBackFunction(this); }); 12095 break; 12096 case FormInputTypes.RadioButton: 12097 case FormInputTypes.CheckBox: 12098 case FormInputTypes.RadioButtonList: 12099 case FormInputTypes.CheckBoxList: 12100 fieldElem.addEventListener('click', function () { callBackFunction(this); }); 12101 break; 12102 case FormInputTypes.DropDownList: 12103 fieldElem.addEventListener('change', function () { callBackFunction(this); }); 12104 break; 12105 default: 12106 fieldElem.addEventListener('change', function () { callBackFunction(this); }); 12107 break; 12108 } 12109 12110 break; 12111 } 12112 12113 break; 12114 } 12115 12116 } 12117 12118 } 12119 12120 }; 12121 12122 const _initializeOneClickCheckout = function (contact_id, paymentProfile_id, accountNumberLastFourWithDescription) { 12123 12124 let oneClickCompatPayMethAvail = _oneClickCompatiblePaymentMethodsAvailable(); 12125 if (oneClickCompatPayMethAvail) 12126 { 12127 12128 if (WebFormContext.Form.EnableOneClickCheckout) { 12129
12130 _logger.Log('init one-click check out for contact ' + contact_id + ' with profile ' + paymentProfile_id + ' (' + accountNumberLastFourWithDescription + ')'); 12131 12132 let config = {}; 12133 config.AutoInitialize = false; 12134 config.ParentElementDomId = thisApi.PaymentMethodField.SectionDomElement.id; 12135 config.AddAfterElementDomId = thisApi.PaymentMethodField.RowDomElement.id; 12136 config.Contact_Id = contact_id; 12137 config.PaymentProfile_Id = paymentProfile_id; 12138 config.PaymentProfileDescription = accountNumberLastFourWithDescription; 12139 config.FormRowCssClass = 'ce-formRows'; 12140 12141 thisApi.OneClickCheckOutApi = getOneClickCheckOutApi(config); 12142 12143 thisApi.OneClickCheckOutApi.RegisterHideAllOptionsCallBack(function () { 12144 12145 _tryHidePaymentMethodField(); 12146 12147 _hideAllPaymentFields(); 12148 12149 }); 12150 12151 thisApi.OneClickCheckOutApi.RegisterDefaultStateShownCallBack(function () { thisApi.HideSubmitButton(); }); 12152 12153 thisApi.OneClickCheckOutApi.RegisterUseAccountSelectedCallBack(function () { thisApi.ShowSubmitButton(); }); 12154 12155 thisApi.OneClickCheckOutApi.RegisterDontUseCallBack(function () { 12156 12157 _tryShowPaymentMethodField(); 12158 12159 if (thisApi.PaymentMethodField.Value <= 0) 12160 thisApi.PaymentMethodField.SelectFirstOption(); //set to first option 12161 else 12162 _handlePaymentMethodChanged(); 12163 12164 }); 12165 12166 thisApi.OneClickCheckOutApi.RegisterForgetInfoCallBack(function () { 12167 12168 _tryShowPaymentMethodField(); 12169 12170 if (thisApi.PaymentMethodField.Value <= 0) 12171 thisApi.PaymentMethodField.SelectFirstOption(); //set to first option 12172 else 12173 _handlePaymentMethodChanged(); 12174 12175 }); 12176 12177 thisApi.OneClickCheckOutApi.RegisterDisabledCallBack(function () { 12178 12179 _tryShowPaymentMethodField(); 12180 12181 if (thisApi.PaymentMethodField.Value <= 0) 12182 thisApi.PaymentMethodField.SelectFirstOption(); //set to first option 12183 else 12184 _handlePaymentMethodChanged(); 12185 12186 }); 12187 12188 thisApi.OneClickCheckOutApi.Initialize(); 12189 12190 } 12191 else 12192 _logger.Log('one click checkout disabled'); 12193 12194 } 12195 else 12196 _logger.Log('one click checkout disabled (no compatible payment method)'); 12197 12198 }; 12199 12200 const _refreshPaymentAmountSectionForFormUpdates = function () { 12201
12202 //_logger.Log('refresh payment amount sections for updates (' + _productizedPaymentAmountSectionInitialized + ')'); 12203 12204 //todo: add any native logic here 12205 12206 //only for configured amount sections logic 12207 if (_productizedPaymentAmountSectionInitialized) { 12208 12209 let freq_id = thisApi.RecurringPaymentFrequencyId; 12210 12211 //handle out of the box settings (look for ask strings) 12212 //hide all ask strings 12213 _hideAllAmountAskStrings(); 12214 12215 //show based on frequency 12216 let isAskStringShown = false; 12217 let asStrs = _getAmountAskStringBlocks(); 12218 for (let x = 0; x < asStrs.length; x++) 12219 if (asStrs[x].tryGetIntegerAttribute('data-payment-frequency-id') == freq_id) { 12220 asStrs[x].show(); 12221 isAskStringShown = true; 12222 break; 12223 } 12224 12225 let askStringCont = document.getElementById('amountAskStringsContainer'); 12226 if (askStringCont != null) { 12227 12228 askStringCont.vanish(); 12229 if (isAskStringShown) 12230 askStringCont.show(); 12231 12232 } 12233 12234 //get textbox amount 12235 let amt = thisApi.FirstGenericAmountInputAmount; 12236 12237 //highlight radio if it matches text box amount 12238 let askInputs = _getShownAskBlockInputs(); 12239 let anyAmountMatches = false; 12240 for (let x = 0; x < askInputs.length; x++) 12241 if (askInputs[x].value.toString() == amt.toString()) { 12242 askInputs[x].checked = true; 12243 anyAmountMatches = true; 12244 } 12245 12246 if (!anyAmountMatches) { 12247 12248 let selInput = _getShownAskBlockSelectedInput(); 12249 if (selInput != null) 12250 selInput.checked = false; 12251 12252 } 12253 12254 } 12255 12256 }; 12257 12258 const _getShownAskStringBlock = function () { 12259 12260 let retVal = null; 12261 12262 if (_productizedPaymentAmountSectionInitialized) { 12263 12264 let freq_id = thisApi.RecurringPaymentFrequencyId; 12265 12266 let asStrs = _getAmountAskStringBlocks(); 12267 for (let x = 0; x < asStrs.length; x++) 12268 if (asStrs[x].tryGetIntegerAttribute('data-payment-frequency-id') == freq_id) { 12269 retVal = asStrs[x].querySelector('.ce-radioButtonLists'); 12270 break; 12271 } 12272 12273 } 12274 12275 return retVal; 12276 12277 }; 12278 12279 const _getShownAskBlockInputs = function () { 12280 12281 let retVal = []; 12282 12283 let visBlock = _getShownAskStringBlock(); 12284 if (visBlock != null) 12285 retVal = visBlock.querySelectorAll('input[type="radio"]'); 12286 12287 return retVal; 12288 12289 }; 12290 12291 const _getShownAskBlockSelectedInput = function () { 12292 12293 let retVal = null; 12294 12295 let visBlock = _getShownAskStringBlock(); 12296 if (visBlock != null) 12297 retVal = visBlock.querySelector('input[type="radio"]:checked'); 12298 12299 return retVal; 12300 12301 }; 12302 12303 const _simulateVisibleAskBlockClick = function () { 12304 12305 let visibleAskStringElem = _getShownAskStringBlock(); 12306 if (visibleAskStringElem != null) 12307 _handleAskStringClick(visibleAskStringElem.id); 12308 12309 }; 12310 12311 const _hideAllAmountAskStrings = function () { 12312 12313 let asStrs = _getAmountAskStringBlocks(); 12314 for (let x = 0; x < asStrs.length; x++) 12315 asStrs[x].vanish(); 12316 12317 }; 12318 12319 const _getAmountAskStringBlocks = function () { 12320 12321 let retVal = []; 12322 12323 if (thisApi.FirstGenericAmountInputField != null) { 12324 12325 let paySecCfg = thisApi.FirstGenericAmountInputField.SectionDomElement; 12326 if (paySecCfg != null) 12327 retVal = paySecCfg.querySelectorAll('.ce-amountAskStringButtonContainers'); 12328 12329 } 12330 12331 return retVal; 12332 12333 }; 12334 12335 const _getAmountAskStringInputs = function () { 12336 12337 let retVal = []; 12338 12339 if (thisApi.FirstGenericAmountInputField != null) { 12340 12341 let paySecCfg = thisApi.FirstGenericAmountInputField.SectionDomElement; 12342 if (paySecCfg != null) 12343 retVal = paySecCfg.querySelectorAll('.ce-amountAskStringButtonContainers input[type="radio"]'); 12344 12345 } 12346 12347 return retVal; 12348 12349 }; 12350 12351 const _addAskStringBlock = function (askStringButtonContainer, domId, freq_id, configOptions) { 12352 12353 if (configOptions != null && configOptions.length > 0) { 12354 12355 let askBlockContainer = createDomElement('div', domId + '_container', 'ce-amountAskStringButtonContainers'); 12356 askBlockContainer.setAttribute('data-payment-frequency-id', freq_id); 12357 12358 let curSymb = _getPaymentCurrencySymbol(); 12359 12360 let opts = []; 12361 for (let x = 0; x < configOptions.length; x++) 12362 opts.push(createMultiSelectInputOptionConfigJson(configOptions[x].Value, formatCurrency(configOptions[x].Value, false, curSymb), c
12362onfigOptions[x].IsDefault)); 12363 12364 askBlockContainer.addRadioButtonList(domId, function () { _handleAskStringClick(domId); }, opts); 12365 12366 askStringButtonContainer.appendChild(askBlockContainer); 12367 12368 //Need to add currency-symbol attribute 12369 let rbList = askStringButtonContainer.querySelector('#' + domId); 12370 12371 if (rbList != null) { 12372 let rbLabels = rbList.querySelectorAll('label'); 12373 12374 for (let i = 0; i < rbLabels.length; i++) { 12375 rbLabels[i].setAttribute('data-currency-symbol', curSymb) 12376 } 12377 } 12378 12379 } 12380 12381 }; 12382 12383 const _handleAskStringClick = function (domId) { 12384 12385 //_logger.Log('ask string domId ' + domId); 12386 12387 let askStringElem = document.getElementById(domId); 12388 12389 //_logger.Log('ask string ' + askStringElem.tagName + ' clicked'); 12390 //_logger.Log('ask string ' + askStringElem.id + ' clicked'); 12391 12392 let selectedAmtValue = getMultiSelectInputSelectedDecimalValue(askStringElem); 12393 12394 _logger.Log('ask string click set payment amount to ' + selectedAmtValue); 12395 12396 thisApi.FirstGenericAmountInputField.SetValue(selectedAmtValue); 12397 12398 }; 12399 12400 const _showCoverFeesWidget = function () { 12401 12402 if (document.getElementById('coverFeesDisplayRow') != null) document.getElementById('coverFeesDisplayRow').show(); 12403 12404 }; 12405 12406 const _hideCoverFeesWidget = function () { 12407 12408 _logger.Log('hide cover fees widget'); 12409 12410 if (document.getElementById('coverFeesDisplayRow') != null) document.getElementById('coverFeesDisplayRow').vanish(); 12411 _hideCoverFeesMessageRows(); //only hide when hiding whole widget 12412 12413 }; 12414 12415 const _refreshCoverFeesWidget = function () { 12416 12417 if (document.getElementById('coverFeesDisplayAmount') != null) { 12418 12419 let amt = thisApi.GetDiscountedAmount(); 12420 if (amt > 0) { 12421 12422 let feesFormatted = _getFeesFormatted(); 12423 12424 document.getElementById('coverFeesDisplayAmount').innerText = '+ ' + feesFormatted + ' processing fee'; 12425 12426 let possibleFees = _calculateSelectedPaymentMethodfees(); 12427 if (possibleFees > 0) 12428 _showCoverFeesWidget(); 12429 else 12430 _hideCoverFeesWidget(); 12431 12432 } 12433 else 12434 _hideCoverFeesWidget(); 12435 12436 } 12437 12438 }; 12439 12440 const _displayCoverFeesMessage = function () { 12441 12442 if (document.getElementById('coverFeesMessageRow') != null) document.getElementById('coverFeesMessageRow').show(); 12443 if (document.getElementById('coverFeesCheckBoxRow') != null) document.getElementById('coverFeesCheckBoxRow').show(); 12444 12445 if (document.getElementById('coverFeesDisplayEditLink') != null) document.getElementById('coverFeesDisplayEditLink').innerText = 'done'; 12446 12447 _updateFormAfterAmountImpactingChange(); //update cost amount 12448 12449 }; 12450 12451 const _hideCoverFeesMessageRows = function () { 12452 12453 if (document.getElementById('coverFeesMessageRow') != null) document.getElementById('coverFeesMessageRow').vanish(); 12454 if (document.getElementById('coverFeesCheckBoxRow') != null) document.getElementById('coverFeesCheckBoxRow').vanish(); 12455 12456 }; 12457 12458 const _hideCoverFeesMessage = function () { 12459 12460 _hideCoverFeesMessageRows(); 12461 12462 if (document.getElementById('coverFeesDisplayEditLink') != null) document.getElementById('coverFeesDisplayEditLink').innerText = 'edit'; 12463 12464 _updateFormAfterAmountImpactingChange(); //update cost amount 12465 12466 }; 12467 12468 const _handleCoverProcessingCostsEditClick = function () { 12469 12470 if (document.getElementById('coverFeesDisplayEditLink') != null) { 12471 12472 if (document.getElementById('coverFeesDisplayEditLink').innerText == 'edit') 12473 _displayCoverFeesMessage(); 12474 else 12475 _hideCoverFeesMessage(); 12476 12477 } 12478 12479 }; 12480 12481 const _handleToggleCoverProcessingCost = function () { 12482
12483 _logger.Log('cover processing fees toggle clicked'); 12484 12485 _hideCoverFeesMessage(); 12486 12487 }; 12488 12489 let _coverFeesInitialized = false; 12490 12491 const _addCoverProcessingCostBlock = function (sectionElem) { 12492 12493 if (!_coverFeesInitialized && sectionElem != null) { 12494 12495 //check for config 12496 if (!WebFormContext.Form.EnableDonorPayProcessingFee) { 12497 _logger.Log('cover processing fees disabled on form (abort initialization)'); 12498 return; 12499 } 12500 12501 _coverFeesInitialized = true; 12502 12503 //add top row with toggle 12504 let coverFeesTopRowAmtToggle = _createFormRow('coverFeesDisplayRow', 'ce-coverFeesDisplayRows'); 12505 12506 coverFeesTopRowAmtToggle.addText('+ $0.00 processing fee', 'coverFeesDisplayAmount'); 12507 12508 coverFeesTopRowAmtToggle.addHtmlClickLink('edit', function () { _handleCoverProcessingCostsEditClick(); }, 'coverFeesDisplayEditLink'); 12509 12510 sectionElem.appendChild(coverFeesTopRowAmtToggle); 12511 12512 //next add middle content row 12513 _addRowWithText(sectionElem, 'coverFeesMessageRow', 'ce-coverFeesMessageRows', 'Payment processors take a cut of each transaction. You have the option to cover these fees so 100% of your gift can go to the cause you care about.', 'coverFeesMessage'); 12514 12515 //bottom row added first 12516 _addRowWithSingleCheckBoxOption(sectionElem, 'coverFeesCheckBoxRow', 'ce-coverFeesCheckBoxes', 'coverFeesCheckBox', true, 'I want to cover costs', true, function () { _handleToggleCoverProcessingCost(); }); 12517 12518 _handleToggleCoverProcessingCost(); 12519 12520 } 12521 12522 }; 12523 12524 const _getAmountSectionDomElement = function () { 12525 12526 let retVal = null; 12527 12528 if(thisApi.FirstGenericAmountInputField != null) 12529 retVal = thisApi.FirstGenericAmountInputField.SectionDomElement; 12530 12531 if (retVal == null && thisApi.RecurringPaymentAmountField != null) 12532 retVal = thisApi.RecurringPaymentAmountField.SectionDomElement; 12533 12534 return retVal; 12535 12536 }; 12537 12538 const _tryHideInKindTypeDetailFields = function () { 12539 12540 let ikFlds = document.querySelectorAll('.ce-inKindDetailRows'); 12541 for (let x = 0; x < ikFlds.length; x++) 12542 ikFlds[x].vanish(); 12543 12544 }; 12545 12546 const _addInKinDetailTextInputRow = function (sectionElement, domId, classes, inputDomId, label, inputClasses, placeHolder, class_id) { 12547 12548 _addRowWithSingleTextInput(sectionElement, domId, classes, inputDomId, label, inputClasses, placeHolder); 12549 document.getElementById(domId).setAttribute('data-in-kind-class-id', class_id); 12550 12551 }; 12552 12553 const _tryShowInKindTypeDetailsByClass = function (class_id) { 12554 12555 _tryHideInKindTypeDetailFields(); 12556 12557 let ikFlds = document.querySelectorAll('.ce-inKindDetailRows'); 12558 for (let x = 0; x < ikFlds.length; x++) 12559 if(ikFlds[x].tryGetIntegerAttribute('data-in-kind-class-id') == class_id) 12560 ikFlds[x].show(); 12561 12562 }; 12563 12564 const _refreshInKindFieldsByTypeSelected = function () { 12565 12566 let ikClassId = thisApi.InKindTypeClassId; 12567 12568 _tryShowInKindTypeDetailsByClass(ikClassId); 12569 12570 }; 12571 12572 const _handleInKindTypeChanged = function () { 12573 12574 _refreshInKindFieldsByTypeSelected(); 12575 12576 //add event call back 12577 12578 }; 12579 12580 let _productizedPaymentAmountSectionInitialized = false; 12581 12582 const _initializePaymentAmountSection = function () { 12583 12584 //native field event bindings (moved to generic amount wire ups for multiple) 12585 //if (thisApi.Payment_XXX_AmountField != null) 12586 // _wireUpFieldValueChangeDetectionEvent(thisApi.Payment_XXX_AmountField.Configuration, _handle_XXX_PaymentAmountChanged); 12587 12588 if (thisApi.RecurringPaymentFrequencyField != null) 12589 bindMultiSelectInputChangedEvent(thisApi.RecurringPaymentFrequencyField.DomElement, function () { _handlePaymentFrequencyChanged(); }); 12590 12591 if (thisApi.PaymentCurrencyField != null) 12592 thisApi.PaymentCurrencyField.DomElement.addEventListener('change', function () { _handleCurrencyChanged(); }); 12593 12594 //custom configuration below this point after native 12595 const paySecCfg = _getDonationAmountSectionConfig(); 12596 if (paySecCfg != null) { 12597
12598 _logger.Log('configure payment amount section (configuration present)'); 12599 12600 _productizedPaymentAmountSectionInitialized = true; 12601 12602 let hasAskStrings = ((paySecCfg.OneTimeAmountOptions != null && paySecCfg.OneTimeAmountOptions.length > 0) 12603 || (paySecCfg.DailyAmountOptions != null && paySecCfg.DailyAmountOptions.length > 0) 12604 || (paySecCfg.WeeklyAmountOptions != null && paySecCfg.WeeklyAmountOptions.length > 0) 12605 || (paySecCfg.BiMonthlyAmountOptions != null && paySecCfg.BiMonthlyAmountOptions.length > 0) 12606 || (paySecCfg.MonthlyAmountOptions != null && paySecCfg.MonthlyAmountOptions.length > 0) 12607 || (paySecCfg.QuarterlyAmountOptions != null && paySecCfg.QuarterlyAmountOptions.length > 0) 12608 || (paySecCfg.BiAnnualAmountOptions != null && paySecCfg.BiAnnualAmountOptions.length > 0) 12609 || (paySecCfg.AnnualAmountOptions != null && paySecCfg.AnnualAmountOptions.length > 0)); 12610 if (hasAskStrings) { 12611 12612 if (thisApi.FirstGenericAmountInputField.RowDomElement != null) 12613 { 12614 12615 _logger.Log('configure ask string blocks'); 12616 12617 let askStringButtonContainer = _createFormRow('amountAskStringsContainer', 'ce-amountAskStringContainers'); 12618 12619 //consider moving this to field options instead of specialton - i.e., there are 0 to 8 radio button lists that have field options for the amounts - benefit: if section is converted to custom, user can show/hide radio sets based on separate logic 12620 _addAskStringBlock(askStringButtonContainer, 'oneTimeAskStrings', PaymentFrequencies.OneTime, paySecCfg.OneTimeAmountOptions); 12621 _addAskStringBlock(askStringButtonContainer, 'dailyAskStrings', PaymentFrequencies.Daily, paySecCfg.DailyAmountOptions); 12622 _addAskStringBlock(askStringButtonContainer, 'weeklyAskStrings', PaymentFrequencies.Weekly, paySecCfg.WeeklyAmountOptions); 12623 _addAskStringBlock(askStringButtonContainer, 'bimonthlyAskStrings', PaymentFrequencies.BiMonthly, paySecCfg.BiMonthlyAmountOptions); 12624 _addAskStringBlock(askStringButtonContainer, 'monthlyAskStrings', PaymentFrequencies.Monthly, paySecCfg.MonthlyAmountOptions); 12625 _addAskStringBlock(askStringButtonContainer, 'quarterlyAskStrings', PaymentFrequencies.Quarterly, paySecCfg.QuarterlyAmountOptions); 12626 _addAskStringBlock(askStringButtonContainer, 'biannualAskStrings', PaymentFrequencies.BiAnnually, paySecCfg.BiAnnualAmountOptions); 12627 _addAskStringBlock(askStringButtonContainer, 'annualAskStrings', PaymentFrequencies.Annually, paySecCfg.AnnualAmountOptions); 12628 12629 thisApi.FirstGenericAmountInputField.RowDomElement.insertBeforeElement(askStringButtonContainer); 12630 12631 } 12632 else 12633 _logger.Log('unable to configure ask string blocks (cannot find donation input field)'); 12634 12635 } 12636 12637 } 12638 else 12639 _logger.Log('unable to configure amount section type (cannot find amount section config)'); 12640 12641 const paySecElem = _getAmountSectionDomElement(); 12642 if (paySecElem != null) { 12643 12644 //add in-kind fields 12645 let inKindAvailOnForm = _inKindPaymentMethodAvailable(); 12646 if (inKindAvailOnForm && thisApi.InKindTypeField != null) { 12647 12648 _logger.Log('initialize inkind fields (payment method available)'); 12649 12650 //native field event bindings 12651 _wireUpFieldValueChangeDetectionEvent(thisApi.InKindTypeField.Configuration, _handleInKindTypeChanged); 12652 12653 _addInKinDetailTextInputRow(paySecElem, 'inKindVehicleYearRow', 'ce-inKindDetailRows', 'inKindVehicleYear', 'Year', 'ce-inKindDetails', 'Vehicle Year', IN_KIND_ASSET_CLASSES.AUTOMOBILES); 12654 _addInKinDetailTextInputRow(paySecElem, 'inKindVehicleMakeRow', 'ce-inKindDetailRows', 'inKindVehicleMake', 'Make/Model', 'ce-inKindDetails', 'Vehicle Make/Model', IN_KIND_ASSET_CLASSES.AUTOMOBILES); 12655 _addInKinDetailTextInputRow(paySecElem, 'inKindeVehicleMileageRow', 'ce-inKindDetailRows', 'inKindeVehicleMileage', 'Mileage', 'ce-inKindDetails', 'Vehicle Mileage', IN_KIND_ASSET_CLASSES.AUTOMOBILES); 12656 _addInKinDetailTextInputRow(paySecElem, 'inKindVinNumberRow', 'ce-inKindDetailRows', 'inKindVinNumber', 'VIN #', 'ce-inKindDetails', 'VIN Number', IN_KIND_ASSET_CLASSES.AUTOMOBILES); 12657 12658 _addInKinDetailTextInputRow(paySecElem, 'inKindStockTickerRow', 'ce-inKindDetailRows', 'inKindStockTicker', 'Stock Ticker', 'ce-inKindDetails', 'Ticker', IN_KIND_ASSET_CLASSES.STOCKS); 12659 _addInKinDetailTextInputRow(paySecElem, 'inKindStockCompanyRow', 'ce-inKindDetailRows', 'inKindStockCompany', 'Company', 'ce-inKindDetails', 'Company', IN_KIND_ASSET_CLASSES.STOCKS); 12660 12661 _addInKinDetailTextInputRow(paySecElem, 'inKindFoodWeightRow', 'ce-inKindDetailRows', 'inKindFoodWeight', 'Weight (lbs)', 'ce-inKindDetails', 'Weight (lbs)', IN_KIND_ASSET_CLASSES.FOOD); 12662 12663 _refreshInKindFieldsByTypeSelected(); 12664 12665 } 12666 12667 _addCoverProcessingCostBlock(paySecElem); 12668 12669 } 12670 else
12671 _logger.Log('unable to configure native amount section functionality (cannot find section dom element)'); 12672 12673 }; 12674 12675 const _reinitiailzeAskAmountBlock = function (askBlock, startAmount, startAmountMultiplier) { 12676 12677 if (askBlock != null) { 12678 12679 let curSymb = _getPaymentCurrencySymbol(); 12680 12681 let rbs = getRadioButtonOptionElements(askBlock); 12682 12683 let newApi = getEndUserSuggestedDonationAskAmountsApi(startAmount, rbs.length, startAmountMultiplier); 12684 let opts = newApi.GenerateAmountsFromSeed(); 12685 12686 for (let x = 0; x < opts.length; x++) { 12687 12688 rbs[x].value = opts[x]; 12689 12690 let lbl = getLabelForField(rbs[x], askBlock); 12691 lbl.innerHTML = formatCurrency(opts[x], false, curSymb); 12692 12693 lbl.setAttribute('data-currency-symbol', curSymb); 12694 12695 } 12696 12697 } 12698 12699 }; 12700 12701 const _reinitializeAskAmounts = function (startAmount, startAmountMultiplier) { 12702 12703 _logger.Log('reinitiailze ask string with start amount ' + startAmount); 12704 12705 let opts = _getAmountAskStringBlocks(); 12706 12707 if (opts.length > 0) { 12708 12709 let seedAmount = 50; 12710 12711 if (startAmount > 0) 12712 seedAmount = roundEndUserSuggestedDonationAmount(startAmount); 12713 12714 //Never start with more than $500 on donation form 12715 if (seedAmount > 500) 12716 seedAmount = 500; 12717 12718 _reinitiailzeAskAmountBlock(document.getElementById('oneTimeAskStrings'), seedAmount, startAmountMultiplier); 12719 12720 let dailySeed = roundEndUserSuggestedDonationAmount(seedAmount * 0.1); 12721 _reinitiailzeAskAmountBlock(document.getElementById('dailyAskStrings'), dailySeed, startAmountMultiplier); 12722 12723 let weeklySeed = roundEndUserSuggestedDonationAmount(seedAmount * 0.2); 12724 _reinitiailzeAskAmountBlock(document.getElementById('weeklyAskStrings'), weeklySeed, startAmountMultiplier); 12725 12726 let bimonthlySeed = roundEndUserSuggestedDonationAmount(seedAmount * 0.3); 12727 _reinitiailzeAskAmountBlock(document.getElementById('bimonthlyAskStrings'), bimonthlySeed, startAmountMultiplier); 12728 12729 let monthlySeed = roundEndUserSuggestedDonationAmount(seedAmount * 0.4); 12730 _reinitiailzeAskAmountBlock(document.getElementById('monthlyAskStrings'), monthlySeed, startAmountMultiplier); 12731 12732 let quarterlySeed = roundEndUserSuggestedDonationAmount(seedAmount * 0.6); 12733 _reinitiailzeAskAmountBlock(document.getElementById('quarterlyAskStrings'), quarterlySeed, startAmountMultiplier); 12734 12735 let biannualSeed = roundEndUserSuggestedDonationAmount(seedAmount * 0.75); 12736 _reinitiailzeAskAmountBlock(document.getElementById('biannualAskStrings'), bi
12736annualSeed, startAmountMultiplier); 12737 12738 let annualSeed = roundEndUserSuggestedDonationAmount(seedAmount * 0.9); 12739 _reinitiailzeAskAmountBlock(document.getElementById('annualAskStrings'), annualSeed, startAmountMultiplier); 12740 12741 if (!_isUrlAmountProvided) 12742 _simulateVisibleAskBlockClick(); 12743 else 12744 _logger.Log('skip startup simulate ask click because of amount url override'); 12745 12746 } 12747 12748 _fireAskStringAmountsChangedEvent(); 12749 12750 }; 12751 12752 const _handleContactTypeChanged = function () { 12753 12754 _tryHideField(thisApi.OrganizationNameField); 12755 12756 let cntTpId = thisApi.ContactTypeId; 12757 12758 _logger.Log('contact type changed to ' + cntTpId); 12759 12760 if (cntTpId > 0) { 12761 12762 switch (cntTpId) { 12763 case CONTACT_TYPES.Person: 12764 //no changd 12765 break; 12766 case CONTACT_TYPES.Organization: 12767 //_logger.Log('try show ' + thisApi.OrganizationNameFieldElement); 12768 _tryShowField(thisApi.OrganizationNameField); 12769 break; 12770 } 12771 12772 } 12773 12774 }; 12775 12776 let _contactInforSectionAddressApi = null; 12777 12778 const _initializeContactInfoSection = function () { 12779 12780 if (thisApi.ContactAddressStreet1Field != null) 12781 _contactInforSectionAddressApi = getFormAddressApi({ AutoInitialize: false, Required: false, FormOrContainerDomId: thisApi.FormDomId, Street1FieldDomId: thisApi.ContactAddressStreet1Field.DomElement.id, Street2FieldDomId: thisApi.ContactAddressStreet2Field.DomElement.id, CityFieldDomId: thisApi.ContactAddressCityField.DomElement.id, StateProvinceFieldDomId: thisApi.ContactAddressStateProvinceField.DomElement.id, PostalCodeFieldDomId: thisApi.ContactAddressPostalCodeField.DomElement.id, CountryFieldDomId: thisApi.ContactAddressCountryField.DomElement.id, ApplyStyles: false, AddBlankStateOption: true }); 12782 12783 //wire up contact type if present 12784 if (thisApi.ContactTypeField != null) 12785 bindMultiSelectInputChangedEvent(thisApi.ContactTypeField.DomElement, function () { _handleContactTypeChanged(); }); 12786 12787 _handleContactTypeChanged(); 12788 _contactInforSectionAddressApi.Initialize(); 12789 12790 }; 12791 12792 const _handleToggleTributeClicked = function () { 12793 12794 let tribSecConfig = _tryGetTributeInfoSectionConfiguration(); 12795 if (tribSecConfig != null) { 12796 12797 let tcb = document.getElementById('dedicateGiftCheckBox').getValue(); 12798 if (tcb) 12799 _tryShowSection(tribSecConfig); 12800 else 12801 _tryHideSection(tribSecConfig); 12802 12803 } 12804 12805 }; 12806 12807 const _tryHideAllTributeContactInfoFields = function () { 12808 12809 for (let x = 0; x < _allFieldApis.length; x++) 12810 if (_allFieldApis[x].IsTributeRecipientContactInformation || _allFieldApis[x].IsTributeCardConfiguration) 12811 _allFieldApis[x].Hide(); 12812 12813 //special field 12814 //tribute single line 12815 if (document.getElementById('tributeAddressSingleLineEntryRow') != null) 12816 document.getElementById('tributeAddressSingleLineEntryRow').vanish(); 12817 12818 //special field 12819 if (document.getElementById('tributeEmailMessageTypeToggleContainer') != null) 12820 document.getElementById('tributeEmailMessageTypeToggleContainer').vanish(); 12821 12822 }; 12823 12824 const _tryGetSelectedTributeRecipientType = function () { 12825 12826 let retVal = ''; 12827 12828 let trbRecipRbl = document.getElementById('tributeRecipientToggle'); 12829 if (trbRecipRbl != null) 12830 retVal = trbRecipRbl.getValue(); 12831 12832 return retVal; 12833 12834 }; 12835 12836 const _handleTributeRecipientTypeClick = function () { 12837 12838 _tryHideAllTributeContactInfoFields(); 12839 12840 let selVal = _tryGetSelectedTributeRecipientType(); 12841 _logger.Log('tribute recipient type changed to: ' + selVal); 12842 switch (selVal) { 12843 case 'mail': 12844 12845 //special field 12846 //tribute single line 12847 if (document.getElementById('tributeAddressSingleLineEntryRow') != null) { 12848 document.getElementById('tributeAddressSingleLineEntryRow').show(); 12849 _hideAllTributeAddressFields(); 12850 } 12851 else { 12852 _tryShowField(thisApi.TributeRecipientAddressStreet1Field); 12853 _tryShowField(thisApi.TributeRecipientAddressStreet2Field); 12854 _tryShowField(thisApi.TributeRecipientAddressCityField); 12855 _tryShowField(thisApi.TributeRecipientAddressStateField); 12856 _tryShowField(thisApi.TributeRecipientAddressPostalCodeField); 12857 _tryShowField(thisApi.TributeRecipientAddressCountryField); 12858 } 12859 12860 _tryShowField(thisApi.PrintCardCountField); 12861 12862 _togglePrintCardsVisibilityBasedOnCountSelected(); 12863 12864 break; 12865 case 'email': 12866 12867 _tryShowField(thisApi.TributeRecipientEmailAddressField); 12868 12869 if (document.getElementById('tributeEmailMessageTypeToggleContainer') != null) 12870 document.getElementById('tributeEmailMessageTypeToggleContainer').show(); 12871 12872 _toggleTributeEcardVisibilityBasedOnConfiguration(); 12873 12874 break; 12875 case 'phone': 12876 _tryShowField(thisApi.TributeRecipientPhoneNumberField); 12877 break; 12878 } 12879 12880 }; 12881 12882 const _isTributeEcardOptionSelected = function () { 12883 12884 let messTp = ''; 12885 let messTpRbl = document.getElementById('tributeEmailMessageTypeToggle'); 12886 if (messTpRbl != null) 12887 messTp = messTpRbl.getValue(); 12888
12889 //_logger.Log('is e-card selected val: ' + messTp); 12890 12891 return (messTp == 'card'); 12892 12893 }; 12894 12895 const _toggleTributeEcardVisibilityBasedOnConfiguration = function () { 12896 12897 _tryHideField(thisApi.ECardField); 12898 12899 let selVal = _tryGetSelectedTributeRecipientType(); 12900 //_logger.Log('toggle tribute e-card visibility: ' + selVal); 12901 if (selVal == 'email' && _isTributeEcardOptionSelected()) 12902 _tryShowFieldIfHasOptions(thisApi.ECardField); 12903 12904 }; 12905 12906 const _handleTributeEmailMessageTypeClick = function () { _toggleTributeEcardVisibilityBasedOnConfiguration(); }; 12907 12908 const _togglePrintCardsVisibilityBasedOnCountSelected = function () { 12909 12910 _tryHideField(thisApi.PrintCardField); 12911 12912 let selVal = _tryGetSelectedTributeRecipientType(); 12913 if (selVal == 'mail' && thisApi.PrintCardCount > 0) 12914 _tryShowFieldIfHasOptions(thisApi.PrintCardField); 12915 12916 }; 12917 12918 const _handlePrintCardCountChanged = function () { _togglePrintCardsVisibilityBasedOnCountSelected(); }; 12919 12920 const _tryGetTributeInfoSectionConfiguration = function () { 12921 12922 let retVal = null; 12923 12924 if (thisApi.TributeTypeField != null || thisApi.TributeNameField != null) { 12925 12926 if (thisApi.TributeTypeField != null) 12927 retVal = thisApi.TributeTypeField.SectionConfiguration; 12928 else 12929 retVal = thisApi.TributeNameField.SectionConfiguration; 12930 12931 } 12932 12933 return retVal; 12934 12935 }; 12936 12937 let _tributeRecipientInforSectionAddressApi = null; 12938 12939 const _initializeTributeSection = function () { 12940 12941 if (thisApi.TributeRecipientAddressStreet1Field != null) 12942 _tributeRecipientInforSectionAddressApi = getFormAddressApi({ AutoInitialize: false, Required: false, FormOrContainerDomId: thisApi.FormDomId, Street1FieldDomId: thisApi.TributeRecipientAddressStreet1Field.DomElement.id, Street2FieldDomId: thisApi.TributeRecipientAddressStreet2Field.DomElement.id, CityFieldDomId: thisApi.TributeRecipientAddressCityField.DomElement.id, StateProvinceFieldDomId: thisApi.TributeRecipientAddressStateField.DomElement.id, PostalCodeFieldDomId: thisApi.TributeRecipientAddressPostalCodeField.DomElement.id, CountryFieldDomId: thisApi.TributeRecipientAddressCountryField.DomElement.id, ApplyStyles: false, AddBlankStateOption: true }); 12943 12944 //NOTE: tribute dependent on amount section - we're putting the checkbox there for now. 12945 let tribSecConfig = _tryGetTributeInfoSectionConfiguration(); 12946 if (tribSecConfig != null) { 12947 12948 _logger.Log('begin initialize tribute section (tribute type field present)'); 12949 12950 let paymentSectionElem = _getAmountSectionDomElement(); 12951 12952 if (paymentSectionElem != null) { 12953 12954 _addRowWithSingleCheckBoxOption(paymentSectionElem, 'dedicateGiftCheckBoxContainer', '', 'dedicateGiftCheckBox', true, 'Dedicate this gift', false, function () { _handleToggleTributeClicked(); }); 12955 12956 _handleToggleTributeClicked(); 12957 12958 } 12959 else 12960 _logger.Log('unable to initialize tribute section becuase there is no amount section'); 12961 12962 //toggle should be assumed 12963 let firstTributeContactInfField = _getFirstTributeRecipientContactInfoField(); 12964 if (firstTributeContactInfField != null) { 12965 12966 let enableEmail = true; 12967 let emailBtnLabel = 'Email'; 12968 let enablePhone = true; 12969 let phoneBtnLabel = 'Phone Call'; 12970 let enableLetter = true; 12971 let letterBtnLabel = 'Letter'; 12972 12973 let noAnnouncementBtnLbl = 'No Announcement'; 12974 12975 let tribOobCfg = thisApi.TributeSectionConfig; 12976 if (tribOobCfg != null) { 12977 12978 enableEmail = tribOobCfg.EnableEmailAcknowledgement; 12979 if (!isBlank(tribOobCfg.EmailAcknowledgementLabel)) 12980 emailBtnLabel = tribOobCfg.EmailAcknowledgementLabel; 12981 12982 enablePhone = false; // not implemented tribOobCfg.; 12983 12984 enableLetter = tribOobCfg.EnableMailAcknowledgement; 12985 if (!isBlank(tribOobCfg.MailAcknowledgementLabel)) 12986 letterBtnLabel = tribOobCfg.MailAcknowledgementLabel; 12987 12988 noAnnouncementBtnLbl = ''; //allow removal via blank label 12989 if (!isBlank(tribOobCfg.NoAcknowledgementLabel)) 12990 noAnnouncementBtnLbl = tribOobCfg.NoAcknowledgementLabel; 12991 12992 } 12993 12994 let optCnt = 0; 12995 let optsCsv = ''; 12996 let defOptVal = ''; 12997 12998 if (enableEmail && thisApi.TributeRecipientEmailAddressField != null) { 12999 13000 optsCsv = optsCsv.delimitedListAdd('email=' + emailBtnLabel); 13001 defOptVal = 'email'; 13002 optCnt++; 13003 13004 //wire up experience for e-card what type of message would you like: gift notification? e-card? 13005 if (thisApi.ECardField != null && thisApi.ECardField.HasOptions) { 13006 13007 let tributeEmailMessageTypeToggleRow = _createRowWithRadioButtonList('tributeEmailMessageTypeToggleContainer', 'ce-tributeRecipientEmailMessageTypeToggleContainers', 'tributeEmailMessageTypeToggle', 'What type of message would you like to send?', 'general=gift notification,card=e-card', 'general', function () { _handleTributeEmailMessageTypeClick(); }); 13008 13009 firstTributeContactInfField.SectionDomElement.insertAfterElement(tributeEmailMessageTypeToggleRow, thisApi.TributeRecipientEmailAddressField.RowDomElement); 13010 13011 } 13012 13013 } 13014 13015 if (enablePhone && thisApi.TributeRecipientPhoneNumberField != null) { 13016 optsCsv = optsCsv.delimitedListAdd('phone=' + phoneBtnLabel); 13017 defOptVal = 'phone'; 13018 optCnt++; 13019 } 13020 13021 if (enableLetter && thisApi.TributeRecipientAddressStreet1Field != null) { 13022 optsCsv = optsCsv.delimitedListAdd('mail=' + letterBtnLabel); 13023 defOptVal = 'mail'; 13024 optCnt++; 13025 } 13026 13027 //don't show if none 13028 if (optCnt > 0) { 13029 13030 if (!isBlank(noAnnouncementBtnLbl)) { 13031 optsCsv = optsCsv.delimitedListAdd('none=' + noAnnouncementBtnLbl); 13032 defOptVal = 'none'; 13033 optCnt++; 13034 } 13035 13036 let tributeRecipToggleRow = _createRowWithRadioButtonList('tributeRecipientToggleRow', 'ce-tributeRecipientToggleContainers', 'tributeRecipientToggle', '', optsCsv, defOptVal, function () { _handleTributeRecipientTypeClick(); }); 13037 13038 firstTributeContactInfField.RowDomElement.insertBeforeElement(tributeRecipToggleRow); 13039 13040 //hide of we have 1 13041 if (optCnt <= 1) 13042 document.getElementById('tributeRecipientToggleRow').vanish(); 13043 13044 let emailInput = getMultiSelectInputOptionElementByValue(document.getElementById('tributeRecipientToggle'), 'email'); 13045 if(emailInput != null) 13046 _addRadioButtonLabelIcon(emailInput, 'fa fa-paper-plane'); 13047 13048 let phoneInput = getMultiSelectInputOptionElementByValue(document.getElementById('tributeRecipientToggle'), 'phone'); 13049 if(phoneInput != null) 13050 _addRadioButtonLabelIcon(phoneInput, 'fa fa-phone'); 13051 13052 let mailInput = getMultiSelectInputOptionElementByValue(document.getElementById('tributeRecipientToggle'), 'mail'); 13053 if(mailInput != null) 13054 _addRadioButtonLabelIcon(mailInput, 'fa fa-envelope'); 13055 13056 //let noneInput = getMultiSelectInputOptionElementByValue(document.getElementById('tributeRecipientToggle'), 'none'); 13057 13058 } 13059 13060 if (thisApi.PrintCardCountField != null) 13061 _wireUpFieldValueChangeDetectionEvent(thisApi.PrintCardCountField.Configuration, _handlePrintCardCountChanged); 13062 13063 _togglePrintCardsVisibilityBasedOnCountSelected(); 13064 13065 _toggleTributeEcardVisibilityBasedOnConfiguration(); 13066 13067 _handleTributeRecipientTypeClick(); 13068 13069 _tributeRecipientInforSectionAddressApi.Initialize(); 13070 13071 } 13072 else
13073 _logger.Log('unable to initialize tribute buttons becuase there is no recipient contact info fields'); 13074 13075 } 13076 13077 }; 13078 13079 const _serializeCurrentFormFillContactInfo = function () { 13080 13081 let retVal = {}; 13082 13083 retVal.ContactFirstName = thisApi.ContactFirstName; 13084 retVal.ContactMiddleName = thisApi.ContactMiddleName; 13085 retVal.ContactLastName = thisApi.ContactLastName; 13086 13087 retVal.ContactEmailAddress = thisApi.ContactEmailAddress; 13088 13089 retVal.ContactPhoneNumber = thisApi.ContactPhoneNumber; 13090 13091 retVal.ContactSingleLineAddress = _tryGetContactAddressSingleLineEntryValue(); 13092 //if (document.getElementById('contactAddress_XXX_SingleLineInput') != null) 13093 // retVal.ContactSingleLineAddress = document.getElementById('contactAddress_XXX_SingleLineInput').getValue(); 13094 13095 retVal.ContactAddressStreet1 = thisApi.ContactAddressStreet1; 13096 retVal.ContactAddressStreet2 = thisApi.ContactAddressStreet2; 13097 retVal.ContactAddressCity = thisApi.ContactAddressCity; 13098 retVal.ContactAddressStateProvinceId = thisApi.ContactAddressStateProvinceId; 13099 retVal.ContactAddressPostalCode = thisApi.ContactAddressPostalCode; 13100 retVal.ContactAddressCountryId = thisApi.ContactAddressCountryId; 13101 13102 return retVal; 13103 13104 }; 13105 13106 const _getLastFormFillKey = function () { return 'ceLastFormFill_' + WebFormContext.Instance_Id; }; 13107 13108 const _tryGetLastFormFillContactInfo = function () { 13109 13110 let retVal = null; 13111 13112 let strVal = tryGetLocalStorageItem(_getLastFormFillKey()); 13113 13114 _logger.Log('try get last form fill: ' + strVal); 13115 13116 if (!isBlank(strVal) && strVal != '{}') { 13117 13118 try { 13119 retVal = JSON.parse(strVal); 13120 } 13121 catch { 13122 _logger.Log('failed to parse last form fill'); 13123 } 13124 13125 } 13126 13127 13128 return retVal; 13129 13130 }; 13131 13132 const _saveLastFormFillContactInfo = function () { 13133 13134 let cnt = _serializeCurrentFormFillContactInfo(); 13135 13136 let strVal = JSON.stringify(cnt); 13137 13138 _logger.Log('try save last form fill: ' + strVal); 13139 13140 trySetLocalStorageItem(_getLastFormFillKey(), strVal); 13141 13142 }; 13143 13144 const _tryPopulateFormFromLastFormFill = function () { 13145 13146 let cnt = _tryGetLastFormFillContactInfo(); 13147 if (cnt != null) { 13148 13149 _logger.Log('populate form from last user fill'); 13150 13151 //only populate if values are blank 13152 let isNameEmpty = (isBlank(thisApi.ContactFirstName) && isBlank(thisApi.ContactMiddleName) && isBlank(thisApi.ContactLastName)); 13153 if (isNameEmpty) { 13154 13155 if(typeof cnt.ContactFirstName != 'undefined' && !isBlank(cnt.ContactFirstName)) 13156 _trySetFieldValue(thisApi.ContactFirstNameField, cnt.ContactFirstName); 13157 13158 if(typeof cnt.ContactMiddleName != 'undefined' && !isBlank(cnt.ContactMiddleName)) 13159 _trySetFieldValue(thisApi.ContactMiddleNameField, cnt.ContactMiddleName); 13160 13161 if(typeof cnt.ContactLastName != 'undefined' && !isBlank(cnt.ContactLastName)) 13162 _trySetFieldValue(thisApi.ContactLastNameField, cnt.ContactLastName); 13163 13164 } 13165 13166 if (isBlank(thisApi.ContactEmailAddress) && (typeof cnt.ContactEmailAddress != 'undefined' && !isBlank(cnt.ContactEmailAddress))) 13167 _trySetFieldValue(thisApi.ContactEmailAddressField, cnt.ContactEmailAddress); 13168 13169 if (isBlank(thisApi.ContactPhoneNumber) && (typeof cnt.ContactPhoneNumber != 'undefined' && !isBlank(cnt.ContactPhoneNumber))) 13170 _trySetFieldValue(thisApi.ContactPhoneNumberField, cnt.ContactPhoneNumber); 13171 13172 let isAddressEmpty = (isBlank(thisApi.ContactAddressStreet1) && isBlank(thisApi.ContactAddressStreet2) && isBlank(thisApi.ContactAddressCity) && isBlank(thisApi.ContactAddressStateProvinceCode) && isBlank(thisApi.ContactAddressPostalCode)); 13173 if (isAddressEmpty) { 13174 13175 const cntSlaEntElem = _tryGetContactAddressSingleLineEntryDomElement(); 13176 if (cntSlaEntElem != null && isBlank(cntSlaEntElem.getValue()) && (typeof cnt.ContactSingleLineAddress != 'undefined' && !isBlank(cnt.ContactSingleLineAddress))) 13177 cntSlaEntElem.setValue(cnt.ContactSingleLineAddress); 13178 13179 if (typeof cnt.ContactAddressStreet1 != 'undefined' && !isBlank(cnt.ContactAddressStreet1)) 13180 _trySetFieldValue(thisApi.ContactAddressStreet1Field, cnt.ContactAddressStreet1); 13181 13182 if (typeof cnt.ContactAddressStreet2 != 'undefined' && !isBlank(cnt.ContactAddressStreet2)) 13183 _trySetFieldValue(thisApi.ContactAddressStreet2Field, cnt.ContactAddressStreet2); 13184 13185 if (typeof cnt.ContactAddressCity != 'undefined' && !isBlank(cnt.ContactAddressCity)) 13186 _trySetFieldValue(thisApi.ContactAddressCityField, cnt.ContactAddressCity); 13187 13188 if (typeof cnt.ContactAddressStateProvinceId != 'undefined') 13189 _trySetFieldValue(thisApi.ContactAddressStateProvinceField, cnt.ContactAddressStateProvinceId); 13190 13191 if (typeof cnt.ContactAddressPostalCode != 'undefined' && !isBlank(cnt.ContactAddressPostalCode)) 13192 _trySetFieldValue(thisApi.ContactAddressPostalCodeField, cnt.ContactAddressPostalCode); 13193 13194 if (typeof cnt.ContactAddressCountryId != 'undefined') 13195 _trySetFieldValue(thisApi.ContactAddressCountryField, cnt.ContactAddressCountryId); 13196 13197 } 13198 13199 } 13200 13201 }; 13202 13203 const _initializeVisitorAnonymousContextInfo = function () { 13204 13205 //initialize context
13206 _logger.Log('initializing web visitor data analysis engine'); 13207 13208 _logger.Log('initiate geolocation analysis for web visitor IP address ' + WebFormContext.VisitorLocation.IPAddress); 13209 13210 _logger.Log('begin location analysis...'); 13211 _logger.Log('country identified ' + WebFormContext.VisitorLocation.Country.Name); 13212 13213 //TODO: create currency conversion rates dictionary in context and pass to conversion function 13214 _logger.Log('perform country localization to default currency ' + WebFormContext.VisitorLocation.Country.DefaultCurrency.Name); 13215 13216 _logger.Log('city identified ' + WebFormContext.VisitorLocation.City); 13217 13218 _logger.Log('begin demographic analysis...'); 13219 _logger.Log('demographic income group ' + WebFormContext.VisitorLocation.DemographicInfo.LocationMedianIncomeGroup_Id); 13220 _logger.Log('demographic median income floor ' + WebFormContext.VisitorLocation.DemographicInfo.LocationMedianIncomeFloor); 13221 13222 _logger.Log('geolocation analysis completed') 13223 13224 let initDefaultId = Currencies.USD; 13225 13226 if (WebFormContext.VisitorLocation.Country.DefaultCurrency.Id > 0) 13227 initDefaultId = WebFormContext.VisitorLocation.Country.DefaultCurrency.Id; 13228 13229 _logger.Log('try to set currency to ' + initDefaultId); 13230 13231 _trySetFieldValue(thisApi.PaymentCurrencyField, initDefaultId); 13232 13233 if (WebFormContext.VisitorLocation.LocationMedianIncomeGroup_Id > 0) 13234 _reinitializeAskAmounts(0, WebFormContext.VisitorLocation.LocationMedianIncomeGroup_Id); 13235 //else 13236 //_reinitializeAskAmounts(0); //needed for startup 13237 13238 }; 13239 13240 const _initializeKnownVisitorContext = function () { 13241 13242 let cntxCntId = getWebAppVisitorContextContactId(); 13243 13244 _logger.Log('init visitor: ' + cntxCntId); 13245 13246 //only register and fire if we have an identified visitor 13247 if (cntxCntId > 0) { 13248 13249 App.Captcha.RegisterInitializedCallBack(function () { 13250 13251 _logger.Log('call get form visitor context info (from captcha initialized event)'); 13252 13253 //switched to local debug request - need to test in beta with real browser. 13254 ////needed to prevent captcha errors due to timing of post after page load with no UI interactions 13255 //setTimeout(function () { 13256 13257 //try get contact (note: dependent on CAS which is injected prior to form initialization on page) 13258 App.GetUrlAsync('/api/PublicWebAppUIAjaxEndpoint/GetFormVisitorContextInfo', 13259 function (result) { 13260 13261 _logger.Log('get form visitor context: ' + JSON.stringify(result)); 13262 13263 //if (isBlank(thisApi.ContactFirstName) && isBlank(thisApi.ContactMiddleName) && isBlank(thisApi.ContactLastName)) { 13264 // 13265 // _trySetFieldValue(thisApi.ContactFirstNameFieldField,result.FirstName); 13266 // _trySetFieldValue(thisApi.ContactMiddleNameFieldField,result.MiddleName); 13267 // _trySetFieldValue(thisApi.ContactLastNameFieldField,result.LastName); 13268 // 13269 //} 13270 13271 _tryPopulateFormFromLastFormFill(); 13272 13273 //ask string customization 13274 if (result.HighestPreviousTransactionAmount > 0) 13275 _reinitializeAskAmounts(result.HighestPreviousTransactionAmount, 1); 13276 13277 //configure one-click 13278 _initializeOneClickCheckout(cntxCntId, result.PaymentProfile_Id, result.PaymentProfileDescription); 13279 13280 }, 13281 function (error) { 13282 _logger.Log('error: ' + error); 13283 }); 13284 13285 //}, 5000); 13286 13287 }); 13288 13289 } 13290 else 13291 {
13292 _logger.Log('skip visitor context lookup (anonymous)'); 13293 _tryPopulateFormFromLastFormFill(); 13294 } 13295 13296 }; 13297 13298 const _getQuantityFieldForTicket = function (ticket_id) { 13299 13300 let retVal = null; 13301 13302 if (ticket_id > 0) { 13303 13304 let qtyFields = _getTicketQuantityFields(); 13305 for (let x = 0; x < qtyFields.length; x++) 13306 if (qtyFields[x].DomElement.tryGetIntegerAttribute('data-ticket-id') == ticket_id) { 13307 retVal = qtyFields[x]; 13308 break; 13309 } 13310 13311 } 13312 13313 return retVal; 13314 13315 }; 13316 13317 const _getSubtotalFieldForTicket = function (ticket_id) { 13318 13319 let retVal = null; 13320 13321 if (ticket_id > 0) { 13322 13323 for (let x = 0; x < thisApi.TicketSubTotalFields.length; x++) 13324 if (thisApi.TicketSubTotalFields[x].DomElement.tryGetIntegerAttribute('data-ticket-id') == ticket_id) { 13325 retVal = thisApi.TicketSubTotalFields[x]; 13326 break; 13327 } 13328 13329 } 13330 13331 return retVal; 13332 13333 }; 13334 13335 const _handleTicketQuantityChanged = function () { 13336 13337 _updateFormAfterAmountImpactingChange(); //update cost amount 13338 13339 for (let x = 0; x < thisApi.TicketQuantityFields.length; x++) 13340 if (thisApi.TicketQuantityFields[x].DomElement != null) { 13341 13342 const ticket_id = thisApi.TicketQuantityFields[x].DomElement.tryGetIntegerAttribute('data-ticket-id'); 13343 13344 const subTotaField = _getSubtotalFieldForTicket(ticket_id); 13345 if (subTotaField != null) { 13346 13347 const subTotal = (thisApi.TicketQuantityFields[x].Value * thisApi.TicketQuantityFields[x].DomElement.tryGetDecimalAttribute('data-ticket-price')); 13348 13349 const amtForm = _getAmountFormatted(subTotal); 13350 13351 subTotaField.SetValue(amtForm); 13352 13353 } 13354 13355 const meOrSomeoneElseApi = _getTicketMeOrSomeoneElseApi(ticket_id); 13356 if (meOrSomeoneElseApi != null) 13357 meOrSomeoneElseApi.HandleItemQuantityChanged(); 13358 13359 const multipleTicketsOtherRegistrantsApi = _getMultipleTicketsOtherRegistrantsApi(ticket_id); 13360 if (multipleTicketsOtherRegistrantsApi != null) 13361 multipleTicketsOtherRegistrantsApi.HandleItemQuantityChanged(); 13362 13363 const addContactsApi = _getTicketAdditionalContactsApi(ticket_id); 13364 if (addContactsApi != null) 13365 addContactsApi.HandleItemQuantityChanged(); 13366 13367 } 13368 13369 }; 13370 13371 const _hideTicketSessionFieldsWithNoOptions = function () { 13372 13373 for (let x = 0; x < thisApi.TicketSessionFields.length; x++) 13374 if (!thisApi.TicketSessionFields[x].HasOptions) 13375 !thisApi.TicketSessionFields[x].Hide(); 13376 13377 }; 13378 13379 const _ticketMeOrSomeoneElseApis = []; 13380 const _multipleTicketsOtherRegistrantApis = []; 13381 const _ticketGuestAdditionalContactApis = []; 13382 13383 const _getTicketMeOrSomeoneElseApi = function (ticket_id) { 13384 13385 let retVal = null; 13386 13387 for (let x = 0; x < _ticketMeOrSomeoneElseApis.length; x++) 13388 if (_ticketMeOrSomeoneElseApis[x].RelatedElementReference_Id == ticket_id) { 13389 retVal = _ticketMeOrSomeoneElseApis[x]; 13390 break; 13391 } 13392 13393 return retVal; 13394 13395 }; 13396 13397 const _getMultipleTicketsOtherRegistrantsApi = function (ticket_id) { 13398 13399 let retVal = null; 13400 13401 for (let x = 0; x < _multipleTicketsOtherRegistrantApis.length; x++) 13402 if (_multipleTicketsOtherRegistrantApis[x].RelatedElementReference_Id == ticket_id) { 13403 retVal = _multipleTicketsOtherRegistrantApis[x]; 13404 break; 13405 } 13406 13407 return retVal; 13408 13409 }; 13410 13411 const _getTicketAdditionalContactsApi = function (ticket_id) { 13412 13413 let retVal = null; 13414 13415 for (let x = 0; x < _ticketGuestAdditionalContactApis.length; x++) 13416 if (_ticketGuestAdditionalContactApis[x].RelatedElementReference_Id == ticket_id) { 13417 retVal = _ticketGuestAdditionalContactApis[x]; 13418 break; 13419 } 13420 13421 return retVal; 13422 13423 }; 13424 13425 const _handleReduceTicketQuantityClick = function (ticket_id) { 13426 13427 let tikQtyField = _getQuantityFieldForTicket(ticket_id); 13428 if (tikQtyField != null) { 13429 13430 let curVal = tikQtyField.Value; 13431 if (curVal > 0) { 13432 13433 curVal--; 13434 13435 tikQtyField.SetValue(curVal); 13436 13437 } 13438 13439 } 13440 13441 }; 13442 13443 const _isTicketPurchaseEnabled = function (fldApi) { 13444 13445 return (fldApi.DomElement == null || fldApi.DomElement.getAttribute('data-ticket-enabled') != 'false'); 13446 13447 }; 13448 13449 const _handleIncreaseTicketQuantityClick = function (ticket_id) { 13450 13451 let tikQtyField = _getQuantityFieldForTicket(ticket_id); 13452 if (tikQtyField != null && _isTicketPurchaseEnabled(tikQtyField)) { 13453 13454 let maxAllowed = _getTicketMaxAllowed(tikQtyField); 13455 13456 let curVal = tikQtyField.Value; 13457 if (maxAllowed <= 0 || curVal < maxAllowed) { 13458 13459 curVal++; 13460 13461 tikQtyField.SetValue(curVal); 13462 13463 } 13464 13465 } 13466 13467 }; 13468 13469 const _initializeTicketQuantityFieldDependencies = function () { 13470 13471 //FOR NOW WE ALWAYS HIDE - maybe we show when there's an amount later, but TBD 13472 //(done unconditionally for every ticket, not just ones with an active quantity control, so a ticket rendered as a sale-window message still gets its subtotal hidden) 13473 for (let s = 0; s < thisApi.TicketSubTotalFields.length; s++) 13474 thisApi.TicketSubTotalFields[s].Hide(); 13475 13476 for (let x = 0; x < thisApi.TicketQuantityFields.length; x++) { 13477 13478 const fldApi = thisApi.TicketQuantityFields[x]; 13479 const fe = fldApi.DomElement; 13480 const tid = fe.tryGetIntegerAttribute('data-ticket-id'); 13481 13482 //console.log('fldApi.Configuration.SystemFieldRenderType_Id: ' + fldApi.Configuration.SystemFieldRenderType_Id); 13483 //console.log('FORM_SYSTEM_FIELD_RENDER_TYPES.TextBox: ' + FORM_SYSTEM_FIELD_RENDER_TYPES.TextBox); 13484 if (fldApi.Configuration.SystemFieldRenderType_Id == FORM_SYSTEM_FIELD_RENDER_TYPES.TextBox) { 13485
13486 //add minus button 13487 const tikMinusButton = _generateFormFieldButton('reduceTicketQuantity_' + tid, function (btn) { _handleReduceTicketQuantityClick(btn.tryGetIntegerAttribute('data-ticket-id')); }, 'reduce number of tickets'); 13488 _addFormFieldButtonIconCss(tikMinusButton, 'fa fa-minus'); 13489 tikMinusButton.setAttribute('data-ticket-id', tid); 13490 fldApi.WrapperDomElement.appendChild(tikMinusButton); 13491 13492 const tikPlusButton = _generateFormFieldButton('increaseTicketQuantity_' + tid, function (btn) { _handleIncreaseTicketQuantityClick(btn.tryGetIntegerAttribute('data-ticket-id')); }, 'increase number of tickets'); 13493 _addFormFieldButtonIconCss(tikPlusButton, 'fa fa-plus'); 13494 tikPlusButton.setAttribute('data-ticket-id', tid); 13495 fldApi.WrapperDomElement.appendChild(tikPlusButton); 13496 13497 } 13498 13499 //Me or someone else UX 13500 const meOrSomeoneElseRow = _createFormRow('meOrSomeoneElseWidgetRow_' + tid, 'ce-meOrSomeoneElseWidgetRows'); 13501 13502 fldApi.SectionDomElement.insertAfterElement(meOrSomeoneElseRow, fldApi.RowDomElement); 13503 13504 const newMeOrSomoeneElseApi = getCheckOutItemForMeOrSomeoneElseApi({ "ItemQuantityFieldDomId": fe.id, "RelatedElementContainerDomId": fldApi.RowDomElement.id, "WidgetContainerDomId": meOrSomeoneElseRow.id, "EmailAddressRequired": false, "PhoneNumberRequired": false, "ToggleLableCheckedText": 'I am an attendee', "ToggleLableNotCheckedText": '', "RelatedElementReference_Id": tid, "AutoInitialize": false }); 13505 newMeOrSomoeneElseApi.Initialize(); 13506 _ticketMeOrSomeoneElseApis.push(newMeOrSomoeneElseApi); 13507 13508 //Additional registrants (multi-ticket) 13509 const multiTicketAdditionalAttendeesRowRow = _createFormRow('additionalAttendeesWidgetRow_' + tid, 'ce-additionalContactsWidgetRows'); 13510 13511 fldApi.SectionDomElement.insertAfterElement(multiTicketAdditionalAttendeesRowRow, meOrSomeoneElseRow); 13512 13513 const multipleTicketsOtherRegistrantsApi = getAddAdditionalContactsApi({ "ItemQuantityFieldDomId": fe.id, "RelatedElementContainerDomId": fldApi.RowDomElement.id, "WidgetContainerDomId": multiTicketAdditionalAttendeesRowRow.id, "MaximumContactsPerItem": 1, "GetMaximumContactsFunction": function (qty) { return (qty - 1); }, "EmailAddressRequired": false, "PhoneNumberRequired": false, "TitleHtml": '', "AddContactButtonText": 'Add Attendee', "AddFirstContactRow": false, "RelatedElementReference_Id": tid, "AutoInitialize": false }); 13514 multipleTicketsOtherRegistrantsApi.Initialize(); 13515 _multipleTicketsOtherRegistrantApis.push(multipleTicketsOtherRegistrantsApi); 13516 13517 //Add Guests 13518 const newAdditionalContactsRow = _createFormRow('additionalContactsWidgetRow_' + tid, 'ce-additionalContactsWidgetRows'); 13519 13520 fldApi.SectionDomElement.insertAfterElement(newAdditionalContactsRow, multiTicketAdditionalAttendeesRowRow); 13521 13522 const maxGuests = _getGuestsMaxAllowed(fldApi); 13523 13524 //tickets with maxGuest 0 mean no guests allowed 13525 if (maxGuests > 0) { 13526 let newAdditionalContactsApi = getAddAdditionalContactsApi({ "ItemQuantityFieldDomId": fe.id, "RelatedElementContainerDomId": fldApi.RowDomElement.id, "WidgetContainerDomId": newAdditionalContactsRow.id, "MaximumContactsPerItem": maxGuests, "MaximumContacts": 0, "EmailAddressRequired": false, "PhoneNumberRequired": false, "TitleHtml": '', "AddContactButtonText": 'Add Guest', "AddFirstContactRow": false, "RelatedElementReference_Id": tid, "AutoInitialize": false }); 13527 newAdditionalContactsApi.Initialize(); 13528 _ticketGuestAdditionalContactApis.push(newAdditionalContactsApi); 13529 } 13530 13531 } 13532 13533 }; 13534 13535 const _getTicketMaxAllowed = function (fldApi) { 13536 13537 let retVal = fldApi.DomElement.tryGetIntegerAttribute('data-max-allowed'); 13538 13539 return retVal; 13540 13541 }; 13542 13543 const _getGuestsMaxAllowed = function (fldApi) { 13544 13545 let retVal = fldApi.DomElement.tryGetIntegerAttribute('data-max-guests-allowed'); 13546 13547 return retVal; 13548 13549 } 13550 13551 const _getFormattedTicketPrice = function (fldApi) { 13552 13553 let sym = _getPaymentCurrencySymbol(); 13554 13555 let tikPrice = fldApi.DomElement.tryGetDecimalAttribute('data-ticket-price'); 13556 13557 return formatCurrency(tikPrice, true, sym); 13558 13559 }; 13560 13561 const _initializeEventTicketsSection = function () { 13562 13563 const tikQntFlds = _getTicketQuantityFields(); 13564 if (tikQntFlds.length > 0) { 13565
13566 _logger.Log('begin initialize event tickets section'); 13567 13568 for (let x = 0;x < tikQntFlds.length;x++) { 13569 13570 const fldApi = tikQntFlds[x]; 13571 13572 _wireUpFieldValueChangeDetectionEvent(fldApi.Configuration, _handleTicketQuantityChanged); 13573 13574 const tikPriceFormatted = _getFormattedTicketPrice(fldApi); 13575 13576 //update label to include price 13577 if (fldApi.LabelDomElement != null) 13578 fldApi.LabelDomElement.innerHTML += ' (' + tikPriceFormatted + ')'; 13579 13580 _addCoverProcessingCostBlock(fldApi.SectionDomElement); 13581 13582 } 13583 13584 _initializeTicketQuantityFieldDependencies(); 13585 13586 _hideTicketSessionFieldsWithNoOptions(); 13587 13588 } 13589 13590 }; 13591 13592 const _handleProductConfigChanged = function (product_id) { 13593 13594 let productApi = null; 13595 for (let x = 0;x < WebFormContext.Form.ProductWidgetApis.length;x++) 13596 if (WebFormContext.Form.ProductWidgetApis[x].Product_Id == product_id){ 13597 productApi = WebFormContext.Form.ProductWidgetApis[x]; 13598 break; 13599 } 13600 13601 if (productApi != null) { 13602 13603 const priceDisplayContainer = productApi.ContainerDomElement.querySelector('.ce-productPrices'); 13604 if (priceDisplayContainer != null) { 13605 13606 let sym = _getPaymentCurrencySymbol(); 13607 13608 priceDisplayContainer.innerHTML = formatCurrency(productApi.SelectionDetails.TotalPrice, true, sym); 13609 13610 priceDisplayContainer.vanish(); 13611 if (productApi.SelectionDetails.Quantity > 0) priceDisplayContainer.show(); 13612 13613 } 13614 13615 } 13616 else 13617 _logger.Log('handle product config changed called without product_id (bug)'); 13618 13619 _updateFormAfterAmountImpactingChange(); 13620 13621 }; 13622 13623 const _initializeProducts = function(){ 13624 13625 if (typeof WebFormContext.Form.ProductWidgetApis == 'undefined' || WebFormContext.Form.ProductWidgetApis.length <= 0) 13626 return; 13627 13628 _logger.Log('begin initialize product widgets'); 13629 13630 for (let x = 0;x < WebFormContext.Form.ProductWidgetApis.length;x++) { 13631 13632 const productApi = WebFormContext.Form.ProductWidgetApis[x]; 13633 13634 productApi.Initialize(); //call initialize 13635 13636 const prodWidgetRowDomElem = productApi.ContainerDomElement; 13637 13638 const product_id = productApi.Product_Id; 13639 13640 if (productApi.QuantityDomElement != null){ 13641 13642 const qtyLblElem = productApi.QuantityDomElement.getAssociatedFieldLabel(); 13643 if(qtyLblElem != null) 13644 _initializeDynamicLabelField(productApi.QuantityDomElement, qtyLblElem); 13645 13646 //wire up plus/minus box 13647 getAmountTextBoxPlusMinusButtonsApi({"TextBoxDomId": productApi.QuantityDomElement.id, "MinimumValue": 0, "MaximumValue": 0, "AmountChangedCallBack": function () { _handleProductConfigChanged(product_id); } }).Initialize(); 13648 13649 } 13650 13651 if (productApi.SetPriceDomElement != null) { 13652 13653 const setPriceLblElem = productApi.SetPriceDomElement.getAssociatedFieldLabel(); 13654 if (setPriceLblElem != null) 13655 _initializeDynamicLabelField(productApi.SetPriceDomElement, setPriceLblElem); 13656 13657 //wire up plus/minus box 13658 getAmountTextBoxPlusMinusButtonsApi({"TextBoxDomId": productApi.SetPriceDomElement.id, "MinimumValue": 0, "MaximumValue": 0, "AmountChangedCallBack": function () { _handleProductConfigChanged(product_id); }}).Initialize(); 13659 13660 } 13661 13662 if (productApi.AttributeDropDownDomElements != null) 13663 for (let x = 0;x < productApi.AttributeDropDownDomElements.length;x++) { 13664 13665 const atrElem = productApi.AttributeDropDownDomElements[x]; 13666 13667 const atrLblElem = atrElem.getAssociatedFieldLabel(); 13668 if (atrLblElem != null) 13669 _initializeDynamicLabelField(atrElem, atrLblElem); 13670 13671 atrElem.addEventListener('change', function () {_handleProductConfigChanged(product_id); }); 13672 13673 } 13674 13675 if(productApi.CustomFieldDomElements != null) 13676 for (let x = 0;x < productApi.CustomFieldDomElements.length;x++) { 13677 13678 const cfElem = productApi.SetPriceDomElement[x]; 13679 13680 const cfLblElem = cfElem.getAssociatedFieldLabel(); 13681 if (cfLblElem != null) 13682 _initializeDynamicLabelField(cfElem, cfLblElem); 13683 13684 cfElem.addEventListener('change', function () {_handleProductConfigChanged(product_id);}); 13685 13686 } 13687 13688 const priceDisplayContainer = productApi.ContainerDomElement.querySelector('.ce-productPrices'); 13689 if(priceDisplayContainer != null) priceDisplayContainer.vanish(); 13690 13691 } 13692 13693 }; 13694 13695 const _addMultiSelectFieldElementOption = function (elem, value, text, selected, attributes) { 13696 13697 let domId = elem.id + '_option_' + (elem.children.length + 1); 13698 let domName = elem.id + '_options'; 13699 13700 addMultiSelectInputFieldOption(elem, value, text, selected, 'ce-formFieldOptionItems', domId, domName, attributes); 13701 13702 }; 13703 13704 const _hideGooglePayOptionIfSdkNotLoaded = function () { 13705 13706 if (thisApi.PaymentMethodField != null && thisApi.PaymentMethodField.DomElement != null && !thisApi.GooglePayApi.IsGooglePayApiAvailable()) { 13707 13708 let options = thisApi.PaymentMethodField.DomElement.querySelectorAll('.ce-formFieldOptionItems'); 13709 for (let x = 0; x < options.length; x++) 13710 if (options[x].tryGetIntegerAttribute('data-wallet-type-id') == WALLET_TYPES.GOOG
13710LE_PAY) { 13711 options[x].vanish(); 13712 break; 13713 } 13714 13715 } 13716 13717 }; 13718 13719 const _tryShowPaymentMethodBillingInfoFields = function () { 13720 13721 for (let x = 0; x < _allFieldApis.length; x++) 13722 if (_allFieldApis[x].IsPaymentMethodBillingInformation) 13723 _allFieldApis[x].Show(); 13724 13725 }; 13726 13727 const _tryHidePaymentMethodBillingInfoFields = function () { 13728 13729 for (let x = 0; x < _allFieldApis.length; x++) 13730 if (_allFieldApis[x].IsPaymentMethodBillingInformation) 13731 _allFieldApis[x].Hide(); 13732 13733 }; 13734 13735 const _handleToggleBillingInfoClicked = function () { 13736 13737 if (document.getElementById('togglePaymentMethodBillingInfoCheckBox') != null) { 13738 13739 let tcb = document.getElementById('togglePaymentMethodBillingInfoCheckBox').getValue(); 13740 if (tcb) { 13741 13742 //only show for credit card 13743 if (thisApi.PaymentMethodField.Value == PAYMENT_METHODS.CREDIT_CARD) 13744 _tryShowPaymentMethodBillingInfoFields(); 13745 13746 } 13747 else 13748 _tryHidePaymentMethodBillingInfoFields(); 13749 13750 } 13751 13752 }; 13753 13754 const _tryShowPaymentMethodField = function() { 13755 13756 if (thisApi.PaymentMethodField != null && thisApi.PaymentMethodField.OptionCount > 1) 13757 thisApi.PaymentMethodField.Show(); 13758 13759 }; 13760 13761 const _tryHidePaymentMethodField = function() { 13762 13763 if (thisApi.PaymentMethodField != null) 13764 thisApi.PaymentMethodField.Hide(); 13765 13766 }; 13767 13768 const _addRadioButtonLabelIcon = function (inputElem, iconCssClasses) { 13769 13770 if (inputElem != null && !isBlank(iconCssClasses)) { 13771 13772 let lbl = inputElem.getAssociatedLabel(); 13773 if (lbl != null) { 13774 13775 lbl.addClassName('ce-radioButtonIconLabels'); 13776 13777 let iconElem = document.createElement('span'); 13778 iconElem.addClassNames(iconCssClasses); 13779 13780 lbl.insertFirstElement(iconElem); 13781 13782 } 13783 13784 } 13785 13786 }; 13787 13788 const _addFieldHelpIconWidget = function (field,html) { 13789 13790 if (field != null && field.LabelDomElement != null) { 13791 13792 field.LabelDomElement.addClassName('ce-formFieldHelpIconLabels'); 13793 13794 let newWidget = document.createElement('div'); 13795 newWidget.addClassName('ce-formFieldInfoIconWidgets'); 13796 13797 let icon = document.createElement('span'); 13798 icon.addClassNames('fa fa-question-circle'); 13799 13800 icon.addEventListener('mouseenter', function (event) { event.target.parentElement.querySelector('.ce-formFieldInfoIconOverlays').show(); }); 13801 icon.addEventListener('mouseleave', function (event) { event.target.parentElement.querySelector('.ce-formFieldInfoIconOverlays').vanish(); }); 13802 13803 newWidget.appendChild(icon); 13804 13805 let htmlOverlay = document.createElement('div'); 13806 htmlOverlay.addClassName('ce-formFieldInfoIconOverlays'); 13807 13808 htmlOverlay.addHtml(html); 13809 13810 htmlOverlay.vanish(); 13811 13812 newWidget.appendChild(htmlOverlay); 13813 13814 field.LabelDomElement.appendChild(newWidget); 13815 13816 } 13817 13818 }; 13819 13820 const _addBankAccountHelpIcon = function (field) { 13821 _addFieldHelpIconWidget(field,'<img src="/_sysimages/achhelper.png" class="ce-checkFieldHelperImages" />'); 13822 }; 13823 13824 //moves the PayPal button container into the Payment Method Section BEFORE PayPalApi.Initialize() renders 13825 //the buttons into it - the PayPal SDK (zoid) tears down/re-renders its buttons if their container is 13826 //reparented in the DOM after render(), so this must run first, not from inside _initializePaymentMethodSection 13827 const _movePayPalButtonIntoPaymentMethodSection = function () { 13828 13829 if (thisApi.PaymentMethodField != null && thisApi.PaymentMethodField.DomElement != null) { 13830 13831 let paymentMethodSecElem = thisApi.PaymentMethodField.SectionDomElement; 13832 13833 let ppElem = getMultiSelectInputOptionElementByValue(thisApi.PaymentMethodField.DomElement, PAYMENT_METHODS.PAYPAL); 13834 if (ppElem != null) { 13835 13836 if (!thisApi.PayPalApi.PayPalEnabled || !thisApi.PayPalApi.IsPayPalApiAvailable()) 13837 ppElem.firstParentOrSelfWithClass('ce-formFieldOptionItems').vanish(); 13838 else { 13839 _addRadioButtonLabelIcon(ppElem, 'fa fa-brands fa-paypal'); //todo: maybe make configurable or move to template - TBD 13840 13841 //move PayPal button into Payment Method Section 13842 paymentMethodSecElem.appendChild(thisApi.PayPalApi.GetButtonsContainerElement()); 13843 } 13844 13845 } 13846 13847 } 13848 13849 }; 13850 13851 const _initializePaymentMethodSection = function () { 13852 13853 if (thisApi.PaymentMethodField != null) { 13854 //get payment method section 13855 let paymentMethodSecElem = thisApi.PaymentMethodField.SectionDomElement; 13856 13857 //hide payment method field if there is ony one option 13858 if (thisApi.PaymentMethodField.OptionCount <= 1) 13859 _tryHidePaymentMethodField(); 13860 13861 //native field event bindings 13862 _wireUpFieldValueChangeDetectionEvent(thisApi.PaymentMethodField.Configuration, _handlePaymentMethodChanged); 13863 13864 //wire up credit card UX 13865 13866 //wire up expiration date UX 13867 if (thisApi.CreditCardExpirationDateField != null && thisApi.CreditCardExpirationDateField.DomElement != null) 13868 thisApi.CreditCardExpirationDateField.DomElement.addEventListener('keyup', function (e) { console.log('cc exp key up'); handleMMYYCreditCardExpirationInputChange(thisApi.CreditCardExpirationDateField.DomElement, e); }); 13869 13870 //wire up bank account UX 13871 if (thisApi.BankAccountNumberField != null) 13872 _addBankAccountHelpIcon(thisApi.BankAccountNumberField); 13873 13874 //wire up routing number UX 13875 if (thisApi.BankRoutingNumberField != null) 13876 _addBankAccountHelpIcon(thisApi.BankRoutingNumberField); 13877 13878 let fistBillingInfoField = _getFirstPaymentMethodBillingInfoField(); 13879 if (fistBillingInfoField != null) { 13880 13881 let cb = _createRowWithSingleCheckBoxOption('togglePaymentMethodBillingInfoContainer', '', 'togglePaymentMethodBillingInfoCheckBox', true, 'Billing info is different', false, function () { _handleToggleBillingInfoClicked(); }); 13882 13883 fistBillingInfoField.RowDomElement.insertBeforeElement(cb); 13884 13885 _handleToggleBillingInfoClicked(); 13886 13887 } 13888 13889 if (thisApi.PaymentMethodField.DomElement != null) { 13890 13891 let ccElem = getMultiSelectInputOptionElementByValue(thisApi.PaymentMethodField.DomElement, PAYMENT_METHODS.CREDIT_CARD); 13892 if (ccElem != null) 13893 _addRadioButtonLabelIcon(ccElem, 'fa fa-credit-card'); //todo: maybe make configurable or move to template - TBD 13894 13895 let bnkElem = getMultiSelectInputOptionElementByValue(thisApi.PaymentMethodField.DomElement, PAYMENT_METHODS.ACH); 13896 if (bnkElem != null) 13897 _addRadioButtonLabelIcon(bnkElem,'fa fa-bank'); //todo: maybe make configurable or move to template - TBD 13898 13899 let scElem = getMultiSelectInputOptionElementByValue(thisApi.PaymentMethodField.DomElement, PAYMENT_METHODS.SOFT_CREDIT); 13900 if (scElem != null) { 13901 13902 if (!thisApi.DafPayPaymentMethodAvailable || !thisApi.DafPayApi.DafPayEnabled) 13903 scElem.firstParentOrSelfWithClass('ce-formFieldOptionItems').vanish(); 13904 else { 13905 13906 _addRadioButtonLabelIcon(scElem,'fa fa-hand-holding-heart'); //todo: maybe make configurable or move to template - TBD 13907 13908 //backwards compatibiliyt fix for lamo naming conventions 13909 let scLbl = getLabelForField(scElem); 13910 if (scLbl != null && scLbl.innerText.toLowerCase().trim() == 'soft credit') 13911 scLbl.innerText = 'Donor Advised Fund'; 13912 13913 //move DAFPay button into Payment Method Section 13914 paymentMethodSecElem.appendChild(thisApi.DafPayApi.GetButtonContainerElement()); 13915 13916 } 13917 13918 } 13919 13920 13921 if (thisApi.GooglePayApi.GooglePayEnabled) { 13922 13923 let gpAttrs = []; 13924 gpAttrs.push({ "key": 'data-wallet-type-id', "value": WALLET_TYPES.GOOG
13924LE_PAY }); 13925 13926 _addMultiSelectFieldElementOption(thisApi.PaymentMethodField.DomElement, PAYMENT_METHODS.CREDIT_CARD, 'GooglePay', false, gpAttrs); 13927 13928 let newGpElem = document.querySelector('span.ce-formFieldOptionItems[data-wallet-type-id="' + WALLET_TYPES.GOOGLE_PAY + '"] input'); 13929 if (newGpElem != null) { 13930 _addRadioButtonLabelIcon(newGpElem, 'fa fa-brands fa-google'); //todo: maybe make configurable or move to template - TBD 13931 13932 //move GooglePay button into Payment Method Section 13933 paymentMethodSecElem.appendChild(thisApi.GooglePayApi.GetButtonContainerElement()); 13934 } 13935 13936 _hideGooglePayOptionIfSdkNotLoaded(); 13937 13938 } 13939 13940 if (thisApi.ApplePayApi.ApplePayEnabled && thisApi.ApplePayApi.IsApplePayApiAvailable()) { 13941 13942 let apAttrs = []; 13943 apAttrs.push({ "key": 'data-wallet-type-id', "value": WALLET_TYPES.APPLE_PAY }); 13944 13945 _addMultiSelectFieldElementOption(thisApi.PaymentMethodField.DomElement, PAYMENT_METHODS.CREDIT_CARD, 'ApplePay', false, apAttrs); 13946 13947 let newApElem = document.querySelector('span.ce-formFieldOptionItems[data-wallet-type-id="' + WALLET_TYPES.APPLE_PAY + '"] input'); 13948 if (newApElem != null) { 13949 _addRadioButtonLabelIcon(newApElem, 'fa fa-brands fa-apple'); //todo: maybe make configurable or move to template - TBD 13950 13951 //move ApplePay button into Payment Method Section 13952 paymentMethodSecElem.appendChild(thisApi.ApplePayApi.GetButtonContainerElement()); 13953 } 13954 13955 } 13956 13957 } 13958 13959 } 13960 else 13961 thisApi.ShowSubmitButton(); //_hideAllPaymentFields(); 13962 13963 }; 13964 13965 const _recalculateMembershipDisplayRate = function () { 13966 13967 _logger.Log('recalculate memership rate display'); 13968 13969 let memTpId = thisApi.MembershipTypeId; 13970 13971 _logger.Log('membership type ' + memTpId); 13972 13973 let mtRate = _getSelectedMembershipRate(); 13974 let durationDays = _getSelectedMembershipDurationDays(); 13975 let allowSubMembers = getMultiSelectInputSelectedOptionBoolAttributeValue(thisApi.MembershipTypeField.DomElement,'data-membership-allow-sub-members'); 13976 let maxSubMembers = getMultiSelectInputSelectedOptionIntegerAttributeValue(thisApi.MembershipTypeField.DomElement,'data-membership-maximum-sub-members'); 13977 13978 if (allowSubMembers) 13979 _membershipAdditionalContactApi.Show(); 13980 else 13981 _membershipAdditionalContactApi.Hide(); 13982 13983 _logger.Log('set maximum sub members to ' + maxSubMembers); 13984 _membershipAdditionalContactApi.SetMaximumContactsPerItem(maxSubMembers); 13985 13986 if (durationDays <= 0) 13987 durationDays = 365; 13988 13989 _logger.Log('membership type rate ' + mtRate + ' duration ' + durationDays); 13990 13991 //TODO: need to evaluate whether this should drive membership frequency or if we need a membership specific UI (what do we do if there's an additional monthly gift?) 13992 let memPayFreq_id = _getMembershipPaymentPlanFrequencyId(); 13993 13994 //calculate rate based on frequency (payment plan) 13995 let displayRate = mtRate; 13996 13997 if (displayRate > 0) { 13998 13999 switch (memPayFreq_id) { 14000 case PaymentFrequencies.Annually: 14001 displayRate = mtRate; 14002 break; 14003 case PaymentFrequencies.BiAnnually: 14004 displayRate = (mtRate / 2); //Math.round 14005 break; 14006 case PaymentFrequencies.Quarterly: 14007 displayRate = (mtRate / 4); //Math.round 14008 break; 14009 case PaymentFrequencies.Monthly: 14010 displayRate = (mtRate / 12); //Math.round 14011 break; 14012 case PaymentFrequencies.Weekly: 14013 displayRate = (mtRate / 52); //Math.round 14014 break; 14015 case PaymentFrequencies.Daily: 14016 displayRate = (mtRate / 365); //Math.round 14017 break; 14018 } 14019 14020 } 14021 14022 let memDisp = _getSelectedMembershipRateDisplayElem(); 14023 if (memDisp != null) 14024 memDisp.innerHTML = '<span class="ce-formFieldCurrencyPrefixes">$</span>' + formatCurrency(displayRate, true); 14025 14026 }; 14027 14028 const _handleMembershipTypeChanged = function () { 14029 14030 let memTpId = thisApi.MembershipTypeId; 14031
14032 _logger.Log('membership type changed to ' + memTpId); 14033 14034 _recalculateMembershipDisplayRate(); 14035 14036 _handlePaymentAmountChanged(); 14037 14038 }; 14039 14040 const _handleMembershipTypeSelectorChanged = function () { _logger.Log('membership type selector changed handler fired'); _handleMembershipTypeChanged(); }; 14041 14042 const _handleMembershipPaymentFrequencyChanged = function () { _logger.Log('membership frequency changed handler fired'); _recalculateMembershipDisplayRate(); }; 14043 14044 let _membershipAdditionalContactApi = null; 14045 14046 const _getSelectedMembershipRateDisplayElem = function () { return document.getElementById('membershipDisplayRate'); } 14047 14048 const _getSelectedMembershipRate = function () { 14049 14050 let retVal = 0.00; 14051 14052 if(thisApi.MembershipTypeField != null) 14053 retVal = getMultiSelectInputSelectedOptionDecimalAttributeValue(thisApi.MembershipTypeField.DomElement, 'data-membership-rate'); 14054 14055 return retVal; 14056 14057 }; 14058 14059 //TODO: need to wrie up data-membership-tax-deductible-amount 14060 const _getSelectedMembershipTaxDeductibleAmount = function () { 14061 14062 let retVal = 0.00; 14063 14064 if(thisApi.MembershipTypeField != null) 14065 retVal = getMultiSelectInputSelectedOptionDecimalAttributeValue(thisApi.MembershipTypeField.DomElement, 'data-membership-tax-deductible-amount'); 14066 14067 return retVal; 14068 14069 }; 14070 14071 const _getSelectedMembershipDurationDays = function () { 14072 14073 let retVal = 0; 14074 14075 if(thisApi.MembershipTypeField != null) 14076 retVal = getMultiSelectInputSelectedOptionIntegerAttributeValue(thisApi.MembershipTypeField.DomElement, 'data-membership-duration-days'); 14077 14078 return retVal; 14079 14080 }; 14081 14082 //todo: evaluate new UI 14083 const _getMembershipPaymentPlanFrequencyId = function () { return PaymentFrequencies.Annually; }; 14084 14085 const _initializeMembershipSection = function () { 14086 14087 //NOTE: tribute dependent on amount section - we're putting the checkbox there for now. 14088 if (thisApi.MembershipTypeField != null) { 14089 14090 _logger.Log('begin initialize membership section (membership type field present)'); 14091 14092 //add display amount field after membership 14093 _addRowWithSingleDisplayElement(thisApi.MembershipTypeField.SectionDomElement, 'membershipDisplayRateRow', 'ce-membershipDisplayRateRows', 'membershipDisplayRate', 'Rate', ''); 14094 14095 _addCoverProcessingCostBlock(thisApi.MembershipTypeField.SectionDomElement); 14096 14097 //TODO: we need to make sure visible frequency is displayed after the current field 14098 ////if (thisApi.RecurringPaymentFrequencyField != null && thisApi.RecurringPaymentFrequencyField.Configuration.Type.Id != FORM_FIELD_TYPES.HIDDEN_FIELD) { 14099 14100 _addRowWithSingleCheckBoxOption(thisApi.MembershipTypeField.SectionDomElement, 'autoRenewMembershipCheckBoxRow', 'ce-autoRenewMembershipCheckBoxes', 'autoRenewMembershipCheckBox', true, 'Automatically renew my membership', true); //, function () { _handleAutoRenewmembershipClick(); } 14101 14102 let newSubMembershipRow = _createFormRow('membershipAdditionalContactsWidgetRow', 'ce-additionalContactsWidgetRows'); 14103 14104 thisApi.MembershipTypeField.SectionDomElement.appendChild(newSubMembershipRow); 14105 14106 _membershipAdditionalContactApi = getAddAdditionalContactsApi({ "RelatedElementContainerDomId": thisApi.MembershipTypeField.RowDomElement.id, "WidgetContainerDomId": newSubMembershipRow.id, "MaximumContacts": 0, "EmailAddressRequired": false, "PhoneNumberRequired": false, "TitleHtml": '', "AddContactButtonText": 'Add Member', "AddFirstContactRow": false, "RelatedElementReference_Id": 0, "AutoInitialize": false }); 14107 _membershipAdditionalContactApi.Initialize(); 14108 _membershipAdditionalContactApi.Hide(); //default 14109 14110 _wireUpFieldValueChangeDetectionEvent(thisApi.MembershipTypeField.Configuration, _handleMembershipTypeSelectorChanged); 14111 14112 _handleMembershipTypeSelectorChanged(); 14113 14114 //TODO: need to evaluate whether this should drive membership frequency or if we need a membership specific UI (what do we do if there's an additional monthly gift?) 14115 //thisApi.RegisterPaymentRecurringFrequencyChangedCallBack(_handleMembershipPaymentFrequencyChanged); 14116 14117 } 14118 14119 }; 14120 14121 const _tryInitializeDatePickerField = function (domElem) { 14122 14123 if (domElem != null && typeof flatpickr !== 'undefined') { 14124
14125 flatpickr(domElem, 14126 { 14127 enableTime: false, 14128 dateFormat: "n/j/Y" 14129 }); 14130 14131 } 14132 else 14133 _logger.Log('date field cannot initialize (flatpikr script not installed)'); 14134 14135 }; 14136 14137 const _tryInitializeDateTimePickerField = function (domElem) { 14138 14139 if (domElem != null && typeof flatpickr !== 'undefined') { 14140 14141 flatpickr(domElem, 14142 { 14143 enableTime: true, 14144 dateFormat: "n/j/Y h:i K", 14145 time_24hr: false 14146 }); 14147 14148 } 14149 else 14150 _logger.Log('date field cannot initialize (flatpikr script not installed)'); 14151 14152 }; 14153 14154 const _initializePledgeSection = function () { 14155 14156 if (WebFormContext.Form.Type.Id == FORM_TYPES.Pledge) { 14157 14158 if (thisApi.RecurringPaymentStartDateField != null) 14159 _tryInitializeDatePickerField(thisApi.RecurringPaymentStartDateField.DomElement); 14160 14161 } 14162 14163 }; 14164 14165 const _initializeVolunteerPostTimeEntryWidget = function () { 14166 14167 const timeEntryInputs = document.querySelectorAll('.ce-volunteerPostTimeEntryStartDates,.ce-volunteerPostTimeEntryEndDates'); 14168 for (let x = 0; x < timeEntryInputs.length; x++) 14169 _tryInitializeDateTimePickerField(timeEntryInputs[x]); 14170 14171 }; 14172 14173 const _initializeFileUploadFields = function () { 14174 14175 let fufs = _getFileUploadFieldApis(); 14176 14177 if (fufs.length > 0) { 14178 14179 _logger.Log('initialize ' + fufs.length + ' file upload fields'); 14180 14181 for (let x = 0; x < fufs.length; x++) { 14182 14183 fufs[x].DomElement.addEventListener('change', function () { 14184 14185 _logger.Log('file changed for field ' + this.id); 14186 14187 let fldApi = _getFieldByDomId(this.id); 14188 if (fldApi != null) { 14189 14190 //let ext = fldApi.DomElement.files[0].name.getFileExtension(); 14191 14192 fldApi.SetFileNameValue(''); 14193 fldApi.SetBase64Value(''); 14194 14195 readFileInput(fldApi.DomElement.files[0], 14196 function (fileData) { 14197 14198 _logger.Log('file ' + fldApi.DomElement.files[0].name + ' set with data (' + fileData.Base64Data.length + ')'); 14199 14200 fldApi.SetFileNameValue(fldApi.DomElement.files[0].name); 14201 fldApi.SetBase64Value(fileData.Base64Data); 14202 14203 }); 14204 14205 } 14206 else 14207 _logger.Log('unable to find api for field (file content save aborted)'); 14208 14209 }); 14210 14211 } 14212 14213 } 14214 14215 }; 14216 14217 //called by update form for payment impacting change, so no need to wire up 14218 const _refreshFreeGiftSectionBasedOnAmountChange = function () { 14219 14220 if (thisApi.FreeGiftSelectorField != null) { 14221 14222 let paymentAmount = thisApi.GetTransactionAmount(); 14223 14224 thisApi.RecurringPaymentFrequencyId > 0 14225 14226 let coverFeesAmt = _calculateFees(); 14227 let recurringAmount = thisApi.RecurringPaymentAmount + coverFeesAmt; 14228 14229 if (thisApi.RecurringPaymentFrequencyId <= 0) 14230 recurringAmount = 0; 14231 14232 _logger.Log('refreshing free gift section after amount change (one-time: ' + paymentAmount + ', recurring: ' + recurringAmount + ')'); 14233 14234 let availableFreeGiftCount = 0; 14235 14236 //loop through to get and check 14237 let freeGiftElems = document.querySelectorAll('.ce-freeGifts .ce-formFieldOptionItems'); 14238 for (let x = 0; x < freeGiftElems.length; x++) { 14239 14240 _logger.Log('data-minimum-amount: ' + freeGiftElems[x].tryGetDecimalAttribute('data-minimum-amount')); 14241 _logger.Log('data-minimum-recurring-amount: ' + freeGiftElems[x].tryGetDecimalAttribute('data-minimum-recurring-amount')); 14242 14243 if (paymentAmount >= freeGiftElems[x].tryGetDecimalAttribute('data-minimum-amount') && recurringAmount >= freeGiftElems[x].tryGetDecimalAttribute('data-minimum-recurring-amount')) { 14244 14245 freeGiftElems[x].show(); 14246 14247 availableFreeGiftCount++; 14248 14249 } 14250 else { 14251 freeGiftElems[x].vanish(); 14252 freeGiftElems[x].querySelector('input[type="radio"]').checked = false; 14253 } 14254 14255 } 14256 14257 if (availableFreeGiftCount > 0) 14258 thisApi.FreeGiftSelectorField.Show(); 14259 else 14260 thisApi.FreeGiftSelectorField.Hide(); 14261 14262 //hide section if it is a free gift section type 14263 if (thisApi.FreeGiftSelectorField.Section.Configuration.SystemSectionType == FORM_SECTION_TYPES.Free_Gift) { 14264 14265 if (availableFreeGiftCount > 0) 14266 thisApi.FreeGiftSelectorField.Section.Show(); 14267 else 14268 thisApi.FreeGiftSelectorField.Section.Hide(); 14269 14270 } 14271 14272 //console.log('thisApi.FreeGiftSelectorProductId: ' + thisApi.FreeGiftSelectorProductId); 14273 14274 } 14275 14276 }; 14277 14278 const _initializeDoubleTheDonation = function () { 14279 14280 if (!isBlank(WebFormContext.Form.DoubleTheDonationPublicKey) && document.getElementById('dd-container') == null) { 14281 14282 let ddDiv = document.createElement('div'); 14283 ddDiv.id = 'dd-container'; 14284 ddDiv.vanish(); 14285 14286 if (thisApi.HeaderContainerElement != null && thisApi.HeaderContainerElement.parentElement != null) 14287 thisApi.HeaderContainerElement.parentElement.insertAfterElement(ddDiv, thisApi.HeaderContainerElement); 14288 else
14289 _logger.Log('unable to add double the donation widget (no header container)'); 14290 14291 } 14292 else 14293 _logger.Log('skipping double the donation initialization (disabled or already initailized)'); 14294 14295 }; 14296 14297 let _urlAmount = null; 14298 let _urlCurrencyId = null; 14299 let _urlFrequencyId = null; 14300 let _urlPaymentMethodId = null; 14301 14302 let _isUrlAmountProvided = false; 14303 let _isUrlPaymentMethodProvided = false; 14304 14305 const _setFormDefaultValuesFromUrl = function () { 14306 14307 for (let x = 0; x < _allFieldApis.length; x++) { 14308 14309 let fld = _allFieldApis[x]; 14310 let fldConfig = fld.Configuration; 14311 14312 let domIdUrlParam = getURLParameterByName(fldConfig.DomId); 14313 if (!isBlank(domIdUrlParam)) { 14314 14315 _logger.Log('set field ' + fldConfig.DomId + ' default value based on dom id url parameter'); 14316 fld.SetValue(domIdUrlParam); 14317 14318 if (_isGenericAmountInputField(fldConfig)) 14319 _urlAmount = domIdUrlParam.toDecimalZeroIfInvalid(); 14320 14321 //todo: add non-id mapping attempt 14322 if (_isPaymentCurrencyField(fldConfig)) 14323 _urlCurrencyId = domIdUrlParam.toIntZeroIfInvalid(); 14324 14325 //todo: add non-id mapping attempt 14326 if (_isRecurringFrequencyField(fldConfig)) 14327 _urlFrequencyId = domIdUrlParam.toIntZeroIfInvalid(); 14328 14329 //todo: add non-id mapping attempt 14330 if (_isPaymentMethodField(fldConfig)) 14331 _urlPaymentMethodId = domIdUrlParam.toIntZeroIfInvalid(); 14332 14333 } 14334 14335 let nameUrlParam = getURLParameterByName(fldConfig.Name); 14336 if (!isBlank(nameUrlParam)) { 14337 14338 _logger.Log('set field ' + fldConfig.DomId + ' default value based on name url parameter'); 14339 fld.SetValue(nameUrlParam); 14340 14341 if (_isGenericAmountInputField(fldConfig)) 14342 _urlAmount = nameUrlParam.toDecimalZeroIfInvalid(); 14343 14344 //todo: add non-id mapping attempt 14345 if (_isPaymentCurrencyField(fldConfig)) 14346 _urlCurrencyId = nameUrlParam.toIntZeroIfInvalid(); 14347 14348 //todo: add non-id mapping attempt 14349 if (_isRecurringFrequencyField(fldConfig)) 14350 _urlFrequencyId = nameUrlParam.toIntZeroIfInvalid(); 14351 14352 //todo: add non-id mapping attempt 14353 if (_isPaymentMethodField(fldConfig)) 14354 _urlPaymentMethodId = nameUrlParam.toIntZeroIfInvalid(); 14355 14356 } 14357 14358 } 14359 14360 //add special variables - e.g. amount, frequency, paymentmethod, currency 14361 let amountUrlParam = getURLParameterByName('amount'); 14362 if (thisApi.FirstGenericAmountInputField != null && isBlank(_urlAmount) && !isBlank(amountUrlParam)) { 14363 _logger.Log('set payment amount field value from special url parameter'); 14364 thisApi.FirstGenericAmountInputField.SetValue(amountUrlParam); 14365 _urlAmount = amountUrlParam.toDecimalZeroIfInvalid(); 14366 } 14367 14368 _isUrlAmountProvided = (_urlAmount != null && _urlAmount > 0); 14369 14370 let currencyIdUrlParam = getURLParameterByName('currencyId'); 14371 if (thisApi.PaymentCurrencyField != null && isBlank(_urlCurrencyId) && !isBlank(currencyIdUrlParam)) { 14372 _logger.Log('set currency field value from special url parameter'); 14373 thisApi.PaymentCurrencyField.SetValue(currencyIdUrlParam); 14374 _urlCurrencyId = currencyIdUrlParam.toIntZeroIfInvalid(); 14375 } 14376 14377 let recurringFrequencyIdUrlParam = getURLParameterByName('paymentFrequencyId'); 14378 if (thisApi.RecurringPaymentFrequencyField != null && isBlank(_urlFrequencyId) && !isBlank(recurringFrequencyIdUrlParam)) { 14379 _logger.Log('set frequency field value from special url parameter'); 14380 thisApi.RecurringPaymentFrequencyField.SetValue(recurringFrequencyIdUrlParam); 14381 _urlFrequencyId = recurringFrequencyIdUrlParam.toIntZeroIfInvalid(); 14382 } 14383 14384 let paymentMethodIdUrlParam = getURLParameterByName('paymentMethodId'); 14385 if (thisApi.PaymentMethodField != null && isBlank(_urlPaymentMethodId) && !isBlank(paymentMethodIdUrlParam)) {
14386 _logger.Log('set payment method field value from special url parameter'); 14387 thisApi.PaymentMethodField.SetValue(paymentMethodIdUrlParam); 14388 _urlPaymentMethodId = paymentMethodIdUrlParam.toIntZeroIfInvalid(); 14389 } 14390 14391 _isUrlPaymentMethodProvided = (_urlPaymentMethodId != null && _urlPaymentMethodId.toIntZeroIfInvalid() > 0); 14392 14393 }; 14394 14395 const _handleContactPostalCodeLookup = function (result) { 14396 14397 //_logger.Log('postal code lookup: ' + JSON.stringify(result)); 14398 //_logger.Log('result.City: ' + result.City); 14399 //_logger.Log('result.State_Id: ' + result.State_Id); 14400 //_logger.Log('result.Country_Id: ' + result.Country_Id); 14401 14402 let curCityVal = _tryGetFieldValue(thisApi.ContactAddressCityField); 14403 if(curCityVal != result.Result.City) 14404 _trySetFieldValue(thisApi.ContactAddressCityField,result.Result.City); 14405 14406 let curStateVal = _tryGetFieldValue(thisApi.ContactAddressStateProvinceField); 14407 if (curStateVal != result.Result.State_Id) 14408 _trySetFieldValue(thisApi.ContactAddressStateProvinceField,result.Result.State_Id); 14409 14410 let countryVal = _tryGetFieldValue(thisApi.ContactAddressCountryField); 14411 if (result.Result.Country_Id > 0 && countryVal != result.Result.Country_Id) 14412 _trySetFieldValue(thisApi.ContactAddressCountryField,result.Result.Country_Id); 14413 14414 }; 14415 14416 const _handlePromoCodeValidated = function () { 14417 14418 _logger.Log('promo code validated, handle amount impacting change event'); 14419 _updateFormAfterAmountImpactingChange(); 14420 } 14421 14422 const _fireAndForgetPageViewAjaxCall = function () { 14423 14424 let params = { 14425 "form_id": thisApi.Form_Id, 14426 "trans_id": 0, 14427 "pageRequestUrl": window.location.href, 14428 "cas_id": WebFormContext.Cas_Id, 14429 "conversionHit_id": 0, 14430 "overrideRequestMethodId": 1 14431 }; 14432 14433 App.BackgroundPostJsonAsync('/api/WebFormsAppAjaxEndpoint/LogPageRequestAsync', 14434 JSON.stringify(params), 14435 function (result) { _logger.Log('async page request logged'); }, 14436 function (error) { _logger.Log('error logging page request: ' + error); }); 14437 14438 }; 14439 14440 let _isInitialized = false; 14441 let _initializeCompletedPreCallBacks = false; 14442 14443 const _getLoginModalElem = function () { return document.getElementById('formLoginModal'); }; 14444 const _getLoginModalTopMessageElem = function () { return document.getElementById('formLoginModalTopErrorMessage'); } 14445 const _getLoginModalUsernameElem = function () { return document.getElementById('formLoginUserName'); } 14446 const _getLoginModalPassphraseElem = function () { return document.getElementById('formLoginPassphrase'); } 14447 14448 let _formLoginModal = null; 14449 14450 const _openFormLoginModal = function () { 14451 14452 var modalElem = _getLoginModalElem(); 14453 if (modalElem != null) 14454 modalElem.show(); 14455 14456 } 14457 14458 const _closeFormLoginModal = function () { 14459 14460 var modalElem = _getLoginModalElem(); 14461 if (modalElem != null) 14462 modalElem.vanish(); 14463 14464 } 14465 14466 const _addFormLoginModal = function () { 14467 14468 //Add DOM Elements if they do not exist 14469 let loginModal = _getLoginModalElem(); 14470 if (loginModal == null) { 14471 14472 //create container div 14473 loginModal = createAnonymousDomElement('div', 'pageOverlays'); 14474 loginModal.setAttribute('id', 'formLoginModal'); 14475 14476 //add inner div 14477 let modalInnerDiv = createAnonymousDomElement('div', 'appModals'); 14478 14479 //add header title 14480 let headerElem = createAnonymousDomElement('div', 'appModalHeaders'); 14481 14482 let headerTitleTextElem = createAnonymousDomElement('span', 'appModalHeaderHeaderTitles'); 14483 14484 headerTitleTextElem.innerHTML = 'Enter credentials to access site'; 14485 14486 headerElem.appendChild(headerTitleTextElem); //headerTitle 14487 14488 modalInnerDiv.appendChild(headerElem); 14489 14490 //add content container div 14491 let modalUIContainer = createAnonymousDomElement('div', 'ce-formLoginContainer'); 14492 14493 //add error message div 14494 let topErrorMessage = document.createElement('div'); 14495 topErrorMessage.setAttribute('id', 'formLoginModalTopErrorMessage'); 14496 topErrorMessage.vanish(); 14497 14498 modalUIContainer.appendChild(topErrorMessage); 14499 14500 //add content 14501 let unContainer = createAnonymousDomElement('div', 'ce-formLoginRow'); 14502 14503 let unLabel = createAnonymousDomElement('label', 'ce-formLoginFieldLabel', null, 'Username');
14504 unContainer.appendChild(unLabel); 14505 14506 let unInputDiv = createAnonymousDomElement('div', 'ce-formLoginFieldWrapper', null, '<input type="text" id="formLoginUserName" value="" class="ce-textBoxes ce-formLoginFields" placeholder="Username" aria-required="true" aria-invalid="false">') 14507 unContainer.appendChild(unInputDiv); 14508 14509 modalUIContainer.appendChild(unContainer); 14510 14511 let ppContainer = createAnonymousDomElement('div', 'ce-formLoginRow'); 14512 14513 let ppLabel = createAnonymousDomElement('label', 'ce-formLoginFieldLabel', null, 'Passphrase'); 14514 ppContainer.appendChild(ppLabel); 14515 14516 let ppInputDiv = createAnonymousDomElement('div', 'ce-formLoginFieldWrapper', null, '<input type="text" id="formLoginPassphrase" value="" class="ce-textBoxes ce-formLoginFields" placeholder="Passphrase" aria-required="true" aria-invalid="false">') 14517 ppContainer.appendChild(ppInputDiv); 14518 14519 modalUIContainer.appendChild(ppContainer); 14520 14521 modalInnerDiv.appendChild(modalUIContainer); 14522 14523 //add footer div 14524 let footerElem = createAnonymousDomElement('div', 'ce-formLoginFooter'); 14525 14526 //add login button 14527 let buttonCssClass = 'popUpButtons ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only'; 14528 14529 let buttonLabelCssClasses = 'ui-button-text'; 14530 14531 let processButton = createAnonymousDomElement('button', buttonCssClass, null, '<span class="' + buttonLabelCssClasses + '">Login</span>'); 14532 processButton.setAttribute('type', 'button'); //i'm sure both of these are necessary for Jquery 14533 processButton.setAttribute('role', 'button'); //i'm sure both of these are necessary for Jquery 14534 processButton.setAttribute('aria-disabled', 'false'); 14535 processButton.setAttribute('onclick', 'CE.APPS.WebForm.ProcessFormLogin();'); 14536 14537 footerElem.appendChild(processButton); 14538 14539 modalInnerDiv.appendChild(footerElem); 14540 14541 loginModal.appendChild(modalInnerDiv); 14542 14543 document.body.appendChild(loginModal); 14544 14545 loginModal.vanish(); 14546 } 14547 14548 _openFormLoginModal(); 14549 14550 } 14551 14552 const _showFormLoginError = function (message) { 14553 14554 _getLoginModalTopMessageElem().innerHTML = ''; 14555 14556 if (!isBlank(message)) { 14557 14558 _getLoginModalTopMessageElem().show(); 14559 14560 _getLoginModalTopMessageElem().addHtml(message, 'headerDynamicMessageContent'); 14561 14562 } 14563 else 14564 _getLoginModalTopMessageElem().vanish(); 14565 } 14566 14567 const _clearFormLoginError = function () { 14568 _getLoginModalTopMessageElem().innerHTML = ''; 14569 14570 _getLoginModalTopMessageElem().vanish(); 14571 14572 } 14573 14574 thisApi.ProcessFormLogin = function () { 14575 14576 _logger.Log('call WebFormLoginUser'); 14577 _clearFormLoginError(); 14578 14579 let usernameElem = _getLoginModalUsernameElem(); 14580 let passphraseElem = _getLoginModalPassphraseElem(); 14581 14582 if (usernameElem != null && passphraseElem != null) { 14583 14584 let requiredFieldsValidated = true; 14585 14586 usernameElem.classList.remove('error'); 14587 passphraseElem.classList.remove('error'); 14588 14589 if (usernameElem.getValue() == '' && requiredFieldsValidated) { 14590 requiredFieldsValidated = false; 14591 usernameElem.classList.add('error'); 14592 _showFormLoginError('Username is required'); 14593 } 14594 14595 if (passphraseElem.getValue() == '' && requiredFieldsValidated) { 14596 requiredFieldsValidated = false; 14597 passphraseElem.classList.add('error'); 14598 _showFormLoginError('Passphrase is required'); 14599 } 14600 14601 if (requiredFieldsValidated) { 14602 14603 App.PostJsonAsync('/api/PublicWebAppUIAjaxEndpoint/WebFormLoginUser', 14604 JSON.stringify({ form_id: thisApi.Form_Id, username: usernameElem.getValue(), passphrase: passphraseElem.getValue() }), 14605 function (result) { 14606 14607 //console.log('process web form transaction response: ' + result); 14608 14609 if (result.Success) 14610 _closeFormLoginModal(); 14611 else 14612 _showFormLoginError(result.ErrorMessage); 14613 14614 }, 14615 function (error) { 14616 14617 console.log('error: ' + error); 14618 14619 _showFormLoginError(error); 14620 14621 }); 14622 } 14623 } 14624 else 14625 _showFormLoginError('Username and Passphrase are required'); 14626 } 14627 14628 const _enableDynamicLabels = true; 14629 14630 const _initializeDynamicLabelField = function (domElement, fldLbl) { 14631 14632 if (_enableDynamicLabels && domElement != null && fldLbl != null) { 14633 14634 const fldContainerElem = domElement.firstParentWithClass('ce-formFieldContainers'); 14635 if(fldContainerElem != null) { 14636
14637 const fldType = domElement.getInputFieldType(true); 14638 14639 if (fldType == FormInputTypes.TextBox || fldType == FormInputTypes.Password || fldType == FormInputTypes.TextArea || fldType == FormInputTypes.DropDownList) 14640 initializePublicAppFormDynamicLabel(domElement,fldLbl); 14641 14642 } 14643 14644 } 14645 14646 }; 14647 14648 const _initializeDynamicLabelFields = function () { 14649 14650 if (_enableDynamicLabels) { 14651 14652 thisApi.FormElement.addClassName('ce-dynamicLabelsInitialized'); 14653 14654 for (let x = 0; x < _allFieldApis.length; x++) 14655 if (_allFieldApis[x].LabelDomElement != null){ 14656 14657 let isAlwaysVisibleLabel = (_allFieldApis[x].Configuration.Type.Id == FORM_FIELD_TYPES.TICKET_QUANTITY_SYSTEM); 14658 14659 if (!isAlwaysVisibleLabel) 14660 isAlwaysVisibleLabel = (_allFieldApis[x].LabelText != _allFieldApis[x].PlaceholderText); 14661 14662 const lblElem = _allFieldApis[x].DomElement.getAssociatedFieldLabel(); 14663 14664 if (isAlwaysVisibleLabel && lblElem != null) 14665 lblElem.addClassName('ce-alwaysVisibleLabels'); 14666 14667 _initializeDynamicLabelField(_allFieldApis[x].DomElement, lblElem); 14668 14669 } 14670 14671 _logger.Log('finished initializing standard field dynamic labels (' + _allFieldApis.length + ' fields)'); 14672 14673 } 14674 14675 }; 14676 14677 thisApi.Initialize = function () { 14678 14679 if (!_isInitialized) { 14680 14681 _logger.Log('initialize started'); 14682 14683 //check for critical compatibility issues 14684 let isCriticalCompatibilityIssue = _checkForCriticalConfigurationIssues(); 14685 14686 if (!isCriticalCompatibilityIssue) { 14687 14688 if (thisApi.FormElement != null && thisApi.SubmitButtonElement != null) { 14689 14690 _isInitialized = true; 14691 14692 _setDefaultView(); 14693 14694 _setFormDefaultValuesFromUrl(); 14695 14696 thisApi.SubmitButtonElement.addEventListener('click', function () { _handleSubmitClick(); }); 14697 14698 _initializeDynamicLabelFields(); 14699 14700 //wire up interactive validation 14701 for (let x = 0; x < _allFieldApis.length; x++) 14702 _wireUpFieldValueChangeDetectionEvent(_allFieldApis[x].Configuration, _validateFieldElementRelative); 14703 14704 //wire up all amount fields 14705 if (thisApi.GenericAmountInputFields.length > 0) 14706 for (let x = 0; x < thisApi.GenericAmountInputFields.length; x++) 14707 _wireUpFieldValueChangeDetectionEvent(thisApi.GenericAmountInputFields[x].Configuration, _handlePaymentAmountChanged); 14708 14709 _initializePaymentAmountSection(); 14710 14711 //move the button container into place BEFORE Initialize() renders into it - reparenting 14712 //the container after render() causes the PayPal SDK to tear down/destroy the buttons 14713 _movePayPalButtonIntoPaymentMethodSection(); 14714
14715 //_logger.Log('initialize paypal API (' + (typeof thisApi.PayPalApi) + ')'); 14716 if (thisApi.PayPalApi != null && typeof thisApi.PayPalApi != 'undefined' && thisApi.PayPalApi.PayPalEnabled) { 14717 _logger.Log('Initialize paypal native button'); 14718 14719 thisApi.PayPalApi.RegisterTokenReceivedCallBack(_handlePayPalTokenReceived); 14720 thisApi.PayPalApi.RegisterPaymentAuthorizedCallBack(_handlePaypalPaymentAuthorized); 14721 thisApi.PayPalApi.Initialize(); //{PayPalApiVariableName}.ShowButton(); //why do we have to show it? Is this forms specific behavior? 14722 } 14723 14724 //_logger.Log('initialize applepay API (' + (typeof thisApi.ApplePayApi) + ')'); 14725 if (thisApi.ApplePayApi != null && typeof thisApi.ApplePayApi != 'undefined' && thisApi.ApplePayApi.ApplePayEnabled) { 14726 _logger.Log('Initialize applepay native button'); 14727 thisApi.ApplePayApi.RegisterPaymentAuthorizedCallBack(_handleApplePayPaymentAuthorized); 14728 thisApi.ApplePayApi.Initialize(); 14729 } 14730 14731 //handled in script loaded 14732
14733 //_logger.Log('initialize dafpay API (' + (typeof thisApi.DafPayApi) + ')'); 14734 if (thisApi.DafPayApi != null && typeof thisApi.DafPayApi != 'undefined' && thisApi.DafPayApi.DafPayEnabled) { 14735 _logger.Log('Initialize dafpay native button'); 14736 thisApi.DafPayApi.RegisterButtonClickedCallBack(function (cbParams) { _handleDafPayButtonClicked(cbParams); }); 14737 thisApi.DafPayApi.RegisterPaymentAuthorizedCallBack(function (cbParams) { _handleDafPayPaymentAuthorized(cbParams); }); 14738 thisApi.DafPayApi.RegisterPaymentFailedCallBack(function (cbParams) { _handleDafPayPaymentAuthorizationFailed(cbParams); }); 14739 thisApi.DafPayApi.Initialize(); 14740 } 14741 14742 _initializePaymentMethodSection(); 14743 14744 _initializeMembershipSection(); 14745 14746 _initializePledgeSection(); 14747 14748 _initializeContactInfoSection(); 14749 14750 _initializeTributeSection(); 14751 14752 _initializeEventTicketsSection(); 14753 14754 _initializeProducts(); 14755 14756 _initializeVolunteerPostTimeEntryWidget(); 14757 14758 _initializeFileUploadFields(); 14759 14760 //_refreshFreeGiftSectionBasedOnAmountChange(); //called automatically 14761 14762 _initializeDoubleTheDonation(); 14763 14764 //get visible ask strings if present and simulate click to initalize amount and UI 14765 if (!_isUrlAmountProvided) 14766 _simulateVisibleAskBlockClick(); 14767 else 14768 _logger.Log('skip startup simulate ask click because of amount url override'); 14769 14770 if (thisApi.PaymentMethodField != null) { 14771 14772 //set default form values (config, url, business rules 14773 let defPaymentMethodId = thisApi.PaymentMethodField.DefaultIntegerValue; 14774 if (defPaymentMethodId <= 0) 14775 defPaymentMethodId = PAYMENT_METHODS.CREDIT_CARD; 14776 14777 if (_isUrlPaymentMethodProvided) 14778 defPaymentMethodId = _urlPaymentMethodId; 14779 14780 thisApi.PaymentMethodField.SetValue(defPaymentMethodId); 14781 14782 } 14783 14784 //wire up promo code API (todo: add promo code if no field?) 14785 if (thisApi.PromoCodeField != null) { 14786 thisApi.PromoCodeFieldApi = getPromoCodeFormFieldApi({ "AutoInitialize": false, "PromoEntryTextBoxDomId": thisApi.PromoCodeField.Configuration.DomId, "GetTotalAmountFunction": function () { return thisApi.GetPromoCodeApplicableTotalAmount(); }, "GetSelectedTicketQuantitiesFunction": function () { return thisApi.GetTicketQuantities(); } }); 14787 thisApi.PromoCodeFieldApi.Initialize(); 14788 thisApi.PromoCodeFieldApi.RegisterValidationSuccessfulChangedCallBack(_handlePromoCodeValidated); 14789 } 14790 14791 //wire up postal code 14792 if (thisApi.ContactAddressPostalCodeField != null) { 14793 //NOTE: relaying on event call back instead of field mapping so we can use internal APIs for cascading functionality - i.e., SetValue of city triggers future client event listener 14794 thisApi.ContactPostalCodeFieldApi = getPostalCodeLookupFormFieldApi({ "AutoInitialize": false, "PostalCodeEntryTextBoxDomId": thisApi.ContactAddressPostalCodeField.Configuration.DomId }); 14795 thisApi.ContactPostalCodeFieldApi.RegisterLookupSuccessfulCallBack(_handleContactPostalCodeLookup); 14796 thisApi.ContactPostalCodeFieldApi.Initialize(); 14797 } 14798 14799 //NOTE: google maps gets injected in via script load event (calls google maps script loaded initializer) 14800 // thisApi.HandleGoogleMapsScriptLoaded(); 14801 14802 //TODO: this is already in context, just use if not returned from identify visitor 14803 //just need getters. 14804 _initializeVisitorAnonymousContextInfo(); 14805 14806 //DEBUGGING (COMMENT to SPEED UP DEBUGGING - REMOVE COMMENT BEFORE DEPLOYING) 14807 //DEBUGGING (COMMENT to SPEED UP DEBUGGING - REMOVE COMMENT BEFORE DEPLOYING) 14808 //DEBUGGING (COMMENT to SPEED UP DEBUGGING - REMOVE COMMENT BEFORE DEPLOYING) 14809 14810 //allow disabling 14811 if(WebFormContext.Form.EnableContactIdentification) 14812 _initializeKnownVisitorContext(); 14813 14814 if (thisApi.PaymentMethodField != null) thisApi.PaymentMethodField.HideOptionByValue(PAYMENT_METHODS.ONECLICK_DONATION); //hide one click in the payment methods no matter what (it is fake) 14815 14816 if (!_payPalPaymentMethodAvailable()) 14817 thisApi.PayPalApi.HideButton(); 14818
14819 if (!_creditCardPaymentMethodAvailable()) { 14820 _tryHideGooglePayButton(); 14821 _tryHideApplePayButton(); 14822 } 14823 14824 if (!thisApi.DafPayPaymentMethodAvailable) 14825 thisApi.DafPayApi.HideButton(); 14826 14827 //debugging (force one-click on) 14828 //****************************** 14829 //_oneClickOptContactIn(1); //_initializeOneClickCheckout(1, 2, 'credit card that ends with 5011'); 14830 //****************************** 14831 14832 _updateFormAfterAmountImpactingChange(); 14833 14834 if (WebFormContext.Form.RequireLogin) 14835 _addFormLoginModal(); 14836 14837 //wireup form ready event (IMPORTANT: need theme to comply with calling new intialized event or race conditions can occur - _isThemeInitialized) 14838 if (thisApi.ThemeRunning && !_isThemeInitialized) { 14839 14840 _logger.Log('register theme initialized call back to delegate ready to theme'); 14841 thisApi.RegisterThemeInitializedCallBack(function () { _fireFormReadyEvent(); }); 14842 14843 } 14844 else { 14845 14846 _logger.Log('fire ready event (no theme or theme initialized)'); 14847 _fireFormReadyEvent(); 14848 14849 } 14850 14851 _fireAndForgetPageViewAjaxCall(); 14852 14853 _initializeCompletedPreCallBacks = true; 14854 14855 _fireFormInitializedEvent(); //fire custom events after ready (users should use ready, not initialized) 14856 14857 } 14858 else 14859 _logger.Log('initialization failed (form element and submit button cannot be found)'); 14860 14861 } 14862 else 14863 { 14864 14865 //TODO: we still need to consider the idea that there's one mapped payment field - might be solid, might not 14866 _logger.Log('initialization failed (configuration - check warning above and fix form settings)'); 14867 14868 let errorMessage = 'Form is not confingured properly<br />'; 14869 14870 for (let x = 0; x < _criticalConfigurationMessages.length; x++) 14871 errorMessage += '<br />' + _criticalConfigurationMessages[x]; 14872 14873 _setErrorView('Critical Configuration Error', errorMessage); 14874 14875 _hideAllSections(); 14876 14877 _hideAllPageContentElements(); 14878 14879 thisApi.HideSubmitButton(); 14880 14881 } 14882 14883 } 14884 else 14885 _logger.Log('initialization skipped (already initialized)'); 14886 14887 } 14888 14889 if (thisApi.AutoInitialize) { 14890 14891 document.addEventListener('DOMContentLoaded', function () { thisApi.Initialize(); }); 14892 14893 } 14894 14895 return thisApi; 14896 14897} 14898 14899 14900/*App Core UI Apis*/ 14901 14902function getCeAppsWebFormsApi() { 14903 14904 let thisApi = {}; 14905 14906 const _logger = getConsoleLogApi({ "LogSourceName": 'CE Apps Web Form Api' }); 14907 14908 const _webFormApis = []; 14909 14910 const _isFormApiRegistered = function (api) { 14911 14912 let retVal = false; 14913 14914 for (let x = 0; x < _webFormApis.length; x++) 14915 if (_webFormApis[x].Form_Id == api.Form_Id) { 14916 retVal = true; 14917 break; 14918 } 14919 14920 return retVal; 14921 14922 }; 14923 14924 thisApi.RegisterFormApi = function (api) { 14925 14926 if (!_isFormApiRegistered(api)) 14927 _webFormApis.push(api); 14928 else 14929 _logger.Log('web form ' + api.Form_Id + ' already registered'); 14930 14931 }; 14932 14933 thisApi.GetFormApi = function () { 14934 14935 let retVal = null; 14936 14937 if (_webFormApis.length > 1) 14938 _logger.Log('WARNING: Web Form Api referenced with form when more than one form present on page (use GetWebFormById to get a specific form)'); 14939 14940 _logger.Log('get web form API: ' + _webFormApis.length); 14941 if (_webFormApis.length > 0) 14942 retVal = _webFormApis[0]; 14943 14944 return retVal; 14945 14946 }; //gets first form 14947 14948 //simulate property for function with no parameters
14949 Object.defineProperty(thisApi, 'Form', { 14950 get: function() { 14951 return thisApi.GetFormApi(); 14952 } 14953 }); 14954 14955 thisApi.GetFormApiById = function(form_id) { 14956 14957 let retVal = null; 14958 14959 for (let x = 0; x < _webFormApis.length; x++) 14960 if (_webFormApis[x].Form_Id == form_id) { 14961 retVal = _webFormApis[x]; 14962 break; 14963 } 14964 14965 return retVal; 14966 14967 }; 14968 14969 thisApi.InitializeAll = function() { 14970 14971 for (let x = 0; x < _webFormApis.length; x++) 14972 _webFormApis[x].Initialize(); 14973 14974 }; 14975 14976 return thisApi; 14977 14978} 14979 14980function getCeAppsApi() { 14981 14982 let thisApi = {}; 14983 14984 const _logger = getConsoleLogApi({ "LogSourceName": 'CE Apps Api' }); 14985 14986 thisApi.WebForms = getCeAppsWebFormsApi(); 14987 14988 //daisy-chained getter for single form implementations (UX) 14989 Object.defineProperty(thisApi, 'WebForm', { 14990 get: function() { 14991 return thisApi.WebForms.Form; 14992 } 14993 }); 14994 14995 thisApi.ProcessWebFormTransaction = function (transactionPayLoad, successCallBack, errorCallBack) { 14996 14997 _logger.Log('call ProcessWebFormTransaction'); 14998 14999 App.PostJsonAsync('/api/PublicWebAppUIAjaxEndpoint/ProcessWebFormTransaction', 15000 JSON.stringify(transactionPayLoad), 15001 function (result) { 15002 15003 //console.log('process web form transaction response: ' + result); 15004 15005 if (typeof successCallBack == 'function') 15006 successCallBack(result); 15007 15008 }, 15009 function (error) { 15010 15011 console.log('error: ' + error); 15012 15013 if (typeof errorCallBack == 'function') 15014 errorCallBack(error); 15015 15016 }); 15017 15018 }; 15019 15020 thisApi.Initialize = function () { 15021 15022 _logger.Log('initialize started'); 15023 15024 thisApi.WebForms.InitializeAll(); 15025 15026 }; 15027 15028 return thisApi; 15029 15030} 15031 15032function getCeApi() { 15033 15034 const thisApi = {}; 15035 15036 thisApi.APPS = getCeAppsApi(); 15037 15038 //case-insensitive getter 15039 Object.defineProperty(thisApi, 'Apps', { 15040 get: function() { 15041 return thisApi.APPS; 15042 } 15043 }); 15044 15045 return thisApi; 15046 15047} 15048 15049function getNavigationApi(config) { 15050 15051 const thisApi = {}; 15052 15053 let _applicationType_id = AppTypes.Unknown; 15054 let _applicationEnvironment_id = AppEnvironments.Prod; 15055 15056 if (config != null) { 15057 15058 if (config.ApplicationType_Id != null) 15059 _applicationType_id = config.ApplicationType_Id; 15060 15061 if (config.ApplicationEnvironment_Id != null) 15062 _applicationEnvironment_id = config.ApplicationEnvironment_Id; 15063 15064 } 15065 15066 const _logger = getConsoleLogApi({ "LogSourceName": 'CE App Navigation Api' }); 15067 15068 const _getCrmBaseUrl = function () { 15069 15070 let baseUrl = 'https://admin.charityengine.net'; 15071 15072 switch (_applicationEnvironment_id) { 15073 case AppEnvironments.Dev: 15074 baseUrl = 'https://localhost:44355'; 15075 break; 15076 } 15077 15078 return baseUrl; 15079 15080 }; 15081 15082 thisApi.GoToUrl = function(url, suppressProcessingMessage){ 15083 15084 if (!suppressProcessingMessage) 15085 App.UI.ProcessingOverlay.Show(); 15086 15087 window.location.href = url; 15088 15089 }; 15090 15091 thisApi.ReloadCurrentPage = function (suppressProcessingMessage) { thisApi.GoToUrl(window.location.href, suppressProcessingMessage); }; 15092 15093 const _tryOpenUrl = function (url) { 15094 15095 if (_applicationType_id == AppTypes.OutlookConnector || _applicationType_id == AppTypes.OutlookConnector) { 15096 15097 try { 15098 15099 Office.context.ui.openBrowserWindow(url); 15100 15101 } 15102 catch (e) { 15103 15104 _logger.Log('unable to open url ' + url + ' using office add-in api - fail back to native: ' + e); 15105 15106 openUrlInNewBrowserTab(url); 15107 } 15108 15109 } 15110 else 15111 openUrlInNewBrowserTab(url); 15112 15113 }; 15114 15115 thisApi.OpenCrmActivity = function (activity_id) { 15116 _tryOpenUrl(_getCrmBaseUrl() + '/dsp_crm_contactActivity.aspx?activityid=' + activity_id); 15117 }; 15118 15119 thisApi.OpenCrmTransaction = function (trasaction_id) { 15120 _tryOpenUrl(_getCrmBaseUrl() + '/dsp_ts_transaction.aspx?tid=' + trasaction_id); 15121 }; 15122 15123 thisApi.OpenCrmContact = function (contact_id) { 15124 _tryOpenUrl(_getCrmBaseUrl() + '/dsp_crm_contact.aspx?contid=' + contact_id); 15125 }; 15126 15127 thisApi.OpenCrmNote = function (note_id) { 15128 _tryOpenUrl(_getCrmBaseUrl() + '/dsp_crm_contactNotes.aspx?notesid=' + note_id); 15129 }; 15130 15131 thisApi.OpenQuery = function (query_id) { 15132 _tryOpenUrl(_getCrmBaseUrl() + '/dsp_ce_queryBuilder.aspx?reportID=' + query_id); 15133 }; 15134 15135 thisApi.OpenCase = function (case_id) { 15136 _tryOpenUrl(_getCrmBaseUrl() + '/dsp_sc_ticket.aspx?sc_tkid=' + case_id); 15137 }; 15138 15139 thisApi.OpenOpportunity = function (opportunity_id) { 15140 _tryOpenUrl(_getCrmBaseUrl() + '/dsp_crm_opportunity.aspx?opid=' + opportunity_id); 15141 }; 15142 15143 return thisApi; 15144 15145} 15146
15147function getUIControlsApi(config) { 15148 15149 const thisApi = {}; 15150 15151 let _logger = getConsoleLogApi({ "LogSourceName": 'CE UI Controls Api' }); 15152 15153 thisApi.CreateFormRowElement = function(domId, classes) { 15154 15155 let classVal = 'formRows'; 15156 15157 if (!isBlank(classes)) 15158 classVal += ' ' + classes; 15159 15160 return createDomElement('div', domId, classVal); 15161 15162 }; 15163 15164 thisApi.CreateCenteredFormRowElement = function(domId, classes) { 15165 15166 return thisApi.CreateFormRowElement(domId, 'centered'); 15167 15168 }; 15169 15170 thisApi.CreateLink = function(text, url, newTab, domId, classes, tooltip) { 15171 return createHyperLink(text, url, classes, null, tooltip, domId, newTab); 15172 }; 15173 15174 thisApi.CreateJavascriptLink = function (text, javascriptFunction, domId, classes, tooltip) { 15175 15176 const hl = createHyperLink(text, 'javascript:void(0)', classes, null, tooltip, domId); 15177 15178 hl.addEventListener('click', function () { javascriptFunction(); }); 15179 15180 return hl; 15181 15182 }; 15183 15184 thisApi.AddSingleFieldFormRow = function(form, newElem, labelHtml, rowDomId, rowClasses) { 15185 15186 const newRow = thisApi.CreateFormRowElement(rowDomId, rowClasses); 15187 15188 if (!isBlank(labelHtml)) 15189 newRow.addFormFieldLabel(newElem.id, labelHtml); 15190 15191 newRow.appendChild(newElem); 15192 15193 form.appendChild(newRow); 15194 15195 }; 15196 15197 thisApi.AddSingleFieldCenteredFormRow = function(form, newElem, rowDomId, rowClasses) { 15198 15199 let rowClassVal = 'centered'; 15200 15201 if (!isBlank(rowClasses)) 15202 rowClassVal += ' ' + rowClasses; 15203 15204 return thisApi.AddSingleFieldFormRow(form, newElem, null, rowDomId, rowClassVal); 15205 15206 }; 15207 15208 const _initializeToggleOnOffCheckBox = function (checkBoxElem) { 15209 15210 if (checkBoxElem != null && !checkBoxElem.hasParentWithClass('toggleOnOffCheckBoxes')) { 15211 15212 const checkboxDomId = checkBoxElem.id; 15213 15214 if (!isBlank(checkBoxElem.id)) { 15215 15216 const newWrap = checkBoxElem.wrapElementInNewElement('div', 'toggleOnOffCheckBoxes'); 15217 15218 if (newWrap != null) { 15219 15220 const lbl = document.createElement('label'); 15221 lbl.setAttribute('for', checkBoxElem.id); 15222 15223 newWrap.appendChild(lbl); 15224 15225 } 15226 else 15227 _logger.Log('failed to initialize on/off toggle checkbox (critical dependency failed)'); 15228 15229 } 15230 else 15231 _logger.Log('failed to initialize on/off toggle checkbox (dom id requried)'); 15232 15233 } 15234 15235 }; 15236 15237 const _initializeToggleOnOffCheckBoxes = function () { 15238 15239 //TODO: might want paramter or configuration to scope document to app UI 15240 const cbxLst = document.querySelectorAll('input[type="checkbox"]'); 15241 for (let x = 0; x < cbxLst.length; x++) { 15242 15243 const cb = cbxLst[x]; 15244 15245 if (cb.parentNode != null && !cb.hasClassName('noToggle') && !cb.hasParentWithClass('noToggle')) { 15246 15247 //check to make sure its a single checkbox (we don't want to turn a seris of 5 checkbox/labels into toggle - think check all the options that apply) 15248 let parentCheckboxCount = cb.parentNode.querySelectorAll('input[type="checkbox"]').length 15249 15250 let grandParentCheckboxCount = 0; //belt and suspenders for div wrapped. 15251 15252 if (cb.parentNode.parentNode != null) 15253 grandParentCheckboxCount = cb.parentNode.parentNode.querySelectorAll('input[type="checkbox"]').length; 15254 15255 if (parentCheckboxCount == 1 && (cb.parentNode.hasClassName('formRows') || grandParentCheckboxCount == 1)) 15256 _initializeToggleOnOffCheckBox(cb); 15257 15258 } 15259 15260 } 15261 15262 }; 15263 15264 //Initialize 15265 thisApi.Initialize = function () { 15266 15267 _logger.Log('initialize started'); 15268 15269 _initializeToggleOnOffCheckBoxes(); 15270 15271 }; 15272 15273 return thisApi; 15274 15275} 15276 15277function getUIValidationApi(config) { 15278 15279 const thisApi = {}; 15280 15281 let _logger = getConsoleLogApi({ "LogSourceName": 'CE UI Validation Api' });
15282 15283 thisApi.ValidateForm = function (formElementOrSection) { 15284 15285 App.UI.Messages.ClearMessages(); 15286 15287 const validationResp = validateFormFields(formElementOrSection); 15288 15289 const validated = (validationResp.InvalidFields.length <= 0); 15290 15291 if (!validated) { 15292 15293 let errMess = ''; 15294 15295 if (validationResp.InvalidFields.length == 1) { 15296 15297 const invalidFld = validationResp.InvalidFields[0]; 15298 15299 errMess = getFieldValidationMessageOrLabel(invalidFld); 15300 15301 if (invalidFld.id == 'setupNewAccountPassword') { 15302 15303 const valResp = evaluateUserPassword(invalidFld.getValue()); 15304 errMess = valResp.Reason; 15305 15306 } 15307 15308 } 15309 15310 if (isBlank(errMess)) 15311 errMess = 'Review all of your information for errors'; 15312 15313 App.UI.Messages.SetError(errMess); 15314 15315 } 15316 15317 return validated; 15318 15319 }; 15320 15321 return thisApi; 15322 15323} 15324 15325function getDistributedAppApi(config) { 15326 15327 const thisApi = {}; 15328 15329 thisApi.UI = {}; 15330 15331 thisApi.LocalStorage = getLocalStorageApi(); 15332 15333 thisApi.Currencies = getCurrencyApi(); 15334 15335 thisApi.Countries = getCountriesApi(); 15336 thisApi.StateProvinces = getStateProvincesApi(); 15337 15338 let uiMessagesElementDomId = config.MainContainerElementDomId; 15339 15340 //allow override 15341 if (!isBlank(config.UserMessagesContainerElementDomId)) 15342 uiMessagesElementDomId = config.UserMessagesContainerElementDomId; 15343 15344 thisApi.UI.Messages = getAppUIUserMessagesApi({ "MainContainerElementDomId": uiMessagesElementDomId }); 15345 15346 thisApi.UI.ConfirmActionPopUp = getAppUIConfirmActionPopUpApi(); 15347 15348 thisApi.UI.ProcessingOverlay = getAppUIProcessingOverlayApi(); 15349 15350 thisApi.UI.Controls = getUIControlsApi(); 15351 15352 thisApi.Navigation = getNavigationApi({ ApplicationType_Id: config.ApplicationType_Id, ApplicationEnvironment_Id: config.ApplicationEnvironment_Id }); 15353 15354 thisApi.Initialize = function () { 15355 15356 thisApi.UI.Messages.Initialize(); 15357 thisApi.UI.ConfirmActionPopUp.Initialize(); 15358 thisApi.UI.ProcessingOverlay.Initialize(); 15359 thisApi.UI.Controls.Initialize(); 15360 15361 //TODO: add ready event for apps to listen to instead of DOMLoad 15362 if (typeof config.InitializedCallBack == 'function') 15363 config.InitializedCallBack(); 15364 15365 }; 15366 15367 return thisApi; 15368 15369} 15370 15371function getAppApi(config) { 15372 15373 const thisApi = {}; 15374 15375 const _logger = getConsoleLogApi({ "LogSourceName": 'CE App Api' }); 15376 15377 thisApi.UI = {}; 15378 15379 thisApi.WebAppVisitorContextHeaderKeyName = config.WebAppVisitorContextHeaderKeyName; 15380 15381 thisApi.LocalStorage = getLocalStorageApi(); 15382 15383 if(config.RequestValidationConfig != null) 15384 thisApi.RequestValidation = getRequestValidationApi(config.RequestValidationConfig); 15385 else 15386 _logger.Log('Request validation config not present (dependent functions will not work)'); 15387 15388 if (config.CaptchaConfig != null) 15389 thisApi.Captcha = getGoogleCaptchaApi(config.CaptchaConfig); 15390 else 15391 _logger.Log('Captcha config not present (dependent functions will not work)'); 15392 15393 thisApi.Currencies = getCurrencyApi(); 15394 15395 thisApi.Countries = getCountriesApi(); 15396 thisApi.StateProvinces = getStateProvincesApi(); 15397 15398 thisApi.InKindTypes = getInKindTypesApi(); 15399 15400 let uiMessagesElementDomId = config.MainContainerElementDomId; 15401 15402 //allow override 15403 if (!isBlank(config.UserMessagesContainerElementDomId)) 15404 uiMessagesElementDomId = config.UserMessagesContainerElementDomId; 15405 15406 thisApi.UI.Messages = getAppUIUserMessagesApi({"MainContainerElementDomId": uiMessagesElementDomId}); 15407 15408 thisApi.UI.Validation = getUIValidationApi(); 15409 15410 thisApi.UI.ProcessingOverlay = getAppUIProcessingOverlayApi(); 15411 15412 thisApi.Navigation = getNavigationApi(); 15413 15414 thisApi.UI.CaptchaMfaModal = getCaptchaMfaModalApi(); 15415 15416 thisApi.RegisterInitializedCallBack = function (callBackFunction) { 15417 15418 CallBackManager.add('appApiInitializedCallBack', callBackFunction, null); 15419 15420 }; 15421 15422 const _fireInitializedEvent = function () { 15423 15424 CallBackManager.fire('appApiInitializedCallBack', {}, null); 15425 15426 }; 15427 15428 const _isNonNativeAppPostAjaxSecurityProtocolUrl = function(url) { 15429 return url.containsValue('/GenerateRequestToken'); 15430 }; 15431 15432 const _tryRefreshAppToken = function(successCallBack) { 15433 15434 if (typeof App != 'undefined' && typeof App.RequestValidation != 'undefined') 15435 App.RequestValidation.RefreshToken(successCallBack, successCallBack); //RefreshToken tries to hide processing message (passing both call backs - success + error becuase we just want to know its done) 15436 else { 15437 15438 if (typeof successCallBack == 'function') 15439 successCallBack(); 15440 15441 } 15442 15443 }; 15444 15445 const _tryRefreshCaptchaToken = function(successCallBack) { 15446 15447 if (typeof App != 'undefined' && typeof App.Captcha != 'undefined') 15448 App.Captcha.RefreshCaptchaToken(successCallBack, successCallBack); //RefreshCaptchaToken tries to hide processing message (passing both call backs - success + error becuase we just want to know its done) 15449 else { 15450 15451 if (typeof successCallBack == 'function') 15452 successCallBack(); 15453 15454 } 15455 15456 }; 15457 15458 const _handleNativeAppPostAjaxRequestSecurityProtocols = function(successCallBack) { 15459 15460 _tryRefreshAppToken( 15461 function () { 15462 15463 _tryRefreshCaptchaToken(function () { 15464 15465 hideProcessingMessage(); //always (no harm) 15466 15467 if (typeof successCallBack == 'function') 15468 successCallBack(); 15469 15470 }); 15471 15472 }); 15473 15474 } 15475 15476 thisApi.GetUrlAsync = function (url, successCallback, failureCallback) { 15477 15478 showProcessingMessage(); 15479 15480 getUrlWithSessionAsync(url, 15481 function (result) { 15482 15483 let isBaseSecurityUrl = _isNonNativeAppPostAjaxSecurityProtocolUrl(url); 15484 if (!isBaseSecurityUrl) 15485 _handleNativeAppPostAjaxRequestSecurityProtocols(function () { if(typeof successCallback == 'function') successCallback(result); }); 15486 else 15487 successCallback(result); 15488 15489 }, 15490 function (error) { 15491 15492 let isBaseSecurityUrl = _isNonNativeAppPostAjaxSecurityProtocolUrl(url); 15493 if (!isBaseSecurityUrl) 15494 _handleNativeAppPostAjaxRequestSecurityProtocols(function () { if(typeof failureCallback == 'function') failureCallback(error); }); 15495 else 15496 failureCallback(error); //todo: need to parse WCF responses similar to success 15497 15498 }); 15499 15500 }; 15501 15502 thisApi.ProcessAjaxWithCaptchaFailover = function (ajaxFunction, captchaFailedPhoneNumber,
15502captchaFailedEmail, captchaFailedButtonText) { 15503 15504 showProcessingMessage(); 15505 15506 getUrlWithSessionAsync('/api/PublicWebAppUIAjaxEndpoint/ValidateCaptcha', 15507 function (result) { 15508 15509 //console.log('ValidateCaptcha response: ' + JSON.stringify(result)); 15510 15511 _handleNativeAppPostAjaxRequestSecurityProtocols( 15512 function () { 15513 15514 if (result.Success) 15515 ajaxFunction(); 15516 else if (result.CaptchaFailed) { 15517 15518 App.UI.CaptchaMfaModal.Open(function () { ajaxFunction(); }); 15519 15520 //only set if null to allow blank to disable 15521 if (captchaFailedPhoneNumber == null) 15522 captchaFailedPhoneNumber = App.LocalStorage.GetValue('captcha_mfa_last_phone_number'); 15523 15524 if (!isBlank(captchaFailedPhoneNumber)) 15525 App.UI.CaptchaMfaModal.SetPhoneNumber(captchaFailedPhoneNumber); 15526 15527 //only set if null to allow blank to disable 15528 if (captchaFailedEmail == null) 15529 captchaFailedEmail = App.LocalStorage.GetValue('captcha_mfa_last_email_address'); 15530 15531 if (!isBlank(captchaFailedEmail)) 15532 App.UI.CaptchaMfaModal.SetEmail(captchaFailedEmail); 15533 15534 if (!isBlank(captchaFailedPhoneNumber) || isBlank(captchaFailedEmail)) 15535 App.UI.CaptchaMfaModal.SelectPhone(); 15536 else 15537 App.UI.CaptchaMfaModal.SelectEmail(); 15538 15539 App.UI.CaptchaMfaModal.SetSendCodeButtonText(captchaFailedButtonText); //defaults to complete transaction if blank 15540 15541 if (!isBlank(captchaFailedPhoneNumber) || !isBlank(captchaFailedEmail)) 15542 App.UI.CaptchaMfaModal.RequestCode(); 15543 15544 } 15545 else 15546 _logger.Log('validate captcha failed with non captcha error (make sure app is configured properly!!!)'); 15547 15548 }); 15549 15550 }, 15551 function (error) { 15552 _handleNativeAppPostAjaxRequestSecurityProtocols(); 15553 _logger.Log('validate captcha failed with error: ' + error); 15554 }); 15555 15556 }; 15557 15558 thisApi.PostJsonAsync = function (url, data, successCallback, failureCallback, dontShowProcessingMessage) { 15559 15560 if (!dontShowProcessingMessage) 15561 showProcessingMessage(); 15562 15563 //temporary solution to test porting to webapi from WCF 15564 //NOTE: WCF eloquently handles ajaxParams --> complex ajax / WebApi requires top-level object, not embedded 15565 data = tryParseAjaxJsonPayloadData(data, url); 15566 15567 postJsonDataWithSessionAsync(url, data, 15568 function (result) { 15569 15570 let isBaseSecurityUrl = _isNonNativeAppPostAjaxSecurityProtocolUrl(url); 15571 if (!isBaseSecurityUrl) 15572 _handleNativeAppPostAjaxRequestSecurityProtocols(function () { if(typeof successCallback == 'function') successCallback(result); }); 15573 else 15574 successCallback(result); 15575 15576 }, 15577 function (httpResponseObject, requestUrl) { 15578 15579 _logger.Log('critical error in PostJsonAsync (' + requestUrl + '):' + httpResponseObject); 15580 15581 if(typeof failureCallback != 'function') 15582 thisApi.UI.Messages.SetError('critical error in PostJsonAsync (see log for details)'); 15583 15584 let isBaseSecurityUrl = _isNonNativeAppPostAjaxSecurityProtocolUrl(url); 15585 if (!isBaseSecurityUrl) 15586 _handleNativeAppPostAjaxRequestSecurityProtocols(function () { if(typeof failureCallback == 'function') failureCallback(httpResponseObject); }); 15587 else 15588 failureCallback(httpResponseObject); //todo: need to parse WCF responses similar to success 15589 15590 }); 15591 15592 }; 15593
15594 thisApi.BackgroundPostJsonAsync = function (url, data, successCallback, failureCallback) { 15595 15596 return thisApi.PostJsonAsync(url, data, successCallback, failureCallback, true); 15597 15598 }; 15599 15600 thisApi.Initialize = function () { 15601 15602 _logger.Log('initialize started'); 15603 15604 thisApi.UI.Messages.Initialize(); 15605 thisApi.UI.ProcessingOverlay.Initialize(); 15606 15607 if (typeof thisApi.RequestValidation != 'undefined') 15608 thisApi.RequestValidation.Initialize(); 15609 15610 if (typeof grecaptcha != 'undefined') 15611 grecaptcha.ready(function () { thisApi.Captcha.Initialize(); }); 15612 else 15613 _logger.Log('App Api initialization skipping google captcha because third-party api is not available'); 15614 15615 //TODO: add ready event for apps to listen to instead of DOMLoad 15616 if (typeof config.InitializedCallBack == 'function') 15617 config.InitializedCallBack(); 15618 15619 _fireInitializedEvent(); 15620 15621 }; 15622 15623 return thisApi; 15624 15625} 15626 15627function getAppUIModalApi(config) { 15628 15629 const apiObj = {}; 15630 15631 apiObj.ModalContainerElementId = ''; 15632 15633 if(!isBlank(config.ModalContainerElementId)) 15634 apiObj.ModalContainerElementId = config.ModalContainerElementId; 15635 15636 //core api helpers 15637 apiObj.GetContainerElement = function () { return document.getElementById(apiObj.ModalContainerElementId); }; 15638 15639 apiObj.GetInnerContainerElement = function () { 15640 15641 let retVal = null; 15642 15643 let rootElem = this.GetContainerElement(); 15644 if (rootElem != null && rootElem.firstElementChild != null && rootElem.firstElementChild.hasClassName('appModals')) 15645 retVal = rootElem.firstElementChild; 15646 15647 return retVal; 15648 15649 }; 15650 15651 let _getModalPrimaryContainerByClassName = function (className) { 15652 15653 let retVal = null; 15654 15655 if (!isBlank(className)) { 15656 15657 //must be direct descendent to avoid nested control 15658 let innerContainer = apiObj.GetInnerContainerElement(); 15659 if (innerContainer != null && innerContainer.children.length > 0) 15660 for (let x = 0; x < innerContainer.children.length; x++) 15661 if (innerContainer.children[x].hasClassName(className)) { 15662 retVal = innerContainer.children[x]; 15663 break; 15664 } 15665 15666 } 15667 15668 return retVal; 15669 15670 }; 15671 15672 apiObj.GetHeaderElement = function () { 15673 15674 let retVal = _getModalPrimaryContainerByClassName('appModalHeaders'); //must be direct descendent to avoid nested control 15675 15676 return retVal; 15677 15678 }; 15679 15680 apiObj.GetMainUIElement = function () { 15681 15682 let retVal = _getModalPrimaryContainerByClassName('appModalUIContainers'); //must be direct descendent to avoid nested control 15683 15684 return retVal; 15685 15686 }; 15687 15688 apiObj.ShowMainUI = function () { if (apiObj.GetMainUIElement() != null) apiObj.GetMainUIElement().show(); }; 15689 apiObj.HideMainUI = function () { if (apiObj.GetMainUIElement() != null) apiObj.GetMainUIElement().vanish(); }; 15690 15691 apiObj.GetFooterElement = function () { 15692 15693 let retVal = _getModalPrimaryContainerByClassName('appModalFooters'); //must be direct descendent to avoid nested control 15694 15695 return retVal; 15696 15697 }; 15698 15699 apiObj.GetTitleElement = function () { 15700 15701 let retVal = null; 15702 15703 if (apiObj.GetHeaderElement() != null && apiObj.GetHeaderElement().querySelector('span.appModalHeaderTitles') != null) 15704 retVal = apiObj.GetHeaderElement().querySelector('span.appModalHeaderTitles'); 15705 15706 return retVal; 15707 15708 }; 15709 15710 apiObj.SetTitle = function (title) { 15711 15712 if (apiObj.GetTitleElement() != null) 15713 apiObj.GetTitleElement().innerHTML = title; 15714 15715 }; 15716 15717 apiObj.SetContent = function (html) { 15718 15719 if (apiObj.GetMainUIElement() != null) 15720 apiObj.GetMainUIElement().innerHTML = html; 15721 15722 }; 15723 15724 apiObj.ShowFooterBar = function () { if (apiObj.GetFooterElement() != null) apiObj.GetFooterElement().show(); }; 15725 apiObj.HideFooterBar = function () { if (apiObj.GetFooterElement() != null) apiObj.GetFooterElement().vanish(); }; 15726 15727 apiObj.GetContinueButtonElement = function () { 15728 15729 let retVal = null; 15730 15731 if (apiObj.GetFooterElement() != null && apiObj.GetFooterElement().querySelector('button.appModalFooterContinueButtons') != null) 15732 retVal = apiObj.GetFooterElement().querySelector('button.appModalFooterContinueButtons'); 15733 15734 return retVal; 15735 15736 }; 15737 15738 apiObj.SetContinueButtonText = function (text) { 15739 15740 if (apiObj.GetContinueButtonElement() != null && apiObj.GetContinueButtonElement().querySelector('.ui-button-text') != null) 15741 apiObj.GetContinueButtonElement().querySelector('.ui-button-text').innerText = text; 15742 15743 }; 15744 15745 apiObj.SetContinueButtonHtml = function (html) { 15746 15747 if (apiObj.GetContinueButtonElement() != null) 15748 apiObj.GetContinueButtonElement().innerHTML = html; 15749 15750 }; 15751 15752 apiObj.UpdateContinueButtonFunction = function (jsFunction) { 15753 15754 apiObj.Initialize(); 15755 15756 let processButton = apiObj.GetContinueButtonElement(); 15757 15758 if (processButton != null && typeof (jsFunction) == 'function') { 15759 processButton.removeAttribute("onclick"); 15760 processButton.addEventListener('click', function () { jsFunction(); }); 15761 } 15762 15763 } 15764 15765 apiObj.SetInitialUIState = function () { 15766 15767 apiObj.HideFooterCloseButton(); 15768 apiObj.ShowContinueButton(); 15769 15770 //show direct children groups (assumption is that there is a main UI contianer or two) 15771 let uiTopContainers = apiObj.GetMainUIElement().querySelectorAll(':scope > .formFieldRowGroupContainers'); 15772 for (let x = 0; x < uiTopContainers.length; x++) 15773 uiTopContainers[x].show(); 15774 15775 }; 15776
15777 apiObj.SetActionCompleteUIState = function () { 15778 15779 apiObj.ShowFooterCloseButton(); 15780 apiObj.HideContinueButton(); 15781 15782 //hide direct children groups (assumption is that there is a main UI contianer or two) 15783 let uiTopContainers = apiObj.GetMainUIElement().querySelectorAll(':scope > .formFieldRowGroupContainers'); 15784 for (let x = 0; x < uiTopContainers.length; x++) 15785 uiTopContainers[x].vanish(); 15786 15787 }; 15788 15789 apiObj.ShowContinueButton = function () { if (apiObj.GetContinueButtonElement() != null) apiObj.GetContinueButtonElement().show(); }; 15790 apiObj.HideContinueButton = function () { if (apiObj.GetContinueButtonElement() != null) apiObj.GetContinueButtonElement().vanish(); }; 15791 15792 let _getFooterCloseButtonElem = function () { return apiObj.GetFooterElement().querySelector('.appModalFooterCancelButtons'); }; 15793 15794 apiObj.ShowFooterCloseButton = function () { if (_getFooterCloseButtonElem() != null) _getFooterCloseButtonElem().show(); }; 15795 apiObj.HideFooterCloseButton = function () { if (_getFooterCloseButtonElem() != null) _getFooterCloseButtonElem().vanish(); }; 15796 15797 let _initializeAndShowModal = function (modalElem) { 15798 15799 if (modalElem != null) 15800 modalElem.show(); 15801 15802 }; 15803 15804 //core APIs 15805 apiObj.Open = function () { 15806 15807 //console.log('modal api open api called (' + this.GetContainerElement().id + ')'); 15808 15809 apiObj.Initialize(); //always try to initialize on open 15810 15811 _initializeAndShowModal(this.GetContainerElement()); 15812 15813 document.addEventListener('keyup', _handleModalKeyPress); 15814 15815 apiObj.FireModalOpenedEvent(); 15816 15817 }; 15818 15819 apiObj.Close = function () { 15820 15821 apiObj.GetContainerElement().vanish(); 15822 15823 document.removeEventListener('keyup', _handleModalKeyPress); 15824 15825 apiObj.FireModalClosedEvent(); 15826 15827 }; 15828 15829 let _handleModalKeyPress = function (e) { 15830 15831 console.log('key up pressed for modal ' + apiObj.ModalContainerElementId + ' w key: ' + e.key); 15832 15833 if (e.key == 'Escape') { apiObj.Close(); } 15834 15835 }; 15836 15837 apiObj.IsModalOpen = function () { return (apiObj.GetContainerElement() != null && apiObj.GetContainerElement().isVisible()); }; 15838 15839 apiObj.ToggleVisibility = function () { 15840 15841 if (apiObj.IsModalOpen()) 15842 apiObj.Close(); 15843 else 15844 apiObj.Open(); 15845 15846 }; 15847 15848 apiObj.IsFullScreen = function () { 15849 15850 let retVal = apiObj.GetContainerElement().hasClassName('fullScreenMode'); 15851 15852 return retVal; 15853 15854 }; 15855 15856 apiObj.ToggleFullScreen = function () { 15857 15858 let fullScreenIcon = apiObj.GetContainerElement().querySelector('.topRightFullScreenIcons'); 15859 15860 if (apiObj.IsFullScreen()) { 15861 15862 apiObj.GetContainerElement().removeClassName('fullScreenMode'); 15863 15864 if (fullScreenIcon != null) { 15865 fullScreenIcon.removeClassName('fa-window-restore'); 15866 fullScreenIcon.addClassName('fa-window-maximize'); 15867 fullScreenIcon.setAttribute('title', 'maximize window'); 15868 } 15869 15870 } 15871 else { 15872 15873 apiObj.GetContainerElement().addClassName('fullScreenMode'); 15874 15875 if (fullScreenIcon != null) { 15876 fullScreenIcon.removeClassName('fa-window-maximize'); 15877 fullScreenIcon.addClassName('fa-window-restore'); 15878 fullScreenIcon.setAttribute('title', 'restore window'); 15879 } 15880 15881 } 15882 15883 }; 15884 15885 //custom call backs 15886 apiObj.RegisterModalOpenedCallBack = function (callBackFunction) { 15887 15888 CallBackManager.add(apiObj.ModalContainerElementId + '_modalOpenedCallBack', callBackFunction, null); 15889 15890 }; 15891 15892 apiObj.FireModalOpenedEvent = function () { 15893 15894 CallBackManager.fire(apiObj.ModalContainerElementId + '_modalOpenedCallBack', {}, null); 15895 15896 }; 15897 15898 apiObj.RegisterModalClosedCallBack = function (callBackFunction) { 15899 15900 CallBackManager.add(apiObj.ModalContainerElementId + '_modalClosedCallBack', callBackFunction, null); 15901 15902 }; 15903 15904 apiObj.FireModalClosedEvent = function () { 15905 15906 CallBackManager.fire(apiObj.ModalContainerElementId + '_modalClosedCallBack', {}, null); 15907 15908 }; 15909 15910 apiObj.RegisterInitializedCallBack = function (callBackFunction) { 15911 15912 CallBackManager.add(apiObj.ModalContainerElementId + '_modalInitializedCallBack', callBackFunction, null); 15913 15914 }; 15915 15916 apiObj.FireInitializedEvent = function () { 15917 15918 CallBackManager.fire(apiObj.ModalContainerElementId + '_modalInitializedCallBack', {}, null); 15919 15920 }; 15921 15922 //initialize UI 15923 apiObj.setCoreConfigError = function (message) { apiObj.GetContainerElement().innerHTML = '<h1 style="color:red;">
15923' + message + '</h1>'; }; 15924 15925 let _isInitialized = false; 15926 15927 apiObj.Initialize = function () { 15928 15929 //console.log('is initialized: ' + _isInitialized); 15930 if (!_isInitialized) { 15931 15932 let contElem = apiObj.GetContainerElement(); 15933 if (contElem != null) { 15934 15935 //console.log('initialize modal ' + contElem.id); 15936 15937 let requiredUIElementsValidated = true; 15938 15939 //add page overlay class 15940 if (!apiObj.GetContainerElement().hasClassName('pageOverlays')) 15941 apiObj.GetContainerElement().addClassName('pageOverlays'); 15942 15943 ////make sure class if defined on root element 15944 //if (!apiObj.GetContainerElement().hasClassName('ceModalDia_XXX_logs')) 15945 // apiObj.GetContainerElement().addClassName('ceModalDia_XXX_logs'); 15946 15947 if(apiObj.GetInnerContainerElement() == null && requiredUIElementsValidated) { 15948 requiredUIElementsValidated = false; 15949 apiObj.setCoreConfigError('modal wrapper UI element not defined (add div with class appModals directly inside the root element)'); 15950 } 15951 15952 if (apiObj.GetMainUIElement() == null && requiredUIElementsValidated) { 15953 requiredUIElementsValidated = false; 15954 apiObj.setCoreConfigError('modal UI element not defined (add div with class appModalUIContainers)'); 15955 } 15956 15957 if (requiredUIElementsValidated) { 15958 15959 //check for click outside (add class and register click close event) 15960 if (!isBlank(contElem.getAttribute('data-outside-click-close')) && contElem.getAttribute('data-outside-click-close').toBoolFalseIfInvalid()) { 15961 15962 contElem.addClassName('appClickCloseModals'); 15963 15964 contElem.addEventListener('mousedown', function (evt) { 15965 15966 //console.log('evt: ' + evt.target.id + ', ' + evt.target.className); 15967 if(evt.target.hasClassName('pageOverlays')) 15968 apiObj.Close(); 15969 15970 }); 15971 15972 } 15973 15974 //always add title bar if not defined 15975 //console.log('has header: ' + apiObj.GetHeaderElement()); 15976 if (apiObj.GetHeaderElement() == null) { 15977 15978 let headerElem = createAnonymousDomElement('div', 'appModalHeaders'); 15979 15980 let headerTitleTextElem = createAnonymousDomElement('span', 'appModalHeaderHeaderTitles'); 15981 15982 if (!isBlank(contElem.getAttribute('data-modal-title'))) 15983 headerTitleTextElem.innerHTML = contElem.getAttribute('data-modal-title'); 15984 15985 headerElem.appendChild(headerTitleTextElem); //headerTitle 15986 15987 let headerTopRightIconsContainerElem = createAnonymousDomElement('div', 'topRightIconsContainers'); 15988 15989 //let headerFullScreenIconElem = createAnonymousDomElement('span', 'fa fa-window-maximize topRightFullScreenIcons'); 15990 // 15991 //headerFullScreenIconElem.setAttribute('title', 'maximize window'); 15992 // 15993 //headerFullScreenIconElem.addEventListener('click', function () { apiObj.ToggleFullScreen(); }); 15994 // 15995 //headerTopRightIconsContainerElem.appendChild(headerFullScreenIconElem); 15996 15997 let headerCloseIconElem = createAnonymousDomElement('span', 'fa fa-close topRightCloseIcons'); 15998 15999 headerCloseIconElem.addEventListener('click', function () { apiObj.Close(); }); 16000 16001 headerTopRightIconsContainerElem.appendChild(headerCloseIconElem); 16002 16003 if (!isBlank(contElem.getAttribute('data-show-header-icons')) && !contElem.getAttribute('data-show-header-icons').toBoolFalseIfInvalid()) 16004 headerTopRightIconsContainerElem.vanish(); 16005 16006 headerElem.appendChild(headerTopRightIconsContainerElem); 16007 16008 apiObj.GetInnerContainerElement().insertFirstElement(headerElem); 16009 16010 } 16011
16012 //console.log('init modal ' + contElem.id + ' footer: ' + apiObj.GetFooterElement()); 16013 16014 //always add footer bar if not defined 16015 if (apiObj.GetFooterElement() == null) { 16016 16017 let footerElem = createAnonymousDomElement('div', 'appModalFooters'); 16018 16019 if (!isBlank(contElem.getAttribute('data-show-footer-close-button')) && contElem.getAttribute('data-show-footer-close-button').toBoolFalseIfInvalid()) { 16020 16021 let closeButtonText = 'Close'; 16022 if (!isBlank(contElem.getAttribute('data-close-button-text'))) 16023 closeButtonText = contElem.getAttribute('data-close-button-text'); 16024 16025 let cancelButton = createAnonymousDomElement('button', 'ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only appModalFooterCancelButtons', null, '<span class="ui-button-text appModalFooterCloseButtonLabels">' + closeButtonText + '</span>'); 16026 cancelButton.setAttribute('type', 'button'); //i'm sure both of these are necessary for Jquery 16027 cancelButton.setAttribute('role', 'button'); //i'm sure both of these are necessary for Jquery 16028 cancelButton.setAttribute('aria-disabled', 'false'); 16029 16030 cancelButton.addEventListener('click', function () { apiObj.Close(); }); 16031 16032 footerElem.appendChild(cancelButton); 16033 16034 } 16035 16036 let doNotRenderFooterBlock = (!isBlank(contElem.getAttribute('data-show-footer')) && !contElem.getAttribute('data-show-footer').toBoolFalseIfInvalid()); 16037 if (doNotRenderFooterBlock) 16038 footerElem.vanish(); 16039 16040 apiObj.GetInnerContainerElement().appendChild(footerElem); 16041 16042 //console.log('init modal ' + contElem.id + ' doNotRenderFooterBlock: ' + doNotRenderFooterBlock); 16043 16044 if (!doNotRenderFooterBlock) { 16045 16046 let continueButtonText = 'Continue'; 16047 if (!isBlank(contElem.getAttribute('data-continue-button-text'))) 16048 continueButtonText = contElem.getAttribute('data-continue-button-text'); 16049 16050 let continueFunc = null; 16051 if (!isBlank(contElem.getAttribute('data-continue-function'))) 16052 continueFunc = eval(contElem.getAttribute('data-continue-function')); 16053 16054 //console.log('init modal ' + contElem.id + ' with func: ' + contElem.getAttribute('data-continue-function') + ' (' + continueFunc + ')'); 16055 if (continueFunc != null) 16056 apiObj.AddButton(continueButtonText, continueFunc, null, 'appModalFooterContinueButtons', 'appModalFooterContinueButtonLabels'); 16057 else 16058 console.log('modal ' + contElem.id + ' continue function invali
16058d: ' + contElem.getAttribute('data-continue-function')); 16059 16060 } 16061 else 16062 console.log('skip continue button initialize for modal ' + contElem.id + ' because footer disabled by config (data-show-footer-close-button)'); 16063 16064 } 16065 16066 } 16067 16068 } 16069 else 16070 console.log('modal initialization failed (no container element found for id ' + apiObj.ModalContainerElementId + ')'); 16071 16072 apiObj.FireInitializedEvent(); 16073 16074 _isInitialized = true; 16075 16076 } 16077 16078 }; 16079 16080 apiObj.AddSecondaryButton = function (text, jsFunction, domId, cssClasses, textClasses, toolTip) { 16081 16082 apiObj.Initialize(); 16083 16084 let addCssClasses = 'popUpButtons popUpButtonsSecondary appModalFooterSecondaryButtons'; 16085 16086 if (!isBlank(cssClasses)) 16087 addCssClasses += ' ' + cssClasses; 16088 16089 apiObj.AddButton(text, jsFunction, domId, addCssClasses, textClasses, toolTip); 16090 16091 } 16092 16093 apiObj.AddLeftSecondaryButton = function (text, jsFunction, domId, cssClasses, textClasses, toolTip) { 16094 16095 apiObj.Initialize(); 16096 16097 let addCssClasses = 'popUpButtons popUpButtonsSecondary appModalFooterSecondaryButtons appModalFooterLeftButtons'; 16098 16099 if (!isBlank(cssClasses)) 16100 addCssClasses += ' ' + cssClasses; 16101 16102 apiObj.AddButton(text, jsFunction, domId, addCssClasses, textClasses, toolTip); 16103 16104 } 16105 16106 apiObj.AddButton = function (text, jsFunction, domId, cssClasses, textClasses, toolTip) { 16107 16108 if (apiObj.GetFooterElement() != null) { 16109 16110 let buttonCssClass = 'popUpButtons ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only'; 16111 16112 if (!isBlank(cssClasses)) 16113 buttonCssClass += ' ' + cssClasses; 16114 16115 let buttonLabelCssClasses = 'ui-button-text'; 16116 16117 if (!isBlank(textClasses)) 16118 buttonLabelCssClasses += ' ' + textClasses; 16119 16120 let processButton = createAnonymousDomElement('button', buttonCssClass, null, '<span class="' + buttonLabelCssClasses + '">
16120' + text + '</span>'); 16121 processButton.setAttribute('type', 'button'); //i'm sure both of these are necessary for Jquery 16122 processButton.setAttribute('role', 'button'); //i'm sure both of these are necessary for Jquery 16123 processButton.setAttribute('aria-disabled', 'false'); 16124 16125 if (!isBlank(toolTip)) 16126 processButton.setAttribute('title', toolTip); 16127 16128 //Update to onclick so that it can be removed if UpdateContinueButton is called 16129 if (typeof (jsFunction) == 'function') 16130 processButton.setAttribute('onclick', jsFunction.name + '();'); 16131 //processButton.addEventListener('click', function () { jsFunction(); }); 16132 16133 if (!isBlank(domId)) 16134 processButton.id = domId; 16135 16136 //console.log('modal add button with text: ' + text); 16137 16138 apiObj.GetFooterElement().appendChild(processButton); 16139 16140 } 16141 16142 }; 16143 16144 return apiObj; 16145 16146} 16147 16148function getAppUIUserMessagesApi(config) { 16149 16150 const thisApi = {}; 16151 16152 thisApi.MainContainerElementDomId = config.MainContainerElementDomId; 16153 16154 const _logger = getConsoleLogApi({ "LogSourceName": 'CE UI Messages Api' }); 16155 16156 const _getMainContainerElement = function () { 16157 16158 let retVal = document.getElementById(thisApi.MainContainerElementDomId); 16159 16160 if (retVal == null) 16161 retVal = document.body; 16162 16163 return retVal; 16164 16165 }; 16166 16167 const _getSuccessMessageElement = function () { return document.getElementById('successMessage'); }; 16168 const _getErrorMessageElement = function () { return document.getElementById('errorMessage'); }; 16169 const _getWarningMessageElement = function () { return document.getElementById('warningMessage'); }; 16170 const _getInformationMessageElement = function () { return document.getElementById('informationMessage'); }; 16171 16172 const _getToastPopUpMessageElement = function () { return document.getElementById('toastPopUpMessage'); }; 16173 16174 const _setMessage = function (elem, message, disableAutoScroll) { 16175 16176 if (elem != null) { 16177 16178 elem.innerHTML = message; 16179 16180 if (!isBlank(elem.innerHTML)) { 16181 16182 elem.show(); 16183 16184 if (!disableAutoScroll) 16185 scrollToTopOfWindow(); 16186 16187 } 16188 else 16189 elem.vanish(); 16190 16191 } 16192 16193 }; 16194 16195 thisApi.SetSuccess = function (message, disableAutoScroll) { _setMessage(_getSuccessMessageElement(), message, disableAutoScroll); }; 16196 16197 thisApi.ClearSuccess = function () { _setMessage(_getSuccessMessageElement(), ''); }; 16198 16199 thisApi.SetError = function (message, disableAutoScroll) { _setMessage(_getErrorMessageElement(), message, disableAutoScroll); }; 16200 16201 thisApi.ClearError = function () { _setMessage(_getErrorMessageElement(), ''); }; 16202 16203 thisApi.SetWarning = function (message, disableAutoScroll) { _setMessage(_getWarningMessageElement(), message, disableAutoScroll); }; 16204 16205 thisApi.ClearWarning = function () { _setMessage(_getWarningMessageElement(), ''); }; 16206 16207 thisApi.SetInformation = function (message, disableAutoScroll) { _setMessage(_getInformationMessageElement(), message, disableAutoScroll); }; 16208 16209 thisApi.ClearInformation = function () { _setMessage(_getInformationMessageElement(), ''); }; 16210 16211 thisApi.ClearMessages = function () { 16212 16213 thisApi.ClearSuccess(); 16214 thisApi.ClearError(); 16215 thisApi.ClearWarning(); 16216 thisApi.ClearInformation(); 16217 16218 }; 16219 16220 thisApi.ShowToastPopUp = function (message) { 16221 16222 const toastElem = _getToastPopUpMessageElement(); 16223
16224 //_logger.Log('show toast called with elem ' + toastElem + ' and message: ' + message); 16225 16226 if (toastElem != null && !isBlank(message)) { 16227 16228 toastElem.textContent = message; 16229 toastElem.addClassName('showToastPopUp'); 16230 16231 setTimeout( 16232 function () { 16233 toastElem.removeClassName('showToastPopUp'); 16234 }, 16235 1600 16236 ); 16237 16238 } 16239 16240 }; 16241 16242 thisApi.SetFromPublicAjaxResponse = function (result) { 16243 16244 console.log('set result from ajax: ' + JSON.stringify(result)); 16245 16246 if (result != null) { 16247 16248 if (result.Success) 16249 thisApi.SetSuccess('Success!'); 16250 else { 16251 console.log('set error message: ' + result.ErrorMessage); 16252 thisApi.SetError(result.ErrorMessage); 16253 } 16254 16255 } 16256 else 16257 thisApi.SetError('Unknown error (contact system administrator)'); 16258 16259 16260 }; 16261 16262 //Initialize 16263 thisApi.Initialize = function () { 16264 16265 _logger.Log('initialize started'); 16266 16267 if (isBlank(thisApi.MainContainerElementDomId)) 16268 _logger.Log('MainContainerElementDomId not defined (document body will be used for user messages)'); 16269 16270 const mainContainerElement = _getMainContainerElement(); 16271 if (mainContainerElement != null) { 16272 16273 let erroMessageContainer = document.getElementById('errorMessage'); 16274 if (erroMessageContainer == null) { 16275 16276 erroMessageContainer = document.createElement('div'); 16277 erroMessageContainer.setAttribute('id', 'errorMessage'); 16278 erroMessageContainer.setAttribute('class', 'error'); 16279 16280 mainContainerElement.prepend(erroMessageContainer); 16281 16282 erroMessageContainer.vanish(); 16283 16284 } 16285 16286 let successMessageContainer = document.getElementById('successMessage'); 16287 if (successMessageContainer == null) { 16288 16289 successMessageContainer = document.createElement('div'); 16290 successMessageContainer.setAttribute('id', 'successMessage'); 16291 16292 mainContainerElement.prepend(successMessageContainer); 16293 16294 successMessageContainer.vanish(); 16295 16296 } 16297 16298 let warningMessageContainer = document.getElementById('warningMessage'); 16299 if (warningMessageContainer == null) { 16300 16301 warningMessageContainer = document.createElement('div'); 16302 warningMessageContainer.setAttribute('id', 'warningMessage'); 16303 16304 mainContainerElement.prepend(warningMessageContainer); 16305 16306 warningMessageContainer.vanish(); 16307 16308 } 16309 16310 let infoMessageContainer = document.getElementById('informationMessage'); 16311 if (infoMessageContainer == null) { 16312 16313 infoMessageContainer = document.createElement('div'); 16314 infoMessageContainer.setAttribute('id', 'informationMessage'); 16315 16316 mainContainerElement.prepend(infoMessageContainer); 16317 16318 infoMessageContainer.vanish(); 16319 16320 } 16321 16322 let toasMessageContainer = document.getElementById('toastPopUpMessage'); 16323 if (toasMessageContainer == null) { 16324 16325 toasMessageContainer = document.createElement('div'); 16326 toasMessageContainer.setAttribute('id', 'toastPopUpMessage'); 16327 16328 mainContainerElement.append(toasMessageContainer); 16329 16330 //toasMessageContainer.vanish(); 16331 16332 } 16333 16334 } 16335 else 16336 _logger.Log('unable to initialize User Messages Api becuase no container is available'); 16337 16338 }; 16339 16340 return thisApi; 16341 16342} 16343 16344function getAppUIConfirmActionPopUpApi() { 16345 16346 const thisApi = {}; 16347 16348 const _getConfirmActionOverlayElem = function () { return document.getElementById('confirmActionOverlay'); }; 16349 const _getConfirmActionMessagePrimaryTextElem = function () { return document.getElementById('confirmActionText'); }; 16350 16351 const _logger = getConsoleLogApi({ "LogSourceName": 'CE Confirm Action Pop Up Api' });
16352 16353 let _confirmJavascriptFunction = null; 16354 let _cancelJavascriptFunction = null; 16355 16356 thisApi.Open = function (confirmJavascriptFunction, cancelJavascriptFunction) { 16357 16358 let processingOverlayParent = _getConfirmActionOverlayElem(); 16359 if (processingOverlayParent != null) { 16360 16361 App.UI.Messages.ClearMessages(); 16362 16363 processingOverlayParent.show(); 16364 16365 _confirmJavascriptFunction = confirmJavascriptFunction; 16366 _cancelJavascriptFunction = cancelJavascriptFunction; 16367 16368 } 16369 16370 }; 16371 16372 thisApi.Show = function () { return thisApi.Open(); }; 16373 16374 thisApi.Close = function () { 16375 16376 let processingOverlayParent = _getConfirmActionOverlayElem(); 16377 if (processingOverlayParent != null) 16378 processingOverlayParent.vanish(); 16379 16380 _resetMessage(); 16381 16382 }; 16383 16384 thisApi.Hide = function () { return thisApi.Close(); }; 16385 16386 thisApi.SetMessage = function (message) 16387 { 16388 16389 let messElem = _getConfirmActionMessagePrimaryTextElem(); 16390 if (messElem != null) 16391 messElem.innerHTML = message; 16392 16393 }; 16394 16395 const _resetMessage = function () { thisApi.SetMessage('Are you sure?'); } 16396 16397 const _handleConfirmClick = function () { 16398 16399 thisApi.Close(); 16400 16401 if (typeof _confirmJavascriptFunction == 'function') 16402 _confirmJavascriptFunction(); 16403 16404 }; 16405 16406 const _handleCancelClick = function () { 16407 16408 thisApi.Close(); 16409 16410 if (typeof _cancelJavascriptFunction == 'function') 16411 _cancelJavascriptFunction(); 16412 16413 }; 16414 16415 //Initialize 16416 thisApi.Initialize = function () { 16417 16418 _logger.Log('initialize started'); 16419 16420 //build DOM if not injected via legacy 16421 let processingOverlayParent = _getConfirmActionOverlayElem(); 16422 if (processingOverlayParent == null) { 16423 16424 processingOverlayParent = document.createElement('div'); 16425 processingOverlayParent.setAttribute('id', 'confirmActionOverlay'); 16426 16427 //add inner message container 16428 const innerContainer = document.createElement('div'); 16429 innerContainer.setAttribute('id', 'confirmActionMessage'); 16430 16431 //add text 16432 const textContainer = document.createElement('div'); 16433 textContainer.setAttribute('id', 'confirmActionText'); 16434 16435 innerContainer.appendChild(textContainer); 16436 16437 const confirmButtonsContainer = document.createElement('div'); 16438 confirmButtonsContainer.id = 'confirmActionModalButtonsContainer'; 16439 16440 const contButtonElem = document.createElement('button'); 16441 contButtonElem.setAttribute('title', 'click to contiue'); 16442 16443 contButtonElem.innerHTML = 'Continue'; 16444 16445 contButtonElem.addEventListener('click', function () { _handleConfirmClick(); }); 16446 16447 confirmButtonsContainer.appendChild(contButtonElem); 16448 16449 const cancelButtonElem = document.createElement('button'); 16450 cancelButtonElem.setAttribute('title', 'click to cancel'); 16451 cancelButtonElem.addClassName('cancel'); 16452 16453 cancelButtonElem.innerHTML = 'Cancel'; 16454 16455 cancelButtonElem.addEventListener('click', function () { _handleCancelClick(); }); 16456 16457 confirmButtonsContainer.appendChild(cancelButtonElem); 16458 16459 innerContainer.appendChild(confirmButtonsContainer); 16460 16461 processingOverlayParent.appendChild(innerContainer); 16462 16463 document.body.appendChild(processingOverlayParent); 16464 16465 processingOverlayParent.vanish(); 16466 16467 _resetMessage(); 16468 16469 } 16470 16471 }; 16472 16473 return thisApi; 16474 16475} 16476 16477function getAppUIProcessingOverlayApi() { 16478 16479 const thisApi = {}; 16480 16481 const _getProcessingOverlayElem = function () { return document.getElementById('processingOverlay'); }; 16482 const _getProcessingMessagePrimaryTextElem = function () { return document.getElementById('processingText'); }; 16483 16484 const _logger = getConsoleLogApi({ "LogSourceName": 'CE Processing Overlay Api' });
16485 16486 thisApi.Open = function () { 16487 16488 let processingOverlayParent = _getProcessingOverlayElem(); 16489 if (processingOverlayParent != null) 16490 processingOverlayParent.show(); 16491 16492 }; 16493 16494 thisApi.Show = function () { return thisApi.Open(); }; 16495 16496 thisApi.Close = function () { 16497 16498 let processingOverlayParent = _getProcessingOverlayElem(); 16499 if (processingOverlayParent != null) 16500 processingOverlayParent.vanish(); 16501 16502 _resetMessage(); 16503 16504 }; 16505 16506 thisApi.Hide = function () { return thisApi.Close(); }; 16507 16508 thisApi.SetMessage = function (message) 16509 { 16510 16511 let messElem = _getProcessingMessagePrimaryTextElem(); 16512 if (messElem != null) 16513 messElem.innerHTML = message; 16514 16515 }; 16516 16517 const _resetMessage = function () { thisApi.SetMessage('Processing...'); } 16518 16519 //Initialize 16520 thisApi.Initialize = function () { 16521 16522 _logger.Log('initialize started'); 16523 16524 //build DOM if not injected via legacy 16525 let processingOverlayParent = _getProcessingOverlayElem(); 16526 if (processingOverlayParent == null) { 16527 16528 processingOverlayParent = document.createElement('div'); 16529 processingOverlayParent.setAttribute('id', 'processingOverlay'); 16530 16531 //add inner message container 16532 let innerContainer = document.createElement('div'); 16533 innerContainer.setAttribute('id', 'processingMessage'); 16534 16535 //add text 16536 let textContainer = document.createElement('div'); 16537 textContainer.setAttribute('id', 'processingText'); 16538 //textContainer.innerHTML = 'Processing...'; 16539 16540 innerContainer.appendChild(textContainer); 16541 16542 processingOverlayParent.appendChild(innerContainer); 16543 16544 document.body.appendChild(processingOverlayParent); 16545 16546 processingOverlayParent.vanish(); 16547 16548 _resetMessage(); 16549 16550 } 16551 16552 }; 16553 16554 return thisApi; 16555 16556} 16557 16558function getCaptchaMfaModalApi() { 16559 16560 const thisApi = {}; 16561 16562 let _getModalOverlayElem = function () { return document.getElementById('mfaModalOverlay'); }; 16563 16564 let _logger = getConsoleLogApi({ "LogSourceName": 'CE MFA Modal Api' }); 16565 16566 let _codeValidatedCallBack = new function () { _logger.Log('code validated call back not implemented'); }; 16567 16568 thisApi.Open = function (codeValidatedCallBack) { 16569 16570 thisApi.Initialize(); 16571 16572 let modalOverlayParent = _getModalOverlayElem(); 16573 if (modalOverlayParent != null) { 16574 16575 _codeValidatedCallBack = codeValidatedCallBack; 16576 16577 modalOverlayParent.show(); 16578 16579 _clearErrorMessage(); 16580 16581 thisApi.SetTitle('We couldn\'t verify that you\'re a human.'); 16582 16583 _showRequestCodeView(); 16584 16585 thisApi.SelectPhone(); 16586 16587 } 16588 16589 }; 16590 16591 thisApi.Close = function () { 16592 16593 let modalOverlayParent = _getModalOverlayElem(); 16594 if (modalOverlayParent != null) 16595 modalOverlayParent.vanish(); 16596 16597 }; 16598 16599 thisApi.SetTitle = function (title) { document.getElementById('mfaModalTopMessage').innerHTML = title; }; 16600 thisApi.SetMessage = function (message) { document.getElementById('mfaResendCodeMessage').innerHTML = message; }; 16601 thisApi.SetPhoneNumber = function (val) { document.getElementById('mfaResendPhoneNumber').setValue(val); _handleResendPhoneNumberChange(); }; 16602 thisApi.SetEmail = function (val) { document.getElementById('mfaResendEmailAddress').setValue(val); _handleResendEmailChange(); }; 16603 thisApi.SelectPhone = function () { document.getElementById('mfaResendOptionsPhone').checked = true; _handleMethodRadioChanged(); }; 16604 thisApi.SelectEmail = function () { document.getElementById('mfaResendOptionsEmail').checked = true; _handleMethodRadioChanged(); }; 16605 thisApi.RequestCode = function () { _handleResendMfaCodeClick(); }; 16606 thisApi.SetSendCodeButtonText = function (text) { if(!isBlank(text)) document.getElementById('mfaCodeSubmitButton').setAttribute('value',text); }; 16607 16608 thisApi.GetPhoneNumber = function () { return document.getElementById('mfaResendPhoneNumber').getValue(); }; 16609 thisApi.GetEmail = function () { return document.getElementById('mfaResendEmailAddress').getValue(); }; 16610 16611 let _setErrorMessage = function (mess) { 16612 16613 document.getElementById('mfaModalTopErrorMessage').vanish(); 16614 16615 if (!isBlank(mess)) { 16616 document.getElementById('mfaModalTopErrorMessage').show(); 16617 document.getElementById('mfaModalTopErrorMessage').innerHTML = mess; 16618 } 16619 16620 } 16621 16622 let _clearErrorMessage = function () { _setErrorMessage(''); }; 16623 16624 let _isCodeValid = function () { 16625 16626 let codeVal = document.getElementById('mfaCode').getValue(); 16627 16628 return (!isBlank(codeVal) && codeVal.length == 4); 16629 16630 }; 16631 16632 let _handleEnterCodeChange = function () { 16633 16634 document.getElementById('mfaCodeSubmitButton').vanish(); 16635 if (_isCodeValid()) 16636 document.getElementById('mfaCodeSubmitButton').show(); 16637 16638 }; 16639 16640 let _handleMethodRadioChanged = function () { 16641 16642 document.getElementById('mfaResendPhoneNumber').vanish(); 16643 document.getElementById('mfaResendEmailAddress').vanish(); 16644 16645 if (document.getElementById('mfaResendOptionsEmail').checked) 16646 document.getElementById('mfaResendEmailAddress').show(); 16647 else 16648 document.getElementById('mfaResendPhoneNumber').show(); 16649 16650 _handleResendPhoneNumberChange(); 16651 _handleResendEmailChange(); 16652 16653 }; 16654 16655 let _handleEnterCodeClick = function () { 16656 16657 let codeInput = document.getElementById('mfaCode'); 16658 codeInput.validationSucceeded(); 16659 16660 if (_isCodeValid()) { 16661 16662 let params = { 16663 "ajaxParams": { 16664 "UserSuppliedMfaCode": document.getElementById('mfaCode').getValue(), 16665 } 16666 }; 16667 16668 App.PostJsonAsync('/api/PublicWebAppUIAjaxEndpoint/ValidateCaptchaMfaCode', 16669 JSON.stringify(params), 16670 function (result) { 16671 16672 if (result.Success) { 16673 16674 thisApi.Close(); //always close on success 16675 16676 _codeValidatedCallBack(); 16677 16678 //save contact info for future UX 16679 let ph = App.UI.CaptchaMfaModal.GetPhoneNumber(); 16680 if(!isBlank(ph)) 16681 App.LocalStorage.Save('captcha_mfa_last_phone_number', ph); 16682 16683 let eml = App.UI.CaptchaMfaModal.GetEmail(); 16684 if(!isBlank(eml)) 16685 App.LocalStorage.Save('captcha_mfa_last_email_address', eml); 16686 16687 //_fireCodeValidatedEvent(); 16688 16689 } 16690 else { 16691
16692 _logger.Log('invalid code'); 16693 16694 _setErrorMessage(result.ErrorMessage) 16695 16696 codeInput.validationFailed(); 16697 16698 } 16699 16700 }, 16701 function (error) { 16702 _logger.Log('error: ' + error); 16703 }); 16704 16705 } 16706 else 16707 codeInput.validationFailed(); 16708 16709 }; 16710 16711 let _handleResendMfaCodeClick = function () { 16712 16713 _clearErrorMessage(); 16714 16715 //_showRequestCodeView(); 16716 16717 let modeSelected = document.getElementById('mfaResendOptionsEmail').checked ? 'email' : 'phone'; 16718 16719 let phoneInput = document.getElementById('mfaResendPhoneNumber'); 16720 let emailInput = document.getElementById('mfaResendEmailAddress'); 16721 16722 phoneInput.validationSucceeded(); 16723 emailInput.validationSucceeded(); 16724 16725 let phoneToUse = ''; 16726 let emailToUse = ''; 16727 16728 if (modeSelected == 'phone') { 16729 16730 if (!isBlank(phoneInput.getValue())) 16731 phoneToUse = phoneInput.getValue(); 16732 else 16733 phoneInput.validationFailed(); 16734 16735 } 16736 else { 16737 16738 if (isValidEmailAddress(emailInput.getValue())) 16739 emailToUse = emailInput.getValue(); 16740 else 16741 emailInput.validationFailed(); 16742 16743 } 16744 16745 if (!isBlank(phoneToUse + emailToUse)) { 16746 16747 let params = { 16748 "ajaxParams": { 16749 "PhoneNumber": phoneToUse, 16750 "EmailAddress": emailToUse 16751 } 16752 }; 16753 16754 App.PostJsonAsync('/api/PublicWebAppUIAjaxEndpoint/GenerateCaptchaMfaCode', 16755 JSON.stringify(params), 16756 function (result) { 16757 16758 if (result.Success) { 16759 _showEnterCodeView(); 16760 _fireResendCodeEvent(phoneToUse, emailToUse); 16761 } 16762 else { 16763 16764 _logger.Log('unable to generate captcha mfa code'); 16765 16766 _setErrorMessage(result.ErrorMessage) 16767 16768 if (modeSelected == 'phone') 16769 phoneInput.validationFailed(); 16770 else 16771 emailInput.validationFailed(); 16772 16773 } 16774 16775 }, 16776 function (error) { 16777 _logger.Log('error: ' + error); 16778 }); 16779 16780 } 16781 16782 }; 16783 16784 let _handleResendPhoneNumberChange = function () { 16785 16786 if (document.getElementById('mfaResendOptionsPhone').checked) { 16787 16788 document.getElementById('mfaResendCodeButton').vanish(); 16789 if (!isBlank(document.getElementById('mfaResendPhoneNumber').getValue())) 16790 document.getElementById('mfaResendCodeButton').show(); 16791 16792 } 16793 16794 }; 16795 16796 let _handleResendEmailChange = function () { 16797 16798 if (document.getElementById('mfaResendOptionsEmail').checked) { 16799 16800 document.getElementById('mfaResendCodeButton').vanish(); 16801 if (!isBlank(document.getElementById('mfaResendEmailAddress').getValue())) 16802 document.getElementById('mfaResendCodeButton').show(); 16803 16804 } 16805 16806 }; 16807 16808 thisApi.RegisterInitializedCallBack = function (callBackFunction) { 16809 CallBackManager.add('mfaModalInitializedCallBack', callBackFunction, null); 16810 }; 16811 16812 let _fireInitializedEvent = function () { 16813 CallBackManager.fire('mfaModalInitializedCallBack', {}, null); 16814 }; 16815 16816 //thisApi.RegisterCodeValidatedCallBack = function (callBackFunction) { 16817 // CallBackManager.add('mfaModalCodeValidatedCallBack', callBackFunction, null); 16818 //}; 16819 // 16820 //let _fireCodeValidatedEvent = function () { 16821 // CallBackManager.fire('mfaModalCodeValidatedCallBack', {}, null); 16822 //}; 16823 16824 thisApi.RegisterResendCodeCallBack = function (callBackFunction) { 16825 CallBackManager.add('mfaModalResendCodeCallBack', callBackFunction, null); 16826 }; 16827 16828 let _fireResendCodeEvent = function () { 16829 CallBackManager.fire('mfaModalResendCodeCallBack', {}, null); 16830 }; 16831 16832 let _showRequestCodeView = function () { 16833 16834 thisApi.SetMessage('We can send you a one-time code to continue.'); 16835 16836 document.getElementById('mfaEnterCodeForm').vanish(); 16837 document.getElementById('resendCodeForm').show(); 16838 document.getElementById('didNotGetCodeLinkContainer').vanish(); 16839 16840 if (document.getElementById('mfaResendOptionsEmail').checked) 16841 document.getElementById('mfaResendEmailAddress').focus(); 16842 else 16843 document.getElementById('mfaResendPhoneNumber').focus(); 16844 16845 }; 16846 16847 let _showEnterCodeView = function () { 16848 16849 let modeSelected = document.getElementById('mfaResendOptionsEmail').checked ? 'email' : 'phone'; 16850 16851 thisApi.SetMessage('Enter code we sent to your ' + modeSelected + ' to continue.'); 16852 16853 document.getElementById('mfaEnterCodeForm').show(); 16854 document.getElementById('resendCodeForm').vanish(); 16855 document.getElementById('didNotGetCodeLinkContainer').show(); 16856 16857 _handleEnterCodeChange(); 16858 16859 document.getElementById('mfaCode').focus(); 16860 16861 }; 16862 16863 //Initialize 16864 thisApi.Initialize = function () { 16865 16866 //build DOM if not injected via legacy 16867 let modalOverlayParent = _getModalOverlayElem(); 16868 if (modalOverlayParent == null) { 16869
16870 _logger.Log('initialize started'); 16871 16872 modalOverlayParent = document.createElement('div'); 16873 modalOverlayParent.setAttribute('id', 'mfaModalOverlay'); 16874 16875 //add inner message container 16876 let modalBox = document.createElement('div'); 16877 modalBox.setAttribute('id', 'mfaModalBox'); 16878 16879 //add error message 16880 let topErrorMessage = document.createElement('div'); 16881 topErrorMessage.setAttribute('id', 'mfaModalTopErrorMessage'); 16882 topErrorMessage.vanish(); 16883 16884 modalBox.appendChild(topErrorMessage); 16885 16886 //add message 16887 let messageContainer = document.createElement('div'); 16888 messageContainer.setAttribute('id', 'mfaModalTopMessage'); 16889 //messageContainer.innerHTML = ''; //dynamic 16890 16891 modalBox.appendChild(messageContainer); 16892 16893 let resendCodeContainer = document.createElement('div'); 16894 resendCodeContainer.setAttribute('id', 'mfaResendCodeMessage'); 16895 //resendCodeContainer.innerHTML = ''; //dynamic 16896 16897 modalBox.appendChild(resendCodeContainer); 16898 16899 //enter code form 16900 let formContainer = document.createElement('div'); 16901 formContainer.setAttribute('id', 'mfaEnterCodeForm'); 16902 16903 let codeInput = document.createElement('input'); 16904 codeInput.setAttribute('type', 'text'); 16905 codeInput.setAttribute('id', 'mfaCode'); 16906 codeInput.setAttribute('maxlength', '4'); 16907 codeInput.setAttribute('autocomplete', 'one-time-code'); 16908 codeInput.setAttribute('placeholder', 'enter code'); 16909 16910 codeInput.addEventListener('input', function () { _handleEnterCodeChange(); }); 16911 16912 codeInput.addEventListener('keyup', function (event) { if (event.key == 'Enter') _handleEnterCodeClick(); }); 16913 16914 formContainer.appendChild(codeInput); 16915 16916 let submitMfaCodeButton = createClickInputButton('mfaCodeSubmitButton', '', '', 'Complete Transaction', function () { _handleEnterCodeClick(); }); 16917 16918 formContainer.appendChild(submitMfaCodeButton); 16919 16920 modalBox.appendChild(formContainer); 16921 16922 //add resend UX 16923 let didNotGetCodeLinkContainer = document.createElement('div'); 16924 didNotGetCodeLinkContainer.setAttribute('id', 'didNotGetCodeLinkContainer'); 16925 16926 let didNotGetCodeLink = document.createElement('a'); 16927 didNotGetCodeLink.setAttribute('id', 'didNotGetCodeLink'); 16928 didNotGetCodeLink.setAttribute('href', 'javascript:void(0)'); 16929 didNotGetCodeLink.innerHTML = 'Didn\'t receive code?'; 16930 16931 didNotGetCodeLink.addEventListener('click', function () { _showRequestCodeView(); }); 16932 16933 didNotGetCodeLinkContainer.appendChild(didNotGetCodeLink); 16934 16935 modalBox.appendChild(didNotGetCodeLinkContainer); 16936 16937 //resend form 16938 let resendFormContainer = document.createElement('div'); 16939 resendFormContainer.setAttribute('id', 'resendCodeForm'); 16940 16941 //radio choice selector 16942 let radioDiv = document.createElement('div'); 16943 radioDiv.setAttribute('id', 'resendCodeFormMethodOptionsContainer'); 16944 16945 let phoneRadioDiv = document.createElement('div'); 16946 16947 let phoneRadio = document.createElement('input'); 16948 phoneRadio.setAttribute('type', 'radio'); 16949 phoneRadio.setAttribute('name', 'mfaResendOptions'); 16950 phoneRadio.setAttribute('id', 'mfaResendOptionsPhone'); 16951 phoneRadio.setAttribute('value', 'phone'); 16952 phoneRadio.checked = true; 16953 16954 phoneRadio.addEventListener('change', function () { _handleMethodRadioChanged(); }); 16955 16956 phoneRadioDiv.appendChild(phoneRadio); 16957 16958 let phoneLabel = document.createElement('label'); 16959 phoneLabel.setAttribute('for', 'mfaResendOptionsPhone'); 16960 phoneLabel.innerText = 'Send to my phone'; 16961 16962 phoneRadioDiv.appendChild(phoneLabel); 16963 16964 radioDiv.appendChild(phoneRadioDiv); 16965 16966 let emailRadioDiv = document.createElement('div'); 16967 16968 let emailRadio = document.createElement('input'); 16969 emailRadio.setAttribute('type', 'radio'); 16970 emailRadio.setAttribute('name', 'mfaResendOptions'); 16971 emailRadio.setAttribute('id', 'mfaResendOptionsEmail'); 16972 emailRadio.setAttribute('value', 'email'); 16973 16974 emailRadio.addEventListener('change', function () { _handleMethodRadioChanged(); }); 16975 16976 emailRadioDiv.appendChild(emailRadio); 16977 16978 let emailLabel = document.createElement('label'); 16979 emailLabel.setAttribute('for', 'mfaResendOptionsEmail'); 16980 emailLabel.innerText = 'Send to my email'; 16981 16982 emailRadioDiv.appendChild(emailLabel); 16983 16984 radioDiv.appendChild(emailRadioDiv); 16985 16986 resendFormContainer.appendChild(radioDiv); 16987 16988 //add value inputs 16989 let mfaPhoneInput = document.createElement('input'); 16990 mfaPhoneInput.setAttribute('type', 'text'); 16991 mfaPhoneInput.setAttribute('id', 'mfaResendPhoneNumber'); 16992 mfaPhoneInput.setAttribute('maxlength', '15'); 16993 mfaPhoneInput.setAttribute('placeholder', 'Enter phone number'); 16994 16995 mfaPhoneInput.addEventListener('input', function () { _handleResendPhoneNumberChange(); }); 16996 16997 resendFormContainer.appendChild(mfaPhoneInput); 16998 16999 let mfaEmailInput = document.createElement('input'); 17000 mfaEmailInput.setAttribute('type', 'text'); 17001 mfaEmailInput.setAttribute('id', 'mfaResendEmailAddress'); 17002 mfaEmailInput.setAttribute('maxlength', '500'); 17003 mfaEmailInput.setAttribute('placeholder', 'Enter email'); 17004 17005 mfaEmailInput.addEventListener('input', function () { _handleResendEmailChange(); }); 17006 17007 resendFormContainer.appendChild(mfaEmailInput); 17008 17009 let submitMfaResendCodeButton = createClickInputButton('mfaResendCodeButton', '', '', 'Get Code', function () { _handleResendMfaCodeClick(); }); 17010 17011 resendFormContainer.appendChild(submitMfaResendCodeButton); 17012 17013 modalBox.appendChild(resendFormContainer); 17014 17015 modalOverlayParent.appendChild(modalBox); 17016 17017 document.body.appendChild(modalOverlayParent); 17018 17019 modalOverlayParent.vanish(); 17020 17021 _fireInitializedEvent(); 17022 17023 } 17024 else
17025 _logger.Log('skipping initialize (already initailized'); 17026 17027 }; 17028 17029 return thisApi; 17030 17031} 17032 17033 17034/*App Core Footer*/ 17035 17036//DEFINE EVERGREEN VARIALES 17037const CE = getCeApi(); 17038 17039window.CE = CE; //for external script access (e.g., googlepay onload call back) 17040 17041const gl_ce_pageHostName = getPageHostName();
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.