
// ****************************************************** //
// **************** Kitbag Object Class ***************** //
// ****************************************************** //

// storefolder : string : folder name of the store
function KitbagObjects(storefolder) {

    this.Services = new Services();
    this.Cache = new Cache();

    function Services() {
        this.Product = function() {
            var service = (this._productService) ? this._productService : new ProductServiceProxy("/stores/" + storefolder + "/services/ProductService.svc/");
            this._productService = service;
            return service;
        };
        this.General = function() {
            var service = (this._generalService) ? this._generalService : new GeneralServiceProxy("/stores/" + storefolder + "/services/GeneralService.svc/");
            this._generalService = service;
            return service;
        };
        this.Customer = function() {
            var service = (this._customerService) ? this._customerService : new CustomerServiceProxy("/stores/" + storefolder + "/services/CustomerService.svc/");
            this._customerService = service;
            return service;
        };
        this.Basket = function() {
            var service = (this._basketService) ? this._basketService : new BasketServiceProxy("/stores/" + storefolder + "/services/BasketService.svc/");
            this._basketService = service;
            return service;
        };
    }

    function Cache() {
        this.Set = function(item, data) {
            this[item] = data;
            return this[item];
        };
        this.Get = function(item) {
            if (typeof (this[item]) !== "undefined") {
                return this[item];
            } else {
                return null;
            }
        };
        this.JQuery = function(item) {
            if (typeof (this[item]) === "undefined") {
                this[item] = $(item);
            }
            return this[item];
        };
    }
}

