{"version":3,"file":"xpect.min.js","sources":["xpect.min.js"],"sourcesContent":["/**\n * Cibis | Secure, high performance software\n * Copyright 2021 CIBIS International\n *\n * @author CIBIS International\n * @version 1.0.0\n */\nvar xpectWeb = xpectWeb || {\r\n enums: {\r\n stockOptionGroupSelectType: {\r\n listSingleSelection: 1,\r\n gridSingleSelection: 2,\r\n gridMultipleSelection: 3,\r\n textInput: 4\r\n }\r\n },\r\n timeoutIds: [],\r\n\r\n //\r\n // Check Verification\r\n //\r\n hasVerified: function () {\r\n return ($(\".xp-recaptcha\").length > 0 && typeof grecaptcha !== 'undefined' && grecaptcha.getResponse() !== \"\");\r\n },\r\n\r\n checkVerified: function (notificationContainer) {\r\n var isVerified = xpectWeb.hasVerified();\r\n\r\n if (isVerified) return true;\r\n\r\n xpectWeb.showVerificationNotification(notificationContainer);\r\n\r\n return false;\r\n },\r\n\r\n showVerificationNotification: function (notificationContainer) {\r\n // Show verification alert\r\n xpectWeb.showAlertDangerNotification(notificationContainer, \"verification-notification\", \"verification-notification\", \"
Please confirm you are not a robot.
\");\r\n },\r\n\r\n showAlertInfoNotification: function (notificationContainer, alertId, alertClass, alertHtmlMessage) {\r\n xpectWeb.showAlertNotification(notificationContainer, alertId, alertClass, alertHtmlMessage, 'alert-info');\r\n },\r\n\r\n showAlertSuccessNotification: function (notificationContainer, alertId, alertClass, alertHtmlMessage) {\r\n xpectWeb.showAlertNotification(notificationContainer, alertId, alertClass, alertHtmlMessage, 'alert-success');\r\n },\r\n\r\n showAlertWarningNotification: function (notificationContainer, alertId, alertClass, alertHtmlMessage) {\r\n xpectWeb.showAlertNotification(notificationContainer, alertId, alertClass, alertHtmlMessage, 'alert-warning');\r\n },\r\n\r\n showAlertDangerNotification: function (notificationContainer, alertId, alertClass, alertHtmlMessage) {\r\n xpectWeb.showAlertNotification(notificationContainer, alertId, alertClass, alertHtmlMessage, 'alert-danger');\r\n },\r\n\r\n showAlertNotification: function (notificationContainer, alertId, alertClass, alertHtmlMessage, alertTypeClass = 'alert-danger') {\r\n // Show alert\r\n var jqAlert = notificationContainer.find(\"#\" + alertId);\r\n\r\n /* Bootstrap 5 */\r\n if (jqAlert.length == 0) {\r\n jqAlert = $('', {\r\n \"class\": \"alert mt-4 \" + alertTypeClass + \" \" + alertClass,\r\n id: alertId,\r\n html: 'You must agree to the terms and conditions.
\");\r\n },\r\n\r\n showReadTermsConditionsAlert: function () {\r\n // Show terms and conditions alert\r\n xpectWeb.showAlertDangerNotification(xpectWeb.members.join.getNotificationContainer($(\".xp-member-join button[type='submit']\")), \"read-terms-and-conditions-notification\", \"read-terms-and-conditions-notification\", 'You must read the terms and conditions. Click the link to read the Terms and Conditions.
');\r\n },\r\n\r\n showHideCompanyName: function (companyDropdown) {\r\n var selectedText = $(companyDropdown).find(\":selected\").text().toLowerCase();\r\n\r\n // Show/Hide the Company Name and Company Position fields\r\n if (selectedText == \"company\") {\r\n $(\"#CompanyFields\").slideDown();\r\n $(\".xp-company-fields\").slideDown();\r\n } else {\r\n $(\"#CompanyFields\").slideUp();\r\n $(\".xp-company-fields\").slideUp();\r\n }\r\n },\r\n\r\n //\r\n // Form Submit handler\r\n //\r\n wireUpFormSubmitHandler: function () {\r\n $(document).on(\"click\", \".xp-member-join button[type=submit]\", function (evt) {\r\n if (!xpectWeb.members.join.checkAgreedTermsConditions()) {\r\n evt.preventDefault();\r\n return;\r\n }\r\n\r\n if (!xpectWeb.checkVerified(xpectWeb.members.join.getNotificationContainer($(this)))) {\r\n evt.preventDefault();\r\n return;\r\n }\r\n });\r\n },\r\n\r\n // Notifications can be placed in a specified location by using a container with the class \"xp-notification-container\", otherwise they will go in the parent of the element that initiated the notification.\r\n getNotificationContainer: function ($initiator) {\r\n $notificationContainer = $(\".xp-notification-container\");\r\n\r\n if ($notificationContainer.length == 0) {\r\n $notificationContainer = $initiator.parent();\r\n }\r\n\r\n return $notificationContainer;\r\n }\r\n};\r\n\r\n$(function () {\r\n if ($(\".xp-member-join\").length == 0) {\r\n return;\r\n }\r\n\r\n // Wire up the submit button\r\n xpectWeb.members.join.wireUpFormSubmitHandler();\r\n\r\n //\r\n // Required field validators\r\n //\r\n var requiredFieldMessages = {\r\n Title: 'Title',\r\n Gender: 'Gender',\r\n FirstName: 'First Name',\r\n LastName: 'Last Name',\r\n CompanyName: 'Company Name',\r\n CompanyPosition: 'Company Position',\r\n Phone: 'Phone',\r\n Fax: 'Fax',\r\n Mobile: 'Mobile',\r\n DateOfBirth: 'Date of Birth',\r\n PostalAddress1: 'Billing Address Line 1',\r\n PostalCity: 'City',\r\n PostalState: 'State',\r\n PostalPostcode: 'Postcode',\r\n Country: 'Postal Country',\r\n DeliveryAddress1: 'Delivery Address Line 1',\r\n DeliveryCity: 'City',\r\n DeliveryState: 'State',\r\n DeliveryPostcode: 'Postcode',\r\n DeliveryCountry: 'Country'\r\n };\r\n\r\n $(\".xp-required input:not([data-val-required]), .xp-required select:not([data-val-required])\").each((index, element) => {\r\n if (typeof requiredFieldMessages[element.name] === 'string') {\r\n $(element).attr(\"data-val-required\", \"The \" + requiredFieldMessages[element.name] + \" field is required.\");\r\n }\r\n });\r\n\r\n // Read terms and conditions\r\n $(\"#modal-terms .modal-footer .btn-primary\").on(\"click\", function() {\r\n var jqReadTerms = $(\"#ReadTermsAndConditions\");\r\n\r\n if (jqReadTerms.length > 0) {\r\n $(\"#ReadTermsAndConditions\").prop(\"checked\", true);\r\n }\r\n\r\n var jqAgreedTerms = $(\"#AgreedTermsConditions\");\r\n\r\n if (jqAgreedTerms.length > 0) {\r\n jqAgreedTerms.prop(\"checked\", true);\r\n }\r\n });\r\n\r\n // Delivery address section toggle\r\n $(\"#IsDeliveryAddressDifferentToPostalAddress\").click(function () {\r\n if ($(this).is(':checked')) {\r\n $(\"#DeliveryAddressFields\").slideDown();\r\n } else {\r\n $(\"#DeliveryAddressFields\").slideUp();\r\n }\r\n });\r\n});\nvar xpectWeb = xpectWeb || {};\r\n\r\nxpectWeb.productDetails = {\r\n /* vue.js app. Values initialised later. */\r\n app: {\r\n updated() {\r\n if (typeof xpectWeb.productDetails.productImageSlider === 'undefined' &&\r\n xpectWeb.productDetails.productImageSliderNeedsRefresh == true) {\r\n xpectWeb.productDetails.initProductImagesSlider();\r\n xpectWeb.productDetails.productImageSliderNeedsRefresh = false;\r\n }\r\n\r\n // Ensure the inc/dec buttons are appropriately enabled/disabled and maximum quantity is respected\r\n $(\"#quantity\").keyup();\r\n\r\n // Ensure there is an active tab\r\n if ($('.nav-tabs .active').length == 0) {\r\n $('.nav-tabs a:first').tab('show');\r\n } else {\r\n // A tab may have been hidden while active, if it's restored this will remove the active state\r\n $('.nav-tabs .active:gt(0)').removeClass('active');\r\n }\r\n },\r\n data() {\r\n return {\r\n componentKey: 0, // Used to force a re-render.\r\n\r\n appData: null, // App Data\r\n\r\n selectedVariant: null, // Selected single variant. For multi-variant this is set after colour and size have been selected. For single-variant it will be always set.\r\n selectedColour: 0, // Selected colour\r\n selectedColourDescription: '', // Selected colour description\r\n selectedSize: 0, // Selected size\r\n hasSomeStockAvailable: false, // If a single-variant is available, or if some colours/sizes available for a multi-variant (for out of stock message)\r\n isMultiVariant: false, // If this is a multi-variant\r\n hasRelatedInfoSheets: false, // If there are related information sheets\r\n\r\n stockMasterStockItemNo: 0,\r\n stockStockItemNo: 0, // Stock stockItemNo\r\n stockDescription: '', // Stock description\r\n stockStockNo: '', // (Selected) stock stockNo (product code)\r\n stockWeight: 0, // (Selected) stock weight\r\n stockDimensions: '', // (Selected) stock dimensions\r\n stockQtyOnHand: 0, // (Selected) stock item quantity on hand\r\n stockNonDepletingItem: false, \r\n stockSellingPriceDisplayString: '',\r\n stockSpecialPriceDisplayString: '',\r\n stockOnSpecial: false,\r\n stockBuildItem: false,\r\n fromPriceDisplayString: '',\r\n hasDifferentVariantPricing: false,\r\n\r\n // Total price (used for products with options)\r\n totalPriceDisplayString: '',\r\n\r\n // Terms and Conditions\r\n agreeTermsAndConditionsUrl: '',\r\n termsAttachmentDescription: '',\r\n termsAttachmentDownloadName: '',\r\n termsAttachmentFileSizeDisplayString: '',\r\n\r\n // Stock item attachment fields\r\n downloadItem: false,\r\n downloadID: 0,\r\n dateTransactionDisplayString: '',\r\n downloadExpiresOnDisplayString: '',\r\n downloadExpired: false,\r\n\r\n unitsMeasure: '',\r\n classDescription1: '',\r\n classDescription2: '',\r\n classDescription3: '',\r\n classDescription4: '',\r\n classDescription5: '',\r\n classDescription6: '',\r\n classDescription7: '',\r\n classDescription8: '',\r\n classDescription9: '',\r\n classDescription10: '',\r\n classDescription11: '',\r\n classDescription12: '',\r\n classWebInclude1: false,\r\n classWebInclude2: false,\r\n classWebInclude3: false,\r\n classWebInclude4: false,\r\n classWebInclude5: false,\r\n classWebInclude6: false,\r\n classWebInclude7: false,\r\n classWebInclude8: false,\r\n classWebInclude9: false,\r\n classWebInclude10: false,\r\n classWebInclude11: false,\r\n classWebInclude12: false,\r\n className1: '',\r\n className2: '',\r\n className3: '',\r\n className4: '',\r\n className5: '',\r\n className6: '',\r\n className7: '',\r\n className8: '',\r\n className9: '',\r\n className10: '',\r\n className11: '',\r\n className12: '',\r\n\r\n quantityAutoNumeric: null\r\n };\r\n },\r\n computed: {\r\n filteredStockImages() {\r\n var stockImages = [];\r\n\r\n if (this.appData == null) {\r\n return stockImages;\r\n }\r\n\r\n // Check if we should return multi-variant images or images for a single-variant\r\n if (this.isMultiVariant && this.appData.multiVariant.stockImages != null) {\r\n stockImages = this.appData.multiVariant.stockImages;\r\n\r\n // Filter images to the selected colour and size\r\n var filteredImages = stockImages.filter(x => (this.selectedColour == 0 && x.variantClassNo >= 0) || x.variantClassNo == 0 || x.variantClassNo == this.selectedColour);\r\n\r\n if (filteredImages.length > 0) {\r\n // Return filtered images\r\n return filteredImages;\r\n }\r\n } else if (this.selectedVariant != null && this.selectedVariant.stockImages != null) {\r\n stockImages = this.selectedVariant.stockImages;\r\n }\r\n\r\n if (stockImages == null || stockImages.length == 0) {\r\n // A placeholder image for when there's no stock image\r\n stockImages = [{\r\n imageUrl: this.appData.noImagePlaceholderLgUrl,\r\n thumbnailUrl: this.appData.noImagePlaceholderLgUrl, /* Just use the Large image here */\r\n variantClassNo: -1,\r\n variantDescription: '',\r\n variantCssClass: '',\r\n title: \"No image\"\r\n }];\r\n }\r\n\r\n return stockImages;\r\n },\r\n getMaxQuantity() {\r\n if (this.selectedVariant != null) {\r\n if (this.stockQtyOnHand > 0) {\r\n return this.stockQtyOnHand;\r\n } else if (!this.selectedVariant.stockDetails.nonDepletingItem && !this.selectedVariant.stockDetails.backOrderItem && !this.selectedVariant.stockDetails.buildItem) {\r\n return 0;\r\n }\r\n }\r\n\r\n return 2147483647;\r\n },\r\n getQuantityRegEx() {\r\n var decimalRegEx = \"\";\r\n\r\n if (this.selectedVariant != null && this.selectedVariant.stockDetails != null && this.selectedVariant.stockDetails.qtyDecimalPlaces > 0) {\r\n decimalRegEx = \"\\\\d{1,\" + this.selectedVariant.stockDetails.qtyDecimalPlaces + \"}\";\r\n }\r\n\r\n var regEx = \"^\\\\s*(?=.*[1-9])\\\\d*(?:\\\\.\" + decimalRegEx + \")?\\\\s*$\";\r\n\r\n return regEx;\r\n },\r\n hasAttachments() {\r\n if (this.selectedVariant != null && this.selectedVariant.stockAttachments != null && this.selectedVariant.stockAttachments.length > 0) {\r\n return true;\r\n }\r\n\r\n return false;\r\n },\r\n filteredStockAttachments() {\r\n return this.selectedVariant.stockAttachments ? this.selectedVariant.stockAttachments : [];\r\n },\r\n showStockAvailability() {\r\n if (this.selectedVariant != null && !this.stockNonDepletingItem && this.selectedVariant.stockStatuses != null && this.selectedVariant.stockStatuses.length > 0) {\r\n return true;\r\n }\r\n\r\n return false;\r\n },\r\n filteredStockStatuses() {\r\n return this.selectedVariant.stockStatuses ? this.selectedVariant.stockStatuses : [];\r\n },\r\n isPurchasableItem () {\r\n if (this.selectedVariant != null) {\r\n return this.hasSomeStockAvailable;\r\n } else {\r\n return true;\r\n }\r\n },\r\n isBackOrderItem() {\r\n // Check back order separate to non-depleting\r\n return (this.selectedVariant != null && !this.stockNonDepletingItem && (this.selectedVariant.stockDetails.backOrderItem || this.selectedVariant.stockDetails.buildItem) && this.stockQtyOnHand <= 0);\r\n },\r\n isBuildItem() {\r\n return (this.selectedVariant != null && this.stockBuildItem);\r\n },\r\n isPurchaseButtonDisabled() {\r\n return !this.stockNonDepletingItem && this.stockQtyOnHand <= 0 && !this.stockBuildItem;\r\n },\r\n showAddToCartButton() {\r\n return !this.hasSelectedVariant() || this.stockQtyOnHand > 0 || this.stockNonDepletingItem || this.stockBuildItem;\r\n }\r\n },\r\n methods: {\r\n forceRerender() {\r\n //console.log('forceRerender');\r\n this.componentKey += 1;\r\n },\r\n stockWeightDisplayString() {\r\n //console.log('stockWeightDisplayString');\r\n if (this.stockWeight == 0) {\r\n return '';\r\n }\r\n\r\n // Format stock weight to 3 decimal places\r\n var decimalPlaces = 3;\r\n return this.stockWeight.toLocaleString(undefined, { minimumFractionDigits: decimalPlaces, maximumFractionDigits: decimalPlaces }) + ' kg';\r\n },\r\n hasSelectedVariant() {\r\n //console.log('hasSelectedVariant');\r\n if (this.selectedVariant != null) {\r\n return true;\r\n }\r\n\r\n return false;\r\n },\r\n clearSelectedVariant() {\r\n //console.log('clearSelectedVariant');\r\n // Clear the selected variant\r\n this.selectedVariant = null;\r\n // Restore the default stock values\r\n this.restoreDefaultStockValues();\r\n },\r\n setSelectedColour(classNo) {\r\n //console.log('setSelectedColour', { classNo: classNo });\r\n // Destroy the current carousel\r\n xpectWeb.productDetails.destroyProductImagesCarousel();\r\n\r\n // Indicate we need to reinitialise the carousel\r\n xpectWeb.productDetails.productImageSliderNeedsRefresh = true;\r\n\r\n // Update the selected colour\r\n this.selectedColour = classNo;\r\n var colourDetails = this.appData.multiVariant.colours.find(x => x.classNo == this.selectedColour);\r\n\r\n if (typeof colourDetails !== 'undefined') {\r\n this.selectedColourDescription = colourDetails.description;\r\n } else {\r\n this.selectedColourDescription = '';\r\n }\r\n\r\n // Force a re-render so the v-for's render correctly\r\n this.forceRerender();\r\n },\r\n setSelectedVariant(singleVariant) {\r\n //console.log('setSelectedVariant', { singleVariant: singleVariant });\r\n // Set the selected stock item\r\n this.selectedVariant = singleVariant;\r\n\r\n this.setDefaultPropertiesFromVariant(singleVariant);\r\n },\r\n\r\n setDefaultPropertiesFromVariant(singleVariant) {\r\n //console.log('setDefaultPropertiesFromVariant', { singleVariant: singleVariant });\r\n if (!singleVariant) {\r\n return;\r\n }\r\n\r\n var stockDetails = singleVariant.stockDetails;\r\n\r\n if (!stockDetails) {\r\n return;\r\n }\r\n\r\n // Set the helper properties\r\n this.stockMasterStockItemNo = stockDetails.masterStockItemNo;\r\n this.stockStockNo = stockDetails.stockNo;\r\n this.stockStockItemNo = stockDetails.stockItemNo;\r\n this.stockDescription = stockDetails.description;\r\n this.stockWeight = stockDetails.weight;\r\n this.stockDimensions = stockDetails.dimensions;\r\n this.stockQtyOnHand = stockDetails.qtyOnHand;\r\n this.stockNonDepletingItem = stockDetails.nonDepletingItem;\r\n this.stockSellingPriceDisplayString = stockDetails.sellingPriceDisplayString;\r\n this.stockSpecialPriceDisplayString = stockDetails.specialPriceDisplayString;\r\n this.stockOnSpecial = stockDetails.onSpecial;\r\n this.stockBuildItem = stockDetails.buildItem;\r\n this.fromPriceDisplayString = '';\r\n this.hasDifferentVariantPricing = false;\r\n\r\n this.totalPriceDisplayString = stockDetails.specialPriceDisplayString;\r\n\r\n this.downloadItem = stockDetails.downloadItem ? stockDetails.downloadItem : false;\r\n this.downloadID = stockDetails.downloadID ? stockDetails.downloadID : 0;\r\n this.dateTransactionDisplayString = stockDetails.dateTransactionDisplayString;\r\n this.downloadExpiresOnDisplayString = stockDetails.downloadExpiresOnDisplayString;\r\n this.downloadExpired = stockDetails.downloadExpired;\r\n\r\n this.agreeTermsAndConditionsUrl = stockDetails.agreeTermsAndConditionsUrl;\r\n this.termsAttachmentDescription = stockDetails.termsAttachmentDescription;\r\n this.termsAttachmentDownloadName = stockDetails.termsAttachmentDownloadName;\r\n this.termsAttachmentFileSizeDisplayString = stockDetails.termsAttachmentFileSizeDisplayString;\r\n\r\n this.unitsMeasure = stockDetails.unitsMeasure;\r\n this.classDescription1 = stockDetails.classDescription1;\r\n this.classDescription2 = stockDetails.classDescription2;\r\n this.classDescription3 = stockDetails.classDescription3;\r\n this.classDescription4 = stockDetails.classDescription4;\r\n this.classDescription5 = stockDetails.classDescription5;\r\n this.classDescription6 = stockDetails.classDescription6;\r\n this.classDescription7 = stockDetails.classDescription7;\r\n this.classDescription8 = stockDetails.classDescription8;\r\n this.classDescription9 = stockDetails.classDescription9;\r\n this.classDescription10 = stockDetails.classDescription10;\r\n this.classDescription11 = stockDetails.classDescription11;\r\n this.classDescription12 = stockDetails.classDescription12;\r\n this.classWebInclude1 = stockDetails.classWebInclude1;\r\n this.classWebInclude2 = stockDetails.classWebInclude2;\r\n this.classWebInclude3 = stockDetails.classWebInclude3;\r\n this.classWebInclude4 = stockDetails.classWebInclude4;\r\n this.classWebInclude5 = stockDetails.classWebInclude5;\r\n this.classWebInclude6 = stockDetails.classWebInclude6;\r\n this.classWebInclude7 = stockDetails.classWebInclude7;\r\n this.classWebInclude8 = stockDetails.classWebInclude8;\r\n this.classWebInclude9 = stockDetails.classWebInclude9;\r\n this.classWebInclude10 = stockDetails.classWebInclude10;\r\n this.classWebInclude11 = stockDetails.classWebInclude11;\r\n this.classWebInclude12 = stockDetails.classWebInclude12;\r\n this.className1 = stockDetails.className1;\r\n this.className2 = stockDetails.className2;\r\n this.className3 = stockDetails.className3;\r\n this.className4 = stockDetails.className4;\r\n this.className5 = stockDetails.className5;\r\n this.className6 = stockDetails.className6;\r\n this.className7 = stockDetails.className7;\r\n this.className8 = stockDetails.className8;\r\n this.className9 = stockDetails.className9;\r\n this.className10 = stockDetails.className10;\r\n this.className11 = stockDetails.className11;\r\n this.className12 = stockDetails.className12;\r\n\r\n if (this.quantityAutoNumeric != null) {\r\n // Remove existing AutoNumeric\r\n this.quantityAutoNumeric.remove();\r\n }\r\n\r\n // Configure default options\r\n var options = {\r\n minimumValue: \"0\",\r\n maximumValue: \"2147483647\",\r\n wheelOn: \"hover\",\r\n wheelStep: \"1\",\r\n emptyInputBehavior: \"min\",\r\n allowDecimalPadding: 'floats',\r\n };\r\n\r\n // Set the decimal places from the stock details\r\n options.decimalPlaces = stockDetails.qtyDecimalPlaces;\r\n\r\n // Set up the AutoNumeric quantity\r\n var qty = $(\"#quantity\")[0];\r\n\r\n if (qty) {\r\n this.quantityAutoNumeric = new AutoNumeric(qty, [options, \"numericPos\"]);\r\n }\r\n },\r\n restoreDefaultStockValues() {\r\n //console.log('restoreDefaultStockValues');\r\n if (typeof this.appData === 'undefined') {\r\n return;\r\n }\r\n\r\n if (this.appData.multiVariant != null && this.appData.multiVariant.stockDetails != null) {\r\n var multiVariantStockDetails = this.appData.multiVariant.stockDetails;\r\n\r\n // Restore default values with multi-variant stock details if available\r\n this.stockMasterStockItemNo = multiVariantStockDetails.masterStockItemNo;\r\n this.stockStockNo = multiVariantStockDetails.masterStockNo; // TODO: Perhaps name this just stockNo\r\n this.stockStockItemNo = 0;\r\n this.stockDescription = multiVariantStockDetails.description;\r\n this.stockWeight = multiVariantStockDetails.weight;\r\n this.stockDimensions = multiVariantStockDetails.dimensions;\r\n this.stockQtyOnHand = 0;\r\n this.stockNonDepletingItem = false;\r\n this.stockSellingPriceDisplayString = multiVariantStockDetails.sellingPriceDisplayString;\r\n this.stockSpecialPriceDisplayString = this.stockSellingPriceDisplayString; // Note: Multi-variant doesn't have a special price\r\n this.stockOnSpecial = false;\r\n this.stockBuildItem = false;\r\n this.fromPriceDisplayString = multiVariantStockDetails.fromPriceDisplayString;\r\n this.hasDifferentVariantPricing = multiVariantStockDetails.hasDifferentVariantPricing;\r\n\r\n this.totalPriceDisplayString = multiVariantStockDetails.specialPriceDisplayString;\r\n\r\n this.downloadItem = false;\r\n this.downloadID = 0;\r\n this.dateTransactionDisplayString = '';\r\n this.downloadExpiresOnDisplayString = '';\r\n this.downloadExpired = false;\r\n\r\n this.agreeTermsAndConditionsUrl = '';\r\n this.termsAttachmentDescription = '';\r\n this.termsAttachmentDownloadName = '';\r\n this.termsAttachmentFileSizeDisplayString = '';\r\n\r\n this.unitsMeasure = multiVariantStockDetails.unitsMeasure;\r\n this.classDescription1 = multiVariantStockDetails.classDescription1;\r\n this.classDescription2 = multiVariantStockDetails.classDescription2;\r\n this.classDescription3 = multiVariantStockDetails.classDescription3;\r\n this.classDescription4 = multiVariantStockDetails.classDescription4;\r\n this.classDescription5 = multiVariantStockDetails.classDescription5;\r\n this.classDescription6 = multiVariantStockDetails.classDescription6;\r\n this.classDescription7 = multiVariantStockDetails.classDescription7;\r\n this.classDescription8 = multiVariantStockDetails.classDescription8;\r\n this.classDescription9 = multiVariantStockDetails.classDescription9;\r\n this.classDescription10 = multiVariantStockDetails.classDescription10;\r\n this.classDescription11 = multiVariantStockDetails.classDescription11;\r\n this.classDescription12 = multiVariantStockDetails.classDescription12;\r\n this.classWebInclude1 = multiVariantStockDetails.classWebInclude1;\r\n this.classWebInclude2 = multiVariantStockDetails.classWebInclude2;\r\n this.classWebInclude3 = multiVariantStockDetails.classWebInclude3;\r\n this.classWebInclude4 = multiVariantStockDetails.classWebInclude4;\r\n this.classWebInclude5 = multiVariantStockDetails.classWebInclude5;\r\n this.classWebInclude6 = multiVariantStockDetails.classWebInclude6;\r\n this.classWebInclude7 = multiVariantStockDetails.classWebInclude7;\r\n this.classWebInclude8 = multiVariantStockDetails.classWebInclude8;\r\n this.classWebInclude9 = multiVariantStockDetails.classWebInclude9;\r\n this.classWebInclude10 = multiVariantStockDetails.classWebInclude10;\r\n this.classWebInclude11 = multiVariantStockDetails.classWebInclude11;\r\n this.classWebInclude12 = multiVariantStockDetails.classWebInclude12;\r\n this.className1 = multiVariantStockDetails.className1;\r\n this.className2 = multiVariantStockDetails.className2;\r\n this.className3 = multiVariantStockDetails.className3;\r\n this.className4 = multiVariantStockDetails.className4;\r\n this.className5 = multiVariantStockDetails.className5;\r\n this.className6 = multiVariantStockDetails.className6;\r\n this.className7 = multiVariantStockDetails.className7;\r\n this.className8 = multiVariantStockDetails.className8;\r\n this.className9 = multiVariantStockDetails.className9;\r\n this.className10 = multiVariantStockDetails.className10;\r\n this.className11 = multiVariantStockDetails.className11;\r\n this.className12 = multiVariantStockDetails.className12;\r\n } else if (this.appData.singleVariants != null && this.appData.singleVariants.length > 0) {\r\n // Restore default values with single-variant stock details if available\r\n var singleVariant = this.appData.singleVariants[0];\r\n\r\n this.setDefaultPropertiesFromVariant(singleVariant);\r\n }\r\n },\r\n addToCartSubmitButtonClick(evt, btnJqueryId) {\r\n // Track which submit button was clicked\r\n xpectWeb.productDetails.jqClickedSubmitButton = $(btnJqueryId);\r\n\r\n //console.log('addToCartSubmitClick', { btnJqueryId: btnJqueryId, jqClickedSubmitButton: xpectWeb.productDetails.jqClickedSubmitButton, form: form });\r\n\r\n // Check terms and conditions (if required)\r\n var hasAgreed = xpectWeb.productDetails.hasAgreedTermsAndConditions();\r\n\r\n if (!hasAgreed) {\r\n xpectWeb.productDetails.checkAgreedTermsConditions();\r\n\r\n evt.preventDefault();\r\n return;\r\n }\r\n\r\n // if we can place an order for contacts then we need to show a popup to select the contact(s)\r\n if (this.appData.canPlaceOnlineOrderForContacts) {\r\n xpectWeb.utils.bootstrapModal('xpShopForContactsModal').show();\r\n this.loadShopForContactsList();\r\n return;\r\n }\r\n\r\n xpectWeb.productDetails.addToCartFormSubmit();\r\n },\r\n loadShopForContactsList() {\r\n var elem = $('#xpShopForContacts');\r\n elem.empty().append('');\r\n $.post('/umbraco/Surface/ShoppingCart/LoadShopForContacts', null, function (successResponse) {\r\n //console.log('loadShopForContactsList.success', { successResponse });\r\n elem.empty().append(successResponse);\r\n xpectWeb.productDetails.checkShopForContactsAddButton();\r\n });\r\n },\r\n productGalleryIndex(index) {\r\n return \"product-image-\" + index\r\n },\r\n productGalleryIndexHref(index) {\r\n return \"#product-image-\" + index\r\n },\r\n productImageCss(index) {\r\n return index === 0 ? \"active\" : \"\"\r\n },\r\n }\r\n },\r\n\r\n /* vue.js view model. Initialised later. */\r\n vm: undefined,\r\n\r\n productImageSlider: undefined,\r\n productThumbsSlider: undefined,\r\n productImageSliderNeedsRefresh: false,\r\n selectedCartContactNos: [],\r\n\r\n jqClickedSubmitButton: null, // Holds which submit button was clicked\r\n\r\n // Helpers for calculating a running total for products with options\r\n stockOptionGroups: [],\r\n stockOptionGroupItems: [],\r\n\r\n /* Initialises the vue app. */\r\n init: function (appData) {\r\n // console.log('init', { appData: appData });\r\n // Mount the vue.js app\r\n this.vm = Vue.createApp(this.app).mount('#form');\r\n\r\n // Assign a default AutoNumeric quantity\r\n var qty = $(\"#quantity\")[0];\r\n\r\n if (qty) {\r\n this.vm.quantityAutoNumeric = new AutoNumeric(qty, \"integerPos\");\r\n }\r\n\r\n // Initialise the view model data\r\n this.vm.appData = appData;\r\n\r\n // If there are related information sheets\r\n this.vm.hasRelatedInfoSheets = typeof appData.stockInfoSheets != null && appData.stockInfoSheets.length > 0;\r\n\r\n // If this is a multi-variant product\r\n this.vm.isMultiVariant = appData.multiVariant != null;\r\n\r\n // Restore default values\r\n this.vm.restoreDefaultStockValues();\r\n\r\n // restore selected CartContactNos (coming from the session as a csv string in the AppData)\r\n this.selectedCartContactNos = (appData.selectedCartContactNos)\r\n ? appData.selectedCartContactNos.split(',').map(x => Number(x))\r\n : [];\r\n\r\n // Check if we are using multi-variant or single-variant mode\r\n if (this.vm.isMultiVariant) {\r\n // Set up the colours and sizes\r\n this.initMultiVariant();\r\n } else {\r\n this.initSingleVariant();\r\n }\r\n },\r\n\r\n // Initialises the vue app for a multi-variant stock item\r\n initMultiVariant: function () {\r\n //console.log('initMultiVariant');\r\n // Initial setup the colours/sizes on load\r\n this.setColourEnable();\r\n this.setSizeEnable();\r\n this.selectStockItem();\r\n },\r\n\r\n // Initialises the vue app for a single-variant stock item\r\n initSingleVariant: function () {\r\n //console.log('initSingleVariant');\r\n if (this.vm.appData.singleVariants != null && this.vm.appData.singleVariants.length > 0) {\r\n var firstSingleVariant = this.vm.appData.singleVariants[0];\r\n var firstSingleVariantStockDetails = firstSingleVariant.stockDetails;\r\n\r\n // Populate lookups for calculating running total for variants with options\r\n if (typeof firstSingleVariant.stockOptionGroups !== 'undefined' && firstSingleVariant.stockOptionGroups.length > 0) {\r\n $(firstSingleVariant.stockOptionGroups).each(function (index, stockOptionGroup) {\r\n xpectWeb.productDetails.stockOptionGroups.push({ id: stockOptionGroup.id, selectType: stockOptionGroup.selectType });\r\n\r\n $(stockOptionGroup.stockOptionGroupItems).each(function (index, stockOptionGroupItem) {\r\n xpectWeb.productDetails.stockOptionGroupItems.push({\r\n id: stockOptionGroupItem.id,\r\n stockOptionGroupID: stockOptionGroup.id,\r\n sellingPriceInc: xpectWeb.utils.convertCurrency(\r\n (stockOptionGroupItem.localSellingPriceInc ? stockOptionGroupItem.localSellingPriceInc : 0),\r\n (stockOptionGroupItem.localSellingPriceEx ? stockOptionGroupItem.localSellingPriceEx : 0),\r\n (stockOptionGroupItem.sellingPriceInc ? stockOptionGroupItem.sellingPriceInc : 0),\r\n (stockOptionGroupItem.sellingPriceEx ? stockOptionGroupItem.sellingPriceEx : 0),\r\n xpectWeb.productDetails.vm.appData.customerCurrency,\r\n xpectWeb.productDetails.vm.appData.displayCurrencyPreference)\r\n });\r\n });\r\n });\r\n }\r\n\r\n if (firstSingleVariantStockDetails != null) {\r\n // Check stock availability\r\n this.vm.hasSomeStockAvailable = (firstSingleVariantStockDetails.qtyOnHand > 0) || firstSingleVariantStockDetails.backOrderItem || firstSingleVariantStockDetails.buildItem || firstSingleVariantStockDetails.nonDepletingItem;\r\n\r\n // If we don't have a multi-variant and there is at least one single-variant then set the selected variant to the first variant\r\n this.vm.setSelectedVariant(firstSingleVariant);\r\n }\r\n }\r\n },\r\n\r\n initProductSliders: function () {\r\n //console.log('initProductSliders');\r\n /*\r\n * Product Images Slider\r\n */\r\n this.initProductImagesSlider();\r\n },\r\n\r\n initProductImagesSlider: function () {\r\n //console.log('initProductImagesSlider');\r\n\r\n if ($(\".product-gallery\").length == 0) {\r\n return;\r\n }\r\n\r\n // Initialise product gallery\r\n if (typeof xpectLib === 'object' && typeof xpectLib.default === 'object' && typeof xpectLib.default.productGallery === 'function') {\r\n xpectLib.default.productGallery();\r\n }\r\n\r\n // Initialise zoom\r\n if (typeof xpectLib === 'object' && typeof xpectLib.default === 'object' && typeof xpectLib.default.imageZoom === 'function') {\r\n xpectLib.default.imageZoom();\r\n }\r\n\r\n //if (typeof this.productThumbsSlider === 'undefined') {\r\n // this.productThumbsSlider = new Swiper('.gallery-thumbs', {\r\n // spaceBetween: 10,\r\n // slidesPerView: 5,\r\n // loop: false,\r\n // freeMode: true,\r\n // watchSlidesVisibility: true,\r\n // watchSlidesProgress: true,\r\n // });\r\n //}\r\n\r\n //if (typeof this.productImageSlider === 'undefined') {\r\n // this.productImageSlider = new Swiper('.gallery-top', {\r\n // zoom: true,\r\n // spaceBetween: 10,\r\n // loop: true,\r\n // thumbs: {\r\n // swiper: this.productThumbsSlider,\r\n // },\r\n // });\r\n\r\n // // Disable swiping when zoomed\r\n // this.productImageSlider.on('zoomChange', function (swiper, scale, imageEl, slideEl) {\r\n // this.allowTouchMove = scale === 1;\r\n // });\r\n //}\r\n\r\n // If an image exists for this colour display it\r\n //this.selectImage(this.vm.selectedColour);\r\n this.selectGalleryImage(this.vm.selectedColour);\r\n },\r\n\r\n destroyProductImagesCarousel: function () {\r\n //console.log('destroyProductImagesCarousel');\r\n if (typeof xpectWeb.productDetails.productImageSlider !== 'undefined') {\r\n xpectWeb.productDetails.productImageSlider.destroy(true, true);\r\n xpectWeb.productDetails.productImageSlider = undefined;\r\n }\r\n\r\n if (typeof xpectWeb.productDetails.productThumbsSlider !== 'undefined') {\r\n xpectWeb.productDetails.productThumbsSlider.destroy(true, true);\r\n xpectWeb.productDetails.productThumbsSlider = undefined;\r\n }\r\n },\r\n\r\n // Switch the main image\r\n selectImage: function (classNo) {\r\n //console.log('selectImage');\r\n var productImageIndex = xpectWeb.productDetails.vm.filteredStockImages.findIndex(x => x.variantClassNo == classNo);\r\n\r\n if (productImageIndex > 0) {\r\n this.productImageSlider.slideTo(productImageIndex + 1);\r\n }\r\n },\r\n\r\n // Switch the gallery image\r\n selectGalleryImage: function (classNo) {\r\n var $thumbnailImages = $(\".product-gallery .product-gallery-thumblist-item img\");\r\n\r\n if ($thumbnailImages.length > 0) {\r\n var productImageIndex = xpectWeb.productDetails.vm.filteredStockImages.findIndex(x => x.variantClassNo == classNo);\r\n\r\n if (classNo > 0 && productImageIndex > 0 && productImageIndex < $thumbnailImages.length) {\r\n $($thumbnailImages[productImageIndex]).click();\r\n }\r\n }\r\n },\r\n\r\n // Select a colour\r\n selectColour: function (classNo) {\r\n //console.log('selectColour');\r\n // Ignore selection if not available\r\n if (this.isAvailable(classNo, this.vm.selectedSize)) {\r\n // If this colour has already been selected, set the selection to 0\r\n if (this.vm.selectedColour === classNo) {\r\n this.vm.setSelectedColour(0);\r\n } else {\r\n this.vm.setSelectedColour(classNo);\r\n }\r\n\r\n this.setColourEnable();\r\n this.setSizeEnable(); // Disable sizes that are out of stock\r\n this.selectStockItem();\r\n }\r\n },\r\n\r\n // Select specified size\r\n selectSize: function (size) {\r\n //console.log('selectSize');\r\n // Ignore selection if not available\r\n if (this.isAvailable(this.vm.selectedColour, size)) {\r\n // If this size has already been selected, set the selection to 0\r\n if (this.vm.selectedSize === size) {\r\n this.vm.selectedSize = 0;\r\n } else {\r\n this.vm.selectedSize = size;\r\n }\r\n\r\n this.setColourEnable(); // Disable colours that are out of stock\r\n this.setSizeEnable();\r\n this.selectStockItem();\r\n }\r\n },\r\n\r\n // Select the stock item for the selected colour and size\r\n selectStockItem: function () {\r\n //console.log('selectStockItem');\r\n var j = 0;\r\n var noStock = this.vm.appData.singleVariants.length;\r\n this.vm.clearSelectedVariant();\r\n\r\n while (j < noStock) {\r\n var singleVariant = this.vm.appData.singleVariants[j];\r\n var stockDetails = singleVariant.stockDetails;\r\n\r\n if ((stockDetails.classNo4 == this.vm.selectedColour) && (stockDetails.classNo5 == this.vm.selectedSize)) {\r\n this.vm.setSelectedVariant(singleVariant);\r\n\r\n return;\r\n }\r\n\r\n j++;\r\n }\r\n\r\n // If we didn't select an item restore the default\r\n this.vm.restoreDefaultStockValues();\r\n },\r\n\r\n // Set colour button enable states based on available stock\r\n setColourEnable: function () {\r\n //console.log('setColourEnable');\r\n this.vm.hasSomeStockAvailable = false;\r\n\r\n if (typeof (this.vm.appData.multiVariant.colours) === 'undefined' || this.vm.appData.multiVariant.colours == null) {\r\n return;\r\n }\r\n\r\n // Disable colours that are out of stock\r\n var noColours = this.vm.appData.multiVariant.colours.length;\r\n for (var i = 0; i < noColours; i++) {\r\n var thisColour = this.vm.appData.multiVariant.colours[i].classNo;\r\n var colourVariant = $(\"#xp-colour-variant-\" + thisColour);\r\n\r\n // Check availability\r\n if (this.isAvailable(thisColour, this.vm.selectedSize)) {\r\n this.vm.hasSomeStockAvailable = true;\r\n colourVariant.removeClass(\"xp-unavailable\");\r\n\r\n // If this colour is selected then keep the selection\r\n if (thisColour == this.vm.selectedColour) {\r\n colourVariant.addClass(\"xp-selected\");\r\n } else {\r\n colourVariant.removeClass(\"xp-selected\");\r\n }\r\n } else {\r\n // If this colour was selected in a different size then deselect it as it is not available\r\n if (thisColour == this.vm.selectedColour) {\r\n this.vm.setSelectedColour(0);\r\n }\r\n\r\n colourVariant.removeClass(\"xp-selected\");\r\n colourVariant.addClass(\"xp-unavailable\");\r\n }\r\n }\r\n },\r\n\r\n // Set size button enable states based on available stock\r\n setSizeEnable: function () {\r\n //console.log('setSizeEnable');\r\n this.vm.hasSomeStockAvailable = false;\r\n\r\n if (typeof (this.vm.appData.multiVariant.sizes) === 'undefined' || this.vm.appData.multiVariant.sizes == null) {\r\n return;\r\n }\r\n\r\n // Disable sizes that are out of stock\r\n var noSizes = this.vm.appData.multiVariant.sizes.length;\r\n for (var i = 0; i < noSizes; i++) {\r\n var thisSize = this.vm.appData.multiVariant.sizes[i];\r\n var sizeVariant = $(\"#xp-size-variant-\" + thisSize);\r\n\r\n // Check availability\r\n if (this.isAvailable(this.vm.selectedColour, thisSize)) {\r\n this.vm.hasSomeStockAvailable = true;\r\n sizeVariant.removeClass(\"xp-unavailable\");\r\n\r\n // If this size is selected then keep the selection\r\n if (thisSize == this.vm.selectedSize) {\r\n sizeVariant.addClass(\"xp-selected\");\r\n } else {\r\n sizeVariant.removeClass(\"xp-selected\");\r\n }\r\n } else {\r\n // If this size was selected in a different colour then deselect it as it is not available\r\n if (thisSize == this.vm.selectedSize) {\r\n this.vm.selectedSize = 0;\r\n }\r\n\r\n sizeVariant.removeClass(\"xp-selected\");\r\n sizeVariant.addClass(\"xp-unavailable\");\r\n }\r\n }\r\n },\r\n\r\n // Is this colour and size available. Use 0 or -1 to ignore a parameter.\r\n isAvailable: function (colour, size) {\r\n //console.log('isAvailable');\r\n var j = 0;\r\n var isAvailable = false;\r\n var noStock = this.vm.appData.singleVariants.length;\r\n\r\n while (j < noStock) {\r\n var singleVariant = this.vm.appData.singleVariants[j];\r\n var stockDetails = singleVariant.stockDetails;\r\n\r\n if ((colour <= 0 || stockDetails.classNo4 == colour) && (size <= 0 || stockDetails.classNo5 == size)) {\r\n isAvailable = (stockDetails.qtyOnHand > 0) || stockDetails.backOrderItem || stockDetails.buildItem || stockDetails.nonDepletingItem;\r\n\r\n // If looking for a specific colour\r\n if (colour > 0 && size > 0) {\r\n break;\r\n } else if (isAvailable) {\r\n break;\r\n }\r\n }\r\n\r\n j++;\r\n }\r\n\r\n return isAvailable;\r\n },\r\n\r\n // submit the add-to-cart form\r\n addToCartFormSubmit: function () {\r\n //console.log('addToCartFormSubmit', { cartContactNos: this.selectedCartContactNos });\r\n if (this.selectedCartContactNos.length > 0) {\r\n $('#cartContactNos').val(this.selectedCartContactNos.join(','));\r\n }\r\n else {\r\n $('#cartContactNos').val('');\r\n }\r\n $('#form').find('form').submit();\r\n },\r\n\r\n /*\r\n * AJAX form submission, callbacks, and alerts\r\n */\r\n getSubmitSuccessMessage: function () {\r\n // Default\r\n return '