//  START - Tracking Stack 
// this is a stack implementation
function TrackingStack()
{
	// containers
	// a stack for the tracking entries ( no duplication, index moving)
	this.TrackingStackContainer	   		= new Array();
	
	// Tracking container functions
	this.PushEntry  			= PushEntry;
	this.PopEntry   			= PopEntry;
	this.GetEntry   			= GetEntry;
	this.PopEntryByIndex 		= PopEntryByIndex;
	this.GetEntryByIndex 		= GetEntryByIndex;
	this.PopEntryByIndex 		= PopEntryByIndex;
	this.GetEntryIndexByValue	= GetEntryIndexByValue;
	this.emptyStack  			= emptyStack;
	this.sliceStack				= sliceStack;
	this.size					= size;

}

// Adds Entry to the Container
function PushEntry(newEntry)
{
	this.TrackingStackContainer.push(newEntry);
}

// Retrieves last Entry and removes it from the container
function PopEntry()
{
	return this.TrackingStackContainer.pop();
}

// Retrieves last Entry WITHOUT removes it from the container
function GetEntry()
{
	return this.TrackingStackContainer[this.TrackingStackContainer.length-1];
}

// Retrieves the required Entry and removes all the 
// entries following it.
function PopEntryByIndex(index)
{
	return this.TrackingStackContainer.splice(index , 1)[0];	
}

// Retrieves Entry in the recieved index WITHOUT removing it from the container
function GetEntryByIndex(index)
{
	return this.TrackingStackContainer[index];
}

// Retrieves Entry index in the recieved index WITHOUT removing it from the container
function GetEntryIndexByValue(TrackingEntryForSearch)
{
	var index = 0;
	while(index< this.TrackingStackContainer.length)
	{
		//if(this.TrackingStackContainer[index] == TrackingEntryForSearch)
		if(this.TrackingStackContainer[index].getUrl() == TrackingEntryForSearch.getUrl() && this.TrackingStackContainer[index].postBody == TrackingEntryForSearch.postBody)
			return index;
		++index;
	}
	return -1;
}

function sliceStack(begin , end)
{
	this.TrackingStackContainer.splice(begin , end-begin);
}

// Clears all Entries Container
function emptyStack(index)
{
	return this.TrackingStackContainer[index];
}

function size()
{
	return this.TrackingStackContainer.length;
}

//  END - Tracking container



// START - History Framework 

function HistoryFramework()
{
	this.lastVisitedTitleParam = "";
	// containers
	// a stack for the tracking entries ( no duplication, index moving)
	this.TrackingEntries	   		= new TrackingStack;
	// a stack for the last visited entries
	this.LastVisitedEntries	   		= new TrackingStack;
	
	this.NoDuplicationArray			= new Array();
	
	//members
	this.ActiveTrackingEntryIndex				= -1;
	
	// General functions
	this.saveLastVisitedTitle			= saveLastVisitedTitle;
	this.getLastVisitedTitle			= getLastVisitedTitle;
	this.AddTrackingEntry				= AddTrackingEntry;
	this.AddTrackingEntryAllowDuplicate	= AddTrackingEntryAllowDuplicate;
	this.SetActiveTrackingEntryIndex	= SetActiveTrackingEntryIndex;
	this.GetTrackingListPreActive		= GetTrackingListPreActive;
	this.GetTrackingListPostActive		= GetTrackingListPostActive;
	this.GetActiveTrackingEntryIndex	= GetActiveTrackingEntryIndex;
	this.GetActiveTrackingEntryValue	= GetActiveTrackingEntryValue;
	this.TrackingEntryBack				= TrackingEntryBack;
	this.TrackingEntryForward			= TrackingEntryForward;
	this.GetLastVisitedList				= GetLastVisitedList;
	this.CutLastVisitedListByIndex		= CutLastVisitedListByIndex;
	this.AddToLastVisitedList			= AddToLastVisitedList;
	this.GetEntryIndexByValue			= FrameworkGetEntryIndexByValue;
	this.GetEntryValueByIndex			= GetEntryValueByIndex;
	this.Navigate						= NavigateToHistory;
	this.checkLength					= checkLength;

}

//save the last visited object title
function saveLastVisitedTitle(title)
{
	this.lastVisitedTitleParam = title;
}

