<?php

// dmx_library.php
// -------------------------------------------------------------------------------------------------
// Shared XComponents Library Functions: Low level functioms used for
// session propagation, communication with the dmProtocol server,
// form validation, debugging and benchmarking
//
// Revision   > $Rev: 155 $
// Date		  > $Date: 2009-12-02 16:44:54 +0000 (Wed, 02 Dec 2009) $
// Author     > RAK for DML
// Copyright  > (C) Digital Mail Limited (2005-2009)
// -------------------------------------------------------------------------------------------------

// -------------------------------------------------------------------------------------------------
// S E S S I O N   F U N C T I O N S
// -------------------------------------------------------------------------------------------------

function dmx_get_session($mechanism = 'tst')
{
	// Get the session refererence, using the specified session popagation mechanism
	// Returns the session ref, or an empty string on failure
	global $dmshop, $dmx_debug;
	$ref = '';
	$dmx_debug->heading('dmx_get_session(' . $mechanism . ')');
	
	switch(strtolower($mechanism))
	{
		
		// -----------------------------------------------------------------------------------------
		
		case 'tst':
			// We are using TST to propgate the session reference
			if (isset( $_GET['dmb1_SessRef'])) $ref = substr( $_GET['dmb1_SessRef'], 0, 24); // The 'root' part of the TST session ref
			if (isset($_POST['dmb1_SessRef'])) $ref = substr($_POST['dmb1_SessRef'], 0, 24); // POSTS overrule GETS
			
			if (!$ref)
			{
				// Attempt session reactivation from cookie
				if (isset($_COOKIE['dmb1_backup_session_ref']))
				{
					// Reactivation succeeds, so logout
					$ref = substr($_COOKIE['dmb1_backup_session_ref'], 0, 24); // The 'root' part of the TST session ref
					$dmx_debug->msg('<b>SESSION REACTIVATION FROM COOKIE</b> - TrolleyID: ' . $ref);
					
					// SLATED FOR REMOVAL
					/* WE USED TO LOGOUT, ON SESSION REACTIVATION FROM COOKIE
					$dmx_debug->msg('<b>ATTEMPTING TO LOGOUT</b> TrolleyID: ' . $ref);
					$dmshop->_R['TrolleyID'] = $ref;	
					$dmshop->_R['Cmd'] = 'Logout';
					$dmshop->request();
					*/
					// END SLATED FOR REMOVAL
					
					$dmx_debug->msg($dmshop->debug->flush());
				}
			}
			break;
		
		// -----------------------------------------------------------------------------------------

		case 'cookie':
			// We are using cookies to propgate the session reference
			$ref = isset($_COOKIE['dmx_session']) ? $_COOKIE['dmx_session'] : '';
			break;
			
		// -----------------------------------------------------------------------------------------

		case 'php':
			// We are using PHPs built in session handling to propgate the session reference
			$ref = isset($_SESSION['DMX']['dmx_session']) ? $_SESSION['DMX']['dmx_session'] : '';
			break;
			
		// -----------------------------------------------------------------------------------------

		case 'ob_link_rewriting':
			// We are using output buffering and link rewriting
			// (using regular expressions to parse the dealers HTML, before output, to append the session ref)
			// NOT YET IMPLEMENTED
			$ref = isset($_POST['dmx_session']) ? $_POST['dmx_session'] : isset($_GET['dmx_session']) ? isset($_GET['dmx_session']) : '';
			break;

		// -----------------------------------------------------------------------------------------

	}

	$dmx_debug->msg('Session Mechanism: ' . $mechanism);
	$dmx_debug->msg('Session Reference: ' . ($ref ? $ref : 'None Found'));
	$dmx_debug->msg();
	
	return $ref;
}

// -------------------------------------------------------------------------------------------------

function dmx_set_session($dmx_session, $mechanism='tst')
{
	// Set the session refererence so it is available to the rest of the page

	switch(strtolower($mechanism))
	{
		
		// -----------------------------------------------------------------------------------------
		
		case 'tst':
			// We are using TST to propgate the session reference
			echo "\n";
			echo "<script language=\"javascript\" type=\"text/javascript\">\n";
			echo "/*<![CDATA[[*/\n";
			echo "// Seed TST session reference\n";
			echo "var dmb1_session_ref = '$dmx_session';\n";
			echo "/*]]>*/\n";
			echo "</script>\n";
			echo "\n";
			break;
		
		// -----------------------------------------------------------------------------------------

		case 'cookie':
			// We are using cookies to propgate the session reference
			setcookie('dmx_session', $dmx_session, false, '/');
			break;
			
		// -----------------------------------------------------------------------------------------

		case 'php':
			// We are using PHPs built in session handling to propgate the session reference
			$_SESSION['DMX']['dmx_session'] = $dmx_session;
			break;
			
		// -----------------------------------------------------------------------------------------

		case 'ob_link_rewriting':
			// We are using output buffering and link rewriting
			// (using regular expressions to parse the dealers HTML before output,
			// to append the session ref)
			break;

		// -----------------------------------------------------------------------------------------

	}
}

// -------------------------------------------------------------------------------------------------

function dmx_handle_federated_session_key($fskey)
{
	// Try to login, as an incoming Federated Session Key has been detected

	global $dmshop, $dmx_debug, $dmx_response, $dmx_session;
	
	$dmx_debug->heading("dmx_handle_federated_session_key($fskey)");

	$dmshop->_R = array();
	$dmshop->_R['TrolleyID'] = $dmx_session;	
	$dmshop->_R['Cmd'] = 'Login';
	$dmshop->_R['FedSessKey'] = $fskey;
	$dmshop->_R['_Response'] = 'Extended';
	$dmx_debug->msg('<b>ATTEMPTING TO LOGIN USING FEDERATED SESSTION KEY</b> FedSessKey: ' . $fskey);

	$dmshop->request();
	$dmx_debug->msg($dmshop->debug->flush());

	if ($dmshop->result->status == false || !count($dmshop->result->response))
	{
		return(array('result' => 'comserr', 'details' => ''));
	}
	$dmx_response = $dmshop->result->response;
	
	// Translate results of attempt
	if (!isset($dmx_response['Cmd']))
	{
		return(array('result' => 'comserr', 'details' => $dmx_response['Reason']));
	}
	if ($dmx_response['Cmd'] == 'LoginAck')
	{
		return(array('result' => 'login', 'details' => $dmx_response['DMID']));
	}
	else if ($dmx_response['Cmd'] == 'LoginNak')
	{
		$details = 'FedSessKeyLoginFailed';
	}
	return(array('result' => 'loginerror', 'details' => $details));
}

// -------------------------------------------------------------------------------------------------
// I N I T I A L I S E   T R O L L E Y
// -------------------------------------------------------------------------------------------------

function dmx_initialise_trolley($skip_dmshop = FALSE)
{
	$success = dmx_initialise_trolley_do($skip_dmshop);
	if ($success && $_SESSION['DMSHOP']['new_session'])
	{
		global $dmx_session, $dmx_init_response, $dmx_options;

		$dmx_session = $dmx_init_response['TrolleyID'];
		
		// IS THIS REALLY NECESSARY? What about dmx.trolley_id
		// CONSIDER KILLING THIS NEXT BIT
		// Set session for broser javascript, if this is the first time THIS trolley ID has been used
		if (!(isset($_POST['response_type']) && strtolower($_POST['response_type']) == 'json'))
		{
			dmx_set_session($dmx_session, $dmx_options['session_mech']);
		}
	}
	dmx_get_session_status($GLOBALS['dmx_init_response']);
	if (!isset($_POST['ajax']))
	{
		dmx_include_skype_fix();
		dmx_output_javads();
		dmx_include_brand0();
	}
	if ($success) dmx_get_trolley_focus();
	return $success;
}

