Upgrade to ExtJS 3.2.0 - Released 03/30/2010
[extjs.git] / src / widgets / chart / swfobject.js
1 /*!
2  * Ext JS Library 3.2.0
3  * Copyright(c) 2006-2010 Ext JS, Inc.
4  * licensing@extjs.com
5  * http://www.extjs.com/license
6  */
7 /*! SWFObject v2.2 <http://code.google.com/p/swfobject/> 
8     is released under the MIT License <http://www.opensource.org/licenses/mit-license.php> 
9 */
10
11 var swfobject = function() {
12     
13     var UNDEF = "undefined",
14         OBJECT = "object",
15         SHOCKWAVE_FLASH = "Shockwave Flash",
16         SHOCKWAVE_FLASH_AX = "ShockwaveFlash.ShockwaveFlash",
17         FLASH_MIME_TYPE = "application/x-shockwave-flash",
18         EXPRESS_INSTALL_ID = "SWFObjectExprInst",
19         ON_READY_STATE_CHANGE = "onreadystatechange",
20         
21         win = window,
22         doc = document,
23         nav = navigator,
24         
25         plugin = false,
26         domLoadFnArr = [main],
27         regObjArr = [],
28         objIdArr = [],
29         listenersArr = [],
30         storedAltContent,
31         storedAltContentId,
32         storedCallbackFn,
33         storedCallbackObj,
34         isDomLoaded = false,
35         isExpressInstallActive = false,
36         dynamicStylesheet,
37         dynamicStylesheetMedia,
38         autoHideShow = true,
39     
40     /* Centralized function for browser feature detection
41         - User agent string detection is only used when no good alternative is possible
42         - Is executed directly for optimal performance
43     */  
44     ua = function() {
45         var w3cdom = typeof doc.getElementById != UNDEF && typeof doc.getElementsByTagName != UNDEF && typeof doc.createElement != UNDEF,
46             u = nav.userAgent.toLowerCase(),
47             p = nav.platform.toLowerCase(),
48             windows = p ? /win/.test(p) : /win/.test(u),
49             mac = p ? /mac/.test(p) : /mac/.test(u),
50             webkit = /webkit/.test(u) ? parseFloat(u.replace(/^.*webkit\/(\d+(\.\d+)?).*$/, "$1")) : false, // returns either the webkit version or false if not webkit
51             ie = !+"\v1", // feature detection based on Andrea Giammarchi's solution: http://webreflection.blogspot.com/2009/01/32-bytes-to-know-if-your-browser-is-ie.html
52             playerVersion = [0,0,0],
53             d = null;
54         if (typeof nav.plugins != UNDEF && typeof nav.plugins[SHOCKWAVE_FLASH] == OBJECT) {
55             d = nav.plugins[SHOCKWAVE_FLASH].description;
56             if (d && !(typeof nav.mimeTypes != UNDEF && nav.mimeTypes[FLASH_MIME_TYPE] && !nav.mimeTypes[FLASH_MIME_TYPE].enabledPlugin)) { // navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin indicates whether plug-ins are enabled or disabled in Safari 3+
57                 plugin = true;
58                 ie = false; // cascaded feature detection for Internet Explorer
59                 d = d.replace(/^.*\s+(\S+\s+\S+$)/, "$1");
60                 playerVersion[0] = parseInt(d.replace(/^(.*)\..*$/, "$1"), 10);
61                 playerVersion[1] = parseInt(d.replace(/^.*\.(.*)\s.*$/, "$1"), 10);
62                 playerVersion[2] = /[a-zA-Z]/.test(d) ? parseInt(d.replace(/^.*[a-zA-Z]+(.*)$/, "$1"), 10) : 0;
63             }
64         }
65         else if (typeof win.ActiveXObject != UNDEF) {
66             try {
67                 var a = new ActiveXObject(SHOCKWAVE_FLASH_AX);
68                 if (a) { // a will return null when ActiveX is disabled
69                     d = a.GetVariable("$version");
70                     if (d) {
71                         ie = true; // cascaded feature detection for Internet Explorer
72                         d = d.split(" ")[1].split(",");
73                         playerVersion = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];
74                     }
75                 }
76             }
77             catch(e) {}
78         }
79         return { w3:w3cdom, pv:playerVersion, wk:webkit, ie:ie, win:windows, mac:mac };
80     }(),
81     
82     /* Cross-browser onDomLoad
83         - Will fire an event as soon as the DOM of a web page is loaded
84         - Internet Explorer workaround based on Diego Perini's solution: http://javascript.nwbox.com/IEContentLoaded/
85         - Regular onload serves as fallback
86     */ 
87     onDomLoad = function() {
88         if (!ua.w3) { return; }
89         if ((typeof doc.readyState != UNDEF && doc.readyState == "complete") || (typeof doc.readyState == UNDEF && (doc.getElementsByTagName("body")[0] || doc.body))) { // function is fired after onload, e.g. when script is inserted dynamically 
90             callDomLoadFunctions();
91         }
92         if (!isDomLoaded) {
93             if (typeof doc.addEventListener != UNDEF) {
94                 doc.addEventListener("DOMContentLoaded", callDomLoadFunctions, false);
95             }       
96             if (ua.ie && ua.win) {
97                 doc.attachEvent(ON_READY_STATE_CHANGE, function() {
98                     if (doc.readyState == "complete") {
99                         doc.detachEvent(ON_READY_STATE_CHANGE, arguments.callee);
100                         callDomLoadFunctions();
101                     }
102                 });
103                 if (win == top) { // if not inside an iframe
104                     (function(){
105                         if (isDomLoaded) { return; }
106                         try {
107                             doc.documentElement.doScroll("left");
108                         }
109                         catch(e) {
110                             setTimeout(arguments.callee, 0);
111                             return;
112                         }
113                         callDomLoadFunctions();
114                     })();
115                 }
116             }
117             if (ua.wk) {
118                 (function(){
119                     if (isDomLoaded) { return; }
120                     if (!/loaded|complete/.test(doc.readyState)) {
121                         setTimeout(arguments.callee, 0);
122                         return;
123                     }
124                     callDomLoadFunctions();
125                 })();
126             }
127             addLoadEvent(callDomLoadFunctions);
128         }
129     }();
130     
131     function callDomLoadFunctions() {
132         if (isDomLoaded) { return; }
133         try { // test if we can really add/remove elements to/from the DOM; we don't want to fire it too early
134             var t = doc.getElementsByTagName("body")[0].appendChild(createElement("span"));
135             t.parentNode.removeChild(t);
136         }
137         catch (e) { return; }
138         isDomLoaded = true;
139         var dl = domLoadFnArr.length;
140         for (var i = 0; i < dl; i++) {
141             domLoadFnArr[i]();
142         }
143     }
144     
145     function addDomLoadEvent(fn) {
146         if (isDomLoaded) {
147             fn();
148         }
149         else { 
150             domLoadFnArr[domLoadFnArr.length] = fn; // Array.push() is only available in IE5.5+
151         }
152     }
153     
154     /* Cross-browser onload
155         - Based on James Edwards' solution: http://brothercake.com/site/resources/scripts/onload/
156         - Will fire an event as soon as a web page including all of its assets are loaded 
157      */
158     function addLoadEvent(fn) {
159         if (typeof win.addEventListener != UNDEF) {
160             win.addEventListener("load", fn, false);
161         }
162         else if (typeof doc.addEventListener != UNDEF) {
163             doc.addEventListener("load", fn, false);
164         }
165         else if (typeof win.attachEvent != UNDEF) {
166             addListener(win, "onload", fn);
167         }
168         else if (typeof win.onload == "function") {
169             var fnOld = win.onload;
170             win.onload = function() {
171                 fnOld();
172                 fn();
173             };
174         }
175         else {
176             win.onload = fn;
177         }
178     }
179     
180     /* Main function
181         - Will preferably execute onDomLoad, otherwise onload (as a fallback)
182     */
183     function main() { 
184         if (plugin) {
185             testPlayerVersion();
186         }
187         else {
188             matchVersions();
189         }
190     }
191     
192     /* Detect the Flash Player version for non-Internet Explorer browsers
193         - Detecting the plug-in version via the object element is more precise than using the plugins collection item's description:
194           a. Both release and build numbers can be detected
195           b. Avoid wrong descriptions by corrupt installers provided by Adobe
196           c. Avoid wrong descriptions by multiple Flash Player entries in the plugin Array, caused by incorrect browser imports
197         - Disadvantage of this method is that it depends on the availability of the DOM, while the plugins collection is immediately available
198     */
199     function testPlayerVersion() {
200         var b = doc.getElementsByTagName("body")[0];
201         var o = createElement(OBJECT);
202         o.setAttribute("type", FLASH_MIME_TYPE);
203         var t = b.appendChild(o);
204         if (t) {
205             var counter = 0;
206             (function(){
207                 if (typeof t.GetVariable != UNDEF) {
208                     var d = t.GetVariable("$version");
209                     if (d) {
210                         d = d.split(" ")[1].split(",");
211                         ua.pv = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];
212                     }
213                 }
214                 else if (counter < 10) {
215                     counter++;
216                     setTimeout(arguments.callee, 10);
217                     return;
218                 }
219                 b.removeChild(o);
220                 t = null;
221                 matchVersions();
222             })();
223         }
224         else {
225             matchVersions();
226         }
227     }
228     
229     /* Perform Flash Player and SWF version matching; static publishing only
230     */
231     function matchVersions() {
232         var rl = regObjArr.length;
233         if (rl > 0) {
234             for (var i = 0; i < rl; i++) { // for each registered object element
235                 var id = regObjArr[i].id;
236                 var cb = regObjArr[i].callbackFn;
237                 var cbObj = {success:false, id:id};
238                 if (ua.pv[0] > 0) {
239                     var obj = getElementById(id);
240                     if (obj) {
241                         if (hasPlayerVersion(regObjArr[i].swfVersion) && !(ua.wk && ua.wk < 312)) { // Flash Player version >= published SWF version: Houston, we have a match!
242                             setVisibility(id, true);
243                             if (cb) {
244                                 cbObj.success = true;
245                                 cbObj.ref = getObjectById(id);
246                                 cb(cbObj);
247                             }
248                         }
249                         else if (regObjArr[i].expressInstall && canExpressInstall()) { // show the Adobe Express Install dialog if set by the web page author and if supported
250                             var att = {};
251                             att.data = regObjArr[i].expressInstall;
252                             att.width = obj.getAttribute("width") || "0";
253                             att.height = obj.getAttribute("height") || "0";
254                             if (obj.getAttribute("class")) { att.styleclass = obj.getAttribute("class"); }
255                             if (obj.getAttribute("align")) { att.align = obj.getAttribute("align"); }
256                             // parse HTML object param element's name-value pairs
257                             var par = {};
258                             var p = obj.getElementsByTagName("param");
259                             var pl = p.length;
260                             for (var j = 0; j < pl; j++) {
261                                 if (p[j].getAttribute("name").toLowerCase() != "movie") {
262                                     par[p[j].getAttribute("name")] = p[j].getAttribute("value");
263                                 }
264                             }
265                             showExpressInstall(att, par, id, cb);
266                         }
267                         else { // Flash Player and SWF version mismatch or an older Webkit engine that ignores the HTML object element's nested param elements: display alternative content instead of SWF
268                             displayAltContent(obj);
269                             if (cb) { cb(cbObj); }
270                         }
271                     }
272                 }
273                 else {  // if no Flash Player is installed or the fp version cannot be detected we let the HTML object element do its job (either show a SWF or alternative content)
274                     setVisibility(id, true);
275                     if (cb) {
276                         var o = getObjectById(id); // test whether there is an HTML object element or not
277                         if (o && typeof o.SetVariable != UNDEF) { 
278                             cbObj.success = true;
279                             cbObj.ref = o;
280                         }
281                         cb(cbObj);
282                     }
283                 }
284             }
285         }
286     }
287     
288     function getObjectById(objectIdStr) {
289         var r = null;
290         var o = getElementById(objectIdStr);
291         if (o && o.nodeName == "OBJECT") {
292             if (typeof o.SetVariable != UNDEF) {
293                 r = o;
294             }
295             else {
296                 var n = o.getElementsByTagName(OBJECT)[0];
297                 if (n) {
298                     r = n;
299                 }
300             }
301         }
302         return r;
303     }
304     
305     /* Requirements for Adobe Express Install
306         - only one instance can be active at a time
307         - fp 6.0.65 or higher
308         - Win/Mac OS only
309         - no Webkit engines older than version 312
310     */
311     function canExpressInstall() {
312         return !isExpressInstallActive && hasPlayerVersion("6.0.65") && (ua.win || ua.mac) && !(ua.wk && ua.wk < 312);
313     }
314     
315     /* Show the Adobe Express Install dialog
316         - Reference: http://www.adobe.com/cfusion/knowledgebase/index.cfm?id=6a253b75
317     */
318     function showExpressInstall(att, par, replaceElemIdStr, callbackFn) {
319         isExpressInstallActive = true;
320         storedCallbackFn = callbackFn || null;
321         storedCallbackObj = {success:false, id:replaceElemIdStr};
322         var obj = getElementById(replaceElemIdStr);
323         if (obj) {
324             if (obj.nodeName == "OBJECT") { // static publishing
325                 storedAltContent = abstractAltContent(obj);
326                 storedAltContentId = null;
327             }
328             else { // dynamic publishing
329                 storedAltContent = obj;
330                 storedAltContentId = replaceElemIdStr;
331             }
332             att.id = EXPRESS_INSTALL_ID;
333             if (typeof att.width == UNDEF || (!/%$/.test(att.width) && parseInt(att.width, 10) < 310)) { att.width = "310"; }
334             if (typeof att.height == UNDEF || (!/%$/.test(att.height) && parseInt(att.height, 10) < 137)) { att.height = "137"; }
335             doc.title = doc.title.slice(0, 47) + " - Flash Player Installation";
336             var pt = ua.ie && ua.win ? "ActiveX" : "PlugIn",
337                 fv = "MMredirectURL=" + win.location.toString().replace(/&/g,"%26") + "&MMplayerType=" + pt + "&MMdoctitle=" + doc.title;
338             if (typeof par.flashvars != UNDEF) {
339                 par.flashvars += "&" + fv;
340             }
341             else {
342                 par.flashvars = fv;
343             }
344             // IE only: when a SWF is loading (AND: not available in cache) wait for the readyState of the object element to become 4 before removing it,
345             // because you cannot properly cancel a loading SWF file without breaking browser load references, also obj.onreadystatechange doesn't work
346             if (ua.ie && ua.win && obj.readyState != 4) {
347                 var newObj = createElement("div");
348                 replaceElemIdStr += "SWFObjectNew";
349                 newObj.setAttribute("id", replaceElemIdStr);
350                 obj.parentNode.insertBefore(newObj, obj); // insert placeholder div that will be replaced by the object element that loads expressinstall.swf
351                 obj.style.display = "none";
352                 (function(){
353                     if (obj.readyState == 4) {
354                         obj.parentNode.removeChild(obj);
355                     }
356                     else {
357                         setTimeout(arguments.callee, 10);
358                     }
359                 })();
360             }
361             createSWF(att, par, replaceElemIdStr);
362         }
363     }
364     
365     /* Functions to abstract and display alternative content
366     */
367     function displayAltContent(obj) {
368         if (ua.ie && ua.win && obj.readyState != 4) {
369             // IE only: when a SWF is loading (AND: not available in cache) wait for the readyState of the object element to become 4 before removing it,
370             // because you cannot properly cancel a loading SWF file without breaking browser load references, also obj.onreadystatechange doesn't work
371             var el = createElement("div");
372             obj.parentNode.insertBefore(el, obj); // insert placeholder div that will be replaced by the alternative content
373             el.parentNode.replaceChild(abstractAltContent(obj), el);
374             obj.style.display = "none";
375             (function(){
376                 if (obj.readyState == 4) {
377                     obj.parentNode.removeChild(obj);
378                 }
379                 else {
380                     setTimeout(arguments.callee, 10);
381                 }
382             })();
383         }
384         else {
385             obj.parentNode.replaceChild(abstractAltContent(obj), obj);
386         }
387     } 
388
389     function abstractAltContent(obj) {
390         var ac = createElement("div");
391         if (ua.win && ua.ie) {
392             ac.innerHTML = obj.innerHTML;
393         }
394         else {
395             var nestedObj = obj.getElementsByTagName(OBJECT)[0];
396             if (nestedObj) {
397                 var c = nestedObj.childNodes;
398                 if (c) {
399                     var cl = c.length;
400                     for (var i = 0; i < cl; i++) {
401                         if (!(c[i].nodeType == 1 && c[i].nodeName == "PARAM") && !(c[i].nodeType == 8)) {
402                             ac.appendChild(c[i].cloneNode(true));
403                         }
404                     }
405                 }
406             }
407         }
408         return ac;
409     }
410     
411     /* Cross-browser dynamic SWF creation
412     */
413     function createSWF(attObj, parObj, id) {
414         var r, el = getElementById(id);
415         if (ua.wk && ua.wk < 312) { return r; }
416         if (el) {
417             if (typeof attObj.id == UNDEF) { // if no 'id' is defined for the object element, it will inherit the 'id' from the alternative content
418                 attObj.id = id;
419             }
420             if (ua.ie && ua.win) { // Internet Explorer + the HTML object element + W3C DOM methods do not combine: fall back to outerHTML
421                 var att = "";
422                 for (var i in attObj) {
423                     if (attObj[i] != Object.prototype[i]) { // filter out prototype additions from other potential libraries
424                         if (i.toLowerCase() == "data") {
425                             parObj.movie = attObj[i];
426                         }
427                         else if (i.toLowerCase() == "styleclass") { // 'class' is an ECMA4 reserved keyword
428                             att += ' class="' + attObj[i] + '"';
429                         }
430                         else if (i.toLowerCase() != "classid") {
431                             att += ' ' + i + '="' + attObj[i] + '"';
432                         }
433                     }
434                 }
435                 var par = "";
436                 for (var j in parObj) {
437                     if (parObj[j] != Object.prototype[j]) { // filter out prototype additions from other potential libraries
438                         par += '<param name="' + j + '" value="' + parObj[j] + '" />';
439                     }
440                 }
441                 el.outerHTML = '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"' + att + '>' + par + '</object>';
442                 objIdArr[objIdArr.length] = attObj.id; // stored to fix object 'leaks' on unload (dynamic publishing only)
443                 r = getElementById(attObj.id);  
444             }
445             else { // well-behaving browsers
446                 var o = createElement(OBJECT);
447                 o.setAttribute("type", FLASH_MIME_TYPE);
448                 for (var m in attObj) {
449                     if (attObj[m] != Object.prototype[m]) { // filter out prototype additions from other potential libraries
450                         if (m.toLowerCase() == "styleclass") { // 'class' is an ECMA4 reserved keyword
451                             o.setAttribute("class", attObj[m]);
452                         }
453                         else if (m.toLowerCase() != "classid") { // filter out IE specific attribute
454                             o.setAttribute(m, attObj[m]);
455                         }
456                     }
457                 }
458                 for (var n in parObj) {
459                     if (parObj[n] != Object.prototype[n] && n.toLowerCase() != "movie") { // filter out prototype additions from other potential libraries and IE specific param element
460                         createObjParam(o, n, parObj[n]);
461                     }
462                 }
463                 el.parentNode.replaceChild(o, el);
464                 r = o;
465             }
466         }
467         return r;
468     }
469     
470     function createObjParam(el, pName, pValue) {
471         var p = createElement("param");
472         p.setAttribute("name", pName);  
473         p.setAttribute("value", pValue);
474         el.appendChild(p);
475     }
476     
477     /* Cross-browser SWF removal
478         - Especially needed to safely and completely remove a SWF in Internet Explorer
479     */
480     function removeSWF(id) {
481         var obj = getElementById(id);
482         if (obj && obj.nodeName == "OBJECT") {
483             if (ua.ie && ua.win) {
484                 obj.style.display = "none";
485                 (function(){
486                     if (obj.readyState == 4) {
487                         removeObjectInIE(id);
488                     }
489                     else {
490                         setTimeout(arguments.callee, 10);
491                     }
492                 })();
493             }
494             else {
495                 obj.parentNode.removeChild(obj);
496             }
497         }
498     }
499     
500     function removeObjectInIE(id) {
501         var obj = getElementById(id);
502         if (obj) {
503             for (var i in obj) {
504                 if (typeof obj[i] == "function") {
505                     obj[i] = null;
506                 }
507             }
508             obj.parentNode.removeChild(obj);
509         }
510     }
511     
512     /* Functions to optimize JavaScript compression
513     */
514     function getElementById(id) {
515         var el = null;
516         try {
517             el = doc.getElementById(id);
518         }
519         catch (e) {}
520         return el;
521     }
522     
523     function createElement(el) {
524         return doc.createElement(el);
525     }
526     
527     /* Updated attachEvent function for Internet Explorer
528         - Stores attachEvent information in an Array, so on unload the detachEvent functions can be called to avoid memory leaks
529     */  
530     function addListener(target, eventType, fn) {
531         target.attachEvent(eventType, fn);
532         listenersArr[listenersArr.length] = [target, eventType, fn];
533     }
534     
535     /* Flash Player and SWF content version matching
536     */
537     function hasPlayerVersion(rv) {
538         var pv = ua.pv, v = rv.split(".");
539         v[0] = parseInt(v[0], 10);
540         v[1] = parseInt(v[1], 10) || 0; // supports short notation, e.g. "9" instead of "9.0.0"
541         v[2] = parseInt(v[2], 10) || 0;
542         return (pv[0] > v[0] || (pv[0] == v[0] && pv[1] > v[1]) || (pv[0] == v[0] && pv[1] == v[1] && pv[2] >= v[2])) ? true : false;
543     }
544     
545     /* Cross-browser dynamic CSS creation
546         - Based on Bobby van der Sluis' solution: http://www.bobbyvandersluis.com/articles/dynamicCSS.php
547     */  
548     function createCSS(sel, decl, media, newStyle) {
549         if (ua.ie && ua.mac) { return; }
550         var h = doc.getElementsByTagName("head")[0];
551         if (!h) { return; } // to also support badly authored HTML pages that lack a head element
552         var m = (media && typeof media == "string") ? media : "screen";
553         if (newStyle) {
554             dynamicStylesheet = null;
555             dynamicStylesheetMedia = null;
556         }
557         if (!dynamicStylesheet || dynamicStylesheetMedia != m) { 
558             // create dynamic stylesheet + get a global reference to it
559             var s = createElement("style");
560             s.setAttribute("type", "text/css");
561             s.setAttribute("media", m);
562             dynamicStylesheet = h.appendChild(s);
563             if (ua.ie && ua.win && typeof doc.styleSheets != UNDEF && doc.styleSheets.length > 0) {
564                 dynamicStylesheet = doc.styleSheets[doc.styleSheets.length - 1];
565             }
566             dynamicStylesheetMedia = m;
567         }
568         // add style rule
569         if (ua.ie && ua.win) {
570             if (dynamicStylesheet && typeof dynamicStylesheet.addRule == OBJECT) {
571                 dynamicStylesheet.addRule(sel, decl);
572             }
573         }
574         else {
575             if (dynamicStylesheet && typeof doc.createTextNode != UNDEF) {
576                 dynamicStylesheet.appendChild(doc.createTextNode(sel + " {" + decl + "}"));
577             }
578         }
579     }
580     
581     function setVisibility(id, isVisible) {
582         if (!autoHideShow) { return; }
583         var v = isVisible ? "visible" : "hidden";
584         if (isDomLoaded && getElementById(id)) {
585             getElementById(id).style.visibility = v;
586         }
587         else {
588             createCSS("#" + id, "visibility:" + v);
589         }
590     }
591
592     /* Filter to avoid XSS attacks
593     */
594     function urlEncodeIfNecessary(s) {
595         var regex = /[\\\"<>\.;]/;
596         var hasBadChars = regex.exec(s) != null;
597         return hasBadChars && typeof encodeURIComponent != UNDEF ? encodeURIComponent(s) : s;
598     }
599     
600     /* Release memory to avoid memory leaks caused by closures, fix hanging audio/video threads and force open sockets/NetConnections to disconnect (Internet Explorer only)
601     */
602     var cleanup = function() {
603         if (ua.ie && ua.win) {
604             window.attachEvent("onunload", function() {
605                 // remove listeners to avoid memory leaks
606                 var ll = listenersArr.length;
607                 for (var i = 0; i < ll; i++) {
608                     listenersArr[i][0].detachEvent(listenersArr[i][1], listenersArr[i][2]);
609                 }
610                 // cleanup dynamically embedded objects to fix audio/video threads and force open sockets and NetConnections to disconnect
611                 var il = objIdArr.length;
612                 for (var j = 0; j < il; j++) {
613                     removeSWF(objIdArr[j]);
614                 }
615                 // cleanup library's main closures to avoid memory leaks
616                 for (var k in ua) {
617                     ua[k] = null;
618                 }
619                 ua = null;
620                 for (var l in swfobject) {
621                     swfobject[l] = null;
622                 }
623                 swfobject = null;
624             });
625         }
626     }();
627     
628     return {
629         /* Public API
630             - Reference: http://code.google.com/p/swfobject/wiki/documentation
631         */ 
632         registerObject: function(objectIdStr, swfVersionStr, xiSwfUrlStr, callbackFn) {
633             if (ua.w3 && objectIdStr && swfVersionStr) {
634                 var regObj = {};
635                 regObj.id = objectIdStr;
636                 regObj.swfVersion = swfVersionStr;
637                 regObj.expressInstall = xiSwfUrlStr;
638                 regObj.callbackFn = callbackFn;
639                 regObjArr[regObjArr.length] = regObj;
640                 setVisibility(objectIdStr, false);
641             }
642             else if (callbackFn) {
643                 callbackFn({success:false, id:objectIdStr});
644             }
645         },
646         
647         getObjectById: function(objectIdStr) {
648             if (ua.w3) {
649                 return getObjectById(objectIdStr);
650             }
651         },
652         
653         embedSWF: function(swfUrlStr, replaceElemIdStr, widthStr, heightStr, swfVersionStr, xiSwfUrlStr, flashvarsObj, parObj, attObj, callbackFn) {
654             var callbackObj = {success:false, id:replaceElemIdStr};
655             if (ua.w3 && !(ua.wk && ua.wk < 312) && swfUrlStr && replaceElemIdStr && widthStr && heightStr && swfVersionStr) {
656                 setVisibility(replaceElemIdStr, false);
657                 addDomLoadEvent(function() {
658                     widthStr += ""; // auto-convert to string
659                     heightStr += "";
660                     var att = {};
661                     if (attObj && typeof attObj === OBJECT) {
662                         for (var i in attObj) { // copy object to avoid the use of references, because web authors often reuse attObj for multiple SWFs
663                             att[i] = attObj[i];
664                         }
665                     }
666                     att.data = swfUrlStr;
667                     att.width = widthStr;
668                     att.height = heightStr;
669                     var par = {}; 
670                     if (parObj && typeof parObj === OBJECT) {
671                         for (var j in parObj) { // copy object to avoid the use of references, because web authors often reuse parObj for multiple SWFs
672                             par[j] = parObj[j];
673                         }
674                     }
675                     if (flashvarsObj && typeof flashvarsObj === OBJECT) {
676                         for (var k in flashvarsObj) { // copy object to avoid the use of references, because web authors often reuse flashvarsObj for multiple SWFs
677                             if (typeof par.flashvars != UNDEF) {
678                                 par.flashvars += "&" + k + "=" + flashvarsObj[k];
679                             }
680                             else {
681                                 par.flashvars = k + "=" + flashvarsObj[k];
682                             }
683                         }
684                     }
685                     if (hasPlayerVersion(swfVersionStr)) { // create SWF
686                         var obj = createSWF(att, par, replaceElemIdStr);
687                         if (att.id == replaceElemIdStr) {
688                             setVisibility(replaceElemIdStr, true);
689                         }
690                         callbackObj.success = true;
691                         callbackObj.ref = obj;
692                     }
693                     else if (xiSwfUrlStr && canExpressInstall()) { // show Adobe Express Install
694                         att.data = xiSwfUrlStr;
695                         showExpressInstall(att, par, replaceElemIdStr, callbackFn);
696                         return;
697                     }
698                     else { // show alternative content
699                         setVisibility(replaceElemIdStr, true);
700                     }
701                     if (callbackFn) { callbackFn(callbackObj); }
702                 });
703             }
704             else if (callbackFn) { callbackFn(callbackObj); }
705         },
706         
707         switchOffAutoHideShow: function() {
708             autoHideShow = false;
709         },
710         
711         ua: ua,
712         
713         getFlashPlayerVersion: function() {
714             return { major:ua.pv[0], minor:ua.pv[1], release:ua.pv[2] };
715         },
716         
717         hasFlashPlayerVersion: hasPlayerVersion,
718         
719         createSWF: function(attObj, parObj, replaceElemIdStr) {
720             if (ua.w3) {
721                 return createSWF(attObj, parObj, replaceElemIdStr);
722             }
723             else {
724                 return undefined;
725             }
726         },
727         
728         showExpressInstall: function(att, par, replaceElemIdStr, callbackFn) {
729             if (ua.w3 && canExpressInstall()) {
730                 showExpressInstall(att, par, replaceElemIdStr, callbackFn);
731             }
732         },
733         
734         removeSWF: function(objElemIdStr) {
735             if (ua.w3) {
736                 removeSWF(objElemIdStr);
737             }
738         },
739         
740         createCSS: function(selStr, declStr, mediaStr, newStyleBoolean) {
741             if (ua.w3) {
742                 createCSS(selStr, declStr, mediaStr, newStyleBoolean);
743             }
744         },
745         
746         addDomLoadEvent: addDomLoadEvent,
747         
748         addLoadEvent: addLoadEvent,
749         
750         getQueryParamValue: function(param) {
751             var q = doc.location.search || doc.location.hash;
752             if (q) {
753                 if (/\?/.test(q)) { q = q.split("?")[1]; } // strip question mark
754                 if (param == null) {
755                     return urlEncodeIfNecessary(q);
756                 }
757                 var pairs = q.split("&");
758                 for (var i = 0; i < pairs.length; i++) {
759                     if (pairs[i].substring(0, pairs[i].indexOf("=")) == param) {
760                         return urlEncodeIfNecessary(pairs[i].substring((pairs[i].indexOf("=") + 1)));
761                     }
762                 }
763             }
764             return "";
765         },
766         
767         // For internal usage only
768         expressInstallCallback: function() {
769             if (isExpressInstallActive) {
770                 var obj = getElementById(EXPRESS_INSTALL_ID);
771                 if (obj && storedAltContent) {
772                     obj.parentNode.replaceChild(storedAltContent, obj);
773                     if (storedAltContentId) {
774                         setVisibility(storedAltContentId, true);
775                         if (ua.ie && ua.win) { storedAltContent.style.display = "block"; }
776                     }
777                     if (storedCallbackFn) { storedCallbackFn(storedCallbackObj); }
778                 }
779                 isExpressInstallActive = false;
780             } 
781         }
782     };
783 }();