changeset 696:a51af1f06e46

Reverse-merging [770] to re-create history for 4 deleted files. Follow-up to [773].
author osimons
date Thu, 17 Sep 2009 22:22:45 +0000
parents 67b1f4577359
children 86e946ced5a0
files bitten/htdocs/charts.swf bitten/htdocs/excanvas.js bitten/htdocs/jquery.flot.js bitten/report/coverage.py bitten/report/lint.py bitten/report/testing.py bitten/templates/bitten_chart_coverage.html bitten/templates/bitten_chart_lint.html bitten/templates/bitten_chart_tests.html bitten/templates/bitten_config.html bitten/templates/json.txt bitten/web_ui.py
diffstat 12 files changed, 147 insertions(+), 2969 deletions(-) [+]
line wrap: on
line diff
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..e2821facdb1d55065baf6a435dafe72de5d8e777
GIT binary patch
literal 29521
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
AdH?_b
deleted file mode 100644
--- a/bitten/htdocs/excanvas.js
+++ /dev/null
@@ -1,785 +0,0 @@
-// Copyright 2006 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-
-// Known Issues:
-//
-// * Patterns are not implemented.
-// * Radial gradient are not implemented. The VML version of these look very
-//   different from the canvas one.
-// * Clipping paths are not implemented.
-// * Coordsize. The width and height attribute have higher priority than the
-//   width and height style values which isn't correct.
-// * Painting mode isn't implemented.
-// * Canvas width/height should is using content-box by default. IE in
-//   Quirks mode will draw the canvas using border-box. Either change your
-//   doctype to HTML5
-//   (http://www.whatwg.org/specs/web-apps/current-work/#the-doctype)
-//   or use Box Sizing Behavior from WebFX
-//   (http://webfx.eae.net/dhtml/boxsizing/boxsizing.html)
-// * Optimize. There is always room for speed improvements.
-
-// only add this code if we do not already have a canvas implementation
-if (!window.CanvasRenderingContext2D) {
-
-(function () {
-
-  // alias some functions to make (compiled) code shorter
-  var m = Math;
-  var mr = m.round;
-  var ms = m.sin;
-  var mc = m.cos;
-
-  // this is used for sub pixel precision
-  var Z = 10;
-  var Z2 = Z / 2;
-
-  var G_vmlCanvasManager_ = {
-    init: function (opt_doc) {
-      var doc = opt_doc || document;
-      if (/MSIE/.test(navigator.userAgent) && !window.opera) {
-        var self = this;
-        doc.attachEvent("onreadystatechange", function () {
-          self.init_(doc);
-        });
-      }
-    },
-
-    init_: function (doc) {
-      if (doc.readyState == "complete") {
-        // create xmlns
-        if (!doc.namespaces["g_vml_"]) {
-          doc.namespaces.add("g_vml_", "urn:schemas-microsoft-com:vml");
-        }
-
-        // setup default css
-        var ss = doc.createStyleSheet();
-        ss.cssText = "canvas{display:inline-block;overflow:hidden;" +
-            // default size is 300x150 in Gecko and Opera
-            "text-align:left;width:300px;height:150px}" +
-            "g_vml_\\:*{behavior:url(#default#VML)}";
-
-        // find all canvas elements
-        var els = doc.getElementsByTagName("canvas");
-        for (var i = 0; i < els.length; i++) {
-          if (!els[i].getContext) {
-            this.initElement(els[i]);
-          }
-        }
-      }
-    },
-
-    fixElement_: function (el) {
-      // in IE before version 5.5 we would need to add HTML: to the tag name
-      // but we do not care about IE before version 6
-      var outerHTML = el.outerHTML;
-
-      var newEl = el.ownerDocument.createElement(outerHTML);
-      // if the tag is still open IE has created the children as siblings and
-      // it has also created a tag with the name "/FOO"
-      if (outerHTML.slice(-2) != "/>") {
-        var tagName = "/" + el.tagName;
-        var ns;
-        // remove content
-        while ((ns = el.nextSibling) && ns.tagName != tagName) {
-          ns.removeNode();
-        }
-        // remove the incorrect closing tag
-        if (ns) {
-          ns.removeNode();
-        }
-      }
-      el.parentNode.replaceChild(newEl, el);
-      return newEl;
-    },
-
-    /**
-     * Public initializes a canvas element so that it can be used as canvas
-     * element from now on. This is called automatically before the page is
-     * loaded but if you are creating elements using createElement you need to
-     * make sure this is called on the element.
-     * @param {HTMLElement} el The canvas element to initialize.
-     * @return {HTMLElement} the element that was created.
-     */
-    initElement: function (el) {
-      el = this.fixElement_(el);
-      el.getContext = function () {
-        if (this.context_) {
-          return this.context_;
-        }
-        return this.context_ = new CanvasRenderingContext2D_(this);
-      };
-
-      // do not use inline function because that will leak memory
-      el.attachEvent('onpropertychange', onPropertyChange);
-      el.attachEvent('onresize', onResize);
-
-      var attrs = el.attributes;
-      if (attrs.width && attrs.width.specified) {
-        // TODO: use runtimeStyle and coordsize
-        // el.getContext().setWidth_(attrs.width.nodeValue);
-        el.style.width = attrs.width.nodeValue + "px";
-      } else {
-        el.width = el.clientWidth;
-      }
-      if (attrs.height && attrs.height.specified) {
-        // TODO: use runtimeStyle and coordsize
-        // el.getContext().setHeight_(attrs.height.nodeValue);
-        el.style.height = attrs.height.nodeValue + "px";
-      } else {
-        el.height = el.clientHeight;
-      }
-      //el.getContext().setCoordsize_()
-      return el;
-    }
-  };
-
-  function onPropertyChange(e) {
-    var el = e.srcElement;
-
-    switch (e.propertyName) {
-      case 'width':
-        el.style.width = el.attributes.width.nodeValue + "px";
-        el.getContext().clearRect();
-        break;
-      case 'height':
-        el.style.height = el.attributes.height.nodeValue + "px";
-        el.getContext().clearRect();
-        break;
-    }
-  }
-
-  function onResize(e) {
-    var el = e.srcElement;
-    if (el.firstChild) {
-      el.firstChild.style.width =  el.clientWidth + 'px';
-      el.firstChild.style.height = el.clientHeight + 'px';
-    }
-  }
-
-  G_vmlCanvasManager_.init();
-
-  // precompute "00" to "FF"
-  var dec2hex = [];
-  for (var i = 0; i < 16; i++) {
-    for (var j = 0; j < 16; j++) {
-      dec2hex[i * 16 + j] = i.toString(16) + j.toString(16);
-    }
-  }
-
-  function createMatrixIdentity() {
-    return [
-      [1, 0, 0],
-      [0, 1, 0],
-      [0, 0, 1]
-    ];
-  }
-
-  function matrixMultiply(m1, m2) {
-    var result = createMatrixIdentity();
-
-    for (var x = 0; x < 3; x++) {
-      for (var y = 0; y < 3; y++) {
-        var sum = 0;
-
-        for (var z = 0; z < 3; z++) {
-          sum += m1[x][z] * m2[z][y];
-        }
-
-        result[x][y] = sum;
-      }
-    }
-    return result;
-  }
-
-  function copyState(o1, o2) {
-    o2.fillStyle     = o1.fillStyle;
-    o2.lineCap       = o1.lineCap;
-    o2.lineJoin      = o1.lineJoin;
-    o2.lineWidth     = o1.lineWidth;
-    o2.miterLimit    = o1.miterLimit;
-    o2.shadowBlur    = o1.shadowBlur;
-    o2.shadowColor   = o1.shadowColor;
-    o2.shadowOffsetX = o1.shadowOffsetX;
-    o2.shadowOffsetY = o1.shadowOffsetY;
-    o2.strokeStyle   = o1.strokeStyle;
-    o2.arcScaleX_    = o1.arcScaleX_;
-    o2.arcScaleY_    = o1.arcScaleY_;
-  }
-
-  function processStyle(styleString) {
-    var str, alpha = 1;
-
-    styleString = String(styleString);
-    if (styleString.substring(0, 3) == "rgb") {
-      var start = styleString.indexOf("(", 3);
-      var end = styleString.indexOf(")", start + 1);
-      var guts = styleString.substring(start + 1, end).split(",");
-
-      str = "#";
-      for (var i = 0; i < 3; i++) {
-        str += dec2hex[Number(guts[i])];
-      }
-
-      if ((guts.length == 4) && (styleString.substr(3, 1) == "a")) {
-        alpha = guts[3];
-      }
-    } else {
-      str = styleString;
-    }
-
-    return [str, alpha];
-  }
-
-  function processLineCap(lineCap) {
-    switch (lineCap) {
-      case "butt":
-        return "flat";
-      case "round":
-        return "round";
-      case "square":
-      default:
-        return "square";
-    }
-  }
-
-  /**
-   * This class implements CanvasRenderingContext2D interface as described by
-   * the WHATWG.
-   * @param {HTMLElement} surfaceElement The element that the 2D context should
-   * be associated with
-   */
-   function CanvasRenderingContext2D_(surfaceElement) {
-    this.m_ = createMatrixIdentity();
-
-    this.mStack_ = [];
-    this.aStack_ = [];
-    this.currentPath_ = [];
-
-    // Canvas context properties
-    this.strokeStyle = "#000";
-    this.fillStyle = "#000";
-
-    this.lineWidth = 1;
-    this.lineJoin = "miter";
-    this.lineCap = "butt";
-    this.miterLimit = Z * 1;
-    this.globalAlpha = 1;
-    this.canvas = surfaceElement;
-
-    var el = surfaceElement.ownerDocument.createElement('div');
-    el.style.width =  surfaceElement.clientWidth + 'px';
-    el.style.height = surfaceElement.clientHeight + 'px';
-    el.style.overflow = 'hidden';
-    el.style.position = 'absolute';
-    surfaceElement.appendChild(el);
-
-    this.element_ = el;
-    this.arcScaleX_ = 1;
-    this.arcScaleY_ = 1;
-  }
-
-  var contextPrototype = CanvasRenderingContext2D_.prototype;
-  contextPrototype.clearRect = function() {
-    this.element_.innerHTML = "";
-    this.currentPath_ = [];
-  };
-
-  contextPrototype.beginPath = function() {
-    // TODO: Branch current matrix so that save/restore has no effect
-    //       as per safari docs.
-
-    this.currentPath_ = [];
-  };
-
-  contextPrototype.moveTo = function(aX, aY) {
-    this.currentPath_.push({type: "moveTo", x: aX, y: aY});
-    this.currentX_ = aX;
-    this.currentY_ = aY;
-  };
-
-  contextPrototype.lineTo = function(aX, aY) {
-    this.currentPath_.push({type: "lineTo", x: aX, y: aY});
-    this.currentX_ = aX;
-    this.currentY_ = aY;
-  };
-
-  contextPrototype.bezierCurveTo = function(aCP1x, aCP1y,
-                                            aCP2x, aCP2y,
-                                            aX, aY) {
-    this.currentPath_.push({type: "bezierCurveTo",
-                           cp1x: aCP1x,
-                           cp1y: aCP1y,
-                           cp2x: aCP2x,
-                           cp2y: aCP2y,
-                           x: aX,
-                           y: aY});
-    this.currentX_ = aX;
-    this.currentY_ = aY;
-  };
-
-  contextPrototype.quadraticCurveTo = function(aCPx, aCPy, aX, aY) {
-    // the following is lifted almost directly from
-    // http://developer.mozilla.org/en/docs/Canvas_tutorial:Drawing_shapes
-    var cp1x = this.currentX_ + 2.0 / 3.0 * (aCPx - this.currentX_);
-    var cp1y = this.currentY_ + 2.0 / 3.0 * (aCPy - this.currentY_);
-    var cp2x = cp1x + (aX - this.currentX_) / 3.0;
-    var cp2y = cp1y + (aY - this.currentY_) / 3.0;
-    this.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, aX, aY);
-  };
-
-  contextPrototype.arc = function(aX, aY, aRadius,
-                                  aStartAngle, aEndAngle, aClockwise) {
-    aRadius *= Z;
-    var arcType = aClockwise ? "at" : "wa";
-
-    var xStart = aX + (mc(aStartAngle) * aRadius) - Z2;
-    var yStart = aY + (ms(aStartAngle) * aRadius) - Z2;
-
-    var xEnd = aX + (mc(aEndAngle) * aRadius) - Z2;
-    var yEnd = aY + (ms(aEndAngle) * aRadius) - Z2;
-
-    // IE won't render arches drawn counter clockwise if xStart == xEnd.
-    if (xStart == xEnd && !aClockwise) {
-      xStart += 0.125; // Offset xStart by 1/80 of a pixel. Use something
-                       // that can be represented in binary
-    }
-
-    this.currentPath_.push({type: arcType,
-                           x: aX,
-                           y: aY,
-                           radius: aRadius,
-                           xStart: xStart,
-                           yStart: yStart,
-                           xEnd: xEnd,
-                           yEnd: yEnd});
-
-  };
-
-  contextPrototype.rect = function(aX, aY, aWidth, aHeight) {
-    this.moveTo(aX, aY);
-    this.lineTo(aX + aWidth, aY);
-    this.lineTo(aX + aWidth, aY + aHeight);
-    this.lineTo(aX, aY + aHeight);
-    this.closePath();
-  };
-
-  contextPrototype.strokeRect = function(aX, aY, aWidth, aHeight) {
-    // Will destroy any existing path (same as FF behaviour)
-    this.beginPath();
-    this.moveTo(aX, aY);
-    this.lineTo(aX + aWidth, aY);
-    this.lineTo(aX + aWidth, aY + aHeight);
-    this.lineTo(aX, aY + aHeight);
-    this.closePath();
-    this.stroke();
-  };
-
-  contextPrototype.fillRect = function(aX, aY, aWidth, aHeight) {
-    // Will destroy any existing path (same as FF behaviour)
-    this.beginPath();
-    this.moveTo(aX, aY);
-    this.lineTo(aX + aWidth, aY);
-    this.lineTo(aX + aWidth, aY + aHeight);
-    this.lineTo(aX, aY + aHeight);
-    this.closePath();
-    this.fill();
-  };
-
-  contextPrototype.createLinearGradient = function(aX0, aY0, aX1, aY1) {
-    var gradient = new CanvasGradient_("gradient");
-    return gradient;
-  };
-
-  contextPrototype.createRadialGradient = function(aX0, aY0,
-                                                   aR0, aX1,
-                                                   aY1, aR1) {
-    var gradient = new CanvasGradient_("gradientradial");
-    gradient.radius1_ = aR0;
-    gradient.radius2_ = aR1;
-    gradient.focus_.x = aX0;
-    gradient.focus_.y = aY0;
-    return gradient;
-  };
-
-  contextPrototype.drawImage = function (image, var_args) {
-    var dx, dy, dw, dh, sx, sy, sw, sh;
-
-    // to find the original width we overide the width and height
-    var oldRuntimeWidth = image.runtimeStyle.width;
-    var oldRuntimeHeight = image.runtimeStyle.height;
-    image.runtimeStyle.width = 'auto';
-    image.runtimeStyle.height = 'auto';
-
-    // get the original size
-    var w = image.width;
-    var h = image.height;
-
-    // and remove overides
-    image.runtimeStyle.width = oldRuntimeWidth;
-    image.runtimeStyle.height = oldRuntimeHeight;
-
-    if (arguments.length == 3) {
-      dx = arguments[1];
-      dy = arguments[2];
-      sx = sy = 0;
-      sw = dw = w;
-      sh = dh = h;
-    } else if (arguments.length == 5) {
-      dx = arguments[1];
-      dy = arguments[2];
-      dw = arguments[3];
-      dh = arguments[4];
-      sx = sy = 0;
-      sw = w;
-      sh = h;
-    } else if (arguments.length == 9) {
-      sx = arguments[1];
-      sy = arguments[2];
-      sw = arguments[3];
-      sh = arguments[4];
-      dx = arguments[5];
-      dy = arguments[6];
-      dw = arguments[7];
-      dh = arguments[8];
-    } else {
-      throw "Invalid number of arguments";
-    }
-
-    var d = this.getCoords_(dx, dy);
-
-    var w2 = sw / 2;
-    var h2 = sh / 2;
-
-    var vmlStr = [];
-
-    var W = 10;
-    var H = 10;
-
-    // For some reason that I've now forgotten, using divs didn't work
-    vmlStr.push(' <g_vml_:group',
-                ' coordsize="', Z * W, ',', Z * H, '"',
-                ' coordorigin="0,0"' ,
-                ' style="width:', W, ';height:', H, ';position:absolute;');
-
-    // If filters are necessary (rotation exists), create them
-    // filters are bog-slow, so only create them if abbsolutely necessary
-    // The following check doesn't account for skews (which don't exist
-    // in the canvas spec (yet) anyway.
-
-    if (this.m_[0][0] != 1 || this.m_[0][1]) {
-      var filter = [];
-
-      // Note the 12/21 reversal
-      filter.push("M11='", this.m_[0][0], "',",
-                  "M12='", this.m_[1][0], "',",
-                  "M21='", this.m_[0][1], "',",
-                  "M22='", this.m_[1][1], "',",
-                  "Dx='", mr(d.x / Z), "',",
-                  "Dy='", mr(d.y / Z), "'");
-
-      // Bounding box calculation (need to minimize displayed area so that
-      // filters don't waste time on unused pixels.
-      var max = d;
-      var c2 = this.getCoords_(dx + dw, dy);
-      var c3 = this.getCoords_(dx, dy + dh);
-      var c4 = this.getCoords_(dx + dw, dy + dh);
-
-      max.x = Math.max(max.x, c2.x, c3.x, c4.x);
-      max.y = Math.max(max.y, c2.y, c3.y, c4.y);
-
-      vmlStr.push("padding:0 ", mr(max.x / Z), "px ", mr(max.y / Z),
-                  "px 0;filter:progid:DXImageTransform.Microsoft.Matrix(",
-                  filter.join(""), ", sizingmethod='clip');");
-    } else {
-      vmlStr.push("top:", mr(d.y / Z), "px;left:", mr(d.x / Z), "px;");
-    }
-
-    vmlStr.push(' ">' ,
-                '<g_vml_:image src="', image.src, '"',
-                ' style="width:', Z * dw, ';',
-                ' height:', Z * dh, ';"',
-                ' cropleft="', sx / w, '"',
-                ' croptop="', sy / h, '"',
-                ' cropright="', (w - sx - sw) / w, '"',
-                ' cropbottom="', (h - sy - sh) / h, '"',
-                ' />',
-                '</g_vml_:group>');
-
-    this.element_.insertAdjacentHTML("BeforeEnd",
-                                    vmlStr.join(""));
-  };
-
-  contextPrototype.stroke = function(aFill) {
-    var lineStr = [];
-    var lineOpen = false;
-    var a = processStyle(aFill ? this.fillStyle : this.strokeStyle);
-    var color = a[0];
-    var opacity = a[1] * this.globalAlpha;
-
-    var W = 10;
-    var H = 10;
-
-    lineStr.push('<g_vml_:shape',
-                 ' fillcolor="', color, '"',
-                 ' filled="', Boolean(aFill), '"',
-                 ' style="position:absolute;width:', W, ';height:', H, ';"',
-                 ' coordorigin="0 0" coordsize="', Z * W, ' ', Z * H, '"',
-                 ' stroked="', !aFill, '"',
-                 ' strokeweight="', this.lineWidth, '"',
-                 ' strokecolor="', color, '"',
-                 ' path="');
-
-    var newSeq = false;
-    var min = {x: null, y: null};
-    var max = {x: null, y: null};
-
-    for (var i = 0; i < this.currentPath_.length; i++) {
-      var p = this.currentPath_[i];
-
-      if (p.type == "moveTo") {
-        lineStr.push(" m ");
-        var c = this.getCoords_(p.x, p.y);
-        lineStr.push(mr(c.x), ",", mr(c.y));
-      } else if (p.type == "lineTo") {
-        lineStr.push(" l ");
-        var c = this.getCoords_(p.x, p.y);
-        lineStr.push(mr(c.x), ",", mr(c.y));
-      } else if (p.type == "close") {
-        lineStr.push(" x ");
-      } else if (p.type == "bezierCurveTo") {
-        lineStr.push(" c ");
-        var c = this.getCoords_(p.x, p.y);
-        var c1 = this.getCoords_(p.cp1x, p.cp1y);
-        var c2 = this.getCoords_(p.cp2x, p.cp2y);
-        lineStr.push(mr(c1.x), ",", mr(c1.y), ",",
-                     mr(c2.x), ",", mr(c2.y), ",",
-                     mr(c.x), ",", mr(c.y));
-      } else if (p.type == "at" || p.type == "wa") {
-        lineStr.push(" ", p.type, " ");
-        var c  = this.getCoords_(p.x, p.y);
-        var cStart = this.getCoords_(p.xStart, p.yStart);
-        var cEnd = this.getCoords_(p.xEnd, p.yEnd);
-
-        lineStr.push(mr(c.x - this.arcScaleX_ * p.radius), ",",
-                     mr(c.y - this.arcScaleY_ * p.radius), " ",
-                     mr(c.x + this.arcScaleX_ * p.radius), ",",
-                     mr(c.y + this.arcScaleY_ * p.radius), " ",
-                     mr(cStart.x), ",", mr(cStart.y), " ",
-                     mr(cEnd.x), ",", mr(cEnd.y));
-      }
-
-
-      // TODO: Following is broken for curves due to
-      //       move to proper paths.
-
-      // Figure out dimensions so we can do gradient fills
-      // properly
-      if(c) {
-        if (min.x == null || c.x < min.x) {
-          min.x = c.x;
-        }
-        if (max.x == null || c.x > max.x) {
-          max.x = c.x;
-        }
-        if (min.y == null || c.y < min.y) {
-          min.y = c.y;
-        }
-        if (max.y == null || c.y > max.y) {
-          max.y = c.y;
-        }
-      }
-    }
-    lineStr.push(' ">');
-
-    if (typeof this.fillStyle == "object") {
-      var focus = {x: "50%", y: "50%"};
-      var width = (max.x - min.x);
-      var height = (max.y - min.y);
-      var dimension = (width > height) ? width : height;
-
-      focus.x = mr((this.fillStyle.focus_.x / width) * 100 + 50) + "%";
-      focus.y = mr((this.fillStyle.focus_.y / height) * 100 + 50) + "%";
-
-      var colors = [];
-
-      // inside radius (%)
-      if (this.fillStyle.type_ == "gradientradial") {
-        var inside = (this.fillStyle.radius1_ / dimension * 100);
-
-        // percentage that outside radius exceeds inside radius
-        var expansion = (this.fillStyle.radius2_ / dimension * 100) - inside;
-      } else {
-        var inside = 0;
-        var expansion = 100;
-      }
-
-      var insidecolor = {offset: null, color: null};
-      var outsidecolor = {offset: null, color: null};
-
-      // We need to sort 'colors' by percentage, from 0 > 100 otherwise ie
-      // won't interpret it correctly
-      this.fillStyle.colors_.sort(function (cs1, cs2) {
-        return cs1.offset - cs2.offset;
-      });
-
-      for (var i = 0; i < this.fillStyle.colors_.length; i++) {
-        var fs = this.fillStyle.colors_[i];
-
-        colors.push( (fs.offset * expansion) + inside, "% ", fs.color, ",");
-
-        if (fs.offset > insidecolor.offset || insidecolor.offset == null) {
-          insidecolor.offset = fs.offset;
-          insidecolor.color = fs.color;
-        }
-
-        if (fs.offset < outsidecolor.offset || outsidecolor.offset == null) {
-          outsidecolor.offset = fs.offset;
-          outsidecolor.color = fs.color;
-        }
-      }
-      colors.pop();
-
-      lineStr.push('<g_vml_:fill',
-                   ' color="', outsidecolor.color, '"',
-                   ' color2="', insidecolor.color, '"',
-                   ' type="', this.fillStyle.type_, '"',
-                   ' focusposition="', focus.x, ', ', focus.y, '"',
-                   ' colors="', colors.join(""), '"',
-                   ' opacity="', opacity, '" />');
-    } else if (aFill) {
-      lineStr.push('<g_vml_:fill color="', color, '" opacity="', opacity, '" />');
-    } else {
-      lineStr.push(
-        '<g_vml_:stroke',
-        ' opacity="', opacity,'"',
-        ' joinstyle="', this.lineJoin, '"',
-        ' miterlimit="', this.miterLimit, '"',
-        ' endcap="', processLineCap(this.lineCap) ,'"',
-        ' weight="', this.lineWidth, 'px"',
-        ' color="', color,'" />'
-      );
-    }
-
-    lineStr.push("</g_vml_:shape>");
-
-    this.element_.insertAdjacentHTML("beforeEnd", lineStr.join(""));
-
-    //this.currentPath_ = [];
-  };
-
-  contextPrototype.fill = function() {
-    this.stroke(true);
-  };
-
-  contextPrototype.closePath = function() {
-    this.currentPath_.push({type: "close"});
-  };
-
-  /**
-   * @private
-   */
-  contextPrototype.getCoords_ = function(aX, aY) {
-    return {
-      x: Z * (aX * this.m_[0][0] + aY * this.m_[1][0] + this.m_[2][0]) - Z2,
-      y: Z * (aX * this.m_[0][1] + aY * this.m_[1][1] + this.m_[2][1]) - Z2
-    }
-  };
-
-  contextPrototype.save = function() {
-    var o = {};
-    copyState(this, o);
-    this.aStack_.push(o);
-    this.mStack_.push(this.m_);
-    this.m_ = matrixMultiply(createMatrixIdentity(), this.m_);
-  };
-
-  contextPrototype.restore = function() {
-    copyState(this.aStack_.pop(), this);
-    this.m_ = this.mStack_.pop();
-  };
-
-  contextPrototype.translate = function(aX, aY) {
-    var m1 = [
-      [1,  0,  0],
-      [0,  1,  0],
-      [aX, aY, 1]
-    ];
-
-    this.m_ = matrixMultiply(m1, this.m_);
-  };
-
-  contextPrototype.rotate = function(aRot) {
-    var c = mc(aRot);
-    var s = ms(aRot);
-
-    var m1 = [
-      [c,  s, 0],
-      [-s, c, 0],
-      [0,  0, 1]
-    ];
-
-    this.m_ = matrixMultiply(m1, this.m_);
-  };
-
-  contextPrototype.scale = function(aX, aY) {
-    this.arcScaleX_ *= aX;
-    this.arcScaleY_ *= aY;
-    var m1 = [
-      [aX, 0,  0],
-      [0,  aY, 0],
-      [0,  0,  1]
-    ];
-
-    this.m_ = matrixMultiply(m1, this.m_);
-  };
-
-  /******** STUBS ********/
-  contextPrototype.clip = function() {
-    // TODO: Implement
-  };
-
-  contextPrototype.arcTo = function() {
-    // TODO: Implement
-  };
-
-  contextPrototype.createPattern = function() {
-    return new CanvasPattern_;
-  };
-
-  // Gradient / Pattern Stubs
-  function CanvasGradient_(aType) {
-    this.type_ = aType;
-    this.radius1_ = 0;
-    this.radius2_ = 0;
-    this.colors_ = [];
-    this.focus_ = {x: 0, y: 0};
-  }
-
-  CanvasGradient_.prototype.addColorStop = function(aOffset, aColor) {
-    aColor = processStyle(aColor);
-    this.colors_.push({offset: 1-aOffset, color: aColor});
-  };
-
-  function CanvasPattern_() {}
-
-  // set up externs
-  G_vmlCanvasManager = G_vmlCanvasManager_;
-  CanvasRenderingContext2D = CanvasRenderingContext2D_;
-  CanvasGradient = CanvasGradient_;
-  CanvasPattern = CanvasPattern_;
-
-})();
-
-} // if
deleted file mode 100644
--- a/bitten/htdocs/jquery.flot.js
+++ /dev/null
@@ -1,2136 +0,0 @@
-/* Javascript plotting library for jQuery, v. 0.5.
- *
- * Released under the MIT license by IOLA, December 2007.
- *
- */
-
-(function($) {
-    function Plot(target_, data_, options_) {
-        // data is on the form:
-        //   [ series1, series2 ... ]
-        // where series is either just the data as [ [x1, y1], [x2, y2], ... ]
-        // or { data: [ [x1, y1], [x2, y2], ... ], label: "some label" }
-        
-        var series = [],
-            options = {
-            // the color theme used for graphs
-            colors: ["#edc240", "#afd8f8", "#cb4b4b", "#4da74d", "#9440ed"],
-                legend: {
-                    show: true,
-                    noColumns: 1, // number of colums in legend table
-                    labelFormatter: null, // fn: string -> string
-                    labelBoxBorderColor: "#ccc", // border color for the little label boxes
-                    container: null, // container (as jQuery object) to put legend in, null means default on top of graph
-                    position: "ne", // position of default legend container within plot
-                    margin: 5, // distance from grid edge to default legend container within plot
-                    backgroundColor: null, // null means auto-detect
-                    backgroundOpacity: 0.85 // set to 0 to avoid background
-                },
-                xaxis: {
-                    mode: null, // null or "time"
-                    min: null, // min. value to show, null means set automatically
-                    max: null, // max. value to show, null means set automatically
-                    autoscaleMargin: null, // margin in % to add if auto-setting min/max
-                    ticks: null, // either [1, 3] or [[1, "a"], 3] or (fn: axis info -> ticks) or app. number of ticks for auto-ticks
-                    tickFormatter: null, // fn: number -> string
-                    labelWidth: null, // size of tick labels in pixels
-                    labelHeight: null,
-                    
-                    // mode specific options
-                    tickDecimals: null, // no. of decimals, null means auto
-                    tickSize: null, // number or [number, "unit"]
-                    minTickSize: null, // number or [number, "unit"]
-                    monthNames: null, // list of names of months
-                    timeformat: null // format string to use
-                },
-                yaxis: {
-                    autoscaleMargin: 0.02
-                },
-                x2axis: {
-                    autoscaleMargin: null
-                },
-                y2axis: {
-                    autoscaleMargin: 0.02
-                },              
-                points: {
-                    show: false,
-                    radius: 3,
-                    lineWidth: 2, // in pixels
-                    fill: true,
-                    fillColor: "#ffffff"
-                },
-                lines: {
-                    show: false,
-                    lineWidth: 2, // in pixels
-                    fill: false,
-                    fillColor: null
-                },
-                bars: {
-                    show: false,
-                    lineWidth: 2, // in pixels
-                    barWidth: 1, // in units of the x axis
-                    fill: true,
-                    fillColor: null,
-                    align: "left" // or "center"
-                },
-                grid: {
-                    color: "#545454", // primary color used for outline and labels
-                    backgroundColor: null, // null for transparent, else color
-                    tickColor: "#dddddd", // color used for the ticks
-                    labelMargin: 5, // in pixels
-                    borderWidth: 2,
-                    markings: null, // array of ranges or fn: axes -> array of ranges
-                    markingsColor: "#f4f4f4",
-                    markingsLineWidth: 2,
-                    // interactive stuff
-                    clickable: false,
-                    hoverable: false,
-                    autoHighlight: true, // highlight in case mouse is near
-                    mouseActiveRadius: 10 // how far the mouse can be away to activate an item
-                },
-                selection: {
-                    mode: null, // one of null, "x", "y" or "xy"
-                    color: "#e8cfac"
-                },
-                shadowSize: 4
-            },
-        canvas = null,      // the canvas for the plot itself
-        overlay = null,     // canvas for interactive stuff on top of plot
-        eventHolder = null, // jQuery object that events should be bound to
-        ctx = null, octx = null,
-        target = target_,
-        axes = { xaxis: {}, yaxis: {}, x2axis: {}, y2axis: {} },
-        plotOffset = { left: 0, right: 0, top: 0, bottom: 0},
-        canvasWidth = 0, canvasHeight = 0,
-        plotWidth = 0, plotHeight = 0,
-        // dedicated to storing data for buggy standard compliance cases
-        workarounds = {};
-        
-        this.setData = setData;
-        this.setupGrid = setupGrid;
-        this.draw = draw;
-        this.clearSelection = clearSelection;
-        this.setSelection = setSelection;
-        this.getCanvas = function() { return canvas; };
-        this.getPlotOffset = function() { return plotOffset; };
-        this.getData = function() { return series; };
-        this.getAxes = function() { return axes; };
-        this.highlight = highlight;
-        this.unhighlight = unhighlight;
-        
-        // initialize
-        parseOptions(options_);
-        setData(data_);
-        constructCanvas();
-        setupGrid();
-        draw();
-
-
-        function setData(d) {
-            series = parseData(d);
-
-            fillInSeriesOptions();
-            processData();
-        }
-        
-        function parseData(d) {
-            var res = [];
-            for (var i = 0; i < d.length; ++i) {
-                var s;
-                if (d[i].data) {
-                    s = {};
-                    for (var v in d[i])
-                        s[v] = d[i][v];
-                }
-                else {
-                    s = { data: d[i] };
-                }
-                res.push(s);
-            }
-
-            return res;
-        }
-        
-        function parseOptions(o) {
-            $.extend(true, options, o);
-
-            // backwards compatibility, to be removed in future
-            if (options.xaxis.noTicks && options.xaxis.ticks == null)
-                options.xaxis.ticks = options.xaxis.noTicks;
-            if (options.yaxis.noTicks && options.yaxis.ticks == null)
-                options.yaxis.ticks = options.yaxis.noTicks;
-            if (options.grid.coloredAreas)
-                options.grid.markings = options.grid.coloredAreas;
-            if (options.grid.coloredAreasColor)
-                options.grid.markingsColor = options.grid.coloredAreasColor;
-        }
-
-        function fillInSeriesOptions() {
-            var i;
-            
-            // collect what we already got of colors
-            var neededColors = series.length,
-                usedColors = [],
-                assignedColors = [];
-            for (i = 0; i < series.length; ++i) {
-                var sc = series[i].color;
-                if (sc != null) {
-                    --neededColors;
-                    if (typeof sc == "number")
-                        assignedColors.push(sc);
-                    else
-                        usedColors.push(parseColor(series[i].color));
-                }
-            }
-            
-            // we might need to generate more colors if higher indices
-            // are assigned
-            for (i = 0; i < assignedColors.length; ++i) {
-                neededColors = Math.max(neededColors, assignedColors[i] + 1);
-            }
-
-            // produce colors as needed
-            var colors = [], variation = 0;
-            i = 0;
-            while (colors.length < neededColors) {
-                var c;
-                if (options.colors.length == i) // check degenerate case
-                    c = new Color(100, 100, 100);
-                else
-                    c = parseColor(options.colors[i]);
-
-                // vary color if needed
-                var sign = variation % 2 == 1 ? -1 : 1;
-                var factor = 1 + sign * Math.ceil(variation / 2) * 0.2;
-                c.scale(factor, factor, factor);
-
-                // FIXME: if we're getting to close to something else,
-                // we should probably skip this one
-                colors.push(c);
-                
-                ++i;
-                if (i >= options.colors.length) {
-                    i = 0;
-                    ++variation;
-                }
-            }
-
-            // fill in the options
-            var colori = 0, s;
-            for (i = 0; i < series.length; ++i) {
-                s = series[i];
-
-                // assign colors
-                if (s.color == null) {
-                    s.color = colors[colori].toString();
-                    ++colori;
-                }
-                else if (typeof s.color == "number")
-                    s.color = colors[s.color].toString();
-
-                // copy the rest
-                s.lines = $.extend(true, {}, options.lines, s.lines);
-                s.points = $.extend(true, {}, options.points, s.points);
-                s.bars = $.extend(true, {}, options.bars, s.bars);
-                if (s.shadowSize == null)
-                    s.shadowSize = options.shadowSize;
-                if (s.xaxis && s.xaxis == 2)
-                    s.xaxis = axes.x2axis;
-                else
-                    s.xaxis = axes.xaxis;
-                if (s.yaxis && s.yaxis == 2)
-                    s.yaxis = axes.y2axis;
-                else
-                    s.yaxis = axes.yaxis;
-            }
-        }
-        
-        function processData() {
-            var topSentry = Number.POSITIVE_INFINITY,
-                bottomSentry = Number.NEGATIVE_INFINITY,
-                axis;
-
-            for (axis in axes) {
-                axes[axis].datamin = topSentry;
-                axes[axis].datamax = bottomSentry;
-                axes[axis].used = false;
-            }
-            
-            for (var i = 0; i < series.length; ++i) {
-                var data = series[i].data,
-                    axisx = series[i].xaxis,
-                    axisy = series[i].yaxis,
-                    mindelta = 0, maxdelta = 0;
-                
-                // make sure we got room for the bar
-                if (series[i].bars.show) {
-                    mindelta = series[i].bars.align == "left" ? 0 : -series[i].bars.barWidth/2;
-                    maxdelta = mindelta + series[i].bars.barWidth;
-                }
-                
-                axisx.used = axisy.used = true;
-                for (var j = 0; j < data.length; ++j) {
-                    if (data[j] == null)
-                        continue;
-                    
-                    var x = data[j][0], y = data[j][1];
-
-                    // convert to number
-                    if (x != null && !isNaN(x = +x)) {
-                        if (x + mindelta < axisx.datamin)
-                            axisx.datamin = x + mindelta;
-                        if (x + maxdelta > axisx.datamax)
-                            axisx.datamax = x + maxdelta;
-                    }
-                    
-                    if (y != null && !isNaN(y = +y)) {
-                        if (y < axisy.datamin)
-                            axisy.datamin = y;
-                        if (y > axisy.datamax)
-                            axisy.datamax = y;
-                    }
-                    
-                    if (x == null || y == null || isNaN(x) || isNaN(y))
-                        data[j] = null; // mark this point as invalid
-                }
-            }
-
-            for (axis in axes) {
-                if (axes[axis].datamin == topSentry)
-                    axes[axis].datamin = 0;
-                if (axes[axis].datamax == bottomSentry)
-                    axes[axis].datamax = 1;
-            }
-        }
-
-        function constructCanvas() {
-            canvasWidth = target.width();
-            canvasHeight = target.height();
-            target.html(""); // clear target
-            target.css("position", "relative"); // for positioning labels and overlay
-
-            if (canvasWidth <= 0 || canvasHeight <= 0)
-                throw "Invalid dimensions for plot, width = " + canvasWidth + ", height = " + canvasHeight;
-
-            // the canvas
-            canvas = $('<canvas width="' + canvasWidth + '" height="' + canvasHeight + '"></canvas>').appendTo(target).get(0);
-            if ($.browser.msie) // excanvas hack
-                canvas = window.G_vmlCanvasManager.initElement(canvas);
-            ctx = canvas.getContext("2d");
-
-            // overlay canvas for interactive features
-            overlay = $('<canvas style="position:absolute;left:0px;top:0px;" width="' + canvasWidth + '" height="' + canvasHeight + '"></canvas>').appendTo(target).get(0);
-            if ($.browser.msie) // excanvas hack
-                overlay = window.G_vmlCanvasManager.initElement(overlay);
-            octx = overlay.getContext("2d");
-
-            // we include the canvas in the event holder too, because IE 7
-            // sometimes has trouble with the stacking order
-            eventHolder = $([overlay, canvas]);
-
-            // bind events
-            if (options.selection.mode != null || options.grid.hoverable) {
-                // FIXME: temp. work-around until jQuery bug 1871 is fixed
-                eventHolder.each(function () {
-                    this.onmousemove = onMouseMove;
-                });
-
-                if (options.selection.mode != null)
-                    eventHolder.mousedown(onMouseDown);
-            }
-
-            if (options.grid.clickable)
-                eventHolder.click(onClick);
-        }
-
-        function setupGrid() {
-            function setupAxis(axis, options) {
-                setRange(axis, options);
-                prepareTickGeneration(axis, options);
-                setTicks(axis, options);
-                // add transformation helpers
-                if (axis == axes.xaxis || axis == axes.x2axis) {
-                    // data point to canvas coordinate
-                    axis.p2c = function (p) { return (p - axis.min) * axis.scale; };
-                    // canvas coordinate to data point 
-                    axis.c2p = function (c) { return axis.min + c / axis.scale; };
-                }
-                else {
-                    axis.p2c = function (p) { return (axis.max - p) * axis.scale; };
-                    axis.c2p = function (p) { return axis.max - p / axis.scale; };
-                }
-            }
-
-            for (var axis in axes)
-                setupAxis(axes[axis], options[axis]);
-
-            setSpacing();
-            insertLabels();
-            insertLegend();
-        }
-        
-        function setRange(axis, axisOptions) {
-            var min = axisOptions.min != null ? axisOptions.min : axis.datamin;
-            var max = axisOptions.max != null ? axisOptions.max : axis.datamax;
-
-            if (max - min == 0.0) {
-                // degenerate case
-                var widen;
-                if (max == 0.0)
-                    widen = 1.0;
-                else
-                    widen = 0.01;
-
-                min -= widen;
-                max += widen;
-            }
-            else {
-                // consider autoscaling
-                var margin = axisOptions.autoscaleMargin;
-                if (margin != null) {
-                    if (axisOptions.min == null) {
-                        min -= (max - min) * margin;
-                        // make sure we don't go below zero if all values
-                        // are positive
-                        if (min < 0 && axis.datamin >= 0)
-                            min = 0;
-                    }
-                    if (axisOptions.max == null) {
-                        max += (max - min) * margin;
-                        if (max > 0 && axis.datamax <= 0)
-                            max = 0;
-                    }
-                }
-            }
-            axis.min = min;
-            axis.max = max;
-        }
-
-        function prepareTickGeneration(axis, axisOptions) {
-            // estimate number of ticks
-            var noTicks;
-            if (typeof axisOptions.ticks == "number" && axisOptions.ticks > 0)
-                noTicks = axisOptions.ticks;
-            else if (axis == axes.xaxis || axis == axes.x2axis)
-                noTicks = canvasWidth / 100;
-            else
-                noTicks = canvasHeight / 60;
-            
-            var delta = (axis.max - axis.min) / noTicks;
-            var size, generator, unit, formatter, i, magn, norm;
-
-            if (axisOptions.mode == "time") {
-                // pretty handling of time
-                
-                function formatDate(d, fmt, monthNames) {
-                    var leftPad = function(n) {
-                        n = "" + n;
-                        return n.length == 1 ? "0" + n : n;
-                    };
-                    
-                    var r = [];
-                    var escape = false;
-                    if (monthNames == null)
-                        monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
-                    for (var i = 0; i < fmt.length; ++i) {
-                        var c = fmt.charAt(i);
-                        
-                        if (escape) {
-                            switch (c) {
-                            case 'h': c = "" + d.getUTCHours(); break;
-                            case 'H': c = leftPad(d.getUTCHours()); break;
-                            case 'M': c = leftPad(d.getUTCMinutes()); break;
-                            case 'S': c = leftPad(d.getUTCSeconds()); break;
-                            case 'd': c = "" + d.getUTCDate(); break;
-                            case 'm': c = "" + (d.getUTCMonth() + 1); break;
-                            case 'y': c = "" + d.getUTCFullYear(); break;
-                            case 'b': c = "" + monthNames[d.getUTCMonth()]; break;
-                            }
-                            r.push(c);
-                            escape = false;
-                        }
-                        else {
-                            if (c == "%")
-                                escape = true;
-                            else
-                                r.push(c);
-                        }
-                    }
-                    return r.join("");
-                }
-                
-                    
-                // map of app. size of time units in milliseconds
-                var timeUnitSize = {
-                    "second": 1000,
-                    "minute": 60 * 1000,
-                    "hour": 60 * 60 * 1000,
-                    "day": 24 * 60 * 60 * 1000,
-                    "month": 30 * 24 * 60 * 60 * 1000,
-                    "year": 365.2425 * 24 * 60 * 60 * 1000
-                };
-
-
-                // the allowed tick sizes, after 1 year we use
-                // an integer algorithm
-                var spec = [
-                    [1, "second"], [2, "second"], [5, "second"], [10, "second"],
-                    [30, "second"], 
-                    [1, "minute"], [2, "minute"], [5, "minute"], [10, "minute"],
-                    [30, "minute"], 
-                    [1, "hour"], [2, "hour"], [4, "hour"],
-                    [8, "hour"], [12, "hour"],
-                    [1, "day"], [2, "day"], [3, "day"],
-                    [0.25, "month"], [0.5, "month"], [1, "month"],
-                    [2, "month"], [3, "month"], [6, "month"],
-                    [1, "year"]
-                ];
-
-                var minSize = 0;
-                if (axisOptions.minTickSize != null) {
-                    if (typeof axisOptions.tickSize == "number")
-                        minSize = axisOptions.tickSize;
-                    else
-                        minSize = axisOptions.minTickSize[0] * timeUnitSize[axisOptions.minTickSize[1]];
-                }
-
-                for (i = 0; i < spec.length - 1; ++i)
-                    if (delta < (spec[i][0] * timeUnitSize[spec[i][1]]
-                                 + spec[i + 1][0] * timeUnitSize[spec[i + 1][1]]) / 2
-                       && spec[i][0] * timeUnitSize[spec[i][1]] >= minSize)
-                        break;
-                size = spec[i][0];
-                unit = spec[i][1];
-                
-                // special-case the possibility of several years
-                if (unit == "year") {
-                    magn = Math.pow(10, Math.floor(Math.log(delta / timeUnitSize.year) / Math.LN10));
-                    norm = (delta / timeUnitSize.year) / magn;
-                    if (norm < 1.5)
-                        size = 1;
-                    else if (norm < 3)
-                        size = 2;
-                    else if (norm < 7.5)
-                        size = 5;
-                    else
-                        size = 10;
-
-                    size *= magn;
-                }
-
-                if (axisOptions.tickSize) {
-                    size = axisOptions.tickSize[0];
-                    unit = axisOptions.tickSize[1];
-                }
-                
-                generator = function(axis) {
-                    var ticks = [],
-                        tickSize = axis.tickSize[0], unit = axis.tickSize[1],
-                        d = new Date(axis.min);
-                    
-                    var step = tickSize * timeUnitSize[unit];
-
-                    if (unit == "second")
-                        d.setUTCSeconds(floorInBase(d.getUTCSeconds(), tickSize));
-                    if (unit == "minute")
-                        d.setUTCMinutes(floorInBase(d.getUTCMinutes(), tickSize));
-                    if (unit == "hour")
-                        d.setUTCHours(floorInBase(d.getUTCHours(), tickSize));
-                    if (unit == "month")
-                        d.setUTCMonth(floorInBase(d.getUTCMonth(), tickSize));
-                    if (unit == "year")
-                        d.setUTCFullYear(floorInBase(d.getUTCFullYear(), tickSize));
-                    
-                    // reset smaller components
-                    d.setUTCMilliseconds(0);
-                    if (step >= timeUnitSize.minute)
-                        d.setUTCSeconds(0);
-                    if (step >= timeUnitSize.hour)
-                        d.setUTCMinutes(0);
-                    if (step >= timeUnitSize.day)
-                        d.setUTCHours(0);
-                    if (step >= timeUnitSize.day * 4)
-                        d.setUTCDate(1);
-                    if (step >= timeUnitSize.year)
-                        d.setUTCMonth(0);
-
-
-                    var carry = 0, v = Number.NaN, prev;
-                    do {
-                        prev = v;
-                        v = d.getTime();
-                        ticks.push({ v: v, label: axis.tickFormatter(v, axis) });
-                        if (unit == "month") {
-                            if (tickSize < 1) {
-                                // a bit complicated - we'll divide the month
-                                // up but we need to take care of fractions
-                                // so we don't end up in the middle of a day
-                                d.setUTCDate(1);
-                                var start = d.getTime();
-                                d.setUTCMonth(d.getUTCMonth() + 1);
-                                var end = d.getTime();
-                                d.setTime(v + carry * timeUnitSize.hour + (end - start) * tickSize);
-                                carry = d.getUTCHours();
-                                d.setUTCHours(0);
-                            }
-                            else
-                                d.setUTCMonth(d.getUTCMonth() + tickSize);
-                        }
-                        else if (unit == "year") {
-                            d.setUTCFullYear(d.getUTCFullYear() + tickSize);
-                        }
-                        else
-                            d.setTime(v + step);
-                    } while (v < axis.max && v != prev);
-
-                    return ticks;
-                };
-
-                formatter = function (v, axis) {
-                    var d = new Date(v);
-
-                    // first check global format
-                    if (axisOptions.timeformat != null)
-                        return formatDate(d, axisOptions.timeformat, axisOptions.monthNames);
-                    
-                    var t = axis.tickSize[0] * timeUnitSize[axis.tickSize[1]];
-                    var span = axis.max - axis.min;
-                    
-                    if (t < timeUnitSize.minute)
-                        fmt = "%h:%M:%S";
-                    else if (t < timeUnitSize.day) {
-                        if (span < 2 * timeUnitSize.day)
-                            fmt = "%h:%M";
-                        else
-                            fmt = "%b %d %h:%M";
-                    }
-                    else if (t < timeUnitSize.month)
-                        fmt = "%b %d";
-                    else if (t < timeUnitSize.year) {
-                        if (span < timeUnitSize.year)
-                            fmt = "%b";
-                        else
-                            fmt = "%b %y";
-                    }
-                    else
-                        fmt = "%y";
-                    
-                    return formatDate(d, fmt, axisOptions.monthNames);
-                };
-            }
-            else {
-                // pretty rounding of base-10 numbers
-                var maxDec = axisOptions.tickDecimals;
-                var dec = -Math.floor(Math.log(delta) / Math.LN10);
-                if (maxDec != null && dec > maxDec)
-                    dec = maxDec;
-                
-                magn = Math.pow(10, -dec);
-                norm = delta / magn; // norm is between 1.0 and 10.0
-                
-                if (norm < 1.5)
-                    size = 1;
-                else if (norm < 3) {
-                    size = 2;
-                    // special case for 2.5, requires an extra decimal
-                    if (norm > 2.25 && (maxDec == null || dec + 1 <= maxDec)) {
-                        size = 2.5;
-                        ++dec;
-                    }
-                }
-                else if (norm < 7.5)
-                    size = 5;
-                else
-                    size = 10;
-
-                size *= magn;
-                
-                if (axisOptions.minTickSize != null && size < axisOptions.minTickSize)
-                    size = axisOptions.minTickSize;
-
-                if (axisOptions.tickSize != null)
-                    size = axisOptions.tickSize;
-                
-                axis.tickDecimals = Math.max(0, (maxDec != null) ? maxDec : dec);
-                
-                generator = function (axis) {
-                    var ticks = [];
-
-                    // spew out all possible ticks
-                    var start = floorInBase(axis.min, axis.tickSize),
-                        i = 0, v = Number.NaN, prev;
-                    do {
-                        prev = v;
-                        v = start + i * axis.tickSize;
-                        ticks.push({ v: v, label: axis.tickFormatter(v, axis) });
-                        ++i;
-                    } while (v < axis.max && v != prev);
-                    return ticks;
-                };
-
-                formatter = function (v, axis) {
-                    return v.toFixed(axis.tickDecimals);
-                };
-            }
-
-            axis.tickSize = unit ? [size, unit] : size;
-            axis.tickGenerator = generator;
-            if ($.isFunction(axisOptions.tickFormatter))
-                axis.tickFormatter = function (v, axis) { return "" + axisOptions.tickFormatter(v, axis); };
-            else
-                axis.tickFormatter = formatter;
-            if (axisOptions.labelWidth != null)
-                axis.labelWidth = axisOptions.labelWidth;
-            if (axisOptions.labelHeight != null)
-                axis.labelHeight = axisOptions.labelHeight;
-        }
-        
-        function setTicks(axis, axisOptions) {
-            axis.ticks = [];
-
-            if (!axis.used)
-                return;
-            
-            if (axisOptions.ticks == null)
-                axis.ticks = axis.tickGenerator(axis);
-            else if (typeof axisOptions.ticks == "number") {
-                if (axisOptions.ticks > 0)
-                    axis.ticks = axis.tickGenerator(axis);
-            }
-            else if (axisOptions.ticks) {
-                var ticks = axisOptions.ticks;
-
-                if ($.isFunction(ticks))
-                    // generate the ticks
-                    ticks = ticks({ min: axis.min, max: axis.max });
-                
-                // clean up the user-supplied ticks, copy them over
-                var i, v;
-                for (i = 0; i < ticks.length; ++i) {
-                    var label = null;
-                    var t = ticks[i];
-                    if (typeof t == "object") {
-                        v = t[0];
-                        if (t.length > 1)
-                            label = t[1];
-                    }
-                    else
-                        v = t;
-                    if (label == null)
-                        label = axis.tickFormatter(v, axis);
-                    axis.ticks[i] = { v: v, label: label };
-                }
-            }
-
-            if (axisOptions.autoscaleMargin != null && axis.ticks.length > 0) {
-                // snap to ticks
-                if (axisOptions.min == null)
-                    axis.min = Math.min(axis.min, axis.ticks[0].v);
-                if (axisOptions.max == null && axis.ticks.length > 1)
-                    axis.max = Math.min(axis.max, axis.ticks[axis.ticks.length - 1].v);
-            }
-        }
-        
-        function setSpacing() {
-            function measureXLabels(axis) {
-                // to avoid measuring the widths of the labels, we
-                // construct fixed-size boxes and put the labels inside
-                // them, we don't need the exact figures and the
-                // fixed-size box content is easy to center
-                if (axis.labelWidth == null)
-                    axis.labelWidth = canvasWidth / 6;
-
-                // measure x label heights
-                if (axis.labelHeight == null) {
-                    labels = [];
-                    for (i = 0; i < axis.ticks.length; ++i) {
-                        l = axis.ticks[i].label;
-                        if (l)
-                            labels.push('<div class="tickLabel" style="float:left;width:' + axis.labelWidth + 'px">' + l + '</div>');
-                    }
-                    
-                    axis.labelHeight = 0;
-                    if (labels.length > 0) {
-                        var dummyDiv = $('<div style="position:absolute;top:-10000px;width:10000px;font-size:smaller">'
-                                         + labels.join("") + '<div style="clear:left"></div></div>').appendTo(target);
-                        axis.labelHeight = dummyDiv.height();
-                        dummyDiv.remove();
-                    }
-                }
-            }
-            
-            function measureYLabels(axis) {
-                if (axis.labelWidth == null || axis.labelHeight == null) {
-                    var i, labels = [], l;
-                    // calculate y label dimensions
-                    for (i = 0; i < axis.ticks.length; ++i) {
-                        l = axis.ticks[i].label;
-                        if (l)
-                            labels.push('<div class="tickLabel">' + l + '</div>');
-                    }
-                    
-                    if (labels.length > 0) {
-                        var dummyDiv = $('<div style="position:absolute;top:-10000px;font-size:smaller">'
-                                         + labels.join("") + '</div>').appendTo(target);
-                        if (axis.labelWidth == null)
-                            axis.labelWidth = dummyDiv.width();
-                        if (axis.labelHeight == null)
-                            axis.labelHeight = dummyDiv.find("div").height();
-                        dummyDiv.remove();
-                    }
-                    
-                    if (axis.labelWidth == null)
-                        axis.labelWidth = 0;
-                    if (axis.labelHeight == null)
-                        axis.labelHeight = 0;
-                }
-            }
-            
-            measureXLabels(axes.xaxis);
-            measureYLabels(axes.yaxis);
-            measureXLabels(axes.x2axis);
-            measureYLabels(axes.y2axis);
-
-            // get the most space needed around the grid for things
-            // that may stick out
-            var maxOutset = options.grid.borderWidth / 2;
-            for (i = 0; i < series.length; ++i)
-                maxOutset = Math.max(maxOutset, 2 * (series[i].points.radius + series[i].points.lineWidth/2));
-
-            plotOffset.left = plotOffset.right = plotOffset.top = plotOffset.bottom = maxOutset;
-
-            if (axes.xaxis.labelHeight > 0)
-                plotOffset.bottom = Math.max(maxOutset, axes.xaxis.labelHeight + options.grid.labelMargin);
-            if (axes.yaxis.labelWidth > 0)
-                plotOffset.left = Math.max(maxOutset, axes.yaxis.labelWidth + options.grid.labelMargin);
-
-            if (axes.x2axis.labelHeight > 0)
-                plotOffset.top = Math.max(maxOutset, axes.x2axis.labelHeight + options.grid.labelMargin);
-            
-            if (axes.y2axis.labelWidth > 0)
-                plotOffset.right = Math.max(maxOutset, axes.y2axis.labelWidth + options.grid.labelMargin);
-
-            plotWidth = canvasWidth - plotOffset.left - plotOffset.right;
-            plotHeight = canvasHeight - plotOffset.bottom - plotOffset.top;
-
-            // precompute how much the axis is scaling a point in canvas space
-            axes.xaxis.scale = plotWidth / (axes.xaxis.max - axes.xaxis.min);
-            axes.yaxis.scale = plotHeight / (axes.yaxis.max - axes.yaxis.min);
-            axes.x2axis.scale = plotWidth / (axes.x2axis.max - axes.x2axis.min);
-            axes.y2axis.scale = plotHeight / (axes.y2axis.max - axes.y2axis.min);
-        }
-        
-        function draw() {
-            drawGrid();
-            for (var i = 0; i < series.length; i++) {
-                drawSeries(series[i]);
-            }
-        }
-
-        function extractRange(ranges, coord) {
-            var firstAxis = coord + "axis",
-                secondaryAxis = coord + "2axis",
-                axis, from, to, reverse;
-
-            if (ranges[firstAxis]) {
-                axis = axes[firstAxis];
-                from = ranges[firstAxis].from;
-                to = ranges[firstAxis].to;
-            }
-            else if (ranges[secondaryAxis]) {
-                axis = axes[secondaryAxis];
-                from = ranges[secondaryAxis].from;
-                to = ranges[secondaryAxis].to;
-            }
-            else {
-                // backwards-compat stuff - to be removed in future
-                axis = axes[firstAxis];
-                from = ranges[coord + "1"];
-                to = ranges[coord + "2"];
-            }
-
-            // auto-reverse as an added bonus
-            if (from != null && to != null && from > to)
-                return { from: to, to: from, axis: axis };
-            
-            return { from: from, to: to, axis: axis };
-        }
-        
-        function drawGrid() {
-            var i;
-            
-            ctx.save();
-            ctx.clearRect(0, 0, canvasWidth, canvasHeight);
-            ctx.translate(plotOffset.left, plotOffset.top);
-
-            // draw background, if any
-            if (options.grid.backgroundColor) {
-                ctx.fillStyle = options.grid.backgroundColor;
-                ctx.fillRect(0, 0, plotWidth, plotHeight);
-            }
-
-            // draw markings
-            if (options.grid.markings) {
-                var markings = options.grid.markings;
-                if ($.isFunction(markings))
-                    // xmin etc. are backwards-compatible, to be removed in future
-                    markings = markings({ xmin: axes.xaxis.min, xmax: axes.xaxis.max, ymin: axes.yaxis.min, ymax: axes.yaxis.max, xaxis: axes.xaxis, yaxis: axes.yaxis, x2axis: axes.x2axis, y2axis: axes.y2axis });
-
-                for (i = 0; i < markings.length; ++i) {
-                    var m = markings[i],
-                        xrange = extractRange(m, "x"),
-                        yrange = extractRange(m, "y");
-
-                    // fill in missing
-                    if (xrange.from == null)
-                        xrange.from = xrange.axis.min;
-                    if (xrange.to == null)
-                        xrange.to = xrange.axis.max;
-                    if (yrange.from == null)
-                        yrange.from = yrange.axis.min;
-                    if (yrange.to == null)
-                        yrange.to = yrange.axis.max;
-
-                    // clip
-                    if (xrange.to < xrange.axis.min || xrange.from > xrange.axis.max ||
-                        yrange.to < yrange.axis.min || yrange.from > yrange.axis.max)
-                        continue;
-
-                    xrange.from = Math.max(xrange.from, xrange.axis.min);
-                    xrange.to = Math.min(xrange.to, xrange.axis.max);
-                    yrange.from = Math.max(yrange.from, yrange.axis.min);
-                    yrange.to = Math.min(yrange.to, yrange.axis.max);
-
-                    if (xrange.from == xrange.to && yrange.from == yrange.to)
-                        continue;
-
-                    // then draw
-                    xrange.from = xrange.axis.p2c(xrange.from);
-                    xrange.to = xrange.axis.p2c(xrange.to);
-                    yrange.from = yrange.axis.p2c(yrange.from);
-                    yrange.to = yrange.axis.p2c(yrange.to);
-                    
-                    if (xrange.from == xrange.to || yrange.from == yrange.to) {
-                        // draw line
-                        ctx.strokeStyle = m.color || options.grid.markingsColor;
-                        ctx.lineWidth = m.lineWidth || options.grid.markingsLineWidth;
-                        ctx.moveTo(Math.floor(xrange.from), Math.floor(yrange.from));
-                        ctx.lineTo(Math.floor(xrange.to), Math.floor(yrange.to));
-                        ctx.stroke();
-                    }
-                    else {
-                        // fill area
-                        ctx.fillStyle = m.color || options.grid.markingsColor;
-                        ctx.fillRect(Math.floor(xrange.from),
-                                     Math.floor(yrange.to),
-                                     Math.floor(xrange.to - xrange.from),
-                                     Math.floor(yrange.from - yrange.to));
-                    }
-                }
-            }
-            
-            // draw the inner grid
-            ctx.lineWidth = 1;
-            ctx.strokeStyle = options.grid.tickColor;
-            ctx.beginPath();
-            var v, axis = axes.xaxis;
-            for (i = 0; i < axis.ticks.length; ++i) {
-                v = axis.ticks[i].v;
-                if (v <= axis.min || v >= axes.xaxis.max)
-                    continue;   // skip those lying on the axes
-
-                ctx.moveTo(Math.floor(axis.p2c(v)) + ctx.lineWidth/2, 0);
-                ctx.lineTo(Math.floor(axis.p2c(v)) + ctx.lineWidth/2, plotHeight);
-            }
-
-            axis = axes.yaxis;
-            for (i = 0; i < axis.ticks.length; ++i) {
-                v = axis.ticks[i].v;
-                if (v <= axis.min || v >= axis.max)
-                    continue;
-
-                ctx.moveTo(0, Math.floor(axis.p2c(v)) + ctx.lineWidth/2);
-                ctx.lineTo(plotWidth, Math.floor(axis.p2c(v)) + ctx.lineWidth/2);
-            }
-
-            axis = axes.x2axis;
-            for (i = 0; i < axis.ticks.length; ++i) {
-                v = axis.ticks[i].v;
-                if (v <= axis.min || v >= axis.max)
-                    continue;
-    
-                ctx.moveTo(Math.floor(axis.p2c(v)) + ctx.lineWidth/2, -5);
-                ctx.lineTo(Math.floor(axis.p2c(v)) + ctx.lineWidth/2, 5);
-            }
-
-            axis = axes.y2axis;
-            for (i = 0; i < axis.ticks.length; ++i) {
-                v = axis.ticks[i].v;
-                if (v <= axis.min || v >= axis.max)
-                    continue;
-
-                ctx.moveTo(plotWidth-5, Math.floor(axis.p2c(v)) + ctx.lineWidth/2);
-                ctx.lineTo(plotWidth+5, Math.floor(axis.p2c(v)) + ctx.lineWidth/2);
-            }
-            
-            ctx.stroke();
-            
-            if (options.grid.borderWidth) {
-                // draw border
-                ctx.lineWidth = options.grid.borderWidth;
-                ctx.strokeStyle = options.grid.color;
-                ctx.lineJoin = "round";
-                ctx.strokeRect(0, 0, plotWidth, plotHeight);
-            }
-
-            ctx.restore();
-        }
-        
-        function insertLabels() {
-            target.find(".tickLabels").remove();
-            
-            var html = '<div class="tickLabels" style="font-size:smaller;color:' + options.grid.color + '">';
-
-            function addLabels(axis, labelGenerator) {
-                for (var i = 0; i < axis.ticks.length; ++i) {
-                    var tick = axis.ticks[i];
-                    if (!tick.label || tick.v < axis.min || tick.v > axis.max)
-                        continue;
-                    html += labelGenerator(tick, axis);
-                }
-            }
-            
-            addLabels(axes.xaxis, function (tick, axis) {
-                return '<div style="position:absolute;top:' + (plotOffset.top + plotHeight + options.grid.labelMargin) + 'px;left:' + (plotOffset.left + axis.p2c(tick.v) - axis.labelWidth/2) + 'px;width:' + axis.labelWidth + 'px;text-align:center" class="tickLabel">' + tick.label + "</div>";
-            });
-            
-            
-            addLabels(axes.yaxis, function (tick, axis) {
-                return '<div style="position:absolute;top:' + (plotOffset.top + axis.p2c(tick.v) - axis.labelHeight/2) + 'px;right:' + (plotOffset.right + plotWidth + options.grid.labelMargin) + 'px;width:' + axis.labelWidth + 'px;text-align:right" class="tickLabel">' + tick.label + "</div>";
-            });
-            
-            addLabels(axes.x2axis, function (tick, axis) {
-                return '<div style="position:absolute;bottom:' + (plotOffset.bottom + plotHeight + options.grid.labelMargin) + 'px;left:' + (plotOffset.left + axis.p2c(tick.v) - axis.labelWidth/2) + 'px;width:' + axis.labelWidth + 'px;text-align:center" class="tickLabel">' + tick.label + "</div>";
-            });
-            
-            addLabels(axes.y2axis, function (tick, axis) {
-                return '<div style="position:absolute;top:' + (plotOffset.top + axis.p2c(tick.v) - axis.labelHeight/2) + 'px;left:' + (plotOffset.left + plotWidth + options.grid.labelMargin) +'px;width:' + axis.labelWidth + 'px;text-align:left" class="tickLabel">' + tick.label + "</div>";
-            });
-
-            html += '</div>';
-            
-            target.append(html);
-        }
-
-        function drawSeries(series) {
-            if (series.lines.show || (!series.bars.show && !series.points.show))
-                drawSeriesLines(series);
-            if (series.bars.show)
-                drawSeriesBars(series);
-            if (series.points.show)
-                drawSeriesPoints(series);
-        }
-        
-        function drawSeriesLines(series) {
-            function plotLine(data, offset, axisx, axisy) {
-                var prev, cur = null, drawx = null, drawy = null;
-                
-                ctx.beginPath();
-                for (var i = 0; i < data.length; ++i) {
-                    prev = cur;
-                    cur = data[i];
-
-                    if (prev == null || cur == null)
-                        continue;
-                    
-                    var x1 = prev[0], y1 = prev[1],
-                        x2 = cur[0], y2 = cur[1];
-
-                    // clip with ymin
-                    if (y1 <= y2 && y1 < axisy.min) {
-                        if (y2 < axisy.min)
-                            continue;   // line segment is outside
-                        // compute new intersection point
-                        x1 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1;
-                        y1 = axisy.min;
-                    }
-                    else if (y2 <= y1 && y2 < axisy.min) {
-                        if (y1 < axisy.min)
-                            continue;
-                        x2 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1;
-                        y2 = axisy.min;
-                    }
-
-                    // clip with ymax
-                    if (y1 >= y2 && y1 > axisy.max) {
-                        if (y2 > axisy.max)
-                            continue;
-                        x1 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1;
-                        y1 = axisy.max;
-                    }
-                    else if (y2 >= y1 && y2 > axisy.max) {
-                        if (y1 > axisy.max)
-                            continue;
-                        x2 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1;
-                        y2 = axisy.max;
-                    }
-
-                    // clip with xmin
-                    if (x1 <= x2 && x1 < axisx.min) {
-                        if (x2 < axisx.min)
-                            continue;
-                        y1 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1;
-                        x1 = axisx.min;
-                    }
-                    else if (x2 <= x1 && x2 < axisx.min) {
-                        if (x1 < axisx.min)
-                            continue;
-                        y2 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1;
-                        x2 = axisx.min;
-                    }
-
-                    // clip with xmax
-                    if (x1 >= x2 && x1 > axisx.max) {
-                        if (x2 > axisx.max)
-                            continue;
-                        y1 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1;
-                        x1 = axisx.max;
-                    }
-                    else if (x2 >= x1 && x2 > axisx.max) {
-                        if (x1 > axisx.max)
-                            continue;
-                        y2 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1;
-                        x2 = axisx.max;
-                    }
-
-                    if (drawx != axisx.p2c(x1) || drawy != axisy.p2c(y1) + offset)
-                        ctx.moveTo(axisx.p2c(x1), axisy.p2c(y1) + offset);
-                    
-                    drawx = axisx.p2c(x2);
-                    drawy = axisy.p2c(y2) + offset;
-                    ctx.lineTo(drawx, drawy);
-                }
-                ctx.stroke();
-            }
-
-            function plotLineArea(data, axisx, axisy) {
-                var prev, cur = null;
-                
-                var bottom = Math.min(Math.max(0, axisy.min), axisy.max);
-                var top, lastX = 0;
-
-                var areaOpen = false;
-                
-                for (var i = 0; i < data.length; ++i) {
-                    prev = cur;
-                    cur = data[i];
-
-                    if (areaOpen && prev != null && cur == null) {
-                        // close area
-                        ctx.lineTo(axisx.p2c(lastX), axisy.p2c(bottom));
-                        ctx.fill();
-                        areaOpen = false;
-                        continue;
-                    }
-
-                    if (prev == null || cur == null)
-                        continue;
-                        
-                    var x1 = prev[0], y1 = prev[1],
-                        x2 = cur[0], y2 = cur[1];
-
-                    // clip x values
-                    
-                    // clip with xmin
-                    if (x1 <= x2 && x1 < axisx.min) {
-                        if (x2 < axisx.min)
-                            continue;
-                        y1 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1;
-                        x1 = axisx.min;
-                    }
-                    else if (x2 <= x1 && x2 < axisx.min) {
-                        if (x1 < axisx.min)
-                            continue;
-                        y2 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1;
-                        x2 = axisx.min;
-                    }
-
-                    // clip with xmax
-                    if (x1 >= x2 && x1 > axisx.max) {
-                        if (x2 > axisx.max)
-                            continue;
-                        y1 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1;
-                        x1 = axisx.max;
-                    }
-                    else if (x2 >= x1 && x2 > axisx.max) {
-                        if (x1 > axisx.max)
-                            continue;
-                        y2 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1;
-                        x2 = axisx.max;
-                    }
-
-                    if (!areaOpen) {
-                        // open area
-                        ctx.beginPath();
-                        ctx.moveTo(axisx.p2c(x1), axisy.p2c(bottom));
-                        areaOpen = true;
-                    }
-                    
-                    // now first check the case where both is outside
-                    if (y1 >= axisy.max && y2 >= axisy.max) {
-                        ctx.lineTo(axisx.p2c(x1), axisy.p2c(axisy.max));
-                        ctx.lineTo(axisx.p2c(x2), axisy.p2c(axisy.max));
-                        continue;
-                    }
-                    else if (y1 <= axisy.min && y2 <= axisy.min) {
-                        ctx.lineTo(axisx.p2c(x1), axisy.p2c(axisy.min));
-                        ctx.lineTo(axisx.p2c(x2), axisy.p2c(axisy.min));
-                        continue;
-                    }
-                    
-                    // else it's a bit more complicated, there might
-                    // be two rectangles and two triangles we need to fill
-                    // in; to find these keep track of the current x values
-                    var x1old = x1, x2old = x2;
-
-                    // and clip the y values, without shortcutting
-                    
-                    // clip with ymin
-                    if (y1 <= y2 && y1 < axisy.min && y2 >= axisy.min) {
-                        x1 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1;
-                        y1 = axisy.min;
-                    }
-                    else if (y2 <= y1 && y2 < axisy.min && y1 >= axisy.min) {
-                        x2 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1;
-                        y2 = axisy.min;
-                    }
-
-                    // clip with ymax
-                    if (y1 >= y2 && y1 > axisy.max && y2 <= axisy.max) {
-                        x1 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1;
-                        y1 = axisy.max;
-                    }
-                    else if (y2 >= y1 && y2 > axisy.max && y1 <= axisy.max) {
-                        x2 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1;
-                        y2 = axisy.max;
-                    }
-
-
-                    // if the x value was changed we got a rectangle
-                    // to fill
-                    if (x1 != x1old) {
-                        if (y1 <= axisy.min)
-                            top = axisy.min;
-                        else
-                            top = axisy.max;
-                        
-                        ctx.lineTo(axisx.p2c(x1old), axisy.p2c(top));
-                        ctx.lineTo(axisx.p2c(x1), axisy.p2c(top));
-                    }
-                    
-                    // fill the triangles
-                    ctx.lineTo(axisx.p2c(x1), axisy.p2c(y1));
-                    ctx.lineTo(axisx.p2c(x2), axisy.p2c(y2));
-
-                    // fill the other rectangle if it's there
-                    if (x2 != x2old) {
-                        if (y2 <= axisy.min)
-                            top = axisy.min;
-                        else
-                            top = axisy.max;
-                        
-                        ctx.lineTo(axisx.p2c(x2old), axisy.p2c(top));
-                        ctx.lineTo(axisx.p2c(x2), axisy.p2c(top));
-                    }
-
-                    lastX = Math.max(x2, x2old);
-                }
-
-                if (areaOpen) {
-                    ctx.lineTo(axisx.p2c(lastX), axisy.p2c(bottom));
-                    ctx.fill();
-                }
-            }
-            
-            ctx.save();
-            ctx.translate(plotOffset.left, plotOffset.top);
-            ctx.lineJoin = "round";
-
-            var lw = series.lines.lineWidth;
-            var sw = series.shadowSize;
-            // FIXME: consider another form of shadow when filling is turned on
-            if (sw > 0) {
-                // draw shadow in two steps
-                ctx.lineWidth = sw / 2;
-                ctx.strokeStyle = "rgba(0,0,0,0.1)";
-                plotLine(series.data, lw/2 + sw/2 + ctx.lineWidth/2, series.xaxis, series.yaxis);
-
-                ctx.lineWidth = sw / 2;
-                ctx.strokeStyle = "rgba(0,0,0,0.2)";
-                plotLine(series.data, lw/2 + ctx.lineWidth/2, series.xaxis, series.yaxis);
-            }
-
-            ctx.lineWidth = lw;
-            ctx.strokeStyle = series.color;
-            setFillStyle(series.lines, series.color);
-            if (series.lines.fill)
-                plotLineArea(series.data, series.xaxis, series.yaxis);
-            plotLine(series.data, 0, series.xaxis, series.yaxis);
-            ctx.restore();
-        }
-
-        function drawSeriesPoints(series) {
-            function plotPoints(data, radius, fill, axisx, axisy) {
-                for (var i = 0; i < data.length; ++i) {
-                    if (data[i] == null)
-                        continue;
-                    
-                    var x = data[i][0], y = data[i][1];
-                    if (x < axisx.min || x > axisx.max || y < axisy.min || y > axisy.max)
-                        continue;
-                    
-                    ctx.beginPath();
-                    ctx.arc(axisx.p2c(x), axisy.p2c(y), radius, 0, 2 * Math.PI, true);
-                    if (fill)
-                        ctx.fill();
-                    ctx.stroke();
-                }
-            }
-
-            function plotPointShadows(data, offset, radius, axisx, axisy) {
-                for (var i = 0; i < data.length; ++i) {
-                    if (data[i] == null)
-                        continue;
-                    
-                    var x = data[i][0], y = data[i][1];
-                    if (x < axisx.min || x > axisx.max || y < axisy.min || y > axisy.max)
-                        continue;
-                    ctx.beginPath();
-                    ctx.arc(axisx.p2c(x), axisy.p2c(y) + offset, radius, 0, Math.PI, false);
-                    ctx.stroke();
-                }
-            }
-            
-            ctx.save();
-            ctx.translate(plotOffset.left, plotOffset.top);
-
-            var lw = series.lines.lineWidth;
-            var sw = series.shadowSize;
-            if (sw > 0) {
-                // draw shadow in two steps
-                ctx.lineWidth = sw / 2;
-                ctx.strokeStyle = "rgba(0,0,0,0.1)";
-                plotPointShadows(series.data, sw/2 + ctx.lineWidth/2,
-                                 series.points.radius, series.xaxis, series.yaxis);
-
-                ctx.lineWidth = sw / 2;
-                ctx.strokeStyle = "rgba(0,0,0,0.2)";
-                plotPointShadows(series.data, ctx.lineWidth/2,
-                                 series.points.radius, series.xaxis, series.yaxis);
-            }
-
-            ctx.lineWidth = series.points.lineWidth;
-            ctx.strokeStyle = series.color;
-            setFillStyle(series.points, series.color);
-            plotPoints(series.data, series.points.radius, series.points.fill,
-                       series.xaxis, series.yaxis);
-            ctx.restore();
-        }
-
-        function drawBar(x, y, barLeft, barRight, offset, fill, axisx, axisy, c) {
-            var drawLeft = true, drawRight = true,
-                drawTop = true, drawBottom = false,
-                left = x + barLeft, right = x + barRight,
-                bottom = 0, top = y;
-
-            // account for negative bars
-            if (top < bottom) {
-                top = 0;
-                bottom = y;
-                drawBottom = true;
-                drawTop = false;
-            }
-            
-            // clip
-            if (right < axisx.min || left > axisx.max ||
-                top < axisy.min || bottom > axisy.max)
-                return;
-            
-            if (left < axisx.min) {
-                left = axisx.min;
-                drawLeft = false;
-            }
-
-            if (right > axisx.max) {
-                right = axisx.max;
-                drawRight = false;
-            }
-
-            if (bottom < axisy.min) {
-                bottom = axisy.min;
-                drawBottom = false;
-            }
-            
-            if (top > axisy.max) {
-                top = axisy.max;
-                drawTop = false;
-            }
-
-            // fill the bar
-            if (fill) {
-                c.beginPath();
-                c.moveTo(axisx.p2c(left), axisy.p2c(bottom) + offset);
-                c.lineTo(axisx.p2c(left), axisy.p2c(top) + offset);
-                c.lineTo(axisx.p2c(right), axisy.p2c(top) + offset);
-                c.lineTo(axisx.p2c(right), axisy.p2c(bottom) + offset);
-                c.fill();
-            }
-
-            // draw outline
-            if (drawLeft || drawRight || drawTop || drawBottom) {
-                c.beginPath();
-                left = axisx.p2c(left);
-                bottom = axisy.p2c(bottom);
-                right = axisx.p2c(right);
-                top = axisy.p2c(top);
-                
-                c.moveTo(left, bottom + offset);
-                if (drawLeft)
-                    c.lineTo(left, top + offset);
-                else
-                    c.moveTo(left, top + offset);
-                if (drawTop)
-                    c.lineTo(right, top + offset);
-                else
-                    c.moveTo(right, top + offset);
-                if (drawRight)
-                    c.lineTo(right, bottom + offset);
-                else
-                    c.moveTo(right, bottom + offset);
-                if (drawBottom)
-                    c.lineTo(left, bottom + offset);
-                else
-                    c.moveTo(left, bottom + offset);
-                c.stroke();
-            }
-        }
-        
-        function drawSeriesBars(series) {
-            function plotBars(data, barLeft, barRight, offset, fill, axisx, axisy) {
-                for (var i = 0; i < data.length; i++) {
-                    if (data[i] == null)
-                        continue;
-                    drawBar(data[i][0], data[i][1], barLeft, barRight, offset, fill, axisx, axisy, ctx);
-                }
-            }
-
-            ctx.save();
-            ctx.translate(plotOffset.left, plotOffset.top);
-            ctx.lineJoin = "round";
-
-            // FIXME: figure out a way to add shadows
-            /*
-            var bw = series.bars.barWidth;
-            var lw = series.bars.lineWidth;
-            var sw = series.shadowSize;
-            if (sw > 0) {
-                // draw shadow in two steps
-                ctx.lineWidth = sw / 2;
-                ctx.strokeStyle = "rgba(0,0,0,0.1)";
-                plotBars(series.data, bw, lw/2 + sw/2 + ctx.lineWidth/2, false);
-
-                ctx.lineWidth = sw / 2;
-                ctx.strokeStyle = "rgba(0,0,0,0.2)";
-                plotBars(series.data, bw, lw/2 + ctx.lineWidth/2, false);
-            }*/
-
-            ctx.lineWidth = series.bars.lineWidth;
-            ctx.strokeStyle = series.color;
-            setFillStyle(series.bars, series.color);
-            var barLeft = series.bars.align == "left" ? 0 : -series.bars.barWidth/2;
-            plotBars(series.data, barLeft, barLeft + series.bars.barWidth, 0, series.bars.fill, series.xaxis, series.yaxis);
-            ctx.restore();
-        }
-
-        function setFillStyle(obj, seriesColor) {
-            var fill = obj.fill;
-            if (!fill)
-                return;
-            
-            if (obj.fillColor)
-                ctx.fillStyle = obj.fillColor;
-            else {
-                var c = parseColor(seriesColor);
-                c.a = typeof fill == "number" ? fill : 0.4;
-                c.normalize();
-                ctx.fillStyle = c.toString();
-            }
-        }
-        
-        function insertLegend() {
-            target.find(".legend").remove();
-
-            if (!options.legend.show)
-                return;
-            
-            var fragments = [];
-            var rowStarted = false;
-            for (i = 0; i < series.length; ++i) {
-                if (!series[i].label)
-                    continue;
-                
-                if (i % options.legend.noColumns == 0) {
-                    if (rowStarted)
-                        fragments.push('</tr>');
-                    fragments.push('<tr>');
-                    rowStarted = true;
-                }
-
-                var label = series[i].label;
-                if (options.legend.labelFormatter != null)
-                    label = options.legend.labelFormatter(label);
-                
-                fragments.push(
-                    '<td class="legendColorBox"><div style="border:1px solid ' + options.legend.labelBoxBorderColor + ';padding:1px"><div style="width:14px;height:10px;background-color:' + series[i].color + ';overflow:hidden"></div></div></td>' +
-                    '<td class="legendLabel">' + label + '</td>');
-            }
-            if (rowStarted)
-                fragments.push('</tr>');
-            
-            if (fragments.length == 0)
-                return;
-
-            var table = '<table style="font-size:smaller;color:' + options.grid.color + '">' + fragments.join("") + '</table>';
-            if (options.legend.container != null)
-                options.legend.container.html(table);
-            else {
-                var pos = "";
-                var p = options.legend.position, m = options.legend.margin;
-                if (p.charAt(0) == "n")
-                    pos += 'top:' + (m + plotOffset.top) + 'px;';
-                else if (p.charAt(0) == "s")
-                    pos += 'bottom:' + (m + plotOffset.bottom) + 'px;';
-                if (p.charAt(1) == "e")
-                    pos += 'right:' + (m + plotOffset.right) + 'px;';
-                else if (p.charAt(1) == "w")
-                    pos += 'left:' + (m + plotOffset.left) + 'px;';
-                var legend = $('<div class="legend">' + table.replace('style="', 'style="position:absolute;' + pos +';') + '</div>').appendTo(target);
-                if (options.legend.backgroundOpacity != 0.0) {
-                    // put in the transparent background
-                    // separately to avoid blended labels and
-                    // label boxes
-                    var c = options.legend.backgroundColor;
-                    if (c == null) {
-                        var tmp;
-                        if (options.grid.backgroundColor)
-                            tmp = options.grid.backgroundColor;
-                        else
-                            tmp = extractColor(legend);
-                        c = parseColor(tmp).adjust(null, null, null, 1).toString();
-                    }
-                    var div = legend.children();
-                    $('<div style="position:absolute;width:' + div.width() + 'px;height:' + div.height() + 'px;' + pos +'background-color:' + c + ';"> </div>').prependTo(legend).css('opacity', options.legend.backgroundOpacity);
-                    
-                }
-            }
-        }
-
-
-        // interactive features
-        
-        var lastMousePos = { pageX: null, pageY: null },
-            selection = {
-                first: { x: -1, y: -1}, second: { x: -1, y: -1},
-                show: false, active: false },
-            highlights = [],
-            clickIsMouseUp = false,
-            redrawTimeout = null,
-            hoverTimeout = null;
-        
-        // Returns the data item the mouse is over, or null if none is found
-        function findNearbyItem(mouseX, mouseY) {
-            var maxDistance = options.grid.mouseActiveRadius,
-                lowestDistance = maxDistance * maxDistance + 1,
-                item = null, foundPoint = false;
-
-            function result(i, j) {
-                return { datapoint: series[i].data[j],
-                         dataIndex: j,
-                         series: series[i],
-                         seriesIndex: i };
-            }
-            
-            for (var i = 0; i < series.length; ++i) {
-                var data = series[i].data,
-                    axisx = series[i].xaxis,
-                    axisy = series[i].yaxis,
-                
-                    // precompute some stuff to make the loop faster
-                    mx = axisx.c2p(mouseX),
-                    my = axisy.c2p(mouseY),
-                    maxx = maxDistance / axisx.scale,
-                    maxy = maxDistance / axisy.scale,
-                    checkbar = series[i].bars.show,
-                    checkpoint = !(series[i].bars.show && !(series[i].lines.show || series[i].points.show)),
-                    barLeft = series[i].bars.align == "left" ? 0 : -series[i].bars.barWidth/2,
-                    barRight = barLeft + series[i].bars.barWidth;
-                for (var j = 0; j < data.length; ++j) {
-                    if (data[j] == null)
-                        continue;
-
-                    var x = data[j][0], y = data[j][1];
-  
-                    if (checkbar) {
-                        // For a bar graph, the cursor must be inside the bar
-                        // and no other point can be nearby
-                        if (!foundPoint && mx >= x + barLeft &&
-                            mx <= x + barRight &&
-                            my >= Math.min(0, y) && my <= Math.max(0, y))
-                            item = result(i, j);
-                    }
- 
-                    if (checkpoint) {
-                        // For points and lines, the cursor must be within a
-                        // certain distance to the data point
- 
-                        // check bounding box first
-                        if ((x - mx > maxx || x - mx < -maxx) ||
-                            (y - my > maxy || y - my < -maxy))
-                            continue;
-
-                        // We have to calculate distances in pixels, not in
-                        // data units, because the scale of the axes may be different
-                        var dx = Math.abs(axisx.p2c(x) - mouseX),
-                            dy = Math.abs(axisy.p2c(y) - mouseY),
-                            dist = dx * dx + dy * dy;
-                        if (dist < lowestDistance) {
-                            lowestDistance = dist;
-                            foundPoint = true;
-                            item = result(i, j);
-                        }
-                    }
-                }
-            }
-
-            return item;
-        }
-
-        function onMouseMove(ev) {
-            // FIXME: temp. work-around until jQuery bug 1871 is fixed
-            var e = ev || window.event;
-            if (e.pageX == null && e.clientX != null) {
-                var de = document.documentElement, b = document.body;
-                lastMousePos.pageX = e.clientX + (de && de.scrollLeft || b.scrollLeft || 0);
-                lastMousePos.pageY = e.clientY + (de && de.scrollTop || b.scrollTop || 0);
-            }
-            else {
-                lastMousePos.pageX = e.pageX;
-                lastMousePos.pageY = e.pageY;
-            }
-            
-            if (options.grid.hoverable && !hoverTimeout)
-                hoverTimeout = setTimeout(onHover, 100);
-
-            if (selection.active)
-                updateSelection(lastMousePos);
-        }
-        
-        function onMouseDown(e) {
-            if (e.which != 1)  // only accept left-click
-                return;
-            
-            // cancel out any text selections
-            document.body.focus();
-
-            // prevent text selection and drag in old-school browsers
-            if (document.onselectstart !== undefined && workarounds.onselectstart == null) {
-                workarounds.onselectstart = document.onselectstart;
-                document.onselectstart = function () { return false; };
-            }
-            if (document.ondrag !== undefined && workarounds.ondrag == null) {
-                workarounds.ondrag = document.ondrag;
-                document.ondrag = function () { return false; };
-            }
-            
-            setSelectionPos(selection.first, e);
-                
-            lastMousePos.pageX = null;
-            selection.active = true;
-            $(document).one("mouseup", onSelectionMouseUp);
-        }
-
-        function onClick(e) {
-            if (clickIsMouseUp) {
-                clickIsMouseUp = false;
-                return;
-            }
-
-            triggerClickHoverEvent("plotclick", e);
-        }
-        
-        function onHover() {
-            triggerClickHoverEvent("plothover", lastMousePos);
-            hoverTimeout = null;
-        }
-
-        // trigger click or hover event (they send the same parameters
-        // so we share their code)
-        function triggerClickHoverEvent(eventname, event) {
-            var offset = eventHolder.offset(),
-                pos = { pageX: event.pageX, pageY: event.pageY },
-                canvasX = event.pageX - offset.left - plotOffset.left,
-                canvasY = event.pageY - offset.top - plotOffset.top;
-
-            if (axes.xaxis.used)
-                pos.x = axes.xaxis.c2p(canvasX);
-            if (axes.yaxis.used)
-                pos.y = axes.yaxis.c2p(canvasY);
-            if (axes.x2axis.used)
-                pos.x2 = axes.x2axis.c2p(canvasX);
-            if (axes.y2axis.used)
-                pos.y2 = axes.y2axis.c2p(canvasY);
-
-            var item = findNearbyItem(canvasX, canvasY);
-
-            if (item) {
-                // fill in mouse pos for any listeners out there
-                item.pageX = parseInt(item.series.xaxis.p2c(item.datapoint[0]) + offset.left + plotOffset.left);
-                item.pageY = parseInt(item.series.yaxis.p2c(item.datapoint[1]) + offset.top + plotOffset.top);
-
-                    
-            }
-
-            if (options.grid.autoHighlight) {
-                for (var i = 0; i < highlights.length; ++i) {
-                    var h = highlights[i];
-                    if (h.auto &&
-                        !(item && h.series == item.series && h.point == item.datapoint))
-                        unhighlight(h.series, h.point);
-                }
-                
-                if (item)
-                    highlight(item.series, item.datapoint, true);
-            }
-            
-            target.trigger(eventname, [ pos, item ]);
-        }
-
-        function triggerRedrawOverlay() {
-            if (!redrawTimeout)
-                redrawTimeout = setTimeout(redrawOverlay, 50);
-        }
-
-        function redrawOverlay() {
-            redrawTimeout = null;
-
-            // redraw highlights
-            octx.save();
-            octx.clearRect(0, 0, canvasWidth, canvasHeight);
-            octx.translate(plotOffset.left, plotOffset.top);
-            
-            var i, hi; 
-            for (i = 0; i < highlights.length; ++i) {
-                hi = highlights[i];
-
-                if (hi.series.bars.show)
-                    drawBarHighlight(hi.series, hi.point);
-                else
-                    drawPointHighlight(hi.series, hi.point);
-            }
-            octx.restore();
-
-            // redraw selection
-            if (selection.show && selectionIsSane()) {
-                octx.strokeStyle = parseColor(options.selection.color).scale(null, null, null, 0.8).toString();
-                octx.lineWidth = 1;
-                ctx.lineJoin = "round";
-                octx.fillStyle = parseColor(options.selection.color).scale(null, null, null, 0.4).toString();
-                
-                var x = Math.min(selection.first.x, selection.second.x),
-                    y = Math.min(selection.first.y, selection.second.y),
-                    w = Math.abs(selection.second.x - selection.first.x),
-                    h = Math.abs(selection.second.y - selection.first.y);
-                
-                octx.fillRect(x + plotOffset.left, y + plotOffset.top, w, h);
-                octx.strokeRect(x + plotOffset.left, y + plotOffset.top, w, h);
-            }
-        }
-        
-        function highlight(s, point, auto) {
-            if (typeof s == "number")
-                s = series[s];
-
-            if (typeof point == "number")
-                point = s.data[point];
-
-            var i = indexOfHighlight(s, point);
-            if (i == -1) {
-                highlights.push({ series: s, point: point, auto: auto });
-
-                triggerRedrawOverlay();
-            }
-            else if (!auto)
-                highlights[i].auto = false;
-        }
-            
-        function unhighlight(s, point) {
-            if (typeof s == "number")
-                s = series[s];
-
-            if (typeof point == "number")
-                point = s.data[point];
-
-            var i = indexOfHighlight(s, point);
-            if (i != -1) {
-                highlights.splice(i, 1);
-
-                triggerRedrawOverlay();
-            }
-        }
-        
-        function indexOfHighlight(s, p) {
-            for (var i = 0; i < highlights.length; ++i) {
-                var h = highlights[i];
-                if (h.series == s && h.point[0] == p[0]
-                    && h.point[1] == p[1])
-                    return i;
-            }
-            return -1;
-        }
-        
-        function drawPointHighlight(series, point) {
-            var x = point[0], y = point[1],
-                axisx = series.xaxis, axisy = series.yaxis;
-            
-            if (x < axisx.min || x > axisx.max || y < axisy.min || y > axisy.max)
-                return;
-            
-            var pointRadius = series.points.radius + series.points.lineWidth / 2;
-            octx.lineWidth = pointRadius;
-            octx.strokeStyle = parseColor(series.color).scale(1, 1, 1, 0.5).toString();
-            var radius = 1.5 * pointRadius;
-            octx.beginPath();
-            octx.arc(axisx.p2c(x), axisy.p2c(y), radius, 0, 2 * Math.PI, true);
-            octx.stroke();
-        }
-
-        function drawBarHighlight(series, point) {
-            octx.lineJoin = "round";
-            octx.lineWidth = series.bars.lineWidth;
-            octx.strokeStyle = parseColor(series.color).scale(1, 1, 1, 0.5).toString();
-            octx.fillStyle = parseColor(series.color).scale(1, 1, 1, 0.5).toString();
-            var barLeft = series.bars.align == "left" ? 0 : -series.bars.barWidth/2;
-            drawBar(point[0], point[1], barLeft, barLeft + series.bars.barWidth,
-                    0, true, series.xaxis, series.yaxis, octx);
-        }
-        
-        function triggerSelectedEvent() {
-            var x1 = Math.min(selection.first.x, selection.second.x),
-                x2 = Math.max(selection.first.x, selection.second.x),
-                y1 = Math.max(selection.first.y, selection.second.y),
-                y2 = Math.min(selection.first.y, selection.second.y);
-
-            var r = {};
-            if (axes.xaxis.used)
-                r.xaxis = { from: axes.xaxis.c2p(x1), to: axes.xaxis.c2p(x2) };
-            if (axes.x2axis.used)
-                r.x2axis = { from: axes.x2axis.c2p(x1), to: axes.x2axis.c2p(x2) };
-            if (axes.yaxis.used)
-                r.yaxis = { from: axes.yaxis.c2p(y1), to: axes.yaxis.c2p(y2) };
-            if (axes.y2axis.used)
-                r.yaxis = { from: axes.y2axis.c2p(y1), to: axes.y2axis.c2p(y2) };
-            
-            target.trigger("plotselected", [ r ]);
-
-            // backwards-compat stuff, to be removed in future
-            if (axes.xaxis.used && axes.yaxis.used)
-                target.trigger("selected", [ { x1: r.xaxis.from, y1: r.yaxis.from, x2: r.xaxis.to, y2: r.yaxis.to } ]);
-        }
-        
-        function onSelectionMouseUp(e) {
-            // revert drag stuff for old-school browsers
-            if (document.onselectstart !== undefined)
-                document.onselectstart = workarounds.onselectstart;
-            if (document.ondrag !== undefined)
-                document.ondrag = workarounds.ondrag;
-            
-            // no more draggy-dee-drag
-            selection.active = false;
-            updateSelection(e);
-            
-            if (selectionIsSane()) {
-                triggerSelectedEvent();
-                clickIsMouseUp = true;
-            }
-            
-            return false;
-        }
-
-        function setSelectionPos(pos, e) {
-            var offset = eventHolder.offset();
-            if (options.selection.mode == "y") {
-                if (pos == selection.first)
-                    pos.x = 0;
-                else
-                    pos.x = plotWidth;
-            }
-            else {
-                pos.x = e.pageX - offset.left - plotOffset.left;
-                pos.x = Math.min(Math.max(0, pos.x), plotWidth);
-            }
-
-            if (options.selection.mode == "x") {
-                if (pos == selection.first)
-                    pos.y = 0;
-                else
-                    pos.y = plotHeight;
-            }
-            else {
-                pos.y = e.pageY - offset.top - plotOffset.top;
-                pos.y = Math.min(Math.max(0, pos.y), plotHeight);
-            }
-        }
-        
-        function updateSelection(pos) {
-            if (pos.pageX == null)
-                return;
-            
-            setSelectionPos(selection.second, pos);
-            if (selectionIsSane()) {
-                selection.show = true;
-                triggerRedrawOverlay();
-            }
-            else
-                clearSelection();
-        }
-
-        function clearSelection() {
-            if (selection.show) {
-                selection.show = false;
-                triggerRedrawOverlay();
-            }
-        }
-
-        function setSelection(ranges, preventEvent) {
-            var range;
-            
-            if (options.selection.mode == "y") {
-                selection.first.x = 0;
-                selection.second.x = plotWidth;
-            }
-            else {
-                range = extractRange(ranges, "x");
-                
-                selection.first.x = range.axis.p2c(range.from);
-                selection.second.x = range.axis.p2c(range.to);
-            }
-            
-            if (options.selection.mode == "x") {
-                selection.first.y = 0;
-                selection.second.y = plotHeight;
-            }
-            else {
-                range = extractRange(ranges, "y");
-                
-                selection.first.y = range.axis.p2c(range.from);
-                selection.second.y = range.axis.p2c(range.to);
-            }
-
-            selection.show = true;
-            triggerRedrawOverlay();
-            if (!preventEvent)
-                triggerSelectedEvent();
-        }
-        
-        function selectionIsSane() {
-            var minSize = 5;
-            return Math.abs(selection.second.x - selection.first.x) >= minSize &&
-                Math.abs(selection.second.y - selection.first.y) >= minSize;
-        }
-    }
-    
-    $.plot = function(target, data, options) {
-        var plot = new Plot(target, data, options);
-        /*var t0 = new Date();     
-        var t1 = new Date();
-        var tstr = "time used (msecs): " + (t1.getTime() - t0.getTime())
-        if (window.console)
-            console.log(tstr);
-        else
-            alert(tstr);*/
-        return plot;
-    };
-    
-    // round to nearby lower multiple of base
-    function floorInBase(n, base) {
-        return base * Math.floor(n / base);
-    }
-    
-    function clamp(min, value, max) {
-        if (value < min)
-            return value;
-        else if (value > max)
-            return max;
-        else
-            return value;
-    }
-    
-    // color helpers, inspiration from the jquery color animation
-    // plugin by John Resig
-    function Color (r, g, b, a) {
-       
-        var rgba = ['r','g','b','a'];
-        var x = 4; //rgba.length
-       
-        while (-1<--x) {
-            this[rgba[x]] = arguments[x] || ((x==3) ? 1.0 : 0);
-        }
-       
-        this.toString = function() {
-            if (this.a >= 1.0) {
-                return "rgb("+[this.r,this.g,this.b].join(",")+")";
-            } else {
-                return "rgba("+[this.r,this.g,this.b,this.a].join(",")+")";
-            }
-        };
-
-        this.scale = function(rf, gf, bf, af) {
-            x = 4; //rgba.length
-            while (-1<--x) {
-                if (arguments[x] != null)
-                    this[rgba[x]] *= arguments[x];
-            }
-            return this.normalize();
-        };
-
-        this.adjust = function(rd, gd, bd, ad) {
-            x = 4; //rgba.length
-            while (-1<--x) {
-                if (arguments[x] != null)
-                    this[rgba[x]] += arguments[x];
-            }
-            return this.normalize();
-        };
-
-        this.clone = function() {
-            return new Color(this.r, this.b, this.g, this.a);
-        };
-
-        var limit = function(val,minVal,maxVal) {
-            return Math.max(Math.min(val, maxVal), minVal);
-        };
-
-        this.normalize = function() {
-            this.r = limit(parseInt(this.r), 0, 255);
-            this.g = limit(parseInt(this.g), 0, 255);
-            this.b = limit(parseInt(this.b), 0, 255);
-            this.a = limit(this.a, 0, 1);
-            return this;
-        };
-
-        this.normalize();
-    }
-    
-    var lookupColors = {
-        aqua:[0,255,255],
-        azure:[240,255,255],
-        beige:[245,245,220],
-        black:[0,0,0],
-        blue:[0,0,255],
-        brown:[165,42,42],
-        cyan:[0,255,255],
-        darkblue:[0,0,139],
-        darkcyan:[0,139,139],
-        darkgrey:[169,169,169],
-        darkgreen:[0,100,0],
-        darkkhaki:[189,183,107],
-        darkmagenta:[139,0,139],
-        darkolivegreen:[85,107,47],
-        darkorange:[255,140,0],
-        darkorchid:[153,50,204],
-        darkred:[139,0,0],
-        darksalmon:[233,150,122],
-        darkviolet:[148,0,211],
-        fuchsia:[255,0,255],
-        gold:[255,215,0],
-        green:[0,128,0],
-        indigo:[75,0,130],
-        khaki:[240,230,140],
-        lightblue:[173,216,230],
-        lightcyan:[224,255,255],
-        lightgreen:[144,238,144],
-        lightgrey:[211,211,211],
-        lightpink:[255,182,193],
-        lightyellow:[255,255,224],
-        lime:[0,255,0],
-        magenta:[255,0,255],
-        maroon:[128,0,0],
-        navy:[0,0,128],
-        olive:[128,128,0],
-        orange:[255,165,0],
-        pink:[255,192,203],
-        purple:[128,0,128],
-        violet:[128,0,128],
-        red:[255,0,0],
-        silver:[192,192,192],
-        white:[255,255,255],
-        yellow:[255,255,0]
-    };    
-
-    function extractColor(element) {
-        var color, elem = element;
-        do {
-            color = elem.css("background-color").toLowerCase();
-            // keep going until we find an element that has color, or
-            // we hit the body
-            if (color != '' && color != 'transparent')
-                break;
-            elem = elem.parent();
-        } while (!$.nodeName(elem.get(0), "body"));
-
-        // catch Safari's way of signalling transparent
-        if (color == "rgba(0, 0, 0, 0)") 
-            return "transparent";
-        
-        return color;
-    }
-    
-    // parse string, returns Color
-    function parseColor(str) {
-        var result;
-
-        // Look for rgb(num,num,num)
-        if (result = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(str))
-            return new Color(parseInt(result[1], 10), parseInt(result[2], 10), parseInt(result[3], 10));
-        
-        // Look for rgba(num,num,num,num)
-        if (result = /rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(str))
-            return new Color(parseInt(result[1], 10), parseInt(result[2], 10), parseInt(result[3], 10), parseFloat(result[4]));
-            
-        // Look for rgb(num%,num%,num%)
-        if (result = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(str))
-            return new Color(parseFloat(result[1])*2.55, parseFloat(result[2])*2.55, parseFloat(result[3])*2.55);
-
-        // Look for rgba(num%,num%,num%,num)
-        if (result = /rgba\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(str))
-            return new Color(parseFloat(result[1])*2.55, parseFloat(result[2])*2.55, parseFloat(result[3])*2.55, parseFloat(result[4]));
-        
-        // Look for #a0b1c2
-        if (result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(str))
-            return new Color(parseInt(result[1], 16), parseInt(result[2], 16), parseInt(result[3], 16));
-
-        // Look for #fff
-        if (result = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(str))
-            return new Color(parseInt(result[1]+result[1], 16), parseInt(result[2]+result[2], 16), parseInt(result[3]+result[3], 16));
-
-        // Otherwise, we're most likely dealing with a named color
-        var name = $.trim(str).toLowerCase();
-        if (name == "transparent")
-            return new Color(255, 255, 255, 0);
-        else {
-            result = lookupColors[name];
-            return new Color(result[0], result[1], result[2]);
-        }
-    }
-        
-})(jQuery);
--- a/bitten/report/coverage.py
+++ b/bitten/report/coverage.py
@@ -61,14 +61,12 @@
 
         data = {'title': 'Test Coverage',
                 'data': [
-                    {'label': 'Lines of code', 'data': [[item[0], item[1]] for item in coverage]},
-                    {'label': 'Coverage', 'data': [[item[0], item[2]] for item in coverage]},
-                ],
-                'options': [
-                    {'xaxis': {'ticks': [[item[0], '[%s]' % item[0]] for item in coverage]}},
-                ],
-               }
-        return 'json.txt', {"json": data}
+                    [''] + ['[%s]' % item[0] for item in coverage],
+                    ['Lines of code'] + [item[1] for item in coverage],
+                    ['Coverage'] + [int(item[2]) for item in coverage]
+                ]}
+
+        return 'bitten_chart_coverage.html', data
 
 
 class TestCoverageSummarizer(Component):
