Sortables = Sortables.extend({
	initialize: function(list, options) {
		this.parent(list, options);
		this.last_state = this.serialize_string();
	},
	moveGhost: function(event){
		var value = event.page.y - this.offset;
		value = value.limit(this.coordinates.top, this.coordinates.bottom);
		this.ghost.setStyle('top', value);
		event.stop();
	},
	serialize_string: function(){
		var serial = '';
		this.list.getChildren().each(function(el, i){
			serial += el.id+',';
		}, this);
		return serial;
	},
	
	serialize: function() {
		var serial = [];
		this.list.getChildren().each(function(el, i){
			serial[serial.length] = el.id;
		}, this);
		return serial;
	},
	
	end: function(){
		this.previous = null;
		document.removeListener('mousemove', this.bound.move);
		document.removeListener('mouseup', this.bound.end);
		if (this.options.ghost){
			document.removeListener('mousemove', this.bound.moveGhost);
			this.fireEvent('onDragComplete', [this.active, this.ghost]);
		}
		
		this.cur_state = this.serialize_string();
		if( this.last_state != this.cur_state && this.last_state != '' ) {
			this.last_state = this.cur_state;
			this.fireEvent('onComplete', this.active);
		}
	}
});

Element.extend({
	toggle: function() {
		if (this.getStyle('display') != 'none') this.setStyle('display','none');
		else this.setStyle('display','');
	},	
	lift: function() { this.toggle(); },
	toggleVis: function() {
		if (this.getStyle('visibility') != 'hidden') this.setStyle('visibility','hidden');
		else this.setStyle('visibility','visible');
	},	
	hide: function() {
		this.setStyle('display', 'none');
	},	
	show: function() {
		this.setStyle('display', '');
	},
	moveToBottom: function() {
		var w = this.clone();
		this.remove();
		$(document.body).adopt(w);
	},
	disable: function(txt)	{
		if( this.toString().indexOf('HTMLInput') > 0 ) {
			this.addClass('grey_button');
			if(txt) this.value = txt;
			this.disabled = true;
			return;
		}
		
		$$('#'+this.id+' select, #'+this.id+' input').setProperty('disabled', true);
		
		if(!window.ie) {
			this.setStyle('position', 'relative');
			if( !$(this.id+'_disable') ) {
				var coords = this.getSize();
				this.adopt(
					new Element('div', {
						'id': this.id+'_disable',
						'styles': {
							'background-color': '#fff',
							'width': coords.size.x,
							'height': coords.size.y,
							'top': '0',
							'left': '0',
							'position': 'absolute',
							'opacity': 0.7
						}
					})
				);
			}
		} else {
			this.addClass('grey_out');
		}
	},
	enable: function(txt) {
		
		if( this.toString().indexOf('HTMLInput') > 0 ) {
			this.disabled = false;
			this.removeClass('grey_button');
			if(txt) this.value = txt;
			return;
		}
		
		$$('#'+this.id+' select, #'+this.id+' input').setProperty('disabled', false);
		
		if(!window.ie) {
			this.setStyle('position', '');
			if( $(this.id+'_disable') ) {
				$(this.id+'_disable').remove();
			}
		} else {
			this.removeClass('grey_out');
		}
	}
});

Fx.Slide.implement({
	getStatus: function() {
		return this.wrapper.offsetHeight == 0 || this.wrapper.offsetWidth == 0;
	}
});

/**
 * Class for adding function calls to a custom event
 *  - currently only supports one event type
 */ 
var EventCenterClass = new Class({
	options: {
		onLoad: Class.empty
	},
	initialize: function() {
		this.types = ['global'];
		this.calls = [];
		this.calls[0] = [];
	},
	
	add: function(fn, type) {
		if( !type ) { type = 'global'; }
		
		var index = this.types.indexOf(type)
		if( index < 0 ) {
			index = this.types.length;
			this.types[index] = type;
			this.calls[index] = [];
		}
		
		if( $type(fn) == 'function' ) {	
			this.calls[index][ this.calls[index].length ] = fn;
		}
	},
	
	fire: function(type) {
		if( !type ) { type = 'global'; }
		
		var index = this.types.indexOf(type);
		if( index >= 0 ) {
			for( var i = 0; i < this.calls[index].length; i++ ) { 
				var fn = this.calls[index][i];
				this.calls[index][i] = false;
				if( $type(fn) == 'function' ) fn();
			}
		}
	},
	
	fireAll: function() {
		for( var i = 0; i < this.calls.length; i++ ) { 
			for( var j = 0; j < this.calls[i].length; j++ ) { 
				var fn = this.calls[i][j];
				this.calls[i][j] = false;
				if( $type(fn) == 'function' ) fn();
			}
		}
	}
});
var EventCenter = new EventCenterClass(); // to call statically

/*
Script: Accordion.js
	Contains <Accordion>

License:
	MIT-style license.
*/

