// tst.js
// -----------------------------------------------------------------------------
// Powers Total Sales Tracking (TST)
// Handles TST sessions, runs STPS plugins (Session Tracking Plugin Scripts)
// and transmits and receives data from the TST server
//
// Release    > 1.49.8
// Project    > dmClubAutumn06:S01_C32
// 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_tst = true;
dm_pg_init.b1_tst = false;

// -----------------------------------------------------------------------------
// Where do the server side scripts live?
// Specify path of B1 session scripts, relative to dmb1_root (as defined in core.js)
var dmb1_register_sess 	= 'RegisterSess.cfm';
var dmb1_step_sess 		= 'StepSess.cfm';

// -----------------------------------------------------------------------------

if (dmb1_session_ref == undefined) var dmb1_session_ref; // the session reference

if (tst_public == undefined) {
	var tst_public = new Object(); // the tst_public object is used to allow data to piggyback along with the TST session ref
}
if (dmb1_pagedata.page.params.dmb1_additional) tst_public = dmb1_string_to_object(dmb1_pagedata.page.params.dmb1_additional);

// -----------------------------------------------------------------------------

var dmb1_firstpage; 				// set to true if this is the site entry page, for use by STPS plugins

var dmb1_salesref = new Object();	// the sales reference object
var dmb1_STPS = new Object();		// the plugins object, whereby dealer's can write plugin functions

var dmb1_site_entry;				// A flag set to 1 (true) on site entry, but not on subsequent pages

// -----------------------------------------------------------------------------
// Declare debugging object and debugging options
dmb1_debug.session_ref      = 0; // Debug session generation / retrieval
dmb1_debug.session_request 	= 0; // Debug the session request
dmb1_debug.salesref         = 0; // Debug the sales ref data
dmb1_debug.plugins          = 0; // Debug plugin functionality

dmb1_debuginfo.session_ref     = '';
dmb1_debuginfo.session_requset = '';
dmb1_debuginfo.salesref        = '';
dmb1_debuginfo.plugins         = '';

// -----------------------------------------------------------------------------

// THIS CODE GETS RID OF THE NEED FOR DEALERS TO MODIFY LINKS AND FORMS,
// BY USING EVENT CAPTURING (Gecko) and EVENT BUBBLING (IE) ...
//
// This is currently tested and working in Mozilla, Firefox, Safari 1 & 2,
// Opera, IE6 PC and IE5 Mac... Hooray!

// Attach event handlers to intercept events
if (window.addEventListener) { // W3C
	window.addEventListener('click', dmb1_handle_events, false);
} else if (window.attachEvent) { // IE  - Quirks Mode
	document.attachEvent('onclick', dmb1_handle_events);
} else {
	document.onclick = dmb1_handle_events; // IE5 Mac, and older browsers
}

// -----------------------------------------------------------------------------

function dmb1_handle_events(e) {
	// This function invokes follow_link(el) or submit_form(el)
	// (where el is a reference to the link or form)
	// if a link has been clicked or a form submitted, otherwise it does nothing
	var targ = dmb1_get_target(e);
	if (targ == targ.href) dmb1_SOA_follow_link(targ); // A link has been clicked
	else if (targ.parentNode.href) dmb1_SOA_follow_link(targ.parentNode); // A image inside a link has been clicked
	else if (targ.type == 'submit') dmb1_SOA_submit_form(targ.form);      // A submit button has been clicked	
}

// -----------------------------------------------------------------------------

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) { // defeat Safari bug
		targ = targ.parentNode;
	}
	return targ;
}

// *****************************************************************************
// *****************************************************************************
// *****************************************************************************

function dmb1_tst_page_init() {
	// Set cookie containing dmb1_session_ref on site entry only
	if (dm_pg_init.b1_tst) return;
	if (dmb1_site_entry == 1) {
		var exp = dmb1_get_exp_date(0,1,0); // Set expiry time 1 hour from now
		dmb1_set_cookie('dmb1_backup_session_ref', dmb1_session_ref, exp);
	}
	// Compile TST-related debugging information
	dmb1_compile_tst_debuging_info();
	dm_pg_init.b1_tst = true;	
}

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// -------------------- S E S S I O N   M A N A G E M E N T --------------------
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------

