/* Minification failed. Returning unminified contents.
(1600,13-32): run-time error JS1300: Strict-mode does not allow assignment to undefined variables: initialLoadComplete
(1402,9-23): run-time error JS1300: Strict-mode does not allow assignment to undefined variables: defaultsLoaded
(1378,21-36): run-time error JS1300: Strict-mode does not allow assignment to undefined variables: defaultProducts
(1372,17-32): run-time error JS1300: Strict-mode does not allow assignment to undefined variables: defaultProducts
(1368,17-28): run-time error JS1300: Strict-mode does not allow assignment to undefined variables: forceChoice
(1163,9-23): run-time error JS1300: Strict-mode does not allow assignment to undefined variables: defaultsLoaded
(1161,9-24): run-time error JS1300: Strict-mode does not allow assignment to undefined variables: defaultProducts
(977,9-20): run-time error JS1300: Strict-mode does not allow assignment to undefined variables: forceChoice
(389,13-28): run-time error JS1300: Strict-mode does not allow assignment to undefined variables: defaultProducts
(116,9-18): run-time error JS1300: Strict-mode does not allow assignment to undefined variables: available
 */
var EcommErrorStrings = null;

// Defines a Lexicon class.
// A Lexicon class maintains a hash mapping keywords to translations.
function Lexicon(data) {
    var self = this;
    var initialize = function (languageKey, lexica) {
        self.LanguageKey = languageKey;
        self.Lexica = {};

        if (lexica) {
            $.each(lexica, function (index, lexeme) {
                self.Lexica[lexeme.KeyWord] = lexeme.Translation;
            });
        }
    };

    if (data) {
        initialize(data.LanguageKey, data.Lexica);
    } else {
        initialize(null, null);
    }
}

// Given a key, return its translation.
Lexicon.prototype.translate = function (key) {
    if (EcommErrorStrings === null) {
        return key;
    } else {
        return this.Lexica[key];
    }
};

// Given a JS object representing the server's model for a string resource file,
// creates an instance of Lexicon and makes EcommErrorStrings point to that lexicon.
// EcommErrorStrings is a global variable.
function SetErrorStrings(data) {
    EcommErrorStrings = new Lexicon(data);
}

// Given a key, if there is a translation available, return that translation,
// otherwise, return they key.
function TranslateErrorString(key) {
    if (EcommErrorStrings) {
        return EcommErrorStrings.translate(key);
    }

    return key;
}
;
function convertThisTimeStamp(dateToConvert, language) {
    var parsedDateTime;
    var year, month, day;
    if (language === null) {
        language = "en-us";
    }

    language = language.toLowerCase();

    if (language === "de-de") {
        parsedDateTime = dateToConvert.split(".");
        year = parsedDateTime[2];
        day = parsedDateTime[0];
        month = parsedDateTime[1];

    } else if (language === "fr-ca") {
        parsedDateTime = dateToConvert.split("-");
        year = parsedDateTime[0];
        day = parsedDateTime[2];
        month = parsedDateTime[1];
    } else if (language === "en-us") {
        parsedDateTime = dateToConvert.split("/");
        year = parsedDateTime[2];
        day = parsedDateTime[1];
        month = parsedDateTime[0];
    } else {
        //european date format
        parsedDateTime = dateToConvert.split("/");
        year = parsedDateTime[2];
        day = parsedDateTime[0];
        month = parsedDateTime[1];
    }
    
    return new Date(year, (month -1), day);
};
/* jshint browser:true, eqnull:true, devel:true, debug:true */
/* globals 
		jQuery, 
		available, unavailableDates, defaultProducts, defaultsLoaded, componentsJSON, startDate, endDate, language, salesId, adultCount, childCount, childAges, packageId, los, forceChoice, selectText, componentNotAvailable, alternateDatesJSON, packageDisplayType, saveText, packagePriceText, sessionId, packageNA, childAgesLabel, promoCode, arrivalDate, dateActive, dateInactive, bookFromDate, bookUntilDate, minDate, path,
   	Inntopia, currency, inntopiaUpsell, convertThisTimeStamp, TranslateErrorString, setDefaultsCookie, Globalize, localizeDatepicker
*/

// Globals directive order by line
//      Libraries
//      Global variables and functions defined by the .cshtml file
//      Global variables and functions defined in other Inntopia js files

