Hate UML?

Draw sequence diagrams in seconds.
http://www.websequencediagrams.com

Dissecting Adsense
Posted on: 2007-04-28 09:55:52
A lot of the Internet uses Google Adsense, and yet very little is known about how it works. I downloaded the show_ads.js script that every adsense page references, and analyzed it to try to see how it works. I found lots of hidden features along the way.

The google show_ads.js script has been optimized for quick loading. Google has removed all whitespace, and renamed variables and funcitons to have single letter names. While this is good for fast page loading, it is not so good for understanding what it does. So I ran this through my Javascript PrettyPrinter, and then renamed the functions and some variables based on what I think they do.

A large amount of this code is for dealing with script ads. That is surprising, considering that it is only possible for premium adsense customers (like about.com or slashdot.org) to use script ads. The rest of us are confined to iframes. It is a strange decision to include all of this additional functionality that can never be used. Why not make two show_ads.js -- one for premium customers and one for regular ones?

Regular iFrame Ads

Web sites that do not yet have 20 million unique visitors have to have iframe ads. The iframe is a separate web page embedded in your web page. For example, here is an iframe containing www.example.com:

  1. User script sets variables such as the channels, and ads colours
  2. The show_ads.js script translates the variables into a large query string.
  3. The script will insert an iframe into your document whose source is the query string.
  4. The user's browser retrieves the separate document into the iframe, and the user sees the ad unit.

Script Ads

  1. Sides like about.com will call another script to set variables like "google_cust_name" "google_cust_age" etc. if they know this information.
  2. The show_ads.js script will build a query string to load the another script from google servers. It will also setup callback functions to call once the other script finishes finding ads.
  3. The other script will place the ad content into an array.
  4. The other script calls the callback functions. The callbacks will insert div and iframe elements into your web page, containing the ad units.

The end result for script ads is that more customization is possible, and the ads will load more quickly. Instead of making 5 separate queries from your browser to google, your browser will only make 1 query. The disadvantage is that you need a programmer to make the javascript to set all the variables correctly.

It is against Adsense Terms of Service to modify the Adsense code in any way. However, it is interesting to note the extra API available to premium customers. You can see how Slashdot uses the api in this script, which is included just before show_ads.js.

Script Variable URL Encoding Meaning
google_ad_output output Default: "html". Other values are "js" or "textlink".

If a premium customer is using script ads, this is set to "json_html". JSON stands for Javascript Object Notation . Essentially, this output type will be a Javascript array containing the ad content. Only the HTML type will result in an iframe. All other ad types will include a <script> tag into your web page.

google_ad_client client This identifies the adsense customer (the web master). For example: "pub-8374822756592383". It is included for non-premium adsense customers too.
google_cpa_choice cpa_choice It probably stands for "Code Page" choice, and it controls the unicode character encoding of the ads. If this is set, then the document's character set is appended to the query as "oe". Also, the value of google_cpa_choice is added as well.
google_ad_host host
Date.getTime() dt The current time is added to the request as "dt"
google_language hl This variable is added to the request as "hl"
google_country gl
google_region gr
google_city gcs
google_hints hints
google_safe adsafe
google_encoding oe This must be the charset. Note that if google_cpa_hoice is set, "oe" would be added to the query string twice. They must therefore be mutually exclusive.
google_last_modified_time lmt
google_alternate_ad_url alternate_ad_url
google_alternate_color alt_color
google_skip skip
google_targeting targeting
N/A prev_fmts Seems to keep track of the number of ad units on your page.
N/A format This is derived from the script based on whether the browser is supported.
google_max_num_ads num_ads
google_ad_output output This can be either "json_html" if used by a premium adsense customer, or it's set to whatever google_ad_output is, which usually is null
google_adtest adtest
google_ad_channel channel
google_ad_section This is not encoded in the query string, but read directly by the target script. Slashdot, for example, sets it to 'apple askslashdot backslash books developers'.
N/A pv_ch
google_page_url url
google_color_bg bg
google_color_text text
google_color_link link
google_color_url url
google_color_border border
google_color_line line
google_reuse_colors reuse_colors
google_kw_type kw_type
google_kw kw
google_contents contents
google_num_radlinks num_radlinks
google_max_radlink_len max_radlink_len
google_rl_filtering rl_filtering
google_rl_mode rl_mode
google_rt rt
google_ad_type ad_type
google_image_Size image_size
google_ad_region region
google_feedback feedback_link
google_referrer_url ref
google_page_location loc
google_bid bid
google_cust_age cust_age
google_cust_gender cust_gender
google_cust_interests cust_interests
google_cust_idid cust_id
google_cust_job cust_job
google_cust_u_url cust_l
google_cust_lh cust_lh
google_cust_ch cust_ch
google_ed ed
google_ui_features ui
cc If this is the top frame, this is the ratio of the browser window height divided by the total height of the web page, expressed as a percentage.
google_ad_override google_ad_override
N/A flash This is the version of flash that the user has.
u_h, u_w screen height, width
u_ah, u_aw available screen height, width
u_cd color depth
u_tz document.getTimezoneOffset()
u_his Number of pages in browser history
u_java navigator.javaEnabled()
u_nplug Number of plugins installed
u_nmime Number of mime types the browser recognizes.

