{"version":3,"file":"../pdp-hcp-finder.js","names":["$","isInitialized","searchPractitionersUrl","ipFinderUrl","postalCodeFromCoordsUrl","abortController","AbortController","$buyNowModal","$buyNowForm","find","$selectedPartEl","$postalCodeEl","$practitionersWithinRangeEl","$loadingSpinners","$searchBtn","$hasResultsContainer","$noResultsContainer","$practitionerResultsList","$errorContainer","$advancedOptionsToggleBtn","$buyNowAdvancedSearchContainer","$resultsCollapsableContainer","postalCodeEntered","resultsForPostalCode","resultsCollapsableContainer","bootstrap","Collapse","getOrCreateInstance","jQuery","validator","addMethod","value","element","params","_params$positivePatte","optional","positivePattern","regex","RegExp","result","exec","$validator","validate","onfocusout","valid","onkeyup","event","is","defaults","call","focuscleanup","errorElement","errorClass","rules","SelectedPart","required","PostalCode","sp_positive_pattern","PractitionersWithinRange","max","min","messages","errorPlacement","$errorEl","$inputEl","$validationContainer","closest","appendTo","id","parent","highlight","validClass","$element","$phoneTypeContainer","isIntendedUse","length","addClass","removeClass","$intendedUses","unhighlight","success","$label","invalidHandler","_iterator","_createForOfIteratorHelper","errorList","_step","s","n","done","errorEntry","$parent","settings","err","e","f","validatePractionerLookupFormHtml","form","focusInvalid","toggleResultsCollapsable","isCallbackQueued","lastShow","show","isTransitioning","hide","one","setTimeout","showLoadingSpinners","attr","hasSearchResults","postalCodeFieldEnableDisable","val","setAvailablePractionersHtml","data","undefined","Error","html","errors","console","error","eKey","concat","join","append","template","_iterator2","_step2","_find$hcpUrl","_find","_practitionerRecord$p","_practitionerRecord$a","_ref","_practitionerRecord$p2","_twWebsite","practitionerRecord","twWebsite","purchasingOptionsV2","x","salesChannel","hcpUrl","originalTWUrl","URL","urlWithCorrectPath","productSlug","partNo","href","viewModel","customerName","addressLine1","cityStateZip","city","stateProvince","postalCode","phoneNumber","website","distance","gaddress","encodeURIComponent","address","newElStr","bindViewModelToHtml","setPostalCodeHtml","getPractitionerLookupDataHtml","range","Number","parseInt","$selectedOption","isVeterinary","isNaN","partNumbers","page","pageSize","lookupPractitonersAsync","request","Request","signal","method","body","JSON","stringify","headers","response","fetch","then","r","ok","json","hasContentLengthHeader","has","hasContentTypeHeader","get","text","getCurrentPositionServerAsync","Promise","reject","status","refreshSearchResults","isFormValid","resetAbortController","lookupData","_objectSpread","catch","finally","htmlCopy","viewModelKeys","Object","keys","_iterator3","matchAll","_step3","matchArr","htmlKey","expression","fnBinder","Function","replacementHtml","includes","replace","abort","message","on","key","trigger","document"],"sources":["pdp-hcp-finder.js"],"sourcesContent":["//#region Typedefs\r\n\r\n/**\r\n * @typedef {object} HcpSearchResultsDtoPartial\r\n * @property {number} buyCount\r\n * @property {number} buyCount\r\n * @property {HcpSearchResult[]} value\r\n * @property {{[key: string]: string[]}} errors\r\n * @property {string} postalCode\r\n * @property {number} distance\r\n * @property {number} page\r\n */\r\n\r\n/**\r\n * @typedef {HcpSearchResultsDtoPartial & Record<'@odata.count', number> & Record<'value', HcpSearchResult[]>} HcpSearchResultsDto\r\n */\r\n\r\n/**\r\n * @typedef {object} HcpSearchResultResultsDtoPartial\r\n * @property {number} buyCount\r\n * @property {HcpSearchResult[]} value\r\n * @property {{[key: string]: string[]}} errors\r\n * @property {string} postalCode\r\n * @property {number} distance\r\n * @property {number} page\r\n */\r\n\r\n/**\r\n * @typedef {HcpSearchResultResultsDtoPartial & Record<'@odata.count', number> & Record<'@search.facets', HcpSearchFacetsResultsDto>} HcpSearchResultResultsDto\r\n */\r\n\r\n/**\r\n * @typedef {object} HcpSearchFacetsResultsDtoPartial\r\n * @property {HcpSearchFacetDto[]} marketCodes\r\n * @property {HcpSearchFacetDto[]} purchasingOptions\r\n */\r\n\r\n/**\r\n * @typedef {HcpSearchFacetsResultsDtoPartial & Record<'purchasingOptionsV2/salesChannel', HcpSearchFacetDto[]>} HcpSearchFacetsResultsDto\r\n */\r\n\r\n/**\r\n * @typedef {object} HcpSearchFacetDto\r\n * @property {number} count\r\n * @property {string} value\r\n * @property {boolean} selected\r\n */\r\n\r\n/**\r\n * @typedef {object} HcpSearchResult\r\n * @property {string} addressLine1\r\n * @property {string} addressLine2\r\n * @property {string} city\r\n * @property {string} country\r\n * @property {string} county\r\n * @property {string} customerId\r\n * @property {string} customerName\r\n * @property {string} email\r\n * @property {string} latitude\r\n * @property {string} longitude\r\n * @property {string[]} marketCodes\r\n * @property {string} phoneNumber\r\n * @property {string} postalCode\r\n * @property {string[]} purchasingOptions\r\n * @property {{[key: string]: HcpPurchasingOption}} purchasingOptionsV2\r\n * @property {string} stateProvince\r\n * @property {string} website\r\n * @property {number} distance\r\n * @property {string} address\r\n */\r\n\r\n/**\r\n * @typedef {object} HcpPurchasingOption\r\n * @property {string} hcpUrl\r\n * @property {string} salesChannel\r\n */\r\n\r\n//#endregion\r\n\r\n$(function () {\r\n //#region Fields\r\n\r\n // General variables.\r\n var isInitialized = false;\r\n var searchPractitionersUrl = \"/api/hcpfinder/search\";\r\n var ipFinderUrl = \"/api/hcpfinder/postalcode-from-ip\";\r\n var postalCodeFromCoordsUrl = \"/api/hcpfinder/postalcode-from-coords\";\r\n var abortController = new AbortController();\r\n\r\n // Element variables.\r\n var $buyNowModal = $(\"#BuyNowModal\");\r\n var $buyNowForm = $buyNowModal.find(\"form\");\r\n var $selectedPartEl = $buyNowModal.find(\"[name='SelectedPart']\");\r\n var $postalCodeEl = $buyNowModal.find(\"[name='PostalCode']\");\r\n var $practitionersWithinRangeEl = $buyNowModal.find(\"[name='PractitionersWithinRange']\");\r\n var $loadingSpinners = $buyNowModal.find(\".js-spinner-container\");\r\n var $searchBtn = $buyNowModal.find(\"[name='UpdatePractionerSearchResults']\");\r\n var $hasResultsContainer = $buyNowModal.find(\".js-has-results-container\");\r\n var $noResultsContainer = $buyNowModal.find(\".js-no-results-container\");\r\n var $practitionerResultsList = $buyNowModal.find(\".js-nearest-practitioners-list\");\r\n var $errorContainer = $buyNowModal.find(\".js-error-messages-container\");\r\n var $advancedOptionsToggleBtn = $buyNowModal.find(\".js-advanced-options-toggle\");\r\n var $buyNowAdvancedSearchContainer = $buyNowModal.find(\"#BuyNowAdvancedSearchContainer\");\r\n var $resultsCollapsableContainer = $buyNowModal.find(\"#ResultsCollapsableContainer\");\r\n var postalCodeEntered = null;\r\n var resultsForPostalCode = null;\r\n\r\n // Misc variables.\r\n var resultsCollapsableContainer = bootstrap.Collapse.getOrCreateInstance($resultsCollapsableContainer[0]);\r\n\r\n //#endregion\r\n\r\n //#region Validator\r\n\r\n // This will mark the element as invalid if the pattern does not match.\r\n // NOTE: Requires the parameters \"positivePattern\".\r\n jQuery.validator.addMethod(\"sp_positive_pattern\", function (value, element, params) {\r\n if (this.optional(element) == true) {\r\n return true;\r\n }\r\n\r\n if ((params.positivePattern ?? \"\") === \"\") {\r\n return false;\r\n }\r\n\r\n const regex = new RegExp(params.positivePattern, \"gim\");\r\n const result = regex.exec(value);\r\n\r\n return result !== null;\r\n }, \"Invalid characters.\");\r\n\r\n var $validator = $buyNowForm.validate({\r\n onfocusout: function (element) {\r\n $(element).valid();\r\n },\r\n onkeyup: function (element, event) {\r\n if ($(element).is(\".no-validate-on-key-down\")) {\r\n return;\r\n }\r\n\r\n $.validator.defaults.onkeyup.call(this, element, event);\r\n },\r\n focuscleanup: true,\r\n errorElement: \"label\",\r\n errorClass: \"alert-msg\",\r\n rules: {\r\n SelectedPart: {\r\n required: true,\r\n },\r\n PostalCode: {\r\n required: true,\r\n sp_positive_pattern: {\r\n positivePattern: \"^[0-9]{5}$\",\r\n },\r\n },\r\n PractitionersWithinRange: {\r\n required: true,\r\n max: 100,\r\n min: 5,\r\n },\r\n },\r\n messages: {\r\n PostalCode: {\r\n required: \"Postal code is required.\",\r\n sp_positive_pattern: \"Must be a valid 5 digit postal code.\",\r\n },\r\n },\r\n errorPlacement: function ($errorEl, $inputEl) {\r\n if ($inputEl.is(\"[type='radio']\")) {\r\n const $validationContainer = $inputEl.closest(\".custom-sp-label-wrapper\").find(\".validation\");\r\n $errorEl.appendTo($validationContainer);\r\n } else if ($inputEl[0].id == \"PostalCode\") {\r\n $errorEl.appendTo($('#PostalCodeErrors'))\r\n }\r\n else {\r\n $errorEl.appendTo($inputEl.parent());\r\n }\r\n },\r\n highlight: function (element, errorClass, validClass) {\r\n const $element = $(element);\r\n const $phoneTypeContainer = $element.closest(\".phone-types\");\r\n const isIntendedUse = $element.is(\"[name='IntendedUse']\");\r\n\r\n if ($phoneTypeContainer.length > 0) {\r\n if ((errorClass ?? \"\").length > 0) {\r\n $phoneTypeContainer.addClass(errorClass);\r\n }\r\n\r\n if ((validClass ?? \"\").length > 0) {\r\n $phoneTypeContainer.removeClass(validClass);\r\n }\r\n } else {\r\n if ((errorClass ?? \"\").length > 0) {\r\n $element.addClass(errorClass);\r\n }\r\n\r\n if ((validClass ?? \"\").length > 0) {\r\n $element.removeClass(validClass);\r\n }\r\n }\r\n\r\n if (isIntendedUse) {\r\n if ((errorClass ?? \"\").length > 0) {\r\n $intendedUses.addClass(errorClass);\r\n }\r\n\r\n if ((validClass ?? \"\").length > 0) {\r\n $intendedUses.removeClass(validClass);\r\n }\r\n }\r\n },\r\n unhighlight: function (element, errorClass, validClass) {\r\n const $element = $(element);\r\n const $phoneTypeContainer = $element.closest(\".phone-types\");\r\n const isIntendedUse = $element.is(\"[name='IntendedUse']\");\r\n\r\n if ($phoneTypeContainer.length > 0) {\r\n if ((errorClass ?? \"\").length > 0) {\r\n $phoneTypeContainer.removeClass(errorClass);\r\n }\r\n\r\n if ((validClass ?? \"\").length > 0) {\r\n $phoneTypeContainer.addClass(validClass);\r\n }\r\n } else {\r\n if ((errorClass ?? \"\").length > 0) {\r\n $element.removeClass(errorClass);\r\n }\r\n\r\n if ((validClass ?? \"\").length > 0) {\r\n $element.addClass(validClass);\r\n }\r\n }\r\n\r\n if (isIntendedUse) {\r\n if ((errorClass ?? \"\").length > 0) {\r\n $intendedUses.removeClass(errorClass);\r\n }\r\n\r\n if ((validClass ?? \"\").length > 0) {\r\n $intendedUses.addClass(validClass);\r\n }\r\n }\r\n },\r\n success: function ($label) {\r\n // Helper for removing the error class from the custom\r\n // radio container.\r\n $label\r\n .closest(\".custom-sp-label-wrapper\")\r\n .find(\".labelled-container.error\")\r\n .removeClass(\"error\");\r\n },\r\n invalidHandler: function (event, validator) {\r\n // Helper for adding the error class to the \r\n // radio container.\r\n for (let errorEntry of validator.errorList) {\r\n var $parent = $(errorEntry.element).closest(\".labelled-container\");\r\n\r\n if ($parent.length === 0) {\r\n continue;\r\n }\r\n\r\n $parent.addClass(validator.settings.errorClass);\r\n }\r\n },\r\n });\r\n\r\n //#endregion\r\n\r\n //#region Html Functions\r\n\r\n /**\r\n * @returns {boolean}\r\n */\r\n function validatePractionerLookupFormHtml() {\r\n var result = $validator.form();\r\n\r\n if (!result) {\r\n $validator.focusInvalid();\r\n }\r\n\r\n return result;\r\n }\r\n\r\n var toggleResultsCollapsable = (function () {\r\n var isCallbackQueued = false;\r\n var lastShow = false;\r\n\r\n /**\r\n * Able to toggle the collapsible even when transitioning.\r\n * @param {boolean} show\r\n * @returns {void}\r\n */\r\n return (show) => {\r\n lastShow = show;\r\n\r\n if (isCallbackQueued) {\r\n return;\r\n }\r\n\r\n var isTransitioning = $resultsCollapsableContainer.is(\".collapsing\");\r\n\r\n if (!isTransitioning) {\r\n lastShow\r\n ? resultsCollapsableContainer.show()\r\n : resultsCollapsableContainer.hide();\r\n return;\r\n } else {\r\n // Add event listener.\r\n $resultsCollapsableContainer.one(\"shown.bs.collapse hidden.bs.collapse\", function (e) {\r\n setTimeout(() => {\r\n lastShow\r\n ? resultsCollapsableContainer.show()\r\n : resultsCollapsableContainer.hide();\r\n isCallbackQueued = false;\r\n }, 1);\r\n });\r\n }\r\n }\r\n })();\r\n\r\n /**\r\n * @param {boolean} show\r\n * @returns {void}\r\n */\r\n function showLoadingSpinners(show) {\r\n if (show) {\r\n $loadingSpinners.removeClass(\"d-none\");\r\n $searchBtn.addClass(\"disabled\").attr(\"disabled\", \"disabled\");\r\n $selectedPartEl.addClass(\"disabled\").attr(\"disabled\", \"disabled\");\r\n $postalCodeEl.addClass(\"disabled\").attr(\"disabled\", \"disabled\");\r\n $practitionersWithinRangeEl.addClass(\"disabled\").attr(\"disabled\", \"disabled\");\r\n toggleResultsCollapsable(false);\r\n } else {\r\n $loadingSpinners.addClass(\"d-none\");\r\n $selectedPartEl.removeClass(\"disabled\").attr(\"disabled\", null);\r\n $postalCodeEl.removeClass(\"disabled\").attr(\"disabled\", null);\r\n $practitionersWithinRangeEl.removeClass(\"disabled\").attr(\"disabled\", null);\r\n toggleResultsCollapsable(true);\r\n\r\n var hasSearchResults = $practitionerResultsList.find(\".js-search-result\").length > 0;\r\n\r\n if (hasSearchResults) {\r\n // Has results.\r\n $noResultsContainer.addClass(\"d-none\");\r\n $hasResultsContainer.removeClass(\"d-none\");\r\n } else {\r\n // No results.\r\n $noResultsContainer.removeClass(\"d-none\");\r\n $hasResultsContainer.addClass(\"d-none\");\r\n }\r\n }\r\n }\r\n\r\n function postalCodeFieldEnableDisable(e) {\r\n if ($('#PostalCode').val().length >= 5 && $('#PostalCode').val().length < 9 && $('#PostalCode').val() != resultsForPostalCode) {\r\n postalCodeEntered = $('#PostalCode').val();\r\n $searchBtn.removeClass(\"disabled\").attr(\"disabled\", null);\r\n }\r\n else {\r\n $searchBtn.addClass(\"disabled\").attr(\"disabled\", \"disabled\");\r\n }\r\n }\r\n\r\n /**\r\n * @param {HcpSearchResultsDtoPartial & { partNo: string, productSlug: string }} data\r\n * @returns {void}\r\n */\r\n function setAvailablePractionersHtml(data) {\r\n if (data === null || data === undefined) {\r\n throw new Error(\"The argument \\\"data\\\" cannot be null nor undefined.\");\r\n }\r\n\r\n // Remove error messages.\r\n $errorContainer.html(\"\");\r\n\r\n // Show error messages if any.\r\n if (data.errors !== null && data.errors !== undefined) {\r\n console.error(data.errors);\r\n for (var eKey in data.errors) {\r\n var errors = data.errors[eKey];\r\n // Use the bindViewModelToHtml(...) function to create the html to append to the errors container.\r\n var $errorEl = $(`
${errors.join(\"
\")}
\r\n \r\n {{distance}} mi\r\n
\r\n