1 function Board(options) {
  2 
  3 	//PRIVATE INSTANCE VARIABLES
  4 	options = options || {};
  5 	var __MY_ROOT = options.rootId || "BoardRoot";
  6 	var __HAND_HOLDER = options.handId || "HandRoot";
  7 	var __DECK_OPACITY = options.cardOpacity || .9;
  8 	var __EXTENSION_TYPE = options.extensionType || "gif";
  9 	var __IMAGES_DIRECTORY = options.imagesDirectory || "images/";
 10 	var __CARD_WIDTH = options.cardWidth || 73;
 11 	var __CARD_HEIGHT = options.cardHeight || 102;
 12 	var __DECK_HEIGHT = options.deckHeight || 204;
 13 	var __MAGICAL_UNIT_X = options.magicalX || 150;
 14 	var __MAGICAL_UNIT_Y = options.magicalY || 220;
 15 	var __HAND_WIDTH = options.handWidth || 500;
 16 	var __MULTI_DRAG = (options.multiDrag===false) ? false : true;
 17 	var handHolder=$("#"+__HAND_HOLDER);
 18 	handHolder.addClass("board");
 19 	var root=$("#"+__MY_ROOT);
 20 	root.addClass("board");
 21 	var decks = new Array();
 22 	var appendString = "cardHolder";
 23 	var counter = 0;
 24 	var cardHash = {};
 25 	var maxX = 0;
 26 	var maxY = 0;
 27 	
 28 	//PUBLIC TYPE PROPERTIES
 29 	
 30 	this.defaultType = function(deck) {
 31 		var isHand = isNaN(deck.getX());
 32 		//the offset for hands is proportional to the size of the hand
 33 		var minOverlapX = __CARD_WIDTH / 2;
 34 		var maxOverlapX = ((__HAND_WIDTH - __CARD_WIDTH) / deck.getSize());
 35 		var overlapX = (minOverlapX < maxOverlapX) ? minOverlapX : maxOverlapX;
 36 		var offsetX = isHand ? overlapX : 0;
 37 		//the offset for decks is proportional to the size of the deck
 38 		var minOverlap = __CARD_HEIGHT / 5;
 39 		var maxOverlap = ((__DECK_HEIGHT - __CARD_HEIGHT) / deck.getSize());
 40 		var overlap = (minOverlap < maxOverlap) ? minOverlap : maxOverlap;
 41 		var offsetY = isHand ? 0 : overlap;
 42 		return [offsetX, offsetY];
 43 	};
 44 	this.collapsedType = function(deck) {
 45 		return [0,0];
 46 	};
 47 	this.horizontalType = function(deck) {
 48 		//the offset for hands is proportional to the size of the hand
 49 		var minOverlapX = __CARD_WIDTH / 2;
 50 		var maxOverlapX = ((__HAND_WIDTH - __CARD_WIDTH) / deck.getSize());
 51 		var overlapX = (minOverlapX < maxOverlapX) ? minOverlapX : maxOverlapX;
 52 		var offsetX = overlapX;
 53 		var offsetY = 0;
 54 		return [offsetX, offsetY];
 55 	};
 56 	
 57 	//PRIVATE FUNCTIONS
 58 	
 59 	function createDeck(deck) {
 60 		//observe events
 61 		deck.observe(function() {
 62 			for(var i = 0; i < decks.length; i++) {
 63 				if(deck == decks[i][0]) {
 64 					return reDrawDeck(decks[i]);
 65 				}
 66 			}
 67 			return null;
 68 		});
 69 		var isHand = isNaN(deck.getX());
 70 		//add ui
 71 		var div = $("<div>");
 72 		div.droppable({ 
 73 			accept : function(el) {
 74 				var cards=getCards(el[0]);
 75 				return deck.getFilter()(cards[cards.length-1],getDeck(el[0]),cards.length);
 76 			},
 77 			drop : function(event, ui) {
 78 				var srcDeck=event.srcElement.parentElement;
 79 				var cards=getCards(srcDeck);
 80 				for(var i=cards.length-1;i>=0;i--) {
 81 					try {
 82 						console.log(cards[i]);
 83 						getDeck(srcDeck).remove(cards[i]);
 84 						deck.addTop(cards[i]);
 85 					} catch(e) {
 86 						continue;
 87 					}
 88 				}
 89 				reDrawDeck([getDeck(srcDeck), div]);
 90 				reDrawDeck([deck, $(event.target)]);
 91 			}
 92 		});
 93 		div.addClass(isHand ? "hand" : "deck");
 94 		if(isHand&&deck.isActive()) {
 95 			handHolder.append(div);
 96 		} else if(!isHand) {
 97 			root.append(div);
 98 		}
 99 		return reDrawDeck([deck, div]);
100 	}
101 	
102 	function getSuitName(card) {
103 		var suit=card.getSuit().toUpperCase();
104 		switch(suit) {
105 			case "H":
106 				suit="of hearts";
107 				break;
108 			case "D":
109 				suit="of diamonds";
110 				break;
111 			case "C":
112 				suit="of clubs";
113 				break;
114 			case "S":
115 				suit="of spades";
116 				break;
117 		}
118 		return suit;
119 	}
120 	
121 	function getOffsetByType(deck) {
122 		return deck.getType()(deck);
123 	}
124 	
125 	function getCard(element) {
126 		return cardHash[element.id][1];
127 	}
128 	
129 	function getCards(element) {
130 		var cards=[];
131 		if(__MULTI_DRAG) {
132 		var children=$(element).children(".card");
133 			if(children) {
134 				for(var i = 0; i < children.length; i++) {
135 					cards.push(getCard(children[i]));
136 				}
137 			}
138 		}
139 		cards.push(getCard(element));
140 		return cards;
141 	}
142 	
143 	function getDeck(element) {
144 		return cardHash[element.id][0];
145 	}
146 	
147 	function reDrawDeck(deckArr) {
148 		var deck = deckArr[0];
149 		var div = deckArr[1];
150 		div[0].onclick=deck.getAction();
151 		maxX = deck.getX() > maxX ? deck.getX() : maxX;
152 		maxY = deck.getY() > maxY ? deck.getY() : maxY;
153 		root.css("width",((maxX+1)*__MAGICAL_UNIT_X)+"px");
154 		root.css("height",((maxY+1)*__MAGICAL_UNIT_Y)+"px");
155 		div.empty();
156 		var isHand = isNaN(deck.getX());
157 		if(!isHand) {
158 			div.css("left", deck.getX()*__MAGICAL_UNIT_X);
159 			div.css("top", deck.getY()*__MAGICAL_UNIT_Y);
160 		}
161 		var offsets=getOffsetByType(deck);
162 		var offsetX=offsets[0];
163 		var offsetY=offsets[1];
164 		var cards = deck.getCards();
165 		var soFar=new Array();
166 		for(var i = cards.length - 1; i >= 0; i--) {
167 			var newDiv=(function (stack,i) {
168 				var card = cards[i];
169 				var holder = $("<div>");
170 				cardHash[appendString+counter]=[deck,card];
171 				holder.attr("id", appendString+(counter++));
172 				holder.css("z-index",i+deck.getzOffset());
173 				if(card.isFaceUp()&&deck.isDraggable()) {
174 					holder.draggable({ 
175 						revert : "invalid",
176 						start : function(event, ui) {
177 							holder.css("z-index",99+deck.getzOffset());
178 							if(__MULTI_DRAG) {
179 								for(var j = 0; j < stack.length; j++) {
180 									stack[j][1].detach();
181 									holder.append(stack[j][1]);
182 								}
183 							}
184 						},
185 						stop : function(event, ui) {
186 							var srcDeck=event.srcElement.parentElement;
187 							holder.css("z-index",i+deck.getzOffset());
188 							reDrawDeck([getDeck(srcDeck),div]);
189 						}
190 					});
191 				}
192 				holder.addClass("card");
193 				holder.css("left", (offsetX * i)+"px");
194 				holder.css("top", (offsetY * i)+"px");
195 				holder.css("position", "absolute");
196 				var img = $("<img>");
197 				if(card.isFaceUp()) {
198 					img.attr("alt", card.getRankAsString()+" "+getSuitName(card));
199 					img.attr("title", card.getRankAsString()+" "+getSuitName(card));
200 				}
201 				img.attr("src", card.isFaceUp() ? (__IMAGES_DIRECTORY+card.getRank()+card.getSuit()+"."+__EXTENSION_TYPE) : (__IMAGES_DIRECTORY+"back."+__EXTENSION_TYPE));
202 				img.css("opacity", __DECK_OPACITY);
203 				holder.append(img);
204 				div.append(holder);
205 				return holder;
206 			})(soFar.concat(),i);
207 			soFar.push([cards[i],newDiv]);
208 		}
209 		div.css("width",((cards.length)*offsetX)+__CARD_WIDTH);
210 		div.css("height",((cards.length)*offsetY)+__CARD_HEIGHT);
211 		return div;
212 	}
213 	
214 	//PUBLIC FUNCTIONS
215 	
216 	this.addDeck = function (deck) {
217 		return decks.push([deck, createDeck(deck)]);
218 	};
219 	
220 	this.getDecks = function() {
221 		return decks;
222 	};
223 }