if (!window.Selectables) var Selectables = { };

Object.extend(Selectables, 
{
	hash: new Hash(),
	
	add: function(id, value)
	{
		this.hash.set(id, value);
	},
	
	toggleAll: function(id)
	{
		var select = this.hash.get(id);
		
		if(select)
		{
			select.toggleAll();
		}
	},
	
	get: function(id)
	{
		return this.hash.get(id);
	}
});

Element.addMethods('TABLE', 
{
	toggleAll: function(element)
	{
		Selectables.toggleAll(element.identify());
		
		return element;
	}
});

var positioning = false;

var SelectNode = Class.create(
{
	initialize: function(number, element, id)
	{
		this.element			= element;
		this.positionElement	= element;
		this.number				= number;
		this.selected			= false;
		this.id					= id;
		this.left				= -1;
		this.right				= -1;
		this.top				= -1;
		this.bottom				= -1;
	}
});

var TreeNode = Class.create(
{
	initialize: function(element, id)
	{
		this.element = element;
		this.childrenElement = element;
		this.children = [];
		this.opener;
		this.id = id;
	},
	
	addChild: function(child)
	{
		this.children.push(child);
		
		if(this.element == null)
		{
			child.element.show();
		}
		else if(!this.element.visible())
		{
			child.element.hide();
		}
		
		if(!child.element.visible())
		{
			child.opener = this;
		}
	},
	
	getDescendants: function()
	{
		var collected = [];
		
		this.findDescendants(collected);
		
		return collected;
	},
	
	findDescendants: function(collected)
	{
		for(var i = 0; i < this.children.length; i++)
		{
			collected.push(this.children[i]);
			
			this.children[i].findDescendants(collected);
		}
	},
	
	findDescendant: function(id)
	{
		if(id == this.id)
		{
			return this;
		}
		
		for(var i = 0; i < this.children.length; i++)
		{
			var node = this.children[i].findDescendant(id);
			
			if(node)
			{
				return node;
			}
		}
	}
});

