JAL-3690 update to swingjs patch 3.2.9
authorJim Procter <jprocter@issues.jalview.org>
Sun, 6 Dec 2020 13:35:12 +0000 (13:35 +0000)
committerJim Procter <jprocter@issues.jalview.org>
Sun, 6 Dec 2020 13:35:12 +0000 (13:35 +0000)
doc/building.html
swingjs/SwingJS-site.zip
swingjs/timestamp
swingjs/ver/3.2.7/SwingJS-site.zip
swingjs/ver/3.2.8/SwingJS-site.zip
swingjs/ver/3.2.9-j11/SwingJS-site.zip
swingjs/ver/3.2.9-j11/differences.txt [new file with mode: 0644]
swingjs/ver/3.2.9-j11/net.sf.j2s.core-j11.jar [new file with mode: 0644]
swingjs/ver/3.2.9/SwingJS-site.zip
swingjs/ver/3.2.9/differences.txt [new file with mode: 0644]
swingjs/ver/3.2.9/timestamp

index cc5eb30..7ddb67e 100644 (file)
@@ -1,54 +1,89 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml">
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
 <head>
-  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-  <meta http-equiv="Content-Style-Type" content="text/css" />
+  <meta charset="utf-8" />
   <meta name="generator" content="pandoc" />
