/* ----------------------------------------------------------------------------

	Matthew Brewer
	CS411 Spring 2008 Semester Project
	April 22, 2008
	
	Shitboots is a multiplayer card game.  For information regarding 
	the objective, gameplay strategies, and rules, see the included 
	README file.
	
	
	REQUIREMENTS:
		
		#@ This program requires the MooTools Javascript Library, v1.1 
		
			MooTools is available for download at http://www.mootools.net
			
			Documentation: http://docs.mootools.net



		#@ This program requires the Firebug Lite Javascript Library
		
			Include either firebug.js or firebugx.js for this program
			to work in other browsers besides FireFox with the Firebug
			plugin.
			
			http://www.getfirebug.com	For the latest FireBug release


--------------------------------------------------------------------------------- */









// Categories on the native Array object
Array.extend({
	
	/* ----------------------------------------------------------------
	
		.remove() removes all occurrences of an object from the array
		
		.removeOne() removes only one occurence.  Will place the last
		item of the array in the slot that is being removed.  If the item 
		being removed was the last one, it is just deleted.  
		
		Therefore, don't use this function if order is important, b/c this
		will severely screw up the array

		
		@	@	@	@	@	@	@	@	@	@	@	@	@	@	@	@
		NOTE: This is not used in the project b/c this version is only
		two players, and then there will never be duplicate cards.
		
	--------------------------------------------------------------------*/
	removeOne: function(object) {
		if ( object ) {
			var index = this.indexOf(object);
			if ( index > 0 && index < this.length - 1) {
				this[index] = this.pop();
			} else this.pop();
		}
	},
	
	
	
	
	
	/* ----------------------------------------------------------- 
		Easy way to figure out what the lowest card in the hand is
		
							Based upon weight
	-------------------------------------------------------------*/
	getLowestCard: function() {
		
		if ( this.length == 0 ) return null;
		
		var lowest_card = this[0];
		this.each(function(el) {
			if ( el.weight < lowest_card.weight ) {
				lowest_card = el;
			}
		});
		return lowest_card;
	},
	
	
	
	/* -----------------------------------------------------------
		Computer wants to pick it's best cards for it's topBoots
		
				Just returns the top weighted card
	------------------------------------------------------------*/
	
	getTopWeightedCard: function() {
	
		if ( this.length == 0 ) return null;
		
		var high = this[0];
		this.each(function(card) {
			if ( card.weight > high.weight ) high = card;
		});
		return high;
	}
});
		
		
		
		
		


/* ---------------------------------------------------------------------------------

										Card
									
	This class has a dual purpose.  When created at the beginning of the game,
	the card has information about a card and can create a DOM element with the 
	-domElement() method.
	
	However, once the computer needs to reclaim discarded cards, it has to store 
	them behind the scenes of the user and out of the DOM.  Therefore, Card can 
	be used to recapture the DOM element from earlier and supports all previous
	methods and properties, except for 'suite'.
	
	Examples:
		
		var myCard = new Card('Diamonds', 'Joker', false);
		myCard contains all properties, but until you call -domElement(), there is 
		no DOM element associated with this object.
		
		Conversely, the following is used to create a wrapper around an existing DOM
		
		var existingCard = new Card(domElement, null, true);
		existingCard contains all methods and properties, except for 'suite'
		
------------------------------------------------------------------------------------ */
		
		
var Card = new Class({
    
	
	initialize: function(suite, name, isWrapper){
       
		// If this is just a background card object (internal use)
		if ( !isWrapper ) {
			this.name = name;
			this.suite = suite;
			
			// Calculate the weight of the card, used for validating drops
			this.weight = 0;
			if ( this.name == '8' ) {
				this.weight = 17;
				this.symbol = '8';
			} else if ( this.name == 'Joker' ) {
				this.weight = 16;
				this.symbol = '*';
			} else if ( this.name == '2' ) {
				this.weight = 15;
				this.symbol = '2';
			} else if ( this.name == 'Ace' ) {
				this.weight = 14;
				this.symbol = 'A';
			} else if ( this.name == 'King' ) {
				this.weight = 13;
				this.symbol = 'K';
			} else if ( this.name == 'Queen' ) {
				this.weight = 12;
				this.symbol = 'Q';
			} else if ( this.name == 'Jack' ) {
				this.weight = 11;
				this.sybmol = 'J';
			} else {
				this.weight = this.name.toInt();
				this.symbol = this.name;
			}
		} 
		
		
		// Otherwise, we're not going to create the DOM below, but we're passed an existing one
		else {
			
			// Just b/c I still want that member
			this.suite = 'isWrapper';
			
			// Suite is really the DOM object
			// Name is the 'rel' - weight
			this.dom = suite;
			
			// Get the weight from the DOM object
			this.weight = this.dom.getProperty('rel').toInt();
			
			// Figure out the name from the weight given (backwards of above)
			if ( this.weight == 17 ) {
				this.name = '8';
				this.symbol = '8';
			} else if ( this.weight == 16 ) {
				this.name = 'Joker';
				this.symbol = '*';
			} else if ( this.weight == 15 ) {
				this.name = '2';
				this.symbol = '2';
			} else if ( this.weight == 14 ) {
				this.name = 'Ace';
				this.symbol = 'A';
			} else if ( this.weight == 13 ) {
				this.name = 'King';
				this.symbol = 'K';
			} else if ( this.weight == 12 ) {
				this.name = 'Queen';
				this.symbol = 'Q';
			} else if ( this.weight == 11 ) {
				this.name = 'Jack';
				this.symbol = 'J';
			} else {
				this.name = this.weight.toString();
				this.symbol = this.weight.toString();
			}
		}
    },

	// Will return a div that looks just like the card it represents
	domElement: function() {
	
				
		
		// If the dom member isn't there, create it 
		if ( !this.dom ) {
				
			// Set the background image
			if ( this.suite == 'Hearts') var src = 'url(images/Hearts.png)';
			else if ( this.suite == 'Clubs') var src = 'url(images/Clubs.png)';
			else if ( this.suite == 'Spades') var src = 'url(images/Spades.png)';
			else var src = 'url(images/Diamonds.png)';

			this.dom = new Element('div', {'class':'card'});
			this.dom.setStyle('background',src);
			
			
			/* ---------------------------------------------------------------------
			
			In the game, you're constantly dragging <div> items
			around the page and injecting in other parts of the document.
			
			Weight is used to validate a drop.  Wilds have highest weight, then 
			the weight decreases as the card value decreases.
			
			---------------------------------------------------------------------- */		
			this.dom.setProperty('rel', this.weight.toString());	// Have to store as string...
			
		
			// Overlay the name of the card on the background
			var overlay = new Element('img', {'src':'images/card-numbers.png'});
		
			// Calculate the correct padding so the overlay shows the right name
			var pad = 0;
			if ( this.name == 'Joker') pad = -1400;
			else if ( this.name == 'King') pad = -1300;
			else if ( this.name == 'Queen') pad = -1200;
			else if ( this.name == 'Jack') pad = -1100;
			else if ( this.name == 'Ace') pad = 0;
			else pad = this.name.toInt() * -100;
		
			// Set the padding and inject
			overlay.setStyle('margin-left', pad);
			overlay.injectInside(this.dom);
		} 
		
		// Return the finished card (or one we already storing)
		return this.dom;
	}
});








/* ---------------------------------------------------------------------------

								Player
								
	Class for a player, obviously.  Has the players bottomBoots, the ones you
	don't pick and see until the end, topBoots, which you do pick, and then the
	six cards you start out with in your hand.
	
	-takeTurn() makes the computer play
	
----------------------------------------------------------------------------- */