var Select = Class.create(
{
	initialize: function(element, options)
	{
		this.element			= $(element);
		this.name				= this.element.identify();
		this.selectors			= [];
		this.lastSelection		= null;
		this.lastState			= true;
		this.selected			= null;
		this.position			= null;
		this.positionBegin		= false;
		this.positionStartX;
		this.positionStartY;
		this.positionSource;
		this.positionTarget;
		this.positionType;
		this.positionArrow		= null;
		this.positionCursor		= null;
		this.options = {
			multiple:     		true,
			onDblClick:			null,
			onSelect:			null,
			onPosition: 		null,
			isTree:				false,
			isLarge:			false,
			singleListeners:	[],
			listeners:			[],
			selected:			[],
			expanded:			[]
		};

		Object.extend(this.options, options || { });
		
		// --------
		
		this.element.setStyle({cursor : 'default'});
		
		this.element.observe('mousedown',	this.mouseDown.bindAsEventListener(this));
		this.element.observe('mousemove',	this.mouseMove.bindAsEventListener(this));
		this.element.observe('dblclick',		this.dblClick.bindAsEventListener(this));
		$$('html').first().observe('mouseup',	this.mouseUp.bindAsEventListener(this));
		
		// --------
		
		var descendants = this.element.descendants();
		
		var currentNode = null;
		
		var number = 0;
		
		for(var i = 0; i < descendants.length; i++)
		{

			if(descendants[i].identify().startsWith(this.name + '_'))
			{
				var id = descendants[i].identify().substring(this.name.length + 1);
			
				currentNode = new SelectNode(number, descendants[i], id);
				
				this.selectors.push(currentNode);
				
				number++;
			}
			
			if(descendants[i].hasClassName('position') && currentNode)
			{
				currentNode.positionElement = descendants[i];
			}
		}
		
		// ----------
		
		this.element.insert({after: "<input type='hidden' id='" + this.name + "_selected' name='" + this.name + "_selected' />"});
			
		this.selected = $(this.name + "_selected");
		
		var selectedIDs = this.options.selected;
		
		for(var i = 0; i < selectedIDs.length ; i++)
		{
			var selector = this.getSelector(selectedIDs[i]);
			
			if(selector)
			{
				selector.selected = true;
			}
		}
		
		this.updateSelection();
		
		// ---------
		
		if(this.options.onPosition)
		{
			this.options.multiple = false;
			
			this.positionArrow = $('positionarrow');
			
			if(!this.positionArrow)
			{
				$$('body').first().insert("<img id='positionarrow' src='../images/arrow.gif' style='display:none; z-index:99; position:absolute;' />");
				
				this.positionArrow = $('positionarrow');
			}
			
			this.positionCursor = $('positioncursor');
			
			if(!this.positionCursor)
			{
				$$('body').first().insert("<div id='positioncursor' class='tablehilite' style='font-size:1px; display:none; z-index:99; position:absolute;'></div>");
				
				this.positionCursor = $('positioncursor');
			}
			
			this.element.insert({after: "<input type='hidden' id='" + this.name + "_position' name='" + this.name + "_position' />"});
			
			this.position = $(this.name + "_position");
		}
		
		Selectables.add(this.name, this);
	},
	
	mouseDown: function(event)
	{
		var element = Event.element(event);
		
		if(element.name)
		{
			return;
		}

		var selected = this.getSelector(element);
		
		if(selected)
		{
			if(this.options.multiple && (event.shiftKey || event.ctrlKey || event.altKey))
			{
				this.selectMultiple(event, selected);
			}
			else
			{
				this.selectSingle(event, selected);
			}
			
			if(this.options.onPosition)
			{
				this.startPositioning(event);
			}
		}
		
		Event.stop(event);
	},
	
	mouseMove: function(event)
	{
		if(this.options.onPosition)
		{
			this.movePositioning(event);
			
			Event.stop(event);
		}
	},
	
	mouseUp: function(event)
	{
		if(this.options.onPosition)
		{
			this.endPositioning(event);
		}
	},
	
	dblClick: function(event)
	{
		if(this.options.onDblClick && this.getSelector(Event.element(event)))
		{
			this.options.onDblClick();
		}
	},

	getSelector: function(object)
	{
		if(Object.isElement(object))
		{
			var element = this.getSelectorElement(object);
			
			for(var i = 0; i < this.selectors.length; i++)
			{
				if(this.selectors[i].element == element)
				{
					return this.selectors[i];
				}
			}
		}

		for(var i = 0; i < this.selectors.length; i++)
		{
			if(this.selectors[i].element.identify() == object)
			{
				return this.selectors[i];
			}
			else if(this.selectors[i].id == object)
			{
				return this.selectors[i];
			}
		}
	},
	
	getSelectorElement: function(element)
	{
		if(element.identify().startsWith(this.name + '_'))
		{
			return element;
		}
		
		var ancestors = element.ancestors();
			
		for(var i = 0; i < ancestors.length; i++)
		{
			var item = ancestors[i];
		
			if(item.identify().startsWith(this.name + '_'))
			{
				return item;
			}
		}
	},
	
	selectSingle: function(event, selected)
	{
		for(var i = 0; i < this.selectors.length; i++)
		{
			if(this.selectors[i] == selected)
			{
				this.selectors[i].selected = true;
				
				this.lastSelection = selected;
				
				this.lastState = true;
			}
			else
			{
				this.selectors[i].selected = false;
			}
		}
		
		this.updateSelection();
	},
	
	selectMultiple: function(event, selected)
	{
		if(event.ctrlKey || event.altKey)
		{
			for(var i = 0; i < this.selectors.length; i++)
			{
				if(this.selectors[i] == selected)
				{
					this.selectors[i].selected = !this.selectors[i].selected;
					
					this.lastState = this.selectors[i].selected;
					
					break;
				}
			}
			
			this.lastSelection = selected;
		}
		
		if(event.shiftKey && this.lastSelection)
		{
			var start = this.lastSelection.number;
			
			var end = selected.number;
			
			if(start > end)
			{
				start = selected.number;
				
				end = this.lastSelection.number;
			}
			
			for(var i = 0; i < this.selectors.length; i++)
			{
				if(this.selectors[i].number >= start && this.selectors[i].number <= end)
				{
					this.selectors[i].selected = this.lastState;
				}
			}
		}

		this.updateSelection();
	},
	
	updateSelection: function()
	{
		var ids = [];

		for(var i = 0; i < this.selectors.length; i++)
		{
			if(this.selectors[i] && this.selectors[i].element)
			{
				if(this.selectors[i].selected)
				{
					ids.push(this.selectors[i].id);

					this.selectors[i].element.addClassName('tablehilite');
				}
				else
				{
					this.selectors[i].element.removeClassName('tablehilite');
				}
			}
		}

		this.selected.value = ids.join(",");

		var listeners = this.options.listeners;

		for(var i = 0; i < listeners.length; i++)
		{
			var listener = $(listeners[i]);

			if(listener)
			{
				listener.disabled = (ids.length < 1);
			}
		}

		listeners = this.options.singleListeners;

		for(var i = 0; i < listeners.length; i++)
		{
			listener = $(listeners[i]);

			if(listener)
			{
				listener.disabled = (ids.length != 1);
			}
		}
		
		if(this.options.onSelect)
		{
			this.options.onSelect(ids);
		}
	},
	
	toggleAll: function()
	{
		var state = false;
		
		for(var i = 0; i < this.selectors.length; i++)
		{
			if(!this.selectors[i].selected)
			{
				state = true;
				
				break;
			}
		}
		
		for(var i = 0; i < this.selectors.length; i++)
		{
			this.selectors[i].selected = state;
		}
		
		this.updateSelection();
	},
	
	selectedIDs: function()
	{
		var ids = [];

		for(var i = 0; i < this.selectors.length; i++)
		{
			if(this.selectors[i])
			{
				if(this.selectors[i].selected)
				{
					ids.push(this.selectors[i].id);
				}
			}
		}
		
		return ids;
	},
	
	resetPositioning: function()
	{
		this.positionStartX	= -1;
		this.positionStartY	= -1;
		this.positionBegin	= false;
		this.positionSource	= null;
		this.positionTarget	= null;
		this.positionType	= null;
		
		positioning = false;
	},
	
	updatePositions: function()
	{
		var lastSelector = null;
		
		for(var i = 0; i < this.selectors.length; i++)
		{
			if(this.selectors[i].element.visible())
			{
				var left = 0;
				var top = 0;

				var node = this.selectors[i].positionElement;

				while(node)
				{
					if(node.nodeName == "TR")
					{
						left += node.cells[0].offsetLeft;
						top  += node.cells[0].offsetTop;
					}
					else
					{
						left += node.offsetLeft;
						top  += node.offsetTop;
					}

					node = node.offsetParent;
				}

				var dimensions	= this.selectors[i].positionElement.getDimensions();
	
				this.selectors[i].left		= left;
				this.selectors[i].right		= left + dimensions.width;
				this.selectors[i].top		= top;
				this.selectors[i].bottom	= top + dimensions.height;
				
				if(lastSelector)
				{
					if((this.selectors[i].top - lastSelector.bottom) < 16)
					{
						var average = Math.round((this.selectors[i].top + lastSelector.bottom) / 2);
				
						this.selectors[i].top = average;
						
						lastSelector.bottom = average;
					}
				}
				
				lastSelector = this.selectors[i];
			}
		}
	},
	
	startPositioning: function(event)
	{
		this.resetPositioning();
		
		this.hideCursor();
		
		this.updatePositions();

		this.positionSource = this.getSelector(Event.element(event));
		
		this.positionStartX = Event.pointerX(event);
		
		this.positionStartY = Event.pointerY(event);
		
		positioning = true;
	},
	
	movePositioning: function(event)
	{
		var x = Event.pointerX(event);
	
		var y = Event.pointerY(event);
					
		if(this.positionBegin)
		{
			if(this.positionSource)
			{
				var position = this.getSelector(Event.element(event));
	
				if(position)
				{
					this.positionTarget	= position;

					if(this.options.isTree)
					{
						var topQuarter = position.top + (position.bottom - position.top) / 4;
	
						var bottomQuarter = position.top + (position.bottom - position.top) * 3 / 4;
	
						if(y < topQuarter)
						{
							this.positionType = "before";
						}
						else if(y >= bottomQuarter)
						{
							this.positionType = "after";
						}
						else
						{
							this.positionType = "in";
						}
					}
					else
					{
						var middle = (position.top + position.bottom) / 2;
	
						if(y < middle)
						{
							this.positionType = "before";
						}
						else
						{
							this.positionType = "after";
						}
					}
	
					this.hideCursor();
					
					this.updateCursor();
				}
			}
			else
			{
				this.resetPositioning();
			}
		}
		else
		{
			if(this.positionSource)
			{
				if(Math.abs(x - this.positionStartX) > 1 || Math.abs(y - this.positionStartY) > 4)
				{
					this.positionBegin = true;
				}
			}
		}
	},
	
	endPositioning: function(event)
	{
		this.hideCursor();
		
		if(this.positionTarget && this.positionTarget != this.positionSource)
		{
			this.position.value = this.positionSource.id + " " + this.positionType + " " + this.positionTarget.id;
			
			this.options.onPosition();
		}

		this.resetPositioning();
	},
	
	hideCursor: function()
	{
		this.positionArrow.hide();
		
		this.positionCursor.hide();
		
		for(var i = 0; i < this.selectors.length; i++)
		{	
			if(this.options.isTree)
			{
				this.selectors[i].positionElement.removeClassName('tablehilite');
			}
		}
	},
	
	updateCursor: function()
	{
		var hoffset = 12;
		var voffset = 5;
		var size = 3;

		if(this.positionType == "before")
		{
			this.positionArrow.setStyle(
			{
				left:	this.positionTarget.left - hoffset,
				top:	this.positionTarget.top - voffset
			});
			
			this.positionCursor.setStyle(
			{
				left:	this.positionTarget.left,
				width:	this.positionTarget.right - this.positionTarget.left,
				top:	this.positionTarget.top - 1,
				height:	size
			});
			
			this.positionArrow.show();
			this.positionCursor.show();
		}
		else if(this.positionType == "after")
		{
			this.positionArrow.setStyle(
			{
				left:	this.positionTarget.left - hoffset,
				top:	this.positionTarget.bottom - voffset
			});
			
			this.positionCursor.setStyle(
			{
				left:	this.positionTarget.left,
				width:	this.positionTarget.right - this.positionTarget.left,
				top:	this.positionTarget.bottom - 1,
				height:	size
			});
			
			this.positionArrow.show();
			this.positionCursor.show();
		}
		else if(this.positionType == "in")
		{
			this.positionTarget.positionElement.addClassName('tablehilite');
			
			this.positionArrow.setStyle(
			{
				left:	this.positionTarget.left - hoffset,
				top:	((this.positionTarget.top + this.positionTarget.bottom) / 2) - voffset
			});
			
			this.positionArrow.show();
		}
	}
});

