source: Dev/trunk/src/client/dojox/charting/plot2d/Candlesticks.js

Last change on this file was 483, checked in by hendrikvanantwerpen, 11 years ago

Added Dojo 1.9.3 release.

File size: 8.4 KB
RevLine 
[483]1define(["dojo/_base/lang", "dojo/_base/declare", "dojo/_base/array", "dojo/has", "./CartesianBase", "./_PlotEvents", "./common",
2                "dojox/lang/functional", "dojox/lang/functional/reversed", "dojox/lang/utils", "dojox/gfx/fx"],
3        function(lang, declare, arr, has, CartesianBase, _PlotEvents, dc, df, dfr, du, fx){
4
5        var purgeGroup = dfr.lambda("item.purgeGroup()");
6
7        //      Candlesticks are based on the Bars plot type; we expect the following passed
8        //      as values in a series:
9        //      { x?, open, close, high, low, mid? }
10        //      if x is not provided, the array index is used.
11        //      failing to provide the OHLC values will throw an error.
12        return declare("dojox.charting.plot2d.Candlesticks", [CartesianBase, _PlotEvents], {
13                // summary:
14                //              A plot that represents typical candlesticks (financial reporting, primarily).
15                //              Unlike most charts, the Candlestick expects data points to be represented by
16                //              an object of the form { x?, open, close, high, low, mid? }, where both
17                //              x and mid are optional parameters.  If x is not provided, the index of the
18                //              data array is used.
19                defaultParams: {
20                        gap:    2,              // gap between columns in pixels
21                        animate: null   // animate bars into place
22                },
23                optionalParams: {
24                        minBarSize:     1,      // minimal candle width in pixels
25                        maxBarSize:     1,      // maximal candle width in pixels
26                        // theme component
27                        stroke:         {},
28                        outline:        {},
29                        shadow:         {},
30                        fill:           {},
31                        font:           "",
32                        fontColor:      ""
33                },
34
35                constructor: function(chart, kwArgs){
36                        // summary:
37                        //              The constructor for a candlestick chart.
38                        // chart: dojox/charting/Chart
39                        //              The chart this plot belongs to.
40                        // kwArgs: dojox.charting.plot2d.__BarCtorArgs?
41                        //              An optional keyword arguments object to help define the plot.
42                        this.opt = lang.clone(this.defaultParams);
43                        du.updateWithObject(this.opt, kwArgs);
44                        du.updateWithPattern(this.opt, kwArgs, this.optionalParams);
45                        this.animate = this.opt.animate;
46                },
47
48                collectStats: function(series){
49                        // summary:
50                        //              Collect all statistics for drawing this chart.  Since the common
51                        //              functionality only assumes x and y, Candlesticks must create it's own
52                        //              stats (since data has no y value, but open/close/high/low instead).
53                        // series: dojox.charting.Series[]
54                        //              The data series array to be drawn on this plot.
55                        // returns: Object
56                        //              Returns an object in the form of { hmin, hmax, vmin, vmax }.
57
58                        //      we have to roll our own, since we need to use all four passed
59                        //      values to figure out our stats, and common only assumes x and y.
60                        var stats = lang.delegate(dc.defaultStats);
61                        for(var i=0; i<series.length; i++){
62                                var run = series[i];
63                                if(!run.data.length){ continue; }
64                                var old_vmin = stats.vmin, old_vmax = stats.vmax;
65                                if(!("ymin" in run) || !("ymax" in run)){
66                                        arr.forEach(run.data, function(val, idx){
67                                                if(val !== null){
68                                                        var x = val.x || idx + 1;
69                                                        stats.hmin = Math.min(stats.hmin, x);
70                                                        stats.hmax = Math.max(stats.hmax, x);
71                                                        stats.vmin = Math.min(stats.vmin, val.open, val.close, val.high, val.low);
72                                                        stats.vmax = Math.max(stats.vmax, val.open, val.close, val.high, val.low);
73                                                }
74                                        });
75                                }
76                                if("ymin" in run){ stats.vmin = Math.min(old_vmin, run.ymin); }
77                                if("ymax" in run){ stats.vmax = Math.max(old_vmax, run.ymax); }
78                        }
79                        return stats;   //      Object
80                },
81
82                getSeriesStats: function(){
83                        // summary:
84                        //              Calculate the min/max on all attached series in both directions.
85                        // returns: Object
86                        //              {hmin, hmax, vmin, vmax} min/max in both directions.
87                        var stats = this.collectStats(this.series);
88                        stats.hmin -= 0.5;
89                        stats.hmax += 0.5;
90                        return stats; // Object
91                },
92
93                render: function(dim, offsets){
94                        // summary:
95                        //              Run the calculations for any axes for this plot.
96                        // dim: Object
97                        //              An object in the form of { width, height }
98                        // offsets: Object
99                        //              An object of the form { l, r, t, b}.
100                        // returns: dojox/charting/plot2d/Candlesticks
101                        //              A reference to this plot for functional chaining.
102                        if(this.zoom && !this.isDataDirty()){
103                                return this.performZoom(dim, offsets);
104                        }
105                        this.resetEvents();
106                        this.dirty = this.isDirty();
107                        var s;
108                        if(this.dirty){
109                                arr.forEach(this.series, purgeGroup);
110                                this._eventSeries = {};
111                                this.cleanGroup();
112                                s = this.getGroup();
113                                df.forEachRev(this.series, function(item){ item.cleanGroup(s); });
114                        }
115                        var t = this.chart.theme, f, gap, width,
116                                ht = this._hScaler.scaler.getTransformerFromModel(this._hScaler),
117                                vt = this._vScaler.scaler.getTransformerFromModel(this._vScaler),
118                                events = this.events();
119                        f = dc.calculateBarSize(this._hScaler.bounds.scale, this.opt);
120                        gap = f.gap;
121                        width = f.size;
122                        for(var i = this.series.length - 1; i >= 0; --i){
123                                var run = this.series[i];
124                                if(!this.dirty && !run.dirty){
125                                        t.skip();
126                                        this._reconnectEvents(run.name);
127                                        continue;
128                                }
129                                run.cleanGroup();
130                                s = run.group;
131                                var theme = t.next("candlestick", [this.opt, run]),
132                                        eventSeries = new Array(run.data.length);
133                                for(var j = 0; j < run.data.length; ++j){
134                                        var v = run.data[j];
135                                        if(v !== null){
136                                                var finalTheme = t.addMixin(theme, "candlestick", v, true);
137
138                                                //      calculate the points we need for OHLC
139                                                var x = ht(v.x || (j+0.5)) + offsets.l + gap,
140                                                        y = dim.height - offsets.b,
141                                                        open = vt(v.open),
142                                                        close = vt(v.close),
143                                                        high = vt(v.high),
144                                                        low = vt(v.low);
145                                                if("mid" in v){
146                                                        var mid = vt(v.mid);
147                                                }
148                                                if(low > high){
149                                                        var tmp = high;
150                                                        high = low;
151                                                        low = tmp;
152                                                }
153
154                                                if(width >= 1){
155                                                        //      draw the line and rect, set up as a group and pass that to the events.
156                                                        var doFill = open > close;
157                                                        var line = { x1: width/2, x2: width/2, y1: y - high, y2: y - low },
158                                                                rect = {
159                                                                        x: 0, y: y-Math.max(open, close),
160                                                                        width: width, height: Math.max(doFill ? open-close : close-open, 1)
161                                                                };
162                                                        var shape = s.createGroup();
163                                                        shape.setTransform({dx: x, dy: 0 });
164                                                        var inner = shape.createGroup();
165                                                        inner.createLine(line).setStroke(finalTheme.series.stroke);
166                                                        inner.createRect(rect).setStroke(finalTheme.series.stroke).
167                                                                setFill(doFill ? finalTheme.series.fill : "white");
168                                                        if("mid" in v){
169                                                                //      add the mid line.
170                                                                inner.createLine({
171                                                                        x1: (finalTheme.series.stroke.width||1), x2: width - (finalTheme.series.stroke.width || 1),
172                                                                        y1: y - mid, y2: y - mid
173                                                                }).setStroke(doFill ? "white" : finalTheme.series.stroke);
174                                                        }
175
176                                                        // TODO: double check this.
177                                                        run.dyn.fill   = finalTheme.series.fill;
178                                                        run.dyn.stroke = finalTheme.series.stroke;
179                                                        if(events){
180                                                                var o = {
181                                                                        element: "candlestick",
182                                                                        index:   j,
183                                                                        run:     run,
184                                                                        shape:   inner,
185                                                                        x:       x,
186                                                                        y:       y-Math.max(open, close),
187                                                                        cx:              width/2,
188                                                                        cy:              (y-Math.max(open, close)) + (Math.max(doFill ? open-close : close-open, 1)/2),
189                                                                        width:   width,
190                                                                        height:  Math.max(doFill ? open-close : close-open, 1),
191                                                                        data:    v
192                                                                };
193                                                                this._connectEvents(o);
194                                                                eventSeries[j] = o;
195                                                        }
196                                                }
197                                                if(this.animate){
198                                                        this._animateCandlesticks(shape, y - low, high - low);
199                                                }
200                                        }
201                                }
202                                this._eventSeries[run.name] = eventSeries;
203                                run.dirty = false;
204                        }
205                        this.dirty = false;
206                        // chart mirroring starts
207                        if(has("dojo-bidi")){
208                                this._checkOrientation(this.group, dim, offsets);
209                        }
210                        // chart mirroring ends
211                        return this;    //      dojox/charting/plot2d/Candlesticks
212                },
213
214                tooltipFunc: function(o){
215                        return '<table cellpadding="1" cellspacing="0" border="0" style="font-size:0.9em;">'
216                                                + '<tr><td>Open:</td><td align="right"><strong>' + o.data.open + '</strong></td></tr>'
217                                                + '<tr><td>High:</td><td align="right"><strong>' + o.data.high + '</strong></td></tr>'
218                                                + '<tr><td>Low:</td><td align="right"><strong>' + o.data.low + '</strong></td></tr>'
219                                                + '<tr><td>Close:</td><td align="right"><strong>' + o.data.close + '</strong></td></tr>'
220                                                + (o.data.mid !== undefined ? '<tr><td>Mid:</td><td align="right"><strong>' + o.data.mid + '</strong></td></tr>' : '')
221                                                + '</table>';
222                },
223
224                _animateCandlesticks: function(shape, voffset, vsize){
225                        fx.animateTransform(lang.delegate({
226                                shape: shape,
227                                duration: 1200,
228                                transform: [
229                                        {name: "translate", start: [0, voffset - (voffset/vsize)], end: [0, 0]},
230                                        {name: "scale", start: [1, 1/vsize], end: [1, 1]},
231                                        {name: "original"}
232                                ]
233                        }, this.animate)).play();
234                }
235        });
236});
Note: See TracBrowser for help on using the repository browser.