|
/** Markers **/ |
|
/** |
|
* Formats the marker labels. |
|
* @param {Object} obj - Marker value Object {x:..,y:..} |
|
* @return {String} Formatted marker string |
|
*/ |
|
(function () { |
|
|
|
Flotr.defaultMarkerFormatter = function(obj){ |
|
return (Math.round(obj.y*100)/100)+''; |
|
}; |
|
|
|
Flotr.addType('markers', { |
|
options: { |
|
show: false, // => setting to true will show markers, false will hide |
|
lineWidth: 1, // => line width of the rectangle around the marker |
|
color: '#000000', // => text color |
|
fill: false, // => fill or not the marekers' rectangles |
|
fillColor: "#FFFFFF", // => fill color |
|
fillOpacity: 0.4, // => fill opacity |
|
stroke: false, // => draw the rectangle around the markers |
|
position: 'ct', // => the markers position (vertical align: b, m, t, horizontal align: l, c, r) |
|
verticalMargin: 0, // => the margin between the point and the text. |
|
labelFormatter: Flotr.defaultMarkerFormatter, |
|
fontSize: Flotr.defaultOptions.fontSize, |
|
stacked: false, // => true if markers should be stacked |
|
stackingType: 'b', // => define staching behavior, (b- bars like, a - area like) (see Issue 125 for details) |
|
horizontal: false // => true if markers should be horizontal (For now only in a case on horizontal stacked bars, stacks should be calculated horizontaly) |
|
}, |
|
|
|
// TODO test stacked markers. |
|
stack : { |
|
positive : [], |
|
negative : [], |
|
values : [] |
|
}, |
|
|
|
draw : function (options) { |
|
|
|
var |
|
data = options.data, |
|
context = options.context, |
|
stack = options.stacked ? options.stack : false, |
|
stackType = options.stackingType, |
|
stackOffsetNeg, |
|
stackOffsetPos, |
|
stackOffset, |
|
i, x, y, label; |
|
|
|
context.save(); |
|
context.lineJoin = 'round'; |
|
context.lineWidth = options.lineWidth; |
|
context.strokeStyle = 'rgba(0,0,0,0.5)'; |
|
context.fillStyle = options.fillStyle; |
|
|
|
function stackPos (a, b) { |
|
stackOffsetPos = stack.negative[a] || 0; |
|
stackOffsetNeg = stack.positive[a] || 0; |
|
if (b > 0) { |
|
stack.positive[a] = stackOffsetPos + b; |
|
return stackOffsetPos + b; |
|
} else { |
|
stack.negative[a] = stackOffsetNeg + b; |
|
return stackOffsetNeg + b; |
|
} |
|
} |
|
|
|
for (i = 0; i < data.length; ++i) { |
|
|
|
x = data[i][0]; |
|
y = data[i][1]; |
|
|
|
if (stack) { |
|
if (stackType == 'b') { |
|
if (options.horizontal) y = stackPos(y, x); |
|
else x = stackPos(x, y); |
|
} else if (stackType == 'a') { |
|
stackOffset = stack.values[x] || 0; |
|
stack.values[x] = stackOffset + y; |
|
y = stackOffset + y; |
|
} |
|
} |
|
|
|
label = options.labelFormatter({x: x, y: y, index: i, data : data}); |
|
this.plot(options.xScale(x), options.yScale(y), label, options); |
|
} |
|
context.restore(); |
|
}, |
|
plot: function(x, y, label, options) { |
|
var context = options.context; |
|
if (isImage(label) && !label.complete) { |
|
throw 'Marker image not loaded.'; |
|
} else { |
|
this._plot(x, y, label, options); |
|
} |
|
}, |
|
|
|
_plot: function(x, y, label, options) { |
|
var context = options.context, |
|
margin = 2, |
|
left = x, |
|
top = y, |
|
dim; |
|
|
|
if (isImage(label)) |
|
dim = {height : label.height, width: label.width}; |
|
else |
|
dim = options.text.canvas(label); |
|
|
|
dim.width = Math.floor(dim.width+margin*2); |
|
dim.height = Math.floor(dim.height+margin*2); |
|
|
|
if (options.position.indexOf('c') != -1) left -= dim.width/2 + margin; |
|
else if (options.position.indexOf('l') != -1) left -= dim.width; |
|
|
|
if (options.position.indexOf('m') != -1) top -= dim.height/2 + margin; |
|
else if (options.position.indexOf('t') != -1) top -= dim.height + options.verticalMargin; |
|
else top += options.verticalMargin; |
|
|
|
left = Math.floor(left)+0.5; |
|
top = Math.floor(top)+0.5; |
|
|
|
if(options.fill) |
|
context.fillRect(left, top, dim.width, dim.height); |
|
|
|
if(options.stroke) |
|
context.strokeRect(left, top, dim.width, dim.height); |
|
|
|
if (isImage(label)) |
|
context.drawImage(label, left+margin, top+margin); |
|
else |
|
Flotr.drawText(context, label, left+margin, top+margin, {textBaseline: 'top', textAlign: 'left', size: options.fontSize, color: options.color}); |
|
} |
|
}); |
|
|
|
function isImage (i) { |
|
return typeof i === 'object' && i.constructor && (Image ? true : i.constructor === Image); |
|
} |
|
|
|
})(); |
|
|