This is an automated email from the ASF dual-hosted git repository.

thiagohp pushed a commit to branch feature/coffeescript-to-typescript
in repository https://gitbox.apache.org/repos/asf/tapestry-5.git

commit ec642d7281ee64365506c33d8a4dcde3d062e535
Author: Thiago H. de Paula Figueiredo <thi...@arsmachina.com.br>
AuthorDate: Wed Apr 9 18:03:53 2025 -0300

    TAP5-2804: finishing the CoffeeScript to TypeScript conversion
    
    Thorough tests still needed, though.
---
 .../src/main/typescript/package-lock.json          | 239 +--------------------
 tapestry-core/src/main/typescript/package.json     |   9 +-
 .../main/typescript/src/t5/core/confirm-click.ts   |  11 +-
 .../src/main/typescript/src/t5/core/events.ts      |   4 +-
 .../src/main/typescript/src/t5/core/graphviz.ts    |   9 +-
 .../src/main/typescript/src/t5/core/init.ts        |  17 +-
 .../src/main/typescript/src/t5/core/messages.ts    |  45 ++--
 .../src/main/typescript/src/t5/core/moment.ts      |  11 +-
 .../src/main/typescript/src/t5/core/pageinit.ts    |  78 ++++---
 .../src/main/typescript/src/t5/core/palette.ts     |  90 ++++++--
 .../src/main/typescript/src/t5/core/select.ts      |  21 +-
 .../main/typescript/src/t5/core/time-interval.ts   |  17 +-
 .../src/main/typescript/src/t5/core/tree.ts        |  31 ++-
 .../src/main/typescript/src/t5/core/utils.ts       |   4 +-
 .../src/main/typescript/src/t5/core/validation.ts  |  68 ++++--
 .../main/typescript/src/t5/core/zone-refresh.ts    |  19 +-
 .../src/main/typescript/src/t5/core/zone.ts        |  93 +++++---
 17 files changed, 368 insertions(+), 398 deletions(-)

diff --git a/tapestry-core/src/main/typescript/package-lock.json 
b/tapestry-core/src/main/typescript/package-lock.json
index 2719117b4..4b1160c17 100644
--- a/tapestry-core/src/main/typescript/package-lock.json
+++ b/tapestry-core/src/main/typescript/package-lock.json
@@ -5,62 +5,14 @@
   "packages": {
     "": {
       "license": "Apache-2.0",
-      "dependencies": {
-        "underscore": "^1.13.7"
-      },
       "devDependencies": {
         "@hpcc-js/wasm": "^2.22.4",
         "@types/jquery": "^1.10.45",
         "@types/underscore": "^1.13.0",
-        "jsdoc": "^4.0.4",
+        "moment": "^2.30.1",
         "typedoc": "^0.28.1",
-        "typescript": "^5.0.0"
-      }
-    },
-    "node_modules/@babel/helper-string-parser": {
-      "version": "7.25.9",
-      "resolved": 
"https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz";,
-      "integrity": 
"sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==",
-      "dev": true,
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/helper-validator-identifier": {
-      "version": "7.25.9",
-      "resolved": 
"https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz";,
-      "integrity": 
"sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==",
-      "dev": true,
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/@babel/parser": {
-      "version": "7.27.0",
-      "resolved": 
"https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz";,
-      "integrity": 
"sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==",
-      "dev": true,
-      "dependencies": {
-        "@babel/types": "^7.27.0"
-      },
-      "bin": {
-        "parser": "bin/babel-parser.js"
-      },
-      "engines": {
-        "node": ">=6.0.0"
-      }
-    },
-    "node_modules/@babel/types": {
-      "version": "7.27.0",
-      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz";,
-      "integrity": 
"sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==",
-      "dev": true,
-      "dependencies": {
-        "@babel/helper-string-parser": "^7.25.9",
-        "@babel/helper-validator-identifier": "^7.25.9"
-      },
-      "engines": {
-        "node": ">=6.9.0"
+        "typescript": "^5.0.0",
+        "underscore": "^1.13.7"
       }
     },
     "node_modules/@gerrit0/mini-shiki": {
@@ -88,18 +40,6 @@
         "dot-wasm": "node 
./node_modules/@hpcc-js/wasm-graphviz-cli/bin/index.js"
       }
     },
-    "node_modules/@jsdoc/salty": {
-      "version": "0.2.9",
-      "resolved": "https://registry.npmjs.org/@jsdoc/salty/-/salty-0.2.9.tgz";,
-      "integrity": 
"sha512-yYxMVH7Dqw6nO0d5NIV8OQWnitU8k6vXH8NtgqAfIa/IUqRMxRv/NUJJ08VEKbAakwxlgBl5PJdrU0dMPStsnw==",
-      "dev": true,
-      "dependencies": {
-        "lodash": "^4.17.21"
-      },
-      "engines": {
-        "node": ">=v12.0.0"
-      }
-    },
     "node_modules/@shikijs/engine-oniguruma": {
       "version": "3.2.1",
       "resolved": 
"https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.2.1.tgz";,
@@ -159,28 +99,6 @@
       "integrity": 
"sha512-JvoVtPbu1wrcOldn5gvuyJqr7DLX6I657N68Fqj5S7AUutdYZqabQjfP7dY6Gee7/Afo1uajwcORVlQfnsjkLQ==",
       "dev": true
     },
-    "node_modules/@types/linkify-it": {
-      "version": "5.0.0",
-      "resolved": 
"https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz";,
-      "integrity": 
"sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==",
-      "dev": true
-    },
-    "node_modules/@types/markdown-it": {
-      "version": "14.1.2",
-      "resolved": 
"https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz";,
-      "integrity": 
"sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==",
-      "dev": true,
-      "dependencies": {
-        "@types/linkify-it": "^5",
-        "@types/mdurl": "^2"
-      }
-    },
-    "node_modules/@types/mdurl": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz";,
-      "integrity": 
"sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==",
-      "dev": true
-    },
     "node_modules/@types/underscore": {
       "version": "1.13.0",
       "resolved": 
"https://registry.npmjs.org/@types/underscore/-/underscore-1.13.0.tgz";,
@@ -229,12 +147,6 @@
       "integrity": 
"sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
       "dev": true
     },
-    "node_modules/bluebird": {
-      "version": "3.7.2",
-      "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz";,
-      "integrity": 
"sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
-      "dev": true
-    },
     "node_modules/brace-expansion": {
       "version": "2.0.1",
       "resolved": 
"https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz";,
@@ -244,18 +156,6 @@
         "balanced-match": "^1.0.0"
       }
     },
-    "node_modules/catharsis": {
-      "version": "0.9.0",
-      "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz";,
-      "integrity": 
"sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==",
-      "dev": true,
-      "dependencies": {
-        "lodash": "^4.17.15"
-      },
-      "engines": {
-        "node": ">= 10"
-      }
-    },
     "node_modules/cliui": {
       "version": "8.0.1",
       "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz";,
@@ -315,15 +215,6 @@
         "node": ">=6"
       }
     },
-    "node_modules/escape-string-regexp": {
-      "version": "2.0.0",
-      "resolved": 
"https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz";,
-      "integrity": 
"sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==",
-      "dev": true,
-      "engines": {
-        "node": ">=8"
-      }
-    },
     "node_modules/get-caller-file": {
       "version": "2.0.5",
       "resolved": 
"https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz";,
@@ -333,12 +224,6 @@
         "node": "6.* || 8.* || >= 10.*"
       }
     },
-    "node_modules/graceful-fs": {
-      "version": "4.2.11",
-      "resolved": 
"https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz";,
-      "integrity": 
"sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
-      "dev": true
-    },
     "node_modules/is-fullwidth-code-point": {
       "version": "3.0.0",
       "resolved": 
"https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz";,
@@ -348,53 +233,6 @@
         "node": ">=8"
       }
     },
