/** 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); } })();