• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/**
2 * Copyright (c) 2012 The Chromium Authors. All rights reserved.
3 * Use of this source code is governed by a BSD-style license that can be
4 * found in the LICENSE file.
5 */
6'use strict';
7
8(function() {
9  /**
10   * Main results viewer component.
11   */
12  var Viewer = ui.define('div');
13  (function() {
14    //"Private" functions for Viewer
15    /**
16     * Determines the appropriate parser for a given column, using it's first
17     * data element.
18     * @param {String} firstElement The first (non-header) element of a given
19     * column.
20     * @return {String} The YUI parser needed for the column.
21     */
22    function getColumnParser(firstElement) {
23      if (isNumeric(firstElement)) {
24        return 'number';
25      } else if (isDate(firstElement)) {
26        return 'date';
27      } else {
28        return 'string';
29      }
30    }
31
32    /**
33     * Determines whether or not the given element is a date.
34     * @param {String} str The string representation of a potential date.
35     * @return {boolean} true/false whether or not str can be parsed to
36     * a date.
37     */
38    function isDate(str) {
39      var timestamp = Date.parse(str);
40      return !isNaN(timestamp);
41    }
42
43    /**
44     * Generates the YUI column definition for the given dataset.
45     * @param {String[][]} dataSet the dataset that will be displayed.
46     * @return {JSON[]} The array containing the column definitions.
47     */
48    function createYUIColumnDefinitions(dataSet) {
49      var header = dataSet[0];
50      var firstRow = dataSet[1];
51      var columnDefinitions = [];
52      header.forEach(function (element, index, array) {
53        columnDefinitions.push({
54          'key' : index.toString(),
55          'label':element.toString(),
56          'maxAutoWidth':95,
57          'sortable':true,
58          'parser':getColumnParser(firstRow[index])});
59      });
60      return columnDefinitions;
61    }
62
63    /**
64     * Generates the YUI data source for the given dataset.
65     * @param {String[][]} dataSet the dataset that will be displayed.
66     * @return {YAHOO.util.FunctionDataSource} The YUI data source
67     * derived from the dataset.
68     */
69    function createYUIDataSource(dataSet) {
70      var dataSource = [];
71      //Starts from the first non-header line.
72      for (var i = 1; i < dataSet.length; i++) {
73        var dataSourceLine = {};
74        dataSet[i].forEach(function (element, index, array) {
75          if (isNumeric(element)) {
76            dataSourceLine[index.toString()] = parseFloat(element);
77          } else {
78            dataSourceLine[index.toString()] = element
79          }
80        });
81        dataSource.push(dataSourceLine);
82      }
83      return new YAHOO.util.FunctionDataSource(function() {
84                                               return dataSource});
85    }
86
87    /**
88     * Un-selects all the columns from the given data table.
89     * @param {YAHOO.widget.DataTable} dataTable The data table that
90     * contains the results.
91     */
92    function unselectAllColumns(dataTable) {
93      var selectedColumns = dataTable.getSelectedColumns();
94      for (var i = 0; i < selectedColumns.length; i++) {
95        dataTable.unselectColumn(selectedColumns[i]);
96      }
97    }
98
99    /**
100     * Generates an array that contains the indices of the selected
101     * columns in the data table.
102     * @param {YAHOO.widget.DataTable} dataTable
103     * @return {int[]} An array with the indices of the selected columns.
104     */
105    function getSelectedColumnIndices(dataTable) {
106      var selectedColumnIndices = [];
107      var selectedColumns = dataTable.getSelectedColumns();
108      for (var i = 0; i < selectedColumns.length; i++) {
109        selectedColumnIndices.push(selectedColumns[i].key);
110      }
111      return selectedColumnIndices;
112    }
113
114    Viewer.prototype = {
115      __proto__: HTMLDivElement.prototype,
116      decorate:function() {
117        /**
118         * The id for the element that contains the barchart (Optional).
119         * @type {String}
120         */
121        this.barChartElementId_ = undefined;
122        /**
123         * The rectangular array that contains the contents of the cvs file.
124         * @type {String[][]}
125         */
126        this.dataSet_ = undefined;
127      },
128      set barChartElementId(e) {
129        this.barChartElementId_ = e;
130      },
131      get barChartElementId() {
132        return this.barChartElementId_;
133      },
134      set dataSet(ds) {
135        this.dataSet_ = ds;
136      },
137      get dataSet() {
138        return this.dataSet_;
139      },
140      /**
141       * Renders the Viewer component.
142       * @expose
143       */
144      render: function() {
145        document.body.appendChild(this);
146        var previousBarChart = this.barChartElementId_ != null ?
147                               $(this.barChartElementId_) : null;
148        if (previousBarChart != null) {
149          document.body.removeChild(previousBarChart);
150          window.location.hash = this.id;
151        }
152
153        var columnDefinitions = createYUIColumnDefinitions(this.dataSet_);
154        var dataSource = createYUIDataSource(this.dataSet_);
155        var dataTable = new YAHOO.widget.DataTable(this.id, columnDefinitions,
156            dataSource, {caption:'Results'});
157        var firstRow = this.dataSet_[1];
158        var currentViewer = this;
159
160        dataTable.subscribe('cellClickEvent', function (oArgs) {
161          var selectedColumn = dataTable.getColumn(oArgs.target);
162          var selectedColumnIndex = parseInt(selectedColumn.key);
163
164          if (selectedColumnIndex == 0) {
165            unselectAllColumns(dataTable);
166            return;
167          }
168
169          if (isNumeric(firstRow[selectedColumnIndex])) {
170            dataTable.selectColumn(selectedColumn);
171            if (currentViewer.barChartElementId_ != null) {
172              var viewerBarChart_ =
173                      new ViewerBarChart({ownerDocument:window.document});
174              viewerBarChart_.id = currentViewer.barChartElementId_;
175              viewerBarChart_.dataSet = currentViewer.dataSet_;
176              viewerBarChart_.selectedColumnIndices
177                  = getSelectedColumnIndices(dataTable);
178              viewerBarChart_.render();
179            }
180          }
181        });
182      }
183    };
184  }());
185
186  /**
187   * BarChart component for the results viewer.
188   */
189  var ViewerBarChart = ui.define('div');
190  (function () {
191    //"Private" functions for ViewerBarChart
192    /**
193     * Generates a new array that contains only the first column, and all
194     * other selected columns.
195     * @param {(string|number)[][]} dataset Array with the csv contents.
196     * @param {int[]} selectedColumnIndices Indices for all the selected
197     * columns.
198     * @return {String[][]} A new array containing the first column
199     * and all selected columns.
200     */
201    function extractColumnsToPlot(dataset, selectedColumnIndices) {
202      var lines = [];
203      var line = [];
204      for (var i = 0; i < dataset.length; ++i) {
205        line.push(dataset[i][0]);
206        for (var j = 0; j < selectedColumnIndices.length; j++) {
207          var elementValue = dataset[i][selectedColumnIndices[j]];
208          line.push(isNumeric(elementValue) ? parseFloat(elementValue) :
209                        elementValue);
210        }
211        lines.push(line);
212        line = [];
213      }
214      return lines;
215    }
216
217    ViewerBarChart.prototype = {
218      __proto__:HTMLDivElement.prototype,
219      decorate: function() {
220        /**
221         * Percetage of the window width that will be used for the chart
222         * @const
223         * @type {float}
224         */
225        ViewerBarChart.PERCENTAGE_OF_WINDOW_WIDTH_FOR_CHART = 0.75;
226        /**
227         * Approximate number of pixels that will be used per line
228         * @const
229         * @type {int}
230         */
231        ViewerBarChart.HEIGHT_PER_LINE_IN_PIXELS = 28;
232
233        /**
234         * Raw dataset, which contains the csv file contents.
235         * @type {(String|number)[][]}
236         */
237        this.dataSet_ = undefined;
238        /**
239         * Array that contains the selected indices from the table view.
240         * @type {number[]}
241         */
242        this.selectedColumnIndices_ = undefined;
243      },
244      /**
245       * Renders the ViewerBarChart component.
246       * @expose
247       */
248      render : function() {
249        var existingBarChart = $(this.id);
250        if (existingBarChart != null) {
251          //Remove the previous bar chart
252          document.body.removeChild(existingBarChart);
253        }
254        //Attach this component to the document
255        document.body.appendChild(this);
256        var lines = extractColumnsToPlot(this.dataSet_,
257            this.selectedColumnIndices_);
258        var data = google.visualization.arrayToDataTable(lines);
259
260        var barCharWidth = window.width *
261                           ViewerBarChart.PERCENTAGE_OF_WINDOW_WIDTH_FOR_CHART;
262        var barCharHeight = this.dataSet_.length *
263                            ViewerBarChart.HEIGHT_PER_LINE_IN_PIXELS;
264        var options = {
265          'width': barCharWidth,
266          'height':barCharHeight,
267          'fontSize':15
268        };
269        new google.visualization.BarChart(this).draw(data, options);
270        window.location.hash = this.id;
271      },
272      set dataSet(ds) {
273        this.dataSet_ = ds;
274      },
275      set selectedColumnIndices(sci) {
276        this.selectedColumnIndices_ = sci;
277      }
278    };
279  }());
280
281  /**
282   * Determines whether or not a string can be parsed to a number.
283   * @param {String} element String representation of the potential number.
284   * @return {boolean} True or false depending on whether the element is
285   * numeric or not.
286   */
287  function isNumeric(element) {
288    return !isNaN(parseFloat(element)) && isFinite(element);
289  }
290
291  window.Viewer = Viewer;
292  window.ViewerBarChart = ViewerBarChart;
293})();