Another fix to the render-order, for from document.write() collected output
[openx] / openx.js
1 /** Optimized methods for fetching ad-banners via OpenX */
2
3 /** see: http://enterprisejquery.com/2010/10/how-good-c-habits-can-encourage-bad-javascript-habits-part-1/ */
4
5 (function( openx, $, undefined ) {
6
7   var
8
9   id,
10   node,
11
12   count = 0,
13   slots = {},
14   queue = [],
15   ads = [],
16   output = [];
17
18
19   openx.show_ads = function(server, zones) {
20
21     var
22     domain = document.location.protocol == 'https:' ? 'https://' + server + ':8443':'http://' + server,
23     name,
24     src = domain;
25
26     document.write = document_write;
27     document.writeln = document_write;
28
29     src += "/www/delivery/spc.php?zones=";
30
31     /** Only fetch banners, that are really included in this page */
32     for(name in zones) {
33       $('.oa').each(function() {
34         var
35         node = $(this),
36         id;
37         if (node.hasClass(name)) {
38           id = 'oa_' + ++count;
39           slots[id] = node;
40           queue.push(id);
41           src += escape(id + '=' + zones[name] + "|");
42         }
43       });
44     }
45
46     src += "&nz=1&source=" + escape(OA_source);
47     src += "&r=" + Math.floor(Math.random()*99999999);
48     src += "&block=1&charset=UTF-8";
49
50     if (window.location)   src += "&loc=" + escape(window.location);
51     if (document.referrer) src += "&referer=" + escape(document.referrer);
52
53     $.getScript(src, init_ads);
54
55     src = domain + '/www/delivery/fl.js';
56     $.getScript(src);
57
58   }
59
60   function init_ads() {
61
62     var i, id;
63     for (i=0; i<queue.length; i++) {
64       id = queue[i];
65       if (typeof(OA_output[id]) != 'undefined' && OA_output[id] != '')
66         ads.push(id);
67     }
68
69     render_ads();
70
71   }
72
73   function render_ads() {
74
75     while (ads.length > 0) {
76
77       var result, src, inline, i;
78
79       id = ads.shift();
80       node = slots[id];
81
82       node.slideDown();
83
84       // node.append(id + ": " + node.attr('class'));
85
86       /**
87        * If output was added via document.write(), this output must be
88        * rendered before other banner-code from the OpenX-server is rendered!
89        */
90       insert_output();
91
92       while ((result = /<script/i.exec(OA_output[id])) != null) {
93         node.append(OA_output[id].slice(0,result.index));
94         /** Strip all text before "<script" from OA_output[id] */
95         OA_output[id] = OA_output[id].slice(result.index,OA_output[id].length);
96         result = /^([^>]*)>([\s\S]*?)<\\?\/script>/i.exec(OA_output[id]);
97         if (result == null) {
98           /** Invalid syntax in delivered banner-code: ignoring the rest of this banner-code! */
99           // alert(OA_output[id]);
100           OA_output[id] = "";
101         }
102         else {
103           /** Remember iinline-code, if present */
104           src = result[1]
105           inline = result[2];
106           /** Strip all text up to and including "</script>" from OA_output[id] */
107           OA_output[id] = OA_output[id].slice(result[0].length,OA_output[id].length);
108           result = /src\s*=\s*['"]([^'"]*)['"]/i.exec(src);
109           if (result == null) {
110             /** script-tag with inline-code: execute inline-code! */
111             result = /^\s*<.*$/m.exec(inline);
112             if (result != null) {
113               /** Remove leading HTML-comments, because IE will stumble otherwise */
114               inline = inline.slice(result[0].length,inline.length);
115             }
116             $.globalEval(inline);
117             insert_output(); // << The executed inline-code might have called document.write()!
118           }
119           else {
120             /** script-tag with src-URL! */
121             ads.unshift(id); // << The banner might not be rendered fully, or include more calls to document.write().
122             /** Load the script and halt all work until the script is loaded and executed... */
123             $.getScript(result[1], render_ads); // << jQuery.getScript() generates onload-Handler for _all_ browsers ;)
124             return;
125           }
126         }
127       }
128
129       node.append(OA_output[id]);
130       OA_output[id] = "";
131     }
132
133     /** All entries from OA_output were rendered */
134
135     id = undefined;
136     node = undefined;
137   }
138
139   /** This function is used to overwrite document.write and document.writeln */
140   function document_write() {
141
142     for (var i=0; i<arguments.length; i++)
143       output.push(arguments[i]);
144
145     if (id != ads[0])
146       /**
147        * Re-Add the last banner-code to the working-queue, because included
148        * scripts had added markup via document.write(), which is not
149        * proccessed yet.
150        * Otherwise the added markup would be falsely rendered together with
151        * the markup from the following banner-code.
152        */
153       ads.unshift(id);
154
155   }
156
157   /**
158    * This function prepends the collected output from calls to
159    * document_write() to the current banner-code.
160    */
161   function insert_output() {
162
163     if (output.length > 0) {
164       output.push(OA_output[id]);
165       OA_output[id] = "";
166       for (i=0; i<output.length; i++)
167         OA_output[id] += output[i];
168       output = [];
169     }
170
171   }
172
173 } ( window.openx = window.openx || {}, jQuery ));
174
175 var OA_output = {}; // << Needed, because IE will complain loudly otherwise!