/**
* Global JavaScript Definitions
*
* @author				Matt Gifford
* @copyright			2011 Timeshifting Interactive Limited
*/

// Includes
dojo.require("dojo.NodeList-traverse");	// Adds .next(), .parent(), etc
dojo.require("dojo.fx");	// Adds wipes and other advanced animations
dojo.require("dojox.fx.scroll");	// Smooth scrolling to page elements

// Bootstrap
var viewHandler = WebPage;
function __onloadHandler()
	{
	if (viewHandler !== WebPage)
		{
		// Extend the base page class and create the xhtml application object
		viewHandler.inheritsFrom( WebPage );
		xa = new viewHandler();
		}
	else
		{
		// Create a generic xhtml application object
		xa = new WebPage();
		}

	// Main page initialization
	xa.init();
	}

// Queue bootstrap function
dojo.addOnLoad(__onloadHandler);


/**
* Creates a new WebPage object with methods used by all pages, can be extended to add page specific methods.
*
* @author				Matt Gifford
* @copyright			2011 Timeshifting Interactive Limited
*/
function WebPage()
	{
	// 1. Define Properties

	var _this = this;
	this.artboardScrolling = false;


	// 2. Define Public Methods

	/**
	* Sets up the initial page state and event handlers
	*
	* @return		void
	*/
	this.init = function()
		{
		this.__initAnchors();
		this.__initInputs();

		// Check for url hashs on the homepage
		if (dojo.hasClass('top', 'homepage') && window.location.hash)
			{
			if (window.location.hash == '#photography')
				{
				this.section({name:'photography'});
				}
			}
		}


	/**
	* Displays the specified dialog, or hides all the dialogs if the dialog name is false
	*
	* @param		name		The dialog name to display
	* @param		id				The content id for dialogs with variable content
	* @return		void
	*/
	this.dialog = function(name, id)
		{
		// Check if the container exists
		if (!document.getElementById('dialogs'))
			{
			var div = document.createElement('div');
			div.id = 'dialogs';
			div.className = 'hidden';
			div.innerHTML = '<div class="background" onclick="if(xa)xa.dialog(false);"></div>';
			document.body.appendChild(div);
			}

		// Stop any video players that are playing
		dojo.query('object').forEach(
			function(node)
				{
				try {
					node.sendEvent('PLAY', 'false');
					}
				catch (e) {}
				}
			);
		dojo.query('#dialogs .dialogVideoPlayer').forEach(
			function(node)
				{
				if (node.getElementsByTagName('object').length == 0)
					{
					node.parentNode.removeChild(node);
					}
				}
			);

		// Check we have valid name, if not close the dialogs
		if (!name)
			{
			dojo.query('#dialogs').addClass('hidden');
			return;
			}

		// Display the specified dialog
		switch (name)
			{
			case 'photography-item':
				var query = '#dialogs .dialogPhotographyItem' + id;
				var url = '/?page_id=' + id + '&ajax=1';
				break;

			case 'cinema-item':
				var query = '#dialogs .dialogCinemaItem' + id;
				var url = '/?page_id=' + id + '&ajax=1';
				break;

			case 'team-item':
				var query = '#dialogs .dialogTeamItem' + id;
				var url = '/?page_id=' + id + '&ajax=1';
				break;

			case 'message':
				var query = '#dialogs .dialogMessage';
				var url = '/dialog-message/';
				break;

			default:
				dojo.query('#dialogs').addClass('hidden');
				return;
			}

		// Check the dialog exists
		if (dojo.query(query).length == 0)
			{
			// The dialog doesn't exist in the DOM, so fetch it from the server
			dojo.xhrGet(
					{
					url: !!url ? url : '/default/path/to/get/dialogs/from/' + name,
					load: function(responseText)
						{
						// Add the dialog into the document
						dojo.query('#dialogs .dialogTemplate').addClass('hidden');
						var div = document.createElement('div');
						div.innerHTML = responseText;
						document.getElementById('dialogs').appendChild(div);

						// Parse any addthis buttons
						addthis.button('.addthis_button', addthis_config);

						// Display the dialog container
						dojo.query('#dialogs').removeClass('hidden');
						dojo.query('#dialogs')[0].scrollIntoView();

						// Add any jwplayer embed
						var jwplayers = dojo.query('.jwplayer-popup', div);
						for (var x = 0; x < jwplayers.length; x++)
							{
								jwplayer(jwplayers[x].id).setup({
									flashplayer: document.getElementById('baseurl').getAttribute('content') + "/js/player.swf",
									file: jwplayers[x].getAttribute("data-file"),
									image: jwplayers[x].getAttribute("data-image"),
									width: jwplayers[x].getAttribute("data-width"),
									height: jwplayers[x].getAttribute("data-height")
								});
							}

						// Execute any embedded inline script blocks
						var scripts = div.getElementsByTagName('script');
						for (var x = 0; x < scripts.length; x++)
							{
							if (window.execScript)
								{
								window.execScript(scripts[x].innerHTML);
								}
							else
								{
								window.eval(scripts[x].innerHTML);
								}
							}
						}
					}
				);
			return;
			}
		else
			{
			// Hide all dialogs and then display the new dialog
			dojo.query('#dialogs .dialogTemplate').addClass('hidden');
			dojo.query(query).removeClass('hidden');
			}

		// Display the dialog container
		dojo.query('#dialogs').removeClass('hidden');
		dojo.query('#dialogs')[0].scrollIntoView();
		}


	/**
	* Scrolls to the specified seciton
	*
	* @param		obj		The config object
	* @return		void
	*/
	this.section = function(obj)
		{
		var photography = dojo.query('.articlePhotography')[0];
		var cinema = dojo.query('.articleCinema')[0];

		if (obj.name == 'photography')
			{
            dojo.fx.wipeOut({node:cinema}).play();
            dojo.fx.wipeIn({node:photography}).play();
			obj.name = 'page';
			}

		if (obj.name == 'cinema')
			{
            dojo.fx.wipeIn({node:cinema}).play();
            dojo.fx.wipeOut({node:photography}).play();
			obj.name = 'page';
			}

		dojox.fx.smoothScroll(
			{
			node: document.getElementById(obj.name),
			win: window
			}).play();

		return false;
		}


	/**
	* Scrolls the artboard to the left or right
	*
	* @param		obj		The config object
	* @return		void
	*/
	this.scrollArtboard = function(obj)
		{
		// Return if we're already scrolling
		if (this.artboardScrolling == true)
			{
			return;
			}

		// Check scrolling is initialized for this artboard
		if (document.getElementById(obj.id).getAttribute('data-scrolling') != 1)
			{
			document.getElementById(obj.id).setAttribute('data-scrolling', 1);
			document.getElementById(obj.id).style.left = '0px';
			}

		var items = dojo.query('div.portfolioItem', document.getElementById(obj.id)).length;
		var itemWidth = 327;
		var offset = parseInt(document.getElementById(obj.id).style.left);
		var maxOffset = (items - 3 < 0 ? 0 : items - 3) * itemWidth * -1;

		// Check if we can't scroll any further left
		if (offset == 0 && obj.offset < 0)
			{
			this.artboardScrolling = true;
			dojo.animateProperty({node: obj.id, properties: {left: 40}, duration: 200, onEnd: function(node) { dojo.animateProperty({node: node, properties: {left: 0}, duration: 400, onEnd: function() {xa.artboardScrolling = false;}}).play(); }}).play();
			return;
			}

		// Check if we can't scroll any further right
		if (offset == maxOffset && 0 < obj.offset)
			{
			this.artboardScrolling = true;
			dojo.animateProperty({node: obj.id, properties: {left: maxOffset - 40 }, duration: 200, onEnd: function(node) { dojo.animateProperty({node: node, properties: {left: parseInt(node.style.left) + 40}, duration: 400, onEnd: function() {xa.artboardScrolling = false;}}).play(); }}).play();
			return;
			}

		// Calculate and bound the new offset
		var newOffset = offset + (obj.offset * itemWidth * -1);
		if (0 < newOffset)
			{
			newOffset = 0;
			}
		if (newOffset < maxOffset)
			{
			newOffset = maxOffset;
			}

		// Animate the scroll
		this.artboardScrolling = true;
		dojo.animateProperty({node: document.getElementById(obj.id), properties: {left: newOffset}, duration: 500, onEnd: function() {xa.artboardScrolling = false;}}).play();
		}



	/**
	* Validates and sends the contact form
	*
	* @return		void
	*/
	this.processContactForm = function()
		{
		var fields = [
			{name: 'name', title: 'Your Name'},
			{name: 'email', title: 'Email Address', filter: 'email'},
			{name: 'message', title: 'Your Message'}
			];
		if (this.validateForm({form: 'contactForm', required: fields}) == false)
			{
			return;
			}

		// Bind the contact form and send the ajax request the server
		dojo.xhrPost(
			{
			url: "/ajax-sendmessage.php",
			form: document.getElementById('contactForm'),
			load: function()
				{
				alert('Message Sent!');
				xa.dialog();
				},
			error: function()
				{
				// Generally this occurs on network error or timeout, so display a try again in a moment message
				alert('There was a problem sending your message,\nplease wait a moment and try again.');
				}
			});
		}


	/**
	* Validates the params object
	*
	* @param		obj			The description object of the form to validate
	* @return		void
	*/
	this.validateForm = function(obj)
		{
		// Check the object is actually an object
		if (typeof(obj) != 'object') return false;

		// Get the form fields
		var form = dojo.byId(obj.form);
		var fields = {
			inputs: form.getElementsByTagName('input'),
			selects: form.getElementsByTagName('select'),
			textareas: form.getElementsByTagName('textarea')
			};

		// Validate the fields
		for (var x = 0; x < obj.required.length; x++)
			{
			// Check the text input fields
			for (var y = 0; y < fields.inputs.length; y++)
				{
				// Text and password fields
				if (fields.inputs[y].name == obj.required[x].name && (fields.inputs[y].type == 'text' || fields.inputs[y].type == 'password'))
					{
					// Check it has a value
					if (fields.inputs[y].value.length < 1)
						{
						// Display an error message that the field can't be blank
						alert(('Please fill out the %s field').replace('%s', obj.required[x].title));
						return false;
						}

					// Check for minimum length
					if (!!obj.required[x].minLength && fields.inputs[y].value.length < obj.required[x].minLength)
						{
						// Display an error message that the field is wrong length
						alert(('Please correct the %s field, the minimum length is %d characters').replace('%s', obj.required[x].title).replace('%d', obj.required[x].minLength));
						return false;
						}

					// Check for maximum length
					if (!!obj.required[x].maxLength && obj.required[x].maxLength < fields.inputs[y].value.length)
						{
						// Display an error message that the field is wrong length
						alert(('Please correct the %s field, the maximum length is %d characters').replace('%s', obj.required[x].title).replace('%d', obj.required[x].maxLength));
						return false;
						}

					// If it's an email address, check it's valid
					if (!!obj.required[x].filter && obj.required[x].filter == 'email')
						{
						var regex = /[a-z0-9_+-.]+@[a-z0-9_-]*\.?[a-z0-9_-]*\.?[a-z0-9_-]*\.[a-z]+$/i;
						if (regex.test(fields.inputs[y].value) == false)
							{
							alert(('Please ensure the %s field is a valid email address').replace('%s', obj.required[x].title));
							return false;
							}
						}
					}

				// Checkbox fields, check it has been checked
				if (fields.inputs[y].name == obj.required[x].name && fields.inputs[y].type == 'checkbox' && fields.inputs[y].checked == false)
					{
					// Display an error message that the field can't be left unchecked
					// (this is unformatted as it's often specific to the field in question, like agreeing to terms and conditions)
					alert(obj.required[x].title);
					return false;
					}
				}

			// Check the select fields
			for (var y = 0; y < fields.selects.length; y++)
				{
				// If the field name matches, check it has a selection other than the default
				if (fields.selects[y].name == obj.required[x].name && fields.selects[y].selectedIndex == 0)
					{
					// Display an error message that the field can't be left unchosen
					alert(('Please select a %s').replace('%s', obj.required[x].title));
					return false;
					}
				}

			// Check the textarea fields
			for (var y = 0; y < fields.textareas.length; y++)
				{
				// If the field name matches, check it has a value
				if (fields.textareas[y].name == obj.required[x].name && fields.textareas[y].value.length < 1)
					{
					// Display an error message that the field can't be blank
					alert(('Please fill out the %s field').replace('%s', obj.required[x].title));
					return false;
					}
				}
			}

		// Form passes validation, so return true
		return true;
		}


	// 3. Define Protected Inheritable Methods

	/**
	* Adds standard event handlers to process in-page links and offsite links
	*
	* @return		void
	*/
	this.__initAnchors = function()
		{
		var links = document.getElementsByTagName('a');
		for (var x = links.length-1, node = null; 0 <= x; x--)
			{
			// Make a local reference to the node (dom node array operations are expensive)
			node = links[x];

			// Make offsite links and pdfs open in a new tab/window
			if (/\b(offsite|pdf)\b/.exec(node.className))
				{
				node.onclick = function()
					{
					window.open(this.href,'_blank');
					return false;
					}
				}

			// Set the active class on links to the current page
			if ((node.href == window.location.href || node.href == window.location.href + 'index.html') && node.href.indexOf('#') == -1)
				{
				dojo.addClass(node, 'active');
				}
			}
		}


	/**
	* Adds rollover support to input[type=image] elements, and adds placeholder attribute support if necessary
	*
	* @return		void
	*/
	this.__initInputs = function()
		{
		// 1. Placeholder text
		if (!("placeholder" in document.createElement("input")))
			{
			dojo.query(':not(input[placeholder=""]), :not(input[placeholder=""])').forEach(
				function(node)
					{
					// Set the initial value
					if (node.value.length == 0)
						{
						dojo.addClass(node, 'placeholder');
						node.value = node.getAttribute('placeholder');
						}

					// On focus clear placeholder, if set
					dojo.connect(node, 'onfocus', function()
						{
						if (this.value == this.getAttribute('placeholder'))
							{
							dojo.removeClass(this, 'placeholder');
							this.value = '';
							}
						});

					// On blur set placeholder, if empty
					dojo.connect(node, 'onblur', function()
						{
						if (this.value == '')
							{
							dojo.addClass(this, 'placeholder');
							this.value = this.getAttribute('placeholder');
							}
						});
					}
				);

			}

		// 2. Rollover images
		var rolloverCache = [];
		var inputs = dojo.query('input.hasRollover');
		for (var x = inputs.length-1, node = null, newImage = null; 0 <= x; x--)
			{
			// Make a local reference to the node (dom node array operations are expensive)
			node = inputs[x];

			// Add event handlers to swap the images
			node.onmouseover = function()
				{
				this.src = this.src.replace(/(-i8a8|-i24a8)?\.(gif|jpg|png)/, '-over$1.$2');
				}
			node.onmouseout = function()
				{
				this.src = this.src.replace(/-over(-i8a8|-i24a8)?\.(gif|jpg|png)/, '$1.$2');
				}

			// Pre-cache the rollover image
			newImage = new Image();
			newImage.src = node.src.replace(/(-i8a8|-i24a8)?\.(gif|jpg|png)/, '-over$1.$2');
			rolloverCache.push(newImage);
			}
		}
	}



/**
* Inherts a prototype from the specified class, updates the constructor reference
*
* @param		parent		The parent class or object
* @return		The inherted object
*/
Function.prototype.inheritsFrom = function( baseClass )
	{
	// Inherit the base class
	this.prototype = new baseClass;
	this.prototype.constructor = this;

	// Add access to the base's methods
	this.prototype.base = {};
	for (method in this.prototype)
		{
		// hasOwnProperty test is a workaround for "for..in" bug, see: http://yuiblog.com/blog/2006/09/26/for-in-intrigue/
		if (typeof this.prototype[method] === 'function' && this.prototype.hasOwnProperty(method) && this.prototype[method] !== this.prototype.constructor)
			{
			this.prototype.base[method] = this.prototype[method];
			}
		}
	return this;
	}

