/* @file mootools-1.2-extend.js */
/*
Script: Announce
	Inspired by Growl
*/
//var Announce = new Class();

/*
Script: Behaviour
	Introduces CSS event selectors.
		- Need to look at a way of allowing multiple instances of the Behaviour class to put events
		  onto the same element. Adding is fine, just the deleting would be a bit of a problem
*/
var Behaviour = new Native({
	name						: 'Behaviour',
	sheets						: null,
	initialize					: function(sheets) {
		if ($type(sheets) == 'object') {
			this.sheets = sheets;
		};
		window.addEvent('domready', function(event) {
			this.apply();
		}.bind(this));
		return this;
	}
});
Behaviour.implement({
	apply						: function() {
		for (var selector in this.sheets) {
			try {
				if ($type(this.sheets[selector]) == 'function') {
					$$(selector).each(function(element) {
						this.sheets[selector](element);
					}.bind(this));
				};
			} catch (e) {};
		};
	},
	destroy						: function() {
		for (var selector in this.sheets) {
			if (!selector) {
				return nil;
			};
			$$(selector).each(function(element) {
				if (element.uid && element.removeEvents) {
					element.removeEvents();
				}
			});
		};
		return null;
	},
	reload						: function() {
		for (var selector in this.sheets) {
			$$(selector).each(function(element) {
				element.removeEvents();
			});
		};
		this.apply();
	}
});

/*
Script: Browser.Popup
	Defines the Popup class for making popup windows.
	Browser.Popup.openWindow has to be called after a user's mouseclick
*/
Browser.Popup = new Class({
	Implements					: [Options, Events],
	options						: {
		directories				: 0,
		height					: 300,
		left					: 50,
		location				: 0,
		menubar					: 0,
		name					: 'popup',
		resizable				: 0,
		scrollbars				: 'auto',
		status					: 0,
		toolbar					: 0,
		top						: 50,
		width					: 500
		/* CallBacks */
		//onBlock				: $empty
	},
	blocked						: null,
	focusTries					: 0,
	initialize					: function(url, options) {
		this.url = $pick(url, false);
		this.setOptions(options);
		this.options.name = 'Popup_' + $time();
		if (this.url) {
			this.openWindow();
		};
	},
	openWindow					: function(url) {
		url = $pick(url, this.url);
		
		var options = '';
		$each(this.options, function(value, key) {
			options += key + '=' + value + ',';
		});
		options = options.substring(0, options.length - 1);
		
		this.window = window.open(url, this.options.name, options);
		
		if (!this.window) {
			this.window = window.open('', this.options.name, options);
			this.window.location.href = url;
		};
		
		this.focus.delay(100, this);
		return this;
	},
	focus						: function() {
		if (this.window) {
			this.window.focus();
		} else if (this.focusTries < 10) {
			this.focus.delay(100, this);
		} else {
			this.blocked = true;
			//this.fireEvent('onBlock');
		};
		return this;
	},
	close						: function() {
		this.window.close();
		return this;
	}
});

/*
Script: Element.Extras
	Description here
*/
if ($type(Element) != 'undefined') {
	Element.implement({
		validate				: function() {
			if (!this.length) {
				// nothing selected, can't validate, return nothing
				return;
			};
			var validation = new Validate(this);
		}
	});
};

/*
Script: Element.Forms
	
*/

/*
Script: Hash.Extras
	
*/

/*
Script: Heartbeat
	[Native] - new Heartbeat($function[, 300]);
*/

