var clickdensity;
var clickdensityCommon;

function ClickDensityCommon()
{
    this.clickdensityVersion = 2010012501;
    this.pageId = null;
    this.SessionID = null;
    this.siteId = null;

    this.rSite = 'r.clickdensity.com'; // DO NOT alter this
    
    this.recorderUrl = (window.location.protocol === "https:" ? 'https://' : 'http://')
        + this.rSite + "/Default.aspx?version="+this.clickdensityVersion+"&";
        
    this.loadScript = function(url)
    {
        // Don't send anything unless the page is being viewed as a web page
        if ((window.location.protocol == "http:") || (window.location.protocol == "https:"))
        {
            var scriptTag = document.createElement("script");
            scriptTag.setAttribute("type", "text/javascript");
            scriptTag.setAttribute("src", url);
            
            document.getElementsByTagName("head")[0].appendChild(scriptTag);
            return true;
        }
        else
        {
            return false;
        }
    };
    
    this.getCookie = function(sName)
    {
        var aCookie = document.cookie.split("; ");
        for (var i=0;i<aCookie.length;i++)
        {
            var aCrumb = aCookie[i].split("=");
            if (sName == aCrumb[0]) {
                return aCrumb[1];
            }
        }
        return null;
    };
    
    this.createSession = function()
    {
        var referrerPath = '';
        var referrerQS = '';
    
        try {
            var referrer = document.referrer.split('?');
            referrerPath = referrer[0];
            referrerQS = referrer[1] == 'undefined' ? null : referrer[1];
        } catch(error) {}
        
        this.fontSize = this.getFontSize();
        var paramString = 'siteID=' + this.siteId + '&srx=' + window.screen.width + '&sry=' + window.screen.height + '&pageHost=' + window.location.hostname + '&referrerPath=' + referrerPath + '&referrerQS=' + referrerQS;
        
        if (this.pID == null) {
            paramString += '&page=' + encodeURIComponent(window.location.pathname+window.location.search);
        } else {
            paramString += '&pageID=' + this.pageId;
        }
        
        if (!isNaN(this.fontSize)) {
            paramString += '&fs=' + this.fontSize;
        }

        var _self = this;
        this.sendData(paramString,
            function(responseText) {
                var aSessionIdPageId = responseText.split(",");
                if (aSessionIdPageId.length === 2) {
                    _self.setSession(aSessionIdPageId[0], aSessionIdPageId[1]);
                }
            }
        );
        
        
    };
    
    /**
     * Returns the font size in use by the UA.
     *
     * @return      the font size
     */
    this.getFontSize = function()
    {
        var bodyEl = document.body;
        var docEl;
    
        if (typeof bodyEl.document == 'undefined') {
            docEl = bodyEl.ownerDocument;
        } else {
            docEl = bodyEl.document;
        }
        
        var divTmp = docEl.createElement("div");
        divTmp.style.visibility="hidden";
        divTmp.style.position="absolute";
        divTmp.style.left="0px";
        divTmp.style.top="0px";
        divTmp.style.height="1px";
        divTmp.style.width="10em";
        divTmp.style.margin="0px";
        divTmp.style.padding="0px";
        bodyEl.appendChild(divTmp);
        var divWidth = parseInt(divTmp.offsetWidth / 10, 10);
        bodyEl.removeChild(divTmp);

        return divWidth;
    };
            
    this.sendData = function(paramString)
	{
	    return this.loadScript(this.recorderUrl+paramString);
	};
        
    this.setSession = function(sessID, pgID)
    {
        this.SessionID = sessID;
        document.cookie = "CDSessionID="+sessID+"; ";
        
        if (this.pageId === null) {
            this.pageId = pgID;
        }
                        
        if (this.SessionID > 0) {
            clickdensity = new ClickDensityRecorder(clickdensity_keyElement, clickdensity_siteID);
        }
        
        return null;
    };
}

function ClickDensityRecorder(kE, sI)
{
    /*************************************************************************/
    /*          IVARS                                                        */
    /*************************************************************************/
    
    this.clickdensityVersion = 2008101001;
    
    // init stuff   
    this.bIsIe = document.all ? true : false;
    this.uaIsDom = document.getElementById ? true : false;
    this.uaIsNqm = (document.documentElement &&
                    document.documentElement.clientHeight > 0) ? true : false;
    this.uaIsSaf = (navigator.appVersion.indexOf("Safari") != -1);
    
    // internal counters
    this.pageLoadTime = new Date().getTime();
    this.iSeq = 0;
    
    this.body = null;
    this.divOuter = null;  // Div used to get the outerHTML of other elements.    
    
    // instance stuff
    this.keyElement = kE;
    this.siteID = sI;
    
    
    /************************************************************************/
    /*      NOTHING INTERESTING BELOW THIS POINT - MOVE ALONG, PLEASE       */
    /************************************************************************/
    
    /**
     * Init function.
     */
    this.init = function ()
    {    
        this.body = document.getElementsByTagName("body").item(0);
            
        if (clickdensityCommon.SessionID === null) {
            clickdensityCommon.createSession();
        }
        else {
              this.logPageLoad(); // TODO #pl# should this be removed?
        }
        
        this.divOuter = document.createElement("div");
        this.divOuter.style.display="none";
        this.divOuter.style.position="absolute";
        this.body.appendChild(this.divOuter);
        
        // set up event handlers
        var oldMouseDown = document.onmousedown;
        var tmp = this;
        if (typeof oldMouseDown != 'function') {
            document.onmousedown = function (ev) { tmp.record(ev); };
        }
        else {
            document.onmousedown = function (ev) {
                oldMouseDown(ev);
                tmp.record(ev);
            };
        }

        return null;
    };
    
    /**
     * Click recording method. Now for every UA.
     *
     * @param       ev      the event, or null in IE
     */
    this.record = function (ev)
    {
        try {
            if (clickdensityCommon.SessionID === null || clickdensityCommon.SessionID == -1) {
                return true;
            }
            
            var theButton = null, docEl = null, refEl = null;
            if (this.bIsIe)
            {
                ev = window.event;
                theButton = ev.button;
                docEl = this.uaIsNqm ? document.documentElement : document.body;
                refEl = docEl;
            }
            else
            {
                theButton = ev.which;
                docEl = document.body;
                refEl = this.uaIsNqm ? docEl.parentNode : docEl;
            }
        
            if (theButton != 1) {
                return true;
            }
        
            var aClickData = [];
        
            aClickData.cx = ev.clientX;
            aClickData.cy = ev.clientY;
            aClickData.wx = ((typeof docEl.clientWidth == 'undefined') || this.uaIsSaf)
                                    ? window.innerWidth 
                                    : refEl.clientWidth;
            aClickData.wy = ((typeof docEl.clientHeight == 'undefined') || this.uaIsSaf)
                                    ? window.innerHeight 
                                    : refEl.clientHeight;
            
            if ((aClickData.cx > aClickData.wx) ||
                (aClickData.cy > aClickData.wy)) {
                return true;
            }
            
            if (this.bIsIe)
            {
                aClickData.cx += docEl.scrollLeft;
                aClickData.cy += docEl.scrollTop;
                aClickData.sx = docEl.scrollLeft;
                aClickData.sy = docEl.scrollTop;
            }
            else
            {
                aClickData.cx += window.pageXOffset;
                aClickData.cy += window.pageYOffset;
                aClickData.sx = window.pageXOffset;
                aClickData.sy = window.pageYOffset;
            }
        
            aClickData.dt = (new Date().getTime() - this.pageLoadTime)/1000;
        
            if (this.keyElement === null)
            {
                aClickData.kx = 0;
                aClickData.ky = 0;
            }
            else
            {
                var aKeyOffset = 
                    this.getOffset(document.getElementById(this.keyElement), true);
                aClickData.kx = aKeyOffset.x;
                aClickData.ky = aKeyOffset.y;
                aClickData.cx -= aKeyOffset.x;
                aClickData.cy -= aKeyOffset.y;
            }
            
            aClickData.cn = this.iSeq++;
        
            var targetEl = this.bIsIe  ? ev.srcElement : ev.target;
            var aTargetPos = this.getOffset(targetEl, false);
            
            aClickData.tn = "";
            var el = targetEl;
            
            switch (el.nodeName.toLowerCase())
            {
                case "a":
                case "input":
                case "button":
                case "textarea":
                case "select":
                case "object":
                case "embed":
                    aClickData.tn = this.hex_md5(this.getTargetName(el));
                    aClickData.tt = el.nodeName.toLowerCase();
                    break;
                default:
                    if ((typeof el.hasAttribute != "undefined" && el.hasAttribute("onclick")) || el.onclick != null)
                    {
                        aClickData.tn = this.hex_md5(this.getTargetName(el));
                        aClickData.tt = el.nodeName.toLowerCase();
                    }
                    else
                    {
                        var clickableElement = this.getClickableParent(el);
                        if (clickableElement != null)
                        {
                            aClickData.tn = this.hex_md5(this.getTargetName(clickableElement));
                            aClickData.tt = clickableElement.nodeName.toLowerCase();
                        }
                    }
                    break;
            }
            
            aClickData.tx = aTargetPos.x;
            aClickData.ty = aTargetPos.y;
            
            aClickData.siteID = this.siteID;
            aClickData.sID = clickdensityCommon.SessionID;
            
            // Want to send pageID, but if clickDensity_logPageLoad has not returned, send page.
            if (clickdensityCommon.pageId != null) {
                aClickData.pageID = clickdensityCommon.pageId;
            }
            else {
                aClickData.page = window.location.pathname+window.location.search;
            }
            
            var paramString = '';
            
            // build the query string, ignoring parameters that we did not set to deal with Prototype Arrays.
            var parm;
            for (parm in aClickData) {
                if (parm.length == 2 ||
                    parm == 'siteID' ||
                    parm == 'sID' ||
                    parm == 'pageID' ||
                    parm == 'page' ||
                    parm == 'abid' ||
                    parm == 'abm') {
                    paramString += parm+"="+encodeURIComponent(aClickData[parm])+"&";
                }
            }
            clickdensityCommon.sendData(paramString);
            
            return true;
        }
        catch (e)
        {}
    };
    
    this.getClickableParent = function (el)
    {
        var parentEl = el.parentNode;
        if (parentEl === null || parentEl == document) {
            return null;
        }
        if (parentEl.nodeName.toLowerCase() == "a" ||
          (typeof parentEl.hasAttribute != "undefined" && parentEl.hasAttribute("onclick")) ||
          parentEl.onclick != null) {
            return parentEl;
        }
        return this.getClickableParent(parentEl);
    };
    
    this.trim = function(str)
    {
        while (str.substring(0,1) === '') {
            str = str.substring(1, str.length);
        }
        while (str.substring(str.length-1, str.length) === '') {
            str = str.substring(0, str.length-1);
        }
        return str;
    };
    
    /*
     *
     * Generate the element's identification string
     * 
     * This must match getTargetName in Recording.js
     *
     */
    this.getTargetName = function (el, recurse)
    {
        if (typeof(recurse) == 'undefined') {
            recurse = 3;
        }
        
        var tmpEl = el.cloneNode(true);
        this.divOuter.appendChild(tmpEl);
        var outer = this.trim(this.divOuter.innerHTML);
        this.divOuter.innerHTML = "";
    
        var result = outer.match(/^<\w*/)[0].toLowerCase();
        
        var openingTag = outer.match(/^<([\w\d]+)(.*?)>/)[0];
        
        openingTag = openingTag.slice(0,openingTag.length - 1);
        
        var attribs = openingTag.match(/([\w\d\-]+)=(('(.*?)')|("(.*?)")|(\S+))/g);
        
        var i;
        if (attribs != null)
        {
            attribs.sort();
            
            
            for (i = 0; i < attribs.length; i++)
            {
                var attrib = attribs[i].replace(/"/g, '');
                
                // IE and ffox show some values in different case...
                attrib = attrib.toLowerCase();
                
                // We remove onclick in reporting...
                if (attrib.substring(7,0) == 'onclick') {
                    continue;
                }
                
                // IE changes the style attribute from what is in the html...
                if (attrib.substring(5,0) == "style") {
                    continue;
                }
                    
                // catch for sites using hbx - this puts an 'lid=' attribute into the tag which
                // causes Issues
                if (attrib.substring(4,0) == "lid=") {
                    continue;
                }
                
                // Nasty hack to get ffox to dump these values the same as IE does... :/
                if (el.nodeName.toLowerCase() == 'input' && (attrib == 'type=text' || attrib == 'value=')) {
                    continue;
                }
                
                result += " " + attrib;
            }
        }
        
        result = result + ">";
    
        for (i = 0; i < el.childNodes.length; i++)
        {
            if (result.length >= 200)
            {
                break;
            }
            var nodeName = el.childNodes[i].nodeName.toLowerCase();
            if (nodeName == '#text' || nodeName == '#comment' || nodeName == 'option') {
                result += this.getTagContent(el.childNodes[i]);
            }
            else if (recurse > 0) {
                recurse -= 1;
                result += this.getTargetName(el.childNodes[i], recurse);
            }
        }
        
        return (result.length > 200) ? result.substring(0,200) : result;
    };
    
    /*
     * Logs a page load event
     *
     */
    this.logPageLoad = function()
    {
        if (clickdensityCommon.SessionID === null || clickdensityCommon.SessionID == -1) {
            return false;
        }
        
        var paramString = 'sID=' + clickdensityCommon.SessionID + '&siteID=' + this.siteID + '&pageLoad=true' + '&pageHost=' + window.location.hostname + '&lpageID=' + clickdensityCommon.getCookie("CDLASTPAGEID");
        if (clickdensityCommon.pageId === null) {
            paramString += '&page=' + encodeURIComponent(window.location.pathname+window.location.search);
        }
        else {
            paramString += '&pageID=' + clickdensityCommon.pageId;
        }
        
        clickdensityCommon.sendData(paramString,
            function(responseText) {
                var aPageIds = responseText.split(",");
                if (aPageIds.length === 2) {
                    clickdensity.setPage(aPageIds[0]);
                    clickdensity.setLastPage(aPageIds[1]);
                }
            }
        );
    };
    
    /**
     * Returns the x- and y-coordinate of a given element.
     *
     * @param       el      the element
     * @param       bUseBody    true to use the body if el is null
     * @return      an array containing two elements.
     */
    this.getOffset = function(el, bUseBody)
    {
       var aReturn = [];
        aReturn.x = 0;
        aReturn.y = 0;
        
        if (null === el)
        {
            if (!bUseBody) {
                return aReturn;
            }
                
            var tmp = document.getElementsByTagName("body");
            if (tmp.length === 0) {
                return aReturn;
            }

            el = tmp[0];
        }
        
        // cleverness here, because A tags that contain IMG tags are not useful...
        if (el.nodeName.toLowerCase() === "a" && el.innerHTML.match(/<img/i)) {
            el = el.getElementsByTagName("img")[0];
        }
        
        while (el != null)
        {
            if (isNaN(el.offsetLeft) && isNaN(el.offsetTop)) {
                break;
            }
            else if (isNaN(el.offsetLeft)) {
                aReturn.y += el.offsetTop;
            }
            else if (isNaN(el.offsetTop)) {
                aReturn.y += el.offsetTop;
            }
            else {
                aReturn.x += el.offsetLeft;
                aReturn.y += el.offsetTop;
            }
            el = el.offsetParent;
        }
        
        return aReturn;
    };
    
    this.setLastPage = function(pgID)
    {
        document.cookie = "CDLASTPAGEID=" + pgID + ";";
    };
        
    /*
     * Callback function called from JSON
     *
     * @param       pID     the page ID
     */
    this.setPage = function(pgID)
    {
        clickdensityCommon.pageId = pgID;
        return null;
    };
    
    /**
     * This must match getTagContent in Reporting.js
     *
     * Companion function to the above - returns the textual content of a tag. It's a bit
     * like calling el.innerText only it works in all browsers.
     *
     * @param   oEl the element to get the content of
     * @return  the content of oEl.
     */
    this.getTagContent = function(oEl)
    {
        var ret = (oEl.nodeType == 3) ? oEl.nodeValue.replace(/(\n|\r)+/gm, " ") : "";
        
        var oChild = oEl.firstChild;
        while (oChild != null)
        {
            ret += " "+this.getTagContent(oChild);
            oChild = oChild.nextSibling;    
        }
        
        // runs of whitespace
        ret = ret.replace(/\s+/g, " ");
        // leading and trailing whitespace
        ret = ret.replace(/(^\s+|\s+$)/mg, "");
        
        // return
        return ret;
    };
    
    /**
     * Server-side control function that stops recording data.
     */
    this.stopRecording = function()
    {
        return clickdensityCommon.setSession(-1);
    };
        
    /*
     * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
     * Digest Algorithm, as defined in RFC 1321.
     * Version 2.1 Copyright (C) Paul Johnston 1999 - 2002.
     * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
     * Distributed under the BSD License
     * See http://pajhome.org.uk/crypt/md5 for more info.
     */
    
    /*
     * Configurable variables. You may need to tweak these to be compatible with
     * the server-side, but the defaults work in most cases.
     */
    this.hexcase = 0;  /* hex output format. 0 - lowercase; 1 - uppercase        */
    this.b64pad  = ""; /* base-64 pad character. "=" for strict RFC compliance   */
    this.chrsz   = 8;  /* bits per input character. 8 - ASCII; 16 - Unicode      */
    
    /*
     * These are the functions you'll usually want to call
     * They take string arguments and return either hex or base-64 encoded strings
     */
    this.hex_md5 = function(s) 
    {
        if(!clickdensity_hoverMapRecording) {
            return '';
        }
        return this.binl2hex(this.core_md5(this.str2binl(s),s.length * this.chrsz));
    };
    
    // unused
    /*this.hex_hmac_md5 = function(key, data) 
    { 
        return this.binl2hex(this.core_hmac_md5(key, data));
    }*/
    
    /*
     * Calculate the MD5 of an array of little-endian words, and a bit length
     */
    this.core_md5 = function (x, len)
    {
      /* append padding */
      x[len >> 5] |= 0x80 << ((len) % 32);
      x[(((len + 64) >>> 9) << 4) + 14] = len;
    
      var a =  1732584193;
      var b = -271733879;
      var c = -1732584194;
      var d =  271733878;
    
      for(var i = 0; i < x.length; i += 16)
      {
        var olda = a;
        var oldb = b;
        var oldc = c;
        var oldd = d;
    
        a = this.md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
        d = this.md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
        c = this.md5_ff(c, d, a, b, x[i+ 2], 17,  606105819);
        b = this.md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
        a = this.md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
        d = this.md5_ff(d, a, b, c, x[i+ 5], 12,  1200080426);
        c = this.md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
        b = this.md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
        a = this.md5_ff(a, b, c, d, x[i+ 8], 7 ,  1770035416);
        d = this.md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
        c = this.md5_ff(c, d, a, b, x[i+10], 17, -42063);
        b = this.md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
        a = this.md5_ff(a, b, c, d, x[i+12], 7 ,  1804603682);
        d = this.md5_ff(d, a, b, c, x[i+13], 12, -40341101);
        c = this.md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
        b = this.md5_ff(b, c, d, a, x[i+15], 22,  1236535329);

        a = this.md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
        d = this.md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
        c = this.md5_gg(c, d, a, b, x[i+11], 14,  643717713);
        b = this.md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
        a = this.md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
        d = this.md5_gg(d, a, b, c, x[i+10], 9 ,  38016083);
        c = this.md5_gg(c, d, a, b, x[i+15], 14, -660478335);
        b = this.md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
        a = this.md5_gg(a, b, c, d, x[i+ 9], 5 ,  568446438);
        d = this.md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
        c = this.md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
        b = this.md5_gg(b, c, d, a, x[i+ 8], 20,  1163531501);
        a = this.md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
        d = this.md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
        c = this.md5_gg(c, d, a, b, x[i+ 7], 14,  1735328473);
        b = this.md5_gg(b, c, d, a, x[i+12], 20, -1926607734);

        a = this.md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
        d = this.md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
        c = this.md5_hh(c, d, a, b, x[i+11], 16,  1839030562);
        b = this.md5_hh(b, c, d, a, x[i+14], 23, -35309556);
        a = this.md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
        d = this.md5_hh(d, a, b, c, x[i+ 4], 11,  1272893353);
        c = this.md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
        b = this.md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
        a = this.md5_hh(a, b, c, d, x[i+13], 4 ,  681279174);
        d = this.md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
        c = this.md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
        b = this.md5_hh(b, c, d, a, x[i+ 6], 23,  76029189);
        a = this.md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
        d = this.md5_hh(d, a, b, c, x[i+12], 11, -421815835);
        c = this.md5_hh(c, d, a, b, x[i+15], 16,  530742520);
        b = this.md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);

        a = this.md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
        d = this.md5_ii(d, a, b, c, x[i+ 7], 10,  1126891415);
        c = this.md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
        b = this.md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
        a = this.md5_ii(a, b, c, d, x[i+12], 6 ,  1700485571);
        d = this.md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
        c = this.md5_ii(c, d, a, b, x[i+10], 15, -1051523);
        b = this.md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
        a = this.md5_ii(a, b, c, d, x[i+ 8], 6 ,  1873313359);
        d = this.md5_ii(d, a, b, c, x[i+15], 10, -30611744);
        c = this.md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
        b = this.md5_ii(b, c, d, a, x[i+13], 21,  1309151649);
        a = this.md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
        d = this.md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
        c = this.md5_ii(c, d, a, b, x[i+ 2], 15,  718787259);
        b = this.md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);

        a = this.safe_add(a, olda);
        b = this.safe_add(b, oldb);
        c = this.safe_add(c, oldc);
        d = this.safe_add(d, oldd);
      }
      return [a,b,c,d];
    
    };
    
    /*
     * These functions implement the four basic operations the algorithm uses.
     */
    this.md5_cmn = function(q, a, b, x, s, t)
    {
      return this.safe_add(this.bit_rol(this.safe_add(this.safe_add(a, q), this.safe_add(x, t)), s),b);
    };
    this.md5_ff = function (a, b, c, d, x, s, t)
    {
      return this.md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
    };
    this.md5_gg = function(a, b, c, d, x, s, t)
    {
      return this.md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
    };
    this.md5_hh = function(a, b, c, d, x, s, t)
    {
      return this.md5_cmn(b ^ c ^ d, a, b, x, s, t);
    };
    this.md5_ii = function(a, b, c, d, x, s, t)
    {
      return this.md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
    };
    
    /*
     * Calculate the HMAC-MD5, of a key and some data
     */
    this.core_hmac_md5 = function(key, data)
    {
      var bkey = this.str2binl(key);
      if(bkey.length > 16) { bkey = this.core_md5(bkey, key.length * this.chrsz); }
    
      var ipad = Array(16), opad = Array(16);
      for(var i = 0; i < 16; i++)
      {
        ipad[i] = bkey[i] ^ 0x36363636;
        opad[i] = bkey[i] ^ 0x5C5C5C5C;
      }
    
      var hash = this.core_md5(ipad.concat(this.str2binl(data)), 512 + data.length * this.chrsz);
      return this.core_md5(opad.concat(hash), 512 + 128);
    };
    
    /*
     * Add integers, wrapping at 2^32. This uses 16-bit operations internally
     * to work around bugs in some JS interpreters.
     */
    this.safe_add = function(x, y)
    {
      var lsw = (x & 0xFFFF) + (y & 0xFFFF);
      var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
      return (msw << 16) | (lsw & 0xFFFF);
    };
    
    /*
     * Bitwise rotate a 32-bit number to the left.
     */
    this.bit_rol = function(num, cnt)
    {
      return (num << cnt) | (num >>> (32 - cnt));
    };
    
    /*
     * Convert a string to an array of little-endian words
     * If chrsz is ASCII, characters >255 have their hi-byte silently ignored.
     */
    this.str2binl = function(str)
    {
      var bin = [];
      var mask = (1 << this.chrsz) - 1;
      for(var i = 0; i < str.length * this.chrsz; i += this.chrsz) {
         bin[i>>5] |= (str.charCodeAt(i / this.chrsz) & mask) << (i%32);
      }
      return bin;
    };
    
    /*
     * Convert an array of little-endian words to a hex string.
     */
    this.binl2hex = function(binarray)
    {
      var hex_tab = this.hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
      var str = "";
      for(var i = 0; i < binarray.length * 4; i++)
      {
        str += hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) +
               hex_tab.charAt((binarray[i>>2] >> ((i%4)*8  )) & 0xF);
      }
      return str;
    };
    
    // call init
    this.init();
}