-  <title>Building Jalview from Source</title>
-  <style type="text/css">code{white-space: pre;}</style>
-  <style type="text/css">
-div.sourceCode { overflow-x: auto; }
-table.sourceCode, tr.sourceCode, td.lineNumbers, td.sourceCode {
-  margin: 0; padding: 0; vertical-align: baseline; border: none; }
-table.sourceCode { width: 100%; line-height: 100%; }
-td.lineNumbers { text-align: right; padding-right: 4px; padding-left: 4px; color: #aaaaaa; border-right: 1px solid #aaaaaa; }
-td.sourceCode { padding-left: 5px; }
-code > span.kw { color: #007020; font-weight: bold; } /* Keyword */
-code > span.dt { color: #902000; } /* DataType */
-code > span.dv { color: #40a070; } /* DecVal */
-code > span.bn { color: #40a070; } /* BaseN */
-code > span.fl { color: #40a070; } /* Float */
-code > span.ch { color: #4070a0; } /* Char */
-code > span.st { color: #4070a0; } /* String */
-code > span.co { color: #60a0b0; font-style: italic; } /* Comment */
-code > span.ot { color: #007020; } /* Other */
-code > span.al { color: #ff0000; font-weight: bold; } /* Alert */
-code > span.fu { color: #06287e; } /* Function */
-code > span.er { color: #ff0000; font-weight: bold; } /* Error */
-code > span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
-code > span.cn { color: #880000; } /* Constant */
-code > span.sc { color: #4070a0; } /* SpecialChar */
-code > span.vs { color: #4070a0; } /* VerbatimString */
-code > span.ss { color: #bb6688; } /* SpecialString */
-code > span.im { } /* Import */
-code > span.va { color: #19177c; } /* Variable */
-code > span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
-code > span.op { color: #666666; } /* Operator */
-code > span.bu { } /* BuiltIn */
-code > span.ex { } /* Extension */
-code > span.pp { color: #bc7a00; } /* Preprocessor */
-code > span.at { color: #7d9029; } /* Attribute */
-code > span.do { color: #ba2121; font-style: italic; } /* Documentation */
-code > span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
-code > span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
-code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
+  <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
+  <title>"Building Jalview from Source"</title>
+  <style>
+    code{white-space: pre-wrap;}
+    span.smallcaps{font-variant: small-caps;}
+    span.underline{text-decoration: underline;}
+    div.column{display: inline-block; vertical-align: top; width: 50%;}
+    div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
+    ul.task-list{list-style: none;}
+    pre > code.sourceCode { white-space: pre; position: relative; }
+    pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
+    pre > code.sourceCode > span:empty { height: 1.2em; }
+    code.sourceCode > span { color: inherit; text-decoration: inherit; }
+    div.sourceCode { margin: 1em 0; }
+    pre.sourceCode { margin: 0; }
+    @media screen {
+    div.sourceCode { overflow: auto; }
+    }
+    @media print {
+    pre > code.sourceCode { white-space: pre-wrap; }
+    pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
+    }
+    pre.numberSource code
+      { counter-reset: source-line 0; }
+    pre.numberSource code > span
+      { position: relative; left: -4em; counter-increment: source-line; }
+    pre.numberSource code > span > a:first-child::before
+      { content: counter(source-line);
+        position: relative; left: -1em; text-align: right; vertical-align: baseline;
+        border: none; display: inline-block;
+        -webkit-touch-callout: none; -webkit-user-select: none;
+        -khtml-user-select: none; -moz-user-select: none;
+        -ms-user-select: none; user-select: none;
+        padding: 0 4px; width: 4em;
+        color: #aaaaaa;
+      }
+    pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa;  padding-left: 4px; }
+    div.sourceCode
+      {   }
+    @media screen {
+    pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
+    }
+    code span.al { color: #ff0000; font-weight: bold; } /* Alert */
+    code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
+    code span.at { color: #7d9029; } /* Attribute */
+    code span.bn { color: #40a070; } /* BaseN */
+    code span.bu { } /* BuiltIn */
+    code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
+    code span.ch { color: #4070a0; } /* Char */
+    code span.cn { color: #880000; } /* Constant */
+    code span.co { color: #60a0b0; font-style: italic; } /* Comment */
+    code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
+    code span.do { color: #ba2121; font-style: italic; } /* Documentation */
+    code span.dt { color: #902000; } /* DataType */
+    code span.dv { color: #40a070; } /* DecVal */
+    code span.er { color: #ff0000; font-weight: bold; } /* Error */
+    code span.ex { } /* Extension */
+    code span.fl { color: #40a070; } /* Float */
+    code span.fu { color: #06287e; } /* Function */
+    code span.im { } /* Import */
+    code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
+    code span.kw { color: #007020; font-weight: bold; } /* Keyword */
+    code span.op { color: #666666; } /* Operator */
+    code span.ot { color: #007020; } /* Other */
+    code span.pp { color: #bc7a00; } /* Preprocessor */
+    code span.sc { color: #4070a0; } /* SpecialChar */
+    code span.ss { color: #bb6688; } /* SpecialString */
+    code span.st { color: #4070a0; } /* String */
+    code span.va { color: #19177c; } /* Variable */
+    code span.vs { color: #4070a0; } /* VerbatimString */
+    code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
   </style>
+  <!--[if lt IE 9]>
+    <script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
+  <![endif]-->
   <style>
   @font-face {
     font-family: octicons-link;
     src: url(data:font/woff;charset=utf-8;base64,d09GRgABAAAAAAZwABAAAAAACFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABEU0lHAAAGaAAAAAgAAAAIAAAAAUdTVUIAAAZcAAAACgAAAAoAAQAAT1MvMgAAAyQAAABJAAAAYFYEU3RjbWFwAAADcAAAAEUAAACAAJThvmN2dCAAAATkAAAABAAAAAQAAAAAZnBnbQAAA7gAAACyAAABCUM+8IhnYXNwAAAGTAAAABAAAAAQABoAI2dseWYAAAFsAAABPAAAAZwcEq9taGVhZAAAAsgAAAA0AAAANgh4a91oaGVhAAADCAAAABoAAAAkCA8DRGhtdHgAAAL8AAAADAAAAAwGAACfbG9jYQAAAsAAAAAIAAAACABiATBtYXhwAAACqAAAABgAAAAgAA8ASm5hbWUAAAToAAABQgAAAlXu73sOcG9zdAAABiwAAAAeAAAAME3QpOBwcmVwAAAEbAAAAHYAAAB/aFGpk3jaTY6xa8JAGMW/O62BDi0tJLYQincXEypYIiGJjSgHniQ6umTsUEyLm5BV6NDBP8Tpts6F0v+k/0an2i+itHDw3v2+9+DBKTzsJNnWJNTgHEy4BgG3EMI9DCEDOGEXzDADU5hBKMIgNPZqoD3SilVaXZCER3/I7AtxEJLtzzuZfI+VVkprxTlXShWKb3TBecG11rwoNlmmn1P2WYcJczl32etSpKnziC7lQyWe1smVPy/Lt7Kc+0vWY/gAgIIEqAN9we0pwKXreiMasxvabDQMM4riO+qxM2ogwDGOZTXxwxDiycQIcoYFBLj5K3EIaSctAq2kTYiw+ymhce7vwM9jSqO8JyVd5RH9gyTt2+J/yUmYlIR0s04n6+7Vm1ozezUeLEaUjhaDSuXHwVRgvLJn1tQ7xiuVv/ocTRF42mNgZGBgYGbwZOBiAAFGJBIMAAizAFoAAABiAGIAznjaY2BkYGAA4in8zwXi+W2+MjCzMIDApSwvXzC97Z4Ig8N/BxYGZgcgl52BCSQKAA3jCV8CAABfAAAAAAQAAEB42mNgZGBg4f3vACQZQABIMjKgAmYAKEgBXgAAeNpjYGY6wTiBgZWBg2kmUxoDA4MPhGZMYzBi1AHygVLYQUCaawqDA4PChxhmh/8ODDEsvAwHgMKMIDnGL0x7gJQCAwMAJd4MFwAAAHjaY2BgYGaA4DAGRgYQkAHyGMF8NgYrIM3JIAGVYYDT+AEjAwuDFpBmA9KMDEwMCh9i/v8H8sH0/4dQc1iAmAkALaUKLgAAAHjaTY9LDsIgEIbtgqHUPpDi3gPoBVyRTmTddOmqTXThEXqrob2gQ1FjwpDvfwCBdmdXC5AVKFu3e5MfNFJ29KTQT48Ob9/lqYwOGZxeUelN2U2R6+cArgtCJpauW7UQBqnFkUsjAY/kOU1cP+DAgvxwn1chZDwUbd6CFimGXwzwF6tPbFIcjEl+vvmM/byA48e6tWrKArm4ZJlCbdsrxksL1AwWn/yBSJKpYbq8AXaaTb8AAHja28jAwOC00ZrBeQNDQOWO//sdBBgYGRiYWYAEELEwMTE4uzo5Zzo5b2BxdnFOcALxNjA6b2ByTswC8jYwg0VlNuoCTWAMqNzMzsoK1rEhNqByEyerg5PMJlYuVueETKcd/89uBpnpvIEVomeHLoMsAAe1Id4AAAAAAAB42oWQT07CQBTGv0JBhagk7HQzKxca2sJCE1hDt4QF+9JOS0nbaaYDCQfwCJ7Au3AHj+LO13FMmm6cl7785vven0kBjHCBhfpYuNa5Ph1c0e2Xu3jEvWG7UdPDLZ4N92nOm+EBXuAbHmIMSRMs+4aUEd4Nd3CHD8NdvOLTsA2GL8M9PODbcL+hD7C1xoaHeLJSEao0FEW14ckxC+TU8TxvsY6X0eLPmRhry2WVioLpkrbp84LLQPGI7c6sOiUzpWIWS5GzlSgUzzLBSikOPFTOXqly7rqx0Z1Q5BAIoZBSFihQYQOOBEdkCOgXTOHA07HAGjGWiIjaPZNW13/+lm6S9FT7rLHFJ6fQbkATOG1j2OFMucKJJsxIVfQORl+9Jyda6Sl1dUYhSCm1dyClfoeDve4qMYdLEbfqHf3O/AdDumsjAAB42mNgYoAAZQYjBmyAGYQZmdhL8zLdDEydARfoAqIAAAABAAMABwAKABMAB///AA8AAQAAAAAAAAAAAAAAAAABAAAAAA==) format('woff');
   }
-  
+
   body {
     -webkit-text-size-adjust: 100%;
     text-size-adjust: 100%;
@@ -64,92 +99,92 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
     margin-left: auto;
     margin-right: auto;
   }
-  
+
   body a {
     background-color: transparent;
   }
-  
+
   body a:active,
   body a:hover {
     outline: 0;
   }
-  
+
   body strong {
     font-weight: bold;
   }
-  
+
   body h1 {
     font-size: 2em;
     margin: 0.67em 0;
   }
-  
+
   body img {
     border: 0;
   }
-  
+
   body hr {
     box-sizing: content-box;
     height: 0;
   }
-  
+
   body pre {
     overflow: auto;
   }
-  
+
   body code,
   body kbd,
   body pre {
     font-family: monospace, monospace;
     font-size: 1em;
   }
-  
+
   body input {
     color: inherit;
     font: inherit;
     margin: 0;
   }
-  
+
   body html input[disabled] {
     cursor: default;
   }
-  
+
   body input {
     line-height: normal;
   }
-  
+
   body input[type="checkbox"] {
     box-sizing: border-box;
     padding: 0;
   }
-  
+
   body table {
     border-collapse: collapse;
     border-spacing: 0;
   }
-  
+
   body td,
   body th {
     padding: 0;
   }
-  
+
   body * {
     box-sizing: border-box;
   }
-  
+
   body input {
     font: 13px / 1.4 Helvetica, arial, nimbussansl, liberationsans, freesans, clean, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
   }
-  
+
   body a {
     color: #4078c0;
     text-decoration: none;
   }
-  
+
   body a:hover,
   body a:active {
     text-decoration: underline;
   }
-  
+
   body hr {
     height: 0;
     margin: 15px 0;
@@ -158,18 +193,18 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
     border: 0;
     border-bottom: 1px solid #ddd;
   }
-  
+
   body hr:before {
     display: table;
     content: "";
   }
-  
+
   body hr:after {
     display: table;
     clear: both;
     content: "";
   }
-  
+
   body h1,
   body h2,
   body h3,
@@ -180,73 +215,73 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
     margin-bottom: 15px;
     line-height: 1.1;
   }
-  
+
   body h1 {
     font-size: 30px;
   }
-  
+
   body h2 {
     font-size: 21px;
   }
-  
+
   body h3 {
     font-size: 16px;
   }
-  
+
   body h4 {
     font-size: 14px;
   }
-  
+
   body h5 {
     font-size: 12px;
   }
-  
+
   body h6 {
     font-size: 11px;
   }
-  
+
   body blockquote {
     margin: 0;
   }
-  
+
   body ul,
   body ol {
     padding: 0;
     margin-top: 0;
     margin-bottom: 0;
   }
-  
+
   body ol ol,
   body ul ol {
     list-style-type: lower-roman;
   }
-  
+
   body ul ul ol,
   body ul ol ol,
   body ol ul ol,
   body ol ol ol {
     list-style-type: lower-alpha;
   }
-  
+
   body dd {
     margin-left: 0;
   }
-  
+
   body code {
     font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace;
     font-size: 12px;
   }
-  
+
   body pre {
     margin-top: 0;
     margin-bottom: 0;
     font: 12px Consolas, "Liberation Mono", Menlo, Courier, monospace;
   }
-  
+
   body .select::-ms-expand {
     opacity: 0;
   }
-  
+
   body .octicon {
     font: normal normal normal 16px/1 octicons-link;
     display: inline-block;
@@ -259,45 +294,45 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
     -ms-user-select: none;
     user-select: none;
   }
-  
+
   body .octicon-link:before {
     content: '\f05c';
   }
-  
+
   body:before {
     display: table;
     content: "";
   }
-  
+
   body:after {
     display: table;
     clear: both;
     content: "";
   }
-  
+
   body>*:first-child {
     margin-top: 0 !important;
   }
-  
+
   body>*:last-child {
     margin-bottom: 0 !important;
   }
-  
+
   body a:not([href]) {
     color: inherit;
     text-decoration: none;
   }
-  
+
   body .anchor {
     display: inline-block;
     padding-right: 2px;
     margin-left: -18px;
   }
-  
+
   body .anchor:focus {
     outline: none;
   }
-  
+
   body h1,
   body h2,
   body h3,
@@ -309,7 +344,7 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
     font-weight: bold;
     line-height: 1.4;
   }
-  
+
   body h1 .octicon-link,
   body h2 .octicon-link,
   body h3 .octicon-link,
@@ -320,7 +355,7 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
     vertical-align: middle;
     visibility: hidden;
   }
-  
+
   body h1:hover .anchor,
   body h2:hover .anchor,
   body h3:hover .anchor,
@@ -329,7 +364,7 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
   body h6:hover .anchor {
     text-decoration: none;
   }
-  
+
   body h1:hover .anchor .octicon-link,
   body h2:hover .anchor .octicon-link,
   body h3:hover .anchor .octicon-link,
@@ -338,61 +373,61 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
   body h6:hover .anchor .octicon-link {
     visibility: visible;
   }
-  
+
   body h1 {
     padding-bottom: 0.3em;
     font-size: 1.75em;
     line-height: 1.2;
   }
-  
+
   body h1 .anchor {
     line-height: 1;
   }
-  
+
   body h2 {
     padding-bottom: 0.3em;
     font-size: 1.5em;
     line-height: 1.225;
   }
-  
+
   body h2 .anchor {
     line-height: 1;
   }
-  
+
   body h3 {
     font-size: 1.25em;
     line-height: 1.43;
   }
-  
+
   body h3 .anchor {
     line-height: 1.2;
   }
-  
+
   body h4 {
     font-size: 1em;
   }
-  
+
   body h4 .anchor {
     line-height: 1.2;
   }
-  
+
   body h5 {
     font-size: 1em;
   }
-  
+
   body h5 .anchor {
     line-height: 1.1;
   }
-  
+
   body h6 {
     font-size: 1em;
     color: #777;
   }
-  
+
   body h6 .anchor {
     line-height: 1.1;
   }
-  
+
   body p,
   body blockquote,
   body ul,
@@ -403,7 +438,7 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
     margin-top: 0;
     margin-bottom: 16px;
   }
-  
+
   body hr {
     height: 4px;
     padding: 0;
@@ -411,12 +446,12 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
     background-color: #e7e7e7;
     border: 0 none;
   }
-  
+
   body ul,
   body ol {
     padding-left: 2em;
   }
-  
+
   body ul ul,
   body ul ol,
   body ol ol,
@@ -424,15 +459,15 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
     margin-top: 0;
     margin-bottom: 0;
   }
-  
+
   body li>p {
     margin-top: 16px;
   }
-  
+
   body dl {
     padding: 0;
   }
-  
+
   body dl dt {
     padding: 0;
     margin-top: 16px;
@@ -440,26 +475,26 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
     font-style: italic;
     font-weight: bold;
   }
-  
+
   body dl dd {
     padding: 0 16px;
     margin-bottom: 16px;
   }
-  
+
   body blockquote {
     padding: 0 15px;
     color: #777;
     border-left: 4px solid #ddd;
   }
-  
+
   body blockquote>:first-child {
     margin-top: 0;
   }
-  
+
   body blockquote>:last-child {
     margin-bottom: 0;
   }
-  
+
   body table {
     display: block;
     width: 100%;
@@ -467,32 +502,32 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
     word-break: normal;
     word-break: keep-all;
   }
-  
+
   body table th {
     font-weight: bold;
   }
-  
+
   body table th,
   body table td {
     padding: 6px 13px;
     border: 1px solid #ddd;
   }
-  
+
   body table tr {
     background-color: #fff;
     border-top: 1px solid #ccc;
   }
-  
+
   body table tr:nth-child(2n) {
     background-color: #f8f8f8;
   }
-  
+
   body img {
     max-width: 100%;
     box-sizing: content-box;
     background-color: #fff;
   }
-  
+
   body code {
     padding: 0;
     padding-top: 0;
@@ -502,13 +537,13 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
     background-color: rgba(0,0,0,0.04);
     border-radius: 3px;
   }
-  
+
   body code:before,
   body code:after {
     letter-spacing: -0.2em;
     content: "\00a0";
   }
-  
+
   body pre>code {
     padding: 0;
     margin: 0;
@@ -518,11 +553,11 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
     background: transparent;
     border: 0;
   }
-  
+
   body .highlight {
     margin-bottom: 16px;
   }
-  
+
   body .highlight pre,
   body pre {
     padding: 16px;
@@ -532,16 +567,16 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
     background-color: #f7f7f7;
     border-radius: 3px;
   }
-  
+
   body .highlight pre {
     margin-bottom: 0;
     word-break: normal;
   }
-  
+
   body pre {
     word-wrap: normal;
   }
-  
+
   body pre code {
     display: inline;
     max-width: initial;
@@ -553,12 +588,12 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
     background-color: transparent;
     border: 0;
   }
-  
+
   body pre code:before,
   body pre code:after {
     content: normal;
   }
-  
+
   body kbd {
     display: inline-block;
     padding: 3px 5px;
@@ -572,34 +607,34 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
     border-radius: 3px;
     box-shadow: inset 0 -1px 0 #bbb;
   }
-  
+
   body .pl-c {
     color: #969896;
   }
-  
+
   body .pl-c1,
   body .pl-s .pl-v {
     color: #0086b3;
   }
-  
+
   body .pl-e,
   body .pl-en {
     color: #795da3;
   }
-  
+
   body .pl-s .pl-s1,
   body .pl-smi {
     color: #333;
   }
-  
+
   body .pl-ent {
     color: #63a35c;
   }
-  
+
   body .pl-k {
     color: #a71d5d;
   }
-  
+
   body .pl-pds,
   body .pl-s,
   body .pl-s .pl-pse .pl-s1,
@@ -609,69 +644,69 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
   body .pl-sr .pl-sre {
     color: #183691;
   }
-  
+
   body .pl-v {
     color: #ed6a43;
   }
-  
+
   body .pl-id {
     color: #b52a1d;
   }
-  
+
   body .pl-ii {
     background-color: #b52a1d;
     color: #f8f8f8;
   }
-  
+
   body .pl-sr .pl-cce {
     color: #63a35c;
     font-weight: bold;
   }
-  
+
   body .pl-ml {
     color: #693a17;
   }
-  
+
   body .pl-mh,
   body .pl-mh .pl-en,
   body .pl-ms {
     color: #1d3e81;
     font-weight: bold;
   }
-  
+
   body .pl-mq {
     color: #008080;
   }
-  
+
   body .pl-mi {
     color: #333;
     font-style: italic;
   }
-  
+
   body .pl-mb {
     color: #333;
     font-weight: bold;
   }
-  
+
   body .pl-md {
     background-color: #ffecec;
     color: #bd2c00;
   }
-  
+
   body .pl-mi1 {
     background-color: #eaffea;
     color: #55a532;
   }
-  
+
   body .pl-mdr {
     color: #795da3;
     font-weight: bold;
   }
-  
+
   body .pl-mo {
     color: #1d3e81;
   }
-  
+
   body kbd {
     display: inline-block;
     padding: 3px 5px;
@@ -685,20 +720,20 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
     border-radius: 3px;
     box-shadow: inset 0 -1px 0 #bbb;
   }
-  
+
   body .task-list-item {
     list-style-type: none;
   }
-  
+
   body .task-list-item+.task-list-item {
     margin-top: 3px;
   }
-  
+
   body .task-list-item input {
     margin: 0 0.35em 0.25em -1.6em;
     vertical-align: middle;
   }
-  
+
   body :checked+.radio-label {
     z-index: 1;
     position: relative;
@@ -707,18 +742,22 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
   </style>
 </head>
 <body>
-<div id="TOC">
+<nav id="TOC" role="doc-toc">
+<ul>
+<li><a href="#building-jalview-from-source">Building Jalview from Source</a>
 <ul>
-<li><a href="#building-jalview-from-source">Building Jalview from Source</a><ul>
 <li><a href="#tldr">tl;dr</a></li>
-<li><a href="#setting-up">Setting up</a><ul>
+<li><a href="#setting-up">Setting up</a>
+<ul>
 <li><a href="#java-11-compliant-jdk">Java 11 compliant JDK</a></li>
 <li><a href="#gradle-and-git">gradle and git</a></li>
 </ul></li>
-<li><a href="#downloading-the-jalview-source-tree">Downloading the Jalview source tree</a><ul>
-<li><a href="#whats-in-the-source-tree">What's in the source tree?</a></li>
+<li><a href="#downloading-the-jalview-source-tree">Downloading the Jalview source tree</a>
+<ul>
+<li><a href="#whats-in-the-source-tree">What’s in the source tree?</a></li>
 </ul></li>
-<li><a href="#building-jalview">Building Jalview</a><ul>
+<li><a href="#building-jalview">Building Jalview</a>
+<ul>
 <li><a href="#minimal-jalview-build">Minimal Jalview Build</a></li>
 <li><a href="#jalview-in-a-jar-file">Jalview in a Jar File</a></li>
 <li><a href="#distributed-jar-files">Distributed Jar Files</a></li>
@@ -729,13 +768,14 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
 </ul></li>
 <li><a href="#gradle-properties">Gradle properties</a></li>
 <li><a href="#enabling-code-coverage-with-openclover">Enabling Code Coverage with OpenClover</a></li>
-<li><a href="#setting-up-in-eclipse-ide">Setting up in Eclipse IDE</a><ul>
+<li><a href="#setting-up-in-eclipse-ide">Setting up in Eclipse IDE</a>
+<ul>
 <li><a href="#installing-eclipse-ide">Installing Eclipse IDE</a></li>
 <li><a href="#importing-jalview-as-an-eclipse-project">Importing Jalview as an Eclipse project</a></li>
 </ul></li>
 </ul></li>
 </ul>
-</div>
+</nav>
 <h1 id="building-jalview-from-source">Building Jalview from Source</h1>
 <h2 id="tldr">tl;dr</h2>
 <pre><code># download
@@ -762,11 +802,11 @@ java -jar getdown-launcher.jar . jalview</code></pre>
 <li>git</li>
 </ul>
 <blockquote>
-<p>The versions and installation methods here are just suggestions (which we have tested so are known to work). If you need or wish to use different implementations (particularly you might need a bespoke JDK if you are on an exotic architecture) then the general build instructions should work with any gradle 5+. You should be able to compile the bytecode with any JDK Java 11+. The resulting bytecode (in particular the shadow jar) should be runnable in any JRE Java 1.8+. Remember that because Jalview and the getdown launcher are Java bytecode you can build on one system where you might have gradle, and run on another where you don't (JRE 1.8+ required).</p>
+<p>The versions and installation methods here are just suggestions (which we have tested so are known to work). If you need or wish to use different implementations (particularly you might need a bespoke JDK if you are on an exotic architecture) then the general build instructions should work with any gradle 5+. You should be able to compile the bytecode with any JDK Java 11+. The resulting bytecode (in particular the shadow jar) should be runnable in any JRE Java 1.8+. Remember that because Jalview and the getdown launcher are Java bytecode you can build on one system where you might have gradle, and run on another where you don’t (JRE 1.8+ required).</p>
 </blockquote>
 <h3 id="java-11-compliant-jdk">Java 11 compliant JDK</h3>
 <h4 id="all-platforms">All platforms</h4>
-<p>We recommend obtaining an OpenJDK JDK 11 (since 11 is the long term support release) from AdoptOpenJDK: <a href="https://adoptopenjdk.net/?variant=openjdk11&amp;jvmVariant=hotspot" class="uri">https://adoptopenjdk.net/?variant=openjdk11&amp;jvmVariant=hotspot</a>, either the <em>Installer</em> or <code>.zip</code>/<code>.tar.gz</code> variants whichever you prefer (if you're not sure, choose the <em>Installer</em>).</p>
+<p>We recommend obtaining an OpenJDK JDK 11 (since 11 is the long term support release) from AdoptOpenJDK: <a href="https://adoptopenjdk.net/?variant=openjdk11&amp;jvmVariant=hotspot" class="uri">https://adoptopenjdk.net/?variant=openjdk11&amp;jvmVariant=hotspot</a>, either the <em>Installer</em> or <code>.zip</code>/<code>.tar.gz</code> variants whichever you prefer (if you’re not sure, choose the <em>Installer</em>).</p>
 <blockquote>
 <h5 id="alternativecli-install-of-adoptopenjdk-11">Alternative/CLI install of AdoptOpenJDK 11</h5>
 <p>You can also install adoptopenjdk11 using either <code>brew</code> (macOS), <code>choco</code> (Windows) (see the section on <code>gradle</code> and <code>git</code> for more informaiton on <code>brew</code> and <code>choco</code>) or <code>yum</code> or <code>apt</code> (Linux):</p>
@@ -782,35 +822,35 @@ brew cask install adoptopenjdk11</code></pre>
 <p>You should be able to install the latest (or sufficiently recent) versions of gradle and git using your OS package manager.</p>
 <h4 id="macos">MacOS</h4>
 <p>we recommend using <code>brew</code>, which can be installed following the instructions at <a href="https://brew.sh/" class="uri">https://brew.sh/</a>. After installing <code>brew</code>, open a Terminal window and type in (using an Administrator privileged user):</p>
-<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">brew</span> install gradle git</code></pre></div>
+<div class="sourceCode" id="cb4"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb4-1"><a href="#cb4-1"></a><span class="ex">brew</span> install gradle git</span></code></pre></div>
 <p>or if you aready have them installed but need to upgrade the version:</p>
-<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">brew</span> upgrade gradle git</code></pre></div>
+<div class="sourceCode" id="cb5"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb5-1"><a href="#cb5-1"></a><span class="ex">brew</span> upgrade gradle git</span></code></pre></div>
 <h4 id="windows">Windows</h4>
 <p>we suggest using the <strong>Chocolatey</strong> package manager. See install instructions at <a href="https://chocolatey.org/" class="uri">https://chocolatey.org/</a>, and you will just need</p>
-<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">choco</span> install gradle
-<span class="ex">choco</span> install git</code></pre></div>
+<div class="sourceCode" id="cb6"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb6-1"><a href="#cb6-1"></a><span class="ex">choco</span> install gradle</span>
+<span id="cb6-2"><a href="#cb6-2"></a><span class="ex">choco</span> install git</span></code></pre></div>
 <p>Alternatively, you could install a real <code>bash</code> shell and install both <code>gradle</code> and <code>git</code> through <code>apt-get</code>. See <a href="https://devblogs.microsoft.com/commandline/bash-on-ubuntu-on-windows-download-now-3/" class="uri">https://devblogs.microsoft.com/commandline/bash-on-ubuntu-on-windows-download-now-3/</a> for how to install the ubuntu bash shell in Windows 10.</p>
 <p>Another alternative would be to install them separately. For <code>gradle</code> follow the instructions at <a href="https://gradle.org/install/" class="uri">https://gradle.org/install/</a>, and for <code>git</code> here are a couple of suggestions: Git for Windows <a href="https://gitforwindows.org/" class="uri">https://gitforwindows.org/</a>. Getting the individual installs working together on the command line will be trickier so we recommend using Chocolatey or bash.</p>
 <h4 id="linux">Linux</h4>
-<p>this will depend on which distribution you're using.</p>
-<h5 id="for-debian-based-distributions-e.g.-mint-ubuntu-debian">For <em>Debian</em> based distributions (e.g. Mint, Ubuntu, Debian)</h5>
+<p>this will depend on which distribution you’re using.</p>
+<h5 id="for-debian-based-distributions-e.g.-mint-ubuntu-debian">For <em>Debian</em> based distributions (e.g. Mint, Ubuntu, Debian)</h5>
 <p>run</p>
-<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"> <span class="fu">sudo</span> apt-get install gradle git</code></pre></div>
-<h5 id="for-rpm-based-distributions-e.g.-fedora-centos-redhat">for RPM-based distributions (e.g. Fedora, CentOS, RedHat)</h5>
+<div class="sourceCode" id="cb7"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb7-1"><a href="#cb7-1"></a> <span class="fu">sudo</span> apt-get install gradle git</span></code></pre></div>
+<h5 id="for-rpm-based-distributions-e.g.-fedora-centos-redhat">for RPM-based distributions (e.g. Fedora, CentOS, RedHat)</h5>
 <p>run</p>
-<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="fu">sudo</span> yum install gradle git</code></pre></div>
-<p>If you have some other version of linux you'll probably be able to work it out!</p>
+<div class="sourceCode" id="cb8"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb8-1"><a href="#cb8-1"></a><span class="fu">sudo</span> yum install gradle git</span></code></pre></div>
+<p>If you have some other version of linux you’ll probably be able to work it out!</p>
 <h2 id="downloading-the-jalview-source-tree">Downloading the Jalview source tree</h2>
-<p>This can be done with <code>git</code>. On the command line, change directory to where you want to download Jalview's build-tree top level directory. Then run</p>
-<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="fu">git</span> clone http://source.jalview.org/git/jalview.git</code></pre></div>
-<p>You'll get some progress output and after a minute or two you should have the full Jalview build-tree in the folder <code>jalview</code>.</p>
-<h3 id="whats-in-the-source-tree">What's in the source tree?</h3>
-<p>Jalview is a mature product with its codebase going back many years. As such it doesn't have a folder structure that most new gradle projects would have, so you might not find everything in the place you might expect. Here's a brief description of what you might find in the main folders under the <code>jalview</code> tree.</p>
+<p>This can be done with <code>git</code>. On the command line, change directory to where you want to download Jalview’s build-tree top level directory. Then run</p>
+<div class="sourceCode" id="cb9"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb9-1"><a href="#cb9-1"></a><span class="fu">git</span> clone http://source.jalview.org/git/jalview.git</span></code></pre></div>
+<p>You’ll get some progress output and after a minute or two you should have the full Jalview build-tree in the folder <code>jalview</code>.</p>
+<h3 id="whats-in-the-source-tree">What’s in the source tree?</h3>
+<p>Jalview is a mature product with its codebase going back many years. As such it doesn’t have a folder structure that most new gradle projects would have, so you might not find everything in the place you might expect. Here’s a brief description of what you might find in the main folders under the <code>jalview</code> tree.</p>
 <p>Within the <code>jalview</code> folder you will find (of possible interest):</p>
 <table>
 <colgroup>
-<col width="16%" />
-<col width="83%" />
+<col style="width: 15%" />
+<col style="width: 84%" />
 </colgroup>
 <thead>
 <tr class="header">
@@ -821,7 +861,7 @@ brew cask install adoptopenjdk11</code></pre>
 <tbody>
 <tr class="odd">
 <td><code>bin/</code></td>
-<td>used by eclipse for compiled classes -- no need to touch this</td>
+<td>used by eclipse for compiled classes – no need to touch this</td>
 </tr>
 <tr class="even">
 <td><code>build/</code></td>
@@ -849,7 +889,7 @@ brew cask install adoptopenjdk11</code></pre>
 </tr>
 <tr class="even">
 <td><code>getdown/website/</code></td>
-<td>the assembled &quot;download&quot; folder used by getdown for downloads/upgrades</td>
+<td>the assembled “download” folder used by getdown for downloads/upgrades</td>
 </tr>
 <tr class="odd">
 <td><code>getdown/files/</code></td>
@@ -895,6 +935,10 @@ brew cask install adoptopenjdk11</code></pre>
 <td><code>gradle.properties</code></td>
 <td>configurable properties for the build process</td>
 </tr>
+<tr class="even">
+<td><code>RELEASE</code></td>
+<td>propertyfile configuring JALVIEW_VERSION (from jalview.version) and the release branch (from jalview.release). An alternative file can be specified via JALVIEW_RELEASE_FILE property</td>
+</tr>
 </tbody>
 </table>
 <p>Note that you need a Java 11 JDK to compile Jalview whether your target build is Java 1.8 or Java 11.</p>
@@ -907,32 +951,32 @@ brew cask install adoptopenjdk11</code></pre>
 </blockquote>
 <h3 id="minimal-jalview-build">Minimal Jalview Build</h3>
 <p>To compile the necessary class files, just run</p>
-<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">gradle</span> compileJava</code></pre></div>
+<div class="sourceCode" id="cb11"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb11-1"><a href="#cb11-1"></a><span class="ex">gradle</span> compileJava</span></code></pre></div>
 <p>to compile the classes into the <code>classes</code> folder. You should now be able to run the Jalview application directly with</p>
-<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">java</span> -cp <span class="st">&quot;classes:resources:help:j11lib/*&quot;</span> jalview.bin.Jalview</code></pre></div>
+<div class="sourceCode" id="cb12"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb12-1"><a href="#cb12-1"></a><span class="ex">java</span> -cp <span class="st">&quot;classes:resources:help:j11lib/*&quot;</span> jalview.bin.Jalview</span></code></pre></div>
 <p>You can also run with an automatic large memory setting (which will set the maximum memory heap of the Jalview JVM to 90% of your local physical memory) and docked icon setting (if possible in your OS) with</p>
-<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">java</span> -cp <span class="st">&quot;classes:resources:help:j11lib/*&quot;</span> jalview.bin.Launcher</code></pre></div>
+<div class="sourceCode" id="cb13"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb13-1"><a href="#cb13-1"></a><span class="ex">java</span> -cp <span class="st">&quot;classes:resources:help:j11lib/*&quot;</span> jalview.bin.Launcher</span></code></pre></div>
 <blockquote>
-<p><em>You must use just &quot;<code>j11lib/*</code>&quot; and not &quot;<code>j11lib/*.jar</code>&quot; as this is a special Java classpath argument wildcard interpreted by <code>java</code>, <strong>not</strong> a shell expansion wildcard interpreted by the shell.</em></p>
+<p><em>You must use just “<code>j11lib/*</code>” and not “<code>j11lib/*.jar</code>” as this is a special Java classpath argument wildcard interpreted by <code>java</code>, <strong>not</strong> a shell expansion wildcard interpreted by the shell.</em></p>
 </blockquote>
-<p>Note that <code>jalview.bin.Launcher</code> is a simplified launcher class that re-launches <code>jalview.bin.Jalview</code> with the same JRE (<em>not</em> the same JVM instance), classpath and arguments, but with an automatically determined <code>-Xmx...</code> memory setting if one hasn't been provided.</p>
+<p>Note that <code>jalview.bin.Launcher</code> is a simplified launcher class that re-launches <code>jalview.bin.Jalview</code> with the same JRE (<em>not</em> the same JVM instance), classpath and arguments, but with an automatically determined <code>-Xmx...</code> memory setting if one hasn’t been provided.</p>
 <h3 id="jalview-in-a-jar-file">Jalview in a Jar File</h3>
 <p>To package the <code>classes</code>, <code>resources</code>, and <code>help</code> into one jar, you can run</p>
-<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">gradle</span> jar</code></pre></div>
+<div class="sourceCode" id="cb14"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb14-1"><a href="#cb14-1"></a><span class="ex">gradle</span> jar</span></code></pre></div>
 <p>which assembles the Jalview classes and resources into <code>dist/jalview.jar</code></p>
 <p>To run this, use</p>
-<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">java</span> -cp <span class="st">&quot;dist/jalview.jar:j11lib/*&quot;</span> jalview.bin.Jalview</code></pre></div>
+<div class="sourceCode" id="cb15"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb15-1"><a href="#cb15-1"></a><span class="ex">java</span> -cp <span class="st">&quot;dist/jalview.jar:j11lib/*&quot;</span> jalview.bin.Jalview</span></code></pre></div>
 <h3 id="distributed-jar-files">Distributed Jar Files</h3>
 <p>To simplify this, all required <code>.jar</code> files can be assembled into the <code>dist</code> folder using</p>
-<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">gradle</span> makeDist</code></pre></div>
+<div class="sourceCode" id="cb16"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb16-1"><a href="#cb16-1"></a><span class="ex">gradle</span> makeDist</span></code></pre></div>
 <p>which puts all required jar files into <code>dist</code> so you can run with</p>
-<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">java</span> -cp <span class="st">&quot;dist/*&quot;</span> jalview.bin.Jalview</code></pre></div>
+<div class="sourceCode" id="cb17"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb17-1"><a href="#cb17-1"></a><span class="ex">java</span> -cp <span class="st">&quot;dist/*&quot;</span> jalview.bin.Jalview</span></code></pre></div>
 <h3 id="single-shadow-jar-file">Single <em>shadow</em> Jar File</h3>
-<p>The shadow jar file is a single <code>.jar</code> that contains all required classes and resources from <code>jalview.jar</code> and all of the supporting libraries in <code>j11lib/*.jar</code> merged into one <code>.jar</code> archive file. A default launching class (<code>MAIN-CLASS: jalview.bin.Launcher</code>) is specified in the <code>.jar</code> manifest file (<code>META/MANIFEST.MF</code>) so a start class doesn't need to be specified.</p>
+<p>The shadow jar file is a single <code>.jar</code> that contains all required classes and resources from <code>jalview.jar</code> and all of the supporting libraries in <code>j11lib/*.jar</code> merged into one <code>.jar</code> archive file. A default launching class (<code>MAIN-CLASS: jalview.bin.Launcher</code>) is specified in the <code>.jar</code> manifest file (<code>META/MANIFEST.MF</code>) so a start class doesn’t need to be specified.</p>
 <p>Build the shadow jar file in <code>build/lib/jalview-all-11.jar</code> with</p>
-<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">gradle</span> shadowJar</code></pre></div>
+<div class="sourceCode" id="cb18"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb18-1"><a href="#cb18-1"></a><span class="ex">gradle</span> shadowJar</span></code></pre></div>
 <p>and run it with</p>
-<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">java</span> -jar build/lib/jalview-all-11.jar</code></pre></div>
+<div class="sourceCode" id="cb19"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb19-1"><a href="#cb19-1"></a><span class="ex">java</span> -jar build/lib/jalview-all-11.jar</span></code></pre></div>
 <p>Because no arguments are required, most OSes will associate a <code>.jar</code> file with the <code>java</code> application (if this has been installed through the OS and not just a local unzip) as a <code>-jar</code> argument so you may find you can launch <code>jalview-all-11.jar</code> just by double-clicking on it)!</p>
 <blockquote>
 <p>The <code>shadowJar</code> task is not a requirement for any other task, so to build the shadow jar file you must specify the <code>shadowJar</code> task.</p>
@@ -943,38 +987,38 @@ brew cask install adoptopenjdk11</code></pre>
 <h3 id="building-the-getdown-launcher">Building the <code>getdown</code> launcher</h3>
 <p>We have made significant customisations to the <code>getdown</code> launcher which you can find in <code>getdown/src/getdown</code>.</p>
 <blockquote>
-<p>You don't need to build this afresh as the required <code>gradle-core.jar</code> and <code>gradle-launcher.jar</code> files are already distributed in <code>j11lib</code> and <code>getdown/lib</code> but if you want to, then you'll need a working Maven and also a Java 8 JDK. Ensure the Java 8 <code>javac</code> is forefront in your path and do</p>
-<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="bu">cd</span> getdown/src/getdown
-<span class="ex">mvn</span> clean package -Dgetdown.host.whitelist=<span class="st">&quot;jalview.org,*.jalview.org&quot;</span></code></pre></div>
+<p>You don’t need to build this afresh as the required <code>gradle-core.jar</code> and <code>gradle-launcher.jar</code> files are already distributed in <code>j11lib</code> and <code>getdown/lib</code> but if you want to, then you’ll need a working Maven and also a Java 8 JDK. Ensure the Java 8 <code>javac</code> is forefront in your path and do</p>
+<div class="sourceCode" id="cb20"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb20-1"><a href="#cb20-1"></a><span class="bu">cd</span> getdown/src/getdown</span>
+<span id="cb20-2"><a href="#cb20-2"></a><span class="ex">mvn</span> clean package -Dgetdown.host.whitelist=<span class="st">&quot;jalview.org,*.jalview.org&quot;</span></span></code></pre></div>
 <p>and you will find the required <code>.jar</code> files in <code>core/target/gradle-core-XXX.jar</code> and <code>launcher/target/gradle-launcher-XXX.jar</code>. The <code>gradle-core.jar</code> should then be copied to all three of the <code>j8lib</code>, <code>j11lib</code> and <code>getdown/lib</code> folders, whilst the <code>gradle-launcher.jar</code> only needs to be copied to <code>getdown/lib</code>.</p>
 <p>The <code>mvn</code> command should ideally include the <code>-Dgetdown.host.whitelist=*.jalview.org</code> setting. This, and the necessary file copying commands, can be found in <code>getdown/src/getdown/mvn_cmd</code>.</p>
 </blockquote>
 <p>To assemble Jalview with <code>getdown</code> use the following gradle task:</p>
-<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">gradle</span> getdown</code></pre></div>
-<p>This puts all the necessary files to launch Jalview with <code>getdown</code> into <code>getdown/website/11/</code>. This could be treated as the reference folder for <code>getdown</code>, which is where a getdown launcher will check to see if the Jalview application files it has are up to date, and download if they aren't or it simply doesn't have them.</p>
+<div class="sourceCode" id="cb21"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb21-1"><a href="#cb21-1"></a><span class="ex">gradle</span> getdown</span></code></pre></div>
+<p>This puts all the necessary files to launch Jalview with <code>getdown</code> into <code>getdown/website/11/</code>. This could be treated as the reference folder for <code>getdown</code>, which is where a getdown launcher will check to see if the Jalview application files it has are up to date, and download if they aren’t or it simply doesn’t have them.</p>
 <p>A minimal getdown-launcher can be found in <code>getdown/files/11/</code> which checks its up-to-date status with (the absolute path to) <code>getdown/website/11/</code>.</p>
 <p>This can be launched with</p>
-<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">java</span> -jar getdown/files/11/getdown-launcher.jar getdown/files/11/ jalview</code></pre></div>
+<div class="sourceCode" id="cb22"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb22-1"><a href="#cb22-1"></a><span class="ex">java</span> -jar getdown/files/11/getdown-launcher.jar getdown/files/11/ jalview</span></code></pre></div>
 <blockquote>
-<p>We've already met the <code>-jar file.jar</code> arguments. The next argument is the working folder for getdown, and the final argument, &quot;<code>jalview</code>&quot;, is a getdown application id (only &quot;<code>jalview</code>&quot; is defined here).</p>
+<p>We’ve already met the <code>-jar file.jar</code> arguments. The next argument is the working folder for getdown, and the final argument, “<code>jalview</code>”, is a getdown application id (only “<code>jalview</code>” is defined here).</p>
 </blockquote>
 <h3 id="running-tests">Running tests</h3>
 <p>There are substantial tests written for Jalview that use TestNG, which you can run with</p>
-<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">gradle</span> test</code></pre></div>
+<div class="sourceCode" id="cb23"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb23-1"><a href="#cb23-1"></a><span class="ex">gradle</span> test</span></code></pre></div>
 <p>These normally take around 5 - 10 minutes to complete and outputs its full results into the <code>tests/</code> folder. A summary of results should appear in your console.</p>
 <p>You can run different defined groups of tests with</p>
-<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">gradle</span> test -PtestngGroups=Network</code></pre></div>
+<div class="sourceCode" id="cb24"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb24-1"><a href="#cb24-1"></a><span class="ex">gradle</span> test -PtestngGroups=Network</span></code></pre></div>
 <p>Available groups include Functional (default), Network, External.</p>
 <h4 id="excluding-some-tests">Excluding some tests</h4>
-<p>Some of Jalview's Functional tests don't pass reliably in all environments. We tag these tests with a group like 'Not-bamboo' to mark them for exclusion when we run tests as part of continuous integration.</p>
+<p>Some of Jalview’s Functional tests don’t pass reliably in all environments. We tag these tests with a group like ‘Not-bamboo’ to mark them for exclusion when we run tests as part of continuous integration.</p>
 <p>To exclude one or more groups of tests, add them as a comma separated list in testngExcludedGroups.</p>
-<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">gradle</span> test -PtestngExcludedGroups=Not-bamboo</code></pre></div>
+<div class="sourceCode" id="cb25"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb25-1"><a href="#cb25-1"></a><span class="ex">gradle</span> test -PtestngExcludedGroups=Not-bamboo</span></code></pre></div>
 <h3 id="installer-packaging-with-install4j">Installer packaging with <em>install4j</em></h3>
 <p>Jalview is currently using <em>install4j</em> <a href="https://www.ej-technologies.com/products/install4j/overview.html" class="uri">https://www.ej-technologies.com/products/install4j/overview.html</a> as its installer packaging tool.</p>
 <p>If you have a licensed installation of <em>install4j</em> you can build Jalview installers by running</p>
-<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">gradle</span> installers</code></pre></div>
+<div class="sourceCode" id="cb26"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb26-1"><a href="#cb26-1"></a><span class="ex">gradle</span> installers</span></code></pre></div>
 <p>though you may need to fiddle with the <code>install4j</code> and <code>copyInstall4jTemplate</code> tasks in <code>build.gradle</code> file to point to your installation of <em>install4j</em> and also to bundled JREs if you want to bundle those into the installers.</p>
-<p>If you want more details, get in touch on our development mailing list <a href="mailto:jalview-dev@jalview.org">jalview-dev@jalview.org</a>. Sign up at <a href="http://www.compbio.dundee.ac.uk/mailman/listinfo/jalview-dev" class="uri">http://www.compbio.dundee.ac.uk/mailman/listinfo/jalview-dev</a>.</p>
+<p>If you want more details, get in touch on our development mailing list <a href="mailto:jalview-dev@jalview.org" class="email">jalview-dev@jalview.org</a>. Sign up at <a href="http://www.compbio.dundee.ac.uk/mailman/listinfo/jalview-dev" class="uri">http://www.compbio.dundee.ac.uk/mailman/listinfo/jalview-dev</a>.</p>
 <h2 id="gradle-properties">Gradle properties</h2>
 <p>There are a lot of properties configured in <code>gradle.properties</code> which we strongly recommend being left as they are unless you have a specific problem with the build process.</p>
 <p>There are a few gradle properties you might want to set on the command line with the <code>-P</code> flag when building a version of Jalview with specific requirements:</p>
@@ -982,19 +1026,19 @@ brew cask install adoptopenjdk11</code></pre>
 <p>This changes the <em>target</em> java bytecode version &gt; NOTE that you will need to use a Java 11 (or greater) JDK Java compiler to build Jalview for any byte-code target version.</p>
 <p>Valid values are <code>11</code> and <code>1.8</code>.</p>
 <p>e.g.</p>
-<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">gradle</span> shadowJar -PJAVA_VERSION=1.8</code></pre></div>
+<div class="sourceCode" id="cb27"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb27-1"><a href="#cb27-1"></a><span class="ex">gradle</span> shadowJar -PJAVA_VERSION=1.8</span></code></pre></div>
 <p>When using <code>-PJAVA_VERSION=1.8</code> the libraries from <code>j8lib</code> (instead of <code>j11lib</code>) will be used in the compile<br />
 and runtime classpath and also used in the <code>makeDist</code> build step. Where a Java version of <code>11</code> is used in folder and file names, it will instead use <code>1.8</code>. Also if you are building installer packages with <em>install4j</em> the package builder will look for JRE 1.8 bundles to package in the installers.</p>
 <blockquote>
 <p>Note that continued development of Jalview will assume a Java 11+ runtime environment, the 2.11.0 release will run under a Java 1.8 JRE with a few minor features disabled.</p>
 </blockquote>
 <h4 id="channel"><code>CHANNEL</code></h4>
-<p>This changes the <code>appbase</code> setting in <code>getdown.txt</code> (<code>appbase</code> is where the getdown launcher looks to see if there's an updated file) to point to a particular Jalview channel or some other appropriate place to look for required files. If the selected channel type requires the getdown <code>appbase</code> to be a local directory on the filesystem (instead of a website URL) then a modified version of the <code>getdown-launcher.jar</code> will be used to allow this. The two versions of the <code>getdown-launcher.jar</code> can be found in <code>getdown/lib</code>. Some other variables used in the build process might also be set differently depending on the value of <code>CHANNEL</code> to allow smooth operation of getdown in the given context.</p>
-<p>There are several values of <code>CHANNEL</code> that can be chosen, with a default of <code>LOCAL</code>. Here's what they're for and what they do:</p>
+<p>This changes the <code>appbase</code> setting in <code>getdown.txt</code> (<code>appbase</code> is where the getdown launcher looks to see if there’s an updated file) to point to a particular Jalview channel or some other appropriate place to look for required files. If the selected channel type requires the getdown <code>appbase</code> to be a local directory on the filesystem (instead of a website URL) then a modified version of the <code>getdown-launcher.jar</code> will be used to allow this. The two versions of the <code>getdown-launcher.jar</code> can be found in <code>getdown/lib</code>. Some other variables used in the build process might also be set differently depending on the value of <code>CHANNEL</code> to allow smooth operation of getdown in the given context.</p>
+<p>There are several values of <code>CHANNEL</code> that can be chosen, with a default of <code>LOCAL</code>. Here’s what they’re for and what they do:</p>
 <ul>
 <li><code>LOCAL</code>: This is for running the compiled application from the development directory. It will set
 <ul>
-<li><code>appbase</code> as <code>file://PATH/TO/YOUR/DEVELOPMENT/getdown/files/JAVA_VERSION</code> (e.g. <code>file://home/user/git/jalview/getdown/files/11</code>)</li>
+<li><code>appbase</code> as <code>file://PATH/TO/YOUR/DEVELOPMENT/getdown/files/JAVA_VERSION</code> (e.g. <code>file://home/user/git/jalview/getdown/files/11</code>)</li>
 <li>application subdir as <code>alt</code></li>
 <li>Getdown launcher can use a <code>file://</code> scheme appbase.</li>
 </ul></li>
@@ -1004,37 +1048,37 @@ and runtime classpath and also used in the <code>makeDist</code> build step. Whe
 <li>application subdir as <code>alt</code></li>
 <li>Getdown launcher cannot use a <code>file://</code> scheme appbase.</li>
 </ul></li>
-<li><code>DEVELOP</code>: This is for creating a <code>develop</code> appbase channel on the main web server. This won't become live until the actual getdown artefact is synced to the web server. It will set
+<li><code>DEVELOP</code>: This is for creating a <code>develop</code> appbase channel on the main web server. This won’t become live until the actual getdown artefact is synced to the web server. It will set
 <ul>
 <li><code>appbase</code> as <code>http://www.jalview.org/getdown/develop/JAVA_VERSION</code></li>
 <li>application subdir as <code>alt</code></li>
 <li>Getdown launcher cannot use a <code>file://</code> scheme appbase.</li>
 </ul></li>
-<li><code>SCRATCH-NAME</code>: This is for creating a temporary scratch appbase channel on the main web server. This won't become live until the actual getdown artefact is synced to the web server. This is meant for testing an over-the-air update without interfering with the live <code>release</code> or <code>develop</code> channels. The value of <code>NAME</code> can be any &quot;word-character&quot; [A-Za-z0-9_] It will set
+<li><code>SCRATCH-NAME</code>: This is for creating a temporary scratch appbase channel on the main web server. This won’t become live until the actual getdown artefact is synced to the web server. This is meant for testing an over-the-air update without interfering with the live <code>release</code> or <code>develop</code> channels. The value of <code>NAME</code> can be any “word-character” [A-Za-z0-9_] It will set
 <ul>
 <li><code>appbase</code> as <code>http://www.jalview.org/getdown/SCRATCH-NAME/JAVA_VERSION</code></li>
 <li>application subdir as <code>alt</code></li>
 <li>Getdown launcher cannot use a <code>file://</code> scheme appbase.</li>
 </ul></li>
-<li><code>TEST-LOCAL</code>: Like <code>SCRATCH</code> but with a specific <code>test-local</code> channel name and a local filesystem appbase. This is meant for testing an over-the-air update on the local filesystem. An extra property <code>LOCALDIR</code> must be given (e.g. <code>-PLOCALDIR=/home/user/tmp/test</code>) It will set
+<li><code>TEST-LOCAL</code>: Like <code>SCRATCH</code> but with a specific <code>test-local</code> channel name and a local filesystem appbase. This is meant for testing an over-the-air update on the local filesystem. An extra property <code>LOCALDIR</code> must be given (e.g. <code>-PLOCALDIR=/home/user/tmp/test</code>) It will set
 <ul>
 <li><code>appbase</code> as <code>file://${LOCALDIR}</code></li>
 <li>application subdir as <code>alt</code></li>
 <li>Getdown launcher can use a <code>file://</code> scheme appbase.</li>
 </ul></li>
-<li><code>TEST-RELEASE</code>: Like <code>SCRATCH</code> but with a specific <code>test-release</code> channel name. This won't become live until the actual getdown artefact is synced to the web server. This is meant for testing an over-the-air update without interfering with the live <code>release</code> or <code>develop</code> channels. It will set
+<li><code>TEST-RELEASE</code>: Like <code>SCRATCH</code> but with a specific <code>test-release</code> channel name. This won’t become live until the actual getdown artefact is synced to the web server. This is meant for testing an over-the-air update without interfering with the live <code>release</code> or <code>develop</code> channels. It will set
 <ul>
 <li><code>appbase</code> as <code>http://www.jalview.org/getdown/test-release/JAVA_VERSION</code></li>
 <li>application subdir as <code>alt</code></li>
 <li>Getdown launcher cannot use a <code>file://</code> scheme appbase.</li>
 </ul></li>
-<li><code>RELEASE</code>: This is for an actual release build, and will use an appbase on the main web server with the main <code>release</code> channel name. This won't become live until the actual getdown artefact is synced to the web server. It will set
+<li><code>RELEASE</code>: This is for an actual release build, and will use an appbase on the main web server with the main <code>release</code> channel name. This won’t become live until the actual getdown artefact is synced to the web server. It will set
 <ul>
 <li><code>appbase</code> as <code>http://www.jalview.org/getdown/release/JAVA_VERSION</code></li>
 <li>application subdir as <code>release</code></li>
 <li>Getdown launcher cannot use a <code>file://</code> scheme appbase.</li>
 </ul></li>
-<li><code>ARCHIVE</code>: This is a helper to create a channel for a specific release version, and will use an appbase on the main web server with a specific <code>archive/JALVIEW_VERSION</code> channel name. This won't become live until the actual getdown artefact is synced to the web server. You must also specify an <code>ARCHIVEDIR</code> property that points to an earlier version of Jalview with a <code>dist</code> directory containing the required jar files. This should create a getdown structure and digest with the older jar files. It will set
+<li><code>ARCHIVE</code>: This is a helper to create a channel for a specific release version, and will use an appbase on the main web server with a specific <code>archive/JALVIEW_VERSION</code> channel name. This won’t become live until the actual getdown artefact is synced to the web server. You must also specify an <code>ARCHIVEDIR</code> property that points to an earlier version of Jalview with a <code>dist</code> directory containing the required jar files. This should create a getdown structure and digest with the older jar files. It will set
 <ul>
 <li><code>appbase</code> as <code>http://www.jalview.org/getdown/archive/JALVIEW_VERSION/JAVA_VERSION</code></li>
 <li>application subdir as <code>alt</code></li>
@@ -1048,18 +1092,21 @@ and runtime classpath and also used in the <code>makeDist</code> build step. Whe
 </ul></li>
 </ul>
 <p>e.g.</p>
-<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">gradle</span> getdown -PCHANNEL=SCRATCH-my_test_version</code></pre></div>
+<div class="sourceCode" id="cb28"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb28-1"><a href="#cb28-1"></a><span class="ex">gradle</span> getdown -PCHANNEL=SCRATCH-my_test_version</span></code></pre></div>
+<h4 id="jalview_version-and-the-release-file">JALVIEW_VERSION and the RELEASE file</h4>
+<p>Any Jalview build will include the value of JALVIEW_VERSION in various places, including the ‘About’ and Jalview Desktop window title, and in filenames for the stand-alone executable jar. You can specify a custom version for a build via the JALVIEW_VERSION property, but for most situations, JALVIEW_VERSION will be automatically configured according to the value of the CHANNEL property, using the <code>jalview.version</code> property specified in the RELEASE file: - <code>CHANNEL=RELEASE</code> will set version to jalview.version - <code>CHANNEL=TEST or DEVELOP</code> will append ‘-test’ or ‘-develop’ to jalview.version</p>
+<p>It is also possible to specify a custom location for the RELEASE file via an optional JALVIEW_RELEASE_FILE property.</p>
 <h4 id="install4jmediatypes"><code>install4jMediaTypes</code></h4>
-<p>If you are building <em>install4j</em> installers (requires <em>install4j</em> to be installed) then this property specifies a comma-separated list of media types (i.e. platform specific installers) <em>install4j</em> should actually build.</p>
+<p>If you are building <em>install4j</em> installers (requires <em>install4j</em> to be installed) then this property specifies a comma-separated list of media types (i.e. platform specific installers) <em>install4j</em> should actually build.</p>
 <p>Currently the valid values are <code>linuxDeb</code>, <code>linuxRPM</code>, <code>macosArchive</code>, <code>unixArchive</code>, <code>unixInstaller</code>, <code>windows</code></p>
 <p>The default value is all of them.</p>
 <p>e.g.</p>
-<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="ex">gradle</span> installers -PJAVA_VERSION=1.8 -Pinstall4jMediaTypes=macosArchive</code></pre></div>
+<div class="sourceCode" id="cb29"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb29-1"><a href="#cb29-1"></a><span class="ex">gradle</span> installers -PJAVA_VERSION=1.8 -Pinstall4jMediaTypes=macosArchive</span></code></pre></div>
 <p>To get an up-to-date list of possible values, you can run</p>
-<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="fu">perl</span> -n -e <span class="st">&#39;m/^\s*&lt;(\w+)[^&gt;]*\bmediaFileName=/ &amp;&amp; print &quot;$1\n&quot;;&#39;</span> utils/install4j/install4j_template.install4j  <span class="kw">|</span> <span class="fu">sort</span> -u</code></pre></div>
+<div class="sourceCode" id="cb30"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb30-1"><a href="#cb30-1"></a><span class="fu">perl</span> -n -e <span class="st">&#39;m/^\s*&lt;(\w+)[^&gt;]*\bmediaFileName=/ &amp;&amp; print &quot;$1\n&quot;;&#39;</span> utils/install4j/install4j_template.install4j  <span class="kw">|</span> <span class="fu">sort</span> -u</span></code></pre></div>
 <p>in the <code>jalview</code> root folder.</p>
 <h2 id="enabling-code-coverage-with-openclover">Enabling Code Coverage with OpenClover</h2>
-<p>Bytecode instrumentation tasks are enabled by specifying 'true' (or just a non-whitespace non-numeric word) in the 'clover' property. This adds the 'openclover' plugin to the build script's classpath, making it possible to track code execution during test which can be viewed as an HTML report published at build/reports/clover/index.html.</p>
+<p>Bytecode instrumentation tasks are enabled by specifying ‘true’ (or just a non-whitespace non-numeric word) in the ‘clover’ property. This adds the ‘openclover’ plugin to the build script’s classpath, making it possible to track code execution during test which can be viewed as an HTML report published at build/reports/clover/index.html.</p>
 <p><code>gradle -Pclover=true test cloverReport</code></p>
 <h4 id="troubleshooting-report-generation">Troubleshooting report generation</h4>
 <p>The build forks a new JVM process to run the clover report generation tools (both XML and HTML reports are generated by default). The following properties can be used to specify additional options or adjust JVM memory settings. Default values for these options are:</p>
@@ -1075,31 +1122,28 @@ and runtime classpath and also used in the <code>makeDist</code> build step. Whe
 <h2 id="setting-up-in-eclipse-ide">Setting up in Eclipse IDE</h2>
 <h3 id="installing-eclipse-ide">Installing Eclipse IDE</h3>
 <p>We develop in Eclipse, and support settings to develop and save Jalview source code in our preferred style. We also support running the Jalview application, debugging and running tests with TestNG from within Eclipse.</p>
-<p>To get Jalview set up as a project in Eclipse, we recommend using at least the 2019-12 version of Eclipse IDE for Java Developers which you can download from the Eclipse website: <a href="https://www.eclipse.org/downloads/" class="uri">https://www.eclipse.org/downloads/</a>. Since Eclipse 2020-03 you are encouraged to use the Eclipse Installer (see the Eclipse Downloads page). In the installer, when given a choice of packages for Eclipse you should choose the &quot;Eclipse IDE for Enterprise Java Developers&quot; package.</p>
-<div class="figure">
-<img src="./images/eclipse_installer.png" title="Eclipse Installer screenshot" />
-
-</div>
+<p>To get Jalview set up as a project in Eclipse, we recommend using at least the 2019-12 version of Eclipse IDE for Java Developers which you can download from the Eclipse website: <a href="https://www.eclipse.org/downloads/" class="uri">https://www.eclipse.org/downloads/</a>. Since Eclipse 2020-03 you are encouraged to use the Eclipse Installer (see the Eclipse Downloads page). In the installer, when given a choice of packages for Eclipse you should choose the “Eclipse IDE for Enterprise Java Developers” package.</p>
+<p><img src="./images/eclipse_installer.png" title="Eclipse Installer screenshot" /></p>
 <p>Once Eclipse is installed, we also recommend installing several plugins from the Eclipse Marketplace.</p>
 <p>Some of these should already be installed with the Enterprise Java Developer package:</p>
-<ol style="list-style-type: decimal">
+<ol type="1">
 <li>Buildship Gradle Integration 3.0 (or greater)</li>
 <li>EclEmma Java Code Coverage</li>
 <li>Egit - Git Integration for Eclipse</li>
 </ol>
-<p>To install the others, launch Eclipse, and go to Help-&gt;Eclipse Marketplace...</p>
+<p>To install the others, launch Eclipse, and go to Help-&gt;Eclipse Marketplace…</p>
 <p>Search for and install:</p>
-<ol style="list-style-type: decimal">
+<ol type="1">
 <li>Groovy Development Tools 3.4.0 (or greater)</li>
 <li>Checkstyle Plug-in (optional)</li>
-<li>TestNG for Eclipse (optional -- only needed if you want to run tests from Eclipse)</li>
+<li>TestNG for Eclipse (optional – only needed if you want to run tests from Eclipse)</li>
 </ol>
 <blockquote>
 <p>At time of writing, TestNG for Eclipse does not show up in the Eclipse Marketplace as the latest released version does not install in Eclipse 2019-03. However, you can install a working release of TestNG for Eclipse by going to</p>
-<p>Help-&gt;Install New Software...</p>
+<p>Help-&gt;Install New Software…</p>
 <p>and entering</p>
 <p><code>TestNG Release - https://dl.bintray.com/testng-team/testng-eclipse-release</code></p>
-<p>into the <em>Work with</em> box and click on the <em>Add...</em> button.</p>
+<p>into the <em>Work with</em> box and click on the <em>Add…</em> button.</p>
 <p>Eclipse might pause for a bit with the word <em>Pending</em> in the table below at this point, but it will eventually list TestNG with a selection box under the <em>Name</em> column.</p>
 <p>Select <em>TestNG</em> and carry on through the install process to install the TestNG plugin.</p>
 </blockquote>
@@ -1108,51 +1152,48 @@ and runtime classpath and also used in the <code>makeDist</code> build step. Whe
 <p>Java -&gt; Installed JREs</p>
 <p>If your Java 11 installation is not listed, click on</p>
 <p><em>Add</em> -&gt; Standard VM -&gt; <em>Next</em></p>
-<p>and enter the JRE home. You can browse to where it is installed. Give it a name (like &quot;AdoptOpenJDK 11&quot;). Select this JDK as the default JRE and click on <em>Apply and Close</em>.</p>
+<p>and enter the JRE home. You can browse to where it is installed. Give it a name (like “AdoptOpenJDK 11”). Select this JDK as the default JRE and click on <em>Apply and Close</em>.</p>
 <p>You can now import Jalview.</p>
 <h3 id="importing-jalview-as-an-eclipse-project">Importing Jalview as an Eclipse project</h3>
 <h4 id="importing-an-already-downloaded-git-repo">Importing an already downloaded git repo</h4>
 <p>If you have already downloaded Jalview using <code>git clone</code> then you can import this folder into Eclipse directly.</p>
 <p>It is important to import Jalview as a Gradle project (not as a Java project), so go to</p>
-<p>File-&gt;Import...</p>
+<p>File-&gt;Import…</p>
 <p>find and select</p>
 <p>Gradle-&gt;Existing Gradle Project</p>
 <p>and then click on the <em>Next</em> button.</p>
 <p>In the following options, it is the <strong>Project Root Directory</strong> you should set to be the <code>jalview</code> folder that git downloaded. Then you can click on the <em>Finish</em> button.</p>
 <h4 id="using-eclipse-ide-to-download-the-git-repo">Using Eclipse IDE to download the git repo</h4>
-<p>If you don't have git as a command line tool or would prefer to work entirely within Eclipse IDE then Eclipse's eGit plugin can set up a git repo of the jalview source. Go to</p>
-<p>File-&gt;Import...</p>
+<p>If you don’t have git as a command line tool or would prefer to work entirely within Eclipse IDE then Eclipse’s eGit plugin can set up a git repo of the jalview source. Go to</p>
+<p>File-&gt;Import…</p>
 <p>Find and select</p>
 <p>Git-&gt;Projects from Git</p>
 <p>and then click on the <em>Next</em> button.</p>
 <p>Then select Clone URI and click on <em>Next</em>.</p>
 <p>In the next window (Source Git Repository) you should put the <code>git clone</code> URL in the text box labelled <code>URI</code>. If you have a Jalview developer account (with a username and password for the Jalview git repository) then you should enter <code>https://source.jalview.org/git/jalview.git</code>. If you do not have a Jalview developer account then you should enter <code>http://source.jalview.org/git/jalview.git</code>. You will not be able to push any of your changes back to the Jalview git repository. However you can still pull all branches of the Jalview source code to your computer and develop the code there. &gt; You can sign up for a Jalview developer account at <a href="https://source.jalview.org/crucible/" class="uri">https://source.jalview.org/crucible/</a></p>
-<p>If you have a Jalview developer account, enter the username and password and decide if you want to use Eclipse's secure storage. If you don't have an account you can leave the Authentication section blank.</p>
-<div class="figure">
-<img src="./images/eclipse_egit_connection.png" alt="Eclipse eGit connection configuration" />
-<p class="caption">Eclipse eGit connection configuration</p>
-</div>
+<p>If you have a Jalview developer account, enter the username and password and decide if you want to use Eclipse’s secure storage. If you don’t have an account you can leave the Authentication section blank.</p>
+<figure>
+<img src="./images/eclipse_egit_connection.png" alt="" /><figcaption>Eclipse eGit connection configuration</figcaption>
+</figure>
 <p>Click on the <em>Next</em> button.</p>
 <p>The next window (Branch Selection) gives a list of the many Jalview branches, which by default will be all checked. You probably only want to download one branch (you can always download others at a later time). This is likely to be the <code>develop</code> branch so you can click on the <em>Deselect All</em> button, find the <code>develop</code> branch (the filter text helps), select that, and then click on the <em>Next</em> button.</p>
 <p>Choose a directory to your copy of the git repo in, and leave the other options as they are and click on the <em>Next</em> button. The next stage may take a minute or two as it checks out the selected branch(es) from the Jalview git repository.</p>
-<p>When it has finished it is important to select <strong>Import as general project</strong> and then click on <em>Next</em>. &gt; Ideally there would be an <em>Import as gradle project</em> here but there isn't -- we'll sort that out later.</p>
-<div class="figure">
-<img src="./images/eclipse_egit_import.png" alt="Eclipse eGit import choice" />
-<p class="caption">Eclipse eGit import choice</p>
-</div>
+<p>When it has finished it is important to select <strong>Import as general project</strong> and then click on <em>Next</em>. &gt; Ideally there would be an <em>Import as gradle project</em> here but there isn’t – we’ll sort that out later.</p>
+<figure>
+<img src="./images/eclipse_egit_import.png" alt="" /><figcaption>Eclipse eGit import choice</figcaption>
+</figure>
 <p>Click on the <em>Next</em> button.</p>
-<p>You can change the project name here. By default it will show as <strong>jalview</strong> which is fine unless you have another instance of the a Jalview project also called jalview, in which case you could change this project's name now to avoid a conflict within Eclipse.</p>
+<p>You can change the project name here. By default it will show as <strong>jalview</strong> which is fine unless you have another instance of the a Jalview project also called jalview, in which case you could change this project’s name now to avoid a conflict within Eclipse.</p>
 <p>Click on <em>Finish</em>!</p>
-<p>However, we haven't finished...</p>
+<p>However, we haven’t finished…</p>
 <p>You should now see, and be able to expand, the jalview project in the Project Explorer. We need to tell eclipse that this is a Gradle project, which will then allow the Eclipse Buildship plugin to automatically configure almost everything else!</p>
 <p>Right click on the project name (jalview) in the Project Explorer and find Configure towards the bottom of this long context menu, then choose Add Gradle Nature.</p>
-<div class="figure">
-<img src="./images/eclipse_add_gradle_nature.png" alt="Eclipse Add Gradle Nature" />
-<p class="caption">Eclipse Add Gradle Nature</p>
-</div>
+<figure>
+<img src="./images/eclipse_add_gradle_nature.png" alt="" /><figcaption>Eclipse Add Gradle Nature</figcaption>
+</figure>
 <p>The project should now reconfigure itself using the <code>build.gradle</code> file to dynamically set various aspects of the project including classpath.</p>
 <h4 id="additional-views">Additional views</h4>
-<p>Some views that are automatically added when Importing a Gradle Project are not added when simply Adding a Gradle Nature, but we can add these manually by clicking on Window-&gt;Show View-&gt;Console and Window-&gt;Show View-&gt;Other... Filter with the word &quot;gradle&quot; and choose both <strong>Gradle Executions</strong> and <strong>Gradle Tasks</strong> and then click on the <em>Open</em> button.</p>
+<p>Some views that are automatically added when Importing a Gradle Project are not added when simply Adding a Gradle Nature, but we can add these manually by clicking on Window-&gt;Show View-&gt;Console and Window-&gt;Show View-&gt;Other… Filter with the word “gradle” and choose both <strong>Gradle Executions</strong> and <strong>Gradle Tasks</strong> and then click on the <em>Open</em> button.</p>
 <p>Okay, ready to code! Use of Eclipse is beyond the scope of this document, but you can find more information about developing jalview and our developer workflow in the google doc <a href="https://docs.google.com/document/d/1lZo_pZRkazDBJGNachXr6qCVlw8ByuMYG6e9SZlPUlQ/edit?usp=sharing" class="uri">https://docs.google.com/document/d/1lZo_pZRkazDBJGNachXr6qCVlw8ByuMYG6e9SZlPUlQ/edit?usp=sharing</a></p>
 <hr />
 <p><a href="mailto:help@jalview.org">Jalview Development Team</a></p>
index f22d3d2..26693ec 100644 (file)
Binary files a/swingjs/SwingJS-site.zip and b/swingjs/SwingJS-site.zip differ
index 0fd8664..398b625 100644 (file)
@@ -1 +1 @@
-20201129201754 
+20201205191847 
index 763362e..dd6765a 100644 (file)
Binary files a/swingjs/ver/3.2.7/SwingJS-site.zip and b/swingjs/ver/3.2.7/SwingJS-site.zip differ
index 46f33d8..dd6765a 100644 (file)
Binary files a/swingjs/ver/3.2.8/SwingJS-site.zip and b/swingjs/ver/3.2.8/SwingJS-site.zip differ
index 00a5e9e..dd6765a 100644 (file)
Binary files a/swingjs/ver/3.2.9-j11/SwingJS-site.zip and b/swingjs/ver/3.2.9-j11/SwingJS-site.zip differ
diff --git a/swingjs/ver/3.2.9-j11/differences.txt b/swingjs/ver/3.2.9-j11/differences.txt
new file mode 100644 (file)
index 0000000..46e49ec
--- /dev/null
@@ -0,0 +1,1484 @@
+Notes
+=====
+
+---IMPORTANT CHARACTER SET NOTE---
+
+It is critical that all development work in Java2Script 
+be done in UTF-8. This means:
+
+- making sure your Eclipse project is set up for UTF-8 (not the Eclipse default?)
+- making sure your server can serve up UTF-8 by default for any browser-loaded files
+- making sure you don't edit a Java2Script class file or one of the site .js files
+    using a non-UTF-8 editor. It may replace non-Latin characters with "?" or garbage.
+- making sure that your web pages are delivered with proper headings indicating HTML5 and UTF-8
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+
+Note that the DOCTYPE tag is critical for some browsers to switch into HTML5 mode. (MSIE?)
+
+
+
+  
+In particular, the Mandarin character 秘 (mi; "secret") is used extensively throughout
+the SwingJS class files to distinguish j2s-specific fields and methods that must not 
+ever be shadowed or overridden by subclasses. For example, we see in java.lang.Thread.java:
+
+               public static JSThread 秘thisThread;
+
+----------------------------------
+
+
+updated 3/21/2020 -- adds note about HashMap, Hashtable, and HashSet iterator ordering
+updated 3/20/2020 -- adds note about interning, new String("xxx"), and "xxx"
+updated 2/26/2020 -- adds Graphics.setClip issue
+updated 12/22/19 -- additional issues
+updated 11/03/19 -- adds information about File.exists() and points to src/javajs/async
+updated 10/26/19 -- adds information about File.createTempFile()
+updated 8/16/19 -- minor typos and added summary paragraph
+updated 7/19/19 -- clarification that AWT and Swing classes are supported directly
+updated 5/13/19 -- Mandarin U+79D8 reserved character; Missing Math methods; int and long
+updated 5/10/19 -- adds a section on static issues in multi-(duplicate)-applet pages
+updated 1/4/19 -- nio
+updated 9/15/18 -- adds integer 1/0 == Infinity
+updated 7/24/18 -- most classes replaced with https://github.com/frohoff/jdk8u-jdk
+updated 6/5/17 -- reserved package name "window"
+updated 3/11/17 -- myClass.getField
+updated 3/7/17 -- overloading of JSplitPane.setDividerLocation
+updated 3/2/17 -- more indication of classes not implemented (KeyListener)
+
+=============================================================================
+SwingJS and OpenJDK 8+
+=============================================================================
+
+SwingJS implements a wide range of the Java language in JavaScript. The base
+version for this implementation is OpenJDK8. some classes are implemented using 
+older source code, and there are some missing methods. For the most part, this is 
+no real problem. You can add or modify any java class just be adding it as source
+in your project. Or (preferably) you can contact me, and I can get it into the 
+distribution. Or (even more preferably) you can do that via a patch submission. 
+
+=================
+DESIGN PHILOSOPHY
+=================
+
+The java2script/SwingJS design goal is to recreate a recognizable, easily debuggable
+equivalent in JavaScript for as much of Java as practical. This means, for example, 
+that one can call in JavaScript 
+
+  new java.util.Hashtable()
+  
+and for all practical purposes it will appear that Java is running.
+
+
+Method and Field Disambiguation
+-------------------------------
+
+SwingJS has no problem with the overloading of methods, for example:
+
+  public void print(int b);
+  public void print(float b);
+
+JavaScript does not allow overloading of methods, and the common practice in
+Java of naming a field the same as a method -- isAllowed and isAllowed() -- is
+not possible in JavaScript. As a result, SwingJS implements "fully-qualified" 
+method names using "$" parameter type separation. Thus, these methods in SwingJS
+will be referred to as print$I and print$F. The rules for this encoding are
+relatively simple: 
+
+1. The seven primitive types in Java are encoded $I (int), $L (long), $F (float), 
+$D (double), $B (byte) $Z (boolean), and $H (short). 
+
+2. String and Object are encoded as $S and $O, respectively.
+
+3. "java_lang_" is dropped for all other classes in the java.lang package (as in Java).
+   For example:  $StringBuffer, not $java_lang_StringBuffer
+
+4. All other classes are encoded as 
+
+ "$" + Class.getName().replace(".","_")
+
+For example, in Java we see:
+
+  public void equals(Object o) {...}
+
+Whereas in SwingJS we have:
+
+  Clazz.newMeth(C$, 'equals$O', function (o) {...}
+
+And 
+
+ this.getContentPane().add(bar, "North");
+
+becomes
+
+ this.getContentPane$().add$java_awt_Component$O(bar, "North");
+
+5. Arrays are indicated with appended "A" for each level. So
+
+  setDataVector(Object[][] dataVector, Object[] columnIdentifiers)
+  
+becomes
+
+  setDataVector$OAA$OA(dataVector, columnIdentifiers)
+
+(It is recognized that this design does introduce a bit of ambiguity, in that
+ in principal there could be user class named XA and X in the same package,
+ and methods a(X[]) and a(XA) in the same class that cannot be distinguished.
+ The benefit of this simple system, however, triumphed over the unlikelyhood
+ of that scenario.) The transpiler could be set to flag this possibility.
+
+6. Constructors are prepended with "c$". So 
+
+  public JLabel(String text) {...}
+  
+becomes:
+
+  Clazz.newMeth(C$, 'c$$S', function (text) {...});
+
+Field disambiguation involves prepending. In Java, a class and its subclass 
+can both have the same field name, such as 
+
+ boolean visible;
+When this happens, it is called "shadowing", and though not recommended, Java allows
+it. The Java2Script transpiler will prepend such shadowing fields with "$" so that the
+subclass instance has both "visible" (for use in its methods inherited from its
+superclass) and "$visible" (for its own methods). Thus, we might see in Java:
+
+  this.visible = super.visible;
+  
+while in SwingJS we will see:
+
+  this.$visible=this.visible;
+
+since JavaScript does not have the "super" keyword.
+
+
+
+Parameterless methods such as toString() are appended with "$" to become toString$().
+The one exception to this rule is private methods, which are saved in (truly) private 
+array in the class (and are not accessible by reflection). Private parameterless 
+methods retain their simple Java name, since they cannot conflict with field names.
+
+This renaming of methods has a few consequences, which are discussed more fully below.
+See particularly the section on "qualified field and method names", where it is described
+how you can use packages or classes or interfaces with ".api.js" in them to represent JavaScript
+objects for which all method names are to be left unqualified. Note that it is not 
+possible to cherry-pick methods to be unqualified; only full packages, classes or 
+interfaces can hold this status.
+
+The swingjs.api.js package in particular contains a number of useful interfaces that
+you can import into your project for JavaScript-specific capabilities.
+
+
+Applet vs. Application
+----------------------
+
+One of the very cool aspects of SwingJS is that it doesn't particularly matter if a browser-based
+Java app is an "applet" or an "application". We don't need JNLP (Java Network Launch Protocol) 
+because now we can just start up any Java application in a browser just as easily as any applet.
+The associative array that passes information to the SwingJS applet (information that formerly
+might have been part of the APPLET tag, such as width, height, and codebase, always referred to 
+in our writing as "the Info array") allows the option to specify the JApplet/Applet "code" 
+class or the application "main" class. Either one will run just fine.
+
+
+Performance
+-----------
+
+Obviously, there are limitations. One is performance, but we have seen reproducible 
+performance at 1/6 - 1/3 the speed of Java. Achieving this performance may require
+some refactoring of the Java to make it more efficient in both Java and JavaScript. 
+"for" loops need to be more carefully crafted; use of "new" and "instanceof" need to be
+minimized in critical areas. Note that method overloading -- that is, the same method name
+with different parameters, such as read(int) and read(byte) -- is no longer any problem. 
+  
+
+Threads
+-------
+
+Although there is only a single thread in JavaScript, meaning Thread.wait(), Thread.sleep(int) and 
+Thread.notify() cannot be reproduced, we have found that this is not a serious limitation. 
+For example, javax.swing.Timer() works perfectly in JavaScript. All it means is that threads 
+that use sleep(int) or notify() must be refactored to allow Timer-like callbacks. That is, 
+they must allow full exit and re-entry of Thread.run(), not the typical while/sleep motif. 
+
+The key is to create a state-based run() that can be exited and re-entered in JavaScript.
+
+
+Static fields
+-------------
+
+Final static primitive "constant" fields (String, boolean, int, etc.) such as 
+
+static final int TEST = 3;
+static final String MY_STRING = "my " + "string";
+
+are converted to their primitive form automatically by the Eclipse Java compiler 
+and do not appear in the JavaScript by their names. 
+
+Other static fields are properties of their class and can be used as expected.
+
+Note, however, that SwingJS runs all "Java" code on a page in a common "jvm" 
+(like older versions of Java). So, like the older Java schema, the JavaScript 
+equivalents of both applets and applications will share all of their static 
+fields and methods. This includes java.lang.System. 
+
+Basically, SwingJS implementations of Java run in a browser page-based sandbox 
+instead of an applet-specific one.
+
+In general, this is no problem. But if we are to implement pages with 
+multiple applets present, we must be sure to only have static references 
+that are "final" or specifically meant to be shared in a JavaScript 
+environment only (since they will not be shared in Java).
+
+A simple solution, if static non-constant references are needed, is to attach the 
+field to Thread.currentThread.threadGroup(), which is an applet-specific reference.
+Be sure, if you do this, that you use explicit setters and getters:
+
+For example, 
+
+private static String myvar;
+
+...
+
+public void setMyVar(String x) {
+  ThreadGroup g = Thread.currentThread().threadGroup();
+  /**
+   * @j2sNative g._myvar = x;
+   * 
+   */
+   {
+     myvar = x;
+   }
+}
+
+public String getMyVar() {
+  ThreadGroup g = Thread.currentThread().threadGroup();
+  /**
+   * @j2sNative return g._myvar || null;
+   * 
+   */
+   {
+     return myvar;
+   }
+}
+ in Java will get and set x the same in JavaScript and in Java. 
+A convenient way to do this in general is to supply a singleton class with
+explicitly private-only constructors and then refer to it in Java and in JavaScript
+instead of using static field, referring to myclass.getIntance().xxx instead of 
+myclass.xxx in Java (and JavaScript). 
+
+This was done extensively in the Jalview project. See jalview.bin.Instance.
+
+
+Helper Packages -- swingjs/ and javajs/
+---------------------------------------
+
+The SwingJS library is the swingjs/ package. There are interfaces that may be of assistance
+in swingjs/api, but other than that, it is not recommended that developers access classes in 
+this package. The "public" nature of their methods is really an internal necessity.
+
+In addition to swingjs/, though, there are several useful classes in the javajs/ package
+that could be very useful. This package is a stand-alone package that can be 
+cloned in any Java project that also would be great to have in any JavaScript project
+-- SwingJS-related or not. Functionality ranges from reading and writing various file 
+formats, including PDF, BMP, PNG, GIF, JPG, JSON, ZIP, and CompoundDocument formats.
+
+A variety of highly efficient three- and four-dimensional point, vector, matrix, and 
+quaternion classes are included, as they were developed for JSmol and inherited from that
+project. 
+
+Of particular interest should be javajs/async/, which includes
+
+javajs.async.Async
+javajs.async.AsyncColorChooser
+javajs.async.AsyncDialog
+javajs.async.AsyncFileChooser
+
+See javajs.async.Async JavaDoc comments for a full description of 
+these useful classes.
+
+
+Modal Dialogs
+-------------
+
+Although true modal dialogs are not possible with only one thread, a functional equivalent -- 
+asynchronous modal dialogs -- is relatively easy to set up. All the JOptionPane dialogs will
+return PropertyChangeEvents to signal that they have been disposed of and containing the results. 
+See below and classes in the javajs.async package.
+
+
+Native calls
+------------
+
+Native calls in Java are calls to operating system methods that are not in Java. JavaScript
+has no access to these, of course, and they must all be replaced by JavaScript equivalents.
+Fortunately, they are not common, and those that are present in Java (for example, in calculating
+checksums in ZIP file creation) are at a low enough level that most developers do not utilize them
+or do not even have access to them. All native calls in Java classes have been replaced by 
+Java equivalents.
+
+
+Swing GUI Peers and UIClasses
+-----------------------------
+
+One of the biggest adaptations introduced in SwingJS is in the area of the graphical 
+user interface. The issue here is complex but workable. In Java there are two background 
+concepts -- the Component "peer" (one per "heavy-weight" component, such as a Frame) and the 
+component "uiClass" (one per component, such as JButton or JTextField).
+
+Peers are native objects of the operating system. These are the virtual buttons and text areas
+that the user is interacting with at a very base level. Their events are being passed on to 
+Java or the browser by the operating system. UI classes provide a consistent "look and feel" 
+for these native objects, rendering them onto the native window canvas and handling all 
+user-generated events. They paint the borders, the backgrounds, the highlights, of every 
+control you see in Java. There is one-to-one correspondence of Swing classes and UI classes. 
+Setting the Look and Feel for a project amounts to selecting the directory from which to draw 
+these UI classes. The UI classes can be found in the javax.swing.plaf ("platform look and feel") 
+package.
+
+Early on in the development of SwingJS, we decided not to fully reproduce the painfully detailed 
+bit-by-bit painting of controls as is done in Java. Instead, we felt it was wiser to utilize the standard
+HTML5 UI capabilities as much as possible, using DIV, and INPUT especially, with extensive use
+of CSS and sometimes jQuery (menus, and sliders, for example). Thus, we have created a new 
+set of UIs -- the "HTML5 Look and Feel". These classes can be found in swingjs.plaf. Besides being
+more adaptable, this approach allows far more versatility to SwingJS developers, allowing them
+to modify the GUI to suit their needs if desired.
+
+In SwingJS, since we have no access to native peers except through the browser DOM,
+it seemed logical to merge the peer and UI idea. So instead of having one peer per heavy-weight control and
+one UI class instance for each control type, we just have one UI class instance per control, and
+that UI class instance is what is being referred to when a "peer" is notified. 
+
+In some ways this is a throw back to when all of Swing's components were subclasses of
+specific AWT components such as Button and List. These "heavy-weight components" all had their 
+own individual native peers and thus automatically took on the look and feel provided by the OS. 
+Later Swing versions implemented full look and feel for all peers, leaving only JDialog, JFrame,
+and a few other classes to have native peers. But in SwingJS we have again a 1:1 map of component
+and UI class/peer instance.
+
+The origin of most issues (read "bugs") in relation to the GUI will probably be found in the
+swingjs.plaf JSxxxxUI.java code.
+
+  
+Swing-only Components -- no longer an issue
+-------------------------------------------
+
+Swing was introduced into Java well after the Java Abstract Window Toolkit (AWT) was well
+established. As such, its designers chose to allow AWT controls such as Button and List to be used 
+alongside their Swing counterparts JButton and JList. Reading the code, it is clear that this 
+design choice posed a huge headache for Swing class developers. 
+
+For SwingJS, we decided from the beginning NOT to allow this mixed-mode programming and 
+instead to require that all components be Swing components. 
+
+However, this is no longer an issue. All AWT components in SwingJS are now subclasses of 
+javax.swing.JComponent. So far, we have found no problem with this.
+
+The a2s Adapter Package
+-----------------------
+
+Originally, we thought that we would restrict ourselves to JApplets only. That is, only
+Swing-based applets. But as we worked, we discovered that there are a lot of great 
+applets out there that are pre-Swing pure-AWT java.applet.Applet applets. Our problem was 
+that we also wanted it to be possible to quickly adapt these applets to JavaScript as well.
+The solution turned out to be simple: Write a package (a2s) that recreates the interface for 
+non-Swing components as subclasses of Swing components. Thus, a2s.Button subclasses javax.swing.JButton
+but also accepts all of the methods of java.awt.Button. This works amazingly well, with a few
+special adaptations to the core javax.swing to be "AWT-aware." All AWT components now subclass 
+a2s components, which in turn subclass JComponents. So no changes in code are necessary. We have
+successfully transpiled over 500 applets using this strategy. (Kind of surprising, actually, that
+the original Java developers did not see that option. But we have a hindsight advantage here.)
+
+
+Working with Files
+==================
+
+Simple String file names are not optimal for passing information about
+read files within SwingJS applications. 
+All work with files should either use Path or File objects exclusively. 
+These objects, after a file is read or checked for existence, will already 
+contain the file byte[] data. Doing something like this:
+
+File f = File("./test.dat");
+boolean isOK = f.exists();
+
+will load f with its byte[] data, if the file exists. 
+
+But if after that, we use:
+
+File f2 = new File(f.getAbsolutePath());
+
+f2 will not contain that data. Such copying should be done as:
+
+File f2 = new File(f);
+
+in which case, the byte[] data will be transferred.
+
+
+SwingJS uses the following criteria to determine if File.exists() returns true:
+
+(1) if this File object has been used directly to read data, or 
+(2) if reading data using this File object is successful.
+
+Note that you cannot check to see if a file exists before input or if it 
+was actually written or if it already exists prior to writing in SwingJS.  
+
+Thus, you should check each use of file.exists() carefully, and if necessary, provide a J2sNative 
+block that gives an appropriate "OK" message, for example:
+
+(/** @j2sNative 1 ? false : */ outputfile.exits())
+
+or 
+
+(/** @j2sNative 1 ? true : */ inputfile.exits())
+
+Temporary files can be created in SwingJS. SwingJS will maintain a pseudo-filesystem for files 
+created with File.createTempFile(). This is useful in that closure of writing to a temporary file 
+does not generate a pseudo-download to the user's machine.
+
+
+UNIMPLEMENTED CLASSES BY DESIGN
+===============================
+
+The SwingJS implementation of the following classes are present 
+in a way that gracefully bypasses their functionality:
+
+accessibility
+security
+serialization
+
+
+
+TODO LIST FOR UNIMPLEMENTED CLASSES
+===================================
+
+JEditorPane (minimal implementation) - DONE 12/2018; some issues still
+JSplitPane - DONE 8/2018
+JTabbedPane - DONE 10/2018
+JTree - done 12/2019
+
+
+MINOR ISSUES--required some rewriting/refactoring by Bob and Udo  
+================================================================
+
+Thread.currentThread() == dispatchThread
+
+
+MINOR ISSUES--requiring some rewriting/refactoring outside of SwingJS  
+=====================================================================
+
+See below for a full discussion.
+
+HashMap, Hashtable, and HashSet iterator ordering
+interning, new String("xxx") vs "xxx"
+Names with "$" and "_"
+positive integers do not add to give negative numbers
+ArrayIndexOutOfBounds
+java.awt.Color
+native methods
+javax.swing.JFileDialog
+key focus
+LookAndFeel and UI Classes
+System.exit(0) does not stop all processes
+list cell renderers must be JComponents
+myClass.getField not implemented
+"window" and other reserved JavaScript names
+reserved field and method names
+qualified field and method names
+missing Math methods
+Component.getGraphics(), Graphics.dispose()
+Graphics.setClip()
+
+MAJOR ISSUES--for Bob and Udo within SwingJS
+============================================
+
+fonts
+OS-dependent classes
+AWT component peers
+some aspects of reflection
+
+MAJOR ISSUES--to be resolved by implementers
+============================================
+
+fonts
+threads
+modal dialogs
+image loading
+BigDecimal not fully implemented 
+no format internationalization
+no winding rules
+text-related field implementation
+Formatter/Regex limitations
+integer 1/0 == Infinity
+
+======================================================================== 
+
+DISCUSS
+=======
+
+Table row/col sorter needs checking after removal of java.text.Collator references
+
+I had to move all of SunHints class to RenderingHints, or the 
+two classes could not be loaded. Shouldn't be a problem, I think. The sun classes are
+not accessible to developers in Java anyway, since they are generally package private.
+
+========================================================================== 
+
+//////////////////////////////////////////////////////////////////////////////
+
+UNIMPLEMENTED CLASSES
+=====================
+
+accessibility
+-------------
+
+All Accessibility handling has been commented out to save the download footprint.
+This removes the need for sun.misc.SharedSecrets as well. 
+Nothing says we could not implement accessibility. We just didn't.
+
+
+security
+--------
+
+All JavaScript security is handled by the browser natively. 
+Thus, Java security checking is no longer necessary, and 
+java.security.AccessController has been simplified to work without
+native security checking.
+
+Note that private methods in a class are REALLY private. 
+
+
+serialization
+-------------
+
+All serialization has been removed. It was never very useful for Swing anyway, 
+because one needs exactly the same Java version to save and restore serialized objects.
+
+
+keyboard accelerators and mnemonics
+-----------------------------------
+
+This work was completed in the spring of 2019. Note that in a browser, some 
+key strokes, particularly CTRL-keys, are not available. Bummer.
+
+
+MINOR ISSUES--required some rewriting/refactoring by Bob and Udo  
+================================================================
+
+
+Thread.currentThread() == dispatchThread
+----------------------------------------
+
+changed to JSToolkit.isDispatchThread()
+
+
+MINOR ISSUES--requiring some rewriting/refactoring outside of SwingJS  
+=====================================================================
+
+HashMap, Hashtable, and HashSet iterator ordering
+-------------------------------------------------
+
+In Java, iterators for HashMap, Hashtable, and HashSet do not guarantee any particular order. 
+From the HashMap documentation for Java 8:
+
+       This class makes no guarantees as to the order of the map; in particular, it does not 
+       guarantee that the order will remain constant over time.
+Likewise, for HashSet (because it is simply a convenience method for HashMap<Object,PRESENT>:
+
+       [HashSet] makes no guarantees as to the iteration order of the set.
+
+JavaScript's Map object is different. It is basically a LinkedHashMap, so it guarantees iteration
+in order of object addition.
+
+Starting with java2script 3.2.9.v1, these classes use the JavaScript Map object rather than hash codes
+whenever all keys are strictly of JavaScript typeof "string". If any key is introduced that is not a string, the
+implementation falls back to using hash codes, the same as Java. 
+
+Note strings created using new String("xxxx") are NOT typeof "string"; they are typeof "object".
+
+The result is significantly faster performance (3-12 x faster) than originally, and up to 3 x faster
+performance in JavaScript than in Java itself. Right. Faster than Java. 
+
+The JavaScript Map implementation is implemented UNLESS the constructor used is the one that
+specifies both initial capacity and load factor in their constructor. Thus, 
+
+new Hashtable()
+new HashMap()
+new HashMap(16)
+new HashSet()
+
+all use the JavaScript Map. But
+
+new Hashtable(11, 0.75f)
+new HashMap(16, 0.75f)
+new HashSet(16, 0.75f)
+
+do not. 
+
+This design allows for opting out of the JavaScript Map use in order to retain the exact behavior of 
+iterators in JavaScript as in Java.
+
+
+interning, new String("xxx") vs "xxx"
+-------------------------------------
+
+Note that the following are true in JavaScript:
+
+typeof new String("xxxx") == "object"
+typeof "xxxx" == "string"
+var s = "x";typeof ("xxx" + s) == "string"
+
+There is no equivalence to this behavior in Java, where a String is a String is a String.
+
+Be aware that SwingJS does not always create a JavaScript String object using JavaScript's 
+new String(...) constructor. It only does this for Java new String("xxxx") or new String(new String()). 
+
+In all other cases, new String(...) (in Java) results in a simple "xxxx" string in JavaScript. 
+That is, it will be JavaScript typeof "string", not typeof "object". 
+
+The reason for this design is that several classes in the Java core use toString() 
+methods that return new String(), and those classes that do that would cause a JavaScript error
+if implicitly stringified if new String() returned a JavaScript String object. 
+
+This is fine in JavaScript
+
+test1 = function() { return { toString:function(){ return "OK" } } }
+"testing" + new test1()
+>> "testingOK"
+
+But for whatever reason in JavaScript:
+
+test2 = function() { return { toString:function(){ return new String("OK") } } }
+"testing" + new test2()
+>> Uncaught TypeError: Cannot convert object to primitive value
+
+The lesson here is never to use 
+
+  return new String("...");
+
+in a Java toString() method. In Java it will be fine; in JavaScript it will also be fine as long as
+that method is never called in JavaScript implicitly in the context of string concatenation.
+
+A note about interning. Consider the following six Java constructions, where we have a == "x";
+
+"xxx"
+"xx" + "x"
+new String("xxx").intern()
+
+new String("xxx")
+"xx" + a.toString()
+"xx" + a
+
+All six of these will return java.lang.String for .getClass().getName().
+However, the first three are String literals, while the last three are String objects. 
+Thus:
+        "xxx" == "xxx"
+        "xxx" == "xx" + "x"
+        "xxx" == new String("xxx").intern()
+
+but none of the other three are equivalent to "xxx" or each other:
+
+              "xxx" != new String("xxx")
+              "xxx" != "xx" + a.toString()
+              "xxx" != "xx" + a
+  new String("xxx") != new String("xxx") 
+           "xx" + a != new String("xxx") 
+
+etc.
+
+As in Java, in SwingJS, all of the following Java assertions pass as true:
+
+               assert("xxx" == "xx" + "x"); 
+               assert("xxx" == ("xx" + a).intern()); 
+               assert("xxx" === new String("xxx").intern()); 
+               
+and both of these do as well:
+
+               assert(new String("xxx") != "xxx"); 
+               assert(new String("xxx") != new String("xxx")); 
+
+But the following two fail to assert true:
+
+        assert("xxx" != "xx" + a);
+        assert("xxx" != "xx" + a.toString());
+
+because in JavaScript, both of these right-side expressions evaluate to a simple "interned" string.
+
+In Java, however, these assertions are true because Java implicitly "boxes" String 
+concatentaion as a String object, not a literal. 
+
+Most of us know not to generally use == with Strings unless they are explicitly interned. 
+Where this problem may arise, though, is in IdentityHashMap, which compares objects using 
+System.identityHashCode(), which is not the same for different objects or their string literal equivalents.
+
+My recommendation, if you need to use IdentityHashMap with strings is to always use an explicit String.intern()
+for any keys -- unless you really want to keep every string as separate keys even if they are the same sequence, 
+in which case, use new String(). This will work in Java and in  JavaScript.
+
+Be aware when working with strings that come from SwingJS and are being used by other JavaScript modules
+that those that are String objects will return "object" for the JavaScript typeof operator, not "string".
+
+The easy way to ensure this is no problem is to concatenate strings with "" to force immediate interning:
+
+  var x = aJavaObject.getString() + "";
+
+unless you are certain that the string is being returned is a raw JavaScript string.   
+
+Names with "$" and "_"
+----------------------
+
+For the most part, this should be no problem. 
+
+Note that the use of $ and _ in Java field names has always been discouraged:
+[https://docs.oracle.com/javase/tutorial/java/nutsandbolts/variables.html]
+
+       You may find some situations where auto-generated names will contain the dollar sign, 
+       but your variable names should always avoid using it. A similar convention 
+       exists for the underscore character; while it's technically legal to begin your 
+       variable's name with "_", this practice is discouraged.
+
+Some impacts of transpiling method names with full qualification:
+
+1) SwingJS will introduce fields that start with $ or _. These will not conflict
+   if the above convention is followed.
+   
+2) Fields that have the same Java name as a method are not an issue. 
+
+3) Fields that have a Java name with $ that matches a transpiled method name, 
+   such as toString$, will need to be refactored in Java to not have that name collision.
+   
+4) Fields in a subclass that have the same name as private fields in a superclass
+   represent a name collision, because the superclass method needs to call its private
+   field even if invoked from a subclass. The solution was to modify the subclass field
+   name using one or more prepended $.
+   
+5) Use of Class.getDeclaredMethods() reflection will return Method objects having the transpiled 
+   name, not the Java name. This could require some j2sNative adjustment 
+   to strip the $... parameters from the name if that is needed. 
+
+6) Use of Method.getParameterTypes() should work fine, provided class names
+   do not contain "_". This is because the transpiler converts "." to "_" when
+   creating the fully qualified JavaScript name.
+
+
+positive integers do not add to give negative numbers
+-----------------------------------------------------
+
+In Java, the following is true:
+
+  2000000000 + 2000000000 == -294967296
+
+But in SwingJS, that will be 4000000000. So, for example, the following
+strategy will fail in SwingJS:
+
+               int newLength = lineBuf.length * 2;
+               if (newLength < 0) {
+                       newLength = Integer.MAX_VALUE;
+               }
+
+"-1" in JavaScript is not 0xFFFFFFFF.
+
+And one must take care to not compare a negative number with a 32-bit mask. So
+
+(b & 0xFF000000) == 0xFF000000
+
+is true in Java for (int) b = -1, but is false in JavaScript, because 0xFF000000 is 4278190080, 
+while (-1 & 0xFF000000) is, strangely enough, -16777216, and, in fact, 
+
+(0xFF000000 & 0xFF000000) != 0xFF000000
+
+because -16777216 is not 4278190080.
+
+The fix is that one must compare similar operations:
+
+if ((b & 0xFF000000) == (0xFF000000 & 0xFF000000)) .....
+
+Importantly, the JavaScript Int32Array does behave properly. From 
+the Firefox developer console:
+
+>> x = new Int32Array(1)
+<- Int32Array(1) [ 0 ]
+>> x[0] = 4000000000
+<- 4000000000
+>> x[0]
+<- -294967296
+
+Notice that, perhaps unexpectedly, the following two constructs produce 
+different results in JavaScript:
+
+x = new Int32Array(1);
+b = x[0] = 4000000000;
+
+(b will be 4000000000)
+
+and
+
+x = new Int32Array(1);
+x[0] = 4000000000;
+b = x[0];
+
+(b will be -294967296)
+
+
+SwingJS leverages array typing to handle all byte and short arithmetic so as
+to ensure that any byte or short operation in JavaScript does give the same 
+result in Java. The design decision to not also do this with integer math was
+a trade-off between performance and handling edge cases.
+
+
+ArrayIndexOutOfBounds
+---------------------
+
+You cannot implicitly throw an ArrayIndexOutOfBoundsException in JavaScript.
+JavaScript will simply return "undefined", not throw an Exception. So:
+
+boolean notAGoodIdeaIsOutOfBounds(String[] sa, int i) {
+  try {
+     return (sa[i] == sa[i]);
+  } catch (ArrayIndexOutOfBoundsException e) {
+       return false;
+  }
+}
+
+will work in Java but not in JavaScript. Code should not depend upon this sort 
+of trap anyway, if you ask me. 
+
+Throwable vs Error vs Exception
+-------------------------------
+
+True JavaScript errors are trapped as Throwable, whereas you can still trap
+Error and Exception as well. So if you want to be sure to catch any JavaScript
+error, use try{}catch (Throwable t){}, not try{}catch (Exception e){}. 
+
+j
+ava.awt.Color
+--------------
+
+ColorSpace: only "support" CS_sRGB.
+
+ TODO -- any volunteers??
+
+javax.swing.JFileDialog
+-----------------------
+
+HTML5 cannot expose a file reading directory structure. But you certainly 
+can still do file reading and writing. It just works a little differently.
+It's a simple modification:
+
+               b = new JButton("FileOpenDialog");
+               b.addActionListener(new ActionListener() {
+
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               JFileChooser fc = new JFileChooser();
+                               Test_Dialog.this.onDialogReturn(fc.showOpenDialog(Test_Dialog.this));
+                               // Java will wait until the dialog is closed, then enter the onDialogReturn method.
+                               // JavaScript will exit with NaN immediately, and then call back with its actual value
+                               // asynchronously.
+                       }
+
+               });
+       
+               public void onDialogReturn(int value) {
+                       if (value != Math.floor(value))
+                               return; // in JavaScript, this will be NaN, indicating the dialog has been opened
+                       // If we are here, the dialog has closed, in both Java and JavaScript.
+                       System.out.println("int value is " + value);
+               }
+
+
+       @Override
+       public void propertyChange(PropertyChangeEvent event) {
+               Object val = event.getNewValue();
+               String name = event.getPropertyName();
+               System.out.println(name);
+               switch (event.getSource().getClass().getName()) {
+               case "javax.swing.JOptionPane":
+                       switch (name) {
+                       case "inputValue":
+                               onDialogReturn(val);
+                               return;
+                       case "value":
+                               if (val instanceof Integer)
+                                       onDialogReturn(((Integer) val).intValue());
+                               else
+                                       onDialogReturn(val);
+                               return;
+                       }
+                       break;
+               case "javax.swing.ColorChooserDialog":
+                       switch (name) {
+                       case "SelectedColor":
+                               onDialogReturn(val);
+                               return;
+                       }
+                       break;
+               case "javax.swing.JFileChooser":
+                       switch (name) {
+                       case "SelectedFile":
+                               File file = (File) val;
+                               byte[] array = (val == null ? null : /** @j2sNative file.秘bytes || */
+                                               null);
+                               onDialogReturn("fileName is '" + file.getName() + "'\n\n" + new String(array));
+                               return;
+                       }
+                       break;
+               }
+               System.out.println(
+                               event.getSource().getClass().getName() + " " + event.getPropertyName() + ": " + event.getNewValue());
+       }
+
+
+Developers are encouraged to create a separate class that handles general calls to JFileDialog. 
+An example class can be found in the SwingJS distribution as 
+
+/sources/net.sf.j2s.java.core/src/javajs/async/AsyncFileChooser.java.
+
+
+javax.swing.JOptionPane dialogs
+-------------------------------
+
+For this action to work, the parentComponent must implement
+propertyChangeListener, and any call to JOptionPanel should allow for
+an asynchronous response, meaning that there is no actionable code following the
+call to the dialog opening. 
+
+In addition, for compatibility with the Java version, implementation should
+wrap the call to getConfirmDialog or getOptionDialog in a method call to
+handle the Java:
+
+onDialogReturn(JOptionPane.showConfirmDialog(parentFrame,
+messageOrMessagePanel, "title", JOptionPane.OK_CANCEL_OPTION));
+
+Then parentFrame.propertyChange(event) should also call onDialogReturn.
+
+This will then work in both Java and JavaScript.
+
+Note that there is an int and an Object version of onDialogReturn().
+
+
+In JavaScript:
+
+The initial return from JOptionPane.showConfirmDialog and showMessageDialog
+will be (SwingJS) JDialog.ASYNCHRONOUS_INTEGER (NaN), testable as an impossible 
+Java int value using ret != -(-ret) if the parent implements PropertyChangeListener, or -1
+(CLOSE_OPTION) if not.
+
+For showOptionDialog (which returns Object) or showInputDialog (which returns
+String), the initial return will be (SwingJS) JDialog.ASYNCHRONOUS_OBJECT, testable as
+((Object) ret) instanceof javax.swing.plaf.UIResource if the parent implements
+PropertyChangeListeneer, or null if not.
+
+The second return will be the desired return.
+
+In Java:
+
+The initial return will be the one and only modal final return.
+
+
+
+For full compatibility, The calling method must not continue beyond this
+call.
+
+All of the standard Java events associated with Components are also
+available.
+
+Certain fall back mechanisms are possible, where onReturn does not exist, but
+only for the following cases:
+
+
+For showMessageDialog, for WARNING_MESSAGE and ERROR_MESSAGE, a simple
+JavaScript alert() is used, returning 0 (OK_OPTION) or -1 (CLOSED_OPTION).
+
+For showInputDialog, if the message is a string, a simple JavaScript prompt()
+with input box is used, returning the entered string or null.
+
+For showConfirmDialog, a simple JavaScript confirm() is used, in which case:
+
+for YES_NO_OPTION: YES_OPTION or NO_OPTION
+
+for YES_NO_CANCEL_OPTION: YES_OPTION or CANCEL_OPTION
+
+for OK_CANCEL_OPTION or any other: OK_OPTION or CANCEL_OPTION
+
+Note that you should implement a response for CLOSED_OPTION for
+showConfirmDialog. For other dialogs, a null return indicates the dialog was
+closed, just as for Java.
+
+Developers are encouraged to create a separate class that handles general calls. 
+An example class can be found in the SwingJS distribution as src/javajs/async/AsyncDialog.java.
+Very simple modifications to the Java allows asynchronous operation using AsyncDialog. Here
+is a simple "do you want to close this frame" example, where you can see that what we have
+done is to set the reply into an ActionListener that is defined in the constructor of 
+the AsyncDisplay object:
+
+// Original:
+//
+//     private void promptQuit() {
+//             int sel = JOptionPane.showConfirmDialog(null, PROMPT_EXIT, NAME, JOptionPane.YES_NO_OPTION);
+//             switch (sel) {
+//             case JOptionPane.YES_OPTION:
+//                     resultsTab.clean();
+//                     seqs.dispose();
+//                     if (fromMain) {
+//                             System.exit(0);
+//                     }
+//                     break;
+//             }
+//     }
+
+       private void promptQuitAsync() {
+               new AsyncDialog(new ActionListener() {
+
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                           int sel = ((AsyncDialog)e.getSource()).getOption();
+                               switch (sel) {
+                               case JOptionPane.YES_OPTION:
+                                       resultsTab.clean();
+                                       seqs.dispose();
+                                       if (fromMain) {
+                                               System.exit(0);
+                                       }
+                                       break;
+                               }
+                       }}).showConfirmDialog(null, PROMPT_EXIT, NAME, JOptionPane.YES_NO_OPTION);
+       }
+
+Very simple! 
+
+
+native methods
+--------------
+
+The J2S compiler ignores all static native method declarations.
+Anything of this nature needs to be implemented in JavaScript if it is needed,
+using j2sNative blocks:
+
+/**
+ * @j2sNative
+ *
+ *    var putYourJavaScriptCodeHere
+ *
+ */
+ Note that if you follow that directly with a {...} block, then 
+ the javadoc code will run in JavaScript, and the {...} code will run in Java.
+key Focus
+---------
+
+As of June, 2019, the keyboard focus manager is fully implemented. 
+The one catch is that JTextPane and JTextArea, which already consume
+VK_TAB in Java, cannot use CTRL-TAB to continue a tabbing cycle around
+the components in a window. Instead, CTRL-TAB is absorbed by the browser. 
+
+
+LookAndFeel and UI Classes
+--------------------------
+
+SwingJS implements the native browser look and feel as swingjs.plaf.HTML5LookAndFeel. 
+There are small differences between all look and feels -- MacOS, Windows, SwingJS.
+
+Expert developers know how to coerce changes in the UI by subclassing the UI for a 
+component. This probably will not work in SwingJS. 
+
+Note that LookAndFeel in Java usually determines canvas size in a Frame because 
+different operating systems (Mac OS vs Windows vs HTML5) will have 
+different edge sizes on their frames. If you want to ensure a component size, 
+use getContentPane().setPreferredSize().
+
+
+System.exit(0) does not stop all processes
+------------------------------------------
+
+Although System.ext(int) has been implemented in JavaScript, it just closes the 
+frames, stops all pending javax.swing.Timer objects in the queue, and runs any 
+threads added using Runtime.getRuntime().addShutdownHook(Thread).
+It may not stop all "threads." So don't rely on that.
+Applications are responsible for shutting down prior to executing System.exit(0). 
+
+
+myClass.getField not implemented
+--------------------------------
+
+java.lang.reflect.Field is implemented minimally. It is not
+certain that Field.getDeclaringClass() will work. If you just want a 
+value of a field, you can do this:
+
+/**
+ *@j2sNative
+ *
+ * return myClass[name]
+ */   
+
+But that is not a java.lang.reflection.Field object.
+
+
+"window" and other reserved JavaScript names
+--------------------------------------------
+
+No reserved top-level JavaScript name is allowed for a package name. So, for example, 
+one must rename packages such as "window" or "document" to names such as "win" or "doc".
+
+reserved field and method names
+-------------------------------
+
+In order to minimize the chance of added SwingJS field and method names colliding with ones 
+developers might use in subclassing Java classes, we have added U+79D8 (first character of Mandarin 
+"secret") to the characters already disrecommended by Java documentation ("$" and "_"). The only problem
+would be if you use that character followed by certain English words in certain classes. For example
+\u79D8canvas for JComponents (in java.awt.JSComponent) and \u79D8byte (in java.io.File).
+
+qualified field and method names
+--------------------------------
+
+Method names in SwingJS are fully qualified, meaning two methods with the same Java name but different
+parameters, such as write(int) and write(double), must not have the same name in JavaScript. (In this
+case, we will have write$I and write$D.) However, in certain cases it may be desirable to leave the
+method names unqualified. In particular, when an interface actually represents a JavaScript object, 
+the transpiler can leave a method name unqualified. The default situation for this is a class name 
+includes ".api.js" (case-sensitive). This means that any method in any class in a package js within 
+a package api, or any private interface js that has an outer interface api, will have all-unqualified
+methods. An example of this is swingjs.plaf.JSComboPopupList, which needs to communicate with a jQuery 
+object directly using the following interface:
+
+       private interface api {
+
+               interface js extends JQueryObject {
+
+                       abstract js j2sCB(Object options);
+
+                       abstract Object[] j2sCB(String method);
+
+                       abstract Object[] j2sCB(String method, Object o);
+
+                       abstract Object[] j2sCB(String method, int i);
+
+                       abstract int j2sCB(String OPTION, String name);
+
+               }
+       }
+
+Notice that all these variants of j2sCB() will call the same method in JavaScript by design.
+
+
+missing Math methods
+--------------------
+
+java.lang.Math is worked out, but some methods are missing, either because they
+involve long integer value that are inaccessible in JavaScript, or because I just
+didn't implement them. This is a result of continued Java development. 
+It is easy enough to add these methods if you have the source. They go into j2sClazz.js, 
+which is combined with other initial libraries into swingjs2.js by build_site.xml
+
+
+Component.getGraphics(), Graphics.dispose()
+-------------------------------------------
+
+Use of component.getGraphics() is discouraged in Java and in SwingJS. 
+Specifically in SwingJS, any call to component.getGraphics() or 
+BufferedImage.createGraphics() or Graphics.create(...) should be matched with graphics.dispose(), 
+particularly when it is called outside the context of a paint(Graphics)
+call from the system. 
+
+If you see your graphics scrolling down the page with each repaint, 
+look for where you have used Component.getGraphics() and not Graphics.dispose().
+For example, this will definitely NOT work in SwingJS:
+
+  this.paint(getGraphics())
+  
+and really should not work in Java, either, as it is technically a resource memory leak.
+
+Instead, if you really do not want to use repaint(), use this:
+
+  Graphics g = getGraphics();
+  paint(g);
+  g.dispose();
+
+
+
+Graphics.setClip()
+------------------
+
+The HTML5 canvas.clip() method is permanent. You can only reset the clip using
+save/restore. This is different from Java, where you can temporarily change it using
+
+  Shape oldClip = Graphics.getClip();
+  Graphics.setClip(newClip);
+   ...
+  Graphics.setClip(oldClip); 
+
+If you need to do something like this, you must schedule the paints
+to not have overlapping clip needs.
+
+
+MAJOR ISSUES--for Bob and Udo within SwingJS
+============================================
+
+fonts
+-----
+
+Fonts and FontMetrics will all be handled in JavaScript. Font matching will 
+not be exact, and composite (drawn) fonts will not be supported. 
+
+SwingJS handles calls such as font.getFontMetrics(g).stringWidth("xxx") by 
+creating a <div> containing that text, placing it in an obscure location on 
+the page, and reading div.getBoundingClientRect(). This is a VERY precise
+value, but can be a pixel or two off from what Java reports for the same font.
+OS-dependent classes
+--------------------
+
+Static classes such as:
+
+   java.awt.Toolkit
+   java.awt.GraphicsEnvironment
+   
+   
+which are created using Class.forName are implemented using classes in the swingjs package.
+
+AWTAccessor is not implemented. 
+
+   
+AWT component peers and component "ui" user interfaces
+------------------------------------------------------
+
+ComponentPeer is a class that represents a native AWT component.
+Components with such peers are called "heavy-weight" components.
+They are expected to do the dirty work of graphics drawing. 
+
+Java Swing implements peers only for JApplet, JDialog, JFrame, and JWindow. 
+References to such objects have been removed, but clearly there must be 
+some connection to similar DOM objects, even for "light-weight" components. 
+
+
+  
+MAJOR ISSUES--to be resolved by implementers
+============================================
+
+fonts
+-----
+
+Glyph/composite/outline fonts are not supported.
+   
+
+
+threads
+-------
+
+Thread locking and synchronization are not relevant to JavaScript.
+Thus, anything requiring "notify.." or "waitFor.." could be a serious issue.
+All threading must be "faked" in JavaScript. Specifically not available is:
+
+  Thread.sleep()
+  
+javax.swing.AbstractButton#doClick(pressTime) will not work, as it requires Thread.sleep();
+    
+However, java.lang.Thread itself is implemented and used extensively. 
+
+Methods thread.start() and thread.run() both work fine. 
+
+For simple applications that use Thread.sleep() just to have a delay, as in a frame rate, for 
+example, one can use javax.swing.Timer instead. That is fully implemented. 
+
+Likewise, java.util.Timer can be replaced with no loss of performance with javax.Swing.Timer.
+Note that java.util.TimerTask is implemented, but it can also be replaced by an implementation of Runnable.
+
+task = new TimerTask(){....};
+t = new java.util.Timer();
+t.schedule(task, 0, 1);
+
+becomes
+
+task = new TimerTask(){....}; // or task = new Runnable() {...}
+t = new javax.swing.Timer(1, new ActionListener() {
+       @Override
+       public void actionPerformed(ActionEvent e) {
+               task.run();
+       }
+};
+t.setInitialDelay(0); // not particularly necessary
+t.start();
+
+In addition, SwingJS provides swingjs.JSThread, which can be subclassed
+if desired. This class allows simple 
+
+  while(!interrupted()){
+       wait()
+       ...
+  }  
+
+action through an asynchronous function run1(mode). For example:
+
+       protected void run1(int mode) {
+               try {
+                       while (true)
+                               switch (mode) {
+                               case INIT:
+                                       // once-through stuff here
+                                       mode = LOOP;
+                                       break;
+                               case LOOP:
+                                       if (!doDispatch || isInterrupted()) {
+                                               mode = DONE;
+                                       } else {
+                                               Runnable r = new Runnable() {
+                                                       public void run() {
+                                                               // put the loop code here
+                                                       }
+                                               };
+                                               dispatchAndReturn(r);
+                                               if (isJS)
+                                                       return;
+                                       }
+                                       break;
+                               // add more cases as needed
+                               case DONE:
+                                       // finish up here
+                                       if (isInterrupted())
+                                               return;
+                                       // or here
+                                       break;
+                               }
+               } finally {
+                       // stuff here to be executed after each loop in JS or at the end in Java
+               }
+       }
+
+image loading
+-------------
+- All image loading in SwingJS is synchronous. A MediaTracker call will immediately return "complete".
+  However, it still may take one system clock tick to fully load images. Thus, it is recommended that
+  images be preloaded in the static block of the applet if it is necessary that they be available in init().
+  This is only an issue if you are trying to access the pixel buffer of the image in JavaScript. 
+  
+- Applet.getImage(path, name) will return null if the image does not exist. 
+
+- BufferedImage: only "support" imageType RGB and ARGB
+
+  -BH: This is a temporary edit, just to get us started. Certainly GRAY will be needed
+
+
+BigInteger and BigDecimal
+-------------------------
+
+java.math.BigInteger is fully supported; java.math.BigDecimal is roughed 
+in and not fully tested (07/2019). 
+
+Both classes present significant issues for JavaScript, as they are based in 
+Java's 64-bit long for all their operations. Here is the JavaDoc note I added
+to BigInteger:
+
+ * SwingJS note: Because of the limitations of JavaScript with regard
+ * to long-integer bit storage as a double, this implementation drops
+ * the integer storage bit length to 24, giving 48 for long and leaving
+ * the last 16 bits clear for the exponent of the double number. This should
+ * not affect performance significantly. It does increase the storage 
+ * size by about 33%. By bringing an "int" to 3 bytes, we can easily construct
+ * and use byte[] data intended for the original BitSet.  
+
+"Easily" may be a bit strong there. This was a serious challenge.
+
+BigDecimal seems to run normally, but in order to do that, my hack involves
+reducing the size of an integer that is allowed to be stored as such and not
+in byte[] as a BigInteger. I'm sure there is a performance hit, but it does work.
+
+no format internationalization
+------------------------------
+
+For now, just en for number and date formatters
+
+no winding rules
+----------------
+
+  When filling a graphic, only nonzero winding rule is implemented in HTML5 Canvas2D.
+
+
+
+text-related field implementation
+---------------------------------
+
+Text fields are:
+
+JTextField   (JavaScript <input type="text">)
+JTextArea    (JavaScript <textarea>)
+JTextPane    (JavaScript <div>)
+JEditorPane  (JavaScript <div>)
+
+For the initial implementation, we don't implement infinite undo/redo, and the abstract 
+document model is much less elaborate. Only PlainDocument (in the form of JSPlainDocument)
+is implemented. The Document returned by JTextField.getDocument() is a javax.swing.text.Document.
+
+All scrolling is handled by HTML5. javax.swing.AutoScroller is not implemented.
+public static methods .stop, .isRunning, .processMouseDragged require true Java threading
+and so are not implmented. javax.swing.text.View and its subclasses are not implemented. 
+
+The JS document model does not allow two text fields to address the same underlying document. 
+
+JavaScript is slightly different from Java in that the field value is changed asynchronously after
+the keypressed event, so Java actions that are keyed to KEY_PRESSED may not pick up the new 
+key value even after SwingUtilities.invokeLater() is called. Thus, key pressed actions may need
+to be recorded after a key released event instead. 
+
+Formatter/Regex limitations
+---------------------------
+
+Some browsers cannot process Regex "look-behind" process such as (?<=\W)
+java.util.regex.Matcher and Pattern use JavaScript's RegExp object rather than
+the native Java object. These are not identical. Only flags /igm are supported.
+Matcher.start(groupID) is not supported.
+
+java.util.Formatter will function correctly for all standard %... patterns.
+
+integer 1/0 == Infinity
+-----------------------
+
+1/0 in Java throws "java.lang.ArithmeticException: / by zero", but in JavaScript is just Infinity. 
+
+
+Summary
+-------
+
+These are all the known limitations of SwingJS. We have not found any of these limitations
+to be show-stoppers. The primary issue for newcomers to SwingJS is having the source code.
+You must check that source code for all your library jar files is available or, if you
+choose, you will need to decompile those classes. We have used decompilation on some projects,
+and it works just fine. So, technically, all we really need are JAR/class files. But the 
+source is by far superior. It's generally prettier, and it has the license information that
+may or may not be present with the JAR or class files. Use class files at your own risk.
+
+Bob Hanson
+2019.08.16
+
+
+Additional Issues
+-----------------
+
+Annotation is working for classes, methods, and fields (12/22/19). Method reflection, however,
+is limited. Interfaces do not expose their methods, as the transpiler does not actually transpile
+the interfaces themselves. And method reflection only includes annotated methods.
+
+java.util.concurrent is not fully elaborated. This package is rewritten to not actually use the
+memory handling capabilities of concurrency, which JavaScript does not have access to.
+
+System.getProperties() just returns a minimal set of properties.
+
+
diff --git a/swingjs/ver/3.2.9-j11/net.sf.j2s.core-j11.jar b/swingjs/ver/3.2.9-j11/net.sf.j2s.core-j11.jar
new file mode 100644 (file)
index 0000000..8cae046
Binary files /dev/null and b/swingjs/ver/3.2.9-j11/net.sf.j2s.core-j11.jar differ
index f22d3d2..26693ec 100644 (file)
Binary files a/swingjs/ver/3.2.9/SwingJS-site.zip and b/swingjs/ver/3.2.9/SwingJS-site.zip differ
diff --git a/swingjs/ver/3.2.9/differences.txt b/swingjs/ver/3.2.9/differences.txt
new file mode 100644 (file)
index 0000000..46e49ec
--- /dev/null
@@ -0,0 +1,1484 @@
+Notes
+=====
+
+---IMPORTANT CHARACTER SET NOTE---
+
+It is critical that all development work in Java2Script 
+be done in UTF-8. This means:
+
+- making sure your Eclipse project is set up for UTF-8 (not the Eclipse default?)
+- making sure your server can serve up UTF-8 by default for any browser-loaded files
+- making sure you don't edit a Java2Script class file or one of the site .js files
+    using a non-UTF-8 editor. It may replace non-Latin characters with "?" or garbage.
+- making sure that your web pages are delivered with proper headings indicating HTML5 and UTF-8
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+
+Note that the DOCTYPE tag is critical for some browsers to switch into HTML5 mode. (MSIE?)
+
+
+
+  
+In particular, the Mandarin character 秘 (mi; "secret") is used extensively throughout
+the SwingJS class files to distinguish j2s-specific fields and methods that must not 
+ever be shadowed or overridden by subclasses. For example, we see in java.lang.Thread.java:
+
+               public static JSThread 秘thisThread;
+
+----------------------------------
+
+
+updated 3/21/2020 -- adds note about HashMap, Hashtable, and HashSet iterator ordering
+updated 3/20/2020 -- adds note about interning, new String("xxx"), and "xxx"
+updated 2/26/2020 -- adds Graphics.setClip issue
+updated 12/22/19 -- additional issues
+updated 11/03/19 -- adds information about File.exists() and points to src/javajs/async
+updated 10/26/19 -- adds information about File.createTempFile()
+updated 8/16/19 -- minor typos and added summary paragraph
+updated 7/19/19 -- clarification that AWT and Swing classes are supported directly
+updated 5/13/19 -- Mandarin U+79D8 reserved character; Missing Math methods; int and long
+updated 5/10/19 -- adds a section on static issues in multi-(duplicate)-applet pages
+updated 1/4/19 -- nio
+updated 9/15/18 -- adds integer 1/0 == Infinity
+updated 7/24/18 -- most classes replaced with https://github.com/frohoff/jdk8u-jdk
+updated 6/5/17 -- reserved package name "window"
+updated 3/11/17 -- myClass.getField
+updated 3/7/17 -- overloading of JSplitPane.setDividerLocation
+updated 3/2/17 -- more indication of classes not implemented (KeyListener)
+
+=============================================================================
+SwingJS and OpenJDK 8+
+=============================================================================
+
+SwingJS implements a wide range of the Java language in JavaScript. The base
+version for this implementation is OpenJDK8. some classes are implemented using 
+older source code, and there are some missing methods. For the most part, this is 
+no real problem. You can add or modify any java class just be adding it as source
+in your project. Or (preferably) you can contact me, and I can get it into the 
+distribution. Or (even more preferably) you can do that via a patch submission. 
+
+=================
+DESIGN PHILOSOPHY
+=================
+
+The java2script/SwingJS design goal is to recreate a recognizable, easily debuggable
+equivalent in JavaScript for as much of Java as practical. This means, for example, 
+that one can call in JavaScript 
+
+  new java.util.Hashtable()
+  
+and for all practical purposes it will appear that Java is running.
+
+
+Method and Field Disambiguation
+-------------------------------
+
+SwingJS has no problem with the overloading of methods, for example:
+
+  public void print(int b);
+  public void print(float b);
+
+JavaScript does not allow overloading of methods, and the common practice in
+Java of naming a field the same as a method -- isAllowed and isAllowed() -- is
+not possible in JavaScript. As a result, SwingJS implements "fully-qualified" 
+method names using "$" parameter type separation. Thus, these methods in SwingJS
+will be referred to as print$I and print$F. The rules for this encoding are
+relatively simple: 
+
+1. The seven primitive types in Java are encoded $I (int), $L (long), $F (float), 
+$D (double), $B (byte) $Z (boolean), and $H (short). 
+
+2. String and Object are encoded as $S and $O, respectively.
+
+3. "java_lang_" is dropped for all other classes in the java.lang package (as in Java).
+   For example:  $StringBuffer, not $java_lang_StringBuffer
+
+4. All other classes are encoded as 
+
+ "$" + Class.getName().replace(".","_")
+
+For example, in Java we see:
+
+  public void equals(Object o) {...}
+
+Whereas in SwingJS we have:
+
+  Clazz.newMeth(C$, 'equals$O', function (o) {...}
+
+And 
+
+ this.getContentPane().add(bar, "North");
+
+becomes
+
+ this.getContentPane$().add$java_awt_Component$O(bar, "North");
+
+5. Arrays are indicated with appended "A" for each level. So
+
+  setDataVector(Object[][] dataVector, Object[] columnIdentifiers)
+  
+becomes
+
+  setDataVector$OAA$OA(dataVector, columnIdentifiers)
+
+(It is recognized that this design does introduce a bit of ambiguity, in that
+ in principal there could be user class named XA and X in the same package,
+ and methods a(X[]) and a(XA) in the same class that cannot be distinguished.
+ The benefit of this simple system, however, triumphed over the unlikelyhood
+ of that scenario.) The transpiler could be set to flag this possibility.
+
+6. Constructors are prepended with "c$". So 
+
+  public JLabel(String text) {...}
+  
+becomes:
+
+  Clazz.newMeth(C$, 'c$$S', function (text) {...});
+
+Field disambiguation involves prepending. In Java, a class and its subclass 
+can both have the same field name, such as 
+
+ boolean visible;
+When this happens, it is called "shadowing", and though not recommended, Java allows
+it. The Java2Script transpiler will prepend such shadowing fields with "$" so that the
+subclass instance has both "visible" (for use in its methods inherited from its
+superclass) and "$visible" (for its own methods). Thus, we might see in Java:
+
+  this.visible = super.visible;
+  
+while in SwingJS we will see:
+
+  this.$visible=this.visible;
+
+since JavaScript does not have the "super" keyword.
+
+
+
+Parameterless methods such as toString() are appended with "$" to become toString$().
+The one exception to this rule is private methods, which are saved in (truly) private 
+array in the class (and are not accessible by reflection). Private parameterless 
+methods retain their simple Java name, since they cannot conflict with field names.
+
+This renaming of methods has a few consequences, which are discussed more fully below.
+See particularly the section on "qualified field and method names", where it is described
+how you can use packages or classes or interfaces with ".api.js" in them to represent JavaScript
+objects for which all method names are to be left unqualified. Note that it is not 
+possible to cherry-pick methods to be unqualified; only full packages, classes or 
+interfaces can hold this status.
+
+The swingjs.api.js package in particular contains a number of useful interfaces that
+you can import into your project for JavaScript-specific capabilities.
+
+
+Applet vs. Application
+----------------------
+
+One of the very cool aspects of SwingJS is that it doesn't particularly matter if a browser-based
+Java app is an "applet" or an "application". We don't need JNLP (Java Network Launch Protocol) 
+because now we can just start up any Java application in a browser just as easily as any applet.
+The associative array that passes information to the SwingJS applet (information that formerly
+might have been part of the APPLET tag, such as width, height, and codebase, always referred to 
+in our writing as "the Info array") allows the option to specify the JApplet/Applet "code" 
+class or the application "main" class. Either one will run just fine.
+
+
+Performance
+-----------
+
+Obviously, there are limitations. One is performance, but we have seen reproducible 
+performance at 1/6 - 1/3 the speed of Java. Achieving this performance may require
+some refactoring of the Java to make it more efficient in both Java and JavaScript. 
+"for" loops need to be more carefully crafted; use of "new" and "instanceof" need to be
+minimized in critical areas. Note that method overloading -- that is, the same method name
+with different parameters, such as read(int) and read(byte) -- is no longer any problem. 
+  
+
+Threads
+-------
+
+Although there is only a single thread in JavaScript, meaning Thread.wait(), Thread.sleep(int) and 
+Thread.notify() cannot be reproduced, we have found that this is not a serious limitation. 
+For example, javax.swing.Timer() works perfectly in JavaScript. All it means is that threads 
+that use sleep(int) or notify() must be refactored to allow Timer-like callbacks. That is, 
+they must allow full exit and re-entry of Thread.run(), not the typical while/sleep motif. 
+
+The key is to create a state-based run() that can be exited and re-entered in JavaScript.
+
+
+Static fields
+-------------
+
+Final static primitive "constant" fields (String, boolean, int, etc.) such as 
+
+static final int TEST = 3;
+static final String MY_STRING = "my " + "string";
+
+are converted to their primitive form automatically by the Eclipse Java compiler 
+and do not appear in the JavaScript by their names. 
+
+Other static fields are properties of their class and can be used as expected.
+
+Note, however, that SwingJS runs all "Java" code on a page in a common "jvm" 
+(like older versions of Java). So, like the older Java schema, the JavaScript 
+equivalents of both applets and applications will share all of their static 
+fields and methods. This includes java.lang.System. 
+
+Basically, SwingJS implementations of Java run in a browser page-based sandbox 
+instead of an applet-specific one.
+
+In general, this is no problem. But if we are to implement pages with 
+multiple applets present, we must be sure to only have static references 
+that are "final" or specifically meant to be shared in a JavaScript 
+environment only (since they will not be shared in Java).
+
+A simple solution, if static non-constant references are needed, is to attach the 
+field to Thread.currentThread.threadGroup(), which is an applet-specific reference.
+Be sure, if you do this, that you use explicit setters and getters:
+
+For example, 
+
+private static String myvar;
+
+...
+
+public void setMyVar(String x) {
+  ThreadGroup g = Thread.currentThread().threadGroup();
+  /**
+   * @j2sNative g._myvar = x;
+   * 
+   */
+   {
+     myvar = x;
+   }
+}
+
+public String getMyVar() {
+  ThreadGroup g = Thread.currentThread().threadGroup();
+  /**
+   * @j2sNative return g._myvar || null;
+   * 
+   */
+   {
+     return myvar;
+   }
+}
+ in Java will get and set x the same in JavaScript and in Java. 
+A convenient way to do this in general is to supply a singleton class with
+explicitly private-only constructors and then refer to it in Java and in JavaScript
+instead of using static field, referring to myclass.getIntance().xxx instead of 
+myclass.xxx in Java (and JavaScript). 
+
+This was done extensively in the Jalview project. See jalview.bin.Instance.
+
+
+Helper Packages -- swingjs/ and javajs/
+---------------------------------------
+
+The SwingJS library is the swingjs/ package. There are interfaces that may be of assistance
+in swingjs/api, but other than that, it is not recommended that developers access classes in 
+this package. The "public" nature of their methods is really an internal necessity.
+
+In addition to swingjs/, though, there are several useful classes in the javajs/ package
+that could be very useful. This package is a stand-alone package that can be 
+cloned in any Java project that also would be great to have in any JavaScript project
+-- SwingJS-related or not. Functionality ranges from reading and writing various file 
+formats, including PDF, BMP, PNG, GIF, JPG, JSON, ZIP, and CompoundDocument formats.
+
+A variety of highly efficient three- and four-dimensional point, vector, matrix, and 
+quaternion classes are included, as they were developed for JSmol and inherited from that
+project. 
+
+Of particular interest should be javajs/async/, which includes
+
+javajs.async.Async
+javajs.async.AsyncColorChooser
+javajs.async.AsyncDialog
+javajs.async.AsyncFileChooser
+
+See javajs.async.Async JavaDoc comments for a full description of 
+these useful classes.
+
+
+Modal Dialogs
+-------------
+
+Although true modal dialogs are not possible with only one thread, a functional equivalent -- 
+asynchronous modal dialogs -- is relatively easy to set up. All the JOptionPane dialogs will
+return PropertyChangeEvents to signal that they have been disposed of and containing the results. 
+See below and classes in the javajs.async package.
+
+
+Native calls
+------------
+
+Native calls in Java are calls to operating system methods that are not in Java. JavaScript
+has no access to these, of course, and they must all be replaced by JavaScript equivalents.
+Fortunately, they are not common, and those that are present in Java (for example, in calculating
+checksums in ZIP file creation) are at a low enough level that most developers do not utilize them
+or do not even have access to them. All native calls in Java classes have been replaced by 
+Java equivalents.
+
+
+Swing GUI Peers and UIClasses
+-----------------------------
+
+One of the biggest adaptations introduced in SwingJS is in the area of the graphical 
+user interface. The issue here is complex but workable. In Java there are two background 
+concepts -- the Component "peer" (one per "heavy-weight" component, such as a Frame) and the 
+component "uiClass" (one per component, such as JButton or JTextField).
+
+Peers are native objects of the operating system. These are the virtual buttons and text areas
+that the user is interacting with at a very base level. Their events are being passed on to 
+Java or the browser by the operating system. UI classes provide a consistent "look and feel" 
+for these native objects, rendering them onto the native window canvas and handling all 
+user-generated events. They paint the borders, the backgrounds, the highlights, of every 
+control you see in Java. There is one-to-one correspondence of Swing classes and UI classes. 
+Setting the Look and Feel for a project amounts to selecting the directory from which to draw 
+these UI classes. The UI classes can be found in the javax.swing.plaf ("platform look and feel") 
+package.
+
+Early on in the development of SwingJS, we decided not to fully reproduce the painfully detailed 
+bit-by-bit painting of controls as is done in Java. Instead, we felt it was wiser to utilize the standard
+HTML5 UI capabilities as much as possible, using DIV, and INPUT especially, with extensive use
+of CSS and sometimes jQuery (menus, and sliders, for example). Thus, we have created a new 
+set of UIs -- the "HTML5 Look and Feel". These classes can be found in swingjs.plaf. Besides being
+more adaptable, this approach allows far more versatility to SwingJS developers, allowing them
+to modify the GUI to suit their needs if desired.
+
+In SwingJS, since we have no access to native peers except through the browser DOM,
+it seemed logical to merge the peer and UI idea. So instead of having one peer per heavy-weight control and
+one UI class instance for each control type, we just have one UI class instance per control, and
+that UI class instance is what is being referred to when a "peer" is notified. 
+
+In some ways this is a throw back to when all of Swing's components were subclasses of
+specific AWT components such as Button and List. These "heavy-weight components" all had their 
+own individual native peers and thus automatically took on the look and feel provided by the OS. 
+Later Swing versions implemented full look and feel for all peers, leaving only JDialog, JFrame,
+and a few other classes to have native peers. But in SwingJS we have again a 1:1 map of component
+and UI class/peer instance.
+
+The origin of most issues (read "bugs") in relation to the GUI will probably be found in the
+swingjs.plaf JSxxxxUI.java code.
+
+  
+Swing-only Components -- no longer an issue
+-------------------------------------------
+
+Swing was introduced into Java well after the Java Abstract Window Toolkit (AWT) was well
+established. As such, its designers chose to allow AWT controls such as Button and List to be used 
+alongside their Swing counterparts JButton and JList. Reading the code, it is clear that this 
+design choice posed a huge headache for Swing class developers. 
+
+For SwingJS, we decided from the beginning NOT to allow this mixed-mode programming and 
+instead to require that all components be Swing components. 
+
+However, this is no longer an issue. All AWT components in SwingJS are now subclasses of 
+javax.swing.JComponent. So far, we have found no problem with this.
+
+The a2s Adapter Package
+-----------------------
+
+Originally, we thought that we would restrict ourselves to JApplets only. That is, only
+Swing-based applets. But as we worked, we discovered that there are a lot of great 
+applets out there that are pre-Swing pure-AWT java.applet.Applet applets. Our problem was 
+that we also wanted it to be possible to quickly adapt these applets to JavaScript as well.
+The solution turned out to be simple: Write a package (a2s) that recreates the interface for 
+non-Swing components as subclasses of Swing components. Thus, a2s.Button subclasses javax.swing.JButton
+but also accepts all of the methods of java.awt.Button. This works amazingly well, with a few
+special adaptations to the core javax.swing to be "AWT-aware." All AWT components now subclass 
+a2s components, which in turn subclass JComponents. So no changes in code are necessary. We have
+successfully transpiled over 500 applets using this strategy. (Kind of surprising, actually, that
+the original Java developers did not see that option. But we have a hindsight advantage here.)
+
+
+Working with Files
+==================
+
+Simple String file names are not optimal for passing information about
+read files within SwingJS applications. 
+All work with files should either use Path or File objects exclusively. 
+These objects, after a file is read or checked for existence, will already 
+contain the file byte[] data. Doing something like this:
+
+File f = File("./test.dat");
+boolean isOK = f.exists();
+
+will load f with its byte[] data, if the file exists. 
+
+But if after that, we use:
+
+File f2 = new File(f.getAbsolutePath());
+
+f2 will not contain that data. Such copying should be done as:
+
+File f2 = new File(f);
+
+in which case, the byte[] data will be transferred.
+
+
+SwingJS uses the following criteria to determine if File.exists() returns true:
+
+(1) if this File object has been used directly to read data, or 
+(2) if reading data using this File object is successful.
+
+Note that you cannot check to see if a file exists before input or if it 
+was actually written or if it already exists prior to writing in SwingJS.  
+
+Thus, you should check each use of file.exists() carefully, and if necessary, provide a J2sNative 
+block that gives an appropriate "OK" message, for example:
+
+(/** @j2sNative 1 ? false : */ outputfile.exits())
+
+or 
+
+(/** @j2sNative 1 ? true : */ inputfile.exits())
+
+Temporary files can be created in SwingJS. SwingJS will maintain a pseudo-filesystem for files 
+created with File.createTempFile(). This is useful in that closure of writing to a temporary file 
+does not generate a pseudo-download to the user's machine.
+
+
+UNIMPLEMENTED CLASSES BY DESIGN
+===============================
+
+The SwingJS implementation of the following classes are present 
+in a way that gracefully bypasses their functionality:
+
+accessibility
+security
+serialization
+
+
+
+TODO LIST FOR UNIMPLEMENTED CLASSES
+===================================
+
+JEditorPane (minimal implementation) - DONE 12/2018; some issues still
+JSplitPane - DONE 8/2018
+JTabbedPane - DONE 10/2018
+JTree - done 12/2019
+
+
+MINOR ISSUES--required some rewriting/refactoring by Bob and Udo  
+================================================================
+
+Thread.currentThread() == dispatchThread
+
+
+MINOR ISSUES--requiring some rewriting/refactoring outside of SwingJS  
+=====================================================================
+
+See below for a full discussion.
+
+HashMap, Hashtable, and HashSet iterator ordering
+interning, new String("xxx") vs "xxx"
+Names with "$" and "_"
+positive integers do not add to give negative numbers
+ArrayIndexOutOfBounds
+java.awt.Color
+native methods
+javax.swing.JFileDialog
+key focus
+LookAndFeel and UI Classes
+System.exit(0) does not stop all processes
+list cell renderers must be JComponents
+myClass.getField not implemented
+"window" and other reserved JavaScript names
+reserved field and method names
+qualified field and method names
+missing Math methods
+Component.getGraphics(), Graphics.dispose()
+Graphics.setClip()
+
+MAJOR ISSUES--for Bob and Udo within SwingJS
+============================================
+
+fonts
+OS-dependent classes
+AWT component peers
+some aspects of reflection
+
+MAJOR ISSUES--to be resolved by implementers
+============================================
+
+fonts
+threads
+modal dialogs
+image loading
+BigDecimal not fully implemented 
+no format internationalization
+no winding rules
+text-related field implementation
+Formatter/Regex limitations
+integer 1/0 == Infinity
+
+======================================================================== 
+
+DISCUSS
+=======
+
+Table row/col sorter needs checking after removal of java.text.Collator references
+
+I had to move all of SunHints class to RenderingHints, or the 
+two classes could not be loaded. Shouldn't be a problem, I think. The sun classes are
+not accessible to developers in Java anyway, since they are generally package private.
+
+========================================================================== 
+
+//////////////////////////////////////////////////////////////////////////////
+
+UNIMPLEMENTED CLASSES
+=====================
+
+accessibility
+-------------
+
+All Accessibility handling has been commented out to save the download footprint.
+This removes the need for sun.misc.SharedSecrets as well. 
+Nothing says we could not implement accessibility. We just didn't.
+
+
+security
+--------
+
+All JavaScript security is handled by the browser natively. 
+Thus, Java security checking is no longer necessary, and 
+java.security.AccessController has been simplified to work without
+native security checking.
+
+Note that private methods in a class are REALLY private. 
+
+
+serialization
+-------------
+
+All serialization has been removed. It was never very useful for Swing anyway, 
+because one needs exactly the same Java version to save and restore serialized objects.
+
+
+keyboard accelerators and mnemonics
+-----------------------------------
+
+This work was completed in the spring of 2019. Note that in a browser, some 
+key strokes, particularly CTRL-keys, are not available. Bummer.
+
+
+MINOR ISSUES--required some rewriting/refactoring by Bob and Udo  
+================================================================
+
+
+Thread.currentThread() == dispatchThread
+----------------------------------------
+
+changed to JSToolkit.isDispatchThread()
+
+
+MINOR ISSUES--requiring some rewriting/refactoring outside of SwingJS  
+=====================================================================
+
+HashMap, Hashtable, and HashSet iterator ordering
+-------------------------------------------------
+
+In Java, iterators for HashMap, Hashtable, and HashSet do not guarantee any particular order. 
+From the HashMap documentation for Java 8:
+
+       This class makes no guarantees as to the order of the map; in particular, it does not 
+       guarantee that the order will remain constant over time.
+Likewise, for HashSet (because it is simply a convenience method for HashMap<Object,PRESENT>:
+
+       [HashSet] makes no guarantees as to the iteration order of the set.
+
+JavaScript's Map object is different. It is basically a LinkedHashMap, so it guarantees iteration
+in order of object addition.
+
+Starting with java2script 3.2.9.v1, these classes use the JavaScript Map object rather than hash codes
+whenever all keys are strictly of JavaScript typeof "string". If any key is introduced that is not a string, the
+implementation falls back to using hash codes, the same as Java. 
+
+Note strings created using new String("xxxx") are NOT typeof "string"; they are typeof "object".
+
+The result is significantly faster performance (3-12 x faster) than originally, and up to 3 x faster
+performance in JavaScript than in Java itself. Right. Faster than Java. 
+
+The JavaScript Map implementation is implemented UNLESS the constructor used is the one that
+specifies both initial capacity and load factor in their constructor. Thus, 
+
+new Hashtable()
+new HashMap()
+new HashMap(16)
+new HashSet()
+
+all use the JavaScript Map. But
+
+new Hashtable(11, 0.75f)
+new HashMap(16, 0.75f)
+new HashSet(16, 0.75f)
+
+do not. 
+
+This design allows for opting out of the JavaScript Map use in order to retain the exact behavior of 
+iterators in JavaScript as in Java.
+
+
+interning, new String("xxx") vs "xxx"
+-------------------------------------
+
+Note that the following are true in JavaScript:
+
+typeof new String("xxxx") == "object"
+typeof "xxxx" == "string"
+var s = "x";typeof ("xxx" + s) == "string"
+
+There is no equivalence to this behavior in Java, where a String is a String is a String.
+
+Be aware that SwingJS does not always create a JavaScript String object using JavaScript's 
+new String(...) constructor. It only does this for Java new String("xxxx") or new String(new String()). 
+
+In all other cases, new String(...) (in Java) results in a simple "xxxx" string in JavaScript. 
+That is, it will be JavaScript typeof "string", not typeof "object". 
+
+The reason for this design is that several classes in the Java core use toString() 
+methods that return new String(), and those classes that do that would cause a JavaScript error
+if implicitly stringified if new String() returned a JavaScript String object. 
+
+This is fine in JavaScript
+
+test1 = function() { return { toString:function(){ return "OK" } } }
+"testing" + new test1()
+>> "testingOK"
+
+But for whatever reason in JavaScript:
+
+test2 = function() { return { toString:function(){ return new String("OK") } } }
+"testing" + new test2()
+>> Uncaught TypeError: Cannot convert object to primitive value
+
+The lesson here is never to use 
+
+  return new String("...");
+
+in a Java toString() method. In Java it will be fine; in JavaScript it will also be fine as long as
+that method is never called in JavaScript implicitly in the context of string concatenation.
+
+A note about interning. Consider the following six Java constructions, where we have a == "x";
+
+"xxx"
+"xx" + "x"
+new String("xxx").intern()
+
+new String("xxx")
+"xx" + a.toString()
+"xx" + a
+
+All six of these will return java.lang.String for .getClass().getName().
+However, the first three are String literals, while the last three are String objects. 
+Thus:
+        "xxx" == "xxx"
+        "xxx" == "xx" + "x"
+        "xxx" == new String("xxx").intern()
+
+but none of the other three are equivalent to "xxx" or each other:
+
+              "xxx" != new String("xxx")
+              "xxx" != "xx" + a.toString()
+              "xxx" != "xx" + a
+  new String("xxx") != new String("xxx") 
+           "xx" + a != new String("xxx") 
+
+etc.
+
+As in Java, in SwingJS, all of the following Java assertions pass as true:
+
+               assert("xxx" == "xx" + "x"); 
+               assert("xxx" == ("xx" + a).intern()); 
+               assert("xxx" === new String("xxx").intern()); 
+               
+and both of these do as well:
+
+               assert(new String("xxx") != "xxx"); 
+               assert(new String("xxx") != new String("xxx")); 
+
+But the following two fail to assert true:
+
+        assert("xxx" != "xx" + a);
+        assert("xxx" != "xx" + a.toString());
+
+because in JavaScript, both of these right-side expressions evaluate to a simple "interned" string.
+
+In Java, however, these assertions are true because Java implicitly "boxes" String 
+concatentaion as a String object, not a literal. 
+
+Most of us know not to generally use == with Strings unless they are explicitly interned. 
+Where this problem may arise, though, is in IdentityHashMap, which compares objects using 
+System.identityHashCode(), which is not the same for different objects or their string literal equivalents.
+
+My recommendation, if you need to use IdentityHashMap with strings is to always use an explicit String.intern()
+for any keys -- unless you really want to keep every string as separate keys even if they are the same sequence, 
+in which case, use new String(). This will work in Java and in  JavaScript.
+
+Be aware when working with strings that come from SwingJS and are being used by other JavaScript modules
+that those that are String objects will return "object" for the JavaScript typeof operator, not "string".
+
+The easy way to ensure this is no problem is to concatenate strings with "" to force immediate interning:
+
+  var x = aJavaObject.getString() + "";
+
+unless you are certain that the string is being returned is a raw JavaScript string.   
+
+Names with "$" and "_"
+----------------------
+
+For the most part, this should be no problem. 
+
+Note that the use of $ and _ in Java field names has always been discouraged:
+[https://docs.oracle.com/javase/tutorial/java/nutsandbolts/variables.html]
+
+       You may find some situations where auto-generated names will contain the dollar sign, 
+       but your variable names should always avoid using it. A similar convention 
+       exists for the underscore character; while it's technically legal to begin your 
+       variable's name with "_", this practice is discouraged.
+
+Some impacts of transpiling method names with full qualification:
+
+1) SwingJS will introduce fields that start with $ or _. These will not conflict
+   if the above convention is followed.
+   
+2) Fields that have the same Java name as a method are not an issue. 
+
+3) Fields that have a Java name with $ that matches a transpiled method name, 
+   such as toString$, will need to be refactored in Java to not have that name collision.
+   
+4) Fields in a subclass that have the same name as private fields in a superclass
+   represent a name collision, because the superclass method needs to call its private
+   field even if invoked from a subclass. The solution was to modify the subclass field
+   name using one or more prepended $.
+   
+5) Use of Class.getDeclaredMethods() reflection will return Method objects having the transpiled 
+   name, not the Java name. This could require some j2sNative adjustment 
+   to strip the $... parameters from the name if that is needed. 
+
+6) Use of Method.getParameterTypes() should work fine, provided class names
+   do not contain "_". This is because the transpiler converts "." to "_" when
+   creating the fully qualified JavaScript name.
+
+
+positive integers do not add to give negative numbers
+-----------------------------------------------------
+
+In Java, the following is true:
+
+  2000000000 + 2000000000 == -294967296
+
+But in SwingJS, that will be 4000000000. So, for example, the following
+strategy will fail in SwingJS:
+
+               int newLength = lineBuf.length * 2;
+               if (newLength < 0) {
+                       newLength = Integer.MAX_VALUE;
+               }
+
+"-1" in JavaScript is not 0xFFFFFFFF.
+
+And one must take care to not compare a negative number with a 32-bit mask. So
+
+(b & 0xFF000000) == 0xFF000000
+
+is true in Java for (int) b = -1, but is false in JavaScript, because 0xFF000000 is 4278190080, 
+while (-1 & 0xFF000000) is, strangely enough, -16777216, and, in fact, 
+
+(0xFF000000 & 0xFF000000) != 0xFF000000
+
+because -16777216 is not 4278190080.
+
+The fix is that one must compare similar operations:
+
+if ((b & 0xFF000000) == (0xFF000000 & 0xFF000000)) .....
+
+Importantly, the JavaScript Int32Array does behave properly. From 
+the Firefox developer console:
+
+>> x = new Int32Array(1)
+<- Int32Array(1) [ 0 ]
+>> x[0] = 4000000000
+<- 4000000000
+>> x[0]
+<- -294967296
+
+Notice that, perhaps unexpectedly, the following two constructs produce 
+different results in JavaScript:
+
+x = new Int32Array(1);
+b = x[0] = 4000000000;
+
+(b will be 4000000000)
+
+and
+
+x = new Int32Array(1);
+x[0] = 4000000000;
+b = x[0];
+
+(b will be -294967296)
+
+
+SwingJS leverages array typing to handle all byte and short arithmetic so as
+to ensure that any byte or short operation in JavaScript does give the same 
+result in Java. The design decision to not also do this with integer math was
+a trade-off between performance and handling edge cases.
+
+
+ArrayIndexOutOfBounds
+---------------------
+
+You cannot implicitly throw an ArrayIndexOutOfBoundsException in JavaScript.
+JavaScript will simply return "undefined", not throw an Exception. So:
+
+boolean notAGoodIdeaIsOutOfBounds(String[] sa, int i) {
+  try {
+     return (sa[i] == sa[i]);
+  } catch (ArrayIndexOutOfBoundsException e) {
+       return false;
+  }
+}
+
+will work in Java but not in JavaScript. Code should not depend upon this sort 
+of trap anyway, if you ask me. 
+
+Throwable vs Error vs Exception
+-------------------------------
+
+True JavaScript errors are trapped as Throwable, whereas you can still trap
+Error and Exception as well. So if you want to be sure to catch any JavaScript
+error, use try{}catch (Throwable t){}, not try{}catch (Exception e){}. 
+
+j
+ava.awt.Color
+--------------
+
+ColorSpace: only "support" CS_sRGB.
+
+ TODO -- any volunteers??
+
+javax.swing.JFileDialog
+-----------------------
+
+HTML5 cannot expose a file reading directory structure. But you certainly 
+can still do file reading and writing. It just works a little differently.
+It's a simple modification:
+
+               b = new JButton("FileOpenDialog");
+               b.addActionListener(new ActionListener() {
+
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               JFileChooser fc = new JFileChooser();
+                               Test_Dialog.this.onDialogReturn(fc.showOpenDialog(Test_Dialog.this));
+                               // Java will wait until the dialog is closed, then enter the onDialogReturn method.
+                               // JavaScript will exit with NaN immediately, and then call back with its actual value
+                               // asynchronously.
+                       }
+
+               });
+       
+               public void onDialogReturn(int value) {
+                       if (value != Math.floor(value))
+                               return; // in JavaScript, this will be NaN, indicating the dialog has been opened
+                       // If we are here, the dialog has closed, in both Java and JavaScript.
+                       System.out.println("int value is " + value);
+               }
+
+
+       @Override
+       public void propertyChange(PropertyChangeEvent event) {
+               Object val = event.getNewValue();
+               String name = event.getPropertyName();
+               System.out.println(name);
+               switch (event.getSource().getClass().getName()) {
+               case "javax.swing.JOptionPane":
+                       switch (name) {
+                       case "inputValue":
+                               onDialogReturn(val);
+                               return;
+                       case "value":
+                               if (val instanceof Integer)
+                                       onDialogReturn(((Integer) val).intValue());
+                               else
+                                       onDialogReturn(val);
+                               return;
+                       }
+                       break;
+               case "javax.swing.ColorChooserDialog":
+                       switch (name) {
+                       case "SelectedColor":
+                               onDialogReturn(val);
+                               return;
+                       }
+                       break;
+               case "javax.swing.JFileChooser":
+                       switch (name) {
+                       case "SelectedFile":
+                               File file = (File) val;
+                               byte[] array = (val == null ? null : /** @j2sNative file.秘bytes || */
+                                               null);
+                               onDialogReturn("fileName is '" + file.getName() + "'\n\n" + new String(array));
+                               return;
+                       }
+                       break;
+               }
+               System.out.println(
+                               event.getSource().getClass().getName() + " " + event.getPropertyName() + ": " + event.getNewValue());
+       }
+
+
+Developers are encouraged to create a separate class that handles general calls to JFileDialog. 
+An example class can be found in the SwingJS distribution as 
+
+/sources/net.sf.j2s.java.core/src/javajs/async/AsyncFileChooser.java.
+
+
+javax.swing.JOptionPane dialogs
+-------------------------------
+
+For this action to work, the parentComponent must implement
+propertyChangeListener, and any call to JOptionPanel should allow for
+an asynchronous response, meaning that there is no actionable code following the
+call to the dialog opening. 
+
+In addition, for compatibility with the Java version, implementation should
+wrap the call to getConfirmDialog or getOptionDialog in a method call to
+handle the Java:
+
+onDialogReturn(JOptionPane.showConfirmDialog(parentFrame,
+messageOrMessagePanel, "title", JOptionPane.OK_CANCEL_OPTION));
+
+Then parentFrame.propertyChange(event) should also call onDialogReturn.
+
+This will then work in both Java and JavaScript.
+
+Note that there is an int and an Object version of onDialogReturn().
+
+
+In JavaScript:
+
+The initial return from JOptionPane.showConfirmDialog and showMessageDialog
+will be (SwingJS) JDialog.ASYNCHRONOUS_INTEGER (NaN), testable as an impossible 
+Java int value using ret != -(-ret) if the parent implements PropertyChangeListener, or -1
+(CLOSE_OPTION) if not.
+
+For showOptionDialog (which returns Object) or showInputDialog (which returns
+String), the initial return will be (SwingJS) JDialog.ASYNCHRONOUS_OBJECT, testable as
+((Object) ret) instanceof javax.swing.plaf.UIResource if the parent implements
+PropertyChangeListeneer, or null if not.
+
+The second return will be the desired return.
+
+In Java:
+
+The initial return will be the one and only modal final return.
+
+
+
+For full compatibility, The calling method must not continue beyond this
+call.
+
+All of the standard Java events associated with Components are also
+available.
+
+Certain fall back mechanisms are possible, where onReturn does not exist, but
+only for the following cases:
+
+
+For showMessageDialog, for WARNING_MESSAGE and ERROR_MESSAGE, a simple
+JavaScript alert() is used, returning 0 (OK_OPTION) or -1 (CLOSED_OPTION).
+
+For showInputDialog, if the message is a string, a simple JavaScript prompt()
+with input box is used, returning the entered string or null.
+
+For showConfirmDialog, a simple JavaScript confirm() is used, in which case:
+
+for YES_NO_OPTION: YES_OPTION or NO_OPTION
+
+for YES_NO_CANCEL_OPTION: YES_OPTION or CANCEL_OPTION
+
+for OK_CANCEL_OPTION or any other: OK_OPTION or CANCEL_OPTION
+
+Note that you should implement a response for CLOSED_OPTION for
+showConfirmDialog. For other dialogs, a null return indicates the dialog was
+closed, just as for Java.
+
+Developers are encouraged to create a separate class that handles general calls. 
+An example class can be found in the SwingJS distribution as src/javajs/async/AsyncDialog.java.
+Very simple modifications to the Java allows asynchronous operation using AsyncDialog. Here
+is a simple "do you want to close this frame" example, where you can see that what we have
+done is to set the reply into an ActionListener that is defined in the constructor of 
+the AsyncDisplay object:
+
+// Original:
+//
+//     private void promptQuit() {
+//             int sel = JOptionPane.showConfirmDialog(null, PROMPT_EXIT, NAME, JOptionPane.YES_NO_OPTION);
+//             switch (sel) {
+//             case JOptionPane.YES_OPTION:
+//                     resultsTab.clean();
+//                     seqs.dispose();
+//                     if (fromMain) {
+//                             System.exit(0);
+//                     }
+//                     break;
+//             }
+//     }
+
+       private void promptQuitAsync() {
+               new AsyncDialog(new ActionListener() {
+
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                           int sel = ((AsyncDialog)e.getSource()).getOption();
+                               switch (sel) {
+                               case JOptionPane.YES_OPTION:
+                                       resultsTab.clean();
+                                       seqs.dispose();
+                                       if (fromMain) {
+                                               System.exit(0);
+                                       }
+                                       break;
+                               }
+                       }}).showConfirmDialog(null, PROMPT_EXIT, NAME, JOptionPane.YES_NO_OPTION);
+       }
+
+Very simple! 
+
+
+native methods
+--------------
+
+The J2S compiler ignores all static native method declarations.
+Anything of this nature needs to be implemented in JavaScript if it is needed,
+using j2sNative blocks:
+
+/**
+ * @j2sNative
+ *
+ *    var putYourJavaScriptCodeHere
+ *
+ */
+ Note that if you follow that directly with a {...} block, then 
+ the javadoc code will run in JavaScript, and the {...} code will run in Java.
+key Focus
+---------
+
+As of June, 2019, the keyboard focus manager is fully implemented. 
+The one catch is that JTextPane and JTextArea, which already consume
+VK_TAB in Java, cannot use CTRL-TAB to continue a tabbing cycle around
+the components in a window. Instead, CTRL-TAB is absorbed by the browser. 
+
+
+LookAndFeel and UI Classes
+--------------------------
+
+SwingJS implements the native browser look and feel as swingjs.plaf.HTML5LookAndFeel. 
+There are small differences between all look and feels -- MacOS, Windows, SwingJS.
+
+Expert developers know how to coerce changes in the UI by subclassing the UI for a 
+component. This probably will not work in SwingJS. 
+
+Note that LookAndFeel in Java usually determines canvas size in a Frame because 
+different operating systems (Mac OS vs Windows vs HTML5) will have 
+different edge sizes on their frames. If you want to ensure a component size, 
+use getContentPane().setPreferredSize().
+
+
+System.exit(0) does not stop all processes
+------------------------------------------
+
+Although System.ext(int) has been implemented in JavaScript, it just closes the 
+frames, stops all pending javax.swing.Timer objects in the queue, and runs any 
+threads added using Runtime.getRuntime().addShutdownHook(Thread).
+It may not stop all "threads." So don't rely on that.
+Applications are responsible for shutting down prior to executing System.exit(0). 
+
+
+myClass.getField not implemented
+--------------------------------
+
+java.lang.reflect.Field is implemented minimally. It is not
+certain that Field.getDeclaringClass() will work. If you just want a 
+value of a field, you can do this:
+
+/**
+ *@j2sNative
+ *
+ * return myClass[name]
+ */   
+
+But that is not a java.lang.reflection.Field object.
+
+
+"window" and other reserved JavaScript names
+--------------------------------------------
+
+No reserved top-level JavaScript name is allowed for a package name. So, for example, 
+one must rename packages such as "window" or "document" to names such as "win" or "doc".
+
+reserved field and method names
+-------------------------------
+
+In order to minimize the chance of added SwingJS field and method names colliding with ones 
+developers might use in subclassing Java classes, we have added U+79D8 (first character of Mandarin 
+"secret") to the characters already disrecommended by Java documentation ("$" and "_"). The only problem
+would be if you use that character followed by certain English words in certain classes. For example
+\u79D8canvas for JComponents (in java.awt.JSComponent) and \u79D8byte (in java.io.File).
+
+qualified field and method names
+--------------------------------
+
+Method names in SwingJS are fully qualified, meaning two methods with the same Java name but different
+parameters, such as write(int) and write(double), must not have the same name in JavaScript. (In this
+case, we will have write$I and write$D.) However, in certain cases it may be desirable to leave the
+method names unqualified. In particular, when an interface actually represents a JavaScript object, 
+the transpiler can leave a method name unqualified. The default situation for this is a class name 
+includes ".api.js" (case-sensitive). This means that any method in any class in a package js within 
+a package api, or any private interface js that has an outer interface api, will have all-unqualified
+methods. An example of this is swingjs.plaf.JSComboPopupList, which needs to communicate with a jQuery 
+object directly using the following interface:
+
+       private interface api {
+
+               interface js extends JQueryObject {
+
+                       abstract js j2sCB(Object options);
+
+                       abstract Object[] j2sCB(String method);
+
+                       abstract Object[] j2sCB(String method, Object o);
+
+                       abstract Object[] j2sCB(String method, int i);
+
+                       abstract int j2sCB(String OPTION, String name);
+
+               }
+       }
+
+Notice that all these variants of j2sCB() will call the same method in JavaScript by design.
+
+
+missing Math methods
+--------------------
+
+java.lang.Math is worked out, but some methods are missing, either because they
+involve long integer value that are inaccessible in JavaScript, or because I just
+didn't implement them. This is a result of continued Java development. 
+It is easy enough to add these methods if you have the source. They go into j2sClazz.js, 
+which is combined with other initial libraries into swingjs2.js by build_site.xml
+
+
+Component.getGraphics(), Graphics.dispose()
+-------------------------------------------
+
+Use of component.getGraphics() is discouraged in Java and in SwingJS. 
+Specifically in SwingJS, any call to component.getGraphics() or 
+BufferedImage.createGraphics() or Graphics.create(...) should be matched with graphics.dispose(), 
+particularly when it is called outside the context of a paint(Graphics)
+call from the system. 
+
+If you see your graphics scrolling down the page with each repaint, 
+look for where you have used Component.getGraphics() and not Graphics.dispose().
+For example, this will definitely NOT work in SwingJS:
+
+  this.paint(getGraphics())
+  
+and really should not work in Java, either, as it is technically a resource memory leak.
+
+Instead, if you really do not want to use repaint(), use this:
+
+  Graphics g = getGraphics();
+  paint(g);
+  g.dispose();
+
+
+
+Graphics.setClip()
+------------------
+
+The HTML5 canvas.clip() method is permanent. You can only reset the clip using
+save/restore. This is different from Java, where you can temporarily change it using
+
+  Shape oldClip = Graphics.getClip();
+  Graphics.setClip(newClip);
+   ...
+  Graphics.setClip(oldClip); 
+
+If you need to do something like this, you must schedule the paints
+to not have overlapping clip needs.
+
+
+MAJOR ISSUES--for Bob and Udo within SwingJS
+============================================
+
+fonts
+-----
+
+Fonts and FontMetrics will all be handled in JavaScript. Font matching will 
+not be exact, and composite (drawn) fonts will not be supported. 
+
+SwingJS handles calls such as font.getFontMetrics(g).stringWidth("xxx") by 
+creating a <div> containing that text, placing it in an obscure location on 
+the page, and reading div.getBoundingClientRect(). This is a VERY precise
+value, but can be a pixel or two off from what Java reports for the same font.
+OS-dependent classes
+--------------------
+
+Static classes such as:
+
+   java.awt.Toolkit
+   java.awt.GraphicsEnvironment
+   
+   
+which are created using Class.forName are implemented using classes in the swingjs package.
+
+AWTAccessor is not implemented. 
+
+   
+AWT component peers and component "ui" user interfaces
+------------------------------------------------------
+
+ComponentPeer is a class that represents a native AWT component.
+Components with such peers are called "heavy-weight" components.
+They are expected to do the dirty work of graphics drawing. 
+
+Java Swing implements peers only for JApplet, JDialog, JFrame, and JWindow. 
+References to such objects have been removed, but clearly there must be 
+some connection to similar DOM objects, even for "light-weight" components. 
+
+
+  
+MAJOR ISSUES--to be resolved by implementers
+============================================
+
+fonts
+-----
+
+Glyph/composite/outline fonts are not supported.
+   
+
+
+threads
+-------
+
+Thread locking and synchronization are not relevant to JavaScript.
+Thus, anything requiring "notify.." or "waitFor.." could be a serious issue.
+All threading must be "faked" in JavaScript. Specifically not available is:
+
+  Thread.sleep()
+  
+javax.swing.AbstractButton#doClick(pressTime) will not work, as it requires Thread.sleep();
+    
+However, java.lang.Thread itself is implemented and used extensively. 
+
+Methods thread.start() and thread.run() both work fine. 
+
+For simple applications that use Thread.sleep() just to have a delay, as in a frame rate, for 
+example, one can use javax.swing.Timer instead. That is fully implemented. 
+
+Likewise, java.util.Timer can be replaced with no loss of performance with javax.Swing.Timer.
+Note that java.util.TimerTask is implemented, but it can also be replaced by an implementation of Runnable.
+
+task = new TimerTask(){....};
+t = new java.util.Timer();
+t.schedule(task, 0, 1);
+
+becomes
+
+task = new TimerTask(){....}; // or task = new Runnable() {...}
+t = new javax.swing.Timer(1, new ActionListener() {
+       @Override
+       public void actionPerformed(ActionEvent e) {
+               task.run();
+       }
+};
+t.setInitialDelay(0); // not particularly necessary
+t.start();
+
+In addition, SwingJS provides swingjs.JSThread, which can be subclassed
+if desired. This class allows simple 
+
+  while(!interrupted()){
+       wait()
+       ...
+  }  
+
+action through an asynchronous function run1(mode). For example:
+
+       protected void run1(int mode) {
+               try {
+                       while (true)
+                               switch (mode) {
+                               case INIT:
+                                       // once-through stuff here
+                                       mode = LOOP;
+                                       break;
+                               case LOOP:
+                                       if (!doDispatch || isInterrupted()) {
+                                               mode = DONE;
+                                       } else {
+                                               Runnable r = new Runnable() {
+                                                       public void run() {
+                                                               // put the loop code here
+                                                       }
+                                               };
+                                               dispatchAndReturn(r);
+                                               if (isJS)
+                                                       return;
+                                       }
+                                       break;
+                               // add more cases as needed
+                               case DONE:
+                                       // finish up here
+                                       if (isInterrupted())
+                                               return;
+                                       // or here
+                                       break;
+                               }
+               } finally {
+                       // stuff here to be executed after each loop in JS or at the end in Java
+               }
+       }
+
+image loading
+-------------
+- All image loading in SwingJS is synchronous. A MediaTracker call will immediately return "complete".
+  However, it still may take one system clock tick to fully load images. Thus, it is recommended that
+  images be preloaded in the static block of the applet if it is necessary that they be available in init().
+  This is only an issue if you are trying to access the pixel buffer of the image in JavaScript. 
+  
+- Applet.getImage(path, name) will return null if the image does not exist. 
+
+- BufferedImage: only "support" imageType RGB and ARGB
+
+  -BH: This is a temporary edit, just to get us started. Certainly GRAY will be needed
+
+
+BigInteger and BigDecimal
+-------------------------
+
+java.math.BigInteger is fully supported; java.math.BigDecimal is roughed 
+in and not fully tested (07/2019). 
+
+Both classes present significant issues for JavaScript, as they are based in 
+Java's 64-bit long for all their operations. Here is the JavaDoc note I added
+to BigInteger:
+
+ * SwingJS note: Because of the limitations of JavaScript with regard
+ * to long-integer bit storage as a double, this implementation drops
+ * the integer storage bit length to 24, giving 48 for long and leaving
+ * the last 16 bits clear for the exponent of the double number. This should
+ * not affect performance significantly. It does increase the storage 
+ * size by about 33%. By bringing an "int" to 3 bytes, we can easily construct
+ * and use byte[] data intended for the original BitSet.  
+
+"Easily" may be a bit strong there. This was a serious challenge.
+
+BigDecimal seems to run normally, but in order to do that, my hack involves
+reducing the size of an integer that is allowed to be stored as such and not
+in byte[] as a BigInteger. I'm sure there is a performance hit, but it does work.
+
+no format internationalization
+------------------------------
+
+For now, just en for number and date formatters
+
+no winding rules
+----------------
+
+  When filling a graphic, only nonzero winding rule is implemented in HTML5 Canvas2D.
+
+
+
+text-related field implementation
+---------------------------------
+
+Text fields are:
+
+JTextField   (JavaScript <input type="text">)
+JTextArea    (JavaScript <textarea>)
+JTextPane    (JavaScript <div>)
+JEditorPane  (JavaScript <div>)
+
+For the initial implementation, we don't implement infinite undo/redo, and the abstract 
+document model is much less elaborate. Only PlainDocument (in the form of JSPlainDocument)
+is implemented. The Document returned by JTextField.getDocument() is a javax.swing.text.Document.
+
+All scrolling is handled by HTML5. javax.swing.AutoScroller is not implemented.
+public static methods .stop, .isRunning, .processMouseDragged require true Java threading
+and so are not implmented. javax.swing.text.View and its subclasses are not implemented. 
+
+The JS document model does not allow two text fields to address the same underlying document. 
+
+JavaScript is slightly different from Java in that the field value is changed asynchronously after
+the keypressed event, so Java actions that are keyed to KEY_PRESSED may not pick up the new 
+key value even after SwingUtilities.invokeLater() is called. Thus, key pressed actions may need
+to be recorded after a key released event instead. 
+
+Formatter/Regex limitations
+---------------------------
+
+Some browsers cannot process Regex "look-behind" process such as (?<=\W)
+java.util.regex.Matcher and Pattern use JavaScript's RegExp object rather than
+the native Java object. These are not identical. Only flags /igm are supported.
+Matcher.start(groupID) is not supported.
+
+java.util.Formatter will function correctly for all standard %... patterns.
+
+integer 1/0 == Infinity
+-----------------------
+
+1/0 in Java throws "java.lang.ArithmeticException: / by zero", but in JavaScript is just Infinity. 
+
+
+Summary
+-------
+
+These are all the known limitations of SwingJS. We have not found any of these limitations
+to be show-stoppers. The primary issue for newcomers to SwingJS is having the source code.
+You must check that source code for all your library jar files is available or, if you
+choose, you will need to decompile those classes. We have used decompilation on some projects,
+and it works just fine. So, technically, all we really need are JAR/class files. But the 
+source is by far superior. It's generally prettier, and it has the license information that
+may or may not be present with the JAR or class files. Use class files at your own risk.
+
+Bob Hanson
+2019.08.16
+
+
+Additional Issues
+-----------------
+
+Annotation is working for classes, methods, and fields (12/22/19). Method reflection, however,
+is limited. Interfaces do not expose their methods, as the transpiler does not actually transpile
+the interfaces themselves. And method reflection only includes annotated methods.
+
+java.util.concurrent is not fully elaborated. This package is rewritten to not actually use the
+memory handling capabilities of concurrency, which JavaScript does not have access to.
+
+System.getProperties() just returns a minimal set of properties.
+
+
index 0fd8664..398b625 100644 (file)
@@ -1 +1 @@
-20201129201754 
+20201205191847