/******************************************************************************
 * Javascript Tarot                                                        *
 * by C. David Dent (c) 2008                                                  *
 * http://wildandbad.com:8181/tarot                                           *
 *                                                                            *
 * Partially based on the css Playing Card Javascript Card Objects            *
 * copyright 2001 Mike Hall                                                   *
 * Mike Hall http://www.brainjar.com for terms of use.                        *
 * Do not remove this notice.                                                 *
 ******************************************************************************/
// This requires layouts.js
//=============================================================================
// Helper Functions
//=============================================================================
var $ = function(x){
			if(typeof x=='string') {
			return document.getElementById(x);
			} else {
				return x;
			}
		};	
var addEvent = function(obj, evType, fn){ 
	 if (obj.addEventListener){ 
		obj.addEventListener(evType, fn, false); 
		return true; 
	 } else if (obj.attachEvent){ 
		var r = obj.attachEvent("on"+evType, fn); 
		return r; 
	 } else { 
		return false; 
	 } 
	};	

var eventCall = function(e){
	return (e.target)?e.target:(e.srcElement?e.srcElement:e);	
}
//=============================================================================
// AJAX Object
//=============================================================================

function ajaxObject(url) {
  this.exists = false;
  this.AJAX = null;          
  var that=this;                      
  if (window.XMLHttpRequest) {              
	 that.AJAX=new XMLHttpRequest();              
  } else {                                  
	 that.AJAX=new ActiveXObject("Microsoft.XMLHTTP");
  }                                             
  if (that.AJAX==null) {                             
    return false;                               
  }                                                        
  that.AJAX.open("HEAD", url, false);                         
  that.AJAX.send(null);                                
  that.exists = (that.AJAX.status==200?true:false);   
  return that;
}
//=============================================================================
// Card Object
//=============================================================================
var Card = function(code, name) {
	var arcana = this.arcana?this.arcana: {
			'0': 		'The Fool',
			'I': 		'The Magician',
			'II':		'The High Priestess',
			'III': 	'The Empress',
			'IV': 	'The Emperor',
			'V': 		'The Hierophant',
			'VI': 	'The Lovers',
			'VII': 	'The Chariot',
			'VIII':	'Strength',
			'IX': 	'The Hermit',
			'X': 		'Wheel of Fortune',
			'XI': 	'Justice',
			'XII': 	'The Hanged Man',
			'XIII': 	'Death',
			'XIV': 	'Temperance',
			'XV': 	'The Devil',
			'XVI': 	'The Tower',
			'XVII': 	'The Star',
			'XVIII': 'The Moon',
			'XIX': 	'The Sun',
			'XX': 	'Judgement',
			'XXI': 	'The World'
	};
	// ranks should be keyed by a single Letter/Symbol/Numeral only
	var ranks = this.ranks?this.ranks: {
			'A': 'Ace',
			'2': 'Two',
			'3': 'Three',
			'4': 'Four',
			'5': 'Five',
			'6': 'Six',
			'7': 'Seven',
			'8': 'Eight',
			'9': 'Nine',
			'T': 'Ten',
			'P': 'Page',
			'J': 'Knight',
			'Q': 'Queen',
			'K': 'King'
	};
	var suits = this.suits?this.suits: {
			'C': {name:'Cups',     color:' blue', symbol:'\u00A5'},
			'W': {name:'Wands',    color:' red',  symbol:'\u2021'},
			'P': {name:'Pentacles',color:' gold', symbol:'\u00A9'},
			'S': {name:'Swords',   color:' green',symbol:'\u2020'}
	};	
	var interpret = this.interpret?this.interpret:interpret;

	// at a minimum a card code must be provided.
	if(typeof code == 'undefined') return null;	
	// Begin Constructiong Card
   var reversed   = (code.substr(0,1)=='+'||code.substr(0,1)=='-')? code.substr(0,1) : '+';
	// Major Arcana are not found in the ranks table
	var rank 		= (ranks && ranks[code.substr(1,1)])?code.substr(1,1):code.substr(1);
	var suit 		= rank==code.substr(1)?'A':code.substr(2,1); 
	if(suit=='A' && typeof name!='undefined') {
		// If a name is provided it should override
		suit = name;
	} else if (suit=='A') {
		if(arcana && arcana[rank]) {
			suit = arcana[rank];
		} else {
			suit = 'Major Arcana';	
		}
	} 
	// If interpretations are provided, then attach them.
	var interpretation = typeof interpret[code]!='undefined' ? interpret[code] : 'undefined';
	//-----------------------------------------------------------------------------
	// cardToString(): Returns the name of a card (including rank and suit) as a
	// text string.
	//-----------------------------------------------------------------------------
	var cardToString = function() {
	  var rank, suit;
	  if(this.ranks && this.ranks[this.rank])
		  rank = this.ranks[this.rank];
	  else
	  	  rank = null; // probably a Major Arcana
	
	  if(this.suits && this.suits[this.suit])
		  suit = this.suits[this.suit].name;
	  else
	  	  suit = null; // probably a Major Arcana
		  
	 // Major Arcana
	  if (rank == null || suit == null)
		  return this.rank + " " + this.suit + (this.reversed=='-'?" reversed":"");
	 // Minor Arcana
	  return rank + " of " + suit + (this.reversed=='-'?" reversed":"");
	}
	//-----------------------------------------------------------------------------
	// cardCreateNode(): Returns a DIV node which can be used to display the card 
	// on a page.
	//-----------------------------------------------------------------------------
	var cardCreateNode = function() {
	  var cardNode, frontNode, indexNode, spotNode, tempNode, textNode;
	  var indexStr, spotChar;
	// start the image-finding process
	if(this.r==null)  this.cardExists();
	
	  // This is the main node, a DIV tag.
	  cardNode = document.createElement("DIV");
	  cardNode.className = "card";
	  cardNode.style.background= "url('" + this.imagedir + "back.jpg') center center";
	  cardNode.title = this.toString();
	
	  // Build the front of card.
	  frontNode = document.createElement("DIV");
	
	  // Get proper character for card suit and change font color if necessary.
	  spotChar = "\u00a0"; // unicode Non-Breaking Space
	  spotColor = ' black';
	  if(this.suits && this.suits[this.suit]){
		  spotChar  = this.suits[this.suit].symbol
		  spotColor = this.suits[this.suit].color;
	  }
	  frontNode.className = "front" + spotColor;
	
	  // Create and add the index (rank) to the upper-left corner of the card.
	
	  indexStr = (this.rank!='T')?this.rank:"10";
	  if (this.toString() == "")
		 indexStr = "\u00a0"; // unicode Non-Breaking Space
	// top index
	  spotNode = document.createElement("DIV");
	  spotNode.className = "index top";
	  textNode = document.createTextNode(indexStr);
	  spotNode.appendChild(textNode);
	  spotNode.appendChild(document.createElement("BR"));
	  textNode = document.createTextNode(spotChar);
	  spotNode.appendChild(textNode);
	  frontNode.appendChild(spotNode);
	  // bottom index
	  spotNode = document.createElement("DIV");
	  spotNode.className = "index bottom";
	  textNode = document.createTextNode(spotChar);
	  spotNode.appendChild(textNode);
	  spotNode.appendChild(document.createElement("BR"));
	  textNode = document.createTextNode(indexStr);
	  spotNode.appendChild(textNode);
	  frontNode.appendChild(spotNode);
	
	  // Create and add spots based on card rank (Ace thru T).
	  // spots are an a 3 x 4 grid with columns numbered A-C 
	  // and rows numbered 1-4
		
	  faceNode = document.createElement("DIV");
		faceNode.title = this.toString();
	  faceNode.className = 'face';
	  spotNode = document.createElement("DIV");
	  textNode = document.createTextNode(spotChar);
	  spotNode.appendChild(textNode);
		
	  if (("A23456789TPJQK").indexOf(this.rank)==-1){
		  // Major Arcana 
			faceNode = document.createElement("IMG");
		   faceNode.title = this.toString();
		   faceNode.alt = this.toString();
			faceNode.className = "arcana";
		  if(this.rr.exists) // if reversed images exist use them
				faceNode.src = this.imagedir + this.reversed + this.rank + ".jpg";
			else // otherwise assume major arcana images exist
				faceNode.src = this.imagedir + this.rank + ".jpg";
	  } else {
		  // Minor Arcana
		 if(!(this.r.exists===true    /*   images/rank.jpg  */
			  ||this.rs.exists===true   /*   images/rank+suit.jpg */
			  ||this.rrs.exists===true))/*   images/reversed+rank+suit.jpg */
		 {
		 	if (this.rank == "A") {
				 spotNode.className = "spotB3 ace";
				 tempNode = spotNode.cloneNode(true);
				 faceNode.appendChild(tempNode);
			  }
			  if (this.rank == "3" || this.rank == "5" || this.rank == "9") {
				 spotNode.className = "spotB3";
				 tempNode = spotNode.cloneNode(true);
				 faceNode.appendChild(tempNode);
			  }
			  if (this.rank == "2" || this.rank == "3") {
				 spotNode.className = "spotB1";
				 tempNode = spotNode.cloneNode(true);
				 faceNode.appendChild(tempNode);
			  }
			  if (this.rank == "2" || this.rank == "3") {
				 spotNode.className = "spotB5";
				 tempNode = spotNode.cloneNode(true);
				 faceNode.appendChild(tempNode);
			  }
			  if (this.rank == "4" || this.rank == "5" || this.rank == "6" ||
					this.rank == "7" || this.rank == "8" || this.rank == "9" ||
					this.rank == "T") {
				 spotNode.className = "spotA1";
				 tempNode = spotNode.cloneNode(true);
				 faceNode.appendChild(tempNode);
				 spotNode.className = "spotA5";
				 tempNode = spotNode.cloneNode(true);
				 faceNode.appendChild(tempNode);
				 spotNode.className = "spotC1";
				 tempNode = spotNode.cloneNode(true);
				 faceNode.appendChild(tempNode);
				 spotNode.className = "spotC5";
				 tempNode = spotNode.cloneNode(true);
				 faceNode.appendChild(tempNode);
			  }
			  if (this.rank == "6" || this.rank == "7" || this.rank == "8") {
				 spotNode.className = "spotA3";
				 tempNode = spotNode.cloneNode(true);
				 faceNode.appendChild(tempNode);
				 spotNode.className = "spotC3";
				 tempNode = spotNode.cloneNode(true);
				 faceNode.appendChild(tempNode);
			  }
			  if (this.rank == "7" || this.rank == "8" || this.rank == "T") {
				 spotNode.className = "spotB2";
				 tempNode = spotNode.cloneNode(true);
				 faceNode.appendChild(tempNode);
			  }
			  if (this.rank == "8" || this.rank == "T") {
				 spotNode.className = "spotB4";
				 tempNode = spotNode.cloneNode(true);
				 faceNode.appendChild(tempNode);
			  }
			  if (this.rank == "9" || this.rank == "T") {
				 spotNode.className = "spotA2";
				 tempNode = spotNode.cloneNode(true);
				 faceNode.appendChild(tempNode);
				 spotNode.className = "spotA4";
				 tempNode = spotNode.cloneNode(true);
				 faceNode.appendChild(tempNode);
				 spotNode.className = "spotC2";
				 tempNode = spotNode.cloneNode(true);
				 faceNode.appendChild(tempNode);
				 spotNode.className = "spotC4";
				 tempNode = spotNode.cloneNode(true);
				 faceNode.appendChild(tempNode);
			  }
		  } else {
			faceNode = document.createElement("IMG");
		   faceNode.title = this.toString();
		   faceNode.alt = this.toString();
			faceNode.className = 'face';
			if (this.rrs.exists==true) {
				faceNode.src = this.imagedir + this.reversed + this.rank + this.suit + ".jpg"
			} else if (this.rs.exists==true) {
				faceNode.src = this.imagedir + this.reversed + this.rank + ".jpg"
			} else if (this.rr.exists==true) {
				faceNode.src = this.imagedir + this.reversed + this.rank + ".jpg"
			} else {
				faceNode.src = this.imagedir + this.rank + ".jpg"
			}
			if(!this.rs.exists===true      /* images/rank + suit.jpg */
			&& !this.rrs.exists===true)  { /* images/reverse + rank + suit.jpg */
				// if files don't exist then add spots
				 spotNode.className = "spotA1";
				 tempNode = spotNode.cloneNode(true);
				 frontNode.appendChild(tempNode);
				 spotNode.className = "spotC5";
				 tempNode = spotNode.cloneNode(true);
				 frontNode.appendChild(tempNode);
			} 
		 }
	  }
	  
	 // If card is reversed, make a reversed marker
	  if(this.reversed=='-'){
		  //&& !this.rr.exists && !this.rrs.exists
		 tempNode = document.createElement('div');
		 tempNode.appendChild(document.createTextNode("REV"));
		 tempNode.className = "reversed top";
		 frontNode.appendChild(tempNode);
		 tempNode = document.createElement('div');
		 tempNode.appendChild(document.createTextNode("REV"));
		 tempNode.className = "reversed bottom";
		 frontNode.appendChild(tempNode);
	  }
	
	  // Add front node to the card node.
	   frontNode.appendChild(faceNode);
		cardNode.appendChild(frontNode);
	
		// Make the back of the card clickable to trigger a cardHandler event
		// if that fails, just reveal the card
		addEvent(cardNode, 'mousedown', new Function('ev',
			"var card = new Card('0'); "+
			"card.cardHandler(ev, '" + this.code + "');"));
	
		// Return the card node.
		return cardNode;
	}
	//=============================================================================
	// Image Tests Object. 
	//=============================================================================
	var imageTests = function () {
		var tests = [
			this.imagedir + this.rank + '.jpg',
			this.imagedir + this.reversed + this.rank + '.jpg',
			this.imagedir + this.rank + this.suit + '.jpg',
			this.imagedir + this.reversed + this.rank + this.suit + '.jpg'
		];
		this.r 	= (this.r!=null)?   this.r   : new ajaxObject(tests[0]);
		this.rr	= (this.rr!=null)?  this.rr  : new ajaxObject(tests[1]);
		this.rs	= (this.rs!=null)?  this.rs  : new ajaxObject(tests[2]);
		this.rrs	= (this.rrs!=null)? this.rrs : new ajaxObject(tests[3]);
	}
	//-----------------------------------------------------------------------------
	// cardcardHandler(ev, code): This is the defualt card handling function.  
	//                            It makes the front visible.
	//                            ev = the event sent by clicking
	//                            code = the code of the card clicked.
	//-----------------------------------------------------------------------------
	var cardHandler = function(ev, code){
		e = eventCall(ev);
		if(e.firstChild) {
			if(e.firstChild.style) 
				e.firstChild.style.visibility = 'visible';
			else 
				e.firstChild.style = 'visibility=visible'; 
		}
	};
	// Return Card Object
	var obj = {
		code			: code,
		reversed		: reversed,
		rank			: rank,
		suit			: suit, 
		arcana		: (this.arcana?this.arcana:arcana),
		ranks			: (this.ranks?this.ranks:ranks),
		suits			: (this.suits?this.suits:suits),
		imagedir		: (this.imagedir?this.imagedir:'images/'),
		height		: (this.height?this.height:5.00),
		width 		: (this.width? this.width :3.75),
		interpret   : interpretation,
		cardHandler	: (this.cardHandler?this.cardHandler:cardHandler),
		toString		: cardToString,
		createNode  : (this.cardCreateNode?this.cardCreateNode:cardCreateNode),
		cardExists  : imageTests,
		r				: null,
		rr				: null,
		rs				: null,
		rrs			: null
	};
	return obj;
}


