Upgrade to ExtJS 3.3.1 - Released 11/30/2010
[extjs.git] / src / widgets / chart / swfobject.js
1 /*!
2  * Ext JS Library 3.3.1
3  * Copyright(c) 2006-2010 Sencha Inc.
4  * licensing@sencha.com
5  * http://www.sencha.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)) {
334                 att.width = "310";
335             }
336             
337             if (typeof att.height == UNDEF || (!(/%$/).test(att.height) && parseInt(att.height, 10) < 137)) {
338                 att.height = "137";
339             }
340             doc.title = doc.title.slice(0, 47) + " - Flash Player Installation";
341             var pt = ua.ie && ua.win ? "ActiveX" : "PlugIn",
342                 fv = "MMredirectURL=" + win.location.toString().replace(/&/g,"%26") + "&MMplayerType=" + pt + "&MMdoctitle=" + doc.title;
343             if (typeof par.flashvars != UNDEF) {
344                 par.flashvars += "&" + fv;
345             }
346             else {
347                 par.flashvars = fv;
348             }
349             // 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,
350             // because you cannot properly cancel a loading SWF file without breaking browser load references, also obj.onreadystatechange doesn't work
351             if (ua.ie && ua.win && obj.readyState != 4) {
352                 var newObj = createElement("div");
353                 replaceElemIdStr += "SWFObjectNew";
354                 newObj.setAttribute("id", replaceElemIdStr);
355                 obj.parentNode.insertBefore(newObj, obj); // insert placeholder div that will be replaced by the object element that loads expressinstall.swf
356                 obj.style.display = "none";
357                 (function(){
358                     if (obj.readyState == 4) {
359                         obj.parentNode.removeChild(obj);
360                     }
361                     else {
362                         setTimeout(arguments.callee, 10);
363                     }
364                 })();
365             }
366             createSWF(att, par, replaceElemIdStr);
367         }
368     }
369     
370     /* Functions to abstract and display alternative content
371     */
372     function displayAltContent(obj) {
373         if (ua.ie && ua.win && obj.readyState != 4) {
374             // 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,
375             // because you cannot properly cancel a loading SWF file without breaking browser load references, also obj.onreadystatechange doesn't work
376             var el = createElement("div");
377             obj.parentNode.insertBefore(el, obj); // insert placeholder div that will be replaced by the alternative content
378             el.parentNode.replaceChild(abstractAltContent(obj), el);
379             obj.style.display = "none";
380             (function(){
381                 if (obj.readyState == 4) {
382                     obj.parentNode.removeChild(obj);
383                 }
384                 else {
385                     setTimeout(arguments.callee, 10);
386                 }
387             })();
388         }
389         else {
390             obj.parentNode.replaceChild(abstractAltContent(obj), obj);
391         }
392     } 
393
394     function abstractAltContent(obj) {
395         var ac = createElement("div");
396         if (ua.win && ua.ie) {
397             ac.innerHTML = obj.innerHTML;
398         }
399         else {
400             var nestedObj = obj.getElementsByTagName(OBJECT)[0];
401             if (nestedObj) {
402                 var c = nestedObj.childNodes;
403                 if (c) {
404                     var cl = c.length;
405                     for (var i = 0; i < cl; i++) {
406                         if (!(c[i].nodeType == 1 && c[i].nodeName == "PARAM") && !(c[i].nodeType == 8)) {
407                             ac.appendChild(c[i].cloneNode(true));
408                         }
409                     }
410                 }
411             }
412         }
413         return ac;
414     }
415     
416     /* Cross-browser dynamic SWF creation
417     */
418     function createSWF(attObj, parObj, id) {
419         var r, el = getElementById(id);
420         if (ua.wk && ua.wk < 312) { return r; }
421         if (el) {
422             if (typeof attObj.id == UNDEF) { // if no 'id' is defined for the object element, it will inherit the 'id' from the alternative content
423                 attObj.id = id;
424             }
425             if (ua.ie && ua.win) { // Internet Explorer + the HTML object element + W3C DOM methods do not combine: fall back to outerHTML
426                 var att = "";
427                 for (var i in attObj) {
428                     if (attObj[i] != Object.prototype[i]) { // filter out prototype additions from other potential libraries
429                         if (i.toLowerCase() == "data") {
430                             parObj.movie = attObj[i];
431                         }
432                         else if (i.toLowerCase() == "styleclass") { // 'class' is an ECMA4 reserved keyword
433                             att += ' class="' + attObj[i] + '"';
434                         }
435                         else if (i.toLowerCase() != "classid") {
436                             att += ' ' + i + '="' + attObj[i] + '"';
437                         }
438                     }
439                 }
440                 var par = "";
441                 for (var j in parObj) {
442                     if (parObj[j] != Object.prototype[j]) { // filter out prototype additions from other potential libraries
443                         par += '<param name="' + j + '" value="' + parObj[j] + '" />';
444                     }
445                 }
446                 el.outerHTML = '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"' + att + '>' + par + '</object>';
447                 objIdArr[objIdArr.length] = attObj.id; // stored to fix object 'leaks' on unload (dynamic publishing only)
448                 r = getElementById(attObj.id);  
449             }
450             else { // well-behaving browsers
451                 var o = createElement(OBJECT);
452                 o.setAttribute("type", FLASH_MIME_TYPE);
453                 for (var m in attObj) {
454                     if (attObj[m] != Object.prototype[m]) { // filter out prototype additions from other potential libraries
455                         if (m.toLowerCase() == "styleclass") { // 'class' is an ECMA4 reserved keyword
456                             o.setAttribute("class", attObj[m]);
457                         }
458                         else if (m.toLowerCase() != "classid") { // filter out IE specific attribute
459                             o.setAttribute(m, attObj[m]);
460                         }
461                     }
462                 }
463                 for (var n in parObj) {
464                     if (parObj[n] != Object.prototype[n] && n.toLowerCase() != "movie") { // filter out prototype additions from other potential libraries and IE specific param element
465                         createObjParam(o, n, parObj[n]);
466                     }
467                 }
468                 el.parentNode.replaceChild(o, el);
469                 r = o;
470             }
471         }
472         return r;
473     }
474     
475     function createObjParam(el, pName, pValue) {
476         var p = createElement("param");
477         p.setAttribute("name", pName);  
478         p.setAttribute("value", pValue);
479         el.appendChild(p);
480     }
481     
482     /* Cross-browser SWF removal
483         - Especially needed to safely and completely remove a SWF in Internet Explorer
484     */
485     function removeSWF(id) {
486         var obj = getElementById(id);
487         if (obj && obj.nodeName == "OBJECT") {
488             if (ua.ie && ua.win) {
489                 obj.style.display = "none";
490                 (function(){
491                     if (obj.readyState == 4) {
492                         removeObjectInIE(id);
493                     }
494                     else {
495                         setTimeout(arguments.callee, 10);
496                     }
497                 })();
498             }
499             else {
500                 obj.parentNode.removeChild(obj);
501             }
502         }
503     }
504     
505     function removeObjectInIE(id) {
506         var obj = getElementById(id);
507         if (obj) {
508             for (var i in obj) {
509                 if (typeof obj[i] == "function") {
510                     obj[i] = null;
511                 }
512             }
513             obj.parentNode.removeChild(obj);
514         }
515     }
516     
517     /* Functions to optimize JavaScript compression
518     */
519     function getElementById(id) {
520         var el = null;
521         try {
522             el = doc.getElementById(id);
523         }
524         catch (e) {}
525         return el;
526     }
527     
528     function createElement(el) {
529         return doc.createElement(el);
530     }
531     
532     /* Updated attachEvent function for Internet Explorer
533         - Stores attachEvent information in an Array, so on unload the detachEvent functions can be called to avoid memory leaks
534     */  
535     function addListener(target, eventType, fn) {
536         target.attachEvent(eventType, fn);
537         listenersArr[listenersArr.length] = [target, eventType, fn];
538     }
539     
540     /* Flash Player and SWF content version matching
541     */
542     function hasPlayerVersion(rv) {
543         var pv = ua.pv, v = rv.split(".");
544         v[0] = parseInt(v[0], 10);
545         v[1] = parseInt(v[1], 10) || 0; // supports short notation, e.g. "9" instead of "9.0.0"
546         v[2] = parseInt(v[2], 10) || 0;
547         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;
548     }
549     
550     /* Cross-browser dynamic CSS creation
551         - Based on Bobby van der Sluis' solution: http://www.bobbyvandersluis.com/articles/dynamicCSS.php
552     */  
553     function createCSS(sel, decl, media, newStyle) {
554         if (ua.ie && ua.mac) { return; }
555         var h = doc.getElementsByTagName("head")[0];
556         if (!h) { return; } // to also support badly authored HTML pages that lack a head element
557         var m = (media && typeof media == "string") ? media : "screen";
558         if (newStyle) {
559             dynamicStylesheet = null;
560             dynamicStylesheetMedia = null;
561         }
562         if (!dynamicStylesheet || dynamicStylesheetMedia != m) { 
563             // create dynamic stylesheet + get a global reference to it
564             var s = createElement("style");
565             s.setAttribute("type", "text/css");
566             s.setAttribute("media", m);
567             dynamicStylesheet = h.appendChild(s);
568             if (ua.ie && ua.win && typeof doc.styleSheets != UNDEF && doc.styleSheets.length > 0) {
569                 dynamicStylesheet = doc.styleSheets[doc.styleSheets.length - 1];
570             }
571             dynamicStylesheetMedia = m;
572         }
573         // add style rule
574         if (ua.ie && ua.win) {
575             if (dynamicStylesheet && typeof dynamicStylesheet.addRule == OBJECT) {
576                 dynamicStylesheet.addRule(sel, decl);
577             }
578         }
579         else {
580             if (dynamicStylesheet && typeof doc.createTextNode != UNDEF) {
581                 dynamicStylesheet.appendChild(doc.createTextNode(sel + " {" + decl + "}"));
582             }
583         }
584     }
585     
586     function setVisibility(id, isVisible) {
587         if (!autoHideShow) { return; }
588         var v = isVisible ? "visible" : "hidden";
589         if (isDomLoaded && getElementById(id)) {
590             getElementById(id).style.visibility = v;
591         }
592         else {
593             createCSS("#" + id, "visibility:" + v);
594         }
595     }
596
597     /* Filter to avoid XSS attacks
598     */
599     function urlEncodeIfNecessary(s) {
600         var regex = /[\\\"<>\.;]/;
601         var hasBadChars = regex.exec(s) != null;
602         return hasBadChars && typeof encodeURIComponent != UNDEF ? encodeURIComponent(s) : s;
603     }
604     
605     /* Release memory to avoid memory leaks caused by closures, fix hanging audio/video threads and force open sockets/NetConnections to disconnect (Internet Explorer only)
606     */
607     var cleanup = function() {
608         if (ua.ie && ua.win) {
609             window.attachEvent("onunload", function() {
610                 // remove listeners to avoid memory leaks
611                 var ll = listenersArr.length;
612                 for (var i = 0; i < ll; i++) {
613                     listenersArr[i][0].detachEvent(listenersArr[i][1], listenersArr[i][2]);
614                 }
615                 // cleanup dynamically embedded objects to fix audio/video threads and force open sockets and NetConnections to disconnect
616                 var il = objIdArr.length;
617                 for (var j = 0; j < il; j++) {
618                     removeSWF(objIdArr[j]);
619                 }
620                 // cleanup library's main closures to avoid memory leaks
621                 for (var k in ua) {
622                     ua[k] = null;
623                 }
624                 ua = null;
625                 for (var l in swfobject) {
626                     swfobject[l] = null;
627                 }
628                 swfobject = null;
629             });
630         }
631     }();
632     
633     return {
634         /* Public API
635             - Reference: http://code.google.com/p/swfobject/wiki/documentation
636         */ 
637         registerObject: function(objectIdStr, swfVersionStr, xiSwfUrlStr, callbackFn) {
638             if (ua.w3 && objectIdStr && swfVersionStr) {
639                 var regObj = {};
640                 regObj.id = objectIdStr;
641                 regObj.swfVersion = swfVersionStr;
642                 regObj.expressInstall = xiSwfUrlStr;
643                 regObj.callbackFn = callbackFn;
644                 regObjArr[regObjArr.length] = regObj;
645                 setVisibility(objectIdStr, false);
646             }
647             else if (callbackFn) {
648                 callbackFn({success:false, id:objectIdStr});
649             }
650         },
651         
652         getObjectById: function(objectIdStr) {
653             if (ua.w3) {
654                 return getObjectById(objectIdStr);
655             }
656         },
657         
658         embedSWF: function(swfUrlStr, replaceElemIdStr, widthStr, heightStr, swfVersionStr, xiSwfUrlStr, flashvarsObj, parObj, attObj, callbackFn) {
659             var callbackObj = {success:false, id:replaceElemIdStr};
660             if (ua.w3 && !(ua.wk && ua.wk < 312) && swfUrlStr && replaceElemIdStr && widthStr && heightStr && swfVersionStr) {
661                 setVisibility(replaceElemIdStr, false);
662                 addDomLoadEvent(function() {
663                     widthStr += ""; // auto-convert to string
664                     heightStr += "";
665                     var att = {};
666                     if (attObj && typeof attObj === OBJECT) {
667                         for (var i in attObj) { // copy object to avoid the use of references, because web authors often reuse attObj for multiple SWFs
668                             att[i] = attObj[i];
669                         }
670                     }
671                     att.data = swfUrlStr;
672                     att.width = widthStr;
673                     att.height = heightStr;
674                     var par = {}; 
675                     if (parObj && typeof parObj === OBJECT) {
676                         for (var j in parObj) { // copy object to avoid the use of references, because web authors often reuse parObj for multiple SWFs
677                             par[j] = parObj[j];
678                         }
679                     }
680                     if (flashvarsObj && typeof flashvarsObj === OBJECT) {
681                         for (var k in flashvarsObj) { // copy object to avoid the use of references, because web authors often reuse flashvarsObj for multiple SWFs
682                             if (typeof par.flashvars != UNDEF) {
683                                 par.flashvars += "&" + k + "=" + flashvarsObj[k];
684                             }
685                             else {
686                                 par.flashvars = k + "=" + flashvarsObj[k];
687                             }
688                         }
689                     }
690                     if (hasPlayerVersion(swfVersionStr)) { // create SWF
691                         var obj = createSWF(att, par, replaceElemIdStr);
692                         if (att.id == replaceElemIdStr) {
693                             setVisibility(replaceElemIdStr, true);
694                         }
695                         callbackObj.success = true;
696                         callbackObj.ref = obj;
697                     }
698                     else if (xiSwfUrlStr && canExpressInstall()) { // show Adobe Express Install
699                         att.data = xiSwfUrlStr;
700                         showExpressInstall(att, par, replaceElemIdStr, callbackFn);
701                         return;
702                     }
703                     else { // show alternative content
704                         setVisibility(replaceElemIdStr, true);
705                     }
706                     if (callbackFn) { callbackFn(callbackObj); }
707                 });
708             }
709             else if (callbackFn) { callbackFn(callbackObj); }
710         },
711         
712         switchOffAutoHideShow: function() {
713             autoHideShow = false;
714         },
715         
716         ua: ua,
717         
718         getFlashPlayerVersion: function() {
719             return { major:ua.pv[0], minor:ua.pv[1], release:ua.pv[2] };
720         },
721         
722         hasFlashPlayerVersion: hasPlayerVersion,
723         
724         createSWF: function(attObj, parObj, replaceElemIdStr) {
725             if (ua.w3) {
726                 return createSWF(attObj, parObj, replaceElemIdStr);
727             }
728             else {
729                 return undefined;
730             }
731         },
732         
733         showExpressInstall: function(att, par, replaceElemIdStr, callbackFn) {
734             if (ua.w3 && canExpressInstall()) {
735                 showExpressInstall(att, par, replaceElemIdStr, callbackFn);
736             }
737         },
738         
739         removeSWF: function(objElemIdStr) {
740             if (ua.w3) {
741                 removeSWF(objElemIdStr);
742             }
743         },
744         
745         createCSS: function(selStr, declStr, mediaStr, newStyleBoolean) {
746             if (ua.w3) {
747                 createCSS(selStr, declStr, mediaStr, newStyleBoolean);
748             }
749         },
750         
751         addDomLoadEvent: addDomLoadEvent,
752         
753         addLoadEvent: addLoadEvent,
754         
755         getQueryParamValue: function(param) {
756             var q = doc.location.search || doc.location.hash;
757             if (q) {
758                 if (/\?/.test(q)) { q = q.split("?")[1]; } // strip question mark
759                 if (param == null) {
760                     return urlEncodeIfNecessary(q);
761                 }
762                 var pairs = q.split("&");
763                 for (var i = 0; i < pairs.length; i++) {
764                     if (pairs[i].substring(0, pairs[i].indexOf("=")) == param) {
765                         return urlEncodeIfNecessary(pairs[i].substring((pairs[i].indexOf("=") + 1)));
766                     }
767                 }
768             }
769             return "";
770         },
771         
772         // For internal usage only
773         expressInstallCallback: function() {
774             if (isExpressInstallActive) {
775                 var obj = getElementById(EXPRESS_INSTALL_ID);
776                 if (obj && storedAltContent) {
777                     obj.parentNode.replaceChild(storedAltContent, obj);
778                     if (storedAltContentId) {
779                         setVisibility(storedAltContentId, true);
780                         if (ua.ie && ua.win) { storedAltContent.style.display = "block"; }
781                     }
782                     if (storedCallbackFn) { storedCallbackFn(storedCallbackObj); }
783                 }
784                 isExpressInstallActive = false;
785             } 
786         }
787     };
788 }();