// $Id: mussam.js,v 1.1.2.15 2010/04/28 09:00:40 jrn Exp $s

/**
	* @fileOverview The MUSSAM singleton with utilities for logging and various useful functions.  
	* @version 0.6
	*
	*/

// Class def and instantiation singleton instantiation.

// Class Constants

// These are used by the constructor

MUSSAM_RTO_COOKIE_USE_LOGGING = 'rtoUseLogging';
MUSSAM_RTO_COOKIE_MUSSAM_USE_INTERNAL_LOGGING = 'rto_MUSSAM_UseInternalLogging';
MUSSAM_DEPLOYMENTCONTEXT = "mussam";

/**
	* @private utility function for reading a cookie, used in the object constructor. Therefore it is defined as a global function before the constructor function.
	*/
MUSSAM_readACookie = function(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;
}

/**
	* @class Mussam Global Manager.
	* 
	* <b>MUSSAM</b> is a global singleton object which offers common utilities. 
	* When the <b>mussam.js</b> file is included, the MUSSAM instance is automatically created.<br/>
	* 
	* The MUSSAM instance offers
	* <dl>
	*   <dt>logging
	*	  <dd> The MUSSAM instance instantiates a log4javascript object which can be used for debug logging. You can set the MUSSAM.useLogging to true or false depending on whether you want logging or not.
	* </dl>    
	* @requires log4javascript.js
	*
 	* @property useLogging if true, the MUSSAM functions will log messages.
 	* 
 	* @property useInternalLogging is false by default. If you set MUSSAM.useInternalLogging, the MUSSAM will log messages from its internal workings.<br/> 
 	* 
 	*
 	* @depends json2.js, log4javascript.js 
 	*
	* @see 
	* <a href="http://webreflection.blogspot.com/2007/02/better-singleton-pattern-example.html">Singleton pattern with JavaScript</a>
 	*/ 
function MUSSAM_Class(instance) {
	if(!MUSSAM_Class.getInstance) {
  	MUSSAM_Class.getInstance = function(){	
   		return instance;
  	}; instance = new MUSSAM_Class;
 	};
 
 	// properties
 	this.logger = log4javascript!=null && log4javascript ? log4javascript.getDefaultLogger() : false;
 	// str.substring(0, value.length)===value;
 	var deployUrl = "https://www.kulturarv.dk/mussam";
 	// default: use logging if the user has set enabled the isViewLog cookie
	this.useLogging = (MUSSAM_readACookie(MUSSAM_RTO_COOKIE_USE_LOGGING) === "1"); 
	this.useInternalLogging = (MUSSAM_readACookie(MUSSAM_RTO_COOKIE_MUSSAM_USE_INTERNAL_LOGGING) === "1"); // if set to true, log internal messages
	
//	this.useLogging = true; 
//	this.useInternalLogging = true;
//	 	
}(new MUSSAM_Class);   



MUSSAM_Class.prototype.setupPluginLog = function(base) {
  var log = { info: function(){}, error: function(){}, warn: function(){}, debug: function(){} }; // default no-op logging object
  if (MUSSAM.useLogging && log4javascript != null && log4javascript && base.options.useLogging) {
          base.logger = log4javascript.getDefaultLogger();
          log = { info: function(m){ base.logger.info( base.pluginName+"."+m ) }, error: function(m){ base.logger.error( base.pluginName+"."+m ) }, warn: function(m){ base.logger.warn( base.pluginName+"."+m ) }, debug: function(m){ base.logger.debug( base.pluginName+"."+m ) } };
  }
  return log;
}

MUSSAM_Class.prototype.jsStr = function(s) {
  if (!s) {
    return s;
  }
  return s.replace('"', '\\\"').replace("'", "\\\'");
}

MUSSAM_Class.prototype.jsStrNotNull = function(s) {
  if (!s) {
    return "";
  }
  return s.replace('"', '\\\"').replace("'", "\\\'");
}


MUSSAM_Class.prototype.attrStr = function(s) {
  if (!s) {
    return s;
  }
  return s.replace('"', '&quot;').replace("'", "&apos;");
}

// Constants

//MUSSAM_Class.prototype.DEFAULT_PAGE_SIZE = 10; //- not used anymore, use MUSSAM.getJspInfo('mussamPageSize') instead
MUSSAM_Class.prototype.DEFAULT_SORT_LIST = "betegnelseList";

// log4javascript logging

/**
 *1s to the log4javascript error log.
 */
MUSSAM_Class.prototype.writeLogError = function(myContent) {
	if (this.useLogging && this.logger) this.logger.error(myContent);
}

