--- a/ckanext/dga_stats/public/ckanext/stats/javascript/modules/plot.js
+++ b/ckanext/dga_stats/public/ckanext/stats/javascript/modules/plot.js
@@ -1,1 +1,210 @@
-
+/* 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:
+ *
+ *
+ *
+ *
+ * X Axis |
+ * Series A Legend |
+ * Series B Legend |
+ *
+ *
+ *
+ *
+ * X Value |
+ * Series A Y Value |
+ * Series B Y Value |
+ *
+ * ...
+ *
+ *
+ *
+ * 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('');
+ 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('
Ten | ');
+ * module.getValue(element); //=> 10
+ *
+ * var element = jQuery('20 | ');
+ * module.getValue(element); //=> 20
+ *
+ * var element = jQuery('1343747094 | ');
+ * module.getValue(element); //=>
+ *
+ * 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'); // =>
+ *
+ * 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();
+ }
+ }
+ };
+});
+