function dmb1_SOA_manage_session() {
	// MANAGE SESSION, an umbrella function that tries to retrieve the session reference,
	// constructs the standard sales ref, runs any sales analysis plugin scripts (STPS),
	// then sends the resultant sales ref to the TST server, and fetches an updated session ref
	
	// PRECEDENCE ORDER FOR DEALER ID
	// XComponent seed OVERRIDES Cookie OVERRIDES options file (b1_options.js)
	
	// (1) Are we using cookies to override the dealer ID, and the debug settings specified in dmb1_options.js
	dmb1_dealer_id = dmb1_dealer_id_override ? dmb1_dealer_id_override : dmb1_dealer_id;
	dmb1_debug.master = dmb1_debug.master ? dmb1_debug.master : dmb1_diagnostics;

	// (2) Has the dealer ID been seeded by XComponents code (from CreateTrolley response)
	dmb1_dealer_id = typeof dmb1_dealer_id_seed != 'undefined' ? dmb1_dealer_id_seed : dmb1_dealer_id;

	// Attempt to get dmb1_session_ref, if it isn't already defined
	if (dmb1_session_ref == undefined) {
		dmb1_session_ref = dmb1_fetch_param('dmb1_SessRef'); // expects dmb1_SessRef to be passed in the URL or embedded data
	}
	
	// Cookie-dependent backup mechanism
	// to avoid loosing session ref when user has returned to site entry page
	// by pressing back the browsers BACK button
	if (dmb1_session_ref == undefined) { // The user may have pressed the back button and returned to the site entry page
		var bu_ref = dmb1_get_cookie('dmb1_backup_session_ref'); // Try to get the back-up-ref
		dmb1_session_ref = bu_ref.length > 28 ? bu_ref : dmb1_session_ref; // Return value to undefined
		if (dmb1_session_ref) {
			dmb1_session_ref = dmb1_session_ref.substr(0, dmb1_session_ref.length-4) + '0000';
			// alert('ARRGH! \'BACK\' TO THE SITE ENTRY PAGE: ' + dmb1_session_ref);
		} else {
			dmb1_site_entry = 1;
		}
	} else if (dmb1_session_ref.length < 29) { // This happens when the root is set by dmShop PHP
		dmb1_site_entry = 1;        // We also need to set the backup cookie
	}

	// Chack whether this is a chat operator (session ref ends in '_c')
	if (dmb1_session_ref != undefined && dmb1_session_ref.slice(-2) == '_c') {
		// Set cookie, and skip tracking hit
		dmb1_site_entry = 1;
		return;
	}

	// Run dealer defined plugin functions
	dmb1_run_STPS();
	
	// Start or step session
	dmb1_old_session_ref = dmb1_session_ref;
	dmb1_do_session_hit();
}

// ------- H A N D L E   S E S S I O N   P R O P A G A T I O N ------

function dmb1_SOA_follow_link(el) {
	// Append TST session refererence and JSON representation of the 'tst_public' object to the URL
	// el is a reference to the HTML link node that generated the click event handled by this function
	if (el.href.toString().indexOf('dmb1_SessRef') != -1) return;
	var t = dmb1_add_session_data(el.href);
	el.href = t[1];
}

// -----------------------------------------------------------------------------

function dmb1_SOA_submit_form(el) {
	// Append TST session reference and JSON representation of the 'tst_public' object
	// to form (in new form field - hidden if possible (not possible on IE5 Mac))
	// New form fields are created dynamically if they do not already exist
	//
	// el is a reference to the HTML form that we want to append
	//
	if (el.dmb1_SessRef && el.dmb1_SessRef.value != '') return;
	
	var t = dmb1_add_session_data(el.action);
	var append = t[0]; var url = t[1];
	
	if (append) {
		// Set the form field named dmb1_SessRef it it already exists, otherwise create it then set it
		if (el.dmb1_SessRef != undefined) {
			el.dmb1_SessRef.value = dmb1_session_ref;
		} else {
			var new_field = document.createElement('input');
			new_field.setAttribute('type', 'hidden');
			new_field.setAttribute('name', 'dmb1_SessRef');
			new_field.setAttribute('value', dmb1_session_ref);
			el.appendChild(new_field);
		}
		
		var tst_public_serialized = dmb1_object_to_string(tst_public);
		if (tst_public_serialized != '{}') {
			// Set the form field named dmb1_additional it it already exists, otherwise create it then set it
			if (el.dmb1_additional != undefined) {
				el.dmb1_additional.value = tst_public_serialized;
			} else {
				var new_field = document.createElement('input');
				new_field.setAttribute('type', 'hidden');
				new_field.setAttribute('name', 'dmb1_additional');
				new_field.setAttribute('value', tst_public_serialized);
				el.appendChild(new_field);
			}
		}
		
		// Explicitly set (override) the method, if a valid optional second argument has been specified
		if (arguments[1] != undefined) {
			var new_method = arguments[1].toUpperCase();
			if (new_method == 'GET' || new_method == 'POST') el.method = new_method;
		}
		if (el.method.toUpperCase() == 'POST') {
			// Append session data to the form's 'action', so it is available to the next page in URL parameters
			el.action = url;
		}

	} else {
		el.action = url;
	}

	// Explicitly submit form, just incase clicking a scripted link triggered this function call (to submit a form on the page)
	// NB: It is best NOT to redefine the submit method by naming your <input type="sumbit" ...> element 'submit'.
	// This confuses javascript, and blocks access to the submit method!
	el.submit();
}

