/**
 * CognitiveMatch Javascript Library Version 1.9.4
 */

/*
 * ---- Client Configuration ----
 */

/** Unique reference for customer's website account */
var CM_SITE_NAME = 'cancertypes';

/** Cognitivematch engine server to send all requests to */ 
var CM_SERVER = 'engine.cmmeeu.com';

/** 
 * Path to the cm_request.html which will be loaded in an iframe to make the request. 
 * NB. the path is relative to the page the request comes from 
 */
var CM_REQUEST_HTML_PATH = '/cm_request.html';

/** Name of array containing customer-specific variables to include with requests */
var CM_CUST_VAR_ARRAY_NAME = '';

/** Should the contents of the customer-specific array be included as logEvent parameters? */
var CM_INCLUDE_CUST_ARRAY_IN_LOG_EVENT = false;

/** Log debug information to firebug console (only set true for QA) */
var CM_ENABLE_FIREBUG_LOGGING = false;

/** Polling interval in milliseconds to see if matching area content has been populated */
var CM_RESPONSE_CHECK_PERIOD_MS = 10;

/** Timeout in milliseconds to stop polling and display default content */
var CM_RESPONSE_TIMEOUT_MS = 3500;

/** Delimiter between event-type and event parameters in a log-event query */
var CM_EVENT_DELIM = '|';

/** 
 * Should single-parameter calls to MatchingEngine.setup() use document.appendChild (true) 
 * or document.write() (false) to create the iframe 
 */
var CM_DEFAULT_IFRAME_USE_APPEND = false;

/*
 *	Cookie passing variables - only applicable if client website wants to pass us
 *	cookie data set in its own domain in impression requests (e.g. additional profile
 *	data about logged in users)
 */

/** Should we attempt to extract a profile id from a cookie in the client's domain */
var CM_INCLUDE_PROFILE_ID = false;

/** Name of cookie to look for profile id in*/
var CM_PROFILE_COOKIE_NAME = "";

/** Regular expression for matching profile id in client cookie */
var CM_PROFILE_ID_REGEX = CM_PROFILE_COOKIE_NAME + '=([^;]*)';

/*
 * Query-string parameter constants 
 */
var CM_QS_SITE_NAME_PARAM = "sitenm";
var CM_QS_CAPSULE_NAME_PARAM = "capnm";
var CM_QS_MATCHING_AREA_ID_PARAM = "maid";
var CM_QS_MATCHING_AREA_URL = "murl";
var CM_QS_TIME_PARAM = "t";
var CM_QS_TIME_ZONE_PARAM = "tzo";
var CM_QS_PAGE_VIEW_ID_PARAM = "pgid"; 
var CM_QS_IMPRESSION_ID = "impid";
var CM_QS_NAVIGATOR_PLUGINS = "nplg";
var CM_QS_WINDOW_HISTORY_LENGTH = "hstl";
var CM_QS_WINDOW_SCREEN_HEIGHT = "scrh";
var CM_QS_WINDOW_SCREEN_WIDTH = "scrw";
var CM_QS_WINDOW_PIXEL_DEPTH = "scrd";
var CM_QS_EXTERNAL_PARAMETERS = "ep";
var CM_QS_REFERRER = "ref";
var CM_QS_PAGE = "pg";
var CM_QS_ERROR = "err";
var CM_QS_PROFILE_ID = "pfid";
var CM_QS_PROFILE_ID_ERR = "pfiderr";
var CM_MAX_URL = 1024;

var CM_URI_CLICK_LOG_PATH = "/v1/click";
var CM_URI_REQUEST_LOG_PATH = "/v1/request";
var CM_URI_PAGE_VIEW_PATH = "/v1/page-view";

var CM_URI_VISITOR_EVENT_V1_PATH = "/v1/visitor-event";

var CM_URI_LOG_URL_REQUEST = "/v1/log-url-request";
var CM_URI_REWARD_EVENT_V1_PATH  = "/v1/secure-click-reward";

var CM_COOKIE_NAME_CAPSULE_CLICK = "click";
var CM_AREA_NAME_WILDCARD    = "*";


/*
 * ---------------------------------------------------------------------
 * Global functions, all function names are prefixed with "cm_"
 * ---------------------------------------------------------------------
 */


/*
 * ----------------- Misc utility functions -------------------
 */
/**
 * Optional debug logging to the javascript console for supported browsers/plugins.
 */
function cm_log(m) {
    if (CM_ENABLE_FIREBUG_LOGGING) {
        console.log(m);
    }
}

/**
 * @param p
 * @return truncates given string to CM_MAX_URL characters.
 */
function cm_trim(p) {
     if(!p) {
         return "";
     }
     if(p.length <= CM_MAX_URL) {
         return p;
     }
     return p.substr(0, CM_MAX_URL);
}

/**
 * Basic validation to L-trim the reward and remove the first non numeric character
 * i.e. remove all leading white space chars followed by the pound or dollar symbol
 */
function cm_format_reward(rewardString){
	
	var reward = rewardString.replace(/^\s+/,"");
	
	if(isNaN(reward.charAt(0))) {
		return reward.substring(1, reward.length);
	}
	return reward;
}

/**
 * Generates a unique page id 
 * Source: http://ajaxian.com/archives/uuid-generator-in-javascript 
 */
function cm_generate_page_view_id() {
    var s = [], itoh = '0123456789ABCDEF';
    var i = 0;
    var j = 0;
    // Make array of random hex digits. The UUID only has 32 digits in it, but we
    // allocate an extra items to make room for the '-'s we'll be inserting.
    for (; i <36; i++) {
        s[i] = Math.floor(Math.random()*0x10);
    }

    // Conform to RFC-4122, section 4.4
    s[14] = 4;  // Set 4 high bits of time_high field to version
    s[19] = (s[19] & 0x3) | 0x8;  // Specify 2 high bits of clock sequence

    // Convert to hex chars
    for (; j <36; j++) {
        s[j] = itoh.charAt(s[j]);
    }

    // Insert '-'s
    s[8] = s[13] = s[18] = s[23] = '-';

    var pageViewId = s.join('');
    return pageViewId;
}

/**
 * @param p
 * @return URI encodes the given string for inclusion in querystring
 */
function cm_encode(p) {
    return encodeURIComponent(p);
}

/**
 * @return true if the current url is https
 */
function cm_is_ssl() {
    var https = (document.location.protocol === "https");
    return https /*| true*/;
}

function cm_defined(name) {
    return typeof(name) !== 'undefined';
}

/**
 * Looks for a cookie set in the domain of the current url and returns its value if found.
 * @param name
 * @return
 */
function cm_read_cookie(name) {
    var nameEQ = name + "=";
    var ca = document.cookie.split(';');
    var i=0;
    for(;i < ca.length;i++) {
        var c = ca[i];
        while (c.charAt(0) === ' ') {
            c = c.substring(1,c.length);
        }
        if (c.indexOf(nameEQ) === 0) {
            return c.substring(nameEQ.length,c.length);
        }
    }
    return null;
}

/**
 * @return hostname of current url including port if specified.
 */
function cm_get_window_location() {
    var hostname = window.location.hostname;
    if (cm_defined(window.location.port) & window.location.port !== '') {
        hostname = hostname + ':' + window.location.port;
    }
    return hostname;
}

/**
 * Sets a cookie in the domain of the current url, under "/" path
 * @param name cookie name
 * @param value cookie value
 * @param seconds expiration time in seconds
 */
function cm_set_cookie(name,value,seconds) {
    var expires;
    if (seconds) {
        var date = new Date();
        date.setTime(date.getTime()+(seconds*1000));
        expires = "; expires="+date.toGMTString();
    } else {
        expires = "";
    }
    document.cookie = name+"="+value+expires+"; path=/";
}

function cm_delete_cookie(name) {
    cm_set_cookie(name,"",-1);
}

/*
 * ----------------------- Query-string Functions ------------------------------
 */

/** Unique id for the current pageview (re-generated for every request) */
var CM_PAGE_VIEW_ID = cm_generate_page_view_id();

/**
 * Builds the ProfileId info from the cookies present, using the CM_PROFILE_ID_REGEX regular expression
 * @returns {String}
 */
function cm_build_profile_id_query_string() {

	/* 
	 * Matches the cookie with the passed regex this puts the matched cookie into an array
	 * the 1st array item is the full cookie: eg "ERIGHTSID=123456"
	 * the 2nd arary item is just the value eg "123456"
	 */
	var matchedCookie = document.cookie.match(CM_PROFILE_ID_REGEX);

	if (matchedCookie === null) {
		// No cookie matched
		return CM_QS_PROFILE_ID_ERR + "=cookie";
	} else if (matchedCookie[1] === null || matchedCookie[1].length === 0) {
		// Cookie matched but no value present
		return CM_QS_PROFILE_ID_ERR + "=regex";
	} else {
		// Cookie matched with value
		return CM_QS_PROFILE_ID + "=" + matchedCookie[1];
	}
}

/**
 * @return gets the randomly generated page id for the current page view (used
 *         to link all requests which get sent from this page view)
 */
function cm_make_page_viewid_qs() {
    if (!CM_PAGE_VIEW_ID) {
        CM_PAGE_VIEW_ID = generatePageViewId();
    }
    return CM_QS_PAGE_VIEW_ID_PARAM + '=' + CM_PAGE_VIEW_ID;
}

/**
 * @return builds the client local time and timezone offset parameters of a request querystring
 */
function cm_make_time_qs() {
    var date = new Date();
    return CM_QS_TIME_PARAM + '=' + date.getTime() + '&' + CM_QS_TIME_ZONE_PARAM + '=' + date.getTimezoneOffset();
}

/**
 * @return common querystring parameters to include with every type of request to the capsule engine
 */
function cm_common_qs_params() {
    return CM_QS_SITE_NAME_PARAM + '=' + cm_encode(CM_SITE_NAME) + '&' + cm_make_time_qs() + '&' + cm_make_page_viewid_qs();
}

/**
 * Gets the customer-specific visitor data array (specified by CM_CUST_VAR_ARRAY_NAME).
 * If no variable is defined with this name then an empty array is returned.
 * @return customer-specific visitor data array
 */
function cm_get_cust_array() {
	var cust_arr = new Array();
	if(CM_CUST_VAR_ARRAY_NAME !== "" ) {
		eval("if (typeof(" + CM_CUST_VAR_ARRAY_NAME + ") !== 'undefined') cust_arr = " + CM_CUST_VAR_ARRAY_NAME + ";" );
	}
	return cust_arr;
}

/**
 * Constructs the portion of the querystring containing the common visitor predictors which can be
 * determined via javascript (browser settings, optional client-specific cookie settings, etc.)
 * 
 * @return visitor predictor querystring parameters
 */
function cm_make_production_mode_global_qs_params() {

    var qs = '';

    if (cm_defined(navigator.plugins)) {
        qs = qs + '&' + CM_QS_NAVIGATOR_PLUGINS + '=' +  navigator.plugins.length;
    }
    if (cm_defined(window.history)) {
        qs = qs + '&' + CM_QS_WINDOW_HISTORY_LENGTH + '=' +  window.history.length;
    }
    if (cm_defined(window.screen.height)) {
        qs = qs + '&' + CM_QS_WINDOW_SCREEN_HEIGHT + '=' +  window.screen.height;
    }
    if (cm_defined(window.screen.width)) {
        qs = qs + '&' + CM_QS_WINDOW_SCREEN_WIDTH + '=' +  window.screen.width;
    }
    if (cm_defined(window.screen.pixelDepth)) {
        qs = qs + '&' + CM_QS_WINDOW_PIXEL_DEPTH + '=' +  window.screen.pixelDepth;
    }
    // add the page url only when using SSL
    if( cm_is_ssl() ) {
        qs = qs + '&' + CM_QS_PAGE + '=' + cm_encode(cm_trim(document.location.href));
    }
    if (cm_defined(document.referrer) && document.referrer ) {
        qs = qs + '&' + CM_QS_REFERRER + '=' + cm_encode(document.referrer);
    }

    // If there should be a PROFILE_ID present extract it from the cookie
    if (CM_INCLUDE_PROFILE_ID) {
        visitorProfileParam = cm_build_profile_id_query_string();
        qs = qs + "&" + visitorProfileParam;
    }
    
    return qs;
}

/**
 * Constructs the portion of the request querystring which specifies the matching areas
 * to serve impressions for.
 */
function cm_build_matching_area_identifiers_qs(areaNames) {

    var qs = cm_common_qs_params();
    var i = 0;
    
    for (;i < areaNames.length;i++) {
        qs = qs + '&=[&' + CM_QS_MATCHING_AREA_ID_PARAM + '=' + cm_encode(areaNames[i]) + '&=]';
    }

    qs = qs + '&' + CM_QS_MATCHING_AREA_URL + '=' + cm_encode(document.URL);
    qs = qs + cm_make_production_mode_global_qs_params();

    return qs;
}

/*
   Logging
 */


function cm_new_xmlhttprequest() {

    var req = false;
    // branch for native XMLHttpRequest object
    if(window.XMLHttpRequest && !(window.ActiveXObject)) {
        try {
            req = new XMLHttpRequest();
        } catch(e1) {
            req = false;
        }
    // branch for IE/Windows ActiveX version
    } else if(window.ActiveXObject) {
        try {
            req = new ActiveXObject("Msxml2.XMLHTTP");
        } catch(e2) {
            try {
                req = new ActiveXObject("Microsoft.XMLHTTP");
            } catch(e3) {
                req = false;
            }
        }
    }
    return req;
}

function cm_ajax_log_request(url) {
    req = cm_new_xmlhttprequest();
    if (req) {
        // Asynchronous is false
        req.open("GET", url, false);
        req.send("");
        return true;
    } else {
        return false;
    }
}

function cm_ie_friendly_delay() {
    var x = 0;
    var now = new Date().getTime();
    var delay = now+150;
    while (new Date().getTime() < delay) {
        var e = document.getElementById(new Date().getTime());
        if (e !== null) {
            x = x+1;
        }
    }
    return x > 0;
}

function cm_image_log_click(url) {
    cm_log('cm_image_log_click url='+url);
    var logImage = new Image();
    logImage.src = url;
    cm_ie_friendly_delay();
    return true;
}

function cm_is_safari() {
    var ua = navigator.userAgent.toLowerCase();
    if (ua.indexOf('safari/') !== -1 && ua.indexOf('chrome') === -1){
        return true;
    } else {
        return false;
    }
}

function cm_is_chrome() {
    var ua = navigator.userAgent.toLowerCase();
    return ua.indexOf('chrome') !== -1;
}



/**
 * Sends a log-click request to the capsule server to register positive feedback about an impression.
 *
 * The method of sending the request varies depending on the browser.  
 *
 * @param capsuleName name of capsule for the impression
 * @param impressionId unique id of impression to log feedback against
 * @param reward optional numeric reward value indicating weight of feedback (not used for standard CTR capsules) 
 */
function cm_log_click(capsuleName,impressionId, reward) {

	var qs = cm_common_qs_params();
	qs = qs + '&' + CM_QS_CAPSULE_NAME_PARAM + '=' + cm_encode(capsuleName);
	qs = qs + '&' + CM_QS_IMPRESSION_ID + '=' + cm_encode(impressionId);
	
	var serverPath = CM_URI_CLICK_LOG_PATH;
	if(reward) {
		qs = qs + '&' + "erw" + '=' + reward;
		serverPath = CM_URI_REWARD_EVENT_V1_PATH;
	}
	
	var url = document.location.protocol + '//' + CM_SERVER + serverPath + '?'+ qs;

	if (cm_is_safari() || cm_is_chrome()) {
		cm_ajax_log_request(url);
	} else {
		cm_image_log_click(url);
	}

	return true;
}

function cm_log_click_check() {
	var cookie = cm_read_cookie(CM_COOKIE_NAME_CAPSULE_CLICK);
	if (cookie !== null) {
		var logImage = new Image();
		logImage.src = document.location.protocol + '//' + CM_SERVER + CM_URI_CLICK_LOG_PATH + '?'+cookie;
		cm_delete_cookie(CM_COOKIE_NAME_CAPSULE_CLICK);
	}
}



/**
 * @return the version of Internet Explorer or a -1 if not IE
 */
function cm_get_ie_ver() {
	var rv = -1; // Return value assumes failure.
	if (navigator.appName === 'Microsoft Internet Explorer') {
		var ua = navigator.userAgent;
		var re  = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})");
		if (re.exec(ua) !== null) {
			rv = parseFloat( RegExp.$1 );
		}
	}
	return rv;
}

/*
 * --------------------------------------------
 *      MatchingEngine Definition Start 
 * --------------------------------------------
 */

/**
 * Constructor - instantiates a new MatchingEngine instance with empty
 * area map.
 */
function MatchingEngine() {
    this.areas = []; // Map of matching area content, indexed by matching-area name.
}

/*
 * ------------------------------ Public Methods ------------------------------
 */

/**
 * Set the request path if the the cm_request.html file is not in the
 * root directory
 * 
 * @param (string) pathName new path to the cm_request.html file 
 */
MatchingEngine.prototype.setRequestPath = function(pathName) {
     
    if(arguments.length === 0) {
        // no path specified so return
        return;
    }
    
    var newpath = arguments[0];
    
    if(newpath.charAt(newpath.length-1) === '/'){       
        newpath = newpath.substring(0, newpath.length-1);
    }
    
    CM_REQUEST_HTML_PATH = newpath + CM_REQUEST_HTML_PATH;  
};

/**
 * Setup matching areas.
 * 
 * Builds the url to make the impression request to the capsule engine and
 * constructs an iframe to handle sending the request in the background.
 * 
 * NB. For pages containing matching areas this should be the first cm call on
 * the page. It should never be called more than once per pageview.
 * 
 * @param (string) areaNamesList - comma separated string of matching area names to
 *            request content for
 * @param (string) externalParameters - any client-specific parameters to append to the
 *            request url as a string of "param1=value1&param2=value2..." (optional)
 * @param (boolean) useAppend - if true then we use document.body.appendChild() to insert
 * 				the iframe, if false (default) we use document.write(). Generally we only
 *				need to use append if setup() is called from a document.ready() block (write
 *				overwrites the whole dom)
 */
MatchingEngine.prototype.setup = function(areaNamesList,externalParameters,useAppend) {
    var ie_version = cm_get_ie_ver();
	if (ie_version === 5) {
        return;
    }
    var i = 0;
    this.areaNames = areaNamesList.split(',');
    this.serverTimeout = false;
    this.timestamp = new Date().getTime();
    
    // Initialise the map of matching area content to be populated from the i-frame callback
    for (; i < this.areaNames.length;i++) {
        var area = {};
        var thisAreaName = this.areaNames[i];
        area.name = thisAreaName;
        area.content = null;
        area.displayed = false;
        this.areas[thisAreaName] = area;
    }
    
    // Build the request to send to the capsule engine
    var cm_request_qs = cm_build_matching_area_identifiers_qs(this.areaNames);
    var url = document.location.protocol + '//' + cm_get_window_location() + CM_REQUEST_HTML_PATH + '?' + cm_request_qs;
    
    // Add any customer-specific parameters
    var cust_param_str = '';
    if(cm_defined(externalParameters) && externalParameters.length > 0) {
    	// add parameters passed into the setup() call
    	cust_param_str = cust_param_str + cm_encode(externalParameters);
    }
    if (CM_CUST_VAR_ARRAY_NAME !== '' ) {
    	// add parameters set in the customer-variable array (defined by CM_CUST_VAR_ARRAY_NAME)
    	var cust_var_array_str = this._buildCustomerVariableQs();
    	if(cust_param_str.length > 0) {
    		cust_param_str = cust_param_str + '%26';
    	}
    	cust_param_str = cust_param_str + cust_var_array_str;
    }
    if(cust_param_str.length > 0) {
    	url = url + '&' + CM_QS_EXTERNAL_PARAMETERS + '=' + cust_param_str;
    }

	// execute the request asynchronously in an iframe
	if(!cm_defined(useAppend)) {
		useAppend = CM_DEFAULT_IFRAME_USE_APPEND;
	}
	if(useAppend === true) {
		var iframeEl = document.createElement("iframe");
		iframeEl.setAttribute("id","cm_server_request");
		if (ie_version === 6 || ie_version === 7) {
			iframeEl.style.setAttribute('cssText', 'display:none', 0);
		} else {
			iframeEl.setAttribute("style","display:none;");
		}
		iframeEl.setAttribute("src",url);
		document.body.appendChild(iframeEl);
	} else {
		var iframeSrc = '<iframe id="cm_server_request" style="display:none" src="'+url+'"></iframe>';
		document.write(iframeSrc);
	}

    // start the timer to poll for a response
    setTimeout("MatchingEngine._responseChecker()",CM_RESPONSE_CHECK_PERIOD_MS);
};

/**
 * Sends a visitorEvent request to the capsule-engine (log of a significant
 * end-user interaction on a tracked page on the customer's domain)
 * 
 * e.g. logEvent('purchase','id=3445-23-12','price=123','color=red');
 * 
 * @param {string} eventType description of the interaction (format dependent on
 *        site) 
 * @param eventParameters [optional] eventParameter(s) at least on "name=value"
 *            string.
 */
MatchingEngine.prototype.logEvent = function(eventType) {
    
    var i=1;
    if(arguments.length === 0) {
        // invalid call - no event details
        return;
    }

    eventType = arguments[0];
    var eventQsParam = eventType;
    for(; i < arguments.length; i++) {
        eventQsParam = eventQsParam + CM_EVENT_DELIM + arguments[i];
    }
    
    if(CM_INCLUDE_CUST_ARRAY_IN_LOG_EVENT && CM_CUST_VAR_ARRAY_NAME !== '' ) {
    	var cust_arr = cm_get_cust_array();
    	for(key in cust_arr) {
    		var rawData = cust_arr[key];
    		if(rawData != null) {
    			var safeData = cust_arr[key].toString().replace(/\|/g,',');
    			eventQsParam = eventQsParam + CM_EVENT_DELIM + key + '=' + safeData;
    		}
    	}	
    }
    
    var qs = 'event=' + cm_encode(eventQsParam) + "&" + cm_common_qs_params() + cm_make_production_mode_global_qs_params();
    var logImage = new Image();
    logImage.src = document.location.protocol + '//' + CM_SERVER + CM_URI_VISITOR_EVENT_V1_PATH + '?' + qs;
};

/**
 * Sends a pageView request to the capsule-engine (log of a visitor clicking
 * on any tracked page on the customer's domain)
 */
MatchingEngine.prototype.logPageView = function() {
    var qs = cm_common_qs_params() + cm_make_production_mode_global_qs_params();
    var logImage = new Image();
    logImage.src = document.location.protocol + '//' + CM_SERVER + CM_URI_PAGE_VIEW_PATH + '?' + qs;
};

/**
 * Represents the last stage of a process pipeline, e.g. reaching the product purchase confirmation screen.
 *
 * This will log will log a click event plus any other relevant logging event information.
 *
 * e.g. logConversion('ticket','firstClassId','secondClassId');
 * 
 * Parameters:
 * Parameters represent the logEvent details in the following format:
 * EventDesription followed by optional doucment elementId's that must be located on the page.
 */
MatchingEngine.prototype.logConversion = function() {

    // log the click
    var impressionId = cm_read_cookie("cm_impid");
    var capsuleName = cm_read_cookie("cm_capsulename"); 
    var i=1;
    cm_log_click(capsuleName,impressionId);     
    
    // log the event
    if(arguments.length === 0) {
        return;
    }
    
    var eventParams = arguments[0];
    for(; i < arguments.length; i++) {
        eventParams = eventParams + CM_EVENT_DELIM + arguments[i] + '=' + document.getElementById(arguments[i]).innerHTML;
    }
            
    MatchingEngine.logEvent(eventParams);
};

/**
 * Sends a reward-click event for reward-driven capsule impression to the
 * capsule engine server.
 * 
 * Assumes: 
 *  - A 1st party cookie has been set which contains a capsule name and impression id of the impression 
 *  - There is a "reward" element somewhere on the page which contains the numeric reward data
 * 
 * @return true
 */
MatchingEngine.prototype.logReward = function() {

    var impressionId = cm_read_cookie("cm_impid");
    var capsuleName = cm_read_cookie("cm_capsulename");
    var reward = cm_format_reward(document.getElementById("reward").innerHTML);
    
    cm_log_click(capsuleName, impressionId, reward);
    return true;
};


/**
 * Force display of a matching area (if content has been returned)
 * @deprecated  legacy method for customers who can't update their old matching engine calls.
 */
MatchingEngine.prototype.display = function(areaName) {
    this._displayInternal(areaName, true);
};


/*
 * ------------------------------ Private Methods ------------------------------
 */

/**
 * Converts any data stored in the customer-specific variable array into uri-encoded
 * string which can be included in external parameter querystring param in the impression 
 * request.
 * e.g. if we have an array like:
 * 		tackingInfo['gender']='male';
 * 		trackingInfo['from']='LON';
 * 		trackingInfo['to']='NYC';
 * 	
 * 	would return "gender%3Dmale%26from%3DLON%26to%3DNYC"	
 */
MatchingEngine.prototype._buildCustomerVariableQs = function() {
	var cust_arr = cm_get_cust_array();
	cust_param_str = '';
	for(key in cust_arr) {
		if(cust_param_str.length > 0) {
			cust_param_str = cust_param_str + '&';
		}
		cust_param_str = cust_param_str + key + '=' + cust_arr[key];
	}	
	return cm_encode(cust_param_str);
};

/**
 * Injects the content of the given matching area into its corresponding DOM
 * element and displays/executes it.
 * 
 * @param {string} areaName - name of matching area to display
 * @param {boolean} addElement - optional flag to generate a new span element to
 *            inject the content into rather than injecting into a predefined
 *            element.
 */
MatchingEngine.prototype._displayInternal = function(areaName, addElement) {

    if (cm_get_ie_ver() === 5) {
        this._displayAreaDefault(areaName);
        return;
    }
    
    var area = this.areas[areaName];
    if (cm_defined(area)) {
        if (addElement) {
            document.write('<span id="'+areaName+'">');
        }
        if (area.content !== null) {
            cm_log('display:responseReceived = true '+areaName);
            if (addElement) {
                    document.write(area.content);
                    area.displayed = true;
            } else {
                var cmElement = document.getElementById(areaName);
                if (cmElement !== null) {
                    MatchingEngine._replaceWithScripts(cmElement,area.content);
                       area.displayed = true;
                }
            }
        }
        if (addElement) {
            document.write('</span>');
        }
    }
};

/**
 * @return true if all areas have been displayed, false if at least one area is not yet displayed.
 */
MatchingEngine.prototype._displayAll = function() {

    var allDisplayed = true; // Set true then check for not displayed case.
    var j = 0;
    for (;j<this.areaNames.length;j++)  {

        var area = this.areas[this.areaNames[j]];
        cm_log('area '+area);

        if (!area.displayed) {
            // Check if response filled concurrently via cm_request.html
            if (area.content !== null) {
                var el = document.getElementById(area.name);
                cm_log(area.name+' = '+el);
                // Check for null as element may not be present in DOM as this time.
                if (el !== null) {
                    // Content can be a combination of HTML, JS or both
                    MatchingEngine._replaceWithScripts(el,area.content);
                    area.displayed = true;
                } else {
                    area.displayed = false;
                    allDisplayed = false;
                }
            } else {
                area.displayed = false;
                allDisplayed = false;
            }

        } else {
            cm_log('capsule.displayed capsule='+area.name);
        }
    }
    return allDisplayed;
};

/**
 * Injects the given content into the given destination DOM element in such a way that any script
 * tags in the content will also be executed.
 * 
 *  @param {string} destination - target element to inject content into
 *  @param {string} content     - content to inject
 */
MatchingEngine.prototype._replaceWithScripts = function(destination,content) {
    
//    if(content.toLowerCase().indexOf("<script") >= 0 ) {
//        // work-around for some browsers to allow execution of injected javascript
//        destination.innerHTML = "&nbsp;" + content;
//    } else {
//		destination.innerHTML = content;
//	}
	destination.innerHTML = content;
	
    /*
     * Solution for executing java-script tags injected into the DOM after
     * page load (just setting the inner html writes the tag ok but doesn't execute
     * it). We copy the content of all the script tags we've just injected into a temp
	 * array, delete the original script elements, then programmatically build new 
	 * script elements. 
     */
    var myScripts = destination.getElementsByTagName( "script" );
	
    if( myScripts && myScripts.length > 0 ) {
        var n = myScripts.length;
        var i = n-1;
		var scriptCopy = [];
		for(; i >= 0; i--) {
			var script = myScripts[i];
			scriptCopy[i] = myScripts[i].text;
			script.parentNode.removeChild(script);
		}
		
		for(i=0; i < n; i++) {
			var elScript = document.createElement( "script" );
            elScript.setAttribute( "type", "text/javascript" );
            elScript.setAttribute( "language", "JavaScript" );
            elScript.text = scriptCopy[i];
            destination.appendChild( elScript );
		}
    }
};

/**
 * Scheduled background task which polls at regular intervals until all matching
 * areas have been displayed or the response timeout is triggered (in which case
 * any non-displayed areas are populated with default content).
 */
MatchingEngine.prototype._responseChecker = function () {
    
    if (this.serverTimeout) {
        // we have already timed out - stop polling.
        return; 
    }

    var allCapsulesDisplayed = this._displayAll();
    
    // time in ms since impression request was sent
    var elapsed = new Date().getTime() - this.timestamp; 
                                                                                            
    var timeout = elapsed >= CM_RESPONSE_TIMEOUT_MS;
    cm_log("elapsed "+elapsed);
    if (!allCapsulesDisplayed && !timeout) {
        // not all areas have been displayed, check again in the next interval
        setTimeout("MatchingEngine._responseChecker()",CM_RESPONSE_CHECK_PERIOD_MS);
    } else if (!allCapsulesDisplayed && timeout) {
        // max allowed response time has passed - trigger default display
        cm_log('timeout allDisplayed='+allCapsulesDisplayed);
        this._handleTimeout();
    }
};

/**
 * Handles displaying default content in the event that there has been no response from the capsule
 * engine within the allowed time.
 */
MatchingEngine.prototype._handleTimeout  = function() {
    var i = 0;
    cm_log('timeout');
    this.serverTimeout = true;
    for (;i < this.areaNames.length;i++) {
        var areaName = this.areaNames[i];
        this.areas[areaName].displayed = this._displayAreaDefault(areaName);
    }

};


/**
 * Displays the hard-coded default content for the given matching area (if defined).
 * 
 * Default content is assumed to be provided in a hidden dom element called "<areaName>_default".
 *
 * Returns true if default content was found and displayed successfully.
 */
MatchingEngine.prototype._displayAreaDefault  = function(areaName) {

    var defaultTag = document.getElementById(areaName+'_default');
    if (defaultTag !== null) {
        var el = document.getElementById(areaName);
        if (el !== null) {
            el.innerHTML = defaultTag.innerHTML;
            return true;
        }
    }
    return false;
};

/**
 * Sets the content of a matching area variable (if it exists) and displays it.
 *
 * Called from the cm_request iframe for a successful matching area impression served from
 * the capsule name. 
 */ 
MatchingEngine.prototype._callback = function(areaName,areaContent) {

    cm_log('_callback areaName='+areaName);
    var area = this.areas[areaName];
    if (cm_defined(area)) {
        area.content = areaContent;
        this._displayInternal(areaName, false);
    }
};

/**
 * Sets the content of a matching-area to be the default defined in the page and displays it.
 *
 * Called from the cm_request iframe if the server could not serve content
 * for the area, e.g. if the matching area could not be found.
 * 
 * If areaName == CM_AREA_NAME_WILDCARD then displays default content for all areas on the
 * page (e.g. if the capsule engine is completely unavailable or has encountered a serious error)
 */
MatchingEngine.prototype._callback_display_default = function(areaName) {
    var i = 0;
    cm_log('_callback_display_default areaName='+areaName);
    if (areaName !== CM_AREA_NAME_WILDCARD) {
        this._displayAreaDefault(areaName);
    } else {
        for (;i < this.areaNames.length;i++) {
            this._displayAreaDefault(this.areaNames[i]);
        }
    }
};

/*
 * ------------------------------ MatchingEngine Definition End ------------------------------
 */


/**
 * Declare variable last.
 */
var MatchingEngine = new MatchingEngine();

