/**
 * KnowledgePool Integration Engine
 * Functions that encapsulate the content hierarchy, progress indicators etc.
 *
 * @author Jez
 * @date 29 April 2001
 * @version 1.10
 */

// -----------------------------------------------------------------------------
// Global Variables
// -----------------------------------------------------------------------------

/** Globally visible variable to show KPIE navigation module is loaded */
var KPIE_Navigation_Loaded = true;
/** Contains the size of the progress map block.  2 = 3 pages, 4 = 15 pages, 8 = 255 pages etc */
var blockBitmapSize = 8;

// -----------------------------------------------------------------------------
// Objects
// -----------------------------------------------------------------------------

/**
 * KPIE_Content
 * Constructs a new course object.
 *
 * @param	name		String name of the content
 * @param	api		Object API object
 * @param	launchWindow	Window to start running checks from...
 * @return	new object representing content
 */

function KPIE_Content( name, api, launchWindow )
{

	// Properties
	/** Name of the content */
	this.name = name;
	/** Array containing all the section objects in the content */
	this.sections = new Array();
	/** Holds the current section number */
	this.currentSection = 0;
	/** Holds the window that contains the KPIE content API code */
	this.contentAPIWindow = findKPIEContentAPIWindow( launchWindow );
	/** ContentAPI object */
	this.contentAPI = api;

	// Methods
	this.addSection = KPIE_AddSection;
	this.setCurrentSection = KPIE_SetCurrentSection;
	this.nextPage = KPIE_NextPage;
	this.prevPage = KPIE_PrevPage;
	this.prevBlock = KPIE_PrevBlock;
	this.nextBlock = KPIE_NextBlock;
	this.markCurrentPage = KPIE_MarkCurrentPage;
	this.getCurrentPageFilename = KPIE_GetCurrentPageFilename;
	this.setBlockInCurrentSection = KPIE_SetBlockInCurrentSection;
	this.finish = KPIE_Finish;
	this.getProgressMap = KPIE_GetProgressMap;
	this.setProgressMap = KPIE_SetProgressMap;

	// Set up comms with the LMS API

	if ( this.contentAPI.LMSInitialize( "" ) == "false" )
	{

//	alert( "Progress tracking cannot take place as communication with a suitable API cannot be achieved" );

	}

}

/**
 * KPIE_AddSection
 * Adds a section to a course
 *
 * @param	name	String	section name
 * @return void
 */

function KPIE_AddSection( name )
{

	this.sections[this.sections.length] = new KPIE_Section( name );

}

/**
 * KPIE_SetCurrentSection
 * Sets the current section
 *
 * @param  sectionNumber	Number	section number.  This is zero indexed
 * @return boolean representing successful change
 */

function KPIE_SetCurrentSection( sectionNumber )
{

	if ( sectionNumber >=0 && sectionNumber < this.sections.length )
	{

		this.currentSection = sectionNumber;
		this.sections[sectionNumber].setCurrentBlock(0);
		return true;
	}

	return false;

}

/**
 * KPIE_IsContentComplete
 * Indicates if the content is complete - i.e. have all the sections been completed?
 * 
 * @return boolean indicting if the content has been completed
 */

function KPIE_IsContentComplete()
{

	var sectionCount;
	var complete = true;

	// This loop will break if the section has not been completed.  If it loops
	// through sucessfully then the complete flag will remain true.

	for ( sectionCount = 0; complete && sectionCount < this.sections.length; sectionCount++ )
	{

			complete = this.sections[sectionCount].isSectionComplete();
	}

	return complete;

}

/**
 * KPIE_NextPage
 * Goes to the next page
 *
 * @return void
 */
function KPIE_NextPage()
{

	var currSect = this.currentSection;
	var currBlk = this.sections[currSect].currentBlock;
	var currPage = this.sections[currSect].blocks[currBlk].currentPage;

	// JEZ - 03OCT2001
	// Added next line...
	this.markCurrentPage( true );

	this.sections[currSect].blocks[currBlk].setCurrentPage( currPage + 1 );
}

/**
 * KPIE_PrevPage
 * Goes to the previous page
 *
 * @return void
 */
function KPIE_PrevPage()
{

	var currSect = this.currentSection;
	var currBlk = this.sections[currSect].currentBlock;
	var currPage = this.sections[currSect].blocks[currBlk].currentPage;

	// JEZ - 03OCT2001
	// Added next line...
	this.markCurrentPage( true );

	this.sections[currSect].blocks[currBlk].setCurrentPage( currPage - 1 );

}

/**
 * KPIE_PrevBlock
 * Goes to the last page in the previous block
 *
 * @return void
 */
function KPIE_PrevBlock()
{

	var currSect = this.currentSection;
	var currBlk = this.sections[currSect].currentBlock - 1;

	// JEZ - 03OCT2001
	// Added next line...
	this.markCurrentPage( true );

	this.setBlockInCurrentSection( currBlk );
	this.sections[currSect].blocks[currBlk].setCurrentPage( this.sections[currSect].blocks[currBlk].pages.length - 1 );

}

/**
 * KPIE_NextBlock
 * Goes to the first page in the next block
 *
 * @return void
 */
function KPIE_NextBlock()
{

	var currSect = this.currentSection;
	var currBlk = this.sections[currSect].currentBlock + 1;

	// JEZ - 03OCT2001
	// Added next line...
	this.markCurrentPage( true );

	this.setBlockInCurrentSection( currBlk );
//	this.sections[currSect].blocks[currBlk].setCurrentPage( 0 );

}

/**
 * KPIE_MarkCurrentPage
 * Sets the visited flag for the current page
 *
 * @param	flag	boolean	visited or not visited
 * @return void
 */
function KPIE_MarkCurrentPage( flag )
{

	var currSect = this.currentSection;
	var currBlk = this.sections[currSect].currentBlock;
	var currPage = this.sections[currSect].blocks[currBlk].currentPage;

	this.sections[currSect].blocks[currBlk].pages[currPage].setVisited( flag );

}

/**
 * KPIE_GetCurrentPageFilename
 * Returns the filename of the current page
 *
 * @return String	Filename
 */
function KPIE_GetCurrentPageFilename()
{

	var currSect = this.currentSection;
	var currBlk = this.sections[currSect].currentBlock;
	var currPage = this.sections[currSect].blocks[currBlk].currentPage;

	return this.sections[currSect].blocks[currBlk].pages[currPage].filename;

}

/**
 * KPIE_SetBlockInCurrentSection
 * Sets the block number in the current sections
 *
 * @param		blockNumber	number	block number to set
 * @return void
 */
function KPIE_SetBlockInCurrentSection( blockNumber )
{

	var currSect = this.currentSection;
	this.sections[currSect].setCurrentBlock( blockNumber );

}

/**
 * KPIE_Finish
 * Close down the content
 *
 * @return void
 */

function KPIE_Finish()
{

	this.contentAPI.LMSFinish( "" );

}

/**
 * KPIE_GetProgressMap
 * Returns a map of the visitied state of the content
 *
 * @return string containing map information
 */

function KPIE_GetProgressMap()
{

	var progress = new String( "" );

	var s = 0;
	var b = 0;
	var p = 0;
	var bitmap = 0;

	// Go through each section, block and page examining the visited flag.
	// The returned string is made up of a series of 16 bit hex values representing a bit map of the 
	// visitied pages in the block.

	for ( s = 0; s < this.sections.length; s++ )
	{

		for ( b = 0; b < this.sections[s].blocks.length; b++ )
		{

			bitmap = 0;

			for ( p = 0; p < this.sections[s].blocks[b].pages.length; p++ )
			{

				if ( this.sections[s].blocks[b].pages[p].visited )
				{
					// Since JS doesn't have a "power" function then call our one...
					bitmap += power( 2, p );

				}

			}

			progress += toHex( bitmap, blockBitmapSize );

		}

	}

	return progress;

}

/**
 * KPIE_SetProgressMap
 * Returns a map of the visitied state of the content
 *
 * @param	progress string contains the existing progress map
 * @return void
 */

function KPIE_SetProgressMap( progress )
{

	var working = new String( progress );
	var offset = 0;
	var blockMap = 0;

	// Loop through each section and block then set the pages according to the progress map.
	// Note! this does assume the following:
	//		- the length of the progress string = blockBitmapSize * total number of pages
	//		- the progress map was hex coded with values padded to four characters
	//		- there structure of the content is the same as the when the progress was coded.

	for ( s = 0; s < this.sections.length; s++ )
	{

		for ( b = 0; b < this.sections[s].blocks.length; b++ )
		{

			// Get the next block map
			blockMap = parseInt( working.substr( offset, blockBitmapSize ), 16 );
			offset += blockBitmapSize;

			for ( p = 0; p < this.sections[s].blocks[b].pages.length; p++ )
			{
				this.sections[s].blocks[b].pages[p].visited = ( ( blockMap & ( power( 2, p ) ) ) > 0 ? true : false );
			}

		}

	}

	
}

/**
 * KPIE_Section
 * Constructs a new section
 *
 * @param name String section name
 * @return new object representing a section
 */

function KPIE_Section( name )
{

	// Properties
	/** name of the section */
	this.name = name;
	/** array containing all the blocks in the section */
	this.blocks = new Array();
	/** holds the current block number */
	this.currentBlock = 0;
	/** shows if the section has been visited */
	this.visited = false;

	// Methods
	this.addBlock = KPIE_AddBlock;
	this.setCurrentBlock = KPIE_SetCurrentBlock;
	this.isLastPage = KPIE_IsLastPage;
	this.isSectionComplete = KPIE_IsSectionComplete;

}

/**
 * KPIE_AddBlock
 * Adds a block to a section
 *
 * @param	name String block name
 * @return void
 */

function KPIE_AddBlock( name)
{

	this.blocks[ this.blocks.length ] = new KPIE_Block( name );

}

/**
 * KPIE_SetCurrentBlock
 * Sets the current block
 *
 * @param  blockNumber	Number	block number.  This is zero indexed
 * @return boolean representing successful change
 */

function KPIE_SetCurrentBlock( blockNumber )
{

	if ( blockNumber >=0 && blockNumber < this.blocks.length )
	{

		this.currentBlock = blockNumber;
		this.blocks[blockNumber].setCurrentPage(0);
		return true;
	}

	return false;

}

/**
 * KPIE_IsSectionComplete
 * Indicates if the section is complete - i.e. have all the blocks been completed?
 * 
 * @return boolean indicting if the section has been completed
 */

function KPIE_IsSectionComplete()
{

	var blockCount;
	var complete = true;

	// This loop will break if the block has not been completed.  If it loops
	// through sucessfully then the complete flag will remain true.

	for ( blockCount = 0; complete && blockCount < this.blocks.length; blockCount++ )
	{

			complete = this.blocks[blockCount].isBlockComplete();

	}

	return complete;

}

/**
 * KPIE_IsLastPage
 * Indicates if the current page is the last page in the section.
 * 
 * @return boolean indicting if the current page is the last page
 */
function KPIE_IsLastPage()
{

	if ( ( this.blocks.length == this.currentBlock + 1 ) && ( this.blocks[this.currentBlock].pages.length == this.blocks[this.currentBlock].currentPage + 1 ) )
	{
		return true;
	}

	return false;

}

/**
 * KPIE_Block
 * Constructs a new block
 *
 * @param name String name of the block
 * @return new object representing a block
 */

function KPIE_Block( name )
{

	// Properties
	/** block name */
	this.name = name;
	/** array containing the pages in the block */
	this.pages = new Array();
	/** current page */
  this.currentPage = 0;
	
	// Methods
	this.addPage = KPIE_AddPage;
	this.setCurrentPage = KPIE_SetCurrentPage;
	this.isBlockEnd = KPIE_IsBlockEnd;
	this.isBlockComplete = KPIE_IsBlockComplete;

}

/**
 * KPIE_AddPage
 * Adds a page to a block
 *
 * @param fileName String filename of the page
 * @return new object representing a page
 */

function KPIE_AddPage( fileName )
{

	this.pages[ this.pages.length ] = new KPIE_Page( fileName );

}

/**
 * KPIE_SetCurrentPage
 * Sets the current page
 *
 * @param  pageNumber	Number	page number.  This is zero indexed
 * @return boolean representing successful change
 */

function KPIE_SetCurrentPage( pageNumber )
{

	if ( pageNumber >=0 && pageNumber < this.pages.length )
	{

		this.currentPage = pageNumber;

		// -----------------------------------------
		// JEZ - 03OCT2001
		// The line below was commented out to prevent a bug that 
		// became apparent if a block only had one page - basically the
		// block would be shown as complete even if the page had not been
		// viewed.  The code now sets the visited flag on the prev/next page and block
		// functions
		//
		// this.pages[pageNumber].setVisited( true );
		// ------------------------------------------
		return true;

	}

	return false;

}

/**
 * KPIE_IsBlockEnd
 * Indicates if the current page is at the block end
 * 
 * @return boolean indicting if the page is at the block end
 */

function KPIE_IsBlockEnd()
{

	return ( ( this.currentPage + 1 ) == this.pages.length );

}

/**
 * KPIE_IsBlockComplete
 * Indicates if the block is complete - i.e. have all the pages been accessed?
 * 
 * @return boolean indicting if the block has been completed
 */

function KPIE_IsBlockComplete()
{

	var pageCount;
	var complete = true;

	// This loop will break if the page has not been visited.  If it loops
	// through sucessfully then the complete flag will remain true.

	for ( pageCount = 0; complete && pageCount < this.pages.length; pageCount++ )
	{
			complete = this.pages[pageCount].visited;
	}

	return complete;

}

/**
 * KPIE_Page
 * Constructs a new page
 *
 * @param filename 	String	filename of the page
 * @return new object representing a page
 */

function KPIE_Page( filename )
{

	// Properties
	/** page filename */
	this.filename = filename;
	/** indicates if the page has been visited */
	this.visited = false;

	// Methods
	this.setVisited = KPIE_SetPageVisited;

}

/**
 * KPIE_SetPageVisited
 * Sets the current page visited property
 *
 * @param  visitedFlag	Boolean	indicates if the page has been visited
 * @return boolean new setting of the page visited property
 */

function KPIE_SetPageVisited( visitedFlag )
{

	this.visited = visitedFlag;
	return this.visited

}

// -----------------------------------------------------------------------------
// Functions
// -----------------------------------------------------------------------------

/**
 * findKPIEContentAPIWindow
 * Searches for the window that contains the KnowledgePool content tracking
 * API.  This can then be assigned to a variable.
 *
 * @param		chkWin	Object	Window to check.  The function is called recursively
 * @return	an object representing the window in which the KnowledgePool
 *					content tracking API sits.  This will be null if the API cannot be found.
 */

function findKPIEContentAPIWindow( chkWin )
{

	var i = 0;
	var currWindow = chkWin;
	var foundIt = null;

	if ( currWindow.KPIE_contentAPI_Loaded != null)
	{

		return currWindow;

	}

	for ( i = 0; foundIt == null && i < currWindow.frames.length; i++ )
	{
		foundIt = findKPIEContentAPIWindow( currWindow.frames[i] );

	}

	return foundIt;	

}

/**
 * power
 * Applies a power to the value supplied
 *
 * @param		value	number number to use
 * @param		powerValue number power to apply
 * @return	number containing result
 */

function power( value, powerValue )
{

	var multiplicator = value;
	var i;

	if (powerValue == 0)
	{

		return 1;

	}

	if (powerValue == 1)
	{

		return value;

	}
	
	for ( i = 2; i <= powerValue; i++)
	{

		multiplicator *= value;

	}

	return multiplicator;

}
	
/**
 * toHex
 * Converts a number to a hex value
 *
 * @param		value			number to number convert
 * @param		padLength number padded length to return
 * @return	string containing converted value
 */

function toHex( value, padLength )
{

	var converted = 0;		// amount that has been converted
	var mask = 15;				// bit mask (set to first nibble)
	var hex = "";					// hex string to build up
	var nibble = 0;				// Four bits
	var shifter = 0;			// Mask shift multiplier
	var i;
	var hexValues = [ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F" ];

	while ( converted != value )
	{

		// Get the next nibble
		nibble = ( value & mask );

		// Shift the nibble down to represent a value 0 to 15
		// Use that as an index into the hex values array
		hex = hexValues[ ( nibble >> shifter ) ] + hex;

		// Add the nibble value to the converted amount
		converted += nibble;

		// Shift the mask to the next nibble
		mask = mask << 4;

		// change the shifter
		shifter += 4;

	}
		

	// Do the left padding

	var hexlen = hex.length;

	for ( i = hexlen; i < padLength; i++ )
	{
		hex = "0" + hex;
	}

	return hex;

}


/**
 * doEndbutton
 * displays end button in left hand menu
 * also deletes old cookies if course is running in SVM
 */

function doEndbutton()	{

var apiAdapter = ""; 

if ( objKPIEContentAPI !=null ) 
{ 
        apiAdapter = objKPIEContentAPI.mappedLMSID;  

	if ( apiAdapter != "NULL" ) 
	{ 
		if (apiAdapter == "SVM" )	
		{
			document.write("<a href='svmend.htm' target='_top' onclick='doSVMunload();'><img name='end' border='0' src='images/endup.gif' width='132' height='20'></a>");
			getBail();
		}
		else	{
       			document.write("<a href='javascript:void(0);' onClick='finish();'><img name='end' border='0' src='images/endup.gif' width='132' height='20'></a>");
   			}
	} 

else	{
        apiAdapter = objKPIEContentAPI.mappedLMSID;

		if ( apiAdapter == "NULL" ) 
			{
			document.write("<a href='javascript:top.close();' onMouseOver=\"act('butt1')\" onMouseOut=\"inact('butt4')\"><img src='images/endup.gif' name='butt4' width='152' height='20' border='0'></a>");	
			}
	}
  } 

}
function doSVMunload()
{
	var apiAdapter = ""; 
	apiAdapter = objKPIEContentAPI.mappedLMSID;

	if ( apiAdapter == "SVM" )  
		{
       	 doUnloading();
		}
}



