123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633 |
- /*
- Copyright (c) 2004-2012, The Dojo Foundation All Rights Reserved.
- Available via Academic Free License >= 2.1 OR the modified BSD license.
- see: http://dojotoolkit.org/license for details
- */
- if(!dojo._hasResource["dojox.charting.Theme"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
- dojo._hasResource["dojox.charting.Theme"] = true;
- dojo.provide("dojox.charting.Theme");
- dojo.require("dojox.color");
- dojo.require("dojox.color.Palette");
- dojo.require("dojox.lang.utils");
- dojo.require("dojox.gfx.gradutils");
- dojo.declare("dojox.charting.Theme", null, {
- // summary:
- // A Theme is a pre-defined object, primarily JSON-based, that makes up the definitions to
- // style a chart.
- //
- // description:
- // While you can set up style definitions on a chart directly (usually through the various add methods
- // on a dojox.charting.Chart2D object), a Theme simplifies this manual setup by allowing you to
- // pre-define all of the various visual parameters of each element in a chart.
- //
- // Most of the properties of a Theme are straight-forward; if something is line-based (such as
- // an axis or the ticks on an axis), they will be defined using basic stroke parameters. Likewise,
- // if an element is primarily block-based (such as the background of a chart), it will be primarily
- // fill-based.
- //
- // In addition (for convenience), a Theme definition does not have to contain the entire JSON-based
- // structure. Each theme is built on top of a default theme (which serves as the basis for the theme
- // "GreySkies"), and is mixed into the default theme object. This allows you to create a theme based,
- // say, solely on colors for data series.
- //
- // Defining a new theme is relatively easy; see any of the themes in dojox.charting.themes for examples
- // on how to define your own.
- //
- // When you set a theme on a chart, the theme itself is deep-cloned. This means that you cannot alter
- // the theme itself after setting the theme value on a chart, and expect it to change your chart. If you
- // are looking to make alterations to a theme for a chart, the suggestion would be to create your own
- // theme, based on the one you want to use, that makes those alterations before it is applied to a chart.
- //
- // Finally, a Theme contains a number of functions to facilitate rendering operations on a chart--the main
- // helper of which is the ~next~ method, in which a chart asks for the information for the next data series
- // to be rendered.
- //
- // A note on colors:
- // The Theme constructor was on the use of dojox.color.Palette (in general) for creating a visually distinct
- // set of colors for usage in a chart. A palette is usually comprised of 5 different color definitions, and
- // no more. If you have a need to render a chart with more than 5 data elements, you can simply "push"
- // new color definitions into the theme's .color array. Make sure that you do that with the actual
- // theme object from a Chart, and not in the theme itself (i.e. either do that before using .setTheme
- // on a chart).
- //
- // example:
- // The default theme (and structure) looks like so:
- // | // all objects are structs used directly in dojox.gfx
- // | chart:{
- // | stroke: null,
- // | fill: "white",
- // | pageStyle: null // suggested page style as an object suitable for dojo.style()
- // | },
- // | plotarea:{
- // | stroke: null,
- // | fill: "white"
- // | },
- // | axis:{
- // | stroke: { // the axis itself
- // | color: "#333",
- // | width: 1
- // | },
- // | tick: { // used as a foundation for all ticks
- // | color: "#666",
- // | position: "center",
- // | font: "normal normal normal 7pt Tahoma", // labels on axis
- // | fontColor: "#333" // color of labels
- // | },
- // | majorTick: { // major ticks on axis, and used for major gridlines
- // | width: 1,
- // | length: 6
- // | },
- // | minorTick: { // minor ticks on axis, and used for minor gridlines
- // | width: 0.8,
- // | length: 3
- // | },
- // | microTick: { // minor ticks on axis, and used for minor gridlines
- // | width: 0.5,
- // | length: 1
- // | }
- // | },
- // | series: {
- // | stroke: {width: 1.5, color: "#333"}, // line
- // | outline: {width: 0.1, color: "#ccc"}, // outline
- // | //shadow: {dx: 1, dy: 1, width: 2, color: [0, 0, 0, 0.3]},
- // | shadow: null, // no shadow
- // | fill: "#ccc", // fill, if appropriate
- // | font: "normal normal normal 8pt Tahoma", // if there's a label
- // | fontColor: "#000" // color of labels
- // | labelWiring: {width: 1, color: "#ccc"}, // connect marker and target data item(slice, column, bar...)
- // | },
- // | marker: { // any markers on a series
- // | symbol: "m-3,3 l3,-6 3,6 z", // symbol
- // | stroke: {width: 1.5, color: "#333"}, // stroke
- // | outline: {width: 0.1, color: "#ccc"}, // outline
- // | shadow: null, // no shadow
- // | fill: "#ccc", // fill if needed
- // | font: "normal normal normal 8pt Tahoma", // label
- // | fontColor: "#000"
- // | }
- //
- // example:
- // Defining a new theme is pretty simple:
- // | dojox.charting.themes.Grasslands = new dojox.charting.Theme({
- // | colors: [ "#70803a", "#dde574", "#788062", "#b1cc5d", "#eff2c2" ]
- // | });
- // |
- // | myChart.setTheme(dojox.charting.themes.Grasslands);
- shapeSpaces: {shape: 1, shapeX: 1, shapeY: 1},
- constructor: function(kwArgs){
- // summary:
- // Initialize a theme using the keyword arguments. Note that the arguments
- // look like the example (above), and may include a few more parameters.
- kwArgs = kwArgs || {};
- // populate theme with defaults updating them if needed
- var def = dojox.charting.Theme.defaultTheme;
- dojo.forEach(["chart", "plotarea", "axis", "series", "marker"], function(name){
- this[name] = dojo.delegate(def[name], kwArgs[name]);
- }, this);
- // personalize theme
- if(kwArgs.seriesThemes && kwArgs.seriesThemes.length){
- this.colors = null;
- this.seriesThemes = kwArgs.seriesThemes.slice(0);
- }else{
- this.seriesThemes = null;
- this.colors = (kwArgs.colors || dojox.charting.Theme.defaultColors).slice(0);
- }
- this.markerThemes = null;
- if(kwArgs.markerThemes && kwArgs.markerThemes.length){
- this.markerThemes = kwArgs.markerThemes.slice(0);
- }
- this.markers = kwArgs.markers ? dojo.clone(kwArgs.markers) : dojo.delegate(dojox.charting.Theme.defaultMarkers);
- // set flags
- this.noGradConv = kwArgs.noGradConv;
- this.noRadialConv = kwArgs.noRadialConv;
- if(kwArgs.reverseFills){
- this.reverseFills();
- }
- // private housekeeping
- this._current = 0;
- this._buildMarkerArray();
- },
- clone: function(){
- // summary:
- // Clone the current theme.
- // returns: dojox.charting.Theme
- // The cloned theme; any alterations made will not affect the original.
- var theme = new dojox.charting.Theme({
- // theme components
- chart: this.chart,
- plotarea: this.plotarea,
- axis: this.axis,
- series: this.series,
- marker: this.marker,
- // individual arrays
- colors: this.colors,
- markers: this.markers,
- seriesThemes: this.seriesThemes,
- markerThemes: this.markerThemes,
- // flags
- noGradConv: this.noGradConv,
- noRadialConv: this.noRadialConv
- });
- // copy custom methods
- dojo.forEach(
- ["clone", "clear", "next", "skip", "addMixin", "post", "getTick"],
- function(name){
- if(this.hasOwnProperty(name)){
- theme[name] = this[name];
- }
- },
- this
- );
- return theme; // dojox.charting.Theme
- },
- clear: function(){
- // summary:
- // Clear and reset the internal pointer to start fresh.
- this._current = 0;
- },
- next: function(elementType, mixin, doPost){
- // summary:
- // Get the next color or series theme.
- // elementType: String?
- // An optional element type (for use with series themes)
- // mixin: Object?
- // An optional object to mix into the theme.
- // doPost: Boolean?
- // A flag to post-process the results.
- // returns: Object
- // An object of the structure { series, marker, symbol }
- var merge = dojox.lang.utils.merge, series, marker;
- if(this.colors){
- series = dojo.delegate(this.series);
- marker = dojo.delegate(this.marker);
- var color = new dojo.Color(this.colors[this._current % this.colors.length]), old;
- // modify the stroke
- if(series.stroke && series.stroke.color){
- series.stroke = dojo.delegate(series.stroke);
- old = new dojo.Color(series.stroke.color);
- series.stroke.color = new dojo.Color(color);
- series.stroke.color.a = old.a;
- }else{
- series.stroke = {color: color};
- }
- if(marker.stroke && marker.stroke.color){
- marker.stroke = dojo.delegate(marker.stroke);
- old = new dojo.Color(marker.stroke.color);
- marker.stroke.color = new dojo.Color(color);
- marker.stroke.color.a = old.a;
- }else{
- marker.stroke = {color: color};
- }
- // modify the fill
- if(!series.fill || series.fill.type){
- series.fill = color;
- }else{
- old = new dojo.Color(series.fill);
- series.fill = new dojo.Color(color);
- series.fill.a = old.a;
- }
- if(!marker.fill || marker.fill.type){
- marker.fill = color;
- }else{
- old = new dojo.Color(marker.fill);
- marker.fill = new dojo.Color(color);
- marker.fill.a = old.a;
- }
- }else{
- series = this.seriesThemes ?
- merge(this.series, this.seriesThemes[this._current % this.seriesThemes.length]) :
- this.series;
- marker = this.markerThemes ?
- merge(this.marker, this.markerThemes[this._current % this.markerThemes.length]) :
- series;
- }
- var symbol = marker && marker.symbol || this._markers[this._current % this._markers.length];
- var theme = {series: series, marker: marker, symbol: symbol};
-
- // advance the counter
- ++this._current;
- if(mixin){
- theme = this.addMixin(theme, elementType, mixin);
- }
- if(doPost){
- theme = this.post(theme, elementType);
- }
- return theme; // Object
- },
- skip: function(){
- // summary:
- // Skip the next internal color.
- ++this._current;
- },
- addMixin: function(theme, elementType, mixin, doPost){
- // summary:
- // Add a mixin object to the passed theme and process.
- // theme: dojox.charting.Theme
- // The theme to mixin to.
- // elementType: String
- // The type of element in question. Can be "line", "bar" or "circle"
- // mixin: Object|Array
- // The object or objects to mix into the theme.
- // doPost: Boolean
- // If true, run the new theme through the post-processor.
- // returns: dojox.charting.Theme
- // The new theme.
- if(dojo.isArray(mixin)){
- dojo.forEach(mixin, function(m){
- theme = this.addMixin(theme, elementType, m);
- }, this);
- }else{
- var t = {};
- if("color" in mixin){
- if(elementType == "line" || elementType == "area"){
- dojo.setObject("series.stroke.color", mixin.color, t);
- dojo.setObject("marker.stroke.color", mixin.color, t);
- }else{
- dojo.setObject("series.fill", mixin.color, t);
- }
- }
- dojo.forEach(["stroke", "outline", "shadow", "fill", "font", "fontColor", "labelWiring"], function(name){
- var markerName = "marker" + name.charAt(0).toUpperCase() + name.substr(1),
- b = markerName in mixin;
- if(name in mixin){
- dojo.setObject("series." + name, mixin[name], t);
- if(!b){
- dojo.setObject("marker." + name, mixin[name], t);
- }
- }
- if(b){
- dojo.setObject("marker." + name, mixin[markerName], t);
- }
- });
- if("marker" in mixin){
- t.symbol = mixin.marker;
- }
- theme = dojox.lang.utils.merge(theme, t);
- }
- if(doPost){
- theme = this.post(theme, elementType);
- }
- return theme; // dojox.charting.Theme
- },
- post: function(theme, elementType){
- // summary:
- // Process any post-shape fills.
- // theme: dojox.charting.Theme
- // The theme to post process with.
- // elementType: String
- // The type of element being filled. Can be "bar" or "circle".
- // returns: dojox.charting.Theme
- // The post-processed theme.
- var fill = theme.series.fill, t;
- if(!this.noGradConv && this.shapeSpaces[fill.space] && fill.type == "linear"){
- if(elementType == "bar"){
- // transpose start and end points
- t = {
- x1: fill.y1,
- y1: fill.x1,
- x2: fill.y2,
- y2: fill.x2
- };
- }else if(!this.noRadialConv && fill.space == "shape" && (elementType == "slice" || elementType == "circle")){
- // switch to radial
- t = {
- type: "radial",
- cx: 0,
- cy: 0,
- r: 100
- };
- }
- if(t){
- return dojox.lang.utils.merge(theme, {series: {fill: t}});
- }
- }
- return theme; // dojox.charting.Theme
- },
- getTick: function(name, mixin){
- // summary:
- // Calculates and merges tick parameters.
- // name: String
- // Tick name, can be "major", "minor", or "micro".
- // mixin: Object?
- // Optional object to mix in to the tick.
- var tick = this.axis.tick, tickName = name + "Tick";
- merge = dojox.lang.utils.merge;
- if(tick){
- if(this.axis[tickName]){
- tick = merge(tick, this.axis[tickName]);
- }
- }else{
- tick = this.axis[tickName];
- }
- if(mixin){
- if(tick){
- if(mixin[tickName]){
- tick = merge(tick, mixin[tickName]);
- }
- }else{
- tick = mixin[tickName];
- }
- }
- return tick; // Object
- },
- inspectObjects: function(f){
- dojo.forEach(["chart", "plotarea", "axis", "series", "marker"], function(name){
- f(this[name]);
- }, this);
- if(this.seriesThemes){
- dojo.forEach(this.seriesThemes, f);
- }
- if(this.markerThemes){
- dojo.forEach(this.markerThemes, f);
- }
- },
- reverseFills: function(){
- this.inspectObjects(function(o){
- if(o && o.fill){
- o.fill = dojox.gfx.gradutils.reverse(o.fill);
- }
- });
- },
- addMarker:function(/*String*/ name, /*String*/ segment){
- // summary:
- // Add a custom marker to this theme.
- // example:
- // | myTheme.addMarker("Ellipse", foo);
- this.markers[name] = segment;
- this._buildMarkerArray();
- },
- setMarkers:function(/*Object*/ obj){
- // summary:
- // Set all the markers of this theme at once. obj should be a
- // dictionary of keys and path segments.
- //
- // example:
- // | myTheme.setMarkers({ "CIRCLE": foo });
- this.markers = obj;
- this._buildMarkerArray();
- },
- _buildMarkerArray: function(){
- this._markers = [];
- for(var p in this.markers){
- this._markers.push(this.markers[p]);
- }
- }
- });
- /*=====
- dojox.charting.Theme.__DefineColorArgs = function(num, colors, hue, saturation, low, high, base, generator){
- // summary:
- // The arguments object that can be passed to define colors for a theme.
- // num: Number?
- // The number of colors to generate. Defaults to 5.
- // colors: String[]|dojo.Color[]?
- // A pre-defined set of colors; this is passed through to the Theme directly.
- // hue: Number?
- // A hue to base the generated colors from (a number from 0 - 359).
- // saturation: Number?
- // If a hue is passed, this is used for the saturation value (0 - 100).
- // low: Number?
- // An optional value to determine the lowest value used to generate a color (HSV model)
- // high: Number?
- // An optional value to determine the highest value used to generate a color (HSV model)
- // base: String|dojo.Color?
- // A base color to use if we are defining colors using dojox.color.Palette
- // generator: String?
- // The generator function name from dojox.color.Palette.
- this.num = num;
- this.colors = colors;
- this.hue = hue;
- this.saturation = saturation;
- this.low = low;
- this.high = high;
- this.base = base;
- this.generator = generator;
- }
- =====*/
- dojo.mixin(dojox.charting.Theme, {
- defaultMarkers: {
- CIRCLE: "m-3,0 c0,-4 6,-4 6,0 m-6,0 c0,4 6,4 6,0",
- SQUARE: "m-3,-3 l0,6 6,0 0,-6 z",
- DIAMOND: "m0,-3 l3,3 -3,3 -3,-3 z",
- CROSS: "m0,-3 l0,6 m-3,-3 l6,0",
- X: "m-3,-3 l6,6 m0,-6 l-6,6",
- TRIANGLE: "m-3,3 l3,-6 3,6 z",
- TRIANGLE_INVERTED: "m-3,-3 l3,6 3,-6 z"
- },
- defaultColors:[
- // gray skies
- "#54544c", "#858e94", "#6e767a", "#948585", "#474747"
- ],
- defaultTheme: {
- // all objects are structs used directly in dojox.gfx
- chart:{
- stroke: null,
- fill: "white",
- pageStyle: null,
- titleGap: 20,
- titlePos: "top",
- titleFont: "normal normal bold 14pt Tahoma", // labels on axis
- titleFontColor: "#333"
- },
- plotarea:{
- stroke: null,
- fill: "white"
- },
- // TODO: label rotation on axis
- axis:{
- stroke: { // the axis itself
- color: "#333",
- width: 1
- },
- tick: { // used as a foundation for all ticks
- color: "#666",
- position: "center",
- font: "normal normal normal 7pt Tahoma", // labels on axis
- fontColor: "#333", // color of labels
- titleGap: 15,
- titleFont: "normal normal normal 11pt Tahoma", // labels on axis
- titleFontColor: "#333", // color of labels
- titleOrientation: "axis" // "axis": facing the axis, "away": facing away
- },
- majorTick: { // major ticks on axis, and used for major gridlines
- width: 1,
- length: 6
- },
- minorTick: { // minor ticks on axis, and used for minor gridlines
- width: 0.8,
- length: 3
- },
- microTick: { // minor ticks on axis, and used for minor gridlines
- width: 0.5,
- length: 1
- }
- },
- series: {
- // used as a "main" theme for series, sThemes augment it
- stroke: {width: 1.5, color: "#333"}, // line
- outline: {width: 0.1, color: "#ccc"}, // outline
- //shadow: {dx: 1, dy: 1, width: 2, color: [0, 0, 0, 0.3]},
- shadow: null, // no shadow
- fill: "#ccc", // fill, if appropriate
- font: "normal normal normal 8pt Tahoma", // if there's a label
- fontColor: "#000", // color of labels
- labelWiring: {width: 1, color: "#ccc"} // connect marker and target data item(slice, column, bar...)
- },
- marker: { // any markers on a series
- stroke: {width: 1.5, color: "#333"}, // stroke
- outline: {width: 0.1, color: "#ccc"}, // outline
- //shadow: {dx: 1, dy: 1, width: 2, color: [0, 0, 0, 0.3]},
- shadow: null, // no shadow
- fill: "#ccc", // fill if needed
- font: "normal normal normal 8pt Tahoma", // label
- fontColor: "#000"
- }
- },
- defineColors: function(kwArgs){
- // summary:
- // Generate a set of colors for the theme based on keyword
- // arguments.
- // kwArgs: dojox.charting.Theme.__DefineColorArgs
- // The arguments object used to define colors.
- // returns: dojo.Color[]
- // An array of colors for use in a theme.
- //
- // example:
- // | var colors = dojox.charting.Theme.defineColors({
- // | base: "#369",
- // | generator: "compound"
- // | });
- //
- // example:
- // | var colors = dojox.charting.Theme.defineColors({
- // | hue: 60,
- // | saturation: 90,
- // | low: 30,
- // | high: 80
- // | });
- kwArgs = kwArgs || {};
- var c = [], n = kwArgs.num || 5; // the number of colors to generate
- if(kwArgs.colors){
- // we have an array of colors predefined, so fix for the number of series.
- var l = kwArgs.colors.length;
- for(var i = 0; i < n; i++){
- c.push(kwArgs.colors[i % l]);
- }
- return c; // dojo.Color[]
- }
- if(kwArgs.hue){
- // single hue, generate a set based on brightness
- var s = kwArgs.saturation || 100; // saturation
- var st = kwArgs.low || 30;
- var end = kwArgs.high || 90;
- // we'd like it to be a little on the darker side.
- var l = (end + st) / 2;
- // alternately, use "shades"
- return dojox.color.Palette.generate(
- dojox.color.fromHsv(kwArgs.hue, s, l), "monochromatic"
- ).colors;
- }
- if(kwArgs.generator){
- // pass a base color and the name of a generator
- return dojox.color.Palette.generate(kwArgs.base, kwArgs.generator).colors;
- }
- return c; // dojo.Color[]
- },
-
- generateGradient: function(fillPattern, colorFrom, colorTo){
- var fill = dojo.delegate(fillPattern);
- fill.colors = [
- {offset: 0, color: colorFrom},
- {offset: 1, color: colorTo}
- ];
- return fill;
- },
-
- generateHslColor: function(color, luminance){
- color = new dojox.color.Color(color);
- var hsl = color.toHsl(),
- result = dojox.color.fromHsl(hsl.h, hsl.s, luminance);
- result.a = color.a; // add missing opacity
- return result;
- },
- generateHslGradient: function(color, fillPattern, lumFrom, lumTo){
- color = new dojox.color.Color(color);
- var hsl = color.toHsl(),
- colorFrom = dojox.color.fromHsl(hsl.h, hsl.s, lumFrom),
- colorTo = dojox.color.fromHsl(hsl.h, hsl.s, lumTo);
- colorFrom.a = colorTo.a = color.a; // add missing opacity
- return dojox.charting.Theme.generateGradient(fillPattern, colorFrom, colorTo); // Object
- }
- });
- }
|