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.
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.
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).
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.
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
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.
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.
37 @module java/deployment_toolkit
39 var dtjava = function() {
41 return (o != undefined && o != null);
45 return (fn != null && typeof fn != "undefined");
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) {
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);
67 //set to true to disable FX auto install (before release)
68 var noFXAutoInstall = false;
70 // JRE version we start to have JRE and FX true co-bundle
71 var minJRECobundleVersion = "1.7.0_06";
77 var cbDone = false; //done with onload callbacks
78 var domCb = []; //list of callbacks
81 //add function to be called on DOM ready event
82 function addOnDomReady(fn) {
86 domCb[domCb.length] = fn;
90 //invoke pending onload callbacks
91 function invokeCallbacks() {
93 //swfoject.js tests whether DOM is actually ready first
94 // in order to not fire too early. Use same heuristic
96 var t = d.getElementsByTagName("body")[0].appendChild(
97 d.createElement("div"));
98 t.parentNode.removeChild(t);
103 for (var i = 0; i < domCb.length; i++) {
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() {
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();
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),
151 //Used to be using trick from
152 // http://webreflection.blogspot.com/2009/01/32-bytes-to-know-if-your-browser-is-ie.html
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);
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
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
174 if ((p && /intel/.test(p)) || /intel/.test(u)) {
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;
183 // Check mime types. Works with netscape family browsers and checks latest installed plugin only
184 var mm = navigator.mimeTypes;
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);
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);
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);
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};
212 //partially derived from swfobject.js
213 var initDone = false;
216 if (initDone) return;
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))) {
232 if (isDef(d.addEventListener)) {
233 d.addEventListener("DOMContentLoaded",
234 invokeCallbacks, false);
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);
244 if (w == top) { // if not inside an iframe
251 d.documentElement.doScroll("left");
253 setTimeout(arguments.callee, 0);
265 if (!/loaded|complete/.test(d.readyState)) {
266 setTimeout(arguments.callee, 0);
272 addOnload(invokeCallbacks);
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+?)
278 installNativePlugin();
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.
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.
293 @class PlatformMismatchEvent
296 function PlatformMismatchEvent(a) {
298 //expect to get all parameters needed
306 * Returns string replesentation of event. Useful for debugging.
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 + "]";
316 @method isUnsupportedPlatform
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.)
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.
327 this.isUnsupportedPlatform = function() {
332 @method isUnsupportedBrowser
334 Returns true if error is because current browser is not supported.
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)
340 this.isUnsupportedBrowser = function() {
348 Returns "ok" if error was not due to missing JRE.
349 Otherwise return error code characterizing the problem:
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
357 canAutoInstall() and isRelaunchNeeded() can be used to
358 get more details on how seamless user' install experience will be.
360 this.jreStatus = function() {
365 * @method jreInstallerURL
366 * @param {string} locale (optional) Locale to be used for installation web page
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.
373 this.jreInstallerURL = function(locale) {
374 if (this.os && (this.jre == "old" || this.jre == "none")) {
375 return getJreUrl(locale);
384 Returns "ok" if error was not due to missing JavaFX.
385 Otherwise return error code characterizing the problem:
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
393 canAutoInstall() and isRelaunchNeeded() can be used to
394 get more details on how seamless user' install experience will be.
396 this.javafxStatus = function() {
401 * @method javafxInstallerURL
402 * @param {string} locale (optional) Locale to be used for installation web page
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.
409 this.javafxInstallerURL = function(locale) {
410 if (!this.os && (this.fx == "old" || this.fx == "none")) {
411 return getFxUrl(locale);
417 @method canAutoInstall
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.
423 If any of missing components need to be installed manually
424 (i.e. click through additional web pages) then false is returned.
426 this.canAutoInstall = function() {
427 return isAutoInstallEnabled(this.platform, this.jre, this.fx);
431 @method isRelaunchNeeded
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.
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.
441 this.isRelaunchNeeded = function() {
442 return this.relaunch;
446 //returns version of instaled JavaFX runtime matching requested version
448 function getInstalledFXVersion(requestedVersion) {
449 //NPAPI browser and JRE with cobundle
450 if (ua.fx != null && versionCheckFX(requestedVersion, ua.fx)) {
457 return p.getInstalledFXVersion(requestedVersion);
463 //concatenate list with space as separator
464 function listToString(lst) {
466 return lst.join(" ");
472 function addArgToList(lst, arg) {
482 function doLaunch(ld, platform, cb) {
483 var app = normalizeApp(ld, true);
485 //required argument is missing
486 if (!(notNull(app) && notNull(app.url))) {
487 throw "Required attribute missing! (application url need to be specified)";
490 //if we got array we need to copy over!
491 platform = new dtjava.Platform(platform);
494 cb = new dtjava.Callbacks(cb);
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");
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)) {
525 //Did not launch yet? Try DT plugin (7u2+)
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;
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)
548 for (var k in app.params) {
549 ptmp[k] = String(app.params[k]);
551 callArgs["params"] = ptmp;
553 if (notNull(app.jnlp_content)) {
554 callArgs["jnlp_content"] = app.jnlp_content;
556 var err = p.launchApp(callArgs);
557 if (err == 0) { //0 - error
558 if (isDef(cb.onRuntimeError)) {
559 cb.onRuntimeError(app.id);
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);
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);
582 } //old Java (pre DTLite)? not Windows? or old DT
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);
589 //should never happen
590 d.write(o.innerHTML);
594 var r = doValidateRelaxed(platform);
595 //can not launch, try to fix
597 resolveAndLaunch(app, platform, r, cb, launchFunc);
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);
610 function isDTInitialized(p) {
611 //if plugin is blocked then p.version will be undefined
612 return p != null && isDef(p.version);
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
630 // Problem we are solving:
631 // when plugin is ready to serve request? How do we schedule call to happen when plugin is initialized?
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
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.
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.
648 if (isDTInitialized(p)) {
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]();
663 if (dtjava.dtPendingCnt > 0) {
664 dtjava.dtPendingCnt--;
665 setTimeout(waitAndUse, 500);
669 //add new task in queue
670 if (!notNull(dtjava.dtPending) || dtjava.dtPendingCnt == 0) {
671 dtjava.dtPending = {};
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();
681 //returns same mismatch event if not resolved, null if resolved
682 function resolveAndLaunch(app, platform, v, cb, launchFunction) {
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
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.";
696 doShowMessageInTheArea(app, msg1, msg2, altText, "javafx-chrome.png", onClickFunc);
697 actionLabel = app.id + "-embed";
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.
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
715 resolveAndLaunch(app, platform, vnew, cb, launchFunction);
718 runUsingDT(actionLabel, retryFunc);
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
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);
736 //TODO: may be should call itself again but
737 // then it easy can become infinite loop
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
745 cb.onInstallNeeded(app, platform, cb,
746 v.canAutoInstall(), v.isRelaunchNeeded(), resolveFunc);
750 reportPlatformError(app, v, cb);
753 function haveDTLite() {
754 if (ua.deploy != null) {
755 return versionCheck("10.6+", ua.deploy);
760 function isDTLiteInitialized(p) {
761 //if plugin is blocked then p.version will be undefined
762 return p != null && isDef(p.version);
765 function getDTLitePlugin() {
766 return document.getElementById("dtlite");
769 function doInjectDTLite() {
770 //do not want more than one plugin
771 if (getDTLitePlugin() != null) return;
773 var p = document.createElement('embed');
777 p.type = "application/x-java-applet"; //means we get latest
779 var div = document.createElement("div");
780 div.style.position = "relative";
781 div.style.left = "-10000px";
784 var e = document.getElementsByTagName("body");
785 e[0].appendChild(div);
788 function runUsingDTLite(f) {
789 // Possible situations:
790 // a) first request, plugin is not in the DOM tree yet
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
804 // when plugin is ready to serve request? How do we schedule call to happen when plugin is initialized?
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
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.
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();
819 p = getDTLitePlugin();
822 if (isDTLiteInitialized(p)) {
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);
833 dtjava.pendingLaunch = null;
836 if (dtjava.dtlitePendingCnt > 0) {
837 dtjava.dtlitePendingCnt--;
838 setTimeout(waitAndUse, 500);
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) {
853 function doLaunchUsingDTLite(app, jvmargs, cb) {
854 var launchIt = function() {
855 var pp = getDTLitePlugin();
857 //should not be possible as we guard before enter this function
858 if (isDef(cb.onRuntimeError)) {
859 cb.onRuntimeError(app.id);
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;
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)
880 for (var k in app.params) {
881 ptmp[k] = String(app.params[k]);
883 callArgs["params"] = ptmp;
885 if (notNull(app.jnlp_content)) {
886 callArgs["jnlp_content"] = app.jnlp_content;
888 var err = pp.launchApp(callArgs);
889 if (err == 0) { //0 - error
890 if (isDef(cb.onRuntimeError)) {
891 cb.onRuntimeError(app.id);
896 if (versionCheck("10.4+", ua.deploy)) { //only for NPAPI browsers
897 runUsingDTLite(launchIt);
903 function getWebstartObject(jnlp) {
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?
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';
915 p = d.createElement('param');
917 p.value = notNull(d.documentURI) ? d.documentURI : d.URL;
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";
924 wo.classid = "clsid:8AD9C840-044E-11D1-B3E9-00805F499D93";
926 } else { //TODO: else part should go away once we figure out what is going on with FF
927 wo = d.createElement('embed');
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";
937 var div = d.createElement("div");
938 div.style.position = "relative";
939 div.style.left = "-10000px";
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
949 // 2.0.1 => family 2, update 0.1
950 // 2.0 => family 2. update 0.0
952 // JavaFX version requirements are always treated as "not earlier than this update".
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) {
960 var endChar = query.charAt(query.length - 1);
961 var familyOnly = (endChar == '*');
963 if (endChar == '+') {
964 return versionCheck(query, version);
965 } else { //must be fixed version, e.g. 2.0
966 return versionCheck(query + '+', version);
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 +
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
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);
992 if (versionString == null || versionString.length == 0) {
996 var arr = versionString.split(".");
997 while (arr.length < 4) {
1003 //checks where given version string matches query
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;
1009 var c = query.charAt(query.length - 1);
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 + "*";
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);
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"
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);
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]) {
1045 } else if (qArr[idx] < vArr[idx]) {
1046 //query is larger => fail
1050 //query is equal to version => it is ok
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
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();
1069 //WORKAROUND: bug in native DT!!! TODO: What version? bypass for it only
1070 //return (p.jvms.getLength() > 0);
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
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)) {
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
1091 //try to use DT plugin
1092 var p = getPlugin();
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 =>
1100 if (!notNull(navigator.mimeTypes["application/x-java-applet"])) {
1107 //do not need to try other ways if used DT
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
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
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])) {
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.
1139 function checkJRESupport() {
1140 //Negative test. New platforms will not be rejected
1141 var osProblem = ['iPhone', 'iPod'];
1142 var os = containsAny(osProblem, navigator.userAgent);
1144 //Do not support Chrome/Mac as Chrome is 32 bit only
1145 var browser = (ua.mac && ua.chrome && ua.cputype == "intel");
1147 //autoinstall possible if native plugin is detected or OS is fine
1148 auto = os || (getPlugin() != null);
1150 //false is no problem found
1151 return {os: os, browser: browser, auto: auto};
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() {
1160 //these functions are defined in IE only
1161 var v = 10*ScriptEngineMajorVersion() + ScriptEngineMinorVersion();
1162 if (v < 57) return true; //IE7 will have 57
1171 function checkFXSupport() {
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();
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!
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?
1187 //false is no problem found
1188 return {os: os, browser: browser};
1189 } else if (ua.linux) {
1190 browser = ua.op; //Opera unsupported
1192 //false is no problem found
1193 return {os: false, browser: browser};
1195 //unknown unsupported OS
1196 return {os: true, browser: false};
1200 function relaxVersion(v) {
1201 if (notNull(v) && v.length > 0) {
1202 var c = v.charAt(v.length - 1);
1204 v = v.substring(0, v.length - 1)+"+";
1205 } else if (c != '+') { //exact version (e.g. 1.6)
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
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);
1222 p.jvm = relaxVersion(p.jvm);
1223 //p.javafx = relaxVersion(p.javafx);
1225 return doValidate(p);
1228 function doValidate(platform) {
1229 //ensure some platform is set (we could get array too!)
1230 platform = new dtjava.Platform(platform);
1233 var fx = "ok", jre = "ok", restart = false, os = false, browser = false,
1237 if (notNull(platform.jvm) && jreCheck(platform.jvm) != "ok") { //matching JRE not found
1238 var res = jreCheck("*");
1242 jre = res; //"none" or "disabled"
1245 details = checkJRESupport();
1247 jre = "unsupported";
1250 browser = details.browser;
1252 /* if (notNull(platform.plugin) && jre == "ok") {
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?
1264 } catch (err) { //pre 6u10 or no DT
1271 if (notNull(platform.javafx)) {
1272 details = checkFXSupport();
1273 if (details.os || details.browser) { //FX is not supported,
1276 os = os || details.os;
1277 browser = browser || details.browser;
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)
1282 if (ua.fx != null) {
1283 //found cobundled JavaFX on 7u6+ (and it is NPAPI-based browser)
1284 if (versionCheckFX(platform.javafx, ua.fx)) {
1286 } else if (versionCheckFX("2.0+", ua.fx)) {
1289 } else if (ua.win) { //could be 7u6(cobundle)/IE or JRE6/FX
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 == "") {
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)
1309 } else if (ua.mac || ua.linux) {
1315 //recommend relaunch if OS is ok but browser is not supported
1316 restart = restart || (!os && browser);
1318 //TODO: need a way to find out if java plugin is loaded => will need to relaunch
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});
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});
1337 //TODO: does it make sense to have a way to explicitly request locale?
1338 function guessLocale() {
1341 loc = navigator.userLanguage;
1343 loc = navigator.systemLanguage;
1345 loc = navigator.language;
1348 loc = loc.replace("-", "_")
1353 function getJreUrl(loc) {
1354 if (!notNull(loc)) {
1355 loc = guessLocale();
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
1364 function getFxUrl(locale) {
1365 return "http://www.oracle.com/technetwork/java/javafx/downloads/index.html";
1368 //return true if mismatch event suggest to perform installation
1369 function isMissingComponent(v) {
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");
1380 function showClickToInstall(ld, isJRE, isUpgrade, isAutoinstall, isRelaunchNeeded, actionFunc) {
1382 var productName, productLabel;
1384 productName = "Java";
1385 productLabel = "java";
1387 productName = "JavaFX";
1388 productLabel = "javafx";
1391 var msg1, msg2, imgName;
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";
1397 msg1 = "View the content on this page.";
1398 msg2 = "Please click here to install " + productName;
1399 imgName = "get_"+productLabel+".png";
1401 var altText = "Click to install "+productName;
1403 doShowMessageInTheArea(ld, msg1, msg2, altText, imgName, actionFunc);
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);
1412 var lnk = d.createElement("a");
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
1418 d.createElement("p").appendChild(
1419 d.createTextNode(msg1)));
1420 lnk.appendChild(d.createTextNode(msg2));
1423 var img = d.createElement("img");
1424 img.src = jscodebase + imgName;
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);
1436 wipe(ld.placeholder);
1437 ld.placeholder.appendChild(r);
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")) {
1449 function defaultInstallHandler(app, platform, cb,
1450 isAutoinstall, needRelaunch, launchFunc) {
1451 var installFunc = function() {
1452 doInstall(app, platform, cb, launchFunc);
1455 var s = doValidate(platform);
1456 if (!notNull(s)) { //platform match => nothing to install
1457 if (notNull(launchFunc)) {
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);
1468 showClickToInstall(app, (s.jreStatus() != "ok"), isUpgrade, isAutoinstall, needRelaunch, installFunc);
1471 var r = isAutoinstall;
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
1477 msg = "A newer version of Java is required to view the content on this page. Please click here to update Java.";
1479 msg = "To view the content on this page, please click here to install Java.";
1484 msg = "A newer version of JavaFX is required to view the content on this page. Please click here to update JavaFX.";
1486 msg = "To view the content on this page, please click here to install JavaFX.";
1497 * returns true if we can enable DT plugin auto-install without chance of
1498 * deadlock on cert mismatch dialog
1500 * requestedJREVersion param is optional - if null, it will be
1501 * treated as installing any JRE version
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.
1507 function enableWithoutCertMisMatchWorkaround(requestedJREVersion) {
1509 // Non-IE browser are okay
1510 if (!ua.ie) return true;
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)) {
1519 // If we got there, DT plugin is 6uX
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
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);
1535 // return true if we can auto-install to satisfy the platform requirement
1536 // return false otherwise
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
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;
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;
1557 if (!enableWithoutCertMisMatchWorkaround(platform.jvm)) {
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)) {
1570 // we are going to install co-bundle JRE - check if we can do
1572 if (!enableWithoutCertMisMatchWorkaround(minJRECobundleVersion)) {
1580 function doInstall(app, platform, cb, postInstallFunc) {
1581 var s = doValidate(platform);
1583 cb = new dtjava.Callbacks(cb);
1585 if (notNull(s) && s.isUnsupportedPlatform()) {
1586 reportPlatformError(app, s, cb);
1587 return false; //no install
1590 var placeholder = (app != null) ? app.placeholder : null;
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
1611 codes = ["success", "ignore", "error:download", "error:generic",
1612 "error:generic", "error:generic", "error:generic", "error:cancelled"];
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";
1621 status = "error:generic";
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";
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";
1633 if (isDef(cb.onInstallFinished)) {
1634 cb.onInstallFinished(placeholder, "javafx",
1635 status, s.isRelaunchNeeded());
1637 if (cd == 0) { //proceed on success only
1638 if (notNull(postInstallFunc)) {
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
1650 // NOte - for launchable apps it is different!
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
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);
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
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) {
1693 status = "error:generic"; //just in case
1695 if (isDef(cb.onInstallFinished)) {
1696 cb.onInstallFinished(placeholder, "jre",
1697 status, s.isRelaunchNeeded());
1699 //may also need to launch FX install but only on success!
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);
1706 //nothing more to install => call postInstallFunction
1707 if (postInstallFunc != null) {
1713 if (isDef(cb.onInstallStarted)) {
1714 cb.onInstallStarted(placeholder, "Java",
1721 // pass in javafx requirement and see if we can
1722 // install a co-bundle that satisfy the application
1724 ret = p.installJRE(platform.jvm, platform.javafx,
1726 } catch (ee) { //in IE it will throw exception,
1727 //in FF/Chrome will return 0
1733 var jvm_req = platform.jvm;
1735 if (s.fx != "ok" && canJavaFXCoBundleSatisfy(platform)) {
1736 // We would like to avoid standalone JavaFX
1737 // Runtime install if possible (unless app
1740 // Starting in 7u6 - JavaFX runtime is co-bundle
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
1750 // override java and javafx version requirement to latest
1752 jvm_req = minJRECobundleVersion;
1753 if (platform.jvm.indexOf('*') != -1) {
1755 } else if (platform.jvm.indexOf('+') != -1) {
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
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)
1774 ret = p.installJRE(jvm_req);
1776 ret = 0; //just in case we got exception
1778 setTimeout(function() {
1779 setTimeout(function() {handleResultJRE(ret)}, 0);
1784 } else if (!noFXAutoInstall && s.fx != "ok") {
1785 setTimeout(installFX, 0);
1788 //auto install not possible => can only do manual install
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);
1796 startManualJREInstall();
1797 } else if (s.fx != "ok") {
1798 if (isDef(cb.onInstallStarted)) {
1799 cb.onInstallStarted(placeholder, "JavaFX",
1800 false, getPlugin() != null);
1802 startManualFXInstall();
1803 } else { //what it could be??
1804 reportPlatformError(app, s, cb);
1808 //nothing to install
1809 if (postInstallFunc != null) {
1814 //no install initiated
1818 //just open download URL in new window
1819 function startManualJREInstall() {
1820 w.open(getJreUrl());
1823 //just open download URL in new window
1824 function startManualFXInstall() {
1828 function defaultGetSplashHandler(ld) {
1829 if (ld.placeholder != null) {
1830 var _w = ld.width, _h = ld.height;
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;
1840 var img = d.createElement("img");
1841 img.src = jscodebase + iU;
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);
1852 //webstart or install case
1853 //TODO: show some html splash for webstart? how to hide it?
1858 function defaultGetNoPluginMessageHandler(app) {
1859 if (app.placeholder != null) {
1860 var p = d.createElement("p");
1861 p.appendChild(d.createTextNode("FIXME - add real message!"));
1863 } //no op if not embedded content
1867 //remove all child elements for given node
1869 while(c.hasChildNodes()) c.removeChild(c.firstChild);
1872 function defaultInstallStartedHandler(placeholder, component, isAuto, restartNeeded) {
1873 if (placeholder != null) {
1876 code = (component == "JavaFX") ?
1877 "install:inprogress:javafx": "install:inprogress:jre";
1879 code = (component == "JavaFX") ?
1880 "install:inprogress:javafx:manual" : "install:inprogress:jre:manual";
1883 appletInfoMsg(code);
1887 function defaultInstallFinishedHandler(placeholder, component, status, relaunch) {
1889 if (status != "success") {
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";
1896 msg = "install:fx:"+status;
1898 } else { //must be JRE error
1899 msg = "install:jre:"+status;
1901 if (placeholder != null) {
1902 t = appletErrorMsg(msg, null);
1904 //Instead of hiding splash and applet we simply clear the container
1905 //We are not going to show neither splash nor applet anyways ...
1907 placeholder.appendChild(t);
1909 w.alert(webstartErrorMsg(msg));
1913 t = appletInfoMsg("install:fx:restart");
1915 //Instead of hiding splash and applet we simply clear the container
1916 //We are not going to show neither splash nor applet anyways ...
1918 placeholder.appendChild(t);
1923 function defaultDeployErrorHandler(app, r) {
1926 } else if (r.isUnsupportedBrowser()) {
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()) {
1935 code = "unknown " + r.toString();
1938 if (app.placeholder != null) {//embedded app
1939 showAppletError(app.id, code, null);
1940 } else { //webstart or install case
1941 w.alert(webstartErrorMsg(code));
1945 function defaultRuntimeErrorHandler(id) {
1946 var el_applet = findAppletDiv(id);
1948 if (getErrorDiv(id) != null) {
1949 showAppletError(id, "launch:fx:generic:embedded",
1950 function() {showHideApplet(findAppletDiv(id), false); return false;});
1952 w.alert(webstartErrorMsg("launch:fx:generic"));
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');
1964 function installNativePlugin() {
1965 //already installed?
1966 if (getPlugin() != null) return;
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();
1979 p = d.createElement('object');
1980 //TODO: zero size does not work?? How we can make it less intrusive for layout?
1983 //new CLSID, one with 0000-0000 had been kill bit
1984 p.classid = 'clsid:CAFEEFAC-DEC7-0000-0001-ABCDEFFEDCBA';
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';
1993 for (var i = 0; i < navigator.mimeTypes.length; i++) {
1994 var mt = navigator.mimeTypes[i];
1995 newDT = newDT || ((mt.type == mimeType) && mt.enabledPlugin);
1998 p = d.createElement('embed');
1999 p.setAttribute('type', newDT ? mimeType : oldMimeType);
2000 p.setAttribute('hidden', 'true');
2005 p.setAttribute('id', 'dtjavaPlugin');
2006 d.body.appendChild(p);
2010 var appletCounter = 0;
2012 function prepareAppletID(ld) {
2013 if (notNull(ld.id)) {
2017 return ("dtjava-app-" + appletCounter);
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";
2032 var r = d.createElement("applet"); //TODO: use object!
2034 r.code = "dummy.class";
2036 r.width = normalizeDimension(ld.width);
2037 r.height = normalizeDimension(ld.height);
2039 //things added unconditionally
2040 var sparams = {"jnlp_href" : ld.url,
2041 "java_status_events" : true,
2042 "type" : "application/x-java-applet"};
2044 if (notNull(ld.jnlp_content)) {
2045 sparams['jnlp_embedded'] = ld.jnlp_content;
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);
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;
2059 if (ld.scriptable) {
2060 sparams["scriptable"] = true;
2063 sparams["separate_jvm"] = true;
2066 if (notNull(platform.jvmargs)) {
2067 sparams["java_arguments"] = platform.jvmargs;
2070 //prepare parameters first
2072 for (key in ld.params) {
2073 //do not let to override system parameters
2074 if (!notNull(sparams[key])) {
2075 p = d.createElement("param");
2077 p.value = ld.params[key];
2081 for (key in sparams) {
2082 p = d.createElement("param");
2084 p.value = sparams[key];
2088 if (isDef(cb.onGetNoPluginMessage)) {
2089 p = d.createElement("noapplet");
2090 var t = cb.onGetNoPluginMessage(ld);
2092 //TODO: FIXME: following line fails for me in IE7??
2096 wrapper.appendChild(r);
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);
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;
2117 div.style.left = -10000;
2119 div.style.left = "0px";
2123 function showHideDiv(div, hide) {
2124 if (!notNull(div)) return;
2126 div.style.visibility = "hidden";
2128 div.style.visibility = "visible";
2132 function doHideSplash(id) {
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)
2141 var el = findAppletDiv(id);
2142 showHideApplet(el, false);
2144 //show applet first and then hide splash to avoid blinking
2145 showHideDiv(d.getElementById(id + "-splash"), true);
2149 var javafxURL = "http://java.com/javafx";
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."]
2224 //assume we get list of (tag, param, text) where both param and tag are optional
2226 // ("a", href value, link text)
2229 // (text) //text can not be the same as any of tag names
2230 function msgAsDOM(lst, extra, onClickFunc) {
2232 var root = d.createElement("p");
2234 if (extra != null) {
2235 root.appendChild(extra);
2238 while (i < lst.length) {
2241 el = d.createElement(lst[i]);
2242 el.href = lst[i + 1];
2243 el.appendChild(d.createTextNode(lst[i + 2]));
2247 el = d.createElement(lst[i]);
2250 el = d.createElement(lst[i]);
2251 el.appendChild(d.createTextNode(lst[i + 1]));
2255 el = d.createElement("a");
2257 if (onClickFunc == null) {
2258 onClickFunc = function() {return false;}
2260 el.onclick = onClickFunc;
2261 el.appendChild(d.createTextNode(lst[i + 1]));
2265 el = d.createTextNode(lst[i]);
2268 root.appendChild(el);
2274 function webstartErrorMsg(code) {
2276 var lst = errorMessages[code];
2279 while (i < lst.length) {
2280 if (lst[i] != 'a' && lst[i] != 'br' && lst[i] != 'b') {
2282 } else if (lst[i] == 'a') { //next element is link => skip it
2288 m = "Unknown error: ["+code+"]";
2293 function getErrorDiv(id) {
2294 return d.getElementById(id + "-error");
2297 function showAppletError(id, code, onclickFunc) {
2298 var pane = getErrorDiv(id);
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
2304 //remove old content in the ERROR PANE only (if any)
2307 //populate and show pane
2308 pane.appendChild(appletErrorMsg(code, onclickFunc));
2309 pane.style.visibility = "visible";
2311 //hide splash and applet
2312 showHideDiv(d.getElementById(id+"-splash"), true);
2313 showHideApplet(findAppletDiv(id), true);
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';
2322 img.height = '16px';
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";
2329 var m = errorMessages[code];
2330 //error message is missing => show code as fallback
2335 var hideFunc = null;
2337 if (isDef(onclickFunc)) {
2338 hideFunc = function() {
2339 if (notNull(out.parentNode)) {
2340 out.parentNode.removeChild(out);
2349 out.appendChild(msgAsDOM(m, img, hideFunc));
2353 //returns DOM subtree
2354 function appletInfoMsg(code) {
2355 var out = d.createElement("div");
2357 var m = errorMessages[code];
2358 //error message is missing => show code as fallback
2363 out.appendChild(msgAsDOM(m, null, null));
2367 function normalizeApp(ld, acceptString) {
2369 //normalize launch descriptor
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) {
2377 app = new dtjava.App(ld.url, ld);
2383 function setupAppletCallbacks(platform, callbacks) {
2384 //set default callbacks
2385 var cb = new dtjava.Callbacks(callbacks);
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;
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
2399 //This method will add unit type to numeric dimension specifications. E.g.
2403 function normalizeDimension(v) {
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");
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";
2433 var pendingCallbacks = {};
2435 function doInstallCallbacks(id, cb) {
2437 cb = pendingCallbacks[id];
2439 pendingCallbacks[id] = null;
2444 var a = document.getElementById(id);
2445 if (!notNull(a)) return;
2447 if (isDef(cb.onJavascriptReady)) {
2448 var onReady = cb.onJavascriptReady;
2449 if (a.status < 2) { //not READY yet
2450 a.onLoad = function() {
2452 a.onLoad = null; //workaround bug in plugin for IE in JRE7
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; */
2471 } else if (a.status == 3) { //already failed, call handler in place
2472 cb.onRuntimeError(id);
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.
2482 //Therefore what we do is we insert <script> element AFTER applet tag
2483 //to initiate install after applet tag is parsed
2485 //However, we can not
2487 function getSnippetToInstallCallbacks(id, cb) {
2488 if (!notNull(cb) || !(isDef(cb.onDeployError) || isDef(cb.onJavascriptReady))) {
2492 var s = d.createElement("script");
2493 pendingCallbacks[id] = cb;
2494 s.text = "dtjava.installCallbacks('"+id+"')";
2498 function getErrorPaneSnippet(app) {
2499 var paneDiv = wrapInDiv(app, null, "error");
2500 paneDiv.style.visibility = "hidden";
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)";
2513 app.id = prepareAppletID(app);
2515 //if placeholder is passed as id => find DOM node
2516 if ((typeof app.placeholder == "string")) {
2517 var p = d.getElementById(app.placeholder);
2519 throw "Application placeholder [id="+app.placeholder+"] not found.";
2521 app.placeholder = p;
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));
2528 //if we got array we need to copy over!
2529 platform = new dtjava.Platform(platform);
2531 var cb = setupAppletCallbacks(platform, callbacks);
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);
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);
2551 wipe(app.placeholder);
2552 app.placeholder.appendChild(getErrorPaneSnippet(app));
2553 app.placeholder.appendChild(ss);
2554 app.placeholder.appendChild(appSnippet);
2556 wipe(app.placeholder);
2557 app.placeholder.appendChild(getErrorPaneSnippet(app));
2558 app.placeholder.appendChild(appSnippet);
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);
2565 setTimeout(function() {doInstallCallbacks(app.id, cb)}, 0);
2568 //can not launch yet
2570 resolveAndLaunch(app, platform, v, cb, launchFunction);
2576 function extractApp(e) {
2578 var w = e.width; //TODO: do we need to extract number? e.g. if it was 400px? or 100%?
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, {
2586 placeholder: e.parentNode
2589 throw "Can not find applet with null id";
2593 function processStaticObject(id, platform, callbacks) {
2594 var a = d.getElementById(id); //TODO: use findAppletDiv??
2595 var app = extractApp(a);
2597 var cb = setupAppletCallbacks(platform, callbacks);
2598 //Ensure some platform is set
2599 platform = new dtjava.Platform(platform);
2601 var launchFunc = function() {
2603 app.placeholder.insertBefore(getErrorPaneSnippet(app), a);
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");
2611 app.placeholder.style.position = "relative";
2612 app.placeholder.insertBefore(ss, a);
2613 showHideApplet(a, true);
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 ...
2625 var v = doValidateRelaxed(platform);
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
2636 resolveAndLaunch(app, platform, v, cb, launchFunc);
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);
2649 //perform basic (lightweight) initialization
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.
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.
2667 Version of Javascript part of Deployment Toolkit.
2668 Increasing lexicographically.
2673 version: "20120720",
2676 Validate that platform requirements are met.
2678 @param platform {Platform}
2679 (Optional) set of platform requirements.
2682 Default settings are
2684 <li>platform.jvm : "1.6+"
2685 <li>platform.javafx : null
2686 <li>platform.plugin : "*"
2689 @return {PlatformMismatchEvent}
2690 Returns null if all requirements are met.
2691 Return PlatformMismatchEvent describing the problem otherwise.
2693 validate: function(platform) {
2694 return doValidate(platform);
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.
2703 @param platform {Platform}
2704 Description of platform requirements.
2705 @param callbacks {Callbacks}
2706 Optional set of callbacks to customize install experience.
2708 Returns true if install was initiated.
2711 install: function(platform, callbacks) {
2712 return doInstall(null, platform, callbacks, null);
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?)
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.
2725 @param ld {App | string | array}
2726 Application launch descriptor. Could be defined as one of following:
2728 <li>instance of App object,
2729 <li>string with URL of application JNLP file
2730 <li>or array (where URL attribute is required)
2732 At least link to JNLP file must be provided (could be full URL or relative to
2735 Note that passing parameters through the Apps object is not supported by this method.
2736 Any parameters specified will be ignored.
2738 @param platform {Platform}
2739 Optional platform requirements (such as JRE and JavaFX versions).
2741 @param callbacks {Callbacks | array}
2742 Optional set of callbacks. See Callbacks for details.
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);
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).
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.
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.
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).
2764 @param ld {App | string | array}
2765 Application launch descriptor. Could be defined as one of following:
2767 <li>instance of App object,
2768 <li>array (where attribute names are same as in App object)
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.
2777 embed: function(ld, platform, cb) {
2778 return doEmbed(ld, platform, cb);
2782 Registers statically deployed Java applet to customize loading experience
2783 if Javascript is enabled.
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.
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.
2798 register: function(id, platform, callbacks) {
2799 return doRegister(id, platform, callbacks);
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.
2810 * @method hideSplash
2811 * @param id Identifier of applet whose splash panel need to be hidden
2813 hideSplash: function(id) {
2814 return doHideSplash(id);
2818 Helper function: cross-browser onLoad support
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.
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).
2830 It is ok to call this function multiple times. It will append
2831 to existing chain of events (and do not replace them).
2833 @method addOnloadCallback
2835 @param {function} fn
2836 (required) function to call
2838 @param strictMode {boolean} Flag indicating whether page assets need to
2839 be loaded before launch (default is false).
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)) {
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
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).
2860 installCallbacks: function(id, cb) {
2861 doInstallCallbacks(id, cb);
2864 /** Platform requirements for application launch.
2867 The version pattern strings are of the form #[.#[.#[_#]]][+|*],
2868 which includes strings such as "1.6", * "2.0*", and "1.6.0_18+".
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.
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".
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.
2884 Both "+" and "*" will match any installed version of component. However if component is not
2885 installed then validation will fail.
2891 Array describing platform requirements. Element names should match
2892 Platform properties.
2894 Platform: function(r) {
2895 //init with defaults
2900 @type version pattern string
2905 Minimum JavaFX version.
2907 @type version pattern string
2912 Java Plugin version.
2913 If set to null then browser plugin support for embedded content is not validated.
2915 @type version pattern string
2920 List of requested JVM arguments.
2925 this.jvmargs = null;
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(" ");
2939 * Returns string replesentation of platform spec. Useful for debugging.
2941 this.toString = function() {
2942 return "Platform [jvm=" + this.jvm + ", javafx=" + this.javafx
2943 + ", plugin=" + this.plugin + ", jvmargs=" + this.jvmargs + "]";
2948 Application launch descriptor.
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.
2960 App: function(url, details) {
2962 Location of application's JNLP file. Can not be null or undefined.
2969 this.scriptable = true;
2970 this.sharedjvm = true;
2972 if (details != undefined && details != null) {
2974 Identifier of this App. Expected to be unique on this page.
2975 If null then it is autogenerated.
2979 this.id = details.id;
2981 Base64 encoded content of JNLP file.
2982 @property jnlp_content
2985 this.jnlp_content = details.jnlp_content;
2987 Applet width. Could be absolute or relative (e.g. 50 or 50%)
2991 this.width = details.width;
2993 Applet height. Could be absolute or relative (e.g. 50 or 50%)
2997 this.height = details.height;
3000 Set of named parameters to pass to application.
3004 this.params = details.params;
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.
3011 //TODO: AI: will it affect applet callbacks?
3013 @property scriptable
3017 this.scriptable = details.scriptable;
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.
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.
3030 this.sharedjvm = details.sharedjvm;
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.
3036 Note that element may be not inserted into the DOM tree yet.
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}
3043 this.placeholder = details.placeholder;
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"
3050 this.toolkit = details.toolkit;
3054 * Returns string representation of this object.
3058 this.toString = function() {
3061 if (notNull(this.params)) {
3063 for (p in this.params) {
3064 pstr += ((first) ? "" : ", ") + p + " => " + this.params[p];
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 + "]";
3078 Set of callbacks to be used to customize user experience.
3083 @param cb {list of callbacks}
3084 set of callbacks to set
3086 Callbacks: function(cb) {
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().
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.
3097 @property onGetSplash
3099 @default Default splash panel for JavaFX applications embedded into web page, null otherwise.
3101 this.onGetSplash = defaultGetSplashHandler;
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.
3113 This method is NOT called if platform requirement could not be met
3114 (e.g. if platfrom is not supported or if installation
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().
3120 If handler is null then it is treated as no op handler.
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
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
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
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.
3145 this.onInstallNeeded = defaultInstallHandler;
3148 Called before installation of required component is triggered.
3149 For manual install scenario it is called before installation
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.
3158 In case of automatic launch of installation onInstallFinished will be called
3159 once installation is complete (succesfully or not).
3161 If handler is null then it is treated as no-op handler.
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
3169 <li> <b>component</b> - String "Java", "JavaFX" or "Java bundle"
3170 <li> <b>isAutoInstall</b> - true if installer will be launched
3172 <li> <b>restartNeeded</b> - boolean to specify whether browser restart will be required
3175 @property onInstallStarted
3176 @type function(placeholder, component, isAuto, restartNeeded)
3179 this.onInstallStarted = defaultInstallStartedHandler;
3182 Called once installation of required component
3183 is completed. This method will NOT be called if installation is
3184 performed in manual mode.
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
3197 @property onInstallFinished
3198 @type function(placeholder, component, status, relaunchNeeded)
3201 this.onInstallFinished = defaultInstallFinishedHandler;
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.
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:
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
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")
3223 <li> JavaFX specific codes
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")
3232 Default error handler handles both application launch errors and embedded content.
3234 @property onDeployError
3235 @type function(app, mismatchEvent)
3237 this.onDeployError = defaultDeployErrorHandler;
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.
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.
3248 this.onGetNoPluginMessage = defaultGetNoPluginMessageHandler;
3251 Called once applet is ready to accept Javascript calls.
3252 Only supported for plugin version 10.0.0 or later
3253 @property onJavascriptReady
3257 this.onJavascriptReady = null;
3260 Called if application failed to launch.
3261 Only supported for plugin version 10.0.0 or later.
3263 @property onRuntimeError
3267 this.onRuntimeError = defaultRuntimeErrorHandler;
3269 //overwrite with provided parameters