/**
 * Writes to the log4javascript error log.
 */
MUSSAM_Class.prototype.writeLogWarn = function(myContent) {
	if (this.useLogging && this.logger) this.logger.warn(myContent);
}

/**
 	* Writes to the log4javascript info log.
 	*/
MUSSAM_Class.prototype.writeLogInfo = function(myContent) {
	if (this.useLogging && this.logger) this.logger.info(myContent);
}

/**
 	* Writes to the log4javascript debug log.
 	*/
MUSSAM_Class.prototype.writeLogDebug = function(myContent) {
	if (this.useLogging && this.logger) this.logger.debug(myContent);
}


/**
 * Write internal message to the log4javascript error log.
 */
MUSSAM_Class.prototype.writeInternalLogError = function(myContent) {
	if (this.useInternalLogging && this.useLogging && this.logger) this.logger.error(myContent);
}

/**
 * Write internal message to the log4javascript error log.
 */
MUSSAM_Class.prototype.writeInternalLogWarn = function(myContent) {
	if (this.useInternalLogging && this.useLogging && this.logger) this.logger.warn(myContent);
}

/**
 	* Write internal message to the log4javascript info log.
 	*/
MUSSAM_Class.prototype.writeInternalLogInfo = function(myContent) {
	if (this.useInternalLogging &&  this.useLogging && this.logger) this.logger.info(myContent);
}

/**
 	* Write internal message to the log4javascript debug log.
 	*/
MUSSAM_Class.prototype.writeInternalLogDebug = function(myContent) {
	if (this.useInternalLogging && this.useLogging && this.logger) this.logger.debug(myContent);
}

/**
 * Initialize the JSP information.
 * Call this in JSP pages including mussam.js. 
 * This is the preferred way to inject information from the server to the MUSSAM object (via a JSP page).
 */
MUSSAM_Class.prototype.setJSPInfo = function(infoHash) {
	this.jspInfo = infoHash;
}

/**
  * Set a parameter in the JSP information hash. Of course this 
  * Call this in JSP pages including mussam.js. 
  * This is the preferred way to inject information from the server to the MUSSAM object (via a JSP page).
  */
 MUSSAM_Class.prototype.setJSPInfoParam = function(paramName, paramValue) {
	if (this.jspInfo == null) {
		this.jspInfo = {};
	}
	
	this.jspInfo[paramName] = paramValue;
 }

 
/**
* Return a parameter from the JSP information.
* This information must be set by a script (by caling setJSPInfo() or setJSPInfoParam) in the page including mussam.js.
*
*/
MUSSAM_Class.prototype.getJSPInfo = function(jspParam) {
	if (this.jspInfo[jspParam] ) {
		return this.jspInfo[jspParam];
	} else {
		this.writeInternalLogDebug("MUSSAM.getJSPInfo() - " + jspParam + " not found");
		return null;
	}
}

// properties

/**
 * Get the mussamPageSize property - if it's null we get the value from the JSPInfo.
 */
MUSSAM_Class.prototype.getMussamPageSize = function() {
	if (this.mussamPageSize == null) {
		this.mussamPageSize = this.getJSPInfo('mussamPageSize') > 0 ? this.getJSPInfo('mussamPageSize') : 10;
	}
	
	return this.mussamPageSize; 
}

/**
* Set the mussamPageSize property to the specified value (overwriting what might have been set by JSPInfo).
*
*/
MUSSAM_Class.prototype.setMussamPageSize = function(newPageSize) {
	this.mussamPageSize =	newPageSize;
}


/**
 * Method for creating a url 
 */ 	
MUSSAM_Class.prototype.url = function(urlHash) {
	var url = '';
	if (typeof urlHash == 'object') {
		var dlm = '?';
		
		if (urlHash.url != null) {
			url = urlHash.url;
			if (!this.strStartsWith(urlHash.url, 'https://www.kulturarv.dk/mussam')) {				
				url = "https://www.kulturarv.dk/mussam/" + url;
			}	
		}
		if (urlHash.event != null) {
			url += dlm + encodeURI(urlHash.event);
			dlm = '+';
		}
		if (urlHash.parameters != null) {
			if (typeof urlHash.parameters == 'object') {
				for (var param in urlHash.parameters) {
					url += dlm + encodeURI(param) + "=" + encodeURI(urlHash.parameters[param]);
					dlm = '&';
				}				
			} else { // assume string
				url += dlm + encodeURI(urlHash.parameters);
			}
		}
	} else { // assume a String
		url = urlHash;
		if (!this.strStartsWith(url, 'https://www.kulturarv.dk/mussam')) {
			url = "https://www.kulturarv.dk/mussam" + url;
		}
	}
	return url;
}

