//////////////////////////////////////////////////////////////
// cqp.grid module
//
// Contains class definitions in cqp.grid namespace
// to create Grid widget and helper classes.
//
// Widget displays product information in a N x X grid
// format. N x X products are displayed in a grid
// at a single time. If there are more products than can 
// be displayed, we simply don't show the items, though, in code,
// you can reach them using the nextPage and prevPage methods. In the future
// we will expand the class UI to include links that calls these methods, 
// as well as a way of displaying what the current page is.
//
// Author: Demian Hess, OPD CQ Press
// 
// Initial Date: February 2008
//
// Version: 1.0
//////////////////////////////////////////////////////////////

//************************************************************************
// Namespaces
//************************************************************************
var cqp = cqp ? cqp : {};
cqp.grid = {};

//************************************************************************
// Start Grid class definition
//************************************************************************

/**
 * Grid displays items in a slide show. NumberOfCells 
 * specifies how many items are displayed at a time.
 */
cqp.grid.Grid = function(
   targetId, 
   numberOfColumns,
   numberOfRows,
   viewerWidth, 
   cellPadding,
   borderSize, 
   borderColor, 
   cellClassName, 
   titleText,
   titleClassName){
               
   // Instance properties
   this.targetId = targetId;
   this.currentPage = 0;
   this.numberOfPages = 0;
   this.numberOfColumns = 0;
   if (numberOfColumns != undefined && ! isNaN(numberOfColumns)){
      this.numberOfColumns = Math.floor(numberOfColumns);
   }
   this.numberOfRows = 0;
   if (numberOfRows != undefined && ! isNaN(numberOfRows)){
      this.numberOfRows = Math.floor(numberOfRows);
   }   
   this.numberOfCells = this.numberOfColumns * this.numberOfRows;
   this.items = [];
   this.viewer = new cqp.grid.UI(this.numberOfColumns, this.numberOfRows, viewerWidth, cellPadding, borderSize, borderColor, cellClassName, titleText, titleClassName); 
   this.cells = this.viewer.divs;
   
} // cqp.grid.Grid constructor


/**
 * cqp.grid.Grid.showPage()
 *
 * Display the specified page of items. If page number is out
 * of range, nothing is done. Also reset the page counter.
 */
cqp.grid.Grid.prototype.showPage = function(pageNumber){
   if (isNaN(pageNumber) || pageNumber < 1 || pageNumber > this.numberOfPages || this.numberOfCells < 1){
      return;
   }

   var maxItemIndex = (this.numberOfCells * pageNumber);
   var minItemIndex = (this.numberOfCells * pageNumber) - this.numberOfCells;
   
   // Retrieve the slice of the array for this page
   var cellCounter = 0;      
   for (i = minItemIndex; i < maxItemIndex; i++){
      // Clear cell
      if (this.cells[cellCounter].innerHTML){
        this.cells[cellCounter].innerHTML = '';
      }     
      
      // Retrieve next item (if available)
      if (this.items[i] != undefined){
         this.cells[cellCounter].innerHTML = this.items[i];
      }
      
      // increment cell counter to move through the grid
      cellCounter++;
   }//for
   
   this.currentPage = pageNumber;
   
}//cqp.grid.Grid.showPage()
    
/**
 * cqp.grid.Grid.loadItems()
 *
 * Loads items into the view. Replaces all 
 * previous items. Caller can pass in any
 * number of items. After load is called, we
 * calculate the number of pages and display
 * the first page in the UI.
 */
cqp.grid.Grid.prototype.loadItems = function(){

   // Reinitialize
   this.items = []; // clear previous items
   this.currentPage = 0;
   this.numberOfPages = 0;
            
   // Store all the items
   for (i = 0; i < arguments.length; i++){      
      this.items[i] = arguments[i];
   }
   
   if (this.items.length == 0){
      return;
   }
   
   // Calculate page info.
   
   var temp = (this.numberOfCells == 0) ? 0 : (this.items.length / this.numberOfCells);
   if (temp == 0){
      this.numberOfPages = 0;
      this.currentPage = 0;
   }
   else if (temp < 1){
      this.numberOfPages = 1;
      this.currentPage = 1;
   }
   else{
      this.numberOfPages = Math.ceil(temp);
      this.currentPage = 1;
   }
   
   // Display items in first page
   this.showPage(1);

}//cqp.grid.Grid.loadItems()

/**
 * cqp.grid.Grid.getPrevPage()
 *
 * Page left.
 */
cqp.grid.Grid.prototype.getPrevPage = function(){
   
   if (this.currentPage <= 1){
      this.currentPage = this.numberOfPages;
   }
   else{
      --this.currentPage;
   }
   this.showPage(this.currentPage);

}//Grid.getPrevPage

/**
 * Grid.getNextPage()
 *
 * Page right.
 */
cqp.grid.Grid.prototype.getNextPage = function(){
   if (this.currentPage >= this.numberOfPages){
      this.currentPage = 1;
   }
   else{
      ++this.currentPage;
   }
   this.showPage(this.currentPage);

}//cqp.grid.Grid.getNextPage

/**
 * Make the Grid visible
 */
cqp.grid.Grid.prototype.show = function(){
   if (document.getElementById(this.targetId)){ 
      var target = document.getElementById(this.targetId);
      var c = this.viewer.getContainer();
      if (c){
         target.innerHTML = ''; // clear any previous content
         target.appendChild(c);      
      }
   }
}//cqp.grid.Grid.show()

/**
 * Shuffle the items in the slide show (thus rearrange them) and, once
 * that is done, reset to display the first page only.
 */
cqp.grid.Grid.prototype.shuffle = function(){
   cqp.grid.shuffle(this.items);
   this.showPage(1);
}//Grid.shuffle()

/**
 * Retrieve a random page. Warning: if there is only a single
 * item on the page, only that one item will be shown.
 */
cqp.grid.Grid.prototype.showRandomPage = function(){
   this.showPage(cqp.random(this.numberOfPages) + 1);
}
   

/////////////////////////////////////////////////////////////////////////
// End Grid class definition
/////////////////////////////////////////////////////////////////////////

//************************************************************************
// HELPER FUNCTIONS
//************************************************************************

/**
 * Shuffle items in array Q
 */
cqp.grid.shuffle = function(Q) {
   var J, K, T;
   for (J = Q.length - 1; J > 0; J--) {
      K = cqp.random(J + 1);
      T = Q[J];
      Q[J] = Q[K];
      Q[K] = T;
   }
   return Q;
} //shuffle

/**
 * Generate a random number n such that 0 <= n < X
 */
cqp.random = function(X) {
   return Math.floor(X * (Math.random() % 1));
} //random


//************************************************************************
// Start cqp.grid.UI class definition
//************************************************************************

/** 
 * Helper class builds viewer HTML. Provides methods to 
 * access the div that contains the entire UI as well as methods
 * for accessing the cells that display the items. 
 */
cqp.grid.UI = function(
   numberOfColumns, 
   numberOfRows, 
   viewerWidth, 
   cellPadding, 
   borderSize, 
   borderColor, 
   cellClassName, 
   titleText, 
   titleClassName){
   
   // Make sure we know the full width and number of cells
   if (numberOfColumns == undefined || numberOfRows == undefined){
      throw new Error("Must supply number of columns and rows");
   }
   
   if (isNaN(numberOfColumns) || Math.floor(numberOfColumns) <= 0 || isNaN(numberOfRows) || Math.floor(numberOfRows) <= 0){
      throw new Error("Number of columns and rows must be positive numbers");
   }
     
   // Instance properties
   this.container = document.createElement('div'); 
   this.divs = new Array(Math.floor(numberOfColumns) * Math.floor(numberOfRows));
   
   // Private variables
   var titleDiv = document.createElement('div');
   var table = document.createElement('table');
   var tbody = document.createElement('tbody');
   var normalizedBorderSize;
   var normalizedBorderColor;
         
   // Set up default values
   if (borderSize == undefined){
      normalizedBorderSize = '1px';
   }
   else{
     normalizedBorderSize = borderSize;
   }
   
   if (borderColor == undefined){
      normalizedBorderColor = 'black';
   }
   else{
      normalizedBorderColor = borderColor;
   }
   
   // Set up the basic structure of elements
   this.container.appendChild(titleDiv);
   this.container.appendChild(table);
   table.appendChild(tbody);
   
   // Style the title area
   if (titleClassName != undefined && titleClassName.length > 0){
      titleDiv.className = titleClassName;
   }
   
   if (titleText != undefined && titleText.length > 0){
      titleDiv.innerHTML = titleText;
   }
                    
   // Style the table
   if (viewerWidth != undefined){
      table.style.width = viewerWidth;
   }
   
   table.style.border = normalizedBorderSize;
   table.style.borderStyle = 'solid';
   table.style.borderColor = normalizedBorderColor;
   table.style.borderCollapse = 'collapse';
   
   if (cellPadding == undefined){
      table.style.padding = '0px';
   }
   else {
      table.style.padding = cellPadding;
   }
                     
   // Fill the table with dynamically determined number of cells
   var counter = 0;
   
   for (var x = 0; x < numberOfRows; x++){
      var tempRow = document.createElement('tr');
      tbody.appendChild(tempRow);
      
      for (var i = 0; i < numberOfColumns; i++){
         var tempCell = document.createElement('td');
         tempCell.align = 'center';
         tempCell.valign = 'top';
         tempCell.style.padding = cellPadding;
         this.divs[counter] = document.createElement('div');
         if (cellClassName != undefined){
               this.divs[counter].className = cellClassName;
         }
         tempCell.appendChild(this.divs[counter]);
         tempRow.appendChild(tempCell);
         counter++;
      } //for columns
   }//for rows
   
} // cqp.grid.UI constructor

/**
 * Instance method for UI. Returns the container
 * which holds the entire viewer HTML.
 */
cqp.grid.UI.prototype.getContainer = function(){
   return this.container;
} // cqp.grid.UI.getContainter()

/**
 * Instance method for UI. Returns array
 * containing the cells that hold items.
 */
cqp.grid.UI.prototype.getCells = function(){
   return this.divs;
} // UI.getCells()


/////////////////////////////////////////////////////////////////////////
// End UI class definition
/////////////////////////////////////////////////////////////////////////



