/* Array containig all the instance of the javascript modules.
 * The proper way to add it is to use the instance id as a key!
 * oModuleInstances[ 'INSTANCEID1' ] = new MODULE();
 */
var oModuleInstances = {}; 

/**
 * Emulating console.debug() for non Mozilla Browsers
 * with Debug Plugin
 */
if( typeof console == 'undefined' || ( typeof console == 'object' && typeof console.debug == 'undefined' ) ) {
	if ( typeof console == 'object' ) {
		defineCruxDebugFunction();		
	} else {
		var console = new Object;
		defineCruxDebugFunction();
	}
}

function defineCruxDebugFunction() {
	console.debug = function( mVal ) {
		sStr = 	"Type: " + typeof mVal + "\n" +
				"Value: " + mVal;
		alert( sStr );
	}
}

function ClientSideStrings() {

	var oStrings = {};

	this.setString = function( sSig, sKey, sStr ) {
		if( oStrings[sSig] == null ) {
			oStrings[sSig]={};
		}
		oStrings[sSig][sKey] = sStr;
	}

	this.getString = function( sSig, sKey ) {
		if( oStrings[sSig] == null ) return '[_' + sKey + '_]';
		if( oStrings[sSig][sKey] == null ) return '[_' + sKey + '_]';
		return oStrings[sSig][sKey];
	}
	
	this.gs = function( sSig, sKey ) {
		return this.getString( sSig, sKey );
	}
}

/**
 * A client side ciao user class that handles basis user data and actions
 * Only 1 instance of this class should be instantiated by the container base.
 * 
 * @access public
 * @author Remco Verton
 * 
 * @param {Bool} bStatus 0 = not logged in 1 is logged in
 * 
 * @return {Object} CiaoUser
 */
function CiaoUser( bStatus ){
	
	var bStatus = bStatus;
	var oCallback;
	var bRequestInProgress = false;
	
	addEvents();
	
	/**
	 * Determined if the current user is logged in. If not tries to login.
	 * After the login dialog is done it will call the callback function passing it the returned login status.
	 * 
	 * @access public
	 * @author Remco Verton
	 * 
	 * @param {Object} $oCallback Reference (without parenthesis!)  of the js callback function. This function takes the
	 * login status as a int parameter:
	 * 0 - User is already logged in
	 * 1 - User has been logged in using the login/registration form
	 * 2 - User has cancelled login process and is not logged in 
	 * @return {Void}
	 */
	this.getLoginStatus = function( oCallbackFunction ){
		if( !bRequestInProgress ){
			// If the user is logged in and the logged in session is not expired call the callback directly
			if( bStatus && !isSessionExpired() ) {
				oCallbackFunction( 0 );
			}
			else{
				oCallback = oCallbackFunction;
				bRequestInProgress = true;
				oCrux.callAjax( 'SmartLogin_getLoginStatus', { parameters: [ 'test', 'oCiaoUser.callBackHandler' ] } )	
			}
		}
	} // func
	
	/*
	 * Logs the user in.
	 *
	 * @access public
	 * @author Ben Piche
	 */
	this.login = function( oCallbackFunction ) {
		var aMainParams = new Array( 'test', 'oCiaoUser.dumbCallbackHandler' );
		if( !bRequestInProgress ) {
			oCallback = oCallbackFunction;
			bRequestInProgress = true;
			oCrux.callAjax( 'SmartLogin_getLoginStatus', { parameters: aMainParams } )	
		}
	};
	
	/**
	 * Determined if the current user is logged in. Only if the user is logged in it will call the callback function.
	 * This is a simplified version of getLogin status
	 * 
	 * @access public
	 * @author Remco Verton
	 * 
	 * @param {Object} $oCallback Reference (without parenthesis!)  of the js callback function.
	 * @return {void}
	 */
	this.loginCheck = function( oCallbackFunction ){
		if( !bRequestInProgress ){
			// If the user is logged in and the logged in session is not expired call the callback directly
			if( bStatus && !isSessionExpired() ) {
				oCallbackFunction( );
			}
			else{
				oCallback = oCallbackFunction;
				bRequestInProgress = true;
				oCrux.callAjax( 'SmartLogin_getLoginStatus', { parameters: [ 'test', 'oCiaoUser.simpleCallBackHandler' ] } )	
			}
		}
	} // func

	/**
	 * This handler doesn't care about the login status. The login status can be a specific
	 * value set by the using module.
	 **/ 
	this.dumbCallbackHandler = function( iLoginStatus ) {
		// The request is no longer in progress, a new getLogin status can be handled!
		bRequestInProgress = false;
		// call the handler
		try {
			oCallback( iLoginStatus );
		}
		catch( event ) {
			console.debug( 'CiaoUser.dumbCallbackHandler() A javascript error occured inside the code! ' + event );
		}
		
		// Unset the callback function
		oCallback = undefined;
	};
	
	// Private !
	this.simpleCallBackHandler = function( iLoginStatus ){		 
		// The request is no longer in progress, a new getLogin status can be handled!
		bRequestInProgress = false;
		
		// We need to try catch this because we do not want a bad callback function to break other parts of the page!
		try{
			// Every time smart login comes back reset the status to the proper value
			if( iLoginStatus == 0 || iLoginStatus == 1 ) bStatus = 1;
			else bStatus = 0;
			
			// Validate the integer value of iLoginStatus
			if( iLoginStatus != 0 && iLoginStatus != 1 && iLoginStatus != 2 ){
				console.debug( 'incorrect login status id returned' );
				return;
			}
			
			// Call the callback function, only if the login check passed!
			if( iLoginStatus == 0 || iLoginStatus == 1 ){
				oCallback( );
			}
		}
		catch( event ){
			console.debug( 'CiaoUser.callBackHandler() A javascript error occured inside the code! ' + event );	
		}
		
		// Unset the callback function
		oCallback = undefined;
	}
	
	// Private !
	this.callBackHandler = function( iLoginStatus ){
		// The request is no longer in progress, a new getLogin status can be handled!
		bRequestInProgress = false;
		
		// We need to try catch this because we do not want a bad callback function to break other parts of the page!
		try{
			// Every time smart login comes back reset the status to the proper value
			if( iLoginStatus == 0 || iLoginStatus == 1 ) bStatus = 1;
			else bStatus = 0;
			
			// Validate the integer value of iLoginStatus
			if( iLoginStatus != 0 && iLoginStatus != 1 && iLoginStatus != 2 ){
				console.debug( 'incorrect login status id returned' );
				return;
			}
			
			// Call the callback function
			oCallback( iLoginStatus );
		}
		catch( event ){
			console.debug( 'CiaoUser.callBackHandler() A javascript error occured inside the code! ' + event );
		}
		
		// Unset the callback function
		oCallback = undefined;
	}
	
	/**
	 * Bind the events needed for this instance
	 * 
	 * @access public
	 * @author Remco Verton
	 *
	 * @return {Void}
	 */
	function addEvents(){
		$( document ).bind( 'SLI_LoggedIn', function(){ bStatus = 1 } );
	} // func
	
	/**
	 * Checks if the current user logged in session has expired!
	 * 
	 * @access public
	 * @author Remco Verton
	 *
	 * @return {Bool} returns true if the session is expired false if not
	 */
	function isSessionExpired(){
		return false;
	} // func
	
} // class


/**
 * Inject innerHTML into an dom element. 
 * Only use this function if you have <script> tags inside the html and want them to be able to acces the elements inside sHTML! 
 * Will empty the given DOM element before injecting the html snippet
 * 
 * @access public
 * @author Remco Verton
 *  
 * @param {String} sHTML the html snippet that we inject into a dom element, needs a signle wrapper div around all html!
 * @param {String} oDOM the dom element to inject the html into, MUST BE A JQUERY OBJECT wrapping the dom
 * @return bool
 */
