|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 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: * * <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(); } } }; }); |