Click here to Skip to main content
12,299,086 members (46,072 online)
Click here to Skip to main content

Stats

27.5K views
1.4K downloads
67 bookmarked
Posted

Data Analyzer: HTML table to Chart

, 14 Feb 2013 CPOL
A few lines of code to create dynamic charts based on the HTML table contents with filter option.
/*
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'       : '&nbsp;&nbsp;Chart Type : ', 
                    'attributes'  : { 'id'        : 'cmbChartType', 
                                      'onchange'  : '_ge("cmbPossibleCharts").onchange()'
                                    }, 
                    'data'        : ['Bar Chart', 'Pie Chart', 'Random'], 
                    'value'       : ['bar', 'pie', 'Random'] 
                  },
                  { 'label'       : '&nbsp;&nbsp;Possible Charts : ', 
                    'attributes'  : { 'id'        : 'cmbPossibleCharts', 
                                      'size'      : 1, 
                                      'onchange'  : selfObject + '.draw(this.value);' 
                                    }, 
                    'data'        : ['----Select----'], 
                    'value'       : [-1] 
                  },
                  { 'label'       : '&nbsp;&nbsp;along with : ', 
                    'attributes'  : { 'id'        : 'cmbOperation', 
                                      'size'      : 1,
                                      'onchange'  : '_ge("cmbPossibleCharts").onchange()'
                                    }, 
                    'data'        : ['----Operation----', 'Sum', 'Average'], 
                    'value'       : [-1, 'Sum', 'Average'] 
                  },
                  { 'label'       : '&nbsp;&nbsp;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);
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Niral Soni
Business Analyst Tata Consultancy Services Ltd.
India India
I started my career as Java Developer. But now more inclined towards web designing, JavaScript, HTML and CSS.I have good level of expertise on Java, Oracle and JavaScript. Designing Generic components is my main expertise.

You may also be interested in...

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.160525.2 | Last Updated 14 Feb 2013
Article Copyright 2013 by Niral Soni
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid