/*
####################################################################################################
# NAVIGATION BAR OBJECT
# Version: 1.0.2 | Last Update: 19 May 2009 | Created On: 11 March 2009
####################################################################################################
# Copyright (C) by E-Muze (www.E-Muze.net)
# Copying the content of this file or fragments of this file's content with individual 
# functionality is strictly forbidden without the written consent of the author. Using this code
# as an example in order to understand and learn from it, with the purpose of improving your own
# work and/or writing your own code with similar functionality, is allowed and encouraged.
####################################################################################################
# NO DEPENDENCIES
####################################################################################################
# DOCUMENTATION
# To navBar object must be instanced after the html element which will contain the navigation bar
# has been defined. The navBar class will take 3 primary parameters:
# First parameter is the ID of the html element that will contain the bar. If this is provided
# as a string, its value will be used as it is, otherwise, if provided as an array, the object
# will consider each value as an individual container element id, and it will clone the navigation
# bar html within each of those containers, this is especially useful for the cases when you need
# the navigation bar both at the top and at the bottom of the list.
# Second parameter is an integer number which represents the navigation bar index, this is used to 
# allow multiple instances of the nav bar working on the same page.
# The third parameter, CFG, is an object containing the navigation bar configuration, details about
# each configuration parameter can be found at the bottom of this script, as comments, within the 
# loadConfig method.
####################################################################################################
# NO KNOWN UNFIXED PROBLEMS
####################################################################################################
# INCOMPLETE FEATURES:
# multi-theme not implemented
####################################################################################################
*/