The Dissection

We begin with (function(). This is an odd syntax. However, by defining all of the other functions inside of an unnamed function, Google is giving them their own namespace. That way, you can include the same script numerous times, and include multiple ads per page.

(function()
{

Originally named "n", this function obviously returns its argument with quotes added. It's used for dynamically building HTML.

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

"encodeURIComponent" is a standard javascript function. It turns strings like "http://www.google.com" into "http%3A%2F%2www.google.com". This wrapper checks if it's available in your browser, and if not, it calls escape() instead.


    function encodeUri(b)
    {
        if(typeof encodeURIComponent == "function")
        {
            return encodeURIComponent(b)
        }
        else
        {
            return escape(b)
        }
    }
    
    function appendToQuery(b, a)
    {
        if(a)
        {
            window.google_ad_url += "&" + b + "=" + a
        }
        
    }
    
    function appendToQueryEscaped(b, a)
    {
        if(a)
        {
            appendToQuery(b, encodeUri(a))
        }
        
    }

    function appendColourToQuery(b, a, c)
    {
        if(a && typeof a == "object")
        {
            a = a[c % a.length]
        }
        
        appendToQuery("color_" + b, a)
    }

Here, Google is recording your time zone, screen size, colour depth, and the mime types your browser will accept. Surprisingly, they are also recording how many web pages you have visited in your history (although not which ones). This is a good indication of how long you have been browsing for.

    function appendBrowserCapabilities(b, a)
    {
        var c = b.screen, e = navigator.javaEnabled(),f =- a.getTimezoneOffset();
        if(c)
        {
            appendToQuery("u_h", c.height);
            appendToQuery("u_w", c.width);
            appendToQuery("u_ah", c.availHeight);
            appendToQuery("u_aw", c.availWidth);
            appendToQuery("u_cd", c.colorDepth)
        }
        
        appendToQuery("u_tz", f);
        appendToQuery("u_his", history.length);
        appendToQuery("u_java", e);
        if(navigator.plugins)
        {
            appendToQuery("u_nplug", navigator.plugins.length)
        }
        
        if(navigator.mimeTypes)
        {
            appendToQuery("u_nmime", navigator.mimeTypes.length)
        }
        
    }

This takes a string, makes it lower case, and adds "ca-" in front of it. Maybe it converts it into some kind of name they lookup in a database.

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

This appears to write the start of an add to the document. It writes either javascript, html, or textlint ads. Notice that it does not have the <script> end tag. So the ad still has to be written, and then they have to close off the tag.

    function writeRegularAd(b, a, c)
    {
        c = c.substring(0, 2000);
        c = c.replace( /w ?/, "");
        if(b.google_ad_output == "js" &&(b.google_ad_request_done || b.google_radlink_request_done))
        {
            a.write('<script language="JavaScript1.1" src=' + addQuotes(c) +script>")
        }
        else if(b.google_ad_output == "html")
        {
            if(b.name != "google_ads_frame")
            {
                a.write('<iframe name="google_ads_frame" width=' + addQuotes(b.google_ad_width) +" height=" + addQuotes(b.google_ad_height) +" frameborder=" + addQuotes(b.google_ad_frameborder) +" src=" + addQuotes(c) +' marginwidth="0" marginheight="0" vspace="0" hspace="0" allowtransparency="true" scrolling="no">');
                a.write("</iframe>")
            }
            
        }
        else if(b.google_ad_output == "textlink")
        {
            a.write('<script language="JavaScript1.1" src=' + addQuotes(c) +script>")
        }
        
    }

This function is called to clear all the adsense variables as soon as the script is done with them. That way they don't interfere with the next ad in the page.

    function eraseAllVariables(b)
    {
        var a = null;
        b.google_ad_frameborder = a;
        b.google_ad_format = a;
        b.google_page_url = a;
        b.google_language = a;
        b.google_gl = a;
        b.google_country = a;
        b.google_region = a;
        b.google_city = a;
        b.google_hints = a;
        b.google_safe = a;
        b.google_encoding = a;
        b.google_ad_output = a;
        b.google_max_num_ads = a;
        b.google_ad_channel = a;
        b.google_contents = a;
        b.google_alternate_ad_url = a;
        b.google_alternate_color = a;
        b.google_color_bg = a;
        b.google_color_text = a;
        b.google_color_link = a;
        b.google_color_url = a;
        b.google_color_border = a;
        b.google_color_line = a;
        b.google_reuse_colors = a;
        b.google_adtest = a;
        b.google_kw_type = a;
        b.google_kw = a;
        b.google_num_radlinks = a;
        b.google_max_radlink_len = a;
        b.google_rl_filtering = a;
        b.google_rl_mode = a;
        b.google_rt = a;
        b.google_ad_type = a;
        b.google_image_size = a;
        b.google_feedback = a;
        b.google_skip = a;
        b.google_page_location = a;
        b.google_referrer_url = a;
        b.google_ad_region = a;
        b.google_ad_section = a;
        b.google_bid = a;
        b.google_cpa_choice = a;
        b.google_cust_age = a;
        b.google_cust_gender = a;
        b.google_cust_interests = a;
        b.google_cust_id = a;
        b.google_cust_job = a;
        b.google_cust_u_url = a;
        b.google_cust_l = a;
        b.google_cust_lh = a;
        b.google_cust_ch = a;
        b.google_ed = a;
        b.google_targeting = a;
        b.google_ad_host = a;
        b.google_ad_slotname = a;
        b.google_ad_override = a;
        b.google_ui_features = a
    }

This is the last thing that the script executes, so it must be important. It builds the query URL and begins to write the ad to the document. Notice that google sometimes knows your "cust_age", "cust_gender" and "cust_interest"!

    function buildQueryUrl()
    {
        var b = null, a = window, c = document;
        var e = new Date;
        var curTime = e.getTime();
        var adFormat = a.google_ad_format;
        var scriptAdOkay = false;
        if ( O(a.google_ad_output, a.google_ad_client) && !incompatibleBrowser())
        {
            scriptAdOkay = true
        }
        
        if(a.google_cpa_choice)
        {
            a.google_ad_url = "http://pagead2.googlesyndication.com/cpa/ads?";
            a.google_ad_url += "client=" + escape(E(a.google_ad_client));
            a.google_ad_region = "_google_cpa_region_";
            appendToQuery("cpa_choice", a.google_cpa_choice);
            if(typeof c.characterSet != "undefined")
            {
                appendToQueryEscaped("oe", c.characterSet)
            }
            else if(typeof c.charset != "undefined")
            {
                appendToQueryEscaped("oe", c.charset)
            }
            
        }
        else
        {
            a.google_ad_url = "http://pagead2.googlesyndication.com/pagead/ads?";
            a.google_ad_url += "client=" + escape(E(a.google_ad_client))
        }
        
        appendToQuery("host", a.google_ad_host);
        var j = a.google_num_slots_by_client, C = a.google_num_slots_by_channel, l = a.google_prev_ad_formats_by_region;
        a.onerror = a.google_org_error_handler;
        if(a.google_ad_region == b && a.google_ad_section != b)
        {
            a.google_ad_region = a.google_ad_section
        }
        
        var k = a.google_ad_region == b ? "" : a.google_ad_region, w = false;
        if(adFormat)
        {
            w = i.indexOf("_0ads") >0
        }
        
        if(w)
        {
            if(a.google_num_0ad_slots)
            {
                a.google_num_0ad_slots = a.google_num_0ad_slots + 1
            }
            else
            {
                a.google_num_0ad_slots = 1
            }
            
            if(a.google_num_0ad_slots > 3)
            {
                return
            }
            
        }
        else if( !a.google_cpa_choice)
        {
            if(a.google_num_ad_slots)
            {
                a.google_num_ad_slots = a.google_num_ad_slots + 1
            }
            else
            {
                a.google_num_ad_slots = 1
            }
            
            if(a.google_num_slots_to_rotate)
            {
                l[k] =b;
                if(a.google_num_slot_to_show == b)
                {
                    a.google_num_slot_to_show = curTime % a.google_num_slots_to_rotate + 1
                }
                
                if(a.google_num_slot_to_show != a.google_num_ad_slots)
                {
                    return
                }
                
            }
            else if(a.google_num_ad_slots > 3 && k == "")
            {
                return
            }
            
        }
        
        appendToQuery("dt", e.getTime());
        appendToQuery("hl", a.google_language);
        if(a.google_country)
        {
            appendToQuery("gl", a.google_country)
        }
        else
        {
            appendToQuery("gl", a.google_gl)
        }
        
        appendToQuery("gr", a.google_region);
        appendToQueryEscaped("gcs", a.google_city);
        appendToQueryEscaped("hints", a.google_hints);
        appendToQuery("adsafe", a.google_safe);
        appendToQuery("oe", a.google_encoding);
        appendToQuery("lmt", a.google_last_modified_time);
        appendToQueryEscaped("alternate_ad_url", a.google_alternate_ad_url);
        appendToQuery("alt_color", a.google_alternate_color);
        appendToQuery("skip", a.google_skip);
        appendToQuery("targeting", a.google_targeting);
        var p = a.google_ad_client;
        if( !j[p])
        {
            j[p] =1;
            j.length += 1
        }
        else
        {
            j[p] +=1
        }
        
        if(l[k])
        {
            appendToQueryEscaped("prev_fmts", l[k].toLowerCase());
            if(j.length > 1)
            {
                appendToQuery("slot", j[p])
            }
            
        }
        
        if(adFormat)
        {
            appendToQueryEscaped("format", adFormat.toLowerCase());
            if(l[k])
            {
                l[k] =l[k] +"," + adFormat
            }
            else
            {
                l[k] =adFormat
            }
            
        }
        
        appendToQuery("num_ads", a.google_max_num_ads);
        appendToQuery("output", scriptAdOkay ? "json_html" : a.google_ad_output);
        appendToQuery("adtest", a.google_adtest);
        if(a.google_ad_channel)
        {
            var x = a.google_ad_channel;
            appendToQueryEscaped("channel", x);
            var y = "", z = x.split("+");
            for(var s = 0; s < z.length; s ++) {

                var t = z[s];
                if( !C[t])
                {
                    C[t] =1
                }
                else
                {
                    y += t + "+"
                }
                
            }
            
            appendToQueryEscaped("pv_ch", y)
        }
        
        appendToQueryEscaped("url", a.google_page_url);
        appendColourToQuery("bg", a.google_color_bg, curTime);
        appendColourToQuery("text", a.google_color_text, curTime);
        appendColourToQuery("link", a.google_color_link, curTime);
        appendColourToQuery("url", a.google_color_url, curTime);
        appendColourToQuery("border", a.google_color_border, curTime);
        appendColourToQuery("line", a.google_color_line, curTime);
        appendToQuery("reuse_colors", a.google_reuse_colors);
        appendToQuery("kw_type", a.google_kw_type);
        appendToQueryEscaped("kw", a.google_kw);
        appendToQueryEscaped("contents", a.google_contents);
        appendToQuery("num_radlinks", a.google_num_radlinks);
        appendToQuery("max_radlink_len", a.google_max_radlink_len);
        appendToQuery("rl_filtering", a.google_rl_filtering);
        appendToQuery("rl_mode", a.google_rl_mode);
        appendToQuery("rt", a.google_rt);
        appendToQuery("ad_type", a.google_ad_type);
        appendToQuery("image_size", a.google_image_size);
        appendToQuery("region", a.google_ad_region);
        appendToQuery("feedback_link", a.google_feedback);
        appendToQueryEscaped("ref", a.google_referrer_url);
        appendToQueryEscaped("loc", a.google_page_location);
        appendToQuery("bid", a.google_bid);
        appendToQuery("cust_age", a.google_cust_age);
        appendToQuery("cust_gender", a.google_cust_gender);
        appendToQuery("cust_interests", a.google_cust_interests);
        appendToQuery("cust_id", a.google_cust_id);
        appendToQuery("cust_job", a.google_cust_job);
        appendToQuery("cust_u_url", a.google_cust_u_url);
        appendToQuery("cust_l", a.google_cust_l);
        appendToQuery("cust_lh", a.google_cust_lh);
        appendToQuery("cust_ch", a.google_cust_ch);
        appendToQuery("ed", a.google_ed);
        appendToQueryEscaped("ui", a.google_ui_features);
        if(pageLocationMatches(a, c) &&c.body)
        {
            var A = c.body.scrollHeight, B = c.body.clientHeight;
            if(B && A)
            {
                appendToQueryEscaped("cc", Math.round(B * 100 / A))
            }
            
        }
        
        appendToQuery("google_ad_override", a.google_ad_override);
        appendToQuery("flash", a.google_flash_version);
        appendBrowserCapabilities(a, e);
        if( !scriptAdOkay)
        {
            writeRegularAd(a, c, a.google_ad_url)
        }
        else
        {
            writeScriptAd()
        }
        
        eraseAllVariables(a)
    }
    
    functionbuildQueryUrlAndReturnTrue(b, a, c)
    {
        buildQueryUrl();
        return true
    }
    
    function pageLocationMatches(b, a)
    {
        return b.top.location == a.location
    }

This checks the width and height of the ad. If we don't have at least twice that amount of space available on the page, then it returns false. This is a strange double negative. Someone at google needs to read Code Complete. Also, the page location must be the same as the page that is requesting the ad.

    function addWontFit(b, a)
    {
        var c = a.documentElement;
        if(pageLocationMatches(b, a))return false;
        if(b.google_ad_width && b.google_ad_height)
        {
            var e = 1, f = 1;
            if(b.innerHeight)
            {
                e = b.innerWidth;
                f = b.innerHeight
            }
            else if(c && c.clientHeight)
            {
                e = c.clientWidth;
                f = c.clientHeight
            }
            else if(a.body)
            {
                e = a.body.clientWidth;
                f = a.body.clientHeight
            }
            
            if(f > 2 * b.google_ad_height || e > 2 * b.google_ad_width)
            {
                return false
            }
            
        }
        
        return true
    }
    
    function fillInUnsetVars()
    {
        window.google_org_error_handler = window.onerror;
        window.onerror = buildQueryUrlAndReturnTrue;
        if(window.google_ad_frameborder == null) {
            window.google_ad_frameborder = 0
        }
        
        if(window.google_ad_output == null) {
            window.google_ad_output = "html"
        }
        
        if(window.google_ad_format == null && window.google_ad_output == "html")
        { 
            window.google_ad_format = window.google_ad_width + "x" + window.google_ad_height
        }
        
        if(window.google_page_url == null) {
            window.google_page_url = document.referrer;
            if( !addWontFit(b, document)) {
                window.google_page_url = document.location;
                window.google_last_modified_time = Date.parse(document.lastModified) /1000;
                window.google_referrer_url = document.referrer
            }
            
        } else {
            window.google_page_location = document.referrer;
            if( !adWontFit(b, document)) {
                window.google_page_location = document.location
            }
            
        }
        
        if(window.google_num_slots_by_channel == null) {
            window.google_num_slots_by_channel =[]
        }
        
        if(window.google_num_slots_by_client == null) {
            window.google_num_slots_by_client =[]
        }
        
        if(window.google_prev_ad_formats_by_region == null) {
            window.google_prev_ad_formats_by_region =[]
        }
        
        if(window.google_correlator == null) {
            window.google_correlator =(new Date).getTime()
        }
        
        if(window.google_adslot_loaded == null) {
            window.google_adslot_loaded = { }
            
        }
        
        if(window.google_adContentsBySlot == null) {
            window.google_adContentsBySlot = { }
        }
        
        if(window.google_flash_version == null) {
            window.google_flash_version = getFlashVersion().toString()
        }
        
    }

Here, we are detecting which browser the user has. For example, they might call it with detectBrowser("Firefox"). It caches the result so it can be called many times.

    function detectBrowser(b)
    {
        if(b in q)
        {
            return q[b]
        }
        
        return q[b] =navigator.userAgent.toLowerCase().indexOf(b) !=-1
    }
    
    var q = { } ;

    function isInternetExplorer()
    {
        return detectBrowser("msie") &&!window.opera
    }
    
    function isSarafiBrowser()
    {
        return detectBrowser("safari")
    }
    
    function getInternetExplorerVersion()
    {
        var b = navigator.userAgent, a = b.indexOf("MSIE ");
        if(a ==- 1) {
            return 0
        } else {
            return parseFloat(b.substring(a + 5, b.indexOf(";", a)))
        }
        
    }

This function uses the above to detect if a browser is sufficient to display the ads. It seems we need Intenet Explorer 5, firefox 1.0, Safari 4.12, Operat 9, or Netscape 7.

    function incompatibleBrowser()
    {
        if(isInternetExplorer())
        {
            var b = getInternetExplorerVersion();
            return b <= 5
        }
        else if(detectBrowser("firefox"))
        {
            var a = navigator.userAgent.indexOf("Firefox") +8, c = parseInt(navigator.userAgent.charAt(a)),e = navigator.userAgent.slice(a);
            return c < 1
        }
        else if(isSafariBrowser())
        {
            var a = navigator.userAgent.indexOf("Safari") +7, e = navigator.userAgent.slice(a),f = parseInt(e);
            return f < 412
        }
        else if(window.opera != null)
        {
            var a = navigator.userAgent.indexOf("Opera") +6, e = navigator.userAgent.slice(a),c = parseInt(e);
            return c < 9
        }
        else if( !isInternetExplorer() && !isSafariBrowser() && detectBrowser("mozilla"))
        {
            var a = navigator.userAgent.indexOf("Netscape") +9, e = navigator.userAgent.slice(a),c = parseInt(e);
            return c < 7
        }
        else
        {
            return true
        }
        
    }

I have no idea what this is supposed to be for. O(b, a) returns true only if b is html and a is the string "ca-pub-8954402009499768".

    function O(outputType, clientId)
    {
        if(outputType != "html") {
            return false
        }
        
        var c = { } ;

        c["ca-pub-8954402009499768"] = true;

        return c[clientId] !=null
    }

    function deriveSlotName()
    {
        var b = window;
        b.google_ad_slotname = "_Slot" + b.google_num_ad_slots
    }

This function it an alternative to writeRegularAd() above. It is called instead of writeRegularAd() only under certain circumstances. It can't handle text or html ads, however. Only script ads.

    function writeScriptAd()
    {
        var b = window;
        appendToQueryEscaped("correlator", b.google_correlatorn);
        appendToQuery("impl", "s");
        appendToQuery("callback", "_google_setAdContentsBySlotForSync");

        if( !b.google_ad_slotname) {
            deriveSlotName()
        }
        
        appendToQuery("slotname", b.google_ad_slotname);
        document.write("<script src = '" + b.google_ad_url+"'></script>")
    }

Here, we create a <div> tag with the contents of the ad. This function will be called by the downloaded script.

    window.google_createDivWithContent = function(slot) {
        var id = "google_ads_div_" + slot;
        var div = "<div id=" + id + ">";
        var contents = window.google_adContentsBySlot[slot];

        if(contents == null) {
            return
        }
        
        div += contents._html_;
        div += "n</div>n";
        document.write(div)
    };

This looks for the last script on the web page, and inserts an iframe right after it. The iframe has the given width, height, and contents. Obviously this iframe will contain the ad unit.

    window.google_createDOMIframe = function(id, width, height, contents) {
        var script = document.getElementsByTagName("script");
        var lastScriptOnPage = f[script.length - 1];
        var iframe = document.createElement("iframe");
        iframe.id = "google_ads_iframe_" + id;
        iframe.width = width;
        iframe.height = height;
        iframe.vspace = 0;
        iframe.hspace = 0;
        iframe.allowTransparency = "true";
        iframe.scrolling = "no";
        iframe.marginWidth = 0;
        iframe.marginHeight = 0;
        iframe.frameBorder = 0;
        iframe.style.border = 0;
        lastScriptOnPage.parentNode.appendChild(iframe);
        iframe.contentWindow.document.write(contents);
        iframe.contentWindow.document.close()
    };

This function creates an iframe not using DOM, and writes it directly to the document. You can specify width, height, contents, plus a script function to call when it loads.

    window.google_createiframe = function(id, contents, onLoad, width, height) {
        var divid = "google_ads_div_" + id;
        var frameid = "google_ads_iframe_" + id;
        var div = "<div id=" + i + ">";
        div += "<iframe id=" + frameid + ' width="' +  width+ '" height="' + height + 
            '" vspace="0" hspace="0" allowtransparency="true" scrolling="no" marginwidth="0" marginheight="0" frameborder="0"
            style="border:0px;" src="' + 
            contents.replace( /&/g, "&").replace( /</g, "<").replace( />/g, ">").replace( /"/g, """) +'"';
        div += ' onload="' + onLoad + '"';
        div += "></iframe></div>";
        document.write(div)
    };

This function take an iframe name (which has the format "google_ads_iframe_xxx") and extracts the xxx part, returning the number.

    var iframeNameLength = "google_ads_iframe_".length;
    function getIdFromIFrameName(domTag)
    {
        var attributeValue = domTag.getAttribute("id");
        var c = attibuteValue.substr(iframeNameLength, attributeValue.length - iframeNameLength);
        return c
    }

    
    window._google_syncAdSlotLoaded = function(b)
        {
            var a = getIdFromIFrameName(b);
            window.google_loadiframe(b)
        };

This function looks like it should be called when the ads have finished loading. It takes them from the google_adContentsBySot[] array and creates div and iframe tags for them, with the appropriate content.

    window._google_setAdContentsBySlotForSync = function(slots)
        {
            var a = window;
            for(var slotNum in slots)
            {
                var finishedAd = slots[slotNum];
                a.google_adContentsBySlot[slotNum] = e;
                if(finishedAd == null || finishedAd._empty_) {

                    // nothing to do
                    
                } else if(finishedAd._expandable_) {

                    a.google_createDivWithContent(slotNum)

                } else {

                    if(navigator.userAgent.indexOf("MSIE ") >0)
                    {
                        a.google_adslot_loaded[slotNum] =false;
                        var onLoad = "window._google_syncAdSlotLoaded(this);";
                        a.google_createiframe(slotNum, "about:blank", onLoad, finishedAd._width_, finishedAd._height_)
                    }
                    else
                    {
                        a.google_createDOMIframe(slotNum, finishedAd._width_, finishedAd._height_, finishedAd._html_)
                    }
                }
            }
        };

This is a neat function for splitting a query string into an array. For example, "http://foo.com/?first=steve&last=hanov" would be split into an array with fields "first" and "last", and you can look up the values. Functions like this one exist in all server-side cgi scripts. It is strange to see one on running on the browser, though! It is used for the ad_override functionality.

    function splitQueryString(b) {

        var a = { }

        var c = b.split("?");
        var fields = c[c.length - 1].split("&");

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

            if( i[0] ) {

                try {
                    if ( i.length > 1 ) {
                        if ( window.decodeURIComponent ) {
                            a[ i[0].toLowerCase() ] = decodeURIComponent(i[1].replace( /+/g, " "));
                        } else {
                            a[ i[0].toLowerCase() ] = unescape(i[1]));
                        }
                    } else {
                        a[ i[0].toLowerCase() ] = "";
                    }
                } catch(except) {
                    
                }
                
            }
        }
        
        return a
    }

This is function handles something called a "google_ad_override". If, in the document's address, you have the string ?google_ad_override=blah, it will set the variable google_ad_override to "blah" right in the javascript. Hmm, this means that I can inject code into other pages simply by linking to their URL in a special way. Cool!

    function handleAdOverride()
    {
        var b = window, query = splitQueryString(document.URL);
        if(query["google_ad_override"])
        {
            b.google_ad_override = query["google_ad_override"]
        }
    }
    
    function getFlashVersion()
    {
        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 c = 1;

            while(c) {
                try {
                    c = new ActiveXObject("ShockwaveFlash.ShockwaveFlash." +(b + 1));
                    b ++

                } catch(e) {
                    c = null
                }
            }
            
        } else if(isInternetExplorer()) {
            try
            {
                var c = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7")
            } catch(e) {
                try {
                    var c = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");
                    b = 6;
                    c.AllowScriptAccess = "always"
                } catch(e) {
                    if(b == 6) {
                        return b
                    }
                    
                }
                
                try {
                    c = new ActiveXObject("ShockwaveFlash.ShockwaveFlash")
                } catch(e) {
                    
                }
            }
            
            if(c != null) {
                b = c.GetVariable("$version").split(" ")[1].split(",")[0]
            }
            
        }
        
        return b
    }
    
    handleAdOverride();
    fillInUnsetVars();
    buildQueryUrl();
}

Want more programming tech talk?
Add to Circles on Google Plus
Subscribe to posts

Post comment

Real Name:
Your Email (Not displayed):

Text only. No HTML. If you write "http:" your message will be ignored.
Choose an edit password if you want to be able to edit or delete your comment later.
Editing Password (Optional):

Arthur

2007-09-06 15:39:08
Good article. :)

