// core.js
// -------------------------------------------------------------------------------------------------
// General utility functions (particularly, but not exclusively, for TST)
// Set and read cookies, manipulate and breakapart URLs,
// append parameters to URLs, serialise objects and arrays (JSON), etc
//
// Project    > dmABS
// Revision   > $Rev: 80 $
// Date		  > $Date: 2009-06-25 12:24:44 +0100 (Thu, 25 Jun 2009) $
// Author     > RAK for DML
// Copyright  > (C) Digital Mail Limited (2005-2009)
// -------------------------------------------------------------------------------------------------

// Add entry to library registration object, creating it if not present
if (typeof dmx_library != 'object') var dmx_library = new Object(); // Library registration object
if (typeof dmx_pg_init != 'object') var dmx_pg_init = new Object(); // Has page initialisation run?
dmx_library.b1_core = true;
dmx_pg_init.b1_core = false;

// Pre ECMAScript 3 compatability fixes (mainly for IE 5)
// Prevent 'undefined is undefined' error in IE by defining undefined (in Moz this is defined as the string 'undefined')
var undefined;
dmb1_pre_ecma_3_fixes();

// -------------------------------------------------------------------------------------------------
// Where do the server side scripts live?

// Specify B1 app root. Use absolute address, including a trailing slash
// var dmb1_root = 'http://devweb.digitalmail.com/dmb1_tst/';
var dmb1_root = 'http://my.dmclub.net/dmb1_tst/';

var post_param; // Holds post parameters echoed as JaVaDs

// Declare debugging object and debugging options
var tst_debug			= new Object(); // the debugging options object
tst_debug.master 		= 0; // Debug all of the following...
tst_debug.fetch_param	= 0; // Debug parameter recovery from URL and/or embedded object
tst_debug.init			= 0; // Debug page initialisation

var tst_debug_display = '';	// the string to which debugging info get appended, depending on the options set in tst_debug

var tst_debuginfo = new Object(); // Container object for debugging output
tst_debuginfo.fetch_param     = '';
tst_debuginfo.init            = '';

// Debugging display maximum linelength (characters)
var tst_debug_max_linelength = 80;

var dmb1_diagnostics = false;   // Diagnostics display flag set by cookie cutter (if there is one)
var dmb1_dealer_id = 9111;		// Set default dealer ID. This is usually overridden in dmb1_options.js
var dmb1_dealer_id_override;	// Holds override for dealer ID set by cookie cutter (if there is one)

// Declare application mode object and application operation switches
var dmb1_mode = new Object();

// -------------------------------------------------------------------------------------------------

// Place standard page data (inc params) in dmb1_pagedata object
var dmb1_pagedata = new dmb1_expose_pagedata(document.referrer, location.href);
var dmb1_current = dmb1_pagedata;

// Get browser and platform information
var dmb1_browser = new dmb1_expose_browserdata();

// Determine whether we are using the cookie cutter to set dealer id and diagnostics
dmb1_read_override_cookies();

// -------------------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------------------

function dmb1_read_override_cookies()
{
	// Can set dealer ID and TST debugging
	
	var dealer_id = dmb1_get_cookie('dmb1_dealer_id');
	if (dealer_id != '') dmb1_dealer_id_override = dealer_id;
	
	var temp = dmb1_get_cookie('dmx_debug_settings');
	if (temp) eval('temp =' + temp);
	if (temp.tst != undefined) dmb1_diagnostics = temp.tst == 1;
}

// -------------------------------------------------------------------------------------------------
// U T I L I T Y   F U N C T I O N S 
// -------------------------------------------------------------------------------------------------
// PARAMETER HANDLING

function dmb1_expose_pagedata(last_page, this_page) 
{
	// Gather a standard set of data on the page and its params
	// This function exposes data on the referrering page (url, protocol, host, path, query string, hash and parameters)
	// and current page data (url, protocol, host, path, query string, hash and parameters) returning an object

	pd = new Object();
	pd.ref  = dmb1_explode_url(last_page);
	pd.page = dmb1_explode_url(this_page);
	return pd;
}

// -------------------------------------------------------------------------------------------------