var navBarTracker = new Array ();
function navBar (containerIds, navIdx, CFG)
{
	//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
	// OBJECT PROPERTIES
	
	var self = this; // bind reference to self
	
	this.CFG = null; // container for local configuration parameters
	
	this.navIdx = (navIdx > 0) ? navIdx : 1; // index of nav bar
	this.contList = new Array (); // lsit of references to nav bar containers
	this.pageHolder = null; // reference to page holder
	this.pageList = new Array (); // page list container
	this.midPoint = 2; // default midpoint for a size 3 bar
	this.listOffset = 0; // list offset
	this.selected = 0; // selected page
	
	this.btnFirst = null; // reference to goto first button
	this.btnLast = null; // reference to goto last button
	
	//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
	// OBJECT INIT
	
	this.init = function (CFG)
	{
		// add to navbar tracker
		if (navBarTracker[this.navIdx])
		{
			alert ('Failed to init navigation bar with id #' + this.navIdx + '. Nav bar object already exists for this id.');
			return false;
		}
		else navBarTracker[this.navIdx] = this;
		
		// load configuration params
		this.loadConfig (CFG);
		
		// get selected page
		this.selected = this.CFG.currentPage;
		
		// if single container was provided
		if (containerIds.constructor.toString().indexOf ("Array") == -1)
		{
			// convert to array
			var aux = [containerIds];
			containerIds = aux;
		}
		
		// prepare each container
		for (var idx in containerIds)
		{
			if (containerIds[idx] && document.getElementById (containerIds[idx]))
			{
				// get container index
				var cnt_idx = this.contList.length;
				
				// create object to store container
				this.contList[cnt_idx] = {idx : cnt_idx, ref : document.getElementById (containerIds[idx]), pageHolder : null, btnFirst : null, btnLast : null};
				
				// prepare container and holder and bind references
				this.contList[cnt_idx].ref.innerHTML = '<div class="nav_first_off" id="nav_btn_first_' + this.navIdx + '_' + cnt_idx + '">&nbsp;</div><div class="nav_pages" id="nav_page_holder_' + this.navIdx + '_' + cnt_idx + '">&nbsp;</div><div class="nav_last_off" id="nav_btn_last_' + this.navIdx + '_' + cnt_idx + '">&nbsp;</div>';
				eval ('this.contList[cnt_idx].pageHolder = document.getElementById (\'nav_page_holder_' + this.navIdx + '_' + cnt_idx + '\')');
				eval ('this.contList[cnt_idx].btnFirst = document.getElementById (\'nav_btn_first_' + this.navIdx + '_' + cnt_idx + '\')');
				eval ('this.contList[cnt_idx].btnLast = document.getElementById (\'nav_btn_last_' + this.navIdx + '_' + cnt_idx + '\')');
				
				// setup events
				this.contList[cnt_idx].btnFirst.onclick = this.navClickFirst;
				this.contList[cnt_idx].btnLast.onclick = this.navClickLast;
			}
		}
		
		// make sure size is at least 3 and an uneven integer
		if (this.CFG.size <= 3) this.CFG.size = 3;
		else if (!(this.CFG.size % 2)) this.CFG.size ++;
		
		// get midpoint for current bar size
		this.midPoint = Math.ceil (this.CFG.size / 2);
		
		// if midpoint exceeds total number of available pages, lower it
		if (this.midPoint > this.CFG.totalPages) this.midPoint = this.CFG.totalPages;
		
		// update nav bar
		this.navUpdate ();
	};
	
	//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
	// NAV UPDATE METHOD
	
	this.navUpdate = function ()
	{
		// calculate listing offset
		self.listOffset = self.selected - self.midPoint;
		
		// create list of pages
		for (var i = 0; i < self.CFG.size; i ++)
		{
			var pageNumber = self.listOffset + i + 1;
			if (pageNumber <= 0 || pageNumber > self.CFG.totalPages) pageNumber = '';
			
			var aux = 'nav_page_off';
			if (self.selected == (self.listOffset + i + 1)) aux = 'nav_page_current';
			else if (pageNumber) aux = 'nav_page_on';
			
			self.pageList [i] = '<div class="' + aux + '" onclick="navBarTracker[' + self.navIdx + '].navClickSlot (' + (pageNumber ? pageNumber : 'null') + ');">' + pageNumber + '</div>';
		}
		
		// update for each container
		for (var idx in self.contList)
		{
			if (self.contList[idx])
			{
				// fill page holder
				self.contList[idx].pageHolder.innerHTML = self.pageList.join ("\n");
				
				// update capping buttons
				self.contList[idx].btnLast.className = ((self.CFG.totalPages - self.listOffset) < self.CFG.size) ? 'nav_last_off' : 'nav_last_on';
				self.contList[idx].btnFirst.className = (self.listOffset > 0) ? 'nav_first_on' : 'nav_first_off';
			}
		}
	}
	
	//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
	// SELECT PAGE METHOD
	
	this.selectPage = function (pageNumber, forcedUpdate)
	{
		if (self.processCallback (pageNumber) || forcedUpdate)
		{
			self.selected = pageNumber;
			self.navUpdate ();
		}
	}
	
	//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
	// PROCESS CALLBACK METHOD
	
	this.processCallback = function (pageNumber)
	{
		// if callback is set
		if (self.CFG.callback)
		{
			// if callback returns positive, continue with page selection, otherwise stop page selection
			if (self.CFG.callback (pageNumber, self, self.CFG.callback_aux)) return true;
			else return false
		}
		// else continue with page selection
		else return true;
	}
	
	//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
	// NAV CLICK SLOT METHOD
	
	this.navClickSlot = function (pageNumber)
	{
		if (pageNumber && self.processCallback (pageNumber))
		{
			self.selected = pageNumber;
			self.navUpdate ();
		}
	}
	
	//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
	// NAV CLICK FIRST METHOD
	
	this.navClickFirst = function ()
	{
		if (this.className == 'nav_first_on' && self.processCallback (1))
		{
			self.selected = 1;
			self.navUpdate ();
		}
	}
	
	//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
	// NAV CLICK LAST METHOD
	
	this.navClickLast = function ()
	{
		if (this.className == 'nav_last_on' && self.processCallback (self.CFG.totalPages))
		{
			self.selected = self.CFG.totalPages;
			self.navUpdate ();
		}
	}
	
	//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
	// LOAD CONFIG
	
	/*
		This method loads the configuration parameters from the given configuration array and uses defaults where values are not set.
	*/
	
	this.loadConfig = function (CFG)
	{
		if (!CFG) CFG = new Object ();
		
		this.CFG = 
		{
			size : this.def (CFG.size, 3), // size of navigator (in visible pages), must be uneven number, must be greater than 3
			totalPages : this.def (CFG.totalPages, 1), // number of total pages
			currentPage : this.def (CFG.currentPage, 1), // current page
			callback : this.def (CFG.callback, null), // function to be called when nav is used
			callback_aux : this.def (CFG.callback_aux, null), // aux parameters to be passed to callback
			theme : this.def (CFG.theme, 'nav_default') // name of the nav bar theme (optional)
		};
	};
	
	//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
	// DEF
	
	/*
		Method used to return the value if it is valid (including zeros) or return the default when value is undefined or non existing
	*/
	
	this.def = function (value, def)
	{
		if (value === 0 || (value && value != undefined)) return value;
		else return def;
	};
	
	//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
	// INIT THE OBJECT AUTOMATICALLY
	
	this.init (CFG);
}
	
/*
####################################################################################################
*/