Fixed an error in the ordering, output added via document.write is rendered
[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       if (output.length > 0) {
91         output.push(OA_output[id]);
92         OA_output[id] = "";
93         for (i=0; i<output.length; i++)
94           OA_output[id] += output[i];
95         output = [];
96       }
97
98       while ((result = /<script/i.exec(OA_output[id])) != null) {
99         node.append(OA_output[id].slice(0,result.index));
100         /** Strip all text before "<script" from OA_output[id] */
101         OA_output[id] = OA_output[id].slice(result.index,OA_output[id].length);
102         result = /^([^>]*)>([\s\S]*?)<\\?\/script>/i.exec(OA_output[id]);
103         if (result == null) {
104           /** Invalid syntax in delivered banner-code: ignoring the rest of this banner-code! */
105           // alert(OA_output[id]);
106           OA_output[id] = "";
107         }
108         else {
109           /** Remember iinline-code, if present */
110           src = result[1]
111           inline = result[2];
112           /** Strip all text up to and including "</script>" from OA_output[id] */
113           OA_output[id] = OA_output[id].slice(result[0].length,OA_output[id].length);
114           result = /src\s*=\s*['"]([^'"]*)['"]/i.exec(src);
115           if (result == null) {
116             /** script-tag with inline-code: execute inline-code! */
117             result = /^\s*<.*$/m.exec(inline);
118             if (result != null) {
119               /** Remove leading HTML-comments, because IE will stumble otherwise */
120               inline = inline.slice(result[0].length,inline.length);
121             }
122             $.globalEval(inline);
123           }
124           else {
125             /** script-tag with src-URL! */
126             ads.unshift(id); // << The banner might not be rendered fully, or include more calls to document.write().
127             /** Load the script and halt all work until the script is loaded and executed... */
128             $.getScript(result[1], render_ads); // << jQuery.getScript() generates onload-Handler for _all_ browsers ;)
129             return;
130           }
131         }
132       }
133
134       node.append(OA_output[id]);
135       OA_output[id] = "";
136     }
137
138     /** All entries from OA_output were rendered */
139
140     id = undefined;
141     node = undefined;
142   }
143
144   function document_write() {
145
146     for (var i=0; i<arguments.length; i++)
147       output.push(arguments[i]);
148
149     if (id != ads[0])
150       /**
151        * Re-Add the last banner-code to the working-queue, because included
152        * scripts had added markup via document.write(), which is not
153        * proccessed yet.
154        * Otherwise the added markup would be falsely rendered together with
155        * the markup from the following banner-code.
156        */
157       ads.unshift(id);
158
159   }
160
161 } ( window.openx = window.openx || {}, jQuery ));
162
163 var OA_output = {}; // << Needed, because IE will complain loudly otherwise!