(function(){

var newtable = window.newtable = function( containerId, attributes, cols, rows ) {
  return this instanceof newtable ?
    this.init( containerId, attributes, cols, rows ) :
    new newtable( containerId, attributes, cols, rows );
};

newtable.fn = newtable.prototype = {
  containerId: null,
  cols: [],
  rows: [],
  filters: [],
  orderColumn: 0,
  groupColumn: -1,
  showGroupLinks: true,
  pageSize: -1,
  currentPageIdx: 0,
  currentGroupIdx: 0,
  currentGroup: "",

  init: function( containerId, attributes, cols, rows ) {
    this.containerId = containerId;
    this.cols        = ( ( cols ) ? cols : [] );
    this.rows        = ( ( rows ) ? rows : [] );
    this.orderColumn = -1;
    this.pageSize    = ( ( typeof( attributes.pageSize    ) != "undefined" ) ? attributes.pageSize    : this.pageSize );
    this.groupColumn = ( ( typeof( attributes.groupColumn ) != "undefined" ) ? attributes.groupColumn : this.groupColumn );
    this.showGroupLinks = ( ( typeof( attributes.showGroupLinks ) != "undefined" ) ? attributes.showGroupLinks : this.showGroupLinks );

    for( var i = 0 ; i < this.rows.length ; i++ )
    {
      this.rows[i].newtable = this
    }

    for( var j = 0 ; j < this.cols.length ; j++ )
    {
      this.cols[j].orderDir   = ( ( typeof( this.cols[j].orderDir   ) != "undefined" ) ? this.cols[j].orderDir   : "asc" );
      this.cols[j].filterable = ( ( typeof( this.cols[j].filterable ) != "undefined" ) ? this.cols[j].filterable : true );
      this.cols[j].orderable  = ( ( typeof( this.cols[j].orderable  ) != "undefined" ) ? this.cols[j].orderable  : true );
      this.cols[j].groupable  = ( ( typeof( this.cols[j].groupable  ) != "undefined" ) ? this.cols[j].groupable  : false );
      this.cols[j].cellClass  = ( ( typeof( this.cols[j].cellClass  ) != "undefined" ) ? this.cols[j].cellClass  : "" );
    }

    if( this.groupColumn != -1 && !this.cols[this.groupColumn].groupable )
    {
      this.groupColumn = -1;
    }

    this.orderTable( ( ( typeof( attributes.orderColumn ) != "undefined" ) ? attributes.orderColumn : 0 ), true );

    return this;
  },

  handleClick: function( e, colIndex ) {
    if( e.button == 2 || e.ctrlKey )
    {
      if( this.cols[colIndex].filterable )
      {
        this.showFilters( e, colIndex );
      }
    }
    else
    {
      if( this.cols[colIndex].orderable )
      {
        this.orderTable( colIndex );
      }
    }
  },

  orderTable: function( colIndex, noRedraw ) {
    var switchDir = ( typeof( colIndex ) != "undefined" && colIndex == this.orderColumn );
    this.orderColumn = ( ( typeof( colIndex ) != "undefined" ) ? colIndex : this.orderColumn );
    this.cols[this.orderColumn].orderDir = ( ( switchDir && this.cols[this.orderColumn].orderDir == "asc" ) ? "desc" : "asc" );

    this.rows.sort( this.compareRows );

    if( typeof( noRedraw ) == "undefined" || !noRedraw )
    {
      this.drawTable();
    }
  },

  compareRows: function( rowA, rowB ) {
    var theTable = rowA.newtable;

    var cmp = 0;

    if( theTable.groupColumn != -1 )
    {
      cmp = theTable.compareCells( rowA[theTable.groupColumn], rowB[theTable.groupColumn] );
    }

    if( cmp == 0 )
    {
      cmp = theTable.compareCells( rowA[theTable.orderColumn], rowB[theTable.orderColumn] );

      if( theTable.cols[theTable.orderColumn].orderDir == "desc" )
      {
        return 0 - cmp;
      }
    }

    return cmp;
  },

  compareCells: function( cellA, cellB ) {
    var valA = ( ( cellA.orderValue ) ? cellA.orderValue : cellA.value );
    var valB = ( ( cellB.orderValue ) ? cellB.orderValue : cellB.value );

    if( !isNaN( parseInt( valA ) ) && !isNaN( parseInt( valB ) ) )
    {
      return parseInt( valA ) - parseInt( valB );
    }

    if( valA == valB ) return 0;

    return ( ( valA > valB ) ? 1 : -1 );
  },

  hideFilters: function() {
    $("#newtableFilters").fadeOut();

    return false;
  },

  showFilters: function( e, colIndex ) {

    var filters = this.getUniqueVisibleValues( colIndex );

    var filterHtml = '<div style="font-weight:bold;margin-bottom:4px">Filter by ' + this.cols[colIndex].title + '</div>\n' +
      '<div style="max-height:20em;overflow:auto;margin-bottom:4px"><a href="#" onclick="return $(\'#' + this.containerId + '\').get(0).newtable.removeFilter(' + colIndex + ')">All</a><br />\n';

    for( var i = 0 ; i < filters.length ; i++ )
    {
      filterHtml += '<a href="#" onclick="return $(\'#' + this.containerId + '\').get(0).newtable.addFilter(' + colIndex + ', \'' + filters[i].value + '\')">' + filters[i].value + '</a><br />\n';
    }

    filterHtml += '</div>\n' +
      '<div style="text-align:right"><a href="#" onclick="return $(\'#' + this.containerId + '\').get(0).newtable.hideFilters()">Cancel</a></div>';

    var filterDiv = $("#newtableFilters");

    if( filterDiv.length == 0 )
    {
      filterDiv = $('<div id="newtableFilters"></div>').css( { position:"absolute", backgroundColor:"#ffffff", border:"1px solid #000000", padding:"4px" } ).appendTo( $("body") ).hide();
    }

    filterDiv.html( filterHtml ).css( { top:((e.clientY)?e.clientY:e.pageY) + "px", left:((e.clientX)?e.clientX:e.pageX) + "px" } );

    $("a",filterDiv).css( { color:"#000000", textDecoration:"none" } );

    filterDiv.fadeIn();

    return false;
  },

  getUniqueVisibleValues: function( colIndex ) {
    var values = new Array();

    var count = 0;

    var skipRow = false;

    for( var i = 0 ; i < this.rows.length ; i++ )
    {
      skipRow = false;

      for( var j = 0 ; !skipRow && j < this.cols.length ; j++ )
      {
        if( j == this.groupColumn && colIndex == this.groupColumn ) continue;
        if( j != colIndex && typeof( this.filters[j] ) != "undefined" && this.filters[j] != "" && this.rows[i][j].value != this.filters[j] ) skipRow = true;
      }

      if( skipRow ) continue;

      values[count++] = this.rows[i][colIndex];
    }

    values.sort( this.compareCells );

    for( var i = values.length - 1 ; i > 0 ; i-- )
    {
      if( values[i].value == values[i - 1].value )
      {
        values.splice( i, 1 );
      }
    }

    return values;
  },

  addFilter: function( colIndex, filterValue ) {
    this.filters[colIndex] = filterValue;

    this.hideFilters();

    this.drawTable();

    return false;
  },

  removeFilter: function( colIndex ) {

    this.filters[colIndex] = "";

    this.hideFilters();

    this.drawTable();

    return false;
  },

  showPage: function( e, pageIndex ) {
    var pageLink = ( ( e.srcElement ) ? e.srcElement : e.target );

    $('#' + this.containerId + ' tbody').css( { display:"none" } );
    $('#' + this.containerId + ' #newtablePageBody' + this.currentGroupIdx).css( { display:"" } );
    $('#' + this.containerId + ' #newtablePageBody' + this.currentGroupIdx + ' a' ).removeClass( "active" );
    $('#' + this.containerId + ' #newtableGroupBody' + this.currentGroupIdx + "_" + pageIndex).css( { display:"" } );

    $(pageLink).addClass( "active" );

    this.currentPageIdx = pageIndex;

    return false;
  },

  showGroup: function( e, colIndex, groupValue ) {
    var groupLink = ( ( e.srcElement ) ? e.srcElement : e.target );

    $('#' + this.containerId + ' #newtableGroups span').removeClass( "selected" );
    $(groupLink).parent().addClass( "selected" );

    $('#' + this.containerId + ' tbody').css( { display:"none" } );
    $('#' + this.containerId + ' #newtablePageBody' + colIndex).css( { display:"" } );
    $('#' + this.containerId + ' #newtablePageBody' + colIndex + ' a' ).removeClass( "active" );
    $('#' + this.containerId + ' #newtableGroupBody' + colIndex + "_0").css( { display:"" } );

    var pageLinks = $('#' + this.containerId + ' #newtablePageBody' + colIndex + ' a' );

    if( pageLinks.length > 0 )
    {
      $( pageLinks.get( 0 ) ).addClass( "active" );
    }

    this.currentPageIdx  = 0;
    this.currentGroupIdx = colIndex;
    this.currentGroup    = groupValue;

    this.filters[this.groupColumn] = groupValue;
  },

  drawTable: function() {
    var container = $("#" + this.containerId);

    container.get(0).newtable = this;

    var classes;
    var colCount   = 0;
    var rowCount   = 0;
    var pageCount  = 0;
    var groupCount = 0;
    var tableHtml  = '';
    var lastGroup  = '';
    var groups     = new Array( "" );

    if( this.groupColumn != -1 )
    {
      groups = this.getUniqueVisibleValues( this.groupColumn );

      if( this.currentGroup == "" ) this.currentGroup = groups[0].value;

      if ( this.showGroupLinks )
      {
        tableHtml += '<div id="newtableGroups">\n';
        for( var i = 0 ; i < groups.length ; i++ )
        {
          tableHtml += '<span class="newtableGroup' + ( ( groups[i].value == this.currentGroup ) ? " selected" : "" ) + '" onclick="return $(\'#' + this.containerId + '\').get(0).newtable.showGroup(event, ' + i + ', \'' + groups[i].value + '\')"><span>' + groups[i].value + '</span></span>\n';
          if( groups[i].value == this.currentGroup )
          {
            currentGroupIdx = i;
          }
        }
        tableHtml += '<div class="clear"></div></div>';
      }

      lastGroup = groups[0].value;
      this.filters[this.groupColumn] = this.currentGroup;
    }

    tableHtml += '<table border="0" cellpadding="0" cellspacing="0" class="newtable">\n';
    tableHtml += '<thead>\n';
    tableHtml += '<tr>\n';

    for( var i = 0 ; i < this.cols.length ; i++ )
    {
      if( this.groupColumn != -1 && i == this.groupColumn ) continue;

      classes = ( ( i == this.orderColumn ) ? 'sorted sort' + this.cols[i].orderDir : '' );
      classes += ( ( typeof( this.filters[i] ) != "undefined" && this.filters[i] != "" ) ? ( ( classes == "" ) ? "" : " " ) + 'filtered' : '' );

      tableHtml += '<th' + ( ( classes != "" ) ? ' class="' + classes + '"' : '' ) +
        ( ( this.cols[i].orderable || this.cols[i].filterable ) ? ' onclick="$(\'#' + this.containerId + '\').get(0).newtable.handleClick(event,' + i + ')"' : '' ) +
        ( ( this.cols[i].filterable ) ? ' oncontextmenu="return $(\'#' + this.containerId + '\').get(0).newtable.showFilters(event, ' + i + ')"' : '' ) +
        '>' + this.cols[i].title + '</th>\n';

      colCount++;
    }

    tableHtml += '</tr>\n';
    tableHtml += '</thead>\n';

    tableHtml += '<tbody id="newtableGroupBody' + groupCount + "_" + pageCount + '"' + ( ( this.groupColumn != -1 && groups[groupCount].value != this.currentGroup ) ? ' style="display:none"' : '' ) + '>\n';

    var skipRow = false;

    for( var i = 0 ; i < this.rows.length ; i++ )
    {
      skipRow = false;

      for( var j = 0 ; j < this.cols.length ; j++ )
      {
        if( j != this.groupColumn && typeof( this.filters[j] ) != "undefined" && this.filters[j] != "" && this.rows[i][j].value != this.filters[j] ) skipRow = true;
      }

      if( skipRow ) continue;

      if( this.groupColumn != -1 && this.rows[i][this.groupColumn].value != lastGroup )
      {
        tableHtml += '</tbody>\n';

        if( this.pageSize != -1 && pageCount > 0 )
        {
          tableHtml += '<tbody id="newtablePageBody' + groupCount + '"' + ( ( this.groupColumn != -1 && groups[groupCount].value != this.currentGroup ) ? ' style="display:none"' : '' ) + '>\n';
          tableHtml += '<tr class="pageControls">\n';
          tableHtml += '  <td colspan="' + colCount + '">\n';

          for( var p = 0 ; p <= pageCount ; p++ )
          {
            tableHtml += '<a href="#"' + ( ( p == 0 ) ? ' class="active"' : '' ) + ' onclick="return $(\'#' + this.containerId + '\').get(0).newtable.showPage(event,' + p + ')">' + ( p + 1 ) + '</a>';
          }

          tableHtml += '  </td>\n';
          tableHtml += '</tr>\n';
          tableHtml += '</tbody>\n';
        }

        groupCount++;
        rowCount  = 0;
        pageCount = 0;

        tableHtml += '<tbody id="newtableGroupBody' + groupCount + "_" + pageCount + '"' + ( ( this.groupColumn != -1 && groups[groupCount].value != this.currentGroup ) ? ' style="display:none"' : '' ) + '>\n';

        lastGroup = this.rows[i][this.groupColumn].value
      }

      if( this.pageSize != -1 && rowCount != 0 && ( rowCount % this.pageSize ) == 0 )
      {
        pageCount++;

        tableHtml += '</tbody>\n';
        tableHtml += '<tbody id="newtableGroupBody' + groupCount + "_" + pageCount + '" style="display:none">\n';
      }

      tableHtml += '<tr>\n';

      var hasHref   = false;

      for( var j = 0 ; j < this.cols.length ; j++ )
      {
        if( this.groupColumn != -1 && j == this.groupColumn ) continue;

        if( typeof( this.filters[j] ) != "undefined" && this.filters[j] != "" && this.rows[i][j].value != this.filters[j] ) continue;

        hasHref = ( typeof( this.rows[i][j].href ) != "undefined" && this.rows[i][j].value != null && this.rows[i][j].value != "" );

        tableHtml += '<td' + ( ( this.cols[j].cellClass != '' ) ? ' class="' + this.cols[j].cellClass + '"' : '' ) + '>' + ( ( hasHref ) ? '<a href="' + this.rows[i][j].href + '">' : '' ) + this.rows[i][j].value + ( ( hasHref ) ? '</a>' : '' ) + '</td>\n';
      }

      tableHtml += '</tr>\n';

      rowCount++;
    }

    tableHtml += '</tbody>\n';

    if( this.pageSize != -1 && pageCount > 0 )
    {
      tableHtml += '<tbody id="newtablePageBody' + groupCount + '"' + ( ( this.groupColumn != -1 && groups[groupCount].value != this.currentGroup ) ? ' style="display:none"' : '' ) + '>\n';
      tableHtml += '<tr class="pageControls">\n';
      tableHtml += '  <td colspan="' + colCount + '">\n';

      for( var p = 0 ; p <= pageCount ; p++ )
      {
        tableHtml += '<a href="#"' + ( ( p == 0 ) ? ' class="active"' : '' ) + ' onclick="return $(\'#' + this.containerId + '\').get(0).newtable.showPage(event,' + p + ')">' + ( p + 1 ) + '</a>';
      }

      tableHtml += '  </td>\n';
      tableHtml += '</tr>\n';
      tableHtml += '</tbody>\n';
    }

    tableHtml += '</table>';

    container.html( tableHtml );
  }

};
})();