(function($) {

    // ****************************************************** //
    // ***************** Accordian Plug In ****************** //
    // ****************************************************** //

    // For the accordian to work correctly the html markup must have the following structure //
    // <div>  -  this is the accordian container and should have the .kb_accordian plug in activated on it
    //      <div class='[paneClass]'> - this is the accordian pane
    //          <div class='[headerClass]'></div> - this is the accordian header
    //          <div></div> - this is the accordian body
    //      </div>
    //      ...repeat for each accordian pane
    // </div>


    // options : JSON : configuration options for the accordian
    $.fn.kb_accordian = function(options) {

        // Set default values
        // grow : boolean : determines whether the container div grows with content or not
        // defaultpane : JQuery selector : determine which panel to open first
        var opts = { 'overlap': false, 'defaultpane': ':first', 'grow': false, 'headerClass': '.accHead', 'bodyClass': '.accBody', 'paneClass': '.accArea', 'selectedClass': 'accSelected' };

        // Override the defaults if others have been supplied
        if (options) { $.extend(opts, options); }

        // find all accordian headers within the element
        var accHeaders = $(this).find(opts.headerClass);

        // Add mouse over functionality to add class
        accHeaders.live("mouseenter", function() {
            $(this).addClass(opts.selectedClass).css("cursor", "pointer");
            $(this).siblings(opts.bodyClass).addClass("accBodySelected")
        });

        // Add mouse out functionality to remove class
        accHeaders.live("mouseleave", function() {
            if ($(this).data("selected") != true) {
                $(this).removeClass(opts.selectedClass);
                $(this).siblings(opts.bodyClass).removeClass("accBodySelected")
            }
        });

        // Adds click function to each header
        accHeaders.live("click", function(e) {
            e.preventDefault();
            $.fn.kb_accordian.open(opts, $(this), false);
        });

        // Opens the default accordian pane
        $.fn.kb_accordian.open(opts, accHeaders.filter(opts.defaultpane), true);

        return this;
    };

    // Opens the accordian body associated with the given header
    // opts : JSON : configuration options for the accordian
    // accHeader : JQuery : header object of accordian to open
    // init : boolean : if this is the default accordian to open set heights, and css values (top and z-index)
    $.fn.kb_accordian.open = function(opts, accHeader, init) {

        var accPanel = accHeader.parent();
        var accContainer = accPanel.parent();
        var accPanels = accContainer.children(opts.paneClass);
        var accToOpen = accPanels.index(accPanel);
        var accMaxHeight = 0;
        var accBodyMaxHeight = 0;
        var accContainerHeight;

        if (init) {
            accPanels.children(opts.bodyClass).each(function() {
                if ($(this).outerHeight(true) > accMaxHeight) {
                    accBodyMaxHeight = $(this).outerHeight(true);
                }
            });
            if (opts.grow) {
                accPanels.children(opts.bodyClass).each(function() {
                    $(this).height(accBodyMaxHeight);
                });
            }
            accPanels.each(function() {
                if ($(this).height() > accMaxHeight) {
                    accMaxHeight = $(this).height();
                }
            });
            if (opts.grow) {
                accContainerHeight = accMaxHeight + (accHeader.outerHeight(true) * (accPanels.length - ((opts.overlap) ? 2 : 1)));
                accContainer.height(accContainerHeight);
            }
        }

        accPanels.each(function() {
            var accIndex, accTopPosition;
            accIndex = accPanels.index($(this));

            // Set the top position of the panel
            if (accIndex <= accToOpen) {
                accTopPosition = accIndex * accHeader.outerHeight(true);
            } else {
                accTopPosition = (accContainer.height() - ((accPanels.length - accIndex) * accHeader.outerHeight(true)));
            }
            if (!init) {
                $(this).animate({ top: accTopPosition }, 500, "swing");
            } else {
                $(this).height(accMaxHeight).css({ "top": accTopPosition, "z-index": (accIndex + 1) * 10 });
            }
            $(this).children().eq(0).removeClass(opts.selectedClass).removeData();
            $(this).children(opts.bodyClass).removeClass("accBodySelected");
        });

        // Add the selected class to the clicked accordian header
        accHeader.addClass(opts.selectedClass).data("selected", true);
        accHeader.siblings(opts.bodyClass).addClass("accBodySelected");
        if (typeof Cufon == 'function') {
            Cufon.refresh();
        }
    };

    // ****************************************************** //
    // **************** ContentTree Plug In ***************** //
    // ****************************************************** //

    // Uses the GeneralService to return content tree information
    // Pre-Requisites:  KB - instance of KitbagObjects class that should be instantiated in the [site].js file

    // options : JSON : configuration options for the accordian
    $.fn.kb_content = function(options) {

        // Set default values
        // branch : integer : id of the content branch
        // leaf : string : the name of the leaf to return
        var opts = { 'branch': 0, 'leaf': '' };

        // the current element to poulate with content
        var element = $(this);

        // Override the defaults if others have been supplied
        if (options) { $.extend(opts, options); }

        // Calls webservice to get content
        KB.Services.General().GetLeafContent(opts.branch, opts.leaf, onGetContentComplete, onError);

        function onGetContentComplete(result) {
            if (result) {
                element.html(result);
            }
        }

        function onError(result) {
            element.html("");
        }

        return this;
    };

    // ****************************************************** //
    // ************ Newsletter Sign Up Plug In ************** //
    // ****************************************************** //

    // For the signup to work correctly the html markup must have the following structure //
    // <div>  -  this is the signup container and should have the .kb_signup plug in activated on it
    //      <input class='[boxClass]' /> - this is the input box for the email entry
    //      <a class='[buttonClass]'></div> - this is the link that trigures the sign up
    //      <div class='[responseClass]'></div> - this is the div that will appear, it gives feedback on the success of the sign up
    // </div>

    // Uses the CustomerService to sign customer up or redirects to lyris welcome programme
    // Pre-Requisites:  KB - instance of KitbagObjects class that should be instantiated in the [site].js file
    //                  JQuery 1.4 or higher

    // options : JSON : configuration options for the accordian
    $.fn.kb_signup = function(options) {

        // Set default values
        // branch : integer : id of the content branch
        // leaf : string : the name of the leaf to return
        var opts = { 'welcomeurl': '', 'siteCatalyst': true, 'boxClass': '.signUpBox', 'buttonClass': '.signUpButton', 'responseClass': '.signUpResponse' };

        // Override the defaults if others have been supplied
        if (options) { $.extend(opts, options); }

        // the current element
        var element = $(this);

        // Find email box
        var emailBox = $(this).find(opts.boxClass)

        // Ensures default text in sign up box is removed on focus and replaced on blur if nothing has been entered
        emailBox.focus(function() {
            if (this.defaultValue == this.value) { this.value = ""; }
        }).blur(function() {
            if (this.value == "") { this.value = this.defaultValue; }
        });

        $(this).find(opts.buttonClass).click(function() {
            var myRegxp = /^[\w-\.]{1,}\@([\da-zA-Z-]{1,}\.){1,}[\da-zA-Z-]{2,3}$/;
            if (myRegxp.test(emailBox.val())) {
                if (opts.welcomeurl == '') {
                    // Try and sign the customer up
                    KB.Services.Customer().AddCustomerToMailList(emailBox.val(), onSignUpContentComplete, onError);
                } else {
                    // Launch lyris welcome page
                    document.location.href = options.welcomeurl + "?e=" + emailBox.val();
                }

            } else {
                if (opts.siteCatalyst) {
                    s.sendFormEvent('e', s.pageName, 'Newsletter Reg', 'Err:Email address is invalid');
                }
                element.find(opts.responseClass).html('Email address is invalid').slideDown(400).delay(5000).slideUp(400);
            }
        });

        function onSignUpContentComplete(result) {
            if (opts.siteCatalyst) {
                if (result.indexOf('subscribing') != -1) {
                    s.sendFormEvent('e', s.pageName, 'Newsletter Reg', 'Err:' + result);
                } else {
                    s.sendFormEvent('s', s.pageName, 'Newsletter Reg');
                }
            }
            element.find(opts.responseClass).html(result).slideDown(400).delay(5000).slideUp(400);
        }

        function onError(result) {
            element.find(opts.responseClass).html("Unfortunately an error has occurred, please try again").slideDown(400).delay(5000).slideUp(400);
            if (opts.siteCatalyst) {
                s.sendFormEvent('e', s.pageName, 'Newsletter Reg');
            }
        }

        return this;
    };

    // ****************************************************** //
    // ******************* Tabs Plug In ********************* //
    // ****************************************************** //

    // For the tabs to work correctly the html markup must have the following structure //
    // <div class='[tabclass]'>  -  this is the tabs container and should have the .kb_tabs plug in activated on it
    //      <div class='[headerClass]'> - this is the accordian pane
    //          <ul>
    //              <li><a>tab header 1</a></li>
    //              ...repeat for each tab
    //          </ul>
    //      </div>
    //      <div class="'[panesClass]'">
    //          <div>
    //              contents here
    //          </div>
    //      </div>
    //      ...repeat for each tab
    // </div>


    // options : JSON : configuration options for the accordian
    $.fn.kb_tabs = function(options) {

        // Set default values
        var opts = { 'tabEffect': 'fade', 'tabClass': '.tabs', 'headerClass': '.tabheader', 'panesClass': '.tabcontents', 'selectedClass': 'tabselected', 'selectedIndex': 0 };

        // Override the defaults if others have been supplied
        if (options) { $.extend(opts, options); }

        // Show the first tab if hidden
        $(this).children("div").children(opts.panesClass).children("div").eq(opts.selectedIndex).show();

        // Store the first tab as the active tab
        $(this).data("activeTab", opts.selectedIndex);

        $(this).find(opts.headerClass).bind("click", function(evt) {
            var element = evt.target.tagName.toUpperCase() == "A" ? $(evt.target) : $(evt.target).parents("a").eq(0);
            evt.preventDefault();
            element.addClass(opts.selectedClass);
            $.fn.kb_tabs.open(opts, element);
        });

        $(this).find(opts.headerClass + " a").eq(opts.selectedIndex).addClass(opts.selectedClass);

        return this;
    };

    $.fn.kb_tabs.open = function(opts, tabClicked) {

        var tabsContainer = tabClicked.parents(opts.tabClass);
        var tabs = tabsContainer.find(opts.headerClass + " a");
        var tabPanes = tabsContainer.children("div").children(opts.panesClass).children("div");
        var oldPane = tabPanes.eq(tabsContainer.data("activeTab"));
        var newIndex = tabs.index(tabClicked);
        
        if (tabsContainer.data("activeTab") != newIndex) {

            switch (opts.tabEffect) {
                case 'fade':
                    oldPane.fadeTo(300, 0, function() {
                        oldPane.hide();
                        tabPanes.eq(newIndex).show().fadeTo(200, 1);
                        tabs.removeClass(opts.selectedClass);
                        tabClicked.addClass(opts.selectedClass);
                        if (typeof Cufon == 'function') {
                            Cufon.refresh();
                        }
                    });
                    break;
                case 'scroll':
                    tabs.removeClass(opts.selectedClass);
                    var shift = newIndex * (oldPane.innerWidth() * -1)
                    tabsContainer.children("div").children(opts.panesClass).animate({ left: shift }, 300, "swing");
                    tabClicked.addClass(opts.selectedClass);
                    if (typeof Cufon == 'function') {
                        Cufon.refresh();
                    }
                    break;
            }
            tabsContainer.data("activeTab", newIndex)
        }
    };

})(jQuery);

