/**

	Purpose: Controlling 'paginated' divs and tables inside pages
	Author: Alex Bridge	
	
	
	To make a set of divs paginatable, do the following:
	
		o Include this file and css/pagination.css
		o Create a containing div with class 'paginatable'
		o Give each div inside this a class of 'paginatablePage'
		o Add (inside the container) one or more unordered lists with class
		  'paginationNav', for controlling the navigation between divs
		o Optionally add an element of class 'elementCount', for displaying the 'page x of y' information
		o Optionally add elements with class 'paginationDisplayOff' if they are to be shown when elements are not paginated
		o Optionally add elements with class 'paginationDisplayOn' if they are to be shown when elements are paginated
		o Ensure that all of the elements added above are also assigned a unique id


	Example for Plone/TAL, which fetches a set of results (schools), splits these into groups of 10, 
	and outputs each of these groups on a separate 'page'.
	
	<div class="paginatable" id="paginationContainer"
			   tal:define="items python:context.getFolderContents(contentFilter = {'portal_type':'item', 'review_state':('Published')});
				 		  itemsCount python: len(items);
				 		  itemsFound python: itemsCount > 0;
				 		  pageLength python:10;
				   		  pages python: [items[i:i+pageLength] for i in range(0, itemsCount, pageLength)];">
				   		  
		<div class="paginatablePage" tal:repeat="page pages" tal:attributes="id python:'itemPage%s' % repeat['page'].index">
			<ul class="itemsList">
				<li tal:repeat="item page">
					<span tal:replace="item/Title" />
				</li>
			</ul>
		</div>
		
		<ul class="paginationNav" id="pagenav"><li>Navigation</li></ul>
		
	</div>



	To make a table paginatable, do the following:
	
		o Include this file and css/pagination.css
		o Create a table with class 'paginatable'
		o Give the <tbody> a class which is the number of rows you want per page
		o Put all paginatable <tr>s inside the <tbody>
		o Add (inside the <thead> or <tfoot>) one or more unordered lists with class
		  'paginationNav', for controlling the navigation between divs
		o Optionally add (inside the <thead> or <tfoot>) an element of class 'elementCount', for displaying the 'page x of y' information
		o Ensure that all of the elements added above are also assigned a unique id
		
		
	Example for Plone/TAL, which fetches a set of results (users), splits these into groups of 10, 
	and outputs each of these groups on a separate 'page'.
		
	<table id="currentUsers" class="paginatable">
		<thead>
			<tr class="headerRow">
				<th class="firstName">First Name</th>
				<th class="lastName">Last Name</th>
				<th class="email">Email</th>
			</tr>
		</thead>
		<tfoot>
			<tr>
				<td colspan="3">
					<ul class="paginationNav" id="pagenav"><li>Navigation</li></ul>
					<span class="elementCount" id="userElementCount"></span>
				</td>
			</tr>
		</tfoot>
		<tbody class="10">
			<tal:repeat repeat="usr python:context.getUsersForCorp(state='current')">
				<tr>
					<td tal:content="usr/getFirstName"/>
					<td tal:content="usr/getSurname"/>
					<td tal:content="usr/getEmail"/>
				</tr>
			</tal:repeat>
		</tbody>
	</table>
		
	
**/
	
//	Arrays to contain the elements to be paginated,
//	the current page for each of these, and the
//	navigation elements present on each of these

paginatedElements = new Array();
paginatedTableElements = new Array();

currentPage = new Array();
paginationNavElements = new Array();
paginationCountElements = new Array();

defaultRowsPerTablePage = 3;
rowsPerTablePage = new Array();


/*
	Ensure the pagination is initialised when the page loads
*/

// if window already has an onload function defined
if(typeof window.onload == 'function')
{
	//store it
	var existing = onload;
	
	//add new onload handler
	window.onload = function()
	{
		//call existing onload function
		existing();
		
		//call bespoke onload function
		initialise();
	};
}
else
{
	//setup onload function
	window.onload = function()
	{
		initialise();
	}
}


