You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
582 lines
19 KiB
582 lines
19 KiB
define(["dojo/_base/lang", "dojo/_base/array","dojo/_base/declare","dojo/_base/Color", "dojox/lang/utils", "dojox/gfx/gradutils"], |
|
function(lang, arr, declare, Color, dlu, dgg){ |
|
|
|
var SimpleTheme = declare("dojox.charting.SimpleTheme", null, { |
|
// summary: |
|
// A SimpleTheme or 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.Chart 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: |
|
// A theme 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:{ |
|
// | margins: {l: 10, t: 10, r: 10, b: 10}, |
|
// | 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 |
|
// | }, |
|
// | title: { |
|
// | gap: 15, |
|
// | font: "normal normal normal 11pt Tahoma", // title font |
|
// | fontColor: "#333", // title font color |
|
// | orientation: "axis" // "axis": facing the axis, "away": facing away |
|
// | } |
|
// | }, |
|
// | 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 |
|
// | //filter: dojox/gfx/filters.createFilter(), |
|
// | filter: null, // no filter, to use a filter you must use gfx SVG render and require dojox/gfx/svgext |
|
// | 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" |
|
// | }, |
|
// | grid: { // grid, when not present axis tick strokes are used instead |
|
// | majorLine: { // major grid line |
|
// | color: "#666", |
|
// | width: 1, |
|
// | length: 6 |
|
// | }, |
|
// | minorLine: { // minor grid line |
|
// | color: "#666", |
|
// | width: 0.8, |
|
// | length: 3 |
|
// | }, |
|
// | fill: "grey", // every other stripe |
|
// | alternateFill: "grey" // alternate stripe |
|
// | }, |
|
// | indicator: { |
|
// | lineStroke: {width: 1.5, color: "#333"}, // line |
|
// | lineOutline: {width: 0.1, color: "#ccc"}, // line outline |
|
// | lineShadow: null, // no line shadow |
|
// | lineFill: null, // fill between lines for dual indicators |
|
// | stroke: {width: 1.5, color: "#333"}, // label background stroke |
|
// | outline: {width: 0.1, color: "#ccc"}, // label background outline |
|
// | shadow: null, // no label background shadow |
|
// | fill: "#ccc", // label background fill |
|
// | radius: 3, // radius of the label background |
|
// | font: "normal normal normal 10pt Tahoma", // label font |
|
// | fontColor: "#000" // label color |
|
// | markerFill: "#ccc", // marker fill |
|
// | markerSymbol: "m-3,0 c0,-4 6,-4 6,0 m-6,0 c0,4 6,4 6,0", // marker symbol |
|
// | markerStroke: {width: 1.5, color: "#333"}, // marker stroke |
|
// | markerOutline: {width: 0.1, color: "#ccc"}, // marker outline |
|
// | markerShadow: null // no marker shadow |
|
// | }, |
|
// | pieInnerRadius: 33 |
|
// |
|
// example: |
|
// Defining a new theme is pretty simple: |
|
// | var Grasslands = new SimpleTheme({ |
|
// | colors: [ "#70803a", "#dde574", "#788062", "#b1cc5d", "#eff2c2" ], |
|
// | pieInnerRadius: 15 |
|
// | }); |
|
// | |
|
// | myChart.setTheme(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 = SimpleTheme.defaultTheme; |
|
arr.forEach(["chart", "plotarea", "axis", "grid", "series", "marker", "indicator"], function(name){ |
|
this[name] = lang.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 || SimpleTheme.defaultColors).slice(0); |
|
} |
|
this.markerThemes = null; |
|
if(kwArgs.markerThemes && kwArgs.markerThemes.length){ |
|
this.markerThemes = kwArgs.markerThemes.slice(0); |
|
} |
|
this.markers = kwArgs.markers ? lang.clone(kwArgs.markers) : lang.delegate(SimpleTheme.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.SimpleTheme |
|
// The cloned theme; any alterations made will not affect the original. |
|
var theme = new this.constructor({ |
|
// theme components |
|
chart: this.chart, |
|
plotarea: this.plotarea, |
|
axis: this.axis, |
|
grid: this.grid, |
|
series: this.series, |
|
marker: this.marker, |
|
// individual arrays |
|
colors: this.colors, |
|
markers: this.markers, |
|
indicator: this.indicator, |
|
seriesThemes: this.seriesThemes, |
|
markerThemes: this.markerThemes, |
|
// flags |
|
noGradConv: this.noGradConv, |
|
noRadialConv: this.noRadialConv, |
|
pieInnerRadius: this.pieInnerRadius |
|
}); |
|
// copy custom methods |
|
arr.forEach( |
|
["clone", "clear", "next", "skip", "addMixin", "post", "getTick"], |
|
function(name){ |
|
if(this.hasOwnProperty(name)){ |
|
theme[name] = this[name]; |
|
} |
|
}, |
|
this |
|
); |
|
return theme; // dojox.charting.SimpleTheme |
|
}, |
|
|
|
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 = dlu.merge, series, marker; |
|
if(this.colors){ |
|
series = lang.delegate(this.series); |
|
marker = lang.delegate(this.marker); |
|
var color = new Color(this.colors[this._current % this.colors.length]), old; |
|
// modify the stroke |
|
if(series.stroke && series.stroke.color){ |
|
series.stroke = lang.delegate(series.stroke); |
|
old = new Color(series.stroke.color); |
|
series.stroke.color = new Color(color); |
|
series.stroke.color.a = old.a; |
|
}else{ |
|
series.stroke = {color: color}; |
|
} |
|
if(marker.stroke && marker.stroke.color){ |
|
marker.stroke = lang.delegate(marker.stroke); |
|
old = new Color(marker.stroke.color); |
|
marker.stroke.color = new 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 Color(series.fill); |
|
series.fill = new Color(color); |
|
series.fill.a = old.a; |
|
} |
|
if(!marker.fill || marker.fill.type){ |
|
marker.fill = color; |
|
}else{ |
|
old = new Color(marker.fill); |
|
marker.fill = new 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/SimpleTheme |
|
// 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/SimpleTheme |
|
// The new theme. |
|
if(lang.isArray(mixin)){ |
|
arr.forEach(mixin, function(m){ |
|
theme = this.addMixin(theme, elementType, m); |
|
}, this); |
|
}else{ |
|
var t = {}; |
|
if("color" in mixin){ |
|
if(elementType == "line" || elementType == "area"){ |
|
lang.setObject("series.stroke.color", mixin.color, t); |
|
lang.setObject("marker.stroke.color", mixin.color, t); |
|
}else{ |
|
lang.setObject("series.fill", mixin.color, t); |
|
} |
|
} |
|
arr.forEach(["stroke", "outline", "shadow", "fill", "filter", "font", "fontColor", "labelWiring"], function(name){ |
|
var markerName = "marker" + name.charAt(0).toUpperCase() + name.substr(1), |
|
b = markerName in mixin; |
|
if(name in mixin){ |
|
lang.setObject("series." + name, mixin[name], t); |
|
if(!b){ |
|
lang.setObject("marker." + name, mixin[name], t); |
|
} |
|
} |
|
if(b){ |
|
lang.setObject("marker." + name, mixin[markerName], t); |
|
} |
|
}); |
|
if("marker" in mixin){ |
|
t.symbol = mixin.marker; |
|
t.symbol = mixin.marker; |
|
} |
|
theme = dlu.merge(theme, t); |
|
} |
|
if(doPost){ |
|
theme = this.post(theme, elementType); |
|
} |
|
return theme; // dojox/charting/SimpleTheme |
|
}, |
|
|
|
post: function(theme, elementType){ |
|
// summary: |
|
// Process any post-shape fills. |
|
// theme: dojox/charting/SimpleTheme |
|
// The theme to post process with. |
|
// elementType: String |
|
// The type of element being filled. Can be "bar" or "circle". |
|
// returns: dojox/charting/SimpleTheme |
|
// 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 dlu.merge(theme, {series: {fill: t}}); |
|
} |
|
} |
|
return theme; // dojox/charting/SimpleTheme |
|
}, |
|
|
|
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 = dlu.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){ |
|
arr.forEach(["chart", "plotarea", "axis", "grid", "series", "marker", "indicator"], function(name){ |
|
f(this[name]); |
|
}, this); |
|
if(this.seriesThemes){ |
|
arr.forEach(this.seriesThemes, f); |
|
} |
|
if(this.markerThemes){ |
|
arr.forEach(this.markerThemes, f); |
|
} |
|
}, |
|
|
|
reverseFills: function(){ |
|
this.inspectObjects(function(o){ |
|
if(o && o.fill){ |
|
o.fill = dgg.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]); |
|
} |
|
} |
|
}); |
|
|
|
lang.mixin(SimpleTheme, { |
|
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", // chart title |
|
titleFontColor: "#333", |
|
titleAlign: "middle" |
|
}, |
|
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 |
|
labelGap: 4 // gap between a tick and its label in pixels |
|
}, |
|
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 |
|
}, |
|
title: { |
|
gap: 15, |
|
font: "normal normal normal 11pt Tahoma", // title font |
|
fontColor: "#333", // title font color |
|
orientation: "axis" // "axis": facing the axis, "away": facing away |
|
} |
|
}, |
|
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" |
|
}, |
|
indicator: { |
|
lineStroke: {width: 1.5, color: "#333"}, |
|
lineOutline: {width: 0.1, color: "#ccc"}, |
|
lineShadow: null, |
|
lineFill: null, |
|
stroke: {width: 1.5, color: "#333"}, |
|
outline: {width: 0.1, color: "#ccc"}, |
|
shadow: null, |
|
fill : "#ccc", |
|
radius: 3, |
|
font: "normal normal normal 10pt Tahoma", |
|
fontColor: "#000", |
|
markerFill: "#ccc", |
|
markerSymbol: "m-3,0 c0,-4 6,-4 6,0 m-6,0 c0,4 6,4 6,0", |
|
markerStroke: {width: 1.5, color: "#333"}, |
|
markerOutline: {width: 0.1, color: "#ccc"}, |
|
markerShadow: null |
|
} |
|
} |
|
}); |
|
|
|
return SimpleTheme; |
|
});
|
|
|