Fix missing package definition
[ckanext-dga-stats.git] / ckanext / plot.js


















































































































































































































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