// Adds New Entry as the current Nav Target
function AddTrackingEntry(newTrackingEntryUrl , newTrackingEntryTitle , newTrackingEntryParams , newTrackingEntryContext, newPathIndexes, newFullEntryUrl, postParams)
{
	if (postParams!=null)
	{
		EPCM.getSAPTop().postBody = parsePostParams(EPCM.getSAPTop().postBody, postParams);
	}

	this.saveLastVisitedTitle(newTrackingEntryTitle);
	if(newTrackingEntryUrl != null && newTrackingEntryUrl != "")
	{
		// Adding the new comparable variable since the previous tracking entry might contain the "&" character
		var comparableNewTrackingEntryParams = newTrackingEntryParams;
		
		// only if the new one is not the last one (avoid duplication on refresh...)
		if(this.TrackingEntries.size() == 0 || (newTrackingEntryUrl + comparableNewTrackingEntryParams) != this.TrackingEntries.GetEntryByIndex(this.TrackingEntries.size()-1).getUrl()) 
		{
			var newTrackingObject = new HistoryEntry(newTrackingEntryUrl , newTrackingEntryTitle , newTrackingEntryParams , newTrackingEntryContext , EPCM.getSAPTop().postBody, newPathIndexes,newFullEntryUrl);
			// if entry doesn't exists adding to the tracking stack
			var indexOf = this.TrackingEntries.GetEntryIndexByValue(newTrackingObject);
			if(indexOf == -1)
			{
				
				
				// cut the list if needed
				if(this.TrackingEntries.size() > this.GetActiveTrackingEntryIndex()+1)
					this.TrackingEntries.sliceStack(this.GetActiveTrackingEntryIndex()+1 , this.TrackingEntries.size());
					
				this.checkLength();
				this.TrackingEntries.PushEntry(newTrackingObject);
				this.SetActiveTrackingEntryIndex(this.TrackingEntries.size()-1);
			}
			else
			{
				this.SetActiveTrackingEntryIndex(indexOf);
			}
		}
		else
		{
			this.SetActiveTrackingEntryIndex(this.TrackingEntries.size()-1);
		}
		// adding to the last visited stack
		var newBreadObject = new  HistoryEntry(newTrackingEntryUrl , newTrackingEntryTitle , newTrackingEntryParams , newTrackingEntryContext , EPCM.getSAPTop().postBody, newPathIndexes,newFullEntryUrl);
		this.AddToLastVisitedList(newBreadObject);
		
	}
}

// Adds New Entry as the current Nav Target and allow duplication
function AddTrackingEntryAllowDuplicate(newTrackingEntryUrl , newTrackingEntryTitle , newTrackingEntryParams , newTrackingEntryContext, newPathIndexes,newFullEntryUrl, postParams)
{
	if (postParams!=null)
	{
		EPCM.getSAPTop().postBody = parsePostParams(EPCM.getSAPTop().postBody, postParams);
	}

	if(newTrackingEntryUrl != null && newTrackingEntryUrl != "")
	{
		this.saveLastVisitedTitle(newTrackingEntryTitle);
		// Adding the new comparable variable since the previous tracking entry might contain the "&" character
		var comparableNewTrackingEntryParams = newTrackingEntryParams;
		if (newTrackingEntryParams.length != 0 && newTrackingEntryParams.charAt(0)!='&')
		{
			comparableNewTrackingEntryParams = '&'+comparableNewTrackingEntryParams;
		}
		
		if(this.TrackingEntries.size() == 0 || (newTrackingEntryUrl + comparableNewTrackingEntryParams) != this.TrackingEntries.GetEntryByIndex(this.TrackingEntries.size()-1).getUrl()) 
		{
			var newTrackingObject = new HistoryEntry(newTrackingEntryUrl , newTrackingEntryTitle , newTrackingEntryParams , newTrackingEntryContext , EPCM.getSAPTop().postBody, newPathIndexes,newFullEntryUrl);
			
			// cut the list if needed
			if(this.TrackingEntries.size() > this.GetActiveTrackingEntryIndex()+1)
				this.TrackingEntries.sliceStack(this.GetActiveTrackingEntryIndex()+1 , this.TrackingEntries.size());
			
			this.checkLength();
			
			// if adding to the tracking stack (without duplication test)
			this.TrackingEntries.PushEntry(newTrackingObject);
			this.SetActiveTrackingEntryIndex(this.TrackingEntries.size()-1);
			
			
		}
		else
		{
			this.SetActiveTrackingEntryIndex(this.TrackingEntries.size()-1);
		}
		// adding to the last visited stack
		var newBreadObject = new  HistoryEntry(newTrackingEntryUrl , newTrackingEntryTitle , newTrackingEntryParams , newTrackingEntryContext , EPCM.getSAPTop().postBody, newPathIndexes, newFullEntryUrl);
		this.AddToLastVisitedList(newBreadObject);
	}
}


// 
function SetActiveTrackingEntryIndex(index)
{
	if(index >= 0 && index < this.TrackingEntries.size())
	{
		this.ActiveTrackingEntryIndex = index;
	}
}

// Retrieves the portion of the array preceding the active entry
function GetTrackingListPreActive()
{
	return this.TrackingEntries.TrackingStackContainer.slice(0, this.ActiveTrackingEntryIndex); 
} 