/**
  * Adds a namespace to the MUSSAM global object if it doesn't exist and returns it<br/> 
  *
  * Uses the "Namespace" or <a href="http://yuiblog.com/blog/2007/06/12/module-pattern/">Module pattern</a>
	* 
	* @returns - the referred namespace, so you can use it to refer to the namespace and create it right away if it doesn't exist
	*/
MUSSAM_Class.prototype.namespace = function(namespace) {
	if (!this[namespace]) {
		this[namespace] = {};
	}	
	return this[namespace];
}

// Utility functions

/** 
	* Returns the class name of the argument or undefined if it's not a valid JavaScript object.
	* 
  */  
MUSSAM_Class.prototype.getObjectClass = function(obj) {  
  if (obj && obj.constructor && obj.constructor.toString) {  
	  var arr = obj.constructor.toString().match(/function\s*(\w+)/);  	
	  if (arr && arr.length == 2) {  
	      return arr[1];  
	  }  
	}    
	return undefined;  
}  

// Misc


/**
	* Returns str with first letter as lower case.
	*/ 
MUSSAM_Class.prototype.strFirstLowerCase = function (str) {	
	if (typeof str != "string") {
		throw("NOT A STRING: "+str);
	}
	if (str == null) {
		return null;
	}
	
	return str.substring(0,1).toLowerCase()+str.substring(1);	
}

/**
* Returns str with first letter as upper case.
*/ 
MUSSAM_Class.prototype.strFirstUpperCase = function (str) {	
if (typeof str != "string") {
	throw("NOT A STRING: "+str);
}
if (str == null) {
	return null;
}

return str.substring(0,1).toUpperCase()+str.substring(1);	
}



/**
	* Returns true if the supplied <i>str</i> starts with the <i>value</i> substring.
	*/ 
MUSSAM_Class.prototype.strStartsWith = function (str, value) {	
	if (typeof str != "string") {
		alert("NOT A STRING: "+str);
	}
  if (str == null || value == null || (str.length < value.length)) {
    return false;
  } else {
    return str.substring(0, value.length)===value;
  }
}

/**
	* Returns true if the supplied <i>str</i> ends with the <i>value</i>' substring.
	*/ 
MUSSAM_Class.prototype.strEndsWith = function (str, value) {
  if (str == null || value == null || (str.length < value.length)) {
    return false;
  } else {
    return str.substring(str.length-value.length, str.length)===value;
  }
}


/**
	* Utility function for setting a cookie
	*/
MUSSAM_Class.prototype.createCookie = function(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 = "";
	document.cookie = name+"="+value+expires+"; path=/";
}

/**
	* Utility function for reading a cookie.
	*/
MUSSAM_Class.prototype.readCookie = function(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;
}

/**
	* Utility function for deleting a cookie
	*/
MUSSAM_Class.prototype.deleteCookie = function (name) {
	this.createCookie(name,"",-1);
}

 /**
	* Binding function which returns a function reference which will call the function in the context of the supplied object (that is, as a method of the object).   
	* Similar to Prototype's .bind() extension, but called as a function with arguments instead of being an extension to Object.
	* You must supply arguments object (the object context) and the function to bind. Further arguments will be supplied to the bound function with each call.
	*/
MUSSAM_Class.prototype.bindFunction = function(obj, bindThisFunction) { // obj, bindThisFunction, <args>
	var obj = arguments[0];
	if (typeof obj != 'object') {
		throw "MUSSAM._bindFunction() - obj parameter is not an object";
	}
	var bindThisFunction = arguments[1];
	if (typeof bindThisFunction != 'function') {
		throw "MUSSAM._bindFunction() - addedFunction parameter is not a function";
	}
	var MUSSAM = this; 	
	
	if (arguments.length <= 2) {
		return function() {
    	return bindThisFunction.apply(obj, arguments); // call bound function with the arguments passed to the wrapper (closure) function 
  	}	
	} else {
		var constantArgs = this.listToArray(arguments).slice(2); 
		return function() {
    	return bindThisFunction.apply(obj, constantArgs.concat(MUSSAM.listToArray(arguments))); // supply bound function with both the constant arguments and the new arguments
  	}	
	}
			
}

/**
	* Generic method for checking an object for a specific interface (ie value properties and function properties aka methods).
	* @param reqProperties - list of the required property names
	* @param regFunctions - list of the required method names
	*/