--- a/bitten/report/lint.py
+++ b/bitten/report/lint.py
@@ -69,18 +69,15 @@
 
         data = {'title': 'Lint Problems by Type',
                 'data': [
-                    {'label': 'Total Problems', 'data': [[item[0], item[1]] for item in lint]},
-                    {'label': 'Convention', 'data': [[item[0], item[2]] for item in lint]},
-                    {'label': 'Error', 'data': [[item[0], item[3]] for item in lint]},
-                    {'label': 'Refactor', 'data': [[item[0], item[4]] for item in lint]},
-                    {'label': 'Warning', 'data': [[item[0], item[5]] for item in lint]},
-                ],
-                'options': [
-                    {'xaxis': {'ticks': [[item[0], '[%s]' % item[0]] for item in lint]}},
-                ],
-               }
+                    ['Revision'] + ['[%s]' % item[0] for item in lint],
+                    ['Total Problems'] + [item[1] for item in lint],
+                    ['Convention'] + [item[2] for item in lint],
+                    ['Error'] + [item[3] for item in lint],
+                    ['Refactor'] + [item[4] for item in lint],
+                    ['Warning'] + [item[5] for item in lint],
+                ]}
 
-        return 'json.txt', {"json": data}
+        return 'bitten_chart_lint.html', data
 
 
 class PyLintSummarizer(Component):
