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