function clickdensity_init()
{
    try {
        // quit if this function has already been called
        if (arguments.callee.done) { return; }

        // flag this function so we don't do the same thing twice
        arguments.callee.done = true;

        // use clickdensity_siteID by default falling back to siteID
        if (typeof(clickdensity_siteID) == 'undefined') {
            // No 'var' as they may be specified in the web page - we need it to be global anyway
            clickdensity_siteID = siteID;
        }
        
        // use clickdensity_keyElement by default falling back to keyElement
        if (typeof(clickdensity_keyElement) == 'undefined') {
            // No 'var' as they may be specified in the web page - we need it to be global anyway
            clickdensity_keyElement = keyElement;
        }
        
        // RBS requirement 9108-CR-01 : By default record hover maps,
        // but enable it to be disabled on a per page basis
        if (typeof(clickdensity_hoverMapRecording) == 'undefined') {
            // No 'var' as they may be specified in the web page - we need it to be global anyway
            clickdensity_hoverMapRecording = true;
        }
        
        // use clickdensity_keyElement by default falling back to keyElement
        if (typeof(clickdensity_pageID) == 'undefined' && typeof(pageID) != 'undefined') {
            // No 'var' as they may be specified in the web page - we need it to be global anyway
            clickdensity_pageID = pageID;
        }
                    
        clickdensityCommon = new ClickDensityCommon(); 
        
        //below moved to here to help reduce bandwidth 
        
        // has a virtual page been set?
        if (typeof(clickdensity_pageID) != 'undefined') {
            clickdensityCommon.pageId = clickdensity_pageID;
        }
            
        if(typeof(clickdensity_siteID) != 'undefined') {
            clickdensityCommon.siteId = clickdensity_siteID;
        }

        if (typeof(clickdensity_sessionID) != 'undefined')
        {
            // Looks like the page was loaded via GetPage.ashx
            clickdensityCommon.SessionID = clickdensity_sessionID;
        }
            
        if (clickdensityCommon.SessionID === null) {
            clickdensityCommon.createSession();
        }
    } catch (e) {}
}

var clickdensity_DomLoaded =
{
    onload: [],
    loaded: function()
    {
        if (arguments.callee.done) { return; }
        arguments.callee.done = true;
        for (var i = 0; i < clickdensity_DomLoaded.onload.length;++i) {
            clickdensity_DomLoaded.onload[i]();
        }
    },
    load: function(fireThis)
    {
        this.onload.push(fireThis);
        if (document.addEventListener) {
            document.addEventListener("DOMContentLoaded", clickdensity_DomLoaded.loaded, null);
        }
        if (/KHTML|WebKit/i.test(navigator.userAgent)) {
            var _timer = setInterval(function()
            {
                if (/loaded|complete/.test(document.readyState))
                {
                    clearInterval(_timer);
                    delete _timer;
                    clickdensity_DomLoaded.loaded();
                }
            }, 10);
        }
        var oldOnLoad = window.onload;
        if (typeof oldOnLoad != 'function') {
            window.onload = clickdensity_DomLoaded.loaded;
        }
        else {
            window.onload = function() {
                oldOnLoad();
                clickdensity_DomLoaded.loaded();
            };
        }
    }
};

try {
    clickdensity_DomLoaded.load(clickdensity_init);    
} catch (e) {}