/*********************
	jQuery Carousel
	Written by Xigen
	Version 1.1 (18 May 2010)
**********************
	This file requires:
		jQuery (1.2.6 or later)
		corner.js
		DD_Roundies.js
*********************/

$(function() {

	// A configuration object containing classes and IDs used in this script as well as some of the variables for the AJAX call
	var config = {
		// General
			plateClass:		'plate',			// The class assigned to each panel (or "plate")
			plateIDroot:	'plate',			// The root of the ID for each plate (plates have the ID "plate1", "plate2" etc)
			plateImgClass:	'plateImg',			// The class assigned to the main image of the plate
			contentsClass:	'contents',			// The <div> tag containing the Call to Action and the white box with the tabs
			carousel:		'carousel',			// The ID of the carousel, constructed using this script
			carouselHolder:	'carousel_holder',	// The ID of the <div> tag that will hold the constructed carousel
			headingClass:	'heading',			// The class of the darker bar at the top of each plate
			heroClass:		'hero',				// The class of <div> tag containing the hero text and sub heading
			ctaClass:		'cta',				// The class for the Call to Action link
			noImgClass:		'missingImage',		// A class added to any plate where the image cannot be found
		// Arrows
			next:			'next',				// The ID for the right-hand arrow
			prev:			'prev',				// The ID for the left-hand arrow
			arrowClass:		'arrow',			// The class assigned to both arrows
		// White boxes
			whiteBoxClass:	'whiteBox',			// The white boxes at the bottom of each plate
			tabClass:		'tab',				// The class assigned to each tab
			activeTabClass:	'activetab',		// The class for the active tab
		// AJAX
			//ajaxURL:		'/jsps/home/carouselData.jsp',		// The location of the XML file
			ajaxURL:		'../../../resource/indonesia/homepage_flash/homepage_carousel/xml/panelData.xml',
			ajaxType:		'GET',				// The nature of the AJAX call
		// Rounded corners
			maxTime:		60000,				// The maximum number of milliseconds before the script stops trying to round the corners
			timeIncriment:	100					// The number of milliseconds between image status checks
		},
	// CSS rules dictating the size and location of each plate
		rules = [
			{height: 367, width: 571, top: 0, left: 185},
			{height: 311, width: 486, top: 20, left: 380},
			{height: 268, width: 418, top: 40, left: 518},
			{height: 268, width: 418, top: 40, left: 5},
			{height: 311, width: 486, top: 20, left: 75}
		],
	// The z-indices for each plate, in an array to prevent a rear plate jumping on top of a more forward one
		zindices = [
			[100, 10, 2, 1, 10],
			[100, 10, 1, 2, 10]
		],
	// An array of the classes, to match the rules
		classes = ['front', 'midright', 'rearright', 'rearleft', 'midleft'],
	// Declare the animation to be possible
		animatable = true,
	// Keep a track of which plate is at the front (very roughly - do not rely on this)
		$front = 0,
	// A boolean that will render "true" in IE8 and higher and "false" in everything else
		ie8 = ($.browser.msie && $.browser.version > 7)
	;
	
	
	/***********
		A function to handle adding the corners to the plate images
	***********/
	function addImageCorners() {
	
		// Add corners differently in IE and other browsers
		// The functions "addIECorners" and "addCorners" are in corner.js
		if ($.browser.msie) {
			addIECorners(rules[0]);
			for (var i = 0, il = rules.length; i < il; i++) {setVMLrules((i + 1), i);}
		} else {
			addCorners(rules[0]);
		}
	}

	/***********
		A function that handles adding corners to non-images
	***********/
	function applyCorners() {

		// Headings
		DD_roundies.addRule('.' + classes[0] + ' .' + config.headingClass, '9px 9px 0px 0px', true);
		DD_roundies.addRule('.' + classes[1] + ' .' + config.headingClass + ', .' + classes[4] + ' .' + config.headingClass, '7px 7px 0px 0px', true);
		DD_roundies.addRule('.' + classes[2] + ' .' + config.headingClass + ', .' + classes[3] + ' .' + config.headingClass, '6px 6px 0px 0px', true);

		// White Boxes
		DD_roundies.addRule('.' + config.whiteBoxClass, '0px 0px 8px 8px', true);

		// Tabs
		DD_roundies.addRule('.' + config.tabClass, '4px 2px 0px 0px', true);

		// Call to Actions
		DD_roundies.addRule('.' + config.ctaClass, '3px 3px 3px 3px', true);
	}

	/***********
		The function fired when the animation of the plates changing has been completed
	************
		Takes:
			frontPlate (integer) the plate number of the front-most plate
			generated (boolean) true if the carousel is already generated
	***********/
	function callback(frontPlate, generated) {
		
		// Set a default value for generated,if it is not already passed
		if (typeof(generated) === "undefined") {generated = false;}

		// Fade in the carousel if it's hidden
		if ($('#' + config.carousel).is(':hidden')) {cascade();}
		
		// Create a count of the number of times this has been called if a count does not exist. If it does, increase it by 1
		if (typeof(arguments.callee.count) == "undefined") {
			arguments.callee.count = 1;
		} else {
			arguments.callee.count++;
		}

		// If the count is the same as the rules length, we may be ready
		if (arguments.callee.count == rules.length || generated) {
			
			// Re-set the count and allow more animation to take place
			arguments.callee.count = 0;
			animatable = true;
			
			// Check that the function which does the animation doesn't have any more animation lined up
			if (!setRules.recursing) {
								
				// If the tabs have already been generated, fade them in. If they have not, generate them
				if ($($('.' + config.plateClass)[frontPlate]).find('.' + config.contentsClass).length > 0) {
					$($('.' + config.plateClass)[frontPlate]).find('.' + config.contentsClass).fadeIn('fast');
					
					if ($($('.' + config.plateClass)[frontPlate]).find('.' + config.heroClass).length > 0) {
						$($('.' + config.plateClass)[frontPlate]).find('.' + config.heroClass).fadeIn('fast');
					}
					
					// Try one more time to get the missing images...
					//addImageCorners();
					
				} else {
					getXML(frontPlate);
				}
				
			}
		}
	}

	/***********
		A function to animate the carousel when it first generates
		This is a separate function in-case a more interesting animation is required
	***********/
	function cascade() {
		
		setTimeout(function() {$('#' + config.carousel).fadeIn();}, 250);
	}

	/***********
		A function to work out the rules needed for each plate
	************
		Takes:
			settings (object)
				previous (boolean) true if the carousel is moving to the left, false otherwise
				furtherInteractions (integer) the number of times this function will need to fire again, incase a plate was clicked
	***********/
	function changeRules(settings) {
		
		var defaults = {
			previous: true,
			furtherInterations: 0
		};
		$.extend(defaults, settings);
		
		// Only fire the functionality if we can animate...
		if (animatable) {
			//... and prevent any other animation taking place until the animation is complete
			animatable = false;
			
			// Manipulate $front depending on which direction the carousel should go
			if (defaults.previous) {
				$front++; if ($front >= rules.length) {$front = 0;}
			} else {
				$front--; if ($front < 0) {$front = rules.length - 1;}
			}
			
			// Send our rough front counter to our animation function
			setRules({front: $front, position: (defaults.previous ? 0 : 1), recursing: (defaults.furtherIterations > 0)});
			
			// If we're adding this to the queue, send the settings over to our closure
			if (defaults.furtherIterations > 0) {handleClosure(defaults);}
		}
	}

	/***********
		A function to change the tabs in the white boxes
	************
		Takes:
			tag (object) a reference to the tab clicked
	***********/
	function changeTab(tab) {
		
		// Prevent the function firing if a tab has not been sent
		if (!tab) {return;}
		
		// Add the active tab class to the active tab and remove that class from all other tabs in this plate
		$(tab).addClass(config.activeTabClass).siblings().removeClass(config.activeTabClass);
		
		// Hide the other white boxes and show the one related to the tab clicked
		$(tab).parents('.' + config.contentsClass).find('.' + config.whiteBoxClass).hide();
		$(tab).parents('.' + config.contentsClass).find('.' + config.whiteBoxClass + '[rel=' + $(tab).text().toLowerCase() + ']').show();
	}

	/***********
		Check the images and fire an event when they're complete or they have timed out
	************
		Takes:
			img (object Image)
				the image to check
			timeSpent (integer) [optional, defaults to 0]
				the amount of time that has been spent on this function
	***********/
	function checkImg(img, timeSpent) {
		if (typeof(timeSpent) === "undefined") {timeSpent = 0;}
		
		if (IsImageOk(img)) {
			$(document).trigger('imageReady', [img, true]);
		} else {
			timeSpent += config.timeIncriment;
			if (timeSpent > config.maxTime) {
				$(document).trigger('imageReady', [img, false]);
			} else {
				setTimeout(function() {checkImg(img, timeSpent);}, config.timeIncriment);
			}
		}
	}
	
	/***********
		This function generates the plates for the carousel
	************
		Takes
			xml (XML object) the XML document
	***********/
	function generateCarousel(xml) {

		// Make the carousel
		var $carousel = $('<div id="' + config.carousel + '" />');

		// Make the arrows
		$('<a class="' + config.arrowClass + '" id="' + config.prev + '" href="#"><span>&lt; Previous</span></a>').appendTo($carousel);
		$('<a class="' + config.arrowClass + '" id="' + config.next + '" href="#"><span>Next &gt;</span></a>').appendTo($carousel);

		// Make as many plates as we have rules
		for (var plates = $('panel', xml), i = 0, il = plates.length; i < il; i++) {
			// Give the plates an ID and a class...
			var $plate = $('<div id="' + config.plateIDroot + (i + 1) + '" class="' + config.plateClass + '">');
			// ...an image...
			$('<img src="' + $(plates[i]).find('background').attr('image') + '" class="' + config.plateImgClass + '" />').appendTo($plate);
			// ...and a heading
			$('<div class="' + config.headingClass + '"><p>' + $(plates[i]).find('title').attr('text') + '</p></div>').appendTo($plate);
			// Add the plate to the carousel
			$($carousel).append($plate);
		}
		
		// Add the completed carousel to the holder
		$('#' + config.carouselHolder).append($carousel);
		
		// Check the images were found and handle those that weren't
		$('.' + config.plateImgClass).each(function() {checkImg(this);});
		
		// Declare the function to be done
		arguments.callee.done = true;
	}	
	
	/***********
		A function to generate the white boxes for each plate
	************
		Takes:
			panel (object) a reference to the <panel> tag in the XML document
			plate (integer) the plate number of the front plate
	***********/
	function generateTabs(panel, plate) {
		var $plate = $('.' + config.plateClass)[plate];

		// Generate the hero
		var $hero = $('<div class="' + config.heroClass + '" />');
		if ($(panel).find('big').attr('text') !== "" && $(panel).find('big').attr('text') !== " " && $(panel).find('big').attr('text').toLowerCase().indexOf('nbsp;') < 0) {
			$big = $('<p class="big">' + $(panel).find('big').attr('text') + '</p>');
			// Since MSIE can't handle text-shadows but does have filters, clone the headings so we can add filters in the CSS
			//comment on 16Mar2011
			//if ($.browser.msie) {$($big).clone().appendTo($big);}
			$($big).appendTo($hero);
			if ($(panel).find('little').length > 0) {
				$little = $('<p class="little">' + $(panel).find('little').attr('text') + '</p>');
				//comment on 16Mar2011
				//if ($.browser.msie) {$($little).clone().appendTo($little);}
				$($little).appendTo($hero);
			}
		}
	
		// Only add this if we found anything in the hero text
		if (!$($hero).is(':empty') && ($($plate).find('.' + config.heroClass).length < 1)) {
			$($plate).append($hero);
		}
		
		// Generate the contents
		var $contents = $('<div class="' + config.contentsClass + '" />');
		if (ie8) {
			$('<a href="' + $(panel).find('button').attr('url') + '" class="buttonBlue"><span>' + $(panel).find('button').attr('text') + '</span></a>').appendTo($contents);
		} else {
			$('<a href="' + $(panel).find('button').attr('url') + '" class="' + config.ctaClass + '">' + $(panel).find('button').attr('text') + '</a>').appendTo($contents);
		}
		
		// Create a holding div for the tabs
		$tabNavigator = $('<div class="tabNavigator" />');
		
		$(panel).find('tab').each(function(i) {
			var rel = '',
				type = $(this).attr('type').toLowerCase();
			
			// Add the tab, if necessary
			if ($(this).attr('text') != "") {
				var divClass = (i === 0) ? ' ' + config.activeTabClass : '';
				$('<div class="' + config.tabClass + divClass + '">' + $(this).attr('text') + '</div>').appendTo($tabNavigator);
				rel = ' rel="' + $(this).attr('text').toLowerCase() + '"';
			}
		
			// Create the contents
			$whiteBox = $('<div class="' + config.whiteBoxClass + (ie8 ? ' ' + config.whiteBoxClass + '_ie' : '') + ' ' + type + '"' + rel + ' />');
			
			if (type == "three_images_links") {

				$('<p class="main">' + $('main', this).attr('text') + '</p>').appendTo($whiteBox);
				
				var $quicklinks = $('<ul class="quicklinks" />');
				$('quicklinks link', this).each(function(j) {
					$('<li><a href="' + $(this).attr('url') + '">' + $(this).attr('text') + '</a></li>').appendTo($quicklinks);
				});
				$($whiteBox).append($quicklinks);
				
				var $imagelink = $('<ul class="imagelink" />');
				$('imagelink', this).each(function(j) {
				    //TODO un-harcode this before go live
					$('<li><a href="' + $(this).attr('url') + '"><img src="' + $(this).attr('image') + '" alt="' + $(this).attr('imageAltText') + '" /><span>' + $(this).attr('text') + '</span></a></li>').appendTo($imagelink);
				});
				$($whiteBox).append($imagelink);
				
				
			} else if (type == "text_four_images") {
				
				$('<p class="main">' + $('main', this).attr('text') + '</p>').appendTo($whiteBox);
				
				var $imagelink = $('<ul class="imagelink" />');
				$('imagelink', this).each(function(j) {
					//TODO un-harcode this before go live
					$('<li><a href="' + $(this).attr('url') + '"><img src="' + $(this).attr('image') + '" alt="' + $(this).attr('imageAltText') + '" /><span>' + $(this).attr('text') + '</span></a></li>').appendTo($imagelink);
				});
				$($whiteBox).append($imagelink);
				
				
			} else if (type == "title_two_columns") {
				
				$('<p class="main">' + $('main', this).attr('text') + '</p>').appendTo($whiteBox);
				
				$('quicklinks', this).each(function(j) {
					var $quicklinks = $('<ul class="quicklinks' + (j !== 0 ? ' shorter_quicklinks' : '') + '" />');
					$('link', this).each(function(k) {
						$('<li><a href="' + $(this).attr('url') + '">' + $(this).attr('text') + '</a></li>').appendTo($quicklinks);
					});
					if (!$($quicklinks).is(':empty')) {$($whiteBox).append($quicklinks);}
				});
				
			} else if (type == "six_images_links") {
				
				var $imagelink = $('<ul class="imagelink" />');
				$('imagelink', this).each(function(j){
					//TODO un-harcode this before go live
					$('<li><a href="' + $(this).attr('url') + '"><img src="' + $(this).attr('image') + '" alt="' + $(this).attr('imageAltText') + '" /><span>' + $(this).attr('text') + '</span></a></li>').appendTo($imagelink);
				});
				$($whiteBox).append($imagelink);
				
			}
			
			$($contents).append($whiteBox);
			
		}); // end of each

		// Again, only add if we found something to add
		if (!$($tabNavigator).is(':empty')) {
			$($contents).append($tabNavigator);
			// Assign each tab and handler to change the tabs
			$('.' + config.tabClass, $tabNavigator).click(function(e){changeTab(e.target);});
		}
		
		// Add the contents to the plate
		$($plate).append($contents);
		
		// Hide all but the first tab
		changeTab($('#' + config.plateIDroot + (plate + 1) + ' .' + config.tabClass)[0]);
		
		// Fade in the tabs
		callback(plate, true);
	}
	
	/***********
		The AJAX function to get the information from the XML
	************
		Takes:
			node (integer, optional) a reference to which panel's contents to add
	***********/
	function getXML(node) {
		$.ajax({
			type: config.ajaxType,
			url: config.ajaxURL,
			dataType: ($.browser.msie ? "html" : "xml"),
			success: function(data) {
				
				// Decode the XML data from the string if we're in IE to allow this to work locally
				var xml;
				if (typeof data == "string") {
					xml = new ActiveXObject("Microsoft.XMLDOM");
					xml.async = false;
					xml.loadXML(data);
				} else {
					xml = data;
				}
				
				// Generate the carousel if we don't have a node, and generate the tag is we do
				if (typeof(node) === "undefined") {
					generateCarousel(xml);
				} else {
					generateTabs($('panel', xml)[node], node);
				}

			},
			error: function(xhr, txt, err) {
				alert('There has been a problem loading the XML document.\n\nError Text:' + txt + '\n----------\nError Code:' + err);
			}
		});
	}
	
	/***********
		A function to handle the plates being clicked
	************
		Takes
			element (HTML Element) the plate that was clicked
	***********/
	function handleClick(element) {
		
		// Run through each of the classes that the parent plate has, since the image would be clicked
		var parentClasses = $(element).parents('.' + config.plateClass).attr('class').split(' '),
			plateClass;
		
		// Find which of our classes is attached to this plate
		for (var i = 0, il = classes.length; i < il; i++) {
			if ($.inArray(classes[i], parentClasses) > -1) {
				plateClass = classes[i];
				break;
			}
		}

		// Only change if one of the side plates were clicked -- not the front one
		if (plateClass !== classes[0]) {
			changeRules({previous: (plateClass.indexOf('left') > -1), furtherIterations: ((plateClass.indexOf('rear') > -1) ? 1 : 0)});		
		}
	}

	/***********
		A closure to work out how many more times the carousel needs to animate
	************
		Takes:
			settings (object)
				furtherIterations (integer) the number of times the carousel needs to animate again
	***********/
	function handleClosure(settings) {
		// If we can animate (ie. the carousel is not currently animating) reduce the number of further iterations by one and re-animate it
		if (animatable) {
			$.extend(settings, {furtherIterations: (settings.furtherIterations - 1)});
			changeRules(settings);
		// If not, try this function again and the same settings we currently have
		} else {
			setTimeout(function(){handleClosure(settings);}, 30);
		}
	}

	/***********
		An event bound to the document
	************
		Takes:
			evt (event)
			img (jQuery object Image)
				the image whose status is being checed
			status (boolean)
				true if the image is fine, false if it failed to load
	***********/
	function imgReady(evt, img, status) {
		//var name = $(img).attr('src'),
		//	part = name.substring(name.lastIndexOf('/') + 1, name.lastIndexOf('.'));
		var imgLoc = parseFloat($(img).parents('.' + config.plateClass).attr('id').split(config.plateIDroot)[1]) - 1;
		
		if (status) {
			//alert(part + '\nloaded (finally)');
			$(img).addClass('corner iradius10');
			addImageCorners();
		} else {
			//alert(part + '\nnever loaded');
			//$('#' + config.plateIDroot + (i + 1)).addClass(config.noImgClass);
			$(img).parents('.' + config.plateClass).addClass(config.noImgClass);
		}
		
	}
	
	/***********
		This function handles all the setting up
	***********/
	function initiate() {
	
		// Add corner rules
		if (!ie8) {applyCorners();}
		
		// Create an event for all the images
		$(document).bind('imageReady', imgReady);
	
		// Generate the Carousel from the XML
		getXML();

		// Position all our plates
		setRules({front: $front});
	}

	/***********
		A function that checks the image has loaded properly
		Found at http://www.sajithmr.me/javascript-check-an-image-is-loaded-or-not/
	***********/
	function IsImageOk(img) {
		if (!img.complete) {return false;}
		if (typeof img.naturalWidth !== "undefined" && img.naturalWidth === 0) {return false;}
		return true;
	}
	
	/***********
		Set all the handlers for the plates, arrows and tabs
	***********/
	function setHandlers() {

		// Make sure we can set handlers and only set them once
		if(!generateCarousel.done || !arguments.callee.ready || arguments.callee.done){return;}
		
		// Assign click handlers to each of the plates
		$('.' + config.plateClass).click(function(e){handleClick(e.target);});
		
		// Assign click handlers to next an previous buttons
		$('#' + config.prev).click(function(e){e.preventDefault(); changeRules({previous: true});});
		$('#' + config.next).click(function(e){e.preventDefault(); changeRules({previous: false});});
		
		// Assign click handlers to each of the tabs
		$('.' + config.tabClass).click(function(e){changeTab(e.target);});
		
		// Stop this firing again, the job's done
		arguments.callee.done = true;
	}
	
	/***********
		A function to set the dimensions and positions of all the plates
	************
		Takes:
			settings (object)
				front (integer) the plate at the front
				position (integer) the rule number to use
				recursing (boolean) have one of the back plates been clicked?
	***********/
	function setRules(settings) {

		// Basically, wait until the carousel has been added to the DOM
		if (!generateCarousel.done) { setTimeout(arguments.callee, 30); } else { setHandlers.ready = true; }

		// Extend the defaults and declare one of them to the world
		var defaults = {
			front: 0,
			position: 0,
			recursing: false
		};
		$.extend(defaults, settings);
		arguments.callee.recursing = defaults.recursing;
		
		// Loop through each of the plates
		for (var j, i = 0, il = rules.length; i < il; i++) {
			// Work out which series of rules we want by adding the front plate number to the iteration
			j = defaults.front + i;
			// If our rule number is higher than or equal to our maximum, subtract the maximum to turn 5 into 0, 6 into 1 etc
			if (j >= il) {j -= il;}
			
			// Grab the plate we want to animate
			$('#' + config.plateIDroot + (i + 1))
				// Set the z-index manually since animating it doesn't work
				.css('zIndex', zindices[defaults.position][j])
				// Animate with the given set of rules
				.animate(rules[j], {complete: function(){callback((rules.length - j - 1));}})
				
				// Swap the position class for a new one, since the position is changing
				.removeClass(classes.join(' '))
				.addClass(classes[j])
				
				// Hide the contents
				.find('.' + config.contentsClass).hide()
				.siblings('.' + config.heroClass).hide();
			
			// Tweak the VML, if we have VML...
			setVMLrules((i + 1), j);
		}
		
		// Give each of the elements a handler
		setHandlers();
	}

	/***********
		A function to adjust the rules of the VML
		This is only called in IE
	***********/
	function setVMLrules(plateRef, ruleNo) {

		// Did we use VML?
		if ($('var').length < 1) { return; }

		// Create some default figures and adjust them if we're in IE8
		var w = '100%', h = '100%', m = 0;
		if (document.documentMode && document.documentMode > 7) { w = rules[ruleNo].width + 'px'; h = rules[ruleNo].height + 'px'; m = 'auto';}

		// Adjust the VML
		$('#' + config.plateIDroot + plateRef)
			.find('var').css({width: w, height: h, left: 0, top: 0})
			.find('group').css({width: w, height: h, left: 0, top: 0, margin: m});
		
		// Tweak the margin if the margine have beenbehaving themselves correctly
		if ($.browser.msie && !ie8) {$('#' + config.plateIDroot + plateRef).find('roundrect').css({margin: m});}
		
	}
	
	// Activate the function
	initiate();
});