//=============================================================================
// Stack Object
//=============================================================================
var Stack = function() {
	//-----------------------------------------------------------------------------
	// Randomizes up and down orientation
	//-----------------------------------------------------------------------------
	function stackIsreversed() { 
	  		return (this.usereversed===true
					     ? (Math.floor(Math.random()*2 - 1)>=0 
								? "+"
								: "-")
						  : '+'); 
	  }
	//-----------------------------------------------------------------------------
	// stackMakeDeck(n): Initializes a stack using 'n' packs of cards.
	//-----------------------------------------------------------------------------
	function stackMakeDeck(n) {
	// default is 1 deck
	  if(typeof n == 'undefined') n=1;
	  var arcana = ["0","I","II","IV","V","VI","VII","VIII","IX","X",
						 "XI","XII","XIV","XV","XVI","XVII","XVIII","XIX",
						 "XX","XXI"];
	  var ranks = ["A", "2", "3", "4", "5", "6", "7", "8", "9", "T", "P", "J", "Q", "K"];
	  var suits = ["C", "W", "P", "S"];
	  var i, j, k, c;
	
	  // Set array of cards.
	  this.cards = Array();
	
	  // Fill the array with 'n' packs of cards.
	
	  for (i = 0; i < n; i++){
		 for (j = 0; j < arcana.length; j++){
		   c = new Card(this.isreversed() + arcana[j]);
			this.cards[this.cards.length] = c;
		 }
		 if(!this.arcana){ // is this an arcana only deck?
			 for (j = 0; j < suits.length; j++)
				for (k = 0; k < ranks.length; k++) {
				  c = new Card(this.isreversed() + ranks[k] + suits[j]);
				  this.cards[this.cards.length] = c;
				}
		 }
	  }
	}
	//-----------------------------------------------------------------------------
	// stackShuffle(n): Shuffles a stack of cards 'n' times. 
	//-----------------------------------------------------------------------------
	var stackShuffle = function(n) {
	// default is 1 shuffle
	  if(typeof n == 'undefined') n=1;
	  
	  var i, j, k;
	  var temp;
	
	  // Shuffle the stack 'n' times.
	
	  for (i = 0; i < n; i++)
		 for (j = 0; j < this.cards.length; j++) {
			k = Math.floor(Math.random() * this.cards.length);
			temp = this.cards[j];
			this.cards[j] = this.cards[k];
			this.cards[k] = temp;
		 }
	}
	//-----------------------------------------------------------------------------
	// stackDeal(): Removes the first card in the stack and returns it.
	//-----------------------------------------------------------------------------
	var stackDeal = function() {
	  if (this.cards.length > 0)
		 return this.cards.shift();
	  else
		 return null;
	}
	//-----------------------------------------------------------------------------
	// stackDraw(n): Removes the indicated card from the stack and returns it.
	//-----------------------------------------------------------------------------
	var stackDraw = function (n) {
	  var card;
	
	  if (n >= 0 && n < this.cards.length) {
		 card = this.cards[n];
		 this.cards.splice(n, 1);
	  }
	  else
		 card = null;
	
	  return card;
	}
	//-----------------------------------------------------------------------------
	// stackAdd(card): Adds the given card object to the stack.
	//-----------------------------------------------------------------------------
	var  stackAddCard = function (card) {
	  this.cards.push(card);
	}
	//-----------------------------------------------------------------------------
	// stackCardCount(): Returns the number of cards currently in the stack.
	//-----------------------------------------------------------------------------
	var stackCardCount = function () {
  		return this.cards.length;
	}

  // Create an empty array of cards.
	return {
		cards			: new Array(),
		arcana   	: (this.arcana?this.arcana:false),
		usereversed : (this.usereversed?this.usereversed:true),
		isreversed 	: stackIsreversed,
		makeDeck  	: (this.makeDeck?this.makeDeck:stackMakeDeck),
		shuffle  	: stackShuffle,
		deal  		: stackDeal,
		draw  		: stackDraw,
		addCard  	: stackAddCard,
		cardCount  	: stackCardCount
	};
}