/*
Script: Lightbox
	A Lightbox clone for MooTools
*/
var Lightbox = new Class({
	Implements					: [Options, Events],
	options						: {
		background				: '#000000',
		duration				: {
			move				: 300,
			overlay				: 300,
			open				: 0
		},
		height					: 200,
		id						: null,
		margin					: 75,			/* Math function here :-( ... bigger the lightbox the smaller the value */
		opacity					: 0.8,
		transitions				: {
			move				: Fx.Transitions.Elastic.easeOut,
			overlay				: Fx.Transitions.linear,
			open				: Fx.Transitions.linear
		},
		width					: 400,
		zindex					: 9999,
		/* CallBacks */
		onLoad					: $empty,
		onShow					: $empty,
		onHide					: $empty
	},
	lightbox					: null,
	overlay						: null,
	submitBehaviour				: null,
	transitions					: [],
	wrapper						: null,
	toElement					: function() {
		return this.object;
	},
	initialize					: function(options) {
		this.instance = 'Lightbox_' + $time();
		
		this.setOptions(options);
		this.id = $pick(this.options.id, this.instance);
		
		window.addEvent('domready', this.build.bind(this));
		
		return this;
	},
	build						: function() {
		// Overlay
		this.overlay = new Element('div', {
			'class'				: 'lightbox-overlay',
			'id'				: this.id + '_Overlay',
			'styles'			: {
				//'background'	: this.options.background,
				'display'		: 'none',
				'width'			: window.getScrollWidth(),
				'height'		: window.getScrollHeight(),
				'opacity'		: 0,
				'z-index'		: (this.options.zindex >= 9998 ? this.options.zindex - 3 : this.options.zindex)
			}
		}).injectInside($(document.body));
		this.transitions['overlay'] = new Fx.Morph(this.overlay, {
			duration			: this.options.duration.overlay,
			link				: 'cancel',
			transition			: this.options.transitions.overlay
		});
		// Lightbox Wrapper
		this.wrapper = new Element('div', {
			'class'				: 'lightbox-wrapper',
			'id'				: this.id + '_Wrapper',
			'styles'			: {
				'display'		: 'none',
				'height'		: window.getScrollHeight(),
				'width'			: window.getScrollWidth(),
				'z-index'		: (this.options.zindex >= 9998 ? this.options.zindex - 2 : this.options.zindex)
			}
		}).injectInside($(document.body));
		// Can't use this at the moment, as it plays funny with checkboxes inside lightbox
		//this.wrapper.addEvent('click', function(event) {
		//	new Event(event).stop();
		//	this.hide();
		//}.bind(this));
		// Lightbox
		this.lightbox = new Element('div', {
			'class'				: 'lightbox',
			'id'				: this.id,
			'styles'			: {
				'opacity'		: 1,
				'width'			: this.options.width,
				'z-index'		: this.options.zindex - 1
			}
		}).injectInside(this.wrapper);
		// Close Button
		this.closeButton = new Element('div', {
			'class'				: 'lightbox-close-button'
		}).injectInside(this.lightbox);
		this.closeButtonLink = new Element('a', {
			'href'				: '#close',
			'class'				: 'lightbox-close',
			'styles'			: {
				'z-index'		: this.options.zindex
			},
			'title'				: 'Click to close.'	
		}).injectInside(this.closeButton);
		this.closeButtonLink.addEvent('click', function(event) {
			new Event(event).stop();
			this.hide();
		}.bind(this));
		// Content
		this.content = new Element('div', {
			'class'				: 'lightbox-content'
		}).injectInside(this.lightbox);
		this.transitions['lightbox'] = new Fx.Morph(this.lightbox, {
			duration			: this.options.duration.open,
			link				: 'cancel',
			transition			: this.options.transitions.open
		});
		
		// Window Listeners
		window.addEvent('resize', function(event) {
			if (this.overlay.getStyle('display') == 'block') {
				this.overlay.setStyles({
					'display'	: 'block',
					'height'	: window.getScrollHeight(),
					'width'		: window.getScrollWidth()
				});
				this.lightbox.setStyles({
					'left'		: (window.getScrollWidth() / 2) - (this.width / 2),
					'top'		: 120
				});
			};
		}.bind(this));
		
		this.object = this.overlay;
		this.fireEvent('load');
	},
	show						: function(height, width) {
		// iframe shim
		this.height = $pick(height, this.options.height);
		this.width = $pick(width, this.options.width);
		this.overlay.setStyles({
			'display'			: 'block',
			'height'			: this.height + 10,
			'width'				: this.width + 10
		});

		this.transitions['overlay'].start({
			opacity				: this.options.opacity,
			complete			: function() {
				this.wrapper.setStyle('display', 'block');
				this.lightbox.setStyles({
					'display'	: 'block',
					'opacity'	: 0
				});
				this.lightbox.setStyles({
					'opacity'	: 1,
					'left'		: (window.getScrollWidth() / 2) - ((this.width == 'auto' ? this.lightbox.getScrollSize().x : this.width) / 2),
					'top'		: (window.getSize().y / 2) - (this.height / 2) - this.options.margin + window.getScroll().y
				});
				this.transitions['lightbox'].start({
					'height'	: [10, (this.height == 'auto' ? this.lightbox.getScrollSize().y : this.height)],
					'width'		: [10, (this.width == 'auto' ? this.lightbox.getScrollSize().x : this.width)]
				});
				this.submitListener();
			}.bind(this)
		});
		
		this.hideFlash();
		this.fireEvent('show');
		// this can't go here, it will just basically cause the lightbox to crash
		// PLEASE test all areas of the lightbox before committing a change to one part!!!!
		// SNM.domready();
		return this;
	},
	hide						: function() {
		// iframe shim

		this.overlay.setStyle('display', 'none');
		this.overlay.setStyle('opacity', 0);
		this.wrapper.setStyle('display', 'none');
		
		//this.submitBehaviour.destroy();
		this.showFlash();
		this.fireEvent('hide');
		return this;
	},
	showFlash					: function() {
		var objects = document.getElementsByTagName('object');
		for (var i = 0; i < objects.length; i++) {
			objects[i].style.visibility = 'visible';
		};
		var embeds = document.getElementsByTagName('embed');
		for (var i = 0; i < embeds.length; i++) {
			embeds[i].style.visibility = 'visible';
		};
	},
	hideFlash					: function() {
		var objects = document.getElementsByTagName('object');
		for (var i = 0; i < objects.length; i++) {
			objects[i].style.visibility = 'hidden';
		};
		var embeds = document.getElementsByTagName('embed');
		for (var i = 0; i < embeds.length; i++) {
			embeds[i].style.visibility = 'hidden';
		};
	},
	toggle						: function() {
		if (this.overlay.getStyle('display') == 'block') {
			this.hide();
		} else {
			this.show();
		};
	},
	get							: function(prop) {
		var property = this.content.get(prop);
		return (property && property.get) ? property.get.apply(this.content, Array.slice(arguments, 1)) : this.content.getProperty(prop);
	},
	set							: function(prop, value) {
		switch ($type(prop)) {
			case 'object':
				for (var p in prop) this.content.set(p, prop[p]);
				break;
			case 'string':
				var property = this.content.get(prop);
				(property && property.set) ? property.set.apply(this.content, Array.slice(arguments, 1)) : this.content.setProperty(prop, value);
				break;
		};
		return this;
	},
	post						: function() {
		var req = new Request.HTML({
			data				: this.content.getElement('form'),
			method				: 'post',
			onComplete			: function() {
				this.hide();
			}.bind(this),
			url					: this.content.getElement('form').get('action')
		}).post(this.content.getElement('form'));
	},
	request						: function(url, size) {
		var myRequest = new Request.HTML({ cache: true, method: 'get', onSuccess: function(responseTree, responseElements, responseHTML, responseJavaScript) {
			this.set('html', responseHTML).show(size.height, size.width);
		}.bind(this), url: url }).get();
		this.set('html', null).show(size.height, size.width);
	},
	submitListener				: function() {
		/*this.submitBehaviour = new Behaviour({
			'.lightbox-content input[type=submit]'	: function(element) {
				element.addEvent('click', function(event) {
					new Event(event).stop();
					this.post();
				}.bind(this));
			}.bind(this),
			'.lightbox-content button[type=submit]'	: function(element) {
				element.addEvent('click', function(event) {
					new Event(event).stop();
					this.post();
				}.bind(this));
			}.bind(this)
		});*/
	},
	empty						: function() {
		$A(this.content.childNodes).each(function(node) {
			Browser.freeMem(node);
			Element.empty(node);
			Element.dispose(node);
		}, this.content);
		return this;
	},
	destroy						: function() {
		for (var obj in this) {
			switch($type(this[obj])) {
				case 'element':
					Browser.freeMem(this[obj].empty().dispose());
					break;
			};
		};
		return null;
	}
});