// -------------------------------------------------------------------------------------------------

function dmx_initialise_trolley_do($skip_dmshop)
{
	// Create or continue a trolley session
	// if $skip_dmshop is not FALSE, don't talk to dmShop (just get $dmx_init_response from the local session)
	// provided that the time since the last dmShop hit is less than the value of $skip_dmshop (in seconds)
	
	global $dmshop, $dmx_debug, $dmx_session, $dmx_options, $dmx_init_response, $dmx_memory, $dmx_memories_to_update;
	
	$dmx_debug->heading("dmx_initialise_trolley($skip_dmshop)");
	
	dmx_handle_abs_overrides();
	
	isset($dmx_options['doctype']) ? dmx_add_doctype($dmx_options['doctype']) : dmx_add_doctype();

	if (!$dmx_session)
	{
		// START A NEW TROLLEY SESSION
		$new_session = TRUE; 
		$dmshop->_R['Cmd'] = 'CreateTrolley';
		if ($dmx_options['seeding'] && (isset($_GET['dmx_did']) || isset($_GET['dmx_name'])))
		{
			// Seeding's allowed and there's a seed
			if (isset($_GET['dmx_did']))  $dmshop->_R['DealerID']  = $_GET['dmx_did'];
			if (isset($_GET['dmx_name'])) $dmshop->_R['BrandName'] = $_GET['dmx_name'];
		}
		else
		{
			// Use dealer id from dmx_options.php
			$dmshop->_R['DealerID'] = $dmx_options['dealer_id'];
		}
	}
	else
	{
		// CONTINUE EXISTING TROLLEY SESSION
		$new_session = FALSE;
		$dmshop->_R['Cmd'] 		 = 'ContinueTrolley';
		$dmshop->_R['TrolleyID'] = $dmx_session; 
		$dmshop->_R['_RspStore'] = 'Y';
		$dmshop->_R['_Response'] = 'Extended'; // We could also ask for an extended response and, as this gives a full trolley listing, use this data for all Trolley instances
	}
	if (!empty($dmx_memories_to_update))
	{
		$dmx_memories_to_update = array_unique($dmx_memories_to_update);
		foreach($dmx_memories_to_update as $mem_name)
		{
			// $dmx_memory is updated in dmx_run.php when NCP are encountered, and/or by the dmx_update_nc_memory function
			$dmshop->_R['_Store_' . $mem_name] = serialize( $dmx_memory[strtoupper($mem_name)] );
		}
	}
	
	$ts = isset($_SESSION['DMSHOP']['timestamp']) ? $_SESSION['DMSHOP']['timestamp'] : 0;
	if (!$skip_dmshop || ($skip_dmshop > 0 && gmmktime() - $ts > $skip_dmshop) || $new_session)
	{
		if ($skip_dmshop)
		{
			 // You were going to skip dmShop, but more than $skip_dmshop seconds have passed since the last real dmShop hit
			$dmx_debug->msg('Skip dmShop set, but too much time elapsed since last real dmShop request');
			$dmshop->_R['TimeoutIgnore'] = 'Y';
		}
		$dmshop->request();
		$dmx_debug->msg($dmshop->debug->flush());
		if ($dmshop->result->status == false || !count($dmshop->result->response))
		{
			$GLOBALS['dmx_resultcode']['result']  = 'comserr';
			$GLOBALS['dmx_resultcode']['details'] = 'The shopping server is unavilable';
			return false;
		}
		$dmx_response = $dmshop->result->response;

		if (isset($dmx_response['Reason']) && $dmx_response['Cmd'] == 'ContinueTrolleyNak')
		{
			// We tried ContinueTrolley on a non-existant or timed-out trolley
			// so create a new trolley - this should only happen once!
			$new_session = TRUE; 
			$dmshop->_R['Cmd'] = 'CreateTrolley';
			$dmshop->_R['DealerID'] = $dmx_options['dealer_id'];
			$dmshop->request();
			$dmx_debug->msg($dmshop->debug->flush());
			$dmx_response = $dmshop->result->response;
		}
		$_SESSION['DMSHOP']['timestamp'] = gmmktime(); // Timetsamp real ContineTrolley or CreateTrolley
		$_SESSION['DMSHOP']['source'] = 'dmShop';
	}
	else
	{
		// Get the response from the session to avoid load on dmShop
		$dmx_debug->msg("Loading result from dmABS local session - not going via dmShop");
		$dmx_response = $_SESSION['dmx_init_response'];
		$_SESSION['DMSHOP']['source'] = 'DMXSession';
	}
	$_SESSION['DMSHOP']['new_session'] = $new_session;

	// Store the initial response
	$dmx_init_response = $_SESSION['dmx_init_response'] = $dmx_response;
	
	// Was Trolley initialisation successful?
	if (substr($dmx_response['Cmd'], -10) == 'TrolleyAck')
	{
		// CreateTrollyAck or ContinueTrolleyAck
		$dmx_debug->msg("<b>TROLLEY INITIALISATION SUCCESS</b>\n");
		$dmx_session = $dmx_response['TrolleyID'];
		$dmx_options['dealer_id'] = $dmx_response['DealerID']; // dmProtocol server can override the dealer ID supplied in dmx_options
	}
	else
	{
		$dmx_debug->msg('<b>TROLLEY INITIALISATION FAILURE:</b> ' . $dmx_response['Reason'] . "\n");
		$dmx_response['Reason'] = urldecode($dmx_response['Reason']); // Hack to tackle double-URL encoding problem
		switch($dmx_response['Reason'])
		{
			case 'Illegal DealerID':
				$details = 'BadDealerID';
				break;
			case 'TrolleyID Already taken':
				$details = 'TrolleyIDTaken';
				break;
			default:
				$details = $dmx_response['Reason'];
		}
		$GLOBALS['dmx_resultcode'] = array('result' => 'inittrolleyfail', 'details' => $details);
		return false;
	}
	
	// SCAN FOR INCOMING FEDERATED SESSION KEYS (POSTS overrule GETS)
	$incoming_fskey = NULL;
	if (array_key_exists('FedSessKey', $_GET))  $incoming_fskey =  $_GET['FedSessKey'];
	if (array_key_exists('FedSessKey', $_POST)) $incoming_fskey = $_POST['FedSessKey'];
	if ($incoming_fskey)
	{
		$_SERVER['FedSessKey'] = $incoming_fskey;
		$response = dmx_handle_federated_session_key($incoming_fskey);
		if ($response['result'] == 'login')
		{
			// Get new common request nodes, reflecting updated login status
			$dmx_response = $dmx_init_response = $_SESSION['dmx_init_response'] = $dmshop->result->response;
		}
	}

	$dmx_init_response['EXTENDED'] = TRUE; // We asked for an extended response - CHECK THIS IS NEEDED!!!

	// Retrieve and deseialize any stored variables 
	while ( list($key, $value) = each($dmx_response) )
	{
		if (substr($key, 0, 7) == '_STORE_')
		{
			$mem_name = substr($key, 7);

			// HACK: Overcome weakness in dmShop protocol
			// (ie. that stores are not available immediately, in response when they are created in request)
			// Only retrieve memories that haven't just been updated
			if (in_array( $mem_name, $dmx_memories_to_update)) continue;

			$dmx_memory[$mem_name] = unserialize($value);
		}
	}

	$_SESSION['dmx_memory'] = $dmx_memory;
	$dmx_debug->divide();
	$dmx_debug->inspect('dmx_memory', $dmx_memory);

	if (!$dmx_options['abs'])
	{
		$GLOBALS['dmx_glos'] = new Substitutions();
		$GLOBALS['dmx_glos']->append_from_file(DOCROOT . '/dmx/core/php/dmx_glossary.php');

		$search = array ('{GLIB}',
						 '{ROOT}'
						 );
		
		$replace = array ($dmx_options['glib'],
						  ''
						  );
	
		$GLOBALS['dmx_glos']->append($search, $replace);
	}
	return true;
}

