  //***********************************************************************/
  function QuiddlerBoard(dictionary, gameOver, imageOffset, urlBase)
  {
    //Define Functions
    this.accept                = QuiddlerBoard_accept;
    this.allCardsShown         = QuiddlerBoard_allCardsShown;
    this.cardClicked           = QuiddlerBoard_cardClicked;
    this.checkIfGameIsOver     = QuiddlerBoard_checkIfGameIsOver;
    this.clearAll              = QuiddlerBoard_clearAll;
    this.clearCard             = QuiddlerBoard_clearCard;
    this.displayTip            = QuiddlerBoard_displayTip;
    this.doMoveCard            = QuiddlerBoard_doMoveCard;
    this.isAutoFlip            = QuiddlerBoard_isAutoFlip;
    this.isTwoLetterCardShowing= QuiddlerBoard_isTwoLetterCardShowing;
    this.loadCards             = QuiddlerBoard_loadCards;
    this.lose                  = QuiddlerBoard_lose; 
    this.paintWord             = QuiddlerBoard_paintWord;
    this.remainingCardsShowing = QuiddlerBoard_remainingCardsShowing;
    this.setAutoFlip           = QuiddlerBoard_setAutoFlip;
    this.showBoard             = QuiddlerBoard_showBoard;
    this.subtrackRemainingCards= QuiddlerBoard_subtrackRemainingCards;
    this.turnOverCard          = QuiddlerBoard_turnOverCard;
    this.loadImages            = QuiddlerBoard_loadImages;
    this.win                   = QuiddlerBoard_win;
    
    //init
    this.images = new Array();
    this.cardArray = new Array();
    this.selectedWord = new SelectedWord();
    this.history = new QuiddlerHistory();
    this.emptyCard = null;
    this.moveCard = -1;
    this.tipDisplayed = true;
    this.autoFlip = false;
    this.score = 0;
    this.active = true;
    this.gameStateKnown = true;
    this.dictionary = (dictionary == null) ? new Dictionary(true) : dictionary;
    this.bonus = new Array(0,0,0,0,0,2,5,10,20,30,40,50,60,70);
    this.gameOver = gameOver;
    this.urlBase = urlBase;
		
    this.imageOffset = imageOffset;
		for (var i = 0; i < document.images.length; i++)
		{
			if (document.images[i].name == "card1")
			{
				this.imageOffset = i;
				break;
			}
		}
    return this;
  }
  
  //***********************************************************************/
  function QuiddlerBoard_loadImages()
  {
    var i = 0;
    // Load Cards
    for (var i = 0; i < this.cardArray.length; i++)
    {
      this.images[i] = new Image();
      this.images[i].src = this.cardArray[i].getImageSrc();
      i++;
    }
    // Load Empty
    this.images[i] = new Image();
    this.images[i].src = this.emptyCard.getImageSrc();
    i++;
    
    // Load Game Over
    var letters = "gameovryuwin";
    for(var i = 0; i < letters.length ; i++)
    {
      this.images[i] = new Image();
      this.images[i++].src = new QuiddlerCard(0,letters.charAt(i),false).getImageSrc();
    }
    //alert("Images.length = " + this.images.length);
  }
  //***********************************************************************/
  function QuiddlerBoard_isAutoFlip(flip)
  {
    return this.autoFlip;
  }
  //***********************************************************************/
  function QuiddlerBoard_setAutoFlip(flip)
  {
    this.autoFlip = flip;
    if (flip)
    {
      this.tipDisplayed = true;
      for(var i = 0; i < 8; i++)
      {
        this.turnOverCard(i);
      }
      this.showBoard();
    }
  }
  
  //***********************************************************************/
  function QuiddlerBoard_showBoard()
  {
    for (var i = 0; i < 24; i++)
    {
    	if (i%3 == 2)
      {
        if (this.cardArray[(i-2)/3] != null)
        {
        	if (this.cardArray[(i-2)/3].isFaceDown())
          {
            document.images[i + this.imageOffset].src = this.urlBase + "/images/quiddlercards/medium/quiddler_card_back.gif";
          }
          
          if (!this.cardArray[(i-2)/3].isFaceDown()) // This could be changed if autoFlip is on
          {
            document.images[i + this.imageOffset].src = this.cardArray[(i-2)/3].getImageSrc();
          }
        }
        else
        {
          document.images[i + this.imageOffset].src = this.emptyCard.getImageSrc();
        }
      }
      else
      {
        //Boarders
        //alert("I = " + i + "\nI/3 = " + i/3);
        if (this.cardArray[(i/3) + 8] != null)
        {
          //alert("Underlying Card is ["+  i/3 + "] =" + this.cardArray[i/3].getLetter());
          document.images[i + this.imageOffset].src = this.urlBase + "/images/quiddlercards/medium/quiddler_card_back_top.gif";
          document.images[i+1 + this.imageOffset].src = this.urlBase + "/images/quiddlercards/medium/quiddler_card_back_west.gif";
        }
        else
        {
          document.images[i + this.imageOffset].src = this.urlBase + "/images/quiddlercards/medium/quiddler_card_back_top_blank.gif";
          document.images[i+1 + this.imageOffset].src = this.urlBase + "/images/quiddlercards/medium/quiddler_card_back_west_blank.gif";
        }
        i++;
      }
    }
    this.paintWord();
    this.checkIfGameIsOver();
  }

  //***********************************************************************/
  function QuiddlerBoard_loadCards(c1,p1,c2,p2,c3,p3,c4,p4,c5,p5,c6,p6,
                                   c7,p7,c8,p8,c9,p9,c10,p10,c11,p11,
                                   c12,p12,c13,p13,c14,p14,c15,p15,c16,p16,
                                   location)
  {
    //alert(QuiddlerBoard_loadCards);
    this.cardArray[0] = new QuiddlerCard(p1,c1,false,this.urlBase,location,0);
    this.cardArray[1] = new QuiddlerCard(p2,c2,false,this.urlBase,location,1);
    this.cardArray[2] = new QuiddlerCard(p3,c3,false,this.urlBase,location,2);
    this.cardArray[3] = new QuiddlerCard(p4,c4,false,this.urlBase,location,3);
    this.cardArray[4] = new QuiddlerCard(p5,c5,false,this.urlBase,location,4);
    this.cardArray[5] = new QuiddlerCard(p6,c6,false,this.urlBase,location,5);
    this.cardArray[6] = new QuiddlerCard(p7,c7,false,this.urlBase,location,6);
    this.cardArray[7] = new QuiddlerCard(p8,c8,false,this.urlBase,location,7);
    this.cardArray[8] = new QuiddlerCard(p9,c9,true,this.urlBase,location,0);
    this.cardArray[9] = new QuiddlerCard(p10,c10,true,this.urlBase,location,1);
    this.cardArray[10] = new QuiddlerCard(p11,c11,true,this.urlBase,location,2);
    this.cardArray[11] = new QuiddlerCard(p12,c12,true,this.urlBase,location,3);
    this.cardArray[12] = new QuiddlerCard(p13,c13,true,this.urlBase,location,4);    
    this.cardArray[13] = new QuiddlerCard(p14,c14,true,this.urlBase,location,5);    
    this.cardArray[14] = new QuiddlerCard(p15,c15,true,this.urlBase,location,6);    
    this.cardArray[15] = new QuiddlerCard(p16,c16,true,this.urlBase,location,7);    
    this.history.initCards(this.cardArray);
    this.emptyCard = QuiddlerCard_BlankCard(this.urlBase, location);
    this.loadImages();
    this.showBoard();
  }
  /***********************************************************************/
  /**
    * 
    **/
  function QuiddlerBoard_clearCard(place)
  {
    var cardCleared = this.cardArray[place];
    this.cardArray[place] = this.cardArray[place+8];
    this.cardArray[place+8] = null;
    //alert("this.cardArray[" + (place+8)+ "] =" + this.cardArray[place+8]);
    if (this.autoFlip)
    {
      this.turnOverCard(place); 
    }
    return cardCleared;
  } 
  /***********************************************************************/
  /**
    * 
    **/
  function QuiddlerBoard_turnOverCard(place)
  {
    if (this.cardArray[place] != null)
    {
      this.cardArray[place].setFaceDown(false);
      this.cardArray[place].setPlace(place);
      this.gameStateKnown = false; 
    }
  } 
  //***********************************************************************/
  function QuiddlerBoard_cardClicked(place)
  {
    //alert("cardClicked");
    if (!this.active)
    {
    	return void(0);
    }
  	var card = this.cardArray[place];
  	if (card == null) 
  	{
      //Set card to be Moved
  		this.moveCard = place;
  		this.paintWord();
    }
    else if (this.moveCard != -1)
    {  
      this.doMoveCard(place, this.moveCard);
      this.showBoard();
    }
    else if (card.isFaceDown())
    {
    	this.turnOverCard(place);
    	this.showBoard();
    }
    else if (!card.isSelected())
    {
      card.setSelected(true);
      this.selectedWord.addCard(card);
      this.paintWord();
    }
    else
    {
    	//alert("last card");
      card.setSelected(false);
      this.selectedWord.removeCard(card);
      this.paintWord();
    }
    return void(0);
  }
  //***********************************************************************/
  function QuiddlerBoard_doMoveCard(from, to)
  {
    //alert("move card [" + from + "," + to + "]");
    var card = this.cardArray[from];
    if ((this.cardArray[to] != null) || (card  == null)) {return;}
    
    this.cardArray[to] = this.clearCard(from);
    card.setPlace(to);
    this.history.moveCard(from, to);
    this.moveCard = -1;
    return;
  }
  //***********************************************************************/
  function QuiddlerBoard_paintWord()
  {
  	//alert("Painting word");
  	if (this.moveCard == -1)
    {
  	  document.cardLayout.word.value = this.selectedWord.getString();
    }
    else
    {
      document.cardLayout.word.value = "Move Card...";
    }
    document.cardLayout.score.value = this.score;
    for (var i = 0; i < 8; i++)
    {
      if (this.cardArray[i] == null)
      {
      	document.cardLayout.elements[i].checked = false;
      }
      else
      {
        document.cardLayout.elements[i].checked = this.cardArray[i].isSelected();
      }
    }
    
  }
  //***********************************************************************/
  function QuiddlerBoard_remainingCardsShowing()
  {
  	var remaining = 0;
    for (var i = 0; i < 8; i++)
    {
      if (this.cardArray[i] != null)
      {
        remaining++;
      }
    }
    return remaining;
  }  
  //***********************************************************************/
  /**
    * Makes it so no card is selected
    **/
  function QuiddlerBoard_clearAll()
  {
    for (var i = 0; i < 8; i++)
    {
      if (this.cardArray[i]!= null)
      {
        this.cardArray[i].setSelected(false);
      }
    }
    this.selectedWord.removeAll();
    this.paintWord();
  }  
  
  //***********************************************************************/
  function QuiddlerBoard_accept()
  {
  	if (this.selectedWord.cardArray.length < 2)
    {
    	alert("You must use at least 2 cards to form a word");
    	return;
    }
    if (!this.dictionary.wordInDictionary(this.selectedWord.getString()))
    {
      alert("Sorry, "+ this.selectedWord.getString() +  " is not a valid word...");
      this.clearAll();
      return;
    }
    
    // Get Points and clear Cards.
  	for (var i = 0; i < 8; i++)
    {
    	if ((this.cardArray[i]!= null) && (this.cardArray[i].isSelected()))
      {
      	this.score += this.cardArray[i].pointValue;
      	this.clearCard(i);
      }
    }
    this.gameStateKnown = false;

    //Check for bonus.
    var wordLength = this.selectedWord.getString().length;
    var bonus = this.bonus[wordLength];
    if (bonus != 0)
    {
    	alert(bonus + " point bonus for " + wordLength + " letter word.");
    	this.score += bonus;
    }
    
    this.history.selectWord(this.selectedWord.getCardArray());
    this.selectedWord.removeAll();
    this.showBoard();
  }  
  //***********************************************************************/
  /**
    * returns true if games is over
    **/
  function QuiddlerBoard_checkIfGameIsOver()
  {
    if (this.gameStateKnown) 
    {
      return false;
    }
    
    if(!this.allCardsShown()) 
    {
      //Game not over, cards still showing
      this.gameStateKnown = true;
      return false;
    }
    

    this.gameStateKnown = true;
    // Check if game is over baised on cards remaining
    if (this.remainingCardsShowing() == 0)
    {
      this.win();
      return true;
    }
    else if (this.remainingCardsShowing() == 1)
    {
      alert("Sorry, as two cards must be used to make a word,\n The game is over");
      this.lose();
      return true;
    }
    else
    {
      var cardString = "";
      for (var i = 0; i < 8; i++)
      {
        if ((this.cardArray[i] != null) && (!this.cardArray[i].isFaceDown()))
        {
          cardString += this.cardArray[i].getLetter();
        }
      }
      if(this.isTwoLetterCardShowing())
      {
        var list = this.dictionary.findMakeableWordList(cardString, false);
        var shownCards = new Array();
        for (var i = 0; i < 8; i++) 
        {
          if ((this.cardArray[i] != null) && (!this.cardArray[i].isFaceDown()))
          {
            shownCards[shownCards.length] = this.cardArray[i];
          }
        }
        var count = this.dictionary.findMakeableWordCountByBruteForce(list, shownCards, true);
        if(count == 0)
        {
          alert("Sorry, as there are no more\nwords that can be made,\nThe game is over"); 
          this.lose();
          return true;
        }
      }
      else if(this.dictionary.findMakeableWordList(cardString, true).length == 0)
      {
        alert("Sorry, as there are no more\nwords that can be made,\nThe game is over"); 
        this.lose();
        return true;
      }
      return false;
    }
  }
  //***********************************************************************/
  function QuiddlerBoard_isTwoLetterCardShowing()
  {
    for (var i = 0; i < 8; i++) 
    {
      if ((this.cardArray[i] != null) && 
          (!this.cardArray[i].isFaceDown()) && 
          (this.cardArray[i].getLetterCount() == 2))
      {
      	return true;
      }
    }
    return false;
  }
  //***********************************************************************/
  /**
    * Tests if there is a unfliped card, that card be flipped over.
    * returns true if there are 8 cards showing, or zero card face down.
    **/
  function QuiddlerBoard_allCardsShown()
  {
    var unshownCards = 0;
    var shownCards = 0;
    
    for (var i = 0; i < 16; i++) 
    {
      if (this.cardArray[i] != null)
      {
      	if(this.cardArray[i].isFaceDown())
        {
        	unshownCards++;
        }
        else
        {
        	shownCards++;
        }
      }
    }
    var allShown = ((unshownCards == 0) || (shownCards == 8));
    return allShown;
  }
  //***********************************************************************/
  function QuiddlerBoard_displayTip()
  {
  	if (this.tipDisplayed) {return;}
  	var display = false;
  	for (var i = 8; i < 16; i++)
    {
    	if (this.cardArray[i] == null)
      {
      	display = true;
      	break;
      }
    }
    if (display)
    {
    	alert("You can move cards to the blank spaces by:\n" + 
    	      "1) Selecting the blank space \n" +
    	      "2) Selecting the card to move");
    	this.tipDisplayed = true;
    }
  }
  //***********************************************************************/  
  function QuiddlerBoard_win()
  {
    this.cardArray[0] = new QuiddlerCard(0,"y",false,this.urlBase);
    this.cardArray[1] = new QuiddlerCard(0,"o",false,this.urlBase);
    this.cardArray[2] = new QuiddlerCard(0,"u",false,this.urlBase);
    this.cardArray[3] = null;
    this.cardArray[4] = null;
    this.cardArray[5] = new QuiddlerCard(0,"w",false,this.urlBase);
    this.cardArray[6] = new QuiddlerCard(0,"i",false,this.urlBase);
    this.cardArray[7] = new QuiddlerCard(0,"n",false,this.urlBase);
    this.showBoard();
    this.active = false;
    alert("Congratulations!!!\n"); 
    if (this.gameOver != null)
    {
      this.history.endGame();
      this.gameOver();
    }
  }
  //***********************************************************************/  
  function QuiddlerBoard_lose()
  {
  	this.subtrackRemainingCards();
    this.cardArray[0] = new QuiddlerCard(0,"g",false,this.urlBase);
    this.cardArray[1] = new QuiddlerCard(0,"a",false,this.urlBase);
    this.cardArray[2] = new QuiddlerCard(0,"m",false,this.urlBase);
    this.cardArray[3] = new QuiddlerCard(0,"e",false,this.urlBase);
    this.cardArray[4] = new QuiddlerCard(0,"o",false,this.urlBase);
    this.cardArray[5] = new QuiddlerCard(0,"v",false,this.urlBase);
    this.cardArray[6] = new QuiddlerCard(0,"e",false,this.urlBase);
    this.cardArray[7] = new QuiddlerCard(0,"r",false,this.urlBase);
    this.showBoard();
    this.active = false;
    if (this.gameOver != null)
    {
      this.history.endGame();
      this.gameOver();
    }
  }
  //***********************************************************************/
  function QuiddlerBoard_subtrackRemainingCards()
  {
  	var lostPoints = 0;
  	var text = ""; 
    var selectedWord = new SelectedWord();
    for (var i = 0; i < 16; i++)
    {
      if ((this.cardArray[i] != null))
      {
        
        lostPoints += this.cardArray[i].pointValue;
        text += this.cardArray[i].getLetter() + " - " + this.cardArray[i].pointValue + " points\n";
        selectedWord.addCard(this.cardArray[i]);
      }
    }
    text = "You have lost a total of " + lostPoints + " point\nfor the following unused cards \n" + text;
    this.score -= lostPoints;
    alert(text);
    
  }
  
  //***********************************************************************/