// Retrieves the portion of the array following the active entry
function GetTrackingListPostActive()
{
	return this.TrackingEntries.TrackingStackContainer.slice(this.ActiveTrackingEntryIndex + 1); 
}

function getLastVisitedTitle()
{
	return gHistoryFrameworkObj.lastVisitedTitleParam;
}

function GetActiveTrackingEntryIndex()
{
	return this.ActiveTrackingEntryIndex;
}

function GetActiveTrackingEntryValue()
{
	return this.TrackingEntries.TrackingStackContainer[this.GetActiveTrackingEntryIndex()];
}

// Decreasing active index and retrieves the new active value or null if impossible
function TrackingEntryBack()
{
	var activeIndex = this.GetActiveTrackingEntryIndex();
	
	// if back step is possible
	if(activeIndex != 0 )
	{
		--this.ActiveTrackingEntryIndex;
		var activeValue = this.GetActiveTrackingEntryValue();

		var newBreadObject = new  HistoryEntry(activeValue.URL , activeValue.title , activeValue.params , activeValue.context , activeValue.postBody, activeValue.pathIndexes);
		this.AddToLastVisitedList(newBreadObject);
		return activeValue;
	}
	else
	{
		return null;
	}
}

// Increasing active index and retrieves the new active value or null if impossible
function TrackingEntryForward()
{
	var activeIndex = this.GetActiveTrackingEntryIndex();
	
	// if forward step is possible
	if(activeIndex < this.TrackingEntries.size()-1 )
	{
		++this.ActiveTrackingEntryIndex;
		var activeValue = this.GetActiveTrackingEntryValue();
		var newBreadObject = new  HistoryEntry(activeValue.URL , activeValue.title , activeValue.params , activeValue.context , activeValue.postBody, activeValue.pathIndexes);
		this.AddToLastVisitedList(newBreadObject);
		return activeValue;
	}
	else
	{
		return null;
	}
}

// Retrives the full list of the last visited entries
function GetLastVisitedList()
{
	return this.LastVisitedEntries.TrackingStackContainer;
}

// Cuts the last visited from the index recieved + 1 and on
function CutLastVisitedListByIndex(index)
{
	if(index >= 0 && index < this.LastVisitedEntries.size())
	{
		var newStack = this.LastVisitedEntries.TrackingStackContainer.slice(0 , index+1);
		this.LastVisitedEntries.TrackingStackContainer = newStack;
	}
}

function AddToLastVisitedList(obj)
{
	if(this.LastVisitedEntries.size() > 0)
	{
		if(this.LastVisitedEntries.GetEntry().getUrl() == obj.getUrl())
			return;
	}
	this.LastVisitedEntries.PushEntry(obj);
}

function FrameworkGetEntryIndexByValue(obj)
{
	return this.TrackingEntries.GetEntryIndexByValue(obj)
}

function GetEntryValueByIndex(ind)
{
	var obj = null;
	var currentIndex = this.GetActiveTrackingEntryIndex();
	if(ind == 0)
	{
		obj = this.GetActiveTrackingEntryValue();
	}
	else if(ind > 0 )
	{
		var arr = this.GetTrackingListPostActive();
		if(ind > arr.length)
			return null;
		obj = arr[ind-1];
	}
	else
	{
		ind = -ind;
		var arr = this.GetTrackingListPreActive();
		if(ind > arr.length)
			return null;
		obj = arr[arr.length - ind];
		
	}
	return obj;
	
}
function NavigateToHistory(ind)
{
	var obj = null;
	var currentIndex = this.GetActiveTrackingEntryIndex();
	var obj = this.GetEntryValueByIndex(ind);
	// Reverting CL# 129815.
	// History doesn't work with this code - these two line are cutting the History
	// Stack each time user clicks on one of teh entries in History tray.
	//if(this.TrackingEntries.size() > currentIndex)
	//	this.TrackingEntries.sliceStack(currentIndex+1 , this.TrackingEntries.size());
	
	if(obj != null)
	{
		this.SetActiveTrackingEntryIndex(eval(currentIndex + ind));
		this.AddToLastVisitedList(obj);
		obj.navigate();
	}
}



function checkLength()
{
	if(this.TrackingEntries.size() > 12)
		this.TrackingEntries.PopEntryByIndex(0);
	if(this.LastVisitedEntries.size() > 12)
		this.LastVisitedEntries.PopEntryByIndex(0);
	
}

