/* Minification failed. Returning unminified contents.
(2702,41-52): run-time error JS1306: Invalid numeric literal: 01234567890
(2707,25-36): run-time error JS1306: Invalid numeric literal: 01234567980
 */
// instances
var globalData;
var pageData;
var appConversation;
var appHeader;

// namespace per page
var GetStartedPage = {

};

var LoginPage = {

};

var MemberDetailsPage = {

};

var PaymentPage = {

};

// shared models
var SharedModels = {

};
var SelectClubPage = {

};;
// the reason it is called config and not messages is because i thought that field lengths or other configuration could go here

var appValidationConfig = {
    member: {
        firstName: {
            empty: "Please enter the member first name",
            invalid: "First Name: Only A-Z, a-z, -, ' and space allowed"
        },
        lastName: {
            empty: "Please enter the member last name",
            invalid: "Last Name: Only A-Z, a-z, -, ' and space allowed"
        },
        birthDate: {
            under18: "If under 18, payment must be made at ",//club name here
            invalid: "Please enter a valid date of birth (dd/mm/yyyy)"
        },
        mobile: "Mobile number:  Maximum 10 digits with optional spaces",
        phone: "Phone number: 10 digits including the area code",
        phoneAndMobileMissing: "Please enter either a mobile or phone number",
        email: "Please enter a valid email address",
        street: "Please enter a street address",
        suburb: "Please enter a suburb",
        postcode: "Please enter a postcode",
        state: "Please select a state",
        contactName: {
            empty: "Please enter a contact name",
            invalid: "Only A-Z, a-z, -, ' and space allowed for contact name"
        },
        contactNumber: "Contact number: Maximum 10 digits with optional spaces",
        continueButtonError: "There are some errors, please fix them before proceeding"
    }
};
(function () {
    var validDateFormatRegex = new RegExp(/^(((0[1-9]|[12]\d|3[01])\/(0[13578]|1[02])\/((19|[2-9]\d)\d{2}))|((0[1-9]|[12]\d|30)\/(0[13456789]|1[012])\/((19|[2-9]\d)\d{2}))|((0[1-9]|1\d|2[0-8])\/02\/((19|[2-9]\d)\d{2}))|(29\/02\/((1[6-9]|[2-9]\d)(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00))))$/);
    var validNameRegex = new RegExp("^[a-zA-z ,'-]+$");
    var validPostcodeRegex = new RegExp("^\[0-9]{4}$");
    var validPhoneNumberRegex = new RegExp("^\[0-9]{10}$");

    ko.validation.rules['validateDOB'] = {
        validator: function (val) {
            //has valid date format
            if (!validDateFormatRegex.test(val)) {
                return false;
            }

            //is within acceptable age range
            var today = new Date();
            var birthDate = moment(val, "DD-MM-YYYY").toDate();
            var maxBirthDate = new Date(today.getFullYear() - 110, today.getMonth(), today.getDate());
            var minBirthDate = new Date(today.getFullYear() - 5, today.getMonth(), today.getDate());
            var isValidDateRange = birthDate > maxBirthDate && birthDate < minBirthDate;
            if (!isValidDateRange) {
                return false;
            }

            return true;
        },
        message: appValidationConfig.member.birthDate.invalid
    };

    ko.validation.rules['validateName'] = {
        validator: function (val, otherVal) {
            this.message = otherVal;
            return validNameRegex.test(val);
        }
    };

    ko.validation.rules['validatePostcode'] = {
        validator: function (val) {
            return validPostcodeRegex.test(val) && val > 0;
        },
        message: appValidationConfig.member.postcode
    };

    ko.validation.rules['validatePartnerCode'] = {
        validator: function (val, params) {
            if (!val) {
                return true;
            }
            this.message = params.params.message;
            var validRegex = new RegExp(params.params.regex);
            return validRegex.test(val.trim());
        }
    };

    ko.validation.rules['validatePhoneNumber'] = {
        validator: function (val, otherVal) {
            if (!val) {
                return true;
            }
            this.message = otherVal;
            // Remove spaces from number and check that max is 10.
            var strippedVal = val.toString().replace(/\s+/g, '');
            return validPhoneNumberRegex.test(strippedVal);
        }
    };

    //Credit Card Validation
    ko.validation.rules['creditcardnumber'] = {
        validator: function (val, cardType) {
            if (!val) {
                return false;
            }
            var validateStatus = false; // false means an error happened
            var value = String(val).replace(/[- ]/g, ''); //ignore dashes and whitespace
            var cardRegexLookup = {
                //             mc: Mastercard
                //             ec: Eurocard
                //             vi: Visa
                //             ax: American Express
                //             dc: Diners Club
                //             bl: Carte Blanch
                //             di: Discover
                //             jcb: JCB
                //             er: Enroute
                'mc': '5[1-5][0-9]{14}',
                'vi': '4(?:[0-9]{12}|[0-9]{15})',
                // Types not accepted now
                //'ax': '3[47][0-9]{13}'
                //'ec': '5[1-5][0-9]{14}',
                //'dc': '3(?:0[0-5][0-9]{11}|[68][0-9]{12})',
                //'bl': '3(?:0[0-5][0-9]{11}|[68][0-9]{12})',
                //'di': '6011[0-9]{12}',
                //'jcb': '(?:3[0-9]{15}|(2131|1800)[0-9]{11})',
                //'er': '2(?:014|149)[0-9]{11}'
            };

            // if there is not Credit Card type defined test against all types
            if (cardType.length === 0) {
                for (var creditCardType in cardRegexLookup) {
                    if (cardRegexLookup.hasOwnProperty(creditCardType)) {
                        if (value.match('^' + cardRegexLookup[creditCardType] + '$')) {
                            validateStatus = true;
                            break;
                        } else {
                            validateStatus = false;
                        };
                    };
                };
            } else {
                var regexPartial = cardRegexLookup[cardType];
                if (!regexPartial) { return false; };
                var regex = '^' + regexPartial + '$';
                if (value.match(regex)) { validateStatus = true; };
            };
            return validateStatus;
        },
        message: 'Card number is invalid'
    };

    //This rules checks the credit card details 
    //The card number (inferred) as well as the card type (via the card type field) are required 
    //This checks the length and starting digits of the card per the type
    //It also checks the checksum (see http://en.wikipedia.org/wiki/Luhn_algorithm)
    //The card type field must return 'vc' for visa, 'mc' for mastercard, 'ae' for amex
    //This is based on code from here: http://www.rahulsingla.com/blog/2011/08/javascript-implementing-mod-10-validation-(luhn-formula)-for-credit-card-numbers
    //Example:
    //
    //self.cardNumber.extend({ creditCard: self.cardType });
    ko.validation.rules['validateCreditCard'] = {
        validator: function (val, params) {
            var self = this;

            if (params) {
                return true;
            }
            self.creditCardList = [
           //["3", "34", 15], //amex
           //["3", "37", 15],
           ["2", "51", 16], //mastercard
           ["2", "52", 16],
           ["2", "53", 16],
           ["2", "54", 16],
           ["2", "55", 16],
           ["1", "4", 13], //visa
           ["1", "4", 16]];

            self.Luhn = function (cc) {
                var sum = 0;
                var i;

                for (i = cc.length - 2; i >= 0; i -= 2) {
                    sum += Array(0, 2, 4, 6, 8, 1, 3, 5, 7, 9)[parseInt(cc.charAt(i), 10)];
                }
                for (i = cc.length - 1; i >= 0; i -= 2) {
                    sum += parseInt(cc.charAt(i), 10);
                }
                return (sum % 10) == 0;
            }

            self.IsValidCc = function (ccnumber) {
                if (self.Luhn(ccnumber)) {
                    for (var i = 0; i < self.creditCardList.length; i++) {
                        if (ccnumber.indexOf(self.creditCardList[i][1]) == 0) {
                            if (self.creditCardList[i][2] == ccnumber.length) {
                                return true;
                            }
                        }
                    }
                }
                return false;
            }

            return self.IsValidCc(val);
        },
        message: 'Please enter a valid card number.'
    };

    ko.validation.init({
        registerExtenders: true,
        messagesOnModified: true,
        insertMessages: true,
        decorateInputElement: true,
        errorElementClass: 'validation-error',
        errorMessageClass: 'validation-message',
        parseInputAttributes: true,
        messageTemplate: null
    }, true);
})();;
Function.prototype.bindToEventHandler = function bindToEventHandler() {
    var handler = this;
    var boundParameters = Array.prototype.slice.call(arguments);
    //create closure
    return function (e) {
        e = e || window.event; // get window.event if e argument missing (in IE)   
        boundParameters.unshift(e);
        handler.apply(this, boundParameters);
    }
};

(function () {
    /* === common functions === */
    function addEventHandler(obj, evt, handler) {
        if (obj.addEventListener) {
            // W3C method
            obj.addEventListener(evt, handler, false);
        } else if (obj.attachEvent) {
            // IE method.
            obj.attachEvent('on' + evt, handler);
        } else {
            // Old school method.
            obj['on' + evt] = handler;
        }
    }
    function cancel(e) {
        if (e.preventDefault) {
            e.preventDefault();
        }
        return false;
    }

    function processFile(file, knockoutObservable) {
        knockoutObservable(file);
    }

    /* === fileUploadButton === */
    ko.bindingHandlers.fileUploadButton = {
        init: function (element, valueAccessor) {
            var button = element;

            $(button).on('change', function (evt) {
                var file = evt.target.files[0];
                processFile(file, valueAccessor());
            });
        }
    };

    /* === fileDropzone === */
    ko.bindingHandlers.fileDropzone = {
        update: function (element, valueAccessor) {
            // when the observable is cleared, clear the selected file from the file input box as well
            var value = valueAccessor()();
            if (!value) {
                var button = element.querySelector("input");
                if (button) {
                    if (button.parentNode.reset) {
                        button.parentNode.reset();
                    }
                    $(button).val("");
                }
            }
        },
        init: function (element, valueAccessor) {
            // Tells the browser that we *can* drop on this target
            addEventHandler(element, 'dragover', cancel);
            addEventHandler(element, 'dragenter', cancel);
            addEventHandler(element, 'drop', function (e) {
                e = e || window.event; // get window.event if e argument missing (in IE)
                if (e.preventDefault) {
                    e.preventDefault();
                } // stops the browser from redirecting.

                var dt = e.dataTransfer;
                var files = dt.files;

                var file = files[0];
                processFile(file, valueAccessor());
            });

            //if a button lies within the drop zone, assume it is a hidden button and wire a click event to it
            var button = element.querySelector("input");
            if (button) {
                addEventHandler(button, 'change', function () {
                    var file = button.files[0];
                    processFile(file, valueAccessor());
                });

                addEventHandler(element, 'click', function () {
                    button.click();
                });
            }
        }
    };
})();;
// Avoid `console` errors in browsers that lack a console.
(function() {
    var method;
    var noop = function () {};
    var methods = [
        'assert', 'clear', 'count', 'debug', 'dir', 'dirxml', 'error',
        'exception', 'group', 'groupCollapsed', 'groupEnd', 'info', 'log',
        'markTimeline', 'profile', 'profileEnd', 'table', 'time', 'timeEnd',
        'timeStamp', 'trace', 'warn'
    ];
    var length = methods.length;
    var console = (window.console = window.console || {});

    while (length--) {
        method = methods[length];

        // Only stub undefined methods.
        if (!console[method]) {
            console[method] = noop;
        }
    }
}());
;
var emergencyTablet = false;
var emergency;

$(window).resize(function () {
    //console.log("resize()");
    var windowHeight = $("body").height();
    var headerHeight = 50;
    var contentMinHeight = windowHeight - headerHeight;

    $(".pod-ecom-module").css("min-height", contentMinHeight);
    $(".profile-block").css("min-height", contentMinHeight);

    if (Modernizr.mq("only screen and (min-width: 768px) and (max-width: 1279px)")) {
        // Put the side back in place
        if (!emergencyTablet) {
            $(".profile-block").each(function () {
                emergency = $(this).find(".emergency-contact").detach();
                emergency.appendTo($(this).find(".emergency-target"));
            });
        }
        emergencyTablet = true;
    } else {
        // Put the side back in place
        if (emergencyTablet) {
            $(".profile-block").each(function () {
                emergency = $(this).find(".emergency-contact");
                emergency.insertAfter($(this).find(".emergency-after"));
            });
        }

        emergencyTablet = false;
    }
});

$(document).ready(function () {
    $(window).resize();
});;
// format's text with bold if enclosed in ~ chars
function formatText(text) {
    if (!text) {
        return '';
    }
    if (text.indexOf("~") < 0) {
        return text;
    }

    var result = '';
    var array = text.split("~");
    var isBold = false;
    var i, titleSegment;
    for (i = 0; i < array.length; i++) {
        titleSegment = array[i];
        if (titleSegment) {
            var className = isBold ? 'class="text-bold"' : "";
            result += '<span ' + className + '>' + titleSegment + '</span>';
        }
        isBold = !isBold;
    }
    return result;
}

// ===String format() function==
// usage: "something {0} something".format(stringVar)
if (!String.prototype.format) {
    String.prototype.format = function () {
        var args = arguments;
        return this.replace(/{(\d+)}/g, function (match, number) {
            return typeof args[number] != 'undefined'
              ? args[number]
              : match;
        });
    };
}

function setLoadingIsVisible(isVisible) {
    if (isVisible) {
        $('#loading').show();
        if (!$('#noClubNotification').hasClass('hidden')){
            $('#noClubNotification').addClass('hidden');
        }
    } else {
        $('#loading').hide();
        $('#noClubNotification').removeClass('hidden');
    }
}

function CallAjax(url, type, data, successCallback, errorCallback, noLoading) {
    if (!noLoading) {
        setLoadingIsVisible(true);
    }

    $.ajax({
        url: url,
        type: type,
        data: data,
        contentType: "application/json",
        success: function (data, status, response) {
            // Handle page re-direct required
            if (data.RedirectUrl) {
                window.location.href = data.RedirectUrl;
                return;
            }
            // Handle error
            if (!data.Success) {
                appHeader.SetError(data.Message ? data.Message : 'Oops, something went wrong. Please try again.');
                if (errorCallback) { errorCallback(data.Message); }
                return;
            }
            // Handle success
            if (successCallback) { successCallback(data.Data); }
        },
        error: function (data, status, response) {
            // Handle http error
            appHeader.SetError(data.Message ? data.Message : 'Oops, something went wrong. Please try again.');
            if (errorCallback) { errorCallback(data.Message); }
        },
        complete: function () {
            setLoadingIsVisible(false);
        }
    });
}

function PrintHtml(html, pageTitle) {
    var myWindow = window.open('', '', 'height=400,width=600');
    var myDoc = myWindow.document;

    var onloadFunc = function() {
        myDoc.close(); // necessary for IE >= 10
        myWindow.focus(); // necessary for IE >= 10
        myWindow.print();
        //myWindow.close();
    }

    myDoc.write('<html><head><title>' + pageTitle + '</title></head><body>' + html + '</body></html>');

    // only print once all images are loaded
    var images = $(myDoc.body).find("img");
    var numImages = images.length;

    if (numImages < 1) {
        onloadFunc();
        return;
    }
    
    var loadedImages = 0;
    images.one("load", function () {
        loadedImages++;
        if (loadedImages >= numImages) {
            window.setTimeout(onloadFunc, 100);
        }
    }).each(function () {
        if (this.complete) $(this).load();
    });
}

