
/*
FILE CONCAT ADD FILE
PATH: /ft/resources/client/chart/Chart.js
*/
var Chart = function(args){

    this.init(args);
    
    Chart.Super(this);
};
Chart.Extend(Popup);

Chart.prototype.CSS_LINK = "interactiveChartLink";
Chart.prototype.ATTR_SYMBOL = "intchartsymbol";
Chart.prototype.ATTR_ISSUE_NAME = "issuename";
Chart.prototype.VIEWPORT_OFFSET = 17;

Chart.prototype.init = function(args){
    if (!args) {
        return;
    }
    
    this.elContainer = WSDOM.Element.get("interactiveChart");
    
    this.serializer = new Serializer();
    this.contentBuffer = new ContentBuffer();
    
    this.params = {
        timeframe: 365,
        interval: "auto",
        style: "line",
        scale: "linear",
        dividends: false,
        splits: false,
        earnings: false,
        ftNews: false,
        icNews: false,
        drawTransactions: false,
        upper: [],
        lower: [],
        index: []
    };
    
    this.settings = {
        type: "interactive",
        width: 618,
        yAxisHeightUpper: 230,
        yAxisHeightLower: 120,
        xAxisHeight: 36,
        chartOffset: 1,
        upperChartPadding: 6
    };
    
    
    this.eventManager = new EventManager();
    this.chartImage = new ChartImage();
    this.userStoredData;
    
    if (args) {
        for (var control in args) {
            for (var value in args[control]) {
            
                // this stuff needs to be refactored out to InteractiveChart.js
                if ("lower" == value) {
                    this[control][value] = [];
                    for (var i = 0; i < args[control][value].length; i++) {
                        this[control][value].push(CopyObj(this.lowerIndicators[args[control][value][i]]));
                    }
                }
                else 
                    if ("upper" == value) {
                        this[control][value] = [];
                        for (var i = 0, indicator; i < args[control][value].length; i++) {
                            var indicator = CopyObj(this.upperIndicators[args[control][value][i]]);
                            indicator.color.push(this.upperIndicatorColors.splice(0, 1)[0]);
                            this[control][value].push(indicator);
                        }
                    }
                    else {
                        this[control][value] = args[control][value]
                    }
            }
        }
    }
}

Chart.prototype.getAvailableComparisons = function(){
    return null;
};

Chart.prototype.setAvailableComparisons = function(comparisons){
    comparisons = this.serializer.deserialize(comparisons);
    
    this.getAvailableComparisons = function(){
        return comparisons;
    }
};

Chart.prototype.getUserStoredData = function(){
    return null;
};

Chart.prototype.setUserStoredData = function(data){
    data = this.serializer.deserialize(data);
    
    this.getUserStoredData = function(){
        return data;
    }
};

Chart.prototype.setParameters = function(params){
    for (var i in params) {
        this.params[i] = params[i];
    }
}

Chart.prototype.setSettings = function(settings){
    for (var i in settings) {
        this.settings[i] = settings[i];
    }
}


Chart.prototype.initializeColors = function(){
    this.upperIndicatorColors = ["#ec870e", "#19c7d2", "#7c7c7c", "#ab04ff", "#f61812", "#2453cc", "#f95aca", "#e5ce39"];
    this.indexIndicatorColors = ["#0e8686", "#333333", "#00b104", "#927366"];
}

Chart.prototype.setSymbol = function(symbol){
    if (!symbol) {
        return;
    }
    
    this.setSettings({
        symbol: symbol
    });
};

Chart.prototype.setIssueName = function(issueName){
    if (!issueName) {
        return;
    }
    
    this.setTitleText('INTERACTIVE CHARTING  <span class="name">' + issueName + ' ' + (this.settings.symbol || ''));
};

Chart.prototype.toggleTimeframe = function(timeframe){
    var selectTimeframeOptions = WSDOM.Element.parseSelector("option[value='" + timeframe + "']", "selectTimeframe", "first");
    if (selectTimeframeOptions) {
        selectTimeframeOptions.selected = true;
    }
}

Chart.prototype.setTimeframe = function(e, el, data){
    this.params.timeframe = data.timeframe || el.getAttribute("timeframe") || el.value;
    this.params.timeframe = this.params.timeframe == "ytd" ? "ytd" : Number(this.params.timeframe);
    if (this.settings.type == "performance" && !this.settings.isCommodity && !this.settings.isIndex && !this.settings.isCurrency) {
        var chartAdditionalNews = $ws('.chartAdditionalNews');
        var chartNewsControls = $ws('.chartNewsControls');
        $ws(chartAdditionalNews).addClass('wsodHidden');
        var chartAdditionalNews = WSDOM.Element.parseSelector("a.chartAdditionalNews", "performanceChartControls", "first");
        if (chartAdditionalNews && this.params.timeframe > 5 && WSDOM.Element.hasClass(chartAdditionalNews, this.CSS_HIDDEN)) {
            WSDOM.Element.addClass(chartNewsControls, this.CSS_HIDDEN);
            WSDOM.Element.removeClass(chartAdditionalNews, this.CSS_HIDDEN);
        }
        else 
            if (chartNewsControls && this.params.timeframe <= 5 && WSDOM.Element.hasClass(chartNewsControls, this.CSS_HIDDEN)) {
                WSDOM.Element.removeClass(chartNewsControls, this.CSS_HIDDEN);
                WSDOM.Element.addClass(chartAdditionalNews, this.CSS_HIDDEN);
            }
    }
    
    if (WSDOM.Element.get("timeframeControlsContainer")) {
        WSDOM.Element.removeClass(WSDOM.Element.parseSelector("li", "timeframeControlsContainer"), "active");
        if (data && data.timeframe && this.params.zoom) {
            WSDOM.Element.addClass(WSDOM.Element.parseSelector("li.customRange", "timeframeControlsContainer", "first"), "active");
        }
        else {
            WSDOM.Element.addClass(WSDOM.Element.parseSelector("li[timeframe='" + this.params.timeframe + "']", "timeframeControlsContainer", "first"), "active");
        }
    }
    
    if (!(data && data.timeframe)) {
        this.params.zoom = false;
        this.params.startDate = 0;
        this.params.endDate = 0;
    }
    
    if (data && !data.preventUpdate) {
        this.getAdvancedChart("timeframe", data.getSupplementalData);
    }
    
    window.focus();
}


//build buffer parameters, make function call for buffer load for upper chart
Chart.prototype.getAdvancedChart = function(type, getSupplementalData){
    this.setLoadingState(true);
    
    //cancel previous upper chart request
    this.closeDialog();
    
    this.disableRollover();
    
    this.chartImage.contentBuffer.abortRequests();
    
    this.settings.height = (this.settings.yAxisHeightUpper + this.settings.upperChartPadding) + (this.params.lower.length * this.settings.yAxisHeightLower) + (this.params.lower.length * this.settings.chartOffset) + 1;
    
    var returns = ["fileName:File.Name", "eventPoints:eventPoints", "rangeExport:rangeExport", "datesExport:datesExport", "chartCoords:chartCoords", "timeStamp:timeStamp", "timezoneDelta:timezoneDelta"];
    
    this.chartImage.load({
        type: this.settings.type,
        settings: this.settings,
        params: this.params,
        returns: returns.join(","),
        callback: this.setupAdvancedChart,
        getSupplementalData: getSupplementalData,
        context: this//,
        //validateSymbol: this.validateSymbol 
    });
}

Chart.prototype.setupAdvancedChart = function(){
    if (this.chartImage.settings) {
    
        if (!this.chartImage.settings.FTStandard) {
            // symbol is invalid
            //this.setTitleText("INTERACTIVE CHARTING: Error - " + this.settings.symbol + " not found.")
            this.setIssueName(" ");
            this.popDialog(null, null, {
                type: "error",
                text: "Unable to draw chart. Symbol " + this.settings.symbol + " not found."
            });
        }
        else {
            this.setSymbol(this.chartImage.settings.FTStandard);
            this.setIssueName(this.chartImage.settings.issueName);
        }
    }
    
    WSDOM.Element.setHeight(WSDOM.Element.parseSelector("img.chartImage", this.settings.type + "ChartImageContainer"), this.settings.height);
    
    if ("interactive" == this.settings.type) {
        this.enableRollover();
    }
    
    this.chartImage.eventPoints = this.expandEventCoords(this.chartImage.eventPoints);
    if ("interactive" == this.settings.type) {
        this.chartImage.coords = this.expandChartCoords(this.chartImage.chartCoords);
    }
    
    if (WSDOM.Element.get(this.settings.type + "ChartEvents")) {
        WSDOM.Element.remove(this.settings.type + "ChartEvents");
    }
    
    this.settings.staticEventBubbles = ("performance" == this.settings.type && this.params.timeframe <= 5) ? true : false;
    this.drawEventIcons(this.chartImage.eventPoints);
    
    this.setLoadingState(false);
    //if ("interactive" == this.settings.type) { this.drawIndicatorHeaders(); }
}

Chart.prototype.setStyle = function(e, el, data){
    if (!el.value) {
        return;
    }
    
    this.params.style = el.value;
    
    this.getAdvancedChart("style");
    
    window.focus();
}

Chart.prototype.addIndicator = function(e, el, data){
    e.cancel();
    
    if (!el.value) {
        return;
    }
    
    if (this.upperIndicators[el.value]) {
        this.addUpperIndicator(el.value);
    }
    else {
        this.addLowerIndicator(el.value);
    }
    
    el.selectedIndex = 0;
    
    window.focus();
}

Chart.prototype.addUpperIndicator = function(indicator){
    if (this.params.upper.length < this.maxUpperIndicators) {
        var indicator = CopyObj(this.upperIndicators[indicator]);
        
        indicator.color.push(this.upperIndicatorColors.splice(0, 1)[0]);
        this.params.upper.push(indicator);
        
        this.getAdvancedChart("upperAdd");
    }
    else {
        alert("Maximum number of upper indicators reached.");
        //		this.popDialog(null, null, {title: "Chart Error", errorText: "You can use up to " + this.stringNumber[this.maxUpperIndicators] + " upper indicators. To add another upper indicator, please first remove one from the price performance chart."});
    }
}

Chart.prototype.addLowerIndicator = function(indicator){
    if (this.params.lower.length < this.maxLowerIndicators) {
        var indicator = CopyObj(this.lowerIndicators[indicator]);
        
        this.params.lower.push(indicator);
        
        //		this.drawLowerIndicatorContainer(indicator, this.params.lower.length-1);
        
        this.getAdvancedChart("lowerAdd");
    }
    else {
        alert("Maximum number of lower indicators reached.");
        //		this.popDialog(null, null, {title: "Chart Error", errorText: "You are limited to " + this.maxLowerIndicators + " lower indicators. To view an additional indicator, please remove one lower indicator from the price performance chart."});
    }
}

Chart.prototype.addComparison = function(e, el, data){
    e.cancel();
    
    if (!el.value) {
        return;
    }
    
    if (this.params.index.length < this.maxComparisons) {
    
        this.params.index.push({
            index: unescape(el.value),
            title: this.getAvailableComparisons()[el.value].label,
            color: this.indexIndicatorColors.splice(0, 1)
        })
        
        this.getAdvancedChart("comparisonAdd");
    }
    else {
        alert("Maximum number of comparisons reached.");
        //		this.popDialog(null, null, {title: "Chart Error", errorText: "You are limited to " + this.maxComparisons + " comparisons. To view an additional comparison, please remove one comparison from the price performance chart."});
    }
    
    el.selectedIndex = 0;
    
    window.focus();
}