// -------------------------------------------------------------------------------------------------

function dmx_get_session_status($init_response)
{
	// Is the session:
	// Authenticated (Known + Privileged)
	// Identified	 (Known + Unprivileged) or
	// Anonymous
	
	if (isset($init_response['DMID']) && $init_response['DMID'])
	{
		if (isset($init_response['Authenticated']) && $init_response['Authenticated'] == 'Y')
		{
			$status = 'A'; // Authenticated
		}
		else
		{
			$status = 'I'; // Identified, but not Authenticated
		}
	}
	else
	{
		$status = ''; // Anonymous
	}
	$GLOBALS['dmx_session_status'] = $status;
}

// -------------------------------------------------------------------------------------------------

function dmx_get_trolley_focus()
{
	// DETERMINE TROLLEY FOCUS, AND CURRENT NUMBER ($cn), SERVICE ($cs) AND PLAN ($cp)
	global $dmx_init_response;
	
	$num_items = isset($dmx_init_response['QntItems']) ? $dmx_init_response['QntItems'] : 0;
	
	// Now get number in trolley with focus
	if (isset($_GET['dmx_index']) && $_GET['dmx_index'] <= $num_items)
	{
		// The index of the item in the trolley is plausible, so use it
		$focus = $_GET['dmx_index'];
	}
	else if ($num_items > 0)
	{
		// Default to the last item in the trolley
		$focus = $dmx_init_response['QntItems'];
	}
	else
	{
		// There's nothing in the trolley
		$focus = 0;
	}
	
	// Current service and plan of number with focus
	$cn 			= $focus && $dmx_init_response['EXTENDED'] ? $dmx_init_response['Item'][$focus]['FmtNum'] :  '';
	list($cs, $cp) 	= $focus && $dmx_init_response['EXTENDED'] ? explode(':', $dmx_init_response['Item'][$focus]['SvcPlan']) : array('', '');
	
	// Globalise number with foucs, if possible
	if ($focus)
	{
		$GLOBALS['dmx_focus']['number']  = $cn;
		$GLOBALS['dmx_focus']['service'] = $cs;
		$GLOBALS['dmx_focus']['plan'] 	 = $cp;
	}
}

// -------------------------------------------------------------------------------------------------
// D O C T Y P E   H E A D E R S
// -------------------------------------------------------------------------------------------------

function dmx_add_doctype($type = '')
{
	// Scan output buffer and add DOCTYPE if not already present at top of document, and request is NOT via ajax
	if (isset($_POST['ajax'])) return;
	$html = ob_get_clean();
	if (!preg_match('/.*<!DOCTYPE/im', $html))
	{
		$html = dmx_return_doctype($type) . "\n" . $html;
	}
	if ($GLOBALS['dmx_options']['abs']) $html = $GLOBALS['abs_glos']->substitute($html); // COPES WITH RUNNING IN dmABS
	echo $html;
}

// -------------------------------------------------------------------------------------------------
// d m A B S   O V E R R I D E S
// -------------------------------------------------------------------------------------------------

function dmx_handle_abs_overrides()
{
	if (!$GLOBALS['dmx_options']['abs']) return;
	
	// dmABS installations can override certain XComponent globals, namely:
	// (1) the dealer ID
	// (2) the location of the service graphics library 
	// (3) the location of the stylesheets used by XComponents
	// (4) the Google Analytics account number
	// (5) the full trolley's URL (used as a landing page bu AutoShop plugin)
	
	$GLOBALS['dmx_options']['dealer_id']      	= $GLOBALS['abs_opt']['dealer_id']; 											// (1)
	$GLOBALS['dmx_options']['glib']           	= $GLOBALS['abs_root'] . '/themes/' . $GLOBALS['abs_opt']['theme'] . '/sgm/'; 	// (2)
	$GLOBALS['dmx_options']['css']            	= $GLOBALS['abs_root'] . '/themes/' . $GLOBALS['abs_opt']['theme'] . '/css/'; 	// (3)
	$GLOBALS['dmx_options']['ganal']['accno']	= $GLOBALS['abs_opt']['ganal_accno']; 											// (4)
	$GLOBALS['dmx_options']['auto_trolley_url'] = $GLOBALS['abs_opt']['auto_trolley_url']; 										// (5)
}

// -------------------------------------------------------------------------------------------------
// I N C L U D I N G   B 0   &   T S T
// -------------------------------------------------------------------------------------------------

function dmx_include_brand0()
{
	global $dmx_options;
	$path = $dmx_options['css'];
	$render = ($dmx_options['doctype'] == 'XHTML 1.0 Strict' || $dmx_options['doctype'] == 'XHTML 1.1') ? 'xhtml/' :  'html/';
	include(DOCROOT . $GLOBALS['dmx_options']['templates'] . $render . 'header/b0_css_js.inc');
}

// -------------------------------------------------------------------------------------------------

function dmx_include_tst()
{
	global $dmx_options;
	$render = ($dmx_options['doctype'] == 'XHTML 1.0 Strict' || $dmx_options['doctype'] == 'XHTML 1.1') ? 'xhtml/' :  'html/';
	include(DOCROOT . $GLOBALS['dmx_options']['templates'] . $render . 'header/tst_js.inc');
}

// -------------------------------------------------------------------------------------------------

function dmx_include_skype_fix()
{
	global $dmx_options;
	$render = ($dmx_options['doctype'] == 'XHTML 1.0 Strict' || $dmx_options['doctype'] == 'XHTML 1.1') ? 'xhtml/' :  'html/';
	include(DOCROOT . $GLOBALS['dmx_options']['templates'] . $render . 'header/skype_fix.inc');
}

// -------------------------------------------------------------------------------------------------
// I N C L U D I N G   G O O G L E   A N A L Y T I C S
// -------------------------------------------------------------------------------------------------

function dmx_include_google_analytics()
{
	// Include the Google Analytics tracking javascript, if appropriate
	if ($GLOBALS['dmx_options']['ganal']['accno'] == 'OFF' || dmx_check_for_tester_ip()) return;

	// Prepare variables substituted in template, then call template
	$ganal_accno = $GLOBALS['dmx_options']['ganal']['accno'];
	if (isset($_GET['tsts']) && $_GET['tsts'] != 'gaw')
	{
		// For non-AdWords tagged URLs, map TST's tagging to Googles
		$tsts = $_GET['tsts'];
		$tstc = isset($_GET['tstc']) ? $_GET['tstc'] : '';
		$tstm = isset($_GET['tstm']) ? $_GET['tstm'] : '';
		$tstt = isset($_GET['tstt']) ? $_GET['tstt'] : '';
		$tsta = isset($_GET['tsta']) ? $_GET['tsta'] : '';
		include(DOCROOT . '/dmx/core/ganal/set_campaign_vars.tpl');
	}
	else
	{
		// Tagged AdWords URLs and all others
		include(DOCROOT . '/dmx/core/ganal/tracking.tpl');
	}
}

// -------------------------------------------------------------------------------------------------

function dmx_check_for_tester_ip()
{
	global $dmx_options, $dmx_debug_settings;
	if (isset($dmx_debug_settings->s_goo_my) && $dmx_debug_settings->s_goo_my == 1) return TRUE;
	if (isset($dmx_debug_settings->s_goo_ip) && $dmx_debug_settings->s_goo_ip == 0) return FALSE;

	// Check for 'tester' IP addresses, and suppress if Google Analytics if matched
	if (isset($dmx_options['ganal']['tester_ip']) && is_array($dmx_options['ganal']['tester_ip']))
	{
		foreach($dmx_options['ganal']['tester_ip'] as $ip_regex)
		{
			if (ereg('^' . $ip_regex . '$', $_SERVER['REMOTE_ADDR'])) return TRUE;
		}
	}
	return FALSE;
}

