Add summary and activity screens, remove private datasets from counts
[ckanext-dga-stats.git] / ckanext / dga_stats / public / ckanext / stats / javascript / modules / plot.js
blob:a/ckanext/dga_stats/public/ckanext/stats/javascript/modules/plot.js -> blob:b/ckanext/dga_stats/public/ckanext/stats/javascript/modules/plot.js
  /* A quick module for generating flot charts from an HTML table. Options can
  * be passed directly to flot using the data-module-* attributes. The tables
  * are currently expected to be marked up as follows:
  *
  * <table data-module="plot">
  * <thead>
  * <tr>
  * <th>X Axis</th>
  * <th>Series A Legend</th>
  * <th>Series B Legend</th>
  * </tr>
  * </thead>
  * <tbody>
  * <tr>
  * <th>X Value</th>
  * <td>Series A Y Value</td>
  * <td>Series B Y Value</td>
  * </tr>
  * ...
  * </tbody>
  * </table>
  *
  * Points are pulled out of the th/td elements using innerHTML or by looking
  * for a data-value attribute. This is useful when a more readable value
  * needs to be used in the elements contents (eg. dates). A data-type attribute
  * can also be applied to parse the value. Only data-type="date" is currently
  * supported and expects data-value to be a unix timestamp.
  */
  this.ckan.module('plot', function (jQuery, _) {
  return {
  /* Holds the jQuery.plot() object when created */
  graph: null,
   
  /* Holds the canvas container when created */
  canvas: null,
   
  /* Default options */
  options: {
  xaxis: {},
  yaxis: {},
  legend: {position: 'nw'},
  colors: ['#ffcc33', '#ff8844']
  },
   
  /* Sets up the canvas element and parses the table.
  *
  * Returns nothing.
  */
  initialize: function () {
  jQuery.proxyAll(this, /_on/);
   
  if (!this.el.is('table')) {
  throw new Error('CKAN module plot can only be called on table elements');
  }
   
  this.setupCanvas();
   
  // Because the canvas doesn't render correctly unless visible we must
  // listen for events that reveal the canvas and then try and re-render.
  // Currently the most common of these is the "shown" event triggered by
  // the tabs plugin.
  this.sandbox.body.on("shown", this._onShown);
  this.data = this.parseTable(this.el);
   
  this.draw();
  },
   
  /* Removes event handlers when the module is removed from the DOM.
  *
  * Returns nothing.
  */
  teardown: function () {
  this.sandbox.body.off("shown", this._onShown);
  },
   
  /* Creates the canvas wrapper and removes the table from the document.
  *
  * Returns nothing.
  */
  setupCanvas: function () {
  this.canvas = jQuery('<div class="module-plot-canvas">');
  this.el.replaceWith(this.canvas);
  },
   
  /* Attempts to draw the chart if the canvas is visible. If not visible the
  * graph does not render correctly. So we keep trying until it is.
  *
  * Examples
  *
  * module.draw();
  *
  * Returns nothing.
  */
  draw: function () {
  if (!this.drawn && this.canvas.is(':visible')) {
  this.graph = jQuery.plot(this.canvas, this.data, this.options);
  }
  },
   
  /* Parses an HTML table element to build the data array for the chart.
  * The thead element provides the axis and labels for the series. The
  * first column in the tbody is used for the x-axis and subsequent
  * columns are the series.
  *
  * table - A table element to parse.
  *
  * Examples
  *
  * module.parseTable(module.el);
  *
  * Returns data object suitable for use in jQuery.plot().
  */
  parseTable: function (table) {
  var data = [];
  var _this = this;
   
  var headings = table.find('thead tr:first th').map(function () {
  return this.innerHTML;
  });
   
  table.find('tbody tr').each(function (row) {
  var element = jQuery(this);
  var x = [];
   
  x[row] = _this.getValue(element.find('th'));
   
  element.find('td').each(function (series) {
  var value = _this.getValue(this);
  var label = headings[series + 1];
   
  data[series] = data[series] || {data: [], label: label};
  data[series].data[row] = [x[row], value];
  });
  });
   
  return data;
  },
   
  /* Retrieves the value from a td/th element. This first looks for a
  * data-value attribute on the element otherwise uses the element
  * text contents.
  *
  * A data-type attribute can also be provided to tell the module how
  * to deal with the element. By default we let jQuery.data() handle
  * the parsing but this can provide additional data. See .parseValue()
  * for more info.
  *
  * cell - An element to extract a value from.
  *
  * Examples
  *
  * var element = jQuery('<td data-value="10">Ten</td>');
  * module.getValue(element); //=> 10
  *
  * var element = jQuery('<td>20</td>');
  * module.getValue(element); //=> 20
  *
  * var element = jQuery('<td data-type="date">1343747094</td>');
  * module.getValue(element); //=> <Date Tue Jul 31 2012 16:04:54 GMT+0100 (BST)>
  *
  * Returns the parsed value.
  */
  getValue: function (cell) {
  var item = cell instanceof jQuery ? cell : jQuery(cell);
  var type = item.data('type') || 'string';
  var value = item.data('value') || item.text();
  return this.parseValue(value, type);
  },
   
  /* Provides the ability to further format a value.
  *
  * If date is provided as a type then it expects value to be a unix
  * timestamp in seconds.
  *
  * value - The value extracted from the element.
  * type - A type string, currently only supports "date".
  *
  * Examples
  *
  * module.parseValue(10); // => 10
  * module.parseValue("cat"); // => "cat"
  * module.parseValue(1343747094, 'date'); // => <Date Tue Jul 31 2012 16:04:54 GMT+0100 (BST)>
  *
  * Returns the parsed value.
  */
  parseValue: function (value, type) {
  if (type === 'date') {
  value = new Date(parseInt(value, 10) * 1000);
  if (!value) {
  value = 0;
  }
  }
  return value;
  },
   
  /* Event handler for when tabs are toggled. Determines if the canvas
  * resides in the shown element and attempts to re-render.
  *
  * event - The shown event object.
  *
  * Returns nothing.
  */
  _onShown: function (event) {
  if (!this.drawn && jQuery.contains(jQuery(event.target.hash)[0], this.canvas[0])) {
  this.draw();
  }
  }
  };
  });