diff options
Diffstat (limited to 'js/dojo/dojox/charting/Theme.js')
| -rw-r--r-- | js/dojo/dojox/charting/Theme.js | 658 |
1 files changed, 658 insertions, 0 deletions
diff --git a/js/dojo/dojox/charting/Theme.js b/js/dojo/dojox/charting/Theme.js new file mode 100644 index 0000000..ba09535 --- /dev/null +++ b/js/dojo/dojox/charting/Theme.js @@ -0,0 +1,658 @@ +//>>built +define("dojox/charting/Theme", ["dojo/_base/lang", "dojo/_base/array","dojo/_base/declare","dojo/_base/Color", + "dojox/color/_base", "dojox/color/Palette", "dojox/lang/utils", "dojox/gfx/gradutils"], + function(lang, arr, declare, Color, colorX, Palette, dlu, dgg){ + + var Theme = 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.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: + // 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" + // | }, + // | indicator: { + // | lineStroke: {width: 1.5, color: "#333"}, // line + // | lineOutline: {width: 0.1, color: "#ccc"}, // line outline + // | lineShadow: null, // no line shadow + // | 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 + // | } + // + // 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 = Theme.defaultTheme; + arr.forEach(["chart", "plotarea", "axis", "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 || Theme.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(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 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, + indicator: this.indicator, + seriesThemes: this.seriesThemes, + markerThemes: this.markerThemes, + // flags + noGradConv: this.noGradConv, + noRadialConv: this.noRadialConv + }); + // 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.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 = 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.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(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", "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; + } + theme = dlu.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 dlu.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 = 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", "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]); + } + } +}); + +/*===== +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; +} +=====*/ +lang.mixin(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" + }, + indicator: { + lineStroke: {width: 1.5, color: "#333"}, + lineOutline: {width: 0.1, color: "#ccc"}, + lineShadow: 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 + } + }, + + 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 l, 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. + 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 + st = kwArgs.low || 30, + end = kwArgs.high || 90; + // we'd like it to be a little on the darker side. + l = (end + st) / 2; + // alternately, use "shades" + return colorX.Palette.generate( + colorX.fromHsv(kwArgs.hue, s, l), "monochromatic" + ).colors; + } + if(kwArgs.generator){ + // pass a base color and the name of a generator + return colorX.Palette.generate(kwArgs.base, kwArgs.generator).colors; + } + return c; // dojo.Color[] + }, + + generateGradient: function(fillPattern, colorFrom, colorTo){ + var fill = lang.delegate(fillPattern); + fill.colors = [ + {offset: 0, color: colorFrom}, + {offset: 1, color: colorTo} + ]; + return fill; + }, + + generateHslColor: function(color, luminance){ + color = new Color(color); + var hsl = color.toHsl(), + result = colorX.fromHsl(hsl.h, hsl.s, luminance); + result.a = color.a; // add missing opacity + return result; + }, + + generateHslGradient: function(color, fillPattern, lumFrom, lumTo){ + color = new Color(color); + var hsl = color.toHsl(), + colorFrom = colorX.fromHsl(hsl.h, hsl.s, lumFrom), + colorTo = colorX.fromHsl(hsl.h, hsl.s, lumTo); + colorFrom.a = colorTo.a = color.a; // add missing opacity + return Theme.generateGradient(fillPattern, colorFrom, colorTo); // Object + } +}); + +return Theme; +}); |