Chart.prototype.addEvent = function(e, el, data){
    e.cancel();
    
    if (!el.value) {
        return;
    }
    
    this.params[el.value] = true;
    
    this.getAdvancedChart("eventAdd");
    
    el.selectedIndex = 0;
    
    window.focus();
}

Chart.prototype.toggleEvent = function(e, el, data){
    e.cancel();
    
    WSDOM.Element.toggleClass(el, "selectedButtonGray");
    
    if (this.params[data.event]) {
        this.params[data.event] = false;
    }
    else {
        this.params[data.event] = true;
    }
    
    this.getAdvancedChart("eventToggle");
}

Chart.prototype.drawEventIcons = function(eventPoints){
    var ths = this;
    if (eventPoints.length) {
        var chartEvents = WSDOM.Element.create("div", {
            id: this.settings.type + "ChartEvents"
        }, null, WSDOM.Element.get(this.settings.type + "ChartImageContainer"));
        
        this.map = WSDOM.Element.get('interactiveChartEvents') || false;
        
        for (var i = 0, eventIcon, eventClass, prevPoint; i < eventPoints.length; i++) {
            if (this.chartImage.eventPoints[i].type != "undefined") {
                eventClass = "icon-chart-" + eventPoints[i].type;
                
                if ("splits" == eventPoints[i].type) {
                    eventClass = "icon-chart-split";
                }
                else 
                    if ("dividends" == eventPoints[i].type) {
                        eventClass = "icon-chart-dividend";
                    }
                
                if (prevPoint != eventPoints[i].x0) {
                    eventIcon = WSDOM.Element.create("div", {
                        'class': 'eventIcon icon ' + eventClass
                    }, null, chartEvents);
                    
                    if (this.map && ('ftNews' == eventPoints[i].type || 'icNews' == eventPoints[i].type)) {
                        // Center the icon, instead of having the top-left corner positioned at 0,0
                        eventPoints[i].x0 = eventPoints[i].x0 - 9; // FT icon - width = 18px
                        eventPoints[i].y0 = eventPoints[i].y0 - 8; // FT icon - height = 16px
                    }
                    WSDOM.Element.setXY(eventIcon, eventPoints[i].x0, eventPoints[i].y0);
                    var eventMouseOver = this.eventManager.add({
                        element: eventIcon,
                        type: "mouseover",
                        context: this,
                        handler: this.showEvent,
                        data: eventPoints[i]
                    });
                    
                    if (this.settings.staticEventBubbles) {
                        eventMouseOver.fire();
                    }
                    prevPoint = eventPoints[i].x0;
                }
            }
        }
        
        if (this.map) {
            this.areas = WSDOM.Element.parseSelector('div.icon-chart-ftNews,div.icon-chart-icNews', this.map);
            
            if (this.areas && this.areas.length) {
            
                if (!this.hover) {
                
                    this.hover = new MouseHover();
                    this.hover.CSS_MOUSE_HOVER = "interactiveChartMouseHover mouseHover";
                    this.hover.MARGIN = 0;
                    this.hover.isMovable(false);
                    
                    WSDOM.Element.create('div', {
                        'class': 'arrowIcon'
                    }, null, this.hover.getContainer());
                    
                    this.hover.move = function(e){
                        var iconPos = WSDOM.Element.getXY(this.getCurrentTarget());
                        var viewportX = (WSDOM.Element.getViewport().width)
                        var elDiff = (viewportX - iconPos.x)
                        var elWidth = (Element.getSize(this.getContainer()).width) + ths.VIEWPORT_OFFSET
                        //console.log(elWidth)
                        //console.log(ths.VIEWPORT_OFFSET)
                        var shiftNum = (elWidth - elDiff)
                        
                        if (shiftNum > 0) {
                            WSDOM.Element.setXY(this.getContainer(), (iconPos.x - shiftNum), iconPos.y)
                        }
                        else {
                            WSDOM.Element.setXY(this.getContainer(), iconPos.x, iconPos.y)
                        }
                    };
                }
                
                //this.hover.getMouseOverEvent().removeAllElements();
                this.hover.addTarget(this.areas);
                
            }
        }
    }
}

Chart.prototype.drawIndicatorHeaders = function(){
    var chartImageContainer = WSDOM.Element.get(this.settings.type + "ChartImageContainer");
    
    WSDOM.Element.remove(WSDOM.Element.parseSelector("div.indicatorHeaderContainer", chartImageContainer));
    
    //	var indicatorHeaderContainer = WSDOM.Element.create("div", {'class':'indicatorHeaderContainer'}, null, chartImageContainer);
    var indicatorHeaderContainer = WSDOM.Element.get("chartKeyContainer");
    WSDOM.Element.removeChildNodes(indicatorHeaderContainer)
    //console.log(this.params);
    for (var i = 0, indicatorHeaderModule; i < this.params.upper.length; i++) {
        indicatorHeaderModule = WSDOM.Element.create("div", {
            'class': 'indicatorModule',
            'indicatorType': 'upper',
            'style': 'color:' + this.params.upper[i].color
        }, [WSDOM.Element.create("span", {
            'class': ''
        }, this.params.upper[i].title + ((this.params.upper[i].options.values.length) ? " (" + this.params.upper[i].options.values.join(", ") + ")" : "") + "&nbsp;&nbsp;"), WSDOM.Element.create("a", {
            'Events': [{
                type: "click",
                context: this,
                handler: this.popDialog,
                data: {
                    type: 'upper'
                }
            }]
        }, "Edit"), WSDOM.Element.create("span", {}, "&nbsp;|&nbsp"), WSDOM.Element.create("a", {
            'Events': [{
                type: "click",
                context: this,
                handler: this.removeUpperIndicator
            }]
        }, "Remove")], indicatorHeaderContainer);
    }
    
    for (var i = 0, indicatorHeaderModule; i < this.params.index.length; i++) {
        indicatorHeaderModule = WSDOM.Element.create("div", {
            'class': 'indicatorModule',
            'indicatorType': 'comparison',
            'style': 'color:' + this.params.index[i].color
        }, [WSDOM.Element.create("span", {
            'class': ''
        }, this.params.index[i].title + "&nbsp;&nbsp;"), WSDOM.Element.create("a", {
            'Events': [{
                type: "click",
                context: this,
                handler: this.removeComparison
            }]
        }, "Remove")], indicatorHeaderContainer);
    }
    
    var events = []
    if (this.params.earnings) {
        events.push({
            name: "earnings",
            value: "earnings",
            icon: "icon-chart-earnings",
            color: "#66CC66"
        });
    }
    if (this.params.dividends) {
        events.push({
            name: "dividends",
            value: "dividends",
            icon: "icon-chart-dividend",
            color: "#006699"
        });
    }
    if (this.params.splits) {
        events.push({
            name: "splits",
            value: "splits",
            icon: "icon-chart-split",
            color: "#FF6633"
        });
    }
    if (this.params.ftNews) {
        events.push({
            name: "Financial Times News",
            value: "ftNews",
            icon: "icon-chart-ftNews",
            color: "#000000"
        });
    }
    if (this.params.icNews) {
        events.push({
            name: "Investors Chronicle News",
            value: "icNews",
            icon: "icon-chart-icNews",
            color: "#000000"
        });
    }
    for (var i = 0, indicatorHeaderModule, indicatorBoxWidth = 0; i < events.length; i++) {
        indicatorHeaderModule = WSDOM.Element.create("div", {
            'class': 'indicatorModule',
            'indicatorType': 'event',
            'style': 'color:' + events[i].color
        }, [WSDOM.Element.create("div", {
            'class': 'icon ' + events[i].icon
        }), WSDOM.Element.create("span", {
            'class': ''
        }, events[i].name.substring(0, 1).toUpperCase() + events[i].name.substring(1, events[i].name.length) + "&nbsp;&nbsp;"), WSDOM.Element.create("a", {
            'event': events[i].value,
            'Events': [{
                type: "click",
                context: this,
                handler: this.removeEvent
            }]
        }, "Remove")], indicatorHeaderContainer);
    }
    
    
    for (var i = 0, indicatorHeaderModule; i < this.params.lower.length; i++) {
        indicatorHeaderContainer = WSDOM.Element.create("div", {
            'class': 'indicatorHeaderContainer'
        }, null, chartImageContainer);
        indicatorHeaderModule = WSDOM.Element.create("div", {
            'class': 'indicatorModule',
            'indicatorType': 'lower'
        }, [WSDOM.Element.create("span", {
            'class': ''
        }, this.params.lower[i].title + ((this.params.lower[i].options.values.length) ? " (" + this.params.lower[i].options.values.join(", ") + ")" : "") + "&nbsp;&nbsp;"), WSDOM.Element.create("a", {
            'Events': [{
                type: "click",
                context: this,
                handler: this.popDialog,
                data: {
                    type: 'lower'
                }
            }]
        }, "Edit"), WSDOM.Element.create("span", {}, "&nbsp;|&nbsp"), WSDOM.Element.create("a", {
            'Events': [{
                type: "click",
                context: this,
                handler: this.removeLowerIndicator
            }]
        }, "Remove")], indicatorHeaderContainer);
        
        WSDOM.Element.setXY(indicatorHeaderContainer, null, ((this.settings.yAxisHeightLower + this.settings.chartOffset) * i) + (this.settings.yAxisHeightUpper + this.settings.upperChartPadding * 2));
    }
}

Chart.prototype.removeUpperIndicator = function(e, el, data){
    var indicatorModule = WSDOM.Element.getParentBySelector(el, "div.indicatorModule");
    WSDOM.Element.addClass(indicatorModule, "remove")
    
    var allIndicatorModules = WSDOM.Element.parseSelector('div[indicatorType="upper"]', 'chartKeyContainer');
    for (var i = 0; i < allIndicatorModules.length; i++) {
        if (WSDOM.Element.hasClass(allIndicatorModules[i], "remove")) {
            this.upperIndicatorColors.push(this.params.upper[i].color[0])
            this.params.upper.splice(i, 1);
        }
    }
    
    WSDOM.Element.remove(indicatorModule)
    
    this.getAdvancedChart('upperRemove');
}

Chart.prototype.removeLowerIndicator = function(e, el, data){
    var indicatorModule = WSDOM.Element.getParentBySelector(el, "div.indicatorModule");
    WSDOM.Element.addClass(indicatorModule, "remove")
    
    var allIndicatorModules = WSDOM.Element.parseSelector('div[indicatorType="lower"]', this.settings.type + 'ChartImageContainer');
    for (var i = 0; i < allIndicatorModules.length; i++) {
        if (WSDOM.Element.hasClass(allIndicatorModules[i], "remove")) {
            this.params.lower.splice(i, 1);
        }
    }
    
    this.getAdvancedChart('lowerRemove');
}

Chart.prototype.removeComparison = function(e, el, data){
    var indicatorModule = WSDOM.Element.getParentBySelector(el, "div.indicatorModule");
    WSDOM.Element.addClass(indicatorModule, "remove")
    
    var allIndicatorModules = WSDOM.Element.parseSelector('div[indicatorType="comparison"]', 'chartKeyContainer');
    for (var i = 0; i < allIndicatorModules.length; i++) {
        if (WSDOM.Element.hasClass(allIndicatorModules[i], "remove")) {
            this.indexIndicatorColors.push(this.params.index[i].color[0])
            this.params.index.splice(i, 1);
        }
    }
    
    WSDOM.Element.remove(indicatorModule)
    
    this.getAdvancedChart('comparisonRemove');
}

