<repo_name>bluegraybox/js-time-series-chart<file_sep>/mygraph.js
// I don't know if the data is guaranteed to finish loading before the page is ready, so don't try to plot the graph until both are done.
var pageReady = false;
var parsed = false;

var series;
var options = {
  xaxis: { mode: "time", timeformat: "%Y/%m/%d" },
  yaxis: { min: 0 },
  grid: { hoverable: true }
};

// Show the value when we're hovering over a point on the chart
function hover(event, pos, item) {
  if (item) {
    $("#label").css("display", "block");
    $("#label").css("position", "absolute");
    // Offset the label so it's not covered by the cursor
    $("#label").css("left", (item.pageX + 10) + "px");
    $("#label").css("top", (item.pageY - 20) + "px");
    $("#label").text(item.datapoint[1]);
  }
  else {
    $("#label").css("display", "none");
  }
};

// parse a point of the form [ '2017-11-13 00:00:00', 271662.4285714286 ]
// into JS time and integer
function parsePoint(text) {
  var ds = text[0].replace(/ /, 'T');
  var d = new Date(ds);
  return [d.getTime(), Math.round(text[1])];
}

// parse all the points in a series
function parseSeries(text) {
  return text.map(parsePoint);
}

// parse all the series in the response
// This is the function specified in index.html as the JSONP callback, which is invoked when the data finishes loading.
// (Which is why it's not referenced anywhere else in this file.)
function parseResponse(text) {
  series = text.map(parseSeries);
  parsed = true;
  plotSeries();
}

// use Flot to plot the series on the chart
function plotSeries() {
  // only if the page is ready and the data is loaded and parsed
  if (parsed && pageReady) {
    var placeholder = $("#placeholder");
    var p = $.plot(placeholder, series, options);
    // call our hover function on plothover events
    placeholder.bind("plothover", hover);
  }
}

// when the page is ready
$(function () {
  pageReady = true;
  plotSeries();
});
<file_sep>/random.cgi
#!/bin/bash

# Web service that generates a random number and returns it as JSONP.

echo 'Content-type: application/javascript'
echo
echo "addData($RANDOM);"
<file_sep>/README.md
# JS Time Series Chart

Very simple demo project using [Flot](https://github.com/flot/flot/blob/master/README.md) to graph time series data.

Data is stored as JavaScript in `n-transactions.js` and loaded using JSONP.
It's done this way so that you don't need to run a web server - you can just open the `index.html` file in a browser.

At first, I tried to load the data file with jQuery's `load` function, and got a cross-site request error.
For security reasons I don't fully understand, Chrome won't load files with a `file:` URL - it requires `http:`.
So you need to be running a web server, and getting that set up and configured is more hassle than I want to put someone through.

# Realtime

The various `realtime` files demonstrate graphing a continuous feed of data from a service.
They require a web server because the data service is a local CGI script.
<file_sep>/realtime.js
/* Display a graph of historical values retrieved from the random.cgi service.
 * Clicking anywhere on the page pauses/resumes the requests to the server.
 */

var SERVICE_URL = 'http://localhost/~colin/jsgraph/random.cgi';
var MAX_POINTS = 1000;  // How much history we keep
var UPDATE_INTERVAL = 250;  // Delay between updates, in milliseconds

var timer;
var series = [];
var options = {
  xaxis: { mode: "time", timeformat: "%M:%S" },  // only show minutes & seconds
  yaxis: { min: 0 },
  grid: { hoverable: true }
};

// Show the value when we're hovering over a point on the chart
function hover(event, pos, item) {
  if (item) {
    $("#label").css("display", "block");
    $("#label").css("position", "absolute");
    // Offset the label so it's not covered by the cursor
    $("#label").css("left", (item.pageX + 10) + "px");
    $("#label").css("top", (item.pageY - 20) + "px");
    $("#label").text(item.datapoint[1]);
  }
  else {
    $("#label").css("display", "none");
  }
};

// Callback function which adds retrieved value to time series.
function addData(response) {
  var timestamp = (new Date()).getTime();
  series.push([timestamp, response]);
  plotSeries();
  while (series.length > MAX_POINTS) {
    series.shift();
  }
};

// Fetch data from web service
function update() {
  $.ajax({
    url: SERVICE_URL,
    jsonp: 'addData',
    dataType: 'jsonp',
    success: function(response) {
      // addData() does all the work
    }
  });
}

// use Flot to plot the series on the chart
function plotSeries() {
  if (series.length < 2)
    return;
  var placeholder = $("#placeholder");
  var p = $.plot(placeholder, [series], options);
}

// Start timer to periodically fetch data.
function startTimer() {
  timer = setInterval(update, UPDATE_INTERVAL);
}

// Pause or resume fetching of data
function toggleTimer() {
  if (timer) {
    clearTimeout(timer);
    timer = undefined;
  }
  else {
    startTimer();
  }
}

// when the page is ready
$(function () {
  // call our hover function on plothover events
  var placeholder = $("#placeholder");
  placeholder.bind("plothover", hover);

  addEventListener("click", toggleTimer);

  timer = setInterval(update, 250);
});