function innerHTMLScriptSafe( sHTML, oDOM ){
	try{
		if( oDOM.jquery == undefined ){
			oDOM = $( oDOM );
		}
		
		// !READ THIS BEFORE WORKING ON THIS CODE!
		// We have a big problem with script tags that are being injected with the dom.
		// They seem to execute during .apped() before the rest of the objects are ready.
		// The trick we are doing is to remove all script tags from the object that we create with $( '<html>' )
		// and then append them at a later point in time!
	
		var oInjected 	 = $( sHTML );		 // Turn the string into an object
		if( oInjected.length  == 1 ){
			var oScripts 	 = oInjected.find( 'script' );// Copy all script tags from the dom object
			oInjected.find( 'script' ).remove(); 	 // Remove all script tags from the dom object
			
			oDOM.empty();				 // Empty the control body
			oDOM.append( oInjected );		 // Append the body
			oDOM.append( oScripts );	 // Append the scripts, they execute now!
			return true;
		}
		else{
			console.debug( 'innerHTMLScriptSafe() sHTML needs to be a block of html contained inside a single div element, put a wrapper around it!' )
			return false;
		}
	}
	catch(e){ 
		if( console != undefined && console.debug != undefined ){
			console.debug( 'innerHTMLScriptSafe() A javascript error occured inside the code appended as a tab! ' + e );
		}
		return false;
	}
} // func

function cruxHideLoading(){
	
	if( e = document.getElementById( 'CruxLoadDiv') ){
		document.body.removeChild( e );
	}
} // func

function cruxShowLoading( sCountry ){
	var itop = 0;
	var aLoading = new Array();
	aLoading['de'] = "Laden";
	aLoading['uk'] = "Loading";
	aLoading['us'] = "Loading";	
	aLoading['fr'] = "Loading";
	aLoading['it'] = "Loading";
	aLoading['es'] = "Laddande";
	aLoading['nl'] = "Laden";
	aLoading['se'] = "Laddar";
	
	if( !document.getElementById( 'CruxLoadDiv') ){
		var txtNode = document.createTextNode( aLoading[ sCountry ] + "...." );  // needs localising
		var e = document.createElement( 'div' );
		var img = document.createElement( 'img' );
		img.setAttribute( 'src', 'http://images.ciao.com/iuk/images/crux/icons/pl_loading-ani.gif' );
			
		e.setAttribute( 'id', 'CruxLoadDiv' );
		e.appendChild( img );
		e.appendChild( txtNode );
		
		if( window.pageYOffset ){
           itop = window.pageYOffset+"px";
        } else if ( document.body.scrollTop ){
           itop = document.body.scrollTop+"px";
        } else if ( document.documentElement.scrollTop ){
           itop = document.documentElement.scrollTop+"px";
        }
		e.style.top = itop;
        document.body.appendChild( e );
	} 
}  // func

window.onscroll = function(){

	if( e = document.getElementById( 'CruxLoadDiv') ){
		itop = 0;
		if( window.pageYOffset ){
			var itop = window.pageYOffset+"px";
		} else if ( document.body.scrollTop ){
			var  itop = document.body.scrollTop+"px";
		} else if ( document.documentElement.scrollTop ){
			var itop = document.documentElement.scrollTop+"px";
		}
		
		e.style.top = itop;
	} 
} // func

/**
 * Class that enables external javascript and css files to be loaded on the fly.
 * Required by the tabcontroller and the sortable widget list
 * 
 * @access public
 * @author remco verton
 */