Chart.prototype.removeEvent = function(e, el, data){
    var indicatorModule = WSDOM.Element.getParentBySelector(el, "div.indicatorModule");
    
    this.params[el.getAttribute("event")] = false;
    
    WSDOM.Element.remove(indicatorModule);
    
    this.getAdvancedChart('eventRemove');
}

Chart.prototype.expandEventCoords = function(flat){
    if (flat) {
        var out = [];
        var obj = flat.split("|");
        
        for (var x = 0, len = obj.length; x < len; x++) {
            obj[x] = obj[x].split("*");
        }
        
        for (var x = 0, len = obj.length; x < len; x++) {
            //console.log(obj[x]);			
            out[x] = {};
            
            out[x].type = obj[x][0];
            out[x].date = obj[x][1];
            out[x].msDate = obj[x][2];
            /*
             out[x].value = obj[x][3];
             out[x].x0 = parseFloat(obj[x][4]);
             out[x].y0 = parseFloat(obj[x][5]);
             out[x].width = parseFloat(obj[x][6]);
             if (obj[x][7]) { out[x].url = obj[x][7]; }
             */
            out[x].time = obj[x][3];
            out[x].value = obj[x][4];
            out[x].x0 = parseFloat(obj[x][5]);
            out[x].y0 = parseFloat(obj[x][6]);
            out[x].width = parseFloat(obj[x][7]);
            if (obj[x][8]) {
                out[x].url = obj[x][8];
            }
            if (obj[x][9]) {
                out[x].currency = obj[x][9];
            }
        }
        
        return out;
    }
    
    return [];
}

Chart.prototype.expandChartCoords = function(flat){
    this.rolloverCoords = [];
    var lastPoint = 0;
    
    if (flat) {
        var obj = flat.split("|");
        
        var out = [];
        
        for (var x = 0, len = obj.length; x < len; x++) {
            obj[x] = obj[x].split("*");
        }
        
        for (var x = 0, len = obj.length; x < len; x++) {
        
            out[x] = {}
            
            out[x].date = obj[x][0];
            out[x].open = obj[x][1];
            out[x].high = obj[x][2];
            out[x].low = obj[x][3];
            out[x].close = obj[x][4];
            out[x].x0 = parseInt(obj[x][5]);
            for (var i = lastPoint; i < out[x].x0; i++) {
                if (x == 0) {
                    this.rolloverCoords[i] = x;
                }
                else {
                    this.rolloverCoords[i] = x - 1;
                }
            }
            lastPoint = i;
            out[x].y0 = parseInt(obj[x][6]);
            out[x].volume = obj[x][7];
            //out[x].lower = (obj[x][7]) ? obj[x][7].split("<>") : []; 
        
        }
        
        this.rolloverCoords[i - 2] = x - 1;
        this.rolloverCoords[i - 1] = x - 1;
        this.rolloverCoords[i] = x - 1;
    }
    
    return out || [];
}

Chart.prototype.subscribePopup = function(){
    var outerContainer = (WSDOM.Element.get("interactiveChartImageContainer")) ? WSDOM.Element.get("interactiveChartImageContainer") : WSDOM.Element.get("wsodPop"), sAlertsOrChart = (WSDOM.Element.get("interactiveChartImageContainer")) ? 'chart' : 'alert';
    
    var dialogTitle = SiteRules.isInvestorsChronicle() ? "LOG IN TO INVESTORS CHRONICLE" : "LOG IN TO FT", dialogText = "Please log in to save your " + sAlertsOrChart + " settings. If you are not currently registered for this site, these are some of the benefits of registration:", dialogInputItems = "&bull; Price and portfolio email alerts<br/>&bull; Portfolio tool", dialogActionButton = WSDOM.Element.create("div", {
        'class': 'basicButton',
        'Events': [{
            type: "click",
            context: this,
            handler: function(){
                window.location = window.REGISTRATIONURL + encodeURIComponent(window.location);
            },
            data: {
                type: "saveChartParams"
            }
        }]
    }, [WSDOM.Element.create("span", {}, "Register &amp Log In")])
    
    var p = this.getLoadPopup();
    p.close();
    p.clearContent();
    
    WSDOM.Element.create("form", {
        'id': 'formDialog',
        'Events': [{
            type: "submit",
            handler: function(e){
                e.cancel();
                return;
            }
        }]
    }, [WSDOM.Element.create("div", {
        'class': 'dialogContainer'
    }, [WSDOM.Element.create("div", {
        'class': 'dialogHeader'
    }, [WSDOM.Element.create("div", {
        'class': 'dialogTitle'
    }, dialogTitle), WSDOM.Element.create("div", {
        'class': 'icon icon-delete-close',
        'Events': [{
            type: "click",
            context: this,
            handler: this.closeDialog
        }]
    }), WSDOM.Element.create("div", {
        'class': 'dialogClose',
        'Events': [{
            type: "click",
            context: this,
            handler: this.closeDialog
        }]
    }, "Close")]), WSDOM.Element.create("div", {
        'class': 'dialogBody'
    }, dialogText), WSDOM.Element.create("br", {
        'class': 'clear'
    }), WSDOM.Element.create("div", {
        'class': 'dialogInputs'
    }, dialogInputItems), WSDOM.Element.create("br", {
        'class': 'clear'
    }), WSDOM.Element.create("div", {
        'class': 'dialogButtons'
    }, [dialogActionButton, WSDOM.Element.create("div", {
        'class': 'basicButton icButtonCancel',
        'Events': [{
            type: "click",
            context: this,
            handler: this.closeDialog
        }]
    }, [WSDOM.Element.create("span", {}, "Cancel")])])])], outerContainer);
}

Chart.prototype.popDialog = function(e, el, data){

    if (INFOID && INFOID == 3) { //Check to see if the user is logged in...
        this.subscribePopup();
        
    }
    else {
    
        var interactiveChart = WSDOM.Element.get("interactiveChart");
        if ("upper" == data.type || "lower" == data.type) {
            var instance = 0;
            var currentModule = WSDOM.Element.getParentBySelector(el, "div.indicatorModule");
            WSDOM.Element.addClass(currentModule, "active");
            
            var indicatorModules = WSDOM.Element.parseSelector("div.indicatorModule", (("upper" == data.type) ? "chartKeyContainer" : "interactiveChartImageContainer"));
            for (var i = 0; i < indicatorModules.length; i++) {
                if (WSDOM.Element.hasClass(indicatorModules[i], "active")) {
                    instance = i;
                }
            }
            
            WSDOM.Element.removeClass(currentModule, "active");
            var dialogTitle = this.params[data.type][instance].title;
            var dialogText = this.params[data.type][instance].text;
            for (var i = 0, dialogInputItems = []; i < this.params[data.type][instance].options.values.length; i++) {
                dialogInputItems.push(WSDOM.Element.create("div", {
                    'class': 'dialogInputItem'
                }, [WSDOM.Element.create("div", {}, this.params[data.type][instance].options.labels[i]), WSDOM.Element.create("input", {
                    'value': this.params[data.type][instance].options.values[i],
                    'class': 'indicatorInput'
                })]))
            }
            var dialogActionButton = WSDOM.Element.create("div", {
                'class': 'basicButton',
                'Events': [{
                    type: "click",
                    context: this,
                    handler: this.updateIndicator,
                    data: {
                        type: data.type,
                        instance: instance
                    }
                }]
            }, [WSDOM.Element.create("span", {}, "Update Indicator")]);
        }
        else 
            if ("manageSetting" == data.type) {
                var dialogTitle = "Manage Settings";
                var dialogText = "You can delete saved settings by checking the box next to those you do not want and clicking the <strong>Delete Settings</strong> button.";
                var dialogActionButton = WSDOM.Element.create("div", {
                    'class': 'basicButton',
                    'Events': [{
                        type: "click",
                        context: this,
                        handler: this.deleteSetting,
                        data: {
                            type: "saveChartParams"
                        }
                    }]
                }, [WSDOM.Element.create("span", {}, "Delete Settings")])
                for (var i = 0, settings = WSDOM.Element.parseSelector('div.loadoption a', WSDOM.Element.get('chartSettingsPopup')), dialogInputItems = []; i < settings.length; i++) {
                    if (settings[i].getAttribute('value') != "manageSetting") {
                        dialogInputItems.push(WSDOM.Element.create("div", {
                            'class': 'dialogInputItem' + ((dialogInputItems.length == 0) ? ' first' : '')
                        }, [WSDOM.Element.create("input", {
                            'type': 'checkbox',
                            'value': settings[i].getAttribute('value')
                        }), WSDOM.Element.create("div", {
                            'class': 'checkboxLabel ' + ((this.defaultSave == settings[i].value) ? "default" : "")
                        }, settings[i].innerHTML)]))
                    }
                }
                var p = this.getLoadPopup();
                p.close();
                p.clearContent();
            }
            else 
                if ("manageChart" == data.type) {
                    var dialogTitle = "Manage Charts";
                    var dialogText = "You can delete saved charts by checking the box next to those you do not want and clicking the <strong>Delete Charts</strong> button.";
                    var dialogActionButton = WSDOM.Element.create("div", {
                        'class': 'basicButton',
                        'Events': [{
                            type: "click",
                            context: this,
                            handler: this.deleteSetting,
                            data: {
                                type: "saveChartSettings"
                            }
                        }]
                    }, [WSDOM.Element.create("span", {}, "Delete Charts")])
                    for (var i = 0, settings = WSDOM.Element.parseSelector('div.loadoption a', WSDOM.Element.get('chartSettingsPopup')), dialogInputItems = []; i < settings.length; i++) {
                        if (settings[i].getAttribute('value') != "manageChart") {
                            dialogInputItems.push(WSDOM.Element.create("div", {
                                'class': 'dialogInputItem' + ((dialogInputItems.length == 0) ? ' first' : '')
                            }, [WSDOM.Element.create("input", {
                                'type': 'checkbox',
                                'value': settings[i].getAttribute('value')
                            }), WSDOM.Element.create("div", {
                                'class': 'checkboxLabel ' + ((this.defaultSave == settings[i].value) ? "default" : "")
                            }, settings[i].innerHTML)]))
                        }
                    }
                    var p = this.getLoadPopup();
                    p.close();
                    p.clearContent();
                }
                else 
                    if ("saveSetting" == data.type) {
                        var dialogTitle = "Save Settings";
                        var dialogText = "You can save your chart settings by typing a name in the box below and clicking the <strong>Save Settings</strong> button.";
                        var dialogActionButton = WSDOM.Element.create("div", {
                            'class': 'basicButton',
                            'Events': [{
                                type: "click",
                                context: this,
                                handler: this.saveSetting,
                                data: {
                                    type: "saveChartParams"
                                }
                            }]
                        }, [WSDOM.Element.create("span", {}, "Save Settings")])
                        var dialogInputItems = [WSDOM.Element.create("div", {}, "Name chart settings"), WSDOM.Element.create("input", {})];
                        var dialogOptions = [WSDOM.Element.create("div", {}, "Make default:"), WSDOM.Element.create("input", {
                            'type': 'checkbox',
                            className: "noBorder"
                        })]
                    }
                    else 
                        if ("saveChart" == data.type) {
                            var dialogTitle = "Save Chart";
                            var dialogText = "You can save your chart you are currently viewing by typing a name in the box below and clicking the <strong>Save Chart</strong> button.";
                            var dialogActionButton = WSDOM.Element.create("div", {
                                'class': 'basicButton',
                                'Events': [{
                                    type: "click",
                                    context: this,
                                    handler: this.saveSetting,
                                    data: {
                                        type: "saveChartSettings"
                                    }
                                }]
                            }, [WSDOM.Element.create("span", {}, "Save Chart")]);
                            var dialogOptions = [WSDOM.Element.create("div", {}, "Name chart"), WSDOM.Element.create("input", {})];
                        }
                        else 
                            if ("error" == data.type) {
                                var dialogTitle = "Chart Error";
                                var dialogText = data.text;
                            }
        
        WSDOM.Element.create("form", {
            'id': 'formDialog',
            'Events': [{
                type: "submit",
                handler: function(e){
                    e.cancel();
                    return;
                }
            }]
        }, [WSDOM.Element.create("div", {
            'class': 'dialogContainer'
        }, [WSDOM.Element.create("div", {
            'class': 'dialogHeader'
        }, [WSDOM.Element.create("div", {
            'class': 'dialogTitle'
        }, dialogTitle), WSDOM.Element.create("div", {
            'class': 'icon icon-delete-close',
            'Events': [{
                type: "click",
                context: this,
                handler: this.closeDialog
            }]
        }), WSDOM.Element.create("div", {
            'class': 'dialogClose',
            'Events': [{
                type: "click",
                context: this,
                handler: this.closeDialog
            }]
        }, "Close")]), WSDOM.Element.create("div", {
            'class': 'dialogBody'
        }, dialogText), WSDOM.Element.create("br", {
            'class': 'clear'
        }), WSDOM.Element.create("div", {
            'class': 'dialogInputs'
        }, dialogInputItems), WSDOM.Element.create("div", {
            'class': 'dialogOptions'
        }, (dialogOptions) ? dialogOptions : ""), WSDOM.Element.create("br", {
            'class': 'clear'
        }), WSDOM.Element.create("div", {
            'class': 'dialogButtons'
        }, [dialogActionButton, WSDOM.Element.create("div", {
            'class': 'basicButton icButtonCancel',
            'Events': [{
                type: "click",
                context: this,
                handler: this.closeDialog
            }]
        }, [WSDOM.Element.create("span", {}, "Cancel")])])])], WSDOM.Element.get("interactiveChartImageContainer"));
        
    }
    
    WCH.Apply(WSDOM.Element.parseSelector("div.dialogContainer", "formDialog", "first"), WSDOM.Element.get("formDialog"), false);
}