/*
	Initialise the pagination
*/

function initialise()
{
	var count;
	var pageCount;
	var pageElementCount;
	
	//	get all paginatable container div elements
	paginatableElements = getElementsByClass('paginatable', 'div');
	
	//	initialise the pagination for each of these container elements
	for (count=0; count<paginatableElements.length; count++)
	{
		container = paginatableElements[count].id
		paginatedElements[container] = new Array();
		currentPage[container] = 0;
	
		// populate paginatedElements for the container
		paginatedElements[container] = getElementsByClass('paginatablePage', 'div', container);
		paginationCountElements[container] = getElementsByClass('elementCount', '*', container);
		pageCount = paginatedElements[container].length;

		// display the navigation for this container
		initialiseNavigation(container);
		
		// display the contained elements as pages
		displayPaginated(container);
	}
	
	//	get all paginatable table elements
	paginatableTables = getElementsByClass('paginatable', 'table');
	
	//	initialise the pagination for each of these tables
	for (count=0; count<paginatableTables.length; count++)
	{
		container = paginatableTables[count].id
		paginatedElements[container] = new Array();
		currentPage[container] = 0;
		
		// populate paginatedElements for the table
		paginationCountElements[container] = getElementsByClass('elementCount', '*', container);
		
		// store the number of rows of the table to display per page
		rowsPerTablePage[container] = paginatableTables[count].tBodies[0].className;
		if(!rowsPerTablePage[container]) rowsPerTablePage[container] = defaultRowsPerTablePage;
		
		// display the navigation for this table
		initialiseNavigation(container);
		
		// display the contained rows as pages
		displayPaginated(container);
	}
}


/*
	get all elements of a given CSS class, optionally
	limiting the results by tag type and parent node
*/

function getElementsByClass(searchClass, tag, node)
{
	//	get parent node
	if ( node == null )
	{
		node = document;
	}
	else
	{
		node = document.getElementById(node);
	}
	
	//	select all elements if no tag specified
	if ( tag == null ) tag = '*';
	
	//	get all elements in parent node of type tag	
	var elements = node.getElementsByTagName(tag);
	
	//	initialise variables used for filtering elements by class
	var elementsLen = elements.length;
	var results = new Array();
	var pattern = new RegExp("(^|\\s)"+searchClass+"(\\s|$)");
	
	//	filter elements by class into results
	for (elementsCount=0, resultsCount=0; elementsCount<elementsLen; elementsCount++) 
	{
		if (pattern.test(elements[elementsCount].className)) 
		{
			results[resultsCount] = elements[elementsCount];
			resultsCount++;
		}
	}
	return results;
}


/*
	show the navigation elements for a specified container
*/

function initialiseNavigation(container)
{
	// populate paginationNavElements for the container
	paginationNavElements[container] = getElementsByClass('paginationNav', 'ul', container);
	var navElementCount = paginationNavElements[container].length;
	
	// show all the navigation elements
	for (elementsCount=0; elementsCount<navElementCount; elementsCount++)
	{
		paginationNavElements[container][elementsCount].style.display = 'block';
	}
}


/*
	write out the 'page x of y' string for a specified container
*/

function writePaginationCount(container, showAll)
{
	// if we're displaying as paginated
	if (showAll==null)
	{
		// write out 'page x of y' for divs
		if(containerNode.nodeName=='DIV')
		{
			paginationCountText = 'page ' + (currentPage[container]+1) + ' of ' + paginatedElements[container].length;
		}
		// write out 'results x to y of z' for tables
		else
		{
			rowCount = containerNode.tBodies[0].rows.length;
			startRow = rowsPerTablePage[container]*currentPage[container];
			endRow = (startRow+parseInt(rowsPerTablePage[container]));
			if (endRow > rowCount) endRow = rowCount;
			
			paginationCountText = 'results ' + (startRow+1) + ' to ' + endRow + ' of ' + rowCount;
		}
	}
	// showing all - not count needed
	else
	{
		paginationCountText = '';
	}
	
	// write string out to all relevent elements
	for(elementsCount=0; elementsCount<paginationCountElements[container].length; elementsCount++)
	{
		paginationCountElements[container][elementsCount].innerHTML = paginationCountText;
	}

}