/*
Script: Localisation
	Adds in a Localisation method to our JavaScript
*/
(function() {
	$l10n = function(reference, args) {
		if ($type(l10n) == 'undefined') {
			return;
		};
		// Loop through the value to get the object pointer
		var pointer = 'l10n';
		reference.toString().split(':').each(function(item) {
			pointer += '[\'' + item + '\']';
		});
		var string = eval(pointer);
		
		if (args) {
			if ($type(args) != 'object') {
				// throw error
				// the 2nd argument must be a keyed array (not a string) for $.l10n(' + string + ', ...)
			} else {
				for (var key in args) {
					var regex = new RegExp('\{' + key + '\}', "g");
					string = string.replace(regex, args[key]);
				};
			};
		};
		return string;
	}
})();

/*
Script: Progress Spinner
	Adds a semi-transparent overlay to an element, and adds a progress spinner
*/

/*
Script: Request
	Extends the Request class to give us AJAX caching.
*/

/*
Script: String.Extras
	Extends the String native object to include methods useful in managing various kinds of strings (query strings, urls, html, etc).
*/
String.implement({
	cleanQueryString			: function(method) {
		return this.split('&').filter(method || function(set) {
			return $chk(set.split("=")[1]);
		}).join("&");
	},
	parseQuery					: function(encodeKeys, encodeValues) {
		encodeKeys = $pick(encodeKeys, true);
		encodeValues = $pick(encodeValues, true);
		var vars = this.split(/[&;]/);
		var result = {};
		if (vars.length) {
			vars.each(function(val) {
				var keys = val.split('=');
				if (keys.length && keys.length == 2) {
					result[(encodeKeys) ? encodeURIComponent(keys[0]) : keys[0]] = (encodeValues) ? encodeURIComponent(keys[1]) : keys[1];
				};
			});
		};
		return results;
	},
	printf						: function(string) {
		// This is just a holder method
	},
	stripTags					: function() {
		return this.replace(/<\/?[^>]+>/gi, '');
	},
	tidy						: function() {
		var txt = this.toString();
		$each({
			'[\xa0\u2002\u2003\u2009]'		: ' ',
			'\xb7'							: '*',
			'[\u2018\u2019]'				: "'",
			'[\u201c\u201d]'				: '"',
			'\u2026'						: '...',
			'\u2013'						: '-',
			'\u2014'						: '--',
			'\uFFFD'						: '&raquo;'
		}, function(value, key) {
			txt = txt.replace(new RegExp(key, 'g'), value);
		});
		return txt;
	}
});

