////////////////////////////////////////////////
// ColumnBrowser Object VERSION_0.8
//////////////////////////////////////////////
//
// The list control provides most of the 
// interaction functionality of the mSpace 
// explorer, allowing users to click on 
// items within a dimension, and alter 
// the contents of the current slice, as 
// well as load related information pages.
// 
// The List control uses a dynamic scrolling 
// mechanism that means that very few DOM 
// elements are created for each list, and 
// the contents of these elements are updated 
// dynamically as the users scrolls the list.  
// This is useful for long lists, such as the 
// Story Title dimension (in the NFO dataset), 
// as the List can load items from the server 
// when they need to, reducing initial 
// download requirements.
// 
// The list also includes an ipod style list 
// position indicator that shows the user 
// where abouts they are currently position, 
// in an A-Z context.
// 
//////////////////////////////////////////////

var List = Class.create();
List.prototype = {
//
// Member Variables
//The control surface node
ctlsfc: null, 
LiHldr: null,
ScrlSpcr: null,
//List item Height Guide
Li_H: 0,
//object to contain all list items
Items: null,
//All items that are highlighted
Highlighted: null,
//All selected items
Selected: null,
//stored last scroll position
lastScrollPos: 0,
//TimeoutiID of the scroll poll interval
scrollpollTID: -1,
//Parent column variable
pcol: null,
//first a-z popup item, ie first item displayed at current scroll pos
firstLiIndex: "",
//last a-z popup item, ie last item displayed at current scroll pos
lastLiIndex: "",
//associative array, storing mapping between list items, and the div containing them
//for dynamically scrolling list items, which change the label of the div's only
nodeMap: new Object(),
//node map for the breadcrumb bar dom elements
bcNodeMap: new Object(),
//Timeoutid for the preview mouse over
previewTID: -1,
//Timout ID for interval to check the scroll pos of the list
listscrollTID: -1,
//Last item clicked reference, to handle the double and single clicks on list items
//Cannot use dom event, as both single and double click is fired.
lastClickedLi: null,
//Timeout interval to wait before firing an actual mouse click event, when waiting for dbl click
dblClickInterval: 250,
//Timeout ID for the firing of Actual Mouse Click event, when waiting for a double click
dblClickTID: -1,
//flag to indicate if current preview is selected, ie hovered over
selectedPview: null,
//Text description for tooltips, when no items are selected in list, ie add to scratchpad
ttiptext_TagDesc: " { Scratchpad: Double click to add to your scratchpad! }",
//Text description for tooltips, when items are selected, ie shift click for multi select
ttiptext_MultiSel: " { Multiple Selection: Ctrl/Apple click for multiple selection }",
//flag to indicate if the items in view need to be loaded
//ie are in a oversize dynamic list, and have not been loaded yet.
loadViewLis: false,
//the number of list items returned in the last slicegetitems.
count: 0,
//flag indiating if the text filter is on.
filterIsOn: false,
//variable to store the last scroll bar position when an item was clicked in this list.
//so it can re-scroll back to that position
lastClickedScrollPos: -1,
//server side filter text variable
ssFilterText: "",
//Timeout ID for server side filters, so request is not sent
//until whole word is complete.
ssFilterTID: -1,
//displayed items count after a text filter
disItemCount: 0,
//flag to indicate if all items are loaded, ie dont need
//to be a dynamic list
allItemsLoaded: false,
//array containing the displayed items, after a text filter
Displayed: new Array(),
//the current text filter for the list
currentFilterText: "",

//
// Constructor
initialize: function(oo)
	{
				
		//Get passed parameters
		Object.extend(this, oo);
				
		
		if (this.count<=this.ItemsCount)
		{
			this.allItemsLoaded = true;
		}
		
		//sort out the pre id and 
		//post id variables for this list
		
		this._id = "_" + this.uri;
		var l_id = this._id;
		this._pid = "List_";
		var l_pid = this._pid;
		
		this.ScrlSpcr = Builder.node('div', {id: (l_pid + "ScrlSpcr" + l_id)});
		
		this.LiHldr = $(Builder.node('ul', {className: "LiHldr", id: (l_pid + "LiHldr" + l_id), style: "overflow: hidden"},
													 [ this.ScrlSpcr ]));
		
		//Create the list container divs
		this.ctlsfc = Builder.node('div', {className: "List"}, [ this.LiHldr ]);								   

	
				
		this.Li_H = $("DummyLi").offsetHeight;
		//alert(this.Li_H);
		
		
	
		
		//Register custom events
		////////////////////////
		
		document.observe('List:Resize',												this.Resize.bindAsEventListener(this));
		
		document.observe('List:InternalSelect',								this.InternalSelect.bindAsEventListener(this));
		document.observe('List:InternalClear',								this.InternalClear.bindAsEventListener(this));
		document.observe('List:ItemClick',										this.ExternalListItemClick.bindAsEventListener(this));
		
		document.observe('Item:MouseClick',										this.ItemClick.bindAsEventListener(this));
		
		document.observe('Column:ClearPreviewIcon',						this.ClearPreview.bindAsEventListener(this));
		
		
		
		//Register DOM events
		/////////////////////
		Event.observe(this.ctlsfc, "DOMMouseScroll", this.UpdateScrollPos.bindAsEventListener(this));
		Event.observe(this.ctlsfc, "scroll", this.UpdateScrollPos.bindAsEventListener(this));
		
		if( !((browserDetails[0] == 'msie' && browserDetails[1] >= 7) || browserDetails[0] == 'firefox' || browserDetails[0] == 'safari') )
		{
			Event.observe(this.ctlsfc, "mouseover", this.StartScrollPoll.bindAsEventListener(this));
			Event.observe(this.ctlsfc, "mouseout", this.MouseOut.bindAsEventListener(this));
		}		
		
		this.pcolsfc.appendChild(this.ctlsfc);
		
		
		//force an apply text filter, as items may have just been loaded
		this.ShowAllItems();
	},
	
//The resize function, resizes the list continer
//and also makies ure there are enough List Item
//elements to fill the viewable list area.  Creating
//new ones whe needed.
Resize: function()
	{
		var surfaceheight = this.ctlsfc.clientHeight;
		
		// If the surfaceheight = 0 then the column is closed and it would be a waste of CPU time to try to resize it
		if(surfaceheight == 0)
			return;
		
		var hasChanged = false;
		
		//Get the height of the pages dummy list item, 
		//to how what size each list item actually is
		this.Li_H = $("DummyLi").offsetHeight;
		
		
		//check the dummy item is not < 1 pixel high
		if (this.Li_H<1)
		{
			this.Li_H = $("DummyLi").firstChild.clientHeight;
			if (this.Li_H<1)
				return;
		}
		
		//Work out how many list items will fit in the current display area.
		var items = Math.ceil(surfaceheight/this.Li_H) + 1;
		
		// Add one list item either side of what is deamed to be visible - makes for smoother scrolling when scrolling one at a time
		items += 2;
			
		//If the current number of items in the display area, is more than 
		//the actual number that fit, then delete some of the items.
		if (items!=(this.LiHldr.childNodes.length-1))
		{
			if (((this.LiHldr.childNodes.length-1)>items))
			{
				while ((this.LiHldr.childNodes.length-1)>items)
				{
					var childNode = this.LiHldr.lastChild;
					if (childNode!=this.ScrlSpcr)
					{
						try
						{
						this.LiHldr.removeChild(childNode);
						}
						catch (e) {}
					}
				}
			}
			else
			{
				//If the current number of items in the display area, is less than 
				//the actual number that fit, then create some more list items				
				for (var i=(this.LiHldr.childNodes.length-1);i<items;i++)
				{					
					//Create the listitem dom elements
					var newPrvBtn = Builder.node('img',{id: (this._pid + this._id + "LiPrvwBtn_" + i), className: "Preview", src: "/pics/ColumnBrowser/Column/preview_btn.png", alt: "Show Preview Cue"});
					
					var newItem = Builder.node('li', {id: (this._pid + this._id + "Li_" + i), className: "Li"}, 
					[
						Builder.node('a',{className: "Even"},
						[
							Builder.node('div',{id: (this._pid + this._id + "LiLbl" + i), className: "Label"}),
							Builder.node('div',{className: "Preview"},
							[ newPrvBtn ]),
							Builder.node('div',{className: "Count"})
						])
					]);
								
					//add to list item holder
					this.LiHldr.appendChild(newItem);
	
					//register list item events, over, out and click
					Event.observe(newItem, "mouseover", 	this.MouseOverListItem.bindAsEventListener(this));
					Event.observe(newItem, "mouseout", 		this.MouseOutListItem.bindAsEventListener(this));
					Event.observe(newItem, "click", 			this.MouseClickListItem.bindAsEventListener(this));
					
					//register preview button events
					Event.observe(newPrvBtn, "mouseover", this.MouseOverPreview.bindAsEventListener(this));
					Event.observe(newPrvBtn, "mouseout", 	this.MouseOutPreview.bindAsEventListener(this));
					Event.observe(newPrvBtn, "click", 		this.MouseClickPreview.bindAsEventListener(this));
				}
				hasChanged = true;
			}
		}
		
		//Resize the list item holder, based on the the total number
		//of virtual items that there are.. This makes sure that the
		//scroll bar on the list is acurate.
		var totalItems = this.count;
		if (this.filterIsOn)
		{
			totalItems = this.disItemCount;
		}		
		var totalHeight = totalItems * this.Li_H;		
		
		this.LiHldr.style.height = totalHeight + "px";
	
		//If the resize has changed anything, 
		//update the list items
		
		if (hasChanged)
		{
			this.UpdateListItems();
		}
		
	},
	
//Check to see if another list has had an item clicked
//If so, then this is not the list in focus, and we cannot accurately
//Say if the scrollbar should be in the same position.
ExternalListItemClick: function(e)
	{
		var sender = e.memo;
		if (sender!=this)
		{
			this.lastClickedScrollPos = -1;
		}
	},
	
//Function to set the scroll position of the list
//and update the scroll spacer size
SetScroll: function(pos)
	{
		this.ctlsfc.scrollTop = pos;
		
		var height = this.ctlsfc.scrollTop - this.Li_H;
		
		if( height < 0)
			height = 0;
		
		this.ScrlSpcr.style.height = height + "px";
		if (this.ctlsfc.scrollTop==0)
		{
			this.ScrlSpcr.style.display = "none";
		}
		else
		{
			this.ScrlSpcr.style.display = "";
		}
		this.lastScrollPos = this.ctlsfc.scrollTop;		
	},
	
//This function checks to see if the scroll position
//has changed, and then displays the AtoZ if so
UpdateScrollPos: function(e)
	{
		//element = Event.element(e);
		//Check if scroll pos has changed
		if (this.lastScrollPos!=this.ctlsfc.scrollTop)
		{
			
			if (this.listscrollTID!=-1)
			{
				clearInterval(this.listscrollTID);
				this.listscrollTID = -1;
			}			
			
			//Set the new scroll position
			var newScrollTop = 0;
			
			if(this.lastScrollPos > this.ctlsfc.scrollTop)
			{
				// Scroll UP so round-down instead of up
				newScrollTop = Math.floor(this.ctlsfc.scrollTop / this.Li_H) * this.Li_H;
			}
			else
			{
				// Scroll DOWN so round-up instead of down
				newScrollTop = Math.ceil(this.ctlsfc.scrollTop / this.Li_H) * this.Li_H;
			}
			
			this.SetScroll(newScrollTop);
			//Update list items
			this.UpdateListItems();
			
			//Default A-Z display
			var firstLetter = "A";
			var lastLetter = "Z";
			var actualAZ = this.atoz.split(',');
			
			//Look at the first item in list
			if (!this.Items[this.firstLiIndex])
			{
				for (var x=0;x<actualAZ.length;x++)
				{
					var atz = actualAZ[x].split(':');
					if ((this.firstLiIndex)<atz[0])
					{
						break;
					}
					else if ((this.firstLiIndex)>=atz[0])
					{
						firstLetter = atz[1];
						
					}
				}				
			}
			else
			{
				firstLetter = this.Items[this.firstLiIndex].label;									
			}			
			
			//Look at last item in list
			if (!this.Items[this.lastLiIndex])
			{
				for (var x=0;x<actualAZ.length;x++)
				{
					var atz = actualAZ[x].split(':');
					if ((this.lastLiIndex)<atz[0])
					{
						break;
					}
					else if ((this.lastLiIndex)>=atz[0])
					{
						lastLetter = atz[1];
						
					}
				}				
			}	
			else
			{
				lastLetter = this.Items[this.lastLiIndex].label;
			}
			
			//Assign the first and last items to the popup string
			var popupString = "";
			
			//Check the type of list this is
			//and display the whole text of the
			//first item if its a date for instance
			if (this.pcol.OlType==0)
			{
				if (firstLetter.length>0)
					firstLetter = firstLetter.charAt(0);
				if (lastLetter.length>0)
					lastLetter = lastLetter.charAt(0);		
					
				popupString = firstLetter + " - " + lastLetter;
			}
			else if (this.pcol.OlType==1)
			{
				popupString = firstLetter;	
			}
				
			//fire a list scroll event.
			var thisObj = this;
			document.fire('List:Scroll', { list: this, text: popupString });
			//Fire off a list scroll stop in 400ms
			this.listscrollTID = setTimeout(function() { thisObj.ScrollListStop(); }, 400);
		}	
	},
	
//Function to check when a list stops scrolling	
//and load new list items, if the current ones are not loaded
ScrollListStop: function(e, element)
	{
		if (this.loadViewLis)
		{
			this.loadViewLis = false;
			var startItem = Math.ceil(this.ctlsfc.scrollTop/this.Li_H);
			document.fire('List:LoadPartialItems', { col: this.pcol, start: startItem });			
		}
	},
	
//This method sets an interval, because some browsers dont support the
//on scroll event.
StartScrollPoll: function(e, element)
	{
		//set an interval for browsers that dont support onscroll
		var thisObj = this;
		this.scrollpollTID = setInterval(function() { thisObj.UpdateScrollPos(); }, 5);
	},
	
//Stop the scroll polling
StopScrollPoll: function(e, element)
	{
		if (this.scrollpollTID!=-1)
		{
			clearInterval(this.scrollpollTID);
			this.scrollpollTID = -1;
		}
	},
	
//This function takes the current position in the list, and loads the virtual
//list items into the right positions, in the physical DOM elements.
UpdateListItems: function()
	{
		//Get which item is being displayed, from scroll pos
		var startItem = Math.ceil(this.ctlsfc.scrollTop/this.Li_H);
		
		// Allow for one list item element to be hidden at the top of the list
		if(startItem > 0)
			startItem--;
		
		//console.log("scrollTop: "+this.ctlsfc.scrollTop+"px\tstartItem: "+startItem);
		this.nodeMap = new Object();
		
		//get total selected items
		var totalSelectedItems = 0;
		for (d in this.Selected)
		{
			totalSelectedItems ++;
		}
		
		
		//get total items, which is affected by the
		//text filter
		var totalItems = this.count;
		if (this.filterIsOn)
		{
			totalItems = this.disItemCount;
		}
	
		
		//Get total height of list
		var totalHeight = totalItems * this.Li_H;
		if (isNaN(totalHeight))
			totalHeight = 0;
		
		//update list holder with height
		this.LiHldr.style.height = totalHeight + "px";		
		
		this.loadViewLis = false;
		this.m_currentStartItem = startItem;	
		
		//IE removes the scrollTop and height of the spacer when its removed from the DOM so store them for later use
		var scrollOffset = this.ctlsfc.scrollTop;
		var spacerHeight = this.ScrlSpcr.style.height;
				
		// Calculate the size of some elements that are the same for each iteration
		// This saves using expensive prototype.js calls a large number of times
		var previewIconWidth;
		
		try { previewIconWidth = $$('div.Preview')[0].down('img').getWidth(); } catch(e) { previewIconWidth = 0;}
				
		if(previewIconWidth == 0)
			previewIconWidth = 15;
		
		// Get the width of the first available list item
		var liWidth = 0;
		
		if(this.LiHldr.childNodes[1])
			liWidth = $(this.LiHldr.childNodes[1]).getWidth();
		else
		{
			// The columns does not have a first list item so use the width of the UL instead
			liWidth = $(this.LiHldr).getWidth();
		}
		
		//this.LiHldr.remove();
		
		//loop through actual DOM elements in the list holder
		for (var i=1;i<this.LiHldr.childNodes.length;i++)
		{
			//get dom element
			var liItem = $(this.LiHldr.childNodes[i]);
			var liItemA = $(this.LiHldr.childNodes[i].firstChild);
			var liItemLabel = $(this.LiHldr.childNodes[i].firstChild.firstChild);
			
			//assign the dom element a style
			//depending on if this position is odd or even
			if (isOdd(startItem + (i-1)))
				liItemA.className = "Odd";
			else
				liItemA.className = "Even";
			
			//Preview selection check
			try
			{
				if(this.selectedPview && this.selectedPview.label == liItem.firstChild.firstChild.firstChild.innerHTML)
					liItemA.childNodes[1].firstChild.src="/pics/ColumnBrowser/Column/preview_btn_on.png";
				else
					liItemA.childNodes[1].firstChild.src="/pics/ColumnBrowser/Column/preview_btn.png";
			}
			catch(e){}
			

			//Get the id of the current item
			//ie the position from the very start of
			//the non visible list.
			var li_id = startItem + (i);
			if (this.allItemsLoaded)
				li_id = this.Displayed[startItem + (i)];
			
			//check if the item exists
			if (this.Items[li_id])
			{
				//Update the contents of the DOM element with the item details
				var li 				= this.Items[li_id];
				var itemLabel = li.label;
				var itemUri 	= li.uri;
				
				if (totalSelectedItems>0)
					liItemA.title = li.label + this.ttiptext_MultiSel;
				else
					liItemA.title = li.label + this.ttiptext_TagDesc;
	
				if (this.Selected[startItem + (i)])
					liItemA.addClassName("Selected");					
				else if (this.Highlighted[startItem + (i)])
					liItemA.addClassName("Highlighted");

				
				if (i==1)
					this.firstLiIndex = li_id;

				this.lastLiIndex = li_id;
				
				if (this.hasPreview==true)
					liItemA.childNodes[1].style.display = "";
				else
					liItemA.childNodes[1].style.display = "none";		
				
				liItemLabel.update(itemLabel);
				
				this.nodeMap[li_id] = liItem;
				
				var thisPreviewIconWidth = previewIconWidth;
					
				if(!this.hasPreview)
					thisPreviewIconWidth = 0;
				
				liItemLabel.style.width = IEINT(liWidth - thisPreviewIconWidth - 5) + "px";				

			}
			else if ((!this.allItemsLoaded) && ((startItem + (i))<=totalItems))
			{
				//If the item is not available because it has not been loaded, then 
				//indicate that this item is loading, and update the
				//loadViewLis flag
				this.loadViewLis = true;
				liItemLabel.innerHTML = "Loading...";
				liItemA.childNodes[2].innerHTML = "";
				
				if (i==1)
					this.firstLiIndex = li_id;

				this.lastLiIndex = li_id;
				
			}
			else
			{
				//else the item does not exist at all, and make item blank
				liItemLabel.innerHTML = "";
				liItemA.childNodes[2].innerHTML = "";
			}			
		}
		
		//this.ctlsfc.update(this.LiHldr);
		
		// Must add the scroll details back so IE will work as expected
		this.ctlsfc.scrollTop = scrollOffset;
		this.ScrlSpcr.style.height = spacerHeight;
		
		if (this.loadViewLis)
		{
			this.loadViewLis = false;
			var startItem = Math.ceil(this.ctlsfc.scrollTop/this.Li_H);
			document.fire('List:LoadPartialItems', { col: this.pcol, start: startItem });			
		}

		//this.ToggleShowAll();
	},

//Get a list item object based on the
//mapping from dom nodes to list items
GetListItem: function(domNode)
	{
		
		for (key in this.nodeMap)
		{
			if (this.nodeMap[key]==domNode)
			{
				return this.Items[key];
			}
		}
		
		return null;
	},

//Toggle the show all button
ToggleShowAll: function()
	{
		// NOTE: There is no way to reference the ALLBTN when using when using OO object instance variables
		// When in column search is required again this needs to be fixed
		// suggest passing in a reference to the parent/columns object in the List constructor
		return;

		var showall = false;
			for(id in this.Items)
			{
				if(this.Selected[id] == true)
				{
					showall = true;
					break;
				}
			}

			allBtn = $(this.pcol._pid + "ALLBTN" + this.pcol._id);
			if(showall)
				allBtn.style.display='';
			else
				allBtn.style.display='none';
	},
	
//get a node via its uri
GetNodeByUri: function(uri)
	{
			return this.nodeMap[uri];
	},	

//Mouse over a list item
//currently this is not used, but event is still fired
//for future use by other controls
MouseOverListItem: function(e)
	{
		//prototype Event get element
		//finds the next parent li element
		//it the actual list item
		var element = Event.findElement(e, "li");
		var li = this.GetListItem(element);
		document.fire('Item:MouseOver', { sender: this, sourceItem: li, e: e, column: this.pcol });
	},
	
//Mouse out of list item
//currently this is not used, but event is still fired
//for future use by other controls	
MouseOutListItem: function(e)
	{
		//prototype Event get element
		//finds the next parent li element
		//it the actual list item		
		var element = Event.findElement(e, "li");
		var li = this.GetListItem(element);		
		document.fire('Item:MouseOut', { sender: this, sourceItem: li });
	},

//ITem Double click event
MouseDblClickListItem: function(e)
	{
		//prototype Event get element
		//finds the next parent li element
		//it the actual list item		
		var element = Event.findElement(e, "li");
		var li = this.GetListItem(element);
		document.fire('Column:DoubleClick', {listitem: li, column: this.pcol});
	},
	
//Mouse click on a list item
MouseClickListItem: function(e)
	{
		//prototype Event get element
		//finds the next parent li element
		//it the actual list item		
		var element = Event.findElement(e, "li");
		
		//get list item
		var li = this.GetListItem(element);
		
		if(!li)
			return;
		
		
		//If this is the first time this item has been clicked, 
		//then fire off a timeout to check to see if this is a 
		//single or double click
		//This is needed because most browsers fire both single and doubel
		//click events
		if ((this.lastClickedLi==null) || (this.lastClickedLi!=element))
		{
			element.firstChild.addClassName("Selected");
			this.lastClickedLi = element;
			var thisObj = this;
			this.dblClickTID = setTimeout(function() { thisObj.ActualMouseClickListItem(e); }, this.dblClickInterval);
		}
		else
		{
			element.firstChild.removeClassName("Selected");
			//IF second click, then fire double click call
			if (this.dblClickTID!=-1)
			{
				clearTimeout(this.dblClickTID);
				this.dblClickTID = -1;
			}
			this.lastClickedLi = null;
			this.MouseDblClickListItem(e, element);
		}
	},
	
//Called after a short intervael from the first click
//to simulate a single click, and make sure it is not
//a double click
ActualMouseClickListItem: function(e)
	{
		//get li element
		var element = Event.findElement(e, "li");
		//Record the scroll position, so it can
		//be restored later.
		this.lastClickedScrollPos = this.ctlsfc.scrollTop;
		document.fire('List:ItemClick', this);
		
		this.lastClickedLi = null;
		
		//get list item
		var li = this.GetListItem(element);
		
		//If already selected, un select the item
		if (this.Selected[li.id])
		{
			this.Selected[li.id] = false;
			element.firstChild.removeClassName("Selected");
			
			document.fire('Column:UnSelectItem', { column: this.pcol, listitem: li });
		}
		else
		{
			//else check if control modifer
			var isCtrl = false;
			
			//If the control key is not pressed, clear all currently selected items
			try { //Hack for IE, does not seem to like this
			
				if (e.ctrlKey || (getOS()[0] == 'macosx' && e.metaKey) )
				{
					isCtrl = true;
				}
			}
			catch (e)
			{
			}
			
			//If not control, clear any current selections
			if (!isCtrl)
			{
				for (key in this.Selected)
				{
					this.Selected[key] = false;
					
					// If item has been scrolled out of view then next line will throw and error
					try{ this.nodeMap[key].firstChild.removeClassName("Selected"); } catch(e) {}
				}				
			}
			
			//select item
			this.Selected[li.id] = li;
			element.firstChild.addClassName("Selected");
			
			//fire a column select item event
			document.fire('Column:SelectItem', { column: this.pcol, listitem: li });
		}
		//this.ToggleShowAll();		
	},

//Mouse out event for stopping the scroll poll when
//mouse is not over list
MouseOut: function(e)
	{
		var element = Event.findElement(e, "li");	
		this.StopScrollPoll(e, element);
	},
	
//Clear all the contents of list
Clear: function()
	{
		
		this.Items = new Object();
		this.Selected = new Object();
		this.Highlighted = new Object();
		
		this.Resize();
		this.UpdateListItems();
	},
	
//replace the items within the list
ReplaceItems: function(items)
	{
		Object.extend(this, items);
		
		this.RefreshFilter();
		
	},
	
//Update the list data from new server data
UpdateData: function(columnDataObject, firstLoad)
	{
		var lastCount = this.count;
		
		//extending passed obj will replace 
		//all relevant arrays
		Object.extend(this, columnDataObject);
		
		//check the text filter
		this.ssFilterText = "";
		if (columnDataObject.TextFilter)
		{
			this.ssFilterText = columnDataObject.TextFilter;
		}
		
		//Check if all items are loaded
		this.Displayed = new Array();
		if (!this.allItemsLoaded) 
		{
			this.ShowAllItems();
		}
		else
		{
			this.ApplyTextFilter(true);
		}
		
		
		this.allItemsLoaded = false;
		
		if (this.count<=this.ItemsCount)
		{
			this.allItemsLoaded = true;
		}
		
		
		//check for selected items to scroll into view
		var scrollIntoViewItem = null;
	
		for (id in this.Selected)
		{
			if (scrollIntoViewItem==null || id < scrollIntoViewItem)
			{
				scrollIntoViewItem = id;
			}
		}
		
		//check for highlighted items to scroll into view
		if (scrollIntoViewItem==null)
		{
			for (id in this.Highlighted)
			{
				if (scrollIntoViewItem==null || id < scrollIntoViewItem)
				{
					scrollIntoViewItem = id;
				}
			}			
		}
		
	
		//reset the scroll pos
		this.ctlsfc.scrollTop = 0;
		this.ScrlSpcr.style.height = "0px";
		this.ScrlSpcr.style.height = "0px";
		
		//If there was a last clicked scroll pos, ie
		//this column recieved the click that foreced
		//the update, then reload the scroll pos
		if (this.lastClickedScrollPos!=-1)
		{
			this.ctlsfc.scrollTop = this.lastClickedScrollPos;
			this.ScrlSpcr.style.height = (this.ctlsfc.scrollTop) + "px";
			if (this.ctlsfc.scrollTop==0)
			{
				this.ScrlSpcr.style.display = "none";
			}
			else
			{
				this.ScrlSpcr.style.display = "";
			}		
		}
		else if (scrollIntoViewItem!=null)
		{
			//else scroll the first selected or highlighted
			//item into view
			this.ScrollIdIntoView(scrollIntoViewItem);
		}
		
		
		
		
		//upate the column title
		this.pcol.UpdateTitle((this.SelectedCount>0), (this.HighlightedCount>0));
		
		//Update list items
//		this.Resize();
		this.UpdateListItems();
	},
	
//get item by uri
GetItem: function(uri)
	{
		for (id in this.Items)
		{
			if (this.Items[id].uri == uri)
			{
				return this.Items[id];
			}
		}
		return null;
	},		
	
//Internal selection made by other colums etc, 
//can be used to select an item, but not fire a select
//event right away
InternalSelect: function(e)
	{
		var eventParams = e.memo;
		if (eventParams.sourceItem)
		{
			if (eventParams.sourceItem.column && eventParams.sourceItem.column==this.pcol.uri)	
			{
				
				var li_Uri = eventParams.sourceItem.uri;                
				if (li_Uri.indexOf("http%3A%2F%2F")!=-1)
				{
				   li_Uri = URLDecode(eventParams.sourceItem.uri);
				}				
				
				var li = this.GetItem(li_Uri);
				if (li==null)
					li = { uri: li_Uri, id: -1 };
					
				this.Selected[li.id] = li;
				
				if (eventParams.fire)
				{
					document.fire('Column:SelectItem', { column: this.pcol, listitem: li, goalcol: eventParams.goalcol, goaluri: eventParams.goaluri});
				}
			}
		}
	},

//Function to internally clear all the selections in a column
//without actually updating the slice etc
InternalClear: function(e)
	{
		var uri = e.memo;
		if ((uri==this.pcol.uri) || (uri=="Col_All"))
		{
			this.Selected = new Object();		
		}
	},	
	
//recieve an external item click.
ItemClick: function(e)
	{
		var eventParams = e.memo;
		// Do the required params exist?
		if (eventParams.sender && eventParams.sourceItem)
		{
			// Was this an external click meant for this object?
			if (eventParams.sender!=this && eventParams.sourceItem[0].column && eventParams.sourceItem[0].column==this.pcol.uri)	
			{
				// Should we clear the column first?
				if (eventParams.clearcol)
				{
					document.fire('List:InternalClear', eventParams.clearcol);
				}
				
				// Should we perform a multiselect?
				if (eventParams.sourceItem.length > 1)
				{
					//If there is a multi select, then fire off internal selections
					//this was an NFO hack, so that Story Title and Segment could both be
					//selected at the same time, as the combination of the two, represents
					//a programme goal item. but is useful to have here anyway
					
					// Loop through the array but only tell the last selection to perform an AJAX update
					for(i = 0; i < eventParams.sourceItem.length; i++)
						document.fire('List:InternalSelect', { fire: (i == eventParams.sourceItem.length-1), sourceItem: { uri: eventParams.sourceItem[i].uri, column: eventParams.sourceItem[i].column }, goalcol: eventParams.goalcol, goaluri: eventParams.goaluri });
					
					return;
					
				}				
				
				if(!this.pcol.isopen)
				{
					//If the current column is not actually open, then queue it up to be opened
					document.fire('ColumnBrowser:QueueColumn', { column: this.pcol });
				}
				
				var li_Uri = eventParams.sourceItem[0].uri;                
				if (li_Uri.indexOf("http%3A%2F%2F")!=-1)
				{
				   li_Uri = URLDecode(eventParams.sourceItem[0].uri);
				}				
				
				var li = this.GetItem(li_Uri);
				if (li==null)
					li = { uri: li_Uri, id: -1 };
				
				
				//If there is only one item, then either
				//select it or unselect it and fire an event.
				if (li.id!=-1)
				{
					if (this.Selected[li.id] && eventParams.unselect)
					{
						this.Selected[li.id] = false;
						document.fire('Column:UnSelectItem', { column: this.pcol, listitem: li});
					}
					else
					{
						this.Selected[li.id] = li;
						document.fire('Column:SelectItem', { column: this.pcol, listitem: li});						
					}
				}
				else
				{
					this.Selected[li.id] = li;
					document.fire('Column:SelectItem', { column: this.pcol, listitem: li});
				}
			
							
			}
		}
		

	},

//Clear all selected items
ShowAll: function()
	{
		this.Selected = new Object();
		this.UpdateListItems();

		document.fire('Column:UnSelectAll', {column: this.pcol});
		$(this.pcol._pid + "ALLBTN" + this.pcol._id).style.display='none';

	},
	

//Mouse over the preview cue item
MouseOverPreview: function(e)
	{
		//get the first li dom element, up the bubble tree from the preview div
		//ie the actual list item div
		var element = Event.findElement(e, "li");
		var li = this.GetListItem(element);
		if (li!=null)
		{
			var thisObj = this;
			this.previewTID = setTimeout(function() { thisObj.FirePreview(li, element); }, 750);
		}		
	},
	
//mouse out of the preview cue item
MouseOutPreview: function(e)
	{
		if (this.previewTID!=-1)
		{
			clearTimeout(this.previewTID);
		}
	},	
	
//Mouse click the preview cue icon
MouseClickPreview: function(e, element)
	{
		//get the first li dom element, up the bubble tree from the preview div
		//ie the actual list item div
		element = Event.findElement(e, "li");		
		var li = this.GetListItem(element);
		if (li!=null)
		{
			this.FirePreview(li);
		}
	},
	
//fire a preview cue event after mouse
//has hovered over preview cue for a few
//secs
FirePreview: function(li, element)
	{
		document.fire('Column:ClearPreviewIcon', {sender: this});
		this.selectedPview = li; 
		
		this.UpdateListItems();
		
		document.fire('Item:Preview', { sender: this, listitem: li, column: this.pcol });
	},

//clear the preview cue timeout
ClearPreview: function(e)
	{
		var eventParams = e.memo;
		if(this != eventParams.sender)
		{
			this.selectedPview = null;
			this.UpdateListItems();
		}
	},
	
//clear any selections
ClearSelections: function()
	{
		this.Selected = new Object();
	},
	
//Get a slice string for the column browser breadcrumb bar
GetSliceString: function(BreadCrumb)
	{
		
		var returnDiv = Builder.node('div', {className: "float"});
		BreadCrumb.appendChild(returnDiv);
		
		var totalCount = 0;
		
		var selectedCount = 0;
		var moreSelTooltip = "";
		for (id in this.Selected)
		{
			var li =  this.Selected[id];
		
			if (selectedCount<1)
			{
				var tempDiv = Builder.node('div',{id: "brdcrm_" + this.uri + "_" + li.uri, className: "selected", title: (li.label + " {Click to Unselect}")}, [li.label]);
								
				this.bcNodeMap[tempDiv.id] = li;
				
				returnDiv.appendChild(tempDiv);
				
				Event.observe(tempDiv, "mouseover", this.BreadcrumbMouseOver.bindAsEventListener(this));
				Event.observe(tempDiv, "click", this.BreadcrumbMouseUnSelect.bindAsEventListener(this));				
				
				
			}
			else
			{
				if (selectedCount>1)
				{
					moreSelTooltip += " | ";
				}
				moreSelTooltip += li.label;
			}
			
			selectedCount ++;
			totalCount ++;
		}
	
		if (selectedCount>1)
		{
			var moreDiv = Builder.node('div', {className: "more"});
			moreDiv.title = moreSelTooltip;
			moreDiv.innerHTML = "[" + (selectedCount-1) + " more]";
			returnDiv.appendChild(moreDiv);
		}		
		
		
		if (selectedCount==0)
		{
			var highCount = 0;
			var moreTooltip = "";
			for (id in this.Highlighted)
			{
				var li =  this.Highlighted[id];
			
				if (highCount<1)
				{
					var tempDiv = Builder.node('div',{id: "brdcrm_" + this.uri + "_" + li.uri, className: "highlighted", title: (li.label + " {Click to Select}")}, [li.label]);
					
					this.bcNodeMap[tempDiv.id] = li;
					
					returnDiv.appendChild(tempDiv);
					
					Event.observe(tempDiv, "mouseover", this.BreadcrumbMouseOver.bindAsEventListener(this));
					Event.observe(tempDiv, "click", this.BreadcrumbMouseSelect.bindAsEventListener(this));					
				}
				else
				{
					if (highCount>1)
					{
						moreTooltip += " | ";
					}					
					moreTooltip += li.label;
				}
				
				highCount ++;
				totalCount ++;
			}			
			
			if (highCount>1)
			{
				var moreDiv = Builder.node('div', {className: "more"});
				moreDiv.title = moreTooltip;
				moreDiv.innerHTML = "[" + (highCount-1) + " more]";
				returnDiv.appendChild(moreDiv);
			}			
		}
		
		if (totalCount==0)
		{
				var moreDiv = Builder.node('div', {className: "colname"});
				moreDiv.title = "No selections or highlights in " + this.pcol.label;
				moreDiv.innerHTML = "(" + this.pcol.label + ")";
				returnDiv.appendChild(moreDiv);			
		}
		
		return returnDiv;
		
	},
	
//Mouse over event when the user hovers
//over the breadcrumb entry for this list
BreadcrumbMouseOver: function(e)
	{
		//scroll the relevant highlighted, or selected
		//item into view
		var element = e.element();
		var li = this.bcNodeMap[element.id];
		if (li)
		{
			this.ScrollIdIntoView(li.id);
		}
	},
	
//unselect event from the breadcrumb bar
BreadcrumbMouseUnSelect:function(e)
	{
		var element = e.element();
		var li = this.bcNodeMap[element.id];
		if (li)
		{
			this.Selected[li.id] = false;
			this.UpdateListItems();
			
			document.fire('Column:UnSelectItem', { column: this.pcol, listitem: li });	
		}
	},
	
//select event from the breadcrumb bar
BreadcrumbMouseSelect:function(e)
	{
		var element = e.element();
		var li = this.bcNodeMap[element.id];
		if (li)
		{		
			this.Selected[li.id] = li;
			this.UpdateListItems();
			
			document.fire('Column:SelectItem', { column: this.pcol, listitem: li });			
		}
	},	
	
//Scroll an item with the passed id into view
//scrolls the list to the position of that item
ScrollIdIntoView: function(eventParams)
	{		
		this.ctlsfc.scrollTop = (eventParams-1)* this.Li_H;
		this.ScrlSpcr.style.height = (this.ctlsfc.scrollTop) + "px";
		if (this.ctlsfc.scrollTop==0)
		{
			this.ScrlSpcr.style.display = "none";
		}
		else
		{
			this.ScrlSpcr.style.display = "";
		}
		//this.UpdateListItems();
	},
	

//Load partial column data from the server, ie the
//now visible list item data
LoadVisibleListItems: function(colDetails, clear)
	{
		if (clear)
		{
			if (colDetails.Items)
			{
				this.Items = colDetails.Items;
			}
		}
		else
		{
			if (colDetails.Items)
			{
				for (id in colDetails.Items)
				{
					this.Items[id] = colDetails.Items[id];													
				}
			}
		}
		
		this.ShowAllItems();
		
		this.Resize();
		this.UpdateListItems();
	},
	
//get an array of selected items
GetSelectedItems: function()
	{
		var arr = new Array();
		if (this.Selected)
		{
			for (id in this.Selected)
			{
				if (this.Selected[id])				
					arr.push(this.Selected[id]);													
			}
		}		
		
		return arr;
	},
	
//reset the list
Reset: function()
	{
		this.Selected = {};
		this.Highlighted = {};
		this.Items = new Array();
	},
	
//set the current text filter
SetTextFilter: function(filterText)
	{
		if (this.currentFilterText != filterText)
		{
			this.currentFilterText = filterText;
		
			this.ApplyTextFilter(true);			
		}
		
	},
	
//clear the current text filter
ClearTextFilter: function()
	{
		if (this.currentFilterText!="")
		{
			this.currentFilterText = "";
		
			this.ApplyTextFilter(true);
		}
		
	},	
	
//apply the current ext filter
ApplyTextFilter: function(change)
	{
		
		//If all items are loaded, 
		//then do a local text filter
		if (this.allItemsLoaded)
		{
			if (change && (this.currentFilterText==""))
			{
				this.ShowAllItems();
				this.filterIsOn = false;
			}
			else
			{
				this.LocalTextFilter();
				this.filterIsOn = true;
			}

			return;
		}
		else
		{
			//if all items are not loaded, 
			//then set a time out to wait for
			//more input, then call a server filter
			if (this.ssFilterTID!=-1)
			{
				clearTimeout(this.ssFilterTID);
			}
			
			
			var t = this;
			this.ssFilterTID = setTimeout(function() { t.ServerTextFilter(change); }, 1000);
			
		}
		
		
		
	},
	
//filter the list items locally, as they are all loaded.
LocalTextFilter: function()
	{
		//Loop through items array, and match the items label
		//to the filter text
		var filterArray = this.currentFilterText.toLowerCase().split("+");
		var matchesWord = false;
		this.Displayed = new Array();
		this.disItemCount = 0;
		for (var x =0; x<filterArray.length;x++)
		{
			var filterWord = filterArray[x];
			if (filterWord!="")
			{
				for (id in this.Items)
				{
					var li = this.Items[id];

					// Filter anywhere
					if (li.label.toLowerCase().indexOf(filterWord)!=-1)
					{
						this.disItemCount ++;
						this.Displayed[this.disItemCount] = id;
					}

				}
			}
		}		
	},
	
//Do a server filter 
//just call load partial items, and it will
//take the current filter text as well
ServerTextFilter: function(change)
	{
		this.ssFilterTID = -1;
		if (this.ssFilterText!=this.currentFilterText || change)
		{
			var startItem = Math.ceil(this.ctlsfc.scrollTop/this.Li_H);
			document.fire('List:LoadPartialItems', { col: this.pcol, start: startItem, clear: true });
		}
			
	},
	
//Show all items in the list
ShowAllItems: function()
	{
		this.Displayed = new Array();
		this.disItemCount = 0;
		for (id in this.Items)
		{
			this.disItemCount ++;
			this.Displayed[this.disItemCount] = id;
		}
	},
	
//Get a url param string
//for this list with current
//filter conditions.
GetFilterUrlText: function()
	{
		var rS = "";
		if ((!this.allItemsLoaded) && (this.currentFilterText!=""))
		{
			rS = "&scol[]=scol:" + this.pcol.uri + "&scol[]=val:" + URLEncode(this.currentFilterText);
		}
		
		return rS;
	}
	
}

