diff options
Diffstat (limited to 'js/dojo/dojox/charting/widget/Chart2D.js.uncompressed.js')
| -rw-r--r-- | js/dojo/dojox/charting/widget/Chart2D.js.uncompressed.js | 19339 |
1 files changed, 19339 insertions, 0 deletions
diff --git a/js/dojo/dojox/charting/widget/Chart2D.js.uncompressed.js b/js/dojo/dojox/charting/widget/Chart2D.js.uncompressed.js new file mode 100644 index 0000000..7510f20 --- /dev/null +++ b/js/dojo/dojox/charting/widget/Chart2D.js.uncompressed.js @@ -0,0 +1,19339 @@ +/* + Copyright (c) 2004-2011, 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 +*/ + +/* + This is an optimized version of Dojo, built for deployment and not for + development. To get sources and documentation, please visit: + + http://dojotoolkit.org +*/ + +//>>built +require({cache:{ +'dojox/charting/plot2d/_PlotEvents':function(){ +define("dojox/charting/plot2d/_PlotEvents", ["dojo/_base/lang", "dojo/_base/array", "dojo/_base/declare", "dojo/_base/connect"], + function(lang, arr, declare, hub){ + + return declare("dojox.charting.plot2d._PlotEvents", null, { + constructor: function(){ + this._shapeEvents = []; + this._eventSeries = {}; + }, + destroy: function(){ + // summary: + // Destroy any internal elements and event handlers. + this.resetEvents(); + this.inherited(arguments); + }, + plotEvent: function(o){ + // summary: + // Stub function for use by specific plots. + // o: Object + // An object intended to represent event parameters. + }, + raiseEvent: function(o){ + // summary: + // Raises events in predefined order + // o: Object + // An object intended to represent event parameters. + this.plotEvent(o); + var t = lang.delegate(o); + t.originalEvent = o.type; + t.originalPlot = o.plot; + t.type = "onindirect"; + arr.forEach(this.chart.stack, function(plot){ + if(plot !== this && plot.plotEvent){ + t.plot = plot; + plot.plotEvent(t); + } + }, this); + }, + connect: function(object, method){ + // summary: + // Helper function to connect any object's method to our plotEvent. + // object: Object + // The object to connect to. + // method: String|Function + // The method to fire when our plotEvent is fired. + // returns: Array + // The handle as returned from dojo.connect (see dojo.connect). + this.dirty = true; + return hub.connect(this, "plotEvent", object, method); // Array + }, + events: function(){ + // summary: + // Find out if any event handlers have been connected to our plotEvent. + // returns: Boolean + // A flag indicating that there are handlers attached. + return !!this.plotEvent.after; + }, + resetEvents: function(){ + // summary: + // Reset all events attached to our plotEvent (i.e. disconnect). + if(this._shapeEvents.length){ + arr.forEach(this._shapeEvents, function(item){ + item.shape.disconnect(item.handle); + }); + this._shapeEvents = []; + } + this.raiseEvent({type: "onplotreset", plot: this}); + }, + _connectSingleEvent: function(o, eventName){ + this._shapeEvents.push({ + shape: o.eventMask, + handle: o.eventMask.connect(eventName, this, function(e){ + o.type = eventName; + o.event = e; + this.raiseEvent(o); + o.event = null; + }) + }); + }, + _connectEvents: function(o){ + if(o){ + o.chart = this.chart; + o.plot = this; + o.hAxis = this.hAxis || null; + o.vAxis = this.vAxis || null; + o.eventMask = o.eventMask || o.shape; + this._connectSingleEvent(o, "onmouseover"); + this._connectSingleEvent(o, "onmouseout"); + this._connectSingleEvent(o, "onclick"); + } + }, + _reconnectEvents: function(seriesName){ + var a = this._eventSeries[seriesName]; + if(a){ + arr.forEach(a, this._connectEvents, this); + } + }, + fireEvent: function(seriesName, eventName, index, eventObject){ + // summary: + // Emulates firing an event for a given data value (specified by + // an index) of a given series. + // seriesName: String: + // Series name. + // eventName: String: + // Event name to emulate. + // index: Number: + // Valid data value index used to raise an event. + // eventObject: Object?: + // Optional event object. Especially useful for synthetic events. + // Default: null. + var s = this._eventSeries[seriesName]; + if(s && s.length && index < s.length){ + var o = s[index]; + o.type = eventName; + o.event = eventObject || null; + this.raiseEvent(o); + o.event = null; + } + } + }); +}); + +}, +'dojo/uacss':function(){ +define(["./dom-geometry", "./_base/lang", "./ready", "./_base/sniff", "./_base/window"], + function(geometry, lang, ready, has, baseWindow){ + // module: + // dojo/uacss + // summary: + // Applies pre-set CSS classes to the top-level HTML node, based on: + // - browser (ex: dj_ie) + // - browser version (ex: dj_ie6) + // - box model (ex: dj_contentBox) + // - text direction (ex: dijitRtl) + // + // In addition, browser, browser version, and box model are + // combined with an RTL flag when browser text is RTL. ex: dj_ie-rtl. + + var + html = baseWindow.doc.documentElement, + ie = has("ie"), + opera = has("opera"), + maj = Math.floor, + ff = has("ff"), + boxModel = geometry.boxModel.replace(/-/,''), + + classes = { + "dj_ie": ie, + "dj_ie6": maj(ie) == 6, + "dj_ie7": maj(ie) == 7, + "dj_ie8": maj(ie) == 8, + "dj_ie9": maj(ie) == 9, + "dj_quirks": has("quirks"), + "dj_iequirks": ie && has("quirks"), + + // NOTE: Opera not supported by dijit + "dj_opera": opera, + + "dj_khtml": has("khtml"), + + "dj_webkit": has("webkit"), + "dj_safari": has("safari"), + "dj_chrome": has("chrome"), + + "dj_gecko": has("mozilla"), + "dj_ff3": maj(ff) == 3 + }; // no dojo unsupported browsers + + classes["dj_" + boxModel] = true; + + // apply browser, browser version, and box model class names + var classStr = ""; + for(var clz in classes){ + if(classes[clz]){ + classStr += clz + " "; + } + } + html.className = lang.trim(html.className + " " + classStr); + + // If RTL mode, then add dj_rtl flag plus repeat existing classes with -rtl extension. + // We can't run the code below until the <body> tag has loaded (so we can check for dir=rtl). + // priority is 90 to run ahead of parser priority of 100 + ready(90, function(){ + if(!geometry.isBodyLtr()){ + var rtlClassStr = "dj_rtl dijitRtl " + classStr.replace(/ /g, "-rtl "); + html.className = lang.trim(html.className + " " + rtlClassStr + "dj_rtl dijitRtl " + classStr.replace(/ /g, "-rtl ")); + } + }); + return has; +}); + +}, +'dojox/charting/axis2d/Invisible':function(){ +define(["dojo/_base/lang", "dojo/_base/declare", "./Base", "../scaler/linear", + "dojox/gfx", "dojox/lang/utils", "dojox/lang/functional", "dojo/string"], + function(lang, declare, Base, lin, g, du, df, dstring){ +/*===== +var Base = dojox.charting.axis2d.Base; +=====*/ + var merge = du.merge, + labelGap = 4, // in pixels + centerAnchorLimit = 45; // in degrees + + return declare("dojox.charting.axis2d.Invisible", Base, { + // summary: + // The default axis object used in dojox.charting. See dojox.charting.Chart.addAxis for details. + // + // defaultParams: Object + // The default parameters used to define any axis. + // optionalParams: Object + // Any optional parameters needed to define an axis. + + /* + // TODO: the documentation tools need these to be pre-defined in order to pick them up + // correctly, but the code here is partially predicated on whether or not the properties + // actually exist. For now, we will leave these undocumented but in the code for later. -- TRT + + // opt: Object + // The actual options used to define this axis, created at initialization. + // scalar: Object + // The calculated helper object to tell charts how to draw an axis and any data. + // ticks: Object + // The calculated tick object that helps a chart draw the scaling on an axis. + // dirty: Boolean + // The state of the axis (whether it needs to be redrawn or not) + // scale: Number + // The current scale of the axis. + // offset: Number + // The current offset of the axis. + + opt: null, + scalar: null, + ticks: null, + dirty: true, + scale: 1, + offset: 0, + */ + defaultParams: { + vertical: false, // true for vertical axis + fixUpper: "none", // align the upper on ticks: "major", "minor", "micro", "none" + fixLower: "none", // align the lower on ticks: "major", "minor", "micro", "none" + natural: false, // all tick marks should be made on natural numbers + leftBottom: true, // position of the axis, used with "vertical" + includeZero: false, // 0 should be included + fixed: true, // all labels are fixed numbers + majorLabels: true, // draw major labels + minorTicks: true, // draw minor ticks + minorLabels: true, // draw minor labels + microTicks: false, // draw micro ticks + rotation: 0 // label rotation angle in degrees + }, + optionalParams: { + min: 0, // minimal value on this axis + max: 1, // maximal value on this axis + from: 0, // visible from this value + to: 1, // visible to this value + majorTickStep: 4, // major tick step + minorTickStep: 2, // minor tick step + microTickStep: 1, // micro tick step + labels: [], // array of labels for major ticks + // with corresponding numeric values + // ordered by values + labelFunc: null, // function to compute label values + maxLabelSize: 0, // size in px. For use with labelFunc + maxLabelCharCount: 0, // size in word count. + trailingSymbol: null + + // TODO: add support for minRange! + // minRange: 1, // smallest distance from min allowed on the axis + }, + + constructor: function(chart, kwArgs){ + // summary: + // The constructor for an axis. + // chart: dojox.charting.Chart + // The chart the axis belongs to. + // kwArgs: dojox.charting.axis2d.__AxisCtorArgs? + // Any optional keyword arguments to be used to define this axis. + this.opt = lang.clone(this.defaultParams); + du.updateWithObject(this.opt, kwArgs); + du.updateWithPattern(this.opt, kwArgs, this.optionalParams); + }, + dependOnData: function(){ + // summary: + // Find out whether or not the axis options depend on the data in the axis. + return !("min" in this.opt) || !("max" in this.opt); // Boolean + }, + clear: function(){ + // summary: + // Clear out all calculated properties on this axis; + // returns: dojox.charting.axis2d.Default + // The reference to the axis for functional chaining. + delete this.scaler; + delete this.ticks; + this.dirty = true; + return this; // dojox.charting.axis2d.Default + }, + initialized: function(){ + // summary: + // Finds out if this axis has been initialized or not. + // returns: Boolean + // Whether a scaler has been calculated and if the axis is not dirty. + return "scaler" in this && !(this.dirty && this.dependOnData()); + }, + setWindow: function(scale, offset){ + // summary: + // Set the drawing "window" for the axis. + // scale: Number + // The new scale for the axis. + // offset: Number + // The new offset for the axis. + // returns: dojox.charting.axis2d.Default + // The reference to the axis for functional chaining. + this.scale = scale; + this.offset = offset; + return this.clear(); // dojox.charting.axis2d.Default + }, + getWindowScale: function(){ + // summary: + // Get the current windowing scale of the axis. + return "scale" in this ? this.scale : 1; // Number + }, + getWindowOffset: function(){ + // summary: + // Get the current windowing offset for the axis. + return "offset" in this ? this.offset : 0; // Number + }, + _groupLabelWidth: function(labels, font, wcLimit){ + if(!labels.length){ + return 0; + } + if(lang.isObject(labels[0])){ + labels = df.map(labels, function(label){ return label.text; }); + } + if (wcLimit) { + labels = df.map(labels, function(label){ + return lang.trim(label).length == 0 ? "" : label.substring(0, wcLimit) + this.trailingSymbol; + }, this); + } + var s = labels.join("<br>"); + return g._base._getTextBox(s, {font: font}).w || 0; + }, + calculate: function(min, max, span, labels){ + // summary: + // Perform all calculations needed to render this axis. + // min: Number + // The smallest value represented on this axis. + // max: Number + // The largest value represented on this axis. + // span: Number + // The span in pixels over which axis calculations are made. + // labels: String[] + // Optional list of labels. + // returns: dojox.charting.axis2d.Default + // The reference to the axis for functional chaining. + if(this.initialized()){ + return this; + } + var o = this.opt; + this.labels = "labels" in o ? o.labels : labels; + this.scaler = lin.buildScaler(min, max, span, o); + var tsb = this.scaler.bounds; + if("scale" in this){ + // calculate new range + o.from = tsb.lower + this.offset; + o.to = (tsb.upper - tsb.lower) / this.scale + o.from; + // make sure that bounds are correct + if( !isFinite(o.from) || + isNaN(o.from) || + !isFinite(o.to) || + isNaN(o.to) || + o.to - o.from >= tsb.upper - tsb.lower + ){ + // any error --- remove from/to bounds + delete o.from; + delete o.to; + delete this.scale; + delete this.offset; + }else{ + // shift the window, if we are out of bounds + if(o.from < tsb.lower){ + o.to += tsb.lower - o.from; + o.from = tsb.lower; + }else if(o.to > tsb.upper){ + o.from += tsb.upper - o.to; + o.to = tsb.upper; + } + // update the offset + this.offset = o.from - tsb.lower; + } + // re-calculate the scaler + this.scaler = lin.buildScaler(min, max, span, o); + tsb = this.scaler.bounds; + // cleanup + if(this.scale == 1 && this.offset == 0){ + delete this.scale; + delete this.offset; + } + } + + var ta = this.chart.theme.axis, labelWidth = 0, rotation = o.rotation % 360, + // TODO: we use one font --- of major tick, we need to use major and minor fonts + taFont = o.font || (ta.majorTick && ta.majorTick.font) || (ta.tick && ta.tick.font), + size = taFont ? g.normalizedLength(g.splitFontString(taFont).size) : 0, + cosr = Math.abs(Math.cos(rotation * Math.PI / 180)), + sinr = Math.abs(Math.sin(rotation * Math.PI / 180)); + + if(rotation < 0){ + rotation += 360; + } + + if(size){ + if(this.vertical ? rotation != 0 && rotation != 180 : rotation != 90 && rotation != 270){ + // we need width of all labels + if(this.labels){ + labelWidth = this._groupLabelWidth(this.labels, taFont, o.maxLabelCharCount); + }else{ + var labelLength = Math.ceil( + Math.log( + Math.max( + Math.abs(tsb.from), + Math.abs(tsb.to) + ) + ) / Math.LN10 + ), + t = []; + if(tsb.from < 0 || tsb.to < 0){ + t.push("-"); + } + t.push(dstring.rep("9", labelLength)); + var precision = Math.floor( + Math.log( tsb.to - tsb.from ) / Math.LN10 + ); + if(precision > 0){ + t.push("."); + t.push(dstring.rep("9", precision)); + } + labelWidth = g._base._getTextBox( + t.join(""), + { font: taFont } + ).w; + } + labelWidth = o.maxLabelSize ? Math.min(o.maxLabelSize, labelWidth) : labelWidth; + }else{ + labelWidth = size; + } + switch(rotation){ + case 0: + case 90: + case 180: + case 270: + // trivial cases: use labelWidth + break; + default: + // rotated labels + var gap1 = Math.sqrt(labelWidth * labelWidth + size * size), // short labels + gap2 = this.vertical ? size * cosr + labelWidth * sinr : labelWidth * cosr + size * sinr; // slanted labels + labelWidth = Math.min(gap1, gap2); + break; + } + } + + this.scaler.minMinorStep = labelWidth + labelGap; + this.ticks = lin.buildTicks(this.scaler, o); + return this; // dojox.charting.axis2d.Default + }, + getScaler: function(){ + // summary: + // Get the pre-calculated scaler object. + return this.scaler; // Object + }, + getTicks: function(){ + // summary: + // Get the pre-calculated ticks object. + return this.ticks; // Object + } + }); +}); + +}, +'dojox/lang/utils':function(){ +define("dojox/lang/utils", ["..", "dojo/_base/lang"], + function(dojox, lang){ + var du = lang.getObject("lang.utils", true, dojox); + + var empty = {}, opts = Object.prototype.toString; + + var clone = function(o){ + if(o){ + switch(opts.call(o)){ + case "[object Array]": + return o.slice(0); + case "[object Object]": + return lang.delegate(o); + } + } + return o; + } + + lang.mixin(du, { + coerceType: function(target, source){ + // summary: Coerces one object to the type of another. + // target: Object: object, which typeof result is used to coerce "source" object. + // source: Object: object, which will be forced to change type. + switch(typeof target){ + case "number": return Number(eval("(" + source + ")")); + case "string": return String(source); + case "boolean": return Boolean(eval("(" + source + ")")); + } + return eval("(" + source + ")"); + }, + + updateWithObject: function(target, source, conv){ + // summary: Updates an existing object in place with properties from an "source" object. + // target: Object: the "target" object to be updated + // source: Object: the "source" object, whose properties will be used to source the existed object. + // conv: Boolean?: force conversion to the original type + if(!source){ return target; } + for(var x in target){ + if(x in source && !(x in empty)){ + var t = target[x]; + if(t && typeof t == "object"){ + du.updateWithObject(t, source[x], conv); + }else{ + target[x] = conv ? du.coerceType(t, source[x]) : clone(source[x]); + } + } + } + return target; // Object + }, + + updateWithPattern: function(target, source, pattern, conv){ + // summary: Updates an existing object in place with properties from an "source" object. + // target: Object: the "target" object to be updated + // source: Object: the "source" object, whose properties will be used to source the existed object. + // pattern: Object: object, whose properties will be used to pull values from the "source" + // conv: Boolean?: force conversion to the original type + if(!source || !pattern){ return target; } + for(var x in pattern){ + if(x in source && !(x in empty)){ + target[x] = conv ? du.coerceType(pattern[x], source[x]) : clone(source[x]); + } + } + return target; // Object + }, + + merge: function(object, mixin){ + // summary: Merge two objects structurally, mixin properties will override object's properties. + // object: Object: original object. + // mixin: Object: additional object, which properties will override object's properties. + if(mixin){ + var otype = opts.call(object), mtype = opts.call(mixin), t, i, l, m; + switch(mtype){ + case "[object Array]": + if(mtype == otype){ + t = new Array(Math.max(object.length, mixin.length)); + for(i = 0, l = t.length; i < l; ++i){ + t[i] = du.merge(object[i], mixin[i]); + } + return t; + } + return mixin.slice(0); + case "[object Object]": + if(mtype == otype && object){ + t = lang.delegate(object); + for(i in mixin){ + if(i in object){ + l = object[i]; + m = mixin[i]; + if(m !== l){ + t[i] = du.merge(l, m); + } + }else{ + t[i] = lang.clone(mixin[i]); + } + } + return t; + } + return lang.clone(mixin); + } + } + return mixin; + } + }); + + return du; +}); + +}, +'dojox/charting/plot2d/Pie':function(){ +define("dojox/charting/plot2d/Pie", ["dojo/_base/lang", "dojo/_base/array" ,"dojo/_base/declare", + "../Element", "./_PlotEvents", "./common", "../axis2d/common", + "dojox/gfx", "dojox/gfx/matrix", "dojox/lang/functional", "dojox/lang/utils"], + function(lang, arr, declare, Element, PlotEvents, dc, da, g, m, df, du){ + + /*===== + var Element = dojox.charting.Element; + var PlotEvents = dojox.charting.plot2d._PlotEvents; + dojo.declare("dojox.charting.plot2d.__PieCtorArgs", dojox.charting.plot2d.__DefaultCtorArgs, { + // summary: + // Specialized keyword arguments object for use in defining parameters on a Pie chart. + + // labels: Boolean? + // Whether or not to draw labels for each pie slice. Default is true. + labels: true, + + // ticks: Boolean? + // Whether or not to draw ticks to labels within each slice. Default is false. + ticks: false, + + // fixed: Boolean? + // TODO + fixed: true, + + // precision: Number? + // The precision at which to sum/add data values. Default is 1. + precision: 1, + + // labelOffset: Number? + // The amount in pixels by which to offset labels. Default is 20. + labelOffset: 20, + + // labelStyle: String? + // Options as to where to draw labels. Values include "default", and "columns". Default is "default". + labelStyle: "default", // default/columns + + // htmlLabels: Boolean? + // Whether or not to use HTML to render slice labels. Default is true. + htmlLabels: true, + + // radGrad: String? + // The type of radial gradient to use in rendering. Default is "native". + radGrad: "native", + + // fanSize: Number? + // The amount for a radial gradient. Default is 5. + fanSize: 5, + + // startAngle: Number? + // Where to being rendering gradients in slices, in degrees. Default is 0. + startAngle: 0, + + // radius: Number? + // The size of the radial gradient. Default is 0. + radius: 0 + }); + =====*/ + + var FUDGE_FACTOR = 0.2; // use to overlap fans + + return declare("dojox.charting.plot2d.Pie", [Element, PlotEvents], { + // summary: + // The plot that represents a typical pie chart. + defaultParams: { + labels: true, + ticks: false, + fixed: true, + precision: 1, + labelOffset: 20, + labelStyle: "default", // default/columns + htmlLabels: true, // use HTML to draw labels + radGrad: "native", // or "linear", or "fan" + fanSize: 5, // maximum fan size in degrees + startAngle: 0 // start angle for slices in degrees + }, + optionalParams: { + radius: 0, + // theme components + stroke: {}, + outline: {}, + shadow: {}, + fill: {}, + font: "", + fontColor: "", + labelWiring: {} + }, + + constructor: function(chart, kwArgs){ + // summary: + // Create a pie plot. + this.opt = lang.clone(this.defaultParams); + du.updateWithObject(this.opt, kwArgs); + du.updateWithPattern(this.opt, kwArgs, this.optionalParams); + this.run = null; + this.dyn = []; + }, + clear: function(){ + // summary: + // Clear out all of the information tied to this plot. + // returns: dojox.charting.plot2d.Pie + // A reference to this plot for functional chaining. + this.dirty = true; + this.dyn = []; + this.run = null; + return this; // dojox.charting.plot2d.Pie + }, + setAxis: function(axis){ + // summary: + // Dummy method, since axes are irrelevant with a Pie chart. + // returns: dojox.charting.plot2d.Pie + // The reference to this plot for functional chaining. + return this; // dojox.charting.plot2d.Pie + }, + addSeries: function(run){ + // summary: + // Add a series of data to this plot. + // returns: dojox.charting.plot2d.Pie + // The reference to this plot for functional chaining. + this.run = run; + return this; // dojox.charting.plot2d.Pie + }, + getSeriesStats: function(){ + // summary: + // Returns default stats (irrelevant for this type of plot). + // returns: Object + // {hmin, hmax, vmin, vmax} min/max in both directions. + return lang.delegate(dc.defaultStats); + }, + initializeScalers: function(){ + // summary: + // Does nothing (irrelevant for this type of plot). + return this; + }, + getRequiredColors: function(){ + // summary: + // Return the number of colors needed to draw this plot. + return this.run ? this.run.data.length : 0; + }, + + render: function(dim, offsets){ + // summary: + // Render the plot on the chart. + // dim: Object + // An object of the form { width, height }. + // offsets: Object + // An object of the form { l, r, t, b }. + // returns: dojox.charting.plot2d.Pie + // A reference to this plot for functional chaining. + if(!this.dirty){ return this; } + this.resetEvents(); + this.dirty = false; + this._eventSeries = {}; + this.cleanGroup(); + var s = this.group, t = this.chart.theme; + + if(!this.run || !this.run.data.length){ + return this; + } + + // calculate the geometry + var rx = (dim.width - offsets.l - offsets.r) / 2, + ry = (dim.height - offsets.t - offsets.b) / 2, + r = Math.min(rx, ry), + taFont = "font" in this.opt ? this.opt.font : t.axis.font, + size = taFont ? g.normalizedLength(g.splitFontString(taFont).size) : 0, + taFontColor = "fontColor" in this.opt ? this.opt.fontColor : t.axis.fontColor, + startAngle = m._degToRad(this.opt.startAngle), + start = startAngle, step, filteredRun, slices, labels, shift, labelR, + run = this.run.data, + events = this.events(); + if(typeof run[0] == "number"){ + filteredRun = df.map(run, "x ? Math.max(x, 0) : 0"); + if(df.every(filteredRun, "<= 0")){ + return this; + } + slices = df.map(filteredRun, "/this", df.foldl(filteredRun, "+", 0)); + if(this.opt.labels){ + labels = arr.map(slices, function(x){ + return x > 0 ? this._getLabel(x * 100) + "%" : ""; + }, this); + } + }else{ + filteredRun = df.map(run, "x ? Math.max(x.y, 0) : 0"); + if(df.every(filteredRun, "<= 0")){ + return this; + } + slices = df.map(filteredRun, "/this", df.foldl(filteredRun, "+", 0)); + if(this.opt.labels){ + labels = arr.map(slices, function(x, i){ + if(x <= 0){ return ""; } + var v = run[i]; + return "text" in v ? v.text : this._getLabel(x * 100) + "%"; + }, this); + } + } + var themes = df.map(run, function(v, i){ + if(v === null || typeof v == "number"){ + return t.next("slice", [this.opt, this.run], true); + } + return t.next("slice", [this.opt, this.run, v], true); + }, this); + if(this.opt.labels){ + shift = df.foldl1(df.map(labels, function(label, i){ + var font = themes[i].series.font; + return g._base._getTextBox(label, {font: font}).w; + }, this), "Math.max(a, b)") / 2; + if(this.opt.labelOffset < 0){ + r = Math.min(rx - 2 * shift, ry - size) + this.opt.labelOffset; + } + labelR = r - this.opt.labelOffset; + } + if("radius" in this.opt){ + r = this.opt.radius; + labelR = r - this.opt.labelOffset; + } + var circle = { + cx: offsets.l + rx, + cy: offsets.t + ry, + r: r + }; + + this.dyn = []; + // draw slices + var eventSeries = new Array(slices.length); + arr.some(slices, function(slice, i){ + if(slice < 0){ + // degenerated slice + return false; // continue + } + if(slice == 0){ + this.dyn.push({fill: null, stroke: null}); + return false; + } + var v = run[i], theme = themes[i], specialFill; + if(slice >= 1){ + // whole pie + specialFill = this._plotFill(theme.series.fill, dim, offsets); + specialFill = this._shapeFill(specialFill, + { + x: circle.cx - circle.r, y: circle.cy - circle.r, + width: 2 * circle.r, height: 2 * circle.r + }); + specialFill = this._pseudoRadialFill(specialFill, {x: circle.cx, y: circle.cy}, circle.r); + var shape = s.createCircle(circle).setFill(specialFill).setStroke(theme.series.stroke); + this.dyn.push({fill: specialFill, stroke: theme.series.stroke}); + + if(events){ + var o = { + element: "slice", + index: i, + run: this.run, + shape: shape, + x: i, + y: typeof v == "number" ? v : v.y, + cx: circle.cx, + cy: circle.cy, + cr: r + }; + this._connectEvents(o); + eventSeries[i] = o; + } + + return true; // stop iteration + } + // calculate the geometry of the slice + var end = start + slice * 2 * Math.PI; + if(i + 1 == slices.length){ + end = startAngle + 2 * Math.PI; + } + var step = end - start, + x1 = circle.cx + r * Math.cos(start), + y1 = circle.cy + r * Math.sin(start), + x2 = circle.cx + r * Math.cos(end), + y2 = circle.cy + r * Math.sin(end); + // draw the slice + var fanSize = m._degToRad(this.opt.fanSize); + if(theme.series.fill && theme.series.fill.type === "radial" && this.opt.radGrad === "fan" && step > fanSize){ + var group = s.createGroup(), nfans = Math.ceil(step / fanSize), delta = step / nfans; + specialFill = this._shapeFill(theme.series.fill, + {x: circle.cx - circle.r, y: circle.cy - circle.r, width: 2 * circle.r, height: 2 * circle.r}); + for(var j = 0; j < nfans; ++j){ + var fansx = j == 0 ? x1 : circle.cx + r * Math.cos(start + (j - FUDGE_FACTOR) * delta), + fansy = j == 0 ? y1 : circle.cy + r * Math.sin(start + (j - FUDGE_FACTOR) * delta), + fanex = j == nfans - 1 ? x2 : circle.cx + r * Math.cos(start + (j + 1 + FUDGE_FACTOR) * delta), + faney = j == nfans - 1 ? y2 : circle.cy + r * Math.sin(start + (j + 1 + FUDGE_FACTOR) * delta), + fan = group.createPath(). + moveTo(circle.cx, circle.cy). + lineTo(fansx, fansy). + arcTo(r, r, 0, delta > Math.PI, true, fanex, faney). + lineTo(circle.cx, circle.cy). + closePath(). + setFill(this._pseudoRadialFill(specialFill, {x: circle.cx, y: circle.cy}, r, start + (j + 0.5) * delta, start + (j + 0.5) * delta)); + } + group.createPath(). + moveTo(circle.cx, circle.cy). + lineTo(x1, y1). + arcTo(r, r, 0, step > Math.PI, true, x2, y2). + lineTo(circle.cx, circle.cy). + closePath(). + setStroke(theme.series.stroke); + shape = group; + }else{ + shape = s.createPath(). + moveTo(circle.cx, circle.cy). + lineTo(x1, y1). + arcTo(r, r, 0, step > Math.PI, true, x2, y2). + lineTo(circle.cx, circle.cy). + closePath(). + setStroke(theme.series.stroke); + var specialFill = theme.series.fill; + if(specialFill && specialFill.type === "radial"){ + specialFill = this._shapeFill(specialFill, {x: circle.cx - circle.r, y: circle.cy - circle.r, width: 2 * circle.r, height: 2 * circle.r}); + if(this.opt.radGrad === "linear"){ + specialFill = this._pseudoRadialFill(specialFill, {x: circle.cx, y: circle.cy}, r, start, end); + } + }else if(specialFill && specialFill.type === "linear"){ + specialFill = this._plotFill(specialFill, dim, offsets); + specialFill = this._shapeFill(specialFill, shape.getBoundingBox()); + } + shape.setFill(specialFill); + } + this.dyn.push({fill: specialFill, stroke: theme.series.stroke}); + + if(events){ + var o = { + element: "slice", + index: i, + run: this.run, + shape: shape, + x: i, + y: typeof v == "number" ? v : v.y, + cx: circle.cx, + cy: circle.cy, + cr: r + }; + this._connectEvents(o); + eventSeries[i] = o; + } + + start = end; + + return false; // continue + }, this); + // draw labels + if(this.opt.labels){ + if(this.opt.labelStyle == "default"){ + start = startAngle; + arr.some(slices, function(slice, i){ + if(slice <= 0){ + // degenerated slice + return false; // continue + } + var theme = themes[i]; + if(slice >= 1){ + // whole pie + var v = run[i], elem = da.createText[this.opt.htmlLabels && g.renderer != "vml" ? "html" : "gfx"]( + this.chart, s, circle.cx, circle.cy + size / 2, "middle", labels[i], + theme.series.font, theme.series.fontColor); + if(this.opt.htmlLabels){ + this.htmlElements.push(elem); + } + return true; // stop iteration + } + // calculate the geometry of the slice + var end = start + slice * 2 * Math.PI, v = run[i]; + if(i + 1 == slices.length){ + end = startAngle + 2 * Math.PI; + } + var labelAngle = (start + end) / 2, + x = circle.cx + labelR * Math.cos(labelAngle), + y = circle.cy + labelR * Math.sin(labelAngle) + size / 2; + // draw the label + var elem = da.createText[this.opt.htmlLabels && g.renderer != "vml" ? "html" : "gfx"] + (this.chart, s, x, y, "middle", labels[i], theme.series.font, theme.series.fontColor); + if(this.opt.htmlLabels){ + this.htmlElements.push(elem); + } + start = end; + return false; // continue + }, this); + }else if(this.opt.labelStyle == "columns"){ + start = startAngle; + //calculate label angles + var labeledSlices = []; + arr.forEach(slices, function(slice, i){ + var end = start + slice * 2 * Math.PI; + if(i + 1 == slices.length){ + end = startAngle + 2 * Math.PI; + } + var labelAngle = (start + end) / 2; + labeledSlices.push({ + angle: labelAngle, + left: Math.cos(labelAngle) < 0, + theme: themes[i], + index: i, + omit: end - start < 0.001 + }); + start = end; + }); + //calculate label radius to each slice + var labelHeight = g._base._getTextBox("a",{font:taFont}).h; + this._getProperLabelRadius(labeledSlices, labelHeight, circle.r * 1.1); + //draw label and wiring + arr.forEach(labeledSlices, function(slice, i){ + if (!slice.omit) { + var leftColumn = circle.cx - circle.r * 2, + rightColumn = circle.cx + circle.r * 2, + labelWidth = g._base._getTextBox(labels[i], {font: taFont}).w, + x = circle.cx + slice.labelR * Math.cos(slice.angle), + y = circle.cy + slice.labelR * Math.sin(slice.angle), + jointX = (slice.left) ? (leftColumn + labelWidth) : (rightColumn - labelWidth), + labelX = (slice.left) ? leftColumn : jointX; + var wiring = s.createPath().moveTo(circle.cx + circle.r * Math.cos(slice.angle), circle.cy + circle.r * Math.sin(slice.angle)) + if (Math.abs(slice.labelR * Math.cos(slice.angle)) < circle.r * 2 - labelWidth) { + wiring.lineTo(x, y); + } + wiring.lineTo(jointX, y).setStroke(slice.theme.series.labelWiring); + var elem = da.createText[this.opt.htmlLabels && g.renderer != "vml" ? "html" : "gfx"]( + this.chart, s, labelX, y, "left", labels[i], slice.theme.series.font, slice.theme.series.fontColor); + if (this.opt.htmlLabels) { + this.htmlElements.push(elem); + } + } + },this); + } + } + // post-process events to restore the original indexing + var esi = 0; + this._eventSeries[this.run.name] = df.map(run, function(v){ + return v <= 0 ? null : eventSeries[esi++]; + }); + return this; // dojox.charting.plot2d.Pie + }, + + _getProperLabelRadius: function(slices, labelHeight, minRidius){ + var leftCenterSlice = {},rightCenterSlice = {},leftMinSIN = 1, rightMinSIN = 1; + if (slices.length == 1) { + slices[0].labelR = minRidius; + return; + } + for(var i = 0;i<slices.length;i++){ + var tempSIN = Math.abs(Math.sin(slices[i].angle)); + if(slices[i].left){ + if(leftMinSIN > tempSIN){ + leftMinSIN = tempSIN; + leftCenterSlice = slices[i]; + } + }else{ + if(rightMinSIN > tempSIN){ + rightMinSIN = tempSIN; + rightCenterSlice = slices[i]; + } + } + } + leftCenterSlice.labelR = rightCenterSlice.labelR = minRidius; + this._calculateLabelR(leftCenterSlice,slices,labelHeight); + this._calculateLabelR(rightCenterSlice,slices,labelHeight); + }, + _calculateLabelR: function(firstSlice,slices,labelHeight){ + var i = firstSlice.index,length = slices.length, + currentLabelR = firstSlice.labelR; + while(!(slices[i%length].left ^ slices[(i+1)%length].left)){ + if (!slices[(i + 1) % length].omit) { + var nextLabelR = (Math.sin(slices[i % length].angle) * currentLabelR + ((slices[i % length].left) ? (-labelHeight) : labelHeight)) / + Math.sin(slices[(i + 1) % length].angle); + currentLabelR = (nextLabelR < firstSlice.labelR) ? firstSlice.labelR : nextLabelR; + slices[(i + 1) % length].labelR = currentLabelR; + } + i++; + } + i = firstSlice.index; + var j = (i == 0)?length-1 : i - 1; + while(!(slices[i].left ^ slices[j].left)){ + if (!slices[j].omit) { + var nextLabelR = (Math.sin(slices[i].angle) * currentLabelR + ((slices[i].left) ? labelHeight : (-labelHeight))) / + Math.sin(slices[j].angle); + currentLabelR = (nextLabelR < firstSlice.labelR) ? firstSlice.labelR : nextLabelR; + slices[j].labelR = currentLabelR; + } + i--;j--; + i = (i < 0)?i+slices.length:i; + j = (j < 0)?j+slices.length:j; + } + }, + // utilities + _getLabel: function(number){ + return dc.getLabel(number, this.opt.fixed, this.opt.precision); + } + }); +}); + +}, +'dijit/hccss':function(){ +define("dijit/hccss", [ + "require", // require.toUrl + "dojo/_base/config", // config.blankGif + "dojo/dom-class", // domClass.add domConstruct.create domStyle.getComputedStyle + "dojo/dom-construct", // domClass.add domConstruct.create domStyle.getComputedStyle + "dojo/dom-style", // domClass.add domConstruct.create domStyle.getComputedStyle + "dojo/ready", // ready + "dojo/_base/sniff", // has("ie") has("mozilla") + "dojo/_base/window" // win.body +], function(require, config, domClass, domConstruct, domStyle, ready, has, win){ + + // module: + // dijit/hccss + // summary: + // Test if computer is in high contrast mode, and sets dijit_a11y flag on <body> if it is. + + if(has("ie") || has("mozilla")){ // NOTE: checking in Safari messes things up + // priority is 90 to run ahead of parser priority of 100 + ready(90, function(){ + // summary: + // Detects if we are in high-contrast mode or not + + // create div for testing if high contrast mode is on or images are turned off + var div = domConstruct.create("div",{ + id: "a11yTestNode", + style:{ + cssText:'border: 1px solid;' + + 'border-color:red green;' + + 'position: absolute;' + + 'height: 5px;' + + 'top: -999px;' + + 'background-image: url("' + (config.blankGif || require.toUrl("dojo/resources/blank.gif")) + '");' + } + }, win.body()); + + // test it + var cs = domStyle.getComputedStyle(div); + if(cs){ + var bkImg = cs.backgroundImage; + var needsA11y = (cs.borderTopColor == cs.borderRightColor) || (bkImg != null && (bkImg == "none" || bkImg == "url(invalid-url:)" )); + if(needsA11y){ + domClass.add(win.body(), "dijit_a11y"); + } + if(has("ie")){ + div.outerHTML = ""; // prevent mixed-content warning, see http://support.microsoft.com/kb/925014 + }else{ + win.body().removeChild(div); + } + } + }); + } +}); + +}, +'dojox/charting/action2d/Shake':function(){ +define("dojox/charting/action2d/Shake", ["dojo/_base/connect", "dojo/_base/declare", "./PlotAction", + "dojo/fx", "dojo/fx/easing", "dojox/gfx/matrix", "dojox/gfx/fx"], + function(hub, declare, PlotAction, df, dfe, m, gf){ + + /*===== + dojo.declare("dojox.charting.action2d.__ShakeCtorArgs", dojox.charting.action2d.__PlotActionCtorArgstorArgs, { + // summary: + // Additional arguments for highlighting actions. + + // shift: Number? + // The amount in pixels to shift the pie slice. Default is 3. + shift: 3 + }); + var PlotAction = dojox.charting.action2d.PlotAction; + =====*/ + + var DEFAULT_SHIFT = 3; + + return declare("dojox.charting.action2d.Shake", PlotAction, { + // summary: + // Create a shaking action for use on an element in a chart. + + // the data description block for the widget parser + defaultParams: { + duration: 400, // duration of the action in ms + easing: dfe.backOut, // easing for the action + shiftX: DEFAULT_SHIFT, // shift of the element along the X axis + shiftY: DEFAULT_SHIFT // shift of the element along the Y axis + }, + optionalParams: {}, // no optional parameters + + constructor: function(chart, plot, kwArgs){ + // summary: + // Create the shaking action and connect it to the plot. + // chart: dojox.charting.Chart + // The chart this action belongs to. + // plot: String? + // The plot this action is attached to. If not passed, "default" is assumed. + // kwArgs: dojox.charting.action2d.__ShakeCtorArgs? + // Optional keyword arguments object for setting parameters. + if(!kwArgs){ kwArgs = {}; } + this.shiftX = typeof kwArgs.shiftX == "number" ? kwArgs.shiftX : DEFAULT_SHIFT; + this.shiftY = typeof kwArgs.shiftY == "number" ? kwArgs.shiftY : DEFAULT_SHIFT; + + this.connect(); + }, + + process: function(o){ + // summary: + // Process the action on the given object. + // o: dojox.gfx.Shape + // The object on which to process the slice moving action. + if(!o.shape || !(o.type in this.overOutEvents)){ return; } + + var runName = o.run.name, index = o.index, vector = [], anim, + shiftX = o.type == "onmouseover" ? this.shiftX : -this.shiftX, + shiftY = o.type == "onmouseover" ? this.shiftY : -this.shiftY; + + if(runName in this.anim){ + anim = this.anim[runName][index]; + }else{ + this.anim[runName] = {}; + } + + if(anim){ + anim.action.stop(true); + }else{ + this.anim[runName][index] = anim = {}; + } + + var kwArgs = { + shape: o.shape, + duration: this.duration, + easing: this.easing, + transform: [ + {name: "translate", start: [this.shiftX, this.shiftY], end: [0, 0]}, + m.identity + ] + }; + if(o.shape){ + vector.push(gf.animateTransform(kwArgs)); + } + if(o.oultine){ + kwArgs.shape = o.outline; + vector.push(gf.animateTransform(kwArgs)); + } + if(o.shadow){ + kwArgs.shape = o.shadow; + vector.push(gf.animateTransform(kwArgs)); + } + + if(!vector.length){ + delete this.anim[runName][index]; + return; + } + + anim.action = df.combine(vector); + if(o.type == "onmouseout"){ + hub.connect(anim.action, "onEnd", this, function(){ + if(this.anim[runName]){ + delete this.anim[runName][index]; + } + }); + } + anim.action.play(); + } + }); +}); + +}, +'dojox/lang/functional/lambda':function(){ +define("dojox/lang/functional/lambda", ["../..", "dojo/_base/kernel", "dojo/_base/lang", "dojo/_base/array"], function(dojox, dojo, lang, arr){ + var df = lang.getObject("lang.functional", true, dojox); + +// This module adds high-level functions and related constructs: +// - anonymous functions built from the string + +// Acknoledgements: +// - lambda() is based on work by Oliver Steele +// (http://osteele.com/sources/javascript/functional/functional.js) +// which was published under MIT License + +// Notes: +// - lambda() produces functions, which after the compilation step are +// as fast as regular JS functions (at least theoretically). + +// Lambda input values: +// - returns functions unchanged +// - converts strings to functions +// - converts arrays to a functional composition + + var lcache = {}; + + // split() is augmented on IE6 to ensure the uniform behavior + var split = "ab".split(/a*/).length > 1 ? String.prototype.split : + function(sep){ + var r = this.split.call(this, sep), + m = sep.exec(this); + if(m && m.index == 0){ r.unshift(""); } + return r; + }; + + var lambda = function(/*String*/ s){ + var args = [], sects = split.call(s, /\s*->\s*/m); + if(sects.length > 1){ + while(sects.length){ + s = sects.pop(); + args = sects.pop().split(/\s*,\s*|\s+/m); + if(sects.length){ sects.push("(function(" + args + "){return (" + s + ")})"); } + } + }else if(s.match(/\b_\b/)){ + args = ["_"]; + }else{ + var l = s.match(/^\s*(?:[+*\/%&|\^\.=<>]|!=)/m), + r = s.match(/[+\-*\/%&|\^\.=<>!]\s*$/m); + if(l || r){ + if(l){ + args.push("$1"); + s = "$1" + s; + } + if(r){ + args.push("$2"); + s = s + "$2"; + } + }else{ + // the point of the long regex below is to exclude all well-known + // lower-case words from the list of potential arguments + var vars = s. + replace(/(?:\b[A-Z]|\.[a-zA-Z_$])[a-zA-Z_$\d]*|[a-zA-Z_$][a-zA-Z_$\d]*:|this|true|false|null|undefined|typeof|instanceof|in|delete|new|void|arguments|decodeURI|decodeURIComponent|encodeURI|encodeURIComponent|escape|eval|isFinite|isNaN|parseFloat|parseInt|unescape|dojo|dijit|dojox|window|document|'(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"/g, ""). + match(/([a-z_$][a-z_$\d]*)/gi) || [], t = {}; + arr.forEach(vars, function(v){ + if(!(v in t)){ + args.push(v); + t[v] = 1; + } + }); + } + } + return {args: args, body: s}; // Object + }; + + var compose = function(/*Array*/ a){ + return a.length ? + function(){ + var i = a.length - 1, x = df.lambda(a[i]).apply(this, arguments); + for(--i; i >= 0; --i){ x = df.lambda(a[i]).call(this, x); } + return x; + } + : + // identity + function(x){ return x; }; + }; + + lang.mixin(df, { + // lambda + rawLambda: function(/*String*/ s){ + // summary: + // builds a function from a snippet, or array (composing), + // returns an object describing the function; functions are + // passed through unmodified. + // description: + // This method is to normalize a functional representation (a + // text snippet) to an object that contains an array of + // arguments, and a body , which is used to calculate the + // returning value. + return lambda(s); // Object + }, + buildLambda: function(/*String*/ s){ + // summary: + // builds a function from a snippet, returns a string, which + // represents the function. + // description: + // This method returns a textual representation of a function + // built from the snippet. It is meant to be evaled in the + // proper context, so local variables can be pulled from the + // environment. + s = lambda(s); + return "function(" + s.args.join(",") + "){return (" + s.body + ");}"; // String + }, + lambda: function(/*Function|String|Array*/ s){ + // summary: + // builds a function from a snippet, or array (composing), + // returns a function object; functions are passed through + // unmodified. + // description: + // This method is used to normalize a functional + // representation (a text snippet, an array, or a function) to + // a function object. + if(typeof s == "function"){ return s; } + if(s instanceof Array){ return compose(s); } + if(s in lcache){ return lcache[s]; } + s = lambda(s); + return lcache[s] = new Function(s.args, "return (" + s.body + ");"); // Function + }, + clearLambdaCache: function(){ + // summary: + // clears internal cache of lambdas + lcache = {}; + } + }); + + return df; +}); + +}, +'dojox/lang/functional/reversed':function(){ +define(["dojo/_base/lang", "dojo/_base/window" ,"./lambda"], + function(lang, win, df){ +// This module adds high-level functions and related constructs: +// - reversed versions of array-processing functions similar to standard JS functions + +// Notes: +// - this module provides reversed versions of standard array-processing functions: +// forEachRev, mapRev, filterRev + +// Defined methods: +// - take any valid lambda argument as the functional argument +// - operate on dense arrays +// - take a string as the array argument + +/*===== + var df = dojox.lang.functional; + =====*/ + lang.mixin(df, { + // JS 1.6 standard array functions, which can take a lambda as a parameter. + // Consider using dojo._base.array functions, if you don't need the lambda support. + filterRev: function(/*Array|String*/ a, /*Function|String|Array*/ f, /*Object?*/ o){ + // summary: creates a new array with all elements that pass the test + // implemented by the provided function. + if(typeof a == "string"){ a = a.split(""); } + o = o || win.global; f = df.lambda(f); + var t = [], v, i = a.length - 1; + for(; i >= 0; --i){ + v = a[i]; + if(f.call(o, v, i, a)){ t.push(v); } + } + return t; // Array + }, + forEachRev: function(/*Array|String*/ a, /*Function|String|Array*/ f, /*Object?*/ o){ + // summary: executes a provided function once per array element. + if(typeof a == "string"){ a = a.split(""); } + o = o || win.global; f = df.lambda(f); + for(var i = a.length - 1; i >= 0; f.call(o, a[i], i, a), --i); + }, + mapRev: function(/*Array|String*/ a, /*Function|String|Array*/ f, /*Object?*/ o){ + // summary: creates a new array with the results of calling + // a provided function on every element in this array. + if(typeof a == "string"){ a = a.split(""); } + o = o || win.global; f = df.lambda(f); + var n = a.length, t = new Array(n), i = n - 1, j = 0; + for(; i >= 0; t[j++] = f.call(o, a[i], i, a), --i); + return t; // Array + }, + everyRev: function(/*Array|String*/ a, /*Function|String|Array*/ f, /*Object?*/ o){ + // summary: tests whether all elements in the array pass the test + // implemented by the provided function. + if(typeof a == "string"){ a = a.split(""); } + o = o || win.global; f = df.lambda(f); + for(var i = a.length - 1; i >= 0; --i){ + if(!f.call(o, a[i], i, a)){ + return false; // Boolean + } + } + return true; // Boolean + }, + someRev: function(/*Array|String*/ a, /*Function|String|Array*/ f, /*Object?*/ o){ + // summary: tests whether some element in the array passes the test + // implemented by the provided function. + if(typeof a == "string"){ a = a.split(""); } + o = o || win.global; f = df.lambda(f); + for(var i = a.length - 1; i >= 0; --i){ + if(f.call(o, a[i], i, a)){ + return true; // Boolean + } + } + return false; // Boolean + } + }); + + return df; +}); + +}, +'dojox/charting/scaler/primitive':function(){ +define("dojox/charting/scaler/primitive", ["dojo/_base/lang"], + function(lang){ + var primitive = lang.getObject("dojox.charting.scaler.primitive", true); + return lang.mixin(primitive, { + buildScaler: function(/*Number*/ min, /*Number*/ max, /*Number*/ span, /*Object*/ kwArgs){ + if(min == max){ + // artificially extend bounds + min -= 0.5; + max += 0.5; + // now the line will be centered + } + return { + bounds: { + lower: min, + upper: max, + from: min, + to: max, + scale: span / (max - min), + span: span + }, + scaler: primitive + }; + }, + buildTicks: function(/*Object*/ scaler, /*Object*/ kwArgs){ + return {major: [], minor: [], micro: []}; // Object + }, + getTransformerFromModel: function(/*Object*/ scaler){ + var offset = scaler.bounds.from, scale = scaler.bounds.scale; + return function(x){ return (x - offset) * scale; }; // Function + }, + getTransformerFromPlot: function(/*Object*/ scaler){ + var offset = scaler.bounds.from, scale = scaler.bounds.scale; + return function(x){ return x / scale + offset; }; // Function + } + }); +}); + +}, +'dojox/charting/plot2d/Candlesticks':function(){ +define("dojox/charting/plot2d/Candlesticks", ["dojo/_base/lang", "dojo/_base/declare", "dojo/_base/array", "./Base", "./common", + "dojox/lang/functional", "dojox/lang/functional/reversed", "dojox/lang/utils", "dojox/gfx/fx"], + function(lang, declare, arr, Base, dc, df, dfr, du, fx){ +/*===== +var Base = dojox.charting.plot2d.Base; +=====*/ + + var purgeGroup = dfr.lambda("item.purgeGroup()"); + + // Candlesticks are based on the Bars plot type; we expect the following passed + // as values in a series: + // { x?, open, close, high, low, mid? } + // if x is not provided, the array index is used. + // failing to provide the OHLC values will throw an error. + return declare("dojox.charting.plot2d.Candlesticks", Base, { + // summary: + // A plot that represents typical candlesticks (financial reporting, primarily). + // Unlike most charts, the Candlestick expects data points to be represented by + // an object of the form { x?, open, close, high, low, mid? }, where both + // x and mid are optional parameters. If x is not provided, the index of the + // data array is used. + defaultParams: { + hAxis: "x", // use a horizontal axis named "x" + vAxis: "y", // use a vertical axis named "y" + gap: 2, // gap between columns in pixels + animate: null // animate bars into place + }, + optionalParams: { + minBarSize: 1, // minimal candle width in pixels + maxBarSize: 1, // maximal candle width in pixels + // theme component + stroke: {}, + outline: {}, + shadow: {}, + fill: {}, + font: "", + fontColor: "" + }, + + constructor: function(chart, kwArgs){ + // summary: + // The constructor for a candlestick chart. + // chart: dojox.charting.Chart + // The chart this plot belongs to. + // kwArgs: dojox.charting.plot2d.__BarCtorArgs? + // An optional keyword arguments object to help define the plot. + this.opt = lang.clone(this.defaultParams); + du.updateWithObject(this.opt, kwArgs); + du.updateWithPattern(this.opt, kwArgs, this.optionalParams); + this.series = []; + this.hAxis = this.opt.hAxis; + this.vAxis = this.opt.vAxis; + this.animate = this.opt.animate; + }, + + collectStats: function(series){ + // summary: + // Collect all statistics for drawing this chart. Since the common + // functionality only assumes x and y, Candlesticks must create it's own + // stats (since data has no y value, but open/close/high/low instead). + // series: dojox.charting.Series[] + // The data series array to be drawn on this plot. + // returns: Object + // Returns an object in the form of { hmin, hmax, vmin, vmax }. + + // we have to roll our own, since we need to use all four passed + // values to figure out our stats, and common only assumes x and y. + var stats = lang.delegate(dc.defaultStats); + for(var i=0; i<series.length; i++){ + var run = series[i]; + if(!run.data.length){ continue; } + var old_vmin = stats.vmin, old_vmax = stats.vmax; + if(!("ymin" in run) || !("ymax" in run)){ + arr.forEach(run.data, function(val, idx){ + if(val !== null){ + var x = val.x || idx + 1; + stats.hmin = Math.min(stats.hmin, x); + stats.hmax = Math.max(stats.hmax, x); + stats.vmin = Math.min(stats.vmin, val.open, val.close, val.high, val.low); + stats.vmax = Math.max(stats.vmax, val.open, val.close, val.high, val.low); + } + }); + } + if("ymin" in run){ stats.vmin = Math.min(old_vmin, run.ymin); } + if("ymax" in run){ stats.vmax = Math.max(old_vmax, run.ymax); } + } + return stats; // Object + }, + + getSeriesStats: function(){ + // summary: + // Calculate the min/max on all attached series in both directions. + // returns: Object + // {hmin, hmax, vmin, vmax} min/max in both directions. + var stats = this.collectStats(this.series); + stats.hmin -= 0.5; + stats.hmax += 0.5; + return stats; + }, + + render: function(dim, offsets){ + // summary: + // Run the calculations for any axes for this plot. + // dim: Object + // An object in the form of { width, height } + // offsets: Object + // An object of the form { l, r, t, b}. + // returns: dojox.charting.plot2d.Candlesticks + // A reference to this plot for functional chaining. + if(this.zoom && !this.isDataDirty()){ + return this.performZoom(dim, offsets); + } + this.resetEvents(); + this.dirty = this.isDirty(); + if(this.dirty){ + arr.forEach(this.series, purgeGroup); + this._eventSeries = {}; + this.cleanGroup(); + var s = this.group; + df.forEachRev(this.series, function(item){ item.cleanGroup(s); }); + } + var t = this.chart.theme, f, gap, width, + ht = this._hScaler.scaler.getTransformerFromModel(this._hScaler), + vt = this._vScaler.scaler.getTransformerFromModel(this._vScaler), + baseline = Math.max(0, this._vScaler.bounds.lower), + baselineHeight = vt(baseline), + events = this.events(); + f = dc.calculateBarSize(this._hScaler.bounds.scale, this.opt); + gap = f.gap; + width = f.size; + for(var i = this.series.length - 1; i >= 0; --i){ + var run = this.series[i]; + if(!this.dirty && !run.dirty){ + t.skip(); + this._reconnectEvents(run.name); + continue; + } + run.cleanGroup(); + var theme = t.next("candlestick", [this.opt, run]), s = run.group, + eventSeries = new Array(run.data.length); + for(var j = 0; j < run.data.length; ++j){ + var v = run.data[j]; + if(v !== null){ + var finalTheme = t.addMixin(theme, "candlestick", v, true); + + // calculate the points we need for OHLC + var x = ht(v.x || (j+0.5)) + offsets.l + gap, + y = dim.height - offsets.b, + open = vt(v.open), + close = vt(v.close), + high = vt(v.high), + low = vt(v.low); + if("mid" in v){ + var mid = vt(v.mid); + } + if(low > high){ + var tmp = high; + high = low; + low = tmp; + } + + if(width >= 1){ + // draw the line and rect, set up as a group and pass that to the events. + var doFill = open > close; + var line = { x1: width/2, x2: width/2, y1: y - high, y2: y - low }, + rect = { + x: 0, y: y-Math.max(open, close), + width: width, height: Math.max(doFill ? open-close : close-open, 1) + }; + var shape = s.createGroup(); + shape.setTransform({dx: x, dy: 0 }); + var inner = shape.createGroup(); + inner.createLine(line).setStroke(finalTheme.series.stroke); + inner.createRect(rect).setStroke(finalTheme.series.stroke). + setFill(doFill ? finalTheme.series.fill : "white"); + if("mid" in v){ + // add the mid line. + inner.createLine({ + x1: (finalTheme.series.stroke.width||1), x2: width - (finalTheme.series.stroke.width || 1), + y1: y - mid, y2: y - mid + }).setStroke(doFill ? "white" : finalTheme.series.stroke); + } + + // TODO: double check this. + run.dyn.fill = finalTheme.series.fill; + run.dyn.stroke = finalTheme.series.stroke; + if(events){ + var o = { + element: "candlestick", + index: j, + run: run, + shape: inner, + x: x, + y: y-Math.max(open, close), + cx: width/2, + cy: (y-Math.max(open, close)) + (Math.max(doFill ? open-close : close-open, 1)/2), + width: width, + height: Math.max(doFill ? open-close : close-open, 1), + data: v + }; + this._connectEvents(o); + eventSeries[j] = o; + } + } + if(this.animate){ + this._animateCandlesticks(shape, y - low, high - low); + } + } + } + this._eventSeries[run.name] = eventSeries; + run.dirty = false; + } + this.dirty = false; + return this; // dojox.charting.plot2d.Candlesticks + }, + _animateCandlesticks: function(shape, voffset, vsize){ + fx.animateTransform(lang.delegate({ + shape: shape, + duration: 1200, + transform: [ + {name: "translate", start: [0, voffset - (voffset/vsize)], end: [0, 0]}, + {name: "scale", start: [1, 1/vsize], end: [1, 1]}, + {name: "original"} + ] + }, this.animate)).play(); + } + }); +}); + +}, +'dojox/charting/widget/Sparkline':function(){ +define("dojox/charting/widget/Sparkline", ["dojo/_base/lang", "dojo/_base/array", "dojo/_base/declare", "dojo/_base/html", "dojo/query", + "./Chart", "../themes/GreySkies", "../plot2d/Lines", "dojo/dom-prop"], + function(lang, arrayUtil, declare, html, query, Chart, GreySkies, Lines, domProp){ +/*===== +var Chart = dojox.charting.widget.Chart; +=====*/ + + declare("dojox.charting.widget.Sparkline", Chart, { + theme: GreySkies, + margins: { l: 0, r: 0, t: 0, b: 0 }, + type: "Lines", + valueFn: "Number(x)", + store: "", + field: "", + query: "", + queryOptions: "", + start: "0", + count: "Infinity", + sort: "", + data: "", + name: "default", + buildRendering: function(){ + var n = this.srcNodeRef; + if( !n.childNodes.length || // shortcut the query + !query("> .axis, > .plot, > .action, > .series", n).length){ + var plot = document.createElement("div"); + domProp.set(plot, { + "class": "plot", + "name": "default", + "type": this.type + }); + n.appendChild(plot); + + var series = document.createElement("div"); + domProp.set(series, { + "class": "series", + plot: "default", + name: this.name, + start: this.start, + count: this.count, + valueFn: this.valueFn + }); + arrayUtil.forEach( + ["store", "field", "query", "queryOptions", "sort", "data"], + function(i){ + if(this[i].length){ + domProp.set(series, i, this[i]); + } + }, + this + ); + n.appendChild(series); + } + this.inherited(arguments); + } + } + ); +}); + +}, +'dojox/gfx/matrix':function(){ +define("dojox/gfx/matrix", ["./_base","dojo/_base/lang"], + function(g, lang){ + var m = g.matrix = {}; + /*===== g = dojox.gfx; m = dojox.gfx.matrix =====*/ + + // candidates for dojox.math: + var _degToRadCache = {}; + m._degToRad = function(degree){ + return _degToRadCache[degree] || (_degToRadCache[degree] = (Math.PI * degree / 180)); + }; + m._radToDeg = function(radian){ return radian / Math.PI * 180; }; + + m.Matrix2D = function(arg){ + // summary: + // a 2D matrix object + // description: Normalizes a 2D matrix-like object. If arrays is passed, + // all objects of the array are normalized and multiplied sequentially. + // arg: Object + // a 2D matrix-like object, a number, or an array of such objects + if(arg){ + if(typeof arg == "number"){ + this.xx = this.yy = arg; + }else if(arg instanceof Array){ + if(arg.length > 0){ + var matrix = m.normalize(arg[0]); + // combine matrices + for(var i = 1; i < arg.length; ++i){ + var l = matrix, r = m.normalize(arg[i]); + matrix = new m.Matrix2D(); + matrix.xx = l.xx * r.xx + l.xy * r.yx; + matrix.xy = l.xx * r.xy + l.xy * r.yy; + matrix.yx = l.yx * r.xx + l.yy * r.yx; + matrix.yy = l.yx * r.xy + l.yy * r.yy; + matrix.dx = l.xx * r.dx + l.xy * r.dy + l.dx; + matrix.dy = l.yx * r.dx + l.yy * r.dy + l.dy; + } + lang.mixin(this, matrix); + } + }else{ + lang.mixin(this, arg); + } + } + }; + + // the default (identity) matrix, which is used to fill in missing values + lang.extend(m.Matrix2D, {xx: 1, xy: 0, yx: 0, yy: 1, dx: 0, dy: 0}); + + lang.mixin(m, { + // summary: class constants, and methods of dojox.gfx.matrix + + // matrix constants + + // identity: dojox.gfx.matrix.Matrix2D + // an identity matrix constant: identity * (x, y) == (x, y) + identity: new m.Matrix2D(), + + // flipX: dojox.gfx.matrix.Matrix2D + // a matrix, which reflects points at x = 0 line: flipX * (x, y) == (-x, y) + flipX: new m.Matrix2D({xx: -1}), + + // flipY: dojox.gfx.matrix.Matrix2D + // a matrix, which reflects points at y = 0 line: flipY * (x, y) == (x, -y) + flipY: new m.Matrix2D({yy: -1}), + + // flipXY: dojox.gfx.matrix.Matrix2D + // a matrix, which reflects points at the origin of coordinates: flipXY * (x, y) == (-x, -y) + flipXY: new m.Matrix2D({xx: -1, yy: -1}), + + // matrix creators + + translate: function(a, b){ + // summary: forms a translation matrix + // description: The resulting matrix is used to translate (move) points by specified offsets. + // a: Number: an x coordinate value + // b: Number: a y coordinate value + if(arguments.length > 1){ + return new m.Matrix2D({dx: a, dy: b}); // dojox.gfx.matrix.Matrix2D + } + // branch + // a: dojox.gfx.Point: a point-like object, which specifies offsets for both dimensions + // b: null + return new m.Matrix2D({dx: a.x, dy: a.y}); // dojox.gfx.matrix.Matrix2D + }, + scale: function(a, b){ + // summary: forms a scaling matrix + // description: The resulting matrix is used to scale (magnify) points by specified offsets. + // a: Number: a scaling factor used for the x coordinate + // b: Number: a scaling factor used for the y coordinate + if(arguments.length > 1){ + return new m.Matrix2D({xx: a, yy: b}); // dojox.gfx.matrix.Matrix2D + } + if(typeof a == "number"){ + // branch + // a: Number: a uniform scaling factor used for the both coordinates + // b: null + return new m.Matrix2D({xx: a, yy: a}); // dojox.gfx.matrix.Matrix2D + } + // branch + // a: dojox.gfx.Point: a point-like object, which specifies scale factors for both dimensions + // b: null + return new m.Matrix2D({xx: a.x, yy: a.y}); // dojox.gfx.matrix.Matrix2D + }, + rotate: function(angle){ + // summary: forms a rotating matrix + // description: The resulting matrix is used to rotate points + // around the origin of coordinates (0, 0) by specified angle. + // angle: Number: an angle of rotation in radians (>0 for CW) + var c = Math.cos(angle); + var s = Math.sin(angle); + return new m.Matrix2D({xx: c, xy: -s, yx: s, yy: c}); // dojox.gfx.matrix.Matrix2D + }, + rotateg: function(degree){ + // summary: forms a rotating matrix + // description: The resulting matrix is used to rotate points + // around the origin of coordinates (0, 0) by specified degree. + // See dojox.gfx.matrix.rotate() for comparison. + // degree: Number: an angle of rotation in degrees (>0 for CW) + return m.rotate(m._degToRad(degree)); // dojox.gfx.matrix.Matrix2D + }, + skewX: function(angle) { + // summary: forms an x skewing matrix + // description: The resulting matrix is used to skew points in the x dimension + // around the origin of coordinates (0, 0) by specified angle. + // angle: Number: an skewing angle in radians + return new m.Matrix2D({xy: Math.tan(angle)}); // dojox.gfx.matrix.Matrix2D + }, + skewXg: function(degree){ + // summary: forms an x skewing matrix + // description: The resulting matrix is used to skew points in the x dimension + // around the origin of coordinates (0, 0) by specified degree. + // See dojox.gfx.matrix.skewX() for comparison. + // degree: Number: an skewing angle in degrees + return m.skewX(m._degToRad(degree)); // dojox.gfx.matrix.Matrix2D + }, + skewY: function(angle){ + // summary: forms a y skewing matrix + // description: The resulting matrix is used to skew points in the y dimension + // around the origin of coordinates (0, 0) by specified angle. + // angle: Number: an skewing angle in radians + return new m.Matrix2D({yx: Math.tan(angle)}); // dojox.gfx.matrix.Matrix2D + }, + skewYg: function(degree){ + // summary: forms a y skewing matrix + // description: The resulting matrix is used to skew points in the y dimension + // around the origin of coordinates (0, 0) by specified degree. + // See dojox.gfx.matrix.skewY() for comparison. + // degree: Number: an skewing angle in degrees + return m.skewY(m._degToRad(degree)); // dojox.gfx.matrix.Matrix2D + }, + reflect: function(a, b){ + // summary: forms a reflection matrix + // description: The resulting matrix is used to reflect points around a vector, + // which goes through the origin. + // a: dojox.gfx.Point: a point-like object, which specifies a vector of reflection + // b: null + if(arguments.length == 1){ + b = a.y; + a = a.x; + } + // branch + // a: Number: an x coordinate value + // b: Number: a y coordinate value + + // make a unit vector + var a2 = a * a, b2 = b * b, n2 = a2 + b2, xy = 2 * a * b / n2; + return new m.Matrix2D({xx: 2 * a2 / n2 - 1, xy: xy, yx: xy, yy: 2 * b2 / n2 - 1}); // dojox.gfx.matrix.Matrix2D + }, + project: function(a, b){ + // summary: forms an orthogonal projection matrix + // description: The resulting matrix is used to project points orthogonally on a vector, + // which goes through the origin. + // a: dojox.gfx.Point: a point-like object, which specifies a vector of projection + // b: null + if(arguments.length == 1){ + b = a.y; + a = a.x; + } + // branch + // a: Number: an x coordinate value + // b: Number: a y coordinate value + + // make a unit vector + var a2 = a * a, b2 = b * b, n2 = a2 + b2, xy = a * b / n2; + return new m.Matrix2D({xx: a2 / n2, xy: xy, yx: xy, yy: b2 / n2}); // dojox.gfx.matrix.Matrix2D + }, + + // ensure matrix 2D conformance + normalize: function(matrix){ + // summary: converts an object to a matrix, if necessary + // description: Converts any 2D matrix-like object or an array of + // such objects to a valid dojox.gfx.matrix.Matrix2D object. + // matrix: Object: an object, which is converted to a matrix, if necessary + return (matrix instanceof m.Matrix2D) ? matrix : new m.Matrix2D(matrix); // dojox.gfx.matrix.Matrix2D + }, + + // common operations + + clone: function(matrix){ + // summary: creates a copy of a 2D matrix + // matrix: dojox.gfx.matrix.Matrix2D: a 2D matrix-like object to be cloned + var obj = new m.Matrix2D(); + for(var i in matrix){ + if(typeof(matrix[i]) == "number" && typeof(obj[i]) == "number" && obj[i] != matrix[i]) obj[i] = matrix[i]; + } + return obj; // dojox.gfx.matrix.Matrix2D + }, + invert: function(matrix){ + // summary: inverts a 2D matrix + // matrix: dojox.gfx.matrix.Matrix2D: a 2D matrix-like object to be inverted + var M = m.normalize(matrix), + D = M.xx * M.yy - M.xy * M.yx; + M = new m.Matrix2D({ + xx: M.yy/D, xy: -M.xy/D, + yx: -M.yx/D, yy: M.xx/D, + dx: (M.xy * M.dy - M.yy * M.dx) / D, + dy: (M.yx * M.dx - M.xx * M.dy) / D + }); + return M; // dojox.gfx.matrix.Matrix2D + }, + _multiplyPoint: function(matrix, x, y){ + // summary: applies a matrix to a point + // matrix: dojox.gfx.matrix.Matrix2D: a 2D matrix object to be applied + // x: Number: an x coordinate of a point + // y: Number: a y coordinate of a point + return {x: matrix.xx * x + matrix.xy * y + matrix.dx, y: matrix.yx * x + matrix.yy * y + matrix.dy}; // dojox.gfx.Point + }, + multiplyPoint: function(matrix, /* Number||Point */ a, /* Number? */ b){ + // summary: applies a matrix to a point + // matrix: dojox.gfx.matrix.Matrix2D: a 2D matrix object to be applied + // a: Number: an x coordinate of a point + // b: Number?: a y coordinate of a point + var M = m.normalize(matrix); + if(typeof a == "number" && typeof b == "number"){ + return m._multiplyPoint(M, a, b); // dojox.gfx.Point + } + // branch + // matrix: dojox.gfx.matrix.Matrix2D: a 2D matrix object to be applied + // a: dojox.gfx.Point: a point + // b: null + return m._multiplyPoint(M, a.x, a.y); // dojox.gfx.Point + }, + multiply: function(matrix){ + // summary: combines matrices by multiplying them sequentially in the given order + // matrix: dojox.gfx.matrix.Matrix2D...: a 2D matrix-like object, + // all subsequent arguments are matrix-like objects too + var M = m.normalize(matrix); + // combine matrices + for(var i = 1; i < arguments.length; ++i){ + var l = M, r = m.normalize(arguments[i]); + M = new m.Matrix2D(); + M.xx = l.xx * r.xx + l.xy * r.yx; + M.xy = l.xx * r.xy + l.xy * r.yy; + M.yx = l.yx * r.xx + l.yy * r.yx; + M.yy = l.yx * r.xy + l.yy * r.yy; + M.dx = l.xx * r.dx + l.xy * r.dy + l.dx; + M.dy = l.yx * r.dx + l.yy * r.dy + l.dy; + } + return M; // dojox.gfx.matrix.Matrix2D + }, + + // high level operations + + _sandwich: function(matrix, x, y){ + // summary: applies a matrix at a centrtal point + // matrix: dojox.gfx.matrix.Matrix2D: a 2D matrix-like object, which is applied at a central point + // x: Number: an x component of the central point + // y: Number: a y component of the central point + return m.multiply(m.translate(x, y), matrix, m.translate(-x, -y)); // dojox.gfx.matrix.Matrix2D + }, + scaleAt: function(a, b, c, d){ + // summary: scales a picture using a specified point as a center of scaling + // description: Compare with dojox.gfx.matrix.scale(). + // a: Number: a scaling factor used for the x coordinate + // b: Number: a scaling factor used for the y coordinate + // c: Number: an x component of a central point + // d: Number: a y component of a central point + + // accepts several signatures: + // 1) uniform scale factor, Point + // 2) uniform scale factor, x, y + // 3) x scale, y scale, Point + // 4) x scale, y scale, x, y + + switch(arguments.length){ + case 4: + // a and b are scale factor components, c and d are components of a point + return m._sandwich(m.scale(a, b), c, d); // dojox.gfx.matrix.Matrix2D + case 3: + if(typeof c == "number"){ + // branch + // a: Number: a uniform scaling factor used for both coordinates + // b: Number: an x component of a central point + // c: Number: a y component of a central point + // d: null + return m._sandwich(m.scale(a), b, c); // dojox.gfx.matrix.Matrix2D + } + // branch + // a: Number: a scaling factor used for the x coordinate + // b: Number: a scaling factor used for the y coordinate + // c: dojox.gfx.Point: a central point + // d: null + return m._sandwich(m.scale(a, b), c.x, c.y); // dojox.gfx.matrix.Matrix2D + } + // branch + // a: Number: a uniform scaling factor used for both coordinates + // b: dojox.gfx.Point: a central point + // c: null + // d: null + return m._sandwich(m.scale(a), b.x, b.y); // dojox.gfx.matrix.Matrix2D + }, + rotateAt: function(angle, a, b){ + // summary: rotates a picture using a specified point as a center of rotation + // description: Compare with dojox.gfx.matrix.rotate(). + // angle: Number: an angle of rotation in radians (>0 for CW) + // a: Number: an x component of a central point + // b: Number: a y component of a central point + + // accepts several signatures: + // 1) rotation angle in radians, Point + // 2) rotation angle in radians, x, y + + if(arguments.length > 2){ + return m._sandwich(m.rotate(angle), a, b); // dojox.gfx.matrix.Matrix2D + } + + // branch + // angle: Number: an angle of rotation in radians (>0 for CCW) + // a: dojox.gfx.Point: a central point + // b: null + return m._sandwich(m.rotate(angle), a.x, a.y); // dojox.gfx.matrix.Matrix2D + }, + rotategAt: function(degree, a, b){ + // summary: rotates a picture using a specified point as a center of rotation + // description: Compare with dojox.gfx.matrix.rotateg(). + // degree: Number: an angle of rotation in degrees (>0 for CW) + // a: Number: an x component of a central point + // b: Number: a y component of a central point + + // accepts several signatures: + // 1) rotation angle in degrees, Point + // 2) rotation angle in degrees, x, y + + if(arguments.length > 2){ + return m._sandwich(m.rotateg(degree), a, b); // dojox.gfx.matrix.Matrix2D + } + + // branch + // degree: Number: an angle of rotation in degrees (>0 for CCW) + // a: dojox.gfx.Point: a central point + // b: null + return m._sandwich(m.rotateg(degree), a.x, a.y); // dojox.gfx.matrix.Matrix2D + }, + skewXAt: function(angle, a, b){ + // summary: skews a picture along the x axis using a specified point as a center of skewing + // description: Compare with dojox.gfx.matrix.skewX(). + // angle: Number: an skewing angle in radians + // a: Number: an x component of a central point + // b: Number: a y component of a central point + + // accepts several signatures: + // 1) skew angle in radians, Point + // 2) skew angle in radians, x, y + + if(arguments.length > 2){ + return m._sandwich(m.skewX(angle), a, b); // dojox.gfx.matrix.Matrix2D + } + + // branch + // angle: Number: an skewing angle in radians + // a: dojox.gfx.Point: a central point + // b: null + return m._sandwich(m.skewX(angle), a.x, a.y); // dojox.gfx.matrix.Matrix2D + }, + skewXgAt: function(degree, a, b){ + // summary: skews a picture along the x axis using a specified point as a center of skewing + // description: Compare with dojox.gfx.matrix.skewXg(). + // degree: Number: an skewing angle in degrees + // a: Number: an x component of a central point + // b: Number: a y component of a central point + + // accepts several signatures: + // 1) skew angle in degrees, Point + // 2) skew angle in degrees, x, y + + if(arguments.length > 2){ + return m._sandwich(m.skewXg(degree), a, b); // dojox.gfx.matrix.Matrix2D + } + + // branch + // degree: Number: an skewing angle in degrees + // a: dojox.gfx.Point: a central point + // b: null + return m._sandwich(m.skewXg(degree), a.x, a.y); // dojox.gfx.matrix.Matrix2D + }, + skewYAt: function(angle, a, b){ + // summary: skews a picture along the y axis using a specified point as a center of skewing + // description: Compare with dojox.gfx.matrix.skewY(). + // angle: Number: an skewing angle in radians + // a: Number: an x component of a central point + // b: Number: a y component of a central point + + // accepts several signatures: + // 1) skew angle in radians, Point + // 2) skew angle in radians, x, y + + if(arguments.length > 2){ + return m._sandwich(m.skewY(angle), a, b); // dojox.gfx.matrix.Matrix2D + } + + // branch + // angle: Number: an skewing angle in radians + // a: dojox.gfx.Point: a central point + // b: null + return m._sandwich(m.skewY(angle), a.x, a.y); // dojox.gfx.matrix.Matrix2D + }, + skewYgAt: function(/* Number */ degree, /* Number||Point */ a, /* Number? */ b){ + // summary: skews a picture along the y axis using a specified point as a center of skewing + // description: Compare with dojox.gfx.matrix.skewYg(). + // degree: Number: an skewing angle in degrees + // a: Number: an x component of a central point + // b: Number?: a y component of a central point + + // accepts several signatures: + // 1) skew angle in degrees, Point + // 2) skew angle in degrees, x, y + + if(arguments.length > 2){ + return m._sandwich(m.skewYg(degree), a, b); // dojox.gfx.matrix.Matrix2D + } + + // branch + // degree: Number: an skewing angle in degrees + // a: dojox.gfx.Point: a central point + // b: null + return m._sandwich(m.skewYg(degree), a.x, a.y); // dojox.gfx.matrix.Matrix2D + } + + //TODO: rect-to-rect mapping, scale-to-fit (isotropic and anisotropic versions) + + }); + // propagate Matrix2D up + g.Matrix2D = m.Matrix2D; + + return m; +}); + + + +}, +'dojox/charting/plot2d/Scatter':function(){ +define("dojox/charting/plot2d/Scatter", ["dojo/_base/lang", "dojo/_base/array", "dojo/_base/declare", "./Base", "./common", + "dojox/lang/functional", "dojox/lang/functional/reversed", "dojox/lang/utils", "dojox/gfx/fx", "dojox/gfx/gradutils"], + function(lang, arr, declare, Base, dc, df, dfr, du, fx, gradutils){ +/*===== +var Base = dojox.charting.plot2d.Base; +=====*/ + var purgeGroup = dfr.lambda("item.purgeGroup()"); + + return declare("dojox.charting.plot2d.Scatter", Base, { + // summary: + // A plot object representing a typical scatter chart. + defaultParams: { + hAxis: "x", // use a horizontal axis named "x" + vAxis: "y", // use a vertical axis named "y" + shadows: null, // draw shadows + animate: null // animate chart to place + }, + optionalParams: { + // theme component + markerStroke: {}, + markerOutline: {}, + markerShadow: {}, + markerFill: {}, + markerFont: "", + markerFontColor: "" + }, + + constructor: function(chart, kwArgs){ + // summary: + // Create the scatter plot. + // chart: dojox.charting.Chart + // The chart this plot belongs to. + // kwArgs: dojox.charting.plot2d.__DefaultCtorArgs? + // An optional keyword arguments object to help define this plot's parameters. + this.opt = lang.clone(this.defaultParams); + du.updateWithObject(this.opt, kwArgs); + du.updateWithPattern(this.opt, kwArgs, this.optionalParams); + this.series = []; + this.hAxis = this.opt.hAxis; + this.vAxis = this.opt.vAxis; + this.animate = this.opt.animate; + }, + + render: function(dim, offsets){ + // summary: + // Run the calculations for any axes for this plot. + // dim: Object + // An object in the form of { width, height } + // offsets: Object + // An object of the form { l, r, t, b}. + // returns: dojox.charting.plot2d.Scatter + // A reference to this plot for functional chaining. + if(this.zoom && !this.isDataDirty()){ + return this.performZoom(dim, offsets); + } + this.resetEvents(); + this.dirty = this.isDirty(); + if(this.dirty){ + arr.forEach(this.series, purgeGroup); + this._eventSeries = {}; + this.cleanGroup(); + var s = this.group; + df.forEachRev(this.series, function(item){ item.cleanGroup(s); }); + } + var t = this.chart.theme, events = this.events(); + for(var i = this.series.length - 1; i >= 0; --i){ + var run = this.series[i]; + if(!this.dirty && !run.dirty){ + t.skip(); + this._reconnectEvents(run.name); + continue; + } + run.cleanGroup(); + if(!run.data.length){ + run.dirty = false; + t.skip(); + continue; + } + + var theme = t.next("marker", [this.opt, run]), s = run.group, lpoly, + ht = this._hScaler.scaler.getTransformerFromModel(this._hScaler), + vt = this._vScaler.scaler.getTransformerFromModel(this._vScaler); + if(typeof run.data[0] == "number"){ + lpoly = arr.map(run.data, function(v, i){ + return { + x: ht(i + 1) + offsets.l, + y: dim.height - offsets.b - vt(v) + }; + }, this); + }else{ + lpoly = arr.map(run.data, function(v, i){ + return { + x: ht(v.x) + offsets.l, + y: dim.height - offsets.b - vt(v.y) + }; + }, this); + } + + var shadowMarkers = new Array(lpoly.length), + frontMarkers = new Array(lpoly.length), + outlineMarkers = new Array(lpoly.length); + + arr.forEach(lpoly, function(c, i){ + var finalTheme = typeof run.data[i] == "number" ? + t.post(theme, "marker") : + t.addMixin(theme, "marker", run.data[i], true), + path = "M" + c.x + " " + c.y + " " + finalTheme.symbol; + if(finalTheme.marker.shadow){ + shadowMarkers[i] = s.createPath("M" + (c.x + finalTheme.marker.shadow.dx) + " " + + (c.y + finalTheme.marker.shadow.dy) + " " + finalTheme.symbol). + setStroke(finalTheme.marker.shadow).setFill(finalTheme.marker.shadow.color); + if(this.animate){ + this._animateScatter(shadowMarkers[i], dim.height - offsets.b); + } + } + if(finalTheme.marker.outline){ + var outline = dc.makeStroke(finalTheme.marker.outline); + outline.width = 2 * outline.width + finalTheme.marker.stroke.width; + outlineMarkers[i] = s.createPath(path).setStroke(outline); + if(this.animate){ + this._animateScatter(outlineMarkers[i], dim.height - offsets.b); + } + } + var stroke = dc.makeStroke(finalTheme.marker.stroke), + fill = this._plotFill(finalTheme.marker.fill, dim, offsets); + if(fill && (fill.type === "linear" || fill.type == "radial")){ + var color = gradutils.getColor(fill, {x: c.x, y: c.y}); + if(stroke){ + stroke.color = color; + } + frontMarkers[i] = s.createPath(path).setStroke(stroke).setFill(color); + }else{ + frontMarkers[i] = s.createPath(path).setStroke(stroke).setFill(fill); + } + if(this.animate){ + this._animateScatter(frontMarkers[i], dim.height - offsets.b); + } + }, this); + if(frontMarkers.length){ + run.dyn.stroke = frontMarkers[frontMarkers.length - 1].getStroke(); + run.dyn.fill = frontMarkers[frontMarkers.length - 1].getFill(); + } + + if(events){ + var eventSeries = new Array(frontMarkers.length); + arr.forEach(frontMarkers, function(s, i){ + var o = { + element: "marker", + index: i, + run: run, + shape: s, + outline: outlineMarkers && outlineMarkers[i] || null, + shadow: shadowMarkers && shadowMarkers[i] || null, + cx: lpoly[i].x, + cy: lpoly[i].y + }; + if(typeof run.data[0] == "number"){ + o.x = i + 1; + o.y = run.data[i]; + }else{ + o.x = run.data[i].x; + o.y = run.data[i].y; + } + this._connectEvents(o); + eventSeries[i] = o; + }, this); + this._eventSeries[run.name] = eventSeries; + }else{ + delete this._eventSeries[run.name]; + } + run.dirty = false; + } + this.dirty = false; + return this; // dojox.charting.plot2d.Scatter + }, + _animateScatter: function(shape, offset){ + fx.animateTransform(lang.delegate({ + shape: shape, + duration: 1200, + transform: [ + {name: "translate", start: [0, offset], end: [0, 0]}, + {name: "scale", start: [0, 0], end: [1, 1]}, + {name: "original"} + ] + }, this.animate)).play(); + } + }); +}); + +}, +'dojox/lang/functional/scan':function(){ +define("dojox/lang/functional/scan", ["dojo/_base/kernel", "dojo/_base/lang", "./lambda"], function(d, darray, df){ + +// This module adds high-level functions and related constructs: +// - "scan" family of functions + +// Notes: +// - missing high-level functions are provided with the compatible API: +// scanl, scanl1, scanr, scanr1 + +// Defined methods: +// - take any valid lambda argument as the functional argument +// - operate on dense arrays +// - take a string as the array argument +// - take an iterator objects as the array argument (only scanl, and scanl1) + + var empty = {}; + + d.mixin(df, { + // classic reduce-class functions + scanl: function(/*Array|String|Object*/ a, /*Function|String|Array*/ f, /*Object*/ z, /*Object?*/ o){ + // summary: repeatedly applies a binary function to an array from left + // to right using a seed value as a starting point; returns an array + // of values produced by foldl() at that point. + if(typeof a == "string"){ a = a.split(""); } + o = o || d.global; f = df.lambda(f); + var t, n, i; + if(d.isArray(a)){ + // array + t = new Array((n = a.length) + 1); + t[0] = z; + for(i = 0; i < n; z = f.call(o, z, a[i], i, a), t[++i] = z); + }else if(typeof a.hasNext == "function" && typeof a.next == "function"){ + // iterator + t = [z]; + for(i = 0; a.hasNext(); t.push(z = f.call(o, z, a.next(), i++, a))); + }else{ + // object/dictionary + t = [z]; + for(i in a){ + if(!(i in empty)){ + t.push(z = f.call(o, z, a[i], i, a)); + } + } + } + return t; // Array + }, + scanl1: function(/*Array|String|Object*/ a, /*Function|String|Array*/ f, /*Object?*/ o){ + // summary: repeatedly applies a binary function to an array from left + // to right; returns an array of values produced by foldl1() at that + // point. + if(typeof a == "string"){ a = a.split(""); } + o = o || d.global; f = df.lambda(f); + var t, n, z, first = true; + if(d.isArray(a)){ + // array + t = new Array(n = a.length); + t[0] = z = a[0]; + for(var i = 1; i < n; t[i] = z = f.call(o, z, a[i], i, a), ++i); + }else if(typeof a.hasNext == "function" && typeof a.next == "function"){ + // iterator + if(a.hasNext()){ + t = [z = a.next()]; + for(i = 1; a.hasNext(); t.push(z = f.call(o, z, a.next(), i++, a))); + } + }else{ + // object/dictionary + for(i in a){ + if(!(i in empty)){ + if(first){ + t = [z = a[i]]; + first = false; + }else{ + t.push(z = f.call(o, z, a[i], i, a)); + } + } + } + } + return t; // Array + }, + scanr: function(/*Array|String*/ a, /*Function|String|Array*/ f, /*Object*/ z, /*Object?*/ o){ + // summary: repeatedly applies a binary function to an array from right + // to left using a seed value as a starting point; returns an array + // of values produced by foldr() at that point. + if(typeof a == "string"){ a = a.split(""); } + o = o || d.global; f = df.lambda(f); + var n = a.length, t = new Array(n + 1), i = n; + t[n] = z; + for(; i > 0; --i, z = f.call(o, z, a[i], i, a), t[i] = z); + return t; // Array + }, + scanr1: function(/*Array|String*/ a, /*Function|String|Array*/ f, /*Object?*/ o){ + // summary: repeatedly applies a binary function to an array from right + // to left; returns an array of values produced by foldr1() at that + // point. + if(typeof a == "string"){ a = a.split(""); } + o = o || d.global; f = df.lambda(f); + var n = a.length, t = new Array(n), z = a[n - 1], i = n - 1; + t[i] = z; + for(; i > 0; --i, z = f.call(o, z, a[i], i, a), t[i] = z); + return t; // Array + } + }); +}); + +}, +'dojox/color/_base':function(){ +define("dojox/color/_base", ["dojo/_base/kernel", "../main", "dojo/_base/lang", "dojo/_base/Color", "dojo/colors"], + function(dojo, dojox, lang, Color, colors){ + +var cx = lang.getObject("dojox.color", true); +/*===== cx = dojox.color =====*/ + +// alias all the dojo.Color mechanisms +cx.Color=Color; +cx.blend=Color.blendColors; +cx.fromRgb=Color.fromRgb; +cx.fromHex=Color.fromHex; +cx.fromArray=Color.fromArray; +cx.fromString=Color.fromString; + +// alias the dojo.colors mechanisms +cx.greyscale=colors.makeGrey; + +lang.mixin(cx,{ + fromCmy: function(/* Object|Array|int */cyan, /*int*/magenta, /*int*/yellow){ + // summary + // Create a dojox.color.Color from a CMY defined color. + // All colors should be expressed as 0-100 (percentage) + + if(lang.isArray(cyan)){ + magenta=cyan[1], yellow=cyan[2], cyan=cyan[0]; + } else if(lang.isObject(cyan)){ + magenta=cyan.m, yellow=cyan.y, cyan=cyan.c; + } + cyan/=100, magenta/=100, yellow/=100; + + var r=1-cyan, g=1-magenta, b=1-yellow; + return new Color({ r:Math.round(r*255), g:Math.round(g*255), b:Math.round(b*255) }); // dojox.color.Color + }, + + fromCmyk: function(/* Object|Array|int */cyan, /*int*/magenta, /*int*/yellow, /*int*/black){ + // summary + // Create a dojox.color.Color from a CMYK defined color. + // All colors should be expressed as 0-100 (percentage) + + if(lang.isArray(cyan)){ + magenta=cyan[1], yellow=cyan[2], black=cyan[3], cyan=cyan[0]; + } else if(lang.isObject(cyan)){ + magenta=cyan.m, yellow=cyan.y, black=cyan.b, cyan=cyan.c; + } + cyan/=100, magenta/=100, yellow/=100, black/=100; + var r,g,b; + r = 1-Math.min(1, cyan*(1-black)+black); + g = 1-Math.min(1, magenta*(1-black)+black); + b = 1-Math.min(1, yellow*(1-black)+black); + return new Color({ r:Math.round(r*255), g:Math.round(g*255), b:Math.round(b*255) }); // dojox.color.Color + }, + + fromHsl: function(/* Object|Array|int */hue, /* int */saturation, /* int */luminosity){ + // summary + // Create a dojox.color.Color from an HSL defined color. + // hue from 0-359 (degrees), saturation and luminosity 0-100. + + if(lang.isArray(hue)){ + saturation=hue[1], luminosity=hue[2], hue=hue[0]; + } else if(lang.isObject(hue)){ + saturation=hue.s, luminosity=hue.l, hue=hue.h; + } + saturation/=100; + luminosity/=100; + + while(hue<0){ hue+=360; } + while(hue>=360){ hue-=360; } + + var r, g, b; + if(hue<120){ + r=(120-hue)/60, g=hue/60, b=0; + } else if (hue<240){ + r=0, g=(240-hue)/60, b=(hue-120)/60; + } else { + r=(hue-240)/60, g=0, b=(360-hue)/60; + } + + r=2*saturation*Math.min(r, 1)+(1-saturation); + g=2*saturation*Math.min(g, 1)+(1-saturation); + b=2*saturation*Math.min(b, 1)+(1-saturation); + if(luminosity<0.5){ + r*=luminosity, g*=luminosity, b*=luminosity; + }else{ + r=(1-luminosity)*r+2*luminosity-1; + g=(1-luminosity)*g+2*luminosity-1; + b=(1-luminosity)*b+2*luminosity-1; + } + return new Color({ r:Math.round(r*255), g:Math.round(g*255), b:Math.round(b*255) }); // dojox.color.Color + } +}); + +cx.fromHsv = function(/* Object|Array|int */hue, /* int */saturation, /* int */value){ + // summary + // Create a dojox.color.Color from an HSV defined color. + // hue from 0-359 (degrees), saturation and value 0-100. + + if(lang.isArray(hue)){ + saturation=hue[1], value=hue[2], hue=hue[0]; + } else if (lang.isObject(hue)){ + saturation=hue.s, value=hue.v, hue=hue.h; + } + + if(hue==360){ hue=0; } + saturation/=100; + value/=100; + + var r, g, b; + if(saturation==0){ + r=value, b=value, g=value; + }else{ + var hTemp=hue/60, i=Math.floor(hTemp), f=hTemp-i; + var p=value*(1-saturation); + var q=value*(1-(saturation*f)); + var t=value*(1-(saturation*(1-f))); + switch(i){ + case 0:{ r=value, g=t, b=p; break; } + case 1:{ r=q, g=value, b=p; break; } + case 2:{ r=p, g=value, b=t; break; } + case 3:{ r=p, g=q, b=value; break; } + case 4:{ r=t, g=p, b=value; break; } + case 5:{ r=value, g=p, b=q; break; } + } + } + return new Color({ r:Math.round(r*255), g:Math.round(g*255), b:Math.round(b*255) }); // dojox.color.Color +}; +lang.extend(Color,{ + toCmy: function(){ + // summary + // Convert this Color to a CMY definition. + var cyan=1-(this.r/255), magenta=1-(this.g/255), yellow=1-(this.b/255); + return { c:Math.round(cyan*100), m:Math.round(magenta*100), y:Math.round(yellow*100) }; // Object + }, + + toCmyk: function(){ + // summary + // Convert this Color to a CMYK definition. + var cyan, magenta, yellow, black; + var r=this.r/255, g=this.g/255, b=this.b/255; + black = Math.min(1-r, 1-g, 1-b); + cyan = (1-r-black)/(1-black); + magenta = (1-g-black)/(1-black); + yellow = (1-b-black)/(1-black); + return { c:Math.round(cyan*100), m:Math.round(magenta*100), y:Math.round(yellow*100), b:Math.round(black*100) }; // Object + }, + + toHsl: function(){ + // summary + // Convert this Color to an HSL definition. + var r=this.r/255, g=this.g/255, b=this.b/255; + var min = Math.min(r, b, g), max = Math.max(r, g, b); + var delta = max-min; + var h=0, s=0, l=(min+max)/2; + if(l>0 && l<1){ + s = delta/((l<0.5)?(2*l):(2-2*l)); + } + if(delta>0){ + if(max==r && max!=g){ + h+=(g-b)/delta; + } + if(max==g && max!=b){ + h+=(2+(b-r)/delta); + } + if(max==b && max!=r){ + h+=(4+(r-g)/delta); + } + h*=60; + } + return { h:h, s:Math.round(s*100), l:Math.round(l*100) }; // Object + }, + + toHsv: function(){ + // summary + // Convert this Color to an HSV definition. + var r=this.r/255, g=this.g/255, b=this.b/255; + var min = Math.min(r, b, g), max = Math.max(r, g, b); + var delta = max-min; + var h = null, s = (max==0)?0:(delta/max); + if(s==0){ + h = 0; + }else{ + if(r==max){ + h = 60*(g-b)/delta; + }else if(g==max){ + h = 120 + 60*(b-r)/delta; + }else{ + h = 240 + 60*(r-g)/delta; + } + + if(h<0){ h+=360; } + } + return { h:h, s:Math.round(s*100), v:Math.round(max*100) }; // Object + } +}); + +return cx; +}); + +}, +'dojox/charting/plot2d/OHLC':function(){ +define(["dojo/_base/lang", "dojo/_base/array", "dojo/_base/declare", "./Base", "./common", + "dojox/lang/functional", "dojox/lang/functional/reversed", "dojox/lang/utils", "dojox/gfx/fx"], + function(lang, arr, declare, Base, dc, df, dfr, du, fx){ +/*===== +var Base = dojox.charting.plot2d.Base; +=====*/ + + var purgeGroup = dfr.lambda("item.purgeGroup()"); + + // Candlesticks are based on the Bars plot type; we expect the following passed + // as values in a series: + // { x?, open, close, high, low } + // if x is not provided, the array index is used. + // failing to provide the OHLC values will throw an error. + return declare("dojox.charting.plot2d.OHLC", Base, { + // summary: + // A plot that represents typical open/high/low/close (financial reporting, primarily). + // Unlike most charts, the Candlestick expects data points to be represented by + // an object of the form { x?, open, close, high, low, mid? }, where both + // x and mid are optional parameters. If x is not provided, the index of the + // data array is used. + defaultParams: { + hAxis: "x", // use a horizontal axis named "x" + vAxis: "y", // use a vertical axis named "y" + gap: 2, // gap between columns in pixels + animate: null // animate chart to place + }, + optionalParams: { + minBarSize: 1, // minimal bar size in pixels + maxBarSize: 1, // maximal bar size in pixels + // theme component + stroke: {}, + outline: {}, + shadow: {}, + fill: {}, + font: "", + fontColor: "" + }, + + constructor: function(chart, kwArgs){ + // summary: + // The constructor for a candlestick chart. + // chart: dojox.charting.Chart + // The chart this plot belongs to. + // kwArgs: dojox.charting.plot2d.__BarCtorArgs? + // An optional keyword arguments object to help define the plot. + this.opt = lang.clone(this.defaultParams); + du.updateWithObject(this.opt, kwArgs); + du.updateWithPattern(this.opt, kwArgs, this.optionalParams); + this.series = []; + this.hAxis = this.opt.hAxis; + this.vAxis = this.opt.vAxis; + this.animate = this.opt.animate; + }, + + collectStats: function(series){ + // summary: + // Collect all statistics for drawing this chart. Since the common + // functionality only assumes x and y, OHLC must create it's own + // stats (since data has no y value, but open/close/high/low instead). + // series: dojox.charting.Series[] + // The data series array to be drawn on this plot. + // returns: Object + // Returns an object in the form of { hmin, hmax, vmin, vmax }. + + // we have to roll our own, since we need to use all four passed + // values to figure out our stats, and common only assumes x and y. + var stats = lang.delegate(dc.defaultStats); + for(var i=0; i<series.length; i++){ + var run = series[i]; + if(!run.data.length){ continue; } + var old_vmin = stats.vmin, old_vmax = stats.vmax; + if(!("ymin" in run) || !("ymax" in run)){ + arr.forEach(run.data, function(val, idx){ + if(val !== null){ + var x = val.x || idx + 1; + stats.hmin = Math.min(stats.hmin, x); + stats.hmax = Math.max(stats.hmax, x); + stats.vmin = Math.min(stats.vmin, val.open, val.close, val.high, val.low); + stats.vmax = Math.max(stats.vmax, val.open, val.close, val.high, val.low); + } + }); + } + if("ymin" in run){ stats.vmin = Math.min(old_vmin, run.ymin); } + if("ymax" in run){ stats.vmax = Math.max(old_vmax, run.ymax); } + } + return stats; + }, + + getSeriesStats: function(){ + // summary: + // Calculate the min/max on all attached series in both directions. + // returns: Object + // {hmin, hmax, vmin, vmax} min/max in both directions. + var stats = this.collectStats(this.series); + stats.hmin -= 0.5; + stats.hmax += 0.5; + return stats; + }, + + render: function(dim, offsets){ + // summary: + // Run the calculations for any axes for this plot. + // dim: Object + // An object in the form of { width, height } + // offsets: Object + // An object of the form { l, r, t, b}. + // returns: dojox.charting.plot2d.OHLC + // A reference to this plot for functional chaining. + if(this.zoom && !this.isDataDirty()){ + return this.performZoom(dim, offsets); + } + this.resetEvents(); + this.dirty = this.isDirty(); + if(this.dirty){ + arr.forEach(this.series, purgeGroup); + this._eventSeries = {}; + this.cleanGroup(); + var s = this.group; + df.forEachRev(this.series, function(item){ item.cleanGroup(s); }); + } + var t = this.chart.theme, f, gap, width, + ht = this._hScaler.scaler.getTransformerFromModel(this._hScaler), + vt = this._vScaler.scaler.getTransformerFromModel(this._vScaler), + baseline = Math.max(0, this._vScaler.bounds.lower), + baselineHeight = vt(baseline), + events = this.events(); + f = dc.calculateBarSize(this._hScaler.bounds.scale, this.opt); + gap = f.gap; + width = f.size; + for(var i = this.series.length - 1; i >= 0; --i){ + var run = this.series[i]; + if(!this.dirty && !run.dirty){ + t.skip(); + this._reconnectEvents(run.name); + continue; + } + run.cleanGroup(); + var theme = t.next("candlestick", [this.opt, run]), s = run.group, + eventSeries = new Array(run.data.length); + for(var j = 0; j < run.data.length; ++j){ + var v = run.data[j]; + if(v !== null){ + var finalTheme = t.addMixin(theme, "candlestick", v, true); + + // calculate the points we need for OHLC + var x = ht(v.x || (j+0.5)) + offsets.l + gap, + y = dim.height - offsets.b, + open = vt(v.open), + close = vt(v.close), + high = vt(v.high), + low = vt(v.low); + if(low > high){ + var tmp = high; + high = low; + low = tmp; + } + + if(width >= 1){ + var hl = {x1: width/2, x2: width/2, y1: y - high, y2: y - low}, + op = {x1: 0, x2: ((width/2) + ((finalTheme.series.stroke.width||1)/2)), y1: y-open, y2: y-open}, + cl = {x1: ((width/2) - ((finalTheme.series.stroke.width||1)/2)), x2: width, y1: y-close, y2: y-close}; + var shape = s.createGroup(); + shape.setTransform({dx: x, dy: 0}); + var inner = shape.createGroup(); + inner.createLine(hl).setStroke(finalTheme.series.stroke); + inner.createLine(op).setStroke(finalTheme.series.stroke); + inner.createLine(cl).setStroke(finalTheme.series.stroke); + + // TODO: double check this. + run.dyn.stroke = finalTheme.series.stroke; + if(events){ + var o = { + element: "candlestick", + index: j, + run: run, + shape: inner, + x: x, + y: y-Math.max(open, close), + cx: width/2, + cy: (y-Math.max(open, close)) + (Math.max(open > close ? open-close : close-open, 1)/2), + width: width, + height: Math.max(open > close ? open-close : close-open, 1), + data: v + }; + this._connectEvents(o); + eventSeries[j] = o; + } + } + if(this.animate){ + this._animateOHLC(shape, y - low, high - low); + } + } + } + this._eventSeries[run.name] = eventSeries; + run.dirty = false; + } + this.dirty = false; + return this; // dojox.charting.plot2d.OHLC + }, + _animateOHLC: function(shape, voffset, vsize){ + fx.animateTransform(lang.delegate({ + shape: shape, + duration: 1200, + transform: [ + {name: "translate", start: [0, voffset - (voffset/vsize)], end: [0, 0]}, + {name: "scale", start: [1, 1/vsize], end: [1, 1]}, + {name: "original"} + ] + }, this.animate)).play(); + } + }); +}); + +}, +'dojox/charting/plot2d/ClusteredColumns':function(){ +define("dojox/charting/plot2d/ClusteredColumns", ["dojo/_base/array", "dojo/_base/declare", "./Columns", "./common", + "dojox/lang/functional", "dojox/lang/functional/reversed", "dojox/lang/utils"], + function(arr, declare, Columns, dc, df, dfr, du){ +/*===== +var Columns = dojox.charting.plot2d.Columns; +=====*/ + + var purgeGroup = dfr.lambda("item.purgeGroup()"); + + return declare("dojox.charting.plot2d.ClusteredColumns", Columns, { + // summary: + // A plot representing grouped or clustered columns (vertical bars). + render: function(dim, offsets){ + // summary: + // Run the calculations for any axes for this plot. + // dim: Object + // An object in the form of { width, height } + // offsets: Object + // An object of the form { l, r, t, b}. + // returns: dojox.charting.plot2d.ClusteredColumns + // A reference to this plot for functional chaining. + if(this.zoom && !this.isDataDirty()){ + return this.performZoom(dim, offsets); + } + this.resetEvents(); + this.dirty = this.isDirty(); + if(this.dirty){ + arr.forEach(this.series, purgeGroup); + this._eventSeries = {}; + this.cleanGroup(); + var s = this.group; + df.forEachRev(this.series, function(item){ item.cleanGroup(s); }); + } + var t = this.chart.theme, f, gap, width, thickness, + ht = this._hScaler.scaler.getTransformerFromModel(this._hScaler), + vt = this._vScaler.scaler.getTransformerFromModel(this._vScaler), + baseline = Math.max(0, this._vScaler.bounds.lower), + baselineHeight = vt(baseline), + events = this.events(); + f = dc.calculateBarSize(this._hScaler.bounds.scale, this.opt, this.series.length); + gap = f.gap; + width = thickness = f.size; + for(var i = 0; i < this.series.length; ++i){ + var run = this.series[i], shift = thickness * i; + if(!this.dirty && !run.dirty){ + t.skip(); + this._reconnectEvents(run.name); + continue; + } + run.cleanGroup(); + var theme = t.next("column", [this.opt, run]), s = run.group, + eventSeries = new Array(run.data.length); + for(var j = 0; j < run.data.length; ++j){ + var value = run.data[j]; + if(value !== null){ + var v = typeof value == "number" ? value : value.y, + vv = vt(v), + height = vv - baselineHeight, + h = Math.abs(height), + finalTheme = typeof value != "number" ? + t.addMixin(theme, "column", value, true) : + t.post(theme, "column"); + if(width >= 1 && h >= 0){ + var rect = { + x: offsets.l + ht(j + 0.5) + gap + shift, + y: dim.height - offsets.b - (v > baseline ? vv : baselineHeight), + width: width, height: h + }; + var specialFill = this._plotFill(finalTheme.series.fill, dim, offsets); + specialFill = this._shapeFill(specialFill, rect); + var shape = s.createRect(rect).setFill(specialFill).setStroke(finalTheme.series.stroke); + run.dyn.fill = shape.getFill(); + run.dyn.stroke = shape.getStroke(); + if(events){ + var o = { + element: "column", + index: j, + run: run, + shape: shape, + x: j + 0.5, + y: v + }; + this._connectEvents(o); + eventSeries[j] = o; + } + if(this.animate){ + this._animateColumn(shape, dim.height - offsets.b - baselineHeight, h); + } + } + } + } + this._eventSeries[run.name] = eventSeries; + run.dirty = false; + } + this.dirty = false; + return this; // dojox.charting.plot2d.ClusteredColumns + } + }); +}); + +}, +'dojox/charting/Chart':function(){ +define("dojox/charting/Chart", ["dojo/_base/lang", "dojo/_base/array","dojo/_base/declare", "dojo/_base/html", + "dojo/dom", "dojo/dom-geometry", "dojo/dom-construct","dojo/_base/Color", "dojo/_base/sniff", + "./Element", "./Theme", "./Series", "./axis2d/common", + "dojox/gfx", "dojox/lang/functional", "dojox/lang/functional/fold", "dojox/lang/functional/reversed"], + function(lang, arr, declare, html, + dom, domGeom, domConstruct, Color, has, + Element, Theme, Series, common, + g, func, funcFold, funcReversed){ + /*===== + dojox.charting.__ChartCtorArgs = function(margins, stroke, fill, delayInMs){ + // summary: + // The keyword arguments that can be passed in a Chart constructor. + // + // margins: Object? + // Optional margins for the chart, in the form of { l, t, r, b}. + // stroke: dojox.gfx.Stroke? + // An optional outline/stroke for the chart. + // fill: dojox.gfx.Fill? + // An optional fill for the chart. + // delayInMs: Number + // Delay in ms for delayedRender(). Default: 200. + this.margins = margins; + this.stroke = stroke; + this.fill = fill; + this.delayInMs = delayInMs; + } + =====*/ + var dc = dojox.charting, + clear = func.lambda("item.clear()"), + purge = func.lambda("item.purgeGroup()"), + destroy = func.lambda("item.destroy()"), + makeClean = func.lambda("item.dirty = false"), + makeDirty = func.lambda("item.dirty = true"), + getName = func.lambda("item.name"); + + declare("dojox.charting.Chart", null, { + // summary: + // The main chart object in dojox.charting. This will create a two dimensional + // chart based on dojox.gfx. + // + // description: + // dojox.charting.Chart is the primary object used for any kind of charts. It + // is simple to create--just pass it a node reference, which is used as the + // container for the chart--and a set of optional keyword arguments and go. + // + // Note that like most of dojox.gfx, most of dojox.charting.Chart's methods are + // designed to return a reference to the chart itself, to allow for functional + // chaining. This makes defining everything on a Chart very easy to do. + // + // example: + // Create an area chart, with smoothing. + // | new dojox.charting.Chart(node)) + // | .addPlot("default", { type: "Areas", tension: "X" }) + // | .setTheme(dojox.charting.themes.Shrooms) + // | .addSeries("Series A", [1, 2, 0.5, 1.5, 1, 2.8, 0.4]) + // | .addSeries("Series B", [2.6, 1.8, 2, 1, 1.4, 0.7, 2]) + // | .addSeries("Series C", [6.3, 1.8, 3, 0.5, 4.4, 2.7, 2]) + // | .render(); + // + // example: + // The form of data in a data series can take a number of forms: a simple array, + // an array of objects {x,y}, or something custom (as determined by the plot). + // Here's an example of a Candlestick chart, which expects an object of + // { open, high, low, close }. + // | new dojox.charting.Chart(node)) + // | .addPlot("default", {type: "Candlesticks", gap: 1}) + // | .addAxis("x", {fixLower: "major", fixUpper: "major", includeZero: true}) + // | .addAxis("y", {vertical: true, fixLower: "major", fixUpper: "major", natural: true}) + // | .addSeries("Series A", [ + // | { open: 20, close: 16, high: 22, low: 8 }, + // | { open: 16, close: 22, high: 26, low: 6, mid: 18 }, + // | { open: 22, close: 18, high: 22, low: 11, mid: 21 }, + // | { open: 18, close: 29, high: 32, low: 14, mid: 27 }, + // | { open: 29, close: 24, high: 29, low: 13, mid: 27 }, + // | { open: 24, close: 8, high: 24, low: 5 }, + // | { open: 8, close: 16, high: 22, low: 2 }, + // | { open: 16, close: 12, high: 19, low: 7 }, + // | { open: 12, close: 20, high: 22, low: 8 }, + // | { open: 20, close: 16, high: 22, low: 8 }, + // | { open: 16, close: 22, high: 26, low: 6, mid: 18 }, + // | { open: 22, close: 18, high: 22, low: 11, mid: 21 }, + // | { open: 18, close: 29, high: 32, low: 14, mid: 27 }, + // | { open: 29, close: 24, high: 29, low: 13, mid: 27 }, + // | { open: 24, close: 8, high: 24, low: 5 }, + // | { open: 8, close: 16, high: 22, low: 2 }, + // | { open: 16, close: 12, high: 19, low: 7 }, + // | { open: 12, close: 20, high: 22, low: 8 }, + // | { open: 20, close: 16, high: 22, low: 8 }, + // | { open: 16, close: 22, high: 26, low: 6 }, + // | { open: 22, close: 18, high: 22, low: 11 }, + // | { open: 18, close: 29, high: 32, low: 14 }, + // | { open: 29, close: 24, high: 29, low: 13 }, + // | { open: 24, close: 8, high: 24, low: 5 }, + // | { open: 8, close: 16, high: 22, low: 2 }, + // | { open: 16, close: 12, high: 19, low: 7 }, + // | { open: 12, close: 20, high: 22, low: 8 }, + // | { open: 20, close: 16, high: 22, low: 8 } + // | ], + // | { stroke: { color: "green" }, fill: "lightgreen" } + // | ) + // | .render(); + + // theme: dojox.charting.Theme? + // An optional theme to use for styling the chart. + // axes: dojox.charting.Axis{}? + // A map of axes for use in plotting a chart. + // stack: dojox.charting.plot2d.Base[] + // A stack of plotters. + // plots: dojox.charting.plot2d.Base{} + // A map of plotter indices + // series: dojox.charting.Series[] + // The stack of data runs used to create plots. + // runs: dojox.charting.Series{} + // A map of series indices + // margins: Object? + // The margins around the chart. Default is { l:10, t:10, r:10, b:10 }. + // stroke: dojox.gfx.Stroke? + // The outline of the chart (stroke in vector graphics terms). + // fill: dojox.gfx.Fill? + // The color for the chart. + // node: DOMNode + // The container node passed to the constructor. + // surface: dojox.gfx.Surface + // The main graphics surface upon which a chart is drawn. + // dirty: Boolean + // A boolean flag indicating whether or not the chart needs to be updated/re-rendered. + // coords: Object + // The coordinates on a page of the containing node, as returned from dojo.coords. + + constructor: function(/* DOMNode */node, /* dojox.charting.__ChartCtorArgs? */kwArgs){ + // summary: + // The constructor for a new Chart. Initializes all parameters used for a chart. + // returns: dojox.charting.Chart + // The newly created chart. + + // initialize parameters + if(!kwArgs){ kwArgs = {}; } + this.margins = kwArgs.margins ? kwArgs.margins : {l: 10, t: 10, r: 10, b: 10}; + this.stroke = kwArgs.stroke; + this.fill = kwArgs.fill; + this.delayInMs = kwArgs.delayInMs || 200; + this.title = kwArgs.title; + this.titleGap = kwArgs.titleGap; + this.titlePos = kwArgs.titlePos; + this.titleFont = kwArgs.titleFont; + this.titleFontColor = kwArgs.titleFontColor; + this.chartTitle = null; + + // default initialization + this.theme = null; + this.axes = {}; // map of axes + this.stack = []; // stack of plotters + this.plots = {}; // map of plotter indices + this.series = []; // stack of data runs + this.runs = {}; // map of data run indices + this.dirty = true; + this.coords = null; + + // create a surface + this.node = dom.byId(node); + var box = domGeom.getMarginBox(node); + this.surface = g.createSurface(this.node, box.w || 400, box.h || 300); + }, + destroy: function(){ + // summary: + // Cleanup when a chart is to be destroyed. + // returns: void + arr.forEach(this.series, destroy); + arr.forEach(this.stack, destroy); + func.forIn(this.axes, destroy); + if(this.chartTitle && this.chartTitle.tagName){ + // destroy title if it is a DOM node + domConstruct.destroy(this.chartTitle); + } + this.surface.destroy(); + }, + getCoords: function(){ + // summary: + // Get the coordinates and dimensions of the containing DOMNode, as + // returned by dojo.coords. + // returns: Object + // The resulting coordinates of the chart. See dojo.coords for details. + return html.coords(this.node, true); // Object + }, + setTheme: function(theme){ + // summary: + // Set a theme of the chart. + // theme: dojox.charting.Theme + // The theme to be used for visual rendering. + // returns: dojox.charting.Chart + // A reference to the current chart for functional chaining. + this.theme = theme.clone(); + this.dirty = true; + return this; // dojox.charting.Chart + }, + addAxis: function(name, kwArgs){ + // summary: + // Add an axis to the chart, for rendering. + // name: String + // The name of the axis. + // kwArgs: dojox.charting.axis2d.__AxisCtorArgs? + // An optional keyword arguments object for use in defining details of an axis. + // returns: dojox.charting.Chart + // A reference to the current chart for functional chaining. + var axis, axisType = kwArgs && kwArgs.type || "Default"; + if(typeof axisType == "string"){ + if(!dc.axis2d || !dc.axis2d[axisType]){ + throw Error("Can't find axis: " + axisType + " - Check " + "require() dependencies."); + } + axis = new dc.axis2d[axisType](this, kwArgs); + }else{ + axis = new axisType(this, kwArgs); + } + axis.name = name; + axis.dirty = true; + if(name in this.axes){ + this.axes[name].destroy(); + } + this.axes[name] = axis; + this.dirty = true; + return this; // dojox.charting.Chart + }, + getAxis: function(name){ + // summary: + // Get the given axis, by name. + // name: String + // The name the axis was defined by. + // returns: dojox.charting.axis2d.Default + // The axis as stored in the chart's axis map. + return this.axes[name]; // dojox.charting.axis2d.Default + }, + removeAxis: function(name){ + // summary: + // Remove the axis that was defined using name. + // name: String + // The axis name, as defined in addAxis. + // returns: dojox.charting.Chart + // A reference to the current chart for functional chaining. + if(name in this.axes){ + // destroy the axis + this.axes[name].destroy(); + delete this.axes[name]; + // mark the chart as dirty + this.dirty = true; + } + return this; // dojox.charting.Chart + }, + addPlot: function(name, kwArgs){ + // summary: + // Add a new plot to the chart, defined by name and using the optional keyword arguments object. + // Note that dojox.charting assumes the main plot to be called "default"; if you do not have + // a plot called "default" and attempt to add data series to the chart without specifying the + // plot to be rendered on, you WILL get errors. + // name: String + // The name of the plot to be added to the chart. If you only plan on using one plot, call it "default". + // kwArgs: dojox.charting.plot2d.__PlotCtorArgs + // An object with optional parameters for the plot in question. + // returns: dojox.charting.Chart + // A reference to the current chart for functional chaining. + var plot, plotType = kwArgs && kwArgs.type || "Default"; + if(typeof plotType == "string"){ + if(!dc.plot2d || !dc.plot2d[plotType]){ + throw Error("Can't find plot: " + plotType + " - didn't you forget to dojo" + ".require() it?"); + } + plot = new dc.plot2d[plotType](this, kwArgs); + }else{ + plot = new plotType(this, kwArgs); + } + plot.name = name; + plot.dirty = true; + if(name in this.plots){ + this.stack[this.plots[name]].destroy(); + this.stack[this.plots[name]] = plot; + }else{ + this.plots[name] = this.stack.length; + this.stack.push(plot); + } + this.dirty = true; + return this; // dojox.charting.Chart + }, + getPlot: function(name){ + // summary: + // Get the given plot, by name. + // name: String + // The name the plot was defined by. + // returns: dojox.charting.plot2d.Base + // The plot. + return this.stack[this.plots[name]]; + }, + removePlot: function(name){ + // summary: + // Remove the plot defined using name from the chart's plot stack. + // name: String + // The name of the plot as defined using addPlot. + // returns: dojox.charting.Chart + // A reference to the current chart for functional chaining. + if(name in this.plots){ + // get the index and remove the name + var index = this.plots[name]; + delete this.plots[name]; + // destroy the plot + this.stack[index].destroy(); + // remove the plot from the stack + this.stack.splice(index, 1); + // update indices to reflect the shift + func.forIn(this.plots, function(idx, name, plots){ + if(idx > index){ + plots[name] = idx - 1; + } + }); + // remove all related series + var ns = arr.filter(this.series, function(run){ return run.plot != name; }); + if(ns.length < this.series.length){ + // kill all removed series + arr.forEach(this.series, function(run){ + if(run.plot == name){ + run.destroy(); + } + }); + // rebuild all necessary data structures + this.runs = {}; + arr.forEach(ns, function(run, index){ + this.runs[run.plot] = index; + }, this); + this.series = ns; + } + // mark the chart as dirty + this.dirty = true; + } + return this; // dojox.charting.Chart + }, + getPlotOrder: function(){ + // summary: + // Returns an array of plot names in the current order + // (the top-most plot is the first). + // returns: Array + return func.map(this.stack, getName); // Array + }, + setPlotOrder: function(newOrder){ + // summary: + // Sets new order of plots. newOrder cannot add or remove + // plots. Wrong names, or dups are ignored. + // newOrder: Array: + // Array of plot names compatible with getPlotOrder(). + // returns: dojox.charting.Chart + // A reference to the current chart for functional chaining. + var names = {}, + order = func.filter(newOrder, function(name){ + if(!(name in this.plots) || (name in names)){ + return false; + } + names[name] = 1; + return true; + }, this); + if(order.length < this.stack.length){ + func.forEach(this.stack, function(plot){ + var name = plot.name; + if(!(name in names)){ + order.push(name); + } + }); + } + var newStack = func.map(order, function(name){ + return this.stack[this.plots[name]]; + }, this); + func.forEach(newStack, function(plot, i){ + this.plots[plot.name] = i; + }, this); + this.stack = newStack; + this.dirty = true; + return this; // dojox.charting.Chart + }, + movePlotToFront: function(name){ + // summary: + // Moves a given plot to front. + // name: String: + // Plot's name to move. + // returns: dojox.charting.Chart + // A reference to the current chart for functional chaining. + if(name in this.plots){ + var index = this.plots[name]; + if(index){ + var newOrder = this.getPlotOrder(); + newOrder.splice(index, 1); + newOrder.unshift(name); + return this.setPlotOrder(newOrder); // dojox.charting.Chart + } + } + return this; // dojox.charting.Chart + }, + movePlotToBack: function(name){ + // summary: + // Moves a given plot to back. + // name: String: + // Plot's name to move. + // returns: dojox.charting.Chart + // A reference to the current chart for functional chaining. + if(name in this.plots){ + var index = this.plots[name]; + if(index < this.stack.length - 1){ + var newOrder = this.getPlotOrder(); + newOrder.splice(index, 1); + newOrder.push(name); + return this.setPlotOrder(newOrder); // dojox.charting.Chart + } + } + return this; // dojox.charting.Chart + }, + addSeries: function(name, data, kwArgs){ + // summary: + // Add a data series to the chart for rendering. + // name: String: + // The name of the data series to be plotted. + // data: Array|Object: + // The array of data points (either numbers or objects) that + // represents the data to be drawn. Or it can be an object. In + // the latter case, it should have a property "data" (an array), + // destroy(), and setSeriesObject(). + // kwArgs: dojox.charting.__SeriesCtorArgs?: + // An optional keyword arguments object that will be mixed into + // the resultant series object. + // returns: dojox.charting.Chart: + // A reference to the current chart for functional chaining. + var run = new Series(this, data, kwArgs); + run.name = name; + if(name in this.runs){ + this.series[this.runs[name]].destroy(); + this.series[this.runs[name]] = run; + }else{ + this.runs[name] = this.series.length; + this.series.push(run); + } + this.dirty = true; + // fix min/max + if(!("ymin" in run) && "min" in run){ run.ymin = run.min; } + if(!("ymax" in run) && "max" in run){ run.ymax = run.max; } + return this; // dojox.charting.Chart + }, + getSeries: function(name){ + // summary: + // Get the given series, by name. + // name: String + // The name the series was defined by. + // returns: dojox.charting.Series + // The series. + return this.series[this.runs[name]]; + }, + removeSeries: function(name){ + // summary: + // Remove the series defined by name from the chart. + // name: String + // The name of the series as defined by addSeries. + // returns: dojox.charting.Chart + // A reference to the current chart for functional chaining. + if(name in this.runs){ + // get the index and remove the name + var index = this.runs[name]; + delete this.runs[name]; + // destroy the run + this.series[index].destroy(); + // remove the run from the stack of series + this.series.splice(index, 1); + // update indices to reflect the shift + func.forIn(this.runs, function(idx, name, runs){ + if(idx > index){ + runs[name] = idx - 1; + } + }); + this.dirty = true; + } + return this; // dojox.charting.Chart + }, + updateSeries: function(name, data){ + // summary: + // Update the given series with a new set of data points. + // name: String + // The name of the series as defined in addSeries. + // data: Array|Object: + // The array of data points (either numbers or objects) that + // represents the data to be drawn. Or it can be an object. In + // the latter case, it should have a property "data" (an array), + // destroy(), and setSeriesObject(). + // returns: dojox.charting.Chart + // A reference to the current chart for functional chaining. + if(name in this.runs){ + var run = this.series[this.runs[name]]; + run.update(data); + this._invalidateDependentPlots(run.plot, false); + this._invalidateDependentPlots(run.plot, true); + } + return this; // dojox.charting.Chart + }, + getSeriesOrder: function(plotName){ + // summary: + // Returns an array of series names in the current order + // (the top-most series is the first) within a plot. + // plotName: String: + // Plot's name. + // returns: Array + return func.map(func.filter(this.series, function(run){ + return run.plot == plotName; + }), getName); + }, + setSeriesOrder: function(newOrder){ + // summary: + // Sets new order of series within a plot. newOrder cannot add + // or remove series. Wrong names, or dups are ignored. + // newOrder: Array: + // Array of series names compatible with getPlotOrder(). All + // series should belong to the same plot. + // returns: dojox.charting.Chart + // A reference to the current chart for functional chaining. + var plotName, names = {}, + order = func.filter(newOrder, function(name){ + if(!(name in this.runs) || (name in names)){ + return false; + } + var run = this.series[this.runs[name]]; + if(plotName){ + if(run.plot != plotName){ + return false; + } + }else{ + plotName = run.plot; + } + names[name] = 1; + return true; + }, this); + func.forEach(this.series, function(run){ + var name = run.name; + if(!(name in names) && run.plot == plotName){ + order.push(name); + } + }); + var newSeries = func.map(order, function(name){ + return this.series[this.runs[name]]; + }, this); + this.series = newSeries.concat(func.filter(this.series, function(run){ + return run.plot != plotName; + })); + func.forEach(this.series, function(run, i){ + this.runs[run.name] = i; + }, this); + this.dirty = true; + return this; // dojox.charting.Chart + }, + moveSeriesToFront: function(name){ + // summary: + // Moves a given series to front of a plot. + // name: String: + // Series' name to move. + // returns: dojox.charting.Chart + // A reference to the current chart for functional chaining. + if(name in this.runs){ + var index = this.runs[name], + newOrder = this.getSeriesOrder(this.series[index].plot); + if(name != newOrder[0]){ + newOrder.splice(index, 1); + newOrder.unshift(name); + return this.setSeriesOrder(newOrder); // dojox.charting.Chart + } + } + return this; // dojox.charting.Chart + }, + moveSeriesToBack: function(name){ + // summary: + // Moves a given series to back of a plot. + // name: String: + // Series' name to move. + // returns: dojox.charting.Chart + // A reference to the current chart for functional chaining. + if(name in this.runs){ + var index = this.runs[name], + newOrder = this.getSeriesOrder(this.series[index].plot); + if(name != newOrder[newOrder.length - 1]){ + newOrder.splice(index, 1); + newOrder.push(name); + return this.setSeriesOrder(newOrder); // dojox.charting.Chart + } + } + return this; // dojox.charting.Chart + }, + resize: function(width, height){ + // summary: + // Resize the chart to the dimensions of width and height. + // description: + // Resize the chart and its surface to the width and height dimensions. + // If no width/height or box is provided, resize the surface to the marginBox of the chart. + // width: Number + // The new width of the chart. + // height: Number + // The new height of the chart. + // returns: dojox.charting.Chart + // A reference to the current chart for functional chaining. + var box; + switch(arguments.length){ + // case 0, do not resize the div, just the surface + case 1: + // argument, override node box + box = lang.mixin({}, width); + domGeom.setMarginBox(this.node, box); + break; + case 2: + box = {w: width, h: height}; + // argument, override node box + domGeom.setMarginBox(this.node, box); + break; + } + // in all cases take back the computed box + box = domGeom.getMarginBox(this.node); + var d = this.surface.getDimensions(); + if(d.width != box.w || d.height != box.h){ + // and set it on the surface + this.surface.setDimensions(box.w, box.h); + this.dirty = true; + return this.render(); // dojox.charting.Chart + }else{ + return this; + } + }, + getGeometry: function(){ + // summary: + // Returns a map of information about all axes in a chart and what they represent + // in terms of scaling (see dojox.charting.axis2d.Default.getScaler). + // returns: Object + // An map of geometry objects, a one-to-one mapping of axes. + var ret = {}; + func.forIn(this.axes, function(axis){ + if(axis.initialized()){ + ret[axis.name] = { + name: axis.name, + vertical: axis.vertical, + scaler: axis.scaler, + ticks: axis.ticks + }; + } + }); + return ret; // Object + }, + setAxisWindow: function(name, scale, offset, zoom){ + // summary: + // Zooms an axis and all dependent plots. Can be used to zoom in 1D. + // name: String + // The name of the axis as defined by addAxis. + // scale: Number + // The scale on the target axis. + // offset: Number + // Any offest, as measured by axis tick + // zoom: Boolean|Object? + // The chart zooming animation trigger. This is null by default, + // e.g. {duration: 1200}, or just set true. + // returns: dojox.charting.Chart + // A reference to the current chart for functional chaining. + var axis = this.axes[name]; + if(axis){ + axis.setWindow(scale, offset); + arr.forEach(this.stack,function(plot){ + if(plot.hAxis == name || plot.vAxis == name){ + plot.zoom = zoom; + } + }); + } + return this; // dojox.charting.Chart + }, + setWindow: function(sx, sy, dx, dy, zoom){ + // summary: + // Zooms in or out any plots in two dimensions. + // sx: Number + // The scale for the x axis. + // sy: Number + // The scale for the y axis. + // dx: Number + // The pixel offset on the x axis. + // dy: Number + // The pixel offset on the y axis. + // zoom: Boolean|Object? + // The chart zooming animation trigger. This is null by default, + // e.g. {duration: 1200}, or just set true. + // returns: dojox.charting.Chart + // A reference to the current chart for functional chaining. + if(!("plotArea" in this)){ + this.calculateGeometry(); + } + func.forIn(this.axes, function(axis){ + var scale, offset, bounds = axis.getScaler().bounds, + s = bounds.span / (bounds.upper - bounds.lower); + if(axis.vertical){ + scale = sy; + offset = dy / s / scale; + }else{ + scale = sx; + offset = dx / s / scale; + } + axis.setWindow(scale, offset); + }); + arr.forEach(this.stack, function(plot){ plot.zoom = zoom; }); + return this; // dojox.charting.Chart + }, + zoomIn: function(name, range){ + // summary: + // Zoom the chart to a specific range on one axis. This calls render() + // directly as a convenience method. + // name: String + // The name of the axis as defined by addAxis. + // range: Array + // The end points of the zoom range, measured in axis ticks. + var axis = this.axes[name]; + if(axis){ + var scale, offset, bounds = axis.getScaler().bounds; + var lower = Math.min(range[0],range[1]); + var upper = Math.max(range[0],range[1]); + lower = range[0] < bounds.lower ? bounds.lower : lower; + upper = range[1] > bounds.upper ? bounds.upper : upper; + scale = (bounds.upper - bounds.lower) / (upper - lower); + offset = lower - bounds.lower; + this.setAxisWindow(name, scale, offset); + this.render(); + } + }, + calculateGeometry: function(){ + // summary: + // Calculate the geometry of the chart based on the defined axes of + // a chart. + // returns: dojox.charting.Chart + // A reference to the current chart for functional chaining. + if(this.dirty){ + return this.fullGeometry(); + } + + // calculate geometry + var dirty = arr.filter(this.stack, function(plot){ + return plot.dirty || + (plot.hAxis && this.axes[plot.hAxis].dirty) || + (plot.vAxis && this.axes[plot.vAxis].dirty); + }, this); + calculateAxes(dirty, this.plotArea); + + return this; // dojox.charting.Chart + }, + fullGeometry: function(){ + // summary: + // Calculate the full geometry of the chart. This includes passing + // over all major elements of a chart (plots, axes, series, container) + // in order to ensure proper rendering. + // returns: dojox.charting.Chart + // A reference to the current chart for functional chaining. + this._makeDirty(); + + // clear old values + arr.forEach(this.stack, clear); + + // rebuild new connections, and add defaults + + // set up a theme + if(!this.theme){ + this.setTheme(new Theme(dojox.charting._def)); + } + + // assign series + arr.forEach(this.series, function(run){ + if(!(run.plot in this.plots)){ + if(!dc.plot2d || !dc.plot2d.Default){ + throw Error("Can't find plot: Default - didn't you forget to dojo" + ".require() it?"); + } + var plot = new dc.plot2d.Default(this, {}); + plot.name = run.plot; + this.plots[run.plot] = this.stack.length; + this.stack.push(plot); + } + this.stack[this.plots[run.plot]].addSeries(run); + }, this); + // assign axes + arr.forEach(this.stack, function(plot){ + if(plot.hAxis){ + plot.setAxis(this.axes[plot.hAxis]); + } + if(plot.vAxis){ + plot.setAxis(this.axes[plot.vAxis]); + } + }, this); + + // calculate geometry + + // 1st pass + var dim = this.dim = this.surface.getDimensions(); + dim.width = g.normalizedLength(dim.width); + dim.height = g.normalizedLength(dim.height); + func.forIn(this.axes, clear); + calculateAxes(this.stack, dim); + + // assumption: we don't have stacked axes yet + var offsets = this.offsets = { l: 0, r: 0, t: 0, b: 0 }; + func.forIn(this.axes, function(axis){ + func.forIn(axis.getOffsets(), function(o, i){ offsets[i] += o; }); + }); + // add title area + if(this.title){ + this.titleGap = (this.titleGap==0) ? 0 : this.titleGap || this.theme.chart.titleGap || 20; + this.titlePos = this.titlePos || this.theme.chart.titlePos || "top"; + this.titleFont = this.titleFont || this.theme.chart.titleFont; + this.titleFontColor = this.titleFontColor || this.theme.chart.titleFontColor || "black"; + var tsize = g.normalizedLength(g.splitFontString(this.titleFont).size); + offsets[this.titlePos=="top" ? "t":"b"] += (tsize + this.titleGap); + } + // add margins + func.forIn(this.margins, function(o, i){ offsets[i] += o; }); + + // 2nd pass with realistic dimensions + this.plotArea = { + width: dim.width - offsets.l - offsets.r, + height: dim.height - offsets.t - offsets.b + }; + func.forIn(this.axes, clear); + calculateAxes(this.stack, this.plotArea); + + return this; // dojox.charting.Chart + }, + render: function(){ + // summary: + // Render the chart according to the current information defined. This should + // be the last call made when defining/creating a chart, or if data within the + // chart has been changed. + // returns: dojox.charting.Chart + // A reference to the current chart for functional chaining. + if(this.theme){ + this.theme.clear(); + } + + if(this.dirty){ + return this.fullRender(); + } + + this.calculateGeometry(); + + // go over the stack backwards + func.forEachRev(this.stack, function(plot){ plot.render(this.dim, this.offsets); }, this); + + // go over axes + func.forIn(this.axes, function(axis){ axis.render(this.dim, this.offsets); }, this); + + this._makeClean(); + + // BEGIN FOR HTML CANVAS + if(this.surface.render){ this.surface.render(); }; + // END FOR HTML CANVAS + + return this; // dojox.charting.Chart + }, + fullRender: function(){ + // summary: + // Force a full rendering of the chart, including full resets on the chart itself. + // You should not call this method directly unless absolutely necessary. + // returns: dojox.charting.Chart + // A reference to the current chart for functional chaining. + + // calculate geometry + this.fullGeometry(); + var offsets = this.offsets, dim = this.dim, rect; + + // get required colors + //var requiredColors = func.foldl(this.stack, "z + plot.getRequiredColors()", 0); + //this.theme.defineColors({num: requiredColors, cache: false}); + + // clear old shapes + arr.forEach(this.series, purge); + func.forIn(this.axes, purge); + arr.forEach(this.stack, purge); + if(this.chartTitle && this.chartTitle.tagName){ + // destroy title if it is a DOM node + domConstruct.destroy(this.chartTitle); + } + this.surface.clear(); + this.chartTitle = null; + + // generate shapes + + // draw a plot background + var t = this.theme, + fill = t.plotarea && t.plotarea.fill, + stroke = t.plotarea && t.plotarea.stroke, + // size might be neg if offsets are bigger that chart size this happens quite often at + // initialization time if the chart widget is used in a BorderContainer + // this will fail on IE/VML + w = Math.max(0, dim.width - offsets.l - offsets.r), + h = Math.max(0, dim.height - offsets.t - offsets.b), + rect = { + x: offsets.l - 1, y: offsets.t - 1, + width: w + 2, + height: h + 2 + }; + if(fill){ + fill = Element.prototype._shapeFill(Element.prototype._plotFill(fill, dim, offsets), rect); + this.surface.createRect(rect).setFill(fill); + } + if(stroke){ + this.surface.createRect({ + x: offsets.l, y: offsets.t, + width: w + 1, + height: h + 1 + }).setStroke(stroke); + } + + // go over the stack backwards + func.foldr(this.stack, function(z, plot){ return plot.render(dim, offsets), 0; }, 0); + + // pseudo-clipping: matting + fill = this.fill !== undefined ? this.fill : (t.chart && t.chart.fill); + stroke = this.stroke !== undefined ? this.stroke : (t.chart && t.chart.stroke); + + // TRT: support for "inherit" as a named value in a theme. + if(fill == "inherit"){ + // find the background color of the nearest ancestor node, and use that explicitly. + var node = this.node, fill = new Color(html.style(node, "backgroundColor")); + while(fill.a==0 && node!=document.documentElement){ + fill = new Color(html.style(node, "backgroundColor")); + node = node.parentNode; + } + } + + if(fill){ + fill = Element.prototype._plotFill(fill, dim, offsets); + if(offsets.l){ // left + rect = { + width: offsets.l, + height: dim.height + 1 + }; + this.surface.createRect(rect).setFill(Element.prototype._shapeFill(fill, rect)); + } + if(offsets.r){ // right + rect = { + x: dim.width - offsets.r, + width: offsets.r + 1, + height: dim.height + 2 + }; + this.surface.createRect(rect).setFill(Element.prototype._shapeFill(fill, rect)); + } + if(offsets.t){ // top + rect = { + width: dim.width + 1, + height: offsets.t + }; + this.surface.createRect(rect).setFill(Element.prototype._shapeFill(fill, rect)); + } + if(offsets.b){ // bottom + rect = { + y: dim.height - offsets.b, + width: dim.width + 1, + height: offsets.b + 2 + }; + this.surface.createRect(rect).setFill(Element.prototype._shapeFill(fill, rect)); + } + } + if(stroke){ + this.surface.createRect({ + width: dim.width - 1, + height: dim.height - 1 + }).setStroke(stroke); + } + + //create title: Whether to make chart title as a widget which extends dojox.charting.Element? + if(this.title){ + var forceHtmlLabels = (g.renderer == "canvas"), + labelType = forceHtmlLabels || !has("ie") && !has("opera") ? "html" : "gfx", + tsize = g.normalizedLength(g.splitFontString(this.titleFont).size); + this.chartTitle = common.createText[labelType]( + this, + this.surface, + dim.width/2, + this.titlePos=="top" ? tsize + this.margins.t : dim.height - this.margins.b, + "middle", + this.title, + this.titleFont, + this.titleFontColor + ); + } + + // go over axes + func.forIn(this.axes, function(axis){ axis.render(dim, offsets); }); + + this._makeClean(); + + // BEGIN FOR HTML CANVAS + if(this.surface.render){ this.surface.render(); }; + // END FOR HTML CANVAS + + return this; // dojox.charting.Chart + }, + delayedRender: function(){ + // summary: + // Delayed render, which is used to collect multiple updates + // within a delayInMs time window. + // returns: dojox.charting.Chart + // A reference to the current chart for functional chaining. + + if(!this._delayedRenderHandle){ + this._delayedRenderHandle = setTimeout( + lang.hitch(this, function(){ + clearTimeout(this._delayedRenderHandle); + this._delayedRenderHandle = null; + this.render(); + }), + this.delayInMs + ); + } + + return this; // dojox.charting.Chart + }, + connectToPlot: function(name, object, method){ + // summary: + // A convenience method to connect a function to a plot. + // name: String + // The name of the plot as defined by addPlot. + // object: Object + // The object to be connected. + // method: Function + // The function to be executed. + // returns: Array + // A handle to the connection, as defined by dojo.connect (see dojo.connect). + return name in this.plots ? this.stack[this.plots[name]].connect(object, method) : null; // Array + }, + fireEvent: function(seriesName, eventName, index){ + // summary: + // Fires a synthetic event for a series item. + // seriesName: String: + // Series name. + // eventName: String: + // Event name to simulate: onmouseover, onmouseout, onclick. + // index: Number: + // Valid data value index for the event. + // returns: dojox.charting.Chart + // A reference to the current chart for functional chaining. + if(seriesName in this.runs){ + var plotName = this.series[this.runs[seriesName]].plot; + if(plotName in this.plots){ + var plot = this.stack[this.plots[plotName]]; + if(plot){ + plot.fireEvent(seriesName, eventName, index); + } + } + } + return this; // dojox.charting.Chart + }, + _makeClean: function(){ + // reset dirty flags + arr.forEach(this.axes, makeClean); + arr.forEach(this.stack, makeClean); + arr.forEach(this.series, makeClean); + this.dirty = false; + }, + _makeDirty: function(){ + // reset dirty flags + arr.forEach(this.axes, makeDirty); + arr.forEach(this.stack, makeDirty); + arr.forEach(this.series, makeDirty); + this.dirty = true; + }, + _invalidateDependentPlots: function(plotName, /* Boolean */ verticalAxis){ + if(plotName in this.plots){ + var plot = this.stack[this.plots[plotName]], axis, + axisName = verticalAxis ? "vAxis" : "hAxis"; + if(plot[axisName]){ + axis = this.axes[plot[axisName]]; + if(axis && axis.dependOnData()){ + axis.dirty = true; + // find all plots and mark them dirty + arr.forEach(this.stack, function(p){ + if(p[axisName] && p[axisName] == plot[axisName]){ + p.dirty = true; + } + }); + } + }else{ + plot.dirty = true; + } + } + } + }); + + function hSection(stats){ + return {min: stats.hmin, max: stats.hmax}; + } + + function vSection(stats){ + return {min: stats.vmin, max: stats.vmax}; + } + + function hReplace(stats, h){ + stats.hmin = h.min; + stats.hmax = h.max; + } + + function vReplace(stats, v){ + stats.vmin = v.min; + stats.vmax = v.max; + } + + function combineStats(target, source){ + if(target && source){ + target.min = Math.min(target.min, source.min); + target.max = Math.max(target.max, source.max); + } + return target || source; + } + + function calculateAxes(stack, plotArea){ + var plots = {}, axes = {}; + arr.forEach(stack, function(plot){ + var stats = plots[plot.name] = plot.getSeriesStats(); + if(plot.hAxis){ + axes[plot.hAxis] = combineStats(axes[plot.hAxis], hSection(stats)); + } + if(plot.vAxis){ + axes[plot.vAxis] = combineStats(axes[plot.vAxis], vSection(stats)); + } + }); + arr.forEach(stack, function(plot){ + var stats = plots[plot.name]; + if(plot.hAxis){ + hReplace(stats, axes[plot.hAxis]); + } + if(plot.vAxis){ + vReplace(stats, axes[plot.vAxis]); + } + plot.initializeScalers(plotArea, stats); + }); + } + + return dojox.charting.Chart; +}); + +}, +'dojox/lang/functional/sequence':function(){ +define("dojox/lang/functional/sequence", ["dojo/_base/lang", "./lambda"], function(lang, df){ + +// This module adds high-level functions and related constructs: +// - sequence generators + +// If you want more general sequence builders check out listcomp.js and +// unfold() (in fold.js). + +// Defined methods: +// - take any valid lambda argument as the functional argument + +/*===== + var df = dojox.lang.functional; + =====*/ + + lang.mixin(df, { + // sequence generators + repeat: function(/*Number*/ n, /*Function|String|Array*/ f, /*Object*/ z, /*Object?*/ o){ + // summary: builds an array by repeatedly applying a unary function N times + // with a seed value Z. N should be greater than 0. + o = o || dojo.global; f = df.lambda(f); + var t = new Array(n), i = 1; + t[0] = z; + for(; i < n; t[i] = z = f.call(o, z), ++i); + return t; // Array + }, + until: function(/*Function|String|Array*/ pr, /*Function|String|Array*/ f, /*Object*/ z, /*Object?*/ o){ + // summary: builds an array by repeatedly applying a unary function with + // a seed value Z until the predicate is satisfied. + o = o || dojo.global; f = df.lambda(f); pr = df.lambda(pr); + var t = []; + for(; !pr.call(o, z); t.push(z), z = f.call(o, z)); + return t; // Array + } + }); + + return df; +}); + +}, +'dojox/charting/plot2d/MarkersOnly':function(){ +define("dojox/charting/plot2d/MarkersOnly", ["dojo/_base/declare", "./Default"], function(declare, Default){ +/*===== +var Default = dojox.charting.plot2d.Default; +=====*/ + return declare("dojox.charting.plot2d.MarkersOnly", Default, { + // summary: + // A convenience object to draw only markers (like a scatter but not quite). + constructor: function(){ + // summary: + // Set up our default plot to only have markers and no lines. + this.opt.lines = false; + this.opt.markers = true; + } + }); +}); + +}, +'dojox/charting/plot2d/Areas':function(){ +define("dojox/charting/plot2d/Areas", ["dojo/_base/declare", "./Default"], + function(declare, Default){ +/*===== +var Default = dojox.charting.plot2d.Default; +=====*/ + return declare("dojox.charting.plot2d.Areas", Default, { + // summary: + // Represents an area chart. See dojox.charting.plot2d.Default for details. + constructor: function(){ + this.opt.lines = true; + this.opt.areas = true; + } + }); +}); + +}, +'dojox/charting/action2d/Base':function(){ +define("dojox/charting/action2d/Base", ["dojo/_base/lang", "dojo/_base/declare"], + function(lang, declare){ + + return declare("dojox.charting.action2d.Base", null, { + // summary: + // Base action class for plot and chart actions. + + constructor: function(chart, plot){ + // summary: + // Create a new base action. This can either be a plot or a chart action. + // chart: dojox.charting.Chart + // The chart this action applies to. + // plot: String?|dojox.charting.plot2d.Base? + // Optional target plot for this action. Default is "default". + this.chart = chart; + this.plot = plot ? (lang.isString(plot) ? this.chart.getPlot(plot) : plot) : this.chart.getPlot("default"); + }, + + connect: function(){ + // summary: + // Connect this action to the plot or the chart. + }, + + disconnect: function(){ + // summary: + // Disconnect this action from the plot or the chart. + }, + + destroy: function(){ + // summary: + // Do any cleanup needed when destroying parent elements. + this.disconnect(); + } + }); + +}); + +}, +'dojo/fx':function(){ +define([ + "./_base/lang", + "./Evented", + "./_base/kernel", + "./_base/array", + "./_base/connect", + "./_base/fx", + "./dom", + "./dom-style", + "./dom-geometry", + "./ready", + "require" // for context sensitive loading of Toggler +], function(lang, Evented, dojo, arrayUtil, connect, baseFx, dom, domStyle, geom, ready, require) { + + // module: + // dojo/fx + // summary: + // TODOC + + + /*===== + dojo.fx = { + // summary: Effects library on top of Base animations + }; + var coreFx = dojo.fx; + =====*/ + +// For back-compat, remove in 2.0. +if(!dojo.isAsync){ + ready(0, function(){ + var requires = ["./fx/Toggler"]; + require(requires); // use indirection so modules not rolled into a build + }); +} + + var coreFx = dojo.fx = {}; + + var _baseObj = { + _fire: function(evt, args){ + if(this[evt]){ + this[evt].apply(this, args||[]); + } + return this; + } + }; + + var _chain = function(animations){ + this._index = -1; + this._animations = animations||[]; + this._current = this._onAnimateCtx = this._onEndCtx = null; + + this.duration = 0; + arrayUtil.forEach(this._animations, function(a){ + this.duration += a.duration; + if(a.delay){ this.duration += a.delay; } + }, this); + }; + _chain.prototype = new Evented(); + lang.extend(_chain, { + _onAnimate: function(){ + this._fire("onAnimate", arguments); + }, + _onEnd: function(){ + connect.disconnect(this._onAnimateCtx); + connect.disconnect(this._onEndCtx); + this._onAnimateCtx = this._onEndCtx = null; + if(this._index + 1 == this._animations.length){ + this._fire("onEnd"); + }else{ + // switch animations + this._current = this._animations[++this._index]; + this._onAnimateCtx = connect.connect(this._current, "onAnimate", this, "_onAnimate"); + this._onEndCtx = connect.connect(this._current, "onEnd", this, "_onEnd"); + this._current.play(0, true); + } + }, + play: function(/*int?*/ delay, /*Boolean?*/ gotoStart){ + if(!this._current){ this._current = this._animations[this._index = 0]; } + if(!gotoStart && this._current.status() == "playing"){ return this; } + var beforeBegin = connect.connect(this._current, "beforeBegin", this, function(){ + this._fire("beforeBegin"); + }), + onBegin = connect.connect(this._current, "onBegin", this, function(arg){ + this._fire("onBegin", arguments); + }), + onPlay = connect.connect(this._current, "onPlay", this, function(arg){ + this._fire("onPlay", arguments); + connect.disconnect(beforeBegin); + connect.disconnect(onBegin); + connect.disconnect(onPlay); + }); + if(this._onAnimateCtx){ + connect.disconnect(this._onAnimateCtx); + } + this._onAnimateCtx = connect.connect(this._current, "onAnimate", this, "_onAnimate"); + if(this._onEndCtx){ + connect.disconnect(this._onEndCtx); + } + this._onEndCtx = connect.connect(this._current, "onEnd", this, "_onEnd"); + this._current.play.apply(this._current, arguments); + return this; + }, + pause: function(){ + if(this._current){ + var e = connect.connect(this._current, "onPause", this, function(arg){ + this._fire("onPause", arguments); + connect.disconnect(e); + }); + this._current.pause(); + } + return this; + }, + gotoPercent: function(/*Decimal*/percent, /*Boolean?*/ andPlay){ + this.pause(); + var offset = this.duration * percent; + this._current = null; + arrayUtil.some(this._animations, function(a){ + if(a.duration <= offset){ + this._current = a; + return true; + } + offset -= a.duration; + return false; + }); + if(this._current){ + this._current.gotoPercent(offset / this._current.duration, andPlay); + } + return this; + }, + stop: function(/*boolean?*/ gotoEnd){ + if(this._current){ + if(gotoEnd){ + for(; this._index + 1 < this._animations.length; ++this._index){ + this._animations[this._index].stop(true); + } + this._current = this._animations[this._index]; + } + var e = connect.connect(this._current, "onStop", this, function(arg){ + this._fire("onStop", arguments); + connect.disconnect(e); + }); + this._current.stop(); + } + return this; + }, + status: function(){ + return this._current ? this._current.status() : "stopped"; + }, + destroy: function(){ + if(this._onAnimateCtx){ connect.disconnect(this._onAnimateCtx); } + if(this._onEndCtx){ connect.disconnect(this._onEndCtx); } + } + }); + lang.extend(_chain, _baseObj); + + coreFx.chain = /*===== dojo.fx.chain = =====*/ function(/*dojo.Animation[]*/ animations){ + // summary: + // Chain a list of `dojo.Animation`s to run in sequence + // + // description: + // Return a `dojo.Animation` which will play all passed + // `dojo.Animation` instances in sequence, firing its own + // synthesized events simulating a single animation. (eg: + // onEnd of this animation means the end of the chain, + // not the individual animations within) + // + // example: + // Once `node` is faded out, fade in `otherNode` + // | dojo.fx.chain([ + // | dojo.fadeIn({ node:node }), + // | dojo.fadeOut({ node:otherNode }) + // | ]).play(); + // + return new _chain(animations); // dojo.Animation + }; + + var _combine = function(animations){ + this._animations = animations||[]; + this._connects = []; + this._finished = 0; + + this.duration = 0; + arrayUtil.forEach(animations, function(a){ + var duration = a.duration; + if(a.delay){ duration += a.delay; } + if(this.duration < duration){ this.duration = duration; } + this._connects.push(connect.connect(a, "onEnd", this, "_onEnd")); + }, this); + + this._pseudoAnimation = new baseFx.Animation({curve: [0, 1], duration: this.duration}); + var self = this; + arrayUtil.forEach(["beforeBegin", "onBegin", "onPlay", "onAnimate", "onPause", "onStop", "onEnd"], + function(evt){ + self._connects.push(connect.connect(self._pseudoAnimation, evt, + function(){ self._fire(evt, arguments); } + )); + } + ); + }; + lang.extend(_combine, { + _doAction: function(action, args){ + arrayUtil.forEach(this._animations, function(a){ + a[action].apply(a, args); + }); + return this; + }, + _onEnd: function(){ + if(++this._finished > this._animations.length){ + this._fire("onEnd"); + } + }, + _call: function(action, args){ + var t = this._pseudoAnimation; + t[action].apply(t, args); + }, + play: function(/*int?*/ delay, /*Boolean?*/ gotoStart){ + this._finished = 0; + this._doAction("play", arguments); + this._call("play", arguments); + return this; + }, + pause: function(){ + this._doAction("pause", arguments); + this._call("pause", arguments); + return this; + }, + gotoPercent: function(/*Decimal*/percent, /*Boolean?*/ andPlay){ + var ms = this.duration * percent; + arrayUtil.forEach(this._animations, function(a){ + a.gotoPercent(a.duration < ms ? 1 : (ms / a.duration), andPlay); + }); + this._call("gotoPercent", arguments); + return this; + }, + stop: function(/*boolean?*/ gotoEnd){ + this._doAction("stop", arguments); + this._call("stop", arguments); + return this; + }, + status: function(){ + return this._pseudoAnimation.status(); + }, + destroy: function(){ + arrayUtil.forEach(this._connects, connect.disconnect); + } + }); + lang.extend(_combine, _baseObj); + + coreFx.combine = /*===== dojo.fx.combine = =====*/ function(/*dojo.Animation[]*/ animations){ + // summary: + // Combine a list of `dojo.Animation`s to run in parallel + // + // description: + // Combine an array of `dojo.Animation`s to run in parallel, + // providing a new `dojo.Animation` instance encompasing each + // animation, firing standard animation events. + // + // example: + // Fade out `node` while fading in `otherNode` simultaneously + // | dojo.fx.combine([ + // | dojo.fadeIn({ node:node }), + // | dojo.fadeOut({ node:otherNode }) + // | ]).play(); + // + // example: + // When the longest animation ends, execute a function: + // | var anim = dojo.fx.combine([ + // | dojo.fadeIn({ node: n, duration:700 }), + // | dojo.fadeOut({ node: otherNode, duration: 300 }) + // | ]); + // | dojo.connect(anim, "onEnd", function(){ + // | // overall animation is done. + // | }); + // | anim.play(); // play the animation + // + return new _combine(animations); // dojo.Animation + }; + + coreFx.wipeIn = /*===== dojo.fx.wipeIn = =====*/ function(/*Object*/ args){ + // summary: + // Expand a node to it's natural height. + // + // description: + // Returns an animation that will expand the + // node defined in 'args' object from it's current height to + // it's natural height (with no scrollbar). + // Node must have no margin/border/padding. + // + // args: Object + // A hash-map of standard `dojo.Animation` constructor properties + // (such as easing: node: duration: and so on) + // + // example: + // | dojo.fx.wipeIn({ + // | node:"someId" + // | }).play() + var node = args.node = dom.byId(args.node), s = node.style, o; + + var anim = baseFx.animateProperty(lang.mixin({ + properties: { + height: { + // wrapped in functions so we wait till the last second to query (in case value has changed) + start: function(){ + // start at current [computed] height, but use 1px rather than 0 + // because 0 causes IE to display the whole panel + o = s.overflow; + s.overflow = "hidden"; + if(s.visibility == "hidden" || s.display == "none"){ + s.height = "1px"; + s.display = ""; + s.visibility = ""; + return 1; + }else{ + var height = domStyle.get(node, "height"); + return Math.max(height, 1); + } + }, + end: function(){ + return node.scrollHeight; + } + } + } + }, args)); + + var fini = function(){ + s.height = "auto"; + s.overflow = o; + }; + connect.connect(anim, "onStop", fini); + connect.connect(anim, "onEnd", fini); + + return anim; // dojo.Animation + }; + + coreFx.wipeOut = /*===== dojo.fx.wipeOut = =====*/ function(/*Object*/ args){ + // summary: + // Shrink a node to nothing and hide it. + // + // description: + // Returns an animation that will shrink node defined in "args" + // from it's current height to 1px, and then hide it. + // + // args: Object + // A hash-map of standard `dojo.Animation` constructor properties + // (such as easing: node: duration: and so on) + // + // example: + // | dojo.fx.wipeOut({ node:"someId" }).play() + + var node = args.node = dom.byId(args.node), s = node.style, o; + + var anim = baseFx.animateProperty(lang.mixin({ + properties: { + height: { + end: 1 // 0 causes IE to display the whole panel + } + } + }, args)); + + connect.connect(anim, "beforeBegin", function(){ + o = s.overflow; + s.overflow = "hidden"; + s.display = ""; + }); + var fini = function(){ + s.overflow = o; + s.height = "auto"; + s.display = "none"; + }; + connect.connect(anim, "onStop", fini); + connect.connect(anim, "onEnd", fini); + + return anim; // dojo.Animation + }; + + coreFx.slideTo = /*===== dojo.fx.slideTo = =====*/ function(/*Object*/ args){ + // summary: + // Slide a node to a new top/left position + // + // description: + // Returns an animation that will slide "node" + // defined in args Object from its current position to + // the position defined by (args.left, args.top). + // + // args: Object + // A hash-map of standard `dojo.Animation` constructor properties + // (such as easing: node: duration: and so on). Special args members + // are `top` and `left`, which indicate the new position to slide to. + // + // example: + // | .slideTo({ node: node, left:"40", top:"50", units:"px" }).play() + + var node = args.node = dom.byId(args.node), + top = null, left = null; + + var init = (function(n){ + return function(){ + var cs = domStyle.getComputedStyle(n); + var pos = cs.position; + top = (pos == 'absolute' ? n.offsetTop : parseInt(cs.top) || 0); + left = (pos == 'absolute' ? n.offsetLeft : parseInt(cs.left) || 0); + if(pos != 'absolute' && pos != 'relative'){ + var ret = geom.position(n, true); + top = ret.y; + left = ret.x; + n.style.position="absolute"; + n.style.top=top+"px"; + n.style.left=left+"px"; + } + }; + })(node); + init(); + + var anim = baseFx.animateProperty(lang.mixin({ + properties: { + top: args.top || 0, + left: args.left || 0 + } + }, args)); + connect.connect(anim, "beforeBegin", anim, init); + + return anim; // dojo.Animation + }; + + return coreFx; +}); + +}, +'dojox/gfx/fx':function(){ +define("dojox/gfx/fx", ["dojo/_base/lang", "./_base", "./matrix", "dojo/_base/Color", "dojo/_base/array", "dojo/_base/fx", "dojo/_base/connect"], + function(lang, g, m, Color, arr, fx, Hub){ + var fxg = g.fx = {}; + /*===== g = dojox.gfx; fxg = dojox.gfx.fx; =====*/ + + // Generic interpolators. Should they be moved to dojox.fx? + + function InterpolNumber(start, end){ + this.start = start, this.end = end; + } + InterpolNumber.prototype.getValue = function(r){ + return (this.end - this.start) * r + this.start; + }; + + function InterpolUnit(start, end, units){ + this.start = start, this.end = end; + this.units = units; + } + InterpolUnit.prototype.getValue = function(r){ + return (this.end - this.start) * r + this.start + this.units; + }; + + function InterpolColor(start, end){ + this.start = start, this.end = end; + this.temp = new Color(); + } + InterpolColor.prototype.getValue = function(r){ + return Color.blendColors(this.start, this.end, r, this.temp); + }; + + function InterpolValues(values){ + this.values = values; + this.length = values.length; + } + InterpolValues.prototype.getValue = function(r){ + return this.values[Math.min(Math.floor(r * this.length), this.length - 1)]; + }; + + function InterpolObject(values, def){ + this.values = values; + this.def = def ? def : {}; + } + InterpolObject.prototype.getValue = function(r){ + var ret = lang.clone(this.def); + for(var i in this.values){ + ret[i] = this.values[i].getValue(r); + } + return ret; + }; + + function InterpolTransform(stack, original){ + this.stack = stack; + this.original = original; + } + InterpolTransform.prototype.getValue = function(r){ + var ret = []; + arr.forEach(this.stack, function(t){ + if(t instanceof m.Matrix2D){ + ret.push(t); + return; + } + if(t.name == "original" && this.original){ + ret.push(this.original); + return; + } + if(!(t.name in m)){ return; } + var f = m[t.name]; + if(typeof f != "function"){ + // constant + ret.push(f); + return; + } + var val = arr.map(t.start, function(v, i){ + return (t.end[i] - v) * r + v; + }), + matrix = f.apply(m, val); + if(matrix instanceof m.Matrix2D){ + ret.push(matrix); + } + }, this); + return ret; + }; + + var transparent = new Color(0, 0, 0, 0); + + function getColorInterpol(prop, obj, name, def){ + if(prop.values){ + return new InterpolValues(prop.values); + } + var value, start, end; + if(prop.start){ + start = g.normalizeColor(prop.start); + }else{ + start = value = obj ? (name ? obj[name] : obj) : def; + } + if(prop.end){ + end = g.normalizeColor(prop.end); + }else{ + if(!value){ + value = obj ? (name ? obj[name] : obj) : def; + } + end = value; + } + return new InterpolColor(start, end); + } + + function getNumberInterpol(prop, obj, name, def){ + if(prop.values){ + return new InterpolValues(prop.values); + } + var value, start, end; + if(prop.start){ + start = prop.start; + }else{ + start = value = obj ? obj[name] : def; + } + if(prop.end){ + end = prop.end; + }else{ + if(typeof value != "number"){ + value = obj ? obj[name] : def; + } + end = value; + } + return new InterpolNumber(start, end); + } + + fxg.animateStroke = function(/*Object*/ args){ + // summary: + // Returns an animation which will change stroke properties over time. + // example: + // | dojox.gfx.fx.animateStroke{{ + // | shape: shape, + // | duration: 500, + // | color: {start: "red", end: "green"}, + // | width: {end: 15}, + // | join: {values: ["miter", "bevel", "round"]} + // | }).play(); + if(!args.easing){ args.easing = fx._defaultEasing; } + var anim = new fx.Animation(args), shape = args.shape, stroke; + Hub.connect(anim, "beforeBegin", anim, function(){ + stroke = shape.getStroke(); + var prop = args.color, values = {}, value, start, end; + if(prop){ + values.color = getColorInterpol(prop, stroke, "color", transparent); + } + prop = args.style; + if(prop && prop.values){ + values.style = new InterpolValues(prop.values); + } + prop = args.width; + if(prop){ + values.width = getNumberInterpol(prop, stroke, "width", 1); + } + prop = args.cap; + if(prop && prop.values){ + values.cap = new InterpolValues(prop.values); + } + prop = args.join; + if(prop){ + if(prop.values){ + values.join = new InterpolValues(prop.values); + }else{ + start = prop.start ? prop.start : (stroke && stroke.join || 0); + end = prop.end ? prop.end : (stroke && stroke.join || 0); + if(typeof start == "number" && typeof end == "number"){ + values.join = new InterpolNumber(start, end); + } + } + } + this.curve = new InterpolObject(values, stroke); + }); + Hub.connect(anim, "onAnimate", shape, "setStroke"); + return anim; // dojo.Animation + }; + + fxg.animateFill = function(/*Object*/ args){ + // summary: + // Returns an animation which will change fill color over time. + // Only solid fill color is supported at the moment + // example: + // | dojox.gfx.fx.animateFill{{ + // | shape: shape, + // | duration: 500, + // | color: {start: "red", end: "green"} + // | }).play(); + if(!args.easing){ args.easing = fx._defaultEasing; } + var anim = new fx.Animation(args), shape = args.shape, fill; + Hub.connect(anim, "beforeBegin", anim, function(){ + fill = shape.getFill(); + var prop = args.color, values = {}; + if(prop){ + this.curve = getColorInterpol(prop, fill, "", transparent); + } + }); + Hub.connect(anim, "onAnimate", shape, "setFill"); + return anim; // dojo.Animation + }; + + fxg.animateFont = function(/*Object*/ args){ + // summary: + // Returns an animation which will change font properties over time. + // example: + // | dojox.gfx.fx.animateFont{{ + // | shape: shape, + // | duration: 500, + // | variant: {values: ["normal", "small-caps"]}, + // | size: {end: 10, units: "pt"} + // | }).play(); + if(!args.easing){ args.easing = fx._defaultEasing; } + var anim = new fx.Animation(args), shape = args.shape, font; + Hub.connect(anim, "beforeBegin", anim, function(){ + font = shape.getFont(); + var prop = args.style, values = {}, value, start, end; + if(prop && prop.values){ + values.style = new InterpolValues(prop.values); + } + prop = args.variant; + if(prop && prop.values){ + values.variant = new InterpolValues(prop.values); + } + prop = args.weight; + if(prop && prop.values){ + values.weight = new InterpolValues(prop.values); + } + prop = args.family; + if(prop && prop.values){ + values.family = new InterpolValues(prop.values); + } + prop = args.size; + if(prop && prop.units){ + start = parseFloat(prop.start ? prop.start : (shape.font && shape.font.size || "0")); + end = parseFloat(prop.end ? prop.end : (shape.font && shape.font.size || "0")); + values.size = new InterpolUnit(start, end, prop.units); + } + this.curve = new InterpolObject(values, font); + }); + Hub.connect(anim, "onAnimate", shape, "setFont"); + return anim; // dojo.Animation + }; + + fxg.animateTransform = function(/*Object*/ args){ + // summary: + // Returns an animation which will change transformation over time. + // example: + // | dojox.gfx.fx.animateTransform{{ + // | shape: shape, + // | duration: 500, + // | transform: [ + // | {name: "translate", start: [0, 0], end: [200, 200]}, + // | {name: "original"} + // | ] + // | }).play(); + if(!args.easing){ args.easing = fx._defaultEasing; } + var anim = new fx.Animation(args), shape = args.shape, original; + Hub.connect(anim, "beforeBegin", anim, function(){ + original = shape.getTransform(); + this.curve = new InterpolTransform(args.transform, original); + }); + Hub.connect(anim, "onAnimate", shape, "setTransform"); + return anim; // dojo.Animation + }; + + return fxg; +}); + +}, +'dojox/charting/action2d/PlotAction':function(){ +define("dojox/charting/action2d/PlotAction", ["dojo/_base/connect", "dojo/_base/declare", "./Base", "dojo/fx/easing", "dojox/lang/functional", + "dojox/lang/functional/object"], + function(hub, declare, Base, dfe, df, dlfo){ + + /*===== + dojox.charting.action2d.__PlotActionCtorArgs = function(duration, easing){ + // summary: + // The base keyword arguments object for creating an action2d. + // duration: Number? + // The amount of time in milliseconds for an animation to last. Default is 400. + // easing: dojo.fx.easing.*? + // An easing object (see dojo.fx.easing) for use in an animation. The + // default is dojo.fx.easing.backOut. + this.duration = duration; + this.easing = easing; + } + var Base = dojox.charting.action2d.Base; + =====*/ + + var DEFAULT_DURATION = 400, // ms + DEFAULT_EASING = dfe.backOut; + + return declare("dojox.charting.action2d.PlotAction", Base, { + // summary: + // Base action class for plot actions. + + overOutEvents: {onmouseover: 1, onmouseout: 1}, + + constructor: function(chart, plot, kwargs){ + // summary: + // Create a new base PlotAction. + // chart: dojox.charting.Chart + // The chart this action applies to. + // plot: String? + // The name of the plot this action belongs to. If none is passed "default" is assumed. + // kwargs: dojox.charting.action2d.__PlotActionCtorArgs? + // Optional arguments for the action. + this.anim = {}; + + // process common optional named parameters + if(!kwargs){ kwargs = {}; } + this.duration = kwargs.duration ? kwargs.duration : DEFAULT_DURATION; + this.easing = kwargs.easing ? kwargs.easing : DEFAULT_EASING; + }, + + connect: function(){ + // summary: + // Connect this action to the given plot. + this.handle = this.chart.connectToPlot(this.plot.name, this, "process"); + }, + + disconnect: function(){ + // summary: + // Disconnect this action from the given plot, if connected. + if(this.handle){ + hub.disconnect(this.handle); + this.handle = null; + } + }, + + reset: function(){ + // summary: + // Reset the action. + }, + + destroy: function(){ + // summary: + // Do any cleanup needed when destroying parent elements. + this.inherited(arguments); + df.forIn(this.anim, function(o){ + df.forIn(o, function(anim){ + anim.action.stop(true); + }); + }); + this.anim = {}; + } + }); +}); + +}, +'dijit/BackgroundIframe':function(){ +define("dijit/BackgroundIframe", [ + "require", // require.toUrl + ".", // to export dijit.BackgroundIframe + "dojo/_base/config", + "dojo/dom-construct", // domConstruct.create + "dojo/dom-style", // domStyle.set + "dojo/_base/lang", // lang.extend lang.hitch + "dojo/on", + "dojo/_base/sniff", // has("ie"), has("mozilla"), has("quirks") + "dojo/_base/window" // win.doc.createElement +], function(require, dijit, config, domConstruct, domStyle, lang, on, has, win){ + + // module: + // dijit/BackgroundIFrame + // summary: + // new dijit.BackgroundIframe(node) + // Makes a background iframe as a child of node, that fills + // area (and position) of node + + // TODO: remove _frames, it isn't being used much, since popups never release their + // iframes (see [22236]) + var _frames = new function(){ + // summary: + // cache of iframes + + var queue = []; + + this.pop = function(){ + var iframe; + if(queue.length){ + iframe = queue.pop(); + iframe.style.display=""; + }else{ + if(has("ie") < 9){ + var burl = config["dojoBlankHtmlUrl"] || require.toUrl("dojo/resources/blank.html") || "javascript:\"\""; + var html="<iframe src='" + burl + "' role='presentation'" + + " style='position: absolute; left: 0px; top: 0px;" + + "z-index: -1; filter:Alpha(Opacity=\"0\");'>"; + iframe = win.doc.createElement(html); + }else{ + iframe = domConstruct.create("iframe"); + iframe.src = 'javascript:""'; + iframe.className = "dijitBackgroundIframe"; + iframe.setAttribute("role", "presentation"); + domStyle.set(iframe, "opacity", 0.1); + } + iframe.tabIndex = -1; // Magic to prevent iframe from getting focus on tab keypress - as style didn't work. + } + return iframe; + }; + + this.push = function(iframe){ + iframe.style.display="none"; + queue.push(iframe); + } + }(); + + + dijit.BackgroundIframe = function(/*DomNode*/ node){ + // summary: + // For IE/FF z-index schenanigans. id attribute is required. + // + // description: + // new dijit.BackgroundIframe(node) + // Makes a background iframe as a child of node, that fills + // area (and position) of node + + if(!node.id){ throw new Error("no id"); } + if(has("ie") || has("mozilla")){ + var iframe = (this.iframe = _frames.pop()); + node.appendChild(iframe); + if(has("ie")<7 || has("quirks")){ + this.resize(node); + this._conn = on(node, 'resize', lang.hitch(this, function(){ + this.resize(node); + })); + }else{ + domStyle.set(iframe, { + width: '100%', + height: '100%' + }); + } + } + }; + + lang.extend(dijit.BackgroundIframe, { + resize: function(node){ + // summary: + // Resize the iframe so it's the same size as node. + // Needed on IE6 and IE/quirks because height:100% doesn't work right. + if(this.iframe){ + domStyle.set(this.iframe, { + width: node.offsetWidth + 'px', + height: node.offsetHeight + 'px' + }); + } + }, + destroy: function(){ + // summary: + // destroy the iframe + if(this._conn){ + this._conn.remove(); + this._conn = null; + } + if(this.iframe){ + _frames.push(this.iframe); + delete this.iframe; + } + } + }); + + return dijit.BackgroundIframe; +}); + +}, +'dojox/main':function(){ +define("dojox/main", ["dojo/_base/kernel"], function(dojo) { + // module: + // dojox/main + // summary: + // The dojox package main module; dojox package is somewhat unusual in that the main module currently just provides an empty object. + + return dojo.dojox; +}); +}, +'dojox/charting/action2d/Magnify':function(){ +define("dojox/charting/action2d/Magnify", ["dojo/_base/connect", "dojo/_base/declare", + "./PlotAction", "dojox/gfx/matrix", + "dojox/gfx/fx", "dojo/fx", "dojo/fx/easing"], + function(Hub, declare, PlotAction, m, gf, df, dfe){ + + /*===== + dojo.declare("dojox.charting.action2d.__MagnifyCtorArgs", dojox.charting.action2d.__PlotActionCtorArgs, { + // summary: + // Additional arguments for highlighting actions. + + // scale: Number? + // The amount to magnify the given object to. Default is 2. + scale: 2 + }); + var PlotAction = dojox.charting.action2d.PlotAction; + =====*/ + + var DEFAULT_SCALE = 2; + + return declare("dojox.charting.action2d.Magnify", PlotAction, { + // summary: + // Create an action that magnifies the object the action is applied to. + + // the data description block for the widget parser + defaultParams: { + duration: 400, // duration of the action in ms + easing: dfe.backOut, // easing for the action + scale: DEFAULT_SCALE // scale of magnification + }, + optionalParams: {}, // no optional parameters + + constructor: function(chart, plot, kwArgs){ + // summary: + // Create the magnifying action. + // chart: dojox.charting.Chart + // The chart this action belongs to. + // plot: String? + // The plot to apply the action to. If not passed, "default" is assumed. + // kwArgs: dojox.charting.action2d.__MagnifyCtorArgs? + // Optional keyword arguments for this action. + + // process optional named parameters + this.scale = kwArgs && typeof kwArgs.scale == "number" ? kwArgs.scale : DEFAULT_SCALE; + + this.connect(); + }, + + process: function(o){ + // summary: + // Process the action on the given object. + // o: dojox.gfx.Shape + // The object on which to process the magnifying action. + if(!o.shape || !(o.type in this.overOutEvents) || + !("cx" in o) || !("cy" in o)){ return; } + + var runName = o.run.name, index = o.index, vector = [], anim, init, scale; + + if(runName in this.anim){ + anim = this.anim[runName][index]; + }else{ + this.anim[runName] = {}; + } + + if(anim){ + anim.action.stop(true); + }else{ + this.anim[runName][index] = anim = {}; + } + + if(o.type == "onmouseover"){ + init = m.identity; + scale = this.scale; + }else{ + init = m.scaleAt(this.scale, o.cx, o.cy); + scale = 1 / this.scale; + } + + var kwArgs = { + shape: o.shape, + duration: this.duration, + easing: this.easing, + transform: [ + {name: "scaleAt", start: [1, o.cx, o.cy], end: [scale, o.cx, o.cy]}, + init + ] + }; + if(o.shape){ + vector.push(gf.animateTransform(kwArgs)); + } + if(o.oultine){ + kwArgs.shape = o.outline; + vector.push(gf.animateTransform(kwArgs)); + } + if(o.shadow){ + kwArgs.shape = o.shadow; + vector.push(gf.animateTransform(kwArgs)); + } + + if(!vector.length){ + delete this.anim[runName][index]; + return; + } + + anim.action = df.combine(vector); + if(o.type == "onmouseout"){ + Hub.connect(anim.action, "onEnd", this, function(){ + if(this.anim[runName]){ + delete this.anim[runName][index]; + } + }); + } + anim.action.play(); + } + }); + +}); + +}, +'dojo/Stateful':function(){ +define(["./_base/kernel", "./_base/declare", "./_base/lang", "./_base/array"], function(dojo, declare, lang, array) { + // module: + // dojo/Stateful + // summary: + // TODOC + +return dojo.declare("dojo.Stateful", null, { + // summary: + // Base class for objects that provide named properties with optional getter/setter + // control and the ability to watch for property changes + // example: + // | var obj = new dojo.Stateful(); + // | obj.watch("foo", function(){ + // | console.log("foo changed to " + this.get("foo")); + // | }); + // | obj.set("foo","bar"); + postscript: function(mixin){ + if(mixin){ + lang.mixin(this, mixin); + } + }, + + get: function(/*String*/name){ + // summary: + // Get a property on a Stateful instance. + // name: + // The property to get. + // returns: + // The property value on this Stateful instance. + // description: + // Get a named property on a Stateful object. The property may + // potentially be retrieved via a getter method in subclasses. In the base class + // this just retrieves the object's property. + // For example: + // | stateful = new dojo.Stateful({foo: 3}); + // | stateful.get("foo") // returns 3 + // | stateful.foo // returns 3 + + return this[name]; //Any + }, + set: function(/*String*/name, /*Object*/value){ + // summary: + // Set a property on a Stateful instance + // name: + // The property to set. + // value: + // The value to set in the property. + // returns: + // The function returns this dojo.Stateful instance. + // description: + // Sets named properties on a stateful object and notifies any watchers of + // the property. A programmatic setter may be defined in subclasses. + // For example: + // | stateful = new dojo.Stateful(); + // | stateful.watch(function(name, oldValue, value){ + // | // this will be called on the set below + // | } + // | stateful.set(foo, 5); + // + // set() may also be called with a hash of name/value pairs, ex: + // | myObj.set({ + // | foo: "Howdy", + // | bar: 3 + // | }) + // This is equivalent to calling set(foo, "Howdy") and set(bar, 3) + if(typeof name === "object"){ + for(var x in name){ + this.set(x, name[x]); + } + return this; + } + var oldValue = this[name]; + this[name] = value; + if(this._watchCallbacks){ + this._watchCallbacks(name, oldValue, value); + } + return this; //dojo.Stateful + }, + watch: function(/*String?*/name, /*Function*/callback){ + // summary: + // Watches a property for changes + // name: + // Indicates the property to watch. This is optional (the callback may be the + // only parameter), and if omitted, all the properties will be watched + // returns: + // An object handle for the watch. The unwatch method of this object + // can be used to discontinue watching this property: + // | var watchHandle = obj.watch("foo", callback); + // | watchHandle.unwatch(); // callback won't be called now + // callback: + // The function to execute when the property changes. This will be called after + // the property has been changed. The callback will be called with the |this| + // set to the instance, the first argument as the name of the property, the + // second argument as the old value and the third argument as the new value. + + var callbacks = this._watchCallbacks; + if(!callbacks){ + var self = this; + callbacks = this._watchCallbacks = function(name, oldValue, value, ignoreCatchall){ + var notify = function(propertyCallbacks){ + if(propertyCallbacks){ + propertyCallbacks = propertyCallbacks.slice(); + for(var i = 0, l = propertyCallbacks.length; i < l; i++){ + try{ + propertyCallbacks[i].call(self, name, oldValue, value); + }catch(e){ + console.error(e); + } + } + } + }; + notify(callbacks['_' + name]); + if(!ignoreCatchall){ + notify(callbacks["*"]); // the catch-all + } + }; // we use a function instead of an object so it will be ignored by JSON conversion + } + if(!callback && typeof name === "function"){ + callback = name; + name = "*"; + }else{ + // prepend with dash to prevent name conflicts with function (like "name" property) + name = '_' + name; + } + var propertyCallbacks = callbacks[name]; + if(typeof propertyCallbacks !== "object"){ + propertyCallbacks = callbacks[name] = []; + } + propertyCallbacks.push(callback); + return { + unwatch: function(){ + propertyCallbacks.splice(array.indexOf(propertyCallbacks, callback), 1); + } + }; //Object + } + +}); + +}); + +}, +'dojox/charting/plot2d/Markers':function(){ +define("dojox/charting/plot2d/Markers", ["dojo/_base/declare", "./Default"], function(declare, Default){ +/*===== +var Default = dojox.charting.plot2d.Default +=====*/ + return declare("dojox.charting.plot2d.Markers", Default, { + // summary: + // A convenience plot to draw a line chart with markers. + constructor: function(){ + // summary: + // Set up the plot for lines and markers. + this.opt.markers = true; + } + }); +}); + +}, +'dojox/charting/plot2d/Bubble':function(){ +define("dojox/charting/plot2d/Bubble", ["dojo/_base/lang", "dojo/_base/declare", "dojo/_base/array", + "./Base", "./common", "dojox/lang/functional", "dojox/lang/functional/reversed", + "dojox/lang/utils", "dojox/gfx/fx"], + function(lang, declare, arr, Base, dc, df, dfr, du, fx){ +/*===== +var Base = dojox.charting.plot2d.Base; +=====*/ + + var purgeGroup = dfr.lambda("item.purgeGroup()"); + + return declare("dojox.charting.plot2d.Bubble", Base, { + // summary: + // A plot representing bubbles. Note that data for Bubbles requires 3 parameters, + // in the form of: { x, y, size }, where size determines the size of the bubble. + defaultParams: { + hAxis: "x", // use a horizontal axis named "x" + vAxis: "y", // use a vertical axis named "y" + animate: null // animate bars into place + }, + optionalParams: { + // theme component + stroke: {}, + outline: {}, + shadow: {}, + fill: {}, + font: "", + fontColor: "" + }, + + constructor: function(chart, kwArgs){ + // summary: + // Create a plot of bubbles. + // chart: dojox.charting.Chart + // The chart this plot belongs to. + // kwArgs: dojox.charting.plot2d.__DefaultCtorArgs? + // Optional keyword arguments object to help define plot parameters. + this.opt = lang.clone(this.defaultParams); + du.updateWithObject(this.opt, kwArgs); + du.updateWithPattern(this.opt, kwArgs, this.optionalParams); + this.series = []; + this.hAxis = this.opt.hAxis; + this.vAxis = this.opt.vAxis; + this.animate = this.opt.animate; + }, + + // override the render so that we are plotting only circles. + render: function(dim, offsets){ + // summary: + // Run the calculations for any axes for this plot. + // dim: Object + // An object in the form of { width, height } + // offsets: Object + // An object of the form { l, r, t, b}. + // returns: dojox.charting.plot2d.Bubble + // A reference to this plot for functional chaining. + if(this.zoom && !this.isDataDirty()){ + return this.performZoom(dim, offsets); + } + this.resetEvents(); + this.dirty = this.isDirty(); + if(this.dirty){ + arr.forEach(this.series, purgeGroup); + this._eventSeries = {}; + this.cleanGroup(); + var s = this.group; + df.forEachRev(this.series, function(item){ item.cleanGroup(s); }); + } + + var t = this.chart.theme, + ht = this._hScaler.scaler.getTransformerFromModel(this._hScaler), + vt = this._vScaler.scaler.getTransformerFromModel(this._vScaler), + events = this.events(); + + for(var i = this.series.length - 1; i >= 0; --i){ + var run = this.series[i]; + if(!this.dirty && !run.dirty){ + t.skip(); + this._reconnectEvents(run.name); + continue; + } + run.cleanGroup(); + if(!run.data.length){ + run.dirty = false; + t.skip(); + continue; + } + + if(typeof run.data[0] == "number"){ + console.warn("dojox.charting.plot2d.Bubble: the data in the following series cannot be rendered as a bubble chart; ", run); + continue; + } + + var theme = t.next("circle", [this.opt, run]), s = run.group, + points = arr.map(run.data, function(v, i){ + return v ? { + x: ht(v.x) + offsets.l, + y: dim.height - offsets.b - vt(v.y), + radius: this._vScaler.bounds.scale * (v.size / 2) + } : null; + }, this); + + var frontCircles = null, outlineCircles = null, shadowCircles = null; + + // make shadows if needed + if(theme.series.shadow){ + shadowCircles = arr.map(points, function(item){ + if(item !== null){ + var finalTheme = t.addMixin(theme, "circle", item, true), + shadow = finalTheme.series.shadow; + var shape = s.createCircle({ + cx: item.x + shadow.dx, cy: item.y + shadow.dy, r: item.radius + }).setStroke(shadow).setFill(shadow.color); + if(this.animate){ + this._animateBubble(shape, dim.height - offsets.b, item.radius); + } + return shape; + } + return null; + }, this); + if(shadowCircles.length){ + run.dyn.shadow = shadowCircles[shadowCircles.length - 1].getStroke(); + } + } + + // make outlines if needed + if(theme.series.outline){ + outlineCircles = arr.map(points, function(item){ + if(item !== null){ + var finalTheme = t.addMixin(theme, "circle", item, true), + outline = dc.makeStroke(finalTheme.series.outline); + outline.width = 2 * outline.width + theme.series.stroke.width; + var shape = s.createCircle({ + cx: item.x, cy: item.y, r: item.radius + }).setStroke(outline); + if(this.animate){ + this._animateBubble(shape, dim.height - offsets.b, item.radius); + } + return shape; + } + return null; + }, this); + if(outlineCircles.length){ + run.dyn.outline = outlineCircles[outlineCircles.length - 1].getStroke(); + } + } + + // run through the data and add the circles. + frontCircles = arr.map(points, function(item){ + if(item !== null){ + var finalTheme = t.addMixin(theme, "circle", item, true), + rect = { + x: item.x - item.radius, + y: item.y - item.radius, + width: 2 * item.radius, + height: 2 * item.radius + }; + var specialFill = this._plotFill(finalTheme.series.fill, dim, offsets); + specialFill = this._shapeFill(specialFill, rect); + var shape = s.createCircle({ + cx: item.x, cy: item.y, r: item.radius + }).setFill(specialFill).setStroke(finalTheme.series.stroke); + if(this.animate){ + this._animateBubble(shape, dim.height - offsets.b, item.radius); + } + return shape; + } + return null; + }, this); + if(frontCircles.length){ + run.dyn.fill = frontCircles[frontCircles.length - 1].getFill(); + run.dyn.stroke = frontCircles[frontCircles.length - 1].getStroke(); + } + + if(events){ + var eventSeries = new Array(frontCircles.length); + arr.forEach(frontCircles, function(s, i){ + if(s !== null){ + var o = { + element: "circle", + index: i, + run: run, + shape: s, + outline: outlineCircles && outlineCircles[i] || null, + shadow: shadowCircles && shadowCircles[i] || null, + x: run.data[i].x, + y: run.data[i].y, + r: run.data[i].size / 2, + cx: points[i].x, + cy: points[i].y, + cr: points[i].radius + }; + this._connectEvents(o); + eventSeries[i] = o; + } + }, this); + this._eventSeries[run.name] = eventSeries; + }else{ + delete this._eventSeries[run.name]; + } + + run.dirty = false; + } + this.dirty = false; + return this; // dojox.charting.plot2d.Bubble + }, + _animateBubble: function(shape, offset, size){ + fx.animateTransform(lang.delegate({ + shape: shape, + duration: 1200, + transform: [ + {name: "translate", start: [0, offset], end: [0, 0]}, + {name: "scale", start: [0, 1/size], end: [1, 1]}, + {name: "original"} + ] + }, this.animate)).play(); + } + }); +}); + +}, +'dojo/touch':function(){ +define(["./_base/kernel", "./on", "./has", "./mouse"], function(dojo, on, has, mouse){ +// module: +// dojo/touch + +/*===== + dojo.touch = { + // summary: + // This module provides unified touch event handlers by exporting + // press, move, release and cancel which can also run well on desktop. + // Based on http://dvcs.w3.org/hg/webevents/raw-file/tip/touchevents.html + // + // example: + // 1. Used with dojo.connect() + // | dojo.connect(node, dojo.touch.press, function(e){}); + // | dojo.connect(node, dojo.touch.move, function(e){}); + // | dojo.connect(node, dojo.touch.release, function(e){}); + // | dojo.connect(node, dojo.touch.cancel, function(e){}); + // + // 2. Used with dojo.on + // | define(["dojo/on", "dojo/touch"], function(on, touch){ + // | on(node, touch.press, function(e){}); + // | on(node, touch.move, function(e){}); + // | on(node, touch.release, function(e){}); + // | on(node, touch.cancel, function(e){}); + // + // 3. Used with dojo.touch.* directly + // | dojo.touch.press(node, function(e){}); + // | dojo.touch.move(node, function(e){}); + // | dojo.touch.release(node, function(e){}); + // | dojo.touch.cancel(node, function(e){}); + + press: function(node, listener){ + // summary: + // Register a listener to 'touchstart'|'mousedown' for the given node + // node: Dom + // Target node to listen to + // listener: Function + // Callback function + // returns: + // A handle which will be used to remove the listener by handle.remove() + }, + move: function(node, listener){ + // summary: + // Register a listener to 'touchmove'|'mousemove' for the given node + // node: Dom + // Target node to listen to + // listener: Function + // Callback function + // returns: + // A handle which will be used to remove the listener by handle.remove() + }, + release: function(node, listener){ + // summary: + // Register a listener to 'touchend'|'mouseup' for the given node + // node: Dom + // Target node to listen to + // listener: Function + // Callback function + // returns: + // A handle which will be used to remove the listener by handle.remove() + }, + cancel: function(node, listener){ + // summary: + // Register a listener to 'touchcancel'|'mouseleave' for the given node + // node: Dom + // Target node to listen to + // listener: Function + // Callback function + // returns: + // A handle which will be used to remove the listener by handle.remove() + } + }; +=====*/ + + function _handle(/*String - press | move | release | cancel*/type){ + return function(node, listener){//called by on(), see dojo.on + return on(node, type, listener); + }; + } + var touch = has("touch"); + //device neutral events - dojo.touch.press|move|release|cancel + dojo.touch = { + press: _handle(touch ? "touchstart": "mousedown"), + move: _handle(touch ? "touchmove": "mousemove"), + release: _handle(touch ? "touchend": "mouseup"), + cancel: touch ? _handle("touchcancel") : mouse.leave + }; + return dojo.touch; +}); +}, +'dojox/gfx/gradutils':function(){ +// Various generic utilities to deal with a linear gradient + +define(["./_base", "dojo/_base/lang", "./matrix", "dojo/_base/Color"], + function(g, lang, m, Color){ + + /*===== g= dojox.gfx =====*/ + var gradutils = g.gradutils = {}; + /*===== g= dojox.gfx; gradutils = dojox.gfx.gradutils; =====*/ + + function findColor(o, c){ + if(o <= 0){ + return c[0].color; + } + var len = c.length; + if(o >= 1){ + return c[len - 1].color; + } + //TODO: use binary search + for(var i = 0; i < len; ++i){ + var stop = c[i]; + if(stop.offset >= o){ + if(i){ + var prev = c[i - 1]; + return Color.blendColors(new Color(prev.color), new Color(stop.color), + (o - prev.offset) / (stop.offset - prev.offset)); + } + return stop.color; + } + } + return c[len - 1].color; + } + + gradutils.getColor = function(fill, pt){ + // summary: + // sample a color from a gradient using a point + // fill: Object: + // fill object + // pt: dojox.gfx.Point: + // point where to sample a color + var o; + if(fill){ + switch(fill.type){ + case "linear": + var angle = Math.atan2(fill.y2 - fill.y1, fill.x2 - fill.x1), + rotation = m.rotate(-angle), + projection = m.project(fill.x2 - fill.x1, fill.y2 - fill.y1), + p = m.multiplyPoint(projection, pt), + pf1 = m.multiplyPoint(projection, fill.x1, fill.y1), + pf2 = m.multiplyPoint(projection, fill.x2, fill.y2), + scale = m.multiplyPoint(rotation, pf2.x - pf1.x, pf2.y - pf1.y).x; + o = m.multiplyPoint(rotation, p.x - pf1.x, p.y - pf1.y).x / scale; + break; + case "radial": + var dx = pt.x - fill.cx, dy = pt.y - fill.cy; + o = Math.sqrt(dx * dx + dy * dy) / fill.r; + break; + } + return findColor(o, fill.colors); // dojo.Color + } + // simple color + return new Color(fill || [0, 0, 0, 0]); // dojo.Color + }; + + gradutils.reverse = function(fill){ + // summary: + // reverses a gradient + // fill: Object: + // fill object + if(fill){ + switch(fill.type){ + case "linear": + case "radial": + fill = lang.delegate(fill); + if(fill.colors){ + var c = fill.colors, l = c.length, i = 0, stop, + n = fill.colors = new Array(c.length); + for(; i < l; ++i){ + stop = c[i]; + n[i] = { + offset: 1 - stop.offset, + color: stop.color + }; + } + n.sort(function(a, b){ return a.offset - b.offset; }); + } + break; + } + } + return fill; // Object + }; + + return gradutils; +}); + +}, +'dojo/string':function(){ +define(["./_base/kernel", "./_base/lang"], function(dojo, lang) { + // module: + // dojo/string + // summary: + // TODOC + +lang.getObject("string", true, dojo); + +/*===== +dojo.string = { + // summary: String utilities for Dojo +}; +=====*/ + +dojo.string.rep = function(/*String*/str, /*Integer*/num){ + // summary: + // Efficiently replicate a string `n` times. + // str: + // the string to replicate + // num: + // number of times to replicate the string + + if(num <= 0 || !str){ return ""; } + + var buf = []; + for(;;){ + if(num & 1){ + buf.push(str); + } + if(!(num >>= 1)){ break; } + str += str; + } + return buf.join(""); // String +}; + +dojo.string.pad = function(/*String*/text, /*Integer*/size, /*String?*/ch, /*Boolean?*/end){ + // summary: + // Pad a string to guarantee that it is at least `size` length by + // filling with the character `ch` at either the start or end of the + // string. Pads at the start, by default. + // text: + // the string to pad + // size: + // length to provide padding + // ch: + // character to pad, defaults to '0' + // end: + // adds padding at the end if true, otherwise pads at start + // example: + // | // Fill the string to length 10 with "+" characters on the right. Yields "Dojo++++++". + // | dojo.string.pad("Dojo", 10, "+", true); + + if(!ch){ + ch = '0'; + } + var out = String(text), + pad = dojo.string.rep(ch, Math.ceil((size - out.length) / ch.length)); + return end ? out + pad : pad + out; // String +}; + +dojo.string.substitute = function( /*String*/ template, + /*Object|Array*/map, + /*Function?*/ transform, + /*Object?*/ thisObject){ + // summary: + // Performs parameterized substitutions on a string. Throws an + // exception if any parameter is unmatched. + // template: + // a string with expressions in the form `${key}` to be replaced or + // `${key:format}` which specifies a format function. keys are case-sensitive. + // map: + // hash to search for substitutions + // transform: + // a function to process all parameters before substitution takes + // place, e.g. mylib.encodeXML + // thisObject: + // where to look for optional format function; default to the global + // namespace + // example: + // Substitutes two expressions in a string from an Array or Object + // | // returns "File 'foo.html' is not found in directory '/temp'." + // | // by providing substitution data in an Array + // | dojo.string.substitute( + // | "File '${0}' is not found in directory '${1}'.", + // | ["foo.html","/temp"] + // | ); + // | + // | // also returns "File 'foo.html' is not found in directory '/temp'." + // | // but provides substitution data in an Object structure. Dotted + // | // notation may be used to traverse the structure. + // | dojo.string.substitute( + // | "File '${name}' is not found in directory '${info.dir}'.", + // | { name: "foo.html", info: { dir: "/temp" } } + // | ); + // example: + // Use a transform function to modify the values: + // | // returns "file 'foo.html' is not found in directory '/temp'." + // | dojo.string.substitute( + // | "${0} is not found in ${1}.", + // | ["foo.html","/temp"], + // | function(str){ + // | // try to figure out the type + // | var prefix = (str.charAt(0) == "/") ? "directory": "file"; + // | return prefix + " '" + str + "'"; + // | } + // | ); + // example: + // Use a formatter + // | // returns "thinger -- howdy" + // | dojo.string.substitute( + // | "${0:postfix}", ["thinger"], null, { + // | postfix: function(value, key){ + // | return value + " -- howdy"; + // | } + // | } + // | ); + + thisObject = thisObject || dojo.global; + transform = transform ? + lang.hitch(thisObject, transform) : function(v){ return v; }; + + return template.replace(/\$\{([^\s\:\}]+)(?:\:([^\s\:\}]+))?\}/g, + function(match, key, format){ + var value = lang.getObject(key, false, map); + if(format){ + value = lang.getObject(format, false, thisObject).call(thisObject, value, key); + } + return transform(value, key).toString(); + }); // String +}; + +/*===== +dojo.string.trim = function(str){ + // summary: + // Trims whitespace from both sides of the string + // str: String + // String to be trimmed + // returns: String + // Returns the trimmed string + // description: + // This version of trim() was taken from [Steven Levithan's blog](http://blog.stevenlevithan.com/archives/faster-trim-javascript). + // The short yet performant version of this function is dojo.trim(), + // which is part of Dojo base. Uses String.prototype.trim instead, if available. + return ""; // String +} +=====*/ + +dojo.string.trim = String.prototype.trim ? + lang.trim : // aliasing to the native function + function(str){ + str = str.replace(/^\s+/, ''); + for(var i = str.length - 1; i >= 0; i--){ + if(/\S/.test(str.charAt(i))){ + str = str.substring(0, i + 1); + break; + } + } + return str; + }; + +return dojo.string; +}); + +}, +'dijit/registry':function(){ +define("dijit/registry", [ + "dojo/_base/array", // array.forEach array.map + "dojo/_base/sniff", // has("ie") + "dojo/_base/unload", // unload.addOnWindowUnload + "dojo/_base/window", // win.body + "." // dijit._scopeName +], function(array, has, unload, win, dijit){ + + // module: + // dijit/registry + // summary: + // Registry of existing widget on page, plus some utility methods. + // Must be accessed through AMD api, ex: + // require(["dijit/registry"], function(registry){ registry.byId("foo"); }) + + var _widgetTypeCtr = {}, hash = {}; + + var registry = { + // summary: + // A set of widgets indexed by id + + length: 0, + + add: function(/*dijit._Widget*/ widget){ + // summary: + // Add a widget to the registry. If a duplicate ID is detected, a error is thrown. + // + // widget: dijit._Widget + // Any dijit._Widget subclass. + if(hash[widget.id]){ + throw new Error("Tried to register widget with id==" + widget.id + " but that id is already registered"); + } + hash[widget.id] = widget; + this.length++; + }, + + remove: function(/*String*/ id){ + // summary: + // Remove a widget from the registry. Does not destroy the widget; simply + // removes the reference. + if(hash[id]){ + delete hash[id]; + this.length--; + } + }, + + byId: function(/*String|Widget*/ id){ + // summary: + // Find a widget by it's id. + // If passed a widget then just returns the widget. + return typeof id == "string" ? hash[id] : id; // dijit._Widget + }, + + byNode: function(/*DOMNode*/ node){ + // summary: + // Returns the widget corresponding to the given DOMNode + return hash[node.getAttribute("widgetId")]; // dijit._Widget + }, + + toArray: function(){ + // summary: + // Convert registry into a true Array + // + // example: + // Work with the widget .domNodes in a real Array + // | array.map(dijit.registry.toArray(), function(w){ return w.domNode; }); + + var ar = []; + for(var id in hash){ + ar.push(hash[id]); + } + return ar; // dijit._Widget[] + }, + + getUniqueId: function(/*String*/widgetType){ + // summary: + // Generates a unique id for a given widgetType + + var id; + do{ + id = widgetType + "_" + + (widgetType in _widgetTypeCtr ? + ++_widgetTypeCtr[widgetType] : _widgetTypeCtr[widgetType] = 0); + }while(hash[id]); + return dijit._scopeName == "dijit" ? id : dijit._scopeName + "_" + id; // String + }, + + findWidgets: function(/*DomNode*/ root){ + // summary: + // Search subtree under root returning widgets found. + // Doesn't search for nested widgets (ie, widgets inside other widgets). + + var outAry = []; + + function getChildrenHelper(root){ + for(var node = root.firstChild; node; node = node.nextSibling){ + if(node.nodeType == 1){ + var widgetId = node.getAttribute("widgetId"); + if(widgetId){ + var widget = hash[widgetId]; + if(widget){ // may be null on page w/multiple dojo's loaded + outAry.push(widget); + } + }else{ + getChildrenHelper(node); + } + } + } + } + + getChildrenHelper(root); + return outAry; + }, + + _destroyAll: function(){ + // summary: + // Code to destroy all widgets and do other cleanup on page unload + + // Clean up focus manager lingering references to widgets and nodes + dijit._curFocus = null; + dijit._prevFocus = null; + dijit._activeStack = []; + + // Destroy all the widgets, top down + array.forEach(registry.findWidgets(win.body()), function(widget){ + // Avoid double destroy of widgets like Menu that are attached to <body> + // even though they are logically children of other widgets. + if(!widget._destroyed){ + if(widget.destroyRecursive){ + widget.destroyRecursive(); + }else if(widget.destroy){ + widget.destroy(); + } + } + }); + }, + + getEnclosingWidget: function(/*DOMNode*/ node){ + // summary: + // Returns the widget whose DOM tree contains the specified DOMNode, or null if + // the node is not contained within the DOM tree of any widget + while(node){ + var id = node.getAttribute && node.getAttribute("widgetId"); + if(id){ + return hash[id]; + } + node = node.parentNode; + } + return null; + }, + + // In case someone needs to access hash. + // Actually, this is accessed from WidgetSet back-compatibility code + _hash: hash + }; + + if(has("ie")){ + // Only run _destroyAll() for IE because we think it's only necessary in that case, + // and because it causes problems on FF. See bug #3531 for details. + unload.addOnWindowUnload(function(){ + registry._destroyAll(); + }); + } + + /*===== + dijit.registry = { + // summary: + // A list of widgets on a page. + }; + =====*/ + dijit.registry = registry; + + return registry; +}); + +}, +'dojox/charting/plot2d/Lines':function(){ +define("dojox/charting/plot2d/Lines", ["dojo/_base/declare", "./Default"], function(declare, Default){ +/*===== +var Default = dojox.charting.plot2d.Default; +=====*/ + return declare("dojox.charting.plot2d.Lines", Default, { + // summary: + // A convenience constructor to create a typical line chart. + constructor: function(){ + // summary: + // Preset our default plot to be line-based. + this.opt.lines = true; + } + }); +}); + +}, +'dijit/_base/manager':function(){ +define("dijit/_base/manager", [ + "dojo/_base/array", + "dojo/_base/config", // defaultDuration + "../registry", + ".." // for setting exports to dijit namespace +], function(array, config, registry, dijit){ + + // module: + // dijit/_base/manager + // summary: + // Shim to methods on registry, plus a few other declarations. + // New code should access dijit/registry directly when possible. + + /*===== + dijit.byId = function(id){ + // summary: + // Returns a widget by it's id, or if passed a widget, no-op (like dom.byId()) + // id: String|dijit._Widget + return registry.byId(id); // dijit._Widget + }; + + dijit.getUniqueId = function(widgetType){ + // summary: + // Generates a unique id for a given widgetType + // widgetType: String + return registry.getUniqueId(widgetType); // String + }; + + dijit.findWidgets = function(root){ + // summary: + // Search subtree under root returning widgets found. + // Doesn't search for nested widgets (ie, widgets inside other widgets). + // root: DOMNode + return registry.findWidgets(root); + }; + + dijit._destroyAll = function(){ + // summary: + // Code to destroy all widgets and do other cleanup on page unload + + return registry._destroyAll(); + }; + + dijit.byNode = function(node){ + // summary: + // Returns the widget corresponding to the given DOMNode + // node: DOMNode + return registry.byNode(node); // dijit._Widget + }; + + dijit.getEnclosingWidget = function(node){ + // summary: + // Returns the widget whose DOM tree contains the specified DOMNode, or null if + // the node is not contained within the DOM tree of any widget + // node: DOMNode + return registry.getEnclosingWidget(node); + }; + =====*/ + array.forEach(["byId", "getUniqueId", "findWidgets", "_destroyAll", "byNode", "getEnclosingWidget"], function(name){ + dijit[name] = registry[name]; + }); + + /*===== + dojo.mixin(dijit, { + // defaultDuration: Integer + // The default fx.animation speed (in ms) to use for all Dijit + // transitional fx.animations, unless otherwise specified + // on a per-instance basis. Defaults to 200, overrided by + // `djConfig.defaultDuration` + defaultDuration: 200 + }); + =====*/ + dijit.defaultDuration = config["defaultDuration"] || 200; + + return dijit; +}); + +}, +'dojox/charting/plot2d/StackedAreas':function(){ +define("dojox/charting/plot2d/StackedAreas", ["dojo/_base/declare", "./Stacked"], function(declare, Stacked){ +/*===== +var Stacked = dojox.charting.plot2d.Stacked; +=====*/ + return declare("dojox.charting.plot2d.StackedAreas", Stacked, { + // summary: + // A convenience object to set up a stacked area plot. + constructor: function(){ + // summary: + // Force our Stacked plotter to include both lines and areas. + this.opt.lines = true; + this.opt.areas = true; + } + }); +}); + + +}, +'dojox/charting/plot2d/Stacked':function(){ +define("dojox/charting/plot2d/Stacked", ["dojo/_base/lang", "dojo/_base/declare", "dojo/_base/array", "./Default", "./common", + "dojox/lang/functional", "dojox/lang/functional/reversed", "dojox/lang/functional/sequence"], + function(lang, declare, arr, Default, dc, df, dfr, dfs){ +/*===== +var Default = dojox.charting.plot2d.Default; +=====*/ + var purgeGroup = dfr.lambda("item.purgeGroup()"); + + return declare("dojox.charting.plot2d.Stacked", Default, { + // summary: + // Like the default plot, Stacked sets up lines, areas and markers + // in a stacked fashion (values on the y axis added to each other) + // as opposed to a direct one. + getSeriesStats: function(){ + // summary: + // Calculate the min/max on all attached series in both directions. + // returns: Object + // {hmin, hmax, vmin, vmax} min/max in both directions. + var stats = dc.collectStackedStats(this.series); + this._maxRunLength = stats.hmax; + return stats; + }, + render: function(dim, offsets){ + // summary: + // Run the calculations for any axes for this plot. + // dim: Object + // An object in the form of { width, height } + // offsets: Object + // An object of the form { l, r, t, b}. + // returns: dojox.charting.plot2d.Stacked + // A reference to this plot for functional chaining. + if(this._maxRunLength <= 0){ + return this; + } + + // stack all values + var acc = df.repeat(this._maxRunLength, "-> 0", 0); + for(var i = 0; i < this.series.length; ++i){ + var run = this.series[i]; + for(var j = 0; j < run.data.length; ++j){ + var v = run.data[j]; + if(v !== null){ + if(isNaN(v)){ v = 0; } + acc[j] += v; + } + } + } + // draw runs in backwards + if(this.zoom && !this.isDataDirty()){ + return this.performZoom(dim, offsets); + } + this.resetEvents(); + this.dirty = this.isDirty(); + if(this.dirty){ + arr.forEach(this.series, purgeGroup); + this._eventSeries = {}; + this.cleanGroup(); + var s = this.group; + df.forEachRev(this.series, function(item){ item.cleanGroup(s); }); + } + + var t = this.chart.theme, events = this.events(), + ht = this._hScaler.scaler.getTransformerFromModel(this._hScaler), + vt = this._vScaler.scaler.getTransformerFromModel(this._vScaler); + + for(var i = this.series.length - 1; i >= 0; --i){ + var run = this.series[i]; + if(!this.dirty && !run.dirty){ + t.skip(); + this._reconnectEvents(run.name); + continue; + } + run.cleanGroup(); + var theme = t.next(this.opt.areas ? "area" : "line", [this.opt, run], true), + s = run.group, outline, + lpoly = arr.map(acc, function(v, i){ + return { + x: ht(i + 1) + offsets.l, + y: dim.height - offsets.b - vt(v) + }; + }, this); + + var lpath = this.opt.tension ? dc.curve(lpoly, this.opt.tension) : ""; + + if(this.opt.areas){ + var apoly = lang.clone(lpoly); + if(this.opt.tension){ + var p=dc.curve(apoly, this.opt.tension); + p += " L" + lpoly[lpoly.length - 1].x + "," + (dim.height - offsets.b) + + " L" + lpoly[0].x + "," + (dim.height - offsets.b) + + " L" + lpoly[0].x + "," + lpoly[0].y; + run.dyn.fill = s.createPath(p).setFill(theme.series.fill).getFill(); + } else { + apoly.push({x: lpoly[lpoly.length - 1].x, y: dim.height - offsets.b}); + apoly.push({x: lpoly[0].x, y: dim.height - offsets.b}); + apoly.push(lpoly[0]); + run.dyn.fill = s.createPolyline(apoly).setFill(theme.series.fill).getFill(); + } + } + if(this.opt.lines || this.opt.markers){ + if(theme.series.outline){ + outline = dc.makeStroke(theme.series.outline); + outline.width = 2 * outline.width + theme.series.stroke.width; + } + } + if(this.opt.markers){ + run.dyn.marker = theme.symbol; + } + var frontMarkers, outlineMarkers, shadowMarkers; + if(theme.series.shadow && theme.series.stroke){ + var shadow = theme.series.shadow, + spoly = arr.map(lpoly, function(c){ + return {x: c.x + shadow.dx, y: c.y + shadow.dy}; + }); + if(this.opt.lines){ + if(this.opt.tension){ + run.dyn.shadow = s.createPath(dc.curve(spoly, this.opt.tension)).setStroke(shadow).getStroke(); + } else { + run.dyn.shadow = s.createPolyline(spoly).setStroke(shadow).getStroke(); + } + } + if(this.opt.markers){ + shadow = theme.marker.shadow; + shadowMarkers = arr.map(spoly, function(c){ + return s.createPath("M" + c.x + " " + c.y + " " + theme.symbol). + setStroke(shadow).setFill(shadow.color); + }, this); + } + } + if(this.opt.lines){ + if(outline){ + if(this.opt.tension){ + run.dyn.outline = s.createPath(lpath).setStroke(outline).getStroke(); + } else { + run.dyn.outline = s.createPolyline(lpoly).setStroke(outline).getStroke(); + } + } + if(this.opt.tension){ + run.dyn.stroke = s.createPath(lpath).setStroke(theme.series.stroke).getStroke(); + } else { + run.dyn.stroke = s.createPolyline(lpoly).setStroke(theme.series.stroke).getStroke(); + } + } + if(this.opt.markers){ + frontMarkers = new Array(lpoly.length); + outlineMarkers = new Array(lpoly.length); + outline = null; + if(theme.marker.outline){ + outline = dc.makeStroke(theme.marker.outline); + outline.width = 2 * outline.width + (theme.marker.stroke ? theme.marker.stroke.width : 0); + } + arr.forEach(lpoly, function(c, i){ + var path = "M" + c.x + " " + c.y + " " + theme.symbol; + if(outline){ + outlineMarkers[i] = s.createPath(path).setStroke(outline); + } + frontMarkers[i] = s.createPath(path).setStroke(theme.marker.stroke).setFill(theme.marker.fill); + }, this); + if(events){ + var eventSeries = new Array(frontMarkers.length); + arr.forEach(frontMarkers, function(s, i){ + var o = { + element: "marker", + index: i, + run: run, + shape: s, + outline: outlineMarkers[i] || null, + shadow: shadowMarkers && shadowMarkers[i] || null, + cx: lpoly[i].x, + cy: lpoly[i].y, + x: i + 1, + y: run.data[i] + }; + this._connectEvents(o); + eventSeries[i] = o; + }, this); + this._eventSeries[run.name] = eventSeries; + }else{ + delete this._eventSeries[run.name]; + } + } + run.dirty = false; + // update the accumulator + for(var j = 0; j < run.data.length; ++j){ + var v = run.data[j]; + if(v !== null){ + if(isNaN(v)){ v = 0; } + acc[j] -= v; + } + } + } + this.dirty = false; + return this; // dojox.charting.plot2d.Stacked + } + }); +}); + +}, +'dojo/fx/easing':function(){ +define(["../_base/lang"], function(lang) { +// module: +// dojo/fx/easing +// summary: +// This module defines standard easing functions that are useful for animations. + +var easingFuncs = /*===== dojo.fx.easing= =====*/ { + // summary: + // Collection of easing functions to use beyond the default + // `dojo._defaultEasing` function. + // + // description: + // + // Easing functions are used to manipulate the iteration through + // an `dojo.Animation`s _Line. _Line being the properties of an Animation, + // and the easing function progresses through that Line determing + // how quickly (or slowly) it should go. Or more accurately: modify + // the value of the _Line based on the percentage of animation completed. + // + // All functions follow a simple naming convention of "ease type" + "when". + // If the name of the function ends in Out, the easing described appears + // towards the end of the animation. "In" means during the beginning, + // and InOut means both ranges of the Animation will applied, both + // beginning and end. + // + // One does not call the easing function directly, it must be passed to + // the `easing` property of an animation. + // + // example: + // | dojo.require("dojo.fx.easing"); + // | var anim = dojo.fadeOut({ + // | node: 'node', + // | duration: 2000, + // | // note there is no () + // | easing: dojo.fx.easing.quadIn + // | }).play(); + // + + linear: function(/* Decimal? */n){ + // summary: A linear easing function + return n; + }, + + quadIn: function(/* Decimal? */n){ + return Math.pow(n, 2); + }, + + quadOut: function(/* Decimal? */n){ + return n * (n - 2) * -1; + }, + + quadInOut: function(/* Decimal? */n){ + n = n * 2; + if(n < 1){ return Math.pow(n, 2) / 2; } + return -1 * ((--n) * (n - 2) - 1) / 2; + }, + + cubicIn: function(/* Decimal? */n){ + return Math.pow(n, 3); + }, + + cubicOut: function(/* Decimal? */n){ + return Math.pow(n - 1, 3) + 1; + }, + + cubicInOut: function(/* Decimal? */n){ + n = n * 2; + if(n < 1){ return Math.pow(n, 3) / 2; } + n -= 2; + return (Math.pow(n, 3) + 2) / 2; + }, + + quartIn: function(/* Decimal? */n){ + return Math.pow(n, 4); + }, + + quartOut: function(/* Decimal? */n){ + return -1 * (Math.pow(n - 1, 4) - 1); + }, + + quartInOut: function(/* Decimal? */n){ + n = n * 2; + if(n < 1){ return Math.pow(n, 4) / 2; } + n -= 2; + return -1 / 2 * (Math.pow(n, 4) - 2); + }, + + quintIn: function(/* Decimal? */n){ + return Math.pow(n, 5); + }, + + quintOut: function(/* Decimal? */n){ + return Math.pow(n - 1, 5) + 1; + }, + + quintInOut: function(/* Decimal? */n){ + n = n * 2; + if(n < 1){ return Math.pow(n, 5) / 2; } + n -= 2; + return (Math.pow(n, 5) + 2) / 2; + }, + + sineIn: function(/* Decimal? */n){ + return -1 * Math.cos(n * (Math.PI / 2)) + 1; + }, + + sineOut: function(/* Decimal? */n){ + return Math.sin(n * (Math.PI / 2)); + }, + + sineInOut: function(/* Decimal? */n){ + return -1 * (Math.cos(Math.PI * n) - 1) / 2; + }, + + expoIn: function(/* Decimal? */n){ + return (n == 0) ? 0 : Math.pow(2, 10 * (n - 1)); + }, + + expoOut: function(/* Decimal? */n){ + return (n == 1) ? 1 : (-1 * Math.pow(2, -10 * n) + 1); + }, + + expoInOut: function(/* Decimal? */n){ + if(n == 0){ return 0; } + if(n == 1){ return 1; } + n = n * 2; + if(n < 1){ return Math.pow(2, 10 * (n - 1)) / 2; } + --n; + return (-1 * Math.pow(2, -10 * n) + 2) / 2; + }, + + circIn: function(/* Decimal? */n){ + return -1 * (Math.sqrt(1 - Math.pow(n, 2)) - 1); + }, + + circOut: function(/* Decimal? */n){ + n = n - 1; + return Math.sqrt(1 - Math.pow(n, 2)); + }, + + circInOut: function(/* Decimal? */n){ + n = n * 2; + if(n < 1){ return -1 / 2 * (Math.sqrt(1 - Math.pow(n, 2)) - 1); } + n -= 2; + return 1 / 2 * (Math.sqrt(1 - Math.pow(n, 2)) + 1); + }, + + backIn: function(/* Decimal? */n){ + // summary: + // An easing function that starts away from the target, + // and quickly accelerates towards the end value. + // + // Use caution when the easing will cause values to become + // negative as some properties cannot be set to negative values. + var s = 1.70158; + return Math.pow(n, 2) * ((s + 1) * n - s); + }, + + backOut: function(/* Decimal? */n){ + // summary: + // An easing function that pops past the range briefly, and slowly comes back. + // + // description: + // An easing function that pops past the range briefly, and slowly comes back. + // + // Use caution when the easing will cause values to become negative as some + // properties cannot be set to negative values. + + n = n - 1; + var s = 1.70158; + return Math.pow(n, 2) * ((s + 1) * n + s) + 1; + }, + + backInOut: function(/* Decimal? */n){ + // summary: + // An easing function combining the effects of `backIn` and `backOut` + // + // description: + // An easing function combining the effects of `backIn` and `backOut`. + // Use caution when the easing will cause values to become negative + // as some properties cannot be set to negative values. + var s = 1.70158 * 1.525; + n = n * 2; + if(n < 1){ return (Math.pow(n, 2) * ((s + 1) * n - s)) / 2; } + n-=2; + return (Math.pow(n, 2) * ((s + 1) * n + s) + 2) / 2; + }, + + elasticIn: function(/* Decimal? */n){ + // summary: + // An easing function the elastically snaps from the start value + // + // description: + // An easing function the elastically snaps from the start value + // + // Use caution when the elasticity will cause values to become negative + // as some properties cannot be set to negative values. + if(n == 0 || n == 1){ return n; } + var p = .3; + var s = p / 4; + n = n - 1; + return -1 * Math.pow(2, 10 * n) * Math.sin((n - s) * (2 * Math.PI) / p); + }, + + elasticOut: function(/* Decimal? */n){ + // summary: + // An easing function that elasticly snaps around the target value, + // near the end of the Animation + // + // description: + // An easing function that elasticly snaps around the target value, + // near the end of the Animation + // + // Use caution when the elasticity will cause values to become + // negative as some properties cannot be set to negative values. + if(n==0 || n == 1){ return n; } + var p = .3; + var s = p / 4; + return Math.pow(2, -10 * n) * Math.sin((n - s) * (2 * Math.PI) / p) + 1; + }, + + elasticInOut: function(/* Decimal? */n){ + // summary: + // An easing function that elasticly snaps around the value, near + // the beginning and end of the Animation. + // + // description: + // An easing function that elasticly snaps around the value, near + // the beginning and end of the Animation. + // + // Use caution when the elasticity will cause values to become + // negative as some properties cannot be set to negative values. + if(n == 0) return 0; + n = n * 2; + if(n == 2) return 1; + var p = .3 * 1.5; + var s = p / 4; + if(n < 1){ + n -= 1; + return -.5 * (Math.pow(2, 10 * n) * Math.sin((n - s) * (2 * Math.PI) / p)); + } + n -= 1; + return .5 * (Math.pow(2, -10 * n) * Math.sin((n - s) * (2 * Math.PI) / p)) + 1; + }, + + bounceIn: function(/* Decimal? */n){ + // summary: + // An easing function that 'bounces' near the beginning of an Animation + return (1 - easingFuncs.bounceOut(1 - n)); // Decimal + }, + + bounceOut: function(/* Decimal? */n){ + // summary: + // An easing function that 'bounces' near the end of an Animation + var s = 7.5625; + var p = 2.75; + var l; + if(n < (1 / p)){ + l = s * Math.pow(n, 2); + }else if(n < (2 / p)){ + n -= (1.5 / p); + l = s * Math.pow(n, 2) + .75; + }else if(n < (2.5 / p)){ + n -= (2.25 / p); + l = s * Math.pow(n, 2) + .9375; + }else{ + n -= (2.625 / p); + l = s * Math.pow(n, 2) + .984375; + } + return l; + }, + + bounceInOut: function(/* Decimal? */n){ + // summary: + // An easing function that 'bounces' at the beginning and end of the Animation + if(n < 0.5){ return easingFuncs.bounceIn(n * 2) / 2; } + return (easingFuncs.bounceOut(n * 2 - 1) / 2) + 0.5; // Decimal + } +}; + +lang.setObject("dojo.fx.easing", easingFuncs); + +return easingFuncs; +}); + +}, +'dojox/charting/action2d/Highlight':function(){ +define("dojox/charting/action2d/Highlight", ["dojo/_base/kernel", "dojo/_base/lang", "dojo/_base/declare", "dojo/_base/Color", "dojo/_base/connect", "dojox/color/_base", + "./PlotAction", "dojo/fx/easing", "dojox/gfx/fx"], + function(dojo, lang, declare, Color, hub, c, PlotAction, dfe, dgf){ + + /*===== + dojo.declare("dojox.charting.action2d.__HighlightCtorArgs", dojox.charting.action2d.__PlotActionCtorArgs, { + // summary: + // Additional arguments for highlighting actions. + + // highlight: String|dojo.Color|Function? + // Either a color or a function that creates a color when highlighting happens. + highlight: null + }); + var PlotAction = dojox.charting.action2d.PlotAction; + =====*/ + + var DEFAULT_SATURATION = 100, // % + DEFAULT_LUMINOSITY1 = 75, // % + DEFAULT_LUMINOSITY2 = 50, // % + cc = function(color){ + return function(){ return color; }; + }, + + hl = function(color){ + var a = new c.Color(color), + x = a.toHsl(); + if(x.s == 0){ + x.l = x.l < 50 ? 100 : 0; + }else{ + x.s = DEFAULT_SATURATION; + if(x.l < DEFAULT_LUMINOSITY2){ + x.l = DEFAULT_LUMINOSITY1; + }else if(x.l > DEFAULT_LUMINOSITY1){ + x.l = DEFAULT_LUMINOSITY2; + }else{ + x.l = x.l - DEFAULT_LUMINOSITY2 > DEFAULT_LUMINOSITY1 - x.l ? + DEFAULT_LUMINOSITY2 : DEFAULT_LUMINOSITY1; + } + } + return c.fromHsl(x); + }; + + return declare("dojox.charting.action2d.Highlight", PlotAction, { + // summary: + // Creates a highlighting action on a plot, where an element on that plot + // has a highlight on it. + + // the data description block for the widget parser + defaultParams: { + duration: 400, // duration of the action in ms + easing: dfe.backOut // easing for the action + }, + optionalParams: { + highlight: "red" // name for the highlight color + // programmatic instantiation can use functions and color objects + }, + + constructor: function(chart, plot, kwArgs){ + // summary: + // Create the highlighting action and connect it to the plot. + // chart: dojox.charting.Chart + // The chart this action belongs to. + // plot: String? + // The plot this action is attached to. If not passed, "default" is assumed. + // kwArgs: charting.action2d.__HighlightCtorArgs? + // Optional keyword arguments object for setting parameters. + var a = kwArgs && kwArgs.highlight; + this.colorFun = a ? (lang.isFunction(a) ? a : cc(a)) : hl; + + this.connect(); + }, + + process: function(o){ + // summary: + // Process the action on the given object. + // o: dojox.gfx.Shape + // The object on which to process the highlighting action. + if(!o.shape || !(o.type in this.overOutEvents)){ return; } + + var runName = o.run.name, index = o.index, anim, startFill, endFill; + + if(runName in this.anim){ + anim = this.anim[runName][index]; + }else{ + this.anim[runName] = {}; + } + + if(anim){ + anim.action.stop(true); + }else{ + var color = o.shape.getFill(); + if(!color || !(color instanceof Color)){ + return; + } + this.anim[runName][index] = anim = { + start: color, + end: this.colorFun(color) + }; + } + + var start = anim.start, end = anim.end; + if(o.type == "onmouseout"){ + // swap colors + var t = start; + start = end; + end = t; + } + + anim.action = dgf.animateFill({ + shape: o.shape, + duration: this.duration, + easing: this.easing, + color: {start: start, end: end} + }); + if(o.type == "onmouseout"){ + hub.connect(anim.action, "onEnd", this, function(){ + if(this.anim[runName]){ + delete this.anim[runName][index]; + } + }); + } + anim.action.play(); + } + }); + +}); + +}, +'dojox/color/Palette':function(){ +define("dojox/color/Palette", ["dojo/_base/kernel", "../main", "dojo/_base/lang", "dojo/_base/array", "./_base"], + function(dojo, dojox, lang, arr, dxc){ + + /*************************************************************** + * dojox.color.Palette + * + * The Palette object is loosely based on the color palettes + * at Kuler (http://kuler.adobe.com). They are 5 color palettes + * with the base color considered to be the third color in the + * palette (for generation purposes). + * + * Palettes can be generated from well-known algorithms or they + * can be manually created by passing an array to the constructor. + * + * Palettes can be transformed, using a set of specific params + * similar to the way shapes can be transformed with dojox.gfx. + * However, unlike with transformations in dojox.gfx, transforming + * a palette will return you a new Palette object, in effect + * a clone of the original. + ***************************************************************/ + + // ctor ---------------------------------------------------------------------------- + dxc.Palette = function(/* String|Array|dojox.color.Color|dojox.color.Palette */base){ + // summary: + // An object that represents a palette of colors. + // description: + // A Palette is a representation of a set of colors. While the standard + // number of colors contained in a palette is 5, it can really handle any + // number of colors. + // + // A palette is useful for the ability to transform all the colors in it + // using a simple object-based approach. In addition, you can generate + // palettes using dojox.color.Palette.generate; these generated palettes + // are based on the palette generators at http://kuler.adobe.com. + // + // colors: dojox.color.Color[] + // The actual color references in this palette. + this.colors = []; + if(base instanceof dxc.Palette){ + this.colors = base.colors.slice(0); + } + else if(base instanceof dxc.Color){ + this.colors = [ null, null, base, null, null ]; + } + else if(lang.isArray(base)){ + this.colors = arr.map(base.slice(0), function(item){ + if(lang.isString(item)){ return new dxc.Color(item); } + return item; + }); + } + else if (lang.isString(base)){ + this.colors = [ null, null, new dxc.Color(base), null, null ]; + } + } + + // private functions --------------------------------------------------------------- + + // transformations + function tRGBA(p, param, val){ + var ret = new dxc.Palette(); + ret.colors = []; + arr.forEach(p.colors, function(item){ + var r=(param=="dr")?item.r+val:item.r, + g=(param=="dg")?item.g+val:item.g, + b=(param=="db")?item.b+val:item.b, + a=(param=="da")?item.a+val:item.a + ret.colors.push(new dxc.Color({ + r: Math.min(255, Math.max(0, r)), + g: Math.min(255, Math.max(0, g)), + b: Math.min(255, Math.max(0, b)), + a: Math.min(1, Math.max(0, a)) + })); + }); + return ret; + } + + function tCMY(p, param, val){ + var ret = new dxc.Palette(); + ret.colors = []; + arr.forEach(p.colors, function(item){ + var o=item.toCmy(), + c=(param=="dc")?o.c+val:o.c, + m=(param=="dm")?o.m+val:o.m, + y=(param=="dy")?o.y+val:o.y; + ret.colors.push(dxc.fromCmy( + Math.min(100, Math.max(0, c)), + Math.min(100, Math.max(0, m)), + Math.min(100, Math.max(0, y)) + )); + }); + return ret; + } + + function tCMYK(p, param, val){ + var ret = new dxc.Palette(); + ret.colors = []; + arr.forEach(p.colors, function(item){ + var o=item.toCmyk(), + c=(param=="dc")?o.c+val:o.c, + m=(param=="dm")?o.m+val:o.m, + y=(param=="dy")?o.y+val:o.y, + k=(param=="dk")?o.b+val:o.b; + ret.colors.push(dxc.fromCmyk( + Math.min(100, Math.max(0, c)), + Math.min(100, Math.max(0, m)), + Math.min(100, Math.max(0, y)), + Math.min(100, Math.max(0, k)) + )); + }); + return ret; + } + + function tHSL(p, param, val){ + var ret = new dxc.Palette(); + ret.colors = []; + arr.forEach(p.colors, function(item){ + var o=item.toHsl(), + h=(param=="dh")?o.h+val:o.h, + s=(param=="ds")?o.s+val:o.s, + l=(param=="dl")?o.l+val:o.l; + ret.colors.push(dxc.fromHsl(h%360, Math.min(100, Math.max(0, s)), Math.min(100, Math.max(0, l)))); + }); + return ret; + } + + function tHSV(p, param, val){ + var ret = new dxc.Palette(); + ret.colors = []; + arr.forEach(p.colors, function(item){ + var o=item.toHsv(), + h=(param=="dh")?o.h+val:o.h, + s=(param=="ds")?o.s+val:o.s, + v=(param=="dv")?o.v+val:o.v; + ret.colors.push(dxc.fromHsv(h%360, Math.min(100, Math.max(0, s)), Math.min(100, Math.max(0, v)))); + }); + return ret; + } + + // helper functions + function rangeDiff(val, low, high){ + // given the value in a range from 0 to high, find the equiv + // using the range low to high. + return high-((high-val)*((high-low)/high)); + } + + // object methods --------------------------------------------------------------- + lang.extend(dxc.Palette, { + transform: function(/* dojox.color.Palette.__transformArgs */kwArgs){ + // summary: + // Transform the palette using a specific transformation function + // and a set of transformation parameters. + // description: + // {palette}.transform is a simple way to uniformly transform + // all of the colors in a palette using any of 5 formulae: + // RGBA, HSL, HSV, CMYK or CMY. + // + // Once the forumula to be used is determined, you can pass any + // number of parameters based on the formula "d"[param]; for instance, + // { use: "rgba", dr: 20, dg: -50 } will take all of the colors in + // palette, add 20 to the R value and subtract 50 from the G value. + // + // Unlike other types of transformations, transform does *not* alter + // the original palette but will instead return a new one. + var fn=tRGBA; // the default transform function. + if(kwArgs.use){ + // we are being specific about the algo we want to use. + var use=kwArgs.use.toLowerCase(); + if(use.indexOf("hs")==0){ + if(use.charAt(2)=="l"){ fn=tHSL; } + else { fn=tHSV; } + } + else if(use.indexOf("cmy")==0){ + if(use.charAt(3)=="k"){ fn=tCMYK; } + else { fn=tCMY; } + } + } + // try to guess the best choice. + else if("dc" in kwArgs || "dm" in kwArgs || "dy" in kwArgs){ + if("dk" in kwArgs){ fn = tCMYK; } + else { fn = tCMY; } + } + else if("dh" in kwArgs || "ds" in kwArgs){ + if("dv" in kwArgs){ fn = tHSV; } + else { fn = tHSL; } + } + + var palette = this; + for(var p in kwArgs){ + // ignore use + if(p=="use"){ continue; } + palette = fn(palette, p, kwArgs[p]); + } + return palette; // dojox.color.Palette + }, + clone: function(){ + // summary: + // Clones the current palette. + return new dxc.Palette(this); // dojox.color.Palette + } + }); + +/*===== +dojox.color.Palette.__transformArgs = function(use, dr, dg, db, da, dc, dm, dy, dk, dh, ds, dv, dl){ + // summary: + // The keywords argument to be passed to the dojox.color.Palette.transform function. Note that + // while all arguments are optional, *some* arguments must be passed. The basic concept is that + // you pass a delta value for a specific aspect of a color model (or multiple aspects of the same + // color model); for instance, if you wish to transform a palette based on the HSV color model, + // you would pass one of "dh", "ds", or "dv" as a value. + // + // use: String? + // Specify the color model to use for the transformation. Can be "rgb", "rgba", "hsv", "hsl", "cmy", "cmyk". + // dr: Number? + // The delta to be applied to the red aspect of the RGB/RGBA color model. + // dg: Number? + // The delta to be applied to the green aspect of the RGB/RGBA color model. + // db: Number? + // The delta to be applied to the blue aspect of the RGB/RGBA color model. + // da: Number? + // The delta to be applied to the alpha aspect of the RGBA color model. + // dc: Number? + // The delta to be applied to the cyan aspect of the CMY/CMYK color model. + // dm: Number? + // The delta to be applied to the magenta aspect of the CMY/CMYK color model. + // dy: Number? + // The delta to be applied to the yellow aspect of the CMY/CMYK color model. + // dk: Number? + // The delta to be applied to the black aspect of the CMYK color model. + // dh: Number? + // The delta to be applied to the hue aspect of the HSL/HSV color model. + // ds: Number? + // The delta to be applied to the saturation aspect of the HSL/HSV color model. + // dl: Number? + // The delta to be applied to the luminosity aspect of the HSL color model. + // dv: Number? + // The delta to be applied to the value aspect of the HSV color model. + this.use = use; + this.dr = dr; + this.dg = dg; + this.db = db; + this.da = da; + this.dc = dc; + this.dm = dm; + this.dy = dy; + this.dk = dk; + this.dh = dh; + this.ds = ds; + this.dl = dl; + this.dv = dv; +} +dojox.color.Palette.__generatorArgs = function(base){ + // summary: + // The keyword arguments object used to create a palette based on a base color. + // + // base: dojo.Color + // The base color to be used to generate the palette. + this.base = base; +} +dojox.color.Palette.__analogousArgs = function(base, high, low){ + // summary: + // The keyword arguments object that is used to create a 5 color palette based on the + // analogous rules as implemented at http://kuler.adobe.com, using the HSV color model. + // + // base: dojo.Color + // The base color to be used to generate the palette. + // high: Number? + // The difference between the hue of the base color and the highest hue. In degrees, default is 60. + // low: Number? + // The difference between the hue of the base color and the lowest hue. In degrees, default is 18. + this.base = base; + this.high = high; + this.low = low; +} +dojox.color.Palette.__splitComplementaryArgs = function(base, da){ + // summary: + // The keyword arguments object used to create a palette based on the split complementary rules + // as implemented at http://kuler.adobe.com. + // + // base: dojo.Color + // The base color to be used to generate the palette. + // da: Number? + // The delta angle to be used to determine where the split for the complementary rules happen. + // In degrees, the default is 30. + this.base = base; + this.da = da; +} +=====*/ + lang.mixin(dxc.Palette, { + generators: { + analogous:function(/* dojox.color.Palette.__analogousArgs */args){ + // summary: + // Create a 5 color palette based on the analogous rules as implemented at + // http://kuler.adobe.com. + var high=args.high||60, // delta between base hue and highest hue (subtracted from base) + low=args.low||18, // delta between base hue and lowest hue (added to base) + base = lang.isString(args.base)?new dxc.Color(args.base):args.base, + hsv=base.toHsv(); + + // generate our hue angle differences + var h=[ + (hsv.h+low+360)%360, + (hsv.h+Math.round(low/2)+360)%360, + hsv.h, + (hsv.h-Math.round(high/2)+360)%360, + (hsv.h-high+360)%360 + ]; + + var s1=Math.max(10, (hsv.s<=95)?hsv.s+5:(100-(hsv.s-95))), + s2=(hsv.s>1)?hsv.s-1:21-hsv.s, + v1=(hsv.v>=92)?hsv.v-9:Math.max(hsv.v+9, 20), + v2=(hsv.v<=90)?Math.max(hsv.v+5, 20):(95+Math.ceil((hsv.v-90)/2)), + s=[ s1, s2, hsv.s, s1, s1 ], + v=[ v1, v2, hsv.v, v1, v2 ] + + return new dxc.Palette(arr.map(h, function(hue, i){ + return dxc.fromHsv(hue, s[i], v[i]); + })); // dojox.color.Palette + }, + + monochromatic: function(/* dojox.color.Palette.__generatorArgs */args){ + // summary: + // Create a 5 color palette based on the monochromatic rules as implemented at + // http://kuler.adobe.com. + var base = lang.isString(args.base)?new dxc.Color(args.base):args.base, + hsv = base.toHsv(); + + // figure out the saturation and value + var s1 = (hsv.s-30>9)?hsv.s-30:hsv.s+30, + s2 = hsv.s, + v1 = rangeDiff(hsv.v, 20, 100), + v2 = (hsv.v-20>20)?hsv.v-20:hsv.v+60, + v3 = (hsv.v-50>20)?hsv.v-50:hsv.v+30; + + return new dxc.Palette([ + dxc.fromHsv(hsv.h, s1, v1), + dxc.fromHsv(hsv.h, s2, v3), + base, + dxc.fromHsv(hsv.h, s1, v3), + dxc.fromHsv(hsv.h, s2, v2) + ]); // dojox.color.Palette + }, + + triadic: function(/* dojox.color.Palette.__generatorArgs */args){ + // summary: + // Create a 5 color palette based on the triadic rules as implemented at + // http://kuler.adobe.com. + var base = lang.isString(args.base)?new dxc.Color(args.base):args.base, + hsv = base.toHsv(); + + var h1 = (hsv.h+57+360)%360, + h2 = (hsv.h-157+360)%360, + s1 = (hsv.s>20)?hsv.s-10:hsv.s+10, + s2 = (hsv.s>90)?hsv.s-10:hsv.s+10, + s3 = (hsv.s>95)?hsv.s-5:hsv.s+5, + v1 = (hsv.v-20>20)?hsv.v-20:hsv.v+20, + v2 = (hsv.v-30>20)?hsv.v-30:hsv.v+30, + v3 = (hsv.v-30>70)?hsv.v-30:hsv.v+30; + + return new dxc.Palette([ + dxc.fromHsv(h1, s1, hsv.v), + dxc.fromHsv(hsv.h, s2, v2), + base, + dxc.fromHsv(h2, s2, v1), + dxc.fromHsv(h2, s3, v3) + ]); // dojox.color.Palette + }, + + complementary: function(/* dojox.color.Palette.__generatorArgs */args){ + // summary: + // Create a 5 color palette based on the complementary rules as implemented at + // http://kuler.adobe.com. + var base = lang.isString(args.base)?new dxc.Color(args.base):args.base, + hsv = base.toHsv(); + + var h1 = ((hsv.h*2)+137<360)?(hsv.h*2)+137:Math.floor(hsv.h/2)-137, + s1 = Math.max(hsv.s-10, 0), + s2 = rangeDiff(hsv.s, 10, 100), + s3 = Math.min(100, hsv.s+20), + v1 = Math.min(100, hsv.v+30), + v2 = (hsv.v>20)?hsv.v-30:hsv.v+30; + + return new dxc.Palette([ + dxc.fromHsv(hsv.h, s1, v1), + dxc.fromHsv(hsv.h, s2, v2), + base, + dxc.fromHsv(h1, s3, v2), + dxc.fromHsv(h1, hsv.s, hsv.v) + ]); // dojox.color.Palette + }, + + splitComplementary: function(/* dojox.color.Palette.__splitComplementaryArgs */args){ + // summary: + // Create a 5 color palette based on the split complementary rules as implemented at + // http://kuler.adobe.com. + var base = lang.isString(args.base)?new dxc.Color(args.base):args.base, + dangle = args.da || 30, + hsv = base.toHsv(); + + var baseh = ((hsv.h*2)+137<360)?(hsv.h*2)+137:Math.floor(hsv.h/2)-137, + h1 = (baseh-dangle+360)%360, + h2 = (baseh+dangle)%360, + s1 = Math.max(hsv.s-10, 0), + s2 = rangeDiff(hsv.s, 10, 100), + s3 = Math.min(100, hsv.s+20), + v1 = Math.min(100, hsv.v+30), + v2 = (hsv.v>20)?hsv.v-30:hsv.v+30; + + return new dxc.Palette([ + dxc.fromHsv(h1, s1, v1), + dxc.fromHsv(h1, s2, v2), + base, + dxc.fromHsv(h2, s3, v2), + dxc.fromHsv(h2, hsv.s, hsv.v) + ]); // dojox.color.Palette + }, + + compound: function(/* dojox.color.Palette.__generatorArgs */args){ + // summary: + // Create a 5 color palette based on the compound rules as implemented at + // http://kuler.adobe.com. + var base = lang.isString(args.base)?new dxc.Color(args.base):args.base, + hsv = base.toHsv(); + + var h1 = ((hsv.h*2)+18<360)?(hsv.h*2)+18:Math.floor(hsv.h/2)-18, + h2 = ((hsv.h*2)+120<360)?(hsv.h*2)+120:Math.floor(hsv.h/2)-120, + h3 = ((hsv.h*2)+99<360)?(hsv.h*2)+99:Math.floor(hsv.h/2)-99, + s1 = (hsv.s-40>10)?hsv.s-40:hsv.s+40, + s2 = (hsv.s-10>80)?hsv.s-10:hsv.s+10, + s3 = (hsv.s-25>10)?hsv.s-25:hsv.s+25, + v1 = (hsv.v-40>10)?hsv.v-40:hsv.v+40, + v2 = (hsv.v-20>80)?hsv.v-20:hsv.v+20, + v3 = Math.max(hsv.v, 20); + + return new dxc.Palette([ + dxc.fromHsv(h1, s1, v1), + dxc.fromHsv(h1, s2, v2), + base, + dxc.fromHsv(h2, s3, v3), + dxc.fromHsv(h3, s2, v2) + ]); // dojox.color.Palette + }, + + shades: function(/* dojox.color.Palette.__generatorArgs */args){ + // summary: + // Create a 5 color palette based on the shades rules as implemented at + // http://kuler.adobe.com. + var base = lang.isString(args.base)?new dxc.Color(args.base):args.base, + hsv = base.toHsv(); + + var s = (hsv.s==100 && hsv.v==0)?0:hsv.s, + v1 = (hsv.v-50>20)?hsv.v-50:hsv.v+30, + v2 = (hsv.v-25>=20)?hsv.v-25:hsv.v+55, + v3 = (hsv.v-75>=20)?hsv.v-75:hsv.v+5, + v4 = Math.max(hsv.v-10, 20); + + return new dxc.Palette([ + new dxc.fromHsv(hsv.h, s, v1), + new dxc.fromHsv(hsv.h, s, v2), + base, + new dxc.fromHsv(hsv.h, s, v3), + new dxc.fromHsv(hsv.h, s, v4) + ]); // dojox.color.Palette + } + }, + generate: function(/* String|dojox.color.Color */base, /* Function|String */type){ + // summary: + // Generate a new Palette using any of the named functions in + // dojox.color.Palette.generators or an optional function definition. Current + // generators include "analogous", "monochromatic", "triadic", "complementary", + // "splitComplementary", and "shades". + if(lang.isFunction(type)){ + return type({ base: base }); // dojox.color.Palette + } + else if(dxc.Palette.generators[type]){ + return dxc.Palette.generators[type]({ base: base }); // dojox.color.Palette + } + throw new Error("dojox.color.Palette.generate: the specified generator ('" + type + "') does not exist."); + } + }); + + return dxc.Palette; +}); + +}, +'dijit/a11y':function(){ +define("dijit/a11y", [ + "dojo/_base/array", // array.forEach array.map + "dojo/_base/config", // defaultDuration + "dojo/_base/declare", // declare + "dojo/dom", // dom.byId + "dojo/dom-attr", // domAttr.attr domAttr.has + "dojo/dom-style", // style.style + "dojo/_base/sniff", // has("ie") + "./_base/manager", // manager._isElementShown + "." // for exporting methods to dijit namespace +], function(array, config, declare, dom, domAttr, domStyle, has, manager, dijit){ + + // module: + // dijit/a11y + // summary: + // Accessibility utility functions (keyboard, tab stops, etc.) + + var shown = (dijit._isElementShown = function(/*Element*/ elem){ + var s = domStyle.get(elem); + return (s.visibility != "hidden") + && (s.visibility != "collapsed") + && (s.display != "none") + && (domAttr.get(elem, "type") != "hidden"); + }); + + dijit.hasDefaultTabStop = function(/*Element*/ elem){ + // summary: + // Tests if element is tab-navigable even without an explicit tabIndex setting + + // No explicit tabIndex setting, need to investigate node type + switch(elem.nodeName.toLowerCase()){ + case "a": + // An <a> w/out a tabindex is only navigable if it has an href + return domAttr.has(elem, "href"); + case "area": + case "button": + case "input": + case "object": + case "select": + case "textarea": + // These are navigable by default + return true; + case "iframe": + // If it's an editor <iframe> then it's tab navigable. + var body; + try{ + // non-IE + var contentDocument = elem.contentDocument; + if("designMode" in contentDocument && contentDocument.designMode == "on"){ + return true; + } + body = contentDocument.body; + }catch(e1){ + // contentWindow.document isn't accessible within IE7/8 + // if the iframe.src points to a foreign url and this + // page contains an element, that could get focus + try{ + body = elem.contentWindow.document.body; + }catch(e2){ + return false; + } + } + return body && (body.contentEditable == 'true' || + (body.firstChild && body.firstChild.contentEditable == 'true')); + default: + return elem.contentEditable == 'true'; + } + }; + + var isTabNavigable = (dijit.isTabNavigable = function(/*Element*/ elem){ + // summary: + // Tests if an element is tab-navigable + + // TODO: convert (and rename method) to return effective tabIndex; will save time in _getTabNavigable() + if(domAttr.get(elem, "disabled")){ + return false; + }else if(domAttr.has(elem, "tabIndex")){ + // Explicit tab index setting + return domAttr.get(elem, "tabIndex") >= 0; // boolean + }else{ + // No explicit tabIndex setting, so depends on node type + return dijit.hasDefaultTabStop(elem); + } + }); + + dijit._getTabNavigable = function(/*DOMNode*/ root){ + // summary: + // Finds descendants of the specified root node. + // + // description: + // Finds the following descendants of the specified root node: + // * the first tab-navigable element in document order + // without a tabIndex or with tabIndex="0" + // * the last tab-navigable element in document order + // without a tabIndex or with tabIndex="0" + // * the first element in document order with the lowest + // positive tabIndex value + // * the last element in document order with the highest + // positive tabIndex value + var first, last, lowest, lowestTabindex, highest, highestTabindex, radioSelected = {}; + + function radioName(node){ + // If this element is part of a radio button group, return the name for that group. + return node && node.tagName.toLowerCase() == "input" && + node.type && node.type.toLowerCase() == "radio" && + node.name && node.name.toLowerCase(); + } + + var walkTree = function(/*DOMNode*/parent){ + for(var child = parent.firstChild; child; child = child.nextSibling){ + // Skip text elements, hidden elements, and also non-HTML elements (those in custom namespaces) in IE, + // since show() invokes getAttribute("type"), which crash on VML nodes in IE. + if(child.nodeType != 1 || (has("ie") && child.scopeName !== "HTML") || !shown(child)){ + continue; + } + + if(isTabNavigable(child)){ + var tabindex = domAttr.get(child, "tabIndex"); + if(!domAttr.has(child, "tabIndex") || tabindex == 0){ + if(!first){ + first = child; + } + last = child; + }else if(tabindex > 0){ + if(!lowest || tabindex < lowestTabindex){ + lowestTabindex = tabindex; + lowest = child; + } + if(!highest || tabindex >= highestTabindex){ + highestTabindex = tabindex; + highest = child; + } + } + var rn = radioName(child); + if(domAttr.get(child, "checked") && rn){ + radioSelected[rn] = child; + } + } + if(child.nodeName.toUpperCase() != 'SELECT'){ + walkTree(child); + } + } + }; + if(shown(root)){ + walkTree(root); + } + function rs(node){ + // substitute checked radio button for unchecked one, if there is a checked one with the same name. + return radioSelected[radioName(node)] || node; + } + + return { first: rs(first), last: rs(last), lowest: rs(lowest), highest: rs(highest) }; + }; + dijit.getFirstInTabbingOrder = function(/*String|DOMNode*/ root){ + // summary: + // Finds the descendant of the specified root node + // that is first in the tabbing order + var elems = dijit._getTabNavigable(dom.byId(root)); + return elems.lowest ? elems.lowest : elems.first; // DomNode + }; + + dijit.getLastInTabbingOrder = function(/*String|DOMNode*/ root){ + // summary: + // Finds the descendant of the specified root node + // that is last in the tabbing order + var elems = dijit._getTabNavigable(dom.byId(root)); + return elems.last ? elems.last : elems.highest; // DomNode + }; + + return { + hasDefaultTabStop: dijit.hasDefaultTabStop, + isTabNavigable: dijit.isTabNavigable, + _getTabNavigable: dijit._getTabNavigable, + getFirstInTabbingOrder: dijit.getFirstInTabbingOrder, + getLastInTabbingOrder: dijit.getLastInTabbingOrder + }; +}); + +}, +'dojox/charting/axis2d/Base':function(){ +define("dojox/charting/axis2d/Base", ["dojo/_base/declare", "../Element"], + function(declare, Element){ +/*===== +var Element = dojox.charting.Element; +=====*/ +return declare("dojox.charting.axis2d.Base", Element, { + // summary: + // The base class for any axis. This is more of an interface/API + // definition than anything else; see dojox.charting.axis2d.Default + // for more details. + constructor: function(chart, kwArgs){ + // summary: + // Return a new base axis. + // chart: dojox.charting.Chart + // The chart this axis belongs to. + // kwArgs: dojox.charting.axis2d.__AxisCtorArgs? + // An optional arguments object to define the axis parameters. + this.vertical = kwArgs && kwArgs.vertical; + }, + clear: function(){ + // summary: + // Stub function for clearing the axis. + // returns: dojox.charting.axis2d.Base + // A reference to the axis for functional chaining. + return this; // dojox.charting.axis2d.Base + }, + initialized: function(){ + // summary: + // Return a flag as to whether or not this axis has been initialized. + // returns: Boolean + // If the axis is initialized or not. + return false; // Boolean + }, + calculate: function(min, max, span){ + // summary: + // Stub function to run the calcuations needed for drawing this axis. + // returns: dojox.charting.axis2d.Base + // A reference to the axis for functional chaining. + return this; // dojox.charting.axis2d.Base + }, + getScaler: function(){ + // summary: + // A stub function to return the scaler object created during calculate. + // returns: Object + // The scaler object (see dojox.charting.scaler.linear for more information) + return null; // Object + }, + getTicks: function(){ + // summary: + // A stub function to return the object that helps define how ticks are rendered. + // returns: Object + // The ticks object. + return null; // Object + }, + getOffsets: function(){ + // summary: + // A stub function to return any offsets needed for axis and series rendering. + // returns: Object + // An object of the form { l, r, t, b }. + return {l: 0, r: 0, t: 0, b: 0}; // Object + }, + render: function(dim, offsets){ + // summary: + // Stub function to render this axis. + // returns: dojox.charting.axis2d.Base + // A reference to the axis for functional chaining. + this.dirty = false; + return this; // dojox.charting.axis2d.Base + } +}); +}); + +}, +'dojox/charting/plot2d/Grid':function(){ +define("dojox/charting/plot2d/Grid", ["dojo/_base/lang", "dojo/_base/declare", "dojo/_base/connect", "dojo/_base/array", + "../Element", "./common", "dojox/lang/utils", "dojox/gfx/fx"], + function(lang, declare, hub, arr, Element, dc, du, fx){ + + /*===== + dojo.declare("dojox.charting.plot2d.__GridCtorArgs", dojox.charting.plot2d.__DefaultCtorArgs, { + // summary: + // A special keyword arguments object that is specific to a grid "plot". + + // hMajorLines: Boolean? + // Whether to show lines at the major ticks along the horizontal axis. Default is true. + hMajorLines: true, + + // hMinorLines: Boolean? + // Whether to show lines at the minor ticks along the horizontal axis. Default is false. + hMinorLines: false, + + // vMajorLines: Boolean? + // Whether to show lines at the major ticks along the vertical axis. Default is true. + vMajorLines: true, + + // vMinorLines: Boolean? + // Whether to show lines at the major ticks along the vertical axis. Default is false. + vMinorLines: false, + + // hStripes: String? + // Whether or not to show stripes (alternating fills) along the horizontal axis. Default is "none". + hStripes: "none", + + // vStripes: String? + // Whether or not to show stripes (alternating fills) along the vertical axis. Default is "none". + vStripes: "none", + + // enableCache: Boolean? + // Whether the grid lines are cached from one rendering to another. This improves the rendering performance of + // successive rendering but penalize the first rendering. Default false. + enableCache: false + }); + var Element = dojox.charting.plot2d.Element; + =====*/ + + return declare("dojox.charting.plot2d.Grid", Element, { + // summary: + // A "faux" plot that can be placed behind other plots to represent + // a grid against which other plots can be easily measured. + defaultParams: { + hAxis: "x", // use a horizontal axis named "x" + vAxis: "y", // use a vertical axis named "y" + hMajorLines: true, // draw horizontal major lines + hMinorLines: false, // draw horizontal minor lines + vMajorLines: true, // draw vertical major lines + vMinorLines: false, // draw vertical minor lines + hStripes: "none", // TBD + vStripes: "none", // TBD + animate: null, // animate bars into place + enableCache: false + }, + optionalParams: {}, // no optional parameters + + constructor: function(chart, kwArgs){ + // summary: + // Create the faux Grid plot. + // chart: dojox.charting.Chart + // The chart this plot belongs to. + // kwArgs: dojox.charting.plot2d.__GridCtorArgs? + // An optional keyword arguments object to help define the parameters of the underlying grid. + this.opt = lang.clone(this.defaultParams); + du.updateWithObject(this.opt, kwArgs); + this.hAxis = this.opt.hAxis; + this.vAxis = this.opt.vAxis; + this.dirty = true; + this.animate = this.opt.animate; + this.zoom = null, + this.zoomQueue = []; // zooming action task queue + this.lastWindow = {vscale: 1, hscale: 1, xoffset: 0, yoffset: 0}; + if(this.opt.enableCache){ + this._lineFreePool = []; + this._lineUsePool = []; + } + }, + clear: function(){ + // summary: + // Clear out any parameters set on this plot. + // returns: dojox.charting.plot2d.Grid + // The reference to this plot for functional chaining. + this._hAxis = null; + this._vAxis = null; + this.dirty = true; + return this; // dojox.charting.plot2d.Grid + }, + setAxis: function(axis){ + // summary: + // Set an axis for this plot. + // returns: dojox.charting.plot2d.Grid + // The reference to this plot for functional chaining. + if(axis){ + this[axis.vertical ? "_vAxis" : "_hAxis"] = axis; + } + return this; // dojox.charting.plot2d.Grid + }, + addSeries: function(run){ + // summary: + // Ignored but included as a dummy method. + // returns: dojox.charting.plot2d.Grid + // The reference to this plot for functional chaining. + return this; // dojox.charting.plot2d.Grid + }, + getSeriesStats: function(){ + // summary: + // Returns default stats (irrelevant for this type of plot). + // returns: Object + // {hmin, hmax, vmin, vmax} min/max in both directions. + return lang.delegate(dc.defaultStats); + }, + initializeScalers: function(){ + // summary: + // Does nothing (irrelevant for this type of plot). + return this; + }, + isDirty: function(){ + // summary: + // Return whether or not this plot needs to be redrawn. + // returns: Boolean + // If this plot needs to be rendered, this will return true. + return this.dirty || this._hAxis && this._hAxis.dirty || this._vAxis && this._vAxis.dirty; // Boolean + }, + performZoom: function(dim, offsets){ + // summary: + // Create/alter any zooming windows on this plot. + // dim: Object + // An object of the form { width, height }. + // offsets: Object + // An object of the form { l, r, t, b }. + // returns: dojox.charting.plot2d.Grid + // A reference to this plot for functional chaining. + + // get current zooming various + var vs = this._vAxis.scale || 1, + hs = this._hAxis.scale || 1, + vOffset = dim.height - offsets.b, + hBounds = this._hAxis.getScaler().bounds, + xOffset = (hBounds.from - hBounds.lower) * hBounds.scale, + vBounds = this._vAxis.getScaler().bounds, + yOffset = (vBounds.from - vBounds.lower) * vBounds.scale, + // get incremental zooming various + rVScale = vs / this.lastWindow.vscale, + rHScale = hs / this.lastWindow.hscale, + rXOffset = (this.lastWindow.xoffset - xOffset)/ + ((this.lastWindow.hscale == 1)? hs : this.lastWindow.hscale), + rYOffset = (yOffset - this.lastWindow.yoffset)/ + ((this.lastWindow.vscale == 1)? vs : this.lastWindow.vscale), + + shape = this.group, + anim = fx.animateTransform(lang.delegate({ + shape: shape, + duration: 1200, + transform:[ + {name:"translate", start:[0, 0], end: [offsets.l * (1 - rHScale), vOffset * (1 - rVScale)]}, + {name:"scale", start:[1, 1], end: [rHScale, rVScale]}, + {name:"original"}, + {name:"translate", start: [0, 0], end: [rXOffset, rYOffset]} + ]}, this.zoom)); + + lang.mixin(this.lastWindow, {vscale: vs, hscale: hs, xoffset: xOffset, yoffset: yOffset}); + //add anim to zooming action queue, + //in order to avoid several zooming action happened at the same time + this.zoomQueue.push(anim); + //perform each anim one by one in zoomQueue + hub.connect(anim, "onEnd", this, function(){ + this.zoom = null; + this.zoomQueue.shift(); + if(this.zoomQueue.length > 0){ + this.zoomQueue[0].play(); + } + }); + if(this.zoomQueue.length == 1){ + this.zoomQueue[0].play(); + } + return this; // dojox.charting.plot2d.Grid + }, + getRequiredColors: function(){ + // summary: + // Ignored but included as a dummy method. + // returns: Number + // Returns 0, since there are no series associated with this plot type. + return 0; // Number + }, + cleanGroup: function(){ + this.inherited(arguments); + if(this.opt.enableCache){ + this._lineFreePool = this._lineFreePool.concat(this._lineUsePool); + this._lineUsePool = []; + } + }, + createLine: function(creator, params){ + var line; + if(this.opt.enableCache && this._lineFreePool.length > 0){ + line = this._lineFreePool.pop(); + line.setShape(params); + // was cleared, add it back + creator.add(line); + }else{ + line = creator.createLine(params); + } + if(this.opt.enableCache){ + this._lineUsePool.push(line); + } + return line; + }, + render: function(dim, offsets){ + // summary: + // Render the plot on the chart. + // dim: Object + // An object of the form { width, height }. + // offsets: Object + // An object of the form { l, r, t, b }. + // returns: dojox.charting.plot2d.Grid + // A reference to this plot for functional chaining. + if(this.zoom){ + return this.performZoom(dim, offsets); + } + this.dirty = this.isDirty(); + if(!this.dirty){ return this; } + this.cleanGroup(); + var s = this.group, ta = this.chart.theme.axis; + // draw horizontal stripes and lines + try{ + var vScaler = this._vAxis.getScaler(), + vt = vScaler.scaler.getTransformerFromModel(vScaler), + ticks = this._vAxis.getTicks(); + if(ticks != null){ + if(this.opt.hMinorLines){ + arr.forEach(ticks.minor, function(tick){ + var y = dim.height - offsets.b - vt(tick.value); + var hMinorLine = this.createLine(s, { + x1: offsets.l, + y1: y, + x2: dim.width - offsets.r, + y2: y + }).setStroke(ta.minorTick); + if(this.animate){ + this._animateGrid(hMinorLine, "h", offsets.l, offsets.r + offsets.l - dim.width); + } + }, this); + } + if(this.opt.hMajorLines){ + arr.forEach(ticks.major, function(tick){ + var y = dim.height - offsets.b - vt(tick.value); + var hMajorLine = this.createLine(s, { + x1: offsets.l, + y1: y, + x2: dim.width - offsets.r, + y2: y + }).setStroke(ta.majorTick); + if(this.animate){ + this._animateGrid(hMajorLine, "h", offsets.l, offsets.r + offsets.l - dim.width); + } + }, this); + } + } + }catch(e){ + // squelch + } + // draw vertical stripes and lines + try{ + var hScaler = this._hAxis.getScaler(), + ht = hScaler.scaler.getTransformerFromModel(hScaler), + ticks = this._hAxis.getTicks(); + if(this != null){ + if(ticks && this.opt.vMinorLines){ + arr.forEach(ticks.minor, function(tick){ + var x = offsets.l + ht(tick.value); + var vMinorLine = this.createLine(s, { + x1: x, + y1: offsets.t, + x2: x, + y2: dim.height - offsets.b + }).setStroke(ta.minorTick); + if(this.animate){ + this._animateGrid(vMinorLine, "v", dim.height - offsets.b, dim.height - offsets.b - offsets.t); + } + }, this); + } + if(ticks && this.opt.vMajorLines){ + arr.forEach(ticks.major, function(tick){ + var x = offsets.l + ht(tick.value); + var vMajorLine = this.createLine(s, { + x1: x, + y1: offsets.t, + x2: x, + y2: dim.height - offsets.b + }).setStroke(ta.majorTick); + if(this.animate){ + this._animateGrid(vMajorLine, "v", dim.height - offsets.b, dim.height - offsets.b - offsets.t); + } + }, this); + } + } + }catch(e){ + // squelch + } + this.dirty = false; + return this; // dojox.charting.plot2d.Grid + }, + _animateGrid: function(shape, type, offset, size){ + var transStart = type == "h" ? [offset, 0] : [0, offset]; + var scaleStart = type == "h" ? [1/size, 1] : [1, 1/size]; + fx.animateTransform(lang.delegate({ + shape: shape, + duration: 1200, + transform: [ + {name: "translate", start: transStart, end: [0, 0]}, + {name: "scale", start: scaleStart, end: [1, 1]}, + {name: "original"} + ] + }, this.animate)).play(); + } + }); +}); + +}, +'dojox/gfx/utils':function(){ +define("dojox/gfx/utils", ["dojo/_base/kernel","dojo/_base/lang","./_base", "dojo/_base/html","dojo/_base/array", "dojo/_base/window", "dojo/_base/json", + "dojo/_base/Deferred", "dojo/_base/sniff", "require","dojo/_base/config"], + function(kernel, lang, g, html, arr, win, jsonLib, Deferred, has, require, config){ + var gu = g.utils = {}; + /*===== g= dojox.gfx; gu = dojox.gfx.utils; =====*/ + + lang.mixin(gu, { + forEach: function( + /*dojox.gfx.Surface|dojox.gfx.Shape*/ object, + /*Function|String|Array*/ f, /*Object?*/ o + ){ + // summary: + // Takes a shape or a surface and applies a function "f" to in the context of "o" + // (or global, if missing). If "shape" was a surface or a group, it applies the same + // function to all children recursively effectively visiting all shapes of the underlying scene graph. + // object : The gfx container to iterate. + // f : The function to apply. + // o : The scope. + o = o || win.global; + f.call(o, object); + if(object instanceof g.Surface || object instanceof g.Group){ + arr.forEach(object.children, function(shape){ + gu.forEach(shape, f, o); + }); + } + }, + + serialize: function( + /* dojox.gfx.Surface|dojox.gfx.Shape */ object + ){ + // summary: + // Takes a shape or a surface and returns a DOM object, which describes underlying shapes. + var t = {}, v, isSurface = object instanceof g.Surface; + if(isSurface || object instanceof g.Group){ + t.children = arr.map(object.children, gu.serialize); + if(isSurface){ + return t.children; // Array + } + }else{ + t.shape = object.getShape(); + } + if(object.getTransform){ + v = object.getTransform(); + if(v){ t.transform = v; } + } + if(object.getStroke){ + v = object.getStroke(); + if(v){ t.stroke = v; } + } + if(object.getFill){ + v = object.getFill(); + if(v){ t.fill = v; } + } + if(object.getFont){ + v = object.getFont(); + if(v){ t.font = v; } + } + return t; // Object + }, + + toJson: function( + /* dojox.gfx.Surface|dojox.gfx.Shape */ object, + /* Boolean? */ prettyPrint + ){ + // summary: + // Works just like serialize() but returns a JSON string. If prettyPrint is true, the string is pretty-printed to make it more human-readable. + return jsonLib.toJson(gu.serialize(object), prettyPrint); // String + }, + + deserialize: function( + /* dojox.gfx.Surface|dojox.gfx.Shape */ parent, + /* dojox.gfx.Shape|Array */ object + ){ + // summary: + // Takes a surface or a shape and populates it with an object produced by serialize(). + if(object instanceof Array){ + return arr.map(object, lang.hitch(null, gu.deserialize, parent)); // Array + } + var shape = ("shape" in object) ? parent.createShape(object.shape) : parent.createGroup(); + if("transform" in object){ + shape.setTransform(object.transform); + } + if("stroke" in object){ + shape.setStroke(object.stroke); + } + if("fill" in object){ + shape.setFill(object.fill); + } + if("font" in object){ + shape.setFont(object.font); + } + if("children" in object){ + arr.forEach(object.children, lang.hitch(null, gu.deserialize, shape)); + } + return shape; // dojox.gfx.Shape + }, + + fromJson: function( + /* dojox.gfx.Surface|dojox.gfx.Shape */ parent, + /* String */ json){ + // summary: + // Works just like deserialize() but takes a JSON representation of the object. + return gu.deserialize(parent, jsonLib.fromJson(json)); // Array || dojox.gfx.Shape + }, + + toSvg: function(/*GFX object*/surface){ + // summary: + // Function to serialize a GFX surface to SVG text. + // description: + // Function to serialize a GFX surface to SVG text. The value of this output + // is that there are numerous serverside parser libraries that can render + // SVG into images in various formats. This provides a way that GFX objects + // can be captured in a known format and sent serverside for serialization + // into an image. + // surface: + // The GFX surface to serialize. + // returns: + // Deferred object that will be called when SVG serialization is complete. + + //Since the init and even surface creation can be async, we need to + //return a deferred that will be called when content has serialized. + var deferred = new Deferred(); + + if(g.renderer === "svg"){ + //If we're already in SVG mode, this is easy and quick. + try{ + var svg = gu._cleanSvg(gu._innerXML(surface.rawNode)); + deferred.callback(svg); + }catch(e){ + deferred.errback(e); + } + }else{ + //Okay, now we have to get creative with hidden iframes and the like to + //serialize SVG. + if (!gu._initSvgSerializerDeferred) { + gu._initSvgSerializer(); + } + var jsonForm = gu.toJson(surface); + var serializer = function(){ + try{ + var sDim = surface.getDimensions(); + var width = sDim.width; + var height = sDim.height; + + //Create an attach point in the iframe for the contents. + var node = gu._gfxSvgProxy.document.createElement("div"); + gu._gfxSvgProxy.document.body.appendChild(node); + //Set the node scaling. + win.withDoc(gu._gfxSvgProxy.document, function() { + html.style(node, "width", width); + html.style(node, "height", height); + }, this); + + //Create temp surface to render object to and render. + var ts = gu._gfxSvgProxy[dojox._scopeName].gfx.createSurface(node, width, height); + + //It's apparently possible that a suface creation is async, so we need to use + //the whenLoaded function. Probably not needed for SVG, but making it common + var draw = function(surface) { + try{ + gu._gfxSvgProxy[dojox._scopeName].gfx.utils.fromJson(surface, jsonForm); + + //Get contents and remove temp surface. + var svg = gu._cleanSvg(node.innerHTML); + surface.clear(); + surface.destroy(); + gu._gfxSvgProxy.document.body.removeChild(node); + deferred.callback(svg); + }catch(e){ + deferred.errback(e); + } + }; + ts.whenLoaded(null,draw); + }catch (ex) { + deferred.errback(ex); + } + }; + //See if we can call it directly or pass it to the deferred to be + //called on initialization. + if(gu._initSvgSerializerDeferred.fired > 0){ + serializer(); + }else{ + gu._initSvgSerializerDeferred.addCallback(serializer); + } + } + return deferred; //dojo.Deferred that will be called when serialization finishes. + }, + + //iFrame document used for handling SVG serialization. + _gfxSvgProxy: null, + + //Serializer loaded. + _initSvgSerializerDeferred: null, + + _svgSerializerInitialized: function() { + // summary: + // Internal function to call when the serializer init completed. + // tags: + // private + gu._initSvgSerializerDeferred.callback(true); + }, + + _initSvgSerializer: function(){ + // summary: + // Internal function to initialize the hidden iframe where SVG rendering + // will occur. + // tags: + // private + if(!gu._initSvgSerializerDeferred){ + gu._initSvgSerializerDeferred = new Deferred(); + var f = win.doc.createElement("iframe"); + html.style(f, { + display: "none", + position: "absolute", + width: "1em", + height: "1em", + top: "-10000px" + }); + var intv; + if(has("ie")){ + f.onreadystatechange = function(){ + if(f.contentWindow.document.readyState == "complete"){ + f.onreadystatechange = function() {}; + intv = setInterval(function() { + if(f.contentWindow[kernel.scopeMap["dojo"][1]._scopeName] && + f.contentWindow[kernel.scopeMap["dojox"][1]._scopeName].gfx && + f.contentWindow[kernel.scopeMap["dojox"][1]._scopeName].gfx.utils){ + clearInterval(intv); + f.contentWindow.parent[kernel.scopeMap["dojox"][1]._scopeName].gfx.utils._gfxSvgProxy = f.contentWindow; + f.contentWindow.parent[kernel.scopeMap["dojox"][1]._scopeName].gfx.utils._svgSerializerInitialized(); + } + }, 50); + } + }; + }else{ + f.onload = function(){ + f.onload = function() {}; + intv = setInterval(function() { + if(f.contentWindow[kernel.scopeMap["dojo"][1]._scopeName] && + f.contentWindow[kernel.scopeMap["dojox"][1]._scopeName].gfx && + f.contentWindow[kernel.scopeMap["dojox"][1]._scopeName].gfx.utils){ + clearInterval(intv); + f.contentWindow.parent[kernel.scopeMap["dojox"][1]._scopeName].gfx.utils._gfxSvgProxy = f.contentWindow; + f.contentWindow.parent[kernel.scopeMap["dojox"][1]._scopeName].gfx.utils._svgSerializerInitialized(); + } + }, 50); + }; + } + //We have to load the GFX SVG proxy frame. Default is to use the one packaged in dojox. + var uri = (config["dojoxGfxSvgProxyFrameUrl"]||require.toUrl("dojox/gfx/resources/gfxSvgProxyFrame.html")); + f.setAttribute("src", uri.toString()); + win.body().appendChild(f); + } + }, + + _innerXML: function(/*Node*/node){ + // summary: + // Implementation of MS's innerXML function, borrowed from dojox.xml.parser. + // node: + // The node from which to generate the XML text representation. + // tags: + // private + if(node.innerXML){ + return node.innerXML; //String + }else if(node.xml){ + return node.xml; //String + }else if(typeof XMLSerializer != "undefined"){ + return (new XMLSerializer()).serializeToString(node); //String + } + return null; + }, + + _cleanSvg: function(svg) { + // summary: + // Internal function that cleans up artifacts in extracted SVG content. + // tags: + // private + if(svg){ + //Make sure the namespace is set. + if(svg.indexOf("xmlns=\"http://www.w3.org/2000/svg\"") == -1){ + svg = svg.substring(4, svg.length); + svg = "<svg xmlns=\"http://www.w3.org/2000/svg\"" + svg; + } + //Same for xmlns:xlink (missing in Chrome and Safari) + if(svg.indexOf("xmlns:xlink=\"http://www.w3.org/1999/xlink\"") == -1){ + svg = svg.substring(4, svg.length); + svg = "<svg xmlns:xlink=\"http://www.w3.org/1999/xlink\"" + svg; + } + //and add namespace to href attribute if not done yet + //(FF 5+ adds xlink:href but not the xmlns def) + if(svg.indexOf("xlink:href") === -1){ + svg = svg.replace(/href\s*=/g, "xlink:href="); + } + //Do some other cleanup, like stripping out the + //dojoGfx attributes and quoting ids. + svg = svg.replace(/\bdojoGfx\w*\s*=\s*(['"])\w*\1/g, ""); + svg = svg.replace(/\b__gfxObject__\s*=\s*(['"])\w*\1/g, ""); + svg = svg.replace(/[=]([^"']+?)(\s|>)/g,'="$1"$2'); + } + return svg; //Cleaned SVG text. + } + }); + + return gu; +}); + +}, +'dojox/lang/functional/fold':function(){ +define("dojox/lang/functional/fold", ["dojo/_base/lang", "dojo/_base/array", "dojo/_base/window", "./lambda"], + function(lang, arr, win, df){ + +// This module adds high-level functions and related constructs: +// - "fold" family of functions + +// Notes: +// - missing high-level functions are provided with the compatible API: +// foldl, foldl1, foldr, foldr1 +// - missing JS standard functions are provided with the compatible API: +// reduce, reduceRight +// - the fold's counterpart: unfold + +// Defined methods: +// - take any valid lambda argument as the functional argument +// - operate on dense arrays +// - take a string as the array argument +// - take an iterator objects as the array argument (only foldl, foldl1, and reduce) + + var empty = {}; + +/*===== + var df = dojox.lang.functional; + =====*/ + lang.mixin(df, { + // classic reduce-class functions + foldl: function(/*Array|String|Object*/ a, /*Function*/ f, /*Object*/ z, /*Object?*/ o){ + // summary: repeatedly applies a binary function to an array from left + // to right using a seed value as a starting point; returns the final + // value. + if(typeof a == "string"){ a = a.split(""); } + o = o || win.global; f = df.lambda(f); + var i, n; + if(lang.isArray(a)){ + // array + for(i = 0, n = a.length; i < n; z = f.call(o, z, a[i], i, a), ++i); + }else if(typeof a.hasNext == "function" && typeof a.next == "function"){ + // iterator + for(i = 0; a.hasNext(); z = f.call(o, z, a.next(), i++, a)); + }else{ + // object/dictionary + for(i in a){ + if(!(i in empty)){ + z = f.call(o, z, a[i], i, a); + } + } + } + return z; // Object + }, + foldl1: function(/*Array|String|Object*/ a, /*Function|String|Array*/ f, /*Object?*/ o){ + // summary: repeatedly applies a binary function to an array from left + // to right; returns the final value. + if(typeof a == "string"){ a = a.split(""); } + o = o || win.global; f = df.lambda(f); + var z, i, n; + if(lang.isArray(a)){ + // array + z = a[0]; + for(i = 1, n = a.length; i < n; z = f.call(o, z, a[i], i, a), ++i); + }else if(typeof a.hasNext == "function" && typeof a.next == "function"){ + // iterator + if(a.hasNext()){ + z = a.next(); + for(i = 1; a.hasNext(); z = f.call(o, z, a.next(), i++, a)); + } + }else{ + // object/dictionary + var first = true; + for(i in a){ + if(!(i in empty)){ + if(first){ + z = a[i]; + first = false; + }else{ + z = f.call(o, z, a[i], i, a); + } + } + } + } + return z; // Object + }, + foldr: function(/*Array|String*/ a, /*Function|String|Array*/ f, /*Object*/ z, /*Object?*/ o){ + // summary: repeatedly applies a binary function to an array from right + // to left using a seed value as a starting point; returns the final + // value. + if(typeof a == "string"){ a = a.split(""); } + o = o || win.global; f = df.lambda(f); + for(var i = a.length; i > 0; --i, z = f.call(o, z, a[i], i, a)); + return z; // Object + }, + foldr1: function(/*Array|String*/ a, /*Function|String|Array*/ f, /*Object?*/ o){ + // summary: repeatedly applies a binary function to an array from right + // to left; returns the final value. + if(typeof a == "string"){ a = a.split(""); } + o = o || win.global; f = df.lambda(f); + var n = a.length, z = a[n - 1], i = n - 1; + for(; i > 0; --i, z = f.call(o, z, a[i], i, a)); + return z; // Object + }, + // JS 1.8 standard array functions, which can take a lambda as a parameter. + reduce: function(/*Array|String|Object*/ a, /*Function|String|Array*/ f, /*Object?*/ z){ + // summary: apply a function simultaneously against two values of the array + // (from left-to-right) as to reduce it to a single value. + return arguments.length < 3 ? df.foldl1(a, f) : df.foldl(a, f, z); // Object + }, + reduceRight: function(/*Array|String*/ a, /*Function|String|Array*/ f, /*Object?*/ z){ + // summary: apply a function simultaneously against two values of the array + // (from right-to-left) as to reduce it to a single value. + return arguments.length < 3 ? df.foldr1(a, f) : df.foldr(a, f, z); // Object + }, + // the fold's counterpart: unfold + unfold: function(/*Function|String|Array*/ pr, /*Function|String|Array*/ f, + /*Function|String|Array*/ g, /*Object*/ z, /*Object?*/ o){ + // summary: builds an array by unfolding a value + o = o || win.global; f = df.lambda(f); g = df.lambda(g); pr = df.lambda(pr); + var t = []; + for(; !pr.call(o, z); t.push(f.call(o, z)), z = g.call(o, z)); + return t; // Array + } + }); +}); + +}, +'url:dijit/templates/Tooltip.html':"<div class=\"dijitTooltip dijitTooltipLeft\" id=\"dojoTooltip\"\n\t><div class=\"dijitTooltipContainer dijitTooltipContents\" data-dojo-attach-point=\"containerNode\" role='alert'></div\n\t><div class=\"dijitTooltipConnector\" data-dojo-attach-point=\"connectorNode\"></div\n></div>\n", +'dojox/charting/plot2d/Spider':function(){ +define("dojox/charting/plot2d/Spider", ["dojo/_base/lang", "dojo/_base/declare", "dojo/_base/connect", "dojo/_base/html", "dojo/_base/array", + "dojo/dom-geometry", "dojo/_base/fx", "dojo/fx", "dojo/_base/sniff", + "../Element", "./_PlotEvents", "dojo/_base/Color", "dojox/color/_base", "./common", "../axis2d/common", + "../scaler/primitive", "dojox/gfx", "dojox/gfx/matrix", "dojox/gfx/fx", "dojox/lang/functional", + "dojox/lang/utils", "dojo/fx/easing"], + function(lang, declare, hub, html, arr, domGeom, baseFx, coreFx, has, + Element, PlotEvents, Color, dxcolor, dc, da, primitive, + g, m, gfxfx, df, du, easing){ +/*===== +var Element = dojox.charting.Element; +var PlotEvents = dojox.charting.plot2d._PlotEvents; +=====*/ + var FUDGE_FACTOR = 0.2; // use to overlap fans + + var Spider = declare("dojox.charting.plot2d.Spider", [Element, PlotEvents], { + // summary: + // The plot that represents a typical Spider chart. + defaultParams: { + labels: true, + ticks: false, + fixed: true, + precision: 1, + labelOffset: -10, + labelStyle: "default", // default/rows/auto + htmlLabels: true, // use HTML to draw labels + startAngle: -90, // start angle for slices in degrees + divisions: 3, // radius tick count + axisColor: "", // spider axis color + axisWidth: 0, // spider axis stroke width + spiderColor: "", // spider web color + spiderWidth: 0, // spider web stroke width + seriesWidth: 0, // plot border with + seriesFillAlpha: 0.2, // plot fill alpha + spiderOrigin: 0.16, + markerSize: 3, // radius of plot vertex (px) + spiderType: "polygon", //"circle" + animationType: easing.backOut, + axisTickFont: "", + axisTickFontColor: "", + axisFont: "", + axisFontColor: "" + }, + optionalParams: { + radius: 0, + font: "", + fontColor: "" + }, + + constructor: function(chart, kwArgs){ + // summary: + // Create a Spider plot. + this.opt = lang.clone(this.defaultParams); + du.updateWithObject(this.opt, kwArgs); + du.updateWithPattern(this.opt, kwArgs, this.optionalParams); + this.series = []; + this.dyn = []; + this.datas = {}; + this.labelKey = []; + this.oldSeriePoints = {}; + this.animations = {}; + }, + clear: function(){ + // summary: + // Clear out all of the information tied to this plot. + // returns: dojox.charting.plot2d.Spider + // A reference to this plot for functional chaining. + this.dirty = true; + this.dyn = []; + this.series = []; + this.datas = {}; + this.labelKey = []; + this.oldSeriePoints = {}; + this.animations = {}; + return this; // dojox.charting.plot2d.Spider + }, + setAxis: function(axis){ + // summary: + // Dummy method, since axes are irrelevant with a Spider chart. + // returns: dojox.charting.plot2d.Spider + // The reference to this plot for functional chaining. + return this; // dojox.charting.plot2d.Spider + }, + addSeries: function(run){ + // summary: + // Add a data series to this plot. + // run: dojox.charting.Series + // The series to be added. + // returns: dojox.charting.plot2d.Base + // A reference to this plot for functional chaining. + var matched = false; + this.series.push(run); + for(var key in run.data){ + var val = run.data[key], + data = this.datas[key]; + if(data){ + data.vlist.push(val); + data.min = Math.min(data.min, val); + data.max = Math.max(data.max, val); + }else{ + this.datas[key] = {min: val, max: val, vlist: [val]}; + } + } + if (this.labelKey.length <= 0) { + for (var key in run.data) { + this.labelKey.push(key); + } + } + return this; // dojox.charting.plot2d.Base + }, + getSeriesStats: function(){ + // summary: + // Calculate the min/max on all attached series in both directions. + // returns: Object + // {hmin, hmax, vmin, vmax} min/max in both directions. + return dc.collectSimpleStats(this.series); + }, + calculateAxes: function(dim){ + // summary: + // Stub function for running the axis calculations (depricated). + // dim: Object + // An object of the form { width, height } + // returns: dojox.charting.plot2d.Base + // A reference to this plot for functional chaining. + this.initializeScalers(dim, this.getSeriesStats()); + return this; // dojox.charting.plot2d.Base + }, + getRequiredColors: function(){ + // summary: + // Get how many data series we have, so we know how many colors to use. + // returns: Number + // The number of colors needed. + return this.series.length; // Number + }, + initializeScalers: function(dim, stats){ + // summary: + // Initializes scalers using attached axes. + // dim: Object: + // Size of a plot area in pixels as {width, height}. + // stats: Object: + // Min/max of data in both directions as {hmin, hmax, vmin, vmax}. + // returns: dojox.charting.plot2d.Base + // A reference to this plot for functional chaining. + if(this._hAxis){ + if(!this._hAxis.initialized()){ + this._hAxis.calculate(stats.hmin, stats.hmax, dim.width); + } + this._hScaler = this._hAxis.getScaler(); + }else{ + this._hScaler = primitive.buildScaler(stats.hmin, stats.hmax, dim.width); + } + if(this._vAxis){ + if(!this._vAxis.initialized()){ + this._vAxis.calculate(stats.vmin, stats.vmax, dim.height); + } + this._vScaler = this._vAxis.getScaler(); + }else{ + this._vScaler = primitive.buildScaler(stats.vmin, stats.vmax, dim.height); + } + return this; // dojox.charting.plot2d.Base + }, + render: function(dim, offsets){ + // summary: + // Render the plot on the chart. + // dim: Object + // An object of the form { width, height }. + // offsets: Object + // An object of the form { l, r, t, b }. + // returns: dojox.charting.plot2d.Spider + // A reference to this plot for functional chaining. + if(!this.dirty){ return this; } + this.dirty = false; + this.cleanGroup(); + var s = this.group, t = this.chart.theme; + this.resetEvents(); + + if(!this.series || !this.series.length){ + return this; + } + + // calculate the geometry + var o = this.opt, ta = t.axis, + rx = (dim.width - offsets.l - offsets.r) / 2, + ry = (dim.height - offsets.t - offsets.b) / 2, + r = Math.min(rx, ry), + axisTickFont = o.font || (ta.majorTick && ta.majorTick.font) || (ta.tick && ta.tick.font) || "normal normal normal 7pt Tahoma", + axisFont = o.axisFont || (ta.tick && ta.tick.titleFont) || "normal normal normal 11pt Tahoma", + axisTickFontColor = o.axisTickFontColor || (ta.majorTick && ta.majorTick.fontColor) || (ta.tick && ta.tick.fontColor) || "silver", + axisFontColor = o.axisFontColor || (ta.tick && ta.tick.titleFontColor) || "black", + axisColor = o.axisColor || (ta.tick && ta.tick.axisColor) || "silver", + spiderColor = o.spiderColor || (ta.tick && ta.tick.spiderColor) || "silver", + axisWidth = o.axisWidth || (ta.stroke && ta.stroke.width) || 2, + spiderWidth = o.spiderWidth || (ta.stroke && ta.stroke.width) || 2, + seriesWidth = o.seriesWidth || (ta.stroke && ta.stroke.width) || 2, + asize = g.normalizedLength(g.splitFontString(axisFont).size), + startAngle = m._degToRad(o.startAngle), + start = startAngle, step, filteredRun, slices, labels, shift, labelR, + outerPoints, innerPoints, divisionPoints, divisionRadius, labelPoints, + ro = o.spiderOrigin, dv = o.divisions >= 3 ? o.divisions : 3, ms = o.markerSize, + spt = o.spiderType, at = o.animationType, lboffset = o.labelOffset < -10 ? o.labelOffset : -10, + axisExtra = 0.2; + + if(o.labels){ + labels = arr.map(this.series, function(s){ + return s.name; + }, this); + shift = df.foldl1(df.map(labels, function(label, i){ + var font = t.series.font; + return g._base._getTextBox(label, { + font: font + }).w; + }, this), "Math.max(a, b)") / 2; + r = Math.min(rx - 2 * shift, ry - asize) + lboffset; + labelR = r - lboffset; + } + if ("radius" in o) { + r = o.radius; + labelR = r - lboffset; + } + r /= (1+axisExtra); + var circle = { + cx: offsets.l + rx, + cy: offsets.t + ry, + r: r + }; + + for (var i = this.series.length - 1; i >= 0; i--) { + var serieEntry = this.series[i]; + if (!this.dirty && !serieEntry.dirty) { + t.skip(); + continue; + } + serieEntry.cleanGroup(); + var run = serieEntry.data; + if (run !== null) { + var len = this._getObjectLength(run); + //construct connect points + if (!outerPoints || outerPoints.length <= 0) { + outerPoints = [], innerPoints = [], labelPoints = []; + this._buildPoints(outerPoints, len, circle, r, start, true); + this._buildPoints(innerPoints, len, circle, r*ro, start, true); + this._buildPoints(labelPoints, len, circle, labelR, start); + if(dv > 2){ + divisionPoints = [], divisionRadius = []; + for (var j = 0; j < dv - 2; j++) { + divisionPoints[j] = []; + this._buildPoints(divisionPoints[j], len, circle, r*(ro + (1-ro)*(j+1)/(dv-1)), start, true); + divisionRadius[j] = r*(ro + (1-ro)*(j+1)/(dv-1)); + } + } + } + } + } + + //draw Spider + //axis + var axisGroup = s.createGroup(), axisStroke = {color: axisColor, width: axisWidth}, + spiderStroke = {color: spiderColor, width: spiderWidth}; + for (var j = outerPoints.length - 1; j >= 0; --j) { + var point = outerPoints[j], + st = { + x: point.x + (point.x - circle.cx) * axisExtra, + y: point.y + (point.y - circle.cy) * axisExtra + }, + nd = { + x: point.x + (point.x - circle.cx) * axisExtra / 2, + y: point.y + (point.y - circle.cy) * axisExtra / 2 + }; + axisGroup.createLine({ + x1: circle.cx, + y1: circle.cy, + x2: st.x, + y2: st.y + }).setStroke(axisStroke); + //arrow + this._drawArrow(axisGroup, st, nd, axisStroke); + } + + // draw the label + var labelGroup = s.createGroup(); + for (var j = labelPoints.length - 1; j >= 0; --j) { + var point = labelPoints[j], + fontWidth = g._base._getTextBox(this.labelKey[j], {font: axisFont}).w || 0, + render = this.opt.htmlLabels && g.renderer != "vml" ? "html" : "gfx", + elem = da.createText[render](this.chart, labelGroup, (!domGeom.isBodyLtr() && render == "html") ? (point.x + fontWidth - dim.width) : point.x, point.y, + "middle", this.labelKey[j], axisFont, axisFontColor); + if (this.opt.htmlLabels) { + this.htmlElements.push(elem); + } + } + + //spider web: polygon or circle + var spiderGroup = s.createGroup(); + if(spt == "polygon"){ + spiderGroup.createPolyline(outerPoints).setStroke(spiderStroke); + spiderGroup.createPolyline(innerPoints).setStroke(spiderStroke); + if (divisionPoints.length > 0) { + for (var j = divisionPoints.length - 1; j >= 0; --j) { + spiderGroup.createPolyline(divisionPoints[j]).setStroke(spiderStroke); + } + } + }else{//circle + var ccount = this._getObjectLength(this.datas); + spiderGroup.createCircle({cx: circle.cx, cy: circle.cy, r: r}).setStroke(spiderStroke); + spiderGroup.createCircle({cx: circle.cx, cy: circle.cy, r: r*ro}).setStroke(spiderStroke); + if (divisionRadius.length > 0) { + for (var j = divisionRadius.length - 1; j >= 0; --j) { + spiderGroup.createCircle({cx: circle.cx, cy: circle.cy, r: divisionRadius[j]}).setStroke(spiderStroke); + } + } + } + //text + var textGroup = s.createGroup(), len = this._getObjectLength(this.datas), k = 0; + for(var key in this.datas){ + var data = this.datas[key], min = data.min, max = data.max, distance = max - min, + end = start + 2 * Math.PI * k / len; + for (var i = 0; i < dv; i++) { + var text = min + distance*i/(dv-1), point = this._getCoordinate(circle, r*(ro + (1-ro)*i/(dv-1)), end); + text = this._getLabel(text); + var fontWidth = g._base._getTextBox(text, {font: axisTickFont}).w || 0, + render = this.opt.htmlLabels && g.renderer != "vml" ? "html" : "gfx"; + if (this.opt.htmlLabels) { + this.htmlElements.push(da.createText[render] + (this.chart, textGroup, (!domGeom.isBodyLtr() && render == "html") ? (point.x + fontWidth - dim.width) : point.x, point.y, + "start", text, axisTickFont, axisTickFontColor)); + } + } + k++; + } + + //draw series (animation) + this.chart.seriesShapes = {}; + var animationConnections = []; + for (var i = this.series.length - 1; i >= 0; i--) { + var serieEntry = this.series[i], run = serieEntry.data; + if (run !== null) { + //series polygon + var seriePoints = [], k = 0, tipData = []; + for(var key in run){ + var data = this.datas[key], min = data.min, max = data.max, distance = max - min, + entry = run[key], end = start + 2 * Math.PI * k / len, + point = this._getCoordinate(circle, r*(ro + (1-ro)*(entry-min)/distance), end); + seriePoints.push(point); + tipData.push({sname: serieEntry.name, key: key, data: entry}); + k++; + } + seriePoints[seriePoints.length] = seriePoints[0]; + tipData[tipData.length] = tipData[0]; + var polygonBoundRect = this._getBoundary(seriePoints), + theme = t.next("spider", [o, serieEntry]), ts = serieEntry.group, + f = g.normalizeColor(theme.series.fill), sk = {color: theme.series.fill, width: seriesWidth}; + f.a = o.seriesFillAlpha; + serieEntry.dyn = {fill: f, stroke: sk}; + + var osps = this.oldSeriePoints[serieEntry.name]; + var cs = this._createSeriesEntry(ts, (osps || innerPoints), seriePoints, f, sk, r, ro, ms, at); + this.chart.seriesShapes[serieEntry.name] = cs; + this.oldSeriePoints[serieEntry.name] = seriePoints; + + var po = { + element: "spider_poly", + index: i, + id: "spider_poly_"+serieEntry.name, + run: serieEntry, + plot: this, + shape: cs.poly, + parent: ts, + brect: polygonBoundRect, + cx: circle.cx, + cy: circle.cy, + cr: r, + f: f, + s: s + }; + this._connectEvents(po); + + var so = { + element: "spider_plot", + index: i, + id: "spider_plot_"+serieEntry.name, + run: serieEntry, + plot: this, + shape: serieEntry.group + }; + this._connectEvents(so); + + arr.forEach(cs.circles, function(c, i){ + var shape = c.getShape(), + co = { + element: "spider_circle", + index: i, + id: "spider_circle_"+serieEntry.name+i, + run: serieEntry, + plot: this, + shape: c, + parent: ts, + tdata: tipData[i], + cx: seriePoints[i].x, + cy: seriePoints[i].y, + f: f, + s: s + }; + this._connectEvents(co); + }, this); + } + } + return this; // dojox.charting.plot2d.Spider + }, + _createSeriesEntry: function(ts, osps, sps, f, sk, r, ro, ms, at){ + //polygon + var spoly = ts.createPolyline(osps).setFill(f).setStroke(sk), scircle = []; + for (var j = 0; j < osps.length; j++) { + var point = osps[j], cr = ms; + var circle = ts.createCircle({cx: point.x, cy: point.y, r: cr}).setFill(f).setStroke(sk); + scircle.push(circle); + } + + var anims = arr.map(sps, function(np, j){ + // create animation + var sp = osps[j], + anim = new baseFx.Animation({ + duration: 1000, + easing: at, + curve: [sp.y, np.y] + }); + var spl = spoly, sc = scircle[j]; + hub.connect(anim, "onAnimate", function(y){ + //apply poly + var pshape = spl.getShape(); + pshape.points[j].y = y; + spl.setShape(pshape); + //apply circle + var cshape = sc.getShape(); + cshape.cy = y; + sc.setShape(cshape); + }); + return anim; + }); + + var anims1 = arr.map(sps, function(np, j){ + // create animation + var sp = osps[j], + anim = new baseFx.Animation({ + duration: 1000, + easing: at, + curve: [sp.x, np.x] + }); + var spl = spoly, sc = scircle[j]; + hub.connect(anim, "onAnimate", function(x){ + //apply poly + var pshape = spl.getShape(); + pshape.points[j].x = x; + spl.setShape(pshape); + //apply circle + var cshape = sc.getShape(); + cshape.cx = x; + sc.setShape(cshape); + }); + return anim; + }); + var masterAnimation = coreFx.combine(anims.concat(anims1)); //dojo.fx.chain(anims); + masterAnimation.play(); + return {group :ts, poly: spoly, circles: scircle}; + }, + plotEvent: function(o){ + // summary: + // Stub function for use by specific plots. + // o: Object + // An object intended to represent event parameters. + var runName = o.id ? o.id : "default", a; + if (runName in this.animations) { + a = this.animations[runName]; + a.anim && a.anim.stop(true); + } else { + a = this.animations[runName] = {}; + } + if(o.element == "spider_poly"){ + if(!a.color){ + var color = o.shape.getFill(); + if(!color || !(color instanceof Color)){ + return; + } + a.color = { + start: color, + end: transColor(color) + }; + } + var start = a.color.start, end = a.color.end; + if(o.type == "onmouseout"){ + // swap colors + var t = start; start = end; end = t; + } + a.anim = gfxfx.animateFill({ + shape: o.shape, + duration: 800, + easing: easing.backOut, + color: {start: start, end: end} + }); + a.anim.play(); + }else if(o.element == "spider_circle"){ + var init, scale, defaultScale = 1.5; + if(o.type == "onmouseover"){ + init = m.identity; + scale = defaultScale; + //show tooltip + var aroundRect = {type: "rect"}; + aroundRect.x = o.cx; + aroundRect.y = o.cy; + aroundRect.width = aroundRect.height = 1; + var lt = html.coords(this.chart.node, true); + aroundRect.x += lt.x; + aroundRect.y += lt.y; + aroundRect.x = Math.round(aroundRect.x); + aroundRect.y = Math.round(aroundRect.y); + aroundRect.width = Math.ceil(aroundRect.width); + aroundRect.height = Math.ceil(aroundRect.height); + this.aroundRect = aroundRect; + var position = ["after", "before"]; + dc.doIfLoaded("dijit/Tooltip", dojo.hitch(this, function(Tooltip){ + Tooltip.show(o.tdata.sname + "<br/>" + o.tdata.key + "<br/>" + o.tdata.data, this.aroundRect, position); + })); + }else{ + init = m.scaleAt(defaultScale, o.cx, o.cy); + scale = 1/defaultScale; + dc.doIfLoaded("dijit/Tooltip", dojo.hitch(this, function(Tooltip){ + this.aroundRect && Tooltip.hide(this.aroundRect); + })); + } + var cs = o.shape.getShape(), + init = m.scaleAt(defaultScale, cs.cx, cs.cy), + kwArgs = { + shape: o.shape, + duration: 200, + easing: easing.backOut, + transform: [ + {name: "scaleAt", start: [1, cs.cx, cs.cy], end: [scale, cs.cx, cs.cy]}, + init + ] + }; + a.anim = gfxfx.animateTransform(kwArgs); + a.anim.play(); + }else if(o.element == "spider_plot"){ + //dojo gfx function "moveToFront" not work in IE + if (o.type == "onmouseover" && !has("ie")) { + o.shape.moveToFront(); + } + } + }, + _getBoundary: function(points){ + var xmax = points[0].x, + xmin = points[0].x, + ymax = points[0].y, + ymin = points[0].y; + for(var i = 0; i < points.length; i++){ + var point = points[i]; + xmax = Math.max(point.x, xmax); + ymax = Math.max(point.y, ymax); + xmin = Math.min(point.x, xmin); + ymin = Math.min(point.y, ymin); + } + return { + x: xmin, + y: ymin, + width: xmax - xmin, + height: ymax - ymin + }; + }, + + _drawArrow: function(s, start, end, stroke){ + var len = Math.sqrt(Math.pow(end.x - start.x, 2) + Math.pow(end.y - start.y, 2)), + sin = (end.y - start.y)/len, cos = (end.x - start.x)/len, + point2 = {x: end.x + (len/3)*(-sin), y: end.y + (len/3)*cos}, + point3 = {x: end.x + (len/3)*sin, y: end.y + (len/3)*(-cos)}; + s.createPolyline([start, point2, point3]).setFill(stroke.color).setStroke(stroke); + }, + + _buildPoints: function(points, count, circle, radius, angle, recursive){ + for (var i = 0; i < count; i++) { + var end = angle + 2 * Math.PI * i / count; + points.push(this._getCoordinate(circle, radius, end)); + } + if(recursive){ + points.push(this._getCoordinate(circle, radius, angle + 2 * Math.PI)); + } + }, + + _getCoordinate: function(circle, radius, angle){ + return { + x: circle.cx + radius * Math.cos(angle), + y: circle.cy + radius * Math.sin(angle) + } + }, + + _getObjectLength: function(obj){ + var count = 0; + if(lang.isObject(obj)){ + for(var key in obj){ + count++; + } + } + return count; + }, + + // utilities + _getLabel: function(number){ + return dc.getLabel(number, this.opt.fixed, this.opt.precision); + } + }); + + function transColor(color){ + var a = new dxcolor.Color(color), + x = a.toHsl(); + if(x.s == 0){ + x.l = x.l < 50 ? 100 : 0; + }else{ + x.s = 100; + if(x.l < 50){ + x.l = 75; + }else if(x.l > 75){ + x.l = 50; + }else{ + x.l = x.l - 50 > 75 - x.l ? + 50 : 75; + } + } + var color = dxcolor.fromHsl(x); + color.a = 0.7; + return color; + } + + return Spider; // dojox.plot2d.Spider +}); + +}, +'dojox/charting/plot2d/StackedBars':function(){ +define("dojox/charting/plot2d/StackedBars", ["dojo/_base/lang", "dojo/_base/array", "dojo/_base/declare", "./Bars", "./common", + "dojox/lang/functional", "dojox/lang/functional/reversed", "dojox/lang/functional/sequence"], + function(lang, arr, declare, Bars, dc, df, dfr, dfs){ + + var purgeGroup = dfr.lambda("item.purgeGroup()"); +/*===== +var bars = dojox.charting.plot2d.Bars; +=====*/ + return declare("dojox.charting.plot2d.StackedBars", Bars, { + // summary: + // The plot object representing a stacked bar chart (horizontal bars). + getSeriesStats: function(){ + // summary: + // Calculate the min/max on all attached series in both directions. + // returns: Object + // {hmin, hmax, vmin, vmax} min/max in both directions. + var stats = dc.collectStackedStats(this.series), t; + this._maxRunLength = stats.hmax; + stats.hmin -= 0.5; + stats.hmax += 0.5; + t = stats.hmin, stats.hmin = stats.vmin, stats.vmin = t; + t = stats.hmax, stats.hmax = stats.vmax, stats.vmax = t; + return stats; + }, + render: function(dim, offsets){ + // summary: + // Run the calculations for any axes for this plot. + // dim: Object + // An object in the form of { width, height } + // offsets: Object + // An object of the form { l, r, t, b}. + // returns: dojox.charting.plot2d.StackedBars + // A reference to this plot for functional chaining. + if(this._maxRunLength <= 0){ + return this; + } + + // stack all values + var acc = df.repeat(this._maxRunLength, "-> 0", 0); + for(var i = 0; i < this.series.length; ++i){ + var run = this.series[i]; + for(var j = 0; j < run.data.length; ++j){ + var value = run.data[j]; + if(value !== null){ + var v = typeof value == "number" ? value : value.y; + if(isNaN(v)){ v = 0; } + acc[j] += v; + } + } + } + // draw runs in backwards + if(this.zoom && !this.isDataDirty()){ + return this.performZoom(dim, offsets); + } + this.resetEvents(); + this.dirty = this.isDirty(); + if(this.dirty){ + arr.forEach(this.series, purgeGroup); + this._eventSeries = {}; + this.cleanGroup(); + var s = this.group; + df.forEachRev(this.series, function(item){ item.cleanGroup(s); }); + } + var t = this.chart.theme, f, gap, height, + ht = this._hScaler.scaler.getTransformerFromModel(this._hScaler), + vt = this._vScaler.scaler.getTransformerFromModel(this._vScaler), + events = this.events(); + f = dc.calculateBarSize(this._vScaler.bounds.scale, this.opt); + gap = f.gap; + height = f.size; + for(var i = this.series.length - 1; i >= 0; --i){ + var run = this.series[i]; + if(!this.dirty && !run.dirty){ + t.skip(); + this._reconnectEvents(run.name); + continue; + } + run.cleanGroup(); + var theme = t.next("bar", [this.opt, run]), s = run.group, + eventSeries = new Array(acc.length); + for(var j = 0; j < acc.length; ++j){ + var value = run.data[j]; + if(value !== null){ + var v = acc[j], + width = ht(v), + finalTheme = typeof value != "number" ? + t.addMixin(theme, "bar", value, true) : + t.post(theme, "bar"); + if(width >= 0 && height >= 1){ + var rect = { + x: offsets.l, + y: dim.height - offsets.b - vt(j + 1.5) + gap, + width: width, height: height + }; + var specialFill = this._plotFill(finalTheme.series.fill, dim, offsets); + specialFill = this._shapeFill(specialFill, rect); + var shape = s.createRect(rect).setFill(specialFill).setStroke(finalTheme.series.stroke); + run.dyn.fill = shape.getFill(); + run.dyn.stroke = shape.getStroke(); + if(events){ + var o = { + element: "bar", + index: j, + run: run, + shape: shape, + x: v, + y: j + 1.5 + }; + this._connectEvents(o); + eventSeries[j] = o; + } + if(this.animate){ + this._animateBar(shape, offsets.l, -width); + } + } + } + } + this._eventSeries[run.name] = eventSeries; + run.dirty = false; + // update the accumulator + for(var j = 0; j < run.data.length; ++j){ + var value = run.data[j]; + if(value !== null){ + var v = typeof value == "number" ? value : value.y; + if(isNaN(v)){ v = 0; } + acc[j] -= v; + } + } + } + this.dirty = false; + return this; // dojox.charting.plot2d.StackedBars + } + }); +}); + +}, +'dojox/charting/themes/GreySkies':function(){ +define("dojox/charting/themes/GreySkies", ["../Theme", "./common"], function(Theme, themes){ + + themes.GreySkies=new Theme(Theme._def); + + return themes.GreySkies; +}); + +}, +'dojox/charting/plot2d/Columns':function(){ +define("dojox/charting/plot2d/Columns", ["dojo/_base/lang", "dojo/_base/array", "dojo/_base/declare", "./Base", "./common", + "dojox/lang/functional", "dojox/lang/functional/reversed", "dojox/lang/utils", "dojox/gfx/fx"], + function(lang, arr, declare, Base, dc, df, dfr, du, fx){ + + var purgeGroup = dfr.lambda("item.purgeGroup()"); +/*===== +var Base = dojox.charting.plot2d.Base; +=====*/ + + return declare("dojox.charting.plot2d.Columns", Base, { + // summary: + // The plot object representing a column chart (vertical bars). + defaultParams: { + hAxis: "x", // use a horizontal axis named "x" + vAxis: "y", // use a vertical axis named "y" + gap: 0, // gap between columns in pixels + animate: null, // animate bars into place + enableCache: false + }, + optionalParams: { + minBarSize: 1, // minimal column width in pixels + maxBarSize: 1, // maximal column width in pixels + // theme component + stroke: {}, + outline: {}, + shadow: {}, + fill: {}, + font: "", + fontColor: "" + }, + + constructor: function(chart, kwArgs){ + // summary: + // The constructor for a columns chart. + // chart: dojox.charting.Chart + // The chart this plot belongs to. + // kwArgs: dojox.charting.plot2d.__BarCtorArgs? + // An optional keyword arguments object to help define the plot. + this.opt = lang.clone(this.defaultParams); + du.updateWithObject(this.opt, kwArgs); + du.updateWithPattern(this.opt, kwArgs, this.optionalParams); + this.series = []; + this.hAxis = this.opt.hAxis; + this.vAxis = this.opt.vAxis; + this.animate = this.opt.animate; + }, + + getSeriesStats: function(){ + // summary: + // Calculate the min/max on all attached series in both directions. + // returns: Object + // {hmin, hmax, vmin, vmax} min/max in both directions. + var stats = dc.collectSimpleStats(this.series); + stats.hmin -= 0.5; + stats.hmax += 0.5; + return stats; + }, + + createRect: function(run, creator, params){ + var rect; + if(this.opt.enableCache && run._rectFreePool.length > 0){ + rect = run._rectFreePool.pop(); + rect.setShape(params); + // was cleared, add it back + creator.add(rect); + }else{ + rect = creator.createRect(params); + } + if(this.opt.enableCache){ + run._rectUsePool.push(rect); + } + return rect; + }, + + render: function(dim, offsets){ + // summary: + // Run the calculations for any axes for this plot. + // dim: Object + // An object in the form of { width, height } + // offsets: Object + // An object of the form { l, r, t, b}. + // returns: dojox.charting.plot2d.Columns + // A reference to this plot for functional chaining. + if(this.zoom && !this.isDataDirty()){ + return this.performZoom(dim, offsets); + } + var t = this.getSeriesStats(); + this.resetEvents(); + this.dirty = this.isDirty(); + if(this.dirty){ + arr.forEach(this.series, purgeGroup); + this._eventSeries = {}; + this.cleanGroup(); + var s = this.group; + df.forEachRev(this.series, function(item){ item.cleanGroup(s); }); + } + var t = this.chart.theme, f, gap, width, + ht = this._hScaler.scaler.getTransformerFromModel(this._hScaler), + vt = this._vScaler.scaler.getTransformerFromModel(this._vScaler), + baseline = Math.max(0, this._vScaler.bounds.lower), + baselineHeight = vt(baseline), + min = Math.max(0, Math.floor(this._hScaler.bounds.from - 1)), max = Math.ceil(this._hScaler.bounds.to), + events = this.events(); + f = dc.calculateBarSize(this._hScaler.bounds.scale, this.opt); + gap = f.gap; + width = f.size; + for(var i = this.series.length - 1; i >= 0; --i){ + var run = this.series[i]; + if(!this.dirty && !run.dirty){ + t.skip(); + this._reconnectEvents(run.name); + continue; + } + run.cleanGroup(); + if(this.opt.enableCache){ + run._rectFreePool = (run._rectFreePool?run._rectFreePool:[]).concat(run._rectUsePool?run._rectUsePool:[]); + run._rectUsePool = []; + } + var theme = t.next("column", [this.opt, run]), s = run.group, + eventSeries = new Array(run.data.length); + var l = Math.min(run.data.length, max); + for(var j = min; j < l; ++j){ + var value = run.data[j]; + if(value !== null){ + var v = typeof value == "number" ? value : value.y, + vv = vt(v), + height = vv - baselineHeight, + h = Math.abs(height), + finalTheme = typeof value != "number" ? + t.addMixin(theme, "column", value, true) : + t.post(theme, "column"); + if(width >= 1 && h >= 0){ + var rect = { + x: offsets.l + ht(j + 0.5) + gap, + y: dim.height - offsets.b - (v > baseline ? vv : baselineHeight), + width: width, height: h + }; + var specialFill = this._plotFill(finalTheme.series.fill, dim, offsets); + specialFill = this._shapeFill(specialFill, rect); + var shape = this.createRect(run, s, rect).setFill(specialFill).setStroke(finalTheme.series.stroke); + run.dyn.fill = shape.getFill(); + run.dyn.stroke = shape.getStroke(); + if(events){ + var o = { + element: "column", + index: j, + run: run, + shape: shape, + x: j + 0.5, + y: v + }; + this._connectEvents(o); + eventSeries[j] = o; + } + if(this.animate){ + this._animateColumn(shape, dim.height - offsets.b - baselineHeight, h); + } + } + } + } + this._eventSeries[run.name] = eventSeries; + run.dirty = false; + } + this.dirty = false; + return this; // dojox.charting.plot2d.Columns + }, + _animateColumn: function(shape, voffset, vsize){ + fx.animateTransform(lang.delegate({ + shape: shape, + duration: 1200, + transform: [ + {name: "translate", start: [0, voffset - (voffset/vsize)], end: [0, 0]}, + {name: "scale", start: [1, 1/vsize], end: [1, 1]}, + {name: "original"} + ] + }, this.animate)).play(); + } + }); +}); + +}, +'dijit/place':function(){ +define("dijit/place", [ + "dojo/_base/array", // array.forEach array.map array.some + "dojo/dom-geometry", // domGeometry.getMarginBox domGeometry.position + "dojo/dom-style", // domStyle.getComputedStyle + "dojo/_base/kernel", // kernel.deprecated + "dojo/_base/window", // win.body + "dojo/window", // winUtils.getBox + "." // dijit (defining dijit.place to match API doc) +], function(array, domGeometry, domStyle, kernel, win, winUtils, dijit){ + + // module: + // dijit/place + // summary: + // Code to place a popup relative to another node + + + function _place(/*DomNode*/ node, choices, layoutNode, aroundNodeCoords){ + // summary: + // Given a list of spots to put node, put it at the first spot where it fits, + // of if it doesn't fit anywhere then the place with the least overflow + // choices: Array + // Array of elements like: {corner: 'TL', pos: {x: 10, y: 20} } + // Above example says to put the top-left corner of the node at (10,20) + // layoutNode: Function(node, aroundNodeCorner, nodeCorner, size) + // for things like tooltip, they are displayed differently (and have different dimensions) + // based on their orientation relative to the parent. This adjusts the popup based on orientation. + // It also passes in the available size for the popup, which is useful for tooltips to + // tell them that their width is limited to a certain amount. layoutNode() may return a value expressing + // how much the popup had to be modified to fit into the available space. This is used to determine + // what the best placement is. + // aroundNodeCoords: Object + // Size of aroundNode, ex: {w: 200, h: 50} + + // get {x: 10, y: 10, w: 100, h:100} type obj representing position of + // viewport over document + var view = winUtils.getBox(); + + // This won't work if the node is inside a <div style="position: relative">, + // so reattach it to win.doc.body. (Otherwise, the positioning will be wrong + // and also it might get cutoff) + if(!node.parentNode || String(node.parentNode.tagName).toLowerCase() != "body"){ + win.body().appendChild(node); + } + + var best = null; + array.some(choices, function(choice){ + var corner = choice.corner; + var pos = choice.pos; + var overflow = 0; + + // calculate amount of space available given specified position of node + var spaceAvailable = { + w: { + 'L': view.l + view.w - pos.x, + 'R': pos.x - view.l, + 'M': view.w + }[corner.charAt(1)], + h: { + 'T': view.t + view.h - pos.y, + 'B': pos.y - view.t, + 'M': view.h + }[corner.charAt(0)] + }; + + // configure node to be displayed in given position relative to button + // (need to do this in order to get an accurate size for the node, because + // a tooltip's size changes based on position, due to triangle) + if(layoutNode){ + var res = layoutNode(node, choice.aroundCorner, corner, spaceAvailable, aroundNodeCoords); + overflow = typeof res == "undefined" ? 0 : res; + } + + // get node's size + var style = node.style; + var oldDisplay = style.display; + var oldVis = style.visibility; + if(style.display == "none"){ + style.visibility = "hidden"; + style.display = ""; + } + var mb = domGeometry. getMarginBox(node); + style.display = oldDisplay; + style.visibility = oldVis; + + // coordinates and size of node with specified corner placed at pos, + // and clipped by viewport + var + startXpos = { + 'L': pos.x, + 'R': pos.x - mb.w, + 'M': Math.max(view.l, Math.min(view.l + view.w, pos.x + (mb.w >> 1)) - mb.w) // M orientation is more flexible + }[corner.charAt(1)], + startYpos = { + 'T': pos.y, + 'B': pos.y - mb.h, + 'M': Math.max(view.t, Math.min(view.t + view.h, pos.y + (mb.h >> 1)) - mb.h) + }[corner.charAt(0)], + startX = Math.max(view.l, startXpos), + startY = Math.max(view.t, startYpos), + endX = Math.min(view.l + view.w, startXpos + mb.w), + endY = Math.min(view.t + view.h, startYpos + mb.h), + width = endX - startX, + height = endY - startY; + + overflow += (mb.w - width) + (mb.h - height); + + if(best == null || overflow < best.overflow){ + best = { + corner: corner, + aroundCorner: choice.aroundCorner, + x: startX, + y: startY, + w: width, + h: height, + overflow: overflow, + spaceAvailable: spaceAvailable + }; + } + + return !overflow; + }); + + // In case the best position is not the last one we checked, need to call + // layoutNode() again. + if(best.overflow && layoutNode){ + layoutNode(node, best.aroundCorner, best.corner, best.spaceAvailable, aroundNodeCoords); + } + + // And then position the node. Do this last, after the layoutNode() above + // has sized the node, due to browser quirks when the viewport is scrolled + // (specifically that a Tooltip will shrink to fit as though the window was + // scrolled to the left). + // + // In RTL mode, set style.right rather than style.left so in the common case, + // window resizes move the popup along with the aroundNode. + var l = domGeometry.isBodyLtr(), + s = node.style; + s.top = best.y + "px"; + s[l ? "left" : "right"] = (l ? best.x : view.w - best.x - best.w) + "px"; + s[l ? "right" : "left"] = "auto"; // needed for FF or else tooltip goes to far left + + return best; + } + + /*===== + dijit.place.__Position = function(){ + // x: Integer + // horizontal coordinate in pixels, relative to document body + // y: Integer + // vertical coordinate in pixels, relative to document body + + this.x = x; + this.y = y; + }; + =====*/ + + /*===== + dijit.place.__Rectangle = function(){ + // x: Integer + // horizontal offset in pixels, relative to document body + // y: Integer + // vertical offset in pixels, relative to document body + // w: Integer + // width in pixels. Can also be specified as "width" for backwards-compatibility. + // h: Integer + // height in pixels. Can also be specified as "height" from backwards-compatibility. + + this.x = x; + this.y = y; + this.w = w; + this.h = h; + }; + =====*/ + + return (dijit.place = { + // summary: + // Code to place a DOMNode relative to another DOMNode. + // Load using require(["dijit/place"], function(place){ ... }). + + at: function(node, pos, corners, padding){ + // summary: + // Positions one of the node's corners at specified position + // such that node is fully visible in viewport. + // description: + // NOTE: node is assumed to be absolutely or relatively positioned. + // node: DOMNode + // The node to position + // pos: dijit.place.__Position + // Object like {x: 10, y: 20} + // corners: String[] + // Array of Strings representing order to try corners in, like ["TR", "BL"]. + // Possible values are: + // * "BL" - bottom left + // * "BR" - bottom right + // * "TL" - top left + // * "TR" - top right + // padding: dijit.place.__Position? + // optional param to set padding, to put some buffer around the element you want to position. + // example: + // Try to place node's top right corner at (10,20). + // If that makes node go (partially) off screen, then try placing + // bottom left corner at (10,20). + // | place(node, {x: 10, y: 20}, ["TR", "BL"]) + var choices = array.map(corners, function(corner){ + var c = { corner: corner, pos: {x:pos.x,y:pos.y} }; + if(padding){ + c.pos.x += corner.charAt(1) == 'L' ? padding.x : -padding.x; + c.pos.y += corner.charAt(0) == 'T' ? padding.y : -padding.y; + } + return c; + }); + + return _place(node, choices); + }, + + around: function( + /*DomNode*/ node, + /*DomNode || dijit.place.__Rectangle*/ anchor, + /*String[]*/ positions, + /*Boolean*/ leftToRight, + /*Function?*/ layoutNode){ + + // summary: + // Position node adjacent or kitty-corner to anchor + // such that it's fully visible in viewport. + // + // description: + // Place node such that corner of node touches a corner of + // aroundNode, and that node is fully visible. + // + // anchor: + // Either a DOMNode or a __Rectangle (object with x, y, width, height). + // + // positions: + // Ordered list of positions to try matching up. + // * before: places drop down to the left of the anchor node/widget, or to the right in the case + // of RTL scripts like Hebrew and Arabic; aligns either the top of the drop down + // with the top of the anchor, or the bottom of the drop down with bottom of the anchor. + // * after: places drop down to the right of the anchor node/widget, or to the left in the case + // of RTL scripts like Hebrew and Arabic; aligns either the top of the drop down + // with the top of the anchor, or the bottom of the drop down with bottom of the anchor. + // * before-centered: centers drop down to the left of the anchor node/widget, or to the right + // in the case of RTL scripts like Hebrew and Arabic + // * after-centered: centers drop down to the right of the anchor node/widget, or to the left + // in the case of RTL scripts like Hebrew and Arabic + // * above-centered: drop down is centered above anchor node + // * above: drop down goes above anchor node, left sides aligned + // * above-alt: drop down goes above anchor node, right sides aligned + // * below-centered: drop down is centered above anchor node + // * below: drop down goes below anchor node + // * below-alt: drop down goes below anchor node, right sides aligned + // + // layoutNode: Function(node, aroundNodeCorner, nodeCorner) + // For things like tooltip, they are displayed differently (and have different dimensions) + // based on their orientation relative to the parent. This adjusts the popup based on orientation. + // + // leftToRight: + // True if widget is LTR, false if widget is RTL. Affects the behavior of "above" and "below" + // positions slightly. + // + // example: + // | placeAroundNode(node, aroundNode, {'BL':'TL', 'TR':'BR'}); + // This will try to position node such that node's top-left corner is at the same position + // as the bottom left corner of the aroundNode (ie, put node below + // aroundNode, with left edges aligned). If that fails it will try to put + // the bottom-right corner of node where the top right corner of aroundNode is + // (ie, put node above aroundNode, with right edges aligned) + // + + // if around is a DOMNode (or DOMNode id), convert to coordinates + var aroundNodePos = (typeof anchor == "string" || "offsetWidth" in anchor) + ? domGeometry.position(anchor, true) + : anchor; + + // Adjust anchor positioning for the case that a parent node has overflw hidden, therefore cuasing the anchor not to be completely visible + if(anchor.parentNode){ + var parent = anchor.parentNode; + while(parent && parent.nodeType == 1 && parent.nodeName != "BODY"){ //ignoring the body will help performance + var parentPos = domGeometry.position(parent, true); + var parentStyleOverflow = domStyle.getComputedStyle(parent).overflow; + if(parentStyleOverflow == "hidden" || parentStyleOverflow == "auto" || parentStyleOverflow == "scroll"){ + var bottomYCoord = Math.min(aroundNodePos.y + aroundNodePos.h, parentPos.y + parentPos.h); + var rightXCoord = Math.min(aroundNodePos.x + aroundNodePos.w, parentPos.x + parentPos.w); + aroundNodePos.x = Math.max(aroundNodePos.x, parentPos.x); + aroundNodePos.y = Math.max(aroundNodePos.y, parentPos.y); + aroundNodePos.h = bottomYCoord - aroundNodePos.y; + aroundNodePos.w = rightXCoord - aroundNodePos.x; + } + parent = parent.parentNode; + } + } + + var x = aroundNodePos.x, + y = aroundNodePos.y, + width = "w" in aroundNodePos ? aroundNodePos.w : (aroundNodePos.w = aroundNodePos.width), + height = "h" in aroundNodePos ? aroundNodePos.h : (kernel.deprecated("place.around: dijit.place.__Rectangle: { x:"+x+", y:"+y+", height:"+aroundNodePos.height+", width:"+width+" } has been deprecated. Please use { x:"+x+", y:"+y+", h:"+aroundNodePos.height+", w:"+width+" }", "", "2.0"), aroundNodePos.h = aroundNodePos.height); + + // Convert positions arguments into choices argument for _place() + var choices = []; + function push(aroundCorner, corner){ + choices.push({ + aroundCorner: aroundCorner, + corner: corner, + pos: { + x: { + 'L': x, + 'R': x + width, + 'M': x + (width >> 1) + }[aroundCorner.charAt(1)], + y: { + 'T': y, + 'B': y + height, + 'M': y + (height >> 1) + }[aroundCorner.charAt(0)] + } + }) + } + array.forEach(positions, function(pos){ + var ltr = leftToRight; + switch(pos){ + case "above-centered": + push("TM", "BM"); + break; + case "below-centered": + push("BM", "TM"); + break; + case "after-centered": + ltr = !ltr; + // fall through + case "before-centered": + push(ltr ? "ML" : "MR", ltr ? "MR" : "ML"); + break; + case "after": + ltr = !ltr; + // fall through + case "before": + push(ltr ? "TL" : "TR", ltr ? "TR" : "TL"); + push(ltr ? "BL" : "BR", ltr ? "BR" : "BL"); + break; + case "below-alt": + ltr = !ltr; + // fall through + case "below": + // first try to align left borders, next try to align right borders (or reverse for RTL mode) + push(ltr ? "BL" : "BR", ltr ? "TL" : "TR"); + push(ltr ? "BR" : "BL", ltr ? "TR" : "TL"); + break; + case "above-alt": + ltr = !ltr; + // fall through + case "above": + // first try to align left borders, next try to align right borders (or reverse for RTL mode) + push(ltr ? "TL" : "TR", ltr ? "BL" : "BR"); + push(ltr ? "TR" : "TL", ltr ? "BR" : "BL"); + break; + default: + // To assist dijit/_base/place, accept arguments of type {aroundCorner: "BL", corner: "TL"}. + // Not meant to be used directly. + push(pos.aroundCorner, pos.corner); + } + }); + + var position = _place(node, choices, layoutNode, {w: width, h: height}); + position.aroundNodePos = aroundNodePos; + + return position; + } + }); +}); + +}, +'dojox/lang/functional/array':function(){ +define("dojox/lang/functional/array", ["dojo/_base/kernel", "dojo/_base/lang", "dojo/_base/array", "dojo/_base/window", "./lambda"], + function(dojo, lang, arr, win, df){ + +// This module adds high-level functions and related constructs: +// - array-processing functions similar to standard JS functions + +// Notes: +// - this module provides JS standard methods similar to high-level functions in dojo/_base/array.js: +// forEach, map, filter, every, some + +// Defined methods: +// - take any valid lambda argument as the functional argument +// - operate on dense arrays +// - take a string as the array argument +// - take an iterator objects as the array argument + + var empty = {}; + +/*===== + var df = dojox.lang.functional; + =====*/ + lang.mixin(df, { + // JS 1.6 standard array functions, which can take a lambda as a parameter. + // Consider using dojo._base.array functions, if you don't need the lambda support. + filter: function(/*Array|String|Object*/ a, /*Function|String|Array*/ f, /*Object?*/ o){ + // summary: creates a new array with all elements that pass the test + // implemented by the provided function. + if(typeof a == "string"){ a = a.split(""); } + o = o || win.global; f = df.lambda(f); + var t = [], v, i, n; + if(lang.isArray(a)){ + // array + for(i = 0, n = a.length; i < n; ++i){ + v = a[i]; + if(f.call(o, v, i, a)){ t.push(v); } + } + }else if(typeof a.hasNext == "function" && typeof a.next == "function"){ + // iterator + for(i = 0; a.hasNext();){ + v = a.next(); + if(f.call(o, v, i++, a)){ t.push(v); } + } + }else{ + // object/dictionary + for(i in a){ + if(!(i in empty)){ + v = a[i]; + if(f.call(o, v, i, a)){ t.push(v); } + } + } + } + return t; // Array + }, + forEach: function(/*Array|String|Object*/ a, /*Function|String|Array*/ f, /*Object?*/ o){ + // summary: executes a provided function once per array element. + if(typeof a == "string"){ a = a.split(""); } + o = o || win.global; f = df.lambda(f); + var i, n; + if(lang.isArray(a)){ + // array + for(i = 0, n = a.length; i < n; f.call(o, a[i], i, a), ++i); + }else if(typeof a.hasNext == "function" && typeof a.next == "function"){ + // iterator + for(i = 0; a.hasNext(); f.call(o, a.next(), i++, a)); + }else{ + // object/dictionary + for(i in a){ + if(!(i in empty)){ + f.call(o, a[i], i, a); + } + } + } + return o; // Object + }, + map: function(/*Array|String|Object*/ a, /*Function|String|Array*/ f, /*Object?*/ o){ + // summary: creates a new array with the results of calling + // a provided function on every element in this array. + if(typeof a == "string"){ a = a.split(""); } + o = o || win.global; f = df.lambda(f); + var t, n, i; + if(lang.isArray(a)){ + // array + t = new Array(n = a.length); + for(i = 0; i < n; t[i] = f.call(o, a[i], i, a), ++i); + }else if(typeof a.hasNext == "function" && typeof a.next == "function"){ + // iterator + t = []; + for(i = 0; a.hasNext(); t.push(f.call(o, a.next(), i++, a))); + }else{ + // object/dictionary + t = []; + for(i in a){ + if(!(i in empty)){ + t.push(f.call(o, a[i], i, a)); + } + } + } + return t; // Array + }, + every: function(/*Array|String|Object*/ a, /*Function|String|Array*/ f, /*Object?*/ o){ + // summary: tests whether all elements in the array pass the test + // implemented by the provided function. + if(typeof a == "string"){ a = a.split(""); } + o = o || win.global; f = df.lambda(f); + var i, n; + if(lang.isArray(a)){ + // array + for(i = 0, n = a.length; i < n; ++i){ + if(!f.call(o, a[i], i, a)){ + return false; // Boolean + } + } + }else if(typeof a.hasNext == "function" && typeof a.next == "function"){ + // iterator + for(i = 0; a.hasNext();){ + if(!f.call(o, a.next(), i++, a)){ + return false; // Boolean + } + } + }else{ + // object/dictionary + for(i in a){ + if(!(i in empty)){ + if(!f.call(o, a[i], i, a)){ + return false; // Boolean + } + } + } + } + return true; // Boolean + }, + some: function(/*Array|String|Object*/ a, /*Function|String|Array*/ f, /*Object?*/ o){ + // summary: tests whether some element in the array passes the test + // implemented by the provided function. + if(typeof a == "string"){ a = a.split(""); } + o = o || win.global; f = df.lambda(f); + var i, n; + if(lang.isArray(a)){ + // array + for(i = 0, n = a.length; i < n; ++i){ + if(f.call(o, a[i], i, a)){ + return true; // Boolean + } + } + }else if(typeof a.hasNext == "function" && typeof a.next == "function"){ + // iterator + for(i = 0; a.hasNext();){ + if(f.call(o, a.next(), i++, a)){ + return true; // Boolean + } + } + }else{ + // object/dictionary + for(i in a){ + if(!(i in empty)){ + if(f.call(o, a[i], i, a)){ + return true; // Boolean + } + } + } + } + return false; // Boolean + } + }); + + return df; +}); + +}, +'dojox/charting/Theme':function(){ +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; +}); + +}, +'dojox/charting/themes/common':function(){ +define("dojox/charting/themes/common", ["dojo/_base/lang"], function(lang){ + return lang.getObject("dojox.charting.themes", true); +}); + +}, +'dojox/charting/plot2d/common':function(){ +define("dojox/charting/plot2d/common", ["dojo/_base/lang", "dojo/_base/array", "dojo/_base/Color", + "dojox/gfx", "dojox/lang/functional", "../scaler/common"], + function(lang, arr, Color, g, df, sc){ + + var common = lang.getObject("dojox.charting.plot2d.common", true); + + return lang.mixin(common, { + doIfLoaded: sc.doIfLoaded, + makeStroke: function(stroke){ + if(!stroke){ return stroke; } + if(typeof stroke == "string" || stroke instanceof Color){ + stroke = {color: stroke}; + } + return g.makeParameters(g.defaultStroke, stroke); + }, + augmentColor: function(target, color){ + var t = new Color(target), + c = new Color(color); + c.a = t.a; + return c; + }, + augmentStroke: function(stroke, color){ + var s = common.makeStroke(stroke); + if(s){ + s.color = common.augmentColor(s.color, color); + } + return s; + }, + augmentFill: function(fill, color){ + var fc, c = new Color(color); + if(typeof fill == "string" || fill instanceof Color){ + return common.augmentColor(fill, color); + } + return fill; + }, + + defaultStats: { + vmin: Number.POSITIVE_INFINITY, vmax: Number.NEGATIVE_INFINITY, + hmin: Number.POSITIVE_INFINITY, hmax: Number.NEGATIVE_INFINITY + }, + + collectSimpleStats: function(series){ + var stats = lang.delegate(common.defaultStats); + for(var i = 0; i < series.length; ++i){ + var run = series[i]; + for(var j = 0; j < run.data.length; j++){ + if(run.data[j] !== null){ + if(typeof run.data[j] == "number"){ + // 1D case + var old_vmin = stats.vmin, old_vmax = stats.vmax; + if(!("ymin" in run) || !("ymax" in run)){ + arr.forEach(run.data, function(val, i){ + if(val !== null){ + var x = i + 1, y = val; + if(isNaN(y)){ y = 0; } + stats.hmin = Math.min(stats.hmin, x); + stats.hmax = Math.max(stats.hmax, x); + stats.vmin = Math.min(stats.vmin, y); + stats.vmax = Math.max(stats.vmax, y); + } + }); + } + if("ymin" in run){ stats.vmin = Math.min(old_vmin, run.ymin); } + if("ymax" in run){ stats.vmax = Math.max(old_vmax, run.ymax); } + }else{ + // 2D case + var old_hmin = stats.hmin, old_hmax = stats.hmax, + old_vmin = stats.vmin, old_vmax = stats.vmax; + if(!("xmin" in run) || !("xmax" in run) || !("ymin" in run) || !("ymax" in run)){ + arr.forEach(run.data, function(val, i){ + if(val !== null){ + var x = "x" in val ? val.x : i + 1, y = val.y; + if(isNaN(x)){ x = 0; } + if(isNaN(y)){ y = 0; } + stats.hmin = Math.min(stats.hmin, x); + stats.hmax = Math.max(stats.hmax, x); + stats.vmin = Math.min(stats.vmin, y); + stats.vmax = Math.max(stats.vmax, y); + } + }); + } + if("xmin" in run){ stats.hmin = Math.min(old_hmin, run.xmin); } + if("xmax" in run){ stats.hmax = Math.max(old_hmax, run.xmax); } + if("ymin" in run){ stats.vmin = Math.min(old_vmin, run.ymin); } + if("ymax" in run){ stats.vmax = Math.max(old_vmax, run.ymax); } + } + + break; + } + } + } + return stats; + }, + + calculateBarSize: function(/* Number */ availableSize, /* Object */ opt, /* Number? */ clusterSize){ + if(!clusterSize){ + clusterSize = 1; + } + var gap = opt.gap, size = (availableSize - 2 * gap) / clusterSize; + if("minBarSize" in opt){ + size = Math.max(size, opt.minBarSize); + } + if("maxBarSize" in opt){ + size = Math.min(size, opt.maxBarSize); + } + size = Math.max(size, 1); + gap = (availableSize - size * clusterSize) / 2; + return {size: size, gap: gap}; // Object + }, + + collectStackedStats: function(series){ + // collect statistics + var stats = lang.clone(common.defaultStats); + if(series.length){ + // 1st pass: find the maximal length of runs + stats.hmin = Math.min(stats.hmin, 1); + stats.hmax = df.foldl(series, "seed, run -> Math.max(seed, run.data.length)", stats.hmax); + // 2nd pass: stack values + for(var i = 0; i < stats.hmax; ++i){ + var v = series[0].data[i]; + v = v && (typeof v == "number" ? v : v.y); + if(isNaN(v)){ v = 0; } + stats.vmin = Math.min(stats.vmin, v); + for(var j = 1; j < series.length; ++j){ + var t = series[j].data[i]; + t = t && (typeof t == "number" ? t : t.y); + if(isNaN(t)){ t = 0; } + v += t; + } + stats.vmax = Math.max(stats.vmax, v); + } + } + return stats; + }, + + curve: function(/* Number[] */a, /* Number|String */tension){ + // FIX for #7235, submitted by Enzo Michelangeli. + // Emulates the smoothing algorithms used in a famous, unnamed spreadsheet + // program ;) + var array = a.slice(0); + if(tension == "x") { + array[array.length] = arr[0]; // add a last element equal to the first, closing the loop + } + var p=arr.map(array, function(item, i){ + if(i==0){ return "M" + item.x + "," + item.y; } + if(!isNaN(tension)) { // use standard Dojo smoothing in tension is numeric + var dx=item.x-array[i-1].x, dy=array[i-1].y; + return "C"+(item.x-(tension-1)*(dx/tension))+","+dy+" "+(item.x-(dx/tension))+","+item.y+" "+item.x+","+item.y; + } else if(tension == "X" || tension == "x" || tension == "S") { + // use Excel "line smoothing" algorithm (http://xlrotor.com/resources/files.shtml) + var p0, p1 = array[i-1], p2 = array[i], p3; + var bz1x, bz1y, bz2x, bz2y; + var f = 1/6; + if(i==1) { + if(tension == "x") { + p0 = array[array.length-2]; + } else { // "tension == X || tension == "S" + p0 = p1; + } + f = 1/3; + } else { + p0 = array[i-2]; + } + if(i==(array.length-1)) { + if(tension == "x") { + p3 = array[1]; + } else { // "tension == X || tension == "S" + p3 = p2; + } + f = 1/3; + } else { + p3 = array[i+1]; + } + var p1p2 = Math.sqrt((p2.x-p1.x)*(p2.x-p1.x)+(p2.y-p1.y)*(p2.y-p1.y)); + var p0p2 = Math.sqrt((p2.x-p0.x)*(p2.x-p0.x)+(p2.y-p0.y)*(p2.y-p0.y)); + var p1p3 = Math.sqrt((p3.x-p1.x)*(p3.x-p1.x)+(p3.y-p1.y)*(p3.y-p1.y)); + + var p0p2f = p0p2 * f; + var p1p3f = p1p3 * f; + + if(p0p2f > p1p2/2 && p1p3f > p1p2/2) { + p0p2f = p1p2/2; + p1p3f = p1p2/2; + } else if(p0p2f > p1p2/2) { + p0p2f = p1p2/2; + p1p3f = p1p2/2 * p1p3/p0p2; + } else if(p1p3f > p1p2/2) { + p1p3f = p1p2/2; + p0p2f = p1p2/2 * p0p2/p1p3; + } + + if(tension == "S") { + if(p0 == p1) { p0p2f = 0; } + if(p2 == p3) { p1p3f = 0; } + } + + bz1x = p1.x + p0p2f*(p2.x - p0.x)/p0p2; + bz1y = p1.y + p0p2f*(p2.y - p0.y)/p0p2; + bz2x = p2.x - p1p3f*(p3.x - p1.x)/p1p3; + bz2y = p2.y - p1p3f*(p3.y - p1.y)/p1p3; + } + return "C"+(bz1x+","+bz1y+" "+bz2x+","+bz2y+" "+p2.x+","+p2.y); + }); + return p.join(" "); + }, + + getLabel: function(/*Number*/number, /*Boolean*/fixed, /*Number*/precision){ + return sc.doIfLoaded("dojo/number", function(numberLib){ + return (fixed ? numberLib.format(number, {places : precision}) : + numberLib.format(number)) || ""; + }, function(){ + return fixed ? number.toFixed(precision) : number.toString(); + }); + } + }); +}); + +}, +'dijit/_Widget':function(){ +define("dijit/_Widget", [ + "dojo/aspect", // aspect.around + "dojo/_base/config", // config.isDebug + "dojo/_base/connect", // connect.connect + "dojo/_base/declare", // declare + "dojo/_base/kernel", // kernel.deprecated + "dojo/_base/lang", // lang.hitch + "dojo/query", + "dojo/ready", + "./registry", // registry.byNode + "./_WidgetBase", + "./_OnDijitClickMixin", + "./_FocusMixin", + "dojo/uacss", // browser sniffing (included for back-compat; subclasses may be using) + "./hccss" // high contrast mode sniffing (included to set CSS classes on <body>, module ret value unused) +], function(aspect, config, connect, declare, kernel, lang, query, ready, + registry, _WidgetBase, _OnDijitClickMixin, _FocusMixin){ + +/*===== + var _WidgetBase = dijit._WidgetBase; + var _OnDijitClickMixin = dijit._OnDijitClickMixin; + var _FocusMixin = dijit._FocusMixin; +=====*/ + + +// module: +// dijit/_Widget +// summary: +// Old base for widgets. New widgets should extend _WidgetBase instead + + +function connectToDomNode(){ + // summary: + // If user connects to a widget method === this function, then they will + // instead actually be connecting the equivalent event on this.domNode +} + +// Trap dojo.connect() calls to connectToDomNode methods, and redirect to _Widget.on() +function aroundAdvice(originalConnect){ + return function(obj, event, scope, method){ + if(obj && typeof event == "string" && obj[event] == connectToDomNode){ + return obj.on(event.substring(2).toLowerCase(), lang.hitch(scope, method)); + } + return originalConnect.apply(connect, arguments); + }; +} +aspect.around(connect, "connect", aroundAdvice); +if(kernel.connect){ + aspect.around(kernel, "connect", aroundAdvice); +} + +var _Widget = declare("dijit._Widget", [_WidgetBase, _OnDijitClickMixin, _FocusMixin], { + // summary: + // Base class for all Dijit widgets. + // + // Extends _WidgetBase, adding support for: + // - declaratively/programatically specifying widget initialization parameters like + // onMouseMove="foo" that call foo when this.domNode gets a mousemove event + // - ondijitclick + // Support new data-dojo-attach-event="ondijitclick: ..." that is triggered by a mouse click or a SPACE/ENTER keypress + // - focus related functions + // In particular, the onFocus()/onBlur() callbacks. Driven internally by + // dijit/_base/focus.js. + // - deprecated methods + // - onShow(), onHide(), onClose() + // + // Also, by loading code in dijit/_base, turns on: + // - browser sniffing (putting browser id like .dj_ie on <html> node) + // - high contrast mode sniffing (add .dijit_a11y class to <body> if machine is in high contrast mode) + + + ////////////////// DEFERRED CONNECTS /////////////////// + + onClick: connectToDomNode, + /*===== + onClick: function(event){ + // summary: + // Connect to this function to receive notifications of mouse click events. + // event: + // mouse Event + // tags: + // callback + }, + =====*/ + onDblClick: connectToDomNode, + /*===== + onDblClick: function(event){ + // summary: + // Connect to this function to receive notifications of mouse double click events. + // event: + // mouse Event + // tags: + // callback + }, + =====*/ + onKeyDown: connectToDomNode, + /*===== + onKeyDown: function(event){ + // summary: + // Connect to this function to receive notifications of keys being pressed down. + // event: + // key Event + // tags: + // callback + }, + =====*/ + onKeyPress: connectToDomNode, + /*===== + onKeyPress: function(event){ + // summary: + // Connect to this function to receive notifications of printable keys being typed. + // event: + // key Event + // tags: + // callback + }, + =====*/ + onKeyUp: connectToDomNode, + /*===== + onKeyUp: function(event){ + // summary: + // Connect to this function to receive notifications of keys being released. + // event: + // key Event + // tags: + // callback + }, + =====*/ + onMouseDown: connectToDomNode, + /*===== + onMouseDown: function(event){ + // summary: + // Connect to this function to receive notifications of when the mouse button is pressed down. + // event: + // mouse Event + // tags: + // callback + }, + =====*/ + onMouseMove: connectToDomNode, + /*===== + onMouseMove: function(event){ + // summary: + // Connect to this function to receive notifications of when the mouse moves over nodes contained within this widget. + // event: + // mouse Event + // tags: + // callback + }, + =====*/ + onMouseOut: connectToDomNode, + /*===== + onMouseOut: function(event){ + // summary: + // Connect to this function to receive notifications of when the mouse moves off of nodes contained within this widget. + // event: + // mouse Event + // tags: + // callback + }, + =====*/ + onMouseOver: connectToDomNode, + /*===== + onMouseOver: function(event){ + // summary: + // Connect to this function to receive notifications of when the mouse moves onto nodes contained within this widget. + // event: + // mouse Event + // tags: + // callback + }, + =====*/ + onMouseLeave: connectToDomNode, + /*===== + onMouseLeave: function(event){ + // summary: + // Connect to this function to receive notifications of when the mouse moves off of this widget. + // event: + // mouse Event + // tags: + // callback + }, + =====*/ + onMouseEnter: connectToDomNode, + /*===== + onMouseEnter: function(event){ + // summary: + // Connect to this function to receive notifications of when the mouse moves onto this widget. + // event: + // mouse Event + // tags: + // callback + }, + =====*/ + onMouseUp: connectToDomNode, + /*===== + onMouseUp: function(event){ + // summary: + // Connect to this function to receive notifications of when the mouse button is released. + // event: + // mouse Event + // tags: + // callback + }, + =====*/ + + constructor: function(params){ + // extract parameters like onMouseMove that should connect directly to this.domNode + this._toConnect = {}; + for(var name in params){ + if(this[name] === connectToDomNode){ + this._toConnect[name.replace(/^on/, "").toLowerCase()] = params[name]; + delete params[name]; + } + } + }, + + postCreate: function(){ + this.inherited(arguments); + + // perform connection from this.domNode to user specified handlers (ex: onMouseMove) + for(var name in this._toConnect){ + this.on(name, this._toConnect[name]); + } + delete this._toConnect; + }, + + on: function(/*String*/ type, /*Function*/ func){ + if(this[this._onMap(type)] === connectToDomNode){ + // Use connect.connect() rather than on() to get handling for "onmouseenter" on non-IE, etc. + // Also, need to specify context as "this" rather than the default context of the DOMNode + return connect.connect(this.domNode, type.toLowerCase(), this, func); + } + return this.inherited(arguments); + }, + + _setFocusedAttr: function(val){ + // Remove this method in 2.0 (or sooner), just here to set _focused == focused, for back compat + // (but since it's a private variable we aren't required to keep supporting it). + this._focused = val; + this._set("focused", val); + }, + + ////////////////// DEPRECATED METHODS /////////////////// + + setAttribute: function(/*String*/ attr, /*anything*/ value){ + // summary: + // Deprecated. Use set() instead. + // tags: + // deprecated + kernel.deprecated(this.declaredClass+"::setAttribute(attr, value) is deprecated. Use set() instead.", "", "2.0"); + this.set(attr, value); + }, + + attr: function(/*String|Object*/name, /*Object?*/value){ + // summary: + // Set or get properties on a widget instance. + // name: + // The property to get or set. If an object is passed here and not + // a string, its keys are used as names of attributes to be set + // and the value of the object as values to set in the widget. + // value: + // Optional. If provided, attr() operates as a setter. If omitted, + // the current value of the named property is returned. + // description: + // This method is deprecated, use get() or set() directly. + + // Print deprecation warning but only once per calling function + if(config.isDebug){ + var alreadyCalledHash = arguments.callee._ach || (arguments.callee._ach = {}), + caller = (arguments.callee.caller || "unknown caller").toString(); + if(!alreadyCalledHash[caller]){ + kernel.deprecated(this.declaredClass + "::attr() is deprecated. Use get() or set() instead, called from " + + caller, "", "2.0"); + alreadyCalledHash[caller] = true; + } + } + + var args = arguments.length; + if(args >= 2 || typeof name === "object"){ // setter + return this.set.apply(this, arguments); + }else{ // getter + return this.get(name); + } + }, + + getDescendants: function(){ + // summary: + // Returns all the widgets contained by this, i.e., all widgets underneath this.containerNode. + // This method should generally be avoided as it returns widgets declared in templates, which are + // supposed to be internal/hidden, but it's left here for back-compat reasons. + + kernel.deprecated(this.declaredClass+"::getDescendants() is deprecated. Use getChildren() instead.", "", "2.0"); + return this.containerNode ? query('[widgetId]', this.containerNode).map(registry.byNode) : []; // dijit._Widget[] + }, + + ////////////////// MISCELLANEOUS METHODS /////////////////// + + _onShow: function(){ + // summary: + // Internal method called when this widget is made visible. + // See `onShow` for details. + this.onShow(); + }, + + onShow: function(){ + // summary: + // Called when this widget becomes the selected pane in a + // `dijit.layout.TabContainer`, `dijit.layout.StackContainer`, + // `dijit.layout.AccordionContainer`, etc. + // + // Also called to indicate display of a `dijit.Dialog`, `dijit.TooltipDialog`, or `dijit.TitlePane`. + // tags: + // callback + }, + + onHide: function(){ + // summary: + // Called when another widget becomes the selected pane in a + // `dijit.layout.TabContainer`, `dijit.layout.StackContainer`, + // `dijit.layout.AccordionContainer`, etc. + // + // Also called to indicate hide of a `dijit.Dialog`, `dijit.TooltipDialog`, or `dijit.TitlePane`. + // tags: + // callback + }, + + onClose: function(){ + // summary: + // Called when this widget is being displayed as a popup (ex: a Calendar popped + // up from a DateTextBox), and it is hidden. + // This is called from the dijit.popup code, and should not be called directly. + // + // Also used as a parameter for children of `dijit.layout.StackContainer` or subclasses. + // Callback if a user tries to close the child. Child will be closed if this function returns true. + // tags: + // extension + + return true; // Boolean + } +}); + +// For back-compat, remove in 2.0. +if(!kernel.isAsync){ + ready(0, function(){ + var requires = ["dijit/_base"]; + require(requires); // use indirection so modules not rolled into a build + }); +} +return _Widget; +}); + +}, +'dijit/_FocusMixin':function(){ +define("dijit/_FocusMixin", [ + "./focus", + "./_WidgetBase", + "dojo/_base/declare", // declare + "dojo/_base/lang" // lang.extend +], function(focus, _WidgetBase, declare, lang){ + +/*===== + var _WidgetBase = dijit._WidgetBase; +=====*/ + + // module: + // dijit/_FocusMixin + // summary: + // Mixin to widget to provide _onFocus() and _onBlur() methods that + // fire when a widget or it's descendants get/lose focus + + // We don't know where _FocusMixin will occur in the inheritance chain, but we need the _onFocus()/_onBlur() below + // to be last in the inheritance chain, so mixin to _WidgetBase. + lang.extend(_WidgetBase, { + // focused: [readonly] Boolean + // This widget or a widget it contains has focus, or is "active" because + // it was recently clicked. + focused: false, + + onFocus: function(){ + // summary: + // Called when the widget becomes "active" because + // it or a widget inside of it either has focus, or has recently + // been clicked. + // tags: + // callback + }, + + onBlur: function(){ + // summary: + // Called when the widget stops being "active" because + // focus moved to something outside of it, or the user + // clicked somewhere outside of it, or the widget was + // hidden. + // tags: + // callback + }, + + _onFocus: function(){ + // summary: + // This is where widgets do processing for when they are active, + // such as changing CSS classes. See onFocus() for more details. + // tags: + // protected + this.onFocus(); + }, + + _onBlur: function(){ + // summary: + // This is where widgets do processing for when they stop being active, + // such as changing CSS classes. See onBlur() for more details. + // tags: + // protected + this.onBlur(); + } + }); + + return declare("dijit._FocusMixin", null, { + // summary: + // Mixin to widget to provide _onFocus() and _onBlur() methods that + // fire when a widget or it's descendants get/lose focus + + // flag that I want _onFocus()/_onBlur() notifications from focus manager + _focusManager: focus + }); + +}); + +}, +'dijit/_OnDijitClickMixin':function(){ +define("dijit/_OnDijitClickMixin", [ + "dojo/on", + "dojo/_base/array", // array.forEach + "dojo/keys", // keys.ENTER keys.SPACE + "dojo/_base/declare", // declare + "dojo/_base/sniff", // has("ie") + "dojo/_base/unload", // unload.addOnWindowUnload + "dojo/_base/window" // win.doc.addEventListener win.doc.attachEvent win.doc.detachEvent +], function(on, array, keys, declare, has, unload, win){ + + // module: + // dijit/_OnDijitClickMixin + // summary: + // Mixin so you can pass "ondijitclick" to this.connect() method, + // as a way to handle clicks by mouse, or by keyboard (SPACE/ENTER key) + + + // Keep track of where the last keydown event was, to help avoid generating + // spurious ondijitclick events when: + // 1. focus is on a <button> or <a> + // 2. user presses then releases the ENTER key + // 3. onclick handler fires and shifts focus to another node, with an ondijitclick handler + // 4. onkeyup event fires, causing the ondijitclick handler to fire + var lastKeyDownNode = null; + if(has("ie")){ + (function(){ + var keydownCallback = function(evt){ + lastKeyDownNode = evt.srcElement; + }; + win.doc.attachEvent('onkeydown', keydownCallback); + unload.addOnWindowUnload(function(){ + win.doc.detachEvent('onkeydown', keydownCallback); + }); + })(); + }else{ + win.doc.addEventListener('keydown', function(evt){ + lastKeyDownNode = evt.target; + }, true); + } + + // Custom a11yclick (a.k.a. ondijitclick) event + var a11yclick = function(node, listener){ + if(/input|button/i.test(node.nodeName)){ + // pass through, the browser already generates click event on SPACE/ENTER key + return on(node, "click", listener); + }else{ + // Don't fire the click event unless both the keydown and keyup occur on this node. + // Avoids problems where focus shifted to this node or away from the node on keydown, + // either causing this node to process a stray keyup event, or causing another node + // to get a stray keyup event. + + function clickKey(/*Event*/ e){ + return (e.keyCode == keys.ENTER || e.keyCode == keys.SPACE) && + !e.ctrlKey && !e.shiftKey && !e.altKey && !e.metaKey; + } + var handles = [ + on(node, "keypress", function(e){ + //console.log(this.id + ": onkeydown, e.target = ", e.target, ", lastKeyDownNode was ", lastKeyDownNode, ", equality is ", (e.target === lastKeyDownNode)); + if(clickKey(e)){ + // needed on IE for when focus changes between keydown and keyup - otherwise dropdown menus do not work + lastKeyDownNode = e.target; + + // Prevent viewport scrolling on space key in IE<9. + // (Reproducible on test_Button.html on any of the first dijit.form.Button examples) + // Do this onkeypress rather than onkeydown because onkeydown.preventDefault() will + // suppress the onkeypress event, breaking _HasDropDown + e.preventDefault(); + } + }), + + on(node, "keyup", function(e){ + //console.log(this.id + ": onkeyup, e.target = ", e.target, ", lastKeyDownNode was ", lastKeyDownNode, ", equality is ", (e.target === lastKeyDownNode)); + if(clickKey(e) && e.target == lastKeyDownNode){ // === breaks greasemonkey + //need reset here or have problems in FF when focus returns to trigger element after closing popup/alert + lastKeyDownNode = null; + listener.call(this, e); + } + }), + + on(node, "click", function(e){ + // and connect for mouse clicks too (or touch-clicks on mobile) + listener.call(this, e); + }) + ]; + + return { + remove: function(){ + array.forEach(handles, function(h){ h.remove(); }); + } + }; + } + }; + + return declare("dijit._OnDijitClickMixin", null, { + connect: function( + /*Object|null*/ obj, + /*String|Function*/ event, + /*String|Function*/ method){ + // summary: + // Connects specified obj/event to specified method of this object + // and registers for disconnect() on widget destroy. + // description: + // Provide widget-specific analog to connect.connect, except with the + // implicit use of this widget as the target object. + // This version of connect also provides a special "ondijitclick" + // event which triggers on a click or space or enter keyup. + // Events connected with `this.connect` are disconnected upon + // destruction. + // returns: + // A handle that can be passed to `disconnect` in order to disconnect before + // the widget is destroyed. + // example: + // | var btn = new dijit.form.Button(); + // | // when foo.bar() is called, call the listener we're going to + // | // provide in the scope of btn + // | btn.connect(foo, "bar", function(){ + // | console.debug(this.toString()); + // | }); + // tags: + // protected + + return this.inherited(arguments, [obj, event == "ondijitclick" ? a11yclick : event, method]); + } + }); +}); + +}, +'dojo/cache':function(){ +define(["./_base/kernel", "./text"], function(dojo, text){ + // module: + // dojo/cache + // summary: + // The module defines dojo.cache by loading dojo/text. + + //dojo.cache is defined in dojo/text + return dojo.cache; +}); + +}, +'dojox/charting/plot2d/Bars':function(){ +define("dojox/charting/plot2d/Bars", ["dojo/_base/kernel", "dojo/_base/lang", "dojo/_base/array", "dojo/_base/declare", "./Base", "./common", + "dojox/gfx/fx", "dojox/lang/utils", "dojox/lang/functional", "dojox/lang/functional/reversed"], + function(dojo, lang, arr, declare, Base, dc, fx, du, df, dfr){ + + /*===== + dojo.declare("dojox.charting.plot2d.__BarCtorArgs", dojox.charting.plot2d.__DefaultCtorArgs, { + // summary: + // Additional keyword arguments for bar charts. + + // minBarSize: Number? + // The minimum size for a bar in pixels. Default is 1. + minBarSize: 1, + + // maxBarSize: Number? + // The maximum size for a bar in pixels. Default is 1. + maxBarSize: 1, + + // enableCache: Boolean? + // Whether the bars rect are cached from one rendering to another. This improves the rendering performance of + // successive rendering but penalize the first rendering. Default false. + enableCache: false + }); + var Base = dojox.charting.plot2d.Base; + =====*/ + var purgeGroup = dfr.lambda("item.purgeGroup()"); + + return declare("dojox.charting.plot2d.Bars", Base, { + // summary: + // The plot object representing a bar chart (horizontal bars). + defaultParams: { + hAxis: "x", // use a horizontal axis named "x" + vAxis: "y", // use a vertical axis named "y" + gap: 0, // gap between columns in pixels + animate: null, // animate bars into place + enableCache: false + }, + optionalParams: { + minBarSize: 1, // minimal bar width in pixels + maxBarSize: 1, // maximal bar width in pixels + // theme component + stroke: {}, + outline: {}, + shadow: {}, + fill: {}, + font: "", + fontColor: "" + }, + + constructor: function(chart, kwArgs){ + // summary: + // The constructor for a bar chart. + // chart: dojox.charting.Chart + // The chart this plot belongs to. + // kwArgs: dojox.charting.plot2d.__BarCtorArgs? + // An optional keyword arguments object to help define the plot. + this.opt = lang.clone(this.defaultParams); + du.updateWithObject(this.opt, kwArgs); + du.updateWithPattern(this.opt, kwArgs, this.optionalParams); + this.series = []; + this.hAxis = this.opt.hAxis; + this.vAxis = this.opt.vAxis; + this.animate = this.opt.animate; + }, + + getSeriesStats: function(){ + // summary: + // Calculate the min/max on all attached series in both directions. + // returns: Object + // {hmin, hmax, vmin, vmax} min/max in both directions. + var stats = dc.collectSimpleStats(this.series), t; + stats.hmin -= 0.5; + stats.hmax += 0.5; + t = stats.hmin, stats.hmin = stats.vmin, stats.vmin = t; + t = stats.hmax, stats.hmax = stats.vmax, stats.vmax = t; + return stats; + }, + + createRect: function(run, creator, params){ + var rect; + if(this.opt.enableCache && run._rectFreePool.length > 0){ + rect = run._rectFreePool.pop(); + rect.setShape(params); + // was cleared, add it back + creator.add(rect); + }else{ + rect = creator.createRect(params); + } + if(this.opt.enableCache){ + run._rectUsePool.push(rect); + } + return rect; + }, + + render: function(dim, offsets){ + // summary: + // Run the calculations for any axes for this plot. + // dim: Object + // An object in the form of { width, height } + // offsets: Object + // An object of the form { l, r, t, b}. + // returns: dojox.charting.plot2d.Bars + // A reference to this plot for functional chaining. + if(this.zoom && !this.isDataDirty()){ + return this.performZoom(dim, offsets); + } + this.dirty = this.isDirty(); + this.resetEvents(); + if(this.dirty){ + arr.forEach(this.series, purgeGroup); + this._eventSeries = {}; + this.cleanGroup(); + var s = this.group; + df.forEachRev(this.series, function(item){ item.cleanGroup(s); }); + } + var t = this.chart.theme, f, gap, height, + ht = this._hScaler.scaler.getTransformerFromModel(this._hScaler), + vt = this._vScaler.scaler.getTransformerFromModel(this._vScaler), + baseline = Math.max(0, this._hScaler.bounds.lower), + baselineWidth = ht(baseline), + events = this.events(); + f = dc.calculateBarSize(this._vScaler.bounds.scale, this.opt); + gap = f.gap; + height = f.size; + for(var i = this.series.length - 1; i >= 0; --i){ + var run = this.series[i]; + if(!this.dirty && !run.dirty){ + t.skip(); + this._reconnectEvents(run.name); + continue; + } + run.cleanGroup(); + if(this.opt.enableCache){ + run._rectFreePool = (run._rectFreePool?run._rectFreePool:[]).concat(run._rectUsePool?run._rectUsePool:[]); + run._rectUsePool = []; + } + var theme = t.next("bar", [this.opt, run]), s = run.group, + eventSeries = new Array(run.data.length); + for(var j = 0; j < run.data.length; ++j){ + var value = run.data[j]; + if(value !== null){ + var v = typeof value == "number" ? value : value.y, + hv = ht(v), + width = hv - baselineWidth, + w = Math.abs(width), + finalTheme = typeof value != "number" ? + t.addMixin(theme, "bar", value, true) : + t.post(theme, "bar"); + if(w >= 0 && height >= 1){ + var rect = { + x: offsets.l + (v < baseline ? hv : baselineWidth), + y: dim.height - offsets.b - vt(j + 1.5) + gap, + width: w, height: height + }; + var specialFill = this._plotFill(finalTheme.series.fill, dim, offsets); + specialFill = this._shapeFill(specialFill, rect); + var shape = this.createRect(run, s, rect).setFill(specialFill).setStroke(finalTheme.series.stroke); + run.dyn.fill = shape.getFill(); + run.dyn.stroke = shape.getStroke(); + if(events){ + var o = { + element: "bar", + index: j, + run: run, + shape: shape, + x: v, + y: j + 1.5 + }; + this._connectEvents(o); + eventSeries[j] = o; + } + if(this.animate){ + this._animateBar(shape, offsets.l + baselineWidth, -w); + } + } + } + } + this._eventSeries[run.name] = eventSeries; + run.dirty = false; + } + this.dirty = false; + return this; // dojox.charting.plot2d.Bars + }, + _animateBar: function(shape, hoffset, hsize){ + fx.animateTransform(lang.delegate({ + shape: shape, + duration: 1200, + transform: [ + {name: "translate", start: [hoffset - (hoffset/hsize), 0], end: [0, 0]}, + {name: "scale", start: [1/hsize, 1], end: [1, 1]}, + {name: "original"} + ] + }, this.animate)).play(); + } + }); +}); + +}, +'dojox/gfx/_base':function(){ +define("dojox/gfx/_base", ["dojo/_base/lang", "dojo/_base/html", "dojo/_base/Color", "dojo/_base/sniff", "dojo/_base/window", + "dojo/_base/array","dojo/dom", "dojo/dom-construct","dojo/dom-geometry"], + function(lang, html, Color, has, win, arr, dom, domConstruct, domGeom){ + // module: + // dojox/gfx + // summary: + // This module contains common core Graphics API used by different graphics renderers. + var g = lang.getObject("dojox.gfx", true), + b = g._base = {}; + /*===== g = dojox.gfx; b = dojox.gfx._base; =====*/ + + // candidates for dojox.style (work on VML and SVG nodes) + g._hasClass = function(/*DomNode*/node, /*String*/classStr){ + // summary: + // Returns whether or not the specified classes are a portion of the + // class list currently applied to the node. + // return (new RegExp('(^|\\s+)'+classStr+'(\\s+|$)')).test(node.className) // Boolean + var cls = node.getAttribute("className"); + return cls && (" " + cls + " ").indexOf(" " + classStr + " ") >= 0; // Boolean + }; + g._addClass = function(/*DomNode*/node, /*String*/classStr){ + // summary: + // Adds the specified classes to the end of the class list on the + // passed node. + var cls = node.getAttribute("className") || ""; + if(!cls || (" " + cls + " ").indexOf(" " + classStr + " ") < 0){ + node.setAttribute("className", cls + (cls ? " " : "") + classStr); + } + }; + g._removeClass = function(/*DomNode*/node, /*String*/classStr){ + // summary: Removes classes from node. + var cls = node.getAttribute("className"); + if(cls){ + node.setAttribute( + "className", + cls.replace(new RegExp('(^|\\s+)' + classStr + '(\\s+|$)'), "$1$2") + ); + } + }; + + // candidate for dojox.html.metrics (dynamic font resize handler is not implemented here) + + // derived from Morris John's emResized measurer + b._getFontMeasurements = function(){ + // summary: + // Returns an object that has pixel equivilents of standard font + // size values. + var heights = { + '1em': 0, '1ex': 0, '100%': 0, '12pt': 0, '16px': 0, 'xx-small': 0, + 'x-small': 0, 'small': 0, 'medium': 0, 'large': 0, 'x-large': 0, + 'xx-large': 0 + }; + var p; + + if(has("ie")){ + // we do a font-size fix if and only if one isn't applied already. + // NOTE: If someone set the fontSize on the HTML Element, this will kill it. + win.doc.documentElement.style.fontSize="100%"; + } + + // set up the measuring node. + var div = domConstruct.create("div", {style: { + position: "absolute", + left: "0", + top: "-100px", + width: "30px", + height: "1000em", + borderWidth: "0", + margin: "0", + padding: "0", + outline: "none", + lineHeight: "1", + overflow: "hidden" + }}, win.body()); + + // do the measurements. + for(p in heights){ + div.style.fontSize = p; + heights[p] = Math.round(div.offsetHeight * 12/16) * 16/12 / 1000; + } + + win.body().removeChild(div); + return heights; //object + }; + + var fontMeasurements = null; + + b._getCachedFontMeasurements = function(recalculate){ + if(recalculate || !fontMeasurements){ + fontMeasurements = b._getFontMeasurements(); + } + return fontMeasurements; + }; + + // candidate for dojox.html.metrics + + var measuringNode = null, empty = {}; + b._getTextBox = function( /*String*/ text, + /*Object*/ style, + /*String?*/ className){ + var m, s, al = arguments.length; + var i; + if(!measuringNode){ + measuringNode = domConstruct.create("div", {style: { + position: "absolute", + top: "-10000px", + left: "0" + }}, win.body()); + } + m = measuringNode; + // reset styles + m.className = ""; + s = m.style; + s.borderWidth = "0"; + s.margin = "0"; + s.padding = "0"; + s.outline = "0"; + // set new style + if(al > 1 && style){ + for(i in style){ + if(i in empty){ continue; } + s[i] = style[i]; + } + } + // set classes + if(al > 2 && className){ + m.className = className; + } + // take a measure + m.innerHTML = text; + + if(m["getBoundingClientRect"]){ + var bcr = m.getBoundingClientRect(); + return {l: bcr.left, t: bcr.top, w: bcr.width || (bcr.right - bcr.left), h: bcr.height || (bcr.bottom - bcr.top)}; + }else{ + return domGeom.getMarginBox(m); + } + }; + + // candidate for dojo.dom + + var uniqueId = 0; + b._getUniqueId = function(){ + // summary: returns a unique string for use with any DOM element + var id; + do{ + id = dojo._scopeName + "xUnique" + (++uniqueId); + }while(dom.byId(id)); + return id; + }; + + lang.mixin(g, { + // summary: + // defines constants, prototypes, and utility functions for the core Graphics API + + // default shapes, which are used to fill in missing parameters + defaultPath: { + // summary: + // Defines the default Path prototype object. + type: "path", + // type: String + // Specifies this object is a Path, default value 'path'. + path: "" + // path: String + // The path commands. See W32C SVG 1.0 specification. + // Defaults to empty string value. + }, + defaultPolyline: { + // summary: + // Defines the default PolyLine prototype. + type: "polyline", + // type: String + // Specifies this object is a PolyLine, default value 'polyline'. + points: [] + // points: Array + // An array of point objects [{x:0,y:0},...] defining the default polyline's line segments. Value is an empty array []. + }, + defaultRect: { + // summary: + // Defines the default Rect prototype. + type: "rect", + // type: String + // Specifies this default object is a type of Rect. Value is 'rect' + x: 0, + // x: Number + // The X coordinate of the default rectangles position, value 0. + y: 0, + // y: Number + // The Y coordinate of the default rectangle's position, value 0. + width: 100, + // width: Number + // The width of the default rectangle, value 100. + height: 100, + // height: Number + // The height of the default rectangle, value 100. + r: 0 + // r: Number + // The corner radius for the default rectangle, value 0. + }, + defaultEllipse: { + // summary: + // Defines the default Ellipse prototype. + type: "ellipse", + // type: String + // Specifies that this object is a type of Ellipse, value is 'ellipse' + cx: 0, + // cx: Number + // The X coordinate of the center of the ellipse, default value 0. + cy: 0, + // cy: Number + // The Y coordinate of the center of the ellipse, default value 0. + rx: 200, + // rx: Number + // The radius of the ellipse in the X direction, default value 200. + ry: 100 + // ry: Number + // The radius of the ellipse in the Y direction, default value 200. + }, + defaultCircle: { + // summary: + // An object defining the default Circle prototype. + type: "circle", + // type: String + // Specifies this object is a circle, value 'circle' + cx: 0, + // cx: Number + // The X coordinate of the center of the circle, default value 0. + cy: 0, + // cy: Number + // The Y coordinate of the center of the circle, default value 0. + r: 100 + // r: Number + // The radius, default value 100. + }, + defaultLine: { + // summary: + // An pbject defining the default Line prototype. + type: "line", + // type: String + // Specifies this is a Line, value 'line' + x1: 0, + // x1: Number + // The X coordinate of the start of the line, default value 0. + y1: 0, + // y1: Number + // The Y coordinate of the start of the line, default value 0. + x2: 100, + // x2: Number + // The X coordinate of the end of the line, default value 100. + y2: 100 + // y2: Number + // The Y coordinate of the end of the line, default value 100. + }, + defaultImage: { + // summary: + // Defines the default Image prototype. + type: "image", + // type: String + // Specifies this object is an image, value 'image'. + x: 0, + // x: Number + // The X coordinate of the image's position, default value 0. + y: 0, + // y: Number + // The Y coordinate of the image's position, default value 0. + width: 0, + // width: Number + // The width of the image, default value 0. + height: 0, + // height:Number + // The height of the image, default value 0. + src: "" + // src: String + // The src url of the image, defaults to empty string. + }, + defaultText: { + // summary: + // Defines the default Text prototype. + type: "text", + // type: String + // Specifies this is a Text shape, value 'text'. + x: 0, + // x: Number + // The X coordinate of the text position, default value 0. + y: 0, + // y: Number + // The Y coordinate of the text position, default value 0. + text: "", + // text: String + // The text to be displayed, default value empty string. + align: "start", + // align: String + // The horizontal text alignment, one of 'start', 'end', 'center'. Default value 'start'. + decoration: "none", + // decoration: String + // The text decoration , one of 'none', ... . Default value 'none'. + rotated: false, + // rotated: Boolean + // Whether the text is rotated, boolean default value false. + kerning: true + // kerning: Boolean + // Whether kerning is used on the text, boolean default value true. + }, + defaultTextPath: { + // summary: + // Defines the default TextPath prototype. + type: "textpath", + // type: String + // Specifies this is a TextPath, value 'textpath'. + text: "", + // text: String + // The text to be displayed, default value empty string. + align: "start", + // align: String + // The horizontal text alignment, one of 'start', 'end', 'center'. Default value 'start'. + decoration: "none", + // decoration: String + // The text decoration , one of 'none', ... . Default value 'none'. + rotated: false, + // rotated: Boolean + // Whether the text is rotated, boolean default value false. + kerning: true + // kerning: Boolean + // Whether kerning is used on the text, boolean default value true. + }, + + // default stylistic attributes + defaultStroke: { + // summary: + // A stroke defines stylistic properties that are used when drawing a path. + // This object defines the default Stroke prototype. + type: "stroke", + // type: String + // Specifies this object is a type of Stroke, value 'stroke'. + color: "black", + // color: String + // The color of the stroke, default value 'black'. + style: "solid", + // style: String + // The style of the stroke, one of 'solid', ... . Default value 'solid'. + width: 1, + // width: Number + // The width of a stroke, default value 1. + cap: "butt", + // cap: String + // The endcap style of the path. One of 'butt', 'round', ... . Default value 'butt'. + join: 4 + // join: Number + // The join style to use when combining path segments. Default value 4. + }, + defaultLinearGradient: { + // summary: + // An object defining the default stylistic properties used for Linear Gradient fills. + // Linear gradients are drawn along a virtual line, which results in appearance of a rotated pattern in a given direction/orientation. + type: "linear", + // type: String + // Specifies this object is a Linear Gradient, value 'linear' + x1: 0, + // x1: Number + // The X coordinate of the start of the virtual line along which the gradient is drawn, default value 0. + y1: 0, + // y1: Number + // The Y coordinate of the start of the virtual line along which the gradient is drawn, default value 0. + x2: 100, + // x2: Number + // The X coordinate of the end of the virtual line along which the gradient is drawn, default value 100. + y2: 100, + // y2: Number + // The Y coordinate of the end of the virtual line along which the gradient is drawn, default value 100. + colors: [ + { offset: 0, color: "black" }, { offset: 1, color: "white" } + ] + // colors: Array + // An array of colors at given offsets (from the start of the line). The start of the line is + // defined at offest 0 with the end of the line at offset 1. + // Default value, [{ offset: 0, color: 'black'},{offset: 1, color: 'white'}], is a gradient from black to white. + }, + defaultRadialGradient: { + // summary: + // An object specifying the default properties for RadialGradients using in fills patterns. + type: "radial", + // type: String + // Specifies this is a RadialGradient, value 'radial' + cx: 0, + // cx: Number + // The X coordinate of the center of the radial gradient, default value 0. + cy: 0, + // cy: Number + // The Y coordinate of the center of the radial gradient, default value 0. + r: 100, + // r: Number + // The radius to the end of the radial gradient, default value 100. + colors: [ + { offset: 0, color: "black" }, { offset: 1, color: "white" } + ] + // colors: Array + // An array of colors at given offsets (from the center of the radial gradient). + // The center is defined at offest 0 with the outer edge of the gradient at offset 1. + // Default value, [{ offset: 0, color: 'black'},{offset: 1, color: 'white'}], is a gradient from black to white. + }, + defaultPattern: { + // summary: + // An object specifying the default properties for a Pattern using in fill operations. + type: "pattern", + // type: String + // Specifies this object is a Pattern, value 'pattern'. + x: 0, + // x: Number + // The X coordinate of the position of the pattern, default value is 0. + y: 0, + // y: Number + // The Y coordinate of the position of the pattern, default value is 0. + width: 0, + // width: Number + // The width of the pattern image, default value is 0. + height: 0, + // height: Number + // The height of the pattern image, default value is 0. + src: "" + // src: String + // A url specifing the image to use for the pattern. + }, + defaultFont: { + // summary: + // An object specifying the default properties for a Font used in text operations. + type: "font", + // type: String + // Specifies this object is a Font, value 'font'. + style: "normal", + // style: String + // The font style, one of 'normal', 'bold', default value 'normal'. + variant: "normal", + // variant: String + // The font variant, one of 'normal', ... , default value 'normal'. + weight: "normal", + // weight: String + // The font weight, one of 'normal', ..., default value 'normal'. + size: "10pt", + // size: String + // The font size (including units), default value '10pt'. + family: "serif" + // family: String + // The font family, one of 'serif', 'sanserif', ..., default value 'serif'. + }, + + getDefault: (function(){ + // summary: + // Returns a function used to access default memoized prototype objects (see them defined above). + var typeCtorCache = {}; + // a memoized delegate() + return function(/*String*/ type){ + var t = typeCtorCache[type]; + if(t){ + return new t(); + } + t = typeCtorCache[type] = new Function(); + t.prototype = g[ "default" + type ]; + return new t(); + } + })(), + + normalizeColor: function(/*dojo.Color|Array|string|Object*/ color){ + // summary: + // converts any legal color representation to normalized + // dojo.Color object + return (color instanceof Color) ? color : new Color(color); // dojo.Color + }, + normalizeParameters: function(existed, update){ + // summary: + // updates an existing object with properties from an 'update' + // object + // existed: Object + // the target object to be updated + // update: Object + // the 'update' object, whose properties will be used to update + // the existed object + var x; + if(update){ + var empty = {}; + for(x in existed){ + if(x in update && !(x in empty)){ + existed[x] = update[x]; + } + } + } + return existed; // Object + }, + makeParameters: function(defaults, update){ + // summary: + // copies the original object, and all copied properties from the + // 'update' object + // defaults: Object + // the object to be cloned before updating + // update: Object + // the object, which properties are to be cloned during updating + var i = null; + if(!update){ + // return dojo.clone(defaults); + return lang.delegate(defaults); + } + var result = {}; + for(i in defaults){ + if(!(i in result)){ + result[i] = lang.clone((i in update) ? update[i] : defaults[i]); + } + } + return result; // Object + }, + formatNumber: function(x, addSpace){ + // summary: converts a number to a string using a fixed notation + // x: Number + // number to be converted + // addSpace: Boolean + // whether to add a space before a positive number + var val = x.toString(); + if(val.indexOf("e") >= 0){ + val = x.toFixed(4); + }else{ + var point = val.indexOf("."); + if(point >= 0 && val.length - point > 5){ + val = x.toFixed(4); + } + } + if(x < 0){ + return val; // String + } + return addSpace ? " " + val : val; // String + }, + // font operations + makeFontString: function(font){ + // summary: converts a font object to a CSS font string + // font: Object: font object (see dojox.gfx.defaultFont) + return font.style + " " + font.variant + " " + font.weight + " " + font.size + " " + font.family; // Object + }, + splitFontString: function(str){ + // summary: + // converts a CSS font string to a font object + // description: + // Converts a CSS font string to a gfx font object. The CSS font + // string components should follow the W3C specified order + // (see http://www.w3.org/TR/CSS2/fonts.html#font-shorthand): + // style, variant, weight, size, optional line height (will be + // ignored), and family. + // str: String + // a CSS font string + var font = g.getDefault("Font"); + var t = str.split(/\s+/); + do{ + if(t.length < 5){ break; } + font.style = t[0]; + font.variant = t[1]; + font.weight = t[2]; + var i = t[3].indexOf("/"); + font.size = i < 0 ? t[3] : t[3].substring(0, i); + var j = 4; + if(i < 0){ + if(t[4] == "/"){ + j = 6; + }else if(t[4].charAt(0) == "/"){ + j = 5; + } + } + if(j < t.length){ + font.family = t.slice(j).join(" "); + } + }while(false); + return font; // Object + }, + // length operations + cm_in_pt: 72 / 2.54, + // cm_in_pt: Number + // points per centimeter (constant) + mm_in_pt: 7.2 / 2.54, + // mm_in_pt: Number + // points per millimeter (constant) + px_in_pt: function(){ + // summary: returns the current number of pixels per point. + return g._base._getCachedFontMeasurements()["12pt"] / 12; // Number + }, + pt2px: function(len){ + // summary: converts points to pixels + // len: Number + // a value in points + return len * g.px_in_pt(); // Number + }, + px2pt: function(len){ + // summary: converts pixels to points + // len: Number + // a value in pixels + return len / g.px_in_pt(); // Number + }, + normalizedLength: function(len) { + // summary: converts any length value to pixels + // len: String + // a length, e.g., '12pc' + if(len.length === 0){ return 0; } + if(len.length > 2){ + var px_in_pt = g.px_in_pt(); + var val = parseFloat(len); + switch(len.slice(-2)){ + case "px": return val; + case "pt": return val * px_in_pt; + case "in": return val * 72 * px_in_pt; + case "pc": return val * 12 * px_in_pt; + case "mm": return val * g.mm_in_pt * px_in_pt; + case "cm": return val * g.cm_in_pt * px_in_pt; + } + } + return parseFloat(len); // Number + }, + + pathVmlRegExp: /([A-Za-z]+)|(\d+(\.\d+)?)|(\.\d+)|(-\d+(\.\d+)?)|(-\.\d+)/g, + // pathVmlRegExp: RegExp + // a constant regular expression used to split a SVG/VML path into primitive components + pathSvgRegExp: /([A-Za-z])|(\d+(\.\d+)?)|(\.\d+)|(-\d+(\.\d+)?)|(-\.\d+)/g, + // pathVmlRegExp: RegExp + // a constant regular expression used to split a SVG/VML path into primitive components + + equalSources: function(a /*Object*/, b /*Object*/){ + // summary: compares event sources, returns true if they are equal + // a: first event source + // b: event source to compare against a + return a && b && a === b; + }, + + switchTo: function(renderer/*String|Object*/){ + // summary: switch the graphics implementation to the specified renderer. + // renderer: + // Either the string name of a renderer (eg. 'canvas', 'svg, ...) or the renderer + // object to switch to. + var ns = typeof renderer == "string" ? g[renderer] : renderer; + if(ns){ + arr.forEach(["Group", "Rect", "Ellipse", "Circle", "Line", + "Polyline", "Image", "Text", "Path", "TextPath", + "Surface", "createSurface", "fixTarget"], function(name){ + g[name] = ns[name]; + }); + } + } + }); + return g; // defaults object api +}); + +}, +'dijit/focus':function(){ +define("dijit/focus", [ + "dojo/aspect", + "dojo/_base/declare", // declare + "dojo/dom", // domAttr.get dom.isDescendant + "dojo/dom-attr", // domAttr.get dom.isDescendant + "dojo/dom-construct", // connect to domConstruct.empty, domConstruct.destroy + "dojo/Evented", + "dojo/_base/lang", // lang.hitch + "dojo/on", + "dojo/ready", + "dojo/_base/sniff", // has("ie") + "dojo/Stateful", + "dojo/_base/unload", // unload.addOnWindowUnload + "dojo/_base/window", // win.body + "dojo/window", // winUtils.get + "./a11y", // a11y.isTabNavigable + "./registry", // registry.byId + "." // to set dijit.focus +], function(aspect, declare, dom, domAttr, domConstruct, Evented, lang, on, ready, has, Stateful, unload, win, winUtils, + a11y, registry, dijit){ + + // module: + // dijit/focus + // summary: + // Returns a singleton that tracks the currently focused node, and which widgets are currently "active". + +/*===== + dijit.focus = { + // summary: + // Tracks the currently focused node, and which widgets are currently "active". + // Access via require(["dijit/focus"], function(focus){ ... }). + // + // A widget is considered active if it or a descendant widget has focus, + // or if a non-focusable node of this widget or a descendant was recently clicked. + // + // Call focus.watch("curNode", callback) to track the current focused DOMNode, + // or focus.watch("activeStack", callback) to track the currently focused stack of widgets. + // + // Call focus.on("widget-blur", func) or focus.on("widget-focus", ...) to monitor when + // when widgets become active/inactive + // + // Finally, focus(node) will focus a node, suppressing errors if the node doesn't exist. + + // curNode: DomNode + // Currently focused item on screen + curNode: null, + + // activeStack: dijit._Widget[] + // List of currently active widgets (focused widget and it's ancestors) + activeStack: [], + + registerIframe: function(iframe){ + // summary: + // Registers listeners on the specified iframe so that any click + // or focus event on that iframe (or anything in it) is reported + // as a focus/click event on the <iframe> itself. + // description: + // Currently only used by editor. + // returns: + // Handle with remove() method to deregister. + }, + + registerWin: function(targetWindow, effectiveNode){ + // summary: + // Registers listeners on the specified window (either the main + // window or an iframe's window) to detect when the user has clicked somewhere + // or focused somewhere. + // description: + // Users should call registerIframe() instead of this method. + // targetWindow: Window? + // If specified this is the window associated with the iframe, + // i.e. iframe.contentWindow. + // effectiveNode: DOMNode? + // If specified, report any focus events inside targetWindow as + // an event on effectiveNode, rather than on evt.target. + // returns: + // Handle with remove() method to deregister. + } + }; +=====*/ + + var FocusManager = declare([Stateful, Evented], { + // curNode: DomNode + // Currently focused item on screen + curNode: null, + + // activeStack: dijit._Widget[] + // List of currently active widgets (focused widget and it's ancestors) + activeStack: [], + + constructor: function(){ + // Don't leave curNode/prevNode pointing to bogus elements + var check = lang.hitch(this, function(node){ + if(dom.isDescendant(this.curNode, node)){ + this.set("curNode", null); + } + if(dom.isDescendant(this.prevNode, node)){ + this.set("prevNode", null); + } + }); + aspect.before(domConstruct, "empty", check); + aspect.before(domConstruct, "destroy", check); + }, + + registerIframe: function(/*DomNode*/ iframe){ + // summary: + // Registers listeners on the specified iframe so that any click + // or focus event on that iframe (or anything in it) is reported + // as a focus/click event on the <iframe> itself. + // description: + // Currently only used by editor. + // returns: + // Handle with remove() method to deregister. + return this.registerWin(iframe.contentWindow, iframe); + }, + + registerWin: function(/*Window?*/targetWindow, /*DomNode?*/ effectiveNode){ + // summary: + // Registers listeners on the specified window (either the main + // window or an iframe's window) to detect when the user has clicked somewhere + // or focused somewhere. + // description: + // Users should call registerIframe() instead of this method. + // targetWindow: + // If specified this is the window associated with the iframe, + // i.e. iframe.contentWindow. + // effectiveNode: + // If specified, report any focus events inside targetWindow as + // an event on effectiveNode, rather than on evt.target. + // returns: + // Handle with remove() method to deregister. + + // TODO: make this function private in 2.0; Editor/users should call registerIframe(), + + var _this = this; + var mousedownListener = function(evt){ + _this._justMouseDowned = true; + setTimeout(function(){ _this._justMouseDowned = false; }, 0); + + // workaround weird IE bug where the click is on an orphaned node + // (first time clicking a Select/DropDownButton inside a TooltipDialog) + if(has("ie") && evt && evt.srcElement && evt.srcElement.parentNode == null){ + return; + } + + _this._onTouchNode(effectiveNode || evt.target || evt.srcElement, "mouse"); + }; + + // Listen for blur and focus events on targetWindow's document. + // IIRC, I'm using attachEvent() rather than dojo.connect() because focus/blur events don't bubble + // through dojo.connect(), and also maybe to catch the focus events early, before onfocus handlers + // fire. + // Connect to <html> (rather than document) on IE to avoid memory leaks, but document on other browsers because + // (at least for FF) the focus event doesn't fire on <html> or <body>. + var doc = has("ie") ? targetWindow.document.documentElement : targetWindow.document; + if(doc){ + if(has("ie")){ + targetWindow.document.body.attachEvent('onmousedown', mousedownListener); + var activateListener = function(evt){ + // IE reports that nodes like <body> have gotten focus, even though they have tabIndex=-1, + // ignore those events + var tag = evt.srcElement.tagName.toLowerCase(); + if(tag == "#document" || tag == "body"){ return; } + + // Previous code called _onTouchNode() for any activate event on a non-focusable node. Can + // probably just ignore such an event as it will be handled by onmousedown handler above, but + // leaving the code for now. + if(a11y.isTabNavigable(evt.srcElement)){ + _this._onFocusNode(effectiveNode || evt.srcElement); + }else{ + _this._onTouchNode(effectiveNode || evt.srcElement); + } + }; + doc.attachEvent('onactivate', activateListener); + var deactivateListener = function(evt){ + _this._onBlurNode(effectiveNode || evt.srcElement); + }; + doc.attachEvent('ondeactivate', deactivateListener); + + return { + remove: function(){ + targetWindow.document.detachEvent('onmousedown', mousedownListener); + doc.detachEvent('onactivate', activateListener); + doc.detachEvent('ondeactivate', deactivateListener); + doc = null; // prevent memory leak (apparent circular reference via closure) + } + }; + }else{ + doc.body.addEventListener('mousedown', mousedownListener, true); + doc.body.addEventListener('touchstart', mousedownListener, true); + var focusListener = function(evt){ + _this._onFocusNode(effectiveNode || evt.target); + }; + doc.addEventListener('focus', focusListener, true); + var blurListener = function(evt){ + _this._onBlurNode(effectiveNode || evt.target); + }; + doc.addEventListener('blur', blurListener, true); + + return { + remove: function(){ + doc.body.removeEventListener('mousedown', mousedownListener, true); + doc.body.removeEventListener('touchstart', mousedownListener, true); + doc.removeEventListener('focus', focusListener, true); + doc.removeEventListener('blur', blurListener, true); + doc = null; // prevent memory leak (apparent circular reference via closure) + } + }; + } + } + }, + + _onBlurNode: function(/*DomNode*/ /*===== node =====*/){ + // summary: + // Called when focus leaves a node. + // Usually ignored, _unless_ it *isn't* followed by touching another node, + // which indicates that we tabbed off the last field on the page, + // in which case every widget is marked inactive + this.set("prevNode", this.curNode); + this.set("curNode", null); + + if(this._justMouseDowned){ + // the mouse down caused a new widget to be marked as active; this blur event + // is coming late, so ignore it. + return; + } + + // if the blur event isn't followed by a focus event then mark all widgets as inactive. + if(this._clearActiveWidgetsTimer){ + clearTimeout(this._clearActiveWidgetsTimer); + } + this._clearActiveWidgetsTimer = setTimeout(lang.hitch(this, function(){ + delete this._clearActiveWidgetsTimer; + this._setStack([]); + this.prevNode = null; + }), 100); + }, + + _onTouchNode: function(/*DomNode*/ node, /*String*/ by){ + // summary: + // Callback when node is focused or mouse-downed + // node: + // The node that was touched. + // by: + // "mouse" if the focus/touch was caused by a mouse down event + + // ignore the recent blurNode event + if(this._clearActiveWidgetsTimer){ + clearTimeout(this._clearActiveWidgetsTimer); + delete this._clearActiveWidgetsTimer; + } + + // compute stack of active widgets (ex: ComboButton --> Menu --> MenuItem) + var newStack=[]; + try{ + while(node){ + var popupParent = domAttr.get(node, "dijitPopupParent"); + if(popupParent){ + node=registry.byId(popupParent).domNode; + }else if(node.tagName && node.tagName.toLowerCase() == "body"){ + // is this the root of the document or just the root of an iframe? + if(node === win.body()){ + // node is the root of the main document + break; + } + // otherwise, find the iframe this node refers to (can't access it via parentNode, + // need to do this trick instead). window.frameElement is supported in IE/FF/Webkit + node=winUtils.get(node.ownerDocument).frameElement; + }else{ + // if this node is the root node of a widget, then add widget id to stack, + // except ignore clicks on disabled widgets (actually focusing a disabled widget still works, + // to support MenuItem) + var id = node.getAttribute && node.getAttribute("widgetId"), + widget = id && registry.byId(id); + if(widget && !(by == "mouse" && widget.get("disabled"))){ + newStack.unshift(id); + } + node=node.parentNode; + } + } + }catch(e){ /* squelch */ } + + this._setStack(newStack, by); + }, + + _onFocusNode: function(/*DomNode*/ node){ + // summary: + // Callback when node is focused + + if(!node){ + return; + } + + if(node.nodeType == 9){ + // Ignore focus events on the document itself. This is here so that + // (for example) clicking the up/down arrows of a spinner + // (which don't get focus) won't cause that widget to blur. (FF issue) + return; + } + + this._onTouchNode(node); + + if(node == this.curNode){ return; } + this.set("curNode", node); + }, + + _setStack: function(/*String[]*/ newStack, /*String*/ by){ + // summary: + // The stack of active widgets has changed. Send out appropriate events and records new stack. + // newStack: + // array of widget id's, starting from the top (outermost) widget + // by: + // "mouse" if the focus/touch was caused by a mouse down event + + var oldStack = this.activeStack; + this.set("activeStack", newStack); + + // compare old stack to new stack to see how many elements they have in common + for(var nCommon=0; nCommon<Math.min(oldStack.length, newStack.length); nCommon++){ + if(oldStack[nCommon] != newStack[nCommon]){ + break; + } + } + + var widget; + // for all elements that have gone out of focus, set focused=false + for(var i=oldStack.length-1; i>=nCommon; i--){ + widget = registry.byId(oldStack[i]); + if(widget){ + widget._hasBeenBlurred = true; // TODO: used by form widgets, should be moved there + widget.set("focused", false); + if(widget._focusManager == this){ + widget._onBlur(by); + } + this.emit("widget-blur", widget, by); + } + } + + // for all element that have come into focus, set focused=true + for(i=nCommon; i<newStack.length; i++){ + widget = registry.byId(newStack[i]); + if(widget){ + widget.set("focused", true); + if(widget._focusManager == this){ + widget._onFocus(by); + } + this.emit("widget-focus", widget, by); + } + } + }, + + focus: function(node){ + // summary: + // Focus the specified node, suppressing errors if they occur + if(node){ + try{ node.focus(); }catch(e){/*quiet*/} + } + } + }); + + var singleton = new FocusManager(); + + // register top window and all the iframes it contains + ready(function(){ + var handle = singleton.registerWin(win.doc.parentWindow || win.doc.defaultView); + if(has("ie")){ + unload.addOnWindowUnload(function(){ + handle.remove(); + handle = null; + }) + } + }); + + // Setup dijit.focus as a pointer to the singleton but also (for backwards compatibility) + // as a function to set focus. + dijit.focus = function(node){ + singleton.focus(node); // indirection here allows dijit/_base/focus.js to override behavior + }; + for(var attr in singleton){ + if(!/^_/.test(attr)){ + dijit.focus[attr] = typeof singleton[attr] == "function" ? lang.hitch(singleton, attr) : singleton[attr]; + } + } + singleton.watch(function(attr, oldVal, newVal){ + dijit.focus[attr] = newVal; + }); + + return singleton; +}); + +}, +'dojox/charting/widget/Legend':function(){ +define("dojox/charting/widget/Legend", ["dojo/_base/lang", "dojo/_base/html", "dojo/_base/declare", "dijit/_Widget", "dojox/gfx","dojo/_base/array", + "dojox/lang/functional", "dojox/lang/functional/array", "dojox/lang/functional/fold", + "dojo/dom", "dojo/dom-construct", "dojo/dom-class","dijit/_base/manager"], + function(lang, html, declare, Widget, gfx, arrayUtil, df, dfa, dff, + dom, domFactory, domClass, widgetManager){ +/*===== +var Widget = dijit._Widget; +=====*/ + + var REVERSED_SERIES = /\.(StackedColumns|StackedAreas|ClusteredBars)$/; + + return declare("dojox.charting.widget.Legend", Widget, { + // summary: A legend for a chart. A legend contains summary labels for + // each series of data contained in the chart. + // + // Set the horizontal attribute to boolean false to layout legend labels vertically. + // Set the horizontal attribute to a number to layout legend labels in horizontal + // rows each containing that number of labels (except possibly the last row). + // + // (Line or Scatter charts (colored lines with shape symbols) ) + // -o- Series1 -X- Series2 -v- Series3 + // + // (Area/Bar/Pie charts (letters represent colors)) + // [a] Series1 [b] Series2 [c] Series3 + + chartRef: "", + horizontal: true, + swatchSize: 18, + + legendBody: null, + + postCreate: function(){ + if(!this.chart){ + if(!this.chartRef){ return; } + this.chart = widgetManager.byId(this.chartRef); + if(!this.chart){ + var node = dom.byId(this.chartRef); + if(node){ + this.chart = widgetManager.byNode(node); + }else{ + console.log("Could not find chart instance with id: " + this.chartRef); + return; + } + } + this.series = this.chart.chart.series; + }else{ + this.series = this.chart.series; + } + + this.refresh(); + }, + buildRendering: function(){ + this.domNode = domFactory.create("table", + {role: "group", "aria-label": "chart legend", "class": "dojoxLegendNode"}); + this.legendBody = domFactory.create("tbody", null, this.domNode); + this.inherited(arguments); + }, + refresh: function(){ + // summary: regenerates the legend to reflect changes to the chart + + // cleanup + if(this._surfaces){ + arrayUtil.forEach(this._surfaces, function(surface){ + surface.destroy(); + }); + } + this._surfaces = []; + while(this.legendBody.lastChild){ + domFactory.destroy(this.legendBody.lastChild); + } + + if(this.horizontal){ + domClass.add(this.domNode, "dojoxLegendHorizontal"); + // make a container <tr> + this._tr = domFactory.create("tr", null, this.legendBody); + this._inrow = 0; + } + + var s = this.series; + if(s.length == 0){ + return; + } + if(s[0].chart.stack[0].declaredClass == "dojox.charting.plot2d.Pie"){ + var t = s[0].chart.stack[0]; + if(typeof t.run.data[0] == "number"){ + var filteredRun = df.map(t.run.data, "Math.max(x, 0)"); + if(df.every(filteredRun, "<= 0")){ + return; + } + var slices = df.map(filteredRun, "/this", df.foldl(filteredRun, "+", 0)); + arrayUtil.forEach(slices, function(x, i){ + this._addLabel(t.dyn[i], t._getLabel(x * 100) + "%"); + }, this); + }else{ + arrayUtil.forEach(t.run.data, function(x, i){ + this._addLabel(t.dyn[i], x.legend || x.text || x.y); + }, this); + } + }else{ + if(this._isReversal()){ + s = s.slice(0).reverse(); + } + arrayUtil.forEach(s, function(x){ + this._addLabel(x.dyn, x.legend || x.name); + }, this); + } + }, + _addLabel: function(dyn, label){ + // create necessary elements + var wrapper = domFactory.create("td"), + icon = domFactory.create("div", null, wrapper), + text = domFactory.create("label", null, wrapper), + div = domFactory.create("div", { + style: { + "width": this.swatchSize + "px", + "height":this.swatchSize + "px", + "float": "left" + } + }, icon); + domClass.add(icon, "dojoxLegendIcon dijitInline"); + domClass.add(text, "dojoxLegendText"); + // create a skeleton + if(this._tr){ + // horizontal + this._tr.appendChild(wrapper); + if(++this._inrow === this.horizontal){ + // make a fresh container <tr> + this._tr = domFactory.create("tr", null, this.legendBody); + this._inrow = 0; + } + }else{ + // vertical + var tr = domFactory.create("tr", null, this.legendBody); + tr.appendChild(wrapper); + } + + // populate the skeleton + this._makeIcon(div, dyn); + text.innerHTML = String(label); + text.dir = this.getTextDir(label, text.dir); + }, + _makeIcon: function(div, dyn){ + var mb = { h: this.swatchSize, w: this.swatchSize }; + var surface = gfx.createSurface(div, mb.w, mb.h); + this._surfaces.push(surface); + if(dyn.fill){ + // regions + surface.createRect({x: 2, y: 2, width: mb.w - 4, height: mb.h - 4}). + setFill(dyn.fill).setStroke(dyn.stroke); + }else if(dyn.stroke || dyn.marker){ + // draw line + var line = {x1: 0, y1: mb.h / 2, x2: mb.w, y2: mb.h / 2}; + if(dyn.stroke){ + surface.createLine(line).setStroke(dyn.stroke); + } + if(dyn.marker){ + // draw marker on top + var c = {x: mb.w / 2, y: mb.h / 2}; + if(dyn.stroke){ + surface.createPath({path: "M" + c.x + " " + c.y + " " + dyn.marker}). + setFill(dyn.stroke.color).setStroke(dyn.stroke); + }else{ + surface.createPath({path: "M" + c.x + " " + c.y + " " + dyn.marker}). + setFill(dyn.color).setStroke(dyn.color); + } + } + }else{ + // nothing + surface.createRect({x: 2, y: 2, width: mb.w - 4, height: mb.h - 4}). + setStroke("black"); + surface.createLine({x1: 2, y1: 2, x2: mb.w - 2, y2: mb.h - 2}).setStroke("black"); + surface.createLine({x1: 2, y1: mb.h - 2, x2: mb.w - 2, y2: 2}).setStroke("black"); + } + }, + _isReversal: function(){ + return (!this.horizontal) && arrayUtil.some(this.chart.stack, function(item){ + return REVERSED_SERIES.test(item.declaredClass); + }); + } + }); +}); + +}, +'dojox/charting/plot2d/StackedLines':function(){ +define("dojox/charting/plot2d/StackedLines", ["dojo/_base/declare", "./Stacked"], function(declare, Stacked){ +/*===== +var Stacked = dojox.charting.plot2d.Stacked; +=====*/ + return declare("dojox.charting.plot2d.StackedLines", Stacked, { + // summary: + // A convenience object to create a stacked line chart. + constructor: function(){ + // summary: + // Force our Stacked base to be lines only. + this.opt.lines = true; + } + }); +}); + +}, +'dojox/charting/plot2d/StackedColumns':function(){ +define("dojox/charting/plot2d/StackedColumns", ["dojo/_base/lang", "dojo/_base/array", "dojo/_base/declare", "./Columns", "./common", + "dojox/lang/functional", "dojox/lang/functional/reversed", "dojox/lang/functional/sequence"], + function(lang, arr, declare, Columns, dc, df, dfr, dfs){ + + var purgeGroup = dfr.lambda("item.purgeGroup()"); +/*===== +var Columns = dojox.charting.plot2d.Columns; +=====*/ + return declare("dojox.charting.plot2d.StackedColumns", Columns, { + // summary: + // The plot object representing a stacked column chart (vertical bars). + getSeriesStats: function(){ + // summary: + // Calculate the min/max on all attached series in both directions. + // returns: Object + // {hmin, hmax, vmin, vmax} min/max in both directions. + var stats = dc.collectStackedStats(this.series); + this._maxRunLength = stats.hmax; + stats.hmin -= 0.5; + stats.hmax += 0.5; + return stats; + }, + render: function(dim, offsets){ + // summary: + // Run the calculations for any axes for this plot. + // dim: Object + // An object in the form of { width, height } + // offsets: Object + // An object of the form { l, r, t, b}. + // returns: dojox.charting.plot2d.StackedColumns + // A reference to this plot for functional chaining. + if(this._maxRunLength <= 0){ + return this; + } + + // stack all values + var acc = df.repeat(this._maxRunLength, "-> 0", 0); + for(var i = 0; i < this.series.length; ++i){ + var run = this.series[i]; + for(var j = 0; j < run.data.length; ++j){ + var value = run.data[j]; + if(value !== null){ + var v = typeof value == "number" ? value : value.y; + if(isNaN(v)){ v = 0; } + acc[j] += v; + } + } + } + // draw runs in backwards + if(this.zoom && !this.isDataDirty()){ + return this.performZoom(dim, offsets); + } + this.resetEvents(); + this.dirty = this.isDirty(); + if(this.dirty){ + arr.forEach(this.series, purgeGroup); + this._eventSeries = {}; + this.cleanGroup(); + var s = this.group; + df.forEachRev(this.series, function(item){ item.cleanGroup(s); }); + } + var t = this.chart.theme, f, gap, width, + ht = this._hScaler.scaler.getTransformerFromModel(this._hScaler), + vt = this._vScaler.scaler.getTransformerFromModel(this._vScaler), + events = this.events(); + f = dc.calculateBarSize(this._hScaler.bounds.scale, this.opt); + gap = f.gap; + width = f.size; + for(var i = this.series.length - 1; i >= 0; --i){ + var run = this.series[i]; + if(!this.dirty && !run.dirty){ + t.skip(); + this._reconnectEvents(run.name); + continue; + } + run.cleanGroup(); + var theme = t.next("column", [this.opt, run]), s = run.group, + eventSeries = new Array(acc.length); + for(var j = 0; j < acc.length; ++j){ + var value = run.data[j]; + if(value !== null){ + var v = acc[j], + height = vt(v), + finalTheme = typeof value != "number" ? + t.addMixin(theme, "column", value, true) : + t.post(theme, "column"); + if(width >= 1 && height >= 0){ + var rect = { + x: offsets.l + ht(j + 0.5) + gap, + y: dim.height - offsets.b - vt(v), + width: width, height: height + }; + var specialFill = this._plotFill(finalTheme.series.fill, dim, offsets); + specialFill = this._shapeFill(specialFill, rect); + var shape = s.createRect(rect).setFill(specialFill).setStroke(finalTheme.series.stroke); + run.dyn.fill = shape.getFill(); + run.dyn.stroke = shape.getStroke(); + if(events){ + var o = { + element: "column", + index: j, + run: run, + shape: shape, + x: j + 0.5, + y: v + }; + this._connectEvents(o); + eventSeries[j] = o; + } + if(this.animate){ + this._animateColumn(shape, dim.height - offsets.b, height); + } + } + } + } + this._eventSeries[run.name] = eventSeries; + run.dirty = false; + // update the accumulator + for(var j = 0; j < run.data.length; ++j){ + var value = run.data[j]; + if(value !== null){ + var v = typeof value == "number" ? value : value.y; + if(isNaN(v)){ v = 0; } + acc[j] -= v; + } + } + } + this.dirty = false; + return this; // dojox.charting.plot2d.StackedColumns + } + }); +}); + +}, +'dojox/charting/Series':function(){ +define(["dojo/_base/lang", "dojo/_base/declare", "./Element"], + function(lang, declare, Element){ + /*===== + dojox.charting.__SeriesCtorArgs = function(plot){ + // summary: + // An optional arguments object that can be used in the Series constructor. + // plot: String? + // The plot (by name) that this series belongs to. + this.plot = plot; + } + + var Element = dojox.charting.Element; + =====*/ + return declare("dojox.charting.Series", Element, { + // summary: + // An object representing a series of data for plotting on a chart. + constructor: function(chart, data, kwArgs){ + // summary: + // Create a new data series object for use within charting. + // chart: dojox.charting.Chart + // The chart that this series belongs to. + // data: Array|Object: + // The array of data points (either numbers or objects) that + // represents the data to be drawn. Or it can be an object. In + // the latter case, it should have a property "data" (an array), + // destroy(), and setSeriesObject(). + // kwArgs: dojox.charting.__SeriesCtorArgs? + // An optional keyword arguments object to set details for this series. + lang.mixin(this, kwArgs); + if(typeof this.plot != "string"){ this.plot = "default"; } + this.update(data); + }, + + clear: function(){ + // summary: + // Clear the calculated additional parameters set on this series. + this.dyn = {}; + }, + + update: function(data){ + // summary: + // Set data and make this object dirty, so it can be redrawn. + // data: Array|Object: + // The array of data points (either numbers or objects) that + // represents the data to be drawn. Or it can be an object. In + // the latter case, it should have a property "data" (an array), + // destroy(), and setSeriesObject(). + if(lang.isArray(data)){ + this.data = data; + }else{ + this.source = data; + this.data = this.source.data; + if(this.source.setSeriesObject){ + this.source.setSeriesObject(this); + } + } + this.dirty = true; + this.clear(); + } + }); + +}); + +}, +'dojox/charting/plot2d/Default':function(){ +define("dojox/charting/plot2d/Default", ["dojo/_base/lang", "dojo/_base/declare", "dojo/_base/array", + "./Base", "./common", "dojox/lang/functional", "dojox/lang/functional/reversed", "dojox/lang/utils", "dojox/gfx/fx"], + function(lang, declare, arr, Base, dc, df, dfr, du, fx){ + + /*===== + dojo.declare("dojox.charting.plot2d.__DefaultCtorArgs", dojox.charting.plot2d.__PlotCtorArgs, { + // summary: + // The arguments used for any/most plots. + + // hAxis: String? + // The horizontal axis name. + hAxis: "x", + + // vAxis: String? + // The vertical axis name + vAxis: "y", + + // lines: Boolean? + // Whether or not to draw lines on this plot. Defaults to true. + lines: true, + + // areas: Boolean? + // Whether or not to draw areas on this plot. Defaults to false. + areas: false, + + // markers: Boolean? + // Whether or not to draw markers at data points on this plot. Default is false. + markers: false, + + // tension: Number|String? + // Whether or not to apply 'tensioning' to the lines on this chart. + // Options include a number, "X", "x", or "S"; if a number is used, the + // simpler bezier curve calculations are used to draw the lines. If X, x or S + // is used, the more accurate smoothing algorithm is used. + tension: "", + + // animate: Boolean? + // Whether or not to animate the chart to place. + animate: false, + + // stroke: dojox.gfx.Stroke? + // An optional stroke to use for any series on the plot. + stroke: {}, + + // outline: dojox.gfx.Stroke? + // An optional stroke used to outline any series on the plot. + outline: {}, + + // shadow: dojox.gfx.Stroke? + // An optional stroke to use to draw any shadows for a series on a plot. + shadow: {}, + + // fill: dojox.gfx.Fill? + // Any fill to be used for elements on the plot (such as areas). + fill: {}, + + // font: String? + // A font definition to be used for labels and other text-based elements on the plot. + font: "", + + // fontColor: String|dojo.Color? + // The color to be used for any text-based elements on the plot. + fontColor: "", + + // markerStroke: dojo.gfx.Stroke? + // An optional stroke to use for any markers on the plot. + markerStroke: {}, + + // markerOutline: dojo.gfx.Stroke? + // An optional outline to use for any markers on the plot. + markerOutline: {}, + + // markerShadow: dojo.gfx.Stroke? + // An optional shadow to use for any markers on the plot. + markerShadow: {}, + + // markerFill: dojo.gfx.Fill? + // An optional fill to use for any markers on the plot. + markerFill: {}, + + // markerFont: String? + // An optional font definition to use for any markers on the plot. + markerFont: "", + + // markerFontColor: String|dojo.Color? + // An optional color to use for any marker text on the plot. + markerFontColor: "", + + // enableCache: Boolean? + // Whether the markers are cached from one rendering to another. This improves the rendering performance of + // successive rendering but penalize the first rendering. Default false. + enableCache: false + }); + + var Base = dojox.charting.plot2d.Base; +=====*/ + + var purgeGroup = dfr.lambda("item.purgeGroup()"); + + var DEFAULT_ANIMATION_LENGTH = 1200; // in ms + + return declare("dojox.charting.plot2d.Default", Base, { + defaultParams: { + hAxis: "x", // use a horizontal axis named "x" + vAxis: "y", // use a vertical axis named "y" + lines: true, // draw lines + areas: false, // draw areas + markers: false, // draw markers + tension: "", // draw curved lines (tension is "X", "x", or "S") + animate: false, // animate chart to place + enableCache: false + }, + optionalParams: { + // theme component + stroke: {}, + outline: {}, + shadow: {}, + fill: {}, + font: "", + fontColor: "", + markerStroke: {}, + markerOutline: {}, + markerShadow: {}, + markerFill: {}, + markerFont: "", + markerFontColor: "" + }, + + constructor: function(chart, kwArgs){ + // summary: + // Return a new plot. + // chart: dojox.charting.Chart + // The chart this plot belongs to. + // kwArgs: dojox.charting.plot2d.__DefaultCtorArgs? + // An optional arguments object to help define this plot. + this.opt = lang.clone(this.defaultParams); + du.updateWithObject(this.opt, kwArgs); + du.updateWithPattern(this.opt, kwArgs, this.optionalParams); + this.series = []; + this.hAxis = this.opt.hAxis; + this.vAxis = this.opt.vAxis; + + // animation properties + this.animate = this.opt.animate; + }, + + createPath: function(run, creator, params){ + var path; + if(this.opt.enableCache && run._pathFreePool.length > 0){ + path = run._pathFreePool.pop(); + path.setShape(params); + // was cleared, add it back + creator.add(path); + }else{ + path = creator.createPath(params); + } + if(this.opt.enableCache){ + run._pathUsePool.push(path); + } + return path; + }, + + render: function(dim, offsets){ + // summary: + // Render/draw everything on this plot. + // dim: Object + // An object of the form { width, height } + // offsets: Object + // An object of the form { l, r, t, b } + // returns: dojox.charting.plot2d.Default + // A reference to this plot for functional chaining. + + // make sure all the series is not modified + if(this.zoom && !this.isDataDirty()){ + return this.performZoom(dim, offsets); + } + + this.resetEvents(); + this.dirty = this.isDirty(); + if(this.dirty){ + arr.forEach(this.series, purgeGroup); + this._eventSeries = {}; + this.cleanGroup(); + this.group.setTransform(null); + var s = this.group; + df.forEachRev(this.series, function(item){ item.cleanGroup(s); }); + } + var t = this.chart.theme, stroke, outline, marker, events = this.events(); + + for(var i = this.series.length - 1; i >= 0; --i){ + var run = this.series[i]; + if(!this.dirty && !run.dirty){ + t.skip(); + this._reconnectEvents(run.name); + continue; + } + run.cleanGroup(); + if(this.opt.enableCache){ + run._pathFreePool = (run._pathFreePool?run._pathFreePool:[]).concat(run._pathUsePool?run._pathUsePool:[]); + run._pathUsePool = []; + } + if(!run.data.length){ + run.dirty = false; + t.skip(); + continue; + } + + var theme = t.next(this.opt.areas ? "area" : "line", [this.opt, run], true), + s = run.group, rsegments = [], startindexes = [], rseg = null, lpoly, + ht = this._hScaler.scaler.getTransformerFromModel(this._hScaler), + vt = this._vScaler.scaler.getTransformerFromModel(this._vScaler), + eventSeries = this._eventSeries[run.name] = new Array(run.data.length); + + // optim works only for index based case + var indexed = typeof run.data[0] == "number"; + var min = indexed?Math.max(0, Math.floor(this._hScaler.bounds.from - 1)):0, + max = indexed?Math.min(run.data.length, Math.ceil(this._hScaler.bounds.to)):run.data.length; + + // split the run data into dense segments (each containing no nulls) + for(var j = min; j < max; j++){ + if(run.data[j] != null){ + if(!rseg){ + rseg = []; + startindexes.push(j); + rsegments.push(rseg); + } + rseg.push(run.data[j]); + }else{ + rseg = null; + } + } + + for(var seg = 0; seg < rsegments.length; seg++){ + if(typeof rsegments[seg][0] == "number"){ + lpoly = arr.map(rsegments[seg], function(v, i){ + return { + x: ht(i + startindexes[seg] + 1) + offsets.l, + y: dim.height - offsets.b - vt(v) + }; + }, this); + }else{ + lpoly = arr.map(rsegments[seg], function(v, i){ + return { + x: ht(v.x) + offsets.l, + y: dim.height - offsets.b - vt(v.y) + }; + }, this); + } + + var lpath = this.opt.tension ? dc.curve(lpoly, this.opt.tension) : ""; + + if(this.opt.areas && lpoly.length > 1){ + var fill = theme.series.fill; + var apoly = lang.clone(lpoly); + if(this.opt.tension){ + var apath = "L" + apoly[apoly.length-1].x + "," + (dim.height - offsets.b) + + " L" + apoly[0].x + "," + (dim.height - offsets.b) + + " L" + apoly[0].x + "," + apoly[0].y; + run.dyn.fill = s.createPath(lpath + " " + apath).setFill(fill).getFill(); + } else { + apoly.push({x: lpoly[lpoly.length - 1].x, y: dim.height - offsets.b}); + apoly.push({x: lpoly[0].x, y: dim.height - offsets.b}); + apoly.push(lpoly[0]); + run.dyn.fill = s.createPolyline(apoly).setFill(fill).getFill(); + } + } + if(this.opt.lines || this.opt.markers){ + // need a stroke + stroke = theme.series.stroke; + if(theme.series.outline){ + outline = run.dyn.outline = dc.makeStroke(theme.series.outline); + outline.width = 2 * outline.width + stroke.width; + } + } + if(this.opt.markers){ + run.dyn.marker = theme.symbol; + } + var frontMarkers = null, outlineMarkers = null, shadowMarkers = null; + if(stroke && theme.series.shadow && lpoly.length > 1){ + var shadow = theme.series.shadow, + spoly = arr.map(lpoly, function(c){ + return {x: c.x + shadow.dx, y: c.y + shadow.dy}; + }); + if(this.opt.lines){ + if(this.opt.tension){ + run.dyn.shadow = s.createPath(dc.curve(spoly, this.opt.tension)).setStroke(shadow).getStroke(); + } else { + run.dyn.shadow = s.createPolyline(spoly).setStroke(shadow).getStroke(); + } + } + if(this.opt.markers && theme.marker.shadow){ + shadow = theme.marker.shadow; + shadowMarkers = arr.map(spoly, function(c){ + return this.createPath(run, s, "M" + c.x + " " + c.y + " " + theme.symbol). + setStroke(shadow).setFill(shadow.color); + }, this); + } + } + if(this.opt.lines && lpoly.length > 1){ + if(outline){ + if(this.opt.tension){ + run.dyn.outline = s.createPath(lpath).setStroke(outline).getStroke(); + } else { + run.dyn.outline = s.createPolyline(lpoly).setStroke(outline).getStroke(); + } + } + if(this.opt.tension){ + run.dyn.stroke = s.createPath(lpath).setStroke(stroke).getStroke(); + } else { + run.dyn.stroke = s.createPolyline(lpoly).setStroke(stroke).getStroke(); + } + } + if(this.opt.markers){ + frontMarkers = new Array(lpoly.length); + outlineMarkers = new Array(lpoly.length); + outline = null; + if(theme.marker.outline){ + outline = dc.makeStroke(theme.marker.outline); + outline.width = 2 * outline.width + (theme.marker.stroke ? theme.marker.stroke.width : 0); + } + arr.forEach(lpoly, function(c, i){ + var path = "M" + c.x + " " + c.y + " " + theme.symbol; + if(outline){ + outlineMarkers[i] = this.createPath(run, s, path).setStroke(outline); + } + frontMarkers[i] = this.createPath(run, s, path).setStroke(theme.marker.stroke).setFill(theme.marker.fill); + }, this); + run.dyn.markerFill = theme.marker.fill; + run.dyn.markerStroke = theme.marker.stroke; + if(events){ + arr.forEach(frontMarkers, function(s, i){ + var o = { + element: "marker", + index: i + startindexes[seg], + run: run, + shape: s, + outline: outlineMarkers[i] || null, + shadow: shadowMarkers && shadowMarkers[i] || null, + cx: lpoly[i].x, + cy: lpoly[i].y + }; + if(typeof rsegments[seg][0] == "number"){ + o.x = i + startindexes[seg] + 1; + o.y = rsegments[seg][i]; + }else{ + o.x = rsegments[seg][i].x; + o.y = rsegments[seg][i].y; + } + this._connectEvents(o); + eventSeries[i + startindexes[seg]] = o; + }, this); + }else{ + delete this._eventSeries[run.name]; + } + } + } + run.dirty = false; + } + if(this.animate){ + // grow from the bottom + var plotGroup = this.group; + fx.animateTransform(lang.delegate({ + shape: plotGroup, + duration: DEFAULT_ANIMATION_LENGTH, + transform:[ + {name:"translate", start: [0, dim.height - offsets.b], end: [0, 0]}, + {name:"scale", start: [1, 0], end:[1, 1]}, + {name:"original"} + ] + }, this.animate)).play(); + } + this.dirty = false; + return this; // dojox.charting.plot2d.Default + } + }); +}); + +}, +'dijit/main':function(){ +define("dijit/main", [ + "dojo/_base/kernel" +], function(dojo){ + // module: + // dijit + // summary: + // The dijit package main module + + return dojo.dijit; +}); + +}, +'dojox/charting/plot2d/Base':function(){ +define("dojox/charting/plot2d/Base", ["dojo/_base/lang", "dojo/_base/declare", "dojo/_base/connect", + "../Element", "./_PlotEvents", "dojo/_base/array", + "../scaler/primitive", "./common", "dojox/gfx/fx"], + function(lang, declare, hub, Element, PlotEvents, arr, primitive, common, fx){ +/*===== +var Element = dojox.charting.Element; +var PlotEvents = dojox.charting.plot2d._PlotEvents; +dojox.charting.plot2d.__PlotCtorArgs = function(){ + // summary: + // The base keyword arguments object for plot constructors. + // Note that the parameters for this may change based on the + // specific plot type (see the corresponding plot type for + // details). +} +=====*/ +return declare("dojox.charting.plot2d.Base", [Element, PlotEvents], { + constructor: function(chart, kwArgs){ + // summary: + // Create a base plot for charting. + // chart: dojox.chart.Chart + // The chart this plot belongs to. + // kwArgs: dojox.charting.plot2d.__PlotCtorArgs? + // An optional arguments object to help define the plot. + this.zoom = null, + this.zoomQueue = []; // zooming action task queue + this.lastWindow = {vscale: 1, hscale: 1, xoffset: 0, yoffset: 0}; + }, + clear: function(){ + // summary: + // Clear out all of the information tied to this plot. + // returns: dojox.charting.plot2d.Base + // A reference to this plot for functional chaining. + this.series = []; + this._hAxis = null; + this._vAxis = null; + this.dirty = true; + return this; // dojox.charting.plot2d.Base + }, + setAxis: function(axis){ + // summary: + // Set an axis for this plot. + // axis: dojox.charting.axis2d.Base + // The axis to set. + // returns: dojox.charting.plot2d.Base + // A reference to this plot for functional chaining. + if(axis){ + this[axis.vertical ? "_vAxis" : "_hAxis"] = axis; + } + return this; // dojox.charting.plot2d.Base + }, + toPage: function(coord){ + // summary: + // Compute page coordinates from plot axis data coordinates. + // coord: Object? + // The coordinates in plot axis data coordinate space. For cartesian charts that is of the following form: + // `{ hAxisName: 50, vAxisName: 200 }` + // If not provided return the tranform method instead of the result of the transformation. + // returns: Object + // The resulting page pixel coordinates. That is of the following form: + // `{ x: 50, y: 200 }` + var ah = this._hAxis, av = this._vAxis, + sh = ah.getScaler(), sv = av.getScaler(), + th = sh.scaler.getTransformerFromModel(sh), + tv = sv.scaler.getTransformerFromModel(sv), + c = this.chart.getCoords(), + o = this.chart.offsets, dim = this.chart.dim; + var t = function(coord){ + var r = {}; + r.x = th(coord[ah.name]) + c.x + o.l; + r.y = c.y + dim.height - o.b - tv(coord[av.name]); + return r; + }; + // if no coord return the function so that we can capture the current transforms + // and reuse them later on + return coord?t(coord):t; + }, + toData: function(coord){ + // summary: + // Compute plot axis data coordinates from page coordinates. + // coord: Object + // The pixel coordinate in page coordinate space. That is of the following form: + // `{ x: 50, y: 200 }` + // If not provided return the tranform method instead of the result of the transformation. + // returns: Object + // The resulting plot axis data coordinates. For cartesian charts that is of the following form: + // `{ hAxisName: 50, vAxisName: 200 }` + var ah = this._hAxis, av = this._vAxis, + sh = ah.getScaler(), sv = av.getScaler(), + th = sh.scaler.getTransformerFromPlot(sh), + tv = sv.scaler.getTransformerFromPlot(sv), + c = this.chart.getCoords(), + o = this.chart.offsets, dim = this.chart.dim; + var t = function(coord){ + var r = {}; + r[ah.name] = th(coord.x - c.x - o.l); + r[av.name] = tv(c.y + dim.height - coord.y - o.b); + return r; + }; + // if no coord return the function so that we can capture the current transforms + // and reuse them later on + return coord?t(coord):t; + }, + addSeries: function(run){ + // summary: + // Add a data series to this plot. + // run: dojox.charting.Series + // The series to be added. + // returns: dojox.charting.plot2d.Base + // A reference to this plot for functional chaining. + this.series.push(run); + return this; // dojox.charting.plot2d.Base + }, + getSeriesStats: function(){ + // summary: + // Calculate the min/max on all attached series in both directions. + // returns: Object + // {hmin, hmax, vmin, vmax} min/max in both directions. + return common.collectSimpleStats(this.series); + }, + calculateAxes: function(dim){ + // summary: + // Stub function for running the axis calculations (depricated). + // dim: Object + // An object of the form { width, height } + // returns: dojox.charting.plot2d.Base + // A reference to this plot for functional chaining. + this.initializeScalers(dim, this.getSeriesStats()); + return this; // dojox.charting.plot2d.Base + }, + isDirty: function(){ + // summary: + // Returns whether or not this plot needs to be rendered. + // returns: Boolean + // The state of the plot. + return this.dirty || this._hAxis && this._hAxis.dirty || this._vAxis && this._vAxis.dirty; // Boolean + }, + isDataDirty: function(){ + // summary: + // Returns whether or not any of this plot's data series need to be rendered. + // returns: Boolean + // Flag indicating if any of this plot's series are invalid and need rendering. + return arr.some(this.series, function(item){ return item.dirty; }); // Boolean + }, + performZoom: function(dim, offsets){ + // summary: + // Create/alter any zooming windows on this plot. + // dim: Object + // An object of the form { width, height }. + // offsets: Object + // An object of the form { l, r, t, b }. + // returns: dojox.charting.plot2d.Base + // A reference to this plot for functional chaining. + + // get current zooming various + var vs = this._vAxis.scale || 1, + hs = this._hAxis.scale || 1, + vOffset = dim.height - offsets.b, + hBounds = this._hScaler.bounds, + xOffset = (hBounds.from - hBounds.lower) * hBounds.scale, + vBounds = this._vScaler.bounds, + yOffset = (vBounds.from - vBounds.lower) * vBounds.scale, + // get incremental zooming various + rVScale = vs / this.lastWindow.vscale, + rHScale = hs / this.lastWindow.hscale, + rXOffset = (this.lastWindow.xoffset - xOffset)/ + ((this.lastWindow.hscale == 1)? hs : this.lastWindow.hscale), + rYOffset = (yOffset - this.lastWindow.yoffset)/ + ((this.lastWindow.vscale == 1)? vs : this.lastWindow.vscale), + + shape = this.group, + anim = fx.animateTransform(lang.delegate({ + shape: shape, + duration: 1200, + transform:[ + {name:"translate", start:[0, 0], end: [offsets.l * (1 - rHScale), vOffset * (1 - rVScale)]}, + {name:"scale", start:[1, 1], end: [rHScale, rVScale]}, + {name:"original"}, + {name:"translate", start: [0, 0], end: [rXOffset, rYOffset]} + ]}, this.zoom)); + + lang.mixin(this.lastWindow, {vscale: vs, hscale: hs, xoffset: xOffset, yoffset: yOffset}); + //add anim to zooming action queue, + //in order to avoid several zooming action happened at the same time + this.zoomQueue.push(anim); + //perform each anim one by one in zoomQueue + hub.connect(anim, "onEnd", this, function(){ + this.zoom = null; + this.zoomQueue.shift(); + if(this.zoomQueue.length > 0){ + this.zoomQueue[0].play(); + } + }); + if(this.zoomQueue.length == 1){ + this.zoomQueue[0].play(); + } + return this; // dojox.charting.plot2d.Base + }, + render: function(dim, offsets){ + // summary: + // Render the plot on the chart. + // dim: Object + // An object of the form { width, height }. + // offsets: Object + // An object of the form { l, r, t, b }. + // returns: dojox.charting.plot2d.Base + // A reference to this plot for functional chaining. + return this; // dojox.charting.plot2d.Base + }, + getRequiredColors: function(){ + // summary: + // Get how many data series we have, so we know how many colors to use. + // returns: Number + // The number of colors needed. + return this.series.length; // Number + }, + initializeScalers: function(dim, stats){ + // summary: + // Initializes scalers using attached axes. + // dim: Object: + // Size of a plot area in pixels as {width, height}. + // stats: Object: + // Min/max of data in both directions as {hmin, hmax, vmin, vmax}. + // returns: dojox.charting.plot2d.Base + // A reference to this plot for functional chaining. + if(this._hAxis){ + if(!this._hAxis.initialized()){ + this._hAxis.calculate(stats.hmin, stats.hmax, dim.width); + } + this._hScaler = this._hAxis.getScaler(); + }else{ + this._hScaler = primitive.buildScaler(stats.hmin, stats.hmax, dim.width); + } + if(this._vAxis){ + if(!this._vAxis.initialized()){ + this._vAxis.calculate(stats.vmin, stats.vmax, dim.height); + } + this._vScaler = this._vAxis.getScaler(); + }else{ + this._vScaler = primitive.buildScaler(stats.vmin, stats.vmax, dim.height); + } + return this; // dojox.charting.plot2d.Base + } +}); +}); + +}, +'dojox/charting/action2d/Tooltip':function(){ +define("dojox/charting/action2d/Tooltip", ["dojo/_base/kernel", "dijit/Tooltip","dojo/_base/lang", "dojo/_base/html", "dojo/_base/declare", "./PlotAction", + "dojox/gfx/matrix", "dojox/lang/functional", "dojox/lang/functional/scan", "dojox/lang/functional/fold"], + function(dojo, Tooltip, lang, html, declare, PlotAction, m, df, dfs, dff){ + + /*===== + dojo.declare("dojox.charting.action2d.__TooltipCtorArgs", dojox.charting.action2d.__PlotActionCtorArgs, { + // summary: + // Additional arguments for tooltip actions. + + // text: Function? + // The function that produces the text to be shown within a tooltip. By default this will be + // set by the plot in question, by returning the value of the element. + text: null + }); + var PlotAction = dojox.charting.action2d.PlotAction; + =====*/ + + var DEFAULT_TEXT = function(o){ + var t = o.run && o.run.data && o.run.data[o.index]; + if(t && typeof t != "number" && (t.tooltip || t.text)){ + return t.tooltip || t.text; + } + if(o.element == "candlestick"){ + return '<table cellpadding="1" cellspacing="0" border="0" style="font-size:0.9em;">' + + '<tr><td>Open:</td><td align="right"><strong>' + o.data.open + '</strong></td></tr>' + + '<tr><td>High:</td><td align="right"><strong>' + o.data.high + '</strong></td></tr>' + + '<tr><td>Low:</td><td align="right"><strong>' + o.data.low + '</strong></td></tr>' + + '<tr><td>Close:</td><td align="right"><strong>' + o.data.close + '</strong></td></tr>' + + (o.data.mid !== undefined ? '<tr><td>Mid:</td><td align="right"><strong>' + o.data.mid + '</strong></td></tr>' : '') + + '</table>'; + } + return o.element == "bar" ? o.x : o.y; + }; + + var pi4 = Math.PI / 4, pi2 = Math.PI / 2; + + return declare("dojox.charting.action2d.Tooltip", PlotAction, { + // summary: + // Create an action on a plot where a tooltip is shown when hovering over an element. + + // the data description block for the widget parser + defaultParams: { + text: DEFAULT_TEXT // the function to produce a tooltip from the object + }, + optionalParams: {}, // no optional parameters + + constructor: function(chart, plot, kwArgs){ + // summary: + // Create the tooltip action and connect it to the plot. + // chart: dojox.charting.Chart + // The chart this action belongs to. + // plot: String? + // The plot this action is attached to. If not passed, "default" is assumed. + // kwArgs: dojox.charting.action2d.__TooltipCtorArgs? + // Optional keyword arguments object for setting parameters. + this.text = kwArgs && kwArgs.text ? kwArgs.text : DEFAULT_TEXT; + + this.connect(); + }, + + process: function(o){ + // summary: + // Process the action on the given object. + // o: dojox.gfx.Shape + // The object on which to process the highlighting action. + if(o.type === "onplotreset" || o.type === "onmouseout"){ + Tooltip.hide(this.aroundRect); + this.aroundRect = null; + if(o.type === "onplotreset"){ + delete this.angles; + } + return; + } + + if(!o.shape || o.type !== "onmouseover"){ return; } + + // calculate relative coordinates and the position + var aroundRect = {type: "rect"}, position = ["after", "before"]; + switch(o.element){ + case "marker": + aroundRect.x = o.cx; + aroundRect.y = o.cy; + aroundRect.w = aroundRect.h = 1; + break; + case "circle": + aroundRect.x = o.cx - o.cr; + aroundRect.y = o.cy - o.cr; + aroundRect.w = aroundRect.h = 2 * o.cr; + break; + case "column": + position = ["above", "below"]; + // intentional fall down + case "bar": + aroundRect = lang.clone(o.shape.getShape()); + aroundRect.w = aroundRect.width; + aroundRect.h = aroundRect.height; + break; + case "candlestick": + aroundRect.x = o.x; + aroundRect.y = o.y; + aroundRect.w = o.width; + aroundRect.h = o.height; + break; + default: + //case "slice": + if(!this.angles){ + // calculate the running total of slice angles + if(typeof o.run.data[0] == "number"){ + this.angles = df.map(df.scanl(o.run.data, "+", 0), + "* 2 * Math.PI / this", df.foldl(o.run.data, "+", 0)); + }else{ + this.angles = df.map(df.scanl(o.run.data, "a + b.y", 0), + "* 2 * Math.PI / this", df.foldl(o.run.data, "a + b.y", 0)); + } + } + var startAngle = m._degToRad(o.plot.opt.startAngle), + angle = (this.angles[o.index] + this.angles[o.index + 1]) / 2 + startAngle; + aroundRect.x = o.cx + o.cr * Math.cos(angle); + aroundRect.y = o.cy + o.cr * Math.sin(angle); + aroundRect.w = aroundRect.h = 1; + // calculate the position + if(angle < pi4){ + // do nothing: the position is right + }else if(angle < pi2 + pi4){ + position = ["below", "above"]; + }else if(angle < Math.PI + pi4){ + position = ["before", "after"]; + }else if(angle < 2 * Math.PI - pi4){ + position = ["above", "below"]; + } + /* + else{ + // do nothing: the position is right + } + */ + break; + } + + // adjust relative coordinates to absolute, and remove fractions + var lt = this.chart.getCoords(); + aroundRect.x += lt.x; + aroundRect.y += lt.y; + aroundRect.x = Math.round(aroundRect.x); + aroundRect.y = Math.round(aroundRect.y); + aroundRect.w = Math.ceil(aroundRect.w); + aroundRect.h = Math.ceil(aroundRect.h); + this.aroundRect = aroundRect; + + var tooltip = this.text(o); + if(this.chart.getTextDir){ + var isChartDirectionRtl = (html.style(this.chart.node,"direction") == "rtl"); + var isBaseTextDirRtl = (this.chart.getTextDir(tooltip) == "rtl"); + } + if(tooltip){ + if(isBaseTextDirRtl && !isChartDirectionRtl){ + Tooltip.show("<span dir = 'rtl'>" + tooltip +"</span>", this.aroundRect, position); + } + else if(!isBaseTextDirRtl && isChartDirectionRtl){ + Tooltip.show("<span dir = 'ltr'>" + tooltip +"</span>", this.aroundRect, position); + }else{ + Tooltip.show(tooltip, this.aroundRect, position); + } + } + } + }); +}); + +}, +'dojox/gfx':function(){ +define(["dojo/_base/lang", "./gfx/_base", "./gfx/renderer!"], + function(lang, gfxBase, renderer){ + // module: + // dojox/gfx + // summary: + // This the root of the Dojo Graphics package + gfxBase.switchTo(renderer); + return gfxBase; +}); + +}, +'dojox/gfx/shape':function(){ +define("dojox/gfx/shape", ["./_base", "dojo/_base/lang", "dojo/_base/declare", "dojo/_base/window", "dojo/_base/sniff", + "dojo/_base/connect", "dojo/_base/array", "dojo/dom-construct", "dojo/_base/Color", "./matrix"], + function(g, lang, declare, win, has, events, arr, domConstruct, Color, matrixLib){ + +/*===== + dojox.gfx.shape = { + // summary: + // This module contains the core graphics Shape API. + // Different graphics renderer implementation modules (svg, canvas, vml, silverlight, etc.) extend this + // basic api to provide renderer-specific implementations for each shape. + }; + =====*/ + + var shape = g.shape = {}; + // a set of ids (keys=type) + var _ids = {}; + // a simple set impl to map shape<->id + var registry = {}; + + shape.register = function(/*dojox.gfx.shape.Shape*/shape){ + // summary: + // Register the specified shape into the graphics registry. + // shape: dojox.gfx.shape.Shape + // The shape to register. + // returns: + // The unique id associated with this shape. + // the id pattern : type+number (ex: Rect0,Rect1,etc) + var t = shape.declaredClass.split('.').pop(); + var i = t in _ids ? ++_ids[t] : ((_ids[t] = 0)); + var uid = t+i; + registry[uid] = shape; + return uid; + }; + + shape.byId = function(/*String*/id){ + // summary: + // Returns the shape that matches the specified id. + // id: String + // The unique identifier for this Shape. + return registry[id]; //dojox.gfx.shape.Shape + }; + + shape.dispose = function(/*dojox.gfx.shape.Shape*/shape){ + // summary: + // Removes the specified shape from the registry. + // shape: dojox.gfx.shape.Shape + // The shape to unregister. + delete registry[shape.getUID()]; + }; + + declare("dojox.gfx.shape.Shape", null, { + // summary: a Shape object, which knows how to apply + // graphical attributes and transformations + + constructor: function(){ + // rawNode: Node + // underlying graphics-renderer-specific implementation object (if applicable) + this.rawNode = null; + // shape: Object: an abstract shape object + // (see dojox.gfx.defaultPath, + // dojox.gfx.defaultPolyline, + // dojox.gfx.defaultRect, + // dojox.gfx.defaultEllipse, + // dojox.gfx.defaultCircle, + // dojox.gfx.defaultLine, + // or dojox.gfx.defaultImage) + this.shape = null; + + // matrix: dojox.gfx.Matrix2D + // a transformation matrix + this.matrix = null; + + // fillStyle: Object + // a fill object + // (see dojox.gfx.defaultLinearGradient, + // dojox.gfx.defaultRadialGradient, + // dojox.gfx.defaultPattern, + // or dojo.Color) + this.fillStyle = null; + + // strokeStyle: Object + // a stroke object + // (see dojox.gfx.defaultStroke) + this.strokeStyle = null; + + // bbox: dojox.gfx.Rectangle + // a bounding box of this shape + // (see dojox.gfx.defaultRect) + this.bbox = null; + + // virtual group structure + + // parent: Object + // a parent or null + // (see dojox.gfx.Surface, + // dojox.gfx.shape.VirtualGroup, + // or dojox.gfx.Group) + this.parent = null; + + // parentMatrix: dojox.gfx.Matrix2D + // a transformation matrix inherited from the parent + this.parentMatrix = null; + + var uid = shape.register(this); + this.getUID = function(){ + return uid; + } + }, + + // trivial getters + + getNode: function(){ + // summary: Different graphics rendering subsystems implement shapes in different ways. This + // method provides access to the underlying graphics subsystem object. Clients calling this + // method and using the return value must be careful not to try sharing or using the underlying node + // in a general way across renderer implementation. + // Returns the underlying graphics Node, or null if no underlying graphics node is used by this shape. + return this.rawNode; // Node + }, + getShape: function(){ + // summary: returns the current Shape object or null + // (see dojox.gfx.defaultPath, + // dojox.gfx.defaultPolyline, + // dojox.gfx.defaultRect, + // dojox.gfx.defaultEllipse, + // dojox.gfx.defaultCircle, + // dojox.gfx.defaultLine, + // or dojox.gfx.defaultImage) + return this.shape; // Object + }, + getTransform: function(){ + // summary: Returns the current transformation matrix applied to this Shape or null + return this.matrix; // dojox.gfx.Matrix2D + }, + getFill: function(){ + // summary: Returns the current fill object or null + // (see dojox.gfx.defaultLinearGradient, + // dojox.gfx.defaultRadialGradient, + // dojox.gfx.defaultPattern, + // or dojo.Color) + return this.fillStyle; // Object + }, + getStroke: function(){ + // summary: Returns the current stroke object or null + // (see dojox.gfx.defaultStroke) + return this.strokeStyle; // Object + }, + getParent: function(){ + // summary: Returns the parent Shape, Group or VirtualGroup or null if this Shape is unparented. + // (see dojox.gfx.Surface, + // dojox.gfx.shape.VirtualGroup, + // or dojox.gfx.Group) + return this.parent; // Object + }, + getBoundingBox: function(){ + // summary: Returns the bounding box Rectanagle for this shape or null if a BoundingBox cannot be + // calculated for the shape on the current renderer or for shapes with no geometric area (points). + // A bounding box is a rectangular geometric region + // defining the X and Y extent of the shape. + // (see dojox.gfx.defaultRect) + return this.bbox; // dojox.gfx.Rectangle + }, + getTransformedBoundingBox: function(){ + // summary: returns an array of four points or null + // four points represent four corners of the untransformed bounding box + var b = this.getBoundingBox(); + if(!b){ + return null; // null + } + var m = this._getRealMatrix(), + gm = matrixLib; + return [ // Array + gm.multiplyPoint(m, b.x, b.y), + gm.multiplyPoint(m, b.x + b.width, b.y), + gm.multiplyPoint(m, b.x + b.width, b.y + b.height), + gm.multiplyPoint(m, b.x, b.y + b.height) + ]; + }, + getEventSource: function(){ + // summary: returns a Node, which is used as + // a source of events for this shape + // COULD BE RE-IMPLEMENTED BY THE RENDERER! + return this.rawNode; // Node + }, + + // empty settings + + setShape: function(shape){ + // summary: sets a shape object + // (the default implementation simply ignores it) + // shape: Object + // a shape object + // (see dojox.gfx.defaultPath, + // dojox.gfx.defaultPolyline, + // dojox.gfx.defaultRect, + // dojox.gfx.defaultEllipse, + // dojox.gfx.defaultCircle, + // dojox.gfx.defaultLine, + // or dojox.gfx.defaultImage) + // COULD BE RE-IMPLEMENTED BY THE RENDERER! + this.shape = g.makeParameters(this.shape, shape); + this.bbox = null; + return this; // self + }, + setFill: function(fill){ + // summary: sets a fill object + // (the default implementation simply ignores it) + // fill: Object + // a fill object + // (see dojox.gfx.defaultLinearGradient, + // dojox.gfx.defaultRadialGradient, + // dojox.gfx.defaultPattern, + // or dojo.Color) + // COULD BE RE-IMPLEMENTED BY THE RENDERER! + if(!fill){ + // don't fill + this.fillStyle = null; + return this; // self + } + var f = null; + if(typeof(fill) == "object" && "type" in fill){ + // gradient or pattern + switch(fill.type){ + case "linear": + f = g.makeParameters(g.defaultLinearGradient, fill); + break; + case "radial": + f = g.makeParameters(g.defaultRadialGradient, fill); + break; + case "pattern": + f = g.makeParameters(g.defaultPattern, fill); + break; + } + }else{ + // color object + f = g.normalizeColor(fill); + } + this.fillStyle = f; + return this; // self + }, + setStroke: function(stroke){ + // summary: sets a stroke object + // (the default implementation simply ignores it) + // stroke: Object + // a stroke object + // (see dojox.gfx.defaultStroke) + // COULD BE RE-IMPLEMENTED BY THE RENDERER! + if(!stroke){ + // don't stroke + this.strokeStyle = null; + return this; // self + } + // normalize the stroke + if(typeof stroke == "string" || lang.isArray(stroke) || stroke instanceof Color){ + stroke = {color: stroke}; + } + var s = this.strokeStyle = g.makeParameters(g.defaultStroke, stroke); + s.color = g.normalizeColor(s.color); + return this; // self + }, + setTransform: function(matrix){ + // summary: sets a transformation matrix + // matrix: dojox.gfx.Matrix2D + // a matrix or a matrix-like object + // (see an argument of dojox.gfx.Matrix2D + // constructor for a list of acceptable arguments) + // COULD BE RE-IMPLEMENTED BY THE RENDERER! + this.matrix = matrixLib.clone(matrix ? matrixLib.normalize(matrix) : matrixLib.identity); + return this._applyTransform(); // self + }, + + _applyTransform: function(){ + // summary: physically sets a matrix + // COULD BE RE-IMPLEMENTED BY THE RENDERER! + return this; // self + }, + + // z-index + + moveToFront: function(){ + // summary: moves a shape to front of its parent's list of shapes + var p = this.getParent(); + if(p){ + p._moveChildToFront(this); + this._moveToFront(); // execute renderer-specific action + } + return this; // self + }, + moveToBack: function(){ + // summary: moves a shape to back of its parent's list of shapes + var p = this.getParent(); + if(p){ + p._moveChildToBack(this); + this._moveToBack(); // execute renderer-specific action + } + return this; + }, + _moveToFront: function(){ + // summary: renderer-specific hook, see dojox.gfx.shape.Shape.moveToFront() + // COULD BE RE-IMPLEMENTED BY THE RENDERER! + }, + _moveToBack: function(){ + // summary: renderer-specific hook, see dojox.gfx.shape.Shape.moveToFront() + // COULD BE RE-IMPLEMENTED BY THE RENDERER! + }, + + // apply left & right transformation + + applyRightTransform: function(matrix){ + // summary: multiplies the existing matrix with an argument on right side + // (this.matrix * matrix) + // matrix: dojox.gfx.Matrix2D + // a matrix or a matrix-like object + // (see an argument of dojox.gfx.Matrix2D + // constructor for a list of acceptable arguments) + return matrix ? this.setTransform([this.matrix, matrix]) : this; // self + }, + applyLeftTransform: function(matrix){ + // summary: multiplies the existing matrix with an argument on left side + // (matrix * this.matrix) + // matrix: dojox.gfx.Matrix2D + // a matrix or a matrix-like object + // (see an argument of dojox.gfx.Matrix2D + // constructor for a list of acceptable arguments) + return matrix ? this.setTransform([matrix, this.matrix]) : this; // self + }, + applyTransform: function(matrix){ + // summary: a shortcut for dojox.gfx.Shape.applyRightTransform + // matrix: dojox.gfx.Matrix2D + // a matrix or a matrix-like object + // (see an argument of dojox.gfx.Matrix2D + // constructor for a list of acceptable arguments) + return matrix ? this.setTransform([this.matrix, matrix]) : this; // self + }, + + // virtual group methods + + removeShape: function(silently){ + // summary: removes the shape from its parent's list of shapes + // silently: Boolean + // if true, do not redraw a picture yet + if(this.parent){ + this.parent.remove(this, silently); + } + return this; // self + }, + _setParent: function(parent, matrix){ + // summary: sets a parent + // parent: Object + // a parent or null + // (see dojox.gfx.Surface, + // dojox.gfx.shape.VirtualGroup, + // or dojox.gfx.Group) + // matrix: dojox.gfx.Matrix2D + // a 2D matrix or a matrix-like object + this.parent = parent; + return this._updateParentMatrix(matrix); // self + }, + _updateParentMatrix: function(matrix){ + // summary: updates the parent matrix with new matrix + // matrix: dojox.gfx.Matrix2D + // a 2D matrix or a matrix-like object + this.parentMatrix = matrix ? matrixLib.clone(matrix) : null; + return this._applyTransform(); // self + }, + _getRealMatrix: function(){ + // summary: returns the cumulative ('real') transformation matrix + // by combining the shape's matrix with its parent's matrix + var m = this.matrix; + var p = this.parent; + while(p){ + if(p.matrix){ + m = matrixLib.multiply(p.matrix, m); + } + p = p.parent; + } + return m; // dojox.gfx.Matrix2D + } + }); + + shape._eventsProcessing = { + connect: function(name, object, method){ + // summary: connects a handler to an event on this shape + // COULD BE RE-IMPLEMENTED BY THE RENDERER! + // redirect to fixCallback to normalize events and add the gfxTarget to the event. The latter + // is done by dojox.gfx.fixTarget which is defined by each renderer + return events.connect(this.getEventSource(), name, shape.fixCallback(this, g.fixTarget, object, method)); + + }, + disconnect: function(token){ + // summary: connects a handler by token from an event on this shape + // COULD BE RE-IMPLEMENTED BY THE RENDERER! + + events.disconnect(token); + } + }; + + shape.fixCallback = function(gfxElement, fixFunction, scope, method){ + // summary: + // Wraps the callback to allow for tests and event normalization + // before it gets invoked. This is where 'fixTarget' is invoked. + // gfxElement: Object + // The GFX object that triggers the action (ex.: + // dojox.gfx.Surface and dojox.gfx.Shape). A new event property + // 'gfxTarget' is added to the event to reference this object. + // for easy manipulation of GFX objects by the event handlers. + // fixFunction: Function + // The function that implements the logic to set the 'gfxTarget' + // property to the event. It should be 'dojox.gfx.fixTarget' for + // most of the cases + // scope: Object + // Optional. The scope to be used when invoking 'method'. If + // omitted, a global scope is used. + // method: Function|String + // The original callback to be invoked. + if(!method){ + method = scope; + scope = null; + } + if(lang.isString(method)){ + scope = scope || win.global; + if(!scope[method]){ throw(['dojox.gfx.shape.fixCallback: scope["', method, '"] is null (scope="', scope, '")'].join('')); } + return function(e){ + return fixFunction(e,gfxElement) ? scope[method].apply(scope, arguments || []) : undefined; }; // Function + } + return !scope + ? function(e){ + return fixFunction(e,gfxElement) ? method.apply(scope, arguments) : undefined; } + : function(e){ + return fixFunction(e,gfxElement) ? method.apply(scope, arguments || []) : undefined; }; // Function + }; + lang.extend(shape.Shape, shape._eventsProcessing); + + shape.Container = { + // summary: a container of shapes, which can be used + // as a foundation for renderer-specific groups, or as a way + // to logically group shapes (e.g, to propagate matricies) + + _init: function() { + // children: Array: a list of children + this.children = []; + }, + + // group management + + openBatch: function() { + // summary: starts a new batch, subsequent new child shapes will be held in + // the batch instead of appending to the container directly + }, + closeBatch: function() { + // summary: submits the current batch, append all pending child shapes to DOM + }, + add: function(shape){ + // summary: adds a shape to the list + // shape: dojox.gfx.Shape + // the shape to add to the list + var oldParent = shape.getParent(); + if(oldParent){ + oldParent.remove(shape, true); + } + this.children.push(shape); + return shape._setParent(this, this._getRealMatrix()); // self + }, + remove: function(shape, silently){ + // summary: removes a shape from the list + // shape: dojox.gfx.shape.Shape + // the shape to remove + // silently: Boolean + // if true, do not redraw a picture yet + for(var i = 0; i < this.children.length; ++i){ + if(this.children[i] == shape){ + if(silently){ + // skip for now + }else{ + shape.parent = null; + shape.parentMatrix = null; + } + this.children.splice(i, 1); + break; + } + } + return this; // self + }, + clear: function(){ + // summary: removes all shapes from a group/surface + var shape; + for(var i = 0; i < this.children.length;++i){ + shape = this.children[i]; + shape.parent = null; + shape.parentMatrix = null; + } + this.children = []; + return this; // self + }, + + // moving child nodes + + _moveChildToFront: function(shape){ + // summary: moves a shape to front of the list of shapes + // shape: dojox.gfx.shape.Shape + // one of the child shapes to move to the front + for(var i = 0; i < this.children.length; ++i){ + if(this.children[i] == shape){ + this.children.splice(i, 1); + this.children.push(shape); + break; + } + } + return this; // self + }, + _moveChildToBack: function(shape){ + // summary: moves a shape to back of the list of shapes + // shape: dojox.gfx.shape.Shape + // one of the child shapes to move to the front + for(var i = 0; i < this.children.length; ++i){ + if(this.children[i] == shape){ + this.children.splice(i, 1); + this.children.unshift(shape); + break; + } + } + return this; // self + } + }; + + declare("dojox.gfx.shape.Surface", null, { + // summary: a surface object to be used for drawings + constructor: function(){ + // underlying node + this.rawNode = null; + // the parent node + this._parent = null; + // the list of DOM nodes to be deleted in the case of destruction + this._nodes = []; + // the list of events to be detached in the case of destruction + this._events = []; + }, + destroy: function(){ + // summary: destroy all relevant external resources and release all + // external references to make this object garbage-collectible + arr.forEach(this._nodes, domConstruct.destroy); + this._nodes = []; + arr.forEach(this._events, events.disconnect); + this._events = []; + this.rawNode = null; // recycle it in _nodes, if it needs to be recycled + if(has("ie")){ + while(this._parent.lastChild){ + domConstruct.destroy(this._parent.lastChild); + } + }else{ + this._parent.innerHTML = ""; + } + this._parent = null; + }, + getEventSource: function(){ + // summary: returns a node, which can be used to attach event listeners + return this.rawNode; // Node + }, + _getRealMatrix: function(){ + // summary: always returns the identity matrix + return null; // dojox.gfx.Matrix2D + }, + isLoaded: true, + onLoad: function(/*dojox.gfx.Surface*/ surface){ + // summary: local event, fired once when the surface is created + // asynchronously, used only when isLoaded is false, required + // only for Silverlight. + }, + whenLoaded: function(/*Object|Null*/ context, /*Function|String*/ method){ + var f = lang.hitch(context, method); + if(this.isLoaded){ + f(this); + }else{ + var h = events.connect(this, "onLoad", function(surface){ + events.disconnect(h); + f(surface); + }); + } + } + }); + + lang.extend(shape.Surface, shape._eventsProcessing); + + declare("dojox.gfx.Point", null, { + // summary: a hypothetical 2D point to be used for drawings - {x, y} + // description: This object is defined for documentation purposes. + // You should use the naked object instead: {x: 1, y: 2}. + }); + + declare("dojox.gfx.Rectangle", null, { + // summary: a hypothetical rectangle - {x, y, width, height} + // description: This object is defined for documentation purposes. + // You should use the naked object instead: {x: 1, y: 2, width: 100, height: 200}. + }); + + declare("dojox.gfx.shape.Rect", shape.Shape, { + // summary: a generic rectangle + constructor: function(rawNode){ + // rawNode: Node + // The underlying graphics system object (typically a DOM Node) + this.shape = g.getDefault("Rect"); + this.rawNode = rawNode; + }, + getBoundingBox: function(){ + // summary: returns the bounding box (its shape in this case) + return this.shape; // dojox.gfx.Rectangle + } + }); + + declare("dojox.gfx.shape.Ellipse", shape.Shape, { + // summary: a generic ellipse + constructor: function(rawNode){ + // rawNode: Node + // a DOM Node + this.shape = g.getDefault("Ellipse"); + this.rawNode = rawNode; + }, + getBoundingBox: function(){ + // summary: returns the bounding box + if(!this.bbox){ + var shape = this.shape; + this.bbox = {x: shape.cx - shape.rx, y: shape.cy - shape.ry, + width: 2 * shape.rx, height: 2 * shape.ry}; + } + return this.bbox; // dojox.gfx.Rectangle + } + }); + + declare("dojox.gfx.shape.Circle", shape.Shape, { + // summary: a generic circle + // (this is a helper object, which is defined for convenience) + constructor: function(rawNode){ + // rawNode: Node + // a DOM Node + this.shape = g.getDefault("Circle"); + this.rawNode = rawNode; + }, + getBoundingBox: function(){ + // summary: returns the bounding box + if(!this.bbox){ + var shape = this.shape; + this.bbox = {x: shape.cx - shape.r, y: shape.cy - shape.r, + width: 2 * shape.r, height: 2 * shape.r}; + } + return this.bbox; // dojox.gfx.Rectangle + } + }); + + declare("dojox.gfx.shape.Line", shape.Shape, { + // summary: a generic line + // (this is a helper object, which is defined for convenience) + constructor: function(rawNode){ + // rawNode: Node + // a DOM Node + this.shape = g.getDefault("Line"); + this.rawNode = rawNode; + }, + getBoundingBox: function(){ + // summary: returns the bounding box + if(!this.bbox){ + var shape = this.shape; + this.bbox = { + x: Math.min(shape.x1, shape.x2), + y: Math.min(shape.y1, shape.y2), + width: Math.abs(shape.x2 - shape.x1), + height: Math.abs(shape.y2 - shape.y1) + }; + } + return this.bbox; // dojox.gfx.Rectangle + } + }); + + declare("dojox.gfx.shape.Polyline", shape.Shape, { + // summary: a generic polyline/polygon + // (this is a helper object, which is defined for convenience) + constructor: function(rawNode){ + // rawNode: Node + // a DOM Node + this.shape = g.getDefault("Polyline"); + this.rawNode = rawNode; + }, + setShape: function(points, closed){ + // summary: sets a polyline/polygon shape object + // points: Object + // a polyline/polygon shape object + // closed: Boolean + // close the polyline to make a polygon + if(points && points instanceof Array){ + // points: Array: an array of points + this.inherited(arguments, [{points: points}]); + if(closed && this.shape.points.length){ + this.shape.points.push(this.shape.points[0]); + } + }else{ + this.inherited(arguments, [points]); + } + return this; // self + }, + _normalizePoints: function(){ + // summary: normalize points to array of {x:number, y:number} + var p = this.shape.points, l = p && p.length; + if(l && typeof p[0] == "number"){ + var points = []; + for(var i = 0; i < l; i += 2){ + points.push({x: p[i], y: p[i + 1]}); + } + this.shape.points = points; + } + }, + getBoundingBox: function(){ + // summary: returns the bounding box + if(!this.bbox && this.shape.points.length){ + var p = this.shape.points; + var l = p.length; + var t = p[0]; + var bbox = {l: t.x, t: t.y, r: t.x, b: t.y}; + for(var i = 1; i < l; ++i){ + t = p[i]; + if(bbox.l > t.x) bbox.l = t.x; + if(bbox.r < t.x) bbox.r = t.x; + if(bbox.t > t.y) bbox.t = t.y; + if(bbox.b < t.y) bbox.b = t.y; + } + this.bbox = { + x: bbox.l, + y: bbox.t, + width: bbox.r - bbox.l, + height: bbox.b - bbox.t + }; + } + return this.bbox; // dojox.gfx.Rectangle + } + }); + + declare("dojox.gfx.shape.Image", shape.Shape, { + // summary: a generic image + // (this is a helper object, which is defined for convenience) + constructor: function(rawNode){ + // rawNode: Node + // a DOM Node + this.shape = g.getDefault("Image"); + this.rawNode = rawNode; + }, + getBoundingBox: function(){ + // summary: returns the bounding box (its shape in this case) + return this.shape; // dojox.gfx.Rectangle + }, + setStroke: function(){ + // summary: ignore setting a stroke style + return this; // self + }, + setFill: function(){ + // summary: ignore setting a fill style + return this; // self + } + }); + + declare("dojox.gfx.shape.Text", shape.Shape, { + // summary: a generic text + constructor: function(rawNode){ + // rawNode: Node + // a DOM Node + this.fontStyle = null; + this.shape = g.getDefault("Text"); + this.rawNode = rawNode; + }, + getFont: function(){ + // summary: returns the current font object or null + return this.fontStyle; // Object + }, + setFont: function(newFont){ + // summary: sets a font for text + // newFont: Object + // a font object (see dojox.gfx.defaultFont) or a font string + this.fontStyle = typeof newFont == "string" ? g.splitFontString(newFont) : + g.makeParameters(g.defaultFont, newFont); + this._setFont(); + return this; // self + } + }); + + shape.Creator = { + // summary: shape creators + createShape: function(shape){ + // summary: creates a shape object based on its type; it is meant to be used + // by group-like objects + // shape: Object + // a shape descriptor object + switch(shape.type){ + case g.defaultPath.type: return this.createPath(shape); + case g.defaultRect.type: return this.createRect(shape); + case g.defaultCircle.type: return this.createCircle(shape); + case g.defaultEllipse.type: return this.createEllipse(shape); + case g.defaultLine.type: return this.createLine(shape); + case g.defaultPolyline.type: return this.createPolyline(shape); + case g.defaultImage.type: return this.createImage(shape); + case g.defaultText.type: return this.createText(shape); + case g.defaultTextPath.type: return this.createTextPath(shape); + } + return null; + }, + createGroup: function(){ + // summary: creates a group shape + return this.createObject(g.Group); // dojox.gfx.Group + }, + createRect: function(rect){ + // summary: creates a rectangle shape + // rect: Object + // a path object (see dojox.gfx.defaultRect) + return this.createObject(g.Rect, rect); // dojox.gfx.Rect + }, + createEllipse: function(ellipse){ + // summary: creates an ellipse shape + // ellipse: Object + // an ellipse object (see dojox.gfx.defaultEllipse) + return this.createObject(g.Ellipse, ellipse); // dojox.gfx.Ellipse + }, + createCircle: function(circle){ + // summary: creates a circle shape + // circle: Object + // a circle object (see dojox.gfx.defaultCircle) + return this.createObject(g.Circle, circle); // dojox.gfx.Circle + }, + createLine: function(line){ + // summary: creates a line shape + // line: Object + // a line object (see dojox.gfx.defaultLine) + return this.createObject(g.Line, line); // dojox.gfx.Line + }, + createPolyline: function(points){ + // summary: creates a polyline/polygon shape + // points: Object + // a points object (see dojox.gfx.defaultPolyline) + // or an Array of points + return this.createObject(g.Polyline, points); // dojox.gfx.Polyline + }, + createImage: function(image){ + // summary: creates a image shape + // image: Object + // an image object (see dojox.gfx.defaultImage) + return this.createObject(g.Image, image); // dojox.gfx.Image + }, + createText: function(text){ + // summary: creates a text shape + // text: Object + // a text object (see dojox.gfx.defaultText) + return this.createObject(g.Text, text); // dojox.gfx.Text + }, + createPath: function(path){ + // summary: creates a path shape + // path: Object + // a path object (see dojox.gfx.defaultPath) + return this.createObject(g.Path, path); // dojox.gfx.Path + }, + createTextPath: function(text){ + // summary: creates a text shape + // text: Object + // a textpath object (see dojox.gfx.defaultTextPath) + return this.createObject(g.TextPath, {}).setText(text); // dojox.gfx.TextPath + }, + createObject: function(shapeType, rawShape){ + // summary: creates an instance of the passed shapeType class + // SHOULD BE RE-IMPLEMENTED BY THE RENDERER! + // shapeType: Function + // a class constructor to create an instance of + // rawShape: Object + // properties to be passed in to the classes 'setShape' method + + return null; // dojox.gfx.Shape + } + }; + + return shape; +}); + + +}, +'dojox/charting/Chart2D':function(){ +define("dojox/charting/Chart2D", ["dojo/_base/kernel", "dojox", "./Chart", + "./axis2d/Default", "./axis2d/Invisible", "./plot2d/Default", "./plot2d/Lines", "./plot2d/Areas", + "./plot2d/Markers", "./plot2d/MarkersOnly", "./plot2d/Scatter", "./plot2d/Stacked", "./plot2d/StackedLines", + "./plot2d/StackedAreas", "./plot2d/Columns", "./plot2d/StackedColumns", "./plot2d/ClusteredColumns", + "./plot2d/Bars", "./plot2d/StackedBars", "./plot2d/ClusteredBars", "./plot2d/Grid", "./plot2d/Pie", + "./plot2d/Bubble", "./plot2d/Candlesticks", "./plot2d/OHLC", "./plot2d/Spider"], + function(dojo, dojox, Chart){ + dojo.deprecated("dojox.charting.Chart2D", "Use dojo.charting.Chart instead and require all other components explicitly", "2.0"); + // module: + // dojox/charting/Chart2D + // summary: + // This is a compatibility module which loads all charting modules that used to be automatically + // loaded in versions prior to 1.6. It is highly recommended for performance reasons that + // this module no longer be referenced by applications. Instead, use dojox/charting/Chart. + return dojox.charting.Chart2D = Chart; +}); + +}, +'dojox/charting/scaler/linear':function(){ +define("dojox/charting/scaler/linear", ["dojo/_base/lang", "./common"], + function(lang, common){ + var linear = lang.getObject("dojox.charting.scaler.linear", true); + + var deltaLimit = 3, // pixels + findString = common.findString, + getLabel = common.getNumericLabel; + + var calcTicks = function(min, max, kwArgs, majorTick, minorTick, microTick, span){ + kwArgs = lang.delegate(kwArgs); + if(!majorTick){ + if(kwArgs.fixUpper == "major"){ kwArgs.fixUpper = "minor"; } + if(kwArgs.fixLower == "major"){ kwArgs.fixLower = "minor"; } + } + if(!minorTick){ + if(kwArgs.fixUpper == "minor"){ kwArgs.fixUpper = "micro"; } + if(kwArgs.fixLower == "minor"){ kwArgs.fixLower = "micro"; } + } + if(!microTick){ + if(kwArgs.fixUpper == "micro"){ kwArgs.fixUpper = "none"; } + if(kwArgs.fixLower == "micro"){ kwArgs.fixLower = "none"; } + } + var lowerBound = findString(kwArgs.fixLower, ["major"]) ? + Math.floor(kwArgs.min / majorTick) * majorTick : + findString(kwArgs.fixLower, ["minor"]) ? + Math.floor(kwArgs.min / minorTick) * minorTick : + findString(kwArgs.fixLower, ["micro"]) ? + Math.floor(kwArgs.min / microTick) * microTick : kwArgs.min, + upperBound = findString(kwArgs.fixUpper, ["major"]) ? + Math.ceil(kwArgs.max / majorTick) * majorTick : + findString(kwArgs.fixUpper, ["minor"]) ? + Math.ceil(kwArgs.max / minorTick) * minorTick : + findString(kwArgs.fixUpper, ["micro"]) ? + Math.ceil(kwArgs.max / microTick) * microTick : kwArgs.max; + + if(kwArgs.useMin){ min = lowerBound; } + if(kwArgs.useMax){ max = upperBound; } + + var majorStart = (!majorTick || kwArgs.useMin && findString(kwArgs.fixLower, ["major"])) ? + min : Math.ceil(min / majorTick) * majorTick, + minorStart = (!minorTick || kwArgs.useMin && findString(kwArgs.fixLower, ["major", "minor"])) ? + min : Math.ceil(min / minorTick) * minorTick, + microStart = (! microTick || kwArgs.useMin && findString(kwArgs.fixLower, ["major", "minor", "micro"])) ? + min : Math.ceil(min / microTick) * microTick, + majorCount = !majorTick ? 0 : (kwArgs.useMax && findString(kwArgs.fixUpper, ["major"]) ? + Math.round((max - majorStart) / majorTick) : + Math.floor((max - majorStart) / majorTick)) + 1, + minorCount = !minorTick ? 0 : (kwArgs.useMax && findString(kwArgs.fixUpper, ["major", "minor"]) ? + Math.round((max - minorStart) / minorTick) : + Math.floor((max - minorStart) / minorTick)) + 1, + microCount = !microTick ? 0 : (kwArgs.useMax && findString(kwArgs.fixUpper, ["major", "minor", "micro"]) ? + Math.round((max - microStart) / microTick) : + Math.floor((max - microStart) / microTick)) + 1, + minorPerMajor = minorTick ? Math.round(majorTick / minorTick) : 0, + microPerMinor = microTick ? Math.round(minorTick / microTick) : 0, + majorPrecision = majorTick ? Math.floor(Math.log(majorTick) / Math.LN10) : 0, + minorPrecision = minorTick ? Math.floor(Math.log(minorTick) / Math.LN10) : 0, + scale = span / (max - min); + if(!isFinite(scale)){ scale = 1; } + + return { + bounds: { + lower: lowerBound, + upper: upperBound, + from: min, + to: max, + scale: scale, + span: span + }, + major: { + tick: majorTick, + start: majorStart, + count: majorCount, + prec: majorPrecision + }, + minor: { + tick: minorTick, + start: minorStart, + count: minorCount, + prec: minorPrecision + }, + micro: { + tick: microTick, + start: microStart, + count: microCount, + prec: 0 + }, + minorPerMajor: minorPerMajor, + microPerMinor: microPerMinor, + scaler: linear + }; + }; + + return lang.mixin(linear, { + buildScaler: function(/*Number*/ min, /*Number*/ max, /*Number*/ span, /*Object*/ kwArgs){ + var h = {fixUpper: "none", fixLower: "none", natural: false}; + if(kwArgs){ + if("fixUpper" in kwArgs){ h.fixUpper = String(kwArgs.fixUpper); } + if("fixLower" in kwArgs){ h.fixLower = String(kwArgs.fixLower); } + if("natural" in kwArgs){ h.natural = Boolean(kwArgs.natural); } + } + + // update bounds + if("min" in kwArgs){ min = kwArgs.min; } + if("max" in kwArgs){ max = kwArgs.max; } + if(kwArgs.includeZero){ + if(min > 0){ min = 0; } + if(max < 0){ max = 0; } + } + h.min = min; + h.useMin = true; + h.max = max; + h.useMax = true; + + if("from" in kwArgs){ + min = kwArgs.from; + h.useMin = false; + } + if("to" in kwArgs){ + max = kwArgs.to; + h.useMax = false; + } + + // check for erroneous condition + if(max <= min){ + return calcTicks(min, max, h, 0, 0, 0, span); // Object + } + + var mag = Math.floor(Math.log(max - min) / Math.LN10), + major = kwArgs && ("majorTickStep" in kwArgs) ? kwArgs.majorTickStep : Math.pow(10, mag), + minor = 0, micro = 0, ticks; + + // calculate minor ticks + if(kwArgs && ("minorTickStep" in kwArgs)){ + minor = kwArgs.minorTickStep; + }else{ + do{ + minor = major / 10; + if(!h.natural || minor > 0.9){ + ticks = calcTicks(min, max, h, major, minor, 0, span); + if(ticks.bounds.scale * ticks.minor.tick > deltaLimit){ break; } + } + minor = major / 5; + if(!h.natural || minor > 0.9){ + ticks = calcTicks(min, max, h, major, minor, 0, span); + if(ticks.bounds.scale * ticks.minor.tick > deltaLimit){ break; } + } + minor = major / 2; + if(!h.natural || minor > 0.9){ + ticks = calcTicks(min, max, h, major, minor, 0, span); + if(ticks.bounds.scale * ticks.minor.tick > deltaLimit){ break; } + } + return calcTicks(min, max, h, major, 0, 0, span); // Object + }while(false); + } + + // calculate micro ticks + if(kwArgs && ("microTickStep" in kwArgs)){ + micro = kwArgs.microTickStep; + ticks = calcTicks(min, max, h, major, minor, micro, span); + }else{ + do{ + micro = minor / 10; + if(!h.natural || micro > 0.9){ + ticks = calcTicks(min, max, h, major, minor, micro, span); + if(ticks.bounds.scale * ticks.micro.tick > deltaLimit){ break; } + } + micro = minor / 5; + if(!h.natural || micro > 0.9){ + ticks = calcTicks(min, max, h, major, minor, micro, span); + if(ticks.bounds.scale * ticks.micro.tick > deltaLimit){ break; } + } + micro = minor / 2; + if(!h.natural || micro > 0.9){ + ticks = calcTicks(min, max, h, major, minor, micro, span); + if(ticks.bounds.scale * ticks.micro.tick > deltaLimit){ break; } + } + micro = 0; + }while(false); + } + + return micro ? ticks : calcTicks(min, max, h, major, minor, 0, span); // Object + }, + buildTicks: function(/*Object*/ scaler, /*Object*/ kwArgs){ + var step, next, tick, + nextMajor = scaler.major.start, + nextMinor = scaler.minor.start, + nextMicro = scaler.micro.start; + if(kwArgs.microTicks && scaler.micro.tick){ + step = scaler.micro.tick, next = nextMicro; + }else if(kwArgs.minorTicks && scaler.minor.tick){ + step = scaler.minor.tick, next = nextMinor; + }else if(scaler.major.tick){ + step = scaler.major.tick, next = nextMajor; + }else{ + // no ticks + return null; + } + // make sure that we have finite bounds + var revScale = 1 / scaler.bounds.scale; + if(scaler.bounds.to <= scaler.bounds.from || isNaN(revScale) || !isFinite(revScale) || + step <= 0 || isNaN(step) || !isFinite(step)){ + // no ticks + return null; + } + // loop over all ticks + var majorTicks = [], minorTicks = [], microTicks = []; + while(next <= scaler.bounds.to + revScale){ + if(Math.abs(nextMajor - next) < step / 2){ + // major tick + tick = {value: nextMajor}; + if(kwArgs.majorLabels){ + tick.label = getLabel(nextMajor, scaler.major.prec, kwArgs); + } + majorTicks.push(tick); + nextMajor += scaler.major.tick; + nextMinor += scaler.minor.tick; + nextMicro += scaler.micro.tick; + }else if(Math.abs(nextMinor - next) < step / 2){ + // minor tick + if(kwArgs.minorTicks){ + tick = {value: nextMinor}; + if(kwArgs.minorLabels && (scaler.minMinorStep <= scaler.minor.tick * scaler.bounds.scale)){ + tick.label = getLabel(nextMinor, scaler.minor.prec, kwArgs); + } + minorTicks.push(tick); + } + nextMinor += scaler.minor.tick; + nextMicro += scaler.micro.tick; + }else{ + // micro tick + if(kwArgs.microTicks){ + microTicks.push({value: nextMicro}); + } + nextMicro += scaler.micro.tick; + } + next += step; + } + return {major: majorTicks, minor: minorTicks, micro: microTicks}; // Object + }, + getTransformerFromModel: function(/*Object*/ scaler){ + var offset = scaler.bounds.from, scale = scaler.bounds.scale; + return function(x){ return (x - offset) * scale; }; // Function + }, + getTransformerFromPlot: function(/*Object*/ scaler){ + var offset = scaler.bounds.from, scale = scaler.bounds.scale; + return function(x){ return x / scale + offset; }; // Function + } + }); +}); + +}, +'dojox/gfx/renderer':function(){ +define("dojox/gfx/renderer", ["./_base","dojo/_base/lang", "dojo/_base/sniff", "dojo/_base/window", "dojo/_base/config"], + function(g, lang, has, win, config){ + //>> noBuildResolver +/*===== + dojox.gfx.renderer = { + // summary: + // This module is an AMD loader plugin that loads the appropriate graphics renderer + // implementation based on detected environment and current configuration settings. + }; + =====*/ + var currentRenderer = null; + return { + load: function(id, require, load){ + if(currentRenderer && id != "force"){ + load(currentRenderer); + return; + } + var renderer = config.forceGfxRenderer, + renderers = !renderer && (lang.isString(config.gfxRenderer) ? + config.gfxRenderer : "svg,vml,canvas,silverlight").split(","), + silverlightObject, silverlightFlag; + + while(!renderer && renderers.length){ + switch(renderers.shift()){ + case "svg": + // the next test is from https://github.com/phiggins42/has.js + if("SVGAngle" in win.global){ + renderer = "svg"; + } + break; + case "vml": + if(has("ie")){ + renderer = "vml"; + } + break; + case "silverlight": + try{ + if(has("ie")){ + silverlightObject = new ActiveXObject("AgControl.AgControl"); + if(silverlightObject && silverlightObject.IsVersionSupported("1.0")){ + silverlightFlag = true; + } + }else{ + if(navigator.plugins["Silverlight Plug-In"]){ + silverlightFlag = true; + } + } + }catch(e){ + silverlightFlag = false; + }finally{ + silverlightObject = null; + } + if(silverlightFlag){ + renderer = "silverlight"; + } + break; + case "canvas": + if(win.global.CanvasRenderingContext2D){ + renderer = "canvas"; + } + break; + } + } + + if (renderer === 'canvas' && config.canvasEvents !== false) { + renderer = "canvasWithEvents"; + } + + if(config.isDebug){ + console.log("gfx renderer = " + renderer); + } + + function loadRenderer(){ + require(["dojox/gfx/" + renderer], function(module){ + g.renderer = renderer; + // memorize the renderer module + currentRenderer = module; + // now load it + load(module); + }); + } + if(renderer == "svg" && typeof window.svgweb != "undefined"){ + window.svgweb.addOnLoad(loadRenderer); + }else{ + loadRenderer(); + } + } + }; +}); + +}, +'dojox/charting/widget/Chart':function(){ +define("dojox/charting/widget/Chart", ["dojo/_base/kernel", "dojo/_base/lang", "dojo/_base/array","dojo/_base/html","dojo/_base/declare", "dojo/query", + "dijit/_Widget", "../Chart", "dojox/lang/utils", "dojox/lang/functional","dojox/lang/functional/lambda", + "dijit/_base/manager"], + function(kernel, lang, arr, html, declare, query, Widget, Chart, du, df, dfl){ +/*===== +var Widget = dijit._Widget; +=====*/ + var collectParams, collectAxisParams, collectPlotParams, + collectActionParams, collectDataParams, + notNull = function(o){ return o; }, + dc = lang.getObject("dojox.charting"); + + var ChartWidget = declare("dojox.charting.widget.Chart", Widget, { + // parameters for the markup + + // theme for the chart + theme: null, + + // margins for the chart: {l: 10, r: 10, t: 10, b: 10} + margins: null, + + // chart area, define them as undefined to: + // allow the parser to take them into account + // but make sure they have no defined value to not override theme + stroke: undefined, + fill: undefined, + + // methods + + buildRendering: function(){ + this.inherited(arguments); + + n = this.domNode; + + // collect chart parameters + var axes = query("> .axis", n).map(collectAxisParams).filter(notNull), + plots = query("> .plot", n).map(collectPlotParams).filter(notNull), + actions = query("> .action", n).map(collectActionParams).filter(notNull), + series = query("> .series", n).map(collectDataParams).filter(notNull); + + // build the chart + n.innerHTML = ""; + var c = this.chart = new Chart(n, { + margins: this.margins, + stroke: this.stroke, + fill: this.fill, + textDir: this.textDir + }); + + // add collected parameters + if(this.theme){ + c.setTheme(this.theme); + } + axes.forEach(function(axis){ + c.addAxis(axis.name, axis.kwArgs); + }); + plots.forEach(function(plot){ + c.addPlot(plot.name, plot.kwArgs); + }); + + this.actions = actions.map(function(action){ + return new action.action(c, action.plot, action.kwArgs); + }); + + var render = df.foldl(series, function(render, series){ + if(series.type == "data"){ + c.addSeries(series.name, series.data, series.kwArgs); + render = true; + }else{ + c.addSeries(series.name, [0], series.kwArgs); + var kw = {}; + du.updateWithPattern( + kw, + series.kwArgs, + { + "query": "", + "queryOptions": null, + "start": 0, + "count": 1 //, + // "sort": [] + }, + true + ); + if(series.kwArgs.sort){ + // sort is a complex object type and doesn't survive coercian + kw.sort = lang.clone(series.kwArgs.sort); + } + lang.mixin(kw, { + onComplete: function(data){ + var values; + if("valueFn" in series.kwArgs){ + var fn = series.kwArgs.valueFn; + values = arr.map(data, function(x){ + return fn(series.data.getValue(x, series.field, 0)); + }); + }else{ + values = arr.map(data, function(x){ + return series.data.getValue(x, series.field, 0); + }); + } + c.addSeries(series.name, values, series.kwArgs).render(); + } + }); + series.data.fetch(kw); + } + return render; + }, false); + if(render){ c.render(); } + }, + destroy: function(){ + // summary: properly destroy the widget + this.chart.destroy(); + this.inherited(arguments); + }, + resize: function(box){ + // summary: + // Resize the widget. + // description: + // Resize the domNode and the widget surface to the dimensions of a box of the following form: + // `{ l: 50, t: 200, w: 300: h: 150 }` + // If no box is provided, resize the surface to the marginBox of the domNode. + // box: + // If passed, denotes the new size of the widget. + this.chart.resize(box); + } + }); + + collectParams = function(node, type, kw){ + var dp = eval("(" + type + ".prototype.defaultParams)"); + var x, attr; + for(x in dp){ + if(x in kw){ continue; } + attr = node.getAttribute(x); + kw[x] = du.coerceType(dp[x], attr == null || typeof attr == "undefined" ? dp[x] : attr); + } + var op = eval("(" + type + ".prototype.optionalParams)"); + for(x in op){ + if(x in kw){ continue; } + attr = node.getAttribute(x); + if(attr != null){ + kw[x] = du.coerceType(op[x], attr); + } + } + }; + + collectAxisParams = function(node){ + var name = node.getAttribute("name"), type = node.getAttribute("type"); + if(!name){ return null; } + var o = {name: name, kwArgs: {}}, kw = o.kwArgs; + if(type){ + if(dc.axis2d[type]){ + type = dojo._scopeName + "x.charting.axis2d." + type; + } + var axis = eval("(" + type + ")"); + if(axis){ kw.type = axis; } + }else{ + type = dojo._scopeName + "x.charting.axis2d.Default"; + } + collectParams(node, type, kw); + // compatibility conversions + if(kw.font || kw.fontColor){ + if(!kw.tick){ + kw.tick = {}; + } + if(kw.font){ + kw.tick.font = kw.font; + } + if(kw.fontColor){ + kw.tick.fontColor = kw.fontColor; + } + } + return o; + }; + + collectPlotParams = function(node){ + // var name = d.attr(node, "name"), type = d.attr(node, "type"); + var name = node.getAttribute("name"), type = node.getAttribute("type"); + if(!name){ return null; } + var o = {name: name, kwArgs: {}}, kw = o.kwArgs; + if(type){ + if(dc.plot2d && dc.plot2d[type]){ + type = dojo._scopeName + "x.charting.plot2d." + type; + } + var plot = eval("(" + type + ")"); + if(plot){ kw.type = plot; } + }else{ + type = dojo._scopeName + "x.charting.plot2d.Default"; + } + collectParams(node, type, kw); + return o; + }; + + collectActionParams = function(node){ + // var plot = d.attr(node, "plot"), type = d.attr(node, "type"); + var plot = node.getAttribute("plot"), type = node.getAttribute("type"); + if(!plot){ plot = "default"; } + var o = {plot: plot, kwArgs: {}}, kw = o.kwArgs; + if(type){ + if(dc.action2d[type]){ + type = dojo._scopeName + "x.charting.action2d." + type; + } + var action = eval("(" + type + ")"); + if(!action){ return null; } + o.action = action; + }else{ + return null; + } + collectParams(node, type, kw); + return o; + }; + + collectDataParams = function(node){ + var ga = lang.partial(html.attr, node); + var name = ga("name"); + if(!name){ return null; } + var o = { name: name, kwArgs: {} }, kw = o.kwArgs, t; + t = ga("plot"); + if(t != null){ kw.plot = t; } + t = ga("marker"); + if(t != null){ kw.marker = t; } + t = ga("stroke"); + if(t != null){ kw.stroke = eval("(" + t + ")"); } + t = ga("outline"); + if(t != null){ kw.outline = eval("(" + t + ")"); } + t = ga("shadow"); + if(t != null){ kw.shadow = eval("(" + t + ")"); } + t = ga("fill"); + if(t != null){ kw.fill = eval("(" + t + ")"); } + t = ga("font"); + if(t != null){ kw.font = t; } + t = ga("fontColor"); + if(t != null){ kw.fontColor = eval("(" + t + ")"); } + t = ga("legend"); + if(t != null){ kw.legend = t; } + t = ga("data"); + if(t != null){ + o.type = "data"; + o.data = t ? arr.map(String(t).split(','), Number) : []; + return o; + } + t = ga("array"); + if(t != null){ + o.type = "data"; + o.data = eval("(" + t + ")"); + return o; + } + t = ga("store"); + if(t != null){ + o.type = "store"; + o.data = eval("(" + t + ")"); + t = ga("field"); + o.field = t != null ? t : "value"; + t = ga("query"); + if(!!t){ kw.query = t; } + t = ga("queryOptions"); + if(!!t){ kw.queryOptions = eval("(" + t + ")"); } + t = ga("start"); + if(!!t){ kw.start = Number(t); } + t = ga("count"); + if(!!t){ kw.count = Number(t); } + t = ga("sort"); + if(!!t){ kw.sort = eval("("+t+")"); } + t = ga("valueFn"); + if(!!t){ kw.valueFn = dfl.lambda(t); } + return o; + } + return null; + }; + + return ChartWidget; +}); + +}, +'dojox/lang/functional':function(){ +define("dojox/lang/functional", ["./functional/lambda", "./functional/array", "./functional/object"], function(df){ + return df; +}); + +}, +'dojox/charting/scaler/common':function(){ +define("dojox/charting/scaler/common", ["dojo/_base/lang"], function(lang){ + + var eq = function(/*Number*/ a, /*Number*/ b){ + // summary: compare two FP numbers for equality + return Math.abs(a - b) <= 1e-6 * (Math.abs(a) + Math.abs(b)); // Boolean + }; + + var common = lang.getObject("dojox.charting.scaler.common", true); + + var testedModules = {}; + + return lang.mixin(common, { + doIfLoaded: function(moduleName, ifloaded, ifnotloaded){ + if(testedModules[moduleName] == undefined){ + try{ + testedModules[moduleName] = require(moduleName); + }catch(e){ + testedModules[moduleName] = null; + } + } + if(testedModules[moduleName]){ + return ifloaded(testedModules[moduleName]); + }else{ + return ifnotloaded(); + } + }, + findString: function(/*String*/ val, /*Array*/ text){ + val = val.toLowerCase(); + for(var i = 0; i < text.length; ++i){ + if(val == text[i]){ return true; } + } + return false; + }, + getNumericLabel: function(/*Number*/ number, /*Number*/ precision, /*Object*/ kwArgs){ + var def = ""; + common.doIfLoaded("dojo/number", function(numberLib){ + def = (kwArgs.fixed ? numberLib.format(number, {places : precision < 0 ? -precision : 0}) : + numberLib.format(number)) || ""; + }, function(){ + def = kwArgs.fixed ? number.toFixed(precision < 0 ? -precision : 0) : number.toString(); + }); + if(kwArgs.labelFunc){ + var r = kwArgs.labelFunc(def, number, precision); + if(r){ return r; } + // else fall through to the regular labels search + } + if(kwArgs.labels){ + // classic binary search + var l = kwArgs.labels, lo = 0, hi = l.length; + while(lo < hi){ + var mid = Math.floor((lo + hi) / 2), val = l[mid].value; + if(val < number){ + lo = mid + 1; + }else{ + hi = mid; + } + } + // lets take into account FP errors + if(lo < l.length && eq(l[lo].value, number)){ + return l[lo].text; + } + --lo; + if(lo >= 0 && lo < l.length && eq(l[lo].value, number)){ + return l[lo].text; + } + lo += 2; + if(lo < l.length && eq(l[lo].value, number)){ + return l[lo].text; + } + // otherwise we will produce a number + } + return def; + } + }); +}); + +}, +'dojox/charting/axis2d/common':function(){ +define("dojox/charting/axis2d/common", ["dojo/_base/lang", "dojo/_base/html", "dojo/_base/window", "dojo/dom-geometry", "dojox/gfx"], + function(lang, html, win, domGeom, g){ + + var common = lang.getObject("dojox.charting.axis2d.common", true); + + var clearNode = function(s){ + s.marginLeft = "0px"; + s.marginTop = "0px"; + s.marginRight = "0px"; + s.marginBottom = "0px"; + s.paddingLeft = "0px"; + s.paddingTop = "0px"; + s.paddingRight = "0px"; + s.paddingBottom = "0px"; + s.borderLeftWidth = "0px"; + s.borderTopWidth = "0px"; + s.borderRightWidth = "0px"; + s.borderBottomWidth = "0px"; + }; + + var getBoxWidth = function(n){ + // marginBox is incredibly slow, so avoid it if we can + if(n["getBoundingClientRect"]){ + var bcr = n.getBoundingClientRect(); + return bcr.width || (bcr.right - bcr.left); + }else{ + return domGeom.getMarginBox(n).w; + } + }; + + return lang.mixin(common, { + // summary: + // Common methods to be used by any axis. This is considered "static". + createText: { + gfx: function(chart, creator, x, y, align, text, font, fontColor){ + // summary: + // Use dojox.gfx to create any text. + // chart: dojox.charting.Chart + // The chart to create the text into. + // creator: dojox.gfx.Surface + // The graphics surface to use for creating the text. + // x: Number + // Where to create the text along the x axis (CSS left). + // y: Number + // Where to create the text along the y axis (CSS top). + // align: String + // How to align the text. Can be "left", "right", "center". + // text: String + // The text to render. + // font: String + // The font definition, a la CSS "font". + // fontColor: String|dojo.Color + // The color of the resultant text. + // returns: dojox.gfx.Text + // The resultant GFX object. + return creator.createText({ + x: x, y: y, text: text, align: align + }).setFont(font).setFill(fontColor); // dojox.gfx.Text + }, + html: function(chart, creator, x, y, align, text, font, fontColor, labelWidth){ + // summary: + // Use the HTML DOM to create any text. + // chart: dojox.charting.Chart + // The chart to create the text into. + // creator: dojox.gfx.Surface + // The graphics surface to use for creating the text. + // x: Number + // Where to create the text along the x axis (CSS left). + // y: Number + // Where to create the text along the y axis (CSS top). + // align: String + // How to align the text. Can be "left", "right", "center". + // text: String + // The text to render. + // font: String + // The font definition, a la CSS "font". + // fontColor: String|dojo.Color + // The color of the resultant text. + // labelWidth: Number? + // The maximum width of the resultant DOM node. + // returns: DOMNode + // The resultant DOMNode (a "div" element). + + // setup the text node + var p = win.doc.createElement("div"), s = p.style, boxWidth; + // bidi support, if this function exists the module was loaded + if(chart.getTextDir){ + p.dir = chart.getTextDir(text); + } + clearNode(s); + s.font = font; + p.innerHTML = String(text).replace(/\s/g, " "); + s.color = fontColor; + // measure the size + s.position = "absolute"; + s.left = "-10000px"; + win.body().appendChild(p); + var size = g.normalizedLength(g.splitFontString(font).size); + + // do we need to calculate the label width? + if(!labelWidth){ + boxWidth = getBoxWidth(p); + } + // when the textDir is rtl, but the UI ltr needs + // to recalculate the starting point + if(p.dir == "rtl"){ + x += labelWidth ? labelWidth : boxWidth; + } + + // new settings for the text node + win.body().removeChild(p); + + s.position = "relative"; + if(labelWidth){ + s.width = labelWidth + "px"; + // s.border = "1px dotted grey"; + switch(align){ + case "middle": + s.textAlign = "center"; + s.left = (x - labelWidth / 2) + "px"; + break; + case "end": + s.textAlign = "right"; + s.left = (x - labelWidth) + "px"; + break; + default: + s.left = x + "px"; + s.textAlign = "left"; + break; + } + }else{ + switch(align){ + case "middle": + s.left = Math.floor(x - boxWidth / 2) + "px"; + // s.left = Math.floor(x - p.offsetWidth / 2) + "px"; + break; + case "end": + s.left = Math.floor(x - boxWidth) + "px"; + // s.left = Math.floor(x - p.offsetWidth) + "px"; + break; + //case "start": + default: + s.left = Math.floor(x) + "px"; + break; + } + } + s.top = Math.floor(y - size) + "px"; + s.whiteSpace = "nowrap"; // hack for WebKit + // setup the wrapper node + var wrap = win.doc.createElement("div"), w = wrap.style; + clearNode(w); + w.width = "0px"; + w.height = "0px"; + // insert nodes + wrap.appendChild(p) + chart.node.insertBefore(wrap, chart.node.firstChild); + return wrap; // DOMNode + } + } + }); +}); + +}, +'dijit/_TemplatedMixin':function(){ +define("dijit/_TemplatedMixin", [ + "dojo/_base/lang", // lang.getObject + "dojo/touch", + "./_WidgetBase", + "dojo/string", // string.substitute string.trim + "dojo/cache", // dojo.cache + "dojo/_base/array", // array.forEach + "dojo/_base/declare", // declare + "dojo/dom-construct", // domConstruct.destroy, domConstruct.toDom + "dojo/_base/sniff", // has("ie") + "dojo/_base/unload", // unload.addOnWindowUnload + "dojo/_base/window" // win.doc +], function(lang, touch, _WidgetBase, string, cache, array, declare, domConstruct, has, unload, win) { + +/*===== + var _WidgetBase = dijit._WidgetBase; +=====*/ + + // module: + // dijit/_TemplatedMixin + // summary: + // Mixin for widgets that are instantiated from a template + + var _TemplatedMixin = declare("dijit._TemplatedMixin", null, { + // summary: + // Mixin for widgets that are instantiated from a template + + // templateString: [protected] String + // A string that represents the widget template. + // Use in conjunction with dojo.cache() to load from a file. + templateString: null, + + // templatePath: [protected deprecated] String + // Path to template (HTML file) for this widget relative to dojo.baseUrl. + // Deprecated: use templateString with require([... "dojo/text!..."], ...) instead + templatePath: null, + + // skipNodeCache: [protected] Boolean + // If using a cached widget template nodes poses issues for a + // particular widget class, it can set this property to ensure + // that its template is always re-built from a string + _skipNodeCache: false, + + // _earlyTemplatedStartup: Boolean + // A fallback to preserve the 1.0 - 1.3 behavior of children in + // templates having their startup called before the parent widget + // fires postCreate. Defaults to 'false', causing child widgets to + // have their .startup() called immediately before a parent widget + // .startup(), but always after the parent .postCreate(). Set to + // 'true' to re-enable to previous, arguably broken, behavior. + _earlyTemplatedStartup: false, + +/*===== + // _attachPoints: [private] String[] + // List of widget attribute names associated with data-dojo-attach-point=... in the + // template, ex: ["containerNode", "labelNode"] + _attachPoints: [], + =====*/ + +/*===== + // _attachEvents: [private] Handle[] + // List of connections associated with data-dojo-attach-event=... in the + // template + _attachEvents: [], + =====*/ + + constructor: function(){ + this._attachPoints = []; + this._attachEvents = []; + }, + + _stringRepl: function(tmpl){ + // summary: + // Does substitution of ${foo} type properties in template string + // tags: + // private + var className = this.declaredClass, _this = this; + // Cache contains a string because we need to do property replacement + // do the property replacement + return string.substitute(tmpl, this, function(value, key){ + if(key.charAt(0) == '!'){ value = lang.getObject(key.substr(1), false, _this); } + if(typeof value == "undefined"){ throw new Error(className+" template:"+key); } // a debugging aide + if(value == null){ return ""; } + + // Substitution keys beginning with ! will skip the transform step, + // in case a user wishes to insert unescaped markup, e.g. ${!foo} + return key.charAt(0) == "!" ? value : + // Safer substitution, see heading "Attribute values" in + // http://www.w3.org/TR/REC-html40/appendix/notes.html#h-B.3.2 + value.toString().replace(/"/g,"""); //TODO: add &? use encodeXML method? + }, this); + }, + + buildRendering: function(){ + // summary: + // Construct the UI for this widget from a template, setting this.domNode. + // tags: + // protected + + if(!this.templateString){ + this.templateString = cache(this.templatePath, {sanitize: true}); + } + + // Lookup cached version of template, and download to cache if it + // isn't there already. Returns either a DomNode or a string, depending on + // whether or not the template contains ${foo} replacement parameters. + var cached = _TemplatedMixin.getCachedTemplate(this.templateString, this._skipNodeCache); + + var node; + if(lang.isString(cached)){ + node = domConstruct.toDom(this._stringRepl(cached)); + if(node.nodeType != 1){ + // Flag common problems such as templates with multiple top level nodes (nodeType == 11) + throw new Error("Invalid template: " + cached); + } + }else{ + // if it's a node, all we have to do is clone it + node = cached.cloneNode(true); + } + + this.domNode = node; + + // Call down to _Widget.buildRendering() to get base classes assigned + // TODO: change the baseClass assignment to _setBaseClassAttr + this.inherited(arguments); + + // recurse through the node, looking for, and attaching to, our + // attachment points and events, which should be defined on the template node. + this._attachTemplateNodes(node, function(n,p){ return n.getAttribute(p); }); + + this._beforeFillContent(); // hook for _WidgetsInTemplateMixin + + this._fillContent(this.srcNodeRef); + }, + + _beforeFillContent: function(){ + }, + + _fillContent: function(/*DomNode*/ source){ + // summary: + // Relocate source contents to templated container node. + // this.containerNode must be able to receive children, or exceptions will be thrown. + // tags: + // protected + var dest = this.containerNode; + if(source && dest){ + while(source.hasChildNodes()){ + dest.appendChild(source.firstChild); + } + } + }, + + _attachTemplateNodes: function(rootNode, getAttrFunc){ + // summary: + // Iterate through the template and attach functions and nodes accordingly. + // Alternately, if rootNode is an array of widgets, then will process data-dojo-attach-point + // etc. for those widgets. + // description: + // Map widget properties and functions to the handlers specified in + // the dom node and it's descendants. This function iterates over all + // nodes and looks for these properties: + // * dojoAttachPoint/data-dojo-attach-point + // * dojoAttachEvent/data-dojo-attach-event + // rootNode: DomNode|Widget[] + // the node to search for properties. All children will be searched. + // getAttrFunc: Function + // a function which will be used to obtain property for a given + // DomNode/Widget + // tags: + // private + + var nodes = lang.isArray(rootNode) ? rootNode : (rootNode.all || rootNode.getElementsByTagName("*")); + var x = lang.isArray(rootNode) ? 0 : -1; + for(; x<nodes.length; x++){ + var baseNode = (x == -1) ? rootNode : nodes[x]; + if(this.widgetsInTemplate && (getAttrFunc(baseNode, "dojoType") || getAttrFunc(baseNode, "data-dojo-type"))){ + continue; + } + // Process data-dojo-attach-point + var attachPoint = getAttrFunc(baseNode, "dojoAttachPoint") || getAttrFunc(baseNode, "data-dojo-attach-point"); + if(attachPoint){ + var point, points = attachPoint.split(/\s*,\s*/); + while((point = points.shift())){ + if(lang.isArray(this[point])){ + this[point].push(baseNode); + }else{ + this[point]=baseNode; + } + this._attachPoints.push(point); + } + } + + // Process data-dojo-attach-event + var attachEvent = getAttrFunc(baseNode, "dojoAttachEvent") || getAttrFunc(baseNode, "data-dojo-attach-event"); + if(attachEvent){ + // NOTE: we want to support attributes that have the form + // "domEvent: nativeEvent; ..." + var event, events = attachEvent.split(/\s*,\s*/); + var trim = lang.trim; + while((event = events.shift())){ + if(event){ + var thisFunc = null; + if(event.indexOf(":") != -1){ + // oh, if only JS had tuple assignment + var funcNameArr = event.split(":"); + event = trim(funcNameArr[0]); + thisFunc = trim(funcNameArr[1]); + }else{ + event = trim(event); + } + if(!thisFunc){ + thisFunc = event; + } + // Map "press", "move" and "release" to keys.touch, keys.move, keys.release + this._attachEvents.push(this.connect(baseNode, touch[event] || event, thisFunc)); + } + } + } + } + }, + + destroyRendering: function(){ + // Delete all attach points to prevent IE6 memory leaks. + array.forEach(this._attachPoints, function(point){ + delete this[point]; + }, this); + this._attachPoints = []; + + // And same for event handlers + array.forEach(this._attachEvents, this.disconnect, this); + this._attachEvents = []; + + this.inherited(arguments); + } + }); + + // key is templateString; object is either string or DOM tree + _TemplatedMixin._templateCache = {}; + + _TemplatedMixin.getCachedTemplate = function(templateString, alwaysUseString){ + // summary: + // Static method to get a template based on the templatePath or + // templateString key + // templateString: String + // The template + // alwaysUseString: Boolean + // Don't cache the DOM tree for this template, even if it doesn't have any variables + // returns: Mixed + // Either string (if there are ${} variables that need to be replaced) or just + // a DOM tree (if the node can be cloned directly) + + // is it already cached? + var tmplts = _TemplatedMixin._templateCache; + var key = templateString; + var cached = tmplts[key]; + if(cached){ + try{ + // if the cached value is an innerHTML string (no ownerDocument) or a DOM tree created within the current document, then use the current cached value + if(!cached.ownerDocument || cached.ownerDocument == win.doc){ + // string or node of the same document + return cached; + } + }catch(e){ /* squelch */ } // IE can throw an exception if cached.ownerDocument was reloaded + domConstruct.destroy(cached); + } + + templateString = string.trim(templateString); + + if(alwaysUseString || templateString.match(/\$\{([^\}]+)\}/g)){ + // there are variables in the template so all we can do is cache the string + return (tmplts[key] = templateString); //String + }else{ + // there are no variables in the template so we can cache the DOM tree + var node = domConstruct.toDom(templateString); + if(node.nodeType != 1){ + throw new Error("Invalid template: " + templateString); + } + return (tmplts[key] = node); //Node + } + }; + + if(has("ie")){ + unload.addOnWindowUnload(function(){ + var cache = _TemplatedMixin._templateCache; + for(var key in cache){ + var value = cache[key]; + if(typeof value == "object"){ // value is either a string or a DOM node template + domConstruct.destroy(value); + } + delete cache[key]; + } + }); + } + + // These arguments can be specified for widgets which are used in templates. + // Since any widget can be specified as sub widgets in template, mix it + // into the base widget class. (This is a hack, but it's effective.) + lang.extend(_WidgetBase,{ + dojoAttachEvent: "", + dojoAttachPoint: "" + }); + + return _TemplatedMixin; +}); + +}, +'dojox/lang/functional/object':function(){ +define(["dojo/_base/kernel", "dojo/_base/lang", "dojo/_base/window", "./lambda"], function(dojo, lang, win, df){ + +// This module adds high-level functions and related constructs: +// - object/dictionary helpers + +// Defined methods: +// - take any valid lambda argument as the functional argument +// - skip all attributes that are present in the empty object +// (IE and/or 3rd-party libraries). + + var empty = {}; + +/*===== + var df = dojox.lang.functional; + =====*/ + lang.mixin(df, { + // object helpers + keys: function(/*Object*/ obj){ + // summary: returns an array of all keys in the object + var t = []; + for(var i in obj){ + if(!(i in empty)){ + t.push(i); + } + } + return t; // Array + }, + values: function(/*Object*/ obj){ + // summary: returns an array of all values in the object + var t = []; + for(var i in obj){ + if(!(i in empty)){ + t.push(obj[i]); + } + } + return t; // Array + }, + filterIn: function(/*Object*/ obj, /*Function|String|Array*/ f, /*Object?*/ o){ + // summary: creates new object with all attributes that pass the test + // implemented by the provided function. + o = o || win.global; f = df.lambda(f); + var t = {}, v, i; + for(i in obj){ + if(!(i in empty)){ + v = obj[i]; + if(f.call(o, v, i, obj)){ t[i] = v; } + } + } + return t; // Object + }, + forIn: function(/*Object*/ obj, /*Function|String|Array*/ f, /*Object?*/ o){ + // summary: iterates over all object attributes. + o = o || win.global; f = df.lambda(f); + for(var i in obj){ + if(!(i in empty)){ + f.call(o, obj[i], i, obj); + } + } + return o; // Object + }, + mapIn: function(/*Object*/ obj, /*Function|String|Array*/ f, /*Object?*/ o){ + // summary: creates new object with the results of calling + // a provided function on every attribute in this object. + o = o || win.global; f = df.lambda(f); + var t = {}, i; + for(i in obj){ + if(!(i in empty)){ + t[i] = f.call(o, obj[i], i, obj); + } + } + return t; // Object + } + }); + + return df; +}); + +}, +'dojo/window':function(){ +define(["./_base/lang", "./_base/sniff", "./_base/window", "./dom", "./dom-geometry", "./dom-style"], + function(lang, has, baseWindow, dom, geom, style) { + +// module: +// dojo/window +// summary: +// TODOC + +var window = lang.getObject("dojo.window", true); + +/*===== +dojo.window = { + // summary: + // TODO +}; +window = dojo.window; +=====*/ + +window.getBox = function(){ + // summary: + // Returns the dimensions and scroll position of the viewable area of a browser window + + var + scrollRoot = (baseWindow.doc.compatMode == 'BackCompat') ? baseWindow.body() : baseWindow.doc.documentElement, + // get scroll position + scroll = geom.docScroll(), // scrollRoot.scrollTop/Left should work + w, h; + + if(has("touch")){ // if(scrollbars not supported) + var uiWindow = baseWindow.doc.parentWindow || baseWindow.doc.defaultView; // use UI window, not dojo.global window. baseWindow.doc.parentWindow probably not needed since it's not defined for webkit + // on mobile, scrollRoot.clientHeight <= uiWindow.innerHeight <= scrollRoot.offsetHeight, return uiWindow.innerHeight + w = uiWindow.innerWidth || scrollRoot.clientWidth; // || scrollRoot.clientXXX probably never evaluated + h = uiWindow.innerHeight || scrollRoot.clientHeight; + }else{ + // on desktops, scrollRoot.clientHeight <= scrollRoot.offsetHeight <= uiWindow.innerHeight, return scrollRoot.clientHeight + // uiWindow.innerWidth/Height includes the scrollbar and cannot be used + w = scrollRoot.clientWidth; + h = scrollRoot.clientHeight; + } + return { + l: scroll.x, + t: scroll.y, + w: w, + h: h + }; +}; + +window.get = function(doc){ + // summary: + // Get window object associated with document doc + + // In some IE versions (at least 6.0), document.parentWindow does not return a + // reference to the real window object (maybe a copy), so we must fix it as well + // We use IE specific execScript to attach the real window reference to + // document._parentWindow for later use + if(has("ie") && window !== document.parentWindow){ + /* + In IE 6, only the variable "window" can be used to connect events (others + may be only copies). + */ + doc.parentWindow.execScript("document._parentWindow = window;", "Javascript"); + //to prevent memory leak, unset it after use + //another possibility is to add an onUnload handler which seems overkill to me (liucougar) + var win = doc._parentWindow; + doc._parentWindow = null; + return win; // Window + } + + return doc.parentWindow || doc.defaultView; // Window +}; + +window.scrollIntoView = function(/*DomNode*/ node, /*Object?*/ pos){ + // summary: + // Scroll the passed node into view, if it is not already. + + // don't rely on node.scrollIntoView working just because the function is there + + try{ // catch unexpected/unrecreatable errors (#7808) since we can recover using a semi-acceptable native method + node = dom.byId(node); + var doc = node.ownerDocument || baseWindow.doc, + body = doc.body || baseWindow.body(), + html = doc.documentElement || body.parentNode, + isIE = has("ie"), isWK = has("webkit"); + // if an untested browser, then use the native method + if((!(has("mozilla") || isIE || isWK || has("opera")) || node == body || node == html) && (typeof node.scrollIntoView != "undefined")){ + node.scrollIntoView(false); // short-circuit to native if possible + return; + } + var backCompat = doc.compatMode == 'BackCompat', + clientAreaRoot = (isIE >= 9 && node.ownerDocument.parentWindow.frameElement) + ? ((html.clientHeight > 0 && html.clientWidth > 0 && (body.clientHeight == 0 || body.clientWidth == 0 || body.clientHeight > html.clientHeight || body.clientWidth > html.clientWidth)) ? html : body) + : (backCompat ? body : html), + scrollRoot = isWK ? body : clientAreaRoot, + rootWidth = clientAreaRoot.clientWidth, + rootHeight = clientAreaRoot.clientHeight, + rtl = !geom.isBodyLtr(), + nodePos = pos || geom.position(node), + el = node.parentNode, + isFixed = function(el){ + return ((isIE <= 6 || (isIE && backCompat))? false : (style.get(el, 'position').toLowerCase() == "fixed")); + }; + if(isFixed(node)){ return; } // nothing to do + + while(el){ + if(el == body){ el = scrollRoot; } + var elPos = geom.position(el), + fixedPos = isFixed(el); + + if(el == scrollRoot){ + elPos.w = rootWidth; elPos.h = rootHeight; + if(scrollRoot == html && isIE && rtl){ elPos.x += scrollRoot.offsetWidth-elPos.w; } // IE workaround where scrollbar causes negative x + if(elPos.x < 0 || !isIE){ elPos.x = 0; } // IE can have values > 0 + if(elPos.y < 0 || !isIE){ elPos.y = 0; } + }else{ + var pb = geom.getPadBorderExtents(el); + elPos.w -= pb.w; elPos.h -= pb.h; elPos.x += pb.l; elPos.y += pb.t; + var clientSize = el.clientWidth, + scrollBarSize = elPos.w - clientSize; + if(clientSize > 0 && scrollBarSize > 0){ + elPos.w = clientSize; + elPos.x += (rtl && (isIE || el.clientLeft > pb.l/*Chrome*/)) ? scrollBarSize : 0; + } + clientSize = el.clientHeight; + scrollBarSize = elPos.h - clientSize; + if(clientSize > 0 && scrollBarSize > 0){ + elPos.h = clientSize; + } + } + if(fixedPos){ // bounded by viewport, not parents + if(elPos.y < 0){ + elPos.h += elPos.y; elPos.y = 0; + } + if(elPos.x < 0){ + elPos.w += elPos.x; elPos.x = 0; + } + if(elPos.y + elPos.h > rootHeight){ + elPos.h = rootHeight - elPos.y; + } + if(elPos.x + elPos.w > rootWidth){ + elPos.w = rootWidth - elPos.x; + } + } + // calculate overflow in all 4 directions + var l = nodePos.x - elPos.x, // beyond left: < 0 + t = nodePos.y - Math.max(elPos.y, 0), // beyond top: < 0 + r = l + nodePos.w - elPos.w, // beyond right: > 0 + bot = t + nodePos.h - elPos.h; // beyond bottom: > 0 + if(r * l > 0){ + var s = Math[l < 0? "max" : "min"](l, r); + if(rtl && ((isIE == 8 && !backCompat) || isIE >= 9)){ s = -s; } + nodePos.x += el.scrollLeft; + el.scrollLeft += s; + nodePos.x -= el.scrollLeft; + } + if(bot * t > 0){ + nodePos.y += el.scrollTop; + el.scrollTop += Math[t < 0? "max" : "min"](t, bot); + nodePos.y -= el.scrollTop; + } + el = (el != scrollRoot) && !fixedPos && el.parentNode; + } + }catch(error){ + console.error('scrollIntoView: ' + error); + node.scrollIntoView(false); + } +}; + +return window; +}); + +}, +'dojox/charting/axis2d/Default':function(){ +define("dojox/charting/axis2d/Default", ["dojo/_base/lang", "dojo/_base/array","dojo/_base/sniff", "dojo/_base/declare", + "dojo/_base/connect", "dojo/_base/html", "dojo/dom-geometry", "./Invisible", + "../scaler/common", "../scaler/linear", "./common", "dojox/gfx", "dojox/lang/utils"], + function(lang, arr, has, declare, connect, html, domGeom, Invisible, scommon, + lin, acommon, g, du){ + + /*===== + dojox.charting.axis2d.__AxisCtorArgs = function( + vertical, fixUpper, fixLower, natural, leftBottom, + includeZero, fixed, majorLabels, minorTicks, minorLabels, microTicks, htmlLabels, + min, max, from, to, majorTickStep, minorTickStep, microTickStep, + labels, labelFunc, maxLabelSize, + stroke, majorTick, minorTick, microTick, tick, + font, fontColor + ){ + // summary: + // Optional arguments used in the definition of an axis. + // + // vertical: Boolean? + // A flag that says whether an axis is vertical (i.e. y axis) or horizontal. Default is false (horizontal). + // fixUpper: String? + // Align the greatest value on the axis with the specified tick level. Options are "major", "minor", "micro", or "none". Defaults to "none". + // fixLower: String? + // Align the smallest value on the axis with the specified tick level. Options are "major", "minor", "micro", or "none". Defaults to "none". + // natural: Boolean? + // Ensure tick marks are made on "natural" numbers. Defaults to false. + // leftBottom: Boolean? + // The position of a vertical axis; if true, will be placed against the left-bottom corner of the chart. Defaults to true. + // includeZero: Boolean? + // Include 0 on the axis rendering. Default is false. + // fixed: Boolean? + // Force all axis labels to be fixed numbers. Default is true. + // majorLabels: Boolean? + // Flag to draw all labels at major ticks. Default is true. + // minorTicks: Boolean? + // Flag to draw minor ticks on an axis. Default is true. + // minorLabels: Boolean? + // Flag to draw labels on minor ticks. Default is true. + // microTicks: Boolean? + // Flag to draw micro ticks on an axis. Default is false. + // htmlLabels: Boolean? + // Flag to use HTML (as opposed to the native vector graphics engine) to draw labels. Default is true. + // min: Number? + // The smallest value on an axis. Default is 0. + // max: Number? + // The largest value on an axis. Default is 1. + // from: Number? + // Force the chart to render data visible from this value. Default is 0. + // to: Number? + // Force the chart to render data visible to this value. Default is 1. + // majorTickStep: Number? + // The amount to skip before a major tick is drawn. Default is 4. + // minorTickStep: Number? + // The amount to skip before a minor tick is drawn. Default is 2. + // microTickStep: Number? + // The amount to skip before a micro tick is drawn. Default is 1. + // labels: Object[]? + // An array of labels for major ticks, with corresponding numeric values, ordered by value. + // labelFunc: Function? + // An optional function used to compute label values. + // maxLabelSize: Number? + // The maximum size, in pixels, for a label. To be used with the optional label function. + // stroke: dojox.gfx.Stroke? + // An optional stroke to be used for drawing an axis. + // majorTick: Object? + // An object containing a dojox.gfx.Stroke, and a length (number) for a major tick. + // minorTick: Object? + // An object containing a dojox.gfx.Stroke, and a length (number) for a minor tick. + // microTick: Object? + // An object containing a dojox.gfx.Stroke, and a length (number) for a micro tick. + // tick: Object? + // An object containing a dojox.gfx.Stroke, and a length (number) for a tick. + // font: String? + // An optional font definition (as used in the CSS font property) for labels. + // fontColor: String|dojo.Color? + // An optional color to be used in drawing labels. + // enableCache: Boolean? + // Whether the ticks and labels are cached from one rendering to another. This improves the rendering performance of + // successive rendering but penalize the first rendering. For labels it is only working with gfx labels + // not html ones. Default false. + + this.vertical = vertical; + this.fixUpper = fixUpper; + this.fixLower = fixLower; + this.natural = natural; + this.leftBottom = leftBottom; + this.includeZero = includeZero; + this.fixed = fixed; + this.majorLabels = majorLabels; + this.minorTicks = minorTicks; + this.minorLabels = minorLabels; + this.microTicks = microTicks; + this.htmlLabels = htmlLabels; + this.min = min; + this.max = max; + this.from = from; + this.to = to; + this.majorTickStep = majorTickStep; + this.minorTickStep = minorTickStep; + this.microTickStep = microTickStep; + this.labels = labels; + this.labelFunc = labelFunc; + this.maxLabelSize = maxLabelSize; + this.stroke = stroke; + this.majorTick = majorTick; + this.minorTick = minorTick; + this.microTick = microTick; + this.tick = tick; + this.font = font; + this.fontColor = fontColor; + this.enableCache = enableCache; + } + var Invisible = dojox.charting.axis2d.Invisible + =====*/ + + var labelGap = 4, // in pixels + centerAnchorLimit = 45; // in degrees + + return declare("dojox.charting.axis2d.Default", Invisible, { + // summary: + // The default axis object used in dojox.charting. See dojox.charting.Chart.addAxis for details. + // + // defaultParams: Object + // The default parameters used to define any axis. + // optionalParams: Object + // Any optional parameters needed to define an axis. + + /* + // TODO: the documentation tools need these to be pre-defined in order to pick them up + // correctly, but the code here is partially predicated on whether or not the properties + // actually exist. For now, we will leave these undocumented but in the code for later. -- TRT + + // opt: Object + // The actual options used to define this axis, created at initialization. + // scalar: Object + // The calculated helper object to tell charts how to draw an axis and any data. + // ticks: Object + // The calculated tick object that helps a chart draw the scaling on an axis. + // dirty: Boolean + // The state of the axis (whether it needs to be redrawn or not) + // scale: Number + // The current scale of the axis. + // offset: Number + // The current offset of the axis. + + opt: null, + scalar: null, + ticks: null, + dirty: true, + scale: 1, + offset: 0, + */ + defaultParams: { + vertical: false, // true for vertical axis + fixUpper: "none", // align the upper on ticks: "major", "minor", "micro", "none" + fixLower: "none", // align the lower on ticks: "major", "minor", "micro", "none" + natural: false, // all tick marks should be made on natural numbers + leftBottom: true, // position of the axis, used with "vertical" + includeZero: false, // 0 should be included + fixed: true, // all labels are fixed numbers + majorLabels: true, // draw major labels + minorTicks: true, // draw minor ticks + minorLabels: true, // draw minor labels + microTicks: false, // draw micro ticks + rotation: 0, // label rotation angle in degrees + htmlLabels: true, // use HTML to draw labels + enableCache: false // whether we cache or not + }, + optionalParams: { + min: 0, // minimal value on this axis + max: 1, // maximal value on this axis + from: 0, // visible from this value + to: 1, // visible to this value + majorTickStep: 4, // major tick step + minorTickStep: 2, // minor tick step + microTickStep: 1, // micro tick step + labels: [], // array of labels for major ticks + // with corresponding numeric values + // ordered by values + labelFunc: null, // function to compute label values + maxLabelSize: 0, // size in px. For use with labelFunc + maxLabelCharCount: 0, // size in word count. + trailingSymbol: null, + + // TODO: add support for minRange! + // minRange: 1, // smallest distance from min allowed on the axis + + // theme components + stroke: {}, // stroke for an axis + majorTick: {}, // stroke + length for a tick + minorTick: {}, // stroke + length for a tick + microTick: {}, // stroke + length for a tick + tick: {}, // stroke + length for a tick + font: "", // font for labels + fontColor: "", // color for labels as a string + title: "", // axis title + titleGap: 0, // gap between axis title and axis label + titleFont: "", // axis title font + titleFontColor: "", // axis title font color + titleOrientation: "" // "axis" means the title facing the axis, "away" means facing away + }, + + constructor: function(chart, kwArgs){ + // summary: + // The constructor for an axis. + // chart: dojox.charting.Chart + // The chart the axis belongs to. + // kwArgs: dojox.charting.axis2d.__AxisCtorArgs? + // Any optional keyword arguments to be used to define this axis. + this.opt = lang.clone(this.defaultParams); + du.updateWithObject(this.opt, kwArgs); + du.updateWithPattern(this.opt, kwArgs, this.optionalParams); + if(this.opt.enableCache){ + this._textFreePool = []; + this._lineFreePool = []; + this._textUsePool = []; + this._lineUsePool = []; + } + }, + getOffsets: function(){ + // summary: + // Get the physical offset values for this axis (used in drawing data series). + // returns: Object + // The calculated offsets in the form of { l, r, t, b } (left, right, top, bottom). + var s = this.scaler, offsets = { l: 0, r: 0, t: 0, b: 0 }; + if(!s){ + return offsets; + } + var o = this.opt, labelWidth = 0, a, b, c, d, + gl = scommon.getNumericLabel, + offset = 0, ma = s.major, mi = s.minor, + ta = this.chart.theme.axis, + // TODO: we use one font --- of major tick, we need to use major and minor fonts + taFont = o.font || (ta.majorTick && ta.majorTick.font) || (ta.tick && ta.tick.font), + taTitleFont = o.titleFont || (ta.tick && ta.tick.titleFont), + taTitleGap = (o.titleGap==0) ? 0 : o.titleGap || (ta.tick && ta.tick.titleGap) || 15, + taMajorTick = this.chart.theme.getTick("major", o), + taMinorTick = this.chart.theme.getTick("minor", o), + size = taFont ? g.normalizedLength(g.splitFontString(taFont).size) : 0, + tsize = taTitleFont ? g.normalizedLength(g.splitFontString(taTitleFont).size) : 0, + rotation = o.rotation % 360, leftBottom = o.leftBottom, + cosr = Math.abs(Math.cos(rotation * Math.PI / 180)), + sinr = Math.abs(Math.sin(rotation * Math.PI / 180)); + this.trailingSymbol = (o.trailingSymbol === undefined || o.trailingSymbol === null) ? this.trailingSymbol : o.trailingSymbol; + if(rotation < 0){ + rotation += 360; + } + + if(size){ + // we need width of all labels + if(this.labels){ + labelWidth = this._groupLabelWidth(this.labels, taFont, o.maxLabelCharCount); + }else{ + labelWidth = this._groupLabelWidth([ + gl(ma.start, ma.prec, o), + gl(ma.start + ma.count * ma.tick, ma.prec, o), + gl(mi.start, mi.prec, o), + gl(mi.start + mi.count * mi.tick, mi.prec, o) + ], taFont, o.maxLabelCharCount); + } + labelWidth = o.maxLabelSize ? Math.min(o.maxLabelSize, labelWidth) : labelWidth; + if(this.vertical){ + var side = leftBottom ? "l" : "r"; + switch(rotation){ + case 0: + case 180: + offsets[side] = labelWidth; + offsets.t = offsets.b = size / 2; + break; + case 90: + case 270: + offsets[side] = size; + offsets.t = offsets.b = labelWidth / 2; + break; + default: + if(rotation <= centerAnchorLimit || (180 < rotation && rotation <= (180 + centerAnchorLimit))){ + offsets[side] = size * sinr / 2 + labelWidth * cosr; + offsets[leftBottom ? "t" : "b"] = size * cosr / 2 + labelWidth * sinr; + offsets[leftBottom ? "b" : "t"] = size * cosr / 2; + }else if(rotation > (360 - centerAnchorLimit) || (180 > rotation && rotation > (180 - centerAnchorLimit))){ + offsets[side] = size * sinr / 2 + labelWidth * cosr; + offsets[leftBottom ? "b" : "t"] = size * cosr / 2 + labelWidth * sinr; + offsets[leftBottom ? "t" : "b"] = size * cosr / 2; + }else if(rotation < 90 || (180 < rotation && rotation < 270)){ + offsets[side] = size * sinr + labelWidth * cosr; + offsets[leftBottom ? "t" : "b"] = size * cosr + labelWidth * sinr; + }else{ + offsets[side] = size * sinr + labelWidth * cosr; + offsets[leftBottom ? "b" : "t"] = size * cosr + labelWidth * sinr; + } + break; + } + offsets[side] += labelGap + Math.max(taMajorTick.length, taMinorTick.length) + (o.title ? (tsize + taTitleGap) : 0); + }else{ + var side = leftBottom ? "b" : "t"; + switch(rotation){ + case 0: + case 180: + offsets[side] = size; + offsets.l = offsets.r = labelWidth / 2; + break; + case 90: + case 270: + offsets[side] = labelWidth; + offsets.l = offsets.r = size / 2; + break; + default: + if((90 - centerAnchorLimit) <= rotation && rotation <= 90 || (270 - centerAnchorLimit) <= rotation && rotation <= 270){ + offsets[side] = size * sinr / 2 + labelWidth * cosr; + offsets[leftBottom ? "r" : "l"] = size * cosr / 2 + labelWidth * sinr; + offsets[leftBottom ? "l" : "r"] = size * cosr / 2; + }else if(90 <= rotation && rotation <= (90 + centerAnchorLimit) || 270 <= rotation && rotation <= (270 + centerAnchorLimit)){ + offsets[side] = size * sinr / 2 + labelWidth * cosr; + offsets[leftBottom ? "l" : "r"] = size * cosr / 2 + labelWidth * sinr; + offsets[leftBottom ? "r" : "l"] = size * cosr / 2; + }else if(rotation < centerAnchorLimit || (180 < rotation && rotation < (180 - centerAnchorLimit))){ + offsets[side] = size * sinr + labelWidth * cosr; + offsets[leftBottom ? "r" : "l"] = size * cosr + labelWidth * sinr; + }else{ + offsets[side] = size * sinr + labelWidth * cosr; + offsets[leftBottom ? "l" : "r"] = size * cosr + labelWidth * sinr; + } + break; + } + offsets[side] += labelGap + Math.max(taMajorTick.length, taMinorTick.length) + (o.title ? (tsize + taTitleGap) : 0); + } + } + if(labelWidth){ + this._cachedLabelWidth = labelWidth; + } + return offsets; // Object + }, + cleanGroup: function(creator){ + if(this.opt.enableCache && this.group){ + this._lineFreePool = this._lineFreePool.concat(this._lineUsePool); + this._lineUsePool = []; + this._textFreePool = this._textFreePool.concat(this._textUsePool); + this._textUsePool = []; + } + this.inherited(arguments); + }, + createText: function(labelType, creator, x, y, align, textContent, font, fontColor, labelWidth){ + if(!this.opt.enableCache || labelType=="html"){ + return acommon.createText[labelType]( + this.chart, + creator, + x, + y, + align, + textContent, + font, + fontColor, + labelWidth + ); + } + var text; + if (this._textFreePool.length > 0){ + text = this._textFreePool.pop(); + text.setShape({x: x, y: y, text: textContent, align: align}); + // For now all items share the same font, no need to re-set it + //.setFont(font).setFill(fontColor); + // was cleared, add it back + creator.add(text); + }else{ + text = acommon.createText[labelType]( + this.chart, + creator, + x, + y, + align, + textContent, + font, + fontColor, + labelWidth + ); } + this._textUsePool.push(text); + return text; + }, + createLine: function(creator, params){ + var line; + if(this.opt.enableCache && this._lineFreePool.length > 0){ + line = this._lineFreePool.pop(); + line.setShape(params); + // was cleared, add it back + creator.add(line); + }else{ + line = creator.createLine(params); + } + if(this.opt.enableCache){ + this._lineUsePool.push(line); + } + return line; + }, + render: function(dim, offsets){ + // summary: + // Render/draw the axis. + // dim: Object + // An object of the form { width, height}. + // offsets: Object + // An object of the form { l, r, t, b }. + // returns: dojox.charting.axis2d.Default + // The reference to the axis for functional chaining. + if(!this.dirty){ + return this; // dojox.charting.axis2d.Default + } + // prepare variable + var o = this.opt, ta = this.chart.theme.axis, leftBottom = o.leftBottom, rotation = o.rotation % 360, + start, stop, titlePos, titleRotation=0, titleOffset, axisVector, tickVector, anchorOffset, labelOffset, labelAlign, + + // TODO: we use one font --- of major tick, we need to use major and minor fonts + taFont = o.font || (ta.majorTick && ta.majorTick.font) || (ta.tick && ta.tick.font), + taTitleFont = o.titleFont || (ta.tick && ta.tick.titleFont), + // TODO: we use one font color --- we need to use different colors + taFontColor = o.fontColor || (ta.majorTick && ta.majorTick.fontColor) || (ta.tick && ta.tick.fontColor) || "black", + taTitleFontColor = o.titleFontColor || (ta.tick && ta.tick.titleFontColor) || "black", + taTitleGap = (o.titleGap==0) ? 0 : o.titleGap || (ta.tick && ta.tick.titleGap) || 15, + taTitleOrientation = o.titleOrientation || (ta.tick && ta.tick.titleOrientation) || "axis", + taMajorTick = this.chart.theme.getTick("major", o), + taMinorTick = this.chart.theme.getTick("minor", o), + taMicroTick = this.chart.theme.getTick("micro", o), + + tickSize = Math.max(taMajorTick.length, taMinorTick.length, taMicroTick.length), + taStroke = "stroke" in o ? o.stroke : ta.stroke, + size = taFont ? g.normalizedLength(g.splitFontString(taFont).size) : 0, + cosr = Math.abs(Math.cos(rotation * Math.PI / 180)), + sinr = Math.abs(Math.sin(rotation * Math.PI / 180)), + tsize = taTitleFont ? g.normalizedLength(g.splitFontString(taTitleFont).size) : 0; + if(rotation < 0){ + rotation += 360; + } + if(this.vertical){ + start = {y: dim.height - offsets.b}; + stop = {y: offsets.t}; + titlePos = {y: (dim.height - offsets.b + offsets.t)/2}; + titleOffset = size * sinr + (this._cachedLabelWidth || 0) * cosr + labelGap + Math.max(taMajorTick.length, taMinorTick.length) + tsize + taTitleGap; + axisVector = {x: 0, y: -1}; + labelOffset = {x: 0, y: 0}; + tickVector = {x: 1, y: 0}; + anchorOffset = {x: labelGap, y: 0}; + switch(rotation){ + case 0: + labelAlign = "end"; + labelOffset.y = size * 0.4; + break; + case 90: + labelAlign = "middle"; + labelOffset.x = -size; + break; + case 180: + labelAlign = "start"; + labelOffset.y = -size * 0.4; + break; + case 270: + labelAlign = "middle"; + break; + default: + if(rotation < centerAnchorLimit){ + labelAlign = "end"; + labelOffset.y = size * 0.4; + }else if(rotation < 90){ + labelAlign = "end"; + labelOffset.y = size * 0.4; + }else if(rotation < (180 - centerAnchorLimit)){ + labelAlign = "start"; + }else if(rotation < (180 + centerAnchorLimit)){ + labelAlign = "start"; + labelOffset.y = -size * 0.4; + }else if(rotation < 270){ + labelAlign = "start"; + labelOffset.x = leftBottom ? 0 : size * 0.4; + }else if(rotation < (360 - centerAnchorLimit)){ + labelAlign = "end"; + labelOffset.x = leftBottom ? 0 : size * 0.4; + }else{ + labelAlign = "end"; + labelOffset.y = size * 0.4; + } + } + if(leftBottom){ + start.x = stop.x = offsets.l; + titleRotation = (taTitleOrientation && taTitleOrientation == "away") ? 90 : 270; + titlePos.x = offsets.l - titleOffset + (titleRotation == 270 ? tsize : 0); + tickVector.x = -1; + anchorOffset.x = -anchorOffset.x; + }else{ + start.x = stop.x = dim.width - offsets.r; + titleRotation = (taTitleOrientation && taTitleOrientation == "axis") ? 90 : 270; + titlePos.x = dim.width - offsets.r + titleOffset - (titleRotation == 270 ? 0 : tsize); + switch(labelAlign){ + case "start": + labelAlign = "end"; + break; + case "end": + labelAlign = "start"; + break; + case "middle": + labelOffset.x += size; + break; + } + } + }else{ + start = {x: offsets.l}; + stop = {x: dim.width - offsets.r}; + titlePos = {x: (dim.width - offsets.r + offsets.l)/2}; + titleOffset = size * cosr + (this._cachedLabelWidth || 0) * sinr + labelGap + Math.max(taMajorTick.length, taMinorTick.length) + tsize + taTitleGap; + axisVector = {x: 1, y: 0}; + labelOffset = {x: 0, y: 0}; + tickVector = {x: 0, y: 1}; + anchorOffset = {x: 0, y: labelGap}; + switch(rotation){ + case 0: + labelAlign = "middle"; + labelOffset.y = size; + break; + case 90: + labelAlign = "start"; + labelOffset.x = -size * 0.4; + break; + case 180: + labelAlign = "middle"; + break; + case 270: + labelAlign = "end"; + labelOffset.x = size * 0.4; + break; + default: + if(rotation < (90 - centerAnchorLimit)){ + labelAlign = "start"; + labelOffset.y = leftBottom ? size : 0; + }else if(rotation < (90 + centerAnchorLimit)){ + labelAlign = "start"; + labelOffset.x = -size * 0.4; + }else if(rotation < 180){ + labelAlign = "start"; + labelOffset.y = leftBottom ? 0 : -size; + }else if(rotation < (270 - centerAnchorLimit)){ + labelAlign = "end"; + labelOffset.y = leftBottom ? 0 : -size; + }else if(rotation < (270 + centerAnchorLimit)){ + labelAlign = "end"; + labelOffset.y = leftBottom ? size * 0.4 : 0; + }else{ + labelAlign = "end"; + labelOffset.y = leftBottom ? size : 0; + } + } + if(leftBottom){ + start.y = stop.y = dim.height - offsets.b; + titleRotation = (taTitleOrientation && taTitleOrientation == "axis") ? 180 : 0; + titlePos.y = dim.height - offsets.b + titleOffset - (titleRotation ? tsize : 0); + }else{ + start.y = stop.y = offsets.t; + titleRotation = (taTitleOrientation && taTitleOrientation == "away") ? 180 : 0; + titlePos.y = offsets.t - titleOffset + (titleRotation ? 0 : tsize); + tickVector.y = -1; + anchorOffset.y = -anchorOffset.y; + switch(labelAlign){ + case "start": + labelAlign = "end"; + break; + case "end": + labelAlign = "start"; + break; + case "middle": + labelOffset.y -= size; + break; + } + } + } + + // render shapes + + this.cleanGroup(); + + try{ + var s = this.group, + c = this.scaler, + t = this.ticks, + canLabel, + f = lin.getTransformerFromModel(this.scaler), + // GFX Canvas now supports labels, so let's _not_ fallback to HTML anymore on canvas, just use + // HTML labels if explicitly asked + no rotation + no IE + no Opera + labelType = (!o.title || !titleRotation) && !rotation && this.opt.htmlLabels && !has("ie") && !has("opera") ? "html" : "gfx", + dx = tickVector.x * taMajorTick.length, + dy = tickVector.y * taMajorTick.length; + + s.createLine({ + x1: start.x, + y1: start.y, + x2: stop.x, + y2: stop.y + }).setStroke(taStroke); + + //create axis title + if(o.title){ + var axisTitle = acommon.createText[labelType]( + this.chart, + s, + titlePos.x, + titlePos.y, + "middle", + o.title, + taTitleFont, + taTitleFontColor + ); + if(labelType == "html"){ + this.htmlElements.push(axisTitle); + }else{ + //as soon as rotation is provided, labelType won't be "html" + //rotate gfx labels + axisTitle.setTransform(g.matrix.rotategAt(titleRotation, titlePos.x, titlePos.y)); + } + } + + // go out nicely instead of try/catch + if(t==null){ + this.dirty = false; + return this; + } + + arr.forEach(t.major, function(tick){ + var offset = f(tick.value), elem, + x = start.x + axisVector.x * offset, + y = start.y + axisVector.y * offset; + this.createLine(s, { + x1: x, y1: y, + x2: x + dx, + y2: y + dy + }).setStroke(taMajorTick); + if(tick.label){ + var label = o.maxLabelCharCount ? this.getTextWithLimitCharCount(tick.label, taFont, o.maxLabelCharCount) : { + text: tick.label, + truncated: false + }; + label = o.maxLabelSize ? this.getTextWithLimitLength(label.text, taFont, o.maxLabelSize, label.truncated) : label; + elem = this.createText(labelType, + s, + x + dx + anchorOffset.x + (rotation ? 0 : labelOffset.x), + y + dy + anchorOffset.y + (rotation ? 0 : labelOffset.y), + labelAlign, + label.text, + taFont, + taFontColor + //this._cachedLabelWidth + ); + + // if bidi support was required, the textDir is "auto" and truncation + // took place, we need to update the dir of the element for cases as: + // Fool label: 111111W (W for bidi character) + // truncated label: 11... + // in this case for auto textDir the dir will be "ltr" which is wrong. + if(this.chart.truncateBidi && label.truncated){ + this.chart.truncateBidi(elem, tick.label, labelType); + } + label.truncated && this.labelTooltip(elem, this.chart, tick.label, label.text, taFont, labelType); + if(labelType == "html"){ + this.htmlElements.push(elem); + }else if(rotation){ + elem.setTransform([ + {dx: labelOffset.x, dy: labelOffset.y}, + g.matrix.rotategAt( + rotation, + x + dx + anchorOffset.x, + y + dy + anchorOffset.y + ) + ]); + } + } + }, this); + + dx = tickVector.x * taMinorTick.length; + dy = tickVector.y * taMinorTick.length; + canLabel = c.minMinorStep <= c.minor.tick * c.bounds.scale; + arr.forEach(t.minor, function(tick){ + var offset = f(tick.value), elem, + x = start.x + axisVector.x * offset, + y = start.y + axisVector.y * offset; + this.createLine(s, { + x1: x, y1: y, + x2: x + dx, + y2: y + dy + }).setStroke(taMinorTick); + if(canLabel && tick.label){ + var label = o.maxLabelCharCount ? this.getTextWithLimitCharCount(tick.label, taFont, o.maxLabelCharCount) : { + text: tick.label, + truncated: false + }; + label = o.maxLabelSize ? this.getTextWithLimitLength(label.text, taFont, o.maxLabelSize, label.truncated) : label; + elem = this.createText(labelType, + s, + x + dx + anchorOffset.x + (rotation ? 0 : labelOffset.x), + y + dy + anchorOffset.y + (rotation ? 0 : labelOffset.y), + labelAlign, + label.text, + taFont, + taFontColor + //this._cachedLabelWidth + ); + // if bidi support was required, the textDir is "auto" and truncation + // took place, we need to update the dir of the element for cases as: + // Fool label: 111111W (W for bidi character) + // truncated label: 11... + // in this case for auto textDir the dir will be "ltr" which is wrong. + if(this.chart.getTextDir && label.truncated){ + this.chart.truncateBidi(elem, tick.label, labelType); + } + label.truncated && this.labelTooltip(elem, this.chart, tick.label, label.text, taFont, labelType); + if(labelType == "html"){ + this.htmlElements.push(elem); + }else if(rotation){ + elem.setTransform([ + {dx: labelOffset.x, dy: labelOffset.y}, + g.matrix.rotategAt( + rotation, + x + dx + anchorOffset.x, + y + dy + anchorOffset.y + ) + ]); + } + } + }, this); + + dx = tickVector.x * taMicroTick.length; + dy = tickVector.y * taMicroTick.length; + arr.forEach(t.micro, function(tick){ + var offset = f(tick.value), elem, + x = start.x + axisVector.x * offset, + y = start.y + axisVector.y * offset; + this.createLine(s, { + x1: x, y1: y, + x2: x + dx, + y2: y + dy + }).setStroke(taMicroTick); + }, this); + }catch(e){ + // squelch + } + + this.dirty = false; + return this; // dojox.charting.axis2d.Default + }, + labelTooltip: function(elem, chart, label, truncatedLabel, font, elemType){ + var modules = ["dijit/Tooltip"]; + var aroundRect = {type: "rect"}, position = ["above", "below"], + fontWidth = g._base._getTextBox(truncatedLabel, {font: font}).w || 0, + fontHeight = font ? g.normalizedLength(g.splitFontString(font).size) : 0; + if(elemType == "html"){ + lang.mixin(aroundRect, html.coords(elem.firstChild, true)); + aroundRect.width = Math.ceil(fontWidth); + aroundRect.height = Math.ceil(fontHeight); + this._events.push({ + shape: dojo, + handle: connect.connect(elem.firstChild, "onmouseover", this, function(e){ + require(modules, function(Tooltip){ + Tooltip.show(label, aroundRect, position); + }); + }) + }); + this._events.push({ + shape: dojo, + handle: connect.connect(elem.firstChild, "onmouseout", this, function(e){ + require(modules, function(Tooltip){ + Tooltip.hide(aroundRect); + }); + }) + }); + }else{ + var shp = elem.getShape(), + lt = html.coords(chart.node, true); + aroundRect = lang.mixin(aroundRect, { + x: shp.x - fontWidth / 2, + y: shp.y + }); + aroundRect.x += lt.x; + aroundRect.y += lt.y; + aroundRect.x = Math.round(aroundRect.x); + aroundRect.y = Math.round(aroundRect.y); + aroundRect.width = Math.ceil(fontWidth); + aroundRect.height = Math.ceil(fontHeight); + this._events.push({ + shape: elem, + handle: elem.connect("onmouseenter", this, function(e){ + require(modules, function(Tooltip){ + Tooltip.show(label, aroundRect, position); + }); + }) + }); + this._events.push({ + shape: elem, + handle: elem.connect("onmouseleave", this, function(e){ + require(modules, function(Tooltip){ + Tooltip.hide(aroundRect); + }); + }) + }); + } + } + }); +}); + +}, +'dojox/charting/plot2d/ClusteredBars':function(){ +define("dojox/charting/plot2d/ClusteredBars", ["dojo/_base/lang", "dojo/_base/array", "dojo/_base/declare", "./Bars", "./common", + "dojox/lang/functional", "dojox/lang/functional/reversed", "dojox/lang/utils"], + function(lang, arr, declare, Bars, dc, df, dfr, du){ +/*===== +var Bars = dojox.charting.plot2d.Bars; +=====*/ + + var purgeGroup = dfr.lambda("item.purgeGroup()"); + + return declare("dojox.charting.plot2d.ClusteredBars", Bars, { + // summary: + // A plot representing grouped or clustered bars (horizontal bars) + render: function(dim, offsets){ + // summary: + // Run the calculations for any axes for this plot. + // dim: Object + // An object in the form of { width, height } + // offsets: Object + // An object of the form { l, r, t, b}. + // returns: dojox.charting.plot2d.ClusteredBars + // A reference to this plot for functional chaining. + if(this.zoom && !this.isDataDirty()){ + return this.performZoom(dim, offsets); + } + this.resetEvents(); + this.dirty = this.isDirty(); + if(this.dirty){ + arr.forEach(this.series, purgeGroup); + this._eventSeries = {}; + this.cleanGroup(); + var s = this.group; + df.forEachRev(this.series, function(item){ item.cleanGroup(s); }); + } + var t = this.chart.theme, f, gap, height, thickness, + ht = this._hScaler.scaler.getTransformerFromModel(this._hScaler), + vt = this._vScaler.scaler.getTransformerFromModel(this._vScaler), + baseline = Math.max(0, this._hScaler.bounds.lower), + baselineWidth = ht(baseline), + events = this.events(); + f = dc.calculateBarSize(this._vScaler.bounds.scale, this.opt, this.series.length); + gap = f.gap; + height = thickness = f.size; + for(var i = this.series.length - 1; i >= 0; --i){ + var run = this.series[i], shift = thickness * (this.series.length - i - 1); + if(!this.dirty && !run.dirty){ + t.skip(); + this._reconnectEvents(run.name); + continue; + } + run.cleanGroup(); + var theme = t.next("bar", [this.opt, run]), s = run.group, + eventSeries = new Array(run.data.length); + for(var j = 0; j < run.data.length; ++j){ + var value = run.data[j]; + if(value !== null){ + var v = typeof value == "number" ? value : value.y, + hv = ht(v), + width = hv - baselineWidth, + w = Math.abs(width), + finalTheme = typeof value != "number" ? + t.addMixin(theme, "bar", value, true) : + t.post(theme, "bar"); + if(w >= 0 && height >= 1){ + var rect = { + x: offsets.l + (v < baseline ? hv : baselineWidth), + y: dim.height - offsets.b - vt(j + 1.5) + gap + shift, + width: w, height: height + }; + var specialFill = this._plotFill(finalTheme.series.fill, dim, offsets); + specialFill = this._shapeFill(specialFill, rect); + var shape = s.createRect(rect).setFill(specialFill).setStroke(finalTheme.series.stroke); + run.dyn.fill = shape.getFill(); + run.dyn.stroke = shape.getStroke(); + if(events){ + var o = { + element: "bar", + index: j, + run: run, + shape: shape, + x: v, + y: j + 1.5 + }; + this._connectEvents(o); + eventSeries[j] = o; + } + if(this.animate){ + this._animateBar(shape, offsets.l + baselineWidth, -width); + } + } + } + } + this._eventSeries[run.name] = eventSeries; + run.dirty = false; + } + this.dirty = false; + return this; // dojox.charting.plot2d.ClusteredBars + } + }); +}); + +}, +'dojox/charting/action2d/MoveSlice':function(){ +define("dojox/charting/action2d/MoveSlice", ["dojo/_base/connect", "dojo/_base/declare", "./PlotAction", "dojo/fx/easing", "dojox/gfx/matrix", + "dojox/gfx/fx", "dojox/lang/functional", "dojox/lang/functional/scan", "dojox/lang/functional/fold"], + function(hub, declare, PlotAction, dfe, m, gf, df, dfs, dff){ + + /*===== + dojo.declare("dojox.charting.action2d.__MoveSliceCtorArgs", dojox.charting.action2d.__PlotActionCtorArgs, { + // summary: + // Additional arguments for highlighting actions. + + // scale: Number? + // The amount to scale the pie slice. Default is 1.05. + scale: 1.05, + + // shift: Number? + // The amount in pixels to shift the pie slice. Default is 7. + shift: 7 + }); + var PlotAction = dojox.charting.action2d.PlotAction; + =====*/ + + var DEFAULT_SCALE = 1.05, + DEFAULT_SHIFT = 7; // px + + return declare("dojox.charting.action2d.MoveSlice", PlotAction, { + // summary: + // Create an action for a pie chart that moves and scales a pie slice. + + // the data description block for the widget parser + defaultParams: { + duration: 400, // duration of the action in ms + easing: dfe.backOut, // easing for the action + scale: DEFAULT_SCALE, // scale of magnification + shift: DEFAULT_SHIFT // shift of the slice + }, + optionalParams: {}, // no optional parameters + + constructor: function(chart, plot, kwArgs){ + // summary: + // Create the slice moving action and connect it to the plot. + // chart: dojox.charting.Chart + // The chart this action belongs to. + // plot: String? + // The plot this action is attached to. If not passed, "default" is assumed. + // kwArgs: dojox.charting.action2d.__MoveSliceCtorArgs? + // Optional keyword arguments object for setting parameters. + if(!kwArgs){ kwArgs = {}; } + this.scale = typeof kwArgs.scale == "number" ? kwArgs.scale : DEFAULT_SCALE; + this.shift = typeof kwArgs.shift == "number" ? kwArgs.shift : DEFAULT_SHIFT; + + this.connect(); + }, + + process: function(o){ + // summary: + // Process the action on the given object. + // o: dojox.gfx.Shape + // The object on which to process the slice moving action. + if(!o.shape || o.element != "slice" || !(o.type in this.overOutEvents)){ return; } + + if(!this.angles){ + // calculate the running total of slice angles + var startAngle = m._degToRad(o.plot.opt.startAngle); + if(typeof o.run.data[0] == "number"){ + this.angles = df.map(df.scanl(o.run.data, "+", startAngle), + "* 2 * Math.PI / this", df.foldl(o.run.data, "+", 0)); + }else{ + this.angles = df.map(df.scanl(o.run.data, "a + b.y", startAngle), + "* 2 * Math.PI / this", df.foldl(o.run.data, "a + b.y", 0)); + } + } + + var index = o.index, anim, startScale, endScale, startOffset, endOffset, + angle = (this.angles[index] + this.angles[index + 1]) / 2, + rotateTo0 = m.rotateAt(-angle, o.cx, o.cy), + rotateBack = m.rotateAt( angle, o.cx, o.cy); + + anim = this.anim[index]; + + if(anim){ + anim.action.stop(true); + }else{ + this.anim[index] = anim = {}; + } + + if(o.type == "onmouseover"){ + startOffset = 0; + endOffset = this.shift; + startScale = 1; + endScale = this.scale; + }else{ + startOffset = this.shift; + endOffset = 0; + startScale = this.scale; + endScale = 1; + } + + anim.action = gf.animateTransform({ + shape: o.shape, + duration: this.duration, + easing: this.easing, + transform: [ + rotateBack, + {name: "translate", start: [startOffset, 0], end: [endOffset, 0]}, + {name: "scaleAt", start: [startScale, o.cx, o.cy], end: [endScale, o.cx, o.cy]}, + rotateTo0 + ] + }); + + if(o.type == "onmouseout"){ + hub.connect(anim.action, "onEnd", this, function(){ + delete this.anim[index]; + }); + } + anim.action.play(); + }, + + reset: function(){ + delete this.angles; + } + }); +}); + +}, +'dojo/colors':function(){ +define(["./_base/kernel", "./_base/lang", "./_base/Color", "./_base/array"], function(dojo, lang, Color, ArrayUtil) { + // module: + // dojo/colors + // summary: + // TODOC + + var ColorExt = lang.getObject("dojo.colors", true); + +//TODO: this module appears to break naming conventions + +/*===== + lang.mixin(dojo, { + colors: { + // summary: Color utilities, extending Base dojo.Color + } + }); +=====*/ + + // this is a standard conversion prescribed by the CSS3 Color Module + var hue2rgb = function(m1, m2, h){ + if(h < 0){ ++h; } + if(h > 1){ --h; } + var h6 = 6 * h; + if(h6 < 1){ return m1 + (m2 - m1) * h6; } + if(2 * h < 1){ return m2; } + if(3 * h < 2){ return m1 + (m2 - m1) * (2 / 3 - h) * 6; } + return m1; + }; + // Override base Color.fromRgb with the impl in this module + dojo.colorFromRgb = Color.fromRgb = function(/*String*/ color, /*dojo.Color?*/ obj){ + // summary: + // get rgb(a) array from css-style color declarations + // description: + // this function can handle all 4 CSS3 Color Module formats: rgb, + // rgba, hsl, hsla, including rgb(a) with percentage values. + var m = color.toLowerCase().match(/^(rgba?|hsla?)\(([\s\.\-,%0-9]+)\)/); + if(m){ + var c = m[2].split(/\s*,\s*/), l = c.length, t = m[1], a; + if((t == "rgb" && l == 3) || (t == "rgba" && l == 4)){ + var r = c[0]; + if(r.charAt(r.length - 1) == "%"){ + // 3 rgb percentage values + a = ArrayUtil.map(c, function(x){ + return parseFloat(x) * 2.56; + }); + if(l == 4){ a[3] = c[3]; } + return Color.fromArray(a, obj); // dojo.Color + } + return Color.fromArray(c, obj); // dojo.Color + } + if((t == "hsl" && l == 3) || (t == "hsla" && l == 4)){ + // normalize hsl values + var H = ((parseFloat(c[0]) % 360) + 360) % 360 / 360, + S = parseFloat(c[1]) / 100, + L = parseFloat(c[2]) / 100, + // calculate rgb according to the algorithm + // recommended by the CSS3 Color Module + m2 = L <= 0.5 ? L * (S + 1) : L + S - L * S, + m1 = 2 * L - m2; + a = [ + hue2rgb(m1, m2, H + 1 / 3) * 256, + hue2rgb(m1, m2, H) * 256, + hue2rgb(m1, m2, H - 1 / 3) * 256, + 1 + ]; + if(l == 4){ a[3] = c[3]; } + return Color.fromArray(a, obj); // dojo.Color + } + } + return null; // dojo.Color + }; + + var confine = function(c, low, high){ + // summary: + // sanitize a color component by making sure it is a number, + // and clamping it to valid values + c = Number(c); + return isNaN(c) ? high : c < low ? low : c > high ? high : c; // Number + }; + + Color.prototype.sanitize = function(){ + // summary: makes sure that the object has correct attributes + var t = this; + t.r = Math.round(confine(t.r, 0, 255)); + t.g = Math.round(confine(t.g, 0, 255)); + t.b = Math.round(confine(t.b, 0, 255)); + t.a = confine(t.a, 0, 1); + return this; // dojo.Color + }; + + ColorExt.makeGrey = Color.makeGrey = function(/*Number*/ g, /*Number?*/ a){ + // summary: creates a greyscale color with an optional alpha + return Color.fromArray([g, g, g, a]); // dojo.Color + }; + + // mixin all CSS3 named colors not already in _base, along with SVG 1.0 variant spellings + lang.mixin(Color.named, { + "aliceblue": [240,248,255], + "antiquewhite": [250,235,215], + "aquamarine": [127,255,212], + "azure": [240,255,255], + "beige": [245,245,220], + "bisque": [255,228,196], + "blanchedalmond": [255,235,205], + "blueviolet": [138,43,226], + "brown": [165,42,42], + "burlywood": [222,184,135], + "cadetblue": [95,158,160], + "chartreuse": [127,255,0], + "chocolate": [210,105,30], + "coral": [255,127,80], + "cornflowerblue": [100,149,237], + "cornsilk": [255,248,220], + "crimson": [220,20,60], + "cyan": [0,255,255], + "darkblue": [0,0,139], + "darkcyan": [0,139,139], + "darkgoldenrod": [184,134,11], + "darkgray": [169,169,169], + "darkgreen": [0,100,0], + "darkgrey": [169,169,169], + "darkkhaki": [189,183,107], + "darkmagenta": [139,0,139], + "darkolivegreen": [85,107,47], + "darkorange": [255,140,0], + "darkorchid": [153,50,204], + "darkred": [139,0,0], + "darksalmon": [233,150,122], + "darkseagreen": [143,188,143], + "darkslateblue": [72,61,139], + "darkslategray": [47,79,79], + "darkslategrey": [47,79,79], + "darkturquoise": [0,206,209], + "darkviolet": [148,0,211], + "deeppink": [255,20,147], + "deepskyblue": [0,191,255], + "dimgray": [105,105,105], + "dimgrey": [105,105,105], + "dodgerblue": [30,144,255], + "firebrick": [178,34,34], + "floralwhite": [255,250,240], + "forestgreen": [34,139,34], + "gainsboro": [220,220,220], + "ghostwhite": [248,248,255], + "gold": [255,215,0], + "goldenrod": [218,165,32], + "greenyellow": [173,255,47], + "grey": [128,128,128], + "honeydew": [240,255,240], + "hotpink": [255,105,180], + "indianred": [205,92,92], + "indigo": [75,0,130], + "ivory": [255,255,240], + "khaki": [240,230,140], + "lavender": [230,230,250], + "lavenderblush": [255,240,245], + "lawngreen": [124,252,0], + "lemonchiffon": [255,250,205], + "lightblue": [173,216,230], + "lightcoral": [240,128,128], + "lightcyan": [224,255,255], + "lightgoldenrodyellow": [250,250,210], + "lightgray": [211,211,211], + "lightgreen": [144,238,144], + "lightgrey": [211,211,211], + "lightpink": [255,182,193], + "lightsalmon": [255,160,122], + "lightseagreen": [32,178,170], + "lightskyblue": [135,206,250], + "lightslategray": [119,136,153], + "lightslategrey": [119,136,153], + "lightsteelblue": [176,196,222], + "lightyellow": [255,255,224], + "limegreen": [50,205,50], + "linen": [250,240,230], + "magenta": [255,0,255], + "mediumaquamarine": [102,205,170], + "mediumblue": [0,0,205], + "mediumorchid": [186,85,211], + "mediumpurple": [147,112,219], + "mediumseagreen": [60,179,113], + "mediumslateblue": [123,104,238], + "mediumspringgreen": [0,250,154], + "mediumturquoise": [72,209,204], + "mediumvioletred": [199,21,133], + "midnightblue": [25,25,112], + "mintcream": [245,255,250], + "mistyrose": [255,228,225], + "moccasin": [255,228,181], + "navajowhite": [255,222,173], + "oldlace": [253,245,230], + "olivedrab": [107,142,35], + "orange": [255,165,0], + "orangered": [255,69,0], + "orchid": [218,112,214], + "palegoldenrod": [238,232,170], + "palegreen": [152,251,152], + "paleturquoise": [175,238,238], + "palevioletred": [219,112,147], + "papayawhip": [255,239,213], + "peachpuff": [255,218,185], + "peru": [205,133,63], + "pink": [255,192,203], + "plum": [221,160,221], + "powderblue": [176,224,230], + "rosybrown": [188,143,143], + "royalblue": [65,105,225], + "saddlebrown": [139,69,19], + "salmon": [250,128,114], + "sandybrown": [244,164,96], + "seagreen": [46,139,87], + "seashell": [255,245,238], + "sienna": [160,82,45], + "skyblue": [135,206,235], + "slateblue": [106,90,205], + "slategray": [112,128,144], + "slategrey": [112,128,144], + "snow": [255,250,250], + "springgreen": [0,255,127], + "steelblue": [70,130,180], + "tan": [210,180,140], + "thistle": [216,191,216], + "tomato": [255,99,71], + "turquoise": [64,224,208], + "violet": [238,130,238], + "wheat": [245,222,179], + "whitesmoke": [245,245,245], + "yellowgreen": [154,205,50] + }); + + return Color; +}); + +}, +'dijit/Tooltip':function(){ +require({cache:{ +'url:dijit/templates/Tooltip.html':"<div class=\"dijitTooltip dijitTooltipLeft\" id=\"dojoTooltip\"\n\t><div class=\"dijitTooltipContainer dijitTooltipContents\" data-dojo-attach-point=\"containerNode\" role='alert'></div\n\t><div class=\"dijitTooltipConnector\" data-dojo-attach-point=\"connectorNode\"></div\n></div>\n"}}); +define("dijit/Tooltip", [ + "dojo/_base/array", // array.forEach array.indexOf array.map + "dojo/_base/declare", // declare + "dojo/_base/fx", // fx.fadeIn fx.fadeOut + "dojo/dom", // dom.byId + "dojo/dom-class", // domClass.add + "dojo/dom-geometry", // domGeometry.getMarginBox domGeometry.position + "dojo/dom-style", // domStyle.set, domStyle.get + "dojo/_base/lang", // lang.hitch lang.isArrayLike + "dojo/_base/sniff", // has("ie") + "dojo/_base/window", // win.body + "./_base/manager", // manager.defaultDuration + "./place", + "./_Widget", + "./_TemplatedMixin", + "./BackgroundIframe", + "dojo/text!./templates/Tooltip.html", + "." // sets dijit.showTooltip etc. for back-compat +], function(array, declare, fx, dom, domClass, domGeometry, domStyle, lang, has, win, + manager, place, _Widget, _TemplatedMixin, BackgroundIframe, template, dijit){ + +/*===== + var _Widget = dijit._Widget; + var BackgroundIframe = dijit.BackgroundIframe; + var _TemplatedMixin = dijit._TemplatedMixin; +=====*/ + + // module: + // dijit/Tooltip + // summary: + // Defines dijit.Tooltip widget (to display a tooltip), showTooltip()/hideTooltip(), and _MasterTooltip + + + var MasterTooltip = declare("dijit._MasterTooltip", [_Widget, _TemplatedMixin], { + // summary: + // Internal widget that holds the actual tooltip markup, + // which occurs once per page. + // Called by Tooltip widgets which are just containers to hold + // the markup + // tags: + // protected + + // duration: Integer + // Milliseconds to fade in/fade out + duration: manager.defaultDuration, + + templateString: template, + + postCreate: function(){ + win.body().appendChild(this.domNode); + + this.bgIframe = new BackgroundIframe(this.domNode); + + // Setup fade-in and fade-out functions. + this.fadeIn = fx.fadeIn({ node: this.domNode, duration: this.duration, onEnd: lang.hitch(this, "_onShow") }); + this.fadeOut = fx.fadeOut({ node: this.domNode, duration: this.duration, onEnd: lang.hitch(this, "_onHide") }); + }, + + show: function(innerHTML, aroundNode, position, rtl, textDir){ + // summary: + // Display tooltip w/specified contents to right of specified node + // (To left if there's no space on the right, or if rtl == true) + // innerHTML: String + // Contents of the tooltip + // aroundNode: DomNode || dijit.__Rectangle + // Specifies that tooltip should be next to this node / area + // position: String[]? + // List of positions to try to position tooltip (ex: ["right", "above"]) + // rtl: Boolean? + // Corresponds to `WidgetBase.dir` attribute, where false means "ltr" and true + // means "rtl"; specifies GUI direction, not text direction. + // textDir: String? + // Corresponds to `WidgetBase.textdir` attribute; specifies direction of text. + + + if(this.aroundNode && this.aroundNode === aroundNode && this.containerNode.innerHTML == innerHTML){ + return; + } + + // reset width; it may have been set by orient() on a previous tooltip show() + this.domNode.width = "auto"; + + if(this.fadeOut.status() == "playing"){ + // previous tooltip is being hidden; wait until the hide completes then show new one + this._onDeck=arguments; + return; + } + this.containerNode.innerHTML=innerHTML; + + this.set("textDir", textDir); + this.containerNode.align = rtl? "right" : "left"; //fix the text alignment + + var pos = place.around(this.domNode, aroundNode, + position && position.length ? position : Tooltip.defaultPosition, !rtl, lang.hitch(this, "orient")); + + // Position the tooltip connector for middle alignment. + // This could not have been done in orient() since the tooltip wasn't positioned at that time. + var aroundNodeCoords = pos.aroundNodePos; + if(pos.corner.charAt(0) == 'M' && pos.aroundCorner.charAt(0) == 'M'){ + this.connectorNode.style.top = aroundNodeCoords.y + ((aroundNodeCoords.h - this.connectorNode.offsetHeight) >> 1) - pos.y + "px"; + this.connectorNode.style.left = ""; + }else if(pos.corner.charAt(1) == 'M' && pos.aroundCorner.charAt(1) == 'M'){ + this.connectorNode.style.left = aroundNodeCoords.x + ((aroundNodeCoords.w - this.connectorNode.offsetWidth) >> 1) - pos.x + "px"; + } + + // show it + domStyle.set(this.domNode, "opacity", 0); + this.fadeIn.play(); + this.isShowingNow = true; + this.aroundNode = aroundNode; + }, + + orient: function(/*DomNode*/ node, /*String*/ aroundCorner, /*String*/ tooltipCorner, /*Object*/ spaceAvailable, /*Object*/ aroundNodeCoords){ + // summary: + // Private function to set CSS for tooltip node based on which position it's in. + // This is called by the dijit popup code. It will also reduce the tooltip's + // width to whatever width is available + // tags: + // protected + this.connectorNode.style.top = ""; //reset to default + + //Adjust the spaceAvailable width, without changing the spaceAvailable object + var tooltipSpaceAvaliableWidth = spaceAvailable.w - this.connectorNode.offsetWidth; + + node.className = "dijitTooltip " + + { + "MR-ML": "dijitTooltipRight", + "ML-MR": "dijitTooltipLeft", + "TM-BM": "dijitTooltipAbove", + "BM-TM": "dijitTooltipBelow", + "BL-TL": "dijitTooltipBelow dijitTooltipABLeft", + "TL-BL": "dijitTooltipAbove dijitTooltipABLeft", + "BR-TR": "dijitTooltipBelow dijitTooltipABRight", + "TR-BR": "dijitTooltipAbove dijitTooltipABRight", + "BR-BL": "dijitTooltipRight", + "BL-BR": "dijitTooltipLeft" + }[aroundCorner + "-" + tooltipCorner]; + + // reduce tooltip's width to the amount of width available, so that it doesn't overflow screen + this.domNode.style.width = "auto"; + var size = domGeometry.getContentBox(this.domNode); + + var width = Math.min((Math.max(tooltipSpaceAvaliableWidth,1)), size.w); + var widthWasReduced = width < size.w; + + this.domNode.style.width = width+"px"; + + //Adjust width for tooltips that have a really long word or a nowrap setting + if(widthWasReduced){ + this.containerNode.style.overflow = "auto"; //temp change to overflow to detect if our tooltip needs to be wider to support the content + var scrollWidth = this.containerNode.scrollWidth; + this.containerNode.style.overflow = "visible"; //change it back + if(scrollWidth > width){ + scrollWidth = scrollWidth + domStyle.get(this.domNode,"paddingLeft") + domStyle.get(this.domNode,"paddingRight"); + this.domNode.style.width = scrollWidth + "px"; + } + } + + // Reposition the tooltip connector. + if(tooltipCorner.charAt(0) == 'B' && aroundCorner.charAt(0) == 'B'){ + var mb = domGeometry.getMarginBox(node); + var tooltipConnectorHeight = this.connectorNode.offsetHeight; + if(mb.h > spaceAvailable.h){ + // The tooltip starts at the top of the page and will extend past the aroundNode + var aroundNodePlacement = spaceAvailable.h - ((aroundNodeCoords.h + tooltipConnectorHeight) >> 1); + this.connectorNode.style.top = aroundNodePlacement + "px"; + this.connectorNode.style.bottom = ""; + }else{ + // Align center of connector with center of aroundNode, except don't let bottom + // of connector extend below bottom of tooltip content, or top of connector + // extend past top of tooltip content + this.connectorNode.style.bottom = Math.min( + Math.max(aroundNodeCoords.h/2 - tooltipConnectorHeight/2, 0), + mb.h - tooltipConnectorHeight) + "px"; + this.connectorNode.style.top = ""; + } + }else{ + // reset the tooltip back to the defaults + this.connectorNode.style.top = ""; + this.connectorNode.style.bottom = ""; + } + + return Math.max(0, size.w - tooltipSpaceAvaliableWidth); + }, + + _onShow: function(){ + // summary: + // Called at end of fade-in operation + // tags: + // protected + if(has("ie")){ + // the arrow won't show up on a node w/an opacity filter + this.domNode.style.filter=""; + } + }, + + hide: function(aroundNode){ + // summary: + // Hide the tooltip + + if(this._onDeck && this._onDeck[1] == aroundNode){ + // this hide request is for a show() that hasn't even started yet; + // just cancel the pending show() + this._onDeck=null; + }else if(this.aroundNode === aroundNode){ + // this hide request is for the currently displayed tooltip + this.fadeIn.stop(); + this.isShowingNow = false; + this.aroundNode = null; + this.fadeOut.play(); + }else{ + // just ignore the call, it's for a tooltip that has already been erased + } + }, + + _onHide: function(){ + // summary: + // Called at end of fade-out operation + // tags: + // protected + + this.domNode.style.cssText=""; // to position offscreen again + this.containerNode.innerHTML=""; + if(this._onDeck){ + // a show request has been queued up; do it now + this.show.apply(this, this._onDeck); + this._onDeck=null; + } + }, + + _setAutoTextDir: function(/*Object*/node){ + // summary: + // Resolve "auto" text direction for children nodes + // tags: + // private + + this.applyTextDir(node, has("ie") ? node.outerText : node.textContent); + array.forEach(node.children, function(child){this._setAutoTextDir(child); }, this); + }, + + _setTextDirAttr: function(/*String*/ textDir){ + // summary: + // Setter for textDir. + // description: + // Users shouldn't call this function; they should be calling + // set('textDir', value) + // tags: + // private + + this._set("textDir", typeof textDir != 'undefined'? textDir : ""); + if (textDir == "auto"){ + this._setAutoTextDir(this.containerNode); + }else{ + this.containerNode.dir = this.textDir; + } + } + }); + + dijit.showTooltip = function(innerHTML, aroundNode, position, rtl, textDir){ + // summary: + // Static method to display tooltip w/specified contents in specified position. + // See description of dijit.Tooltip.defaultPosition for details on position parameter. + // If position is not specified then dijit.Tooltip.defaultPosition is used. + // innerHTML: String + // Contents of the tooltip + // aroundNode: dijit.__Rectangle + // Specifies that tooltip should be next to this node / area + // position: String[]? + // List of positions to try to position tooltip (ex: ["right", "above"]) + // rtl: Boolean? + // Corresponds to `WidgetBase.dir` attribute, where false means "ltr" and true + // means "rtl"; specifies GUI direction, not text direction. + // textDir: String? + // Corresponds to `WidgetBase.textdir` attribute; specifies direction of text. + if(!Tooltip._masterTT){ dijit._masterTT = Tooltip._masterTT = new MasterTooltip(); } + return Tooltip._masterTT.show(innerHTML, aroundNode, position, rtl, textDir); + }; + + dijit.hideTooltip = function(aroundNode){ + // summary: + // Static method to hide the tooltip displayed via showTooltip() + return Tooltip._masterTT && Tooltip._masterTT.hide(aroundNode); + }; + + var Tooltip = declare("dijit.Tooltip", _Widget, { + // summary: + // Pops up a tooltip (a help message) when you hover over a node. + + // label: String + // Text to display in the tooltip. + // Specified as innerHTML when creating the widget from markup. + label: "", + + // showDelay: Integer + // Number of milliseconds to wait after hovering over/focusing on the object, before + // the tooltip is displayed. + showDelay: 400, + + // connectId: String|String[] + // Id of domNode(s) to attach the tooltip to. + // When user hovers over specified dom node, the tooltip will appear. + connectId: [], + + // position: String[] + // See description of `dijit.Tooltip.defaultPosition` for details on position parameter. + position: [], + + _setConnectIdAttr: function(/*String|String[]*/ newId){ + // summary: + // Connect to specified node(s) + + // Remove connections to old nodes (if there are any) + array.forEach(this._connections || [], function(nested){ + array.forEach(nested, lang.hitch(this, "disconnect")); + }, this); + + // Make array of id's to connect to, excluding entries for nodes that don't exist yet, see startup() + this._connectIds = array.filter(lang.isArrayLike(newId) ? newId : (newId ? [newId] : []), + function(id){ return dom.byId(id); }); + + // Make connections + this._connections = array.map(this._connectIds, function(id){ + var node = dom.byId(id); + return [ + this.connect(node, "onmouseenter", "_onHover"), + this.connect(node, "onmouseleave", "_onUnHover"), + this.connect(node, "onfocus", "_onHover"), + this.connect(node, "onblur", "_onUnHover") + ]; + }, this); + + this._set("connectId", newId); + }, + + addTarget: function(/*DOMNODE || String*/ node){ + // summary: + // Attach tooltip to specified node if it's not already connected + + // TODO: remove in 2.0 and just use set("connectId", ...) interface + + var id = node.id || node; + if(array.indexOf(this._connectIds, id) == -1){ + this.set("connectId", this._connectIds.concat(id)); + } + }, + + removeTarget: function(/*DomNode || String*/ node){ + // summary: + // Detach tooltip from specified node + + // TODO: remove in 2.0 and just use set("connectId", ...) interface + + var id = node.id || node, // map from DOMNode back to plain id string + idx = array.indexOf(this._connectIds, id); + if(idx >= 0){ + // remove id (modifies original this._connectIds but that's OK in this case) + this._connectIds.splice(idx, 1); + this.set("connectId", this._connectIds); + } + }, + + buildRendering: function(){ + this.inherited(arguments); + domClass.add(this.domNode,"dijitTooltipData"); + }, + + startup: function(){ + this.inherited(arguments); + + // If this tooltip was created in a template, or for some other reason the specified connectId[s] + // didn't exist during the widget's initialization, then connect now. + var ids = this.connectId; + array.forEach(lang.isArrayLike(ids) ? ids : [ids], this.addTarget, this); + }, + + _onHover: function(/*Event*/ e){ + // summary: + // Despite the name of this method, it actually handles both hover and focus + // events on the target node, setting a timer to show the tooltip. + // tags: + // private + if(!this._showTimer){ + var target = e.target; + this._showTimer = setTimeout(lang.hitch(this, function(){this.open(target)}), this.showDelay); + } + }, + + _onUnHover: function(/*Event*/ /*===== e =====*/){ + // summary: + // Despite the name of this method, it actually handles both mouseleave and blur + // events on the target node, hiding the tooltip. + // tags: + // private + + // keep a tooltip open if the associated element still has focus (even though the + // mouse moved away) + if(this._focus){ return; } + + if(this._showTimer){ + clearTimeout(this._showTimer); + delete this._showTimer; + } + this.close(); + }, + + open: function(/*DomNode*/ target){ + // summary: + // Display the tooltip; usually not called directly. + // tags: + // private + + if(this._showTimer){ + clearTimeout(this._showTimer); + delete this._showTimer; + } + Tooltip.show(this.label || this.domNode.innerHTML, target, this.position, !this.isLeftToRight(), this.textDir); + + this._connectNode = target; + this.onShow(target, this.position); + }, + + close: function(){ + // summary: + // Hide the tooltip or cancel timer for show of tooltip + // tags: + // private + + if(this._connectNode){ + // if tooltip is currently shown + Tooltip.hide(this._connectNode); + delete this._connectNode; + this.onHide(); + } + if(this._showTimer){ + // if tooltip is scheduled to be shown (after a brief delay) + clearTimeout(this._showTimer); + delete this._showTimer; + } + }, + + onShow: function(/*===== target, position =====*/){ + // summary: + // Called when the tooltip is shown + // tags: + // callback + }, + + onHide: function(){ + // summary: + // Called when the tooltip is hidden + // tags: + // callback + }, + + uninitialize: function(){ + this.close(); + this.inherited(arguments); + } + }); + + Tooltip._MasterTooltip = MasterTooltip; // for monkey patching + Tooltip.show = dijit.showTooltip; // export function through module return value + Tooltip.hide = dijit.hideTooltip; // export function through module return value + + // dijit.Tooltip.defaultPosition: String[] + // This variable controls the position of tooltips, if the position is not specified to + // the Tooltip widget or *TextBox widget itself. It's an array of strings with the values + // possible for `dijit/place::around()`. The recommended values are: + // + // * before-centered: centers tooltip to the left of the anchor node/widget, or to the right + // in the case of RTL scripts like Hebrew and Arabic + // * after-centered: centers tooltip to the right of the anchor node/widget, or to the left + // in the case of RTL scripts like Hebrew and Arabic + // * above-centered: tooltip is centered above anchor node + // * below-centered: tooltip is centered above anchor node + // + // The list is positions is tried, in order, until a position is found where the tooltip fits + // within the viewport. + // + // Be careful setting this parameter. A value of "above-centered" may work fine until the user scrolls + // the screen so that there's no room above the target node. Nodes with drop downs, like + // DropDownButton or FilteringSelect, are especially problematic, in that you need to be sure + // that the drop down and tooltip don't overlap, even when the viewport is scrolled so that there + // is only room below (or above) the target node, but not both. + Tooltip.defaultPosition = ["after-centered", "before-centered"]; + + + return Tooltip; +}); + +}, +'dojox/charting/Element':function(){ +define("dojox/charting/Element", ["dojo/_base/lang", "dojo/_base/array", "dojo/dom-construct","dojo/_base/declare", "dojox/gfx", "dojox/gfx/utils", "dojox/gfx/shape"], + function(lang, arr, domConstruct, declare, gfx, utils, shape){ + + return declare("dojox.charting.Element", null, { + // summary: + // A base class that is used to build other elements of a chart, such as + // a series. + // chart: dojox.charting.Chart + // The parent chart for this element. + // group: dojox.gfx.Group + // The visual GFX group representing this element. + // htmlElement: Array + // Any DOMNodes used as a part of this element (such as HTML-based labels). + // dirty: Boolean + // A flag indicating whether or not this element needs to be rendered. + + chart: null, + group: null, + htmlElements: null, + dirty: true, + + constructor: function(chart){ + // summary: + // Creates a new charting element. + // chart: dojox.charting.Chart + // The chart that this element belongs to. + this.chart = chart; + this.group = null; + this.htmlElements = []; + this.dirty = true; + this.trailingSymbol = "..."; + this._events = []; + }, + createGroup: function(creator){ + // summary: + // Convenience function to create a new dojox.gfx.Group. + // creator: dojox.gfx.Surface? + // An optional surface in which to create this group. + // returns: dojox.charting.Element + // A reference to this object for functional chaining. + if(!creator){ creator = this.chart.surface; } + if(!this.group){ + this.group = creator.createGroup(); + } + return this; // dojox.charting.Element + }, + purgeGroup: function(){ + // summary: + // Clear any elements out of our group, and destroy the group. + // returns: dojox.charting.Element + // A reference to this object for functional chaining. + this.destroyHtmlElements(); + if(this.group){ + // since 1.7.x we need dispose shape otherwise there is a memoryleak + utils.forEach(this.group, function(child){ + shape.dispose(child); + }); + this.group.clear(); + this.group.removeShape(); + this.group = null; + } + this.dirty = true; + if(this._events.length){ + arr.forEach(this._events, function(item){ + item.shape.disconnect(item.handle); + }); + this._events = []; + } + return this; // dojox.charting.Element + }, + cleanGroup: function(creator){ + // summary: + // Clean any elements (HTML or GFX-based) out of our group, and create a new one. + // creator: dojox.gfx.Surface? + // An optional surface to work with. + // returns: dojox.charting.Element + // A reference to this object for functional chaining. + this.destroyHtmlElements(); + if(!creator){ creator = this.chart.surface; } + if(this.group){ + this.group.clear(); + }else{ + this.group = creator.createGroup(); + } + this.dirty = true; + return this; // dojox.charting.Element + }, + destroyHtmlElements: function(){ + // summary: + // Destroy any DOMNodes that may have been created as a part of this element. + if(this.htmlElements.length){ + arr.forEach(this.htmlElements, domConstruct.destroy); + this.htmlElements = []; + } + }, + destroy: function(){ + // summary: + // API addition to conform to the rest of the Dojo Toolkit's standard. + this.purgeGroup(); + }, + //text utilities + getTextWidth: function(s, font){ + return gfx._base._getTextBox(s, {font: font}).w || 0; + }, + getTextWithLimitLength: function(s, font, limitWidth, truncated){ + // summary: + // Get the truncated string based on the limited width in px(dichotomy algorithm) + // s: String? + // candidate text. + // font: String? + // text's font style. + // limitWidth: Number? + // text limited width in px. + // truncated: Boolean? + // whether the input text(s) has already been truncated. + // returns: Object + // { + // text: processed text, maybe truncated or not + // truncated: whether text has been truncated + // } + if (!s || s.length <= 0) { + return { + text: "", + truncated: truncated || false + }; + } + if(!limitWidth || limitWidth <= 0){ + return { + text: s, + truncated: truncated || false + }; + } + var delta = 2, + //golden section for dichotomy algorithm + trucPercentage = 0.618, + minStr = s.substring(0,1) + this.trailingSymbol, + minWidth = this.getTextWidth(minStr, font); + if (limitWidth <= minWidth) { + return { + text: minStr, + truncated: true + }; + } + var width = this.getTextWidth(s, font); + if(width <= limitWidth){ + return { + text: s, + truncated: truncated || false + }; + }else{ + var begin = 0, + end = s.length; + while(begin < end){ + if(end - begin <= delta ){ + while (this.getTextWidth(s.substring(0, begin) + this.trailingSymbol, font) > limitWidth) { + begin -= 1; + } + return { + text: (s.substring(0,begin) + this.trailingSymbol), + truncated: true + }; + } + var index = begin + Math.round((end - begin) * trucPercentage), + widthIntercepted = this.getTextWidth(s.substring(0, index), font); + if(widthIntercepted < limitWidth){ + begin = index; + end = end; + }else{ + begin = begin; + end = index; + } + } + } + }, + getTextWithLimitCharCount: function(s, font, wcLimit, truncated){ + // summary: + // Get the truncated string based on the limited character count(dichotomy algorithm) + // s: String? + // candidate text. + // font: String? + // text's font style. + // wcLimit: Number? + // text limited character count. + // truncated: Boolean? + // whether the input text(s) has already been truncated. + // returns: Object + // { + // text: processed text, maybe truncated or not + // truncated: whether text has been truncated + // } + if (!s || s.length <= 0) { + return { + text: "", + truncated: truncated || false + }; + } + if(!wcLimit || wcLimit <= 0 || s.length <= wcLimit){ + return { + text: s, + truncated: truncated || false + }; + } + return { + text: s.substring(0, wcLimit) + this.trailingSymbol, + truncated: true + }; + }, + // fill utilities + _plotFill: function(fill, dim, offsets){ + // process a plot-wide fill + if(!fill || !fill.type || !fill.space){ + return fill; + } + var space = fill.space; + switch(fill.type){ + case "linear": + if(space === "plot" || space === "shapeX" || space === "shapeY"){ + // clone a fill so we can modify properly directly + fill = gfx.makeParameters(gfx.defaultLinearGradient, fill); + fill.space = space; + // process dimensions + if(space === "plot" || space === "shapeX"){ + // process Y + var span = dim.height - offsets.t - offsets.b; + fill.y1 = offsets.t + span * fill.y1 / 100; + fill.y2 = offsets.t + span * fill.y2 / 100; + } + if(space === "plot" || space === "shapeY"){ + // process X + var span = dim.width - offsets.l - offsets.r; + fill.x1 = offsets.l + span * fill.x1 / 100; + fill.x2 = offsets.l + span * fill.x2 / 100; + } + } + break; + case "radial": + if(space === "plot"){ + // this one is used exclusively for scatter charts + // clone a fill so we can modify properly directly + fill = gfx.makeParameters(gfx.defaultRadialGradient, fill); + fill.space = space; + // process both dimensions + var spanX = dim.width - offsets.l - offsets.r, + spanY = dim.height - offsets.t - offsets.b; + fill.cx = offsets.l + spanX * fill.cx / 100; + fill.cy = offsets.t + spanY * fill.cy / 100; + fill.r = fill.r * Math.sqrt(spanX * spanX + spanY * spanY) / 200; + } + break; + case "pattern": + if(space === "plot" || space === "shapeX" || space === "shapeY"){ + // clone a fill so we can modify properly directly + fill = gfx.makeParameters(gfx.defaultPattern, fill); + fill.space = space; + // process dimensions + if(space === "plot" || space === "shapeX"){ + // process Y + var span = dim.height - offsets.t - offsets.b; + fill.y = offsets.t + span * fill.y / 100; + fill.height = span * fill.height / 100; + } + if(space === "plot" || space === "shapeY"){ + // process X + var span = dim.width - offsets.l - offsets.r; + fill.x = offsets.l + span * fill.x / 100; + fill.width = span * fill.width / 100; + } + } + break; + } + return fill; + }, + _shapeFill: function(fill, bbox){ + // process shape-specific fill + if(!fill || !fill.space){ + return fill; + } + var space = fill.space; + switch(fill.type){ + case "linear": + if(space === "shape" || space === "shapeX" || space === "shapeY"){ + // clone a fill so we can modify properly directly + fill = gfx.makeParameters(gfx.defaultLinearGradient, fill); + fill.space = space; + // process dimensions + if(space === "shape" || space === "shapeX"){ + // process X + var span = bbox.width; + fill.x1 = bbox.x + span * fill.x1 / 100; + fill.x2 = bbox.x + span * fill.x2 / 100; + } + if(space === "shape" || space === "shapeY"){ + // process Y + var span = bbox.height; + fill.y1 = bbox.y + span * fill.y1 / 100; + fill.y2 = bbox.y + span * fill.y2 / 100; + } + } + break; + case "radial": + if(space === "shape"){ + // this one is used exclusively for bubble charts and pie charts + // clone a fill so we can modify properly directly + fill = gfx.makeParameters(gfx.defaultRadialGradient, fill); + fill.space = space; + // process both dimensions + fill.cx = bbox.x + bbox.width / 2; + fill.cy = bbox.y + bbox.height / 2; + fill.r = fill.r * bbox.width / 200; + } + break; + case "pattern": + if(space === "shape" || space === "shapeX" || space === "shapeY"){ + // clone a fill so we can modify properly directly + fill = gfx.makeParameters(gfx.defaultPattern, fill); + fill.space = space; + // process dimensions + if(space === "shape" || space === "shapeX"){ + // process X + var span = bbox.width; + fill.x = bbox.x + span * fill.x / 100; + fill.width = span * fill.width / 100; + } + if(space === "shape" || space === "shapeY"){ + // process Y + var span = bbox.height; + fill.y = bbox.y + span * fill.y / 100; + fill.height = span * fill.height / 100; + } + } + break; + } + return fill; + }, + _pseudoRadialFill: function(fill, center, radius, start, end){ + // process pseudo-radial fills + if(!fill || fill.type !== "radial" || fill.space !== "shape"){ + return fill; + } + // clone and normalize fill + var space = fill.space; + fill = gfx.makeParameters(gfx.defaultRadialGradient, fill); + fill.space = space; + if(arguments.length < 4){ + // process both dimensions + fill.cx = center.x; + fill.cy = center.y; + fill.r = fill.r * radius / 100; + return fill; + } + // convert to a linear gradient + var angle = arguments.length < 5 ? start : (end + start) / 2; + return { + type: "linear", + x1: center.x, + y1: center.y, + x2: center.x + fill.r * radius * Math.cos(angle) / 100, + y2: center.y + fill.r * radius * Math.sin(angle) / 100, + colors: fill.colors + }; + return fill; + } + }); +}); + +}, +'dijit/_WidgetBase':function(){ +define("dijit/_WidgetBase", [ + "require", // require.toUrl + "dojo/_base/array", // array.forEach array.map + "dojo/aspect", + "dojo/_base/config", // config.blankGif + "dojo/_base/connect", // connect.connect + "dojo/_base/declare", // declare + "dojo/dom", // dom.byId + "dojo/dom-attr", // domAttr.set domAttr.remove + "dojo/dom-class", // domClass.add domClass.replace + "dojo/dom-construct", // domConstruct.create domConstruct.destroy domConstruct.place + "dojo/dom-geometry", // isBodyLtr + "dojo/dom-style", // domStyle.set, domStyle.get + "dojo/_base/kernel", + "dojo/_base/lang", // mixin(), isArray(), etc. + "dojo/on", + "dojo/ready", + "dojo/Stateful", // Stateful + "dojo/topic", + "dojo/_base/window", // win.doc.createTextNode + "./registry" // registry.getUniqueId(), registry.findWidgets() +], function(require, array, aspect, config, connect, declare, + dom, domAttr, domClass, domConstruct, domGeometry, domStyle, kernel, + lang, on, ready, Stateful, topic, win, registry){ + +/*===== +var Stateful = dojo.Stateful; +=====*/ + +// module: +// dijit/_WidgetBase +// summary: +// Future base class for all Dijit widgets. + +// For back-compat, remove in 2.0. +if(!kernel.isAsync){ + ready(0, function(){ + var requires = ["dijit/_base/manager"]; + require(requires); // use indirection so modules not rolled into a build + }); +} + +// Nested hash listing attributes for each tag, all strings in lowercase. +// ex: {"div": {"style": true, "tabindex" true}, "form": { ... +var tagAttrs = {}; +function getAttrs(obj){ + var ret = {}; + for(var attr in obj){ + ret[attr.toLowerCase()] = true; + } + return ret; +} + +function nonEmptyAttrToDom(attr){ + // summary: + // Returns a setter function that copies the attribute to this.domNode, + // or removes the attribute from this.domNode, depending on whether the + // value is defined or not. + return function(val){ + domAttr[val ? "set" : "remove"](this.domNode, attr, val); + this._set(attr, val); + }; +} + +return declare("dijit._WidgetBase", Stateful, { + // summary: + // Future base class for all Dijit widgets. + // description: + // Future base class for all Dijit widgets. + // _Widget extends this class adding support for various features needed by desktop. + // + // Provides stubs for widget lifecycle methods for subclasses to extend, like postMixInProperties(), buildRendering(), + // postCreate(), startup(), and destroy(), and also public API methods like set(), get(), and watch(). + // + // Widgets can provide custom setters/getters for widget attributes, which are called automatically by set(name, value). + // For an attribute XXX, define methods _setXXXAttr() and/or _getXXXAttr(). + // + // _setXXXAttr can also be a string/hash/array mapping from a widget attribute XXX to the widget's DOMNodes: + // + // - DOM node attribute + // | _setFocusAttr: {node: "focusNode", type: "attribute"} + // | _setFocusAttr: "focusNode" (shorthand) + // | _setFocusAttr: "" (shorthand, maps to this.domNode) + // Maps this.focus to this.focusNode.focus, or (last example) this.domNode.focus + // + // - DOM node innerHTML + // | _setTitleAttr: { node: "titleNode", type: "innerHTML" } + // Maps this.title to this.titleNode.innerHTML + // + // - DOM node innerText + // | _setTitleAttr: { node: "titleNode", type: "innerText" } + // Maps this.title to this.titleNode.innerText + // + // - DOM node CSS class + // | _setMyClassAttr: { node: "domNode", type: "class" } + // Maps this.myClass to this.domNode.className + // + // If the value of _setXXXAttr is an array, then each element in the array matches one of the + // formats of the above list. + // + // If the custom setter is null, no action is performed other than saving the new value + // in the widget (in this). + // + // If no custom setter is defined for an attribute, then it will be copied + // to this.focusNode (if the widget defines a focusNode), or this.domNode otherwise. + // That's only done though for attributes that match DOMNode attributes (title, + // alt, aria-labelledby, etc.) + + // id: [const] String + // A unique, opaque ID string that can be assigned by users or by the + // system. If the developer passes an ID which is known not to be + // unique, the specified ID is ignored and the system-generated ID is + // used instead. + id: "", + _setIdAttr: "domNode", // to copy to this.domNode even for auto-generated id's + + // lang: [const] String + // Rarely used. Overrides the default Dojo locale used to render this widget, + // as defined by the [HTML LANG](http://www.w3.org/TR/html401/struct/dirlang.html#adef-lang) attribute. + // Value must be among the list of locales specified during by the Dojo bootstrap, + // formatted according to [RFC 3066](http://www.ietf.org/rfc/rfc3066.txt) (like en-us). + lang: "", + // set on domNode even when there's a focus node. but don't set lang="", since that's invalid. + _setLangAttr: nonEmptyAttrToDom("lang"), + + // dir: [const] String + // Bi-directional support, as defined by the [HTML DIR](http://www.w3.org/TR/html401/struct/dirlang.html#adef-dir) + // attribute. Either left-to-right "ltr" or right-to-left "rtl". If undefined, widgets renders in page's + // default direction. + dir: "", + // set on domNode even when there's a focus node. but don't set dir="", since that's invalid. + _setDirAttr: nonEmptyAttrToDom("dir"), // to set on domNode even when there's a focus node + + // textDir: String + // Bi-directional support, the main variable which is responsible for the direction of the text. + // The text direction can be different than the GUI direction by using this parameter in creation + // of a widget. + // Allowed values: + // 1. "ltr" + // 2. "rtl" + // 3. "auto" - contextual the direction of a text defined by first strong letter. + // By default is as the page direction. + textDir: "", + + // class: String + // HTML class attribute + "class": "", + _setClassAttr: { node: "domNode", type: "class" }, + + // style: String||Object + // HTML style attributes as cssText string or name/value hash + style: "", + + // title: String + // HTML title attribute. + // + // For form widgets this specifies a tooltip to display when hovering over + // the widget (just like the native HTML title attribute). + // + // For TitlePane or for when this widget is a child of a TabContainer, AccordionContainer, + // etc., it's used to specify the tab label, accordion pane title, etc. + title: "", + + // tooltip: String + // When this widget's title attribute is used to for a tab label, accordion pane title, etc., + // this specifies the tooltip to appear when the mouse is hovered over that text. + tooltip: "", + + // baseClass: [protected] String + // Root CSS class of the widget (ex: dijitTextBox), used to construct CSS classes to indicate + // widget state. + baseClass: "", + + // srcNodeRef: [readonly] DomNode + // pointer to original DOM node + srcNodeRef: null, + + // domNode: [readonly] DomNode + // This is our visible representation of the widget! Other DOM + // Nodes may by assigned to other properties, usually through the + // template system's data-dojo-attach-point syntax, but the domNode + // property is the canonical "top level" node in widget UI. + domNode: null, + + // containerNode: [readonly] DomNode + // Designates where children of the source DOM node will be placed. + // "Children" in this case refers to both DOM nodes and widgets. + // For example, for myWidget: + // + // | <div data-dojo-type=myWidget> + // | <b> here's a plain DOM node + // | <span data-dojo-type=subWidget>and a widget</span> + // | <i> and another plain DOM node </i> + // | </div> + // + // containerNode would point to: + // + // | <b> here's a plain DOM node + // | <span data-dojo-type=subWidget>and a widget</span> + // | <i> and another plain DOM node </i> + // + // In templated widgets, "containerNode" is set via a + // data-dojo-attach-point assignment. + // + // containerNode must be defined for any widget that accepts innerHTML + // (like ContentPane or BorderContainer or even Button), and conversely + // is null for widgets that don't, like TextBox. + containerNode: null, + +/*===== + // _started: Boolean + // startup() has completed. + _started: false, +=====*/ + + // attributeMap: [protected] Object + // Deprecated. Instead of attributeMap, widget should have a _setXXXAttr attribute + // for each XXX attribute to be mapped to the DOM. + // + // attributeMap sets up a "binding" between attributes (aka properties) + // of the widget and the widget's DOM. + // Changes to widget attributes listed in attributeMap will be + // reflected into the DOM. + // + // For example, calling set('title', 'hello') + // on a TitlePane will automatically cause the TitlePane's DOM to update + // with the new title. + // + // attributeMap is a hash where the key is an attribute of the widget, + // and the value reflects a binding to a: + // + // - DOM node attribute + // | focus: {node: "focusNode", type: "attribute"} + // Maps this.focus to this.focusNode.focus + // + // - DOM node innerHTML + // | title: { node: "titleNode", type: "innerHTML" } + // Maps this.title to this.titleNode.innerHTML + // + // - DOM node innerText + // | title: { node: "titleNode", type: "innerText" } + // Maps this.title to this.titleNode.innerText + // + // - DOM node CSS class + // | myClass: { node: "domNode", type: "class" } + // Maps this.myClass to this.domNode.className + // + // If the value is an array, then each element in the array matches one of the + // formats of the above list. + // + // There are also some shorthands for backwards compatibility: + // - string --> { node: string, type: "attribute" }, for example: + // | "focusNode" ---> { node: "focusNode", type: "attribute" } + // - "" --> { node: "domNode", type: "attribute" } + attributeMap: {}, + + // _blankGif: [protected] String + // Path to a blank 1x1 image. + // Used by <img> nodes in templates that really get their image via CSS background-image. + _blankGif: config.blankGif || require.toUrl("dojo/resources/blank.gif"), + + //////////// INITIALIZATION METHODS /////////////////////////////////////// + + postscript: function(/*Object?*/params, /*DomNode|String*/srcNodeRef){ + // summary: + // Kicks off widget instantiation. See create() for details. + // tags: + // private + this.create(params, srcNodeRef); + }, + + create: function(/*Object?*/params, /*DomNode|String?*/srcNodeRef){ + // summary: + // Kick off the life-cycle of a widget + // params: + // Hash of initialization parameters for widget, including + // scalar values (like title, duration etc.) and functions, + // typically callbacks like onClick. + // srcNodeRef: + // If a srcNodeRef (DOM node) is specified: + // - use srcNodeRef.innerHTML as my contents + // - if this is a behavioral widget then apply behavior + // to that srcNodeRef + // - otherwise, replace srcNodeRef with my generated DOM + // tree + // description: + // Create calls a number of widget methods (postMixInProperties, buildRendering, postCreate, + // etc.), some of which of you'll want to override. See http://dojotoolkit.org/reference-guide/dijit/_WidgetBase.html + // for a discussion of the widget creation lifecycle. + // + // Of course, adventurous developers could override create entirely, but this should + // only be done as a last resort. + // tags: + // private + + // store pointer to original DOM tree + this.srcNodeRef = dom.byId(srcNodeRef); + + // For garbage collection. An array of listener handles returned by this.connect() / this.subscribe() + this._connects = []; + + // For widgets internal to this widget, invisible to calling code + this._supportingWidgets = []; + + // this is here for back-compat, remove in 2.0 (but check NodeList-instantiate.html test) + if(this.srcNodeRef && (typeof this.srcNodeRef.id == "string")){ this.id = this.srcNodeRef.id; } + + // mix in our passed parameters + if(params){ + this.params = params; + lang.mixin(this, params); + } + this.postMixInProperties(); + + // generate an id for the widget if one wasn't specified + // (be sure to do this before buildRendering() because that function might + // expect the id to be there.) + if(!this.id){ + this.id = registry.getUniqueId(this.declaredClass.replace(/\./g,"_")); + } + registry.add(this); + + this.buildRendering(); + + if(this.domNode){ + // Copy attributes listed in attributeMap into the [newly created] DOM for the widget. + // Also calls custom setters for all attributes with custom setters. + this._applyAttributes(); + + // If srcNodeRef was specified, then swap out original srcNode for this widget's DOM tree. + // For 2.0, move this after postCreate(). postCreate() shouldn't depend on the + // widget being attached to the DOM since it isn't when a widget is created programmatically like + // new MyWidget({}). See #11635. + var source = this.srcNodeRef; + if(source && source.parentNode && this.domNode !== source){ + source.parentNode.replaceChild(this.domNode, source); + } + } + + if(this.domNode){ + // Note: for 2.0 may want to rename widgetId to dojo._scopeName + "_widgetId", + // assuming that dojo._scopeName even exists in 2.0 + this.domNode.setAttribute("widgetId", this.id); + } + this.postCreate(); + + // If srcNodeRef has been processed and removed from the DOM (e.g. TemplatedWidget) then delete it to allow GC. + if(this.srcNodeRef && !this.srcNodeRef.parentNode){ + delete this.srcNodeRef; + } + + this._created = true; + }, + + _applyAttributes: function(){ + // summary: + // Step during widget creation to copy widget attributes to the + // DOM according to attributeMap and _setXXXAttr objects, and also to call + // custom _setXXXAttr() methods. + // + // Skips over blank/false attribute values, unless they were explicitly specified + // as parameters to the widget, since those are the default anyway, + // and setting tabIndex="" is different than not setting tabIndex at all. + // + // For backwards-compatibility reasons attributeMap overrides _setXXXAttr when + // _setXXXAttr is a hash/string/array, but _setXXXAttr as a functions override attributeMap. + // tags: + // private + + // Get list of attributes where this.set(name, value) will do something beyond + // setting this[name] = value. Specifically, attributes that have: + // - associated _setXXXAttr() method/hash/string/array + // - entries in attributeMap. + var ctor = this.constructor, + list = ctor._setterAttrs; + if(!list){ + list = (ctor._setterAttrs = []); + for(var attr in this.attributeMap){ + list.push(attr); + } + + var proto = ctor.prototype; + for(var fxName in proto){ + if(fxName in this.attributeMap){ continue; } + var setterName = "_set" + fxName.replace(/^[a-z]|-[a-zA-Z]/g, function(c){ return c.charAt(c.length-1).toUpperCase(); }) + "Attr"; + if(setterName in proto){ + list.push(fxName); + } + } + } + + // Call this.set() for each attribute that was either specified as parameter to constructor, + // or was found above and has a default non-null value. For correlated attributes like value and displayedValue, the one + // specified as a parameter should take precedence, so apply attributes in this.params last. + // Particularly important for new DateTextBox({displayedValue: ...}) since DateTextBox's default value is + // NaN and thus is not ignored like a default value of "". + array.forEach(list, function(attr){ + if(this.params && attr in this.params){ + // skip this one, do it below + }else if(this[attr]){ + this.set(attr, this[attr]); + } + }, this); + for(var param in this.params){ + this.set(param, this[param]); + } + }, + + postMixInProperties: function(){ + // summary: + // Called after the parameters to the widget have been read-in, + // but before the widget template is instantiated. Especially + // useful to set properties that are referenced in the widget + // template. + // tags: + // protected + }, + + buildRendering: function(){ + // summary: + // Construct the UI for this widget, setting this.domNode. + // Most widgets will mixin `dijit._TemplatedMixin`, which implements this method. + // tags: + // protected + + if(!this.domNode){ + // Create root node if it wasn't created by _Templated + this.domNode = this.srcNodeRef || domConstruct.create('div'); + } + + // baseClass is a single class name or occasionally a space-separated list of names. + // Add those classes to the DOMNode. If RTL mode then also add with Rtl suffix. + // TODO: make baseClass custom setter + if(this.baseClass){ + var classes = this.baseClass.split(" "); + if(!this.isLeftToRight()){ + classes = classes.concat( array.map(classes, function(name){ return name+"Rtl"; })); + } + domClass.add(this.domNode, classes); + } + }, + + postCreate: function(){ + // summary: + // Processing after the DOM fragment is created + // description: + // Called after the DOM fragment has been created, but not necessarily + // added to the document. Do not include any operations which rely on + // node dimensions or placement. + // tags: + // protected + }, + + startup: function(){ + // summary: + // Processing after the DOM fragment is added to the document + // description: + // Called after a widget and its children have been created and added to the page, + // and all related widgets have finished their create() cycle, up through postCreate(). + // This is useful for composite widgets that need to control or layout sub-widgets. + // Many layout widgets can use this as a wiring phase. + if(this._started){ return; } + this._started = true; + array.forEach(this.getChildren(), function(obj){ + if(!obj._started && !obj._destroyed && lang.isFunction(obj.startup)){ + obj.startup(); + obj._started = true; + } + }); + }, + + //////////// DESTROY FUNCTIONS //////////////////////////////// + + destroyRecursive: function(/*Boolean?*/ preserveDom){ + // summary: + // Destroy this widget and its descendants + // description: + // This is the generic "destructor" function that all widget users + // should call to cleanly discard with a widget. Once a widget is + // destroyed, it is removed from the manager object. + // preserveDom: + // If true, this method will leave the original DOM structure + // alone of descendant Widgets. Note: This will NOT work with + // dijit._Templated widgets. + + this._beingDestroyed = true; + this.destroyDescendants(preserveDom); + this.destroy(preserveDom); + }, + + destroy: function(/*Boolean*/ preserveDom){ + // summary: + // Destroy this widget, but not its descendants. + // This method will, however, destroy internal widgets such as those used within a template. + // preserveDom: Boolean + // If true, this method will leave the original DOM structure alone. + // Note: This will not yet work with _Templated widgets + + this._beingDestroyed = true; + this.uninitialize(); + + // remove this.connect() and this.subscribe() listeners + var c; + while(c = this._connects.pop()){ + c.remove(); + } + + // destroy widgets created as part of template, etc. + var w; + while(w = this._supportingWidgets.pop()){ + if(w.destroyRecursive){ + w.destroyRecursive(); + }else if(w.destroy){ + w.destroy(); + } + } + + this.destroyRendering(preserveDom); + registry.remove(this.id); + this._destroyed = true; + }, + + destroyRendering: function(/*Boolean?*/ preserveDom){ + // summary: + // Destroys the DOM nodes associated with this widget + // preserveDom: + // If true, this method will leave the original DOM structure alone + // during tear-down. Note: this will not work with _Templated + // widgets yet. + // tags: + // protected + + if(this.bgIframe){ + this.bgIframe.destroy(preserveDom); + delete this.bgIframe; + } + + if(this.domNode){ + if(preserveDom){ + domAttr.remove(this.domNode, "widgetId"); + }else{ + domConstruct.destroy(this.domNode); + } + delete this.domNode; + } + + if(this.srcNodeRef){ + if(!preserveDom){ + domConstruct.destroy(this.srcNodeRef); + } + delete this.srcNodeRef; + } + }, + + destroyDescendants: function(/*Boolean?*/ preserveDom){ + // summary: + // Recursively destroy the children of this widget and their + // descendants. + // preserveDom: + // If true, the preserveDom attribute is passed to all descendant + // widget's .destroy() method. Not for use with _Templated + // widgets. + + // get all direct descendants and destroy them recursively + array.forEach(this.getChildren(), function(widget){ + if(widget.destroyRecursive){ + widget.destroyRecursive(preserveDom); + } + }); + }, + + uninitialize: function(){ + // summary: + // Stub function. Override to implement custom widget tear-down + // behavior. + // tags: + // protected + return false; + }, + + ////////////////// GET/SET, CUSTOM SETTERS, ETC. /////////////////// + + _setStyleAttr: function(/*String||Object*/ value){ + // summary: + // Sets the style attribute of the widget according to value, + // which is either a hash like {height: "5px", width: "3px"} + // or a plain string + // description: + // Determines which node to set the style on based on style setting + // in attributeMap. + // tags: + // protected + + var mapNode = this.domNode; + + // Note: technically we should revert any style setting made in a previous call + // to his method, but that's difficult to keep track of. + + if(lang.isObject(value)){ + domStyle.set(mapNode, value); + }else{ + if(mapNode.style.cssText){ + mapNode.style.cssText += "; " + value; + }else{ + mapNode.style.cssText = value; + } + } + + this._set("style", value); + }, + + _attrToDom: function(/*String*/ attr, /*String*/ value, /*Object?*/ commands){ + // summary: + // Reflect a widget attribute (title, tabIndex, duration etc.) to + // the widget DOM, as specified by commands parameter. + // If commands isn't specified then it's looked up from attributeMap. + // Note some attributes like "type" + // cannot be processed this way as they are not mutable. + // + // tags: + // private + + commands = arguments.length >= 3 ? commands : this.attributeMap[attr]; + + array.forEach(lang.isArray(commands) ? commands : [commands], function(command){ + + // Get target node and what we are doing to that node + var mapNode = this[command.node || command || "domNode"]; // DOM node + var type = command.type || "attribute"; // class, innerHTML, innerText, or attribute + + switch(type){ + case "attribute": + if(lang.isFunction(value)){ // functions execute in the context of the widget + value = lang.hitch(this, value); + } + + // Get the name of the DOM node attribute; usually it's the same + // as the name of the attribute in the widget (attr), but can be overridden. + // Also maps handler names to lowercase, like onSubmit --> onsubmit + var attrName = command.attribute ? command.attribute : + (/^on[A-Z][a-zA-Z]*$/.test(attr) ? attr.toLowerCase() : attr); + + domAttr.set(mapNode, attrName, value); + break; + case "innerText": + mapNode.innerHTML = ""; + mapNode.appendChild(win.doc.createTextNode(value)); + break; + case "innerHTML": + mapNode.innerHTML = value; + break; + case "class": + domClass.replace(mapNode, value, this[attr]); + break; + } + }, this); + }, + + get: function(name){ + // summary: + // Get a property from a widget. + // name: + // The property to get. + // description: + // Get a named property from a widget. The property may + // potentially be retrieved via a getter method. If no getter is defined, this + // just retrieves the object's property. + // + // For example, if the widget has properties `foo` and `bar` + // and a method named `_getFooAttr()`, calling: + // `myWidget.get("foo")` would be equivalent to calling + // `widget._getFooAttr()` and `myWidget.get("bar")` + // would be equivalent to the expression + // `widget.bar2` + var names = this._getAttrNames(name); + return this[names.g] ? this[names.g]() : this[name]; + }, + + set: function(name, value){ + // summary: + // Set a property on a widget + // name: + // The property to set. + // value: + // The value to set in the property. + // description: + // Sets named properties on a widget which may potentially be handled by a + // setter in the widget. + // + // For example, if the widget has properties `foo` and `bar` + // and a method named `_setFooAttr()`, calling + // `myWidget.set("foo", "Howdy!")` would be equivalent to calling + // `widget._setFooAttr("Howdy!")` and `myWidget.set("bar", 3)` + // would be equivalent to the statement `widget.bar = 3;` + // + // set() may also be called with a hash of name/value pairs, ex: + // + // | myWidget.set({ + // | foo: "Howdy", + // | bar: 3 + // | }); + // + // This is equivalent to calling `set(foo, "Howdy")` and `set(bar, 3)` + + if(typeof name === "object"){ + for(var x in name){ + this.set(x, name[x]); + } + return this; + } + var names = this._getAttrNames(name), + setter = this[names.s]; + if(lang.isFunction(setter)){ + // use the explicit setter + var result = setter.apply(this, Array.prototype.slice.call(arguments, 1)); + }else{ + // Mapping from widget attribute to DOMNode attribute/value/etc. + // Map according to: + // 1. attributeMap setting, if one exists (TODO: attributeMap deprecated, remove in 2.0) + // 2. _setFooAttr: {...} type attribute in the widget (if one exists) + // 3. apply to focusNode or domNode if standard attribute name, excluding funcs like onClick. + // Checks if an attribute is a "standard attribute" by whether the DOMNode JS object has a similar + // attribute name (ex: accept-charset attribute matches jsObject.acceptCharset). + // Note also that Tree.focusNode() is a function not a DOMNode, so test for that. + var defaultNode = this.focusNode && !lang.isFunction(this.focusNode) ? "focusNode" : "domNode", + tag = this[defaultNode].tagName, + attrsForTag = tagAttrs[tag] || (tagAttrs[tag] = getAttrs(this[defaultNode])), + map = name in this.attributeMap ? this.attributeMap[name] : + names.s in this ? this[names.s] : + ((names.l in attrsForTag && typeof value != "function") || + /^aria-|^data-|^role$/.test(name)) ? defaultNode : null; + if(map != null){ + this._attrToDom(name, value, map); + } + this._set(name, value); + } + return result || this; + }, + + _attrPairNames: {}, // shared between all widgets + _getAttrNames: function(name){ + // summary: + // Helper function for get() and set(). + // Caches attribute name values so we don't do the string ops every time. + // tags: + // private + + var apn = this._attrPairNames; + if(apn[name]){ return apn[name]; } + var uc = name.replace(/^[a-z]|-[a-zA-Z]/g, function(c){ return c.charAt(c.length-1).toUpperCase(); }); + return (apn[name] = { + n: name+"Node", + s: "_set"+uc+"Attr", // converts dashes to camel case, ex: accept-charset --> _setAcceptCharsetAttr + g: "_get"+uc+"Attr", + l: uc.toLowerCase() // lowercase name w/out dashes, ex: acceptcharset + }); + }, + + _set: function(/*String*/ name, /*anything*/ value){ + // summary: + // Helper function to set new value for specified attribute, and call handlers + // registered with watch() if the value has changed. + var oldValue = this[name]; + this[name] = value; + if(this._watchCallbacks && this._created && value !== oldValue){ + this._watchCallbacks(name, oldValue, value); + } + }, + + on: function(/*String*/ type, /*Function*/ func){ + // summary: + // Call specified function when event occurs, ex: myWidget.on("click", function(){ ... }). + // description: + // Call specified function when event `type` occurs, ex: `myWidget.on("click", function(){ ... })`. + // Note that the function is not run in any particular scope, so if (for example) you want it to run in the + // widget's scope you must do `myWidget.on("click", lang.hitch(myWidget, func))`. + + return aspect.after(this, this._onMap(type), func, true); + }, + + _onMap: function(/*String*/ type){ + // summary: + // Maps on() type parameter (ex: "mousemove") to method name (ex: "onMouseMove") + var ctor = this.constructor, map = ctor._onMap; + if(!map){ + map = (ctor._onMap = {}); + for(var attr in ctor.prototype){ + if(/^on/.test(attr)){ + map[attr.replace(/^on/, "").toLowerCase()] = attr; + } + } + } + return map[type.toLowerCase()]; // String + }, + + toString: function(){ + // summary: + // Returns a string that represents the widget + // description: + // When a widget is cast to a string, this method will be used to generate the + // output. Currently, it does not implement any sort of reversible + // serialization. + return '[Widget ' + this.declaredClass + ', ' + (this.id || 'NO ID') + ']'; // String + }, + + getChildren: function(){ + // summary: + // Returns all the widgets contained by this, i.e., all widgets underneath this.containerNode. + // Does not return nested widgets, nor widgets that are part of this widget's template. + return this.containerNode ? registry.findWidgets(this.containerNode) : []; // dijit._Widget[] + }, + + getParent: function(){ + // summary: + // Returns the parent widget of this widget + return registry.getEnclosingWidget(this.domNode.parentNode); + }, + + connect: function( + /*Object|null*/ obj, + /*String|Function*/ event, + /*String|Function*/ method){ + // summary: + // Connects specified obj/event to specified method of this object + // and registers for disconnect() on widget destroy. + // description: + // Provide widget-specific analog to dojo.connect, except with the + // implicit use of this widget as the target object. + // Events connected with `this.connect` are disconnected upon + // destruction. + // returns: + // A handle that can be passed to `disconnect` in order to disconnect before + // the widget is destroyed. + // example: + // | var btn = new dijit.form.Button(); + // | // when foo.bar() is called, call the listener we're going to + // | // provide in the scope of btn + // | btn.connect(foo, "bar", function(){ + // | console.debug(this.toString()); + // | }); + // tags: + // protected + + var handle = connect.connect(obj, event, this, method); + this._connects.push(handle); + return handle; // _Widget.Handle + }, + + disconnect: function(handle){ + // summary: + // Disconnects handle created by `connect`. + // Also removes handle from this widget's list of connects. + // tags: + // protected + var i = array.indexOf(this._connects, handle); + if(i != -1){ + handle.remove(); + this._connects.splice(i, 1); + } + }, + + subscribe: function(t, method){ + // summary: + // Subscribes to the specified topic and calls the specified method + // of this object and registers for unsubscribe() on widget destroy. + // description: + // Provide widget-specific analog to dojo.subscribe, except with the + // implicit use of this widget as the target object. + // t: String + // The topic + // method: Function + // The callback + // example: + // | var btn = new dijit.form.Button(); + // | // when /my/topic is published, this button changes its label to + // | // be the parameter of the topic. + // | btn.subscribe("/my/topic", function(v){ + // | this.set("label", v); + // | }); + // tags: + // protected + var handle = topic.subscribe(t, lang.hitch(this, method)); + this._connects.push(handle); + return handle; // _Widget.Handle + }, + + unsubscribe: function(/*Object*/ handle){ + // summary: + // Unsubscribes handle created by this.subscribe. + // Also removes handle from this widget's list of subscriptions + // tags: + // protected + this.disconnect(handle); + }, + + isLeftToRight: function(){ + // summary: + // Return this widget's explicit or implicit orientation (true for LTR, false for RTL) + // tags: + // protected + return this.dir ? (this.dir == "ltr") : domGeometry.isBodyLtr(); //Boolean + }, + + isFocusable: function(){ + // summary: + // Return true if this widget can currently be focused + // and false if not + return this.focus && (domStyle.get(this.domNode, "display") != "none"); + }, + + placeAt: function(/* String|DomNode|_Widget */reference, /* String?|Int? */position){ + // summary: + // Place this widget's domNode reference somewhere in the DOM based + // on standard domConstruct.place conventions, or passing a Widget reference that + // contains and addChild member. + // + // description: + // A convenience function provided in all _Widgets, providing a simple + // shorthand mechanism to put an existing (or newly created) Widget + // somewhere in the dom, and allow chaining. + // + // reference: + // The String id of a domNode, a domNode reference, or a reference to a Widget possessing + // an addChild method. + // + // position: + // If passed a string or domNode reference, the position argument + // accepts a string just as domConstruct.place does, one of: "first", "last", + // "before", or "after". + // + // If passed a _Widget reference, and that widget reference has an ".addChild" method, + // it will be called passing this widget instance into that method, supplying the optional + // position index passed. + // + // returns: + // dijit._Widget + // Provides a useful return of the newly created dijit._Widget instance so you + // can "chain" this function by instantiating, placing, then saving the return value + // to a variable. + // + // example: + // | // create a Button with no srcNodeRef, and place it in the body: + // | var button = new dijit.form.Button({ label:"click" }).placeAt(win.body()); + // | // now, 'button' is still the widget reference to the newly created button + // | button.on("click", function(e){ console.log('click'); })); + // + // example: + // | // create a button out of a node with id="src" and append it to id="wrapper": + // | var button = new dijit.form.Button({},"src").placeAt("wrapper"); + // + // example: + // | // place a new button as the first element of some div + // | var button = new dijit.form.Button({ label:"click" }).placeAt("wrapper","first"); + // + // example: + // | // create a contentpane and add it to a TabContainer + // | var tc = dijit.byId("myTabs"); + // | new dijit.layout.ContentPane({ href:"foo.html", title:"Wow!" }).placeAt(tc) + + if(reference.declaredClass && reference.addChild){ + reference.addChild(this, position); + }else{ + domConstruct.place(this.domNode, reference, position); + } + return this; + }, + + getTextDir: function(/*String*/ text,/*String*/ originalDir){ + // summary: + // Return direction of the text. + // The function overridden in the _BidiSupport module, + // its main purpose is to calculate the direction of the + // text, if was defined by the programmer through textDir. + // tags: + // protected. + return originalDir; + }, + + applyTextDir: function(/*===== element, text =====*/){ + // summary: + // The function overridden in the _BidiSupport module, + // originally used for setting element.dir according to this.textDir. + // In this case does nothing. + // element: DOMNode + // text: String + // tags: + // protected. + } +}); + +}); + +}}}); + +require(["dojo/i18n"], function(i18n){ +i18n._preloadLocalizations("dojox/charting/widget/nls/Chart2D", []); +}); +define("dojox/charting/widget/Chart2D", ["dojo/_base/kernel", "./Chart", "../Chart2D", + "../action2d/Highlight", "../action2d/Magnify", + "../action2d/MoveSlice", "../action2d/Shake", "../action2d/Tooltip"], function(dojo, Chart) { + dojo.deprecated("dojox.charting.widget.Chart2D", "Use dojo.charting.widget.Chart instead and require all other components explicitly", "2.0"); + return dojox.charting.widget.Chart2D = Chart; +}); |
