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