jfed: added the js locally
[unfold.git] / portal / static / js / fed4fire_dtjava_orig.js
1 /*
2  * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25
26 /**
27   The Java Deployment Toolkit is utility to deploy Java content in
28   the browser as applets or applications using right version of Java.
29   If needed it can initiate upgrade of user's system to install required
30   components of Java platform.
31   <p>
32   Note that some of Deployment Toolkit methods may not be fully operational if
33   used before web page body is loaded (because DT native plugins could not be instantiated).
34   If you intend to use it before web page DOM tree is ready then dtjava.js
35   need to be loaded inside the body element of the page and before use of other DT APIs.
36
37   @module java/deployment_toolkit
38 */
39 var dtjava = function() {
40     function notNull(o) {
41         return (o != undefined && o != null);
42     }
43
44     function isDef(fn) {
45         return (fn != null && typeof fn != "undefined");
46     }
47
48     //return true if any of patterns from query list is found in the given string
49     function containsAny(lst, str) {
50         for (var q = 0; q < lst.length; q++) {
51             if (str.indexOf(lst[q]) != -1) {
52                 return true;
53             }
54         }
55         return false;
56     }
57
58     /* Location of static web content - images, javascript files. */
59     var jscodebase =  (function () {
60         // <script> elements are added to the DOM and run synchronously,
61         // the currently running script will also be the last element in the array
62         var scripts = document.getElementsByTagName("script");
63         var src = scripts[scripts.length - 1].getAttribute("src");
64         return src.substring(0, src.lastIndexOf('/') + 1);
65     })();
66
67     //set to true to disable FX auto install (before release)
68     var noFXAutoInstall = false;
69     
70     // JRE version we start to have JRE and FX true co-bundle
71     var minJRECobundleVersion = "1.7.0_06";
72
73     //aliases
74     var d = document;
75     var w = window;
76
77     var cbDone = false;  //done with onload callbacks
78     var domCb = [];      //list of callbacks
79     var ua = null;
80
81     //add function to be called on DOM ready event
82     function addOnDomReady(fn) {
83         if (cbDone) {
84             fn();
85         } else {
86             domCb[domCb.length] = fn;
87         }
88     }
89
90     //invoke pending onload callbacks
91     function invokeCallbacks() {
92         if (!cbDone) {
93             //swfoject.js tests whether DOM is actually ready first
94             //  in order to not fire too early. Use same heuristic
95             try {
96                 var t = d.getElementsByTagName("body")[0].appendChild(
97                     d.createElement("div"));
98                 t.parentNode.removeChild(t);
99             } catch (e) {
100                 return;
101             }
102             cbDone = true;
103             for (var i = 0; i < domCb.length; i++) {
104                 domCb[i]();
105             }
106         }
107     }
108
109     //cross browser onload support.
110     //Derived from swfobject.js
111     function addOnload(fn) {
112         if (isDef(w.addEventListener)) {
113             w.addEventListener("load", fn, false);
114         } else if (isDef(d.addEventListener)) {
115             d.addEventListener("load", fn, false);
116         } else if (isDef(w.attachEvent)) {
117             w.attachEvent("onload", fn);
118             //TODO: swfobject also keeps references to the listeners to detach them on onload
119             // to avoid memory leaks ...
120         } else if (typeof w.onload == "function") {
121             var fnOld = w.onload;
122             w.onload = function() {
123                 fnOld();
124                 fn();
125             };
126         } else {
127             w.onload = fn;
128         }
129     }
130
131     function detectEnv() {
132         var dom = isDef(d.getElementById) && isDef(d.getElementsByTagName) && isDef(d.createElement);
133         var u = navigator.userAgent.toLowerCase(),
134             p = navigator.platform.toLowerCase();
135
136         //NB: may need to be refined as some user agent may contain strings related to other browsers
137         //   (e.g. Chrome has both Safari and mozilla, Safari also has mozilla
138         var windows = p ? /win/.test(p) : /win/.test(u),
139             mac = p ? /mac/.test(p) : /mac/.test(u),
140             linux = p ? /linux/.test(p) : /linux/.test(u),
141             chrome = /chrome/.test(u),
142             // get webkit version or false if not webkit
143             webkit = !chrome && /webkit/.test(u) ?
144                 parseFloat(u.replace(/^.*webkit\/(\d+(\.\d+)?).*$/, "$1")) : false,
145             opera = /opera/.test(u),
146             cputype = null,
147             osVersion = null;
148
149         var ie = false;
150         try {
151             //Used to be using trick from
152             //  http://webreflection.blogspot.com/2009/01/32-bytes-to-know-if-your-browser-is-ie.html
153             //ie = !+"\v1",
154             //but it does not work with IE9 in standards mode
155             //Reverting to alternative - use execScript
156             //ie = isDef(window.execScript);
157         ie = /trident/.test(u);
158         } catch (ee) {
159             //if javafx app is in the iframe and content of main window is coming from other domain
160             //  then some browsers may restrict access to outer window properties,
161             //  e.g. FF can throw exception for top.execScript (see RT-17885)
162             //We could revert to more naive test, e.g. test user agent for "MSIE " string
163             // but so far IE does not seem to throw exception => if we get here it is not IE anyways
164             ie = false;
165         }
166
167         //we are not required to detect everything and can leave values null as
168         // long as we later treat them accordingly.
169         //We use "cputype" to detect if given hardware is supported,
170         // e.g. we do not support PPC or iPhone/iPad despite they are running Mac OS
171         //We use "osVersion" to detect if Java/JavaFX can be installed on this OS
172         // e.g. Oracle Java for Mac requires 10.7.3
173         if (mac) {
174             if ((p && /intel/.test(p)) || /intel/.test(u)) {
175                 cputype = "intel";
176             }
177             //looking for things like 10_7, 10_6_8, 10.4, 11_2_2 in the user agent
178             var t = u.match(/(1[0-9_\.]+)[^0-9_\.]/);
179             //normalize to "." separators
180             osVersion = notNull(t) ? t[0].replace(/_/g, ".") : null;
181         }
182
183         // Check mime types. Works with netscape family browsers and checks latest installed plugin only
184         var mm = navigator.mimeTypes;
185         var jre = null;
186         var deploy = null;
187         var fx = null;
188
189         //Cache configuration from plugin mimetypes
190         //It is only available for NPAPI browsers
191         for (var t = 0; t < mm.length; t++) {
192             // The jpi-version is the JRE version.
193             var m = navigator.mimeTypes[t].type;
194             if (m.indexOf("application/x-java-applet;jpi-version") != -1 && m.indexOf('=') != -1) {
195                 jre = m.substring(m.indexOf('=') + 1);
196             }
197             //Supported for 7u6 or later
198             if (m.indexOf("application/x-java-applet;deploy") != -1 && m.indexOf('=') != -1) {
199                 deploy = m.substring(m.indexOf('=') + 1);
200             }
201             //javafx version for cobundled javafx (7u6+)
202             if (m.indexOf("application/x-java-applet;javafx") != -1 && m.indexOf('=') != -1) {
203                 fx = m.substring(m.indexOf('=') + 1);
204             }
205         }
206         return {haveDom:dom, wk:webkit, ie:ie, win:windows,
207                 linux:linux, mac:mac, op: opera, chrome:chrome,
208                 jre:jre, deploy:deploy, fx:fx,
209                 cputype: cputype, osVersion: osVersion};
210     }
211
212     //partially derived from swfobject.js
213     var initDone = false;
214
215     function init() {
216         if (initDone) return;
217
218         ua = detectEnv();
219         if (!ua.haveDom) {
220             return;
221         }
222
223         //NB: dtjava.js can be added dynamically and init() can be called after
224         //    document onload event is fired
225         if (( isDef(d.readyState) && d.readyState == "complete") ||
226             (!isDef(d.readyState) &&
227                 (d.getElementsByTagName("body")[0] || d.body))) {
228             invokeCallbacks();
229         }
230
231         if (!cbDone) {
232             if (isDef(d.addEventListener)) {
233                 d.addEventListener("DOMContentLoaded",
234                     invokeCallbacks, false);
235             }
236         else if (isDef(d.attachEvent)) {
237             //if (ua.ie && ua.win) {
238                 d.attachEvent("onreadystatechange", function() {
239                     if (d.readyState == "complete") {
240                         d.detachEvent("onreadystatechange", arguments.callee);
241                         invokeCallbacks();
242                     }
243                 });
244                 if (w == top) { // if not inside an iframe
245                     (function() {
246                         if (cbDone) {
247                             return;
248                         }
249                         //AI: what for??
250                         try {
251                             d.documentElement.doScroll("left");
252                         } catch(e) {
253                             setTimeout(arguments.callee, 0);
254                             return;
255                         }
256                         invokeCallbacks();
257                     })();
258                 }
259             }
260             if (ua.wk) {
261                 (function() {
262                     if (cbDone) {
263                         return;
264                     }
265                     if (!/loaded|complete/.test(d.readyState)) {
266                         setTimeout(arguments.callee, 0);
267                         return;
268                     }
269                     invokeCallbacks();
270                 })();
271             }
272             addOnload(invokeCallbacks);
273         }
274         //only try to install native plugin if we do not have DTLite
275         //Practically this means we are running NPAPI browser on Windows
276         //(Chrome or FF) and recent JRE (7u4+?)
277         if (!haveDTLite()) {
278             installNativePlugin();
279         }
280     }
281
282     /**
283      This class provides details on why current platform does not meet
284      application platform requirements. Note that severe problems are
285      reported immediately and therefore full check may be not performed and
286      some (unrelated to fatal problem)
287      methods may provide false positive answers.
288      <p>
289      If multiple components do not match then worst status is reported.
290      Application need to repeat checks on each individual component
291      if it want to find out all details.
292
293      @class PlatformMismatchEvent
294      @for   dtjava
295      */
296     function PlatformMismatchEvent(a) {
297
298         //expect to get all parameters needed
299         for (var p in a) {
300             this[p] = a[p];
301         }
302
303         /**
304          * @method toString
305          * @return {string}
306          *    Returns string replesentation of event. Useful for debugging.
307          */
308         this.toString = function() {
309             return "MISMATCH [os=" + this.os + ", browser=" + this.browser
310                 + ", jre=" + this.jre + ", fx=" + this.fx
311                 + ", relaunch=" + this.relaunch + ", platform=" 
312                 + this.platform + "]";
313         };
314
315         /**
316          @method isUnsupportedPlatform
317          @return {boolean}
318          Returns true if this platform (OS/hardware) is not supported in a way
319          to satisfy all platfrom requirements.
320          (E.g. page is viewed on iPhone or JavaFX 2.0 application on Solaris.)
321          <p>
322          Note that this does not include browser match data.
323          If platform is unsupported then application can not be
324          launched and user need to use another platform to view it.
325          */
326
327         this.isUnsupportedPlatform = function() {
328             return this.os;
329         };
330
331         /**
332          @method isUnsupportedBrowser
333          @return {boolean}
334          Returns true if error is because current browser is not supported.
335          <p>
336          If true is returned and isRelaunchNeeded() returns true too then
337          there are known supported browsers browsers for this platform.
338          (but they are not necessary installed on end user system)
339          */
340         this.isUnsupportedBrowser = function() {
341             return this.browser;
342         };
343
344         /**
345          @method jreStatus
346          @return {string}
347
348          Returns "ok" if error was not due to missing JRE.
349          Otherwise return error code characterizing the problem:
350          <ul>
351          <li> none - no JRE were detected on the system
352          <li> old - some version of JRE was detected but it does not match platform requirements
353          <li> oldplugin - matching JRE found but it is configured to use deprecated Java plugin that
354          does not support Java applets
355          <ul>
356          <p>
357          canAutoInstall() and isRelaunchNeeded() can be used to
358          get more details on how seamless user' install experience will be.
359          */
360         this.jreStatus = function() {
361             return this.jre;
362         };
363
364         /**
365          * @method jreInstallerURL
366          * @param {string} locale (optional) Locale to be used for installation web page
367          * @return {string}
368          *
369          * Return URL of page to visit to install required version of Java.
370          * If matching java runtime is already installed or not officially supported
371          * then return value is null.
372          */
373         this.jreInstallerURL = function(locale) {
374             if (this.os && (this.jre == "old" || this.jre == "none")) {
375                 return getJreUrl(locale);
376             }
377             return null;
378         };
379
380         /**
381          @method javafxStatus
382          @return {string}
383
384          Returns "ok" if error was not due to missing JavaFX.
385          Otherwise return error code characterizing the problem:
386          <ul>
387          <li> none - no JavaFX runtime is detected on the system
388          <li> old - some version of JavaFX runtime iss detected but it does not match platform requirements
389          <li> disabled - matching JavaFX is detected but it is disabled
390          <li> unsupported - JavaFX is not supported on this platform
391          <ul>
392          <p>
393          canAutoInstall() and isRelaunchNeeded() can be used to
394          get more details on how seamless user' install experience will be.
395          */
396         this.javafxStatus = function() {
397             return this.fx;
398         };
399
400         /**
401          * @method javafxInstallerURL
402          * @param {string} locale (optional) Locale to be used for installation web page
403          * @return {string}
404          *
405          * Return URL of page to visit to install required version of JavaFX.
406          * If matching JavaFX runtime is already installed or not officially supported
407          * then return value is null.
408          */
409         this.javafxInstallerURL = function(locale) {
410             if (!this.os && (this.fx == "old" || this.fx == "none")) {
411                 return getFxUrl(locale);
412             }
413             return null;
414         };
415
416         /**
417          @method canAutoInstall
418          @return {boolean}
419          Returns true if installation of missing components can be
420          triggered automatically. In particular, ture is returned
421          if there are no missing components too.
422          <p>
423          If any of missing components need to be installed manually
424          (i.e. click through additional web pages) then false is returned.
425          */
426         this.canAutoInstall = function() {
427             return isAutoInstallEnabled(this.platform, this.jre, this.fx);
428         };
429
430         /**
431          @method isRelaunchNeeded
432          @return {boolean}
433
434          Returns true if browser relaunch is needed before application can be loaded.
435          This often is true in conjuction with need to perform installation.
436          <p>
437          Other typical case - use of unsupported browser when
438          it is known that there are supported browser for this pltaform.
439          Then both isUnsupportedBrowser() and isRelaunchNeeded() return true.
440          */
441         this.isRelaunchNeeded = function() {
442             return this.relaunch;
443         };
444     }
445
446     //returns version of instaled JavaFX runtime matching requested version
447     //or null otherwise
448     function getInstalledFXVersion(requestedVersion) {
449         //NPAPI browser and JRE with cobundle
450         if (ua.fx != null && versionCheckFX(requestedVersion, ua.fx)) {
451             return ua.fx;
452         }
453         //try to use DT
454         var p = getPlugin();
455         if (notNull(p)) {
456             try {
457                 return p.getInstalledFXVersion(requestedVersion);
458             } catch(e) {}
459         }
460         return null;
461     }
462
463     //concatenate list with space as separator
464     function listToString(lst) {
465       if (lst != null) {
466           return lst.join(" ");
467       } else {
468           return null;
469       }
470     }
471
472     function addArgToList(lst, arg) {
473         if (notNull(lst)) {
474            lst.push(arg);
475            return lst;
476         } else {
477             var res = [arg];
478             return res;
479         }
480     }
481
482     function doLaunch(ld, platform, cb) {
483         var app = normalizeApp(ld, true);
484
485         //required argument is missing
486         if (!(notNull(app) && notNull(app.url))) {
487             throw "Required attribute missing! (application url need to be specified)";
488         }
489
490         //if we got array we need to copy over!
491         platform = new dtjava.Platform(platform);
492
493         //normalize handlers
494         cb = new dtjava.Callbacks(cb);
495
496         var launchFunc = function() {
497             //prepare jvm arguments
498             var jvmArgs = notNull(platform.jvmargs) ? platform.jvmargs : null;
499             if (notNull(platform.javafx)) {
500                 //if FX is needed we know it is available or
501                 // we will not get here
502                 var v = getInstalledFXVersion(platform.javafx);
503                 //add hint that we need FX toolkit to avoid relaunch
504                 // if JNLP is not embedded
505                 jvmArgs = addArgToList(jvmArgs, " -Djnlp.fx=" + v);
506                 //for swing applications embedding FX we do not want this property as it will
507                 // trigger FX toolkit and lead to app failure!
508                 //But for JavaFX application it saves us relaunch as otherwise we wil launch with AWT toolkit ...
509                 if (!notNull(ld.toolkit) || ld.toolkit == "fx") {
510                     jvmArgs = addArgToList(jvmArgs, " -Djnlp.tk=jfx");
511                 }
512
513             }
514
515
516             //if we on 7u6+ we can use DTLite plugin in the NPAPI browsers
517             //Caveat: as of 7u6 it does not work with Chrome on Linux because Chrome expects
518             //   DTLite plugin to implement xembed (or claim to support xembed)
519             if (haveDTLite() && !(ua.linux && ua.chrome)) {
520                 if (doLaunchUsingDTLite(app, jvmArgs, cb)) {
521                     return;
522                 }
523             }
524
525             //Did not launch yet? Try DT plugin (7u2+)
526             var p =  getPlugin();
527             if (notNull(p)) {
528                 try {
529                     try {
530                         //check if new DT APIs are available
531                         if (versionCheck("10.6+", ua.deploy)) {
532                             //    obj.launchApp({"url" : "http://somewhere/my.jnlp",
533                             //                   "jnlp_content" : "... BASE 64 ...",
534                             //                   "vmargs" : [ "-ea -Djnlp.foo=bar"
535                             //                   "appargs" : [ "first arg,  second arg" ]
536                             //                   "params" : {"p1" : "aaa", "p2" : "bbb"}});
537                             var callArgs = {"url":app.url};
538                             if (notNull(jvmArgs)) {
539                                 callArgs["vmargs"] = jvmArgs;
540                             }
541                             //Only use HTML parameters, they are supposed to overwrite values in the JNLP
542                             //In the future we want to pass arguments too but this needs also be exposed for
543                             // embedded deployment
544                             if (notNull(app.params)) {
545                                 //copy over and ensure all values are strings
546                                 // (native code will ignore them otherwise)
547                                 var ptmp = {};
548                                 for (var k in app.params) {
549                                     ptmp[k] = String(app.params[k]);
550                                 }
551                                 callArgs["params"] = ptmp;
552                             }
553                             if (notNull(app.jnlp_content)) {
554                                 callArgs["jnlp_content"] = app.jnlp_content;
555                             }
556                             var err = p.launchApp(callArgs);
557                             if (err == 0) { //0 - error
558                                 if (isDef(cb.onRuntimeError)) {
559                                     cb.onRuntimeError(app.id);
560                                 }
561                             }
562                         } else { //revert to old DT APIs
563                             //older DT APIs expects vmargs as a single string
564                             if (!p.launchApp(app.url, app.jnlp_content, listToString(jvmArgs))) {
565                                 if (isDef(cb.onRuntimeError)) {
566                                     cb.onRuntimeError(app.id);
567                                 }
568                             }
569                         }
570                         return;
571                     } catch (ee) { //temp  support for older build of DT
572                         if (!p.launchApp(app.url, app.jnlp_content)) {
573                            if (isDef(cb.onRuntimeError)) {
574                               cb.onRuntimeError(app.id);
575                            }
576                         }
577                         return;
578                     }
579                 } catch (e) {
580                     //old DT
581                 }
582             } //old Java (pre DTLite)? not Windows? or old DT
583
584             //use old way to launch it using java plugin
585             var o = getWebstartObject(app.url);
586             if (notNull(d.body)) {
587                 d.body.appendChild(o);
588             } else {
589                 //should never happen
590                 d.write(o.innerHTML);
591             }
592         }
593
594         var r = doValidateRelaxed(platform);
595         //can not launch, try to fix
596         if (r != null) {
597             resolveAndLaunch(app, platform, r, cb, launchFunc);
598         } else {
599             launchFunc();
600         }
601     }
602
603     //process unhandled platform error - convert to code and call callback
604     function reportPlatformError(app, r, cb) {
605         if (isDef(cb.onDeployError)) {
606             cb.onDeployError(app, r);
607         }
608     }
609
610     function isDTInitialized(p) {
611         //if plugin is blocked then p.version will be undefined
612         return p != null && isDef(p.version);
613     }
614
615     //Wait until DT plugin is initialized and then run the code
616     //Currently we only use it for embeded apps and Chrome on Windows
617     function runUsingDT(label, f) {
618         //  Possible situations:
619         //   a) plugin is live and we can simply run code
620         //        - just run the code
621         //   b) plugin is in the DOM tree but it is not initialized yet (e.g. Chrome blocking)
622         //      and there is live timer (pendingCount > 0)
623         //        - there could be another request. We will APPEND to it
624         //        (this is different from dtlite as in this case we can not have multiple clicks)
625         //        - renew timer life counter (do not want new timer)
626         //   c) plugin is in the DOM tree and it is not fully initialized yet but timer is stopped
627         //        - overwrite old request
628         //        - restart timer
629         //
630         // Problem we are solving:
631         //    when plugin is ready to serve request? How do we schedule call to happen when plugin is initialized?
632         // Caveat:
633         //    Chrome can popup dialog asking user to grant permissions to load the plugin.
634         //    There is no API to detect dialog is shown and when user grants or declines permissions
635         //
636         // Note:
637         //    If we set property on plugin object before it is unblocked then they seem to be lost
638         //   and are not propagated to the final object once it is instantiated.
639         //
640         // Workaround we use:
641         //    Once plugin is added we will be checking if it is initialized and once we detect it we will execute code.
642         //  We will stop checking after some time.
643         var p = getPlugin();
644         if (p == null) {
645             return; //NO DT
646         }
647
648         if (isDTInitialized(p)) {
649             f(p);
650         } else {
651             // see if we need new timer
652             var waitAndUse = null;
653             if (!isDef(dtjava.dtPendingCnt) || dtjava.dtPendingCnt == 0) {
654                 waitAndUse = function () {
655                     if (isDTInitialized(p)) {
656                         if (notNull(dtjava.dtPending)) {
657                             for (var i in dtjava.dtPending) {
658                                 dtjava.dtPending[i]();
659                             }
660                         }
661                         return;
662                     }
663                     if (dtjava.dtPendingCnt > 0) {
664                         dtjava.dtPendingCnt--;
665                         setTimeout(waitAndUse, 500);
666                     }
667                 }
668             }
669             //add new task in queue
670             if (!notNull(dtjava.dtPending) || dtjava.dtPendingCnt == 0) {
671                 dtjava.dtPending = {};
672             }
673             dtjava.dtPending[label] = f; //use map to ensure repitative actions are not queued (e.g. multiple click to launch webstart)
674             //reset the timer counter
675             dtjava.dtPendingCnt = 1000; //timer is gone after 500s
676             //start timer if needed
677             if (waitAndUse != null) waitAndUse();
678         }
679     }
680
681     //returns same mismatch event if not resolved, null if resolved
682     function resolveAndLaunch(app, platform, v, cb, launchFunction) {
683         var p = getPlugin();
684
685         //Special case: Chrome/Windows
686         // (Note: IE may also block activeX control but then it will block attempts to use it too)
687         if (ua.chrome && ua.win && p != null && !isDTInitialized(p)) {
688             //this likely means DT plugin is blocked by Chrome
689             //tell user to grant permissions and retry
690             var actionLabel;
691             if (notNull(app.placeholder)) {
692                 var onClickFunc = function() {w.open("http://www.java.com/en/download/faq/chrome.xml"); return false;};
693                 var msg1 = "Please give Java permission to run on this browser web page.";
694                 var msg2 = "Click for more information.";
695                 var altText = "";
696                 doShowMessageInTheArea(app, msg1, msg2, altText, "javafx-chrome.png", onClickFunc);
697                 actionLabel = app.id + "-embed";
698             } else {
699                 v.jre = "blocked";
700                 reportPlatformError(app, v, cb);
701                 actionLabel = "launch"; //we only queue ONE webstart launch.
702                                         //Do not want to try to queue different apps - bad UE
703                                         // (once user enable multiple things can spawn)
704                                         //Note: what if multiple webstart apps are set to launch on page load (suer do not need to click)?
705                                         //      Guess do not worry for now
706                                         //Note: app.id may be null in case of webstart app.
707             }
708
709             //now we need to start waiter. Once DT is initialized we can proceeed
710             var retryFunc = function() {
711                 var vnew = doValidateRelaxed(platform);
712                 if (vnew == null) { //no problems with env
713                     launchFunction();
714                 } else {
715                     resolveAndLaunch(app, platform, vnew, cb, launchFunction);
716                 }
717             };
718             runUsingDT(actionLabel, retryFunc);
719
720             return;
721         }
722
723         if (!v.isUnsupportedPlatform() && !v.isUnsupportedBrowser()) { //otherwise fatal, at least until restart of browser
724             if (isMissingComponent(v) && isDef(cb.onInstallNeeded)) {
725                 var resolveFunc= function() {
726                     //once install is over we need to revalidate
727                     var vnew = doValidateRelaxed(platform);
728                     if (vnew == null) { //if no problems found - can launch
729                         launchFunction();
730                     } else { //TODO: what happens if we installed everything but relaunch is needed??
731                         //We can not get here if component install was not offered for any or missing componens
732                         //(if auto install was possible, see doInstall() implementation)
733                         //Hence, it is safe to assume we failed to meet requirements
734                         reportPlatformError(app, vnew, cb);
735
736                         //TODO: may be should call itself again but
737                         // then it easy can become infinite loop
738
739                         //e.g. user installs but we fail to detect it because DT
740                         // is not FX aware and retry, etc.
741                         //TODO: think it through
742                     }
743                 };
744
745                 cb.onInstallNeeded(app, platform, cb,
746                             v.canAutoInstall(), v.isRelaunchNeeded(), resolveFunc);
747                 return;
748             }
749         }
750         reportPlatformError(app, v, cb);
751     }
752
753     function haveDTLite() {
754         if (ua.deploy != null) {
755             return versionCheck("10.6+", ua.deploy);
756         }
757         return false;
758     }
759
760     function isDTLiteInitialized(p) {
761         //if plugin is blocked then p.version will be undefined
762         return p != null && isDef(p.version);
763     }
764
765     function getDTLitePlugin() {
766         return document.getElementById("dtlite");
767     }
768
769     function doInjectDTLite() {
770         //do not want more than one plugin
771         if (getDTLitePlugin() != null) return;
772
773         var p = document.createElement('embed');
774         p.width = '10px';
775         p.height = '10px';
776         p.id = "dtlite";
777         p.type = "application/x-java-applet";  //means we get latest
778
779         var div = document.createElement("div");
780         div.style.position = "relative";
781         div.style.left = "-10000px";
782         div.appendChild(p);
783
784         var e = document.getElementsByTagName("body");
785         e[0].appendChild(div);
786     }
787
788     function runUsingDTLite(f) {
789         //  Possible situations:
790         //   a) first request, plugin is not in the DOM tree yet
791         //        - add plugin
792         //        - setup wait mechanism and run f() once plugin is ready
793         //   b) plugin is live and we can simply run code
794         //        - just run the code
795         //   c) plugin is in the DOM tree but it is not initialized yet (e.g. Chrome blocking)
796         //      and there is live timer (pendingCount > 0)
797         //        - there could be another request. We will override it (e.g. user clicked multiple times)
798         //        - renew timer life counter (do not want new timer)
799         //   d) plugin is in the DOM tree and it is not fully initialized yet but timer is stopped
800         //        - overwrite old request
801         //        - restart timer
802         //
803         // Problem:
804         //    when plugin is ready to serve request? How do we schedule call to happen when plugin is initialized?
805         // Caveat:
806         //    Chrome can popup dialog asking user to grant permissions to load the plugin.
807         //    There is no API to detect dialog is shown and when user grants or declines permissions
808         //
809         // Note:
810         //    If we set property on plugin object before it is unblocked then they seem to be lost
811         //   and are not propagated to the final object once it is instantiated.
812         //
813         // Workaround we use:
814         //    Once plugin is added we will be checking if it is initialized and once we detect it we will execute code.
815         //  We will stop checking after some time.
816         var p = getDTLitePlugin();
817         if (p == null) {
818             doInjectDTLite();
819             p = getDTLitePlugin();
820         }
821
822         if (isDTLiteInitialized(p)) {
823             f(p);
824         } else {
825             // see if we need new timer
826             var waitAndUse = null;
827             if (!isDef(dtjava.dtlitePendingCnt) || dtjava.dtlitePendingCnt == 0) {
828                 waitAndUse = function () {
829                     if (isDef(p.version)) {
830                         if (dtjava.pendingLaunch != null) {
831                             dtjava.pendingLaunch(p);
832                         }
833                         dtjava.pendingLaunch = null;
834                         return;
835                     }
836                     if (dtjava.dtlitePendingCnt > 0) {
837                         dtjava.dtlitePendingCnt--;
838                         setTimeout(waitAndUse, 500);
839                     }
840                 }
841             }
842             //add new task in queue
843             dtjava.pendingLaunch = f;
844             //reset the timer counter
845             dtjava.dtlitePendingCnt = 1000; //timer is gone after 500s
846             //start timer if needed
847             if (waitAndUse != null) {
848                 waitAndUse();
849             }
850         }
851     }
852
853     function doLaunchUsingDTLite(app, jvmargs, cb) {
854         var launchIt = function() {
855             var pp = getDTLitePlugin();
856             if (pp == null) {
857                 //should not be possible as we guard before enter this function
858                 if (isDef(cb.onRuntimeError)) {
859                     cb.onRuntimeError(app.id);
860                 }
861             }
862             
863             //DTLite only support new invocation API
864             //    obj.launchApp({"url" : "http://somewhere/my.jnlp",
865             //                   "jnlp_content" : "... BASE 64 ...",
866             //                   "vmargs" : [ "-ea -Djnlp.foo=bar"
867             //                   "appargs" : [ "first arg,  second arg" ]
868             //                   "params" : {"p1" : "aaa", "p2" : "bbb"}});
869             var callArgs = {"url" : app.url};
870             if (notNull(jvmargs)) {
871                callArgs["vmargs"] = jvmargs;
872             }
873             //Only use HTML parameters, they are supposed to overwrite values in the JNLP
874             //In the future we want to pass arguments too but this needs also be exposed for
875             // embedded deployment
876             if (notNull(app.params)) {
877                 //copy over and ensure all values are stings
878                 // (native code will ignore them otherwise)
879                 var ptmp = {};
880                 for (var k in app.params) {
881                     ptmp[k] = String(app.params[k]);
882                 }
883                 callArgs["params"] = ptmp;
884             }
885             if (notNull(app.jnlp_content)) {
886                callArgs["jnlp_content"] = app.jnlp_content;
887             }
888             var err = pp.launchApp(callArgs);
889             if (err == 0) { //0 - error
890                 if (isDef(cb.onRuntimeError)) {
891                     cb.onRuntimeError(app.id);
892                 }
893             }
894         };
895         
896         if (versionCheck("10.4+", ua.deploy)) { //only for NPAPI browsers
897             runUsingDTLite(launchIt);
898             return true;
899         }
900         return false;
901     }
902     
903     function getWebstartObject(jnlp) {
904         var wo = null;
905         if (ua.ie) { //TODO: attempt to use object in FF 3.6 lead to hang. Revert to embed for now
906                      //TODO: Should Chrome use object?
907             //object tag itself
908             wo = d.createElement('object');
909             wo.width = '1px'; //zero size reports invalid argument in IE!
910             wo.height = '1px'; //TODO: make it less distruptive to page layout? hide div?
911             var p = d.createElement('param');
912             p.name = 'launchjnlp';
913             p.value = jnlp;
914             wo.appendChild(p);
915             p = d.createElement('param');
916             p.name = 'docbase';
917             p.value = notNull(d.documentURI) ? d.documentURI : d.URL;
918             wo.appendChild(p);
919
920             if (!ua.ie) {
921                 //NB:do not need to use exact version in mime type as generic should be mapped to latest?
922                 wo.type = "application/x-java-applet;version=1.7";
923             } else {
924                 wo.classid = "clsid:8AD9C840-044E-11D1-B3E9-00805F499D93";
925             }
926         } else { //TODO: else part should go away once we figure out what is going on with FF
927             wo = d.createElement('embed');
928             wo.width = '0px';
929             wo.height = '0px';
930             //NB: dot notation did not work for custom attributes??? revert to setAttribute
931             wo.setAttribute('launchjnlp', jnlp);
932             wo.setAttribute('docbase', (notNull(d.documentURI) ? d.documentURI : d.URL));
933             //NB:do not need to use exact version in mime type as generic should be mapped to latest?
934             wo.type = "application/x-java-applet;version=1.7";
935         }
936
937         var div = d.createElement("div");
938         div.style.position = "relative";
939         div.style.left = "-10000px";
940         div.appendChild(wo);
941         return div;
942     }
943
944     //this is similar to version check rules except for
945     // JavaFX we treat version slightly differently.
946     //For Javafx version really is FAMILY.UPDATE_VERSION
947     // where FAMILY is everything before first dot
948     // E.g.
949     //     2.0.1 => family 2, update 0.1
950     //     2.0   => family 2. update 0.0
951     //
952     // JavaFX version requirements are always treated as "not earlier than this update".
953     // I.e. we expect
954     //     2.2.0 to match 2.2*, 2.2+, 2.1+, 2.1*, 2.0 and 1+
955     //           but not match 2.2.1+, 2.2.1*, 2.3*, 2.3+ or 1*
956     function versionCheckFX(query, version) {
957         if (query == null || query.length == 0) {
958             return true;
959         }
960         var endChar = query.charAt(query.length - 1);
961         var familyOnly = (endChar == '*');
962         if (!familyOnly) {
963             if (endChar == '+') {
964                 return versionCheck(query, version);
965             } else { //must be fixed version, e.g. 2.0
966                 return versionCheck(query + '+', version);
967             }
968         } else {
969             return (versionCheck(query.charAt(0)+".*", version) && //required family (version belongs to family 2)
970                     versionCheck(query.substring(0, query.length - 1)+"+", version)); //global lookup (version >= 2.1.1), replace * with +
971         }
972     }
973
974     //Convert version string into 4 element array with version components
975     //If input string has fewer components then pad with zeros from the right
976     //If input string ends with suffix like '+' or '*' then it is stripped
977     //
978     //Examples:
979     //    10.1.2.3 => {10, 1, 2, 3}
980     //    10.1     => {10, 1, 0, 0}
981     //    10.1+    => {10, 1, 0, 0}
982     function convertVersionToArray(versionString) {
983         if (versionString != null) {
984             var c = versionString.charAt(versionString.length - 1);
985             //if it is not digit we want to strip last char
986             if (c <= '0' || c >= '9') {
987                 versionString = versionString.substring(0, versionString.length - 1);
988             }
989         }
990
991         //corner case inputs
992         if (versionString == null || versionString.length == 0) {
993             return [0, 0, 0, 0];
994         }
995
996         var arr = versionString.split(".");
997         while (arr.length < 4) {
998             arr.push(0);
999         }
1000         return arr;
1001     }
1002
1003     //checks where given version string matches query
1004     //
1005     //NB: assume format is correct. Can add format check later if needed
1006     function versionCheck(query, version) {
1007         if (query == null || query.length == 0) return true;
1008
1009         var c = query.charAt(query.length - 1);
1010
1011         //if it is not explicit pattern but does not have update version then need to append *
1012         if (c != '+' && c != '*' && (query.indexOf('_') != -1 && c != '_')) {
1013             query = query + "*";
1014             c = '*';
1015         }
1016
1017         query = query.substring(0, query.length - 1);
1018         //if query ends with ".", "_" then we want to strip it to allow match of "1.6.*" to shorter form such as "1.6"
1019         //TODO: add support for match of "1.7.0*" to "1.7"?
1020         if (query.length > 0) {
1021             var z = query.charAt(query.length - 1);
1022             if (z == '.' || z == '_') {
1023                 query = query.substring(0, query.length - 1);
1024             }
1025         }
1026         if (c == '*') {
1027             //it is match if version starts from it
1028             return (version.indexOf(query) == 0);
1029         } else if (c == '+') {
1030             //lexicographical comparison is not good here as we may have case like
1031             //   query="10.6*" and version="10.10.2"
1032             //Approach:
1033             //   split into tokens and compare each of tokens numerically
1034             //Keep comparing until tokens are the same or we reached end.
1035             //If tokens differ then we have a match if query is smaller and
1036             // non-match if it is greater
1037             var qArr = convertVersionToArray(query);
1038             var vArr = convertVersionToArray(version);
1039
1040             //qArr and vArr are expected to be arrays of same length
1041             for (var idx=0; idx < qArr.length; idx++) {
1042                 if (qArr[idx] < vArr[idx]) {
1043                     //query is smaller
1044                     return true;
1045                 } else if (qArr[idx] < vArr[idx]) {
1046                     //query is larger => fail
1047                     return false;
1048                 }
1049             }
1050             //query is equal to version => it is ok
1051             return true;
1052         }
1053         return false;
1054     }
1055
1056     //as JavaFX comes with own plugin binaries then check based on mime types, etc.
1057     // may be false positive as it only checks for plugin version, not real JRE
1058     //Here we check that DT plugin is aware of JRE installations
1059     //Note that:
1060     //  - if DT is not available we will return false but we only do this i
1061     //    ready to launch => DT must be found
1062     //  - we do not want to check in jreCheck() as we want to avoid loading
1063     //    DT plugin if we can (as old DT may make it not possible to autostart)
1064     function doublecheckJrePresence() {
1065         if (!haveDTLite()) { //basically IE on windows or Old JRE on windows
1066           var p = getPlugin();
1067           if (p != null) {
1068             return true;
1069             //WORKAROUND: bug in native DT!!! TODO: What version? bypass for it only
1070             //return (p.jvms.getLength() > 0);
1071           }
1072
1073           return false;
1074         }
1075
1076         //if we are not using native DT plugin (i.e. using DTLite) then no way we can do sanity check
1077         //   => assume first check is accurate
1078         return true;
1079     }
1080
1081     function jreCheck(jre) {
1082         // Check if latest JRE is exposed in mimetype and if it is good enough (only for NPAPI browsers)
1083         if (ua.jre != null) {
1084             if (versionCheck(jre, ua.jre)) {
1085                return "ok";
1086             }
1087             //Note: if we have JRE but it is not match that means we may need an upgrade message
1088             // but we still could be able to get more accurate answer with native DT plugin
1089         }
1090
1091         //try to use DT plugin
1092         var p = getPlugin();
1093         if (p != null) {
1094             var VMs = p.jvms;
1095             for (var i = 0; VMs != null && i < VMs.getLength(); i++) {
1096                 if (versionCheck(jre, VMs.get(i).version)) {
1097                     if (!ua.ie && notNull(navigator.mimeTypes)) {
1098                         //if mime types are available but plugin is not there =>
1099                         //  it is disabled
1100                         if (!notNull(navigator.mimeTypes["application/x-java-applet"])) {
1101                             return "disabled";
1102                         }
1103                     }
1104                     return "ok";
1105                 }
1106             }
1107             //do not need to try other ways if used DT
1108             return "none";
1109         }
1110
1111         //No full DT => On Windows we can not launch FX anyways
1112         //   but may have old JRE
1113         //And we might be able to launch on Mac/Linux
1114
1115
1116         //This is only IE on Windows. This gives no update version. only e.g. 1.6.0
1117         //and also cause java plugin to be loaded => browser will need to be restarted
1118         //if new JRE is installed.
1119         //However, if we got here than DT is not available and autoinstall is not possible
1120         if (ua.ie) {
1121             var lst = ["1.8.0", "1.7.0", "1.6.0", "1.5.0"];
1122             for (var v = 0; v < lst.length; v++) {
1123                 if (versionCheck(jre, lst[v])) {
1124                     try {
1125                         //TODO: FIXME: This does not seem to work in my testing in IE7?
1126                         var axo = new ActiveXObject("JavaWebStart.isInstalled." + lst[v] + ".0");
1127                         // This is not hit if the above throws an exception.
1128                         return "ok";
1129                     } catch (ignored) {
1130                     }
1131                 }
1132             }
1133         }
1134
1135
1136         return "none";
1137     }
1138
1139     function checkJRESupport() {
1140         //Negative test. New platforms will not be rejected
1141         var osProblem = ['iPhone', 'iPod'];
1142         var os = containsAny(osProblem, navigator.userAgent);
1143
1144         //Do not support Chrome/Mac as Chrome is 32 bit only
1145         var browser = (ua.mac && ua.chrome && ua.cputype == "intel");
1146
1147         //autoinstall possible if native plugin is detected or OS is fine
1148         auto = os || (getPlugin() != null);
1149
1150         //false is no problem found
1151         return {os: os, browser: browser, auto: auto};
1152     }
1153
1154     //it is not clear if we can work in IE6
1155     // but it is hard to test and JRE7 does not even support it
1156     // mark as unsupported for now
1157     function isUnsupportedVersionOfIE() {
1158         if (ua.ie) {
1159             try {
1160               //these functions are defined in IE only
1161               var v = 10*ScriptEngineMajorVersion() + ScriptEngineMinorVersion();
1162               if (v < 57) return true; //IE7 will have 57
1163             } catch (err) {
1164                 //really old IE?
1165                 return true;
1166             }
1167         }
1168         return false;
1169     }
1170
1171     function checkFXSupport() {
1172         var browser;
1173         if (ua.win) {
1174             //do not support Opera and Safari
1175             // (not really tested, may be it works but known to have problems with DT detection)
1176             browser = ua.op || ua.wk || isUnsupportedVersionOfIE();
1177
1178             //false is no problem found
1179             return {os: false, browser: browser};
1180         } else if (ua.mac && ua.cputype == "intel") { //do not support PPC/iphone/ipad ...
1181             var os = !versionCheck("10.7.3+", ua.osVersion); //10.7.3 or later!
1182             browser = ua.op ||
1183                 (ua.mac && ua.chrome); //Opera is not supported
1184             //Chrome on Mac is 32 bit => plugin only work in 64 bit ...
1185             //TODO: How do we detect FF running in 32 bit mode?
1186
1187             //false is no problem found
1188             return {os: os, browser: browser};
1189         } else if (ua.linux) {
1190             browser = ua.op; //Opera unsupported
1191
1192             //false is no problem found
1193             return {os: false, browser: browser};
1194         } else {
1195             //unknown unsupported OS
1196             return {os: true, browser: false};
1197         }
1198     }
1199
1200     function relaxVersion(v) {
1201         if (notNull(v) && v.length > 0) {
1202             var c = v.charAt(v.length - 1);
1203             if (c == '*') {
1204               v = v.substring(0, v.length - 1)+"+";
1205             } else if (c != '+') { //exact version (e.g. 1.6)
1206                 v = v + "+";
1207             }
1208         }
1209         return v;
1210     }
1211
1212     //we relax validation rules where we try to embed or launch app
1213     // in order to deal with requests for OLDER jres at the java level
1214     //Basically we convert request for version in JRE family to request for any future JRE
1215     //We do NOT do same for JavaFX right now. There is no real need before 3.0 and it is not clear if it is good thing
1216     //
1217     //Note we keep validation strict for install and validate-only scenarios.
1218     // This allows to query accurate details from javascript
1219     function doValidateRelaxed(platform) {
1220         var p = new dtjava.Platform(platform);
1221
1222         p.jvm = relaxVersion(p.jvm);
1223         //p.javafx = relaxVersion(p.javafx);
1224
1225         return doValidate(p);
1226     }
1227
1228     function doValidate(platform) {
1229         //ensure some platform is set (we could get array too!)
1230         platform = new dtjava.Platform(platform);
1231
1232         //problem markers
1233         var fx = "ok", jre = "ok", restart = false, os = false, browser = false,
1234             p, details;
1235
1236         //check JRE
1237         if (notNull(platform.jvm) && jreCheck(platform.jvm) != "ok") { //matching JRE not found
1238             var res = jreCheck("*");
1239             if (res == "ok") {
1240                 jre = "old";
1241             } else {
1242                 jre = res; //"none" or "disabled"
1243             }
1244
1245             details = checkJRESupport();
1246             if (details.os) {
1247                 jre = "unsupported";
1248                 os = true;
1249             }
1250             browser = details.browser;
1251         }
1252 /*        if (notNull(platform.plugin) && jre == "ok") {
1253             try {
1254                 p = getPlugin();
1255                 //TEMPORARY DISABLE because isPlugin2() is broken in 1.7.0
1256                 // it is not fixed in 7-client but if FX is enabled then
1257                 // it must be new plugin anyways
1258                 //=> keep this disabled for now until we find use case
1259                 if (false && (p == null || !p.isPlugin2())) {
1260                     //TODO: FIXME: seem to get here always because isPlugin2() returns 0?
1261                     jre = "oldplugin";
1262                     relaunch = true;
1263                 }
1264             } catch (err) { //pre 6u10 or no DT
1265                 jre = "oldplugin";
1266                 relaunch = true;
1267             }
1268         }
1269 */
1270         //check FX
1271         if (notNull(platform.javafx)) {
1272             details = checkFXSupport();
1273             if (details.os || details.browser) { //FX is not supported,
1274                                                   //do not even try
1275                 fx = "unsupported";
1276                 os = os || details.os;
1277                 browser = browser || details.browser;
1278             } else {
1279                 //on non windows platforms automated install is not possible
1280                 // (if it is needed on windows and possible we will set it to false later)
1281                 
1282                 if (ua.fx != null) {
1283                   //found cobundled JavaFX on 7u6+ (and it is NPAPI-based browser)
1284                   if (versionCheckFX(platform.javafx, ua.fx)) {
1285                         fx = "ok";
1286                   } else if (versionCheckFX("2.0+", ua.fx)) {
1287                         fx = "old";
1288                   }
1289                 } else if (ua.win) { //could be 7u6(cobundle)/IE or JRE6/FX
1290                   try {
1291                     p = getPlugin();
1292                     //typeof did not work in IE
1293                     var v = p.getInstalledFXVersion(platform.javafx);
1294                     //if found we should get version string, otherwise empty string or null. If found then fx=false!
1295                     if (v == "" || v == null) {
1296                         v = p.getInstalledFXVersion("2.0+"); //check for any FX version
1297                         if (v == null || v == "") {
1298                             fx = "none";
1299                         } else {
1300                             fx = "old";
1301                         }
1302                     }
1303                   } catch(err) {
1304                     //If we got here then environment is supported but
1305                     //this is non FX aware JRE => no FX and can only offer manual install
1306                     // (restart needed as toolkit is already loaded)
1307                     fx = "none";
1308                   }
1309                 } else if (ua.mac || ua.linux) {
1310                     fx = "none";
1311                 }
1312             }
1313         }
1314
1315         //recommend relaunch if OS is ok but browser is not supported
1316         restart = restart || (!os && browser);
1317
1318         //TODO: need a way to find out if java plugin is loaded => will need to relaunch
1319
1320         //we need to return null if everything is ok. Check for problems.
1321         if (fx != "ok" || jre != "ok" || restart || os || browser) {
1322             return new PlatformMismatchEvent(
1323                 {fx: fx, jre: jre, relaunch: restart, os: os, browser: browser,
1324                     platform: platform});
1325         } else {
1326             //if all looks good check JRE again, it could be false positive
1327             if (!doublecheckJrePresence()) {
1328                return new PlatformMismatchEvent(
1329                  {fx: fx, jre: "none", relaunch: restart, os: os, 
1330                      browser: browser, platform: platform});
1331             }
1332         }
1333
1334         return null;
1335     }
1336
1337     //TODO: does it make sense to have a way to explicitly request locale?
1338     function guessLocale() {
1339         var loc = null;
1340
1341         loc = navigator.userLanguage;
1342         if (loc == null)
1343             loc = navigator.systemLanguage;
1344         if (loc == null)
1345             loc = navigator.language;
1346
1347         if (loc != null) {
1348             loc = loc.replace("-", "_")
1349         }
1350         return loc;
1351     }
1352
1353     function getJreUrl(loc) {
1354         if (!notNull(loc)) {
1355             loc = guessLocale();
1356         }
1357         return 'http://jdl.sun.com/webapps/getjava/BrowserRedirect?host=java.com' +
1358             ((notNull(window.location) && notNull(window.location.href)) ?
1359                 ('&returnPage=' + window.location.href) : '') +
1360             (notNull(loc) ? ('&locale=' + loc) : '');
1361         //NB: brand parameter is not supported for now
1362     }
1363
1364     function getFxUrl(locale) {
1365         return "http://www.oracle.com/technetwork/java/javafx/downloads/index.html";
1366     }
1367
1368     //return true if mismatch event suggest to perform installation
1369     function isMissingComponent(v) {
1370         if (v != null) {
1371             var jre = v.jreStatus();
1372             var fx = v.javafxStatus();
1373             //if anything is disabled then this need to be resolved before any further installs
1374             return (jre == "none" || fx == "none" || jre == "old" || fx == "old")
1375                && (fx != "disabled" && jre != "disabled");
1376         }
1377         return false;
1378     }
1379
1380     function showClickToInstall(ld, isJRE, isUpgrade, isAutoinstall, isRelaunchNeeded, actionFunc) {
1381         //what product?
1382         var productName, productLabel;
1383         if (isJRE) {
1384             productName = "Java";
1385             productLabel = "java";
1386         } else {
1387             productName = "JavaFX";
1388             productLabel = "javafx";
1389         }
1390
1391         var msg1, msg2, imgName;
1392         if (isUpgrade) {
1393             msg1 = "A newer version of " + productName + "is required to view the content on this page.";
1394             msg2 = "Please click here to update " + productName;
1395             imgName = "upgrade_"+productLabel+".png";
1396         } else {
1397             msg1 = "View the content on this page.";
1398             msg2 = "Please click here to install " + productName;
1399             imgName = "get_"+productLabel+".png";
1400         }
1401         var altText = "Click to install "+productName;
1402
1403         doShowMessageInTheArea(ld, msg1, msg2, altText, imgName, actionFunc);
1404     }
1405
1406     function doShowMessageInTheArea(ld, msg1, msg2, altText, imgName, actionFunc) {
1407         //if image will fit (size 238x155)
1408         var r = d.createElement("div");
1409         r.width = normalizeDimension(ld.width);
1410         r.height = normalizeDimension(ld.height);
1411
1412         var lnk = d.createElement("a");
1413         lnk.href="";
1414         lnk.onclick = function() {actionFunc(); return false;};
1415         if (ld.width < 250 || ld.height < 160) { //if relative size this will fail =>
1416                                                  // will choose image
1417             r.appendChild(
1418                d.createElement("p").appendChild(
1419                   d.createTextNode(msg1)));
1420             lnk.appendChild(d.createTextNode(msg2));
1421             r.appendChild(lnk);
1422         } else {
1423             var img = d.createElement("img");
1424             img.src = jscodebase + imgName;
1425             img.alt = altText;
1426             img.style.borderWidth="0px";
1427             img.style.borderStyle="none";
1428 //FIXME: centering image does not work (in a way it also work with relative dimensions ...)
1429 //            lnk.style.top="50%";
1430 //            lnk.style.left="50%";
1431 //            lnk.style.marginTop = -119; // 238/2
1432 //            lnk.style.marginLeft = -77; //155/2
1433             lnk.appendChild(img);
1434             r.appendChild(lnk);
1435         }
1436         wipe(ld.placeholder);
1437         ld.placeholder.appendChild(r);
1438     }
1439
1440     function canJavaFXCoBundleSatisfy(platform) {     
1441         // check if latest co-bundle can satisfy
1442         if (versionCheck(platform.jvm, minJRECobundleVersion) &&
1443             versionCheckFX(platform.javafx, "2.2.0")) {
1444             return true;        
1445         }
1446         return false;
1447     }
1448
1449     function defaultInstallHandler(app, platform, cb,
1450                                    isAutoinstall, needRelaunch, launchFunc) {
1451         var installFunc = function() {
1452             doInstall(app, platform, cb, launchFunc);
1453         };
1454
1455         var s = doValidate(platform);
1456         if (!notNull(s)) { //platform match => nothing to install
1457             if (notNull(launchFunc)) {
1458                 launchFunc();
1459             }
1460         }
1461
1462         var isUpgrade = notNull(s) && (s.javafxStatus() == "old" || s.jreStatus() == "old");
1463         if (notNull(app.placeholder)) { //embedded
1464             if (canJavaFXCoBundleSatisfy(platform)) { //if both JRE and FX are missing we will start install from JRE
1465                 //it is only JRE that needs to be updated
1466                showClickToInstall(app, true, isUpgrade, isAutoinstall, needRelaunch, installFunc);
1467             } else {
1468                showClickToInstall(app, (s.jreStatus() != "ok"), isUpgrade, isAutoinstall, needRelaunch, installFunc);
1469             }
1470         } else { //webstart
1471           var r = isAutoinstall;
1472           var msg = null;
1473           if (!r) {
1474              if (canJavaFXCoBundleSatisfy(platform)) { //if both JRE and FX are missing we will start install from JRE
1475                  //it is only JRE that needs to be updated
1476                  if (isUpgrade) {
1477                      msg = "A newer version of Java is required to view the content on this page. Please click here to update Java.";
1478                  } else {
1479                      msg = "To view the content on this page, please click here to install Java.";
1480                  }
1481                  r = confirm(msg);
1482              } else {
1483                  if (isUpgrade) {
1484                      msg = "A newer version of JavaFX is required to view the content on this page. Please click here to update JavaFX.";
1485                  } else {
1486                      msg = "To view the content on this page, please click here to install JavaFX.";
1487                  }
1488                  r = confirm(msg);
1489              }
1490           }
1491           if (r)
1492              installFunc();
1493         }
1494     }
1495     
1496     /** 
1497      * returns true if we can enable DT plugin auto-install without chance of
1498      * deadlock on cert mismatch dialog
1499      *
1500      * requestedJREVersion param is optional - if null, it will be
1501      * treated as installing any JRE version
1502      * 
1503      * DT plugin for 6uX only knows about JRE installer signed by SUN cert.
1504      * If it encounter Oracle signed JRE installer, it will have chance of
1505      * deadlock when running with IE.  This function is to guard against this.
1506      */
1507     function enableWithoutCertMisMatchWorkaround(requestedJREVersion) {
1508
1509        // Non-IE browser are okay
1510        if (!ua.ie) return true;
1511
1512        // if DT plugin is 10.0.0 or above, return true
1513        // This is because they are aware of both SUN and Oracle signature and
1514        // will not show cert mismatch dialog that might cause deadlock
1515        if (versionCheck("10.0.0+", getPlugin().version)) {
1516           return true;
1517        }
1518
1519        // If we got there, DT plugin is 6uX
1520
1521        if (requestedJREVersion  == null) {
1522           // if requestedJREVersion is not defined - it means ANY.
1523           // can not guarantee it is safe to install ANY version because 6uX 
1524           // DT does not know about Oracle certificates and may deadlock
1525           return false;
1526        }
1527
1528        // 6u32 or earlier JRE installer used Sun certificate
1529        // 6u33+ uses Oracle's certificate
1530        // DT in JRE6 does not know about Oracle certificate => can only 
1531        // install 6u32 or earlier without risk of deadlock
1532        return !versionCheck("1.6.0_33+", requestedJREVersion);
1533     }
1534     
1535     // return true if we can auto-install to satisfy the platform requirement
1536     // return false otherwise
1537     // 
1538     // We can auto-install if all below is true:
1539     //   - windows platform
1540     //   - native DT plugin available
1541     //   - if JRE install is required, JRE exe is signed by compatible 
1542     //       certificate
1543     //   - if FX install is required, JRE co-bundle can satisfy the 
1544     //       requirement or DT plugin supports FX auto-install
1545     function isAutoInstallEnabled(platform, jre, fx) {
1546        // auto-install is windows only
1547        if (!ua.win) return false;
1548        
1549        // if no DT plugin, return false
1550        // if DT plugin is there but not operational (e.g. blocked)
1551        //  then pretend there is no autoinstall
1552        var p = getPlugin();
1553        if (p == null || !isDef(p.version)) return false;
1554
1555        if (jre != "ok") {
1556            // need JRE install
1557            if (!enableWithoutCertMisMatchWorkaround(platform.jvm)) {
1558                return false;
1559            }
1560        }
1561        
1562        if (fx != "ok") {
1563             if (!canJavaFXCoBundleSatisfy(platform)) {
1564                 // no cobundle, check if there is standalone FX auto-install
1565                 // DT from Java 7 or later should be ok
1566                 if (!versionCheck("10.0.0+", getPlugin().version)) {
1567                     return false;
1568                 }
1569             } else {
1570                 // we are going to install co-bundle JRE - check if we can do
1571                 // that
1572                 if (!enableWithoutCertMisMatchWorkaround(minJRECobundleVersion)) {
1573                     return false;
1574                 }
1575             }
1576         }
1577         return true;
1578     }
1579
1580     function doInstall(app, platform, cb, postInstallFunc) {
1581         var s = doValidate(platform);
1582
1583         cb = new dtjava.Callbacks(cb);
1584
1585         if (notNull(s) && s.isUnsupportedPlatform()) {
1586             reportPlatformError(app, s, cb);
1587             return false; //no install
1588         }
1589
1590         var placeholder = (app != null) ? app.placeholder : null;
1591
1592         var codes, status;
1593         if (isMissingComponent(s)) { //otherwise nothing to install
1594             if (s.canAutoInstall()) {
1595                 var p = getPlugin();
1596                 //helper function to launch FX installer
1597                 var installFX = function() {
1598                     var handleResultFX = function(cd) {
1599                         //return codes from DT (JREInstall.h) where BASE is 10000:
1600                         //  #define INSTALL_ERR_SUCCESS  BASE+0
1601                         //  #define INSTALL_ERR_STARTED  BASE+1
1602                         //  #define INSTALL_ERR_DOWNLOAD BASE+2
1603                         //  #define INSTALL_ERR_VALIDATE BASE+3
1604                         //  #define INSTALL_ERR_EXEC     4
1605                         //  #define INSTALL_ERR_PLATFORM BASE+5
1606                         //  #define INSTALL_ERR_SYSTEM   BASE+6
1607                         //  #define INSTALL_ERR_USER_CANCEL BASE+7
1608                         if (cd == 10000+1) { //skip start notification
1609                            return;
1610                         }
1611                         codes = ["success", "ignore", "error:download", "error:generic",
1612                             "error:generic", "error:generic", "error:generic", "error:cancelled"];
1613                         if (cd > 19900) {
1614                             //we got process exit code (20000 + code) and it is not good
1615                             //for now treat everything as same error
1616                             if (cd == 20000 + 1602 || cd === 20000 - 2) {
1617                                 //-2 is exit code for welcome panel
1618                                 //1602 is MSI exit code for user cancel
1619                                 status = "error:cancelled";
1620                             } else {
1621                                 status = "error:generic";
1622                             }
1623                         } else if (cd >= 10000 && cd <= 19900) {
1624                             //this is DT error case
1625                             status = (cd >= 10000 && cd < 10000+codes.length) ?
1626                                 codes[cd-10000] : "error:unknown";
1627                         } else {
1628                             //Generally we do not expect codes in this range
1629                             // unless it is old DT?
1630                             //JRE7 DT will return 1 for any error
1631                             status = "error:generic";
1632                         }
1633                         if (isDef(cb.onInstallFinished)) {
1634                             cb.onInstallFinished(placeholder, "javafx",
1635                                                  status, s.isRelaunchNeeded());
1636                         }
1637                         if (cd == 0) { //proceed on success only
1638                             if (notNull(postInstallFunc)) {
1639                                 postInstallFunc();
1640                             }
1641                         }
1642                     }
1643                     //TODO: hook install progress callback once installer support it
1644                     if (isDef(cb.onInstallStarted)) {
1645                         cb.onInstallStarted(placeholder, "JavaFX",
1646                         //need to restart as DT plugin is used for sure ..
1647                         //TODO: restart not needed if can detect FX version
1648                         //  (if DT does not know how to detect then
1649                         //   need to restart)
1650                         // NOte - for launchable apps it is different!
1651                                             true, true);
1652                     }
1653                     var ret = 0;
1654                     try {
1655                        //try new way (callbacks first)
1656                        ret = p.installJavaFX(platform.javafx, handleResultFX);
1657                     } catch (ee) { //in IE it will throw exception,
1658                                    //in FF/Chrome will return 0
1659                         ret = 0;
1660                     }
1661                     if (ret == 0) { //failed to call installJavaFX with 2 args
1662                            // or we called it but it did nothing (returned -1)
1663                            // => will try to use JRE7 API (one arg and no callbacks)
1664                            ret = p.installJavaFX(platform.javafx);
1665                            setTimeout(function() {
1666                                //ret will be boolean here
1667                                setTimeout(function() {handleResultFX(ret ? 1:0)}, 0);
1668                            }, 0);
1669                     }
1670                 }
1671                 if (s.jre != "ok" || canJavaFXCoBundleSatisfy(platform)) {
1672                     //TODO: hook install progress callback once installer support it
1673                     //NB: we use setTimeout here to make sure main thread
1674                     //    will get control before we stuck in the call to launch installer
1675                     //    This way UI can be updated.
1676                     setTimeout(function() {
1677                         var handleResultJRE = function(cc) {
1678                             if (cc == 10000+1) { //skip start notification
1679                               return;
1680                             }
1681                             if (cc > 19900) {
1682                                //we got process exit code (20000 + code) and it is not good
1683                                //for now treat everything as same error
1684                                //TODO: separate user cancel event
1685                                status = "error:generic";
1686                             } else if (cc == -1) {
1687                                status = "error:generic";
1688                             } else if (cc > 10000) { //DT error
1689                                status = "error:generic";
1690                             } else if (cc == 0) {
1691                                 status = "success";
1692                             } else {
1693                                 status = "error:generic"; //just in case
1694                             }
1695                             if (isDef(cb.onInstallFinished)) {
1696                                cb.onInstallFinished(placeholder, "jre",
1697                                                status, s.isRelaunchNeeded());
1698                             }
1699                             //may also need to launch FX install but only on success!
1700                             if (cc == 0) {
1701                                 //revalidate, if it was cobundle install there is a chance we are good by now
1702                                 s = doValidate(platform);
1703                                 if (s != null && s.jre == "ok" && !noFXAutoInstall && s.fx != "ok" ) {
1704                                     setTimeout(installFX, 0);
1705                                 } else {
1706                                     //nothing more to install => call postInstallFunction
1707                                     if (postInstallFunc != null) {
1708                                         postInstallFunc();
1709                                     }
1710                                 }
1711                             }
1712                         }
1713                         if (isDef(cb.onInstallStarted)) {
1714                             cb.onInstallStarted(placeholder, "Java",
1715                                                 true, true);
1716                         }
1717
1718                         var ret = 0;
1719
1720                         try {
1721                            // pass in javafx requirement and see if we can
1722                            // install a co-bundle that satisfy the application
1723                            // requirement
1724                            ret = p.installJRE(platform.jvm, platform.javafx, 
1725                                     handleResultJRE);
1726                         } catch (ee) { //in IE it will throw exception,
1727                                        //in FF/Chrome will return 0
1728                             ret = 0;
1729                         }
1730
1731                         if (ret == 0) {
1732                                        
1733                             var jvm_req = platform.jvm;
1734
1735                             if (s.fx != "ok" && canJavaFXCoBundleSatisfy(platform)) {
1736                                 // We would like to avoid standalone JavaFX
1737                                 // Runtime install if possible (unless app
1738                                 // requires JRE 6*)
1739                                 // 
1740                                 // Starting in 7u6 - JavaFX runtime is co-bundle
1741                                 // inside JRE.
1742                                 // So if we need to install JavaFX, and the
1743                                 // application platform requirement can allow
1744                                 // JRE 7u6+ and FX 2.2+, we will try to increase
1745                                 // the minimum platform requirement to 7u6
1746                                 // to install co-bundle JRE, which should
1747                                 // satisfy the application requirement and avoid
1748                                 // standalone JavaFX install
1749                                 // 
1750                                 // override java and javafx version requirement to latest
1751                                 // co-bundle
1752                                 jvm_req = minJRECobundleVersion;
1753                                 if (platform.jvm.indexOf('*') != -1) {
1754                                     jvm_req += "*";
1755                                 } else if (platform.jvm.indexOf('+') != -1) {
1756                                     jvm_req += "+";
1757                                 }
1758                             }          
1759                             
1760                             try {
1761                                 //since 7-client/FX2.0 installJRE may take additional
1762                                 // callback argument.
1763                                 ret = p.installJRE(jvm_req, handleResultJRE);
1764                             } catch (ee) { //in IE it will throw exception,
1765                                 //in FF/Chrome will return 0
1766                                 ret = 0;
1767                             }
1768
1769                             if (ret == 0) {
1770                                // //failed to call installRE
1771                                // or we called it but it did nothing (returned -1)
1772                                // => will try to use old API (one arg and no callbacks)
1773                                try {
1774                                   ret = p.installJRE(jvm_req);
1775                                } catch (ee) {
1776                                    ret = 0; //just in case we got exception
1777                                }
1778                                setTimeout(function() {
1779                                    setTimeout(function() {handleResultJRE(ret)}, 0);
1780                                }, 0);
1781                             }
1782                         }
1783                     }, 0);
1784                 } else if (!noFXAutoInstall && s.fx != "ok") {
1785                     setTimeout(installFX, 0);
1786                 }
1787             } else {
1788                 //auto install not possible => can only do manual install
1789                 //
1790                 //Start from JRE install first, even if it is JavaFX case but cobundle can help
1791                 if (s.jre != "ok" || canJavaFXCoBundleSatisfy(platform)) {
1792                     if (isDef(cb.onInstallStarted)) {
1793                         cb.onInstallStarted(placeholder, "Java",
1794                                             false, getPlugin() != null);
1795                     }
1796                     startManualJREInstall();
1797                 } else if (s.fx != "ok") {
1798                     if (isDef(cb.onInstallStarted)) {
1799                         cb.onInstallStarted(placeholder, "JavaFX",
1800                                             false, getPlugin() != null);
1801                     }
1802                     startManualFXInstall();
1803                 } else { //what it could be??
1804                   reportPlatformError(app, s, cb);
1805                 }
1806             }
1807         } else {
1808             //nothing to install
1809             if (postInstallFunc != null) {
1810                 postInstallFunc();
1811             }
1812             return true;
1813         }
1814         //no install initiated
1815         return false;
1816     }
1817
1818     //just open download URL in new window
1819     function startManualJREInstall() {
1820         w.open(getJreUrl());
1821     }
1822
1823     //just open download URL in new window
1824     function startManualFXInstall() {
1825         w.open(javafxURL);
1826     }
1827
1828     function defaultGetSplashHandler(ld) {
1829         if (ld.placeholder != null) {
1830             var _w = ld.width, _h = ld.height;
1831             //prepare image
1832             //if width and height are relative then comparison with int will be false
1833             //  and we will end up using large image. This is on purpose
1834             //  as it is unlikely that relative dimensions are used for tiny applet areas
1835             var isBig = !(_w < 100 && _h < 100);
1836             var iU = isBig ? 'javafx-loading-100x100.gif' : 'javafx-loading-25x25.gif';
1837             var iW = isBig ? 80 : 25;
1838             var iH = isBig ? 80 : 25;
1839
1840             var img = d.createElement("img");
1841             img.src = jscodebase + iU;
1842             img.alt = "";
1843             //position in the center of the container
1844             img.style.position = "relative";
1845             img.style.top = "50%";
1846             img.style.left = "50%";
1847             img.style.marginTop =  normalizeDimension(-iH/2);
1848             img.style.marginLeft = normalizeDimension(-iW/2);
1849
1850             return img;
1851         } else {
1852             //webstart or install case
1853             //TODO: show some html splash for webstart? how to hide it?
1854             return null;
1855         }
1856     }
1857
1858     function defaultGetNoPluginMessageHandler(app) {
1859         if (app.placeholder != null) {
1860             var p = d.createElement("p");
1861             p.appendChild(d.createTextNode("FIXME - add real message!"));
1862             return p;
1863         } //no op if not embedded content
1864         return null;
1865     }
1866
1867     //remove all child elements for given node
1868     function wipe(c) {
1869         while(c.hasChildNodes()) c.removeChild(c.firstChild);
1870     }
1871
1872     function defaultInstallStartedHandler(placeholder, component, isAuto, restartNeeded) {
1873         if (placeholder != null) {
1874             var code = null;
1875             if (isAuto) {
1876                 code = (component == "JavaFX") ?
1877                     "install:inprogress:javafx": "install:inprogress:jre";
1878             } else {
1879                 code = (component == "JavaFX") ?
1880                     "install:inprogress:javafx:manual" : "install:inprogress:jre:manual";
1881             }
1882
1883             appletInfoMsg(code);
1884         }
1885     }
1886
1887     function defaultInstallFinishedHandler(placeholder, component, status, relaunch) {
1888         var t;
1889         if (status != "success") {
1890             var msg = null;
1891             if (component == "javafx") {
1892                 if (!doublecheckJrePresence()) { //guess if we failed due to no JRE
1893                     //need to request to install JRE first
1894                     msg = "install:fx:error:nojre";
1895                 } else {
1896                     msg = "install:fx:"+status;
1897                 }
1898             } else { //must be JRE error
1899                 msg = "install:jre:"+status;
1900             }
1901             if (placeholder != null) {
1902                 t = appletErrorMsg(msg, null);
1903
1904                 //Instead of hiding splash and applet we simply clear the container
1905                 //We are not going to show neither splash nor applet anyways ...
1906                 wipe(placeholder);
1907                 placeholder.appendChild(t);
1908             } else {
1909                 w.alert(webstartErrorMsg(msg));
1910             }
1911         } else { //success
1912             if (relaunch) {
1913                 t = appletInfoMsg("install:fx:restart");
1914
1915                 //Instead of hiding splash and applet we simply clear the container
1916                 //We are not going to show neither splash nor applet anyways ...
1917                 wipe(placeholder);
1918                 placeholder.appendChild(t);
1919             }
1920         }
1921     }
1922
1923     function defaultDeployErrorHandler(app, r) {
1924         if (r == null) {
1925             code = "success";
1926         } else if (r.isUnsupportedBrowser()) {
1927             code = "browser";
1928         } else if (r.jreStatus() != "ok") {
1929             code = "jre:" + r.jreStatus();
1930         } else if (r.javafxStatus() != "ok") {
1931             code = "javafx:" + r.javafxStatus();
1932         } else if (r.isRelaunchNeeded()) {
1933             code = "relaunch";
1934         } else {
1935             code = "unknown " + r.toString();
1936         }
1937
1938         if (app.placeholder != null) {//embedded app
1939             showAppletError(app.id, code, null);
1940         } else { //webstart or install case
1941             w.alert(webstartErrorMsg(code));
1942         }
1943     }
1944
1945     function defaultRuntimeErrorHandler(id) {
1946         var el_applet = findAppletDiv(id);
1947
1948         if (getErrorDiv(id) != null) {
1949             showAppletError(id, "launch:fx:generic:embedded",
1950                 function() {showHideApplet(findAppletDiv(id), false); return false;});
1951         } else {
1952             w.alert(webstartErrorMsg("launch:fx:generic"));
1953         }
1954     }
1955
1956     //TODO: Does availability of object mean initialization is completed (or even started?)
1957     //Can we expect that any subsequent call to this object will actually work?
1958     //Perhaps it is false alarm
1959     function getPlugin() {
1960         navigator.plugins.refresh(false);
1961         return document.getElementById('dtjavaPlugin');
1962     }
1963
1964     function installNativePlugin() {
1965         //already installed?
1966         if (getPlugin() != null) return;
1967
1968         //can not install plugin now as page has no body yet, postpone
1969         //NB: use cbDone here to avoid infinite recursion (corner case)
1970         if (!notNull(d.body) && !cbDone) {
1971             addOnDomReady(function() {
1972                 installNativePlugin();
1973             });
1974             return;
1975         }
1976
1977         var p = null;
1978         if (ua.ie) {
1979             p = d.createElement('object');
1980             //TODO: zero size does not work?? How we can make it less intrusive for layout?
1981             p.width  = '1px';
1982             p.height = '1px';
1983             //new CLSID, one with 0000-0000 had been kill bit
1984             p.classid = 'clsid:CAFEEFAC-DEC7-0000-0001-ABCDEFFEDCBA';
1985         } else {
1986             // Safari and Opera browsers find the plugin but it
1987             // doesn't work, so until we can get it to work - don't use it.
1988             if (!ua.wk && !ua.op && navigator.mimeTypes != null) {
1989                 // mime-type of the DeployToolkit plugin object
1990                 // (do not care about old DT plugin anymore)
1991                 var mimeType = 'application/java-deployment-toolkit';
1992                 var newDT = false;
1993                 for (var i = 0; i < navigator.mimeTypes.length; i++) {
1994                     var mt = navigator.mimeTypes[i];
1995                     newDT = newDT || ((mt.type == mimeType) && mt.enabledPlugin);
1996                 }
1997                 if (newDT) {
1998                     p = d.createElement('embed');
1999                     p.setAttribute('type', newDT ? mimeType : oldMimeType);
2000                     p.setAttribute('hidden', 'true');
2001                 }
2002             }
2003         }
2004         if (p != null) {
2005             p.setAttribute('id', 'dtjavaPlugin');
2006             d.body.appendChild(p);
2007         }
2008     }
2009
2010     var appletCounter = 0;
2011
2012     function prepareAppletID(ld) {
2013         if (notNull(ld.id)) {
2014             return ld.id;
2015         } else {
2016             appletCounter++;
2017             return ("dtjava-app-" + appletCounter);
2018         }
2019     }
2020
2021     //returns object that represents an applet/object tag
2022     function getAppletSnippet(ld, platform, cb) {
2023         //we use wrapper div here as changing style on applet tag
2024         // cause liveconnect to be initialized and slows down startup
2025         var wrapper = d.createElement("div");
2026         wrapper.width = normalizeDimension(ld.width);
2027         wrapper.height = normalizeDimension(ld.height);
2028         wrapper.id = ld.id + "-app";
2029         //without this it splash will not work in Chrome
2030         wrapper.style.position = "relative";
2031
2032         var r = d.createElement("applet"); //TODO: use object!
2033
2034         r.code = "dummy.class";
2035         r.id = ld.id;
2036         r.width = normalizeDimension(ld.width);
2037         r.height = normalizeDimension(ld.height);
2038
2039         //things added unconditionally
2040         var sparams = {"jnlp_href" : ld.url,
2041             "java_status_events" : true,
2042             "type" : "application/x-java-applet"};
2043
2044         if (notNull(ld.jnlp_content)) {
2045             sparams['jnlp_embedded'] = ld.jnlp_content;
2046         }
2047         if (notNull(platform.javafx)) {
2048             //for swing applications embedding FX we do not want this property as it will 
2049             // trigger FX toolkit and lead to app failure!
2050             if (!notNull(ld.toolkit) || ld.toolkit == "fx") {
2051                sparams["javafx_version"] = ((platform.javafx == "*") ? "2.0+" : platform.javafx);
2052             }
2053             //FX requires new VM per applet, do it unconditionally
2054             sparams["separate_jvm"] = true;
2055             sparams["javafx_applet_id"] = r.id;
2056             //enable scripting for FX unconditionally for now
2057             sparams["scriptable"] = true;
2058         } else {
2059             if (ld.scriptable) {
2060                 sparams["scriptable"] = true;
2061             }
2062             if (ld.sharedjvm) {
2063                 sparams["separate_jvm"] = true;
2064             }
2065         }
2066         if (notNull(platform.jvmargs)) {
2067             sparams["java_arguments"] = platform.jvmargs;
2068         }
2069
2070         //prepare parameters first
2071         var key, p;
2072         for (key in ld.params) {
2073             //do not let to override system parameters
2074             if (!notNull(sparams[key])) {
2075                 p = d.createElement("param");
2076                 p.name = key;
2077                 p.value = ld.params[key];
2078                 r.appendChild(p);
2079             }
2080         }
2081         for (key in sparams) {
2082             p = d.createElement("param");
2083             p.name = key;
2084             p.value = sparams[key];
2085             r.appendChild(p);
2086         }
2087
2088         if (isDef(cb.onGetNoPluginMessage)) {
2089             p = d.createElement("noapplet");
2090             var t = cb.onGetNoPluginMessage(ld);
2091             p.appendChild(t);
2092             //TODO: FIXME: following line fails for me in IE7??
2093             //r.appendChild(p);
2094         }
2095
2096         wrapper.appendChild(r);
2097         return wrapper;
2098     }
2099
2100     function findAppletDiv(id) {
2101         //TODO: FIXME: in static deployment scenario this seem to cause restart of plugin (in FF)
2102         //Weird but similar code works in the deployJava.js ...
2103         //TODO: reinvestigate
2104         var el = d.getElementById(id + "-app");
2105         if (el == null) { //wrapping div for applet is not required
2106             el = d.getElementById(id);
2107         }
2108         return el;
2109     }
2110
2111     //IMPORTANT: whilst we can update style on the applet element itself
2112     //  this is not best idea as this may also cause wait till liveconnect
2113     //  is initialized and slow startup.
2114     function showHideApplet(div, hide) {
2115         if (!notNull(div)) return;
2116         if (hide) {
2117             div.style.left = -10000;
2118         } else {
2119             div.style.left = "0px";
2120         }
2121     }
2122
2123     function showHideDiv(div, hide) {
2124         if (!notNull(div)) return;
2125         if (hide) {
2126             div.style.visibility = "hidden";
2127         } else {
2128             div.style.visibility = "visible";
2129         }
2130     }
2131
2132     function doHideSplash(id) {
2133         try {
2134             var errPane = getErrorDiv(id);
2135             if (errPane != null && errPane.style != null && errPane.style.visibility == "visible") {
2136                 //if we have error pane shown then ignore this request
2137                 // (could be race condition and applet is asking to hide splash to show error too)
2138                 return;
2139             }
2140
2141             var el = findAppletDiv(id);
2142             showHideApplet(el, false);
2143
2144             //show applet first and then hide splash to avoid blinking
2145             showHideDiv(d.getElementById(id + "-splash"), true);
2146         } catch(err) {}
2147     }
2148
2149     var javafxURL = "http://java.com/javafx";
2150
2151     //TODO: validate ALL messages are shown as expected and when expected (for applet/webstart/install)
2152     var errorMessages = {
2153         "launch:fx:generic" : ["JavaFX application could not launch due to system configuration.",
2154             " See ", "a", "http://java.com/javafx", "java.com/javafx",
2155             " for troubleshooting information."],
2156         "launch:fx:generic:embedded" : ["JavaFX application could not launch due to system configuration ",
2157             "(", "onclick", "show error details", ").",
2158             " See ", "a", "http://java.com/javafx", "java.com/javafx",
2159             " for troubleshooting information."],
2160         "install:fx:restart" : ["Restart your browser to complete the JavaFX installation,",
2161             " then return to this page."],
2162         "install:fx:error:generic" : ["JavaFX install not completed.",
2163             " See ", "a", "http://java.com/javafx", "java.com/javafx",
2164             " for troubleshooting information."],
2165         "install:fx:error:download" : ["JavaFX install could not start because of a download error.",
2166             " See ", "a", "http://java.com/javafx", "java.com/javafx",
2167             " for troubleshooting information."],
2168         "install:fx:error:cancelled" : ["JavaFX install was cancelled.",
2169             " Reload the page and click on the download button to try again."],
2170         "install:jre:error:cancelled" : ["Java install was cancelled.",
2171             " Reload the page and click on the download button to try again."],
2172         "install:jre:error:generic" : ["Java install not completed.",
2173             " See ", "a", "http://java.com/", "java.com",
2174             " for troubleshooting information."],
2175         "install:jre:error:download" : ["Java install could not start because of a download error.",
2176             " See ", "a", "http://java.com/", "java.com/",
2177             " for troubleshooting information."],
2178         "install:inprogress:jre" : ["Java install in progress."],
2179         "install:inprogress:javafx" : ["JavaFX install in progress."],
2180         "install:inprogress:javafx:manual" : ["Please download and run JavaFX Setup from ",
2181             "a", getFxUrl(null), "java.com/javafx",
2182             ". When complete, restart your browser to finish the installation,",
2183             " then return to this page."],
2184         "install:inprogress:jre:manual" : ["Please download and run Java Setup from ",
2185             "a", getJreUrl(), "java.com/download",
2186             ". When complete, reload the page."],
2187         "install:fx:error:nojre" : ["b", "Installation failed.", "br",
2188             "Java Runtime is required to install JavaFX and view this content. ",
2189             "a", getJreUrl(), "Download Java Runtime",
2190             " and run the installer. Then reload the page to install JavaFX."],
2191         "browser":    [ 'Content can not be displayed using your Web browser. Please open this page using another browser.'],
2192         "jre:none":    [ 'JavaFX application requires a recent Java runtime. Please download and install the latest JRE from ',
2193             'a', 'http://java.com', "java.com", '.'],
2194         "jre:old" :    [ 'JavaFX application requires a recent Java runtime. Please download and install the latest JRE from ',
2195             'a', 'http://java.com', "java.com", '.'],
2196         "jre:plugin":  ['b', "A Java plugin is required to view this content.", 'br',
2197             "Make sure that ", "a", 'http://java.com', "a recent Java runtime",
2198             " is installed, and the Java plugin is enabled."],
2199         "jre:blocked": ["Please give Java permission to run. This will allow Java to present content provided on this page."],
2200         "jre:unsupported": ["b", "Java is required to view this content but Java is currently unsupported on this platform.",
2201             "br", "Please consult ", "a", "http://java.com", "the Java documentation",
2202             " for list of supported platforms."],
2203         "jre:browser" : ["b", "Java plugin is required to view this content but Java plugin is currently unsupported in this browser.",
2204             "br", "Please try to launch this application using other browser. Please consult ",
2205             "a", "http://java.com", "the Java documentation",
2206             " for list of supported browsers for your OS."],
2207         "javafx:unsupported" : ["b", "JavaFX 2.0 is required to view this content but JavaFX is currently unsupported on this platform.",
2208             "br", "Please consult ", "a", javafxURL, "the JavaFX documentation",
2209             " for list of supported platforms."],
2210         "javafx:old" :    [ 'This application requires newer version of JavaFX runtime. ',
2211             'Please download and install the latest JavaFX Runtime from ',
2212             'a', javafxURL, "java.com/javafx", '.'],
2213         "javafx:none" : ["b", "JavaFX 2.0 is required to view this content.",
2214             "br", "a", javafxURL, "Get the JavaFX runtime from java.com/javafx",
2215             " and run the installer. Then restart the browser."],
2216         "javafx:disabled" : ["JavaFX is disabled. Please open Java Control Panel, switch to Advanced tab and enable it. ",
2217             "Then restart the browser."],
2218         "jre:oldplugin" : ["New generation Java plugin is required to view this content." +
2219                 " Please open Java Control Panel and enable New Generation Java Plugin."],
2220         "jre:disabled" : ["Java plugin appear to be disabled in your browser. ",
2221                 " Please enable Java in the browser options."]
2222     };
2223
2224     //assume we get list of (tag, param, text) where both param and tag are optional
2225     // Supported tags:
2226     //  ("a", href value, link text)
2227     //  ("b", text)
2228     //  ("br")
2229     //  (text) //text can not be the same as any of tag names
2230     function msgAsDOM(lst, extra, onClickFunc) {
2231         var i = 0;
2232         var root = d.createElement("p");
2233
2234         if (extra != null) {
2235             root.appendChild(extra);
2236         }
2237         var el;
2238         while (i < lst.length) {
2239             switch (lst[i]) {
2240                 case "a":
2241                     el = d.createElement(lst[i]);
2242                     el.href = lst[i + 1];
2243                     el.appendChild(d.createTextNode(lst[i + 2]));
2244                     i = i + 2;
2245                     break;
2246                 case "br":
2247                     el = d.createElement(lst[i]);
2248                     break;
2249                 case "b":
2250                     el = d.createElement(lst[i]);
2251                     el.appendChild(d.createTextNode(lst[i + 1]));
2252                     i++;
2253                     break;
2254                 case "onclick":
2255                     el = d.createElement("a");
2256                     el.href = "";
2257                     if (onClickFunc == null) {
2258                        onClickFunc = function() {return false;}
2259                     }
2260                     el.onclick = onClickFunc;
2261                     el.appendChild(d.createTextNode(lst[i + 1]));
2262                     i = i + 1;
2263                     break;
2264                 default:
2265                     el = d.createTextNode(lst[i]);
2266                     break;
2267             }
2268             root.appendChild(el);
2269             i++;
2270         }
2271         return root;
2272     }
2273
2274     function webstartErrorMsg(code) {
2275         var m = "";
2276         var lst = errorMessages[code];
2277         var i = 0;
2278         if (notNull(lst)) {
2279           while (i < lst.length) {
2280               if (lst[i] != 'a' && lst[i] != 'br' && lst[i] != 'b') {
2281                   m += lst[i];
2282               } else if (lst[i] == 'a') { //next element is link => skip it
2283                   i++;
2284               }
2285               i++;
2286           }
2287         } else {
2288             m = "Unknown error: ["+code+"]";
2289         }
2290         return m;
2291     }
2292
2293     function getErrorDiv(id) {
2294         return d.getElementById(id + "-error");
2295     }
2296
2297     function showAppletError(id, code, onclickFunc) {
2298         var pane = getErrorDiv(id);
2299
2300         if (!notNull(pane)) { //should not be possible, we add error pane right a way and then add it again before we add splash/app
2301             return;
2302         }
2303
2304         //remove old content in the ERROR PANE only (if any)
2305         wipe(pane);
2306
2307         //populate and show pane
2308         pane.appendChild(appletErrorMsg(code, onclickFunc));
2309         pane.style.visibility = "visible";
2310
2311         //hide splash and applet
2312         showHideDiv(d.getElementById(id+"-splash"), true);
2313         showHideApplet(findAppletDiv(id), true);
2314     }
2315
2316     //returns DOM subtree
2317     function appletErrorMsg(code, onclickFunc) {
2318         var out = d.createElement("div");
2319         var img = d.createElement("img");
2320         img.src = jscodebase + 'error.png';
2321         img.width = '16px';
2322         img.height = '16px';
2323         img.alt = "";
2324         img.style.cssFloat = "left";
2325         img.style.styleFloat = "left"; //IE way
2326         img.style.margin = "0px 10px 60px 10px";
2327         img.style.verticalAlign="text-top";
2328
2329         var m = errorMessages[code];
2330         //error message is missing => show code as fallback
2331         if (!notNull(m)) {
2332             m = [code];
2333         }
2334
2335         var hideFunc = null;
2336
2337         if (isDef(onclickFunc)) {
2338             hideFunc = function() {
2339                 if (notNull(out.parentNode)) {
2340                   out.parentNode.removeChild(out);
2341                 }
2342                 try {
2343                     onclickFunc();
2344                 } catch (e) {}
2345                 return false;
2346             }
2347         }
2348
2349         out.appendChild(msgAsDOM(m, img, hideFunc));
2350         return out;
2351     }
2352
2353     //returns DOM subtree
2354     function appletInfoMsg(code) {
2355         var out = d.createElement("div");
2356
2357         var m = errorMessages[code];
2358         //error message is missing => show code as fallback
2359         if (!notNull(m)) {
2360             m = [code];
2361         }
2362
2363         out.appendChild(msgAsDOM(m, null, null));
2364         return out;
2365     }
2366
2367     function normalizeApp(ld, acceptString) {
2368         var app = null;
2369         //normalize launch descriptor
2370         if (notNull(ld)) {
2371             //could be either url or set of parameters
2372             if (acceptString && typeof ld === 'string') {
2373                 app = new dtjava.App(ld, null);
2374             } else if (ld instanceof dtjava.App) {
2375                 app = ld;
2376             } else {
2377                 app = new dtjava.App(ld.url, ld);
2378             }
2379         }
2380         return app;
2381     }
2382
2383     function setupAppletCallbacks(platform, callbacks) {
2384         //set default callbacks
2385         var cb = new dtjava.Callbacks(callbacks);
2386
2387         //disable splash if it is was not requested explicitly and
2388         // it is not JavaFX app
2389         if (platform.javafx == null && cb.onGetSplash === defaultGetSplashHandler) {
2390             cb.onGetSplash = null;
2391         }
2392         return cb;
2393     }
2394
2395     //width and height in styles need to have unit type explicitly referenced
2396     // or they will not conform to strict doctypes
2397     //On other hand we can have relative dimensions, e.g. 100% and these are fine without units
2398     //
2399     //This method will add unit type to numeric dimension specifications. E.g.
2400     //   400 => 400px
2401     //   -10 => -10px
2402     //   50% => 50%
2403     function normalizeDimension(v) {
2404         if (isFinite(v)) {
2405             return v + 'px';
2406         } else {
2407             return v;
2408         }
2409     }
2410
2411     //wrap given node s in the div
2412     function wrapInDiv(ld, s, suffix) {
2413         var sid = ld.id + "-" + suffix;
2414         var div = d.createElement("div");
2415         div.id = sid;
2416         div.style.width = normalizeDimension(ld.width);
2417         //this does not work well for different browsers
2418         //if height is relative ...
2419         //For firefox it becomes better if 100% is hardcode
2420         // but then image is off in Chrome and it does not work in IE too ...
2421         div.style.height = normalizeDimension(ld.height);
2422         div.style.position = "absolute";
2423         //TODO: provide way to specify bgcolor
2424         // Perhaps app.style.bgcolor, app.style.splash-image, ... ?
2425         // What was the param name supported by regular applet?
2426         div.style.backgroundColor = "white";
2427         if (s != null) {
2428             div.appendChild(s);
2429         }
2430         return div;
2431     }
2432
2433     var pendingCallbacks = {};
2434
2435     function doInstallCallbacks(id, cb) {
2436         if (cb == null) {
2437             cb = pendingCallbacks[id];
2438             if (notNull(cb)) {
2439               pendingCallbacks[id] = null;
2440             } else {
2441                 return;
2442             }
2443         }
2444         var a = document.getElementById(id);
2445         if (!notNull(a)) return;
2446
2447         if (isDef(cb.onJavascriptReady)) {
2448             var onReady = cb.onJavascriptReady;
2449             if (a.status < 2) { //not READY yet
2450               a.onLoad = function() {
2451                   onReady(id);
2452                   a.onLoad = null; //workaround bug in plugin for IE in JRE7
2453               }
2454             }
2455         }
2456
2457         if (isDef(cb.onRuntimeError)) {
2458             if (a.status < 3) { //not ERROR or READY yet
2459                a.onError = function() {
2460                   cb.onRuntimeError(id);
2461                   //This used to be added as
2462                   //  "workaround bug in plugin for IE in JRE7"
2463                   //I do not have recollection what the bug was
2464                   // and can not reproduce it now
2465                   //(perhaps multiple calls into callback?)
2466                   //With FX 2.0 it cause restart of the applet in IE
2467                   // for reason that is not completely clear
2468                   //Disable it for now
2469                   /*   a.onError = null; */
2470               }
2471             } else if (a.status == 3) { //already failed, call handler in place
2472                cb.onRuntimeError(id);
2473             }
2474         }
2475     }
2476
2477
2478     //we can not install applet callbacks until applet is instantiated as
2479     //hook entry points are not defined and we do not control when applet is
2480     //instantiated as developer may not add it to the DOM tree for a while.
2481     //
2482     //Therefore what we do is we insert <script> element AFTER applet tag
2483     //to initiate install after applet tag is parsed
2484     //
2485     //However, we can not
2486     //
2487     function getSnippetToInstallCallbacks(id, cb) {
2488         if (!notNull(cb) || !(isDef(cb.onDeployError) || isDef(cb.onJavascriptReady))) {
2489             return null;
2490         }
2491
2492         var s = d.createElement("script");
2493         pendingCallbacks[id] = cb;
2494         s.text = "dtjava.installCallbacks('"+id+"')";
2495         return s;
2496     }
2497
2498     function getErrorPaneSnippet(app) {
2499         var paneDiv = wrapInDiv(app, null, "error");
2500         paneDiv.style.visibility = "hidden";
2501         return paneDiv;
2502     }
2503
2504     function doEmbed(ld, platform, callbacks) {
2505         var app = normalizeApp(ld, false);
2506         //required argument is missing
2507         if (!(notNull(app) && notNull(app.url) &&
2508               notNull(app.width) && notNull(app.height) && notNull(app.placeholder))) {
2509             //deployment error, not runtime => exception is ok
2510             throw "Required attributes are missing! (url, width, height and placeholder are required)";
2511         }
2512
2513         app.id = prepareAppletID(app);
2514
2515         //if placeholder is passed as id => find DOM node
2516         if ((typeof app.placeholder == "string")) {
2517            var p = d.getElementById(app.placeholder);
2518            if (p == null) {
2519                throw "Application placeholder [id="+app.placeholder+"] not found.";
2520            }
2521             app.placeholder = p;
2522         }
2523
2524         //we may fail before we even try to add splash. E.g. because it is unsupported platform
2525         //make sure we have error pane in place to show error
2526         app.placeholder.appendChild(getErrorPaneSnippet(app));
2527
2528         //if we got array we need to copy over!
2529         platform = new dtjava.Platform(platform);
2530
2531         var cb = setupAppletCallbacks(platform, callbacks);
2532
2533         //allow family match to match next family
2534         //Once we get to java layer we will deal with it there
2535         var v = doValidateRelaxed(platform);
2536         var launchFunction = function() {
2537             var appSnippet = getAppletSnippet(app, platform, cb);
2538             var splashSnippet = (cb.onGetSplash == null) ? null : cb.onGetSplash(ld);
2539
2540             //what we try to do:
2541             // placeholder need to have relative positioning (then splash will pe position relative to it)
2542             // if splash is present it needs to have position "absolute", then it will not occupy space
2543             //  and can be placed on top of applet
2544             app.placeholder.style.position = "relative";
2545             if (splashSnippet != null) {
2546                 //position splash on top of applet area and hide applet temporarily
2547                 var ss = wrapInDiv(app, splashSnippet, "splash");
2548                 showHideDiv(ss, false);
2549                 showHideApplet(appSnippet, true);
2550
2551                 wipe(app.placeholder);
2552                 app.placeholder.appendChild(getErrorPaneSnippet(app));
2553                 app.placeholder.appendChild(ss);
2554                 app.placeholder.appendChild(appSnippet);
2555             } else {
2556                 wipe(app.placeholder);
2557                 app.placeholder.appendChild(getErrorPaneSnippet(app));
2558                 app.placeholder.appendChild(appSnippet);
2559             }
2560             //Note: this is not needed as we use setTimeout for the same
2561             //var cbSnippet = getSnippetToInstallCallbacks(app.id, cb);
2562             //if (cbSnippet != null) {
2563             //    app.placeholder.appendChild(cbSnippet);
2564             //}
2565             setTimeout(function() {doInstallCallbacks(app.id, cb)}, 0);
2566         };
2567
2568         //can not launch yet
2569         if (v != null) {
2570             resolveAndLaunch(app, platform, v, cb, launchFunction);
2571         } else {
2572             launchFunction();
2573         }
2574     }
2575
2576     function extractApp(e) {
2577         if (notNull(e)) {
2578             var w = e.width;    //TODO: do we need to extract number? e.g. if it was 400px? or 100%?
2579             var h = e.height;
2580             var jnlp = "dummy"; //Can find it from list of parameters but it is not really needed in
2581                                 //static deployment scenario
2582             return new dtjava.App(jnlp, {
2583                 id: e.id,
2584                 width: w,
2585                 height: h,
2586                 placeholder: e.parentNode
2587             });
2588         } else {
2589             throw "Can not find applet with null id";
2590         }
2591     }
2592
2593     function processStaticObject(id, platform, callbacks) {
2594         var a = d.getElementById(id); //TODO: use findAppletDiv??
2595         var app = extractApp(a);
2596
2597         var cb = setupAppletCallbacks(platform, callbacks);
2598         //Ensure some platform is set
2599         platform = new dtjava.Platform(platform);
2600
2601         var launchFunc = function() {
2602             //add error pane
2603             app.placeholder.insertBefore(getErrorPaneSnippet(app), a);
2604
2605             if (cb.onGetSplash != null) {
2606                 //TODO: show splash if it was not hidden yet!
2607                 var splashSnippet = cb.onGetSplash(app);
2608                 if (notNull(splashSnippet)) {
2609                     var ss = wrapInDiv(app, splashSnippet, "splash");
2610                     if (notNull(ss)) {
2611                         app.placeholder.style.position = "relative";
2612                         app.placeholder.insertBefore(ss, a);
2613                         showHideApplet(a, true);
2614                     }
2615                 }
2616             }
2617
2618             //TODO: install applet callbacks if they are provided
2619             //Note - in theory we need to check if callbacks are supported too
2620             // but if detection was not possible then it is hard to do
2621             //they always wotk for FX or jre 7+ but how validate this?
2622             //otherwise attempt to set them will block js and then trigger exception ...
2623         }
2624
2625         var v = doValidateRelaxed(platform);
2626         if (v != null) {
2627             //TODO: Problem
2628             //  if FX missing and static deployment
2629             // then JRE will try to autoinstall itself - this will cause popup
2630             // Then DT will detect problem and also initiate install too
2631             //   a) double install
2632             //   b) if popup is canceled then we still offer to install again but it will not help applet to launch
2633             //   c) popup is unconditional and really ugly ...
2634             //But popup comes from JRE7 - can not fix it, on other hand 6 will go manual install route
2635
2636             resolveAndLaunch(app, platform, v, cb, launchFunc);
2637         } else {
2638             launchFunc();
2639         }
2640     }
2641
2642     function doRegister(id, platform, cb) {
2643         //we will record static object and process it once onload is done
2644         addOnDomReady(function() {
2645             processStaticObject(id, platform, cb);
2646         });
2647     }
2648
2649     //perform basic (lightweight) initialization
2650     init();
2651
2652     /**
2653      The Java Deployment Toolkit is utility to deploy Java content in
2654      the browser as applets or applications using right version of Java.
2655      If needed it can initiate upgrade of user's system to install required
2656      components of Java platform.
2657      <p>
2658      Note that some of Deployment Toolkit methods may not be fully operational if
2659      used before web page body is loaded (because DT native plugins could not be instantiated).
2660      If you intend to use it before web page DOM tree is ready then dtjava.js needs to be loaded inside the
2661      body element of the page and before use of other DT APIs.
2662
2663      @class dtjava
2664      @static */
2665     return {
2666         /**
2667          Version of Javascript part of Deployment Toolkit.
2668          Increasing lexicographically.
2669
2670          @property version
2671          @type string
2672          */
2673         version: "20120720",
2674
2675         /**
2676          Validate that platform requirements are met.
2677
2678          @param platform {Platform}
2679          (Optional) set of platform requirements.
2680          <p>
2681
2682          Default settings are
2683          <ul>
2684          <li>platform.jvm : "1.6+"
2685          <li>platform.javafx : null
2686          <li>platform.plugin : "*"
2687          </ul>
2688
2689          @return {PlatformMismatchEvent}
2690          Returns null if all requirements are met.
2691          Return PlatformMismatchEvent describing the problem otherwise.
2692          */
2693         validate: function(platform) {
2694             return doValidate(platform);
2695         },
2696
2697         /**
2698          Perform install of missing components based on given
2699          platform requirements. By default if automated install is
2700          not possible then manual install will be offered.
2701
2702          @method install
2703          @param platform {Platform}
2704          Description of platform requirements.
2705          @param callbacks {Callbacks}
2706          Optional set of callbacks to customize install experience.
2707          @return {boolean}
2708          Returns true if install was initiated.
2709
2710          */
2711         install: function(platform, callbacks) {
2712             return doInstall(null, platform, callbacks, null);
2713         },
2714
2715         //              (TODO: AI: what are limitations on "connect back to origin host?"
2716         //                   can someone provide us fake JNLP url to get access to other host?
2717         //                   Perhaps we should support this for relative URLs only?)
2718         /**
2719          Launch application (not embedded into browser) based on given
2720          application descriptor. If launch requirements are not met
2721          then autoinstall may be initiated if requested and supported.
2722          By default autoinstall is disabled.
2723
2724          @method launch
2725          @param ld {App | string | array}
2726          Application launch descriptor. Could be defined as one of following:
2727          <ul>
2728          <li>instance of App object,
2729          <li>string with URL of application JNLP file
2730          <li>or array (where URL attribute is required)
2731          </ul>
2732          At least link to JNLP file must be provided (could be full URL or relative to
2733          document location).
2734          <p>
2735          Note that passing parameters through the Apps object is not supported by this method.
2736          Any parameters specified will be ignored.
2737
2738          @param platform {Platform}
2739          Optional platform requirements (such as JRE and JavaFX versions).
2740
2741          @param callbacks {Callbacks | array}
2742          Optional set of callbacks. See Callbacks for details.
2743          */
2744             //this will not use jvargs either but we do not necessary need to document it
2745         launch: function(ld, platform, callbacks) {
2746             return doLaunch(ld, platform, callbacks);
2747         },
2748
2749         /**
2750          Embeds application into browser based on given application descriptor
2751          (required elements: url of JNLP file, width and height, id or reference to placeholder node).
2752          <p>
2753          If JRE or JavaFX installation is required then default handler is to return "click to install" html snippet.
2754          To enable autoinstall custom onDeployError handler need to be used.
2755          <p>
2756          If applet can not be launched because platform requirements are not met
2757          (e.g. DT plugin is not available or mandatory parameters are missing)
2758          return value will be null.
2759          <p>
2760          Set applet identifier in the launch descriptor if you want to name your
2761          applet in the DOM tree (e.g. to use it from javascript later).
2762
2763          @method embed
2764          @param ld {App | string | array}
2765          Application launch descriptor. Could be defined as one of following:
2766          <ul>
2767          <li>instance of App object,
2768          <li>array (where attribute names are same as in App object)
2769          </ul>
2770          At least link to JNLP file, width and height must be provided.
2771          @param platform {Platform}
2772          Optional platform requirements (such as JRE and JavaFX versions).
2773          @param cb {Callbacks | array}
2774          Optional set of callbacks. See Callbacks for details.
2775          @return {void}
2776          */
2777         embed: function(ld, platform, cb) {
2778             return doEmbed(ld, platform, cb);
2779         },
2780
2781         /**
2782          Registers statically deployed Java applet to customize loading experience
2783          if Javascript is enabled.
2784          <p>
2785          Note that launch of statically deployed applet will be initiated
2786          before this this function will get control. Hence platform
2787          requirements listed here will NOT be validated prior to launch
2788          and will be used if applet launch can not be initiated otherwise.
2789
2790          @method register
2791          @param id
2792          Identifier of application.
2793          @param platform {Platform}
2794          Optional platform requirements (such as JRE and JavaFX versions).
2795          @param cb {Callbacks | array}
2796          Optional set of callbacks. See Callbacks for details.
2797          */
2798         register: function(id, platform, callbacks) {
2799             return doRegister(id, platform, callbacks);
2800         },
2801
2802
2803         /**
2804          * Hides html splash panel for applet with given id.
2805          * If splash panel does not exist this method has no effect.
2806          * For JavaFX applications this method will be called automatically once application is ready.
2807          * For Swing/AWT applets application code need to call into this method explicitly if they were deployed
2808          * with custom splash handler.
2809          *
2810          * @method hideSplash
2811          * @param id    Identifier of applet whose splash panel need to be hidden
2812          */
2813         hideSplash: function(id) {
2814             return doHideSplash(id);
2815         },
2816
2817         /**
2818          Helper function: cross-browser onLoad support
2819          <p>
2820          This will call fn() once document is loaded.
2821          If page is already loaded when this method is
2822          called then fn() is called immediately.
2823          <p>
2824          If strictMode is true then fn() is called once page
2825          and all its assets are loaded (i.e. when document
2826          ready state will be 'complete').
2827          Otherwise fn() is called after DOM tree is fully created
2828          (but some assets may not yet be loaded).
2829          <p>
2830          It is ok to call this function multiple times. It will append
2831          to existing chain of events (and do not replace them).
2832
2833          @method addOnloadCallback
2834
2835          @param {function} fn
2836          (required) function to call
2837
2838          @param strictMode {boolean} Flag indicating whether page assets need to
2839          be loaded before launch (default is false).
2840          */
2841         addOnloadCallback: function(fn, strictMode) {
2842             //WORKAROUND for RT-21574
2843             // avoid using onDomReady because it leads to deadlocks
2844             if (strictMode || (ua.chrome && !ua.win)) {
2845                 addOnload(fn);
2846             } else {
2847                 addOnDomReady(fn);
2848             }
2849         },
2850
2851         /**
2852          * Add onJavascriptReady and onDeployError callbacks
2853          * to the existing Java applet or JavaFX application.
2854          * Application need to be alive in the browser DOM tree for this to work
2855          *
2856          * @param id {string} applet id
2857          * @param cb {array}  Set of callbacks. If null then pending callbacks are installed (if any for this applet).
2858          * @private
2859          */
2860         installCallbacks: function(id, cb) {
2861             doInstallCallbacks(id, cb);
2862         },
2863
2864         /** Platform requirements for application launch.
2865
2866          <p><br>
2867          The version pattern strings are of the form #[.#[.#[_#]]][+|*],
2868          which includes strings such as "1.6", * "2.0*", and "1.6.0_18+".
2869          <p>
2870
2871          A star (*) means "any version within this family" where family is defined
2872          by prefix and a plus (+) means "any version greater or equal to the specified version".
2873          For example "1.6.0*" matches 1.6.0_25 but not 1.7.0_01,
2874          whereas "1.6.0+" or "1.*" match both.
2875          <p>
2876          If the version pattern does not include all four version components
2877          but does not end with a star or plus, it will be treated as if it
2878          ended with a star.  "2.0" is exactly equivalent to "2.0*", and will
2879          match any version number beginning with "2.0".
2880          <p>
2881          Null version string is treated as "there is no requirement to have it installed".
2882          Validation will pass whether this component is installed or not.
2883          <p>
2884          Both "+" and "*" will match any installed version of component. However if component is not
2885          installed then validation will fail.
2886
2887          @class Platform
2888          @for dtjava
2889          @constructor
2890          @param r {array}
2891          Array describing platform requirements. Element names should match
2892          Platform properties.
2893          */
2894         Platform: function(r) {
2895             //init with defaults
2896
2897             /**
2898              JRE/JVM version.
2899              @property jvm
2900              @type version pattern string
2901              @default "1.6+"
2902              */
2903             this.jvm = "1.6+";
2904             /**
2905              Minimum JavaFX version.
2906              @property javafx
2907              @type version pattern string
2908              @default null
2909              */
2910             this.javafx = null;
2911             /**
2912              Java Plugin version.
2913              If set to null then browser plugin support for embedded content is not validated.
2914              @property plugin
2915              @type version pattern string
2916              @default "*"
2917              */
2918             this.plugin = "*";
2919             /**
2920              List of requested JVM arguments.
2921              @property jvmargs
2922              @type string
2923              @default null
2924              */
2925             this.jvmargs = null;
2926
2927             //copy over
2928             for (var v in r) {
2929                 this[v] = r[v];
2930                 //we expect jvmargs to come as array. if not - convert to array
2931                 if (this["jvmargs"] != null && typeof this.jvmargs == "string") {
2932                     this["jvmargs"] = this["jvmargs"].split(" ");
2933                 }
2934             }
2935
2936            /**
2937              * @method toString
2938              * @return {string}
2939              *    Returns string replesentation of platform spec. Useful for debugging.
2940              */
2941             this.toString = function() {
2942                 return "Platform [jvm=" + this.jvm + ", javafx=" + this.javafx
2943                 + ", plugin=" + this.plugin + ", jvmargs=" + this.jvmargs + "]";
2944             };
2945         },
2946
2947         /**
2948          Application launch descriptor.
2949
2950          @class App
2951          @for dtjava
2952          @constructor
2953          @param url {string}
2954          (Required) location of JNLP file. Could be full URL or partial
2955          relative to document base.
2956          @param details {array}
2957          (Optional) set of values for other object properties.
2958          Name should match documented object properties.
2959          */
2960         App: function(url, details) {
2961             /**
2962              Location of application's JNLP file.  Can not be null or undefined.
2963              @property url
2964              @type string
2965              */
2966             this.url = url;
2967
2968             //default behavior
2969             this.scriptable = true;
2970             this.sharedjvm = true;
2971
2972             if (details != undefined && details != null) {
2973                 /**
2974                  Identifier of this App. Expected to be unique on this page.
2975                  If null then it is autogenerated.
2976                  @property id
2977                  @type string
2978                  */
2979                 this.id = details.id;
2980                 /**
2981                  Base64 encoded content of JNLP file.
2982                  @property jnlp_content
2983                  @type string
2984                  */
2985                 this.jnlp_content = details.jnlp_content;
2986                 /**
2987                  Applet width. Could be absolute or relative (e.g. 50 or 50%)
2988                  @property width
2989                  @type string
2990                  */
2991                 this.width = details.width;
2992                 /**
2993                  Applet height. Could be absolute or relative (e.g. 50 or 50%)
2994                  @property height
2995                  @type int
2996                  */
2997                 this.height = details.height;
2998
2999                 /**
3000                  Set of named parameters to pass to application.
3001                  @property params
3002                  @type array
3003                  */
3004                 this.params = details.params;
3005
3006                 /**
3007                  If set to true then Javascript to Java bridge will be initialized.
3008                  Note that some platform requirements imply Javascript bridge is initialized anyways.
3009                  If set to false the Java to Javascript calls are still possible.
3010
3011                  //TODO: AI: will it affect applet callbacks?
3012
3013                  @property scriptable
3014                  @type boolean
3015                  @default true
3016                  */
3017                 this.scriptable = details.scriptable;
3018
3019                 /**
3020                  True if application does not need JVM instance to be dedicated to this application.
3021                  Some of platform requirements may imply exclusive use of JVM.
3022                  <p>
3023                  Note that even if sharing is enabled java plugin may choose to run applets in different JVM
3024                  instances. There is no way to force java plugin to reuse same JVM.
3025
3026                  @property sharedjvm
3027                  @type boolean
3028                  @default true
3029                  */
3030                 this.sharedjvm = details.sharedjvm;
3031
3032                 /**
3033                  Reference to DOM node to embed application into.
3034                  If not provided by the user and application is embedded then will be allocated dynamically.
3035                  <p>
3036                  Note that element may be not inserted into the DOM tree yet.
3037                  <p>
3038                  User may also provide identifier of the existing DOM node to be used as placeholder.
3039                  @property placeholder
3040                  @type {DOM node | DOM node id}
3041                  @default null
3042                  */
3043                 this.placeholder = details.placeholder;
3044                 
3045                 /**
3046                   Tookit used by the application.
3047                   By default it is "fx" (and null is treated as JavaFX too).
3048                   Swing applications embedding JavaFX components need to pass "swing"
3049                 */
3050                 this.toolkit = details.toolkit;
3051             }
3052
3053             /**
3054              * Returns string representation of this object.
3055              *
3056              * @return {string}
3057              */
3058             this.toString = function() {
3059                 var pstr = "null";
3060                 var first = true;
3061                 if (notNull(this.params)) {
3062                     pstr = "{";
3063                     for (p in this.params) {
3064                         pstr += ((first) ? "" : ", ") + p + " => " + this.params[p];
3065                         first = false;
3066                     }
3067                     pstr += "}";
3068                 }
3069                 return "dtjava.App: [url=" + this.url + ", id=" + this.id + ", dimensions=(" + this.width + "," + this.height + ")"
3070                     + ", toolkit=" + this.toolkit 
3071                     + ", embedded_jnlp=" + (notNull(this.jnlp_content) ? (this.jnlp_content.length + " bytes") : "NO")
3072                     + ", params=" + pstr + "]";
3073             }
3074         },
3075
3076
3077         /**
3078          Set of callbacks to be used to customize user experience.
3079
3080          @class Callbacks
3081          @for dtjava
3082          @constructor
3083          @param cb {list of callbacks}
3084          set of callbacks to set
3085          */
3086         Callbacks: function(cb) {
3087             /**
3088              Callback to be called to obtain content of the splash panel. Gets application
3089              launch descriptor as an input. If null is returned then splash is disabled.
3090              Non-null return value is expected to be html snippet to be added into splash overlay.
3091              Only applicable to embed().
3092              <p>
3093              Note that autohiding splash is not supported by all platforms. Splash will be hidden by default
3094              for JavaFX application but not for Swing/AWT applets. In later case if use of splash is desirable
3095              then app need to call dtjava.hideSplash() explicitly to initiate hiding splash.
3096
3097              @property onGetSplash
3098              @type function(app)
3099              @default Default splash panel for JavaFX applications embedded into web page, null otherwise.
3100              */
3101             this.onGetSplash = defaultGetSplashHandler;
3102
3103             /**
3104              Called if embedding or launching application need
3105              additional components to be installed. This callback is
3106              responsible for handling such situation, e.g. reporting
3107              need to install something to the user,
3108              initiating installation using install() and
3109              hiding splash panel for embedded apps (if needed).
3110              After installation is complete callback implementation may
3111              retry attempt to launch application using provided launch function.
3112              <p>
3113              This method is NOT called if platform requirement could not be met
3114              (e.g. if platfrom is not supported or if installation
3115              is not possible).
3116              <p>Default handler provides "click to install" solution for
3117              embedded application and attempt to perform installation without
3118              additional questions for apps started using launch().
3119              <p>
3120              If handler is null then it is treated as no op handler.
3121              <p>
3122              Parameters:
3123              <ul>
3124              <li> <b>app</b> - application launch descriptor.
3125                  For embedded applications app.placeholder will refer to
3126                  the root of the applet area in the DOM tree (to be used for
3127                  visual feedback)
3128              <li> <b>platform</b> - application platform requirements
3129              <li> <b>cb</b> - set of callbacks to be used during
3130                    installation process
3131              <li> <b>isAutoinstall</b> - true if install can be launched
3132                  automatically
3133              <li> <b>needRestart</b> - true if browser restart will be required
3134                  once installation is complete
3135              <li> <b>launchFunction</b> - function to be executed to
3136                  retry launching the application once installation is finished
3137              </ul>
3138
3139              @property onInstallNeeded
3140              @type function(app, platform, cb, isAutoinstall, needRelaunch, launchFunc)
3141              @default Default implementation shows "click to install" banner
3142                for embedded applications or initiates installation immediately
3143                for applications launched from web page.
3144              */
3145             this.onInstallNeeded = defaultInstallHandler;
3146
3147             /**
3148              Called before installation of required component is triggered.
3149              For manual install scenario it is called before installation
3150              page is opened.
3151              <p>
3152              This method can be used to provide visual feedback to the user
3153              during the installation. Placeholder
3154              points to the area that can be used for visualization,
3155              for embedded applications it will be applet area.
3156              If null then callee need to find place for visualization on its own.
3157              <p>
3158              In case of automatic launch of installation onInstallFinished will be called
3159              once installation is complete (succesfully or not).
3160              <p>
3161              If handler is null then it is treated as no-op handler.
3162
3163              Parameters:
3164              <ul>
3165              <li> <b>placeholder</b> - DOM element to insert visual feedback into.
3166                   If null then callee need to add visual feedback to the document on its own
3167                   (e.g. placeholder will be null if installation is not happening in context of embedding application into
3168                   web page).
3169              <li> <b>component</b> - String "Java", "JavaFX" or "Java bundle"
3170              <li> <b>isAutoInstall</b> - true if installer will be launched
3171                   automatically
3172              <li> <b>restartNeeded</b> - boolean to specify whether browser restart will be required
3173              </ul>
3174
3175              @property onInstallStarted
3176              @type function(placeholder, component, isAuto, restartNeeded)
3177              @default No-op
3178              */
3179             this.onInstallStarted = defaultInstallStartedHandler;
3180
3181             /**
3182              Called once installation of required component
3183              is completed. This method will NOT be called if installation is
3184              performed in manual mode.
3185
3186              Parameters:
3187              <ul>
3188              <li> <b>placeholder</b> - DOM element that was passed to
3189                  onInstallStarted to insert visual feedback into.
3190              <li> <b>component</b> - String "jre" or "javafx"
3191              <li> <b>status</b> - status code is string categorizing the status of install.
3192              ("success", "error:generic", "error:download" or "error:canceled")
3193              <li> <b>relaunchNeeded</b> - boolean to specify
3194              whether browser restart is required to complete the installation
3195              </ul>
3196
3197              @property onInstallFinished
3198              @type function(placeholder, component, status, relaunchNeeded)
3199              @default no op
3200              */
3201             this.onInstallFinished = defaultInstallFinishedHandler;
3202
3203             /**
3204              This function is called if application can not be deployed because
3205              current platform does not match given platform requirements.
3206              It is also called if request to install missing components can not be
3207              completed due to platform.
3208              <p>
3209              Problem can be fatal error or transient issue (e.g. relaunch needed). Further
3210              details can be extracted from provided mismatchEvent. Here are some typical combinations:
3211
3212              <ul>
3213              <li><em>Current browser is not supported by Java</em> - (r.isUnsupportedBrowser())
3214              <li><em>Browser need to be restarted before application can be launched</em> - (r.isRelaunchNeeded())
3215              <li>JRE specific codes
3216              <ul>
3217              <li><em>JRE is not supported on this platform</em> - (r.jreStatus() == "unsupported")
3218              <li><em>JRE is not detected and need to be installed</em> - (r.jreStatus() == "none")
3219              <li><em>Installed version of JRE does not match requirements</em> - (r.jreStatus() == "old")
3220              <li><em>Matching JRE is detected but deprecated Java plugin is used and
3221                      it does not support JNLP applets</em> - (r.jreStatus() == "oldplugin")
3222              </ul>
3223              <li> JavaFX specific codes
3224              <ul>
3225              <li><em>JavaFX is not supported on this platform</em> - (r.javafxStatus() == "unsupported")
3226              <li><em>JavaFX Runtime is missing and need to be installed manually</em> - (r.javafxStatus() == "none")
3227              <li><em>Installed version of JavaFX Runtime does not match requirements</em> - (r.javafxStatus() == "old")
3228              <li><em>JavaFX Runtime is installed but currently disabled</em> - (r.javafxStatus() == "disabled")
3229              </ul>
3230              </ul>
3231
3232              Default error handler handles both application launch errors and embedded content.
3233
3234              @property onDeployError
3235              @type function(app, mismatchEvent)
3236              */
3237             this.onDeployError = defaultDeployErrorHandler;
3238
3239             /**
3240              * Called to get content to be shown in the applet area if Java plugin is not installed
3241              * and none of callbacks helped to resolve this.
3242              *
3243              * @property onGetNoPluginMessage
3244              * @type function(app)
3245              * @return DOM Element object representing content to be shown in the applet area if
3246              *         java plugin is not detected by browser.
3247              */
3248             this.onGetNoPluginMessage = defaultGetNoPluginMessageHandler;
3249
3250             /**
3251              Called once applet is ready to accept Javascript calls.
3252              Only supported for plugin version 10.0.0 or later
3253              @property onJavascriptReady
3254              @type function(id)
3255              @default null
3256              */
3257             this.onJavascriptReady = null;
3258
3259             /**
3260              Called if application failed to launch.
3261              Only supported for plugin version 10.0.0 or later.
3262
3263              @property onRuntimeError
3264              @type function(id)
3265              @default no op
3266              */
3267             this.onRuntimeError = defaultRuntimeErrorHandler;
3268
3269             //overwrite with provided parameters
3270             for (c in cb) {
3271                 this[c] = cb[c];
3272             }
3273         }
3274     };
3275 }();