/*
 +-------------------------------------------------------------------+
 |                   H T M L - G R A P H S   (v2.3)                  |
 |                                                                   |
 | Copyright Gerd Tentler               info@gerd-tentler.de         |
 | Created: Sep. 17, 2002               Last modified: Aug. 13, 2004 |
 +-------------------------------------------------------------------+
 | This program may be used and hosted free of charge by anyone for  |
 | personal purpose as long as this copyright notice remains intact. |
 |                                                                   |
 | Obtain permission before selling the code for this program or     |
 | hosting this software on a commercial website or redistributing   |
 | this software over the Internet or in any other medium. In all    |
 | cases copyright must remain intact.                               |
 +-------------------------------------------------------------------+

======================================================================================================
 Example:

   graph = new BAR_GRAPH("hBar");
   graph.values = new Array(234, 125, 289, 147,190);
   document.write(graph.create());

 Returns HTML code
------------------------------------------------------------------------------------------------------
 This script was tested on the following systems and browsers:

 - Windows XP: IE 6, NN 7, Opera 7
 - Mac OS X:   IE 5, Safari 1

 If you use another browser or system, this script may not work for you - sorry.
======================================================================================================
*/
  var graphstyle_nr = 1;

  function BAR_GRAPH(type) {
//----------------------------------------------------------------------------------------------------
// Configuration
//----------------------------------------------------------------------------------------------------
    this.type = 'hBar';                     // graph type: "hBar", "vBar" or "pBar"
    if(type) this.type = type;
    this.values;                            // graph data: string with comma-separated values or array

    this.labels;                            // label names: string with comma-separated values or array
    this.labelColor = 'black';              // label font color: string
    this.labelBGColor = '#C0E0FF';          // label background color: string
    this.labelBorder = '2px groove white';  // label border: string (CSS specification)
    this.labelFont = 'Arial, Helvetica';    // label font family: string (CSS specification)
    this.labelSize = 12;                    // label font size: integer (pixels)

    this.barWidth = 20;                     // bar width: integer (pixels)
    this.barLength = 1.0;                   // bar length ratio: float (from 0.1 to 2.9)
    this.barColor;                          // bar color: string with comma-separated values or array
    this.barBGColor;                        // bar background color: string
    this.barBorder = '2px outset white';    // bar border: string (CSS specification)
    this.barLevelColor;                     // bar level color: array (bLevel, bColor); draw bars >= bLevel with bColor

    this.showValues = 0;                    // show values: 0 = % only, 1 = abs. and %, 2 = abs. only, 3 = none
    this.valuesColor;                       // values font color: string (if not set, same color like labels)
    this.valuesFont = 'Arial, Helvetica';   // values font family: string (CSS specification)
    this.valuesSize = 12;                   // values font size: integer (pixels)

    this.charts = 1;                        // number of charts: integer

    // hBar/vBar only:
    this.legend;                            // legend items: string with comma-separated values or array
    this.legendColor = 'black';             // legend font color: string
    this.legendBGColor = '#F0F0F0';         // legend background color: string
    this.legendBorder = '2px groove white'; // legend border: string (CSS specification)
    this.legendFont = 'Arial, Helvetica';   // legend font family: string (CSS specification)
    this.legendSize = 12;                   // legend font size: integer (pixels)

    // debug mode: false = off, true = on; just shows some extra information
    this.debug = false;

    // default bar colors; only used if barColor isn't set
    this.colors = new Array('#0000FF', '#FF0000', '#00E000', '#A0A0FF', '#FFA0A0', '#00A000');

    // error messages
    this.err_type = 'ERROR: Type must be "hBar", "vBar" or "pBar"';

    // CSS class names (don't change)
    if(!graphstyle_nr) graphstyle_nr = 1;
    this.clsBAR = 'clsBAR' + graphstyle_nr;
    this.clsBARBG = 'clsBARBG' + graphstyle_nr;
    this.clsLABEL = 'clsLABEL' + graphstyle_nr;
    this.clsLABELBG = 'clsLABELBG' + graphstyle_nr;
    this.clsLEGEND = 'clsLEGEND' + graphstyle_nr;
    this.clsLEGENDBG = 'clsLEGENDBG' + graphstyle_nr;
    this.clsVALUES = 'clsVALUES' + graphstyle_nr;
    graphstyle_nr++;

//----------------------------------------------------------------------------------------------------
// Functions
//----------------------------------------------------------------------------------------------------
    this.set_style = function() {
      var style = '<style> .' + this.clsBAR + ' { ';
      if(this.barBorder) style += 'border: ' + this.barBorder + '; ';
      style += '} .' + this.clsBARBG + ' { ';
      if(this.barBGColor) style += 'background-color: ' + this.barBGColor + '; ';
      style += '} .' + this.clsLABEL + ' { ';
      if(this.labelColor) style += 'color: ' + this.labelColor + '; ';
      if(this.labelBGColor) style += 'background-color: ' + this.labelBGColor + '; ';
      if(this.labelBorder) style += 'border: ' + this.labelBorder + '; ';
      if(this.labelFont) style += 'font-family: ' + this.labelFont + '; ';
      if(this.labelSize) style += 'font-size: ' + this.labelSize + 'px; ';
      style += '} .' + this.clsLABELBG + ' { ';
      if(this.labelBGColor) style += 'background-color: ' + this.labelBGColor + '; ';
      style += '} .' + this.clsLEGEND + ' { ';
      if(this.legendColor) style += 'color: ' + this.legendColor + '; ';
      if(this.legendFont) style += 'font-family: ' + this.legendFont + '; ';
      if(this.legendSize) style += 'font-size: ' + this.legendSize + 'px; ';
      style += '} .' + this.clsLEGENDBG + ' { ';
      if(this.legendBGColor) style += 'background-color: ' + this.legendBGColor + '; ';
      if(this.legendBorder) style += 'border: ' + this.legendBorder + '; ';
      style += '} .' + this.clsVALUES + ' { ';
      if(this.valuesColor) style += 'color: ' + this.valuesColor + '; ';
      if(this.valuesFont) style += 'font-family: ' + this.valuesFont + '; ';
      if(this.valuesSize) style += 'font-size: ' + this.valuesSize + 'px; ';
      style += '} </style>';
      return style;
    }

    this.level_color = function(value, color) {
      if(this.barLevelColor) {
        if((this.barLevelColor[0] > 0 && value >= this.barLevelColor[0]) ||
           (this.barLevelColor[0] < 0 && value <= this.barLevelColor[0])) {
          color = this.barLevelColor[1];
        }
      }
      return color;
    }

    this.draw_bar = function(width, height, color) {
      var bar = '<table border=0 cellspacing=0 cellpadding=0><tr>';
      bar += '<td class="' + this.clsBAR + '" bgcolor=' + color + '>';
      bar += '<table border=0 cellspacing=0 cellpadding=0><tr>';
      bar += '<td width=' + width + ' height=' + height + '></td>';
      bar += '</tr></table>';
      bar += '</td></tr></table>';
      return bar;
    }

    this.build_legend = function(barColors) {
      var legend = '<table border=0 cellspacing=0 cellpadding=0><tr>';
      legend += '<td class="' + this.clsLEGENDBG + '">';
      legend += '<table border=0 cellspacing=4 cellpadding=0>';
      var l = (typeof(this.legend) == 'string') ? this.legend.split(',') : this.legend;

      for(i = 0; i < barColors.length; i++) {
        legend += '<tr>';
        legend += '<td class="' + this.clsBAR + '" bgcolor=' + barColors[i] + ' nowrap>&nbsp;&nbsp;&nbsp;</td>';
        legend += '<td class="' + this.clsLEGEND + '" nowrap>' + l[i] + '</td>';
        legend += '</tr>';
      }
      legend += '</table></td></tr></table>';
      return legend;
    }

    this.create = function() {
      this.type = this.type.toLowerCase();
      var d = (typeof(this.values) == 'string') ? this.values.split(',') : this.values;
      if(this.labels) var r = (typeof(this.labels) == 'string') ? this.labels.split(',') : this.labels;
      else var r = new Array();
      var label = graph = bColor = '';
      var percent = rowspan = colspan = 0;
      if(this.barColor) var drf = (typeof(this.barColor) == 'string') ? this.barColor.split(',') : this.barColor;
      else var drf = new Array();
      var drw, val = new Array();
      var bc = new Array();
      if(this.barLength < 0.1) this.barLength = 0.1;
      else if(this.barLength > 2.9) this.barLength = 2.9;

      if(this.type == 'pbar') {
        if(!this.barBGColor) this.barBGColor = this.labelBGColor;
        if(this.labelBGColor == this.barBGColor) {
          this.labelBGColor = '';
          this.labelBorder = '';
        }
      }

      if(this.legend && this.type != 'pbar')
        graph += '<table border=0 cellspacing=0 cellpadding=0><tr valign=top><td>';

      if(this.charts > 1) {
        divide = Math.ceil(d.length / this.charts);
        graph += '<table border=0 cellspacing=0 cellpadding=6><tr valign=top><td>';
      }
      else divide = 0;

      var sum = max = max_neg = ccnt = lcnt = chart = 0;
      val[chart] = new Array();

      for(var i = 0; i < d.length; i++) {
        if(divide && i && !(i % divide)) {
          lcnt = 0;
          chart++;
          val[chart] = new Array();
        }
        if(typeof(d[i]) == 'string') drw = d[i].split(';');
        else {
          drw = new Array();
          drw[0] = d[i];
        }
        val[chart][lcnt] = new Array();

        for(var j = v = 0; j < drw.length; j++) {
          val[chart][lcnt][j] = v = parseFloat(drw[j]);
          if(v > max) max = v;
          else if(v < max_neg) max_neg = v;
          if(v < 0) v *= -1;
          sum += v;

          if(!bc[j]) {
            if(ccnt >= this.colors.length) ccnt = 0;
            bc[j] = (!drf[j] || drf[j].length < 3) ? this.colors[ccnt++] : drf[j];
          }
        }
        lcnt++;
      }

      var border = parseInt(this.barBorder);
      var mPerc = sum ? Math.round(max * 100 / sum) : 0;
      if(this.type == 'pbar') var mul = 2;
      else var mul = mPerc ? 100 / mPerc : 1;
      mul *= this.barLength;
      var valSpace = Math.round(this.valuesSize * ((this.type == 'hbar') ? 2.6 : 1.4));
      var spacer = maxSize = Math.round(mPerc * mul) + valSpace + (border * 2);

      if(max_neg) {
        var mPerc_neg = sum ? Math.round(-max_neg * 100 / sum) : 0;
        var spacer_neg = Math.round(mPerc_neg * mul) + valSpace + (border * 2);
        maxSize += spacer_neg;
      }

      for(chart = lcnt = 0; chart < val.length; chart++) {
        graph += '<table border=0 cellspacing=2 cellpadding=0>';

        if(this.type == 'hbar') {
          for(i = 0; i < val[chart].length; i++, lcnt++) {
            label = (lcnt < r.length) ? r[lcnt] : lcnt+1;
            rowspan = val[chart][i].length;
            graph += '<tr><td class="' + this.clsLABEL + '"' + ((rowspan > 1) ? ' rowspan=' + rowspan : '') + ' align=center>';
            graph += '&nbsp;' + label + '&nbsp;</td>';

            for(j = 0; j < val[chart][i].length; j++) {
              percent = sum ? Math.round(val[chart][i][j] * 100 / sum) : 0;
              bColor = this.level_color(val[chart][i][j], bc[j]);

              if(this.showValues == 1 || this.showValues == 2) {
                graph += '<td class="' + this.clsLABEL + '" align=right nowrap>';
                graph += '<span class="' + this.clsVALUES + '">&nbsp;' + val[chart][i][j] + '&nbsp;</span></td>';
              }
              graph += '<td class="' + this.clsBARBG + '" height=100% width=' + maxSize + '>';
              graph += '<table border=0 cellspacing=0 cellpadding=0 height=100%><tr>';

              if(percent < 0) {
                percent *= -1;
                graph += '<td class="' + this.clsLABELBG + '" height=' + this.barWidth + ' width=' + Math.round((mPerc_neg - percent) * mul + valSpace) + ' align=right nowrap>';
                if(this.showValues < 2) graph += '<span class="' + this.clsVALUES + '">' + percent + '%</span>';
                graph += '&nbsp;</td><td class="' + this.clsLABELBG + '">';
                graph += this.draw_bar(Math.round(percent * mul), this.barWidth, bColor);
                graph += '</td><td width=' + spacer + '></td>';
              }
              else {
                if(max_neg) {
                  graph += '<td class="' + this.clsLABELBG + '" width=' + spacer_neg + '>';
                  graph += '<table border=0 cellspacing=0 cellpadding=0><tr><td></td></tr></table></td>';
                }
                if(percent) {
                  graph += '<td>';
                  graph += this.draw_bar(Math.round(percent * mul), this.barWidth, bColor);
                  graph += '</td>';
                }
                else graph += '<td height=' + (this.barWidth + (border * 2)) + '></td>';
                graph += '<td class="' + this.clsVALUES + '" width=' + Math.round((mPerc - percent) * mul + valSpace) + ' nowrap>';
                if(this.showValues < 2) graph += '&nbsp;' + percent + '%';
                graph += '&nbsp;</td>';
              }
              graph += '</tr></table></td></tr>';
              if(j < val[chart][i].length - 1) graph += '<tr>';
            }
          }
        }
        else if(this.type == 'vbar') {
          graph += '<tr align=center valign=bottom>';
          for(i = 0; i < val[chart].length; i++) {

            for(j = 0; j < val[chart][i].length; j++) {
              percent = sum ? Math.round(val[chart][i][j] * 100 / sum) : 0;
              bColor = this.level_color(val[chart][i][j], bc[j]);

              graph += '<td class="' + this.clsBARBG + '">';
              graph += '<table border=0 cellspacing=0 cellpadding=0 width=100%><tr align=center>';

              if(percent < 0) {
                percent *= -1;
                graph += '<td height=' + spacer + '></td></tr><tr align=center valign=top><td class="' + this.clsLABELBG + '">';
                graph += this.draw_bar(this.barWidth, Math.round(percent * mul), bColor);
                graph += '</td></tr><tr align=center valign=top>';
                graph += '<td class="' + this.clsLABELBG + '" height=' + Math.round((mPerc_neg - percent) * mul + valSpace) + ' nowrap>';
                graph += (this.showValues < 2) ? '<span class="' + this.clsVALUES + '">' + percent + '%</span>' : '&nbsp;';
                graph += '</td>';
              }
              else {
                graph += '<td class="' + this.clsVALUES + '" valign=bottom height=' + Math.round((mPerc - percent) * mul + valSpace) + ' nowrap>';
                if(this.showValues < 2) graph += percent + '%';
                graph += '</td>';
                if(percent) {
                  graph += '</tr><tr align=center valign=bottom><td>';
                  graph += this.draw_bar(this.barWidth, Math.round(percent * mul), bColor);
                  graph += '</td>';
                }
                else graph += '</tr><tr><td width=' + (this.barWidth + (border * 2)) + '></td>';
                if(max_neg) {
                  graph += '</tr><tr><td class="' + this.clsLABELBG + '" height=' + spacer_neg + '>';
                  graph += '<table border=0 cellspacing=0 cellpadding=0><tr><td></td></tr></table></td>';
                }
              }
              graph += '</tr></table></td>';
            }
          }
          if(this.showValues == 1 || this.showValues == 2) {
            graph += '</tr><tr align=center>';
            for(i = 0; i < val[chart].length; i++) {
              for(j = 0; j < val[chart][i].length; j++) {
                graph += '<td class="' + this.clsLABEL + '" nowrap>';
                graph += '<span class="' + this.clsVALUES + '">&nbsp;' + val[chart][i][j] + '&nbsp;</span></td>';
              }
            }
          }
          graph += '</tr><tr>';
          for(i = 0; i < val[chart].length; i++, lcnt++) {
            label = (lcnt < r.length) ? r[lcnt] : lcnt+1;
            colspan = val[chart][i].length;
            graph += '<td class="' + this.clsLABEL + '"' + ((colspan > 1) ? ' colspan=' + colspan : '') + ' align=center>';
            graph += '&nbsp;' + label + '&nbsp;</td>';
          }
          graph += '</tr>';
        }
        else if(this.type == 'pbar') {
          for(i = 0; i < val[chart].length; i++, lcnt++) {
            label = (lcnt < r.length) ? r[lcnt] : '';
            graph += '<tr>';

            if(label) {
              graph += '<td class="' + this.clsLABEL + '" align=right>';
              graph += '&nbsp;' + label + '&nbsp;</td>';
            }
            sum = val[chart][i][1];
            percent = sum ? Math.round(val[chart][i][0] * 100 / sum) : 0;
            if(this.showValues == 1 || this.showValues == 2) {
              graph += '<td class="' + this.clsLABEL + '" align=right nowrap>';
              graph += '<span class="' + this.clsVALUES + '">&nbsp;' + val[chart][i][0] + ' / ' + sum + '&nbsp;</span></td>';
            }
            graph += '<td class="' + this.clsBARBG + '">';

            if(percent) {
              this.barColor = drf[i] ? drf[i] : this.colors[0];
              bColor = this.level_color(val[chart][i][0], this.barColor);
              graph += '<table border=0 cellspacing=0 cellpadding=0><tr><td>';
              graph += this.draw_bar(Math.round(percent * mul), this.barWidth, bColor);
              graph += '</td><td width=' + Math.round((100 - percent) * mul) + '></td>';
              graph += '</tr></table>';
            }
            graph += '</td>';
            if(this.showValues < 2) graph += '<td class="' + this.clsVALUES + '" nowrap>&nbsp;' + percent + '%</td>';
            graph += '</tr>';
          }
        }
        else graph += '<tr><td>' + this.err_type + '</td></tr>';

        graph += '</table>';

        if(chart < this.charts - 1 && val[chart+1].length) {
          graph += '</td>';
          if(this.type == 'vbar') graph += '</tr><tr valign=top>';
          graph += '<td>';
        }
      }

      if(this.charts > 1) graph += '</td></tr></table>';

      if(this.legend && this.type != 'pbar') {
        graph += '</td><td width=10>&nbsp;</td><td>';
        graph += this.build_legend(bc);
        graph += '</td></tr></table>';
      }

      if(this.debug) {
        graph += '<br>sum='+sum+' max='+max+' max_neg='+max_neg+' mPerc='+mPerc;
        graph += ' mPerc_neg='+mPerc_neg+' mul='+mul+' valSpace='+valSpace;
      }

      graph += this.set_style();

      return graph;
    }
  }