// -------------------------------------------------------------------------------------------------
// S H A R E   D A T A   W I T H   J A V A S C R I P T
// -------------------------------------------------------------------------------------------------

function dmx_output_javads()
{
	// Expose results of any actions to javascript 
	// Output JaVaDs for $dmx_session and $dmx_resultcode
	// And seed the dealer ID from dmb1_options.php, for use by TST
	global $dmx_session, $dmx_resultcode, $dmx_options, $dmx_init_response;
	
	$xhtml = substr($dmx_options['doctype'], 0, 5) == 'XHTML' ? 1 : 0;
	$json = new Services_JSON(); // Create a new instance of Services_JSON

	echo "\n";
	echo $xhtml ? "<script type=\"text/javascript\">\n" : "<script language=\"javascript\" type=\"text/javascript\">\n";
	echo $xhtml ? "/*<![CDATA[[*/\n" : "<!--\n";
	echo "// ( GENERATED BY dmx_initialise_trolley() - PHP )\n";
	echo "//\n";
	if (!$dmx_options['debug_level']) echo "window.onerror = function() { return true; }\n";
	echo "try {\n";
	echo "\t// 'Public' JS object for interfacing with XComponents\n";
	echo "\tvar dmx = new Object();\n";
	echo "\t// Trolley session\n";
	echo "\tdmx.trolley_id = '$dmx_session';\n";
	echo "\t// Customers session status\n";
	echo "\tdmx.session_status = '" . $GLOBALS['dmx_session_status'] . "';\n";
	if (isset($dmx_resultcode) && is_array($dmx_resultcode))
	{
		echo "\t// Resultcode generated by user-interaction with XComponent\n";
		echo "\tdmx.resultcode = " . $json->encode($dmx_resultcode) . ";\n";
	}
	echo "\t// The initial respose from CreateTrolley or ContinueTrolley. Contains lots of status data\n";
	echo "\tdmx.init_response = " . $json->encode($dmx_init_response) . ";\n";
	echo "\t// Seed DMID for use by TST (overrides DMID set in dmb1_options.js)\n";
	echo "\tvar dmb1_dealer_id_seed = {$dmx_options['dealer_id']};\n";
	if (isset($_SERVER['FedSessKey']))
	{
		echo "\t// Federated Session Key\n";
		echo "\tdmb1_FedSessKey = '{$_SERVER['FedSessKey']}';\n";
	}
	if (isset($dmx_options['use_infotext']))
	{
		echo "\t// Tell B0 whether to use infotext (overrides b0_use_infotext in b0_interface.js)\n";
		echo "\tvar b0_use_infotext = {$dmx_options['use_infotext']};\n";
	}
	echo "}\n";
	echo "catch(error) {;}\n";
	if (!$dmx_options['error_rep'])
	{
		echo "window.onerror = null;\n";
	}
	echo $xhtml ? "/*]]>*/\n" : "//-->\n";
	echo "</script>\n";
	echo "\n";
}

// -------------------------------------------------------------------------------------------------
// I N I T I A L I S E   P A N E L   F U N C T I O N A L I T Y
// -------------------------------------------------------------------------------------------------

function dmx_init_panel()
{
	// Include panel class, and initialise it
	global $dmx_options;
	require_once('class/panel.class.php');
	$panel = new Panel(
		DOCROOT . $dmx_options['panel']['root'],
		$dmx_options['panel']['version']
	);
	$panel->pan_globals = array('dmx_glib'); // Place these variables in scope of panel renderer
	return $panel; // Make the panel object available to template
}

// -------------------------------------------------------------------------------------------------
// N U M B E R C H O O S E R   M E M O R Y
// -------------------------------------------------------------------------------------------------

function dmx_expose_nc_state($tariff = NULL, $comp_name = NULL)
{
	// For 'host' apps to mine information from NumberChooser memory
	// Used to predict the results of a NumberChooser render before it is rendered
	// so a page can know what type of numbers it shows before executing the NumberChooser code
	
	// Queries the memory for a component, modifies the tariff,
	// translates the modified tariff (NOT the memory)
	// then super-imposes defaults (as defined in dmx_options.php)
	// over any NULL values
	
	global $dmx_state_default;
	
	if (!$comp_name && isset($_SESSION['NC_STATE'])) $comp_name = $_SESSION['NC_STATE']['last_comp_name'];
	$_SESSION['NC_STATE']['last_comp_name'] = $comp_name;

	$state 		= dmx_query_nc_memory($comp_name);
	$tariff 	= dmx_modify_tariff($tariff, $state->root, $state->range, $state->service, $state->plan);
	$modified 	= dmx_translate_tariff($tariff);
	$state 		= (object) array_merge((array) $state, array_filter((array) $modified));
	if (!$state->prefix) $state->prefix = dmx_get_default_prefix($tariff);
	$state 		= (object) array_merge((array) $dmx_state_default, array_filter((array) $state));
	return $state;
}

// -------------------------------------------------------------------------------------------------

function dmx_query_nc_memory($comp_name = NULL)
{
	// Mines information from NumberChooser memory
	// If $comp_name is supplied, the individual components memory can trump the global memory
	
	global $dmx_memory;
	
	$t = isset($dmx_memory['NCGLOBAL']) ? $dmx_memory['NCGLOBAL'] : array();
	if ($comp_name && isset($dmx_memory[strtoupper($comp_name)]) && is_array($dmx_memory[strtoupper($comp_name)]))
	{
		$t = array_merge($t, array_filter($dmx_memory[strtoupper($comp_name)])); 
	}	
	
	$state = new stdClass;
	$state->root 		= isset($t['root']) 	? $t['root'] 		: NULL; 
	$state->range 		= isset($t['range']) 	? $t['range'] 		: NULL; 
	$state->service		= isset($t['service']) 	? $t['service'] 	: NULL; 
	$state->plan 		= isset($t['plan']) 	? $t['plan'] 		: NULL; 
	$state->gold 		= isset($t['gold']) 	? $t['gold'] 		: NULL; 
	$state->areacode 	= isset($t['areacode']) ? $t['areacode'] 	: NULL; 
	$state->areaname 	= isset($t['areaname']) ? $t['areaname'] 	: NULL; 
	$state->prefix	 	= isset($t['prefix']) 	? $t['prefix']	 	: NULL; 
	
	if ($state->range && $state->range != 'ukgeo')
	{
		$state->areacode = NULL; 
		$state->areaname = NULL; 
	}
	
	return $state;
}

// -------------------------------------------------------------------------------------------------

function dmx_update_nc_memory($comp_name = NULL, $new_state_arr)
{
	// For 'host' apps to update NumberChooser memory
	// If $comp_name is supplied, just that individual NumberChoosers memory is updated
	// Otherwise all NumberChooser memories are updated
	
	// $new_state_arra is an associate array e.g. array('service' => 'fax', 'gold' => '0-200')
	
	global $dmx_memory, $dmx_memories_to_update, $dmx_state_default;
	
	$new_state_arr = dmx_validate_and_augment_state($new_state_arr);
	if ($comp_name)
	{
		$comp_name = strtoupper($comp_name);
		if (!isset($dmx_memory[$comp_name])) $dmx_memory[$comp_name] = (array) $dmx_state_default;
		$dmx_memory[$comp_name] = array_merge($dmx_memory[$comp_name], $new_state_arr);
	}
	else
	{
		array_push($dmx_memories_to_update, 'NCGLOBAL');
		if (!isset($dmx_memory['NCGLOBAL']) || !is_array($dmx_memory['NCGLOBAL'])) $dmx_memory['NCGLOBAL'] = array();
		$dmx_memory['NCGLOBAL'] = array_merge($dmx_memory['NCGLOBAL'], $new_state_arr);
		foreach($new_state_arr as $key => $value)
		{
			if ($key == 'class') continue;
			foreach($dmx_memory as $mem_name => $mem)
			{
				if (!isset($mem['class']) || $mem['class'] != 'NumberChooser') continue;
				array_push($dmx_memories_to_update, $mem_name);
				$dmx_memory[$mem_name] = array_merge($mem, array($key => NULL));
			}
		}
	}
}

