// namespacing
if( typeof Gratuitous == 'undefined' ) Gratuitous = {};
/*
 * Gratuitous constructor
 * @author	Dan Neame <danneame@gmail.com>
 * @arg		params				Array
 * @return	void
 */
var Gratuitous = function( params ){
	
	// params is an empty array by default
	params = params || [];

	// set object vars
	this.container;
	this.timer;
	this.restore_to;
	// if pagewrapper truthy, set pagewrapper to element id passed. Default to element id 'pagewrapper'
	this.pagewrapper = !!params[ 'pagewrapper' ] ? document.getElementById( params[ 'pagewrapper' ] ) : document.getElementById( 'pagewrapper' );
	// set wrapper. Default to element id 'wrapper'
	this.wrapper = !!params[ 'wrapper' ] ? document.getElementById( params[ 'wrapper' ] ) : document.getElementById( 'wrapper' );
	// set close button path. Default to http://www.3ev.com/demos/gratuitous/images/close.png
	this.closebutton_path = !!params[ 'closebutton_path' ] ? params[ 'closebutton_path' ] : 'http://www.3ev.com/demos/gratuitous/images/close.png';
	// set loading path. Default to http://www.3ev.com/demos/gratuitous/images/loader.gif
	this.loadbutton_path = !!params[ 'loadbutton_path' ] ? params[ 'loadbutton_path' ] : 'http://www.3ev.com/demos/gratuitous/images/close.png';
	
	// create loadbutton now - we do not want to have to preload this, need width and height attributes in another onload
	this.loadimg = document.createElement( 'img' );
	this.loadimg.src = this.loadbutton_path;
	this.loadimg.style.display = 'none';
	document.body.appendChild( this.loadimg );

	// set class to bind to. Default to 'gratuitous_launch'.
	// set FPS
	this.fps = !!params[ 'fps' ] ? ( 1000 / params[ 'fps' ] ) : 30;

	// bind showcase calls to anchor elements of class name passed or default of 'gratuitous_launch'
	this.bind( params[ 'bindtoclass' ] || 'gratuitous_launch' );

};

/*
 * showcase method
 * @author	Dan Neame <danneame@gmail.com>
 * @arg		anchor_element		HTMLAnchorElement
 * @arg		type				String (enum image|youtube)
 * @return	void
 */
Gratuitous.prototype.showcase = function( anchor_element ){

	// inject gratuitous xhtml
	this.prepare_container();
	// get link path
	var path = anchor_element.href;
	
	// if the link is to youtube, prepare for video
	// else prepare for image
	var ytreg = path.match( /^http:\/\/(www|)\.youtube\.com\/watch\?v=([0-9A-Z-_]*)/i );
	if( !!ytreg ){
		this.prepare_youtube( ytreg[ 2 ] ); // 0 is everything, 1 is www , 2 is the video id
		this.hide_loading();
		this.showcase_animate();
	}else{
		this.prepare_image( path );
		// do not call animate here - called on image load - see prepare_image
	}

};
/*
 * showcase_animate method
 * does the actual animation
 * @author	Dan Neame <danneame@gmail.com>
 * @arg		anchor_element		HTMLAnchorElement
 * @arg		type				String (enum image|youtube)
 * @return	void
 */
Gratuitous.prototype.showcase_animate = function( anchor_element ){
	// set local reference to this for setInterval method
	var that = this;

	// everything is in place - run the animation
	// turn off document scrolling
	document.body.style.overflowX = 'hidden';
	this.timer = setInterval( function(){

		// container x , wrapper x and delta x
		var cx = parseInt( that.container.style.left );
		var wx = parseInt( that.wrapper.style.marginLeft );
		var dx = Math.floor( cx / 4 );
		
		// stop if dx is 0
		if( dx == 0 ){
			clearInterval( that.timer );
		}
		
		// else move container and wrapper to the left by dx
		that.container.style.left = ( cx - dx ) + 'px';
		that.wrapper.style.marginLeft = ( wx - dx ) + 'px';

	} , 1000 / this.fps );
	
	// remember where we are now
	this.restore_to = this.get_current_scroll();
	// scroll to 0 , 0
	this.scrolltimer = setInterval( function(){
		
		// get current scroll
		var currentscroll = that.get_current_scroll();

		// current scroll x and y
		var sx = currentscroll[ 0 ];
		var sy = currentscroll[ 1 ];
		// delta x and y
		var dx = Math.floor( sx / 5 );
		var dy = Math.floor( sy / 5 );

		if( dx == 0 && dy == 0 ){
			clearInterval( that.scrolltimer );
		}

		window.scrollTo( dx , dy );
	} , 1000 / this.fps );

};

/*
 * unshowcase method
 * moves from the video / image back to the page
 * @author	Dan Neame <danneame@gmail.com>
 * @arg		null
 * @return	void
 */
