source: Dev/trunk/src/client/dojox/charting/SimpleTheme.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: 18.4 KB
Line 
1define(["dojo/_base/lang", "dojo/_base/array","dojo/_base/declare","dojo/_base/Color", "dojox/lang/utils", "dojox/gfx/gradutils"],
2        function(lang, arr, declare, Color, dlu, dgg){
3       
4        var SimpleTheme = declare("dojox.charting.SimpleTheme", null, {
5        // summary:
6        //              A SimpleTheme or Theme is a pre-defined object, primarily JSON-based, that makes up the definitions to
7        //              style a chart.
8        //
9        // description:
10        //              While you can set up style definitions on a chart directly (usually through the various add methods
11        //              on a dojox.charting.Chart object), a Theme simplifies this manual setup by allowing you to
12        //              pre-define all of the various visual parameters of each element in a chart.
13        //
14        //              Most of the properties of a Theme are straight-forward; if something is line-based (such as
15        //              an axis or the ticks on an axis), they will be defined using basic stroke parameters.  Likewise,
16        //              if an element is primarily block-based (such as the background of a chart), it will be primarily
17        //              fill-based.
18        //
19        //              In addition (for convenience), a Theme definition does not have to contain the entire JSON-based
20        //              structure.  Each theme is built on top of a default theme (which serves as the basis for the theme
21        //              "GreySkies"), and is mixed into the default theme object.  This allows you to create a theme based,
22        //              say, solely on colors for data series.
23        //
24        //              Defining a new theme is relatively easy; see any of the themes in dojox.charting.themes for examples
25        //              on how to define your own.
26        //
27        //              When you set a theme on a chart, the theme itself is deep-cloned.  This means that you cannot alter
28        //              the theme itself after setting the theme value on a chart, and expect it to change your chart.  If you
29        //              are looking to make alterations to a theme for a chart, the suggestion would be to create your own
30        //              theme, based on the one you want to use, that makes those alterations before it is applied to a chart.
31        //
32        //              Finally, a Theme contains a number of functions to facilitate rendering operations on a chart--the main
33        //              helper of which is the ~next~ method, in which a chart asks for the information for the next data series
34        //              to be rendered.
35        //
36        //              A note on colors:
37        //              A theme palette is usually comprised of 5 different color definitions, and
38        //              no more.  If you have a need to render a chart with more than 5 data elements, you can simply "push"
39        //              new color definitions into the theme's .color array.  Make sure that you do that with the actual
40        //              theme object from a Chart, and not in the theme itself (i.e. either do that before using .setTheme
41        //              on a chart).
42        //
43        // example:
44        //              The default theme (and structure) looks like so:
45        //      |       // all objects are structs used directly in dojox.gfx
46        //      |       chart:{
47        //      |               stroke: null,
48        //      |               fill: "white",
49        //      |               pageStyle: null // suggested page style as an object suitable for dojo.style()
50        //      |       },
51        //      |       plotarea:{
52        //      |               stroke: null,
53        //      |               fill: "white"
54        //      |       },
55        //      |       axis:{
56        //      |               stroke: { // the axis itself
57        //      |                       color: "#333",
58        //      |                       width: 1
59        //      |               },
60        //      |               tick: { // used as a foundation for all ticks
61        //      |                       color:     "#666",
62        //      |                       position:  "center",
63        //      |                       font:      "normal normal normal 7pt Tahoma",   // labels on axis
64        //      |                       fontColor: "#333"                                                               // color of labels
65        //      |               },
66        //      |               majorTick:      { // major ticks on axis, and used for major gridlines
67        //      |                       width:  1,
68        //      |                       length: 6
69        //      |               },
70        //      |               minorTick:      { // minor ticks on axis, and used for minor gridlines
71        //      |                       width:  0.8,
72        //      |                       length: 3
73        //      |               },
74        //      |               microTick:      { // minor ticks on axis, and used for minor gridlines
75        //      |                       width:  0.5,
76        //      |                       length: 1
77        //      |               },
78        //      |               title: {
79        //      |                       gap:  15,
80        //      |                       font: "normal normal normal 11pt Tahoma",       // title font
81        //      |                       fontColor: "#333",                                                      // title font color
82        //      |                       orientation: "axis"                                             // "axis": facing the axis, "away": facing away
83        //      |               }
84        //      |       },
85        //      |       series: {
86        //      |               stroke:  {width: 1.5, color: "#333"},           // line
87        //      |               outline: {width: 0.1, color: "#ccc"},           // outline
88        //      |               //shadow:  {dx: 1, dy: 1, width: 2, color: [0, 0, 0, 0.3]},
89        //      |               shadow: null,                                                           // no shadow
90        //      |               //filter:  dojox/gfx/filters.createFilter(),
91        //      |               filter: null,                                                           // no filter, to use a filter you must use gfx SVG render and require dojox/gfx/svgext
92        //      |               fill:    "#ccc",                                                        // fill, if appropriate
93        //      |               font:    "normal normal normal 8pt Tahoma",     // if there's a label
94        //      |               fontColor: "#000"                                                       // color of labels
95        //      |               labelWiring: {width: 1, color: "#ccc"},         // connect marker and target data item(slice, column, bar...)
96        //      |       },
97        //      |       marker: {       // any markers on a series
98        //      |               symbol:  "m-3,3 l3,-6 3,6 z",                           // symbol
99        //      |               stroke:  {width: 1.5, color: "#333"},           // stroke
100        //      |               outline: {width: 0.1, color: "#ccc"},           // outline
101        //      |               shadow: null,                                                           // no shadow
102        //      |               fill:    "#ccc",                                                        // fill if needed
103        //      |               font:    "normal normal normal 8pt Tahoma",     // label
104        //      |               fontColor: "#000"
105        //      |       },
106        //      |       grid: { // grid, when not present axis tick strokes are used instead
107        //      |               majorLine: {    // major grid line
108        //      |                       color:     "#666",
109        //      |                       width:  1,
110        //      |                       length: 6
111        //      |               },
112        //      |               minorLine: {    // minor grid line
113        //      |                       color:     "#666",
114        //      |                       width:  0.8,
115        //      |                       length: 3
116        //      |               },
117        //      |               fill: "grey",  // every other stripe
118        //      |               alternateFill: "grey" // alternate stripe
119        //      |       },
120        //      |       indicator: {
121        //      |               lineStroke:  {width: 1.5, color: "#333"},               // line
122        //      |               lineOutline: {width: 0.1, color: "#ccc"},               // line outline
123        //      |               lineShadow: null,                                                               // no line shadow
124        //      |               lineFill: null,                                                                 // fill between lines for dual indicators
125        //      |               stroke:  {width: 1.5, color: "#333"},                   // label background stroke
126        //      |               outline: {width: 0.1, color: "#ccc"},                   // label background outline
127        //      |               shadow: null,                                                                   // no label background shadow
128        //      |               fill:  "#ccc",                                                                  // label background fill
129        //      |               radius: 3,                                                                              // radius of the label background
130        //      |               font:    "normal normal normal 10pt Tahoma",    // label font
131        //      |               fontColor: "#000"                                                               // label color
132        //      |               markerFill:    "#ccc",                                                  // marker fill
133        //      |               markerSymbol:  "m-3,0 c0,-4 6,-4 6,0 m-6,0 c0,4 6,4 6,0",       // marker symbol
134        //      |               markerStroke:  {width: 1.5, color: "#333"},             // marker stroke
135        //      |               markerOutline: {width: 0.1, color: "#ccc"},             // marker outline
136        //      |               markerShadow: null,                                                             // no marker shadow
137        //      |       }
138        //
139        // example:
140        //              Defining a new theme is pretty simple:
141        //      |       var Grasslands = new SimpleTheme({
142        //      |               colors: [ "#70803a", "#dde574", "#788062", "#b1cc5d", "#eff2c2" ]
143        //      |       });
144        //      |
145        //      |       myChart.setTheme(Grasslands);
146
147        shapeSpaces: {shape: 1, shapeX: 1, shapeY: 1},
148
149        constructor: function(kwArgs){
150                // summary:
151                //              Initialize a theme using the keyword arguments.  Note that the arguments
152                //              look like the example (above), and may include a few more parameters.
153                kwArgs = kwArgs || {};
154
155                // populate theme with defaults updating them if needed
156                var def = SimpleTheme.defaultTheme;
157                arr.forEach(["chart", "plotarea", "axis", "grid", "series", "marker", "indicator"], function(name){
158                        this[name] = lang.delegate(def[name], kwArgs[name]);
159                }, this);
160
161                // personalize theme
162                if(kwArgs.seriesThemes && kwArgs.seriesThemes.length){
163                        this.colors  = null;
164                        this.seriesThemes = kwArgs.seriesThemes.slice(0);
165                }else{
166                        this.seriesThemes = null;
167                        this.colors = (kwArgs.colors || SimpleTheme.defaultColors).slice(0);
168                }
169                this.markerThemes = null;
170                if(kwArgs.markerThemes && kwArgs.markerThemes.length){
171                        this.markerThemes = kwArgs.markerThemes.slice(0);
172                }
173                this.markers = kwArgs.markers ? lang.clone(kwArgs.markers) : lang.delegate(SimpleTheme.defaultMarkers);
174
175                // set flags
176                this.noGradConv = kwArgs.noGradConv;
177                this.noRadialConv = kwArgs.noRadialConv;
178                if(kwArgs.reverseFills){
179                        this.reverseFills();
180                }
181
182                //      private housekeeping
183                this._current = 0;
184                this._buildMarkerArray();
185        },
186
187        clone: function(){
188                // summary:
189                //              Clone the current theme.
190                // returns: dojox.charting.SimpleTheme
191                //              The cloned theme; any alterations made will not affect the original.
192                var theme = new this.constructor({
193                        // theme components
194                        chart: this.chart,
195                        plotarea: this.plotarea,
196                        axis: this.axis,
197                        grid: this.grid,
198                        series: this.series,
199                        marker: this.marker,
200                        // individual arrays
201                        colors: this.colors,
202                        markers: this.markers,
203                        indicator: this.indicator,
204                        seriesThemes: this.seriesThemes,
205                        markerThemes: this.markerThemes,
206                        // flags
207                        noGradConv: this.noGradConv,
208                        noRadialConv: this.noRadialConv
209                });
210                // copy custom methods
211                arr.forEach(
212                        ["clone", "clear", "next", "skip", "addMixin", "post", "getTick"],
213                        function(name){
214                                if(this.hasOwnProperty(name)){
215                                        theme[name] = this[name];
216                                }
217                        },
218                        this
219                );
220                return theme;   //      dojox.charting.SimpleTheme
221        },
222
223        clear: function(){
224                // summary:
225                //              Clear and reset the internal pointer to start fresh.
226                this._current = 0;
227        },
228
229        next: function(elementType, mixin, doPost){
230                // summary:
231                //              Get the next color or series theme.
232                // elementType: String?
233                //              An optional element type (for use with series themes)
234                // mixin: Object?
235                //              An optional object to mix into the theme.
236                // doPost: Boolean?
237                //              A flag to post-process the results.
238                // returns: Object
239                //              An object of the structure { series, marker, symbol }
240                var merge = dlu.merge, series, marker;
241                if(this.colors){
242                        series = lang.delegate(this.series);
243                        marker = lang.delegate(this.marker);
244                        var color = new Color(this.colors[this._current % this.colors.length]), old;
245                        // modify the stroke
246                        if(series.stroke && series.stroke.color){
247                                series.stroke = lang.delegate(series.stroke);
248                                old = new Color(series.stroke.color);
249                                series.stroke.color = new Color(color);
250                                series.stroke.color.a = old.a;
251                        }else{
252                                series.stroke = {color: color};
253                        }
254                        if(marker.stroke && marker.stroke.color){
255                                marker.stroke = lang.delegate(marker.stroke);
256                                old = new Color(marker.stroke.color);
257                                marker.stroke.color = new Color(color);
258                                marker.stroke.color.a = old.a;
259                        }else{
260                                marker.stroke = {color: color};
261                        }
262                        // modify the fill
263                        if(!series.fill || series.fill.type){
264                                series.fill = color;
265                        }else{
266                                old = new Color(series.fill);
267                                series.fill = new Color(color);
268                                series.fill.a = old.a;
269                        }
270                        if(!marker.fill || marker.fill.type){
271                                marker.fill = color;
272                        }else{
273                                old = new Color(marker.fill);
274                                marker.fill = new Color(color);
275                                marker.fill.a = old.a;
276                        }
277                }else{
278                        series = this.seriesThemes ?
279                                merge(this.series, this.seriesThemes[this._current % this.seriesThemes.length]) :
280                                this.series;
281                        marker = this.markerThemes ?
282                                merge(this.marker, this.markerThemes[this._current % this.markerThemes.length]) :
283                                series;
284                }
285
286                var symbol = marker && marker.symbol || this._markers[this._current % this._markers.length];
287
288                var theme = {series: series, marker: marker, symbol: symbol};
289               
290                // advance the counter
291                ++this._current;
292
293                if(mixin){
294                        theme = this.addMixin(theme, elementType, mixin);
295                }
296                if(doPost){
297                        theme = this.post(theme, elementType);
298                }
299
300                return theme;   //      Object
301        },
302
303        skip: function(){
304                // summary:
305                //              Skip the next internal color.
306                ++this._current;
307        },
308
309        addMixin: function(theme, elementType, mixin, doPost){
310                // summary:
311                //              Add a mixin object to the passed theme and process.
312                // theme: dojox/charting/SimpleTheme
313                //              The theme to mixin to.
314                // elementType: String
315                //              The type of element in question. Can be "line", "bar" or "circle"
316                // mixin: Object|Array
317                //              The object or objects to mix into the theme.
318                // doPost: Boolean
319                //              If true, run the new theme through the post-processor.
320                // returns: dojox/charting/SimpleTheme
321                //              The new theme.
322                if(lang.isArray(mixin)){
323                        arr.forEach(mixin, function(m){
324                                theme = this.addMixin(theme, elementType, m);
325                        }, this);
326                }else{
327                        var t = {};
328                        if("color" in mixin){
329                                if(elementType == "line" || elementType == "area"){
330                                        lang.setObject("series.stroke.color", mixin.color, t);
331                                        lang.setObject("marker.stroke.color", mixin.color, t);
332                                }else{
333                                        lang.setObject("series.fill", mixin.color, t);
334                                }
335                        }
336                        arr.forEach(["stroke", "outline", "shadow", "fill", "filter", "font", "fontColor", "labelWiring"], function(name){
337                                var markerName = "marker" + name.charAt(0).toUpperCase() + name.substr(1),
338                                        b = markerName in mixin;
339                                if(name in mixin){
340                                        lang.setObject("series." + name, mixin[name], t);
341                                        if(!b){
342                                                lang.setObject("marker." + name, mixin[name], t);
343                                        }
344                                }
345                                if(b){
346                                        lang.setObject("marker." + name, mixin[markerName], t);
347                                }
348                        });
349                        if("marker" in mixin){
350                                t.symbol = mixin.marker;
351                                t.symbol = mixin.marker;
352                        }
353                        theme = dlu.merge(theme, t);
354                }
355                if(doPost){
356                        theme = this.post(theme, elementType);
357                }
358                return theme;   //      dojox/charting/SimpleTheme
359        },
360
361        post: function(theme, elementType){
362                // summary:
363                //              Process any post-shape fills.
364                // theme: dojox/charting/SimpleTheme
365                //              The theme to post process with.
366                // elementType: String
367                //              The type of element being filled.  Can be "bar" or "circle".
368                // returns: dojox/charting/SimpleTheme
369                //              The post-processed theme.
370                var fill = theme.series.fill, t;
371                if(!this.noGradConv && this.shapeSpaces[fill.space] && fill.type == "linear"){
372                        if(elementType == "bar"){
373                                // transpose start and end points
374                                t = {
375                                        x1: fill.y1,
376                                        y1: fill.x1,
377                                        x2: fill.y2,
378                                        y2: fill.x2
379                                };
380                        }else if(!this.noRadialConv && fill.space == "shape" && (elementType == "slice" || elementType == "circle")){
381                                // switch to radial
382                                t = {
383                                        type: "radial",
384                                        cx: 0,
385                                        cy: 0,
386                                        r:  100
387                                };
388                        }
389                        if(t){
390                                return dlu.merge(theme, {series: {fill: t}});
391                        }
392                }
393                return theme;   //      dojox/charting/SimpleTheme
394        },
395
396        getTick: function(name, mixin){
397                // summary:
398                //              Calculates and merges tick parameters.
399                // name: String
400                //              Tick name, can be "major", "minor", or "micro".
401                // mixin: Object?
402                //              Optional object to mix in to the tick.
403                var tick = this.axis.tick, tickName = name + "Tick",
404                        merge = dlu.merge;
405                if(tick){
406                        if(this.axis[tickName]){
407                                tick = merge(tick, this.axis[tickName]);
408                        }
409                }else{
410                        tick = this.axis[tickName];
411                }
412                if(mixin){
413                        if(tick){
414                                if(mixin[tickName]){
415                                        tick = merge(tick, mixin[tickName]);
416                                }
417                        }else{
418                                tick = mixin[tickName];
419                        }
420                }
421                return tick;    //      Object
422        },
423
424        inspectObjects: function(f){
425                arr.forEach(["chart", "plotarea", "axis", "grid", "series", "marker", "indicator"], function(name){
426                        f(this[name]);
427                }, this);
428                if(this.seriesThemes){
429                        arr.forEach(this.seriesThemes, f);
430                }
431                if(this.markerThemes){
432                        arr.forEach(this.markerThemes, f);
433                }
434        },
435
436        reverseFills: function(){
437                this.inspectObjects(function(o){
438                        if(o && o.fill){
439                                o.fill = dgg.reverse(o.fill);
440                        }
441                });
442        },
443
444        addMarker:function(/*String*/ name, /*String*/ segment){
445                // summary:
446                //              Add a custom marker to this theme.
447                // example:
448                //      |       myTheme.addMarker("Ellipse", foo);
449                this.markers[name] = segment;
450                this._buildMarkerArray();
451        },
452
453        setMarkers:function(/*Object*/ obj){
454                // summary:
455                //              Set all the markers of this theme at once.  obj should be a
456                //              dictionary of keys and path segments.
457                //
458                // example:
459                //      |       myTheme.setMarkers({ "CIRCLE": foo });
460                this.markers = obj;
461                this._buildMarkerArray();
462        },
463
464        _buildMarkerArray: function(){
465                this._markers = [];
466                for(var p in this.markers){
467                        this._markers.push(this.markers[p]);
468                }
469        }
470});
471
472lang.mixin(SimpleTheme, {
473        defaultMarkers: {
474                CIRCLE:   "m-3,0 c0,-4 6,-4 6,0 m-6,0 c0,4 6,4 6,0",
475                SQUARE:   "m-3,-3 l0,6 6,0 0,-6 z",
476                DIAMOND:  "m0,-3 l3,3 -3,3 -3,-3 z",
477                CROSS:    "m0,-3 l0,6 m-3,-3 l6,0",
478                X:        "m-3,-3 l6,6 m0,-6 l-6,6",
479                TRIANGLE: "m-3,3 l3,-6 3,6 z",
480                TRIANGLE_INVERTED: "m-3,-3 l3,6 3,-6 z"
481        },
482
483        defaultColors:[
484                // gray skies
485                "#54544c", "#858e94", "#6e767a", "#948585", "#474747"
486        ],
487
488        defaultTheme: {
489                // all objects are structs used directly in dojox.gfx
490                chart:{
491                        stroke: null,
492                        fill: "white",
493                        pageStyle: null,
494                        titleGap:               20,
495                        titlePos:               "top",
496                        titleFont:      "normal normal bold 14pt Tahoma",       // chart title
497                        titleFontColor: "#333"
498                },
499                plotarea:{
500                        stroke: null,
501                        fill: "white"
502                },
503                // TODO: label rotation on axis
504                axis:{
505                        stroke: { // the axis itself
506                                color: "#333",
507                                width: 1
508                        },
509                        tick: { // used as a foundation for all ticks
510                                color:     "#666",
511                                position:  "center",
512                                font:      "normal normal normal 7pt Tahoma",   // labels on axis
513                                fontColor: "#333",                                                              // color of labels
514                                labelGap:  4                                    // gap between a tick and its label in pixels
515                        },
516                        majorTick:      { // major ticks on axis, and used for major gridlines
517                                width:  1,
518                                length: 6
519                        },
520                        minorTick:      { // minor ticks on axis, and used for minor gridlines
521                                width:  0.8,
522                                length: 3
523                        },
524                        microTick:      { // minor ticks on axis, and used for minor gridlines
525                                width:  0.5,
526                                length: 1
527                        },
528                        title: {
529                                gap:  15,
530                                font: "normal normal normal 11pt Tahoma",       // title font
531                                fontColor: "#333",                                                      // title font color
532                                orientation: "axis"                                             // "axis": facing the axis, "away": facing away
533                        }
534                },
535                series: {
536                        // used as a "main" theme for series, sThemes augment it
537                        stroke:  {width: 1.5, color: "#333"},           // line
538                        outline: {width: 0.1, color: "#ccc"},           // outline
539                        //shadow:  {dx: 1, dy: 1, width: 2, color: [0, 0, 0, 0.3]},
540                        shadow: null,                                                           // no shadow
541                        fill:    "#ccc",                                                        // fill, if appropriate
542                        font:    "normal normal normal 8pt Tahoma",     // if there's a label
543                        fontColor: "#000",                                                      // color of labels
544                        labelWiring: {width: 1, color: "#ccc"}          // connect marker and target data item(slice, column, bar...)
545                },
546                marker: {       // any markers on a series
547                        stroke:  {width: 1.5, color: "#333"},           // stroke
548                        outline: {width: 0.1, color: "#ccc"},           // outline
549                        //shadow:  {dx: 1, dy: 1, width: 2, color: [0, 0, 0, 0.3]},
550                        shadow: null,                                                           // no shadow
551                        fill:    "#ccc",                                                        // fill if needed
552                        font:    "normal normal normal 8pt Tahoma",     // label
553                        fontColor: "#000"
554                },
555                indicator: {
556                        lineStroke:  {width: 1.5, color: "#333"},               
557                        lineOutline: {width: 0.1, color: "#ccc"},               
558                        lineShadow: null,
559                        lineFill: null,
560                        stroke:  {width: 1.5, color: "#333"},           
561                        outline: {width: 0.1, color: "#ccc"},           
562                        shadow: null,                                                           
563                        fill : "#ccc",
564                        radius: 3,
565                        font:    "normal normal normal 10pt Tahoma",   
566                        fontColor: "#000",                                                     
567                        markerFill:    "#ccc",                                                 
568                        markerSymbol:  "m-3,0 c0,-4 6,-4 6,0 m-6,0 c0,4 6,4 6,0",                       
569                        markerStroke:  {width: 1.5, color: "#333"},             
570                        markerOutline: {width: 0.1, color: "#ccc"},             
571                        markerShadow: null                                                             
572                }
573        }
574});
575
576return SimpleTheme;
577});
Note: See TracBrowser for help on using the repository browser.