jQWidgets Forums

jQuery UI Widgets Forums Navigation Tabs flot graph not displayed in jqxTab

This topic contains 6 replies, has 2 voices, and was last updated by  mim 12 years, 7 months ago.

Viewing 7 posts - 1 through 7 (of 7 total)
  • Author
  • flot graph not displayed in jqxTab #7890

    mim
    Participant

    Hi,

    tried desparately to get a Flot graph plotted in a tab via the following code.
    Can anybody have a look at it? Displayed is just the plot frame without any grid and plot…

    Kr,
    Michael

    This demo demonstrates the jqxDateTimeInput’s range selection mode. Click on a date to select the start date and then click on another cell to select the end date.
    <link rel="stylesheet" href="/jqx.base.css” type=”text/css” />
    <script type="text/javascript" src="/gettheme.js”>
    <script type="text/javascript" src="./jquery-1.8.0.min.js”>

    <script type="text/javascript" src="/jqxcore.js”>
    <script type="text/javascript" src="./jqxtabs.js”>

    <script language="javascript" type="text/javascript" src="/jquery.flot.js”>

    <?php
    if ( !mysql_connect("localhost",,) ) die ( “Error! “.mysql_error() );
    mysql_select_db(“Database”);
    $db = “Data”;
    $query = “SELECT * FROM $db ORDER BY dt”;
    $result = mysql_query($query);
    while ($row = mysql_fetch_array($result,MYSQL_ASSOC)) {
    $Array[] = array( strtotime($row[‘dt’])*1000, floatval($row[$val]) );
    } // Eo while
    ?>

    $(document).ready(function () {

    var dataset = {
    “Graph”:{ label: “Plot”,
    data:
    }
    }; // Eo dataset

    var options = {
    series: { lines: { show: true },
    points: { show: false },
    },
    legend: { noColumns: 2, position: “sw” },
    xaxis: { mode: “time”,
    timeformat: “%0d.%0m.%y %h:%M”,
    minTickSize: [30, “minute”],
    }
    }; // Eo options

    // create jqxtabs.
    $(‘#jqxtabs’).jqxTabs({ width: 600, height: 400 });

    $(“#jqxtab”).bind(‘tabsload’, function(event) {
    loadChart($(“#plot”), dataset, options);
    });

    function loadChart(chart, chartData,chartOptions) {
    if (chart != null) {
    var plot = $.plot(chart,chartData,chartOptions);
    }
    } // Eo loadChart

    });

    Plot
    Other

    Other Content

    flot graph not displayed in jqxTab #7894

    Peter Stoev
    Keymaster

    Hi mim,

    I suggest you to take a look at the source of this sample: integration.htm.

    Best Wishes,
    Peter Stoev

    jQWidgets Team
    http://www.jqwidgets.com

    flot graph not displayed in jqxTab #7931

    mim
    Participant

    Hi Peter,
    I did what you’ve proposed.
    But same tab behaviour as described about showed up: chart frame showed, but no grid/chart inside…
    The one and only .js I have added was the jquery.flot.js…
    Additionally I had to change the initChart function to my needs, changes marked in bold blue:

    So, what may hinder the plotting of the Chart?
    The mysql query and data provisiong works correctly without jqx framework…

    Kr,
    Michael

    This demo shows how to integrate jqxTabs with other widgets.

    -->

    $(document).ready(function () {
    var theme = getTheme();

    var initGrid = function () {

    var source =
    {
    datatype: "csv",
    datafields: [
    { name: 'Date' },
    { name: 'S&P 500' },
    { name: 'NASDAQ' }
    ],
    url: '../sampledata/nasdaq_vs_sp500.txt'
    };

    var dataAdapter = new $.jqx.dataAdapter(source, { async: false, loadError: function (xhr, status, error) {
    alert('Error loading "' + source.url + '" : ' + error); } });

    $("#jqxGrid").jqxGrid(
    {
    width: '100%',
    height: '84%',
    source: dataAdapter,
    theme: theme,
    columns: [
    { text: 'Date', datafield: 'Date', cellsformat: 'd', width: 250 },
    { text: 'S&P 500', datafield: 'S&P 500', width: 150 },
    { text: 'NASDAQ', datafield: 'NASDAQ' }
    ]
    });
    }

    var initChart = function () {




    var plotContainer = $("#flotChart"); // plot container

    var dataset = {
    "Chart":{
    label: "Chart",
    data:
    }
    }; // Eo dataset

    var options = {
    series: { lines: { show: true },
    points: { show: false },
    },
    legend: { noColumns: 1, position: "sw" },
    xaxis: { mode: "time"}
    }; // Eo options

    plot = $.plot(plotContainer, dataset, options);

    } //Eo initChart

    // init widgets.
    var initWidgets = function (tab) {
    switch (tab) {
    case 0:
    initGrid();
    break;
    case 1:
    initChart();
    break;
    }
    }

    $('#jqxTabs').jqxTabs({ width: 600, height: 560, theme: theme, initTabContent: initWidgets });
    });

    US Indexes

    NASDAQ compared to S&P 500

    The S&P 500 index finished 2011 less than a point away from where it ended 2010
    -- 0.04 points down to be exact. That's the smallest annual change in history. At
    its peak in April, the S&P had climbed more than 8%. But by October, at the lowest
    levels of the year, it was down more than 12%. The Nasdaq, meanwhile, lost 1.8%
    for the year.

    <div id='flotChart' style="width: 100%; height: 100%">

    Here is for information the jquery.flot.js:

    /*! Javascript plotting library for jQuery, v. 0.7.
    *
    * Released under the MIT license by IOLA, December 2007.
    *
    */

    // first an inline dependency, jquery.colorhelpers.js, we inline it here
    // for convenience

    /* Plugin for jQuery for working with colors.
    *
    * Version 1.1.
    *
    * Inspiration from jQuery color animation plugin by John Resig.
    *
    * Released under the MIT license by Ole Laursen, October 2009.
    *
    * Examples:
    *
    * $.color.parse("#fff").scale('rgb', 0.25).add('a', -0.5).toString()
    * var c = $.color.extract($("#mydiv"), 'background-color');
    * console.log(c.r, c.g, c.b, c.a);
    * $.color.make(100, 50, 25, 0.4).toString() // returns "rgba(100,50,25,0.4)"
    *
    * Note that .scale() and .add() return the same modified object
    * instead of making a new one.
    *
    * V. 1.1: Fix error handling so e.g. parsing an empty string does
    * produce a color rather than just crashing.
    */
    (function(B){B.color={};B.color.make=function(F,E,C,D){var G={};G.r=F||0;G.g=E||0;G.b=C||0;G.a=D!=null?D:1;G.add=function(J,I){for(var H=0;H<J.length;++H){G[J.charAt(H)]+=I}return G.normalize()};G.scale=function(J,I){for(var H=0;H=1){return"rgb("+[G.r,G.g,G.b].join(",")+")"}else{return"rgba("+[G.r,G.g,G.b,G.a].join(",")+")"}};G.normalize=function(){function H(J,K,I){return KI?I:K)}G.r=H(0,parseInt(G.r),255);G.g=H(0,parseInt(G.g),255);G.b=H(0,parseInt(G.b),255);G.a=H(0,G.a,1);return G};G.clone=function(){return B.color.make(G.r,G.b,G.g,G.a)};return G.normalize()};B.color.extract=function(D,C){var E;do{E=D.css(C).toLowerCase();if(E!=""&&E!="transparent"){break}D=D.parent()}while(!B.nodeName(D.get(0),"body"));if(E=="rgba(0, 0, 0, 0)"){E="transparent"}return B.color.parse(E)};B.color.parse=function(F){var E,C=B.color.make;if(E=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(F)){return C(parseInt(E[1],10),parseInt(E[2],10),parseInt(E[3],10))}if(E=/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(F)){return C(parseInt(E[1],10),parseInt(E[2],10),parseInt(E[3],10),parseFloat(E[4]))}if(E=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(F)){return C(parseFloat(E[1])*2.55,parseFloat(E[2])*2.55,parseFloat(E[3])*2.55)}if(E=/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(F)){return C(parseFloat(E[1])*2.55,parseFloat(E[2])*2.55,parseFloat(E[3])*2.55,parseFloat(E[4]))}if(E=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(F)){return C(parseInt(E[1],16),parseInt(E[2],16),parseInt(E[3],16))}if(E=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(F)){return C(parseInt(E[1]+E[1],16),parseInt(E[2]+E[2],16),parseInt(E[3]+E[3],16))}var D=B.trim(F).toLowerCase();if(D=="transparent"){return C(255,255,255,0)}else{E=A[D]||[0,0,0];return C(E[0],E[1],E[2])}};var A={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0]}})(jQuery);

    // the actual Flot code
    (function($) {
    function Plot(placeholder, data_, options_, plugins) {
    // 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: {
    show: null, // null = auto-detect, true = always, false = never
    position: "bottom", // or "top"
    mode: null, // null or "time"
    //NEW
    timezone: null, // "browser" for local to the client
    color: null, // base color, labels, ticks
    tickColor: null, // possibly different color of ticks, e.g. "rgba(0,0,0,0.15)"
    transform: null, // null or f: number -> number to transform axis
    inverseTransform: null, // if transform is set, this should be the inverse function
    min: null, // min. value to show, null means set automatically
    max: null, // max. value to show, null means set automatically
    autoscaleMargin: null, // margin in % to add if auto-setting min/max
    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,
    reserveSpace: null, // whether to reserve space even if axis isn't shown
    tickLength: null, // size in pixels of ticks, or "full" for whole line
    alignTicksWithAxis: null, // axis number or null for no sync

    // 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
    twelveHourClock: false // 12 or 24 time in time mode
    },
    yaxis: {
    autoscaleMargin: 0.02,
    position: "left" // or "right"
    },
    xaxes: [],
    yaxes: [],
    series: {
    points: {
    show: false,
    radius: 3,
    lineWidth: 2, // in pixels
    fill: true,
    fillColor: "#ffffff",
    symbol: "circle" // or callback
    },
    lines: {
    // we don't put in show: false so we can see
    // whether lines were actively disabled
    lineWidth: 2, // in pixels
    fill: false,
    fillColor: null,
    steps: false
    },
    bars: {
    show: false,
    lineWidth: 2, // in pixels
    barWidth: 1, // in units of the x axis
    fill: true,
    fillColor: null,
    align: "left", // or "center"
    horizontal: false
    },
    shadowSize: 3
    },
    grid: {
    show: true,
    aboveData: false,
    color: "#545454", // primary color used for outline and labels
    backgroundColor: null, // null for transparent, else color
    borderColor: null, // set if different from the grid color
    tickColor: null, // color for the ticks, e.g. "rgba(0,0,0,0.15)"
    labelMargin: 5, // in pixels
    axisMargin: 8, // in pixels
    borderWidth: 2, // in pixels
    minBorderMargin: null, // in pixels, null means taken from points radius
    markings: null, // array of ranges or fn: axes -> array of ranges
    markingsColor: "#f4f4f4",
    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
    },
    hooks: {}
    },
    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,
    xaxes = [], yaxes = [],
    plotOffset = { left: 0, right: 0, top: 0, bottom: 0},
    canvasWidth = 0, canvasHeight = 0,
    plotWidth = 0, plotHeight = 0,
    hooks = {
    processOptions: [],
    processRawData: [],
    processDatapoints: [],
    drawSeries: [],
    draw: [],
    bindEvents: [],
    drawOverlay: [],
    shutdown: []
    },
    plot = this;

    // public functions
    plot.setData = setData;
    plot.setupGrid = setupGrid;
    plot.draw = draw;
    plot.getPlaceholder = function() { return placeholder; };
    plot.getCanvas = function() { return canvas; };
    plot.getPlotOffset = function() { return plotOffset; };
    plot.width = function () { return plotWidth; };
    plot.height = function () { return plotHeight; };
    plot.offset = function () {
    var o = eventHolder.offset();
    o.left += plotOffset.left;
    o.top += plotOffset.top;
    return o;
    };
    plot.getData = function () { return series; };
    plot.getAxes = function () {
    var res = {}, i;
    $.each(xaxes.concat(yaxes), function (_, axis) {
    if (axis)
    res[axis.direction + (axis.n != 1 ? axis.n : "") + "axis"] = axis;
    });
    return res;
    };
    plot.getXAxes = function () { return xaxes; };
    plot.getYAxes = function () { return yaxes; };
    plot.c2p = canvasToAxisCoords;
    plot.p2c = axisToCanvasCoords;
    plot.getOptions = function () { return options; };
    plot.highlight = highlight;
    plot.unhighlight = unhighlight;
    plot.triggerRedrawOverlay = triggerRedrawOverlay;
    plot.pointOffset = function(point) {
    return {
    left: parseInt(xaxes[axisNumber(point, "x") - 1].p2c(+point.x) + plotOffset.left),
    top: parseInt(yaxes[axisNumber(point, "y") - 1].p2c(+point.y) + plotOffset.top)
    };
    };
    plot.shutdown = shutdown;
    plot.resize = function () {
    getCanvasDimensions();
    resizeCanvas(canvas);
    resizeCanvas(overlay);
    };

    // public attributes
    plot.hooks = hooks;

    // initialize
    initPlugins(plot);
    parseOptions(options_);
    setupCanvases();
    setData(data_);
    setupGrid();
    draw();
    bindEvents();

    function executeHooks(hook, args) {
    args = [plot].concat(args);
    for (var i = 0; i < hook.length; ++i)
    hook[i].apply(this, args);
    }

    function initPlugins() {
    for (var i = 0; i < plugins.length; ++i) {
    var p = plugins[i];
    p.init(plot);
    if (p.options)
    $.extend(true, options, p.options);
    }
    }

    function parseOptions(opts) {
    var i;

    $.extend(true, options, opts);

    if (options.xaxis.color == null)
    options.xaxis.color = options.grid.color;
    if (options.yaxis.color == null)
    options.yaxis.color = options.grid.color;

    if (options.xaxis.tickColor == null) // backwards-compatibility
    options.xaxis.tickColor = options.grid.tickColor;
    if (options.yaxis.tickColor == null) // backwards-compatibility
    options.yaxis.tickColor = options.grid.tickColor;

    if (options.grid.borderColor == null)
    options.grid.borderColor = options.grid.color;
    if (options.grid.tickColor == null)
    options.grid.tickColor = $.color.parse(options.grid.color).scale('a', 0.22).toString();

    // fill in defaults in axes, copy at least always the
    // first as the rest of the code assumes it'll be there
    for (i = 0; i < Math.max(1, options.xaxes.length); ++i)
    options.xaxes[i] = $.extend(true, {}, options.xaxis, options.xaxes[i]);
    for (i = 0; i < Math.max(1, options.yaxes.length); ++i)
    options.yaxes[i] = $.extend(true, {}, options.yaxis, options.yaxes[i]);

    // backwards compatibility, to be removed in future
    if (options.xaxis.noTicks && options.xaxis.ticks == null)
    options.xaxis.ticks = options.xaxis.noTicks;
    if (options.yaxis.noTicks && options.yaxis.ticks == null)
    options.yaxis.ticks = options.yaxis.noTicks;
    if (options.x2axis) {
    options.xaxes[1] = $.extend(true, {}, options.xaxis, options.x2axis);
    options.xaxes[1].position = "top";
    }
    if (options.y2axis) {
    options.yaxes[1] = $.extend(true, {}, options.yaxis, options.y2axis);
    options.yaxes[1].position = "right";
    }
    if (options.grid.coloredAreas)
    options.grid.markings = options.grid.coloredAreas;
    if (options.grid.coloredAreasColor)
    options.grid.markingsColor = options.grid.coloredAreasColor;
    if (options.lines)
    $.extend(true, options.series.lines, options.lines);
    if (options.points)
    $.extend(true, options.series.points, options.points);
    if (options.bars)
    $.extend(true, options.series.bars, options.bars);
    if (options.shadowSize != null)
    options.series.shadowSize = options.shadowSize;

    // save options on axes for future reference
    for (i = 0; i < options.xaxes.length; ++i)
    getOrCreateAxis(xaxes, i + 1).options = options.xaxes[i];
    for (i = 0; i < options.yaxes.length; ++i)
    getOrCreateAxis(yaxes, i + 1).options = options.yaxes[i];

    // add hooks from options
    for (var n in hooks)
    if (options.hooks[n] && options.hooks[n].length)
    hooks[n] = hooks[n].concat(options.hooks[n]);

    executeHooks(hooks.processOptions, [options]);
    }

    function setData(d) {
    series = parseData(d);
    fillInSeriesOptions();
    processData();
    }

    function parseData(d) {
    var res = [];
    for (var i = 0; i < d.length; ++i) {
    var s = $.extend(true, {}, options.series);

    if (d[i].data != null) {
    s.data = d[i].data; // move the data instead of deep-copy
    delete d[i].data;

    $.extend(true, s, d[i]);

    d[i].data = s.data;
    }
    else
    s.data = d[i];
    res.push(s);
    }

    return res;
    }

    function axisNumber(obj, coord) {
    var a = obj[coord + "axis"];
    if (typeof a == "object") // if we got a real axis, extract number
    a = a.n;
    if (typeof a != "number")
    a = 1; // default to first axis
    return a;
    }

    function allAxes() {
    // return flat array without annoying null entries
    return $.grep(xaxes.concat(yaxes), function (a) { return a; });
    }

    function canvasToAxisCoords(pos) {
    // return an object with x/y corresponding to all used axes
    var res = {}, i, axis;
    for (i = 0; i < xaxes.length; ++i) {
    axis = xaxes[i];
    if (axis && axis.used)
    res["x" + axis.n] = axis.c2p(pos.left);
    }

    for (i = 0; i < yaxes.length; ++i) {
    axis = yaxes[i];
    if (axis && axis.used)
    res["y" + axis.n] = axis.c2p(pos.top);
    }

    if (res.x1 !== undefined)
    res.x = res.x1;
    if (res.y1 !== undefined)
    res.y = res.y1;

    return res;
    }

    function axisToCanvasCoords(pos) {
    // get canvas coords from the first pair of x/y found in pos
    var res = {}, i, axis, key;

    for (i = 0; i < xaxes.length; ++i) {
    axis = xaxes[i];
    if (axis && axis.used) {
    key = "x" + axis.n;
    if (pos[key] == null && axis.n == 1)
    key = "x";

    if (pos[key] != null) {
    res.left = axis.p2c(pos[key]);
    break;
    }
    }
    }

    for (i = 0; i < yaxes.length; ++i) {
    axis = yaxes[i];
    if (axis && axis.used) {
    key = "y" + axis.n;
    if (pos[key] == null && axis.n == 1)
    key = "y";

    if (pos[key] != null) {
    res.top = axis.p2c(pos[key]);
    break;
    }
    }
    }

    return res;
    }

    function getOrCreateAxis(axes, number) {
    if (!axes[number - 1])
    axes[number - 1] = {
    n: number, // save the number for future reference
    direction: axes == xaxes ? "x" : "y",
    options: $.extend(true, {}, axes == xaxes ? options.xaxis : options.yaxis)
    };

    return axes[number - 1];
    }

    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($.color.parse(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 = 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();

    // turn on lines automatically in case nothing is set
    if (s.lines.show == null) {
    var v, show = true;
    for (v in s)
    if (s[v] && s[v].show) {
    show = false;
    break;
    }
    if (show)
    s.lines.show = true;
    }

    // setup axes
    s.xaxis = getOrCreateAxis(xaxes, axisNumber(s, "x"));
    s.yaxis = getOrCreateAxis(yaxes, axisNumber(s, "y"));
    }
    }

    function processData() {
    var topSentry = Number.POSITIVE_INFINITY,
    bottomSentry = Number.NEGATIVE_INFINITY,
    fakeInfinity = Number.MAX_VALUE,
    i, j, k, m, length,
    s, points, ps, x, y, axis, val, f, p;

    function updateAxis(axis, min, max) {
    if (min axis.datamax && max != fakeInfinity)
    axis.datamax = max;
    }

    $.each(allAxes(), function (_, axis) {
    // init axis
    axis.datamin = topSentry;
    axis.datamax = bottomSentry;
    axis.used = false;
    });

    for (i = 0; i < series.length; ++i) {
    s = series[i];
    s.datapoints = { points: [] };

    executeHooks(hooks.processRawData, [ s, s.data, s.datapoints ]);
    }

    // first pass: clean and copy data
    for (i = 0; i < series.length; ++i) {
    s = series[i];

    var data = s.data, format = s.datapoints.format;

    if (!format) {
    format = [];
    // find out how to copy
    format.push({ x: true, number: true, required: true });
    format.push({ y: true, number: true, required: true });

    if (s.bars.show || (s.lines.show && s.lines.fill)) {
    format.push({ y: true, number: true, required: false, defaultValue: 0 });
    if (s.bars.horizontal) {
    delete format[format.length - 1].y;
    format[format.length - 1].x = true;
    }
    }

    s.datapoints.format = format;
    }

    if (s.datapoints.pointsize != null)
    continue; // already filled in

    s.datapoints.pointsize = format.length;

    ps = s.datapoints.pointsize;
    points = s.datapoints.points;

    insertSteps = s.lines.show && s.lines.steps;
    s.xaxis.used = s.yaxis.used = true;

    for (j = k = 0; j < data.length; ++j, k += ps) {
    p = data[j];

    var nullify = p == null;
    if (!nullify) {
    for (m = 0; m < ps; ++m) {
    val = p[m];
    f = format[m];

    if (f) {
    if (f.number && val != null) {
    val = +val; // convert to number
    if (isNaN(val))
    val = null;
    else if (val == Infinity)
    val = fakeInfinity;
    else if (val == -Infinity)
    val = -fakeInfinity;
    }

    if (val == null) {
    if (f.required)
    nullify = true;

    if (f.defaultValue != null)
    val = f.defaultValue;
    }
    }

    points[k + m] = val;
    }
    }

    if (nullify) {
    for (m = 0; m 0
    && points[k - ps] != null
    && points[k - ps] != points[k]
    && points[k - ps + 1] != points[k + 1]) {
    // copy the point to make room for a middle point
    for (m = 0; m < ps; ++m)
    points[k + ps + m] = points[k + m];

    // middle point has same y
    points[k + 1] = points[k - ps + 1];

    // we've added a point, better reflect that
    k += ps;
    }
    }
    }
    }

    // give the hooks a chance to run
    for (i = 0; i < series.length; ++i) {
    s = series[i];

    executeHooks(hooks.processDatapoints, [ s, s.datapoints]);
    }

    // second pass: find datamax/datamin for auto-scaling
    for (i = 0; i < series.length; ++i) {
    s = series[i];
    points = s.datapoints.points,
    ps = s.datapoints.pointsize;

    var xmin = topSentry, ymin = topSentry,
    xmax = bottomSentry, ymax = bottomSentry;

    for (j = 0; j < points.length; j += ps) {
    if (points[j] == null)
    continue;

    for (m = 0; m < ps; ++m) {
    val = points[j + m];
    f = format[m];
    if (!f || val == fakeInfinity || val == -fakeInfinity)
    continue;

    if (f.x) {
    if (val xmax)
    xmax = val;
    }
    if (f.y) {
    if (val ymax)
    ymax = val;
    }
    }
    }

    if (s.bars.show) {
    // make sure we got room for the bar on the dancing floor
    var delta = s.bars.align == "left" ? 0 : -s.bars.barWidth/2;
    if (s.bars.horizontal) {
    ymin += delta;
    ymax += delta + s.bars.barWidth;
    }
    else {
    xmin += delta;
    xmax += delta + s.bars.barWidth;
    }
    }

    updateAxis(s.xaxis, xmin, xmax);
    updateAxis(s.yaxis, ymin, ymax);
    }

    $.each(allAxes(), function (_, axis) {
    if (axis.datamin == topSentry)
    axis.datamin = null;
    if (axis.datamax == bottomSentry)
    axis.datamax = null;
    });
    }

    function makeCanvas(skipPositioning, cls) {
    var c = document.createElement('canvas');
    c.className = cls;
    c.width = canvasWidth;
    c.height = canvasHeight;

    if (!skipPositioning)
    $(c).css({ position: 'absolute', left: 0, top: 0 });

    $(c).appendTo(placeholder);

    if (!c.getContext) // excanvas hack
    c = window.G_vmlCanvasManager.initElement(c);

    // used for resetting in case we get replotted
    c.getContext("2d").save();

    return c;
    }

    function getCanvasDimensions() {
    canvasWidth = placeholder.width();
    canvasHeight = placeholder.height();

    if (canvasWidth <= 0 || canvasHeight <= 0)
    throw "Invalid dimensions for plot, width = " + canvasWidth + ", height = " + canvasHeight;
    }

    function resizeCanvas(c) {
    // resizing should reset the state (excanvas seems to be
    // buggy though)
    if (c.width != canvasWidth)
    c.width = canvasWidth;

    if (c.height != canvasHeight)
    c.height = canvasHeight;

    // so try to get back to the initial state (even if it's
    // gone now, this should be safe according to the spec)
    var cctx = c.getContext("2d");
    cctx.restore();

    // and save again
    cctx.save();
    }

    function setupCanvases() {
    var reused,
    existingCanvas = placeholder.children("canvas.base"),
    existingOverlay = placeholder.children("canvas.overlay");

    if (existingCanvas.length == 0 || existingOverlay == 0) {
    // init everything

    placeholder.html(""); // make sure placeholder is clear

    placeholder.css({ padding: 0 }); // padding messes up the positioning

    if (placeholder.css("position") == 'static')
    placeholder.css("position", "relative"); // for positioning labels and overlay

    getCanvasDimensions();

    canvas = makeCanvas(true, "base");
    overlay = makeCanvas(false, "overlay"); // overlay canvas for interactive features

    reused = false;
    }
    else {
    // reuse existing elements

    canvas = existingCanvas.get(0);
    overlay = existingOverlay.get(0);

    reused = true;
    }

    ctx = canvas.getContext("2d");
    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]);

    if (reused) {
    // run shutdown in the old plot object
    placeholder.data("plot").shutdown();

    // reset reused canvases
    plot.resize();

    // make sure overlay pixels are cleared (canvas is cleared when we redraw)
    octx.clearRect(0, 0, canvasWidth, canvasHeight);

    // then whack any remaining obvious garbage left
    eventHolder.unbind();
    placeholder.children().not([canvas, overlay]).remove();
    }

    // save in case we get replotted
    placeholder.data("plot", plot);
    }

    function bindEvents() {
    // bind events
    if (options.grid.hoverable) {
    eventHolder.mousemove(onMouseMove);
    eventHolder.mouseleave(onMouseLeave);
    }

    if (options.grid.clickable)
    eventHolder.click(onClick);

    executeHooks(hooks.bindEvents, [eventHolder]);
    }

    function shutdown() {
    if (redrawTimeout)
    clearTimeout(redrawTimeout);

    eventHolder.unbind("mousemove", onMouseMove);
    eventHolder.unbind("mouseleave", onMouseLeave);
    eventHolder.unbind("click", onClick);

    executeHooks(hooks.shutdown, [eventHolder]);
    }

    function setTransformationHelpers(axis) {
    // set helper functions on the axis, assumes plot area
    // has been computed already

    function identity(x) { return x; }

    var s, m, t = axis.options.transform || identity,
    it = axis.options.inverseTransform;

    // precompute how much the axis is scaling a point
    // in canvas space
    if (axis.direction == "x") {
    s = axis.scale = plotWidth / Math.abs(t(axis.max) - t(axis.min));
    m = Math.min(t(axis.max), t(axis.min));
    }
    else {
    s = axis.scale = plotHeight / Math.abs(t(axis.max) - t(axis.min));
    s = -s;
    m = Math.max(t(axis.max), t(axis.min));
    }

    // data point to canvas coordinate
    if (t == identity) // slight optimization
    axis.p2c = function (p) { return (p - m) * s; };
    else
    axis.p2c = function (p) { return (t(p) - m) * s; };
    // canvas coordinate to data point
    if (!it)
    axis.c2p = function (c) { return m + c / s; };
    else
    axis.c2p = function (c) { return it(m + c / s); };
    }

    function measureTickLabels(axis) {
    var opts = axis.options, i, ticks = axis.ticks || [], labels = [],
    l, w = opts.labelWidth, h = opts.labelHeight, dummyDiv;

    function makeDummyDiv(labels, width) {
    return $('

    ' +
    '

    '
    + labels.join("") + '

    ')
    .appendTo(placeholder);
    }

    if (axis.direction == "x") {
    // to avoid measuring the widths of the labels (it's slow), 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 (w == null)
    w = Math.floor(canvasWidth / (ticks.length > 0 ? ticks.length : 1));

    // measure x label heights
    if (h == null) {
    labels = [];
    for (i = 0; i < ticks.length; ++i) {
    l = ticks[i].label;
    if (l)
    labels.push('

    ' + l + '

    ');
    }

    if (labels.length > 0) {
    // stick them all in the same div and measure
    // collective height
    labels.push('

    ');
    dummyDiv = makeDummyDiv(labels, "width:10000px;");
    h = dummyDiv.height();
    dummyDiv.remove();
    }
    }
    }
    else if (w == null || h == null) {
    // calculate y label dimensions
    for (i = 0; i < ticks.length; ++i) {
    l = ticks[i].label;
    if (l)
    labels.push('

    ' + l + '

    ');
    }

    if (labels.length > 0) {
    dummyDiv = makeDummyDiv(labels, "");
    if (w == null)
    w = dummyDiv.children().width();
    if (h == null)
    h = dummyDiv.find("div.tickLabel").height();
    dummyDiv.remove();
    }
    }

    if (w == null)
    w = 0;
    if (h == null)
    h = 0;

    axis.labelWidth = w;
    axis.labelHeight = h;
    }

    function allocateAxisBoxFirstPhase(axis) {
    // find the bounding box of the axis by looking at label
    // widths/heights and ticks, make room by diminishing the
    // plotOffset

    var lw = axis.labelWidth,
    lh = axis.labelHeight,
    pos = axis.options.position,
    tickLength = axis.options.tickLength,
    axismargin = options.grid.axisMargin,
    padding = options.grid.labelMargin,
    all = axis.direction == "x" ? xaxes : yaxes,
    index;

    // determine axis margin
    var samePosition = $.grep(all, function (a) {
    return a && a.options.position == pos && a.reserveSpace;
    });
    if ($.inArray(axis, samePosition) == samePosition.length - 1)
    axismargin = 0; // outermost

    // determine tick length - if we're innermost, we can use "full"
    if (tickLength == null)
    tickLength = "full";

    var sameDirection = $.grep(all, function (a) {
    return a && a.reserveSpace;
    });

    var innermost = $.inArray(axis, sameDirection) == 0;
    if (!innermost && tickLength == "full")
    tickLength = 5;

    if (!isNaN(+tickLength))
    padding += +tickLength;

    // compute box
    if (axis.direction == "x") {
    lh += padding;

    if (pos == "bottom") {
    plotOffset.bottom += lh + axismargin;
    axis.box = { top: canvasHeight - plotOffset.bottom, height: lh };
    }
    else {
    axis.box = { top: plotOffset.top + axismargin, height: lh };
    plotOffset.top += lh + axismargin;
    }
    }
    else {
    lw += padding;

    if (pos == "left") {
    axis.box = { left: plotOffset.left + axismargin, width: lw };
    plotOffset.left += lw + axismargin;
    }
    else {
    plotOffset.right += lw + axismargin;
    axis.box = { left: canvasWidth - plotOffset.right, width: lw };
    }
    }

    // save for future reference
    axis.position = pos;
    axis.tickLength = tickLength;
    axis.box.padding = padding;
    axis.innermost = innermost;
    }

    function allocateAxisBoxSecondPhase(axis) {
    // set remaining bounding box coordinates
    if (axis.direction == "x") {
    axis.box.left = plotOffset.left;
    axis.box.width = plotWidth;
    }
    else {
    axis.box.top = plotOffset.top;
    axis.box.height = plotHeight;
    }
    }

    function setupGrid() {
    var i, axes = allAxes();

    // first calculate the plot and axis box dimensions

    $.each(axes, function (_, axis) {
    axis.show = axis.options.show;
    if (axis.show == null)
    axis.show = axis.used; // by default an axis is visible if it's got data

    axis.reserveSpace = axis.show || axis.options.reserveSpace;

    setRange(axis);
    });

    allocatedAxes = $.grep(axes, function (axis) { return axis.reserveSpace; });

    plotOffset.left = plotOffset.right = plotOffset.top = plotOffset.bottom = 0;
    if (options.grid.show) {
    $.each(allocatedAxes, function (_, axis) {
    // make the ticks
    setupTickGeneration(axis);
    setTicks(axis);
    snapRangeToTicks(axis, axis.ticks);

    // find labelWidth/Height for axis
    measureTickLabels(axis);
    });

    // with all dimensions in house, we can compute the
    // axis boxes, start from the outside (reverse order)
    for (i = allocatedAxes.length - 1; i >= 0; --i)
    allocateAxisBoxFirstPhase(allocatedAxes[i]);

    // make sure we've got enough space for things that
    // might stick out
    var minMargin = options.grid.minBorderMargin;
    if (minMargin == null) {
    minMargin = 0;
    for (i = 0; i < series.length; ++i)
    minMargin = Math.max(minMargin, series[i].points.radius + series[i].points.lineWidth/2);
    }

    for (var a in plotOffset) {
    plotOffset[a] += options.grid.borderWidth;
    plotOffset[a] = Math.max(minMargin, plotOffset[a]);
    }
    }

    plotWidth = canvasWidth - plotOffset.left - plotOffset.right;
    plotHeight = canvasHeight - plotOffset.bottom - plotOffset.top;

    // now we got the proper plotWidth/Height, we can compute the scaling
    $.each(axes, function (_, axis) {
    setTransformationHelpers(axis);
    });

    if (options.grid.show) {
    $.each(allocatedAxes, function (_, axis) {
    allocateAxisBoxSecondPhase(axis);
    });

    insertAxisLabels();
    }

    insertLegend();
    }

    function setRange(axis) {
    var opts = axis.options,
    min = +(opts.min != null ? opts.min : axis.datamin),
    max = +(opts.max != null ? opts.max : axis.datamax),
    delta = max - min;

    if (delta == 0.0) {
    // degenerate case
    var widen = max == 0 ? 1 : 0.01;

    if (opts.min == null)
    min -= widen;
    // always widen max if we couldn't widen min to ensure we
    // don't fall into min == max which doesn't work
    if (opts.max == null || opts.min != null)
    max += widen;
    }
    else {
    // consider autoscaling
    var margin = opts.autoscaleMargin;
    if (margin != null) {
    if (opts.min == null) {
    min -= delta * margin;
    // make sure we don't go below zero if all values
    // are positive
    if (min = 0)
    min = 0;
    }
    if (opts.max == null) {
    max += delta * margin;
    if (max > 0 && axis.datamax != null && axis.datamax 0)
    noTicks = opts.ticks;
    else
    // heuristic based on the model a*sqrt(x) fitted to
    // some data points that seemed reasonable
    noTicks = 0.3 * Math.sqrt(axis.direction == "x" ? canvasWidth : canvasHeight);

    var delta = (axis.max - axis.min) / noTicks,
    size, generator, unit, formatter, i, magn, norm;

    if (opts.mode == "time") {
    // pretty handling of time

    // To have a consistent view of time based data independent of which time zone the
    // client happens to be in we need a date-like object intependent of time zones. This
    // is done through a wrapper that only calls the UTC versions of the accessor methods.
    function makeUtcWrapper(d) {

    function addProxyMethod(sourceObj, sourceMethod, targetObj, targetMethod) {
    sourceObj[sourceMethod] = function() {
    return targetObj[targetMethod].apply(targetObj, arguments);
    }
    };
    var utc = {
    date: d
    };
    addProxyMethod(utc, "getTime", d, "getTime");
    addProxyMethod(utc, "setTime", d, "setTime");
    var props = [ "Date", "Day", "FullYear", "Hours", "Milliseconds", "Minutes", "Month", "Seconds" ];
    for (var p = 0; p < props.length; p++) {
    addProxyMethod(utc, "get" + props[p], d, "getUTC" + props[p]);
    addProxyMethod(utc, "set" + props[p], d, "setUTC" + props[p]);
    }
    return utc;
    };

    // select time zone strategy. This returns a date-like object tied to the desired timezone
    function dateGenerator(ts) {

    if (opts.timezone == "browser") {
    return new Date(ts);
    } else if (!opts.timezone || opts.timezone == "utc") {
    return makeUtcWrapper(new Date(ts));
    } else if (typeof timezoneJS != "undefined" && typeof timezoneJS.Date != "undefined") {
    var d = new timezoneJS.Date();
    // timezone-js is fickle, so be sure to set the time zone before setting the time.
    d.setTimezone(opts.timezone);
    d.setTime(ts);
    return d;
    } else {
    return makeUtcWrapper(new Date(ts));
    }
    }

    // map of app. size of time units in milliseconds
    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 (opts.minTickSize != null) {
    if (typeof opts.tickSize == "number")
    minSize = opts.tickSize;
    else
    minSize = opts.minTickSize[0] * timeUnitSize[opts.minTickSize[1]];
    }

    for (var i = 0; i < spec.length - 1; ++i)
    if (delta = 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 = timeUnitSize.minute)
    // d.setUTCSeconds(0);
    d.setSeconds(0);
    if (step >= timeUnitSize.hour)
    // d.setUTCMinutes(0);
    d.setMinutes(0);
    if (step >= timeUnitSize.day)
    // d.setUTCHours(0);
    d.setHours(0);
    if (step >= timeUnitSize.day * 4)
    // d.setUTCDate(1);
    d.setDate(1);
    if (step >= timeUnitSize.year)
    // d.setUTCMonth(0);
    d.setMonth(0);

    /*

    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);
    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);
    d.setDate(1);
    var start = d.getTime();
    // d.setUTCMonth(d.getUTCMonth() + 1);
    d.setMonth(d.getMonth() + 1);
    var end = d.getTime();
    d.setTime(v + carry * timeUnitSize.hour + (end - start) * tickSize);
    // carry = d.getUTCHours();
    // d.setUTCHours(0);
    carry = d.getHours();
    d.setHours(0);

    }
    else
    // d.setUTCMonth(d.getUTCMonth() + tickSize);
    d.setMonth(d.getMonth() + tickSize);
    }
    else if (unit == "year") {
    // d.setUTCFullYear(d.getUTCFullYear() + tickSize);
    d.setFullYear(d.getFullYear() + tickSize);
    }
    else
    d.setTime(v + step);

    } while (v < axis.max && v != prev);

    return ticks;
    };

    formatter = function (v, axis) {
    // var d = new Date(v);
    var d = dateGenerator(v);

    // first check global format
    if (opts.timeformat != null)
    return $.plot.formatDate(d, opts.timeformat, opts.monthNames);

    var t = axis.tickSize[0] * timeUnitSize[axis.tickSize[1]];
    var span = axis.max - axis.min;
    var suffix = (opts.twelveHourClock) ? " %p" : "";

    if (t < timeUnitSize.minute)
    fmt = "%h:%M:%S" + suffix;
    else if (t < timeUnitSize.day) {
    if (span < 2 * timeUnitSize.day)
    fmt = "%h:%M" + suffix;
    else
    fmt = "%b %d %h:%M" + suffix;
    }
    else if (t < timeUnitSize.month)
    fmt = "%b %d";
    else if (t < timeUnitSize.year) {
    if (span 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 2.25 && (maxDec == null || dec + 1 <= maxDec)) {
    size = 2.5;
    ++dec;
    }
    }
    else if (norm < 7.5)
    size = 5;
    else
    size = 10;

    size *= magn;

    if (opts.minTickSize != null && size < opts.minTickSize)
    size = opts.minTickSize;

    axis.tickDecimals = Math.max(0, maxDec != null ? maxDec : dec);
    axis.tickSize = opts.tickSize || size;

    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);
    ++i;
    } while (v 0) {
    if (opts.min == null)
    axis.min = Math.min(axis.min, niceTicks[0]);
    if (opts.max == null && niceTicks.length > 1)
    axis.max = Math.max(axis.max, niceTicks[niceTicks.length - 1]);
    }

    generator = function (axis) {
    // copy ticks, scaled to this axis
    var ticks = [], v, i;
    for (i = 0; i 1 && /\..*0$/.test((ts[1] - ts[0]).toFixed(extraDec))))
    axis.tickDecimals = extraDec;
    }
    }
    }

    axis.tickGenerator = generator;
    if ($.isFunction(opts.tickFormatter))
    axis.tickFormatter = function (v, axis) { return "" + opts.tickFormatter(v, axis); };
    else
    axis.tickFormatter = formatter;
    }

    function setTicks(axis) {
    var oticks = axis.options.ticks, ticks = [];
    if (oticks == null || (typeof oticks == "number" && oticks > 0))
    ticks = axis.tickGenerator(axis);
    else if (oticks) {
    if ($.isFunction(oticks))
    // generate the ticks
    ticks = oticks({ min: axis.min, max: axis.max });
    else
    ticks = oticks;
    }

    // clean up/labelify the supplied ticks, copy them over
    var i, v;
    axis.ticks = [];
    for (i = 0; i 1)
    label = t[1];
    }
    else
    v = +t;
    if (label == null)
    label = axis.tickFormatter(v, axis);
    if (!isNaN(v))
    axis.ticks.push({ v: v, label: label });
    }
    }

    function snapRangeToTicks(axis, ticks) {
    if (axis.options.autoscaleMargin && ticks.length > 0) {
    // snap to ticks
    if (axis.options.min == null)
    axis.min = Math.min(axis.min, ticks[0].v);
    if (axis.options.max == null && ticks.length > 1)
    axis.max = Math.max(axis.max, ticks[ticks.length - 1].v);
    }
    }

    function draw() {
    ctx.clearRect(0, 0, canvasWidth, canvasHeight);

    var grid = options.grid;

    // draw background, if any
    if (grid.show && grid.backgroundColor)
    drawBackground();

    if (grid.show && !grid.aboveData)
    drawGrid();

    for (var i = 0; i < series.length; ++i) {
    executeHooks(hooks.drawSeries, [ctx, series[i]]);
    drawSeries(series[i]);
    }

    executeHooks(hooks.draw, [ctx]);

    if (grid.show && grid.aboveData)
    drawGrid();
    }

    function extractRange(ranges, coord) {
    var axis, from, to, key, axes = allAxes();

    for (i = 0; i to) {
    var tmp = from;
    from = to;
    to = tmp;
    }

    return { from: from, to: to, axis: axis };
    }

    function drawBackground() {
    ctx.save();
    ctx.translate(plotOffset.left, plotOffset.top);

    ctx.fillStyle = getColorOrGradient(options.grid.backgroundColor, plotHeight, 0, "rgba(255, 255, 255, 0)");
    ctx.fillRect(0, 0, plotWidth, plotHeight);
    ctx.restore();
    }

    function drawGrid() {
    var i;

    ctx.save();
    ctx.translate(plotOffset.left, plotOffset.top);

    // draw markings
    var markings = options.grid.markings;
    if (markings) {
    if ($.isFunction(markings)) {
    var axes = plot.getAxes();
    // xmin etc. is backwards compatibility, to be
    // removed in the future
    axes.xmin = axes.xaxis.min;
    axes.xmax = axes.xaxis.max;
    axes.ymin = axes.yaxis.min;
    axes.ymax = axes.yaxis.max;

    markings = markings(axes);
    }

    for (i = 0; i < markings.length; ++i) {
    var m = markings[i],
    xrange = extractRange(m, "x"),
    yrange = extractRange(m, "y");

    // 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.max ||
    yrange.to 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.beginPath();
    ctx.strokeStyle = m.color || options.grid.markingsColor;
    ctx.lineWidth = m.lineWidth || options.grid.markingsLineWidth;
    ctx.moveTo(xrange.from, yrange.from);
    ctx.lineTo(xrange.to, yrange.to);
    ctx.stroke();
    }
    else {
    // fill area
    ctx.fillStyle = m.color || options.grid.markingsColor;
    ctx.fillRect(xrange.from, yrange.to,
    xrange.to - xrange.from,
    yrange.from - yrange.to);
    }
    }
    }

    // draw the ticks
    var axes = allAxes(), bw = options.grid.borderWidth;

    for (var j = 0; j < axes.length; ++j) {
    var axis = axes[j], box = axis.box,
    t = axis.tickLength, x, y, xoff, yoff;
    if (!axis.show || axis.ticks.length == 0)
    continue

    ctx.strokeStyle = axis.options.tickColor || $.color.parse(axis.options.color).scale('a', 0.22).toString();
    ctx.lineWidth = 1;

    // find the edges
    if (axis.direction == "x") {
    x = 0;
    if (t == "full")
    y = (axis.position == "top" ? 0 : plotHeight);
    else
    y = box.top - plotOffset.top + (axis.position == "top" ? box.height : 0);
    }
    else {
    y = 0;
    if (t == "full")
    x = (axis.position == "left" ? 0 : plotWidth);
    else
    x = box.left - plotOffset.left + (axis.position == "left" ? box.width : 0);
    }

    // draw tick bar
    if (!axis.innermost) {
    ctx.beginPath();
    xoff = yoff = 0;
    if (axis.direction == "x")
    xoff = plotWidth;
    else
    yoff = plotHeight;

    if (ctx.lineWidth == 1) {
    x = Math.floor(x) + 0.5;
    y = Math.floor(y) + 0.5;
    }

    ctx.moveTo(x, y);
    ctx.lineTo(x + xoff, y + yoff);
    ctx.stroke();
    }

    // draw ticks
    ctx.beginPath();
    for (i = 0; i < axis.ticks.length; ++i) {
    var v = axis.ticks[i].v;

    xoff = yoff = 0;

    if (v axis.max
    // skip those lying on the axes if we got a border
    || (t == "full" && bw > 0
    && (v == axis.min || v == axis.max)))
    continue;

    if (axis.direction == "x") {
    x = axis.p2c(v);
    yoff = t == "full" ? -plotHeight : t;

    if (axis.position == "top")
    yoff = -yoff;
    }
    else {
    y = axis.p2c(v);
    xoff = t == "full" ? -plotWidth : t;

    if (axis.position == "left")
    xoff = -xoff;
    }

    if (ctx.lineWidth == 1) {
    if (axis.direction == "x")
    x = Math.floor(x) + 0.5;
    else
    y = Math.floor(y) + 0.5;
    }

    ctx.moveTo(x, y);
    ctx.lineTo(x + xoff, y + yoff);
    }

    ctx.stroke();
    }

    // draw border
    if (bw) {
    ctx.lineWidth = bw;
    ctx.strokeStyle = options.grid.borderColor;
    ctx.strokeRect(-bw/2, -bw/2, plotWidth + bw, plotHeight + bw);
    }

    ctx.restore();
    }

    function insertAxisLabels() {
    placeholder.find(".tickLabels").remove();

    var html = ['

    '];

    var axes = allAxes();
    for (var j = 0; j < axes.length; ++j) {
    var axis = axes[j], box = axis.box;
    if (!axis.show)
    continue;
    //debug: html.push('

    ')
    html.push('

    ');
    for (var i = 0; i < axis.ticks.length; ++i) {
    var tick = axis.ticks[i];
    if (!tick.label || tick.v axis.max)
    continue;

    var pos = {}, align;

    if (axis.direction == "x") {
    align = "center";
    pos.left = Math.round(plotOffset.left + axis.p2c(tick.v) - axis.labelWidth/2);
    if (axis.position == "bottom")
    pos.top = box.top + box.padding;
    else
    pos.bottom = canvasHeight - (box.top + box.height - box.padding);
    }
    else {
    pos.top = Math.round(plotOffset.top + axis.p2c(tick.v) - axis.labelHeight/2);
    if (axis.position == "left") {
    pos.right = canvasWidth - (box.left + box.width - box.padding)
    align = "right";
    }
    else {
    pos.left = box.left + box.padding;
    align = "left";
    }
    }

    pos.width = axis.labelWidth;

    var style = ["position:absolute", "text-align:" + align ];
    for (var a in pos)
    style.push(a + ":" + pos[a] + "px")

    html.push('

    ' + tick.label + '

    ');
    }
    html.push('

    ');
    }

    html.push('

    ');

    placeholder.append(html.join(""));
    }

    function drawSeries(series) {
    if (series.lines.show)
    drawSeriesLines(series);
    if (series.bars.show)
    drawSeriesBars(series);
    if (series.points.show)
    drawSeriesPoints(series);
    }

    function drawSeriesLines(series) {
    function plotLine(datapoints, xoffset, yoffset, axisx, axisy) {
    var points = datapoints.points,
    ps = datapoints.pointsize,
    prevx = null, prevy = null;

    ctx.beginPath();
    for (var i = ps; i < points.length; i += ps) {
    var x1 = points[i - ps], y1 = points[i - ps + 1],
    x2 = points[i], y2 = points[i + 1];

    if (x1 == null || x2 == null)
    continue;

    // 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 = 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 = x2 && x1 > axisx.max) {
    if (x2 > axisx.max)
    continue;
    y1 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1;
    x1 = axisx.max;
    }
    else if (x2 >= x1 && x2 > axisx.max) {
    if (x1 > axisx.max)
    continue;
    y2 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1;
    x2 = axisx.max;
    }

    if (x1 != prevx || y1 != prevy)
    ctx.moveTo(axisx.p2c(x1) + xoffset, axisy.p2c(y1) + yoffset);

    prevx = x2;
    prevy = y2;
    ctx.lineTo(axisx.p2c(x2) + xoffset, axisy.p2c(y2) + yoffset);
    }
    ctx.stroke();
    }

    function plotLineArea(datapoints, axisx, axisy) {
    var points = datapoints.points,
    ps = datapoints.pointsize,
    bottom = Math.min(Math.max(0, axisy.min), axisy.max),
    i = 0, top, areaOpen = false,
    ypos = 1, segmentStart = 0, segmentEnd = 0;

    // we process each segment in two turns, first forward
    // direction to sketch out top, then once we hit the
    // end we go backwards to sketch the bottom
    while (true) {
    if (ps > 0 && i > points.length + ps)
    break;

    i += ps; // ps is negative if going backwards

    var x1 = points[i - ps],
    y1 = points[i - ps + ypos],
    x2 = points[i], y2 = points[i + ypos];

    if (areaOpen) {
    if (ps > 0 && x1 != null && x2 == null) {
    // at turning point
    segmentEnd = i;
    ps = -ps;
    ypos = 2;
    continue;
    }

    if (ps < 0 && i == segmentStart + ps) {
    // done with the reverse sweep
    ctx.fill();
    areaOpen = false;
    ps = -ps;
    ypos = 1;
    i = segmentStart = segmentEnd + ps;
    continue;
    }
    }

    if (x1 == null || x2 == null)
    continue;

    // clip x values

    // clip with xmin
    if (x1 <= x2 && x1 < axisx.min) {
    if (x2 < axisx.min)
    continue;
    y1 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1;
    x1 = axisx.min;
    }
    else if (x2 <= x1 && x2 < axisx.min) {
    if (x1 = x2 && x1 > axisx.max) {
    if (x2 > axisx.max)
    continue;
    y1 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1;
    x1 = axisx.max;
    }
    else if (x2 >= x1 && x2 > axisx.max) {
    if (x1 > axisx.max)
    continue;
    y2 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1;
    x2 = axisx.max;
    }

    if (!areaOpen) {
    // open area
    ctx.beginPath();
    ctx.moveTo(axisx.p2c(x1), axisy.p2c(bottom));
    areaOpen = true;
    }

    // now first check the case where both is outside
    if (y1 >= axisy.max && y2 >= axisy.max) {
    ctx.lineTo(axisx.p2c(x1), axisy.p2c(axisy.max));
    ctx.lineTo(axisx.p2c(x2), axisy.p2c(axisy.max));
    continue;
    }
    else if (y1 <= axisy.min && y2 <= axisy.min) {
    ctx.lineTo(axisx.p2c(x1), axisy.p2c(axisy.min));
    ctx.lineTo(axisx.p2c(x2), axisy.p2c(axisy.min));
    continue;
    }

    // else it's a bit more complicated, there might
    // be a flat maxed out rectangle first, then a
    // triangular cutout or reverse; to find these
    // keep track of the current x values
    var x1old = x1, x2old = x2;

    // clip the y values, without shortcutting, we
    // go through all cases in turn

    // clip with ymin
    if (y1 <= y2 && y1 = axisy.min) {
    x1 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1;
    y1 = axisy.min;
    }
    else if (y2 <= y1 && y2 = axisy.min) {
    x2 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1;
    y2 = axisy.min;
    }

    // clip with ymax
    if (y1 >= y2 && y1 > axisy.max && y2 = y1 && y2 > axisy.max && y1 0 && sw > 0) {
    // draw shadow as a thick and thin line with transparency
    ctx.lineWidth = sw;
    ctx.strokeStyle = "rgba(0,0,0,0.1)";
    // position shadow at angle from the mid of line
    var angle = Math.PI/18;
    plotLine(series.datapoints, Math.sin(angle) * (lw/2 + sw/2), Math.cos(angle) * (lw/2 + sw/2), series.xaxis, series.yaxis);
    ctx.lineWidth = sw/2;
    plotLine(series.datapoints, Math.sin(angle) * (lw/2 + sw/4), Math.cos(angle) * (lw/2 + sw/4), series.xaxis, series.yaxis);
    }

    ctx.lineWidth = lw;
    ctx.strokeStyle = series.color;
    var fillStyle = getFillStyle(series.lines, series.color, 0, plotHeight);
    if (fillStyle) {
    ctx.fillStyle = fillStyle;
    plotLineArea(series.datapoints, series.xaxis, series.yaxis);
    }

    if (lw > 0)
    plotLine(series.datapoints, 0, 0, series.xaxis, series.yaxis);
    ctx.restore();
    }

    function drawSeriesPoints(series) {
    function plotPoints(datapoints, radius, fillStyle, offset, shadow, axisx, axisy, symbol) {
    var points = datapoints.points, ps = datapoints.pointsize;

    for (var i = 0; i < points.length; i += ps) {
    var x = points[i], y = points[i + 1];
    if (x == null || x axisx.max || y axisy.max)
    continue;

    ctx.beginPath();
    x = axisx.p2c(x);
    y = axisy.p2c(y) + offset;
    if (symbol == "circle")
    ctx.arc(x, y, radius, 0, shadow ? Math.PI : Math.PI * 2, false);
    else
    symbol(ctx, x, y, radius, shadow);
    ctx.closePath();

    if (fillStyle) {
    ctx.fillStyle = fillStyle;
    ctx.fill();
    }
    ctx.stroke();
    }
    }

    ctx.save();
    ctx.translate(plotOffset.left, plotOffset.top);

    var lw = series.points.lineWidth,
    sw = series.shadowSize,
    radius = series.points.radius,
    symbol = series.points.symbol;
    if (lw > 0 && sw > 0) {
    // draw shadow in two steps
    var w = sw / 2;
    ctx.lineWidth = w;
    ctx.strokeStyle = "rgba(0,0,0,0.1)";
    plotPoints(series.datapoints, radius, null, w + w/2, true,
    series.xaxis, series.yaxis, symbol);

    ctx.strokeStyle = "rgba(0,0,0,0.2)";
    plotPoints(series.datapoints, radius, null, w/2, true,
    series.xaxis, series.yaxis, symbol);
    }

    ctx.lineWidth = lw;
    ctx.strokeStyle = series.color;
    plotPoints(series.datapoints, radius,
    getFillStyle(series.points, series.color), 0, false,
    series.xaxis, series.yaxis, symbol);
    ctx.restore();
    }

    function drawBar(x, y, b, barLeft, barRight, offset, fillStyleCallback, axisx, axisy, c, horizontal, lineWidth) {
    var left, right, bottom, top,
    drawLeft, drawRight, drawTop, drawBottom,
    tmp;

    // in horizontal mode, we start the bar from the left
    // instead of from the bottom so it appears to be
    // horizontal rather than vertical
    if (horizontal) {
    drawBottom = drawRight = drawTop = true;
    drawLeft = false;
    left = b;
    right = x;
    top = y + barLeft;
    bottom = y + barRight;

    // account for negative bars
    if (right < left) {
    tmp = right;
    right = left;
    left = tmp;
    drawLeft = true;
    drawRight = false;
    }
    }
    else {
    drawLeft = drawRight = drawTop = true;
    drawBottom = false;
    left = x + barLeft;
    right = x + barRight;
    bottom = b;
    top = y;

    // account for negative bars
    if (top < bottom) {
    tmp = top;
    top = bottom;
    bottom = tmp;
    drawBottom = true;
    drawTop = false;
    }
    }

    // clip
    if (right axisx.max ||
    top axisy.max)
    return;

    if (left axisx.max) {
    right = axisx.max;
    drawRight = false;
    }

    if (bottom axisy.max) {
    top = axisy.max;
    drawTop = false;
    }

    left = axisx.p2c(left);
    bottom = axisy.p2c(bottom);
    right = axisx.p2c(right);
    top = axisy.p2c(top);

    // fill the bar
    if (fillStyleCallback) {
    c.beginPath();
    c.moveTo(left, bottom);
    c.lineTo(left, top);
    c.lineTo(right, top);
    c.lineTo(right, bottom);
    c.fillStyle = fillStyleCallback(bottom, top);
    c.fill();
    }

    // draw outline
    if (lineWidth > 0 && (drawLeft || drawRight || drawTop || drawBottom)) {
    c.beginPath();

    // FIXME: inline moveTo is buggy with excanvas
    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(datapoints, barLeft, barRight, offset, fillStyleCallback, axisx, axisy) {
    var points = datapoints.points, ps = datapoints.pointsize;

    for (var i = 0; i < points.length; i += ps) {
    if (points[i] == null)
    continue;
    drawBar(points[i], points[i + 1], points[i + 2], barLeft, barRight, offset, fillStyleCallback, axisx, axisy, ctx, series.bars.horizontal, series.bars.lineWidth);
    }
    }

    ctx.save();
    ctx.translate(plotOffset.left, plotOffset.top);

    // FIXME: figure out a way to add shadows (for instance along the right edge)
    ctx.lineWidth = series.bars.lineWidth;
    ctx.strokeStyle = series.color;
    var barLeft = series.bars.align == "left" ? 0 : -series.bars.barWidth/2;
    var fillStyleCallback = series.bars.fill ? function (bottom, top) { return getFillStyle(series.bars, series.color, bottom, top); } : null;
    plotBars(series.datapoints, barLeft, barLeft + series.bars.barWidth, 0, fillStyleCallback, series.xaxis, series.yaxis);
    ctx.restore();
    }

    function getFillStyle(filloptions, seriesColor, bottom, top) {
    var fill = filloptions.fill;
    if (!fill)
    return null;

    if (filloptions.fillColor)
    return getColorOrGradient(filloptions.fillColor, bottom, top, seriesColor);

    var c = $.color.parse(seriesColor);
    c.a = typeof fill == "number" ? fill : 0.4;
    c.normalize();
    return c.toString();
    }

    function insertLegend() {
    placeholder.find(".legend").remove();

    if (!options.legend.show)
    return;

    var fragments = [], rowStarted = false,
    lf = options.legend.labelFormatter, s, label;
    for (var i = 0; i < series.length; ++i) {
    s = series[i];
    label = s.label;
    if (!label)
    continue;

    if (i % options.legend.noColumns == 0) {
    if (rowStarted)
    fragments.push('');
    fragments.push('');
    rowStarted = true;
    }

    if (lf)
    label = lf(label, s);

    fragments.push(
    '

    ' +
    '' + label + '');
    }
    if (rowStarted)
    fragments.push('');

    if (fragments.length == 0)
    return;

    var table = '' + fragments.join("") + '';
    if (options.legend.container != null)
    $(options.legend.container).html(table);
    else {
    var pos = "",
    p = options.legend.position,
    m = options.legend.margin;
    if (m[0] == null)
    m = [m, m];
    if (p.charAt(0) == "n")
    pos += 'top:' + (m[1] + plotOffset.top) + 'px;';
    else if (p.charAt(0) == "s")
    pos += 'bottom:' + (m[1] + plotOffset.bottom) + 'px;';
    if (p.charAt(1) == "e")
    pos += 'right:' + (m[0] + plotOffset.right) + 'px;';
    else if (p.charAt(1) == "w")
    pos += 'left:' + (m[0] + plotOffset.left) + 'px;';
    var legend = $('

    ' + table.replace('style="', 'style="position:absolute;' + pos +';') + '

    ').appendTo(placeholder);
    if (options.legend.backgroundOpacity != 0.0) {
    // put in the transparent background
    // separately to avoid blended labels and
    // label boxes
    var c = options.legend.backgroundColor;
    if (c == null) {
    c = options.grid.backgroundColor;
    if (c && typeof c == "string")
    c = $.color.parse(c);
    else
    c = $.color.extract(legend, 'background-color');
    c.a = 1;
    c = c.toString();
    }
    var div = legend.children();
    $('

    ').prependTo(legend).css('opacity', options.legend.backgroundOpacity);
    }
    }
    }

    // interactive features

    var highlights = [],
    redrawTimeout = null;

    // returns the data item the mouse is over, or null if none is found
    function findNearbyItem(mouseX, mouseY, seriesFilter) {
    var maxDistance = options.grid.mouseActiveRadius,
    smallestDistance = maxDistance * maxDistance + 1,
    item = null, foundPoint = false, i, j;

    for (i = series.length - 1; i >= 0; --i) {
    if (!seriesFilter(series[i]))
    continue;

    var s = series[i],
    axisx = s.xaxis,
    axisy = s.yaxis,
    points = s.datapoints.points,
    ps = s.datapoints.pointsize,
    mx = axisx.c2p(mouseX), // precompute some stuff to make the loop faster
    my = axisy.c2p(mouseY),
    maxx = maxDistance / axisx.scale,
    maxy = maxDistance / axisy.scale;

    // with inverse transforms, we can't use the maxx/maxy
    // optimization, sadly
    if (axisx.options.inverseTransform)
    maxx = Number.MAX_VALUE;
    if (axisy.options.inverseTransform)
    maxy = Number.MAX_VALUE;

    if (s.lines.show || s.points.show) {
    for (j = 0; j maxx || x - mx maxy || y - my < -maxy)
    continue;

    // We have to calculate distances in pixels, not in
    // data units, because the scales of the axes may be different
    var dx = Math.abs(axisx.p2c(x) - mouseX),
    dy = Math.abs(axisy.p2c(y) - mouseY),
    dist = dx * dx + dy * dy; // we save the sqrt

    // use <= to ensure last point takes precedence
    // (last generally means on top of)
    if (dist < smallestDistance) {
    smallestDistance = dist;
    item = [i, j / ps];
    }
    }
    }

    if (s.bars.show && !item) { // no other point can be nearby
    var barLeft = s.bars.align == "left" ? 0 : -s.bars.barWidth/2,
    barRight = barLeft + s.bars.barWidth;

    for (j = 0; j < points.length; j += ps) {
    var x = points[j], y = points[j + 1], b = points[j + 2];
    if (x == null)
    continue;

    // for a bar graph, the cursor must be inside the bar
    if (series[i].bars.horizontal ?
    (mx = Math.min(b, x) &&
    my >= y + barLeft && my = x + barLeft && mx = Math.min(b, y) && my <= Math.max(b, y)))
    item = [i, j / ps];
    }
    }
    }

    if (item) {
    i = item[0];
    j = item[1];
    ps = series[i].datapoints.pointsize;

    return { datapoint: series[i].datapoints.points.slice(j * ps, (j + 1) * ps),
    dataIndex: j,
    series: series[i],
    seriesIndex: i };
    }

    return null;
    }

    function onMouseMove(e) {
    if (options.grid.hoverable)
    triggerClickHoverEvent("plothover", e,
    function (s) { return s["hoverable"] != false; });
    }

    function onMouseLeave(e) {
    if (options.grid.hoverable)
    triggerClickHoverEvent("plothover", e,
    function (s) { return false; });
    }

    function onClick(e) {
    triggerClickHoverEvent("plotclick", e,
    function (s) { return s["clickable"] != false; });
    }

    // trigger click or hover event (they send the same parameters
    // so we share their code)
    function triggerClickHoverEvent(eventname, event, seriesFilter) {
    var offset = eventHolder.offset(),
    canvasX = event.pageX - offset.left - plotOffset.left,
    canvasY = event.pageY - offset.top - plotOffset.top,
    pos = canvasToAxisCoords({ left: canvasX, top: canvasY });

    pos.pageX = event.pageX;
    pos.pageY = event.pageY;

    var item = findNearbyItem(canvasX, canvasY, seriesFilter);

    if (item) {
    // fill in mouse pos for any listeners out there
    item.pageX = parseInt(item.series.xaxis.p2c(item.datapoint[0]) + offset.left + plotOffset.left);
    item.pageY = parseInt(item.series.yaxis.p2c(item.datapoint[1]) + offset.top + plotOffset.top);
    }

    if (options.grid.autoHighlight) {
    // clear auto-highlights
    for (var i = 0; i < highlights.length; ++i) {
    var h = highlights[i];
    if (h.auto == eventname &&
    !(item && h.series == item.series &&
    h.point[0] == item.datapoint[0] &&
    h.point[1] == item.datapoint[1]))
    unhighlight(h.series, h.point);
    }

    if (item)
    highlight(item.series, item.datapoint, eventname);
    }

    placeholder.trigger(eventname, [ pos, item ]);
    }

    function triggerRedrawOverlay() {
    if (!redrawTimeout)
    redrawTimeout = setTimeout(drawOverlay, 30);
    }

    function drawOverlay() {
    redrawTimeout = null;

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

    executeHooks(hooks.drawOverlay, [octx]);
    }

    function highlight(s, point, auto) {
    if (typeof s == "number")
    s = series[s];

    if (typeof point == "number") {
    var ps = s.datapoints.pointsize;
    point = s.datapoints.points.slice(ps * point, ps * (point + 1));
    }

    var i = indexOfHighlight(s, point);
    if (i == -1) {
    highlights.push({ series: s, point: point, auto: auto });

    triggerRedrawOverlay();
    }
    else if (!auto)
    highlights[i].auto = false;
    }

    function unhighlight(s, point) {
    if (s == null && point == null) {
    highlights = [];
    triggerRedrawOverlay();
    }

    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.max || y axisy.max)
    return;

    var pointRadius = series.points.radius + series.points.lineWidth / 2;
    octx.lineWidth = pointRadius;
    octx.strokeStyle = $.color.parse(series.color).scale('a', 0.5).toString();
    var radius = 1.5 * pointRadius,
    x = axisx.p2c(x),
    y = axisy.p2c(y);

    octx.beginPath();
    if (series.points.symbol == "circle")
    octx.arc(x, y, radius, 0, 2 * Math.PI, false);
    else
    series.points.symbol(octx, x, y, radius, false);
    octx.closePath();
    octx.stroke();
    }

    function drawBarHighlight(series, point) {
    octx.lineWidth = series.bars.lineWidth;
    octx.strokeStyle = $.color.parse(series.color).scale('a', 0.5).toString();
    var fillStyle = $.color.parse(series.color).scale('a', 0.5).toString();
    var barLeft = series.bars.align == "left" ? 0 : -series.bars.barWidth/2;
    drawBar(point[0], point[1], point[2] || 0, barLeft, barLeft + series.bars.barWidth,
    0, function () { return fillStyle; }, series.xaxis, series.yaxis, octx, series.bars.horizontal, series.bars.lineWidth);
    }

    function getColorOrGradient(spec, bottom, top, defaultColor) {
    if (typeof spec == "string")
    return spec;
    else {
    // assume this is a gradient spec; IE currently only
    // supports a simple vertical gradient properly, so that's
    // what we support too
    var gradient = ctx.createLinearGradient(0, top, 0, bottom);

    for (var i = 0, l = spec.colors.length; i < l; ++i) {
    var c = spec.colors[i];
    if (typeof c != "string") {
    var co = $.color.parse(defaultColor);
    if (c.brightness != null)
    co = co.scale('rgb', c.brightness)
    if (c.opacity != null)
    co.a *= c.opacity;
    c = co.toString();
    }
    gradient.addColorStop(i / (l - 1), c);
    }

    return gradient;
    }
    }
    }

    $.plot = function(placeholder, data, options) {
    //var t0 = new Date();
    var plot = new Plot($(placeholder), data, options, $.plot.plugins);
    //(window.console ? console.log : alert)("time used (msecs): " + ((new Date()).getTime() - t0.getTime()));
    return plot;
    };

    $.plot.version = "0.7";

    $.plot.plugins = [];

    // returns a string with the date d formatted according to fmt
    $.plot.formatDate = function(d, fmt, monthNames) {
    var leftPad = function(n) {
    n = "" + n;
    return n.length == 1 ? "0" + n : n;
    };

    var r = [];
    var escape = false, padNext = false;
    // var hours = d.getUTCHours();
    var hours = d.getHours();
    var isAM = hours 12) {
    hours = hours - 12;
    } else if (hours == 0) {
    hours = 12;
    }
    }
    for (var i = 0; i < fmt.length; ++i) {
    var c = fmt.charAt(i);

    if (escape) {
    switch (c) {
    case 'h': c = "" + hours; break;
    case 'H': c = leftPad(hours); 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; */
    case 'M': c = leftPad(d.getMinutes()); break;
    case 'S': c = leftPad(d.getSeconds()); break;
    case 'd': c = "" + d.getDate(); break;
    case 'm': c = "" + (d.getMonth() + 1); break;
    case 'y': c = "" + d.getFullYear(); break;
    case 'b': c = "" + monthNames[d.getMonth()]; break;
    case 'p': c = (isAM) ? ("" + "am") : ("" + "pm"); break;
    case 'P': c = (isAM) ? ("" + "AM") : ("" + "PM"); break;
    case '0': c = ""; padNext = true; break;
    }
    if (c && padNext) {
    c = leftPad(c);
    padNext = false;
    }
    r.push(c);
    if (!padNext)
    escape = false;
    }
    else {
    if (c == "%")
    escape = true;
    else
    r.push(c);
    }
    }
    return r.join("");
    };

    // round to nearby lower multiple of base
    function floorInBase(n, base) {
    return base * Math.floor(n / base);
    }

    })(jQuery);

    flot graph not displayed in jqxTab #7932

    mim
    Participant

    oh interesting:
    the ag removed the php part of the code!

    ....bw: scrollable boxes for the code would be nicer...;-)

    flot graph not displayed in jqxTab #7942

    Peter Stoev
    Keymaster

    Hi mim,

    For code formatting in the Forum posts, please see: http://www.jqwidgets.com/community/topic/code-formatting/

    The sample I sent you works with our widgets. I am afraid that I am not familiar with the rendering of other third-party widgets and unfortunately, I will not be able to help you about the integration with them.

    Best Wishes,
    Peter Stoev

    jQWidgets Team
    http://www.jqwidgets.com

    flot graph not displayed in jqxTab #7953

    mim
    Participant

    ok, well, anyhow…
    one step further

    by changing just

    from

    var plot = $.plot(plotContainer, dataset, options);

    to

    var plot = $.plot(plotContainer, [dataset], options);

    a pure scaling from -1.00 to 1.00 (yaxis) and 0:00:00 an th xaxis shows up….so my assumption is
    that the data series is not interpreted in the right way, although it works fine stand alone without jqxTab…

    at least any thoughts on that?

    kr,
    michael

    flot graph not displayed in jqxTab #7954

    mim
    Participant

    ….and here comes the solution, but I have no explaination to it:

    changed the file suffix from html to php….and the graph is shown…???!!!

Viewing 7 posts - 1 through 7 (of 7 total)

You must be logged in to reply to this topic.