source: Dev/trunk/src/client/dojox/charting/Chart.js @ 532

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

Added Dojo 1.9.3 release.

File size: 40.2 KB
Line 
1define(["../main", "dojo/_base/lang", "dojo/_base/array","dojo/_base/declare", "dojo/dom-style",
2        "dojo/dom", "dojo/dom-geometry", "dojo/dom-construct","dojo/_base/Color", "dojo/sniff",
3        "./Element", "./SimpleTheme", "./Series", "./axis2d/common", "dojox/gfx/shape",
4        "dojox/gfx", "dojo/has!dojo-bidi?./bidi/Chart", "dojox/lang/functional", "dojox/lang/functional/fold", "dojox/lang/functional/reversed"],
5        function(dojox, lang, arr, declare, domStyle,
6                         dom, domGeom, domConstruct, Color, has,
7                         Element, SimpleTheme, Series, common, shape,
8                         g, BidiChart, func){
9        /*=====
10        var __ChartCtorArgs = {
11                // summary:
12                //              The keyword arguments that can be passed in a Chart constructor.
13                // margins: Object?
14                //              Optional margins for the chart, in the form of { l, t, r, b}.
15                // stroke: dojox.gfx.Stroke?
16                //              An optional outline/stroke for the chart.
17                // fill: dojox.gfx.Fill?
18                //              An optional fill for the chart.
19                // delayInMs: Number
20                //              Delay in ms for delayedRender(). Default: 200.
21        };
22        =====*/
23
24        /*=====
25        var __SeriesCtorArgs = {
26                // summary:
27                //              An optional arguments object that can be used in the Series constructor.
28                // plot: String?
29                //              The plot (by name) that this series belongs to.
30        };
31        =====*/
32
33        /*=====
34        var __BaseAxisCtorArgs = {
35                // summary:
36                //              Optional arguments used in the definition of an invisible axis.
37                // vertical: Boolean?
38                //              A flag that says whether an axis is vertical (i.e. y axis) or horizontal. Default is false (horizontal).
39                // min: Number?
40                //              The smallest value on an axis. Default is 0.
41                // max: Number?
42                //              The largest value on an axis. Default is 1.
43        };
44        =====*/
45
46        var dc = lang.getObject("charting", true, dojox),
47                clear = func.lambda("item.clear()"),
48                purge = func.lambda("item.purgeGroup()"),
49                destroy = func.lambda("item.destroy()"),
50                makeClean = func.lambda("item.dirty = false"),
51                makeDirty = func.lambda("item.dirty = true"),
52                getName = func.lambda("item.name");
53
54        var Chart = declare(has("dojo-bidi")? "dojox.charting.NonBidiChart" : "dojox.charting.Chart", null, {
55                // summary:
56                //              The main chart object in dojox.charting.  This will create a two dimensional
57                //              chart based on dojox.gfx.
58                //
59                // description:
60                //              dojox.charting.Chart is the primary object used for any kind of charts.  It
61                //              is simple to create--just pass it a node reference, which is used as the
62                //              container for the chart--and a set of optional keyword arguments and go.
63                //
64                //              Note that like most of dojox.gfx, most of dojox.charting.Chart's methods are
65                //              designed to return a reference to the chart itself, to allow for functional
66                //              chaining.  This makes defining everything on a Chart very easy to do.
67                //
68                // example:
69                //              Create an area chart, with smoothing.
70                //      |       require(["dojox/charting/Chart", "dojox/charting/themes/Shrooms", "dojox/charting/plot2d/Areas", ...],
71                //      |               function(Chart, Shrooms, Areas, ...){
72                //      |               new Chart(node)
73                //      |                       .addPlot("default", { type: Areas, tension: "X" })
74                //      |                       .setTheme(Shrooms)
75                //      |                       .addSeries("Series A", [1, 2, 0.5, 1.5, 1, 2.8, 0.4])
76                //      |                       .addSeries("Series B", [2.6, 1.8, 2, 1, 1.4, 0.7, 2])
77                //      |                       .addSeries("Series C", [6.3, 1.8, 3, 0.5, 4.4, 2.7, 2])
78                //      |                       .render();
79                //      |       });
80                //
81                // example:
82                //              The form of data in a data series can take a number of forms: a simple array,
83                //              an array of objects {x,y}, or something custom (as determined by the plot).
84                //              Here's an example of a Candlestick chart, which expects an object of
85                //              { open, high, low, close }.
86                //      |       require(["dojox/charting/Chart", "dojox/charting/plot2d/Candlesticks", ...],
87                //      |               function(Chart, Candlesticks, ...){
88                //      |               new Chart(node)
89                //      |                       .addPlot("default", {type: Candlesticks, gap: 1})
90                //      |                       .addAxis("x", {fixLower: "major", fixUpper: "major", includeZero: true})
91                //      |                       .addAxis("y", {vertical: true, fixLower: "major", fixUpper: "major", natural: true})
92                //      |                       .addSeries("Series A", [
93                //      |                                       { open: 20, close: 16, high: 22, low: 8 },
94                //      |                                       { open: 16, close: 22, high: 26, low: 6, mid: 18 },
95                //      |                                       { open: 22, close: 18, high: 22, low: 11, mid: 21 },
96                //      |                                       { open: 18, close: 29, high: 32, low: 14, mid: 27 },
97                //      |                                       { open: 29, close: 24, high: 29, low: 13, mid: 27 },
98                //      |                                       { open: 24, close: 8, high: 24, low: 5 },
99                //      |                                       { open: 8, close: 16, high: 22, low: 2 },
100                //      |                                       { open: 16, close: 12, high: 19, low: 7 },
101                //      |                                       { open: 12, close: 20, high: 22, low: 8 },
102                //      |                                       { open: 20, close: 16, high: 22, low: 8 },
103                //      |                                       { open: 16, close: 22, high: 26, low: 6, mid: 18 },
104                //      |                                       { open: 22, close: 18, high: 22, low: 11, mid: 21 },
105                //      |                                       { open: 18, close: 29, high: 32, low: 14, mid: 27 },
106                //      |                                       { open: 29, close: 24, high: 29, low: 13, mid: 27 },
107                //      |                                       { open: 24, close: 8, high: 24, low: 5 },
108                //      |                                       { open: 8, close: 16, high: 22, low: 2 },
109                //      |                                       { open: 16, close: 12, high: 19, low: 7 },
110                //      |                                       { open: 12, close: 20, high: 22, low: 8 },
111                //      |                                       { open: 20, close: 16, high: 22, low: 8 },
112                //      |                                       { open: 16, close: 22, high: 26, low: 6 },
113                //      |                                       { open: 22, close: 18, high: 22, low: 11 },
114                //      |                                       { open: 18, close: 29, high: 32, low: 14 },
115                //      |                                       { open: 29, close: 24, high: 29, low: 13 },
116                //      |                                       { open: 24, close: 8, high: 24, low: 5 },
117                //      |                                       { open: 8, close: 16, high: 22, low: 2 },
118                //      |                                       { open: 16, close: 12, high: 19, low: 7 },
119                //      |                                       { open: 12, close: 20, high: 22, low: 8 },
120                //      |                                       { open: 20, close: 16, high: 22, low: 8 }
121                //      |                               ],
122                //      |                               { stroke: { color: "green" }, fill: "lightgreen" }
123                //      |                       )
124                //      |                       .render();
125                //      |       });
126               
127                // theme: dojox/charting/SimpleTheme?
128                //              An optional theme to use for styling the chart.
129                // axes: dojox/charting/axis2d/Base{}?
130                //              A map of axes for use in plotting a chart.
131                // stack: dojox/charting/plot2d/Base[]
132                //              A stack of plotters.
133                // plots: dojox/charting/plot2d/Base{}
134                //              A map of plotter indices
135                // series: dojox/charting/Series[]
136                //              The stack of data runs used to create plots.
137                // runs: dojox/charting/Series{}
138                //              A map of series indices
139                // margins: Object?
140                //              The margins around the chart. Default is { l:10, t:10, r:10, b:10 }.
141                // stroke: dojox.gfx.Stroke?
142                //              The outline of the chart (stroke in vector graphics terms).
143                // fill: dojox.gfx.Fill?
144                //              The color for the chart.
145                // node: DOMNode
146                //              The container node passed to the constructor.
147                // surface: dojox/gfx/shape.Surface
148                //              The main graphics surface upon which a chart is drawn.
149                // dirty: Boolean
150                //              A boolean flag indicating whether or not the chart needs to be updated/re-rendered.
151                // htmlLabels: Boolean
152                //              A boolean flag indicating whether or not it should try to use HTML-based labels for the title or not.
153                //              The default is true.  The only caveat is IE and Opera browsers will always use GFX-based labels.
154
155                constructor: function(/* DOMNode */node, /* __ChartCtorArgs? */kwArgs){
156                        // summary:
157                        //              The constructor for a new Chart.  Initializes all parameters used for a chart.
158                        // returns: dojox/charting/Chart
159                        //              The newly created chart.
160
161                        // initialize parameters
162                        if(!kwArgs){ kwArgs = {}; }
163                        this.margins   = kwArgs.margins ? kwArgs.margins : {l: 10, t: 10, r: 10, b: 10};
164                        this.stroke    = kwArgs.stroke;
165                        this.fill      = kwArgs.fill;
166                        this.delayInMs = kwArgs.delayInMs || 200;
167                        this.title     = kwArgs.title;
168                        this.titleGap  = kwArgs.titleGap;
169                        this.titlePos  = kwArgs.titlePos;
170                        this.titleFont = kwArgs.titleFont;
171                        this.titleFontColor = kwArgs.titleFontColor;
172                        this.chartTitle = null;
173                        this.htmlLabels = true;
174                        if("htmlLabels" in kwArgs){
175                                this.htmlLabels = kwArgs.htmlLabels;
176                        }
177
178                        // default initialization
179                        this.theme = null;
180                        this.axes = {};         // map of axes
181                        this.stack = [];        // stack of plotters
182                        this.plots = {};        // map of plotter indices
183                        this.series = [];       // stack of data runs
184                        this.runs = {};         // map of data run indices
185                        this.dirty = true;
186
187                        // create a surface
188                        this.node = dom.byId(node);
189                        var box = domGeom.getMarginBox(node);
190                        this.surface = g.createSurface(this.node, box.w || 400, box.h || 300);
191                        if(this.surface.declaredClass.indexOf("vml") == -1){
192                                // except if vml use native clipping
193                                this._nativeClip = true;
194                        }
195                },
196                destroy: function(){
197                        // summary:
198                        //              Cleanup when a chart is to be destroyed.
199                        // returns: void
200                        arr.forEach(this.series, destroy);
201                        arr.forEach(this.stack,  destroy);
202                        func.forIn(this.axes, destroy);
203                        this.surface.destroy();
204                        if(this.chartTitle && this.chartTitle.tagName){
205                                // destroy title if it is a DOM node
206                                domConstruct.destroy(this.chartTitle);
207                        }
208                },
209                getCoords: function(){
210                        // summary:
211                        //              Get the coordinates and dimensions of the containing DOMNode, as
212                        //              returned by dojo.coords.
213                        // returns: Object
214                        //              The resulting coordinates of the chart.  See dojo.coords for details.
215                        var node = this.node;
216                        var s = domStyle.getComputedStyle(node), coords = domGeom.getMarginBox(node, s);
217                        var abs = domGeom.position(node, true);
218                        coords.x = abs.x;
219                        coords.y = abs.y;
220                        return coords;  //      Object
221                },
222                setTheme: function(theme){
223                        // summary:
224                        //              Set a theme of the chart.
225                        // theme: dojox/charting/SimpleTheme
226                        //              The theme to be used for visual rendering.
227                        // returns: dojox/charting/Chart
228                        //              A reference to the current chart for functional chaining.
229                        this.theme = theme.clone();
230                        this.dirty = true;
231                        return this;    //      dojox/charting/Chart
232                },
233                addAxis: function(name, kwArgs){
234                        // summary:
235                        //              Add an axis to the chart, for rendering.
236                        // name: String
237                        //              The name of the axis.
238                        // kwArgs: __BaseAxisCtorArgs?
239                        //              An optional keyword arguments object for use in defining details of an axis.
240                        // returns: dojox/charting/Chart
241                        //              A reference to the current chart for functional chaining.
242                        var axis, axisType = kwArgs && kwArgs.type || "Default";
243                        if(typeof axisType == "string"){
244                                if(!dc.axis2d || !dc.axis2d[axisType]){
245                                        throw Error("Can't find axis: " + axisType + " - Check " + "require() dependencies.");
246                                }
247                                axis = new dc.axis2d[axisType](this, kwArgs);
248                        }else{
249                                axis = new axisType(this, kwArgs);
250                        }
251                        axis.name = name;
252                        axis.dirty = true;
253                        if(name in this.axes){
254                                this.axes[name].destroy();
255                        }
256                        this.axes[name] = axis;
257                        this.dirty = true;
258                        return this;    //      dojox/charting/Chart
259                },
260                getAxis: function(name){
261                        // summary:
262                        //              Get the given axis, by name.
263                        // name: String
264                        //              The name the axis was defined by.
265                        // returns: dojox/charting/axis2d/Default
266                        //              The axis as stored in the chart's axis map.
267                        return this.axes[name]; //      dojox/charting/axis2d/Default
268                },
269                removeAxis: function(name){
270                        // summary:
271                        //              Remove the axis that was defined using name.
272                        // name: String
273                        //              The axis name, as defined in addAxis.
274                        // returns: dojox/charting/Chart
275                        //              A reference to the current chart for functional chaining.
276                        if(name in this.axes){
277                                // destroy the axis
278                                this.axes[name].destroy();
279                                delete this.axes[name];
280                                // mark the chart as dirty
281                                this.dirty = true;
282                        }
283                        return this;    //      dojox/charting/Chart
284                },
285                addPlot: function(name, kwArgs){
286                        // summary:
287                        //              Add a new plot to the chart, defined by name and using the optional keyword arguments object.
288                        //              Note that dojox.charting assumes the main plot to be called "default"; if you do not have
289                        //              a plot called "default" and attempt to add data series to the chart without specifying the
290                        //              plot to be rendered on, you WILL get errors.
291                        // name: String
292                        //              The name of the plot to be added to the chart.  If you only plan on using one plot, call it "default".
293                        // kwArgs: dojox.charting.plot2d.__PlotCtorArgs
294                        //              An object with optional parameters for the plot in question.
295                        // returns: dojox/charting/Chart
296                        //              A reference to the current chart for functional chaining.
297                        var plot, plotType = kwArgs && kwArgs.type || "Default";
298                        if(typeof plotType == "string"){
299                                if(!dc.plot2d || !dc.plot2d[plotType]){
300                                        throw Error("Can't find plot: " + plotType + " - didn't you forget to dojo" + ".require() it?");
301                                }
302                                plot = new dc.plot2d[plotType](this, kwArgs);
303                        }else{
304                                plot = new plotType(this, kwArgs);
305                        }
306                        plot.name = name;
307                        plot.dirty = true;
308                        if(name in this.plots){
309                                this.stack[this.plots[name]].destroy();
310                                this.stack[this.plots[name]] = plot;
311                        }else{
312                                this.plots[name] = this.stack.length;
313                                this.stack.push(plot);
314                        }
315                        this.dirty = true;
316                        return this;    //      dojox/charting/Chart
317                },
318                getPlot: function(name){
319                        // summary:
320                        //              Get the given plot, by name.
321                        // name: String
322                        //              The name the plot was defined by.
323                        // returns: dojox/charting/plot2d/Base
324                        //              The plot.
325                        return this.stack[this.plots[name]];
326                },
327                removePlot: function(name){
328                        // summary:
329                        //              Remove the plot defined using name from the chart's plot stack.
330                        // name: String
331                        //              The name of the plot as defined using addPlot.
332                        // returns: dojox/charting/Chart
333                        //              A reference to the current chart for functional chaining.
334                        if(name in this.plots){
335                                // get the index and remove the name
336                                var index = this.plots[name];
337                                delete this.plots[name];
338                                // destroy the plot
339                                this.stack[index].destroy();
340                                // remove the plot from the stack
341                                this.stack.splice(index, 1);
342                                // update indices to reflect the shift
343                                func.forIn(this.plots, function(idx, name, plots){
344                                        if(idx > index){
345                                                plots[name] = idx - 1;
346                                        }
347                                });
348                                // remove all related series
349                                var ns = arr.filter(this.series, function(run){ return run.plot != name; });
350                                if(ns.length < this.series.length){
351                                        // kill all removed series
352                                        arr.forEach(this.series, function(run){
353                                                if(run.plot == name){
354                                                        run.destroy();
355                                                }
356                                        });
357                                        // rebuild all necessary data structures
358                                        this.runs = {};
359                                        arr.forEach(ns, function(run, index){
360                                                this.runs[run.plot] = index;
361                                        }, this);
362                                        this.series = ns;
363                                }
364                                // mark the chart as dirty
365                                this.dirty = true;
366                        }
367                        return this;    //      dojox/charting/Chart
368                },
369                getPlotOrder: function(){
370                        // summary:
371                        //              Returns an array of plot names in the current order
372                        //              (the top-most plot is the first).
373                        // returns: Array
374                        return func.map(this.stack, getName); // Array
375                },
376                setPlotOrder: function(newOrder){
377                        // summary:
378                        //              Sets new order of plots. newOrder cannot add or remove
379                        //              plots. Wrong names, or dups are ignored.
380                        // newOrder: Array
381                        //              Array of plot names compatible with getPlotOrder().
382                        // returns: dojox/charting/Chart
383                        //              A reference to the current chart for functional chaining.
384                        var names = {},
385                                order = func.filter(newOrder, function(name){
386                                        if(!(name in this.plots) || (name in names)){
387                                                return false;
388                                        }
389                                        names[name] = 1;
390                                        return true;
391                                }, this);
392                        if(order.length < this.stack.length){
393                                func.forEach(this.stack, function(plot){
394                                        var name = plot.name;
395                                        if(!(name in names)){
396                                                order.push(name);
397                                        }
398                                });
399                        }
400                        var newStack = func.map(order, function(name){
401                                        return this.stack[this.plots[name]];
402                                }, this);
403                        func.forEach(newStack, function(plot, i){
404                                this.plots[plot.name] = i;
405                        }, this);
406                        this.stack = newStack;
407                        this.dirty = true;
408                        return this;    //      dojox/charting/Chart
409                },
410                movePlotToFront: function(name){
411                        // summary:
412                        //              Moves a given plot to front.
413                        // name: String
414                        //              Plot's name to move.
415                        // returns: dojox/charting/Chart
416                        //              A reference to the current chart for functional chaining.
417                        if(name in this.plots){
418                                var index = this.plots[name];
419                                if(index){
420                                        var newOrder = this.getPlotOrder();
421                                        newOrder.splice(index, 1);
422                                        newOrder.unshift(name);
423                                        return this.setPlotOrder(newOrder);     //      dojox/charting/Chart
424                                }
425                        }
426                        return this;    //      dojox/charting/Chart
427                },
428                movePlotToBack: function(name){
429                        // summary:
430                        //              Moves a given plot to back.
431                        // name: String
432                        //              Plot's name to move.
433                        // returns: dojox/charting/Chart
434                        //              A reference to the current chart for functional chaining.
435                        if(name in this.plots){
436                                var index = this.plots[name];
437                                if(index < this.stack.length - 1){
438                                        var newOrder = this.getPlotOrder();
439                                        newOrder.splice(index, 1);
440                                        newOrder.push(name);
441                                        return this.setPlotOrder(newOrder);     //      dojox/charting/Chart
442                                }
443                        }
444                        return this;    //      dojox/charting/Chart
445                },
446                addSeries: function(name, data, kwArgs){
447                        // summary:
448                        //              Add a data series to the chart for rendering.
449                        // name: String
450                        //              The name of the data series to be plotted.
451                        // data: Array|Object
452                        //              The array of data points (either numbers or objects) that
453                        //              represents the data to be drawn. Or it can be an object. In
454                        //              the latter case, it should have a property "data" (an array),
455                        //              destroy(), and setSeriesObject().
456                        // kwArgs: __SeriesCtorArgs?
457                        //              An optional keyword arguments object that will be mixed into
458                        //              the resultant series object.
459                        // returns: dojox/charting/Chart
460                        //              A reference to the current chart for functional chaining.
461                        var run = new Series(this, data, kwArgs);
462                        run.name = name;
463                        if(name in this.runs){
464                                this.series[this.runs[name]].destroy();
465                                this.series[this.runs[name]] = run;
466                        }else{
467                                this.runs[name] = this.series.length;
468                                this.series.push(run);
469                        }
470                        this.dirty = true;
471                        // fix min/max
472                        if(!("ymin" in run) && "min" in run){ run.ymin = run.min; }
473                        if(!("ymax" in run) && "max" in run){ run.ymax = run.max; }
474                        return this;    //      dojox/charting/Chart
475                },
476                getSeries: function(name){
477                        // summary:
478                        //              Get the given series, by name.
479                        // name: String
480                        //              The name the series was defined by.
481                        // returns: dojox/charting/Series
482                        //              The series.
483                        return this.series[this.runs[name]];
484                },
485                removeSeries: function(name){
486                        // summary:
487                        //              Remove the series defined by name from the chart.
488                        // name: String
489                        //              The name of the series as defined by addSeries.
490                        // returns: dojox/charting/Chart
491                        //              A reference to the current chart for functional chaining.
492                        if(name in this.runs){
493                                // get the index and remove the name
494                                var index = this.runs[name];
495                                delete this.runs[name];
496                                // destroy the run
497                                this.series[index].destroy();
498                                // remove the run from the stack of series
499                                this.series.splice(index, 1);
500                                // update indices to reflect the shift
501                                func.forIn(this.runs, function(idx, name, runs){
502                                        if(idx > index){
503                                                runs[name] = idx - 1;
504                                        }
505                                });
506                                this.dirty = true;
507                        }
508                        return this;    //      dojox/charting/Chart
509                },
510                updateSeries: function(name, data, offsets){
511                        // summary:
512                        //              Update the given series with a new set of data points.
513                        // name: String
514                        //              The name of the series as defined in addSeries.
515                        // data: Array|Object
516                        //              The array of data points (either numbers or objects) that
517                        //              represents the data to be drawn. Or it can be an object. In
518                        //              the latter case, it should have a property "data" (an array),
519                        //              destroy(), and setSeriesObject().
520                        // offsets: Boolean?
521                        //              If true recomputes the offsets of the chart based on the new
522                        //              data. This is useful if the range of data is drastically changing
523                        //              and offsets need to be recomputed.
524                        // returns: dojox/charting/Chart
525                        //              A reference to the current chart for functional chaining.
526                        if(name in this.runs){
527                                var run = this.series[this.runs[name]];
528                                run.update(data);
529                                if(offsets){
530                                        this.dirty = true;
531                                }else{
532                                        this._invalidateDependentPlots(run.plot, false);
533                                        this._invalidateDependentPlots(run.plot, true);
534                                }
535                        }
536                        return this;    //      dojox/charting/Chart
537                },
538                getSeriesOrder: function(plotName){
539                        // summary:
540                        //              Returns an array of series names in the current order
541                        //              (the top-most series is the first) within a plot.
542                        // plotName: String
543                        //              Plot's name.
544                        // returns: Array
545                        return func.map(func.filter(this.series, function(run){
546                                        return run.plot == plotName;
547                                }), getName);
548                },
549                setSeriesOrder: function(newOrder){
550                        // summary:
551                        //              Sets new order of series within a plot. newOrder cannot add
552                        //              or remove series. Wrong names, or dups are ignored.
553                        // newOrder: Array
554                        //              Array of series names compatible with getPlotOrder(). All
555                        //              series should belong to the same plot.
556                        // returns: dojox/charting/Chart
557                        //              A reference to the current chart for functional chaining.
558                        var plotName, names = {},
559                                order = func.filter(newOrder, function(name){
560                                        if(!(name in this.runs) || (name in names)){
561                                                return false;
562                                        }
563                                        var run = this.series[this.runs[name]];
564                                        if(plotName){
565                                                if(run.plot != plotName){
566                                                        return false;
567                                                }
568                                        }else{
569                                                plotName = run.plot;
570                                        }
571                                        names[name] = 1;
572                                        return true;
573                                }, this);
574                        func.forEach(this.series, function(run){
575                                var name = run.name;
576                                if(!(name in names) && run.plot == plotName){
577                                        order.push(name);
578                                }
579                        });
580                        var newSeries = func.map(order, function(name){
581                                        return this.series[this.runs[name]];
582                                }, this);
583                        this.series = newSeries.concat(func.filter(this.series, function(run){
584                                return run.plot != plotName;
585                        }));
586                        func.forEach(this.series, function(run, i){
587                                this.runs[run.name] = i;
588                        }, this);
589                        this.dirty = true;
590                        return this;    //      dojox/charting/Chart
591                },
592                moveSeriesToFront: function(name){
593                        // summary:
594                        //              Moves a given series to front of a plot.
595                        // name: String
596                        //              Series' name to move.
597                        // returns: dojox/charting/Chart
598                        //              A reference to the current chart for functional chaining.
599                        if(name in this.runs){
600                                var index = this.runs[name],
601                                        newOrder = this.getSeriesOrder(this.series[index].plot);
602                                if(name != newOrder[0]){
603                                        newOrder.splice(index, 1);
604                                        newOrder.unshift(name);
605                                        return this.setSeriesOrder(newOrder);   //      dojox/charting/Chart
606                                }
607                        }
608                        return this;    //      dojox/charting/Chart
609                },
610                moveSeriesToBack: function(name){
611                        // summary:
612                        //              Moves a given series to back of a plot.
613                        // name: String
614                        //              Series' name to move.
615                        // returns: dojox/charting/Chart
616                        //              A reference to the current chart for functional chaining.
617                        if(name in this.runs){
618                                var index = this.runs[name],
619                                        newOrder = this.getSeriesOrder(this.series[index].plot);
620                                if(name != newOrder[newOrder.length - 1]){
621                                        newOrder.splice(index, 1);
622                                        newOrder.push(name);
623                                        return this.setSeriesOrder(newOrder);   //      dojox/charting/Chart
624                                }
625                        }
626                        return this;    //      dojox/charting/Chart
627                },
628                resize: function(width, height){
629                        // summary:
630                        //              Resize the chart to the dimensions of width and height.
631                        // description:
632                        //              Resize the chart and its surface to the width and height dimensions.
633                        //              If a single argument of the form {w: value1, h: value2} is provided take that argument as the dimensions to use.
634                        //              Finally if no argument is provided, resize the surface to the marginBox of the chart.
635                        // width: Number|Object?
636                        //              The new width of the chart or the box definition.
637                        // height: Number?
638                        //              The new height of the chart.
639                        // returns: dojox/charting/Chart
640                        //              A reference to the current chart for functional chaining.
641                        switch(arguments.length){
642                                // case 0, do not resize the div, just the surface
643                                case 1:
644                                        // argument, override node box
645                                        domGeom.setMarginBox(this.node, width);
646                                        break;
647                                case 2:
648                                        // argument, override node box
649                                        domGeom.setMarginBox(this.node, {w: width, h: height});
650                                        break;
651                        }
652                        // in all cases take back the computed box
653                        var box = domGeom.getMarginBox(this.node);
654                        var d = this.surface.getDimensions();
655                        if(d.width != box.w || d.height != box.h){
656                                // and set it on the surface
657                                this.surface.setDimensions(box.w, box.h);
658                                this.dirty = true;
659                                return this.render();   //      dojox/charting/Chart
660                        }else{
661                                return this;
662                        }
663                },
664                getGeometry: function(){
665                        // summary:
666                        //              Returns a map of information about all axes in a chart and what they represent
667                        //              in terms of scaling (see dojox.charting.axis2d.Default.getScaler).
668                        // returns: Object
669                        //              An map of geometry objects, a one-to-one mapping of axes.
670                        var ret = {};
671                        func.forIn(this.axes, function(axis){
672                                if(axis.initialized()){
673                                        ret[axis.name] = {
674                                                name:           axis.name,
675                                                vertical:       axis.vertical,
676                                                scaler:         axis.scaler,
677                                                ticks:          axis.ticks
678                                        };
679                                }
680                        });
681                        return ret;     //      Object
682                },
683                setAxisWindow: function(name, scale, offset, zoom){
684                        // summary:
685                        //              Zooms an axis and all dependent plots. Can be used to zoom in 1D.
686                        // name: String
687                        //              The name of the axis as defined by addAxis.
688                        // scale: Number
689                        //              The scale on the target axis.
690                        // offset: Number
691                        //              Any offest, as measured by axis tick
692                        // zoom: Boolean|Object?
693                        //              The chart zooming animation trigger.  This is null by default,
694                        //              e.g. {duration: 1200}, or just set true.
695                        // returns: dojox/charting/Chart
696                        //              A reference to the current chart for functional chaining.
697                        var axis = this.axes[name];
698                        if(axis){
699                                axis.setWindow(scale, offset);
700                                arr.forEach(this.stack,function(plot){
701                                        if(plot.hAxis == name || plot.vAxis == name){
702                                                plot.zoom = zoom;
703                                        }
704                                });
705                        }
706                        return this;    //      dojox/charting/Chart
707                },
708                setWindow: function(sx, sy, dx, dy, zoom){
709                        // summary:
710                        //              Zooms in or out any plots in two dimensions.
711                        // sx: Number
712                        //              The scale for the x axis.
713                        // sy: Number
714                        //              The scale for the y axis.
715                        // dx: Number
716                        //              The pixel offset on the x axis.
717                        // dy: Number
718                        //              The pixel offset on the y axis.
719                        // zoom: Boolean|Object?
720                        //              The chart zooming animation trigger.  This is null by default,
721                        //              e.g. {duration: 1200}, or just set true.
722                        // returns: dojox/charting/Chart
723                        //              A reference to the current chart for functional chaining.
724                        if(!("plotArea" in this)){
725                                this.calculateGeometry();
726                        }
727                        func.forIn(this.axes, function(axis){
728                                var scale, offset, bounds = axis.getScaler().bounds,
729                                        s = bounds.span / (bounds.upper - bounds.lower);
730                                if(axis.vertical){
731                                        scale  = sy;
732                                        offset = dy / s / scale;
733                                }else{
734                                        scale  = sx;
735                                        offset = dx / s / scale;
736                                }
737                                axis.setWindow(scale, offset);
738                        });
739                        arr.forEach(this.stack, function(plot){ plot.zoom = zoom; });
740                        return this;    //      dojox/charting/Chart
741                },
742                zoomIn: function(name, range, delayed){
743                        // summary:
744                        //              Zoom the chart to a specific range on one axis.  This calls render()
745                        //              directly as a convenience method.
746                        // name: String
747                        //              The name of the axis as defined by addAxis.
748                        // range: Array
749                        //              The end points of the zoom range, measured in axis ticks.
750                        var axis = this.axes[name];
751                        if(axis){
752                                var scale, offset, bounds = axis.getScaler().bounds;
753                                var lower = Math.min(range[0],range[1]);
754                                var upper = Math.max(range[0],range[1]);
755                                lower = range[0] < bounds.lower ? bounds.lower : lower;
756                                upper = range[1] > bounds.upper ? bounds.upper : upper;
757                                scale = (bounds.upper - bounds.lower) / (upper - lower);
758                                offset = lower - bounds.lower;
759                                this.setAxisWindow(name, scale, offset);
760                                if(delayed){
761                                        this.delayedRender();
762                                }else{
763                                        this.render();
764                                }
765                        }
766                },
767                calculateGeometry: function(){
768                        // summary:
769                        //              Calculate the geometry of the chart based on the defined axes of
770                        //              a chart.
771                        // returns: dojox/charting/Chart
772                        //              A reference to the current chart for functional chaining.
773                        if(this.dirty){
774                                return this.fullGeometry();
775                        }
776
777                        // calculate geometry
778                        var dirty = arr.filter(this.stack, function(plot){
779                                        return plot.dirty ||
780                                                (plot.hAxis && this.axes[plot.hAxis].dirty) ||
781                                                (plot.vAxis && this.axes[plot.vAxis].dirty);
782                                }, this);
783                        calculateAxes(dirty, this.plotArea);
784
785                        return this;    //      dojox/charting/Chart
786                },
787                fullGeometry: function(){
788                        // summary:
789                        //              Calculate the full geometry of the chart.  This includes passing
790                        //              over all major elements of a chart (plots, axes, series, container)
791                        //              in order to ensure proper rendering.
792                        // returns: dojox/charting/Chart
793                        //              A reference to the current chart for functional chaining.
794                        this._makeDirty();
795
796                        // clear old values
797                        arr.forEach(this.stack, clear);
798
799                        // rebuild new connections, and add defaults
800
801                        // set up a theme
802                        if(!this.theme){
803                                this.setTheme(new SimpleTheme());
804                        }
805
806                        // assign series
807                        arr.forEach(this.series, function(run){
808                                if(!(run.plot in this.plots)){
809                                        // TODO remove auto-assignment
810                                        if(!dc.plot2d || !dc.plot2d.Default){
811                                                throw Error("Can't find plot: Default - didn't you forget to dojo" + ".require() it?");
812                                        }
813                                        var plot = new dc.plot2d.Default(this, {});
814                                        plot.name = run.plot;
815                                        this.plots[run.plot] = this.stack.length;
816                                        this.stack.push(plot);
817                                }
818                                this.stack[this.plots[run.plot]].addSeries(run);
819                        }, this);
820                        // assign axes
821                        arr.forEach(this.stack, function(plot){
822                                if(plot.assignAxes){
823                                        plot.assignAxes(this.axes);
824                                }
825                        }, this);
826
827                        // calculate geometry
828
829                        // 1st pass
830                        var dim = this.dim = this.surface.getDimensions();
831                        dim.width  = g.normalizedLength(dim.width);
832                        dim.height = g.normalizedLength(dim.height);
833                        func.forIn(this.axes, clear);
834                        calculateAxes(this.stack, dim);
835
836                        // assumption: we don't have stacked axes yet
837                        var offsets = this.offsets = {l: 0, r: 0, t: 0, b: 0};
838                        // chart mirroring starts
839                        var self = this;
840                        func.forIn(this.axes, function(axis){
841                                if(has("dojo-bidi")){
842                                        self._resetLeftBottom(axis);
843                                }
844                                func.forIn(axis.getOffsets(), function(o, i){ offsets[i] = Math.max(o, offsets[i]); });
845                        });
846                        // chart mirroring ends
847                        // add title area
848                        if(this.title){
849                                this.titleGap = (this.titleGap==0) ? 0 : this.titleGap || this.theme.chart.titleGap || 20;
850                                this.titlePos = this.titlePos || this.theme.chart.titlePos || "top";
851                                this.titleFont = this.titleFont || this.theme.chart.titleFont;
852                                this.titleFontColor = this.titleFontColor || this.theme.chart.titleFontColor || "black";
853                                var tsize = g.normalizedLength(g.splitFontString(this.titleFont).size);
854                                offsets[this.titlePos == "top" ? "t" : "b"] += (tsize + this.titleGap);
855                        }
856                        // add margins
857                        func.forIn(this.margins, function(o, i){ offsets[i] += o; });
858
859                        // 2nd pass with realistic dimensions
860                        this.plotArea = {
861                                width: dim.width - offsets.l - offsets.r,
862                                height: dim.height - offsets.t - offsets.b
863                        };
864                        func.forIn(this.axes, clear);
865                        calculateAxes(this.stack, this.plotArea);
866
867                        return this;    //      dojox/charting/Chart
868                },
869                render: function(){
870                        // summary:
871                        //              Render the chart according to the current information defined.  This should
872                        //              be the last call made when defining/creating a chart, or if data within the
873                        //              chart has been changed.
874                        // returns: dojox/charting/Chart
875                        //              A reference to the current chart for functional chaining.
876
877                        // do we have a delayed renderer pending? If yes we need to clear it
878                        if(this._delayedRenderHandle){
879                                clearTimeout(this._delayedRenderHandle);
880                                this._delayedRenderHandle = null;
881                        }
882                       
883                        if(this.theme){
884                                this.theme.clear();
885                        }
886
887                        if(this.dirty){
888                                return this.fullRender();
889                        }
890
891                        this.calculateGeometry();
892
893                        // go over the stack backwards
894                        func.forEachRev(this.stack, function(plot){ plot.render(this.dim, this.offsets); }, this);
895
896                        // go over axes
897                        func.forIn(this.axes, function(axis){ axis.render(this.dim, this.offsets); }, this);
898
899                        this._makeClean();
900
901                        return this;    //      dojox/charting/Chart
902                },
903                fullRender: function(){
904                        // summary:
905                        //              Force a full rendering of the chart, including full resets on the chart itself.
906                        //              You should not call this method directly unless absolutely necessary.
907                        // returns: dojox/charting/Chart
908                        //              A reference to the current chart for functional chaining.
909
910                        // calculate geometry
911                        this.fullGeometry();
912                        var offsets = this.offsets, dim = this.dim;
913                        var w = Math.max(0, dim.width  - offsets.l - offsets.r),
914                                h = Math.max(0, dim.height - offsets.t - offsets.b);
915
916                        // get required colors
917                        //var requiredColors = func.foldl(this.stack, "z + plot.getRequiredColors()", 0);
918                        //this.theme.defineColors({num: requiredColors, cache: false});
919
920                        // clear old shapes
921                        arr.forEach(this.series, purge);
922                        func.forIn(this.axes, purge);
923                        arr.forEach(this.stack,  purge);
924                        var children = this.surface.children;
925                        // starting with 1.9 the registry is optional and thus dispose is
926                        if(shape.dispose){
927                                for(var i = 0; i < children.length;++i){
928                                        shape.dispose(children[i]);
929                                }
930                        }
931                        if(this.chartTitle && this.chartTitle.tagName){
932                                // destroy title if it is a DOM node
933                            domConstruct.destroy(this.chartTitle);
934                        }
935                        this.surface.clear();
936                        this.chartTitle = null;
937
938                        this._renderChartBackground(dim, offsets);
939                        if(this._nativeClip){
940                                this._renderPlotBackground(dim, offsets, w, h);
941                        }else{
942                                // VML
943                                this._renderPlotBackground(dim, offsets, w, h);
944                        }
945
946                        // go over the stack backwards
947                        func.foldr(this.stack, function(z, plot){ return plot.render(dim, offsets), 0; }, 0);
948
949                        if(!this._nativeClip){
950                                // VML, matting-clipping
951                                this._renderChartBackground(dim, offsets);
952                        }
953
954                        //create title: Whether to make chart title as a widget which extends dojox.charting.Element?
955                        if(this.title){
956                                var forceHtmlLabels = (g.renderer == "canvas") && this.htmlLabels,
957                                        labelType = forceHtmlLabels || !has("ie") && !has("opera") && this.htmlLabels ? "html" : "gfx",
958                                        tsize = g.normalizedLength(g.splitFontString(this.titleFont).size);
959                                this.chartTitle = common.createText[labelType](
960                                        this,
961                                        this.surface,
962                                        dim.width/2,
963                                        this.titlePos=="top" ? tsize + this.margins.t : dim.height - this.margins.b,
964                                        "middle",
965                                        this.title,
966                                        this.titleFont,
967                                        this.titleFontColor
968                                );
969                        }
970
971                        // go over axes
972                        func.forIn(this.axes, function(axis){ axis.render(dim, offsets); });
973
974                        this._makeClean();
975
976                        return this;    //      dojox/charting/Chart
977                },
978                _renderChartBackground: function(dim, offsets){
979                        var t = this.theme, rect;
980                        // chart background
981                        var fill   = this.fill   !== undefined ? this.fill   : (t.chart && t.chart.fill);
982                        var stroke = this.stroke !== undefined ? this.stroke : (t.chart && t.chart.stroke);
983
984                        // TRT: support for "inherit" as a named value in a theme.
985                        if(fill == "inherit"){
986                                //      find the background color of the nearest ancestor node, and use that explicitly.
987                                var node = this.node;
988                                fill = new Color(domStyle.get(node, "backgroundColor"));
989                                while(fill.a==0 && node!=document.documentElement){
990                                        fill = new Color(domStyle.get(node, "backgroundColor"));
991                                        node = node.parentNode;
992                                }
993                        }
994
995                        if(fill){
996                                if(this._nativeClip){
997                                        fill = Element.prototype._shapeFill(Element.prototype._plotFill(fill, dim),
998                                                { x:0, y: 0, width: dim.width + 1, height: dim.height + 1 });
999                                        this.surface.createRect({ width: dim.width + 1, height: dim.height + 1 }).setFill(fill);
1000                                }else{
1001                                        // VML
1002                                        fill = Element.prototype._plotFill(fill, dim, offsets);
1003                                        if(offsets.l){  // left
1004                                                rect = {
1005                                                        x: 0,
1006                                                        y: 0,
1007                                                        width:  offsets.l,
1008                                                        height: dim.height + 1
1009                                                };
1010                                                this.surface.createRect(rect).setFill(Element.prototype._shapeFill(fill, rect));
1011                                        }
1012                                        if(offsets.r){  // right
1013                                                rect = {
1014                                                        x: dim.width - offsets.r,
1015                                                        y: 0,
1016                                                        width:  offsets.r + 1,
1017                                                        height: dim.height + 2
1018                                                };
1019                                                this.surface.createRect(rect).setFill(Element.prototype._shapeFill(fill, rect));
1020                                        }
1021                                        if(offsets.t){  // top
1022                                                rect = {
1023                                                        x: 0,
1024                                                        y: 0,
1025                                                        width:  dim.width + 1,
1026                                                        height: offsets.t
1027                                                };
1028                                                this.surface.createRect(rect).setFill(Element.prototype._shapeFill(fill, rect));
1029                                        }
1030                                        if(offsets.b){  // bottom
1031                                                rect = {
1032                                                        x: 0,
1033                                                        y: dim.height - offsets.b,
1034                                                        width:  dim.width + 1,
1035                                                        height: offsets.b + 2
1036                                                };
1037                                                this.surface.createRect(rect).setFill(Element.prototype._shapeFill(fill, rect));
1038                                        }
1039                                }
1040                        }
1041                        if(stroke){
1042                                this.surface.createRect({
1043                                        width:  dim.width - 1,
1044                                        height: dim.height - 1
1045                                }).setStroke(stroke);
1046                        }
1047                },
1048                _renderPlotBackground: function(dim, offsets, w, h){
1049                        var t = this.theme;
1050
1051                        // draw a plot background
1052                        var fill   = t.plotarea && t.plotarea.fill;
1053                        var stroke = t.plotarea && t.plotarea.stroke;
1054                        // size might be neg if offsets are bigger that chart size this happens quite often at
1055                        // initialization time if the chart widget is used in a BorderContainer
1056                        // this will fail on IE/VML
1057                        var rect = {
1058                                x: offsets.l - 1, y: offsets.t - 1,
1059                                width:  w + 2,
1060                                height: h + 2
1061                        };
1062                        if(fill){
1063                                fill = Element.prototype._shapeFill(Element.prototype._plotFill(fill, dim, offsets), rect);
1064                                this.surface.createRect(rect).setFill(fill);
1065                        }
1066                        if(stroke){
1067                                this.surface.createRect({
1068                                        x: offsets.l, y: offsets.t,
1069                                        width:  w + 1,
1070                                        height: h + 1
1071                                }).setStroke(stroke);
1072                        }
1073                },
1074                delayedRender: function(){
1075                        // summary:
1076                        //              Delayed render, which is used to collect multiple updates
1077                        //              within a delayInMs time window.
1078                        // returns: dojox/charting/Chart
1079                        //              A reference to the current chart for functional chaining.
1080
1081                        if(!this._delayedRenderHandle){
1082                                this._delayedRenderHandle = setTimeout(
1083                                        lang.hitch(this, function(){
1084                                                this.render();
1085                                        }),
1086                                        this.delayInMs
1087                                );
1088                        }
1089
1090                        return this;    //      dojox/charting/Chart
1091                },
1092                connectToPlot: function(name, object, method){
1093                        // summary:
1094                        //              A convenience method to connect a function to a plot.
1095                        // name: String
1096                        //              The name of the plot as defined by addPlot.
1097                        // object: Object
1098                        //              The object to be connected.
1099                        // method: Function
1100                        //              The function to be executed.
1101                        // returns: Array
1102                        //              A handle to the connection, as defined by dojo.connect (see dojo.connect).
1103                        return name in this.plots ? this.stack[this.plots[name]].connect(object, method) : null;        //      Array
1104                },
1105                fireEvent: function(seriesName, eventName, index){
1106                        // summary:
1107                        //              Fires a synthetic event for a series item.
1108                        // seriesName: String
1109                        //              Series name.
1110                        // eventName: String
1111                        //              Event name to simulate: onmouseover, onmouseout, onclick.
1112                        // index: Number
1113                        //              Valid data value index for the event.
1114                        // returns: dojox/charting/Chart
1115                        //              A reference to the current chart for functional chaining.
1116                        if(seriesName in this.runs){
1117                                var plotName = this.series[this.runs[seriesName]].plot;
1118                                if(plotName in this.plots){
1119                                        var plot = this.stack[this.plots[plotName]];
1120                                        if(plot){
1121                                                plot.fireEvent(seriesName, eventName, index);
1122                                        }
1123                                }
1124                        }
1125                        return this;    //      dojox/charting/Chart
1126                },
1127                _makeClean: function(){
1128                        // reset dirty flags
1129                        arr.forEach(this.axes,   makeClean);
1130                        arr.forEach(this.stack,  makeClean);
1131                        arr.forEach(this.series, makeClean);
1132                        this.dirty = false;
1133                },
1134                _makeDirty: function(){
1135                        // reset dirty flags
1136                        arr.forEach(this.axes,   makeDirty);
1137                        arr.forEach(this.stack,  makeDirty);
1138                        arr.forEach(this.series, makeDirty);
1139                        this.dirty = true;
1140                },
1141                _invalidateDependentPlots: function(plotName, /* Boolean */ verticalAxis){
1142                        if(plotName in this.plots){
1143                                var plot = this.stack[this.plots[plotName]], axis,
1144                                        axisName = verticalAxis ? "vAxis" : "hAxis";
1145                                if(plot[axisName]){
1146                                        axis = this.axes[plot[axisName]];
1147                                        if(axis && axis.dependOnData()){
1148                                                axis.dirty = true;
1149                                                // find all plots and mark them dirty
1150                                                arr.forEach(this.stack, function(p){
1151                                                        if(p[axisName] && p[axisName] == plot[axisName]){
1152                                                                p.dirty = true;
1153                                                        }
1154                                                });
1155                                        }
1156                                }else{
1157                                        plot.dirty = true;
1158                                }
1159                        }
1160                },
1161                setDir : function(dir){
1162                        return this;
1163                },
1164                _resetLeftBottom: function(axis){
1165                },
1166                formatTruncatedLabel: function(element, label, labelType){                     
1167                }
1168        });
1169
1170        function hSection(stats){
1171                return {min: stats.hmin, max: stats.hmax};
1172        }
1173
1174        function vSection(stats){
1175                return {min: stats.vmin, max: stats.vmax};
1176        }
1177
1178        function hReplace(stats, h){
1179                stats.hmin = h.min;
1180                stats.hmax = h.max;
1181        }
1182
1183        function vReplace(stats, v){
1184                stats.vmin = v.min;
1185                stats.vmax = v.max;
1186        }
1187
1188        function combineStats(target, source){
1189                if(target && source){
1190                        target.min = Math.min(target.min, source.min);
1191                        target.max = Math.max(target.max, source.max);
1192                }
1193                return target || source;
1194        }
1195
1196        function calculateAxes(stack, plotArea){
1197                var plots = {}, axes = {};
1198                arr.forEach(stack, function(plot){
1199                        var stats = plots[plot.name] = plot.getSeriesStats();
1200                        if(plot.hAxis){
1201                                axes[plot.hAxis] = combineStats(axes[plot.hAxis], hSection(stats));
1202                        }
1203                        if(plot.vAxis){
1204                                axes[plot.vAxis] = combineStats(axes[plot.vAxis], vSection(stats));
1205                        }
1206                });
1207                arr.forEach(stack, function(plot){
1208                        var stats = plots[plot.name];
1209                        if(plot.hAxis){
1210                                hReplace(stats, axes[plot.hAxis]);
1211                        }
1212                        if(plot.vAxis){
1213                                vReplace(stats, axes[plot.vAxis]);
1214                        }
1215                        plot.initializeScalers(plotArea, stats);
1216                });
1217        }
1218       
1219        return has("dojo-bidi")? declare("dojox.charting.Chart", [Chart, BidiChart]) : Chart;
1220});
Note: See TracBrowser for help on using the repository browser.