pandorafms/pandora_console/include/graphs/flot/jquery.flot.min.js

18 lines
60 KiB
JavaScript

/* Javascript plotting library for jQuery, version 0.8.3.
Copyright (c) 2007-2014 IOLA and Ole Laursen.
Licensed under the MIT license.
*/
(function ($) { $.color = {}; $.color.make = function (r, g, b, a) { var o = {}; o.r = r || 0; o.g = g || 0; o.b = b || 0; o.a = a != null ? a : 1; o.add = function (c, d) { for (var i = 0; i < c.length; ++i)o[c.charAt(i)] += d; return o.normalize() }; o.scale = function (c, f) { for (var i = 0; i < c.length; ++i)o[c.charAt(i)] *= f; return o.normalize() }; o.toString = function () { if (o.a >= 1) { return "rgb(" + [o.r, o.g, o.b].join(",") + ")" } else { return "rgba(" + [o.r, o.g, o.b, o.a].join(",") + ")" } }; o.normalize = function () { function clamp(min, value, max) { return value < min ? min : value > max ? max : value } o.r = clamp(0, parseInt(o.r), 255); o.g = clamp(0, parseInt(o.g), 255); o.b = clamp(0, parseInt(o.b), 255); o.a = clamp(0, o.a, 1); return o }; o.clone = function () { return $.color.make(o.r, o.b, o.g, o.a) }; return o.normalize() }; $.color.extract = function (elem, css) { var c; do { c = elem.css(css).toLowerCase(); if (c != "" && c != "transparent") break; elem = elem.parent() } while (elem.length && !$.nodeName(elem.get(0), "body")); if (c == "rgba(0, 0, 0, 0)") c = "transparent"; return $.color.parse(c) }; $.color.parse = function (str) { var res, m = $.color.make; if (res = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(str)) return m(parseInt(res[1], 10), parseInt(res[2], 10), parseInt(res[3], 10)); if (res = /rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(str)) return m(parseInt(res[1], 10), parseInt(res[2], 10), parseInt(res[3], 10), parseFloat(res[4])); if (res = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(str)) return m(parseFloat(res[1]) * 2.55, parseFloat(res[2]) * 2.55, parseFloat(res[3]) * 2.55); if (res = /rgba\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(str)) return m(parseFloat(res[1]) * 2.55, parseFloat(res[2]) * 2.55, parseFloat(res[3]) * 2.55, parseFloat(res[4])); if (res = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(str)) return m(parseInt(res[1], 16), parseInt(res[2], 16), parseInt(res[3], 16)); if (res = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(str)) return m(parseInt(res[1] + res[1], 16), parseInt(res[2] + res[2], 16), parseInt(res[3] + res[3], 16)); var name = $.trim(str).toLowerCase(); if (name == "transparent") return m(255, 255, 255, 0); else { res = lookupColors[name] || [0, 0, 0]; return m(res[0], res[1], res[2]) } }; var lookupColors = { aqua: [0, 255, 255], azure: [240, 255, 255], beige: [245, 245, 220], black: [0, 0, 0], blue: [0, 0, 255], brown: [165, 42, 42], cyan: [0, 255, 255], darkblue: [0, 0, 139], darkcyan: [0, 139, 139], darkgrey: [169, 169, 169], darkgreen: [0, 100, 0], 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], darkviolet: [148, 0, 211], fuchsia: [255, 0, 255], gold: [255, 215, 0], green: [0, 128, 0], indigo: [75, 0, 130], khaki: [240, 230, 140], lightblue: [173, 216, 230], lightcyan: [224, 255, 255], lightgreen: [144, 238, 144], lightgrey: [211, 211, 211], lightpink: [255, 182, 193], lightyellow: [255, 255, 224], lime: [0, 255, 0], magenta: [255, 0, 255], maroon: [128, 0, 0], navy: [0, 0, 128], olive: [128, 128, 0], orange: [255, 165, 0], pink: [255, 192, 203], purple: [128, 0, 128], violet: [128, 0, 128], red: [255, 0, 0], silver: [192, 192, 192], white: [255, 255, 255], yellow: [255, 255, 0] } })(jQuery); (function ($) {
var hasOwnProperty = Object.prototype.hasOwnProperty; if (!$.fn.detach) { $.fn.detach = function () { return this.each(function () { if (this.parentNode) { this.parentNode.removeChild(this) } }) } } function Canvas(cls, container) { var element = container.children("." + cls)[0]; if (element == null) { element = document.createElement("canvas"); element.className = cls; $(element).css({ direction: "ltr", position: "absolute", left: 0, top: 0 }).appendTo(container); if (!element.getContext) { if (window.G_vmlCanvasManager) { element = window.G_vmlCanvasManager.initElement(element) } else { throw new Error("Canvas is not available. If you're using IE with a fall-back such as Excanvas, then there's either a mistake in your conditional include, or the page has no DOCTYPE and is rendering in Quirks Mode.") } } } this.element = element; var context = this.context = element.getContext("2d"); var devicePixelRatio = window.devicePixelRatio || 1, backingStoreRatio = context.webkitBackingStorePixelRatio || context.mozBackingStorePixelRatio || context.msBackingStorePixelRatio || context.oBackingStorePixelRatio || context.backingStorePixelRatio || 1; this.pixelRatio = devicePixelRatio / backingStoreRatio; this.resize(container.width(), container.height()); this.textContainer = null; this.text = {}; this._textCache = {} } Canvas.prototype.resize = function (width, height) { if (width <= 0 || height <= 0) { throw new Error("Invalid dimensions for plot, width = " + width + ", height = " + height) } var element = this.element, context = this.context, pixelRatio = this.pixelRatio; if (this.width != width) { element.width = width * pixelRatio; element.style.width = width + "px"; this.width = width } if (this.height != height) { element.height = height * pixelRatio; element.style.height = height + "px"; this.height = height } context.restore(); context.save(); context.scale(pixelRatio, pixelRatio) }; Canvas.prototype.clear = function () { this.context.clearRect(0, 0, this.width, this.height) }; Canvas.prototype.render = function () { var cache = this._textCache; for (var layerKey in cache) { if (hasOwnProperty.call(cache, layerKey)) { var layer = this.getTextLayer(layerKey), layerCache = cache[layerKey]; layer.hide(); for (var styleKey in layerCache) { if (hasOwnProperty.call(layerCache, styleKey)) { var styleCache = layerCache[styleKey]; for (var key in styleCache) { if (hasOwnProperty.call(styleCache, key)) { var positions = styleCache[key].positions; for (var i = 0, position; position = positions[i]; i++) { if (position.active) { if (!position.rendered) { layer.append(position.element); position.rendered = true } } else { positions.splice(i--, 1); if (position.rendered) { position.element.detach() } } } if (positions.length == 0) { delete styleCache[key] } } } } } layer.show() } } }; Canvas.prototype.getTextLayer = function (classes) { var layer = this.text[classes]; if (layer == null) { if (this.textContainer == null) { this.textContainer = $("<div class='flot-text'></div>").css({ position: "absolute", top: 0, left: 0, bottom: 0, right: 0, "font-size": "smaller"}).insertAfter(this.element) } layer = this.text[classes] = $("<div></div>").addClass(classes).css({ position: "absolute", top: 0, left: 0, bottom: 0, right: 0 }).appendTo(this.textContainer) } return layer }; Canvas.prototype.getTextInfo = function (layer, text, font, angle, width) { var textStyle, layerCache, styleCache, info; text = "" + text; if (typeof font === "object") { textStyle = font.style + " " + font.variant + " " + font.weight + " " + font.size + "px/" + font.lineHeight + "px " + font.family } else { textStyle = font } layerCache = this._textCache[layer]; if (layerCache == null) { layerCache = this._textCache[layer] = {} } styleCache = layerCache[textStyle]; if (styleCache == null) { styleCache = layerCache[textStyle] = {} } info = styleCache[text]; if (info == null) { var element = $("<div></div>").html(text).css({ position: "absolute", "max-width": width, top: -9999 }).appendTo(this.getTextLayer(layer)); if (typeof font === "object") { element.css({ font: textStyle, color: font.color }) } else if (typeof font === "string") { element.addClass(font) } info = styleCache[text] = { width: element.outerWidth(true), height: element.outerHeight(true), element: element, positions: [] }; element.detach() } return info }; Canvas.prototype.addText = function (layer, x, y, text, font, angle, width, halign, valign) { var info = this.getTextInfo(layer, text, font, angle, width), positions = info.positions; if (halign == "center") { x -= info.width / 2 } else if (halign == "right") { x -= info.width } if (valign == "middle") { y -= info.height / 2 } else if (valign == "bottom") { y -= info.height } for (var i = 0, position; position = positions[i]; i++) { if (position.x == x && position.y == y) { position.active = true; return } } position = { active: true, rendered: false, element: positions.length ? info.element.clone() : info.element, x: x, y: y }; positions.push(position); position.element.css({ top: Math.round(y), left: Math.round(x), "text-align": halign }) }; Canvas.prototype.removeText = function (layer, x, y, text, font, angle) { if (text == null) { var layerCache = this._textCache[layer]; if (layerCache != null) { for (var styleKey in layerCache) { if (hasOwnProperty.call(layerCache, styleKey)) { var styleCache = layerCache[styleKey]; for (var key in styleCache) { if (hasOwnProperty.call(styleCache, key)) { var positions = styleCache[key].positions; for (var i = 0, position; position = positions[i]; i++) { position.active = false } } } } } } } else { var positions = this.getTextInfo(layer, text, font, angle).positions; for (var i = 0, position; position = positions[i]; i++) { if (position.x == x && position.y == y) { position.active = false } } } }; function Plot(placeholder, data_, options_, plugins) {
var series = [], options = { colors: ["#edc240", "#afd8f8", "#cb4b4b", "#4da74d", "#9440ed"], legend: { show: true, noColumns: 1, labelFormatter: null, labelBoxBorderColor: "#ccc", container: null, position: "ne", margin: 5, backgroundColor: null, backgroundOpacity: .85, sorted: null }, xaxis: { show: null, position: "bottom", mode: null, font: null, color: null, tickColor: null, transform: null, inverseTransform: null, min: null, max: null, autoscaleMargin: null, ticks: null, tickFormatter: null, labelWidth: null, labelHeight: null, reserveSpace: null, tickLength: null, alignTicksWithAxis: null, tickDecimals: null, tickSize: null, minTickSize: null }, yaxis: { autoscaleMargin: .02, position: "left" }, xaxes: [], yaxes: [], series: { points: { show: false, radius: 3, lineWidth: 2, fill: true, fillColor: "#ffffff", symbol: "circle" }, lines: { lineWidth: 2, fill: false, fillColor: null, steps: false }, bars: { show: false, lineWidth: 2, barWidth: 1, fill: true, fillColor: null, align: "left", horizontal: false, zero: true }, shadowSize: 3, highlightColor: null }, grid: { show: true, aboveData: false, color: "#545454", backgroundColor: null, borderColor: null, tickColor: null, margin: 0, labelMargin: 5, axisMargin: 8, borderWidth: 2, minBorderMargin: null, markings: null, markingsColor: "#f4f4f4", markingsLineWidth: 2, clickable: false, hoverable: false, autoHighlight: true, mouseActiveRadius: 10 }, interaction: { redrawOverlayInterval: 1e3 / 60 }, hooks: {} }, surface = null, overlay = null, eventHolder = null, ctx = null, octx = null, xaxes = [], yaxes = [], plotOffset = { left: 0, right: 0, top: 0, bottom: 0 }, plotWidth = 0, plotHeight = 0, hooks = { processOptions: [], processRawData: [], processDatapoints: [], processOffset: [], drawBackground: [], drawSeries: [], draw: [], bindEvents: [], drawOverlay: [], shutdown: [] }, plot = this; plot.setData = setData; plot.setupGrid = setupGrid; plot.draw = draw; plot.getPlaceholder = function () { return placeholder }; plot.getCanvas = function () { return surface.element }; plot.getPlotOffset = function () { return plotOffset }; plot.width = function () { return plotWidth }; plot.height = function () { return plotHeight }; plot.offset = function () { var o = eventHolder.offset(); o.left += plotOffset.left; o.top += plotOffset.top; return o }; plot.getData = function () { return series }; plot.getAxes = function () { var res = {}, i; $.each(xaxes.concat(yaxes), function (_, axis) { if (axis) res[axis.direction + (axis.n != 1 ? axis.n : "") + "axis"] = axis }); return res }; plot.getXAxes = function () { return xaxes }; plot.getYAxes = function () { return yaxes }; plot.c2p = canvasToAxisCoords; plot.p2c = axisToCanvasCoords; plot.getOptions = function () { return options }; plot.highlight = highlight; plot.unhighlight = unhighlight; plot.triggerRedrawOverlay = triggerRedrawOverlay; plot.pointOffset = function (point) { return { left: parseInt(xaxes[axisNumber(point, "x") - 1].p2c(+point.x) + plotOffset.left, 10), top: parseInt(yaxes[axisNumber(point, "y") - 1].p2c(+point.y) + plotOffset.top, 10) } }; plot.shutdown = shutdown; plot.destroy = function () { shutdown(); placeholder.removeData("plot").empty(); series = []; options = null; surface = null; overlay = null; eventHolder = null; ctx = null; octx = null; xaxes = []; yaxes = []; hooks = null; highlights = []; plot = null }; plot.resize = function () { var width = placeholder.width(), height = placeholder.height(); surface.resize(width, height); overlay.resize(width, height) }; plot.hooks = hooks; initPlugins(plot); parseOptions(options_); setupCanvases(); setData(data_); setupGrid(); draw(); bindEvents(); function executeHooks(hook, args) { args = [plot].concat(args); for (var i = 0; i < hook.length; ++i)hook[i].apply(this, args) } function initPlugins() { var classes = { Canvas: Canvas }; for (var i = 0; i < plugins.length; ++i) { var p = plugins[i]; p.init(plot, classes); if (p.options) $.extend(true, options, p.options) } } function parseOptions(opts) { $.extend(true, options, opts); if (opts && opts.colors) { options.colors = opts.colors } if (options.xaxis.color == null) options.xaxis.color = $.color.parse(options.grid.color).scale("a", .22).toString(); if (options.yaxis.color == null) options.yaxis.color = $.color.parse(options.grid.color).scale("a", .22).toString(); if (options.xaxis.tickColor == null) options.xaxis.tickColor = options.grid.tickColor || options.xaxis.color; if (options.yaxis.tickColor == null) options.yaxis.tickColor = options.grid.tickColor || options.yaxis.color; if (options.grid.borderColor == null) options.grid.borderColor = options.grid.color; if (options.grid.tickColor == null) options.grid.tickColor = $.color.parse(options.grid.color).scale("a", .22).toString(); var i, axisOptions, axisCount, fontSize = placeholder.css("font-size"), fontSizeDefault = fontSize ? +fontSize.replace("px", "") : 13, fontDefaults = { style: placeholder.css("font-style"), size: Math.round(.8 * fontSizeDefault), variant: placeholder.css("font-variant"), weight: placeholder.css("font-weight"), family: placeholder.css("font-family") }; axisCount = options.xaxes.length || 1; for (i = 0; i < axisCount; ++i) { axisOptions = options.xaxes[i]; if (axisOptions && !axisOptions.tickColor) { axisOptions.tickColor = axisOptions.color } axisOptions = $.extend(true, {}, options.xaxis, axisOptions); options.xaxes[i] = axisOptions; if (axisOptions.font) { axisOptions.font = $.extend({}, fontDefaults, axisOptions.font); if (!axisOptions.font.color) { axisOptions.font.color = axisOptions.color } if (!axisOptions.font.lineHeight) { axisOptions.font.lineHeight = Math.round(axisOptions.font.size * 1.15) } } } axisCount = options.yaxes.length || 1; for (i = 0; i < axisCount; ++i) { axisOptions = options.yaxes[i]; if (axisOptions && !axisOptions.tickColor) { axisOptions.tickColor = axisOptions.color } axisOptions = $.extend(true, {}, options.yaxis, axisOptions); options.yaxes[i] = axisOptions; if (axisOptions.font) { axisOptions.font = $.extend({}, fontDefaults, axisOptions.font); if (!axisOptions.font.color) { axisOptions.font.color = axisOptions.color } if (!axisOptions.font.lineHeight) { axisOptions.font.lineHeight = Math.round(axisOptions.font.size * 1.15) } } } if (options.xaxis.noTicks && options.xaxis.ticks == null) options.xaxis.ticks = options.xaxis.noTicks; if (options.yaxis.noTicks && options.yaxis.ticks == null) options.yaxis.ticks = options.yaxis.noTicks; if (options.x2axis) { options.xaxes[1] = $.extend(true, {}, options.xaxis, options.x2axis); options.xaxes[1].position = "top"; if (options.x2axis.min == null) { options.xaxes[1].min = null } if (options.x2axis.max == null) { options.xaxes[1].max = null } } if (options.y2axis) { options.yaxes[1] = $.extend(true, {}, options.yaxis, options.y2axis); options.yaxes[1].position = "right"; if (options.y2axis.min == null) { options.yaxes[1].min = null } if (options.y2axis.max == null) { options.yaxes[1].max = null } } if (options.grid.coloredAreas) options.grid.markings = options.grid.coloredAreas; if (options.grid.coloredAreasColor) options.grid.markingsColor = options.grid.coloredAreasColor; if (options.lines) $.extend(true, options.series.lines, options.lines); if (options.points) $.extend(true, options.series.points, options.points); if (options.bars) $.extend(true, options.series.bars, options.bars); if (options.shadowSize != null) options.series.shadowSize = options.shadowSize; if (options.highlightColor != null) options.series.highlightColor = options.highlightColor; for (i = 0; i < options.xaxes.length; ++i)getOrCreateAxis(xaxes, i + 1).options = options.xaxes[i]; for (i = 0; i < options.yaxes.length; ++i)getOrCreateAxis(yaxes, i + 1).options = options.yaxes[i]; for (var n in hooks) if (options.hooks[n] && options.hooks[n].length) hooks[n] = hooks[n].concat(options.hooks[n]); executeHooks(hooks.processOptions, [options]) } function setData(d) { series = parseData(d); fillInSeriesOptions(); processData() } function parseData(d) { var res = []; for (var i = 0; i < d.length; ++i) { var s = $.extend(true, {}, options.series); if (d[i].data != null) { s.data = d[i].data; delete d[i].data; $.extend(true, s, d[i]); d[i].data = s.data } else s.data = d[i]; res.push(s) } return res } function axisNumber(obj, coord) { var a = obj[coord + "axis"]; if (typeof a == "object") a = a.n; if (typeof a != "number") a = 1; return a } function allAxes() { return $.grep(xaxes.concat(yaxes), function (a) { return a }) } function canvasToAxisCoords(pos) { var res = {}, i, axis; for (i = 0; i < xaxes.length; ++i) { axis = xaxes[i]; if (axis && axis.used) res["x" + axis.n] = axis.c2p(pos.left) } for (i = 0; i < yaxes.length; ++i) { axis = yaxes[i]; if (axis && axis.used) res["y" + axis.n] = axis.c2p(pos.top) } if (res.x1 !== undefined) res.x = res.x1; if (res.y1 !== undefined) res.y = res.y1; return res } function axisToCanvasCoords(pos) { var res = {}, i, axis, key; for (i = 0; i < xaxes.length; ++i) { axis = xaxes[i]; if (axis && axis.used) { key = "x" + axis.n; if (pos[key] == null && axis.n == 1) key = "x"; if (pos[key] != null) { res.left = axis.p2c(pos[key]); break } } } for (i = 0; i < yaxes.length; ++i) { axis = yaxes[i]; if (axis && axis.used) { key = "y" + axis.n; if (pos[key] == null && axis.n == 1) key = "y"; if (pos[key] != null) { res.top = axis.p2c(pos[key]); break } } } return res } function getOrCreateAxis(axes, number) { if (!axes[number - 1]) axes[number - 1] = { n: number, direction: axes == xaxes ? "x" : "y", options: $.extend(true, {}, axes == xaxes ? options.xaxis : options.yaxis) }; return axes[number - 1] } function fillInSeriesOptions() { var neededColors = series.length, maxIndex = -1, i; for (i = 0; i < series.length; ++i) { var sc = series[i].color; if (sc != null) { neededColors--; if (typeof sc == "number" && sc > maxIndex) { maxIndex = sc } } } if (neededColors <= maxIndex) { neededColors = maxIndex + 1 } var c, colors = [], colorPool = options.colors, colorPoolSize = colorPool.length, variation = 0; for (i = 0; i < neededColors; i++) { c = $.color.parse(colorPool[i % colorPoolSize] || "#666"); if (i % colorPoolSize == 0 && i) { if (variation >= 0) { if (variation < .5) { variation = -variation - .2 } else variation = 0 } else variation = -variation } colors[i] = c.scale("rgb", 1 + variation) } var colori = 0, s; for (i = 0; i < series.length; ++i) { s = series[i]; if (s.color == null) { s.color = colors[colori].toString(); ++colori } else if (typeof s.color == "number") s.color = colors[s.color].toString(); if (s.lines.show == null) { var v, show = true; for (v in s) if (s[v] && s[v].show) { show = false; break } if (show) s.lines.show = true } if (s.lines.zero == null) { s.lines.zero = !!s.lines.fill } s.xaxis = getOrCreateAxis(xaxes, axisNumber(s, "x")); s.yaxis = getOrCreateAxis(yaxes, axisNumber(s, "y")) } } function processData() { var topSentry = Number.POSITIVE_INFINITY, bottomSentry = Number.NEGATIVE_INFINITY, fakeInfinity = Number.MAX_VALUE, i, j, k, m, length, s, points, ps, x, y, axis, val, f, p, data, format; function updateAxis(axis, min, max) { if (min < axis.datamin && min != -fakeInfinity) axis.datamin = min; if (max > axis.datamax && max != fakeInfinity) axis.datamax = max } $.each(allAxes(), function (_, axis) { axis.datamin = topSentry; axis.datamax = bottomSentry; axis.used = false }); for (i = 0; i < series.length; ++i) { s = series[i]; s.datapoints = { points: [] }; executeHooks(hooks.processRawData, [s, s.data, s.datapoints]) } for (i = 0; i < series.length; ++i) { s = series[i]; data = s.data; format = s.datapoints.format; if (!format) { format = []; format.push({ x: true, number: true, required: true }); format.push({ y: true, number: true, required: true }); if (s.bars.show || s.lines.show && s.lines.fill) { var autoscale = !!(s.bars.show && s.bars.zero || s.lines.show && s.lines.zero); format.push({ y: true, number: true, required: false, defaultValue: 0, autoscale: autoscale }); if (s.bars.horizontal) { delete format[format.length - 1].y; format[format.length - 1].x = true } } s.datapoints.format = format } if (s.datapoints.pointsize != null) continue; s.datapoints.pointsize = format.length; ps = s.datapoints.pointsize; points = s.datapoints.points; var insertSteps = s.lines.show && s.lines.steps; s.xaxis.used = s.yaxis.used = true; for (j = k = 0; j < data.length; ++j, k += ps) { p = data[j]; var nullify = p == null; if (!nullify) { for (m = 0; m < ps; ++m) { val = p[m]; f = format[m]; if (f) { if (f.number && val != null) { val = +val; if (isNaN(val)) val = null; else if (val == Infinity) val = fakeInfinity; else if (val == -Infinity) val = -fakeInfinity } if (val == null) { if (f.required) nullify = true; if (f.defaultValue != null) val = f.defaultValue } } points[k + m] = val } } if (nullify) { for (m = 0; m < ps; ++m) { val = points[k + m]; if (val != null) { f = format[m]; if (f.autoscale !== false) { if (f.x) { updateAxis(s.xaxis, val, val) } if (f.y) { updateAxis(s.yaxis, val, val) } } } points[k + m] = null } } else { if (insertSteps && k > 0 && points[k - ps] != null && points[k - ps] != points[k] && points[k - ps + 1] != points[k + 1]) { for (m = 0; m < ps; ++m)points[k + ps + m] = points[k + m]; points[k + 1] = points[k - ps + 1]; k += ps } } } } for (i = 0; i < series.length; ++i) { s = series[i]; executeHooks(hooks.processDatapoints, [s, s.datapoints]) } for (i = 0; i < series.length; ++i) { s = series[i]; points = s.datapoints.points; ps = s.datapoints.pointsize; format = s.datapoints.format; var xmin = topSentry, ymin = topSentry, xmax = bottomSentry, ymax = bottomSentry; for (j = 0; j < points.length; j += ps) { if (points[j] == null) continue; for (m = 0; m < ps; ++m) { val = points[j + m]; f = format[m]; if (!f || f.autoscale === false || val == fakeInfinity || val == -fakeInfinity) continue; if (f.x) { if (val < xmin) xmin = val; if (val > xmax) xmax = val } if (f.y) { if (val < ymin) ymin = val; if (val > ymax) ymax = val } } } if (s.bars.show) { var delta; switch (s.bars.align) { case "left": delta = 0; break; case "right": delta = -s.bars.barWidth; break; default: delta = -s.bars.barWidth / 2 }if (s.bars.horizontal) { ymin += delta; ymax += delta + s.bars.barWidth } else { xmin += delta; xmax += delta + s.bars.barWidth } } updateAxis(s.xaxis, xmin, xmax); updateAxis(s.yaxis, ymin, ymax) } $.each(allAxes(), function (_, axis) { if (axis.datamin == topSentry) axis.datamin = null; if (axis.datamax == bottomSentry) axis.datamax = null }) } function setupCanvases() { placeholder.css("padding", 0).children().filter(function () { return !$(this).hasClass("flot-overlay") && !$(this).hasClass("flot-base") }).remove(); if (placeholder.css("position") == "static") placeholder.css("position", "relative"); surface = new Canvas("flot-base", placeholder); overlay = new Canvas("flot-overlay", placeholder); ctx = surface.context; octx = overlay.context; eventHolder = $(overlay.element).unbind(); var existing = placeholder.data("plot"); if (existing) { existing.shutdown(); overlay.clear() } placeholder.data("plot", plot) } function bindEvents() { if (options.grid.hoverable) { eventHolder.mousemove(onMouseMove); eventHolder.bind("mouseleave", onMouseLeave) } if (options.grid.clickable) eventHolder.click(onClick); executeHooks(hooks.bindEvents, [eventHolder]) } function shutdown() { if (redrawTimeout) clearTimeout(redrawTimeout); eventHolder.unbind("mousemove", onMouseMove); eventHolder.unbind("mouseleave", onMouseLeave); eventHolder.unbind("click", onClick); executeHooks(hooks.shutdown, [eventHolder]) } function setTransformationHelpers(axis) { function identity(x) { return x } var s, m, t = axis.options.transform || identity, it = axis.options.inverseTransform; if (axis.direction == "x") { s = axis.scale = plotWidth / Math.abs(t(axis.max) - t(axis.min)); m = Math.min(t(axis.max), t(axis.min)) } else { s = axis.scale = plotHeight / Math.abs(t(axis.max) - t(axis.min)); s = -s; m = Math.max(t(axis.max), t(axis.min)) } if (t == identity) axis.p2c = function (p) { return (p - m) * s }; else axis.p2c = function (p) { return (t(p) - m) * s }; if (!it) axis.c2p = function (c) { return m + c / s }; else axis.c2p = function (c) { return it(m + c / s) } } function measureTickLabels(axis) { var opts = axis.options, ticks = axis.ticks || [], labelWidth = opts.labelWidth || 0, labelHeight = opts.labelHeight || 0, maxWidth = labelWidth || (axis.direction == "x" ? Math.floor(surface.width / (ticks.length || 1)) : null), legacyStyles = axis.direction + "Axis " + axis.direction + axis.n + "Axis", layer = "flot-" + axis.direction + "-axis flot-" + axis.direction + axis.n + "-axis " + legacyStyles, font = opts.font || "flot-tick-label tickLabel"; for (var i = 0; i < ticks.length; ++i) { var t = ticks[i]; if (!t.label) continue; var info = surface.getTextInfo(layer, t.label, font, null, maxWidth); labelWidth = Math.max(labelWidth, info.width); labelHeight = Math.max(labelHeight, info.height) } axis.labelWidth = opts.labelWidth || labelWidth; axis.labelHeight = opts.labelHeight || labelHeight } function allocateAxisBoxFirstPhase(axis) { var lw = axis.labelWidth, lh = axis.labelHeight, pos = axis.options.position, isXAxis = axis.direction === "x", tickLength = axis.options.tickLength, axisMargin = options.grid.axisMargin, padding = options.grid.labelMargin, innermost = true, outermost = true, first = true, found = false; $.each(isXAxis ? xaxes : yaxes, function (i, a) { if (a && (a.show || a.reserveSpace)) { if (a === axis) { found = true } else if (a.options.position === pos) { if (found) { outermost = false } else { innermost = false } } if (!found) { first = false } } }); if (outermost) { axisMargin = 0 } if (tickLength == null) { tickLength = first ? "full" : 5 } if (!isNaN(+tickLength)) padding += +tickLength; if (isXAxis) { lh += padding; if (pos == "bottom") { plotOffset.bottom += lh + axisMargin; axis.box = { top: surface.height - plotOffset.bottom, height: lh } } else { axis.box = { top: plotOffset.top + axisMargin, height: lh }; plotOffset.top += lh + axisMargin } } else { lw += padding; if (pos == "left") { axis.box = { left: plotOffset.left + axisMargin, width: lw }; plotOffset.left += lw + axisMargin } else { plotOffset.right += lw + axisMargin; axis.box = { left: surface.width - plotOffset.right, width: lw } } } axis.position = pos; axis.tickLength = tickLength; axis.box.padding = padding; axis.innermost = innermost } function allocateAxisBoxSecondPhase(axis) { if (axis.direction == "x") { axis.box.left = plotOffset.left - axis.labelWidth / 2; axis.box.width = surface.width - plotOffset.left - plotOffset.right + axis.labelWidth } else { axis.box.top = plotOffset.top - axis.labelHeight / 2; axis.box.height = surface.height - plotOffset.bottom - plotOffset.top + axis.labelHeight } } function adjustLayoutForThingsStickingOut() { var minMargin = options.grid.minBorderMargin, axis, i; if (minMargin == null) { minMargin = 0; for (i = 0; i < series.length; ++i)minMargin = Math.max(minMargin, 2 * (series[i].points.radius + series[i].points.lineWidth / 2)) } var margins = { left: minMargin, right: minMargin, top: minMargin, bottom: minMargin }; $.each(allAxes(), function (_, axis) { if (axis.reserveSpace && axis.ticks && axis.ticks.length) { if (axis.direction === "x") { margins.left = Math.max(margins.left, axis.labelWidth / 2); margins.right = Math.max(margins.right, axis.labelWidth / 2) } else { margins.bottom = Math.max(margins.bottom, axis.labelHeight / 2); margins.top = Math.max(margins.top, axis.labelHeight / 2) } } }); plotOffset.left = Math.ceil(Math.max(margins.left, plotOffset.left)); plotOffset.right = Math.ceil(Math.max(margins.right, plotOffset.right)); plotOffset.top = Math.ceil(Math.max(margins.top, plotOffset.top)); plotOffset.bottom = Math.ceil(Math.max(margins.bottom, plotOffset.bottom)) } function setupGrid() { var i, axes = allAxes(), showGrid = options.grid.show; for (var a in plotOffset) { var margin = options.grid.margin || 0; plotOffset[a] = typeof margin == "number" ? margin : margin[a] || 0 } executeHooks(hooks.processOffset, [plotOffset]); for (var a in plotOffset) { if (typeof options.grid.borderWidth == "object") { plotOffset[a] += showGrid ? options.grid.borderWidth[a] : 0 } else { plotOffset[a] += showGrid ? options.grid.borderWidth : 0 } } $.each(axes, function (_, axis) { var axisOpts = axis.options; axis.show = axisOpts.show == null ? axis.used : axisOpts.show; axis.reserveSpace = axisOpts.reserveSpace == null ? axis.show : axisOpts.reserveSpace; setRange(axis) }); if (showGrid) { var allocatedAxes = $.grep(axes, function (axis) { return axis.show || axis.reserveSpace }); $.each(allocatedAxes, function (_, axis) { setupTickGeneration(axis); setTicks(axis); snapRangeToTicks(axis, axis.ticks); measureTickLabels(axis) }); for (i = allocatedAxes.length - 1; i >= 0; --i)allocateAxisBoxFirstPhase(allocatedAxes[i]); adjustLayoutForThingsStickingOut(); $.each(allocatedAxes, function (_, axis) { allocateAxisBoxSecondPhase(axis) }) } plotWidth = surface.width - plotOffset.left - plotOffset.right; plotHeight = surface.height - plotOffset.bottom - plotOffset.top; $.each(axes, function (_, axis) { setTransformationHelpers(axis) }); if (showGrid) { drawAxisLabels() } insertLegend() } function setRange(axis) { var opts = axis.options, min = +(opts.min != null ? opts.min : axis.datamin), max = +(opts.max != null ? opts.max : axis.datamax), delta = max - min; if (delta == 0) { var widen = max == 0 ? 1 : .01; if (opts.min == null) min -= widen; if (opts.max == null || opts.min != null) max += widen } else { var margin = opts.autoscaleMargin; if (margin != null) { if (opts.min == null) { min -= delta * margin; if (min < 0 && axis.datamin != null && axis.datamin >= 0) min = 0 } if (opts.max == null) { max += delta * margin; if (max > 0 && axis.datamax != null && axis.datamax <= 0) max = 0 } } } axis.min = min; axis.max = max } function setupTickGeneration(axis) { var opts = axis.options; var noTicks; if (typeof opts.ticks == "number" && opts.ticks > 0) noTicks = opts.ticks; else noTicks = .3 * Math.sqrt(axis.direction == "x" ? surface.width : surface.height); var delta = (axis.max - axis.min) / noTicks, dec = -Math.floor(Math.log(delta) / Math.LN10), maxDec = opts.tickDecimals; if (maxDec != null && dec > maxDec) { dec = maxDec } var magn = Math.pow(10, -dec), norm = delta / magn, size; if (norm < 1.5) { size = 1 } else if (norm < 3) { size = 2; if (norm > 2.25 && (maxDec == null || dec + 1 <= maxDec)) { size = 2.5; ++dec } } else if (norm < 7.5) { size = 5 } else { size = 10 } size *= magn; if (opts.minTickSize != null && size < opts.minTickSize) { size = opts.minTickSize } axis.delta = delta; axis.tickDecimals = Math.max(0, maxDec != null ? maxDec : dec); axis.tickSize = opts.tickSize || size; if (opts.mode == "time" && !axis.tickGenerator) { throw new Error("Time mode requires the flot.time plugin.") } if (!axis.tickGenerator) { axis.tickGenerator = function (axis) { var ticks = [], start = floorInBase(axis.min, axis.tickSize), i = 0, v = Number.NaN, prev; do { prev = v; v = start + i * axis.tickSize; ticks.push(v); ++i } while (v < axis.max && v != prev); return ticks }; axis.tickFormatter = function (value, axis) { var factor = axis.tickDecimals ? Math.pow(10, axis.tickDecimals) : 1; var formatted = "" + Math.round(value * factor) / factor; if (axis.tickDecimals != null) { var decimal = formatted.indexOf("."); var precision = decimal == -1 ? 0 : formatted.length - decimal - 1; if (precision < axis.tickDecimals) { return (precision ? formatted : formatted + ".") + ("" + factor).substr(1, axis.tickDecimals - precision) } } return formatted } } if ($.isFunction(opts.tickFormatter)) axis.tickFormatter = function (v, axis) { return "" + opts.tickFormatter(v, axis) }; if (opts.alignTicksWithAxis != null) { var otherAxis = (axis.direction == "x" ? xaxes : yaxes)[opts.alignTicksWithAxis - 1]; if (otherAxis && otherAxis.used && otherAxis != axis) { var niceTicks = axis.tickGenerator(axis); if (niceTicks.length > 0) { if (opts.min == null) axis.min = Math.min(axis.min, niceTicks[0]); if (opts.max == null && niceTicks.length > 1) axis.max = Math.max(axis.max, niceTicks[niceTicks.length - 1]) } axis.tickGenerator = function (axis) { var ticks = [], v, i; for (i = 0; i < otherAxis.ticks.length; ++i) { v = (otherAxis.ticks[i].v - otherAxis.min) / (otherAxis.max - otherAxis.min); v = axis.min + v * (axis.max - axis.min); ticks.push(v) } return ticks }; if (!axis.mode && opts.tickDecimals == null) { var extraDec = Math.max(0, -Math.floor(Math.log(axis.delta) / Math.LN10) + 1), ts = axis.tickGenerator(axis); if (!(ts.length > 1 && /\..*0$/.test((ts[1] - ts[0]).toFixed(extraDec)))) axis.tickDecimals = extraDec } } } } function setTicks(axis) { var oticks = axis.options.ticks, ticks = []; if (oticks == null || typeof oticks == "number" && oticks > 0) ticks = axis.tickGenerator(axis); else if (oticks) { if ($.isFunction(oticks)) ticks = oticks(axis); else ticks = oticks } var i, v; axis.ticks = []; for (i = 0; i < ticks.length; ++i) { var label = null; var t = ticks[i]; if (typeof t == "object") { v = +t[0]; if (t.length > 1) label = t[1] } else v = +t; if (label == null) label = axis.tickFormatter(v, axis); if (!isNaN(v)) axis.ticks.push({ v: v, label: label }) } } function snapRangeToTicks(axis, ticks) { if (axis.options.autoscaleMargin && ticks.length > 0) { if (axis.options.min == null) axis.min = Math.min(axis.min, ticks[0].v); if (axis.options.max == null && ticks.length > 1) axis.max = Math.max(axis.max, ticks[ticks.length - 1].v) } } function draw() { surface.clear(); executeHooks(hooks.drawBackground, [ctx]); var grid = options.grid; if (grid.show && grid.backgroundColor) drawBackground(); if (grid.show && !grid.aboveData) { drawGrid() } for (var i = 0; i < series.length; ++i) { executeHooks(hooks.drawSeries, [ctx, series[i]]); drawSeries(series[i]) } executeHooks(hooks.draw, [ctx]); if (grid.show && grid.aboveData) { drawGrid() } surface.render(); triggerRedrawOverlay() } function extractRange(ranges, coord) { var axis, from, to, key, axes = allAxes(); for (var i = 0; i < axes.length; ++i) { axis = axes[i]; if (axis.direction == coord) { key = coord + axis.n + "axis"; if (!ranges[key] && axis.n == 1) key = coord + "axis"; if (ranges[key]) { from = ranges[key].from; to = ranges[key].to; break } } } if (!ranges[key]) { axis = coord == "x" ? xaxes[0] : yaxes[0]; from = ranges[coord + "1"]; to = ranges[coord + "2"] } if (from != null && to != null && from > to) { var tmp = from; from = to; to = tmp } return { from: from, to: to, axis: axis } } function drawBackground() { ctx.save(); ctx.translate(plotOffset.left, plotOffset.top); ctx.fillStyle = getColorOrGradient(options.grid.backgroundColor, plotHeight, 0, "rgba(255, 255, 255, 0)"); ctx.fillRect(0, 0, plotWidth, plotHeight); ctx.restore() } function drawGrid() {
var i, axes, bw, bc; ctx.save(); ctx.translate(plotOffset.left, plotOffset.top); var markings = options.grid.markings; if (markings) {
if ($.isFunction(markings)) { axes = plot.getAxes(); axes.xmin = axes.xaxis.min; axes.xmax = axes.xaxis.max; axes.ymin = axes.yaxis.min; axes.ymax = axes.yaxis.max; markings = markings(axes) } for (i = 0; i < markings.length; ++i) {
var m = markings[i], xrange = extractRange(m, "x"), yrange = extractRange(m, "y"); if (xrange.from == null) xrange.from = xrange.axis.min; if (xrange.to == null) xrange.to = xrange.axis.max;
if (yrange.from == null) yrange.from = yrange.axis.min; if (yrange.to == null) yrange.to = yrange.axis.max; if (xrange.to < xrange.axis.min || xrange.from > xrange.axis.max || yrange.to < yrange.axis.min || yrange.from > yrange.axis.max) continue; xrange.from = Math.max(xrange.from, xrange.axis.min); xrange.to = Math.min(xrange.to, xrange.axis.max); yrange.from = Math.max(yrange.from, yrange.axis.min); yrange.to = Math.min(yrange.to, yrange.axis.max); var xequal = xrange.from === xrange.to, yequal = yrange.from === yrange.to; if (xequal && yequal) { continue } xrange.from = Math.floor(xrange.axis.p2c(xrange.from)); xrange.to = Math.floor(xrange.axis.p2c(xrange.to)); yrange.from = Math.floor(yrange.axis.p2c(yrange.from)); yrange.to = Math.floor(yrange.axis.p2c(yrange.to)); if (xequal || yequal) { var lineWidth = m.lineWidth || options.grid.markingsLineWidth, subPixel = lineWidth % 2 ? .5 : 0; ctx.beginPath(); ctx.strokeStyle = m.color || options.grid.markingsColor; ctx.lineWidth = lineWidth; if (xequal) { ctx.moveTo(xrange.to + subPixel, yrange.from); ctx.lineTo(xrange.to + subPixel, yrange.to) } else { ctx.moveTo(xrange.from, yrange.to + subPixel); ctx.lineTo(xrange.to, yrange.to + subPixel) } ctx.stroke() } else { ctx.fillStyle = m.color || options.grid.markingsColor; ctx.fillRect(xrange.from, yrange.to, xrange.to - xrange.from, yrange.from - yrange.to) }
}
} axes = allAxes(); bw = options.grid.borderWidth; for (var j = 0; j < axes.length; ++j) { var axis = axes[j], box = axis.box, t = axis.tickLength, x, y, xoff, yoff; if (!axis.show || axis.ticks.length == 0) continue; ctx.lineWidth = 1; if (axis.direction == "x") { x = 0; if (t == "full") y = axis.position == "top" ? 0 : plotHeight; else y = box.top - plotOffset.top + (axis.position == "top" ? box.height : 0) } else { y = 0; if (t == "full") x = axis.position == "left" ? 0 : plotWidth; else x = box.left - plotOffset.left + (axis.position == "left" ? box.width : 0) } if (!axis.innermost) { ctx.strokeStyle = axis.options.color; ctx.beginPath(); xoff = yoff = 0; if (axis.direction == "x") xoff = plotWidth + 1; else yoff = plotHeight + 1; if (ctx.lineWidth == 1) { if (axis.direction == "x") { y = Math.floor(y) + .5 } else { x = Math.floor(x) + .5 } } ctx.moveTo(x, y); ctx.lineTo(x + xoff, y + yoff); ctx.stroke() } ctx.strokeStyle = axis.options.tickColor; ctx.beginPath(); for (i = 0; i < axis.ticks.length; ++i) { var v = axis.ticks[i].v; xoff = yoff = 0; if (isNaN(v) || v < axis.min || v > axis.max || t == "full" && (typeof bw == "object" && bw[axis.position] > 0 || bw > 0) && (v == axis.min || v == axis.max)) continue; if (axis.direction == "x") { x = axis.p2c(v); yoff = t == "full" ? -plotHeight : t; if (axis.position == "top") yoff = -yoff } else { y = axis.p2c(v); xoff = t == "full" ? -plotWidth : t; if (axis.position == "left") xoff = -xoff } if (ctx.lineWidth == 1) { if (axis.direction == "x") x = Math.floor(x) + .5; else y = Math.floor(y) + .5 } ctx.moveTo(x, y); ctx.lineTo(x + xoff, y + yoff) } ctx.stroke() } if (bw) { bc = options.grid.borderColor; if (typeof bw == "object" || typeof bc == "object") { if (typeof bw !== "object") { bw = { top: bw, right: bw, bottom: bw, left: bw } } if (typeof bc !== "object") { bc = { top: bc, right: bc, bottom: bc, left: bc } } if (bw.top > 0) { ctx.strokeStyle = bc.top; ctx.lineWidth = bw.top; ctx.beginPath(); ctx.moveTo(0 - bw.left, 0 - bw.top / 2); ctx.lineTo(plotWidth, 0 - bw.top / 2); ctx.stroke() } if (bw.right > 0) { ctx.strokeStyle = bc.right; ctx.lineWidth = bw.right; ctx.beginPath(); ctx.moveTo(plotWidth + bw.right / 2, 0 - bw.top); ctx.lineTo(plotWidth + bw.right / 2, plotHeight); ctx.stroke() } if (bw.bottom > 0) { ctx.strokeStyle = bc.bottom; ctx.lineWidth = bw.bottom; ctx.beginPath(); ctx.moveTo(plotWidth + bw.right, plotHeight + bw.bottom / 2); ctx.lineTo(0, plotHeight + bw.bottom / 2); ctx.stroke() } if (bw.left > 0) { ctx.strokeStyle = bc.left; ctx.lineWidth = bw.left; ctx.beginPath(); ctx.moveTo(0 - bw.left / 2, plotHeight + bw.bottom); ctx.lineTo(0 - bw.left / 2, 0); ctx.stroke() } } else { ctx.lineWidth = bw; ctx.strokeStyle = options.grid.borderColor; ctx.strokeRect(-bw / 2, -bw / 2, plotWidth + bw, plotHeight + bw) } } ctx.restore()
} function drawAxisLabels() { $.each(allAxes(), function (_, axis) { var box = axis.box, legacyStyles = axis.direction + "Axis " + axis.direction + axis.n + "Axis", layer = "flot-" + axis.direction + "-axis flot-" + axis.direction + axis.n + "-axis " + legacyStyles, font = axis.options.font || "flot-tick-label tickLabel", tick, x, y, halign, valign; surface.removeText(layer); if (!axis.show || axis.ticks.length == 0) return; for (var i = 0; i < axis.ticks.length; ++i) { tick = axis.ticks[i]; if (!tick.label || tick.v < axis.min || tick.v > axis.max) continue; if (axis.direction == "x") { halign = "center"; x = plotOffset.left + axis.p2c(tick.v); if (axis.position == "bottom") { y = box.top + box.padding } else { y = box.top + box.height - box.padding; valign = "bottom" } } else { valign = "middle"; y = plotOffset.top + axis.p2c(tick.v); if (axis.position == "left") { x = box.left + box.width - box.padding; halign = "right" } else { x = box.left + box.padding } } surface.addText(layer, x, y, tick.label, font, null, null, halign, valign) } }) } function drawSeries(series) { if (series.lines.show) drawSeriesLines(series); if (series.bars.show) drawSeriesBars(series); if (series.points.show) drawSeriesPoints(series) } function drawSeriesLines(series) { function plotLine(datapoints, xoffset, yoffset, axisx, axisy) { var points = datapoints.points, ps = datapoints.pointsize, prevx = null, prevy = null; ctx.beginPath(); for (var i = ps; i < points.length; i += ps) { var x1 = points[i - ps], y1 = points[i - ps + 1], x2 = points[i], y2 = points[i + 1]; if (x1 == null || x2 == null) continue; if (y1 <= y2 && y1 < axisy.min) { if (y2 < axisy.min) continue; x1 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1; y1 = axisy.min } else if (y2 <= y1 && y2 < axisy.min) { if (y1 < axisy.min) continue; x2 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1; y2 = axisy.min } if (y1 >= y2 && y1 > axisy.max) { if (y2 > axisy.max) continue; x1 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1; y1 = axisy.max } else if (y2 >= y1 && y2 > axisy.max) { if (y1 > axisy.max) continue; x2 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1; y2 = axisy.max } if (x1 <= x2 && x1 < axisx.min) { if (x2 < axisx.min) continue; y1 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1; x1 = axisx.min } else if (x2 <= x1 && x2 < axisx.min) { if (x1 < axisx.min) continue; y2 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1; x2 = axisx.min } if (x1 >= x2 && x1 > axisx.max) { if (x2 > axisx.max) continue; y1 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1; x1 = axisx.max } else if (x2 >= x1 && x2 > axisx.max) { if (x1 > axisx.max) continue; y2 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1; x2 = axisx.max } if (x1 != prevx || y1 != prevy) ctx.moveTo(axisx.p2c(x1) + xoffset, axisy.p2c(y1) + yoffset); prevx = x2; prevy = y2; ctx.lineTo(axisx.p2c(x2) + xoffset, axisy.p2c(y2) + yoffset) } ctx.stroke() } function plotLineArea(datapoints, axisx, axisy) { var points = datapoints.points, ps = datapoints.pointsize, bottom = Math.min(Math.max(0, axisy.min), axisy.max), i = 0, top, areaOpen = false, ypos = 1, segmentStart = 0, segmentEnd = 0; while (true) { if (ps > 0 && i > points.length + ps) break; i += ps; var x1 = points[i - ps], y1 = points[i - ps + ypos], x2 = points[i], y2 = points[i + ypos]; if (areaOpen) { if (ps > 0 && x1 != null && x2 == null) { segmentEnd = i; ps = -ps; ypos = 2; continue } if (ps < 0 && i == segmentStart + ps) { ctx.fill(); areaOpen = false; ps = -ps; ypos = 1; i = segmentStart = segmentEnd + ps; continue } } if (x1 == null || x2 == null) continue; if (x1 <= x2 && x1 < axisx.min) { if (x2 < axisx.min) continue; y1 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1; x1 = axisx.min } else if (x2 <= x1 && x2 < axisx.min) { if (x1 < axisx.min) continue; y2 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1; x2 = axisx.min } if (x1 >= x2 && x1 > axisx.max) { if (x2 > axisx.max) continue; y1 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1; x1 = axisx.max } else if (x2 >= x1 && x2 > axisx.max) { if (x1 > axisx.max) continue; y2 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1; x2 = axisx.max } if (!areaOpen) { ctx.beginPath(); ctx.moveTo(axisx.p2c(x1), axisy.p2c(bottom)); areaOpen = true } if (y1 >= axisy.max && y2 >= axisy.max) { ctx.lineTo(axisx.p2c(x1), axisy.p2c(axisy.max)); ctx.lineTo(axisx.p2c(x2), axisy.p2c(axisy.max)); continue } else if (y1 <= axisy.min && y2 <= axisy.min) { ctx.lineTo(axisx.p2c(x1), axisy.p2c(axisy.min)); ctx.lineTo(axisx.p2c(x2), axisy.p2c(axisy.min)); continue } var x1old = x1, x2old = x2; if (y1 <= y2 && y1 < axisy.min && y2 >= axisy.min) { x1 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1; y1 = axisy.min } else if (y2 <= y1 && y2 < axisy.min && y1 >= axisy.min) { x2 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1; y2 = axisy.min } if (y1 >= y2 && y1 > axisy.max && y2 <= axisy.max) { x1 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1; y1 = axisy.max } else if (y2 >= y1 && y2 > axisy.max && y1 <= axisy.max) { x2 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1; y2 = axisy.max } if (x1 != x1old) { ctx.lineTo(axisx.p2c(x1old), axisy.p2c(y1)) } ctx.lineTo(axisx.p2c(x1), axisy.p2c(y1)); ctx.lineTo(axisx.p2c(x2), axisy.p2c(y2)); if (x2 != x2old) { ctx.lineTo(axisx.p2c(x2), axisy.p2c(y2)); ctx.lineTo(axisx.p2c(x2old), axisy.p2c(y2)) } } } ctx.save(); ctx.translate(plotOffset.left, plotOffset.top); ctx.lineJoin = "round"; var lw = series.lines.lineWidth, sw = series.shadowSize; if (lw > 0 && sw > 0) { ctx.lineWidth = sw; ctx.strokeStyle = "rgba(0,0,0,0.1)"; var angle = Math.PI / 18; plotLine(series.datapoints, Math.sin(angle) * (lw / 2 + sw / 2), Math.cos(angle) * (lw / 2 + sw / 2), series.xaxis, series.yaxis); ctx.lineWidth = sw / 2; plotLine(series.datapoints, Math.sin(angle) * (lw / 2 + sw / 4), Math.cos(angle) * (lw / 2 + sw / 4), series.xaxis, series.yaxis) } ctx.lineWidth = lw; ctx.strokeStyle = series.color; var fillStyle = getFillStyle(series.lines, series.color, 0, plotHeight); if (fillStyle) { ctx.fillStyle = fillStyle; plotLineArea(series.datapoints, series.xaxis, series.yaxis) } if (lw > 0) plotLine(series.datapoints, 0, 0, series.xaxis, series.yaxis); ctx.restore() } function drawSeriesPoints(series) { function plotPoints(datapoints, radius, fillStyle, offset, shadow, axisx, axisy, symbol) { var points = datapoints.points, ps = datapoints.pointsize; for (var i = 0; i < points.length; i += ps) { var x = points[i], y = points[i + 1]; if (x == null || x < axisx.min || x > axisx.max || y < axisy.min || y > axisy.max) continue; ctx.beginPath(); x = axisx.p2c(x); y = axisy.p2c(y) + offset; if (symbol == "circle") ctx.arc(x, y, radius, 0, shadow ? Math.PI : Math.PI * 2, false); else symbol(ctx, x, y, radius, shadow); ctx.closePath(); if (fillStyle) { ctx.fillStyle = fillStyle; ctx.fill() } ctx.stroke() } } ctx.save(); ctx.translate(plotOffset.left, plotOffset.top); var lw = series.points.lineWidth, sw = series.shadowSize, radius = series.points.radius, symbol = series.points.symbol; if (lw == 0) lw = 1e-4; if (lw > 0 && sw > 0) { var w = sw / 2; ctx.lineWidth = w; ctx.strokeStyle = "rgba(0,0,0,0.1)"; plotPoints(series.datapoints, radius, null, w + w / 2, true, series.xaxis, series.yaxis, symbol); ctx.strokeStyle = "rgba(0,0,0,0.2)"; plotPoints(series.datapoints, radius, null, w / 2, true, series.xaxis, series.yaxis, symbol) } ctx.lineWidth = lw; ctx.strokeStyle = series.color; plotPoints(series.datapoints, radius, getFillStyle(series.points, series.color), 0, false, series.xaxis, series.yaxis, symbol); ctx.restore() } function drawBar(x, y, b, barLeft, barRight, fillStyleCallback, axisx, axisy, c, horizontal, lineWidth) { var left, right, bottom, top, drawLeft, drawRight, drawTop, drawBottom, tmp; if (horizontal) { drawBottom = drawRight = drawTop = true; drawLeft = false; left = b; right = x; top = y + barLeft; bottom = y + barRight; if (right < left) { tmp = right; right = left; left = tmp; drawLeft = true; drawRight = false } } else { drawLeft = drawRight = drawTop = true; drawBottom = false; left = x + barLeft; right = x + barRight; bottom = b; top = y; if (top < bottom) { tmp = top; top = bottom; bottom = tmp; drawBottom = true; drawTop = false } } if (right < axisx.min || left > axisx.max || top < axisy.min || bottom > axisy.max) return; if (left < axisx.min) { left = axisx.min; drawLeft = false } if (right > axisx.max) { right = axisx.max; drawRight = false } if (bottom < axisy.min) { bottom = axisy.min; drawBottom = false } if (top > axisy.max) { top = axisy.max; drawTop = false } left = axisx.p2c(left); bottom = axisy.p2c(bottom); right = axisx.p2c(right); top = axisy.p2c(top); if (fillStyleCallback) { c.fillStyle = fillStyleCallback(bottom, top); c.fillRect(left, top, right - left, bottom - top) } if (lineWidth > 0 && (drawLeft || drawRight || drawTop || drawBottom)) { c.beginPath(); c.moveTo(left, bottom); if (drawLeft) c.lineTo(left, top); else c.moveTo(left, top); if (drawTop) c.lineTo(right, top); else c.moveTo(right, top); if (drawRight) c.lineTo(right, bottom); else c.moveTo(right, bottom); if (drawBottom) c.lineTo(left, bottom); else c.moveTo(left, bottom); c.stroke() } } function drawSeriesBars(series) { function plotBars(datapoints, barLeft, barRight, fillStyleCallback, axisx, axisy) { var points = datapoints.points, ps = datapoints.pointsize; for (var i = 0; i < points.length; i += ps) { if (points[i] == null) continue; drawBar(points[i], points[i + 1], points[i + 2], barLeft, barRight, fillStyleCallback, axisx, axisy, ctx, series.bars.horizontal, series.bars.lineWidth) } } ctx.save(); ctx.translate(plotOffset.left, plotOffset.top); ctx.lineWidth = series.bars.lineWidth; ctx.strokeStyle = series.color; var barLeft; switch (series.bars.align) { case "left": barLeft = 0; break; case "right": barLeft = -series.bars.barWidth; break; default: barLeft = -series.bars.barWidth / 2 }var fillStyleCallback = series.bars.fill ? function (bottom, top) { return getFillStyle(series.bars, series.color, bottom, top) } : null; plotBars(series.datapoints, barLeft, barLeft + series.bars.barWidth, fillStyleCallback, series.xaxis, series.yaxis); ctx.restore() } function getFillStyle(filloptions, seriesColor, bottom, top) { var fill = filloptions.fill; if (!fill) return null; if (filloptions.fillColor) return getColorOrGradient(filloptions.fillColor, bottom, top, seriesColor); var c = $.color.parse(seriesColor); c.a = typeof fill == "number" ? fill : .4; c.normalize(); return c.toString() } function insertLegend() { if (options.legend.container != null) { $(options.legend.container).html("") } else { placeholder.find(".legend").remove() } if (!options.legend.show) { return } var fragments = [], entries = [], rowStarted = false, lf = options.legend.labelFormatter, s, label; for (var i = 0; i < series.length; ++i) { s = series[i]; if (s.label) { label = lf ? lf(s.label, s) : s.label; if (label) { entries.push({ label: label, color: s.color }) } } } if (options.legend.sorted) { if ($.isFunction(options.legend.sorted)) { entries.sort(options.legend.sorted) } else if (options.legend.sorted == "reverse") { entries.reverse() } else { var ascending = options.legend.sorted != "descending"; entries.sort(function (a, b) { return a.label == b.label ? 0 : a.label < b.label != ascending ? 1 : -1 }) } } for (var i = 0; i < entries.length; ++i) { var entry = entries[i]; if (i % options.legend.noColumns == 0) { if (rowStarted) fragments.push("</tr>"); fragments.push("<tr>"); rowStarted = true } fragments.push('<td class="legendColorBox"><div style="border:1px solid ' + options.legend.labelBoxBorderColor + ';padding:1px"><div style="width:4px;height:0;border:5px solid ' + entry.color + ';overflow:hidden"></div></div></td>' + '<td class="legendLabel">' + entry.label.replace("font-size: 9pt;", "") + "</td>") } if (rowStarted) fragments.push("</tr>"); if (fragments.length == 0) return; var table = '<table style="font-size:smaller;color:' + options.grid.color + '">' + fragments.join("") + "</table>"; if (options.legend.container != null) $(options.legend.container).html(table); else { var pos = "", p = options.legend.position, m = options.legend.margin; if (m[0] == null) m = [m, m]; if (p.charAt(0) == "n") pos += "top:" + (m[1] + plotOffset.top) + "px;"; else if (p.charAt(0) == "s") pos += "bottom:" + (m[1] + plotOffset.bottom) + "px;"; if (p.charAt(1) == "e") pos += "right:" + (m[0] + plotOffset.right) + "px;"; else if (p.charAt(1) == "w") pos += "left:" + (m[0] + plotOffset.left) + "px;"; var legend = $('<div class="legend">' + table.replace('style="', 'style="position:absolute;' + pos + ";") + "</div>").appendTo(placeholder); if (options.legend.backgroundOpacity != 0) { var c = options.legend.backgroundColor; if (c == null) { c = options.grid.backgroundColor; if (c && typeof c == "string") c = $.color.parse(c); else c = $.color.extract(legend, "background-color"); c.a = 1; c = c.toString() } var div = legend.children(); $('<div class="bg_222_important" style="position:absolute;width:' + div.width() + "px;height:" + div.height() + "px;" + pos + "background-color:" + c + ';"> </div>').prependTo(legend).css("opacity", options.legend.backgroundOpacity) } } } var highlights = [], redrawTimeout = null; function findNearbyItem(mouseX, mouseY, seriesFilter) { var maxDistance = options.grid.mouseActiveRadius, smallestDistance = maxDistance * maxDistance + 1, item = null, foundPoint = false, i, j, ps; for (i = series.length - 1; i >= 0; --i) { if (!seriesFilter(series[i])) continue; var s = series[i], axisx = s.xaxis, axisy = s.yaxis, points = s.datapoints.points, mx = axisx.c2p(mouseX), my = axisy.c2p(mouseY), maxx = maxDistance / axisx.scale, maxy = maxDistance / axisy.scale; ps = s.datapoints.pointsize; if (axisx.options.inverseTransform) maxx = Number.MAX_VALUE; if (axisy.options.inverseTransform) maxy = Number.MAX_VALUE; if (s.lines.show || s.points.show) { for (j = 0; j < points.length; j += ps) { var x = points[j], y = points[j + 1]; if (x == null) continue; if (x - mx > maxx || x - mx < -maxx || y - my > maxy || y - my < -maxy) continue; var dx = Math.abs(axisx.p2c(x) - mouseX), dy = Math.abs(axisy.p2c(y) - mouseY), dist = dx * dx + dy * dy; if (dist < smallestDistance) { smallestDistance = dist; item = [i, j / ps] } } } if (s.bars.show && !item) { var barLeft, barRight; switch (s.bars.align) { case "left": barLeft = 0; break; case "right": barLeft = -s.bars.barWidth; break; default: barLeft = -s.bars.barWidth / 2 }barRight = barLeft + s.bars.barWidth; for (j = 0; j < points.length; j += ps) { var x = points[j], y = points[j + 1], b = points[j + 2]; if (x == null) continue; if (series[i].bars.horizontal ? mx <= Math.max(b, x) && mx >= Math.min(b, x) && my >= y + barLeft && my <= y + barRight : mx >= x + barLeft && mx <= x + barRight && my >= Math.min(b, y) && my <= Math.max(b, y)) item = [i, j / ps] } } } if (item) { i = item[0]; j = item[1]; ps = series[i].datapoints.pointsize; return { datapoint: series[i].datapoints.points.slice(j * ps, (j + 1) * ps), dataIndex: j, series: series[i], seriesIndex: i } } return null } function onMouseMove(e) { if (options.grid.hoverable) triggerClickHoverEvent("plothover", e, function (s) { return s["hoverable"] != false }) } function onMouseLeave(e) { if (options.grid.hoverable) triggerClickHoverEvent("plothover", e, function (s) { return false }) } function onClick(e) { triggerClickHoverEvent("plotclick", e, function (s) { return s["clickable"] != false }) } function triggerClickHoverEvent(eventname, event, seriesFilter) { var offset = eventHolder.offset(), canvasX = event.pageX - offset.left - plotOffset.left, canvasY = event.pageY - offset.top - plotOffset.top, pos = canvasToAxisCoords({ left: canvasX, top: canvasY }); pos.pageX = event.pageX; pos.pageY = event.pageY; var item = findNearbyItem(canvasX, canvasY, seriesFilter); if (item) { item.pageX = parseInt(item.series.xaxis.p2c(item.datapoint[0]) + offset.left + plotOffset.left, 10); item.pageY = parseInt(item.series.yaxis.p2c(item.datapoint[1]) + offset.top + plotOffset.top, 10) } if (options.grid.autoHighlight) { for (var i = 0; i < highlights.length; ++i) { var h = highlights[i]; if (h.auto == eventname && !(item && h.series == item.series && h.point[0] == item.datapoint[0] && h.point[1] == item.datapoint[1])) unhighlight(h.series, h.point) } if (item) highlight(item.series, item.datapoint, eventname) } placeholder.trigger(eventname, [pos, item]) } function triggerRedrawOverlay() { var t = options.interaction.redrawOverlayInterval; if (t == -1) { drawOverlay(); return } if (!redrawTimeout) redrawTimeout = setTimeout(drawOverlay, t) } function drawOverlay() { redrawTimeout = null; octx.save(); overlay.clear(); octx.translate(plotOffset.left, plotOffset.top); var i, hi; for (i = 0; i < highlights.length; ++i) { hi = highlights[i]; if (hi.series.bars.show) drawBarHighlight(hi.series, hi.point); else drawPointHighlight(hi.series, hi.point) } octx.restore(); executeHooks(hooks.drawOverlay, [octx]) } function highlight(s, point, auto) { if (typeof s == "number") s = series[s]; if (typeof point == "number") { var ps = s.datapoints.pointsize; point = s.datapoints.points.slice(ps * point, ps * (point + 1)) } var i = indexOfHighlight(s, point); if (i == -1) { highlights.push({ series: s, point: point, auto: auto }); triggerRedrawOverlay() } else if (!auto) highlights[i].auto = false } function unhighlight(s, point) { if (s == null && point == null) { highlights = []; triggerRedrawOverlay(); return } if (typeof s == "number") s = series[s]; if (typeof point == "number") { var ps = s.datapoints.pointsize; point = s.datapoints.points.slice(ps * point, ps * (point + 1)) } var i = indexOfHighlight(s, point); if (i != -1) { highlights.splice(i, 1); triggerRedrawOverlay() } } function indexOfHighlight(s, p) { for (var i = 0; i < highlights.length; ++i) { var h = highlights[i]; if (h.series == s && h.point[0] == p[0] && h.point[1] == p[1]) return i } return -1 } function drawPointHighlight(series, point) { var x = point[0], y = point[1], axisx = series.xaxis, axisy = series.yaxis, highlightColor = typeof series.highlightColor === "string" ? series.highlightColor : $.color.parse(series.color).scale("a", .5).toString(); if (x < axisx.min || x > axisx.max || y < axisy.min || y > axisy.max) return; var pointRadius = series.points.radius + series.points.lineWidth / 2; octx.lineWidth = pointRadius; octx.strokeStyle = highlightColor; var radius = 1.5 * pointRadius; x = axisx.p2c(x); y = axisy.p2c(y); octx.beginPath(); if (series.points.symbol == "circle") octx.arc(x, y, radius, 0, 2 * Math.PI, false); else series.points.symbol(octx, x, y, radius, false); octx.closePath(); octx.stroke() } function drawBarHighlight(series, point) { var highlightColor = typeof series.highlightColor === "string" ? series.highlightColor : $.color.parse(series.color).scale("a", .5).toString(), fillStyle = highlightColor, barLeft; switch (series.bars.align) { case "left": barLeft = 0; break; case "right": barLeft = -series.bars.barWidth; break; default: barLeft = -series.bars.barWidth / 2 }octx.lineWidth = series.bars.lineWidth; octx.strokeStyle = highlightColor; drawBar(point[0], point[1], point[2] || 0, barLeft, barLeft + series.bars.barWidth, function () { return fillStyle }, series.xaxis, series.yaxis, octx, series.bars.horizontal, series.bars.lineWidth) } function getColorOrGradient(spec, bottom, top, defaultColor) { if (typeof spec == "string") return spec; else { var gradient = ctx.createLinearGradient(0, top, 0, bottom); for (var i = 0, l = spec.colors.length; i < l; ++i) { var c = spec.colors[i]; if (typeof c != "string") { var co = $.color.parse(defaultColor); if (c.brightness != null) co = co.scale("rgb", c.brightness); if (c.opacity != null) co.a *= c.opacity; c = co.toString() } gradient.addColorStop(i / (l - 1), c) } return gradient } }
} $.plot = function (placeholder, data, options) { var plot = new Plot($(placeholder), data, options, $.plot.plugins); return plot }; $.plot.version = "0.8.3"; $.plot.plugins = []; $.fn.plot = function (data, options) { return this.each(function () { $.plot(this, data, options) }) }; function floorInBase(n, base) { return base * Math.floor(n / base) }
})(jQuery);