//parse post params that are comming in the form of "parmKey1=paramValue1;paramkey2=paramValue2" into an array 
//excludes session termination keys
function parsePostParams(pBody, postParams)
{
	var result = null;
	//collect all post params data

	if (postParams!=null)
	{
		//if postbody doesnt exist, create a new array, else add params to it.
		if (pBody==null || typeof pBody !='undefined')
		{
			result = new Array();
		}
		else
		{
			result = pBody;
		}
		
		//get session terminatinon keys in order not to save them.
		var innerPageData = EPCM.DSM.getTerminationData( false );
		var termDataBackup = EPCM.getSAPTop().termDataBackup;
		var data = EPCM.DSM.mergeTerminationData(termDataBackup, innerPageData);
		var param = postParams.split(';');
		var i=0;
		for (i=0; i<param.length; i++)
		{
			var indexOfEq = param[i].indexOf("=");
			if (indexOfEq>=0)
			{
				var paramName = param[i].substring(0,indexOfEq);
				var paramValue = param[i].substr(indexOfEq+1);
				var found = false;
				for (var sessionTerKey in data)
				{
					if (paramName==sessionTerKey)
					{
						found = true;
						break;
					}															
				}
				//dont save the DSM terminator attributes
				if (found==false)
				{
					result.push({name:paramName , value:paramValue});
				}
			}
		}
	}
	return result;	
}

//  END - General functions  //

var gHistoryFrameworkObj;

function init()
{
	gHistoryFrameworkObj = new HistoryFramework();
	EPCM.subscribeEvent('urn:com.sapportals:navigation','SaveLastVisitedTitle', onSaveLastVisitedTitle);
	EPCM.subscribeEvent('urn:com.sapportals:navigation','AddNavTarget', onNewNavigationTarget);
	EPCM.subscribeEvent('urn:com.sapportals:navigation','AddNavTargetAllowDuplicate', onNewNavigationTargetAllowDuplicate);
	EPCM.subscribeEvent('urn:com.sapportals:navigation','setNavTargetPanel', onNavPanelChanged);
	EPCM.subscribeEvent('urn:com.sapportals:navigation','historyNavigate', onHistoryNavigate);
	
}


function onSaveLastVisitedTitle(evt)
{
	gHistoryFrameworkObj.saveLastVisitedTitle(evt.dataObject.title);
}



function onNewNavigationTarget(evt)
{
	gHistoryFrameworkObj.AddTrackingEntry(evt.dataObject.URL , evt.dataObject.title , evt.dataObject.params, evt.dataObject.context, evt.dataObject.pathIndexes, evt.dataObject.fullURL, evt.dataObject.postParams);
}

function onNewNavigationTargetAllowDuplicate(evt)
{
	gHistoryFrameworkObj.AddTrackingEntryAllowDuplicate(evt.dataObject.URL , evt.dataObject.title , evt.dataObject.params, evt.dataObject.context, evt.dataObject.pathIndexes, evt.dataObject.fullURL, evt.dataObject.postParams);
	
}

function onNavPanelChanged(evt)
{
	if (gHistoryFrameworkObj.GetActiveTrackingEntryValue() != null)
		gHistoryFrameworkObj.GetActiveTrackingEntryValue().setNavPanelMode(evt.dataObject);
}

function onHistoryNavigate(evt)
{
	gHistoryFrameworkObj.Navigate(evt.dataObject);
}

init();

// History Entry 

function HistoryEntry(URL , title , params, context , postBody, pathIndexes, fullURL)
{
	// members
	this.URL	   			= URL.replace(/&/g,"%26");
	this.fullURL      = fullURL;
  this.title				= title;
	this.params 			= params;
	this.context 			= null;
	this.postBody			= null;
	this.pathIndexes = pathIndexes;
	
	if(context)
	{
		this.context = context.replace(/&/g,"%26");
	}	
	if(postBody)
		this.postBody = postBody
		
	this.getUrl = function()
	{
		if(this.params != null && this.params != ""){
		if (this.params.charAt(0) != '&')
			return this.URL + "&" +  this.params;
		else
			return this.URL + this.params;
		}
		else
			return this.URL;
	}
	
	this.navigate = function()
	{
		var mode = EPCM.getSAPTop().isPerson;
		if(typeof(mode) == 'undefined')
			mode = "2";
		var url = this.getUrl() + "&ExecuteLocally=true";
		EPCM.doNavigate(url,'','','',mode,'',this.context , this.postBody);
	}
	
	this.noDuplicateEntry = gHistoryFrameworkObj.NoDuplicationArray[this.getUrl()];
	if(! this.noDuplicateEntry)
	{
		this.noDuplicateEntry = new NoDuplicateEntry();
		gHistoryFrameworkObj.NoDuplicationArray[this.getUrl()] = this.noDuplicateEntry;
	}
	else
	{
	}
	
	this.setNavPanelMode = function(str)
	{
		this.noDuplicateEntry.navPanelMode = str;
	}
	
	this.getNavPanelMode = function()
	{
		return this.noDuplicateEntry.navPanelMode;
	}
	
	this.getPathIndexes = function(){
		return this.pathIndexes.split(',');
	}	
	this.getFullURL=function ()
	{
	   return this.fullURL;
  	}	
}


function NoDuplicateEntry()
{
	this.navPanelMode  		= null;
}