I'm trying to investigate a strange conflict that occurs when I have AdSense, Amazon.com live previews, and the Prototype JS lib installed at the same time. I appreciate that you've worked out the Google side of it all. :)

The eraseAllVariables() method, called W() in the script, is interesting, in that they could have saved at least 100 or more characters by realizing that JavaScript assignment statements are chained:

function eraseAllVariables(b)

{ b.google_ad_frameborder =

b.google_ad_format =

// etc...

b.google_ui_features = null

}

Cheers :)

Hammad

2010-06-14 07:54:40
Nice B-\

Exactly what I have been looking for. I am working with development of an Ad network same as adsense but my javascript is not that good. Do you think if,in logic, I replace called function buildQueryUrl and modify others as per my requirement, I can re-use this show_ads.js in my software to render Ads on publishers' web pages?

From SW architect's perspective, what could be a better option for showing ads on userpages using RESTful services at backend?

Thanks

Ouroborus

2011-01-02 01:10:44
When you iframe google.com, they auto-unframe it. Makes it kinda hard to view this page.

Dinesh Karki

2011-04-26 01:50:02
I found this a very helpful for me ..nice post

H9N9

2012-03-01 05:50:01
MY adsense page reloads itself, triggering my php hit counter up to 3 times per page load. How can i modify my page to prevent adsense page reloading 3 times?