MUSSAM_Class.prototype.checkInterface = function(obj, reqProperties, reqFunctions) {
	var missing = "";
	var dlm = "";
	for (var i=0; i<reqProperties.length; i++) {
		if (obj[reqProperties[i]] === undefined) { // just check if the property exists, it may be null
			missing += dlm + reqProperties[i];
			dlm = ", ";
		}  
	}
	for (var i=0; i<reqFunctions.length; i++) {
		if ( (obj[reqFunctions[i]] == null) || (obj[reqFunctions[i]] != null && typeof(obj[reqFunctions[i]]) != 'function') ) {
			missing += dlm + reqFunctions[i]+"()";
			dlm = ", ";		
		}
	}
	return missing;
}	

MUSSAM_Class.prototype.isEmpty = function(o) {
    var i, v;
    if (typeof(o) === 'object') {
        for (i in o) {
            v = o[i];
            if (v !== undefined && typeof(v) !== 'function') {
                return false;
            }
        }
    }
    return true;
}

/**
  * Opens a new popup window.  
  *
  * @returns the new window object
  */
MUSSAM_Class.prototype.openNewPopup = function (url, xsize, ysize) {
  var imageWin = top.open(url,"","toolbar=0,location=0,directories=0,status=0,menubar=0,scrollbars=1,resizable=1,copyhistory=0,width="+xsize+",height="+ysize);
  imageWin.focus();
  
  return imageWin;
}

/**
 * Looks up a key in the (js version of) StripesResources and returns the appropriate message.
 * If the key is not found, it returns the key name wrapped with ???!.
 * 
 * Note that the keys can be used with or without the leading "mussam.".
 */
MUSSAM_Class.prototype.message = function(msgKey) { // extra arguments to the function are treated as arguments (){0}, {1}, ...) to the StripesResource message
	var lang = this.getJSPInfo('language');
	lang = lang != null ? lang : 'da';
	
	this.writeInternalLogDebug("MUSSAM.message() - now looking up "+msgKey+" for language "+lang); 
	var trunkedMsgKey = null;
	if (this.strStartsWith(msgKey, "mussam.")) {
		trunkedMsgKey = msgKey.substring(7);
//		this.writeInternalLogDebug("MUSSAM.message() - and now looking up truncated key "+trunkedMsgKey+" for language "+lang);
		if (this.msg[lang][trunkedMsgKey]) {
			return this.formatMessage(lang, trunkedMsgKey, arguments);
		}
	}
	
	if (this.msg[lang][msgKey]) {
		return this.formatMessage(lang, msgKey, arguments);
		//return this.formatMessage(lang, this.msg[lang][msgKey], arguments);
	} else {
		//this.writeInternalLogWarn("MUSSAM.message() - key '"+msgKey + "' not found");
		return "???!"+msgKey+"!???";
	}
}

 /**
  * 	Replace {n} arguments with possible extra arguments to this function. {0} is replaced with the second argument and so on
  */
MUSSAM_Class.prototype.formatMessage = function(lang, msgKey, msgArgs) {
	var myMsg = this.msg[lang][msgKey];
	var argPattern = new RegExp("", "g");
	for(var i=1; i<msgArgs.length; i++) {
		argPattern.compile("\\{" + (i-1) + "\\}");
		myMsg = myMsg.replace(argPattern, msgArgs[i]);
	}
	
	return myMsg;
}


MUSSAM_Class.prototype.contextPath = function() {
   if (!MUSSAM.contextPathValue) {
     if (MUSSAM.strStartsWith(window.location.pathname, "/" + MUSSAM_DEPLOYMENTCONTEXT)) {
       var secondSlashPos = window.location.pathname.substring(1).indexOf("/")+1;        
       if (secondSlashPos > 0 ) {        
         MUSSAM.contextPathValue = window.location.pathname.substring(0, secondSlashPos);
         this.writeInternalLogDebug("Updating MUSSAM.contextPathValue=" + MUSSAM.contextPathValue);
       }
     } else {
       MUSSAM.contextPathValue = "";
     }
   }
   return MUSSAM.contextPathValue;
}


if (!this.MUSSAM) {
	// Create MUSSAM
	MUSSAM = MUSSAM_Class.getInstance();		
	MUSSAM.writeInternalLogDebug("MUSSAM created");
	
	// Create resource namespace and hashes
	
	MUSSAM.namespace("msg");
	MUSSAM.msg = {
			'da' : {},
			'gr' : {},
			'fo' : {} 
	};
}