function ExternalFiles () {
	var oLoaded  = {};

	/**
	 * Checks if an external file has already been loaded
	 * Returns true if the file has been loaded, false if not
	 * 
	 * @access public
	 * @author remco verton 
	 * 
	 * @param {string} sFileName
	 * @return bool
	 */
	this.checkLoaded = function( sFileName ){		
		if( typeof oLoaded[ sFileName ] != 'undefined'){
			return true;
		}
		else {
			return false;
		}
	} // func
	
	/**
	 * Require an external javascript file
	 * 
	 * @access public
	 * @author remco verton 
	 * 
	 * @param {string} sFileName
	 * @return bool
	 */	
	this.loadJs = function( sFileName ) {		
		if( !this.checkLoaded( sFileName ) ){
			// inserting via DOM fails in Safari 2.0, so brute force approach
			//document.write('\n<script type="text/javascript" src="'+ sFileName +'"></script>');
			oLoaded[ sFileName ] = true;
			var oScript = $( '<script>' );
			oScript.attr({
				type: 'text/javascript',
				src: sFileName
			});
			$( 'head' ).append( oScript );
			//document.getElementsByTagName('head')[0].appendChild( oScript );
			return true;
		}
		else{
			return false;
		}
	} // func
	
	/**
	 * Require a list of external javascript files, format should be 
	 * 
	 * array( 
	 * 	0 => 'file'
	 *  1 => 'file'
	 * )
	 * 
	 * @access public
	 * @author remco verton 
	 * 
	 * @param {Array} aFileList
	 * @return bool
	 */	
	this.loadJsList = function( aFileList ) {		
		var bLoaded = false;
		for( var i = 0; i < aFileList.length; i++ ){
			if( this.loadJs( aFileList[i] ) ){
				bLoaded = true;
			}
		}
		return bLoaded;
	} // func
	
	/**
	 * Require an external css file
	 * 
	 * @access public
	 * @author remco verton 
	 * 
	 * @param {string} sFileName
	 * @return bool
	 */	
	this.loadCss = function( sFileName ) {		
		if( !this.checkLoaded( sFileName ) ){
			// inserting via DOM fails in Safari 2.0, so brute force approach
			//document.write('\n	<link href="'+ sFileName +'" rel="stylesheet" type="text/css" />');
			var Ocss  = document.createElement('link');
			Ocss.rel = 'stylesheet'
			Ocss.type = 'text/css';
			Ocss.href = sFileName;
			document.getElementsByTagName('head')[0].appendChild( Ocss );
			
			oLoaded[ sFileName ] = true;
			return true;
		}
		else{
			return false;
		}
	} // func
	
	/**
	 * Require a list of external css files, format should be 
	 * 
	 * array( 
	 * 	0 => 'file'
	 *  1 => 'file'
	 * )
	 * 
	 * @access public
	 * @author remco verton 
	 * 
	 * @param {Array} aFileList
	 * @return bool
	 */	
	this.loadCssList = function( aFileList ) {		
		var bLoaded = false;
		for( var i = 0; i < aFileList.length; i++ ){
			if( this.loadCss( aFileList[i] ) ){
				bLoaded = true;
			}
		}
		return bLoaded;
	} // func
	
} // class

function createCookie(name,value,days) {
	if (days) {
		var date = new Date();
		date.setTime(date.getTime()+(days*24*60*60*1000));
		var expires = "; expires="+date.toGMTString();
	}
	else var expires = "";
	
	// Unset the cookie for the whole domain
	var aDomain = 	 document.location.toString().split( '://' )[1].split( '/')[ 0 ].split( ':')[0].split('.');
	var sDomain = '';
	if( aDomain[ aDomain.length ] == 'uk' ){
		sDomain = '.' + aDomain[ aDomain.length -3 ] + '.' + aDomain[ aDomain.length -2 ] + '.' + aDomain[ aDomain.length -1 ];
	}
	else{
		sDomain = '.' + aDomain[ aDomain.length -2 ] + '.' + aDomain[ aDomain.length -1 ];
	}
	document.cookie = name+"="+value+expires+"; path=/"+';domain='+sDomain;
}

function readCookie(name) {
	var nameEQ = name + "=";
	var ca = document.cookie.split(';');
	for(var i=0;i < ca.length;i++) {
		var c = ca[i];
		while (c.charAt(0)==' ') c = c.substring(1,c.length);
		if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
	}
	return null;
}
function eraseCookie(name) {
	createCookie(name,"",-1);
}

/**
 * Extensions of the default String class
 * @author Alexander Dobrinescu
 */