Gratuitous.prototype.unshowcase = function(){

	// set local variable that to this for referencing in setInterval context
	var that = this;
	// we need to know when to stop animating - this will be when the container left is smaller than 0 - it's width
	var gratuitous_width = parseInt( that.container.style.width );
	// everything is in place - run the animation
	this.timer = setInterval( function(){

		// container x , wrapper x and delta x
		var cx = parseInt( that.container.style.left );
		var wx = parseInt( that.wrapper.style.marginLeft );
		var dx = Math.floor( wx / 4 );

		// stop if next tick will take us over negative gratuitous_width
		if( dx == 0 ){
			clearInterval( that.timer ); // stop timer
			document.body.style.overflowX = 'auto'; // turn back on document body scroll in x
			that.pagewrapper.removeChild( that.container ); // remove gratuitous DOM objects
			that.wrapper.style.marginLeft = '0px'; // explicitly set marginLeft to 0
		}
		
		// else move container and wrapper to the left by dx
		that.container.style.left = ( cx - dx ) + 'px';
		that.wrapper.style.marginLeft = ( wx - dx ) + 'px';

	} , 1000 / this.fps );

	// scroll to this.restore_to
	this.scrolltimer = setInterval( function(){
		
		// get current scroll
		var currentscroll = that.get_current_scroll();

		// current scroll x and y
		var sx = currentscroll[ 0 ];
		var sy = currentscroll[ 1 ];

		// new x and y
		var dx = Math.floor( ( that.restore_to[ 0 ] - sx ) / 5 );
		var dy = Math.floor( ( that.restore_to[ 1 ] - sy ) / 5 );

		// if dx and dy smaller than 2, stop (can afford a little inaccuracy for speed here)
		if( dx < 2 && dy < 2 ){
			clearInterval( that.scrolltimer );
		}

		window.scrollTo( sx + dx , sy + dy );
	} , 1000 / this.fps );

};

/*
 * show_loading method
 * show the loading animation
 * @author	Dan Neame <danneame@gmail.com>
 * @arg		aElem			HTMLAnchorElement
 * @return	HTMLAnchorElement
 */
Gratuitous.prototype.show_loading = function( aElem ){
	var root_coord = this.get_element_position( aElem );
	this.loadimg.style.position = 'absolute';
	this.loadimg.style.left = root_coord[ 0 ] + 'px';
	this.loadimg.style.top = root_coord[ 1 ] + 'px';
	this.loadimg.style.display = 'block';
};

/*
 * hide_loading method
 * hide the loading animation
 * @author	Dan Neame <danneame@gmail.com>
 * @arg		null
 * @return	HTMLAnchorElement
 */
Gratuitous.prototype.hide_loading = function(){
	this.loadimg.style.display = 'none';
};

/*
 * create_closebutton method
 * creates a close button
 * @author	Dan Neame <danneame@gmail.com>
 * @arg		null
 * @return	HTMLAnchorElement
 */
Gratuitous.prototype.create_closebutton = function(){

	// create local reference to this for use in anchor onclick handler
	var that = this;

	var a = document.createElement( 'a' );
	a.style.display = 'block';
	a.style.position = 'absolute';
	a.style.top = a.style.left = '0px';

	var img = document.createElement( 'img' );
	img.src = this.closebutton_path;

	// on click, unshowcase Gratuitous and do not bubble the click event / return false
	a.onclick = function(){
		that.unshowcase();
		return false;
	};
	
	// append img to anchor
	a.appendChild( img );

	// return anchor
	return a;

};


/*
 * prepare_container method
 * creates DOM element/s required to hold the element we want to highlight
 * injects required DOM elements to show the item
 * @author	Dan Neame <danneame@gmail.com>
 * @arg		null
 * @return	void
 */
Gratuitous.prototype.prepare_container = function(){
	
	var containerID = 'GratuitousContainer';

	// if GratuitousContainer in DOM then exit method
	if( !!document.getElementById( containerID ) ) return;

	// get window dimensions - width is used to position container
	var windowDims = this.get_window_dims();
	// get wrapper position - top is used to position container
	var wrapperPos = this.get_element_position( this.wrapper );
	// create gratuitous container
	this.container = document.createElement( 'div' );
	// prepare container - ID and style
	this.container.id = containerID;
	this.container.style.position = 'absolute';
	this.container.style.top = wrapperPos[ 1 ] + 'px';
	this.container.style.left = -windowDims[ 0 ] + 'px';
	this.container.style.width = windowDims[ 0 ] + 'px';
	this.container.style.overflow = 'hidden';

	// explicitly set wrapper marginLeft to 0px
	this.wrapper.style.marginLeft = '0px';

	// append to pagewrapper
	this.pagewrapper.appendChild( this.container );

};