Chart.prototype.closeDialog = function(e, el, data){
    var customTimeframe = WSDOM.Element.get("div-CustomTimeframe");
    if (customTimeframe && customTimeframe.style.display == "block") {
        WSDOM.Element.setDisplay(customTimeframe, "none");
        WCH.Discard(customTimeframe);
    }
    if (WSDOM.Element.get("formDialog")) {
        WSDOM.Element.remove("formDialog");
    }
}

Chart.prototype.popZoomControls = function(e, el, data){
    e.cancel();
    
    //			var links = WSDOM.WSDOM.Element.parseSelector("a", WSDOM.WSDOM.Element.get("div-chartTabs"));
    //			WSDOM.WSDOM.Element.removeClass(links, "selected");
    //			WSDOM.WSDOM.Element.addClass(el, "selected");
    
    var container = WSDOM.Element.get("div-CustomTimeframe");
    
    if (!this.calendar) {
        WSDOM.Element.removeChildNodes("div-CalendarBegin");
        WSDOM.Element.removeChildNodes("div-CalendarEnd");
        
        this.calendarLeft = new Calendar();
        this.calendarLeft.draw(WSDOM.Element.get("div-CalendarBegin"));
        WSDOM.Element.addClass(WSDOM.Element.parseSelector("TD.today", WSDOM.Element.get("div-CalendarBegin"), "first"), "selected");
        this.calendarLeft.subscribeSelectHandler(function(e, el){
            WSDOM.Element.addClass(el, "selected");
        });
        
        this.calendarRight = new Calendar();
        this.calendarRight.draw(WSDOM.Element.get("div-CalendarEnd"));
        WSDOM.Element.addClass(WSDOM.Element.parseSelector("TD.today", WSDOM.Element.get("div-CalendarEnd"), "first"), "selected");
        this.calendarRight.subscribeSelectHandler(function(e, el){
            WSDOM.Element.addClass(el, "selected");
        });
        
        this.eventManager.add({
            element: WSDOM.Element.get("applyButton"),
            type: "click",
            context: this,
            handler: this.setZoom
        });
    }
    
    this.calendar = true;
    
    if (container.style.display != "block") {
        WSDOM.Element.setDisplay(container, "block");
        WCH.Apply(WSDOM.Element.get("div-CustomTimeframe"), WSDOM.Element.get("interactiveChartContent"), false);
    }
    else {
        WSDOM.Element.setDisplay(container, "none");
        WCH.Discard(WSDOM.Element.get("div-CustomTimeframe"));
    }
    
    //applyMouseovers();
}

Chart.prototype.setZoom = function(e, el, data){
    e.cancel();
    
    var year = WSDOM.Element.parseSelector("select.select-year", "div-CalendarBegin", "first").value;
    var month = WSDOM.Element.parseSelector("select.select-month", "div-CalendarBegin", "first").value;
    var day = WSDOM.Element.parseSelector("td.selected", "div-CalendarBegin", "first");
    if (day) {
        day = day.innerHTML
    }
    else {
        alert("Please select a start day.");
        return;
    }
    
    var startDate = new Date(year, month, day);
    
    year = WSDOM.Element.parseSelector("select.select-year", "div-CalendarEnd", "first").value;
    month = WSDOM.Element.parseSelector("select.select-month", "div-CalendarEnd", "first").value;
    day = WSDOM.Element.parseSelector("td.selected", "div-CalendarEnd", "first");
    if (day) {
        day = day.innerHTML
    }
    else {
        alert("Please select an end day.");
        return;
    }
    
    var endDate = new Date(year, month, day);
    
    var today = new Date();
    
    if (startDate.getTime() > today.getTime() || endDate.getTime() > today.getTime()) {
        //alert("Please select both an end date and start date today or earlier.");
        //return;
    }
    
    startDate = jsToMsDate(startDate);
    endDate = jsToMsDate(endDate);
    
    if (startDate > endDate) {
        alert("Please select a start to your range that is less than the end of your range.");
        return;
    }
    
    this.params.zoom = true;
    this.params.startDate = startDate;
    this.params.endDate = endDate;
    
    WSDOM.Element.removeClass(WSDOM.Element.parseSelector("li", "timeframeControlsContainer"), "active");
    WSDOM.Element.addClass(WSDOM.Element.parseSelector("li.customRange", "timeframeControlsContainer"), "active");
    //WSDOM.Element.setDisplay("div-CustomTimeframe", "none");
    
    this.getAdvancedChart("zoom");
    
    window.focus();
}

Chart.prototype.onSaveSetting = function(){
    var e = this.eventManager.add("saveSettingEvent");
    this.onSaveSetting = function(){
        return e;
    }
    return this.onSaveSetting();
}

Chart.prototype.saveSetting = function(e, el, data){
    e.cancel();
    
    var input = WSDOM.Element.parseSelector("input", "formDialog", "first");
    if (input.value.length > 30) {
        alert('Please limit the name of your save to 30 characters or less');
        return;
    }
    var checkbox = WSDOM.Element.parseSelector("input[type='checkbox']", "formDialog", "first");
    if (!input.value) {
        return;
    }
    var storedData = this.getUserStoredData();
    saveId = "";
    var len = 0;
    for (var item in storedData.settings) {
        if (storedData.settings[item].label == input.value) {
            saveId = storedData.settings[item].value
        }
        len++;
    }
    
    if (len < this.maxSaves + 2) {
        if (data.type == "saveChartSettings") {
            this.params.symbol = this.settings.symbol;
        }
        
        this.contentBuffer.load({
            url: SiteRules.getURL("/chart/bufferChartSaves.asp"),
            method: "post",
            contentType: "text/javascript",
            context: this,
            data: {
                storageType: data.type,
                saveType: "save",
                saveName: encodeURIComponent(this.escapeInput(input.value)),
                saveValue: this.serializer.serialize(this.params),
                saveId: saveId,
                saveDefault: ((checkbox) ? checkbox.checked : false)
            },
            onload: function(){
                this.addUserDataOption(this.saveOutput, data.type);
                this.closeDialog();
            }
        });
    }
    else {
        alert("Maximum number of saves reached.");
    }
}