My 1 ad test page: http://fragmentearth.com/ADSENSE-TEST-PAGE.html

Email
steve.hanov@gmail.com

Other posts by Steve

Yes, You Absolutely Might Possibly Need an EIN to Sell Software to the US How Asana Breaks the Rules About Per-Seat Pricing 5 Ways PowToon Made Me Want to Buy Their Software How I run my business selling software to Americans 0, 1, Many, a Zillion Give your Commodore 64 new life with an SD card reader 20 lines of code that will beat A/B testing every time [comic] Appreciation of xkcd comics vs. technical ability VP trees: A data structure for finding stuff fast Why you should go to the Business of Software Conference Next Year Four ways of handling asynchronous operations in node.js Type-checked CoffeeScript with jzbuild Zero load time file formats Finding the top K items in a list efficiently An instant rhyming dictionary for any web site Succinct Data Structures: Cramming 80,000 words into a Javascript file. Throw away the keys: Easy, Minimal Perfect Hashing Why don't web browsers do this? Fun with Colour Difference Compressing dictionaries with a DAWG Fast and Easy Levenshtein distance using a Trie The Curious Complexity of Being Turned On Cross-domain communication the HTML5 way Five essential steps to prepare for your next programming interview Minimal usable Ubuntu with one command Finding awesome developers in programming interviews Compress your JSON with automatic type extraction JZBUILD - An Easy Javascript Build System Pssst! Want to stream your videos to your iPod? "This is stupid. Your program doesn't work," my wife told me The simple and obvious way to walk through a graph Asking users for steps to reproduce bugs, and other dumb ideas Creating portable binaries on Linux Bending over: How to sell your software to large companies Regular Expression Matching can be Ugly and Slow C++: A language for next generation web apps qb.js: An implementation of QBASIC in Javascript Zwibbler: A simple drawing program using Javascript and Canvas You don't need a project/solution to use the VC++ debugger Boring Date (comic) barcamp (comic) How IE <canvas> tag emulation works I didn't know you could mix and match (comic) Sign here (comic) It's a dirty job... (comic) The PenIsland Problem: Text-to-speech for domain names Pitching to VCs #2 (comic) Building a better rhyming dictionary Does Android team with eccentric geeks? (comic) Comment spam defeated at last Pitching to VCs (comic) How QBASIC almost got me killed Blame the extensions (comic) How to run a linux based home web server Microsoft's generosity knows no end for a year (comic) Using the Acer Aspire One as a web server When programmers design web sites (comic) Finding great ideas for your startup Game Theory, Salary Negotiation, and Programmers Coding tips they don't teach you in school When a reporter mangles your elevator pitch Test Driven Development without Tears Drawing Graphs with Physics Free up disk space in Ubuntu Keeping Abreast of Pornographic Research in Computer Science Exploiting perceptual colour difference for edge detection Experiment: Deleting a post from the Internet Is 2009 the year of Linux malware? Email Etiquette How a programmer reads your resume (comic) How wide should you make your web page? Usability Nightmare: Xfce Settings Manager cairo blur image surface Automatically remove wordiness from your writing Why Perforce is more scalable than Git Optimizing Ubuntu to run from a USB key or SD card UMA Questions Answered Make Windows XP look like Ubuntu, with Spinning Cube Effect See sound without drugs Standby Preventer Stock Picking using Python Spoke.com scam Stackoverflow.com Copy a cairo surface to the windows clipboard Simulating freehand drawing with Cairo Free, Raw Stock Data Installing Ubuntu on the Via Artigo Why are all my lines fuzzy in cairo? A simple command line calculator Tool for Creating UML Sequence Diagrams Exploring sound with Wavelets UMA and free long distance UMA's dirty secrets Installing the Latest Debian on an Ancient Laptop Dissecting Adsense HTML/ Javascript/ CSS Pretty Printer Web Comic Aggregator Experiments in making money online How much cash do celebrities make? Draw waveforms and hear them Cell Phones on Airplanes Detecting C++ memory leaks What does your phone number spell? A Rhyming Engine Rules for Effective C++ Cell Phone Secrets