/************************************************************************
/
/	ELEMENT EXTENSIONS
/	-------------------------------------------------------
/	Helper functions that allow you to easily extend an element
/	with UI functionality
/	Also some of the jQuery API is implemented and some aliases too
/
/	V1.02 	- 2 customised methods added (KPB), removed Browser.js dependencies
/	V1.03	- Selectors.Pseudo.lt / gt added to return all matched elements before or after a given number
/
/***********************************************************************/
Element.implement({
	/*
	---------------------------------------------------------------------
	CLICK
	---------------------------------------------------------------------
	Adds the jQuery "$$().click()" style event binding
	---------------------------------------------------------------------
	*/
	click: function(fn)	{
		return this.addEvent('click', fn);
	},
	/*
	---------------------------------------------------------------------
	SET LABEL
	---------------------------------------------------------------------
	A label that appears inside a form field until you click on it, removes
	it when forms are submitted too
	---------------------------------------------------------------------
	*/
	setLabel: function(attr)	{
		var attr = attr || 'alt';
		var label = this.getProperty(attr);
		if($defined(label))	{
			this.store('abacus:innerLabel', label);
			this.addEvents({
				'focus': function()	{
					if(this.get('value').clean() == label)
						this.set('value', '').removeClass('helpOn');
				},
				'blur': function()	{
					if(this.get('value').clean() == '' || this.get('value').clean() == label)
						this.set('value', label).addClass('helpOn');
				},
				'click': function()	{
				}
			}).fireEvent('blur');
			var element = this;
			$$('FORM').addEvent('submit', function()    {
				if(element.get('value').clean() == label)
					element.set('value', '').removeClass('helpOn');
			});
		}
		return this.removeProperty(attr);
	},
	/*
	---------------------------------------------------------------------
	MAKE BUTTON
	---------------------------------------------------------------------
	Adds mouseenter/mouseleave events to an element so it can have a
	hover state cross-browser, preloads hover states
	---------------------------------------------------------------------
	*/
	makeButton: function()	{
		this.addEvents({
			'mouseover': function()	{
				this.setProperty('src', this.getProperty('src').replace(".over", "").replace(/\.(...)$/, ".over.$1"));
			},
			'mouseout': function()	{
				this.setProperty('src', this.getProperty('src').replace(/\.over\.(...)$/, ".$1"));
			},
			'click': function() {
				this.fireEvent('mouseout');
			}
		}).fireEvent('mouseout');
		new Image().src = this.getProperty('src').replace(/\.(...)$/, ".over.$1");
		return this;
	},
	/*
	---------------------------------------------------------------------
	MAKE TABS
	---------------------------------------------------------------------
	Turns the elements into tabs to show and hide content with
	---------------------------------------------------------------------
	*/
	makeTabs: function(selector)	{
		var tabs = this.getChildren();
		var contents = this.getAllNext();
		tabs.each(function(tab, i)	{
			tab.addEvent('click', function()	{
				tabs.removeClass('selected');
				contents.removeClass('selected');
				this.addClass('selected').blur();
				contents[i].addClass('selected');
				return false;
			});
		});
		if($defined(selector))	
			this.getElement(selector).fireEvent('click');
		return this;
	},
	/*
	---------------------------------------------------------------------
	CHANGE INTO TABS
	---------------------------------------------------------------------
	Changes a series of lists with headings into a tabbed block
	---------------------------------------------------------------------
	*/
	changeIntoTabs: function(selector)	{
		var tabs = this.getElements('.active_block > H2');
		var blocks = this.getElements('.active_block');
		var tabContainer = new Element('DIV').addClass('tab_block').set('html', '<ul></ul>').inject(this, 'top');
		tabs.each(function(tab, i)	{
			new Element('LI').grab(tab).inject(tabContainer.getElement('UL'));
			tab.addEvent('click', function()	{
				blocks.setStyle('display', 'none');
				blocks[i].setStyle('display', '');
				tabs.getParents().each(function(el)	{
					el.removeClass('current');
				});
				this.getParent().addClass('current');
			});
		});
		if($defined(selector))	
			this.getElement(selector).fireEvent('click');
		else
			tabs[0].fireEvent('click');
		return this;
	},
	/*
	---------------------------------------------------------------------
	CHANGE INTO TABS - CUSTOMISED
	---------------------------------------------------------------------
	Changes elements with class="give hover" into a tabbed block
	---------------------------------------------------------------------
	*/
	customChangeIntoTabs: function(selector) {
		var tabs = this.getElements('.active_block > .give');
		var blocks = this.getElements('.active_block');
		var tabContainer = new Element('DIV').addClass('tab_block').set('html', '<ul></ul>').inject(this, 'top');
		tabs.each(function(tab, i)	{
			new Element('LI').grab(tab).inject(tabContainer.getElement('UL'));
			tab.addEvent('click', function()	{
				blocks.setStyle('display', 'none');
				blocks[i].setStyle('display', '');
				tabs.getParents().each(function(el)	{
					el.removeClass('current');
				});
				this.getParent().addClass('current');
			});
		});
		if($defined(selector))	
			this.getElement(selector).fireEvent('click');
		else
			tabs[0].fireEvent('click');
		return this;
	},
	/*
	---------------------------------------------------------------------
	CHECK
	---------------------------------------------------------------------
	Checks a checkbox for you
	---------------------------------------------------------------------
	*/
	check: function()	{
		this.checked = true;
		return this;
	},
	/*
	---------------------------------------------------------------------
	UNCHECK
	---------------------------------------------------------------------
	Unchecks a checkbox for you
	---------------------------------------------------------------------
	*/
	uncheck: function()	{
		this.checked = false;
		return this;
	},
	/*
	---------------------------------------------------------------------
	MAKE ALL CLICKABLE
	---------------------------------------------------------------------
	Hunts down a link within an element and makes the whole thing clickable
	---------------------------------------------------------------------
	*/
	makeAllClickable: function(onClick)	{
		var linkElements = this.getElements('A');
		var href = linkElements[0].getProperty('href').clean();
		this.addEvents({
			'mouseenter': function()	{
				this.addClass('hover');
				if(this.hasClass('first'))
					this.addClass('firstHover');
				if(this.hasClass('last'))
					this.addClass('lastHover');
			},
			'mouseleave': function()	{
				this.removeClass('hover');
				if(this.hasClass('first'))
					this.removeClass('firstHover');
				if(this.hasClass('last'))
					this.removeClass('lastHover');
			},
			'click': function() {
				if (this.hasClass('newWindow')) {
					window.open(href);
				} else {
					$defined(onClick) ? onClick.attempt(href, this) : window.location = href;
				}
			}
		});
		var self = this;
		linkElements.addEvent('click', function() {
			this.blur();
			self.fireEvent('click');
			return false;
		});
		return this.store('abacus:href', href);
	},
	/*
	---------------------------------------------------------------------
	GIVE FOCUS CLASS ON SELECT
	---------------------------------------------------------------------
	When you select a form element in IE it needs to be given a class of
	'focus' so we can use the pseudo CSS selector :FOCUS
	---------------------------------------------------------------------
	*/
	giveFocusClassOnSelect: function()	{
		if(Browser.Engine.trident)	{
			this.addEvents({
				'focus': function()	{
					this.addClass('focus');	
				},
				'blur': function()	{
					this.removeClass('focus');	
				}
			});
		}
		return this;
	},
	/*
	---------------------------------------------------------------------
	SET AS LOADING
	---------------------------------------------------------------------
	Allows you to define an element as loading. A div with a class of
	'loading' will be absolutely positioned above the element.
	---------------------------------------------------------------------
	*/
	setAsLoading: function()	{
		var loadingElement = this.retrieve('abacus:ui:loading');
		if(!$defined(loadingElement))	{
			loadingElement = new Element('DIV');
			this.store('abacus:ui:loading', loadingElement);
		}
		loadingElement.setStyles(this.getCoordinates()).setStyles({
			position: 'absolute',
			opacity: 0.7
		}).addClass('loading').inject(document.id(document.body));
		return this;
	},
	/*
	---------------------------------------------------------------------
	STOP AS LOADING
	---------------------------------------------------------------------
	Destroys the element positioned above it with the 'loading' class
	---------------------------------------------------------------------
	*/
	stopAsLoading: function(nohighlight)	{
		var loadingElement = this.retrieve('abacus:ui:loading');
		loadingElement.dispose();
		return ($defined(nohighlight)) ? this : this.set('tween', {duration: 'long'}).highlight('#FFC');
	},
	/*
	---------------------------------------------------------------------
	MAKE EXPANDABLE
	---------------------------------------------------------------------
	Lets you set a list to have an expanded and collapsed state
	---------------------------------------------------------------------
	*/
	makeExpandable: function()	{
		if(this.get('tag') == 'ul')	{
			// It's a list so add more/less links
			this.getElements('LI').each(function(item, i)	{
				if(i < 5)	{
					item.addClass('always');
				}
				else if(i == 5)	{
					new Element('LI').addClass('more').set('html', 'more&hellip;').inject(this);
				}
			}, this);
			this.getElements('LI.more').addEvent('click', function()	{
				this.set('html', this.getParent().hasClass('collapsed') ? 'less&hellip;' : 'more&hellip;');
				this.getParent().toggleClass('collapsed');
			});
			if(!this.hasClass('start expanded'))	this.addClass('collapsed');
		}
		else	{
			// Not a list so it's a whole section
			var toggle = this.getFirst();
			toggle.addEvent('click', function()	{
				this.getParent().toggleClass('hidden');							  
			});
			if(this.hasClass('start collapsed'))	{
				this.addClass('hidden');
			}
		}
		return this;
	},
	/*
	---------------------------------------------------------------------
	MAKE EXPANDABLE - CUSTOMISED
	---------------------------------------------------------------------
	Change text of clicked element (e.g. "more" --> "less")
	---------------------------------------------------------------------
	*/
	customMakeExpandable: function()	{
		if(this.get('tag') == 'ul')	{
			// It's a list so add more/less links
			this.getElements('LI').each(function(item, i)	{
				if(i < 5)	{
					item.addClass('always');
				}
				else if(i == 5)	{
					new Element('LI').addClass('more').set('html', 'more&hellip;').inject(this);
				}
			}, this);
			this.getElements('LI.more').addEvent('click', function()	{
				this.set('html', this.getParent().hasClass('collapsed') ? 'less&hellip;' : 'more&hellip;');
				this.getParent().toggleClass('collapsed');
			});
			if(!this.hasClass('start expanded'))	this.addClass('collapsed');
		}
		else	{
			// Not a list so it's a whole section
			var toggle = this.getFirst();
			toggle.addEvent('click', function()	{
				this.getParent().toggleClass('hidden');							  
				//change SPAN to show "more"/"less" as appropriate
				var span = this.getElement('span');
				span.toggleClass('moreless').set('html', span.hasClass('moreless') ? 'more' : 'less');
			});
			if(this.hasClass('start collapsed'))	{
				this.addClass('hidden');
			}
		}
		return this;
	},
	/*
	---------------------------------------------------------------------
	HIDE
	---------------------------------------------------------------------
	Sets an element's display: none
	---------------------------------------------------------------------
	*/
	hide: function()	{
		return this.setStyle('display', 'none');
	},
	/*
	---------------------------------------------------------------------
	SHOW
	---------------------------------------------------------------------
	Sets an element's display to a blank so it follows the stylesheet.
	---------------------------------------------------------------------
	*/
	show: function(type)	{
		return this.setStyle('display', $defined(type) ? type : '');
	},
	/*
	---------------------------------------------------------------------
	EMULATE BREAK WORD
	---------------------------------------------------------------------
	Pads out an element's inner HTML with a special character that allows
	break-word in Firefox and Safari / Chrome.
	---------------------------------------------------------------------
	*/
	emulateBreakWord: function()	{
		if(Browser.Engine.gecko || Browser.Engine.webkit)	{
			var html = this.get('html');
			html = html.split('').join(String.fromCharCode(8203));
			return this.set('html', html);
		}
	},
	/*
	---------------------------------------------------------------------
	GIVE HOVER STATE
	---------------------------------------------------------------------
	Gives a hover state in IE6 (.hover) or all browsers if you pass in 'true'
	---------------------------------------------------------------------
	*/
	giveHoverState: function(all)    {
		var all = all || false;
		if(all || (Browser.Engine.trident && Browser.Engine.version == 4))	{
			this.addEvents({
				'mouseenter': function()	{
					this.addClass('hover');
				},
				'mouseleave': function()	{
					this.removeClass('hover');
				}
			});
		}
		return this.fireEvent('mouseleave');
	},
	/*
	---------------------------------------------------------------------
	MAKE VIEW SWITCHER
	---------------------------------------------------------------------
	Adds the view changing functionality 
	---------------------------------------------------------------------
	*/
	makeViewSwitcher: function()    {
		var element = this;
		return this.getElements('.viewChanger .view').addEvent('click', function()	{
			element.getElements('.viewChanger .view').removeClass('selected');
			this.addClass('selected');
			element.getElements('UL').setProperty('class', '').addClass(this.getParent().getProperty('class'));
			this.blur();
			return false;
		});
	},
	/*
	---------------------------------------------------------------------
	PARSE CLASS
	---------------------------------------------------------------------
	Parses the class attribute for passed options
	---------------------------------------------------------------------
	*/
	parseClass: function(name){

		var classname = this.get('class') || '';
		var regex = new RegExp("^.*(\{.*\}).*$", "gi");
		var obj = classname.match(regex) ? JSON.decode(classname.replace(regex, "$1")) : {};

		if (name) return obj[name] || false;
		else return obj;
	},
	
	makePlaceholder: function(colour){
		
		var defaultColour = this.getStyle('color');
		var colour = colour || defaultColour;
		var text = this.parseClass('text');
		
		this.setStyle('color', colour).addEvents({
			'focus': function(){
				if (this.get('value') == '' || this.get('value') == text){
					this.setStyle('color', defaultColour);
					this.set('value', '');
				}
			},
			'blur': function(){
				if (this.get('value') == '' || this.get('value') == text) {
					this.setStyle('color', colour);
					this.set('value', text);
				}
			}
		});
		
		if (this.get('value') == ''){
			this.set('value', text);
		}
	
		var form = this.getParent('form');
		if (form){
			form.addEvent('submit', function(){
				if (this.get('value') == text) this.set('value','');
			}.bind(this));
		}
	}
});