/*
Script: Validate
	
*/
var Validate = new Native({
	name						: 'Validate',
	initialize					: function(element) {
		this.element = element;
		this.setupListeners();
	}
});
Validate.implement({
	element						: null,
	validated					: $H(),
	reset						: function(elements) {
		elements.each(function(element) {
			element.removeClass('input-error');
		});
	},
	setupListeners				: function() {
		var elements = this.element.getElements('input');
		this.element.getElements('textarea').each(function(textarea) {
			elements.push(textarea);
		});
		/* Blur Listener */
		elements.each(function(elem) {
			var rel = elem.getAttribute('rel');
			if (rel && rel.indexOf('validate') > -1) {
				elem.addEvent('blur', function(event) {
					this.validate(elem, true);
				}.bind(this));
			};
		}.bind(this));
		/* Submit Listener */
		this.element.addEvent('submit', function(event) {
			if (window['validate'] != 'false') {
			
				//$(this.element.get('id') + '-error').addClass('hide');
				var elements = this.element.getElements('input');
				this.element.getElements('textarea').each(function(textarea) {
					elements.push(textarea);
				});
				this.element.getElements('select').each(function(select) {
					elements.push(select);
				});
				this.reset(elements);
				elements.each(function(elem) {
					var rel = elem.get('rel');
					if (rel && rel.indexOf('validate') > -1) {
						if (this.validate(elem)) {
							this.validated[elem.get('name')] = 'passed';
						} else {
							this.validated[elem.get('name')] = 'failed';
						};
					};
				}.bind(this));
				// Checks if there are any errors on the page.
				if (this.validated.keyOf('failed')) {
					new Event(event).stop();
					var required = this.validated.filter(function(value, key) {
						return value == false;
					});
					var count = 0;
					var elements = {};
					required.each(function(value, key) {
						count++;
						elements['field-' + count] = $l10n('form:' + key);
					});
					if (count > 4) {
						elements['num'] = count - 3;
					};
					var pointer = '';
					switch (count) {
						case 1:
							pointer = 'form:one-field-required';
							break;
						case 2:
							pointer = 'form:two-fields-required';
							break;
						case 3:
							pointer = 'form:three-fields-required';
							break;
						case 4:
							pointer = 'form:four-fields-required';
							break;
						default:
							pointer = 'form:lots-fields-required';
							break;
					};
					// Throw the errors up inside a div
						// this.error($l10n('form:fill-in-all-required'), $l10n(pointer, elements));
					this.error('Please fill out all the required fields.');
					$(document.body).scrollTo(this.element.getPosition().x, this.element.getPosition().y);
					//$(document.body).scrollTo($(this.element.get('id') + '-error').getPosition().x, $(this.element.get('id') + '-error').getPosition().y);
					//$(document.body).scrollTo(0, 0);
				} else {
					if (this.element.get('rel') == 'ajax-form') {
						new Event(event).stop();
						if (!$(this.element.get('id') + '_is_ajax')) {
							var isAjax = new Element('input', {
								'id'		: this.element.get('id') + '_is_ajax',
								'name'		: 'is_ajax',
								'type'		: 'hidden',
								'value'		: 'true'
							});
							isAjax.injectInside(this.element);
						};
						// Hides the Post button
						this.element.getElement('button[type=submit]').setStyle('visibility', 'hidden');
						//$(this.element.get('id') + '-posting').removeClass('hide');
						this.element.set('send', {
							method			: 'post',
							onSuccess		: function(responseText, responseXML) {
								this.empty();
								// Hides the Post button
								this.element.getElement('button[type=submit]').setStyle('visibility', 'visible');
								//$(this.element.get('id') + '-posting').addClass('hide');
								this.showHTML(responseXML);
							}.bind(this)
						});
						this.element.send();
					};
				};
				
			};
		}.bind(this));
	},
	empty						: function() {
		var elements = this.element.getElements('input[type=text]');
		this.element.getElements('textarea').each(function(textarea) {
			elements.push(textarea);
		});
		elements.each(function(elem) {
			elem.value = '';
		});
	},
	error						: function(title, content) {
		if ($(this.element.get('id') + '-error')) {
			$(this.element.get('id') + '-error').empty();
			var titleElem = new Element('h5', {
				'html'				: title
			}).injectInside($(this.element.get('id') + '-error'));
			var contentElem = new Element('div', {
				'html'				: content
			}).injectInside($(this.element.get('id') + '-error'));
			$(this.element.get('id') + '-error').removeClass('hide');
		};
	},
	parse						: function(string) {
		var regex = new RegExp(/validate\[(.*)\]/i);
		var validateMethods = string.match(regex);
		var methods = validateMethods[1].split(',');
		return methods;
	},
	pass						: function(element) {
		switch (element.get('type')) {
			case 'checkbox':
				if (element.getParent('.cq-check-buttons')) {
					element.getParent('.cq-check-buttons').removeClass('input-error');
				};
			break;
		
			case 'radio':
				if (element.getParent('.radio-buttons')) {
					element.getParent('.radio-buttons').removeClass('input-error');
				};
			break;
		
			default:
				element.removeClass('input-error');
			break;
		}
	},
	fail						: function(element) {
		switch (element.get('type')) {
			case 'checkbox':
				if (element.getParent('.cq-check-buttons')) {
					element.getParent('.cq-check-buttons').addClass('input-error');
				};
			break;
		
			case 'radio':
				if (element.getParent('.radio-buttons')) {
					element.getParent('.radio-buttons').addClass('input-error');
				};
			break;
			
			default:
				element.addClass('input-error');
			break;
		}
	},
	showHTML					: function(response) {
		// TODO: Move over to the new JSON stuff ... similar to the XML stuff :-P
		if (response.getElementsByTagName("errors")[0].firstChild != null) {
			
		} else {
			var message = response.getElementsByTagName("message")[0].firstChild.nodeValue;
			var return_html = response.getElementsByTagName("return_html")[0].firstChild.nodeValue;
			
			if (return_html.substr(0, 4) == "<li>" && return_html.substr(return_html.length - 5, return_html.length) == "</li>") {
				return_html = return_html.substring(4, return_html.length - 5);
			};
			
			var notice = $('' + this.element.get('id') + '-notice');
			notice.set('html', message);
			notice.removeClass('hide');
			
			var hideFunction = function() {
				var myFx = new Fx.Slide('' + this.element.get('id') + '-notice').slideOut();
			}.bind(this);
			var myTimer = hideFunction.delay(2000);
			
			var target_id = response.getElementsByTagName("return_html")[0].getAttribute("target_id");
			var container = $(target_id);
			
			var listElement = new Element('li', {
				'styles'		: {
					'display'	: 'none'
				}
			}).set('html', return_html).inject(container, SNM.Injections[target_id]);
			var listSlide = new Fx.Slide(listElement);
			listSlide.hide();
			listElement.setStyle('display', 'block');
			listSlide.slideIn();
			//. shouldn't be called willy-nilly ... very powerful!!!!!!!
			// SNM.domready();
		};
		// END TODO
	},
	validate					: function(element, live) {
		var validate = this.parse(element.get('rel'));
		var validated = $H();
		validate.each(function(method) {
			method = method.trim();
			if (SNM.Validation[method]) {
				if (SNM.Validation[method](element)) {
					validated[element.get('name')] = (validated[element.get('name')] == 'failed') ? 'failed' : 'passed';
				} else {
					validated[element.get('name')] = 'failed';
				};
			};
		});
		// Checks if there are any errors on the page.
		if (validated[element.get('name')] == 'failed') {
			this.fail(element);
			return false;
		} else {
			this.pass(element);
			return true;
		};
	}
});
/* @end */