String.prototype.trim = function() {
	return this.replace(/^\s+|\s+$/g,"");
}

String.prototype.ltrim = function() {
	return this.replace(/^\s+/,"");
}

String.prototype.rtrim = function() {
	return this.replace(/\s+$/,"");
}

var externalFiles =  new ExternalFiles();

/**
 * Main Crux Javascript object
 * Contains main functionality of the JS part of Crux, including wrapper functions to other libraries, configuration, etc.
 * @author Alexander Dobrinescu
 */
function CruxJS () {
	// Private properties

	// Public properties
	this.bCruxLoaded = true;
	this.Config = {
		Constants : {
			XAJAX_CRUX_METHOD : "crux method"
			}
	};
	
	/*
	 * Does an xajax request.
	 * Use it like this:
	 * callAjaxWithCallback('myMethod',
	 *						'myModule',
	 *						{ parameters: sInstanceId },
	 *						{ onComplete: function() {myCallbackComplete();}, onFailure: function() {myCallbackFailure;}, ... }
	 *						);
	 */
	this.callAjaxWithCallback = function(sMethodName, sModuleName, oParameters, oCallback) {
		var myLocalCallback = xajax.callback.create();
		if(oCallback != undefined) {
			for(sCallbackName in oCallback) {
				myLocalCallback[sCallbackName] = oCallback[sCallbackName];
			}
			oParameters.callback = myLocalCallback;
		}
		return this.callAjax(
				{
					crux_xjxtype: oCrux.Config.Constants.XAJAX_CRUX_METHOD,
					crux_xjxcls: sModuleName,
					crux_xjxmthd: sMethodName 
				},
				oParameters
	 		);
	};

	
	/**
	 * Creates and sends an XAjax request
	 * For backward compatibility, this function still supports the legacy syntax.
	 * 
	 * 1. Recomended (new) syntax:
	 * 		oCrux.callAjax( { crux_xjxtype:oCrux.Config.Constants.XAJAX_CRUX_METHOD,
	 * 						crux_xjxcls:'MyCruxClass',
	 * 						crux_xjxmthd:'MyMethod' }
	 * 					{ [XAjax params] } }
	 * 		);
	 * Where [XAjax params] is a list of properties accepted by the Xajax.request() method in the XAjax JS framework.
	 * You can also manually add a list of other XAjax options to the first json list,
	 * see also the JS documentation part of the XAjax framework.
	 * 
	 * 2. Legacy (obsolete) syntax - please DON'T USE it in new code:
	 * 		oCrux.call( 'CruxObject_Method', { [XAjax params] } );
	 * 
	 * @param mixed Arguments
	 * @return bool
	 */
	this.callAjax = function() {
		var iArgs = arguments.length;
		var sMethod;
		var aClass;
		var sClass;
		
		if ( 2 != iArgs ) return false;
		if ( "object" == typeof arguments[0] ) {
			return xajax.request( arguments[0], arguments[1] );
		} else if ( "string" == typeof arguments[0] ) {
			aClass = arguments[0].split( "_" );			
			sMethod = aClass.pop();
			sClass = aClass.join( '_' );
			return xajax.request(
				{
					crux_xjxtype:this.Config.Constants.XAJAX_CRUX_METHOD,
					crux_xjxcls:"cruxajax_" + sClass,
					crux_xjxmthd:sMethod
				},
				arguments[1]
			);
		}
		return false;		
	}

	this.wordCount = function( sText, iMinWordLength ) {
	
		var iWordCounter	= 0 ;
		if ( ! iMinWordLength ) {
			iMinWordLength = 2 ; 
		}
		var sText 	= sText;
		var sExp 	= /[^A-Za-z0-9'`"]+/gi;

		sText = sText.replace(sExp, " ");
				
		var sSplitString = sText.split(" ");

		for(t = 0 ; t < sSplitString.length ; t++)
			if(sSplitString[t].length >= iMinWordLength)
				iWordCounter ++ ;
	
		return iWordCounter ; 
	}

};

var oCrux = new CruxJS();