// inntopia.js has to be loaded first because it overwrites the Inntopia namespace
Inntopia.PackageBuilder = (function ($) {
    "use strict";

    var isoDateFormat = "yyyy-MM-dd";
    var self = {};
    var readyQueue = [];
    var products = [];
    var loadedComponents = {};
    var enforceQuantityRules = false;
    var currentActProducts = [];
    // Private Helpers
    function notAvailable() {
        $(".alert-error").hide();
        $("#packageComponents").hide();
        $("#cta").hide();
        $("#optionalComponents").hide();
        $("#packageDescription").append('<div class="alert alert-error" name="packageNotAvailable">' + packageNA + '</div>');
        available = false;
    }

    class basicProduct {
        constructor(productId, supplierId) {
            this.productId = productId;
            this.supplierId = supplierId;
        }

        get getProductId() {
            return this.productId;
        }

        get getSupplierId() {
            return this.supplierId;
        }
    }

    function setLodgingAvailability(supplierId, productId, componentIndex, productRow) {
        var availableQuantity = $(productRow).data('availableQuantity');
        // don't check more than once per product per page load
        if (!availableQuantity) {
            $.ajax({
                type: 'GET',
                url: '/ecomm/JSON/ProductPriceAvailability',
                cache: false,
                data: {
                    SalesId: salesId,
                    SupplierId: supplierId,
                    StartDate: startDate,
                    EndDate: endDate,
                    AdultCount: adultCount,
                    Quantity: 1,
                    ProductId: productId,
                    ChildAges: childAges,
                    PackageId: packageId,
                    PackageComponentId: componentIndex,
                    Language: language
                }
            }).done(function (data) {
                    if (data[0]) {
                        var availableQuantity = data[0].Quantity;
                        $(productRow).data('availableQuantity', availableQuantity);
                        updateQuantityDropdown(componentIndex, availableQuantity);
                    }
                })
                .fail(function (error) {
                    console.log(error);
                })
                .always(function () {
                    // even if we fail to validate quantity, let them book at least one
                    $('.btn-large[name=buyNow]').removeAttr('disabled');
                });
        } else {
            updateQuantityDropdown();
        }
    }

    function updateQuantityDropdown(componentIndex, availableQuantity) {
        var minQuantity = $('#minQty-' + componentIndex).val(),
            maxQuantity = $('#maxQty-' + componentIndex).val(),
            absoluteMax = availableQuantity > maxQuantity ? maxQuantity : availableQuantity,
            quantityOptions = '';
        var currentQty = $('#qty-' + componentIndex).val();

        for (var q = minQuantity; q <= absoluteMax; q++) {
            var displayVal = q < 10 ? '0' + q : q;

            quantityOptions += '<option value="' + q + '">' + displayVal + '</option>';
        }
        var $qty = $('#qty-' + componentIndex)
        $qty.removeAttr('disabled').html(quantityOptions);
        if (currentQty > 1) {
            $qty.val(currentQty);
        }
        updatePackagePrices();
    }

    self.selectComponent = function (componentIndex, supplierId, productId, lowestPricedProductId, lowestPricedProductName, thisProdPrice, thisOriginalProdPrice) {
        $("#moreInfo-" + componentIndex).show();
        $("#priceDisplay-" + componentIndex).show();

        var qty = $('#qty-' + componentIndex).val();
        var categoryId = $("#categoryId-" + componentIndex).val();
        var thisItem;
        var thisLocationId;
        var thisProductName;
        var thisLocationDescription;
        var thisIMG;
        var thisLocationName;
        var thisPrice;
        var thisOriginalPrice;

        if (supplierId == null) {
            supplierId = $("#supplierId-" + componentIndex).val();
        }
        if (productId == null) {
            productId = $("#productId-" + componentIndex).val();
        }

        if (categoryId == 1) {
            thisItem = $('#supplierList-' + componentIndex + ' tr[data-supplier-id="' + supplierId + '"][data-product-id="' + productId + '"]');
            thisLocationId = $(thisItem).data('locationId');
            thisProductName = $(thisItem).data('productName');
            thisLocationDescription = $("#supplierDescription_" + thisLocationId).html();
            thisIMG = $("#locationImage_" + thisLocationId).html();
            thisLocationName = $('#supplierTile_' + thisLocationId).data('name');
            thisPrice = $(thisItem).data('price');
            thisOriginalPrice = $(thisItem).data('originalPrice');
            if (enforceQuantityRules) {
                setLodgingAvailability(supplierId, productId, componentIndex, this);
            }
        }
        else {
            thisItem = $('#supplierList-' + componentIndex + ' li[data-supplier-id="' + supplierId + '"][data-product-id="' + productId + '"]');
            thisLocationDescription = $("#description-" + supplierId + '-' + productId).html();
            // if product in group, grab image from group leader
            let imageProductId = thisItem.data('group-leader-id') != '' ? thisItem.data('group-leader-id') : productId;
            thisIMG = $("#img-" + supplierId + '-' + imageProductId).html();

            if (thisItem.length == 0) {
                thisItem = $('#productList tr[data-supplier-id="' + supplierId + '"][data-product-id="' + productId + '"]');
                thisLocationDescription = $("#productList").find("[id='productDescription-" + supplierId + "-" + productId + "']").html();

                let newImage = $("#productList").find("img").first();
                // if no image available, grab image from group leader
                if (newImage.length == 0) {
                    let groupID = $("#groupId-" + componentIndex).val();
                    let $leaderElement = $('#supplierList-' + componentIndex + ' li[data-supplier-id="' + supplierId + '"][data-group-id="' + groupID + '"]');
                    let groupLeaderID = $leaderElement.data('group-leader-id');
                    newImage = $("#img-" + supplierId + '-' + groupLeaderID).html();
                }
                thisIMG = newImage;
            }
            thisProductName = $(thisItem).data('name');
            var thisQty = $("#qty-" + supplierId + "-" + productId + "- :selected").text();

            if (Number(thisQty) != 0) {
                qty = thisQty;
            }

            if ((lowestPricedProductId != '') && (lowestPricedProductId != null)) {
                productId = lowestPricedProductId;
                thisProductName = lowestPricedProductName;
            }

            thisLocationName = $(thisItem).data('supplierName');

            if (thisProdPrice != null) {
                thisPrice = thisProdPrice;
            }
            else {
                thisPrice = $(thisItem).data('price');
            }

            if (thisOriginalProdPrice != null) {
                thisOriginalPrice = thisOriginalProdPrice;
            }
            else {
                thisOriginalPrice = $(thisItem).data('originalPrice');
            }

        }
        var groupId = thisItem.data("groupId");
        //catch any non-US number formatting
        if (thisPrice != null) {
            var thisPriceString = thisPrice.toString();
            thisPrice = Number(thisPriceString.replace(',', '.'));
        }

        if (thisOriginalPrice != null) {
            var thisOriginalPriceString = thisOriginalPrice.toString();
            thisOriginalPrice = Number(thisOriginalPriceString.replace(',', '.'));
        }

        if (categoryId == 1) {
            thisPrice = (thisPrice * los);
            thisOriginalPrice = (thisOriginalPrice * los);

            thisPrice = (thisPrice / qty);
            thisOriginalPrice = (thisOriginalPrice / qty);
        }

        $('#qty-' + componentIndex).val(qty);
        $('#qtyDisplay-' + componentIndex).html(qty);
        $('#supplierId-' + componentIndex).val(supplierId);
        $('#productId-' + componentIndex).val(productId);
        $('#groupId-' + componentIndex).val(groupId);
        $('#price-' + componentIndex).text(formatPriceCurrencyWrapper(thisPrice)).attr('data-price', thisPrice);
        $('#originalPrice-' + componentIndex).text(formatPriceCurrencyWrapper(thisOriginalPrice)).attr('data-original-price', thisOriginalPrice);

        if ((packageDisplayType == 2) && (Number(thisPrice) == Number(thisOriginalPrice))) {
            $('#originalPrice-' + componentIndex).parent().hide();
        } else if (packageDisplayType == 1) {
            $('#price-' + componentIndex).parent().hide();
        }
        else {
            $('#originalPrice-' + componentIndex).parent().show();
        }
        $('#locationName-' + componentIndex).html(thisLocationName);
        $('#productName-' + componentIndex).html(thisProductName);

        $('#locationDescription-' + componentIndex).html(thisLocationDescription);
        $('#locationIMG-' + componentIndex).html(thisIMG);
        $('#productListModal').modal('hide');
        $('#componentOptionsAdd-' + componentIndex).hide();
        $('#componentOptionsOpener-' + componentIndex).show();
        $('#componentOptionsDelete-' + componentIndex).show();
        $('#priceDisplay-' + componentIndex).show();
        $('#moreInfo-' + componentIndex).show();

        updatePackagePrices();
        self.showComponentOptions(componentIndex, false);
    }

    function getLodgingTile(supplierId, componentId, componentIndex) {
        var quantity = $("#qty-" + componentIndex).val();
        return $.ajax({
            type: "GET",
            url: "/Ecomm/Listings/SupplierTile/" + salesId + '/' + language,
            cache: false,
            data: {
                supplierId: supplierId,
                arrivalDate: startDate,
                departureDate: endDate,
                adultCount: adultCount,
                childCount: childCount,
                quantity: quantity,
                childAges: childAges,
                packageId: packageId,
                packageComponentId: componentId,
                useCarousel: false
            }
        });
    }

    function lodgingTileDoneHandler(tiles, componentIndex, supplierId) {
        var locationsAvailableCount = 0,
            locationPrices = [];

        // add all tiles 
        for (var i = 0; i < tiles.length; i++) {
            var locationId = tiles[i].locationId;

            // do not process empty tiles, which can occur due to lack of inventory
            if (tiles[i][0].trim().length > 0) {
                $('#supplierList-' + componentIndex).append(tiles[i][0]);
                $('#supplierList-' + componentIndex).find('[name="productNameDiv"]').attr('style', '');
                $('#supplierTile_' + locationId).attr('class', 'span2');
                $('#supplierTile_' + locationId).attr('style', 'width:250px');
                $('#supplierTile_' + locationId + ' a').hide();
                $('#productList_' + locationId).hide();
                $('#supplierDescription_' + locationId).show();

                // adds 'select' button to supplier tile
                var supplierTileButton = $('<button class="btn btn-primary btn-block" id="compButton-' + locationId + '-' + componentIndex + '">' + selectText + '</button>');
                $('#supplierTile_' + locationId).append(supplierTileButton);
                supplierTileButton.click(self.selectProduct.bind(null, locationId, componentIndex));

                if ($('#supplierTile_' + locationId + ' tr').length > 0) {
                    locationsAvailableCount++;
                }

                locationPrices.push({
                    price: Number($('#supplierTile_' + locationId).data('minPrice')) * los,
                    productId: $('#supplierTile_' + locationId).data('minProductId')
                });           
            }
        }

        locationPrices = locationPrices.sort(function (a, b) { return a.price < b.price ? -1 : 1 });
        var cheapestLocation = locationPrices[0];
            // if any defaults are defined we should let loadDefaults handle the product selection
            defaultProducts = defaultProducts || self.getParameterByName('defaultProducts');

        // if not first component and not force choice, ensure the cheapest (non-zero) option is displayed
        if ((forceChoice == 0 || componentIndex != 1) && defaultProducts.length == 0) {
            self.selectComponent(componentIndex, supplierId, cheapestLocation.productId);
        }

        // ?: not sure why this is needed but it is references in several other places
        var locationsQueried = tiles.length + 1;
        $("#locationsQueriedCount-" + componentIndex).val(locationsQueried);
        $("#locationsAvailableCount-" + componentIndex).val(locationsAvailableCount);
        $("div[name='InvokeWhiteSpaceWrap']").addClass('wrapText');

        // hide preloader, show component
        $("#loadingComponent-" + componentIndex).hide();
        $("#componentSelected-" + componentIndex).slideDown();
    }

    function loadSupplier(supplierId, componentIndex, componentId) {
        var categoryId = $("#categoryId-" + componentIndex).val();

        if (categoryId == 1) {
            return getLodgingTile(supplierId, componentId, componentIndex);
        }
        else {
            var thisStartDate = $("#startDate-" + componentIndex).val();
            return getActivityTile(supplierId, componentId, categoryId, thisStartDate, componentIndex, null);
        }
    }

    function isProductAdded(product) {
        return products.filter(function (item) {
            return item.productId == product.productId &&
                item.supplierId == product.supplierId &&
                item.componentIndex == product.componentIndex &&
                item.componentId == product.componentId
        }).length > 0;
    }

    function addProductsToList(componentIndex, supplierId, componentId) {

        var productIds = [];
        //construct product list
        var currentElement = $("#supplierList-" + componentIndex + " li[data-supplier-id='" + supplierId + "']");
        currentElement.each(function (index) {
            var supplierId = $(this).data('supplierId');
            var productId = $(this).data('productId');

            productIds.push(new basicProduct(productId, supplierId));
            var thisQty = 0;

            var days = componentsJSON.filter(function (c) {
                return c.componentIndex == componentIndex.toString() && c.componentId == componentId.toString()
            });

            if (days.length > 0 && days[0].availableDays) {
                var q = days[0].availableDays.filter(function (a) {
                    return a.productid == productId.toString() && a.supplierId == supplierId.toString();
                });

                if (q.length > 0 && q[0].quantity) {
                    thisQty = q[0].quantity;
                }
            }

            var product = {
                remainingQuantity: Number(thisQty),
                initialQuantity: Number(thisQty),
                usedQuantity: 0,
                productId: productId,
                supplierId: supplierId,
                componentId: componentId,
                componentIndex: componentIndex,
                index: index,
                displayed: false,
                removed: false
            }
            //do not add extra products if already added
            if (!isProductAdded(product)) {
                products.push(product);
                updateProductsWithAvailableQuantity(productId, supplierId);
            }
        });

        return productIds;
    }

    function getFilteredProducts(element, componentId, componentIndex, isSameComponentIndex, isDisplayed, isSameCompareElement, hasQuantity) {
        return products.filter(function (item) {
            return (isSameCompareElement ? item.productId == element.productId : item.productId !== element.productId) &&
                (isSameCompareElement ? item.supplierId == element.supplierId : (item.supplierId !== element.supplierId || item.supplierId == element.supplierId)) &&
                item.componentId === componentId &&
                //(isSameComponentIndex ? item.componentIndex === componentIndex : item.componentIndex !== componentIndex) &&
                (isDisplayed ? item.displayed : !item.displayed) &&
                !item.removed &&
                (hasQuantity ? item.remainingQuantity > 0 : item.remainingQuantity >= 0);
        });
    }

    function updateProductsWithAvailableQuantity(productId, supplierId) {
        var filteredProducts = products.filter(function (item) {
            return item.productId == productId && item.supplierId == supplierId;
        });
        //get minimum available quantity from products with the same productid and supplierid
        var minRemainingQuantity = Math.min.apply(Math, filteredProducts.map(function (o) {
            return o.remainingQuantity;
        }));
        //get maximum used quantity
        var maxUsedQuantity = Math.max.apply(Math, filteredProducts.map(function (o) {
            return o.usedQuantity;
        }));
        //update all of them to min remaining quantity and max used value
        filteredProducts.forEach(function (p) {
            if (minRemainingQuantity < p.remainingQuantity) {
                p.remainingQuantity = minRemainingQuantity;
            }
            if (maxUsedQuantity > p.usedQuantity) {
                p.usedQuantity = maxUsedQuantity;
            }
        });
    }

    function cleanUI(componentIndex, supplierId) {
        var results = products.filter(function (p) {
            return (!p.displayed && p.remainingQuantity == 0 && !p.removed);
        });
        results.forEach(function (item) {
            $("#supplierList-" + item.componentIndex + " li[data-supplier-id='" + supplierId + "']").each(function () {
                var obj = $(this);
                var prod = obj.data('productId');
                var supp = obj.data('supplierId');
                if (item.productId == prod && item.supplierId == supp) {
                    item.usedQuantity = item.initialQuantity - item.remainingQuantity;
                    item.removed = true;
                    //if we have products within activity tile that have 0 remaining quantity then they need to be removed
                    obj.remove();
                }
            });
        });
        //get all products that are not displayed and still have quantity
        results = products.filter(function (p) {
            return (!p.displayed && p.remainingQuantity > 0 && !p.removed);
        });
        //count how many are there
        var countedProductsOccurences = products.filter(function (p) {
            return (!p.displayed && p.remainingQuantity > 0);
        }).length;
        var totalUsedQuantity = 0;

        if (countedProductsOccurences > 0) {
            results.forEach(function (item) {
                totalUsedQuantity += item.usedQuantity;
                $("#supplierList-" + item.componentIndex + " li[data-supplier-id='" + supplierId + "']").each(function () {
                    var obj = $(this);
                    var prod = obj.data('productId');
                    var supp = obj.data('supplierId');
                    //remove products untill we have enought quantity left for all counted products
                    if (item.productId == prod && item.supplierId == supp && totalUsedQuantity >= countedProductsOccurences) {
                        obj.remove();
                        totalUsedQuantity -= 1;//decrement so we do not delete all
                        item.removed = true; //mark as removed from product list 
                    }
                });
            });
        }
    }

    function getActivityTile(supplierId, componentId, categoryId, thisStartDate, componentIndex, disableAnimation) {
        if (!useV1PackageLogic) {
            // v2 package logic requires one call per component index (due to UI limitations... componentId would be preferred)
            // v1 package logic requires one call per component index and supplier
            var propertykey = '' + componentIndex;
            if (loadedComponents.hasOwnProperty(propertykey) && loadedComponents[propertykey] === thisStartDate) {
                return $.Deferred().resolve("");
            }
            loadedComponents[propertykey] = thisStartDate;
        }

        $('#supplierList-' + componentIndex).html('');
        var selectedProductId = $("#productId-" + componentIndex).val();

        return $.ajax({
            type: 'GET',
            url: '/ecomm/Package/Activities/',
            cache: false,
            data: {
                'salesId': salesId,
                'supplierId': supplierId,   // for v2 logic this parameter is ignored
                'startDate': thisStartDate,
                'productCategoryId': categoryId,
                'language': language,
                'packageId': packageId,
                'packageComponentId': componentId,
                'useV1PackageLogic': useV1PackageLogic
            }
        });
    }

    function activityTileDoneHandler(tiles, componentIndex, componentId, categoryId) {
        var tileData = tiles.length > 0 ? tiles[0] : tiles,
            hasActivityTileData = tileData && tileData.toString().trim().length > 0,
            selectedProductId = $("#productId-" + componentIndex).val(),
            selectedGroupId = $("#groupId-" + componentIndex).val();

        if (selectedGroupId === '') {
            selectedGroupId = 0;
        } else {
            selectedGroupId = Number(selectedGroupId);
        }

        $('#supplierList-' + componentIndex).append(tileData);

        var parsedData = $('<ul>' + tileData + '</ul>');
        var activeSupplierIds = {};
        // get distinct supplier ids
        parsedData.find('li[data-supplier-id]').each(function (idx, element) {
            var supid = $(element).data('supplierId');
            activeSupplierIds['' + supid] = true;
        });

        var itemCount = 0;

        for (var localSupplierId in activeSupplierIds) {
            if (!activeSupplierIds.hasOwnProperty(localSupplierId)) {
                continue;
            }

            $("#supplierList-" + componentIndex + " li[data-supplier-id='" + localSupplierId + "']").attr('class', 'span2');
            $("#supplierList-" + componentIndex + " li[data-supplier-id='" + localSupplierId + "']").attr('style', 'width:250px');

            //each response data will be pushed to product list
            currentActProducts = addProductsToList(componentIndex, localSupplierId, componentId);

            $("#supplierList-" + componentIndex + " li[data-supplier-id='" + localSupplierId + "']").each(function () {
                var listItem = $(this);
                var previousPrice = $('#price-' + componentIndex).html();
                var currentPrice = listItem.data('price');
                var thisSupplierId = listItem.data('supplierId');
                var thisProductId = listItem.data('productId');
                var thisGroupId = listItem.data('groupId');
                var previousProductExists = true;
                var lowestPricedProductId = listItem.data('lowestPricedProductId');
                var lowestPricedProductName = listItem.data('lowestPricedProductName');
                var compareElement = { productId: thisProductId, supplierId: thisSupplierId };
                var otherDisplayedProducts = getFilteredProducts(compareElement, componentId, componentIndex, false, true, true, true);
                var currentProducts = getFilteredProducts(compareElement, componentId, componentIndex, true, false, true, true);
                var otherComponentIdProductsNotDisplayed = getFilteredProducts(compareElement, componentId, componentIndex, true, false, false, true);

                if (otherDisplayedProducts.length === 0 && currentProducts.length === 0 && thisGroupId > 0) {
                    var results = products.find(function (p) {
                        return (p.supplierId === thisSupplierId  && p.productId === thisProductId && p.componentIndex == componentIndex);
                    });

                    // This is the variable causing the inconsistent behavior, the first time it loads.
                    // It's set to false, then later code sets it to true, but after it's been removed from UI view.
                    // Don't think this is root cause still looking
                    results.removed = true;
                }

                if (otherDisplayedProducts.length > 0) {
                    otherDisplayedProducts.forEach(function (item) {
                        if (item.remainingQuantity == 0) {
                            if (currentProducts.length > 0) {
                                currentProducts.forEach(function (item) {
                                    item.removed = true;
                                    item.displayed = false;
                                });
                                //remove from UI the curent product because it is already displayed for another componentIndex and does not have any quantity left
                                listItem.remove();
                                return;
                            }
                            else if (currentProducts.length == 0) {
                                //current element does not have any quantity left so it will be removed form UI
                                listItem.remove();
                                return;
                            }
                        }
                    });
                }

                if (currentProducts.length > 0) {
                    //mark product as displayed and subtract 1 from initial quantity
                    currentProducts.forEach(function (item) {
                        //check if componentIndex has a product displayed
                        var componentIndexHasProductDisplayed = products.filter(function (i) {
                            return item.componentIndex === i.componentIndex && i.displayed;
                        });


                        // Logic to check if the previous product, was returned by the recent product availability call.
                        if (componentIndexHasProductDisplayed.length > 0 && currentActProducts != undefined && currentActProducts.length > 0) {
                            previousProductExists = false;
                            componentIndexHasProductDisplayed.forEach(function (item) {
                                if (currentActProducts.some(ai => ai.productId === item.productId && ai.supplierId === item.supplierId)) {
                                    previousProductExists = true;
                                }
                            });
                        }

                        if (componentIndexHasProductDisplayed.length == 0) {
                            //if no product displayed for componentindex then mark current candidate as displayed and subtract 1 from initial quantity
                            item.displayed = true;
                            item.removed = false;
                            item.remainingQuantity -= 1;
                            item.usedQuantity = item.initialQuantity - item.remainingQuantity;
                            updateProductsWithAvailableQuantity(item.productId, item.supplierId);
                            itemCount += 1;
                            
                        }
                    });
                }

                if (otherComponentIdProductsNotDisplayed.length > 0) {
                    //make sure that all not displayed products for current component have their usedQuantity incremented
                    otherComponentIdProductsNotDisplayed.forEach(function (o) {
                        o.usedQuantity = o.initialQuantity - o.remainingQuantity;
                        updateProductsWithAvailableQuantity(o.productId, o.supplierId);
                    });
                }

                listItem.find('button').attr("id", "compButton-" + thisSupplierId + "-" + thisProductId + "-" + componentIndex);
                listItem.attr("data-package-price", Number(currentPrice));

                var productId = listItem.data('productId');
                var groupId = listItem.data('groupId');
                if (groupId == '') {
                    groupId = 0;
                }

                listItem.attr('onclick', 'Inntopia.PackageBuilder.selectActivityProduct(' + thisSupplierId + ',' + productId + ',' + groupId + ',' + componentIndex + ',' + componentId + ')');

                // if no product is selected we select the lowest price product
                if (!selectedProductId && (Number(currentPrice) < Number(previousPrice)) || previousPrice == '') {
                    self.selectComponent(componentIndex, thisSupplierId, thisProductId, lowestPricedProductId, lowestPricedProductName);
                }

                // if the product being displayed wasn't returned from the call and the groupId matches the current product groupId.
                // This will occur if there is a sell-out of a product or a pricing group change
                if ((selectedProductId !== thisProductId && !previousProductExists && groupId > 0 && groupId === selectedGroupId) || previousPrice == '') {
                    self.selectComponent(componentIndex, thisSupplierId, thisProductId, lowestPricedProductId, lowestPricedProductName);
                }

                // if product id is the same but the price changed we need to update the price
                if (selectedProductId == thisProductId && ((Number(currentPrice) != Number(previousPrice)) || previousPrice == '')) {
                    self.selectComponent(componentIndex, thisSupplierId, thisProductId, lowestPricedProductId, lowestPricedProductName);
                }
            });

            cleanUI(componentIndex, localSupplierId);
        }

        var locationsQueriedCount = Number($("#locationsQueriedCount-" + componentIndex).val()) + 1;
        $("#locationsQueriedCount-" + componentIndex).val(locationsQueriedCount);

        if (products.filter(function (p) {
            return p.componentIndex == componentIndex && p.displayed;
        }).length > 0) {
            //case when the user changes date from datepicker we need display the same product as before
            itemCount += 1;
        }

        if (itemCount > 0) {
            var locationsAvailableCount = Number($("#locationsAvailableCount-" + componentIndex).val()) + 1;
            $("#locationsAvailableCount-" + componentIndex).val(locationsAvailableCount);
        }
        else if (itemCount == 0) {
            componentItemNotAvailable(componentIndex);
        }

        // category 102 = events
        if ($("#locationsQueriedCount-" + componentIndex).val() == $("#locationsCount-" + componentIndex).val()
            || categoryId == 102
            || !useV1PackageLogic) {

            componentPricingUpdate(componentIndex);

            if (!hasComponentAvailableDates(componentIndex)) {
                componentItemNotAvailable(componentIndex);
            }

            if (Number($("#locationsAvailableCount-" + componentIndex).val()) == 0 && categoryId != 102) {
                componentItemNotAvailable(componentIndex);
            }
        }

        if (hasActivityTileData) {
            $("#componentSelected-" + componentIndex).slideDown();
        }

        $("div[name='InvokeWhiteSpaceWrap']").addClass('wrapText');
        $("#loadingComponent-" + componentIndex).hide();
    }

    function hasComponentAvailableDates(componentIndex) {
        var availableDays = componentsJSON.filter(function (comp) {
            return comp.componentIndex == componentIndex;
        }).map(function (comp) {
            return comp.availableDays;
        });
        return availableDays[0].length >= 1 ? true : false;
    }

    var alternateDateCalls = [];
    function componentItemNotAvailable(componentIndex) {

        $("#locationName-" + componentIndex).html("<text class='text-error'>" + componentNotAvailable + "</text>");
        $("#componentOptionsOpener-" + componentIndex).hide();
        $("#moreInfo-" + componentIndex).hide();
        $("#price-" + componentIndex).html('');
        $("#originalPrice-" + componentIndex).html('');
        $("#priceDisplay-" + componentIndex).hide();
        $("#locationImg-" + componentIndex).html('');
        $("#cta").hide();

        if (alternateDateCalls.indexOf(componentIndex) == -1) {
            alternateDateCalls.push(componentIndex);
            findAlternateDates(componentIndex);
        }
    }

    function loadActivityTilesForStartDates(componentIndex, currentStartDate, startDates) {
        var isLastStartDate = startDates.indexOf(currentStartDate) == startDates.length - 1,
            nextStartDate, disableAnimation = true;

        $("#startDate-" + componentIndex).val(moment(currentStartDate).format("L"));//locale date format

        self.loadComponentIndex(componentIndex, disableAnimation).done(function () {
            if (isLastStartDate) {
                $("#startDatePicker-" + componentIndex).datepicker("setDate", currentStartDate);
                $("#componentSelected-" + componentIndex).slideDown();
                return;
            } else {

                nextStartDate = startDates[startDates.indexOf(currentStartDate) + 1];
                loadActivityTilesForStartDates(componentIndex, nextStartDate, startDates);
            }
        });
    }

    function findAlternateDates(componentIndex) {
        var startDates = alternateDatesJSON
            .filter(function (alternateDate) {
                return alternateDate.componentIndex == componentIndex;
            })
            .map(function (alternateDate) {
                return alternateDate.startDate;
            });

        if (startDates.length) {
            loadActivityTilesForStartDates(componentIndex, startDates[0], startDates);
        }
    }

    function updatePackagePrices() {
        componentPricingInitialize();
        self.totalPrice();
    }

    function componentPricingInitialize() {
        for (var i = 0; i < componentsJSON.length; i++) {
            if (componentsJSON[i].required) {
                componentPricingUpdate(componentsJSON[i].componentIndex);
            }
        }
    }

    function componentPricingUpdate(componentIndex) {
        window.requestAnimationFrame(() => {
            var thisPrice = null;
            var thisPriceString = null;
            var categoryId = $("#categoryId-" + componentIndex).val();
            var componentPrice = $('#price-' + componentIndex).attr('data-price');
            var thisQuantity = $("#qty-" + componentIndex).val();
            var componentOriginalPrice = $('#originalPrice-' + componentIndex).attr('data-original-price');
            var totalPrice = $('#totalPrice').attr('total-price');
            var totalOriginalPrice = $('#totalOriginalPrice').html();

            // This happens if the DOM is not fully updated when we try to access the data. 
            // This should no longer occur with the requestAnimationFrame wrapper
            if (componentPrice == undefined) {
                console.error('Component ' + componentIndex + ' price not yet defined. Cannot update pricing.');
                return;
            }

            //catch any non-US number formatting
            if (componentPrice != null) {
                var componentPriceString = componentPrice.toString();
                componentPrice = Number(componentPriceString.replace(',', '.'));
            }
            var totalPriceString = totalPrice ? totalPrice.toString() : "";
            totalPrice = Number(totalPriceString.replace(',', '.'));

            var otherPricesTotal = totalPrice - componentPrice;
            var otherOriginalPricesTotal = totalOriginalPrice - componentOriginalPrice;

            $("#supplierList-" + componentIndex + " li").each(function () {
                if (categoryId == '1') {
                    thisPrice = $(this).data("minPrice");
                    //catch any non-US number formatting
                    thisPriceString = thisPrice.toString();
                    thisPrice = Number(thisPriceString.replace(',', '.'));
                    thisPrice = ((Number(thisPrice) * los) / thisQuantity);

                    $(this).find('tr').each(function () {
                        var thisProdPrice = $(this).data('price');
                        var thisOriginalProdPrice = $(this).data('originalPrice');

                        //catch any non-US number formatting
                        if (thisProdPrice != null) {
                            var thisProdPriceString = thisProdPrice.toString();
                            thisProdPrice = Number(thisProdPriceString.replace(',', '.'));
                        }
                        //catch any non-US number formatting
                        if (thisOriginalProdPrice != null) {
                            var thisOriginalProdPriceString = thisOriginalProdPrice.toString();
                            thisOriginalProdPrice = Number(thisOriginalProdPriceString.replace(',', '.'));
                        }

                        thisProdPrice = (thisProdPrice * los);
                        thisOriginalProdPrice = (thisOriginalProdPrice * los);
                        thisProdPrice = thisProdPrice / thisQuantity;
                        thisOriginalProdPrice = thisOriginalProdPrice / thisQuantity;
                        var thisSavings = (thisOriginalProdPrice + otherOriginalPricesTotal) - (thisProdPrice + otherPricesTotal);

                        if (packageDisplayType == 2) {
                            var originalProductPriceHtml = '<strong>' + formatPriceCurrencyWrapper(thisOriginalProdPrice) + '</strong>';
                            if (thisOriginalProdPrice > thisProdPrice) {
                                originalProductPriceHtml = '<del>' + originalProductPriceHtml + '</del>';
                            }
                            $(this).find('[name="originalProductPrice"]').html(originalProductPriceHtml);
                            $(this).find('[name="productPrice"]').html('<strong>' + formatPriceCurrencyWrapper(thisProdPrice) + '</strong>');
                            $(this).find('[name="productPrice"]').show();
                        }
                        else {
                            $(this).find('[name="originalProductPrice"]').html('<br/><span class="text-success">' + saveText + ' ' + formatPriceCurrencyWrapper(thisSavings) + '</span>').css('textDecoration', 'none').css('text-align', 'right');
                            $(this).find('[name="productPrice"]').html('<strong>' + packagePriceText + ' ' + formatPriceCurrencyWrapper(thisProdPrice + otherPricesTotal) + '</strong>');
                            $(this).find('[name="productPrice"]').show();
                        }
                    });
                }
                else {
                    thisPrice = $(this).data('price');
                    //catch any non-US number formatting
                    if (thisPrice != null) {
                        thisPriceString = thisPrice.toString();
                        thisPrice = Number(thisPriceString.replace(',', '.'));
                    }

                    var thisOriginalProdPrice = $(this).data('originalPrice');
                    //catch any non-US number formatting
                    if (thisOriginalProdPrice != null) {
                        var thisOriginalProdPriceString = thisOriginalProdPrice.toString();
                        thisOriginalProdPrice = Number(thisOriginalProdPriceString.replace(',', '.'));
                    }
                }

                var thisTotalPrice = thisPrice + otherPricesTotal;
                var supplierId = $(this).data('supplierId');

                if (categoryId == 1) {
                    var selector = "ul#supplierList-{componentIndex} li#supplierTile_{supplierId}"
                        .replace("{componentIndex}", componentIndex)
                        .replace("{supplierId}", supplierId);

                    $(selector).attr('data-package-price', thisTotalPrice);

                    //If the price isn't Valid default to Select.
                    if (thisTotalPrice > 0) {
                        $('#compButton-' + supplierId + '-' + componentIndex).html(packagePriceText + ' ' + formatPriceCurrencyWrapper(thisTotalPrice));
                    }
                }
                else {
                    var productId = $(this).data('productId');

                    var selector = "ul#supplierList-{componentIndex} li#productTile-{supplierId}-{productId}"
                        .replace("{componentIndex}", componentIndex)
                        .replace("{supplierId}", supplierId)
                        .replace("{productId}", productId);

                    $(selector).attr('data-package-price', thisTotalPrice);
                    $('#compButton-' + supplierId + '-' + productId + '-' + componentIndex).html(packagePriceText + ' ' + formatPriceCurrencyWrapper(thisTotalPrice));
                }
            });

        });        
    }

    function checkAvailabilityClickHandler() {
        forceChoice = getParameterByName('forceChoice') || 0;
        self.updateChildCountPB();
        $("#formError").html('');
        var thisArrivalDate = $("#arrivalDate").val();
        var thisDepartureDate = $("#departureDate").val();
        var thisAdultCount = $("#adultCount").val();
        var thisChildCount = $("#childCount").val();
        var thisChildAgePBArray = [];
        var hasError = false;
        if (thisArrivalDate == '') {
            $("#formError").html('<div class="text-error">' + TranslateErrorString('ArrivalDate') + '</div>');
            hasError = true;
        }
        if (isNaN(thisAdultCount)) {
            $("#formError").html('<div class="text-error">' + TranslateErrorString('NumAdults') + '</div>');
            hasError = true;
        }
        if (isNaN(thisChildCount)) {
            $("#formError").html('<div class="text-error">' + TranslateErrorString('NumChildren') + '</div>');
            hasError = true;
        }
        if ((thisAdultCount == '0') && (thisAdultCount == '0')) {
            $("#formError").html('<div class="text-error"> Please enter the number of guests' + '</div>');
            hasError = true;
        }
        if (thisAdultCount == '') {
            $("#formError").html('<div class="text-error">' + TranslateErrorString('NumAdults') + '</div>');
            hasError = true;
        }

        moment.locale(language);

        var today = new Date();
        var arrivalDateDT = moment(thisArrivalDate).toDate();
        var departureDateDT = moment(thisDepartureDate).toDate();
        today.setHours(0, 0, 0, 0);

        if (arrivalDateDT < today) {
            $("#formError").html('<div class="text-error">' + TranslateErrorString('ArrivalDateAfterToday') + '</div>');
            hasError = true;
        }
        if (thisDepartureDate == '') {
            $("#formError").html('<div class="text-error">' + TranslateErrorString('DepartureDate') + '</div>');
            hasError = true;
        }
        if (arrivalDateDT >= departureDateDT) {
            $("#formError").html('<div class="text-error">' + TranslateErrorString('DateBeforeDate') + '</div>');
            hasError = true;
        }

        if (Inntopia.requireChildAge && thisChildCount > 0) {
            for (var i = 0; i < thisChildCount; i++) {
                var value = $('input[id="childAgePB_' + i + '"]').val();

                if (isNaN(value) || value == '') {
                    $("#childAgePB_" + i).after("<div class='text-error'>" + TranslateErrorString('NumChildrenAgeRequired') + "<div>");
                    hasError = true;
                } else if (value > 21) {
                    $("#childAgePB_" + i).after("<div class='text-error'>" + TranslateErrorString('NumChildrenAgeTooOld') + "<div>");
                    hasError = true;
                } else {
                    thisChildAgePBArray[i] = value;
                }
            }
        }

        if (!hasError) {
            setDefaultsCookie(thisArrivalDate, thisDepartureDate, thisAdultCount, thisChildCount, thisChildAgePBArray, null, null, null, null, null);

            var url = '/ecomm/package/packagebuilder/' + salesId + '/' + language + '/?packageid=' + packageId + '&startDate=' + thisArrivalDate + '&endDate=' + thisDepartureDate + '&adultcount=' + thisAdultCount + '&childCount=' + thisChildCount + '&forceChoice=' + forceChoice + '&childAgeArray=' + thisChildAgePBArray;

            if (promoCode != '') {
                url = url + '&promocode=' + promoCode;
            }

            if ((location.pathname + location.search + location.hash) == url) {
                location.reload(true);
            }
            else {
                window.location = url;
            }
        }
    }

    function formatPriceCurrencyWrapper(price) {
        return Inntopia.FormatPrice(Number(price), currencyInfo, false);
    }

    // Public Functions
    self.getParameterByName = function (name) {
        name = name.toUpperCase();
        name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
        var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
            results = regex.exec(location.search.toUpperCase());
        return results == null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
    };
    self.checkComponentAvailableDay = function (componentIndex, day) {
        var availableDays = componentsJSON.filter(function (comp) {
            return comp.componentIndex == componentIndex;
        }).map(function (comp) {
            return comp.availableDays;
        });
        var productId = $("#productId-" + componentIndex).val();
        var supplierId = $("#supplierId-" + componentIndex).val();
        var result = availableDays.filter(function (ad) {
            return ad.some(function (res) {
                return res.day == day &&
                    (productId != "" ? res.productid == productId : true) &&
                    (supplierId != "" ? res.supplierId == supplierId : true);
            });
        })[0];

        return result;
    };
    self.unavailableArrival = function (date) {
        var isoDate = date.toString(isoDateFormat);

        if ($.inArray(isoDate, unavailableDates) == -1 && date >= minDate && date <= maxDate) {
            return [true, "", "available"];
        } else {
            return [false, "", "unAvailable"];
        }
    };
    self.unavailableDeparture = function (date) {
        var isoDate = date.toString(isoDateFormat);
        var isoPreviousDate = new Date(date).addDays(-1).toString(isoDateFormat);

        if (($.inArray(isoDate, unavailableDates) == -1 || $.inArray(isoPreviousDate, unavailableDates) == -1) && date >= minDate && date <= maxDate) {
            return [true, "", "available"];
        } else {
            return [false, "", "unAvailable"];
        }
    };
    self.inntopiaResize = function () {
        $("ul[id^='supplierList-']").width($('#inntopiaBody').width());
    };
    self.showComponentOptions = function (componentIndex, show) {
        var $modal = $("#componentOptionsModal-" + componentIndex),
            inntopiaBodyWidth = $('#inntopiaBody').width(),
            $packageSlides = $modal.find('.package-slide');

        if (!show) {
            if ($modal.is(':visible')) {
                $modal.modal('hide');
            }
            return;
        }

        //tile sorting will be done right before opening the componentOptionsModal
        var $listItems = $("ul#supplierList-" + componentIndex + " li");
        $listItems.sort(function (a, b) {
            return Number($(a).data('package-price')).toFixed(2) - Number($(b).data('package-price')).toFixed(2);
        });
        $listItems.appendTo("ul#supplierList-" + componentIndex);

        $packageSlides.hide();
        $modal.off();

        $modal.on('shown', function () {
            $packageSlides.show(function () {
                if (inntopiaBodyWidth * 1.25 < window.innerWidth) {
                    $modal.css({
                        'width': '' + (inntopiaBodyWidth - 40) + 'px',
                        'margin-left': function () {
                            return -(inntopiaBodyWidth - 40) / 2;
                        }
                    });
                }
            });
        });

        $('html,body').animate({ scrollTop: $("#componentSelected-" + componentIndex).offset().top }, 800);

        $modal.modal({
            show: true,
            backdrop: 'static',
            keyboard: false
        });
    };

    self.changeLodgingQuantity = function (componentIndex) {
        var selectedSupplierId = $('#supplierId-' + componentIndex).val();
        var selectedProductId = $('#productId-' + componentIndex).val();
        //TODO: global variables should be read only
        defaultProducts = selectedSupplierId + ',' + selectedProductId + '|';
        //TODO: global variables should be read only  
        defaultsLoaded = 0;
        $('#supplierList-' + componentIndex).html('');
        self.loadComponentIndex(componentIndex).done(self.loadDefaults);
    };

    self.changeOtherQuantity = function (componentIndex) {
        var quantity = $('#qtySelect-' + componentIndex).val();
        $('#qty-' + componentIndex).val(quantity);
        $('#qtyDisplay-' + componentIndex).html(quantity);
    };

    function setLocationsCount(componentIndex, count) {
        $("#locationsQueriedCount-" + componentIndex).val('0');
        $("#locationsAvailableCount-" + componentIndex).val('0');
        $("#locationsCount-" + componentIndex).val(count);
    }

    function loadLodgingComponent(componentData) {
        var promises = [],
            supplierId = componentData.availableDays[0].supplierId;
        
        // make a request for each component location
        for (var x = 0; x < componentData.locations.length; x++) {
            var locationId = componentData.locations[x].locationId;
            setLocationsCount(componentData.componentIndex, componentData.locations.length+1);
            promises.push(getLodgingTile(locationId, componentData.componentId, componentData.componentIndex));            
        }

        return $.when.apply($, promises).done(function () {            
            if (componentData.locations.length == 1) {
                arguments.locationId = componentData.locations[0].locationId;

                lodgingTileDoneHandler([arguments], componentData.componentIndex, supplierId);
            } else {
                for (var i = 0; i < arguments.length; i++) {
                    arguments[i].locationId = componentData.locations[i].locationId;
                }

                lodgingTileDoneHandler(arguments, componentData.componentIndex, supplierId);
            }
        });
    }

    function loadActivityComponent(componentData) {
        var promises = [];

        for (var x = 0; x < componentData.locations.length; x++) {
            var locationId = componentData.locations[x].locationId;

            setLocationsCount(componentData.componentIndex, componentData.locations.length+1)
            var thisStartDate = $("#startDate-" + componentData.componentIndex).val();
            promises.push(getActivityTile(locationId, componentData.componentId, componentData.productCategoryId, thisStartDate, componentData.componentIndex, null));
        }

        return $.when.apply($, promises).done(function () {
            if (componentData.locations.length > 1) {
                for (var i = 0; i < arguments.length; i++) {
                    activityTileDoneHandler(arguments[i], componentData.componentIndex, componentData.componentId, componentData.productCategoryId);
                }
            } else {
                activityTileDoneHandler(arguments, componentData.componentIndex, componentData.componentId, componentData.productCategoryId);
            }
        });
    }

    // find component by ID and determine how to load it
    self.loadComponentItems = function (componentId) {
        var promises = [];
        for (var i = 0; i < componentsJSON.length; i++) {
            if (componentsJSON[i].componentId == componentId) {
                if (componentsJSON[i].productCategoryId == 1) {
                    promises.push(loadLodgingComponent(componentsJSON[i]));
                } else {
                    promises.push(loadActivityComponent(componentsJSON[i]));
                }
            }
        }

        return $.when.apply($, promises);
    };

    self.loadComponentIndex = function (componentIndex, disableAnimation, forceReload) {
        $("#locationsQueriedCount-" + componentIndex).val('0');
        $("#locationsAvailableCount-" + componentIndex).val('0');
        $("#loadingComponent-" + componentIndex).show();
        $("#componentSelected-" + componentIndex).hide();
        $('#supplierList-' + componentIndex).html('');

        var componentKey = '' + componentIndex;
        if (forceReload && loadedComponents.hasOwnProperty(componentKey)) {
            delete loadedComponents[componentKey];
        }

        for (var i = 0; i < componentsJSON.length; i++) {
            if (componentsJSON[i].componentIndex == componentIndex) {
                $("#locationsCount-" + componentIndex).val(componentsJSON[i].locations.length);
                if (componentsJSON[i].productCategoryId == 1) {
                    return loadLodgingComponent(componentsJSON[i]);
                } else {
                    return loadActivityComponent(componentsJSON[i]);
                }
            }
        }
    };

    function openComponent(componentId) {
        $('html,body').animate(
            { scrollTop: $("#trhead-" + componentId).offset().top },
            { duration: 800, done: updatePackagePrices }
        );
    }

    self.showOptional = function (componentId) {
        var alreadyLoaded = $("#trbody-" + componentId).data('loaded');
        $("#trbody-" + componentId).slideDown();
        $("#optionalButton-" + componentId).hide();
        $("#removeOptionalButton-" + componentId).show();
        var counter = $('#counter-' + componentId).data('counter');
        $('#price-' + counter).removeAttr('hidden');
        $('#originalPrice-' + counter).removeAttr('hidden');

        if (alreadyLoaded !== 1) {
            self.loadComponentItems(componentId).done(openComponent.apply(self, [componentId]));
        } else {
            $('#qty-' + componentId).val("1");
            openComponent(componentId);
        }
    };

    self.hideOptional = function (componentId) {
        $('#qty-' + componentId).val("0");
        $('#trbody-' + componentId).data('loaded', 1).slideUp();
        $('#optionalButton-' + componentId).show();
        $('#removeOptionalButton-' + componentId).hide();
        var counter = $('#counter-' + componentId).data('counter');
        $('#price-' + counter).attr('hidden', true);
        $('#originalPrice-' + counter).attr('hidden', true);

        updatePackagePrices();
    };

    self.showProductDetails = function (thisX) {
        $("#productDetailsModalBody").html('<div style="text-align: center;"><img src="/EComm/Images/wait.gif"/></div>');
        var thisSupplierId = $("#supplierId-" + thisX).val();
        var thisProductId = $("#productId-" + thisX).val();

        var locationName = $("#locationName-" + thisX).html();
        var productName = $("#productName-" + thisX).html();
        var modalHead = '<h3>' + locationName + ' </h3><h4>' + productName + '</h4>';

        $("#productDetailsModalHead").html(modalHead);

        $("#productDetailsModal").modal({
            show: true
        });

        var packageComponentId = $("#componentId-" + thisX).val();

        $.ajax({
            type: "GET",
            url: "/Ecomm/Listings/ProductDetail/" + salesId + "/",
            cache: false,
            data: {
                supplierId: thisSupplierId,
                productId: thisProductId,
                arrivalDate: startDate,
                Language: language,
                showLocationDetails: true,
                packageId: packageId,
                packageComponentId : packageComponentId
            },
            success: function (data) {
                $("#productDetailsModalBody").html(data);
            }
        });
    };

    self.removeComponent = function (componentIndex) {
        $('#qty-' + componentIndex).val("0");
        $('#qtyDisplay-' + componentIndex).html("0");
        $('#supplierId-' + componentIndex).val("");
        $('#productId-' + componentIndex).val("");
        $('#groupId-' + componentIndex).val("");
        $('#locationName-' + componentIndex).html("");
        $('#productName-' + componentIndex).html("");
        $('#locationDescription-' + componentIndex).html("");
        $('#locationIMG-' + componentIndex).html("");
        $('#productListModal').modal('hide');
        $('#dateSelector-' + componentIndex).hide();
        
        $('#componentOptionsAdd-' + componentIndex).show();
        $('#componentOptionsDelete-' + componentIndex).hide();
        $('#componentOptionsOpener-' + componentIndex).hide();
        $('#priceDisplay-' + componentIndex).hide();
        $('#moreInfo-' + componentIndex).hide();
        $('#componentSelected-' + componentIndex + ' > .guest-settings').hide();
        self.showComponentOptions(componentIndex, false);
        updatePackagePrices();
    };

    self.loadDefaults = function () {
        if (defaultsLoaded == 0) {
            if (forceChoice == 1) {
                self.showComponentOptions(1, true);
                //TODO: global variables should be read only
                forceChoice = 0;
            }
            if (defaultProducts == '') {
                //TODO: global variables should be read only
                defaultProducts = self.getParameterByName('defaultProducts');
            }

            if (defaultProducts.length > 0) {
                if (defaultProducts.indexOf('|') == -1) {
                    //TODO: global variables should be read only
                    defaultProducts = defaultProducts + '|';
                }

                var defaultProductsList = defaultProducts.split('|');
                var defaultEachCallback = function () {
                    var thisComponentIndex = $(this).closest('ul').attr('id');
                    thisComponentIndex = thisComponentIndex.split('-')[1];
                    var thisCSupplierId = $(this).data('supplierId');
                    var thisCProductId = $(this).data('productId');

                    if ((thisCSupplierId == thisDefaultSupplierId) && (thisCProductId == thisDefaultProductId) && ((forceChoice == 0) || (thisComponentIndex != 1))) {
                        self.selectComponent(thisComponentIndex, thisCSupplierId, thisCProductId);
                    }
                };

                for (var i = 0; i < defaultProductsList.length; i++) {
                    var thisDefaultProduct = defaultProductsList[i].split(',');
                    var thisDefaultSupplierId = thisDefaultProduct[0];
                    var thisDefaultProductId = thisDefaultProduct[1];
                    $("ul[name='supplierList'] li tr").each(defaultEachCallback);
                }
            }
        }
        //TODO: global variables should be read only
        defaultsLoaded = 1;
    };

    self.bookPackage = function (overrideAction) {
        var itemJSON = [];
        itemJSON.length = 0;
        var packageBuilderGTM = [];
        packageBuilderGTM.length = 0;
        let gaFourProducts;
        let nights = 1;

        $("[name='qty']").each(function () {
            if ($(this).val() != 0) {
                var thisId = $(this).attr("id").split("-")[1];
                var thisPackageComponentId = $("#componentId-" + thisId).val();
                var thisProductId = $("#productId-" + thisId).val();
                var thisSupplierId = $("#supplierId-" + thisId).val();
                var quantity = $("#qty-" + thisId).val();
                var startDate = $("#startDate-" + thisId).val();
                var endDate = $("#endDate-" + thisId).val();
                if (endDate == null) {
                    endDate = startDate;
                }
                var adultCount = $("#adultCount-" + thisId).val();
                var childCount = $("#childCount-" + thisId).val();
                var childAgesArray = null;
                var productCategory = $("#categoryId-" + thisId).val();
                if (parseInt(productCategory, 10) == 1) //only set child ages array for lodging
                {
                    childAgesArray = childAges;

                    let ad = moment(startDate);
                    let dd = moment(endDate);
                    nights = dd.diff(ad, 'days');
                }
                if ((thisSupplierId != '') && (thisProductId != '') && (thisSupplierId != undefined)) {
                    var productName = document.getElementById('productName-' + thisId).innerHTML;
                    var productPrice = document.getElementById('price-' + thisId).innerHTML;
                    var categoryName = document.getElementById('componentSelected-' + thisId).getAttribute('data-product-category-name');
                    var supplierName = $('#locationName-' + thisId).text();

                    if (productPrice != '') {
                        packageBuilderGTM.push({
                            "name": productName,
                            "id": thisSupplierId + "-" + thisProductId,
                            "price": productPrice,
                            "brand": supplierName,
                            "category": categoryName,
                            "variant": thisSupplierId,
                            "quantity": quantity
                        });

                        itemJSON.push({
                            "ArrivalDate": startDate,
                            "DepartureDate": endDate,
                            "AdultCount": adultCount,
                            "ChildCount": childCount,
                            "SupplierId": thisSupplierId,
                            "ProductId": thisProductId,
                            "Quantity": quantity,
                            "PackageComponentId": thisPackageComponentId,
                            "childAges": childAgesArray
                        });
                    }
                }
            }
        });

        $.ajax({
            type: "POST",
            url: "/Ecomm/json/itineraryitemsadd/" + salesId + "/" + language,
            dataType: "json",
            data: { sessionId: sessionId, packageId: packageId, adultCount: adultCount, itineraryItems: JSON.stringify(itemJSON), overrideAction: overrideAction },
            error: function () {

                $("#packageFailedToAdd").modal({
                    show: true,
                    backdrop: 'static',
                    keyboard: false
                });
            },
            success: function (data) {
                var response = Number(data[0].ErrorCode);
                var upsellItemsList = "";
                var itineraryId = data[0].ItineraryId;

                for (let d of data) {
                    if (d.UpsellItemList != null) {
                        upsellItemsList = upsellItemsList + d.UpsellItemList;
                    }
                    if (d.GaFourProducts != null) {
                        gaFourProducts = d.GaFourProducts;
                        for (let prod of gaFourProducts) {
                            if (prod.item_category == '1') {
                                prod.price = prod.price / nights;
                                prod.quantity = prod.quantity * nights;
                            }
                        }
                    }
                }

                if (response == 0) {
                    const promises = [];
                    promises.push(googleTagManager.pushAddToCart(itineraryId, packageBuilderGTM));
                    promises.push(googleAnalyticsFour.pushAddToCart('packagebuilder', itineraryId, gaFourProducts));
                    Promise.all(promises).then(function () {
                        $(document.body).trigger('inntopia_cart_itemsAdded').trigger('inntopia_cart_updated');

                        if (upsellItemsList.length != 0) {
                            inntopiaUpsell(upsellItemsList);
                        }
                        else {
                            $("[name='buyNow']").button('loading');
                            if ((location.hostname.indexOf('localhost') == -1) && (location.hostname.indexOf('stage.inntopia') == -1)) {
                                window.location = "https://" + location.hostname + "/Ecomm/Checkout/Customer/" + salesId + "/" + language;
                            }
                            else {
                                window.location = "/Ecomm/Checkout/Customer/" + salesId + "/" + language;
                            }
                        }
                    });
                } else {
                    $("#packageFailedToAdd").modal({
                        show: true,
                        backdrop: 'static',
                        keyboard: false
                    });
                }
            }
        });
    };
    self.totalPrice = function () {
        var isPriced = true;
        var totalPrice = 0;
        var totalOriginalPrice = 0;
        if (available == false) {
            isPriced = false;
        }

        $("[name='qty']").each(function () {
            if ($(this).val() != 0) {
                var thisId = $(this).attr('id').split('-')[1];
                var originalPrice = 0;
                var price = 0;
                var thisId = $(this).attr('id').split('-')[1];
                var thisPrice = Number($('#price-' + thisId).attr('data-price') || 0);
                var thisQty = Number($("#qty-" + thisId).val()) || 1;
                var thisOriginalPrice = Number($("#originalPrice-" + thisId).attr('data-original-price') || 0);
                price = price + (thisPrice * thisQty);
                totalPrice += price;

                originalPrice = (originalPrice + (thisOriginalPrice * thisQty));
                totalOriginalPrice += originalPrice;
                var isRequired = $(this).data('required');

                if (thisPrice === 0 && isRequired === 'True') {
                    isPriced = false;
                }

                var savings = originalPrice - price;

                var $priceObject = $('#componentSelected-' + thisId + '  .price-' + thisId);
                var $originalPriceObject = $('#componentSelected-' + thisId + ' .originalPrice-' + thisId).parent();
                // 1 shows original price and hides the current price from PackageDisplayType*
                if (packageDisplayType == 1) {
                    $originalPriceObject.show();
                    $priceObject.hide();
                }
                // Just show original price
                else if (packageDisplayType == 3) {
                    $originalPriceObject.show();
                    $priceObject.hide();
                }
                // 2 show both original price and price if there is a discount on the item
                else {
                    if (savings <= 0) {
                        $priceObject.show();
                        $originalPriceObject.hide();
                    }
                    else {
                        $priceObject.show();
                        $originalPriceObject.show();
                    }
                }
            }
        });

        var totalSavings = totalOriginalPrice - totalPrice;
        if (isPriced == true) {
            $("[name='totalOriginalPrice']").html(totalOriginalPrice.toFixed(2));

            // if total has savings show message
            if (totalSavings > 0) {
                $("[name='savingsMsg']").show();
            } else { 
                $("[name='savingsMsg']").hide();
            }
            //TODO: global variables should be read only
            initialLoadComplete = true;
            $("#cta").show();
        }

        $("[name='savings']").text(formatPriceCurrencyWrapper(totalSavings)).attr('total-savings', totalSavings);
        $("[name='totalPrice']").text(formatPriceCurrencyWrapper(totalPrice)).attr('total-price', totalPrice);
    };

    self.updateChildCountPB = function () {
        var childCountPB = $('input[name="childCount"]').val();
        var childAgesPB = [];
        if (childAges.indexOf(',') != -1) {
            childAgesPB = childAges.split(',');
        }
        if (childCountPB > 0) {
            for (var i = 0; i < childCountPB; i++) {
                var value = $('input[id="childAgePB_' + i + '"]').val();
                if ((value != '') && (value != null)) {
                    childAgesPB[i] = value;
                }
            }
        }
        if (!Inntopia.requireChildAge || childCountPB == '0') {

            $('#childrenAgesPB').html('');
            $('#childAgesDisplayLabelPB').html('');

        } else {
            $('#childrenAgesPB').html(childAgesLabel + "<br />");
            for (var i = 0; i < childCountPB; i++) {
                var addChildPB = '<input type="number" class="input-mini" id="childAgePB_' + i + '"  size="2" name="childAgePB" min="0" max="21" step="1"></input>';
                $('#childrenAgesPB').append(addChildPB);
                $('input[id="childAgePB_' + i + '"]').val(childAgesPB[i]);
                $('#childAgesDisplayLabelPB').html("<strong>" + childAgesLabel + "</strong>");
            }
        }
    };

    self.selectProduct = function (locationId, componentIndex) {
        $("#componentOptionsModal-" + componentIndex).modal('hide');
        $("#productListModal").modal({
            show: true,
            backdrop: 'static',
            keyboard: false
        });

        var productList = $("#productList_" + locationId).html();
        var img = $("#locationImage_" + locationId).html();
        var desc = $("#supplierDescription_" + locationId).html();
        var lName = $("#supplierName_" + locationId).html();

        $("#productListModalHead").html(lName);
        productList = img + desc + '<table class="table table-condensed">' + productList + '</table>';
        $("#productList").html(productList);
        $("#productList tr").each(function () {
            var productId = $(this).data('productId');
            var supplierId = $(this).data('supplierId');
            $(this).attr("style", "");
            var rowElement = $('<td align="right"><button class="btn btn-primary">' + selectText + '</button></td>');
            $(this).append(rowElement);
            rowElement.click(self.selectComponent.bind(this, componentIndex, supplierId, productId));
        });
    };

    self.selectActivityProduct = function (thisSupplierId, thisProductId, groupId, componentIndex, componentId) {
        $("#componentOptionsModal-" + componentIndex).modal('hide');

        if (groupId == 0) {
            groupId = '';
        }
        $("#productList").html('<div class="text-center"><img alt="wait gif" src="/EComm/Images/wait.gif"/></div>');
        $("#productListModal").modal({
            show: true,
            backdrop: 'static',
            keyboard: false
        });

        var lName = $("#name-" + thisSupplierId + '-' + thisProductId).html();
        $("#productListModalHead").html(lName);

        var thisStartDate = $('#startDate-' + componentIndex).val(); //iso date format
        var componentPrice = $('#price-' + componentIndex).attr('data-price');
        var componentOriginalPrice = $('#originalPrice-' + componentIndex).attr('data-original-price');
        var totalPrice = $('#totalPrice').attr('total-price');
        var totalOriginalPrice = $('#totalOriginalPrice').html();
        var otherPricesTotal = totalPrice - componentPrice;
        var otherOriginalPricesTotal = totalOriginalPrice - componentOriginalPrice;

        $.ajax({
            type: "GET",
            url: "/Ecomm/Listings/ActivityProductDetail/" + salesId + '/' + language,
            cache: false,
            data: {
                supplierId: thisSupplierId,
                productId: thisProductId,
                arrivalDate: thisStartDate,
                groupId: groupId,
                packageId: packageId,
                packageComponentId: componentId
            },
            success: function (data) {
                document.getElementById('productList').innerHTML = data;
                var thisId = thisSupplierId + '-' + thisProductId;

                var defaultQty = $("#qty-" + componentIndex).val();
                var minQty = $("#minQty-" + componentIndex).val();
                var maxQty = $("#maxQty-" + componentIndex).val();
                var thisPrice = $("#productTile-" + thisId).data("culture-insensitive-price");
                var thisQty = $("#maxQty-" + thisSupplierId + "-" + thisProductId + "-").val();
                $("#priceDisplay-" + thisId + "-").html(Number(thisPrice.replace(',', '')).toFixed(2));
                var qtySelect = '<select class="input input-mini" id="qty-' + thisId + '-" name="qty">';
                for (var i = 1; i <= thisQty; i++) {
                    qtySelect = qtySelect + '<option value="' + i + '">' + i + '</option>';
                }

                qtySelect = qtySelect + '</select>';
                $("#qtySelect-" + thisId + "-").html(qtySelect);
                $('#dateSelector-' + componentIndex).show();
                $('#componentSelected-' + componentIndex + ' > .guest-settings').show();
                $("#productList tr").each(function () {
                    var productId = $(this).data('productId');
                    var supplierId = $(this).data('supplierId');
                    thisId = thisSupplierId + '-' + thisProductId;
                    var thisProdPrice = $(this).data('price');
                    var thisOriginalProdPrice = $(this).data('originalPrice');
                    var thisMaxQty = $(this).find("[name='qty']  option:last-child").val();

                    //catch any non-US number formatting
                    if (thisProdPrice != null) {
                        var thisProdPriceString = thisProdPrice.toString();
                        thisProdPrice = Number(thisProdPriceString.replace(',', '.')).toFixed(2);
                    }
                    if (thisOriginalProdPrice != null) {
                        var thisOriginalProdPriceString = thisOriginalProdPrice.toString();
                        thisOriginalProdPrice = Number(thisOriginalProdPriceString.replace(',', '.')).toFixed(2);
                    }

                    var thisInventoryQty = $("#maxQty-" + supplierId + "-" + productId + "-").val();
                    if (thisInventoryQty && maxQty && (Number(thisInventoryQty) < Number(maxQty))) {
                        maxQty = thisInventoryQty;
                    }

                    thisMaxQty = thisMaxQty < maxQty ? thisMaxQty : maxQty;

                    if ($.isNumeric(thisMaxQty)) {
                        thisMaxQty = Number(thisMaxQty);
                    }
                    if ($.isNumeric(minQty)) {
                        minQty = Number(minQty);
                    }

                    qtySelect = '';
                    for (var i = minQty; i <= thisMaxQty; i++) {
                        if (i == defaultQty) {
                            qtySelect = qtySelect + '<option value="' + i + '" selected>' + i + '</option>';
                        }
                        else {
                            qtySelect = qtySelect + '<option value="' + i + '">' + i + '</option>';
                        }
                    }

                    $(this).find("[name='qty']").html(qtySelect);

                    var thisSavings = (Number(thisOriginalProdPrice) + Number(otherOriginalPricesTotal)) - (Number(thisProdPrice) + Number(otherPricesTotal));
                    if (packageDisplayType == 2) {
                        $(this).find('[name="price"]').html('<strong>' + formatPriceCurrencyWrapper(thisProdPrice) + '</strong>&nbsp;<strike>' + formatPriceCurrencyWrapper(thisOriginalProdPrice) + '</strike>');
                    }
                    else {
                        $(this).find('[name="price"]').html('<strong>' + packagePriceText + ' ' + formatPriceCurrencyWrapper(Number(thisProdPrice) + Number(otherPricesTotal)) + '</strong><br/><span class="text-success">' + saveText + ' ' + formatPriceCurrencyWrapper(thisSavings) + '</span>');
                    }

                    $(this).attr("style", "");
                    $(this).find("[id^='addBtn']").hide();
                    var rowElement = $('<td align="right"><button class="btn btn-primary">Select</button></td>');
                    $(this).append(rowElement);
                    rowElement.click(function () {
                        self.selectComponent(componentIndex, supplierId, productId, null, null, thisProdPrice, thisOriginalProdPrice);
                    });
                });
            }
        });
    };
    self.queueReady = function (readyFunc) {
        readyQueue.push(readyFunc);
    };
    self.init = function (forceQuantityRules) {
        enforceQuantityRules = forceQuantityRules;
        moment.locale(language);
        var arrivalDateElement = $("#arrivalDateDatePicker");
        var departureDateElement = $("#departureDateDatePicker");
        var formatArrivalDate = moment(startDate, moment.ISO_8601);
        var formatDepartureDate = moment(endDate, moment.ISO_8601);

        $('body').attr('onunload', '');
        $("#arrivalDateDisplay").html(formatArrivalDate);
        $("#departureDateDisplay").html(formatDepartureDate);
        $("[name='packageStartDate']").html(dateActive.format('dddd ll'));
        $("[name='packageEndDate']").html(dateInactive.format('dddd ll'));
        $("[name='bookingStartDate']").html(bookFromDate.format('dddd ll'));
        $("[name='bookingEndDate']").html(bookUntilDate.format('dddd ll'));

        if (startDate != '') {
            arrivalDateElement.val(moment(startDate).format('L'));
            departureDateElement.val(moment(endDate).format('L'));
        }

        // Initialize datepicker
        $.datepicker.setDefaults($.datepicker.regional[language]);
        arrivalDateElement.datepicker({
            defaultDate: moment(startDate).toDate(),
            minDate: moment(minDate).toDate(),
            maxDate: moment(dateInactive).toDate(),
            changeMonth: true,
            numberOfMonths: 1,
            altFormat: 'yy-mm-dd',
            altField: '#arrivalDate',
            regional: language,
            beforeShowDay: self.unavailableArrival,
            onClose: function (selectedDate) {
                var minDateForDepartureDateElement = selectedDate != "" ? selectedDate : moment(minDate).toDate();
                departureDateElement.datepicker("option", "minDate", minDateForDepartureDateElement);
            }
        });
        localizeDatepicker(path + "/", arrivalDateElement, language);

        departureDateElement.datepicker({
            defaultDate: moment(endDate).toDate(),
            changeMonth: true,
            numberOfMonths: 1,
            minDate: moment(minDate).toDate(),
            maxDate: moment(dateInactive).toDate(),
            altFormat: 'yy-mm-dd',
            altField: '#departureDate',
            beforeShowDay: self.unavailableDeparture,
            onClose: function(selectedDate) {
                arrivalDateElement.datepicker("option", "maxDate", selectedDate);
            }
        });

        localizeDatepicker(path + "/", departureDateElement, language);

        // Assign Event Handlers
        $("#checkAvailability").click(checkAvailabilityClickHandler);
        $("#changeDates").click(function() {
            $("#changeDatesForm").slideDown();
            $("#searchDatesDisplay").hide();
        });
        $('input[name="childCount"]').change(function() {
            self.updateChildCountPB();
        });
        $(document).ajaxComplete(function() {
            self.updateChildCountPB();
        });

        // If the package isn't available, call the notAvailable function
        var promises = [];
        if (available) {
            $("#searchDatesDisplay").hide();

            var prevComponentId = 0;
            for (var i = 0; i < componentsJSON.length; i++) {
                if (componentsJSON[i].required) {
                    if (prevComponentId != componentsJSON[i].componentId) {
                        promises.push(self.loadComponentItems(componentsJSON[i].componentId));
                    }
                    prevComponentId = componentsJSON[i].componentId;
                }
            }
        } else {
            $("#changeDatesForm").hide();
            $(".alert-error").hide();
            $("#packageComponents").hide();
            $("#cta").hide();
            $("#optionalComponents").hide();
            $("#packageDescription").append('<div class="alert alert-error" name="packageNotAvailable">' + packageNA + '</div>');
        }

        $.when.apply($, promises).done(function() {
            // after the initialization has happened, run the document.ready functions
            // this assumes that the init function is run inside of a document.ready function
            for (var i = 0; i < readyQueue.length; i++) {
                var readyQueueFunc = readyQueue[i];
                if (readyQueueFunc != null) {
                    readyQueueFunc();
                }
            }
            readyQueue = []; //clear the queue

            self.updateChildCountPB();
            self.loadDefaults();
        });
    };

    return self;
})(jQuery);;
