// 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
//
// 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.tst = true;
dmx_pg_init.tst = false;

// Where do the server side scripts live?
// Specify path of B1 session scripts, relative to dmb1_root (as defined in core_1.5.0-7.js)
var tst_register_sess 	= 'RegisterSess.cfm';
var tst_step_sess 		= 'StepSess.cfm';

// -------------------------------------------------------------------------------------------------

if (dmb1_session_ref == undefined) var dmb1_session_ref; // the session reference

 // the tst_public object is used to allow data to piggyback along with the TST session ref
if (tst_public == undefined) var tst_public = new Object();
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_STPS = new Object();		// the plugins object, whereby dealers can write plugin functions

var dmb1_site_entry;				// A flag set to 1 (true) on site entry, but not on subsequent pages

if (typeof tst_additional_params == 'undefined') tst_additional_params = new Object(); // Additional params to add to the TST hit - may already be seeded

// -------------------------------------------------------------------------------------------------
// Declare debugging object and debugging options
tst_debug.session_ref      = 0; // Debug session generation / retrieval
tst_debug.session_request  = 0; // Debug the session request
tst_debug.plugins          = 0; // Debug plugin functionality
tst_debug.tst_additional   = 0; // Debug additional TST data added by STPS

tst_debuginfo.session_ref     = '';
tst_debuginfo.session_requset = '';
tst_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 & 3
// Opera 6+, IE6 PC and IE5 Mac... Hooray!

// Attach event handlers to intercept events
if (window.addEventListener)
{
	window.addEventListener('click', tst_handle_events, false);  // W3C
}
else if (window.attachEvent)
{
	document.attachEvent('onclick', tst_handle_events); // IE  - Quirks Mode
}
else
{
	document.onclick = tst_handle_events; // IE5 Mac, and older browsers
}

// -------------------------------------------------------------------------------------------------

function tst_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) tst_follow_link(targ); // A link has been clicked
	else if (targ.parentNode.href) tst_follow_link(targ.parentNode); // A image inside a link has been clicked
	else if (targ.type == 'submit') tst_submit_form(targ.form);      // A submit button has been clicked	
}

// *************************************************************************************************
// *************************************************************************************************
// *************************************************************************************************

function tst_init()
{
	// Set cookie containing dmb1_session_ref on site entry only
	if (dmx_pg_init.tst) return;
	if (dmb1_site_entry == 1)
	{
		dmb1_set_cookie('dmb1_backup_session_ref', dmb1_session_ref); // Deleted when browser closed
	}
	// Compile TST-related debugging information
	tst_compile_debuging_info();
	dmx_pg_init.b1_tst = true;	
}

// -------------------------------------------------------------------------------------------------
// SESSION MANAGEMENT

function tst_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;
	tst_debug.master = tst_debug.master ? tst_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
	}

	// SLATED FOR DEPRECATION
	// Check 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;
	}
	// END SLATED FOR DEPRECATION
	
	// Run dealer defined plugin functions
	tst_run_STPS();
	
	// Start or step session
	dmb1_old_session_ref = dmb1_session_ref;
	tst_hit();
}

// -------------------------------------------------------------------------------------------------
// HANDLE SESSION PROPAGATION

function tst_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 = tst_add_session_data(el.href);
	el.href = t[1];
}

// -------------------------------------------------------------------------------------------------

function tst_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 = tst_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!
	if (el.action != '') el.submit();
}

// -------------------------------------------------------------------------------------------------

function tst_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;
		if (typeof dmb1_FedSessKey != 'undefined') params.FedSessKey = dmb1_FedSessKey;
		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;
	}
}

// -------------------------------------------------------------------------------------------------
// PRIVATE FUNCTIONS

function tst_run_STPS()
{
	// Run dealer defined plugin functions
	// Note that these are defined as methods of the 'dmb1_STPS' 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]();
		tst_debuginfo.plugins += (tst_debug.plugins || tst_debug.master) && (run != -1) ? 'Running plugin function: ' + p + '()<br>\n' : '';
	}
}

// -------------------------------------------------------------------------------------------------

function tst_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;
		}
		tst_debuginfo.session_request = (tst_debug.session_request || tst_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 target_url;
	var session_script;
	var query_data;
		
	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);
			session_script = tst_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;	
			session_script = tst_register_sess;
		}
	}
	else
	{
		// Register session
		query_data = '?dmb1_DealerID=' + dmb1_dealer_id;	
		session_script = tst_register_sess;
	}
	query_data += '&pd_CurrURL=' + encodeURIComponent(location.href);
	query_data += '&pd_PrevURL=' + encodeURIComponent(document.referrer);
	
	 // Add JSON-encoded additional params in 'jextra' parameter
	var jex = new Object;
	jex.jextra = dmb1_object_to_string(tst_additional_params);
	target_url = dmb1_append_param_to_url(dmb1_root + session_script + query_data, jex);

	// Do hit to session tracking app on dmClub server
	document.writeln('<script language="javascript" type="text/javascript" src="' + target_url + '">');
	document.writeln('<\/scr' + 'ipt>'); // NB ESCAPE THE CLOSING SCRIPT TAG FOR THE HTML PARSER, SO IT DOES NOT TERMINATE THE CURRENT SCRIPT!
	tst_debuginfo.session_request = (tst_debug.session_request || tst_debug.master) ? dmb1_root + session_script + query_data : '' ;
}

// -------------------------------------------------------------------------------------------------

function tst_compile_debuging_info()
{
	// Compile and format debugging info
	
	if (tst_debug.fetch_param || tst_debug.master)
	{
		tst_debug_display += tst_debuginfo.fetch_param;
		tst_debug_display += '<br />\n';
	}

	if (tst_debug.plugins || tst_debug.master)
	{
		tst_debug_display += '<strong>tst_run_STPS()</strong><br />\n';
		tst_debug_display += tst_debuginfo.plugins;
		tst_debug_display += '<br />\n';
	}
	
	if (tst_debug.tst_additional || tst_debug.master)
	{
		tst_debug_display +='<strong>Additional TST params</strong><br />\n';
		tst_debug_display += '<table cellpadding="2" border="1">\n';
		tst_debug_display += dmb1_walk_object(tst_additional_params, 'tst_additional_params');
		tst_debug_display += '</table>\n<br />\n';
	}
	
	if (tst_debug.session_request || tst_debug.master)
	{
		tst_debug_display += '<strong>tst_hit()</strong><br />\n';
		tst_debug_display += dmb1_wrap_lines(tst_debuginfo.session_request, tst_debug_max_linelength, 1);
		tst_debug_display += '<br /><br />\n';
	}

	if (tst_debug.session_ref || tst_debug.master)
	{
		tst_debug_display += '<strong>tst_manage_session()</strong><br />\n';
		tst_debug_display += 'Received Step Session Ref: ' + dmb1_old_session_ref + '<br />\n';
		tst_debug_display += 'New Step Session Ref: ' + dmb1_session_ref + '<br />\n';
		tst_debug_display += '<br />\n';
	}

	if (tst_debug.init || tst_debug.master)
	{
		tst_debug_display += tst_debuginfo.init;
		tst_debug_display += '<br />\n';
	}

}
