/**
Depends on the Mootools v1.11 library
**/

/**
A slider that does not change width. when 'hidden' it will look blank.
Also has two additional modes reverseHorizontal and reverseVertical. In these modesDifferent from Fx.Slide
**/
Fx.ReverseSlide = Fx.Slide.extend({

	initialize: function(el, options){
		this.sign = 1;
		if(options['mode']!=null){
			if(options['mode']=='reverseHorizontal'){
				options['mode']='horizontal';
				this.sign = -1;
			}
			if(options['mode']=='reverseVertical'){
				options['mode']='vertical';
				this.sign = -1;
			}
		}
		this.parent(el, options);
		if(this.options['mode']=='vertical'){
			this.margin = 'margin-top';
			this.reverseMargin = 'margin-bottom';
			this.elementSize = this.element.getSize().size.y;
		}
		else{
			this.margin = 'margin-left';
			this.reverseMargin = 'margin-right';
			this.elementSize = this.element.getSize().size.x;
		}
	},


	slideOut: function(mode){
		return this.start([0,0], [this.sign*this.elementSize, 0]);
	},
	slideIn: function(mode){
		return this.start([this.sign*this.elementSize, 0], [0,0]);
	},
	hide: function(mode){
		this[mode || this.options.mode]();
		this.open = false;
		return this.set([this.sign*this.elementSize, 0]);
	},


	toggle: function(mode){
		if(this.element.getStyle("margin-right").toInt()==0){
			return this.slideOut(mode);
		}
		return this.slideIn(mode);
	},


	increase: function(){
		try{
			this.element.setStyle(this.margin,        -this.now[0]);
			this.element.setStyle(this.reverseMargin,  this.now[0]);
		}
		catch(err){//some times the animation will start before the elements are fully defined. Just ignore these errors
			//alert("desc:"+err.description + "\nthis.element:"+this.element);
		}
	}

});

/**
A slider that keeps a div inline
**/
Fx.BetterSlide = Fx.Slide.extend({
	initialize: function(el, options){
		this.parent(el, options);
		if($defined($('commentStretchDiv'))){
			this.wrapper.setStyle('float', 'none');	
		}
		else{
			this.wrapper.setStyle('float', 'left');
		}
		
	}
});

var Tools = { };

