/*
Data Analyzer
Author : Niral Soni
Date created : 10-Jan-2013
Date modified : 15-Jan-2013
Version : 2.0
Email : soniniral@yahoo.co.in
*/
function DataAnalyzer(thisObject, dataSourceId, vHeaderStartsAt, vAddFilter, vBypassCols, vAnalyzeCols) {
this.chartHeight = 800;
this.chartWidth = 250;
var selfObject = thisObject,
dataSource = _ge(dataSourceId) || null,
headerStartsAt = vHeaderStartsAt || 1,
addFilter = vAddFilter || false,
bypassCols = vBypassCols || [],
analyzeCols = vAnalyzeCols || [],
MetaData = null,
Charts = null,
sourceList = null,
sourceType = 0,
ErrorCode = { 'SOURCE_NOT_SET' : '"DataSource" is not set.',
'INVALID_OBJECT' : '"DataSource" is not an Object.',
'INVALID_TABLE' : '"DataSource" is not a Table Object.',
'HEADERROWS_NOT_SET' : '"headerStartsAt" count is not set.',
'INVALID_NUMBER' : 'Specified value in "HeaderRows" is not a Number.',
'OUT_OF_RANGE' : 'Specified value in "HeaderRows" is out of range.',
'NO_DATA_AVAILABLE' : '"DataSource" does not contain any data to analyze',
'SUCCESS' : 'Success',
'INVALID_VALUE' : 'Method invoked with improper values.',
'NO_METADATA' : 'Metadata is not available.'},
DataTypes = { 'NULL' : 0,
'NUMBER' : 1,
'STRING' : 2},
SourceTypes = { 'ALL_COLUMNS' : 0,
'ANALYZE_COLUMNS' : 1,
'BYPASS_COLUMNS' : 2}
autoAnalyzeOnFilter = false,
filterOnly = false,
chartSrc = 'Flash Chart';
function init() {
MetaData = [];
Charts = [{'Title': 'Show All Charts'}];
sourceList = [];
sourceType = SourceTypes.ALL_COLUMNS;
if(dataSource == null) { return ErrorCode.SOURCE_NOT_SET; }
if(typeof dataSource != 'object') { return ErrorCode.INVALID_OBJECT; }
if(dataSource.nodeName.toUpperCase() != 'TABLE') { return ErrorCode.INVALID_TABLE; }
if(headerStartsAt == null) { return ErrorCode.HEADERROWS_NOT_SET; }
if(typeof headerStartsAt != 'number') { return ErrorCode.INVALID_NUMBER; }
if((dataSource.rows.length - headerStartsAt) < 0 ) { return ErrorCode.OUT_OF_RANGE; }
if((dataSource.rows.length - headerStartsAt) == 0) { return ErrorCode.NO_DATA_AVAILABLE; }
if(analyzeCols.length > 0) {
sourceList = analyzeCols;
sourceType = SourceTypes.ANALYZE_COLUMNS;
}
else if(bypassCols.length > 0) {
sourceList = bypassCols;
sourceType = SourceTypes.BYPASS_COLUMNS;
}
return ErrorCode.SUCCESS;
}
function columnExists(compareText, compareIndex) {
var colCount, colValue, colLength;
for(colCount = 0, colLength = sourceList.length; colCount < colLength; colCount++) {
colValue = sourceList[colCount];
if(colValue == null) continue;
colValue = Trim(colValue);
if(colValue.length == 0) continue;
if(isNaN(colValue) && colValue == compareText) return true;
else if(parseInt(colValue,10) == compareIndex) return true;
}
return false;
}
function analyzeHeaders() {
var rowCount = headerStartsAt - 1,
colCount, colLength, colText, colExists,
currentRow = dataSource.rows[rowCount];
if(currentRow.cells == null) return;
for(colCount = 0, colLength = currentRow.cells.length; colCount < colLength; colCount++) {
MetaData[colCount] = null;
if(currentRow.cells[colCount] == null) continue;
colText = (document.all) ? currentRow.cells[colCount].innerText : currentRow.cells[colCount].textContent;
if(colText == null) continue;
colText = Trim(colText);
if(colText.length == 0) continue;
colExists = columnExists(colText, colCount);
if((colExists && sourceType == SourceTypes.ANALYZE_COLUMNS) ||
(!colExists && sourceType == SourceTypes.BYPASS_COLUMNS) ||
sourceType == SourceTypes.ALL_COLUMNS ) {
MetaData[colCount] = {};
MetaData[colCount].columnRow = rowCount;
MetaData[colCount].columnIndex = colCount;
MetaData[colCount].columnLabel = colText;
MetaData[colCount].columnType = DataTypes.NULL;
MetaData[colCount].columnData = {};
}
else {
MetaData[colCount] = null;
}
}
if(addFilter) {
addAutoFilter();
}
}
function addAutoFilter() {
if(_ge('filterHolder')) return;
var filterHolder = document.createElement('DIV');
filterHolder.id = 'filterHolder';
dataSource.parentNode.insertBefore(filterHolder, dataSource);
var filterContent = '<span style="float: left;">Filter By: <select id="CmbSearch">' +
'<option value="-1">Global Search</option>';
for(var i = 0, j = MetaData.length; i < j; i++ ) {
if(MetaData[i] == null) continue;
filterContent += '<option value="' + MetaData[i].columnIndex + '">' + MetaData[i].columnLabel + '</option>';
}
filterContent += '</select><input onkeyup="' + selfObject + '.filterTable(this, \'' +
dataSource.id + '\', this.previousSibling.value,' + headerStartsAt + ')" type="text">';
filterContent += '<input type="button" value="Clear" onclick="this.previousSibling.value = \'\'; ' +
'this.previousSibling.onkeyup();"></span>';
filterContent += '<span id="TotalCount" style="float: right;">Total Records - ' +
(dataSource.rows.length-headerStartsAt) + '</span>';
filterHolder.innerHTML = filterContent;
}
function clearAnalyzedData() {
for(var count = 0; count < MetaData.length; count++) {
if(MetaData[count] == null) continue;
MetaData[count].columnData = {};
}
}
function analyzeData() {
clearAnalyzedData();
var visibleRows = 0, currentRow, currentCell, length, count, uniqueCount = 0, colText, colCount, rowCount;
for(rowCount = headerStartsAt, rowLength = dataSource.rows.length; rowCount < rowLength; rowCount++) {
currentRow = dataSource.rows[rowCount];
if(currentRow.style.display == 'none') continue;
visibleRows++;
for(colCount = 0, colLength = currentRow.cells.length; colCount < colLength; colCount++) {
if(MetaData[colCount] == null) continue;
currentCell = currentRow.cells[colCount];
if(currentCell == null) continue;
colText = (document.all) ? currentCell.innerText : currentCell.textContent;
if(colText == null) continue;
colText = Trim(colText);
if(colText.length == 0) continue;
if(isNaN(colText) && MetaData[colCount].columnType <= DataTypes.STRING) {
MetaData[colCount].columnType = DataTypes.STRING;
}
else if(MetaData[colCount].columnType <= DataTypes.NUMBER) {
MetaData[colCount].columnType = DataTypes.NUMBER;
}
if(MetaData[colCount].columnData[colText] == null) {
MetaData[colCount].columnData[colText] = {};
MetaData[colCount].columnData[colText].Count = 1;
MetaData[colCount].columnData[colText].Rows = [];
}
else {
MetaData[colCount].columnData[colText].Count =
(MetaData[colCount].columnData[colText].Count + 1);
}
length = MetaData[colCount].columnData[colText].Rows.length;
MetaData[colCount].columnData[colText].Rows[length] = rowCount;
}
}
for(count = 0, length = MetaData.length; count < length; count++) {
if(MetaData[count] == null) continue;
uniqueCount = 0;
for(data in MetaData[count].columnData) uniqueCount++;
if(uniqueCount == visibleRows) {
MetaData[count] = null;
}
else {
MetaData[count].DataLength = uniqueCount;
}
}
}
function prepareDataSets() {
var report_suffix = '', count, DataSet, data, length;
Charts = [{'Title': 'Show All Charts'}];
for(count = 0; count < MetaData.length; count++) {
if(MetaData[count] == null) continue;
DataSet = [];
for(columnName in MetaData[count].columnData) {
data = [];
data[0] = columnName;
data[1] = MetaData[count].columnData[columnName].Count;
DataSet[DataSet.length] = data;
}
if(DataSet.length > 1) {
length = Charts.length;
Charts[length] = {};
Charts[length].Title = 'Group by ' + MetaData[count].columnLabel;
Charts[length].LabelX = MetaData[count].columnLabel;
Charts[length].LabelY = "Count";
Charts[length].Source = count;
Charts[length].DataSet = DataSet;
}
else if(DataSet.length == 1) {
report_suffix += MetaData[count].columnLabel + '=' + DataSet[0][0] + ' and ';
}
}
if(report_suffix.length > 0) {
report_suffix = ' for ' + report_suffix.substr(0 , report_suffix.length-5);
for(count = 0; count < Charts.length; count++) {
Charts[count].Title = Charts[count].Title + report_suffix;
}
}
}
function updateDataSet(operation, numColumn, reportIndex) {
if(Charts[reportIndex].Source == numColumn) return;
if(operation == -1 || numColumn == -1) {
prepareDataSets();
return;
}
var sum = 0, rowCount, colCount = 0, data, DataSet = [], currentCell, colText, label = '';
for(columnName in MetaData[Charts[reportIndex].Source].columnData) {
data = [], sum = colCount = 0;
data[0] = columnName;
for(rowCount = 0; rowCount < MetaData[Charts[reportIndex].Source].columnData[columnName].Rows.length; rowCount++) {
row = MetaData[Charts[reportIndex].Source].columnData[columnName].Rows[rowCount];
currentCell = dataSource.rows[row].cells[numColumn];
if(currentCell == null) continue;
colText = (document.all) ? currentCell.innerText : currentCell.textContent;
if(colText == null) continue;
colText = Trim(colText);
if(colText.length == 0) continue;
if(!isNaN(colText)) {
sum += parseInt(colText, 10);
colCount++;
}
}
switch(operation) {
case 'Sum':
data[1] = sum;
break;
case 'Average':
data[1] = parseFloat(sum / colCount, 10).toFixed(2);
break;
}
DataSet[DataSet.length] = data;
}
Charts[reportIndex].LabelY = operation + ' of ' + MetaData[numColumn].columnLabel;
Charts[reportIndex].DataSet = DataSet;
}
function report() {
var option, cmbObj, chartType, i, j, count,
statusObj = _ge('AnalysisStatus'),
errMsgObj = _ge('errMsg'),
dest = _ge('chartContainer'),
details = [{ 'label' : ' Chart Type : ',
'attributes' : { 'id' : 'cmbChartType',
'onchange' : '_ge("cmbPossibleCharts").onchange()'
},
'data' : ['Bar Chart', 'Pie Chart', 'Random'],
'value' : ['bar', 'pie', 'Random']
},
{ 'label' : ' Possible Charts : ',
'attributes' : { 'id' : 'cmbPossibleCharts',
'size' : 1,
'onchange' : selfObject + '.draw(this.value);'
},
'data' : ['----Select----'],
'value' : [-1]
},
{ 'label' : ' along with : ',
'attributes' : { 'id' : 'cmbOperation',
'size' : 1,
'onchange' : '_ge("cmbPossibleCharts").onchange()'
},
'data' : ['----Operation----', 'Sum', 'Average'],
'value' : [-1, 'Sum', 'Average']
},
{ 'label' : ' of : ',
'attributes' : { 'id' : 'cmbNumCols',
'size' : 1,
'onchange' : '_ge("cmbPossibleCharts").onchange()'
},
'data' : ['----Numeric Column----'],
'value' : [-1]
}];
cmbObj = _ge('cmbPossibleCharts');
if(Charts.length > 1) {
if(cmbObj == null) {
for(i = 0; i < details.length; i++) {
cmbObj = document.createElement('select');
for(attr in details[i].attributes) {
cmbObj.setAttribute(attr, details[i].attributes[attr]);
}
for(j = 0; j < details[i].data.length; j++) {
option = document.createElement('option');
option.value = details[i].value[j];
option.text = details[i].data[j];
try { cmbObj.add(option); }
catch (e) { cmbObj.add(option, null); }
}
statusObj.innerHTML += details[i].label;
statusObj.appendChild(cmbObj);
}
}
else {
cmbObj.options.length = 1;
}
cmbObj = _ge('cmbPossibleCharts');
cmbObj.options[0].selected = true;
for(count = 0; count < Charts.length; count++) {
option = document.createElement('option');
option.value = count;
option.text = Charts[count].Title;
try { cmbObj.add(option); }
catch (e) { cmbObj.add(option, null); }
}
cmbObj.options[1].value = Charts.length;
if(errMsgObj) {
errMsgObj.innerHTML = '';
}
cmbObj = _ge('cmbNumCols');
if(cmbObj.options.length == 1) {
cmbObj.options[0].selected = true;
for(count = 0; count < MetaData.length; count++) {
if(MetaData[count] == null) continue;
if(MetaData[count].columnType == DataTypes.NUMBER) {
option = document.createElement('option');
option.value = count;
option.text = MetaData[count].columnLabel;
try { cmbObj.add(option); }
catch (e) { cmbObj.add(option, null); }
}
}
}
}
else {
cmbObj.options.length = 1;
if(!errMsgObj) {
errMsgObj = document.createElement('span');
errMsgObj.id = 'errMsg';
statusObj.appendChild(errMsgObj);
}
errMsgObj.innerHTML = '<br>No "Group By" Charts possible as all available records are distinct.';
}
dest.innerHTML = '';
}
function Trim(s) {
if(s.length > 0) {
while (s.substring(0,1) == ' ') s = s.substring(1,s.length);
while (s.substring(s.length-1,s.length) == ' ') s = s.substring(0,s.length-1);
if(s.search(/[\S]/) == -1) s="";
}
return s;
}
function analyze() {
analyzeHeaders();
if(addFilter && filterOnly) return true;
analyzeData();
prepareDataSets();
report();
}
this.start = function(vFilterOnly, vAutoAnalyzeOnFilter) {
var status = init();
autoAnalyzeOnFilter = vAutoAnalyzeOnFilter || autoAnalyzeOnFilter;
filterOnly = vFilterOnly || filterOnly;
if(status != ErrorCode.SUCCESS) {
alert(status);
return false;
}
analyze();
return true;
}
this.filterTable = function(phrase, tableId, searchIn, headerStartsAt) {
var words = phrase.value.toLowerCase().split(" "),
table = _ge(tableId), ele, i,
cnt = headerStartsAt || 1;
for (var r = cnt; r < table.rows.length; r++) {
searchIn = (typeof searchIn == 'undefined' || searchIn == null ) ? -1 : searchIn;
if(parseInt(searchIn,10) == -1)
ele = (document.all) ? table.rows[r].innerText : table.rows[r].textContent;
else if(parseInt(searchIn,10) > -1)
ele = (document.all) ? table.rows[r].cells[searchIn].innerText : table.rows[r].cells[searchIn].textContent;
var displayStyle = '';
for (i = 0; i < words.length; i++) {
if (ele.toLowerCase().indexOf(words[i])>=0) {
displayStyle = '';
}
else {
displayStyle = 'none';
cnt++;
break;
}
}
table.rows[r].style.display = displayStyle;
}
_ge('TotalCount').innerHTML = "Total Records - " + (table.rows.length-cnt);
cnt = 0;
for(i = headerStartsAt; i < table.rows.length; i++) {
if(table.rows[i].style.display != 'none') {
table.rows[i].className = (cnt%2 == 0) ? 'TablerowBG1' : 'TablerowBG2';
cnt++;
}
}
if(!filterOnly && autoAnalyzeOnFilter) {
analyze();
}
}
this.draw = function(reportIndex) {
var dest = _ge('chartContainer'), chartType,
count = (reportIndex == Charts.length) ? 1 : reportIndex,
operation = _ge('cmbOperation').value,
numColumn = _ge('cmbNumCols').value;
dest.innerHTML = '';
if(reportIndex == -1) return;
do {
updateDataSet(operation, numColumn, count);
var div = document.createElement('div');
div.id = "Chart" + count;
div.className = 'chartCSS';
dest.appendChild(div);
chartType = _ge('cmbChartType').value;
if(chartType == 'Random') {
chartType = (Math.floor(Math.random()*2)) == 0 ? 'pie' : 'bar';
}
if(chartSrc == 'Flash Chart') {
window['myChart'+count] = new SWFObject("open-flash-chart.swf", div.id, this.chartHeight, this.chartWidth, "9", "#FFFFFF");
window['myChart'+count].addVariable("variables","true");
window['myChart'+count].addVariable('x_label_style','10,0x0000FF,2');
window['myChart'+count].addVariable("colours", "#d01f3c,#356aa0,#C79810,#73880A,#D15600,#6BBA70,#D54C78,#C31812");
window['myChart'+count].addVariable("tool_tip", Charts[count].LabelX + ": #x_label#<br>" + (operation != -1 ? Charts[count].LabelY : 'Count') + ": #val#");
window['myChart'+count].addVariable((chartType == 'pie' ? 'pie' : 'bar_glass'), '50,#7E97A6');
window['myChart'+count].addVariable("title",Charts[count].Title + ',{font-size: 16px;}');
window['myChart'+count].addVariable("x_legend",Charts[count].LabelX + ',12,#40515B');
window['myChart'+count].addVariable("y_legend",Charts[count].LabelY + ',12,#40515B');
window['myChart'+count].addVariable('y_min','0');
var lbl = cnt = '', max = 0, j, dataset;
for(j = 0, dataset = Charts[count].DataSet; j < dataset.length; j++) {
lbl += dataset[j][0] + (j == dataset.length -1 ? '' : ',');
cnt += dataset[j][1] + (j == dataset.length -1 ? '' : ',');
max = (Math.ceil(dataset[j][1]) > max) ? Math.ceil(dataset[j][1]) : max;
}
window['myChart'+count].addVariable('y_max',max+5);
window['myChart'+count].addVariable("values", cnt);
window['myChart'+count].addVariable((chartType == 'pie' ? 'pie' : 'x') + "_labels",lbl);
window['myChart'+count].write(div.id);
}
count++;
}while(count < reportIndex);
}
}
function _ge(id) {
return document.getElementById(id);
}