-    "node_modules/js2xmlparser": {
-      "version": "4.0.2",
-      "resolved": 
"https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz";,
-      "integrity": 
"sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==",
-      "dev": true,
-      "dependencies": {
-        "xmlcreate": "^2.0.4"
-      }
-    },
-    "node_modules/jsdoc": {
-      "version": "4.0.4",
-      "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.4.tgz";,
-      "integrity": 
"sha512-zeFezwyXeG4syyYHbvh1A967IAqq/67yXtXvuL5wnqCkFZe8I0vKfm+EO+YEvLguo6w9CDUbrAXVtJSHh2E8rw==",
-      "dev": true,
-      "dependencies": {
-        "@babel/parser": "^7.20.15",
-        "@jsdoc/salty": "^0.2.1",
-        "@types/markdown-it": "^14.1.1",
-        "bluebird": "^3.7.2",
-        "catharsis": "^0.9.0",
-        "escape-string-regexp": "^2.0.0",
-        "js2xmlparser": "^4.0.2",
-        "klaw": "^3.0.0",
-        "markdown-it": "^14.1.0",
-        "markdown-it-anchor": "^8.6.7",
-        "marked": "^4.0.10",
-        "mkdirp": "^1.0.4",
-        "requizzle": "^0.2.3",
-        "strip-json-comments": "^3.1.0",
-        "underscore": "~1.13.2"
-      },
-      "bin": {
-        "jsdoc": "jsdoc.js"
-      },
-      "engines": {
-        "node": ">=12.0.0"
-      }
-    },
-    "node_modules/klaw": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz";,
-      "integrity": 
"sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==",
-      "dev": true,
-      "dependencies": {
-        "graceful-fs": "^4.1.9"
-      }
-    },
     "node_modules/linkify-it": {
       "version": "5.0.0",
       "resolved": 
"https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz";,
@@ -404,12 +242,6 @@
         "uc.micro": "^2.0.0"
       }
     },
-    "node_modules/lodash": {
-      "version": "4.17.21",
-      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz";,
-      "integrity": 
"sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
-      "dev": true
-    },
     "node_modules/lunr": {
       "version": "2.3.9",
       "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz";,
@@ -433,28 +265,6 @@
         "markdown-it": "bin/markdown-it.mjs"
       }
     },
-    "node_modules/markdown-it-anchor": {
-      "version": "8.6.7",
-      "resolved": 
"https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.7.tgz";,
-      "integrity": 
"sha512-FlCHFwNnutLgVTflOYHPW2pPcl2AACqVzExlkGQNsi4CJgqOHN7YTgDd4LuhgN1BFO3TS0vLAruV1Td6dwWPJA==",
-      "dev": true,
-      "peerDependencies": {
-        "@types/markdown-it": "*",
-        "markdown-it": "*"
-      }
-    },
-    "node_modules/marked": {
-      "version": "4.3.0",
-      "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz";,
-      "integrity": 
"sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==",
-      "dev": true,
-      "bin": {
-        "marked": "bin/marked.js"
-      },
-      "engines": {
-        "node": ">= 12"
-      }
-    },
     "node_modules/mdurl": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz";,
@@ -476,16 +286,13 @@
         "url": "https://github.com/sponsors/isaacs";
       }
     },
-    "node_modules/mkdirp": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz";,
-      "integrity": 
"sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
+    "node_modules/moment": {
+      "version": "2.30.1",
+      "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz";,
+      "integrity": 
"sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
       "dev": true,
-      "bin": {
-        "mkdirp": "bin/cmd.js"
-      },
       "engines": {
-        "node": ">=10"
+        "node": "*"
       }
     },
     "node_modules/punycode.js": {
@@ -506,15 +313,6 @@
         "node": ">=0.10.0"
       }
     },
-    "node_modules/requizzle": {
-      "version": "0.2.4",
-      "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.4.tgz";,
-      "integrity": 
"sha512-JRrFk1D4OQ4SqovXOgdav+K8EAhSB/LJZqCz8tbX0KObcdeM15Ss59ozWMBWmmINMagCwmqn4ZNryUGpBsl6Jw==",
-      "dev": true,
-      "dependencies": {
-        "lodash": "^4.17.21"
-      }
-    },
     "node_modules/string-width": {
       "version": "4.2.3",
       "resolved": 
"https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz";,
@@ -541,18 +339,6 @@
         "node": ">=8"
       }
     },
-    "node_modules/strip-json-comments": {
-      "version": "3.1.1",
-      "resolved": 
"https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz";,
-      "integrity": 
"sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
-      "dev": true,
-      "engines": {
-        "node": ">=8"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus";
-      }
-    },
     "node_modules/typedoc": {
       "version": "0.28.1",
       "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.28.1.tgz";,
@@ -598,7 +384,8 @@
     "node_modules/underscore": {
       "version": "1.13.7",
       "resolved": 
"https://registry.npmjs.org/underscore/-/underscore-1.13.7.tgz";,
-      "integrity": 
"sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g=="
+      "integrity": 
"sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g==",
+      "dev": true
     },
     "node_modules/wrap-ansi": {
       "version": "7.0.0",
@@ -617,12 +404,6 @@
         "url": "https://github.com/chalk/wrap-ansi?sponsor=1";
       }
     },