var Table = Class.create(Select, 
{
	initialize: function($super, element, options)
	{	
		$super(element, options);
	}
});

var ToggleTable = Class.create(Select,
{
	initialize: function($super, element, options)
	{
		if(!options)
		{
			options = {};	
		}
		
		if(!options.expandedURL)
		{
			options.expandedURL = '../images/expanded.gif';
		}
		
		if(!options.collapsedURL)
		{
			options.collapsedURL = '../images/collapsed.gif';
		}

		$super(element, options);
		
		// ---------
		
		this.element.observe('click', this.click.bindAsEventListener(this));
		
		// --------
		
		var descendants = this.element.descendants();
				
		for(var i = 0; i < descendants.length; i++)
		{
			if(descendants[i].identify().startsWith('parent_' + this.name + '_'))
			{
				var toggle = $('toggle_' + descendants[i].identify().substring(7));
				
				if(toggle)
				{
					if(descendants[i].visible())
					{
						toggle.src = this.options.expandedURL;
					}
					else
					{
						toggle.src = this.options.collapsedURL;
					}
				}
			}
		}
	},
	
	mouseDown: function($super, event)
	{
		var toggle = Event.element(event);

		if(toggle.identify().startsWith('toggle_' + this.name + '_'))
		{
			return;
		}
		
		$super(event);
	},
	
	click: function(event)
	{
		var toggle = Event.element(event);

		if(toggle.identify().startsWith('toggle_' + this.name + '_'))
		{
			var element = $('parent_' + toggle.identify().substring(7));
			
			if(element)
			{
				element.toggle();
				
				if(element.visible())
				{
					toggle.src = this.options.expandedURL;
					
					if(this.options.onExpand)
					{
						this.options.onExpand();
					}
				}
				else
				{
					toggle.src = this.options.collapsedURL;
					
					if(this.options.onCollapse)
					{
						this.options.onCollapse();
					}
				}
			}
		}
	},
	
	getSelectorElement: function($super, element)
	{
		var result = $super(element);
		
		if(result)
		{
			return result;
		}
		
		if(element.identify().startsWith('parent_' + this.name + '_'))
		{
			return $(element.identify().substring(7));
		}
		
		var ancestors = element.ancestors();
			
		for(var i = 0; i < ancestors.length; i++)
		{
			var item = ancestors[i];
		
			if(item.identify().startsWith('parent_' + this.name + '_'))
			{
				return $(item.identify().substring(7));
			}
		}
	},
	
	updateSelection: function($super)
	{
		$super();
		
		var ids = [];

		for(var i = 0; i < this.selectors.length; i++)
		{
			if(this.selectors[i])
			{
				var element = $('parent_' + this.name + '_' + this.selectors[i].id)
				
				if(element)
				{
					if(this.selectors[i].selected)
					{
						element.addClassName('tablehilite');
					}
					else
					{
						element.removeClassName('tablehilite');
					}
				}
			}
		}
	}
});

