Skip to content

Instantly share code, notes, and snippets.

@timelyportfolio
Forked from benjchristensen/index.html
Last active December 14, 2015 21:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save timelyportfolio/5152233 to your computer and use it in GitHub Desktop.
Save timelyportfolio/5152233 to your computer and use it in GitHub Desktop.
Fork of Ben Christensen multiline with zoomooz

#Fork of Ben Christensen multi-line graph Fork adds zoomooz zoom functionality to the graph. Works well, but the hover element does not offset to reflect the zoom.

Original Readme.md is below


Proof of concept line graph implemented using d3.js and some jQuery that builds on previous examples.

The top graph is 24 hours of data in 2 minute increments. I have it rolling every 2 seconds to simulate live updating. In real-life it would only update every 2 minutes to match the data granularity.

See it running at http://bl.ocks.org/2657838

Features:

  • incrementally update data or replace entire dataset
  • interactive mouseover to view details
  • animated switching of y-axis scale types (linear, power, log)
  • dynamic redrawing on window resize

Missing:

  • not yet integrated with touch events, only mouse

I don't normally work in javascript, so if it isn't quite right, I'd appreciate suggestions on where to improve it.

<!--fork of Ben Christensen gist https://gist.github.com/benjchristensen/2657838*/!-->
<html>
<head>
<title>Interactive Line Graph</title>
<script src="http://d3js.org/d3.v3.js"></script>
<!--
using JQuery for element dimensions
This is a small aspect of this example so it can be removed fairly easily if needed.
-->
<script src="http://code.jquery.com/jquery-1.7.2.min.js"></script>
<script src="sample_data.js"></script>
<script src="line-graph.js"></script>
<script src="jquery.zoomooz.js"></script>
<link rel="stylesheet" href="style.css" type="text/css">
<style>
body {
font-family: "Helvetica Neue", Helvetica;
}
p {
clear:both;
top: 20px;
}
div.aGraph {
margin-bottom: 30px;
}
</style>
</head>
<body>
<div id="graph1" class="aGraph" style="position:relative;width:100%;height:400px"></div>
<div id="graph2" class="aGraph zoomTarget" data-targetsize="0.8" data-debug="false" style="float:left;position:relative;width:49%;height:200px"></div>
<div id="graph3" class="aGraph zoomTarget" data-targetsize="0.8" data-debug="false" style="float:left;position:relative;width:49%;height:200px"></div>
<script>
/*
* If running inside bl.ocks.org we want to resize the iframe to fit both graphs
*/
if(parent.document.getElementsByTagName("iframe")[0]) {
parent.document.getElementsByTagName("iframe")[0].setAttribute('style', 'height: 650px !important');
}
/*
* Note how the 'data' object is added to here before rendering to provide decoration information.
* <p>
* This is purposefully done here instead of in data.js as an example of how data would come from a server
* and then have presentation information injected into it (rather than as separate arguments in another object)
* and passed into LineGraph.
*
* Also, CSS can be used to style colors etc, but this is also doable via the 'data' object so that the styling
* of different data points can be done in code which is often more natural for display names, legends, line colors etc
*/
// add presentation logic for 'data' object using optional data arguments
data["displayNames"] = ["2xx","3xx","4xx","5xx"];
data["colors"] = ["green","orange","red","darkred"];
data["scale"] = "pow";
// add presentation logic for 'data' object using optional data arguments
data2["displayNames"] = ["2xx","3xx","4xx","5xx"];
data2["colors"] = ["green","orange","red","darkred"];
data2["scale"] = "linear";
// add presentation logic for 'data' object using optional data arguments
data3["displayNames"] = ["Data1", "Data2"];
data3["axis"] = ["left", "right"];
data3["colors"] = ["#2863bc","#c8801c"];
data3["rounding"] = [2, 0];
// create graph now that we've added presentation config
var l1 = new LineGraph({containerId: 'graph1', data: data});
var l2 = new LineGraph({containerId: 'graph2', data: data2});
var l3 = new LineGraph({containerId: 'graph3', data: data3});
setInterval(function() {
/*
* The following will simulate live updating of the data (see dataA, dataB, dataC etc in data.js which are real examples)
* This is being simulated so this example functions standalone without a backend server which generates data such as data.js contains.
*/
// for each data series ...
var newData = [];
data.values.forEach(function(dataSeries, index) {
// take the first value and move it to the end
// and capture the value we're moving so we can send it to the graph as an update
var v = dataSeries.shift();
dataSeries.push(v);
// put this value in newData as an array with 1 value
newData[index] = [v];
})
// we will reuse dataA each time
dataA.values = newData;
// increment time 1 step
dataA.start = dataA.start + dataA.step;
dataA.end = dataA.end + dataA.step;
l1.slideData(dataA);
}, 2000);
</script>
</body>
</html>
//see https://github.com/jaukia/zoomooz for original source
//all credit and attribution should be directed to https://github.com/jaukia/zoomooz not me
// Everything but the relevant parts stripped out by Janne Aukia
// for Zoomooz on April 4 2012 by using jscoverage coverage analysis tool.
// === Sylvester ===
// Vector and Matrix mathematics modules for JavaScript
// Copyright (c) 2007 James Coglan
//
// 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.
var Sylvester = {
version: '0.1.3',
precision: 1e-6
};
function Matrix() {}
Matrix.prototype = {
// Returns a copy of the matrix
dup: function() {
return Matrix.create(this.elements);
},
// Maps the matrix to another matrix (of the same dimensions) according to the given function
/*map: function(fn) {
var els = [], ni = this.elements.length, ki = ni, i, nj, kj = this.elements[0].length, j;
do { i = ki - ni;
nj = kj;
els[i] = [];
do { j = kj - nj;
els[i][j] = fn(this.elements[i][j], i + 1, j + 1);
} while (--nj);
} while (--ni);
return Matrix.create(els);
},*/
// Returns true iff the matrix can multiply the argument from the left
canMultiplyFromLeft: function(matrix) {
var M = matrix.elements || matrix;
if (typeof(M[0][0]) == 'undefined') { M = Matrix.create(M).elements; }
// this.columns should equal matrix.rows
return (this.elements[0].length == M.length);
},
// Returns the result of multiplying the matrix from the right by the argument.
// If the argument is a scalar then just multiply all the elements. If the argument is
// a vector, a vector is returned, which saves you having to remember calling
// col(1) on the result.
multiply: function(matrix) {
/*if (!matrix.elements) {
return this.map(function(x) { return x * matrix; });
}*/
var returnVector = matrix.modulus ? true : false;
var M = matrix.elements || matrix;
if (typeof(M[0][0]) == 'undefined') { M = Matrix.create(M).elements; }
if (!this.canMultiplyFromLeft(M)) { return null; }
var ni = this.elements.length, ki = ni, i, nj, kj = M[0].length, j;
var cols = this.elements[0].length, elements = [], sum, nc, c;
do { i = ki - ni;
elements[i] = [];
nj = kj;
do { j = kj - nj;
sum = 0;
nc = cols;
do { c = cols - nc;
sum += this.elements[i][c] * M[c][j];
} while (--nc);
elements[i][j] = sum;
} while (--nj);
} while (--ni);
M = Matrix.create(elements);
return returnVector ? M.col(1) : M;
},
// Returns true iff the matrix is square
isSquare: function() {
return (this.elements.length == this.elements[0].length);
},
// Make the matrix upper (right) triangular by Gaussian elimination.
// This method only adds multiples of rows to other rows. No rows are
// scaled up or switched, and the determinant is preserved.
toRightTriangular: function() {
var M = this.dup(), els;
var n = this.elements.length, k = n, i, np, kp = this.elements[0].length, p;
do { i = k - n;
if (M.elements[i][i] === 0) {
for (j = i + 1; j < k; j++) {
if (M.elements[j][i] !== 0) {
els = []; np = kp;
do { p = kp - np;
els.push(M.elements[i][p] + M.elements[j][p]);
} while (--np);
M.elements[i] = els;
break;
}
}
}
if (M.elements[i][i] !== 0) {
for (j = i + 1; j < k; j++) {
var multiplier = M.elements[j][i] / M.elements[i][i];
els = []; np = kp;
do { p = kp - np;
// Elements with column numbers up to an including the number
// of the row that we're subtracting can safely be set straight to
// zero, since that's the point of this routine and it avoids having
// to loop over and correct rounding errors later
els.push(p <= i ? 0 : M.elements[j][p] - M.elements[i][p] * multiplier);
} while (--np);
M.elements[j] = els;
}
}
} while (--n);
return M;
},
// Returns the determinant for square matrices
determinant: function() {
if (!this.isSquare()) { return null; }
var M = this.toRightTriangular();
var det = M.elements[0][0], n = M.elements.length - 1, k = n, i;
do { i = k - n + 1;
det = det * M.elements[i][i];
} while (--n);
return det;
},
// Returns true iff the matrix is singular
isSingular: function() {
return (this.isSquare() && this.determinant() === 0);
},
// Returns the result of attaching the given argument to the right-hand side of the matrix
augment: function(matrix) {
var M = matrix.elements || matrix;
if (typeof(M[0][0]) == 'undefined') { M = Matrix.create(M).elements; }
var T = this.dup(), cols = T.elements[0].length;
var ni = T.elements.length, ki = ni, i, nj, kj = M[0].length, j;
if (ni != M.length) { return null; }
do { i = ki - ni;
nj = kj;
do { j = kj - nj;
T.elements[i][cols + j] = M[i][j];
} while (--nj);
} while (--ni);
return T;
},
// Returns the inverse (if one exists) using Gauss-Jordan
inverse: function() {
if (!this.isSquare() || this.isSingular()) { return null; }
var ni = this.elements.length, ki = ni, i, j;
var M = this.augment(Matrix.I(ni)).toRightTriangular();
var np, kp = M.elements[0].length, p, els, divisor;
var inverse_elements = [], new_element;
// Matrix is non-singular so there will be no zeros on the diagonal
// Cycle through rows from last to first
do { i = ni - 1;
// First, normalise diagonal elements to 1
els = []; np = kp;
inverse_elements[i] = [];
divisor = M.elements[i][i];
do { p = kp - np;
new_element = M.elements[i][p] / divisor;
els.push(new_element);
// Shuffle of the current row of the right hand side into the results
// array as it will not be modified by later runs through this loop
if (p >= ki) { inverse_elements[i].push(new_element); }
} while (--np);
M.elements[i] = els;
// Then, subtract this row from those above it to
// give the identity matrix on the left hand side
for (j = 0; j < i; j++) {
els = []; np = kp;
do { p = kp - np;
els.push(M.elements[j][p] - M.elements[i][p] * M.elements[j][i]);
} while (--np);
M.elements[j] = els;
}
} while (--ni);
return Matrix.create(inverse_elements);
},
// Set the matrix's elements from an array. If the argument passed
// is a vector, the resulting matrix will be a single column.
setElements: function(els) {
var i, elements = els.elements || els;
if (typeof(elements[0][0]) != 'undefined') {
var ni = elements.length, ki = ni, nj, kj, j;
this.elements = [];
do { i = ki - ni;
nj = elements[i].length; kj = nj;
this.elements[i] = [];
do { j = kj - nj;
this.elements[i][j] = elements[i][j];
} while (--nj);
} while(--ni);
return this;
}
var n = elements.length, k = n;
this.elements = [];
do { i = k - n;
this.elements.push([elements[i]]);
} while (--n);
return this;
}
};
// Constructor function
Matrix.create = function(elements) {
var M = new Matrix();
return M.setElements(elements);
};
// Identity matrix of size n
Matrix.I = function(n) {
var els = [], k = n, i, nj, j;
do { i = k - n;
els[i] = []; nj = k;
do { j = k - nj;
els[i][j] = (i == j) ? 1 : 0;
} while (--nj);
} while (--n);
return Matrix.create(els);
};;/*
* purecssmatrix.js, version 0.10, part of:
* http://janne.aukia.com/zoomooz
*
* 0.10 initial stand-alone version
*
* LICENCE INFORMATION:
*
* Copyright (c) 2010 Janne Aukia (janne.aukia.com)
* Dual licensed under the MIT (MIT-LICENSE.txt)
* and GPL Version 2 (GPL-LICENSE.txt) licenses.
*
*/
PureCSSMatrix = (function() {
"use strict";
//**********************************//
//*** Variables ***//
//**********************************//
var regexp_is_deg = /deg$/;
var regexp_filter_number = /([0-9.\-e]+)/g;
var regexp_trans_splitter = /([a-zA-Z]+)\(([^\)]+)\)/g;
//**********************************//
//*** WebKitCSSMatrix in ***//
//*** pure Javascript ***//
//**********************************//
function CssMatrix(trans) {
if(trans && trans !== null && trans!="none") {
if(trans instanceof Matrix) {
this.setMatrix(trans);
} else {
this.setMatrixValue(trans);
}
} else {
this.m = Matrix.I(3);
}
}
CssMatrix.prototype.setMatrix = function(matr) {
this.m = matr;
};
function rawRotationToRadians(raw) {
var rot = parseFloat(filterNumber(raw));
if(raw.match(regexp_is_deg)) {
rot = (2*Math.PI)*rot/360.0;
}
return rot;
}
CssMatrix.prototype.setMatrixValue = function(transString) {
var mtr = Matrix.I(3);
var items;
while((items = regexp_trans_splitter.exec(transString)) !== null) {
var action = items[1].toLowerCase();
var val = items[2].split(",");
var trans;
if(action=="matrix") {
trans = Matrix.create([[parseFloat(val[0]),parseFloat(val[2]),parseFloat(filterNumber(val[4]))],
[parseFloat(val[1]),parseFloat(val[3]),parseFloat(filterNumber(val[5]))],
[ 0, 0, 1]]);
} else if(action=="translate") {
trans = Matrix.I(3);
trans.elements[0][2] = parseFloat(filterNumber(val[0]));
trans.elements[1][2] = parseFloat(filterNumber(val[1]));
} else if(action=="scale") {
var sx = parseFloat(val[0]);
var sy;
if(val.length>1) {
sy = parseFloat(val[1]);
} else {
sy = sx;
}
trans = Matrix.create([[sx, 0, 0], [0, sy, 0], [0, 0, 1]]);
} else if(action=="rotate") {
trans = Matrix.RotationZ(rawRotationToRadians(val[0]));
} else if(action=="skew" || action=="skewx") {
// TODO: supports only one parameter skew
trans = Matrix.I(3);
trans.elements[0][1] = Math.tan(rawRotationToRadians(val[0]));
} else if(action=="skewy") {
// TODO: test that this works (or unit test them all!)
trans = Matrix.I(3);
trans.elements[1][0] = Math.tan(rawRotationToRadians(val[0]));
} else {
console.log("Problem with setMatrixValue", action, val);
}
mtr = mtr.multiply(trans);
}
this.m = mtr;
};
CssMatrix.prototype.multiply = function(m2) {
return new CssMatrix(this.m.multiply(m2.m));
};
CssMatrix.prototype.inverse = function() {
if(Math.abs(this.m.elements[0][0])<0.000001) {
/* fixes a weird displacement problem with 90 deg rotations */
this.m.elements[0][0] = 0;
}
return new CssMatrix(this.m.inverse());
};
CssMatrix.prototype.translate = function(x,y) {
var trans = Matrix.I(3);
trans.elements[0][2] = x;
trans.elements[1][2] = y;
return new CssMatrix(this.m.multiply(trans));
};
CssMatrix.prototype.scale = function(sx,sy) {
var trans = Matrix.create([[sx, 0, 0], [0, sy, 0], [0, 0, 1]]);
return new CssMatrix(this.m.multiply(trans));
};
CssMatrix.prototype.rotate = function(rot) {
var trans = Matrix.RotationZ(rot);
return new CssMatrix(this.m.multiply(trans));
};
CssMatrix.prototype.toString = function() {
var e = this.m.elements;
var pxstr = "";
if($.browser.mozilla || $.browser.opera) {
pxstr = "px";
}
return "matrix("+printFixedNumber(e[0][0])+", "+printFixedNumber(e[1][0])+", "+
printFixedNumber(e[0][1])+", "+printFixedNumber(e[1][1])+", "+
printFixedNumber(e[0][2])+pxstr+", "+printFixedNumber(e[1][2])+pxstr+")";
};
//****************************************//
//*** Not part of the WebkitCSSMatrix ***//
//*** interface (but used in Zoomooz) ***//
//****************************************//
CssMatrix.prototype.elements = function() {
var mv = this.m.elements;
return {"a":mv[0][0],"b":mv[1][0],"c":mv[0][1],
"d":mv[1][1],"e":mv[0][2],"f":mv[1][2]};
};
//**********************************//
//*** Helpers ***//
//**********************************//
function filterNumber(x) {
return x.match(regexp_filter_number);
}
function printFixedNumber(x) {
return Number(x).toFixed(6);
}
return CssMatrix;
})();;/*
* jquery.zoomooz-helpers.js, part of:
* http://janne.aukia.com/zoomooz
*
* LICENCE INFORMATION:
*
* Copyright (c) 2010 Janne Aukia (janne.aukia.com)
* Dual licensed under the MIT (MIT-LICENSE.txt)
* and GPL Version 2 (GPL-LICENSE.txt) licenses.
*
*/
/*jslint sub: true */
if(!$.zoomooz) {
$.zoomooz = {};
}
$.zoomooz.helpers = (function($, ns) {
"use strict";
//**********************************//
//*** Variables ***//
//**********************************//
var browser_prefixes = ["-moz-","-webkit-","-o-","-ms-"];
//**********************************//
//*** Helpers ***//
//**********************************//
ns.forEachPrefix = function(func,includeNoPrefix) {
for(var i=0;i<browser_prefixes.length;i++) {
func(browser_prefixes[i]);
}
if(includeNoPrefix) {
func("");
}
};
ns.getElementTransform = function(elem) {
var retVal;
ns.forEachPrefix(function(prefix) {
retVal = retVal || $(elem).css(prefix+"transform");
},true);
return retVal;
};
return ns;
})(jQuery, {});;/*
* jquery.zoomooz-anim.js, part of:
* http://janne.aukia.com/zoomooz
*
* LICENCE INFORMATION:
*
* Copyright (c) 2010 Janne Aukia (janne.aukia.com)
* Dual licensed under the MIT (MIT-LICENSE.txt)
* and GPL Version 2 (GPL-LICENSE.txt) licenses.
*
* LICENCE INFORMATION FOR DERIVED FUNCTIONS:
*
* Functions CubicBezierAtPosition and
* CubicBezierAtTime are written by Christian Effenberger,
* and correspond 1:1 to WebKit project functions.
* "WebCore and JavaScriptCore are available under the
* Lesser GNU Public License. WebKit is available under
* a BSD-style license."
*
*/
/*jslint sub: true */
(function($) {
"use strict";
//**********************************//
//*** Variables ***//
//**********************************//
var animation_start_time;
var animation_interval_timer;
var regexp_filter_number = /([0-9.\-e]+)/g;
var regexp_trans_splitter = /([a-z]+)\(([^\)]+)\)/g;
var regexp_is_deg = /deg$/;
var helpers = $.zoomooz.helpers;
var default_settings = {
duration: 450,
easing: "ease",
/* Native animation may cause issues with pixelated content while zooming,
and there might be other issues with browser compatibility etc. so use
it with care and test on your target devices/browsers :). */
nativeanimation: false
};
var endCallbackTimeout;
//**********************************//
//*** Setup css hook for IE ***//
//**********************************//
jQuery.cssHooks["MsTransform"] = {
set: function( elem, value ) {
elem.style.msTransform = value;
}
};
jQuery.cssHooks["MsTransformOrigin"] = {
set: function( elem, value ) {
elem.style.msTransformOrigin = value;
}
};
//**********************************//
//*** jQuery functions ***//
//**********************************//
$.fn.animateTransformation = function(transformation, settings, posOffset, animateEndCallback, animateStartedCallback) {
settings = jQuery.extend({}, default_settings, settings);
// FIXME: what would be the best way to handle leftover animations?
if(endCallbackTimeout) {
clearTimeout(endCallbackTimeout);
endCallbackTimeout = null;
}
if(settings.nativeanimation && animateEndCallback) {
endCallbackTimeout = setTimeout(animateEndCallback, settings.duration);
}
this.each(function() {
var $target = $(this);
if(!transformation) transformation = new PureCSSMatrix();
var current_affine = constructAffineFixingRotation($target, posOffset);
var final_affine = fixRotationToSameLap(current_affine, affineTransformDecompose(transformation));
if(settings.nativeanimation) {
$target.css(constructZoomRootCssTransform(matrixCompose(final_affine), settings.duration, settings.easing));
if(animateStartedCallback) {
animateStartedCallback();
}
} else {
animateTransition($target, current_affine, final_affine, settings, animateEndCallback, animateStartedCallback);
}
});
};
$.fn.setTransformation = function(transformation) {
this.each(function() {
var $target = $(this);
var current_affine = constructAffineFixingRotation($target);
var final_affine = fixRotationToSameLap(current_affine, affineTransformDecompose(transformation));
$target.css(constructZoomRootCssTransform(matrixCompose(final_affine)));
});
};
//**********************************//
//*** Element positioning ***//
//**********************************//
function constructZoomRootCssTransform(trans, duration, easing) {
var propMap = {};
helpers.forEachPrefix(function(prefix) {
propMap[prefix+"transform"] = trans;
},true);
if(duration) {
var transdur = roundNumber(duration/1000,6)+"s";
propMap["-webkit-transition-duration"] = transdur;
propMap["-o-transition-duration"] = transdur;
propMap["-moz-transition-duration"] = transdur;
}
if(easing) {
var transtiming = constructEasingCss(easing);
propMap["-webkit-transition-timing-function"] = transtiming;
propMap["-o-transition-timing-function"] = transtiming;
propMap["-moz-transition-timing-function"] = transtiming;
}
return propMap;
}
//**********************************//
//*** Non-native animation ***//
//**********************************//
function animateTransition($target, st, et, settings, animateEndCallback, animateStartedCallback) {
if(!st) {
st = affineTransformDecompose(new PureCSSMatrix());
}
animation_start_time = (new Date()).getTime();
if(animation_interval_timer) {
clearInterval(animation_interval_timer);
animation_interval_timer = null;
}
if(settings.easing) {
settings.easingfunction = constructEasingFunction(settings.easing, settings.duration);
}
// first step
animationStep($target, st, et, settings, animateEndCallback);
if(animateStartedCallback) {
animateStartedCallback();
}
animation_interval_timer = setInterval(function() { animationStep($target, st, et, settings, animateEndCallback); }, 1);
}
function animationStep($target, affine_start, affine_end, settings, animateEndCallback) {
var current_time = (new Date()).getTime() - animation_start_time;
var time_value;
if(settings.easingfunction) {
time_value = settings.easingfunction(current_time/settings.duration);
} else {
time_value = current_time/settings.duration;
}
$target.css(constructZoomRootCssTransform(matrixCompose(interpolateArrays(affine_start, affine_end, time_value))));
if(current_time>settings.duration) {
clearInterval(animation_interval_timer);
animation_interval_timer = null;
time_value=1.0;
if(animateEndCallback) {
animateEndCallback();
}
}
}
/* Based on pseudo-code in:
* https://bugzilla.mozilla.org/show_bug.cgi?id=531344
*/
function affineTransformDecompose(matrix) {
var m = matrix.elements();
var a=m.a, b=m.b, c=m.c, d=m.d, e=m.e, f=m.f;
if(Math.abs(a*d-b*c)<0.01) {
console.log("fail!");
return;
}
var tx = e, ty = f;
var sx = Math.sqrt(a*a+b*b);
a = a/sx;
b = b/sx;
var k = a*c+b*d;
c -= a*k;
d -= b*k;
var sy = Math.sqrt(c*c+d*d);
c = c/sy;
d = d/sy;
k = k/sy;
if((a*d-b*c)<0.0) {
a = -a;
b = -b;
c = -c;
d = -d;
sx = -sx;
sy = -sy;
}
var r = Math.atan2(b,a);
return {"tx":tx, "ty":ty, "r":r, "k":Math.atan(k), "sx":sx, "sy":sy};
}
function matrixCompose(ia) {
var ret = "";
/* this probably made safari 5.1.1. + os 10.6.8 + non-unibody mac? */
//ret += "translateZ(0) ";
ret += "translate("+roundNumber(ia.tx,6)+"px,"+roundNumber(ia.ty,6)+"px) ";
ret += "rotate("+roundNumber(ia.r,6)+"rad) skewX("+roundNumber(ia.k,6)+"rad) ";
ret += "scale("+roundNumber(ia.sx,6)+","+roundNumber(ia.sy,6)+")";
return ret;
}
//**********************************//
//*** Easing functions ***//
//**********************************//
function constructEasingCss(input) {
if((input instanceof Array)) {
return "cubic-bezier("+roundNumber(input[0],6)+","+roundNumber(input[1],6)+","+
roundNumber(input[2],6)+","+roundNumber(input[3],6)+")";
} else {
return input;
}
}
function constructEasingFunction(input, dur) {
var params = [];
if((input instanceof Array)) {
params = input;
} else {
switch(input) {
case "linear": params = [0.0,0.0,1.0,1.0]; break;
case "ease": params = [0.25,0.1,0.25,1.0]; break;
case "ease-in": params = [0.42,0.0,1.0,1.0]; break;
case "ease-out": params = [0.0,0.0,0.58,1.0]; break;
case "ease-in-out": params = [0.42,0.0,0.58,1.0]; break;
}
}
var easingFunc = function(t) {
return cubicBezierAtTime(t, params[0], params[1], params[2], params[3], dur);
};
return easingFunc;
}
// From: http://www.netzgesta.de/dev/cubic-bezier-timing-function.html
function cubicBezierAtPosition(t,P1x,P1y,P2x,P2y) {
var x,y,k=((1-t)*(1-t)*(1-t));
x=P1x*(3*t*t*(1-t))+P2x*(3*t*(1-t)*(1-t))+k;
y=P1y*(3*t*t*(1-t))+P2y*(3*t*(1-t)*(1-t))+k;
return {x:Math.abs(x),y:Math.abs(y)};
}
// From: http://www.netzgesta.de/dev/cubic-bezier-timing-function.html
// 1:1 conversion to js from webkit source files
// UnitBezier.h, WebCore_animation_AnimationBase.cpp
function cubicBezierAtTime(t,p1x,p1y,p2x,p2y,duration) {
var ax=0,bx=0,cx=0,ay=0,by=0,cy=0;
// `ax t^3 + bx t^2 + cx t' expanded using Horner's rule.
function sampleCurveX(t) {return ((ax*t+bx)*t+cx)*t;}
function sampleCurveY(t) {return ((ay*t+by)*t+cy)*t;}
function sampleCurveDerivativeX(t) {return (3.0*ax*t+2.0*bx)*t+cx;}
// The epsilon value to pass given that the animation is going to run over |dur| seconds. The longer the
// animation, the more precision is needed in the timing function result to avoid ugly discontinuities.
function solveEpsilon(duration) {return 1.0/(200.0*duration);}
function solve(x,epsilon) {return sampleCurveY(solveCurveX(x,epsilon));}
// Given an x value, find a parametric value it came from.
function solveCurveX(x,epsilon) {var t0,t1,t2,x2,d2,i;
function fabs(n) {if(n>=0) {return n;}else {return 0-n;}}
// First try a few iterations of Newton's method -- normally very fast.
for(t2=x, i=0; i<8; i++) {x2=sampleCurveX(t2)-x; if(fabs(x2)<epsilon) {return t2;} d2=sampleCurveDerivativeX(t2); if(fabs(d2)<1e-6) {break;} t2=t2-x2/d2;}
// Fall back to the bisection method for reliability.
t0=0.0; t1=1.0; t2=x; if(t2<t0) {return t0;} if(t2>t1) {return t1;}
while(t0<t1) {x2=sampleCurveX(t2); if(fabs(x2-x)<epsilon) {return t2;} if(x>x2) {t0=t2;}else {t1=t2;} t2=(t1-t0)*0.5+t0;}
return t2; // Failure.
}
// Calculate the polynomial coefficients, implicit first and last control points are (0,0) and (1,1).
cx=3.0*p1x; bx=3.0*(p2x-p1x)-cx; ax=1.0-cx-bx; cy=3.0*p1y; by=3.0*(p2y-p1y)-cy; ay=1.0-cy-by;
// Convert from input time to parametric value in curve, then from that to output time.
return solve(t, solveEpsilon(duration));
}
//**********************************//
//*** CSS Matrix helpers ***//
//**********************************//
function constructAffineFixingRotation(elem, posOffset) {
var rawTrans = helpers.getElementTransform(elem);
var matr;
if(!rawTrans) {
matr = new PureCSSMatrix();
} else {
matr = new PureCSSMatrix(rawTrans);
}
if(posOffset) {
matr = matr.translate(posOffset.x,posOffset.y);
}
var current_affine = affineTransformDecompose(matr);
current_affine.r = getTotalRotation(rawTrans);
return current_affine;
}
function getTotalRotation(transString) {
var totalRot = 0;
var items;
while((items = regexp_trans_splitter.exec(transString)) !== null) {
var action = items[1].toLowerCase();
var val = items[2].split(",");
if(action=="matrix") {
var recomposedTransItem = action+"("+items[2]+")";
totalRot += affineTransformDecompose(new PureCSSMatrix(recomposedTransItem)).r;
} else if(action=="rotate") {
var raw = val[0];
var rot = parseFloat(filterNumber(raw));
if(raw.match(regexp_is_deg)) {
rot = (2*Math.PI)*rot/360.0;
}
totalRot += rot;
}
}
return totalRot;
}
// TODO: use modulo instead of loops
function fixRotationToSameLap(current_affine, final_affine) {
if(Math.abs(current_affine.r-final_affine.r)>Math.PI) {
if(final_affine.r<current_affine.r) {
while(Math.abs(current_affine.r-final_affine.r)>Math.PI) {
final_affine.r+=(2*Math.PI);
}
} else {
while(Math.abs(current_affine.r-final_affine.r)>Math.PI) {
final_affine.r-=(2*Math.PI);
}
}
}
return final_affine;
}
//**********************************//
//*** Helpers ***//
//**********************************//
function interpolateArrays(st, et, pos) {
var it = {};
for(var i in st) {
if (st.hasOwnProperty(i)) {
it[i] = st[i]+(et[i]-st[i])*pos;
}
}
return it;
}
function roundNumber(number, precision) {
precision = Math.abs(parseInt(precision,10)) || 0;
var coefficient = Math.pow(10, precision);
return Math.round(number*coefficient)/coefficient;
}
function filterNumber(x) {
return x.match(regexp_filter_number);
}
})(jQuery);;/*
* jquery.zoomooz-core.js, part of:
* http://janne.aukia.com/zoomooz
*
* Version history:
* 1.1.5 zoom for scrolled pages without flickering
* 1.1.0 carousel prev/next navigation
* 1.0.6 support for jQuery 1.9
* 1.0.4 fixed examples, iphone tuneups, transform offset fix
* 1.0.3 added closeclick, code structuring
* 1.0.2 bug fix on endcallback resetting for native animation
* 1.0.1 declarative syntax and fixes
* 0.9.2 working scrolling
* 0.9.1 simplifying code base and scrolling for non-body zoom roots
* 0.9.0 fixing margin on first body child
* 0.8.9 support for jQuery 1.7
* 0.8.8 fixed a bug with 90 deg rotations
* 0.8.7 fixed a bug with settings and a couple of demos
* 0.8.6 fixed a bug with non-body zoom root
* 0.8.5 basic IE9 support
* 0.8.1 basic support for scrolling
* 0.8.0 refactored position code to a separate file
* 0.7.2 fixed a bug with skew in Webkit
* 0.7.1 fixed bugs with FF4
* 0.7.0 support for non-body zoom root
* 0.6.9 better settings management
* 0.6.8 root element tuning
* 0.6.7 adjustable zoom origin (not fully working yet)
* 0.6.5 zoom origin to center
* 0.6.3 basic Opera support
* 0.6.1 refactored to use CSSMatrix classes
* 0.5.1 initial public version
*
* LICENCE INFORMATION:
*
* Copyright (c) 2010 Janne Aukia (janne.aukia.com)
* Dual licensed under the MIT (MIT-LICENSE.txt)
* and GPL Version 2 (GPL-LICENSE.txt) licenses.
*
* LICENCE INFORMATION FOR DERIVED FUNCTIONS:
*
* Function computeTotalTransformation based
* on jquery.fn.offset, copyright John Resig, 2010
* (MIT and GPL Version 2).
*
*/
/*jslint sub: true */
(function($) {
"use strict";
//**********************************//
//*** Variables ***//
//**********************************//
var helpers = $.zoomooz.helpers;
var animationSettings = ["duration", "easing", "nativeanimation"];
//**********************************//
//*** Static setup ***//
//**********************************//
// document.ready needed for scroll bar width
// calculation
setupCssStyles();
//**********************************//
//*** jQuery functions ***//
//**********************************//
if(!$.zoomooz) {
$.zoomooz = {};
}
/* this can be used for setting the default settings for zoomooz explicitly. */
$.zoomooz.setup = function(settings) {
$.zoomooz.defaultSettings = jQuery.extend(constructDefaultSettings(), settings);
};
/* returns the zooming settings of a particular element. used by zoomTarget. */
$.fn.zoomSettings = function(settings) {
var retValue;
this.each(function() {
var $elem = $(this);
retValue = setupElementSettings($elem, settings);
});
return retValue;
};
/* the main zooming method. */
$.fn.zoomTo = function(settings, skipElementSettings) {
this.each(function() {
var $this = $(this);
if(!skipElementSettings) {
settings = $this.zoomSettings(settings);
}
zoomTo($this, settings);
if(settings.debug) {
if($("#debug").length===0) {
$(settings.root).append('<div id="debug"><div>');
} else {
$("#debug").html("");
}
showDebug($this,settings);
} else {
if($("#debug").length!==0) {
$("#debug").html("");
}
}
});
return this;
};
//**********************************//
//*** Setup functions ***//
//**********************************//
function setupElementSettings($elem, baseSettings) {
var settings = jQuery.extend({}, baseSettings);
if(!$.zoomooz.defaultSettings) {
$.zoomooz.setup();
}
var defaultSettings = $.zoomooz.defaultSettings;
var elementSettings = jQuery.extend({},settings);
var key;
for(key in defaultSettings) {
if (defaultSettings.hasOwnProperty(key) && !elementSettings[key]) {
elementSettings[key] = $elem.data(key);
}
}
// FIXME: it would be better, that the animationSettings
// would come from the jquery.zoomooz-anim file somehow
for(var i=0;i<animationSettings.length;i++) {
key = animationSettings[i];
if(!elementSettings[key]) {
elementSettings[key] = $elem.data(key);
}
}
return jQuery.extend({}, defaultSettings, elementSettings);
}
/* setup css styles in javascript to not need an extra zoomooz.css file for the user to load.
having the styles here helps also at keeping the css requirements minimal. */
function setupCssStyles() {
var style = document.createElement('style');
style.type = 'text/css';
var transformOrigin = "";
helpers.forEachPrefix(function(prefix) {
transformOrigin += prefix+"transform-origin: 0 0;";
},true);
// FIXME: how to remove the html height requirement?
// FIXME: how to remove the transform origin?
style.innerHTML = "html {height:100%;}" +
".noScroll{overflow:hidden !important;}" +
"* {"+transformOrigin+"}";
document.getElementsByTagName('head')[0].appendChild(style);
$(document).ready(function() {
var scrollBarWidth = window.innerWidth - $("body").width();
style.innerHTML += "body.noScroll,html.noScroll body{margin-right:"+scrollBarWidth+"px;}";
});
}
function constructDefaultSettings() {
var retObject = {
targetsize: 0.9,
scalemode: "both",
root: $(document.body),
debug: false,
animationendcallback: null,
closeclick: false
};
// FIXME: feat detection would be better
var isFF = (window.mozInnerScreenX !== undefined);
retObject.scrollresetbeforezoom = isFF;
return retObject;
}
//**********************************//
//*** Main zoom function ***//
//**********************************//
function zoomTo(elem, settings) {
// scrolling:
var useScrollResetBeforeZoom = settings.scrollresetbeforezoom;
var scrollData = null;
var startedZoomFromScroll;
(function() {
var $root = settings.root;
var $scroll = $root.parent();
if(elem[0] === $root[0]) {
scrollData = getExistingScrollData($root, $scroll);
} else if(!$root.data("original-scroll")) {
startedZoomFromScroll = true;
scrollData = storeNewScrollData($root, $scroll, useScrollResetBeforeZoom);
} else if(!useScrollResetBeforeZoom) {
scrollData = getExistingScrollData($root, $scroll);
}
}());
var rootTransformation;
var animationendcallback = null;
setTransformOrigin(settings.root);
var animScrollData = null;
if(elem[0] !== settings.root[0]) {
var inv = computeTotalTransformation(elem, settings.root).inverse();
if(!useScrollResetBeforeZoom) {
animScrollData = scrollData;
}
rootTransformation = computeViewportTransformation(elem, inv, animScrollData, settings);
if(settings.animationendcallback) {
animationendcallback = function() {
settings.animationendcallback.call(elem[0]);
};
}
} else {
if(useScrollResetBeforeZoom) {
rootTransformation = (new PureCSSMatrix()).translate(-scrollData.x,-scrollData.y);
}
animationendcallback = function() {
var $root = $(settings.root);
var $scroll = scrollData.elem;
$scroll.removeClass("noScroll");
$root.setTransformation(new PureCSSMatrix());
$root.data("original-scroll",null);
$(document).off("touchmove");
if(useScrollResetBeforeZoom) {
// this needs to be after the setTransformation and
// done with window.scrollTo to not have iPhone repaints
if($scroll[0]==document.body || $scroll[0]==window) {
window.scrollTo(scrollData.x,scrollData.y);
} else {
$scroll.scrollLeft(scrollData.x);
$scroll.scrollTop(scrollData.y);
}
}
if(settings.animationendcallback) {
settings.animationendcallback.call(elem[0]);
}
};
}
var animationstartedcallback = null;
if(useScrollResetBeforeZoom && scrollData && scrollData.animationstartedcallback) {
animationstartedcallback = scrollData.animationstartedcallback;
}
if(!startedZoomFromScroll) {
animScrollData = false;
}
$(settings.root).animateTransformation(rootTransformation, settings, animScrollData, animationendcallback, animationstartedcallback);
}
//**********************************//
//*** Handle scrolling ***//
//**********************************//
function getExistingScrollData($root, $scroll) {
var scrollData = $root.data("original-scroll");
if(!scrollData) {
scrollData = {"elem": $scroll, "x":0,"y:":0};
}
return scrollData;
}
function storeNewScrollData($root, $scroll, useScrollResetBeforeZoom) {
// safari
var scrollY = $root.scrollTop();
var scrollX = $root.scrollLeft();
var elem = $root;
// moz
if(!scrollY) {
scrollY = $scroll.scrollTop();
scrollX = $scroll.scrollLeft();
elem = $scroll;
}
var scrollData = {"elem":elem,"x":scrollX,"y":scrollY};
$root.data("original-scroll",scrollData);
$(document).on("touchmove", function(e) {
e.preventDefault();
});
var transformStr = "translate(-"+scrollX+"px,-"+scrollY+"px)";
helpers.forEachPrefix(function(prefix) {
$root.css(prefix+"transform", transformStr);
});
elem.addClass("noScroll");
if(useScrollResetBeforeZoom) {
scrollData.animationstartedcallback = function() {
// this needs to be after the setTransformation and
// done with window.scrollTo to not have iPhone repaints
if(elem[0]==document.body || elem[0]==document) {
window.scrollTo(0,0);
} else {
elem.scrollLeft(0);
elem.scrollTop(0);
}
};
}
return scrollData;
}
//**********************************//
//*** Element positioning ***//
//**********************************//
function setTransformOrigin(zoomParent) {
var zoomViewport = $(zoomParent).parent();
var dw = zoomViewport.width();
var dh = zoomViewport.height();
var xrotorigin = dw/2.0;
var yrotorigin = dh/2.0;
var offsetStr = printFixedNumber(xrotorigin)+"px "+printFixedNumber(yrotorigin)+"px";
helpers.forEachPrefix(function(prefix) {
zoomParent.css(prefix+"transform-origin", offsetStr);
});
}
function computeViewportTransformation(elem, endtrans, scrollData, settings) {
var zoomAmount = settings.targetsize;
var zoomMode = settings.scalemode;
var zoomParent = settings.root;
var zoomViewport = $(zoomParent).parent();
var dw = zoomViewport.width();
var dh = zoomViewport.height();
var relw = dw/elem.outerWidth();
var relh = dh/elem.outerHeight();
var scale;
if(zoomMode=="width") {
scale = zoomAmount*relw;
} else if(zoomMode=="height") {
scale = zoomAmount*relh;
} else if(zoomMode=="both") {
scale = zoomAmount*Math.min(relw,relh);
} else if(zoomMode=="scale") {
scale = zoomAmount;
} else {
console.log("wrong zoommode");
return;
}
var xoffset = (dw-elem.outerWidth()*scale)/2.0;
var yoffset = (dh-elem.outerHeight()*scale)/2.0;
var xrotorigin = dw/2.0;
var yrotorigin = dh/2.0;
/* fix for body margins, hope that this does not break anything .. */
/* see also the part of the fix that is in computeTotalTransformation! */
var xmarginfix = -parseFloat(zoomParent.css("margin-left")) || 0;
var ymarginfix = -parseFloat(zoomParent.css("margin-top")) || 0;
var initTransformation = (new PureCSSMatrix());
if(scrollData) {
initTransformation = initTransformation.translate(scrollData.x,scrollData.y);
}
var viewportTransformation =
initTransformation
.translate(xmarginfix,ymarginfix)
.translate(-xrotorigin,-yrotorigin)
.translate(xoffset,yoffset)
.scale(scale,scale)
.multiply(endtrans)
.translate(xrotorigin,yrotorigin);
return viewportTransformation;
}
//**********************************//
//*** Debugging positioning ***//
//**********************************//
function calcPoint(e,x,y) {
return [e.a*x+e.c*y+e.e,e.b*x+e.d*y+e.f];
}
function showDebug(elem, settings) {
var e = computeTotalTransformation(elem, settings.root).elements();
displayLabel(calcPoint(e,0,0));
displayLabel(calcPoint(e,0,elem.outerHeight()));
displayLabel(calcPoint(e,elem.outerWidth(),elem.outerHeight()));
displayLabel(calcPoint(e,elem.outerWidth(),0));
}
function displayLabel(pos) {
var labelStyle = "width:4px;height:4px;background-color:red;position:absolute;margin-left:-2px;margin-top:-2px;";
labelStyle += 'left:'+pos[0]+'px;top:'+pos[1]+'px;';
var label = '<div class="debuglabel" style="'+labelStyle+'"></div>';
$("#debug").append(label);
}
//**********************************//
//*** Calculating element ***//
//*** total transformation ***//
//**********************************//
/* Based on:
* jQuery.fn.offset
*/
function computeTotalTransformation(input, transformationRootElement) {
var elem = input[0];
if( !elem || !elem.ownerDocument ) {
return null;
}
var totalTransformation = new PureCSSMatrix();
var trans;
if ( elem === elem.ownerDocument.body ) {
var bOffset = jQuery.offset.bodyOffset( elem );
trans = new PureCSSMatrix();
trans = trans.translate(bOffset.left, bOffset.top);
totalTransformation = totalTransformation.multiply(trans);
return totalTransformation;
}
var support;
if(jQuery.offset.initialize) {
jQuery.offset.initialize();
support = {
fixedPosition:jQuery.offset.supportsFixedPosition,
doesNotAddBorder:jQuery.offset.doesNotAddBorder,
doesAddBorderForTableAndCells:jQuery.support.doesAddBorderForTableAndCells,
subtractsBorderForOverflowNotVisible:jQuery.offset.subtractsBorderForOverflowNotVisible
};
} else {
support = jQuery.support;
}
var offsetParent = elem.offsetParent;
var doc = elem.ownerDocument;
var computedStyle;
var docElem = doc.documentElement;
var body = doc.body;
var root = transformationRootElement[0];
var defaultView = doc.defaultView;
var prevComputedStyle;
if(defaultView) {
prevComputedStyle = defaultView.getComputedStyle( elem, null );
} else {
prevComputedStyle = elem.currentStyle;
}
/*
function offsetParentInsideRoot($elem, $root) {
// FIXME:
// wondering, should this be $root.closest()
// or $root.parent().closest...
var $viewport = $root.parent();
var $offsetParent = $elem.offsetParent();
return ($viewport[0]==$offsetParent[0]) || $viewport.closest($offsetParent).length==0;
}
console.log("inside root",offsetParentInsideRoot(input, transformationRootElement));
*/
var top = elem.offsetTop;
var left = elem.offsetLeft;
var transformation = constructTransformation().translate(left,top);
transformation = transformation.multiply(constructTransformation(elem));
totalTransformation = transformation.multiply((totalTransformation));
// loop from node down to root
while ( (elem = elem.parentNode) && elem !== root) {
top = 0; left = 0;
if ( support.fixedPosition && prevComputedStyle.position === "fixed" ) {
break;
}
computedStyle = defaultView ? defaultView.getComputedStyle(elem, null) : elem.currentStyle;
top -= elem.scrollTop;
left -= elem.scrollLeft;
if ( elem === offsetParent ) {
top += elem.offsetTop;
left += elem.offsetLeft;
if ( support.doesNotAddBorder && !(support.doesAddBorderForTableAndCells && /^t(able|d|h)$/i.test(elem.nodeName)) ) {
top += parseFloat( computedStyle.borderTopWidth ) || 0;
left += parseFloat( computedStyle.borderLeftWidth ) || 0;
}
offsetParent = elem.offsetParent;
}
if ( support.subtractsBorderForOverflowNotVisible && computedStyle.overflow !== "visible" ) {
top += parseFloat( computedStyle.borderTopWidth ) || 0;
left += parseFloat( computedStyle.borderLeftWidth ) || 0;
}
prevComputedStyle = computedStyle;
if(elem.offsetParent==root) {
top -= parseFloat($(elem.offsetParent).css("margin-top")) || 0;
left -= parseFloat($(elem.offsetParent).css("margin-left")) || 0;
}
transformation = constructTransformation().translate(left,top);
transformation = transformation.multiply(constructTransformation(elem));
totalTransformation = transformation.multiply(totalTransformation);
}
top = 0;
left = 0;
// fixme: should disable these for non-body roots?
if ( prevComputedStyle.position === "relative" || prevComputedStyle.position === "static" ) {
top += body.offsetTop;
left += body.offsetLeft;
}
if ( support.fixedPosition && prevComputedStyle.position === "fixed" ) {
top += Math.max( docElem.scrollTop, body.scrollTop );
left += Math.max( docElem.scrollLeft, body.scrollLeft );
}
var itertrans = (new PureCSSMatrix()).translate(left,top);
totalTransformation = totalTransformation.multiply(itertrans);
return totalTransformation;
}
//**********************************//
//*** Helpers ***//
//**********************************//
function printFixedNumber(x) {
return Number(x).toFixed(6);
}
function constructTransformation(elem) {
var rawTrans = helpers.getElementTransform(elem);
if(!rawTrans) {
return new PureCSSMatrix();
} else {
return new PureCSSMatrix(rawTrans);
}
}
})(jQuery);;/*
* jquery.zoomooz-zoomTarget.js, part of:
* http://janne.aukia.com/zoomooz
*
* LICENCE INFORMATION:
*
* Copyright (c) 2010 Janne Aukia (janne.aukia.com)
* Dual licensed under the MIT (MIT-LICENSE.txt)
* and GPL Version 2 (GPL-LICENSE.txt) licenses.
*
*/
/*jslint sub: true */
(function($) {
"use strict";
if(!$.zoomooz) {
$.zoomooz = {};
}
//**********************************//
//*** Variables ***//
//**********************************//
var helpers = $.zoomooz.helpers;
//**********************************//
//*** jQuery functions ***//
//**********************************//
$.fn.zoomTarget = function(baseSettings) {
this.each(function() {
var settings = $(this).zoomSettings(baseSettings);
setupClickHandler($(this),$(this),settings);
});
};
//**********************************//
//*** Helper functions ***//
//**********************************//
function setupClickHandler(clickTarget, zoomTarget, settings) {
clickTarget.addClass("zoomTarget");
if(!settings.animationendcallback) {
if(!settings.closeclick) {
settings.animationendcallback = function() {
$(".selectedZoomTarget").removeClass("selectedZoomTarget zoomNotClickable");
clickTarget.addClass("selectedZoomTarget zoomNotClickable");
};
} else {
settings.animationendcallback = function() {
$(".selectedZoomTarget").removeClass("selectedZoomTarget zoomNotClickable");
clickTarget.addClass("selectedZoomTarget");
};
}
}
var zoomContainer = zoomTarget.closest(".zoomContainer");
if(zoomContainer.length!==0) {
settings.root = zoomContainer;
}
var $root = settings.root;
if(!$root.hasClass("zoomTarget")) {
var rootSettings = $root.zoomSettings({});
rootSettings.animationendcallback = function() {
var $elem = $(this);
$(".selectedZoomTarget").removeClass("selectedZoomTarget zoomNotClickable");
$elem.addClass("selectedZoomTarget zoomNotClickable");
$elem.parent().addClass("selectedZoomTarget zoomNotClickable");
};
setupClickHandler($root,$root,rootSettings);
setupClickHandler($root.parent(),$root,rootSettings);
// FIXME: there could be many of these called simultaneously,
// don't know what would happen then
$root.click();
}
clickTarget.on("click", function(evt) {
// closeclick not available here...
if(settings.closeclick && zoomTarget.hasClass("selectedZoomTarget")) {
settings.root.click();
} else {
zoomTarget.zoomTo(settings);
}
evt.stopPropagation();
});
}
//**********************************//
//*** Setup functions ***//
//**********************************//
/* setup css styles in javascript to not need an extra zoomooz.css file for the user to load.
having the styles here helps also at keeping the css requirements minimal. */
function setupCssStyles() {
var style = document.createElement('style');
style.type = 'text/css';
function setupSelectionCss(enabled) {
var selectionString = "-webkit-touch-callout: "+(enabled?"default":"none")+";";
helpers.forEachPrefix(function(prefix) {
selectionString += prefix+"user-select:"+(enabled?"text":"none")+";";
},true);
return selectionString;
}
// FIXME: how to remove the html height requirement?
// FIXME: how to remove the transform origin?
style.innerHTML = ".zoomTarget{"+setupSelectionCss(false)+"}"+
".zoomTarget:hover{cursor:pointer!important;}"+
".zoomNotClickable{"+setupSelectionCss(true)+"}"+
".zoomNotClickable:hover{cursor:auto!important;}"+
/* padding to fix margin collapse issues */
".zoomContainer{position:relative;padding:1px;margin:-1px;}";
document.getElementsByTagName('head')[0].appendChild(style);
}
//**********************************//
//*** Static setup ***//
//**********************************//
setupCssStyles();
// make all elements with the zoomTarget class zooming
$(document).ready(function() {
// this needs to be after the "$.fn.zoomTarget" has been initialized
$(".zoomTarget").zoomTarget();
});
})(jQuery);
;/*
* jquery.zoomooz-zoomContainer.js, part of:
* http://janne.aukia.com/zoomooz
*
* LICENCE INFORMATION:
*
* Copyright (c) 2010 Janne Aukia (janne.aukia.com)
* Dual licensed under the MIT (MIT-LICENSE.txt)
* and GPL Version 2 (GPL-LICENSE.txt) licenses.
*
*/
/*jslint sub: true */
(function($) {
"use strict";
if(!$.zoomooz) {
$.zoomooz = {};
}
//**********************************//
//*** Variables ***//
//**********************************//
//var helpers = $.zoomooz.helpers;
//**********************************//
//*** jQuery functions ***//
//**********************************//
$.fn.zoomContainer = function(settings) {
// add next and previous calls to the canvas
// (auto detect next and previous buttons)
};
//**********************************//
//*** Static setup ***//
//**********************************//
// FIXME: move zoomContainer styling here?
//setupCssStyles();
// make all elements with the zoomContainer class zooming containers
$(document).ready(function() {
// this needs to be after the "$.fn.zoomContainer" has been initialized
$(".zoomContainer").zoomContainer();
});
})(jQuery);
;/*
* jquery.zoomooz-zoomButton.js, part of:
* http://janne.aukia.com/zoomooz
*
* LICENCE INFORMATION:
*
* Copyright (c) 2010 Janne Aukia (janne.aukia.com)
* Dual licensed under the MIT (MIT-LICENSE.txt)
* and GPL Version 2 (GPL-LICENSE.txt) licenses.
*
*/
/*jslint sub: true */
(function($) {
if(!$.zoomooz) {
$.zoomooz = {};
}
//**********************************//
//*** Variables ***//
//**********************************//
var helpers = $.zoomooz.helpers;
//**********************************//
//*** jQuery functions ***//
//**********************************//
$.fn.zoomButton = function(baseSettings) {
this.each(function() {
var settings = setupZoomButtonSettings($(this),baseSettings);
setupClickHandler($(this),settings);
});
};
//**********************************//
//*** Setup functions ***//
//**********************************//
function setupZoomButtonSettings($elem, settings) {
var defaultSettings = constructDefaultSettings();
var elementSettings = jQuery.extend({},settings);
// FIXME: could move the core declarative stuff to a separate lib or file
for(var key in defaultSettings) {
if (defaultSettings.hasOwnProperty(key) && !elementSettings[key]) {
if(defaultSettings[key] instanceof jQuery) {
elementSettings[key] = $($elem.data(key));
} else {
elementSettings[key] = $elem.data(key);
}
}
}
return jQuery.extend({}, defaultSettings, elementSettings);
}
function constructDefaultSettings() {
return {
type: "next",
root: $(document.body),
wrap: "true"
};
}
//**********************************//
//*** Helper functions ***//
//**********************************//
function setupClickHandler(clickTarget, settings) {
clickTarget.addClass("zoomButton");
var $root;
if(settings.root.hasClass("zoomContainer")) {
$root = settings.root;
} else {
$root = settings.root.find(".zoomContainer");
}
var displayList = (function() {
var listData = jQuery.makeArray($root.find(".zoomTarget"));
function _getIndex(elem) {
return listData.indexOf(elem);
}
function _getNext(elem) {
var index = _getIndex(elem)+1;
if(index<listData.length && index!==0) {
return listData[index];
} else {
return null;
}
}
function _getPrev(elem) {
var index = _getIndex(elem)-1;
if(index<0) {
return null;
} else {
return listData[index];
}
}
function _getFirst() {
return listData[0];
}
function _getLast() {
return listData[listData.length-1];
}
return {
next: _getNext,
prev: _getPrev,
last: _getLast,
first: _getFirst
};
}());
clickTarget.on("click", function(evt) {
var target;
var performZoom = true;
var $selected = $root.find(".selectedZoomTarget");
if($selected.length===0) {
$selected = displayList.first();
}
if(settings.type.indexOf("prev")===0) {
target = displayList.prev($selected[0]);
if(target === null) {
if(settings.wrap) {
target = displayList.last();
} else {
performZoom = false;
}
}
} else {
target = displayList.next($selected[0]);
if(target === null) {
if(settings.wrap) {
target = displayList.first();
} else {
performZoom = false;
}
}
}
if(performZoom) {
// not this easy! would need to read the data fields
//target.zoomTo();
// FIXME: hacky...
target.click();
} else {
// don't do anything if no wrap
// (would be great if the button was disabled)
}
evt.stopPropagation();
});
}
//**********************************//
//*** Static setup ***//
//**********************************//
// make all elements with the zoomButton class activate
$(document).ready(function() {
// this needs to be after the "$.fn.zoomButton" has been initialized
$(".zoomButton").zoomButton();
});
})(jQuery);
/**
* Create and draw a new line-graph.
*
* Arguments:
* containerId => id of container to insert SVG into [REQUIRED]
* marginTop => Number of pixels for top margin. [OPTIONAL => Default: 20]
* marginRight => Number of pixels for right margin. [OPTIONAL => Default: 20]
* marginBottom => Number of pixels for bottom margin. [OPTIONAL => Default: 35]
* marginLeft => Number of pixels for left margin. [OPTIONAL => Default: 90]
* data => a dictionary containing the following keys [REQUIRED]
* values => The data array of arrays to graph. [REQUIRED]
* start => The start time in milliseconds since epoch of the data. [REQUIRED]
* end => The end time in milliseconds since epoch of the data. [REQUIRED]
* step => The time in milliseconds between each data value. [REQUIRED]
* names => The metric name for each array of data. [REQUIRED]
* displayNames => Display name for each metric. [OPTIONAL => Default: same as 'names' argument]
* Example: ['MetricA', 'MetricB']
* axis => Which axis (left/right) to put each metric on. [OPTIONAL => Default: Display all values on single axis]
* Example: ['left', 'right', 'right'] to display first metric on left axis, next two on right axis.
* colors => What color to use for each metric. [OPTIONAL => Default: black]
* Example: ['blue', 'red'] to display first metric in blue and second in red.
* scale => What scale to display the graph with. [OPTIONAL => Default: linear]
* Possible Values: linear, pow, log
* rounding => How many decimal points to round each metric to. [OPTIONAL => Default: Numbers are rounded to whole numbers (0 decimals)]
* Example: [2, 1] to display first metric with 2 decimals and second metric with 1.
* numAxisLabelsPowerScale => Hint for how many labels should be displayed for the Y-axis in Power scale. [OPTIONAL => Default: 6]
* numAxisLabelsLinearScale => Hint for how many labels should be displayed for the Y-axis in Linear scale. [OPTIONAL => Default: 6]
*
* Events (fired from container):
* LineGraph:dataModification => whenever data is changed
* LineGraph:configModification => whenever config is changed
*/
function LineGraph(argsMap) {
/* *************************************************************** */
/* public methods */
/* *************************************************************** */
var self = this;
/**
* This allows appending new data points to the end of the lines and sliding them within the time window:
* - x-axis will slide to new range
* - new data will be added to the end of the lines
* - equivalent number of data points will be removed from beginning of lines
* - lines will be transitioned through horizontoal slide to show progression over time
*/
this.slideData = function(newData) {
// validate data
var tempData = processDataMap(newData);
debug("Existing startTime: " + data.startTime + " endTime: " + data.endTime);
debug("New startTime: " + tempData.startTime + " endTime: " + tempData.endTime);
// validate step is the same on each
if(tempData.step != newData.step) {
throw new Error("The step size on appended data must be the same as the existing data => " + data.step + " != " + tempData.step);
}
if(tempData.values[0].length == 0) {
throw new Error("There is no data to append.");
}
var numSteps = tempData.values[0].length;
console.log("slide => add num new values: " + numSteps);
console.log(tempData.values[0])
tempData.values.forEach(function(dataArrays, i) {
var existingDataArrayForIndex = data.values[i];
dataArrays.forEach(function(v) {
console.log("slide => add new value: " + v);
// push each new value onto the existing data array
existingDataArrayForIndex.push(v);
// shift the front value off to compensate for what we just added
existingDataArrayForIndex.shift();
})
})
// shift domain by number of data elements we just added
// == numElements * step
data.startTime = new Date(data.startTime.getTime() + (data.step * numSteps));
data.endTime = tempData.endTime;
debug("Updated startTime: " + data.startTime + " endTime: " + data.endTime);
/*
* The following transition implementation was learned from examples at http://bost.ocks.org/mike/path/
* In particular, view the HTML source for the last example on the page inside the tick() function.
*/
// redraw each of the lines
// Transitions are turned off on this since the small steps we're taking
// don't actually look good when animated and it uses unnecessary CPU
// The quick-steps look cleaner, and keep the axis/line in-sync instead of jittering
redrawAxes(false);
redrawLines(false);
// slide the lines left
graph.selectAll("g .lines path")
.attr("transform", "translate(-" + x(numSteps*data.step) + ")");
handleDataUpdate();
// fire an event that data was updated
$(container).trigger('LineGraph:dataModification')
}
/**
* This does a full refresh of the data:
* - x-axis will slide to new range
* - lines will change in place
*/
this.updateData = function(newData) {
// data is being replaced, not appended so we re-assign 'data'
data = processDataMap(newData);
// and then we rebind data.values to the lines
graph.selectAll("g .lines path").data(data.values)
// redraw (with transition)
redrawAxes(true);
// transition is 'false' for lines because the transition is really weird when the data significantly changes
// such as going from 700 points to 150 to 400
// and because of that we rebind the data anyways which doesn't work with transitions very well at all
redrawLines(false);
handleDataUpdate();
// fire an event that data was updated
$(container).trigger('LineGraph:dataModification')
}
this.switchToPowerScale = function() {
yScale = 'pow';
redrawAxes(true);
redrawLines(true);
// fire an event that config was changed
$(container).trigger('LineGraph:configModification')
}
this.switchToLogScale = function() {
yScale = 'log';
redrawAxes(true);
redrawLines(true);
// fire an event that config was changed
$(container).trigger('LineGraph:configModification')
}
this.switchToLinearScale = function() {
yScale = 'linear';
redrawAxes(true);
redrawLines(true);
// fire an event that config was changed
$(container).trigger('LineGraph:configModification')
}
/**
* Return the current scale value: pow, log or linear
*/
this.getScale = function() {
return yScale;
}
/* *************************************************************** */
/* private variables */
/* *************************************************************** */
// the div we insert the graph into
var containerId;
var container;
// functions we use to display and interact with the graphs and lines
var graph, x, yLeft, yRight, xAxis, yAxisLeft, yAxisRight, yAxisLeftDomainStart, linesGroup, linesGroupText, lines, lineFunction, lineFunctionSeriesIndex = -1;
var yScale = 'linear'; // can be pow, log, linear
var scales = [['linear','Linear'], ['pow','Power'], ['log','Log']];
var hoverContainer, hoverLine, hoverLineXOffset, hoverLineYOffset, hoverLineGroup;
var legendFontSize = 12; // we can resize dynamically to make fit so we remember it here
// instance storage of data to be displayed
var data;
// define dimensions of graph
var margin = [-1, -1, -1, -1]; // margins (top, right, bottom, left)
var w, h; // width & height
var transitionDuration = 300;
var formatNumber = d3.format(",.0f") // for formatting integers
var tickFormatForLogScale = function(d) { return formatNumber(d) };
// used to track if the user is interacting via mouse/finger instead of trying to determine
// by analyzing various element class names to see if they are visible or not
var userCurrentlyInteracting = false;
var currentUserPositionX = -1;
/* *************************************************************** */
/* initialization and validation */
/* *************************************************************** */
var _init = function() {
// required variables that we'll throw an error on if we don't find
containerId = getRequiredVar(argsMap, 'containerId');
container = document.querySelector('#' + containerId);
// margins with defaults (do this before processDataMap since it can modify the margins)
margin[0] = getOptionalVar(argsMap, 'marginTop', 20) // marginTop allows fitting the actions, date and top of axis labels
margin[1] = getOptionalVar(argsMap, 'marginRight', 20)
margin[2] = getOptionalVar(argsMap, 'marginBottom', 35) // marginBottom allows fitting the legend along the bottom
margin[3] = getOptionalVar(argsMap, 'marginLeft', 90) // marginLeft allows fitting the axis labels
// assign instance vars from dataMap
data = processDataMap(getRequiredVar(argsMap, 'data'));
/* set the default scale */
yScale = data.scale;
// do this after processing margins and executing processDataMap above
initDimensions();
createGraph()
//debug("Initialization successful for container: " + containerId)
// window resize listener
// de-dupe logic from http://stackoverflow.com/questions/667426/javascript-resize-event-firing-multiple-times-while-dragging-the-resize-handle/668185#668185
var TO = false;
$(window).resize(function(){
if(TO !== false)
clearTimeout(TO);
TO = setTimeout(handleWindowResizeEvent, 200); // time in miliseconds
});
}
/* *************************************************************** */
/* private methods */
/* *************************************************************** */
/*
* Return a validated data map
*
* Expects a map like this:
* {"start": 1335035400000, "end": 1335294600000, "step": 300000, "values": [[28,22,45,65,34], [45,23,23,45,65]]}
*/
var processDataMap = function(dataMap) {
// assign data values to plot over time
var dataValues = getRequiredVar(dataMap, 'values', "The data object must contain a 'values' value with a data array.")
var startTime = new Date(getRequiredVar(dataMap, 'start', "The data object must contain a 'start' value with the start time in milliseconds since epoch."))
var endTime = new Date(getRequiredVar(dataMap, 'end', "The data object must contain an 'end' value with the end time in milliseconds since epoch."))
var step = getRequiredVar(dataMap, 'step', "The data object must contain a 'step' value with the time in milliseconds between each data value.")
var names = getRequiredVar(dataMap, 'names', "The data object must contain a 'names' array with the same length as 'values' with a name for each data value array.")
var displayNames = getOptionalVar(dataMap, 'displayNames', names);
var numAxisLabelsPowerScale = getOptionalVar(dataMap, 'numAxisLabelsPowerScale', 6);
var numAxisLabelsLinearScale = getOptionalVar(dataMap, 'numAxisLabelsLinearScale', 6);
var axis = getOptionalVar(dataMap, 'axis', []);
// default axis values
if(axis.length == 0) {
displayNames.forEach(function (v, i) {
// set the default to left axis
axis[i] = "left";
})
} else {
var hasRightAxis = false;
axis.forEach(function(v) {
if(v == 'right') {
hasRightAxis = true;
}
})
if(hasRightAxis) {
// add space to right margin
margin[1] = margin[1] + 50;
}
}
var colors = getOptionalVar(dataMap, 'colors', []);
// default colors values
if(colors.length == 0) {
displayNames.forEach(function (v, i) {
// set the default
colors[i] = "black";
})
}
var maxValues = [];
var rounding = getOptionalVar(dataMap, 'rounding', []);
// default rounding values
if(rounding.length == 0) {
displayNames.forEach(function (v, i) {
// set the default to 0 decimals
rounding[i] = 0;
})
}
/* copy the dataValues array, do NOT assign the reference otherwise we modify the original source when we shift/push data */
var newDataValues = [];
dataValues.forEach(function (v, i) {
newDataValues[i] = v.slice(0);
maxValues[i] = d3.max(newDataValues[i])
})
return {
"values" : newDataValues,
"startTime" : startTime,
"endTime" : endTime,
"step" : step,
"names" : names,
"displayNames": displayNames,
"axis" : axis,
"colors": colors,
"scale" : getOptionalVar(dataMap, 'scale', yScale),
"maxValues" : maxValues,
"rounding" : rounding,
"numAxisLabelsLinearScale": numAxisLabelsLinearScale,
"numAxisLabelsPowerScale": numAxisLabelsPowerScale
}
}
var redrawAxes = function(withTransition) {
initY();
initX();
if(withTransition) {
// slide x-axis to updated location
graph.selectAll("g .x.axis").transition()
.duration(transitionDuration)
.ease("linear")
.call(xAxis)
// slide y-axis to updated location
graph.selectAll("g .y.axis.left").transition()
.duration(transitionDuration)
.ease("linear")
.call(yAxisLeft)
if(yAxisRight != undefined) {
// slide y-axis to updated location
graph.selectAll("g .y.axis.right").transition()
.duration(transitionDuration)
.ease("linear")
.call(yAxisRight)
}
} else {
// slide x-axis to updated location
graph.selectAll("g .x.axis")
.call(xAxis)
// slide y-axis to updated location
graph.selectAll("g .y.axis.left")
.call(yAxisLeft)
if(yAxisRight != undefined) {
// slide y-axis to updated location
graph.selectAll("g .y.axis.right")
.call(yAxisRight)
}
}
}
var redrawLines = function(withTransition) {
/**
* This is a hack to deal with the left/right axis.
*
* See createGraph for a larger comment explaining this.
*
* Yes, it's ugly. If you can suggest a better solution please do.
*/
lineFunctionSeriesIndex =-1;
// redraw lines
if(withTransition) {
graph.selectAll("g .lines path")
.transition()
.duration(transitionDuration)
.ease("linear")
.attr("d", lineFunction)
.attr("transform", null);
} else {
graph.selectAll("g .lines path")
.attr("d", lineFunction)
.attr("transform", null);
}
}
/*
* Allow re-initializing the y function at any time.
* - it will properly determine what scale is being used based on last user choice (via public switchScale methods)
*/
var initY = function() {
initYleft();
initYright();
}
var initYleft = function() {
var maxYscaleLeft = calculateMaxY(data, 'left')
//debug("initY => maxYscale: " + maxYscaleLeft);
var numAxisLabels = 6;
if(yScale == 'pow') {
yLeft = d3.scale.pow().exponent(0.3).domain([0, maxYscaleLeft]).range([h, 0]).nice();
numAxisLabels = data.numAxisLabelsPowerScale;
} else if(yScale == 'log') {
// we can't have 0 so will represent 0 with a very small number
// 0.1 works to represent 0, 0.01 breaks the tickFormatter
yLeft = d3.scale.log().domain([0.1, maxYscaleLeft]).range([h, 0]).nice();
} else if(yScale == 'linear') {
yLeft = d3.scale.linear().domain([0, maxYscaleLeft]).range([h, 0]).nice();
numAxisLabels = data.numAxisLabelsLinearScale;
}
yAxisLeft = d3.svg.axis().scale(yLeft).ticks(numAxisLabels, tickFormatForLogScale).orient("left");
}
var initYright = function() {
var maxYscaleRight = calculateMaxY(data, 'right')
// only create the right axis if it has values
if(maxYscaleRight != undefined) {
//debug("initY => maxYscale: " + maxYscaleRight);
var numAxisLabels = 6;
if(yScale == 'pow') {
yRight = d3.scale.pow().exponent(0.3).domain([0, maxYscaleRight]).range([h, 0]).nice();
numAxisLabels = data.numAxisLabelsPowerScale;
} else if(yScale == 'log') {
// we can't have 0 so will represent 0 with a very small number
// 0.1 works to represent 0, 0.01 breaks the tickFormatter
yRight = d3.scale.log().domain([0.1, maxYscaleRight]).range([h, 0]).nice();
} else if(yScale == 'linear') {
yRight = d3.scale.linear().domain([0, maxYscaleRight]).range([h, 0]).nice();
numAxisLabels = data.numAxisLabelsLinearScale;
}
yAxisRight = d3.svg.axis().scale(yRight).ticks(numAxisLabels, tickFormatForLogScale).orient("right");
}
}
/*
* Whenever we add/update data we want to re-calculate if the max Y scale has changed
*/
var calculateMaxY = function(data, whichAxis) {
// Y scale will fit values from 0-10 within pixels h-0 (Note the inverted domain for the y-scale: bigger is up!)
// we get the max of the max of values for the given index since we expect an array of arrays
// we can shortcut to using data.maxValues since we've already calculated the max of each series in processDataMap
var maxValuesForAxis = [];
data.maxValues.forEach(function(v, i) {
if(data.axis[i] == whichAxis) {
maxValuesForAxis.push(v);
}
})
// we now have the max values for the axis we're interested in so get the max of them
return d3.max(maxValuesForAxis);
}
/*
* Allow re-initializing the x function at any time.
*/
var initX = function() {
// X scale starts at epoch time 1335035400000, ends at 1335294600000 with 300s increments
x = d3.time.scale().domain([data.startTime, data.endTime]).range([0, w]);
// create yAxis (with ticks)
xAxis = d3.svg.axis().scale(x).tickSize(-h).tickSubdivide(1);
// without ticks
//xAxis = d3.svg.axis().scale(x);
}
/**
* Creates the SVG elements and displays the line graph.
*
* Expects to be called once during instance initialization.
*/
var createGraph = function() {
// Add an SVG element with the desired dimensions and margin.
graph = d3.select("#" + containerId).append("svg:svg")
.attr("class", "line-graph")
.attr("width", w + margin[1] + margin[3])
.attr("height", h + margin[0] + margin[2])
.append("svg:g")
.attr("transform", "translate(" + margin[3] + "," + margin[0] + ")");
initX()
// Add the x-axis.
graph.append("svg:g")
.attr("class", "x axis")
.attr("transform", "translate(0," + h + ")")
.call(xAxis);
// y is all done in initY because we need to re-assign vars quite often to change scales
initY();
// Add the y-axis to the left
graph.append("svg:g")
.attr("class", "y axis left")
.attr("transform", "translate(-10,0)")
.call(yAxisLeft);
if(yAxisRight != undefined) {
// Add the y-axis to the right if we need one
graph.append("svg:g")
.attr("class", "y axis right")
.attr("transform", "translate(" + (w+10) + ",0)")
.call(yAxisRight);
}
// create line function used to plot our data
lineFunction = d3.svg.line()
// assign the X function to plot our line as we wish
.x(function(d,i) {
/*
* Our x value is defined by time and since our data doesn't have per-metric timestamps
* we calculate time as (startTime + the step between metrics * the index)
*
* We also reach out to the persisted 'data' object for time
* since the 'd' passed in here is one of the children, not the parent object
*/
var _x = x(data.startTime.getTime() + (data.step*i));
// verbose logging to show what's actually being done
//debug("Line X => index: " + i + " scale: " + _x)
// return the X coordinate where we want to plot this datapoint
return _x;
})
.y(function(d, i) {
if(yScale == 'log' && d < 0.1) {
// log scale can't have 0s, so we set it to the smallest value we set on y
d = 0.1;
}
/**
* This is a hack that relies on:
* a) the single-threaded nature of javascript that this will not be interleaved
* b) that lineFunction will always be passed the data[] for all lines in the same way each time
*
* We then use an external variable to track each time we move from one series to the next
* so that we can have its seriesIndex to access information in the data[] object, particularly
* so we can determine what axis this data is supposed to be on.
*
* I didn't want to split the line function into left and right lineFunctions as that would really
* complicate the data binding.
*
* Also ... I can't figure out nested functions to keep it scoped so I had to put lineFunctionSeriesIndex
* as a variable in the same scope as lineFunction. Ugly. And worse ... reset it in redrawAxes.
*
* Anyone reading this who knows a better solution please let me know.
*/
if(i == 0) {
lineFunctionSeriesIndex++;
}
var axis = data.axis[lineFunctionSeriesIndex];
var _y;
if(axis == 'right') {
_y = yRight(d);
} else {
_y = yLeft(d);
}
// verbose logging to show what's actually being done
//debug("Line Y => data: " + d + " scale: " + _y)
// return the Y coordinate where we want to plot this datapoint
return _y;
})
.defined(function(d) {
// handle missing data gracefully
// feature added in https://github.com/mbostock/d3/pull/594
return d >= 0;
});
// append a group to contain all lines
lines = graph.append("svg:g")
.attr("class", "lines")
.selectAll("path")
.data(data.values); // bind the array of arrays
// persist this reference so we don't do the selector every mouse event
hoverContainer = container.querySelector('g .lines');
$(container).mouseleave(function(event) {
handleMouseOutGraph(event);
})
$(container).mousemove(function(event) {
handleMouseOverGraph(event);
})
// add a line group for each array of values (it will iterate the array of arrays bound to the data function above)
linesGroup = lines.enter().append("g")
.attr("class", function(d, i) {
return "line_group series_" + i;
});
// add path (the actual line) to line group
linesGroup.append("path")
.attr("class", function(d, i) {
//debug("Appending line [" + containerId + "]: " + i)
return "line series_" + i;
})
.attr("fill", "none")
.attr("stroke", function(d, i) {
return data.colors[i];
})
.attr("d", lineFunction) // use the 'lineFunction' to create the data points in the correct x,y axis
.on('mouseover', function(d, i) {
handleMouseOverLine(d, i);
});
// add line label to line group
linesGroupText = linesGroup.append("svg:text");
linesGroupText.attr("class", function(d, i) {
//debug("Appending line [" + containerId + "]: " + i)
return "line_label series_" + i;
})
.text(function(d, i) {
return "";
});
// add a 'hover' line that we'll show as a user moves their mouse (or finger)
// so we can use it to show detailed values of each line
hoverLineGroup = graph.append("svg:g")
.attr("class", "hover-line");
// add the line to the group
hoverLine = hoverLineGroup
.append("svg:line")
.attr("x1", 10).attr("x2", 10) // vertical line so same value on each
.attr("y1", 0).attr("y2", h); // top to bottom
// hide it by default
hoverLine.classed("hide", true);
createScaleButtons();
createDateLabel();
createLegend();
setValueLabelsToLatest();
}
/**
* Create a legend that displays the name of each line with appropriate color coding
* and allows for showing the current value when doing a mouseOver
*/
var createLegend = function() {
// append a group to contain all lines
var legendLabelGroup = graph.append("svg:g")
.attr("class", "legend-group")
.selectAll("g")
.data(data.displayNames)
.enter().append("g")
.attr("class", "legend-labels");
legendLabelGroup.append("svg:text")
.attr("class", "legend name")
.text(function(d, i) {
return d;
})
.attr("font-size", legendFontSize)
.attr("fill", function(d, i) {
// return the color for this row
return data.colors[i];
})
.attr("y", function(d, i) {
return h+28;
})
// put in placeholders with 0 width that we'll populate and resize dynamically
legendLabelGroup.append("svg:text")
.attr("class", "legend value")
.attr("font-size", legendFontSize)
.attr("fill", function(d, i) {
return data.colors[i];
})
.attr("y", function(d, i) {
return h+28;
})
// x values are not defined here since those get dynamically calculated when data is set in displayValueLabelsForPositionX()
}
var redrawLegendPosition = function(animate) {
var legendText = graph.selectAll('g.legend-group text');
if(animate) {
legendText.transition()
.duration(transitionDuration)
.ease("linear")
.attr("y", function(d, i) {
return h+28;
});
} else {
legendText.attr("y", function(d, i) {
return h+28;
});
}
}
/**
* Create scale buttons for switching the y-axis
*/
var createScaleButtons = function() {
var cumulativeWidth = 0;
// append a group to contain all lines
var buttonGroup = graph.append("svg:g")
.attr("class", "scale-button-group")
.selectAll("g")
.data(scales)
.enter().append("g")
.attr("class", "scale-buttons")
.append("svg:text")
.attr("class", "scale-button")
.text(function(d, i) {
return d[1];
})
.attr("font-size", "12") // this must be before "x" which dynamically determines width
.attr("fill", function(d) {
if(d[0] == yScale) {
return "black";
} else {
return "blue";
}
})
.classed("selected", function(d) {
if(d[0] == yScale) {
return true;
} else {
return false;
}
})
.attr("x", function(d, i) {
// return it at the width of previous labels (where the last one ends)
var returnX = cumulativeWidth;
// increment cumulative to include this one
cumulativeWidth += this.getComputedTextLength()+5;
return returnX;
})
.attr("y", -4)
.on('click', function(d, i) {
handleMouseClickScaleButton(this, d, i);
});
}
var handleMouseClickScaleButton = function(button, buttonData, index) {
if(index == 0) {
self.switchToLinearScale();
} else if(index == 1) {
self.switchToPowerScale();
} else if(index == 2) {
self.switchToLogScale();
}
// change text decoration
graph.selectAll('.scale-button')
.attr("fill", function(d) {
if(d[0] == yScale) {
return "black";
} else {
return "blue";
}
})
.classed("selected", function(d) {
if(d[0] == yScale) {
return true;
} else {
return false;
}
})
}
/**
* Create a data label
*/
var createDateLabel = function() {
var date = new Date(); // placeholder just so we can calculate a valid width
// create the date label to the left of the scaleButtons group
var buttonGroup = graph.append("svg:g")
.attr("class", "date-label-group")
.append("svg:text")
.attr("class", "date-label")
.attr("text-anchor", "end") // set at end so we can position at far right edge and add text from right to left
.attr("font-size", "10")
.attr("y", -4)
.attr("x", w)
.text(date.toDateString() + " " + date.toLocaleTimeString())
}
/**
* Called when a user mouses over a line.
*/
var handleMouseOverLine = function(lineData, index) {
//debug("MouseOver line [" + containerId + "] => " + index)
// user is interacting
userCurrentlyInteracting = true;
}
/**
* Called when a user mouses over the graph.
*/
var handleMouseOverGraph = function(event) {
var mouseX = event.pageX-hoverLineXOffset;
var mouseY = event.pageY-hoverLineYOffset;
//debug("MouseOver graph [" + containerId + "] => x: " + mouseX + " y: " + mouseY + " height: " + h + " event.clientY: " + event.clientY + " offsetY: " + event.offsetY + " pageY: " + event.pageY + " hoverLineYOffset: " + hoverLineYOffset)
if(mouseX >= 0 && mouseX <= w && mouseY >= 0 && mouseY <= h) {
// show the hover line
hoverLine.classed("hide", false);
// set position of hoverLine
hoverLine.attr("x1", mouseX).attr("x2", mouseX)
displayValueLabelsForPositionX(mouseX)
// user is interacting
userCurrentlyInteracting = true;
currentUserPositionX = mouseX;
} else {
// proactively act as if we've left the area since we're out of the bounds we want
handleMouseOutGraph(event)
}
}
var handleMouseOutGraph = function(event) {
// hide the hover-line
hoverLine.classed("hide", true);
setValueLabelsToLatest();
//debug("MouseOut graph [" + containerId + "] => " + mouseX + ", " + mouseY)
// user is no longer interacting
userCurrentlyInteracting = false;
currentUserPositionX = -1;
}
/* // if we need to support older browsers without pageX/pageY we can use this
var getMousePositionFromEvent = function(e, element) {
var posx = 0;
var posy = 0;
if (!e) var e = window.event;
if (e.pageX || e.pageY) {
posx = e.pageX;
posy = e.pageY;
}
else if (e.clientX || e.clientY) {
posx = e.clientX + document.body.scrollLeft
+ document.documentElement.scrollLeft;
posy = e.clientY + document.body.scrollTop
+ document.documentElement.scrollTop;
}
return {x: posx, y: posy};
}
*/
/*
* Handler for when data is updated.
*/
var handleDataUpdate = function() {
if(userCurrentlyInteracting) {
// user is interacting, so let's update values to wherever the mouse/finger is on the updated data
if(currentUserPositionX > -1) {
displayValueLabelsForPositionX(currentUserPositionX)
}
} else {
// the user is not interacting with the graph, so we'll update the labels to the latest
setValueLabelsToLatest();
}
}
/**
* Display the data values at position X in the legend value labels.
*/
var displayValueLabelsForPositionX = function(xPosition, withTransition) {
var animate = false;
if(withTransition != undefined) {
if(withTransition) {
animate = true;
}
}
var dateToShow;
var labelValueWidths = [];
graph.selectAll("text.legend.value")
.text(function(d, i) {
var valuesForX = getValueForPositionXFromData(xPosition, i);
dateToShow = valuesForX.date;
return valuesForX.value;
})
.attr("x", function(d, i) {
labelValueWidths[i] = this.getComputedTextLength();
})
// position label names
var cumulativeWidth = 0;
var labelNameEnd = [];
graph.selectAll("text.legend.name")
.attr("x", function(d, i) {
// return it at the width of previous labels (where the last one ends)
var returnX = cumulativeWidth;
// increment cumulative to include this one + the value label at this index
cumulativeWidth += this.getComputedTextLength()+4+labelValueWidths[i]+8;
// store where this ends
labelNameEnd[i] = returnX + this.getComputedTextLength()+5;
return returnX;
})
// remove last bit of padding from cumulativeWidth
cumulativeWidth = cumulativeWidth - 8;
if(cumulativeWidth > w) {
// decrease font-size to make fit
legendFontSize = legendFontSize-1;
//debug("making legend fit by decreasing font size to: " + legendFontSize)
graph.selectAll("text.legend.name")
.attr("font-size", legendFontSize);
graph.selectAll("text.legend.value")
.attr("font-size", legendFontSize);
// recursively call until we get ourselves fitting
displayValueLabelsForPositionX(xPosition);
return;
}
// position label values
graph.selectAll("text.legend.value")
.attr("x", function(d, i) {
return labelNameEnd[i];
})
// show the date
graph.select('text.date-label').text(dateToShow.toDateString() + " " + dateToShow.toLocaleTimeString())
// move the group of labels to the right side
if(animate) {
graph.selectAll("g.legend-group g")
.transition()
.duration(transitionDuration)
.ease("linear")
.attr("transform", "translate(" + (w-cumulativeWidth) +",0)")
} else {
graph.selectAll("g.legend-group g")
.attr("transform", "translate(" + (w-cumulativeWidth) +",0)")
}
}
/**
* Set the value labels to whatever the latest data point is.
*/
var setValueLabelsToLatest = function(withTransition) {
displayValueLabelsForPositionX(w, withTransition);
}
/**
* Convert back from an X position on the graph to a data value from the given array (one of the lines)
* Return {value: value, date, date}
*/
var getValueForPositionXFromData = function(xPosition, dataSeriesIndex) {
var d = data.values[dataSeriesIndex]
// get the date on x-axis for the current location
var xValue = x.invert(xPosition);
// Calculate the value from this date by determining the 'index'
// within the data array that applies to this value
var index = (xValue.getTime() - data.startTime) / data.step;
if(index >= d.length) {
index = d.length-1;
}
// The date we're given is interpolated so we have to round off to get the nearest
// index in the data array for the xValue we're given.
// Once we have the index, we then retrieve the data from the d[] array
index = Math.round(index);
// bucketDate is the date rounded to the correct 'step' instead of interpolated
var bucketDate = new Date(data.startTime.getTime() + data.step * (index+1)); // index+1 as it is 0 based but we need 1-based for this math
var v = d[index];
var roundToNumDecimals = data.rounding[dataSeriesIndex];
return {value: roundNumber(v, roundToNumDecimals), date: bucketDate};
}
/**
* Called when the window is resized to redraw graph accordingly.
*/
var handleWindowResizeEvent = function() {
//debug("Window Resize Event [" + containerId + "] => resizing graph")
initDimensions();
initX();
// reset width/height of SVG
d3.select("#" + containerId + " svg")
.attr("width", w + margin[1] + margin[3])
.attr("height", h + margin[0] + margin[2]);
// reset transform of x axis
graph.selectAll("g .x.axis")
.attr("transform", "translate(0," + h + ")");
if(yAxisRight != undefined) {
// Reset the y-axisRight transform if it exists
graph.selectAll("g .y.axis.right")
.attr("transform", "translate(" + (w+10) + ",0)");
}
// reset legendFontSize on window resize so it has a chance to re-calculate to a bigger size if it can now fit
legendFontSize = 12;
//debug("making legend fit by decreasing font size to: " + legendFontSize)
graph.selectAll("text.legend.name")
.attr("font-size", legendFontSize);
graph.selectAll("text.legend.value")
.attr("font-size", legendFontSize);
// move date label
graph.select('text.date-label')
.transition()
.duration(transitionDuration)
.ease("linear")
.attr("x", w)
// redraw the graph with new dimensions
redrawAxes(true);
redrawLines(true);
// reposition legend if necessary
redrawLegendPosition(true);
// force legend to redraw values
setValueLabelsToLatest(true);
}
/**
* Set height/width dimensions based on container.
*/
var initDimensions = function() {
// automatically size to the container using JQuery to get width/height
w = $("#" + containerId).width() - margin[1] - margin[3]; // width
h = $("#" + containerId).height() - margin[0] - margin[2]; // height
// make sure to use offset() and not position() as we want it relative to the document, not its parent
hoverLineXOffset = margin[3]+$(container).offset().left;
hoverLineYOffset = margin[0]+$(container).offset().top;
}
/**
* Return the value from argsMap for key or throw error if no value found
*/
var getRequiredVar = function(argsMap, key, message) {
if(!argsMap[key]) {
if(!message) {
throw new Error(key + " is required")
} else {
throw new Error(message)
}
} else {
return argsMap[key]
}
}
/**
* Return the value from argsMap for key or defaultValue if no value found
*/
var getOptionalVar = function(argsMap, key, defaultValue) {
if(!argsMap[key]) {
return defaultValue
} else {
return argsMap[key]
}
}
var error = function(message) {
console.log("ERROR: " + message)
}
var debug = function(message) {
console.log("DEBUG: " + message)
}
/* round a number to X digits: num => the number to round, dec => the number of decimals */
/* private */ function roundNumber(num, dec) {
var result = Math.round(num*Math.pow(10,dec))/Math.pow(10,dec);
var resultAsString = result.toString();
if(dec > 0) {
if(resultAsString.indexOf('.') == -1) {
resultAsString = resultAsString + '.';
}
// make sure we have a decimal and pad with 0s to match the number we were asked for
var indexOfDecimal = resultAsString.indexOf('.');
while(resultAsString.length <= (indexOfDecimal+dec)) {
resultAsString = resultAsString + '0';
}
}
return resultAsString;
};
/* *************************************************************** */
/* execute init now that everything is defined */
/* *************************************************************** */
_init();
};
/*
* sample data to plot over time
*/
var data = {"start":1336594920000,"end":1336680960000,"step":120000,"names":["Stats_count2xx","Stats_count3xx","Stats_count4xx","Stats_count5xx"],"values":[[15820.0101840488, 15899.7253668067, 16047.4476816121, 16225.0631734631, 16321.0429563369, 16477.289219996, 16372.5034462091, 16420.2024254868, 16499.3156905815, 16422.1844610347, 16419.7447928312, 16602.0198900243, 16795.2846238759, 16708.9466016093, 16709.8158889291, 16796.7377507963, 16814.8517758747, 16944.4126048633, 16959.6935058422, 17249.8381137218, 17589.8424377422, 17531.9557988989, 17441.3403929293, 17316.4010302911, 17346.7236664321, 17430.6259138371, 17451.6104156993, 17583.58587248, 17613.4099078811, 17606.2248380331, 17585.3724344667, 17758.0871611239, 17838.6330766152, 17926.3287042096, 18061.8656942517, 18180.5441958674, 18266.052913289, 18199.5231629554, 18132.5794044834, 18141.4166460033, 18385.5401052213, 18449.9025074614, 18447.4506647813, 18482.1044829091, 18468.1994941392, 18519.2243177982, 18594.1226441619, 18630.7525298225, 18795.6021068975, 19046.8705959796, 19347.9530297036, 19377.7583961603, 19342.6089029458, 19136.7707279898, 19155.068178316, 19273.7388986087, 19456.5071519156, 19517.1777183944, 19647.581127671, 19548.8244351324, 19481.9477886176, 19538.5674838664, 19475.9133223104, 19882.9488574303, 20117.2818676399, 20160.5471055383, 20177.8829716204, 20021.5963993264, 19986.7238925349, 20081.494103713, 20153.8820301457, 20090.2815698175, 20347.6956210229, 20432.0559505945, 20424.1161313398, 20552.6149036244, 20401.7971566235, 20566.8264513574, 20775.0508913518, 20940.7424057749, 21439.2242959797, 21345.464856675, 21118.3658347535, 21037.2860196285, 21067.5253100188, 21154.2218964385, 20989.7128501239, 21138.0600264289, 21012.9565798469, 21157.7756723043, 21249.5570784826, 21319.2373460359, 21404.9235135248, 21580.3566445823, 21768.5431480858, 21950.7385701792, 21897.3405917937, 21818.9508751978, 21651.3323299552, 21686.9853043388, 21760.8918839863, 21858.4296394364, 21971.167058634, 22030.5293445048, 21861.9702222994, 21875.1332874723, 21948.3998912861, 22111.948785479, 22307.3585558342, 22692.7187186022, 23307.2513893047, 23423.8805303572, 23212.4216103056, 23238.8696427149, 23181.0371920121, 23193.0515463873, 23285.9241257163, 23275.3939293097, 23271.0871088623, 23129.5240882902, 23120.1068556278, 23229.6601916077, 23434.5382267342, 23485.8104706076, 23609.6959450202, 23804.9976645638, 23858.4516662707, 23744.0808142359, 23916.2167893042, 24148.3549591356, 24115.0352366057, 23949.3748940255, 23974.2751803021, 24145.7675582647, 24166.5636974786, 24148.5787052585, 24123.6233746251, 24337.6623798142, 24471.1541825222, 24846.4872913435, 25482.0199773185, 25675.6398419917, 25510.4564056644, 25411.2267110759, 25261.0073135841, 25301.1434977761, 25322.6369257256, 25232.6531923282, 25085.4909645092, 25244.830437283, 25181.2240054306, 25066.3031983445, 25090.8419105293, 25098.8487788195, 25255.2381159365, 25501.9078428897, 25612.3616706778, 25344.7934914239, 25376.9544940168, 25532.0984757033, 25673.6747603799, 25662.9153148183, 25456.3376698033, 25402.3336064905, 25363.2108652185, 25328.7273101396, 25219.9403901692, 25385.7529787453, 25352.8371058252, 25519.1636706939, 26270.9893668139, 26591.8050932774, 26493.5543171911, 26349.72124345, 26258.2841205458, 26136.3550394024, 26065.5729197509, 26147.5149947415, 26097.2901499403, 25767.6076328634, 25697.4426945047, 25587.1067445672, 25493.8927734193, 25396.7646613749, 25476.7504401207, 25584.3123369988, 25617.8860990079, 25446.6056808533, 25163.7663416429, 25045.1917357367, 25023.8472355981, 24964.3611953357, 25314.8126943617, 25151.5174664357, 24650.3184810382, 24404.687046978, 24369.2819234262, 24387.8468081395, 24423.4453260322, 24620.9386883983, 25378.2062459302, 25739.1371179967, 25446.5101654548, 25240.315103579, 24941.0976749202, 25016.8164322584, 24977.8134965294, 24670.9251981503, 24412.5246017431, 24186.2795508664, 24021.1212020799, 23923.3899570841, 23847.6257540143, 23669.3851906967, 23476.0400974209, 23333.6870052769, 23391.6202248235, 23160.8852743403, 23022.7107406385, 22710.759073303, 22625.5414323392, 22462.0701596631, 22399.3556465886, 22192.4958500694, 21924.0499518697, 21686.4948134259, 21009.9686972802, 20537.4119350977, 21450.9742406896, 21573.7761172922, 21827.8208461644, 21780.1682154418, 21634.2899558655, 21478.957394686, 21209.8026538992, 20875.5797814554, 20738.6399913502, 20465.8578200756, 20352.6790948811, 20128.957419549, 20063.8159761699, 19840.0084892574, 19763.5971075154, 19692.4601403978, 19497.5611002563, 19518.9185246791, 19462.484626295, 19299.5461830323, 19146.9111595586, 19060.4518314206, 18818.9593496115, 18506.4297721978, 18392.854423485, 18047.9060829786, 18021.1521862988, 17580.9640835308, 17069.6140124493, 16604.1685534979, 17063.3934254775, 17138.7253548185, 17357.7216636292, 17419.9325161178, 17172.0860972453, 16935.3523541474, 16790.8550814195, 16815.0737236178, 16721.4862458401, 16348.2178041446, 16086.7389076296, 15835.8695126713, 15709.6750849638, 15527.6470317214, 15492.8290612983, 15348.6513109241, 15172.6582708453, 15085.9406538944, 14929.7157915823, 14772.8383678848, 14609.1219600887, 14477.1356737357, 14411.4307066544, 14165.5237173371, 13803.3410529682, 13843.9663884317, 13663.5817296593, 13538.1739054421, 14062.2963996296, 13342.2106498274, 13249.5839729943, 13159.5897179248, 13240.8369180488, 13170.7647221301, 12900.0603194213, 12260.8632653026, 12530.2703371548, 12469.6397353676, 12334.6682932274, 12158.5428087262, 11912.8783397203, 11800.1345149188, 11609.7385162009, 11297.8300390438, 11415.252719941, 11313.7941056848, 11258.4046571493, 11175.3247282648, 11035.4286656547, 10966.3874316186, 10454.9208885721, 10562.297632475, 10538.2247059982, 10475.1398089124, 10404.5014231804, 10313.9823835279, 10135.4429334051, 9959.6437413725, 9857.72338165576, 9906.09248401907, 9711.42981752865, 9519.79813225009, 9697.20356744398, 9715.53159355971, 9552.38048671776, 9400.61471510077, 9325.82040677331, 9266.86203511198, 9166.44331834776, 9013.51948562947, 8971.42023163578, 8881.34744044969, 8637.90527596941, 8440.68040526367, 8578.82518790602, 8452.54845623202, 8411.59763289963, 8430.05069183353, 8316.65640732738, 8225.95338381445, 7842.94358618416, 7889.9962551255, 7888.36645707219, 7921.23764065062, 7861.63456874685, 7742.70580927082, 7693.05403819126, 7564.90102732863, 7499.38854010269, 7451.86660622516, 7214.90039567523, 7238.2711595717, 7485.38240202161, 7411.77084071513, 7352.59558324266, 7315.60699835775, 7228.01695595991, 7202.20029402037, 7116.4657870828, 7081.94067954451, 7014.7846279503, 6914.06835811903, 6726.03292057554, 6653.03092049874, 6621.39129339016, 6662.29191505853, 6656.46478630078, 6638.44895596061, 6578.65366153794, 6509.36162212383, 6436.00899658729, 6424.96869686787, 6388.50555509291, 6290.08844466873, 6259.85742377368, 6234.41808842776, 6011.9621878234, 6163.81998601278, 6119.21468332925, 6142.01274779788, 6125.58864523293, 6145.4217608467, 6216.13407332018, 6180.73381411946, 6129.05233390237, 6084.38883503978, 6048.19664148673, 6020.28339783233, 5831.31881718479, 5929.56929608561, 5888.3584697955, 5895.84974175094, 5829.6874631232, 5801.09708685975, 5774.42817742989, 5768.76209185514, 5755.60479706488, 5825.48793408754, 5774.46396985043, 5791.98150097203, 5759.20069227433, 5764.861981028, 5773.35447217717, 5693.63870605284, 5736.49017708281, 5730.73468483553, 5717.39412464424, 5709.46933881467, 5669.00929619975, 5724.81872307549, 5763.17161534376, 5873.94226430884, 6069.44720682584, 6033.0424459429, 6033.27974042495, 6040.97119873984, 6015.30763157916, 6083.55629124657, 6067.0255714614, 6166.71237091515, 6165.43640074816, 6161.17064195287, 6178.21343013752, 6160.50343506911, 6210.87838209952, 6201.21227762995, 6276.7481764166, 6435.02358660558, 6416.29652323725, 6411.4318110943, 6475.08008243712, 6564.55011688024, 6599.59922806665, 6600.19530272348, 6692.84504217159, 6653.95919543989, 6699.3176041198, 6721.34011362646, 6756.6550083673, 6816.91669538665, 6916.03095025055, 7061.70757763678, 7306.43568518486, 7295.21896680175, 7308.54257802798, 7337.99830329325, 7404.57801343458, 7483.83849194661, 7422.26481797287, 7525.76485654947, 7444.6709551673, 7463.33136071938, 7544.57445209844, 7519.57242835395, 7653.30711615792, 7633.70519530973, 7792.37287206626, 7848.12396982234, 7790.50230143194, 7819.56413797317, 7790.5149609893, 7814.91026908091, 7860.67359635183, 7885.92853203582, 7963.71693766284, 7933.21807106144, 7859.59538599298, 7963.66108001079, 7934.61174493327, 7947.86833825034, 7965.83307383418, 8139.42397848865, 8271.10382025013, 8217.63570213121, 8136.03244721085, 8159.73879317376, 8228.83751060172, 8136.56109051381, 8188.91325305579, 8293.29824416838, 8216.71925901295, 8188.23950467452, 8187.62301071626, 8173.13013624697, 8148.43293904041, 8191.69650900253, 8292.00631240498, 8325.95607105937, 8196.11498565071, 8134.27681048506, 8156.79898294794, 8132.43164831366, 8180.67490417091, 8231.18139238596, 8233.86833559433, 8182.06761851354, 8222.30242099209, 8230.41305056328, 8213.54738525887, 8319.86665007795, 8305.37026554814, 8446.97435527574, 8677.91915806234, 8604.46026321996, 8611.64106134412, 8531.21997780817, 8507.57892659313, 8641.85431149536, 8641.91934749958, 8725.92223757115, 8700.19212453347, 8758.28849017521, 8745.60579163138, 8778.87297988531, 8816.77441013827, 8911.67660625692, 8945.12784225865, 9069.01319022581, 9004.91516802334, 9026.17098619087, 9024.37227462223, 9054.58910537334, 9044.25683960873, 9066.1656468853, 9092.37183218393, 9111.52220219353, 9116.69898305474, 9202.91493012035, 9157.09971242374, 9172.24102837033, 9306.31345826189, 9335.21923479676, 9546.06404072832, 9557.99306551975, 9447.23094711361, 9436.17555674842, 9493.99638313776, 9491.42447216866, 9476.21591619196, 9513.73871257243, 9567.47256293599, 9511.64399803855, 9550.0673590846, 9603.05278031383, 9578.2047888808, 9665.43910209485, 9701.40652211287, 9745.91179865674, 9758.14589497082, 9821.33016587823, 9713.81211449266, 9678.49943477509, 9692.28562801975, 9665.48271140795, 9719.94097897836, 9696.7108567526, 9663.63367909621, 9690.73025980914, 9761.25390401674, 9807.53196560752, 9929.94495578682, 10053.0147388618, 10181.6021495362, 10179.8192172014, 10116.0686681145, 10027.7166529273, 10000.8581003101, 10046.1171720242, 10045.8630714378, 10073.4862606299, 9995.28802950606, 10104.2557562047, 10177.0406643142, 10127.4857820466, 10114.3552465463, 10184.9660676613, 10234.79888954, 10265.0442559925, 10239.4218975944, 10208.7615124848, 10171.5062608022, 10245.8260560629, 10196.518492162, 10246.5577477623, 10272.1660612081, 10299.8650323284, 10238.7190708738, 10272.5359341166, 10320.5181410794, 10373.7566187853, 10232.3463069939, 10679.7396034628, 10973.3397498035, 10914.4766335803, 10834.5392696539, 10784.429975534, 10830.1698541282, 10871.3871054867, 10896.6061110782, 10839.3655324167, 10879.4025374898, 10872.3934997027, 10867.4498985069, 10938.530231238, 11003.906787043, 11072.9036411838, 11108.7221393116, 11238.3344389481, 11106.9212524696, 11083.6914044644, 11169.8842697777, 11165.485337622, 11156.7206322643, 11195.9620961877, 11184.5268761971, 11199.3972138659, 11184.9014840992, 11175.8338167541, 11256.3904325293, 11278.3633183144, 11322.6075688869, 11496.6707873812, 11698.126099871, 11688.4289934511, 11633.756879227, 11655.515043646, 11657.4038594037, 11716.1508574315, 11745.8679959335, 11707.1953216919, 11601.1140473951, 11612.4245745052, 11661.9130550982, 11664.7131037632, 11578.2008509045, 11568.537469768, 11697.4314576822, 11842.3209293884, 11682.4874931984, 11822.434226969, 11804.9959989516, 11758.0051524706, 11820.0264110637, 11670.3487910917, 11726.3099732255, 11732.9616365212, 11742.9111353087, 11746.3044353076, 11827.6343746315, 11875.5692518763, 11872.8436931058, 12039.0708317277, 12240.6070968432, 12275.2174163512, 12206.2512987274, 12118.5054055728, 12177.6685530706, 12238.9862577882, 12183.2374514958, 12211.5405094269, 12152.5435877037, 12185.2696254524, 12162.1064925148, 12224.7714760525, 12234.3032926718, 12321.4638331116, 12393.4106913113, 12515.1873855379, 12443.5989229592, 12467.8831140464, 12475.972977096, 12536.015324256, 12684.8624444048, 12641.3888189578, 12657.9116380533, 12773.8884055854, 12912.3705135144, 12851.9657415346, 12923.1541849091, 13036.8058203422, 13173.6669322284, 13230.4902057188, 13671.187570497, 13739.0163192321, 13727.3220076901, 13654.7971998567, 13666.4135152319, 13725.3611961455, 13693.0575509512, 13825.2946450688, 13835.7221165874, 13813.2660449661, 13842.5431730383, 13893.7922775107, 14135.2989022499, 14119.3773175833, 14209.3151203062, 14378.0121545595, 14284.0201251531, 14290.2099309504, 14345.0614009986, 14391.6868428215, 14510.7297872662, 14491.7013144698, 14583.9695120333, 14735.6580638827, 14693.8317193741, 14806.5665084588, 14748.3914783763, 14914.0356578109, 15006.7008556155, 15159.2655588919, 15456.2077473699, 15486.5589740315, 15420.5091363059, 15455.6627877337, 15474.6685551093, 15535.7078312375, 15578.6108985781, 15669.5817934132],[417.896653157652, 421.939526610002, 424.182351484078, 428.010724137391, 430.954421741925, 435.155825361582, 433.273171079374, 430.209062841366, 432.591549079551, 430.765991741519, 430.448242685685, 432.861993829354, 438.812055560809, 435.92254751833, 436.438030566965, 436.919532080568, 441.39033586533, 441.002053743608, 439.379156019208, 455.71007306555, 463.463943613933, 457.084357117552, 452.947206570355, 449.709449903717, 454.743774398171, 454.808752167753, 456.236640675908, 459.813163888814, 461.030643541157, 455.363979690805, 458.040882818146, 463.747657693129, 462.919526488447, 463.906420064534, 466.677529934491, 473.79193687102, 475.067981863178, 473.593408263573, 471.805991619663, 476.959998185822, 475.268421490975, 480.484043394977, 482.774721025233, 484.114188027164, 481.044847799643, 481.448299110608, 484.414382891513, 480.285473279238, 490.34823466621, 495.152084306067, 507.867809927969, 501.748789829794, 497.713741732373, 495.536413304932, 499.779942833792, 501.619113379403, 505.20914493845, 505.727048627159, 508.705516498269, 501.62416226126, 500.110907325404, 500.843395600351, 497.283343755822, 508.348013476184, 509.244221154599, 511.642704727323, 513.26342192596, 511.187000234841, 510.072702450494, 512.447563705355, 512.349258798763, 514.354579953437, 522.207148434834, 519.321121791517, 518.839529934275, 519.884548442384, 518.21836546243, 525.440763978751, 529.389282537031, 531.684470058085, 546.315026135052, 538.739564334365, 538.154526834604, 535.269819815021, 539.633241896162, 541.515559038697, 539.142041321797, 537.489548964395, 534.894998804853, 541.821136537358, 539.279876568243, 532.682191845622, 535.704572111483, 543.022244380526, 548.332129069579, 554.716060876508, 555.19684308005, 544.998412520622, 540.460734487996, 542.443390786104, 543.938492602332, 553.57068268227, 555.691657756405, 549.665362914334, 549.791749356403, 551.053982426849, 538.954824844933, 547.724867314801, 565.0980888706, 578.322625492672, 595.01829461484, 594.444487972585, 585.475637874194, 582.189545510688, 583.966792395165, 589.558907040535, 593.228823980374, 590.796372362968, 585.660838466137, 579.613717832427, 575.028545699441, 578.63333781436, 586.508509167001, 588.428855928167, 592.409775663321, 602.630814006581, 597.351254472048, 599.350348479695, 597.747802933104, 597.918169262604, 597.345400315989, 596.137244138892, 595.362014406938, 598.91824734659, 605.761737466471, 605.401953973058, 611.602045549785, 596.913555491227, 622.218968449702, 635.772502211104, 659.890267247318, 655.690356899494, 643.81350317147, 639.622662934792, 635.181398389345, 635.931861463261, 637.176018625806, 633.029283735613, 631.500373395668, 633.663871035543, 628.052224352674, 623.478007754914, 624.60010433486, 634.232288703686, 633.213265113651, 643.780990192965, 645.623394201921, 638.376679983418, 641.175819967212, 648.134452966916, 654.743672067733, 649.797827174356, 649.916006130494, 645.722756831619, 645.095912843758, 644.986747727879, 642.896246457364, 646.278582060434, 644.450816910178, 652.660709138635, 678.978816491674, 686.17377420837, 678.698046947575, 673.861985181877, 675.300461027151, 668.905375480507, 670.19555920919, 672.272005062817, 676.058023582845, 669.331721074294, 663.284260419022, 658.590962802747, 655.429670561712, 653.952977270816, 648.512899415908, 658.070950016862, 656.667459757417, 650.278612998846, 642.803386032555, 644.504562122976, 640.406588354718, 635.188727379749, 640.673381333944, 643.986005719374, 634.707448544848, 626.03911017735, 622.102670838679, 624.754503368222, 628.92330154394, 639.235778309533, 661.411389358975, 663.600225756025, 652.98187294648, 649.425336707865, 640.911072628596, 644.544501767659, 644.485362827545, 634.115243276707, 621.621507020589, 615.424451106147, 611.265692097548, 614.674402765371, 610.870137689054, 610.442192841681, 608.122986387184, 609.631105630981, 608.310677322541, 602.536163813803, 596.589409204398, 587.895792087477, 587.156029083567, 585.346680274341, 582.392271327794, 576.477593737616, 570.943436794218, 564.817937470409, 565.566547753383, 569.714471757767, 562.045977644868, 574.001446068153, 591.170397164235, 585.147246622572, 574.377053084713, 569.473554927773, 563.819611627521, 557.738450292467, 550.558124566308, 543.95249606324, 538.383193950184, 535.518460191947, 534.009542617799, 527.507081571095, 525.855818936404, 515.956765375317, 514.948755898741, 518.928189617426, 519.946202561792, 517.22969713167, 514.624030500916, 514.450245504861, 502.833852224001, 501.733790420308, 500.905343422962, 491.452700818365, 493.160481567375, 469.743401803475, 466.683819300785, 474.257789661729, 479.893845873501, 476.235605739805, 490.669910680023, 487.094772195582, 476.757380730878, 471.000824787846, 466.007027523328, 460.980305579693, 462.708103494415, 456.157316565676, 450.266791673963, 446.723012001431, 446.77050357947, 444.318627189423, 447.828678540986, 439.255185243151, 434.6798118143, 430.389431853554, 430.901327795982, 426.925522321766, 419.461987707464, 414.357650899457, 405.320864999822, 399.249835429345, 388.072378672975, 398.645554599721, 394.840139247208, 387.690122291633, 403.53443982937, 405.40442636148, 401.87049888819, 402.484586693824, 406.365860233637, 399.312203651765, 389.864265290082, 369.755960598218, 377.377285639826, 374.422134399153, 368.930497095881, 364.553646668297, 357.354928824187, 353.870463867288, 350.731593414858, 333.353935305727, 347.556152107053, 345.918852422907, 344.149590814626, 342.834778600676, 339.109961997237, 339.578200394015, 331.002925701714, 329.149877912037, 327.99366987618, 328.255414400322, 325.917449343282, 320.331925660669, 316.82794804017, 314.634729599431, 313.296376503199, 316.690022996553, 310.842062671798, 305.15448535675, 312.258909370797, 311.138461403303, 309.716334792024, 306.73416419366, 302.763540785339, 302.688011777715, 297.855200192353, 294.052152027449, 291.886775176745, 290.884990988734, 284.373554262901, 277.417488806928, 282.42976036264, 282.860411110281, 284.542997063741, 283.459521255207, 276.64292463277, 276.311067178907, 263.450317508319, 268.996111742332, 270.285060210108, 268.897079969822, 268.037600632729, 261.59430525914, 261.507829693609, 259.926415726835, 260.235509088198, 258.770801678023, 249.471210990052, 251.220981641975, 262.407302554646, 257.080418210065, 252.205876245362, 252.452162352191, 252.150038944078, 254.807129611095, 252.642365503301, 250.401028846014, 249.250651179802, 246.865328645916, 237.926233203099, 238.686475962834, 238.91884286276, 241.702820747253, 242.821686774038, 243.487257138651, 238.842003619971, 235.320846824331, 233.903760486764, 232.164318861336, 231.639932898906, 229.819236354946, 227.208242657444, 225.557039982655, 216.551528462898, 225.794616577135, 223.064778412131, 224.656540058904, 223.193470291976, 224.681158473113, 227.29598072403, 222.074821764545, 220.065407115879, 217.279154733635, 218.31464361396, 219.221257040992, 202.862339813822, 216.26637816344, 215.859339129687, 217.113731315167, 216.287889931048, 214.424644431988, 213.408980681774, 213.169027489645, 213.055780567502, 215.689849385557, 209.798252360761, 211.437980363251, 210.664934006509, 213.001398776548, 213.45268602509, 211.4684378063, 212.062859487271, 211.010646854662, 210.983281710135, 210.170119875808, 206.966414645286, 204.441662494224, 204.830757809215, 212.03017759837, 222.248125054601, 213.165975680006, 212.144278745578, 212.018484925496, 211.809552271421, 214.254595786471, 211.635586385428, 216.269691038343, 215.566025711025, 217.372433978759, 216.624402249839, 214.313303955595, 216.446786446292, 216.414008142746, 221.327779614084, 227.293746223446, 221.196326490423, 221.845170552435, 223.549433001874, 224.480295089392, 225.327205712307, 223.513235337365, 229.674407465528, 224.893385569578, 226.613397356111, 226.79819276229, 227.063954237474, 230.43769614539, 229.873195945009, 237.763744622536, 248.526733262175, 238.759184162207, 234.739496678125, 234.856267882392, 237.735121763476, 242.291969344929, 236.513332905027, 241.689170223761, 237.359605915126, 237.307918803237, 240.278063427439, 238.627486203554, 244.536455127804, 242.153300966415, 247.199975857985, 248.994340480206, 243.087860328681, 244.805572668378, 243.146778583601, 244.974734542258, 243.402651608419, 243.986690964703, 247.287324917495, 243.930651204356, 242.876400913575, 242.858729898422, 242.20329355847, 242.289739453934, 244.355139573454, 251.799900761617, 257.326711652043, 248.884173523008, 246.319590940855, 243.340639477124, 248.716160561694, 244.9358093909, 245.280056089005, 249.078307924094, 243.942794637274, 244.987290421346, 247.525859153585, 247.223764023216, 245.765485909428, 245.336016427828, 250.575826186572, 252.705909139878, 249.000250873976, 243.952925853124, 242.878201085381, 242.858030592417, 244.168504737503, 244.494020035285, 246.408324892354, 241.165995605476, 241.568382901734, 244.64750499489, 243.819884884581, 244.065894537057, 240.76247990709, 253.068453269524, 261.114971495371, 254.972296755507, 252.616886401716, 249.201795269459, 250.280853298693, 255.64594254278, 255.367989961601, 256.143719393746, 253.184573069496, 256.011917013549, 256.133853708926, 257.673056541842, 256.27777878315, 258.570738595544, 259.744045420529, 266.498261422868, 260.42766853431, 261.008761898516, 256.696572351582, 259.404066585352, 260.046310212919, 258.325150270904, 260.483414967525, 255.375475110453, 257.849225091888, 259.303561072975, 259.332310908615, 259.445725199389, 261.272592196972, 268.411594281805, 272.980642330116, 270.225360784368, 266.71744019311, 265.77183281934, 270.127252675469, 268.557458719194, 270.944030664588, 270.714691848188, 272.068966258483, 270.080024804363, 269.909059665163, 268.389299559823, 268.330233578691, 269.317902161563, 271.861382148962, 276.029383661139, 272.641736614304, 274.192337708943, 268.381482407536, 272.852439357259, 270.96613367647, 270.376186032202, 273.979476425802, 270.29538683125, 270.612082965166, 272.469562229519, 275.614992805863, 272.291738700014, 279.157760939839, 282.249855181079, 284.96451991249, 281.555340000851, 279.281843272282, 280.317427112642, 278.813195704218, 283.684029370464, 279.516372363228, 281.773157332368, 278.983712291633, 276.792460520374, 279.733800244049, 280.207283440365, 280.412020302784, 280.981230685765, 287.146148183357, 287.227764169373, 284.697959425298, 286.488552985262, 285.864949018685, 286.202819748388, 284.628910203891, 286.214735387324, 286.237443970225, 284.583532987681, 284.128643959009, 284.855010458799, 288.114161679616, 290.81087706117, 292.23336258955, 300.960027519598, 306.544290484747, 304.169720638703, 300.612476970424, 303.159857597497, 298.405547709346, 300.935344937154, 299.816268685432, 301.678388561829, 302.998716519342, 304.419986632806, 303.788135314585, 306.828595905151, 307.987137572742, 307.172448353568, 310.794982059298, 314.393071213892, 311.759560686089, 308.639580527543, 306.376137564242, 306.169789779256, 307.009375366454, 306.851410820609, 311.355810164906, 307.194211179998, 306.690701436426, 305.563743196781, 306.42305489021, 312.620714882764, 311.614761874132, 315.192671320326, 324.892245575155, 320.699020461892, 317.538652706244, 317.83559513674, 319.330031584887, 320.241310741065, 321.351248457961, 321.12493601455, 317.714925700924, 320.616734452721, 321.424152719548, 317.943438577956, 315.994418591242, 316.12254897162, 319.927682859169, 323.991403301118, 321.868983966387, 322.796396433831, 321.211482248061, 317.48532482324, 323.250132142807, 318.273281599321, 321.638067108341, 321.631166038591, 318.617732180177, 317.842240414491, 318.470614736886, 320.896793527721, 324.827898003061, 329.259909375579, 335.88430813325, 333.662514618628, 330.601280927277, 327.066941489344, 331.084194269467, 330.367989905781, 327.413694270244, 325.735848048666, 327.405942251541, 329.32941784747, 329.471631755201, 331.924768049361, 333.963000964858, 337.574284815578, 338.346156341195, 339.924691811256, 337.024616750442, 338.147145520058, 342.561080120943, 342.893333476003, 345.148299972999, 345.137126382678, 347.188720595187, 352.692951877898, 353.379545192515, 350.690088371374, 347.996164640976, 349.403815496585, 352.367357372738, 360.373665877019, 372.229939382202, 369.027304752567, 367.396947932756, 364.214682803042, 364.915382710192, 370.995303817464, 368.850503169007, 368.766952933689, 369.614123451277, 373.072464906063, 373.294053546558, 374.481276248065, 378.278383597706, 378.116847488255, 379.756834260378, 384.609746202786, 381.34534315145, 379.010534315401, 378.011454931315, 383.233445518287, 383.386733216836, 383.368598820589, 386.913003921952, 387.275778888019, 385.605444605594, 388.391090686531, 390.889253112929, 393.023587084953, 396.766673662647, 404.250169385693, 411.346089099044, 407.528501370045, 405.556581170845, 405.353478877966, 407.306171829272, 410.608032590171, 411.468698240786, 417.71555038532],[22.7139048615593, 22.1835797158183, 21.398454467832, 21.5057455371495, 21.8859268838019, 21.577669136753, 21.6352715280214, 22.4262783251323, 22.0333682049363, 22.6326473966623, 23.6930382776561, 22.1170368771612, 21.3631656852941, 22.860482082986, 23.6731905404224, 23.8868259284315, 22.0255659515288, 23.0939439586438, 24.2480258350701, 26.135530217946, 28.1263042503254, 24.041239982156, 23.2594693195129, 23.1669269629776, 26.0336835858489, 23.3076792298819, 23.3388811442721, 24.7677517723648, 25.5677654475073, 23.5376320890123, 23.9759192642085, 23.6186993682972, 24.0733687550228, 23.789611225804, 25.3474227511129, 24.0144499331934, 23.9345637831779, 24.2845842319815, 25.2790966278147, 61.1051725752672, 30.212878805673, 25.3977526358445, 26.310232880516, 26.2298519319925, 24.0571841758467, 24.7331824411411, 26.5068775932588, 24.7039425085353, 25.4827426197246, 28.3381210530702, 30.8284451448391, 26.3611075439416, 25.9511547226755, 28.0365716116292, 26.8851083059821, 26.9852961208882, 27.247172804663, 31.3411831728002, 28.5544343587452, 27.6483462482868, 26.5415934966833, 26.9430264240002, 29.3141140772715, 30.4086727249667, 28.3264607455818, 27.9338963277662, 26.5585566078957, 28.1491312090324, 27.3782694261932, 27.5603894055974, 27.7826852702392, 27.5272783479336, 29.9117018865449, 28.1140804687536, 28.5897386634783, 29.409022877307, 28.5600827910056, 28.8081083065128, 27.4078270687279, 31.2052771249094, 37.4311128602194, 33.3204621321502, 33.473089359258, 31.7577834881452, 29.2659395590259, 30.8090901615417, 29.5224003504259, 30.1560884667179, 30.4411691937187, 31.3751193606822, 31.5879224471118, 31.9433455770292, 30.695365106826, 31.5871922944499, 30.7252287680098, 30.970861775215, 31.3268913645699, 31.6555661543936, 31.8528944348593, 30.0231584250416, 30.6151955473931, 30.3909443594145, 32.0541117251103, 32.1178594477766, 30.3865037188688, 30.0538601063227, 29.5317125184459, 32.1491202044894, 33.1082219214234, 35.213148622664, 42.4966676633059, 35.1953904870379, 35.1131146561431, 35.0492796197069, 33.7872021815136, 35.1540880105048, 35.6047107265456, 35.5409569322476, 33.9241958495735, 34.8242546710379, 35.4761517044561, 34.5197888354682, 33.6328616604436, 35.0820241208954, 55.6711301016908, 41.5241245631833, 34.2608969750899, 37.106868514809, 37.2538739931989, 35.0246988253797, 34.8797725349217, 33.9900066112945, 33.0547840064666, 34.8747182274462, 34.1615505410471, 34.5601710148433, 35.0411584328344, 35.0658640350031, 36.1933154615015, 39.1729044463257, 44.3734626909983, 38.1346019387893, 36.8370849428568, 37.0703115177443, 36.0885654928149, 37.1246746749949, 37.9774239349188, 37.8857800901269, 36.8212102291381, 34.7943164552613, 33.9715026536776, 34.8978711881783, 33.6580717094486, 34.9082552143899, 34.7683583586218, 34.8712279432635, 36.929392177488, 36.1709109546353, 34.9832139653761, 36.2829520683472, 35.0921597663994, 33.8060954439387, 34.0270091355783, 35.9974107677845, 34.7079898093699, 33.8355495424519, 35.5301135073587, 35.5841373671989, 35.6422533267596, 34.9470074555956, 45.6963310683929, 36.7461504511855, 37.7241033824506, 36.797428973985, 36.0374440639296, 36.0581811421496, 36.4223498976347, 36.2247504340624, 36.8389207282881, 36.4152635075966, 35.6842787599006, 35.4340016980722, 36.7236072854739, 40.177454566621, 39.3015656050232, 37.0354058270684, 34.5727394474706, 35.4118599510259, 33.2216399518315, 35.482761370735, 32.4071617819498, 32.7570348047002, 31.1610609496332, 31.1706877576788, 32.3905358721113, 32.6610656835452, 30.4434145678946, 33.0034543107787, 31.5071016900255, 33.2342450977346, 42.3835839704103, 36.9854633025348, 34.0797106821697, 32.7790295002199, 31.8468756494856, 32.1156751923622, 32.4158457275916, 34.248002142678, 31.4654036600638, 30.4948833812918, 31.634054851485, 31.0162932088973, 30.918623281567, 31.4251900988893, 30.1566915481872, 29.8719460451793, 30.7268140776264, 29.1349327203131, 27.7866270108596, 28.8375410040863, 28.4031139619687, 27.3072708161624, 28.7046959994069, 25.7856866174376, 25.9854951838988, 27.1884360760177, 25.0191570512959, 26.1444306661218, 26.4465327311417, 27.6655756860951, 35.3464081776898, 26.4055615461178, 26.494774793367, 25.9485481811513, 25.5121470971429, 25.0880243246202, 24.9743847347549, 25.71508139457, 24.3631485131882, 23.6574567958729, 24.169621696681, 21.6905875188717, 23.6287967205566, 24.9265787613068, 23.4941429142724, 22.7782081625482, 21.5195851514574, 23.4541956919286, 23.3557345136374, 22.8650983770136, 22.3667078044408, 20.7634338757071, 21.4964361404676, 20.5090304816729, 19.8437888585569, 19.3919841746043, 19.5269138231221, 19.8081973066273, 21.2229954431737, 21.7380422865031, 27.5081722745699, 20.4679682307232, 20.730400789349, 18.1770864055104, 19.3940190651138, 18.7831367076949, 18.7185201270527, 19.9887531092108, 18.301594487489, 17.2477615691694, 16.7179263998417, 17.1951918183636, 16.0261954626759, 16.6046548382853, 15.6057857665212, 16.5707303698615, 16.0261799100271, 14.9876997664478, 15.5245380557215, 15.1764224980745, 15.3302861803383, 15.601469178172, 15.2381340672048, 14.9119056996342, 14.5790765428273, 13.7462180734522, 14.8472809534186, 14.8722752019515, 15.1458216013406, 15.203184148777, 19.8790778146853, 15.0367148092305, 13.9658263048447, 13.4289420388527, 13.1473735101629, 12.5615034549669, 12.3482678125865, 13.5035078919905, 12.2315844400238, 11.5964802094642, 11.5433191356293, 10.9304899713981, 11.8229104675762, 11.2854172236975, 12.2814905735375, 11.5410086852498, 11.1097688331475, 11.2365949893026, 10.9156720800477, 11.4759614372779, 11.7148918081981, 10.8712635650855, 10.6149125719955, 10.7233160397398, 10.4755012491656, 10.1792740341452, 9.74387150178194, 9.73893245756734, 9.63350830014551, 10.2858904619879, 11.5696304599009, 10.6587583683357, 10.3859412979669, 9.71126503586956, 10.0794654920601, 10.0073178607839, 8.79416298890572, 9.81966964956207, 9.08674183581313, 9.76003153038283, 8.32099333090908, 7.9831706609758, 8.58834282753532, 8.46219136472267, 8.28704004381968, 8.59370561650961, 8.03333073486709, 7.45990144379175, 8.03353878201558, 8.74963857468114, 8.28570159803657, 8.33584096321878, 8.68006389539643, 9.10234546737519, 8.77283341053731, 8.5148515867175, 7.66212839733662, 7.71078467771864, 8.1298559600758, 8.84550511114518, 10.8325076362719, 8.96617376521507, 8.43482406702756, 8.30046169661281, 7.36345306452613, 6.96272856283801, 6.92398896336959, 8.34558058495531, 7.35599329587111, 7.27240829368549, 6.53319342786816, 6.73188165782901, 6.14681999924699, 6.84624623376926, 6.71851858841208, 7.44185827446734, 7.32072959154604, 6.04590131506118, 6.59745477635748, 6.97764018249236, 6.87366586129257, 6.05550783883942, 9.30363764273429, 7.74346354514129, 5.70531867575187, 5.970239054372, 6.10977730228083, 6.40218712700333, 7.27032206479321, 7.70338055626264, 8.60531620706121, 6.6393935663833, 5.82569261177328, 5.32586109594956, 6.19679557738446, 6.06647686916667, 5.64860095784259, 7.69181436091584, 5.95259447543101, 5.4486427299529, 5.9794613884888, 5.00267766080813, 5.43673440231017, 5.66733599046908, 5.69224428131187, 5.96379547972323, 5.90677522531917, 6.99093337391655, 6.15061379984839, 4.93983622841414, 5.48400617153349, 5.98760687281515, 6.3313463986966, 6.40796284858688, 5.66577903130989, 5.88731567137874, 5.70829105864989, 5.29627958243853, 5.59440322644478, 6.96406591252026, 8.31062739882078, 6.55474097043517, 6.49650603942589, 5.69942975349686, 6.26141525353314, 6.90050872281977, 5.84025984971949, 7.90850994999487, 6.49034703122675, 6.0051778000861, 6.40417828911173, 7.01817576754371, 6.67761226100902, 6.83851137598591, 7.38103161698937, 7.36343441931731, 7.62710356085051, 7.51092551759645, 7.04256457326224, 7.13653436417809, 7.7214880541234, 7.24088161130583, 7.54226859459334, 7.58722190947148, 8.08276579856651, 7.56546964375802, 8.06105843168397, 7.55301518261221, 7.82741385466865, 8.84754363570095, 11.0080873784381, 8.83283255077616, 7.79127968096746, 8.26578489086533, 8.70336306969532, 9.24507326634756, 8.82055865081867, 11.0419357711197, 10.0517873482497, 8.96221117540912, 9.197833412779, 8.75865471681808, 9.56864737645389, 9.10493487778466, 9.51583206069623, 9.92111398520095, 9.99866229300907, 10.1532749326282, 9.05446906747092, 10.3454028351448, 11.2539164857841, 9.71779272051998, 10.3828450443177, 10.1698279454484, 10.444343549999, 10.0107392348361, 10.0871641816135, 10.0141101890093, 10.4995016517483, 11.8114348034855, 14.1411486295131, 11.1497800473287, 9.93910557965551, 10.7016222340763, 10.3620620677702, 10.0614298103306, 9.56753672583054, 11.286713065511, 10.809302596379, 10.2790374773836, 9.75423290693143, 9.7127656000958, 9.86869639542378, 10.5080289236009, 10.7700610191882, 11.051857268484, 9.9366684999102, 10.4511850460691, 10.2425908396077, 9.58426724741114, 10.4007485762895, 10.0708428788889, 9.86989503529126, 10.5667168785092, 10.1231124250185, 9.9509309068325, 10.1781317925233, 10.5926785586042, 12.0755500491396, 11.3510866746285, 12.5952885308576, 12.3278566297216, 11.8419002914055, 11.7909745736979, 10.8176787846651, 11.0503094759748, 12.0871924651884, 12.2892831329067, 11.8022262602463, 12.0257718306487, 11.3059663681482, 12.0581590363408, 11.216759719136, 12.193102356791, 11.7464602622796, 12.1329473403724, 11.6436435237705, 10.9924086852494, 11.7260188185652, 11.9780890845513, 12.5640893433927, 13.081176927434, 12.9700274195121, 13.1806865072615, 12.1535155770971, 13.0680393821546, 12.7360812858513, 11.9505356444506, 11.5980709167047, 14.1355140344329, 15.7331594604175, 14.3880866724832, 12.4451064229031, 12.9523604736017, 12.1489034598273, 12.8340205435816, 13.7123377278864, 12.4169776306811, 13.3622707321217, 13.751275675944, 13.580560045799, 14.5237347342343, 13.84087516611, 13.7895809290771, 14.0009913006201, 14.1232206143211, 14.2509923403862, 14.6735482485134, 14.6038644032132, 14.2591768348281, 13.8125684785892, 13.2455405473899, 13.133657893492, 13.5171506367111, 12.7483049389852, 13.7296229022426, 13.3465985845591, 13.1722812695675, 14.2177583582663, 14.5885660280596, 15.2046334557919, 14.8687887339893, 14.3459501635749, 14.4316041842002, 14.2378990416765, 14.6351277818234, 14.6669007254223, 14.1462800643496, 13.2175419170909, 15.0384746745156, 14.6896054393555, 14.0727006349137, 13.9313227859325, 13.3076457805515, 14.4221317469856, 13.772059414646, 13.6519138667643, 14.2060297370715, 14.3750877510171, 14.2125352292124, 15.5897247776351, 13.6458287779134, 13.4373921690398, 13.948400717379, 13.7553883127199, 14.2765267563396, 14.1938557052254, 15.1853111756452, 14.7066555790146, 16.2396967542325, 18.0700873272928, 14.5026995913549, 14.1154322903435, 14.1772300343777, 15.3454105691695, 14.9056632812574, 15.4907925363525, 14.9305564366964, 15.1409572042096, 14.8161952358938, 15.2384459597034, 16.7699158684884, 18.1805371260253, 16.6347888136311, 16.579227418191, 17.2194324830177, 15.7352429066967, 15.4157569087143, 15.4042937838442, 14.7473668944001, 15.6901884268396, 15.2748801196939, 14.9684331926984, 15.2093336704195, 15.8902960052904, 14.6198413259409, 15.4592784919166, 15.0479378900611, 14.7841778141295, 16.7691616417058, 17.9164662182839, 15.8221578923089, 15.8960328185387, 15.1970643516187, 15.0027448557532, 15.8112458850347, 16.2422133155578, 16.0378846158357, 15.6294595622881, 15.9840628836787, 14.7546780078839, 16.1474324367158, 16.148655165209, 15.6523805611154, 16.0496893310395, 16.8985593327365, 15.4535032642915, 15.8538492257548, 16.1517334815507, 16.3343355850797, 17.2968043198046, 16.9943273259634, 16.658421844334, 16.8810568401919, 16.0690820892001, 15.6642813471789, 17.4751238831762, 16.6879307315727, 17.3506953672174, 17.6756717742325, 19.4497864770733, 16.4928252316959, 17.1335191619456, 16.5618871512082, 16.0928550103379, 15.972171800139, 16.8526482539702, 16.0226758588423, 16.7998367577753, 17.5285337370328, 16.1062100256727, 16.5432929895327, 17.4998900836879, 17.4042941428377, 17.336936648218, 17.8366103308107, 16.6241004724155, 17.7667580175988, 17.5087523510664, 17.9489856583791, 17.3227446345981, 17.1998620212445, 16.55342937171, 17.303545432554, 18.3098200016186, 18.5428979075433, 18.2698626003572, 17.9666765205678, 17.1742004848881, 18.6397656387099, 22.7904425324245, 20.5871420669428, 19.4217886532154, 18.4783865455099, 18.7665216206943, 18.7455167816932, 19.7773526859082, 19.7453991552185, 18.8811126413612, 18.906580594858, 18.7557595465075, 19.265808724767, 19.4883049693211, 18.3929086675299, 17.9974832713362, 18.7202053413453, 19.7259855588387, 19.8567048612003, 20.058695362358, 20.4369625781949, 21.7106526722873, 21.2460470237909, 19.6553365196066, 20.8620197251006, 19.6970337872282, 19.844992494008, 19.5636265179336, 21.6818834550339, 19.9739464718905, 22.5521569684899, 24.706308329735, 22.5628960301204, 22.4896658413027, 23.2145430335079, 23.2892186245651, 21.9927637150795, 21.2959159180347, 21.8392545012819],[42.6722032588484, 29.7597119861394, 26.753479497815, 21.3779943963802, 23.5180320870782, 20.4160273650264, 21.6592988304624, 22.5336443432532, 20.2671612593362, 23.5399141967304, 20.4430592024246, 19.2643064730577, 23.09595182256, 25.0267598040285, 31.6968549111032, 24.0908238250784, 25.5393898522129, 31.7075728632433, 41.1836451973671, 48.5702805231008, 28.7584338151552, 26.2598484085803, 23.8576868928514, 23.5098916318726, 30.5431079153685, 23.8327393934619, 24.4849762714983, 31.9761045957918, 41.809628957709, 45.7139166884661, 36.650969137303, 32.573698239583, 29.4001051133529, 24.7729839121875, 27.5467976010315, 28.6982412753956, 22.6488598971878, 23.0151192801781, 24.1402524928187, 29.6915742340768, 27.2503487867359, 26.9055104161108, 28.1790211354422, 24.6566895950566, 27.1174221286017, 24.4891209550294, 25.4508989599267, 25.9910156417965, 24.5252757398669, 26.3883890558586, 27.5209269131146, 25.6793595493624, 28.5636083584857, 28.1777892613039, 36.4504317162694, 26.0413673673992, 27.5107841923445, 26.0485392568327, 27.5672728936274, 30.6481058316777, 27.7129794591456, 27.5524240647644, 28.6996811429015, 69.6841885875692, 60.0312610766115, 34.6950166928944, 24.3817049594558, 26.8989264350325, 31.5911667832899, 28.3956626162963, 31.9781210051286, 34.4796122572046, 35.3073824661517, 27.7205743970567, 28.8442545324934, 32.9677266813713, 29.4684824027718, 31.6114226898927, 27.7747261552007, 27.7511766241169, 30.0627894436663, 32.0665262526539, 34.717718029005, 39.6470247468769, 37.7938801086447, 31.7684482022953, 29.7197604317071, 31.3483181311461, 30.8002692909768, 49.1841569973485, 65.5470086077452, 61.2026024909806, 46.0151776794476, 36.2390240444611, 37.5471749902809, 36.8145092528944, 36.9658638178577, 36.6101680633515, 36.4314980883674, 40.2104928486047, 36.6411820636045, 34.1808309048942, 35.4013537869645, 35.4239116611291, 32.2311433151589, 33.3334133366176, 30.1222711270941, 30.2903696039771, 28.9359411393613, 38.9686537654724, 39.6616240734768, 34.7037978125189, 35.920073368717, 36.1972578462478, 36.6044994685474, 45.5837915345655, 46.0637409675962, 40.8031204320251, 29.3729644691184, 34.4816962427797, 49.9232731242331, 88.3322853841411, 54.4585394575022, 36.9931786199971, 44.779028094342, 42.8021023985602, 45.6746063332343, 80.2364226503937, 103.275692110929, 65.5336751794182, 48.3332609074906, 35.6507950172304, 43.0060395728019, 43.150165435146, 42.1619845639232, 42.8137586420117, 39.4753917740048, 42.7583999588166, 42.4042397925763, 50.6840394959759, 43.6695104476403, 42.5894016784898, 46.4926034531527, 41.3681806189315, 45.0969747419678, 49.0766005124189, 51.9433836429035, 44.9557171636918, 40.7932610940616, 47.3459506995739, 42.9444407493012, 38.3332018075951, 37.0403945315726, 40.6236040452821, 41.3547600344358, 42.2536136515104, 40.675397219414, 42.4100523617115, 40.2111383657933, 46.5807101750804, 52.6134246850967, 36.2760418617763, 36.2908452584699, 37.5262029700784, 42.2688147699872, 40.8319481930759, 38.8900154243966, 46.4216544933736, 37.8869625694716, 39.5296462087132, 41.3176631640185, 38.9990372634359, 50.7881191071869, 45.8590803647103, 45.4726563158543, 47.3863223833536, 41.7665304237039, 43.0228152942796, 40.6620142540596, 45.6246870462051, 45.7918960862212, 38.6188252524842, 47.4891101400162, 67.5427967663027, 97.0639166970753, 59.6490633882027, 43.5610384540415, 42.1943071310281, 39.1515693007808, 43.7155024377105, 40.4757706841108, 43.7132115917579, 42.0678354053799, 39.741135539915, 41.1038004395721, 38.3792119570558, 41.807924059757, 43.6066321456653, 36.6068245261073, 42.8774756240858, 50.2942853451411, 48.7197683085663, 43.7210879520051, 37.1684282449174, 45.1412573130098, 44.8268751232585, 43.0956720763188, 41.6549038685349, 32.6166662971834, 43.3631676752749, 44.7631633545878, 46.1853189922537, 47.3323727401444, 47.089104742627, 43.2779334860173, 40.6849360726571, 43.6115899914027, 39.5669189529535, 36.8914517938024, 41.1125165270758, 42.0340214413129, 36.8233674735164, 39.3719926686623, 32.6722895944411, 33.5317260953113, 34.9918513475705, 37.5447493458398, 32.9183993417099, 33.9251397293651, 43.2229167685256, 35.6385466213975, 36.5902721740627, 37.4551823081983, 38.4888776878885, 35.8340721176935, 33.3403586681393, 33.8546828943959, 36.7216522931891, 31.9328396731727, 28.020945591457, 35.2032972671169, 32.8868757985435, 34.0559780304424, 35.8682914314074, 36.0429296442964, 30.7004562958979, 40.0223626363024, 34.5441438987989, 37.1956783386288, 37.3161221278867, 38.0315822709311, 36.1592825551482, 40.9307203441047, 38.3034653797697, 34.8841741293051, 28.8298227649124, 31.2351079846709, 26.58024867476, 33.2910895119295, 34.2948774610296, 29.1890591794299, 31.9696665225484, 28.2913563451578, 25.6154933522359, 31.8700912480585, 28.9337581860246, 31.0361103684804, 25.8114259564524, 25.9375745231057, 26.4999495789325, 22.953108116799, 25.7107283378609, 22.2085674572694, 20.6814215655407, 21.4229226038809, 23.6173436627767, 23.0054614138692, 27.2597257331308, 31.2037199842905, 30.8445735055566, 24.3455107485269, 24.5080508209162, 23.9845808234031, 21.8390989223883, 20.6512724777729, 27.9811463395092, 26.7396914417538, 18.3044065746177, 21.1340811616463, 22.3099971650573, 20.5529916974034, 24.7306502581991, 22.5605955272623, 23.2499520155363, 20.8233198306113, 19.2020605877322, 19.5258948300709, 19.7174167986696, 22.9711080138203, 26.5812468902838, 15.7666816540463, 18.4107624398377, 17.9920787363005, 16.1351584337972, 18.7771156353442, 19.9514690575553, 17.0969759456737, 19.8209370054012, 17.7951145480586, 18.3442513716899, 16.8590727756861, 19.0878080783168, 19.9717929599164, 17.7595149510379, 18.4134537880026, 15.7127269834616, 15.354294321979, 12.9770883715219, 13.3120110504337, 16.4540548337466, 13.438059494666, 17.0511297495394, 18.0500606522035, 18.1877342186245, 15.4602363014115, 16.2075233942782, 17.991562503935, 15.5123644728833, 15.1489873794801, 15.615591484629, 10.506989561797, 12.6779802637024, 15.5492964069148, 12.9291270445479, 11.747908426531, 11.0668867279814, 12.2460533502478, 9.39058947829773, 9.89146601845933, 12.5677791352393, 14.3704252021625, 15.3413700465061, 15.8109484103791, 14.9073058843551, 15.0477837399721, 12.9098442180014, 13.0144121703451, 14.285194663505, 12.4377935346652, 13.6702757979847, 11.8704845221998, 13.3734266583546, 12.9161294949607, 8.81675955143653, 9.89464438650754, 8.83079874024471, 10.747556445615, 11.638692945368, 11.5629127438259, 11.6028040492564, 9.74684477186343, 11.0828098114564, 10.4035324548224, 9.63560776408342, 10.5477692246192, 11.9584691921743, 10.8796508013276, 10.1770738456325, 11.3639775477361, 11.3076717331872, 8.8286741671423, 10.2680272777867, 9.67350498678802, 10.0205441169394, 8.49793954071686, 7.62962645016326, 8.74504938671, 9.49028311785272, 8.92625194407108, 9.28452714108943, 8.78297134898559, 8.4536582910836, 7.65509188699382, 7.68329904144858, 8.84339788260906, 7.64048288592267, 9.93498381237973, 8.73566744916831, 7.35152516121185, 9.37813827392415, 8.39788874509507, 8.20404450134356, 7.10866264774915, 8.12450490569416, 9.07341199429936, 7.96611044032304, 7.79008668034987, 8.87158835742697, 8.30176667162634, 8.47178188742093, 6.97208711507982, 8.17616432222799, 7.70350628804225, 8.16139008502279, 8.26132444881544, 6.17166124993781, 7.36228044860325, 7.59071189103607, 7.30183841123908, 7.76653615195461, 9.54126852914144, 9.02914443272915, 8.99873914121632, 10.0731436820697, 7.22884631094947, 8.22417451876903, 6.92364195805875, 7.3054957712252, 7.44119898166016, 8.79124382923602, 6.3702250705772, 8.82194386430006, 9.7899577807902, 7.52012318305048, 9.00697120682769, 8.61801566085461, 9.20458813662013, 9.07750102975259, 8.28569509248035, 8.20854630399802, 9.04461221879735, 10.8445305372469, 8.42593561229266, 6.92282213350238, 9.58747674364022, 9.54236805557813, 9.69295558825124, 7.23085438497574, 8.98330710653858, 10.3888102488019, 8.42064486643296, 6.59103889552387, 7.55770614697796, 8.37983864075794, 7.06095395879438, 8.21295597746197, 9.30155401423346, 8.7291486771678, 9.77250163833402, 8.09129438696569, 7.93091299995955, 8.35297699815799, 8.78441011304854, 8.35676586129302, 8.10511055238686, 8.9292230190236, 10.0763233224256, 9.72155416694892, 7.54029361451903, 9.66505744405793, 8.50789138245677, 9.68007957922729, 8.96945370846316, 8.44239982837616, 9.82247372402016, 9.70471848263007, 10.7861049941195, 8.81533270613568, 10.2695722603084, 8.71616427898897, 10.7125618141037, 11.9980218523513, 9.09725868644891, 9.17344025979816, 8.52229344875836, 6.65438933133763, 8.66372417981076, 9.75285154309771, 8.31956862398328, 8.00636938518332, 8.09599717187772, 8.40299193490586, 9.29813127766656, 7.3721462060642, 9.0800937998736, 8.47703928389856, 9.52348313448719, 9.30480318799906, 7.55488627183968, 9.84172804655935, 7.79609393372798, 9.70157649109868, 9.21268604758428, 8.70662823302027, 9.56646220876334, 8.15716306112789, 8.52352972766067, 8.92511297465147, 10.4827146804264, 8.80222392387146, 8.95152092413902, 9.51389838079701, 9.79534131613929, 9.41184595823495, 9.34524526597822, 9.93264925869453, 9.88331249751977, 8.56816298161805, 14.63190876725, 28.0163281760302, 16.4409556828738, 13.2399031959945, 11.6987786164509, 11.0008465713124, 11.8915980916749, 10.1211367015143, 13.0684556659104, 11.6951033375861, 11.3276754767616, 9.4856777829417, 10.6740636548413, 11.977519561609, 11.4610400437761, 11.1517921588024, 10.7150395342977, 10.2929155683609, 10.6208777832064, 10.3404043000724, 10.1389292963425, 15.9038810105199, 22.631974812018, 23.4093253794684, 14.8493243157723, 12.9349630011927, 13.2873092588217, 11.8208866020943, 10.8892802623005, 12.198400734963, 12.0891408552484, 12.5563983255916, 13.2126847258719, 15.4673164563224, 15.7170263443343, 14.5837472630384, 14.4394635911574, 11.9470813406968, 13.6030627183389, 11.3327336484205, 11.9072267824781, 16.1594107422218, 16.0839790303073, 13.537913350259, 13.0012759809886, 13.7568273872586, 12.6644123382577, 12.2757065651008, 14.6168640671074, 13.1186300480347, 10.7479824190967, 12.252837126911, 12.7677151226176, 13.7681930440971, 12.0994162904006, 10.8099240333248, 12.0270679981318, 12.6919025514606, 15.8253820424759, 12.3598562819862, 11.8455604544011, 23.273430299197, 25.2892664805916, 20.5022577182934, 17.0858576752559, 13.4492744789577, 19.2510533644223, 13.1502251355313, 14.6250236176353, 15.6895933931026, 15.6262071604035, 13.7045261596467, 13.4983008071578, 13.9616671898319, 13.2298336796905, 14.9655990094314, 17.0963537675676, 11.9926199572016, 14.5280364722282, 15.8826614382683, 14.5016290871551, 16.2913830634443, 15.1611229368713, 13.1332879517606, 13.9463535254468, 12.6258678756354, 15.1001798282247, 13.3033742088874, 14.3629505770085, 16.3711071662181, 16.5917666851451, 14.6487982052776, 14.9992021647243, 17.0806960932656, 28.126236883757, 29.8395157445974, 22.0001506676282, 17.9745114970772, 16.3913954781932, 16.391840068976, 14.5802127740562, 15.8957696616031, 15.979526085945, 18.1420161105158, 16.1258052535434, 16.8259450057398, 17.3620391736216, 14.6949201031453, 14.6904510752936, 17.6966886234852, 14.6953799234211, 13.999631214266, 11.5792424239497, 13.4262660811334, 15.5445531570256, 12.697376208542, 14.655287275451, 15.7677847846662, 15.1028552696662, 16.2007818847487, 13.8799207179112, 16.9400875445644, 13.923855374758, 17.7331515023543, 19.4341540016074, 16.7448167849617, 12.7704865787242, 13.913539343141, 16.5645228423266, 16.1498493451454, 14.7154209009956, 12.9186824059871, 15.1343847858204, 16.4221479628997, 29.8138759769783, 22.6831791420895, 19.6565931592558, 19.6134254358956, 23.0402475976937, 20.7721076207397, 20.6707295880198, 16.1993589684653, 17.0866543009975, 17.6548614797783, 16.2798786934982, 15.6998807183732, 15.7980433851087, 15.104207064221, 18.0240807573678, 16.2168518356821, 18.5209861096145, 16.7456083899396, 17.3041971928179, 17.02449555368, 19.1244595483576, 20.6774789798205, 17.9783046263623, 18.9634545655338, 15.8677686467535, 17.1285755746644, 15.7138946644387, 16.6947096316587, 14.1682480292604, 17.3049928651538, 16.6290652418838, 13.7608960422955, 16.1888630163493, 17.3629584122282, 18.3899894368682, 19.829317409703, 16.9971692317782, 17.6691694926117, 42.9388653389784, 45.7216379192677, 21.9206142722481, 18.8429438526348, 22.0792367213738, 22.2153093417223, 23.2538519665537, 20.717011548976, 21.8359313053631, 20.7975011243119, 19.5002413268156, 22.8140406126357, 19.0133345697823, 21.1757031557784, 22.01185894265, 17.8157041467532, 18.0817383377276, 19.7478029939906, 21.9439084424589, 20.4690466883923, 28.3884452138226, 36.119888205948, 29.9812442251303, 24.5297404272364, 25.1347306561429, 22.9406813197982, 20.840613057704, 24.4924940063462, 27.5803817686452, 26.9270863985476, 24.1811335228224, 28.5087529099383, 30.0828941087562, 25.472927314843, 29.1797151864966, 26.0576042808294, 24.3345298262233, 22.132493414588]]};
/*
* delta updates to data that would be incrementally appended to the original every 2 minutes (120000ms)
*/
var dataA = {"start":1336681080000,"end":1336681080000,"step":120000,"names":["Stats_count2xx","Stats_count3xx","Stats_count4xx","Stats_count5xx"],"values":[[15625.6826207297],[411.161376855185],[22.3887353437241],[22.3334186252455]]};
var dataB = {"start":1336681200000,"end":1336681200000,"step":120000,"names":["Stats_count2xx","Stats_count3xx","Stats_count4xx","Stats_count5xx"],"values":[[15540.6970523899],[410.530462906806],[21.0841810692307],[26.1008030172405]]};
var dataC = {"start":1336681320000,"end":1336681320000,"step":120000,"names":["Stats_count2xx","Stats_count3xx","Stats_count4xx","Stats_count5xx"],"values":[[15641.1082208258],[410.469367403579],[21.1010449137829],[51.24715381879]]};
var dataD = {"start":1336681440000,"end":1336681440000,"step":120000,"names":["Stats_count2xx","Stats_count3xx","Stats_count4xx","Stats_count5xx"],"values":[[15814.0450377164],[412.641854739243],[22.5711995438838],[41.9139079953229]]};
var dataE = {"start":1336681560000,"end":1336681680000,"step":120000,"names":["Stats_count2xx","Stats_count3xx","Stats_count4xx","Stats_count5xx"],"values":[[15859.6222573285, 15868.8955336875],[412.37113849966, 410.678153589876],[20.9144118654616, 20.6286077453409],[23.0275855057073, 22.9727162887915]]};
var data2 = {"start":1336700820000,"end":1336707720000,"step":60000,"names":["Stats_count2xx","Stats_count3xx","Stats_count4xx","Stats_count5xx"],"displayNames":["2xx","3xx","4xx","5xx"],"values":[[15561.3775732188, 15652.117643655, 15439.6144775067, 15556.4867279272, 15689.6703076805, 15751.7991002345, 15721.3820103242, 15643.9847968288, 15654.0823563109, 15603.0571195629, 15762.3138603365, 15753.6726343221, 16005.8511227081, 16154.9957268687, 16664.0927138963, 17294.5911779906, 17615.6319566415, 17410.0518462873, 17087.1814374022, 16995.4895626303, 16942.8641595466, 16810.5846722766, 16742.0824327817, 16758.9939222202, 16712.7590141833, 16669.4107936712, 16623.9533902628, 16526.1037253997, 16411.0716568429, 16396.4750301532, 16329.6806023645, 16289.0008076023, 16249.4892488981, 16063.0617638686, 15932.4240873799, 15892.952965665, 15902.7149210005, 15905.9715452788, 15612.2939713209, 15622.8775735103, 15751.2347086033, 15901.151645055, 15829.1353549119, 15719.7882223326, 15776.672679827, 15652.2633979826, 15453.069480672, 15268.3097042126, 15275.0258046411, 15160.1621979406, 15100.6558177349, 15039.4299412935, 14869.6207938091, 14738.6933539836, 14879.4725774044, 14858.9862496651, 14944.7564490541, 15184.5013882885, 15228.7893051868, 15235.9850692415, 15053.5063940683, 14965.3512041596, 14791.1704115068, 14776.9548485161, 14736.6476267017, 14776.4747042786, 14553.2746835049, 14584.0454248057, 14513.0490118922, 13714.7435712979, 13881.4066790036, 13851.0571540835, 14740.3908999476, 14904.0019806147, 15303.1273568168, 15528.52621491, 15508.1254774485, 15277.635546401, 15219.3947847374, 15150.5006844289, 14930.0148560864, 14732.5764124321, 14699.3539870029, 14626.1537548112, 14675.6999429452, 14477.2528626666, 14592.2307565135, 14465.6101470285, 14386.1155224068, 14436.3079386099, 14378.0867617162, 14136.3619070832, 14036.5375943694, 13932.2794985018, 13860.8405066643, 13843.4973564891, 13677.8169253535, 13598.8464571359, 13646.3497087913, 13574.253455634, 13471.9532210538, 13584.8790764831, 13452.8925800134, 13465.8212377569, 13433.4282455114, 13341.5676843751, 13322.3688605698, 13379.3895195237, 13297.0322418826, 13246.7843850634, 12996.4993229442, 12812.5118007746, 12847.6421190263, 12596.5142097218, 12585.5313261985, 12510.6868913193],[638.443971530326, 637.220285293543, 634.970368044194, 639.596448743683, 637.950998328559, 643.345996256487, 650.873409847199, 648.212109820469, 648.054131392016, 651.039367637167, 651.36772792345, 658.617192353121, 658.506980148127, 668.693745644564, 682.466681635212, 702.813025759769, 707.430835666445, 702.470065232817, 696.354964391518, 689.203523210888, 681.794445458101, 677.088465033619, 680.71388634168, 680.797125390613, 675.304352826557, 668.639067519935, 670.718387813961, 676.198979979379, 668.89228409763, 664.358302188762, 666.273323184445, 667.482671883488, 664.433449074677, 658.013118451316, 655.340680650006, 654.392740400409, 653.793374788697, 647.470375217427, 646.481919959594, 648.112148235271, 651.848392519912, 652.721629363907, 652.799605864023, 650.917731223063, 656.58040631131, 650.191232120298, 648.826092950745, 647.243566379495, 641.907980287912, 639.47053395402, 638.727885412416, 636.330010535911, 633.292767141803, 630.737180262256, 625.628842175709, 627.467659888023, 627.675522254451, 637.521468267799, 644.501636365719, 640.810255195788, 634.477376516782, 634.038949555322, 629.637345870219, 633.28264940923, 627.766780156681, 628.978505595697, 627.31116404511, 628.383075687635, 629.260851176047, 628.992513691999, 636.596195441036, 633.141629083662, 635.100095868791, 643.77255503767, 656.478375585916, 662.38308152617, 660.285273092074, 653.324236551684, 649.671131737662, 642.051664070858, 638.290463917959, 636.078756581499, 640.336451463137, 637.036637912894, 633.61708864354, 631.759222921161, 632.275620017408, 631.598962272226, 627.58645753934, 624.978193764449, 625.560914433368, 615.563060119222, 615.719520504137, 614.471431639559, 616.575814561871, 610.45566883691, 608.162564107372, 603.44338831323, 605.403245056785, 601.407214753202, 599.693908089561, 598.856658168687, 600.685186170311, 598.042676581054, 596.016036289173, 590.713506124718, 593.974042212487, 595.585906545105, 591.058078830964, 590.598622397951, 586.245932427695, 585.544852889813, 585.338068755182, 581.697254092539, 581.017710337292, 583.855665862638],[36.1649174643383, 37.0177823551602, 37.329999031026, 37.2836963770788, 36.0280797140669, 35.8438517422347, 36.2320638466919, 36.4112237823172, 37.3439928373529, 37.0770865554348, 35.574601951628, 36.7667027985075, 34.8157216425256, 43.771724923628, 51.1516307828587, 42.805109426828, 39.2403214485822, 38.2323165799422, 39.1060079376386, 38.3835942833147, 37.5585211917841, 37.2186344163937, 36.93560486554, 36.912594607571, 36.4856031351596, 36.6477863710023, 36.4305168297744, 36.286190554503, 36.4820199468499, 38.021906561751, 37.851298521766, 38.1382663425207, 36.3405743523845, 36.1506105776344, 35.1652337357932, 35.5560277252467, 35.6701909951617, 33.2647170748041, 34.0319018292706, 34.4646537203075, 34.164781593033, 33.0455623041717, 35.2955032979767, 36.0956737534543, 36.0862828475811, 35.1988102796235, 32.095751958889, 31.2039475211224, 32.2935991303062, 34.0693538058798, 32.5877275932507, 32.4213146213982, 32.0924829391752, 31.7288301370707, 33.3033885489846, 32.9749897764156, 31.6518531636599, 32.5426368487365, 30.1258901828397, 31.5991874649914, 32.8293005677837, 33.787545425429, 32.1314156052065, 31.0792899579898, 32.2711856840471, 32.1841661164147, 30.6346651105639, 31.7331129731986, 31.2879550414213, 31.7169715244488, 32.0120570193128, 32.7071010895207, 32.2653970187325, 38.9079149123769, 43.2017432305432, 36.2522709432187, 33.8348192274207, 31.9227520909763, 32.3070910272764, 32.0302085739153, 32.01954360206, 32.4343522206252, 31.3271147316889, 32.0667241174602, 31.1995355645818, 31.2296519516852, 31.2542760507012, 30.7516279320849, 31.3255765538479, 19.5766898738338, 19.6304466528159, 30.8456617586958, 31.0739475629595, 30.9220492116723, 19.261689623793, 31.0878949652739, 31.2204002443203, 32.1919503785021, 31.7021686864387, 30.3488880633936, 31.1252787428841, 32.172113992636, 31.4518241380035, 30.4139238179403, 30.4268936550986, 30.1051039006144, 30.5236314258168, 30.6172730743092, 19.0701022658699, 30.6495965579316, 31.4870648480992, 31.5019230393236, 19.7896958682167, 18.0036634824051, 18.5582584756899, 16.9194094550122],[74.1602384709859, 57.4943223010318, 54.1536255886398, 56.9926309545533, 55.275492071299, 42.5805667245307, 36.4743875779638, 45.3145713491707, 49.5329182547038, 51.213460607915, 52.4679571897654, 52.1617073260999, 53.8263946225893, 55.0907977844544, 59.0363532817437, 54.2286764447033, 50.213819681142, 49.7820921490716, 52.8866704182808, 49.5458348232274, 37.4623177921273, 38.0459734609214, 45.0732542111332, 52.3507599581385, 51.7630054965915, 44.9391360382929, 43.2720780366799, 47.9762046637662, 45.1127196068014, 47.4343367623041, 41.01389948341, 37.162597266729, 43.3228177227352, 51.6710007196427, 54.2479528794955, 46.7613737901555, 49.0244269742445, 47.1416997840853, 43.7021505588771, 47.7825400949965, 45.149090197819, 43.8272015987117, 48.9042042352909, 56.2850175958141, 52.2227754991926, 46.5546636594659, 43.6673335204669, 45.0120421710924, 41.8445320705265, 41.5905442504496, 42.1938947067436, 41.8618897706145, 42.7308503037221, 45.8172397403316, 47.5574309746286, 40.9765405905146, 45.5580509560832, 49.1071902308056, 44.6527401392699, 48.0875181538515, 42.9530333694996, 45.5472332249859, 49.4059639866303, 43.4371319578748, 41.5677269951915, 38.8637046331557, 42.6041358314802, 44.5584998672936, 41.8593786688262, 43.6395537843263, 43.5112139273016, 46.6330465878063, 51.8402845420458, 45.3739650299113, 42.6909652691773, 41.5510853909189, 42.4335090017537, 45.186741712851, 44.4317947903739, 40.368902302555, 42.4904935360309, 47.1968152889616, 41.2488864460104, 44.1457240978645, 40.4452766184277, 36.7471597463218, 38.9197459206283, 39.5606777487967, 44.3443893587701, 43.0952739835518, 36.4706374408558, 39.8078447285754, 40.469665496116, 38.8477223418482, 39.4734423579917, 36.2337852513363, 37.5238033463348, 40.9091824716216, 43.6823969971648, 39.3667702155646, 42.0348443652282, 48.7059798210668, 51.6666480304664, 48.2698440412875, 40.2885012047223, 38.189881733114, 41.3669110406678, 40.5304219971405, 40.209216031085, 36.4906010588927, 33.0340748600847, 42.2213387501689, 39.8621356866956, 42.3171332400425, 41.8255110916335, 34.002464394489]]};
var data3 = {"start":1938982200000,"end":1939413600000,"step":600000,"names":["Data1","Data2"],"values":[[2.439, 2.44, 2.54, 2.376, 2.335, 2.356, 2.395, 2.888, 2.96, 3.087, 3.331, 3.423, 3.526, 3.328, 3.164, 3.257, 3.308, 3.47, 3.698, 3.504, 3.433, 3.295, 3.21, 3.366, 3.514, 3.554, 3.416, 3.42, 3.437, 3.328, 3.348, 3.25, 3.235, 3.155, 3.113, 3.091, 3.16, 2.973, 2.86, 2.76, 2.634, 2.604, 2.492, 2.241, 2.168, 2.164, 2.071, 2.009, 2.16, 2.006, 2.018, 2.031, 1.909, 1.925, 1.949, 1.775, 1.787, 1.74, 1.738, 1.674, 1.714, 1.555, 1.514, 1.526, 1.482, 1.494, 1.52, 1.439, 1.432, 1.422, 1.395, 1.413, 1.489, 1.471, 1.483, 1.586, 1.568, 1.693, 1.774, 1.802, 1.877, 1.952, 2.082, 2.161, 2.381, 2.556, 2.611, 2.822, 2.752, 2.894, 2.767, 2.734, 2.721, 2.835, 2.891, 2.796, 2.969, 2.797, 2.752, 2.803, 2.877, 3.012, 3.043, 2.952, 2.866, 2.705, 2.691, 2.762, 2.931, 2.951, 2.977, 3.036, 3.056, 3.673, 4.16, 3.83, 3.7, 3.759, 3.8, 3.816, 3.97, 4.001, 4.01, 3.998, 4.017, 4.135, 4.158, 3.938, 3.824, 3.78, 3.709, 3.748, 3.901, 3.874, 3.909, 3.934, 3.957, 4.058, 4.111, 4.007, 3.934, 3.681, 3.649, 3.72, 3.823, 3.801, 3.865, 3.88, 3.931, 4.045, 4.114, 4.094, 3.923, 3.927, 3.86, 4.001, 4.438, 4.326, 4.05, 3.903, 4.03, 4.186, 4.437, 4.241, 3.914, 3.801, 3.653, 3.656, 4.036, 4.099, 3.917, 3.751, 3.621, 3.585, 3.786, 3.613, 3.563, 3.557, 3.419, 3.35, 3.535, 3.304, 3.204, 3.112, 3.019, 3.591, 3.582, 3.398, 3.269, 3.464, 3.458, 3.55, 4.271, 4.259, 3.983, 3.755, 3.555, 3.204, 3.082, 3.054, 3.066, 3.022, 3.282, 3.32, 3.477, 3.124, 2.91, 2.785, 2.609, 2.444, 2.297, 2.288, 2.299, 2.338, 2.306, 2.346, 2.57, 2.429, 2.392, 2.714, 2.664, 2.685, 2.785, 2.912, 2.913, 3.169, 3.329, 3.458, 3.834, 4.035, 4.474, 4.776, 4.688, 4.669, 4.43, 4.354, 4.057, 4.333, 4.152, 4.227, 4.141, 4.025, 4.045, 4.239, 4.152, 4.11, 4.153, 4.018, 3.979, 3.903, 3.968, 4.078, 4.294, 4.141, 3.883, 3.995, 4.041, 4.069, 4.22, 4.086, 3.89, 3.888, 3.978, 3.972, 4.234, 4.153, 3.814, 3.773, 3.709, 3.757, 3.898, 3.859, 3.886, 4.029, 4.053, 4.079, 4.183, 3.97, 3.721, 3.788, 3.613, 3.394, 3.696, 3.714, 3.651, 3.701, 3.802, 3.828, 4.035, 3.972, 3.971, 3.979, 3.974, 4.139, 4.379, 4.044, 3.784, 3.79, 4.036, 3.958, 4.525, 4.225, 4.079, 4.222, 4.162, 3.997, 4.13, 3.815, 4.008, 3.924, 3.953, 3.995, 4.301, 4.305, 4.157, 3.929, 3.893, 3.664, 3.974, 4.27, 3.631, 3.154, 3.056, 3.142, 3.248, 3.208, 3.025, 2.761, 2.606, 2.581, 2.664, 2.524, 2.393, 2.342, 2.342, 2.303, 2.476, 2.347, 2.328, 2.336, 2.269, 2.217, 2.144, 2.179, 2.075, 2.171, 2.124, 2.031, 1.995, 1.992, 2.02, 2.006, 2.052, 1.934, 2.067, 1.955, 2.07, 2.061, 2.256, 2.378, 2.566, 2.479, 2.492, 2.523, 2.498, 2.555, 2.768, 2.812, 2.833, 3.027, 3.101, 3.218, 3.583, 3.556, 3.761, 3.963, 4.092, 4.169, 4.446, 4.213, 4.17, 4.157, 4.082, 3.982, 4.261, 4.412, 4.297, 4.262, 3.961, 3.802, 4.058, 4.12, 4.122, 4.076, 3.878, 3.945, 4.312, 4.477, 4.526, 4.484, 4.046, 3.89, 3.777, 3.803, 3.813, 3.919, 4.206, 3.913, 4.042, 4.08, 4.084, 4.142, 3.963, 3.753, 3.725, 3.668, 3.755, 3.872, 3.828, 3.858, 4.074, 4.203, 4.143, 4.136, 4.349, 4.241, 3.739, 3.817, 3.56, 3.56, 3.742, 3.794, 4.129, 4.027, 4.334, 4.209, 4.173, 4.019, 3.946, 3.995, 4.073, 4.147, 4.2, 3.802, 4.148, 3.883, 3.887, 3.756, 3.734, 3.747, 4.063, 4.04, 4.004, 4.053, 4.065, 4.075, 4.381, 4.345, 4.226, 4.136, 4.232, 3.99, 4.246, 4.098, 4.397, 4.226, 3.931, 3.794, 3.933, 3.682, 3.771, 3.688, 3.468, 3.036, 2.905, 2.766, 2.598, 2.514, 2.506, 2.293, 2.4, 2.301, 2.187, 2.105, 2.01, 2.088, 2.051, 2.075, 1.956, 2.08, 1.98, 2.039, 2.048, 1.952, 1.981, 1.952, 1.981, 1.959, 2.021, 1.936, 1.972, 1.964, 1.971, 2.012, 2.176, 2.162, 2.19, 2.363, 2.445, 2.611, 2.818, 2.865, 2.942, 3.068, 3.178, 3.388, 3.745, 3.919, 3.99, 4.141, 3.944, 4.102, 4.457, 4.29, 4.366, 4.188, 4.095, 4.207, 4.639, 4.855, 4.653, 4.306, 4.431, 3.997, 4.38, 4.329, 4.242, 3.919, 3.829, 3.741, 4.16, 4.271, 4.125, 3.956, 3.792, 3.836, 4.133, 4.254, 3.97, 3.83, 3.83, 3.647, 3.566, 3.201, 3.799, 3.872, 3.912, 3.926, 4.09, 4.128, 4.01, 3.669, 2.75, 2.524, 2.69, 2.771, 2.963, 2.779, 2.946, 2.781, 2.824, 2.874, 2.935, 2.92, 2.924, 2.962, 3.037, 3.203, 3.281, 3.336, 3.26, 3.284, 3.41, 3.468, 3.584, 3.697, 3.772, 3.694, 4.038, 4.11, 4.04, 4.177, 3.941, 3.956, 4.171, 4.08, 3.859, 3.777, 3.587, 3.586, 3.824, 3.828, 3.781, 3.672, 3.755, 3.427, 3.815, 3.708, 3.646, 3.612, 3.463, 3.355, 3.367, 3.198, 3.05, 2.973, 2.79, 2.686, 3.23, 3.54, 3.316, 3.415, 4.102, 3.636, 3.491, 3.294, 3.055, 2.936, 2.738, 2.605, 2.577, 2.391, 2.331, 2.452, 2.246, 2.327, 2.369, 2.347, 2.293, 2.332, 2.22, 2.225, 2.337, 2.245, 2.305, 2.347, 2.411, 2.359, 2.392, 2.403, 2.363, 2.619, 2.645, 2.714, 2.858, 2.961, 2.959, 3.203, 3.336, 3.433, 3.623, 3.744, 3.907, 4.115, 4.346, 4.235, 4.008, 3.939, 3.867, 4.094, 4.261, 4.209, 3.982, 4.12, 3.832, 3.793, 3.855, 3.733, 4.002, 4.045, 4.115, 4.135, 4.01, 3.867, 4.175, 3.996, 4.094, 3.814, 3.843, 3.891, 4.065, 4.118, 3.972, 3.849, 3.903, 3.733, 3.89, 3.915, 3.912, 3.908, 3.837, 3.935, 4.018, 4.12, 3.953, 3.956, 3.83, 3.892, 3.846, 3.866, 3.906, 3.957, 4.178, 4.016, 4.121, 4.065, 4.028, 3.808, 3.886],[849, 849.4, 881.1, 894.7, 907.1, 941.8, 893.8, 723.1, 710.1, 723, 723, 711.2, 723.9, 756.2, 776.5, 777.6, 778, 781.3, 795.3, 799.8, 858.6, 862.3, 861.8, 863.9, 863.1, 863.1, 871.5, 897.4, 898, 898.8, 897.6, 898.3, 899, 899, 899, 899, 899, 899, 894.7, 897.6, 897.5, 899, 899, 899, 896.4, 896.9, 899, 898, 825.8, 824, 786.7, 756.5, 757, 709.3, 696.8, 696.9, 647.8, 643, 603, 594.5, 595, 595, 583.4, 551, 542.2, 509.5, 503.6, 475, 466.1, 443, 436.6, 414, 406.2, 388, 379.2, 365, 359, 344, 344, 344, 344, 344, 344, 344, 344, 344, 344.2, 355.5, 360, 374.1, 384.7, 401.7, 405.1, 406, 412.5, 430.8, 447.1, 460, 478.1, 491, 491, 486.7, 504.5, 522.8, 544.7, 560.5, 561, 560.9, 560.9, 562, 562, 562, 562, 460.7, 422.2, 450, 450.4, 451, 451.4, 453, 453, 452.7, 453, 453, 453, 453, 453.6, 485.9, 497, 497, 497, 497.6, 498, 498, 498, 498, 498, 492.8, 498, 499.4, 533.9, 544, 544.4, 545.4, 546.9, 547, 547, 547, 547, 547, 540.1, 557.5, 596.4, 598.9, 599.4, 600, 600.4, 605, 653.8, 658.9, 659, 659.3, 663.2, 716.5, 731.6, 787.2, 791.3, 795.5, 797, 797.9, 810.5, 846.4, 848.5, 849, 846.5, 849, 847.5, 849, 848.8, 849, 849, 849, 848.9, 849, 839.6, 705.5, 699.6, 698.8, 699.6, 628.6, 600, 550.2, 478, 453, 453, 453, 453, 453, 446.3, 423.9, 401.3, 380.2, 332.6, 309.1, 300, 300, 298.9, 300, 300, 300, 300, 286.9, 270, 260.1, 243, 231.7, 219, 219, 219, 200.9, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 200.7, 214.9, 222.5, 237.7, 251.6, 261.4, 278.3, 282.7, 301.5, 311.4, 334.9, 342.6, 343.7, 343, 373.5, 376.5, 381.3, 408.3, 415, 415, 415.6, 417.9, 436.1, 458, 458, 458, 458.6, 459, 462.2, 495.4, 497.1, 500.4, 501, 503.4, 512.9, 551.8, 553.2, 554, 554, 554, 554, 554, 554, 554, 554, 556.5, 599.6, 606, 606.5, 601.4, 605.4, 609, 609, 605, 609, 608.6, 609, 609, 609, 608.2, 609, 609, 608.8, 609.9, 646, 661.3, 665.3, 666, 668.4, 669, 669.4, 719.1, 733.1, 734.7, 759.9, 801.8, 805.6, 805.7, 803.6, 806.6, 808, 807.5, 792.4, 811.4, 837, 846.3, 848.1, 848.6, 848.8, 849, 849, 847.3, 842.2, 849, 846.2, 849, 849, 849, 849, 846, 847.2, 849, 818.1, 763.9, 743, 686.2, 687.4, 644.7, 620, 590.7, 558, 537.8, 503, 493.2, 454, 453.1, 453, 453, 424.5, 408, 389.1, 368, 350, 332, 321.9, 299, 282.2, 249, 227.1, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 235.1, 244.7, 260.3, 266.4, 271.1, 295.4, 296.5, 297, 310.6, 323, 334.5, 353.1, 354.3, 357.8, 358, 378.4, 390, 390, 368.9, 383.2, 389.7, 410.5, 427.6, 456.2, 468.4, 470.6, 473.5, 474.5, 475, 474.8, 474.6, 474.6, 475, 475, 479.2, 516.7, 518.6, 519.6, 520, 520.7, 519.8, 516.2, 520.5, 521, 520.9, 521.3, 523.3, 565.6, 574.8, 617.2, 621.9, 623.1, 626.1, 627.9, 629.4, 627.5, 629.6, 630, 629.8, 674.9, 682.6, 684.7, 675.3, 680.8, 717.5, 754.7, 757.1, 757.4, 783.9, 829.8, 834, 836.7, 836.6, 836.5, 838.1, 838.7, 839.4, 835.6, 848.6, 847.8, 847.9, 847.8, 847.9, 845.4, 847, 847.7, 848.5, 848, 848.9, 848.4, 849, 848.5, 849, 849, 849, 849, 849, 849, 849, 849, 849, 848.2, 785.2, 765, 764.8, 765, 751.5, 689, 682.9, 621.1, 619.1, 559.6, 559, 512.4, 503.4, 491.4, 453.9, 445.6, 409, 400.8, 367.1, 368.3, 334.3, 333, 305.5, 299.8, 276.7, 270, 252.6, 243, 228.6, 219, 219, 219, 219, 218.9, 219, 219, 219, 219, 219, 217.4, 224.8, 239.9, 240, 240.4, 257.9, 264.1, 281.9, 284.4, 285.2, 285.9, 304.9, 317.4, 339.9, 345.1, 346, 347, 372.8, 381.6, 409.4, 416.4, 417.7, 418.9, 432, 462, 462.1, 463.8, 463.8, 460.3, 465.8, 502.8, 504, 506.6, 505.5, 509, 511, 511, 511, 511, 511, 511.3, 542.6, 559, 560.1, 561.7, 554.6, 545.6, 549.8, 549.8, 550, 550, 549.7, 550, 550, 549.7, 550, 549.5, 550, 549.7, 550, 550, 549.8, 550, 550, 549.8, 548.9, 550, 550, 550, 550, 547.3, 550, 550, 563.4, 599.3, 602.4, 603.3, 624.6, 654.4, 661, 661.8, 663.1, 661, 664, 662.9, 664, 664, 663.3, 665, 665, 661.7, 665, 665, 665, 665, 663.2, 665, 665, 665, 560.3, 499.9, 500, 465.4, 400, 400, 395, 395.1, 400, 399.9, 400, 400, 399.8, 397.8, 386, 360, 360, 333.6, 324, 306.7, 292, 275.8, 263, 254.7, 237, 234.4, 214, 211, 193, 193, 193, 193, 192.7, 175.2, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174.1, 183.3, 189.9, 205.1, 209, 209.7, 210, 223, 229.8, 245.3, 251.1, 271.1, 276.6, 277.3, 277.9, 277.8, 278, 279, 300.1, 302, 302, 302.4, 329.2, 335, 335, 335, 335, 335, 335.2, 353.6, 363.9, 364.4, 365, 367.4, 368, 368, 368, 367.2, 367.2, 368, 368.2, 386.8, 401.6, 402, 402, 402.4, 403, 403, 403.4, 404, 402.2, 403.9, 424.4, 438.9, 438.9]]};
/*
* The CSS below is intended for the "global" styles of svg.line-graph, not specifics per graph such as color of lines
* which is expected to come from the data itself (even though it can be done via CSS as well if wanted)
*/
svg.line-graph text {
cursor: default;
}
svg.line-graph .hover-line {
stroke: #6E7B8B;
}
svg.line-graph .hide {
opacity: 0;
}
svg.line-graph .axis {
shape-rendering: crispEdges;
}
svg.line-graph .x.axis line {
stroke: #D3D3D3;
}
svg.line-graph .x.axis .minor {
stroke-opacity: .5;
}
svg.line-graph .x.axis path {
display: none;
}
svg.line-graph .x.axis text {
font-size: 10px;
}
svg.line-graph .y.axis line, .y.axis path {
fill: none;
stroke: #000;
}
svg.line-graph .y.axis text {
font-size: 12px;
}
svg.line-graph .scale-button:not(.selected):hover {
text-decoration: underline;
cursor: pointer !important;
}
svg.line-graph .date-label {
fill: #6E7B8B;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment