/*
* This is the configuration file for 442
* This is used on the /tests/ to test the class, configuration adn compatibility with 3rd party javascripts
* It also allows ad-operations to test adverts
*/
var HaymarketAnalyticsConfig = {
    charSet: "UTF-8"
	, currencyCode: "GBP"
    /* Google Analytics */
	/*, googleAnalyticsAccount: '  ' */
    /* Nedstat Sitestat */
	/*, nedstatSitestatAccount: 'haymarket/442'*/
	/*, nedstatSitestatTrackableLinks: true*/
    /* DART */
	, dartSitename: 'fft'
    , dartPromoteHierarchy1ToSubsite: false
    , dartMaxItemsInZone: 1
    /* Revenue Sciences */
    	, revenueSciencesAccount: "D09820"
    /* DEBUG true/false - reports to firebug console */
	, debug: false
    /* Other Includes */
	, includes: [/*'http://js.forrestersurveys.com/socratos_75281.js', 'http://cetrk.com/pages/scripts/0008/3039.js'*/]
};
/**
* HaymarketAnalytics
*/
HaymarketAnalytics.startTime = (new Date()).getTime();

function HaymarketAnalytics(params) {

    var startTime;
    this.params = params || {};
    this.siteConfig = (typeof (HaymarketAnalyticsConfig) == 'object') ? HaymarketAnalyticsConfig : {};
    if (this.siteConfig.jQueryNoConflictMode == true) {
        jQuery.noConflict();
    }
    this.maxAffiliationDays = isNaN(this.siteConfig.maxAffiliationDays) ? 15 : this.siteConfig.maxAffiliationDays;
    this.debug = new HaymarketAnalyticsDebug(this.params.debug || this.siteConfig.debug);
    this.scheme = (("https:" == document.location.protocol) ? "https" : "http");
    this.hostname = window.location.hostname;
    this.path = window.location.pathname;
    this.randomNumber = HaymarketAnalytics.random();
    /* metrics containers - NB: limit metric names <= 9 chars */
    this.systemMetrics = { id: '', scheme: this.scheme, hostname: this.hostname, version: '', server: '', locale: '', userid: '', userstatus: '', controller: '', action: '' };
    this.contentMetrics = { id: '', title: '', subtitle: '', rating: '', language: '', author: '', type: '', group: '', page: '', format: '' };
    this.navigationMetrics = { site: '', section: '', hierarchy: [] };
    this.productMetrics = { id: '', dealer: '', brand: '', model: '', version: '', edition: '', format: '', style: '', size: '', price: '', condition: '' };
    this.locationMetrics = { geocode: '', continent: '', country: '', region: '', subregion: '', city: '', postcode: '' };
    this.affiliateMetrics = this.retrieveAffiliation({ code: '' }, typeof (this.siteConfig.affiliateKeys) != 'undefined' ? this.siteConfig.affiliateKeys : new Array("affilate", "referrer"));

    this.searchMetrics = { query: '', filters: [], count: '', result: '' };
    this.teamMetrics = { nationality: '', club: '', division: '', position: '', sponsor: '', skill: '', country: '', region: '', competition: '' };

    return this;
};

HaymarketAnalytics.prototype.render = function() {

    /* RevenueSciences - required to set cookies */
    if (typeof (this.siteConfig.revenueSciencesAccount) != 'undefined') {
        this.setRSCookies();
    }
    /* Google Analytics */
    if (typeof (this.siteConfig.googleAnalyticsAccount) != 'undefined') {
        this.renderGoogleAnalytics();
    }
    /* Nedstat Sitestat */
    if (typeof (this.siteConfig.nedstatSitestatAccount) != 'undefined') {
        this.renderNedstatSitestat();
    }

    /* ClickDensity */
    if (typeof (this.siteConfig.clickdensitySiteID) != 'undefined') {
        this.renderClickDensity();
    }

    /* Arbitrary includes */
    this.renderConfigDefinedIncludes();

    this.saveAffiliation();
};

/**
* config can include an arbitrary list of other js includes to include dynamically
*/
HaymarketAnalytics.prototype.renderConfigDefinedIncludes = function() {

    if (typeof (this.siteConfig.includes) == "object" && this.siteConfig.includes.length > 0) {
        for (var i in this.siteConfig.includes) {
            this.includeJavascript(this.siteConfig.includes[i], function() { return true; }, this);
        }
    }
};

HaymarketAnalytics.prototype.setSystemMetrics = function(vars) {
    this.systemMetrics = HaymarketAnalytics.mergeValues(this.systemMetrics, vars);
};
HaymarketAnalytics.prototype.setContentMetrics = function(vars) {
    this.contentMetrics = HaymarketAnalytics.mergeValues(this.contentMetrics, vars);
};
HaymarketAnalytics.prototype.setNavigationMetrics = function(vars) {
    this.navigationMetrics = HaymarketAnalytics.mergeValues(this.navigationMetrics, vars);
};
HaymarketAnalytics.prototype.setProductMetrics = function(vars) {
    this.productMetrics = HaymarketAnalytics.mergeValues(this.productMetrics, vars);
};
HaymarketAnalytics.prototype.setLocationMetrics = function(vars) {
    this.locationMetrics = HaymarketAnalytics.mergeValues(this.locationMetrics, vars);
};
HaymarketAnalytics.prototype.setAffiliateMetrics = function(vars) {
    this.affiliateMetrics = HaymarketAnalytics.mergeValues(this.affiliateMetrics, vars);
};
HaymarketAnalytics.prototype.setSearchMetrics = function(vars) {
    this.searchMetrics = HaymarketAnalytics.mergeValues(this.searchMetrics, vars);
};
HaymarketAnalytics.prototype.setTeamMetrics = function(vars) {
    this.teamMetrics = HaymarketAnalytics.mergeValues(this.teamMetrics, vars);
};

HaymarketAnalytics.prototype.overrideConfig = function(config) {
    this.siteConfig = HaymarketAnalytics.mergeValues(this.siteConfig, config);
};

HaymarketAnalytics.prototype.retrieveAffiliation = function(matrix, keys) {
    this.renewAffiliation = {};

    var reserve = new Array();
    for (var r in matrix) {
        reserve[reserve.length] = r;
    }
    for (var i in keys) {
        var key = keys[i];
        this.renewAffiliation[key] = false;
        /*code is reserve  word*/
        if (HaymarketAnalytics.itemInArray(key, reserve)) {
            if (typeof (key) == "string") {
                this.logToConsole(key + " is already used in affiliate matrix please change site configuration");
            }
            continue;
        }
        try {
            matrix[key] = HaymarketAnalytics.queryString(key);
            if (matrix[key] == null) {
                matrix[key] = HaymarketAnalytics.getCookie(key);
            } else {
                this.renewAffiliation[key] = true;
            }
            if (matrix[key] != null) {
                this.logToConsole("retrieved affilate " + key + "[" + matrix[key] + "] from " + (this.renewAffiliation[key] ? "cookie" : "query"));
            }
        } catch (ex) { this.logToConsole("retrieve affilate key " + key + " faild exception" + ex); }

    }
    return matrix;
};

HaymarketAnalytics.prototype.saveAffiliation = function() {
    for (var key in this.affiliateMetrics) {
        var value = this.affiliateMetrics[key];
        try {
            if (typeof (value) != "undefined" && value != null && value != "") {
                if (this.renewAffiliation[key]) {
                    HaymarketAnalytics.setCookie(key, value, this.maxAffiliationDays);
                    this.logToConsole("save affilate " + key + "[" + value + "] for " + this.maxAffiliationDays + " days");
                }
            } else {
                HaymarketAnalytics.setCookie(key, "", -5);
            }
        }
        catch (ex) { this.logToConsole("save affilate key " + key + "  faild exception " + ex); }
    }
};

/* util - get all completed metrics as kv pairs 
* pass "requestedSet" (e.g. this.getAllMetricsKeyValue("product") ) or leave undefined for all
*/
HaymarketAnalytics.prototype.getAllMetricsKeyValue = function(requestedSet) {
    var keyValues = HaymarketAnalytics.getKeyValues(this.getAllMetrics(requestedSet), true);
    for (var key in keyValues) {
        if (HaymarketAnalytics.isNotEmptyString(key)) {
            keyValues[key] = HaymarketAnalytics.validChars(keyValues[key]);
        }
    }
    return keyValues;
};

HaymarketAnalytics.prototype.getAllMetrics = function(requestedSet) {
    var allMetrics = {
        system: this.systemMetrics, content: this.contentMetrics, navigation: this.navigationMetrics,
        product: this.productMetrics, location: this.locationMetrics, affiliate: this.affiliateMetrics,
        search: this.searchMetrics, team: this.teamMetrics
    };
    if (HaymarketAnalytics.isNotEmptyString(requestedSet)) {
        var subset = {};
        subset[requestedSet] = allMetrics[requestedSet];
        return subset;
    }
    return allMetrics;
};

/** create a breadcrumb-like string used in most targetting systems from the navigation information 
* Currently, the ZONE is constructed form the navigation hierarchy and some product properties (if present)
**/
HaymarketAnalytics.prototype.getGenerateNavigationZone = function(requiredDepth, includeProductMetrics) {

    var allMetrics = this.getAllMetricsKeyValue();
    requiredDepth = (requiredDepth == null) ? 10 : requiredDepth;
    var includeProductMetrics = (includeProductMetrics == null || includeProductMetrics === true) ? true : false;
    var formatter = function(name, val, mix) { val = HaymarketAnalytics.stripNonAlphanum(val); return val; };
    var nav = HaymarketAnalytics.MixValues(allMetrics, ["navigation_hierarchy_.*"], function(name, val, mix) { return !(HaymarketAnalytics.isNotEmptyString(val) && mix.length < requiredDepth); }, formatter);
    if (includeProductMetrics) {
        var prod = HaymarketAnalytics.MixValues(allMetrics,
        ["product_brand", "product_model", "product_edition", "product_format", "product_style", "product_size"],
         function(name, val, mix) { return !HaymarketAnalytics.isNotEmptyString(val) }, formatter);
        for (var v in prod) { nav.push(prod[v]); }
    }
    return nav.join('.');
};

HaymarketAnalytics.prototype.getNedstatCustomValues = function() {
    var customValues = "";
    var spaceChar = "_";
    var allMetrics = this.getAllMetricsKeyValue();
    for (var i in allMetrics) {
        if (typeof (allMetrics[i]) != "function") {
            customValues += ("&hm_" + i + "=" + HaymarketAnalytics.encodeURIComponent(allMetrics[i], spaceChar));

            /* search is treated specially */
            if (i == "search_query") {
                customValues += ("&ns_search_term" + "=" + HaymarketAnalytics.encodeURIComponent(allMetrics[i], spaceChar));
            }
            if (i == "search_count") {
                customValues += ("&ns_search_result" + "=" + HaymarketAnalytics.encodeURIComponent(allMetrics[i], spaceChar));
            }
        }
    }
    return customValues;
};

HaymarketAnalytics.prototype.renderNedstatSitestat = function(brandname, productname) {

    var ns_l = this.scheme + "://uk.sitestat.com/" + this.siteConfig.nedstatSitestatAccount + "/s?" + this.getGenerateNavigationZone().toUpperCase();
    ns_l += this.getNedstatCustomValues();
    ns_l += "&ns__t=" + new Date().getTime();
    /* add page load */
    ns_0 = document.referrer;
    ns_l += "&ns_jspageurl=" + escape(document.domain + location.pathname);
    ns_0 = (ns_0.lastIndexOf("/") == ns_0.length - 1) ? ns_0.substring(ns_0.lastIndexOf("/"), 0) : ns_0;
    if (ns_0.length > 0) ns_l += "&ns_referrer=" + escape(ns_0);
    if (document.images) {
        ns_1 = new Image(); ns_1.src = ns_l;
    }
    else {
        document.write("<img src=" + ns_l + " width=1 height=1>")
    };
    if (this.siteConfig.nedstatSitestatTrackableLinks && (typeof (jQuery) != "undefined")) {
        this.observeLinks(this, HaymarketAnalytics.nedstatOvserver, { zone: this.getGenerateNavigationZone().toUpperCase() });
    }
};

/* direct copy of code supplied by nedstat, with includion of our config where required */
HaymarketAnalytics.ns_onclick = function(thisObj, e, i, b, c, h, j) {
    e += c;
    e += "&ns_type=" + h + "&ns_action=view";
    e += "&ns__t=" + (new Date()).getTime();
    if (!b) {
        b = i.href
    }
    var d = document.referrer;
    if (d.lastIndexOf("/") == d.length - 1) {
        d = d.substring(d.lastIndexOf("/"), 0)
    }
    if (d.length > 0) {
        e += "&amp;ns_referrer=" + escape(d)
    }
    j = j || "";
    var f = (i && i.target && i.target != "") ? (i.target.substring(0, 1) == "_") ? i.target.substring(1) : i.target : "self";
    var g = new Image();
    if (f && b) {
        if (window[f] && b.substring(0, 4) == "http") {
            window.ns_softclick_timer = function(k, a) {
                return function() {
                    if (window.ns_softclick_timeout) {
                        window.clearTimeout(window.ns_softclick_timeout);
                    }
                    g.onload = g.onerror = function() {
                        return;
                    };
                    /* window[((window[k]) ? k : "self")].location.href = a; */
                };
            }
            (f, b);
            ns_softclick_timeout = window.setTimeout("ns_softclick_timer()", 5000);
            g.onload = g.onerror = window.ns_softclick_timer;
        }
        else {
            /* window.open(b, f, j); */
        }
    }
    thisObj.logToConsole("trackable link: " + e);
    g.src = e;
    return false;
};

HaymarketAnalytics.prototype.renderGoogleAnalytics = function() {
    var gaJsHost = ((this.scheme == "https") ? "https://ssl." : "http://www.");
    var gapath = unescape(gaJsHost + 'google-analytics.com/ga.js');
    this.includeJavascript(gapath, HaymarketAnalytics.renderGoogleAnalyticsCallback, this);
};

HaymarketAnalytics.renderGoogleAnalyticsCallback = function(instance) {
    var cfg = instance.siteConfig;
    var pageView = '';

    if (typeof (_gat) != 'object') {
        instance.logToConsole('Google Analytics object missing. Perhaps javascript did not load');
    }
    else {
        pageTracker = _gat._getTracker(instance.siteConfig.googleAnalyticsAccount);
        if (typeof (cfg.googleAnalyticsDomain) != 'undefined') {
            pageTracker._setDomainName(cfg.googleAnalyticsDomain);
            pageTracker._setAllowHash("false");
            pageTracker._setAllowLinker(true);
            instance.logToConsole('Google Analytics domain set: ' + cfg.googleAnalyticsDomain);
        }
        cfg = cfg.googleAnalytics;
        if (typeof (cfg) == 'object') {
            var allMetrics = instance.getAllMetricsKeyValue();
            if (typeof (cfg.Variables) == 'object') {
                for (var slot = 0; slot < Math.min(cfg.Variables.length, 5); slot++) {
                    HaymarketAnalytics.addGoogleAnalyticsCustomVar(instance, allMetrics, slot, cfg.Variables[slot]);
                }
            }
            if (typeof (cfg.TrackView) != 'undefined') {
                var filter = function(name, val, mix) { return !HaymarketAnalytics.isNotEmptyString(val); };
                var formatter = function(name, val, mix) { return HaymarketAnalytics.stripNonAlphanum(val,'-'); };
                var mix = HaymarketAnalytics.MixValues(allMetrics, cfg.TrackView, filter, formatter);
                pageView = mix.join('/');
            }
            instance.observeLinks(instance, HaymarketAnalytics.googleAnalyticsObserver, { pageTracker: pageTracker, allMetrics: allMetrics, cfg: cfg.Event });
        }

        pageTracker._initData();

        if (HaymarketAnalytics.isNotEmptyString(pageView)) {
            pageTracker._trackPageview(pageView);
            instance.logToConsole("Google Analytics pageview:" + pageView);
        } else {
            pageTracker._trackPageview();
        }

    }
};
HaymarketAnalytics.googleAnalyticsObserver = function(instance, element, tokens, external, args) {

    instance.logToConsole("GoogleAnalytics: event tracking registerd " + tokens[2] + " : " + element.title + "::" + element.innerText + " " + instance.navigationMetrics.section.toLowerCase());
    jQuery(element).click(function(e) {
        try {
            args.allMetrics.relTokens = tokens;

            var keyValues = HaymarketAnalytics.getKeyValues(args.allMetrics, true);
            var filter = function(name, val, mix) { return !HaymarketAnalytics.isNotEmptyString(val); };
            var formatter = function(name, val, mix) { return HaymarketAnalytics.validChars(val); };

            var category = HaymarketAnalytics.MixValues(keyValues, args.cfg.category, filter, formatter).join('.');
            var action = HaymarketAnalytics.MixValues(keyValues, args.cfg.action, filter, formatter).join('.');
            var opt_label = HaymarketAnalytics.MixValues(keyValues, args.cfg.label, filter, formatter).join('.');

            args.pageTracker._trackEvent(category, action, opt_label);
            instance.logToConsole("GoogleAnalytics:trackable event catagory:" + category + ", action:" + action + ", opt_label:" + opt_label);

        } catch (err) { instance.logToConsole(err.message); }
    });
};

/* NOTE: jQuery required
Whereas omniture tracks external links automatically, in nedstat we must attach observers
Usage of ns_type equal to "clickout" or "hidden" will track the data in nedstat, but not cause it to be counted an a page impression. However, custom reports are required report on hits with an ns_type of "hidden".
*/
HaymarketAnalytics.prototype.observeLinks = function(instance, observer, args) {
    if ((typeof (jQuery) != "undefined")) {
        jQuery(document).ready(function() {
            jQuery("a[rel*='trackable_']").each(function(index, element) {
                var s = new String(element.rel);
                var results = s.match(/trackable_(.+)\s?/i);
                if (results != null && typeof (results[1]) != "undefined") {
                    var originalHref = element.href;
                    var tokens = [];
                    for (var v in results) { if (HaymarketAnalytics.isNotEmptyString(results[v])) { tokens.push(results[v].toString()); } }
                    var externalLink = s.match(/\s?external\s?/i);
                    var external = (externalLink != null && typeof (externalLink) != "undefined");
                    observer(instance, element, tokens, external, args);
                }
            })
        });
    }
};


HaymarketAnalytics.nedstatOvserver = function(instance, element, tokens, external, args) {
    if (tokens.length > 0) {
        var originalHref = element.href;
        var trackingToken = tokens[0];
        /* nedstat URL */
        var ns_l = instance.scheme + "://uk.sitestat.com/" + instance.siteConfig.nedstatSitestatAccount + "/s?";
        var ns_action = "view";
        var ns_type = "hidden"; /* so they don't count as page impressions */
        /* clickout or internal link tracking ? */
        var ns_event = (external) ? "clickout" : "link";
        var counter = args.zone + "." + trackingToken.toUpperCase() + "&ns_event=" + ns_event;
        /* bind onclick event */
        instance.logToConsole("trackable onclick(" + ns_event + ")enabled: " + originalHref + " => " + counter);

        jQuery(element).click(function(e) {
            try {
                HaymarketAnalytics.ns_onclick(instance, ns_l, element, originalHref, counter, ns_type);
            } catch (err) { }
        });
    }
}

HaymarketAnalytics.addGoogleAnalyticsCustomVar = function(instance, allMetrics, slot, setting) {
    var MAX_GA_VAR_NAME_LEN = 7;
    var MAX_GA_VAR_NAME_VAL_LEN = 32;

    var name = setting.name.substring(0, Math.min(MAX_GA_VAR_NAME_LEN, setting.name.length));
    var maxInMix = Math.floor((MAX_GA_VAR_NAME_VAL_LEN - name.length) / 2);
    var filter = function(name, val, mix) { return !(HaymarketAnalytics.isNotEmptyString(val) && mix.length < maxInMix); };
    var formatter = function(name, val, mix) { return HaymarketAnalytics.stripNonAlphanum(val); };

    /*build values with mix and match from all matrix 12 at max are treated*/
    var mix = HaymarketAnalytics.MixValues(allMetrics, setting.values, filter, formatter);

    /*Out of chosen max [12] calculate how many chars per value can be used in concatenation*/
    var maxLengthInMix = Math.ceil((MAX_GA_VAR_NAME_VAL_LEN - name.length - mix.length) / Math.max(mix.length, 1));

    for (var m in mix) { mix[m] = mix[m].substring(0, Math.min(maxLengthInMix, mix[m].length)); }
    var value = mix.join('.');

    if (HaymarketAnalytics.isNotEmptyString(value)) {
        value = value.substring(0, Math.min(value.length, MAX_GA_VAR_NAME_VAL_LEN - name.length));
        pageTracker._setCustomVar(slot, name, value, setting.scope);
        instance.debug.googleAnalyticsVarAdded(name, value, slot, maxLengthInMix, setting);
    }
};



/*Deprecated use createRenderAdvert() instead */
HaymarketAnalytics.prototype.getRenderAdvertSrc = function(dimensions, position, tile) {
    return this.createRenderAdvert(dimensions, position, tile).adurl;
}
HaymarketAnalytics.prototype.createRenderAdvert = function(dimensions, position, tile) {

    /* we need a count of ads rendered */
    if (typeof (HaymarketAnalytics.prototype.getRenderAdvertSrc.count) == "undefined") {
        HaymarketAnalytics.prototype.getRenderAdvertSrc.count = 0;
    }
    var dimensions = ((typeof (dimensions) == "undefined") || (dimensions == null)) ? "" : dimensions;
    var adserver = this.scheme + "://ad.uk.doubleclick.net/adj/";
    var site = this.siteConfig.dartSitename;
    var thePosition = (position) ? position : this.nextDARTPosition();
    var theTile = (tile) ? tile : this.nextDARTTile();
    var sitePostfix = "";
    if ((typeof (this.siteConfig.dartPromoteHierarchy1ToSubsite) != "undefined") && (this.siteConfig.dartPromoteHierarchy1ToSubsite == true)) {
        /* unlike the naming suggests, this actually promoted the "section" to subsite, and in other ares we ignore hierarchy[0] */
        site += (this.navigationMetrics.section != null) ? ("." + this.navigationMetrics.section.toLowerCase()) : "";
    }
    var zone = this.getAdvertZone();
    var customValues = this.getAdvertCustomValues();
    /* revenue sciences integration */
    var segQS = '';
    if (typeof (this.siteConfig.revenueSciencesAccount) != 'undefined') {
        segQS = this.getRevenueSciencesTag();
        this.logToConsole("RS TAGS: " + segQS);
    }
    /* ad special k-v to first ad, allowing injection of Overlays by DART */
    if (HaymarketAnalytics.prototype.getRenderAdvertSrc.count == 0) {
        customValues += 'dcopt=ist;';
    }
    /* site+/+zone must be <= 64 characters */
    var siteandZone = (site + '/' + zone).substring(0, 64);
    var src = adserver + siteandZone + ';' + segQS + customValues + 'pos=' + thePosition + ';sz=' + dimensions + ';tile=' + theTile + ';ord=' + this.randomNumber;

    HaymarketAnalytics.prototype.getRenderAdvertSrc.count++;
    this.logToConsole("getRenderAdvertSrc.count=" + HaymarketAnalytics.prototype.getRenderAdvertSrc.count);
    /*the object returned is liable to change only src and few standard elements are guaranteed to stay*/
    return { adurl: src, adserver: adserver, siteandZone: siteandZone, custom: segQS + customValues, sz: dimensions, pos: thePosition, tile: theTile, ord: this.randomNumber };
};

HaymarketAnalytics.prototype.renderAdvert = function(dimensions, position, tile) {
    var rendered = this.createRenderAdvert(dimensions, position, tile);
    document.write('<SCR');
    document.write('IPT SRC="' + rendered.adurl + '">');
    document.write('</SCR');
    document.write('IPT>');
    this.debug.adRendered(dimensions, position, tile, rendered);
};

HaymarketAnalytics.prototype.getRenderAdvertString = function(dimensions, position, tile) {
    var adurl = this.createRenderAdvert(dimensions, position, tile).adurl;
    var string = "<script type=\"text\/javascript\" src=\"" + adurl + "\"></script>";
    return string;
};

/* generate DART specific custom kv pairs */
HaymarketAnalytics.prototype.getAdvertCustomValues = function() {
    var customValues = "";
    var allMetrics = this.getAllMetricsKeyValue();

    var productSlug = ""; /* a more complete product identifier */
    var contentSlug = ""; /* a "tag" version of the sentence-like title */
    var prodSep = "";
    var contentSep = "";

    var allowedInAd = new Array("product_dealer", "product_brand", "product_model", "product_edition", "product_format", "product_style", "product_size"
		  , "content_id", "content_type", "content_group", "content_title", "content_subtitle"
		  , "location_country", "location_region", "location_subregion", "team_.*", "affiliate_.*");

    var formatter = function(name, val, mix) { return HaymarketAnalytics.encodeURIComponent(HaymarketAnalytics.stripNonAlphanum(val, "-").substring(0, 55).toLowerCase()); };
    var dartMetric = HaymarketAnalytics.MixValues(allMetrics, allowedInAd, function(name, val, mix) { return !(HaymarketAnalytics.isNotEmptyString(val)); }, formatter, true);
    for (var i in dartMetric) {
        if (HaymarketAnalytics.isNotEmptyString(dartMetric[i])) {
            /* we want product_model to read p_model to comply with DART requirements plus restrict length */
            var newCustomKey = i.replace(/(.){1}.+_/gi, "$1_").substring(0, 12);
            var newCustomValue = dartMetric[i];

            customValues += (newCustomKey + "=" + newCustomValue + ";");
            if (i == "product_brand" || i == "product_model" || i == "product_edition") {
                productSlug += (prodSep + newCustomValue);
                prodSep = ".";
            }

            if (i == "content_title") {
                contentSlug += (contentSep + newCustomValue);
                contentSep = ".";
            }
        }
    }
    if (productSlug.length > 0) {
        customValues += ("p_slug=" + productSlug.toLowerCase().substring(0, 55) + ";");
    }
    if (contentSlug.length > 0) {
        customValues += ("c_slug=" + contentSlug.toLowerCase().substring(0, 55) + ";");
    }
    return customValues;
};

/* allow run-time override of one length (i.e. customise by section/page) 
this must be called before clals to renderAdvert() to have effect
*/
HaymarketAnalytics.prototype.setMaxItemsInAdvertZone = function(length) { this.siteConfig.dartMaxItemsInZone = length; };

/* generate DART specific ZONE name - customiseable per config */
HaymarketAnalytics.prototype.getAdvertZone = function() {
    var cfg = this.siteConfig;
    var zone = "";
    if (typeof (cfg.getCustomAdvertZone) == "function") {
        this.logToConsole("Custom advert zone created (cfg.getCustomAdvertZone)");
        zone = cfg.getCustomAdvertZone(this);
    }
    else {
        var affiliateCode = this.affiliateMetrics['code'];
        if (typeof (affiliateCode) == "string" && affiliateCode.length > 0) {
            zone = affiliateCode.toLowerCase();
        }
        else {
            /* by defult we take the first portion of the hierarchy, but by config the first portion can be promoted to sub-site - assues that navigationMetrics.section === hierarchy[0] */
            /* if we promote zone to subsite, we start index a little higher */
            var i = HaymarketAnalytics.validOf(cfg.dartPromoteHierarchy1ToSubsite, false) ? 1 : 0;
            /* now begin zone logic */
            var maxSegemnts = i + HaymarketAnalytics.validOf(cfg.dartMaxItemsInZone, 5);

            var zoneBase = [];
            for (j = i; j < Math.min(maxSegemnts, this.navigationMetrics.hierarchy.length); j = j + 1) {
                if (HaymarketAnalytics.isNotEmptyString(this.navigationMetrics.hierarchy[j])) {
                    zoneBase.push(HaymarketAnalytics.stripNonAlphanum(this.navigationMetrics.hierarchy[j]));
                }
            }
            /* append certain product metrics */
            var productMetrics = this.getAllMetricsKeyValue("product");
            for (var p in productMetrics) {
                if (
                (p == "product_brand" ||
                (p == "product_model" && HaymarketAnalytics.validOf(cfg.dartIncludeProductModelInZone, false))
                 ) && HaymarketAnalytics.isNotEmptyString(productMetrics[p])) {
                    zoneBase.push(HaymarketAnalytics.stripNonAlphanum(productMetrics[p]));
                }
            }
            zone = zoneBase.join('.').toLowerCase();
        }
    }
    return zone;
};



HaymarketAnalytics.prototype.nextDARTPosition = function() {
    HaymarketAnalytics.currentDartPosition = HaymarketAnalytics.validOf(HaymarketAnalytics.currentDartPosition, 0);
    return ++HaymarketAnalytics.currentDartPosition;
};

HaymarketAnalytics.prototype.nextDARTTile = function() {
    HaymarketAnalytics.currentDartTile = HaymarketAnalytics.validOf(HaymarketAnalytics.currentDartTile, 0);
    return ++HaymarketAnalytics.currentDartTile;
};


/**
* Returns a full-stop deliniated list of RS segment IDs as set in "setRevenueSciencesCollectionCookies"
**/
HaymarketAnalytics.prototype.getRevenueSciencesTag = function() {
    var rsi_segs = [];
    var segs_beg = document.cookie.indexOf('rsi_segs=');
    if (segs_beg >= 0) {
        segs_beg = document.cookie.indexOf('=', segs_beg) + 1;
        if (segs_beg > 0) {
            var segs_end = document.cookie.indexOf(';', segs_beg);
            if (segs_end == -1) { segs_end = document.cookie.length };
            rsi_segs = document.cookie.substring(segs_beg, segs_end).split('|');
        }
    }
    var segLen = 20;
    var segQS = "";
    if (rsi_segs.length < segLen) { segLen = rsi_segs.length }
    for (var i = 0; i < segLen; i++) {
        segQS += ("rsi" + "=" + rsi_segs[i] + ";")
    }
    return segQS;
};

HaymarketAnalytics.prototype.setRSCookies = function() {
    var csid = this.siteConfig.revenueSciencesAccount;
    var pixellingCode = this.scheme + '://js.revsci.net/gateway/gw.js?csid=' + csid;
    this.includeJavascript(pixellingCode, this.setRSCookiesCallback, this);
};

/**
* This code is directly from RS - its sets a cookie of segments per user/domain
**/
HaymarketAnalytics.prototype.setRSCookiesCallback = function(thisInstance) {

    var RsObject = window[thisInstance.siteConfig.revenueSciencesAccount];
    if (typeof (RsObject) == "object" && typeof (RsObject.DM_tag) == "function") {
        /* customise */
        var zone = thisInstance.getGenerateNavigationZone();
        if (zone.length > 0) {
            RsObject.DM_cat(zone);
            thisInstance.logToConsole("Revenue Sciences:  DM_cat(" + zone + ");");
        }
        /* init - must happen last */
        RsObject.DM_tag();
    }
    else {
        thisInstance.logToConsole("Revenue Sciences Integration failed");
    }

};

/**
* ClickDensity
**/
HaymarketAnalytics.prototype.renderClickDensity = function() {
    var path = unescape(this.scheme + '://j.clickdensity.com/cr.js');
    this.includeJavascript(path, this.renderClickDensityCallback, this);
};

HaymarketAnalytics.prototype.renderClickDensityCallback = function(thisInstance) {
    window.clickdensity_siteID = thisInstance.siteConfig.clickdensitySiteID;
    window.clickdensity_keyElement = 'kvalue';
};

/**
* UTILITIES
**/
HaymarketAnalytics.prototype.includeJavascript = function(script_filename, callback, thisInstance) {
    return (typeof (jQuery) != "undefined") ? this.includeJavascriptViaJquery(script_filename, callback, thisInstance) : this.includeJavascriptViaDomInsertion(script_filename, callback, thisInstance);
};

HaymarketAnalytics.prototype.includeJavascriptViaJquery = function(script_filename, callback, thisInstance) {
    if (typeof (script_filename) == "string") {
        jQuery(document).ready(function() {

            thisInstance.logToConsole("getScript(jQuery): " + script_filename);

            jQuery.getScript(script_filename, function() {
                thisInstance.logToConsole("callback: " + script_filename);
                callback(thisInstance);
            });
        });
    }
};


HaymarketAnalytics.prototype.includeJavascriptViaDomInsertion = function(script_filename, callback, thisInstance) {
    if (typeof (script_filename) == "string") {
        var html_doc = document.getElementsByTagName('head').item(0);
        if (typeof (html_doc) != 'undefined') {
            var js = document.createElement('script');
            js.setAttribute('language', 'javascript');
            js.setAttribute('type', 'text/javascript');
            js.setAttribute('src', script_filename);
            html_doc.appendChild(js);

            thisInstance.logToConsole("getScript(dom): " + script_filename);

            /* js.onerror = function(message) { alert(message); }; */
            js.onreadystatechange = function() {
                if (js.readyState == 'complete' || js.readyState == 'loaded') {
                    thisInstance.logToConsole("callback: " + script_filename);
                    callback(thisInstance);
                }
            }
            js.onload = function() {
                thisInstance.logToConsole("callback: " + script_filename);
                callback(thisInstance);
            }
        }
    }
};



HaymarketAnalytics.getUrlPathToZone = function() {
    var splitPath = window.location.pathname.split("/");
    splitPath.shift();
    splitPath.pop();
    return splitPath.join(".").toLowerCase();
};


HaymarketAnalytics.stripNonAlphanum = function(text, replacement) {
    var replacement = (replacement == null) ? "" : replacement;
    return (new String(HaymarketAnalytics.trim(text))).replace(/[^a-zA-Z0-9_-]+/gi, replacement);
};

/* encode safe for URI, except prefer + over %20 */
HaymarketAnalytics.encodeURIComponent = function(text, altSpaceChar) {
    var replacement = (altSpaceChar == null) ? "+" : altSpaceChar;
    text = encodeURIComponent(text);
    return text.replace(/%20/g, replacement);
};

/* helper method if sites want to use it - requires jQuery */
HaymarketAnalytics.prototype.safelyMoveAdvert = function(fromElement, toElement) {
    toElementObject = $(document.getElementById(toElement));
    fromElementObject = $(document.getElementById(fromElement));
    if (toElementObject.size() && fromElementObject.size()) {
        /* remove scripts injected by this class, so ads don't duplicate in IE */
        fromElementObject.find('script').each(function(i) { $(this).remove(); });
        /* use document.getElementById to avoid jQuery "cleverness" in cloning whilst moving */
        setTimeout(function() {
            var element = document.getElementById(fromElement);
            document.getElementById(toElement).appendChild(element);
        }, 50);
        toElementObject.show();
        fromElementObject.show();
    }
    this.debug.adMoved(fromElement, toElement);
};


HaymarketAnalytics.mergeValues = function(existing, newvalues) { for (i in newvalues) { existing[i] = newvalues[i]; } return existing; };
HaymarketAnalytics.random = function() { return Math.round(Math.random() * 10000000000); };
/* STRIP: # , * . ( ) = + < > [ ] -  i.e. non ascii */
HaymarketAnalytics.validChars = function(text) { return text.replace(/[^\x00-\x7F]+/gi, ""); };
HaymarketAnalytics.trim = function(str) { return str.replace(/^\s+|\s+$/g, ''); };
HaymarketAnalytics.itemInArray = function(item, arr) { for (a in arr) { if (arr[a] == item) { return true; } } return false; };

HaymarketAnalytics.prototype.logToConsole = function(text) { this.debug.logToConsole(text); };

HaymarketAnalytics.queryString = function(parameter) {
    var regex = new RegExp("[?&]" + escape(unescape(parameter)) + "(?:=([^&]*))?", "i");
    var match = regex.exec(window.location.href);
    return (match != null) ? value = match[1] : null;
};
HaymarketAnalytics.setCookie = function(name, value, days) {
    var date = new Date();
    date.setHours(24 * days);
    document.cookie = name + "=" + escape(unescape(escape(value))) + ("; expires=" + date.toUTCString());
};
HaymarketAnalytics.getCookie = function(name) {
    if (document.cookie.length > 0) {
        var c_start = document.cookie.indexOf(name + "=");
        if (c_start != -1) {
            c_start = c_start + name.length + 1;
            var c_end = document.cookie.indexOf(";", c_start);
            if (c_end == -1) c_end = document.cookie.length;
            return unescape(document.cookie.substring(c_start, c_end));
        }
    }
    return null;
};
HaymarketAnalytics.MixValues = function(collection, names, filter, formatter, named) {

    named = HaymarketAnalytics.validOf(named, false);
    filter = HaymarketAnalytics.validOf(filter, function(name, value, mix) { return false; });
    formatter = HaymarketAnalytics.validOf(formatter, function(name, value, mix) { return value; });

    var mix = named ? {} : new Array();
    var locatePartial = function(name) {
        var values = {};
        if (name != null && name.indexOf('*', 0) >= 0) {
            var rgx = new RegExp(name);
            for (var v in collection) {
                var value = collection[v];
                if (value != null && rgx.test(v)) {
                    values[v] = value;
                }
            }
        }
        return values;
    };

    var pushItem = function(name, value) {
        if (!filter(name, value, mix)) {
            value = formatter(name, value, mix);
            if (!filter(name, value, mix)) {
                if (named) { mix[name] = value; } else { mix.push(value); }
            }
        }
    };

    for (var v in names) {/*build mix with mix and match of names in collection*/
        if (HaymarketAnalytics.isNotEmptyString(names[v])) {
            var isConst = names[v].indexOf("const:", 0) == 0;
            var value = isConst ? names[v].substring(6) : collection[names[v]];
            if (!isConst && !HaymarketAnalytics.isNotEmptyString(value)) {
                var values = locatePartial(names[v]);
                for (var j in values) {
                    pushItem(j, values[j]);
                }
            } else {
                pushItem(names[v], value);
            }
        }
    }
    return mix;
};


HaymarketAnalytics.getKeyValues = function(obj, recursive, nameprefix, collection) {

    nameprefix = HaymarketAnalytics.validOf(nameprefix, '');
    recursive = HaymarketAnalytics.validOf(recursive, false);

    collection = typeof (collection) == 'undefined' ? { inner: {}, processed: [] }
    : typeof (collection.processed) == 'undefined' ? { inner: collection, processed: []} : collection;

    if (!HaymarketAnalytics.itemInArray(obj, collection.processed) && typeof (obj) == 'object') {

        collection.processed[collection.processed.length] = obj;

        for (var i in obj) {

            var _type = typeof (obj[i]);
            if (_type == 'object' && recursive) {
                HaymarketAnalytics.getKeyValues(obj[i], recursive, nameprefix + i + '_', collection);
            } else if (_type == 'string' || _type == 'number' || _type == 'boolean') {
                collection.inner[nameprefix + i] = obj[i];
            }
        }

    }
    return collection.inner;
}

HaymarketAnalytics.isNotEmptyString = function(str) { return (str != null && typeof (str) == 'string' && str != ''); }
HaymarketAnalytics.validOf = function(value, def) { return HaymarketAnalytics.sameType(value, def) ? value : def; }
HaymarketAnalytics.sameType = function(value, def) { return typeof (value) == typeof (def); }

function HaymarketAnalyticsDebug(enabled) {
    this.enabled = enabled;
    this.renderedAds = new Array();
    this.movedAds = new Array();
    this.googleVariables = new Array();
};
HaymarketAnalyticsDebug.prototype.adMoved = function(fromElement, toElement) {
    this.movedAds.push({ fromElement: fromElement, toElement: toElement });
};
HaymarketAnalyticsDebug.prototype.adRendered = function(dimensions, position, tile, rendered) {
    this.renderedAds.push({ dimensions: dimensions, position: position, tile: tile, rendered: rendered });
};
HaymarketAnalyticsDebug.prototype.googleAnalyticsVarAdded = function(name, value, slot, maxLengthInMix, setting) {
    this.googleVariables.push({ name: name, value: value, maxLengthInMix: maxLengthInMix, setting: setting });
    this.logToConsole("Google Analytics CustomVar " + name + ":" + value + " slot:" + slot + " scope:" + setting.scope + " maxLengthInMix:" + maxLengthInMix);
};
HaymarketAnalyticsDebug.prototype.logToConsole = function(text) {
    if (this.enabled && window.console && window.console.log) { console.log(text); } // else { alert(text); }
};
HaymarketAnalyticsDebug.prototype.getRenderedAds = function() { return this.renderedAds; }
HaymarketAnalyticsDebug.prototype.getMovedAds = function() { return this.movedAds; };