//=============================================================================
// Reading Object
//=============================================================================
var Reading = function() {  
	//-----------------------------------------------------------------------------
	// getLayout(layoutName): Retrieve a layout object from the Layouts collection.  
	//              If no name is specified it returns the last layout.  And by
	//              default, the STANDARD layout. which is built-in.
	//-----------------------------------------------------------------------------
	var spreadLayout  = function(lay){
			if(typeof layouts != 'undefined')
				if(typeof layouts[lay] != 'undefined')
					return layouts[lay];
	// This is a default layout;
			return {
					code:'STANDARD',
					name:'Five Card Spread',
					cards:['Present/General Theme','Past Influences','Future','Reason','Potential'],
					positions:[[1,1],[0,1],[2,1],[1,2],[1,0]]
				};	
	}
	//-----------------------------------------------------------------------------
	// clear(): resets the playing field to blank status.  
	//                 Does not remove the select statment
	//-----------------------------------------------------------------------------
	var spreadClear 	= function (){
		$(this.spreadId).innerHTML="";
		var c = $(this.readingId).getElementsByTagName('CODE');
		c = c[0]
		if(typeof this.layout != 'undefined')
			c.innerHTML = this.layout.code+":";
		d=$(this.readingId);
		var p = d.getElementsByTagName('H3'); 
		p = p[0];
		if(p)
			p.innerHTML = this.layout.name;
		p = d.getElementsByTagName('P')
		while(p) {
			if(p.length>0) d.removeChild(p[0]); else break;
			p = d.getElementsByTagName('P');
		}
	}
	//-----------------------------------------------------------------------------
	// read(): Deals a spread and displays the interpretation if it is a previously
	//         dealt spread.
	//-----------------------------------------------------------------------------
  	var spreadRead 	= function(code){
		var lay, s, waitNode, c;
		var mySpread = $(this.spreadId);
		mySpread.innerHTML = "";
		waitNode = mySpread.cloneNode(false);
		//waitNode.id = 'tempSpreadNode';
		waitNode.appendChild(document.createElement('IMG'));
		waitNode.firstChild.border =0;
		waitNode.firstChild.src = this.loadImage;
		document.body.appendChild(waitNode);
		mySpread.style.display = 'none';
		this.spread = new Stack();	
	  if(code){
		  this.last = code;
		  lay = code.substr(0,code.indexOf(':'));
		  this.layout = this.getLayout(lay);
		  code = code.substr(code.indexOf(':')+1);
		  var reading = code.split('\.');
		  for (i = 0; i < this.layout.cards.length; i++){
		  		c = new Card(reading[i]);
				this.spread.addCard(c);
		  }
		  code = true;
	  } else {
			if(this.freshRead) {
				this.deck.makeDeck();
				this.deck.shuffle();
			}
		  s = $(this.readingId).getElementsByTagName('SELECT');
		  s = s[0]
		  if(s.options)
			  lay = s.options[s.selectedIndex].value;
		  else
			  lay = s.value;
		  this.layout = this.getLayout(lay);
	  		
		  this.deck.shuffle();
		  lay = lay + ":";
		  for (i = 0; i < this.layout.cards.length; i++){
			  	c = this.deck.deal();
				this.spread.addCard(c);
				lay = lay + (i>0?'.':'') + c.code;
		  }
			code = false;
			this.fortunes[this.fortunes.length] = lay;
			this.last = lay;
	  } 
	  this.display(code);
	  document.body.removeChild(waitNode);
	  mySpread.style.display = 'block';
  };
	//-----------------------------------------------------------------------------
	// display(): This renders the card objects in the spread DIV.  If the isVisible
	//            flag is true, the cards will be visible and the interpretations 
	//            will be visible as well
	//-----------------------------------------------------------------------------
	var spreadDisplay	= function(isVisible) {
	  this.clear();
	  isVisible = this.facedown==true ? (typeof isVisible == 'undefined'? false : isVisible) : true;
	  codelink = this.layout.code + ":"; maxy = 0; maxx = 0;
	  for (i = 0; i < this.spread.cardCount(); i++){
			cardNode = this.spread.cards[i].createNode();
			ypos = this.layout.positions[i][1];
			maxy = ypos>maxy ? ypos : maxy;
			xpos = this.layout.positions[i][0];
			maxx = xpos>maxx ? xpos : maxx;
			cardNode.style.top  = (ypos*(this.spread.cards[i].height+this.gap)) +'em';
			cardNode.style.left = (xpos*(this.spread.cards[i].width +this.gap)) +'em';
			cardNode.id = 'spread'+i;
			code = this.spread.cards[i].code;
			$(this.spreadId).appendChild(cardNode);
			if(isVisible||!this.facedown) {
				// if visible turn card face up and display meaning
				this.spread.cards[i].cardHandler(cardNode, this.spread.cards[i].code);
			}
			c=$(this.readingId).getElementsByTagName('CODE');
			c = c[0];
			c.appendChild(document.createTextNode((i>0?'.':'')+this.spread.cards[i].code));
			codelink = codelink + (i>0?'.':'')+this.spread.cards[i].code;
		}
		if((!isVisible&&codelink!="")){
			tempNode = document.createElement('A');
			tempNode.href="#";
			tempNode.title = codelink;
			tempNode.appendChild(document.createTextNode(this.layout.name.replace(/[<\(][^\)\(<>]*?[\)>]/g,"")));
			addEvent(tempNode,'click', new Function('ev', "tarot.read('"+codelink+"'); return void(0); "));
			$(this.readingId).appendChild(tempNode);
			$(this.readingId).appendChild(document.createElement('BR'));
		}
		h =$(this.spreadId);
		maxx = (maxx+1) * (this.spread.cards[0].width + this.gap);
		maxy = (maxy+1) * (this.spread.cards[0].height+ this.gap);
		ratio = Math.round(this.width/maxx*100)/100;
		if(maxy/ratio>this.width){
			ratio = ratio * Math.round(this.width/maxy*100)/100;
		} 
		h.style.height = maxy + "em";
		h.style.width = maxx + "em";
		h.style.fontSize = ratio + "em";
	}
	//-----------------------------------------------------------------------------
	// init():	This initializes the required DIVs for doing a reading.  
	//-----------------------------------------------------------------------------
	var spreadInit = function(){
  		var tempNode;
		if(!$(this.spreadId)){
			tempNode = document.createElement('DIV');
			tempNode.id = this.spreadId;
			document.body.appendChild(tempNode);
		}
		if(!$(this.readingId)){
			tempNode = document.createElement('DIV');
			tempNode.id = this.readingId;
			document.body.appendChild(tempNode);
			$(this.readingId).appendChild(document.createElement("H3"));
		} else {
			$(this.readingId).innerHTML = "";
			$(this.readingId).appendChild(document.createElement("H3"));
		}
		var c=$(this.readingId).getElementsByTagName('CODE');
		if(!c[0]){
			tempNode = document.createElement('CODE');
			$(this.readingId).appendChild(tempNode);
			$(this.readingId).appendChild(document.createElement('BR'));
		}
	//	is there a select statement for layouts?
		var s = $(this.readingId).getElementsByTagName('SELECT');
		if(!s[0]){
			// Add an action button
			var tempNode = document.createElement('INPUT');
			tempNode.type='button';
			tempNode.value = 'Read';
			addEvent(tempNode, 'click', new Function("tarot.read(); return void(0);"));
			$(this.readingId).insertBefore(tempNode, $(this.readingId).firstChild);
			// Add Layouts
			if(typeof layouts != 'undefined'){
				tempNode = document.createElement('SELECT');
				for(lay in layouts) {
					tempNode.options.length++;
					tempNode.options[tempNode.options.length-1].value = lay;
					tempNode.options[tempNode.options.length-1].text = layouts[lay].name.replace(/[<\(][^\)\(<>]*?[\)>]/g,"");
				}
			} else if(typeof this.layout != 'undefined'){
				var tempNode = document.createElement('INPUT');
				tempNode.type='hidden';
				tempNode.value = this.layout.code;
			} else {
				var tempNode = document.createElement('INPUT');
				tempNode.type='hidden';
				tempNode.value = 'STANDARD';
			}
			tempNode.name='layouts';
			$(this.readingId).insertBefore(tempNode, $(this.readingId).firstChild);
		}
		var r = $(this.readingId).innerHTML;
		if(!r.match(/\(c\) 2008 C. David Dent/)){
			tempNode = document.createElement('DIV');
			tempNode.innerHTML = 'Javascript Tarot (c) 2008 C. David Dent <a href="http://wildandbad.com:8181/tarot">Wild and Bad</a>';
			tempNode.className = "copyright";
			$(this.readingId).insertBefore(tempNode, $(this.readingId).firstChild);
		}
		var i;
		for(i=0; i<this.fortunes.length; i++) {
			c = this.getLayout(this.fortunes[i].substring(0,this.fortunes[i].indexOf(":")));
			tempNode = document.createElement('A');
			tempNode.href="#";
			tempNode.title = this.fortunes[i];
			tempNode.appendChild(document.createTextNode(c.name));
			addEvent(tempNode,'click', new Function('ev', "tarot.read('"+this.fortunes[i]+"'); return void(0); "));
			$(this.readingId).appendChild(tempNode);
			$(this.readingId).appendChild(document.createElement('BR'));	
		}
	}
	//-----------------------------------------------------------------------------
	// Constructor:	Created each time 
	//-----------------------------------------------------------------------------
	var deck 		= new Stack();
	deck.makeDeck();
	return {
   	deck		 : deck,
  		layout    : spreadLayout('STANDARD'),
		width		 : (this.width?this.width:25), 
		gap		 : (this.gap?this.gap:0.5), 
		facedown  : (this.facedown?this.facedown:true),
		freshRead : (this.freshRead?this.freshRead:true),
		spreadId  : (this.spreadId?this.spreadId:'spread'),
		readingId : (this.readingId?this.readingId:'reading'),
		loadImage : (this.loadImage?this.loadImage:'ajax-loader.gif'),
		init		 : spreadInit,
		display   : spreadDisplay,
		read		 : spreadRead,
		clear	    : spreadClear,
		getLayout : spreadLayout,
		fortunes  : [],
		last      : ''
	};
}