-    "node_modules/xmlcreate": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz";,
-      "integrity": 
"sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==",
-      "dev": true
-    },
     "node_modules/y18n": {
       "version": "5.0.8",
       "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz";,
diff --git a/tapestry-core/src/main/typescript/package.json 
b/tapestry-core/src/main/typescript/package.json
index c3f09ea08..6cd5b7a06 100644
--- a/tapestry-core/src/main/typescript/package.json
+++ b/tapestry-core/src/main/typescript/package.json
@@ -2,14 +2,14 @@
   "type": "module",
   "devDependencies": {
     "@hpcc-js/wasm": "^2.22.4",
-    "@types/jquery": "^1.10.45", 
+    "@types/jquery": "^1.10.45",
     "@types/underscore": "^1.13.0",
-    "jsdoc": "^4.0.4",
     "typedoc": "^0.28.1",
-    "typescript": "^5.0.0"
+    "typescript": "^5.0.0",
+    "moment": "^2.30.1",
+    "underscore": "^1.13.7"
   },
   "dependencies": {
-    "underscore": "^1.13.7"
   },
   "scripts": {
     "build": "npx tsc",
@@ -19,6 +19,7 @@
     "clean-docs": "rm -rf out"
   },
   "license": "Apache-2.0",
+  "name": "tapestry-5",
   "description": "Apache Tapestry's TypeScript/JavaScript code",
   "homepage": "https://tapestry.apache.org";,
   "keywords": [
diff --git a/tapestry-core/src/main/typescript/src/t5/core/confirm-click.ts 
b/tapestry-core/src/main/typescript/src/t5/core/confirm-click.ts
index fe9589e58..fd6cb0fdf 100644
--- a/tapestry-core/src/main/typescript/src/t5/core/confirm-click.ts
+++ b/tapestry-core/src/main/typescript/src/t5/core/confirm-click.ts
@@ -1,6 +1,9 @@
-// ## t5/core/confirm-click
-//
-// Support for the Tapestry Confirm mixin, and for running confirmation 
dialogs programmatically.
+/**
+ * ## t5/core/confirm-click
+ * 
+ * Support for the Tapestry Confirm mixin, and for running confirmation 
dialogs programmatically.
+ * @packageDocumentation
+ */
 
 import $ from "jquery";
 import  "bootstrap/modal";
@@ -52,6 +55,7 @@ const runDialog = function(options: DialogOptions) {
   });
 
   // Let the animation run before (perhaps) invoking the callback.
+  // @ts-ignore
   $dialog.modal().on("hidden.bs.modal", function() {
     $dialog.remove();
     if (confirmed) {
@@ -68,6 +72,7 @@ const runDialog = function(options: DialogOptions) {
 // Support for the Confirm mixin
 $("body").on("click", "[data-confirm-message]:not(.disabled)", function(event){
 
+  // @ts-ignore
   const $this = $(this);
 
   // We use a data- attribute as a flag, to indicate that the user confirmed 
the behavior.
diff --git a/tapestry-core/src/main/typescript/src/t5/core/events.ts 
b/tapestry-core/src/main/typescript/src/t5/core/events.ts
index 3f8d86dfd..64235cfaa 100644
--- a/tapestry-core/src/main/typescript/src/t5/core/events.ts
+++ b/tapestry-core/src/main/typescript/src/t5/core/events.ts
@@ -12,11 +12,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-/** ## t5/core/events
+/** 
+ * ## t5/core/events
  *
  * This module defines logical names for all events that Tapestry-controlled 
elements
  * trigger or listener for. Prototype requires that all custom events have a 
namespace prefix; jQuery appears to
  * allow it without issue.
+ * @packageDocumentation
  */
 export default {
 
diff --git a/tapestry-core/src/main/typescript/src/t5/core/graphviz.ts 
b/tapestry-core/src/main/typescript/src/t5/core/graphviz.ts
index f25da8c0b..352245332 100644
--- a/tapestry-core/src/main/typescript/src/t5/core/graphviz.ts
+++ b/tapestry-core/src/main/typescript/src/t5/core/graphviz.ts
@@ -12,9 +12,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// ## t5/core/graphviz
-//
-// Support to the core/Graphviz Tapestry component.
+/** 
+ * ## t5/core/graphviz
+ * 
+ * Support to the core/Graphviz Tapestry component.
+ * @packageDocumentation
+ */
 import { Graphviz } from 
"https://cdn.jsdelivr.net/npm/@hpcc-js/wasm/dist/graphviz.js";;
 
 export default (value: string, id: string , showDownloadLink: boolean) => 
Graphviz.load().then(function(graphviz: any) {
diff --git a/tapestry-core/src/main/typescript/src/t5/core/init.ts 
b/tapestry-core/src/main/typescript/src/t5/core/init.ts
index eef3c18e0..a21ae45af 100644
--- a/tapestry-core/src/main/typescript/src/t5/core/init.ts
+++ b/tapestry-core/src/main/typescript/src/t5/core/init.ts
@@ -17,15 +17,18 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// ## t5/core/init
-//
-// Compatibility module, invokes functions on the T5.initializers namespace.
-//
-// Introduced in 5.4, to be removed at some point in the future, when 
T5.initializers is itself no more.
-import console from "t5/core/console";
+/**
+ * ## t5/core/init
+ * 
+ * Compatibility module, invokes functions on the T5.initializers namespace.
+ * Introduced in 5.4, to be removed at some point in the future, when 
T5.initializers is itself no more.
+ * @packageDocumentation
+ */
+import console from "t5/core/console.js";
 
-export default console => // Exports a single function that finds an 
initializer in `T5.initializers` and invokes it.
+export default (console: { error: (arg0: string) => any; }) => // Exports a 
single function that finds an initializer in `T5.initializers` and invokes it.
 function(initName: string | number, ...args: any) {
+  // @ts-ignore
   const fn = T5.initializers[initName];
   if (!fn) {
     return console.error(`Initialization function '${initName}' not found in 
T5.initializers namespace.`);
diff --git a/tapestry-core/src/main/typescript/src/t5/core/messages.ts 
b/tapestry-core/src/main/typescript/src/t5/core/messages.ts
index 5c71535a6..acbbe7d64 100644
--- a/tapestry-core/src/main/typescript/src/t5/core/messages.ts
+++ b/tapestry-core/src/main/typescript/src/t5/core/messages.ts
@@ -12,35 +12,40 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// ## t5/core/messages
-//
-// For all of these modules, we've turned off CoffeeScript's normal outer 
function
-// wrapper, as each module is just a call to `define()` with a function that 
fulfills
-// the same purpose. This one is different, as it is necessary to compute one 
of the dependencies.
-// On the server `t5/core/messages/<locale>` is actually generated 
dynamically, as is a simple
-// mapping of message keys to message values, from the global application 
message catalog.
-//
-// This module provides access to localized messages from the Tapestry 
applications' server-side
-// application message catalog (which is, itself, built from multiple 
resources, some provided by
-// the framework, others provided by the application, or third-party 
libraries).
-//
-// Messages in the catalog that contain Java-style format specifiers are not 
included, as there
-// is no facility for formatting those on the client. This is actually done as 
a simple test for the
-// presence of the `%` character.  In addition, any message key that begins 
with "private-" is
-// assumed to contain sensitive data (such as database URLs or passwords) and 
will not be
-// exposed to the client.
+
+/**
+ * ## t5/core/messages
+ *
+ *For all of these modules, we've turned off CoffeeScript's normal outer 
function
+ * wrapper, as each module is just a call to `define()` with a function that 
fulfills
+ * the same purpose. This one is different, as it is necessary to compute one 
of the dependencies.
+ * On the server `t5/core/messages/<locale>` is actually generated 
dynamically, as is a simple
+ * mapping of message keys to message values, from the global application 
message catalog.
+ *
+ * This module provides access to localized messages from the Tapestry 
applications' server-side
+ * application message catalog (which is, itself, built from multiple 
resources, some provided by
+ * the framework, others provided by the application, or third-party 
libraries).
+ *
+ * Messages in the catalog that contain Java-style format specifiers are not 
included, as there
+ * is no facility for formatting those on the client. This is actually done as 
a simple test for the
+ * presence of the `%` character.  In addition, any message key that begins 
with "private-" is
+ * assumed to contain sensitive data (such as database URLs or passwords) and 
will not be
+ * exposed to the client.
+ * @packageDocumentation
+ */
 
 import _ from "underscore";
-import console from "t5/core/console";
+import console from "t5/core/console.js";
 
 // In the unexpected case that the data-locale attribute is missing, assume 
English
 const locale = (document.documentElement.getAttribute("data-locale")) || "en";
 
-const messages = import(`t5/core/messages/${locale}`);
+let messages: any = {};
+import(`t5/core/messages/${locale}`).then((m: any) => messages = m);
 
 // Returns the application message catalog message for the given key. Returns
 // a placeholder if the key is not found.
-const get = function(key) {
+const get = function(key: string) {
   const value = messages[key];
 
   if (value) {
diff --git a/tapestry-core/src/main/typescript/src/t5/core/moment.ts 
b/tapestry-core/src/main/typescript/src/t5/core/moment.ts
index acb95c85b..c92cb58d2 100644
--- a/tapestry-core/src/main/typescript/src/t5/core/moment.ts
+++ b/tapestry-core/src/main/typescript/src/t5/core/moment.ts
@@ -12,10 +12,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// ## t5/core/moment
-//
-// A wrapper around Moment.js; this simply initializes Moment to
-// use the correct locale (as per the data-locale attribute of the document 
element).
+/** 
+ * ## t5/core/moment
+ *
+ * A wrapper around Moment.js; this simply initializes Moment to
+ * use the correct locale (as per the data-locale attribute of the document 
element).
+ * @packageDocumentation
+ */
 import moment from "moment";
 
 const locale = (document.documentElement.getAttribute("data-locale")) || "en";
diff --git a/tapestry-core/src/main/typescript/src/t5/core/pageinit.ts 
b/tapestry-core/src/main/typescript/src/t5/core/pageinit.ts
index 1fb8399e1..93b6a9399 100644
--- a/tapestry-core/src/main/typescript/src/t5/core/pageinit.ts
+++ b/tapestry-core/src/main/typescript/src/t5/core/pageinit.ts
@@ -1,11 +1,3 @@
-/*
- * decaffeinate suggestions:
- * DS101: Remove unnecessary use of Array.from
- * DS102: Remove unnecessary code created because of implicit returns
- * DS201: Simplify complex destructure assignments
- * DS207: Consider shorter variations of null checks
- * Full docs: 
https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
- */
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
 // You may obtain a copy of the License at
@@ -18,28 +10,33 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// ## t5/core/pageinit
-//
-// Module that defines functions used for page initialization.
-// The initialize function is passed an array of initializers; each 
initializer is itself
-// an array. The first value in the initializer defines the name of the module 
to invoke.
-// The module name may also indicate the function exported by the module, as a 
suffix following a colon:
-// e.g., "my/module:myfunc".
-// Any additional values in the initializer are passed to the function. The 
context of the function (this) is null.
+/**
+ * ## t5/core/pageinit
+ *
+ * Module that defines functions used for page initialization.
+ * The initialize function is passed an array of initializers; each 
initializer is itself
+ * an array. The first value in the initializer defines the name of the module 
to invoke.
+ * The module name may also indicate the function exported by the module, as a 
suffix following a colon:
+ * e.g., "my/module:myfunc".
+ * Any additional values in the initializer are passed to the function. The 
context of the function (this) is null.
+ * @packageDocumentation
+ */
 
 import _ from "underscore";
-import console from "t5/core/console";
-import dom from "t5/core/dom";
-import events from "t5/core/events";
+import console from "t5/core/console.js";
+import dom from "t5/core/dom.js";
+import events from "t5/core/events.js";
 
-let exports;
-let pathPrefix = null;
+let exports: any;
+let pathPrefix: String | null = null;
 
 // Borrowed from Prototype:
+// @ts-ignore
 const isOpera = Object.prototype.toString.call(window.opera) === '[object 
Opera]';
+// @ts-ignore
 const isIE = !!window.attachEvent && !isOpera;
 
-const rebuildURL = function(path) {
+const rebuildURL = function(path: string) {
   if (path.match(/^https?:/)) { return path; }
 
   // See Tapestry.rebuildURL() for an error about the path not starting with a 
leading '/'
@@ -56,7 +53,12 @@ const rebuildURL = function(path) {
 const rebuildURLOnIE =
   isIE ? rebuildURL : _.identity;
 
-const addStylesheets = function(newStylesheets) {
+type StylesheetLink = {
+  href: string;
+  media?: string;
+}
+
+const addStylesheets = function(newStylesheets: StylesheetLink) {
   if (!newStylesheets) { return; }
 
   // Figure out which stylesheets are loaded; adjust for IE, and especially, 
IE 9
@@ -65,9 +67,11 @@ const addStylesheets = function(newStylesheets) {
   .pluck("href")
   .without("")
   .without(null)
+  // @ts-ignore
   .map(rebuildURLOnIE);
 
   const insertionPoint = _.find(document.styleSheets, function(ss) {
+    // @ts-ignore
     const parent = ss.ownerNode || ss.owningElement;
     return parent.rel === "stylesheet ajax-insertion-point";
 });
@@ -77,7 +81,9 @@ const addStylesheets = function(newStylesheets) {
 
   _.chain(newStylesheets)
   .map(ss => ({
+    // @ts-ignore
     href: rebuildURL(ss.href),
+    // @ts-ignore
     media: ss.media
   }))
   .reject(ss => loaded.contains(ss.href).value())
@@ -101,10 +107,11 @@ const addStylesheets = function(newStylesheets) {
 
 };
 
-const invokeInitializer = function(tracker, qualifiedName, initArguments) {
+const invokeInitializer = function(tracker: () => any, qualifiedName: string, 
initArguments: any[]) {
   const [moduleName, functionName] = Array.from(qualifiedName.split(':'));
 
-  return require([moduleName], function(moduleLib) {
+  // @ts-ignore
+  return require([moduleName], function(moduleLib: any) {
   
     try {
       // Some modules export nothing but do some full-page initialization, 
such as adding
@@ -149,10 +156,13 @@ const invokeInitializer = function(tracker, 
qualifiedName, initArguments) {
 // 'true'.
 //
 // This is the main export of the module; other functions are attached as 
properties.
+// @ts-ignore
 const loadLibrariesAndInitialize = function(libraries, inits) {
   console.debug(`Loading ${(libraries != null ? libraries.length : undefined) 
|| 0} libraries`);
-  return exports.loadLibraries(libraries,
-    () => exports.initialize(inits,
+  // @ts-ignore
+  return loadLibraries(libraries,
+    // @ts-ignore
+    () => initialize(inits,
       function() {
         // At this point, all libraries have been loaded, and all inits should 
have executed. Unless some of
         // the inits triggered Ajax updates (such as a core/ProgressiveDisplay 
component), then the page should
@@ -170,6 +180,7 @@ const loadLibrariesAndInitialize = function(libraries, 
inits) {
 export default exports = _.extend(loadLibrariesAndInitialize, {
   // Passed a list of initializers, executes each initializer in order. Due to 
asynchronous loading
   // of modules, the exact order in which initializer functions are invoked is 
not predictable.
+  // @ts-ignore
   initialize(inits, callback) {
     if (inits == null) { inits = []; }
     console.debug(`Executing ${inits.length} inits`);
@@ -195,7 +206,9 @@ export default exports = 
_.extend(loadLibrariesAndInitialize, {
       if (_.isString(init)) {
         invokeInitializer(tracker, init, []);
       } else {
+        // @ts-ignore
         var [qualifiedName, ...initArguments] = Array.from(init);
+        // @ts-ignore
         invokeInitializer(tracker, qualifiedName, initArguments);
       }
     }
@@ -205,9 +218,11 @@ export default exports = 
_.extend(loadLibrariesAndInitialize, {
 
   // Pre-loads a number of libraries in order. When the last library is loaded,
   // invokes the callback (with no parameters).
-  loadLibraries(libraries, callback) {
+  loadLibraries(libraries: string[], callback: () => any) {
+    // @ts-ignore
     const reducer = (callback, library) => (function() {
       console.debug(`Loading library ${library}`);
+      // @ts-ignore
       return require([library], callback);
     });
 
@@ -216,7 +231,7 @@ export default exports = 
_.extend(loadLibrariesAndInitialize, {
     return finalCallback.call(null);
   },
 
-  evalJavaScript(js) {
+  evalJavaScript(js: string) {
     console.debug(`Evaluating: ${js}`);
     return eval(js);
   },
@@ -225,7 +240,7 @@ export default exports = 
_.extend(loadLibrariesAndInitialize, {
   // second, which helps ensure that other initializions on the page are in 
place.
   //
   // * fieldId - element id of field to focus on
-  focus(fieldId) {
+  focus(fieldId: string) {
     const field = dom(fieldId);
 
     if (field) {
@@ -243,6 +258,7 @@ export default exports = 
_.extend(loadLibrariesAndInitialize, {
 
   // * response - the Ajax response object
   // * callback - invoked after scripts are loaded, but before page 
initializations occur (may be null)
+  // @ts-ignore
   handlePartialPageRenderResponse(response, callback) {
 
     // Capture the partial page response portion of the overall response, and
@@ -255,6 +271,7 @@ export default exports = 
_.extend(loadLibrariesAndInitialize, {
     // No other initialization or callback invocation occurs.
     if (partial != null ? partial.redirectURL : undefined) {
       if (window.location.href === partial.redirectURL) {
+        // @ts-ignore
         window.location.reload(true);
       } else {
         window.location.href = partial.redirectURL;
@@ -272,6 +289,7 @@ export default exports = 
_.extend(loadLibrariesAndInitialize, {
         const [id, content] = Array.from(args[0]);
         console.debug(`Updating content for zone ${id}`);
 
+        // @ts-ignore
         const zone = dom.wrap(id);
 
         if (zone) {
diff --git a/tapestry-core/src/main/typescript/src/t5/core/palette.ts 
b/tapestry-core/src/main/typescript/src/t5/core/palette.ts
index e0a6af31e..3093a56b2 100644
--- a/tapestry-core/src/main/typescript/src/t5/core/palette.ts
+++ b/tapestry-core/src/main/typescript/src/t5/core/palette.ts
@@ -12,36 +12,52 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// ## t5/core/palette
-//
-// Support for the `core/Palette` component.
-import dom from "t5/core/dom";
+/**
+ * ## t5/core/palette
+ * 
+ * Support for the `core/Palette` component.
+ * @packageDocumentation
+ */
+import dom from "t5/core/dom.js";
 import _ from "underscore";
-import events from "t5/core/events";
+import events from "t5/core/events.js";
+import { ElementWrapper } from "./types.js";
 
-const isSelected = option => option.selected;
+const isSelected = (option: any) => option.selected;
 
 class PaletteController {
-
-  constructor(id) {
-    this.selected = (dom(id));
-    this.container = this.selected.findParent(".palette");
-    this.available = this.container.findFirst(".palette-available select");
-    this.hidden = this.container.findFirst("input[type=hidden]");
-
-    this.select = this.container.findFirst("[data-action=select]");
-    this.deselect = this.container.findFirst("[data-action=deselect]");
-
-    this.moveUp = this.container.findFirst("[data-action=move-up]");
-    this.moveDown = this.container.findFirst("[data-action=move-down]");
+  selected: ElementWrapper;
+  container: ElementWrapper;
+  available: ElementWrapper;
+  hidden: ElementWrapper;
+  moveUp: ElementWrapper;
+  moveDown: ElementWrapper;
+  doDeselectElement: ElementWrapper;
+  reorder: boolean;
+  valueToOrderIndex: {};
+
+  constructor(id: string) {
+    this.selected = (dom(id))!;
+    this.container = this.selected.findParent(".palette")!;
+    this.available = this.container.findFirst(".palette-available select")!;
+    this.hidden = this.container.findFirst("input[type=hidden]")!;
+
+    this.selected = this.container.findFirst("[data-action=select]")!;
+    this.doDeselectElement = 
this.container.findFirst("[data-action=deselect]")!;
+
+    this.moveUp = this.container.findFirst("[data-action=move-up]")!;
+    this.moveDown = this.container.findFirst("[data-action=move-down]")!;
 
     // Track where reorder is allowed based on whether the buttons actually 
exist
     this.reorder = this.moveUp !== null;
 
     this.valueToOrderIndex = {};
 
+    // @ts-ignore
     for (let i = 0; i < this.available.element.options.length; i++) {
+      // @ts-ignore
       var option = this.available.element.options[i];
+      // @ts-ignore
       this.valueToOrderIndex[option.value] = i;
     }
 
@@ -49,6 +65,7 @@ class PaletteController {
     // values correctly. Otherwise it looks like nothing is selected.
     this.initialTransfer();
 
+    // @ts-ignore
     if (!this.selected.element.disabled) {
       this.updateButtons();
       this.bindEvents();
@@ -58,11 +75,13 @@ class PaletteController {
   initialTransfer() {
     // Get the values for options that should move over
     let i, option;
+    // @ts-ignore
     const values = JSON.parse(this.hidden.value());
     const valueToPosition = {};
 
     for (i = 0; i < values.length; i++) {
       var v = values[i];
+      // @ts-ignore
       valueToPosition[v] = i;
     }
 
@@ -70,14 +89,18 @@ class PaletteController {
 
     const movers = [];
 
+    // @ts-ignore
     for (i = e.options.length - 1; i >= 0; i--) {
+      // @ts-ignore
       option = e.options[i];
       var {
         value
       } = option;
+      // @ts-ignore
       var pos = valueToPosition[value];
       if (pos !== undefined) {
         movers[pos] = option;
+        // @ts-ignore
         e.remove(i);
       }
     }
@@ -85,6 +108,7 @@ class PaletteController {
     return (() => {
       const result = [];
       for (option of Array.from(movers)) {
+        // @ts-ignore
         result.push(this.selected.element.add(option));
       }
       return result;
@@ -99,6 +123,7 @@ class PaletteController {
   }
 
   updateHidden() {
+    // @ts-ignore
     const values = (Array.from(this.selected.element.options).map((option) => 
option.value));
     return this.hidden.value(JSON.stringify(values));
   }
@@ -109,32 +134,38 @@ class PaletteController {
       return false;
     });
 
+    // @ts-ignore
     this.select.on("click", () => {
       this.doSelect();
       return false;
     });
 
+    // @ts-ignore
     this.available.on("dblclick", () => {
       this.doSelect();
       return false;
     });
 
+    // @ts-ignore
     this.deselect.on("click", () => {
       this.doDeselect();
       return false;
     });
 
+    // @ts-ignore
     this.selected.on("dblclick", () => {
       this.doDeselect();
       return false;
     });
 
     if (this.reorder) {
+      // @ts-ignore      
       this.moveUp.on("click", () => {
         this.doMoveUp();
         return false;
       });
 
+      // @ts-ignore
       return this.moveDown.on("click", () => {
         this.doMoveDown();
         return false;
@@ -145,14 +176,19 @@ class PaletteController {
   // Invoked whenever the selections in either list changes or after an 
updates; figures out which buttons
   // should be enabled and which disabled.
   updateButtons() {
+    // @ts-ignore
     this.select.element.disabled = this.available.element.selectedIndex < 0;
 
+    // @ts-ignore
     const nothingSelected = this.selected.element.selectedIndex < 0;
 
+    // @ts-ignore
     this.deselect.element.disabled = nothingSelected;
 
     if (this.reorder) {
+      // @ts-ignore
       this.moveUp.element.disabled = nothingSelected || 
this.allSelectionsAtTop();
+      // @ts-ignore
       return this.moveDown.element.disabled = nothingSelected || 
this.allSelectionsAtBottom();
     }
   }
@@ -162,6 +198,7 @@ class PaletteController {
   doDeselect() { return this.transferOptions(this.selected, this.available, 
false); }
 
   doMoveUp() {
+    // @ts-ignore
     let options = _.toArray(this.selected.element.options);
 
     const groups = _.partition(options, isSelected);
@@ -188,6 +225,7 @@ class PaletteController {
 
 
   doMoveDown() {
+    // @ts-ignore
     let options = _.toArray(this.selected.element.options);
 
     const groups = _.partition(options, isSelected);
@@ -214,6 +252,7 @@ class PaletteController {
 
   // Reorders the selected options to the provided list of options; handles 
triggering the willUpdate and
   // didUpdate events.
+  // @ts-ignore
   reorderSelected(options) {
 
     return this.performUpdate(true, options, () => {
@@ -221,11 +260,13 @@ class PaletteController {
       this.deleteOptions(this.selected);
 
       return Array.from(options).map((o) =>
+        // @ts-ignore
         this.selected.element.add(o, null));
     });
   }
 
   // Performs the update, which includes the willChange and didChange events.
+  // @ts-ignore
   performUpdate(reorder, selectedOptions, updateCallback) {
 
     let canceled = false;
@@ -254,6 +295,7 @@ class PaletteController {
   }
 
   // Deletes all options from a select (an ElementWrapper), prior to new 
options being populated in.
+  // @ts-ignore
   deleteOptions(select) {
 
     const e = select.element;
@@ -268,6 +310,7 @@ class PaletteController {
   }
 
   // Moves options between the available and selected lists, including event 
notifiations before and after.
+  // @ts-ignore
   transferOptions(from, to, atEnd) {
 
     let o;
@@ -319,12 +362,14 @@ class PaletteController {
     });
   }
 
-
+  // @ts-ignore
   insertOption(options, option, atEnd) {
 
     let before;
     if (!atEnd) {
+      // @ts-ignore
       const optionOrder = this.valueToOrderIndex[option.value];
+      // @ts-ignore
       before = _.find(options, o => this.valueToOrderIndex[o.value] > 
optionOrder);
     }
 
@@ -336,7 +381,7 @@ class PaletteController {
     }
   }
 
-
+  // @ts-ignore
   indexOfLastSelection(select) {
     const e = select.element;
     if (e.selectedIndex < 0) {
@@ -354,16 +399,21 @@ class PaletteController {
 
   allSelectionsAtTop() {
     const last = this.indexOfLastSelection(this.selected);
+    // @ts-ignore
     const options = _.toArray(this.selected.element.options);
 
+    // @ts-ignore
     return _(options.slice(0, +last + 1 || undefined)).all(o => o.selected);
   }
 
   allSelectionsAtBottom() {
     const e = this.selected.element;
+    // @ts-ignore
     const last = e.selectedIndex;
+    // @ts-ignore
     const options = _.toArray(e.options);
 
+    // @ts-ignore
     return _(options.slice(last)).all(o => o.selected);
   }
 }
diff --git a/tapestry-core/src/main/typescript/src/t5/core/select.ts 
b/tapestry-core/src/main/typescript/src/t5/core/select.ts
index fa4159a11..8f3f2e3ec 100644
--- a/tapestry-core/src/main/typescript/src/t5/core/select.ts
+++ b/tapestry-core/src/main/typescript/src/t5/core/select.ts
@@ -10,22 +10,29 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// ## t5/core/select
-//
-// Provides a document event handler that triggers an update a zone when the 
value
-// of a select element within the zone changes.
-import events from "t5/core/events";
-import dom from "t5/core/dom";
-import zone from "t5/core/zone";
+/** 
+ * ## t5/core/select
+ * 
+ * Provides a document event handler that triggers an update a zone when the 
value
+ * of a select element within the zone changes.
+ * @packageDocumentation
+ */
+
+import events from "t5/core/events.js";
+import dom from "t5/core/dom.js";
+import zone from "t5/core/zone.js";
 
 dom.onDocument("change", "select[data-update-zone]", function() {
 
+  // @ts-ignore
   const containingZone = zone.findZone(this);
 
   if (containingZone) {
     containingZone.trigger(events.zone.refresh, {
+      // @ts-ignore
       url: this.attr("data-update-url"),
       parameters: {
+        // @ts-ignore
         "t:selectvalue": this.value()
       }
     }
diff --git a/tapestry-core/src/main/typescript/src/t5/core/time-interval.ts 
b/tapestry-core/src/main/typescript/src/t5/core/time-interval.ts
index 22d366630..059d57b20 100644
--- a/tapestry-core/src/main/typescript/src/t5/core/time-interval.ts
+++ b/tapestry-core/src/main/typescript/src/t5/core/time-interval.ts
@@ -12,20 +12,25 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// ## t5/core/time-interval
-//
-// Used with the Interval component to express the interval between two 
timestamps,
-// or the dynamic difference between now and an end point in the past or 
future.
+/**
+ * ## t5/core/time-interval
+ *
+ * Used with the Interval component to express the interval between two 
timestamps,
+ * or the dynamic difference between now and an end point in the past or 
future.
+ * @packageDocumentation
+ */
 
-import dom from "t5/core/dom";
-import moment from "t5/core/moment";
+import dom from "t5/core/dom.js";
+import moment from "t5/core/moment.js";
 
 const ATTR = "data-timeinterval";
 
 const DEFAULT_FORMAT = 'YYYY-MM-DDTHH:mm:ss.SSSZ';
 
+// @ts-ignore
 const toMoment = function(s) { if (s) { return (moment(s, DEFAULT_FORMAT)); } 
else { return moment(); } };
 
+// @ts-ignore
 const updateElement = function(el) {
   const start = toMoment(el.attr("data-timeinterval-start"));
   const end = toMoment(el.attr("data-timeinterval-end"));
diff --git a/tapestry-core/src/main/typescript/src/t5/core/tree.ts 
b/tapestry-core/src/main/typescript/src/t5/core/tree.ts
index 359ed2277..485919e64 100644
--- a/tapestry-core/src/main/typescript/src/t5/core/tree.ts
+++ b/tapestry-core/src/main/typescript/src/t5/core/tree.ts
@@ -12,12 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// ## t5/core/tree
-//
-// Handlers to support to the core/Tree Tapestry component.
-
-import dom from "t5/core/dom";
-import ajax from "t5/core/ajax";
+/**
+ * ## t5/core/tree
+ * 
+ * Handlers to support to the core/Tree Tapestry component.
+ * @packageDocumentation
+ */
+
+import dom from "t5/core/dom.js";
+import ajax from "t5/core/ajax.js";
 import "t5/core/zone";
 
 const TREE = "[data-component-type='core/Tree']";
@@ -29,6 +32,7 @@ const LOADED = "tree-children-loaded";
 const EXPANDED = "tree-expanded";
 const SELECTED = "selected-leaf-node";
 
+// @ts-ignore
 const send = function(node, action, success) {
   const container = node.findParent(TREE);
   const url = container.attr("data-tree-action-url");
@@ -43,6 +47,7 @@ const send = function(node, action, success) {
   );
 };
 
+// @ts-ignore
 const loadChildren = function(node) {
 
   // Ignore duplicate requests to load the children.
@@ -53,6 +58,7 @@ const loadChildren = function(node) {
   node.addClass("empty-node");
   node.update("<span class='tree-ajax-wait'/>");
 
+  // @ts-ignore
   return send(node, "expand", function(response) {
     // Remove the Ajax spinner and  mark the node as expanded (it will have a 
"-"
     // icon instead of a "+" icon)
@@ -69,18 +75,21 @@ const loadChildren = function(node) {
 
 // toggles a folder in the tree between expanded and collapsed (once data for 
the folder
 // has been loaded).
+// @ts-ignore
 const toggle = function(node) {
   const sublist = node.findParent("li").findFirst("ul");
 
   if (node.hasClass(EXPANDED)) {
     node.removeClass(EXPANDED);
     sublist.hide();
+    // @ts-ignore
     send(node, "markCollapsed");
     return;
   }
 
   node.addClass(EXPANDED);
   sublist.show();
+  // @ts-ignore
   return send(node, "markExpanded");
 };
 
@@ -88,14 +97,18 @@ const toggle = function(node) {
 const clickHandler = function() {
 
   // Ignore clicks on leaf nodes, and on folders that are known to be empty.
+  // @ts-ignore
   if ((this.parent().hasClass("leaf-node")) || (this.hasClass("empty-node"))) {
     return false;
   }
 
   // If not already loaded then fire off the Ajax request to load the content.
+  // @ts-ignore
   if ((this.meta(LOADED)) || (this.hasClass(EXPANDED))) {
+    // @ts-ignore
     toggle(this);
   } else {
+    // @ts-ignore
     loadChildren(this);
   }
 
@@ -104,15 +117,21 @@ const clickHandler = function() {
 
 const toggleSelection = function() {
 
+  // @ts-ignore
   const selected = this.hasClass(SELECTED);
 
+  // @ts-ignore
   const node = this.findParent("li").findFirst(`[${NODE_ID}]`);
 
   if (selected) {
+    // @ts-ignore
     this.removeClass(SELECTED);
+    // @ts-ignore
     send(node, "deselect");
   } else {
+    // @ts-ignore
     this.addClass(SELECTED);
+    // @ts-ignore
     send(node, "select");
   }
 
diff --git a/tapestry-core/src/main/typescript/src/t5/core/utils.ts 
b/tapestry-core/src/main/typescript/src/t5/core/utils.ts
index 10a213d30..e279038a4 100644
--- a/tapestry-core/src/main/typescript/src/t5/core/utils.ts
+++ b/tapestry-core/src/main/typescript/src/t5/core/utils.ts
@@ -12,9 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-/** ## t5/core/utils
+/** 
+ * ## t5/core/utils
  *
  * A few handy functions.
+ * @packageDocumentation
  */
 import _ from "underscore";
 
diff --git a/tapestry-core/src/main/typescript/src/t5/core/validation.ts 
b/tapestry-core/src/main/typescript/src/t5/core/validation.ts
index dca211512..d2fb73c2b 100644
--- a/tapestry-core/src/main/typescript/src/t5/core/validation.ts
+++ b/tapestry-core/src/main/typescript/src/t5/core/validation.ts
@@ -16,17 +16,19 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// ## t5/core/validation
-//
-// Support for Tapestry's built-in set of translators and validators.
-//
+/**
+ * ## t5/core/validation
+ * 
+ * Support for Tapestry's built-in set of translators and validators.
+ * @packageDocumentation
+ */
 
 import _ from "underscore";
-import dom from "t5/core/dom";
-import events from "t5/core/events";
-import utils from "t5/core/utils";
-import messages from "t5/core/messages"
-import fields from  "t5/core/fields";
+import dom from "t5/core/dom.js";
+import events from "t5/core/events.js";
+import utils from "t5/core/utils.js";
+import messages from "t5/core/messages.js"
+import { ElementWrapper } from "./types.js";
 
 const REGEXP_META = "t5:regular-expression";
 
@@ -46,13 +48,13 @@ const decimal = messages("decimal-symbols.decimal");
 //
 // * input - input string to be converted
 // * isInteger - restrict to integer values (decimal point not allowed)
-const parseNumber = function(input, isInteger) {
+const parseNumber = function(input: string, isInteger: boolean) {
 
   let canonical = "";
 
-  const accept = ch => canonical += ch;
+  const accept = (ch: string) => canonical += ch;
 
-  const acceptDigitOnly = function(ch) {
+  const acceptDigitOnly = function(ch: string) {
     if ((ch < "0") || (ch > "9")) {
       throw new Error(messages("core-input-not-numeric"));
     }
@@ -60,17 +62,17 @@ const parseNumber = function(input, isInteger) {
     accept(ch);
   };
 
-  const mustBeDigit = function(ch) {
+  const mustBeDigit = function(ch: string) {
     acceptDigitOnly(ch);
     return any;
   };
 
-  var decimalPortion = function(ch) {
+  var decimalPortion = function(ch: string) {
     acceptDigitOnly(ch);
     return decimalPortion;
   };
 
-  var any = function(ch) {
+  var any = function(ch: string) {
     switch (ch) {
       case grouping: return mustBeDigit;
       case decimal:
@@ -85,7 +87,7 @@ const parseNumber = function(input, isInteger) {
     }
   };
 
-  const leadingMinus = function(ch) {
+  const leadingMinus = function(ch: string) {
     if (ch === minus) {
       accept("-");
       return mustBeDigit;
@@ -103,7 +105,7 @@ const parseNumber = function(input, isInteger) {
   return Number(canonical);
 };
 
-const matches = function(input, re) {
+const matches = function(input: string, re: RegExp) {
   const groups = input.match(re);
 
   // Unlike Java, there isn't an easy way to match the entire string. This
@@ -116,7 +118,7 @@ const matches = function(input, re) {
 
 const emailRE = new 
RegExp("[A-Za-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[A-Za-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?\\.)+[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?");
 
-const translate = function(field, memo, isInteger) {
+const translate = function(field: ElementWrapper, memo: any, isInteger: 
boolean) {
   try {
     const result = parseNumber(memo.value, isInteger);
 
@@ -125,58 +127,73 @@ const translate = function(field, memo, isInteger) {
     }
 
     return memo.translated = result;
-  } catch (e) {
+  } catch (e: any) {
     memo.error = (field.attr("data-translation-message")) || e.message || 
"ERROR";
     return false;
   }
 };
 
-dom.onDocument(events.field.optional, "[data-optionality=required]", 
function(event, memo) {
+dom.onDocument(events.field.optional, "[data-optionality=required]", 
function(event, memo: any) {
 
   if (utils.isBlank(memo.value)) {
+    // @ts-ignore
     return memo.error =  (this.attr("data-required-message")) || "REQUIRED";
   }
 });
 
 dom.onDocument(events.field.translate, "[data-translation=numeric]", 
function(event, memo) {
+  // @ts-ignore
   return translate(this, memo, false);
 });
 
 dom.onDocument(events.field.translate, "[data-translation=integer]", 
function(event, memo) {
+  // @ts-ignore
   return translate(this, memo, true);
 });
 
 dom.onDocument(events.field.validate, "[data-validate-min-length]", 
function(event, memo) {
+  // @ts-ignore
   const min = parseInt(this.attr("data-validate-min-length"));
 
+  // @ts-ignore
   if (memo.translated.length < min) {
+    // @ts-ignore
     memo.error = (this.attr("data-min-length-message")) || "TOO SHORT";
     return false;
   }
 });
 
 dom.onDocument(events.field.validate, "[data-validate-max-length]", 
function(event, memo) {
+  // @ts-ignore
   const max = parseInt(this.attr("data-validate-max-length"));
 
+  // @ts-ignore
   if (memo.translated.length > max) {
+    // @ts-ignore
     memo.error = (this.attr("data-max-length-message")) || "TOO LONG";
     return false;
   }
 });
 
 dom.onDocument(events.field.validate, "[data-validate-max]", function(event, 
memo) {
+  // @ts-ignore
   const max = parseInt(this.attr("data-validate-max"));
 
+  // @ts-ignore
   if (memo.translated > max) {
+    // @ts-ignore
     memo.error = (this.attr("data-max-message")) || "TOO LARGE";
     return false;
   }
 });
 
 dom.onDocument(events.field.validate, "[data-validate-min]", function(event, 
memo) {
+  // @ts-ignore
   const min = parseInt(this.attr("data-validate-min"));
 
+  // @ts-ignore
   if (memo.translated < min) {
+    // @ts-ignore
     memo.error = (this.attr("data-min-message")) || "TOO SMALL";
     return false;
   }
@@ -184,7 +201,9 @@ dom.onDocument(events.field.validate, 
"[data-validate-min]", function(event, mem
 
 dom.onDocument(events.field.validate, "[data-validate-email]", function(event, 
memo) {
 
+  // @ts-ignore
   if (!matches(memo.translated, emailRE)) {
+    // @ts-ignore
     memo.error = (this.attr("data-email-message")) || "INVALID EMAIL";
     return false;
   }
@@ -193,14 +212,19 @@ dom.onDocument(events.field.validate, 
"[data-validate-email]", function(event, m
 dom.onDocument(events.field.validate, "[data-validate-regexp]", 
function(event, memo) {
 
   // Cache the compiled regular expression.
+  // @ts-ignore
   let re = this.meta(REGEXP_META);
 
   if (!re) {
+    // @ts-ignore
     re = new RegExp(this.attr("data-validate-regexp"));
+    // @ts-ignore
     this.meta(REGEXP_META, re);
   }
 
+  // @ts-ignore
   if (!matches(memo.translated, re)) {
+    // @ts-ignore
     memo.error = (this.attr("data-regexp-message")) || "INVALID";
     return false;
   }
@@ -208,14 +232,18 @@ dom.onDocument(events.field.validate, 
"[data-validate-regexp]", function(event,
 
 dom.onDocument(events.field.validate, "[data-expected-status=checked]", 
function(event, memo) {
 
+  // @ts-ignore
   if (!memo.value) {
+    // @ts-ignore
     return memo.error =  (this.attr("data-checked-message")) || "MUST BE 
CHECKED";
   }
 });
 
 dom.onDocument(events.field.validate, "[data-expected-status=unchecked]", 
function(event, memo) {
 
+  // @ts-ignore
   if (memo.value) {
+    // @ts-ignore
     return memo.error =  (this.attr("data-checked-message")) || "MUST NOT BE 
CHECKED";
   }
 });
diff --git a/tapestry-core/src/main/typescript/src/t5/core/zone-refresh.ts 
b/tapestry-core/src/main/typescript/src/t5/core/zone-refresh.ts
index 10b52c4dc..d8d458cc3 100644
--- a/tapestry-core/src/main/typescript/src/t5/core/zone-refresh.ts
+++ b/tapestry-core/src/main/typescript/src/t5/core/zone-refresh.ts
@@ -12,19 +12,24 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// ## t5/core/zone-refresh
+/**
+ * ## t5/core/zone-refresh
+ * 
+ * @packageDocumentation
+ */
 
-import events from "t5/core/events";
-import dom from "t5/core/dom";
-import console from "t5/core/console";
+import events from "t5/core/events.js";
+import dom from "t5/core/dom.js";
+import console from "t5/core/console.js";
 
 // Initialize a timer for the zone at the specified period (in seconds). The 
zone will be
 // refreshed with the provided URL.
+// @ts-ignore
 const initialize = function(zoneId, period, url) {
   let zone = dom(zoneId);
 
   if (!zone) {
-    console.err(`Zone ${zoneId} not found for periodic refresh.`);
+    console.error(`Zone ${zoneId} not found for periodic refresh.`);
     return;
   }
 
@@ -37,6 +42,7 @@ const initialize = function(zoneId, period, url) {
 
   // Whenever the zone updates, we can clear the executing flag.
 
+  // @ts-ignore
   zone.on(events.zone.didUpdate, function() {
     executing = false;
   });
@@ -53,6 +59,7 @@ const initialize = function(zoneId, period, url) {
     // If the zone element is no longer part of the DOM, stop the
     // timer
 
+    // @ts-ignore
     if (!zone.closest('body')) {
       cleanUp();
       return;
@@ -61,12 +68,14 @@ const initialize = function(zoneId, period, url) {
     // Set the flag now, it will clear when the zone updates.
     executing = true;
 
+    // @ts-ignore
     return zone.trigger(events.zone.refresh, { url });
   };
 
   var intervalId = window.setInterval(handler, period * 1000);
 
   // Not sure if this is needed except for IE:
+  // @ts-ignore
   return (dom(window)).on("beforeunload", cleanUp);
 };
 
diff --git a/tapestry-core/src/main/typescript/src/t5/core/zone.ts 
b/tapestry-core/src/main/typescript/src/t5/core/zone.ts
index 534f1d594..a59f6c9aa 100644
--- a/tapestry-core/src/main/typescript/src/t5/core/zone.ts
+++ b/tapestry-core/src/main/typescript/src/t5/core/zone.ts
@@ -10,23 +10,27 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// ## t5/core/zone
-//
-// Provides a default handler for events related to zones. A zone is any kind 
of
-// client-side element that can be updated; a zone will normally have a unique 
id.
-// Typically, a client-side zone element is rendered by, and corresponds to, a 
server-side
-// core/Zone component; however, certain other components (such as 
core/ProgressiveDisplay) may
-// also be treated as zones.
-//
-// Most often, a zone is any element with attribute `data-container-type=zone` 
and corresponds
-// to a core/Zone server-side component.
-
-import dom from "t5/core/dom"
-import events from "t5/core/events";
-import ajax from "t5/core/ajax";
-import console from "t5/core/console";
-import forms from "t5/core/forms";
+/**
+ *  ## t5/core/zone/
+ * 
+ * Provides a default handler for events related to zones. A zone is any kind 
of
+ * client-side element that can be updated; a zone will normally have a unique 
id.
+ * Typically, a client-side zone element is rendered by, and corresponds to, a 
server-side
+ * core/Zone component; however, certain other components (such as 
core/ProgressiveDisplay) may
+ * also be treated as zones.
+ * 
+ * Most often, a zone is any element with attribute `data-container-type=zone` 
and corresponds
+ * to a core/Zone server-side component.
+ * @packageDocumentation
+ */
+
+import dom from "t5/core/dom.js"
+import events from "t5/core/events.js";
+import ajax from "t5/core/ajax.js";
+import console from "t5/core/console.js";
+import forms from "t5/core/forms.js";
 import _ from "underscore";
+import { ElementWrapper, ResponseWrapper } from "./types.js";
 
 if ((typeof ajax) !== "function") {
   console.error("ajax variable is not a function, but instead it is " + 
JSON.stringify(ajax));
@@ -34,14 +38,19 @@ if ((typeof ajax) !== "function") {
   throw new Error("ajax variable is not a function");
 }
 
-// For a given element that may have the `data-update-zone` attribute, locates 
the
-// zone element. May return null if the zone can not be found (after logging 
an error
-// to the console).
-//
-// * element - starting point for determining zone
-const findZone = function(element) {
-  let zone;
-  const zoneId = element.attr("data-update-zone"); // TODO: replace with 
vanilla version
+/**
+ * For a given element that may have the `data-update-zone` attribute, locates 
the
+ * zone element. May return null if the zone can not be found (after logging 
an error
+ * to the console).
+ * 
+ * @param {HTMLElement | ElementWrapper} element starting point for 
determining zone
+ */ 
+const findZone = function(element: HTMLElement | ElementWrapper) {
+  let zone: ElementWrapper | null;
+  if (element instanceof HTMLElement) {
+    element = dom(element)!;
+  }
+  const zoneId = element.attr("data-update-zone") as string; // TODO: replace 
with vanilla version
 
   if (zoneId === "^") {
     zone = element.findParent("[data-container-type=zone]");
@@ -64,6 +73,7 @@ const findZone = function(element) {
 
 dom.onDocument("click", "a[data-update-zone]", function(event) {
 
+  // @ts-ignore
   const element = this.closest("[data-update-zone]");
 
   if (!element) {
@@ -76,17 +86,21 @@ dom.onDocument("click", "a[data-update-zone]", 
function(event) {
     zone.trigger(events.zone.refresh,  {url: element.attr("href")});
   }
 
+  // @ts-ignore
   event.nativeEvent.preventDefault();
 });
 
 dom.onDocument("submit", "form[data-update-zone]", function() {
 
+  // @ts-ignore
   const zone = findZone(this);
 
   if (zone) {
+    // @ts-ignore
     const formParameters = forms.gatherParameters(this);
 
     zone.trigger(events.zone.refresh, {
+      // @ts-ignore
       url: (this.attr("action")),
       parameters: formParameters
     }
@@ -98,12 +112,16 @@ dom.onDocument("submit", "form[data-update-zone]", 
function() {
 
 dom.onDocument("submit", "form[data-async-trigger]", function() {
 
+  // @ts-ignore
   const formParameters = forms.gatherParameters(this);
 
+  // @ts-ignore
   this.addClass("ajax-update");
 
+  // @ts-ignore
   ajax((this.attr("action")), {
     data: formParameters,
+    // @ts-ignore
     complete: () => this.removeClass("ajax-update")
   }
   );
@@ -111,8 +129,10 @@ dom.onDocument("submit", "form[data-async-trigger]", 
function() {
   return false;
 });
 
+// @ts-ignore
 dom.onDocument(events.zone.update, function(event) {
 
+  // @ts-ignore
   this.trigger(events.zone.willUpdate);
 
   const {
@@ -126,17 +146,22 @@ dom.onDocument(events.zone.update, function(event) {
   // Note that currently, the willUpdate and didUpdate events are triggered 
even when the zone is not actually
   // updated. That may be a bug.
   if (content !== undefined) {
+    // @ts-ignore
     this.update(content);
   }
 
+  // @ts-ignore
   this.trigger(events.initializeComponents);
+  // @ts-ignore
   return this.trigger(events.zone.didUpdate);
 });
 
+// @ts-ignore
 dom.onDocument(events.zone.refresh, function(event) {
 
   // This event may be triggered on an element inside the zone, rather than on 
the zone itself. Scan upwards
   // to find the actual zone.
+  // @ts-ignore
   const zone = this.closest("[data-container-type=zone]");
 
   // A Zone inside a form will render some additional parameters to coordinate 
updates with the Form on the server.
@@ -148,7 +173,7 @@ dom.onDocument(events.zone.refresh, function(event) {
 
   return ajax(event.memo.url, {
     data: _.extend({ "t:zoneid": zone.element.id }, simpleIdParams, 
parameters, event.memo.parameters),
-    success(response) {
+    success(response: ResponseWrapper) {
       return zone.trigger(events.zone.update, {content: (response.json != null 
? response.json.content : undefined)});
     }
   }
@@ -156,6 +181,7 @@ dom.onDocument(events.zone.refresh, function(event) {
 });
 
 dom.onDocument("click", "a[data-async-trigger]", function(event){
+  // @ts-ignore
   const link = this.closest('a[data-async-trigger]');
 
   link.addClass("ajax-update");
@@ -163,16 +189,19 @@ dom.onDocument("click", "a[data-async-trigger]", 
function(event){
   ajax((link.attr("href")),
     {complete() { return link.removeClass("ajax-update"); }});
 
+  // @ts-ignore
   event.nativeEvent.preventDefault();
 
 });
 
-// Locates a zone element by its unique id attribute, and (deferred, to a 
later event loop cycle),
-// performs a standard refresh of the zone. This is primarily used by the 
core/ProgressiveDisplay component.
-//
-// * id - client id of the element
-// * url - URL to use to refresh the element.
-const deferredZoneUpdate = (id, url) => _.defer(function() {
+/**
+ * Locates a zone element by its unique id attribute, and (deferred, to a 
later event loop cycle),
+ * performs a standard refresh of the zone. This is primarily used by the 
core/ProgressiveDisplay component.
+ * 
+ * @param {string} id - client id of the element
+ * @param {string} url - URL to use to refresh the element.
+ */
+const deferredZoneUpdate = (id: string, url: string) => _.defer(function() {
   const zone = dom(id);
 
   if (zone === null) {
@@ -183,6 +212,6 @@ const deferredZoneUpdate = (id, url) => _.defer(function() {
   return zone.trigger(events.zone.refresh, { url });
 });
 
-    // Most of this module is document-level event handlers, but there's also 
some exports:
+// Most of this module is document-level event handlers, but there's also some 
exports:
 export default { deferredZoneUpdate, findZone };
 

Reply via email to