/*  Facebox for Prototype, version 2.0
 *  By Robert Gaal - http://wakoopa.com 
 *
 *  Heavily based on Facebox by Chris Wanstrath - http://famspam.com/facebox
 *  First ported to Prototype by Phil Burrows - http://blog.philburrows.com
 *
 *  Licensed under the MIT:
 *  http://www.opensource.org/licenses/mit-license.php
 *
 *  Need help?  Join the Google Groups mailing list:
 *  http://groups.google.com/group/facebox/
 *
 *  Dependencies:   prototype & script.aculo.us + images & CSS files from original facebox
 *  Usage:          Append 'rel="facebox"' to an element to call it inside a so-called facebox
 *
 *--------------------------------------------------------------------------*/

var Facebox = Class.create({
	initialize: function(settings)
	{
		this.locked = false;
		this.isopen = false;
		this.pagename = '';
		this.cbparams = '';
		this.onclose = '';
		this.scrollingrequests = 0;

		this.settings = {
			followScroll: true,		// TRUE/FALSE: determines whether or not the facebox should slide across screen to follow scrolls and resizes
			positioning: 'absolute',	// STRING: 'center' or 'absolute': determines the positioning and reveal style of the box
			modal: false,			// BOOLEAN

			loading_image: '/core/plugins/facebox/images/loading.gif',
			close_image: '/core/plugins/facebox/images/close.png',
			image_types: new RegExp('\.' + ['png', 'jpg', 'jpeg', 'gif'].join('|') + '$', 'i'),
			inited: true
		};

		// Import passed settings, ITIS
		if (settings) Object.extend(this.settings, settings);

		// Generate facebox HTML
		this.settings.facebox_html = '\
			<div id="faceboxoverlay" style="display: none;"></div> \
			<div id="facebox" style="display:none;"> \
				<div class="popup"> \
					<table id="facebox_table"> \
						<tbody> \
							<tr> \
								<td class="facebox_tl"/><td class="facebox_t"/><td class="facebox_tr"/> \
							</tr> \
							<tr> \
								<td class="facebox_l"><div></div></td> \
								<td class="body"> \
									<div class="toprow"> \
										<a href="/_error.php" onclick="window.close();" class="close" style=""><img src="' + this.settings.close_image + '" title="close" class="close_image" /></a> \
										<div style="clear: right;"></div> \
										<div id="facebox_responses_wrapper" class="ajax_responses_wrapper"> \
											<img src="'+this.settings.loading_image+'" class="ajax_responses_loading" alt="loading..." id="facebox_responses_loading" style="display: none;" /> \
											<img src="/core/images/image.php?src=/images/responses/icon_response_error.png&amp;h=20" class="ajax_responses_error" alt="Error: " id="facebox_responses_error" style="display: none;" /> \
											<img src="/core/images/image.php?src=/images/responses/icon_response_success.png&amp;h=20" class="ajax_responses_success" alt="Success: " id="facebox_responses_success" style="display: none;" /> \
											<div id="facebox_responses_text" class="ajax_responses_text"></div> \
											<div style="clear: both;"></div> \
										</div> \
									</div> \
									<div class="content"> \
									</div> \
								</td> \
								<td class="facebox_r"/><div></div></td> \
							</tr> \
							<tr> \
								<td class="facebox_bl"></td><td class="facebox_b"></td><td class="facebox_br"></td> \
							</tr> \
						</tbody> \
					</table> \
				</div> \
			</div>';

		$(document.body).insert({bottom: this.settings.facebox_html});
		
		this.preload = [ new Image(), new Image() ];
		this.preload[0].src = this.settings.close_image;
		this.preload[1].src = this.settings.loading_image;
		
		f = this;
		$$('#facebox .b:first-child, #facebox .bl, #facebox .br, #facebox .tl, #facebox .tr').each(function(elem){
			f.preload.push(new Image());
			f.preload.slice(-1).src = elem.getStyle('background-image').replace(/url\((.+)\)/, '$1');
		});
		
		this.facebox = $('facebox');
    		this.keyPressListener = this.watchKeyPress.bindAsEventListener(this);
    		this.scrollListener = this.watchScroll.bindAsEventListener(this);
		
		this.watchClickEvents();
		fb = this;
		Event.observe($$('#facebox .close').first(), 'click', function(e)
		{
			Event.stop(e);
			fb.close();
		});
		Event.observe($$('#facebox .close_image').first(), 'click', function(e)
		{
			Event.stop(e);
			fb.close();
		});
	},
	
	watchKeyPress: function(e)
	{
		// Close if espace is pressed or if there's a click outside of the facebox
		if (this.isopen && (e.keyCode == 27))// || !Event.element(e).descendantOf(this.facebox)))
		{
			this.close();
		}
	},
	
	watchScroll: function(e)
	{
		if (!this.settings.followScroll) return true;
	
		// IE seems to fire several scroll events for a single user scroll action, instead of one.  This causes some glitchiness in the animation...
		// To resolve this, we use a short time internal to "compress" all of the near-instantly-sequential events into one: the last one:
		
		// Increment the scrollingrequests counter (to mark that we're sending a request)
		this.scrollingrequests++;
		// In 50 milliseconds, check if this is the last request, and if so, do the scroll
		window.setTimeout('facebox.doScroll('+this.scrollingrequests+');', 50);
	},
	
	doScroll: function(scrollingrequests)
	{
		if (scrollingrequests == this.scrollingrequests)
		{
			this.scrollingrequests = 0;

			// Realign the overlay, ITIS
			if ($('faceboxoverlay').visible()) $('faceboxoverlay').setStyle({'width': document.viewport.getWidth()+'px', 'height': document.viewport.getHeight()+'px', 'left': document.viewport.getScrollOffsets().left+'px', 'top': document.viewport.getScrollOffsets().top+'px'});

			// Slide the facebox to realign it with the scrolled document
			var pageScroll = document.viewport.getScrollOffsets();
			new Effect.Move(this.facebox, {
					x: parseInt(document.viewport.getWidth() / 2 - (this.facebox.getWidth() / 2)),
					y: parseInt(pageScroll.top + (document.viewport.getHeight() / 10)),
					mode: 'absolute',
					duration: 1
			});
		}
	},
	
	watchClickEvents: function(e)
	{
		var f = this;
		$$('a[rel*=facebox]').each(function(elem,i)
		{
			Event.observe(elem, 'click', function(e)
			{
				// Get the pagename from the href
				pagename = '';
				href = elem.href.toString();
				
				// Get the pagename from the filename
				pagename = href.split('.php');
				pagename = pagename.shift().split('/');
				pagename = pagename.pop();

				// Get the onclose from the querystring, ITIS
				onclose = '';
				qs = elem.href.toString();
				qs = qs.split('?');
				qs = qs.pop().split('&');

				regexp = /^onclose=/i;
				for (i = 0; i < qs.length; i++)
				{
					if (qs[i].replace(regexp, '') != qs[i])
					{
						onclose = qs[i].replace(regexp, '').replace(':', '=');
					}
				}

				// Get the cbparams from the querystring, ITIS
				cbparams = '';
				qs = elem.href.toString();
				qs = qs.split('?');
				qs = qs.pop().split('&');

				regexp = /^cbparams=/i;
				for (i = 0; i < qs.length; i++)
				{
					if (qs[i].replace(regexp, '') != qs[i])
					{
						cbparams = qs[i].replace(regexp, '').replace(':', '=');
					}
				}

				Event.stop(e);
				f.click_handler(elem, e, pagename, cbparams, onclose);
			});
		});
	},

	lock: function()
	{
		facebox.locked = true;
	},
	
	unlock: function()
	{
		facebox.locked = false;
	},
	
	loading: function()
	{
		// If loading indicator is already showing, just return true
		loadingindicator = $$('#facebox .ajax_responses_loading').first();
		if (loadingindicator.visible() && this.locked) return true;
		
		// Hide any responses from a previous facebox, etc, ITIS
		ajax.clearResponse('facebox_responses_wrapper');

		// Hide content wrapper and remove old contents, ITIS
		contentWrapper = $$('#facebox .content').first();
		// Hide...
		contentWrapper.setStyle('display: none;');
		// Remove old contents...
		contentWrapper.childElements().each(function(elem, i)
		{
			elem.remove();
		});
		
		// Remove old contentSpacer, ITIS
		contentSpacer = $$('#facebox .contentspacer').first();
		if (contentSpacer) contentSpacer.remove();

		// Show loading indicator
		facebox.showLoadingIndicator();

		// Make sure close button is hidden (this is really only for the first facebox load on this page, because on subsequent loads it will have already been hidden during the closing routine for the previous load)
		closeBtn = $$('#facebox .close').shift();
		closeBtn.hide();

		// Slide the viewport if the facebox is "above" the current viewport
		if ($('facebox').positionedOffset().top - document.body.cumulativeScrollOffset().top < 0)
		{
			window.setTimeout('Effect.ScrollTo(\'facebox\', {\'duration\': .5, \'offset\': -5});', 500);
		}

		// Align the facebox, ITIS
		if (this.settings.positioning == 'center')
		{
			$('facebox').setStyle({
				'left': document.viewport.getWidth() / 2 - ($('facebox').getWidth() / 2) + 'px'
			});
		}

    		// (Re)Start listeners
    		Event.stopObserving(document, 'keypress', this.keyPressListener);
    		Event.stopObserving(document, 'click', this.keyPressListener);
    		Event.stopObserving(window, 'scroll', this.scrollListener);
    		Event.stopObserving(window, 'resize', this.scrollListener);
    		Event.observe(document, 'keypress', this.keyPressListener);
    		Event.observe(document, 'click', this.keyPressListener);
    		Event.observe(window, 'scroll', this.scrollListener);
    		Event.observe(window, 'resize', this.scrollListener);
	},
	
	showLoadingIndicator: function()
	{
		cb = function()
		{
			// Hide close button
			closeBtn = $$('#facebox .close').shift();
			closeBtn.hide();
		};
		ajax.showLoadingIndicator('facebox_responses_wrapper', cb);
	},
	
	hideLoadingIndicator: function()
	{
		cb = function()
		{
			if (!facebox.modal) $$('#facebox .close').shift().show('Appear');
		};
		ajax.hideLoadingIndicator('facebox_responses_wrapper', cb);
	},
	
	open: function(elem, pagename, cbparams, onclose, params)	// If calling this function directly, method is like this: facebox.open(url);
	{
		// Data protection
		params = (params != undefined ? params : []);

		// Check lock
		if (this.locked) return false;

		// Set loading
		this.loading();

		// Data protection
		elemIsURL = false;
		try
		{
			if (elem.href.match(/#/)) {}
		}catch(e){
			elemIsURL = true;
		}

		if (elem.toString() == elem && elemIsURL)
		{
			url = elem;
			elem = null;
			elem = [];
			elem.href = url;
		}
				
		// support for rel="facebox[.inline_popup]" syntax, to add a class to the facebox
		var klass = (elem.rel ? elem.rel.match(/facebox\[\.(\w+)\]/) : '');
		if (klass) klass = klass[1];

		// Add modality, ITIS
		if (klass)
		{
			if (klass.match(/modal/)) params.modal = true;
			this.modal = params.modal;
		}

		// Apply class to wrapper, ITIS
		contentWrapper = $$('#facebox .content').first();
		if (klass) contentWrapper.addClassName(klass);
		
		// Set lock
		this.lock();
		
		// Show the facebox, though it will be empty still
		cb = function()
		{
			facebox.loading();
			facebox.isopen = true;
		}
		new Effect.Appear(this.facebox, {duration: .5, afterFinish: cb});

		// Save the pagename
		this.pagename = (pagename ? pagename : '');

		// Save the cbparams
		this.cbparams = (cbparams ? cbparams : '');

		// Save the onclose
		this.onclose = (onclose ? onclose : '');

		// Detect target URI type
		if (elem.href.match(/#/))
		{
			// anchor???

			var url = window.location.href.split('#')[0];
			var target = elem.href.replace(url+'#','');

			// var data = $$(target).first();
			var d = $(target);
			
			// create a new element so as to not delete the original on close()
			var data = new Element(d.tagName);
			data.innerHTML = d.innerHTML;
			this.reveal(data, null, params);
		}else if (elem.href.match(this.settings.image_types) && !elem.href.match(/force=nonimage/))
		{
			// Image
	
			var image = new Image();
			fb = this;
			image.onload = function() {
				fb.reveal('<div class="image"><img src="' + image.src + '" /></div>', null, params)
			}
			image.src = elem.href;
		}else{
			// Default: normal get request
			
			var fb  = this;
			var url = elem.href+(elem.href.indexOf('?') > 0 ? '&skin=none' : '?skin=none');

			new Ajax.Request(url,
			{
				method: 'get',
				requestTimeout: 1,
				
				onSuccess: function(transport)
				{
					fb.reveal(transport.responseText, null, params);
				},

				onFailure: function(transport)
				{
					fb.reveal(transport.responseText, null, params);
				},
				
				onTimeout: function(transport)
				{
					fb.reveal('<div class="regularcontent"><h1>Loading time out</h1><p>Oops! It looks like a network failure occured or this page took too long to respond. Please try again.</p></div>', null, params);
				}
			});
		}
	},
	
	reveal: function(data, klass, params)
	{
		// Data protection
		params = (params != undefined ? params : []);
		
		// Set modality, ITIS
		if (params.modal)
		{
			this.modal = true;
		}else{
			this.modal = false;
		}

		// Make sure we are showing the loading indicator
		this.loading();

		// Show overlay, ITIS
		if (this.modal)
		{
			$('faceboxoverlay').setStyle({'width': document.viewport.getWidth()+'px', 'height': document.viewport.getHeight()+'px', 'left': document.viewport.getScrollOffsets().left+'px', 'top': document.viewport.getScrollOffsets().top+'px'});
			$('faceboxoverlay').setOpacity(.5);
			$('faceboxoverlay').show();
		}

		// Apply class to wrapper, ITIS
		contentWrapper = $$('#facebox .content').first();
		if (klass) contentWrapper.addClassName(klass);

		if (!this.isopen)
		{
			// Wait... call this function again in a moment to try again
			facebox.currentData = data;

			window.setTimeout('facebox.reveal(facebox.currentData, \''+klass+'\', {'+(this.modal ? "'modal': true" : "")+'});', 250);
			return;
		}

		// Clear currentData, ITIS
		if (facebox.currentData) facebox.currentData = null;

		// Insert the contents
		contentWrapper.insert({bottom: data});

		// Create and insert spacer element - since the contentWrapper we just added to the DOM is hidden, we need to duplicate the with of it, and insert a spacer element so that our facebox is the correct width for the about-to-be-revealed content
		contentspacer = new Element('div', {'class': 'content contentspacer', 'style': 'width: '+parseInt(contentWrapper.getWidth())+'px; height: 1px; padding-top: 0; padding-bottom: 0; margin-top: 0; margin-bottom: 0;'});
		Element.insert(contentWrapper.parentNode, {bottom: contentspacer});

		// Show the facebox contents, if necessary
		if(!this.facebox.visible())
		{
			$$('#facebox .content').first().childElements().each(function(elem,i)
			{
				elem.show();
			});
		}

		// Align the facebox, ITIS
		if (this.settings.positioning == 'center')
		{
			this.facebox.setStyle({
				'left': document.viewport.getWidth() / 2 - (this.facebox.getWidth() / 2) + 'px'
			});
		}

    		// (Re)Start listeners
    		Event.stopObserving(document, 'keypress', this.keyPressListener);
    		Event.stopObserving(document, 'click', this.keyPressListener);
    		Event.stopObserving(window, 'scroll', this.scrollListener);
    		Event.stopObserving(window, 'resize', this.scrollListener);
		Event.observe(document, 'keypress', this.keyPressListener);
		Event.observe(document, 'click', this.keyPressListener);
    		Event.observe(window, 'scroll', this.scrollListener);
    		Event.observe(window, 'resize', this.scrollListener);

		// Graphically deliver the payload
		window.setTimeout('Effect.Grow($$(\'#facebox .content\').first(), {\'direction\': \'top-left\', \'duration\': .5, afterFinish: facebox._done});', 500);
	},
	
	_done: function()
	{
		// Reload
		facebox.reload();
	
		facebox.hideLoadingIndicator();
		facebox.unlock();
		ajax.done();
	},
	
	close: function(forced)
	{
		// Data protection
		forced = (forced == true ? true : false);

		// Reject if modal
		if (this.modal && !forced) return false;

		if ((!this.locked || forced) && this.isopen)
		{
			// Set lock
			this.lock();

			// Verify with the onclose function that we can move forward with the close
			goahead = true;
			if (this.onclose != '') 
			{
				if (eval(onclose+'(\''+this.pagename+'\');') == false)
				{
					goahead = false;
				}
			}

			if (goahead)
			{
				contentWrapper = $$('#facebox .content').first();

				// Show the loading indicator
				this.showLoadingIndicator();
				
				// Shrink the content
				new Effect.Shrink(contentWrapper, {duration: .5, direction: 'top-left'});

				// Hide the facebox
				window.setTimeout('new Effect.Fade(\'facebox\', {duration: .5});', 500);

				// Unlock, flag as not open, and hide overlay, ITIS
				window.setTimeout('facebox.unlock(); facebox.isopen = false; if ($(\'faceboxoverlay\') && $(\'faceboxoverlay\').visible()) $(\'faceboxoverlay\').hide();', 550);

				if (this.pagename != '' && typeof(closefaceboxcb) == 'function')
				{
					// Run the close callback function on the dashboard
					params = this.cbparams.toString() + params.toString();
					closefaceboxcb(this.pagename, params, this.onclose);
				}
			}
		}else{
			if (this.locked)
			{
				// Wait a little bit and try again
				window.setTimeout('facebox.close('+(forced ? 'true' : 'false')+');', 250);
			}
		}
	},
	
	click_handler: function(elem, e, pagename, cbparams, onclose)
	{
		Event.stop(e);

		this.open(elem, pagename, cbparams, onclose);
	},
	
	isChild: function(elem)
	{
		// This function returns true or false, depending on whether or not the passed elem is a CHILD of a facebox as per the DOM
		while (elem.parentNode)
		{
			if (elem.parentNode == this.facebox) return true;
			
			elem = elem.parentNode;
		}

		return false;
	},
	
	reload: function()
	{
		reloadFaceboxCodeObject();
	}
});





var facebox;
// Initial facebox is loaded at bottom of refering page...

function reloadFaceboxCodeObject()
{
	// Stop observing keypresses and scrolls
	facebox.keyPressListener = null;
	facebox.scrollListener = null;

	// Stop observing link clicks
	$$('a[rel*=facebox]').each(function(elem, i)
	{
		$(elem).stopObserving();
	});

	// Restart observations
	facebox.keyPressListener = facebox.watchKeyPress.bindAsEventListener(facebox);
	facebox.scrollListener = facebox.watchScroll.bindAsEventListener(facebox);
	facebox.watchClickEvents();
}