/************************************************************************
/
/	Selectors
/
/***********************************************************************/
Selectors.Pseudo.gt = function(count){
	return $(this).getAllPrevious(this.get('tag')).length >= count;
};
Selectors.Pseudo.lt = function(count){
	return $(this).getAllPrevious(this.get('tag')).length < count - 1;
};

/************************************************************************
/
/	Aliases
/
/***********************************************************************/
Element.alias('match', 'is');

/************************************************************************
/
/	Element Events
/
/***********************************************************************/
(function(){

	Element.Properties.showing = {
		get: function(){
			return this.retrieve('showing') || false;	
		},
		set: function(value){
			return this.store('showing', value);	
		}
	};

	var tracker = function(evt){
		$(this).store('position:current', evt.page);
		(function(evt){
			$(this).store('position:last', evt.page);
		}).delay(100, this, evt);
	};

	var hoveringOver = function(element){
		var position = {
			current: $(element).retrieve('position:current'),
			last: $(element).retrieve('position:last')
		};
		if (position.last){
			var delta = Math.abs(position.last.x - position.current.x) + Math.abs(position.last.y - position.current.y);
			if (delta <= 5) return true;
		}
		return false;
	};

	Element.Events.delayedMouseenter = {
		onAdd: function(){
			var timer = null;
			$(this).addEvents({
				'mouseenter': function(){
					$(this).addEvent('mousemove', tracker);
					timer = (function(){
						if (hoveringOver(this)){
							$(this).fireEvent('delayedMouseenter');
							$(this).set('showing', true);
						}
					}).periodical(150, this);
				},
				'mouseleave': function(){
					$(this).removeEvent('mousemove', tracker);
					$clear(timer);
				}
			});
		}
	};

	Element.Events.delayedMouseleave = {
		onAdd: function(){
			var timer = null;
			$(this).addEvents({
				'mouseleave': function(){
					timer = (function(){
						if ($(this).get('showing')){
							$(this).fireEvent('delayedMouseleave');
							$(this).set('showing', false);
						}
					}).delay(150, this);
				},
				'mouseenter': function(){
					$clear(timer);
				}
			});
		}
	};

})();

Element.Events.outerClick = {
	base : 'click',	
	condition : function(event){
		event.stopPropagation();
		return false;
	},
	onAdd : function(fn){
		this.getDocument().addEvent('click', fn);
	},
	onRemove : function(fn){
		this.getDocument().removeEvent('click', fn);
	}
};
