1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138 |
- define("dojox/charting/Chart", ["dojo/_base/lang", "dojo/_base/array","dojo/_base/declare", "dojo/_base/html",
- "dojo/dom", "dojo/dom-geometry", "dojo/dom-construct","dojo/_base/Color", "dojo/_base/sniff",
- "./Element", "./Theme", "./Series", "./axis2d/common", "dojox/gfx/shape",
- "dojox/gfx", "dojox/lang/functional", "dojox/lang/functional/fold", "dojox/lang/functional/reversed"],
- function(lang, arr, declare, html,
- dom, domGeom, domConstruct, Color, has,
- Element, Theme, Series, common, shape,
- g, func, funcFold, funcReversed){
- /*=====
- dojox.charting.__ChartCtorArgs = function(margins, stroke, fill, delayInMs){
- // summary:
- // The keyword arguments that can be passed in a Chart constructor.
- //
- // margins: Object?
- // Optional margins for the chart, in the form of { l, t, r, b}.
- // stroke: dojox.gfx.Stroke?
- // An optional outline/stroke for the chart.
- // fill: dojox.gfx.Fill?
- // An optional fill for the chart.
- // delayInMs: Number
- // Delay in ms for delayedRender(). Default: 200.
- this.margins = margins;
- this.stroke = stroke;
- this.fill = fill;
- this.delayInMs = delayInMs;
- }
- =====*/
- var dc = dojox.charting,
- clear = func.lambda("item.clear()"),
- purge = func.lambda("item.purgeGroup()"),
- destroy = func.lambda("item.destroy()"),
- makeClean = func.lambda("item.dirty = false"),
- makeDirty = func.lambda("item.dirty = true"),
- getName = func.lambda("item.name");
- declare("dojox.charting.Chart", null, {
- // summary:
- // The main chart object in dojox.charting. This will create a two dimensional
- // chart based on dojox.gfx.
- //
- // description:
- // dojox.charting.Chart is the primary object used for any kind of charts. It
- // is simple to create--just pass it a node reference, which is used as the
- // container for the chart--and a set of optional keyword arguments and go.
- //
- // Note that like most of dojox.gfx, most of dojox.charting.Chart's methods are
- // designed to return a reference to the chart itself, to allow for functional
- // chaining. This makes defining everything on a Chart very easy to do.
- //
- // example:
- // Create an area chart, with smoothing.
- // | new dojox.charting.Chart(node))
- // | .addPlot("default", { type: "Areas", tension: "X" })
- // | .setTheme(dojox.charting.themes.Shrooms)
- // | .addSeries("Series A", [1, 2, 0.5, 1.5, 1, 2.8, 0.4])
- // | .addSeries("Series B", [2.6, 1.8, 2, 1, 1.4, 0.7, 2])
- // | .addSeries("Series C", [6.3, 1.8, 3, 0.5, 4.4, 2.7, 2])
- // | .render();
- //
- // example:
- // The form of data in a data series can take a number of forms: a simple array,
- // an array of objects {x,y}, or something custom (as determined by the plot).
- // Here's an example of a Candlestick chart, which expects an object of
- // { open, high, low, close }.
- // | new dojox.charting.Chart(node))
- // | .addPlot("default", {type: "Candlesticks", gap: 1})
- // | .addAxis("x", {fixLower: "major", fixUpper: "major", includeZero: true})
- // | .addAxis("y", {vertical: true, fixLower: "major", fixUpper: "major", natural: true})
- // | .addSeries("Series A", [
- // | { open: 20, close: 16, high: 22, low: 8 },
- // | { open: 16, close: 22, high: 26, low: 6, mid: 18 },
- // | { open: 22, close: 18, high: 22, low: 11, mid: 21 },
- // | { open: 18, close: 29, high: 32, low: 14, mid: 27 },
- // | { open: 29, close: 24, high: 29, low: 13, mid: 27 },
- // | { open: 24, close: 8, high: 24, low: 5 },
- // | { open: 8, close: 16, high: 22, low: 2 },
- // | { open: 16, close: 12, high: 19, low: 7 },
- // | { open: 12, close: 20, high: 22, low: 8 },
- // | { open: 20, close: 16, high: 22, low: 8 },
- // | { open: 16, close: 22, high: 26, low: 6, mid: 18 },
- // | { open: 22, close: 18, high: 22, low: 11, mid: 21 },
- // | { open: 18, close: 29, high: 32, low: 14, mid: 27 },
- // | { open: 29, close: 24, high: 29, low: 13, mid: 27 },
- // | { open: 24, close: 8, high: 24, low: 5 },
- // | { open: 8, close: 16, high: 22, low: 2 },
- // | { open: 16, close: 12, high: 19, low: 7 },
- // | { open: 12, close: 20, high: 22, low: 8 },
- // | { open: 20, close: 16, high: 22, low: 8 },
- // | { open: 16, close: 22, high: 26, low: 6 },
- // | { open: 22, close: 18, high: 22, low: 11 },
- // | { open: 18, close: 29, high: 32, low: 14 },
- // | { open: 29, close: 24, high: 29, low: 13 },
- // | { open: 24, close: 8, high: 24, low: 5 },
- // | { open: 8, close: 16, high: 22, low: 2 },
- // | { open: 16, close: 12, high: 19, low: 7 },
- // | { open: 12, close: 20, high: 22, low: 8 },
- // | { open: 20, close: 16, high: 22, low: 8 }
- // | ],
- // | { stroke: { color: "green" }, fill: "lightgreen" }
- // | )
- // | .render();
-
- // theme: dojox.charting.Theme?
- // An optional theme to use for styling the chart.
- // axes: dojox.charting.Axis{}?
- // A map of axes for use in plotting a chart.
- // stack: dojox.charting.plot2d.Base[]
- // A stack of plotters.
- // plots: dojox.charting.plot2d.Base{}
- // A map of plotter indices
- // series: dojox.charting.Series[]
- // The stack of data runs used to create plots.
- // runs: dojox.charting.Series{}
- // A map of series indices
- // margins: Object?
- // The margins around the chart. Default is { l:10, t:10, r:10, b:10 }.
- // stroke: dojox.gfx.Stroke?
- // The outline of the chart (stroke in vector graphics terms).
- // fill: dojox.gfx.Fill?
- // The color for the chart.
- // node: DOMNode
- // The container node passed to the constructor.
- // surface: dojox.gfx.Surface
- // The main graphics surface upon which a chart is drawn.
- // dirty: Boolean
- // A boolean flag indicating whether or not the chart needs to be updated/re-rendered.
- // coords: Object
- // The coordinates on a page of the containing node, as returned from dojo.coords.
- constructor: function(/* DOMNode */node, /* dojox.charting.__ChartCtorArgs? */kwArgs){
- // summary:
- // The constructor for a new Chart. Initializes all parameters used for a chart.
- // returns: dojox.charting.Chart
- // The newly created chart.
- // initialize parameters
- if(!kwArgs){ kwArgs = {}; }
- this.margins = kwArgs.margins ? kwArgs.margins : {l: 10, t: 10, r: 10, b: 10};
- this.stroke = kwArgs.stroke;
- this.fill = kwArgs.fill;
- this.delayInMs = kwArgs.delayInMs || 200;
- this.title = kwArgs.title;
- this.titleGap = kwArgs.titleGap;
- this.titlePos = kwArgs.titlePos;
- this.titleFont = kwArgs.titleFont;
- this.titleFontColor = kwArgs.titleFontColor;
- this.chartTitle = null;
- // default initialization
- this.theme = null;
- this.axes = {}; // map of axes
- this.stack = []; // stack of plotters
- this.plots = {}; // map of plotter indices
- this.series = []; // stack of data runs
- this.runs = {}; // map of data run indices
- this.dirty = true;
- this.coords = null;
- this._clearRects = [];
- // create a surface
- this.node = dom.byId(node);
- var box = domGeom.getMarginBox(node);
- this.surface = g.createSurface(this.node, box.w || 400, box.h || 300);
- },
- destroy: function(){
- // summary:
- // Cleanup when a chart is to be destroyed.
- // returns: void
- arr.forEach(this.series, destroy);
- arr.forEach(this.stack, destroy);
- func.forIn(this.axes, destroy);
- if(this.chartTitle && this.chartTitle.tagName){
- // destroy title if it is a DOM node
- domConstruct.destroy(this.chartTitle);
- }
- arr.forEach(this._clearRects, function(child){
- shape.dispose(child);
- });
- this.surface.destroy();
- },
- getCoords: function(){
- // summary:
- // Get the coordinates and dimensions of the containing DOMNode, as
- // returned by dojo.coords.
- // returns: Object
- // The resulting coordinates of the chart. See dojo.coords for details.
- return html.coords(this.node, true); // Object
- },
- setTheme: function(theme){
- // summary:
- // Set a theme of the chart.
- // theme: dojox.charting.Theme
- // The theme to be used for visual rendering.
- // returns: dojox.charting.Chart
- // A reference to the current chart for functional chaining.
- this.theme = theme.clone();
- this.dirty = true;
- return this; // dojox.charting.Chart
- },
- addAxis: function(name, kwArgs){
- // summary:
- // Add an axis to the chart, for rendering.
- // name: String
- // The name of the axis.
- // kwArgs: dojox.charting.axis2d.__AxisCtorArgs?
- // An optional keyword arguments object for use in defining details of an axis.
- // returns: dojox.charting.Chart
- // A reference to the current chart for functional chaining.
- var axis, axisType = kwArgs && kwArgs.type || "Default";
- if(typeof axisType == "string"){
- if(!dc.axis2d || !dc.axis2d[axisType]){
- throw Error("Can't find axis: " + axisType + " - Check " + "require() dependencies.");
- }
- axis = new dc.axis2d[axisType](this, kwArgs);
- }else{
- axis = new axisType(this, kwArgs);
- }
- axis.name = name;
- axis.dirty = true;
- if(name in this.axes){
- this.axes[name].destroy();
- }
- this.axes[name] = axis;
- this.dirty = true;
- return this; // dojox.charting.Chart
- },
- getAxis: function(name){
- // summary:
- // Get the given axis, by name.
- // name: String
- // The name the axis was defined by.
- // returns: dojox.charting.axis2d.Default
- // The axis as stored in the chart's axis map.
- return this.axes[name]; // dojox.charting.axis2d.Default
- },
- removeAxis: function(name){
- // summary:
- // Remove the axis that was defined using name.
- // name: String
- // The axis name, as defined in addAxis.
- // returns: dojox.charting.Chart
- // A reference to the current chart for functional chaining.
- if(name in this.axes){
- // destroy the axis
- this.axes[name].destroy();
- delete this.axes[name];
- // mark the chart as dirty
- this.dirty = true;
- }
- return this; // dojox.charting.Chart
- },
- addPlot: function(name, kwArgs){
- // summary:
- // Add a new plot to the chart, defined by name and using the optional keyword arguments object.
- // Note that dojox.charting assumes the main plot to be called "default"; if you do not have
- // a plot called "default" and attempt to add data series to the chart without specifying the
- // plot to be rendered on, you WILL get errors.
- // name: String
- // The name of the plot to be added to the chart. If you only plan on using one plot, call it "default".
- // kwArgs: dojox.charting.plot2d.__PlotCtorArgs
- // An object with optional parameters for the plot in question.
- // returns: dojox.charting.Chart
- // A reference to the current chart for functional chaining.
- var plot, plotType = kwArgs && kwArgs.type || "Default";
- if(typeof plotType == "string"){
- if(!dc.plot2d || !dc.plot2d[plotType]){
- throw Error("Can't find plot: " + plotType + " - didn't you forget to dojo" + ".require() it?");
- }
- plot = new dc.plot2d[plotType](this, kwArgs);
- }else{
- plot = new plotType(this, kwArgs);
- }
- plot.name = name;
- plot.dirty = true;
- if(name in this.plots){
- this.stack[this.plots[name]].destroy();
- this.stack[this.plots[name]] = plot;
- }else{
- this.plots[name] = this.stack.length;
- this.stack.push(plot);
- }
- this.dirty = true;
- return this; // dojox.charting.Chart
- },
- getPlot: function(name){
- // summary:
- // Get the given plot, by name.
- // name: String
- // The name the plot was defined by.
- // returns: dojox.charting.plot2d.Base
- // The plot.
- return this.stack[this.plots[name]];
- },
- removePlot: function(name){
- // summary:
- // Remove the plot defined using name from the chart's plot stack.
- // name: String
- // The name of the plot as defined using addPlot.
- // returns: dojox.charting.Chart
- // A reference to the current chart for functional chaining.
- if(name in this.plots){
- // get the index and remove the name
- var index = this.plots[name];
- delete this.plots[name];
- // destroy the plot
- this.stack[index].destroy();
- // remove the plot from the stack
- this.stack.splice(index, 1);
- // update indices to reflect the shift
- func.forIn(this.plots, function(idx, name, plots){
- if(idx > index){
- plots[name] = idx - 1;
- }
- });
- // remove all related series
- var ns = arr.filter(this.series, function(run){ return run.plot != name; });
- if(ns.length < this.series.length){
- // kill all removed series
- arr.forEach(this.series, function(run){
- if(run.plot == name){
- run.destroy();
- }
- });
- // rebuild all necessary data structures
- this.runs = {};
- arr.forEach(ns, function(run, index){
- this.runs[run.plot] = index;
- }, this);
- this.series = ns;
- }
- // mark the chart as dirty
- this.dirty = true;
- }
- return this; // dojox.charting.Chart
- },
- getPlotOrder: function(){
- // summary:
- // Returns an array of plot names in the current order
- // (the top-most plot is the first).
- // returns: Array
- return func.map(this.stack, getName); // Array
- },
- setPlotOrder: function(newOrder){
- // summary:
- // Sets new order of plots. newOrder cannot add or remove
- // plots. Wrong names, or dups are ignored.
- // newOrder: Array:
- // Array of plot names compatible with getPlotOrder().
- // returns: dojox.charting.Chart
- // A reference to the current chart for functional chaining.
- var names = {},
- order = func.filter(newOrder, function(name){
- if(!(name in this.plots) || (name in names)){
- return false;
- }
- names[name] = 1;
- return true;
- }, this);
- if(order.length < this.stack.length){
- func.forEach(this.stack, function(plot){
- var name = plot.name;
- if(!(name in names)){
- order.push(name);
- }
- });
- }
- var newStack = func.map(order, function(name){
- return this.stack[this.plots[name]];
- }, this);
- func.forEach(newStack, function(plot, i){
- this.plots[plot.name] = i;
- }, this);
- this.stack = newStack;
- this.dirty = true;
- return this; // dojox.charting.Chart
- },
- movePlotToFront: function(name){
- // summary:
- // Moves a given plot to front.
- // name: String:
- // Plot's name to move.
- // returns: dojox.charting.Chart
- // A reference to the current chart for functional chaining.
- if(name in this.plots){
- var index = this.plots[name];
- if(index){
- var newOrder = this.getPlotOrder();
- newOrder.splice(index, 1);
- newOrder.unshift(name);
- return this.setPlotOrder(newOrder); // dojox.charting.Chart
- }
- }
- return this; // dojox.charting.Chart
- },
- movePlotToBack: function(name){
- // summary:
- // Moves a given plot to back.
- // name: String:
- // Plot's name to move.
- // returns: dojox.charting.Chart
- // A reference to the current chart for functional chaining.
- if(name in this.plots){
- var index = this.plots[name];
- if(index < this.stack.length - 1){
- var newOrder = this.getPlotOrder();
- newOrder.splice(index, 1);
- newOrder.push(name);
- return this.setPlotOrder(newOrder); // dojox.charting.Chart
- }
- }
- return this; // dojox.charting.Chart
- },
- addSeries: function(name, data, kwArgs){
- // summary:
- // Add a data series to the chart for rendering.
- // name: String:
- // The name of the data series to be plotted.
- // data: Array|Object:
- // The array of data points (either numbers or objects) that
- // represents the data to be drawn. Or it can be an object. In
- // the latter case, it should have a property "data" (an array),
- // destroy(), and setSeriesObject().
- // kwArgs: dojox.charting.__SeriesCtorArgs?:
- // An optional keyword arguments object that will be mixed into
- // the resultant series object.
- // returns: dojox.charting.Chart:
- // A reference to the current chart for functional chaining.
- var run = new Series(this, data, kwArgs);
- run.name = name;
- if(name in this.runs){
- this.series[this.runs[name]].destroy();
- this.series[this.runs[name]] = run;
- }else{
- this.runs[name] = this.series.length;
- this.series.push(run);
- }
- this.dirty = true;
- // fix min/max
- if(!("ymin" in run) && "min" in run){ run.ymin = run.min; }
- if(!("ymax" in run) && "max" in run){ run.ymax = run.max; }
- return this; // dojox.charting.Chart
- },
- getSeries: function(name){
- // summary:
- // Get the given series, by name.
- // name: String
- // The name the series was defined by.
- // returns: dojox.charting.Series
- // The series.
- return this.series[this.runs[name]];
- },
- removeSeries: function(name){
- // summary:
- // Remove the series defined by name from the chart.
- // name: String
- // The name of the series as defined by addSeries.
- // returns: dojox.charting.Chart
- // A reference to the current chart for functional chaining.
- if(name in this.runs){
- // get the index and remove the name
- var index = this.runs[name];
- delete this.runs[name];
- // destroy the run
- this.series[index].destroy();
- // remove the run from the stack of series
- this.series.splice(index, 1);
- // update indices to reflect the shift
- func.forIn(this.runs, function(idx, name, runs){
- if(idx > index){
- runs[name] = idx - 1;
- }
- });
- this.dirty = true;
- }
- return this; // dojox.charting.Chart
- },
- updateSeries: function(name, data){
- // summary:
- // Update the given series with a new set of data points.
- // name: String
- // The name of the series as defined in addSeries.
- // data: Array|Object:
- // The array of data points (either numbers or objects) that
- // represents the data to be drawn. Or it can be an object. In
- // the latter case, it should have a property "data" (an array),
- // destroy(), and setSeriesObject().
- // returns: dojox.charting.Chart
- // A reference to the current chart for functional chaining.
- if(name in this.runs){
- var run = this.series[this.runs[name]];
- run.update(data);
- this._invalidateDependentPlots(run.plot, false);
- this._invalidateDependentPlots(run.plot, true);
- }
- return this; // dojox.charting.Chart
- },
- getSeriesOrder: function(plotName){
- // summary:
- // Returns an array of series names in the current order
- // (the top-most series is the first) within a plot.
- // plotName: String:
- // Plot's name.
- // returns: Array
- return func.map(func.filter(this.series, function(run){
- return run.plot == plotName;
- }), getName);
- },
- setSeriesOrder: function(newOrder){
- // summary:
- // Sets new order of series within a plot. newOrder cannot add
- // or remove series. Wrong names, or dups are ignored.
- // newOrder: Array:
- // Array of series names compatible with getPlotOrder(). All
- // series should belong to the same plot.
- // returns: dojox.charting.Chart
- // A reference to the current chart for functional chaining.
- var plotName, names = {},
- order = func.filter(newOrder, function(name){
- if(!(name in this.runs) || (name in names)){
- return false;
- }
- var run = this.series[this.runs[name]];
- if(plotName){
- if(run.plot != plotName){
- return false;
- }
- }else{
- plotName = run.plot;
- }
- names[name] = 1;
- return true;
- }, this);
- func.forEach(this.series, function(run){
- var name = run.name;
- if(!(name in names) && run.plot == plotName){
- order.push(name);
- }
- });
- var newSeries = func.map(order, function(name){
- return this.series[this.runs[name]];
- }, this);
- this.series = newSeries.concat(func.filter(this.series, function(run){
- return run.plot != plotName;
- }));
- func.forEach(this.series, function(run, i){
- this.runs[run.name] = i;
- }, this);
- this.dirty = true;
- return this; // dojox.charting.Chart
- },
- moveSeriesToFront: function(name){
- // summary:
- // Moves a given series to front of a plot.
- // name: String:
- // Series' name to move.
- // returns: dojox.charting.Chart
- // A reference to the current chart for functional chaining.
- if(name in this.runs){
- var index = this.runs[name],
- newOrder = this.getSeriesOrder(this.series[index].plot);
- if(name != newOrder[0]){
- newOrder.splice(index, 1);
- newOrder.unshift(name);
- return this.setSeriesOrder(newOrder); // dojox.charting.Chart
- }
- }
- return this; // dojox.charting.Chart
- },
- moveSeriesToBack: function(name){
- // summary:
- // Moves a given series to back of a plot.
- // name: String:
- // Series' name to move.
- // returns: dojox.charting.Chart
- // A reference to the current chart for functional chaining.
- if(name in this.runs){
- var index = this.runs[name],
- newOrder = this.getSeriesOrder(this.series[index].plot);
- if(name != newOrder[newOrder.length - 1]){
- newOrder.splice(index, 1);
- newOrder.push(name);
- return this.setSeriesOrder(newOrder); // dojox.charting.Chart
- }
- }
- return this; // dojox.charting.Chart
- },
- resize: function(width, height){
- // summary:
- // Resize the chart to the dimensions of width and height.
- // description:
- // Resize the chart and its surface to the width and height dimensions.
- // If no width/height or box is provided, resize the surface to the marginBox of the chart.
- // width: Number
- // The new width of the chart.
- // height: Number
- // The new height of the chart.
- // returns: dojox.charting.Chart
- // A reference to the current chart for functional chaining.
- var box;
- switch(arguments.length){
- // case 0, do not resize the div, just the surface
- case 1:
- // argument, override node box
- box = lang.mixin({}, width);
- domGeom.setMarginBox(this.node, box);
- break;
- case 2:
- box = {w: width, h: height};
- // argument, override node box
- domGeom.setMarginBox(this.node, box);
- break;
- }
- // in all cases take back the computed box
- box = domGeom.getMarginBox(this.node);
- var d = this.surface.getDimensions();
- if(d.width != box.w || d.height != box.h){
- // and set it on the surface
- this.surface.setDimensions(box.w, box.h);
- this.dirty = true;
- return this.render(); // dojox.charting.Chart
- }else{
- return this;
- }
- },
- getGeometry: function(){
- // summary:
- // Returns a map of information about all axes in a chart and what they represent
- // in terms of scaling (see dojox.charting.axis2d.Default.getScaler).
- // returns: Object
- // An map of geometry objects, a one-to-one mapping of axes.
- var ret = {};
- func.forIn(this.axes, function(axis){
- if(axis.initialized()){
- ret[axis.name] = {
- name: axis.name,
- vertical: axis.vertical,
- scaler: axis.scaler,
- ticks: axis.ticks
- };
- }
- });
- return ret; // Object
- },
- setAxisWindow: function(name, scale, offset, zoom){
- // summary:
- // Zooms an axis and all dependent plots. Can be used to zoom in 1D.
- // name: String
- // The name of the axis as defined by addAxis.
- // scale: Number
- // The scale on the target axis.
- // offset: Number
- // Any offest, as measured by axis tick
- // zoom: Boolean|Object?
- // The chart zooming animation trigger. This is null by default,
- // e.g. {duration: 1200}, or just set true.
- // returns: dojox.charting.Chart
- // A reference to the current chart for functional chaining.
- var axis = this.axes[name];
- if(axis){
- axis.setWindow(scale, offset);
- arr.forEach(this.stack,function(plot){
- if(plot.hAxis == name || plot.vAxis == name){
- plot.zoom = zoom;
- }
- });
- }
- return this; // dojox.charting.Chart
- },
- setWindow: function(sx, sy, dx, dy, zoom){
- // summary:
- // Zooms in or out any plots in two dimensions.
- // sx: Number
- // The scale for the x axis.
- // sy: Number
- // The scale for the y axis.
- // dx: Number
- // The pixel offset on the x axis.
- // dy: Number
- // The pixel offset on the y axis.
- // zoom: Boolean|Object?
- // The chart zooming animation trigger. This is null by default,
- // e.g. {duration: 1200}, or just set true.
- // returns: dojox.charting.Chart
- // A reference to the current chart for functional chaining.
- if(!("plotArea" in this)){
- this.calculateGeometry();
- }
- func.forIn(this.axes, function(axis){
- var scale, offset, bounds = axis.getScaler().bounds,
- s = bounds.span / (bounds.upper - bounds.lower);
- if(axis.vertical){
- scale = sy;
- offset = dy / s / scale;
- }else{
- scale = sx;
- offset = dx / s / scale;
- }
- axis.setWindow(scale, offset);
- });
- arr.forEach(this.stack, function(plot){ plot.zoom = zoom; });
- return this; // dojox.charting.Chart
- },
- zoomIn: function(name, range){
- // summary:
- // Zoom the chart to a specific range on one axis. This calls render()
- // directly as a convenience method.
- // name: String
- // The name of the axis as defined by addAxis.
- // range: Array
- // The end points of the zoom range, measured in axis ticks.
- var axis = this.axes[name];
- if(axis){
- var scale, offset, bounds = axis.getScaler().bounds;
- var lower = Math.min(range[0],range[1]);
- var upper = Math.max(range[0],range[1]);
- lower = range[0] < bounds.lower ? bounds.lower : lower;
- upper = range[1] > bounds.upper ? bounds.upper : upper;
- scale = (bounds.upper - bounds.lower) / (upper - lower);
- offset = lower - bounds.lower;
- this.setAxisWindow(name, scale, offset);
- this.render();
- }
- },
- calculateGeometry: function(){
- // summary:
- // Calculate the geometry of the chart based on the defined axes of
- // a chart.
- // returns: dojox.charting.Chart
- // A reference to the current chart for functional chaining.
- if(this.dirty){
- return this.fullGeometry();
- }
- // calculate geometry
- var dirty = arr.filter(this.stack, function(plot){
- return plot.dirty ||
- (plot.hAxis && this.axes[plot.hAxis].dirty) ||
- (plot.vAxis && this.axes[plot.vAxis].dirty);
- }, this);
- calculateAxes(dirty, this.plotArea);
- return this; // dojox.charting.Chart
- },
- fullGeometry: function(){
- // summary:
- // Calculate the full geometry of the chart. This includes passing
- // over all major elements of a chart (plots, axes, series, container)
- // in order to ensure proper rendering.
- // returns: dojox.charting.Chart
- // A reference to the current chart for functional chaining.
- this._makeDirty();
- // clear old values
- arr.forEach(this.stack, clear);
- // rebuild new connections, and add defaults
- // set up a theme
- if(!this.theme){
- this.setTheme(new Theme(dojox.charting._def));
- }
- // assign series
- arr.forEach(this.series, function(run){
- if(!(run.plot in this.plots)){
- if(!dc.plot2d || !dc.plot2d.Default){
- throw Error("Can't find plot: Default - didn't you forget to dojo" + ".require() it?");
- }
- var plot = new dc.plot2d.Default(this, {});
- plot.name = run.plot;
- this.plots[run.plot] = this.stack.length;
- this.stack.push(plot);
- }
- this.stack[this.plots[run.plot]].addSeries(run);
- }, this);
- // assign axes
- arr.forEach(this.stack, function(plot){
- if(plot.hAxis){
- plot.setAxis(this.axes[plot.hAxis]);
- }
- if(plot.vAxis){
- plot.setAxis(this.axes[plot.vAxis]);
- }
- }, this);
- // calculate geometry
- // 1st pass
- var dim = this.dim = this.surface.getDimensions();
- dim.width = g.normalizedLength(dim.width);
- dim.height = g.normalizedLength(dim.height);
- func.forIn(this.axes, clear);
- calculateAxes(this.stack, dim);
- // assumption: we don't have stacked axes yet
- var offsets = this.offsets = { l: 0, r: 0, t: 0, b: 0 };
- func.forIn(this.axes, function(axis){
- func.forIn(axis.getOffsets(), function(o, i){ offsets[i] += o; });
- });
- // add title area
- if(this.title){
- this.titleGap = (this.titleGap==0) ? 0 : this.titleGap || this.theme.chart.titleGap || 20;
- this.titlePos = this.titlePos || this.theme.chart.titlePos || "top";
- this.titleFont = this.titleFont || this.theme.chart.titleFont;
- this.titleFontColor = this.titleFontColor || this.theme.chart.titleFontColor || "black";
- var tsize = g.normalizedLength(g.splitFontString(this.titleFont).size);
- offsets[this.titlePos=="top" ? "t":"b"] += (tsize + this.titleGap);
- }
- // add margins
- func.forIn(this.margins, function(o, i){ offsets[i] += o; });
- // 2nd pass with realistic dimensions
- this.plotArea = {
- width: dim.width - offsets.l - offsets.r,
- height: dim.height - offsets.t - offsets.b
- };
- func.forIn(this.axes, clear);
- calculateAxes(this.stack, this.plotArea);
- return this; // dojox.charting.Chart
- },
- render: function(){
- // summary:
- // Render the chart according to the current information defined. This should
- // be the last call made when defining/creating a chart, or if data within the
- // chart has been changed.
- // returns: dojox.charting.Chart
- // A reference to the current chart for functional chaining.
- if(this.theme){
- this.theme.clear();
- }
- if(this.dirty){
- return this.fullRender();
- }
- this.calculateGeometry();
- // go over the stack backwards
- func.forEachRev(this.stack, function(plot){ plot.render(this.dim, this.offsets); }, this);
- // go over axes
- func.forIn(this.axes, function(axis){ axis.render(this.dim, this.offsets); }, this);
- this._makeClean();
- // BEGIN FOR HTML CANVAS
- if(this.surface.render){ this.surface.render(); };
- // END FOR HTML CANVAS
- return this; // dojox.charting.Chart
- },
- fullRender: function(){
- // summary:
- // Force a full rendering of the chart, including full resets on the chart itself.
- // You should not call this method directly unless absolutely necessary.
- // returns: dojox.charting.Chart
- // A reference to the current chart for functional chaining.
- // calculate geometry
- this.fullGeometry();
- var offsets = this.offsets, dim = this.dim, rect;
- // get required colors
- //var requiredColors = func.foldl(this.stack, "z + plot.getRequiredColors()", 0);
- //this.theme.defineColors({num: requiredColors, cache: false});
- // clear old shapes
- arr.forEach(this.series, purge);
- func.forIn(this.axes, purge);
- arr.forEach(this.stack, purge);
- arr.forEach(this._clearRects, function(child){
- shape.dispose(child);
- });
- this._clearRects = [];
- if(this.chartTitle && this.chartTitle.tagName){
- // destroy title if it is a DOM node
- domConstruct.destroy(this.chartTitle);
- }
- this.surface.clear();
- this.chartTitle = null;
- // generate shapes
- // draw a plot background
- var t = this.theme,
- fill = t.plotarea && t.plotarea.fill,
- stroke = t.plotarea && t.plotarea.stroke,
- // size might be neg if offsets are bigger that chart size this happens quite often at
- // initialization time if the chart widget is used in a BorderContainer
- // this will fail on IE/VML
- w = Math.max(0, dim.width - offsets.l - offsets.r),
- h = Math.max(0, dim.height - offsets.t - offsets.b),
- rect = {
- x: offsets.l - 1, y: offsets.t - 1,
- width: w + 2,
- height: h + 2
- };
- if(fill){
- fill = Element.prototype._shapeFill(Element.prototype._plotFill(fill, dim, offsets), rect);
- this._clearRects.push(this.surface.createRect(rect).setFill(fill));
- }
- if(stroke){
- this._clearRects.push(this.surface.createRect({
- x: offsets.l, y: offsets.t,
- width: w + 1,
- height: h + 1
- }).setStroke(stroke));
- }
- // go over the stack backwards
- func.foldr(this.stack, function(z, plot){ return plot.render(dim, offsets), 0; }, 0);
- // pseudo-clipping: matting
- fill = this.fill !== undefined ? this.fill : (t.chart && t.chart.fill);
- stroke = this.stroke !== undefined ? this.stroke : (t.chart && t.chart.stroke);
- // TRT: support for "inherit" as a named value in a theme.
- if(fill == "inherit"){
- // find the background color of the nearest ancestor node, and use that explicitly.
- var node = this.node, fill = new Color(html.style(node, "backgroundColor"));
- while(fill.a==0 && node!=document.documentElement){
- fill = new Color(html.style(node, "backgroundColor"));
- node = node.parentNode;
- }
- }
- if(fill){
- fill = Element.prototype._plotFill(fill, dim, offsets);
- if(offsets.l){ // left
- rect = {
- width: offsets.l,
- height: dim.height + 1
- };
- this._clearRects.push(this.surface.createRect(rect).setFill(Element.prototype._shapeFill(fill, rect)));
- }
- if(offsets.r){ // right
- rect = {
- x: dim.width - offsets.r,
- width: offsets.r + 1,
- height: dim.height + 2
- };
- this._clearRects.push(this.surface.createRect(rect).setFill(Element.prototype._shapeFill(fill, rect)));
- }
- if(offsets.t){ // top
- rect = {
- width: dim.width + 1,
- height: offsets.t
- };
- this._clearRects.push(this.surface.createRect(rect).setFill(Element.prototype._shapeFill(fill, rect)));
- }
- if(offsets.b){ // bottom
- rect = {
- y: dim.height - offsets.b,
- width: dim.width + 1,
- height: offsets.b + 2
- };
- this._clearRects.push(this.surface.createRect(rect).setFill(Element.prototype._shapeFill(fill, rect)));
- }
- }
- if(stroke){
- this._clearRects.push(this.surface.createRect({
- width: dim.width - 1,
- height: dim.height - 1
- }).setStroke(stroke));
- }
- //create title: Whether to make chart title as a widget which extends dojox.charting.Element?
- if(this.title){
- var forceHtmlLabels = (g.renderer == "canvas"),
- labelType = forceHtmlLabels || !has("ie") && !has("opera") ? "html" : "gfx",
- tsize = g.normalizedLength(g.splitFontString(this.titleFont).size);
- this.chartTitle = common.createText[labelType](
- this,
- this.surface,
- dim.width/2,
- this.titlePos=="top" ? tsize + this.margins.t : dim.height - this.margins.b,
- "middle",
- this.title,
- this.titleFont,
- this.titleFontColor
- );
- }
- // go over axes
- func.forIn(this.axes, function(axis){ axis.render(dim, offsets); });
- this._makeClean();
- // BEGIN FOR HTML CANVAS
- if(this.surface.render){ this.surface.render(); };
- // END FOR HTML CANVAS
- return this; // dojox.charting.Chart
- },
- delayedRender: function(){
- // summary:
- // Delayed render, which is used to collect multiple updates
- // within a delayInMs time window.
- // returns: dojox.charting.Chart
- // A reference to the current chart for functional chaining.
- if(!this._delayedRenderHandle){
- this._delayedRenderHandle = setTimeout(
- lang.hitch(this, function(){
- clearTimeout(this._delayedRenderHandle);
- this._delayedRenderHandle = null;
- this.render();
- }),
- this.delayInMs
- );
- }
- return this; // dojox.charting.Chart
- },
- connectToPlot: function(name, object, method){
- // summary:
- // A convenience method to connect a function to a plot.
- // name: String
- // The name of the plot as defined by addPlot.
- // object: Object
- // The object to be connected.
- // method: Function
- // The function to be executed.
- // returns: Array
- // A handle to the connection, as defined by dojo.connect (see dojo.connect).
- return name in this.plots ? this.stack[this.plots[name]].connect(object, method) : null; // Array
- },
- fireEvent: function(seriesName, eventName, index){
- // summary:
- // Fires a synthetic event for a series item.
- // seriesName: String:
- // Series name.
- // eventName: String:
- // Event name to simulate: onmouseover, onmouseout, onclick.
- // index: Number:
- // Valid data value index for the event.
- // returns: dojox.charting.Chart
- // A reference to the current chart for functional chaining.
- if(seriesName in this.runs){
- var plotName = this.series[this.runs[seriesName]].plot;
- if(plotName in this.plots){
- var plot = this.stack[this.plots[plotName]];
- if(plot){
- plot.fireEvent(seriesName, eventName, index);
- }
- }
- }
- return this; // dojox.charting.Chart
- },
- _makeClean: function(){
- // reset dirty flags
- arr.forEach(this.axes, makeClean);
- arr.forEach(this.stack, makeClean);
- arr.forEach(this.series, makeClean);
- this.dirty = false;
- },
- _makeDirty: function(){
- // reset dirty flags
- arr.forEach(this.axes, makeDirty);
- arr.forEach(this.stack, makeDirty);
- arr.forEach(this.series, makeDirty);
- this.dirty = true;
- },
- _invalidateDependentPlots: function(plotName, /* Boolean */ verticalAxis){
- if(plotName in this.plots){
- var plot = this.stack[this.plots[plotName]], axis,
- axisName = verticalAxis ? "vAxis" : "hAxis";
- if(plot[axisName]){
- axis = this.axes[plot[axisName]];
- if(axis && axis.dependOnData()){
- axis.dirty = true;
- // find all plots and mark them dirty
- arr.forEach(this.stack, function(p){
- if(p[axisName] && p[axisName] == plot[axisName]){
- p.dirty = true;
- }
- });
- }
- }else{
- plot.dirty = true;
- }
- }
- }
- });
- function hSection(stats){
- return {min: stats.hmin, max: stats.hmax};
- }
- function vSection(stats){
- return {min: stats.vmin, max: stats.vmax};
- }
- function hReplace(stats, h){
- stats.hmin = h.min;
- stats.hmax = h.max;
- }
- function vReplace(stats, v){
- stats.vmin = v.min;
- stats.vmax = v.max;
- }
- function combineStats(target, source){
- if(target && source){
- target.min = Math.min(target.min, source.min);
- target.max = Math.max(target.max, source.max);
- }
- return target || source;
- }
- function calculateAxes(stack, plotArea){
- var plots = {}, axes = {};
- arr.forEach(stack, function(plot){
- var stats = plots[plot.name] = plot.getSeriesStats();
- if(plot.hAxis){
- axes[plot.hAxis] = combineStats(axes[plot.hAxis], hSection(stats));
- }
- if(plot.vAxis){
- axes[plot.vAxis] = combineStats(axes[plot.vAxis], vSection(stats));
- }
- });
- arr.forEach(stack, function(plot){
- var stats = plots[plot.name];
- if(plot.hAxis){
- hReplace(stats, axes[plot.hAxis]);
- }
- if(plot.vAxis){
- vReplace(stats, axes[plot.vAxis]);
- }
- plot.initializeScalers(plotArea, stats);
- });
- }
-
- return dojox.charting.Chart;
- });
|