//======================================================================
//  Knowledge Force Accelerator Course
//======================================================================


//======================================================================
//  Course Controller Classes
//======================================================================


/***********************************************************************
  This controller manages the launch- and result-handling for a
  course.
***********************************************************************/
var CCourseController = Class.create({

	m_objCourse : null,
	m_objProfileController : null,

	// used html document
	m_objDoc : null,

	// cache-map for variable texts
	m_mapTextCache : {},

	// additional params for all sequence start urls
	m_sAdditionalStartParams_lm : "",
	m_sAdditionalStartParams_am : "",

	m_sLastSequenceID : "",

	m_sELearningPopupParameter : "left=0,top=0,status=no,toolbar=no,menubar=no,location=no,resizable=yes,scrollbars=auto",

	options : {},

	// class names for DOM manipulation
	m_mapCSSClassNames : {
		"ProgressBar" : "divProgressBar",
		"ProgressResult" : "divProgressResult",
		"QualificationResult" : "divQualificationResult",
		"QualificationResultIcon" : "divQualificationResultIcon",
		"MasteryScore" : "divMasteryScore",
//		"CourseQualification" : "pCourseQualification",
		"CourseResult" : "pCourseResult",
		"CourseResultSuccess" : "pCourseResultSuccess",
		"CourseResultFailed" : "pCourseResultFailed",
		"ELearningStart" : "aELearningStart",
		"ELearningStartAS" : "aELearningStartAS",
		"ELearningResume" : "aELearningResume"
	},

	/***********************************************************************
		Constructor
	***********************************************************************/
	initialize : function( objCourse, options ) {

		this.options = {
			courseDocument : null,
			sMsgConfirmCloseOnOpenSco : null,
			sMsgLaunchOnOpenSco : null,
			sCloseForwardingUri : null,
			bRemoveHrefOnStartTags : false,
			sLaunchMode : "popup"
		};
		Object.extend(this.options, options || { });

		this.m_objCourse = objCourse;
		this.m_objProfileController = this.m_objCourse.getProfileController();
		this.m_objDoc = this.options.courseDocument ? this.options.courseDocument : document;

		this.initEventListener();
	},


	//======================================================================
	//	PUBLIC
	//======================================================================


	initEventListener : function() {

		Event.stopObserving( document, EVENT_COURSE_BUFFER_REFRESHED, this.refreshCourseView.bind(this) );
		Event.stopObserving( document, EVENT_COURSE_SEQUENCE_TERMINATED, this.onELearningTerminate.bind(this) );

		Event.observe( document, EVENT_COURSE_BUFFER_REFRESHED, this.refreshCourseView.bind(this) );
		Event.observe( document, EVENT_COURSE_SEQUENCE_TERMINATED, this.onELearningTerminate.bind(this) );
	},


	/***********************************************************************
	 * sets a dialog that will be confirm the termination of the course
	 * - parameter -
	 * sMsg:	message or null to disable the confirmation dialog
	***********************************************************************/
	setLmsDialogs : function( sMsgOpenSco, sMsgCloseCourse ) {
		this.options.sMsgLaunchOnOpenSco = unescape(sMsgOpenSco);
		this.options.sMsgConfirmCloseOnOpenSco = unescape(sMsgCloseCourse);
	},

	/***********************************************************************
	 * sets a uri that will be called on terminateCourse
	 *
	 * - parameter -
	 * sUri:	uri or null to disable the forwarding
	***********************************************************************/
	setForwardingOnTermination : function( sUri ) {
		this.options.sCloseForwardingUri = sUri;
	},

	/***********************************************************************
	 * sets the popup-window parameter for the ELearning view
	 * - parameter -
	 * sParameterList: parameter list (used with window.open function)
	***********************************************************************/
	setELearningPopupParameter : function( sParameterList ) {
		this.m_sELearningPopupParameter = sParameterList;
	},

	/***********************************************************************
	 * add parameters for all sequence start urls
	 * - parameter -
	 * sModes: comma separated list of TTPlayer specific wbt modes
	 *		"learning_mode" for learning mode
	 *		"assessment_mode" for assessment mode
	 * sHTMLParam : parameter to add
	***********************************************************************/
	addELearningStartParam : function( sModes, sHTMLParam ) {

		if( this.matchRegExp( sModes, CCourseController.RegExpListParamLearningMode ) ) {
			this.m_sAdditionalStartParams_lm += "&" + sHTMLParam;
		}

		if( this.matchRegExp( sModes, CCourseController.RegExpListParamAssessmentMode ) ) {
			this.m_sAdditionalStartParams_am += "&" + sHTMLParam;
		}
	},

	/***********************************************************************
	 * opens the given ELearning-Sequence
	 * - parameter -
	 * sId: sequence id
	 * sUsecase : startup usecase possible values: start, resume
	***********************************************************************/
	openELearningView : function( sId, sUsecase )	{

		if( this.m_objCourse.isSequenceConnected() ) {

			if( this.options.sMsgLaunchOnOpenSco != null ) {
				alert( this.options.sMsgLaunchOnOpenSco );
			}

			// supress the href call
			return( true );
		}

		this.m_sLastSequenceID = sId;
		this.m_objCourse.prepareSequenceLaunch( sId, sUsecase );

		var sequenceData = this.m_objCourse.getSequenceData(sId);
		if( sequenceData ) {
			var sStartURL = sequenceData.getStartUrl(sUsecase);
			if( (sStartURL != null) && (sStartURL != "") ) {

				if( this.options.sLaunchMode == "iframe" ) {
					return( this.openELearningInFrame( sStartURL ) );
				}
				else {
					return( this.openELearningInPopupWindow(sStartURL) );
				}
			}
		}

		return( false );
	},

	openELearningInFrame : function( sStartURL )	{
		return( this.setIFrameView( sStartURL, true ) );
	},

	restoreCourseView : function()	{
		return( this.setIFrameView( "about:blank", false ) );
	},

	setIFrameView : function( sURL, bVisible )	{

		var objCourseViewport = document.getElementById("courseDispatchersViewport");
		var objWbtViewport = document.getElementById("eLearningViewport");
		var objStage = document.getElementById("eLearningStage");
		if( objCourseViewport && objWbtViewport && objStage ) {

			if( bVisible ) {
				objCourseViewport.style.display = "none";
				objWbtViewport.style.display = "block";
			}
			else {
				objCourseViewport.style.display = "block";
				objWbtViewport.style.display = "none";
			}

			jQuery.ajax({
				url: url,
				type : 'GET',
				dataType: "html",
				success : function (data) {
					jQuery('eLearningStage').html(data);
				}
			});

			return( true );
		}

		return( false );
	},

	openELearningInPopupWindow : function( sStartURL )	{

		var objWindow = window.open( sStartURL, "ELearningView", this.m_sELearningPopupParameter );
		if( objWindow ) {
			objWindow.focus();
			// returning true avoid the href call
			return( true );
		}

		return( false );
	},

	onELearningTerminate : function()	{
		if( this.options.sLaunchMode == "iframe" ) {
			this.restoreCourseView();
		}
	},

	/***********************************************************************
	 * get filtered sequence count
	 * - parameter -
	 * sFilter : comma separeted list of sequence types that will be count
	 *		possible values are:
	 *		learning_mode
	 *		assessment_mode
	 *		result_relevant
	 *		passed
	***********************************************************************/
	getSequenceCount : function( sFilter ) {

		var nCount = 0;
		var sequenceDataMap = this.m_objCourse.getSequenceDataMap();
		for( var sId in sequenceDataMap ) {

			if(		this.matchRegExp( sFilter, CCourseController.RegExpListParamLearningMode )
				&&	sequenceDataMap[sId].isELearning() )
			{
				nCount++;
				continue;
			}

			if(		this.matchRegExp( sFilter, CCourseController.RegExpListParamAssessmentMode )
				&&	sequenceDataMap[sId].isAssessment() )
			{
				nCount++;
				continue;
			}

			if(		this.matchRegExp( sFilter, CCourseController.RegExpListParamResultRelevant )
				&&	sequenceDataMap[sId].isResultRelevant() )
			{
				nCount++;
				continue;
			}

			if(		this.matchRegExp( sFilter, CCourseController.RegExpListParamPassed )
				&&	sequenceDataMap[sId].isPassed() )
			{
				nCount++;
				continue;
			}
		}

		return( nCount );
	},

	/***********************************************************************
		get count of passed and result relevant sequences

	***********************************************************************/
	getPassedCount : function() {

		var nCount = 0;
		var sequenceDataMap = this.m_objCourse.getSequenceDataMap();
		for( var sId in sequenceDataMap ) {

			if( sequenceDataMap[sId].isPassed() && sequenceDataMap[sId].isResultRelevant() ){
				nCount++;
				continue;
			}
		}

		return( nCount );
	},


	redirectBuilder : null,

	/***********************************************************************
	 * Initialize the course.
	 * The course-html must be ready loaded.
	***********************************************************************/
	initCourse : function( redirectBuilder ) {

		if( redirectBuilder ) {
			this.redirectBuilder = redirectBuilder;
		}

		this.initStaticClassObjects();
		this.initScoreIDs();

		if( !this.m_objCourse.isSequenceConnected() ) {
			console.log("CCourseController::initCourse");
			this.m_objCourse.refreshStatus();
		}

		return true;
	},

	/***********************************************************************
	 * shut down sequence for the course.
	***********************************************************************/
	terminateCourse : function() {
		// This isn't necessary anymore due to single document use.
		// If you do this, the browser windows will be closed when you reload
		// the course page with loginRequired=false !!!
		// Amar, 16.04.2013

		this.m_objCourse.terminate();

//		var bClose = true;
//		if( (this.options.sMsgConfirmCloseOnOpenSco != null ) && this.m_objCourse.isSequenceConnected() ) {
//			bClose = confirm( this.options.sMsgConfirmCloseOnOpenSco );
//		}
//
//		if( bClose ) {
//
//			this.m_objCourse.terminate();
//
//			if( this.m_objCourse.handleBrowserOnTerminate() ) {
//
//				var isDeeplinked = TTPortal.Url.getParam('usecase',location.search)=='deeplink';
//				if( !isDeeplinked ) {
//					if( this.options.sCloseForwardingUri != null ) {
//						if(typeof(parent.location) != "undefined") {
//							parent.location.replace( this.options.sCloseForwardingUri );
//						} else {
//							location.replace( this.options.sCloseForwardingUri );
//						}
//					}
//					else if( typeof(parent.closeBrowserWindow) != "undefined" ) {
//						parent.closeBrowserWindow();
//					}
//					else if( typeof(closeBrowserWindow) != "undefined" ) {
//						closeBrowserWindow();
//					}
//					else {
//						location.replace( "about:blank" );
//					}
//				}
//			}
//		}
	},

	/***********************************************************************
	 * refreshes the course view by DOM manipulation.
	***********************************************************************/
	refreshCourseView : function() {
		this.initDynamicClassObjects();
		this.initResultClassObjects();
	},

	/***********************************************************************
	 * checks for assigned sequence
	 * - parameter -
	 * sType : possible values are:
	 *			learning_mode
	 *			assessment_mode
	***********************************************************************/
	hasAssignedSequence : function( sType ) {
		var sID = this.getAssignedSequence( this.m_sLastSequenceID, sType );
		return( sID != "" );
	},

	/***********************************************************************
	 * start next sequence
	 * - parameter -
	 * sType : possible values are:
	 *			learning_mode
	 *			assessment_mode
	***********************************************************************/
	startAssignedSequence : function( sType ) {

		var sID = this.getAssignedSequence( this.m_sLastSequenceID, sType );
		if( sID != "" ) {
			this.openELearningView( sID, "start" );
			return( true );
		}

		return( false );
	},

	/***********************************************************************
	 * checks for next sequence
	 * - parameter -
	 * sFilter : comma separeted list of sequence types that will be count
	 *		possible values are:
	 *			learning_mode
	 *			assessment_mode
	***********************************************************************/
	hasNextSequence : function( sFilter ) {
		var sID = this.getNextSequence( this.m_sLastSequenceID, sFilter );
		return( sID != "" );
	},

	/***********************************************************************
	 * start next sequence
	 * - parameter -
	 * sFilter : comma separeted list of sequence types that will be count
	 *		possible values are:
	 *			learning_mode
	 *			assessment_mode
	***********************************************************************/
	startNextSequence : function( sFilter ) {

		var sID = this.getNextSequence( this.m_sLastSequenceID, sFilter );
		if( sID != "" ) {
			this.openELearningView( sID, "start" );
			return( true );
		}

		return( false );
	},

	/***********************************************************************
	 * checks for previous sequence
	 * - parameter -
	 * sFilter : comma separeted list of sequence types that will be count
	 *		possible values are:
	 *			learning_mode
	 *			assessment_mode
	***********************************************************************/
	hasPreviousSequence : function( sFilter ) {
		var sID = this.getPreviousSequence( this.m_sLastSequenceID, sFilter );
		return( sID != "" );
	},

	/***********************************************************************
	 * starts previous sequence
	 * - parameter -
	 * sFilter : comma separeted list of sequence types that will be count
	 *		possible values are:
	 *			learning_mode
	 *			assessment_mode
	***********************************************************************/
	startPreviousSequence : function( sFilter ) {

		var sID = this.getPreviousSequence( this.m_sLastSequenceID, sFilter );
		if( sID != "" ) {
			this.openELearningView( sID, "start" );
			return( true );
		}

		return( false );
	},


	//======================================================================
	//	PRIVATE
	//======================================================================


	/***********************************************************************
	 * get filtered sequence count
	 *
	 * - parameter -
	 * sId: sequence id
	 * sType : possible values are:
	 *			learning_mode
	 *			assessment_mode
	***********************************************************************/
	getAssignedSequence : function( sID, sType ) {

		if( sID == "" ) {
			return("");
		}

		var sLookupID = sID.substr( 0, sID.length - 2 );

		var sequenceDataMap = this.m_objCourse.getSequenceDataMap();
		for( sequenceID in sequenceDataMap ) {

			if( !sequenceID ) {
				continue;
			}

			var sCurrentLookupID = sequenceID.substr( 0, sID.length - 2 )
			if( sCurrentLookupID != sLookupID ) {
				continue;
			}

			var sequenceData = sequenceDataMap[sequenceID];
			if( !sequenceData || !sequenceData.getData() ) {
				continue;
			}

			if(	(sType == "learning_mode") && sequenceData.isELearning() ) {
				return( sequenceID );
			}

			if(	(sType == "assessment_mode") && sequenceData.isAssessment() ) {
				return( sequenceID );
			}
		}

		return( "" );
	},

	/***********************************************************************
	 * get next sequence id from course structure
	 *
	 * - parameter -
	 *	sStartID: sequence where lookup starts in course structure
	 *	sFilter : comma separeted list of sequence types that will be count
	 *		possible values are:
	 *			learning_mode
	 *			assessment_mode
	***********************************************************************/
	getNextSequence : function( sStartID, sFilter ) {

		// if sStartId is not set, get first matching sequence
		var bStartSequenceFound = ( sStartID == "" ) ? true : false;

		var sequenceDataMap = this.m_objCourse.getSequenceDataMap();
		for( sequenceID in sequenceDataMap ) {

			if( !sequenceID ) {
				continue;
			}

			if( !bStartSequenceFound ) {
				bStartSequenceFound = ( sequenceID == sStartID ) ? true : false;
				continue;
			}

			var sequenceData = sequenceDataMap[sequenceID];
			if( !sequenceData || !sequenceData.getData() ) {
				continue;
			}

			if(	this.matchRegExp( sFilter, CCourseController.RegExpListParamLearningMode ) && sequenceData.isELearning() ) {
				return( sequenceID );
			}

			if(	this.matchRegExp( sFilter, CCourseController.RegExpListParamAssessmentMode ) &&	sequenceData.isAssessment() ) {
				return( sequenceID );
			}
		}

		return( "" );
	},

	/***********************************************************************
	 * get previous sequence id from course structure
	 *
	 * - parameter -
	 *	sStartID: sequence where lookup starts in course structure
	 *	sFilter : comma separeted list of sequence types that will be count
	 *		possible values are:
	 *			learning_mode
	 *			assessment_mode
	***********************************************************************/
	getPreviousSequence : function( sStartID, sFilter ) {

		// if sStartId is not set, get first matching sequence
		if( sStartID == "" ) {
			return( "", sFilter );
		}

		var sPrevSequenceID = "";

		var sequenceDataMap = this.m_objCourse.getSequenceDataMap();
		for( sequenceID in sequenceDataMap ) {

			if( !sequenceID ) {
				continue;
			}

			if( sequenceID == sStartID ) {
				return( sPrevSequenceID );
			}

			var sequenceData = sequenceDataMap[sequenceID];
			if( !sequenceData || !sequenceData.getData() ) {
				continue;
			}

			if(	this.matchRegExp( sFilter, CCourseController.RegExpListParamLearningMode ) && sequenceData.isELearning() ) {
				sPrevSequenceID = sequenceID;
				continue;
			}

			if(	this.matchRegExp( sFilter, CCourseController.RegExpListParamAssessmentMode ) &&	sequenceData.isAssessment() ) {
				sPrevSequenceID = sequenceID;
				continue;
			}
		}

		return( "" );
	},

	/***********************************************************************
	 * string-match for a cached regular expression object
	***********************************************************************/
	matchRegExp : function( sString, objRegExp ) {
		if( !sString || !objRegExp ) { return( false ); }
		objRegExp.index = 0;
		objRegExp.lastIndex = 0;
		return( objRegExp.test( sString ) );
	},

	/***********************************************************************
		builds the query params for a given sequence

		- parameter -
		sId: sequence id
	***********************************************************************/
	getLaunchParams : function( objAnchorElement, sId ) {
		var sStorageMethod = this.m_objProfileController.getStorageMethod(),
			sQueryParams = "storagemethod=" + sStorageMethod,
			sUri = this.getUri(objAnchorElement),
			sClassId = this.m_mapCSSClassNames[objAnchorElement.className];

		if (typeof( this.m_objProfileController.getUserID ) != "undefined") {
			sQueryParams +=	"&userid=" + this.m_objProfileController.getUserID();
		}

		if (sUri.indexOf("playerType=HTML5") > -1) {
			switch(sStorageMethod) {
				case "jsapi":
					sQueryParams += "&persistenceType=scorm12";
					break;
				case "scorm2004":
					sQueryParams += "&persistenceType=scorm2004";
					break;
			}
		}

		switch (sClassId) {
			case "aELearningStart":
				if (this.m_sAdditionalStartParams_lm.length > 0) {
					sQueryParams += this.m_sAdditionalStartParams_lm;
				}
				break;
			case "aELearningResume":
				if (this.m_sAdditionalStartParams_lm.length > 0) {
					sQueryParams += this.m_sAdditionalStartParams_lm;
				}
				sQueryParams += "&entry=resume&resume=true";
				break;
			case "aELearningStartAS":
				if (this.m_sAdditionalStartParams_am.length > 0) {
					sQueryParams += this.m_sAdditionalStartParams_am;
				}
				break;
		}

		return sQueryParams;
	},

	/***********************************************************************
	 * builds the start url for given sequence
	 *
	 * - parameter -
	 * objAnchorElement: DOM anchor-element
	 * sId: sequence id
	***********************************************************************/
	rebuildStartUri : function( objAnchorElement, sId ) {

		if( !objAnchorElement ) {
			return "";
		}

		// uri encoding in due to the DOM access
		var sUri = this.getUri(objAnchorElement);
		if( sUri.indexOf("uri_rebuilded=true")>0 ) {
			return( sUri );
		}

		// add params
		var sLaunchParams = this.getLaunchParams( objAnchorElement, sId );
		if(sLaunchParams.length > 0) {
			sUri += (sUri.indexOf("?") < 0) ? "?" : "&";
//			sUri += "launch_params=" + encodeURIComponent( sLaunchParams );
			sUri += sLaunchParams;
		}

		// check storage system
		if( this.redirectBuilder && this.m_objCourse.useScoLaunchParams()) {
			// use lms launch template
//			sUri +=	"&sco_launch=true";
//			sUri +=	"&sco_id=" + encodeURIComponent( sId );
			var scoId = encodeURIComponent(sId);
			sUri = this.redirectBuilder(sUri, scoId);
		}

		if(this.options.bRemoveHrefOnStartTags) {
			sUri = "javascript:void(0)";
			objAnchorElement.target = "";
		}

		sUri += (sUri.indexOf("?") < 0) ? "?" : "&";
		sUri += "uri_rebuilded=true";

		objAnchorElement.href = sUri;

		return sUri;
	},

	/***********************************************************************
	 * gets and encodes the href from a given DOM anchor element
	 *
	 * - parameter -
	 * objAnchorElement: DOM anchor-element
	***********************************************************************/
	getUri : function( objAnchorElement ) {

		if( !objAnchorElement || (objAnchorElement.tagName.toLowerCase() != "a") ) {
			return("");
		}

		var sUri = objAnchorElement.href;
		var asURL = sUri.split("?");
		if( asURL.length == 2 ) {

			// param Slash/BackSlash URL-reencoding

			var sUriParams = "";

			var sKey = "";
			var sValue = "";

			var sCurrentParam = "";
			var rxParam = new RegExp("(.*?)=(.*)","g");
			var asCurrentParam;

			var asParams = asURL[1].split("&");
			for(var i=0; i<asParams.length; i++) {

				sKey = "";
				sValue = "";

				sCurrentParam = asParams[i];
				if(rxParam.test(sCurrentParam))	{
					rxParam.lastIndex = 0;
					rxParam.exec(sCurrentParam);
					if( (rxParam.lastIndex > -1) && RegExp.$1 && RegExp.$2) {
						sKey = RegExp.$1;
						sValue = RegExp.$2;
					}
				}
				else {
					asCurrentParam = sCurrentParam.split("=");
					if( asCurrentParam.length == 2 ) {
						sKey = asCurrentParam[0];
						sValue = asCurrentParam[1];
					}
				}

				if( (sKey != "") && (sValue != "") ) {
					sUriParams += (sUriParams == "") ? "?" : "&";
					sUriParams += sKey  + "=" + encodeURIComponent( decodeURIComponent(sValue) );
				}
			}

			sUri = asURL[0] + sUriParams;
		}

		return( sUri );
	},

	/***********************************************************************
		extract sequence id out of html-tag-id
	***********************************************************************/
	extractSequenceID : function( sRawId ) {

		if( sRawId.length == 0 ) {
			return( "invalid" );
		}

		CCourseController.RegExpSequenceUniquePrefix.index = 0;
		CCourseController.RegExpSequenceUniquePrefix.lastIndex = 0;

		if( CCourseController.RegExpSequenceUniquePrefix.test( sRawId ) )	{
			CCourseController.RegExpSequenceUniquePrefix.index = 0;
			CCourseController.RegExpSequenceUniquePrefix.lastIndex = 0;
			var sId = sRawId.replace( CCourseController.RegExpSequenceUniquePrefix, "" );
			if( (sId != null) && (sId.length > 0) ) {
				CCourseController.RegExpSequenceId.index = 0;
				CCourseController.RegExpSequenceId.lastIndex = 0;
				if( CCourseController.RegExpSequenceId.test( sId ) )	{
					return( sId );
				}
			}
		}

		return( "invalid" );
	},

	/***********************************************************************
		intitalize list of sequence, that will be used for result calculation
	***********************************************************************/
	initScoreIDs : function() {
		var nAssessmentCount = this.getSequenceCount("assessment_mode");
		var bResult = this.m_objCourse.initScoreIDs( (nAssessmentCount > 0) );
		return( bResult );
	},

	getClassNames : function( objElement ) {
		var eClassNames = {};
		var sClassNames = objElement.className;
		var aClassNames = sClassNames.split(" ");
		for( var i=0; i<aClassNames.length; i++ ) {
			eClassNames[ aClassNames[i] ] = "";
		}
		return( eClassNames );
	},

	//======================================================================
	//	DOM modifier functions
	//======================================================================

	/***********************************************************************
		checks an element for use as sequence object.
		if the element is valid, this function returns the sequence data
		object.
	***********************************************************************/
	initSequenceObject : function( objElement ) {

		if( !objElement ) {
			return( null );
		}

		var sId = this.extractSequenceID(objElement.id);
		if( sId == "invalid" ) {
			return( null );
		}

		var objClassNames = this.getClassNames( objElement );
		for( var sClassName in objClassNames ) {
			if( typeof(this.m_mapCSSClassNames[sClassName]) != "undefined" ) {
				return( sId );
			}
		}

		return( null );
	},

	/***********************************************************************
		Modify all static tags.
		Static means that they will only be modifed on course initialization.
	***********************************************************************/
	initStaticClassObjects : function() {
		var sId = "";
		this.m_objCourse.clearSequenceDataMap();

		var aElements = this.m_objDoc.getElementsByTagName( "a" );
		for( var i=0; i<aElements.length; i++ )	{

			sId = this.initSequenceObject(aElements[i]);
			if( !sId ) { continue; }

			var objSequenceData = this.m_objCourse.initSequenceData( sId );

			var objClassNames = this.getClassNames(aElements[i]);
			for( var sClassName in objClassNames ) {

				var sClassId = this.m_mapCSSClassNames[sClassName];
				switch( sClassId ) {

					case "aELearningStart":
						var sUri = this.rebuildStartUri( aElements[i], sId );
						objSequenceData.setStartUrl( sUri, "start" );
						objSequenceData.setMode( "learning", true );
						break;

					case "aELearningResume":
						var sUri = this.rebuildStartUri( aElements[i], sId );
						objSequenceData.setStartUrl( sUri, "resume" );
						objSequenceData.setMode( "learning", true );
						aElements[i].style.visibility = "hidden";
						break;

					case "aELearningStartAS":
						var sUri = this.rebuildStartUri( aElements[i], sId );
						objSequenceData.setStartUrl( sUri, "start" );
						objSequenceData.setMode( "assessment", true );
						break;
				}
			}

			this.m_objCourse.setSequenceData( sId, objSequenceData );
		}

		aElements = this.m_objDoc.getElementsByTagName( "div" );
		for( var i=0; i<aElements.length; i++ )	{

			sId = this.initSequenceObject(aElements[i]);
			if( !sId ) { continue; }

			var objSequenceData = this.m_objCourse.initSequenceData( sId );

			var objClassNames = this.getClassNames(aElements[i]);
			for( var sClassName in objClassNames ) {

				var sClassId = this.m_mapCSSClassNames[sClassName];
				switch( sClassId ) {

					case "divQualificationResult":
					case "divQualificationResultIcon":
						objSequenceData.setResultRelevant( true );
						break;

					case "divMasteryScore":
						objSequenceData.setMasteryScore( aElements[i].innerHTML );
						break;
				}
			}

			this.m_objCourse.setSequenceData( sId, objSequenceData );
		}
	},

	/***********************************************************************
		Modify all dynamic tags.
		Dynamic means that they will be modifed on every status refresh.
	***********************************************************************/
	initDynamicClassObjects : function() {
		var sId = "",
			aElements = this.m_objDoc.getElementsByTagName( "div" ), divElement, i;
		for(i=aElements.length - 1; i >= 0; i--) {
			divElement = aElements[i];
			sId = this.initSequenceObject(divElement);
			if(!sId) {
				continue;
			}

			var divJQ = jQuery(divElement),
				objSequenceData = this.m_objCourse.initSequenceData(sId),
				objClassNames = this.getClassNames(divElement), sClassName, sClassId;
			for(sClassName in objClassNames) {
				sClassId = this.m_mapCSSClassNames[sClassName];
				switch(sClassId) {
					case "divProgressBar":

						var nScore = this.m_objProfileController.getScore(sId);
						objSequenceData.setScore(nScore);

						if(nScore > 0 && nScore < 100) {
							var startModesLayer = jQuery('#startmodes_' + sId);
							if (startModesLayer.length > 0) {
								startModesLayer.addClass('HasStartmodes');
								startModesLayer.parent().addClass('HasStartmodes');
							}
						}

						var sProgress = nScore.toString() + "%";
						divJQ.css("width", sProgress);

						divJQ.parent('.DocumentTitleBoxProgressBarBackground').find('.ProgressLabel').html(' ' + sProgress);
						
						break;
						
					case "divProgressResult":
						var nScore = this.m_objProfileController.getScore(sId);
						objSequenceData.setScore(nScore);
						var sScore = nScore.toString();
						if(this.m_mapTextCache[divElement.id] == null) {
							this.m_mapTextCache[divElement.id] = divElement.title || "\{0\}%";
						}
						sScore = this.m_mapTextCache[divElement.id].replace("\{0\}", nScore.toString());
						divJQ.html(sScore);
						divElement.title = '';
						break;
						
					case "divQualificationResult":
						if(this.m_mapTextCache[divElement.id] == null) {
							this.m_mapTextCache[divElement.id] = divElement.innerHTML;
						}

						var aSwitch = this.m_mapTextCache[divElement.id].split("|");
						if(aSwitch.length < 2) {
							break;
						}

						var nScore = this.m_objProfileController.getScore( sId );
						objSequenceData.setScore(nScore);
						divElement.innerHTML = (objSequenceData.isPassed()) ? aSwitch[0] : aSwitch[1];
						divElement.style.visibility = "visible";
						break;

					case "divQualificationResultIcon":

						if(this.m_mapTextCache[divElement.id] == null) {
							this.m_mapTextCache[divElement.id] = divElement.innerHTML;
						}

						var aSwitch = this.m_mapTextCache[divElement.id].split("|");
						if( aSwitch.length < 2 ) { break; }

						var nScore = this.m_objProfileController.getScore( sId );
						objSequenceData.setScore( nScore );
						var addClassName = objSequenceData.isPassed() ? aSwitch[0] : aSwitch[1];
						var removeClassName = !objSequenceData.isPassed() ? aSwitch[0] : aSwitch[1];
						divJQ.addClass( addClassName );
						divJQ.removeClass( removeClassName );
						divJQ.html("&nbsp;");
						// KF-15457
						var sLessonStatus = this.m_objProfileController.getLessonStatus(sId);
						if (sLessonStatus !== "unknown" && sLessonStatus !== "na") {
							divJQ.css({"visibility" : "visible"});
						}

						break;
				}
			}
			this.m_objCourse.setSequenceData(sId, objSequenceData);
		}

		aElements = this.m_objDoc.getElementsByTagName( "a" );
		for(i=aElements.length - 1; i >= 0; i--) {
			divElement = aElements[i];
			sId = this.initSequenceObject(divElement);
			if(!sId) {
				continue;
			}
			var objSequenceData = this.m_objCourse.initSequenceData(sId),
				objClassNames = this.getClassNames(divElement), sClassName;
			for(sClassName in objClassNames) {
				var sClassId = this.m_mapCSSClassNames[divElement.className];
				switch(sClassId) {
					case "aELearningResume":
						divElement.style.visibility = this.m_objProfileController.canResume(sId) ? "visible" : "hidden";
						break;
				}
			}
		}
	},

	/***********************************************************************
		Modify all result tags.
		This tags will be modifed on every status refresh.
	***********************************************************************/
	initResultClassObjects : function()	{

		var nResultCount = this.getSequenceCount("result_relevant");
		var nPassedCount = this.getPassedCount();

		var nMasteryScore = this.m_objCourse.getCourseMasteryScore();
		var nCourseResult = 0;
		var bCoursePassed = false;
		if( nResultCount > 0 ) {
			nCourseResult = (nPassedCount / nResultCount ) * 100;
			bCoursePassed = ( nCourseResult >= nMasteryScore ) ? true : false;
		}

		aElements = this.m_objDoc.getElementsByTagName( "p" );
		for( var i=0; i<aElements.length; i++ )	{

			var pElement = aElements[i];

			var objClassNames = this.getClassNames(aElements[i]);
			for( var sClassName in objClassNames ) {

				if( typeof(this.m_mapCSSClassNames[sClassName]) == "undefined" ) { continue; }

				var sClassId = this.m_mapCSSClassNames[sClassName];
				switch( sClassId ) {

//					case "pCourseQualification":
					case "pCourseResult":

						if( pElement.innerHTML.length > 0 ) {

							if(this.m_mapTextCache[pElement.id] == null) {
								this.m_mapTextCache[pElement.id] = pElement.innerHTML;
							}

							var sText = this.m_mapTextCache[pElement.id];

							// singular/plural
							var aSwitch = sText.split("|");
							if( aSwitch.length >= 2 ) {
								sText = (nResultCount == 1) ? aSwitch[0] : aSwitch[1];
							}

							sText = sText.replace( "\{0\}", nPassedCount );
							sText = sText.replace( "\{1\}", nResultCount );
							sText = sText.replace( "\{2\}", nCourseResult.toString() );
							sText = sText.replace( "\{3\}", nMasteryScore.toString() );
							aElements[i].innerHTML = sText;

							pElement.style.display = "block";
						}
						else {
							pElement.style.display = "none";
						}

						break;

					case "pCourseResultSuccess":
						pElement.style.display = !bCoursePassed ? "none" : "block";
						break;

					case "pCourseResultFailed":
						pElement.style.display = bCoursePassed ? "none" : "block";
						break;
				}
			}
		}
	}
});