/**
A module for scrolling through pages
**/
Tools.PageScroll = new Class({

	initialize: function(element, options) {
		this.options = {
			mode: 'horizontal',
			wait: true,
			cache: true,
			initPage: 0
		};
		this.cache = { };
		this.page = this.options.initPage;
		this.options = Object.extend(this.options, options || {});
		this.window = $(element);
		this.window.setStyle('overflow', 'hidden');
		this.windowContents = this.window.getChildren();

		this.container = new Element('div');
		this.container.injectInside(this.window);

		this.blankDiv = new Element('div');
		this.blankDiv.setStyle('float', 'left');
		this.blankDiv.setStyle('display', 'block');
		if (this.options.mode == 'vertical') {
			this.container.setStyle('height', this.window.getStyle('height').toInt() * 2);
			this.blankDiv.setStyle('height', this.window.getStyle('height'));
		} else {
			this.container.setStyle('width', this.window.getStyle('width').toInt() * 2);
			this.blankDiv.setStyle('width', this.window.getStyle('width'));
		}
		this.contentDiv = this.blankDiv.clone();
        this.currentDiv = this.contentDiv;
		this.contentDiv.injectInside(this.container);
		this.contentDiv.adopt(this.windowContents);
		if (this.options.cache) {
			this.cache[''+this.page] = this.windowContents;
		}

		this.slider = new Fx.BetterSlide(this.contentDiv, this.options);
		this.slider.addEvent('onStart', this.slideOnStart.bind(this, this.slider));
		this.slider.addEvent('onComplete', this.slideOnComplete.bind(this));
		this.slider.show();

		this.deleteQueue = new Array();
		this.slideQueue = new Array();
		this.sliding = false;
	},

	toPage: function(page, options) {
		if (page > this.page) {
			this.slideRight(page, options);
		} else if (page < this.page) {
			this.slideLeft(page, options);
		}
	},

	prev: function(options) {
		this.toPage(this.page - 1, options);
	},

	next: function(options) {
		this.toPage(this.page + 1, options);
	},

	/**
	* options:
	*	innerHTML - for non ajax requests
	*	ajaxUrl - for ajax requests
	*	element - element or element id to include
	*
	*	also, all the options for ajax requests
	**/
	slideLeft: function (page, options) {
		if (this.page != page) {
			if (!(this.options.wait && this.waiting)) {
				this.waiting = true;
				var useCache = this.options.cache && $defined(this.cache[''+page]);
				this.newDiv = this.blankDiv.clone();
                this.currentDiv = this.newDiv;
                if (useCache) {
					this.newDiv.adopt(this.cache[''+page]);
				}
				this.newDiv.injectTop(this.container);
				if (this.sliding) {
					this.currentSlider.options.duration = this.options.duration / (this.slideQueue.length + 1);
					this.currentSlider.options.transition = Fx.Transitions.linear;
				}
				this.deleteQueue.push(this.newDiv);
				this.slider = new Fx.BetterSlide(this.newDiv, this.options);
				this.slider.addEvent('onStart', this.slideOnStart.bind(this, this.slider));
				this.slider.addEvent('onComplete', this.slideOnComplete.bind(this));
				this.slider.hide();
				if ($defined(options)) {
					if (!useCache && $defined(options.ajaxUrl)) {
						var newOptions = {
									method: 'get',
									update: this.newDiv
								  };
						newOptions = Object.extend(newOptions, options || {});
						var ajax = new Ajax(options.ajaxUrl, newOptions)
						ajax.addEvent('onComplete', function() {
							this.leftLoaded(page);
							if (this.options.cache) {
								this.cache[''+page] = this.newDiv.getChildren();
							}
						}.bind(this));
						ajax.request();
					} else {
						if ($defined(options.innerHTML)) {
							this.newDiv.innerHTML = options.innerHTML;
						} else if ($defined(options.element)) {
							if ($type(options.element) == 'string') {
								options.element = $(options.element);
							}
							this.newDiv.adopt(options.element);
						}
						if ($defined(options.onComplete)) {
							options.onComplete();
						}
						this.leftLoaded(page);
					}
				} else {
					this.leftLoaded(page);
				}
				this.page = page;
			}
		}
	},

	slideRight: function (page, options) {
		if (this.page != page) {
			if (!(this.options.wait && this.waiting)) {
				this.waiting = true;
				var useCache = this.options.cache && $defined(this.cache[''+page]);
				this.newDiv = this.blankDiv.clone();
                this.currentDiv = this.newDiv;
                if (useCache) {
					this.newDiv.adopt(this.cache[''+page]);
				}
				this.newDiv.injectInside(this.container);
				var tempSlider = new Fx.BetterSlide(this.newDiv, this.options);
				if (this.sliding) {
					tempSlider.element.setStyle('display', 'none');
					this.currentSlider.options.duration = this.options.duration / (this.slideQueue.length + 1);
					this.currentSlider.options.transition = Fx.Transitions.linear;
				}
				this.deleteQueue.push(this.newDiv);
				if ($defined(options)) {
					if (!useCache && $defined(options.ajaxUrl)) {
						var newOptions = {
									method: 'get',
									update: this.newDiv
								  };
						newOptions = Object.extend(newOptions, options || {});
						var ajax = new Ajax(options.ajaxUrl, newOptions)
						ajax.addEvent('onComplete', function() {
							this.rightLoaded(page, tempSlider);
							if (this.options.cache) {
								this.cache[''+page] = this.newDiv.getChildren();
							}
						}.bind(this));
						ajax.request();
					} else {
						if ($defined(options.innerHTML)) {
							this.newDiv.innerHTML = options.innerHTML;
						} else if ($defined(options.element)) {
							if ($type(options.element) == 'string') {
								options.element = $(options.element);
							}
							this.newDiv.empty();
							this.newDiv.adopt(options.element);
						}
						if ($defined(options.onComplete)) {
							options.onComplete();
						}
						this.rightLoaded(page, tempSlider);
					}
				} else {
					this.rightLoaded(page, tempSlider);
				}
			}
		}
	},

	leftLoaded: function(page) {
		if (this.sliding) {
			this.slider.element.setStyle('display', 'none');
			this.slideQueue.push(
				function(slider) {
					slider.options.duration = this.options.duration / (this.slideQueue.length + 1);
					if (this.slideQueue.length > 0) {
						slider.options.transition = Fx.Transitions.linear;
					}
					slider.element.setStyle('display', this.blankDiv.getStyle('display'));
					slider.slideIn();
				}.bind(this, this.slider)
			);
		} else {
			this.slider.slideIn();
		}
	},

	rightLoaded: function(page, nextSlider) {
		if (this.sliding) {
			this.slideQueue.push(
				function(slider, nextSlider) {
					nextSlider.element.setStyle('display', this.blankDiv.getStyle('display'));
					nextSlider.show();
					slider.options.duration = this.options.duration / (this.slideQueue.length + 1);
					if (this.slideQueue.length > 0) {
						slider.options.transition = Fx.Transitions.linear;
					}
					slider.slideOut();
				}.bind(this, [this.slider, nextSlider])
			);
		} else {
			nextSlider.element.setStyle('display', this.blankDiv.getStyle('display'));
			nextSlider.show();
			this.slider.slideOut();
		}
		this.slider = nextSlider;
		this.slider.addEvent('onStart', this.slideOnStart.bind(this, nextSlider));
		this.slider.addEvent('onComplete', this.slideOnComplete.bind(this));
		this.slider.show();
		this.page = page;
	},

	slideOnStart: function(slider) {
		this.currentSlider = slider;
		this.sliding = true;
	},

	slideOnComplete: function() {
		this.contentDiv.getParent().remove();
		this.contentDiv = this.deleteQueue.shift();
		if (this.slideQueue.length > 0) {
			var slide = this.slideQueue.shift();
			slide();
		} else {
			this.sliding = false;
			this.waiting = false;
		}
		
		if($defined($('commentStretchDiv'))){
			reInitGuestbook();
		}
	},

    addToCurrentDiv: function(element)
    {
        var messagesDiv = this.currentDiv.getElementById('commentStretchDiv');
        var children = messagesDiv.getChildren();
        element.injectTop(messagesDiv);
        var children2 = messagesDiv.getChildren();
    }
});