/*
Class: Accordion
	The Accordion class creates a group of elements that are toggled when their handles are clicked. When one elements toggles in, the others toggles back.
	Inherits methods, properties, options and events from <Fx.Elements>.
	
Note:
	The Accordion requires an XHTML doctype.

Arguments:
	togglers - required, a collection of elements, the elements handlers that will be clickable.
	elements - required, a collection of elements the transitions will be applied to.
	options - optional, see options below, and <Fx.Base> options and events.

Options:
	show - integer, the Index of the element to show at start.
	display - integer, the Index of the element to show at start (with a transition). defaults to 0.
	fixedHeight - integer, if you want the elements to have a fixed height. defaults to false.
	fixedWidth - integer, if you want the elements to have a fixed width. defaults to false.
	height - boolean, will add a height transition to the accordion if true. defaults to true.
	opacity - boolean, will add an opacity transition to the accordion if true. defaults to true.
	width - boolean, will add a width transition to the accordion if true. defaults to false, css mastery is required to make this work!
	alwaysHide - boolean, will allow to hide all elements if true, instead of always keeping one element shown. defaults to false.
	
Events:
	onActive - function to execute when an element starts to show
	onBackground - function to execute when an element starts to hide
*/

var Accordion = Fx.Elements.extend({

	options: {
		onActive: Class.empty,
		onBackground: Class.empty,
		display: 0,
		show: false,
		height: true,
		width: false,
		opacity: true,
		fixedHeight: false,
		fixedWidth: false,
		overflow: 'hidden',
		wait: false,
		alwaysHide: false
	},

	initialize: function(){
		var options, togglers, elements, container;
		$each(arguments, function(argument, i){
			switch($type(argument)){
				case 'object': options = argument; break;
				case 'element': container = $(argument); break;
				default:
					var temp = $$(argument);
					if (!togglers) togglers = temp;
					else elements = temp;
			}
		});
		this.togglers = togglers || [];
		this.elements = elements || [];
		this.container = $(container);
		this.setOptions(options);
		this.previous = -1;
		if (this.options.alwaysHide) this.options.wait = true;
		if ($chk(this.options.show)){
			this.options.display = false;
			this.previous = this.options.show;
		}
		if (this.options.start){
			this.options.display = false;
			this.options.show = false;
		}
		this.effects = {};
		if (this.options.opacity) this.effects.opacity = 'fullOpacity';
		if (this.options.width) this.effects.width = this.options.fixedWidth ? 'fullWidth' : 'offsetWidth';
		if (this.options.height) this.effects.height = this.options.fixedHeight ? 'fullHeight' : 'scrollHeight';
		for (var i = 0, l = this.togglers.length; i < l; i++) this.addSection(this.togglers[i], this.elements[i]);
		this.elements.each(function(el, i){
			if (this.options.show === i){
				this.fireEvent('onActive', [this.togglers[i], el]);
			} else {
				for (var fx in this.effects) el.setStyle(fx, 0);
			}
		}, this);
		this.parent(this.elements);
		if ($chk(this.options.display)) this.display(this.options.display);
	},

	/*
	Property: addSection
		Dynamically adds a new section into the accordion at the specified position.

	Arguments:
		toggler - (dom element) the element that toggles the accordion section open.
		element - (dom element) the element that stretches open when the toggler is clicked.
		pos - (integer) the index where these objects are to be inserted within the accordion.
	*/

	addSection: function(toggler, element, pos){
		toggler = $(toggler);
		element = $(element);
		var test = this.togglers.contains(toggler);
		var len = this.togglers.length;
		this.togglers.include(toggler);
		this.elements.include(element);
		if (len && (!test || pos)){
			pos = $pick(pos, len - 1);
			toggler.injectBefore(this.togglers[pos]);
			element.injectAfter(toggler);
		} else if (this.container && !test){
			toggler.inject(this.container);
			element.inject(this.container);
		}
		var idx = this.togglers.indexOf(toggler);
		toggler.addEvent('click', this.display.bind(this, idx));
		if (this.options.height) element.setStyles({'padding-top': 0, 'border-top': 'none', 'padding-bottom': 0, 'border-bottom': 'none'});
		if (this.options.width) element.setStyles({'padding-left': 0, 'border-left': 'none', 'padding-right': 0, 'border-right': 'none'});
		element.fullOpacity = 1;
		if (this.options.fixedWidth) element.fullWidth = this.options.fixedWidth;
		if (this.options.fixedHeight) element.fullHeight = this.options.fixedHeight;

		element.setStyle('overflow', this.options.overflow);
		if (!test){
			for (var fx in this.effects) element.setStyle(fx, 0);
		}
		return this;
	},

	/*
	Property: display
		Shows a specific section and hides all others. Useful when triggering an accordion from outside.

	Arguments:
		index - integer, the index of the item to show, or the actual element to show.
	*/

	display: function(index){
		index = ($type(index) == 'element') ? this.elements.indexOf(index) : index;
		if ((this.timer && this.options.wait) || (index === this.previous && !this.options.alwaysHide)) return this;
		this.previous = index;
		var obj = {};
		this.elements.each(function(el, i){
			obj[i] = {};
			var hide = (i != index) || (this.options.alwaysHide && (el.offsetHeight > 0));
			this.fireEvent(hide ? 'onBackground' : 'onActive', [this.togglers[i], el]);
			for (var fx in this.effects) obj[i][fx] = hide ? 0 : el[this.effects[fx]];
		}, this);
		return this.start(obj);
	},

	showThisHideOpen: function(index){return this.display(index);}

});

