/*
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
*/
if(!dojo._hasResource["dojox.charting.Element"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojox.charting.Element"] = true;
dojo.provide("dojox.charting.Element");
dojo.require("dojox.gfx");
dojo.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.Chart2D
// 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.Chart2D
// 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){
this.group.clear();
this.group.removeShape();
this.group = null;
}
this.dirty = true;
if(this._events.length){
dojo.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){
dojo.forEach(this.htmlElements, dojo.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 dojox.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 = dojox.gfx.makeParameters(dojox.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 = dojox.gfx.makeParameters(dojox.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 = dojox.gfx.makeParameters(dojox.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 = dojox.gfx.makeParameters(dojox.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 = dojox.gfx.makeParameters(dojox.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 = dojox.gfx.makeParameters(dojox.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 = dojox.gfx.makeParameters(dojox.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;
}
});
}