function submitFORM(path, params, method, targetName) {
    method = method || "POST";

    var form = document.createElement("form");
    form.setAttribute("method", method);
    form.setAttribute("action", path);
    if (targetName) {
        form.setAttribute("target", targetName);
    }

    //Move the submit function to another variable
    //so that it doesn't get overwritten.
    form._submit_function_ = form.submit;

    for (var key in params) {
        if (params.hasOwnProperty(key)) {
            var hiddenField = document.createElement("input");
            hiddenField.setAttribute("type", "hidden");
            hiddenField.setAttribute("name", key);
            hiddenField.setAttribute("value", params[key]);

            form.appendChild(hiddenField);
        }
    }

    document.body.appendChild(form);
    form._submit_function_();
}

function scrollToElement(selector) {
    setTimeout(function() { $.scrollTo(selector, 800, { easing: "easeOutExpo", axis: "y" }); }, 1000);
};
(function () {
    setLoadingIsVisible(true);
    GetStartedPage.SelectClubPageViewModel = function () {
        var self = this;
        this.states = ko.observableArray([{ name: "All", value: "ALL", active: ko.observable(true) },
//                                          { name: "ACT", value: "Australian Capital Teritory", active: ko.observable(false) },
//                                          { name: "NSW", value: "New South Wales", active: ko.observable(false) },
//                                          { name: "QLD", value: "Queensland", active: ko.observable(false) },
                                            { name: "SA", value: "South Australia", active: ko.observable(false) },
                                            { name: "TAS", value: "Tasmania", active: ko.observable(false) },
                                            { name: "VIC", value: "Victoria", active: ko.observable(false) }
//                                          { name: "WA", value: "Western Australia", active: ko.observable(false) },
                                          
        ]);


        this.clubs = ko.observableArray();
        this.allClubs = ko.observableArray();
        self.states.sort(function (left, right) {
            return left.name === right.name ? 0
                : left.name < right.name ? -1
                : 1;
        });
        globalData.clubs.forEach(function (club) {
            if (!club) {
                setLoadingIsVisible(false);
                return;
            }
            self.clubs.push(club);
            self.allClubs.push(club);
            setLoadingIsVisible(false);
        });
        self.clubs.sort(function (left, right) {
            return left.Name === right.Name ? 0
                : left.Name < right.Name ? -1
                : 1;
        });

        self.allClubs.sort(function (left, right) {
            return left.Name === right.Name ? 0
                : left.Name < right.Name ? -1
                : 1;
        });


        clubSelected = function (args) {
            var selectedClub = JSON.parse(JSON.stringify(args));
            document.cookie = "Zap.ClubShortlist=" + selectedClub.ClubId + ";expires=" + (new Date(9999, 0, 1)).toGMTString() + ";path=/";

            CallAjax(
                "/ClubSelector/SelectClub",
                "POST",
                JSON.stringify(selectedClub),
                function () {
                    window.location.href = "/GetStarted";
                });
        }

        self.filterClub = function (args) {
            if (args == "ALL") {
                self.clubs(self.allClubs());
                return;
            }

            var newArr = ko.observableArray([]);
            for (var i = 0; i < self.allClubs().length; i++) {
                if (self.allClubs()[i].Address.State.toLowerCase() == args.toString().toLowerCase()) {
                    newArr.push(self.allClubs()[i]);
                }
            }
            self.clubs(newArr());
            self.matchHeight();
        }

        self.styling = function (state) {
            if (state.active() === true) {
                return 'selected';
            }
            else {
                return '';
            }
        };

        self.toggle = function (state) {
            
            ko.utils.arrayForEach(self.states(), function (obj) {
                obj.active(false);
            });

            state.active(true);
        }

        this.matchHeight = function () {
            $(".pod-ecom-club ul li").matchHeight();
        }
    }

})();;
(function () {
    SharedModels.ConversationModel = function (json) {

        //console.log(json);

        //json.SelectedPartner = {
        //    Id: "AE82F54B-7741-4355-8AE2-5B70FEF6760E",
        //    Name: "Flybuys",
        //    ValidationText: "FLYBUYS DETAILS",
        //    ValidationRegex: "^\[0-9]{4}$", //postcode regex
        //    ValidationPlaceholderText: "Enter number",
        //    TermsLabel: "some text above",
        //    TermsAndConditions: "<p>some html below</p>",
        //};

        this.ConversationId = ko.observable(json.ConversationId);
        this.SelectedPartner = ko.observable(json.SelectedPartner ? json.SelectedPartner : null);
        this.SelectedClub = ko.observable(json.SelectedClub ? json.SelectedClub : null);
        this.SelectedMembership = ko.observable(json.SelectedMembership ? new SharedModels.MembershipModel(json.SelectedMembership) : null);
        this.SelectedOptions = ko.observableArray(json.SelectedOptions);
        this.MemberDetailsRawJson = json.MemberDetails; // member details uses just the raw json, so should payment details, todo
        this.MemberDetails = ko.mapping.fromJS(json.MemberDetails); //todo: maybe this shouldn't be part of the conversation -- does not really belong and can add uneccessary image data

        this.isClubSelected = ko.computed(function () {
            return !!this.SelectedClub();
        }, this);

        this.isMembershipSelected = ko.computed(function () {
            return !!this.SelectedMembership();
        }, this);

        this.isClubAccessOptionAvailable = ko.computed(function () {
            return this.isMembershipSelected() ? this.SelectedMembership().isClubAccessOptionAvailable : false;
        }, this);

        this.isAnyValueOptionsAvailable = ko.computed(function () {
            return this.isMembershipSelected() ? this.SelectedMembership().Options.length > 0 : false;
        }, this);

        this.isAnyValueOptionsSelected = ko.computed(function () {
            return this.SelectedOptions().length > 0;
        }, this);

        this.saveAndContinueToLogin = function() {
            var json = {
                ConversationId: this.ConversationId(),
                SelectedClub: this.SelectedClub(),
                SelectedPartner: this.SelectedPartner(),
                SelectedMembership: this.SelectedMembership(),
                SelectedOptions: this.SelectedOptions()
            };

            if (this.isAnyValueOptionsSelected()) {
                var cartOptions = [];

                // Add selected products to Google Analytics
                _.each(this.SelectedOptions(), function(option) {
                    cartOptions.push({
                        'name': option.Name,
                        'id': option.Name,
                        'price': option.Price,
                        'brand': appHeader.clubName(),
                        'category': 'Option',
                        'variant': 'Option',
                        'list': 'Option',
                        'quantity': 1
                    });
                });

                dataLayer.push({
                    'event': 'addToCart',
                    'ecommerce': {
                        'currencyCode': 'AUD',
                        'add': {
                            'actionField': { 'list': 'Option' },
                            'products': cartOptions
                        }
                    }
                });
            }

            CallAjax(
                "/Conversation/AddConversation",
                "POST",
                JSON.stringify(json),
                function() {
                    //window.location.href = "/login";
                    window.location.href = "/details";
                });

        }.bind(this);
    };
})();;
(function () {
    SharedModels.HeaderViewModel = function (conversation) {
        var self = this;

        var msgTimeout;

        var savePosition = 0;
        var tempPosition = 0;

        // List of work-flow steps
        self.WorkflowStepEnum = {
            SelectClub: 1,
            SelectMembershipOption: 2,
            SelectClubAccess: 3,
            SelectValueOptions: 4,
            Login: 5,
            MemberDetails: 6,
            Payment: 7,
            WhatsLeft: 8
        };

        self.isLocked = ko.observable(false);
        self.enableGoodlifeCMSLink = true;

        self.statusText = ko.observable("");

        self.lock = function () {
            self.isLocked(true);
            self.enableGoodlifeCMSLink = false;
        }

        self.unlock = function () {
            self.isLocked(false);
            self.enableGoodlifeCMSLink = true;
        }

        // === Club computeds ===
        self.isClubSelected = ko.pureComputed(function () {
            return conversation.isClubSelected();
        }, this);

        self.clubName = ko.pureComputed(function () {
            return conversation.isClubSelected() ? conversation.SelectedClub().Name : "Goodlife";
        }, this);

        self.clubAddress = ko.pureComputed(function () {
            return conversation.isClubSelected() ? conversation.SelectedClub().Address.Line1 : '';
        }, this);

        // === Membership computeds ===
        self.isMembershipSelected = ko.pureComputed(function () {
            return conversation.isMembershipSelected();
        }, this);

        self.selectedMembershipWeeklyPrice = ko.pureComputed(function () {
            return conversation.isMembershipSelected() ? conversation.SelectedMembership().WeeklyPrice : '';
        }, this);

        self.selectedTotalWeeklyPrice = ko.pureComputed(function () {
            // Total up ongoing options to include in the weekly price.
            var optionsTotal = 0;
            conversation.SelectedOptions().forEach(function (opt) {
                if (opt.IsPaidWeekly) {
                    optionsTotal += opt.Price;
                }
            });
            return conversation.isMembershipSelected() ? (conversation.SelectedMembership().WeeklyPrice + optionsTotal).toFixed(2) : '';
        }, this);

        self.selectedMembershipName = ko.pureComputed(function () {
            return conversation.isMembershipSelected() ? conversation.SelectedMembership().Name : '';
        }, this);

        self.selectedMembershipTag = ko.pureComputed(function () {
            return conversation.isMembershipSelected() ? conversation.SelectedMembership().Tag : '';
        }, this);

        self.isSelectedMembershipNational = ko.pureComputed(function () {
            return conversation.isMembershipSelected() ? conversation.SelectedMembership().IsNational : null;
        }, this);

        // === Value option computeds ===
        self.isAnyValueOptionsSelected = ko.pureComputed(function () {
            return conversation.isAnyValueOptionsSelected();
        }, this);

        self.valueOptionsAreEditable = ko.pureComputed(function () {
            return conversation.isAnyValueOptionsAvailable() && self.IsAccessSelected();
        }, this);

        self.selectedOptionsSummary = ko.pureComputed(function () {
            var selectedOptions = conversation.SelectedOptions();
            if (selectedOptions.length < 1) {
                return "Option: none selected";
            }
            if (selectedOptions.length > 1) {
                return selectedOptions.length + " options";
            }
            return "Option: " + selectedOptions[0].Name;
        }, this);

        self.selectedOptions = ko.pureComputed(function () {
            return conversation.SelectedOptions();
        });

        // Keep track of the step the user is in the work-flow. Default to SelectClub
        self.WorkflowStep = ko.observable();
        self.IsAccessSelected = ko.observable(false);

        // Sitecore URL
        self.GoodlifeSiteUrl = globalData.sitecoreUrl;

        // Check if the Workflow is on the Select Club step or error page
        self.CanWorkflowStepGoBack = ko.pureComputed(function () {
            return (self.WorkflowStep() <= self.WorkflowStepEnum.SelectClub) ? true : false;
        });

        // UI
        self.SetTopMessage = function (message) {
            clearTimeout(msgTimeout);
            if ($(".pod-top-module .msg").hasClass('error-msg')) {
                $(".pod-top-module .msg").removeClass('error-msg');
            }
            $(".pod-top-module .msg").html(formatText(message));
            $(".pod-top-module .msg").fadeIn(400, function () {
                msgTimeout = setTimeout(function () { $(".pod-top-module .msg").fadeOut(400); }, 3000);
            });
        };
        self.SetError = function (error) {
            clearTimeout(msgTimeout);
            $(".pod-top-module .msg").addClass('error-msg').html(formatText(error));
            $(".pod-top-module .msg").fadeIn(1000, function () {
                msgTimeout = setTimeout(function () {
                    $(".pod-top-module .msg").fadeOut(1000);
                    $(".pod-top-module .msg").removeClass('error-msg');
                }, 20000);
            });
        };
        self.ClearError = function () {
            $(".pod-top-module .msg").removeClass('error-msg');
            $(".pod-top-module .msg").hide();
        };
        self.ToggleSummary = function () {
            if (self.isLocked()) {
                return;
            }
            if (self.WorkflowStep() >= self.WorkflowStepEnum.SelectClub) {
                tempPosition = $(document).scrollTop();

                $(".pod-top-module").toggleClass("on");
                $(".pod-top-module .pod-top-ecom").toggleClass("hidden");
                $(".ecom-mask").toggleClass("visible");

                $("html").toggleClass("fixed");
                $('body').toggleClass('no-scroll');

                if ($("html").hasClass("fixed")) {
                    // opening summary
                    $(".pod-top-ecom .top-module:not(.hidden)").matchHeight(); //resize summary boxes on opening
                    $('body').css('top', -(tempPosition) + 'px');
                } else {
                    // closing summary
                    $('body').scrollTo(savePosition);
                }
                savePosition = tempPosition;
            }
        };

        self.CloseSummary = function () {
            tempPosition = $(document).scrollTop();

            if ($("html").hasClass("fixed")) {

                $(".pod-top-module").toggleClass("on");
                $(".pod-top-module .pod-top-ecom").toggleClass("hidden");
                $(".ecom-mask").toggleClass("visible");

                $("html").toggleClass("fixed");
                $('body').toggleClass('no-scroll');

                $('body').scrollTo(savePosition);
            }
            savePosition = tempPosition;
        };

        self.GotoGoodlifeCMS = function () {
            if (!self.enableGoodlifeCMSLink) {
                return;
            }

            var url = self.GoodlifeSiteUrl;
            if (url.indexOf('http://') != 0 && url.indexOf('https://') != 0) {
                url = 'http://' + url;
            }
            window.location.href = url;
        };

        // Navigate the GetStarted Page Sections using the left top side arrow (or logo)
        self.BackButtonNavigation = function () {
            var ActualWorkflowStep = self.WorkflowStep();
            var PreviousWorkflowStep;

            if (ActualWorkflowStep === self.WorkflowStepEnum.MemberDetails)
            {
                PreviousWorkflowStep = self.WorkflowStepEnum.SelectValueOptions;
            }
            else
            {
                PreviousWorkflowStep = ActualWorkflowStep - 1;
            }
            //var PreviousWorkflowStep = ActualWorkflowStep - 1;
            // if in the select club already, goes back to site core
            if (ActualWorkflowStep <= self.WorkflowStepEnum.SelectClub) {
                self.GotoGoodlifeCMS();
            } else {
                // Check for gaps in the workflow
                switch (PreviousWorkflowStep) {
                    case self.WorkflowStepEnum.SelectMembershipOption:
                        if (!conversation.isClubSelected()) { PreviousWorkflowStep = self.WorkflowStepEnum.SelectClub; };
                        break;
                    case self.WorkflowStepEnum.SelectClubAccess:
                        if (!conversation.isClubAccessOptionAvailable()) { PreviousWorkflowStep = self.WorkflowStepEnum.SelectMembershipOption; };
                        if (!conversation.isMembershipSelected()) { PreviousWorkflowStep = self.WorkflowStepEnum.SelectMembershipOption; };
                        if (!conversation.isClubSelected()) { PreviousWorkflowStep = self.WorkflowStepEnum.SelectClub; };
                        break;
                    case self.WorkflowStepEnum.SelectValueOptions:
                        if (!conversation.isAnyValueOptionsAvailable()) { PreviousWorkflowStep = self.WorkflowStepEnum.SelectClubAccess; };
                        if (!conversation.isClubAccessOptionAvailable()) { PreviousWorkflowStep = self.WorkflowStepEnum.SelectMembershipOption; };
                        if (!conversation.isMembershipSelected()) { PreviousWorkflowStep = self.WorkflowStepEnum.SelectMembershipOption; };
                        if (!conversation.isClubSelected()) { PreviousWorkflowStep = self.WorkflowStepEnum.SelectClub; };
                        break;
                };
                // Goes to the next in the workflow list
                self.WorkflowStep(PreviousWorkflowStep);
                // Navigate to Object
                self.NavigateToWorkflowStep(self.WorkflowStep());
            };
        };

        self.height = 50; // height of the header div

        // Generic Navigation function
        self.NavigateToWorkflowStep = function (WorkflowStep, isSummaryOpen) {
            // check if the parameter is correct
            if (WorkflowStep >= self.WorkflowStepEnum.SelectClub && WorkflowStep <= self.WorkflowStepEnum.WhatsLeft) {
                if (isSummaryOpen) {
                    self.ToggleSummary();
                }
                // Change the workflow step
                self.WorkflowStep(WorkflowStep);
                // Adjustment to fit just below the header
                var topHeaderHeight = 0;
                // which page
                var domain = window.location.origin;
                if (!domain) {
                    // handle browsers that don't support origin.
                    domain = window.location.protocol + "//" + window.location.hostname + (window.location.port ? ':' + window.location.port : '');
                }
                var targetPage;
                // where in the page
                var elementId;
                // pick the target page / element
                switch (WorkflowStep) {
                    case self.WorkflowStepEnum.SelectClub:
                        topHeaderHeight = self.height;
                        elementId = '#selectYourClub';
                        targetPage = "/GetStarted/";
                        break;
                    case self.WorkflowStepEnum.SelectMembershipOption:
                        elementId = '#selectMembershipOption';
                        targetPage = "/GetStarted/";
                        break;
                    case self.WorkflowStepEnum.SelectClubAccess:
                        elementId = '#selectClubAccess';
                        targetPage = "/GetStarted/";
                        break;
                    case self.WorkflowStepEnum.SelectValueOptions:
                        elementId = '#selectValueOptions';
                        targetPage = "/GetStarted/";
                        break;
                    case self.WorkflowStepEnum.Login:
                        elementId = '';
                        targetPage = '/Login/';
                        break;
                    case self.WorkflowStepEnum.MemberDetails:
                        elementId = 'input[name=SubmitButton]';
                        targetPage = '/Details/';
                        break;
                    case self.WorkflowStepEnum.Payment:
                        elementId = 'p .submit';
                        targetPage = '/Payment/';
                        break;
                    case self.WorkflowStepEnum.WhatsLeft:
                        elementId = '';
                        targetPage = '/Payment/';
                        break;
                    default:
                        elementId = 'selectYourClub';
                        targetPage = "/GetStarted/";
                };
                // scroll the desired element, if it exist in the page (GetStarted).
                if ($(elementId).length > 0) {
                    $('html, body').animate({
                        scrollTop: $(elementId).offset().top - topHeaderHeight
                    }, 1000);
                } else {
                    // if not, goes to the target Page
                    window.location.href = domain + targetPage + ((targetPage === "/GetStarted/" && elementId) ? elementId : '');
                };
            };
        };
    };
})();
;
//var LoginManagerModel = function () {
//    this.hasInitialized = ko.observable(false);
//    this.accountInfo = ko.observable(null);
//    this.isLoggedIn = ko.computed(function() {
//        return !!this.accountInfo();
//    }, this);