// -------------------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------------------

function dmx_validate_and_augment_state($state_arr)
{
	global $dmx_debug;
	$validation_fns = array('root' ,'range', 'service', 'plan', 'gold', 'areacode');
	/*
	print_r($state_arr);
	echo('<hr>');
	echo('<pre>' . print_r(debug_backtrace(), 1) . '</pre>');
	echo('<hr>');
	*/
	foreach($state_arr as $key => $val)
	{
		if (!in_array($key, $validation_fns)) 	continue;
		if ($val === NULL) 						continue;
		$fn = 'dmx_validate_' . $key;
		$state_arr = array_merge($state_arr, $fn($val));
	}
	return $state_arr;
}

// -------------------------------------------------------------------------------------------------

function dmx_validate_root($root)
{
	global $dmx_debug;
	$dmx_debug->msg('<b>VALIDATING ROOT: ' . $root . '</b>');
	return array('root' => $root);
}

// -------------------------------------------------------------------------------------------------

function dmx_validate_range($range)
{
	global $dmx_debug;
	$dmx_debug->msg('<b>VALIDATING RANGE: ' . $range . '</b>');
	$range 		= strtolower($range);
	$valid 		= array('0800', 	'0844', 	'0845', 	'0870', 	'0871', 	'ukgeo', 	'07d', 	'07k', 	'07pn2');
	$pre_arr 	= array('!44800', 	'!44844', 	'!44845', 	'!44870', 	'!44871', 	'!44', 		'!447',	'!447',	'!447');
	$result 	= array_search($range, $valid);
	if ($result === FALSE)
	{
		$dmx_debug->msg('UNKNOWN RANGE MODIFIER: ' . $range);
		return array('range' => NULL, 'prefix' => NULL);
	}
	$prefix	 = $pre_arr[$result];
	return array('range' => $range, 'prefix' => $prefix);
}

// -------------------------------------------------------------------------------------------------

function dmx_validate_service($service)
{
	global $dmx_debug;
	$dmx_debug->msg('<b>VALIDATING SERVICE: ' . $service . '</b>');
	$service = strtolower($service);
	$valid = array('connect', 'voice', 'fax', 'switchboard');
	$result = in_array($service, $valid) ? $service : NULL;
	if (!$result) $dmx_debug->msg('UNKNOWN SERVICE MODIFIER: ' . $service);
	return array('service' => $result);
}

// -------------------------------------------------------------------------------------------------

function dmx_validate_plan($plan)
{
	global $dmx_debug;
	$dmx_debug->msg('<b>VALIDATING PLAN: ' . $plan . '</b>');
	$plan = strtolower($plan);
	$valid = array('bas', 'pro', 'ent');
	$result = in_array($plan, $valid) ? $plan : NULL;
	if (!$result) $dmx_debug->msg('UNKNOWN PLAN MODIFIER: ' . $plan);
	return array('plan' => $result);
}

// -------------------------------------------------------------------------------------------------

function dmx_validate_gold($gold)
{
	global $dmx_debug;
	$dmx_debug->msg('<b>VALIDATING GOLD: ' . $gold . '</b>');
	$ogold = NULL; // Ordered Gold
	$gbits = explode('-', $gold);
	if ( count($gbits) != 2 || !is_numeric($gbits[0]) || !is_numeric($gbits[1]) )
	{
		$dmx_debug->msg('INVALID GOLD RANGE: ' . $gold);
	}
	else
	{
		$ogold = min($gbits) . '-' . max($gbits);
		$dmx_debug->msg('Gold range is: ' . $ogold);
	}
	return array('gold' => $ogold);
}

// -------------------------------------------------------------------------------------------------

function dmx_validate_areacode($areacode)
{
	// Try to find a match for$areacode, and return the areacode and areaname
	// or NULL and NULL if not matched

	global $dmx_debug;
	$dmx_debug->msg('<b>ATTEMPTING TO FIND AREACODE MATCH: ' . $areacode . '</b>');
	$areaname = NULL;
	$allcodes = file_get_contents(DOCROOT . '/dmx/templates/options/numberchooser_areacodes.csv');
	preg_match('/^' . $areacode . ',([^,\n\r]+)(?(?=,),([^\n\r]+))$/m', $allcodes, $matches);
	if (isset($matches[1]))
	{
		$areaname = $matches[1];
		if (isset($matches[2])) $areaname = $matches[2]; // Optionally contains short version
		$dmx_debug->msg('Areacode matched: ' . $areaname);
	}
	else
	{
		$areacode = NULL;
		$dmx_debug->msg('No match for areacode found - AREACODE IGNORED');
	}
	return array('areacode' => $areacode, 'areaname' => $areaname);
}

// -------------------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------------------

