123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244 |
- /*
- Copyright (c) 2004-2012, The Dojo Foundation All Rights Reserved.
- Available via Academic Free License >= 2.1 OR the modified BSD license.
- see: http://dojotoolkit.org/license for details
- */
- if(!dojo._hasResource["dojox.charting.plot2d.Candlesticks"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
- dojo._hasResource["dojox.charting.plot2d.Candlesticks"] = true;
- dojo.provide("dojox.charting.plot2d.Candlesticks");
- dojo.require("dojox.charting.plot2d.common");
- dojo.require("dojox.charting.plot2d.Base");
- dojo.require("dojox.lang.utils");
- dojo.require("dojox.lang.functional");
- dojo.require("dojox.lang.functional.reversed");
- (function(){
- var df = dojox.lang.functional, du = dojox.lang.utils,
- dc = dojox.charting.plot2d.common,
- purgeGroup = df.lambda("item.purgeGroup()");
- // Candlesticks are based on the Bars plot type; we expect the following passed
- // as values in a series:
- // { x?, open, close, high, low, mid? }
- // if x is not provided, the array index is used.
- // failing to provide the OHLC values will throw an error.
- dojo.declare("dojox.charting.plot2d.Candlesticks", dojox.charting.plot2d.Base, {
- // summary:
- // A plot that represents typical candlesticks (financial reporting, primarily).
- // Unlike most charts, the Candlestick expects data points to be represented by
- // an object of the form { x?, open, close, high, low, mid? }, where both
- // x and mid are optional parameters. If x is not provided, the index of the
- // data array is used.
- defaultParams: {
- hAxis: "x", // use a horizontal axis named "x"
- vAxis: "y", // use a vertical axis named "y"
- gap: 2, // gap between columns in pixels
- animate: null // animate bars into place
- },
- optionalParams: {
- minBarSize: 1, // minimal candle width in pixels
- maxBarSize: 1, // maximal candle width in pixels
- // theme component
- stroke: {},
- outline: {},
- shadow: {},
- fill: {},
- font: "",
- fontColor: ""
- },
- constructor: function(chart, kwArgs){
- // summary:
- // The constructor for a candlestick chart.
- // chart: dojox.charting.Chart2D
- // The chart this plot belongs to.
- // kwArgs: dojox.charting.plot2d.__BarCtorArgs?
- // An optional keyword arguments object to help define the plot.
- this.opt = dojo.clone(this.defaultParams);
- du.updateWithObject(this.opt, kwArgs);
- du.updateWithPattern(this.opt, kwArgs, this.optionalParams);
- this.series = [];
- this.hAxis = this.opt.hAxis;
- this.vAxis = this.opt.vAxis;
- this.animate = this.opt.animate;
- },
- collectStats: function(series){
- // summary:
- // Collect all statistics for drawing this chart. Since the common
- // functionality only assumes x and y, Candlesticks must create it's own
- // stats (since data has no y value, but open/close/high/low instead).
- // series: dojox.charting.Series[]
- // The data series array to be drawn on this plot.
- // returns: Object
- // Returns an object in the form of { hmin, hmax, vmin, vmax }.
- // we have to roll our own, since we need to use all four passed
- // values to figure out our stats, and common only assumes x and y.
- var stats = dojo.delegate(dc.defaultStats);
- for(var i=0; i<series.length; i++){
- var run = series[i];
- if(!run.data.length){ continue; }
- var old_vmin = stats.vmin, old_vmax = stats.vmax;
- if(!("ymin" in run) || !("ymax" in run)){
- dojo.forEach(run.data, function(val, idx){
- if(val !== null){
- var x = val.x || idx + 1;
- stats.hmin = Math.min(stats.hmin, x);
- stats.hmax = Math.max(stats.hmax, x);
- stats.vmin = Math.min(stats.vmin, val.open, val.close, val.high, val.low);
- stats.vmax = Math.max(stats.vmax, val.open, val.close, val.high, val.low);
- }
- });
- }
- if("ymin" in run){ stats.vmin = Math.min(old_vmin, run.ymin); }
- if("ymax" in run){ stats.vmax = Math.max(old_vmax, run.ymax); }
- }
- return stats; // Object
- },
- getSeriesStats: function(){
- // summary:
- // Calculate the min/max on all attached series in both directions.
- // returns: Object
- // {hmin, hmax, vmin, vmax} min/max in both directions.
- var stats = this.collectStats(this.series);
- stats.hmin -= 0.5;
- stats.hmax += 0.5;
- return stats;
- },
- render: function(dim, offsets){
- // summary:
- // Run the calculations for any axes for this plot.
- // dim: Object
- // An object in the form of { width, height }
- // offsets: Object
- // An object of the form { l, r, t, b}.
- // returns: dojox.charting.plot2d.Candlesticks
- // A reference to this plot for functional chaining.
- if(this.zoom && !this.isDataDirty()){
- return this.performZoom(dim, offsets);
- }
- this.resetEvents();
- this.dirty = this.isDirty();
- if(this.dirty){
- dojo.forEach(this.series, purgeGroup);
- this._eventSeries = {};
- this.cleanGroup();
- var s = this.group;
- df.forEachRev(this.series, function(item){ item.cleanGroup(s); });
- }
- var t = this.chart.theme, f, gap, width,
- ht = this._hScaler.scaler.getTransformerFromModel(this._hScaler),
- vt = this._vScaler.scaler.getTransformerFromModel(this._vScaler),
- baseline = Math.max(0, this._vScaler.bounds.lower),
- baselineHeight = vt(baseline),
- events = this.events();
- f = dc.calculateBarSize(this._hScaler.bounds.scale, this.opt);
- gap = f.gap;
- width = f.size;
- for(var i = this.series.length - 1; i >= 0; --i){
- var run = this.series[i];
- if(!this.dirty && !run.dirty){
- t.skip();
- this._reconnectEvents(run.name);
- continue;
- }
- run.cleanGroup();
- var theme = t.next("candlestick", [this.opt, run]), s = run.group,
- eventSeries = new Array(run.data.length);
- for(var j = 0; j < run.data.length; ++j){
- var v = run.data[j];
- if(v !== null){
- var finalTheme = t.addMixin(theme, "candlestick", v, true);
- // calculate the points we need for OHLC
- var x = ht(v.x || (j+0.5)) + offsets.l + gap,
- y = dim.height - offsets.b,
- open = vt(v.open),
- close = vt(v.close),
- high = vt(v.high),
- low = vt(v.low);
- if("mid" in v){
- var mid = vt(v.mid);
- }
- if(low > high){
- var tmp = high;
- high = low;
- low = tmp;
- }
- if(width >= 1){
- // draw the line and rect, set up as a group and pass that to the events.
- var doFill = open > close;
- var line = { x1: width/2, x2: width/2, y1: y - high, y2: y - low },
- rect = {
- x: 0, y: y-Math.max(open, close),
- width: width, height: Math.max(doFill ? open-close : close-open, 1)
- };
- shape = s.createGroup();
- shape.setTransform({dx: x, dy: 0 });
- var inner = shape.createGroup();
- inner.createLine(line).setStroke(finalTheme.series.stroke);
- inner.createRect(rect).setStroke(finalTheme.series.stroke).
- setFill(doFill ? finalTheme.series.fill : "white");
- if("mid" in v){
- // add the mid line.
- inner.createLine({
- x1: (finalTheme.series.stroke.width||1), x2: width - (finalTheme.series.stroke.width || 1),
- y1: y - mid, y2: y - mid
- }).setStroke(doFill ? "white" : finalTheme.series.stroke);
- }
- // TODO: double check this.
- run.dyn.fill = finalTheme.series.fill;
- run.dyn.stroke = finalTheme.series.stroke;
- if(events){
- var o = {
- element: "candlestick",
- index: j,
- run: run,
- shape: inner,
- x: x,
- y: y-Math.max(open, close),
- cx: width/2,
- cy: (y-Math.max(open, close)) + (Math.max(doFill ? open-close : close-open, 1)/2),
- width: width,
- height: Math.max(doFill ? open-close : close-open, 1),
- data: v
- };
- this._connectEvents(o);
- eventSeries[j] = o;
- }
- }
- if(this.animate){
- this._animateCandlesticks(shape, y - low, high - low);
- }
- }
- }
- this._eventSeries[run.name] = eventSeries;
- run.dirty = false;
- }
- this.dirty = false;
- return this; // dojox.charting.plot2d.Candlesticks
- },
- _animateCandlesticks: function(shape, voffset, vsize){
- dojox.gfx.fx.animateTransform(dojo.delegate({
- shape: shape,
- duration: 1200,
- transform: [
- {name: "translate", start: [0, voffset - (voffset/vsize)], end: [0, 0]},
- {name: "scale", start: [1, 1/vsize], end: [1, 1]},
- {name: "original"}
- ]
- }, this.animate)).play();
- }
- });
- })();
- }
|