//    this.showLoginScreen = function () {
//        gigya.accounts.showScreenSet({
//            screenSet: 'Ecom-RegistrationLogin',
//            containerID: 'gigyaLoginScreen',
//            onAfterSubmit: function (arg) {
//                var event = arg.response.event;
//                var response = event.response;
//                populateAccountInfo(response);
//            }.bind(this),
//            onHide: function () {
//                // when logging in through site account, onAfterSubmit doesn't fire. this is the fallback event to populate account details.
//                queryAccountInfo();
//            }
//        });
//    }.bind(this);

//    this.logout = function () {
//        gigya.accounts.logout({
//            callback: function (response) {
//                if (response.errorCode == 0) {
//                    this.accountInfo(null);
//                } else {
//                    alert('Error :' + response.errorMessage);
//                }
//            }.bind(this)
//        });
//    };

//    var writeLoginCookie = function(idFields) {
//        var value = idFields ? idFields.UID : "";
//        var expiry = idFields ? "" : ";expires=Thu, 01 Jan 1970 00:00:01 GMT";
//        var name = globalData.loginCookieName + '.' + globalData.cookieDomain;
//        document.cookie = name + "=" + value + expiry + ";path=/";
//    };

//    var manageLoginCookie = ko.computed(function () {
//        if (!this.hasInitialized()) {
//            return;
//        }

//        var accountInfo = this.accountInfo();
//        if (accountInfo) {
//            // write session cookie that server will recognise - so it knows we are logged in
//            writeLoginCookie(accountInfo.idFields);
//        } else {
//            // remove cookie as it is invalid
//            writeLoginCookie(null);
//        }
//    }, this);

//    var populateAccountInfo = function (ai) {
//        console.log(ai);

//        this.accountInfo({
//            idFields: {
//                UID: ai.UID,
//                UIDSignature: ai.UIDSignature,
//                Timestamp: ai.signatureTimestamp
//            },
//            profile: ai.profile, // profile can contain: email, firstName, lastName, photoURL... see http://developers.gigya.com/display/GD/Profile+JS
//            data: ai.data // custom fields in here
//        });
//    }.bind(this);

//    var queryAccountInfo = function () {
//        gigya.accounts.getAccountInfo({
//            callback: function (response) {
//                if (response.errorCode !== 0) {
//                    // user not logged in so clear account info and display login screen
//                    this.accountInfo(null);
//                } else {
//                    populateAccountInfo(response);
//                }
//                this.hasInitialized(true);
//            }.bind(this)
//        });
//    }.bind(this);

