PageRenderTime 64ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 1ms

/deploy/javafx-deploy/src/js/dtjava.js

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