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