Chart.prototype.escapeInput = function(input){
    input = input.replace(/\</g, "&lt;");
    input = input.replace(/\>/g, "&gt;");
    input = input.replace(/\"/g, "&quot;");
    return input;
}

Chart.prototype.addUserDataOption = function(setting, type){
    this.userStoredData = this.getUserStoredData();
    var list = (type == 'saveChartParams') ? this.userStoredData.settings : this.userStoredData.charts;
    list[setting.id] = {};
    list[setting.id].label = setting.name;
    list[setting.id].value = setting.id;
    (type == 'saveChartParams') ? this.userStoredData.settings = list : this.userStoredData.charts = list;
}

Chart.prototype.deleteSetting = function(e, el, data){
    e.cancel();
    var checkboxes = WSDOM.Element.parseSelector("input", "formDialog");
    for (var i = 0, saveIds = []; i < checkboxes.length; i++) {
        if (checkboxes[i].checked) {
            saveIds.push(checkboxes[i].value);
        }
    }
    this.contentBuffer.load({
        url: SiteRules.getURL("/chart/bufferChartSaves.asp"),
        method: "post",
        contentType: "text/javascript",
        context: this,
        data: {
            storageType: data.type,
            saveType: "delete",
            saveIds: this.serializer.serialize(saveIds)
        },
        onload: function(){
            this.removeUserDataOption(this.saveOutput, data.type)
        }
    });
}

Chart.prototype.removeUserDataOption = function(settings, type){
    this.userStoredData = this.getUserStoredData();
    var list = (type == 'saveChartParams') ? this.userStoredData.settings : this.userStoredData.charts;
    for (var i = 0; i < settings.length; i++) {
        for (var item in list) {
            if (item == settings[i]) {
                delete (list[item]);
            }
        }
    }
    this.closeDialog();
}

Chart.prototype.loadSave = function(e, el, data){
    var saveId = data.id || el.value || "";
    
    if (!saveId) {
        return;
    }
    
    if ("manageChart" == saveId || "manageSetting" == saveId) {
        this.popDialog(null, null, {
            type: saveId
        });
        saveId = "";
        return;
    }
    
    
    this.chartImage.contentBuffer.abortRequests();
    this.contentBuffer.abortRequests();
    
    this.contentBuffer.load({
        url: SiteRules.getURL("/chart/bufferChartSaves.asp"),
        method: "post",
        contentType: "text/javascript",
        context: this,
        data: {
            storageType: data.type,
            saveType: "load",
            saveName: saveId
        },
        onload: function(){
            this.params = this.serializer.deserialize(this.saveSettings);
            if (this.params.zoom && this.calendarLeft && this.calendarRight) {
                this.calendarLeft._setSelectedDate(msToJsDate(this.params.startDate))
                this.calendarRight._setSelectedDate(msToJsDate(this.params.endDate))
            }
            if (this.params.symbol) {
                this.settings.symbol = this.params.symbol;
                this.params.symbol = null;
            }
            this.setTimeframe(null, null, {
                timeframe: this.params.timeframe,
                getSupplementalData: true
            });
        }
    });
    
    if (el) {
        el.selectedIndex = 0;
    }
    
    window.focus();
}

Chart.prototype.updateIndicator = function(e, el, data){
    e.cancel();
    
    var inputs = WSDOM.Element.parseSelector("input", "formDialog");
    var errorText = this.checkValues(data.type, data.instance, inputs);
    
    if (!errorText) {
        for (var i = 0; i < inputs.length; i++) {
            this.params[data.type][data.instance].options.values[i] = inputs[i].value;
        }
        
        this.closeDialog();
        
        this.getAdvancedChart();
    }
    else {
        alert(errorText);
    }
}

Chart.prototype.checkValues = function(type, instance, inputs){
    var text = "";
    var indicatorName = this.params[type][instance].name;
    var indicatorOptions = this.params[type][instance].options;
    
    for (var i = 0; i < indicatorOptions.values.length; i++) {
        if (isNaN(inputs[i].value) || inputs[i].value < indicatorOptions.minValues[i] || inputs[i].value > indicatorOptions.maxValues[i]) {
            text = "Please enter " + indicatorOptions.labels[i].toLowerCase() + " between " + indicatorOptions.minValues[i] + " and " + indicatorOptions.maxValues[i] + ".";
            return text;
        }
    }
    
    if (indicatorName == "macd" && (parseInt(inputs[1].value) >= parseInt(inputs[2].value))) {
        text = "Slow value must be greater than fast value.";
    }
    else 
        if (indicatorName == "ultimate" && (parseInt(inputs[0].value) >= parseInt(inputs[1].value) || parseInt(inputs[1].value) >= parseInt(inputs[2].value))) {
            text = "Each period must be greater than the previous.";
        }
    
    return text;
}

Chart.prototype.treatHeadline = function(words, width){
    width = width || 200;
    if (width <= 80) {
        var chars = 7;
        var maxLength = 35;
    }
    else 
        if (width <= 170) {
            var chars = 14;
            var maxLength = 50;
        }
        else {
            var chars = 20;
            var maxLength = 75;
        }
    
    words = words.split(" ");
    
    for (var i = 0; i < words.length; i++) {
        if (words[i].length > chars) {
            words.splice(i + 1, 0, words[i].substr(chars, words[i].length));
            words[i] = words[i].substr(0, chars) + "- ";
        }
    }
    
    return trimString({
        value: words.join(" "),
        chars: maxLength
    });
};


Chart.prototype.showEvent = function(e, el, data){
    var theChart = this;
    
    if (this.rolloverActive) {
        this.rolloverChart(e, el);
    }
    
    if (el.dialogEventOut) {
        el.dialogEventOut.clearDelayTimeouts();
        el.dialogEventOut.removeAllElements();
        
        el.dialogEventOver.removeAllElements();
    }
    else {
        if (('ftNews' == data.type || 'icNews' == data.type) && this.settings.type == 'interactive') {
            el.dialogEventOut = false;
            el.dialogEventOver = false;
        }
        else {
            el.dialogEventOut = this.eventManager.add({
                element: null,
                type: "mouseout",
                context: this,
                handler: function(e){
                    this.hideEvent(e, el);
                },
                delay: 100
            });
            el.dialogEventOver = this.eventManager.add({
                element: null,
                type: "mouseover",
                context: el.dialogEventOut,
                handler: el.dialogEventOut.clearDelayTimeouts
            });
        }
    }
    
    WSDOM.Element.addClass(el, "active");
    
    if (!el.eventDialogBubble) {
        var dialogContent = [];
        
        if (this.settings.type != 'performance') {
            if ("ftNews" != data.type && "icNews" != data.type) {
                dialogContent.push(WSDOM.Element.create("div", {
                    'class': 'eventDialogContent'
                }, data.date));
            }
        }
        
        if ('ftNews' == data.type || 'icNews' == data.type) {
            var headlines = data.value.split("@@");
            var urls = data.url.split("@@");
            var time = data.time.split("@@");
            var h;
            
            if (this.settings.type == 'performance') {
                for (var i = 0, h, words; i < Math.min(headlines.length, 2); i++) {
                    h = theChart.treatHeadline(headlines[i], data.width);
                    
                    if (urls[i]) {
                        dialogContent.push(WSDOM.Element.create('a', {
                            'class': 'eventDialogContent storyLink',
                            'href': urls[i],
                            'Events': [{
                                type: 'click',
                                handler: popNewsStory
                            }]
                        }, h));
                    }
                    else {
                        dialogContent.push(WSDOM.Element.create('div', {
                            'class': 'eventDialogContent storyLink'
                        }, h));
                    }
                }
            }
            else {
                // Interactive Chart
                if (this.hover) {
                
                    this.hover.clearContent();
                    
                    var innerContent = WSDOM.Element.create('div', {
                        'class': 'innerContent'
                    }, null, this.hover.getContent());
                    WSDOM.Element.create('div', {
                        'class': 'newsDate'
                    }, data.date, innerContent);
                    
                    var tBody = WSDOM.Element.create('tbody', {}, null);
                    WSDOM.Element.create('table', {
                        'class': 'newsTimesAndHeadlines'
                    }, tBody, innerContent);
                    
                    for (var i = 0, h, words; i < headlines.length; i++) {
                        h = theChart.treatHeadline(headlines[i], data.width);
                        var finalHeadline = h;
                        if (urls[i]) {
                            finalHeadline = WSDOM.Element.create('a', {
                                'href': urls[i],
                                'Events': [{
                                    type: 'click',
                                    handler: popNewsStory
                                }]
                            }, h);
                        }
                        
                        WSDOM.Element.create('tr', {}, [WSDOM.Element.create('td', {
                            'class': 'newsTime'
                        }, time[i]), WSDOM.Element.create('td', {
                            'class': 'storyLink'
                        }, finalHeadline)], tBody);
                    }
                }
            }
        }
        else {
            dialogContent.push(WSDOM.Element.create("div", {
                'class': 'eventDialogContent'
            }, data.value));
        }
        
        if (dialogContent.length) {
            el.eventDialogBubble = WSDOM.Element.create("div", {
                'class': 'eventDialogBubble ' + data.type
            }, dialogContent, WSDOM.Element.get(this.settings.type + "ChartEvents"));
        }
    }
    
    if (el.dialogEventOut) {
        el.dialogEventOut.addElement([el.eventDialogBubble, el]);
    }
    
    if (el.dialogEventOver) {
        el.dialogEventOver.addElement([el.eventDialogBubble, el]);
    }
    
    if (el.eventDialogBubble) {
        WSDOM.Element.setXY(el.eventDialogBubble, data.x0 - 5, data.y0 - 5)
        WSDOM.Element.removeClass(el.eventDialogBubble, "narrowEventBubble")
        
        if (data.width) {
            WSDOM.Element.setWidth(el.eventDialogBubble, Math.min(data.width - 5, 200));
            
            if (data.width < 80) {
                WSDOM.Element.addClass(el.eventDialogBubble, "narrowEventBubble");
            }
        }
    }
}

// hide event dialog bubble
Chart.prototype.hideEvent = function(e, el, data){
    if (!this.settings.staticEventBubbles) {
        WSDOM.Element.removeClass(el, "active");
        
        WSDOM.Element.remove(el.eventDialogBubble);
        el.eventDialogBubble = null;
    }
}

Chart.prototype.getRolloverMousemove = function(){
    var e = this.eventManager.add(null, "mousemove", this.rolloverChart, this);
    this.getRolloverMousemove = function(){
        return e;
    }
    return this.getRolloverMousemove();
}

Chart.prototype.getRolloverMouseout = function(){
    var e = this.eventManager.add(null, "mouseout", this.rolloutChart, this);
    this.getRolloverMouseout = function(){
        return e;
    }
    return this.getRolloverMouseout();
}

// move to interactiveChart
Chart.prototype.enableRollover = function(){
    if (!this.rolloverActive) {
        var chartImage = WSDOM.Element.parseSelector("img.chartImage", this.settings.type + "ChartImageContainer", "first");
        this.getRolloverMousemove().addElement(chartImage);
        this.getRolloverMouseout().addElement(chartImage);
        this.rolloverActive = true;
    }
}

Chart.prototype.disableRollover = function(){
    if (this.rolloverActive) {
        if (this.rollover) {
            WSDOM.Element.remove(this.rollover);
            WSDOM.Element.remove(this.rolloverText);
            this.rollover = null;
            this.rolloverText = null;
            this.rolloverLine = null;
        }
        else {
            this.rolloverActive = false;
            //return;
        }
        
        this.getRolloverMousemove().removeAllElements();
        this.getRolloverMouseout().removeAllElements();
        
        this.rolloverActive = false;
    }
}

Chart.prototype.setLoadingState = function(state){
    this._loadingState = state || false;
}

Chart.prototype.getLoadingState = function(state){
    return this._loadingState;
}

/*
FILE CONCAT ADD FILE
PATH: /ft/resources/client/chart/ChartImage.js
*/
function ChartImage() 
{
	this.serializer = new Serializer();
	this.contentBuffer = new ContentBuffer();
	this.contentBuffer.debug = true;
}

ChartImage.prototype.CSS_HIDDEN = "wsodHidden";
ChartImage.prototype.URL = SiteRules.getURL("/chart/getChart.asp");

ChartImage.prototype.load = function(args) 
{
	this.type = args.type || "interactive";
	this.callback = args.callback || null;
	this.context = args.context || window;

	this.showLoading();

	var cht = args.settings.type || "interactive";
	var settings = (args.settings) ? this.serializer.serialize(args.settings) : "";
	var params = (args.params) ? this.serializer.serialize(args.params) : "";
	var returns = args.returns || "fileName:File.Name";
	var validateSymbol = args.validateSymbol || "";
	var minsDelayed = args.minsDelayed || "";
	var getDisclaimer = args.getDisclaimer || "";
	var getSupplementalData = args.getSupplementalData || "";
	var sessionName = args.sessionName || '';
	var sessionValue = args.sessionValue || '';
	
	this.img = Element.parseSelector("img.chartImage", this.type+"ChartImageContainer");
	
	this.contentBuffer.abortRequests();
	var connection = this.contentBuffer.load({
		url: this.URL,
		method: "post",
		contentType: "text/javascript",
		context: this,
		data: {
			cht: cht
			,settings: settings
			,params: params
			,returnVars: returns 
			,validateSymbol: validateSymbol
			,getDisclaimer: getDisclaimer
			,minsDelayed: minsDelayed
			,getSupplementalData: getSupplementalData
			,sessionName: sessionName
			,sessionValue: sessionValue
		}/*,
		onload: function() {
			this.showImage();
		}*/
	});

	return connection;
}

ChartImage.prototype.onload = function(results) {
	results = this.serializer.deserialize(results);
	
	for (var i in results) {
		this[i] = results[i];
	}
	
	this.showImage();
};

ChartImage.prototype.showImage = function() 
{
	var img = document.createElement('img');
	Events.add({element: img, type: "load", context: this, handler: this.finishLoad});
	img.src = this.fileName;
}

ChartImage.prototype.finishLoad = function() 
{
	for (var i = 0; i < this.img.length; i++) {
		this.img[i].src = this.fileName;
	}

	if (this.callback) { this.callback.apply(this.context); }
	
	this.hideLoading();
}

ChartImage.prototype.showLoading = function() 
{
	var chartSize = Element.getSize(this.type+"ChartImageContainer");
	var chartLoading = Element.parseSelector("div.chartLoading",this.type+"ChartImageContainer");
	if (chartSize.width > 0) { Element.setSize(chartLoading, chartSize.width, chartSize.height); }
	
	Element.removeClass(chartLoading, this.CSS_HIDDEN);
}

ChartImage.prototype.hideLoading = function() 
{
	Element.addClass(Element.parseSelector("div.chartLoading", this.type+"ChartImageContainer"), this.CSS_HIDDEN);
	// remember to remove the below line
	//Element.addClass(Element.parseSelector("div.chartLoading", this.type+"ChartImageContainer"), "hide");
}

ChartImage.prototype.testit = function() {
	alert('here');
}
/*
FILE CONCAT ADD FILE
PATH: /ft/resources/client/chart/Trendline.4.js
*/
/**  
 * Trendline.2.js
 * created 11/01/2006 by Andrew Cattau 
 * updated 7/09/2007 by Gregg Johnson 
 * updated 3/06/2008 by Steven Racz
 * added compatability for Events.3.js while retaining backwards compatability
 * added event cancelling for text selection (strange issue when labels were visible in IE)
 
 TO DO:
 - new formula to determine the infinite line
 - fix the logic of the remove handles on resize
 
 
 */

/* Before we begin, a few words on style:
 *
 * Most of the objects that get created dynamically by this file don't have
 * any explicit style associated with them.  In order for this file to work
 * properly, the following CSS rule is required at a minimum:

.tl {
	position: absolute;
	background-color: #000000;  // Can be any color, but must be specified
	line-height: 0;             // Needed for IE
	font-size: 0;               // Needed for IE
}

 * You can of course add any additional rules that you want to get the desired
 * appearance.  Generally speaking, it isn't necessary to give z-indexes to the
 * trendline elements- they are appended to the end of htmlElement, so they
 * will appear above anything inside of htmlElment without an explicit z-index.
 * Again, though, you can add one if necessary in your CSS file.  Second, in
 * order for the positioning logic that we use here to work properly,
 * htmlElement must be (either via CSS rule or explicit style assignment)
 * "position: relative" (alternatively it must be positioned at the exact top
 * left of another relatively positioned object).
 *
 * .tl is the class for the line segments.
 *
 * Other classes that you can style as you please:
 * .TrendLineCanvas  // The div that will be used to detect mouse events
 * .TrendlineHandle  // The images that will denote the trendline endpoints
 * .TrendLabel       // The % label
 *
 * Lastly, there are a few other things you can configure via the object prototypes:
 *
 * // number of lines
 * TrendLineCanvas.prototype.maxLines = 5;
 *
 * // className of line
 * TrendLineCanvas.prototype.lineClassName = 'tl';
 *
 * // endpoint image
 * TrendLine.prototype.imgHandleProps.src = '/gif/trendline/crosshair.gif';
 * TrendLine.prototype.imgHandleProps.width = 6;
 * TrendLine.prototype.imgHandleProps.height = 6;
 * 
 * // upon resize if the endpoint falls off the chart we display by default change below to true if you want to drop the image
 * TrendLineCanvas.prototype.resizeDropEndPoint = false;
 *
 * // block the labels from displaying (default is set to false);
 * TrendLine.prototype.blockTrendLabels = true;
 *
 * // label colors
 * TrendLine.prototype.posColor = '#009900';
 * TrendLine.prototype.negColor = '#990000';
 *
 *
 * And now, usage:
 *
 * document.body.appendChild(
 * 		Element.create('div', { 'id': 'ChartContainer', 'style': 'position:relative' }, [
 * 			Element.create('img', { 'id': 'myChart', 'src': '/path/to/chart.gif' } )
 * 		] )
 * 	);
 * var trendlines = new TrendLineCanvas(Element.get('ChartContainer'));
 *
 * // x and y values are relative to the top-left corner of your container.
 * trendlines.setBounds(left, right, top, bottom);
 * trendlines.setAxes(xMin, xMax, yMin, yMax);
 *
 * // If the size of your chart changes, (e.g. window resize), you have to update the canvas:
 * trendlines.setPosition();
 *
 * // After calling any combination of the above, you can call 
 * trendlines.resizeLines()
 * // and all of your trendlines will be rescaled to fit the new size / limits.
 * // Lines that cross the boundaries will be truncated, and lines entirely
 * // outside the bounds will be deleted.  As an odd compatibility note, you
 * // should not call resizeLines() from inside a window onresize event.  IE 6
 * // can cause spurious onresize events when you click to draw a trendline.
 * // After rescaling all of the lines on the canvas, resizeLines will clear
 * // any lines with a 0 length, including your newly created line.  The result
 * // is that the trendlines appear to just not work.  This is almost
 * // impossible to detect, and even harder to work around, so just don't do
 * // it.
 *
 * // Other functions:
 *
 * trendlines.hideLines();
 * trendlines.showLines();
 * trendlines.clearLines();
 * // don't clear existing lines, but disable/enable drawing / editing.
 * trendlines.disableCanvas();
 * trendlines.enableCanvas();
 *
 * */

// TrendLineCanvas Object

function TrendLineCanvas(htmlElement) {
	this.canvasElement = Element.create('div', { 'class': 'TrendLineCanvas', 'style': 'position:absolute;top:0;left:0' });
	this.ownerElement = htmlElement;
	this.ownerElement.appendChild(this.canvasElement);

	// For some unknown reason, the Internet Explorer developers saw fit to
	// ignore event handlers on empty DIVs.  We can get around that by giving
	// the element a background-color, but we don't want it to cover our
	// original element.  Opacity to the rescue!
	// The browser detect here isn't strictly necessary, because the following
	// code will still work in other browsers.  But in this case it actually
	// feels less dirty to me than mucking around unnecessarily in well behaved
	// browsers.  Anyway, since there is really no penalty for mis-detection
	// here other than offending my aesthetic sense...
	if (navigator.userAgent.indexOf('MSIE') != -1) {
		Element.setStyle(this.canvasElement, 'background-color:#ffffff');
		Element.setOpacity(this.canvasElement, 0);
	}

	this.lines = [ ];
	this.currLine = null;

	this.setPosition();

	this.bounds = { 'top':    0,
					'right':  this.ownerSize.width,
					'bottom': this.ownerSize.height,
					'left':   0,

					'width':  this.ownerSize.width,
					'height': this.ownerSize.height
				};

	this.axes = {
					'xMinVal': 0,
					'xMaxVal': 0,
					'yMinVal': 0,
					'yMaxVal': 0,

					'xRange':  0,
					'yRange':  0
				};

	Events.add( {	'element': this.canvasElement,
					'type': 'mousedown',
					'handler': this.beginDraw,
					'context': this
				} );

	// cancel selecting/dragging in IE
	Events.add( {	'element': this.canvasElement,
					'type': 'dragstart',
					'handler': Events.cancel,
					'context': this
				} );
	Events.add( {	'element': this.canvasElement,
					'type': 'selectstart',
					'handler': Events.cancel,
					'context': this
				} );

}

TrendLineCanvas.prototype.maxLines = 5;

TrendLineCanvas.prototype.lineClassName = "tl";

// User functions.  Use these to setup up the canvas to match your data.

TrendLineCanvas.prototype.setPosition = function () {
	this.ownerSize = Element.getSize(this.ownerElement);

	this.canvasElement.style.width = this.ownerSize.width + 'px';
	this.canvasElement.style.height = this.ownerSize.height + 'px';
}

TrendLineCanvas.prototype.setBounds = function (left, right, top, bottom) {
	this.bounds = { 'left':   left,
		'right':  right,
		'bottom': bottom,
		'top':    top,

		'width':  right - left,
		'height': bottom - top
	};
}


TrendLineCanvas.prototype.setAxes = function (xMinVal, xMaxVal, yMinVal, yMaxVal) {
	this.axes = {
		'xMinVal': xMinVal,
		'xMaxVal': xMaxVal,
		'yMinVal': yMinVal,
		'yMaxVal': yMaxVal,

		'xRange':  xMaxVal - xMinVal,
		'yRange':  yMaxVal - yMinVal
	};
}

TrendLineCanvas.prototype.resizeLines = function () {
	// do the loop in reverse since some of the lines may fall out of the array
	// in the process
	for (var i = this.lines.length - 1; i >= 0; i--) {
		// calculate new coordinates
		var x1 = this.getXFromValue(this.lines[i].anchor.xVal);
		var x2 = this.getXFromValue(this.lines[i].drag.xVal);

		var y1 = this.getYFromValue(this.lines[i].anchor.yVal)
		var y2 = this.getYFromValue(this.lines[i].drag.yVal)
		
		// clip new coordinates to current viewable area
		var newCoords = this.checkClip(x1, x2, y1, y2);

		// if line has collapsed to a point, delete it.
		// else redraw it
		if (newCoords.x1 == newCoords.x2 && newCoords.y1 == newCoords.y2) {
			this.clearLine(this.lines[i]);
			
		} else {
			this.lines[i].setAnchorPos( newCoords.x1, newCoords.y1);
			this.lines[i].setDragPos( newCoords.x2, newCoords.y2);
			
			if (this.resizeDropEndPoint) {
				this.lines[i].setHandle( newCoords.leftHandle, newCoords.rightHandle);
			}

			this.lines[i].paint();
		}
	}
}

TrendLineCanvas.prototype.hideLines = function () {
	for (var i = 0; i < this.lines.length; i++) {
		this.lines[i].hide();
	}
}

TrendLineCanvas.prototype.showLines = function () {
	for (var i = 0; i < this.lines.length; i++) {
		this.lines[i].show();
	}
}

TrendLineCanvas.prototype.clearLines = function () {
	var line;
	while (line = this.lines.pop()) {
		line.clear();
	}
}

TrendLineCanvas.prototype.resizeDropEndPoint = false;

TrendLineCanvas.prototype.disableCanvas = function () {
	this.canvasElement.style.display = 'none';
}

TrendLineCanvas.prototype.enableCanvas = function () {
	this.canvasElement.style.display = '';
}

// utility functions.
// These are mostly used internally, but they can be called by the end user if
// you need access to any of this data.

TrendLineCanvas.prototype.pctChg = function (a, b) {
	// From: http://barney.wallst.com/wsodwiki/index.php/QA:Rules_Based_QA
	// Calculating Percent Change
	//   1. The formula is: (New - Old) / Old
	//         1. Watch out when going from a negative number to another negative number or a positive number.
	//         2. If comparing two negative numbers, the smaller number should be an increase. This requires a change in the formula 
	// 
	var pct = Number.NaN;

	if (a > 0 && b >= 0) {
		pct = (b - a) / a;
	} else if (a < 0 && b < 0) {
		pct = - (b - a) / a;
	} else if (a == 0 && b == 0) {
		pct = 0;
	}

	if (!isNaN(pct)) {
		return (pct * 100).toFixed(2);
	} else {
		return '--';
	}
}

// Hey, algebra can actually be useful!
// (x - xMin) / (xMax - xMin) = (val - valMin) / (valMax - valMin)
// solve for val...
TrendLineCanvas.prototype.getValueFromX = function (relX) {
	var x = relX - this.bounds.left;
	var pct = x / this.bounds.width;
	var val = this.axes.xMinVal + pct * this.axes.xRange;

	return val;
}

// Now do the same thing, but solve for x...
TrendLineCanvas.prototype.getXFromValue = function (value) {
	var pct = (value - this.axes.xMinVal) / this.axes.xRange;
	var x = pct * this.bounds.width;
	var relX = x + this.bounds.left;

	return Math.round(relX);
}

// same idea, but slightly different, since yMin => valMax / yMax => valMin
// (y - yMin) / (yMax - yMin) = (valMax - val) / (valMax - valMin)
TrendLineCanvas.prototype.getValueFromY = function (relY) {
	var y = relY - this.bounds.top;
	var pct = y / this.bounds.height;
	var val = this.axes.yMaxVal - pct * this.axes.yRange;

	return val;
}

// now solve for y...
TrendLineCanvas.prototype.getYFromValue = function (value) {
	var pct = (this.axes.yMaxVal - value) / this.axes.yRange;
	var y = pct * this.bounds.height;
	var relY = y + this.bounds.top;

	return Math.round(relY);
}

// internal functions

TrendLineCanvas.prototype.checkClip = function (x1, x2, y1, y2) {
	var xMin = this.bounds.left;
	var xMax = this.bounds.right;
	var yMin = this.bounds.top;
	var yMax = this.bounds.bottom;
	var leftHandle = rightHandle = true;

	// if I make sure x1 is less than x2 now, i can reduce the number of checks to do later
	if (x1 > x2) {
		var _x = x1; x1 = x2; x2 = _x;
		var _y = y1; y1 = y2; y2 = _y;
	}

	/**
	 * if the line is clipped then do not show the line handles
	 */
	if (x1 < xMin || y1 < yMin || x2 < xMin || y2 < yMin) {
		leftHandle = false;
	}
	if (x1 > xMax || y1 > yMax || x2 > xMax || y2 > yMax) {
		rightHandle = false;
	}

	// if the line is entirely outside of the view area, collapse it to a single point.
	// it will then get cleaned up later.
	if (x1 < xMin && x2 < xMin) {
		x1 = xMin;
		x2 = xMin;
		y1 = y2;
	} else if (x1 < xMin) {
		// More fun with algebra.  And you thought this would never be useful...
		// we know x1,y1 and x2,y2.  We want to find the coordinate y' of the
		// line at an arbitrary x'.
		// (x' - x1) / (y' - y1) = (x2 - x1) / (y2 - y1)
		// solve for y'
		// in this case x' is xMin, and x' and y' become our new x1 and y1
		y1 = (y2 - y1) * (xMin - x1) / (x2 - x1) + y1;
		x1 = xMin;
	}
	
	if (x1 > xMax && x2 > xMax) {
		x1 = xMax;
		x2 = xMax;
		y2 = y1;
	} else if (x2 > xMax) {
		// same as before but this time, x' is xMax, and y' becomes y2
		y2 = (y2 - y1) * (xMax - x1) / (x2 - x1) + y1;
		x2 = xMax;
	}

	// again swap the endpoints (if necessary) so that y1 < y2
	if (y1 > y2) {
		var _x = x1; x1 = x2; x2 = _x;
		var _y = y1; y1 = y2; y2 = _y;
	}

	if (y1 < yMin && y2 < yMin) {
		y1 = yMin;
		y2 = yMin;
		x1 = x2;
	} else if (y1 < yMin) {
		// same equation.  this time solve for x' at a known y'
		x1 = (x2 - x1) * (yMin - y1) / (y2 - y1) + x1;
		y1 = yMin;
	}
	
	if (y1 > yMax && y2 > yMax) {
		y1 = yMax;
		y2 = yMax;
		x2 = x1;
	} else if (y2 > yMax) {
		// one last time...
		x2 = (x2 - x1) * (yMax - y1) / (y2 - y1) + x1;
		y2 = yMax;
	}

	return {
		'x1': Math.round(x1),
		'x2': Math.round(x2),
		'y1': Math.round(y1),
		'y2': Math.round(y2),
		
		'leftHandle': leftHandle,
		'rightHandle': rightHandle
	};

}

TrendLineCanvas.prototype.getNextLine = function () {
	// add a new line to the front of the array
	this.lines.unshift(new TrendLine(this));

	// if we're over the limit, remove one from the end.
	if (this.lines.length > this.maxLines) {
		var old = this.lines.pop();
		old.clear();
	}
	
	return this.lines[0];
}

TrendLineCanvas.prototype.clearLine = function (line) {
	line.clear();

	// remove the line from the array so it doesn't count against our limit.
	for (var i = 0; i < this.lines.length; i++) {
		if (this.lines[i] == line) {
			this.lines.splice(i, 1);
		}
	}
}

TrendLineCanvas.prototype.setDrawable = function (line) {
	this.currLine = line;

	line.beginDrag();

	// Danger, Will Robinson!!
	// Hopefully no one else has any 'onmouseup' event listeners on the document,
	// otherwise they are going to lose them when we clear this away later...
	Events.add( {	'element': document.body,
					'type':    'mouseup',
					'handler': this.endDraw,
					'context': this
				} );

	Events.add( {	'element': this.canvasElement,
					'type':    'mousemove',
					'handler': this.refresh,
					'context': this
				} );
}

TrendLineCanvas.prototype.unsetDrawable = function (line) {
	if (line) {
		Events.remove(this.canvasElement, 'mousemove');

		Events.remove(document.body, 'mouseup');

		line.endDrag(this.lineClassName);
		//line.lineClassNameStore = this.lineClassName;
		
		this.currLine = null;
	}
}

// event handlers

TrendLineCanvas.prototype.beginDraw = function (evt, element) {
	this.disableTextSelect();

	var relX = (evt.nativeEvent) ? evt.nativeEvent.layerX || evt.nativeEvent.offsetX : evt.layerX || evt.offsetX;
	var relY = (evt.nativeEvent) ? evt.nativeEvent.layerY || evt.nativeEvent.offsetY : evt.layerY || evt.offsetY;

	if (relX < this.bounds.left  ||
		relX > this.bounds.right ||
		relY < this.bounds.top   ||
		relY > this.bounds.bottom
	) {
		return Events.cancel(evt);
	}

	var line = this.getNextLine();

	line.setAnchorPos(relX, relY);
	line.setDragPos(relX, relY);
	line.lineClassNameStore = this.lineClassName;
	line.paint();

	this.setDrawable(line);

	// this should prevent selecting/dragging in non-IE
	return Events.cancel(evt);
}

TrendLineCanvas.prototype.grabHandle = function (evt, element, line) {

	// Move the line to the front of the list if it's not already there.  This
	// way the most recently edited line is treated the same as the most
	// recently created.  Mainly this is to keep a line that was just modified
	// from being cleaned up when the user hits their limit, as they are
	// probably more interested in the most recently modified, versus the most
	// recently created.
	for (var i = 1; i < this.lines.length; i++) {
		if (this.lines[i] == line) {
			this.lines.splice(i, 1);
			this.lines.unshift(line);
		}
	}

	// set the selected endpoint as the draggable end
	line.setDrag(element);

	this.setDrawable(line);

	// this should prevent selecting/dragging in non-IE
	return Events.cancel(evt);
}

TrendLineCanvas.prototype.refresh = function (evt, element) {
	if (!this.currLine) {
		return;
	} else {
		var line = this.currLine;
	}

	var relX = (evt.nativeEvent) ? evt.nativeEvent.layerX || evt.nativeEvent.offsetX : evt.layerX || evt.offsetX;
	var relY = (evt.nativeEvent) ? evt.nativeEvent.layerY || evt.nativeEvent.offsetY : evt.layerY || evt.offsetY;

	// there are really only two elements we could mouse over here, the canvas
	// or the label.  We added the mouseover handler to the label to prevent a
	// jarring stopp if the user move a little too fast.  If it's the label, we
	// have to take that into account when getting our position.
	if (element != this.canvasElement) {
		relX += parseInt(element.style.left);
		relY += parseInt(element.style.top);
	}

	if (relX < this.bounds.left  ||
		relX > this.bounds.right ||
		relY < this.bounds.top   ||
		relY > this.bounds.bottom
	) {
		return;
	}

	line.setDragPos(relX, relY);
	line.lineClassNameStore = this.lineClassName;
	line.paint();
}

TrendLineCanvas.prototype.endDraw = function (evt, element) {
	this.enableTextSelect();

	this.unsetDrawable(this.currLine);
}

TrendLineCanvas.prototype.disableTextSelect = function() {
	if (!document.onselectstart) {
		document.onselectstart = function() {
			return false;
		};
	}
};

TrendLineCanvas.prototype.enableTextSelect = function() {
	if (document.onselectstart) {
		document.onselectstart = null;
	}
};


// TrendLine Object

function TrendLine(owner) {
	this.handleOffsetX = Math.floor(this.imgHandleProps.width / 2) - 1;
	this.handleOffsetY = Math.floor(this.imgHandleProps.height / 2) - 1;

	this.ownerCanvas = owner;

	this.createCanvas();
	if (!this.blockTrendLabels) {
		this.createLabel();
	}
	this.createHandles();

	this.segments = [ ];
}

TrendLine.prototype.imgHandleProps = {	'src': '/gif/trendline/crosshair.gif',
										'class':  'TrendlineHandle',
										'height': 6,
										'width':  6,
										'style': 'position:absolute'
									};
TrendLine.prototype.posColor = '#009900';
TrendLine.prototype.negColor = '#990000';

// Don't Change ME!!
TrendLine.prototype.lineRegex = /%(\d+);(\d+);(\d+);(\d+)/g;

// 'Public' interface

TrendLine.prototype.reset = function () {
	this.drag.style.display = '';
	this.anchor.style.display = '';
	this.canvas.style.display = '';
	if (!this.blockTrendLabels) {
		this.label.style.display = '';
	}
}

TrendLine.prototype.setHandle = function (left, right) {
	if (!left)
		this.anchor.style.display = 'none';
	if (!right)
		this.drag.style.display = 'none';
	
}

TrendLine.prototype.setDrag = function (handle) {
	this.drag   = (handle == this.handles['a']) ? this.handles['a'] : this.handles['b'];
	this.anchor = (handle == this.handles['a']) ? this.handles['b'] : this.handles['a'];
}

TrendLine.prototype.isInfinite = false;

/**
 * not really infinite just setting a Max and Min value that can never be reach via the chart
 */
TrendLine.prototype.setInfiniteTrend = {
	'min': 0,
	'max': 0	
}

	
TrendLine.prototype.createInfiniteTrend = function () {
	if (this.anchor._x > this.drag._x) {
		var x1 = this.drag._x;
		var x2 = this.anchor._x;
		var y1 = this.drag._y;
		var y2 = this.anchor._y;
	} else {
		var x2 = this.drag._x;
		var x1 = this.anchor._x;
		var y2 = this.drag._y;
		var y1 = this.anchor._y;	
	}
	
	// find the X coordinate given the min and max X values (MS Date usually)
	minX = this.ownerCanvas.getXFromValue(this.setInfiniteTrend.min);
	maxX = this.ownerCanvas.getXFromValue(this.setInfiniteTrend.max);
	
	/*
	x1 = 23;
	y1 = 205;
	x2 = 68;
	y2 = 198;
	
	minX = x1;
	maxX = x2; 
	*/
	
	// find the slope
	this.slope = (y2 - y1) / (x2 - x1);
	
	// find min and max y	
	maxY = this.slope * (maxX - x2) + y2;
	minY = (-1 * (this.slope * (x1 - minX))) + y1;
	
	if (0 == this.slope) {
		minY = maxY;
	}
	
	//console.log("slope: " + this.slope);
	//console.log("Anchor: " + minX + ", " + minY);
	//console.log("Drag: " + maxX + ", " + maxY);
	this.ownerCanvas.resizeLines();
	
	// clip new coordinates to current viewable area
	var newCoords = this.ownerCanvas.checkClip(minX, maxX, minY, maxY);

	// if line has collapsed to a point, delete it.
	// else redraw it
	if (newCoords.x1 == newCoords.x2 && newCoords.y1 == newCoords.y2) {
		this.clear();
		
	} else {
		this.setAnchorPos(newCoords.x1, newCoords.y1);
		this.setDragPos(newCoords.x2, newCoords.y2);
		
		if (this.resizeDropEndPoint) {
			this.setHandle(newCoords.leftHandle, newCoords.rightHandle);
		}

		this.paint();
	}

}

TrendLine.prototype.setAnchorPos = function (x, y) {
	//console.log(x + ", " + y);
	this.anchor._x = x;
	this.anchor._y = y;

	this.anchor.xVal = this.ownerCanvas.getValueFromX(x);
	this.anchor.yVal = this.ownerCanvas.getValueFromY(y);

	this.anchor.style.top  = (y - this.handleOffsetY) + 'px';
	this.anchor.style.left = (x - this.handleOffsetX) + 'px';
}

TrendLine.prototype.setDragPos = function (x, y) {
	this.drag._x = x;
	this.drag._y = y;

	this.drag.xVal = this.ownerCanvas.getValueFromX(x);
	this.drag.yVal = this.ownerCanvas.getValueFromY(y);

	this.drag.style.top  = (y - this.handleOffsetY) + 'px';
	this.drag.style.left = (x - this.handleOffsetX) + 'px';
}

TrendLine.prototype.beginDrag = function () {
	// Totally an aesthetic decision on my part, but I like it better if the
	// handle is not in the way (visibly) when I'm dragging it.
	// And since I wrote the code, all you suckers are stuck with it :-P
	this.drag.style.display = 'none';

	if (!this.blockTrendLabels) {
		Events.add( {	'element': this.label,
			'type':    'mousemove',
			'handler': this.ownerCanvas.refresh,
			'context': this.ownerCanvas
		} );
	}
}

TrendLine.prototype.endDrag = function () {
	if (Math.abs(this.drag._x - this.anchor._x) < this.imgHandleProps.width &&
		Math.abs(this.drag._y - this.anchor._y) < this.imgHandleProps.height
	) {
		// have the canvas clean us up so that we get removed from the list of lines...
		this.ownerCanvas.clearLine(this);
	} else {
		this.drag.style.display = '';
	}
	
	if (this.isInfinite) {
		this.createInfiniteTrend();
		
	}
	
	if (!this.blockTrendLabels) {
		Events.remove(this.label, 'mousemove');
	}
}

TrendLine.prototype.lineClassNameStore = 'tl3';
TrendLine.prototype.blockTrendLabels = false;

TrendLine.prototype.paint = function () {
	this.segments = [ ];
	this.makeLine(	this.anchor._x, this.anchor._y,
					this.drag._x,   this.drag._y,   this.segments );
	this.canvas.innerHTML = this.segments.join("").replace(this.lineRegex,'<div class="trendClass" style="left:$1px;top:$2px;width:$3px;height:$4px;"></div>').replace(/trendClass/g, this.lineClassNameStore);
	console.log("TESTING");
	if (!this.blockTrendLabels) {
		if (this.anchor._x <= this.drag._x) {
			this.label.innerHTML = this.ownerCanvas.pctChg(this.anchor.yVal, this.drag.yVal);
			this.label.style.top = (this.drag._y - this.label.offsetHeight) + 'px';
			this.label.style.left = (this.drag._x + 8) + 'px';
		} else {
			this.label.innerHTML = this.ownerCanvas.pctChg(this.drag.yVal, this.anchor.yVal);
			this.label.style.top = (this.anchor._y - this.label.offsetHeight) + 'px';
			this.label.style.left = (this.anchor._x + 8) + 'px';
		}
	
		if (this.label.innerHTML > 0) {
			this.label.style.color = this.posColor;
			this.setLineClass("Positive");
		} else if (this.label.innerHTML < 0) {
			this.label.style.color = this.negColor;
			this.setLineClass("Negative");
		} else {
			this.label.style.color = '';
			this.setLineClass("Neutral");
		}
	
		if (this.label.innerHTML != '--') {
			this.label.innerHTML += '%';
		}
	}
}

TrendLine.prototype.setLineClass = function (sClass) {
	this.canvas.className = "trend"+sClass;
}

TrendLine.prototype.hide = function () {
	if (!this.blockTrendLabels) {
		this.label.style.display = 'none';
	}
	this.canvas.style.display = 'none';
	this.anchor.style.display = 'none';
	this.drag.style.display = 'none';
}

TrendLine.prototype.show = function () {
	if (!this.blockTrendLabels) {
		this.label.style.display = '';
	}
	this.canvas.style.display = '';
	this.anchor.style.display = '';
	this.drag.style.display = '';
}

TrendLine.prototype.clear = function () {
	if (!this.blockTrendLabels) {
		this.label.parentNode.removeChild(this.label);
	}
	this.canvas.parentNode.removeChild(this.canvas);
	this.anchor.parentNode.removeChild(this.anchor);
	this.drag.parentNode.removeChild(this.drag);
}

// Internal functions

TrendLine.prototype.createHandles = function () {
	this.handles = {
						'a': Element.create('img', this.imgHandleProps),
						'b': Element.create('img', this.imgHandleProps)
					};

	this.handles['a']._x = 0; this.handles['a']._y = 0;
	this.handles['b']._x = 0; this.handles['b']._y = 0;

	Events.add( {	'element': this.handles['a'],
					'type': 'mousedown',
					'handler': this.ownerCanvas.grabHandle,
					'context': this.ownerCanvas,
					'data':    this
				} );
	Events.add( {	'element': this.handles['b'],
					'type': 'mousedown',
					'handler': this.ownerCanvas.grabHandle,
					'context': this.ownerCanvas,
					'data':    this
				} );

	// cancel selecting/dragging in IE
	Events.add( {	'element': this.handles['a'],
					'type': 'dragstart',
					'handler': Events.cancel,
					'context': this
				} );

	Events.add( {	'element': this.handles['b'],
					'type': 'dragstart',
					'handler': Events.cancel,
					'context': this
				} );


	this.anchor = this.handles['a'];
	this.drag   = this.handles['b'];

	this.ownerCanvas.ownerElement.appendChild(this.handles['a']);
	this.ownerCanvas.ownerElement.appendChild(this.handles['b']);
}

TrendLine.prototype.createLabel = function () {
	this.label = Element.create('span', { 'class': 'TrendLabel', 'style': 'position:absolute' })
	this.ownerCanvas.ownerElement.appendChild(this.label);
}

TrendLine.prototype.createCanvas = function () {
	this.canvas = Element.create('div')
	this.ownerCanvas.ownerElement.appendChild(this.canvas);
}

// this is all black magic to me, but it seems to work as advertised,
// so I'm not gonna mess with it.

/*based on mkLine() from wz_jsgraphics.js  v. 2.3;Copyright (c) 2002-2004 Walter Zorn. All rights reserved.;Created 3. 11. 2002 by Walter Zorn (Web: http://www.walterzorn.com );Performance optimizations for Internet Explorer;by Thomas Frank and John Holdsworth;This program is free software;you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation;either version 2 of the License, or (at your option) any later version.This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the GNU General Public License at http://www.gnu.org/copyleft/gpl.html for more details.*/
TrendLine.prototype.makeLine = function (x1, y1, x2, y2, segments) {
	if(x1>x2)
	{
		_x2=x2;
		_y2=y2;
		x2=x1;
		y2=y1;
		x1=_x2;
		y1=_y2;
	}
	
	dx=x2-x1;
	dy=Math.abs(y2-y1);
	x=x1;
	y=y1;
	yIncr=(y1>y2)?-1:1;
	if(dx>=dy)
	{
		pr=dy<<1;
		pru=pr-(dx<<1);
		p=pr-dx;
		ox=x;
		while((dx--)>1)
		{
			++x;
			if(p>0)
			{
				segments[segments.length]='%'+ox+';'+y+';'+(x-ox)+';2';
				y+=yIncr;
				p+=pru;
				ox=x;
			}
			else{ p += pr; }
		}
		segments[segments.length]='%'+ox+';'+y+';'+(x2-ox+2)+';2';
	}
	else
	{
		pr=dx<<1;
		pru=pr-(dy<<1);
		p=pr-dy;
		oy=y;
		if(y2<=y1)
		{
			while((dy--)>1)
			{
				if(p>0)
				{
					segments[segments.length]='%'+(x++)+';'+y+';2;'+(oy-y+2);
					y+=yIncr;
					p+=pru;
					oy=y;
				}
				else
				{
					y+=yIncr;
					p+=pr;
				}
			}
			segments[segments.length]='%'+x2+';'+y2+';2;'+(oy-y2+2);
		}
		else
		{
			while((dy--)>1)
			{
				y+=yIncr;
				if(p>0)
				{
					segments[segments.length]='%'+(x++)+';'+oy+';2;'+(y-oy);
					p+=pru;oy=y;
				}
				else{ p+=pr; }
			}
			segments[segments.length]='%'+x2+';'+oy+';2;'+(y2-oy+2);
		}
	}
}

/*
FILE CONCAT ADD FILE
PATH: /ft/resources/client/DataDefinitions.js
*/
function DataDefinitions () {
	DataDefinitions.Super(this);
	this.definitions = null;
};
DataDefinitions.Extend(Popup);

DataDefinitions.prototype.getBuffer = function() {
	var cb = new ContentBuffer();
	this.getBuffer = function() { return cb; };
	
	return this.getBuffer();
};

DataDefinitions.prototype.setDefinitionSet = function(set) {
	this.definitionSet = set;
};

DataDefinitions.prototype.requestURL = "/ft/resources/buffer/getDataDefinitions.asp";

DataDefinitions.prototype.getDefinitions = function() {
	this.getBuffer().load({
		url: this.requestURL,
		data: {
			view: this.definitionSet
		},
		onload: this.updateContent,
		context: this
	})
};

DataDefinitions.prototype.draw = function() {
	
	if (this.isVisible()) {		
		return;
	}
	
	if (!this.definitions) {
		this.setTitleText("Data Definitions - " + this.definitionSet);
		Element.addClass(this.getFrame(), "dataDefinitions");
		this.getDefinitions();
		Element.addChild(this.getContent(), Element.create("p", null, "Loading definitions for data items displayed on this page."));
	}
	
	DataDefinitions.Super(this, "draw");
};

DataDefinitions.prototype.updateContent = function(response) {
	this.clearContent();
	this.definitions = response.getResult();
	this.getContent().innerHTML = this.definitions;
};