//    // initialize
//    queryAccountInfo();
//};;
(function () {
    SharedModels.MembershipCollectionModel = function () {
        this.memberships = ko.observableArray();

        this.getById = function (id) {
            return _.find(this.memberships(), function (membership) {
                return membership.Id === id;
            });
        }.bind(this);

        this.getMembershipsForSlot = function (slotName) {
            return _.filter(this.memberships(), function (membership) {
                return membership.slotName === slotName;
            });
        }.bind(this);

        this.leftMemberships = ko.computed(function () {
            return this.getMembershipsForSlot("left");
        }, this);

        this.rightMemberships = ko.computed(function () {
            return this.getMembershipsForSlot("right");
        }, this);

        this.promoMemberships = ko.computed(function () {
            return this.getMembershipsForSlot("promo");
        }, this);

        this.getMembershipRow = function (isNational) {
            var promo = _.find(this.promoMemberships(), function (membership) {
                return !!membership.IsNational === isNational;
            });
            if (!promo) {
                promo = _.first(this.promoMemberships());
            }

            var left = _.find(this.leftMemberships(), function (membership) {
                return !!membership.IsNational === isNational;
            });
            if (!left) {
                left = _.first(this.leftMemberships());
            }

            var right = _.find(this.rightMemberships(), function (membership) {
                return !!membership.IsNational === isNational;
            });
            if (!right) {
                right = _.first(this.rightMemberships());
            }

            var returnArray = [];
            if (promo) returnArray.push(promo);
            if (left) returnArray.push(left);
            if (right) returnArray.push(right);

            return returnArray;
        }.bind(this);

        this.nationalMemberships = ko.computed(function () {
            return this.getMembershipRow(true);
        }, this);

        this.singleMemberships = ko.computed(function () {
            return this.getMembershipRow(false);
        }, this);

        this.isAny = ko.computed(function () {
            return this.memberships().length > 0;
        }, this);

        this.populateMemberships = function (response) {
            if (response.options && response.options.length > 0) {
                var membershipArray = _.map(response.options, function (membership) {
                    return new SharedModels.MembershipModel(membership);
                });
                this.memberships(membershipArray);
                return true;
            }

            var partner = appConversation.SelectedPartner();
            if (partner) {
                //show banner message
                var clubName = appConversation.SelectedClub().Name;
                appHeader.SetError("Sorry, " + clubName + " cannot accept " + partner.Name + " memberships, please select another club.");
            }
            else if (response.RedirectUrl) {
                window.location = response.RedirectUrl;
            }
            return false;
        }.bind(this);

        this.loadMembershipsForClub = function (clubId, hideLoading) {
            this.memberships.removeAll();

            var partnerId = null;
            var partner = appConversation.SelectedPartner();
            if (partner) {
                partnerId = partner.Id;
            }

            CallAjax(
                "/GetStarted/GetMembershipOptionsByClubId",
                "POST",
                JSON.stringify({ ClubId: clubId, PartnerId: partnerId }),
                function (data) {
                    var success = this.populateMemberships(data);
                    if (success && !hideLoading) {
                        scrollToElement(".pod-ecom-memberships");
                    }
                }.bind(this),
                function () {
                    // scroll back to top
                    scrollToElement(".pod-header");
                }, hideLoading);
        }.bind(this);
    };
})();;
(function() {
    SharedModels.MembershipModel = function (json) {
        //todo work out how to do this automatically without making observables of everything
        this.Quantity = json.Quantity;
        this.AdditionalCosts = json.AdditionalCosts;
        this.PromoLine = json.PromoLine;
        this.DisplayPrice = json.DisplayPrice;
        this.Tag = json.Tag;
        this.ClubEnhancementFee = json.ClubEnhancementFee;
        this.ClubEnhancementFeeInterval = json.ClubEnhancementFeeInterval;
        this.Length = json.Length;
        this.Name = json.Name;
        this.BannerAcknowledgement = json.BannerAcknowledgement;
        this.FreeTimeModel = json.FreeTimeModel;
        this.ClubEnhancementFeeName = json.ClubEnhancementFeeName;
        this.IsOnGoing = json.IsOnGoing;
        this.IsPromotion = json.IsPromotion;
        this.PairedMembershipOptionId = json.PairedMembershipOptionId;
        this.WeeklyPrice = json.WeeklyPrice;
        this.Term = json.Term;
        this.AdminFeeName = json.AdminFeeName;
        this.DisplayOnLeft = json.DisplayOnLeft;
        this.IsNational = json.IsNational;
        this.MembershipOptionGuid = json.MembershipOptionGuid;
        this.Includes = json.Includes;
        this.Id = json.Id;
        this.AdminFee = json.AdminFee;
        this.Options = json.Options;
        this.Discount = json.Discount;
        this.IsAdminFeeWaived = json.IsAdminFeeWaived;
        //end json properties

        this.isClubAccessOptionAvailable = !!this.PairedMembershipOptionId;
        this.isAnyOptionsAvailable = this.Options.length > 0;
        this.showFromText = this.WeeklyPrice !== this.DisplayPrice;

        var getAvailabilityText = function() {
            if (this.isClubAccessOptionAvailable) {
                return ("National/one club option available");
            } else if (this.IsNational) {
                return ("National club option only");
            } else {
                return ("Home club option only");
            }
        }.bind(this);
        this.availabilityText = getAvailabilityText();

        var getSlotName = function () {
            if (this.IsPromotion) {
                return "promo";
            }
            if (this.DisplayOnLeft) {
                return "left";
            }
            return "right";
        }.bind(this);
        this.slotName = getSlotName();
    };
})();;
(function () {
    GetStartedPage.GetStartedPageViewModel = function (conversation) {
        setLoadingIsVisible(false);
        this.availableMemberships = new SharedModels.MembershipCollectionModel();

        this.SelectClubSection = new GetStartedPage.SelectClubViewModel(conversation.SelectedClub);
        this.SelectMembershipSection = new GetStartedPage.SelectMembershipViewModel(conversation.SelectedMembership, this.availableMemberships);
        this.SelectClubAccessSection = new GetStartedPage.SelectClubAccessViewModel(conversation.SelectedMembership, this.availableMemberships, conversation.SelectedClub, this.SelectClubSection.clubs);
        this.SelectValueOptionsSection = new GetStartedPage.SelectValueOptionsViewModel(conversation.SelectedMembership, conversation.SelectedOptions);

        conversation.SelectedClub.subscribe(function (selectedClub) {
            if (!selectedClub) {
                return;
            }
            this.availableMemberships.loadMembershipsForClub(selectedClub.ClubId);
        }, this);

        var onloadSelectedClub = conversation.SelectedClub();
        if (onloadSelectedClub) {
            var firstClub = this.SelectClubSection.firstClubOnLoad;
            if (firstClub.ClubId === onloadSelectedClub.ClubId) {
                var hideLoading = window.location.hash !== "#selectMembershipOption";
                this.availableMemberships.loadMembershipsForClub(onloadSelectedClub.ClubId, hideLoading);
            } else {
                this.SelectClubSection.selectClub(firstClub);
            }
        }
    };
})();;
(function () {
    GetStartedPage.SelectClubAccessViewModel = function (selectedMembership, availableMemberships, selectedClub, clubShortlist) {
        setLoadingIsVisible(false);
        this.homeOption = ko.observable(null);
        this.allClubsOption = ko.observable(null);
        this.selectedMembership = selectedMembership;
        this.selectedClub = selectedClub;

        this.isVisible = ko.pureComputed(function () {
            if (this.homeOption() && this.allClubsOption()) {
                return true;
            }
            return false;
        }, this);

        this.sliderState = ko.pureComputed(function () {
            if (selectedMembership() && selectedMembership().IsNational) {
                return 'all';
            };
            return 'one';
        }, this);

        this.selectHome = function () {
            selectedMembership(this.homeOption());
            appHeader.SetTopMessage('One club that rules them all');
        }.bind(this);

        this.selectAllClubs = function () {
            selectedMembership(this.allClubsOption());
            appHeader.SetTopMessage('All clubs it is!');
        }.bind(this);

        this.sliderClick = function () {
            if (selectedMembership().IsNational) {
                this.selectHome();
            } else {
                this.selectAllClubs();
            };
            appHeader.IsAccessSelected(true);

            // Write product impression and detail view for tag manager.
            var membership = [
                {
                    'name': selectedMembership().Name,
                    'id': selectedMembership().MembershipOptionGuid,
                    'price': selectedMembership().DisplayPrice,
                    'brand': appHeader.clubName(),
                    'category': 'Membership',
                    'list': 'Membership',
                    'variant': selectedMembership().Term
                }
            ];

            dataLayer.push({
                'event': 'membershipsLoaded',
                'ecommerce': {
                    'currencyCode': 'AUD',
                    'impressions': membership,
                    'detail': {
                        'actionField': { 'list': 'Membership' },
                        'products': membership
                    }
                }
            });
        }.bind(this);

        // Slider swipe event handler
        // todo --- make this a custom binding --- this probably won't work as is
        $(".pod-ecom-allclubs .slider").swipe({
            swipe: function (event, direction) {
                if (direction === "left") {
                    this.selectAllClubs();
                } else {
                    this.selectHome();
                }
            }.bind(this),
            // Default is 75px, set to 0 for demo so any distance triggers swipe
            threshold: 25
        });

        this.continueClick = function () {
            appHeader.IsAccessSelected(true);

            // Write selected membership to Google Analytics.
            dataLayer.push({
                'event': 'addToCart',
                'ecommerce': {
                    'currencyCode': 'AUD',
                    'add': {
                        'actionField': { 'list': 'Membership' },
                        'products': [
                            {
                                'name': selectedMembership().Name,
                                'id': selectedMembership().MembershipOptionGuid,
                                'price': selectedMembership().WeeklyPrice,
                                'brand': appHeader.clubName(),
                                'category': 'Membership',
                                'list': 'Membership',
                                'variant': selectedMembership().Term,
                                'quantity': 1
                            }
                        ]
                    }
                }
            });

            if (!selectedMembership().isAnyOptionsAvailable) {

                // submit the page and redirect to member details
                appConversation.saveAndContinueToLogin(); //todo remove global reference to appConversation
         
            } else {
                // Scrolls automatically to the options. Update the Workflow step to the next step
                appHeader.WorkflowStep(appHeader.WorkflowStepEnum.SelectValueOptions);

                // Write options impressions and selected membership to global Google eCommerce dataLayer.
                var options = [];

                _.each(selectedMembership().Options, function(option) {
                    options.push({
                        'name': option.Name,
                        'id': option.Name,
                        'price': option.Price,
                        'brand': appHeader.clubName(),
                        'category': 'Option',
                        'list': 'Option',
                        'variant': 'Option'
                    });
                });

                dataLayer.push({
                    'event': 'optionsLoaded',
                    'ecommerce': {
                        'currencyCode': 'AUD',
                        'impressions': options,
                        'detail': {
                            'actionField': { 'list': 'Option' },
                            'products': options
                        }
                    }
                });

                scrollToElement(".pod-ecom-options");
            }
        }.bind(this);

        // Maps functionality
        this.showMap = function () {
            var center = new google.maps.LatLng(-23.7, 133.87); // Alice Springs
            if (this.mapall == null) {
                this.mapall = new google.maps.Map(document.getElementById("all-clubs-map"), {
                    zoom: 4,
                    center: center,
                    mapTypeId: google.maps.MapTypeId.ROADMAP
                });

                var markers = [];
                for (var i = 0; i < globalData.clubs.length; i++) {
                    var club = globalData.clubs[i];
                    var latLng = new google.maps.LatLng(club.Latitude, club.Longitude);
                    var marker = new google.maps.Marker({
                        position: latLng,
                        icon: "/Content/Image/icn-marker.png"
                    });
                    markers.push(marker);
                }

                var mcOptions = {
                    styles: [
                        {
                            height: 53,
                            url: "https://google-maps-utility-library-v3.googlecode.com/svn/trunk/markerclusterer/images/m1.png",
                            width: 53
                        },
                        {
                            height: 56,
                            url: "https://google-maps-utility-library-v3.googlecode.com/svn/trunk/markerclusterer/images/m2.png",
                            width: 56
                        },
                        {
                            height: 66,
                            url: "https://google-maps-utility-library-v3.googlecode.com/svn/trunk/markerclusterer/images/m3.png",
                            width: 66
                        },
                        {
                            height: 78,
                            url: "https://google-maps-utility-library-v3.googlecode.com/svn/trunk/markerclusterer/images/m4.png",
                            width: 78
                        },
                        {
                            height: 90,
                            url: "https://google-maps-utility-library-v3.googlecode.com/svn/trunk/markerclusterer/images/m5.png",
                            width: 90
                        }
                    ]
                }

                var markerClusterAll = new MarkerClusterer(this.mapall, markers, mcOptions);
                if (this.mapall == null) return;
                var currentCenter = this.mapall.getCenter();
                this.mapall.setCenter(currentCenter);
            }

            $('.pod-all-clubs-map').removeClass('hidden');
            $('.pod-all-clubs-map').addClass('visible');

            google.maps.event.trigger(this.mapall, "resize");
        }.bind(this);

        this.nearbyClubs = ko.computed(function () {
            var firstClub = selectedClub();
            if (!firstClub) {
                return [];
            }

            var clubs = [firstClub];
            clubShortlist().forEach(function (club) {
                if (club.ClubId !== firstClub.ClubId && clubs.length < 3) {
                    clubs.push(club);
                }
            });

            return clubs;
        });

        this.nearbyClubsString = ko.pureComputed(function () {
            var flattenedArray = this.nearbyClubs().map(function (club) {
                return club.Name;
            });
            return flattenedArray.join(", ");
        }, this);

        this.clubCount = ko.pureComputed(function () {
            var allCount = globalData.clubs.length;
            var nearbyCount = this.nearbyClubs().length;
            return allCount - nearbyCount;
        }, this);

        this.refreshData = function () {
            var userMembership = selectedMembership();
            if (!userMembership) {
                this.homeOption(null);
                this.allClubsOption(null);
                return;
            }

            // if new membership is one of the already populated memberships then do nothing
            if (this.homeOption() === userMembership || this.allClubsOption() === userMembership) {
                return;
            }

            var memberships = availableMemberships.getMembershipsForSlot(userMembership.slotName);

            var single = _.find(memberships, function (m) {
                return !m.IsNational;
            });
            this.homeOption(single ? single : null);

            var national = _.find(memberships, function (m) {
                return !!m.IsNational;
            });
            this.allClubsOption(national ? national : null);
        }.bind(this);

        selectedMembership.subscribe(function () {
            this.refreshData();
        }, this);

        availableMemberships.memberships.subscribe(function () {
            this.refreshData();
        }, this);

        // if there is a selected membership on page load, mark club access as selected
        if (selectedMembership()) {
            appHeader.IsAccessSelected(true);
        }
    };
    
})();;
(function () {
    var getClubById = function (clubId) {
        setLoadingIsVisible(false);
        var allClubs = globalData.clubs;
        return _.findWhere(allClubs, { ClubId: clubId.toLowerCase() });
    };

    var getCookieClubs = function () {
        setLoadingIsVisible(false);
        var cookieName = globalData.clubShortlistCookieName;
        var cookieValue = $.cookie(cookieName) && $.cookie(cookieName).toLowerCase();

        if (cookieValue) {
            // clean the cookie value -- not sure if neccessary but leaving it here for now
            cookieValue = cookieValue.replace(/^\s+/g, "");
        }

        if (!cookieValue) {
            return [];
        }
        var cookieClubIds = cookieValue.split(",");
        if (!cookieClubIds) {
            return [];
        }
        return _.map(cookieClubIds, function (clubId) {
            return getClubById(clubId);
        });
    };

    var writeCookieClubIds = function (clubIds) {
        //Clear cookies everytime the page loads.
        document.cookie = "Zap.ClubShortlist=;expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;domain=;";

        var newCookieValue = clubIds.join(",");
        var date = new Date(9999, 0, 1);
        var cookieExpires = ";expires=" + date.toGMTString();
        var cookieDomain = "";
        if (globalData.cookieDomain.length > 0) {
            cookieDomain = ";domain=." + globalData.cookieDomain;
        }
        document.cookie = globalData.clubShortlistCookieName + "=" + newCookieValue + cookieExpires + ";path=/";
    };

    GetStartedPage.SelectClubViewModel = function (selectedClub) {
        var self = this;
        this.selectedClub = selectedClub;
        this.clubs = ko.observableArray();

        // get clubs from cookie or partner
        var cookieClubs = getCookieClubs();
        var partner = appConversation.SelectedPartner();
        if (partner && partner.ExclusiveToClubId) {
            cookieClubs = [getClubById(partner.ExclusiveToClubId)];
        }
        cookieClubs.forEach(function (club) {
            if (!club) {
                return;
            }
            self.clubs.push(club);
        });

        if (this.clubs().length > 0)
            this.firstClubOnLoad = this.clubs().length > 1 ? this.clubs()[1] : this.clubs()[0];
        else
            this.firstClubOnLoad = null;

        this.OpenMoreClubsSelector = function () {
            // Redirect to Sitecore
            //window.location.href = appHeader.GoodlifeSiteUrl + '?path=ecom';
            window.location.href = '/ClubSelector';
            if (!$('#noClubNotification').hasClass('hidden')){
                $('#noClubNotification').addClass('hidden');
            }
        };
        
        if (this.clubs().length < 1) {
            this.OpenMoreClubsSelector();
        };

        self.ShowMembershipOptions = function() {
            // Show membership options
            scrollToElement(".pod-ecom-memberships");
        };

        // When clicked, the club becomes the Home Club 
        self.selectClub = function (club) {
            self.selectedClub(club);

            //update the Workflow step to the next step
            appHeader.WorkflowStep(appHeader.WorkflowStepEnum.SelectMembershipOption);

            // Update the home club variable and place the home club as first in the cookie variable
            var homeClubId = club.ClubId;
            var otherClubs = $.grep(self.clubs(), function (elementOfArray) { return elementOfArray.ClubId === homeClubId; }, true);
            otherClubs = _.map(otherClubs, function(club) {
                return club.ClubId;
            });

            // write the cookie
            var cookieClubs = [homeClubId].concat(otherClubs);
            writeCookieClubIds(cookieClubs);
            
            // Show banner message
            appHeader.SetTopMessage(club.Name + " is a great club!");
        };
        
        self.showMoreClubsButton = ko.pureComputed(function () {
            var partner = appConversation.SelectedPartner();
            if (partner && partner.ExclusiveToClubId) {
                return false;
            }
            return true;
        });

        self.selectedClubId = ko.pureComputed(function() {
            return self.selectedClub() ? self.selectedClub().ClubId : null;
        });

        // on page load, wait 2 seconds and select a club automatically
        // if no club selection already made
        // and user didn't specifically navigate to the 'select your club' section
        $(window).load(function () {
            if (window._initBannerErrorMessage) {
                appHeader.SetError(_initBannerErrorMessage);
            } else {
                setTimeout(function () {
                    if (window.location.hash === '#selectYourClub' || this.clubs().length < 1) {
                        return;
                    }
                    if (window.location.hash === '' && selectedClub()) {
                        appHeader.WorkflowStep(appHeader.WorkflowStepEnum.SelectMembershipOption);
                        self.ShowMembershipOptions();
                        return;
                    }
                    if (!selectedClub()) {
                        this.selectClub(this.firstClubOnLoad);
                    }
                }.bind(this), 2000);
            }
        }.bind(this));

        this.matchHeight = function () {
            $(".pod-ecom-club ul li").matchHeight();
        }
    };
})();;
(function () {
    GetStartedPage.SelectMembershipViewModel = function (selectedMembership, availableMemberships) {
        setLoadingIsVisible(false);
        this.selectedMembership = selectedMembership;
        this.availableMemberships = availableMemberships;

        this.isSelected = function (membership) {
            if (!selectedMembership()) {
                return false;
            }
            // displayed membership or its pair could be selected
            return selectedMembership().Id === membership.Id || selectedMembership().PairedMembershipOptionId === membership.Id;
        }.bind(this);

        availableMemberships.memberships.subscribe(function (newMemberships) {
            var currentMembership = selectedMembership();
            if (!currentMembership || newMemberships.length < 1) {
                return;
            }

            // may be membership with same id but different club pricing... need to update selected membership so that pricing is accurate
            var equivalentMembership = availableMemberships.getById(currentMembership.Id);
            if (equivalentMembership) {
                selectedMembership(equivalentMembership);
            } else {
                selectedMembership(null);
            }
        }, this);

        this.selectMembership = function (displayedMembership) {
            // determine which membership to select based on user preference for national vs home club
            var isNationalMembershipPreferred = this.selectedMembership() ? this.selectedMembership().IsNational : true;
            var membershipPair = [displayedMembership];
            var pairedId = displayedMembership.PairedMembershipOptionId;
            if (pairedId) {
                membershipPair.push(availableMemberships.getById(pairedId));
            }
            var membership = _.find(membershipPair, function (thisMembership) {
                return !!thisMembership.IsNational === isNationalMembershipPreferred;
            });
            if (!membership) {
                membership = _.first(membershipPair);
            }

            // select the membership (notify subscribers)
            this.selectedMembership(membership);

            // Show banner message
            if (membership.BannerAcknowledgement.length > 0) {
                appHeader.SetTopMessage(membership.BannerAcknowledgement);
            } else {
                appHeader.SetTopMessage(membership.Name + ' locked in!');
            }

            // If the there is not a set of paired options ie only all club or single club option
            if (membership.isClubAccessOptionAvailable) {
                // update the workflow step to the next step
                appHeader.IsAccessSelected(false);
                appHeader.WorkflowStep(appHeader.WorkflowStepEnum.SelectClubAccess);
                // do scrolling
                scrollToElement(".pod-ecom-allclubs");
            } else {
                // Skip club access so treat it as selected
                appHeader.IsAccessSelected(true);

                // Write selected membership to Google Analytics.
                dataLayer.push({
                    'event': 'addToCart',
                    'ecommerce': {
                        'currencyCode': 'AUD',
                        'add': {
                            'actionField': { 'list': 'Membership' },
                            'products': [
                                {
                                    'name': membership.Name,
                                    'id': membership.MembershipOptionGuid,
                                    'price': membership.WeeklyPrice,
                                    'brand': appHeader.clubName(),
                                    'category': 'Membership',
                                    'list': 'Membership',
                                    'variant': membership.Term,
                                    'quantity': 1
                                }
                            ]
                        }
                    }
                });

                // if there is no valueOption options 
                if (membership.isAnyOptionsAvailable) {
                    // Scrolls automatically to the options. Update the Workflow step to the next step
                    appHeader.WorkflowStep(appHeader.WorkflowStepEnum.SelectValueOptions);
                    scrollToElement(".pod-ecom-options");
                } else {
                    // Update the Workflow step to the next step
                    appHeader.WorkflowStep(appHeader.WorkflowStepEnum.MemberDetails);

                    // No value options so submit the page and redirect to member details
                    appConversation.saveAndContinueToLogin(); //todo remove global reference to appConversation
                }
            }

            // match the height
            if (Modernizr.mq("only screen and (min-width: 768px)")) {
                $(".pod-top-ecom .top-module:not(.hidden)").matchHeight();
            }
        }.bind(this);

        this.displayMemberships = ko.pureComputed(function () {
            if (!this.availableMemberships.isAny()) {
                return [];
            }

            // eCommerce tag - impressions and product detail hits.
            var memberships = [];

            _.each(this.availableMemberships.nationalMemberships(), function (membership) {
                memberships.push({
                    'name': membership.Name,
                    'id': membership.MembershipOptionGuid,
                    'price': membership.DisplayPrice,
                    'brand': appHeader.clubName(),
                    'category': 'Membership',
                    'list': 'Membership',
                    'variant': membership.Term
                });
            });

            dataLayer.push({
                'event': 'membershipsLoaded',
                'ecommerce': {
                    'currencyCode': 'AUD',
                    'impressions': memberships,
                    'detail': {
                        'actionField': { 'list': 'Membership' },
                        'products': memberships
                    }
                },
                'eventCallback': function() {
                    if (appConversation.SelectedMembership() !== null) {
                        if (appConversation.SelectedMembership().Options.length > 0) {
                            // Fire options impressions tag once more.
                            var options = [];

                            _.each(appConversation.SelectedMembership().Options,
                                function(option) {
                                    options.push({
                                        'name': option.Name,
                                        'id': option.Name,
                                        'price': option.Price,
                                        'brand': appHeader.clubName(),
                                        'category': 'Option',
                                        'list': 'Option',
                                        'variant': 'Option'
                                    });

                                });

                            dataLayer.push({
                                'event': 'optionsLoaded',
                                'ecommerce': {
                                    'currencyCode': 'AUD',
                                    'impressions': options,
                                    'detail': {
                                        'actionField': { 'list': 'Option' },
                                        'products': options
                                    }
                                }
                            });
                        }
                    }
                }
            });

            return this.availableMemberships.nationalMemberships();
        }, this);

        // Unfold the details in mobile view-port
        self.unfoldDetails = function (item, event) {
            var parentDOM = event.target.offsetParent;
            $(parentDOM).find(".unfold").addClass("unfolded");
            $(parentDOM).find(".folded").addClass("unfolded");
        };

        // Match the height of the membership boxes
        this.matchHeight = function () {
            $(".pod-ecom-memberships .submod").matchHeight();
        }
    };
})();;
(function () {
    GetStartedPage.SelectValueOptionsViewModel = function (selectedMembership, selectedOptions) {
        setLoadingIsVisible(false);
        appHeader.WorkflowStep(appHeader.WorkflowStepEnum.SelectValueOptions);

        this.availableOptions = ko.computed(function () {
            var membership = selectedMembership();
            if (!membership) {
                return [];
            }

            return membership.Options;
        }, this);

        this.currentIndex = ko.observable(0);

        this.findInAvailableList = function (option) {
            return _.find(this.availableOptions(), function (availableOption) {
                return availableOption.Id === option.Id;
            });
        }.bind(this);

        this.getIndexInAvailableList = function (option) {
            var found = this.findInAvailableList(option);
            return this.availableOptions().indexOf(found);
        }.bind(this);

        this.findInSelectedList = function (option) {
            return _.find(selectedOptions(), function (selectedOption) {
                return selectedOption.Id === option.Id;
            });
        }

        this.isSelected = function (option) {
            return this.findInSelectedList(option);
        }.bind(this);

        this.isRejected = function (option) {
            if (this.isSelected(option)) return false;
            var optionIndex = this.getIndexInAvailableList(option);
            return optionIndex < this.currentIndex();
        }.bind(this);

        this.canContinue = ko.pureComputed(function () {
            return this.currentIndex() >= this.availableOptions().length;
        }, this);

        this.isVisible = ko.pureComputed(function () {
            return this.availableOptions().length > 0 && appHeader.IsAccessSelected();
        }, this);

        this.onSelectionAction = function (option) {
            // update current index if neccessary to reveal next option
            if (this.getIndexInAvailableList(option) === this.currentIndex()) {
                this.currentIndex(this.currentIndex() + 1);
            }

            // scroll to the next option, if it exists
            if (Modernizr.mq('only all and (max-width: 767px)')) {
                var index = this.getIndexInAvailableList(option) + 1;
                var element = $("#valueOption" + index);
                if (element.length > 0) {
                    $('html, body').animate({
                        scrollTop: element.offset().top - appHeader.height
                    }, 500);
                } else {
                    $('html, body').animate({
                        scrollTop: $(document).height()
                    }, 500);
                }
            }
        }.bind(this);

        this.selectOption = function (option) {
            if (this.getIndexInAvailableList(option) > this.currentIndex()) {
                return;
            }

            var isSelected = this.findInSelectedList(option);
            if (!isSelected) {
                selectedOptions.push(option);
            }
            appHeader.SetTopMessage(option.SelectAcknowledgement);
            this.onSelectionAction(option);
        }.bind(this);

        this.rejectOption = function (option) {
            if (this.getIndexInAvailableList(option) > this.currentIndex()) {
                return;
            }

            selectedOptions.remove(function (selectedOption) {
                return selectedOption.Id === option.Id;
            });
            appHeader.SetTopMessage(option.UnselectAcknowledgement);
            this.onSelectionAction(option);
        }.bind(this);

        this.continueClick = function () {
            if (!this.canContinue()) {
                return;
            }

            appConversation.saveAndContinueToLogin(); //todo remove global reference
        }.bind(this);

        this.previouslyAvailableOptions = [];
        this.onAvailableOptionsChange = function (availableOptions, isPageInitialising) {
            // if all the options are the same, do nothing
            if (_.isEqual(availableOptions, this.previouslyAvailableOptions)) {
                return;
            }
            
            // if page initialising keep the selected options (if applicable)
            // otherwise reset option selections
            if (isPageInitialising) {
                // remove any options that are no longer available
                selectedOptions.remove(function (selectedOption) {
                    var isStillAvailable = this.findInAvailableList(selectedOption);
                    return !isStillAvailable;
                }.bind(this));

                // update index so that continue button is enabled
                this.currentIndex(availableOptions.length);
            } else {
                selectedOptions.removeAll();
                this.currentIndex(0);
            }

            // create a copy of available options to compare with when the available options change
            this.previouslyAvailableOptions = JSON.parse(JSON.stringify(availableOptions));
        }.bind(this);

        this.availableOptions.subscribe(function (availableOptions) {
            this.onAvailableOptionsChange(availableOptions);
        }, this);

        // run initially as subscribe doesn't trigger on page load
        this.onAvailableOptionsChange(this.availableOptions(), true);

        this.matchHeight = function () {
            $(".pod-membership-options-container .outer-sub").matchHeight();
        }
    };
})();;
(function () {
    LoginPage.LoginPageViewModel = function (loginManager) {
        setLoadingIsVisible(false);
        this.loginManager = loginManager;

        this.logoutClick = function () {
            loginManager.logout();
            loginManager.showLoginScreen();
        }.bind(this);

        this.continueClick = function () {
            window.location.href = "/details";
        };

        appHeader.WorkflowStep(appHeader.WorkflowStepEnum.Login);
        appHeader.IsAccessSelected(true);

        // Write member details checkout step to Google eCommerce.
        // All cart products are required. 
        var cartProducts = [];
        var membership = appConversation.SelectedMembership();

        cartProducts.push({
            'name': membership.Name,
            'id': membership.MembershipOptionGuid,
            'price': membership.WeeklyPrice,
            'brand': appHeader.clubName(),
            'category': 'Membership',
            'list': 'Membership',
            'variant': membership.Term,
            'quantity': 1
        });

        _.each(appConversation.SelectedOptions(), function (option) {
            cartProducts.push({
                'name': option.Name,
                'id': option.Name,
                'price': option.Price,
                'brand': appHeader.clubName(),
                'category': 'Option',
                'variant': 'Option',
                'list': 'Option',
                'quantity': 1
            });
        });

        dataLayer.push({
            'event': 'checkoutOption',
            'ecommerce': {
                'checkout': {
                    'actionField': { 'step': 1 },
                    'products': cartProducts
                }
            }
        });

        loginManager.showLoginScreen();
    };
})();;

