function Main() {
    waitDiv = new Jax.Widgets.Element("loading", null);
    rbnForm = new RbnForm("orderform", document.forms[0]);
    rbnForm.clickHandler(onClick);
    rbnForm.submitHandler(onSubmit);
    waitDiv.hide();
}

function onClick(sender, eventArg) {
    var orderForm = sender.parent;
    var widget = orderForm.widgets[sender.index];
    
    if (sender.getValue() === "") {
        if (validateOrder(widget)) {
            sender.setSource(sender.images[1]); // from Purchase image to Remove image.
            if (widget.order != null) {
                if (widget.order.getValue() === "") {
                    widget.order.setValue("1");
                }
                widget.order.setCssClass("static normal"); // disable entry.
                widget.order.readOnly(true);
                sender.setValue(widget.order.getValue());
            }
            else {
                sender.setValue("1");
            }
        }
    }
    else {
        sender.setSource(sender.images[0]); // from Remove image to Purchase image.
        sender.setValue("");
        widget.total.setValue("");
        if (widget.order != null) {
            widget.order.setValue("");
            widget.order.setCssClass(""); // enable entry.
            widget.order.readOnly(false);
        }
    }
    
    orderForm.calculateTotals(widget);
    return false;
    
    //////////////////////////////////////////////////////////////////////////
    // validateOrder.
    //////////////////////////////////////////////////////////////////////////
    function validateOrder(widget) {
        var retval = true;
        if (widget.order != null) {
            var orderValue = widget.order.isEmpty() ? 1 : parseInt(widget.order.getValue());
            var limitValue = parseInt(widget.limit.getValue());
            if (orderValue < 0) {
                alert("Negative number is not accepted.");
                return false;
            }
            else if (orderValue == 0) {
                alert("Zero is not an acceptable quantity.");
                return false;
            }
            else if (orderValue > limitValue) {
                alert("Your request for " + widget.label.getText() + " exceeds order limit of " + limitValue + ".");
                widget.order.setValue(widget.limit.getValue());
            }
        }
        
        return retval;
    }
}

function onSubmit(sender) {
	var result = rbnForm.validate();
	
	if ( result == true )
		return rbnForm.submit(sender);
	else
		return false; 	
}

