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,"&")
.replace(/</g,"<")
.replace(/>/g,">")
.replace(/\"/g,""") : 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();
}) ()