function dmx_modify_tariff($tariff, $new_root = '', $new_range = '', $new_service = '', $new_plan = '', $finding_defaults = FALSE)
{
	global  $dmx_state_default, $dmx_debug;
	
	if (!$finding_defaults)
	{
		$dmx_debug->msg("<b>ATTEMPTING TO MODIFY TARIFF: $new_root $new_range $new_service $new_plan</b>");
	}
	else
	{
		$dmx_debug->msg("Finding default tariff: $new_root $new_range $new_service $new_plan</b>");
	}

	if (!$finding_defaults)
	{
		// Find defaults form $dmx_default_state (as defined in dmx_options.php)
		$def = $dmx_state_default;
		$default_tariff = dmx_modify_tariff('*', $def->root, $def->range, $def->service, $def->plan, TRUE);
		$dbits = array_pad(preg_split('/[.-]/', $default_tariff), 6, '*');
	}
	else
	{
		$dbits = array_fill(0, 6, '*');
	}

	// Now modify tariff
	$original = $tariff;
	$tbits = array_pad(preg_split('/[.-]/', $tariff), 6, '*');
	$tbits[0] = $new_root ? $new_root : $dbits[0];
	if ($tbits[0] == '*') $tbits[0] = 'Public'; // Fallback
	if ($new_range && (empty($tariff) || $tariff == '*'))
	{
		switch (strtolower($new_range))
		{
			case '0800':
				$tbits[2] = 'UK';
				$tbits[3] = 'NGN';
				$tbits[4] = 'Free';
				break;
			case '0844':
				$tbits[2] = 'UK';
				$tbits[3] = 'NGN';
				$tbits[4] = 'G6';
				break;
			case '0845':
				$tbits[2] = 'UK';
				$tbits[3] = 'NGN';
				$tbits[4] = 'Loc';
				break;
			case '0870':
				$tbits[2] = 'UK';
				$tbits[3] = 'NGN';
				$tbits[4] = 'Nat';
				break;
			case '0871':
				$tbits[2] = 'UK';
				$tbits[3] = 'NGN';
				$tbits[4] = 'G7';
				break;
			case 'ukgeo':
				$tbits[2] = 'UK';
				$tbits[3] = 'GEO';
				$tbits[4] = 'All';
				break;
			case '07d':
				$tbits[2] = 'UK';
				$tbits[3] = 'Per';
				$tbits[4] = 'D';
				break;
			case '07k':
				$tbits[2] = 'UK';
				$tbits[3] = 'Per';
				$tbits[4] = 'K';
				break;
			case '07pn2':
				$tbits[2] = 'UK';
				$tbits[3] = 'Per';
				$tbits[4] = 'PN2';
				break;
			default:
				$dmx_debug->msg('UNKNOWN RANGE MODIFIER: ' . $new_range);
		}
	}
	else
	{
		// Enforce default range (as specified in dmx_options by $dmx_state_default) if range not set
		if ($tbits[2] . $tbits[3] . $tbits[4] == '***')
		{
			$tbits[2] = $dbits[2];
			$tbits[3] = $dbits[3];
			$tbits[4] = $dbits[4];
			
			// Fallback to ukgeo, if this still fails (could be bad data in dmx_options)
			if ($tbits[2] . $tbits[3] . $tbits[4] == '***')
			{
				$dmx_debug->msg('Falling back to UK-GEO-ALL, as tange could not be found from $dmx_state_default');
				$tbits[2] = 'UK';
				$tbits[3] = 'GEO';
				$tbits[4] = 'All';
			}
		}
	}
	if ($new_service)
	{
		switch (strtolower($new_service))
		{
			case 'connect':
				$tbits[1] = 'EC';
				break;
			case 'voice':
				$tbits[1] = 'EXV';
				break;
			case 'fax':
				$tbits[1] = 'EXF';
				break;
			case 'switchboard':
				$tbits[1] = 'EXS';
				break;
			case '':
				break;
			default:
				$dmx_debug->msg('UNKNOWN SERVICE MODIFIER: ' . $new_service);
		}
	}
	else
	{
		// Enforce default service, dmConnect, if service not set
		if ($tbits[1] == '*') $tbits[1] = $dbits[1];
		if ($tbits[1] == '*') $tbits[1] = 'EC'; // Fallback
	}
	if (strtolower($tbits[3]) == 'per' && strtolower($tbits[1]) == 'ec') $tbits[1] = 'EXV'; // dmConnect not available on personal numbers
	if ($new_plan)
	{
		switch (strtolower($new_plan))
		{
			case 'bas':
				$tbits[5] = 'Bas';
				break;
			case 'pro':
				$tbits[5] = 'Pro';
				break;
			case 'ent':
				$tbits[5] = 'Ent';
				break;
			case '':
				break;
			default:
				$dmx_debug->msg('UNKNOWN PLAN MODIFIER: ' . $new_plan);
		}
	}
	else
	{
		// Enforce default plan, Bas, if plan not set
		if ($tbits[5] == '*') $tbits[5] = $dbits[5];
		if ($tbits[5] == '*') $tbits[5] = 'Bas'; // Fallback
	}
	$tariff = $tbits[0] . '.' .	implode('-', array_slice($tbits, 1));
	if ($tariff != $original)
	{
		$dmx_debug->msg('Tariff modified: From ' . $original . ' to ' . $tariff);
	}
	else
	{
		$dmx_debug->msg('Tariff is still: ' . $original);
	}
	$dmx_debug->msg();
	return $tariff;
}

// -------------------------------------------------------------------------------------------------

function dmx_translate_tariff($tariff)
{
	$tbits = array_pad(preg_split('/[.-]/', $tariff), 6, NULL);
	$root = $tbits[0];
	switch (strtoupper($tbits[2] . '-' . $tbits[3] . '-' . $tbits[4]))
	{
		case 'UK-NGN-FREE':
			$range 	= '0800';
			break;
		case 'UK-NGN-G6':
			$range 	= '0844';
			break;
		case 'UK-NGN-LOC':
			$range 	= '0845';
			break;
		case 'UK-NGN-NAT':
			$range 	= '0870';
			break;
		case 'UK-NGN-G7':
			$range 	= '0871';
			break;
		case 'UK-GEO-ALL':
			$range 	= 'ukgeo';
			break;
		case 'UK-PER-D':
			$range 	= '07d';
			break;
		case 'UK-PER-K':
			$range 	= '07k';
			break;
		case 'UK-PER-PN2':
			$range 	= '07pn2';
			break;
		default:
			$range 	= NULL;
			break;
	}
	switch (strtoupper($tbits[1]))
	{
		case 'EC':
			$service = 'connect';
			break;
		case 'EXV':
			$service = 'voice';
			break;
		case 'EXF':
			$service = 'fax';
			break;
		case 'EXS':
			$service = 'switchboard';
			break;
		default:
			$service = NULL;
			break;
	}
	switch (strtoupper($tbits[5]))
	{
		case 'BAS':
			$plan = 'bas';
			break;
		case 'PRO':
			$plan = 'pro';
			break;
		case 'ENT':
			$plan = 'ent';
			break;
		default:
			$plan = NULL;
			break;
	}
	$state = new stdClass;
	$state->root 		= $root; 
	$state->range 		= $range; 
	$state->service		= $service; 
	$state->plan 		= $plan; 
	return $state;
}

// -------------------------------------------------------------------------------------------------

function dmx_get_default_prefix($tariff)
{
	$tbits = array_pad(preg_split('/[.-]/', $tariff), 6, NULL);
	switch (strtoupper($tbits[2] . '-' . $tbits[3] . '-' . $tbits[4]))
	{
		case 'UK-NGN-FREE':
			$prefix = '!44800';
			break;
		case 'UK-NGN-G6':
			$prefix = '!44844';
			break;
		case 'UK-NGN-LOC':
			$prefix = '!44845';
			break;
		case 'UK-NGN-NAT':
			$prefix = '!44870';
			break;
		case 'UK-NGN-G7':
			$prefix = '!44871';
			break;
		case 'UK-GEO-ALL':
			$prefix = '!44';
			break;
		case 'UK-PER-D':
			$prefix = '!447';
			break;
		case 'UK-PER-K':
			$prefix = '!447';
			break;
		case 'UK-PER-PN2':
			$prefix = '!447';
			break;
		default:
			$prefix = NULL;
			break;
	}
	return $prefix;
}

// -------------------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------------------

function dmx_generate_filter($prefix = '', $or_array = array())
{
	$filter = '';
	if ($prefix)
	{
		if (substr($prefix, 0, 1) != '!' ) $prefix = '!' . $prefix;
		if (substr($prefix, -1)   != '*' ) $prefix .= '*';
		if (substr($prefix, 0, 2) == '!0') $prefix = '!44' . substr($prefix, 2);
	}
	
	for($i = 0; $i < count($or_array); $i++)
	{
		if (!preg_match('/[!*]/', $or_array[$i]))
		{
			$or_array[$i] = '*' . $or_array[$i] . '*'; // If the pattern doesn't contain wildcards (* or !) surround with *
		}
		else
		{
			$f = substr($or_array[$i], 0, 1);
			$l = substr($or_array[$i], -1);
			$sw = ($f == '!' || $f == '*'); // Starts with * or !
			$ew = ($l == '!' || $l == '*'); // Ends   with * or !
			if ($sw && !$ew)
			{
				$or_array[$i] = $or_array[$i] . '*';
			}
			else if (!$sw && $ew)
			{
				$or_array[$i] = '*' . $or_array[$i];
			}
		}
	}
	$or_list = trim(implode(' OR ', $or_array));

	if ($prefix && $or_list)
	{
		$filter = $prefix . ' AND ( ' . $or_list . ' )';
	}
	else if ($prefix || $or_list)
	{
		$filter = $prefix . $or_list; // One of these is empty, so just concatenate;
	}
	else
	{
		$filter = '*'; // <!--- DO WE REALLY NEED THIS?
	}
	return $filter;
}

// -------------------------------------------------------------------------------------------------
// R E N D E R   H E L P E R S
// -------------------------------------------------------------------------------------------------