var Player = new Class({
	
	initialize: function(name) {
		if ( name != null ) this.name = name;
		else this.name = '';
						
		this.hand = new Array();			// Will hold the cards that you can use
		this.bottomBoots = new Array();		// Will hold the 3 hards that you can't see
		this.topBoots = new Array();		// Player can choose these, visible on top of the other bottom boots
	},
	
	
	
	// Use for the computer doing it's stuff
	takeTurn: function() {
		
		console.log("Computer's turn");
		
		var discard = $('discard');
		
		// If there is a card from hand to discard
		if ( this.hand.length > 0 ) {
			
			// "Validate the drop" - but using JS objects and not DOM elements as
			// done in the GUI
			var topCard = discard.getLast();
			if ( topCard ) {
			
			
				/* --------------------------------------------------------
				
					Figure out what the lowest card, >= card on discard
					we have in our hand.  If nothing works, then draw later
					
				-----------------------------------------------------------*/
				var discard_weight = topCard.getProperty('rel').toInt();
			
				// If the top card is a Joker or a 2 (8 would already be gone), reset weight
				if ( discard_weight == 16 || discard_weight == 15 ) discard_weight = 2;
				
				// Get an array of possible cards to play
				var gte_cards = new Array();
				this.hand.each(function(el) {
					if ( el.weight >= discard_weight ) gte_cards.push(el);
				});
				
				// If we have those cards, pick the lowest to use
				if ( gte_cards.length > 0 ) var lowest = gte_cards.getLowestCard();
				

				// If we do have something to play
				if ( lowest ) {
				
					// card.weight == el.weight at least once (cause card is still in gte_cards)!!!
					gte_cards.each(function(card) {
					
						if ( card.weight == lowest.weight ) {
						
							// Remove the card from the computer's hand
							this.hand.remove(card);
						
							// Now to move the previous card to the discard-overflow div, 
							// which is completely hidden
							var previous = discard.getLast();
							if ( previous )  {
								previous.removeEvents();	// Remove the DblClick event that was here
								previous.remove();
								previous.setStyles({
									'position' : 'relative',
									'left' : 0,
									'right' : 0,
									'top': 0,
									'bottom' : 0,
									'cursor': 'move'
								});
								previous.inject($('discard-overflow'));
							}
					
					
							
							// Style and inject a non-draggable card on discard
							var dom = card.domElement();
							var fx = dom.effect('opacity', {duration:1500}).set(0);
							
							// Setup so user can dblclick on the card to draw cards from discard into hand
							dom.addEvent('dblclick', place_cards_in_hand);
							
							// Actually insert the card
							dom.injectInside(discard);
							fx.start(1);
							
							// For debugging
							console.info("Computer discarded a " + card.name + ' of ' + card.suite);
							
							// If placing an 8, need to kill the deck!
							if ( card.name == '8' ) {
								$('discard-overflow').empty();
								
								/* ------------- Quick Note ---------------
						
								Had used: discard.empty()
								
								However, caused errors b/c discard was still 
								calling empty() when effect was applied.
								
								Doing: getLast().remove() works on removing 
								the child, and not modifying discard itself,
								so the effect executes without a billion errors
								trying to apply CSS changes every .0000001 seconds
								
								-------------------------------------------- */
								var finished = discard.getLast().remove();
								var eff = discard.effect('background-color', {duration:600}).start('#fa0').chain(function() {
										eff.start('#303030');
								});
														
								var was8_or_Joker = true;	// So know to take another turn
								
								// Let user know why computer is taking two turns											
								update_status(status_messages['computer_8_or_Joker']);
							}
							
							
							// If was dropping a Joker, set flag to take another turn
							else if ( card.name == 'Joker' ) var was8_or_Joker = true;
							
							
							// Check to see if we need to draw a card (and there is some available)
							if ( this.hand.length < 3 && deck.cardAvailable() ) {
								console.info("Computer is drawing a card");
								
								// Actually draw the cards and place in hand
								var drew = deck.drawCard();
								if ( drew ) {
									this.hand.push(drew);
								} 
								
								// If computer drew the last card, fix the deck to show that
								if ( !deck.cardAvailable() ) {
									$('deck').effect('opacity', {duration:700}).start(0.5);
									$('deck').setStyle('cursor', 'default');
								}
							}
							
							// Check to see if the computer one by playing this card
							if ( this.hand.length == 0 && this.topBoots.length == 0 && this.bottomBoots.length == 0 ) {
								var p = $('player');
								update_status(status_messages['computer_won']);
								var eff = p.effect('opacity', {duration:900}).start(0).chain(function() {
									p.setHTML('<a href="play.html"><img src="images/computer-won.png"/></a>');
									eff.start(1);
								});
								
								console.log("Computer won while playing last from #hand");
								return;
							}
							
							// If had dropped an 8 or Joker, computer gets to go again
							if ( was8_or_Joker ) this.takeTurn.delay(comp_delay, this);
						}
					}, this);
										
				}
				
				// Has to draw from discard pile (was no card)
				else this.pickup_discarded_cards();
			} 
			
			
			// Discard pile is empty, so can play any card! (We're nice and play the lowest in our hand)
			else {
			
				// Lowest card in hand (wilds rated high)
				var lowest = this.hand.getLowestCard();
				
				// If we do have something to play
				if ( lowest ) {
				
					// card.weight == el.weight at least once (cause card is still in gte_cards)!!!
					this.hand.each(function(card) {
					
						if ( card.weight == lowest.weight ) {
						
							// Remove the card from the computer's hand
							this.hand.remove(card);
						
							// Now to move the previous card to the discard-overflow div, 
							// which is completely hidden
							var previous = discard.getLast();
							if ( previous )  {
								previous.removeEvents();	// Remove the DblClick event that was here
								previous.remove();
								previous.setStyles({
									'position' : 'relative',
									'left' : 0,
									'right' : 0,
									'top': 0,
									'bottom' : 0,
									'cursor': 'move'
								});
								previous.inject($('discard-overflow'));
							}
					
					
							
							// Style and inject a non-draggable card on discard
							var dom = card.domElement();
							var fx = dom.effect('opacity', {duration:1500}).set(0);
							
							// Setup so user can dblclick on the card to draw cards from discard into hand
							dom.addEvent('dblclick', place_cards_in_hand);
							
							// Actually insert the card
							dom.injectInside(discard);
							fx.start(1);
							
							// For debugging
							console.info("Computer discarded a " + card.name + ' of ' + card.suite);
							
							// If placing an 8, need to kill the deck!
							if ( card.name == '8' ) {
								$('discard-overflow').empty();
								
								/* ------------- Quick Note ---------------
						
								Had used: discard.empty()
								
								However, caused errors b/c discard was still 
								calling empty() when effect was applied.
								
								Doing: getLast().remove() works on removing 
								the child, and not modifying discard itself,
								so the effect executes without a billion errors
								trying to apply CSS changes every .0000001 seconds
								
								-------------------------------------------- */
								var finished = discard.getLast().remove();
								var eff = discard.effect('background-color', {duration:600}).start('#fa0').chain(function() {
										eff.start('#303030');
								});
														
								var was8_or_Joker = true;	// So know to take another turn
								
								// Let user know why computer is taking two turns											
								update_status(status_messages['computer_8_or_Joker']);
							}
							
							
							// If was dropping a Joker, set flag to take another turn
							else if ( card.name == 'Joker' ) var was8_or_Joker = true;
							
							
							// Check to see if we need to draw a card (and there is some available)
							if ( this.hand.length < 3 && deck.cardAvailable() ) {
								console.info("Computer is drawing a card");
								
								// Actually draw the cards and place in hand
								var drew = deck.drawCard();
								if ( drew ) {
									this.hand.push(drew);
								} 
								
								// If computer drew the last card, fix the deck to show that
								if ( !deck.cardAvailable() ) {
									$('deck').effect('opacity', {duration:700}).start(0.5);
									$('deck').setStyle('cursor', 'default');
								}
							}
							
							// Check to see if the computer one by playing this card
							if ( this.hand.length == 0 && this.topBoots.length == 0 && this.bottomBoots.length == 0 ) {
								var p = $('player');
								update_status(status_messages['computer_won']);
								var eff = p.effect('opacity', {duration:900}).start(0).chain(function() {
									p.setHTML('<a href="play.html"><img src="images/computer-won.png"/></a>');
									eff.start(1);
								});
								
								console.log("Computer won while playing last from #hand");
								return;
							}
							
							// If had dropped an 8 or Joker, computer gets to go again
							if ( was8_or_Joker ) this.takeTurn.delay(comp_delay, this);
						}
					}, this);
				}
			}
		}
		
		
		
		// Otherwise can use a topboot
		else if ( this.topBoots.length > 0 ) {
			
			console.info('Computer playing a topBoot now');
			
			var topCard = discard.getLast();
			if ( topCard ) {
			
				/* --------------------------------------------------------
				
					Figure out what the lowest card, >= card on discard
					we have in our hand.  If nothing works, then draw later
					
				-----------------------------------------------------------*/
				var discard_weight = topCard.getProperty('rel').toInt();
			
				// If the top card is a Joker or a 2 (8 would already be gone), reset weight
				if ( discard_weight == 16 || discard_weight == 15 ) discard_weight = 2;

				// Get an array of possible cards to play
				var gte_cards = new Array();
				this.topBoots.each(function(el) {
					if ( el.weight >= discard_weight ) gte_cards.push(el);
				});
				
				// If we have those cards, pick the lowest to use
				if ( gte_cards.length > 0 ) var lowest = gte_cards.getLowestCard();
					
				// If we do have something to play
				if ( lowest ) {
				
					// card.weight == el.weight at least once (cause card is still in gte_cards)!!!
					gte_cards.each(function(card) {
					
						if ( card.weight == lowest.weight ) {
						
							// Remove the card from the computer's hand
							this.topBoots.remove(card);
						
							// Now to move the previous card to the discard-overflow div, 
							// which is completely hidden
							var previous = discard.getLast();
							if ( previous )  {
								previous.removeEvents();	// Remove the DblClick event that was here
								previous.remove();
								previous.setStyles({
									'position' : 'relative',
									'left' : 0,
									'right' : 0,
									'top': 0,
									'bottom' : 0,
									'cursor': 'move'
								});
								previous.inject($('discard-overflow'));
							}
					
					
							// Style and inject a non-draggable card on discard
							var dom = card.domElement();
							var fx = dom.effect('opacity', {duration:1500}).set(0);
							
							// Setup so user can dblclick on the card to draw cards from discard into hand
							dom.addEvent('dblclick', place_cards_in_hand);
							
							// Actually insert the card
							dom.injectInside(discard);
							fx.start(1);
							
							// For debugging
							console.info("Computer discarded a " + card.name + ' of ' + card.suite);
							
							// If placing an 8, need to kill the deck!
							if ( card.name == '8' ) {
								$('discard-overflow').empty();
								
								/* ------------- Quick Note ---------------
						
								Had used: discard.empty()
								
								However, caused errors b/c discard was still 
								calling empty() when effect was applied.
								
								Doing: getLast().remove() works on removing 
								the child, and not modifying discard itself,
								so the effect executes without a billion errors
								trying to apply CSS changes every .0000001 seconds
								
								-------------------------------------------- */
								var finished = discard.getLast().remove();
								var eff = discard.effect('background-color', {duration:600}).start('#fa0').chain(function() {
										eff.start('#303030');
								});
														
								var was8_or_Joker = true;	// So know to take another turn
								
								// Let user know why computer is taking two turns											
								update_status(status_messages['computer_8_or_Joker']);
							}
							
							
							// If was dropping a Joker, set flag to take another turn
							else if ( card.name == 'Joker' ) var was8_or_Joker = true;
							
							
							// Check to see if we need to draw a card (and there is some available)
							if ( this.hand.length < 3 && deck.cardAvailable() ) {
								console.info("Computer is drawing a card");
								
								// Actually draw the cards and place in hand
								var drew = deck.drawCard();
								if ( drew ) {
									this.hand.push(drew);
								} 
								
								// If computer drew the last card, fix the deck to show that
								if ( !deck.cardAvailable() ) {
									$('deck').effect('opacity', {duration:700}).start(0.5);
									$('deck').setStyle('cursor', 'default');
								}
							}
							
							
							// If had dropped an 8 or Joker, computer gets to go again
							if ( was8_or_Joker ) this.takeTurn.delay(comp_delay, this);
						}
					}, this);
										
				}
			
				// None of the topBoots would work, so draw from discard, placing in hand
				else this.pickup_discarded_cards();
			}
			
			// Playing a topBoot, but no cards in #discard
			else {
			
				var lowest = this.topBoots.getLowestCard();
				
				// If we do have something to play
				if ( lowest ) {
				
					// card.weight == el.weight at least once (cause card is still in gte_cards)!!!
					this.topBoots.each(function(card) {
					
						if ( card.weight == lowest.weight ) {
						
							// Remove the card from the computer's hand
							this.topBoots.remove(card);
						
							// Now to move the previous card to the discard-overflow div, 
							// which is completely hidden
							var previous = discard.getLast();
							if ( previous )  {
								previous.removeEvents();	// Remove the DblClick event that was here
								previous.remove();
								previous.setStyles({
									'position' : 'relative',
									'left' : 0,
									'right' : 0,
									'top': 0,
									'bottom' : 0,
									'cursor': 'move'
								});
								previous.inject($('discard-overflow'));
							}
					
					
							
							// Style and inject a non-draggable card on discard
							var dom = card.domElement();
							var fx = dom.effect('opacity', {duration:1500}).set(0);
							
							// Setup so user can dblclick on the card to draw cards from discard into hand
							dom.addEvent('dblclick', place_cards_in_hand);
							
							// Actually insert the card
							dom.injectInside(discard);
							fx.start(1);
							
							// For debugging
							console.info("Computer discarded a " + card.name + ' of ' + card.suite);
							
							// If placing an 8, need to kill the deck!
							if ( card.name == '8' ) {
								$('discard-overflow').empty();
								
								/* ------------- Quick Note ---------------
						
								Had used: discard.empty()
								
								However, caused errors b/c discard was still 
								calling empty() when effect was applied.
								
								Doing: getLast().remove() works on removing 
								the child, and not modifying discard itself,
								so the effect executes without a billion errors
								trying to apply CSS changes every .0000001 seconds
								
								-------------------------------------------- */
								var finished = discard.getLast().remove();
								var eff = discard.effect('background-color', {duration:600}).start('#fa0').chain(function() {
										eff.start('#303030');
								});
														
								var was8_or_Joker = true;	// So know to take another turn
								
								// Let user know why computer is taking two turns											
								update_status(status_messages['computer_8_or_Joker']);
							}
							
							
							// If was dropping a Joker, set flag to take another turn
							else if ( card.name == 'Joker' ) var was8_or_Joker = true;
							
							
							// Check to see if we need to draw a card (and there is some available)
							if ( this.hand.length < 3 && deck.cardAvailable() ) {
								console.info("Computer is drawing a card");
								
								// Actually draw the cards and place in hand
								var drew = deck.drawCard();
								if ( drew ) {
									this.hand.push(drew);
								} 
								
								// If computer drew the last card, fix the deck to show that
								if ( !deck.cardAvailable() ) {
									$('deck').effect('opacity', {duration:700}).start(0.5);
									$('deck').setStyle('cursor', 'default');
								}
							}
							
							// If had dropped an 8 or Joker, computer gets to go again
							if ( was8_or_Joker ) this.takeTurn.delay(comp_delay, this);
						}
					}, this);
				}
			}
		}
		
		
		// Playing a bottom boot
		else if ( this.bottomBoots.length > 0 ) {
			console.warn("Computer playing a bottom boot");
			
			var topCard = discard.getLast();
			if ( topCard ) {
			
				/* --------------------------------------------------------
				
					Figure out what the lowest card, >= card on discard
					we have in our hand.  If nothing works, then draw later
					
				-----------------------------------------------------------*/
				var discard_weight = topCard.getProperty('rel').toInt();
			
				// If the top card is a Joker or a 2 (8 would already be gone), reset weight
				if ( discard_weight == 16 || discard_weight == 15 ) discard_weight = 2;
				
				// Just pick a random card to play (same as user plays)
				var card = this.bottomBoots.getRandom();
								
				// If we do have something to play
				if ( card.weight >= discard_weight ) {
					
					// Remove the card from the computer's hand
					this.bottomBoots.remove(card);
					
					// Now to move the previous card to the discard-overflow div, 
					// which is completely hidden
					var previous = discard.getLast();
					if ( previous )  {
						previous.removeEvents();	// Remove the DblClick event that was here
						previous.remove();
						previous.setStyles({
							'position' : 'relative',
							'left' : 0,
							'right' : 0,
							'top': 0,
							'bottom' : 0,
							'cursor': 'move'
						});
						previous.inject($('discard-overflow'));
					}
			
					
					// Style and inject a non-draggable card on discard
					var dom = card.domElement();
					var fx = dom.effect('opacity', {duration:1500}).set(0);
					
					// Setup so user can dblclick on the card to draw cards from discard into hand
					dom.addEvent('dblclick', place_cards_in_hand);
					
					// Actually insert the card
					dom.injectInside(discard);
					fx.start(1);
					
					// For debugging
					console.info("Computer discarded a " + card.name + ' of ' + card.suite);
					
					// If placing an 8, need to kill the deck!
					if ( card.name == '8' ) {
						$('discard-overflow').empty();
						
						/* ------------- Quick Note ---------------
				
						Had used: discard.empty()
						
						However, caused errors b/c discard was still 
						calling empty() when effect was applied.
						
						Doing: getLast().remove() works on removing 
						the child, and not modifying discard itself,
						so the effect executes without a billion errors
						trying to apply CSS changes every .0000001 seconds
						
						-------------------------------------------- */
						var finished = discard.getLast().remove();
						var eff = discard.effect('background-color', {duration:600}).start('#fa0').chain(function() {
								eff.start('#303030');
						});
												
						var was8_or_Joker = true;	// So know to take another turn
						
						// Let user know why computer is taking two turns											
						update_status(status_messages['computer_8_or_Joker']);
					}
					
					
					// If was dropping a Joker, set flag to take another turn
					else if ( card.name == 'Joker' ) var was8_or_Joker = true;
					
					if ( this.bottomBoots.length == 0 ) {
						console.log("Seems the computer won");
						
						var p = $('player');
						update_status(status_messages['computer_won']);
						var eff = p.effect('opacity', {duration:900}).start(0).chain(function() {
							p.setHTML('<a href="play.html"><img src="images/computer-won.png"/></a>');
							eff.start(1);
						});
						
						return;
					}
					
					// If had dropped an 8 or Joker, computer gets to go again
					if ( was8_or_Joker ) this.takeTurn.delay(comp_delay, this);
									
				} 
				
				
				// Have to pickup #discard sadly 
				else {
					console.warn("Didn't have anything to play");
					
					// Pickup the cards
					this.pickup_discarded_cards();
				}
			}
			
			// Was no top card, so play what I want
			else {
			
				var card = this.bottomBoots.getRandom();
				if ( card ) {
					
					// So won't use this again
					this.bottomBoots.remove(card);
					
					// Style and inject a non-draggable card on discard
					var dom = card.domElement();
					var fx = dom.effect('opacity', {duration:1500}).set(0);
					
					// Setup so user can dblclick on the card to draw cards from discard into hand
					dom.addEvent('dblclick', place_cards_in_hand);
					
					// Actually insert the card
					dom.injectInside(discard);
					fx.start(1);
					
					// For debugging
					console.info("Computer discarded a " + card.name + ' of ' + card.suite);
					
					// If placing an 8, need to kill the deck!
					if ( card.name == '8' ) {
						$('discard-overflow').empty();
						
						/* ------------- Quick Note ---------------
				
						Had used: discard.empty()
						
						However, caused errors b/c discard was still 
						calling empty() when effect was applied.
						
						Doing: getLast().remove() works on removing 
						the child, and not modifying discard itself,
						so the effect executes without a billion errors
						trying to apply CSS changes every .0000001 seconds
						
						-------------------------------------------- */
						var finished = discard.getLast().remove();
						var eff = discard.effect('background-color', {duration:600}).start('#fa0').chain(function() {
								eff.start('#303030');
						});
												
						var was8_or_Joker = true;	// So know to take another turn
						
						// Let user know why computer is taking two turns											
						update_status(status_messages['computer_8_or_Joker']);
					}
					
					// If was dropping a Joker, set flag to take another turn
					else if ( card.name == 'Joker' ) var was8_or_Joker = true;
					
					if ( this.bottomBoots.length == 0 ) {
						console.log("Seems the computer won");
						
						var p = $('player');
						update_status(status_messages['computer_won']);
						var eff = p.effect('opacity', {duration:900}).start(0).chain(function() {
							p.setHTML('<a href="play.html"><img src="images/computer-won.png"/></a>');
							eff.start(1);
						});
						
						return;
					}					
					
					// If had dropped an 8 or Joker, computer gets to go again
					if ( was8_or_Joker ) this.takeTurn.delay(comp_delay, this);
				}
				
				// Shouldn't be an else....should have ended game by now
			}
		}
		
		// Let user know how many cards the computer has left in hand
		$('computer_numCards').setText(this.hand.length);
		
		// So user can take a turn
		hasDiscardedThisTurn = false;

	},
	
	
	// Just easy function to pickup all the cards
	pickup_discarded_cards: function() {
	
		var discard = $('discard');
		
		console.log('Computer has to take cards from discard pile');
		
		// Let user know what happened
		update_status(status_messages['computer_drew']);
						
		// Check if there are any cards in the discard pile anyway
		if ( discard.getLast() ) {
		
			// Let user know what's going on
			update_status(status_messages['computer_drew']);
			
			// Take the card from #discard and place in our hand, remove from DOM
			this.hand.push(new Card(discard.getLast(), 0, true));
			discard.getLast().remove();

			// Foreach of the cards to pick up from the hidden overflown div
			var kids = $('discard-overflow').getChildren();
			if ( kids ) {
				kids.each(function(el) {
					el.removeEvents();
					el.remove();	// Remove from DOM
					this.hand.push(new Card(el, 0, true));	// Add to computers hand
				}, this);
			}
		}		
	}
});












/* ------------------------------------------------------------------------------------------

											Deck
											
	Simple deck class.  Has support for multiple players.  1<4 players gets 1 deck, 5<9 
	gets 2, etc.
	
	-drawCard() returns a card
	-dealHands() returns an array of players with the hands and bottomBoots already set
	-cardAvailable() true/false
	-init_DND_for_hand() sets up all the Drag-n-Drop stuff for draggin from deck to #hand
	.numCardsRemaining for internal use
	
--------------------------------------------------------------------------------------------- */


var Deck = new Class({
	
	
	// Intializes the Deck object
	initialize: function(numPlayers) {
		
		this.numPlayers = 2;	// Default - 1 player and the computer

		// If didn't specify number of players, using default deck size of 52 cards
		if ( numPlayers == null ) {
			console.warn("Didn't specify how many players when creating the deck.  Using default of 52 cards, 1 player");
		} else if ( numPlayers < 1 ) console.warn("Initialized deck with non-Natural # of players, using default deck of 52 cards, 1 player");
		else this.numPlayers = numPlayers;
		

		// One of each type of card, per a suite
		var suite = ['Ace', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'Jack', 'Queen', 'King', 'Joker'];
		
		// If < 4 people, this is 1, so don't add another deck of cards and play with 52
		// If > 4 < 9, this is 2, add one pack and play with 2, 104 cards, etc
		var deckMultipler = Math.ceil(this.numPlayers/4); 
		var i = 1;
		while ( i < deckMultipler ) {
			suite = suite.extend(suite);
		}
		
		this.numCardsRemaining = deckMultipler * 54;
	
		this.suite_names = ['Spades', 'Clubs', 'Hearts', 'Diamonds'];
		var suites = [suite, suite.copy(), suite.copy(), suite.copy()];
		
		// Create the hash object
		this.cards = suites.associate(this.suite_names);
	},
	
	
	
	// Will return a new Card, if there is one left in the deck.  Otherwise returns null
	drawCard: function() {
					
		// Grab a name from the deck
		var suite = this.suite_names.getRandom();
		if ( suite != null ) {
			if ( this.cards[suite].length > 0 ) {
					var name = this.cards[suite].getRandom();	
					this.cards[suite].remove(name);
					this.numCardsRemaining--;
					if ( $('deck-count') ) $('deck-count').setText(this.numCardsRemaining); // Update numCardsRemaining in UI
					return new Card(suite, name, false);
			} else {
				this.suite_names.remove(suite);	// So we won't try to use this suite again
				return this.drawCard();	// Recursively try to find again
			}
		} else {
			console.error("The deck is empty.  Cannot deal cards from an empty deck!");
			return null;
		}
	},
	
	
	// Simply returns an array of players with their hands already dealt
	dealHands: function() {
		var players = new Array();
		for ( var i = 0; i < this.numPlayers; i++ ) {
			var player = new Player("Player " + (i+1));
			
			// Put the cards the user can see
			for ( var j = 0; j < 6; j++ ) {
				var card = this.drawCard();
				if ( card ) player.hand.push(card);
				else {
					console.error("Setting up hand for a player, but deck returned a null card");
					return null;
				}
			}
						
			// Place the cards that the user can't see, the bottom boots (upside down cards)
			for ( j = 0; j < 3; j++ ) {
				card = this.drawCard();
				if ( card ) player.bottomBoots.push(card);
				else { 
					console.error("Deck returned null card for bottom boot");
					return null;
				}
			}
			players.push(player);
		}
		return players;
	},
	
	cardAvailable: function() {
		console.info('Have ' + this.numCardsRemaining + ' cards remaining in deck');
		if ( this.numCardsRemaining > 0 ) return true;
		else return false;
	},



	/* ---------------------------------------------------------------------

							Drag - n - Drop Support
									
	----------------------------------------------------------------------- */
	init_DND_support: function() {
			
		

		// SO I CAN REFERENCE THE LARGER OBJECT (deck) WITHIN OTHER GARBAGE BELOW
		var deck = this;

		// Just for convenience, this is a DOM Element
		var div_deck = $('deck');
		var hand = $('hand');
		var discard = $('discard');
		
		// Unregister all previous events from the deck 
		div_deck.removeEvents();


		/* ------------------------------------------------------------------

		Creates a copy of the overturned card for user to drag-n-drop over 
		the user's hand, when clicking on the deck

		--------------------------------------------------------------------*/
		div_deck.addEvent('mousedown', function(e) {

			e = new Event(e).stop();

			// Number of cards in UI hand
			var user_hand_length = hand.getChildren() ? hand.getChildren().length : 0;

			// If the deck isn't empty
			if ( deck.cardAvailable() ) {

				// If user actually needs to draw anyway
				if ( user_hand_length >= 3 ) {
					console.warn("User tried to draw from deck, but does not need to draw with: %d cards remaining in hand", user_hand_length);
					update_status(status_messages['do_not_draw']);
					return;
				}
				
				// Drag a clone, so original stays behind (blue card in background)
				var clone = $('insideDeck').clone()
					.setStyles(div_deck.getCoordinates()) // this returns an object with left/top/bottom/right, so its perfect
					.setStyles({'opacity': 0.7, 'position': 'absolute'})	
					.addEvent('emptydrop', function() {
						var snap = clone.effects({duration:400}).start(div_deck.getCoordinates()).chain(function(){
							clone.effect('opacity', {duration:400}).start(0).chain(function(){ 
								clone.remove();
							});
						});
					}).inject(document.body);


				var drag = clone.makeDraggable({
					droppables: hand
				}); // this returns the dragged element

				drag.start(e); // start the event manually
			}
			
			// If the deck was empty
			else {
				console.warn("The Deck is empty, write code to deal with it");
			}
		});



		// Add events for handling drag-n-drop to recepeint area
		hand.addEvents({

			/* ---------------------------------------------------------------

			Just draw a card from the deck, slowly fade in after all
			the other cards in the hand, then return hand background to normal
			
			To note: el is a draggable object, but it is just a div of an 
			overturned card.  Therefore I create my new card and such, but
			have to enable dragging of that as well.

			-----------------------------------------------------------------*/
			'drop': function(el, obj) {
				
				
				// 'drop' is called several times
				// validating drop...if it's not in the document, we've
				// already run through all this before, so don't do it again
				if ( !document.body.hasChild(el) ) return;
			
			
				// Weight of the card in #discard right now.  0 if not one
				var discard_weight = discard.getLast() ? discard.getLast().getProperty('rel').toInt() : 0;

			
				// Draw a card from the deck and create a draggable from it
				var card = deck.drawCard().domElement();
				
				
				// If that was the last card in the deck, fix the deck to show that
				if ( !deck.cardAvailable() ) {
					$('deck').setStyle('cursor', 'default');
					$('deck').effect('opacity', {duration:600}).start(0.5);
				}
						
										
				// Insert the draggable card into the #hand
				var effect = card.effect('opacity', {duration:800}).set(0);
				card.injectInside(hand);
				el.remove();
				effect.start(1);
				
				
				// Sort the hand
				sort_hand();
				
				
				// What we just inserted was a non-draggable card, now update to make draggable to discard pile
				init_DND_for_hand_to_discard();
				
				
				// Number of cards in UI hand
				var user_hand_length = hand.getChildren() ? hand.getChildren().length : 0;
				
				
				/* ------------------------------------------------------------------------------------
				
											Whether Computer Should Go Next?
											
					Complicated mess of a validation here.  If the user has already dropped one card, 
					and his hand has at least 3 cards, then normally that's all we'd have to check for.
					
					The second half checks to see if the last card dropped was an 8 (hench discard.getLast() 
					doesn't exist and that would be true || last card was a Joker and we're going again.
					
					This is here, for this complicated example:
						User plays an 8, killing the discard deck.  Then, draws another card to have 3.
						Then, user plays another card and possible another of the same suite (two 5's 
						for example).  Then the user draws 2 more cards from the deck, and on the second 
						drop, the computer finally takes it's turn
						
				------------------------------------------------------------------------------------------ */
				if ( hasDiscardedThisTurn && user_hand_length >= 3 && 
						( discard_weight != 16 && discard_weight != 0 ) ) {
					computer.takeTurn.delay(comp_delay, computer);
				} 
			},

			'over': function(el, obj) {
				el.effect('opacity').start(1);
			},

			'leave': function(el, obj) {
				el.effect('opacity').start(0.7);
			}
		});
		
		// Change the cursor to indicate draggable now
		$('deck').setStyle('cursor', 'move');
	}
});
		
		
		






// Just a way to print each card for each person
function hands() {
	
	// For each player in array, print out all cards
	players.each(function(player) {
		
		console.group(player.name);
		
		console.group("Hidden Boots");
		player.bottomBoots.each(function(card) {
			console.log(" - " + card.name + " of " + card.suite);
		}); console.groupEnd();
		
		console.group("Top Boots");
		player.topBoots.each(function(card) {
			console.log(" - " + card.name + " of " + card.suite);
		}); console.groupEnd();
				
		console.group("Cards");
		player.hand.each(function(card) {
			console.log(" - " + card.name + " of " + card.suite);
		}); console.groupEnd();
				
		console.groupEnd();
	});
}










/* -----------------------------------------------------------------------------------------------------

											Global vars
									
								The deck and an array of players
						
----------------------------------------------------------------------------------------------------- */
						
var deck = new Deck(2);				// Default size of 2, one player and the computer
var players = deck.dealHands();		// Setup the players and init the hands therin
var computer = players[1];			
var user = players[0];
var hasDiscardedThisTurn = false;	// Used to determine if user has went or not and validate drop 
var shift_down = false;
var comp_delay = 2000;








/* -----------------------------------------------------------------------------------------------------

											Main()
											
						Inits all the stuff for the player and the computer
						
						Waits for the player to pick his top boots
						-- then calls beginGamePlay() to start the game
						
------------------------------------------------------------------------------------------------------- */
function main() {

	// Obviously JS is enabled, so remove the warning that the user has it disabled and the site won't work
	$('js-warning').remove();

	// Hide the whole player area
	$('player').setOpacity(0);
	
	// Just for convenience to the UI hand
	var hand = $('hand');
	
	// Make the deck faded to start with
	$('deck').setOpacity(0.5);
	
	// Quickly sort the player's hand behind the scenes
	user.hand.sort(function(a,b){return a.weight - b.weight;});
	
	// Foreach card in the player's hand...
	user.hand.each(function(card) {
		card.domElement().injectInside(hand);
	});
		
	// Enable support for drag-n-drop from the #hand to a topBoot
	init_DND_for_hand_to_topboots();
	
	// Fade out the progress bar now that we are done loading the game and fade in the player's area
	$('loader').effect('opacity', {duration:1000, transition:Fx.Transitions.linear}).start(0).chain(function() {
		$('loader').remove();	// The loader was in the way of the cards, even though it was hidden
		$('player').effect('opacity', {duration:1000}).start(1);
	});		
	
	// Start the "slideshow" of tips in the sidebar, changing out the message every 15 seconds
	update_quickTips.periodical(15000);
}