function RbnForm(name, form) {

	// Declare class and set instance.
	Jax.oop.declare(RbnForm, Jax.Widgets.Form);
	this.setInstance();
    
	// Set properties.
	this.btnSubmit = null;

    this.initialize = function() {
        var images = ["images/purchase.png", "images/remove.png"];
        this.txtSubTotal = new Jax.Widgets.Textbox("txtSubTotal", this);
        this.txtShippingTotal = new Jax.Widgets.Textbox("txtShippingTotal", this);
        this.txtGrandTotal = new Jax.Widgets.Textbox("txtGrandTotal", this);
        this.btnSubmit = new Jax.Widgets.SubmitButton("submitbutton", this);
        prepareShippingSelections.call(this);
        preparePaymentSelections.call(this);
        this.widgets = 
        [
            {
                sku: new Jax.Widgets.Hidden("hdnRelaxerFreeShip", this),
                label: new Jax.Widgets.Element("lblRelaxerFreeShipLabel", this),
                price: new Jax.Widgets.Textbox("txtRelaxerFreeShipPrice", this), 
                order: null, 
                limit: new Jax.Widgets.Hidden("hdnRelaxerFreeShipLimit", this),
                total: new Jax.Widgets.Textbox("txtRelaxerFreeShipTotal", this),
                button: new Jax.Widgets.ImageButton("btnRelaxerFreeShipOrder", this, images)
            },
            { 
                sku: new Jax.Widgets.Hidden("hdnRelaxerSpecial", this),
                label: new Jax.Widgets.Element("lblRelaxerSpecialLabel", this),
                price: new Jax.Widgets.Textbox("txtRelaxerSpecialPrice", this),
                order: null, 
                limit: new Jax.Widgets.Hidden("hdnRelaxerSpecialLimit", this),
                total: new Jax.Widgets.Textbox("txtRelaxerSpecialTotal", this),
                button: new Jax.Widgets.ImageButton("btnRelaxerSpecialOrder", this, images)
            },
            { 
                sku: new Jax.Widgets.Hidden("hdnRelaxerUnit", this),
                label: new Jax.Widgets.Element("lblRelaxerLabel", this),
                price: new Jax.Widgets.Textbox("txtRelaxerPrice", this),
                order: new Jax.Widgets.Numberbox("txtRelaxerOrder", this), 
                limit: new Jax.Widgets.Hidden("hdnRelaxerLimit", this),
                total: new Jax.Widgets.Textbox("txtRelaxerTotal", this),
                button: new Jax.Widgets.ImageButton("btnRelaxerOrder", this, images)
            },
            { 
                sku: new Jax.Widgets.Hidden("hdnRelaxerCase", this),
                label: new Jax.Widgets.Element("lblRelaxerCaseLabel", this),
                price: new Jax.Widgets.Textbox("txtRelaxerCasePrice", this),
                order: new Jax.Widgets.Numberbox("txtRelaxerCaseOrder", this), 
                limit: new Jax.Widgets.Hidden("hdnRelaxerCaseLimit", this),
                total: new Jax.Widgets.Textbox("txtRelaxerCaseTotal", this),
                button: new Jax.Widgets.ImageButton("btnRelaxerCaseOrder", this, images)
            },
            { 
                sku: new Jax.Widgets.Hidden("hdnShampooUnit", this),
                label: new Jax.Widgets.Element("lblShampooLabel", this),
                price: new Jax.Widgets.Textbox("txtShampooPrice", this),
                order: new Jax.Widgets.Numberbox("txtShampooOrder", this),
                limit: new Jax.Widgets.Hidden("hdnShampooLimit", this),
                total: new Jax.Widgets.Textbox("txtShampooTotal", this),
                button: new Jax.Widgets.ImageButton("btnShampooOrder", this, images)
            },
            { 
                sku: new Jax.Widgets.Hidden("hdnConditionerUnit", this),
                label: new Jax.Widgets.Element("lblConditionerLabel", this),
                price: new Jax.Widgets.Textbox("txtConditionerPrice", this),
                order: new Jax.Widgets.Numberbox("txtConditionerOrder", this),
                limit: new Jax.Widgets.Hidden("hdnConditionerLimit", this),
                total: new Jax.Widgets.Textbox("txtConditionerTotal", this),
                button: new Jax.Widgets.ImageButton("btnConditionerOrder", this, images)
            }
        ];
        
        this.widgets.forEach(function(item, index) {
            item.button.index = index;
            if (item.total.getValue() != "") {
                onClick(item.button);
            }
        });
        
        if (Jax.dom.isIE()) {
            var tblOrderForm = new Jax.Widgets.Element("tblOrderForm" , this);
            tblOrderForm.setCssClass("ieorderform");
        }
    }
    
    //////////////////////////////////////////////////////////////////////////
    //  clickHandler : Handles button click.
    //////////////////////////////////////////////////////////////////////////
    this.clickHandler = function(handler) {
        this.widgets.forEach(function(item) {
            item.button.clickHandler(handler);
        });
    }
    
    //////////////////////////////////////////////////////////////////////////
    //  submitHandler : Handles submission.
    //////////////////////////////////////////////////////////////////////////
    this.submitHandler = function(handler) {
        this.btnSubmit.clickHandler(handler);
    }
    
    //////////////////////////////////////////////////////////////////////////
    //  calculateTotals : Calculate totals.
    //////////////////////////////////////////////////////////////////////////
    this.calculateTotals = function(widget) {
        this.calcItemTotal(widget);
        this.calcGrandTotals();
    }
    
    //////////////////////////////////////////////////////////////////////////
    //  validate : Validate options.
    //////////////////////////////////////////////////////////////////////////
    this.validate = function() {
    
        if (this.txtGrandTotal.isEmpty()) {
            alert("Please enter your order");
            return false;
        }
        
        if (this.optShippingDestination.isEmpty()) {
            alert("Please select the shipping destination");
            this.optShippingDestination.setFocus();
            return false;
        }
        
        var destination = this.optShippingDestination.getValue().trim();
        if (this.optShippingDestination.options.getItem(destination).locales != undefined) {
            var pair = this.optShippingDestination.options.getItem(destination).locales[0].split(":");
            if (this.optShippingDestinationLocale.isEmpty()) {
                alert("Please select the shipping destination " + pair[1].toLowerCase());
                this.optShippingDestinationLocale.setFocus();
                return false;
            }
            else {
                var localType = new Jax.Widgets.Hidden("hdnShippingDestinationLocaleType", this)
                localType.setValue(pair[1]);
            }
        }
        
        if (destination == "NZL") {
            if (!this.radPaymentSelections.isSelected()) {
                alert("Please select your method of payment");
                this.radPaymentSelections.setFocus();
                return false;
            }
        }
        
        preparePurchases.call(this);
        return true;
    }
    
    //////////////////////////////////////////////////////////////////////////
    //	calcItemTotal
    //////////////////////////////////////////////////////////////////////////
    this.calcItemTotal = function(widget)
    {
        var order = widget.button.isEmpty() ? 0 : parseInt(widget.button.getValue());
        var sku = widget.sku.getValue();
        var request = new RbnCalcOrderRequest(sku, order);
        widget.total.setValue(request.getResponse());
    }

    //////////////////////////////////////////////////////////////////////////
    //	calcSubTotal
    //////////////////////////////////////////////////////////////////////////
    this.calcSubTotal = function()
    {
        var subTotal = 0;
        this.widgets.forEach(function(item) {
            if (!item.total.isEmpty()) {
                subTotal += parseFloat(item.total.getValue().substring(1));
            }
        });
        
        if (subTotal == 0) {
            this.txtSubTotal.setValue("");
        }
        else {
            this.txtSubTotal.setValue("$" + subTotal.toFixed(2))
        }
        
        return subTotal;
    }
    
    /////////////////////////////////////////////////////////////////////////
    //  calcShippingTotal
    //
    //  serialized pattern:
    //      <CC>|<ShippingOption>|<RbnId>:<order>|...|<RbnId>:<order>
    //////////////////////////////////////////////////////////////////////////
    this.calcShippingTotal = function()
    {
        // Get subtotal.
        var subTotal = (this.txtSubTotal.getValue() === "") 
            ? 0
            : parseFloat(this.txtSubTotal.getValue().substring(1));

        // Build serialized request used to determine shipping.
        var request = prepareShippingRequest.call(this);
        var shipping = testShippingRequest(request) 
            ? parseFloat(new RbnCalcShippingRequest(request).getResponse())
            : 0

        this.txtShippingTotal.setValue((shipping == 0) ? "" : "$" + shipping.toFixed(2));
        return shipping;
    }
    
    //////////////////////////////////////////////////////////////////////////
    //      calcGrandTotals
    //////////////////////////////////////////////////////////////////////////
    this.calcGrandTotals = function() {
        var subTotal = this.calcSubTotal();
        var shipping = this.calcShippingTotal();
        var grandTotal = subTotal + shipping;
        if (grandTotal == 0) {
            this.txtGrandTotal.setValue("");
        }
        else {
            this.txtGrandTotal.setValue("$" + grandTotal.toFixed(2));
        }
    }
    
    //////////////////////////////////////////////////////////////////////////
    // onShippingDestinationChange
    //////////////////////////////////////////////////////////////////////////
    function onShippingDestinationChange(sender) {
        if (sender.getValue().trim() === "") {
            this.txtShippingMethod.container.hide();
            this.optShippingDestinationLocale.container.hide();
        }
        else {
            this.txtShippingMethod.container.show();
            var shippingOption = sender.options.getItem(sender.getValue());
            var keyval = shippingOption.methods[0].split(":");
            this.txtShippingMethod.setValue(keyval[1]);
            if (shippingOption.locales != undefined) {
                this.optShippingDestinationLocale.clear();
                shippingOption.locales.forEach(new Delegate(this, function(item, index) {
                    var pair = item.split(":");
                    if (index == 0) {
                        this.optShippingDestinationLocale.label.setText("Select " + pair[1]);
                    }
                    else {
                        this.optShippingDestinationLocale.add(pair[0], pair[1]);
                    }
                }));
                
                this.optShippingDestinationLocale.container.show();
            }
            else {
                this.optShippingDestinationLocale.container.hide();
            }
        }
        
        preparePaymentMethod.call(this, sender);
        this.calcGrandTotals();
    }
    
    //////////////////////////////////////////////////////////////////////////
    // onShippingDestinationLocaleChange
    //////////////////////////////////////////////////////////////////////////
    function onShippingDestinationLocaleChange(sender) {
        this.calcGrandTotals();
    }
    
    //////////////////////////////////////////////////////////////////////////
    // prepareShippingRequest
    //////////////////////////////////////////////////////////////////////////
    function prepareShippingRequest() {
        var request = "";
        var sender = this.optShippingDestination;
        
        if (sender.getValue().trim() !== "") {
            var value = sender.getValue();
            request = request + sender.getValue() + "|";
            if (sender.options.getItem(value).locales != undefined) {
                var locale = this.optShippingDestinationLocale.getValue();
                request = request + locale + "|";
            }
            
            request += sender.options.getItem(sender.getValue()).methods[0].split(":")[0];
            request += "|";
            
            var purchases = "";
            this.widgets.forEach(new Delegate(this, function(item) {
                if (!item.total.isEmpty()) {
                    var sku = item.sku.getValue();
                    var order = item.button.isEmpty() ? 0 : parseInt(item.button.getValue());
                    var param = sku + ":" + order;
                    purchases = purchases + param + "|";
                }
            }));
            
            purchases += (purchases === "") ? "|" : "";
            request += purchases;
            request = request.slice(0, -1);
        }
        
        return request;
    }
    
    //////////////////////////////////////////////////////////////////////////
    // testShippingRequest.
    //////////////////////////////////////////////////////////////////////////
    function testShippingRequest(request) {
        var retval = (request !== "");
        if (retval) {
            var values = request.split("|");
            values.forEach(function(item) {
                retval = retval && (item !== "");
            });
        }
        
        return retval;
    }
    
    //////////////////////////////////////////////////////////////////////////
    // prepareShippingSelections
    //////////////////////////////////////////////////////////////////////////
    function prepareShippingSelections() {
        this.optShippingDestination = new Jax.Widgets.DropDown("optShippingDestination", this);
        this.optShippingDestination.changeHandler(new Delegate(this, onShippingDestinationChange));
        this.optShippingDestination.options = new Hashtable();
        this.txtShippingMethod = new Jax.Widgets.Textbox("txtShippingMethod", this);
        this.txtShippingMethod.container = new Jax.Widgets.Element("pnlShippingMethod", this);
        this.txtShippingMethod.container.hide();
        this.optShippingDestinationLocale = new Jax.Widgets.DropDown("optShippingDestinationLocale", this);
        this.optShippingDestinationLocale.changeHandler(new Delegate(this, onShippingDestinationLocaleChange));
        this.optShippingDestinationLocale.container = new Jax.Widgets.Element("pnlShippingDestinationLocale", this);
        this.optShippingDestinationLocale.container.hide();
        this.optShippingDestinationLocale.label = new Jax.Widgets.Element("lblShippingDestinationLocale", this);
        
        // Populate destinations.
        var destinations = (new RbnShippingDestinationsRequest()).getResponse().split("|");
        destinations.forEach(new Delegate(this, function(item) {
            var pair = item.split(":");
            this.optShippingDestination.add(pair[1], pair[0]);
            var option = {};
            var request = new RbnShippingDestinationLocalesRequest(pair[0]).getResponse();
            if (request != "") {
                option.locales = request.split("|");
            }
            
            option.methods = (new RbnShippingMethodsRequest(pair[0])).getResponse().split("|");
            this.optShippingDestination.options.setItem(pair[0], option);
        }));
    }
    
    //////////////////////////////////////////////////////////////////////////
    // preparePaymentSelections
    //////////////////////////////////////////////////////////////////////////
    function preparePaymentSelections() {
        this.radPaymentSelections = new Jax.Widgets.RadioList("PaymentMethod", this);
        this.spnPaymentMethod = new Jax.Widgets.Element("spnPaymentMethod", this);
        this.spnPaymentMethodCreditCard = new Jax.Widgets.Element("spnPaymentMethodCreditCard", this);
        this.spnPaymentMethod.hide();
        this.spnPaymentMethodCreditCard.hide();
    }
    
    //////////////////////////////////////////////////////////////////////////
    // preparePaymentMethod
    //////////////////////////////////////////////////////////////////////////
    function preparePaymentMethod(sender) {
        this.spnPaymentMethod.hide();
        this.spnPaymentMethodCreditCard.hide();
        switch (sender.getValue().trim()) {
            case "": 
                break;
                
            case "NZL":
                this.spnPaymentMethod.show();
                break;
                
            default:
                this.spnPaymentMethodCreditCard.show();
                break;
        }
    }
    
    //////////////////////////////////////////////////////////////////////////
    // preparePurchases
    //////////////////////////////////////////////////////////////////////////
    function preparePurchases() {
        var subTotal = new Jax.Widgets.Hidden("hdnSubTotal", this);
        var shippingTotal = new Jax.Widgets.Hidden("hdnShippingTotal", this);
        var grandTotal = new Jax.Widgets.Hidden("hdnGrandTotal", this);
        var purchases = 
        [
            { 
                price: new Jax.Widgets.Hidden("hdnRelaxerFreeShipPrice", this), 
                order: new Jax.Widgets.Hidden("hdnRelaxerFreeShipOrder", this),
                total: new Jax.Widgets.Hidden("hdnRelaxerFreeShipTotal", this)
            },
            { 
                price: new Jax.Widgets.Hidden("hdnRelaxerSpecialPrice", this),
                order: new Jax.Widgets.Hidden("hdnRelaxerSpecialOrder", this), 
                total: new Jax.Widgets.Hidden("hdnRelaxerSpecialTotal", this)
            },
            { 
                price: new Jax.Widgets.Hidden("hdnRelaxerPrice", this),
                order: new Jax.Widgets.Hidden("hdnRelaxerOrder", this), 
                total: new Jax.Widgets.Hidden("hdnRelaxerTotal", this)
            },
            { 
                price: new Jax.Widgets.Hidden("hdnRelaxerCasePrice", this),
                order: new Jax.Widgets.Hidden("hdnRelaxerCaseOrder", this), 
                total: new Jax.Widgets.Hidden("hdnRelaxerCaseTotal", this)
            },
            { 
                price: new Jax.Widgets.Hidden("hdnShampooPrice", this),
                order: new Jax.Widgets.Hidden("hdnShampooOrder", this),
                total: new Jax.Widgets.Hidden("hdnShampooTotal", this)
            },
            { 
                price: new Jax.Widgets.Hidden("hdnConditionerPrice", this),
                order: new Jax.Widgets.Hidden("hdnConditionerOrder", this),
                total: new Jax.Widgets.Hidden("hdnConditionerTotal", this)
            },
        ];
        
        subTotal.setValue(this.txtSubTotal.getValue());
        shippingTotal.setValue(this.txtShippingTotal.getValue());
        grandTotal.setValue(this.txtGrandTotal.getValue());
        this.widgets.forEach(function(item, index) {
            purchases[index].price.setValue(item.price.getValue());
            purchases[index].total.setValue(item.total.getValue());
            var order = (item.order != null)
                ? item.order.getValue() 
                : (item.total.isEmpty())
                    ? ""
                    : "1";
            purchases[index].order.setValue(order);
        });
        
        var hdnCreditCardPayment = new Jax.Widgets.Hidden("hdnCreditCardPayment", this);
        var isCC = this.spnPaymentMethodCreditCard.isVisible() || this.radPaymentSelections.getSelectedAt() == this.radPaymentSelections.count() - 1;
        hdnCreditCardPayment.setValue(isCC);
    }
    
    //////////////////////////////////////////////////////////////////////////
    // Initialize instance.
    //////////////////////////////////////////////////////////////////////////
    if (arguments.length) {
        this.name = name;
        this.form = form;
        this.element = form;
        this.items = this.getItems(this.name, this);
        this.initialize();
    }
}