var Tree = Class.create(Select,
{
	nodes: new Hash(),
	
	initialize: function($super, element, options)
	{
		if(!options)
		{
			options = {};	
		}
		
		if(!options.expandedURL)
		{
			options.expandedURL = '../images/expanded.gif';
		}
		
		if(!options.collapsedURL)
		{
			options.collapsedURL = '../images/collapsed.gif';
		}
		
		options.isTree = true;
		
		$super(element, options);

		this.root = new TreeNode(null);
		
		// --------
		
		this.element.observe('click', this.click.bindAsEventListener(this));
		
		// --------
	
		var currentNode = null;
		
		var descendants = this.element.descendants();
		
		for(var i = 0; i < descendants.length; i++)
		{
			if(descendants[i].identify().startsWith(this.name + '_'))
			{
				var id = descendants[i].identify().substring(this.name.length + 1);
		
				var parentid = '';
		
				var classNames = $w(descendants[i].className);

				for(var j = 0; j < classNames.length; j++)
				{
					if(classNames[j].startsWith('parent_' + this.name + '_'))
					{
						parentid = classNames[j].substring(this.name.length + 8);
					}
				}

				var parent = this.nodes.get(parentid);
					
				if(parent == null)
				{
					parent = this.root;
				}
				
				currentNode = new TreeNode(descendants[i], id);

				this.nodes.set(id, currentNode);
					
				parent.addChild(currentNode);
			}
			
			if(descendants[i].hasClassName('children') && currentNode)
			{
				currentNode.childrenElement = descendants[i];
			}
		}
		
		// ---------
		
		var nodes = this.root.getDescendants();
		
		for(var i = 0; i < nodes.length; i++)
		{
			var toggle = $('toggle_' + nodes[i].element.identify())
				
			if(toggle)
			{
				if(this.isExpanded(nodes[i]))
				{
					toggle.src = this.options.expandedURL;
				}
				else
				{
					toggle.src = this.options.collapsedURL;
				}
			}
		}
		
		// -------
		
		this.element.insert({after: "<input type='hidden' id='" + this.name + "_expanded' name='" + this.name + "_expanded' />"});
			
		this.expanded = $(this.name + "_expanded");
		
		// -------
		
		this.updateExpansions();
	},
	
	mouseDown: function($super, event)
	{
		var toggle = Event.element(event);

		if(toggle.identify().startsWith('toggle_' + this.name + '_'))
		{
			return;
		}
		
		$super(event);
	},
	
	click: function(event)
	{
		var toggle = Event.element(event);

		if(toggle.identify().startsWith('toggle_' + this.name + '_'))
		{
			var element = $(toggle.identify().substring(7));
			
			if(element)
			{
				var id = element.identify().substring(this.name.length + 1);

				if(this.options.isLarge)
				{
					var expansions = [];
					
					var collapsed = false;
					
					for(var i = 0; i < this.options.expanded.length; i++)
					{
						if(this.options.expanded[i] == id)
						{
							collapsed = true;
							
							continue;
						}
						
						expansions.push(this.options.expanded[i]);
					}
					
					if(!collapsed)
					{
						expansions.push(id);
					}
					
					this.expanded.value = expansions.join(",");

					if(this.options.persistant)
					{
						setCookie(this.options.persistant, this.expanded.value, 0, "/");
					}

					var ancestors = this.element.ancestors();
			
					var submittedForm = false;
			
					for(var i = 0; i < ancestors.length; i++)
					{
						var item = ancestors[i];
					
						if(item.tagName == "FORM")
						{
							submittedForm = true;
							
							item.submit();
						}
					}
					
					if(!submittedForm)
					{
						window.location.reload(true);
					}
				}
				else
				{
					var node = this.nodes.get(id);
	
					if(this.options.isTable)
					{
						var descendants = node.getDescendants();
						
						for(var i = 0; i < descendants.length; i++)
						{
							if(descendants[i].opener == null)
							{
								descendants[i].opener = node;
								
								descendants[i].element.hide();
							}
							else if(descendants[i].opener == node)
							{
								descendants[i].opener = null;
								
								descendants[i].element.show();
							}
						}
					}
					else
					{
						node.childrenElement.toggle();
					}
					
					if(this.isExpanded(node))
					{
						toggle.src = this.options.expandedURL;
						
						if(this.options.onExpanded)
						{
							this.options.onExpanded();
						}
					}
					else
					{
						toggle.src = this.options.collapsedURL;
						
						var descendants = node.getDescendants();
						
						for(var i = 0; i < descendants.length; i++)
						{
							var selector = this.getSelector(descendants[i].id);
							
							if(selector)
							{
								selector.selected = false;
							}
						}
							
						this.updateSelection();
						
						if(this.options.onCollapssd)
						{
							this.options.onCollapssd();
						}
					}
					
					this.updateExpansions();
				}
			}
		}
	},
	
	isExpanded: function(node)
	{
		if(this.options.isTable)
		{
			return(node.children.length > 0 && node.children[0].element.visible());
		}
		else
		{
			return node.childrenElement.visible();
		}
	},
	
	updateExpansions: function(id, isExpanding)
	{
		var expansions	= [];
		
		var nodes = this.root.getDescendants();
		
		for(var i = 0; i < nodes.length; i++)
		{
			if(nodes[i].id && nodes[i].children.length > 0 && this.isExpanded(nodes[i]))
			{
				expansions.push(nodes[i].id);
			}
		}
		
		this.expanded.value = expansions.join(",");

		if(this.options.persistant)
		{
			setCookie(this.options.persistant, this.expanded.value, 0, "/");
		}
	}
});

var TreeTable = Class.create(Tree,
{
	initialize: function($super, element, options)
	{
		if(!options)
		{
			options = {};	
		}
		
		options.isTable = true;
		
		$super(element, options);
	}
});


function disableSelect()
{
	if(positioning)
	{
		return false;
	}
}

function restoreSelect()
{
	return true;
}

document.onselectstart = disableSelect;

document.onmousedown = disableSelect;

document.onclick = restoreSelect;