/* --------------------------------------------------------------
				
						Starts the Game
						
	Called after user picks the three cards at the beginning.
	
	Starts the alternating between player turns.
	
---------------------------------------------------------------- */

function beginGamePlay() {

	// Set the inital status message on the left
	update_status(status_messages['begin_gameplay']);
		
	// Want to dim the topboots cause we can't use them for awhile
	$('topBoots').effect('opacity', {duration:800}).start(0.5);
	
	// Fade back in the deck
	$('deck').effect('opacity').start(1);
	
	// Have to make the computer pick it's topBoots as well
	var i = 0;
	var boots = $('computer_topBoots');
	while ( i < 3 ) {
		var card = computer.hand.getTopWeightedCard();
		if ( card ) {
			computer.topBoots.push(card);
			computer.hand.remove(card);
			var mini_card = new Element('div', {'class': 'mini-card'});
			mini_card.setText(card.symbol);
			mini_card.inject(boots);
			i++;
		}
	}
			
	// Currently cards in hand are set to drop over topBoots area.  
	// Need to reset that to drop on discard div
	init_DND_for_hand_to_discard();
}




















/* -----------------------------------------------------------------------------------------------------

								Drag n Drop Support from #hand to topboots
								
-------------------------------------------------------------------------------------------------------*/

function init_DND_for_hand_to_topboots() {

	var fx = [];	// Will hold all the effects that I use on the top boots slots
	var topBoots = $$('#topBoots .openSlot');
	$$('.card').each(function(item) {

		item.addEvent('mousedown', function(e) {
			e = new Event(e).stop();

			// Use a clone of the card in hand, so if user doesn't drop it somewhere, it disappears and there is 
			// still one available to use in the hand
			var clone = this.clone()
				.setStyles(this.getCoordinates()) // this returns an object with left/top/bottom/right, so its perfect
				.setStyles({'opacity': 0.7, 'position': 'absolute'})	
				.addEvent('emptydrop', function() {
					var snap = this.effects({duration:400}).start(item.getCoordinates()).chain(function(){
						clone.effect('opacity', {duration:400}).start(0).chain(function(){ 
							clone.remove();
						});
					});
				}).inject(document.body);

			// We receive the clone when dragging-n-dropping.  We want to be able to remove the original from the
			// hand if we sucsessfully drop the clone.  This provides access to that.
			clone.original = this;

			// Make the cloned element draggable, like the original was
			var drag = clone.makeDraggable({
				droppables: topBoots
			}); // this returns the dragged element
			drag.start(e); // start the event manually
		});
	});
	
	
	
	/* 
		Register drop zone to receive a drop 
		
	*/
	
	topBoots.each(function(drop, index) {
		fx[index] = drop.effect('opacity', {wait: false}); 
		
		var hand = $('hand');
		
		drop.addEvents({
			
			// Performing the actual drop
			'drop': function(el, obj) {
				
				
				// 'drop' is called several times
				// validating drop...if it's not in the document, we've
				// already run through all this before, so don't do it again
				if ( !document.body.hasChild(el) ) return;
				
				
				// If we can drop a card here (only 1 per slot)
				if ( this.getChildren().length > 0 ) {
					el.fireEvent('emptydrop');	// Cleanup the card, cause we're not going to drop it here
					return;
				}
				
				/*	Otherwise, we're dropping our first card here and we're fine.
				
					Remove the original from the hand, create a non-draggable clone to 
					place inside the drop area.  Remove the card we're actually dragging
				*/
				el.original.remove();
				var non_draggable = el.clone();
				el.remove();
				
				// Hide the one we're injecting and then center it, and fade it back in
				non_draggable.setOpacity(0);
				non_draggable.setStyles({		// Set styles so card will hide properly in #discard-overflow
					'position' : 'relative',
					'left' : 0,
					'right' : 0,
					'top': 0,
					'bottom' : 0
				});
				
				non_draggable.inject(this);
				
				// Fade the card suite back in
				non_draggable.effect('opacity', {duration:400}).start(1);
				
				// Check to see if this was our last card we needed to drop, continue gameplay
				if ( hand.getChildren().length == 3 ) beginGamePlay();
			},
		
			// Called when dragging a card over a drop area, validate the drop and provide visual cue to user
			'over': function(el, obj) {
			
				// If there is a card here already, pulsate to alert the user
				var el_effect = el.effect('opacity', {duration:200});
				if ( this.getChildren().length > 0 ) {
					el_effect.start(0.3).chain(function() {
						el_effect.start(0.7);
					}).chain(function() {
						el_effect.start(0.3);
					}).chain(function() {
						el_effect.start(0.7);
					});
				} else el_effect.start(1.0);
			},
			
			// When exiting the drop area
			'leave': function(el, obj) {
				el.effect('opacity').start(0.7);
			}
		});
	});
}









/* -----------------------------------------------------------------------------------------------------
	
							Enable drag-n-drop support from #hand to discard pile
							
------------------------------------------------------------------------------------------------------ */