/**
generic tabswitching class
 NOTE that we are using tabproperty "tabtitle"
 instead of title beacuse if we use "title" attribute
 of <LI> tag then the title string will showup
 on mouseover. 
**/
Tools.Tabs = new Class({
	initialize: function(tabs, panels, options) {
		this.options = Object.extend({
			tabElements:		'ul.mootabs_title li',
			panelElements:		'.mootabs_panel',
			tabProperty:		'tabtitle',
			mouseOverClass:		'over',
			activeClass:		'active',
			activateOnLoad:		'first',
			ajaxUrl: 			'#',
			ajaxOptions: 		{method:'get'},
			ajaxLoadingText: 	'Loading...',
			ajaxOnComplete:		'',
			ajaxOnFailure:		'',
			onComplete:			'',
			useCaching:			true
		}, options || {});

		this.tabcontainer = $(tabs);
		this.tabcontainerid = tabs;

		this.panelcontainer = $(panels);
		this.panelcontainerid = panels;

		this.tabs = $$('#' + this.tabcontainerid + ' ' + this.options.tabElements);
		this.panels = $$('#' + this.panelcontainerid + ' ' + this.options.panelElements);

		this.panelCached = { };

		this.tabs.each(function(item) {
			var itemTitle = item.getProperty(this.options.tabProperty);
			this.panelCached[itemTitle] = false;

			item.addEvent('click', function(){
					item.removeClass(this.options.mouseOverClass);
					this.activate(item);
				}.bind(this)
			);

			item.addEvent('mouseover', function() {
				if(item != this.activeTitle) {
					item.addClass(this.options.mouseOverClass);
				}
			}.bind(this));

			item.addEvent('mouseout', function() {
				if(item != this.activeTitle) {
					item.removeClass(this.options.mouseOverClass);
				}
			}.bind(this));
		}, this);


		if(this.options.activateOnLoad != 'none') {
			if(this.options.activateOnLoad == 'first') {
				this.activate(this.tabs[0]);
			}
			else {
				this.activate(this.options.activateOnLoad);
			}
		}
	},

	activate: function(tab){
		if($type(tab) == 'string') {
			tab = $$('#' + this.tabcontainerid + ' ' + this.options.tabElements).filterByAttribute(this.options.tabProperty, '=', tab)[0];
		}

		if($type(tab) == 'element') {
			var tabTitle = tab.getProperty(this.options.tabProperty);

			this.panels.removeClass(this.options.activeClass);
			this.activePanel = this.panels.filterById(tabTitle)[0];
			this.activePanel.addClass(this.options.activeClass);

			this.tabs.removeClass(this.options.activeClass);
			tab.addClass(this.options.activeClass);

			this.activeTitle = tabTitle;

			var ajaxUrl = this.options.ajaxUrl[tabTitle];

			if($defined(ajaxUrl)) {
				var ajaxOptions = { };
				if ($defined(this.options.ajaxOnComplete[tabTitle])) {
					ajaxOptions['onComplete'] = function() {
						this.options.ajaxOnComplete[tabTitle]();
						this.panelCached[tabTitle] = true;
					}.bind(this);
				} else {
					ajaxOptions['onComplete'] = function() {
						this.panelCached[tabTitle] = true;
					}.bind(this);
				}
				if ($defined(this.options.ajaxOnFailure[tabTitle])) {
					ajaxOptions['onFailure'] = function() {
						this.options.ajaxOnFailure[tabTitle]();
						this.panelCached[tabTitle] = false;
					}.bind(this);
				} else {
					ajaxOptions['onFailure'] = function() {
						this.panelCached[tabTitle] = false;
					}.bind(this);
				}
				if (!(this.options.useCaching && this.panelCached[tabTitle])) {
					this.ajaxRequest(ajaxUrl, ajaxOptions);
				} else {
					if ($defined(ajaxOptions['onComplete'])) {
						ajaxOptions['onComplete']();
					}
				}
			} else {
				this.panelCached[tabTitle] = true;
				if ($defined(this.options.onComplete[tabTitle])) {
					this.options.onComplete[tabTitle]();
				}
			}
		}
	},

	ajaxRequest: function(ajaxUrl, ajaxOptions){
		this.activePanel.setHTML(this.options.ajaxLoadingText);
		var newOptions = Object.extend({update: this.activePanel.getProperty('id')}, ajaxOptions || {});
		var options = Object.extend(this.options.ajaxOptions, newOptions || {});
		var tabRequest = new Ajax(this.getUrlWithParameters(ajaxUrl), options);
		tabRequest.request();
	},

	getUrlWithParameters : function (baseUrl, parameters) {
		var newUrl = baseUrl;
		var queryStr = window.top.location.search.substring(1);
		if (!newUrl.contains("?")) {
			newUrl += "?";
		} else {
			newUrl += "&";
		}
		return newUrl + queryStr;
	}
});

