dojox
Version:
Dojo eXtensions, a rollup of many useful sub-projects and varying states of maturity – from very stable and robust, to alpha and experimental. See individual projects contain README files for details.
357 lines (330 loc) • 12.4 kB
JavaScript
define(["dojo/_base/lang", "dojo/_base/declare", "dojo/_base/array", "dojo/sniff",
"./CartesianBase", "./common", "dojox/lang/utils", "dojox/gfx/fx"],
function(lang, declare, arr, has, CartesianBase, dc, du, fx){
var sortTicks = function(a,b){return a.value - b.value};
/*=====
declare("dojox.charting.plot2d.__GridCtorArgs", dojox.charting.plot2d.__CartesianCtorArgs, {
// summary:
// A special keyword arguments object that is specific to a grid "plot".
// majorHLine: dojox.gfx.Stroke?
// An optional dojox.gfx.Stroke for a major horizontal line. By default major lines use major tick stroke.
majorHLine:undefined,
// minorHLine: dojox.gfx.Stroke?
// An optional dojox.gfx.Stroke for a minor horizontal line. By default minor lines use minor tick stroke.
minorHLine:undefined,
// majorVLine: dojox.gfx.Stroke?
// An optional dojox.gfx.Stroke for a major vertical line. By default major lines use major tick stroke.
majorVLine:undefined,
// minorVLine: dojox.gfx.Stroke?
// An optional dojox.gfx.Stroke for a minor vertical line. By default major lines use major tick stroke.
minorVLine:undefined,
// hFill: dojox.gfx.Fill?
// An optional dojox.gfx.Fill used to fill every other horizontal stripe created by grid lines.
hFill: undefined,
// hAlternateFill: dojox.gfx.Fill?
// An optional dojox.gfx.Fill used to fill alternating horizontal stripe created by grid lines not filled by `hFill`.
hAlternateFill: undefined,
// vFill: dojox.gfx.Fill?
// An optional dojox.gfx.Fill used to fill every other vertical stripe created by grid lines.
vFill: undefined,
// vAlternateFill: dojox.gfx.Fill?
// An optional dojox.gfx.Fill used to fill alternating vertical stripe created by grid lines not filled by `vFill`.
vAlternateFill: undefined,
// hMajorLines: Boolean?
// Whether to show lines at the major ticks along the horizontal axis. Default is true.
hMajorLines: true,
// hMinorLines: Boolean?
// Whether to show lines at the minor ticks along the horizontal axis. Default is false.
hMinorLines: false,
// vMajorLines: Boolean?
// Whether to show lines at the major ticks along the vertical axis. Default is true.
vMajorLines: true,
// vMinorLines: Boolean?
// Whether to show lines at the major ticks along the vertical axis. Default is false.
vMinorLines: false,
// hStripes: Boolean?
// Whether to show horizontal stripes. Default is false.
hStripes: false,
// vStripes: Boolean?
// Whether to show vertical stripes. Default is false.
vStripes: false,
// enableCache: Boolean?
// Whether the grid lines are cached from one rendering to another. This improves the rendering performance of
// successive rendering but penalize the first rendering. Default false.
enableCache: false,
// renderOnAxis: Boolean?
// Whether or not the grid is rendered when drawn at horizontal or vertical axis position. Default is true.
renderOnAxis: true
});
=====*/
return declare("dojox.charting.plot2d.Grid", CartesianBase, {
// summary:
// A "faux" plot that can be placed behind other plots to represent
// a grid against which other plots can be easily measured.
defaultParams: {
hMajorLines: true, // draw horizontal major lines
hMinorLines: false, // draw horizontal minor lines
vMajorLines: true, // draw vertical major lines
vMinorLines: false, // draw vertical minor lines
hStripes: false, // draw vertical stripes
vStripes: false, // draw vertical stripes
animate: null, // animate bars into place
enableCache: false,
renderOnAxis: true
},
optionalParams: {
majorHLine: {},
minorHLine: {},
majorVLine: {},
minorVLine: {},
hFill: {},
vFill: {},
hAlternateFill: {},
vAlternateFill: {}
},
constructor: function(chart, kwArgs){
// summary:
// Create the faux Grid plot.
// chart: dojox/charting/Chart
// The chart this plot belongs to.
// kwArgs: dojox.charting.plot2d.__GridCtorArgs?
// An optional keyword arguments object to help define the parameters of the underlying grid.
this.opt = lang.clone(this.defaultParams);
du.updateWithObject(this.opt, kwArgs);
du.updateWithPattern(this.opt, kwArgs, this.optionalParams);
this.animate = this.opt.animate;
if(this.opt.enableCache){
this._lineFreePool = [];
this._lineUsePool = [];
this._rectFreePool = [];
this._rectUsePool = [];
}
},
addSeries: function(run){
// summary:
// Ignored but included as a dummy method.
// returns: dojox/charting/plot2d/Grid
// The reference to this plot for functional chaining.
return this; // dojox/charting/plot2d/Grid
},
getSeriesStats: function(){
// summary:
// Returns default stats (irrelevant for this type of plot).
// returns: Object
// {hmin, hmax, vmin, vmax} min/max in both directions.
return lang.delegate(dc.defaultStats); // Object
},
cleanGroup: function(){
this.inherited(arguments);
if(this.opt.enableCache){
this._lineFreePool = this._lineFreePool.concat(this._lineUsePool);
this._lineUsePool = [];
this._rectFreePool = this._rectFreePool.concat(this._rectUsePool);
this._rectUsePool = [];
}
},
createLine: function(creator, params){
var line;
if(this.opt.enableCache && this._lineFreePool.length > 0){
line = this._lineFreePool.pop();
line.setShape(params);
// was cleared, add it back
creator.add(line);
}else{
line = creator.createLine(params);
}
if(this.opt.enableCache){
this._lineUsePool.push(line);
}
return line;
},
createRect: function(creator, params){
var rect;
if(this.opt.enableCache && this._rectFreePool.length > 0){
rect = this._rectFreePool.pop();
rect.setShape(params);
// was cleared, add it back
creator.add(rect);
}else{
rect = creator.createRect(params);
}
if(this.opt.enableCache){
this._rectUsePool.push(rect);
}
return rect;
},
render: function(dim, offsets){
// summary:
// Render the plot on the chart.
// dim: Object
// An object of the form { width, height }.
// offsets: Object
// An object of the form { l, r, t, b }.
// returns: dojox/charting/plot2d/Grid
// A reference to this plot for functional chaining.
if(this.zoom){
return this.performZoom(dim, offsets);
}
this.dirty = this.isDirty();
if(!this.dirty){ return this; }
this.cleanGroup();
var s = this.getGroup(), ta = this.chart.theme, lineStroke, ticks;
if((has("ios") && has("ios") < 6) || has("android") || (has("safari") && !has("ios"))){
// clipping seems buggy in some mobile Webkit browser and Safari desktop
// it does not clip correctly if only lines are present => create a invisible rectangle...
var w = Math.max(0, dim.width - offsets.l - offsets.r),
h = Math.max(0, dim.height - offsets.t - offsets.b);
s.createRect({ x: offsets.l, y: offsets.t, width: w, height: h});
}
if(this._vAxis){
// draw horizontal stripes and lines
ticks = this._vAxis.getTicks();
var vScaler = this._vAxis.getScaler();
if(ticks != null && vScaler != null){
var vt = vScaler.scaler.getTransformerFromModel(vScaler);
if(this.opt.hStripes){
this._renderHRect(ticks, ta.grid, dim, offsets, vScaler, vt);
}
if(this.opt.hMinorLines){
lineStroke = this.opt.minorHLine || (ta.grid && ta.grid.minorLine) || ta.axis.minorTick;
this._renderHLines(ticks.minor, lineStroke, dim, offsets, vScaler, vt);
}
if(this.opt.hMajorLines){
lineStroke = this.opt.majorHLine || (ta.grid && ta.grid.majorLine) || ta.axis.majorTick;
this._renderHLines(ticks.major, lineStroke, dim, offsets, vScaler, vt);
}
}
}
if(this._hAxis){
// draw vertical stripes and lines
ticks = this._hAxis.getTicks();
var hScaler = this._hAxis.getScaler();
if(ticks != null && hScaler != null){
var ht = hScaler.scaler.getTransformerFromModel(hScaler);
if(this.opt.vStripes){
this._renderVRect(ticks, ta.grid, dim, offsets, hScaler, ht);
}
if(ticks && this.opt.vMinorLines){
lineStroke = this.opt.minorVLine || (ta.grid && ta.grid.minorLine) || ta.axis.minorTick;
this._renderVLines(ticks.minor, lineStroke, dim, offsets, hScaler, ht);
}
if(ticks && this.opt.vMajorLines){
lineStroke = this.opt.majorVLine || (ta.grid && ta.grid.majorLine) || ta.axis.majorTick;
this._renderVLines(ticks.major, lineStroke, dim, offsets, hScaler, ht);
}
}
}
this.dirty = false;
return this; // dojox/charting/plot2d/Grid
},
_renderHLines: function(ticks, lineStroke, dim, offsets, vScaler, vt){
var s = this.getGroup();
arr.forEach(ticks, function(tick){
if(!this.opt.renderOnAxis && tick.value == (this._vAxis.opt.leftBottom?vScaler.bounds.from:vScaler.bounds.to)){
return;
}
var y = dim.height - offsets.b - vt(tick.value);
var hLine = this.createLine(s, {
x1: offsets.l,
y1: y,
x2: dim.width - offsets.r,
y2: y
}).setStroke(lineStroke);
if(this.animate){
this._animateGrid(hLine, "h", offsets.l, offsets.r + offsets.l - dim.width);
}
}, this);
},
_renderVLines: function(ticks, lineStroke, dim, offsets, hScaler, ht){
var s = this.getGroup();
arr.forEach(ticks, function(tick){
if(!this.opt.renderOnAxis && tick.value == (this._hAxis.opt.leftBottom?hScaler.bounds.from:hScaler.bounds.to)){
return;
}
var x = offsets.l + ht(tick.value);
var vLine = this.createLine(s, {
x1: x,
y1: offsets.t,
x2: x,
y2: dim.height - offsets.b
}).setStroke(lineStroke);
if(this.animate){
this._animateGrid(vLine, "v", dim.height - offsets.b, dim.height - offsets.b - offsets.t);
}
}, this);
},
_renderHRect: function(ticks, theme, dim, offsets, vScaler, vt){
var fill, tick, y, y2, hStripe;
var allTicks = ticks.major.concat(ticks.minor);
allTicks.sort(sortTicks);
if(allTicks[0].value > vScaler.bounds.from){
allTicks.splice(0, 0, {value: vScaler.bounds.from});
}
if(allTicks[allTicks.length - 1].value < vScaler.bounds.to){
allTicks.push({value: vScaler.bounds.to});
}
var s = this.getGroup();
for(var j = 0; j < allTicks.length - 1; j++){
tick = allTicks[j];
y = dim.height - offsets.b - vt(tick.value);
y2 = dim.height - offsets.b - vt(allTicks[j+1].value);
fill = (j%2 == 0)?(this.opt.hAlternateFill ||(theme && theme.alternateFill)):
(this.opt.hFill || (theme && theme.fill));
if(fill){
hStripe = this.createRect(s, {
x: offsets.l,
y: y,
width: dim.width - offsets.r,
height: y - y2
}).setFill(fill);
if(this.animate){
this._animateGrid(hStripe, "h", offsets.l, offsets.r + offsets.l - dim.width);
}
}
}
},
_renderVRect: function(ticks, theme, dim, offsets, hScaler, ht){
var fill, tick, x, x2, vStripe;
var allTicks = ticks.major.concat(ticks.minor);
allTicks.sort(sortTicks);
if(allTicks[0].value > hScaler.bounds.from){
allTicks.splice(0, 0, {value: hScaler.bounds.from});
}
if(allTicks[allTicks.length - 1].value < hScaler.bounds.to){
allTicks.push({value: hScaler.bounds.to});
}
var s = this.getGroup();
for(var j = 0; j < allTicks.length - 1; j++){
tick = allTicks[j];
x = offsets.l + ht(tick.value);
x2 = offsets.l + ht(allTicks[j+1].value);
fill = (j%2 == 0)?(this.opt.vAlternateFill ||(theme && theme.alternateFill)):
(this.opt.vFill || (theme && theme.fill));
if(fill){
vStripe = this.createRect(s, {
x: x,
y: offsets.t,
width: x2 - x,
height: dim.width - offsets.r
}).setFill(fill);
if(this.animate){
this._animateGrid(vStripe, "v", dim.height - offsets.b, dim.height - offsets.b - offsets.t);
}
}
}
},
_animateGrid: function(shape, type, offset, size){
var transStart = type == "h" ? [offset, 0] : [0, offset];
var scaleStart = type == "h" ? [1/size, 1] : [1, 1/size];
fx.animateTransform(lang.delegate({
shape: shape,
duration: 1200,
transform: [
{name: "translate", start: transStart, end: [0, 0]},
{name: "scale", start: scaleStart, end: [1, 1]},
{name: "original"}
]
}, this.animate)).play();
}
});
});