//use global RegExp objects for best performance
CCourseController.RegExpSequenceUniquePrefix = new RegExp("^(l|a|r|m|qr|qri|pr|ms|pb|c)(.*?)_","g");
CCourseController.RegExpSequenceId = new RegExp("^(.*?)[0-9a-zA-Z]{20,32}(lm|am)$","g");
CCourseController.RegExpListParamLearningMode = new RegExp("[,]?(learning_mode)[,]?","i");
CCourseController.RegExpListParamAssessmentMode = new RegExp("[,]?(assessment_mode)[,]?","i");
CCourseController.RegExpListParamResultRelevant = new RegExp("[,]?(result_relevant)[,]?","i");
CCourseController.RegExpListParamPassed = new RegExp("[,]?(passed)[,]?","i");


//======================================================================
//  Public Functions
//======================================================================
Object.extend( TTPortal, {

	Course : {

		Instance : null,
		Controller : null,

		StatusRefreshTimer : null,

		apiAlreadyInitialized : false,

		/***********************************************************************
		  This function creates a course in subject to the given options.
		  See CCourseSettings for option detail.
		***********************************************************************/
		createCourse : function ( options ) {

			if( !options ) {
				TTPortal.Logging.logError("createCourse" , "No options available!");
				return false;
			}

			var courseOptions = {
				sCourseId : options.sCourseId,
				sLaunchMode : options.sLaunchMode || "popup",
				bAutomaticStatusRefresh : options.bAutomaticStatusRefresh || true,
				objScormAPI : options.objScormAPI || null,
				sMsgCloseOnOpenSco : options.sMsgCloseOnOpenSco || "",
				sMsgLaunchOnOpenSco : options.sMsgLaunchOnOpenSco || "",
				sContentClosedUri : options.sContentClosedUri || "",
				fnRedirectBuilder : options.fnRedirectBuilder || null
			};

			var objCourse = new CCourseFactory().createCourse( options );
			if( objCourse ) {

				switch( objCourse.getStorageSystem() ) {
					case "scorm_lms":
						this.apiAlreadyInitialized = courseOptions.objScormAPI != null;
						break;
					case "local_maschine":
						if( courseOptions.bAutomaticStatusRefresh ) {
							this.StatusRefreshTimer = new CTimer( CTimer.TIMER_MODE_COOKIE_CHANGE );
							this.StatusRefreshTimer.startTimer( 4000, "course:refresh", null );
						}
						break;
				}

				var objCourseController = new CCourseController( objCourse, { sLaunchMode : courseOptions.sLaunchMode } );
				if( objCourseController.initCourse( courseOptions.fnRedirectBuilder ) ) {

					if( !objCourse.isMultiScoLaunchAllowed() ) {
						objCourseController.setLmsDialogs( courseOptions.sMsgLaunchOnOpenSco, courseOptions.sMsgCloseOnOpenSco );
						objCourseController.setForwardingOnTermination( courseOptions.sContentClosedUri );
					}

					this.Instance = objCourse;
					this.Controller = objCourseController;

					TTPortal.Logging.logInfo("course:" + courseOptions.sCourseId, "Course created! Storage system: " + objCourse.getStorageSystem() + " - Method: " + objCourse.getStorageMethod() );

					return true;
				}
			}

			TTPortal.Logging.logError("course:" + courseOptions.sCourseId, "Error while creating course!");

			this.Instance = null;
			this.Controller = null;

			return false;
		},

		Certificate : {

			initUsername : function( options ) {

				var usernameOptions = {
					sUsername : null,
					sCertificateStageId : "stageCertificate",
					sCertificateInputId : "stageCertificateInput",
					sCertificateUsernameId : "certUsername"
				};

				Object.extend(usernameOptions, options || { });

				try {
					var objCertificate = $(usernameOptions.sCertificateStageId);
					var objCertificateInput = $(usernameOptions.sCertificateInputId);
					var objUsername = $(usernameOptions.sCertificateUsernameId);

					if( objCertificate && objCertificateInput  && objUsername ) {

						var sUsername =	"";

						// test for existing username

						if( TTPortal.Course != null ) {
							sUsername = TTPortal.Course.Instance.getUsername();
						}

						if( (sUsername == null) || (sUsername.length == 0) ) {
							if( usernameOptions.sUsername != null ) {
								sUsername = usernameOptions.sUsername;
							}
						}

						if( (sUsername == null) || (sUsername.length == 0) ) {
							sUsername = document.certPersonalizeForm.username.value;
						}

						if(	(sUsername == null) || (sUsername.length == 0) ) {
							sUsername = TTPortal.Core.getGlobalValue( "CertUserName" );
						}

						if( (sUsername != null) && (sUsername.length > 0) ) {
							TTPortal.Core.setGlobalValue( "CertUserName", sUsername );
							TTPortal.Course.Instance.setUsername(sUsername);
							objUsername.innerHTML = sUsername;
							return sUsername;
						}
						return false;
					}
				}
				catch(e){
                    TTPortal.Logging.logException("Could not init username", e);
                    return false;
				}
			}
		}
	}
});

var TTPortalCourse = TTPortal.Course;

Event.observe( window, "beforeunload", function() {

	if( TTPortalCourse.StatusRefreshTimer ) {
		TTPortalCourse.StatusRefreshTimer.stopTimer();
		TTPortalCourse.StatusRefreshTimer = null;
	}

	if( TTPortalCourse.Controller && !TTPortalCourse.apiAlreadyInitialized) {
		TTPortalCourse.Controller.terminateCourse();
	}
});


var onELearningStart = function( sId, sUsecase )	{

	//return false to supress the href call
	if (TTPortalCourse.Controller) {
		return !TTPortalCourse.Controller.openELearningView(sId,sUsecase);
	}

	return true;
};
