﻿/// <reference path="jquery-1.3.2.js" />
/*global jQuery, Cufon $ */

if (location.pathname !== "/" || !location.hash) {
    var pathname = location.hash[1] === '/' ? location.hash.substring(1) : location.pathname;
    location.replace(location.protocol + "//" + location.host + "/#" + pathname);
}

if (!jQuery) {
    alert("Error: jQuery is not defined");
}
else {

    (function() {

        var jQuery_fx_step_default = jQuery.fx.step._default;
        jQuery.fx.step._default = function(fx) {
            if (!fx.elem.customAnimate) {
                return jQuery_fx_step_default.apply(this, arguments);
            }
            fx.elem[fx.prop] = fx.now;
            fx.elem.updated = true;
        };

    })();



    (function() {

        function num(elem, prop) {
            return elem[0] && parseInt(jQuery.curCSS(elem[0], prop, true), 10) || 0;
        }
        jQuery.fn.extend({
            left: function(val) {
                if (arguments.length === 0) {
                    return this.offset().left;
                }
                else {
                    val -= this.offsetParent().offset().left;
                    return this.css("left", val);
                }
            },

            top: function(val) {
                if (arguments.length === 0) {
                    return this.offset().top;
                }
                else {
                    val -= this.offsetParent().offset().left;
                    return this.css("top", val);
                }
            }
        });

    })();

    Math.fitTo = Math.fitTo || function(val, min, max) {
        if (min > max) {
            return Math.fitTo(val, max, min);
        }
        return val <= min ? min : val >= max ? max : val;
    };

    Math.interpolate = Math.interpolate || function(left, right, t, min, max) {
        t = Math.fitTo(t, 0, 1);

        var ret = left + t * (right - left);

        if (min !== undefined && max !== undefined) {
            ret = Math.fitTo(ret, max, min);
        }
        return ret;
    };

    String.prototype.repeat = String.prototype.repeat || function(num) {
        var a = [];
        a.length = num + 1;
        return a.join(this);
    };

    var Controtex = Controtex || {};

    if (!Controtex.PageSlider) {

        // Helper
        var createProperty = function(opt) {
            opt.isMutable = opt.isMutable || function() { return true; };
            opt.isValid = opt.isValid || function() { return true; };
            opt.onSetImmutable = opt.onSetImmutable || opt.onError || function() {
                throw new Error("Property is immutable");
            };
            opt.onSetInvalid = opt.onSetInvalid || opt.onError || function(val) {
                throw new Error("Value " + val + " is invalid for property");
            };
            opt.getter = opt.getter || function(val) {
                throw new Error("Property has not getter");
            };
            opt.setter = opt.setter || function(val) {
                throw new Error("Property has not setter");
            };

            return function(x) {
                if (arguments.length === 0) {
                    return opt.getter.call(this);
                }
                else {
                    if (!opt.isMutable.call(this, x)) {
                        opt.onSetImmutable.call(this, x);
                    }
                    else if (!opt.isValid.call(this, x)) {
                        opt.onSetInvalid.call(this, x);
                    }
                    else {
                        opt.setter.call(this, x);
                    }
                    return this;
                }
            };
        };

        var PageState = { INIT: 0, LOADING: 1, LOADED: 2, ERROR: 3 };

        Controtex.PageSlider = function() {
            // Fields
            this._options = {
                defaultSection: "",
                defaultPage: "",
                scrollDuration: 1200,
                scrollEasing: "cubicEaseInOut",
                onRefresh: function() { },
                styles: {
                    menuDefault: {
                        color: jQuery.getRGB("#404e5f")
                    },
                    menuActive: {
                        color: jQuery.getRGB("#f9ce04")
                    }
                },
                pageMargin: 120,
                minHeight: 200
            };
            this._sections = [];
            this._sectionMap = {};
            this._initialised = false;
            this._animator = jQuery.extend($("<div>")[0], {
                index: -1,
                customAnimate: true,
                updated: true
            });
            this._currentIndex = -1;
        };

        // Methods
        jQuery.extend(Controtex.PageSlider.prototype, {
            // Private
            _pathParser: /^\/?([^\/]*)\/?(.*)/,
            _parsePath: function(path) {
                var match = this._pathParser.exec(path);
                return {
                    section: match[1] || this._options.defaultSection,
                    page: match[2] || this._options.defaultPage
                };
            },
            _getPath: function(section, page) {
                var path = [];
                if (section !== this._options.defaultSection) {
                    path.push("/");
                    path.push(section);
                }
                if (page !== this._options.defaultPage) {
                    path.push("/");
                    path.push(page);
                }

                return path.length === 0 ? "/" : path.join("");
            },
            _loadPage: function(sectionName, page) {
                var slider = this;
                var index = this._sectionMap[sectionName];
                if (index !== undefined) {
                    var section = this._sections[index];

                    if (section.activePage === page && (section.state === PageState.LOADING || section.state === PageState.LOADED)) {
                        // Already loading (or loaded) this page, so don't try to load it
                        return;
                    }
                    else if (section.activePage !== page && section.state === PageState.LOADING && section.currentAjax !== undefined) {
                        // Already loading another page, so cancel that
                        section.currentAjax.abort();
                    }
                    section.state = PageState.LOADING;

                    var titleChangerDots = 0;
                    var titleChangerInterval = setInterval(function() {
                        titleChangerDots = (titleChangerDots + 1) % 3;
                        document.title = "Loading" + ".".repeat(titleChangerDots);
                    }, 500);

                    section.activePage = page;

                    if (section.pageElement !== undefined) {
                        var oldPage = section.pageElement;
                        oldPage.fadeOut(500, function() { oldPage.remove(); });
                    }

                    section.currentAjax = jQuery.ajax({
                        url: this._getPath(sectionName, page),
                        success: function(data, textStatus) {
                            clearInterval(titleChangerInterval);
                            slider._insertLoadedPage(index, data);
                            section.state = PageState.LOADED;
                        },
                        error: function(xhr, textStatus, errorThrown) {
                            clearInterval(titleChangerInterval);
                            switch (textStatus) {
                                case "timeout":
                                case "error":
                                    slider._insertLoadedPage(index, xhr.responseText, false);
                                    section.state = PageState.ERROR;
                                    break;
                                case "notmodified":
                                    document.title = section.activeTitle;
                                    section.state = PageState.LOADED;
                                    break;
                            }

                        },
                        dataType: "html"
                    });
                }

            },
            _insertLoadedPage: function(index, doc) {
                var section = this._sections[index];

                var dummyDoc = $("<div/>").append(doc.replace(/<script(.|\s)*?\/script>/g, ""));

                var page = dummyDoc.find(this._contentSelector);
                page.css({ position: "absolute", left: 0, top: 0 });
                page.removeAttr("id");
                section.pageWrapper.append(page);
                section.pageWrapper.height(page.outerHeight(true));
                section.pageElement = page;
                section.activeTitle = dummyDoc.find("title").text();
                page.hide();
                page.fadeIn(500);

                if (this._currentIndex === index) {
                    this._sliderWrapper.stop().animate({ height: this._getIndexHeight(index) }, 500);
                    document.title = section.activeTitle;
                }

                this._options.onRefresh();
            },

            _getIndexHeight: function(index) {
                var left = Math.floor(index), right = Math.ceil(index);

                if (left === right) {
                    // We're directly on top of a page, so return the max of the min height and the
                    // height at the index
                    var height = this._options.minHeight;
                    if (this._sections[index]) {
                        height = Math.max(height, this._sections[index].pageWrapper.height());
                    }
                    return height;
                }
                else {
                    // We're between two pages, so recurse to get the values at exact indices, and
                    // set the height to interpolate between right and left values
                    return Math.interpolate(this._getIndexHeight(left), this._getIndexHeight(right), index - left);
                }
            },
            _jumpToSectionIndex: function(index, opt) {
                opt = opt || {};
                opt.duration = opt.duration !== undefined ? opt.duration : this._options.scrollDuration;
                opt.easing = opt.easing !== undefined ? opt.easing : this._options.scrollEasing;

                var section = this._sections[index];
                if (section !== undefined) {
                    // Try to reload the page, in case it's an error page
                    this._loadPage(section.name, section.activePage);
                    // Set the hash to the current page's address
                    location.hash = "#" + this._getPath(section.name, section.activePage);
                    document.title = section.activeTitle;
                }

                this._currentIndex = index;
                this._sliderWrapper.stop().animate({ height: this._getIndexHeight(index) }, jQuery.extend({}, opt));
                $(this._animator).stop().animate({ index: index }, jQuery.extend({}, opt));
            },
            _setSectionIndex: function(index) {
                this._currentIndex = this._animator.index = index;

                this._sliderWrapper.height(this._getIndexHeight(index));
                this._updateDisplay();
            },
            _snapToIndex: function() {
                var i = Math.round(Math.fitTo(this._animator.index, 0, this._sections.length - 1));
                this._jumpToSectionIndex(i, { duration: 600 });
            },
            _getPageOuterWidth: function() {
                return (2 * this._options.pageMargin) + this._pageWidth;
            },
            _getSliderOffset: function(index) {
                return -index * this._getPageOuterWidth();
            },
            _getSectionValues: function(index) {
                var link;
                if (index < 0) {
                    // We're off the left side
                    // So set the values as though the first link repeated leftwards infinitely
                    link = this._sections[0].menuItem;
                    return {
                        menuLeft: link._measurer.left() + index * link.outerWidth(true),
                        menuWidth: link.width()
                    };
                }
                else if (index >= this._sections.length) {
                    // We're off the right side
                    // So set the values as though the last link repeated rightwards infinitely
                    link = this._sections[this._sections.length - 1].menuItem;
                    return {
                        menuLeft: link._measurer.left() + (index - (this._sections.length - 1)) * link.outerWidth(true),
                        menuWidth: link.width()
                    };
                }
                else {
                    // We're inside the bounds
                    // So get the values of the appropriate link
                    link = this._sections[index].menuItem;
                    return {
                        menuLeft: link._measurer.left(),
                        menuWidth: link.width()
                    };
                }
            },
            _getIndexFromUnderlinePos: function(mouseX, posAlongLine) {
                var linkPositions = jQuery.map(this._sections, function(section) {
                    var link = section.menuItem;
                    return link._measurer.left() + posAlongLine * link.width();
                });
                var index = 0;

                // If we're off one of the edges, return an index assuming widths are the same as the nearest element
                if (mouseX < linkPositions[0]) {
                    // We're off the left side
                    index = -(linkPositions[0] - mouseX) / this._sections[0].menuItem.outerWidth(true);
                }
                else if (mouseX > linkPositions[linkPositions.length - 1]) {
                    // We're off the right side
                    index = (this._sections.length - 1) + (mouseX - linkPositions[linkPositions.length - 1]) / this._sections[this._sections.length - 1].menuItem.outerWidth(true);
                }
                else {
                    // We're somewhere within the sections, so find where
                    var left = 0;
                    while (mouseX >= linkPositions[left + 1]) {
                        left++;
                    }

                    var leftpos = linkPositions[left];
                    if (mouseX === leftpos) {
                        // We're directly over a section
                        index = left;
                    }
                    else {
                        index = left + (mouseX - leftpos) / (linkPositions[left + 1] - leftpos);
                    }
                }

                return index;
            },
            _updateDisplay: function() {
                var index = this._animator.index;

                if (this._sections.length === 0) {
                    return;
                }

                // Move the slider to the appropriate position
                this._slider.css("left", this._getSliderOffset(index));

                var left = Math.floor(index), right = Math.ceil(index);
                var underline = this._menuUnderline;
                var underlineWidth, underlineLeft;

                if (left === right) {
                    // We're directly on top of a page
                    var values = this._getSectionValues(index);

                    // Set the underline to the current link's left and width
                    underlineLeft = values.menuLeft;
                    underlineWidth = values.menuWidth;
                }
                else {
                    // We're between two pages
                    var lValues = this._getSectionValues(left);
                    var rValues = this._getSectionValues(right);

                    // Set the underline to interpolate between right and left values
                    underlineLeft = Math.interpolate(lValues.menuLeft, rValues.menuLeft, index - left);
                    underlineWidth = Math.interpolate(lValues.menuWidth, rValues.menuWidth, index - left);
                }
                underline.left(underlineLeft);
                underline.width(underlineWidth);

                // Set the displays of all the sections
                jQuery.each(this._sections, function(i) {
                    this.updateDisplay(i - index);
                });

                this._options.onRefresh();
            },
            _interpolator: {
                color: function(start, end, t) {
                    return "rgb(" + [
			            parseInt(Math.interpolate(start[0], end[0], t, 0, 255), 10),
			            parseInt(Math.interpolate(start[1], end[1], t, 0, 255), 10),
			            parseInt(Math.interpolate(start[2], end[2], t, 0, 255), 10)
		            ].join(",") + ")";
                }
            },
            _updateSectionDisplay: function(section, dist) {
                var slider = this;
                var styles = slider._options.styles;
                dist = Math.min(Math.abs(dist), 1);

                var menuItemStyle = {};
                jQuery.each(this._options.styles.menuDefault, function(key) {
                    if (!styles.menuActive[key]) {
                        return;
                    }

                    // Distance of 0 is fully active, distance >= 1 is fully inactive
                    var start = styles.menuActive[key];
                    var end = this;
                    menuItemStyle[key] = slider._interpolator[key](start, end, dist);
                });
                section.menuItem.css(menuItemStyle);

                section.pageWrapper.css("opacity", 1 - Math.min(2 * dist, 1));
            },

            // Public
            container: createProperty({
                getter: function() { return this._container; },
                setter: function(val) { this._container = $(val); },
                isMutable: function() { return !this._initialised; },
                onSetImmutable: function() { throw new Error("Slider already initialised"); }
            }),
            contentSelector: createProperty({
                getter: function() { return this._contentSelector; },
                setter: function(val) { this._contentSelector = val; },
                isMutable: function() { return !this._initialised; },
                onSetImmutable: function() { throw new Error("Slider already initialised"); }
            }),
            menu: createProperty({
                getter: function() { return this._menu; },
                setter: function(val) {
                    this._menu = $(val);
                    this._links = this._menu.find("a");
                },
                isMutable: function() { return !this._initialised; },
                onSetImmutable: function() { throw new Error("Slider already initialised"); }
            }),
            links: createProperty({
                getter: function() { return this._links; }
            }),
            options: createProperty({
                getter: function() { return this._options; },
                setter: function(val) { jQuery.extend(this._options, val); },
                isMutable: function() { return !this._initialised; },
                onSetImmutable: function() { throw new Error("Slider already initialised"); }
            }),
            sections: createProperty({
                getter: function() { return this._sections; }
            }),

            goToPage: function(path) {
                var target = this._parsePath(path);
                var index = this._sectionMap[target.section];
                if (index !== undefined) {
                    this._loadPage(target.section, target.page);
                    this._jumpToSectionIndex(index);
                }
                else {
                    location = path;
                }
            },

            init: function() {
                // Make immutable
                this._initialised = true;

                // For closures
                var slider = this;

                var current = this._parsePath(location.hash.substring(1));
                current.index = -1;

                // Set sections from menu links
                if (this._links && this._links.length > 0) {
                    this._sections.length = 0;
                    this._links.each(function(i) {
                        // Parse the link, and set the appropriate section name and current page
                        var parsedLink = slider._parsePath(this.pathname);
                        if (parsedLink.section === current.section) {
                            current.index = i;
                            parsedLink.page = current.page;
                        }

                        var $this = $(this);
                        slider._sections[i] = {
                            name: parsedLink.section,
                            menuItem: $this,
                            activePage: parsedLink.page,
                            state: PageState.INIT,
                            updateDisplay: function(dist) { slider._updateSectionDisplay(this, dist); }
                        };
                        slider._sectionMap[parsedLink.section] = i;

                        // Also, add a span inside the link to make later calculations easier/cheaper
                        $this._measurer = $("<div>").css({ width: "100%", height: "0", position: "absolute" });
                        $this.css({ position: "relative" }).prepend($this._measurer);

                        // And set the style of the link to be the default, removing any classes
                        $this.removeClass().css(slider._options.styles.menuDefault);
                    });
                }
                else {
                    throw new Error("No links found");
                }

                // If the current page is not in the sections, then we're screwed with the whole slider thing
                // So just return here and forget about the rest
                if (current.index === -1) {
                    return;
                }

                this._pageWidth = this._container.width();

                this._slider = $("<div id=\"slider\">").css({
                    position: "absolute",
                    top: "0",
                    left: "0",
                    height: "100%"
                });
                this._sliderWrapper = $("<div id=\"sliderWrapper\">").css({
                    position: "relative",
                    overflow: "hidden"
                });
                this._sliderWrapper.width(this._getPageOuterWidth()).css("left", (-this._options.pageMargin) + "px");
                this._slider.width(this._getPageOuterWidth() * this._sections.length);

                var pageWrapperTemplate = $("<div>").width(this._pageWidth).css({
                    position: "absolute",
                    overflow: "hidden"
                }).css("top", 0);

                // Add a page wrapper to each section (and the slider), and load the pages by ajax
                jQuery.each(this._sections, function(i) {
                    var wrapper = pageWrapperTemplate.clone();
                    wrapper.css("left", i * slider._getPageOuterWidth() + slider._options.pageMargin);

                    this.pageWrapper = wrapper;
                    slider._slider.append(wrapper);
                    slider._loadPage(this.name, this.activePage);
                });


                // Clear the container, to make space for the slider
                this._container.empty();
                this._container.append(this._sliderWrapper.append(this._slider));


                // Create the underline that moves around on the menu
                this._menuUnderline = $("<div id=\"menuUnderline\">");
                this._menuUnderline.mousedown(function(e) {
                    var $this = $(this);
                    var posAlong = (e.pageX - $this.left()) / $this.width();
                    var dragfunc = function(e) {
                        var i = slider._getIndexFromUnderlinePos(e.pageX, posAlong);
                        slider._setSectionIndex(i);
                    };
                    $(document).mousemove(dragfunc);
                    $(document).mouseup(function(e) {
                        $(document).unbind("mousemove", dragfunc);
                        slider._snapToIndex();
                    });
                    e.preventDefault();
                });
                this._menu.append(this._menuUnderline);


                // Make all links attempt to use the slider first
                $("a").live("click", function(e) {
                    if (this.host === location.host) {
                        slider.goToPage(this.pathname);
                        e.preventDefault();
                    }
                });


                // Periodically check if the animator has updated, and update the display if it has
                setInterval(function() {
                    if (slider._animator.updated) {
                        slider._animator.updated = false;
                        slider._updateDisplay();
                    }
                }, 40);

                // Move off the edge of the sections...
                this._setSectionIndex(-1);

                // And jump to the current one, once it's loaded
                var curSection = this._sections[current.index];
                var tryJumpToCurrent = function() {
                    if (curSection.state !== PageState.LOADING && curSection.state !== PageState.INIT) {
                        slider.goToPage(location.hash.substring(1));
                        slider._menuUnderline.show(500);
                    }
                    else {
                        setTimeout(tryJumpToCurrent, 40);
                    }
                };
                this._menuUnderline.hide();
                tryJumpToCurrent();
            }
        });
    }


    Cufon.replace('#header #logo h1', { fontFamily: 'Myriad Pro', letterSpacing: "-5px" });
    Cufon.replace('#header #logo .subtitle', { fontFamily: 'Myriad Pro' });
    Cufon.replace('#header #menu li a', { fontFamily: 'Myriad Pro Cond' });
    Cufon.replace('#container #main h2', { fontFamily: 'Myriad Pro Cond' });
    $(document).ready(function() {
        Cufon.now();
    });

    var slider = new Controtex.PageSlider();
    slider.options({
        defaultSection: "Home",
        defaultPage: "Index",
        onRefresh: function() { Cufon.refresh(); }
    });

    $(document).ready(function() {
        slider.container("#main");
        slider.menu("#menu");
        slider.contentSelector("#content");

        slider.init();
    });



    /* ==================== *


    (function() {

        var clicking = false;

        var pages;
        var menuLinks;
        var slider, menuUnderline;

        var currentPageView = jQuery.extend($("<div>")[0], {
            index: -1,
            customAnimate: true
        });
        var currentHash;



        function createSlider(pages, menuLinks) {


            sliderWrapper.append(slider);

            sliderWrapper
    .width(pageWidth + 2 * pageMargin)
    .css("left", (-pageMargin) + "px");
            slider.width((pageWidth + 2 * pageMargin) * pages.length);

            var pageWrapper = $("<div>")
    .width(pageWidth)
    .css({
        "float": "left",
        "margin-left": pageMargin + "px",
        "margin-right": pageMargin + "px"
    });

            var index = 0;
            for (var i = 0; i < pages.length; i++) {
                (function(i) {
                    var page = pages[i];
                    if (!page) {
                        return;
                    }
                    if (currentPageView.pathname == page.pathname) {
                        currentPageView.index = index;
                    }

                    pages[index] = page;

                    $(page).removeAttr("id");
                    slider.append(pageWrapper.clone().append(page));

                    var closureIndex = index; // for closure
                    $(page.link).fadeIn(1000);

                    $(page.link).click(function(e) {
                        clicking = true;
                        e.preventDefault();

                        if (pages[currentPageView.index] && pages[currentPageView.index].pathname == this.pathname) {
                            return;
                        }
                        $(currentPageView).stop().animate({ "index": closureIndex }, {
                            duration: 1000,
                            easing: "easeInOutQuart",
                            queue: "false",
                            step: indexStep
                        });

                        location.hash = this.pathname;
                        currentHash = location.hash;
                        clicking = false;
                    });

                    index++;
                })(i)
            }
            pages.length = index;

            $("#menu").append(menuUnderline);

            main.append(sliderWrapper);
            sliderWrapper.height(slider.height());
            main.height(sliderWrapper.height());

            Cufon.refresh();

            var curIndex = currentPageView.index;
            currentPageView.index = -1;
            $(currentPageView).stop().animate({ "index": curIndex }, {
                duration: 1000,
                easing: "easeOutQuart",
                queue: "false",
                step: indexStep
            });
        }

        $(document).ready(function() {
            Cufon.now();

            var content = $("#content");
            $("#main").empty();

            currentPageView.pathname = location.pathname;

            pages = [];
            menuLinks = $("#menu > ul > li > a");
            menuLinks.hide();

            var pagesToGet = menuLinks.length;
            menuLinks.each(function(i) {
                var link = this;
                var pathname = this.pathname;

                var onSuccess = function(page) {
                    jQuery.extend(page, {
                        link: link,
                        pathname: pathname,
                        setDist: function(dist) {
                            var dist = Math.min(Math.abs(dist), 1);
                            $(this.link).css("color", getLinkCol(dist));
                            $(this).css("opacity", Math.max(1 - dist * 2, 0));
                            Cufon.refresh("#header #menu li a");
                        }
                    });
                    pages[i] = page;
                    if (--pagesToGet == 0) {
                        createSlider(pages, menuLinks);
                    }
                }

                if (pathname != location.pathname) {
                    if (!normCol) {
                        normCol = jQuery.getColor(this, "color");
                    }
                    jQuery.ajax({
                        url: pathname,
                        success: function(data, textStatus) {
                            onSuccess($("#content", $(data)));
                        },
                        error: function(XMLHttpRequest, textStatus, errorThrown) {
                            menuLinks = menuLinks.not(link);
                            if (--pagesToGet == 0) {
                                createSlider(pages, menuLinks);
                            }
                        },
                        dataType: "html"
                    });
                }
                else {
                    if (!selCol) {
                        selCol = jQuery.getColor(this, "color");
                    }
                    $(this).removeClass("selected");
                    onSuccess(content);
                }
            });

            setInterval(
    function() {
        if (clicking) {
            return;
        }
        if (!location.hash || currentHash == location.hash) {
            return;
        }
        currentHash = location.hash;

        var page = jQuery.grep(pages, function(x, i) { return x.link.pathname == location.hash.substring(1); })[0];
        if (page) {
            $(page.link).click();
        }
    }, 50);
        });

    })();// */
}