/*
	Check last class name and append/amend if necessary
*/

function checkDisplayClass(paginatedElement, appendClassName)
{
	//Store the class path for the paginated element
	var classPath = paginatedElement.className;
	
	//Find where the last class name starts
	var lastSpace = classPath.lastIndexOf(' ') + 1;
	
	//Store the class path before the last class name
	var beforeLastClassName = classPath.substring(0, lastSpace);			
	
	//Store the last class name
	var lastClassName = classPath.substring(lastSpace);
	

	//If the last class name is not 'doDisplay' or 'doNotDisplay' then append the class name
	if ((lastClassName != 'doNotDisplay') && (lastClassName != 'doDisplay') && (lastClassName != 'doDisplayRow')
	 	&& (lastClassName != 'paginationDisplayOff') && (lastClassName != 'paginationDisplayOn'))
	{
		paginatedElement.className += ' ' + appendClassName;
	} else {
		//If the above is not true then remove the last class name and add the new one
		paginatedElement.className = beforeLastClassName + appendClassName;
	}
}

/*
	show requested page in specified container
*/

function showPage(container, pageNumber)
{
	containerNode = document.getElementById(container)
	
	//	paginated div
	if(containerNode.nodeName=='DIV')
	{
		//	hide currently displayed page
		checkDisplayClass(paginatedElements[container][currentPage[container]], 'doNotDisplay');
		
		//	set specified page to be current and display it
		currentPage[container] = pageNumber;
		checkDisplayClass(paginatedElements[container][currentPage[container]], 'doDisplay');
	}
	//	paginated table
	else
	{
		// hide currently displayed rows
		for(rowCount=0; rowCount<rowsPerTablePage[container]; rowCount++)
		{
			rowIndex = (currentPage[container]*rowsPerTablePage[container])+rowCount;
			if(rowIndex>=containerNode.tBodies[0].rows.length) break;
		
			checkDisplayClass(containerNode.tBodies[0].rows[rowIndex], 'doNotDisplay')
		}
		
		//	set specified page to be current and display its rows
		currentPage[container] = pageNumber;
		
		for(rowCount=0; rowCount<rowsPerTablePage[container]; rowCount++)
		{
			rowIndex = (currentPage[container]*rowsPerTablePage[container])+rowCount;
			if(rowIndex>=containerNode.tBodies[0].rows.length) break;
			
			checkDisplayClass(containerNode.tBodies[0].rows[rowIndex], 'doDisplayRow')
		}
	}
	
	//	update the navigation links
	writeNavigationLinks(container);
	writePaginationCount(container);
}


/*
	show all pages in specified container
*/

function displayAll(container)
{
	var pageCount = paginatedElements[container].length;
	
	containerNode = document.getElementById(container)
	
	//	paginated div
	if(containerNode.nodeName=='DIV')
	{
		for (elementsCount=0; elementsCount<pageCount; elementsCount++)
		{
			checkDisplayClass(paginatedElements[container][elementsCount], 'doDisplay');
		}
	}
	//	paginated table
	else
	{
		for (rowCount=0; rowCount<containerNode.tBodies[0].rows.length; rowCount++)
		{
			checkDisplayClass(containerNode.tBodies[0].rows[rowCount], 'doDisplayRow');
		}
	}
	
	switchDisplayedElements(container);
	
//	update the navigation links
	writeNavigationLinks(container, 1);
	writePaginationCount(container, 1);
}


/*
	switch around the elements with classes paginationDisplayOff and paginationDisplayOn
*/
function switchDisplayedElements(container)
{	
	paginationDisplayOffElements = getElementsByClass('paginationDisplayOff', null, container);
	paginationDisplayOnElements = getElementsByClass('paginationDisplayOn', null, container);
	
	for(elementsCount=0; elementsCount<paginationDisplayOffElements.length; elementsCount++)
	{
		checkDisplayClass(paginationDisplayOffElements[elementsCount], 'paginationDisplayOn');
	}
	
	for(elementsCount=0; elementsCount<paginationDisplayOnElements.length; elementsCount++)
	{
		checkDisplayClass(paginationDisplayOnElements[elementsCount], 'paginationDisplayOff');
	}
}


