Christian Kreibich
ICIR ICSI
ICSI » ICIR » Christian Kreibich » Adsense
Adsense's JavaScript Dissected
A close look at the the client side of Google's Adsense

Introduction

Whenever you visit a site that tries to make an extra buck or two by showing ads provided by Google as part of the Adsense program, your browser interprets a good amount of JavaScript provided by Google in order to render the ads in your browser. As provided, the 21KB of code are not very readable. In the following, I present a possible interpretation of the code. Only symbol names were changed and whitespace introduced as needed. The goal was not to understand every last bit but to get a good idea of what actually happens in the browser every time it shows these ads. Some interpretations may be wrong. Feel free to refer to other analyses available on the web as well, such as Steve Hanov's, whose naming suggestions are partially followed. The code analysed below is more recent than that in Steve's writeup, though it has largely been extended and so far not changed fundamentally.

The publisher's view

Publishers of Adsense content place a snippet like the following on their pages. (Variations exist, but this is the typical scenario for "average" publishers.) The pagead2 URL at the end is the mechanism for getting the JavaScript necessary to render the ads into your browser (and thus the one to block if you don't want Google ads in your browsing experience).

<script type="text/javascript"><!--
google_ad_client = "pub-1234567890123456";
google_ad_width = 120;
google_ad_height = 600;
google_ad_format = "120x600_as";
google_ad_type = "text";
google_ad_channel ="";
google_color_border = "39306C";
google_color_bg = "39306C";
google_color_link = "FEC060";
google_color_url = "FFFFA0";
google_color_text = "FFFFA0";
//--></script>
<script type="text/javascript"
  src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>

Resulting URLs

When interpreted, this code causes the ad content to be fetched using a request that looks roughly as follows (broken into multiple lines for readability):

http://pagead2.googlesyndication.com/pagead/ads?
        client=ca-pub-1234567890123456
      & dt=1199057250414
      & lmt=1182723147
      & format=120x600_as
      & output=html
      & correlator=1199057250377
      & url=<percent-encoded referrer URL>
      & color_bg=39306C
      & color_text=FFFFA0
      & color_link=FEC060
      & color_url=FFFFA0
      & color_border=39306C
      & ad_type=text
      & cc=52
      & ga_vid=566287836.1160590581
      & ga_sid=1199057250
      & ga_hid=574051960
      & ga_fc=true
      & flash=9
      & u_h=768
      & u_w=1024
      & u_ah=768
      & u_aw=1024
      & u_cd=24
      & u_tz=60
      & u_his=10
      & u_java=true
      & u_nplug=7
      & u_nmime=22

As can be seen in the code below, variations in the URL scheme are possible, and may include cpa/ads?, pagead/sdo?, and pagead/ads?.

The returned content is rendered into one or more iframes that contain the ads the page visitor may now click on. When doing so, another request to Google is triggered, looking roughly as follows (some content snipped for readability):

http://pagead2.googlesyndication.com/pagead/iclk?
        sa=l
      & ai=BgvlRTfKCR9HcG4a6rAO8_pHHBrqRrkDe__vBBMCNtwHQ[...]
      & num=1
      & adurl=<the advertiser's URL to which the user will be redirected>
      & client=ca-pub-1234567890123456
      & nm=8

Here, the adurl component contains the URL to which the user will be redirected once the click has been processed.

The JavaScript

Finally, the code. The raw show_ads.js variant I have analysed has an MD5 hash value of b404d11df3a2d9579767707f085cf776 and was grabbed in mid-December 2007. It is still current at the time of this writing. Feedback is welcome. Note that I have never fed this code to an interpreter, so I may well have introduced bugs while trawling through the code.

// Pseudo-namespaces with automatic evaluation. 
(function(){
    
    // Associative arrays for URL-relevant strings.  I don't see an 
    // obvious reason for the two separate arrays. 
    // 
    var gStrDefs = {
	google_ad_channel:"channel",
	google_ad_host:"host",
	google_ad_region:"region",
	google_ad_section:"region",
	google_ad_type:"ad_type",
	google_adtest:"adtest",
	google_alternate_ad_url:"alternate_ad_url",
	google_alternate_color:"alt_color",
	google_bid:"bid",
	google_city:"gcs",
	google_color_bg:"color_bg",
	google_color_border:"color_border",
	google_color_line:"color_line",
	google_color_link:"color_link",
	google_color_text:"color_text",
	google_color_url:"color_url",
	google_contents:"contents",
	google_country:"gl",
	google_cust_age:"cust_age",
	google_cust_ch:"cust_ch",
	google_cust_gender:"cust_gender",
	google_cust_id:"cust_id",
	google_cust_interests:"cust_interests",
	google_cust_job:"cust_job",
	google_cust_l:"cust_l",
	google_cust_lh:"cust_lh",
	google_cust_u_url:"cust_u_url",
	google_disable_video_autoplay:"disable_video_autoplay",
	google_ed:"ed",
	google_encoding:"oe",
	google_feedback:"feedback_link",
	google_flash_version:"flash",
	google_gl:"gl",
	google_hints:"hints",
	google_kw:"kw",
	google_kw_type:"kw_type",
	google_language:"hl",
	google_referrer_url:"ref",
	google_region:"gr",
	google_reuse_colors:"reuse_colors",
	google_safe:"adsafe",
	google_targeting:"targeting",
	google_ui_features:"ui"},
	
	gStrDefsAddl = {
        google_ad_format:"format",
	google_ad_output:"output",
	google_ad_callback:"callback",
	google_ad_override:"google_ad_override",
	google_ad_slot:"slotname",
	google_analytics_webpropids:"ga_wpids",
	google_correlator:"correlator",
	google_cpa_choice:"cpa_choice",
	google_image_size:"image_size",
	google_last_modified_time:"lmt",
	google_max_num_ads:"num_ads",
	google_max_radlink_len:"max_radlink_len",
	google_num_radlinks:"num_radlinks",
	google_num_radlinks_per_unit:"num_radlinks_per_unit",
	google_only_ads_with_video:"only_ads_with_video",
	google_page_location:"loc",
	google_page_url:"url",
	google_rl_dest_url:"rl_dest_url",
	google_rl_filtering:"rl_filtering",
	google_rl_mode:"rl_mode",
	google_rt:"rt",
	google_skip:"skip"};


    // Looks up a string in the global associative arrays, and returns 
    // null on failure. 
    // 
    function lookupGoogleString(key) {
	return gStrDefs[key] || gStrDefsAddl[key] || null;
    }

    var Doc = document;

    // This extracts Google Analytics information from any available 
    // cookies, and if not available, generates suitable numerical 
    // identifiers as needed. 
    // 
    // The resulting object is hooked into the window object as 
    // window.gaGlobal and also returned. 
    // 
    function establishAnalyticsState() {
	var cookie = Doc.cookie,

	    // A crude random number or numerical identifier 
	    a = Math.round((new Date).getTime() / 1000),

	    // predicates testing for various Google Analytics cookies 
	    hasGaCookieA = cookie.indexOf("__utma=") > -1,
	    hasGaCookieB = cookie.indexOf("__utmb=") > -1,
	    hasGaCookieC = cookie.indexOf("__utmc=") > -1,
	    f,
	    g = {};

	if (hasGaCookieA) {
	    // Separate out the numbers of a utma-style cookie. Example: 
	    // 173272373.449341548.1193258541.1194650238.1195068298.3 
	    // 
	    f = cookie.split("__utma=")[1].split(";")[0].split(".");

	    // If not all cookies exist, use new value. 
	    g.sid = (!hasGaCookieB || !hasGaCookieC ? a : f[4]) + "";
	    g.vid = f[1] + "." + f[2];
	    g.from_cookie = true;
	} else {
	    g.sid = window && window.gaGlobal && window.gaGlobal.sid ?
		window.gaGlobal.sid : a + "";
	    g.vid = window && window.gaGlobal && window.gaGlobal.vid ?
		window.gaGlobal.vid : Math.round(Math.random()*2147483647) + "." + a;
	    g.from_cookie = false;
	}

	g.hid = window && window.gaGlobal && window.gaGlobal.hid ?
	    window.gaGlobal.hid : Math.round(Math.random()*2147483647);

	window.gaGlobal = g;

	return g;
    }

    // ---- Construction of "IDICommon" componenet ----------------------- 
    // =================================================================== 

    (function() {

	function b(){}

	// Returns everything following the first occurrence of "#" in 
	// the input string, or an empty string if "#" does not exist. 
	// 
	b.prototype.getHash = function(e) {
	    var c = e.indexOf("#") + 1;
	    return c ? e.substr(c) : "";
	};

	b.prototype.htmlEscape = function(e) {
	    return /[&<>\"]/.test(e) ? e.replace(/&/g,"&amp;")
                                               .replace(/</g,"&lt;")
                                               .replace(/>/g,"&gt;")
                                               .replace(/\"/g,"&quot;") : e;
	};

	// Creates and returns an iframe tag, given an associative 
	// array of attributes. 
	// 
	b.prototype.makeIframeTag = function(e) {
	    var c = "<iframe";
	    for (var f in e) {
		c += " " + f + '="' + this.htmlEscape(e[f]) + '"';
	    }

	    return c + "></iframe>";
	};

	// Returns an iframe of name frameId for the given window. 
	// 
	b.prototype.getIframe = function(win, frameId) {
	    try {
		return win.frames[frameId];
	    } catch (e) {
		return null;
	    }
	};

	b.prototype.makeIframeNode = function(attribs) {
	    var el = document.createElement("iframe");
	    for (var a in attribs) {
		el.setAttribute(a, attribs[a]);
	    }

	    return el;
	};

	b.prototype.appendHiddenIframe = function(name, url) {
	    var f = this;

	    // This launches a separate "thread" of sorts to append 
	    // the iframe to the document as soon as possible. 
	    // 
	    setTimeout(function() {
	                   document.body.appendChild(f.makeIframeNode({id:name,
                                                                       name:name,
                                                                       src:url,
                                                                       width:0,
                                                                       height:0,
                                                                       frameBorder:0}))
		       }, 0);
	};

	b.prototype.writeHiddenIframe = function(name, url) {
	    var f = this;
	    document.write(f.makeIframeTag({id:name, name:name, src:url,
                                            width:0, height:0, frameBorder:0}));
	};

	// This method chops a URI argument string into substrings 
	// that do not cut %-encoded values in half, while each 
	// substring is no longer than the provided maximum length. 
	// The resulting vector of substrings is returned. 
	// 
	b.prototype.splitURIComponent = function(argStr, maxLen) {
	    var results = [],
	        len = argStr.length,
	        k = 0;

	    while (k < len) {
		var argSub = argStr.substr(k, maxLen),
		    argSubLen = argSub.length;

		if (k + argSubLen < len) {
		    for (var i = 1; i < 3; ++i) {
			if (argSub.charAt(argSubLen - i) == "%") {
			    argSub = argSub.substr(0, argSubLen -= i);
			}
		    }
		}

		results.push(argSub);
		k += argSubLen;
	    }

	    return results;
	};

	b.prototype.exportSingleton = function(name, obj, attrTable) {
	    if (! window[name]) {
		var g = window[name] = new obj;

		// Hook the methods/variables into the new object 
		for (var k = 0; k < attrTable.length; ++k) {
		    g[attrTable[k][0]] = attrTable[k][1];
		}
	    }
	};

	var proto = b.prototype,
	    attrTable = [["getHash", proto.getHash],
			 ["htmlEscape", proto.htmlEscape],
			 ["makeIframeTag", proto.makeIframeTag],
			 ["getIframe", proto.getIframe],
			 ["makeIframeNode", proto.makeIframeNode],
			 ["appendHiddenIframe", proto.appendHiddenIframe],
			 ["writeHiddenIframe", proto.writeHiddenIframe],
			 ["splitURIComponent", proto.splitURIComponent],
			 ["exportSingleton", proto.exportSingleton],
			 ["MAX_URL_LENGTH", 4095],
			 ["IDI_DEFAULT_POLLING_INTERVAL", 1000]];

	// Hook the functions defined above into the window 
	// object under key "IDICommon", thus making available 
	// globally the IDICommon object. 
	// 
	b.prototype.exportSingleton("IDICommon", b, attrTable);

    }) ();

    // ---- Construction of "IDIHost" componenet ------------------------- 
    // =================================================================== 

    (function() {
	function addAttrs(dst, src) {
	    for (var g in src) {
		dst[g] = src[g];
	    }
	}

	function a() {
	    // Take URL as originally requested and change it to the 
	    // robots.txt URL. 
	    // 
	    this.hostRelayUrl =
                window.location.href.replace(/([^:\/])\/.*$/, "$1/robots.txt");
	    this.moduleRelayUrl = "";
	    this.frameUses = {};
	    this.moduleIds = {};
	    this.frameHashes = {};
	    this.frameUrls = {};
	    this.frameListeners = {};
	}

	// A way to dispatch messages to individual iframe modules, 
	// presumably for external use, since the method is not 
	// actually used in this code. 
	// 
	a.prototype.postMessageToModule = function(frameId, msg, obj) {
	    var url;

	    if (typeof obj == "object") {
		url = obj.moduleRelayUrl;
	    }

	    var id = this.moduleIds[frameId];

	    if (isNaN(id)) {
		throw new Error("Invalid module id");
	    } else {
		var mru = (typeof url == "string") ?
		           url :
		           this.getModuleRelayUrl(this.frameUrls[frameId]),
		    encMsg = encodeURIComponent(msg) + "$",
		    lenAvail = IDICommon.MAX_URL_LENGTH - 1 - mru.length,
		    argsChunks = IDICommon.splitURIComponent(encMsg, lenAvail),
		    numargsChunks = argsChunks.length;

		for (var p = 0; p < numArgsChunks; ++p) {
		    IDICommon.appendHiddenIframe(frameId + "_" + (id + p),
						 mru + "#" + argsChunks[p]);
		}
		
		this.moduleIds[frameId] += numArgsChunks;
	    }
	};

	a.prototype.registerListener = function(frameId, callback, g) {
	    this.unregisterListener(frameId);
	    this.frameListeners[frameId] =
	        window.setInterval(function() { this.dispatch(frameId, callback) },
				   typeof g == "object" && g.pollingInterval
				   || IDICommon.IDI_DEFAULT_POLLING_INTERVAL);
	};

	a.prototype.unregisterListener = function(frameId) {
	    window.clearInterval(this.frameListeners[frameId]);
	    this.frameListeners[frameId] = 0;
	};
	
	a.prototype.setHostRelayUrl = function(c) {
	    this.hostRelayUrl = c;
	};
	
	a.prototype.setModuleRelayUrl = function(c) {
	    this.moduleRelayUrl = c;
	};
	
	a.prototype.getModuleRelayUrl = function(c) {
	    return this.moduleRelayUrl ||
	           c.replace(/([^:\/]\/).*$/,"$1ig/idi_relay");
	};

	// This function is called by the the listeners established by 
	// registerListener() above. The given frame is looked up, its 
	// usage recorded, and the given callback dispatched for each 
	// physical frame affected. 
	// 
	a.prototype.dispatch = function(frameId, callback) {
	    var win = window.frames[frameId];

	    if (win) {
		var j;
		while (j = IDICommon.getIframe(win, frameId + "_"
                                                            + this.frameUses[frameId])) {
		    try {
			if (j.location.href == "about:blank") {
			    break;
			}
		    } catch(l) {
			break;
		    }

		    this.frameHashes[frameId] += IDICommon.getHash(j.location.href);
		    ++this.frameUses[frameId];
		}

		var i = this.frameHashes[frameId].split("$"),
		    o = i.length - 1;

		if (o > 0) {
		    this.frameHashes[frameId] = i[o];
		    for (var q = 0; q < o; ++q) {
		       callback(decodeURIComponent(i[q]), frameId);
		    }
		}
	    }
	};

	// This is the main method that generates the iframe content. 
	// 
	a.prototype.createModule = function(gau, frameId, adWidth, adHeight, params) {

	    var attrs = { frameBorder:0, scrolling:"no" },
	        ia, cb, up, pi, pd;

	    if (typeof params == "object") {
		ia = params.iframeAttrs;
		cb = params.callback;
		up = params.userPrefs;
		pi = params.pollingInterval; // unused 
		pd = params.parentDivId;
	    }

	    if (typeof ia == "object") {
		addAttrs(attrs, ia)
	    }

	    addAttrs(attrs, {id:frameId, name:frameId, src:gau,
                             width:adWidth, height:adHeight});

	    this.frameUses[frameId] = 0;
	    this.moduleIds[frameId] = 0;
	    this.frameHashes[frameId] = "";
	    this.frameUrls[frameId] = gau;

	    var addlArgs = [];

	    if (typeof up == "object") {
		for (var s in up) {
		    addlArgs.push(encodeURIComponent(s)
                                  + "="
                                  + encodeURIComponent(up[s]));
		}
	    }

	    if (typeof cb == "function") {
		addlArgs.push("idi_hr=" + encodeURIComponent(this.hostRelayUrl));
		this.registerListener(frameId, cb, params);
	    }

	    if (addlArgs.length) {
		var argsStr = addlArgs.join("&");

		// If resulting iframe URL is too long, trigger 
		// relay processing. 
		// 
		if (attrs.src.length + 1 + argsStr.length > IDICommon.MAX_URL_LENGTH) {

		    // Add separator to argument string. 
		    argsStr += "$";

		    // The arguments are cut into pieces, and multiple 
		    // iframes are requested, one for each chunk. 
		    // 
		    var mru = this.getModuleRelayUrl(gau),
			lenAvail = IDICommon.MAX_URL_LENGTH - 1 - mru.length,
			argsChunks = IDICommon.splitURIComponent(argsStr, lenAvail),
			numArgsChunks = argsChunks.length;

		    for (var s = 0; s < numArgsChunks; ++s) {
			var frameIdCount = frameId + "_" + s,
			    chunkUrl = mru + "#" + argsChunks[s];

			if (pd) {
			    var div = document.getElementById(pd);

                            // This is bizarre -- it looks like this is 
			    // supposed to call IDICommon.makeIframeNode(), 
			    // since a.e() was that function's original 
                            // name. However, "this.e" doesn't seem to be 
                            // defined for function a() (i.e., this). 
                            // 
                            // How does this work? 
			    // 
			    div.innerHTML =
				div.innerHTML +
				this.e( {id:frameIdCount, name:frameIdCount, src:chunkUrl,
					 width:0, height:0, frameBorder:0});
			} else {
			    IDICommon.writeHiddenIframe(frameIdCount, chunkUrl);
			}
		    }
		    
		    this.moduleIds[frameId] += numArgsChunks;
		    argsStr = "";
	        }

		attrs.src += "#" + argsStr;
	    }

	    if (pd) {
		var div = document.getElementById(pd);
		div.innerHTML = div.innerHTML + IDICommon.makeIframeTag(attrs);
	    } else {
		document.write(IDICommon.makeIframeTag(attrs));
	    }
	};

	var proto = a.prototype,
	    attrTable = [["setHostRelayUrl", proto.setHostRelayUrl],
			 ["setModuleRelayUrl", proto.setModuleRelayUrl],
			 ["getModuleRelayUrl", proto.getModuleRelayUrl],
			 ["createModule", proto.createModule],
			 ["postMessageToModule", proto.postMessageToModule],
			 ["registerListener", proto.registerListener],
			 ["unregisterListener", proto.unregisterListener]];

	// Make the function set available globally under IDIHost. 
	// 
	IDICommon.exportSingleton("IDIHost", a , attrTable);
    }) ();

    // ---- Non-modularized code ----------------------------------------- 
    // =================================================================== 

    function quoteString(b) {
	return b != null ? '"' + b + '"' : '""';
    }

    function encodeUri(uri) {
	if (typeof encodeURIComponent == "function") { 
	    return encodeURIComponent(uri);
	} else {
	    return escape(uri);
	}
    }

    function appendToQuery(key, val) {
	if (key && val) {
	    window.google_ad_url += "&" + key + "=" + val;
	}
    }

    function appendItemToQuery(id) {
	var win = window,
	    key = lookupGoogleString(id),
	    val = win[key];

	appendToQuery(key, val);
    }

    function appendToQueryEscaped(b,a) {
	if (a) {
	    appendToQuery(b, encodeUri(a));
	}
    }

    function appendItemToQueryEscaped(id) {
	var win = window,
	    key = lookupGoogleString(id),
	    val = win[id];

	appendToQueryEscaped(key, val);
    }

    function appendIndexedItemToQuery(id, index) {
	var win = window,
	    key = lookupGoogleString(id),
	    val = win[id];

	if(key && val && typeof val == "object") {
	    val = val[index % val.length];
	}

	appendToQuery(key, val);
    }

    // This function is adding a "fingerprint" of browser capabilities 
    // to the google_ad_url, collecting all kinds of things about your 
    // browser setup and usage. 
    // 
    function appendBrowserCapabilities(b,a) {
	var scr = b.screen,
	    e = navigator.javaEnabled(),
	    c =- a.getTimezoneOffset();
	
	// If we have screen information via the window, 
	// add information to the window's google_ad_url. 
	// 
	if (d) {
	    appendToQuery("u_h", scr.height);
	    appendToQuery("u_w", scr.width);
	    appendToQuery("u_ah", scr.availHeight);
	    appendToQuery("u_aw", scr.availWidth);
	    appendToQuery("u_cd", scr.colorDepth);
	}

	// Add timezone delta + java status, and length of history. 
	// 
	appendToQuery("u_tz", c);
	appendToQuery("u_his", history.length);
	appendToQuery("u_java", e);

	// Add number of plugins 
	// 
	if (navigator.plugins) {
	    appendToQuery("u_nplug",navigator.plugins.length);
	}

        // Add number of mimetypes 
        // 
	if(navigator.mimeTypes){
	    appendToQuery("u_nmime",navigator.mimeTypes.length);
	}
    }

    function convertToCaName(b) {
	if (b) {
	    b = b.toLowerCase();
	    if (b.substring(0,3) != "ca-") {
		b = "ca-" + b;
	    }
	}

	return b;
    }

    function ensureDistAffPrefix(b) {
	if (b) {
	    b = b.toLowerCase();
	    if (b.substring(0,9) != "dist-aff-") {
		b = "dist-aff-" + b;
	    }
	}

	return b;
    }

    // Retrieves an element from the DOM and sets 
    // its height to the given value. 
    // 
    function setElementHeight(id, height) {
	var el = document.getElementById(id);
	el.style.height = height + "px";
    }

    function setFlashAdHeight(str, elementId, timer) {
	window.clearTimeout(timer);
	var re = /^google_resize_flash_ad_idi\((\d+)\)/,
	    results = str.match(re);

	if (results) {
	    setElementHeight(elementId, results[1]);
	}
    }

    // This function generates the Google ads output, either 
    // indirectly via JavaScript code being written to the document, 
    // or via an iframe and HTML. 
    // 
    function createAds(win, doc, gau /* Google ad url */, e /* always null */) {
	gau = gau.substring(0,2000);
	gau = gau.replace(/%\w?$/,"");

	if ((win.google_ad_output == "js" || win.google_ad_output == "json_html") &&
	    (win.google_ad_request_done || win.google_radlink_request_done)) {
	    doc.write('<script language="JavaScript1.1" src='
                      + quoteString(gau)
                      + "><\/script>");
	} else if (win.google_ad_output == "html") {
	    if (win.name != "google_ads_frame") {
		if (e != null) { // This cannot currently happen 
		    doc.write('<div id="' + e + '">');
		}

		if (isSpecialAdClient(win.google_ad_output, win.google_ad_client)) {
		    IDIHost.setModuleRelayUrl("http://pagead2.googlesyndication.com/"
                                              + "pagead/idi_relay.html");
		    var c = 0;

		    if (win.google_num_0ad_slots) {
			c += win.google_num_0ad_slots;
		    }

		    if (win.google_num_ad_slots) {
			c += win.google_num_ad_slots;
		    }

		    if (win.google_num_sdo_slots) {
			c += win.google_num_sdo_slots;
		    }
		    
		    var f = "google_inline_div" + c,
			g = "<div id=" + quoteString(f)
                            + ' style="position:relative;width:' + win.google_ad_width
                            + 'px"></div><div style="position:relative;width:'
                            +  win.google_ad_width + "px;height:" + win.google_ad_height
                            + 'px;z-index:-1"></div>';
		    
		    doc.write(g);

		    var frameId = "google_frame" + c,
			timer = win.setTimeout(function(){ IDIHost.unregisterListener(k) },
                                               5000);
		    
		    IDIHost.createModule(gau, frameId,
                                         win.google_ad_width,
                                         win.google_ad_height,
					 { callback:function(str, elmentId)
                                             { setFlashAdHeight(str, elementId, timer) },
					   pollingInterval:500,
					   iframeAttrs:{ style:"position: absolute;left:0px",
                                                         marginWidth:"0",
						         marginHeight:"0",
                                                         vspace:"0", hspace:"0", 
						         allowTransparency:"true"},
					   parentDivId:f});
		} else {
		    doc.write('<iframe name="google_ads_frame" width='
                              + quoteString(win.google_ad_width)
			      + " height=" + quoteString(win.google_ad_height)
			      + " frameborder=" + quoteString(win.google_ad_frameborder)
			      + " src=" + quoteString(gau)
			      + ' marginwidth="0" marginheight="0" vspace="0" hspace="0" '
                              + ' allowtransparency="true" scrolling="no">');
		    doc.write("</iframe>");
		}
		
		if (e != null) {
		    doc.write("</div>");
		}
	    }
	} else if (win.google_ad_output == "textlink") {
            
	    // This has the same effect as the initial if()-clause 
	    // above. 
	    // 
	    doc.write('<script language="JavaScript1.1" src='
                      + quoteString(gau)
                      + "><\/script>");
	}
    }

    // This is an initializer, taking all the string definitions and 
    // initializing them to null as members of the window. 
    // 
    function initVars(win) {
	for (var def in gStrDefs) {
	    win[def] = null;
	}

	for (var a in gStrDefsAddl) {
	    win[def] = null;
	}
    }

    // Returns true if we are using link units (one of the alternative 
    // display formats). I don't know the radlinks stuff. 
    // 
    function usingLinkUnits(win) {
	if (win.google_ad_format) {
	    return win.google_ad_format.indexOf("_0ads") > 0;
	}

	return win.google_ad_output != "html" && win.google_num_radlinks > 0;
    }

    function isSDOFormat(b) {
	return b && b.indexOf("_sdo") != -1;
    }

    function buildQueryUrl() {
	var win = window,
	    doc = document,
	    date = new Date,
	    rnd = e.getTime(),
	    adFormat = win.google_ad_format;

	// Cost per Action choice -- Defined only in the some of the 
	// snippets the hoster carries in his webpages. 
	// 
	if (win.google_cpa_choice != null) {
	    win.google_ad_url = "http://pagead2.googlesyndication.com/cpa/ads?";
	    win.google_ad_url += "client="
                                 + escape(convertToCaName(win.google_ad_client));
	    win.google_ad_region = "_google_cpa_region_";
	    appendItemToQuery("google_cpa_choice");

	    if (typeof doc.characterSet != "undefined") {
		appendToQueryEscaped("oe", doc.characterSet);
	    } else if (typeof doc.charset != "undefined") {
		appendToQueryEscaped("oe", doc.charset);
	    }
	} else if (isSDOFormat(adFormat)) {
	    win.google_ad_url = "http://pagead2.googlesyndication.com/pagead/sdo?";
	    win.google_ad_url += "client="
                                 + escape(ensureDistAffPrefix(win.google_ad_client));
	} else {
	    win.google_ad_url = "http://pagead2.googlesyndication.com/pagead/ads?";
	    win.google_ad_url += "client="
                                 + escape(convertToCaName(win.google_ad_client));
	}

	appendItemToQuery("google_ad_host");

	var ad_slots_client = win.google_num_slots_by_client,
	    ad_slots_channel = win.google_num_slots_by_channel,
	    adFormats = win.google_prev_ad_formats_by_region,
	    ad_slotnames = win.google_prev_ad_slotnames_by_region;

	if (win.google_ad_region == null && win.google_ad_section != null) {
	    win.google_ad_region = win.google_ad_section;
	}

	var adRegion = win.google_ad_region == null ? "" : win.google_ad_region;

	// I believe the below somehow implement selection of the ad 
	// block in case multiple blocks are placed on a single page, 
	// but I'm currently unclear on the details. 
	// 
	if (isSDOFormat(adFormat)) {
	    if (win.google_num_sdo_slots) {
		win.google_num_sdo_slots = win.google_num_sdo_slots + 1;
	    } else {
		win.google_num_sdo_slots = 1;
	    }

	    if (win.google_num_sdo_slots > 4) {
		return false;
	    }
	} else if (usingLinkUnits(win)) {
	    if (win.google_num_0ad_slots) {
		win.google_num_0ad_slots = win.google_num_0ad_slots + 1;
	    } else {
		win.google_num_0ad_slots = 1;
	    }

	    if (win.google_num_0ad_slots > 3) {
		return false;
	    }
	} else if (win.google_cpa_choice == null) {
	    if (win.google_num_ad_slots) {
		win.google_num_ad_slots = win.google_num_ad_slots + 1;
	    } else {
		win.google_num_ad_slots = 1;
	    }

	    if (win.google_num_slots_to_rotate) {
		adFormats[adRegion] = null;
		ad_slotnames[adRegion] = null;

		if (win.google_num_slot_to_show == null) {
		    win.google_num_slot_to_show
                      = rnd % win.google_num_slots_to_rotate + 1;
		}

		if (win.google_num_slot_to_show != win.google_num_ad_slots) {
		    return false;
		}
	    } else if (win.google_num_ad_slots > 6 && adRegion == "") {
		return false;
	    }
	}

	appendToQuery("dt", date.getTime());
	appendItemToQuery("google_language");

	if (win.google_country) {
	    appendItemToQuery("google_country");
	} else {
	    appendItemToQuery("google_gl");
	}

	appendItemToQuery("google_region");
	appendItemToQueryEscaped("google_city");
	appendItemToQueryEscaped("google_hints");
	appendItemToQuery("google_safe");
	appendItemToQuery("google_encoding");
	appendItemToQuery("google_last_modified_time");
	appendItemToQueryEscaped("google_alternate_ad_url");
	appendItemToQuery("google_alternate_color");
	appendItemToQuery("google_skip");
	appendItemToQuery("google_targeting");

	var o = win.google_ad_client;

	if (! ad_slots_client[o]) {
	    ad_slots_client[o] = 1;
	    ad_slots_client.length += 1;
	} else{
	    ad_slots_client[o] += 1;
	}
	
	if (adFormats[adRegion]) {
	    if (!isSDOFormat(adFormat)) {
		appendToQueryEscaped("prev_fmts", adFormats[adRegion].toLowerCase());

		if (ad_slots_client.length > 1) {
		    appendToQuery("slot", ad_slots_client[o]);
		}
	    }
	}

	if (ad_slotnames[adRegion]) {
	    appendToQueryEscaped("prev_slotnames",
                                 ad_slotnames[adRegion].toLowerCase());
	}

	if (adFormat && !win.google_ad_slot) {
	    appendToQueryEscaped("format", adFormat.toLowerCase());
	    if (!isSDOFormat(adFormat)) {
		if(adFormats[adRegion]) {
		    adFormats[adRegion] = adFormats[adRegion] + "," + adFormat;
		} else{
		    adFormats[adRegion] = adFormat;
		}
	    }
	}

	if (win.google_ad_slot) {
	    if (ad_slotnames[adRegion]) {
		ad_slotnames[adRegion] = ad_slotnames[adRegion]
                                          + ","
                                          + win.google_ad_slot;
	    } else {
		ad_slotnames[adRegion] = win.google_ad_slot;
	    }
	}

	appendItemToQuery("google_max_num_ads");
	appendToQuery("output",win.google_ad_output);
	appendItemToQuery("google_adtest");
	appendItemToQuery("google_ad_callback");
	appendItemToQuery("google_ad_slot");
	appendItemToQueryEscaped("google_correlator");

	if (win.google_ad_channel) {
	    appendItemToQueryEscaped("google_ad_channel");

	    var result = "",
		channels = win.google_ad_channel.split("+");
	    
	    for(var t = 0; t < channels.length; t++) {
		var chan = channels[t];
		if (! ad_slots_channel[chan]) {
		    ad_slots_channel[chan] = 1;
		} else {
		    result += chan + "+";
		}
	    }

	    appendToQueryEscaped("pv_ch", result);
	}

	appendItemToQueryEscaped("google_page_url");
	appendIndexedItemToQuery("google_color_bg", rnd);
	appendIndexedItemToQuery("google_color_text", rnd);
	appendIndexedItemToQuery("google_color_link", rnd);
	appendIndexedItemToQuery("google_color_url", rnd);
	appendIndexedItemToQuery("google_color_border", rnd);
	appendIndexedItemToQuery("google_color_line", rnd);

	if (win.google_reuse_colors)
	    appendToQuery("reuse_colors",1);
	else 
	    appendToQuery("reuse_colors",0);

	appendItemToQuery("google_kw_type");
	appendItemToQueryEscaped("google_kw");
	appendItemToQueryEscaped("google_contents");
	appendItemToQuery("google_num_radlinks");
	appendItemToQuery("google_max_radlink_len");
	appendItemToQuery("google_rl_filtering");
	appendItemToQuery("google_rl_mode");
	appendItemToQuery("google_rt");
	appendItemToQueryEscaped("google_rl_dest_url");
	appendItemToQuery("google_num_radlinks_per_unit");
	appendItemToQuery("google_ad_type");
	appendItemToQuery("google_image_size");
	appendItemToQuery("google_ad_region");
	appendItemToQuery("google_feedback");
	appendItemToQueryEscaped("google_referrer_url");
	appendItemToQueryEscaped("google_page_location");
	appendItemToQuery("google_bid");
	appendItemToQuery("google_cust_age");
	appendItemToQuery("google_cust_gender");
	appendItemToQuery("google_cust_interests");
	appendItemToQuery("google_cust_id");
	appendItemToQuery("google_cust_job");
	appendItemToQuery("google_cust_u_url");
	appendItemToQuery("google_cust_l");
	appendItemToQuery("google_cust_lh");
	appendItemToQuery("google_cust_ch");
	appendItemToQuery("google_ed");
	appendItemToQueryEscaped("google_ui_features");
	appendItemToQueryEscaped("google_only_ads_with_video");
	appendItemToQueryEscaped("google_disable_video_autoplay");

	if (pageLocationMatches(win, doc) && doc.body) {
	    var v = doc.body.scrollHeight, // full document height 
		s = doc.body.clientHeight; // window height 

	    if (s && v) {
		appendToQueryEscaped("cc", Math.round(s*100/v));
	    }
	}

	establishAnalyticsState();

	appendToQuery("ga_vid", win.gaGlobal.vid);
	appendToQuery("ga_sid", win.gaGlobal.sid);
	appendToQuery("ga_hid", win.gaGlobal.hid);
	appendToQuery("ga_fc", win.gaGlobal.from_cookie);
	appendItemToQueryEscaped("google_analytics_webpropids");
	appendItemToQuery("google_ad_override");
	appendItemToQuery("google_flash_version");
	
	appendBrowserCapabilities(win, date);

	return true;
    }

    function makeAds() {
	var win = window,
	    doc = document;

	if (! buildQueryUrl()) {
	    return;
	}

	createAds(win, doc, win.google_ad_url, null);
	initVars(win);
    }

    function errorHandler(unused1, unused2, unused3) {
	makeAds();
	return true;
    }

    function pageLocationMatches(win, doc) {
	return win.top.location == doc.location;
    }

    // Tests whether we cannot render the ads in the given space? 
    // 
    function isTooSmall(win, doc) {
	var d = a.documentElement;

	if (pageLocationMatches(win, doc))
	    return false;

	if (win.google_ad_width && win.google_ad_height) {
	    var w = 1,
		h = 1;

	    if (win.innerHeight) {
		w = win.innerWidth;
		h = win.innerHeight;
	    } else if (d && d.clientHeight) {
		w = d.clientWidth;
		h = d.clientHeight;
	    } else if (doc.body) {
		w = doc.body.clientWidth;
		h = doc.body.clientHeight;
	    }

	    if (h > 2 * win.google_ad_height || w > 2 * win.google_ad_width) {
		return false;
	    }
	}
	
	return true;
    }

    function fillInUnsetVars(errorHandler) {
	var win = window,
	    origErrorHandler = win.onerror;

	win.onerror = errorHandler;

	if (win.google_ad_frameborder == null) {
	    win.google_ad_frameborder = 0;
	}

	if (win.google_ad_output == null) {
	    win.google_ad_output = "html";
	}

	if (isSDOFormat(win.google_ad_format)) {
	    var c = win.google_ad_format.match(/^(\d+)x(\d+)_.*/);

	    if (c) {
		win.google_ad_width=parseInt(c[1]);
		win.google_ad_height=parseInt(c[2]);
		win.google_ad_output="html";
	    }
	}

	if (win.google_ad_format == null && win.google_ad_output == "html") {
	    win.google_ad_format = win.google_ad_width + "x" + win.google_ad_height;
	}

	setLocation(win, document);

	if (win.google_num_slots_by_channel == null) {
	    win.google_num_slots_by_channel = [];
	}

	if (win.google_num_slots_by_client == null) {
	    win.google_num_slots_by_client =[];
	}

	if (win.google_prev_ad_formats_by_region == null) {
	    win.google_prev_ad_formats_by_region = [];
	}

	if (win.google_prev_ad_slotnames_by_region == null) {
	    win.google_prev_ad_slotnames_by_region = [];
	}
	
	if (win.google_correlator == null) {
	    win.google_correlator = (new Date).getTime();
	}

	if (win.google_adslot_loaded == null) {
	    win.google_adslot_loaded = {};
	}

	if (win.google_adContentsBySlot == null) {
	    win.google_adContentsBySlot = {};
	}
	
	if (win.google_flash_version == null) {
	    win.google_flash_version = detectFlashVersion().toString();
	}

	win.onerror = origErrorHandler;
    }

    function detectBrowser(str) {
	if (str in userAgentSubstrings) {
	    return userAgentSubstrings[str];
	}

	return userAgentSubstrings[str]
            = navigator.userAgent.toLowerCase().indexOf(str) != -1;
    }

    var userAgentSubstrings = {};

    function isSpecialAdClient(adOutput, adClient) {
	if (adOutput != "html") {
	    // This cannot currently happen, given how we're called. 
	    return false;
	}

	var d = {};

	d["ca-pub-7027491298716603"] = true;
	d["ca-pub-8344185808443527"] = true;
	d["ca-pub-9812682548211238"] = true;	
	d["ca-pub-4424308218891706"] = true;
	d["ca-pub-6922559858235084"] = true;
	d["ca-pub-6477563040863705"] = true;
	d["ca-google"] = true;

	return d[convertToCaName(adClient)] != null;
    }

    function splitQueryString(b) {

	// For this method's explanation, see 
	// http://gandolf.homelinux.org/~smhanov/blog/?id=21 

	var a = {},
	    d = b.split("?"),
	    e = d[d.length - 1].split("&");

	for(var c = 0; c < e.length; c++) {
	    var f = e[c].split("=");

	    if (f[0]) {
		try {
		    a[f[0].toLowerCase()] = f.length > 1 ?
			(window.decodeURIComponent ?
			   decodeURIComponent(f[1].replace(/\+/g," ")) : unescape(f[1]))
			: "";
		} catch(g) {}
	    }
	}

	return a;
    }

    function handleAdOverride() {
	var win = window,
	    args = splitQueryString(document.URL);

	if (args.google_ad_override) {
	    win.google_ad_override = args.google_ad_override;
	}
    }

    function detectFlashVersion() {
	var b = 0;

	if (navigator.plugins && navigator.mimeTypes.length) {
	    var a = navigator.plugins["Shockwave Flash"];
	    if (a && a.description) {
		b = a.description.replace(/([a-zA-Z]|\s)+/,"").split(".")[0];
	    }
	} else if (navigator.userAgent &&
                   navigator.userAgent.indexOf("Windows CE") >= 0) {
	    b = 3;
	    var d = 1;

	    while (d) {
		try {
		    d = new ActiveXObject("ShockwaveFlash.ShockwaveFlash." + (b+1));
		    b++;
		} catch(e) {
		    d = null;
		}
	    }
	} else if (detectBrowser("msie") && !window.opera) {
	    try {
		var d = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7");
	    } catch (e) {
		try{
		    var d = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");
		    b = 6;
		    d.AllowScriptAccess = "always";
		} catch(e) {
		    if (b == 6) {
			return b;
		    }
		}

		try {
		    d = new ActiveXObject("ShockwaveFlash.ShockwaveFlash");
		} catch(e) {}
	    }

	    if (d != null) {
		b = d.GetVariable("$version").split(" ")[1].split(",")[0];
	    }
	}

	return b;
    };

    // This function establishes the location URL at which the 
    // ads are shown. 
    // 
    function setLocation(win, doc) {
	if (win.google_page_url == null) {

	    // Wow -- special URL massaging to extract meaningful values 
	    // from yieldmanager.com URLs. 
	    // 
	    if (specialSites[doc.domain] && doc.domain == "ad.yieldmanager.com") {
		var d = doc.URL.substring(doc.URL.lastIndexOf("http"));
		win.google_page_url = d;
		win.google_page_location = doc.location;
		win.google_referrer_url = d;
	    } else {
		win.google_page_url = doc.referrer;
		if (! isTooSmall(win, doc)) {
		    win.google_page_url = doc.location;
		    win.google_last_modified_time = Date.parse(doc.lastModified) / 1000;
		    win.google_referrer_url = doc.referrer;
		}
	    }
	} else {
	    win.google_page_location = doc.referrer;
	    if (! isTooSmall(win, doc)) {
		win.google_page_location = doc.location;
	    }
	}
    }

    var specialSites = {};
    specialSites["ad.yieldmanager.com"] = true;

    handleAdOverride();
    fillInUnsetVars(errorHandler);
    makeAds();
}) ()
updated on 26 July 19 | yummy spam, yesss... built with TT | (cc) Christian Kreibich