// -----------------------------------------------------------------------------

function dmb1_add_session_data(url) {
	// Add TST session reference (and arbitrary additional session data from tst_public) to the URL
	// if this is appropriate (ie. link within domain, or destination domain on is on 'safe hosts' list)
	//
	// RETURNS an array [whether to append - BOOLEAN, url to follow]
	
	// If url contains dmb1_notrack=1, don't append session ref, and strip notrack parameter
	if (url.match(/[&\?]dmb1_notrack=1/)) return [false, url.replace(/.dmb1_notrack=1/, '')];
	
	if (url.substring(0,11).toLowerCase() == 'javascript:') return [false, url]; // Don't alter when href begins 'javascript:....' 

	// Only append session data to URLs within the current domain,
	// and to URLs on hosts that appear in the 'safe hosts' list
	var append = false;
	if (url.substring(0,9).match(/(\w+):\/\/.*/)) {
		// Absolute URL, so check host
		var target = new dmb1_explode_url(url);
		if (target.host == dmb1_current.page.host) append = true;
		if (typeof dmb1_safehosts == 'object') {
			for (i in dmb1_safehosts) {
				if (target.host == dmb1_safehosts[i]) append = true;
			}
		}
	} else {
		// Root or relative URL, so append ref
		append = true;		
	}
	
	if (append) {
		var params = new Object(); // Append session data to URL
		params.dmb1_SessRef = dmb1_session_ref;	
		var tst_public_serialized = dmb1_object_to_string(tst_public);
		if (tst_public_serialized != '{}') params.dmb1_additional = tst_public_serialized;
		url = dmb1_append_param_to_url(url, params);
	}
	return [append, url];
}

// -----------------------------------------------------------------------------

function tst_map_params_for_ganal() {
	// Map incoming tst parameters to their Google Analytics equivalents
	// if link is tagged and click does not come from a Google AdWords ad
	if (typeof dmb1_current.page.params.tstc != 'undefined' && dmb1_current.page.params.tstc != 'gaw') {
		_uccn  = dmb1_current.page.params.tstc;
		if (typeof dmb1_current.page.params.tstm != 'undefined') _ucmd = dmb1_current.page.params.tstm;
		if (typeof dmb1_current.page.params.tsts != 'undefined') _ucsr = dmb1_current.page.params.tsts;
		if (typeof dmb1_current.page.params.tstt != 'undefined') _uctr = dmb1_current.page.params.tstt;
		if (typeof dmb1_current.page.params.tsta != 'undefined') _ucct = dmb1_current.page.params.tsta;
	}
}

// -----------------------------------------------------------------------------
// ------------- P R I V A T E   F U N C T I O N S -----------------------------
// -----------------------------------------------------------------------------

function dmb1_run_STPS() {
	// Run dealer defined plugin functions
	// Note that these are defined as methods of the 'dmb1_js_plugins' object
	var p, run;
	dmb1_firstpage = dmb1_session_ref ? false : true; // Allow plugins to know whether this is the site entry page
	for (var p in dmb1_STPS) {
		run = dmb1_STPS[p]();
		dmb1_debuginfo.plugins += (dmb1_debug.plugins || dmb1_debug.master) && (run != -1) ? 'Running plugin function: ' + p + '()<br>\n' : '';
	}
}

// -----------------------------------------------------------------------------

