first commit
[wnstats.git] / public / javascripts / jqplot / plugins / jqplot.ohlcRenderer.js
blob2f143f1b00b735223abc74e7764ab9a56385a20f
1 /**
2  * jqPlot
3  * Pure JavaScript plotting plugin using jQuery
4  *
5  * Version: 1.0.4
6  * Revision: 1121
7  *
8  * Copyright (c) 2009-2012 Chris Leonello
9  * jqPlot is currently available for use in all personal or commercial projects 
10  * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL 
11  * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can 
12  * choose the license that best suits your project and use it accordingly. 
13  *
14  * Although not required, the author would appreciate an email letting him 
15  * know of any substantial use of jqPlot.  You can reach the author at: 
16  * chris at jqplot dot com or see http://www.jqplot.com/info.php .
17  *
18  * If you are feeling kind and generous, consider supporting the project by
19  * making a donation at: http://www.jqplot.com/donate.php .
20  *
21  * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
22  *
23  *     version 2007.04.27
24  *     author Ash Searle
25  *     http://hexmen.com/blog/2007/03/printf-sprintf/
26  *     http://hexmen.com/js/sprintf.js
27  *     The author (Ash Searle) has placed this code in the public domain:
28  *     "This code is unrestricted: you are free to use it however you like."
29  * 
30  */
31 (function($) {
32     /**
33      * Class: $.jqplot.OHLCRenderer
34      * jqPlot Plugin to draw Open Hi Low Close, Candlestick and Hi Low Close charts.
35      * 
36      * To use this plugin, include the renderer js file in 
37      * your source:
38      * 
39      * > <script type="text/javascript" src="plugins/jqplot.ohlcRenderer.js"></script>
40      * 
41      * You will most likely want to use a date axis renderer
42      * for the x axis also, so include the date axis render js file also:
43      * 
44      * > <script type="text/javascript" src="plugins/jqplot.dateAxisRenderer.js"></script>
45      * 
46      * Then you set the renderer in the series options on your plot:
47      * 
48      * > series: [{renderer:$.jqplot.OHLCRenderer}]
49      * 
50      * For OHLC and candlestick charts, data should be specified
51      * like so:
52      * 
53      * > dat = [['07/06/2009',138.7,139.68,135.18,135.4], ['06/29/2009',143.46,144.66,139.79,140.02], ...]
54      * 
55      * If the data array has only 4 values per point instead of 5,
56      * the renderer will create a Hi Low Close chart instead.  In that case,
57      * data should be supplied like:
58      * 
59      * > dat = [['07/06/2009',139.68,135.18,135.4], ['06/29/2009',144.66,139.79,140.02], ...]
60      * 
61      * To generate a candlestick chart instead of an OHLC chart,
62      * set the "candlestick" option to true:
63      * 
64      * > series: [{renderer:$.jqplot.OHLCRenderer, rendererOptions:{candleStick:true}}],
65      * 
66      */
67     $.jqplot.OHLCRenderer = function(){
68         // subclass line renderer to make use of some of it's methods.
69         $.jqplot.LineRenderer.call(this);
70         // prop: candleStick
71         // true to render chart as candleStick.
72         // Must have an open price, cannot be a hlc chart.
73         this.candleStick = false;
74         // prop: tickLength
75         // length of the line in pixels indicating open and close price.
76         // Default will auto calculate based on plot width and 
77         // number of points displayed.
78         this.tickLength = 'auto';
79         // prop: bodyWidth
80         // width of the candlestick body in pixels.  Default will auto calculate
81         // based on plot width and number of candlesticks displayed.
82         this.bodyWidth = 'auto';
83         // prop: openColor
84         // color of the open price tick mark.  Default is series color.
85         this.openColor = null;
86         // prop: closeColor
87         // color of the close price tick mark.  Default is series color.
88         this.closeColor = null;
89         // prop: wickColor
90         // color of the hi-lo line thorugh the candlestick body.
91         // Default is the series color.
92         this.wickColor = null;
93         // prop: fillUpBody
94         // true to render an "up" day (close price greater than open price)
95         // with a filled candlestick body.
96         this.fillUpBody = false;
97         // prop: fillDownBody
98         // true to render a "down" day (close price lower than open price)
99         // with a filled candlestick body.
100         this.fillDownBody = true;
101         // prop: upBodyColor
102         // Color of candlestick body of an "up" day.  Default is series color.
103         this.upBodyColor = null;
104         // prop: downBodyColor
105         // Color of candlestick body on a "down" day.  Default is series color.
106         this.downBodyColor = null;
107         // prop: hlc
108         // true if is a hi-low-close chart (no open price).
109         // This is determined automatically from the series data.
110         this.hlc = false;
111         // prop: lineWidth
112         // Width of the hi-low line and open/close ticks.
113         // Must be set in the rendererOptions for the series.
114         this.lineWidth = 1.5;
115         this._tickLength;
116         this._bodyWidth;
117     };
118     
119     $.jqplot.OHLCRenderer.prototype = new $.jqplot.LineRenderer();
120     $.jqplot.OHLCRenderer.prototype.constructor = $.jqplot.OHLCRenderer;
121     
122     // called with scope of series.
123     $.jqplot.OHLCRenderer.prototype.init = function(options) {
124         options = options || {};
125         // lineWidth has to be set on the series, changes in renderer
126         // constructor have no effect.  set the default here
127         // if no renderer option for lineWidth is specified.
128         this.lineWidth = options.lineWidth || 1.5;
129         $.jqplot.LineRenderer.prototype.init.call(this, options);
130         this._type = 'ohlc';
131         // set the yaxis data bounds here to account for hi and low values
132         var db = this._yaxis._dataBounds;
133         var d = this._plotData;
134         // if data points have less than 5 values, force a hlc chart.
135         if (d[0].length < 5) {
136             this.renderer.hlc = true;
138             for (var j=0; j<d.length; j++) { 
139                 if (d[j][2] < db.min || db.min == null) {
140                     db.min = d[j][2];
141                 }
142                 if (d[j][1] > db.max || db.max == null) {
143                     db.max = d[j][1];
144                 }             
145             }
146         }
147         else {
148             for (var j=0; j<d.length; j++) { 
149                 if (d[j][3] < db.min || db.min == null) {
150                     db.min = d[j][3];
151                 }
152                 if (d[j][2] > db.max || db.max == null) {
153                     db.max = d[j][2];
154                 }             
155             }
156         }
157         
158     };
159     
160     // called within scope of series.
161     $.jqplot.OHLCRenderer.prototype.draw = function(ctx, gd, options) {
162         var d = this.data;
163         var xmin = this._xaxis.min;
164         var xmax = this._xaxis.max;
165         // index of last value below range of plot.
166         var xminidx = 0;
167         // index of first value above range of plot.
168         var xmaxidx = d.length;
169         var xp = this._xaxis.series_u2p;
170         var yp = this._yaxis.series_u2p;
171         var i, prevColor, ops, b, h, w, a, points;
172         var o;
173         var r = this.renderer;
174         var opts = (options != undefined) ? options : {};
175         var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow;
176         var fill = (opts.fill != undefined) ? opts.fill : this.fill;
177         var fillAndStroke = (opts.fillAndStroke != undefined) ? opts.fillAndStroke : this.fillAndStroke;
178         r.bodyWidth = (opts.bodyWidth != undefined) ? opts.bodyWidth : r.bodyWidth;
179         r.tickLength = (opts.tickLength != undefined) ? opts.tickLength : r.tickLength;
180         ctx.save();
181         if (this.show) {
182             var x, open, hi, low, close;
183             // need to get widths based on number of points shown,
184             // not on total number of points.  Use the results 
185             // to speed up drawing in next step.
186             for (var i=0; i<d.length; i++) {
187                 if (d[i][0] < xmin) {
188                     xminidx = i;
189                 }
190                 else if (d[i][0] < xmax) {
191                     xmaxidx = i+1;
192                 }
193             }
195             var dwidth = this.gridData[xmaxidx-1][0] - this.gridData[xminidx][0];
196             var nvisiblePoints = xmaxidx - xminidx;
197             try {
198                 var dinterval = Math.abs(this._xaxis.series_u2p(parseInt(this._xaxis._intervalStats[0].sortedIntervals[0].interval, 10)) - this._xaxis.series_u2p(0)); 
199             }
201             catch (e) {
202                 var dinterval = dwidth / nvisiblePoints;
203             }
204             
205             if (r.candleStick) {
206                 if (typeof(r.bodyWidth) == 'number') {
207                     r._bodyWidth = r.bodyWidth;
208                 }
209                 else {
210                     r._bodyWidth = Math.min(20, dinterval/1.65);
211                 }
212             }
213             else {
214                 if (typeof(r.tickLength) == 'number') {
215                     r._tickLength = r.tickLength;
216                 }
217                 else {
218                     r._tickLength = Math.min(10, dinterval/3.5);
219                 }
220             }
221             
222             for (var i=xminidx; i<xmaxidx; i++) {
223                 x = xp(d[i][0]);
224                 if (r.hlc) {
225                     open = null;
226                     hi = yp(d[i][1]);
227                     low = yp(d[i][2]);
228                     close = yp(d[i][3]);
229                 }
230                 else {
231                     open = yp(d[i][1]);
232                     hi = yp(d[i][2]);
233                     low = yp(d[i][3]);
234                     close = yp(d[i][4]);
235                 }
236                 o = {};
237                 if (r.candleStick && !r.hlc) {
238                     w = r._bodyWidth;
239                     a = x - w/2;
240                     // draw candle
241                     // determine if candle up or down
242                     // up, remember grid coordinates increase downward
243                     if (close < open) {
244                         // draw wick
245                         if (r.wickColor) {
246                             o.color = r.wickColor;
247                         }
248                         else if (r.downBodyColor) {
249                             o.color = r.upBodyColor;
250                         }
251                         ops = $.extend(true, {}, opts, o);
252                         r.shapeRenderer.draw(ctx, [[x, hi], [x, close]], ops); 
253                         r.shapeRenderer.draw(ctx, [[x, open], [x, low]], ops); 
254                         o = {};
255                         b = close;
256                         h = open - close;
257                         // if color specified, use it
258                         if (r.fillUpBody) {
259                             o.fillRect = true;
260                         }
261                         else {
262                             o.strokeRect = true;
263                             w = w - this.lineWidth;
264                             a = x - w/2;
265                         }
266                         if (r.upBodyColor) {
267                             o.color = r.upBodyColor;
268                             o.fillStyle = r.upBodyColor;
269                         }
270                         points = [a, b, w, h];
271                     }
272                     // down
273                     else if (close >  open) {
274                         // draw wick
275                         if (r.wickColor) {
276                             o.color = r.wickColor;
277                         }
278                         else if (r.downBodyColor) {
279                             o.color = r.downBodyColor;
280                         }
281                         ops = $.extend(true, {}, opts, o);
282                         r.shapeRenderer.draw(ctx, [[x, hi], [x, open]], ops); 
283                         r.shapeRenderer.draw(ctx, [[x, close], [x, low]], ops);
284                          
285                         o = {};
286                         
287                         b = open;
288                         h = close - open;
289                         // if color specified, use it
290                         if (r.fillDownBody) {
291                             o.fillRect = true;
292                         }
293                         else {
294                             o.strokeRect = true;
295                             w = w - this.lineWidth;
296                             a = x - w/2;
297                         }
298                         if (r.downBodyColor) {
299                             o.color = r.downBodyColor;
300                             o.fillStyle = r.downBodyColor;
301                         }
302                         points = [a, b, w, h];
303                     }
304                     // even, open = close
305                     else  {
306                         // draw wick
307                         if (r.wickColor) {
308                             o.color = r.wickColor;
309                         }
310                         ops = $.extend(true, {}, opts, o);
311                         r.shapeRenderer.draw(ctx, [[x, hi], [x, low]], ops); 
312                         o = {};
313                         o.fillRect = false;
314                         o.strokeRect = false;
315                         a = [x - w/2, open];
316                         b = [x + w/2, close];
317                         w = null;
318                         h = null;
319                         points = [a, b];
320                     }
321                     ops = $.extend(true, {}, opts, o);
322                     r.shapeRenderer.draw(ctx, points, ops);
323                 }
324                 else {
325                     prevColor = opts.color;
326                     if (r.openColor) {
327                         opts.color = r.openColor;
328                     }
329                     // draw open tick
330                     if (!r.hlc) {
331                         r.shapeRenderer.draw(ctx, [[x-r._tickLength, open], [x, open]], opts);    
332                     }
333                     opts.color = prevColor;
334                     // draw wick
335                     if (r.wickColor) {
336                         opts.color = r.wickColor;
337                     }
338                     r.shapeRenderer.draw(ctx, [[x, hi], [x, low]], opts); 
339                     opts.color  = prevColor;
340                     // draw close tick
341                     if (r.closeColor) {
342                         opts.color = r.closeColor;
343                     }
344                     r.shapeRenderer.draw(ctx, [[x, close], [x+r._tickLength, close]], opts); 
345                     opts.color = prevColor;
346                 }
347             }
348         }
349         
350         ctx.restore();
351     };  
352     
353     $.jqplot.OHLCRenderer.prototype.drawShadow = function(ctx, gd, options) {
354         // This is a no-op, shadows drawn with lines.
355     };
356     
357     // called with scope of plot.
358     $.jqplot.OHLCRenderer.checkOptions = function(target, data, options) {
359         // provide some sensible highlighter options by default
360         // These aren't good for hlc, only for ohlc or candlestick
361         if (!options.highlighter) {
362             options.highlighter = {
363                 showMarker:false,
364                 tooltipAxes: 'y',
365                 yvalues: 4,
366                 formatString:'<table class="jqplot-highlighter"><tr><td>date:</td><td>%s</td></tr><tr><td>open:</td><td>%s</td></tr><tr><td>hi:</td><td>%s</td></tr><tr><td>low:</td><td>%s</td></tr><tr><td>close:</td><td>%s</td></tr></table>'
367             };
368         }
369     };
370     
371     //$.jqplot.preInitHooks.push($.jqplot.OHLCRenderer.checkOptions);
372     
373 })(jQuery);