(function () {
    MemberDetailsPage.CropImageModal = function () {
        setLoadingIsVisible(false);
        this.member = ko.observable(null);

        var canvas = document.getElementById('crop-image-modal-canvas');
        var context = canvas.getContext('2d');
        var jcrop_api;

        var $containerElement = $("#crop-image-modal");
        var $imageElement = $('#crop-image-modal-image');
        var imageElement = $imageElement[0];

        var isOpen = false;

        var destroyJcrop = function() {
            if (jcrop_api) {
                jcrop_api.destroy();
            };
        }

        var applyJcrop = function(member) {
            // determine width and height of image based on container and image size
            var fixedImage = member.fixedImage();
            var rawImage = member.rawImage();
            var rawImageWidth = fixedImage ? fixedImage.width : rawImage.width;
            var rawImageHeight = fixedImage ? fixedImage.height : rawImage.height;
            
            var containerWidth = $containerElement.width();
            var containerHeight = $containerElement.height();
            var containerRatio = containerWidth / containerHeight;
            var imageRatio = rawImageWidth / rawImageHeight;

            var targetWidth, targetHeight;
            if (imageRatio > containerRatio) {
                targetWidth = containerWidth;
                targetHeight = containerWidth / imageRatio;
            } else {
                targetWidth = containerHeight * imageRatio;
                targetHeight = containerHeight;
            }

            if (targetWidth > rawImageWidth * 2) {
                targetWidth = rawImageWidth * 2;
                targetHeight = rawImageHeight * 2;
            }

            $imageElement.css('width', targetWidth).css('height', targetHeight);

            // initialise jcrop
            var onSelectionFinished = function (selection) {
                member.cropSelection = selection;
            };

            var s = member.cropSelection;
            var cropSelection = [s.x, s.y, s.x2, s.y2];

            $imageElement.Jcrop({
                onSelect: onSelectionFinished,
                aspectRatio: 4.0 / 3.0,
                trueSize: [rawImageWidth, rawImageHeight],
                setSelect: cropSelection
            }, function () {
                jcrop_api = this;
            });
        }.bind(this);

        this.open = function (member) {
            isOpen = true;

            // destroy existing jcrop instance if exists
            destroyJcrop();

            // set current member
            this.member(member);

            // load the image for the member + apply Jcrop
            imageElement.onload = function () {
                imageElement.onload = null;

                // show modal
                $('#crop-image-modal').addClass('visible');
                $('#crop-image-modal').show(); //just toggling the visible class does not fully work in ie10 - this is a quick fix
                $('body').toggleClass('no-scroll');
                
                applyJcrop(member);
            };
            imageElement.src = member.fixedImage() ? member.fixedImage().dataUrl : member.rawImage().src;
        }.bind(this);

        this.saveAndClose = function() {
            this.close();

            // save the cropped image to member model
            var member = this.member();
            var sel = member.cropSelection;
            var rawImage = member.fixedImage() ? member.fixedImage().canvas : member.rawImage();
            var croppedImage = this.getProcessedImage(rawImage, sel.x, sel.y, sel.w, sel.h);
            member.MemberImageBase64(croppedImage);
        }.bind(this);

        this.close = function() {
            destroyJcrop();

            $('body').toggleClass('no-scroll');

            // hide modal
            $('#crop-image-modal').hide(); //just toggling the visible class does not fully work in ie10 - this is a quick fix
            $('#crop-image-modal').removeClass('visible');
            imageElement.src = "#";
            isOpen = false;
        }.bind(this);

        this.getProcessedImage = function (originalImage, x, y, width, height) {
            canvas.width = 640;
            canvas.height = 480;
            context.drawImage(originalImage, x, y, width, height, 0, 0, 640, 480);
            return canvas.toDataURL("image/jpeg", 0.8);
        }

        // handle viewport changes on resize
        var onFinishedResizing = function() {
            if (!isOpen) {
                return;
            }

            applyJcrop(this.member());
        }.bind(this);
        var resizeTimeout;
        window.onresize = function () {
            if (!isOpen) {
                return;
            }

            destroyJcrop();

            clearTimeout(resizeTimeout);
            resizeTimeout = setTimeout(onFinishedResizing, 50);
        };
    }
})();;
(function () {
    var URL = window.URL || window.webkitURL;

    /**
        * Detecting vertical squash in loaded image.
        * Fixes a bug which squash image vertically while drawing into canvas for some images.
        * This is a bug in iOS6 devices. This function from https://github.com/stomita/ios-imagefile-megapixel
        * 
        */
    var detectVerticalSquashCanvas; //re-use same canvas for detect vertical squash
    function detectVerticalSquash(img) {
        var ih = img.naturalHeight;
        detectVerticalSquashCanvas = detectVerticalSquashCanvas || document.createElement('canvas');
        var canvas = detectVerticalSquashCanvas;
        canvas.width = 1;
        canvas.height = ih;
        var ctx = canvas.getContext('2d');
        ctx.drawImage(img, 0, 0);
        try {
            // Prevent cross origin error
            var data = ctx.getImageData(0, 0, 1, ih).data;
        } catch (err) {
            // hopeless, assume the image is well and good.
            //console.log("Cannot check verticalSquash: CORS?");
            return 1;
        }
        // search image edge pixel position in case it is squashed vertically.
        var sy = 0;
        var ey = ih;
        var py = ih;
        while (py > sy) {
            var alpha = data[(py - 1) * 4 + 3];
            if (alpha === 0) {
                ey = py;
            } else {
                sy = py;
            }
            py = (ey + sy) >> 1;
        }
        var ratio = (py / ih);
        return (ratio === 0) ? 1 : ratio;
    }

    function formatDateFromServer(dateString) {
        //console.log("formatDateFromServer()");

        //var utcDate = moment.utc(dateString);
        //console.log("utcDate: " + utcDate.format());

        var estDate = moment(dateString);
        //console.log("estDate: " + estDate.format());

        return estDate.format('DD/MM/YYYY');
    }

    function formatDateForServer(dateString) {
        //console.log("formatDateForServer()");

        var utcDate = moment.utc(dateString, "DD/MM/YYYY");
        //console.log("utcDate: " + utcDate.format());
        //console.log("estDate: " + utcDate.utcOffset(10).format());

        return utcDate.format();
    }

    MemberDetailsPage.MemberModel = function (json) {
        //begin json properties
        this.MemberDetailsId = ko.observable(json ? json.MemberDetailsId : null);

        var validation = appValidationConfig.member;

        this.Gender = ko.observable(json && json.Gender ? json.Gender : "f");
        this.FirstName = ko.observable(json ? json.FirstName : null).extend({ required: { message: validation.firstName.empty }, validateName: validation.firstName.invalid });
        this.LastName = ko.observable(json ? json.LastName : null).extend({ required: { message: validation.lastName.empty }, validateName: validation.lastName.invalid });
        this.Birthdate = ko.observable(json ? formatDateFromServer(json.Birthdate) : null).extend({ validateDOB: true });

        this.State = ko.observable(json && json.State ? json.State : undefined).extend({ required: { message: validation.state } });
        this.Suburb = ko.observable(json ? json.Suburb : null).extend({ required: { message: validation.suburb } });
        this.Street1 = ko.observable(json ? json.Street1 : null).extend({ required: { message: validation.street } });
        this.Street2 = ko.observable(json ? json.Street2 : null);
        this.Postcode = ko.observable(json ? json.Postcode : null).extend({ validatePostcode: true });

        this.MemberImageBase64 = ko.observable(json ? json.MemberImageBase64 : null);
        this.imageFileBlob = ko.observable(null);
        this.rawImage = ko.observable(null);
        this.cropSelection = null;
        this.fixedImage = ko.observable(null);

        this.Phone = ko.observable(json ? json.Phone : null).extend({ validatePhoneNumber: validation.phone });
        this.Mobile = ko.observable(json ? json.Mobile : null).extend({ validatePhoneNumber: validation.mobile });
        this.Email = ko.observable(json ? json.Email : null).extend({ required: { message: validation.email }, email: { message: validation.email } });
        this.ContactPreference = ko.observable(json ? json.ContactPreference : null);
        this.ReceiveNewsletters = ko.observable(json ? json.ReceiveNewsletters : null);

        this.EmergencyContactName = ko.observable(json ? json.EmergencyContactName : null).extend({ required: { message: validation.contactName.empty }, validateName: validation.contactName.invalid });
        this.EmergencyContactNumber = ko.observable(json ? json.EmergencyContactNumber : null).extend({ required: { message: validation.contactNumber }, validatePhoneNumber: validation.contactNumber });

        this.ShowPartnerValidation = false;
        this.PartnerValidationCode = ko.observable(json ? json.PartnerValidationCode : null);

        // activate partner validation, if applicable
        var partner = appConversation.SelectedPartner();
        if (partner && partner.ValidationText) {
            this.ShowPartnerValidation = true;
            this.PartnerValidationLabel = partner.ValidationText;
            this.PartnerHeading = partner.Name + " DETAILS";
            this.PartnerValidationPlaceholder = partner.ValidationPlaceholderText;
            var validationMessage = "Please enter a valid " + partner.ValidationPlaceholderText;
            this.PartnerValidationCode.extend({
                required: { message: validationMessage },
                validatePartnerCode: { params: { regex: partner.ValidationRegex, message: validationMessage } }
            });
        }

        //end json properties

        this.mobileAndPhoneErrorMessage = ko.observable("");

        this.imagePreviewSrc = ko.computed(function () {
            var memberImageBase64 = this.MemberImageBase64();
            if (memberImageBase64) return memberImageBase64;
            return null;
        }, this);

        this.removeImage = function () {
            if (this.rawImage()) {
                URL.revokeObjectURL(this.rawImage().src);
            }
            this.MemberImageBase64(null);
            this.rawImage(null);
            this.fixedImage(null);
            this.cropSelection = null;
            this.imageFileBlob(null);
        }.bind(this);

        var populateObservableFromProfile = function (observable, profileProperty) {
            if (!observable() && profileProperty) {
                observable(profileProperty);
            }
        }

        var autoPopulateFields = ko.computed(function () {
            return;
            //var loginManager = appLoginManager;
            //var accountInfo = loginManager.accountInfo();
            //if (!accountInfo) {
            //    return;
            //}
            //var profile = accountInfo.profile;

            //populateObservableFromProfile(this.FirstName, profile.firstName);
            //populateObservableFromProfile(this.LastName, profile.lastName);
            
            //var gender = profile.gender;
            //if (gender && (gender === 'm' || gender === 'f')) {
            //    populateObservableFromProfile(this.Gender, gender);
            //}
            //populateObservableFromProfile(this.Email, profile.email);

            //// todo: moment seems to work with month values starting at 0 - investigate if this is how gigya works too
            //var birthDay = profile.birthDay, birthYear = profile.birthYear, birthMonth = profile.birthMonth;
            //if (birthDay && birthMonth && birthYear) {
            //    var birthdate = moment({y: birthYear, M: birthMonth, d: birthDay});
            //    console.log(birthdate);
            //    if (birthdate.isValid()) {
            //        var formattedBirthdate = birthdate.format('DD/MM/YYYY');
            //        console.log(formattedBirthdate);
            //        populateObservableFromProfile(this.Birthdate, formattedBirthdate);
            //    }
            //}
        }, this);

        var getDefaultSelection = function (imgWidth, imgHeight) {
            // center selection with correct aspect ratio
            var selRatio = 4.0 / 3.0;
            var imageRatio = imgWidth / imgHeight;
            var selWidth, selHeight, selX, selY;

            if (imageRatio > selRatio) {
                selWidth = imgHeight * selRatio;
                selHeight = imgHeight;
                selX = (imgWidth - selWidth) / 2;
                selY = 0;
            } else {
                selWidth = imgWidth;
                selHeight = imgWidth / selRatio;
                selX = 0;
                selY = (imgHeight - selHeight) / 2;
            }

            return {
                x: selX,
                y: selY,
                x2: selX + selWidth,
                y2: selY + selHeight,
                width: selWidth,
                height: selHeight
            };
        };

        var loadImageIOSmethod = function (blob, orientation) {
            canvasResize(blob, {
                width: 1920,
                height: 1920,
                crop: false,
                quality: 80,
                exifOrientation: orientation,
                callback: function (dataUrl, width, height, canvas) {
                    if (this.rawImage()) {
                        URL.revokeObjectURL(this.rawImage().src);
                    }
                    this.rawImage(null);
                    this.fixedImage({
                        width: width,
                        height: height,
                        dataUrl: dataUrl,
                        canvas: canvas
                    });

                    var sel = this.cropSelection = getDefaultSelection(width, height);

                    var processedImage = _cropImageModal.getProcessedImage(canvas, sel.x, sel.y, sel.width, sel.height);
                    this.MemberImageBase64(processedImage);
                    setLoadingIsVisible(false);
                }.bind(this)
            });
        }.bind(this);

        var loadImageRegularMethod = function (blob, orientation) {
            var imageUrl = URL.createObjectURL(blob);
            var img = new Image();
            img.onload = function () {
                var vertSquash = detectVerticalSquash(img);
                if (vertSquash !== 1) {
                    URL.revokeObjectURL(imageUrl);
                    loadImageIOSmethod(blob, orientation);
                    return;
                }

                this.fixedImage(null);
                if (this.rawImage()) {
                    URL.revokeObjectURL(this.rawImage().src);
                }
                this.rawImage(img);

                var sel = this.cropSelection = getDefaultSelection(img.width, img.height);
                var processedImage = _cropImageModal.getProcessedImage(img, sel.x, sel.y, sel.width, sel.height);
                this.MemberImageBase64(processedImage);
                setLoadingIsVisible(false);
            }.bind(this);
            img.onerror = function () {
                appHeader.SetError('Failed to load the image');
                this.imageFileBlob(null);
                setLoadingIsVisible(false);
            }.bind(this);
            img.src = imageUrl;
        }.bind(this);

        this.imageFileBlob.subscribe(function (blob) {
            if (!blob) {
                return;
            }

            appHeader.ClearError();

            // size validation
            if (blob.size > 6000000) { // more than 6 megabytes
                appHeader.SetError('Selected file is too large. Maximum of 6MB is allowed');
                this.imageFileBlob(null);
                return;
            }

            // mime type validation
            if (!(blob.type.substring(0, 10) === "image/jpeg" ||
                blob.type.substring(0, 9) === "image/png" ||
                blob.type.substring(0, 9) === "image/gif" ||
                blob.type.substring(0, 11) === 'image/pjpeg')) {

                appHeader.SetError('File must be a valid image format i.e. JPG, PNG or GIF');
                this.imageFileBlob(null);
                return;
            }

            setLoadingIsVisible(true);
            EXIF.getData(blob, function () {
                var orientation = 1;
                var info = EXIF.pretty(this);
                if (info.length > 0) {
                    var orientationTitleString = "Orientation : ";
                    var orientationTitleIndex = info.indexOf(orientationTitleString);
                    if (orientationTitleIndex > -1) {
                        try {
                            orientation = parseInt(info[orientationTitleIndex + orientationTitleString.length], 10);
                            if (isNaN(orientation) || orientation < 1) { //orientation < 1 is an ie10 fix
                                orientation = 1;
                            }
                        } catch (ex) {
                            orientation = 1;
                        }
                    }
                }

                if (orientation !== 1) {
                    loadImageIOSmethod(blob, orientation);
                    return;
                }

                loadImageRegularMethod(blob, orientation);
            });
        }, this);

        this.ToggleGender = function () {
            if (this.Gender() === "f") {
                this.Gender("m");
            } else {
                this.Gender("f");
            }
        }.bind(this);

        //todo ===refactor===
        this.IsMinor = ko.pureComputed(function () {
            var dateValue = this.Birthdate();
            if (new RegExp(/\b\d{1,2}[/]\d{1,2}[/]\d{4}\b/).test(dateValue)) {
                var today = new Date();
                var birthDate = moment(dateValue, "DD-MM-YYYY").toDate();
                var isPastDate = (birthDate < today);

                var maxBirthdate = new Date(today.getFullYear() - 18, today.getMonth(), today.getDate());

                return isPastDate && birthDate > maxBirthdate;
            }

            return false;
        }, this);

        this.MobileEnabled = ko.computed(function () {
            return this.Mobile() && this.Mobile.isValid();
        }, this);

        this.PhoneEnabled = ko.computed(function () {
            return this.Phone() && this.Phone.isValid();
        }, this);

        this.showNoNumberErrorMessage = function (value) {
            if (value) {
                this.mobileAndPhoneErrorMessage(appValidationConfig.member.phoneAndMobileMissing);
            } else {
                this.mobileAndPhoneErrorMessage("");
            }
        }.bind(this);

        this.isAtLeastOneNumberPopulated = ko.computed(function () {
            var mobile = this.Mobile();
            var phone = this.Phone();
            var numberModified = this.Mobile.isModified() || this.Phone.isModified();
            if (numberModified && !mobile && !phone) {
                this.showNoNumberErrorMessage(true);
                return false;
            }
            this.showNoNumberErrorMessage(false);
            return true;
        }, this);

        this.adjustContactPreferenceOnChange = ko.computed(function () {
            var mobileEnabled = this.MobileEnabled();
            var phoneEnabled = this.PhoneEnabled();
            var preference = this.ContactPreference();

            if ((preference == "mobile" && !mobileEnabled) ||
                (preference == "phone" && !phoneEnabled)) {
                preference = "";
            }

            if (!preference) {
                if (mobileEnabled) preference = "mobile";
                if (phoneEnabled) preference = "phone";
            }

            this.ContactPreference(preference);
        }, this);

        this.completeFormForTest = function () {
            this.FirstName('testy');
            this.LastName('tester');
            this.Birthdate('02/07/1976');
            this.Email('changemeifyoutestemail@test.com');
            this.ReceiveNewsletters(false);
            this.EmergencyContactName('blah');
            this.EmergencyContactNumber(01234567890);
            this.Street1('blah st');
            this.Suburb('blahtown');
            this.Postcode(4209);
            this.State('qld');
            this.Mobile(01234567980);
        }.bind(this);

        this.GetJson = function () {

            var partnerCode = null;
            if (this.PartnerValidationCode() != null) {
                partnerCode = this.PartnerValidationCode().trim();
            }

            var street2 = null;
            if (this.Street2() != null) {
                street2 = this.Street2().trim();
            }

            var member = {
                FirstName: this.FirstName().trim(),
                LastName: this.LastName().trim(),
                Gender: this.Gender(),
                Birthdate: formatDateForServer(this.Birthdate()),
                Mobile: this.Mobile(),
                Phone: this.Phone(),
                ContactPreference: this.ContactPreference(),
                Email: this.Email().trim(),
                ReceiveNewsletters: this.ReceiveNewsletters(),
                EmergencyContactName: this.EmergencyContactName().trim(),
                EmergencyContactNumber: this.EmergencyContactNumber(),
                Street1: this.Street1().trim(),
                Street2: street2,
                Suburb: this.Suburb().trim(),
                Postcode: this.Postcode(),
                State: this.State(),
                MemberImageBase64: this.MemberImageBase64(),
                MemberDetailsId: this.MemberDetailsId(),
                PartnerValidationCode: partnerCode
            };
            return member;
        }.bind(this);
    }
})();;
(function () {
    MemberDetailsPage.MemberDetailsPageViewModel = function (conversation) {
        setLoadingIsVisible(false);
        if (window._initBannerErrorMessage) {
            appHeader.SetError(_initBannerErrorMessage);
        }

        this.enableValidationMessage = ko.observable(false);

        appHeader.WorkflowStep(appHeader.WorkflowStepEnum.MemberDetails);
        appHeader.IsAccessSelected(true);

        var membersList = conversation.MemberDetailsRawJson;

        // There should be only one
        if (membersList && membersList.length > 1) {
            membersList = membersList.slice(0, 1);
        }

        this.Members = ko.observableArray(ko.utils.arrayMap(membersList, function (json) {
            return new MemberDetailsPage.MemberModel(json);
        }));

        if (this.Members().length < 1) {
            this.Members.push(new MemberDetailsPage.MemberModel());
        }

        // Write member details checkout step to Google eCommerce.
        // All cart products are required. 
        var cartProducts = [];
        var membership = conversation.SelectedMembership();

        cartProducts.push({
            'name': membership.Name,
            'id': membership.MembershipOptionGuid,
            'price': membership.WeeklyPrice,
            'brand': appHeader.clubName(),
            'category': 'Membership',
            'list': 'Membership',
            'variant': membership.Term,
            'quantity': 1
        });

        _.each(conversation.SelectedOptions(), function (option) {
            cartProducts.push({
                'name': option.Name,
                'id': option.Name,
                'price': option.Price,
                'brand': appHeader.clubName(),
                'category': 'Option',
                'variant': 'Option',
                'list': 'Option',
                'quantity': 1
            });
        });

        dataLayer.push({
            'event': 'checkoutOption',
            'ecommerce': {
                'checkout': {
                    'actionField': { 'step': 2 },
                    'products': cartProducts
                }
            }
        });

        var validationGroup = ko.validation.group(this, { deep: true });

        this.doValidation = function () {
            var isValid = true;

            validationGroup.showAllMessages(true);
            var validationErrors = validationGroup();
            if (validationErrors.length > 0) {
                isValid = false;
            }

            this.Members().forEach(function (member) {
                if (!member.isAtLeastOneNumberPopulated()) {
                    member.showNoNumberErrorMessage(true);
                    isValid = false;
                }
            });

            return isValid;
        }.bind(this);

        this.validationMessage = ko.computed(function() {
            if (!this.enableValidationMessage()) {
                return null;
            }

            if (this.doValidation()) {
                this.enableValidationMessage(false);
                return null;
            }

            return appValidationConfig.member.continueButtonError;
        }, this);

        this.Submit = function () {
            this.enableValidationMessage(true);

            if (this.validationMessage()) {
                return;
            }

            var members = ko.utils.arrayMap(this.Members(), function (member) {
                return member.GetJson();
            });

            // Add marketing flag to analytics, and then proceed.
            var receiveMarketing;
            if (members[0].ReceiveNewsletters) {
                receiveMarketing = "Marketing Opt-In";
            } else {
                receiveMarketing = "Marketing Opt-Out";
            }

            dataLayer.push({
                'event': 'checkoutOption',
                'ecommerce': {
                    'checkout_option': {
                        'actionField': { 'step': 2, 'option': receiveMarketing }
                    }
                }
            });

            CallAjax(
                "/Details/SaveMembers",
                "POST",
                JSON.stringify(members),
                function() {
                    window.location.href = "/Payment";
                });

        }.bind(this);
    };
    
})();;
(function () {
    //todo remove duplication of this function between models
    var printableASCIIValidator = function (val) {
        if (val == null) return true;
        for (var i = 0; i < val.length; i++) {
            if (val.charCodeAt(i) < 32 || val.charCodeAt(i) > 126) return false;
        }
        return true;
    };

    PaymentPage.AccountDebitModel = function () {
        var self = this;
        // Model Definition
        self.FinancialInstitution = ko.observable().extend({
            required: { message: 'Please enter the Financial Institution name' },
            validation: {
                validator: printableASCIIValidator,
                message: "Financial institution name has invalid characters"
            }
        });
        self.AccountName = ko.observable().extend({
            required: { message: 'Please enter the Account name' },
            validation: {
                validator: printableASCIIValidator,
                message: "Account name has invalid characters"
            }
        });
        self.BSB = ko.observable().extend({
            required: { message: 'Please enter a BSB (6 digits)' },
            number: { message: 'BSB must be 6 digits' },
            minLength: { params: 6, message: 'BSB must be 6 digits' },
            maxLength: { params: 6, message: 'BSB must be 6 digits' }
        });
        self.AccountNumber = ko.observable().extend({
            required: { message: 'Please enter the Account number' },
            number: { message: 'Account number must be only digits' },
            minLength: { params: 6, message: 'Account number must be 6 to 9 digits' },
            maxLength: { params: 9, message: 'Account number must be 6 to 9 digits' }
        });
        // Setter
        self.SetData = function (data) {
            if (data) {
                self.FinancialInstitution(data.FinancialInstitution);
                self.AccountName(data.AccountName);
                self.BSB(data.BSB);
                self.AccountNumber(data.AccountNumber);
            }
        };

        self.errors = ko.validation.group(self);
    };
})();;
(function() {
    PaymentPage.AddressModel = function () {
        setLoadingIsVisible(false);
        var self = this;

        // Model Definition
        self.Street1 = ko.observable(null).extend({
            required: { params: true, message: 'Please enter a street address' }
        });
        self.Street2 = ko.observable(null).extend({
        });
        self.Suburb = ko.observable(null).extend({
            required: { params: true, message: 'Please enter a suburb' },
        });
        self.PostCode = ko.observable(null).extend({
            required: { params: true, message: 'Please enter a postcode' },
            minLength: { params: 4, message: 'Please enter a valid postcode' },
            maxLength: { params: 4, message: 'Please enter a valid postcode' },
            min: { params: 1, message: 'Please enter a valid postcode' }
        });;
        self.State = ko.observable(undefined).extend({
            required: { params: true, message: 'Please select a state' }
        });

        // Additional lines for server side class mappings.
        self.Line1 = ko.observable();
        self.Line2= ko.observable();

        // Setter
        self.SetData = function (data) {
            if (data) {
                self.Street1(data.Street1);
                self.Street2(data.Street2);
                self.Suburb(data.Suburb);
                self.PostCode(data.PostCode);
                self.State(data.State);
                self.Line1(data.Street1);
                self.Line2(data.Street2);
            }
        };

        self.errors = ko.validation.group(self);
    };
})();;
(function () {
    //todo remove duplication of this function between models
    var printableASCIIValidator = function (val) {
        setLoadingIsVisible(false);
        //console.log(val);
        if (val == null) return true;
        for (var i = 0; i < val.length; i++) {
            if (val.charCodeAt(i) < 32 || val.charCodeAt(i) > 126) return false;
        }
        return true;
    };

    PaymentPage.CreditCardModel = function () {
        var self = this;

        self.BypassLuhnCheck = ko.observable(false);

        // Model Definition
        self.CardNumber = ko.observable().extend({
            required: { message: 'Please enter the credit card number' },
            number: { message: 'Card number is invalid' },
            validateCreditCard: { params: self.BypassLuhnCheck, message: 'Card number is invalid' },
        });
        self.NameOnCard = ko.observable().extend({
            required: { message: 'Please enter the name on the credit card' },
            validation: {
                validator: printableASCIIValidator,
                message: "Please enter the name on the credit card"
            }
        });
        self.ExpiryMonth = ko.observable().extend({
            required: { params: true, message: 'Expiry month invalid (mm)' },
            minLength: { params: 2, message: 'Expiry month invalid (mm)' },
            maxLength: { params: 2, message: 'Expiry month invalid (mm)' },
            min: { params: 1, message: 'Expiry month invalid (mm)' },
            max: { params: 12, message: 'Expiry month invalid (mm)' },
            validation: {
                validator: function (val) {
                    if (self.ExpiryYear() === new Date().getFullYear().toString().substr(2, 2)) {
                        return val >= new Date().getMonth() + 1;
                    }
                    return true;
                },
                message: "Expiry month invalid (mm)"
            }
        });
        self.ExpiryYear = ko.observable().extend({
            required: { params: true, message: 'Expiry year invalid (yy)' },
            minLength: { params: 2, message: 'Expiry year invalid (yy)' },
            maxLength: { params: 2, message: 'Expiry year invalid (yy)' },
            min: { params: Number(new Date().getFullYear().toString().substr(2, 2)), message: 'Expiry year invalid (yy)' },
            max: { params: Number((new Date().getFullYear() + 20).toString().substr(2, 2)), message: 'Expiry year invalid (yy)' }
        });
        self.CVV = ko.observable().extend({
            required: { message: 'Please enter the CVV' },
            number: { message: 'CVV must be 3 or 4 digits' },
            minLength: { params: 3, message: 'CVV must be 3 or 4 digits' },
            maxLength: { params: 4, message: 'CVV must be 3 or 4 digits' },
            min: { params: 0, message: 'CVV must be 3 or 4 digits' },
            max: { params: 9999, message: 'CVV must be 3 or 4 digits' }
        });
        // Setter
        self.SetData = function (data) {
            if (data) {
                self.CardNumber(data.CardNumber);
                self.NameOnCard(data.NameOnCard);
                self.ExpiryMonth(data.ExpiryMonth);
                self.ExpiryYear(data.ExpiryYear);
                self.CVV(data.CVV);
            }
        };

        self.errors = ko.validation.group(self);
    };
})();;
(function() {
    PaymentPage.PaymentItemsModel = function () {
        setLoadingIsVisible(false);
        var self = this;
        // Model Definition
        self.Description = '';
        self.Value = 0.00;
        self.Discount = 0.00;
        self.DiscountedValue = 0.00;
        self.DiscountMessage = '';
        self.Frequency = 0;
        self.FrequencyDescription = '';
        // Setter
        self.SetData = function (data) {
            if (data) {
                self.Description = data.Description;
                self.Value = data.Value.toFixed(2);
                self.Discount = data.Discount.toFixed(2);
                self.DiscountedValue = data.DiscountedValue.toFixed(2);
                self.DiscountMessage = data.DiscountMessage;
                self.Frequency = data.Frequency;
                if ($(window).width() < 350 || data.Description.length > 16) {
                    self.FrequencyDescription = data.FrequencyShortDescription;
                } else {
                    self.FrequencyDescription = data.FrequencyDescription; 
                }
            }
        };
    };
})();;
(function () {
    PaymentPage.PaymentPageViewModel = function (conversation) {
        setLoadingIsVisible(false);
        var self = this;
        appHeader.WorkflowStep(appHeader.WorkflowStepEnum.Payment);
        appHeader.IsAccessSelected(true);

        // Enumerator - to match GoodlifeEcom.Web.Enums.PaymentType
        self.PaymentTypeEnum = {
            AccountDebit: 1,
            CreditCard: 2,
            NotApplicable: 3
        };
        self.PaymentStatusEnum = {
            Successful: 0,
            Recoverable: 1,
            Fatal: 2
        }

        self.PaymentConfig = ko.observable();

        // Upfront properties
        self.UpfrontItems = ko.observableArray();
        self.UpfrontTotalValue = 0.00;
        self.UpfrontTotalDiscountedValue = 0.00;
        self.UpfrontPaymentType = ko.observable(self.PaymentTypeEnum.NotApplicable);
        self.UpfrontAccountDebit = new PaymentPage.AccountDebitModel();
        self.UpfrontCreditCard = new PaymentPage.CreditCardModel();
        // Ongoing properties
        self.OngoingItems = ko.observableArray();
        self.OngoingTotalValue = 0.00;
        self.OngoingTotalDiscountedValue = 0.00;
        self.TotalMinimumCost = 0.00;
        self.OngoingPaymentType = ko.observable(self.PaymentTypeEnum.AccountDebit);
        self.OngoingAccountDebit = new PaymentPage.AccountDebitModel();
        self.OngoingCreditCard = new PaymentPage.CreditCardModel();
        self.EnhancementFee = new PaymentPage.PaymentItemsModel();
        // General Properties
        self.MemberFirstName = ko.observable(null);
        self.PromoCode = ko.observable('');
        // Tag property
        self.CartProducts = [];

        // Payment Progress Properties
        self.ConversationId = conversation.ConversationId();
        self.memberDetails = conversation.MemberDetails(); //local reference to the memberimages
        self.formToShow = ko.observable('paymentForm');
        self.partialToShow = ko.observable('noPartialToShow');
        self.creatingMembershipFor = ko.pureComputed(function () {
            var memberFirstName = (self.memberDetails != null && self.memberDetails.length !== 0 ? self.memberDetails[0].FirstName() : "");
            return "Creating membership for " + memberFirstName.toUpperCase();
        });
        self.createdMembershipFor = ko.pureComputed(function () {
            if (!self.memberDetails || self.memberDetails.length === 0) {
                return "";
            }

            var memberFirstName = self.memberDetails[0].FirstName().toUpperCase();
            var memberAge = parseInt(self.memberDetails[0].Age());
            if (memberAge < 18) {
                return "You're almost done " + memberFirstName + "!";
            } else {
                return "Done! Welcome aboard " + memberFirstName + "!";
            }
        });

        self.TermsAndConditionsHtml = ko.computed(function () {
            var paymentConfig = self.PaymentConfig();

            var termsHtml = "";

            var selectedPartner = appConversation.SelectedPartner();
            if (selectedPartner && selectedPartner.TermsAndConditions) {
                termsHtml += selectedPartner.TermsAndConditions;
            }

            if (paymentConfig) {
                termsHtml += paymentConfig.TermsAndConditionsHtml;
            }

            return termsHtml;
        });

        self.whatsLeftToDo = ko.computed(function () {
            var paymentConfig = self.PaymentConfig();
            if (!paymentConfig) {
                return [];
            }
            var nextStepsModel = paymentConfig.NextSteps;
            var memberAge = parseInt(self.memberDetails[0].Age());
            var returnSteps;
            if (memberAge < 18) {
                returnSteps = { Title: nextStepsModel.Under18.Title, Steps: nextStepsModel.Under18.Steps };
            } else {
                returnSteps = { Title: nextStepsModel.AtLeast18.Title, Steps: nextStepsModel.AtLeast18.Steps };
            }

            // Replace [[Clubname]] in the nextsteps with the actual club name.
            returnSteps.Steps.forEach(function (step, index, array) {
                array[index] = step.replace("[[Clubname]]", self.clubName());
            });

            return returnSteps;
        });

        self.clubName = ko.computed(function () {
            return appHeader.clubName();
        });
        self.progErrMsg = ko.observable("");
        self.memberImage = ko.observable("");
        self.hasPayNowBeenAttempted = ko.observable(false);
        self.isTermsAndConditionsAgreedTo = ko.observable(false);
        self.showTermsAndConditionsError = ko.computed(function () {
            if (self.hasPayNowBeenAttempted() && !self.isTermsAndConditionsAgreedTo()) {
                return true;
            }
            return false;
        });

        // if the member/s has uploaded any profile images, set the profile image 
        if (self.memberDetails != null && self.memberDetails.length !== 0) {
            // Set the initial member image to an avatar photo
            if (self.memberDetails[0].Gender() === "f") {
                self.memberImage("url('Content/Image/2535-female.png')");
            } else {
                self.memberImage("url('Content/Image/2535-male.png')");
            }

            if (self.memberDetails[0].MemberImageBase64() != null) {
                self.memberImage(("url('" + self.memberDetails[0].MemberImageBase64() + "')"));
            }
        } else {
            self.memberImage = "";
        }

        // CheckBoxes
        // Use same payment method as for upfront payment
        self.OngoingSameAddressAs = ko.observable(true);

        self.OngoingAddress = new PaymentPage.AddressModel(function () {
            return self.OngoingSameAddressAs();
        });
        self.EnteredAddress = new PaymentPage.AddressModel(function () {
            return !self.OngoingSameAddressAs();
        });

        self.SelectedAddress = ko.computed(function () {
            if (self.OngoingSameAddressAs()) {
                return self.OngoingAddress;
            } else {
                return self.EnteredAddress;
            }
        });
        // Same billing address as
        self.OngoingSamePaymentAsUpfront = ko.observable(true);
        // I have a promo code
        self.HavePromoCode = ko.observable(false);
        // Check if the Upfront Payment type exists as Ongoing Payment Type
        self.OngoingPaymentTypeCanDifferFromUpfront = ko.pureComputed(function () {
            if (self.UpfrontPaymentType() === self.PaymentTypeEnum.CreditCard) {
                return true;
            };
            return false;
        });
        // UpfrontIsAccountDebit must remain a computed observable, not pureComputed, because it is a trigger to change a value
        self.UpfrontIsAccountDebit = ko.computed(function () {
            if (self.UpfrontPaymentType() === self.PaymentTypeEnum.AccountDebit) {
                self.OngoingSamePaymentAsUpfront(true);
                return true;
            }
            return false;
        });

        //Adyen Encrypted Form Data
        self.AdyenEncryptedData = ko.observable("");


        // Setters
        self.SetUpfrontItems = function (data) {
            if ((data) && (Array.isArray(data))) {
                data.forEach(function (item) {
                    if (item) {
                        var paymentItem = new PaymentPage.PaymentItemsModel();
                        paymentItem.SetData(item);
                        self.UpfrontItems.push(paymentItem);
                    }
                });
            };
        };
        self.SetOngoingItems = function (data) {
            if ((data) && (Array.isArray(data))) {
                data.forEach(function (item) {
                    if (item) {
                        var paymentItem = new PaymentPage.PaymentItemsModel();
                        paymentItem.SetData(item);
                        self.OngoingItems.push(paymentItem);
                    }
                });
            };
        };
        self.SetData = function (data) {
            if (data) {
                // Upfront properties
                self.SetUpfrontItems(data.UpfrontItems);
                self.UpfrontTotalValue = data.UpfrontTotalValue.toFixed(2);
                self.UpfrontTotalDiscountedValue = data.UpfrontTotalDiscountedValue.toFixed(2);
                self.showUpfrontPaymentDetails = self.UpfrontTotalDiscountedValue > 0;
                self.UpfrontPaymentType(data.UpfrontPaymentType ? data.UpfrontPaymentType : self.PaymentTypeEnum.NotApplicable);
                if (self.showUpfrontPaymentDetails && self.UpfrontPaymentType() === self.PaymentTypeEnum.NotApplicable) {
                    self.UpfrontPaymentType(self.PaymentTypeEnum.AccountDebit);
                }
                self.UpfrontAccountDebit.SetData(data.UpfrontAccountDebit);
                self.UpfrontCreditCard.SetData(data.UpfrontCreditCard);
                // Ongoing properties
                self.SetOngoingItems(data.OngoingItems);
                self.OngoingTotalValue = data.OngoingTotalValue.toFixed(2);
                self.OngoingTotalDiscountedValue = data.OngoingTotalDiscountedValue.toFixed(2);
                self.TotalMinimumCost = data.TotalMinimumCost.toFixed(2);
                self.OngoingPaymentType(data.OngoingPaymentType ? data.OngoingPaymentType : self.PaymentTypeEnum.AccountDebit);
                self.OngoingAccountDebit.SetData(data.OngoingAccountDebit);
                self.OngoingCreditCard.SetData(data.OngoingCreditCard);
                self.OngoingAddress.SetData(data.OngoingAddress);
                if (data.EnhancementFee) {
                    self.EnhancementFee.SetData(data.EnhancementFee);
                }

                // General Properties
                self.PromoCode(data.PromoCode);

                self.PaymentConfig(JSON.parse(data.PaymentConfigString));
            }
        };
        // Set the Payment Type
        self.SetPaymentType = function (data, event) {
            if (event.currentTarget.id === "UpfrontAccountDebit") {
                self.UpfrontPaymentType(self.PaymentTypeEnum.AccountDebit);
            } else if (event.currentTarget.id === "UpfrontCreditCard") {
                self.UpfrontPaymentType(self.PaymentTypeEnum.CreditCard);
            } else if (event.currentTarget.id === "OngoingAccountDebit") {
                self.OngoingPaymentType(self.PaymentTypeEnum.AccountDebit);
            } else if (event.currentTarget.id === "OngoingCreditCard") {
                self.OngoingPaymentType(self.PaymentTypeEnum.CreditCard);
            }
        };

        // Check if all the relevant payment form inputs are valid or not 
        var areAllInputsValid = function () {
            var totalNumberOfErrors = 0;

            if (self.showUpfrontPaymentDetails) {
                if (self.UpfrontPaymentType() === self.PaymentTypeEnum.AccountDebit && self.UpfrontAccountDebit.errors().length !== 0) {
                    totalNumberOfErrors += self.UpfrontAccountDebit.errors().length;
                    self.UpfrontAccountDebit.errors.showAllMessages();
                }
                if (self.UpfrontPaymentType() === self.PaymentTypeEnum.CreditCard && self.UpfrontCreditCard.errors().length !== 0) {
                    totalNumberOfErrors += self.UpfrontCreditCard.errors().length;
                    self.UpfrontCreditCard.errors.showAllMessages();
                }
            }

            if (!self.showUpfrontPaymentDetails || !self.OngoingSamePaymentAsUpfront()) {
                if (self.OngoingPaymentType() === self.PaymentTypeEnum.CreditCard && self.OngoingCreditCard.errors().length !== 0) {
                    totalNumberOfErrors += self.OngoingCreditCard.errors().length;
                    self.OngoingCreditCard.errors.showAllMessages();
                }
                if (self.OngoingPaymentType() === self.PaymentTypeEnum.AccountDebit && self.OngoingAccountDebit.errors().length !== 0) {
                    totalNumberOfErrors += self.OngoingAccountDebit.errors().length;
                    self.OngoingAccountDebit.errors.showAllMessages();
                }
            }

            if (!self.OngoingSameAddressAs() && self.EnteredAddress.errors().length !== 0) {
                totalNumberOfErrors += self.EnteredAddress.errors().length;
                self.EnteredAddress.errors.showAllMessages();
            }

            if (!self.isTermsAndConditionsAgreedTo()) {
                totalNumberOfErrors++;
            }

            if (totalNumberOfErrors > 0) {
                return false;
            }

            return true;
        };

        self.hasValidationErrors = ko.computed(function () {
            if (!self.hasPayNowBeenAttempted()) {
                return false;
            };

            if (areAllInputsValid()) {
                self.hasPayNowBeenAttempted(false);
                return false;
            }

            return true;
        });

        // On inital page load hide the what's left part of the page
        $("#whatsLeft").toggle(0);

        self.populateTestCreditCardDetails = function () {
            if (self.showUpfrontPaymentDetails) {
                self.UpfrontPaymentType(self.PaymentTypeEnum.CreditCard);
                self.UpfrontCreditCard.CardNumber("5136333333333335");
                self.UpfrontCreditCard.NameOnCard("John Doe");
                self.UpfrontCreditCard.ExpiryMonth("10");
                self.UpfrontCreditCard.ExpiryYear("20");
                self.UpfrontCreditCard.CVV("737");
            }
            self.OngoingPaymentType(self.PaymentTypeEnum.CreditCard);
            self.OngoingCreditCard.CardNumber("5136333333333335");
            self.OngoingCreditCard.NameOnCard("John Doe");
            self.OngoingCreditCard.ExpiryMonth("10");
            self.OngoingCreditCard.ExpiryYear("20");
            self.OngoingCreditCard.CVV("737");
        };

        self.populateTestDirectDebitDetails = function () {
            if (self.showUpfrontPaymentDetails) {
                self.UpfrontPaymentType(self.PaymentTypeEnum.AccountDebit);
                self.UpfrontAccountDebit.FinancialInstitution("Commonwealth Bank");
                self.UpfrontAccountDebit.AccountName("Mr Somebody");
                self.UpfrontAccountDebit.BSB("123456");
                self.UpfrontAccountDebit.AccountNumber("12345678");
            }
            self.OngoingPaymentType(self.PaymentTypeEnum.AccountDebit);
            self.OngoingAccountDebit.FinancialInstitution("Commonwealth Bank");
            self.OngoingAccountDebit.AccountName("Mr Somebody");
            self.OngoingAccountDebit.BSB("123456");
            self.OngoingAccountDebit.AccountNumber("12345678");
        };

        self.disableLuhnChecks = function () {
            self.UpfrontCreditCard.BypassLuhnCheck(!self.UpfrontCreditCard.BypassLuhnCheck());
            self.OngoingCreditCard.BypassLuhnCheck(!self.OngoingCreditCard.BypassLuhnCheck());
        };

        // Submit the payment
        self.PayNow = function (data) {
            self.hasPayNowBeenAttempted(true);

            if (self.hasValidationErrors()) {
                return;
            }

            //Adyen client side encrytion
            self.AdyenCSE();


            // Copy the Payment Method from Upfront to Ongoing if flag is checked
            if (self.OngoingSamePaymentAsUpfront() && self.showUpfrontPaymentDetails) {
                self.OngoingPaymentType(self.UpfrontPaymentType());
                if (self.UpfrontPaymentType() === self.PaymentTypeEnum.AccountDebit) {
                    self.OngoingAccountDebit.SetData(ko.toJS(self.UpfrontAccountDebit));
                } else if (self.UpfrontPaymentType() === self.PaymentTypeEnum.CreditCard) {
                    self.OngoingCreditCard.SetData(ko.toJS(self.UpfrontCreditCard));
                };
            };

            self.OngoingAddress.SetData(ko.toJS(self.SelectedAddress));

            $("#whatsLeft").toggle(0, function () {
                $(".progress .rotate").addClass("loop");
            });

            self.formToShow("inProgress");
            self.partialToShow("inProgress");

            appHeader.lock();
            appHeader.CloseSummary();
            appHeader.SetTopMessage("Your payment is being processed...");

            // Write Google Analytics steps 2 and 3 for upfront and ongoing paymemt options.

            var upfrontPaymentName;
            var ongoingPaymentName;
            if (self.UpfrontPaymentType() === self.PaymentTypeEnum.CreditCard) {
                upfrontPaymentName = "CC";
            } else if (self.UpfrontPaymentType() === self.PaymentTypeEnum.AccountDebit) {
                upfrontPaymentName = "AD";
            } else {
                upfrontPaymentName = "N/A";
            }

            if (self.OngoingPaymentType() === self.PaymentTypeEnum.CreditCard) {
                ongoingPaymentName = "CC";
            } else if (self.OngoingPaymentType() === self.PaymentTypeEnum.AccountDebit) {
                ongoingPaymentName = "AD";
            } else {
                ongoingPaymentName = "N/A";
            }

            dataLayer.push({
                'event': 'checkoutOption',
                'ecommerce': {
                    'checkout_option': {
                        'actionField': { 'step': 3, 'option': 'Upfront: ' + upfrontPaymentName + ', Ongoing: ' + ongoingPaymentName }
                    }
                }
            });

            // scroll to top of page so user can see progress circle
            $(document).scrollTop(0);

            CallAjax(
                "/Payment/ProcessPayment",
                "POST",
                ko.toJSON(self),
                self.ProcessPaymentComplete,
                function(err) {
                    appHeader.SetError("Oops, something went wrong. Please contact Zap Fitness to finalise your membership.");
                    appHeader.enableGoodlifeCMSLink = true;
                    $(".progress .rotate").addClass("red");
                    $(".progress .rotate").removeClass("orange");
                    self.partialToShow("failAbortProgress");
                },
                true);
        };

        self.clearPaymentDetails = function () {
            self.UpfrontAccountDebit.SetData({});
            self.UpfrontCreditCard.SetData({});
            self.OngoingAccountDebit.SetData({});
            self.OngoingCreditCard.SetData({});
        };

        self.receiptHtml = null;
        self.ProcessPaymentComplete = function (response) {
            appHeader.enableGoodlifeCMSLink = true;

            dataLayer.push({
                'event': 'checkoutOption',
                'ecommerce': {
                    'checkout': {
                        'actionField': { 'step': 4 },
                        'products': self.CartProducts
                    }
                }
            });

            if (response.Success) {
                window.history.replaceState({}, 'Success', '/Success');
                $(".progress .rotate").removeClass("orange");
                $(".progress .rotate").addClass("blue");

                self.receiptHtml = response.ReceiptHtml;
                self.partialToShow("successProgress");
                appHeader.SetTopMessage("Payment accepted!");
                appHeader.statusText("Welcome to Zap Fitness " + appHeader.clubName());

                // Fire Floodlight tracking tag completion
                self.FireFloodlightCompletion();

                dataLayer.push({
                    'event': 'checkoutOption',
                    'ecommerce': {
                        'checkout_option': {
                            'actionField': { 'step': 4, 'option': 'Over 18' }
                        }
                    },
                    'eventCallback': function() {
                        dataLayer.push({
                            'event': 'checkoutComplete',
                            'ecommerce': {
                                'purchase': {
                                    'actionField': {
                                        'id': self.ConversationId,
                                        'affiliation': 'eCommerce',
                                        'revenue': self.TotalMinimumCost
                                    },
                                    'products': self.CartProducts
                                }
                            }
                        });
                    }
                });

                if (!response.ErrorType) {
                    self.clearPaymentDetails();
                }

                return;
            }

            $(".progress .rotate").removeClass("orange");
            $(".progress .rotate").addClass("red");

            if (response.ErrorType === "Payment") {
                appHeader.SetError("Oops, something went wrong. Please try again.");
                self.partialToShow("failCanRetryProgress");
            } else {
                dataLayer.push({
                    'event': 'checkoutOption',
                    'ecommerce': {
                        'checkout_option': {
                            'actionField': { 'step': 4, 'option': 'Error' }
                        }
                    }
                });
                appHeader.SetError("Oops, something went wrong. Please contact Zap Fitness to finalise your membership.");
                self.partialToShow("failAbortProgress");
            }

            self.progErrMsg(response.ErrorMessage);

            if (response.ErrorType !== "Payment") {
                self.clearPaymentDetails();
            }
        };

        self.RetryPaymentForm = function () {
            $(".progress .rotate").removeClass("loop");
            $(".progress .rotate").removeClass("red");
            $(".progress .rotate").addClass("orange");

            // Hide the what's left part of the page
            $("#whatsLeft").toggle(0);
            self.formToShow("paymentForm");
            self.partialToShow("noPartialToShow");
            appHeader.unlock();
        };

        self.AskContactHelp = function () {
            alert("Put it on the ask contact help TODO list.");
        };

        self.IsShowWaivers = ko.observable(false);

        self.ToggleShowWaivers = function () {
            self.IsShowWaivers(!self.IsShowWaivers());
        }

        self.GoToClub = function () {
            window.location.href = appHeader.GoodlifeSiteUrl;
        }

        self.BuildCartProducts = function () {
            // All cart products are required. 
            var membership = conversation.SelectedMembership();

            self.CartProducts.push({
                'name': membership.Name,
                'id': membership.MembershipOptionGuid,
                'price': membership.WeeklyPrice,
                'brand': appHeader.clubName(),
                'category': 'Membership',
                'list': 'Membership',
                'variant': membership.Term,
                'quantity': 1
            });

            _.each(conversation.SelectedOptions(), function (option) {
                self.CartProducts.push({
                    'name': option.Name,
                    'id': option.Name,
                    'price': option.Price,
                    'brand': appHeader.clubName(),
                    'category': 'Option',
                    'variant': 'Option',
                    'list': 'Option',
                    'quantity': 1
                });
            });
        }

        self.FireFloodlightCompletion = function () {

            // Write to the datalayer for the Floodlight tags.
            dataLayer.push({
                'totalCost': self.TotalMinimumCost,
                'conversationId': self.ConversationId,
                'event': 'gtmPaymentComplete'
            });
        }

        var currentUrl = window.location.href.toLowerCase();
        // CHeck of any of the member/s signing up is under 18. If under 18 skip payment process and go straight to
        // success progress page with additional information on what to do next.
        if (currentUrl.indexOf("/payment") >= 0 && self.memberDetails != null && self.memberDetails.length !== 0) {
            for (var i = 0; i < self.memberDetails.length; i++) {
                var memberAge = parseInt(self.memberDetails[i].Age());

                if (memberAge < 18) {
                    self.formToShow("inProgress");
                    self.partialToShow("inProgress");

                    $("#whatsLeft").toggle(0, function () {
                        $(".progress .rotate").addClass("loop");
                    });

                    appHeader.lock();
                    appHeader.SetTopMessage("Hang on while we create your Zap Fitness profile!");

                    var successFunction = function () {
                        $(".progress .rotate").removeClass("orange");
                        $(".progress .rotate").addClass("blue");

                        appHeader.SetTopMessage("All done!");
                        appHeader.enableGoodlifeCMSLink = true;
                        self.partialToShow("successProgress");
                        appHeader.statusText("Welcome to Zap Fitness " + appHeader.clubName());

                        dataLayer.push({
                            'event': 'checkoutOption',
                            'ecommerce': {
                                'checkout_option': {
                                    'actionField': { 'step': 4, 'option': 'Under 18' }
                                }
                            },
                            'eventCallback': function () {
                                dataLayer.push({
                                    'event': 'checkoutComplete',
                                    'ecommerce': {
                                        'purchase': {
                                            'actionField': {
                                                'id': self.ConversationId,
                                                'affiliation': 'eCommerce',
                                                'revenue': self.TotalMinimumCost
                                            },
                                            'products': self.CartProducts
                                        }
                                    }
                                });
                            }
                        });
                    }

                    var errorFunction = function () {
                        appHeader.SetError("Oops, something went wrong. Please contact the club to complete your membership.");

                        $(".progress .rotate").removeClass("orange");
                        $(".progress .rotate").addClass("red");
                        appHeader.enableGoodlifeCMSLink = true;
                        self.partialToShow("failAbortProgress");
                    }

                    CallAjax(
                        "/Payment/ProcessUnder18Prospect",
                        "POST",
                        ko.toJSON(self),
                        successFunction,
                        errorFunction,
                        true
                    );
                }
            }

            self.BuildCartProducts();

            dataLayer.push({
                'event': 'checkoutOption',
                'ecommerce': {
                    'checkout': {
                        'actionField': { 'step': 3 },
                        'products': self.CartProducts
                    }
                }
            });
        }

        self.OpenReceiptPdf = function () {
            var url = "/Payment/Receipt";
            var params = {
                "UriEncodedHtml": encodeURIComponent(self.receiptHtml)
            };
            submitFORM(url, params, null);
        };


        //ADYEN CLIENT SIDE ENCRYPTION
        self.AdyenCSE = function () {

            //Adyen encryption
            var key = document.getElementById("adyen.csePublicKey").value;
            var options = {
            };

            var cseInstance = adyen.encrypt.createEncryption(key, options);


            var postData = {};


            var cardData = {};
            if (self.showUpfrontPaymentDetails) {
                cardData = {
                    number: self.UpfrontCreditCard.CardNumber(),
                    cvc: self.UpfrontCreditCard.CVV(),
                    holderName: self.UpfrontCreditCard.NameOnCard(),
                    expiryMonth: self.UpfrontCreditCard.ExpiryMonth(),
                    expiryYear: "20" + self.UpfrontCreditCard.ExpiryYear(),
                    generationtime: new Date().toISOString()// Should be server side
                };
            }else{
                cardData = {
                    number: self.OngoingCreditCard.CardNumber(),
                    cvc: self.OngoingCreditCard.CVV(),
                    holderName: self.OngoingCreditCard.NameOnCard(),
                    expiryMonth: self.OngoingCreditCard.ExpiryMonth(),
                    expiryYear: "20" + self.OngoingCreditCard.ExpiryYear(),
                    generationtime: new Date().toISOString()
                }
            }
            

            postData['adyen-encrypted-data'] = cseInstance.encrypt(cardData);
            self.AdyenEncryptedData(JSON.stringify(postData));
        }
    }
})();;