function init_DND_for_hand_to_discard() {
	
	var discard = $('discard');	
	var hand = $('hand');
	
	// Remove events from #topBoots (dragging to #discard and we picked up #discard)
	$$('#topBoots .card').removeEvents();
	$$('#topBoots .openSlot').removeEvents();
	
	// Start adding our new events
	$$('#hand .card').each(function(item) {
		
		// Remove previous events, which was dragging to topBoots
		item.removeEvents();
		
		// Set to drop over discard area now
		item.addEvent('mousedown', function(e) {
			e = new Event(e).stop();
			
			// If user is holding down shift to indicate multiple drops coming up
			if ( e.shift ) shift_down = true;
			else shift_down = false;

			// Use a clone of the card in hand, so if user doesn't drop it somewhere, it disappears and there is 
			// still one available to use in the hand
			var clone = item.clone()
				.setStyles(this.getCoordinates()) // this returns an object with left/top/bottom/right, so its perfect
				.setStyles({'opacity': 0.7, 'position': 'absolute'})	
				.addEvent('emptydrop', function() {
					var snap = clone.effects({duration:400}).start(item.getCoordinates()).chain(function(){
						clone.effect('opacity', {duration:400}).start(0).chain(function(){ 
							if ( document.body.hasChild(clone) ) clone.remove();
						});
					});
				}).inject(document.body);

			// We receive the clone when dragging-n-dropping.  We want to be able to remove the original from the
			// hand if we sucsessfully drop the clone.  This provides access to that.
			clone.original = this;

			// Make the cloned element draggable, like the original was
			var drag = clone.makeDraggable({
				droppables: discard
			}); // this returns the dragged element

			drag.start(e); // start the event manually
		});
	});
	
	
	
	
	// Register the discard div for drop alerts 
	discard.removeEvents();	// Remove previous events - very important!
	
	discard.addEvents({
		
		// Performing the actual drop
		'drop': function(el, obj) {
		
			// 'drop' is called several times
			// validating drop...if it's not in the document, we've
			// already run through all this before, so don't do it again
			if ( !document.body.hasChild(el) ) return;
		
		
			// Weight of the card in #discard right now.  17 if not one (assumes 8 was last dropped, so 17 = '8'
			var discard_weight = discard.getLast() ? discard.getLast().getProperty('rel').toInt() : 17;

			
			// Get the weight of the card we're dropping
			var drop_weight = el.getProperty('rel').toInt();




			/* ----------------------------------------------------------
			
				Check to see if user is attempting to drop a card that
				is the same as a previous one he's dropped.
				
				If not (doesn't match weight and has dropped before),
				let user know and exit.  Invalid drop.
				
			------------------------------------------------------------ */
				
			if ( hasDiscardedThisTurn && discard_weight != drop_weight ) {
				console.warn("User attempting 2nd drop");
				update_status(status_messages['user_discard_twice']);
				el.fireEvent('emptydrop');
				return;
			}


			
			// If the top card is a wild (8, 2, Joker)
			// Even though 8 is already gone, going to assume it was there before
			if ( discard_weight == 16 || discard_weight == 15 || discard_weight == 17) {
				
				// Because essentially these start the deck over at a low number
				discard_weight = 2;
			}
			
			
			// Check to make sure the card will drop according to gameplay rules
			if ( drop_weight < discard_weight ) {
				console.warn("User attempted to drop card that wouldn't go on pile");
				update_status(status_messages['user_invalid_weight_drop']);
				el.fireEvent('emptydrop');	
				return;
			}
			
			
			
			
			
			
			
			// So we know user has discarded if attempts another drop
			hasDiscardedThisTurn = true;
			
			
			/*	
				Remove the original from the hand, create a non-draggable clone to 
				place inside the drop area.  Remove the card we're actually dragging
			*/			
			el.original.remove();
			var non_draggable = el.clone();
			el.remove();
			
			
			
			// Now to move the previous card to the discard-overflow div, 
			// which is completely hidden
			var previous = discard.getLast();
			if ( previous )  {
				previous.removeEvents();	// Remove DblClick event that was here
				previous.remove();			// Remove from DOM
				previous.setStyles({		// Set styles so card will hide properly in #discard-overflow
					'position' : 'relative',
					'left' : 0,
					'right' : 0,
					'top': 0,
					'bottom' : 0,
					'cursor': 'move'
				});
				previous.inject($('discard-overflow'));
			}
			
			
			
			// Hide the one we're injecting and then center it, and fade it back in
			non_draggable.setOpacity(0);
			non_draggable.setStyles(this.getCoordinates());
			non_draggable.inject(this);
			
			// Fade the card suite back in
			non_draggable.effect('opacity', {duration:400}).start(1);
			
			// Set the cursor of the discard pile back to non-draggable
			non_draggable.setStyle('cursor', 'default');
			
			
			// If card is a Joker...
			if ( drop_weight == 16 ) {
				console.log("User dropping a joker");
				
				// Let the user know that they need to go one more time
				update_status(status_messages['user_go_again']);
				
				// So user can go one more time (since only two players, reversing just does that)
				hasDiscardedThisTurn = false;
			}
			
			// If card is an 8....
			else if ( drop_weight == 17 ) {
				console.log("user dropping an 8");
				$('discard-overflow').empty();
				

				/* ------------- Quick Note ---------------
				
				Had used: discard.empty()
				
				However, caused errors b/c discard was still 
				calling empty() when effect was applied.
				
				Doing: getLast().remove() works on removing 
				the child, and not modifying discard itself,
				so the effect executes without a billion errors
				trying to apply CSS changes every .0000001 seconds
				
				-------------------------------------------- */
				var finished = discard.getLast().remove();
				var eff = discard.effect('background-color', {duration:600}).start('#fa0').chain(function() {
								eff.start('#303030');
				});
									
				hasDiscardedThisTurn = false;	// Going to discard again
				
				// Let the user know that he gets another turn
				update_status(status_messages['user_go_again']);
			}
			
			// Enable dragging from the deck again
			if ( deck.cardAvailable() ) deck.init_DND_support();
					
			// Check to see if our hand is empty and whether we should start using our top boots now
			if ( hand.getChildren().length < 1 && !deck.cardAvailable() ) {
				
				if ( $$('#topBoots .card').length > 0 ) init_DND_for_topBoots_to_discard();
				else if ( user.bottomBoots.length > 0 ) init_DND_for_bottomBoots_to_discard();
				else {
					var p = $('player');
					update_status(status_messages['user_won']);
					var eff = p.effect('opacity', {duration:900}).start(0).chain(function() {
					p.setHTML('<a href="play.html"><img src="images/Winner.png"/></a>');
					eff.start(1);
				});
					
					console.log("User won");
					return;
				}
			}
			
			// How many cards the user has
			var hand_count = hand.getFirst() ? hand.getChildren().length : 0;
			
			// Let computer go if we didn't just drop a wild or hold down the shift key
			if ( !shift_down && drop_weight != 16 && drop_weight != 17 && 
						(( hand_count < 3 && !deck.cardAvailable() ) || hand_count >= 3 ) ) computer.takeTurn.delay(comp_delay, computer);
			
		},
	
		// Called when dragging a card over a drop area, validate the drop and provide visual cue to user
		'over': function(el, obj) {
			el.effect('opacity', {duration:200}).start(1);
		},
		
		// When exiting the drop area
		'leave': function(el, obj) {
			el.effect('opacity').start(0.7);
		}
	});
}



function init_DND_for_topBoots_to_discard() {
	
	console.info("Setting DND for topBoots to #discard");
	
	var discard = $('discard');	
	var hand = $('hand');
	
	// Fade the boots back in
	$('topBoots').effect('opacity', {duration:900}).start(1);
	
	
	// For each boot...
	$$('#topBoots .card').each(function(item) {
		
		item.setStyle('cursor', 'move');
			
		// Set to drop over discard area now
		item.addEvent('mousedown', function(e) {
			e = new Event(e).stop();
			
			// If user is holding down shift to indicate multiple drops coming up
			if ( e.shift ) shift_down = true;
			else shift_down = false;

			// Use a clone of the card in hand, so if user doesn't drop it somewhere, it disappears and there is 
			// still one available to use in the hand
			var clone = item.clone()
				.setStyles(this.getCoordinates()) // this returns an object with left/top/bottom/right, so its perfect
				.setStyles({'opacity': 0.7, 'position': 'absolute'})	
				.addEvent('emptydrop', function() {
					var snap = clone.effects({duration:400}).start(item.getCoordinates()).chain(function(){
						clone.effect('opacity', {duration:400}).start(0).chain(function(){ 
							if ( document.body.hasChild(clone) ) clone.remove();
						});
					});
				}).inject(document.body);

			// We receive the clone when dragging-n-dropping.  We want to be able to remove the original from the
			// hand if we sucsessfully drop the clone.  This provides access to that.
			clone.original = this;

			// Make the cloned element draggable, like the original was
			var drag = clone.makeDraggable({
				droppables: discard
			}); // this returns the dragged element

			drag.start(e); // start the event manually
		});
	});
	
	
	
	
	// Remove previous events
	discard.removeEvents();
	
	// Register for new drag/drop events
	discard.addEvents({
		
		// Performing the actual drop
		'drop': function(el, obj) {
		
			// 'drop' is called several times
			// validating drop...if it's not in the document, we've
			// already run through all this before, so don't do it again
			if ( !document.body.hasChild(el) ) return;
		
		
			// Weight of the card in #discard right now.  17 if not one (assumes 8 was last dropped, so 17 = '8'
			var discard_weight = discard.getLast() ? discard.getLast().getProperty('rel').toInt() : 17;

			
			// Get the weight of the card we're dropping
			var drop_weight = el.getProperty('rel').toInt();




			/* ----------------------------------------------------------
			
				Check to see if user is attempting to drop a card that
				is the same as a previous one he's dropped.
				
				If not (doesn't match weight and has dropped before),
				let user know and exit.  Invalid drop.
				
			------------------------------------------------------------ */
				
			if ( hasDiscardedThisTurn && discard_weight != drop_weight ) {
				console.warn("User attempting 2nd drop");
				update_status(status_messages['user_discard_twice']);
				el.fireEvent('emptydrop');
				return;
			}


			
			// If the top card is a wild (8, 2, Joker)
			// Even though 8 is already gone, going to assume it was there before
			if ( discard_weight == 16 || discard_weight == 15 || discard_weight == 17) {
				
				// Because essentially these start the deck over at a low number
				discard_weight = 2;
			}
			
			
			// Check to make sure the card will drop according to gameplay rules
			if ( drop_weight < discard_weight ) {
				console.warn("User attempted to drop card that wouldn't go on pile");
				update_status(status_messages['user_invalid_weight_drop']);
				el.fireEvent('emptydrop');	
				return;
			}
			
	
			
			// So we know user has discarded if attempts another drop
			hasDiscardedThisTurn = true;
			
			
			/*	
				Remove the original from the hand, create a non-draggable clone to 
				place inside the drop area.  Remove the card we're actually dragging
			*/			
			el.original.remove();
			var non_draggable = el.clone();
			el.remove();
			
			
			
			// Now to move the previous card to the discard-overflow div, 
			// which is completely hidden
			var previous = discard.getLast();
			if ( previous )  {
				previous.removeEvents();	// Remove DblClick event that was here
				previous.remove();			// Remove from DOM
				previous.setStyles({		// Set styles so card will hide properly in #discard-overflow
					'position' : 'relative',
					'left' : 0,
					'right' : 0,
					'top': 0,
					'bottom' : 0,
					'cursor': 'move'
				});
				previous.inject($('discard-overflow'));
			}
			
			
			// Hide the one we're injecting and then center it, and fade it back in
			non_draggable.setOpacity(0);
			non_draggable.setStyles(this.getCoordinates());
			non_draggable.inject(this);
			
			// Fade the card suite back in
			non_draggable.effect('opacity', {duration:400}).start(1);
			
			// Set the cursor of the discard pile back to non-draggable
			non_draggable.setStyle('cursor', 'default');
			
			
			// If card is a Joker...
			if ( drop_weight == 16 ) {
				console.log("User dropping a joker");
				
				// Let the user know that they need to go one more time
				update_status(status_messages['user_go_again']);
				
				// So user can go one more time (since only two players, reversing just does that)
				hasDiscardedThisTurn = false;
			}
			
			// If card is an 8....
			else if ( drop_weight == 17 ) {
				console.log("user dropping an 8");
				$('discard-overflow').empty();
				

				/* ------------- Quick Note ---------------
				
				Had used: discard.empty()
				
				However, caused errors b/c discard was still 
				calling empty() when effect was applied.
				
				Doing: getLast().remove() works on removing 
				the child, and not modifying discard itself,
				so the effect executes without a billion errors
				trying to apply CSS changes every .0000001 seconds
				
				-------------------------------------------- */
				var finished = discard.getLast().remove();
				var eff = discard.effect('background-color', {duration:600}).start('#fa0').chain(function() {
								eff.start('#303030');
				});
									
				hasDiscardedThisTurn = false;	// Going to discard again
				
				// Let the user know that he gets another turn
				update_status(status_messages['user_go_again']);
			}
			
			// Enable dragging from the deck again
			if ( deck.cardAvailable() ) deck.init_DND_support();
			
			if ( !shift_down && drop_weight != 16 && drop_weight != 17  ) {
				computer.takeTurn.delay(comp_delay, computer);
			}			
			
			// See if we need to enable the user's bottom boots now
			if ( $$('#topBoots .card').length < 1 ) init_DND_for_bottomBoots_to_discard();
			
		},
	
		// Called when dragging a card over a drop area, validate the drop and provide visual cue to user
		'over': function(el, obj) {
			el.effect('opacity', {duration:200}).start(1);
		},
		
		// When exiting the drop area
		'leave': function(el, obj) {
			el.effect('opacity').start(0.7);
		}
	});
}