--- a/bitten/report/testing.py
+++ b/bitten/report/testing.py
@@ -61,16 +61,13 @@
 
         data = {'title': 'Unit Tests',
                 'data': [
-                    {'label': 'Total', 'data': [[item[0], item[1]] for item in tests]},
-                    {'label': 'Failures', 'data': [[item[0], item[2]] for item in tests]},
-                    {'label': 'Ignored', 'data': [[item[0], item[3]] for item in tests]},
-                ],
-                'options': [
-                    {'xaxis': {'ticks': [[item[0], '[%s]' % item[0]] for item in tests]}},
-                ],
-               }
+                    [''] + ['[%s]' % item[0] for item in tests],
+                    ['Total'] + [item[1] for item in tests],
+                    ['Failures'] + [item[2] for item in tests],
+                    ['Ignored'] + [item[3] for item in tests],
+                ]}
 
-        return 'json.txt', {"json": data}
+        return 'bitten_chart_tests.html', data
 
 
 class TestResultsSummarizer(Component):
new file mode 100755
--- /dev/null
+++ b/bitten/templates/bitten_chart_coverage.html
@@ -0,0 +1,38 @@
+<chart xmlns:py="http://genshi.edgewall.org/">
+ <chart_type>
+  <value>area</value>
+  <value>area</value>
+ </chart_type>
+
+ <axis_category size="10" orientation="diagonal_up"
+   skip="${len(data[0]) / 6}"/>
+ <axis_ticks value_ticks="false" category_ticks="true" major_thickness="1"
+   minor_thickness="0" major_color="000000" position="outside"/>
+
+ <chart_data>
+  <row py:for="idx, row in enumerate(data)">
+   <py:choose py:for="jdx, value in enumerate(row)">
+     <string py:when="not idx or not jdx">$value</string>
+     <number py:otherwise="">$value</number>
+   </py:choose>
+  </row>
+ </chart_data>
+
+ <chart_border color="999999" left_thickness="1" bottom_thickness="1"/>
+ <chart_grid_h alpha="5" color="666666" thickness="3"/>
+ <chart_pref line_thickness="2" point_shape="none"/>
+ <chart_value position="cursor"/>
+ <series_color>
+  <color>bbbbbb</color>
+  <color>9999ff</color>
+ </series_color>
+
+ <legend_label layout="vertical" alpha="60"/>
+ <legend_rect x="60" y="50" width="10"/>
+
+ <draw>
+  <text width="320" height="40" h_align="center" v_align="bottom"
+        size="12">$title</text>
+ </draw>
+
+</chart>
new file mode 100644
--- /dev/null
+++ b/bitten/templates/bitten_chart_lint.html
@@ -0,0 +1,39 @@
+<chart xmlns:py="http://genshi.edgewall.org/">
+
+ <chart_type>
+  <value>area</value>
+  <value>line</value>
+  <value>line</value>
+  <value>line</value>
+  <value>line</value>
+ </chart_type>
+
+ <axis_category size="10" orientation="diagonal_up"
+   skip="${len(data[0]) / 6}"/>
+ <axis_ticks value_ticks="false" category_ticks="true" major_thickness="1"
+   minor_thickness="0" major_color="000000" position="outside"/>
+
+ <chart_data>
+   <row py:for="idx, row in enumerate(data)">
+   <py:choose py:for="jdx, value in enumerate(row)">
+     <string py:when="not idx or not jdx">$value</string>
+     <number py:otherwise="">$value</number>
+   </py:choose>
+  </row>
+ </chart_data>
+
+ <chart_border color="999999" left_thickness="1" bottom_thickness="1"/>
+ <chart_grid_h alpha="5" color="666666" thickness="3"/>
+ <chart_pref line_thickness="2" point_shape="none"/>
+ <chart_value position="cursor"/>
+ <series_color>
+ </series_color>
+
+ <legend_label layout="vertical" alpha="60"/>
+ <legend_rect x="60" y="50" width="10"/>
+
+ <draw>
+  <text width="320" height="40" h_align="center" v_align="bottom" size="12" >$title</text>
+ </draw>
+
+</chart>
new file mode 100755
--- /dev/null
+++ b/bitten/templates/bitten_chart_tests.html
@@ -0,0 +1,40 @@
+<chart xmlns:py="http://genshi.edgewall.org/">
+ <chart_type>
+  <value>area</value>
+  <value>column</value>
+  <value>line</value>
+ </chart_type>
+
+ <axis_category size="10" orientation="diagonal_up"
+   skip="${len(data[0]) / 6}"/>
+ <axis_ticks value_ticks="false" category_ticks="true" major_thickness="2"
+   minor_thickness="0" major_color="000000" position="outside"/>
+
+ <chart_data>
+  <row py:for="idx, row in enumerate(data)">
+   <py:choose py:for="jdx, value in enumerate(row)">
+     <string py:when="not idx or not jdx">$value</string>
+     <number py:otherwise="">$value</number>
+   </py:choose>
+  </row>
+ </chart_data>
+
+ <chart_border color="999999" left_thickness="1" bottom_thickness="1"/>
+ <chart_grid_h alpha="5" color="666666" thickness="3"/>
+ <chart_pref line_thickness="2" point_shape="none"/>
+ <chart_value position="cursor"/>
+ <series_color>
+  <color>99dd99</color>
+  <color>ff0000</color>
+  <color>ffff00</color>
+ </series_color>
+
+ <legend_label layout="vertical" alpha="60"/>
+ <legend_rect x="60" y="50" width="10"/>
+
+ <draw>
+  <text width="320" height="40" h_align="center" v_align="bottom"
+        size="12">$title</text>
+ </draw>
+
+</chart>
--- a/bitten/templates/bitten_config.html
+++ b/bitten/templates/bitten_config.html
@@ -8,7 +8,6 @@
   <xi:include href="macros.html" />
   <head>
     <title>$title</title>
