function HierarchyTable(
szName)
{
   /* Variables */
   var m_aszSubTableToShow;
   var m_aszSubTableToShow;
   var m_htObject;
   var m_divTable;
   var m_szName = szName;
   var m_colColumns = new Collection;
   var m_colRows = new Collection;
   var m_colSubTables = new Collection;
   var m_nCntCols = 0;
   var m_nColWidthGrid;

   /* Se toma el puntero al objeto base */
   m_htObject = this;

   this.setDivObject = function(
   szDivObject)
   {
      m_divTable = document.getElementById(szDivObject);
   }

   this.CreateGrid = function(
   nCntCols,
   nColWidthGrid)
   {
      m_nCntCols = nCntCols;
      m_nColWidthGrid = nColWidthGrid;
   }

   this.getColsCount = function()
   {
      return m_nCntCols;
   }

   this.getColWidthGrid = function()
   {
      return m_nColWidthGrid;
   }

   this.addColumn = function(
   szName)
   {
      var clColumn = new Column(szName, m_nColWidthGrid);
      m_colColumns.add(szName, clColumn);
      return clColumn;
   }

   this.Columns = function()
   {
      return m_colColumns;
   } 

   this.Rows = function()
   {
      return m_colRows;
   }

   this.addSubTable = function (
   szName)
   {
      /* Se crea */
      var stbSubTable = new SubTable(szName, m_nColWidthGrid);
 
      /* Se inserta */
      m_colSubTables.add(szName, stbSubTable);

      /* Se retorna */
      return stbSubTable;
   }

   this.getSubTable = function (
   szName)
   {
      return m_colSubTables.getValueByKey(szName);
   }

   this.removeSubTable = function (
   szName)
   {
      return m_colSubTables.remove(szName);
   }


   this.getSubTables = function ()
   {
      return m_colSubTables;
   }

   this.DrawTable = function ()
   {
      /* Si aún no se ha inicializado */
      if ( m_aszSubTableToShow == null) 
      {
         /* Se inicializa la información de registros expandidos/colapsados */
         m_aszSubTableToShow = new Array(m_colRows.length());

         for (var nRow=0; nRow<=m_aszSubTableToShow.length-1; nRow++)
         {
            m_aszSubTableToShow[nRow] = "";
         }
      }

      /* Se asigna la tabla */
      m_divTable.innerHTML = this.htmlGetTable();
   }

   this.htmlGetTable = function ()
   {
      var colColumns;
      var clColumnHeader;
      var clColumnRow;
      var rdRowData;
      var stbSubtable;
   
      var szMainField;
      var szSubField;
      var szMainValue;
      var szSubValue;
      var fShowRow
      var szStyle = "";
   
      /* Se abre la tabla */
      szTable = "<TABLE WIDTH=100% HEIGHT=100% style='padding:0px;spacing:0px;border:0px'>\n";
      /********/
      /* GRID */
      /********/
      /* Se pinta el registro de definirá el "Grid" */
      szTable += this.htmlGetMainRow();

      /*******************/
      /* Tabla principal */
      /*******************/
      /* Se abre el registro */
      szTable += "<TR>\n";

      /*************/
      /* Cabeceras */
      /*************/
      /* Se pintan */
      for ( var nMainCol = 0; nMainCol < m_colColumns.length(); ++nMainCol )
      {

         clColumnHeader = m_colColumns.getValueByIndex(nMainCol);
         szTable += clColumnHeader.htmlGetHeaderTD();
      }

      /* Se cierra el registro oculto */
      szTable += "</TR>\n";

      /*********/
      /* Datos */
      /*********/
      for (var nMainRow=0; nMainRow<=m_colRows.length()-1; nMainRow++)
      {
         /* Se abre el registro */
         szTable += "<TR>\n";

         /* Se toma la información del registro */
         rdRowData = m_colRows.getValueByIndex(nMainRow).RowData();

         /* Se pintan las celdas */
         for ( var nCol = 0; nCol < rdRowData.length(); ++nCol )
         {
            /* Columna de información de formato */
            clColumnHeader = m_colColumns.getValueByIndex(nCol);

            /* Se pone el valor */
            szTable += clColumnHeader.htmlGetCellTD(m_szName, rdRowData.getValueByIndex(nCol), nMainRow, m_aszSubTableToShow[nMainRow].length == 0 );
         }

         /* Se cierra el registro */
         szTable += "</TR>\n";

         /************/
         /* SubTabla */
         /************/
         /* Si hay que mostrarla */
         if ( m_aszSubTableToShow[nMainRow] != "")
         {
            /* Se toma la subtabla */
            stbSubtable = this.getSubTable(m_aszSubTableToShow[nMainRow]);

            /* Se toman sus columnas */
            colColumns = stbSubtable.Columns();

            /************/
            /* Cabecera */
            /************/
            szTable += "<TR>\n";

            /* Se ponen las columnas */
            for ( var nCol = 0; nCol < colColumns.length(); ++nCol )
            {
               /* Se toma la cabecera */
               clColumn = colColumns.getValueByIndex(nCol);
               szTable += clColumn.htmlGetHeaderTD();
            }

            /* Se cierra la cabecera */
            szTable += "</TR>\n";

            /*********/
            /* Datos */
            /*********/
            /* Se pintan los registros */
            for (var nSubRow=0; nSubRow<=stbSubtable.Rows().length()-1; nSubRow++)
            {
               /* Se iniciliza la variable */
               fShowRow = true;

               /* Su busca por claves */
               for (var nKey=0; nKey<=stbSubtable.Keys().length()-1; nKey++)
               {
                  /* Se toman los campos clave principal y secundario */
                  szMainField = stbSubtable.Keys().getKeyByIndex(0);
                  szSubField = stbSubtable.Keys().getValueByIndex(nKey);

                  /* Se toman los valores */
                  szMainValue = m_colRows.getValueByIndex(nMainRow).RowData().getValueByKey(szMainField);
                  szSubValue = stbSubtable.Rows().getValueByIndex(nSubRow).RowData().getValueByKey(szSubField);

                  /* Si no son iguales, se sale */
                  if ( szMainValue != szSubValue)
                  {
                     fShowRow = false;
                     break;
                  }
               }

               /* Si se quieren mostrar los registros */
               if ( fShowRow )
               {
                  /* Se toma la información del registro */
                  rdRowData = stbSubtable.Rows().getValueByIndex(nSubRow).RowData();

                  /* Se abre el registro */
                  szTable += "<TR " + stbSubtable.Rows().getValueByIndex(nSubRow).getStyle() + ">\n";

                  /* Se pintan las celdas */
                  for ( var nCol = 0; nCol < rdRowData.length(); ++nCol )
                  {
       
                     /* Columna de información de formato */
                     clColumnHeader = stbSubtable.Columns().getValueByIndex(nCol);

                     /* Se pone el valor */
                     szTable += clColumnHeader.htmlGetCellTD(m_szName, rdRowData.getValueByIndex(nCol), nSubRow);
                  }

                  /* Se cierra el registro */
                  szTable += "</TR>\n";
               }
            }
            szTable += "<TR HEIGHT=12px><TD></TD></TR>\n";
         }
      }

      /* Se cierra la tabla */
      szTable += "</TABLE>\n";

      /* Se retorna */
      return szTable;
   }

   this.htmlGetMainRow = function()
   {
      var szRow = "";

      /* Registro para definir el ancho de columnas */
      szRow += "<TR height=0>\n";
      for ( var nCol = 0; nCol < m_nCntCols; ++nCol )
      {
         szRow += "<TD WIDTH=" + m_nColWidthGrid + "px style='padding:0px;spacing:0px;BORDER:none'></TD>\n";
      }
      szRow += "</TR>\n";

      /* Se retorna */
      return szRow;
   }

   this.ShowSubRows = function (
   szSubTable,
   nRow)
   {
      if ( m_aszSubTableToShow[nRow] != szSubTable)
      {
         m_aszSubTableToShow[nRow] = szSubTable;
      }
      else
      {
         if ( m_aszSubTableToShow[nRow] == "")
         {
            m_aszSubTableToShow[nRow] = szSubTable;
         }
         else
         {
            m_aszSubTableToShow[nRow] = "";
         }
      }
      m_htObject.DrawTable();
   }
}