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 };