function dmx_format_sgm_number($number)
{
	// Format number for display inside service graphic
	$sg_num = array_pad(explode('-', $number), 4, '');
	if ($sg_num[3] != '')
	{
		$r  = $sg_num[0] . '-' . $sg_num[1] . '<br />' . $sg_num[2] . '-' . $sg_num[3];
	}
	else
	{
		$r =  '+44-' . substr($sg_num[0], 1) . '<br />' . $sg_num[1] . '-' . $sg_num[2];
	}
	return $r;
}

// -------------------------------------------------------------------------------------------------

function dmx_get_number_icon($service)
{
	// Translate service to corresponding SG icon
	$service = strtolower($service);
	if (substr($service, 0, 2) == 'dm') $service = substr($service, 2);
	switch ($service)
	{
		case 'connect':
		case 'connect_pnxlite1':
			$r = 'dmConnect';
			break;
		case 'voice':
		case 'voice_pnxlite1':
			$r = 'dmVoice';
			break;
		case 'fax':
		case 'fax_pnxlite1':
			$r = 'dmFax';
			break;
		case 'switchboard':
		case 'switchboard_pnxlite1':
			$r = 'dmSwitchboard';
			break;
		case 'usagepp':
			$r = 'NEED_NEW_ICON'; // NO ICON FOR USAGE PREPAYMENT!
			break;
		default:
			$r = 'generic';
	}
	return $r;
}

// -------------------------------------------------------------------------------------------------

function dmx_get_plan_icon($plan)
{
	// Translate plan to corresponding SG icon
	switch (strtolower($plan))
	{
		case 'bas':
			$r = 'plan_bas';
			break;
		case 'pro':
			$r = 'plan_pro';
			break;
		case 'ent':
			$r = 'plan_ent';
			break;
		default:
			$r = 'plan_bas';
	}
	return $r;
}

// -------------------------------------------------------------------------------------------------

function dmx_get_function_icons($service)
{
	// Return array containing names of function icons for service
	switch (strtolower($service))
	{
		case 'dmconnect':
			$r[] = array('icon' => 'webcontrol', 	'infovar' => 'icon_func_webcontrol'		);
			break;
		case 'dmvoice':
			$r[] = array('icon' => 'voicemail', 	'infovar' => 'icon_func_voicemail'		);
			$r[] = array('icon' => 'email', 		'infovar' => 'icon_func_email_voice'	);
			$r[] = array('icon' => 'sms', 			'infovar' => 'icon_func_sms_voice'		);
			$r[] = array('icon' => 'webcontrol', 	'infovar' => 'icon_func_webcontrol'		);
			break;
		case 'dmfax':
			$r[] = array('icon' => 'email', 		'infovar' => 'icon_func_email_fax'		);
			$r[] = array('icon' => 'sms', 			'infovar' => 'icon_func_sms_fax'		);
			$r[] = array('icon' => 'webcontrol', 	'infovar' => 'icon_func_webcontrol'		);
			break;
		case 'dmswitchboard':
			$r[] = array('icon' => 'voicemail', 	'infovar' => 'icon_func_voicemail'		);
			$r[] = array('icon' => 'email', 		'infovar' => 'icon_func_email_voice'	);
			$r[] = array('icon' => 'sms', 			'infovar' => 'icon_func_sms_voice'		);
			$r[] = array('icon' => 'teamDivert',	'infovar' => 'icon_func_teamdivert'		);
			$r[] = array('icon' => 'menu', 		 	'infovar' => 'icon_func_menu'			);
			$r[] = array('icon' => 'infodept', 		'infovar' => 'icon_func_infodept'		);
			$r[] = array('icon' => 'wizard', 		'infovar' => 'icon_func_wizard'			);
			$r[] = array('icon' => 'webcontrol', 	'infovar' => 'icon_func_webcontrol'		);
			break;
		default:
			$r = array();
	}
	return $r;
}

// -------------------------------------------------------------------------------------------------

function dmx_get_currency_symbol($curr)
{
	// Work our currency symbol
	switch ($curr)
	{
		case 'UKPX':
		case 'UKPI':
			$cs = '£';
		case 'FUTURE':
			$cs = '?';
		default:
			$cs = '£';
	}
	return htmlentities($cs);
}

// -------------------------------------------------------------------------------------------------
// X C O M P O N E N T   M E M O R Y
// -------------------------------------------------------------------------------------------------

function dmx_store($var_name, $store)
{
	// Store data (usually related to component history) on the shopping server
	// $store is normally an array of values
	//
	global $dmshop, $dmx_debug, $dmx_session, $dmx_response, $dmx_memory;

	// Merge with any pre-existing memory
	if (isset($dmx_memory[strtoupper($var_name)]) && is_array($dmx_memory[strtoupper($var_name)]))
	{
		$store = array_merge($dmx_memory[strtoupper($var_name)], $store);
	}
	// Keep $dmx_memory up to date
	$dmx_memory[strtoupper($var_name)] = $store;

	$dmshop->_R = array();
	$dmshop->_R['TrolleyID'] = $dmx_session;	
	$dmshop->_R['Cmd'] = 'ListTrolley';
	$dmshop->_R['_Store_' . $var_name] = serialize($store); // Serialise data and send to the shopping server, using the list trolley command
	$dmx_debug->msg("<b>STORING DATA ON SHOPPING SERVER</b>\n_Store_" . $var_name . ' = ' . $dmshop->_R['_Store_' . $var_name]);
	$dmshop->request();
	$dmx_debug->msg($dmshop->debug->flush());
	if ($dmshop->result->status == false || !count($dmshop->result->response))
	{
		return(array('result' => 'comserr', 'details' => ''));
	}
	$dmx_response = $dmshop->result->response;
	if ($dmx_response['Cmd'] != 'ListTrolleyAck')
	{
		$dmx_debug->msg('<b>STORING DATA FAILED: </b> ' . $dmx_response['Reason']);			
	}
	$dmx_debug->divide();
}

// -------------------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------------------

function dmx_translate_err_code($comp_class, $err)
{
	// Translate the dmShop protocol error code into a corresponding error message to display
	// Error messages for each component are stored in the component options file
	
	@include DOCROOT . $GLOBALS['dmx_options']['templates'] . $comp_class . '_options.php';
	$err = strtolower($err);
	$err_msg = isset($err_msg) ? array_change_key_case($err_msg, CASE_LOWER) : array();
	if (isset($err_msg[$err]))
	{
		$output = $err_msg[$err];
	}
	else
	{
		$output = 'ERROR: ' . $GLOBALS['dmx_response']['Reason'];
	}
	$output .= '<br />';
	return $output;
}

// -------------------------------------------------------------------------------------------------
// B E N C H M A R K I N G   F U N C T I O N S
// -------------------------------------------------------------------------------------------------

function dmx_add_benchmark($name, $prev=-1)
{
	// Calculate the time since the page start, and, if a second argument
	// representing a previous benchmark is passed,
	// the time elapsed since the previous benchmark

	global $dmx_benchmarks, $dmx_time0;
	$mt = dmx_get_microtime();
	$bmi = count($dmx_benchmarks);
	$dmx_benchmarks[$bmi]['name'] = $name;
	$dmx_benchmarks[$bmi]['since_start'] = round(abs($mt - $dmx_time0) * 1000);
	$dmx_benchmarks[$bmi]['since_prev'] = $prev >= 0 ? round(abs($mt - $prev) * 1000): '';
	return $mt;
}

// -------------------------------------------------------------------------------------------------

function dmx_get_microtime()
{
	$mtime = microtime();
	$mtime = explode(' ', $mtime);
	$mtime = doubleval($mtime[1]) + doubleval($mtime[0]);
	return ($mtime);
}

// -------------------------------------------------------------------------------------------------
// F O R M   U T I L I T I E S
// -------------------------------------------------------------------------------------------------

// Functions for the validation of user data and the construction of forms (esp. popups)

