// core.js
// -----------------------------------------------------------------------------
// General utility functions (particularly, but not exclusively, for TST)
// Handles page initialisation
// Set and read cookies, manipulate and breakapart URLs,
// append parameters to URLs, serialise objects and arrays (JSON), etc
//
// Release    > 1.49.8
// Project    > brand1_TST_aug05:S01_C04
// Changedate > 11-10-07
// Copyright  > (C) Digital Mail Limited (2005-2007)
// Author     > RAK for DML
// -----------------------------------------------------------------------------

// Add entry to library registration object, creating it if not present
if (typeof dm_library != 'object') var dm_library = new Object(); // Library registration object
if (typeof dm_pg_init != 'object') var dm_pg_init = new Object(); // Has page initialisation run?
dm_library.b1_core = true;
dm_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://localhost/test-server/DML/MarComs04/brand1_TST_aug05/sales_origin_analysis/code_10/';
var dmb1_root = 'http://my.dmclub.net/dmb1_tst/';

// ------------------------------------------------------------
var post_param; // Holds post parameters exhoed as JaVaDs

// ------------------------------------------------------------
// Declare debugging object and debugging options
var dmb1_debug			= new Object(); // the debugging options object
dmb1_debug.master 		= 0; // Debug all of the following...
dmb1_debug.fetch_param	= 0; // Debug parameter recovery from URL and/or embedded object
dmb1_debug.init			= 0; // Debug Brand One page initialisation

var dmb1_debug_display = '';	// the string to which debugging info get appended, depending on the options set in dmb1_debug

var dmb1_debuginfo = new Object(); // Container object for debugging output
dmb1_debuginfo.fetch_param     = '';
dmb1_debuginfo.init            = '';

// Debugging display maximum linelength (characters)
var dmb1_debug_max_linelength = 80;

var dmb1_extent		 = 'full';	// Extent of diagnostrics ie 'min' or 'full'
var dmb1_diagnostics = false;   // Diagnostics display flag set by cookie cutter (if there is one)
var dmb1_dealer_id = 9222;		// 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();
dmb1_mode.use_get_post_util = 0; // Point all links and form submissions at the get/post debugging utility

// ------------------------------------------------------------
// ------------------------------------------------------------
// ------------------------------------------------------------

// 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_testsite_cookies();

// Set up page initialisation event handler
if (window.addEventListener) { // W3C
	window.addEventListener('load', dmb1_page_init, true);
} else if (window.attachEvent) { // IE  - Quirks Mode
	window.attachEvent('onload', dmb1_page_init);
} else {
	window.onload = dmb1_page_init;
}

// ------------------------------------------------------------

function dmb1_read_testsite_cookies() {
	// Are we using the testsite cookies to define the dealer ID and debugging switches?
	var dealer_id = dmb1_get_cookie('dmb1_dealer_id');
	if (dealer_id != '') {
		dmb1_dealer_id_override = dealer_id;
	}
	dmb1_diagnostics = dmb1_get_cookie('dmb1_diagnostics');
	dmb1_diagnostics = dmb1_diagnostics == 'true' ? true : false; // Convert back to true Boolean datatype
	dmb1_extent = dmb1_get_cookie('dmb1_extent');
}

// ------------------------------------------------------------
// ------------ P A G E   I N I T I A L I S A T I O N ---------
// ------------------------------------------------------------

function dmb1_page_init() {	
	if (dm_pg_init.b1_core) return;
	// Page initialisation wrapper function
	dmb1_debug.init || dmb1_debug.master ? 	dmb1_debuginfo.init += '<strong>dmb1_page_init()</strong><br>\n' : '';
			
	if (dm_library.b1_tst) {
		dmb1_tst_page_init();
		dm_pg_init.b1_tst = true;	
	}
	if (dm_library.dmb0_lib) {
		dmb0_page_init();
		dm_pg_init.dmb0_lib = true;	
	}
	dm_pg_init.b1_core = true;	

	// Are we setting a Google Analytics e-commerce conversion?
	if (document.forms.utmform) {
		if (document.forms.utmform.utmtrans && document.forms.utmform.utmtrans.value.replace('/\s/', '').length) __utmSetTrans(); // Run __utmSetTrans() from urchin.js (Google javascript)
		if (document.forms.utmform.transfer_url) __utmLinker(document.forms.utmform.transfer_url.value); // If there's an extra field 'transfer_url' in the utmtrans form,  call __urmLinker
	}

	// Are we running with diagnostics enabled?
	if (dmb1_debug_display != '') {
		var d = document.createElement('div');
		d.style.padding = '12px';
		d.style.display = 'block';
		
		// Write header and [ + ] or [ - ] link
		var plus_minus = dmb1_return_toggle_span(dmb1_extent == 'full', 'toggle');
		var output = '<br><h1>TST debugging information <a href="#" onclick="dmb1_toggle_diagnostics(); return false;">' + plus_minus + '</a></h1>';
		d.innerHTML = output;
		
		var detail = document.createElement('div');
		detail.setAttribute('id', 'diagnostic_toggle_area');
		detail.style.display = dmb1_extent == 'full' ? 'block' : 'none';
		detail.innerHTML = dmb1_debug_display;
		
		d.appendChild(detail);
		window.document.body.appendChild(d);
	}
}

// ------------------------------------------------------------
// ------------------------------------------------------------
// ------------ U T I L I T Y   F U N C T I O N S -------------
// ------------------------------------------------------------
// ------------------------------------------------------------

function dmb1_toggle_diagnostics() {
	// Toggle to the opposite state, and set a cookie to preserve the toggle stste
	dmb1_extent = dmb1_extent == 'full' ? 'min' : 'full';
	var exp = dmb1_get_exp_date(1,0,0); // Set a cookie expiry date 24 hours from now
	dmb1_set_cookie('dmb1_extent', dmb1_extent, exp);
	
	// Change the toggle link
	var s = get_DOM_ref('toggle');
	s.innerHTML = dmb1_return_toggle_text(dmb1_extent == 'full');
	
	// Toggle the display of the diagnostic details
	var detail = get_DOM_ref('diagnostic_toggle_area');
	detail.style.display = dmb1_extent == 'full' ? 'block' : 'none';
}

// ------------------------------------------------------------

function dmb1_return_toggle_span(state, toggle_id) {
	// Returns '[ - ]' if state not 0, otherwise '[ + ]', in a span with the id given by toggle_id
	var toggle_span = '<span id="' + toggle_id + '">';
	toggle_span    += dmb1_return_toggle_text(state);
	toggle_span    += '</span>';
	return toggle_span;
}

function dmb1_return_toggle_text(state) {
	var toggle_text = state ? '[ - ]' : '[ + ]';
	return toggle_text;
}

// ------------------------------------------------------------
// ------------ P A R A M E T E R   H A N D L I N G -----------
// ------------------------------------------------------------

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];
	dmb1_debug.fetch_param || dmb1_debug.master ? dmb1_debuginfo.fetch_param += '<strong>dmb1_fetch_param(' + param_name + ')</strong><br />\n' : '';
	dmb1_debug.fetch_param || dmb1_debug.master ? dmb1_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;
}

// ------------------------------------------------------------
// --------------- S E R I A I L I S A T I O N ----------------
// ------------------------------------------------------------

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;
}

// ------------------------------------------------------------
// ------------- C O O K I E   U T I L I T I E S --------------
// ------------------------------------------------------------

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 '';
}

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();
	}
}

// ------------------------------------------------------------
// ------------- G E N E R A L   U T I L I T I E S ------------
// ------------------------------------------------------------

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, dmb1_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;
}

// ------------------------------------------------------------
// ----------- C O M P A T I B I L I T Y   F I X E S ----------
// ------------------------------------------------------------

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","&")');
	}
}

// ------------------------------------------------------------

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
	} else if (document.all && document.all(obj)) {
		return document.all(obj); // MSIE 4 DOM
	} else if (document.layers && document.layers[obj]) {
		return document.layers[obj]; // NN 4 DOM.. note: this won't find nested layers
	}
	return false;
}