function init_DND_for_bottomBoots_to_discard() {

	console.info("Setting DND for bottomBoots to #discard");
	
	var discard = $('discard');	
	var hand = $('hand');
	
	// Fade out the topboots again
	$('topBoots').effect('opacity').start(1);
	
	// For each boot...
	$$('#topBoots .openSlot').each(function(item) {
		
		item.setStyle('cursor', 'move');
			
		// Set to drop over discard area now
		item.addEvent('mousedown', function(e) {
			e = new Event(e).stop();
			
			// Use a clone of the card in hand, so if user doesn't drop it somewhere, it disappears and there is 
			// still one available to use in the hand
			var clone = item.clone()
				.setStyles(this.getCoordinates()) // this returns an object with left/top/bottom/right, so its perfect
				.setStyles({'opacity': 0.7, 'position': 'absolute', 'margin-left':0})	
				.addEvent('emptydrop', function() {
					var snap = clone.effects({duration:400}).start(item.getCoordinates()).chain(function(){
						clone.effect('opacity', {duration:400}).start(0).chain(function(){ 
							if ( document.body.hasChild(clone) ) clone.remove();
						});
					});
				}).inject(document.body);

			// We receive the clone when dragging-n-dropping.  We want to be able to remove the original from the
			// hand if we sucsessfully drop the clone.  This provides access to that.
			clone.original = this;

			// Make the cloned element draggable, like the original was
			var drag = clone.makeDraggable({
				droppables: discard
			}); // this returns the dragged element

			drag.start(e); // start the event manually
		});
	});
	
	
	
	
	// Remove previous events
	discard.removeEvents();
	
	// Register for new drag/drop events
	discard.addEvents({
		
		// Performing the actual drop
		'drop': function(el, obj) {
		
			// 'drop' is called several times
			// validating drop...if it's not in the document, we've
			// already run through all this before, so don't do it again
			if ( !document.body.hasChild(el) ) return;
		
		
			// Weight of the card in #discard right now.  17 if not one (assumes 8 was last dropped, so 17 = '8'
			var discard_weight = discard.getLast() ? discard.getLast().getProperty('rel').toInt() : 17;

			// Get the card we're actually going to use (Random from user)
			var card = user.bottomBoots.getRandom();
			user.bottomBoots.remove(card);

			// Get the weight of the card we're dropping
			var drop_weight = card.weight;



			/* ----------------------------------------------------------
			
				Check to see if user is attempting to drop a card that
				is the same as a previous one he's dropped.
				
				If not (doesn't match weight and has dropped before),
				let user know and exit.  Invalid drop.
				
			------------------------------------------------------------ */
				
			if ( hasDiscardedThisTurn && discard_weight != drop_weight ) {
				console.warn("User attempting 2nd drop");
				update_status(status_messages['user_discard_twice']);
				el.fireEvent('emptydrop');
				return;
			}


			
			// If the top card is a wild (8, 2, Joker)
			// Even though 8 is already gone, going to assume it was there before
			if ( discard_weight == 16 || discard_weight == 15 || discard_weight == 17) {
				
				// Because essentially these start the deck over at a low number
				discard_weight = 2;
			}
			
			
			// Check to make sure the card will drop according to gameplay rules
			if ( drop_weight < discard_weight ) {
				console.warn("User dropping a bottomBoot that wouldn't work: %s", card.name);
				update_status(status_messages['invalid_bottom_boot']);
				
				(function() {
					el.original.remove();
					el.remove();
							
								
					// The "card" we're dragging from #bottomBoots, now put in #hand
					card.domElement().inject(hand);
									
					place_cards_in_hand();
				}).delay(1000);
				return;
			}
			
			
			// So we know user has discarded if attempts another drop
			hasDiscardedThisTurn = true;
			
			
			/*	
				Remove the original from the hand, create a non-draggable clone to 
				place inside the drop area.  Remove the card we're actually dragging
			*/			
			el.original.remove();
			el.remove();
			var non_draggable = card.domElement();
			
			
			
			// Now to move the previous card to the discard-overflow div, 
			// which is completely hidden
			var previous = discard.getLast();
			if ( previous )  {
				previous.removeEvents();	// Remove DblClick event that was here
				previous.remove();			// Remove from DOM
				previous.setStyles({		// Set styles so card will hide properly in #discard-overflow
					'position' : 'relative',
					'left' : 0,
					'right' : 0,
					'top': 0,
					'bottom' : 0,
					'cursor': 'move'
				});
				previous.inject($('discard-overflow'));
			}
			
			
			
			// Hide the one we're injecting and then center it, and fade it back in
			non_draggable.setOpacity(0);
			non_draggable.setStyle('cursor','default');
			non_draggable.inject(this);
			
			// Fade the card suite back in
			non_draggable.effect('opacity', {duration:400}).start(1);
			
			// Set the cursor of the discard pile back to non-draggable
			non_draggable.setStyle('cursor', 'default');
			
			
			// If card is a Joker...
			if ( drop_weight == 16 ) {
				console.log("User dropping a joker");
				
				// Let the user know that they need to go one more time
				update_status(status_messages['user_go_again']);
				
				// So user can go one more time (since only two players, reversing just does that)
				hasDiscardedThisTurn = false;
			}
			
			// If card is an 8....
			else if ( drop_weight == 17 ) {
				console.log("user dropping an 8");
				$('discard-overflow').empty();
				

				/* ------------- Quick Note ---------------
				
				Had used: discard.empty()
				
				However, caused errors b/c discard was still 
				calling empty() when effect was applied.
				
				Doing: getLast().remove() works on removing 
				the child, and not modifying discard itself,
				so the effect executes without a billion errors
				trying to apply CSS changes every .0000001 seconds
				
				-------------------------------------------- */
				var finished = discard.getLast().remove();
				var eff = discard.effect('background-color', {duration:600}).start('#fa0').chain(function() {
								eff.start('#303030');
				});
									
				hasDiscardedThisTurn = false;	// Going to discard again
				
				// Let the user know that he gets another turn
				update_status(status_messages['user_go_again']);
			}
			
			// Enable dragging from the deck again
			if ( deck.cardAvailable() ) deck.init_DND_support();
			
			// If user is dragging the last bottom boot, then we won!
			if ( user.bottomBoots.length < 1 ) {
				var p = $('player');
				update_status(status_messages['user_won']);
				var eff = p.effect('opacity', {duration:900}).start(0).chain(function() {
					p.setHTML('<a href="play.html"><img src="images/Winner.png"/></a>');
					eff.start(1);
				});
				return;
			}
			
			// If it's the computer's turn
			if ( !shift_down && drop_weight != 16 && drop_weight != 17  ) {
				computer.takeTurn.delay(comp_delay, computer);
			}
			
			
		},
	
		// Called when dragging a card over a drop area, validate the drop and provide visual cue to user
		'over': function(el, obj) {
			el.effect('opacity', {duration:200}).start(1);
		},
		
		// When exiting the drop area
		'leave': function(el, obj) {
			el.effect('opacity').start(0.7);
		}
	});
}