//=============================================================================
// CardHandler Routine
//=============================================================================
function replaceCardHandler(ev, code){
	var n;
	var e = eventCall(ev);
	var c = new Card(code);
	if(e.id && e.id.substr(0,6)=='spread') {
		e.firstChild.style.visibility = 'visible';
		n = e.id.substr(6);
		tempNode = document.createElement('P');
		tempNode.appendChild(document.createElement("STRONG"));
		tempNode.firstChild.appendChild(document.createTextNode(tarot.layout.cards[n]+":"));
		tempNode.appendChild(document.createElement("EM"));
		tempNode.lastChild.appendChild(document.createTextNode("("+c+")"));
		tempNode.appendChild(document.createTextNode(" "+c.interpret));
		$(tarot.readingId).appendChild(tempNode);
	} else {
//		console.debug(c);
//		alert(c + "\n" + c.interpret);
	  while(!(e.id && e.id.substr(0,6)=='spread')){
		  e = e.parentNode;
	  }
	  // make them all 'normal-sized'
	  for(i=0; i<e.parentNode.childNodes.length; i++) {
		  e.parentNode.childNodes[i].style.marginLeft = '0px';
		  e.parentNode.childNodes[i].style.marginTop  = '0px';
		  e.parentNode.childNodes[i].style.top  = e.parentNode.childNodes[i].offsetTop  + "px";
		  e.parentNode.childNodes[i].style.left = e.parentNode.childNodes[i].offsetLeft  + "px";
		  e.parentNode.childNodes[i].style.fontSize = '100%';
	  }
	  if(e.style.zIndex<100){
	  	  oh = e.offsetHeight;
	  	  ow = e.offsetWidth;
		  fs = oh/c.height;
		  e.style.fontSize = '60px';
		  nh = e.offsetHeight;
		  nw = e.offsetWidth;
		  e.style.marginLeft = '-' + Math.floor((nw-ow)/2) + 'px';
		  e.style.marginTop  = '-' + Math.floor((nh-oh)/2) + 'px';
	  	  e.style.zIndex = e.style.zIndex+100;
	  } else {
	  	  e.style.zIndex = e.style.zIndex%100;
	  }
	}
}