/*
 * prepare_image method
 * injects
 * @author	Dan Neame <danneame@gmail.com>
 * @arg		null
 * @return	void
 */
Gratuitous.prototype.prepare_image = function( path ){

	var that = this;

	var img = document.createElement( 'img' );
	img.src = path;

	// onload, run the showcase animation
	img.onload = function(){
		that.hide_loading();
		that.showcase_animate();
	};

	// onclick, run the unshowcase animation
	img.onclick = function(){
		that.unshowcase();
		return false;
	};

	// append image to container
	this.container.appendChild( img );
	// append close button to container
	this.container.appendChild( this.create_closebutton() );

};

/*
 * prepare_youtube method
 * injects the youtube video specified by ID
 * @author	Dan Neame <danneame@gmail.com>
 * @arg		null
 * @return	void
 */
Gratuitous.prototype.prepare_youtube = function( id ){

	// local reference to this for use with onclick
	var that = this;
	
	// calculate left co-ord
	var x = ( Math.floor( parseInt( this.container.style.width ) * 0.5 ) - 320 );

	// prepare youtube object
	var youtube = document.createElement( 'object' );
	youtube.width = '640px';
	youtube.height = '385px';
	youtube.type = 'application/x-shockwave-flash';
	youtube.data = 'http://www.youtube.com/v/' + id + '&autoplay=1&showinfo=0';
	youtube.style.position = 'relative';
	youtube.style.top = '0px';
	youtube.style.left = x + 'px'; // middle of screen minus half youtube width

	// prepare close button
	var closebutton = this.create_closebutton();
	closebutton.style.left = x + 'px';
	closebutton.style.top = '0px';//'-385px';

	// append youtube to container
	this.container.appendChild( youtube );
	// append close button to container
	this.container.appendChild( closebutton );

};

/*
 * get_window_dims method
 * returns current window dimensions
 * @author	Dan Neame <danneame@gmail.com>
 * @arg		null
 * @return	Array
 */
Gratuitous.prototype.get_window_dims = function(){

	var x = 0;
	if( self.innerHeight ){
		x = self.innerWidth;
	}else if( document.documentElement && document.documentElement.clientHeight ){
		x = document.documentElement.clientWidth;
	}else if( document.body ){
		x = document.body.clientWidth;
	}
	
	var y = 0;
	if( self.innerHeight ){
		y = self.innerHeight;
	}else if( document.documentElement && document.documentElement.clientHeight ){
		y = document.documentElement.clientHeight;
	}else if( document.body ){
		y = document.body.clientHeight;
	}
	return [ x , y ];

};

/*
 * get_element_position method
 * returns object position on screen as [ x , y ]
 * @author	Dan Neame <danneame@gmail.com>
 * @arg		null
 * @return	Array
 */
Gratuitous.prototype.get_element_position = function( obj ){
	var curleft = curtop = 0;
	if( obj.offsetParent ){
		do{
			curleft += obj.offsetLeft;
			curtop += obj.offsetTop;
		}while( obj = obj.offsetParent );
	}
	return [ curleft , curtop ];
};

/*
 * get_current_scroll method
 * returns current window scroll as [ x , y ]
 * original code found at http://www.howtocreate.co.uk/tutorials/javascript/browserwindow
 * @author	Dan Neame <danneame@gmail.com>
 * @arg		null
 * @return	Array
 */
Gratuitous.prototype.get_current_scroll = function(){

	var scrOfX = 0, scrOfY = 0;
	if( typeof( window.pageYOffset ) == 'number' ) {
		//Netscape compliant
		scrOfY = window.pageYOffset;
		scrOfX = window.pageXOffset;
	} else if( document.body && ( document.body.scrollLeft || document.body.scrollTop ) ) {
		//DOM compliant
		scrOfY = document.body.scrollTop;
		scrOfX = document.body.scrollLeft;
	} else if( document.documentElement && ( document.documentElement.scrollLeft || document.documentElement.scrollTop ) ) {
		//IE6 standards compliant mode
		scrOfY = document.documentElement.scrollTop;
		scrOfX = document.documentElement.scrollLeft;
	}
	return [ scrOfX, scrOfY ];

};


Gratuitous.prototype.bind = function( cName ){
	// set local reference to this for use in onclick method
	var that = this;
	var candidates = document.getElementsByTagName( 'a' );
	// for every item in the NodeList
	for( var i=0; i < candidates.length; i++ ){
		// if it has a class and it matches cName passed
		if( candidates.item( i ).hasAttribute( 'class' ) && candidates.item( i ).className == cName ){
			// onclick, trigger showcase
			candidates.item( i ).onclick = function( e ){
				e.preventDefault();
				// show loading
				that.show_loading( this );
				// run showcase
				that.showcase( this );
				return false;
			};
		}
	}
};