function dmb1_explode_url(url)
{
	// Take a URL and return an object that allows every part to be easily accessed
	// namely: the full url, protocol, host, path, query, hash and individual parameters

	var ex = new Object(); // Contains 'exploded' URL
	var regex = /(\w+):\/\/([\w.]+)(\/[^\?]*)?(\?[^#]*)?(#.*)*/; // regular expression to split up URL;
	var result = url.match(regex);
	if (result != null)
	{
		ex.error    = 0;
		ex.url      = result[0]; // the complete URL
		ex.protocol = result[1]; // the protocol
		ex.host     = result[2]; // the host
		ex.path     = result[3]; // the path to the page on the host
		ex.query    = result[4] != undefined ? result[4] : ''; // the query string, if any
		ex.hash     = result[5] != undefined ? result[5] : ''; // the hash (anchor) if any
		ex.params   = dmb1_explode_query_string(ex.query);     // eg. ?a=1&b=2 => ex.params.a (set to 1) and ex.params.b (set to 2)
	}
	else
	{
		ex.error = 'BADLY FORMED URL'; // May happen if HTTP Referrer data is stripped from request by a firewall, and this function is passed the referring URL
		ex.url = ex.protocol = ex.host = ex.path = ex.query = ex.hash = ex.params = ''; // Make sure these properties are defined, even if no referrer
	}
	return ex;
}

// -------------------------------------------------------------------------------------------------

function dmb1_explode_query_string(query)
{
	// Take a query string of form ?param1=value1&param2=value2...
	// extract name=value pairs and return a corresponding object
	
	var params = new Object();
	if (!query) return ''; // Empty property to prevent errors on converting serialised value back to an object

	query = query.substring(1);
	var pairs = query.split('&');
	
	for (i=0; i<pairs.length; i++)
	{
		var pos = pairs[i].indexOf('=');
		if (pos == -1) continue;
		var argname = pairs[i].substring(0, pos);
		var value = pairs[i].substring(pos+1);
		// alert(argname + ' HAS VALUE: ' + value);
		params[argname] = decodeURIComponent(value);
	}
	
	return params;
}

// -------------------------------------------------------------------------------------------------

function dmb1_fetch_param(param_name)
{
	// Return the value of the named parameter, resolving any precedence conflicts
	// POST params take precedence over GET params
	var temp;
	temp = post_param ? post_param[param_name] : undefined;
	temp = temp != undefined ? temp : dmb1_current.page.params[param_name];
	tst_debug.fetch_param || tst_debug.master ? tst_debuginfo.fetch_param += '<strong>dmb1_fetch_param(' + param_name + ')</strong><br />\n' : '';
	tst_debug.fetch_param || tst_debug.master ? tst_debuginfo.fetch_param += 'RETURNED VALUE: ' + temp + '<br><br>\n\n' : '';
	return temp;
}

// -------------------------------------------------------------------------------------------------

function dmb1_append_param_to_url(url, params)
{
	// Append one of more parameters to a url
	// Expects the URL as its first argument, and an object containing
	// a variable number of name-value pairs as its second

	// If the URL contains an anchor extract it, for concatenating to the modified URL later
	// Anchors should always be the last part of a URL, after the query string (if any)
	// URL ? name=value&name=value # anchor
	if (url.indexOf('#') != -1)
	{
		var anchor = url.slice(url.indexOf('#'));
		url = url.slice(0, url.indexOf('#'));
	}
	else
	{
		var anchor = '';
	}
	
	for (name in params)
	{
		url += '&' + name +'=' + encodeURIComponent(params[name]);
	}
	
	// If the url does not contain a '?' already, change the first '&' to '?'
	if (url.indexOf('?') == -1)
	{
		url = url.substr(0, url.indexOf('&')) + '?' + url.substr(url.indexOf('&') + 1);
	}
	url = url + anchor;
	return url;
}

// -------------------------------------------------------------------------------------------------

function dmb1_expose_browserdata()
{ 
	// Expose browser and platform data, returning an object
	this.ver =   navigator.appVersion;
	this.agent = navigator.userAgent;
	this.dom = document.getElementById ? 1 : 0;
	this.ie5 = (this.ver.indexOf('MSIE 5') > -1 && this.dom) ? 1 : 0; 
	this.ie6 = (this.ver.indexOf('MSIE 6') > -1 && this.dom) ? 1 : 0;
	this.ie  = this.ie5 || this.ie6;
	this.mac = this.agent.indexOf('Mac') > -1;
	this.moz = (this.dom && parseInt(this.ver) >= 5) ? 1 : 0; 
	this.ns6 = this.moz; 
	this.safari = this.agent.indexOf('AppleWebKit') > -1; 
	this.bw = (this.ie5 || this.ie6 || this.ns6);
	return this;
}

// -------------------------------------------------------------------------------------------------

function dmb1_reconstruct_browserdata(user_agent)
{
	// Reconstruct browser and platform data (from user_agent entry in session log),
	// returning an object
	// this.ver =   navigator.appVersion;
	var bd = new Object();
	bd.agent = user_agent;
	bd.dom = 'Cannot easily reconstruct this property';
	bd.ie5 = (user_agent.indexOf('MSIE 5') > -1) ? 1 : 0;
	bd.ie6 = (user_agent.indexOf('MSIE 6') > -1) ? 1 : 0;
	bd.ie = bd.ie5 || bd.ie6;
	bd.mac = user_agent.indexOf('Mac') > -1 ? 1 : 0;
	bd.safari = user_agent.indexOf('AppleWebKit') > -1 ? 1 : 0;
	bd.moz = (user_agent.indexOf('Mozilla/5') > -1 && !bd.ie && !bd.safari) ? 1 : 0;
	return bd;
}

// -------------------------------------------------------------------------------------------------
// SERIAILISATION

function dmb1_object_to_string(obj)
{
	// Serialise any javascript object
	// this is necessary before the data in an object can be
	// stored in a cookie or transmitted in an HTTP request
	// NB: This function can parse multi-demensional objects 
	// by recursively calling itself
	
	var val, output = '';
	if (obj && typeof obj == 'object')
	{
		output += '{';
		for (var i in obj)
		{
			val = obj[i];
			switch (typeof val)
			{
				case 'object':
					if (val[0])
					{
				 		output += i + ':' + dmb1_array_to_string(val) + ',';
					}
					else
					{
				 		output += i + ':' + dmb1_object_to_string(val) + ','; // RECURSION
					}
				 	break;
				
				case 'string':
					output += i + ':\'' + encodeURIComponent(val) + '\',';
					break;
				
				default:
					output += '"' + i + '":' + val + ',';
			}		
		}
		output = output.substring(0, output.length-1) + '}';		
	}
	else if (obj)
	{
		// the function has been passed a primitive data type (number, string or boolean), so just encode it and pass back
		output = encodeURIComponent(obj);
	}
	if (output == '}') output = '{}'; // Deal with case of empty object
	return output;
}

// -------------------------------------------------------------------------------------------------

function dmb1_array_to_string(array)
{
	// Serialise any javascript array
	// NB This may be called by and may call dmb1_object_to_string
	
	var output = '';
	if (array)
	{
		output += '[';
		for (var i in array)
		{
			val = array[i];
			switch (typeof val)
			{
				case 'object':
					if (val[0])
					{
						output += dmb1_array_to_string(val); // RECURSION
					}
					else
					{
						output += dmb1_object_to_string(val);
					}
					break;
				
				case 'string':
					output += '\'' + encodeURIComponent(val) + '\',';
					break;
				
				default:
					output += val + ',';
			}
		}
		output = output.substring(0, output.length-1) + ']';	
	}
	if (output == ']') output = '[]'; // Deal with case of empty array
	return output;
}
	
// -------------------------------------------------------------------------------------------------

function dmb1_string_to_object(string)
{
	eval('var result = ' + string);
	result = dmb1_walk_and_unescape_strings(result);
	return result;
}

// -------------------------------------------------------------------------------------------------

function dmb1_string_to_array(string)
{
	eval('var result = ' + string);
	result = dmb1_walk_and_unescape_strings(result);
	return result;
}

// -------------------------------------------------------------------------------------------------

function dmb1_walk_and_unescape_strings(obj)
{
	for (var i in obj)
	{
		val = obj[i];
		if (typeof val == 'object' )
		{
	 		dmb1_walk_and_unescape_strings(val); // RECURSION
	 	}
	 	else if (typeof val == 'string')
	 	{
	 		obj[i] = decodeURIComponent(val);		
	 	}
	}
	return obj;
}

// -------------------------------------------------------------------------------------------------
// COOKIE UTILITIES

function dmb1_get_cookie(name)
{
	// Retreive cookie by name
	var arg = name + '=';
	var alen = arg.length;
	var clen = document.cookie.length;
	var i = 0;
	while (i < clen)
	{
		var j = i + alen;
		if (document.cookie.substring(i, j) == arg) return dmb1_get_cookie_value(j);
		i = document.cookie.indexOf(' ', i) + 1;
		if (i == 0) break;
	}
	return false;
}

function dmb1_get_cookie_value(offset)
{
	// utility function called by dmb1_get_cookie
	var endstr = document.cookie.indexOf(';', offset);
	if (endstr == -1) endstr = document.cookie.length;
	return unescape(document.cookie.substring(offset, endstr));
}

// -------------------------------------------------------------------------------------------------

function dmb1_set_cookie(name, value, expires, path, domain, secure)
{
	// store cookie named name with value value
	// name and value are obligatory, the rest of the arguments are optional
	var cs = name + '=' + escape(value) +
		((expires) ? '; expires=' + expires : '') +
		((path) ? '; path=' + path : '; path=/') +
		((domain) ? '; domain=' + domain : '') +
		((secure) ? '; secure' : '');
		document.cookie = cs;
}

// -------------------------------------------------------------------------------------------------

function dmb1_delete_cookie(name, path, domain) 
{
	// remove a cookie by setting an ancient expiry date
	if (dmb1_get_cookie(name))
	{
		document.cookie = name + '=' +
			((path) ? '; path=' + path : '; path=/') +
			((domain) ? ';domain=' + domain : '') +
			'; expires=Thu, 01-Jan-70 00:00:01 GMT';
	}
}

// -------------------------------------------------------------------------------------------------

function dmb1_get_exp_date(days, hours, minutes)
{
	// utility function to retrieve an expriry date in proper format
	// pass three integer parameters for the number of days, hours and minutes
	// from now that you want the cookie to expire (or negative values for a past date)
	// NB: all three parameters are required, so use zeros where necessary
	var exp_date = new Date();
	if (typeof days == 'number' && typeof hours == 'number' && typeof minutes == 'number')
	{
		exp_date.setDate(exp_date.getDate() + parseInt(days));
		exp_date.setHours(exp_date.getHours() + parseInt(hours));
		exp_date.setMinutes(exp_date.getMinutes() + parseInt(minutes));
		return exp_date.toGMTString();
	}
}

// -------------------------------------------------------------------------------------------------
// GENERAL UTILITIES

function dmb1_walk_object(obj, root)
{
	// Produce the insides of a table showing the values stored in an object
	var val, output = '';
	if (obj && typeof obj == 'object')
	{
		for (var i in obj)
		{
			val = obj[i];
			if (typeof val == 'object')
			{
				output += dmb1_walk_object(val, root + '.' + i); // RECURSION
			}
			else
			{
				output += '<tr><td class="cell2i_thin">' + root + '.' + i + '</td><td class="cell2f_thin">' + dmb1_wrap_lines(val, tst_debug_max_linelength, 1) + '</td></tr>\n';
			}
		}
	}
	else if (obj && typeof obj == 'string')
	{
		output += '<tr><td>' + root + '</td><td>' + obj + '</td></tr>\n';
	}
	return output;
}

// -------------------------------------------------------------------------------------------------

function dmb1_wrap_lines(str, line_len, html_mode)
{
	var br = html_mode ? '<br>\n' : '\n';
	if (str.length > line_len)
	{
		for (var i=1, t=0; t<str.length; i++, t+=line_len)
		{
			str = str.substr(0, line_len*i) + br + str.substr(t+line_len);
		}
	}
	return str;
}

// -------------------------------------------------------------------------------------------------
// COMPATIBILITY FIXES

function dmb1_pre_ecma_3_fixes()
{
	// Make sure that code runs in clients which do not fully implement the ECMAScript 3 spec

	// Do encode URI methode exist? If not, create some 'quick and dirty' definitions
	if (typeof encodeURI != 'function')
	{
		encodeURI = new Function('arg', 'return escape(arg)');
		encodeURIComponent = new Function('arg', 'return escape(arg).replace("&","%26")');
		decodeURI = new Function('arg', 'return unescape(arg)');
		decodeURIComponent = new Function('arg', 'return unescape(arg).replace("%26","&")');
	}
	
	// Ensure Firebug debugging does nothing when Firebug is not installed
	if (typeof console == undefined)
	{
		console = new Object;
		console.log = console.dir = function() { ; }
	}
}

// -------------------------------------------------------------------------------------------------

function dmb1_get_target(e)
{
	// Get the target of an event
	var targ;
	if (!e) var e = window.event;
	if (e.target) targ = e.target;
	else if (e.srcElement) targ = e.srcElement;
	if (targ.nodeType == 3) targ = targ.parentNode; // defeat Safari bug
	return targ;
}

// -------------------------------------------------------------------------------------------------

function get_DOM_ref(obj)
{
	// cross-browser function to get an object's DOM reference given its id
	if (document.getElementById && document.getElementById(obj)) return document.getElementById(obj); // W3C DOM
	if (document.all && document.all(obj)) return document.all(obj); // MSIE 4 DOM
	if (document.layers && document.layers[obj]) return document.layers[obj]; // NN 4 DOM.. note: this won't find nested layers
	return false;
}

