
nv.models.stackedArea = function() {

    //============================================================
    // Public Variables with Default Settings
    //------------------------------------------------------------

    var margin = {top: 0, right: 0, bottom: 0, left: 0}
        , width = 960
        , height = 500
        , color = nv.utils.defaultColor() // a function that computes the color
        , id = Math.floor(Math.random() * 100000) //Create semi-unique ID incase user doesn't selet one
        , getX = function(d) { return d.x } // accessor to get the x value from a data point
        , getY = function(d) { return d.y } // accessor to get the y value from a data point
        , style = 'stack'
        , offset = 'zero'
        , order = 'default'
        , interpolate = 'linear'  // controls the line interpolation
        , clipEdge = false // if true, masks lines within x and y scale
        , x //can be accessed via chart.xScale()
        , y //can be accessed via chart.yScale()
        , scatter = nv.models.scatter()
        , dispatch =  d3.dispatch('tooltipShow', 'tooltipHide', 'areaClick', 'areaMouseover', 'areaMouseout')
        ;

    scatter
        .size(2.2) // default size
        .sizeDomain([2.2]) // all the same size by default
    ;

    /************************************
     * offset:
     *   'wiggle' (stream)
     *   'zero' (stacked)
     *   'expand' (normalize to 100%)
     *   'silhouette' (simple centered)
     *
     * order:
     *   'inside-out' (stream)
     *   'default' (input order)
     ************************************/

        //============================================================


    function chart(selection) {
        selection.each(function(data) {
            var availableWidth = width - margin.left - margin.right,
                availableHeight = height - margin.top - margin.bottom,
                container = d3.select(this);

            //------------------------------------------------------------
            // Setup Scales

            x = scatter.xScale();
            y = scatter.yScale();

            //------------------------------------------------------------


            // Injecting point index into each point because d3.layout.stack().out does not give index
            // ***Also storing getY(d,i) as stackedY so that it can be set to 0 if series is disabled
            data = data.map(function(aseries, i) {
                aseries.values = aseries.values.map(function(d, j) {
                    d.index = j;
                    d.stackedY = aseries.disabled ? 0 : getY(d,j);
                    return d;
                })
                return aseries;
            });


            data = d3.layout.stack()
                .order(order)
                .offset(offset)
                .values(function(d) { return d.values })  //TODO: make values customizeable in EVERY model in this fashion
                .x(getX)
                .y(function(d) { return d.stackedY })
                .out(function(d, y0, y) {
                    d.display = {
                        y: y,
                        y0: y0
                    };
                })
                (data);


            //------------------------------------------------------------
            // Setup containers and skeleton of chart

            var wrap = container.selectAll('g.nv-wrap.nv-stackedarea').data([data]);
            var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-stackedarea');
            var defsEnter = wrapEnter.append('defs');
            var gEnter = wrapEnter.append('g');
            var g = wrap.select('g');

            gEnter.append('g').attr('class', 'nv-areaWrap');
            gEnter.append('g').attr('class', 'nv-scatterWrap');

            wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');

            //------------------------------------------------------------


            scatter
                .width(availableWidth)
                .height(availableHeight)
                .x(getX)
                .y(function(d) { return d.display.y + d.display.y0 })
                .forceY([0])
                .color(data.map(function(d,i) {
                return d.color || color(d, i);
            }).filter(function(d,i) { return !data[i].disabled }));


            var scatterWrap = g.select('.nv-scatterWrap')
                .datum(data.filter(function(d) { return !d.disabled }))

            d3.transition(scatterWrap).call(scatter);
            //scatterWrap.call(scatter);





            defsEnter.append('clipPath')
                .attr('id', 'nv-edge-clip-' + id)
                .append('rect');

            wrap.select('#nv-edge-clip-' + id + ' rect')
                .attr('width', availableWidth)
                .attr('height', availableHeight);

            g   .attr('clip-path', clipEdge ? 'url(#nv-edge-clip-' + id + ')' : '');




            var area = d3.svg.area()
                .x(function(d,i)  { return x(getX(d,i)) })
                .y0(function(d) { return y(d.display.y0) })
                .y1(function(d) { return y(d.display.y + d.display.y0) })
                .interpolate(interpolate);

            var zeroArea = d3.svg.area()
                .x(function(d,i)  { return x(getX(d,i)) })
                .y0(function(d) { return y(d.display.y0) })
                .y1(function(d) { return y(d.display.y0) });


            var path = g.select('.nv-areaWrap').selectAll('path.nv-area')
                .data(function(d) { return d });
            //.data(function(d) { return d }, function(d) { return d.key });
            path.enter().append('path').attr('class', function(d,i) { return 'nv-area nv-area-' + i })
                .on('mouseover', function(d,i) {
                    d3.select(this).classed('hover', true);
                    dispatch.areaMouseover({
                        point: d,
                        series: d.key,
                        pos: [d3.event.pageX, d3.event.pageY],
                        seriesIndex: i
                    });
                })
                .on('mouseout', function(d,i) {
                    d3.select(this).classed('hover', false);
                    dispatch.areaMouseout({
                        point: d,
                        series: d.key,
                        pos: [d3.event.pageX, d3.event.pageY],
                        seriesIndex: i
                    });
                })
                .on('click', function(d,i) {
                    d3.select(this).classed('hover', false);
                    dispatch.areaClick({
                        point: d,
                        series: d.key,
                        pos: [d3.event.pageX, d3.event.pageY],
                        seriesIndex: i
                    });
                })
            d3.transition(path.exit())
                //path.exit()
                .attr('d', function(d,i) { return zeroArea(d.values,i) })
                .remove();
            path
                .style('fill', function(d,i){ return d.color || color(d, i) })
                .style('stroke', function(d,i){ return d.color || color(d, i) });
            d3.transition(path)
                //path
                .attr('d', function(d,i) { return area(d.values,i) })


            //============================================================
            // Event Handling/Dispatching (in chart's scope)
            //------------------------------------------------------------

            scatter.dispatch.on('elementMouseover.area', function(e) {
                g.select('.nv-chart-' + id + ' .nv-area-' + e.seriesIndex).classed('hover', true);
            });
            scatter.dispatch.on('elementMouseout.area', function(e) {
                g.select('.nv-chart-' + id + ' .nv-area-' + e.seriesIndex).classed('hover', false);
            });

            //============================================================

        });


        return chart;
    }


    //============================================================
    // Event Handling/Dispatching (out of chart's scope)
    //------------------------------------------------------------

    scatter.dispatch.on('elementClick.area', function(e) {
        dispatch.areaClick(e);
    })
    scatter.dispatch.on('elementMouseover.tooltip', function(e) {
        e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top],
            dispatch.tooltipShow(e);
    });
    scatter.dispatch.on('elementMouseout.tooltip', function(e) {
        dispatch.tooltipHide(e);
    });

    //============================================================


    //============================================================
    // Global getters and setters
    //------------------------------------------------------------

    chart.dispatch = dispatch;
    chart.scatter = scatter;

    d3.rebind(chart, scatter, 'interactive', 'size', 'xScale', 'yScale', 'zScale', 'xDomain', 'yDomain', 'sizeDomain', 'forceX', 'forceY', 'forceSize', 'clipVoronoi', 'clipRadius');

    chart.x = function(_) {
        if (!arguments.length) return getX;
        getX = d3.functor(_);
        return chart;
    };

    chart.y = function(_) {
        if (!arguments.length) return getY;
        getY = d3.functor(_);
        return chart;
    }

    chart.margin = function(_) {
        if (!arguments.length) return margin;
        margin.top    = typeof _.top    != 'undefined' ? _.top    : margin.top;
        margin.right  = typeof _.right  != 'undefined' ? _.right  : margin.right;
        margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
        margin.left   = typeof _.left   != 'undefined' ? _.left   : margin.left;
        return chart;
    };

    chart.width = function(_) {
        if (!arguments.length) return width;
        width = _;
        return chart;
    };

    chart.height = function(_) {
        if (!arguments.length) return height;
        height = _;
        return chart;
    };

    chart.clipEdge = function(_) {
        if (!arguments.length) return clipEdge;
        clipEdge = _;
        return chart;
    };

    chart.color = function(_) {
        if (!arguments.length) return color;
        color = nv.utils.getColor(_);
        return chart;
    };

    chart.offset = function(_) {
        if (!arguments.length) return offset;
        offset = _;
        return chart;
    };

    chart.order = function(_) {
        if (!arguments.length) return order;
        order = _;
        return chart;
    };

    //shortcut for offset + order
    chart.style = function(_) {
        if (!arguments.length) return style;
        style = _;

        switch (style) {
            case 'stack':
                chart.offset('zero');
                chart.order('default');
                break;
            case 'stream':
                chart.offset('wiggle');
                chart.order('inside-out');
                break;
            case 'stream-center':
                chart.offset('silhouette');
                chart.order('inside-out');
                break;
            case 'expand':
                chart.offset('expand');
                chart.order('default');
                break;
        }

        return chart;
    };

    chart.interpolate = function(_) {
        if (!arguments.length) return interpolate;
        interpolate = _;
        return interpolate;

    };

    //============================================================


    return chart;
}