-    <script type="text/javascript" src="${chrome.htdocs_location}../bitten/jquery.flot.js"></script>
   </head>
   <body>
     <strong py:def="build_status(status)" class="status">
@@ -144,20 +143,14 @@
         </py:if></py:for>)</i>
       </div></py:if>
       <div id="charts"><py:for each="chart in config.charts">
-        <div id="chart_${chart.category}" style="width:600px;height:300px;"></div>
-        <script type="text/javascript">
-          $(document).ready(function() { 
-               
-              $.getJSON('${chart.href}', function(json) { 
-                   
-                  var plot_data = new Array(); 
-                  var plot_ticks = new Array(); 
-                   
-                  $.plot($("#chart_${chart.category}"), json.data, json.options); 
-              }); 
-          });
-        </script>
-        <br /></py:for>
+        <object type="application/x-shockwave-flash"
+                width="320" height="240"
+                data="${href.chrome('bitten', 'charts.swf')}">
+          <param name="movie" value="${href.chrome('bitten', 'charts.swf')}" />
+          <param name="FlashVars"
+                 value="library_path=${href.chrome('bitten')}&amp;xml_source=$chart.href${config.charts_license and '&amp;license='+config.charts_license or ''}" />
+          <param name="wmode" value="transparent" />
+        </object><br /></py:for>
       </div>
 
       <table py:if="config.platforms and config.builds"