// Called when user double clicks on the topmost card in the discard pile (only one in #discard)
function place_cards_in_hand() {

	console.info("User is drawing cards from discard");
	
	var discard = $('discard');
	var hand = $('hand');
	
	// Grab the one card we clicked on and place in hand
	var card = discard.getLast().remove();
	card.removeEvents();	// Remove the dblclick event
	card.setStyles({		// Have to set the styles on this one cause it hasn't been moved to #discard-overflow yet
			'position' : 'relative',
			'left' : 0,
			'right' : 0,
			'top': 0,
			'bottom' : 0,
			'cursor': 'move'
	});
	card.inject(hand);

	
	// Now place all the other cards in #discard-overflow in the #hand
	var kids = $('discard-overflow').getChildren();
	if ( kids ) hand.adopt(kids);
	
	// Fade out the topboots again
	$('topBoots').effect('opacity').start(0.5);
	$$('#topBoots .openSlot').each(function(el) {
		el.setStyle('cursor', 'default');
	});
	
	
	// Sort the hand for the user
	sort_hand();
	
	// Have now added a ton of cards to #hand, have to enable them as draggables
	init_DND_for_hand_to_discard();
	
	// Give the computer it's turn, basically - Don't get your turn when you pickup
	hasDiscardedThisTurn = true; // So user can't drop a card in the 3 second window
	computer.takeTurn.delay(comp_delay, computer);
}





// Reorders players hand by weight
function sort_hand() {

	var hand = $('hand');
	
	var cards = hand.getChildren();
	
	cards.sort(function(a,b) {
		return a.getProperty('rel') - b.getProperty('rel');
	});
	hand.adopt(cards);
}












/* ---------------------------------------------------------------------------------------------------------------

												Status Messages
												
			I want to keep the user informed of what is going on and provide quick links to find out more 
			information about gameplay and rules.  Therefore, the sidebar on the left has a few div's 
			that can quickly be updated to reflect events during gameplay.
			
			The array status_messages[] simply contains the strings, as HTML, that will be injected and updated
			in the divs, and the function update_status() takes one of those strings and actually does the 
			updating.
			
---------------------------------------------------------------------------------------------------------------- */

var status_messages = new Array();
status_messages['computer_8_or_Joker'] = "<h3>Important Messages</h3>" + 
									"<p>Your opponent just went twice in a row!  Why&apos;s that you ask?</p>" +
									"<p>Remember that rule about using wild cards?  Turns out if you drop an 8 or Joker you " + 
									"get to go again.  So, mystery solved.  Remember that next time you lay one down!" +
									"</p><p>Read the rules and objectives <a href=\"help\" target=\"_blank\">here</a></p>";
											
status_messages['begin_gameplay'] = "<h3>Important Messages</h3>" + 
										"<p>You are now ready to begin!</p><p>You must place a card from your hand" +
										" on the discard pile.  The card must be be of equal or greater value, or be a wild." + 
										"  Then draw one card from the deck to keep a minimum of three cards in your hand at all times." + 
										"</p><p>Read the rules and objectives <a href=\"help\" target=\"_blank\">here</a></p>";
	

status_messages['user_discard_twice'] = "<h3>Important Messages</h3>" + 
										"<p>Oops!  You have already discarded for your turn.</p>" + 
										"<p>However, you can discard multiple cards of the same value on one turn." +
										"If you have more cards that are the same as the one you just discarded, go for it." +
										"</p><p>Read the rules and objectives <a href=\"help\" target=\"_blank\">here</a></p>";


status_messages['user_invalid_weight_drop'] = "<h3>Important Messages</h3>" + 
												"<p>Remember, you can only discard a card if it&apos;s of equal or greater value." + 
												"If you have a wild, you can play that as well, even on an Ace.  If you do not have " +
												"any cards to play, double-click on the top card and all the discarded cards will be " +
												"added to your hand.  Sorry!" + 
												"</p><p>Read the rules and objectives <a href=\"help\" target=\"_blank\">here</a></p>";
															
												
status_messages['user_go_again'] = "<h3>Important Messages</h3>" + 
									"<p>You&apos;ve played either an 8 or a Joker, way to go!</p>" + 
									"<p>Remember that these cards give you another turn.  Go ahead and " +
									"draw another card from the deck, then play again.  Finally, draw again " +
									"if you need to (less than 3 cards, remember?).  Then the computer will go." +
									"</p><p>Read the rules and objectives <a href=\"help\" target=\"_blank\">here</a></p>";
									
status_messages['computer_drew'] = "<h3>Important Messages</h3>" +
									"<p>The computer was not able to play a card and had to pickup</p>" +
									"<p>It&apos;s now your turn.  You may play any card, but now would be a good" +
									" time to get rid of any low cards you have" + 
									"</p><p>Read the rules and objectives <a href=\"help\" target=\"_blank\">here</a></p>";
									
status_messages['invalid_bottom_boot'] = "<h3>Important Messages</h3>" +
										"<p>Bad news.  That bottom boot didn&apos;t meet the cut!</p>"+
										"<p>You&apos;ve picked up the discard deck and have to play the cards from " + 
										"hand now, before you can return and play from the rest of your bottom boots" +
										" to finish the game.  Sucks to be you!" + 
										"</p><p>Read the rules and objectives <a href=\"help\" target=\"_blank\">here</a></p>";


				
status_messages['user_won'] = "<h3>Game Over</h3>" + 
							"<p>Awesome game, congratulations!</p>" + 
							"<p>If you don't mind, please take the time to submit suggestions or bug reports" + 
							"to improve the game.</p>" +
							"<p><a href=\"play.html\">Play Again</a></p>";
							
status_messages['computer_won'] = "<h3>Game Over</h3>" + 
							"<p>You played an awesome game, but better luck next time.</p>" + 
							"<p>If you don't mind, please take the time to submit suggestions or bug reports" + 
							"to improve the game.</p>" +
							"<p><a href=\"play.html\">Play Again</a></p>";
							
							
status_messages['do_not_draw'] = "<h3>Important Messages</h3>" + 
							"<p>You&apos;ve attemped to draw a card, but you don&apos;t need to!</p>" + 
							"<p>You have more than 3 cards in your hand, why would you want more?" + 
							"</p><p>Read the rules and objectives <a href=\"help\" target=\"_blank\">here</a></p>";





 

// Simply takes HTML to insert into the box in the sidebar and updates is nicely
function update_status(message) {
	
	var status_console = $('status');
	var stat_fx = status_console.effect('opacity', {duration:600}).start(0).chain(function() {
		status_console.empty().setHTML(message);
		stat_fx.start(1);
	});
}













/* ---------------------------------------------------------------------------------------------------------------

												Quick Tips
												
			Just an easy way to provides hints about the game and strategy, since most users won't read
			the rules and objectives, or won't retain much of it anyway.  Just set on a timer to fade-in/out
			every 15 seconds, nothing special.
			
---------------------------------------------------------------------------------------------------------------- */


var quick_tips = new Array();
quick_tips.push("<h3>Quick Tips</h3>" +
				"<p>Be sure to keep an eye out over here for gameplay tips or message alerts!" + 
				"</p><p>Read the rules and objectives <a href=\"help\" target=\"_blank\">here</a></p>");
				
quick_tips.push("<h3>Quick Tips</h3>" +
				"<p>Save your better cards, a 2, 8, Joker, or any other high card, " + 
				"for the end of the game where it counts!  Place those on the overturned cards at the bottom of the screen" + 
				"</p><p>Read the rules and objectives <a href=\"help\" target=\"_blank\">here</a></p>");
				
quick_tips.push("<h3>Quick Tips</h3>" +
				"<p>Play more than one card in a turn!</p>" + 
				"<p>If you have more than one card of the same type (not suite), " +
				"keep discarding.  Wait to draw new cards until you have finished placing the cards, or " +
				"the computer might get it&apos;s turn!" + 
				"</p><p>Read the rules and objectives <a href=\"help\" target=\"_blank\">here</a></p>");
				
quick_tips.push("<h3>Quick Tips</h3>" + 
				"<p>You don&apos;t have to be nice to the computer!</p>" + 
				"<p>Play a high card and the computer might have to pick up all the cards that have been discarded." +
				"  Careful though, if it does have a card to play, you might get stuck with all the cards!" + 
				"</p><p>Read the rules and objectives <a href=\"help\" target=\"_blank\">here</a></p>");
				
quick_tips.push("<h3>Quick Tips</h3>" + 
				"<p>When you find that you cannot play a card on your turn, " +
				"you are forced to take all cards from the discard pile.</p>" +
				"<p>To do this, simply double-click the discard pile and the " +
				"cards will be added to your hand!" +
				"</p><p>Read the rules and objectives <a href=\"help\" target=\"_blank\">here</a></p>");







function update_quickTips() {
	
	var tips = $('tips');
	var length = quick_tips.length;
	if ( !this.index ) this.index = 0;
	this.index = this.index >= quick_tips.length - 1 ? 0 : this.index + 1;
	var i = this.index;
	tips.effect('opacity', {duration:600}).start(0).chain(function() {
		tips.empty().setHTML(quick_tips[i]);
		this.start(1);
	});
}
