# HG changeset patch # User Thierry Florac # Date 1571223643 -7200 # Node ID 9f02c09d239363c728742d88a4d58f391b697965 # Parent f3af77e371a1a0c9d60936e29244264cd078c17e Updated JQuery-flot package diff -r f3af77e371a1 -r 9f02c09d2393 src/pyams_skin/resources/js/ext/flot/jquery.canvaswrapper.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/pyams_skin/resources/js/ext/flot/jquery.canvaswrapper.js Wed Oct 16 13:00:43 2019 +0200 @@ -0,0 +1,550 @@ +/** ## jquery.flot.canvaswrapper + +This plugin contains the function for creating and manipulating both the canvas +layers and svg layers. + +The Canvas object is a wrapper around an HTML5 canvas tag. +The constructor Canvas(cls, container) takes as parameters cls, +the list of classes to apply to the canvas adnd the containter, +element onto which to append the canvas. The canvas operations +don't work unless the canvas is attached to the DOM. + +### jquery.canvaswrapper.js API functions +*/ + +(function($) { + var Canvas = function(cls, container) { + var element = container.getElementsByClassName(cls)[0]; + + if (!element) { + element = document.createElement('canvas'); + element.className = cls; + element.style.direction = 'ltr'; + element.style.position = 'absolute'; + element.style.left = '0px'; + element.style.top = '0px'; + + container.appendChild(element); + + // If HTML5 Canvas isn't available, throw + + if (!element.getContext) { + throw new Error('Canvas is not available.'); + } + } + + this.element = element; + + var context = this.context = element.getContext('2d'); + this.pixelRatio = $.plot.browser.getPixelRatio(context); + + // Size the canvas to match the internal dimensions of its container + var width = $(container).width(); + var height = $(container).height(); + this.resize(width, height); + + // Collection of HTML div layers for text overlaid onto the canvas + + this.SVGContainer = null; + this.SVG = {}; + + // Cache of text fragments and metrics, so we can avoid expensively + // re-calculating them when the plot is re-rendered in a loop. + + this._textCache = {}; + } + + /** + - resize(width, height) + + Resizes the canvas to the given dimensions. + The width represents the new width of the canvas, meanwhile the height + is the new height of the canvas, both of them in pixels. + */ + + Canvas.prototype.resize = function(width, height) { + var minSize = 10; + width = width < minSize ? minSize : width; + height = height < minSize ? minSize : height; + + var element = this.element, + context = this.context, + pixelRatio = this.pixelRatio; + + // Resize the canvas, increasing its density based on the display's + // pixel ratio; basically giving it more pixels without increasing the + // size of its element, to take advantage of the fact that retina + // displays have that many more pixels in the same advertised space. + + // Resizing should reset the state (excanvas seems to be buggy though) + + 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; + } + + // Save the context, so we can reset in case we get replotted. The + // restore ensure that we're really back at the initial state, and + // should be safe even if we haven't saved the initial state yet. + + context.restore(); + context.save(); + + // Scale the coordinate space to match the display density; so even though we + // may have twice as many pixels, we still want lines and other drawing to + // appear at the same size; the extra pixels will just make them crisper. + + context.scale(pixelRatio, pixelRatio); + }; + + /** + - clear() + + Clears the entire canvas area, not including any overlaid HTML text + */ + Canvas.prototype.clear = function() { + this.context.clearRect(0, 0, this.width, this.height); + }; + + /** + - render() + + Finishes rendering the canvas, including managing the text overlay. + */ + Canvas.prototype.render = function() { + var cache = this._textCache; + + // For each text layer, add elements marked as active that haven't + // already been rendered, and remove those that are no longer active. + + for (var layerKey in cache) { + if (hasOwnProperty.call(cache, layerKey)) { + var layer = this.getSVGLayer(layerKey), + layerCache = cache[layerKey]; + + var display = layer.style.display; + layer.style.display = 'none'; + + 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 val = styleCache[key], + positions = val.positions; + + for (var i = 0, position; positions[i]; i++) { + position = positions[i]; + if (position.active) { + if (!position.rendered) { + layer.appendChild(position.element); + position.rendered = true; + } + } else { + positions.splice(i--, 1); + if (position.rendered) { + while (position.element.firstChild) { + position.element.removeChild(position.element.firstChild); + } + position.element.parentNode.removeChild(position.element); + } + } + } + + if (positions.length === 0) { + if (val.measured) { + val.measured = false; + } else { + delete styleCache[key]; + } + } + } + } + } + } + + layer.style.display = display; + } + } + }; + + /** + - getSVGLayer(classes) + + Creates (if necessary) and returns the SVG overlay container. + The classes string represents the string of space-separated CSS classes + used to uniquely identify the text layer. It return the svg-layer div. + */ + Canvas.prototype.getSVGLayer = function(classes) { + var layer = this.SVG[classes]; + + // Create the SVG layer if it doesn't exist + + if (!layer) { + // Create the svg layer container, if it doesn't exist + + var svgElement; + + if (!this.SVGContainer) { + this.SVGContainer = document.createElement('div'); + this.SVGContainer.className = 'flot-svg'; + this.SVGContainer.style.position = 'absolute'; + this.SVGContainer.style.top = '0px'; + this.SVGContainer.style.left = '0px'; + this.SVGContainer.style.height = '100%'; + this.SVGContainer.style.width = '100%'; + this.SVGContainer.style.pointerEvents = 'none'; + this.element.parentNode.appendChild(this.SVGContainer); + + svgElement = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); + svgElement.style.width = '100%'; + svgElement.style.height = '100%'; + + this.SVGContainer.appendChild(svgElement); + } else { + svgElement = this.SVGContainer.firstChild; + } + + layer = document.createElementNS('http://www.w3.org/2000/svg', 'g'); + layer.setAttribute('class', classes); + layer.style.position = 'absolute'; + layer.style.top = '0px'; + layer.style.left = '0px'; + layer.style.bottom = '0px'; + layer.style.right = '0px'; + svgElement.appendChild(layer); + this.SVG[classes] = layer; + } + + return layer; + }; + + /** + - getTextInfo(layer, text, font, angle, width) + + Creates (if necessary) and returns a text info object. + The object looks like this: + ```js + { + width //Width of the text's wrapper div. + height //Height of the text's wrapper div. + element //The HTML div containing the text. + positions //Array of positions at which this text is drawn. + } + ``` + The positions array contains objects that look like this: + ```js + { + active //Flag indicating whether the text should be visible. + rendered //Flag indicating whether the text is currently visible. + element //The HTML div containing the text. + text //The actual text and is identical with element[0].textContent. + x //X coordinate at which to draw the text. + y //Y coordinate at which to draw the text. + } + ``` + Each position after the first receives a clone of the original element. + The idea is that that the width, height, and general 'identity' of the + text is constant no matter where it is placed; the placements are a + secondary property. + + Canvas maintains a cache of recently-used text info objects; getTextInfo + either returns the cached element or creates a new entry. + + The layer parameter is string of space-separated CSS classes uniquely + identifying the layer containing this text. + Text is the text string to retrieve info for. + Font is either a string of space-separated CSS classes or a font-spec object, + defining the text's font and style. + Angle is the angle at which to rotate the text, in degrees. Angle is currently unused, + it will be implemented in the future. + The last parameter is the Maximum width of the text before it wraps. + The method returns a text info object. + */ + Canvas.prototype.getTextInfo = function(layer, text, font, angle, width) { + var textStyle, layerCache, styleCache, info; + + // Cast the value to a string, in case we were given a number or such + + text = '' + text; + + // If the font is a font-spec object, generate a CSS font definition + + if (typeof font === 'object') { + textStyle = font.style + ' ' + font.variant + ' ' + font.weight + ' ' + font.size + 'px/' + font.lineHeight + 'px ' + font.family; + } else { + textStyle = font; + } + + // Retrieve (or create) the cache for the text's layer and styles + + layerCache = this._textCache[layer]; + + if (layerCache == null) { + layerCache = this._textCache[layer] = {}; + } + + styleCache = layerCache[textStyle]; + + if (styleCache == null) { + styleCache = layerCache[textStyle] = {}; + } + + var key = generateKey(text); + info = styleCache[key]; + + // If we can't find a matching element in our cache, create a new one + + if (!info) { + var element = document.createElementNS('http://www.w3.org/2000/svg', 'text'); + if (text.indexOf('
') !== -1) { + addTspanElements(text, element, -9999); + } else { + var textNode = document.createTextNode(text); + element.appendChild(textNode); + } + + element.style.position = 'absolute'; + element.style.maxWidth = width; + element.setAttributeNS(null, 'x', -9999); + element.setAttributeNS(null, 'y', -9999); + + if (typeof font === 'object') { + element.style.font = textStyle; + element.style.fill = font.fill; + } else if (typeof font === 'string') { + element.setAttribute('class', font); + } + + this.getSVGLayer(layer).appendChild(element); + var elementRect = element.getBBox(); + + info = styleCache[key] = { + width: elementRect.width, + height: elementRect.height, + measured: true, + element: element, + positions: [] + }; + + //remove elements from dom + while (element.firstChild) { + element.removeChild(element.firstChild); + } + element.parentNode.removeChild(element); + } + + info.measured = true; + return info; + }; + + function updateTransforms (element, transforms) { + element.transform.baseVal.clear(); + if (transforms) { + transforms.forEach(function(t) { + element.transform.baseVal.appendItem(t); + }); + } + } + + /** + - addText (layer, x, y, text, font, angle, width, halign, valign, transforms) + + Adds a text string to the canvas text overlay. + The text isn't drawn immediately; it is marked as rendering, which will + result in its addition to the canvas on the next render pass. + + The layer is string of space-separated CSS classes uniquely + identifying the layer containing this text. + X and Y represents the X and Y coordinate at which to draw the text. + and text is the string to draw + */ + Canvas.prototype.addText = function(layer, x, y, text, font, angle, width, halign, valign, transforms) { + var info = this.getTextInfo(layer, text, font, angle, width), + positions = info.positions; + + // Tweak the div's position to match the text's alignment + + 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; + } + + y += 0.75 * info.height; + + + // Determine whether this text already exists at this position. + // If so, mark it for inclusion in the next render pass. + + for (var i = 0, position; positions[i]; i++) { + position = positions[i]; + if (position.x === x && position.y === y && position.text === text) { + position.active = true; + // update the transforms + updateTransforms(position.element, transforms); + + return; + } else if (position.active === false) { + position.active = true; + position.text = text; + if (text.indexOf('
') !== -1) { + y -= 0.25 * info.height; + addTspanElements(text, position.element, x); + } else { + position.element.textContent = text; + } + position.element.setAttributeNS(null, 'x', x); + position.element.setAttributeNS(null, 'y', y); + position.x = x; + position.y = y; + // update the transforms + updateTransforms(position.element, transforms); + + return; + } + } + + // If the text doesn't exist at this position, create a new entry + + // For the very first position we'll re-use the original element, + // while for subsequent ones we'll clone it. + + position = { + active: true, + rendered: false, + element: positions.length ? info.element.cloneNode() : info.element, + text: text, + x: x, + y: y + }; + + positions.push(position); + + if (text.indexOf('
') !== -1) { + y -= 0.25 * info.height; + addTspanElements(text, position.element, x); + } else { + position.element.textContent = text; + } + + // Move the element to its final position within the container + position.element.setAttributeNS(null, 'x', x); + position.element.setAttributeNS(null, 'y', y); + position.element.style.textAlign = halign; + // update the transforms + updateTransforms(position.element, transforms); + }; + + var addTspanElements = function(text, element, x) { + var lines = text.split('
'), + tspan, i, offset; + + for (i = 0; i < lines.length; i++) { + if (!element.childNodes[i]) { + tspan = document.createElementNS('http://www.w3.org/2000/svg', 'tspan'); + element.appendChild(tspan); + } else { + tspan = element.childNodes[i]; + } + tspan.textContent = lines[i]; + offset = i * 1 + 'em'; + tspan.setAttributeNS(null, 'dy', offset); + tspan.setAttributeNS(null, 'x', x); + } + } + + /** + - removeText (layer, x, y, text, font, angle) + + The function removes one or more text strings from the canvas text overlay. + If no parameters are given, all text within the layer is removed. + + Note that the text is not immediately removed; it is simply marked as + inactive, which will result in its removal on the next render pass. + This avoids the performance penalty for 'clear and redraw' behavior, + where we potentially get rid of all text on a layer, but will likely + add back most or all of it later, as when redrawing axes, for example. + + The layer is a string of space-separated CSS classes uniquely + identifying the layer containing this text. The following parameter are + X and Y coordinate of the text. + Text is the string to remove, while the font is either a string of space-separated CSS + classes or a font-spec object, defining the text's font and style. + */ + Canvas.prototype.removeText = function(layer, x, y, text, font, angle) { + var info, htmlYCoord; + 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; + positions.forEach(function(position) { + position.active = false; + }); + } + } + } + } + } + } else { + info = this.getTextInfo(layer, text, font, angle); + positions = info.positions; + positions.forEach(function(position) { + htmlYCoord = y + 0.75 * info.height; + if (position.x === x && position.y === htmlYCoord && position.text === text) { + position.active = false; + } + }); + } + }; + + /** + - clearCache() + + Clears the cache used to speed up the text size measurements. + As an (unfortunate) side effect all text within the text Layer is removed. + Use this function before plot.setupGrid() and plot.draw() if the plot just + became visible or the styles changed. + */ + Canvas.prototype.clearCache = function() { + var cache = this._textCache; + for (var layerKey in cache) { + if (hasOwnProperty.call(cache, layerKey)) { + var layer = this.getSVGLayer(layerKey); + while (layer.firstChild) { + layer.removeChild(layer.firstChild); + } + } + }; + + this._textCache = {}; + }; + + function generateKey(text) { + return text.replace(/0|1|2|3|4|5|6|7|8|9/g, '0'); + } + + if (!window.Flot) { + window.Flot = {}; + } + + window.Flot.Canvas = Canvas; +})(jQuery); diff -r f3af77e371a1 -r 9f02c09d2393 src/pyams_skin/resources/js/ext/flot/jquery.canvaswrapper.min.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/pyams_skin/resources/js/ext/flot/jquery.canvaswrapper.min.js Wed Oct 16 13:00:43 2019 +0200 @@ -0,0 +1,1 @@ +!function(t){function e(t,e){t.transform.baseVal.clear(),e&&e.forEach(function(e){t.transform.baseVal.appendItem(e)})}function i(t){return t.replace(/0|1|2|3|4|5|6|7|8|9/g,"0")}var n=function(e,i){var n=i.getElementsByClassName(e)[0];if(!n&&(n=document.createElement("canvas"),n.className=e,n.style.direction="ltr",n.style.position="absolute",n.style.left="0px",n.style.top="0px",i.appendChild(n),!n.getContext))throw new Error("Canvas is not available.");this.element=n;var r=this.context=n.getContext("2d");this.pixelRatio=t.plot.browser.getPixelRatio(r);var l=t(i).width(),o=t(i).height();this.resize(l,o),this.SVGContainer=null,this.SVG={},this._textCache={}};n.prototype.resize=function(t,e){t=t<10?10:t,e=e<10?10:e;var i=this.element,n=this.context,r=this.pixelRatio;this.width!==t&&(i.width=t*r,i.style.width=t+"px",this.width=t),this.height!==e&&(i.height=e*r,i.style.height=e+"px",this.height=e),n.restore(),n.save(),n.scale(r,r)},n.prototype.clear=function(){this.context.clearRect(0,0,this.width,this.height)},n.prototype.render=function(){var t=this._textCache;for(var e in t)if(hasOwnProperty.call(t,e)){var i=this.getSVGLayer(e),n=t[e],r=i.style.display;i.style.display="none";for(var l in n)if(hasOwnProperty.call(n,l)){var o=n[l];for(var s in o)if(hasOwnProperty.call(o,s)){for(var a,h=o[s],d=h.positions,p=0;d[p];p++)if((a=d[p]).active)a.rendered||(i.appendChild(a.element),a.rendered=!0);else if(d.splice(p--,1),a.rendered){for(;a.element.firstChild;)a.element.removeChild(a.element.firstChild);a.element.parentNode.removeChild(a.element)}0===d.length&&(h.measured?h.measured=!1:delete o[s])}}i.style.display=r}},n.prototype.getSVGLayer=function(t){var e=this.SVG[t];if(!e){var i;this.SVGContainer?i=this.SVGContainer.firstChild:(this.SVGContainer=document.createElement("div"),this.SVGContainer.className="flot-svg",this.SVGContainer.style.position="absolute",this.SVGContainer.style.top="0px",this.SVGContainer.style.left="0px",this.SVGContainer.style.height="100%",this.SVGContainer.style.width="100%",this.SVGContainer.style.pointerEvents="none",this.element.parentNode.appendChild(this.SVGContainer),(i=document.createElementNS("http://www.w3.org/2000/svg","svg")).style.width="100%",i.style.height="100%",this.SVGContainer.appendChild(i)),(e=document.createElementNS("http://www.w3.org/2000/svg","g")).setAttribute("class",t),e.style.position="absolute",e.style.top="0px",e.style.left="0px",e.style.bottom="0px",e.style.right="0px",i.appendChild(e),this.SVG[t]=e}return e},n.prototype.getTextInfo=function(t,e,n,l,o){var s,a,h,d;e=""+e,s="object"==typeof n?n.style+" "+n.variant+" "+n.weight+" "+n.size+"px/"+n.lineHeight+"px "+n.family:n,null==(a=this._textCache[t])&&(a=this._textCache[t]={}),null==(h=a[s])&&(h=a[s]={});var p=i(e);if(!(d=h[p])){var c=document.createElementNS("http://www.w3.org/2000/svg","text");if(-1!==e.indexOf("
"))r(e,c,-9999);else{var f=document.createTextNode(e);c.appendChild(f)}c.style.position="absolute",c.style.maxWidth=o,c.setAttributeNS(null,"x",-9999),c.setAttributeNS(null,"y",-9999),"object"==typeof n?(c.style.font=s,c.style.fill=n.fill):"string"==typeof n&&c.setAttribute("class",n),this.getSVGLayer(t).appendChild(c);var y=c.getBBox();for(d=h[p]={width:y.width,height:y.height,measured:!0,element:c,positions:[]};c.firstChild;)c.removeChild(c.firstChild);c.parentNode.removeChild(c)}return d.measured=!0,d},n.prototype.addText=function(t,i,n,l,o,s,a,h,d,p){var c=this.getTextInfo(t,l,o,s,a),f=c.positions;"center"===h?i-=c.width/2:"right"===h&&(i-=c.width),"middle"===d?n-=c.height/2:"bottom"===d&&(n-=c.height),n+=.75*c.height;for(var y,m=0;f[m];m++){if((y=f[m]).x===i&&y.y===n&&y.text===l)return y.active=!0,void e(y.element,p);if(!1===y.active)return y.active=!0,y.text=l,-1!==l.indexOf("
")?(n-=.25*c.height,r(l,y.element,i)):y.element.textContent=l,y.element.setAttributeNS(null,"x",i),y.element.setAttributeNS(null,"y",n),y.x=i,y.y=n,void e(y.element,p)}y={active:!0,rendered:!1,element:f.length?c.element.cloneNode():c.element,text:l,x:i,y:n},f.push(y),-1!==l.indexOf("
")?(n-=.25*c.height,r(l,y.element,i)):y.element.textContent=l,y.element.setAttributeNS(null,"x",i),y.element.setAttributeNS(null,"y",n),y.element.style.textAlign=h,e(y.element,p)};var r=function(t,e,i){var n,r,l,o=t.split("
");for(r=0;r= 1.0) { - return "rgb("+[o.r, o.g, o.b].join(",")+")"; + return "rgb(" + [o.r, o.g, o.b].join(",") + ")"; } else { - return "rgba("+[o.r, o.g, o.b, o.a].join(",")+")"; + 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); + 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); @@ -79,18 +83,21 @@ c = elem.css(css).toLowerCase(); // keep going until we find an element that has color, or // we hit the body or root (have no parent) - if (c != '' && c != 'transparent') + if (c !== '' && c !== 'transparent') { break; + } + elem = elem.parent(); } while (elem.length && !$.nodeName(elem.get(0), "body")); // catch Safari's way of signalling transparent - if (c == "rgba(0, 0, 0, 0)") + if (c === "rgba(0, 0, 0, 0)") { c = "transparent"; - + } + return $.color.parse(c); } - + // parse CSS color string (like "rgb(10, 32, 43)" or "#fff"), // returns color object, if parsing failed, you get black (0, 0, // 0) out @@ -98,83 +105,95 @@ var res, m = $.color.make; // Look for rgb(num,num,num) - if (res = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(str)) + res = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(str); + if (res) { return m(parseInt(res[1], 10), parseInt(res[2], 10), parseInt(res[3], 10)); - + } + // Look for rgba(num,num,num,num) - 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)) + 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) + if (res) { return m(parseInt(res[1], 10), parseInt(res[2], 10), parseInt(res[3], 10), parseFloat(res[4])); - + } + // Look for rgb(num%,num%,num%) - 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); + res = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)%\s*,\s*([0-9]+(?:\.[0-9]+)?)%\s*,\s*([0-9]+(?:\.[0-9]+)?)%\s*\)/.exec(str); + if (res) { + return m(parseFloat(res[1]) * 2.55, parseFloat(res[2]) * 2.55, parseFloat(res[3]) * 2.55); + } // Look for rgba(num%,num%,num%,num) - 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])); - + 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); + if (res) { + return m(parseFloat(res[1]) * 2.55, parseFloat(res[2]) * 2.55, parseFloat(res[3]) * 2.55, parseFloat(res[4])); + } + // Look for #a0b1c2 - if (res = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(str)) + res = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(str); + if (res) { return m(parseInt(res[1], 16), parseInt(res[2], 16), parseInt(res[3], 16)); + } // Look for #fff - 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)); + res = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(str); + if (res) { + return m(parseInt(res[1] + res[1], 16), parseInt(res[2] + res[2], 16), parseInt(res[3] + res[3], 16)); + } // Otherwise, we're most likely dealing with a named color var name = $.trim(str).toLowerCase(); - if (name == "transparent") + if (name === "transparent") { return m(255, 255, 255, 0); - else { + } else { // default to black 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] + 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); diff -r f3af77e371a1 -r 9f02c09d2393 src/pyams_skin/resources/js/ext/flot/jquery.colorhelpers.min.js --- a/src/pyams_skin/resources/js/ext/flot/jquery.colorhelpers.min.js Thu Sep 26 11:55:17 2019 +0200 +++ b/src/pyams_skin/resources/js/ext/flot/jquery.colorhelpers.min.js Wed Oct 16 13:00:43 2019 +0200 @@ -1,1 +1,1 @@ -!function(r){r.color={},r.color.make=function(e,a,n,t){var o={};return o.r=e||0,o.g=a||0,o.b=n||0,o.a=null!=t?t:1,o.add=function(r,e){for(var a=0;a=1?"rgb("+[o.r,o.g,o.b].join(",")+")":"rgba("+[o.r,o.g,o.b,o.a].join(",")+")"},o.normalize=function(){function r(r,e,a){return ea?a:e}return o.r=r(0,parseInt(o.r),255),o.g=r(0,parseInt(o.g),255),o.b=r(0,parseInt(o.b),255),o.a=r(0,o.a,1),o},o.clone=function(){return r.color.make(o.r,o.b,o.g,o.a)},o.normalize()},r.color.extract=function(e,a){var n;do{if(""!=(n=e.css(a).toLowerCase())&&"transparent"!=n)break;e=e.parent()}while(e.length&&!r.nodeName(e.get(0),"body"));return"rgba(0, 0, 0, 0)"==n&&(n="transparent"),r.color.parse(n)},r.color.parse=function(a){var n,t=r.color.make;if(n=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(a))return t(parseInt(n[1],10),parseInt(n[2],10),parseInt(n[3],10));if(n=/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(a))return t(parseInt(n[1],10),parseInt(n[2],10),parseInt(n[3],10),parseFloat(n[4]));if(n=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(a))return t(2.55*parseFloat(n[1]),2.55*parseFloat(n[2]),2.55*parseFloat(n[3]));if(n=/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(a))return t(2.55*parseFloat(n[1]),2.55*parseFloat(n[2]),2.55*parseFloat(n[3]),parseFloat(n[4]));if(n=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(a))return t(parseInt(n[1],16),parseInt(n[2],16),parseInt(n[3],16));if(n=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(a))return t(parseInt(n[1]+n[1],16),parseInt(n[2]+n[2],16),parseInt(n[3]+n[3],16));var o=r.trim(a).toLowerCase();return"transparent"==o?t(255,255,255,0):(n=e[o]||[0,0,0],t(n[0],n[1],n[2]))};var e={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(r){r.color={},r.color.make=function(e,a,n,t){var o={};return o.r=e||0,o.g=a||0,o.b=n||0,o.a=null!=t?t:1,o.add=function(r,e){for(var a=0;a=1?"rgb("+[o.r,o.g,o.b].join(",")+")":"rgba("+[o.r,o.g,o.b,o.a].join(",")+")"},o.normalize=function(){function r(r,e,a){return ea?a:e}return o.r=r(0,parseInt(o.r),255),o.g=r(0,parseInt(o.g),255),o.b=r(0,parseInt(o.b),255),o.a=r(0,o.a,1),o},o.clone=function(){return r.color.make(o.r,o.b,o.g,o.a)},o.normalize()},r.color.extract=function(e,a){var n;do{if(""!==(n=e.css(a).toLowerCase())&&"transparent"!==n)break;e=e.parent()}while(e.length&&!r.nodeName(e.get(0),"body"));return"rgba(0, 0, 0, 0)"===n&&(n="transparent"),r.color.parse(n)},r.color.parse=function(a){var n,t=r.color.make;if(n=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(a))return t(parseInt(n[1],10),parseInt(n[2],10),parseInt(n[3],10));if(n=/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(a))return t(parseInt(n[1],10),parseInt(n[2],10),parseInt(n[3],10),parseFloat(n[4]));if(n=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)%\s*,\s*([0-9]+(?:\.[0-9]+)?)%\s*,\s*([0-9]+(?:\.[0-9]+)?)%\s*\)/.exec(a))return t(2.55*parseFloat(n[1]),2.55*parseFloat(n[2]),2.55*parseFloat(n[3]));if(n=/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(a))return t(2.55*parseFloat(n[1]),2.55*parseFloat(n[2]),2.55*parseFloat(n[3]),parseFloat(n[4]));if(n=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(a))return t(parseInt(n[1],16),parseInt(n[2],16),parseInt(n[3],16));if(n=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(a))return t(parseInt(n[1]+n[1],16),parseInt(n[2]+n[2],16),parseInt(n[3]+n[3],16));var o=r.trim(a).toLowerCase();return"transparent"===o?t(255,255,255,0):(n=e[o]||[0,0,0],t(n[0],n[1],n[2]))};var e={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); diff -r f3af77e371a1 -r 9f02c09d2393 src/pyams_skin/resources/js/ext/flot/jquery.flot.axislabels.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/pyams_skin/resources/js/ext/flot/jquery.flot.axislabels.js Wed Oct 16 13:00:43 2019 +0200 @@ -0,0 +1,212 @@ +/* +Axis label plugin for flot + +Derived from: +Axis Labels Plugin for flot. +http://github.com/markrcote/flot-axislabels + +Original code is Copyright (c) 2010 Xuan Luo. +Original code was released under the GPLv3 license by Xuan Luo, September 2010. +Original code was rereleased under the MIT license by Xuan Luo, April 2012. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +(function($) { + "use strict"; + + var options = { + axisLabels: { + show: true + } + }; + + function AxisLabel(axisName, position, padding, placeholder, axisLabel, surface) { + this.axisName = axisName; + this.position = position; + this.padding = padding; + this.placeholder = placeholder; + this.axisLabel = axisLabel; + this.surface = surface; + this.width = 0; + this.height = 0; + this.elem = null; + } + + AxisLabel.prototype.calculateSize = function() { + var axisId = this.axisName + 'Label', + layerId = axisId + 'Layer', + className = axisId + ' axisLabels'; + + var info = this.surface.getTextInfo(layerId, this.axisLabel, className); + this.labelWidth = info.width; + this.labelHeight = info.height; + + if (this.position === 'left' || this.position === 'right') { + this.width = this.labelHeight + this.padding; + this.height = 0; + } else { + this.width = 0; + this.height = this.labelHeight + this.padding; + } + }; + + AxisLabel.prototype.transforms = function(degrees, x, y, svgLayer) { + var transforms = [], translate, rotate; + if (x !== 0 || y !== 0) { + translate = svgLayer.createSVGTransform(); + translate.setTranslate(x, y); + transforms.push(translate); + } + if (degrees !== 0) { + rotate = svgLayer.createSVGTransform(); + var centerX = Math.round(this.labelWidth / 2), + centerY = 0; + rotate.setRotate(degrees, centerX, centerY); + transforms.push(rotate); + } + + return transforms; + }; + + AxisLabel.prototype.calculateOffsets = function(box) { + var offsets = { + x: 0, + y: 0, + degrees: 0 + }; + if (this.position === 'bottom') { + offsets.x = box.left + box.width / 2 - this.labelWidth / 2; + offsets.y = box.top + box.height - this.labelHeight; + } else if (this.position === 'top') { + offsets.x = box.left + box.width / 2 - this.labelWidth / 2; + offsets.y = box.top; + } else if (this.position === 'left') { + offsets.degrees = -90; + offsets.x = box.left - this.labelWidth / 2; + offsets.y = box.height / 2 + box.top; + } else if (this.position === 'right') { + offsets.degrees = 90; + offsets.x = box.left + box.width - this.labelWidth / 2; + offsets.y = box.height / 2 + box.top; + } + offsets.x = Math.round(offsets.x); + offsets.y = Math.round(offsets.y); + + return offsets; + }; + + AxisLabel.prototype.cleanup = function() { + var axisId = this.axisName + 'Label', + layerId = axisId + 'Layer', + className = axisId + ' axisLabels'; + this.surface.removeText(layerId, 0, 0, this.axisLabel, className); + }; + + AxisLabel.prototype.draw = function(box) { + var axisId = this.axisName + 'Label', + layerId = axisId + 'Layer', + className = axisId + ' axisLabels', + offsets = this.calculateOffsets(box), + style = { + position: 'absolute', + bottom: '', + right: '', + display: 'inline-block', + 'white-space': 'nowrap' + }; + + var layer = this.surface.getSVGLayer(layerId); + var transforms = this.transforms(offsets.degrees, offsets.x, offsets.y, layer.parentNode); + + this.surface.addText(layerId, 0, 0, this.axisLabel, className, undefined, undefined, undefined, undefined, transforms); + this.surface.render(); + Object.keys(style).forEach(function(key) { + layer.style[key] = style[key]; + }); + }; + + function init(plot) { + plot.hooks.processOptions.push(function(plot, options) { + if (!options.axisLabels.show) { + return; + } + + var axisLabels = {}; + var defaultPadding = 2; // padding between axis and tick labels + + plot.hooks.axisReserveSpace.push(function(plot, axis) { + var opts = axis.options; + var axisName = axis.direction + axis.n; + + axis.labelHeight += axis.boxPosition.centerY; + axis.labelWidth += axis.boxPosition.centerX; + + if (!opts || !opts.axisLabel || !axis.show) { + return; + } + + var padding = opts.axisLabelPadding === undefined + ? defaultPadding + : opts.axisLabelPadding; + + var axisLabel = axisLabels[axisName]; + if (!axisLabel) { + axisLabel = new AxisLabel(axisName, + opts.position, padding, + plot.getPlaceholder()[0], opts.axisLabel, plot.getSurface()); + axisLabels[axisName] = axisLabel; + } + + axisLabel.calculateSize(); + + // Incrementing the sizes of the tick labels. + axis.labelHeight += axisLabel.height; + axis.labelWidth += axisLabel.width; + }); + + // TODO - use the drawAxis hook + plot.hooks.draw.push(function(plot, ctx) { + $.each(plot.getAxes(), function(flotAxisName, axis) { + var opts = axis.options; + if (!opts || !opts.axisLabel || !axis.show) { + return; + } + + var axisName = axis.direction + axis.n; + axisLabels[axisName].draw(axis.box); + }); + }); + + plot.hooks.shutdown.push(function(plot, eventHolder) { + for (var axisName in axisLabels) { + axisLabels[axisName].cleanup(); + } + }); + }); + }; + + $.plot.plugins.push({ + init: init, + options: options, + name: 'axisLabels', + version: '3.0' + }); +})(jQuery); diff -r f3af77e371a1 -r 9f02c09d2393 src/pyams_skin/resources/js/ext/flot/jquery.flot.axislabels.min.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/pyams_skin/resources/js/ext/flot/jquery.flot.axislabels.min.js Wed Oct 16 13:00:43 2019 +0200 @@ -0,0 +1,1 @@ +!function(t){"use strict";function i(t,i,e,s,a,h){this.axisName=t,this.position=i,this.padding=e,this.placeholder=s,this.axisLabel=a,this.surface=h,this.width=0,this.height=0,this.elem=null}i.prototype.calculateSize=function(){var t=this.axisName+"Label",i=t+"Layer",e=t+" axisLabels",s=this.surface.getTextInfo(i,this.axisLabel,e);this.labelWidth=s.width,this.labelHeight=s.height,"left"===this.position||"right"===this.position?(this.width=this.labelHeight+this.padding,this.height=0):(this.width=0,this.height=this.labelHeight+this.padding)},i.prototype.transforms=function(t,i,e,s){var a,h,o=[];if(0===i&&0===e||((a=s.createSVGTransform()).setTranslate(i,e),o.push(a)),0!==t){h=s.createSVGTransform();var n=Math.round(this.labelWidth/2);h.setRotate(t,n,0),o.push(h)}return o},i.prototype.calculateOffsets=function(t){var i={x:0,y:0,degrees:0};return"bottom"===this.position?(i.x=t.left+t.width/2-this.labelWidth/2,i.y=t.top+t.height-this.labelHeight):"top"===this.position?(i.x=t.left+t.width/2-this.labelWidth/2,i.y=t.top):"left"===this.position?(i.degrees=-90,i.x=t.left-this.labelWidth/2,i.y=t.height/2+t.top):"right"===this.position&&(i.degrees=90,i.x=t.left+t.width-this.labelWidth/2,i.y=t.height/2+t.top),i.x=Math.round(i.x),i.y=Math.round(i.y),i},i.prototype.cleanup=function(){var t=this.axisName+"Label",i=t+"Layer",e=t+" axisLabels";this.surface.removeText(i,0,0,this.axisLabel,e)},i.prototype.draw=function(t){var i=this.axisName+"Label",e=i+"Layer",s=i+" axisLabels",a=this.calculateOffsets(t),h={position:"absolute",bottom:"",right:"",display:"inline-block","white-space":"nowrap"},o=this.surface.getSVGLayer(e),n=this.transforms(a.degrees,a.x,a.y,o.parentNode);this.surface.addText(e,0,0,this.axisLabel,s,void 0,void 0,void 0,void 0,n),this.surface.render(),Object.keys(h).forEach(function(t){o.style[t]=h[t]})},t.plot.plugins.push({init:function(e){e.hooks.processOptions.push(function(e,s){if(s.axisLabels.show){var a={};e.hooks.axisReserveSpace.push(function(t,e){var s=e.options,h=e.direction+e.n;if(e.labelHeight+=e.boxPosition.centerY,e.labelWidth+=e.boxPosition.centerX,s&&s.axisLabel&&e.show){var o=void 0===s.axisLabelPadding?2:s.axisLabelPadding,n=a[h];n||(n=new i(h,s.position,o,t.getPlaceholder()[0],s.axisLabel,t.getSurface()),a[h]=n),n.calculateSize(),e.labelHeight+=n.height,e.labelWidth+=n.width}}),e.hooks.draw.push(function(i,e){t.each(i.getAxes(),function(t,i){var e=i.options;if(e&&e.axisLabel&&i.show){var s=i.direction+i.n;a[s].draw(i.box)}})}),e.hooks.shutdown.push(function(t,i){for(var e in a)a[e].cleanup()})}})},options:{axisLabels:{show:!0}},name:"axisLabels",version:"3.0"})}(jQuery); diff -r f3af77e371a1 -r 9f02c09d2393 src/pyams_skin/resources/js/ext/flot/jquery.flot.browser.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/pyams_skin/resources/js/ext/flot/jquery.flot.browser.js Wed Oct 16 13:00:43 2019 +0200 @@ -0,0 +1,98 @@ +/** ## jquery.flot.browser.js + +This plugin is used to make available some browser-related utility functions. + +### Methods +*/ + +(function ($) { + 'use strict'; + + var browser = { + /** + - getPageXY(e) + + Calculates the pageX and pageY using the screenX, screenY properties of the event + and the scrolling of the page. This is needed because the pageX and pageY + properties of the event are not correct while running tests in Edge. */ + getPageXY: function (e) { + // This code is inspired from https://stackoverflow.com/a/3464890 + var doc = document.documentElement, + pageX = e.clientX + (window.pageXOffset || doc.scrollLeft) - (doc.clientLeft || 0), + pageY = e.clientY + (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0); + return { X: pageX, Y: pageY }; + }, + + /** + - getPixelRatio(context) + + This function returns the current pixel ratio defined by the product of desktop + zoom and page zoom. + Additional info: https://www.html5rocks.com/en/tutorials/canvas/hidpi/ + */ + getPixelRatio: function(context) { + var devicePixelRatio = window.devicePixelRatio || 1, + backingStoreRatio = + context.webkitBackingStorePixelRatio || + context.mozBackingStorePixelRatio || + context.msBackingStorePixelRatio || + context.oBackingStorePixelRatio || + context.backingStorePixelRatio || 1; + return devicePixelRatio / backingStoreRatio; + }, + + /** + - isSafari, isMobileSafari, isOpera, isFirefox, isIE, isEdge, isChrome, isBlink + + This is a collection of functions, used to check if the code is running in a + particular browser or Javascript engine. + */ + isSafari: function() { + // *** https://stackoverflow.com/questions/9847580/how-to-detect-safari-chrome-ie-firefox-and-opera-browser + // Safari 3.0+ "[object HTMLElementConstructor]" + return /constructor/i.test(window.top.HTMLElement) || (function (p) { return p.toString() === "[object SafariRemoteNotification]"; })(!window.top['safari'] || (typeof window.top.safari !== 'undefined' && window.top.safari.pushNotification)); + }, + + isMobileSafari: function() { + //isMobileSafari adapted from https://stackoverflow.com/questions/3007480/determine-if-user-navigated-from-mobile-safari + return navigator.userAgent.match(/(iPod|iPhone|iPad)/) && navigator.userAgent.match(/AppleWebKit/); + }, + + isOpera: function() { + // *** https://stackoverflow.com/questions/9847580/how-to-detect-safari-chrome-ie-firefox-and-opera-browser + //Opera 8.0+ + return (!!window.opr && !!opr.addons) || !!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0; + }, + + isFirefox: function() { + // *** https://stackoverflow.com/questions/9847580/how-to-detect-safari-chrome-ie-firefox-and-opera-browser + // Firefox 1.0+ + return typeof InstallTrigger !== 'undefined'; + }, + + isIE: function() { + // *** https://stackoverflow.com/questions/9847580/how-to-detect-safari-chrome-ie-firefox-and-opera-browser + // Internet Explorer 6-11 + return /*@cc_on!@*/false || !!document.documentMode; + }, + + isEdge: function() { + // *** https://stackoverflow.com/questions/9847580/how-to-detect-safari-chrome-ie-firefox-and-opera-browser + // Edge 20+ + return !browser.isIE() && !!window.StyleMedia; + }, + + isChrome: function() { + // *** https://stackoverflow.com/questions/9847580/how-to-detect-safari-chrome-ie-firefox-and-opera-browser + // Chrome 1+ + return !!window.chrome && !!window.chrome.webstore; + }, + + isBlink: function() { + // *** https://stackoverflow.com/questions/9847580/how-to-detect-safari-chrome-ie-firefox-and-opera-browser + return (browser.isChrome() || browser.isOpera()) && !!window.CSS; + } + }; + + $.plot.browser = browser; +})(jQuery); diff -r f3af77e371a1 -r 9f02c09d2393 src/pyams_skin/resources/js/ext/flot/jquery.flot.browser.min.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/pyams_skin/resources/js/ext/flot/jquery.flot.browser.min.js Wed Oct 16 13:00:43 2019 +0200 @@ -0,0 +1,1 @@ +!function(i){"use strict";var e={getPageXY:function(i){var e=document.documentElement;return{X:i.clientX+(window.pageXOffset||e.scrollLeft)-(e.clientLeft||0),Y:i.clientY+(window.pageYOffset||e.scrollTop)-(e.clientTop||0)}},getPixelRatio:function(i){return(window.devicePixelRatio||1)/(i.webkitBackingStorePixelRatio||i.mozBackingStorePixelRatio||i.msBackingStorePixelRatio||i.oBackingStorePixelRatio||i.backingStorePixelRatio||1)},isSafari:function(){return/constructor/i.test(window.top.HTMLElement)||"[object SafariRemoteNotification]"===(!window.top["safari"]||typeof window.top.safari!=="undefined"&&window.top.safari.pushNotification).toString()},isMobileSafari:function(){return navigator.userAgent.match(/(iPod|iPhone|iPad)/)&&navigator.userAgent.match(/AppleWebKit/)},isOpera:function(){return!!window.opr&&!!opr.addons||!!window.opera||navigator.userAgent.indexOf(" OPR/")>=0},isFirefox:function(){return"undefined"!=typeof InstallTrigger},isIE:function(){return!!document.documentMode},isEdge:function(){return!e.isIE()&&!!window.StyleMedia},isChrome:function(){return!!window.chrome&&!!window.chrome.webstore},isBlink:function(){return(e.isChrome()||e.isOpera())&&!!window.CSS}};jQuery.plot.browser=e}(); diff -r f3af77e371a1 -r 9f02c09d2393 src/pyams_skin/resources/js/ext/flot/jquery.flot.categories.js --- a/src/pyams_skin/resources/js/ext/flot/jquery.flot.categories.js Thu Sep 26 11:55:17 2019 +0200 +++ b/src/pyams_skin/resources/js/ext/flot/jquery.flot.categories.js Wed Oct 16 13:00:43 2019 +0200 @@ -9,24 +9,24 @@ To enable it, you must specify mode: "categories" on the axis with the textual labels, e.g. - $.plot("#placeholder", data, { xaxis: { mode: "categories" } }); + $.plot("#placeholder", data, { xaxis: { mode: "categories" } }); By default, the labels are ordered as they are met in the data series. If you need a different ordering, you can specify "categories" on the axis options and list the categories there: - xaxis: { - mode: "categories", - categories: ["February", "March", "April"] - } + xaxis: { + mode: "categories", + categories: ["February", "March", "April"] + } If you need to customize the distances between the categories, you can specify "categories" as an object mapping labels to values - xaxis: { - mode: "categories", - categories: { "February": 1, "March": 3, "April": 4 } - } + xaxis: { + mode: "categories", + categories: { "February": 1, "March": 3, "April": 4 } + } If you don't specify all categories, the remaining categories will be numbered from the max value plus 1 (with a spacing of 1 between each). @@ -52,17 +52,18 @@ categories: null } }; - + function processRawData(plot, series, data, datapoints) { // if categories are enabled, we need to disable // auto-transformation to numbers so the strings are intact // for later processing - var xCategories = series.xaxis.options.mode == "categories", - yCategories = series.yaxis.options.mode == "categories"; - - if (!(xCategories || yCategories)) + var xCategories = series.xaxis.options.mode === "categories", + yCategories = series.yaxis.options.mode === "categories"; + + if (!(xCategories || yCategories)) { return; + } var format = datapoints.format; @@ -70,36 +71,41 @@ // FIXME: auto-detection should really not be defined here var s = series; format = []; - format.push({ x: true, number: true, required: true }); - format.push({ y: true, number: true, required: true }); + format.push({ x: true, number: true, required: true, computeRange: true}); + format.push({ y: true, number: true, required: true, computeRange: 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 }); + var autoScale = !!((s.bars.show && s.bars.zero) || (s.lines.show && s.lines.zero)); + format.push({ y: true, number: true, required: false, defaultValue: 0, computeRange: autoScale }); if (s.bars.horizontal) { delete format[format.length - 1].y; format[format.length - 1].x = true; } } - + datapoints.format = format; } for (var m = 0; m < format.length; ++m) { - if (format[m].x && xCategories) + if (format[m].x && xCategories) { format[m].number = false; - - if (format[m].y && yCategories) + } + + if (format[m].y && yCategories) { format[m].number = false; + format[m].computeRange = false; + } } } function getNextIndex(categories) { var index = -1; - - for (var v in categories) - if (categories[v] > index) + + for (var v in categories) { + if (categories[v] > index) { index = categories[v]; + } + } return index + 1; } @@ -108,41 +114,45 @@ var res = []; for (var label in axis.categories) { var v = axis.categories[label]; - if (v >= axis.min && v <= axis.max) + if (v >= axis.min && v <= axis.max) { res.push([v, label]); + } } res.sort(function (a, b) { return a[0] - b[0]; }); return res; } - + function setupCategoriesForAxis(series, axis, datapoints) { - if (series[axis].options.mode != "categories") + if (series[axis].options.mode !== "categories") { return; - + } + if (!series[axis].categories) { // parse options var c = {}, o = series[axis].options.categories || {}; if ($.isArray(o)) { - for (var i = 0; i < o.length; ++i) + for (var i = 0; i < o.length; ++i) { c[o[i]] = i; + } + } else { + for (var v in o) { + c[v] = o[v]; + } } - else { - for (var v in o) - c[v] = o[v]; - } - + series[axis].categories = c; } // fix ticks - if (!series[axis].options.ticks) + if (!series[axis].options.ticks) { series[axis].options.ticks = categoriesTickGenerator; + } transformPointsOnAxis(datapoints, axis, series[axis].categories); } - + function transformPointsOnAxis(datapoints, axis, categories) { // go through the points, transforming them var points = datapoints.points, @@ -152,20 +162,22 @@ index = getNextIndex(categories); for (var i = 0; i < points.length; i += ps) { - if (points[i] == null) + if (points[i] == null) { continue; - + } + for (var m = 0; m < ps; ++m) { var val = points[i + m]; - if (val == null || !format[m][formatColumn]) + if (val == null || !format[m][formatColumn]) { continue; + } if (!(val in categories)) { categories[val] = index; ++index; } - + points[i + m] = categories[val]; } } @@ -180,7 +192,7 @@ plot.hooks.processRawData.push(processRawData); plot.hooks.processDatapoints.push(processDatapoints); } - + $.plot.plugins.push({ init: init, options: options, diff -r f3af77e371a1 -r 9f02c09d2393 src/pyams_skin/resources/js/ext/flot/jquery.flot.categories.min.js --- a/src/pyams_skin/resources/js/ext/flot/jquery.flot.categories.min.js Thu Sep 26 11:55:17 2019 +0200 +++ b/src/pyams_skin/resources/js/ext/flot/jquery.flot.categories.min.js Wed Oct 16 13:00:43 2019 +0200 @@ -1,1 +1,1 @@ -!function(r){function o(r,o,e,i){var s="categories"==o.xaxis.options.mode,n="categories"==o.yaxis.options.mode;if(s||n){var a=i.format;if(!a){var t=o;if((a=[]).push({x:!0,number:!0,required:!0}),a.push({y:!0,number:!0,required:!0}),t.bars.show||t.lines.show&&t.lines.fill){var u=!!(t.bars.show&&t.bars.zero||t.lines.show&&t.lines.zero);a.push({y:!0,number:!0,required:!1,defaultValue:0,autoscale:u}),t.bars.horizontal&&(delete a[a.length-1].y,a[a.length-1].x=!0)}i.format=a}for(var f=0;fo&&(o=r[e]);return o+1}function i(r){var o=[];for(var e in r.categories){var i=r.categories[e];i>=r.min&&i<=r.max&&o.push([i,e])}return o.sort(function(r,o){return r[0]-o[0]}),o}function s(o,e,s){if("categories"==o[e].options.mode){if(!o[e].categories){var a={},t=o[e].options.categories||{};if(r.isArray(t))for(var u=0;uo&&(o=e[r]);return o+1}function i(e){var o=[];for(var r in e.categories){var i=e.categories[r];i>=e.min&&i<=e.max&&o.push([i,r])}return o.sort(function(e,o){return e[0]-o[0]}),o}function n(o,r,n){if("categories"===o[r].options.mode){if(!o[r].categories){var a={},t=o[r].options.categories||{};if(e.isArray(t))for(var u=0;u'; + tempImg.sourceComponent = canvasOrSvgSource; + + return function doGenerateTempImg(successCallbackFunc, failureCallbackFunc) { + tempImg.onload = function(evt) { + tempImg.successfullyLoaded = true; + successCallbackFunc(tempImg); + }; + + tempImg.onabort = function(evt) { + tempImg.successfullyLoaded = false; + console.log('Can\'t generate temp image from ' + tempImg.sourceDescription + '. It is possible that it is missing some properties or its content is not supported by this browser. Source component:', tempImg.sourceComponent); + successCallbackFunc(tempImg); //call successCallback, to allow snapshot of all working images + }; + + tempImg.onerror = function(evt) { + tempImg.successfullyLoaded = false; + console.log('Can\'t generate temp image from ' + tempImg.sourceDescription + '. It is possible that it is missing some properties or its content is not supported by this browser. Source component:', tempImg.sourceComponent); + successCallbackFunc(tempImg); //call successCallback, to allow snapshot of all working images + }; + + generateTempImageFromCanvasOrSvg(canvasOrSvgSource, tempImg); + }; + } + + function getExecuteImgComposition(destinationCanvas) { + return function executeImgComposition(tempImgs) { + var compositionResult = copyImgsToCanvas(tempImgs, destinationCanvas); + return compositionResult; + }; + } + + function copyCanvasToImg(canvas, img) { + img.src = canvas.toDataURL('image/png'); + } + + function getCSSRules(document) { + var styleSheets = document.styleSheets, + rulesList = []; + for (var i = 0; i < styleSheets.length; i++) { + // CORS requests for style sheets throw and an exception on Chrome > 64 + try { + // in Chrome, the external CSS files are empty when the page is directly loaded from disk + var rules = styleSheets[i].cssRules || []; + for (var j = 0; j < rules.length; j++) { + var rule = rules[j]; + rulesList.push(rule.cssText); + } + } catch (e) { + console.log('Failed to get some css rules'); + } + } + return rulesList; + } + + function embedCSSRulesInSVG(rules, svg) { + var text = [ + '', + '', + svg.innerHTML, + '' + ].join('\n'); + return text; + } + + function copySVGToImgMostBrowsers(svg, img) { + var rules = getCSSRules(document), + source = embedCSSRulesInSVG(rules, svg); + + source = patchSVGSource(source); + + var blob = new Blob([source], {type: "image/svg+xml;charset=utf-8"}), + domURL = self.URL || self.webkitURL || self, + url = domURL.createObjectURL(blob); + img.src = url; + } + + function copySVGToImgSafari(svg, img) { + // Use this method to convert a string buffer array to a binary string. + // Do so by breaking up large strings into smaller substrings; this is necessary to avoid the + // "maximum call stack size exceeded" exception that can happen when calling 'String.fromCharCode.apply' + // with a very long array. + function buildBinaryString (arrayBuffer) { + var binaryString = ""; + const utf8Array = new Uint8Array(arrayBuffer); + const blockSize = 16384; + for (var i = 0; i < utf8Array.length; i = i + blockSize) { + const binarySubString = String.fromCharCode.apply(null, utf8Array.subarray(i, i + blockSize)); + binaryString = binaryString + binarySubString; + } + return binaryString; + }; + + var rules = getCSSRules(document), + source = embedCSSRulesInSVG(rules, svg), + data, + utf8BinaryString; + + source = patchSVGSource(source); + + // Encode the string as UTF-8 and convert it to a binary string. The UTF-8 encoding is required to + // capture unicode characters correctly. + utf8BinaryString = buildBinaryString(new (TextEncoder || TextEncoderLite)('utf-8').encode(source)); + + data = "data:image/svg+xml;base64," + btoa(utf8BinaryString); + img.src = data; + } + + function patchSVGSource(svgSource) { + var source = ''; + //add name spaces. + if (!svgSource.match(/^]+xmlns="http:\/\/www\.w3\.org\/2000\/svg"/)) { + source = svgSource.replace(/^]+"http:\/\/www\.w3\.org\/1999\/xlink"/)) { + source = svgSource.replace(/^\r\n' + source; + } + + function copySVGToImg(svg, img) { + if (browser.isSafari() || browser.isMobileSafari()) { + copySVGToImgSafari(svg, img); + } else { + copySVGToImgMostBrowsers(svg, img); + } + } + + function adaptDestSizeToZoom(destinationCanvas, sources) { + function containsSVGs(source) { + return source.srcImgTagName === 'svg'; + } + + if (sources.find(containsSVGs) !== undefined) { + if (pixelRatio < 1) { + destinationCanvas.width = destinationCanvas.width * pixelRatio; + destinationCanvas.height = destinationCanvas.height * pixelRatio; + } + } + } + + function prepareImagesToBeComposed(sources, destination) { + var result = SUCCESSFULIMAGEPREPARATION; + if (sources.length === 0) { + result = EMPTYARRAYOFIMAGESOURCES; //nothing to do if called without sources + } else { + var minX = sources[0].genLeft; + var minY = sources[0].genTop; + var maxX = sources[0].genRight; + var maxY = sources[0].genBottom; + var i = 0; + + for (i = 1; i < sources.length; i++) { + if (minX > sources[i].genLeft) { + minX = sources[i].genLeft; + } + + if (minY > sources[i].genTop) { + minY = sources[i].genTop; + } + } + + for (i = 1; i < sources.length; i++) { + if (maxX < sources[i].genRight) { + maxX = sources[i].genRight; + } + + if (maxY < sources[i].genBottom) { + maxY = sources[i].genBottom; + } + } + + if ((maxX - minX <= 0) || (maxY - minY <= 0)) { + result = NEGATIVEIMAGESIZE; //this might occur on hidden images + } else { + destination.width = Math.round(maxX - minX); + destination.height = Math.round(maxY - minY); + + for (i = 0; i < sources.length; i++) { + sources[i].xCompOffset = sources[i].genLeft - minX; + sources[i].yCompOffset = sources[i].genTop - minY; + } + + adaptDestSizeToZoom(destination, sources); + } + } + return result; + } + + function copyImgsToCanvas(sources, destination) { + var prepareImagesResult = prepareImagesToBeComposed(sources, destination); + if (prepareImagesResult === SUCCESSFULIMAGEPREPARATION) { + var destinationCtx = destination.getContext('2d'); + + for (var i = 0; i < sources.length; i++) { + if (sources[i].successfullyLoaded === true) { + destinationCtx.drawImage(sources[i], sources[i].xCompOffset * pixelRatio, sources[i].yCompOffset * pixelRatio); + } + } + } + return prepareImagesResult; + } + + function adnotateDestImgWithBoundingClientRect(srcCanvasOrSvg, destImg) { + destImg.genLeft = srcCanvasOrSvg.getBoundingClientRect().left; + destImg.genTop = srcCanvasOrSvg.getBoundingClientRect().top; + + if (srcCanvasOrSvg.tagName === 'CANVAS') { + destImg.genRight = destImg.genLeft + srcCanvasOrSvg.width; + destImg.genBottom = destImg.genTop + srcCanvasOrSvg.height; + } + + if (srcCanvasOrSvg.tagName === 'svg') { + destImg.genRight = srcCanvasOrSvg.getBoundingClientRect().right; + destImg.genBottom = srcCanvasOrSvg.getBoundingClientRect().bottom; + } + } + + function generateTempImageFromCanvasOrSvg(srcCanvasOrSvg, destImg) { + if (srcCanvasOrSvg.tagName === 'CANVAS') { + copyCanvasToImg(srcCanvasOrSvg, destImg); + } + + if (srcCanvasOrSvg.tagName === 'svg') { + copySVGToImg(srcCanvasOrSvg, destImg); + } + + destImg.srcImgTagName = srcCanvasOrSvg.tagName; + adnotateDestImgWithBoundingClientRect(srcCanvasOrSvg, destImg); + } + + function failureCallback() { + return GENERALFAILURECALLBACKERROR; + } + + // used for testing + $.plot.composeImages = composeImages; + + function init(plot) { + // used to extend the public API of the plot + plot.composeImages = composeImages; + } + + $.plot.plugins.push({ + init: init, + name: 'composeImages', + version: '1.0' + }); +})(jQuery); diff -r f3af77e371a1 -r 9f02c09d2393 src/pyams_skin/resources/js/ext/flot/jquery.flot.composeImages.min.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/pyams_skin/resources/js/ext/flot/jquery.flot.composeImages.min.js Wed Oct 16 13:00:43 2019 +0200 @@ -0,0 +1,1 @@ +Unexpected token: keyword (const) diff -r f3af77e371a1 -r 9f02c09d2393 src/pyams_skin/resources/js/ext/flot/jquery.flot.crosshair.js --- a/src/pyams_skin/resources/js/ext/flot/jquery.flot.crosshair.js Thu Sep 26 11:55:17 2019 +0200 +++ b/src/pyams_skin/resources/js/ext/flot/jquery.flot.crosshair.js Wed Oct 16 13:00:43 2019 +0200 @@ -5,11 +5,11 @@ The plugin supports these options: - crosshair: { - mode: null or "x" or "y" or "xy" - color: color - lineWidth: number - } + crosshair: { + mode: null or "x" or "y" or "xy" + color: color + lineWidth: number + } Set the mode to one of "x", "y" or "xy". The "x" mode enables a vertical crosshair that lets you trace the values on the x axis, "y" enables a @@ -39,19 +39,19 @@ Example usage: - var myFlot = $.plot( $("#graph"), ..., { crosshair: { mode: "x" } } }; - $("#graph").bind( "plothover", function ( evt, position, item ) { - if ( item ) { - // Lock the crosshair to the data point being hovered - myFlot.lockCrosshair({ - x: item.datapoint[ 0 ], - y: item.datapoint[ 1 ] - }); - } else { - // Return normal crosshair operation - myFlot.unlockCrosshair(); - } - }); + var myFlot = $.plot( $("#graph"), ..., { crosshair: { mode: "x" } } }; + $("#graph").bind( "plothover", function ( evt, position, item ) { + if ( item ) { + // Lock the crosshair to the data point being hovered + myFlot.lockCrosshair({ + x: item.datapoint[ 0 ], + y: item.datapoint[ 1 ] + }); + } else { + // Return normal crosshair operation + myFlot.unlockCrosshair(); + } + }); - unlockCrosshair() @@ -66,63 +66,83 @@ lineWidth: 1 } }; - + function init(plot) { // position of crosshair in pixels - var crosshair = { x: -1, y: -1, locked: false }; + var crosshair = {x: -1, y: -1, locked: false, highlighted: false}; plot.setCrosshair = function setCrosshair(pos) { - if (!pos) + if (!pos) { crosshair.x = -1; - else { + } else { var o = plot.p2c(pos); crosshair.x = Math.max(0, Math.min(o.left, plot.width())); crosshair.y = Math.max(0, Math.min(o.top, plot.height())); } - + plot.triggerRedrawOverlay(); }; - + plot.clearCrosshair = plot.setCrosshair; // passes null for pos - + plot.lockCrosshair = function lockCrosshair(pos) { - if (pos) + if (pos) { plot.setCrosshair(pos); + } + crosshair.locked = true; }; plot.unlockCrosshair = function unlockCrosshair() { crosshair.locked = false; + crosshair.rect = null; }; function onMouseOut(e) { - if (crosshair.locked) + if (crosshair.locked) { return; + } - if (crosshair.x != -1) { + if (crosshair.x !== -1) { crosshair.x = -1; plot.triggerRedrawOverlay(); } } function onMouseMove(e) { - if (crosshair.locked) + var offset = plot.offset(); + if (crosshair.locked) { + var mouseX = Math.max(0, Math.min(e.pageX - offset.left, plot.width())); + var mouseY = Math.max(0, Math.min(e.pageY - offset.top, plot.height())); + + if ((mouseX > crosshair.x - 4) && (mouseX < crosshair.x + 4) && (mouseY > crosshair.y - 4) && (mouseY < crosshair.y + 4)) { + if (!crosshair.highlighted) { + crosshair.highlighted = true; + plot.triggerRedrawOverlay(); + } + } else { + if (crosshair.highlighted) { + crosshair.highlighted = false; + plot.triggerRedrawOverlay(); + } + } return; - + } + if (plot.getSelection && plot.getSelection()) { crosshair.x = -1; // hide the crosshair while selecting return; } - - var offset = plot.offset(); + crosshair.x = Math.max(0, Math.min(e.pageX - offset.left, plot.width())); crosshair.y = Math.max(0, Math.min(e.pageY - offset.top, plot.height())); plot.triggerRedrawOverlay(); } - + plot.hooks.bindEvents.push(function (plot, eventHolder) { - if (!plot.getOptions().crosshair.mode) + if (!plot.getOptions().crosshair.mode) { return; + } eventHolder.mouseout(onMouseOut); eventHolder.mousemove(onMouseMove); @@ -130,15 +150,16 @@ plot.hooks.drawOverlay.push(function (plot, ctx) { var c = plot.getOptions().crosshair; - if (!c.mode) + if (!c.mode) { return; + } var plotOffset = plot.getPlotOffset(); - + ctx.save(); ctx.translate(plotOffset.left, plotOffset.top); - if (crosshair.x != -1) { + if (crosshair.x !== -1) { var adj = plot.getOptions().crosshair.lineWidth % 2 ? 0.5 : 0; ctx.strokeStyle = c.color; @@ -146,16 +167,21 @@ ctx.lineJoin = "round"; ctx.beginPath(); - if (c.mode.indexOf("x") != -1) { + if (c.mode.indexOf("x") !== -1) { var drawX = Math.floor(crosshair.x) + adj; ctx.moveTo(drawX, 0); ctx.lineTo(drawX, plot.height()); } - if (c.mode.indexOf("y") != -1) { + if (c.mode.indexOf("y") !== -1) { var drawY = Math.floor(crosshair.y) + adj; ctx.moveTo(0, drawY); ctx.lineTo(plot.width(), drawY); } + if (crosshair.locked) { + if (crosshair.highlighted) ctx.fillStyle = 'orange'; + else ctx.fillStyle = c.color; + ctx.fillRect(Math.floor(crosshair.x) + adj - 4, Math.floor(crosshair.y) + adj - 4, 8, 8); + } ctx.stroke(); } ctx.restore(); @@ -166,7 +192,7 @@ eventHolder.unbind("mousemove", onMouseMove); }); } - + $.plot.plugins.push({ init: init, options: options, diff -r f3af77e371a1 -r 9f02c09d2393 src/pyams_skin/resources/js/ext/flot/jquery.flot.crosshair.min.js --- a/src/pyams_skin/resources/js/ext/flot/jquery.flot.crosshair.min.js Thu Sep 26 11:55:17 2019 +0200 +++ b/src/pyams_skin/resources/js/ext/flot/jquery.flot.crosshair.min.js Wed Oct 16 13:00:43 2019 +0200 @@ -1,7 +1,1 @@ -/* Javascript plotting library for jQuery, version 0.8.3. - -Copyright (c) 2007-2014 IOLA and Ole Laursen. -Licensed under the MIT license. - -*/ -(function($){var options={crosshair:{mode:null,color:"rgba(170, 0, 0, 0.80)",lineWidth:1}};function init(plot){var crosshair={x:-1,y:-1,locked:false};plot.setCrosshair=function setCrosshair(pos){if(!pos)crosshair.x=-1;else{var o=plot.p2c(pos);crosshair.x=Math.max(0,Math.min(o.left,plot.width()));crosshair.y=Math.max(0,Math.min(o.top,plot.height()))}plot.triggerRedrawOverlay()};plot.clearCrosshair=plot.setCrosshair;plot.lockCrosshair=function lockCrosshair(pos){if(pos)plot.setCrosshair(pos);crosshair.locked=true};plot.unlockCrosshair=function unlockCrosshair(){crosshair.locked=false};function onMouseOut(e){if(crosshair.locked)return;if(crosshair.x!=-1){crosshair.x=-1;plot.triggerRedrawOverlay()}}function onMouseMove(e){if(crosshair.locked)return;if(plot.getSelection&&plot.getSelection()){crosshair.x=-1;return}var offset=plot.offset();crosshair.x=Math.max(0,Math.min(e.pageX-offset.left,plot.width()));crosshair.y=Math.max(0,Math.min(e.pageY-offset.top,plot.height()));plot.triggerRedrawOverlay()}plot.hooks.bindEvents.push(function(plot,eventHolder){if(!plot.getOptions().crosshair.mode)return;eventHolder.mouseout(onMouseOut);eventHolder.mousemove(onMouseMove)});plot.hooks.drawOverlay.push(function(plot,ctx){var c=plot.getOptions().crosshair;if(!c.mode)return;var plotOffset=plot.getPlotOffset();ctx.save();ctx.translate(plotOffset.left,plotOffset.top);if(crosshair.x!=-1){var adj=plot.getOptions().crosshair.lineWidth%2?.5:0;ctx.strokeStyle=c.color;ctx.lineWidth=c.lineWidth;ctx.lineJoin="round";ctx.beginPath();if(c.mode.indexOf("x")!=-1){var drawX=Math.floor(crosshair.x)+adj;ctx.moveTo(drawX,0);ctx.lineTo(drawX,plot.height())}if(c.mode.indexOf("y")!=-1){var drawY=Math.floor(crosshair.y)+adj;ctx.moveTo(0,drawY);ctx.lineTo(plot.width(),drawY)}ctx.stroke()}ctx.restore()});plot.hooks.shutdown.push(function(plot,eventHolder){eventHolder.unbind("mouseout",onMouseOut);eventHolder.unbind("mousemove",onMouseMove)})}$.plot.plugins.push({init:init,options:options,name:"crosshair",version:"1.0"})})(jQuery); \ No newline at end of file +!function(e){jQuery.plot.plugins.push({init:function(e){function t(t){i.locked||-1!==i.x&&(i.x=-1,e.triggerRedrawOverlay())}function o(t){var o=e.offset();if(i.locked){var r=Math.max(0,Math.min(t.pageX-o.left,e.width())),h=Math.max(0,Math.min(t.pageY-o.top,e.height()));r>i.x-4&&ri.y-4&&h p1 and p1 -> mp + x2 = x1; + y2 = y1; + x1 = mx; + y1 = my; + + // 'remove' middle point + mx = null; + my = null; + + // subtract pointsize from i to have current point p1 handled again + i -= ps; + } else if (y1 !== y2 && x1 !== x2) { + // create a middle point + y2 = y1; + mx = x2; + my = y1; + } + } + + // clip with ymin + if (y1 <= y2 && y1 < axisy.min) { + if (y2 < axisy.min) { + // line segment is outside + continue; + } + // compute new intersection point + 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; + } + + // clip with ymax + 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; + } + + // clip with xmin + 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; + } + + // clip with xmax + 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, fillTowards, ctx, steps) { + var points = datapoints.points, + ps = datapoints.pointsize, + bottom = fillTowards > axisy.min ? Math.min(axisy.max, fillTowards) : axisy.min, + i = 0, + ypos = 1, + areaOpen = false, + segmentStart = 0, + segmentEnd = 0, + mx = null, + my = null; + + // we process each segment in two turns, first forward + // direction to sketch out top, then once we hit the + // end we go backwards to sketch the bottom + while (true) { + if (ps > 0 && i > points.length + ps) { + break; + } + + i += ps; // ps is negative if going backwards + + var x1 = points[i - ps], + y1 = points[i - ps + ypos], + x2 = points[i], + y2 = points[i + ypos]; + + if (ps === -2) { + /* going backwards and no value for the bottom provided in the series*/ + y1 = y2 = bottom; + } + + if (areaOpen) { + if (ps > 0 && x1 != null && x2 == null) { + // at turning point + segmentEnd = i; + ps = -ps; + ypos = 2; + continue; + } + + if (ps < 0 && i === segmentStart + ps) { + // done with the reverse sweep + ctx.fill(); + areaOpen = false; + ps = -ps; + ypos = 1; + i = segmentStart = segmentEnd + ps; + continue; + } + } + + if (x1 == null || x2 == null) { + mx = null; + my = null; + continue; + } + + if(steps){ + if (mx !== null && my !== null) { + // if middle point exists, transfer p2 -> p1 and p1 -> mp + x2 = x1; + y2 = y1; + x1 = mx; + y1 = my; + + // 'remove' middle point + mx = null; + my = null; + + // subtract pointsize from i to have current point p1 handled again + i -= ps; + } else if (y1 !== y2 && x1 !== x2) { + // create a middle point + y2 = y1; + mx = x2; + my = y1; + } + } + + // clip x values + + // clip with xmin + 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; + } + + // clip with xmax + 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) { + // open area + ctx.beginPath(); + ctx.moveTo(axisx.p2c(x1), axisy.p2c(bottom)); + areaOpen = true; + } + + // now first check the case where both is outside + 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; + } + + // else it's a bit more complicated, there might + // be a flat maxed out rectangle first, then a + // triangular cutout or reverse; to find these + // keep track of the current x values + var x1old = x1, + x2old = x2; + + // clip the y values, without shortcutting, we + // go through all cases in turn + + // clip with ymin + 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; + } + + // clip with ymax + 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 the x value was changed we got a rectangle + // to fill + if (x1 !== x1old) { + ctx.lineTo(axisx.p2c(x1old), axisy.p2c(y1)); + // it goes to (x1, y1), but we fill that below + } + + // fill triangular section, this sometimes result + // in redundant points if (x1, y1) hasn't changed + // from previous line to, but we just ignore that + ctx.lineTo(axisx.p2c(x1), axisy.p2c(y1)); + ctx.lineTo(axisx.p2c(x2), axisy.p2c(y2)); + + // fill the other rectangle if it's there + if (x2 !== x2old) { + ctx.lineTo(axisx.p2c(x2), axisy.p2c(y2)); + ctx.lineTo(axisx.p2c(x2old), axisy.p2c(y2)); + } + } + } + + /** + - drawSeriesLines(series, ctx, plotOffset, plotWidth, plotHeight, drawSymbol, getColorOrGradient) + + This function is used for drawing lines or area fill. In case the series has line decimation function + attached, before starting to draw, as an optimization the points will first be decimated. + + The series parameter contains the series to be drawn on ctx context. The plotOffset, plotWidth and + plotHeight are the corresponding parameters of flot used to determine the drawing surface. + The function getColorOrGradient is used to compute the fill style of lines and area. + */ + function drawSeriesLines(series, ctx, plotOffset, plotWidth, plotHeight, drawSymbol, getColorOrGradient) { + ctx.save(); + ctx.translate(plotOffset.left, plotOffset.top); + ctx.lineJoin = "round"; + + if (series.lines.dashes && ctx.setLineDash) { + ctx.setLineDash(series.lines.dashes); + } + + var datapoints = { + format: series.datapoints.format, + points: series.datapoints.points, + pointsize: series.datapoints.pointsize + }; + + if (series.decimate) { + datapoints.points = series.decimate(series, series.xaxis.min, series.xaxis.max, plotWidth, series.yaxis.min, series.yaxis.max, plotHeight); + } + + var lw = series.lines.lineWidth; + + ctx.lineWidth = lw; + ctx.strokeStyle = series.color; + var fillStyle = getFillStyle(series.lines, series.color, 0, plotHeight, getColorOrGradient); + if (fillStyle) { + ctx.fillStyle = fillStyle; + plotLineArea(datapoints, series.xaxis, series.yaxis, series.lines.fillTowards || 0, ctx, series.lines.steps); + } + + if (lw > 0) { + plotLine(datapoints, 0, 0, series.xaxis, series.yaxis, ctx, series.lines.steps); + } + + ctx.restore(); + } + + /** + - drawSeriesPoints(series, ctx, plotOffset, plotWidth, plotHeight, drawSymbol, getColorOrGradient) + + This function is used for drawing points using a given symbol. In case the series has points decimation + function attached, before starting to draw, as an optimization the points will first be decimated. + + The series parameter contains the series to be drawn on ctx context. The plotOffset, plotWidth and + plotHeight are the corresponding parameters of flot used to determine the drawing surface. + The function drawSymbol is used to compute and draw the symbol chosen for the points. + */ + function drawSeriesPoints(series, ctx, plotOffset, plotWidth, plotHeight, drawSymbol, getColorOrGradient) { + function drawCircle(ctx, x, y, radius, shadow, fill) { + ctx.moveTo(x + radius, y); + ctx.arc(x, y, radius, 0, shadow ? Math.PI : Math.PI * 2, false); + } + drawCircle.fill = true; + function plotPoints(datapoints, radius, fill, offset, shadow, axisx, axisy, drawSymbolFn) { + var points = datapoints.points, + ps = datapoints.pointsize; + + ctx.beginPath(); + 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; + } + + x = axisx.p2c(x); + y = axisy.p2c(y) + offset; + + drawSymbolFn(ctx, x, y, radius, shadow, fill); + } + if (drawSymbolFn.fill && !shadow) { + ctx.fill(); + } + ctx.stroke(); + } + + ctx.save(); + ctx.translate(plotOffset.left, plotOffset.top); + + var datapoints = { + format: series.datapoints.format, + points: series.datapoints.points, + pointsize: series.datapoints.pointsize + }; + + if (series.decimatePoints) { + datapoints.points = series.decimatePoints(series, series.xaxis.min, series.xaxis.max, plotWidth, series.yaxis.min, series.yaxis.max, plotHeight); + } + + var lw = series.points.lineWidth, + radius = series.points.radius, + symbol = series.points.symbol, + drawSymbolFn; + + if (symbol === 'circle') { + drawSymbolFn = drawCircle; + } else if (typeof symbol === 'string' && drawSymbol && drawSymbol[symbol]) { + drawSymbolFn = drawSymbol[symbol]; + } else if (typeof drawSymbol === 'function') { + drawSymbolFn = drawSymbol; + } + + // If the user sets the line width to 0, we change it to a very + // small value. A line width of 0 seems to force the default of 1. + + if (lw === 0) { + lw = 0.0001; + } + + ctx.lineWidth = lw; + ctx.fillStyle = getFillStyle(series.points, series.color, null, null, getColorOrGradient); + ctx.strokeStyle = series.color; + plotPoints(datapoints, radius, + true, 0, false, + series.xaxis, series.yaxis, drawSymbolFn); + ctx.restore(); + } + + function drawBar(x, y, b, barLeft, barRight, fillStyleCallback, axisx, axisy, c, horizontal, lineWidth) { + var left = x + barLeft, + right = x + barRight, + bottom = b, top = y, + drawLeft, drawRight, drawTop, drawBottom = false, + tmp; + + drawLeft = drawRight = drawTop = true; + + // in horizontal mode, we start the bar from the left + // instead of from the bottom so it appears to be + // horizontal rather than vertical + if (horizontal) { + drawBottom = drawRight = drawTop = true; + drawLeft = false; + left = b; + right = x; + top = y + barLeft; + bottom = y + barRight; + + // account for negative bars + 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; + + // account for negative bars + if (top < bottom) { + tmp = top; + top = bottom; + bottom = tmp; + drawBottom = true; + drawTop = false; + } + } + + // clip + 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); + + // fill the bar + if (fillStyleCallback) { + c.fillStyle = fillStyleCallback(bottom, top); + c.fillRect(left, top, right - left, bottom - top) + } + + // draw outline + if (lineWidth > 0 && (drawLeft || drawRight || drawTop || drawBottom)) { + c.beginPath(); + + // FIXME: inline moveTo is buggy with excanvas + 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(); + } + } + + /** + - drawSeriesBars(series, ctx, plotOffset, plotWidth, plotHeight, drawSymbol, getColorOrGradient) + + This function is used for drawing series represented as bars. In case the series has decimation + function attached, before starting to draw, as an optimization the points will first be decimated. + + The series parameter contains the series to be drawn on ctx context. The plotOffset, plotWidth and + plotHeight are the corresponding parameters of flot used to determine the drawing surface. + The function getColorOrGradient is used to compute the fill style of bars. + */ + function drawSeriesBars(series, ctx, plotOffset, plotWidth, plotHeight, drawSymbol, getColorOrGradient) { + function plotBars(datapoints, barLeft, barRight, fillStyleCallback, axisx, axisy) { + var points = datapoints.points, + ps = datapoints.pointsize, + fillTowards = series.bars.fillTowards || 0, + calculatedBottom = fillTowards > axisy.min ? Math.min(axisy.max, fillTowards) : axisy.min; + + for (var i = 0; i < points.length; i += ps) { + if (points[i] == null) { + continue; + } + + // Use third point as bottom if pointsize is 3 + var bottom = ps === 3 ? points[i + 2] : calculatedBottom; + drawBar(points[i], points[i + 1], bottom, barLeft, barRight, fillStyleCallback, axisx, axisy, ctx, series.bars.horizontal, series.bars.lineWidth); + } + } + + ctx.save(); + ctx.translate(plotOffset.left, plotOffset.top); + + var datapoints = { + format: series.datapoints.format, + points: series.datapoints.points, + pointsize: series.datapoints.pointsize + }; + + if (series.decimate) { + datapoints.points = series.decimate(series, series.xaxis.min, series.xaxis.max, plotWidth); + } + + ctx.lineWidth = series.bars.lineWidth; + ctx.strokeStyle = series.color; + + var barLeft; + var barWidth = series.bars.barWidth[0] || series.bars.barWidth; + switch (series.bars.align) { + case "left": + barLeft = 0; + break; + case "right": + barLeft = -barWidth; + break; + default: + barLeft = -barWidth / 2; + } + + var fillStyleCallback = series.bars.fill ? function(bottom, top) { + return getFillStyle(series.bars, series.color, bottom, top, getColorOrGradient); + } : null; + + plotBars(datapoints, barLeft, barLeft + barWidth, fillStyleCallback, series.xaxis, series.yaxis); + ctx.restore(); + } + + function getFillStyle(filloptions, seriesColor, bottom, top, getColorOrGradient) { + 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 : 0.4; + c.normalize(); + return c.toString(); + } + + this.drawSeriesLines = drawSeriesLines; + this.drawSeriesPoints = drawSeriesPoints; + this.drawSeriesBars = drawSeriesBars; + this.drawBar = drawBar; + }; + + $.plot.drawSeries = new DrawSeries(); +})(jQuery); diff -r f3af77e371a1 -r 9f02c09d2393 src/pyams_skin/resources/js/ext/flot/jquery.flot.drawSeries.min.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/pyams_skin/resources/js/ext/flot/jquery.flot.drawSeries.min.js Wed Oct 16 13:00:43 2019 +0200 @@ -0,0 +1,1 @@ +!function(i){"use strict";i.plot.drawSeries=new function(){function n(i,n,a,e,l,t,o){var s=i.points,m=i.pointsize,r=null,f=null,c=0,x=0,p=0,u=0,d=null,h=null,v=0;for(t.beginPath(),v=m;v=u&&x>l.max){if(u>l.max)continue;c=(l.max-x)/(u-x)*(p-c)+c,x=l.max}else if(u>=x&&u>l.max){if(x>l.max)continue;p=(l.max-x)/(u-x)*(p-c)+c,u=l.max}if(c<=p&&c=p&&c>e.max){if(p>e.max)continue;x=(e.max-c)/(p-c)*(u-x)+x,c=e.max}else if(p>=c&&p>e.max){if(c>e.max)continue;u=(e.max-c)/(p-c)*(u-x)+x,p=e.max}c===r&&x===f||t.moveTo(e.p2c(c)+n,l.p2c(x)+a),r=p,f=u,t.lineTo(e.p2c(p)+n,l.p2c(u)+a)}else d=null,h=null;t.stroke()}function a(i,n,a,e,l,t){for(var o=i.points,s=i.pointsize,m=e>a.min?Math.min(a.max,e):a.min,r=0,f=1,c=!1,x=0,p=0,u=null,d=null;!(s>0&&r>o.length+s);){var h=o[(r+=s)-s],v=o[r-s+f],T=o[r],b=o[r+f];if(-2===s&&(v=b=m),c){if(s>0&&null!=h&&null==T){p=r,s=-s,f=2;continue}if(s<0&&r===x+s){l.fill(),c=!1,f=1,r=x=p+(s=-s);continue}}if(null!=h&&null!=T){if(t&&(null!==u&&null!==d?(T=h,b=v,h=u,v=d,u=null,d=null,r-=s):v!==b&&h!==T&&(b=v,u=T,d=v)),h<=T&&h=T&&h>n.max){if(T>n.max)continue;v=(n.max-h)/(T-h)*(b-v)+v,h=n.max}else if(T>=h&&T>n.max){if(h>n.max)continue;b=(n.max-h)/(T-h)*(b-v)+v,T=n.max}if(c||(l.beginPath(),l.moveTo(n.p2c(h),a.p2c(m)),c=!0),v>=a.max&&b>=a.max)l.lineTo(n.p2c(h),a.p2c(a.max)),l.lineTo(n.p2c(T),a.p2c(a.max));else if(v<=a.min&&b<=a.min)l.lineTo(n.p2c(h),a.p2c(a.min)),l.lineTo(n.p2c(T),a.p2c(a.min));else{var y=h,g=T;v<=b&&v=a.min?(h=(a.min-v)/(b-v)*(T-h)+h,v=a.min):b<=v&&b=a.min&&(T=(a.min-v)/(b-v)*(T-h)+h,b=a.min),v>=b&&v>a.max&&b<=a.max?(h=(a.max-v)/(b-v)*(T-h)+h,v=a.max):b>=v&&b>a.max&&v<=a.max&&(T=(a.max-v)/(b-v)*(T-h)+h,b=a.max),h!==y&&l.lineTo(n.p2c(y),a.p2c(v)),l.lineTo(n.p2c(h),a.p2c(v)),l.lineTo(n.p2c(T),a.p2c(b)),T!==g&&(l.lineTo(n.p2c(T),a.p2c(b)),l.lineTo(n.p2c(g),a.p2c(b)))}}else u=null,d=null}}function e(i,n,a,e,l,t,o,s,m,r,f){var c,x,p,u,d=i+e,h=i+l,v=a,T=n,b=!1;c=x=p=!0,r?(b=x=p=!0,c=!1,T=n+e,v=n+l,(h=i)<(d=a)&&(u=h,h=d,d=u,c=!0,x=!1)):(c=x=p=!0,b=!1,d=i+e,h=i+l,(T=n)<(v=a)&&(u=T,T=v,v=u,b=!0,p=!1)),ho.max||Ts.max||(do.max&&(h=o.max,x=!1),vs.max&&(T=s.max,p=!1),d=o.p2c(d),v=s.p2c(v),h=o.p2c(h),T=s.p2c(T),t&&(m.fillStyle=t(v,T),m.fillRect(d,T,h-d,v-T)),f>0&&(c||x||p||b)&&(m.beginPath(),m.moveTo(d,v),c?m.lineTo(d,T):m.moveTo(d,T),p?m.lineTo(h,T):m.moveTo(h,T),x?m.lineTo(h,v):m.moveTo(h,v),b?m.lineTo(d,v):m.moveTo(d,v),m.stroke()))}function l(n,a,e,l,t){var o=n.fill;if(!o)return null;if(n.fillColor)return t(n.fillColor,e,l,a);var s=i.color.parse(a);return s.a="number"==typeof o?o:.4,s.normalize(),s.toString()}this.drawSeriesLines=function(i,e,t,o,s,m,r){e.save(),e.translate(t.left,t.top),e.lineJoin="round",i.lines.dashes&&e.setLineDash&&e.setLineDash(i.lines.dashes);var f={format:i.datapoints.format,points:i.datapoints.points,pointsize:i.datapoints.pointsize};i.decimate&&(f.points=i.decimate(i,i.xaxis.min,i.xaxis.max,o,i.yaxis.min,i.yaxis.max,s));var c=i.lines.lineWidth;e.lineWidth=c,e.strokeStyle=i.color;var x=l(i.lines,i.color,0,s,r);x&&(e.fillStyle=x,a(f,i.xaxis,i.yaxis,i.lines.fillTowards||0,e,i.lines.steps)),c>0&&n(f,0,0,i.xaxis,i.yaxis,e,i.lines.steps),e.restore()},this.drawSeriesPoints=function(i,n,a,e,t,o,s){function m(i,n,a,e,l,t){i.moveTo(n+e,a),i.arc(n,a,e,0,l?Math.PI:2*Math.PI,!1)}m.fill=!0,n.save(),n.translate(a.left,a.top);var r={format:i.datapoints.format,points:i.datapoints.points,pointsize:i.datapoints.pointsize};i.decimatePoints&&(r.points=i.decimatePoints(i,i.xaxis.min,i.xaxis.max,e,i.yaxis.min,i.yaxis.max,t));var f,c=i.points.lineWidth,x=i.points.radius,p=i.points.symbol;"circle"===p?f=m:"string"==typeof p&&o&&o[p]?f=o[p]:"function"==typeof o&&(f=o),0===c&&(c=1e-4),n.lineWidth=c,n.fillStyle=l(i.points,i.color,null,null,s),n.strokeStyle=i.color,function(i,a,e,l,t,o,s,m){var r=i.points,f=i.pointsize;n.beginPath();for(var c=0;co.max||ps.max||(x=o.p2c(x),p=s.p2c(p)+l,m(n,x,p,a,t,e))}m.fill&&!t&&n.fill(),n.stroke()}(r,x,!0,0,!1,i.xaxis,i.yaxis,f),n.restore()},this.drawSeriesBars=function(i,n,a,t,o,s,m){n.save(),n.translate(a.left,a.top);var r={format:i.datapoints.format,points:i.datapoints.points,pointsize:i.datapoints.pointsize};i.decimate&&(r.points=i.decimate(i,i.xaxis.min,i.xaxis.max,t)),n.lineWidth=i.bars.lineWidth,n.strokeStyle=i.color;var f,c=i.bars.barWidth[0]||i.bars.barWidth;switch(i.bars.align){case"left":f=0;break;case"right":f=-c;break;default:f=-c/2}!function(a,l,t,o,s,m){for(var r=a.points,f=a.pointsize,c=i.bars.fillTowards||0,x=c>m.min?Math.min(m.max,c):m.min,p=0;p ax[1].max || y < ax[1].min || upper < ax[0].min || lower > ax[0].max) + if (err[e].err === 'x') { + if (y > ax[1].max || y < ax[1].min || upper < ax[0].min || lower > ax[0].max) { continue; - if (err[e].err == 'y') - if (x > ax[0].max || x < ax[0].min || upper < ax[1].min || lower > ax[1].max) + } + } + + if (err[e].err === 'y') { + if (x > ax[0].max || x < ax[0].min || upper < ax[1].min || lower > ax[1].max) { continue; + } + } // prevent errorbars getting out of the canvas var drawUpper = true, @@ -227,9 +242,9 @@ } //sanity check, in case some inverted axis hack is applied to flot - if ((err[e].err == 'x' && invertX) || (err[e].err == 'y' && invertY)) { + if ((err[e].err === 'x' && invertX) || (err[e].err === 'y' && invertY)) { //swap coordinates - var tmp = lower; + tmp = lower; lower = upper; upper = tmp; tmp = drawLower; @@ -241,9 +256,9 @@ } // convert to pixels - x = ax[0].p2c(x), - y = ax[1].p2c(y), - upper = ax[e].p2c(upper); + x = ax[0].p2c(x); + y = ax[1].p2c(y); + upper = ax[e].p2c(upper); lower = ax[e].p2c(lower); minmax[0] = ax[e].p2c(minmax[0]); minmax[1] = ax[e].p2c(minmax[1]); @@ -257,13 +272,15 @@ var w = sw / 2; ctx.lineWidth = w; ctx.strokeStyle = "rgba(0,0,0,0.1)"; - drawError(ctx, err[e], x, y, upper, lower, drawUpper, drawLower, radius, w + w/2, minmax); + drawError(ctx, err[e], x, y, upper, lower, drawUpper, drawLower, radius, w + w / 2, minmax); ctx.strokeStyle = "rgba(0,0,0,0.2)"; - drawError(ctx, err[e], x, y, upper, lower, drawUpper, drawLower, radius, w/2, minmax); + drawError(ctx, err[e], x, y, upper, lower, drawUpper, drawLower, radius, w / 2, minmax); } - ctx.strokeStyle = err[e].color? err[e].color: s.color; + ctx.strokeStyle = err[e].color + ? err[e].color + : s.color; ctx.lineWidth = lw; //draw it drawError(ctx, err[e], x, y, upper, lower, drawUpper, drawLower, radius, 0, minmax); @@ -272,69 +289,74 @@ } } - function drawError(ctx,err,x,y,upper,lower,drawUpper,drawLower,radius,offset,minmax){ - + function drawError(ctx, err, x, y, upper, lower, drawUpper, drawLower, radius, offset, minmax) { //shadow offset y += offset; upper += offset; lower += offset; // error bar - avoid plotting over circles - if (err.err == 'x'){ - if (upper > x + radius) drawPath(ctx, [[upper,y],[Math.max(x + radius,minmax[0]),y]]); + if (err.err === 'x') { + if (upper > x + radius) drawPath(ctx, [[upper, y], [Math.max(x + radius, minmax[0]), y]]); else drawUpper = false; - if (lower < x - radius) drawPath(ctx, [[Math.min(x - radius,minmax[1]),y],[lower,y]] ); + + if (lower < x - radius) drawPath(ctx, [[Math.min(x - radius, minmax[1]), y], [lower, y]]); else drawLower = false; - } - else { - if (upper < y - radius) drawPath(ctx, [[x,upper],[x,Math.min(y - radius,minmax[0])]] ); + } else { + if (upper < y - radius) drawPath(ctx, [[x, upper], [x, Math.min(y - radius, minmax[0])]]); else drawUpper = false; - if (lower > y + radius) drawPath(ctx, [[x,Math.max(y + radius,minmax[1])],[x,lower]] ); + + if (lower > y + radius) drawPath(ctx, [[x, Math.max(y + radius, minmax[1])], [x, lower]]); else drawLower = false; } //internal radius value in errorbar, allows to plot radius 0 points and still keep proper sized caps //this is a way to get errorbars on lines without visible connecting dots - radius = err.radius != null? err.radius: radius; + radius = err.radius != null + ? err.radius + : radius; // upper cap if (drawUpper) { - if (err.upperCap == '-'){ - if (err.err=='x') drawPath(ctx, [[upper,y - radius],[upper,y + radius]] ); - else drawPath(ctx, [[x - radius,upper],[x + radius,upper]] ); - } else if ($.isFunction(err.upperCap)){ - if (err.err=='x') err.upperCap(ctx, upper, y, radius); + if (err.upperCap === '-') { + if (err.err === 'x') drawPath(ctx, [[upper, y - radius], [upper, y + radius]]); + else drawPath(ctx, [[x - radius, upper], [x + radius, upper]]); + } else if ($.isFunction(err.upperCap)) { + if (err.err === 'x') err.upperCap(ctx, upper, y, radius); else err.upperCap(ctx, x, upper, radius); } } // lower cap if (drawLower) { - if (err.lowerCap == '-'){ - if (err.err=='x') drawPath(ctx, [[lower,y - radius],[lower,y + radius]] ); - else drawPath(ctx, [[x - radius,lower],[x + radius,lower]] ); - } else if ($.isFunction(err.lowerCap)){ - if (err.err=='x') err.lowerCap(ctx, lower, y, radius); + if (err.lowerCap === '-') { + if (err.err === 'x') drawPath(ctx, [[lower, y - radius], [lower, y + radius]]); + else drawPath(ctx, [[x - radius, lower], [x + radius, lower]]); + } else if ($.isFunction(err.lowerCap)) { + if (err.err === 'x') err.lowerCap(ctx, lower, y, radius); else err.lowerCap(ctx, x, lower, radius); } } } - function drawPath(ctx, pts){ + function drawPath(ctx, pts) { ctx.beginPath(); ctx.moveTo(pts[0][0], pts[0][1]); - for (var p=1; p < pts.length; p++) + for (var p = 1; p < pts.length; p++) { ctx.lineTo(pts[p][0], pts[p][1]); + } + ctx.stroke(); } - function draw(plot, ctx){ + function draw(plot, ctx) { var plotOffset = plot.getPlotOffset(); ctx.save(); ctx.translate(plotOffset.left, plotOffset.top); $.each(plot.getData(), function (i, s) { - if (s.points.errorbars && (s.points.xerr.show || s.points.yerr.show)) + if (s.points.errorbars && (s.points.xerr.show || s.points.yerr.show)) { drawSeriesErrors(plot, ctx, s); + } }); ctx.restore(); } @@ -345,9 +367,9 @@ } $.plot.plugins.push({ - init: init, - options: options, - name: 'errorbars', - version: '1.0' - }); + init: init, + options: options, + name: 'errorbars', + version: '1.0' + }); })(jQuery); diff -r f3af77e371a1 -r 9f02c09d2393 src/pyams_skin/resources/js/ext/flot/jquery.flot.errorbars.min.js --- a/src/pyams_skin/resources/js/ext/flot/jquery.flot.errorbars.min.js Thu Sep 26 11:55:17 2019 +0200 +++ b/src/pyams_skin/resources/js/ext/flot/jquery.flot.errorbars.min.js Wed Oct 16 13:00:43 2019 +0200 @@ -1,7 +1,1 @@ -/* Javascript plotting library for jQuery, version 0.8.3. - -Copyright (c) 2007-2014 IOLA and Ole Laursen. -Licensed under the MIT license. - -*/ -(function($){var options={series:{points:{errorbars:null,xerr:{err:"x",show:null,asymmetric:null,upperCap:null,lowerCap:null,color:null,radius:null},yerr:{err:"y",show:null,asymmetric:null,upperCap:null,lowerCap:null,color:null,radius:null}}}};function processRawData(plot,series,data,datapoints){if(!series.points.errorbars)return;var format=[{x:true,number:true,required:true},{y:true,number:true,required:true}];var errors=series.points.errorbars;if(errors=="x"||errors=="xy"){if(series.points.xerr.asymmetric){format.push({x:true,number:true,required:true});format.push({x:true,number:true,required:true})}else format.push({x:true,number:true,required:true})}if(errors=="y"||errors=="xy"){if(series.points.yerr.asymmetric){format.push({y:true,number:true,required:true});format.push({y:true,number:true,required:true})}else format.push({y:true,number:true,required:true})}datapoints.format=format}function parseErrors(series,i){var points=series.datapoints.points;var exl=null,exu=null,eyl=null,eyu=null;var xerr=series.points.xerr,yerr=series.points.yerr;var eb=series.points.errorbars;if(eb=="x"||eb=="xy"){if(xerr.asymmetric){exl=points[i+2];exu=points[i+3];if(eb=="xy")if(yerr.asymmetric){eyl=points[i+4];eyu=points[i+5]}else eyl=points[i+4]}else{exl=points[i+2];if(eb=="xy")if(yerr.asymmetric){eyl=points[i+3];eyu=points[i+4]}else eyl=points[i+3]}}else if(eb=="y")if(yerr.asymmetric){eyl=points[i+2];eyu=points[i+3]}else eyl=points[i+2];if(exu==null)exu=exl;if(eyu==null)eyu=eyl;var errRanges=[exl,exu,eyl,eyu];if(!xerr.show){errRanges[0]=null;errRanges[1]=null}if(!yerr.show){errRanges[2]=null;errRanges[3]=null}return errRanges}function drawSeriesErrors(plot,ctx,s){var points=s.datapoints.points,ps=s.datapoints.pointsize,ax=[s.xaxis,s.yaxis],radius=s.points.radius,err=[s.points.xerr,s.points.yerr];var invertX=false;if(ax[0].p2c(ax[0].max)ax[1].max||yax[0].max)continue;if(err[e].err=="y")if(x>ax[0].max||xax[1].max)continue;var drawUpper=true,drawLower=true;if(upper>minmax[1]){drawUpper=false;upper=minmax[1]}if(lower0&&sw>0){var w=sw/2;ctx.lineWidth=w;ctx.strokeStyle="rgba(0,0,0,0.1)";drawError(ctx,err[e],x,y,upper,lower,drawUpper,drawLower,radius,w+w/2,minmax);ctx.strokeStyle="rgba(0,0,0,0.2)";drawError(ctx,err[e],x,y,upper,lower,drawUpper,drawLower,radius,w/2,minmax)}ctx.strokeStyle=err[e].color?err[e].color:s.color;ctx.lineWidth=lw;drawError(ctx,err[e],x,y,upper,lower,drawUpper,drawLower,radius,0,minmax)}}}}function drawError(ctx,err,x,y,upper,lower,drawUpper,drawLower,radius,offset,minmax){y+=offset;upper+=offset;lower+=offset;if(err.err=="x"){if(upper>x+radius)drawPath(ctx,[[upper,y],[Math.max(x+radius,minmax[0]),y]]);else drawUpper=false;if(lowery+radius)drawPath(ctx,[[x,Math.max(y+radius,minmax[1])],[x,lower]]);else drawLower=false}radius=err.radius!=null?err.radius:radius;if(drawUpper){if(err.upperCap=="-"){if(err.err=="x")drawPath(ctx,[[upper,y-radius],[upper,y+radius]]);else drawPath(ctx,[[x-radius,upper],[x+radius,upper]])}else if($.isFunction(err.upperCap)){if(err.err=="x")err.upperCap(ctx,upper,y,radius);else err.upperCap(ctx,x,upper,radius)}}if(drawLower){if(err.lowerCap=="-"){if(err.err=="x")drawPath(ctx,[[lower,y-radius],[lower,y+radius]]);else drawPath(ctx,[[x-radius,lower],[x+radius,lower]])}else if($.isFunction(err.lowerCap)){if(err.err=="x")err.lowerCap(ctx,lower,y,radius);else err.lowerCap(ctx,x,lower,radius)}}}function drawPath(ctx,pts){ctx.beginPath();ctx.moveTo(pts[0][0],pts[0][1]);for(var p=1;pl[1].max||wl[0].max))continue;if("y"===u[y].err&&(f>l[0].max||fl[1].max))continue;var v=!0,g=!0;C>d[1]&&(v=!1,C=d[1]),b0&&k>0){var S=k/2;e.lineWidth=S,e.strokeStyle="rgba(0,0,0,0.1)",o(e,u[y],f,w,C,b,v,g,s,S+S/2,d),e.strokeStyle="rgba(0,0,0,0.2)",o(e,u[y],f,w,C,b,v,g,s,S/2,d)}e.strokeStyle=u[y].color?u[y].color:i.color,e.lineWidth=q,o(e,u[y],f,w,C,b,v,g,s,0,d)}}}function o(e,n,i,o,p,t,l,s,u,m,c){o+=m,p+=m,t+=m,"x"===n.err?(p>i+u?a(e,[[p,o],[Math.max(i+u,c[0]),o]]):l=!1,to+u?a(e,[[i,Math.max(o+u,c[1])],[i,t]]):s=!1),u=null!=n.radius?n.radius:u,l&&("-"===n.upperCap?"x"===n.err?a(e,[[p,o-u],[p,o+u]]):a(e,[[i-u,p],[i+u,p]]):r.isFunction(n.upperCap)&&("x"===n.err?n.upperCap(e,p,o,u):n.upperCap(e,i,p,u))),s&&("-"===n.lowerCap?"x"===n.err?a(e,[[t,o-u],[t,o+u]]):a(e,[[i-u,t],[i+u,t]]):r.isFunction(n.lowerCap)&&("x"===n.err?n.lowerCap(e,t,o,u):n.lowerCap(e,i,t,u)))}function a(r,e){r.beginPath(),r.moveTo(e[0][0],e[0][1]);for(var n=1;n= allseries.length ) { - return null; - } - return allseries[ s.fillBetween ]; - } + if (typeof s.fillBetween === "number") { + if (s.fillBetween < 0 || s.fillBetween >= allseries.length) { + return null; + } + return allseries[ s.fillBetween ]; + } - return null; - } + return null; + } - function computeFillBottoms( plot, s, datapoints ) { - - if ( s.fillBetween == null ) { - return; - } + function computeFormat(plot, s, data, datapoints) { + if (s.fillBetween == null) { + return; + } + + format = datapoints.format; + var plotHasId = function(id) { + var plotData = plot.getData(); + for (i = 0; i < plotData.length; i++) { + if (plotData[i].id === id) { + return true; + } + } - var other = findBottomSeries( s, plot.getData() ); + return false; + } - if ( !other ) { - return; - } + if (!format) { + format = []; - var ps = datapoints.pointsize, - points = datapoints.points, - otherps = other.datapoints.pointsize, - otherpoints = other.datapoints.points, - newpoints = [], - px, py, intery, qx, qy, bottom, - withlines = s.lines.show, - withbottom = ps > 2 && datapoints.format[2].y, - withsteps = withlines && s.lines.steps, - fromgap = true, - i = 0, - j = 0, - l, m; + format.push({ + x: true, + number: true, + computeRange: s.xaxis.options.autoScale !== 'none', + required: true + }); + format.push({ + y: true, + number: true, + computeRange: s.yaxis.options.autoScale !== 'none', + required: true + }); - while ( true ) { - - if ( i >= points.length ) { - break; - } + if (s.fillBetween !== undefined && s.fillBetween !== '' && plotHasId(s.fillBetween) && s.fillBetween !== s.id) { + format.push({ + x: false, + y: true, + number: true, + required: false, + computeRange: s.yaxis.options.autoScale !== 'none', + defaultValue: 0 + }); + } - l = newpoints.length; - - if ( points[ i ] == null ) { - - // copy gaps + datapoints.format = format; + } + } - for ( m = 0; m < ps; ++m ) { - newpoints.push( points[ i + m ] ); - } + function computeFillBottoms(plot, s, datapoints) { + if (s.fillBetween == null) { + return; + } - i += ps; + var other = findBottomSeries(s, plot.getData()); - } else if ( j >= otherpoints.length ) { - - // for lines, we can't use the rest of the points + if (!other) { + return; + } - if ( !withlines ) { - for ( m = 0; m < ps; ++m ) { - newpoints.push( points[ i + m ] ); - } - } - - i += ps; + var ps = datapoints.pointsize, + points = datapoints.points, + otherps = other.datapoints.pointsize, + otherpoints = other.datapoints.points, + newpoints = [], + px, py, intery, qx, qy, bottom, + withlines = s.lines.show, + withbottom = ps > 2 && datapoints.format[2].y, + withsteps = withlines && s.lines.steps, + fromgap = true, + i = 0, + j = 0, + l, m; - } else if ( otherpoints[ j ] == null ) { - - // oops, got a gap + while (true) { + if (i >= points.length) { + break; + } - for ( m = 0; m < ps; ++m ) { - newpoints.push( null ); - } - - fromgap = true; - j += otherps; + l = newpoints.length; - } else { - - // cases where we actually got two points - - px = points[ i ]; - py = points[ i + 1 ]; - qx = otherpoints[ j ]; - qy = otherpoints[ j + 1 ]; - bottom = 0; - - if ( px === qx ) { + if (points[ i ] == null) { + // copy gaps + for (m = 0; m < ps; ++m) { + newpoints.push(points[ i + m ]); + } - for ( m = 0; m < ps; ++m ) { - newpoints.push( points[ i + m ] ); - } + i += ps; + } else if (j >= otherpoints.length) { + // for lines, we can't use the rest of the points + if (!withlines) { + for (m = 0; m < ps; ++m) { + newpoints.push(points[ i + m ]); + } + } - //newpoints[ l + 1 ] += qy; - bottom = qy; - - i += ps; - j += otherps; - - } else if ( px > qx ) { + i += ps; + } else if (otherpoints[ j ] == null) { + // oops, got a gap + for (m = 0; m < ps; ++m) { + newpoints.push(null); + } - // we got past point below, might need to - // insert interpolated extra point + fromgap = true; + j += otherps; + } else { + // cases where we actually got two points + px = points[ i ]; + py = points[ i + 1 ]; + qx = otherpoints[ j ]; + qy = otherpoints[ j + 1 ]; + bottom = 0; + + if (px === qx) { + for (m = 0; m < ps; ++m) { + newpoints.push(points[ i + m ]); + } - if ( withlines && i > 0 && points[ i - ps ] != null ) { - intery = py + ( points[ i - ps + 1 ] - py ) * ( qx - px ) / ( points[ i - ps ] - px ); - newpoints.push( qx ); - newpoints.push( intery ); - for ( m = 2; m < ps; ++m ) { - newpoints.push( points[ i + m ] ); - } - bottom = qy; - } + //newpoints[ l + 1 ] += qy; + bottom = qy; + + i += ps; + j += otherps; + } else if (px > qx) { + // we got past point below, might need to + // insert interpolated extra point - j += otherps; - - } else { // px < qx - - // if we come from a gap, we just skip this point - - if ( fromgap && withlines ) { - i += ps; - continue; - } + if (withlines && i > 0 && points[ i - ps ] != null) { + intery = py + (points[ i - ps + 1 ] - py) * (qx - px) / (points[ i - ps ] - px); + newpoints.push(qx); + newpoints.push(intery); + for (m = 2; m < ps; ++m) { + newpoints.push(points[ i + m ]); + } + bottom = qy; + } - for ( m = 0; m < ps; ++m ) { - newpoints.push( points[ i + m ] ); - } + j += otherps; + } else { + // px < qx + // if we come from a gap, we just skip this point - // we might be able to interpolate a point below, - // this can give us a better y + if (fromgap && withlines) { + i += ps; + continue; + } - if ( withlines && j > 0 && otherpoints[ j - otherps ] != null ) { - bottom = qy + ( otherpoints[ j - otherps + 1 ] - qy ) * ( px - qx ) / ( otherpoints[ j - otherps ] - qx ); - } + for (m = 0; m < ps; ++m) { + newpoints.push(points[ i + m ]); + } - //newpoints[l + 1] += bottom; + // we might be able to interpolate a point below, + // this can give us a better y - i += ps; - } - - fromgap = false; + if (withlines && j > 0 && otherpoints[ j - otherps ] != null) { + bottom = qy + (otherpoints[ j - otherps + 1 ] - qy) * (px - qx) / (otherpoints[ j - otherps ] - qx); + } - if ( l !== newpoints.length && withbottom ) { - newpoints[ l + 2 ] = bottom; - } - } + //newpoints[l + 1] += bottom; - // maintain the line steps invariant + i += ps; + } + + fromgap = false; - if ( withsteps && l !== newpoints.length && l > 0 && - newpoints[ l ] !== null && - newpoints[ l ] !== newpoints[ l - ps ] && - newpoints[ l + 1 ] !== newpoints[ l - ps + 1 ] ) { - for (m = 0; m < ps; ++m) { - newpoints[ l + ps + m ] = newpoints[ l + m ]; - } - newpoints[ l + 1 ] = newpoints[ l - ps + 1 ]; - } - } + if (l !== newpoints.length && withbottom) { + newpoints[ l + 2 ] = bottom; + } + } + + // maintain the line steps invariant - datapoints.points = newpoints; - } - - plot.hooks.processDatapoints.push( computeFillBottoms ); - } + if (withsteps && l !== newpoints.length && l > 0 && + newpoints[ l ] !== null && + newpoints[ l ] !== newpoints[ l - ps ] && + newpoints[ l + 1 ] !== newpoints[ l - ps + 1 ]) { + for (m = 0; m < ps; ++m) { + newpoints[ l + ps + m ] = newpoints[ l + m ]; + } + newpoints[ l + 1 ] = newpoints[ l - ps + 1 ]; + } + } - $.plot.plugins.push({ - init: init, - options: options, - name: "fillbetween", - version: "1.0" - }); + datapoints.points = newpoints; + } + + plot.hooks.processRawData.push(computeFormat); + plot.hooks.processDatapoints.push(computeFillBottoms); + } + $.plot.plugins.push({ + init: init, + options: options, + name: "fillbetween", + version: "1.0" + }); })(jQuery); diff -r f3af77e371a1 -r 9f02c09d2393 src/pyams_skin/resources/js/ext/flot/jquery.flot.fillbetween.min.js --- a/src/pyams_skin/resources/js/ext/flot/jquery.flot.fillbetween.min.js Thu Sep 26 11:55:17 2019 +0200 +++ b/src/pyams_skin/resources/js/ext/flot/jquery.flot.fillbetween.min.js Wed Oct 16 13:00:43 2019 +0200 @@ -1,1 +1,1 @@ -!function(e){jQuery.plot.plugins.push({init:function(e){function n(e,n){var l;for(l=0;l=n.length?null:n[e.fillBetween]:null}e.hooks.processDatapoints.push(function(e,l,i){if(null!=l.fillBetween){var t=n(l,e.getData());if(t){for(var s,f,o,u,p,r,h,a,g=i.pointsize,w=i.points,B=t.datapoints.pointsize,c=t.datapoints.points,v=[],d=l.lines.show,m=g>2&&i.format[2].y,y=d&&l.lines.steps,b=!0,z=0,D=0;!(z>=w.length);){if(h=v.length,null==w[z]){for(a=0;a=c.length){if(!d)for(a=0;au){if(d&&z>0&&null!=w[z-g]){for(o=f+(w[z-g+1]-f)*(u-s)/(w[z-g]-s),v.push(u),v.push(o),a=2;a0&&null!=c[D-B]&&(r=p+(c[D-B+1]-p)*(s-u)/(c[D-B]-u)),z+=g}b=!1,h!==v.length&&m&&(v[h+2]=r)}if(y&&h!==v.length&&h>0&&null!==v[h]&&v[h]!==v[h-g]&&v[h+1]!==v[h-g+1]){for(a=0;a=n.length?null:n[e.fillBetween]:null}e.hooks.processRawData.push(function(e,n,t,l){null!=n.fillBetween&&(format=l.format,format||(format=[],format.push({x:!0,number:!0,computeRange:"none"!==n.xaxis.options.autoScale,required:!0}),format.push({y:!0,number:!0,computeRange:"none"!==n.yaxis.options.autoScale,required:!0}),void 0!==n.fillBetween&&""!==n.fillBetween&&function(n){var t=e.getData();for(i=0;i2&&l.format[2].y,v=d&&t.lines.steps,x=!0,b=0,D=0;!(b>=c.length);){if(p=B.length,null==c[b]){for(h=0;h=g.length){if(!d)for(h=0;hs){if(d&&b>0&&null!=c[b-m]){for(u=f+(c[b-m+1]-f)*(s-o)/(c[b-m]-o),B.push(s),B.push(u),h=2;h0&&null!=g[D-w]&&(a=r+(g[D-w+1]-r)*(o-s)/(g[D-w]-s)),b+=m}x=!1,p!==B.length&&y&&(B[p+2]=a)}if(v&&p!==B.length&&p>0&&null!==B[p]&&B[p]!==B[p-m]&&B[p+1]!==B[p-m+1]){for(h=0;h 0 + && series[0].xaxis.c2p !== undefined + && series[0].yaxis.c2p !== undefined) { + var eventToTrigger = "plot" + eventType; + var seriesFlag = eventType + "able"; + triggerClickHoverEvent(eventToTrigger, event, + function(i) { + return series[i][seriesFlag] !== false; + }, searchDistance); + } + } + + function onMouseMove(e) { + lastMouseMoveEvent = e; + plot.getPlaceholder()[0].lastMouseMoveEvent = e; + doTriggerClickHoverEvent(e, eventType.hover); + } + + function onMouseLeave(e) { + lastMouseMoveEvent = undefined; + plot.getPlaceholder()[0].lastMouseMoveEvent = undefined; + triggerClickHoverEvent("plothover", e, + function(i) { + return false; + }); + } + + function onClick(e) { + doTriggerClickHoverEvent(e, eventType.click); + } + + function triggerCleanupEvent() { + plot.unhighlight(); + plot.getPlaceholder().trigger('plothovercleanup'); + } + + // trigger click or hover event (they send the same parameters + // so we share their code) + function triggerClickHoverEvent(eventname, event, seriesFilter, searchDistance) { + var options = plot.getOptions(), + offset = plot.offset(), + page = browser.getPageXY(event), + canvasX = page.X - offset.left, + canvasY = page.Y - offset.top, + pos = plot.c2p({ + left: canvasX, + top: canvasY + }), + distance = searchDistance !== undefined ? searchDistance : options.grid.mouseActiveRadius; + + pos.pageX = page.X; + pos.pageY = page.Y; + + var item = plot.findNearbyItem(canvasX, canvasY, seriesFilter, distance); + + if (item) { + // fill in mouse pos for any listeners out there + item.pageX = parseInt(item.series.xaxis.p2c(item.datapoint[0]) + offset.left, 10); + item.pageY = parseInt(item.series.yaxis.p2c(item.datapoint[1]) + offset.top, 10); + } + + if (options.grid.autoHighlight) { + // clear auto-highlights + 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])) || !item) { + unhighlight(h.series, h.point); + } + } + + if (item) { + highlight(item.series, item.datapoint, eventname); + } + } + + plot.getPlaceholder().trigger(eventname, [pos, item]); + } + + function highlight(s, point, auto) { + if (typeof s === "number") { + s = plot.getData()[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 + }); + + plot.triggerRedrawOverlay(); + } else if (!auto) { + highlights[i].auto = false; + } + } + + function unhighlight(s, point) { + if (s == null && point == null) { + highlights = []; + plot.triggerRedrawOverlay(); + return; + } + + if (typeof s === "number") { + s = plot.getData()[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); + + plot.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 processDatapoints() { + triggerCleanupEvent(); + doTriggerClickHoverEvent(lastMouseMoveEvent, eventType.hover); + } + + function setupGrid() { + doTriggerClickHoverEvent(lastMouseMoveEvent, eventType.hover); + } + + function drawOverlay(plot, octx, overlay) { + var plotOffset = plot.getPlotOffset(), + i, hi; + + octx.save(); + octx.translate(plotOffset.left, plotOffset.top); + for (i = 0; i < highlights.length; ++i) { + hi = highlights[i]; + + if (hi.series.bars.show) drawBarHighlight(hi.series, hi.point, octx); + else drawPointHighlight(hi.series, hi.point, octx, plot); + } + octx.restore(); + } + + function drawPointHighlight(series, point, octx, plot) { + 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', 0.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(); + var symbol = series.points.symbol; + if (symbol === 'circle') { + octx.arc(x, y, radius, 0, 2 * Math.PI, false); + } else if (typeof symbol === 'string' && plot.drawSymbol && plot.drawSymbol[symbol]) { + plot.drawSymbol[symbol](octx, x, y, radius, false); + } + + octx.closePath(); + octx.stroke(); + } + + function drawBarHighlight(series, point, octx) { + var highlightColor = (typeof series.highlightColor === "string") ? series.highlightColor : $.color.parse(series.color).scale('a', 0.5).toString(), + fillStyle = highlightColor, + barLeft; + + var barWidth = series.bars.barWidth[0] || series.bars.barWidth; + switch (series.bars.align) { + case "left": + barLeft = 0; + break; + case "right": + barLeft = -barWidth; + break; + default: + barLeft = -barWidth / 2; + } + + octx.lineWidth = series.bars.lineWidth; + octx.strokeStyle = highlightColor; + + var fillTowards = series.bars.fillTowards || 0, + bottom = fillTowards > series.yaxis.min ? Math.min(series.yaxis.max, fillTowards) : series.yaxis.min; + + $.plot.drawSeries.drawBar(point[0], point[1], point[2] || bottom, barLeft, barLeft + barWidth, + function() { + return fillStyle; + }, series.xaxis, series.yaxis, octx, series.bars.horizontal, series.bars.lineWidth); + } + + function initHover(plot, options) { + plot.highlight = highlight; + plot.unhighlight = unhighlight; + if (options.grid.hoverable || options.grid.clickable) { + plot.hooks.drawOverlay.push(drawOverlay); + plot.hooks.processDatapoints.push(processDatapoints); + plot.hooks.setupGrid.push(setupGrid); + } + + lastMouseMoveEvent = plot.getPlaceholder()[0].lastMouseMoveEvent; + } + + plot.hooks.bindEvents.push(bindEvents); + plot.hooks.shutdown.push(shutdown); + plot.hooks.processOptions.push(initHover); + } + + $.plot.plugins.push({ + init: init, + options: options, + name: 'hover', + version: '0.1' + }); +})(jQuery); diff -r f3af77e371a1 -r 9f02c09d2393 src/pyams_skin/resources/js/ext/flot/jquery.flot.hover.min.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/pyams_skin/resources/js/ext/flot/jquery.flot.hover.min.js Wed Oct 16 13:00:43 2019 +0200 @@ -0,0 +1,1 @@ +!function(e){"use strict";var t=e.plot.browser,i={click:"click",hover:"hover"};e.plot.plugins.push({init:function(o){function r(e){var t=o.getOptions(),r=new CustomEvent("mouseevent");return r.pageX=e.detail.changedTouches[0].pageX,r.pageY=e.detail.changedTouches[0].pageY,r.clientX=e.detail.changedTouches[0].clientX,r.clientY=e.detail.changedTouches[0].clientY,t.grid.hoverable&&a(r,i.hover,30),!1}function a(e,t,i){var r=o.getData();if(void 0!==e&&r.length>0&&void 0!==r[0].xaxis.c2p&&void 0!==r[0].yaxis.c2p){var a=t+"able";h("plot"+t,e,function(e){return!1!==r[e][a]},i)}}function n(e){y=e,o.getPlaceholder()[0].lastMouseMoveEvent=e,a(e,i.hover)}function s(e){y=void 0,o.getPlaceholder()[0].lastMouseMoveEvent=void 0,h("plothover",e,function(e){return!1})}function l(e){a(e,i.click)}function c(){o.unhighlight(),o.getPlaceholder().trigger("plothovercleanup")}function h(e,i,r,a){var n=o.getOptions(),s=o.offset(),l=t.getPageXY(i),c=l.X-s.left,h=l.Y-s.top,g=o.c2p({left:c,top:h}),d=void 0!==a?a:n.grid.mouseActiveRadius;g.pageX=l.X,g.pageY=l.Y;var v=o.findNearbyItem(c,h,r,d);if(v&&(v.pageX=parseInt(v.series.xaxis.p2c(v.datapoint[0])+s.left,10),v.pageY=parseInt(v.series.yaxis.p2c(v.datapoint[1])+s.top,10)),n.grid.autoHighlight){for(var f=0;fs.max||nl.max)){var h=t.points.radius+t.points.lineWidth/2;o.lineWidth=h,o.strokeStyle=c;var p=1.5*h;a=s.p2c(a),n=l.p2c(n),o.beginPath();var u=t.points.symbol;"circle"===u?o.arc(a,n,p,0,2*Math.PI,!1):"string"==typeof u&&r.drawSymbol&&r.drawSymbol[u]&&r.drawSymbol[u](o,a,n,p,!1),o.closePath(),o.stroke()}}function m(t,i,o){var r,a="string"==typeof t.highlightColor?t.highlightColor:e.color.parse(t.color).scale("a",.5).toString(),n=a,s=t.bars.barWidth[0]||t.bars.barWidth;switch(t.bars.align){case"left":r=0;break;case"right":r=-s;break;default:r=-s/2}o.lineWidth=t.bars.lineWidth,o.strokeStyle=a;var l=t.bars.fillTowards||0,c=l>t.yaxis.min?Math.min(t.yaxis.max,l):t.yaxis.min;e.plot.drawSeries.drawBar(i[0],i[1],i[2]||c,r,r+s,function(){return n},t.xaxis,t.yaxis,o,t.bars.horizontal,t.bars.lineWidth)}var y,k=[];o.hooks.bindEvents.push(function(e,t){var i=e.getOptions();(i.grid.hoverable||i.grid.clickable)&&(t[0].addEventListener("touchevent",c,!1),t[0].addEventListener("tap",r,!1)),i.grid.clickable&&t.bind("click",l),i.grid.hoverable&&(t.bind("mousemove",n),t.bind("mouseleave",s))}),o.hooks.shutdown.push(function(e,t){t[0].removeEventListener("tap",r),t[0].removeEventListener("touchevent",c),t.unbind("mousemove",n),t.unbind("mouseleave",s),t.unbind("click",l),k=[]}),o.hooks.processOptions.push(function(e,t){e.highlight=p,e.unhighlight=u,(t.grid.hoverable||t.grid.clickable)&&(e.hooks.drawOverlay.push(f),e.hooks.processDatapoints.push(d),e.hooks.setupGrid.push(v)),y=e.getPlaceholder()[0].lastMouseMoveEvent})},options:{grid:{hoverable:!1,clickable:!1}},name:"hover",version:"0.1"})}(jQuery); diff -r f3af77e371a1 -r 9f02c09d2393 src/pyams_skin/resources/js/ext/flot/jquery.flot.image.js --- a/src/pyams_skin/resources/js/ext/flot/jquery.flot.image.js Thu Sep 26 11:55:17 2019 +0200 +++ b/src/pyams_skin/resources/js/ext/flot/jquery.flot.image.js Wed Oct 16 13:00:43 2019 +0200 @@ -11,7 +11,7 @@ There are two helpers included for retrieving images. The easiest work the way that you put in URLs instead of images in the data, like this: - [ "myimage.png", 0, 0, 10, 10 ] + [ "myimage.png", 0, 0, 10, 10 ] Then call $.plot.image.loadData( data, options, callback ) where data and options are the same as you pass in to $.plot. This loads the images, replaces @@ -25,20 +25,20 @@ The plugin supports these options: - series: { - images: { - show: boolean - anchor: "corner" or "center" - alpha: [ 0, 1 ] - } - } + series: { + images: { + show: boolean + anchor: "corner" or "center" + alpha: [ 0, 1 ] + } + } They can be specified for a specific series: - $.plot( $("#placeholder"), [{ - data: [ ... ], - images: { ... } - ]) + $.plot( $("#placeholder"), [{ + data: [ ... ], + images: { ... } + ]) Note that because the data format is different from usual data points, you can't use images with anything else in a specific data series. @@ -69,16 +69,18 @@ var urls = [], points = []; var defaultShow = options.series.images.show; - + $.each(series, function (i, s) { - if (!(defaultShow || s.images.show)) + if (!(defaultShow || s.images.show)) { return; - - if (s.data) + } + + if (s.data) { s = s.data; + } $.each(s, function (i, p) { - if (typeof p[0] == "string") { + if (typeof p[0] === "string") { urls.push(p[0]); points.push(p); } @@ -88,42 +90,45 @@ $.plot.image.load(urls, function (loadedImages) { $.each(points, function (i, p) { var url = p[0]; - if (loadedImages[url]) + if (loadedImages[url]) { p[0] = loadedImages[url]; + } }); callback(); }); } - + $.plot.image.load = function (urls, callback) { var missing = urls.length, loaded = {}; - if (missing == 0) + if (missing === 0) { callback({}); + } $.each(urls, function (i, url) { var handler = function () { --missing; - loaded[url] = this; - - if (missing == 0) + + if (missing === 0) { callback(loaded); + } }; $('').load(handler).error(handler).attr('src', url); }); }; - + function drawSeries(plot, ctx, series) { var plotOffset = plot.getPlotOffset(); - - if (!series.images || !series.images.show) + + if (!series.images || !series.images.show) { return; - + } + var points = series.datapoints.points, ps = series.datapoints.pointsize; - + for (var i = 0; i < points.length; i += ps) { var img = points[i], x1 = points[i + 1], y1 = points[i + 2], @@ -134,8 +139,9 @@ // actually we should check img.complete, but it // appears to be a somewhat unreliable indicator in // IE6 (false even after load event) - if (!img || img.width <= 0 || img.height <= 0) + if (!img || img.width <= 0 || img.height <= 0) { continue; + } if (x1 > x2) { tmp = x2; @@ -147,23 +153,24 @@ y2 = y1; y1 = tmp; } - - // if the anchor is at the center of the pixel, expand the + + // if the anchor is at the center of the pixel, expand the // image by 1/2 pixel in each direction - if (series.images.anchor == "center") { - tmp = 0.5 * (x2-x1) / (img.width - 1); + if (series.images.anchor === "center") { + tmp = 0.5 * (x2 - x1) / (img.width - 1); x1 -= tmp; x2 += tmp; - tmp = 0.5 * (y2-y1) / (img.height - 1); + tmp = 0.5 * (y2 - y1) / (img.height - 1); y1 -= tmp; y2 += tmp; } - + // clip - if (x1 == x2 || y1 == y2 || + if (x1 === x2 || y1 === y2 || x1 >= xaxis.max || x2 <= xaxis.min || - y1 >= yaxis.max || y2 <= yaxis.min) + y1 >= yaxis.max || y2 <= yaxis.min) { continue; + } var sx1 = 0, sy1 = 0, sx2 = img.width, sy2 = img.height; if (x1 < xaxis.min) { @@ -185,12 +192,12 @@ sy1 += (sy1 - sy2) * (yaxis.max - y2) / (y2 - y1); y2 = yaxis.max; } - + x1 = xaxis.p2c(x1); x2 = xaxis.p2c(x2); y1 = yaxis.p2c(y1); y2 = yaxis.p2c(y2); - + // the transformation may have swapped us if (x1 > x2) { tmp = x2; @@ -214,8 +221,9 @@ } function processRawData(plot, series, data, datapoints) { - if (!series.images.show) + if (!series.images.show) { return; + } // format is Image, x1, y1, x2, y2 (opposite corners) datapoints.format = [ @@ -226,12 +234,12 @@ { y: true, number: true, required: true } ]; } - + function init(plot) { plot.hooks.processRawData.push(processRawData); plot.hooks.drawSeries.push(drawSeries); } - + $.plot.plugins.push({ init: init, options: options, diff -r f3af77e371a1 -r 9f02c09d2393 src/pyams_skin/resources/js/ext/flot/jquery.flot.image.min.js --- a/src/pyams_skin/resources/js/ext/flot/jquery.flot.image.min.js Thu Sep 26 11:55:17 2019 +0200 +++ b/src/pyams_skin/resources/js/ext/flot/jquery.flot.image.min.js Wed Oct 16 13:00:43 2019 +0200 @@ -1,7 +1,1 @@ -/* Javascript plotting library for jQuery, version 0.8.3. - -Copyright (c) 2007-2014 IOLA and Ole Laursen. -Licensed under the MIT license. - -*/ -(function($){var options={series:{images:{show:false,alpha:1,anchor:"corner"}}};$.plot.image={};$.plot.image.loadDataImages=function(series,options,callback){var urls=[],points=[];var defaultShow=options.series.images.show;$.each(series,function(i,s){if(!(defaultShow||s.images.show))return;if(s.data)s=s.data;$.each(s,function(i,p){if(typeof p[0]=="string"){urls.push(p[0]);points.push(p)}})});$.plot.image.load(urls,function(loadedImages){$.each(points,function(i,p){var url=p[0];if(loadedImages[url])p[0]=loadedImages[url]});callback()})};$.plot.image.load=function(urls,callback){var missing=urls.length,loaded={};if(missing==0)callback({});$.each(urls,function(i,url){var handler=function(){--missing;loaded[url]=this;if(missing==0)callback(loaded)};$("").load(handler).error(handler).attr("src",url)})};function drawSeries(plot,ctx,series){var plotOffset=plot.getPlotOffset();if(!series.images||!series.images.show)return;var points=series.datapoints.points,ps=series.datapoints.pointsize;for(var i=0;ix2){tmp=x2;x2=x1;x1=tmp}if(y1>y2){tmp=y2;y2=y1;y1=tmp}if(series.images.anchor=="center"){tmp=.5*(x2-x1)/(img.width-1);x1-=tmp;x2+=tmp;tmp=.5*(y2-y1)/(img.height-1);y1-=tmp;y2+=tmp}if(x1==x2||y1==y2||x1>=xaxis.max||x2<=xaxis.min||y1>=yaxis.max||y2<=yaxis.min)continue;var sx1=0,sy1=0,sx2=img.width,sy2=img.height;if(x1xaxis.max){sx2+=(sx2-sx1)*(xaxis.max-x2)/(x2-x1);x2=xaxis.max}if(y1yaxis.max){sy1+=(sy1-sy2)*(yaxis.max-y2)/(y2-y1);y2=yaxis.max}x1=xaxis.p2c(x1);x2=xaxis.p2c(x2);y1=yaxis.p2c(y1);y2=yaxis.p2c(y2);if(x1>x2){tmp=x2;x2=x1;x1=tmp}if(y1>y2){tmp=y2;y2=y1;y1=tmp}tmp=ctx.globalAlpha;ctx.globalAlpha*=series.images.alpha;ctx.drawImage(img,sx1,sy1,sx2-sx1,sy2-sy1,x1+plotOffset.left,y1+plotOffset.top,x2-x1,y2-y1);ctx.globalAlpha=tmp}}function processRawData(plot,series,data,datapoints){if(!series.images.show)return;datapoints.format=[{required:true},{x:true,number:true,required:true},{y:true,number:true,required:true},{x:true,number:true,required:true},{y:true,number:true,required:true}]}function init(plot){plot.hooks.processRawData.push(processRawData);plot.hooks.drawSeries.push(drawSeries)}$.plot.plugins.push({init:init,options:options,name:"image",version:"1.1"})})(jQuery); \ No newline at end of file +!function(a){function i(a,i,e){var n=a.getPlotOffset();if(e.images&&e.images.show)for(var o=e.datapoints.points,t=e.datapoints.pointsize,r=0;ru&&(s=u,u=h,h=s),p>g&&(s=g,g=p,p=s),"center"===e.images.anchor&&(h-=s=.5*(u-h)/(m.width-1),u+=s,p-=s=.5*(g-p)/(m.height-1),g+=s),!(h===u||p===g||h>=c.max||u<=c.min||p>=l.max||g<=l.min))){var f=0,d=0,x=m.width,w=m.height;hc.max&&(x+=(x-f)*(c.max-u)/(u-h),u=c.max),pl.max&&(d+=(d-w)*(l.max-g)/(g-p),g=l.max),h=c.p2c(h),u=c.p2c(u),p=l.p2c(p),g=l.p2c(g),h>u&&(s=u,u=h,h=s),p>g&&(s=g,g=p,p=s),s=i.globalAlpha,i.globalAlpha*=e.images.alpha,i.drawImage(m,f,d,x-f,w-d,h+n.left,p+n.top,u-h,g-p),i.globalAlpha=s}}}function e(a,i,e,n){i.images.show&&(n.format=[{required:!0},{x:!0,number:!0,required:!0},{y:!0,number:!0,required:!0},{x:!0,number:!0,required:!0},{y:!0,number:!0,required:!0}])}a.plot.image={},a.plot.image.loadDataImages=function(i,e,n){var o=[],t=[],r=e.series.images.show;a.each(i,function(i,e){(r||e.images.show)&&(e.data&&(e=e.data),a.each(e,function(a,i){"string"==typeof i[0]&&(o.push(i[0]),t.push(i))}))}),a.plot.image.load(o,function(i){a.each(t,function(a,e){var n=e[0];i[n]&&(e[0]=i[n])}),n()})},a.plot.image.load=function(i,e){var n=i.length,o={};0===n&&e({}),a.each(i,function(i,t){var r=function(){--n,o[t]=this,0===n&&e(o)};a("").load(r).error(r).attr("src",t)})},a.plot.plugins.push({init:function(a){a.hooks.processRawData.push(e),a.hooks.drawSeries.push(i)},options:{series:{images:{show:!1,alpha:1,anchor:"corner"}}},name:"image",version:"1.1"})}(jQuery); diff -r f3af77e371a1 -r 9f02c09d2393 src/pyams_skin/resources/js/ext/flot/jquery.flot.js --- a/src/pyams_skin/resources/js/ext/flot/jquery.flot.js Thu Sep 26 11:55:17 2019 +0200 +++ b/src/pyams_skin/resources/js/ext/flot/jquery.flot.js Wed Oct 16 13:00:43 2019 +0200 @@ -1,510 +1,98 @@ -/* Javascript plotting library for jQuery, version 0.8.3. +/* Javascript plotting library for jQuery, version 3.0.0. Copyright (c) 2007-2014 IOLA and Ole Laursen. Licensed under the MIT license. */ -// first an inline dependency, jquery.colorhelpers.js, we inline it here -// for convenience - -/* Plugin for jQuery for working with colors. - * - * Version 1.1. - * - * Inspiration from jQuery color animation plugin by John Resig. - * - * Released under the MIT license by Ole Laursen, October 2009. - * - * Examples: - * - * $.color.parse("#fff").scale('rgb', 0.25).add('a', -0.5).toString() - * var c = $.color.extract($("#mydiv"), 'background-color'); - * console.log(c.r, c.g, c.b, c.a); - * $.color.make(100, 50, 25, 0.4).toString() // returns "rgba(100,50,25,0.4)" - * - * Note that .scale() and .add() return the same modified object - * instead of making a new one. - * - * V. 1.1: Fix error handling so e.g. parsing an empty string does - * produce a color rather than just crashing. - */ -(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=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 valuemax?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); - // the actual Flot code (function($) { - - // Cache the prototype hasOwnProperty for faster access - - var hasOwnProperty = Object.prototype.hasOwnProperty; - - // A shim to provide 'detach' to jQuery versions prior to 1.4. Using a DOM - // operation produces the same effect as detach, i.e. removing the element - // without touching its jQuery data. - - // Do not merge this into Flot 0.9, since it requires jQuery 1.4.4+. - - if (!$.fn.detach) { - $.fn.detach = function() { - return this.each(function() { - if (this.parentNode) { - this.parentNode.removeChild( this ); - } - }); - }; + "use strict"; + + var Canvas = window.Flot.Canvas; + + function defaultTickGenerator(axis) { + var ticks = [], + start = $.plot.saturated.saturate($.plot.saturated.floorInBase(axis.min, axis.tickSize)), + i = 0, + v = Number.NaN, + prev; + + if (start === -Number.MAX_VALUE) { + ticks.push(start); + start = $.plot.saturated.floorInBase(axis.min + axis.tickSize, axis.tickSize); + } + + do { + prev = v; + //v = start + i * axis.tickSize; + v = $.plot.saturated.multiplyAdd(axis.tickSize, i, start); + ticks.push(v); + ++i; + } while (v < axis.max && v !== prev); + + return ticks; } - /////////////////////////////////////////////////////////////////////////// - // The Canvas object is a wrapper around an HTML5 tag. - // - // @constructor - // @param {string} cls List of classes to apply to the canvas. - // @param {element} container Element onto which to append the canvas. - // - // Requiring a container is a little iffy, but unfortunately canvas - // operations don't work unless the canvas is attached to the DOM. - - 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 HTML5 Canvas isn't available, fall back to [Ex|Flash]canvas - - 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"); - - // Determine the screen's ratio of physical to device-independent - // pixels. This is the ratio between the canvas width that the browser - // advertises and the number of pixels actually present in that space. - - // The iPhone 4, for example, has a device-independent width of 320px, - // but its screen is actually 640px wide. It therefore has a pixel - // ratio of 2, while most normal devices have a ratio of 1. - - var devicePixelRatio = window.devicePixelRatio || 1, - backingStoreRatio = - context.webkitBackingStorePixelRatio || - context.mozBackingStorePixelRatio || - context.msBackingStorePixelRatio || - context.oBackingStorePixelRatio || - context.backingStorePixelRatio || 1; - - this.pixelRatio = devicePixelRatio / backingStoreRatio; - - // Size the canvas to match the internal dimensions of its container - - this.resize(container.width(), container.height()); - - // Collection of HTML div layers for text overlaid onto the canvas - - this.textContainer = null; - this.text = {}; - - // Cache of text fragments and metrics, so we can avoid expensively - // re-calculating them when the plot is re-rendered in a loop. - - this._textCache = {}; - } - - // Resizes the canvas to the given dimensions. - // - // @param {number} width New width of the canvas, in pixels. - // @param {number} width New height of the canvas, in pixels. - - 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; - - // Resize the canvas, increasing its density based on the display's - // pixel ratio; basically giving it more pixels without increasing the - // size of its element, to take advantage of the fact that retina - // displays have that many more pixels in the same advertised space. - - // Resizing should reset the state (excanvas seems to be buggy though) - - 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; - } - - // Save the context, so we can reset in case we get replotted. The - // restore ensure that we're really back at the initial state, and - // should be safe even if we haven't saved the initial state yet. - - context.restore(); - context.save(); - - // Scale the coordinate space to match the display density; so even though we - // may have twice as many pixels, we still want lines and other drawing to - // appear at the same size; the extra pixels will just make them crisper. - - context.scale(pixelRatio, pixelRatio); - }; - - // Clears the entire canvas area, not including any overlaid HTML text - - Canvas.prototype.clear = function() { - this.context.clearRect(0, 0, this.width, this.height); - }; - - // Finishes rendering the canvas, including managing the text overlay. - - Canvas.prototype.render = function() { - - var cache = this._textCache; - - // For each text layer, add elements marked as active that haven't - // already been rendered, and remove those that are no longer active. - - 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(); - } - } - }; - - // Creates (if necessary) and returns the text overlay container. - // - // @param {string} classes String of space-separated CSS classes used to - // uniquely identify the text layer. - // @return {object} The jQuery-wrapped text-layer div. - - Canvas.prototype.getTextLayer = function(classes) { - - var layer = this.text[classes]; - - // Create the text layer if it doesn't exist - - if (layer == null) { - - // Create the text layer container, if it doesn't exist - - if (this.textContainer == null) { - this.textContainer = $("
") - .css({ - position: "absolute", - top: 0, - left: 0, - bottom: 0, - right: 0, - 'font-size': "smaller", - color: "#545454" - }) - .insertAfter(this.element); - } - - layer = this.text[classes] = $("
") - .addClass(classes) - .css({ - position: "absolute", - top: 0, - left: 0, - bottom: 0, - right: 0 - }) - .appendTo(this.textContainer); - } - - return layer; - }; - - // Creates (if necessary) and returns a text info object. - // - // The object looks like this: - // - // { - // width: Width of the text's wrapper div. - // height: Height of the text's wrapper div. - // element: The jQuery-wrapped HTML div containing the text. - // positions: Array of positions at which this text is drawn. - // } - // - // The positions array contains objects that look like this: - // - // { - // active: Flag indicating whether the text should be visible. - // rendered: Flag indicating whether the text is currently visible. - // element: The jQuery-wrapped HTML div containing the text. - // x: X coordinate at which to draw the text. - // y: Y coordinate at which to draw the text. - // } - // - // Each position after the first receives a clone of the original element. - // - // The idea is that that the width, height, and general 'identity' of the - // text is constant no matter where it is placed; the placements are a - // secondary property. - // - // Canvas maintains a cache of recently-used text info objects; getTextInfo - // either returns the cached element or creates a new entry. - // - // @param {string} layer A string of space-separated CSS classes uniquely - // identifying the layer containing this text. - // @param {string} text Text string to retrieve info for. - // @param {(string|object)=} font Either a string of space-separated CSS - // classes or a font-spec object, defining the text's font and style. - // @param {number=} angle Angle at which to rotate the text, in degrees. - // Angle is currently unused, it will be implemented in the future. - // @param {number=} width Maximum width of the text before it wraps. - // @return {object} a text info object. - - Canvas.prototype.getTextInfo = function(layer, text, font, angle, width) { - - var textStyle, layerCache, styleCache, info; - - // Cast the value to a string, in case we were given a number or such - - text = "" + text; - - // If the font is a font-spec object, generate a CSS font definition - - if (typeof font === "object") { - textStyle = font.style + " " + font.variant + " " + font.weight + " " + font.size + "px/" + font.lineHeight + "px " + font.family; - } else { - textStyle = font; - } - - // Retrieve (or create) the cache for the text's layer and styles - - layerCache = this._textCache[layer]; - - if (layerCache == null) { - layerCache = this._textCache[layer] = {}; - } - - styleCache = layerCache[textStyle]; - - if (styleCache == null) { - styleCache = layerCache[textStyle] = {}; - } - - info = styleCache[text]; - - // If we can't find a matching element in our cache, create a new one - - if (info == null) { - - var element = $("
").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; - }; - - // Adds a text string to the canvas text overlay. - // - // The text isn't drawn immediately; it is marked as rendering, which will - // result in its addition to the canvas on the next render pass. - // - // @param {string} layer A string of space-separated CSS classes uniquely - // identifying the layer containing this text. - // @param {number} x X coordinate at which to draw the text. - // @param {number} y Y coordinate at which to draw the text. - // @param {string} text Text string to draw. - // @param {(string|object)=} font Either a string of space-separated CSS - // classes or a font-spec object, defining the text's font and style. - // @param {number=} angle Angle at which to rotate the text, in degrees. - // Angle is currently unused, it will be implemented in the future. - // @param {number=} width Maximum width of the text before it wraps. - // @param {string=} halign Horizontal alignment of the text; either "left", - // "center" or "right". - // @param {string=} valign Vertical alignment of the text; either "top", - // "middle" or "bottom". - - 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; - - // Tweak the div's position to match the text's alignment - - 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; - } - - // Determine whether this text already exists at this position. - // If so, mark it for inclusion in the next render pass. - - for (var i = 0, position; position = positions[i]; i++) { - if (position.x == x && position.y == y) { - position.active = true; - return; - } - } - - // If the text doesn't exist at this position, create a new entry - - // For the very first position we'll re-use the original element, - // while for subsequent ones we'll clone it. - - position = { - active: true, - rendered: false, - element: positions.length ? info.element.clone() : info.element, - x: x, - y: y - }; - - positions.push(position); - - // Move the element to its final position within the container - - position.element.css({ - top: Math.round(y), - left: Math.round(x), - 'text-align': halign // In case the text wraps - }); - }; - - // Removes one or more text strings from the canvas text overlay. - // - // If no parameters are given, all text within the layer is removed. - // - // Note that the text is not immediately removed; it is simply marked as - // inactive, which will result in its removal on the next render pass. - // This avoids the performance penalty for 'clear and redraw' behavior, - // where we potentially get rid of all text on a layer, but will likely - // add back most or all of it later, as when redrawing axes, for example. - // - // @param {string} layer A string of space-separated CSS classes uniquely - // identifying the layer containing this text. - // @param {number=} x X coordinate of the text. - // @param {number=} y Y coordinate of the text. - // @param {string=} text Text string to remove. - // @param {(string|object)=} font Either a string of space-separated CSS - // classes or a font-spec object, defining the text's font and style. - // @param {number=} angle Angle at which the text is rotated, in degrees. - // Angle is currently unused, it will be implemented in the future. - - 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; - } - } - } - }; - - /////////////////////////////////////////////////////////////////////////// - // The top-level container for the entire plot. - + function defaultTickFormatter(value, axis, precision) { + var oldTickDecimals = axis.tickDecimals, + expPosition = ("" + value).indexOf("e"); + + if (expPosition !== -1) { + return expRepTickFormatter(value, axis, precision); + } + + if (precision > 0) { + axis.tickDecimals = precision; + } + + var factor = axis.tickDecimals ? parseFloat('1e' + axis.tickDecimals) : 1, + formatted = "" + Math.round(value * factor) / factor; + + // If tickDecimals was specified, ensure that we have exactly that + // much precision; otherwise default to the value's own precision. + if (axis.tickDecimals != null) { + var decimal = formatted.indexOf("."), + decimalPrecision = decimal === -1 ? 0 : formatted.length - decimal - 1; + if (decimalPrecision < axis.tickDecimals) { + var decimals = ("" + factor).substr(1, axis.tickDecimals - decimalPrecision); + formatted = (decimalPrecision ? formatted : formatted + ".") + decimals; + } + } + + axis.tickDecimals = oldTickDecimals; + return formatted; + }; + + function expRepTickFormatter(value, axis, precision) { + var expPosition = ("" + value).indexOf("e"), + exponentValue = parseInt(("" + value).substr(expPosition + 1)), + tenExponent = expPosition !== -1 ? exponentValue : (value > 0 ? Math.floor(Math.log(value) / Math.LN10) : 0), + roundWith = parseFloat('1e' + tenExponent), + x = value / roundWith; + + if (precision) { + var updatedPrecision = recomputePrecision(value, precision); + return (value / roundWith).toFixed(updatedPrecision) + 'e' + tenExponent; + } + + if (axis.tickDecimals > 0) { + return x.toFixed(recomputePrecision(value, axis.tickDecimals)) + 'e' + tenExponent; + } + return x.toFixed() + 'e' + tenExponent; + } + + function recomputePrecision(num, precision) { + //for numbers close to zero, the precision from flot will be a big number + //while for big numbers, the precision will be negative + var log10Value = Math.log(Math.abs(num)) * Math.LOG10E, + newPrecision = Math.abs(log10Value + precision); + + return newPrecision <= 20 ? Math.floor(newPrecision) : 20; + } + + /////////////////////////////////////////////////////////////////////////// + // The top-level container for the entire plot. function Plot(placeholder, data_, options_, plugins) { // data is on the form: // [ series1, series2 ... ] @@ -515,18 +103,6 @@ options = { // the color theme used for graphs colors: ["#edc240", "#afd8f8", "#cb4b4b", "#4da74d", "#9440ed"], - legend: { - show: true, - noColumns: 1, // number of colums in legend table - labelFormatter: null, // fn: string -> string - labelBoxBorderColor: "#ccc", // border color for the little label boxes - container: null, // container (as jQuery object) to put legend in, null means default on top of graph - position: "ne", // position of default legend container within plot - margin: 5, // distance from grid edge to default legend container within plot - backgroundColor: null, // null means auto-detect - backgroundOpacity: 0.85, // set to 0 to avoid background - sorted: null // default to no legend sorting - }, xaxis: { show: null, // null = auto-detect, true = always, false = never position: "bottom", // or "top" @@ -538,21 +114,35 @@ inverseTransform: null, // if transform is set, this should be the inverse function min: null, // min. value to show, null means set automatically max: null, // max. value to show, null means set automatically - autoscaleMargin: null, // margin in % to add if auto-setting min/max + autoScaleMargin: null, // margin in % to add if autoScale option is on "loose" mode, + autoScale: "exact", // Available modes: "none", "loose", "exact", "sliding-window" + windowSize: null, // null or number. This is the size of sliding-window. + growOnly: null, // grow only, useful for smoother auto-scale, the scales will grow to accomodate data but won't shrink back. ticks: null, // either [1, 3] or [[1, "a"], 3] or (fn: axis info -> ticks) or app. number of ticks for auto-ticks tickFormatter: null, // fn: number -> string + showTickLabels: "major", // "none", "endpoints", "major", "all" labelWidth: null, // size of tick labels in pixels labelHeight: null, reserveSpace: null, // whether to reserve space even if axis isn't shown - tickLength: null, // size in pixels of ticks, or "full" for whole line + tickLength: null, // size in pixels of major tick marks + showMinorTicks: null, // true = show minor tick marks, false = hide minor tick marks + showTicks: null, // true = show tick marks, false = hide all tick marks + gridLines: null, // true = show grid lines, false = hide grid lines alignTicksWithAxis: null, // axis number or null for no sync tickDecimals: null, // no. of decimals, null means auto tickSize: null, // number or [number, "unit"] - minTickSize: null // number or [number, "unit"] + minTickSize: null, // number or [number, "unit"] + offset: { below: 0, above: 0 }, // the plot drawing offset. this is calculated by the flot.navigate for each axis + boxPosition: { centerX: 0, centerY: 0 } //position of the axis on the corresponding axis box }, yaxis: { - autoscaleMargin: 0.02, - position: "left" // or "right" + autoScaleMargin: 0.02, // margin in % to add if autoScale option is on "loose" mode + autoScale: "loose", // Available modes: "none", "loose", "exact" + growOnly: null, // grow only, useful for smoother auto-scale, the scales will grow to accomodate data but won't shrink back. + position: "left", // or "right" + showTickLabels: "major", // "none", "endpoints", "major", "all" + offset: { below: 0, above: 0 }, // the plot drawing offset. this is calculated by the flot.navigate for each axis + boxPosition: { centerX: 0, centerY: 0 } //position of the axis on the corresponding axis box }, xaxes: [], yaxes: [], @@ -563,12 +153,12 @@ lineWidth: 2, // in pixels fill: true, fillColor: "#ffffff", - symbol: "circle" // or callback + symbol: 'circle' // or callback }, lines: { // we don't put in show: false so we can see // whether lines were actively disabled - lineWidth: 2, // in pixels + lineWidth: 1, // in pixels fill: false, fillColor: null, steps: false @@ -578,11 +168,14 @@ bars: { show: false, lineWidth: 2, // in pixels - barWidth: 1, // in units of the x axis + // barWidth: number or [number, absolute] + // when 'absolute' is false, 'number' is relative to the minimum distance between points for the series + // when 'absolute' is true, 'number' is considered to be in units of the x-axis + horizontal: false, + barWidth: 0.8, fill: true, fillColor: null, align: "left", // "left", "right", or "center" - horizontal: false, zero: true }, shadowSize: 3, @@ -598,7 +191,7 @@ margin: 0, // distance from the canvas edge to the grid labelMargin: 5, // in pixels axisMargin: 8, // in pixels - borderWidth: 2, // in pixels + borderWidth: 1, // in pixels minBorderMargin: null, // in pixels, null means taken from points radius markings: null, // array of ranges or fn: axes -> array of ranges markingsColor: "#f4f4f4", @@ -607,65 +200,108 @@ clickable: false, hoverable: false, autoHighlight: true, // highlight in case mouse is near - mouseActiveRadius: 10 // how far the mouse can be away to activate an item + mouseActiveRadius: 15 // how far the mouse can be away to activate an item }, interaction: { - redrawOverlayInterval: 1000/60 // time between updates, -1 means in same flow + redrawOverlayInterval: 1000 / 60 // time between updates, -1 means in same flow }, hooks: {} }, - surface = null, // the canvas for the plot itself - overlay = null, // canvas for interactive stuff on top of plot - eventHolder = null, // jQuery object that events should be bound to - 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; + surface = null, // the canvas for the plot itself + overlay = null, // canvas for interactive stuff on top of plot + eventHolder = null, // jQuery object that events should be bound to + ctx = null, + octx = null, + xaxes = [], + yaxes = [], + plotOffset = { + left: 0, + right: 0, + top: 0, + bottom: 0 + }, + plotWidth = 0, + plotHeight = 0, + hooks = { + processOptions: [], + processRawData: [], + processDatapoints: [], + processOffset: [], + setupGrid: [], + adjustSeriesDataRange: [], + setRange: [], + drawBackground: [], + drawSeries: [], + drawAxis: [], + draw: [], + axisReserveSpace: [], + bindEvents: [], + drawOverlay: [], + resize: [], + shutdown: [] + }, + plot = this; + + var eventManager = {}; + + // interactive features + + var redrawTimeout = null; // public functions 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 () { + plot.getPlaceholder = function() { + return placeholder; + }; + plot.getCanvas = function() { + return surface.element; + }; + plot.getSurface = function() { + return surface; + }; + plot.getEventHolder = function() { + return eventHolder[0]; + }; + 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; + plot.getData = function() { + return series; + }; + plot.getAxes = function() { + var res = {}; + $.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.getXAxes = function() { + return xaxes; + }; + plot.getYAxes = function() { + return yaxes; + }; + plot.c2p = canvasToCartesianAxisCoords; + plot.p2c = cartesianAxisToCanvasCoords; + plot.getOptions = function() { + return options; + }; plot.triggerRedrawOverlay = triggerRedrawOverlay; plot.pointOffset = function(point) { return { @@ -674,7 +310,7 @@ }; }; plot.shutdown = shutdown; - plot.destroy = function () { + plot.destroy = function() { shutdown(); placeholder.removeData("plot").empty(); @@ -688,37 +324,54 @@ xaxes = []; yaxes = []; hooks = null; - highlights = []; plot = null; }; - plot.resize = function () { - var width = placeholder.width(), - height = placeholder.height(); + + plot.resize = function() { + var width = placeholder.width(), + height = placeholder.height(); surface.resize(width, height); overlay.resize(width, height); + + executeHooks(hooks.resize, [width, height]); }; + plot.clearTextCache = function () { + surface.clearCache(); + overlay.clearCache(); + }; + + plot.autoScaleAxis = autoScaleAxis; + plot.computeRangeForDataSeries = computeRangeForDataSeries; + plot.adjustSeriesDataRange = adjustSeriesDataRange; + plot.findNearbyItem = findNearbyItem; + plot.findNearbyInterpolationPoint = findNearbyInterpolationPoint; + plot.computeValuePrecision = computeValuePrecision; + plot.computeTickSize = computeTickSize; + plot.addEventHandler = addEventHandler; + // public attributes plot.hooks = hooks; // initialize + var MINOR_TICKS_COUNT_CONSTANT = $.plot.uiConstants.MINOR_TICKS_COUNT_CONSTANT; + var TICK_LENGTH_CONSTANT = $.plot.uiConstants.TICK_LENGTH_CONSTANT; initPlugins(plot); - parseOptions(options_); setupCanvases(); + parseOptions(options_); setData(data_); - setupGrid(); + setupGrid(true); draw(); bindEvents(); - function executeHooks(hook, args) { args = [plot].concat(args); - for (var i = 0; i < hook.length; ++i) + for (var i = 0; i < hook.length; ++i) { hook[i].apply(this, args); + } } function initPlugins() { - // References to key classes, allowing plugins to modify them var classes = { @@ -728,13 +381,13 @@ for (var i = 0; i < plugins.length; ++i) { var p = plugins[i]; p.init(plot, classes); - if (p.options) + if (p.options) { $.extend(true, options, p.options); + } } } function parseOptions(opts) { - $.extend(true, options, opts); // $.extend merges arrays, rather than replacing them. When less @@ -743,23 +396,34 @@ // not expected behavior; avoid it by replacing them here. if (opts && opts.colors) { - options.colors = opts.colors; + options.colors = opts.colors; } - if (options.xaxis.color == null) + if (options.xaxis.color == null) { options.xaxis.color = $.color.parse(options.grid.color).scale('a', 0.22).toString(); - if (options.yaxis.color == null) + } + + if (options.yaxis.color == null) { options.yaxis.color = $.color.parse(options.grid.color).scale('a', 0.22).toString(); - - if (options.xaxis.tickColor == null) // grid.tickColor for back-compatibility + } + + if (options.xaxis.tickColor == null) { + // grid.tickColor for back-compatibility options.xaxis.tickColor = options.grid.tickColor || options.xaxis.color; - if (options.yaxis.tickColor == null) // grid.tickColor for back-compatibility + } + + if (options.yaxis.tickColor == null) { + // grid.tickColor for back-compatibility options.yaxis.tickColor = options.grid.tickColor || options.yaxis.color; - - if (options.grid.borderColor == null) + } + + if (options.grid.borderColor == null) { options.grid.borderColor = options.grid.color; - if (options.grid.tickColor == null) + } + + if (options.grid.tickColor == null) { options.grid.tickColor = $.color.parse(options.grid.color).scale('a', 0.22).toString(); + } // Fill in defaults for axis options, including any unspecified // font-spec fields, if a font-spec was provided. @@ -780,7 +444,6 @@ axisCount = options.xaxes.length || 1; for (i = 0; i < axisCount; ++i) { - axisOptions = options.xaxes[i]; if (axisOptions && !axisOptions.tickColor) { axisOptions.tickColor = axisOptions.color; @@ -802,7 +465,6 @@ axisCount = options.yaxes.length || 1; for (i = 0; i < axisCount; ++i) { - axisOptions = options.yaxes[i]; if (axisOptions && !axisOptions.tickColor) { axisOptions.tickColor = axisOptions.color; @@ -822,66 +484,35 @@ } } - // backwards compatibility, to be removed in future - 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"; - // Override the inherit to allow the axis to auto-scale - if (options.x2axis.min == null) { - options.xaxes[1].min = null; - } - if (options.x2axis.max == null) { - options.xaxes[1].max = null; + // save options on axes for future reference + 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]; + } + + //process boxPosition options used for axis.box size + $.each(allAxes(), function(_, axis) { + axis.boxPosition = axis.options.boxPosition || {centerX: 0, centerY: 0}; + }); + + // add hooks from options + for (var n in hooks) { + if (options.hooks[n] && options.hooks[n].length) { + hooks[n] = hooks[n].concat(options.hooks[n]); } } - if (options.y2axis) { - options.yaxes[1] = $.extend(true, {}, options.yaxis, options.y2axis); - options.yaxes[1].position = "right"; - // Override the inherit to allow the axis to auto-scale - 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; - - // save options on axes for future reference - 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]; - - // add hooks from options - 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) { + var oldseries = series; series = parseData(d); fillInSeriesOptions(); - processData(); + processData(oldseries); } function parseData(d) { @@ -896,9 +527,10 @@ $.extend(true, s, d[i]); d[i].data = s.data; + } else { + s.data = d[i]; } - else - s.data = d[i]; + res.push(s); } @@ -907,51 +539,68 @@ function axisNumber(obj, coord) { var a = obj[coord + "axis"]; - if (typeof a == "object") // if we got a real axis, extract number + if (typeof a === "object") { + // if we got a real axis, extract number a = a.n; - if (typeof a != "number") + } + + if (typeof a !== "number") { a = 1; // default to first axis + } + return a; } function allAxes() { // return flat array without annoying null entries - return $.grep(xaxes.concat(yaxes), function (a) { return a; }); + return xaxes.concat(yaxes).filter(function(a) { + return a; + }); } - function canvasToAxisCoords(pos) { + // canvas to axis for cartesian axes + function canvasToCartesianAxisCoords(pos) { // return an object with x/y corresponding to all used axes - var res = {}, i, axis; + var res = {}, + i, axis; for (i = 0; i < xaxes.length; ++i) { axis = xaxes[i]; - if (axis && axis.used) + 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) + if (axis && axis.used) { res["y" + axis.n] = axis.c2p(pos.top); + } } - if (res.x1 !== undefined) + if (res.x1 !== undefined) { res.x = res.x1; - if (res.y1 !== undefined) + } + + if (res.y1 !== undefined) { res.y = res.y1; + } return res; } - function axisToCanvasCoords(pos) { + // axis to canvas for cartesian axes + function cartesianAxisToCanvasCoords(pos) { // get canvas coords from the first pair of x/y found in pos - var res = {}, i, axis, key; + 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) + if (pos[key] == null && axis.n === 1) { key = "x"; + } if (pos[key] != null) { res.left = axis.p2c(pos[key]); @@ -964,8 +613,9 @@ axis = yaxes[i]; if (axis && axis.used) { key = "y" + axis.n; - if (pos[key] == null && axis.n == 1) + if (pos[key] == null && axis.n === 1) { key = "y"; + } if (pos[key] != null) { res.top = axis.p2c(pos[key]); @@ -978,19 +628,21 @@ } function getOrCreateAxis(axes, number) { - if (!axes[number - 1]) + if (!axes[number - 1]) { axes[number - 1] = { n: number, // save the number for future reference - direction: axes == xaxes ? "x" : "y", - options: $.extend(true, {}, axes == xaxes ? options.xaxis : options.yaxis) + 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; + var neededColors = series.length, + maxIndex = -1, + i; // Subtract the number of series that already have fixed colors or // color indexes from the number that we still need to generate. @@ -999,7 +651,7 @@ var sc = series[i].color; if (sc != null) { neededColors--; - if (typeof sc == "number" && sc > maxIndex) { + if (typeof sc === "number" && sc > maxIndex) { maxIndex = sc; } } @@ -1015,12 +667,14 @@ // Generate all the colors, using first the option colors and then // variations on those colors once they're exhausted. - var c, colors = [], colorPool = options.colors, - colorPoolSize = colorPool.length, variation = 0; + var c, colors = [], + colorPool = options.colors, + colorPoolSize = colorPool.length, + variation = 0, + definedColors = Math.max(0, series.length - neededColors); for (i = 0; i < neededColors; i++) { - - c = $.color.parse(colorPool[i % colorPoolSize] || "#666"); + c = $.color.parse(colorPool[(definedColors + i) % colorPoolSize] || "#666"); // Each time we exhaust the colors in the pool we adjust // a scaling factor used to produce more variations on @@ -1030,7 +684,7 @@ // Reset the variation after every few cycles, or else // it will end up producing only white or black colors. - if (i % colorPoolSize == 0 && i) { + if (i % colorPoolSize === 0 && i) { if (variation >= 0) { if (variation < 0.5) { variation = -variation - 0.2; @@ -1043,7 +697,8 @@ // Finalize the series options, filling in their colors - var colori = 0, s; + var colori = 0, + s; for (i = 0; i < series.length; ++i) { s = series[i]; @@ -1051,20 +706,23 @@ if (s.color == null) { s.color = colors[colori].toString(); ++colori; + } else if (typeof s.color === "number") { + s.color = colors[s.color].toString(); } - else if (typeof s.color == "number") - s.color = colors[s.color].toString(); // turn on lines automatically in case nothing is set if (s.lines.show == null) { var v, show = true; - for (v in s) + for (v in s) { if (s[v] && s[v].show) { show = false; break; } - if (show) + } + + if (show) { s.lines.show = true; + } } // If nothing was provided for lines.zero, default it to match @@ -1080,33 +738,58 @@ } } - function processData() { + function processData(prevSeries) { 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, + i, j, k, m, + s, points, ps, val, f, p, data, format; function updateAxis(axis, min, max) { - if (min < axis.datamin && min != -fakeInfinity) + if (min < axis.datamin && min !== -Infinity) { axis.datamin = min; - if (max > axis.datamax && max != fakeInfinity) + } + + if (max > axis.datamax && max !== Infinity) { axis.datamax = max; + } } - $.each(allAxes(), function (_, axis) { + function reusePoints(prevSeries, i) { + if (prevSeries && prevSeries[i] && prevSeries[i].datapoints && prevSeries[i].datapoints.points) { + return prevSeries[i].datapoints.points; + } + + return []; + } + + $.each(allAxes(), function(_, axis) { // init axis - axis.datamin = topSentry; - axis.datamax = bottomSentry; + if (axis.options.growOnly !== true) { + axis.datamin = topSentry; + axis.datamax = bottomSentry; + } else { + if (axis.datamin === undefined) { + axis.datamin = topSentry; + } + if (axis.datamax === undefined) { + 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 ]); + s.datapoints = { + points: [] + }; + + if (s.datapoints.points.length === 0) { + s.datapoints.points = reusePoints(prevSeries, i); + } + + executeHooks(hooks.processRawData, [s, s.data, s.datapoints]); } // first pass: clean and copy data @@ -1119,31 +802,50 @@ if (!format) { format = []; // find out how to copy - 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; + format.push({ + x: true, + y: false, + number: true, + required: true, + computeRange: s.xaxis.options.autoScale !== 'none', + defaultValue: null + }); + + format.push({ + x: false, + y: true, + number: true, + required: true, + computeRange: s.yaxis.options.autoScale !== 'none', + defaultValue: null + }); + + if (s.stack || s.bars.show || (s.lines.show && s.lines.fill)) { + var expectedPs = s.datapoints.pointsize != null ? s.datapoints.pointsize : (s.data && s.data[0] && s.data[0].length ? s.data[0].length : 3); + if (expectedPs > 2) { + format.push({ + x: false, + y: true, + number: true, + required: false, + computeRange: s.yaxis.options.autoScale !== 'none', + defaultValue: 0 + }); } } s.datapoints.format = format; } - if (s.datapoints.pointsize != null) - continue; // already filled in + s.xaxis.used = s.yaxis.used = true; + + if (s.datapoints.pointsize != null) continue; // already filled in 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]; @@ -1157,20 +859,15 @@ if (f) { if (f.number && val != null) { val = +val; // convert to number - if (isNaN(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; + if (f.required) nullify = true; + + if (f.defaultValue != null) val = f.defaultValue; } } @@ -1184,7 +881,7 @@ if (val != null) { f = format[m]; // extract min/max info - if (f.autoscale !== false) { + if (f.computeRange) { if (f.x) { updateAxis(s.xaxis, val, val); } @@ -1196,122 +893,62 @@ points[k + m] = null; } } - else { - // a little bit of line specific stuff that - // perhaps shouldn't be here, but lacking - // better means... - if (insertSteps && k > 0 - && points[k - ps] != null - && points[k - ps] != points[k] - && points[k - ps + 1] != points[k + 1]) { - // copy the point to make room for a middle point - for (m = 0; m < ps; ++m) - points[k + ps + m] = points[k + m]; - - // middle point has same y - points[k + 1] = points[k - ps + 1]; - - // we've added a point, better reflect that - k += ps; - } - } } + + points.length = k; //trims the internal buffer to the correct length } // give the hooks a chance to run for (i = 0; i < series.length; ++i) { s = series[i]; - executeHooks(hooks.processDatapoints, [ s, s.datapoints]); + executeHooks(hooks.processDatapoints, [s, s.datapoints]); } // second pass: find datamax/datamin for auto-scaling 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 (format.every(function (f) { return !f.computeRange; })) { + continue; } - if (s.bars.show) { - // make sure we got room for the bar on the dancing floor - 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; - } + var range = plot.adjustSeriesDataRange(s, + plot.computeRangeForDataSeries(s)); + + executeHooks(hooks.adjustSeriesDataRange, [s, range]); + + updateAxis(s.xaxis, range.xmin, range.xmax); + updateAxis(s.yaxis, range.ymin, range.ymax); + } + + $.each(allAxes(), function(_, axis) { + if (axis.datamin === topSentry) { + axis.datamin = null; } - 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) + if (axis.datamax === bottomSentry) { axis.datamax = null; + } }); } function setupCanvases() { - // Make sure the placeholder is clear of everything except canvases // from a previous plot in this container that we'll try to re-use. placeholder.css("padding", 0) // padding messes up the positioning - .children().filter(function(){ + .children().filter(function() { return !$(this).hasClass("flot-overlay") && !$(this).hasClass('flot-base'); }).remove(); - if (placeholder.css("position") == 'static') + if (placeholder.css("position") === 'static') { placeholder.css("position", "relative"); // for positioning labels and overlay - - surface = new Canvas("flot-base", placeholder); - overlay = new Canvas("flot-overlay", placeholder); // overlay canvas for interactive features + } + + surface = new Canvas("flot-base", placeholder[0]); + overlay = new Canvas("flot-overlay", placeholder[0]); // overlay canvas for interactive features ctx = surface.context; octx = overlay.context; @@ -1333,32 +970,27 @@ } function bindEvents() { - // bind events - if (options.grid.hoverable) { - eventHolder.mousemove(onMouseMove); - - // Use bind, rather than .mouseleave, because we officially - // still support jQuery 1.2.6, which doesn't define a shortcut - // for mouseenter or mouseleave. This was a bug/oversight that - // was fixed somewhere around 1.3.x. We can return to using - // .mouseleave when we drop support for 1.2.6. - - eventHolder.bind("mouseleave", onMouseLeave); - } - - if (options.grid.clickable) - eventHolder.click(onClick); - executeHooks(hooks.bindEvents, [eventHolder]); } + function addEventHandler(event, handler, eventHolder, priority) { + var key = eventHolder + event; + var eventList = eventManager[key] || []; + + eventList.push({"event": event, "handler": handler, "eventHolder": eventHolder, "priority": priority}); + eventList.sort((a, b) => b.priority - a.priority ); + eventList.forEach( eventData => { + eventData.eventHolder.unbind(eventData.event, eventData.handler); + eventData.eventHolder.bind(eventData.event, eventData.handler); + }); + + eventManager[key] = eventList; + } + function shutdown() { - if (redrawTimeout) + if (redrawTimeout) { clearTimeout(redrawTimeout); - - eventHolder.unbind("mousemove", onMouseMove); - eventHolder.unbind("mouseleave", onMouseLeave); - eventHolder.unbind("click", onClick); + } executeHooks(hooks.shutdown, [eventHolder]); } @@ -1367,54 +999,92 @@ // set helper functions on the axis, assumes plot area // has been computed already - function identity(x) { return x; } + function identity(x) { + return x; + } var s, m, t = axis.options.transform || identity, it = axis.options.inverseTransform; // precompute how much the axis is scaling a point // in canvas space - if (axis.direction == "x") { - s = axis.scale = plotWidth / Math.abs(t(axis.max) - t(axis.min)); + if (axis.direction === "x") { + if (isFinite(t(axis.max) - t(axis.min))) { + s = axis.scale = plotWidth / Math.abs(t(axis.max) - t(axis.min)); + } else { + s = axis.scale = 1 / Math.abs($.plot.saturated.delta(t(axis.min), t(axis.max), plotWidth)); + } m = Math.min(t(axis.max), t(axis.min)); - } - else { - s = axis.scale = plotHeight / Math.abs(t(axis.max) - t(axis.min)); + } else { + if (isFinite(t(axis.max) - t(axis.min))) { + s = axis.scale = plotHeight / Math.abs(t(axis.max) - t(axis.min)); + } else { + s = axis.scale = 1 / Math.abs($.plot.saturated.delta(t(axis.min), t(axis.max), plotHeight)); + } s = -s; m = Math.max(t(axis.max), t(axis.min)); } // data point to canvas coordinate - if (t == identity) // slight optimization - axis.p2c = function (p) { return (p - m) * s; }; - else - axis.p2c = function (p) { return (t(p) - m) * s; }; + if (t === identity) { + // slight optimization + axis.p2c = function(p) { + if (isFinite(p - m)) { + return (p - m) * s; + } else { + return (p / 4 - m / 4) * s * 4; + } + }; + } else { + axis.p2c = function(p) { + var tp = t(p); + + if (isFinite(tp - m)) { + return (tp - m) * s; + } else { + return (tp / 4 - m / 4) * s * 4; + } + }; + } + // canvas coordinate to data point - if (!it) - axis.c2p = function (c) { return m + c / s; }; - else - axis.c2p = function (c) { return it(m + c / 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 || [], + ticks = opts.showTickLabels !== 'none' && axis.ticks ? axis.ticks : [], + showMajorTickLabels = opts.showTickLabels === 'major' || opts.showTickLabels === 'all', + showEndpointsTickLabels = opts.showTickLabels === 'endpoints' || opts.showTickLabels === 'all', 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) + var label = t.label; + + if (!t.label || + (showMajorTickLabels === false && i > 0 && i < ticks.length - 1) || + (showEndpointsTickLabels === false && (i === 0 || i === ticks.length - 1))) { continue; - - var info = surface.getTextInfo(layer, t.label, font, null, maxWidth); + } + + if (typeof t.label === 'object') { + label = t.label.name; + } + + var info = surface.getTextInfo(layer, label, font); labelWidth = Math.max(labelWidth, info.width); labelHeight = Math.max(labelHeight, info.height); @@ -1431,16 +1101,21 @@ // dimension per axis, the other dimension depends on the // other axes so will have to wait + // here reserve additional space + executeHooks(hooks.axisReserveSpace, [axis]); + var lw = axis.labelWidth, lh = axis.labelHeight, pos = axis.options.position, isXAxis = axis.direction === "x", tickLength = axis.options.tickLength, + showTicks = axis.options.showTicks, + showMinorTicks = axis.options.showMinorTicks, + gridLines = axis.options.gridLines, axisMargin = options.grid.axisMargin, padding = options.grid.labelMargin, innermost = true, outermost = true, - first = true, found = false; // Determine the axis's position in its direction and on its side @@ -1456,55 +1131,82 @@ innermost = false; } } - if (!found) { - first = false; - } } }); // The outermost axis on each side has no margin - if (outermost) { axisMargin = 0; } - // The ticks for the first axis in each direction stretch across - + // Set the default tickLength if necessary if (tickLength == null) { - tickLength = first ? "full" : 5; + tickLength = TICK_LENGTH_CONSTANT; + } + + // By default, major tick marks are visible + if (showTicks == null) { + showTicks = true; + } + + // By default, minor tick marks are visible + if (showMinorTicks == null) { + showMinorTicks = true; } - if (!isNaN(+tickLength)) - padding += +tickLength; + // By default, grid lines are visible + if (gridLines == null) { + if (innermost) { + gridLines = true; + } else { + gridLines = false; + } + } + + if (!isNaN(+tickLength)) { + padding += showTicks ? +tickLength : 0; + } if (isXAxis) { lh += padding; - if (pos == "bottom") { + if (pos === "bottom") { plotOffset.bottom += lh + axisMargin; - axis.box = { top: surface.height - plotOffset.bottom, height: lh }; + axis.box = { + top: surface.height - plotOffset.bottom, + height: lh + }; + } else { + axis.box = { + top: plotOffset.top + axisMargin, + height: lh + }; + plotOffset.top += lh + axisMargin; } - 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 + }; } } - 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 }; - } - } - - // save for future reference + + // save for future reference axis.position = pos; axis.tickLength = tickLength; + axis.showMinorTicks = showMinorTicks; + axis.showTicks = showTicks; + axis.gridLines = gridLines; axis.box.padding = padding; axis.innermost = innermost; } @@ -1512,11 +1214,10 @@ function allocateAxisBoxSecondPhase(axis) { // now that all axis boxes have been placed in one // dimension, we can set the remaining dimension coordinates - if (axis.direction == "x") { + if (axis.direction === "x") { axis.box.left = plotOffset.left - axis.labelWidth / 2; axis.box.width = surface.width - plotOffset.left - plotOffset.right + axis.labelWidth; - } - else { + } else { axis.box.top = plotOffset.top - axis.labelHeight / 2; axis.box.height = surface.height - plotOffset.bottom - plotOffset.top + axis.labelHeight; } @@ -1527,28 +1228,30 @@ // inside the canvas and isn't clipped off var minMargin = options.grid.minBorderMargin, - axis, i; + i; // check stuff from the plot (FIXME: this should just read // a value from the series, otherwise it's impossible to // customize) 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)); + 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 - }; + var a, offset = {}, + margins = { + left: minMargin, + right: minMargin, + top: minMargin, + bottom: minMargin + }; // check axis labels, note we don't check the actual // labels but instead use the overall width/height to not // jump as much around with replots - $.each(allAxes(), function (_, axis) { + $.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); @@ -1560,53 +1263,87 @@ } }); + for (a in margins) { + offset[a] = margins[a] - plotOffset[a]; + } + $.each(xaxes.concat(yaxes), function(_, axis) { + alignAxisWithGrid(axis, offset, function (offset) { + return offset > 0; + }); + }); + 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; + function alignAxisWithGrid(axis, offset, isValid) { + if (axis.direction === "x") { + if (axis.position === "bottom" && isValid(offset.bottom)) { + axis.box.top -= Math.ceil(offset.bottom); + } + if (axis.position === "top" && isValid(offset.top)) { + axis.box.top += Math.ceil(offset.top); + } + } else { + if (axis.position === "left" && isValid(offset.left)) { + axis.box.left += Math.ceil(offset.left); + } + if (axis.position === "right" && isValid(offset.right)) { + axis.box.left -= Math.ceil(offset.right); + } + } + } + + function setupGrid(autoScale) { + var i, a, axes = allAxes(), + showGrid = options.grid.show; // Initialize the plot's offset from the edge of the canvas - for (var a in plotOffset) { - var margin = options.grid.margin || 0; - plotOffset[a] = typeof margin == "number" ? margin : margin[a] || 0; + for (a in plotOffset) { + plotOffset[a] = 0; } executeHooks(hooks.processOffset, [plotOffset]); // If the grid is visible, add its border width to the offset - - for (var a in plotOffset) { - if(typeof(options.grid.borderWidth) == "object") { + for (a in plotOffset) { + if (typeof (options.grid.borderWidth) === "object") { plotOffset[a] += showGrid ? options.grid.borderWidth[a] : 0; - } - else { + } else { plotOffset[a] += showGrid ? options.grid.borderWidth : 0; } } - $.each(axes, function (_, axis) { + $.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); + setupTickFormatter(axis); + executeHooks(hooks.setRange, [axis, autoScale]); + setRange(axis, autoScale); }); if (showGrid) { - - var allocatedAxes = $.grep(axes, function (axis) { + plotWidth = surface.width - plotOffset.left - plotOffset.right; + plotHeight = surface.height - plotOffset.bottom - plotOffset.top; + + var allocatedAxes = $.grep(axes, function(axis) { return axis.show || axis.reserveSpace; }); - $.each(allocatedAxes, function (_, axis) { + $.each(allocatedAxes, function(_, axis) { // make the ticks setupTickGeneration(axis); - setTicks(axis); - snapRangeToTicks(axis, axis.ticks); + setMajorTicks(axis); + snapRangeToTicks(axis, axis.ticks, series); + + //for computing the endpoints precision, transformationHelpers are needed + setTransformationHelpers(axis); + setEndpointTicks(axis, series); + // find labelWidth/Height for axis measureTickLabels(axis); }); @@ -1614,23 +1351,38 @@ // with all dimensions calculated, we can compute the // axis bounding boxes, start from the outside // (reverse order) - for (i = allocatedAxes.length - 1; i >= 0; --i) + for (i = allocatedAxes.length - 1; i >= 0; --i) { allocateAxisBoxFirstPhase(allocatedAxes[i]); + } // make sure we've got enough space for things that // might stick out adjustLayoutForThingsStickingOut(); - $.each(allocatedAxes, function (_, axis) { + $.each(allocatedAxes, function(_, axis) { allocateAxisBoxSecondPhase(axis); }); } + //adjust axis and plotOffset according to grid.margins + if (options.grid.margin) { + for (a in plotOffset) { + var margin = options.grid.margin || 0; + plotOffset[a] += typeof margin === "number" ? margin : (margin[a] || 0); + } + $.each(xaxes.concat(yaxes), function(_, axis) { + alignAxisWithGrid(axis, options.grid.margin, function(offset) { + return offset !== undefined && offset !== null; + }); + }); + } + + //after adjusting the axis, plot width and height will be modified plotWidth = surface.width - plotOffset.left - plotOffset.right; plotHeight = surface.height - plotOffset.bottom - plotOffset.top; // now we got the proper plot dimensions, we can compute the scaling - $.each(axes, function (_, axis) { + $.each(axes, function(_, axis) { setTransformationHelpers(axis); }); @@ -1638,69 +1390,154 @@ drawAxisLabels(); } - insertLegend(); + executeHooks(hooks.setupGrid, []); } - 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.0) { + function widenMinMax(minimum, maximum) { + var min = (minimum === undefined ? null : minimum); + var max = (maximum === undefined ? null : maximum); + var delta = max - min; + if (delta === 0.0) { // degenerate case - var widen = max == 0 ? 1 : 0.01; - - if (opts.min == null) - min -= widen; + var widen = max === 0 ? 1 : 0.01; + var wmin = null; + if (min == null) { + wmin -= widen; + } + // always widen max if we couldn't widen min to ensure we // don't fall into min == max which doesn't work - if (opts.max == null || opts.min != null) + if (max == null || min != null) { max += widen; - } - else { - // consider autoscaling - var margin = opts.autoscaleMargin; - if (margin != null) { - if (opts.min == null) { - min -= delta * margin; - // make sure we don't go below zero if all values - // are positive - 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; - } + } + + if (wmin != null) { + min = wmin; } } - axis.min = min; - axis.max = max; + + return { + min: min, + max: max + }; } - function setupTickGeneration(axis) { - var opts = axis.options; - - // estimate number of ticks - var noTicks; - if (typeof opts.ticks == "number" && opts.ticks > 0) - noTicks = opts.ticks; - else - // heuristic based on the model a*sqrt(x) fitted to - // some data points that seemed reasonable - noTicks = 0.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; + function autoScaleAxis(axis) { + var opts = axis.options, + min = opts.min, + max = opts.max, + datamin = axis.datamin, + datamax = axis.datamax, + delta; + + switch (opts.autoScale) { + case "none": + min = +(opts.min != null ? opts.min : datamin); + max = +(opts.max != null ? opts.max : datamax); + break; + case "loose": + if (datamin != null && datamax != null) { + min = datamin; + max = datamax; + delta = $.plot.saturated.saturate(max - min); + var margin = ((typeof opts.autoScaleMargin === 'number') ? opts.autoScaleMargin : 0.02); + min = $.plot.saturated.saturate(min - delta * margin); + max = $.plot.saturated.saturate(max + delta * margin); + + // make sure we don't go below zero if all values are positive + if (min < 0 && datamin >= 0) { + min = 0; + } + } else { + min = opts.min; + max = opts.max; + } + break; + case "exact": + min = (datamin != null ? datamin : opts.min); + max = (datamax != null ? datamax : opts.max); + break; + case "sliding-window": + if (datamax > max) { + // move the window to fit the new data, + // keeping the axis range constant + max = datamax; + min = Math.max(datamax - (opts.windowSize || 100), min); + } + break; + } + + var widenedMinMax = widenMinMax(min, max); + min = widenedMinMax.min; + max = widenedMinMax.max; + + // grow loose or grow exact supported + if (opts.growOnly === true && opts.autoScale !== "none" && opts.autoScale !== "sliding-window") { + min = (min < datamin) ? min : (datamin !== null ? datamin : min); + max = (max > datamax) ? max : (datamax !== null ? datamax : max); } - var magn = Math.pow(10, -dec), + axis.autoScaledMin = min; + axis.autoScaledMax = max; + } + + function setRange(axis, autoScale) { + var min = typeof axis.options.min === 'number' ? axis.options.min : axis.min, + max = typeof axis.options.max === 'number' ? axis.options.max : axis.max, + plotOffset = axis.options.offset; + + if (autoScale) { + autoScaleAxis(axis); + min = axis.autoScaledMin; + max = axis.autoScaledMax; + } + + min = (min != null ? min : -1) + (plotOffset.below || 0); + max = (max != null ? max : 1) + (plotOffset.above || 0); + + if (min > max) { + var tmp = min; + min = max; + max = tmp; + axis.options.offset = { above: 0, below: 0 }; + } + + axis.min = $.plot.saturated.saturate(min); + axis.max = $.plot.saturated.saturate(max); + } + + function computeValuePrecision (min, max, direction, ticks, tickDecimals) { + var noTicks = fixupNumberOfTicks(direction, surface, ticks); + + var delta = $.plot.saturated.delta(min, max, noTicks), + dec = -Math.floor(Math.log(delta) / Math.LN10); + + //if it is called with tickDecimals, then the precision should not be greather then that + if (tickDecimals && dec > tickDecimals) { + dec = tickDecimals; + } + + var magn = parseFloat('1e' + (-dec)), + norm = delta / magn; + + if (norm > 2.25 && norm < 3 && (dec + 1) <= tickDecimals) { + //we need an extra decimals when tickSize is 2.5 + ++dec; + } + + return isFinite(dec) ? dec : 0; + }; + + function computeTickSize (min, max, noTicks, tickDecimals) { + var delta = $.plot.saturated.delta(min, max, noTicks), + dec = -Math.floor(Math.log(delta) / Math.LN10); + + //if it is called with tickDecimals, then the precision should not be greather then that + if (tickDecimals && dec > tickDecimals) { + dec = tickDecimals; + } + + var magn = parseFloat('1e' + (-dec)), norm = delta / magn, // norm is between 1.0 and 10.0 size; @@ -1708,10 +1545,8 @@ size = 1; } else if (norm < 3) { size = 2; - // special case for 2.5, requires an extra decimal - if (norm > 2.25 && (maxDec == null || dec + 1 <= maxDec)) { + if (norm > 2.25 && (tickDecimals == null || (dec + 1) <= tickDecimals)) { size = 2.5; - ++dec; } } else if (norm < 7.5) { size = 5; @@ -1720,82 +1555,97 @@ } size *= magn; - - if (opts.minTickSize != null && size < opts.minTickSize) { - size = opts.minTickSize; + return size; + } + + function getAxisTickSize(min, max, direction, options, tickDecimals) { + var noTicks; + + if (typeof options.ticks === "number" && options.ticks > 0) { + noTicks = options.ticks; + } else { + // heuristic based on the model a*sqrt(x) fitted to + // some data points that seemed reasonable + noTicks = 0.3 * Math.sqrt(direction === "x" ? surface.width : surface.height); + } + + var size = computeTickSize(min, max, noTicks, tickDecimals); + + if (options.minTickSize != null && size < options.minTickSize) { + size = options.minTickSize; } - axis.delta = delta; - axis.tickDecimals = Math.max(0, maxDec != null ? maxDec : dec); - axis.tickSize = opts.tickSize || size; - - // Time mode was moved to a plug-in in 0.8, and since so many people use it - // we'll add an especially friendly reminder to make sure they included it. - - if (opts.mode == "time" && !axis.tickGenerator) { - throw new Error("Time mode requires the flot.time plugin."); + return options.tickSize || size; + }; + + function fixupNumberOfTicks(direction, surface, ticksOption) { + var noTicks; + + if (typeof ticksOption === "number" && ticksOption > 0) { + noTicks = ticksOption; + } else { + noTicks = 0.3 * Math.sqrt(direction === "x" ? surface.width : surface.height); } + return noTicks; + } + + function setupTickFormatter(axis) { + var opts = axis.options; + if (!axis.tickFormatter) { + if (typeof opts.tickFormatter === 'function') { + axis.tickFormatter = function() { + var args = Array.prototype.slice.call(arguments); + return "" + opts.tickFormatter.apply(null, args); + }; + } else { + axis.tickFormatter = defaultTickFormatter; + } + } + } + + function setupTickGeneration(axis) { + var opts = axis.options; + var noTicks; + + noTicks = fixupNumberOfTicks(axis.direction, surface, opts.ticks); + + axis.delta = $.plot.saturated.delta(axis.min, axis.max, noTicks); + var precision = plot.computeValuePrecision(axis.min, axis.max, axis.direction, noTicks, opts.tickDecimals); + + axis.tickDecimals = Math.max(0, opts.tickDecimals != null ? opts.tickDecimals : precision); + axis.tickSize = getAxisTickSize(axis.min, axis.max, axis.direction, opts, opts.tickDecimals); + // Flot supports base-10 axes; any other mode else is handled by a plug-in, // like flot.time.js. 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 tickDecimals was specified, ensure that we have exactly that - // much precision; otherwise default to the value's own precision. - - 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 (typeof opts.tickGenerator === 'function') { + axis.tickGenerator = opts.tickGenerator; + } else { + axis.tickGenerator = defaultTickGenerator; + } } - 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 otherAxis = (axis.direction === "x" ? xaxes : yaxes)[opts.alignTicksWithAxis - 1]; + if (otherAxis && otherAxis.used && otherAxis !== axis) { // consider snapping min/max to outermost nice ticks - var niceTicks = axis.tickGenerator(axis); + var niceTicks = axis.tickGenerator(axis, plot); if (niceTicks.length > 0) { - if (opts.min == null) + if (opts.min == null) { axis.min = Math.min(axis.min, niceTicks[0]); - if (opts.max == null && niceTicks.length > 1) + } + + if (opts.max == null && niceTicks.length > 1) { axis.max = Math.max(axis.max, niceTicks[niceTicks.length - 1]); + } } - axis.tickGenerator = function (axis) { + axis.tickGenerator = function(axis) { // copy ticks, scaled to this axis - var ticks = [], v, i; + 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); @@ -1808,28 +1658,31 @@ // ticks don't necessarily fit naturally if (!axis.mode && opts.tickDecimals == null) { var extraDec = Math.max(0, -Math.floor(Math.log(axis.delta) / Math.LN10) + 1), - ts = axis.tickGenerator(axis); + ts = axis.tickGenerator(axis, plot); // only proceed if the tick interval rounded // with an extra decimal doesn't give us a // zero at end - if (!(ts.length > 1 && /\..*0$/.test((ts[1] - ts[0]).toFixed(extraDec)))) + 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)) - // generate the ticks + function setMajorTicks(axis) { + var oticks = axis.options.ticks, + ticks = []; + if (oticks == null || (typeof oticks === "number" && oticks > 0)) { + ticks = axis.tickGenerator(axis, plot); + } else if (oticks) { + if ($.isFunction(oticks)) { + // generate the ticks ticks = oticks(axis); - else + } else { ticks = oticks; + } } // clean up/labelify the supplied ticks, copy them over @@ -1838,48 +1691,105 @@ for (i = 0; i < ticks.length; ++i) { var label = null; var t = ticks[i]; - if (typeof t == "object") { + if (typeof t === "object") { v = +t[0]; - if (t.length > 1) + if (t.length > 1) { label = t[1]; + } + } else { + v = +t; } - else - v = +t; - if (label == null) - label = axis.tickFormatter(v, axis); - if (!isNaN(v)) - axis.ticks.push({ v: v, label: label }); + + if (!isNaN(v)) { + axis.ticks.push( + newTick(v, label, axis, 'major')); + } } } - function snapRangeToTicks(axis, ticks) { - if (axis.options.autoscaleMargin && ticks.length > 0) { + function newTick(v, label, axis, type) { + if (label === null) { + switch (type) { + case 'min': + case 'max': + //improving the precision of endpoints + var precision = getEndpointPrecision(v, axis); + label = isFinite(precision) ? axis.tickFormatter(v, axis, precision, plot) : axis.tickFormatter(v, axis, precision, plot); + break; + case 'major': + label = axis.tickFormatter(v, axis, undefined, plot); + } + } + return { + v: v, + label: label + }; + } + + function snapRangeToTicks(axis, ticks, series) { + var anyDataInSeries = function(series) { + return series.some(e => e.datapoints.points.length > 0); + } + + if (axis.options.autoScale === "loose" && ticks.length > 0 && anyDataInSeries(series)) { // snap to ticks - 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); + axis.min = Math.min(axis.min, ticks[0].v); + axis.max = Math.max(axis.max, ticks[ticks.length - 1].v); + } + } + + function getEndpointPrecision(value, axis) { + var canvas1 = Math.floor(axis.p2c(value)), + canvas2 = axis.direction === "x" ? canvas1 + 1 : canvas1 - 1, + point1 = axis.c2p(canvas1), + point2 = axis.c2p(canvas2), + precision = computeValuePrecision(point1, point2, axis.direction, 1); + + return precision; + } + + function setEndpointTicks(axis, series) { + if (isValidEndpointTick(axis, series)) { + axis.ticks.unshift(newTick(axis.min, null, axis, 'min')); + axis.ticks.push(newTick(axis.max, null, axis, 'max')); + } + } + + function isValidEndpointTick(axis, series) { + if (axis.options.showTickLabels === 'endpoints') { + return true; + } + if (axis.options.showTickLabels === 'all') { + var associatedSeries = series.filter(function(s) { + return s.xaxis === axis; + }), + notAllBarSeries = associatedSeries.some(function(s) { + return !s.bars.show; + }); + return associatedSeries.length === 0 || notAllBarSeries; + } + if (axis.options.showTickLabels === 'major' || axis.options.showTickLabels === 'none') { + return false; } } function draw() { - surface.clear(); - executeHooks(hooks.drawBackground, [ctx]); var grid = options.grid; // draw background, if any - if (grid.show && grid.backgroundColor) + 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]]); + executeHooks(hooks.drawSeries, [ctx, series[i], i, getColorOrGradient]); drawSeries(series[i]); } @@ -1893,7 +1803,6 @@ // A draw implies that either the axes or data have changed, so we // should probably update the overlay highlights as well. - triggerRedrawOverlay(); } @@ -1902,10 +1811,13 @@ for (var i = 0; i < axes.length; ++i) { axis = axes[i]; - if (axis.direction == coord) { + if (axis.direction === coord) { key = coord + axis.n + "axis"; - if (!ranges[key] && axis.n == 1) - key = coord + "axis"; // support x1axis as xaxis + if (!ranges[key] && axis.n === 1) { + // support x1axis as xaxis + key = coord + "axis"; + } + if (ranges[key]) { from = ranges[key].from; to = ranges[key].to; @@ -1916,7 +1828,7 @@ // backwards-compat stuff - to be removed in future if (!ranges[key]) { - axis = coord == "x" ? xaxes[0] : yaxes[0]; + axis = coord === "x" ? xaxes[0] : yaxes[0]; from = ranges[coord + "1"]; to = ranges[coord + "2"]; } @@ -1928,7 +1840,11 @@ to = tmp; } - return { from: from, to: to, axis: axis }; + return { + from: from, + to: to, + axis: axis + }; } function drawBackground() { @@ -1940,14 +1856,11 @@ ctx.restore(); } - function drawGrid() { - var i, axes, bw, bc; - - ctx.save(); - ctx.translate(plotOffset.left, plotOffset.top); - + function drawMarkings() { // draw markings - var markings = options.grid.markings; + var markings = options.grid.markings, + axes; + if (markings) { if ($.isFunction(markings)) { axes = plot.getAxes(); @@ -1961,25 +1874,34 @@ markings = markings(axes); } + var i; for (i = 0; i < markings.length; ++i) { var m = markings[i], xrange = extractRange(m, "x"), yrange = extractRange(m, "y"); // fill in missing - if (xrange.from == null) + if (xrange.from == null) { xrange.from = xrange.axis.min; - if (xrange.to == null) + } + + if (xrange.to == null) { xrange.to = xrange.axis.max; - if (yrange.from == null) + } + + if (yrange.from == null) { yrange.from = yrange.axis.min; - if (yrange.to == null) + } + + if (yrange.to == null) { yrange.to = yrange.axis.max; + } // clip if (xrange.to < xrange.axis.min || xrange.from > xrange.axis.max || - yrange.to < yrange.axis.min || yrange.from > yrange.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); @@ -2010,183 +1932,369 @@ ctx.lineTo(xrange.to + subPixel, yrange.to); } else { ctx.moveTo(xrange.from, yrange.to + subPixel); - ctx.lineTo(xrange.to, 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); + xrange.to - xrange.from, + yrange.from - yrange.to); } } } - - // draw the ticks - 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; - - // find the edges - 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); + } + + function findEdges(axis) { + var box = axis.box, + x = 0, + y = 0; + + // find the edges + if (axis.direction === "x") { + x = 0; + y = box.top - plotOffset.top + (axis.position === "top" ? box.height : 0); + } else { + y = 0; + x = box.left - plotOffset.left + (axis.position === "left" ? box.width : 0) + axis.boxPosition.centerX; + } + + return { + x: x, + y: y + }; + }; + + function alignPosition(lineWidth, pos) { + return ((lineWidth % 2) !== 0) ? Math.floor(pos) + 0.5 : pos; + }; + + function drawTickBar(axis) { + ctx.lineWidth = 1; + var edges = findEdges(axis), + x = edges.x, + y = edges.y; + + // draw tick bar + if (axis.show) { + var xoff = 0, + yoff = 0; + + ctx.strokeStyle = axis.options.color; + ctx.beginPath(); + if (axis.direction === "x") { + xoff = plotWidth + 1; + } else { + yoff = plotHeight + 1; } - 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.direction === "x") { + y = alignPosition(ctx.lineWidth, y); + } else { + x = alignPosition(ctx.lineWidth, x); } - // draw tick bar - 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) + 0.5; - } else { - x = Math.floor(x) + 0.5; + ctx.moveTo(x, y); + ctx.lineTo(x + xoff, y + yoff); + ctx.stroke(); + } + }; + + function drawTickMarks(axis) { + var t = axis.tickLength, + minorTicks = axis.showMinorTicks, + minorTicksNr = MINOR_TICKS_COUNT_CONSTANT, + edges = findEdges(axis), + x = edges.x, + y = edges.y, + i = 0; + + // draw major tick marks + ctx.strokeStyle = axis.options.color; + ctx.beginPath(); + + for (i = 0; i < axis.ticks.length; ++i) { + var v = axis.ticks[i].v, + xoff = 0, + yoff = 0, + xminor = 0, + yminor = 0, + j; + + if (!isNaN(v) && v >= axis.min && v <= axis.max) { + if (axis.direction === "x") { + x = axis.p2c(v); + yoff = t; + + if (axis.position === "top") { + yoff = -yoff; + } + } else { + y = axis.p2c(v); + xoff = t; + + if (axis.position === "left") { + xoff = -xoff; } } - ctx.moveTo(x, y); - ctx.lineTo(x + xoff, y + yoff); - ctx.stroke(); - } - - // draw ticks - - 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 - // skip those lying on the axes if we got a border - || (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) + 0.5; - else - y = Math.floor(y) + 0.5; + if (axis.direction === "x") { + x = alignPosition(ctx.lineWidth, x); + } else { + y = alignPosition(ctx.lineWidth, y); } ctx.moveTo(x, y); ctx.lineTo(x + xoff, y + yoff); } - ctx.stroke(); + //draw minor tick marks + if (minorTicks === true && i < axis.ticks.length - 1) { + var v1 = axis.ticks[i].v, + v2 = axis.ticks[i + 1].v, + step = (v2 - v1) / (minorTicksNr + 1); + + for (j = 1; j <= minorTicksNr; j++) { + // compute minor tick position + if (axis.direction === "x") { + yminor = t / 2; // minor ticks are half length + x = alignPosition(ctx.lineWidth, axis.p2c(v1 + j * step)) + + if (axis.position === "top") { + yminor = -yminor; + } + + // don't go over the plot borders + if ((x < 0) || (x > plotWidth)) { + continue; + } + } else { + xminor = t / 2; // minor ticks are half length + y = alignPosition(ctx.lineWidth, axis.p2c(v1 + j * step)); + + if (axis.position === "left") { + xminor = -xminor; + } + + // don't go over the plot borders + if ((y < 0) || (y > plotHeight)) { + continue; + } + } + + ctx.moveTo(x, y); + ctx.lineTo(x + xminor, y + yminor); + } + } + } + + ctx.stroke(); + }; + + function drawGridLines(axis) { + // check if the line will be overlapped with a border + var overlappedWithBorder = function (value) { + var bw = options.grid.borderWidth; + return (((typeof bw === "object" && bw[axis.position] > 0) || bw > 0) && (value === axis.min || value === axis.max)); + }; + + ctx.strokeStyle = options.grid.tickColor; + ctx.beginPath(); + var i; + for (i = 0; i < axis.ticks.length; ++i) { + var v = axis.ticks[i].v, + xoff = 0, + yoff = 0, + x = 0, + y = 0; + + if (isNaN(v) || v < axis.min || v > axis.max) continue; + + // skip those lying on the axes if we got a border + if (overlappedWithBorder(v)) continue; + + if (axis.direction === "x") { + x = axis.p2c(v); + y = plotHeight; + yoff = -plotHeight; + } else { + x = 0; + y = axis.p2c(v); + xoff = plotWidth; + } + + if (axis.direction === "x") { + x = alignPosition(ctx.lineWidth, x); + } else { + y = alignPosition(ctx.lineWidth, y); + } + + ctx.moveTo(x, y); + ctx.lineTo(x + xoff, y + yoff); } + ctx.stroke(); + }; + + function drawBorder() { + // If either borderWidth or borderColor is an object, then draw the border + // line by line instead of as one rectangle + var bw = options.grid.borderWidth, + 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); + } + }; + + function drawGrid() { + var axes, bw; + + ctx.save(); + ctx.translate(plotOffset.left, plotOffset.top); + + drawMarkings(); + + axes = allAxes(); + bw = options.grid.borderWidth; + + for (var j = 0; j < axes.length; ++j) { + var axis = axes[j]; + + if (!axis.show) { + continue; + } + + drawTickBar(axis); + if (axis.showTicks === true) { + drawTickMarks(axis); + } + + if (axis.gridLines === true) { + drawGridLines(axis, bw); + } + } // draw border if (bw) { - // If either borderWidth or borderColor is an object, then draw the border - // line by line instead of as one rectangle - 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); - } + drawBorder(); } ctx.restore(); } function drawAxisLabels() { - - $.each(allAxes(), function (_, axis) { + $.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; + i, x, y, halign, valign, info, + margin = 3, + nullBox = {x: NaN, y: NaN, width: NaN, height: NaN}, newLabelBox, labelBoxes = [], + overlapping = function(x11, y11, x12, y12, x21, y21, x22, y22) { + return ((x11 <= x21 && x21 <= x12) || (x21 <= x11 && x11 <= x22)) && + ((y11 <= y21 && y21 <= y12) || (y21 <= y11 && y11 <= y22)); + }, + overlapsOtherLabels = function(newLabelBox, previousLabelBoxes) { + return previousLabelBoxes.some(function(labelBox) { + return overlapping( + newLabelBox.x, newLabelBox.y, newLabelBox.x + newLabelBox.width, newLabelBox.y + newLabelBox.height, + labelBox.x, labelBox.y, labelBox.x + labelBox.width, labelBox.y + labelBox.height); + }); + }, + drawAxisLabel = function (tick, labelBoxes) { + if (!tick || !tick.label || tick.v < axis.min || tick.v > axis.max) { + return nullBox; + } + + info = surface.getTextInfo(layer, tick.label, font); + + if (axis.direction === "x") { + halign = "center"; + x = plotOffset.left + axis.p2c(tick.v); + if (axis.position === "bottom") { + y = box.top + box.padding - axis.boxPosition.centerY; + } else { + y = box.top + box.height - box.padding + axis.boxPosition.centerY; + valign = "bottom"; + } + newLabelBox = {x: x - info.width / 2 - margin, y: y - margin, width: info.width + 2 * margin, height: info.height + 2 * margin}; + } else { + valign = "middle"; + y = plotOffset.top + axis.p2c(tick.v); + if (axis.position === "left") { + x = box.left + box.width - box.padding - axis.boxPosition.centerX; + halign = "right"; + } else { + x = box.left + box.padding + axis.boxPosition.centerX; + } + newLabelBox = {x: x - info.width / 2 - margin, y: y - margin, width: info.width + 2 * margin, height: info.height + 2 * margin}; + } + + if (overlapsOtherLabels(newLabelBox, labelBoxes)) { + return nullBox; + } + + surface.addText(layer, x, y, tick.label, font, null, null, halign, valign); + + return newLabelBox; + }; // Remove text before checking for axis.show and ticks.length; // otherwise plugins, like flot-tickrotor, that draw their own @@ -2194,928 +2302,439 @@ surface.removeText(layer); - if (!axis.show || axis.ticks.length == 0) + executeHooks(hooks.drawAxis, [axis, surface]); + + if (!axis.show) { 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"; + } + + switch (axis.options.showTickLabels) { + case 'none': + break; + case 'endpoints': + labelBoxes.push(drawAxisLabel(axis.ticks[0], labelBoxes)); + labelBoxes.push(drawAxisLabel(axis.ticks[axis.ticks.length - 1], labelBoxes)); + break; + case 'major': + labelBoxes.push(drawAxisLabel(axis.ticks[0], labelBoxes)); + labelBoxes.push(drawAxisLabel(axis.ticks[axis.ticks.length - 1], labelBoxes)); + for (i = 1; i < axis.ticks.length - 1; ++i) { + labelBoxes.push(drawAxisLabel(axis.ticks[i], labelBoxes)); } - } 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; + break; + case 'all': + labelBoxes.push(drawAxisLabel(axis.ticks[0], [])); + labelBoxes.push(drawAxisLabel(axis.ticks[axis.ticks.length - 1], labelBoxes)); + for (i = 1; i < axis.ticks.length - 1; ++i) { + labelBoxes.push(drawAxisLabel(axis.ticks[i], labelBoxes)); } - } - - surface.addText(layer, x, y, tick.label, font, null, null, halign, valign); + break; } }); } function drawSeries(series) { - if (series.lines.show) - drawSeriesLines(series); - if (series.bars.show) - drawSeriesBars(series); - if (series.points.show) - drawSeriesPoints(series); + if (series.lines.show) { + $.plot.drawSeries.drawSeriesLines(series, ctx, plotOffset, plotWidth, plotHeight, plot.drawSymbol, getColorOrGradient); + } + + if (series.bars.show) { + $.plot.drawSeries.drawSeriesBars(series, ctx, plotOffset, plotWidth, plotHeight, plot.drawSymbol, getColorOrGradient); + } + + if (series.points.show) { + $.plot.drawSeries.drawSeriesPoints(series, ctx, plotOffset, plotWidth, plotHeight, plot.drawSymbol, getColorOrGradient); + } } - 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) + function computeRangeForDataSeries(series, force, isValid) { + var points = series.datapoints.points, + ps = series.datapoints.pointsize, + format = series.datapoints.format, + topSentry = Number.POSITIVE_INFINITY, + bottomSentry = Number.NEGATIVE_INFINITY, + range = { + xmin: topSentry, + ymin: topSentry, + xmax: bottomSentry, + ymax: bottomSentry + }; + + for (var j = 0; j < points.length; j += ps) { + if (points[j] === null) { + continue; + } + + if (typeof (isValid) === 'function' && !isValid(points[j])) { + continue; + } + + for (var m = 0; m < ps; ++m) { + var val = points[j + m], + f = format[m]; + if (f === null || f === undefined) { continue; - - // clip with ymin - if (y1 <= y2 && y1 < axisy.min) { - if (y2 < axisy.min) - continue; // line segment is outside - // compute new intersection point - 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; - } - - // clip with ymax - 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; - } - - // clip with xmin - 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 (typeof (isValid) === 'function' && !isValid(val)) { + continue; } - // clip with xmax - 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 ((!force && !f.computeRange) || val === Infinity || val === -Infinity) { + continue; } - 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; - - // we process each segment in two turns, first forward - // direction to sketch out top, then once we hit the - // end we go backwards to sketch the bottom - while (true) { - if (ps > 0 && i > points.length + ps) - break; - - i += ps; // ps is negative if going backwards - - 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) { - // at turning point - segmentEnd = i; - ps = -ps; - ypos = 2; - continue; + if (f.x === true) { + if (val < range.xmin) { + range.xmin = val; } - if (ps < 0 && i == segmentStart + ps) { - // done with the reverse sweep - ctx.fill(); - areaOpen = false; - ps = -ps; - ypos = 1; - i = segmentStart = segmentEnd + ps; - continue; + if (val > range.xmax) { + range.xmax = val; } } - if (x1 == null || x2 == null) - continue; - - // clip x values - - // clip with xmin - 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; - } - - // clip with xmax - 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) { - // open area - ctx.beginPath(); - ctx.moveTo(axisx.p2c(x1), axisy.p2c(bottom)); - areaOpen = true; - } - - // now first check the case where both is outside - 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; - } - - // else it's a bit more complicated, there might - // be a flat maxed out rectangle first, then a - // triangular cutout or reverse; to find these - // keep track of the current x values - var x1old = x1, x2old = x2; - - // clip the y values, without shortcutting, we - // go through all cases in turn - - // clip with ymin - 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; - } - - // clip with ymax - 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 the x value was changed we got a rectangle - // to fill - if (x1 != x1old) { - ctx.lineTo(axisx.p2c(x1old), axisy.p2c(y1)); - // it goes to (x1, y1), but we fill that below - } - - // fill triangular section, this sometimes result - // in redundant points if (x1, y1) hasn't changed - // from previous line to, but we just ignore that - ctx.lineTo(axisx.p2c(x1), axisy.p2c(y1)); - ctx.lineTo(axisx.p2c(x2), axisy.p2c(y2)); - - // fill the other rectangle if it's there - if (x2 != x2old) { - ctx.lineTo(axisx.p2c(x2), axisy.p2c(y2)); - ctx.lineTo(axisx.p2c(x2old), axisy.p2c(y2)); + if (f.y === true) { + if (val < range.ymin) { + range.ymin = val; + } + + if (val > range.ymax) { + range.ymax = val; + } } } } - ctx.save(); - ctx.translate(plotOffset.left, plotOffset.top); - ctx.lineJoin = "round"; - - var lw = series.lines.lineWidth, - sw = series.shadowSize; - // FIXME: consider another form of shadow when filling is turned on - if (lw > 0 && sw > 0) { - // draw shadow as a thick and thin line with transparency - ctx.lineWidth = sw; - ctx.strokeStyle = "rgba(0,0,0,0.1)"; - // position shadow at angle from the mid of line - 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); + return range; + }; + + function adjustSeriesDataRange(series, range) { + if (series.bars.show) { + // make sure we got room for the bar on the dancing floor + var delta; + + // update bar width if needed + var useAbsoluteBarWidth = series.bars.barWidth[1]; + if (series.datapoints && series.datapoints.points && !useAbsoluteBarWidth) { + computeBarWidth(series); + } + + var barWidth = series.bars.barWidth[0] || series.bars.barWidth; + switch (series.bars.align) { + case "left": + delta = 0; + break; + case "right": + delta = -barWidth; + break; + default: + delta = -barWidth / 2; + } + + if (series.bars.horizontal) { + range.ymin += delta; + range.ymax += delta + barWidth; + } + else { + range.xmin += delta; + range.xmax += delta + barWidth; + } } - 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(); + if ((series.bars.show && series.bars.zero) || (series.lines.show && series.lines.zero)) { + var ps = series.datapoints.pointsize; + + // make sure the 0 point is included in the computed y range when requested + if (ps <= 2) { + /*if ps > 0 the points were already taken into account for autoScale */ + range.ymin = Math.min(0, range.ymin); + range.ymax = Math.max(0, range.ymax); + } + } + + return range; + }; + + function computeBarWidth(series) { + var xValues = []; + var pointsize = series.datapoints.pointsize, minDistance = Number.MAX_VALUE; + + if (series.datapoints.points.length <= pointsize) { + minDistance = 1; + } + + var start = series.bars.horizontal ? 1 : 0; + for (var j = start; j < series.datapoints.points.length; j += pointsize) { + if (isFinite(series.datapoints.points[j]) && series.datapoints.points[j] !== null) { + xValues.push(series.datapoints.points[j]); + } + } + + function onlyUnique(value, index, self) { + return self.indexOf(value) === index; + } + + xValues = xValues.filter( onlyUnique ); + xValues.sort(function(a, b){return a - b}); + + for (var j = 1; j < xValues.length; j++) { + var distance = Math.abs(xValues[j] - xValues[j - 1]); + if (distance < minDistance && isFinite(distance)) { + minDistance = distance; } } - 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 the user sets the line width to 0, we change it to a very - // small value. A line width of 0 seems to force the default of 1. - // Doing the conditional here allows the shadow setting to still be - // optional even with a lineWidth of 0. - - if( lw == 0 ) - lw = 0.0001; - - if (lw > 0 && sw > 0) { - // draw shadow in two steps - 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); + if (typeof series.bars.barWidth === "number") { + series.bars.barWidth = series.bars.barWidth * minDistance; + } else { + series.bars.barWidth[0] = series.bars.barWidth[0] * minDistance; } - - 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; - - // in horizontal mode, we start the bar from the left - // instead of from the bottom so it appears to be - // horizontal rather than vertical - if (horizontal) { - drawBottom = drawRight = drawTop = true; - drawLeft = false; - left = b; - right = x; - top = y + barLeft; - bottom = y + barRight; - - // account for negative bars - if (right < left) { - tmp = right; - right = left; - left = tmp; - drawLeft = true; - drawRight = false; + // returns the data item the mouse is over/ the cursor is closest to, or null if none is found + function findNearbyItem(mouseX, mouseY, seriesFilter, radius, computeDistance) { + var i, j, + item = null, + smallestDistance = radius * radius + 1; + + for (var i = series.length - 1; i >= 0; --i) { + if (!seriesFilter(i)) continue; + + var s = series[i]; + if (!s.datapoints) return; + + if (s.lines.show || s.points.show) { + var found = findNearbyPoint(s, mouseX, mouseY, radius, smallestDistance, computeDistance); + if (found) { + smallestDistance = found.distance; + item = [i, found.dataIndex]; + } } - } - else { - drawLeft = drawRight = drawTop = true; - drawBottom = false; - left = x + barLeft; - right = x + barRight; - bottom = b; - top = y; - - // account for negative bars - if (top < bottom) { - tmp = top; - top = bottom; - bottom = tmp; - drawBottom = true; - drawTop = false; + + if (s.bars.show && !item) { // no other point can be nearby + var foundIndex = findNearbyBar(s, mouseX, mouseY); + if (foundIndex >= 0) item = [i, foundIndex]; } } - // clip - 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 (item) { + i = item[0]; + j = item[1]; + var ps = series[i].datapoints.pointsize; + + return { + datapoint: series[i].datapoints.points.slice(j * ps, (j + 1) * ps), + dataIndex: j, + series: series[i], + seriesIndex: i + }; } - 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); - - // fill the bar - if (fillStyleCallback) { - c.fillStyle = fillStyleCallback(bottom, top); - c.fillRect(left, top, right - left, bottom - top) + return null; + } + + function findNearbyPoint (series, mouseX, mouseY, maxDistance, smallestDistance, computeDistance) { + var mx = series.xaxis.c2p(mouseX), + my = series.yaxis.c2p(mouseY), + maxx = maxDistance / series.xaxis.scale, + maxy = maxDistance / series.yaxis.scale, + points = series.datapoints.points, + ps = series.datapoints.pointsize; + + // with inverse transforms, we can't use the maxx/maxy + // optimization, sadly + if (series.xaxis.options.inverseTransform) { + maxx = Number.MAX_VALUE; } - // draw outline - if (lineWidth > 0 && (drawLeft || drawRight || drawTop || drawBottom)) { - c.beginPath(); - - // FIXME: inline moveTo is buggy with excanvas - 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(); + if (series.yaxis.options.inverseTransform) { + maxy = Number.MAX_VALUE; } - } - - 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); + + var found = null; + for (var j = 0; j < points.length; j += ps) { + var x = points[j]; + var y = points[j + 1]; + if (x == null) { + continue; + } + + if (x - mx > maxx || x - mx < -maxx || + y - my > maxy || y - my < -maxy) { + continue; + } + + // We have to calculate distances in pixels, not in + // data units, because the scales of the axes may be different + var dx = Math.abs(series.xaxis.p2c(x) - mouseX); + var dy = Math.abs(series.yaxis.p2c(y) - mouseY); + var dist = computeDistance ? computeDistance(dx, dy) : dx * dx + dy * dy; + + // use <= to ensure last point takes precedence + // (last generally means on top of) + if (dist < smallestDistance) { + smallestDistance = dist; + found = { dataIndex: j / ps, distance: dist }; } } - ctx.save(); - ctx.translate(plotOffset.left, plotOffset.top); - - // FIXME: figure out a way to add shadows (for instance along the right edge) - ctx.lineWidth = series.bars.lineWidth; - ctx.strokeStyle = series.color; - - var barLeft; + return found; + } + + function findNearbyBar (series, mouseX, mouseY) { + var barLeft, barRight, + barWidth = series.bars.barWidth[0] || series.bars.barWidth, + mx = series.xaxis.c2p(mouseX), + my = series.yaxis.c2p(mouseY), + points = series.datapoints.points, + ps = series.datapoints.pointsize; switch (series.bars.align) { case "left": barLeft = 0; break; case "right": - barLeft = -series.bars.barWidth; + barLeft = -barWidth; break; default: - barLeft = -series.bars.barWidth / 2; + barLeft = -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 : 0.4; - c.normalize(); - return c.toString(); + barRight = barLeft + barWidth; + + var fillTowards = series.bars.fillTowards || 0; + var bottom = fillTowards > series.yaxis.min ? Math.min(series.yaxis.max, fillTowards) : series.yaxis.min; + + var foundIndex = -1; + for (var j = 0; j < points.length; j += ps) { + var x = points[j], y = points[j + 1]; + if (x == null) + continue; + + // for a bar graph, the cursor must be inside the bar + if (series.bars.horizontal ? + (mx <= Math.max(bottom, x) && mx >= Math.min(bottom, x) && + my >= y + barLeft && my <= y + barRight) : + (mx >= x + barLeft && mx <= x + barRight && + my >= Math.min(bottom, y) && my <= Math.max(bottom, y))) + foundIndex = j / ps; + } + + return foundIndex; } - 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; - - // Build a list of legend entries, with each having a label and a color - - 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 - }); + function findNearbyInterpolationPoint(posX, posY, seriesFilter) { + var i, j, dist, dx, dy, ps, + item, + smallestDistance = Number.MAX_VALUE; + + for (i = 0; i < series.length; ++i) { + if (!seriesFilter(i)) { + continue; + } + var points = series[i].datapoints.points; + ps = series[i].datapoints.pointsize; + + // if the data is coming from positive -> negative, reverse the comparison + const comparer = points[points.length - ps] < points[0] + ? function (x1, x2) { return x1 > x2 } + : function (x1, x2) { return x2 > x1 }; + + // do not interpolate outside the bounds of the data. + if (comparer(posX, points[0])) { + continue; + } + + // Find the nearest points, x-wise + for (j = ps; j < points.length; j += ps) { + if (comparer(posX, points[j])) { + break; } } - } - - // Sort the legend using either the default or a custom comparator - - 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 // Logical XOR - ); - }); - } - } - - // Generate markup for the list of entries, in their final order - - for (var i = 0; i < entries.length; ++i) { - - var entry = entries[i]; - - if (i % options.legend.noColumns == 0) { - if (rowStarted) - fragments.push(''); - fragments.push(''); - rowStarted = true; - } - - fragments.push( - '
' + - '' + entry.label + '' - ); - } - - if (rowStarted) - fragments.push(''); - - if (fragments.length == 0) - return; - - var table = '' + fragments.join("") + '
'; - 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 = $('
' + table.replace('style="', 'style="position:absolute;' + pos +';') + '
').appendTo(placeholder); - if (options.legend.backgroundOpacity != 0.0) { - // put in the transparent background - // separately to avoid blended labels and - // label boxes - 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(); - $('
').prependTo(legend).css('opacity', options.legend.backgroundOpacity); + + // Now Interpolate + var y, + p1x = points[j - ps], + p1y = points[j - ps + 1], + p2x = points[j], + p2y = points[j + 1]; + + if ((p1x === undefined) || (p2x === undefined) || + (p1y === undefined) || (p2y === undefined)) { + continue; } - } - } - - - // interactive features - - var highlights = [], - redrawTimeout = null; - - // returns the data item the mouse is over, or null if none is found - 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), // precompute some stuff to make the loop faster - my = axisy.c2p(mouseY), - maxx = maxDistance / axisx.scale, - maxy = maxDistance / axisy.scale; - - ps = s.datapoints.pointsize; - // with inverse transforms, we can't use the maxx/maxy - // optimization, sadly - 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; - - // For points and lines, the cursor must be within a - // certain distance to the data point - if (x - mx > maxx || x - mx < -maxx || - y - my > maxy || y - my < -maxy) - continue; - - // We have to calculate distances in pixels, not in - // data units, because the scales of the axes may be different - var dx = Math.abs(axisx.p2c(x) - mouseX), - dy = Math.abs(axisy.p2c(y) - mouseY), - dist = dx * dx + dy * dy; // we save the sqrt - - // use <= to ensure last point takes precedence - // (last generally means on top of) - if (dist < smallestDistance) { - smallestDistance = dist; - item = [i, j / ps]; - } - } + + if (p1x === p2x) { + y = p2y + } else { + y = p1y + (p2y - p1y) * (posX - p1x) / (p2x - p1x); } - if (s.bars.show && !item) { // no other point can be nearby - - 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; - - // for a bar graph, the cursor must be inside the bar - 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]; - } + posY = y; + + dx = Math.abs(series[i].xaxis.p2c(p2x) - posX); + dy = Math.abs(series[i].yaxis.p2c(p2y) - posY); + dist = dx * dx + dy * dy; + + if (dist < smallestDistance) { + smallestDistance = dist; + item = [posX, posY, i, j]; } } if (item) { - i = item[0]; - j = item[1]; + i = item[2]; + j = item[3]; ps = series[i].datapoints.pointsize; - - return { datapoint: series[i].datapoints.points.slice(j * ps, (j + 1) * ps), - dataIndex: j, - series: series[i], - seriesIndex: i }; + points = series[i].datapoints.points; + p1x = points[j - ps]; + p1y = points[j - ps + 1]; + p2x = points[j]; + p2y = points[j + 1]; + + return { + datapoint: [item[0], item[1]], + leftPoint: [p1x, p1y], + rightPoint: [p2x, p2y], + 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; }); - } - - // trigger click or hover event (they send the same parameters - // so we share their code) - 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) { - // fill in mouse pos for any listeners out there - 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) { - // clear auto-highlights - 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) { // skip event queue + if (t === -1) { // skip event queue drawOverlay(); return; } - if (!redrawTimeout) - redrawTimeout = setTimeout(drawOverlay, t); - } - - function drawOverlay() { - redrawTimeout = null; - - // draw highlights - 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); + if (!redrawTimeout) { + redrawTimeout = setTimeout(function() { + drawOverlay(plot); + }, t); } - 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(); + function drawOverlay(plot) { + redrawTimeout = null; + + if (!octx) { 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', 0.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', 0.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); + overlay.clear(); + executeHooks(hooks.drawOverlay, [octx, overlay]); + var event = new CustomEvent('onDrawingDone'); + plot.getEventHolder().dispatchEvent(event); + plot.getPlaceholder().trigger('drawingdone'); } function getColorOrGradient(spec, bottom, top, defaultColor) { - if (typeof spec == "string") + if (typeof spec === "string") { return spec; - else { + } else { // assume this is a gradient spec; IE currently only // supports a simple vertical gradient properly, so that's // what we support too @@ -3123,12 +2742,16 @@ for (var i = 0, l = spec.colors.length; i < l; ++i) { var c = spec.colors[i]; - if (typeof c != "string") { + if (typeof c !== "string") { var co = $.color.parse(defaultColor); - if (c.brightness != null) + if (c.brightness != null) { co = co.scale('rgb', c.brightness); - if (c.opacity != null) + } + + if (c.opacity != null) { co.a *= c.opacity; + } + c = co.toString(); } gradient.addColorStop(i / (l - 1), c); @@ -3142,27 +2765,22 @@ // Add the plot function to the top level of the jQuery object $.plot = function(placeholder, data, options) { - //var t0 = new Date(); var plot = new Plot($(placeholder), data, options, $.plot.plugins); - //(window.console ? console.log : alert)("time used (msecs): " + ((new Date()).getTime() - t0.getTime())); return plot; }; - $.plot.version = "0.8.3"; + $.plot.version = "3.0.0"; $.plot.plugins = []; // Also add the plot function as a chainable property - $.fn.plot = function(data, options) { return this.each(function() { $.plot(this, data, options); }); }; - // round to nearby lower multiple of base - function floorInBase(n, base) { - return base * Math.floor(n / base); - } - + $.plot.linearTickGenerator = defaultTickGenerator; + $.plot.defaultTickFormatter = defaultTickFormatter; + $.plot.expRepTickFormatter = expRepTickFormatter; })(jQuery); diff -r f3af77e371a1 -r 9f02c09d2393 src/pyams_skin/resources/js/ext/flot/jquery.flot.legend.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/pyams_skin/resources/js/ext/flot/jquery.flot.legend.js Wed Oct 16 13:00:43 2019 +0200 @@ -0,0 +1,437 @@ +/* Flot plugin for drawing legends. + +*/ + +(function($) { + var defaultOptions = { + legend: { + show: false, + noColumns: 1, + labelFormatter: null, // fn: string -> string + container: null, // container (as jQuery object) to put legend in, null means default on top of graph + position: 'ne', // position of default legend container within plot + margin: 5, // distance from grid edge to default legend container within plot + sorted: null // default to no legend sorting + } + }; + + function insertLegend(plot, options, placeholder, legendEntries) { + // clear before redraw + if (options.legend.container != null) { + $(options.legend.container).html(''); + } else { + placeholder.find('.legend').remove(); + } + + if (!options.legend.show) { + return; + } + + // Save the legend entries in legend options + var entries = options.legend.legendEntries = legendEntries, + plotOffset = options.legend.plotOffset = plot.getPlotOffset(), + html = [], + entry, labelHtml, iconHtml, + j = 0, + i, + pos = "", + p = options.legend.position, + m = options.legend.margin, + shape = { + name: '', + label: '', + xPos: '', + yPos: '' + }; + + html[j++] = ''; + html[j++] = ''; + html[j++] = svgShapeDefs; + + var left = 0; + var columnWidths = []; + var style = window.getComputedStyle(document.querySelector('body')); + for (i = 0; i < entries.length; ++i) { + var columnIndex = i % options.legend.noColumns; + entry = entries[i]; + shape.label = entry.label; + var info = plot.getSurface().getTextInfo('', shape.label, { + style: style.fontStyle, + variant: style.fontVariant, + weight: style.fontWeight, + size: parseInt(style.fontSize), + lineHeight: parseInt(style.lineHeight), + family: style.fontFamily + }); + + var labelWidth = info.width; + // 36px = 1.5em + 6px margin + var iconWidth = 48; + if (columnWidths[columnIndex]) { + if (labelWidth > columnWidths[columnIndex]) { + columnWidths[columnIndex] = labelWidth + iconWidth; + } + } else { + columnWidths[columnIndex] = labelWidth + iconWidth; + } + } + + // Generate html for icons and labels from a list of entries + for (i = 0; i < entries.length; ++i) { + var columnIndex = i % options.legend.noColumns; + entry = entries[i]; + iconHtml = ''; + shape.label = entry.label; + shape.xPos = (left + 3) + 'px'; + left += columnWidths[columnIndex]; + if ((i + 1) % options.legend.noColumns === 0) { + left = 0; + } + shape.yPos = Math.floor(i / options.legend.noColumns) * 1.5 + 'em'; + // area + if (entry.options.lines.show && entry.options.lines.fill) { + shape.name = 'area'; + shape.fillColor = entry.color; + iconHtml += getEntryIconHtml(shape); + } + // bars + if (entry.options.bars.show) { + shape.name = 'bar'; + shape.fillColor = entry.color; + iconHtml += getEntryIconHtml(shape); + } + // lines + if (entry.options.lines.show && !entry.options.lines.fill) { + shape.name = 'line'; + shape.strokeColor = entry.color; + shape.strokeWidth = entry.options.lines.lineWidth; + iconHtml += getEntryIconHtml(shape); + } + // points + if (entry.options.points.show) { + shape.name = entry.options.points.symbol; + shape.strokeColor = entry.color; + shape.fillColor = entry.options.points.fillColor; + shape.strokeWidth = entry.options.points.lineWidth; + iconHtml += getEntryIconHtml(shape); + } + + labelHtml = '' + shape.label + '' + html[j++] = '' + iconHtml + labelHtml + ''; + } + + html[j++] = ''; + 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 width = 6; + for (i = 0; i < columnWidths.length; ++i) { + width += columnWidths[i]; + } + + var legendEl, + height = Math.ceil(entries.length / options.legend.noColumns) * 1.6; + if (!options.legend.container) { + legendEl = $('
' + html.join('') + '
').appendTo(placeholder); + legendEl.css('width', width + 'px'); + legendEl.css('height', height + 'em'); + legendEl.css('pointerEvents', 'none'); + } else { + legendEl = $(html.join('')).appendTo(options.legend.container)[0]; + options.legend.container.style.width = width + 'px'; + options.legend.container.style.height = height + 'em'; + } + } + + // Generate html for a shape + function getEntryIconHtml(shape) { + var html = '', + name = shape.name, + x = shape.xPos, + y = shape.yPos, + fill = shape.fillColor, + stroke = shape.strokeColor, + width = shape.strokeWidth; + switch (name) { + case 'circle': + html = ''; + break; + case 'diamond': + html = ''; + break; + case 'cross': + html = ''; + break; + case 'rectangle': + html = ''; + break; + case 'plus': + html = ''; + break; + case 'bar': + html = ''; + break; + case 'area': + html = ''; + break; + case 'line': + html = ''; + break; + default: + // default is circle + html = ''; + } + + return html; + } + + // Define svg symbols for shapes + var svgShapeDefs = '' + + '' + + '' + + '' + + '' + + + '' + + '' + + '' + + + '' + + '' + + '' + + + '' + + '' + + '' + + '' + + '' + + '' + + + '' + + '' + + '' + + '' + + '' + + '' + + + '' + + '' + + '' + + '' + + '' + + '' + + + '' + + '' + + '' + + '' + + '' + + '' + + + '' + + '' + + '' + + '' + + '' + + '' + + ''; + + // Generate a list of legend entries in their final order + function getLegendEntries(series, labelFormatter, sorted) { + var lf = labelFormatter, + legendEntries = series.reduce(function(validEntries, s, i) { + var labelEval = (lf ? lf(s.label, s) : s.label) + if (s.hasOwnProperty("label") ? labelEval : true) { + var entry = { + label: labelEval || 'Plot ' + (i + 1), + color: s.color, + options: { + lines: s.lines, + points: s.points, + bars: s.bars + } + } + validEntries.push(entry) + } + return validEntries; + }, []); + + // Sort the legend using either the default or a custom comparator + if (sorted) { + if ($.isFunction(sorted)) { + legendEntries.sort(sorted); + } else if (sorted === 'reverse') { + legendEntries.reverse(); + } else { + var ascending = (sorted !== 'descending'); + legendEntries.sort(function(a, b) { + return a.label === b.label + ? 0 + : ((a.label < b.label) !== ascending ? 1 : -1 // Logical XOR + ); + }); + } + } + + return legendEntries; + } + + // return false if opts1 same as opts2 + function checkOptions(opts1, opts2) { + for (var prop in opts1) { + if (opts1.hasOwnProperty(prop)) { + if (opts1[prop] !== opts2[prop]) { + return true; + } + } + } + return false; + } + + // Compare two lists of legend entries + function shouldRedraw(oldEntries, newEntries) { + if (!oldEntries || !newEntries) { + return true; + } + + if (oldEntries.length !== newEntries.length) { + return true; + } + var i, newEntry, oldEntry, newOpts, oldOpts; + for (i = 0; i < newEntries.length; i++) { + newEntry = newEntries[i]; + oldEntry = oldEntries[i]; + + if (newEntry.label !== oldEntry.label) { + return true; + } + + if (newEntry.color !== oldEntry.color) { + return true; + } + + // check for changes in lines options + newOpts = newEntry.options.lines; + oldOpts = oldEntry.options.lines; + if (checkOptions(newOpts, oldOpts)) { + return true; + } + + // check for changes in points options + newOpts = newEntry.options.points; + oldOpts = oldEntry.options.points; + if (checkOptions(newOpts, oldOpts)) { + return true; + } + + // check for changes in bars options + newOpts = newEntry.options.bars; + oldOpts = oldEntry.options.bars; + if (checkOptions(newOpts, oldOpts)) { + return true; + } + } + + return false; + } + + function init(plot) { + plot.hooks.setupGrid.push(function (plot) { + var options = plot.getOptions(); + var series = plot.getData(), + labelFormatter = options.legend.labelFormatter, + oldEntries = options.legend.legendEntries, + oldPlotOffset = options.legend.plotOffset, + newEntries = getLegendEntries(series, labelFormatter, options.legend.sorted), + newPlotOffset = plot.getPlotOffset(); + + if (shouldRedraw(oldEntries, newEntries) || + checkOptions(oldPlotOffset, newPlotOffset)) { + insertLegend(plot, options, plot.getPlaceholder(), newEntries); + } + }); + } + + $.plot.plugins.push({ + init: init, + options: defaultOptions, + name: 'legend', + version: '1.0' + }); +})(jQuery); diff -r f3af77e371a1 -r 9f02c09d2393 src/pyams_skin/resources/js/ext/flot/jquery.flot.legend.min.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/pyams_skin/resources/js/ext/flot/jquery.flot.legend.min.js Wed Oct 16 13:00:43 2019 +0200 @@ -0,0 +1,1 @@ +!function(e){function t(t,l,n,i){if(null!=l.legend.container?e(l.legend.container).html(""):n.find(".legend").remove(),l.legend.show){var r,a,h,d,c=l.legend.legendEntries=i,g=l.legend.plotOffset=t.getPlotOffset(),p=[],f=0,m="",u=l.legend.position,b=l.legend.margin,y={name:"",label:"",xPos:"",yPos:""};p[f++]='',p[f++]='',p[f++]=s;var x=0,w=[],k=window.getComputedStyle(document.querySelector("body"));for(d=0;dw[L]&&(w[L]=v+48):w[L]=v+48}for(d=0;d'+y.label+"",p[f++]=""+h+a+""}p[f++]="",null==b[0]&&(b=[b,b]),"n"===u.charAt(0)?m+="top:"+(b[1]+g.top)+"px;":"s"===u.charAt(0)&&(m+="bottom:"+(b[1]+g.bottom)+"px;"),"e"===u.charAt(1)?m+="right:"+(b[0]+g.right)+"px;":"w"===u.charAt(1)&&(m+="left:"+(b[0]+g.left)+"px;");var M=6;for(d=0;d'+p.join("")+"").appendTo(n)).css("width",M+"px"),C.css("height",P+"em"),C.css("pointerEvents","none"))}}function o(e){var t="",o=e.name,l=e.xPos,n=e.yPos,i=e.fillColor,s=e.strokeColor,r=e.strokeWidth;switch(o){case"circle":t='';break;case"diamond":t='';break;case"cross":t='';break;case"rectangle":t='';break;case"plus":t='';break;case"bar":t='';break;case"area":t='';break;case"line":t='';break;default:t=''}return t}function l(t,o,l){var n=o,i=t.reduce(function(e,t,o){var l=n?n(t.label,t):t.label;if(!t.hasOwnProperty("label")||l){var i={label:l||"Plot "+(o+1),color:t.color,options:{lines:t.lines,points:t.points,bars:t.bars}};e.push(i)}return e},[]);if(l)if(e.isFunction(l))i.sort(l);else if("reverse"===l)i.reverse();else{var s="descending"!==l;i.sort(function(e,t){return e.label===t.label?0:e.label= min) { + minIdx = i; + return true; + } else { + return false; + } + }); + + PREFERRED_LOG_TICK_VALUES.some(function (val, i) { + if (val >= max) { + maxIdx = i; + return true; + } else { + return false; + } + }); + + if (maxIdx === -1) { + maxIdx = PREFERRED_LOG_TICK_VALUES.length - 1; + } + + if (maxIdx - minIdx <= noTicks / 4 && logTickValues.length !== EXTENDED_LOG_TICK_VALUES.length) { + //try with multiple of 5 for tick values + logTickValues = EXTENDED_LOG_TICK_VALUES; + minIdx *= 2; + maxIdx *= 2; + } + + var lastDisplayed = null, + inverseNoTicks = 1 / noTicks, + tickValue, pixelCoord, tick; + + // Count the number of tick values would appear, if we can get at least + // nTicks / 4 accept them. + if (maxIdx - minIdx >= noTicks / 4) { + for (var idx = maxIdx; idx >= minIdx; idx--) { + tickValue = logTickValues[idx]; + pixelCoord = (Math.log(tickValue) - Math.log(min)) / (Math.log(max) - Math.log(min)); + tick = tickValue; + + if (lastDisplayed === null) { + lastDisplayed = { + pixelCoord: pixelCoord, + idealPixelCoord: pixelCoord + }; + } else { + if (Math.abs(pixelCoord - lastDisplayed.pixelCoord) >= inverseNoTicks) { + lastDisplayed = { + pixelCoord: pixelCoord, + idealPixelCoord: lastDisplayed.idealPixelCoord - inverseNoTicks + }; + } else { + tick = null; + } + } + + if (tick) { + ticks.push(tick); + } + } + // Since we went in backwards order. + ticks.reverse(); + } else { + var tickSize = plot.computeTickSize(min, max, noTicks), + customAxis = {min: min, max: max, tickSize: tickSize}; + ticks = $.plot.linearTickGenerator(customAxis); + } + + return ticks; + }; + + var clampAxis = function (axis, plot) { + var min = axis.min, + max = axis.max; + + if (min <= 0) { + //for empty graph if axis.min is not strictly positive make it 0.1 + if (axis.datamin === null) { + min = axis.min = 0.1; + } else { + min = processAxisOffset(plot, axis); + } + + if (max < min) { + axis.max = axis.datamax !== null ? axis.datamax : axis.options.max; + axis.options.offset.below = 0; + axis.options.offset.above = 0; + } + } + + return min; + } + + /** + - logTickFormatter(value, axis, precision) + + This is the corresponding tickFormatter of the logaxis. + For a number greater that 10^6 or smaller than 10^(-3), this will be drawn + with e representation + */ + var logTickFormatter = function (value, axis, precision) { + var tenExponent = value > 0 ? Math.floor(Math.log(value) / Math.LN10) : 0; + + if (precision) { + if ((tenExponent >= -4) && (tenExponent <= 7)) { + return $.plot.defaultTickFormatter(value, axis, precision); + } else { + return $.plot.expRepTickFormatter(value, axis, precision); + } + } + if ((tenExponent >= -4) && (tenExponent <= 7)) { + //if we have float numbers, return a limited length string(ex: 0.0009 is represented as 0.000900001) + var formattedValue = tenExponent < 0 ? value.toFixed(-tenExponent) : value.toFixed(tenExponent + 2); + if (formattedValue.indexOf('.') !== -1) { + var lastZero = formattedValue.lastIndexOf('0'); + + while (lastZero === formattedValue.length - 1) { + formattedValue = formattedValue.slice(0, -1); + lastZero = formattedValue.lastIndexOf('0'); + } + + //delete the dot if is last + if (formattedValue.indexOf('.') === formattedValue.length - 1) { + formattedValue = formattedValue.slice(0, -1); + } + } + return formattedValue; + } else { + return $.plot.expRepTickFormatter(value, axis); + } + }; + + /*logaxis caracteristic functions*/ + var logTransform = function (v) { + if (v < PREFERRED_LOG_TICK_VALUES[0]) { + v = PREFERRED_LOG_TICK_VALUES[0]; + } + + return Math.log(v); + }; + + var logInverseTransform = function (v) { + return Math.exp(v); + }; + + var invertedTransform = function (v) { + return -v; + } + + var invertedLogTransform = function (v) { + return -logTransform(v); + } + + var invertedLogInverseTransform = function (v) { + return logInverseTransform(-v); + } + + /** + - setDataminRange(plot, axis) + + It is used for clamping the starting point of a logarithmic axis. + This will set the axis datamin range to 0.1 or to the first datapoint greater then 0. + The function is usefull since the logarithmic representation can not show + values less than or equal to 0. + */ + function setDataminRange(plot, axis) { + if (axis.options.mode === 'log' && axis.datamin <= 0) { + if (axis.datamin === null) { + axis.datamin = 0.1; + } else { + axis.datamin = processAxisOffset(plot, axis); + } + } + } + + function processAxisOffset(plot, axis) { + var series = plot.getData(), + range = series + .filter(function(series) { + return series.xaxis === axis || series.yaxis === axis; + }) + .map(function(series) { + return plot.computeRangeForDataSeries(series, null, isValid); + }), + min = axis.direction === 'x' + ? Math.min(0.1, range && range[0] ? range[0].xmin : 0.1) + : Math.min(0.1, range && range[0] ? range[0].ymin : 0.1); + + axis.min = min; + + return min; + } + + function isValid(a) { + return a > 0; + } + + function init(plot) { + plot.hooks.processOptions.push(function (plot) { + $.each(plot.getAxes(), function (axisName, axis) { + var opts = axis.options; + if (opts.mode === 'log') { + axis.tickGenerator = function (axis) { + var noTicks = 11; + return logTickGenerator(plot, axis, noTicks); + }; + if (typeof axis.options.tickFormatter !== 'function') { + axis.options.tickFormatter = logTickFormatter; + } + axis.options.transform = opts.inverted ? invertedLogTransform : logTransform; + axis.options.inverseTransform = opts.inverted ? invertedLogInverseTransform : logInverseTransform; + axis.options.autoScaleMargin = 0; + plot.hooks.setRange.push(setDataminRange); + } else if (opts.inverted) { + axis.options.transform = invertedTransform; + axis.options.inverseTransform = invertedTransform; + } + }); + }); + } + + $.plot.plugins.push({ + init: init, + options: options, + name: 'log', + version: '0.1' + }); + + $.plot.logTicksGenerator = logTickGenerator; + $.plot.logTickFormatter = logTickFormatter; +})(jQuery); diff -r f3af77e371a1 -r 9f02c09d2393 src/pyams_skin/resources/js/ext/flot/jquery.flot.logaxis.min.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/pyams_skin/resources/js/ext/flot/jquery.flot.logaxis.min.js Wed Oct 16 13:00:43 2019 +0200 @@ -0,0 +1,1 @@ +!function(t){"use strict";function n(t,n){for(var o,e,r=Math.floor(Math.log(t)*Math.LOG10E)-1,i=[],a=-r;a<=r;a++){e=parseFloat("1e"+a);for(var l=1;l<9;l+=n)o=e*l,i.push(o)}return i}function o(t,n){"log"===n.options.mode&&n.datamin<=0&&(null===n.datamin?n.datamin=.1:n.datamin=e(t,n))}function e(t,n){var o=t.getData().filter(function(t){return t.xaxis===n||t.yaxis===n}).map(function(n){return t.computeRangeForDataSeries(n,null,r)}),e="x"===n.direction?Math.min(.1,o&&o[0]?o[0].xmin:.1):Math.min(.1,o&&o[0]?o[0].ymin:.1);return n.min=e,e}function r(t){return t>0}var i=n(Number.MAX_VALUE,10),a=n(Number.MAX_VALUE,4),l=function(n,o,e){var r=[],l=-1,s=-1,f=n.getCanvas(),c=i,p=u(o,n),m=o.max;e||(e=.3*Math.sqrt("x"===o.direction?f.width:f.height)),i.some(function(t,n){return t>=p&&(l=n,!0)}),i.some(function(t,n){return t>=m&&(s=n,!0)}),-1===s&&(s=i.length-1),s-l<=e/4&&c.length!==a.length&&(c=a,l*=2,s*=2);var h,d,x,g=null,v=1/e;if(s-l>=e/4){for(var M=s;M>=l;M--)h=c[M],d=(Math.log(h)-Math.log(p))/(Math.log(m)-Math.log(p)),x=h,null===g?g={pixelCoord:d,idealPixelCoord:d}:Math.abs(d-g.pixelCoord)>=v?g={pixelCoord:d,idealPixelCoord:g.idealPixelCoord-v}:x=null,x&&r.push(x);r.reverse()}else{var k=n.computeTickSize(p,m,e),F={min:p,max:m,tickSize:k};r=t.plot.linearTickGenerator(F)}return r},u=function(t,n){var o=t.min,r=t.max;return o<=0&&r<(o=null===t.datamin?t.min=.1:e(n,t))&&(t.max=null!==t.datamax?t.datamax:t.options.max,t.options.offset.below=0,t.options.offset.above=0),o},s=function(n,o,e){var r=n>0?Math.floor(Math.log(n)/Math.LN10):0;if(e)return r>=-4&&r<=7?t.plot.defaultTickFormatter(n,o,e):t.plot.expRepTickFormatter(n,o,e);if(r>=-4&&r<=7){var i=r<0?n.toFixed(-r):n.toFixed(r+2);if(-1!==i.indexOf(".")){for(var a=i.lastIndexOf("0");a===i.length-1;)a=(i=i.slice(0,-1)).lastIndexOf("0");i.indexOf(".")===i.length-1&&(i=i.slice(0,-1))}return i}return t.plot.expRepTickFormatter(n,o)},f=function(t){return t=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 valuemax?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=$("
").css({position:"absolute",top:0,left:0,bottom:0,right:0,"font-size":"smaller",color:"#545454"}).insertAfter(this.element)}layer=this.text[classes]=$("
").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=$("
").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;imaxIndex){maxIndex=sc}}}if(neededColors<=maxIndex){neededColors=maxIndex+1}var c,colors=[],colorPool=options.colors,colorPoolSize=colorPool.length,variation=0;for(i=0;i=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;iaxis.datamax&&max!=fakeInfinity)axis.datamax=max}$.each(allAxes(),function(_,axis){axis.datamin=topSentry;axis.datamax=bottomSentry;axis.used=false});for(i=0;i0&&points[k-ps]!=null&&points[k-ps]!=points[k]&&points[k-ps+1]!=points[k+1]){for(m=0;mxmax)xmax=val}if(f.y){if(valymax)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=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&&size0){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;i1&&/\..*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;i1)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;ito){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;ixrange.axis.max||yrange.toyrange.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;jaxis.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;iaxis.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=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=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=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){x1=(axisy.min-y1)/(y2-y1)*(x2-x1)+x1;y1=axisy.min}else if(y2<=y1&&y2=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;iaxisx.max||yaxisy.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(rightaxisx.max||topaxisy.max)return;if(leftaxisx.max){right=axisx.max;drawRight=false}if(bottomaxisy.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");fragments.push("");rowStarted=true}fragments.push('
'+''+entry.label+"")}if(rowStarted)fragments.push("");if(fragments.length==0)return;var table=''+fragments.join("")+"
";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=$('
'+table.replace('style="','style="position:absolute;'+pos+";")+"
").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();$('
').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;jmaxx||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=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;iaxisx.max||yaxisy.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;i0&&h.which!=l.which||a(h.target).is(l.not))return;switch(h.type){case"mousedown":return a.extend(l,a(j).offset(),{elem:j,target:h.target,pageX:h.pageX,pageY:h.pageY}),b.add(document,"mousemove mouseup",e,l),i(j,!1),d.dragging=null,!1;case!d.dragging&&"mousemove":if(g(h.pageX-l.pageX)+g(h.pageY-l.pageY) box.left) && (ec.left < box.left + box.width) && + (ec.top > box.top) && (ec.top < box.top + box.height); + } + }); + + if (axes.length === 0) { + axes = undefined; + } + + if (zoomOut) { + plot.zoomOut({ + center: c, + axes: axes, + amount: amount + }); + } else { + plot.zoom({ + center: c, + axes: axes, + amount: amount + }); + } } + var prevCursor = 'default', + panHint = null, + panTimeout = null, + plotState, + prevDragPosition = { x: 0, y: 0 }, + isPanAction = false; + function onMouseWheel(e, delta) { - e.preventDefault(); - onZoomClick(e, delta < 0); - return false; + var maxAbsoluteDeltaOnMac = 1, + isMacScroll = Math.abs(e.originalEvent.deltaY) <= maxAbsoluteDeltaOnMac, + defaultNonMacScrollAmount = null, + macMagicRatio = 50, + amount = isMacScroll ? 1 + Math.abs(e.originalEvent.deltaY) / macMagicRatio : defaultNonMacScrollAmount; + + if (isPanAction) { + onDragEnd(e); + } + + if (plot.getOptions().zoom.active) { + e.preventDefault(); + onZoomClick(e, delta < 0, amount); + return false; + } } - - var prevCursor = 'default', prevPageX = 0, prevPageY = 0, - panTimeout = null; + + plot.navigationState = function(startPageX, startPageY) { + var axes = this.getAxes(); + var result = {}; + Object.keys(axes).forEach(function(axisName) { + var axis = axes[axisName]; + result[axisName] = { + navigationOffset: { below: axis.options.offset.below || 0, + above: axis.options.offset.above || 0}, + axisMin: axis.min, + axisMax: axis.max, + diagMode: false + } + }); + + result.startPageX = startPageX || 0; + result.startPageY = startPageY || 0; + return result; + } + + function onMouseDown(e) { + canDrag = true; + } + + function onMouseUp(e) { + canDrag = false; + } + + function isLeftMouseButtonPressed(e) { + return e.button === 0; + } function onDragStart(e) { - if (e.which != 1) // only accept left-click + if (!canDrag || !isLeftMouseButtonPressed(e)) { return false; + } + + isPanAction = true; + var page = browser.getPageXY(e); + + var ec = plot.getPlaceholder().offset(); + ec.left = page.X - ec.left; + ec.top = page.Y - ec.top; + + panAxes = plot.getXAxes().concat(plot.getYAxes()).filter(function (axis) { + var box = axis.box; + if (box !== undefined) { + return (ec.left > box.left) && (ec.left < box.left + box.width) && + (ec.top > box.top) && (ec.top < box.top + box.height); + } + }); + + if (panAxes.length === 0) { + panAxes = undefined; + } + var c = plot.getPlaceholder().css('cursor'); - if (c) + if (c) { prevCursor = c; + } + plot.getPlaceholder().css('cursor', plot.getOptions().pan.cursor); - prevPageX = e.pageX; - prevPageY = e.pageY; + + if (useSmartPan) { + plotState = plot.navigationState(page.X, page.Y); + } else if (useManualPan) { + prevDragPosition.x = page.X; + prevDragPosition.y = page.Y; + } } function onDrag(e) { + if (!isPanAction) { + return; + } + + var page = browser.getPageXY(e); var frameRate = plot.getOptions().pan.frameRate; - if (panTimeout || !frameRate) - return; - panTimeout = setTimeout(function () { - plot.pan({ left: prevPageX - e.pageX, - top: prevPageY - e.pageY }); - prevPageX = e.pageX; - prevPageY = e.pageY; - + if (frameRate === -1) { + if (useSmartPan) { + plot.smartPan({ + x: plotState.startPageX - page.X, + y: plotState.startPageY - page.Y + }, plotState, panAxes, false, smartPanLock); + } else if (useManualPan) { + plot.pan({ + left: prevDragPosition.x - page.X, + top: prevDragPosition.y - page.Y, + axes: panAxes + }); + prevDragPosition.x = page.X; + prevDragPosition.y = page.Y; + } + return; + } + + if (panTimeout || !frameRate) return; + + panTimeout = setTimeout(function() { + if (useSmartPan) { + plot.smartPan({ + x: plotState.startPageX - page.X, + y: plotState.startPageY - page.Y + }, plotState, panAxes, false, smartPanLock); + } else if (useManualPan) { + plot.pan({ + left: prevDragPosition.x - page.X, + top: prevDragPosition.y - page.Y, + axes: panAxes + }); + prevDragPosition.x = page.X; + prevDragPosition.y = page.Y; + } + panTimeout = null; }, 1 / frameRate * 1000); } function onDragEnd(e) { + if (!isPanAction) { + return; + } + if (panTimeout) { clearTimeout(panTimeout); panTimeout = null; } - + + isPanAction = false; + var page = browser.getPageXY(e); + plot.getPlaceholder().css('cursor', prevCursor); - plot.pan({ left: prevPageX - e.pageX, - top: prevPageY - e.pageY }); + + if (useSmartPan) { + plot.smartPan({ + x: plotState.startPageX - page.X, + y: plotState.startPageY - page.Y + }, plotState, panAxes, false, smartPanLock); + plot.smartPan.end(); + } else if (useManualPan) { + plot.pan({ + left: prevDragPosition.x - page.X, + top: prevDragPosition.y - page.Y, + axes: panAxes + }); + prevDragPosition.x = 0; + prevDragPosition.y = 0; + } } - + + function onDblClick(e) { + plot.activate(); + var o = plot.getOptions() + + if (!o.recenter.interactive) { return; } + + var axes = plot.getTouchedAxis(e.clientX, e.clientY), + event; + + plot.recenter({ axes: axes[0] ? axes : null }); + + if (axes[0]) { + event = new $.Event('re-center', { detail: { + axisTouched: axes[0] + }}); + } else { + event = new $.Event('re-center', { detail: e }); + } + plot.getPlaceholder().trigger(event); + } + + function onClick(e) { + plot.activate(); + + if (isPanAction) { + onDragEnd(e); + } + + return false; + } + + plot.activate = function() { + var o = plot.getOptions(); + if (!o.pan.active || !o.zoom.active) { + o.pan.active = true; + o.zoom.active = true; + plot.getPlaceholder().trigger("plotactivated", [plot]); + } + } + function bindEvents(plot, eventHolder) { var o = plot.getOptions(); if (o.zoom.interactive) { - eventHolder[o.zoom.trigger](onZoomClick); eventHolder.mousewheel(onMouseWheel); } if (o.pan.interactive) { - eventHolder.bind("dragstart", { distance: 10 }, onDragStart); - eventHolder.bind("drag", onDrag); - eventHolder.bind("dragend", onDragEnd); + plot.addEventHandler("dragstart", onDragStart, eventHolder, 0); + plot.addEventHandler("drag", onDrag, eventHolder, 0); + plot.addEventHandler("dragend", onDragEnd, eventHolder, 0); + eventHolder.bind("mousedown", onMouseDown); + eventHolder.bind("mouseup", onMouseUp); } + + eventHolder.dblclick(onDblClick); + eventHolder.click(onClick); } - plot.zoomOut = function (args) { - if (!args) + plot.zoomOut = function(args) { + if (!args) { args = {}; - - if (!args.amount) + } + + if (!args.amount) { args.amount = plot.getOptions().zoom.amount; + } args.amount = 1 / args.amount; plot.zoom(args); }; - - plot.zoom = function (args) { - if (!args) + + plot.zoom = function(args) { + if (!args) { args = {}; - + } + var c = args.center, amount = args.amount || plot.getOptions().zoom.amount, - w = plot.width(), h = plot.height(); + w = plot.width(), + h = plot.height(), + axes = args.axes || plot.getAxes(); - if (!c) - c = { left: w / 2, top: h / 2 }; - + if (!c) { + c = { + left: w / 2, + top: h / 2 + }; + } + var xf = c.left / w, yf = c.top / h, minmax = { @@ -229,18 +465,24 @@ } }; - $.each(plot.getAxes(), function(_, axis) { - var opts = axis.options, + for (var key in axes) { + if (!axes.hasOwnProperty(key)) { + continue; + } + + var axis = axes[key], + opts = axis.options, min = minmax[axis.direction].min, max = minmax[axis.direction].max, - zr = opts.zoomRange, - pr = opts.panRange; + navigationOffset = axis.options.offset; - if (zr === false) // no zooming on this axis - return; - - min = axis.c2p(min); - max = axis.c2p(max); + //skip axis without axisZoom when zooming only on certain axis or axis without plotZoom for zoom on entire plot + if ((!opts.axisZoom && args.axes) || (!args.axes && !opts.plotZoom)) { + continue; + } + + min = $.plot.saturated.saturate(axis.c2p(min)); + max = $.plot.saturated.saturate(axis.c2p(max)); if (min > max) { // make sure min < max var tmp = min; @@ -248,95 +490,305 @@ max = tmp; } - //Check that we are in panRange - if (pr) { - if (pr[0] != null && min < pr[0]) { - min = pr[0]; - } - if (pr[1] != null && max > pr[1]) { - max = pr[1]; - } - } + var offsetBelow = $.plot.saturated.saturate(navigationOffset.below - (axis.min - min)); + var offsetAbove = $.plot.saturated.saturate(navigationOffset.above - (axis.max - max)); + opts.offset = { below: offsetBelow, above: offsetAbove }; + }; - var range = max - min; - if (zr && - ((zr[0] != null && range < zr[0] && amount >1) || - (zr[1] != null && range > zr[1] && amount <1))) - return; - - opts.min = min; - opts.max = max; - }); - - plot.setupGrid(); + plot.setupGrid(true); plot.draw(); - - if (!args.preventEvent) - plot.getPlaceholder().trigger("plotzoom", [ plot, args ]); + + if (!args.preventEvent) { + plot.getPlaceholder().trigger("plotzoom", [plot, args]); + } }; - plot.pan = function (args) { + plot.pan = function(args) { var delta = { x: +args.left, y: +args.top }; - if (isNaN(delta.x)) - delta.x = 0; - if (isNaN(delta.y)) - delta.y = 0; + if (isNaN(delta.x)) delta.x = 0; + if (isNaN(delta.y)) delta.y = 0; + + $.each(args.axes || plot.getAxes(), function(_, axis) { + var opts = axis.options, + d = delta[axis.direction]; + + //skip axis without axisPan when panning only on certain axis or axis without plotPan for pan the entire plot + if ((!opts.axisPan && args.axes) || (!opts.plotPan && !args.axes)) { + return; + } + + if (d !== 0) { + var navigationOffsetBelow = saturated.saturate(axis.c2p(axis.p2c(axis.min) + d) - axis.c2p(axis.p2c(axis.min))), + navigationOffsetAbove = saturated.saturate(axis.c2p(axis.p2c(axis.max) + d) - axis.c2p(axis.p2c(axis.max))); + + if (!isFinite(navigationOffsetBelow)) { + navigationOffsetBelow = 0; + } + + if (!isFinite(navigationOffsetAbove)) { + navigationOffsetAbove = 0; + } + + opts.offset = { + below: saturated.saturate(navigationOffsetBelow + (opts.offset.below || 0)), + above: saturated.saturate(navigationOffsetAbove + (opts.offset.above || 0)) + }; + } + }); - $.each(plot.getAxes(), function (_, axis) { - var opts = axis.options, - min, max, d = delta[axis.direction]; + plot.setupGrid(true); + plot.draw(); + if (!args.preventEvent) { + plot.getPlaceholder().trigger("plotpan", [plot, args]); + } + }; - min = axis.c2p(axis.p2c(axis.min) + d), - max = axis.c2p(axis.p2c(axis.max) + d); + plot.recenter = function(args) { + $.each(args.axes || plot.getAxes(), function(_, axis) { + if (args.axes) { + if (this.direction === 'x') { + axis.options.offset = { below: 0 }; + } else if (this.direction === 'y') { + axis.options.offset = { above: 0 }; + } + } else { + axis.options.offset = { below: 0, above: 0 }; + } + }); + plot.setupGrid(true); + plot.draw(); + }; + + var shouldSnap = function(delta) { + return (Math.abs(delta.y) < SNAPPING_CONSTANT && Math.abs(delta.x) >= SNAPPING_CONSTANT) || + (Math.abs(delta.x) < SNAPPING_CONSTANT && Math.abs(delta.y) >= SNAPPING_CONSTANT); + } + + // adjust delta so the pan action is constrained on the vertical or horizontal direction + // it the movements in the other direction are small + var adjustDeltaToSnap = function(delta) { + if (Math.abs(delta.x) < SNAPPING_CONSTANT && Math.abs(delta.y) >= SNAPPING_CONSTANT) { + return {x: 0, y: delta.y}; + } - var pr = opts.panRange; - if (pr === false) // no panning on this axis - return; - - if (pr) { - // check whether we hit the wall - if (pr[0] != null && pr[0] > min) { - d = pr[0] - min; - min += d; - max += d; - } - - if (pr[1] != null && pr[1] < max) { - d = pr[1] - max; - min += d; - max += d; + if (Math.abs(delta.y) < SNAPPING_CONSTANT && Math.abs(delta.x) >= SNAPPING_CONSTANT) { + return {x: delta.x, y: 0}; + } + + return delta; + } + + var lockedDirection = null; + var lockDeltaDirection = function(delta) { + if (!lockedDirection && Math.max(Math.abs(delta.x), Math.abs(delta.y)) >= SNAPPING_CONSTANT) { + lockedDirection = Math.abs(delta.x) < Math.abs(delta.y) ? 'y' : 'x'; + } + + switch (lockedDirection) { + case 'x': + return { x: delta.x, y: 0 }; + case 'y': + return { x: 0, y: delta.y }; + default: + return { x: 0, y: 0 }; + } + } + + var isDiagonalMode = function(delta) { + if (Math.abs(delta.x) > 0 && Math.abs(delta.y) > 0) { + return true; + } + return false; + } + + var restoreAxisOffset = function(axes, initialState, delta) { + var axis; + Object.keys(axes).forEach(function(axisName) { + axis = axes[axisName]; + if (delta[axis.direction] === 0) { + axis.options.offset.below = initialState[axisName].navigationOffset.below; + axis.options.offset.above = initialState[axisName].navigationOffset.above; + } + }); + } + + var prevDelta = { x: 0, y: 0 }; + plot.smartPan = function(delta, initialState, panAxes, preventEvent, smartLock) { + var snap = smartLock ? true : shouldSnap(delta), + axes = plot.getAxes(), + opts; + delta = smartLock ? lockDeltaDirection(delta) : adjustDeltaToSnap(delta); + + if (isDiagonalMode(delta)) { + initialState.diagMode = true; + } + + if (snap && initialState.diagMode === true) { + initialState.diagMode = false; + restoreAxisOffset(axes, initialState, delta); + } + + if (snap) { + panHint = { + start: { + x: initialState.startPageX - plot.offset().left + plot.getPlotOffset().left, + y: initialState.startPageY - plot.offset().top + plot.getPlotOffset().top + }, + end: { + x: initialState.startPageX - delta.x - plot.offset().left + plot.getPlotOffset().left, + y: initialState.startPageY - delta.y - plot.offset().top + plot.getPlotOffset().top } } - - opts.min = min; - opts.max = max; + } else { + panHint = { + start: { + x: initialState.startPageX - plot.offset().left + plot.getPlotOffset().left, + y: initialState.startPageY - plot.offset().top + plot.getPlotOffset().top + }, + end: false + } + } + + if (isNaN(delta.x)) delta.x = 0; + if (isNaN(delta.y)) delta.y = 0; + + if (panAxes) { + axes = panAxes; + } + + var axis, axisMin, axisMax, p, d; + Object.keys(axes).forEach(function(axisName) { + axis = axes[axisName]; + axisMin = axis.min; + axisMax = axis.max; + opts = axis.options; + + d = delta[axis.direction]; + p = prevDelta[axis.direction]; + + //skip axis without axisPan when panning only on certain axis or axis without plotPan for pan the entire plot + if ((!opts.axisPan && panAxes) || (!panAxes && !opts.plotPan)) { + return; + } + + if (d !== 0) { + var navigationOffsetBelow = saturated.saturate(axis.c2p(axis.p2c(axisMin) - (p - d)) - axis.c2p(axis.p2c(axisMin))), + navigationOffsetAbove = saturated.saturate(axis.c2p(axis.p2c(axisMax) - (p - d)) - axis.c2p(axis.p2c(axisMax))); + + if (!isFinite(navigationOffsetBelow)) { + navigationOffsetBelow = 0; + } + + if (!isFinite(navigationOffsetAbove)) { + navigationOffsetAbove = 0; + } + + axis.options.offset.below = saturated.saturate(navigationOffsetBelow + (axis.options.offset.below || 0)); + axis.options.offset.above = saturated.saturate(navigationOffsetAbove + (axis.options.offset.above || 0)); + } }); - - plot.setupGrid(); + + prevDelta = delta; + plot.setupGrid(true); plot.draw(); - - if (!args.preventEvent) - plot.getPlaceholder().trigger("plotpan", [ plot, args ]); + + if (!preventEvent) { + plot.getPlaceholder().trigger("plotpan", [plot, delta, panAxes, initialState]); + } }; + plot.smartPan.end = function() { + panHint = null; + lockedDirection = null; + prevDelta = { x: 0, y: 0 }; + plot.triggerRedrawOverlay(); + } + function shutdown(plot, eventHolder) { - eventHolder.unbind(plot.getOptions().zoom.trigger, onZoomClick); eventHolder.unbind("mousewheel", onMouseWheel); + eventHolder.unbind("mousedown", onMouseDown); + eventHolder.unbind("mouseup", onMouseUp); eventHolder.unbind("dragstart", onDragStart); eventHolder.unbind("drag", onDrag); eventHolder.unbind("dragend", onDragEnd); - if (panTimeout) - clearTimeout(panTimeout); + eventHolder.unbind("dblclick", onDblClick); + eventHolder.unbind("click", onClick); + + if (panTimeout) clearTimeout(panTimeout); } - + + function drawOverlay(plot, ctx) { + if (panHint) { + ctx.strokeStyle = 'rgba(96, 160, 208, 0.7)'; + ctx.lineWidth = 2; + ctx.lineJoin = "round"; + var startx = Math.round(panHint.start.x), + starty = Math.round(panHint.start.y), + endx, endy; + + if (panAxes) { + if (panAxes[0].direction === 'x') { + endy = Math.round(panHint.start.y); + endx = Math.round(panHint.end.x); + } else if (panAxes[0].direction === 'y') { + endx = Math.round(panHint.start.x); + endy = Math.round(panHint.end.y); + } + } else { + endx = Math.round(panHint.end.x); + endy = Math.round(panHint.end.y); + } + + ctx.beginPath(); + + if (panHint.end === false) { + ctx.moveTo(startx, starty - PANHINT_LENGTH_CONSTANT); + ctx.lineTo(startx, starty + PANHINT_LENGTH_CONSTANT); + + ctx.moveTo(startx + PANHINT_LENGTH_CONSTANT, starty); + ctx.lineTo(startx - PANHINT_LENGTH_CONSTANT, starty); + } else { + var dirX = starty === endy; + + ctx.moveTo(startx - (dirX ? 0 : PANHINT_LENGTH_CONSTANT), starty - (dirX ? PANHINT_LENGTH_CONSTANT : 0)); + ctx.lineTo(startx + (dirX ? 0 : PANHINT_LENGTH_CONSTANT), starty + (dirX ? PANHINT_LENGTH_CONSTANT : 0)); + + ctx.moveTo(startx, starty); + ctx.lineTo(endx, endy); + + ctx.moveTo(endx - (dirX ? 0 : PANHINT_LENGTH_CONSTANT), endy - (dirX ? PANHINT_LENGTH_CONSTANT : 0)); + ctx.lineTo(endx + (dirX ? 0 : PANHINT_LENGTH_CONSTANT), endy + (dirX ? PANHINT_LENGTH_CONSTANT : 0)); + } + + ctx.stroke(); + } + } + + plot.getTouchedAxis = function(touchPointX, touchPointY) { + var ec = plot.getPlaceholder().offset(); + ec.left = touchPointX - ec.left; + ec.top = touchPointY - ec.top; + + var axis = plot.getXAxes().concat(plot.getYAxes()).filter(function (axis) { + var box = axis.box; + if (box !== undefined) { + return (ec.left > box.left) && (ec.left < box.left + box.width) && + (ec.top > box.top) && (ec.top < box.top + box.height); + } + }); + + return axis; + } + + plot.hooks.drawOverlay.push(drawOverlay); plot.hooks.bindEvents.push(bindEvents); plot.hooks.shutdown.push(shutdown); } - + $.plot.plugins.push({ init: init, options: options, diff -r f3af77e371a1 -r 9f02c09d2393 src/pyams_skin/resources/js/ext/flot/jquery.flot.navigate.min.js --- a/src/pyams_skin/resources/js/ext/flot/jquery.flot.navigate.min.js Thu Sep 26 11:55:17 2019 +0200 +++ b/src/pyams_skin/resources/js/ext/flot/jquery.flot.navigate.min.js Wed Oct 16 13:00:43 2019 +0200 @@ -1,1 +1,1 @@ -!function(e){function t(a){var g,d=this,l=a.data||{};if(l.elem)d=a.dragTarget=l.elem,a.dragProxy=u.proxy||d,a.cursorOffsetX=l.pageX-l.left,a.cursorOffsetY=l.pageY-l.top,a.offsetX=a.pageX-a.cursorOffsetX,a.offsetY=a.pageY-a.cursorOffsetY;else if(u.dragging||l.which>0&&a.which!=l.which||e(a.target).is(l.not))return;switch(a.type){case"mousedown":return e.extend(l,e(d).offset(),{elem:d,target:a.target,pageX:a.pageX,pageY:a.pageY}),i.add(document,"mousemove mouseup",t,l),r(d,!1),u.dragging=null,!1;case!u.dragging&&"mousemove":if(o(a.pageX-l.pageX)+o(a.pageY-l.pageY)r){var u=o;o=r,r=u}s&&(null!=s[0]&&os[1]&&(r=s[1]));var d=r-o;i&&(null!=i[0]&&d1||null!=i[1]&&d>i[1]&&a<1)||(n.min=o,n.max=r)}}),t.setupGrid(),t.draw(),n.preventEvent||t.getPlaceholder().trigger("plotzoom",[t,n])},t.pan=function(n){var o={x:+n.left,y:+n.top};isNaN(o.x)&&(o.x=0),isNaN(o.y)&&(o.y=0),e.each(t.getAxes(),function(e,t){var n,a,r=t.options,i=o[t.direction];n=t.c2p(t.p2c(t.min)+i),a=t.c2p(t.p2c(t.max)+i);var s=r.panRange;!1!==s&&(s&&(null!=s[0]&&s[0]>n&&(n+=i=s[0]-n,a+=i),null!=s[1]&&s[1]e.left&&s.lefte.top&&s.tope.left&&n.lefte.top&&n.topx){var m=g;g=x,x=m}var h=t.plot.saturated.saturate(v.below-(p.min-g)),b=t.plot.saturated.saturate(v.above-(p.max-x));d.offset={below:h,above:b}}}e.setupGrid(!0),e.draw(),a.preventEvent||e.getPlaceholder().trigger("plotzoom",[e,a])},e.pan=function(o){var n={x:+o.left,y:+o.top};isNaN(n.x)&&(n.x=0),isNaN(n.y)&&(n.y=0),t.each(o.axes||e.getAxes(),function(t,e){var i=e.options,r=n[e.direction];if((i.axisPan||!o.axes)&&(i.plotPan||o.axes)&&0!==r){var s=a.saturate(e.c2p(e.p2c(e.min)+r)-e.c2p(e.p2c(e.min))),f=a.saturate(e.c2p(e.p2c(e.max)+r)-e.c2p(e.p2c(e.max)));isFinite(s)||(s=0),isFinite(f)||(f=0),i.offset={below:a.saturate(s+(i.offset.below||0)),above:a.saturate(f+(i.offset.above||0))}}}),e.setupGrid(!0),e.draw(),o.preventEvent||e.getPlaceholder().trigger("plotpan",[e,o])},e.recenter=function(a){t.each(a.axes||e.getAxes(),function(t,e){a.axes?"x"===this.direction?e.options.offset={below:0}:"y"===this.direction&&(e.options.offset={above:0}):e.options.offset={below:0,above:0}}),e.setupGrid(!0),e.draw()};var A=function(t){return Math.abs(t.y)=n||Math.abs(t.x)=n},N=function(t){return Math.abs(t.x)=n?{x:0,y:t.y}:Math.abs(t.y)=n?{x:t.x,y:0}:t},k=null,E=function(t){switch(!k&&Math.max(Math.abs(t.x),Math.abs(t.y))>=n&&(k=Math.abs(t.x)0&&Math.abs(t.y)>0},G=function(t,e,a){var o;Object.keys(t).forEach(function(n){o=t[n],0===a[o.direction]&&(o.options.offset.below=e[n].navigationOffset.below,o.options.offset.above=e[n].navigationOffset.above)})},S={x:0,y:0};e.smartPan=function(t,o,n,i,r){var s,f=!!r||A(t),l=e.getAxes();t=r?E(t):N(t),z(t)&&(o.diagMode=!0),f&&!0===o.diagMode&&(o.diagMode=!1,G(l,o,t)),Y=f?{start:{x:o.startPageX-e.offset().left+e.getPlotOffset().left,y:o.startPageY-e.offset().top+e.getPlotOffset().top},end:{x:o.startPageX-t.x-e.offset().left+e.getPlotOffset().left,y:o.startPageY-t.y-e.offset().top+e.getPlotOffset().top}}:{start:{x:o.startPageX-e.offset().left+e.getPlotOffset().left,y:o.startPageY-e.offset().top+e.getPlotOffset().top},end:!1},isNaN(t.x)&&(t.x=0),isNaN(t.y)&&(t.y=0),n&&(l=n);var u,c,p,d,g;Object.keys(l).forEach(function(e){if(u=l[e],c=u.min,p=u.max,s=u.options,g=t[u.direction],d=S[u.direction],(s.axisPan||!n)&&(n||s.plotPan)&&0!==g){var o=a.saturate(u.c2p(u.p2c(c)-(d-g))-u.c2p(u.p2c(c))),i=a.saturate(u.c2p(u.p2c(p)-(d-g))-u.c2p(u.p2c(p)));isFinite(o)||(o=0),isFinite(i)||(i=0),u.options.offset.below=a.saturate(o+(u.options.offset.below||0)),u.options.offset.above=a.saturate(i+(u.options.offset.above||0))}}),S=t,e.setupGrid(!0),e.draw(),i||e.getPlaceholder().trigger("plotpan",[e,t,n,o])},e.smartPan.end=function(){Y=null,k=null,S={x:0,y:0},e.triggerRedrawOverlay()},e.getTouchedAxis=function(t,a){var o=e.getPlaceholder().offset();return o.left=t-o.left,o.top=a-o.top,e.getXAxes().concat(e.getYAxes()).filter(function(t){var e=t.box;if(void 0!==e)return o.left>e.left&&o.lefte.top&&o.top 1) { - options.series.pie.tilt = 1; - } else if (options.series.pie.tilt < 0) { - options.series.pie.tilt = 0; - } - } - }); + if (options.series.pie.tilt > 1) { + options.series.pie.tilt = 1; + } else if (options.series.pie.tilt < 0) { + options.series.pie.tilt = 0; + } + } + }); - plot.hooks.bindEvents.push(function(plot, eventHolder) { - var options = plot.getOptions(); - if (options.series.pie.show) { - if (options.grid.hoverable) { - eventHolder.unbind("mousemove").mousemove(onMouseMove); - } - if (options.grid.clickable) { - eventHolder.unbind("click").click(onClick); - } - } - }); + plot.hooks.bindEvents.push(function(plot, eventHolder) { + var options = plot.getOptions(); + if (options.series.pie.show) { + if (options.grid.hoverable) { + eventHolder.unbind("mousemove").mousemove(onMouseMove); + } + if (options.grid.clickable) { + eventHolder.unbind("click").click(onClick); + } + } + }); - plot.hooks.processDatapoints.push(function(plot, series, data, datapoints) { - var options = plot.getOptions(); - if (options.series.pie.show) { - processDatapoints(plot, series, data, datapoints); - } - }); + plot.hooks.processDatapoints.push(function(plot, series, data, datapoints) { + var options = plot.getOptions(); + if (options.series.pie.show) { + processDatapoints(plot, series, data, datapoints); + } + }); - plot.hooks.drawOverlay.push(function(plot, octx) { - var options = plot.getOptions(); - if (options.series.pie.show) { - drawOverlay(plot, octx); - } - }); + plot.hooks.drawOverlay.push(function(plot, octx) { + var options = plot.getOptions(); + if (options.series.pie.show) { + drawOverlay(plot, octx); + } + }); - plot.hooks.draw.push(function(plot, newCtx) { - var options = plot.getOptions(); - if (options.series.pie.show) { - draw(plot, newCtx); - } - }); + plot.hooks.draw.push(function(plot, newCtx) { + var options = plot.getOptions(); + if (options.series.pie.show) { + draw(plot, newCtx); + } + }); - function processDatapoints(plot, series, datapoints) { - if (!processed) { - processed = true; - canvas = plot.getCanvas(); - target = $(canvas).parent(); - options = plot.getOptions(); - plot.setData(combine(plot.getData())); - } - } - - function combine(data) { + function processDatapoints(plot, series, datapoints) { + if (!processed) { + processed = true; + canvas = plot.getCanvas(); + target = $(canvas).parent(); + options = plot.getOptions(); + plot.setData(combine(plot.getData())); + } + } - var total = 0, - combined = 0, - numCombined = 0, - color = options.series.pie.combine.color, - newdata = []; + function combine(data) { + var total = 0, + combined = 0, + numCombined = 0, + color = options.series.pie.combine.color, + newdata = [], + i, + value; - // Fix up the raw data from Flot, ensuring the data is numeric - - for (var i = 0; i < data.length; ++i) { + // Fix up the raw data from Flot, ensuring the data is numeric - var value = data[i].data; + for (i = 0; i < data.length; ++i) { + value = data[i].data; - // If the data is an array, we'll assume that it's a standard - // Flot x-y pair, and are concerned only with the second value. + // If the data is an array, we'll assume that it's a standard + // Flot x-y pair, and are concerned only with the second value. - // Note how we use the original array, rather than creating a - // new one; this is more efficient and preserves any extra data - // that the user may have stored in higher indexes. + // Note how we use the original array, rather than creating a + // new one; this is more efficient and preserves any extra data + // that the user may have stored in higher indexes. - if ($.isArray(value) && value.length == 1) { - value = value[0]; - } + if ($.isArray(value) && value.length === 1) { + value = value[0]; + } - if ($.isArray(value)) { - // Equivalent to $.isNumeric() but compatible with jQuery < 1.7 - if (!isNaN(parseFloat(value[1])) && isFinite(value[1])) { - value[1] = +value[1]; - } else { - value[1] = 0; - } - } else if (!isNaN(parseFloat(value)) && isFinite(value)) { - value = [1, +value]; - } else { - value = [1, 0]; - } + if ($.isArray(value)) { + // Equivalent to $.isNumeric() but compatible with jQuery < 1.7 + if (!isNaN(parseFloat(value[1])) && isFinite(value[1])) { + value[1] = +value[1]; + } else { + value[1] = 0; + } + } else if (!isNaN(parseFloat(value)) && isFinite(value)) { + value = [1, +value]; + } else { + value = [1, 0]; + } - data[i].data = [value]; - } + data[i].data = [value]; + } - // Sum up all the slices, so we can calculate percentages for each + // Sum up all the slices, so we can calculate percentages for each - for (var i = 0; i < data.length; ++i) { - total += data[i].data[0][1]; - } + for (i = 0; i < data.length; ++i) { + total += data[i].data[0][1]; + } - // Count the number of slices with percentages below the combine - // threshold; if it turns out to be just one, we won't combine. + // Count the number of slices with percentages below the combine + // threshold; if it turns out to be just one, we won't combine. - for (var i = 0; i < data.length; ++i) { - var value = data[i].data[0][1]; - if (value / total <= options.series.pie.combine.threshold) { - combined += value; - numCombined++; - if (!color) { - color = data[i].color; - } - } - } + for (i = 0; i < data.length; ++i) { + value = data[i].data[0][1]; + if (value / total <= options.series.pie.combine.threshold) { + combined += value; + numCombined++; + if (!color) { + color = data[i].color; + } + } + } - for (var i = 0; i < data.length; ++i) { - var value = data[i].data[0][1]; - if (numCombined < 2 || value / total > options.series.pie.combine.threshold) { - newdata.push( - $.extend(data[i], { /* extend to allow keeping all other original data values - and using them e.g. in labelFormatter. */ - data: [[1, value]], - color: data[i].color, - label: data[i].label, - angle: value * Math.PI * 2 / total, - percent: value / (total / 100) - }) - ); - } - } + for (i = 0; i < data.length; ++i) { + value = data[i].data[0][1]; + if (numCombined < 2 || value / total > options.series.pie.combine.threshold) { + newdata.push( + $.extend(data[i], { /* extend to allow keeping all other original data values + and using them e.g. in labelFormatter. */ + data: [[1, value]], + color: data[i].color, + label: data[i].label, + angle: value * Math.PI * 2 / total, + percent: value / (total / 100) + }) + ); + } + } - if (numCombined > 1) { - newdata.push({ - data: [[1, combined]], - color: color, - label: options.series.pie.combine.label, - angle: combined * Math.PI * 2 / total, - percent: combined / (total / 100) - }); - } + if (numCombined > 1) { + newdata.push({ + data: [[1, combined]], + color: color, + label: options.series.pie.combine.label, + angle: combined * Math.PI * 2 / total, + percent: combined / (total / 100) + }); + } - return newdata; - } - - function draw(plot, newCtx) { + return newdata; + } - if (!target) { - return; // if no series were passed - } + function draw(plot, newCtx) { + if (!target) { + return; // if no series were passed + } - var canvasWidth = plot.getPlaceholder().width(), - canvasHeight = plot.getPlaceholder().height(), - legendWidth = target.children().filter(".legend").children().width() || 0; + var canvasWidth = plot.getPlaceholder().width(), + canvasHeight = plot.getPlaceholder().height(), + legendWidth = target.children().filter(".legend").children().width() || 0; - ctx = newCtx; + ctx = newCtx; - // WARNING: HACK! REWRITE THIS CODE AS SOON AS POSSIBLE! + // WARNING: HACK! REWRITE THIS CODE AS SOON AS POSSIBLE! - // When combining smaller slices into an 'other' slice, we need to - // add a new series. Since Flot gives plugins no way to modify the - // list of series, the pie plugin uses a hack where the first call - // to processDatapoints results in a call to setData with the new - // list of series, then subsequent processDatapoints do nothing. + // When combining smaller slices into an 'other' slice, we need to + // add a new series. Since Flot gives plugins no way to modify the + // list of series, the pie plugin uses a hack where the first call + // to processDatapoints results in a call to setData with the new + // list of series, then subsequent processDatapoints do nothing. - // The plugin-global 'processed' flag is used to control this hack; - // it starts out false, and is set to true after the first call to - // processDatapoints. - - // Unfortunately this turns future setData calls into no-ops; they - // call processDatapoints, the flag is true, and nothing happens. + // The plugin-global 'processed' flag is used to control this hack; + // it starts out false, and is set to true after the first call to + // processDatapoints. - // To fix this we'll set the flag back to false here in draw, when - // all series have been processed, so the next sequence of calls to - // processDatapoints once again starts out with a slice-combine. - // This is really a hack; in 0.9 we need to give plugins a proper - // way to modify series before any processing begins. + // Unfortunately this turns future setData calls into no-ops; they + // call processDatapoints, the flag is true, and nothing happens. - processed = false; + // To fix this we'll set the flag back to false here in draw, when + // all series have been processed, so the next sequence of calls to + // processDatapoints once again starts out with a slice-combine. + // This is really a hack; in 0.9 we need to give plugins a proper + // way to modify series before any processing begins. - // calculate maximum radius and center point + processed = false; - maxRadius = Math.min(canvasWidth, canvasHeight / options.series.pie.tilt) / 2; - centerTop = canvasHeight / 2 + options.series.pie.offset.top; - centerLeft = canvasWidth / 2; + // calculate maximum radius and center point + maxRadius = Math.min(canvasWidth, canvasHeight / options.series.pie.tilt) / 2; + centerTop = canvasHeight / 2 + options.series.pie.offset.top; + centerLeft = canvasWidth / 2; - if (options.series.pie.offset.left == "auto") { - if (options.legend.position.match("w")) { - centerLeft += legendWidth / 2; - } else { - centerLeft -= legendWidth / 2; - } - if (centerLeft < maxRadius) { - centerLeft = maxRadius; - } else if (centerLeft > canvasWidth - maxRadius) { - centerLeft = canvasWidth - maxRadius; - } - } else { - centerLeft += options.series.pie.offset.left; - } - - var slices = plot.getData(), - attempts = 0; + if (options.series.pie.offset.left === "auto") { + if (options.legend.position.match("w")) { + centerLeft += legendWidth / 2; + } else { + centerLeft -= legendWidth / 2; + } + if (centerLeft < maxRadius) { + centerLeft = maxRadius; + } else if (centerLeft > canvasWidth - maxRadius) { + centerLeft = canvasWidth - maxRadius; + } + } else { + centerLeft += options.series.pie.offset.left; + } - // Keep shrinking the pie's radius until drawPie returns true, - // indicating that all the labels fit, or we try too many times. + var slices = plot.getData(), + attempts = 0; - do { - if (attempts > 0) { - maxRadius *= REDRAW_SHRINK; - } - attempts += 1; - clear(); - if (options.series.pie.tilt <= 0.8) { - drawShadow(); - } - } while (!drawPie() && attempts < REDRAW_ATTEMPTS) + // Keep shrinking the pie's radius until drawPie returns true, + // indicating that all the labels fit, or we try too many times. + do { + if (attempts > 0) { + maxRadius *= REDRAW_SHRINK; + } + attempts += 1; + clear(); + if (options.series.pie.tilt <= 0.8) { + drawShadow(); + } + } while (!drawPie() && attempts < REDRAW_ATTEMPTS) - if (attempts >= REDRAW_ATTEMPTS) { - clear(); - target.prepend("
Could not draw pie with labels contained inside canvas
"); - } + if (attempts >= REDRAW_ATTEMPTS) { + clear(); + target.prepend("
Could not draw pie with labels contained inside canvas
"); + } - if (plot.setSeries && plot.insertLegend) { - plot.setSeries(slices); - plot.insertLegend(); - } - - // we're actually done at this point, just defining internal functions at this point + if (plot.setSeries && plot.insertLegend) { + plot.setSeries(slices); + plot.insertLegend(); + } - function clear() { - ctx.clearRect(0, 0, canvasWidth, canvasHeight); - target.children().filter(".pieLabel, .pieLabelBackground").remove(); - } - - function drawShadow() { + // we're actually done at this point, just defining internal functions at this point + function clear() { + ctx.clearRect(0, 0, canvasWidth, canvasHeight); + target.children().filter(".pieLabel, .pieLabelBackground").remove(); + } - var shadowLeft = options.series.pie.shadow.left; - var shadowTop = options.series.pie.shadow.top; - var edge = 10; - var alpha = options.series.pie.shadow.alpha; - var radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius; + function drawShadow() { + var shadowLeft = options.series.pie.shadow.left; + var shadowTop = options.series.pie.shadow.top; + var edge = 10; + var alpha = options.series.pie.shadow.alpha; + var radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius; - if (radius >= canvasWidth / 2 - shadowLeft || radius * options.series.pie.tilt >= canvasHeight / 2 - shadowTop || radius <= edge) { - return; // shadow would be outside canvas, so don't draw it - } - - ctx.save(); - ctx.translate(shadowLeft,shadowTop); - ctx.globalAlpha = alpha; - ctx.fillStyle = "#000"; + if (radius >= canvasWidth / 2 - shadowLeft || radius * options.series.pie.tilt >= canvasHeight / 2 - shadowTop || radius <= edge) { + return; // shadow would be outside canvas, so don't draw it + } - // center and rotate to starting position + ctx.save(); + ctx.translate(shadowLeft, shadowTop); + ctx.globalAlpha = alpha; + ctx.fillStyle = "#000"; - ctx.translate(centerLeft,centerTop); - ctx.scale(1, options.series.pie.tilt); - - //radius -= edge; + // center and rotate to starting position + ctx.translate(centerLeft, centerTop); + ctx.scale(1, options.series.pie.tilt); - for (var i = 1; i <= edge; i++) { - ctx.beginPath(); - ctx.arc(0, 0, radius, 0, Math.PI * 2, false); - ctx.fill(); - radius -= i; - } + //radius -= edge; + for (var i = 1; i <= edge; i++) { + ctx.beginPath(); + ctx.arc(0, 0, radius, 0, Math.PI * 2, false); + ctx.fill(); + radius -= i; + } - ctx.restore(); - } - - function drawPie() { + ctx.restore(); + } - var startAngle = Math.PI * options.series.pie.startAngle; - var radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius; - - // center and rotate to starting position + function drawPie() { + var startAngle = Math.PI * options.series.pie.startAngle; + var radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius; + var i; + // center and rotate to starting position - ctx.save(); - ctx.translate(centerLeft,centerTop); - ctx.scale(1, options.series.pie.tilt); - //ctx.rotate(startAngle); // start at top; -- This doesn't work properly in Opera + ctx.save(); + ctx.translate(centerLeft, centerTop); + ctx.scale(1, options.series.pie.tilt); + //ctx.rotate(startAngle); // start at top; -- This doesn't work properly in Opera - // draw slices + // draw slices + ctx.save(); - ctx.save(); - var currentAngle = startAngle; - for (var i = 0; i < slices.length; ++i) { - slices[i].startAngle = currentAngle; - drawSlice(slices[i].angle, slices[i].color, true); - } - ctx.restore(); + var currentAngle = startAngle; + for (i = 0; i < slices.length; ++i) { + slices[i].startAngle = currentAngle; + drawSlice(slices[i].angle, slices[i].color, true); + } + + ctx.restore(); - // draw slice outlines + // draw slice outlines + if (options.series.pie.stroke.width > 0) { + ctx.save(); + ctx.lineWidth = options.series.pie.stroke.width; + currentAngle = startAngle; + for (i = 0; i < slices.length; ++i) { + drawSlice(slices[i].angle, options.series.pie.stroke.color, false); + } - if (options.series.pie.stroke.width > 0) { - ctx.save(); - ctx.lineWidth = options.series.pie.stroke.width; - currentAngle = startAngle; - for (var i = 0; i < slices.length; ++i) { - drawSlice(slices[i].angle, options.series.pie.stroke.color, false); - } - ctx.restore(); - } + ctx.restore(); + } - // draw donut hole + // draw donut hole + drawDonutHole(ctx); - drawDonutHole(ctx); - - ctx.restore(); + ctx.restore(); - // Draw the labels, returning true if they fit within the plot - - if (options.series.pie.label.show) { - return drawLabels(); - } else return true; - - function drawSlice(angle, color, fill) { + // Draw the labels, returning true if they fit within the plot + if (options.series.pie.label.show) { + return drawLabels(); + } else return true; - if (angle <= 0 || isNaN(angle)) { - return; - } + function drawSlice(angle, color, fill) { + if (angle <= 0 || isNaN(angle)) { + return; + } - if (fill) { - ctx.fillStyle = color; - } else { - ctx.strokeStyle = color; - ctx.lineJoin = "round"; - } + if (fill) { + ctx.fillStyle = color; + } else { + ctx.strokeStyle = color; + ctx.lineJoin = "round"; + } - ctx.beginPath(); - if (Math.abs(angle - Math.PI * 2) > 0.000000001) { - ctx.moveTo(0, 0); // Center of the pie - } + ctx.beginPath(); + if (Math.abs(angle - Math.PI * 2) > 0.000000001) { + ctx.moveTo(0, 0); // Center of the pie + } - //ctx.arc(0, 0, radius, 0, angle, false); // This doesn't work properly in Opera - ctx.arc(0, 0, radius,currentAngle, currentAngle + angle / 2, false); - ctx.arc(0, 0, radius,currentAngle + angle / 2, currentAngle + angle, false); - ctx.closePath(); - //ctx.rotate(angle); // This doesn't work properly in Opera - currentAngle += angle; + //ctx.arc(0, 0, radius, 0, angle, false); // This doesn't work properly in Opera + ctx.arc(0, 0, radius, currentAngle, currentAngle + angle / 2, false); + ctx.arc(0, 0, radius, currentAngle + angle / 2, currentAngle + angle, false); + ctx.closePath(); + //ctx.rotate(angle); // This doesn't work properly in Opera + currentAngle += angle; - if (fill) { - ctx.fill(); - } else { - ctx.stroke(); - } - } - - function drawLabels() { + if (fill) { + ctx.fill(); + } else { + ctx.stroke(); + } + } - var currentAngle = startAngle; - var radius = options.series.pie.label.radius > 1 ? options.series.pie.label.radius : maxRadius * options.series.pie.label.radius; - - for (var i = 0; i < slices.length; ++i) { - if (slices[i].percent >= options.series.pie.label.threshold * 100) { - if (!drawLabel(slices[i], currentAngle, i)) { - return false; - } - } - currentAngle += slices[i].angle; - } + function drawLabels() { + var currentAngle = startAngle; + var radius = options.series.pie.label.radius > 1 ? options.series.pie.label.radius : maxRadius * options.series.pie.label.radius; - return true; - - function drawLabel(slice, startAngle, index) { + for (var i = 0; i < slices.length; ++i) { + if (slices[i].percent >= options.series.pie.label.threshold * 100) { + if (!drawLabel(slices[i], currentAngle, i)) { + return false; + } + } + currentAngle += slices[i].angle; + } - if (slice.data[0][1] == 0) { - return true; - } + return true; - // format label text + function drawLabel(slice, startAngle, index) { + if (slice.data[0][1] === 0) { + return true; + } - var lf = options.legend.labelFormatter, text, plf = options.series.pie.label.formatter; + // format label text + var lf = options.legend.labelFormatter, text, plf = options.series.pie.label.formatter; - if (lf) { - text = lf(slice.label, slice); - } else { - text = slice.label; - } + if (lf) { + text = lf(slice.label, slice); + } else { + text = slice.label; + } - if (plf) { - text = plf(text, slice); - } + if (plf) { + text = plf(text, slice); + } - var halfAngle = ((startAngle + slice.angle) + startAngle) / 2; - var x = centerLeft + Math.round(Math.cos(halfAngle) * radius); - var y = centerTop + Math.round(Math.sin(halfAngle) * radius) * options.series.pie.tilt; + var halfAngle = ((startAngle + slice.angle) + startAngle) / 2; + var x = centerLeft + Math.round(Math.cos(halfAngle) * radius); + var y = centerTop + Math.round(Math.sin(halfAngle) * radius) * options.series.pie.tilt; - var html = "" + text + ""; - target.append(html); + var html = "" + text + ""; + target.append(html); - var label = target.children("#pieLabel" + index); - var labelTop = (y - label.height() / 2); - var labelLeft = (x - label.width() / 2); + var label = target.children("#pieLabel" + index); + var labelTop = (y - label.height() / 2); + var labelLeft = (x - label.width() / 2); - label.css("top", labelTop); - label.css("left", labelLeft); - - // check to make sure that the label is not outside the canvas + label.css("top", labelTop); + label.css("left", labelLeft); - if (0 - labelTop > 0 || 0 - labelLeft > 0 || canvasHeight - (labelTop + label.height()) < 0 || canvasWidth - (labelLeft + label.width()) < 0) { - return false; - } - - if (options.series.pie.label.background.opacity != 0) { + // check to make sure that the label is not outside the canvas + if (0 - labelTop > 0 || 0 - labelLeft > 0 || canvasHeight - (labelTop + label.height()) < 0 || canvasWidth - (labelLeft + label.width()) < 0) { + return false; + } - // put in the transparent background separately to avoid blended labels and label boxes - - var c = options.series.pie.label.background.color; + if (options.series.pie.label.background.opacity !== 0) { + // put in the transparent background separately to avoid blended labels and label boxes + var c = options.series.pie.label.background.color; + if (c == null) { + c = slice.color; + } - if (c == null) { - c = slice.color; - } - - var pos = "top:" + labelTop + "px;left:" + labelLeft + "px;"; - $("
") - .css("opacity", options.series.pie.label.background.opacity) - .insertBefore(label); - } + var pos = "top:" + labelTop + "px;left:" + labelLeft + "px;"; + $("
") + .css("opacity", options.series.pie.label.background.opacity) + .insertBefore(label); + } - return true; - } // end individual label function - } // end drawLabels function - } // end drawPie function - } // end draw function + return true; + } // end individual label function + } // end drawLabels function + } // end drawPie function + } // end draw function - // Placed here because it needs to be accessed from multiple locations - - function drawDonutHole(layer) { - if (options.series.pie.innerRadius > 0) { + // Placed here because it needs to be accessed from multiple locations - // subtract the center - - layer.save(); - var innerRadius = options.series.pie.innerRadius > 1 ? options.series.pie.innerRadius : maxRadius * options.series.pie.innerRadius; - layer.globalCompositeOperation = "destination-out"; // this does not work with excanvas, but it will fall back to using the stroke color - layer.beginPath(); - layer.fillStyle = options.series.pie.stroke.color; - layer.arc(0, 0, innerRadius, 0, Math.PI * 2, false); - layer.fill(); - layer.closePath(); - layer.restore(); + function drawDonutHole(layer) { + if (options.series.pie.innerRadius > 0) { + // subtract the center + layer.save(); + var innerRadius = options.series.pie.innerRadius > 1 ? options.series.pie.innerRadius : maxRadius * options.series.pie.innerRadius; + layer.globalCompositeOperation = "destination-out"; // this does not work with excanvas, but it will fall back to using the stroke color + layer.beginPath(); + layer.fillStyle = options.series.pie.stroke.color; + layer.arc(0, 0, innerRadius, 0, Math.PI * 2, false); + layer.fill(); + layer.closePath(); + layer.restore(); - // add inner stroke - - layer.save(); - layer.beginPath(); - layer.strokeStyle = options.series.pie.stroke.color; - layer.arc(0, 0, innerRadius, 0, Math.PI * 2, false); - layer.stroke(); - layer.closePath(); - layer.restore(); + // add inner stroke + layer.save(); + layer.beginPath(); + layer.strokeStyle = options.series.pie.stroke.color; + layer.arc(0, 0, innerRadius, 0, Math.PI * 2, false); + layer.stroke(); + layer.closePath(); + layer.restore(); - // TODO: add extra shadow inside hole (with a mask) if the pie is tilted. - } - } + // TODO: add extra shadow inside hole (with a mask) if the pie is tilted. + } + } - //-- Additional Interactive related functions -- + //-- Additional Interactive related functions -- - function isPointInPoly(poly, pt) { - for(var c = false, i = -1, l = poly.length, j = l - 1; ++i < l; j = i) - ((poly[i][1] <= pt[1] && pt[1] < poly[j][1]) || (poly[j][1] <= pt[1] && pt[1]< poly[i][1])) - && (pt[0] < (poly[j][0] - poly[i][0]) * (pt[1] - poly[i][1]) / (poly[j][1] - poly[i][1]) + poly[i][0]) - && (c = !c); - return c; - } - - function findNearbySlice(mouseX, mouseY) { + function isPointInPoly(poly, pt) { + for (var c = false, i = -1, l = poly.length, j = l - 1; ++i < l; j = i) { + ((poly[i][1] <= pt[1] && pt[1] < poly[j][1]) || + (poly[j][1] <= pt[1] && pt[1] < poly[i][1])) && + (pt[0] < (poly[j][0] - poly[i][0]) * (pt[1] - poly[i][1]) / (poly[j][1] - poly[i][1]) + poly[i][0]) && + (c = !c); + } + return c; + } - var slices = plot.getData(), - options = plot.getOptions(), - radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius, - x, y; - - for (var i = 0; i < slices.length; ++i) { - - var s = slices[i]; + function findNearbySlice(mouseX, mouseY) { + var slices = plot.getData(), + options = plot.getOptions(), + radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius, + x, y; - if (s.pie.show) { - - ctx.save(); - ctx.beginPath(); - ctx.moveTo(0, 0); // Center of the pie - //ctx.scale(1, options.series.pie.tilt); // this actually seems to break everything when here. - ctx.arc(0, 0, radius, s.startAngle, s.startAngle + s.angle / 2, false); - ctx.arc(0, 0, radius, s.startAngle + s.angle / 2, s.startAngle + s.angle, false); - ctx.closePath(); - x = mouseX - centerLeft; - y = mouseY - centerTop; + for (var i = 0; i < slices.length; ++i) { + var s = slices[i]; + if (s.pie.show) { + ctx.save(); + ctx.beginPath(); + ctx.moveTo(0, 0); // Center of the pie + //ctx.scale(1, options.series.pie.tilt); // this actually seems to break everything when here. + ctx.arc(0, 0, radius, s.startAngle, s.startAngle + s.angle / 2, false); + ctx.arc(0, 0, radius, s.startAngle + s.angle / 2, s.startAngle + s.angle, false); + ctx.closePath(); + x = mouseX - centerLeft; + y = mouseY - centerTop; - if (ctx.isPointInPath) { - if (ctx.isPointInPath(mouseX - centerLeft, mouseY - centerTop)) { - ctx.restore(); - return { - datapoint: [s.percent, s.data], - dataIndex: 0, - series: s, - seriesIndex: i - }; - } - } else { - - // excanvas for IE doesn;t support isPointInPath, this is a workaround. + if (ctx.isPointInPath) { + if (ctx.isPointInPath(mouseX - centerLeft, mouseY - centerTop)) { + ctx.restore(); + return { + datapoint: [s.percent, s.data], + dataIndex: 0, + series: s, + seriesIndex: i + }; + } + } else { + // excanvas for IE doesn;t support isPointInPath, this is a workaround. + var p1X = radius * Math.cos(s.startAngle), + p1Y = radius * Math.sin(s.startAngle), + p2X = radius * Math.cos(s.startAngle + s.angle / 4), + p2Y = radius * Math.sin(s.startAngle + s.angle / 4), + p3X = radius * Math.cos(s.startAngle + s.angle / 2), + p3Y = radius * Math.sin(s.startAngle + s.angle / 2), + p4X = radius * Math.cos(s.startAngle + s.angle / 1.5), + p4Y = radius * Math.sin(s.startAngle + s.angle / 1.5), + p5X = radius * Math.cos(s.startAngle + s.angle), + p5Y = radius * Math.sin(s.startAngle + s.angle), + arrPoly = [[0, 0], [p1X, p1Y], [p2X, p2Y], [p3X, p3Y], [p4X, p4Y], [p5X, p5Y]], + arrPoint = [x, y]; - var p1X = radius * Math.cos(s.startAngle), - p1Y = radius * Math.sin(s.startAngle), - p2X = radius * Math.cos(s.startAngle + s.angle / 4), - p2Y = radius * Math.sin(s.startAngle + s.angle / 4), - p3X = radius * Math.cos(s.startAngle + s.angle / 2), - p3Y = radius * Math.sin(s.startAngle + s.angle / 2), - p4X = radius * Math.cos(s.startAngle + s.angle / 1.5), - p4Y = radius * Math.sin(s.startAngle + s.angle / 1.5), - p5X = radius * Math.cos(s.startAngle + s.angle), - p5Y = radius * Math.sin(s.startAngle + s.angle), - arrPoly = [[0, 0], [p1X, p1Y], [p2X, p2Y], [p3X, p3Y], [p4X, p4Y], [p5X, p5Y]], - arrPoint = [x, y]; - - // TODO: perhaps do some mathmatical trickery here with the Y-coordinate to compensate for pie tilt? + // TODO: perhaps do some mathmatical trickery here with the Y-coordinate to compensate for pie tilt? - if (isPointInPoly(arrPoly, arrPoint)) { - ctx.restore(); - return { - datapoint: [s.percent, s.data], - dataIndex: 0, - series: s, - seriesIndex: i - }; - } - } + if (isPointInPoly(arrPoly, arrPoint)) { + ctx.restore(); + return { + datapoint: [s.percent, s.data], + dataIndex: 0, + series: s, + seriesIndex: i + }; + } + } - ctx.restore(); - } - } + ctx.restore(); + } + } - return null; - } + return null; + } + + function onMouseMove(e) { + triggerClickHoverEvent("plothover", e); + } - function onMouseMove(e) { - triggerClickHoverEvent("plothover", e); - } + function onClick(e) { + triggerClickHoverEvent("plotclick", e); + } - function onClick(e) { - triggerClickHoverEvent("plotclick", e); - } - - // trigger click or hover event (they send the same parameters so we share their code) + // trigger click or hover event (they send the same parameters so we share their code) - function triggerClickHoverEvent(eventname, e) { + function triggerClickHoverEvent(eventname, e) { + var offset = plot.offset(); + var canvasX = parseInt(e.pageX - offset.left); + var canvasY = parseInt(e.pageY - offset.top); + var item = findNearbySlice(canvasX, canvasY); - var offset = plot.offset(); - var canvasX = parseInt(e.pageX - offset.left); - var canvasY = parseInt(e.pageY - offset.top); - var item = findNearbySlice(canvasX, canvasY); - - if (options.grid.autoHighlight) { - - // clear auto-highlights + if (options.grid.autoHighlight) { + // clear auto-highlights + for (var i = 0; i < highlights.length; ++i) { + var h = highlights[i]; + if (h.auto === eventname && !(item && h.series === item.series)) { + unhighlight(h.series); + } + } + } - for (var i = 0; i < highlights.length; ++i) { - var h = highlights[i]; - if (h.auto == eventname && !(item && h.series == item.series)) { - unhighlight(h.series); - } - } - } + // highlight the slice - // highlight the slice + if (item) { + highlight(item.series, eventname); + } + + // trigger any hover bind events - if (item) { - highlight(item.series, eventname); - } - - // trigger any hover bind events + var pos = { pageX: e.pageX, pageY: e.pageY }; + target.trigger(eventname, [pos, item]); + } - var pos = { pageX: e.pageX, pageY: e.pageY }; - target.trigger(eventname, [pos, item]); - } + function highlight(s, auto) { + //if (typeof s == "number") { + // s = series[s]; + //} - function highlight(s, auto) { - //if (typeof s == "number") { - // s = series[s]; - //} + var i = indexOfHighlight(s); - var i = indexOfHighlight(s); - - if (i == -1) { - highlights.push({ series: s, auto: auto }); - plot.triggerRedrawOverlay(); - } else if (!auto) { - highlights[i].auto = false; - } - } + if (i === -1) { + highlights.push({ series: s, auto: auto }); + plot.triggerRedrawOverlay(); + } else if (!auto) { + highlights[i].auto = false; + } + } - function unhighlight(s) { - if (s == null) { - highlights = []; - plot.triggerRedrawOverlay(); - } + function unhighlight(s) { + if (s == null) { + highlights = []; + plot.triggerRedrawOverlay(); + } - //if (typeof s == "number") { - // s = series[s]; - //} + //if (typeof s == "number") { + // s = series[s]; + //} - var i = indexOfHighlight(s); + var i = indexOfHighlight(s); - if (i != -1) { - highlights.splice(i, 1); - plot.triggerRedrawOverlay(); - } - } + if (i !== -1) { + highlights.splice(i, 1); + plot.triggerRedrawOverlay(); + } + } - function indexOfHighlight(s) { - for (var i = 0; i < highlights.length; ++i) { - var h = highlights[i]; - if (h.series == s) - return i; - } - return -1; - } - - function drawOverlay(plot, octx) { + function indexOfHighlight(s) { + for (var i = 0; i < highlights.length; ++i) { + var h = highlights[i]; + if (h.series === s) { + return i; + } + } + return -1; + } - var options = plot.getOptions(); - - var radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius; + function drawOverlay(plot, octx) { + var options = plot.getOptions(); + var radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius; - octx.save(); - octx.translate(centerLeft, centerTop); - octx.scale(1, options.series.pie.tilt); + octx.save(); + octx.translate(centerLeft, centerTop); + octx.scale(1, options.series.pie.tilt); - for (var i = 0; i < highlights.length; ++i) { - drawHighlight(highlights[i].series); - } + for (var i = 0; i < highlights.length; ++i) { + drawHighlight(highlights[i].series); + } - drawDonutHole(octx); + drawDonutHole(octx); - octx.restore(); + octx.restore(); - function drawHighlight(series) { - - if (series.angle <= 0 || isNaN(series.angle)) { - return; - } + function drawHighlight(series) { + if (series.angle <= 0 || isNaN(series.angle)) { + return; + } - //octx.fillStyle = parseColor(options.series.pie.highlight.color).scale(null, null, null, options.series.pie.highlight.opacity).toString(); - octx.fillStyle = "rgba(255, 255, 255, " + options.series.pie.highlight.opacity + ")"; // this is temporary until we have access to parseColor - octx.beginPath(); - if (Math.abs(series.angle - Math.PI * 2) > 0.000000001) { - octx.moveTo(0, 0); // Center of the pie - } - octx.arc(0, 0, radius, series.startAngle, series.startAngle + series.angle / 2, false); - octx.arc(0, 0, radius, series.startAngle + series.angle / 2, series.startAngle + series.angle, false); - octx.closePath(); - octx.fill(); - } - } - } // end init (plugin body) - - // define pie specific options and their default values + //octx.fillStyle = parseColor(options.series.pie.highlight.color).scale(null, null, null, options.series.pie.highlight.opacity).toString(); + octx.fillStyle = "rgba(255, 255, 255, " + options.series.pie.highlight.opacity + ")"; // this is temporary until we have access to parseColor + octx.beginPath(); + if (Math.abs(series.angle - Math.PI * 2) > 0.000000001) { + octx.moveTo(0, 0); // Center of the pie + } + octx.arc(0, 0, radius, series.startAngle, series.startAngle + series.angle / 2, false); + octx.arc(0, 0, radius, series.startAngle + series.angle / 2, series.startAngle + series.angle, false); + octx.closePath(); + octx.fill(); + } + } + } // end init (plugin body) - var options = { - series: { - pie: { - show: false, - radius: "auto", // actual radius of the visible pie (based on full calculated radius if <=1, or hard pixel value) - innerRadius: 0, /* for donut */ - startAngle: 3/2, - tilt: 1, - shadow: { - left: 5, // shadow left offset - top: 15, // shadow top offset - alpha: 0.02 // shadow alpha - }, - offset: { - top: 0, - left: "auto" - }, - stroke: { - color: "#fff", - width: 1 - }, - label: { - show: "auto", - formatter: function(label, slice) { - return "
" + label + "
" + Math.round(slice.percent) + "%
"; - }, // formatter function - radius: 1, // radius at which to place the labels (based on full calculated radius if <=1, or hard pixel value) - background: { - color: null, - opacity: 0 - }, - threshold: 0 // percentage at which to hide the label (i.e. the slice is too narrow) - }, - combine: { - threshold: -1, // percentage at which to combine little slices into one larger slice - color: null, // color to give the new slice (auto-generated if null) - label: "Other" // label to give the new slice - }, - highlight: { - //color: "#fff", // will add this functionality once parseColor is available - opacity: 0.5 - } - } - } - }; + // define pie specific options and their default values + var options = { + series: { + pie: { + show: false, + radius: "auto", // actual radius of the visible pie (based on full calculated radius if <=1, or hard pixel value) + innerRadius: 0, /* for donut */ + startAngle: 3 / 2, + tilt: 1, + shadow: { + left: 5, // shadow left offset + top: 15, // shadow top offset + alpha: 0.02 // shadow alpha + }, + offset: { + top: 0, + left: "auto" + }, + stroke: { + color: "#fff", + width: 1 + }, + label: { + show: "auto", + formatter: function(label, slice) { + return "
" + label + "
" + Math.round(slice.percent) + "%
"; + }, // formatter function + radius: 1, // radius at which to place the labels (based on full calculated radius if <=1, or hard pixel value) + background: { + color: null, + opacity: 0 + }, + threshold: 0 // percentage at which to hide the label (i.e. the slice is too narrow) + }, + combine: { + threshold: -1, // percentage at which to combine little slices into one larger slice + color: null, // color to give the new slice (auto-generated if null) + label: "Other" // label to give the new slice + }, + highlight: { + //color: "#fff", // will add this functionality once parseColor is available + opacity: 0.5 + } + } + } + }; - $.plot.plugins.push({ - init: init, - options: options, - name: "pie", - version: "1.1" - }); - + $.plot.plugins.push({ + init: init, + options: options, + name: "pie", + version: "1.1" + }); })(jQuery); diff -r f3af77e371a1 -r 9f02c09d2393 src/pyams_skin/resources/js/ext/flot/jquery.flot.pie.min.js --- a/src/pyams_skin/resources/js/ext/flot/jquery.flot.pie.min.js Thu Sep 26 11:55:17 2019 +0200 +++ b/src/pyams_skin/resources/js/ext/flot/jquery.flot.pie.min.js Wed Oct 16 13:00:43 2019 +0200 @@ -1,7 +1,1 @@ -/* Javascript plotting library for jQuery, version 0.8.3. - -Copyright (c) 2007-2014 IOLA and Ole Laursen. -Licensed under the MIT license. - -*/ -(function($){var REDRAW_ATTEMPTS=10;var REDRAW_SHRINK=.95;function init(plot){var canvas=null,target=null,options=null,maxRadius=null,centerLeft=null,centerTop=null,processed=false,ctx=null;var highlights=[];plot.hooks.processOptions.push(function(plot,options){if(options.series.pie.show){options.grid.show=false;if(options.series.pie.label.show=="auto"){if(options.legend.show){options.series.pie.label.show=false}else{options.series.pie.label.show=true}}if(options.series.pie.radius=="auto"){if(options.series.pie.label.show){options.series.pie.radius=3/4}else{options.series.pie.radius=1}}if(options.series.pie.tilt>1){options.series.pie.tilt=1}else if(options.series.pie.tilt<0){options.series.pie.tilt=0}}});plot.hooks.bindEvents.push(function(plot,eventHolder){var options=plot.getOptions();if(options.series.pie.show){if(options.grid.hoverable){eventHolder.unbind("mousemove").mousemove(onMouseMove)}if(options.grid.clickable){eventHolder.unbind("click").click(onClick)}}});plot.hooks.processDatapoints.push(function(plot,series,data,datapoints){var options=plot.getOptions();if(options.series.pie.show){processDatapoints(plot,series,data,datapoints)}});plot.hooks.drawOverlay.push(function(plot,octx){var options=plot.getOptions();if(options.series.pie.show){drawOverlay(plot,octx)}});plot.hooks.draw.push(function(plot,newCtx){var options=plot.getOptions();if(options.series.pie.show){draw(plot,newCtx)}});function processDatapoints(plot,series,datapoints){if(!processed){processed=true;canvas=plot.getCanvas();target=$(canvas).parent();options=plot.getOptions();plot.setData(combine(plot.getData()))}}function combine(data){var total=0,combined=0,numCombined=0,color=options.series.pie.combine.color,newdata=[];for(var i=0;ioptions.series.pie.combine.threshold){newdata.push($.extend(data[i],{data:[[1,value]],color:data[i].color,label:data[i].label,angle:value*Math.PI*2/total,percent:value/(total/100)}))}}if(numCombined>1){newdata.push({data:[[1,combined]],color:color,label:options.series.pie.combine.label,angle:combined*Math.PI*2/total,percent:combined/(total/100)})}return newdata}function draw(plot,newCtx){if(!target){return}var canvasWidth=plot.getPlaceholder().width(),canvasHeight=plot.getPlaceholder().height(),legendWidth=target.children().filter(".legend").children().width()||0;ctx=newCtx;processed=false;maxRadius=Math.min(canvasWidth,canvasHeight/options.series.pie.tilt)/2;centerTop=canvasHeight/2+options.series.pie.offset.top;centerLeft=canvasWidth/2;if(options.series.pie.offset.left=="auto"){if(options.legend.position.match("w")){centerLeft+=legendWidth/2}else{centerLeft-=legendWidth/2}if(centerLeftcanvasWidth-maxRadius){centerLeft=canvasWidth-maxRadius}}else{centerLeft+=options.series.pie.offset.left}var slices=plot.getData(),attempts=0;do{if(attempts>0){maxRadius*=REDRAW_SHRINK}attempts+=1;clear();if(options.series.pie.tilt<=.8){drawShadow()}}while(!drawPie()&&attempts=REDRAW_ATTEMPTS){clear();target.prepend("
Could not draw pie with labels contained inside canvas
")}if(plot.setSeries&&plot.insertLegend){plot.setSeries(slices);plot.insertLegend()}function clear(){ctx.clearRect(0,0,canvasWidth,canvasHeight);target.children().filter(".pieLabel, .pieLabelBackground").remove()}function drawShadow(){var shadowLeft=options.series.pie.shadow.left;var shadowTop=options.series.pie.shadow.top;var edge=10;var alpha=options.series.pie.shadow.alpha;var radius=options.series.pie.radius>1?options.series.pie.radius:maxRadius*options.series.pie.radius;if(radius>=canvasWidth/2-shadowLeft||radius*options.series.pie.tilt>=canvasHeight/2-shadowTop||radius<=edge){return}ctx.save();ctx.translate(shadowLeft,shadowTop);ctx.globalAlpha=alpha;ctx.fillStyle="#000";ctx.translate(centerLeft,centerTop);ctx.scale(1,options.series.pie.tilt);for(var i=1;i<=edge;i++){ctx.beginPath();ctx.arc(0,0,radius,0,Math.PI*2,false);ctx.fill();radius-=i}ctx.restore()}function drawPie(){var startAngle=Math.PI*options.series.pie.startAngle;var radius=options.series.pie.radius>1?options.series.pie.radius:maxRadius*options.series.pie.radius;ctx.save();ctx.translate(centerLeft,centerTop);ctx.scale(1,options.series.pie.tilt);ctx.save();var currentAngle=startAngle;for(var i=0;i0){ctx.save();ctx.lineWidth=options.series.pie.stroke.width;currentAngle=startAngle;for(var i=0;i1e-9){ctx.moveTo(0,0)}ctx.arc(0,0,radius,currentAngle,currentAngle+angle/2,false);ctx.arc(0,0,radius,currentAngle+angle/2,currentAngle+angle,false);ctx.closePath();currentAngle+=angle;if(fill){ctx.fill()}else{ctx.stroke()}}function drawLabels(){var currentAngle=startAngle;var radius=options.series.pie.label.radius>1?options.series.pie.label.radius:maxRadius*options.series.pie.label.radius;for(var i=0;i=options.series.pie.label.threshold*100){if(!drawLabel(slices[i],currentAngle,i)){return false}}currentAngle+=slices[i].angle}return true;function drawLabel(slice,startAngle,index){if(slice.data[0][1]==0){return true}var lf=options.legend.labelFormatter,text,plf=options.series.pie.label.formatter;if(lf){text=lf(slice.label,slice)}else{text=slice.label}if(plf){text=plf(text,slice)}var halfAngle=(startAngle+slice.angle+startAngle)/2;var x=centerLeft+Math.round(Math.cos(halfAngle)*radius);var y=centerTop+Math.round(Math.sin(halfAngle)*radius)*options.series.pie.tilt;var html=""+text+"";target.append(html);var label=target.children("#pieLabel"+index);var labelTop=y-label.height()/2;var labelLeft=x-label.width()/2;label.css("top",labelTop);label.css("left",labelLeft);if(0-labelTop>0||0-labelLeft>0||canvasHeight-(labelTop+label.height())<0||canvasWidth-(labelLeft+label.width())<0){return false}if(options.series.pie.label.background.opacity!=0){var c=options.series.pie.label.background.color;if(c==null){c=slice.color}var pos="top:"+labelTop+"px;left:"+labelLeft+"px;";$("
").css("opacity",options.series.pie.label.background.opacity).insertBefore(label)}return true}}}}function drawDonutHole(layer){if(options.series.pie.innerRadius>0){layer.save();var innerRadius=options.series.pie.innerRadius>1?options.series.pie.innerRadius:maxRadius*options.series.pie.innerRadius;layer.globalCompositeOperation="destination-out";layer.beginPath();layer.fillStyle=options.series.pie.stroke.color;layer.arc(0,0,innerRadius,0,Math.PI*2,false);layer.fill();layer.closePath();layer.restore();layer.save();layer.beginPath();layer.strokeStyle=options.series.pie.stroke.color;layer.arc(0,0,innerRadius,0,Math.PI*2,false);layer.stroke();layer.closePath();layer.restore()}}function isPointInPoly(poly,pt){for(var c=false,i=-1,l=poly.length,j=l-1;++i1?options.series.pie.radius:maxRadius*options.series.pie.radius,x,y;for(var i=0;i1?options.series.pie.radius:maxRadius*options.series.pie.radius;octx.save();octx.translate(centerLeft,centerTop);octx.scale(1,options.series.pie.tilt);for(var i=0;i1e-9){octx.moveTo(0,0)}octx.arc(0,0,radius,series.startAngle,series.startAngle+series.angle/2,false);octx.arc(0,0,radius,series.startAngle+series.angle/2,series.startAngle+series.angle,false);octx.closePath();octx.fill()}}}var options={series:{pie:{show:false,radius:"auto",innerRadius:0,startAngle:3/2,tilt:1,shadow:{left:5,top:15,alpha:.02},offset:{top:0,left:"auto"},stroke:{color:"#fff",width:1},label:{show:"auto",formatter:function(label,slice){return"
"+label+"
"+Math.round(slice.percent)+"%
"},radius:1,background:{color:null,opacity:0},threshold:0},combine:{threshold:-1,color:null,label:"Other"},highlight:{opacity:.5}}}};$.plot.plugins.push({init:init,options:options,name:"pie",version:"1.1"})})(jQuery); \ No newline at end of file +!function(e){var i=10,s=.95,t={series:{pie:{show:!1,radius:"auto",innerRadius:0,startAngle:1.5,tilt:1,shadow:{left:5,top:15,alpha:.02},offset:{top:0,left:"auto"},stroke:{color:"#fff",width:1},label:{show:"auto",formatter:function(e,i){return"
"+e+"
"+Math.round(i.percent)+"%
"},radius:1,background:{color:null,opacity:0},threshold:0},combine:{threshold:-1,color:null,label:"Other"},highlight:{opacity:.5}}}};e.plot.plugins.push({init:function(t){function r(i,s,t){y||(y=!0,b=i.getCanvas(),w=e(b).parent(),k=i.getOptions(),i.setData(a(i.getData())))}function a(i){var s,t,r=0,a=0,l=0,n=k.series.pie.combine.color,o=[];for(s=0;sk.series.pie.combine.threshold)&&o.push(e.extend(i[s],{data:[[1,t]],color:i[s].color,label:i[s].label,angle:t*Math.PI*2/r,percent:t/(r/100)}));return l>1&&o.push({data:[[1,a]],color:n,label:k.series.pie.combine.label,angle:a*Math.PI*2/r,percent:a/(r/100)}),o}function l(t,r){function a(){m.clearRect(0,0,l,o),w.children().filter(".pieLabel, .pieLabelBackground").remove()}if(w){var l=t.getPlaceholder().width(),o=t.getPlaceholder().height(),p=w.children().filter(".legend").children().width()||0;m=r,y=!1,M=Math.min(l,o/k.series.pie.tilt)/2,A=o/2+k.series.pie.offset.top,P=l/2,"auto"===k.series.pie.offset.left?(k.legend.position.match("w")?P+=p/2:P-=p/2,Pl-M&&(P=l-M)):P+=k.series.pie.offset.left;var h=t.getData(),g=0;do{g>0&&(M*=s),g+=1,a(),k.series.pie.tilt<=.8&&function(){var e=k.series.pie.shadow.left,i=k.series.pie.shadow.top,s=k.series.pie.shadow.alpha,t=k.series.pie.radius>1?k.series.pie.radius:M*k.series.pie.radius;if(!(t>=l/2-e||t*k.series.pie.tilt>=o/2-i||t<=10)){m.save(),m.translate(e,i),m.globalAlpha=s,m.fillStyle="#000",m.translate(P,A),m.scale(1,k.series.pie.tilt);for(var r=1;r<=10;r++)m.beginPath(),m.arc(0,0,t,0,2*Math.PI,!1),m.fill(),t-=r;m.restore()}}()}while(!function(){function i(e,i,s){e<=0||isNaN(e)||(s?m.fillStyle=i:(m.strokeStyle=i,m.lineJoin="round"),m.beginPath(),Math.abs(e-2*Math.PI)>1e-9&&m.moveTo(0,0),m.arc(0,0,r,a,a+e/2,!1),m.arc(0,0,r,a+e/2,a+e,!1),m.closePath(),a+=e,s?m.fill():m.stroke())}var s,t=Math.PI*k.series.pie.startAngle,r=k.series.pie.radius>1?k.series.pie.radius:M*k.series.pie.radius;m.save(),m.translate(P,A),m.scale(1,k.series.pie.tilt),m.save();var a=t;for(s=0;s0){for(m.save(),m.lineWidth=k.series.pie.stroke.width,a=t,s=0;s1?k.series.pie.label.radius:M*k.series.pie.label.radius,r=0;r=100*k.series.pie.label.threshold&&!function(i,t,r){if(0===i.data[0][1])return!0;var a,n=k.legend.labelFormatter,p=k.series.pie.label.formatter;a=n?n(i.label,i):i.label,p&&(a=p(a,i));var h=(t+i.angle+t)/2,g=P+Math.round(Math.cos(h)*s),c=A+Math.round(Math.sin(h)*s)*k.series.pie.tilt,u=""+a+"";w.append(u);var d=w.children("#pieLabel"+r),f=c-d.height()/2,v=g-d.width()/2;if(d.css("top",f),d.css("left",v),0-f>0||0-v>0||o-(f+d.height())<0||l-(v+d.width())<0)return!1;if(0!==k.series.pie.label.background.opacity){var b=k.series.pie.label.background.color;null==b&&(b=i.color);var M="top:"+f+"px;left:"+v+"px;";e("
").css("opacity",k.series.pie.label.background.opacity).insertBefore(d)}return!0}(h[r],i,r))return!1;i+=h[r].angle}return!0}()}()&&g=i&&(a(),w.prepend("
Could not draw pie with labels contained inside canvas
")),t.setSeries&&t.insertLegend&&(t.setSeries(h),t.insertLegend())}}function n(e){if(k.series.pie.innerRadius>0){e.save();var i=k.series.pie.innerRadius>1?k.series.pie.innerRadius:M*k.series.pie.innerRadius;e.globalCompositeOperation="destination-out",e.beginPath(),e.fillStyle=k.series.pie.stroke.color,e.arc(0,0,i,0,2*Math.PI,!1),e.fill(),e.closePath(),e.restore(),e.save(),e.beginPath(),e.strokeStyle=k.series.pie.stroke.color,e.arc(0,0,i,0,2*Math.PI,!1),e.stroke(),e.closePath(),e.restore()}}function o(e,i){for(var s=!1,t=-1,r=e.length,a=r-1;++t1?l.series.pie.radius:M*l.series.pie.radius,p=0;p1?s.series.pie.radius:M*s.series.pie.radius;i.save(),i.translate(P,A),i.scale(1,s.series.pie.tilt);for(var r=0;r1e-9&&i.moveTo(0,0),i.arc(0,0,t,e.startAngle,e.startAngle+e.angle/2,!1),i.arc(0,0,t,e.startAngle+e.angle/2,e.startAngle+e.angle,!1),i.closePath(),i.fill())}(I[r].series);n(i),i.restore()}var b=null,w=null,k=null,M=null,P=null,A=null,y=!1,m=null,I=[];t.hooks.processOptions.push(function(e,i){i.series.pie.show&&(i.grid.show=!1,"auto"===i.series.pie.label.show&&(i.legend.show?i.series.pie.label.show=!1:i.series.pie.label.show=!0),"auto"===i.series.pie.radius&&(i.series.pie.label.show?i.series.pie.radius=.75:i.series.pie.radius=1),i.series.pie.tilt>1?i.series.pie.tilt=1:i.series.pie.tilt<0&&(i.series.pie.tilt=0))}),t.hooks.bindEvents.push(function(e,i){var s=e.getOptions();s.series.pie.show&&(s.grid.hoverable&&i.unbind("mousemove").mousemove(h),s.grid.clickable&&i.unbind("click").click(g))}),t.hooks.processDatapoints.push(function(e,i,s,t){e.getOptions().series.pie.show&&r(e)}),t.hooks.drawOverlay.push(function(e,i){e.getOptions().series.pie.show&&v(e,i)}),t.hooks.draw.push(function(e,i){e.getOptions().series.pie.show&&l(e,i)})},options:t,name:"pie",version:"1.1"})}(jQuery); diff -r f3af77e371a1 -r 9f02c09d2393 src/pyams_skin/resources/js/ext/flot/jquery.flot.resize.js --- a/src/pyams_skin/resources/js/ext/flot/jquery.flot.resize.js Thu Sep 26 11:55:17 2019 +0200 +++ b/src/pyams_skin/resources/js/ext/flot/jquery.flot.resize.js Wed Oct 16 13:00:43 2019 +0200 @@ -1,3 +1,4 @@ +/* eslint-disable */ /* Flot plugin for automatically redrawing plots as the placeholder resizes. Copyright (c) 2007-2014 IOLA and Ole Laursen. @@ -21,6 +22,7 @@ */ (function($,e,t){"$:nomunge";var i=[],n=$.resize=$.extend($.resize,{}),a,r=false,s="setTimeout",u="resize",m=u+"-special-event",o="pendingDelay",l="activeDelay",f="throttleWindow";n[o]=200;n[l]=20;n[f]=true;$.event.special[u]={setup:function(){if(!n[f]&&this[s]){return false}var e=$(this);i.push(this);e.data(m,{w:e.width(),h:e.height()});if(i.length===1){a=t;h()}},teardown:function(){if(!n[f]&&this[s]){return false}var e=$(this);for(var t=i.length-1;t>=0;t--){if(i[t]==this){i.splice(t,1);break}}e.removeData(m);if(!i.length){if(r){cancelAnimationFrame(a)}else{clearTimeout(a)}a=null}},add:function(e){if(!n[f]&&this[s]){return false}var i;function a(e,n,a){var r=$(this),s=r.data(m)||{};s.w=n!==t?n:r.width();s.h=a!==t?a:r.height();i.apply(this,arguments)}if($.isFunction(e)){i=e;return a}else{i=e.handler;e.handler=a}}};function h(t){if(r===true){r=t||1}for(var s=i.length-1;s>=0;s--){var l=$(i[s]);if(l[0]==e||l.is(":visible")){var f=l.width(),c=l.height(),d=l.data(m);if(d&&(f!==d.w||c!==d.h)){l.trigger(u,[d.w=f,d.h=c]);r=t||true}}else{d=l.data(m);d.w=0;d.h=0}}if(a!==null){if(r&&(t==null||t-r<1e3)){a=e.requestAnimationFrame(h)}else{a=setTimeout(h,n[o]);r=false}}}if(!e.requestAnimationFrame){e.requestAnimationFrame=function(){return e.webkitRequestAnimationFrame||e.mozRequestAnimationFrame||e.oRequestAnimationFrame||e.msRequestAnimationFrame||function(t,i){return e.setTimeout(function(){t((new Date).getTime())},n[l])}}()}if(!e.cancelAnimationFrame){e.cancelAnimationFrame=function(){return e.webkitCancelRequestAnimationFrame||e.mozCancelRequestAnimationFrame||e.oCancelRequestAnimationFrame||e.msCancelRequestAnimationFrame||clearTimeout}()}})(jQuery,this); +/* eslint-enable */ (function ($) { var options = { }; // no options @@ -30,14 +32,13 @@ // somebody might have hidden us and we can't plot // when we don't have the dimensions - if (placeholder.width() == 0 || placeholder.height() == 0) - return; + if (placeholder.width() === 0 || placeholder.height() === 0) return; plot.resize(); plot.setupGrid(); plot.draw(); } - + function bindEvents(plot, eventHolder) { plot.getPlaceholder().resize(onResize); } @@ -45,11 +46,11 @@ function shutdown(plot, eventHolder) { plot.getPlaceholder().unbind("resize", onResize); } - + plot.hooks.bindEvents.push(bindEvents); plot.hooks.shutdown.push(shutdown); } - + $.plot.plugins.push({ init: init, options: options, diff -r f3af77e371a1 -r 9f02c09d2393 src/pyams_skin/resources/js/ext/flot/jquery.flot.resize.min.js --- a/src/pyams_skin/resources/js/ext/flot/jquery.flot.resize.min.js Thu Sep 26 11:55:17 2019 +0200 +++ b/src/pyams_skin/resources/js/ext/flot/jquery.flot.resize.min.js Wed Oct 16 13:00:43 2019 +0200 @@ -1,7 +1,1 @@ -/* Javascript plotting library for jQuery, version 0.8.3. - -Copyright (c) 2007-2014 IOLA and Ole Laursen. -Licensed under the MIT license. - -*/ -(function($,e,t){"$:nomunge";var i=[],n=$.resize=$.extend($.resize,{}),a,r=false,s="setTimeout",u="resize",m=u+"-special-event",o="pendingDelay",l="activeDelay",f="throttleWindow";n[o]=200;n[l]=20;n[f]=true;$.event.special[u]={setup:function(){if(!n[f]&&this[s]){return false}var e=$(this);i.push(this);e.data(m,{w:e.width(),h:e.height()});if(i.length===1){a=t;h()}},teardown:function(){if(!n[f]&&this[s]){return false}var e=$(this);for(var t=i.length-1;t>=0;t--){if(i[t]==this){i.splice(t,1);break}}e.removeData(m);if(!i.length){if(r){cancelAnimationFrame(a)}else{clearTimeout(a)}a=null}},add:function(e){if(!n[f]&&this[s]){return false}var i;function a(e,n,a){var r=$(this),s=r.data(m)||{};s.w=n!==t?n:r.width();s.h=a!==t?a:r.height();i.apply(this,arguments)}if($.isFunction(e)){i=e;return a}else{i=e.handler;e.handler=a}}};function h(t){if(r===true){r=t||1}for(var s=i.length-1;s>=0;s--){var l=$(i[s]);if(l[0]==e||l.is(":visible")){var f=l.width(),c=l.height(),d=l.data(m);if(d&&(f!==d.w||c!==d.h)){l.trigger(u,[d.w=f,d.h=c]);r=t||true}}else{d=l.data(m);d.w=0;d.h=0}}if(a!==null){if(r&&(t==null||t-r<1e3)){a=e.requestAnimationFrame(h)}else{a=setTimeout(h,n[o]);r=false}}}if(!e.requestAnimationFrame){e.requestAnimationFrame=function(){return e.webkitRequestAnimationFrame||e.mozRequestAnimationFrame||e.oRequestAnimationFrame||e.msRequestAnimationFrame||function(t,i){return e.setTimeout(function(){t((new Date).getTime())},n[l])}}()}if(!e.cancelAnimationFrame){e.cancelAnimationFrame=function(){return e.webkitCancelRequestAnimationFrame||e.mozCancelRequestAnimationFrame||e.oCancelRequestAnimationFrame||e.msCancelRequestAnimationFrame||clearTimeout}()}})(jQuery,this);(function($){var options={};function init(plot){function onResize(){var placeholder=plot.getPlaceholder();if(placeholder.width()==0||placeholder.height()==0)return;plot.resize();plot.setupGrid();plot.draw()}function bindEvents(plot,eventHolder){plot.getPlaceholder().resize(onResize)}function shutdown(plot,eventHolder){plot.getPlaceholder().unbind("resize",onResize)}plot.hooks.bindEvents.push(bindEvents);plot.hooks.shutdown.push(shutdown)}$.plot.plugins.push({init:init,options:options,name:"resize",version:"1.0"})})(jQuery); \ No newline at end of file +!function(e,t,i){"$:nomunge";function n(i){!0===s&&(s=i||1);for(var u=r.length-1;u>=0;u--){var c=e(r[u]);if(c[0]==t||c.is(":visible")){var d=c.width(),f=c.height(),g=c.data(m);!g||d===g.w&&f===g.h||(c.trigger(h,[g.w=d,g.h=f]),s=i||!0)}else(g=c.data(m)).w=0,g.h=0}null!==a&&(s&&(null==i||i-s<1e3)?a=t.requestAnimationFrame(n):(a=setTimeout(n,o[l]),s=!1))}var a,r=[],o=e.resize=e.extend(e.resize,{}),s=!1,u="setTimeout",h="resize",m=h+"-special-event",l="pendingDelay",c="activeDelay",d="throttleWindow";o[l]=200,o[c]=20,o[d]=!0,e.event.special[h]={setup:function(){if(!o[d]&&this[u])return!1;var t=e(this);r.push(this),t.data(m,{w:t.width(),h:t.height()}),1===r.length&&(a=i,n())},teardown:function(){if(!o[d]&&this[u])return!1;for(var t=e(this),i=r.length-1;i>=0;i--)if(r[i]==this){r.splice(i,1);break}t.removeData(m),r.length||(s?cancelAnimationFrame(a):clearTimeout(a),a=null)},add:function(t){function n(t,n,r){var o=e(this),s=o.data(m)||{};s.w=n!==i?n:o.width(),s.h=r!==i?r:o.height(),a.apply(this,arguments)}if(!o[d]&&this[u])return!1;var a;if(e.isFunction(t))return a=t,n;a=t.handler,t.handler=n}},t.requestAnimationFrame||(t.requestAnimationFrame=t.webkitRequestAnimationFrame||t.mozRequestAnimationFrame||t.oRequestAnimationFrame||t.msRequestAnimationFrame||function(e,i){return t.setTimeout(function(){e((new Date).getTime())},o[c])}),t.cancelAnimationFrame||(t.cancelAnimationFrame=t.webkitCancelRequestAnimationFrame||t.mozCancelRequestAnimationFrame||t.oCancelRequestAnimationFrame||t.msCancelRequestAnimationFrame||clearTimeout)}(jQuery,this),function(e){jQuery.plot.plugins.push({init:function(e){function t(){var t=e.getPlaceholder();0!==t.width()&&0!==t.height()&&(e.resize(),e.setupGrid(),e.draw())}e.hooks.bindEvents.push(function(e,i){e.getPlaceholder().resize(t)}),e.hooks.shutdown.push(function(e,i){e.getPlaceholder().unbind("resize",t)})},options:{},name:"resize",version:"1.0"})}(); diff -r f3af77e371a1 -r 9f02c09d2393 src/pyams_skin/resources/js/ext/flot/jquery.flot.saturated.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/pyams_skin/resources/js/ext/flot/jquery.flot.saturated.js Wed Oct 16 13:00:43 2019 +0200 @@ -0,0 +1,43 @@ +(function ($) { + 'use strict'; + var saturated = { + saturate: function (a) { + if (a === Infinity) { + return Number.MAX_VALUE; + } + + if (a === -Infinity) { + return -Number.MAX_VALUE; + } + + return a; + }, + delta: function(min, max, noTicks) { + return ((max - min) / noTicks) === Infinity ? (max / noTicks - min / noTicks) : (max - min) / noTicks + }, + multiply: function (a, b) { + return saturated.saturate(a * b); + }, + // returns c * bInt * a. Beahves properly in the case where c is negative + // and bInt * a is bigger that Number.MAX_VALUE (Infinity) + multiplyAdd: function (a, bInt, c) { + if (isFinite(a * bInt)) { + return saturated.saturate(a * bInt + c); + } else { + var result = c; + + for (var i = 0; i < bInt; i++) { + result += a; + } + + return saturated.saturate(result); + } + }, + // round to nearby lower multiple of base + floorInBase: function(n, base) { + return base * Math.floor(n / base); + } + }; + + $.plot.saturated = saturated; +})(jQuery); diff -r f3af77e371a1 -r 9f02c09d2393 src/pyams_skin/resources/js/ext/flot/jquery.flot.saturated.min.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/pyams_skin/resources/js/ext/flot/jquery.flot.saturated.min.js Wed Oct 16 13:00:43 2019 +0200 @@ -0,0 +1,1 @@ +!function(t){"use strict";var r={saturate:function(t){return t===1/0?Number.MAX_VALUE:t===-1/0?-Number.MAX_VALUE:t},delta:function(t,r,u){return(r-t)/u==1/0?r/u-t/u:(r-t)/u},multiply:function(t,u){return r.saturate(t*u)},multiplyAdd:function(t,u,n){if(isFinite(t*u))return r.saturate(t*u+n);for(var e=n,a=0;a max ? max: value); + return value < min ? min : (value > max ? max : value); + } + + function selectionDirection(plot) { + var o = plot.getOptions(); + + if (o.selection.mode === 'smart') { + return selection.currentMode; + } else { + return o.selection.mode; + } + } + + function updateMode(pos) { + if (selection.first) { + var delta = { + x: pos.x - selection.first.x, + y: pos.y - selection.first.y + }; + + if (Math.abs(delta.x) < SNAPPING_CONSTANT) { + selection.currentMode = 'y'; + } else if (Math.abs(delta.y) < SNAPPING_CONSTANT) { + selection.currentMode = 'x'; + } else { + selection.currentMode = 'xy'; + } + } } function setSelectionPos(pos, e) { - var o = plot.getOptions(); var offset = plot.getPlaceholder().offset(); var plotOffset = plot.getPlotOffset(); pos.x = clamp(0, e.pageX - offset.left - plotOffset.left, plot.width()); pos.y = clamp(0, e.pageY - offset.top - plotOffset.top, plot.height()); - if (o.selection.mode == "y") - pos.x = pos == selection.first ? 0 : plot.width(); + if (pos !== selection.first) updateMode(pos); - if (o.selection.mode == "x") - pos.y = pos == selection.first ? 0 : plot.height(); + if (selectionDirection(plot) === "y") { + pos.x = pos === selection.first ? 0 : plot.width(); + } + + if (selectionDirection(plot) === "x") { + pos.y = pos === selection.first ? 0 : plot.height(); + } } function updateSelection(pos) { - if (pos.pageX == null) - return; + if (pos.pageX == null) return; setSelectionPos(selection.second, pos); if (selectionIsSane()) { selection.show = true; plot.triggerRedrawOverlay(); - } - else - clearSelection(true); + } else clearSelection(true); } function clearSelection(preventEvent) { if (selection.show) { selection.show = false; + selection.currentMode = ''; plot.triggerRedrawOverlay(); - if (!preventEvent) + if (!preventEvent) { plot.getPlaceholder().trigger("plotunselected", [ ]); + } } } @@ -227,10 +287,13 @@ for (var k in axes) { axis = axes[k]; - if (axis.direction == coord) { + if (axis.direction === coord) { key = coord + axis.n + "axis"; - if (!ranges[key] && axis.n == 1) - key = coord + "axis"; // support x1axis as xaxis + if (!ranges[key] && axis.n === 1) { + // support x1axis as xaxis + key = coord + "axis"; + } + if (ranges[key]) { from = ranges[key].from; to = ranges[key].to; @@ -241,7 +304,7 @@ // backwards-compat stuff - to be removed in future if (!ranges[key]) { - axis = coord == "x" ? plot.getXAxes()[0] : plot.getYAxes()[0]; + axis = coord === "x" ? plot.getXAxes()[0] : plot.getYAxes()[0]; from = ranges[coord + "1"]; to = ranges[coord + "2"]; } @@ -252,39 +315,36 @@ from = to; to = tmp; } - + return { from: from, to: to, axis: axis }; } - + function setSelection(ranges, preventEvent) { - var axis, range, o = plot.getOptions(); + var range; - if (o.selection.mode == "y") { + if (selectionDirection(plot) === "y") { selection.first.x = 0; selection.second.x = plot.width(); - } - else { + } else { range = extractRange(ranges, "x"); - selection.first.x = range.axis.p2c(range.from); selection.second.x = range.axis.p2c(range.to); } - if (o.selection.mode == "x") { + if (selectionDirection(plot) === "x") { selection.first.y = 0; selection.second.y = plot.height(); - } - else { + } else { range = extractRange(ranges, "y"); - selection.first.y = range.axis.p2c(range.from); selection.second.y = range.axis.p2c(range.to); } selection.show = true; plot.triggerRedrawOverlay(); - if (!preventEvent && selectionIsSane()) + if (!preventEvent && selectionIsSane()) { triggerSelectedEvent(); + } } function selectionIsSane() { @@ -305,6 +365,88 @@ } }); + function drawSelectionDecorations(ctx, x, y, w, h, oX, oY, mode) { + var spacing = 3; + var fullEarWidth = 15; + var earWidth = Math.max(0, Math.min(fullEarWidth, w / 2 - 2, h / 2 - 2)); + ctx.fillStyle = '#ffffff'; + + if (mode === 'xy') { + ctx.beginPath(); + ctx.moveTo(x, y + earWidth); + ctx.lineTo(x - 3, y + earWidth); + ctx.lineTo(x - 3, y - 3); + ctx.lineTo(x + earWidth, y - 3); + ctx.lineTo(x + earWidth, y); + ctx.lineTo(x, y); + ctx.closePath(); + + ctx.moveTo(x, y + h - earWidth); + ctx.lineTo(x - 3, y + h - earWidth); + ctx.lineTo(x - 3, y + h + 3); + ctx.lineTo(x + earWidth, y + h + 3); + ctx.lineTo(x + earWidth, y + h); + ctx.lineTo(x, y + h); + ctx.closePath(); + + ctx.moveTo(x + w, y + earWidth); + ctx.lineTo(x + w + 3, y + earWidth); + ctx.lineTo(x + w + 3, y - 3); + ctx.lineTo(x + w - earWidth, y - 3); + ctx.lineTo(x + w - earWidth, y); + ctx.lineTo(x + w, y); + ctx.closePath(); + + ctx.moveTo(x + w, y + h - earWidth); + ctx.lineTo(x + w + 3, y + h - earWidth); + ctx.lineTo(x + w + 3, y + h + 3); + ctx.lineTo(x + w - earWidth, y + h + 3); + ctx.lineTo(x + w - earWidth, y + h); + ctx.lineTo(x + w, y + h); + ctx.closePath(); + + ctx.stroke(); + ctx.fill(); + } + + x = oX; + y = oY; + + if (mode === 'x') { + ctx.beginPath(); + ctx.moveTo(x, y + fullEarWidth); + ctx.lineTo(x, y - fullEarWidth); + ctx.lineTo(x - spacing, y - fullEarWidth); + ctx.lineTo(x - spacing, y + fullEarWidth); + ctx.closePath(); + + ctx.moveTo(x + w, y + fullEarWidth); + ctx.lineTo(x + w, y - fullEarWidth); + ctx.lineTo(x + w + spacing, y - fullEarWidth); + ctx.lineTo(x + w + spacing, y + fullEarWidth); + ctx.closePath(); + ctx.stroke(); + ctx.fill(); + } + + if (mode === 'y') { + ctx.beginPath(); + + ctx.moveTo(x - fullEarWidth, y); + ctx.lineTo(x + fullEarWidth, y); + ctx.lineTo(x + fullEarWidth, y - spacing); + ctx.lineTo(x - fullEarWidth, y - spacing); + ctx.closePath(); + + ctx.moveTo(x - fullEarWidth, y + h); + ctx.lineTo(x + fullEarWidth, y + h); + ctx.lineTo(x + fullEarWidth, y + h + spacing); + ctx.lineTo(x - fullEarWidth, y + h + spacing); + ctx.closePath(); + ctx.stroke(); + ctx.fill(); + } + } plot.hooks.drawOverlay.push(function (plot, ctx) { // draw selection @@ -316,32 +458,58 @@ ctx.translate(plotOffset.left, plotOffset.top); var c = $.color.parse(o.selection.color); + var visualization = o.selection.visualization; - ctx.strokeStyle = c.scale('a', 0.8).toString(); + var scalingFactor = 1; + + // use a dimmer scaling factor if visualization is "fill" + if (visualization === "fill") { + scalingFactor = 0.8; + } + + ctx.strokeStyle = c.scale('a', scalingFactor).toString(); ctx.lineWidth = 1; ctx.lineJoin = o.selection.shape; ctx.fillStyle = c.scale('a', 0.4).toString(); var x = Math.min(selection.first.x, selection.second.x) + 0.5, + oX = x, y = Math.min(selection.first.y, selection.second.y) + 0.5, + oY = y, w = Math.abs(selection.second.x - selection.first.x) - 1, h = Math.abs(selection.second.y - selection.first.y) - 1; - ctx.fillRect(x, y, w, h); - ctx.strokeRect(x, y, w, h); + if (selectionDirection(plot) === 'x') { + h += y; + y = 0; + } + + if (selectionDirection(plot) === 'y') { + w += x; + x = 0; + } + + if (visualization === "fill") { + ctx.fillRect(x, y, w, h); + ctx.strokeRect(x, y, w, h); + } else { + ctx.fillRect(0, 0, plot.width(), plot.height()); + ctx.clearRect(x, y, w, h); + drawSelectionDecorations(ctx, x, y, w, h, oX, oY, selectionDirection(plot)); + } ctx.restore(); } }); - + plot.hooks.shutdown.push(function (plot, eventHolder) { eventHolder.unbind("mousemove", onMouseMove); eventHolder.unbind("mousedown", onMouseDown); - - if (mouseUpHandler) + + if (mouseUpHandler) { $(document).unbind("mouseup", mouseUpHandler); + } }); - } $.plot.plugins.push({ @@ -349,7 +517,8 @@ options: { selection: { mode: null, // one of null, "x", "y" or "xy" - color: "#e8cfac", + visualization: "focus", // "focus" or "fill" + color: "#888888", shape: "round", // one of "round", "miter", or "bevel" minSize: 5 // minimum number of pixels } diff -r f3af77e371a1 -r 9f02c09d2393 src/pyams_skin/resources/js/ext/flot/jquery.flot.selection.min.js --- a/src/pyams_skin/resources/js/ext/flot/jquery.flot.selection.min.js Thu Sep 26 11:55:17 2019 +0200 +++ b/src/pyams_skin/resources/js/ext/flot/jquery.flot.selection.min.js Wed Oct 16 13:00:43 2019 +0200 @@ -1,7 +1,1 @@ -/* Javascript plotting library for jQuery, version 0.8.3. - -Copyright (c) 2007-2014 IOLA and Ole Laursen. -Licensed under the MIT license. - -*/ -(function($){function init(plot){var selection={first:{x:-1,y:-1},second:{x:-1,y:-1},show:false,active:false};var savedhandlers={};var mouseUpHandler=null;function onMouseMove(e){if(selection.active){updateSelection(e);plot.getPlaceholder().trigger("plotselecting",[getSelection()])}}function onMouseDown(e){if(e.which!=1)return;document.body.focus();if(document.onselectstart!==undefined&&savedhandlers.onselectstart==null){savedhandlers.onselectstart=document.onselectstart;document.onselectstart=function(){return false}}if(document.ondrag!==undefined&&savedhandlers.ondrag==null){savedhandlers.ondrag=document.ondrag;document.ondrag=function(){return false}}setSelectionPos(selection.first,e);selection.active=true;mouseUpHandler=function(e){onMouseUp(e)};$(document).one("mouseup",mouseUpHandler)}function onMouseUp(e){mouseUpHandler=null;if(document.onselectstart!==undefined)document.onselectstart=savedhandlers.onselectstart;if(document.ondrag!==undefined)document.ondrag=savedhandlers.ondrag;selection.active=false;updateSelection(e);if(selectionIsSane())triggerSelectedEvent();else{plot.getPlaceholder().trigger("plotunselected",[]);plot.getPlaceholder().trigger("plotselecting",[null])}return false}function getSelection(){if(!selectionIsSane())return null;if(!selection.show)return null;var r={},c1=selection.first,c2=selection.second;$.each(plot.getAxes(),function(name,axis){if(axis.used){var p1=axis.c2p(c1[axis.direction]),p2=axis.c2p(c2[axis.direction]);r[name]={from:Math.min(p1,p2),to:Math.max(p1,p2)}}});return r}function triggerSelectedEvent(){var r=getSelection();plot.getPlaceholder().trigger("plotselected",[r]);if(r.xaxis&&r.yaxis)plot.getPlaceholder().trigger("selected",[{x1:r.xaxis.from,y1:r.yaxis.from,x2:r.xaxis.to,y2:r.yaxis.to}])}function clamp(min,value,max){return valuemax?max:value}function setSelectionPos(pos,e){var o=plot.getOptions();var offset=plot.getPlaceholder().offset();var plotOffset=plot.getPlotOffset();pos.x=clamp(0,e.pageX-offset.left-plotOffset.left,plot.width());pos.y=clamp(0,e.pageY-offset.top-plotOffset.top,plot.height());if(o.selection.mode=="y")pos.x=pos==selection.first?0:plot.width();if(o.selection.mode=="x")pos.y=pos==selection.first?0:plot.height()}function updateSelection(pos){if(pos.pageX==null)return;setSelectionPos(selection.second,pos);if(selectionIsSane()){selection.show=true;plot.triggerRedrawOverlay()}else clearSelection(true)}function clearSelection(preventEvent){if(selection.show){selection.show=false;plot.triggerRedrawOverlay();if(!preventEvent)plot.getPlaceholder().trigger("plotunselected",[])}}function extractRange(ranges,coord){var axis,from,to,key,axes=plot.getAxes();for(var k in axes){axis=axes[k];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"?plot.getXAxes()[0]:plot.getYAxes()[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 setSelection(ranges,preventEvent){var axis,range,o=plot.getOptions();if(o.selection.mode=="y"){selection.first.x=0;selection.second.x=plot.width()}else{range=extractRange(ranges,"x");selection.first.x=range.axis.p2c(range.from);selection.second.x=range.axis.p2c(range.to)}if(o.selection.mode=="x"){selection.first.y=0;selection.second.y=plot.height()}else{range=extractRange(ranges,"y");selection.first.y=range.axis.p2c(range.from);selection.second.y=range.axis.p2c(range.to)}selection.show=true;plot.triggerRedrawOverlay();if(!preventEvent&&selectionIsSane())triggerSelectedEvent()}function selectionIsSane(){var minSize=plot.getOptions().selection.minSize;return Math.abs(selection.second.x-selection.first.x)>=minSize&&Math.abs(selection.second.y-selection.first.y)>=minSize}plot.clearSelection=clearSelection;plot.setSelection=setSelection;plot.getSelection=getSelection;plot.hooks.bindEvents.push(function(plot,eventHolder){var o=plot.getOptions();if(o.selection.mode!=null){eventHolder.mousemove(onMouseMove);eventHolder.mousedown(onMouseDown)}});plot.hooks.drawOverlay.push(function(plot,ctx){if(selection.show&&selectionIsSane()){var plotOffset=plot.getPlotOffset();var o=plot.getOptions();ctx.save();ctx.translate(plotOffset.left,plotOffset.top);var c=$.color.parse(o.selection.color);ctx.strokeStyle=c.scale("a",.8).toString();ctx.lineWidth=1;ctx.lineJoin=o.selection.shape;ctx.fillStyle=c.scale("a",.4).toString();var x=Math.min(selection.first.x,selection.second.x)+.5,y=Math.min(selection.first.y,selection.second.y)+.5,w=Math.abs(selection.second.x-selection.first.x)-1,h=Math.abs(selection.second.y-selection.first.y)-1;ctx.fillRect(x,y,w,h);ctx.strokeRect(x,y,w,h);ctx.restore()}});plot.hooks.shutdown.push(function(plot,eventHolder){eventHolder.unbind("mousemove",onMouseMove);eventHolder.unbind("mousedown",onMouseDown);if(mouseUpHandler)$(document).unbind("mouseup",mouseUpHandler)})}$.plot.plugins.push({init:init,options:{selection:{mode:null,color:"#e8cfac",shape:"round",minSize:5}},name:"selection",version:"1.1"})})(jQuery); \ No newline at end of file +!function(e){e.plot.plugins.push({init:function(o){function t(e){m.active&&(d(e),o.getPlaceholder().trigger("plotselecting",[l()]))}function n(t){var n=o.getOptions();1===t.which&&null!==n.selection.mode&&(m.currentMode="xy",document.body.focus(),void 0!==document.onselectstart&&null==v.onselectstart&&(v.onselectstart=document.onselectstart,document.onselectstart=function(){return!1}),void 0!==document.ondrag&&null==v.ondrag&&(v.ondrag=document.ondrag,document.ondrag=function(){return!1}),u(m.first,t),m.active=!0,T=function(e){i(e)},e(document).one("mouseup",T))}function i(e){return T=null,void 0!==document.onselectstart&&(document.onselectstart=v.onselectstart),void 0!==document.ondrag&&(document.ondrag=v.ondrag),m.active=!1,d(e),g()?r():(o.getPlaceholder().trigger("plotunselected",[]),o.getPlaceholder().trigger("plotselecting",[null])),!1}function l(){if(!g())return null;if(!m.show)return null;var t={},n={x:m.first.x,y:m.first.y},i={x:m.second.x,y:m.second.y};return"x"===c(o)&&(n.y=0,i.y=o.height()),"y"===c(o)&&(n.x=0,i.x=o.width()),e.each(o.getAxes(),function(e,o){if(o.used){var l=o.c2p(n[o.direction]),r=o.c2p(i[o.direction]);t[e]={from:Math.min(l,r),to:Math.max(l,r)}}}),t}function r(){var e=l();o.getPlaceholder().trigger("plotselected",[e]),e.xaxis&&e.yaxis&&o.getPlaceholder().trigger("selected",[{x1:e.xaxis.from,y1:e.yaxis.from,x2:e.xaxis.to,y2:e.yaxis.to}])}function s(e,o,t){return ot?t:o}function c(e){var o=e.getOptions();return"smart"===o.selection.mode?m.currentMode:o.selection.mode}function a(e){if(m.first){var o={x:e.x-m.first.x,y:e.y-m.first.y};Math.abs(o.x)l){var a=i;i=l,l=a}return{from:i,to:l,axis:n}}function g(){var e=o.getOptions().selection.minSize;return Math.abs(m.second.x-m.first.x)>=e&&Math.abs(m.second.y-m.first.y)>=e}function x(e,o,t,n,i,l,r,s){var c=Math.max(0,Math.min(15,n/2-2,i/2-2));e.fillStyle="#ffffff","xy"===s&&(e.beginPath(),e.moveTo(o,t+c),e.lineTo(o-3,t+c),e.lineTo(o-3,t-3),e.lineTo(o+c,t-3),e.lineTo(o+c,t),e.lineTo(o,t),e.closePath(),e.moveTo(o,t+i-c),e.lineTo(o-3,t+i-c),e.lineTo(o-3,t+i+3),e.lineTo(o+c,t+i+3),e.lineTo(o+c,t+i),e.lineTo(o,t+i),e.closePath(),e.moveTo(o+n,t+c),e.lineTo(o+n+3,t+c),e.lineTo(o+n+3,t-3),e.lineTo(o+n-c,t-3),e.lineTo(o+n-c,t),e.lineTo(o+n,t),e.closePath(),e.moveTo(o+n,t+i-c),e.lineTo(o+n+3,t+i-c),e.lineTo(o+n+3,t+i+3),e.lineTo(o+n-c,t+i+3),e.lineTo(o+n-c,t+i),e.lineTo(o+n,t+i),e.closePath(),e.stroke(),e.fill()),o=l,t=r,"x"===s&&(e.beginPath(),e.moveTo(o,t+15),e.lineTo(o,t-15),e.lineTo(o-3,t-15),e.lineTo(o-3,t+15),e.closePath(),e.moveTo(o+n,t+15),e.lineTo(o+n,t-15),e.lineTo(o+n+3,t-15),e.lineTo(o+n+3,t+15),e.closePath(),e.stroke(),e.fill()),"y"===s&&(e.beginPath(),e.moveTo(o-15,t),e.lineTo(o+15,t),e.lineTo(o+15,t-3),e.lineTo(o-15,t-3),e.closePath(),e.moveTo(o-15,t+i),e.lineTo(o+15,t+i),e.lineTo(o+15,t+i+3),e.lineTo(o-15,t+i+3),e.closePath(),e.stroke(),e.fill())}var m={first:{x:-1,y:-1},second:{x:-1,y:-1},show:!1,currentMode:"xy",active:!1},y=e.plot.uiConstants.SNAPPING_CONSTANT,v={},T=null;o.clearSelection=f,o.setSelection=function(e,t){var n;"y"===c(o)?(m.first.x=0,m.second.x=o.width()):(n=h(e,"x"),m.first.x=n.axis.p2c(n.from),m.second.x=n.axis.p2c(n.to)),"x"===c(o)?(m.first.y=0,m.second.y=o.height()):(n=h(e,"y"),m.first.y=n.axis.p2c(n.from),m.second.y=n.axis.p2c(n.to)),m.show=!0,o.triggerRedrawOverlay(),!t&&g()&&r()},o.getSelection=l,o.hooks.bindEvents.push(function(e,o){null!=e.getOptions().selection.mode&&(o.mousemove(t),o.mousedown(n))}),o.hooks.drawOverlay.push(function(o,t){if(m.show&&g()){var n=o.getPlotOffset(),i=o.getOptions();t.save(),t.translate(n.left,n.top);var l=e.color.parse(i.selection.color),r=i.selection.visualization,s=1;"fill"===r&&(s=.8),t.strokeStyle=l.scale("a",s).toString(),t.lineWidth=1,t.lineJoin=i.selection.shape,t.fillStyle=l.scale("a",.4).toString();var a=Math.min(m.first.x,m.second.x)+.5,u=a,d=Math.min(m.first.y,m.second.y)+.5,f=d,h=Math.abs(m.second.x-m.first.x)-1,y=Math.abs(m.second.y-m.first.y)-1;"x"===c(o)&&(y+=d,d=0),"y"===c(o)&&(h+=a,a=0),"fill"===r?(t.fillRect(a,d,h,y),t.strokeRect(a,d,h,y)):(t.fillRect(0,0,o.width(),o.height()),t.clearRect(a,d,h,y),x(t,a,d,h,y,u,f,c(o))),t.restore()}}),o.hooks.shutdown.push(function(o,i){i.unbind("mousemove",t),i.unbind("mousedown",n),T&&e(document).unbind("mouseup",T)})},options:{selection:{mode:null,visualization:"focus",color:"#888888",shape:"round",minSize:5}},name:"selection",version:"1.1"})}(jQuery); diff -r f3af77e371a1 -r 9f02c09d2393 src/pyams_skin/resources/js/ext/flot/jquery.flot.stack.js --- a/src/pyams_skin/resources/js/ext/flot/jquery.flot.stack.js Thu Sep 26 11:55:17 2019 +0200 +++ b/src/pyams_skin/resources/js/ext/flot/jquery.flot.stack.js Wed Oct 16 13:00:43 2019 +0200 @@ -1,4 +1,4 @@ -/* Flot plugin for stacking data sets rather than overlyaing them. +/* Flot plugin for stacking data sets rather than overlaying them. Copyright (c) 2007-2014 IOLA and Ole Laursen. Licensed under the MIT license. @@ -14,16 +14,16 @@ key (which can be any number or string or just "true"). To specify the default stack, you can set the stack option like this: - series: { - stack: null/false, true, or a key (number/string) - } + series: { + stack: null/false, true, or a key (number/string) + } You can also specify it for a single series, like this: - $.plot( $("#placeholder"), [{ - data: [ ... ], - stack: true - }]) + $.plot( $("#placeholder"), [{ + data: [ ... ], + stack: true + }]) The stacking order is determined by the order of the data series in the array (later series end up on top of the previous). @@ -39,28 +39,53 @@ var options = { series: { stack: null } // or number/string }; - + function init(plot) { function findMatchingSeries(s, allseries) { var res = null; for (var i = 0; i < allseries.length; ++i) { - if (s == allseries[i]) - break; - - if (allseries[i].stack == s.stack) + if (s === allseries[i]) break; + + if (allseries[i].stack === s.stack) { res = allseries[i]; + } } - + return res; } - + + function addBottomPoints (s, datapoints) { + var formattedPoints = []; + for (var i = 0; i < datapoints.points.length; i += 2) { + formattedPoints.push(datapoints.points[i]); + formattedPoints.push(datapoints.points[i + 1]); + formattedPoints.push(0); + } + + datapoints.format.push({ + x: false, + y: true, + number: true, + required: false, + computeRange: s.yaxis.options.autoScale !== 'none', + defaultValue: 0 + }); + datapoints.points = formattedPoints; + datapoints.pointsize = 3; + } + function stackData(plot, s, datapoints) { - if (s.stack == null || s.stack === false) - return; + if (s.stack == null || s.stack === false) return; + + var needsBottom = s.bars.show || (s.lines.show && s.lines.fill); + var hasBottom = datapoints.pointsize > 2 && (horizontal ? datapoints.format[2].x : datapoints.format[2].y); + // Series data is missing bottom points - need to format + if (needsBottom && !hasBottom) { + addBottomPoints(s, datapoints); + } var other = findMatchingSeries(s, plot.getData()); - if (!other) - return; + if (!other) return; var ps = datapoints.pointsize, points = datapoints.points, @@ -70,7 +95,6 @@ px, py, intery, qx, qy, bottom, withlines = s.lines.show, horizontal = s.bars.horizontal, - withbottom = ps > 2 && (horizontal ? datapoints.format[2].x : datapoints.format[2].y), withsteps = withlines && s.lines.steps, fromgap = true, keyOffset = horizontal ? 1 : 0, @@ -78,33 +102,35 @@ i = 0, j = 0, l, m; while (true) { - if (i >= points.length) - break; + if (i >= points.length) break; l = newpoints.length; if (points[i] == null) { // copy gaps - for (m = 0; m < ps; ++m) + for (m = 0; m < ps; ++m) { newpoints.push(points[i + m]); + } + i += ps; - } - else if (j >= otherpoints.length) { + } else if (j >= otherpoints.length) { // for lines, we can't use the rest of the points if (!withlines) { - for (m = 0; m < ps; ++m) + for (m = 0; m < ps; ++m) { newpoints.push(points[i + m]); + } } + i += ps; - } - else if (otherpoints[j] == null) { + } else if (otherpoints[j] == null) { // oops, got a gap - for (m = 0; m < ps; ++m) + for (m = 0; m < ps; ++m) { newpoints.push(null); + } + fromgap = true; j += otherps; - } - else { + } else { // cases where we actually got two points px = points[i + keyOffset]; py = points[i + accumulateOffset]; @@ -112,73 +138,79 @@ qy = otherpoints[j + accumulateOffset]; bottom = 0; - if (px == qx) { - for (m = 0; m < ps; ++m) + if (px === qx) { + for (m = 0; m < ps; ++m) { newpoints.push(points[i + m]); + } newpoints[l + accumulateOffset] += qy; bottom = qy; - + i += ps; j += otherps; - } - else if (px > qx) { + } else if (px > qx) { // we got past point below, might need to // insert interpolated extra point if (withlines && i > 0 && points[i - ps] != null) { intery = py + (points[i - ps + accumulateOffset] - py) * (qx - px) / (points[i - ps + keyOffset] - px); newpoints.push(qx); newpoints.push(intery + qy); - for (m = 2; m < ps; ++m) + for (m = 2; m < ps; ++m) { newpoints.push(points[i + m]); - bottom = qy; + } + + bottom = qy; } j += otherps; - } - else { // px < qx + } else { // px < qx if (fromgap && withlines) { // if we come from a gap, we just skip this point i += ps; continue; } - - for (m = 0; m < ps; ++m) + + for (m = 0; m < ps; ++m) { newpoints.push(points[i + m]); - + } + // we might be able to interpolate a point below, // this can give us a better y - if (withlines && j > 0 && otherpoints[j - otherps] != null) + if (withlines && j > 0 && otherpoints[j - otherps] != null) { bottom = qy + (otherpoints[j - otherps + accumulateOffset] - qy) * (px - qx) / (otherpoints[j - otherps + keyOffset] - qx); + } newpoints[l + accumulateOffset] += bottom; - + i += ps; } fromgap = false; - - if (l != newpoints.length && withbottom) + + if (l !== newpoints.length && needsBottom) { newpoints[l + 2] += bottom; + } } // maintain the line steps invariant - if (withsteps && l != newpoints.length && l > 0 - && newpoints[l] != null - && newpoints[l] != newpoints[l - ps] - && newpoints[l + 1] != newpoints[l - ps + 1]) { - for (m = 0; m < ps; ++m) + if (withsteps && l !== newpoints.length && l > 0 && + newpoints[l] !== null && + newpoints[l] !== newpoints[l - ps] && + newpoints[l + 1] !== newpoints[l - ps + 1]) { + for (m = 0; m < ps; ++m) { newpoints[l + ps + m] = newpoints[l + m]; + } + newpoints[l + 1] = newpoints[l - ps + 1]; } } datapoints.points = newpoints; } - + plot.hooks.processDatapoints.push(stackData); } - + $.plot.plugins.push({ init: init, options: options, diff -r f3af77e371a1 -r 9f02c09d2393 src/pyams_skin/resources/js/ext/flot/jquery.flot.stack.min.js --- a/src/pyams_skin/resources/js/ext/flot/jquery.flot.stack.min.js Thu Sep 26 11:55:17 2019 +0200 +++ b/src/pyams_skin/resources/js/ext/flot/jquery.flot.stack.min.js Wed Oct 16 13:00:43 2019 +0200 @@ -1,1 +1,1 @@ -!function(s){jQuery.plot.plugins.push({init:function(s){function n(s,n){for(var t=null,i=0;i2&&(d?i.format[2].x:i.format[2].y),D=z&&t.lines.steps,b=!0,j=d?1:0,w=d?0:1,x=0,Q=0;!(x>=g.length);){if(r=m.length,null==g[x]){for(h=0;h=v.length){if(!z)for(h=0;hf){if(z&&x>0&&null!=g[x-c]){for(u=e+(g[x-c+w]-e)*(f-o)/(g[x-c+j]-o),m.push(f),m.push(u+p),h=2;h0&&null!=v[Q-k]&&(a=p+(v[Q-k+w]-p)*(o-f)/(v[Q-k+j]-f)),m[r+w]+=a,x+=c}b=!1,r!=m.length&&y&&(m[r+2]+=a)}if(D&&r!=m.length&&r>0&&null!=m[r]&&m[r]!=m[r-c]&&m[r+1]!=m[r-c+1]){for(h=0;h2&&(w?o.format[2].x:o.format[2].y);e&&!l&&t(i,o);var u=n(i,s.getData());if(u){for(var p,a,f,r,h,c,g,k,m=o.pointsize,v=o.points,z=u.datapoints.pointsize,d=u.datapoints.points,y=[],b=i.lines.show,w=i.bars.horizontal,x=b&&i.lines.steps,D=!0,j=w?1:0,q=w?0:1,Q=0,R=0;!(Q>=v.length);){if(g=y.length,null==v[Q]){for(k=0;k=d.length){if(!b)for(k=0;kr){if(b&&Q>0&&null!=v[Q-m]){for(f=a+(v[Q-m+q]-a)*(r-p)/(v[Q-m+j]-p),y.push(r),y.push(f+h),k=2;k0&&null!=d[R-z]&&(c=h+(d[R-z+q]-h)*(p-r)/(d[R-z+j]-r)),y[g+q]+=c,Q+=m}D=!1,g!==y.length&&e&&(y[g+2]+=c)}if(x&&g!==y.length&&g>0&&null!==y[g]&&y[g]!==y[g-m]&&y[g+1]!==y[g-m+1]){for(k=0;k s = r * sqrt(pi)/2 - var size = radius * Math.sqrt(Math.PI) / 2; - ctx.rect(x - size, y - size, size + size, size + size); - }, - diamond: function (ctx, x, y, radius, shadow) { - // pi * r^2 = 2s^2 => s = r * sqrt(pi/2) - var size = radius * Math.sqrt(Math.PI / 2); - ctx.moveTo(x - size, y); - ctx.lineTo(x, y - size); - ctx.lineTo(x + size, y); - ctx.lineTo(x, y + size); - ctx.lineTo(x - size, y); - }, - triangle: function (ctx, x, y, radius, shadow) { - // pi * r^2 = 1/2 * s^2 * sin (pi / 3) => s = r * sqrt(2 * pi / sin(pi / 3)) - var size = radius * Math.sqrt(2 * Math.PI / Math.sin(Math.PI / 3)); - var height = size * Math.sin(Math.PI / 3); - ctx.moveTo(x - size/2, y + height/2); - ctx.lineTo(x + size/2, y + height/2); - if (!shadow) { - ctx.lineTo(x, y - height/2); - ctx.lineTo(x - size/2, y + height/2); - } - }, - cross: function (ctx, x, y, radius, shadow) { - // pi * r^2 = (2s)^2 => s = r * sqrt(pi)/2 - var size = radius * Math.sqrt(Math.PI) / 2; - ctx.moveTo(x - size, y - size); - ctx.lineTo(x + size, y + size); - ctx.moveTo(x - size, y + size); - ctx.lineTo(x + size, y - size); + var square = function (ctx, x, y, radius, shadow) { + // pi * r^2 = (2s)^2 => s = r * sqrt(pi)/2 + var size = radius * Math.sqrt(Math.PI) / 2; + ctx.rect(x - size, y - size, size + size, size + size); + }, + rectangle = function (ctx, x, y, radius, shadow) { + // pi * r^2 = (2s)^2 => s = r * sqrt(pi)/2 + var size = radius * Math.sqrt(Math.PI) / 2; + ctx.rect(x - size, y - size, size + size, size + size); + }, + diamond = function (ctx, x, y, radius, shadow) { + // pi * r^2 = 2s^2 => s = r * sqrt(pi/2) + var size = radius * Math.sqrt(Math.PI / 2); + ctx.moveTo(x - size, y); + ctx.lineTo(x, y - size); + ctx.lineTo(x + size, y); + ctx.lineTo(x, y + size); + ctx.lineTo(x - size, y); + ctx.lineTo(x, y - size); + }, + triangle = function (ctx, x, y, radius, shadow) { + // pi * r^2 = 1/2 * s^2 * sin (pi / 3) => s = r * sqrt(2 * pi / sin(pi / 3)) + var size = radius * Math.sqrt(2 * Math.PI / Math.sin(Math.PI / 3)); + var height = size * Math.sin(Math.PI / 3); + ctx.moveTo(x - size / 2, y + height / 2); + ctx.lineTo(x + size / 2, y + height / 2); + if (!shadow) { + ctx.lineTo(x, y - height / 2); + ctx.lineTo(x - size / 2, y + height / 2); + ctx.lineTo(x + size / 2, y + height / 2); } + }, + cross = function (ctx, x, y, radius, shadow) { + // pi * r^2 = (2s)^2 => s = r * sqrt(pi)/2 + var size = radius * Math.sqrt(Math.PI) / 2; + ctx.moveTo(x - size, y - size); + ctx.lineTo(x + size, y + size); + ctx.moveTo(x - size, y + size); + ctx.lineTo(x + size, y - size); + }, + ellipse = function(ctx, x, y, radius, shadow, fill) { + if (!shadow) { + ctx.moveTo(x + radius, y); + ctx.arc(x, y, radius, 0, Math.PI * 2, false); + } + }, + plus = function (ctx, x, y, radius, shadow) { + var size = radius * Math.sqrt(Math.PI / 2); + ctx.moveTo(x - size, y); + ctx.lineTo(x + size, y); + ctx.moveTo(x, y + size); + ctx.lineTo(x, y - size); + }, + handlers = { + square: square, + rectangle: rectangle, + diamond: diamond, + triangle: triangle, + cross: cross, + ellipse: ellipse, + plus: plus }; - var s = series.points.symbol; - if (handlers[s]) - series.points.symbol = handlers[s]; + square.fill = true; + rectangle.fill = true; + diamond.fill = true; + triangle.fill = true; + ellipse.fill = true; + + function init(plot) { + plot.drawSymbol = handlers; } - - function init(plot) { - plot.hooks.processDatapoints.push(processRawData); - } - + $.plot.plugins.push({ init: init, name: 'symbols', diff -r f3af77e371a1 -r 9f02c09d2393 src/pyams_skin/resources/js/ext/flot/jquery.flot.symbol.min.js --- a/src/pyams_skin/resources/js/ext/flot/jquery.flot.symbol.min.js Thu Sep 26 11:55:17 2019 +0200 +++ b/src/pyams_skin/resources/js/ext/flot/jquery.flot.symbol.min.js Wed Oct 16 13:00:43 2019 +0200 @@ -1,7 +1,1 @@ -/* Javascript plotting library for jQuery, version 0.8.3. - -Copyright (c) 2007-2014 IOLA and Ole Laursen. -Licensed under the MIT license. - -*/ -(function($){function processRawData(plot,series,datapoints){var handlers={square:function(ctx,x,y,radius,shadow){var size=radius*Math.sqrt(Math.PI)/2;ctx.rect(x-size,y-size,size+size,size+size)},diamond:function(ctx,x,y,radius,shadow){var size=radius*Math.sqrt(Math.PI/2);ctx.moveTo(x-size,y);ctx.lineTo(x,y-size);ctx.lineTo(x+size,y);ctx.lineTo(x,y+size);ctx.lineTo(x-size,y)},triangle:function(ctx,x,y,radius,shadow){var size=radius*Math.sqrt(2*Math.PI/Math.sin(Math.PI/3));var height=size*Math.sin(Math.PI/3);ctx.moveTo(x-size/2,y+height/2);ctx.lineTo(x+size/2,y+height/2);if(!shadow){ctx.lineTo(x,y-height/2);ctx.lineTo(x-size/2,y+height/2)}},cross:function(ctx,x,y,radius,shadow){var size=radius*Math.sqrt(Math.PI)/2;ctx.moveTo(x-size,y-size);ctx.lineTo(x+size,y+size);ctx.moveTo(x-size,y+size);ctx.lineTo(x+size,y-size)}};var s=series.points.symbol;if(handlers[s])series.points.symbol=handlers[s]}function init(plot){plot.hooks.processDatapoints.push(processRawData)}$.plot.plugins.push({init:init,name:"symbols",version:"1.0"})})(jQuery); \ No newline at end of file +!function(o){var n=function(o,n,t,i,l){var a=i*Math.sqrt(Math.PI)/2;o.rect(n-a,t-a,a+a,a+a)},t=function(o,n,t,i,l){var a=i*Math.sqrt(Math.PI)/2;o.rect(n-a,t-a,a+a,a+a)},i=function(o,n,t,i,l){var a=i*Math.sqrt(Math.PI/2);o.moveTo(n-a,t),o.lineTo(n,t-a),o.lineTo(n+a,t),o.lineTo(n,t+a),o.lineTo(n-a,t),o.lineTo(n,t-a)},l=function(o,n,t,i,l){var a=i*Math.sqrt(2*Math.PI/Math.sin(Math.PI/3)),e=a*Math.sin(Math.PI/3);o.moveTo(n-a/2,t+e/2),o.lineTo(n+a/2,t+e/2),l||(o.lineTo(n,t-e/2),o.lineTo(n-a/2,t+e/2),o.lineTo(n+a/2,t+e/2))},a=function(o,n,t,i,l,a){l||(o.moveTo(n+i,t),o.arc(n,t,i,0,2*Math.PI,!1))},e={square:n,rectangle:t,diamond:i,triangle:l,cross:function(o,n,t,i,l){var a=i*Math.sqrt(Math.PI)/2;o.moveTo(n-a,t-a),o.lineTo(n+a,t+a),o.moveTo(n-a,t+a),o.lineTo(n+a,t-a)},ellipse:a,plus:function(o,n,t,i,l){var a=i*Math.sqrt(Math.PI/2);o.moveTo(n-a,t),o.lineTo(n+a,t),o.moveTo(n,t+a),o.lineTo(n,t-a)}};n.fill=!0,t.fill=!0,i.fill=!0,l.fill=!0,a.fill=!0,o.plot.plugins.push({init:function(o){o.drawSymbol=e},name:"symbols",version:"1.0"})}(jQuery); diff -r f3af77e371a1 -r 9f02c09d2393 src/pyams_skin/resources/js/ext/flot/jquery.flot.threshold.js --- a/src/pyams_skin/resources/js/ext/flot/jquery.flot.threshold.js Thu Sep 26 11:55:17 2019 +0200 +++ b/src/pyams_skin/resources/js/ext/flot/jquery.flot.threshold.js Wed Oct 16 13:00:43 2019 +0200 @@ -5,29 +5,29 @@ The plugin supports these options: - series: { - threshold: { - below: number - color: colorspec - } - } + series: { + threshold: { + below: number + color: colorspec + } + } It can also be applied to a single series, like this: - $.plot( $("#placeholder"), [{ - data: [ ... ], - threshold: { ... } - }]) + $.plot( $("#placeholder"), [{ + data: [ ... ], + threshold: { ... } + }]) An array can be passed for multiple thresholding, like this: - threshold: [{ - below: number1 - color: color1 - },{ - below: number2 - color: color2 - }] + threshold: [{ + below: number1 + color: color1 + },{ + below: number2 + color: color2 + }] These multiple threshold objects can be passed in any order since they are sorted by the processing function. @@ -46,7 +46,7 @@ var options = { series: { threshold: null } // or { below: number, color: color spec} }; - + function init(plot) { function thresholdData(plot, s, datapoints, below, color) { var ps = datapoints.pointsize, i, x, y, p, prevp, @@ -58,7 +58,7 @@ thresholded.threshold = null; thresholded.originSeries = s; thresholded.data = []; - + var origpoints = datapoints.points, addCrossingPoints = s.lines.show; @@ -71,68 +71,69 @@ y = origpoints[i + 1]; prevp = p; - if (y < below) - p = threspoints; - else - p = newpoints; + if (y < below) p = threspoints; + else p = newpoints; - if (addCrossingPoints && prevp != p && x != null - && i > 0 && origpoints[i - ps] != null) { + if (addCrossingPoints && prevp !== p && + x !== null && i > 0 && + origpoints[i - ps] != null) { var interx = x + (below - y) * (x - origpoints[i - ps]) / (y - origpoints[i - ps + 1]); prevp.push(interx); prevp.push(below); - for (m = 2; m < ps; ++m) + for (m = 2; m < ps; ++m) { prevp.push(origpoints[i + m]); - + } + p.push(null); // start new segment p.push(null); - for (m = 2; m < ps; ++m) + for (m = 2; m < ps; ++m) { p.push(origpoints[i + m]); + } + p.push(interx); p.push(below); - for (m = 2; m < ps; ++m) + for (m = 2; m < ps; ++m) { p.push(origpoints[i + m]); + } } p.push(x); p.push(y); - for (m = 2; m < ps; ++m) + for (m = 2; m < ps; ++m) { p.push(origpoints[i + m]); + } } datapoints.points = newpoints; thresholded.datapoints.points = threspoints; - + if (thresholded.datapoints.points.length > 0) { var origIndex = $.inArray(s, plot.getData()); // Insert newly-generated series right after original one (to prevent it from becoming top-most) plot.getData().splice(origIndex + 1, 0, thresholded); } - + // FIXME: there are probably some edge cases left in bars } - + function processThresholds(plot, s, datapoints) { - if (!s.threshold) - return; - + if (!s.threshold) return; if (s.threshold instanceof Array) { s.threshold.sort(function(a, b) { return a.below - b.below; }); - + $(s.threshold).each(function(i, th) { thresholdData(plot, s, datapoints, th.below, th.color); }); - } - else { + } else { thresholdData(plot, s, datapoints, s.threshold.below, s.threshold.color); } } - + plot.hooks.processDatapoints.push(processThresholds); } - + $.plot.plugins.push({ init: init, options: options, diff -r f3af77e371a1 -r 9f02c09d2393 src/pyams_skin/resources/js/ext/flot/jquery.flot.threshold.min.js --- a/src/pyams_skin/resources/js/ext/flot/jquery.flot.threshold.min.js Thu Sep 26 11:55:17 2019 +0200 +++ b/src/pyams_skin/resources/js/ext/flot/jquery.flot.threshold.min.js Wed Oct 16 13:00:43 2019 +0200 @@ -1,1 +1,1 @@ -!function(o){o.plot.plugins.push({init:function(s){function t(s,t,n,l,h){var r,e,i,p,u,a=n.pointsize,f=o.extend({},t);f.datapoints={points:[],pointsize:a,format:n.format},f.label=null,f.color=h,f.threshold=null,f.originSeries=t,f.data=[];var d,c=n.points,g=t.lines.show,b=[],v=[];for(r=0;r0&&null!=c[r-a]){var w=e+(l-i)*(e-c[r-a])/(i-c[r-a+1]);for(u.push(w),u.push(l),d=2;d0){var m=o.inArray(t,s.getData());s.getData().splice(m+1,0,f)}}s.hooks.processDatapoints.push(function(s,n,l){n.threshold&&(n.threshold instanceof Array?(n.threshold.sort(function(o,s){return o.below-s.below}),o(n.threshold).each(function(o,h){t(s,n,l,h.below,h.color)})):t(s,n,l,n.threshold.below,n.threshold.color))})},options:{series:{threshold:null}},name:"threshold",version:"1.2"})}(jQuery); +!function(o){o.plot.plugins.push({init:function(s){function t(s,t,n,l,h){var r,e,i,p,u,a=n.pointsize,f=o.extend({},t);f.datapoints={points:[],pointsize:a,format:n.format},f.label=null,f.color=h,f.threshold=null,f.originSeries=t,f.data=[];var d,c=n.points,g=t.lines.show,b=[],v=[];for(r=0;r0&&null!=c[r-a]){var w=e+(l-i)*(e-c[r-a])/(i-c[r-a+1]);for(u.push(w),u.push(l),d=2;d0){var m=o.inArray(t,s.getData());s.getData().splice(m+1,0,f)}}s.hooks.processDatapoints.push(function(s,n,l){n.threshold&&(n.threshold instanceof Array?(n.threshold.sort(function(o,s){return o.below-s.below}),o(n.threshold).each(function(o,h){t(s,n,l,h.below,h.color)})):t(s,n,l,n.threshold.below,n.threshold.color))})},options:{series:{threshold:null}},name:"threshold",version:"1.2"})}(jQuery); diff -r f3af77e371a1 -r 9f02c09d2393 src/pyams_skin/resources/js/ext/flot/jquery.flot.time.js --- a/src/pyams_skin/resources/js/ext/flot/jquery.flot.time.js Thu Sep 26 11:55:17 2019 +0200 +++ b/src/pyams_skin/resources/js/ext/flot/jquery.flot.time.js Wed Oct 16 13:00:43 2019 +0200 @@ -5,56 +5,113 @@ Set axis.mode to "time" to enable. See the section "Time series data" in API.txt for details. - */ (function($) { + 'use strict'; - var options = { - xaxis: { - timezone: null, // "browser" for local to the client or timezone for timezone-js - timeformat: null, // format string to use - twelveHourClock: false, // 12 or 24 time in time mode - monthNames: null // list of names of months - } - }; + var options = { + xaxis: { + timezone: null, // "browser" for local to the client or timezone for timezone-js + timeformat: null, // format string to use + twelveHourClock: false, // 12 or 24 time in time mode + monthNames: null, // list of names of months + timeBase: 'seconds' // are the values in given in mircoseconds, milliseconds or seconds + }, + yaxis: { + timeBase: 'seconds' + } + }; + + var floorInBase = $.plot.saturated.floorInBase; - // round to nearby lower multiple of base + // Method to provide microsecond support to Date like classes. + var CreateMicroSecondDate = function(dateType, microEpoch) { + var newDate = new dateType(microEpoch); + + var oldSetTime = newDate.setTime.bind(newDate); + newDate.update = function(microEpoch) { + oldSetTime(microEpoch); + + // Round epoch to 3 decimal accuracy + microEpoch = Math.round(microEpoch*1000)/1000; + + // Microseconds are stored as integers + var seconds = microEpoch/1000; + this.microseconds = 1000000 * (seconds - Math.floor(seconds)); + }; - function floorInBase(n, base) { - return base * Math.floor(n / base); - } + var oldGetTime = newDate.getTime.bind(newDate); + newDate.getTime = function () { + var microEpoch = oldGetTime() + this.microseconds / 1000; + return microEpoch; + }; - // Returns a string with the date d formatted according to fmt. - // A subset of the Open Group's strftime format is supported. + newDate.setTime = function (microEpoch) { + this.update(microEpoch); + }; + + newDate.getMicroseconds = function() { + return this.microseconds; + }; - function formatDate(d, fmt, monthNames, dayNames) { + newDate.setMicroseconds = function(microseconds) { + // Replace the microsecond part (6 last digits) in microEpoch + var epochWithoutMicroseconds = oldGetTime(); + var newEpoch = epochWithoutMicroseconds + microseconds/1000; + this.update(newEpoch); + }; + + newDate.setUTCMicroseconds = function(microseconds) { this.setMicroseconds(microseconds); } + + newDate.getUTCMicroseconds = function() { return this.getMicroseconds(); } - if (typeof d.strftime == "function") { - return d.strftime(fmt); - } + newDate.microseconds = null; + newDate.microEpoch = null; + newDate.update(microEpoch); + return newDate; + } - var leftPad = function(n, pad) { + // Returns a string with the date d formatted according to fmt. + // A subset of the Open Group's strftime format is supported. + + function formatDate(d, fmt, monthNames, dayNames) { + if (typeof d.strftime === "function") { + return d.strftime(fmt); + } + + var leftPad = function(n, pad) { n = "" + n; pad = "" + (pad == null ? "0" : pad); return n.length == 1 ? pad + n : n; }; - var r = []; - var escape = false; - var hours = d.getHours(); + var formatMicroseconds = function(n, dec) { + if (dec < 6 && dec > 0) { + var magnitude = parseFloat('1e' + (dec-6)); + n = Math.round(Math.round(n*magnitude)/magnitude); + n = ('00000' + n).slice(-6,-(6 - dec)); + } else { + n = Math.round(n) + n = ('00000' + n).slice(-6); + } + return n; + }; + + var r = []; + var escape = false; + var hours = d.getHours(); var isAM = hours < 12; - if (monthNames == null) { - monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; - } + if (!monthNames) { + monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; + } - if (dayNames == null) { - dayNames = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; - } + if (!dayNames) { + dayNames = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; + } - var hours12; - + var hours12; if (hours > 12) { hours12 = hours - 12; } else if (hours == 0) { @@ -63,11 +120,13 @@ hours12 = hours; } - for (var i = 0; i < fmt.length; ++i) { - + var decimals = -1; + for (var i = 0; i < fmt.length; ++i) { var c = fmt.charAt(i); - if (escape) { + if (!isNaN(Number(c)) && Number(c) > 0) { + decimals = Number(c); + } else if (escape) { switch (c) { case 'a': c = "" + dayNames[d.getDay()]; break; case 'b': c = "" + monthNames[d.getMonth()]; break; @@ -83,6 +142,7 @@ case 'q': c = "" + (Math.floor(d.getMonth() / 3) + 1); break; case 'S': c = leftPad(d.getSeconds()); break; + case 's': c = "" + formatMicroseconds(d.getMicroseconds(),decimals); break; case 'y': c = leftPad(d.getFullYear() % 100); break; case 'Y': c = "" + d.getFullYear(); break; case 'p': c = (isAM) ? ("" + "am") : ("" + "pm"); break; @@ -101,285 +161,377 @@ } return r.join(""); - } + } - // To have a consistent view of time-based data independent of which time - // zone the client happens to be in we need a date-like object independent - // of time zones. This is done through a wrapper that only calls the UTC - // versions of the accessor methods. + // To have a consistent view of time-based data independent of which time + // zone the client happens to be in we need a date-like object independent + // of time zones. This is done through a wrapper that only calls the UTC + // versions of the accessor methods. - function makeUtcWrapper(d) { - - function addProxyMethod(sourceObj, sourceMethod, targetObj, targetMethod) { - sourceObj[sourceMethod] = function() { - return targetObj[targetMethod].apply(targetObj, arguments); - }; - }; + function makeUtcWrapper(d) { + function addProxyMethod(sourceObj, sourceMethod, targetObj, targetMethod) { + sourceObj[sourceMethod] = function() { + return targetObj[targetMethod].apply(targetObj, arguments); + }; + } - var utc = { - date: d - }; + var utc = { + date: d + }; - // support strftime, if found + // support strftime, if found + if (d.strftime !== undefined) { + addProxyMethod(utc, "strftime", d, "strftime"); + } - if (d.strftime != undefined) { - addProxyMethod(utc, "strftime", d, "strftime"); - } + addProxyMethod(utc, "getTime", d, "getTime"); + addProxyMethod(utc, "setTime", d, "setTime"); + + var props = ["Date", "Day", "FullYear", "Hours", "Minutes", "Month", "Seconds", "Milliseconds", "Microseconds"]; - addProxyMethod(utc, "getTime", d, "getTime"); - addProxyMethod(utc, "setTime", d, "setTime"); - - var props = ["Date", "Day", "FullYear", "Hours", "Milliseconds", "Minutes", "Month", "Seconds"]; + for (var p = 0; p < props.length; p++) { + addProxyMethod(utc, "get" + props[p], d, "getUTC" + props[p]); + addProxyMethod(utc, "set" + props[p], d, "setUTC" + props[p]); + } - for (var p = 0; p < props.length; p++) { - addProxyMethod(utc, "get" + props[p], d, "getUTC" + props[p]); - addProxyMethod(utc, "set" + props[p], d, "setUTC" + props[p]); - } + return utc; + } - return utc; - }; + // select time zone strategy. This returns a date-like object tied to the + // desired timezone + function dateGenerator(ts, opts) { + var maxDateValue = 8640000000000000; - // select time zone strategy. This returns a date-like object tied to the - // desired timezone + if (opts && opts.timeBase === 'seconds') { + ts *= 1000; + } else if (opts.timeBase === 'microseconds') { + ts /= 1000; + } - function dateGenerator(ts, opts) { - if (opts.timezone == "browser") { - return new Date(ts); - } else if (!opts.timezone || opts.timezone == "utc") { - return makeUtcWrapper(new Date(ts)); - } else if (typeof timezoneJS != "undefined" && typeof timezoneJS.Date != "undefined") { - var d = new timezoneJS.Date(); - // timezone-js is fickle, so be sure to set the time zone before - // setting the time. - d.setTimezone(opts.timezone); - d.setTime(ts); - return d; - } else { - return makeUtcWrapper(new Date(ts)); - } - } - - // map of app. size of time units in milliseconds + if (ts > maxDateValue) { + ts = maxDateValue; + } else if (ts < -maxDateValue) { + ts = -maxDateValue; + } + + if (opts.timezone === "browser") { + return CreateMicroSecondDate(Date, ts); + } else if (!opts.timezone || opts.timezone === "utc") { + return makeUtcWrapper(CreateMicroSecondDate(Date, ts)); + } else if (typeof timezoneJS !== "undefined" && typeof timezoneJS.Date !== "undefined") { + var d = CreateMicroSecondDate(timezoneJS.Date, ts); + // timezone-js is fickle, so be sure to set the time zone before + // setting the time. + d.setTimezone(opts.timezone); + d.setTime(ts); + return d; + } else { + return makeUtcWrapper(CreateMicroSecondDate(Date, ts)); + } + } - var timeUnitSize = { - "second": 1000, - "minute": 60 * 1000, - "hour": 60 * 60 * 1000, - "day": 24 * 60 * 60 * 1000, - "month": 30 * 24 * 60 * 60 * 1000, - "quarter": 3 * 30 * 24 * 60 * 60 * 1000, - "year": 365.2425 * 24 * 60 * 60 * 1000 - }; - - // the allowed tick sizes, after 1 year we use - // an integer algorithm + // map of app. size of time units in seconds + var timeUnitSizeSeconds = { + "microsecond": 0.000001, + "millisecond": 0.001, + "second": 1, + "minute": 60, + "hour": 60 * 60, + "day": 24 * 60 * 60, + "month": 30 * 24 * 60 * 60, + "quarter": 3 * 30 * 24 * 60 * 60, + "year": 365.2425 * 24 * 60 * 60 + }; - var baseSpec = [ - [1, "second"], [2, "second"], [5, "second"], [10, "second"], - [30, "second"], - [1, "minute"], [2, "minute"], [5, "minute"], [10, "minute"], - [30, "minute"], - [1, "hour"], [2, "hour"], [4, "hour"], - [8, "hour"], [12, "hour"], - [1, "day"], [2, "day"], [3, "day"], - [0.25, "month"], [0.5, "month"], [1, "month"], - [2, "month"] - ]; + // map of app. size of time units in milliseconds + var timeUnitSizeMilliseconds = { + "microsecond": 0.001, + "millisecond": 1, + "second": 1000, + "minute": 60 * 1000, + "hour": 60 * 60 * 1000, + "day": 24 * 60 * 60 * 1000, + "month": 30 * 24 * 60 * 60 * 1000, + "quarter": 3 * 30 * 24 * 60 * 60 * 1000, + "year": 365.2425 * 24 * 60 * 60 * 1000 + }; - // we don't know which variant(s) we'll need yet, but generating both is - // cheap + // map of app. size of time units in microseconds + var timeUnitSizeMicroseconds = { + "microsecond": 1, + "millisecond": 1000, + "second": 1000000, + "minute": 60 * 1000000, + "hour": 60 * 60 * 1000000, + "day": 24 * 60 * 60 * 1000000, + "month": 30 * 24 * 60 * 60 * 1000000, + "quarter": 3 * 30 * 24 * 60 * 60 * 1000000, + "year": 365.2425 * 24 * 60 * 60 * 1000000 + }; - var specMonths = baseSpec.concat([[3, "month"], [6, "month"], - [1, "year"]]); - var specQuarters = baseSpec.concat([[1, "quarter"], [2, "quarter"], - [1, "year"]]); + // the allowed tick sizes, after 1 year we use + // an integer algorithm - function init(plot) { - plot.hooks.processOptions.push(function (plot, options) { - $.each(plot.getAxes(), function(axisName, axis) { - - var opts = axis.options; + var baseSpec = [ + [1, "microsecond"], [2, "microsecond"], [5, "microsecond"], [10, "microsecond"], + [25, "microsecond"], [50, "microsecond"], [100, "microsecond"], [250, "microsecond"], [500, "microsecond"], + [1, "millisecond"], [2, "millisecond"], [5, "millisecond"], [10, "millisecond"], + [25, "millisecond"], [50, "millisecond"], [100, "millisecond"], [250, "millisecond"], [500, "millisecond"], + [1, "second"], [2, "second"], [5, "second"], [10, "second"], + [30, "second"], + [1, "minute"], [2, "minute"], [5, "minute"], [10, "minute"], + [30, "minute"], + [1, "hour"], [2, "hour"], [4, "hour"], + [8, "hour"], [12, "hour"], + [1, "day"], [2, "day"], [3, "day"], + [0.25, "month"], [0.5, "month"], [1, "month"], + [2, "month"] + ]; - if (opts.mode == "time") { - axis.tickGenerator = function(axis) { - - var ticks = []; - var d = dateGenerator(axis.min, opts); - var minSize = 0; - - // make quarter use a possibility if quarters are - // mentioned in either of these options + // we don't know which variant(s) we'll need yet, but generating both is + // cheap - var spec = (opts.tickSize && opts.tickSize[1] === - "quarter") || - (opts.minTickSize && opts.minTickSize[1] === - "quarter") ? specQuarters : specMonths; + var specMonths = baseSpec.concat([[3, "month"], [6, "month"], + [1, "year"]]); + var specQuarters = baseSpec.concat([[1, "quarter"], [2, "quarter"], + [1, "year"]]); + + + function dateTickGenerator(axis) { + var opts = axis.options, + ticks = [], + d = dateGenerator(axis.min, opts), + minSize = 0; - if (opts.minTickSize != null) { - if (typeof opts.tickSize == "number") { - minSize = opts.tickSize; - } else { - minSize = opts.minTickSize[0] * timeUnitSize[opts.minTickSize[1]]; - } - } + // make quarter use a possibility if quarters are + // mentioned in either of these options + var spec = (opts.tickSize && opts.tickSize[1] === + "quarter") || + (opts.minTickSize && opts.minTickSize[1] === + "quarter") ? specQuarters : specMonths; - for (var i = 0; i < spec.length - 1; ++i) { - if (axis.delta < (spec[i][0] * timeUnitSize[spec[i][1]] - + spec[i + 1][0] * timeUnitSize[spec[i + 1][1]]) / 2 - && spec[i][0] * timeUnitSize[spec[i][1]] >= minSize) { - break; - } - } + var timeUnitSize; + if (opts.timeBase === 'seconds') { + timeUnitSize = timeUnitSizeSeconds; + } else if (opts.timeBase === 'microseconds') { + timeUnitSize = timeUnitSizeMicroseconds; + } else { + timeUnitSize = timeUnitSizeMilliseconds; + } - var size = spec[i][0]; - var unit = spec[i][1]; - - // special-case the possibility of several years - - if (unit == "year") { - - // if given a minTickSize in years, just use it, - // ensuring that it's an integer - - if (opts.minTickSize != null && opts.minTickSize[1] == "year") { - size = Math.floor(opts.minTickSize[0]); - } else { + if (opts.minTickSize !== null && opts.minTickSize !== undefined) { + if (typeof opts.tickSize === "number") { + minSize = opts.tickSize; + } else { + minSize = opts.minTickSize[0] * timeUnitSize[opts.minTickSize[1]]; + } + } - var magn = Math.pow(10, Math.floor(Math.log(axis.delta / timeUnitSize.year) / Math.LN10)); - var norm = (axis.delta / timeUnitSize.year) / magn; + for (var i = 0; i < spec.length - 1; ++i) { + if (axis.delta < (spec[i][0] * timeUnitSize[spec[i][1]] + + spec[i + 1][0] * timeUnitSize[spec[i + 1][1]]) / 2 && + spec[i][0] * timeUnitSize[spec[i][1]] >= minSize) { + break; + } + } - if (norm < 1.5) { - size = 1; - } else if (norm < 3) { - size = 2; - } else if (norm < 7.5) { - size = 5; - } else { - size = 10; - } + var size = spec[i][0]; + var unit = spec[i][1]; + // special-case the possibility of several years + if (unit === "year") { + // if given a minTickSize in years, just use it, + // ensuring that it's an integer - size *= magn; - } + if (opts.minTickSize !== null && opts.minTickSize !== undefined && opts.minTickSize[1] === "year") { + size = Math.floor(opts.minTickSize[0]); + } else { + var magn = parseFloat('1e' + Math.floor(Math.log(axis.delta / timeUnitSize.year) / Math.LN10)); + var norm = (axis.delta / timeUnitSize.year) / magn; - // minimum size for years is 1 + if (norm < 1.5) { + size = 1; + } else if (norm < 3) { + size = 2; + } else if (norm < 7.5) { + size = 5; + } else { + size = 10; + } - if (size < 1) { - size = 1; - } - } + size *= magn; + } + + // minimum size for years is 1 - axis.tickSize = opts.tickSize || [size, unit]; - var tickSize = axis.tickSize[0]; - unit = axis.tickSize[1]; - - var step = tickSize * timeUnitSize[unit]; + if (size < 1) { + size = 1; + } + } - if (unit == "second") { - d.setSeconds(floorInBase(d.getSeconds(), tickSize)); - } else if (unit == "minute") { - d.setMinutes(floorInBase(d.getMinutes(), tickSize)); - } else if (unit == "hour") { - d.setHours(floorInBase(d.getHours(), tickSize)); - } else if (unit == "month") { - d.setMonth(floorInBase(d.getMonth(), tickSize)); - } else if (unit == "quarter") { - d.setMonth(3 * floorInBase(d.getMonth() / 3, - tickSize)); - } else if (unit == "year") { - d.setFullYear(floorInBase(d.getFullYear(), tickSize)); - } + axis.tickSize = opts.tickSize || [size, unit]; + var tickSize = axis.tickSize[0]; + unit = axis.tickSize[1]; + + var step = tickSize * timeUnitSize[unit]; - // reset smaller components + if (unit === "microsecond") { + d.setMicroseconds(floorInBase(d.getMicroseconds(), tickSize)); + } else if (unit === "millisecond") { + d.setMilliseconds(floorInBase(d.getMilliseconds(), tickSize)); + } else if (unit === "second") { + d.setSeconds(floorInBase(d.getSeconds(), tickSize)); + } else if (unit === "minute") { + d.setMinutes(floorInBase(d.getMinutes(), tickSize)); + } else if (unit === "hour") { + d.setHours(floorInBase(d.getHours(), tickSize)); + } else if (unit === "month") { + d.setMonth(floorInBase(d.getMonth(), tickSize)); + } else if (unit === "quarter") { + d.setMonth(3 * floorInBase(d.getMonth() / 3, + tickSize)); + } else if (unit === "year") { + d.setFullYear(floorInBase(d.getFullYear(), tickSize)); + } - d.setMilliseconds(0); + // reset smaller components - if (step >= timeUnitSize.minute) { - d.setSeconds(0); - } - if (step >= timeUnitSize.hour) { - d.setMinutes(0); - } - if (step >= timeUnitSize.day) { - d.setHours(0); - } - if (step >= timeUnitSize.day * 4) { - d.setDate(1); - } - if (step >= timeUnitSize.month * 2) { - d.setMonth(floorInBase(d.getMonth(), 3)); - } - if (step >= timeUnitSize.quarter * 2) { - d.setMonth(floorInBase(d.getMonth(), 6)); - } - if (step >= timeUnitSize.year) { - d.setMonth(0); - } + if (step >= timeUnitSize.millisecond) { + if (step >= timeUnitSize.second) { + d.setMicroseconds(0); + } else { + d.setMicroseconds(d.getMilliseconds()*1000); + } + } + if (step >= timeUnitSize.minute) { + d.setSeconds(0); + } + if (step >= timeUnitSize.hour) { + d.setMinutes(0); + } + if (step >= timeUnitSize.day) { + d.setHours(0); + } + if (step >= timeUnitSize.day * 4) { + d.setDate(1); + } + if (step >= timeUnitSize.month * 2) { + d.setMonth(floorInBase(d.getMonth(), 3)); + } + if (step >= timeUnitSize.quarter * 2) { + d.setMonth(floorInBase(d.getMonth(), 6)); + } + if (step >= timeUnitSize.year) { + d.setMonth(0); + } - var carry = 0; - var v = Number.NaN; - var prev; + var carry = 0; + var v = Number.NaN; + var v1000; + var prev; + do { + prev = v; + v1000 = d.getTime(); + if (opts && opts.timeBase === 'seconds') { + v = v1000 / 1000; + } else if (opts && opts.timeBase === 'microseconds') { + v = v1000 * 1000; + } else { + v = v1000; + } - do { - - prev = v; - v = d.getTime(); - ticks.push(v); + ticks.push(v); - if (unit == "month" || unit == "quarter") { - if (tickSize < 1) { - - // a bit complicated - we'll divide the - // month/quarter up but we need to take - // care of fractions so we don't end up in - // the middle of a day + if (unit === "month" || unit === "quarter") { + if (tickSize < 1) { + // a bit complicated - we'll divide the + // month/quarter up but we need to take + // care of fractions so we don't end up in + // the middle of a day + d.setDate(1); + var start = d.getTime(); + d.setMonth(d.getMonth() + + (unit === "quarter" ? 3 : 1)); + var end = d.getTime(); + d.setTime((v + carry * timeUnitSize.hour + (end - start) * tickSize)); + carry = d.getHours(); + d.setHours(0); + } else { + d.setMonth(d.getMonth() + + tickSize * (unit === "quarter" ? 3 : 1)); + } + } else if (unit === "year") { + d.setFullYear(d.getFullYear() + tickSize); + } else { + if (opts.timeBase === 'seconds') { + d.setTime((v + step) * 1000); + } else if (opts.timeBase === 'microseconds') { + d.setTime((v + step) / 1000); + } else { + d.setTime(v + step); + } + } + } while (v < axis.max && v !== prev); - d.setDate(1); - var start = d.getTime(); - d.setMonth(d.getMonth() + - (unit == "quarter" ? 3 : 1)); - var end = d.getTime(); - d.setTime(v + carry * timeUnitSize.hour + (end - start) * tickSize); - carry = d.getHours(); - d.setHours(0); - } else { - d.setMonth(d.getMonth() + - tickSize * (unit == "quarter" ? 3 : 1)); - } - } else if (unit == "year") { - d.setFullYear(d.getFullYear() + tickSize); - } else { - d.setTime(v + step); - } - } while (v < axis.max && v != prev); + return ticks; + }; - return ticks; - }; - - axis.tickFormatter = function (v, axis) { + function init(plot) { + plot.hooks.processOptions.push(function (plot) { + $.each(plot.getAxes(), function(axisName, axis) { + var opts = axis.options; + if (opts.mode === "time") { + axis.tickGenerator = dateTickGenerator; - var d = dateGenerator(v, axis.options); + axis.tickFormatter = function (v, axis) { + var d = dateGenerator(v, axis.options); - // first check global format - + // first check global format if (opts.timeformat != null) { return formatDate(d, opts.timeformat, opts.monthNames, opts.dayNames); } // possibly use quarters if quarters are mentioned in // any of these places - var useQuarters = (axis.options.tickSize && axis.options.tickSize[1] == "quarter") || (axis.options.minTickSize && - axis.options.minTickSize[1] == "quarter"); + axis.options.minTickSize[1] == "quarter"); + + var timeUnitSize; + if (opts.timeBase === 'seconds') { + timeUnitSize = timeUnitSizeSeconds; + } else if (opts.timeBase === 'microseconds') { + timeUnitSize = timeUnitSizeMicroseconds; + } else { + timeUnitSize = timeUnitSizeMilliseconds; + } var t = axis.tickSize[0] * timeUnitSize[axis.tickSize[1]]; var span = axis.max - axis.min; var suffix = (opts.twelveHourClock) ? " %p" : ""; var hourCode = (opts.twelveHourClock) ? "%I" : "%H"; + var factor; var fmt; - if (t < timeUnitSize.minute) { + if (opts.timeBase === 'seconds') { + factor = 1; + } else if (opts.timeBase === 'microseconds') { + factor = 1000000 + } else { + factor = 1000; + } + + if (t < timeUnitSize.second) { + var decimals = -Math.floor(Math.log10(t/factor)) + + // the two-and-halves require an additional decimal + if (String(t).indexOf('25') > -1) { + decimals++; + } + + fmt = "%S.%" + decimals + "s"; + } else + if (t < timeUnitSize.minute) { fmt = hourCode + ":%M:%S" + suffix; } else if (t < timeUnitSize.day) { if (span < 2 * timeUnitSize.day) { @@ -409,24 +561,25 @@ var rt = formatDate(d, fmt, opts.monthNames, opts.dayNames); return rt; - }; - } - }); - }); - } + }; + } + }); + }); + } - $.plot.plugins.push({ - init: init, - options: options, - name: 'time', - version: '1.0' - }); + $.plot.plugins.push({ + init: init, + options: options, + name: 'time', + version: '1.0' + }); - // Time-axis support used to be in Flot core, which exposed the - // formatDate function on the plot object. Various plugins depend - // on the function, so we need to re-expose it here. + // Time-axis support used to be in Flot core, which exposed the + // formatDate function on the plot object. Various plugins depend + // on the function, so we need to re-expose it here. - $.plot.formatDate = formatDate; - $.plot.dateGenerator = dateGenerator; - + $.plot.formatDate = formatDate; + $.plot.dateGenerator = dateGenerator; + $.plot.dateTickGenerator = dateTickGenerator; + $.plot.makeUtcWrapper = makeUtcWrapper; })(jQuery); diff -r f3af77e371a1 -r 9f02c09d2393 src/pyams_skin/resources/js/ext/flot/jquery.flot.time.min.js --- a/src/pyams_skin/resources/js/ext/flot/jquery.flot.time.min.js Thu Sep 26 11:55:17 2019 +0200 +++ b/src/pyams_skin/resources/js/ext/flot/jquery.flot.time.min.js Wed Oct 16 13:00:43 2019 +0200 @@ -1,7 +1,1 @@ -/* Javascript plotting library for jQuery, version 0.8.3. - -Copyright (c) 2007-2014 IOLA and Ole Laursen. -Licensed under the MIT license. - -*/ -(function($){var options={xaxis:{timezone:null,timeformat:null,twelveHourClock:false,monthNames:null}};function floorInBase(n,base){return base*Math.floor(n/base)}function formatDate(d,fmt,monthNames,dayNames){if(typeof d.strftime=="function"){return d.strftime(fmt)}var leftPad=function(n,pad){n=""+n;pad=""+(pad==null?"0":pad);return n.length==1?pad+n:n};var r=[];var escape=false;var hours=d.getHours();var isAM=hours<12;if(monthNames==null){monthNames=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]}if(dayNames==null){dayNames=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"]}var hours12;if(hours>12){hours12=hours-12}else if(hours==0){hours12=12}else{hours12=hours}for(var i=0;i=minSize){break}}var size=spec[i][0];var unit=spec[i][1];if(unit=="year"){if(opts.minTickSize!=null&&opts.minTickSize[1]=="year"){size=Math.floor(opts.minTickSize[0])}else{var magn=Math.pow(10,Math.floor(Math.log(axis.delta/timeUnitSize.year)/Math.LN10));var norm=axis.delta/timeUnitSize.year/magn;if(norm<1.5){size=1}else if(norm<3){size=2}else if(norm<7.5){size=5}else{size=10}size*=magn}if(size<1){size=1}}axis.tickSize=opts.tickSize||[size,unit];var tickSize=axis.tickSize[0];unit=axis.tickSize[1];var step=tickSize*timeUnitSize[unit];if(unit=="second"){d.setSeconds(floorInBase(d.getSeconds(),tickSize))}else if(unit=="minute"){d.setMinutes(floorInBase(d.getMinutes(),tickSize))}else if(unit=="hour"){d.setHours(floorInBase(d.getHours(),tickSize))}else if(unit=="month"){d.setMonth(floorInBase(d.getMonth(),tickSize))}else if(unit=="quarter"){d.setMonth(3*floorInBase(d.getMonth()/3,tickSize))}else if(unit=="year"){d.setFullYear(floorInBase(d.getFullYear(),tickSize))}d.setMilliseconds(0);if(step>=timeUnitSize.minute){d.setSeconds(0)}if(step>=timeUnitSize.hour){d.setMinutes(0)}if(step>=timeUnitSize.day){d.setHours(0)}if(step>=timeUnitSize.day*4){d.setDate(1)}if(step>=timeUnitSize.month*2){d.setMonth(floorInBase(d.getMonth(),3))}if(step>=timeUnitSize.quarter*2){d.setMonth(floorInBase(d.getMonth(),6))}if(step>=timeUnitSize.year){d.setMonth(0)}var carry=0;var v=Number.NaN;var prev;do{prev=v;v=d.getTime();ticks.push(v);if(unit=="month"||unit=="quarter"){if(tickSize<1){d.setDate(1);var start=d.getTime();d.setMonth(d.getMonth()+(unit=="quarter"?3:1));var end=d.getTime();d.setTime(v+carry*timeUnitSize.hour+(end-start)*tickSize);carry=d.getHours();d.setHours(0)}else{d.setMonth(d.getMonth()+tickSize*(unit=="quarter"?3:1))}}else if(unit=="year"){d.setFullYear(d.getFullYear()+tickSize)}else{d.setTime(v+step)}}while(v12?a-12:0==a?12:a;for(var u=-1,d=0;d0)u=Number(l);else if(r){switch(l){case"a":l=""+o[e.getDay()];break;case"b":l=""+i[e.getMonth()];break;case"d":l=n(e.getDate());break;case"e":l=n(e.getDate()," ");break;case"h":case"H":l=n(a);break;case"I":l=n(m);break;case"l":l=n(m," ");break;case"m":l=n(e.getMonth()+1);break;case"M":l=n(e.getMinutes());break;case"q":l=""+(Math.floor(e.getMonth()/3)+1);break;case"S":l=n(e.getSeconds());break;case"s":l=""+function(e,t){if(t<6&&t>0){var i=parseFloat("1e"+(t-6));e=("00000"+(e=Math.round(Math.round(e*i)/i))).slice(-6,-(6-t))}else e=("00000"+(e=Math.round(e))).slice(-6);return e}(e.getMicroseconds(),u);break;case"y":l=n(e.getFullYear()%100);break;case"Y":l=""+e.getFullYear();break;case"p":l=c?"am":"pm";break;case"P":l=c?"AM":"PM";break;case"w":l=""+e.getDay()}s.push(l),r=!1}else"%"==l?r=!0:s.push(l)}return s.join("")}function i(e){function t(e,t,i,o){e[t]=function(){return i[o].apply(i,arguments)}}var i={date:e};void 0!==e.strftime&&t(i,"strftime",e,"strftime"),t(i,"getTime",e,"getTime"),t(i,"setTime",e,"setTime");for(var o=["Date","Day","FullYear","Hours","Minutes","Month","Seconds","Milliseconds","Microseconds"],n=0;n864e13?e=864e13:e<-864e13&&(e=-864e13),"browser"===t.timezone)return r(Date,e);if(t.timezone&&"utc"!==t.timezone){if("undefined"!=typeof timezoneJS&&void 0!==timezoneJS.Date){var o=r(timezoneJS.Date,e);return o.setTimezone(t.timezone),o.setTime(e),o}return i(r(Date,e))}return i(r(Date,e))}function n(e){var t,i=e.options,n=[],r=o(e.min,i),u=0,h=i.tickSize&&"quarter"===i.tickSize[1]||i.minTickSize&&"quarter"===i.minTickSize[1]?l:d;t="seconds"===i.timeBase?a:"microseconds"===i.timeBase?m:c,null!==i.minTickSize&&void 0!==i.minTickSize&&(u="number"==typeof i.tickSize?i.tickSize:i.minTickSize[0]*t[i.minTickSize[1]]);for(var M=0;M=u);++M);var f=h[M][0],g=h[M][1];if("year"===g){if(null!==i.minTickSize&&void 0!==i.minTickSize&&"year"===i.minTickSize[1])f=Math.floor(i.minTickSize[0]);else{var k=parseFloat("1e"+Math.floor(Math.log(e.delta/t.year)/Math.LN10)),p=e.delta/t.year/k;f=p<1.5?1:p<3?2:p<7.5?5:10,f*=k}f<1&&(f=1)}e.tickSize=i.tickSize||[f,g];var y=e.tickSize[0],S=y*t[g=e.tickSize[1]];"microsecond"===g?r.setMicroseconds(s(r.getMicroseconds(),y)):"millisecond"===g?r.setMilliseconds(s(r.getMilliseconds(),y)):"second"===g?r.setSeconds(s(r.getSeconds(),y)):"minute"===g?r.setMinutes(s(r.getMinutes(),y)):"hour"===g?r.setHours(s(r.getHours(),y)):"month"===g?r.setMonth(s(r.getMonth(),y)):"quarter"===g?r.setMonth(3*s(r.getMonth()/3,y)):"year"===g&&r.setFullYear(s(r.getFullYear(),y)),S>=t.millisecond&&(S>=t.second?r.setMicroseconds(0):r.setMicroseconds(1e3*r.getMilliseconds())),S>=t.minute&&r.setSeconds(0),S>=t.hour&&r.setMinutes(0),S>=t.day&&r.setHours(0),S>=4*t.day&&r.setDate(1),S>=2*t.month&&r.setMonth(s(r.getMonth(),3)),S>=2*t.quarter&&r.setMonth(s(r.getMonth(),6)),S>=t.year&&r.setMonth(0);var T,v,z=0,b=Number.NaN;do{if(v=b,T=r.getTime(),b=i&&"seconds"===i.timeBase?T/1e3:i&&"microseconds"===i.timeBase?1e3*T:T,n.push(b),"month"===g||"quarter"===g)if(y<1){r.setDate(1);var q=r.getTime();r.setMonth(r.getMonth()+("quarter"===g?3:1));var B=r.getTime();r.setTime(b+z*t.hour+(B-q)*y),z=r.getHours(),r.setHours(0)}else r.setMonth(r.getMonth()+y*("quarter"===g?3:1));else"year"===g?r.setFullYear(r.getFullYear()+y):"seconds"===i.timeBase?r.setTime(1e3*(b+S)):"microseconds"===i.timeBase?r.setTime((b+S)/1e3):r.setTime(b+S)}while(b-1&&k++,l="%S.%"+k+"s"}else l=h= minLongTapDuration && !gestureState.interceptedLongTap) { + if (distance(gestureState.currentTapStart.x, gestureState.currentTapStart.y, gestureState.currentTapEnd.x, gestureState.currentTapEnd.y) < maxLongTapDistance) { + gestureState.interceptedLongTap = true; + return true; + } + } + return false; + }, + + waitForLongTap: function(e) { + var longTapTrigger = function() { + if (longTap.isLongTap(e)) { + mainEventHolder.dispatchEvent(new CustomEvent('longtap', { detail: e })); + } + gestureState.longTapTriggerId = null; + }; + if (!gestureState.longTapTriggerId) { + gestureState.longTapTriggerId = setTimeout(longTapTrigger, minLongTapDuration); + } + } + }; + + var tap = { + touchstart: function(e) { + gestureState.tapStartTime = new Date().getTime(); + }, + + touchmove: function(e) { + }, + + touchend: function(e) { + if (tap.isTap(e)) { + mainEventHolder.dispatchEvent(new CustomEvent('tap', { detail: e })); + preventEventBehaviors(e); + } + }, + + isTap: function(e) { + var currentTime = new Date().getTime(), + tapDuration = currentTime - gestureState.tapStartTime; + if (tapDuration <= pressedTapDuration) { + if (distance(gestureState.currentTapStart.x, gestureState.currentTapStart.y, gestureState.currentTapEnd.x, gestureState.currentTapEnd.y) < maxLongTapDistance) { + return true; + } + } + return false; + } + }; + + if (options.pan.enableTouch === true || options.zoom.enableTouch) { + plot.hooks.bindEvents.push(bindEvents); + plot.hooks.shutdown.push(shutdown); + }; + + function updatePrevForDoubleTap() { + gestureState.prevTap = { + x: gestureState.currentTap.x, + y: gestureState.currentTap.y + }; + }; + + function updateCurrentForDoubleTap(e) { + gestureState.currentTap = { + x: e.touches[0].pageX, + y: e.touches[0].pageY + }; + } + + function updateStateForLongTapStart(e) { + gestureState.tapStartTime = new Date().getTime(); + gestureState.interceptedLongTap = false; + gestureState.currentTapStart = { + x: e.touches[0].pageX, + y: e.touches[0].pageY + }; + gestureState.currentTapEnd = { + x: e.touches[0].pageX, + y: e.touches[0].pageY + }; + }; + + function updateStateForLongTapEnd(e) { + gestureState.currentTapEnd = { + x: e.touches[0].pageX, + y: e.touches[0].pageY + }; + }; + + function isDoubleTap(e) { + var currentTime = new Date().getTime(), + intervalBetweenTaps = currentTime - gestureState.prevTapTime; + + if (intervalBetweenTaps >= 0 && intervalBetweenTaps < maxIntervalBetweenTaps) { + if (distance(gestureState.prevTap.x, gestureState.prevTap.y, gestureState.currentTap.x, gestureState.currentTap.y) < maxDistanceBetweenTaps) { + e.firstTouch = gestureState.prevTap; + e.secondTouch = gestureState.currentTap; + return true; + } + } + gestureState.prevTapTime = currentTime; + return false; + } + + function preventEventBehaviors(e) { + if (!gestureState.isUnsupportedGesture) { + e.preventDefault(); + if (!plot.getOptions().propagateSupportedGesture) { + e.stopPropagation(); + } + } + } + + function distance(x1, y1, x2, y2) { + return Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); + } + + function noTouchActive(e) { + return (e.touches && e.touches.length === 0); + } + + function wasPinchEvent(e) { + return (gestureState.twoTouches && e.touches.length === 1); + } + + function updateOnMultipleTouches(e) { + if (e.touches.length >= 3) { + gestureState.isUnsupportedGesture = true; + } else { + gestureState.isUnsupportedGesture = false; + } + } + + function isPinchEvent(e) { + if (e.touches && e.touches.length >= 2) { + if (e.touches[0].target === plot.getEventHolder() && + e.touches[1].target === plot.getEventHolder()) { + return true; + } + } + return false; + } + } + + $.plot.plugins.push({ + init: init, + options: options, + name: 'navigateTouch', + version: '0.3' + }); +})(jQuery); diff -r f3af77e371a1 -r 9f02c09d2393 src/pyams_skin/resources/js/ext/flot/jquery.flot.touch.min.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/pyams_skin/resources/js/ext/flot/jquery.flot.touch.min.js Wed Oct 16 13:00:43 2019 +0200 @@ -0,0 +1,1 @@ +!function(t){"use strict";function e(t,e){function n(e){var n=t.getOptions();(n.pan.active||n.zoom.active)&&(h(e),v.dispatchEvent(new CustomEvent("touchevent",{detail:e})),g(e)?o(e,"pinch"):(o(e,"pan"),d(e)||(c(e)&&o(e,"doubleTap"),o(e,"tap"),o(e,"longTap"))))}function o(t,e){switch(e){case"pan":f[t.type](t);break;case"pinch":w[t.type](t);break;case"doubleTap":y.onDoubleTap(t);break;case"longTap":x[t.type](t);break;case"tap":L[t.type](t)}}function a(){l.prevTap={x:l.currentTap.x,y:l.currentTap.y}}function r(t){l.currentTap={x:t.touches[0].pageX,y:t.touches[0].pageY}}function u(t){l.tapStartTime=(new Date).getTime(),l.interceptedLongTap=!1,l.currentTapStart={x:t.touches[0].pageX,y:t.touches[0].pageY},l.currentTapEnd={x:t.touches[0].pageX,y:t.touches[0].pageY}}function p(t){l.currentTapEnd={x:t.touches[0].pageX,y:t.touches[0].pageY}}function c(t){var e=(new Date).getTime(),n=e-l.prevTapTime;return n>=0&&n=3?l.isUnsupportedGesture=!0:l.isUnsupportedGesture=!1}function g(e){return!!(e.touches&&e.touches.length>=2&&e.touches[0].target===t.getEventHolder()&&e.touches[1].target===t.getEventHolder())}var v,l={twoTouches:!1,currentTapStart:{x:0,y:0},currentTapEnd:{x:0,y:0},prevTap:{x:0,y:0},currentTap:{x:0,y:0},interceptedLongTap:!1,isUnsupportedGesture:!1,prevTapTime:null,tapStartTime:null,longTapTriggerId:null},m=20,E=500,f={touchstart:function(t){a(),r(t),u(t),v.dispatchEvent(new CustomEvent("panstart",{detail:t}))},touchmove:function(t){i(t),r(t),p(t),l.isUnsupportedGesture||v.dispatchEvent(new CustomEvent("pandrag",{detail:t}))},touchend:function(t){i(t),d(t)?(v.dispatchEvent(new CustomEvent("pinchend",{detail:t})),v.dispatchEvent(new CustomEvent("panstart",{detail:t}))):T(t)&&v.dispatchEvent(new CustomEvent("panend",{detail:t}))}},w={touchstart:function(t){v.dispatchEvent(new CustomEvent("pinchstart",{detail:t}))},touchmove:function(t){i(t),l.twoTouches=g(t),l.isUnsupportedGesture||v.dispatchEvent(new CustomEvent("pinchdrag",{detail:t}))},touchend:function(t){i(t)}},y={onDoubleTap:function(t){i(t),v.dispatchEvent(new CustomEvent("doubletap",{detail:t}))}},x={touchstart:function(t){x.waitForLongTap(t)},touchmove:function(t){},touchend:function(t){l.longTapTriggerId&&(clearTimeout(l.longTapTriggerId),l.longTapTriggerId=null)},isLongTap:function(t){return(new Date).getTime()-l.tapStartTime>=1500&&!l.interceptedLongTap&&s(l.currentTapStart.x,l.currentTapStart.y,l.currentTapEnd.x,l.currentTapEnd.y)<20&&(l.interceptedLongTap=!0,!0)},waitForLongTap:function(t){l.longTapTriggerId||(l.longTapTriggerId=setTimeout(function(){x.isLongTap(t)&&v.dispatchEvent(new CustomEvent("longtap",{detail:t})),l.longTapTriggerId=null},1500))}},L={touchstart:function(t){l.tapStartTime=(new Date).getTime()},touchmove:function(t){},touchend:function(t){L.isTap(t)&&(v.dispatchEvent(new CustomEvent("tap",{detail:t})),i(t))},isTap:function(t){return(new Date).getTime()-l.tapStartTime<=125&&s(l.currentTapStart.x,l.currentTapStart.y,l.currentTapEnd.x,l.currentTapEnd.y)<20}};(!0===e.pan.enableTouch||e.zoom.enableTouch)&&(t.hooks.bindEvents.push(function(t,e){v=e[0],e[0].addEventListener("touchstart",n,!1),e[0].addEventListener("touchmove",n,!1),e[0].addEventListener("touchend",n,!1)}),t.hooks.shutdown.push(function(t,e){e[0].removeEventListener("touchstart",n),e[0].removeEventListener("touchmove",n),e[0].removeEventListener("touchend",n),l.longTapTriggerId&&(clearTimeout(l.longTapTriggerId),l.longTapTriggerId=null)}))}jQuery.plot.plugins.push({init:function(t){t.hooks.processOptions.push(e)},options:{propagateSupportedGesture:!1},name:"navigateTouch",version:"0.3"})}(); diff -r f3af77e371a1 -r 9f02c09d2393 src/pyams_skin/resources/js/ext/flot/jquery.flot.touchNavigate.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/pyams_skin/resources/js/ext/flot/jquery.flot.touchNavigate.js Wed Oct 16 13:00:43 2019 +0200 @@ -0,0 +1,360 @@ +/* global jQuery */ + +(function($) { + 'use strict'; + + var options = { + zoom: { + enableTouch: false + }, + pan: { + enableTouch: false, + touchMode: 'manual' + }, + recenter: { + enableTouch: true + } + }; + + var ZOOM_DISTANCE_MARGIN = $.plot.uiConstants.ZOOM_DISTANCE_MARGIN; + + function init(plot) { + plot.hooks.processOptions.push(initTouchNavigation); + } + + function initTouchNavigation(plot, options) { + var gestureState = { + zoomEnable: false, + prevDistance: null, + prevTapTime: 0, + prevPanPosition: { x: 0, y: 0 }, + prevTapPosition: { x: 0, y: 0 } + }, + navigationState = { + prevTouchedAxis: 'none', + currentTouchedAxis: 'none', + touchedAxis: null, + navigationConstraint: 'unconstrained', + initialState: null, + }, + useManualPan = options.pan.interactive && options.pan.touchMode === 'manual', + smartPanLock = options.pan.touchMode === 'smartLock', + useSmartPan = options.pan.interactive && (smartPanLock || options.pan.touchMode === 'smart'), + pan, pinch, doubleTap; + + function bindEvents(plot, eventHolder) { + var o = plot.getOptions(); + + if (o.zoom.interactive && o.zoom.enableTouch) { + eventHolder[0].addEventListener('pinchstart', pinch.start, false); + eventHolder[0].addEventListener('pinchdrag', pinch.drag, false); + eventHolder[0].addEventListener('pinchend', pinch.end, false); + } + + if (o.pan.interactive && o.pan.enableTouch) { + eventHolder[0].addEventListener('panstart', pan.start, false); + eventHolder[0].addEventListener('pandrag', pan.drag, false); + eventHolder[0].addEventListener('panend', pan.end, false); + } + + if ((o.recenter.interactive && o.recenter.enableTouch)) { + eventHolder[0].addEventListener('doubletap', doubleTap.recenterPlot, false); + } + } + + function shutdown(plot, eventHolder) { + eventHolder[0].removeEventListener('panstart', pan.start); + eventHolder[0].removeEventListener('pandrag', pan.drag); + eventHolder[0].removeEventListener('panend', pan.end); + eventHolder[0].removeEventListener('pinchstart', pinch.start); + eventHolder[0].removeEventListener('pinchdrag', pinch.drag); + eventHolder[0].removeEventListener('pinchend', pinch.end); + eventHolder[0].removeEventListener('doubletap', doubleTap.recenterPlot); + } + + pan = { + start: function(e) { + presetNavigationState(e, 'pan', gestureState); + updateData(e, 'pan', gestureState, navigationState); + + if (useSmartPan) { + var point = getPoint(e, 'pan'); + navigationState.initialState = plot.navigationState(point.x, point.y); + } + }, + + drag: function(e) { + presetNavigationState(e, 'pan', gestureState); + + if (useSmartPan) { + var point = getPoint(e, 'pan'); + plot.smartPan({ + x: navigationState.initialState.startPageX - point.x, + y: navigationState.initialState.startPageY - point.y + }, navigationState.initialState, navigationState.touchedAxis, false, smartPanLock); + } else if (useManualPan) { + plot.pan({ + left: -delta(e, 'pan', gestureState).x, + top: -delta(e, 'pan', gestureState).y, + axes: navigationState.touchedAxis + }); + updatePrevPanPosition(e, 'pan', gestureState, navigationState); + } + }, + + end: function(e) { + presetNavigationState(e, 'pan', gestureState); + + if (useSmartPan) { + plot.smartPan.end(); + } + + if (wasPinchEvent(e, gestureState)) { + updateprevPanPosition(e, 'pan', gestureState, navigationState); + } + } + }; + + var pinchDragTimeout; + pinch = { + start: function(e) { + if (pinchDragTimeout) { + clearTimeout(pinchDragTimeout); + pinchDragTimeout = null; + } + presetNavigationState(e, 'pinch', gestureState); + setPrevDistance(e, gestureState); + updateData(e, 'pinch', gestureState, navigationState); + }, + + drag: function(e) { + if (pinchDragTimeout) { + return; + } + pinchDragTimeout = setTimeout(function() { + presetNavigationState(e, 'pinch', gestureState); + plot.pan({ + left: -delta(e, 'pinch', gestureState).x, + top: -delta(e, 'pinch', gestureState).y, + axes: navigationState.touchedAxis + }); + updatePrevPanPosition(e, 'pinch', gestureState, navigationState); + + var dist = pinchDistance(e); + + if (gestureState.zoomEnable || Math.abs(dist - gestureState.prevDistance) > ZOOM_DISTANCE_MARGIN) { + zoomPlot(plot, e, gestureState, navigationState); + + //activate zoom mode + gestureState.zoomEnable = true; + } + pinchDragTimeout = null; + }, 1000 / 60); + }, + + end: function(e) { + if (pinchDragTimeout) { + clearTimeout(pinchDragTimeout); + pinchDragTimeout = null; + } + presetNavigationState(e, 'pinch', gestureState); + gestureState.prevDistance = null; + } + }; + + doubleTap = { + recenterPlot: function(e) { + if (e && e.detail && e.detail.type === 'touchstart') { + // only do not recenter for touch start; + recenterPlotOnDoubleTap(plot, e, gestureState, navigationState); + } + } + }; + + if (options.pan.enableTouch === true || options.zoom.enableTouch === true) { + plot.hooks.bindEvents.push(bindEvents); + plot.hooks.shutdown.push(shutdown); + } + + function presetNavigationState(e, gesture, gestureState) { + navigationState.touchedAxis = getAxis(plot, e, gesture, navigationState); + if (noAxisTouched(navigationState)) { + navigationState.navigationConstraint = 'unconstrained'; + } else { + navigationState.navigationConstraint = 'axisConstrained'; + } + } + } + + $.plot.plugins.push({ + init: init, + options: options, + name: 'navigateTouch', + version: '0.3' + }); + + function recenterPlotOnDoubleTap(plot, e, gestureState, navigationState) { + checkAxesForDoubleTap(plot, e, navigationState); + if ((navigationState.currentTouchedAxis === 'x' && navigationState.prevTouchedAxis === 'x') || + (navigationState.currentTouchedAxis === 'y' && navigationState.prevTouchedAxis === 'y') || + (navigationState.currentTouchedAxis === 'none' && navigationState.prevTouchedAxis === 'none')) { + var event; + + plot.recenter({ axes: navigationState.touchedAxis }); + + if (navigationState.touchedAxis) { + event = new $.Event('re-center', { detail: { axisTouched: navigationState.touchedAxis } }); + } else { + event = new $.Event('re-center', { detail: e }); + } + plot.getPlaceholder().trigger(event); + } + } + + function checkAxesForDoubleTap(plot, e, navigationState) { + var axis = plot.getTouchedAxis(e.detail.firstTouch.x, e.detail.firstTouch.y); + if (axis[0] !== undefined) { + navigationState.prevTouchedAxis = axis[0].direction; + } + + axis = plot.getTouchedAxis(e.detail.secondTouch.x, e.detail.secondTouch.y); + if (axis[0] !== undefined) { + navigationState.touchedAxis = axis; + navigationState.currentTouchedAxis = axis[0].direction; + } + + if (noAxisTouched(navigationState)) { + navigationState.touchedAxis = null; + navigationState.prevTouchedAxis = 'none'; + navigationState.currentTouchedAxis = 'none'; + } + } + + function zoomPlot(plot, e, gestureState, navigationState) { + var offset = plot.offset(), + center = { + left: 0, + top: 0 + }, + zoomAmount = pinchDistance(e) / gestureState.prevDistance, + dist = pinchDistance(e); + + center.left = getPoint(e, 'pinch').x - offset.left; + center.top = getPoint(e, 'pinch').y - offset.top; + + // send the computed touched axis to the zoom function so that it only zooms on that one + plot.zoom({ + center: center, + amount: zoomAmount, + axes: navigationState.touchedAxis + }); + gestureState.prevDistance = dist; + } + + function wasPinchEvent(e, gestureState) { + return (gestureState.zoomEnable && e.detail.touches.length === 1); + } + + function getAxis(plot, e, gesture, navigationState) { + if (e.type === 'pinchstart') { + var axisTouch1 = plot.getTouchedAxis(e.detail.touches[0].pageX, e.detail.touches[0].pageY); + var axisTouch2 = plot.getTouchedAxis(e.detail.touches[1].pageX, e.detail.touches[1].pageY); + + if (axisTouch1.length === axisTouch2.length && axisTouch1.toString() === axisTouch2.toString()) { + return axisTouch1; + } + } else if (e.type === 'panstart') { + return plot.getTouchedAxis(e.detail.touches[0].pageX, e.detail.touches[0].pageY); + } else if (e.type === 'pinchend') { + //update axis since instead on pinch, a pan event is made + return plot.getTouchedAxis(e.detail.touches[0].pageX, e.detail.touches[0].pageY); + } else { + return navigationState.touchedAxis; + } + } + + function noAxisTouched(navigationState) { + return (!navigationState.touchedAxis || navigationState.touchedAxis.length === 0); + } + + function setPrevDistance(e, gestureState) { + gestureState.prevDistance = pinchDistance(e); + } + + function updateData(e, gesture, gestureState, navigationState) { + var axisDir, + point = getPoint(e, gesture); + + switch (navigationState.navigationConstraint) { + case 'unconstrained': + navigationState.touchedAxis = null; + gestureState.prevTapPosition = { + x: gestureState.prevPanPosition.x, + y: gestureState.prevPanPosition.y + }; + gestureState.prevPanPosition = { + x: point.x, + y: point.y + }; + break; + case 'axisConstrained': + axisDir = navigationState.touchedAxis[0].direction; + navigationState.currentTouchedAxis = axisDir; + gestureState.prevTapPosition[axisDir] = gestureState.prevPanPosition[axisDir]; + gestureState.prevPanPosition[axisDir] = point[axisDir]; + break; + default: + break; + } + } + + function distance(x1, y1, x2, y2) { + return Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); + } + + function pinchDistance(e) { + var t1 = e.detail.touches[0], + t2 = e.detail.touches[1]; + return distance(t1.pageX, t1.pageY, t2.pageX, t2.pageY); + } + + function updatePrevPanPosition(e, gesture, gestureState, navigationState) { + var point = getPoint(e, gesture); + + switch (navigationState.navigationConstraint) { + case 'unconstrained': + gestureState.prevPanPosition.x = point.x; + gestureState.prevPanPosition.y = point.y; + break; + case 'axisConstrained': + gestureState.prevPanPosition[navigationState.currentTouchedAxis] = + point[navigationState.currentTouchedAxis]; + break; + default: + break; + } + } + + function delta(e, gesture, gestureState) { + var point = getPoint(e, gesture); + + return { + x: point.x - gestureState.prevPanPosition.x, + y: point.y - gestureState.prevPanPosition.y + } + } + + function getPoint(e, gesture) { + if (gesture === 'pinch') { + return { + x: (e.detail.touches[0].pageX + e.detail.touches[1].pageX) / 2, + y: (e.detail.touches[0].pageY + e.detail.touches[1].pageY) / 2 + } + } else { + return { + x: e.detail.touches[0].pageX, + y: e.detail.touches[0].pageY + } + } + } +})(jQuery); diff -r f3af77e371a1 -r 9f02c09d2393 src/pyams_skin/resources/js/ext/flot/jquery.flot.touchNavigate.min.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/pyams_skin/resources/js/ext/flot/jquery.flot.touchNavigate.min.js Wed Oct 16 13:00:43 2019 +0200 @@ -0,0 +1,1 @@ +!function(e){"use strict";function t(e,t){function i(t,n,i){A.touchedAxis=r(e,t,n,A),c(A)?A.navigationConstraint="unconstrained":A.navigationConstraint="axisConstrained"}var d,g,T,f={zoomEnable:!1,prevDistance:null,prevTapTime:0,prevPanPosition:{x:0,y:0},prevTapPosition:{x:0,y:0}},A={prevTouchedAxis:"none",currentTouchedAxis:"none",touchedAxis:null,navigationConstraint:"unconstrained",initialState:null},P=t.pan.interactive&&"manual"===t.pan.touchMode,m="smartLock"===t.pan.touchMode,y=t.pan.interactive&&(m||"smart"===t.pan.touchMode);d={start:function(t){if(i(t,"pan"),u(t,"pan",f,A),y){var n=v(t,"pan");A.initialState=e.navigationState(n.x,n.y)}},drag:function(t){if(i(t,"pan"),y){var n=v(t,"pan");e.smartPan({x:A.initialState.startPageX-n.x,y:A.initialState.startPageY-n.y},A.initialState,A.touchedAxis,!1,m)}else P&&(e.pan({left:-l(t,"pan",f).x,top:-l(t,"pan",f).y,axes:A.touchedAxis}),h(t,"pan",f,A))},end:function(t){i(t,"pan"),y&&e.smartPan.end(),o(t,f)&&updateprevPanPosition(t,"pan",f,A)}};var E;g={start:function(e){E&&(clearTimeout(E),E=null),i(e,"pinch"),s(e,f),u(e,"pinch",f,A)},drag:function(t){E||(E=setTimeout(function(){i(t,"pinch"),e.pan({left:-l(t,"pinch",f).x,top:-l(t,"pinch",f).y,axes:A.touchedAxis}),h(t,"pinch",f,A);var n=p(t);(f.zoomEnable||Math.abs(n-f.prevDistance)>x)&&(a(e,t,f,A),f.zoomEnable=!0),E=null},1e3/60))},end:function(e){E&&(clearTimeout(E),E=null),i(e,"pinch"),f.prevDistance=null}},T={recenterPlot:function(t){t&&t.detail&&"touchstart"===t.detail.type&&n(e,t,f,A)}},!0!==t.pan.enableTouch&&!0!==t.zoom.enableTouch||(e.hooks.bindEvents.push(function(e,t){var n=e.getOptions();n.zoom.interactive&&n.zoom.enableTouch&&(t[0].addEventListener("pinchstart",g.start,!1),t[0].addEventListener("pinchdrag",g.drag,!1),t[0].addEventListener("pinchend",g.end,!1)),n.pan.interactive&&n.pan.enableTouch&&(t[0].addEventListener("panstart",d.start,!1),t[0].addEventListener("pandrag",d.drag,!1),t[0].addEventListener("panend",d.end,!1)),n.recenter.interactive&&n.recenter.enableTouch&&t[0].addEventListener("doubletap",T.recenterPlot,!1)}),e.hooks.shutdown.push(function(e,t){t[0].removeEventListener("panstart",d.start),t[0].removeEventListener("pandrag",d.drag),t[0].removeEventListener("panend",d.end),t[0].removeEventListener("pinchstart",g.start),t[0].removeEventListener("pinchdrag",g.drag),t[0].removeEventListener("pinchend",g.end),t[0].removeEventListener("doubletap",T.recenterPlot)}))}function n(t,n,a,o){if(i(t,n,o),"x"===o.currentTouchedAxis&&"x"===o.prevTouchedAxis||"y"===o.currentTouchedAxis&&"y"===o.prevTouchedAxis||"none"===o.currentTouchedAxis&&"none"===o.prevTouchedAxis){var r;t.recenter({axes:o.touchedAxis}),r=o.touchedAxis?new e.Event("re-center",{detail:{axisTouched:o.touchedAxis}}):new e.Event("re-center",{detail:n}),t.getPlaceholder().trigger(r)}}function i(e,t,n){var i=e.getTouchedAxis(t.detail.firstTouch.x,t.detail.firstTouch.y);void 0!==i[0]&&(n.prevTouchedAxis=i[0].direction),void 0!==(i=e.getTouchedAxis(t.detail.secondTouch.x,t.detail.secondTouch.y))[0]&&(n.touchedAxis=i,n.currentTouchedAxis=i[0].direction),c(n)&&(n.touchedAxis=null,n.prevTouchedAxis="none",n.currentTouchedAxis="none")}function a(e,t,n,i){var a=e.offset(),o={left:0,top:0},r=p(t)/n.prevDistance,c=p(t);o.left=v(t,"pinch").x-a.left,o.top=v(t,"pinch").y-a.top,e.zoom({center:o,amount:r,axes:i.touchedAxis}),n.prevDistance=c}function o(e,t){return t.zoomEnable&&1===e.detail.touches.length}function r(e,t,n,i){if("pinchstart"!==t.type)return"panstart"===t.type?e.getTouchedAxis(t.detail.touches[0].pageX,t.detail.touches[0].pageY):"pinchend"===t.type?e.getTouchedAxis(t.detail.touches[0].pageX,t.detail.touches[0].pageY):i.touchedAxis;var a=e.getTouchedAxis(t.detail.touches[0].pageX,t.detail.touches[0].pageY),o=e.getTouchedAxis(t.detail.touches[1].pageX,t.detail.touches[1].pageY);return a.length===o.length&&a.toString()===o.toString()?a:void 0}function c(e){return!e.touchedAxis||0===e.touchedAxis.length}function s(e,t){t.prevDistance=p(e)}function u(e,t,n,i){var a,o=v(e,t);switch(i.navigationConstraint){case"unconstrained":i.touchedAxis=null,n.prevTapPosition={x:n.prevPanPosition.x,y:n.prevPanPosition.y},n.prevPanPosition={x:o.x,y:o.y};break;case"axisConstrained":a=i.touchedAxis[0].direction,i.currentTouchedAxis=a,n.prevTapPosition[a]=n.prevPanPosition[a],n.prevPanPosition[a]=o[a]}}function d(e,t,n,i){return Math.sqrt((e-n)*(e-n)+(t-i)*(t-i))}function p(e){var t=e.detail.touches[0],n=e.detail.touches[1];return d(t.pageX,t.pageY,n.pageX,n.pageY)}function h(e,t,n,i){var a=v(e,t);switch(i.navigationConstraint){case"unconstrained":n.prevPanPosition.x=a.x,n.prevPanPosition.y=a.y;break;case"axisConstrained":n.prevPanPosition[i.currentTouchedAxis]=a[i.currentTouchedAxis]}}function l(e,t,n){var i=v(e,t);return{x:i.x-n.prevPanPosition.x,y:i.y-n.prevPanPosition.y}}function v(e,t){return"pinch"===t?{x:(e.detail.touches[0].pageX+e.detail.touches[1].pageX)/2,y:(e.detail.touches[0].pageY+e.detail.touches[1].pageY)/2}:{x:e.detail.touches[0].pageX,y:e.detail.touches[0].pageY}}var x=e.plot.uiConstants.ZOOM_DISTANCE_MARGIN;e.plot.plugins.push({init:function(e){e.hooks.processOptions.push(t)},options:{zoom:{enableTouch:!1},pan:{enableTouch:!1,touchMode:"manual"},recenter:{enableTouch:!0}},name:"navigateTouch",version:"0.3"})}(jQuery); diff -r f3af77e371a1 -r 9f02c09d2393 src/pyams_skin/resources/js/ext/flot/jquery.flot.uiConstants.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/pyams_skin/resources/js/ext/flot/jquery.flot.uiConstants.js Wed Oct 16 13:00:43 2019 +0200 @@ -0,0 +1,10 @@ +(function ($) { + 'use strict'; + $.plot.uiConstants = { + SNAPPING_CONSTANT: 20, + PANHINT_LENGTH_CONSTANT: 10, + MINOR_TICKS_COUNT_CONSTANT: 4, + TICK_LENGTH_CONSTANT: 10, + ZOOM_DISTANCE_MARGIN: 25 + }; +})(jQuery); diff -r f3af77e371a1 -r 9f02c09d2393 src/pyams_skin/resources/js/ext/flot/jquery.flot.uiConstants.min.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/pyams_skin/resources/js/ext/flot/jquery.flot.uiConstants.min.js Wed Oct 16 13:00:43 2019 +0200 @@ -0,0 +1,1 @@ +!function(N){"use strict";jQuery.plot.uiConstants={SNAPPING_CONSTANT:20,PANHINT_LENGTH_CONSTANT:10,MINOR_TICKS_COUNT_CONSTANT:4,TICK_LENGTH_CONSTANT:10,ZOOM_DISTANCE_MARGIN:25}}();