// ****************************************************** //
// ************** Height Equaliser Plugin *************** //
// ****************************************************** //

// equalises all heights of elements passed in.
// minHeight - integer - minimum height of elements (optional)
// maxHeight - integer - maximum height of elements (optional)

(function($) {
    $.fn.kb_equaliseHeights = function(options) {

        // Set default values
        var opts = { 'minHeight': 0, 'maxHeight': 0, 'overflow': 'hidden' };

        // Override the defaults if others have been supplied
        if (options) { $.extend(opts, options); }

        this.each(function() {
            if ($(this).height() > opts.minHeight) {
                opts.minHeight = $(this).height();
            }
        });

        if ((opts.maxHeight > 0) && (opts.minHeight > opts.maxHeight)) {
            opts.minHeight = opts.maxHeight;
        }

        return this.each(function() {
            $(this).height(opts.minHeight).css("overflow", opts.overflow);
        });
    }
})(jQuery);

// ****************************************************** //
// **************** Shadow Viewer Class ***************** //
// ****************************************************** //

// Finds all instances of .viewer class and sets up the click event to trigger shadow viewer effect

var shadowViewer = {

    // Store the element highlighted here so it can be closed via a close link
    elementHighlighted: {},

    init: function() {

        $(".viewer").live("click", function(event) {
            // Cancel any default actions
            event.preventDefault();

            // Find the element to show from the rel attribute and then send to highlight function
            shadowViewer.highlight({ 'elementToHighlight': $(this).attr("rel") });
        });

        $(".viewerClose").live("click", function() {
            // Fade out the element to show
            shadowViewer.hide();
        });
    },

    //
    highlight: function(options) {
        // elementToHighlight - object - the element to highlight can be a jquery css selector or a dom element. If none given a new dom element will be created
        // duration - integer - the duration which the animation to highlight the element should last
        // animation - string - the animation type used to highlight the element (currently 3 options)
        // width - integer - width in pixels to set highlight to. overrides and value in css
        // height - integer - height in pixels to set highlight to. overrides and value in css
        // callback - function - a function to run once the element has been highlighted, dont pass function name with parenthesis otherwise it will be invoked immediately
        // resourceURL - string - a resource to load using ajax
        // resourceContainer - object - the element to load the resource into, it can be a dom element or jquery css selector. If none provided then it will load into the elementToHighlight
        // resourceCache - boolean - determines whether the resource should be cached or not
        var opts = { 'elementToHighlight': null, 'duration': 600, 'animation': 'fadeIn', 'width': null, 'height': null, 'callback': null, 'resourceURL': null, 'resourceContainer': null, 'resourceCache': false };

        // Override the defaults if others have been supplied
        if (options) { $.extend(opts, options); }

        // Store the highlighted element, create one if one hasn't been provided
        shadowViewer.elementHighlighted = (opts.elementToHighlight) ? $(opts.elementToHighlight) : $("<div></div>", { 'class': 'highlightDiv' }).data("new", true);

        // Set new height and width if they have been explicitly set
        if (opts.height) { shadowViewer.elementHighlighted.height(opts.height); }
        if (opts.width) { shadowViewer.elementHighlighted.width(opts.width); }

        // Position highlight element
        shadowViewer.elementHighlighted.css({ "position": "absolute", "margin": "auto", "z-index": "10001" }); ;

        // If a resource has to be loaded then load it
        if (opts.resourceURL) {
            var docContainer = (opts.resourceContainer) ? $(opts.resourceContainer) : shadowViewer.elementHighlighted;
            $.ajax({
                url: opts.resourceURL,
                async: false,
                cache: opts.resourceCache,
                success: function(data) {
                    docContainer.html(data);
                }
            });
        }

        // Calculate dimensions and positions
        var docwidth = window.innerWidth || (window.document.documentElement.clientWidth || window.document.body.clientWidth);
        var docheight = window.innerHeight || (window.document.documentElement.clientHeight || window.document.body.clientHeight);
        var docy = window.pageYOffset || (window.document.documentElement.scrollTop || window.document.body.scrollTop);
        var newTop = Math.max((docheight / 2) - (((opts.height) ? opts.height : shadowViewer.elementHighlighted.height()) / 2) + docy, 0);
        var newLeft = (docwidth / 2) - (((opts.width) ? opts.width : shadowViewer.elementHighlighted.width()) / 2);

        // Create shadow frame using shadowDiv class (make iframe if IE6) and then set css properties
        var shadowDiv = ($.browser.msie) ? $('<iframe class="shadowDiv" src="/stores/common/products/block.html"></iframe>') : $('<div class="shadowDiv"></div>');
        shadowDiv.height($(document).height()).appendTo($("body")).css("opacity", 0.8).show(0);
        shadowViewer.elementHighlighted.wrap('<div class="shadowViewerPlaceHolder" />').appendTo($("body")).css("left", newLeft - 10).css("top", newTop);

        switch (opts.animation) {
            case "show":
                shadowViewer.elementHighlighted.show(opts.duration, function() { if (opts.callback) { opts.callback() }; });
                break;
            case "slide":
                shadowViewer.elementHighlighted.slideDown(opts.duration, function() { if (opts.callback) { opts.callback() }; });
                break;
            default:
                shadowViewer.elementHighlighted.fadeIn(opts.duration, function() { if (opts.callback) { opts.callback() }; });
        }

    },

    hide: function(options) {

        var opts = { 'duration': 300, 'animation': 'fadeOut', 'callback': null };

        // Override the defaults if others have been supplied
        if (options) { $.extend(opts, options); }

        switch (opts.animation) {
            case "hide":
                shadowViewer.elementHighlighted.hide(opts.duration, function() { shadowViewer.hideContent(opts.callback); });
                break;
            case "slide":
                shadowViewer.elementHighlighted.slideUp(opts.duration, function() { shadowViewer.hideContent(opts.callback); });
                break;
            default:
                shadowViewer.elementHighlighted.fadeOut(opts.duration, function() { shadowViewer.hideContent(opts.callback); });
        }

    },

    hideContent: function(callback) {
        shadowViewer.elementHighlighted.appendTo(".shadowViewerPlaceHolder").unwrap();
        $(".shadowDiv").remove();
        if (shadowViewer.elementHighlighted.data("new")) {
            shadowViewer.elementHighlighted.remove();
        }
        if (callback) { callback() };
    }
};