/*
	show only the current page in specified container
*/

function displayPaginated(container)
{
	var pageCount = 0;
	containerNode = document.getElementById(container)
	
	//	paginated div
	if(containerNode.nodeName=='DIV')
	{
		pageCount = paginatedElements[container].length;
		for (elementsCount=0; elementsCount<pageCount; elementsCount++)
		{
			if(elementsCount != currentPage[container])
			{
				checkDisplayClass(paginatedElements[container][elementsCount], 'doNotDisplay');
			}
		}
	}
	//	paginated table
	else
	{
		// calculate number of pages	
		numberOfPages = Math.ceil(containerNode.tBodies[0].rows.length/rowsPerTablePage[container]);
		
		// loop through pages
		for (pageCount=0; pageCount<numberOfPages; pageCount++)
		{
			// loop through rows if loop page isn't the currently displayed page
			if(pageCount != currentPage[container])
			{
				for (rowCount=0; rowCount<rowsPerTablePage[container]; rowCount++)
				{
					rowIndex = (pageCount*rowsPerTablePage[container])+rowCount;
					
					// break out of loop if rowIndex exceeds the number of rows
					if(rowIndex>=containerNode.tBodies[0].rows.length) break;

					// hide row
					checkDisplayClass(containerNode.tBodies[0].rows[rowIndex], 'doNotDisplay');
				}
			}
		}
	}
	
	switchDisplayedElements(container);
		
//	update the navigation links
	writeNavigationLinks(container);
	writePaginationCount(container);
}


/*
	write a list of links to the different pages inside a paginated container
*/

function writeNavigationLinks(container, showAll)
{
	var navHTML = '';
	var pageCount = 0;
	
	// get number of pages
	if(containerNode.nodeName=='DIV')
	{
		pageCount = paginatedElements[container].length;
	}
	else pageCount = Math.ceil(containerNode.tBodies[0].rows.length/rowsPerTablePage[container]);
	

//	output navigation if user hasn't opted to show all pages
	if(showAll == null)
	{
		if (pageCount > 1)
		{
			//	add the 'Previous' link
			if (currentPage[container] > 0)
			{
				navHTML += '<li class="previous"><a href="#' + container + '" onClick="showPage(\''+ container + '\', ' + (currentPage[container]-1) +'); return false;">prev</a></li>';
			}
			
			//	add numbered links for each page
			for (elementsCount=0; elementsCount<pageCount; elementsCount++)
			{
				if (elementsCount != currentPage[container])
				{
					navHTML += '<li><a href="#' + container + '" onClick="showPage(\'' + container + '\', ' + elementsCount + '); return false;">'+ (elementsCount+1) +'</a></li>';
				}
				else
				{
					navHTML += '<li>' + (elementsCount+1) + '</li>';
				}
			}
			
			//	add the 'Next' link
			if (currentPage[container] < (pageCount-1))
			{
				navHTML += '<li class="next"><a href="#' + container + '" onClick="showPage(\''+ container + '\', ' + (currentPage[container]+1) +'); return false;">next</a></li>';
			}
			
			//	add the 'Show All' link
			navHTML += '<li class="all"><a href="#' + container + '" onClick="displayAll(\'' + container + '\'); return false">show all</a>';
		}
	}
//	add the 'Show pagination' link
	else navHTML += '<li><a href="#' + container + '" onClick="displayPaginated(\'' + container + '\'); return false">show paginated</a>';
	
	
	//	write the set of links into all navigation elements in the container
	for (count=0; count<paginationNavElements[container].length; count++)
	{
		document.getElementById(paginationNavElements[container][count].id).innerHTML = navHTML;
	}
}