;(function($) {

  $.fn.sumtr = function(options) {

    var settings = null;
    if(typeof options === 'object')
    {
        settings = options;
    }

    // extend user settings with default settings
    settings = $.extend({}, $.fn.sumtr.defaultSettings, settings || {});

    this.each(function() {
      var s = []; // we hold the results here

      var table       = $(this);
      var bodyRows    = $(this).find(settings.bodyRows);
      var summaryRows = $(this).find(settings.summaryRows);

      // do nothing if there is nothing to sum
      if (bodyRows.length == 0) return;

      bodyRows.each(function(index) {
          var col = 0;
          $(this).children("td").each(function() {
              if ($(this).is(settings.sumCells)){
                if (s.length < col + 1) s[col] = 0;
                var val = settings.readValue($(this));
                s[col] = s[col] + val;
              } else {
                s[col] = "noCount"; // a flag which tells us we're not counting that column
              }
              var colspan = (parseInt($(this).attr('colspan'))) || 1;
              col = col + colspan;
          });
      });

      summaryRows.each(function(index) {
        var col = 0;
        $(this).children("td").each(function() {
            if (s[col] != "noCount"){
              $(this).html(settings.formatValue(s[col]));
              settings.onSum($(this), s[col]);
            }
            var colspan = (parseInt($(this).attr('colspan'))) || 1;
            col = col + colspan;
        });
      });

      settings.onComplete(table);
    });
  };

  $.fn.sumtr.defaultSettings = {
    readValue : function(e) { var r = parseFloat(e.html()); return !isNaN(r) ? r : 0; },
    formatValue : function(val) { return val; },
    onComplete : function(e) {  },
    onSum : function(e,sum) { e.data('sumtr', sum); },
    sumCells : '.sum',
    bodyRows : 'tbody tr',
    summaryRows : 'tr.summary',
  };

  // a related helper
  $.fn.sumtrRatio = function(n,d,q) {

    function format_percent(n){
      return roundNumber(n, 2).toString() + "%";
    }

    function roundNumber(num, dec) {
      var result = Math.round(num*Math.pow(10,dec))/Math.pow(10,dec);
      return result;
    }

    var _n = $(this).find(n).data('sumtr');
    var _d = $(this).find(d).data('sumtr');
    var _q = _d > 0 ? (_n / _d) : 0;

    $(this).find(q).html(_q > 0 ? format_percent(_q * 100) : '-');
  }

})(jQuery);