Home | History | Annotate | Download | only in src
      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 })();