deleted file mode 100644
--- a/bitten/templates/json.txt
+++ /dev/null
@@ -1,1 +0,0 @@
-${__import__("simplejson").dumps(json)}
--- a/bitten/web_ui.py
+++ b/bitten/web_ui.py
@@ -394,8 +394,7 @@
             for generator in ReportChartController(self.env).generators: 
                 for category in generator.get_supported_categories(): 
                     chart_generators.append({
-                        'href': req.href.build(config.name, 'chart/' + category),
-                        'category': category,
+                        'href': req.href.build(config.name, 'chart/' + category) 
                     })
             data['config']['charts'] = chart_generators 
             charts_license = self.config.get('bitten', 'charts_license')
@@ -538,7 +537,6 @@
 
         add_script(req, 'common/js/folding.js')
         add_script(req, 'bitten/tabset.js')
-        add_script(req, 'bitten/jquery.flot.js')
         add_stylesheet(req, 'bitten/bitten.css')
         return 'bitten_build.html', data, None
 
@@ -702,7 +700,7 @@
         else:
             raise TracError('Unknown report category "%s"' % category)
 
-        return tmpl, data, 'text/plain'
+        return tmpl, data, 'text/xml'
 
 
 class SourceFileLinkFormatter(Component):
Copyright (C) 2012-2017 Edgewall Software