function dmx_save_form_field_values($fields, $from)
{
	foreach ($fields as $name) $f[$name] = isset($from[$name]) ? $from[$name] : '';
	return $f;
}

// -------------------------------------------------------------------------------------------------

function dmx_test_exists($test, $alert = '_error')
{
	// $test is an array of 3 element arrays [ variable to test, form field identifier(for alert), error message ] ...
	// $alert is the error identifier HTML
	$error['message'] = '';
	for ($i=0; $i<count($test); $i++)
	{
		if (trim($test[$i][0]) == '')
		{
			$error['alert'][$test[$i][1]] = $alert;
			$error['message'] .= $test[$i][2] . '<br />';
		}
		else
		{
			$error['alert'][$test[$i][1]] = '';
		}
	}
	return $error;
}		

// -------------------------------------------------------------------------------------------------

function dmx_test_length($test, $alert = '_error')
{
	// $test is an array of 4 element arrays [ variable to test, length, form field identifier(for alert), error message ] ...
	// $alert is the error identifier HTML
	$error['message'] = '';
	for ($i=0; $i<count($test); $i++)
	{
		if (strlen($test[$i][0]) < $test[$i][1])
		{
			$error['alert'][$test[$i][2]] = $alert;
			$error['message'] .= $test[$i][3] . '<br />';
		}
		else
		{
			$error['alert'][$test[$i][2]] = '';
		}
	}
	return $error;
}

// -------------------------------------------------------------------------------------------------

function dmx_test_integer($test, $alert = '_error')
{
	// $test is an array of 3 element arrays [ variable to test, form field identifier(for alert), error message ] ...
	// $alert is the error identifier HTML
	$error['message'] = '';
	for ($i=0; $i<count($test); $i++)
	{
		$str = ereg_replace('^\++', '', $test[$i][0]); // Trim leading +'s (phone number test)
		$dec = strchr($str, '.'); // test for a decimal point
		if (!is_numeric($str) || $dec)
		{
			$error['alert'][$test[$i][1]] = $alert;
			$error['message'] .= $test[$i][2] . '<br />';
		}
		else
		{
			$error['alert'][$test[$i][1]] = '';
		}
	}
	return $error;
}

// -------------------------------------------------------------------------------------------------

function dmx_test_number($test, $alert = '_error')
{
	// $test is an array of 3 element arrays [ variable to test, form field identifier(for alert), error message ] ...
	// $alert is the error identifier HTML
	$error['message'] = '';
	for ($i=0; $i<count($test); $i++)
	{
		if (!is_numeric($test[$i][0]))
		{
			$error['alert'][$test[$i][1]] = $alert;
			$error['message'] .= $test[$i][2] . '<br />';
		}
		else
		{
			$error['alert'][$test[$i][1]] = '';
		}
	}
	return $error;
}

// -------------------------------------------------------------------------------------------------

function dmx_test_email($test, $alert = '_error')
{
	// $test is an array of 3 element arrays [ variable to test, form field identifier(for alert), error message ] ...
	// $alert is the error identifier HTML
	$error['message'] = '';
	for ($i=0; $i<count($test); $i++)
	{
		if (!ereg('^.+@.+\\..+$', $test[$i][0]))
		{
			$error['alert'][$test[$i][1]] = $alert;
			$error['message'] .= $test[$i][2] . '<br />';
		}
		else
		{
			$error['alert'][$test[$i][1]] = '';
		}
	}
	return $error;
}

// -------------------------------------------------------------------------------------------------

function dmx_generate_select($values, $selected='', $tabs=0)
{
	// generate a option list from the supplied array of values
	// if a value contains a colon, use the first argument in the value parameter and the second to display
	// selected specifies the option to mark as selected, if any
	// tabs specifies the number of tabs to insert, for neat formatting

	$t = '';
	while ($tabs > 0)
	{
		$t .= "\t";
		$tabs--;
	}
	$options = '';
	$loop = count($values);
	for ($i=0; $i<$loop; $i++)
	{
		$data = split(':', $values[$i]);
		$sel = trim($data[0]) == $selected ? ' selected="selected"' : '';
		if (isset($data[1]))
		{
			$options .= "$t<option value=\"". trim($data[0]) . "\"$sel>" . htmlentities(trim($data[1])) . "</option>\n";
		}
		else
		{
			$options .= "$t<option$sel>" . htmlentities(trim($data[0])) . "</option>\n";
		}
	}
	return $options;
}

// -------------------------------------------------------------------------------------------------
// G E N E R A L   U T I L I T I E S
// -------------------------------------------------------------------------------------------------

function dmx_cast_array_to_lc($array1)
{
	// Make the keys and string valuse of an associative array all lover case
	$array2 = array();
	reset($array1);
	while (list ($key, $value) = each($array1))
	{
		$array2[strtolower($key)] = gettype($value) == 'string' ? strtolower($value) : $value;
	}
	return $array2;
}

// -------------------------------------------------------------------------------------------------

function dmx_handle_magic_quotes($arr)
{
	// If magic quotes are on, stripslashes then return values as if they are off
	if (get_magic_quotes_gpc())
	{
		// Strip slashes 
		$arr = array_map('dmx_strip_slashes', $arr);
	}
	return $arr;
}

function dmx_strip_slashes($element) 
{
	if (is_array($element))	return array_map('dmx_strip_slashes', $element);
	else return stripslashes($element);
}

// -------------------------------------------------------------------------------------------------

function dmx_slasher($input)
{
	// Decode Expression Engline's slash encoding
	// (see definition of SLASH constant in core.system.php)
	//
	return str_replace('&#47;', '/', $input);
}

// -------------------------------------------------------------------------------------------------

function dmx_serialize($arr = array())
{
	// Added to handle custom serializaetion format created to prevent XComponents contravening
	// Expression Engines' security filters, when serialized values are passed through URLs
	//
	$result = serialize($arr);
	$result = str_replace(';' , '|sc|', $result);
	$result = str_replace('?' , '|qu|', $result);
	$result = str_replace('\'', '|sq|', $result);
	$result = str_replace('"' , '|dq|', $result);
	return $result;
}

// -------------------------------------------------------------------------------------------------

function dmx_unserialize($val = '')
{
	// Added to handle custom serializaetion format created to prevent XComponents contravening
	// Expression Engines' security filters, when serialized values are passed through URLs
	//
	$temp = urldecode($val); // Stripslashes just in case magic quotes is on
	$temp = str_replace('|sc|', ';' , $temp);
	$temp = str_replace('|qu|', '?' , $temp);
	$temp = str_replace('|sq|', '\'', $temp);
	$temp = str_replace('|dq|', '"' , $temp);
	$result = array_map('dmx_slasher', unserialize($temp));
	return $result;
}

// -------------------------------------------------------------------------------------------------

function dmx_return_doctype($type = '')
{
	// Translate DOCTYPE specified in dmx_options.php into required output
	switch($type)
	{
		case 'HTML 3.2':
			return "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\"\n        \"http://www.w3.org/MarkUp/Wilbur/HTML32.dtd\">";
		case 'HTML 4.01 Strict':
			return "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\"\n        \"http://www.w3.org/TR/html4/strict.dtd\">";
		case 'HTML 4.01 Transitional':
			return "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\n        \"http://www.w3.org/TR/html4/loose.dtd\">";
		case 'XHTML 1.0 Transitional':
			return "<?xml version=\"1.0\"?>\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n        \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">";
		case 'XHTML 1.0 Strict':
			return "<?xml version=\"1.0\"?>\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"\n        \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">";
		case 'XHTML 1.1':
			return "<?xml version=\"1.0\"?>\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\"\n        \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">";
		default:
			// XHTML 1.0 Transitional
			return "<?xml version=\"1.0\"?>\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n        \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">";
	}
}

?>