/**
a tool for quickly searching an ordered array
@author jkoberstein
**/
Tools.biniarySearch = function(array, find, comparator, lowIndex, highIndex){
	lowIndex = lowIndex || 0;
	highIndex = highIndex || array.length - 1;
	var midIndex = parseInt((lowIndex+highIndex) / 2);
	var compVal = comparator(array[midIndex], find);

	if(compVal == 0){
		return midIndex;
	}
	if(midIndex==lowIndex){//we reached the end. Return the correct value or the one above it for consistency (if element not in list)
		if(compVal > 0){
			return midIndex-1;
		}
		compVal = comparator(array[highIndex], find);
		if(compVal <= 0){
			return highIndex;
		}
		if(compVal > 0){
			return midIndex;
		}
	}
	if(compVal > 0){
		return Tools.biniarySearch(array, find, comparator, lowIndex, midIndex);
	}
	return Tools.biniarySearch(array, find, comparator, midIndex, highIndex);
};

/**
a tool for creating multiple sortable lists
@author jkoberstein
**/
Tools.MultiSortables = new Class({

	options: {
		handles: false,
		ghost: true,
		ghostClass: 'sortableGhost',
		activeClass: 'sortableGhost',
		activeHoverClass: 'sortableGhost',
		selectedClass: 'sortableGhost',
		selectedHoverClass: 'sortableGhost',
		normalClass: 'sortable',
		normalHoverClass: 'sortable',
		snap: 3,
		onStart: Class.empty, //function(){},
		onComplete: Class.empty, //function(){},
		onDragStart: Class.empty, //function(){},
		onDragComplete: Class.empty, //function(){},
		onDrag: Class.empty, //function(event){},
		onMoveToNewList: Class.empty, //function(newList, selectedEls){},
		defaultConverter: function(el){
			return el.getProperty('sortValue');
		}
	},

	initialize: function(lists, sorters, options){
		sorters = sorters || [];
		this.setOptions(options);
		this.lists = [];
		this.elements = [];
		lists.each(function(list, listIdx){
			var list = this.lists[listIdx] = $(list);
			if($defined(this.lists[listIdx])){
				list.getElements = function(list){ return list.getChildren().filterByTag("li"); }.pass(this.lists[listIdx]);
				list.getElements().each(function(el, i){
					el.setProperty('class', this.options.normalClass);
				}.bind(this));
				if(sorters.length > listIdx && sorters[listIdx]!=null){
					list.isSorted = true;
					list.sorter = sorters[listIdx];
					if(list.getElements().length > 0){
						this.insertSelection(list, 'insideOnTop', list.getElements().sort(list.sorter));
					}
				}
				else{
					this.lists[listIdx].isSorted = false;
				}
			}
		}.bind(this) );

		this.attach();
		if (this.options.initialize) this.options.initialize.call(this);

		this.bound = {};
		this.bound.move = this.move.bindWithEvent(this);
		this.bound.end = this.end.bind(this);
		this.bound.outEnd = this.outEnd.bind(this);

		this.selection = {};
		this.selection.elements = [];

		this.ghost = {};
		this.ghost.elements = [];
		this.ghost.trash = null;

		for(var listNum = 0; listNum < this.lists.length; listNum++){
			var elements = this.lists[listNum].getElements();
			for(var idx = 0; idx < elements.length; idx++){
				var element = elements[idx];
				element.addEvent('mouseover',this.onMouseHover.bindWithEvent(this, element));
				element.addEvent('mouseout' ,this.onMouseLeave.bindWithEvent(this, element));
			}
		}
	},

	onMouseHover: function(event, element){
		if(this.selection.hasMoved){
			return;
		}
		var classType = element.getProperty('class');
		if(classType == this.options.activeClass){
			element.setProperty('class', this.options.activeHoverClass);
		}
		else if(classType == this.options.selectedClass){
			element.setProperty('class', this.options.selectedHoverClass);
		}
		else if(classType == this.options.normalClass){
			element.setProperty('class', this.options.normalHoverClass);
		}
	},

	onMouseLeave: function(event, element){
		var classType = element.getProperty('class');
		if(classType == this.options.activeHoverClass){
			element.setProperty('class', this.options.activeClass);
		}
		else if(classType == this.options.selectedHoverClass){
			element.setProperty('class', this.options.selectedClass);
		}
		else if(classType == this.options.normalHoverClass){
			element.setProperty('class', this.options.normalClass);
		}
	},

	//adds the mouse down event to call the start function on all the elements of all the lists
	attach: function(){
		var bindingIndex = 0;
		this.bindings = [];
		for(var listNum = 0; listNum < this.lists.length; listNum++){
			var elements = this.lists[listNum].getElements();
			for(var idx = 0; idx < elements.length; idx++){
				var element = elements[idx];
				var startFunction = this.start.bindWithEvent(this, [element, listNum]);
				this.bindings[bindingIndex] = {};
				this.bindings[bindingIndex].element = element;
				this.bindings[bindingIndex].startFunction = startFunction;
				element.addEvent('mousedown',startFunction);
				bindingIndex++;
			}
		}
	},

	detach: function(){
		this.bindings.each(function(binding, i){
			binding.element.removeEvent('mousedown',binding.startFunction);
		});
		this.bindings = [];
	},

	start: function(event, el, listNum){
		//set up selection info
		this.selection.elements = this.selection.elements || [];
		this.selection.hasMoved = false;
		this.selection.control = event.control;
		this.selection.shift = event.shift;
		//figure out the ghosts offset from cursor
		var elPos = el.getPosition([this.lists[listNum]]);
		this.selection.offset = {
			x: event.page.x - elPos.x,
			y: event.page.y - elPos.y
		};
		this.selection.clicked = el;
		this.selection.overEl = el;

		//clear the previous selection if they select a different list
		if($defined(this.selection.listNum) && this.selection.listNum!=listNum){
			this.clearSelection();
			this.selection.active = null;
		}

		this.selection.listNum = listNum;
		this.selectMouseDown();

		document.addListener('mousemove', this.bound.move);
		document.addListener('mouseup', this.bound.end);
		document.addListener('mouseout', this.bound.outEnd);
		this.fireEvent('onStart');
		event.stop();
		this.detach();
	},

	moveGhost: function(event){
		if(this.ghost.elements.length == 0 && this.selection.elements.length > 0 ){
			//create the new div to hold the ghost elements
			this.ghost.trash = new Element('div').inject(document.body);
			this.ghost.trash.setStyle('position', 'absolute');
			this.ghost.trash.setStyle('left', 0);
			this.ghost.trash.setStyle('top', 0);

			//copy the selected element to the ghost
			var ghostHeight = 0;
			var passedClicked = false;
			this.selection.elements.each(function(sel, i){
				this.ghost.elements[i] = sel.clone().inject(this.ghost.trash);
				this.ghost.elements[i].setStyle('position', 'absolute');
				this.ghost.elements[i].setStyle('left', 0);
				this.ghost.elements[i].setStyle('top', ghostHeight);
				this.ghost.elements[i].setProperty('class', this.options.ghostClass);
				ghostHeight += this.ghost.elements[i].getCoordinates().height;
				if(this.ghost.elements[i].getProperty('sortValue') == this.selection.clicked.getProperty('sortValue')){
					passedClicked = true;
				}
				else if(!passedClicked){
					this.selection.offset.y += this.ghost.elements[i].getCoordinates().height;
				}
			}.bind(this));
		}
		this.ghost.trash.setStyle('left', event.page.x - this.selection.offset.x);
		this.ghost.trash.setStyle('top' , event.page.y - this.selection.offset.y);
		event.stop();
	},

	move: function(event){
		if(!this.selection.hasMoved){
			this.selectMouseMove(event);
			this.fireEvent('onDragStart');
		}
		this.fireEvent('onDrag',event);
		if(this.options.ghost){
			this.moveGhost(event);
		}

		var now = {x:event.page.x, y:event.page.y};
		var coords = this.selection.overEl.getCoordinates();
		var overList = this.getContainingList(now);
		//if we are in a new list you have to insert the element
		if(overList != this.selection.listNum){
		this.insertSelectionIntoList(overList, now.y);
		}
		//check if we have moved off the element
		if((coords.top > now.y && this.selection.elements[0].getPrevious()!=null) ||
		   (now.y > coords.bottom && this.selection.elements[this.selection.elements.length-1].getNext()!=null) ){
			this.insertSelectionIntoList(overList, now.y);
		}
	},

	selectMouseDown: function(){
		var clicked = this.selection.clicked;
		var listEls = this.lists[this.selection.listNum].getElements();
		if(listEls.length==0){
			return;
		}
		if(!this.selection.shift && !this.selection.control){
			if(!this.isInSelection(clicked)){
				this.clearSelection();
				this.addToSelection(clicked, true);
			}
		}
		else if(this.selection.shift){
			this.selection.active = this.selection.active || listEls[0];
			var first = listEls.indexOf(this.selection.active);
			var last = listEls.indexOf(clicked);
			if(last<first){
				var tmp = last;
				last = first;
				first = tmp;
			}
			this.clearSelection();
			for(var i=first; i<=last; i++){
				this.addToSelection(listEls[i]);
			}
		}
	},

	selectMouseMove: function(event){
		this.selection.hasMoved = true;
		if(this.selection.control){ //  if (event.control)
			if(!this.isInSelection(this.selection.clicked)){
				this.addToSelection(this.selection.clicked, true);
			}
		}
		//remove any hover class
		this.selection.elements.each(function(el){
			this.onMouseLeave(event, el);
		}, this);
		this.sortSelection();
	},

	sortSelection: function(){
		//sort the selected elements to preserve their ordering in the list. (in case they Ctrl click in a non sequential order)
		var selListEls = this.lists[this.selection.listNum].getElements();
		this.selection.elements.sort(function(a, b){
			var indexA = selListEls.indexOf(a);
			var indexB = selListEls.indexOf(b);
			if(indexA == indexB)
				return 0;
			if(indexA > indexB)
				return 1
			return -1;
		}.bind(this));
	},

	selectMouseUp: function(){
		if(this.selection.hasMoved){
			return;
		}
		var clicked = this.selection.clicked;
		if(!this.selection.shift && !this.selection.control){
			if(this.isInSelection(clicked)){
				this.clearSelection();
				this.addToSelection(clicked, true);
			}
		}
		else if(this.selection.control){
			if(this.isInSelection(this.selection.clicked)){
				this.removeFromSelection(this.selection.clicked);
				this.selection.active = this.selection.clicked;
			}
			else{
				this.addToSelection(this.selection.clicked, true);
			}
		}
	},

	addToSelection: function(el, isActive){
		this.selection.elements.push(el);
		if($defined(isActive) && isActive){
			this.selection.active = el;
		}
		if(this.selection.active == el){
			if(this.selection.clicked == el){
				el.setProperty('class', this.options.activeHoverClass);
			}
			else{
				el.setProperty('class', this.options.activeClass);
			}
		}
		else{
			if(this.selection.clicked == el){
				el.setProperty('class', this.options.selectedHoverClass);
			}
			else{
				el.setProperty('class', this.options.selectedClass);
			}
		}
	},

	removeFromSelection: function(el){
		var selEls = this.selection.elements;
		var indexOfEl = selEls.indexOf(el);
		if(indexOfEl >= 0){
			if(this.selection.overEl == el){
				el.setProperty('class', this.options.normalHoverClass);
			}
			else{
				el.setProperty('class', this.options.normalClass);
			}
			selEls.splice(indexOfEl,1);
		}
	},

	clearSelection: function(){
		while(this.selection.elements.length > 0){
			this.removeFromSelection(this.selection.elements[0]);
		}
	},

	isInSelection: function(el){
		return this.selection.elements.indexOf(el) >= 0;
	},

	insertSelectionIntoList: function(listNum, height){
		var list = this.lists[listNum];
		if(list.isSorted){
			this.insertSelectionIntoSortedList(listNum);
		}
		else{
			if(!$defined(height)){
				height = 9999999;
		}
			this.insertSelectionIntoUnsortedList(listNum, height);
		}
		if(this.selection.listNum != listNum){
			this.selection.listNum = listNum;
			this.fireEvent('onMoveToNewList', [list, this.selection.elements]);
		}
	},

	insertSelectionIntoUnsortedList: function(listNum, height){
		var list = this.lists[listNum];

		//subtract the height of any elements above the clicked element
		for(var i=0; i<this.selection.elements.length; i++){
			if(this.selection.elements[i].getProperty('sortValue') == this.selection.clicked.getProperty('sortValue')){
				break;
			}
			height = height - this.selection.elements[i].getCoordinates().height;
		}

		var domEls = list.getElements();
		//is it above
		if(domEls.length == 0 || height < domEls[0].getCoordinates([list]).top){
			return this.insertSelection(list, 'insideOnTop');
		}
		var heightInList = domEls[0].getCoordinates([list]).top;
		//is it in the list
		for(var i=0; i < domEls.length; i++){
			elCoords = domEls[i].getCoordinates([list]);
			if(domEls[i].getProperty('class') == this.options.normalClass){
				heightInList += elCoords.height;
			}
			if(heightInList > height){
				return this.insertSelection(domEls[i], 'before');
			}
		}
		return this.insertSelection(domEls[domEls.length-1], 'after');
	},

	insertSelectionIntoSortedList: function(listNum){
		var list = this.lists[listNum];
		if(this.selection.listNum!=listNum){
			for(var i=0; i<this.selection.elements.length; i++){
				var elements = list.getElements();
				if(elements.length==0){
					this.insertSelection(list, 'insideOnTop',[this.selection.elements[i]]);
				}
				else{
					var index = Tools.biniarySearch(elements, this.selection.elements[i], list.sorter);
					if(index<0){
						this.insertSelection(list, 'insideOnTop',[this.selection.elements[i]]);
					}
					else{
						this.insertSelection(elements[index], 'after',[this.selection.elements[i]]);
					}
				}
			}
		}
	},

	insertSelection: function(el, where, elements){
		elements = elements || this.selection.elements;
		if(elements.length == 0){
			return;
		}
		if(where == 'before'){
			elements[0].injectBefore(el);
		}
		else if(where == 'insideOnTop'){
			elements[0].inject(el, 'top');
		}
		else if(where == 'insideOnBottom'){
			elements[0].inject(el, 'bottom');
		}
		else{
			elements[0].injectAfter(el);
		}
		for(var i=1; i<elements.length; i++){
			elements[i].injectAfter(elements[i-1]);
		}
	},

	getContainingList: function(pos){
		for(var i=0; i<this.lists.length; i++){
			var coords = this.lists[i].getCoordinates();
			if(coords.top < pos.y && pos.y < coords.bottom && coords.left < pos.x && pos.x < coords.right){
				return i;
			}
		}
		return this.selection.listNum;
	},

	serializeAll: function(converter){
		var mapper = function(list){
			return "[" + this.serializeList(list, converter) + "]";
		}.bind(this);
		return this.lists.map(mapper);
	},

	serialize: function(listNum, converter){
		return this.serializeList(this.lists[listNum], converter);
	},

	serializeList: function(list, converter){
		//
		//return this.options.defaultConverter(list.getElements()[0]);
		return list.getElements().map(converter || this.options.defaultConverter);
	},

	outEnd: function(event){
		//drops the selection if user scrolls off the edge of the browser window (in IE) firefox still tracks events fine outside the window)
		if(event.clientX==-1 && event.clientY==-1){
			this.end();
		}
	},

	end: function(){
		this.selectMouseUp();
		this.previous = null;
		document.removeListener('mousemove', this.bound.move);
		document.removeListener('mouseup', this.bound.end);
		document.removeListener('mouseout', this.bound.outEnd);
		if (this.options.ghost){
			if(this.ghost.trash != null){
				this.ghost.trash.remove();
				this.ghost.trash = null;
				this.ghost.elements = [];
			}
		}
		if(this.selection.hasMoved)
			this.fireEvent('onDragComplete');
		this.fireEvent('onComplete');
		this.selection.hasMoved = false;
		this.attach();
	},

	//moveover: moves the selected items to the list number specified by the parameter "destination"
	moveover: function(destination){
		//if the selected items are already in the destination list, do nothing
		if(this.selection.listNum != destination){
			var list = this.lists[destination];
			if(list!=null){
				this.insertSelectionIntoList(destination);
			}
		}
	},

	//moveup: moves each of the selected items up by one position in the list they're in
	moveup: function(){
		this.sortSelection();
		var currentList = this.lists[this.selection.listNum];
		var selectedEls = this.selection.elements;
		//if the list containing the selection is supposed to stay sorted, don't do anything
		if(!currentList.isSorted){
			//loop through each selected element and move its position up by 1
			for(var i=0; i<selectedEls.length; i++){
				var el = selectedEls[i];
				var prevEl = el.getPrevious();
				if(prevEl!=null && selectedEls.indexOf(prevEl) == -1){
					this.insertSelection(prevEl, 'before', [el]);
				}
			}
		}
	},

	//movedown: moves each of the selected items down by one position in the list they're in
	movedown: function(){
		this.sortSelection();
		var currentList = this.lists[this.selection.listNum];
		var selectedEls = this.selection.elements;
		//if the list containing the selection is supposed to stay sorted, don't do anything
		if(!currentList.isSorted){
			//loop through each selected element and move its position down by 1
			for(var i=selectedEls.length-1; i >= 0; i--){
				var el = selectedEls[i];
				var nextEl = el.getNext();
				if(nextEl!=null && selectedEls.indexOf(nextEl) == -1){
					this.insertSelection(nextEl, 'after', [el]);
				}
			}
		}
	}

});
Tools.MultiSortables.implement(new Events, new Options);