function dmb1_do_session_hit() {
	// Return a session reference
	// If dmb1_mode.fake_session_hit is true, return a fake session ref without connecting
	// to the dmClub server, otherwise make a connection to obtain the response
	
	if (dmb1_mode.fake_session_hit) { // Fake return of session ref
		if (dmb1_session_ref == undefined) {
			var date = new Date();
			var $dc = date.getFullYear() + '-' + date.getMonth() + '-' + date.getDate() + '-' + date.getHours() + '-' + date.getMinutes() + '-' + date.getSeconds();
			dmb1_session_ref = 'FAKED-SESSION-REF-' + $dc + '-0001';
		} else {
			var step = (parseInt(dmb1_session_ref.substr(dmb1_session_ref.length-4), 10) + 1).toString();
			while (step.length < 4) step = '0' + step;
			dmb1_session_ref = dmb1_session_ref.substr(0, dmb1_session_ref.length-4) + step;
		}
		dmb1_debuginfo.session_request = (dmb1_debug.session_request || dmb1_debug.master) ? 'In fake session hit mode' : '' ;
		return;
	}

	// NOT IN FAKING MODE IF WE REACH THIS POINT
		
	// Construct hit to session tracking app on dmClub server
	var query_data;
	var session_script;
		
	if (dmb1_session_ref) {
		if (dmb1_session_ref.length >= 29) { // We have a fully-formed TST session ref (29 or more chars), so step session
			query_data = '?dmb1_SessRef=' + dmb1_session_ref.slice(0, 29) + '&pd_Anal=' + encodeURIComponent(dmb1_object_to_string(dmb1_salesref));
			session_script = dmb1_step_sess;
		} else { // Seed session, usuing TST session root passed to page (24 chars)
			query_data = '?dmb1_SessRef=' + dmb1_session_ref + '&dmb1_DealerID=' + dmb1_dealer_id + '&pd_Anal=' + encodeURIComponent(dmb1_object_to_string(dmb1_salesref));	
			session_script = dmb1_register_sess;
		}
	} else { // Register session
		query_data = '?dmb1_DealerID=' + dmb1_dealer_id + '&pd_Anal=' + encodeURIComponent(dmb1_object_to_string(dmb1_salesref));	
		session_script = dmb1_register_sess;
	}
	query_data += '&pd_CurrURL=' + encodeURIComponent(location.href);
	query_data += '&pd_PrevURL=' + encodeURIComponent(document.referrer);

	// Do hit to session tracking app on dmClub server
	document.writeln('<script language="javascript" type="text/javascript" src="' + dmb1_root + session_script + query_data + '">');
	document.writeln('<\/scr' + 'ipt>'); // NB ESCAPE THE CLOSING SCRIPT TAG FOR THE HTML PARSER, SO IT DOES NOT TERMINATE THE CURRENT SCRIPT!
	dmb1_debuginfo.session_request = (dmb1_debug.session_request || dmb1_debug.master) ? dmb1_root + session_script + query_data : '' ;

}

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------

function dmb1_compile_tst_debuging_info() {
	// Compile and format debugging info
	
	if (dmb1_debug.fetch_param || dmb1_debug.master) {
		dmb1_debug_display += dmb1_debuginfo.fetch_param;
		dmb1_debug_display += '<br />\n';
	}

	if (dmb1_debug.plugins || dmb1_debug.master) {
		dmb1_debug_display += '<strong>dmb1_run_STPS()</strong><br />\n';
		dmb1_debug_display += dmb1_debuginfo.plugins;
		dmb1_debug_display += '<br />\n';
	}
	
	if (dmb1_debug.salesref || dmb1_debug.master) {
		dmb1_debug_display +='<strong>Data structure of dmb1_salesref, the sales reference object</strong><br />\n';
		dmb1_debug_display += '<table cellpadding="2" border="1">\n';
		dmb1_debug_display += dmb1_walk_object(dmb1_salesref, 'dmb1_salesref');
		dmb1_debug_display += '</table>\n<br />\n';
		dmb1_debug_display += '<strong>Serialised version of sales ref</strong><br />\n';
		dmb1_debug_display += dmb1_wrap_lines(dmb1_object_to_string(dmb1_salesref), dmb1_debug_max_linelength, 1);
		dmb1_debug_display += '<br /><br />\n';
	}
	
	if (dmb1_debug.session_request || dmb1_debug.master) {
		dmb1_debug_display += '<strong>dmb1_do_session_hit()</strong><br />\n';
		dmb1_debug_display += dmb1_wrap_lines(dmb1_debuginfo.session_request, dmb1_debug_max_linelength, 1);
		dmb1_debug_display += '<br /><br />\n';
	}

	if (dmb1_debug.session_ref || dmb1_debug.master) {
		dmb1_debug_display += '<strong>dmb1_SOA_manage_session()</strong><br />\n';
		dmb1_debug_display += 'Received Step Session Ref: ' + dmb1_old_session_ref + '<br />\n';
		dmb1_debug_display += 'New Step Session Ref: ' + dmb1_session_ref + '<br />\n';
		dmb1_debug_display += '<br />\n';
	}

	if (dmb1_debug.init || dmb1_debug.master) {
		dmb1_debug_display += dmb1_debuginfo.init;
		dmb1_debug_display += '<br />\n';
	}

}