1/* Chart Class */ 2function ODChart(config) { 3 4 var self = this; 5 this.config = config; // Chart Resources and Config 6 this.resource = this.config.resource; 7 this.chart = this.config.chart; 8 9 this.init = function() { 10 11 var value = self.chart; 12 13 if (value.chart_type !== 'text' || (value.prepare_data_from_array !== undefined && value.prepare_data_from_array === true)) { 14 self.chart['ChartData'] = self.makeDataTable(value.columns); 15 if (value.colors !== undefined) { 16 value.ChartData.addColumn({type: 'string', role: 'style' }); 17 } 18 } 19 20 this.getData().done(function(data){ 21 22 //Show Error if there's no data record return 23 if (data.result.records.length >= 1) { 24 self.processAfterData(data); 25 } else { 26 $('#chart_js_error').html( 27 dashboard.config_error_msg + "<br> <span class='error_msg'> " + dashboard.no_data_error + "</span>" 28 ).show(); 29 $('#'+self.chart.container_id).hide(); 30 } 31 32 }) 33 .fail(function( jqXHR, textStatus, errorThrown ) { 34 35 var responseText = JSON.parse(jqXHR.responseText); 36 37 if (responseText.error.message) { 38 var errorMsg = responseText.error.message; 39 } else { 40 var errorMsg = JSON.stringify(responseText.error); 41 } 42 43 $('#chart_js_error').html( 44 dashboard.config_error_msg + "<br> "+ dashboard.error_msg +"<span class='error_msg'>" 45 + errorMsg + "</span>" //Errors are not same need to check all possible ways. 46 ).show(); 47 }); 48 49 }; 50 51 this.getData = function() { 52 53 var ajax_opt = { 54 url: dashboard.ckan_url + '/api/action/datastore_search', 55 data: { 56 resource_id : this.resource.id, 57 filters : this.resource.filters 58 }, 59 dataType: 'json' 60 }; 61 62 //Assign Authorization Header if resource is Private 63 if(config.resource.private) { 64 ajax_opt.headers = { 65 Authorization : ckan_api_key 66 }; 67 } 68 69 return $.ajax(ajax_opt); 70 71 }; 72 73 this.makeDataTable = function(columns) { 74 75 var dataTable = new google.visualization.DataTable(); 76 $.map(columns, function(value, index){ 77 dataTable.addColumn(value, index); 78 }); 79 80 return dataTable; 81 82 }; 83 84 this.processAfterData = function(data) { 85 86 if (self.resource.singlerow || data.result.records.length == 1) { 87 self.data = data.result.records[0]; 88 } else { 89 self.data = data.result.records; 90 } 91 92 var value = self.chart; 93 94 //$.map(self.charts, function(value, index){ 95 96 if(value.chart_type == 'text') { 97 98 if ((self.data[value.field] === undefined || self.data[value.field] === null) || (self.data[value.field] === 0 && value.ignore_zero === true)) { 99 100 $('#'+value.container_id).hide(); 101 102 } else { 103 104 // Format Number 105 var f_val = self.data[value.field]; 106 if (value.numberformat === true) { 107 f_val = self.formatNumber(self.data[value.field]); 108 } 109 110 // Check if there *title* field option 111 if (value.title !== undefined && value.title !== '') { 112 f_val = value.title + ' : ' + f_val; 113 } 114 115 if (value.unit !== undefined && value.unit !== '') { 116 f_val += ' ' + value.unit; 117 } 118 119 $('#'+value.container_id).html(f_val); 120 $('#'+value.container_id).show(); 121 122 } 123 124 } else { 125 126 var row_count = value.ChartData.getNumberOfRows(); 127 if (row_count > 0) { 128 value.ChartData.removeRows(0, row_count); 129 } 130 131 var c_data = self.prepareData(value); 132 if(value.chart_type == 'treemap') { 133 c_data = self.prepareDataForTreeMap(value); 134 } else if (value.fixed_structure === true) { 135 c_data = self.prepareDataFromFixStructure(value); 136 } 137 138 if (c_data === false) { 139 140 $("#"+value.container_id).parent('div').hide(); 141 142 } else { 143 144 value.ChartData.addRows(c_data); 145 146 googleChart.draw(value.chart_type, document.getElementById(value.container_id), value.ChartData, value.chart_options); 147 148 if (value.data_source_table === true) { 149 150 self.prepareDataSourceModal(value); 151 152 googleChart.draw('table', document.getElementById(value.container_id + '_table_wrapper'), value.ChartData); 153 154 } 155 156 jQuery('#' + value.container_id + '_table_wrapper').prepend(self.getDataSourceLinkTemplate(value)); 157 158 //Add Title for table if it's in config 159 if (value.chart_type == 'table' && value.chart_options.title !== undefined) { 160 jQuery('#' + value.container_id).prepend('<h5>' + value.chart_options.title +'</h5>'); 161 } 162 163 } 164 165 } 166 167 //}); 168 169 }; 170 171 this.prepareDataSourceModal = function(value) { 172 173 var container = jQuery('#'+value.container_id); 174 175 //Remove previous DOM 176 container.siblings('.data_source_bar').remove(); 177 container.siblings('.source-table-wrapper').remove(); 178 179 container.before(self.getDataSourceButtonTemplate(value)); 180 181 container.after(self.getTableContainerTemplate(value)); 182 183 }; 184 185 this.getDataSourceLinkTemplate = function(value) { 186 187 var resource_container = jQuery('<div>').addClass('resource_link'); 188 189 if (self.resource.dataset_id !== undefined) { 190 191 var dataset_url = data_source_url + '/' + self.resource.dataset_id; 192 193 resource_container.append( 194 jQuery('<a>').attr('href', dataset_url) 195 .attr('target', '_blank') 196 .text( dashboard.data_source + ': ' + self.resource.resource_title) 197 ); 198 } 199 200 if (self.resource.download_link !== undefined) { 201 202 resource_container.append( 203 jQuery('<a>') 204 .addClass('resource_download') 205 .text(' ' + dashboard.download) 206 .prepend(jQuery('<i>').addClass('fa fa-download')) 207 .on('click', function(){ 208 self.downloadCSV(value.ChartData, value.container_id); 209 }) 210 ); 211 212 } 213 214 return resource_container; 215 216 217 }; 218 219 this.getDataSourceButtonTemplate = function(value) { 220 221 return jQuery('<div>').addClass('data_source_bar') 222 .append( 223 jQuery('<a>').addClass('data_source_btn') 224 .attr('data-target', value.container_id + "_table") 225 .html(' <span class="data_source_btn_text">'+ dashboard.show_data +'<span>') 226 .prepend( 227 jQuery('<i>').addClass('fa fa-gear') 228 ) 229 ); 230 231 }; 232 233 this.getTableContainerTemplate = function(value) { 234 235 return jQuery('<div>').addClass('source-table-wrapper hide') 236 .attr('id', value.container_id +"_table") 237 .append( 238 jQuery('<div>').addClass('modal-content') 239 .append ( 240 jQuery('<div>').addClass('data_table_wrapper') 241 .attr('id', value.container_id + '_table_wrapper') 242 ) 243 ); 244 245 }; 246
247 this.prepareData = function(chart) { 248 249 var result = []; 250 251 var zero_count = 0; 252 253 $.map(chart.fields, function(value, index){ 254 var num_value = self.data[index]; 255 if (num_value && num_value.match(/,/g)) { 256 num_value = num_value.replace(/,/g, ''); 257 } 258 259 if (num_value === null || num_value === '') { 260 num_value = 0; 261 } 262 263 if (num_value === 0) { 264 zero_count++; 265 } 266 267 var row_data = [value, parseInt(num_value, 10)]; 268 if(chart.colors !== undefined) { 269 row_data.push(chart.colors[index]); 270 } 271 result.push(row_data); 272 }); 273 274 if (result.length == zero_count) { 275 return false; 276 } 277 278 return result; 279 280 }; 281 282 this.prepareDataFromFixStructure = function(chart) { 283 284 var result = []; 285 286 $.map(chart.fields, function(value, index){ 287 288 var res = $.map(value, function(value, index){ 289 if (index === 0) { 290 return value; 291 } else { 292 return parseInt(self.data[value]); 293 } 294 }); 295 296 result.push(res); 297 298 }); 299 300 return result; 301 302 }; 303 304 this.prepareDataForTreeMap = function(chart) { 305 306 var results = []; 307 308 $.map(chart.parents, function(value, index){ 309 results.push(value); 310 }); 311 312 var fieldsArr = Object.keys(chart.fields); 313 314 $.map(chart.fields, function(value, index){ 315 results.push([value.data_dictionary, value.parent, parseInt(self.data[index])]); 316 }); 317 318 return results; 319 320 }; 321 322 this.formatNumber = function(num) { 323 return num.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, "$1,"); 324 }; 325 326 327 /* Download DataTable as CSV File 328 329 Code from : https://gist.github.com/pilate/1477368 330 Credit to : pilate(https://github.com/pilate) and olmomp(https://github.com/olmomp) 331 332 */ 333 this.downloadCSV = function(ChartData, filename) { 334 335 //DataTable to CSV 336 var dt_cols = ChartData.getNumberOfColumns(); 337 var dt_rows = ChartData.getNumberOfRows(); 338 339 var csv_cols = []; 340 var csv_out; 341 342 // Iterate columns 343 for (var i=0; i<dt_cols; i++) { 344 // Replace any commas in column labels 345 csv_cols.push(ChartData.getColumnLabel(i).replace(/,/g,"")); 346 } 347 348 // Create column row of CSV 349 csv_out = csv_cols.join(",")+"\r\n"; 350 351 // Iterate rows 352 for (i=0; i<dt_rows; i++) { 353 var raw_col = []; 354 for (var j=0; j<dt_cols; j++) { 355 // Replace any commas in row values 356 raw_col.push(ChartData.getFormattedValue(i, j, 'label').replace(/,/g,"")); 357 } 358 // Add row to CSV text 359 csv_out += raw_col.join(",")+"\r\n"; 360 } 361 362 363 364 //Download CSV 365 var blob = new Blob([csv_out], {type: 'text/csv;charset=utf-8'}); 366 var url = window.URL || window.webkitURL; 367 var link = document.createElementNS("http://www.w3.org/1999/xhtml", "a"); 368 link.href = url.createObjectURL(blob); 369 link.download = filename; 370 371 var event = document.createEvent("MouseEvents"); 372 event.initEvent("click", true, false); 373 link.dispatchEvent(event); 374 375 } 376 377}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.