Major housekeeping.

This commit is contained in:
flash 2025-05-19 00:16:41 +00:00
parent f4d422cb69
commit 14f9eab077
Signed by: flash
GPG key ID: 2C9C2C574D47FE3E
36 changed files with 847 additions and 1448 deletions

519
package-lock.json generated
View file

@ -80,9 +80,9 @@
}
},
"node_modules/@swc/core": {
"version": "1.11.21",
"resolved": "https://registry.npmjs.org/@swc/core/-/core-1.11.21.tgz",
"integrity": "sha512-/Y3BJLcwd40pExmdar8MH2UGGvCBrqNN7hauOMckrEX2Ivcbv3IMhrbGX4od1dnF880Ed8y/E9aStZCIQi0EGw==",
"version": "1.11.24",
"resolved": "https://registry.npmjs.org/@swc/core/-/core-1.11.24.tgz",
"integrity": "sha512-MaQEIpfcEMzx3VWWopbofKJvaraqmL6HbLlw2bFZ7qYqYw3rkhM0cQVEgyzbHtTWwCwPMFZSC2DUbhlZgrMfLg==",
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
@ -97,16 +97,16 @@
"url": "https://opencollective.com/swc"
},
"optionalDependencies": {
"@swc/core-darwin-arm64": "1.11.21",
"@swc/core-darwin-x64": "1.11.21",
"@swc/core-linux-arm-gnueabihf": "1.11.21",
"@swc/core-linux-arm64-gnu": "1.11.21",
"@swc/core-linux-arm64-musl": "1.11.21",
"@swc/core-linux-x64-gnu": "1.11.21",
"@swc/core-linux-x64-musl": "1.11.21",
"@swc/core-win32-arm64-msvc": "1.11.21",
"@swc/core-win32-ia32-msvc": "1.11.21",
"@swc/core-win32-x64-msvc": "1.11.21"
"@swc/core-darwin-arm64": "1.11.24",
"@swc/core-darwin-x64": "1.11.24",
"@swc/core-linux-arm-gnueabihf": "1.11.24",
"@swc/core-linux-arm64-gnu": "1.11.24",
"@swc/core-linux-arm64-musl": "1.11.24",
"@swc/core-linux-x64-gnu": "1.11.24",
"@swc/core-linux-x64-musl": "1.11.24",
"@swc/core-win32-arm64-msvc": "1.11.24",
"@swc/core-win32-ia32-msvc": "1.11.24",
"@swc/core-win32-x64-msvc": "1.11.24"
},
"peerDependencies": {
"@swc/helpers": ">=0.5.17"
@ -118,9 +118,9 @@
}
},
"node_modules/@swc/core-darwin-arm64": {
"version": "1.11.21",
"resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.11.21.tgz",
"integrity": "sha512-v6gjw9YFWvKulCw3ZA1dY+LGMafYzJksm1mD4UZFZ9b36CyHFowYVYug1ajYRIRqEvvfIhHUNV660zTLoVFR8g==",
"version": "1.11.24",
"resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.11.24.tgz",
"integrity": "sha512-dhtVj0PC1APOF4fl5qT2neGjRLgHAAYfiVP8poJelhzhB/318bO+QCFWAiimcDoyMgpCXOhTp757gnoJJrheWA==",
"cpu": [
"arm64"
],
@ -134,9 +134,9 @@
}
},
"node_modules/@swc/core-darwin-x64": {
"version": "1.11.21",
"resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.11.21.tgz",
"integrity": "sha512-CUiTiqKlzskwswrx9Ve5NhNoab30L1/ScOfQwr1duvNlFvarC8fvQSgdtpw2Zh3MfnfNPpyLZnYg7ah4kbT9JQ==",
"version": "1.11.24",
"resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.11.24.tgz",
"integrity": "sha512-H/3cPs8uxcj2Fe3SoLlofN5JG6Ny5bl8DuZ6Yc2wr7gQFBmyBkbZEz+sPVgsID7IXuz7vTP95kMm1VL74SO5AQ==",
"cpu": [
"x64"
],
@ -150,9 +150,9 @@
}
},
"node_modules/@swc/core-linux-arm-gnueabihf": {
"version": "1.11.21",
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.11.21.tgz",
"integrity": "sha512-YyBTAFM/QPqt1PscD8hDmCLnqPGKmUZpqeE25HXY8OLjl2MUs8+O4KjwPZZ+OGxpdTbwuWFyMoxjcLy80JODvg==",
"version": "1.11.24",
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.11.24.tgz",
"integrity": "sha512-PHJgWEpCsLo/NGj+A2lXZ2mgGjsr96ULNW3+T3Bj2KTc8XtMUkE8tmY2Da20ItZOvPNC/69KroU7edyo1Flfbw==",
"cpu": [
"arm"
],
@ -166,9 +166,9 @@
}
},
"node_modules/@swc/core-linux-arm64-gnu": {
"version": "1.11.21",
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.11.21.tgz",
"integrity": "sha512-DQD+ooJmwpNsh4acrftdkuwl5LNxxg8U4+C/RJNDd7m5FP9Wo4c0URi5U0a9Vk/6sQNh9aSGcYChDpqCDWEcBw==",
"version": "1.11.24",
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.11.24.tgz",
"integrity": "sha512-C2FJb08+n5SD4CYWCTZx1uR88BN41ZieoHvI8A55hfVf2woT8+6ZiBzt74qW2g+ntZ535Jts5VwXAKdu41HpBg==",
"cpu": [
"arm64"
],
@ -182,9 +182,9 @@
}
},
"node_modules/@swc/core-linux-arm64-musl": {
"version": "1.11.21",
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.11.21.tgz",
"integrity": "sha512-y1L49+snt1a1gLTYPY641slqy55QotPdtRK9Y6jMi4JBQyZwxC8swWYlQWb+MyILwxA614fi62SCNZNznB3XSA==",
"version": "1.11.24",
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.11.24.tgz",
"integrity": "sha512-ypXLIdszRo0re7PNNaXN0+2lD454G8l9LPK/rbfRXnhLWDBPURxzKlLlU/YGd2zP98wPcVooMmegRSNOKfvErw==",
"cpu": [
"arm64"
],
@ -198,9 +198,9 @@
}
},
"node_modules/@swc/core-linux-x64-gnu": {
"version": "1.11.21",
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.11.21.tgz",
"integrity": "sha512-NesdBXv4CvVEaFUlqKj+GA4jJMNUzK2NtKOrUNEtTbXaVyNiXjFCSaDajMTedEB0jTAd9ybB0aBvwhgkJUWkWA==",
"version": "1.11.24",
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.11.24.tgz",
"integrity": "sha512-IM7d+STVZD48zxcgo69L0yYptfhaaE9cMZ+9OoMxirNafhKKXwoZuufol1+alEFKc+Wbwp+aUPe/DeWC/Lh3dg==",
"cpu": [
"x64"
],
@ -214,9 +214,9 @@
}
},
"node_modules/@swc/core-linux-x64-musl": {
"version": "1.11.21",
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.11.21.tgz",
"integrity": "sha512-qFV60pwpKVOdmX67wqQzgtSrUGWX9Cibnp1CXyqZ9Mmt8UyYGvmGu7p6PMbTyX7vdpVUvWVRf8DzrW2//wmVHg==",
"version": "1.11.24",
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.11.24.tgz",
"integrity": "sha512-DZByJaMVzSfjQKKQn3cqSeqwy6lpMaQDQQ4HPlch9FWtDx/dLcpdIhxssqZXcR2rhaQVIaRQsCqwV6orSDGAGw==",
"cpu": [
"x64"
],
@ -230,9 +230,9 @@
}
},
"node_modules/@swc/core-win32-arm64-msvc": {
"version": "1.11.21",
"resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.11.21.tgz",
"integrity": "sha512-DJJe9k6gXR/15ZZVLv1SKhXkFst8lYCeZRNHH99SlBodvu4slhh/MKQ6YCixINRhCwliHrpXPym8/5fOq8b7Ig==",
"version": "1.11.24",
"resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.11.24.tgz",
"integrity": "sha512-Q64Ytn23y9aVDKN5iryFi8mRgyHw3/kyjTjT4qFCa8AEb5sGUuSj//AUZ6c0J7hQKMHlg9do5Etvoe61V98/JQ==",
"cpu": [
"arm64"
],
@ -246,9 +246,9 @@
}
},
"node_modules/@swc/core-win32-ia32-msvc": {
"version": "1.11.21",
"resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.11.21.tgz",
"integrity": "sha512-TqEXuy6wedId7bMwLIr9byds+mKsaXVHctTN88R1UIBPwJA92Pdk0uxDgip0pEFzHB/ugU27g6d8cwUH3h2eIw==",
"version": "1.11.24",
"resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.11.24.tgz",
"integrity": "sha512-9pKLIisE/Hh2vJhGIPvSoTK4uBSPxNVyXHmOrtdDot4E1FUUI74Vi8tFdlwNbaj8/vusVnb8xPXsxF1uB0VgiQ==",
"cpu": [
"ia32"
],
@ -262,9 +262,9 @@
}
},
"node_modules/@swc/core-win32-x64-msvc": {
"version": "1.11.21",
"resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.11.21.tgz",
"integrity": "sha512-BT9BNNbMxdpUM1PPAkYtviaV0A8QcXttjs2MDtOeSqqvSJaPtyM+Fof2/+xSwQDmDEFzbGCcn75M5+xy3lGqpA==",
"version": "1.11.24",
"resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.11.24.tgz",
"integrity": "sha512-sybnXtOsdB+XvzVFlBVGgRHLqp3yRpHK7CrmpuDKszhj/QhmsaZzY/GHSeALlMtLup13M0gqbcQvsTNlAHTg3w==",
"cpu": [
"x64"
],
@ -357,9 +357,9 @@
"license": "ISC"
},
"node_modules/browserslist": {
"version": "4.24.4",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz",
"integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==",
"version": "4.24.5",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.5.tgz",
"integrity": "sha512-FDToo4Wo82hIdgc1CQ+NQD0hEhmpPjrZ3hiUgwgOG6IuTdlpr8jdjyG24P6cNP1yJpTLzS5OcGgSw0xmDU1/Tw==",
"funding": [
{
"type": "opencollective",
@ -376,10 +376,10 @@
],
"license": "MIT",
"dependencies": {
"caniuse-lite": "^1.0.30001688",
"electron-to-chromium": "^1.5.73",
"caniuse-lite": "^1.0.30001716",
"electron-to-chromium": "^1.5.149",
"node-releases": "^2.0.19",
"update-browserslist-db": "^1.1.1"
"update-browserslist-db": "^1.1.3"
},
"bin": {
"browserslist": "cli.js"
@ -417,9 +417,9 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001715",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001715.tgz",
"integrity": "sha512-7ptkFGMm2OAOgvZpwgA4yjQ5SQbrNVGdRjzH0pBdy1Fasvcr+KAeECmbCAECzTuDuoX0FCY8KzUxjf9+9kfZEw==",
"version": "1.0.30001718",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001718.tgz",
"integrity": "sha512-AflseV1ahcSunK53NfEs9gFWgOEmzr0f+kaMFA4xiLZlr9Hzt7HxcSpIFcnNCUkz6R6dWKa54rUz3HUmI3nVcw==",
"funding": [
{
"type": "opencollective",
@ -529,13 +529,13 @@
}
},
"node_modules/cssnano": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cssnano/-/cssnano-7.0.6.tgz",
"integrity": "sha512-54woqx8SCbp8HwvNZYn68ZFAepuouZW4lTwiMVnBErM3VkO7/Sd4oTOt3Zz3bPx3kxQ36aISppyXj2Md4lg8bw==",
"version": "7.0.7",
"resolved": "https://registry.npmjs.org/cssnano/-/cssnano-7.0.7.tgz",
"integrity": "sha512-evKu7yiDIF7oS+EIpwFlMF730ijRyLFaM2o5cTxRGJR9OKHKkc+qP443ZEVR9kZG0syaAJJCPJyfv5pbrxlSng==",
"license": "MIT",
"dependencies": {
"cssnano-preset-default": "^7.0.6",
"lilconfig": "^3.1.2"
"cssnano-preset-default": "^7.0.7",
"lilconfig": "^3.1.3"
},
"engines": {
"node": "^18.12.0 || ^20.9.0 || >=22.0"
@ -545,63 +545,63 @@
"url": "https://opencollective.com/cssnano"
},
"peerDependencies": {
"postcss": "^8.4.31"
"postcss": "^8.4.32"
}
},
"node_modules/cssnano-preset-default": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-7.0.6.tgz",
"integrity": "sha512-ZzrgYupYxEvdGGuqL+JKOY70s7+saoNlHSCK/OGn1vB2pQK8KSET8jvenzItcY+kA7NoWvfbb/YhlzuzNKjOhQ==",
"version": "7.0.7",
"resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-7.0.7.tgz",
"integrity": "sha512-jW6CG/7PNB6MufOrlovs1TvBTEVmhY45yz+bd0h6nw3h6d+1e+/TX+0fflZ+LzvZombbT5f+KC063w9VoHeHow==",
"license": "MIT",
"dependencies": {
"browserslist": "^4.23.3",
"browserslist": "^4.24.5",
"css-declaration-sorter": "^7.2.0",
"cssnano-utils": "^5.0.0",
"postcss-calc": "^10.0.2",
"postcss-colormin": "^7.0.2",
"postcss-convert-values": "^7.0.4",
"postcss-discard-comments": "^7.0.3",
"postcss-discard-duplicates": "^7.0.1",
"postcss-discard-empty": "^7.0.0",
"postcss-discard-overridden": "^7.0.0",
"postcss-merge-longhand": "^7.0.4",
"postcss-merge-rules": "^7.0.4",
"postcss-minify-font-values": "^7.0.0",
"postcss-minify-gradients": "^7.0.0",
"postcss-minify-params": "^7.0.2",
"postcss-minify-selectors": "^7.0.4",
"postcss-normalize-charset": "^7.0.0",
"postcss-normalize-display-values": "^7.0.0",
"postcss-normalize-positions": "^7.0.0",
"postcss-normalize-repeat-style": "^7.0.0",
"postcss-normalize-string": "^7.0.0",
"postcss-normalize-timing-functions": "^7.0.0",
"postcss-normalize-unicode": "^7.0.2",
"postcss-normalize-url": "^7.0.0",
"postcss-normalize-whitespace": "^7.0.0",
"postcss-ordered-values": "^7.0.1",
"postcss-reduce-initial": "^7.0.2",
"postcss-reduce-transforms": "^7.0.0",
"postcss-svgo": "^7.0.1",
"postcss-unique-selectors": "^7.0.3"
"cssnano-utils": "^5.0.1",
"postcss-calc": "^10.1.1",
"postcss-colormin": "^7.0.3",
"postcss-convert-values": "^7.0.5",
"postcss-discard-comments": "^7.0.4",
"postcss-discard-duplicates": "^7.0.2",
"postcss-discard-empty": "^7.0.1",
"postcss-discard-overridden": "^7.0.1",
"postcss-merge-longhand": "^7.0.5",
"postcss-merge-rules": "^7.0.5",
"postcss-minify-font-values": "^7.0.1",
"postcss-minify-gradients": "^7.0.1",
"postcss-minify-params": "^7.0.3",
"postcss-minify-selectors": "^7.0.5",
"postcss-normalize-charset": "^7.0.1",
"postcss-normalize-display-values": "^7.0.1",
"postcss-normalize-positions": "^7.0.1",
"postcss-normalize-repeat-style": "^7.0.1",
"postcss-normalize-string": "^7.0.1",
"postcss-normalize-timing-functions": "^7.0.1",
"postcss-normalize-unicode": "^7.0.3",
"postcss-normalize-url": "^7.0.1",
"postcss-normalize-whitespace": "^7.0.1",
"postcss-ordered-values": "^7.0.2",
"postcss-reduce-initial": "^7.0.3",
"postcss-reduce-transforms": "^7.0.1",
"postcss-svgo": "^7.0.2",
"postcss-unique-selectors": "^7.0.4"
},
"engines": {
"node": "^18.12.0 || ^20.9.0 || >=22.0"
},
"peerDependencies": {
"postcss": "^8.4.31"
"postcss": "^8.4.32"
}
},
"node_modules/cssnano-utils": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-5.0.0.tgz",
"integrity": "sha512-Uij0Xdxc24L6SirFr25MlwC2rCFX6scyUmuKpzI+JQ7cyqDEwD42fJ0xfB3yLfOnRDU5LKGgjQ9FA6LYh76GWQ==",
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-5.0.1.tgz",
"integrity": "sha512-ZIP71eQgG9JwjVZsTPSqhc6GHgEr53uJ7tK5///VfyWj6Xp2DBmixWHqJgPno+PqATzn48pL42ww9x5SSGmhZg==",
"license": "MIT",
"engines": {
"node": "^18.12.0 || ^20.9.0 || >=22.0"
},
"peerDependencies": {
"postcss": "^8.4.31"
"postcss": "^8.4.32"
}
},
"node_modules/csso": {
@ -703,9 +703,9 @@
}
},
"node_modules/electron-to-chromium": {
"version": "1.5.139",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.139.tgz",
"integrity": "sha512-GGnRYOTdN5LYpwbIr0rwP/ZHOQSvAF6TG0LSzp28uCBb9JiXHJGmaaKw29qjNJc5bGnnp6kXJqRnGMQoELwi5w==",
"version": "1.5.155",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.155.tgz",
"integrity": "sha512-ps5KcGGmwL8VaeJlvlDlu4fORQpv3+GIcF5I3f9tUKUlJ/wsysh6HU8P5L1XWRYeXfA0oJd4PyM8ds8zTFf6Ng==",
"license": "ISC"
},
"node_modules/entities": {
@ -928,12 +928,12 @@
}
},
"node_modules/postcss-colormin": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-7.0.2.tgz",
"integrity": "sha512-YntRXNngcvEvDbEjTdRWGU606eZvB5prmHG4BF0yLmVpamXbpsRJzevyy6MZVyuecgzI2AWAlvFi8DAeCqwpvA==",
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-7.0.3.tgz",
"integrity": "sha512-xZxQcSyIVZbSsl1vjoqZAcMYYdnJsIyG8OvqShuuqf12S88qQboxxEy0ohNCOLwVPXTU+hFHvJPACRL2B5ohTA==",
"license": "MIT",
"dependencies": {
"browserslist": "^4.23.3",
"browserslist": "^4.24.5",
"caniuse-api": "^3.0.0",
"colord": "^2.9.3",
"postcss-value-parser": "^4.2.0"
@ -942,140 +942,114 @@
"node": "^18.12.0 || ^20.9.0 || >=22.0"
},
"peerDependencies": {
"postcss": "^8.4.31"
"postcss": "^8.4.32"
}
},
"node_modules/postcss-convert-values": {
"version": "7.0.4",
"resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-7.0.4.tgz",
"integrity": "sha512-e2LSXPqEHVW6aoGbjV9RsSSNDO3A0rZLCBxN24zvxF25WknMPpX8Dm9UxxThyEbaytzggRuZxaGXqaOhxQ514Q==",
"version": "7.0.5",
"resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-7.0.5.tgz",
"integrity": "sha512-0VFhH8nElpIs3uXKnVtotDJJNX0OGYSZmdt4XfSfvOMrFw1jKfpwpZxfC4iN73CTM/MWakDEmsHQXkISYj4BXw==",
"license": "MIT",
"dependencies": {
"browserslist": "^4.23.3",
"browserslist": "^4.24.5",
"postcss-value-parser": "^4.2.0"
},
"engines": {
"node": "^18.12.0 || ^20.9.0 || >=22.0"
},
"peerDependencies": {
"postcss": "^8.4.31"
"postcss": "^8.4.32"
}
},
"node_modules/postcss-discard-comments": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-7.0.3.tgz",
"integrity": "sha512-q6fjd4WU4afNhWOA2WltHgCbkRhZPgQe7cXF74fuVB/ge4QbM9HEaOIzGSiMvM+g/cOsNAUGdf2JDzqA2F8iLA==",
"version": "7.0.4",
"resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-7.0.4.tgz",
"integrity": "sha512-6tCUoql/ipWwKtVP/xYiFf1U9QgJ0PUvxN7pTcsQ8Ns3Fnwq1pU5D5s1MhT/XySeLq6GXNvn37U46Ded0TckWg==",
"license": "MIT",
"dependencies": {
"postcss-selector-parser": "^6.1.2"
"postcss-selector-parser": "^7.1.0"
},
"engines": {
"node": "^18.12.0 || ^20.9.0 || >=22.0"
},
"peerDependencies": {
"postcss": "^8.4.31"
}
},
"node_modules/postcss-discard-comments/node_modules/postcss-selector-parser": {
"version": "6.1.2",
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
"integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
"license": "MIT",
"dependencies": {
"cssesc": "^3.0.0",
"util-deprecate": "^1.0.2"
},
"engines": {
"node": ">=4"
"postcss": "^8.4.32"
}
},
"node_modules/postcss-discard-duplicates": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-7.0.1.tgz",
"integrity": "sha512-oZA+v8Jkpu1ct/xbbrntHRsfLGuzoP+cpt0nJe5ED2FQF8n8bJtn7Bo28jSmBYwqgqnqkuSXJfSUEE7if4nClQ==",
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-7.0.2.tgz",
"integrity": "sha512-eTonaQvPZ/3i1ASDHOKkYwAybiM45zFIc7KXils4mQmHLqIswXD9XNOKEVxtTFnsmwYzF66u4LMgSr0abDlh5w==",
"license": "MIT",
"engines": {
"node": "^18.12.0 || ^20.9.0 || >=22.0"
},
"peerDependencies": {
"postcss": "^8.4.31"
"postcss": "^8.4.32"
}
},
"node_modules/postcss-discard-empty": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-7.0.0.tgz",
"integrity": "sha512-e+QzoReTZ8IAwhnSdp/++7gBZ/F+nBq9y6PomfwORfP7q9nBpK5AMP64kOt0bA+lShBFbBDcgpJ3X4etHg4lzA==",
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-7.0.1.tgz",
"integrity": "sha512-cFrJKZvcg/uxB6Ijr4l6qmn3pXQBna9zyrPC+sK0zjbkDUZew+6xDltSF7OeB7rAtzaaMVYSdbod+sZOCWnMOg==",
"license": "MIT",
"engines": {
"node": "^18.12.0 || ^20.9.0 || >=22.0"
},
"peerDependencies": {
"postcss": "^8.4.31"
"postcss": "^8.4.32"
}
},
"node_modules/postcss-discard-overridden": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-7.0.0.tgz",
"integrity": "sha512-GmNAzx88u3k2+sBTZrJSDauR0ccpE24omTQCVmaTTZFz1du6AasspjaUPMJ2ud4RslZpoFKyf+6MSPETLojc6w==",
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-7.0.1.tgz",
"integrity": "sha512-7c3MMjjSZ/qYrx3uc1940GSOzN1Iqjtlqe8uoSg+qdVPYyRb0TILSqqmtlSFuE4mTDECwsm397Ya7iXGzfF7lg==",
"license": "MIT",
"engines": {
"node": "^18.12.0 || ^20.9.0 || >=22.0"
},
"peerDependencies": {
"postcss": "^8.4.31"
"postcss": "^8.4.32"
}
},
"node_modules/postcss-merge-longhand": {
"version": "7.0.4",
"resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-7.0.4.tgz",
"integrity": "sha512-zer1KoZA54Q8RVHKOY5vMke0cCdNxMP3KBfDerjH/BYHh4nCIh+1Yy0t1pAEQF18ac/4z3OFclO+ZVH8azjR4A==",
"version": "7.0.5",
"resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-7.0.5.tgz",
"integrity": "sha512-Kpu5v4Ys6QI59FxmxtNB/iHUVDn9Y9sYw66D6+SZoIk4QTz1prC4aYkhIESu+ieG1iylod1f8MILMs1Em3mmIw==",
"license": "MIT",
"dependencies": {
"postcss-value-parser": "^4.2.0",
"stylehacks": "^7.0.4"
"stylehacks": "^7.0.5"
},
"engines": {
"node": "^18.12.0 || ^20.9.0 || >=22.0"
},
"peerDependencies": {
"postcss": "^8.4.31"
"postcss": "^8.4.32"
}
},
"node_modules/postcss-merge-rules": {
"version": "7.0.4",
"resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-7.0.4.tgz",
"integrity": "sha512-ZsaamiMVu7uBYsIdGtKJ64PkcQt6Pcpep/uO90EpLS3dxJi6OXamIobTYcImyXGoW0Wpugh7DSD3XzxZS9JCPg==",
"version": "7.0.5",
"resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-7.0.5.tgz",
"integrity": "sha512-ZonhuSwEaWA3+xYbOdJoEReKIBs5eDiBVLAGpYZpNFPzXZcEE5VKR7/qBEQvTZpiwjqhhqEQ+ax5O3VShBj9Wg==",
"license": "MIT",
"dependencies": {
"browserslist": "^4.23.3",
"browserslist": "^4.24.5",
"caniuse-api": "^3.0.0",
"cssnano-utils": "^5.0.0",
"postcss-selector-parser": "^6.1.2"
"cssnano-utils": "^5.0.1",
"postcss-selector-parser": "^7.1.0"
},
"engines": {
"node": "^18.12.0 || ^20.9.0 || >=22.0"
},
"peerDependencies": {
"postcss": "^8.4.31"
}
},
"node_modules/postcss-merge-rules/node_modules/postcss-selector-parser": {
"version": "6.1.2",
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
"integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
"license": "MIT",
"dependencies": {
"cssesc": "^3.0.0",
"util-deprecate": "^1.0.2"
},
"engines": {
"node": ">=4"
"postcss": "^8.4.32"
}
},
"node_modules/postcss-minify-font-values": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-7.0.0.tgz",
"integrity": "sha512-2ckkZtgT0zG8SMc5aoNwtm5234eUx1GGFJKf2b1bSp8UflqaeFzR50lid4PfqVI9NtGqJ2J4Y7fwvnP/u1cQog==",
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-7.0.1.tgz",
"integrity": "sha512-2m1uiuJeTplll+tq4ENOQSzB8LRnSUChBv7oSyFLsJRtUgAAJGP6LLz0/8lkinTgxrmJSPOEhgY1bMXOQ4ZXhQ==",
"license": "MIT",
"dependencies": {
"postcss-value-parser": "^4.2.0"
@ -1084,88 +1058,75 @@
"node": "^18.12.0 || ^20.9.0 || >=22.0"
},
"peerDependencies": {
"postcss": "^8.4.31"
"postcss": "^8.4.32"
}
},
"node_modules/postcss-minify-gradients": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-7.0.0.tgz",
"integrity": "sha512-pdUIIdj/C93ryCHew0UgBnL2DtUS3hfFa5XtERrs4x+hmpMYGhbzo6l/Ir5de41O0GaKVpK1ZbDNXSY6GkXvtg==",
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-7.0.1.tgz",
"integrity": "sha512-X9JjaysZJwlqNkJbUDgOclyG3jZEpAMOfof6PUZjPnPrePnPG62pS17CjdM32uT1Uq1jFvNSff9l7kNbmMSL2A==",
"license": "MIT",
"dependencies": {
"colord": "^2.9.3",
"cssnano-utils": "^5.0.0",
"cssnano-utils": "^5.0.1",
"postcss-value-parser": "^4.2.0"
},
"engines": {
"node": "^18.12.0 || ^20.9.0 || >=22.0"
},
"peerDependencies": {
"postcss": "^8.4.31"
"postcss": "^8.4.32"
}
},
"node_modules/postcss-minify-params": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-7.0.2.tgz",
"integrity": "sha512-nyqVLu4MFl9df32zTsdcLqCFfE/z2+f8GE1KHPxWOAmegSo6lpV2GNy5XQvrzwbLmiU7d+fYay4cwto1oNdAaQ==",
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-7.0.3.tgz",
"integrity": "sha512-vUKV2+f5mtjewYieanLX0xemxIp1t0W0H/D11u+kQV/MWdygOO7xPMkbK+r9P6Lhms8MgzKARF/g5OPXhb8tgg==",
"license": "MIT",
"dependencies": {
"browserslist": "^4.23.3",
"cssnano-utils": "^5.0.0",
"browserslist": "^4.24.5",
"cssnano-utils": "^5.0.1",
"postcss-value-parser": "^4.2.0"
},
"engines": {
"node": "^18.12.0 || ^20.9.0 || >=22.0"
},
"peerDependencies": {
"postcss": "^8.4.31"
"postcss": "^8.4.32"
}
},
"node_modules/postcss-minify-selectors": {
"version": "7.0.4",
"resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-7.0.4.tgz",
"integrity": "sha512-JG55VADcNb4xFCf75hXkzc1rNeURhlo7ugf6JjiiKRfMsKlDzN9CXHZDyiG6x/zGchpjQS+UAgb1d4nqXqOpmA==",
"version": "7.0.5",
"resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-7.0.5.tgz",
"integrity": "sha512-x2/IvofHcdIrAm9Q+p06ZD1h6FPcQ32WtCRVodJLDR+WMn8EVHI1kvLxZuGKz/9EY5nAmI6lIQIrpo4tBy5+ug==",
"license": "MIT",
"dependencies": {
"cssesc": "^3.0.0",
"postcss-selector-parser": "^6.1.2"
"postcss-selector-parser": "^7.1.0"
},
"engines": {
"node": "^18.12.0 || ^20.9.0 || >=22.0"
},
"peerDependencies": {
"postcss": "^8.4.31"
}
},
"node_modules/postcss-minify-selectors/node_modules/postcss-selector-parser": {
"version": "6.1.2",
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
"integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
"license": "MIT",
"dependencies": {
"cssesc": "^3.0.0",
"util-deprecate": "^1.0.2"
},
"engines": {
"node": ">=4"
"postcss": "^8.4.32"
}
},
"node_modules/postcss-normalize-charset": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-7.0.0.tgz",
"integrity": "sha512-ABisNUXMeZeDNzCQxPxBCkXexvBrUHV+p7/BXOY+ulxkcjUZO0cp8ekGBwvIh2LbCwnWbyMPNJVtBSdyhM2zYQ==",
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-7.0.1.tgz",
"integrity": "sha512-sn413ofhSQHlZFae//m9FTOfkmiZ+YQXsbosqOWRiVQncU2BA3daX3n0VF3cG6rGLSFVc5Di/yns0dFfh8NFgQ==",
"license": "MIT",
"engines": {
"node": "^18.12.0 || ^20.9.0 || >=22.0"
},
"peerDependencies": {
"postcss": "^8.4.31"
"postcss": "^8.4.32"
}
},
"node_modules/postcss-normalize-display-values": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-7.0.0.tgz",
"integrity": "sha512-lnFZzNPeDf5uGMPYgGOw7v0BfB45+irSRz9gHQStdkkhiM0gTfvWkWB5BMxpn0OqgOQuZG/mRlZyJxp0EImr2Q==",
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-7.0.1.tgz",
"integrity": "sha512-E5nnB26XjSYz/mGITm6JgiDpAbVuAkzXwLzRZtts19jHDUBFxZ0BkXAehy0uimrOjYJbocby4FVswA/5noOxrQ==",
"license": "MIT",
"dependencies": {
"postcss-value-parser": "^4.2.0"
@ -1174,13 +1135,13 @@
"node": "^18.12.0 || ^20.9.0 || >=22.0"
},
"peerDependencies": {
"postcss": "^8.4.31"
"postcss": "^8.4.32"
}
},
"node_modules/postcss-normalize-positions": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-7.0.0.tgz",
"integrity": "sha512-I0yt8wX529UKIGs2y/9Ybs2CelSvItfmvg/DBIjTnoUSrPxSV7Z0yZ8ShSVtKNaV/wAY+m7bgtyVQLhB00A1NQ==",
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-7.0.1.tgz",
"integrity": "sha512-pB/SzrIP2l50ZIYu+yQZyMNmnAcwyYb9R1fVWPRxm4zcUFCY2ign7rcntGFuMXDdd9L2pPNUgoODDk91PzRZuQ==",
"license": "MIT",
"dependencies": {
"postcss-value-parser": "^4.2.0"
@ -1189,13 +1150,13 @@
"node": "^18.12.0 || ^20.9.0 || >=22.0"
},
"peerDependencies": {
"postcss": "^8.4.31"
"postcss": "^8.4.32"
}
},
"node_modules/postcss-normalize-repeat-style": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-7.0.0.tgz",
"integrity": "sha512-o3uSGYH+2q30ieM3ppu9GTjSXIzOrRdCUn8UOMGNw7Af61bmurHTWI87hRybrP6xDHvOe5WlAj3XzN6vEO8jLw==",
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-7.0.1.tgz",
"integrity": "sha512-NsSQJ8zj8TIDiF0ig44Byo3Jk9e4gNt9x2VIlJudnQQ5DhWAHJPF4Tr1ITwyHio2BUi/I6Iv0HRO7beHYOloYQ==",
"license": "MIT",
"dependencies": {
"postcss-value-parser": "^4.2.0"
@ -1204,13 +1165,13 @@
"node": "^18.12.0 || ^20.9.0 || >=22.0"
},
"peerDependencies": {
"postcss": "^8.4.31"
"postcss": "^8.4.32"
}
},
"node_modules/postcss-normalize-string": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-7.0.0.tgz",
"integrity": "sha512-w/qzL212DFVOpMy3UGyxrND+Kb0fvCiBBujiaONIihq7VvtC7bswjWgKQU/w4VcRyDD8gpfqUiBQ4DUOwEJ6Qg==",
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-7.0.1.tgz",
"integrity": "sha512-QByrI7hAhsoze992kpbMlJSbZ8FuCEc1OT9EFbZ6HldXNpsdpZr+YXC5di3UEv0+jeZlHbZcoCADgb7a+lPmmQ==",
"license": "MIT",
"dependencies": {
"postcss-value-parser": "^4.2.0"
@ -1219,13 +1180,13 @@
"node": "^18.12.0 || ^20.9.0 || >=22.0"
},
"peerDependencies": {
"postcss": "^8.4.31"
"postcss": "^8.4.32"
}
},
"node_modules/postcss-normalize-timing-functions": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-7.0.0.tgz",
"integrity": "sha512-tNgw3YV0LYoRwg43N3lTe3AEWZ66W7Dh7lVEpJbHoKOuHc1sLrzMLMFjP8SNULHaykzsonUEDbKedv8C+7ej6g==",
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-7.0.1.tgz",
"integrity": "sha512-bHifyuuSNdKKsnNJ0s8fmfLMlvsQwYVxIoUBnowIVl2ZAdrkYQNGVB4RxjfpvkMjipqvbz0u7feBZybkl/6NJg==",
"license": "MIT",
"dependencies": {
"postcss-value-parser": "^4.2.0"
@ -1234,29 +1195,29 @@
"node": "^18.12.0 || ^20.9.0 || >=22.0"
},
"peerDependencies": {
"postcss": "^8.4.31"
"postcss": "^8.4.32"
}
},
"node_modules/postcss-normalize-unicode": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-7.0.2.tgz",
"integrity": "sha512-ztisabK5C/+ZWBdYC+Y9JCkp3M9qBv/XFvDtSw0d/XwfT3UaKeW/YTm/MD/QrPNxuecia46vkfEhewjwcYFjkg==",
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-7.0.3.tgz",
"integrity": "sha512-EcoA29LvG3F+EpOh03iqu+tJY3uYYKzArqKJHxDhUYLa2u58aqGq16K6/AOsXD9yqLN8O6y9mmePKN5cx6krOw==",
"license": "MIT",
"dependencies": {
"browserslist": "^4.23.3",
"browserslist": "^4.24.5",
"postcss-value-parser": "^4.2.0"
},
"engines": {
"node": "^18.12.0 || ^20.9.0 || >=22.0"
},
"peerDependencies": {
"postcss": "^8.4.31"
"postcss": "^8.4.32"
}
},
"node_modules/postcss-normalize-url": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-7.0.0.tgz",
"integrity": "sha512-+d7+PpE+jyPX1hDQZYG+NaFD+Nd2ris6r8fPTBAjE8z/U41n/bib3vze8x7rKs5H1uEw5ppe9IojewouHk0klQ==",
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-7.0.1.tgz",
"integrity": "sha512-sUcD2cWtyK1AOL/82Fwy1aIVm/wwj5SdZkgZ3QiUzSzQQofrbq15jWJ3BA7Z+yVRwamCjJgZJN0I9IS7c6tgeQ==",
"license": "MIT",
"dependencies": {
"postcss-value-parser": "^4.2.0"
@ -1265,13 +1226,13 @@
"node": "^18.12.0 || ^20.9.0 || >=22.0"
},
"peerDependencies": {
"postcss": "^8.4.31"
"postcss": "^8.4.32"
}
},
"node_modules/postcss-normalize-whitespace": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-7.0.0.tgz",
"integrity": "sha512-37/toN4wwZErqohedXYqWgvcHUGlT8O/m2jVkAfAe9Bd4MzRqlBmXrJRePH0e9Wgnz2X7KymTgTOaaFizQe3AQ==",
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-7.0.1.tgz",
"integrity": "sha512-vsbgFHMFQrJBJKrUFJNZ2pgBeBkC2IvvoHjz1to0/0Xk7sII24T0qFOiJzG6Fu3zJoq/0yI4rKWi7WhApW+EFA==",
"license": "MIT",
"dependencies": {
"postcss-value-parser": "^4.2.0"
@ -1280,45 +1241,45 @@
"node": "^18.12.0 || ^20.9.0 || >=22.0"
},
"peerDependencies": {
"postcss": "^8.4.31"
"postcss": "^8.4.32"
}
},
"node_modules/postcss-ordered-values": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-7.0.1.tgz",
"integrity": "sha512-irWScWRL6nRzYmBOXReIKch75RRhNS86UPUAxXdmW/l0FcAsg0lvAXQCby/1lymxn/o0gVa6Rv/0f03eJOwHxw==",
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-7.0.2.tgz",
"integrity": "sha512-AMJjt1ECBffF7CEON/Y0rekRLS6KsePU6PRP08UqYW4UGFRnTXNrByUzYK1h8AC7UWTZdQ9O3Oq9kFIhm0SFEw==",
"license": "MIT",
"dependencies": {
"cssnano-utils": "^5.0.0",
"cssnano-utils": "^5.0.1",
"postcss-value-parser": "^4.2.0"
},
"engines": {
"node": "^18.12.0 || ^20.9.0 || >=22.0"
},
"peerDependencies": {
"postcss": "^8.4.31"
"postcss": "^8.4.32"
}
},
"node_modules/postcss-reduce-initial": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-7.0.2.tgz",
"integrity": "sha512-pOnu9zqQww7dEKf62Nuju6JgsW2V0KRNBHxeKohU+JkHd/GAH5uvoObqFLqkeB2n20mr6yrlWDvo5UBU5GnkfA==",
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-7.0.3.tgz",
"integrity": "sha512-RFvkZaqiWtGMlVjlUHpaxGqEL27lgt+Q2Ixjf83CRAzqdo+TsDyGPtJUbPx2MuYIJ+sCQc2TrOvRnhcXQfgIVA==",
"license": "MIT",
"dependencies": {
"browserslist": "^4.23.3",
"browserslist": "^4.24.5",
"caniuse-api": "^3.0.0"
},
"engines": {
"node": "^18.12.0 || ^20.9.0 || >=22.0"
},
"peerDependencies": {
"postcss": "^8.4.31"
"postcss": "^8.4.32"
}
},
"node_modules/postcss-reduce-transforms": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-7.0.0.tgz",
"integrity": "sha512-pnt1HKKZ07/idH8cpATX/ujMbtOGhUfE+m8gbqwJE05aTaNw8gbo34a2e3if0xc0dlu75sUOiqvwCGY3fzOHew==",
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-7.0.1.tgz",
"integrity": "sha512-MhyEbfrm+Mlp/36hvZ9mT9DaO7dbncU0CvWI8V93LRkY6IYlu38OPg3FObnuKTUxJ4qA8HpurdQOo5CyqqO76g==",
"license": "MIT",
"dependencies": {
"postcss-value-parser": "^4.2.0"
@ -1327,7 +1288,7 @@
"node": "^18.12.0 || ^20.9.0 || >=22.0"
},
"peerDependencies": {
"postcss": "^8.4.31"
"postcss": "^8.4.32"
}
},
"node_modules/postcss-selector-parser": {
@ -1344,9 +1305,9 @@
}
},
"node_modules/postcss-svgo": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-7.0.1.tgz",
"integrity": "sha512-0WBUlSL4lhD9rA5k1e5D8EN5wCEyZD6HJk0jIvRxl+FDVOMlJ7DePHYWGGVc5QRqrJ3/06FTXM0bxjmJpmTPSA==",
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-7.0.2.tgz",
"integrity": "sha512-5Dzy66JlnRM6pkdOTF8+cGsB1fnERTE8Nc+Eed++fOWo1hdsBptCsbG8UuJkgtZt75bRtMJIrPeZmtfANixdFA==",
"license": "MIT",
"dependencies": {
"postcss-value-parser": "^4.2.0",
@ -1356,35 +1317,22 @@
"node": "^18.12.0 || ^20.9.0 || >= 18"
},
"peerDependencies": {
"postcss": "^8.4.31"
"postcss": "^8.4.32"
}
},
"node_modules/postcss-unique-selectors": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-7.0.3.tgz",
"integrity": "sha512-J+58u5Ic5T1QjP/LDV9g3Cx4CNOgB5vz+kM6+OxHHhFACdcDeKhBXjQmB7fnIZM12YSTvsL0Opwco83DmacW2g==",
"version": "7.0.4",
"resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-7.0.4.tgz",
"integrity": "sha512-pmlZjsmEAG7cHd7uK3ZiNSW6otSZ13RHuZ/4cDN/bVglS5EpF2r2oxY99SuOHa8m7AWoBCelTS3JPpzsIs8skQ==",
"license": "MIT",
"dependencies": {
"postcss-selector-parser": "^6.1.2"
"postcss-selector-parser": "^7.1.0"
},
"engines": {
"node": "^18.12.0 || ^20.9.0 || >=22.0"
},
"peerDependencies": {
"postcss": "^8.4.31"
}
},
"node_modules/postcss-unique-selectors/node_modules/postcss-selector-parser": {
"version": "6.1.2",
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
"integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
"license": "MIT",
"dependencies": {
"cssesc": "^3.0.0",
"util-deprecate": "^1.0.2"
},
"engines": {
"node": ">=4"
"postcss": "^8.4.32"
}
},
"node_modules/postcss-value-parser": {
@ -1431,32 +1379,19 @@
}
},
"node_modules/stylehacks": {
"version": "7.0.4",
"resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-7.0.4.tgz",
"integrity": "sha512-i4zfNrGMt9SB4xRK9L83rlsFCgdGANfeDAYacO1pkqcE7cRHPdWHwnKZVz7WY17Veq/FvyYsRAU++Ga+qDFIww==",
"version": "7.0.5",
"resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-7.0.5.tgz",
"integrity": "sha512-5kNb7V37BNf0Q3w+1pxfa+oiNPS++/b4Jil9e/kPDgrk1zjEd6uR7SZeJiYaLYH6RRSC1XX2/37OTeU/4FvuIA==",
"license": "MIT",
"dependencies": {
"browserslist": "^4.23.3",
"postcss-selector-parser": "^6.1.2"
"browserslist": "^4.24.5",
"postcss-selector-parser": "^7.1.0"
},
"engines": {
"node": "^18.12.0 || ^20.9.0 || >=22.0"
},
"peerDependencies": {
"postcss": "^8.4.31"
}
},
"node_modules/stylehacks/node_modules/postcss-selector-parser": {
"version": "6.1.2",
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
"integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
"license": "MIT",
"dependencies": {
"cssesc": "^3.0.0",
"util-deprecate": "^1.0.2"
},
"engines": {
"node": ">=4"
"postcss": "^8.4.32"
}
},
"node_modules/svgo": {
@ -1494,13 +1429,13 @@
}
},
"node_modules/terser": {
"version": "5.39.0",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.39.0.tgz",
"integrity": "sha512-LBAhFyLho16harJoWMg/nZsQYgTrg5jXOn2nCYjRUcZZEdE3qa2zb8QEDRUGVZBW4rlazf2fxkg8tztybTaqWw==",
"version": "5.39.2",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.39.2.tgz",
"integrity": "sha512-yEPUmWve+VA78bI71BW70Dh0TuV4HHd+I5SHOAfS1+QBOmvmCiiffgjR8ryyEd3KIfvPGFqoADt8LdQ6XpXIvg==",
"license": "BSD-2-Clause",
"dependencies": {
"@jridgewell/source-map": "^0.3.3",
"acorn": "^8.8.2",
"acorn": "^8.14.0",
"commander": "^2.20.0",
"source-map-support": "~0.5.20"
},

View file

@ -1,138 +1,41 @@
#include easings.js
// This probably need a bit of a rewrite at some point to allow starting for arbitrary time points
const MamiAnimate = info => {
if(typeof info !== 'object')
throw 'info must be an object';
let onUpdate;
if('update' in info && typeof info.update === 'function')
onUpdate = info.update;
if(onUpdate === undefined)
throw 'update is a required parameter for info';
let duration;
if('duration' in info)
duration = parseFloat(info.duration);
if(duration <= 0)
throw 'duration is a required parameter for info and must be greater than 0';
let onStart;
if('start' in info && typeof info.start === 'function')
onStart = info.start;
let onEnd;
if('end' in info && typeof info.end === 'function')
onEnd = info.end;
let onCancel;
if('cancel' in info && typeof info.cancel === 'function')
onCancel = info.cancel;
let easing;
if('easing' in info)
easing = info.easing;
let delayed;
if('delayed' in info)
delayed = !!info.delayed;
let async;
if('async' in info)
async = !!info.async;
const easingType = typeof easing;
if(easingType !== 'function') {
if(easingType === 'string' && easing in MamiEasings)
const MamiAnimate = ({ update, duration, easing='linear', signal=null }) => {
return new Promise((resolve, reject) => {
if(typeof update !== 'function')
throw new Error('update argument must be a function');
if(typeof duration !== 'number' || duration < 0)
throw new Error('duration argument must be a positive number');
if(typeof easing === 'string' && easing in MamiEasings)
easing = MamiEasings[easing];
else
easing = MamiEasings.linear;
}
else if(typeof easing !== 'function')
throw new Error('easing argument was must be an acceptable easing name or an easing function');
if(signal !== null && !(signal instanceof AbortSignal))
throw new Error('signal must be null or an instance of AbortSignal');
if(typeof window.mami?.settings?.get === 'function')
duration *= mami.settings.get('dbgAnimDurationMulti');
let start = null;
let tStart, tLast,
cancel = false,
tRawCompletion = 0,
tCompletion = 0,
started = !delayed;
const update = tCurrent => {
if(tStart === undefined) {
tStart = tCurrent;
if(onStart !== undefined)
onStart();
}
const tElapsed = tCurrent - tStart;
tRawCompletion = Math.min(1, Math.max(0, tElapsed / duration));
tCompletion = easing(tRawCompletion);
onUpdate(tCompletion, tRawCompletion);
if(tElapsed < duration) {
if(cancel) {
if(onCancel !== undefined)
onCancel(tCompletion, tRawCompletion);
const frame = time => {
if(signal?.aborted) {
reject(signal.reason);
return;
}
tLast = tCurrent;
requestAnimationFrame(update);
} else if(onEnd !== undefined)
onEnd();
};
start ??= time;
let promise;
if(async)
promise = new Promise((resolve, reject) => {
if(onCancel === undefined) {
onCancel = reject;
} else {
const realOnCancel = onCancel;
onCancel = (...args) => {
realOnCancel(...args);
reject(...args);
};
const elapsed = time - start;
const completion = Math.min(1, Math.max(0, elapsed / duration));
update(easing(completion), completion);
if(elapsed < duration) {
requestAnimationFrame(frame);
return;
}
if(onEnd === undefined) {
onEnd = resolve;
} else {
const realOnEnd = onEnd;
onEnd = (...args) => {
realOnEnd(...args);
resolve(...args);
};
}
resolve();
};
if(!delayed)
requestAnimationFrame(update);
});
if(!delayed) {
if(promise !== undefined)
return promise;
requestAnimationFrame(update);
}
return {
get completion() { return tCompletion; },
get rawCompletion() { return tRawCompletion; },
start: () => {
if(!started) {
started = true;
requestAnimationFrame(update);
}
if(promise !== undefined)
return promise;
},
cancel: () => {
cancel = true;
},
};
requestAnimationFrame(frame);
});
};

View file

@ -1,184 +0,0 @@
const MamiArgs = (argName, input, builder) => {
if(input !== undefined && typeof input !== 'object')
throw `${argName} must be an object or undefined`;
if(typeof builder !== 'function')
throw 'builder must be a function';
const args = new Map;
builder(name => {
if(typeof name !== 'string')
throw 'name must be a string';
const checkDefined = () => {
if(args.has(name))
throw `${name} has already been defined`;
};
checkDefined();
let created = false;
const checkCreated = () => {
if(created)
throw 'argument has already been defined';
};
const info = {
name: name,
type: undefined,
filter: undefined,
constraint: undefined,
fallback: undefined,
throw: undefined,
required: undefined,
min: undefined,
max: undefined,
};
const blueprint = {
type: value => {
if(typeof value !== 'string' && !Array.isArray(value))
throw 'type must be a javascript type or array of valid string values';
checkCreated();
info.type = value;
return blueprint;
},
default: value => {
checkCreated();
info.fallback = value;
if(info.type === undefined && info.constraint === undefined)
info.type = typeof info.fallback;
return blueprint;
},
filter: value => {
if(typeof value !== 'function')
throw 'filter must be a function';
checkCreated();
info.filter = value;
return blueprint;
},
constraint: value => {
if(typeof value !== 'function')
throw 'constraint must be a function';
checkCreated();
info.constraint = value;
return blueprint;
},
throw: value => {
checkCreated();
info.throw = value === undefined || value === true;
return blueprint;
},
required: value => {
checkCreated();
info.required = value === undefined || value === true;
if(info.required && info.throw === undefined)
info.throw = true;
return blueprint;
},
min: value => {
checkCreated();
if(typeof value !== 'number')
throw 'value must be a number';
info.min = value;
return blueprint;
},
max: value => {
checkCreated();
if(typeof value !== 'number')
throw 'value must be a number';
info.max = value;
return blueprint;
},
done: () => {
checkCreated();
checkDefined();
args.set(name, info);
},
};
return blueprint;
});
const inputIsNull = input === undefined || input === null;
if(inputIsNull)
input = {};
const output = {};
for(const [name, info] of args) {
let value = info.fallback;
if(info.name in input) {
const defaultOrThrow = ex => {
if(info.throw)
throw ex;
value = info.fallback;
};
value = input[info.name];
if(info.type !== undefined) {
if(Array.isArray(info.type)) {
if(!info.type.includes(value))
defaultOrThrow(`${info.name} must match an enum value`);
} else {
const type = typeof value;
let resolved = false;
if(type !== info.type) {
if(type === 'string') {
if(info.type === 'number') {
value = parseFloat(value);
resolved = true;
if(info.min !== undefined && value < info.min)
value = info.min;
else if(info.max !== undefined && value > info.max)
value = info.max;
} else if(info.type === 'boolean') {
value = !!value;
resolved = true;
}
} else if(info.type === 'string') {
value = value.toString();
resolved = true;
}
} else resolved = true;
if(!resolved)
defaultOrThrow(`${info.name} must be of type ${info.type}`);
}
}
if(info.constraint !== undefined && !info.constraint(value))
defaultOrThrow(`${info.name} did not fit within constraints`);
// you could have a devious streak with this one cuz the checks aren't rerun
// but surely you wouldn't fuck yourself over like that
if(info.filter !== undefined)
value = info.filter(value);
} else if(info.required) {
if(inputIsNull)
throw `${argName} must be a non-null object`;
throw `${argName} is missing required key ${info.name}`;
}
output[info.name] = value;
}
return output;
};

View file

@ -1,27 +0,0 @@
const $arrayRemoveAt = function(array, index) {
array.splice(index, 1);
};
const $arrayRemoveValue = function(array, item) {
let index;
while(array.length > 0 && (index = array.indexOf(item)) >= 0)
$arrayRemoveAt(array, index);
};
const $arrayRemoveAny = function(array, predicate) {
let index;
while(array.length > 0 && (index = array.findIndex(predicate)) >= 0)
$arrayRemoveAt(array, index);
};
const $arrayShuffle = function(array) {
if(array.length < 2)
return;
for(let i = array.length - 1; i > 0; --i) {
const j = Math.floor(Math.random() * (i + 1));
const tmp = array[i];
array[i] = array[j];
array[j] = tmp;
}
};

View file

@ -1,10 +1,7 @@
#include srle.js
const MamiDetectAutoPlaySource = '/+NIxA!!FhpbmcA#PA$wA@fgAV$#q$#/$#A#OUxBTUUzLjEwMAIeA!)UCCQC8CIA@gA@H49wpKWgA!%&/+MYxA$NIA$ExBTUUzLjEwMFV^%/+MYxDsA@NIA$FV&&/+MYxHYA@NIA$FV&&';
const MamiDetectAutoPlaySource = 'data:audio/mpeg;base64,/+NIxAAAAAAAAAAAAFhpbmcAAAAPAAAAAwAAAfgAVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq////////////////////////////////////////////AAAAOUxBTUUzLjEwMAIeAAAAAAAAAAAUCCQC8CIAAAgAAAH49wpKWgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/+MYxAAAAANIAAAAAExBTUUzLjEwMFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV/+MYxDsAAANIAAAAAFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV/+MYxHYAAANIAAAAAFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV';
const MamiDetectAutoPlay = async () => {
try {
await $element('audio', { src: 'data:audio/mpeg;base64,' + MamiSRLE.decode(MamiDetectAutoPlaySource) }).play();
await $element('audio', { src: MamiDetectAutoPlaySource }).play();
} catch(ex) {
return false;
}

View file

@ -1,19 +1,2 @@
const MamiSleep = durationMs => new Promise(resolve => { setTimeout(resolve, durationMs); });
const MamiWaitVisible = () => {
return new Promise(resolve => {
if(document.visibilityState === 'visible') {
resolve();
return;
}
const handler = () => {
if(document.visibilityState === 'visible') {
window.removeEventListener('visibilitychange', handler);
resolve();
}
};
window.addEventListener('visibilitychange', handler);
});
};
const MamiTimeout = (promise, time) => Promise.race([promise, new Promise((resolve, reject) => setTimeout(reject, time))]);

View file

@ -1,4 +1,3 @@
#include args.js
#include colour.js
#include controls/tabs.js
#include colpick/tgrid.jsx
@ -7,22 +6,32 @@
#include colpick/vhex.jsx
#include colpick/vraw.jsx
const MamiColourPicker = function(options) {
options = MamiArgs('options', options, define => {
define('colour').default(0).filter(value => Math.min(0xFFFFFF, Math.max(0, value))).done();
define('posX').default(-1).done();
define('posY').default(-1).done();
define('flashii').type('object').default(null).done();
define('presets').default([]).constraint(value => Array.isArray(value)).done();
define('showPresetsTab').default(true).done();
define('showGridTab').default(true).done();
define('showSlidersTab').default(true).done();
define('showHexValue').default(true).done();
define('showRawValue').default(true).done();
define('showDialogButtons').default(true).done();
});
const MamiColourPicker = function({
colour=0,
posX=-1,
posY=-1,
flashii=null,
presets=[],
showPresetsTab=true,
showGridTab=true,
showSlidersTab=true,
showHexValue=true,
showRawValue=true,
showDialogButtons=true,
}={}) {
if(typeof colour !== 'number')
throw new Error('colour argument must be a number');
if(typeof posX !== 'number')
throw new Error('posX argument must be a number');
if(typeof posY !== 'number')
throw new Error('posY argument must be a number');
if(typeof flashii !== 'object')
throw new Error('flashii argument must be an object or null');
if(!Array.isArray(presets))
throw new Error('presets argument must be an array');
colour = Math.min(0xFFFFFF, Math.max(0, colour | 0));
let colour;
const needsColour = [];
let promiseResolve;
@ -115,11 +124,11 @@ const MamiColourPicker = function(options) {
},
});
if(options.showPresetsTab && (options.flashii !== null || options.presets.length > 0))
tabs.add(new MamiColourPickerPresetsTab(options.flashii ?? options.presets));
if(options.showGridTab)
if(showPresetsTab && (flashii !== null || presets.length > 0))
tabs.add(new MamiColourPickerPresetsTab(flashii ?? presets));
if(showGridTab)
tabs.add(new MamiColourPickerGridTab);
if(options.showSlidersTab)
if(showSlidersTab)
tabs.add(new MamiColourPickerSlidersTab);
if(tabs.count < 1)
@ -134,9 +143,9 @@ const MamiColourPicker = function(options) {
</label>);
};
if(options.showHexValue)
if(showHexValue)
addValue('hex', 'Hex', new MamiColourPickerValueHex);
if(options.showRawValue)
if(showRawValue)
addValue('raw', 'Raw', new MamiColourPickerValueRaw);
const addButton = (id, name, action) => {
@ -153,13 +162,13 @@ const MamiColourPicker = function(options) {
buttons.appendChild(button);
};
if(options.showDialogButtons) {
if(showDialogButtons) {
addButton('cancel', 'Cancel', runReject);
addButton('apply', 'Apply');
}
setPosition({ x: options.posX, y: options.posY });
setColour(options.colour);
setPosition({ x: posX, y: posY });
setColour(colour);
return {
get element() { return html; },

View file

@ -47,9 +47,6 @@ const MamiConnectionManager = function(client, settings, flashii, eventTarget) {
resetTimeout();
(async () => {
if(settings.get('onlyConnectWhenVisible2'))
await MamiWaitVisible();
eventTarget.dispatch('attempt', {
url: url,
started: started,

View file

@ -1,13 +1,12 @@
#include animate.js
#include args.js
const MamiMessageBoxContainer = function() {
const container = <div class="msgbox-container"/>;
let raised = false;
let currAnim;
let animeAbort;
return {
raise: async parent => {
async raise(parent) {
if(raised) return;
raised = true;
container.style.pointerEvents = null;
@ -15,41 +14,46 @@ const MamiMessageBoxContainer = function() {
if(!parent.contains(container))
parent.appendChild(container);
currAnim?.cancel();
animeAbort?.abort();
animeAbort = new AbortController;
const signal = animeAbort.signal;
container.style.opacity = '0';
currAnim = MamiAnimate({
async: true,
delayed: true,
duration: 300,
easing: 'outExpo',
start: () => { container.style.opacity = '0'; },
update: t => { container.style.opacity = t; },
end: () => { container.style.opacity = null; },
});
try {
await currAnim.start();
await MamiAnimate({
duration: 300,
easing: 'outExpo',
signal,
update(t) {
container.style.opacity = t;
},
});
container.style.opacity = null;
} catch(ex) {}
},
dismiss: async parent => {
async dismiss(parent) {
if(!raised) return;
raised = false;
container.style.pointerEvents = 'none';
currAnim?.cancel();
animeAbort?.abort();
animeAbort = new AbortController;
const signal = animeAbort.signal;
currAnim = MamiAnimate({
async: true,
delayed: true,
duration: 300,
easing: 'outExpo',
update: t => { container.style.opacity = 1 - t; },
end: t => { parent.removeChild(container); },
});
try {
await currAnim.start();
await MamiAnimate({
duration: 300,
easing: 'outExpo',
signal,
update(t) {
container.style.opacity = 1 - t;
},
});
parent.removeChild(container);
} catch(ex) {}
},
show: async dialog => {
async show(dialog) {
if(typeof dialog !== 'object' || dialog === null)
throw 'dialog must be a non-null object';
@ -167,6 +171,8 @@ const MamiMessageBoxDialog = function(info) {
if(buttons.childElementCount > 2)
buttons.classList.add('msgbox-dialog-buttons-many');
const showAnimAbort = new AbortController;
return {
get buttonCount() {
return buttons.childElementCount;
@ -204,31 +210,36 @@ const MamiMessageBoxDialog = function(info) {
if(primaryButton instanceof HTMLButtonElement)
primaryButton.focus();
dialog.style.pointerEvents = null;
MamiAnimate({
async: true,
duration: 500,
easing: 'outElasticHalf',
signal: showAnimAbort.signal,
update: t => {
dialog.style.transform = `scale(${t})`;
},
end: () => {
dialog.style.transform = null;
},
});
}).then(() => {
dialog.style.transform = null;
}).catch(ex => { });
});
},
dismiss: async () => {
dismiss: () => {
showAnimAbort.abort();
dialog.style.pointerEvents = 'none';
await MamiAnimate({
async: true,
MamiAnimate({
duration: 600,
easing: 'outExpo',
update: t => { dialog.style.opacity = 1 - t; },
end: () => { dialog.style.opacity = '0'; },
update(t) {
dialog.style.opacity = 1 - t;
},
}).catch(ex => { }).then(() => {
dialog.style.opacity = '0';
}).finally(() => {
dialog.remove();
});
dialog.remove();
},
cancel: () => {
doReject();
@ -236,12 +247,10 @@ const MamiMessageBoxDialog = function(info) {
};
};
const MamiMessageBoxControl = function(options) {
options = MamiArgs('options', options, define => {
define('parent').required().constraint(value => value instanceof Element).done();
});
const MamiMessageBoxControl = function({ parent }) {
if(!(parent instanceof HTMLElement))
throw new Error('parent argument must be an instance of HTMLElement');
const parent = options.parent;
const container = new MamiMessageBoxContainer;
const queue = [];
let currentDialog;

View file

@ -1,17 +1,13 @@
#include args.js
const MamiTabsControl = function(options) {
options = MamiArgs('options', options, define => {
define('onAdd').required().type('function').done();
define('onRemove').required().type('function').done();
define('onSwitch').required().type('function').done();
});
const onAdd = options.onAdd;
const onRemove = options.onRemove;
const onSwitch = options.onSwitch;
const MamiTabsControl = function({ onAdd, onRemove, onSwitch }) {
if(typeof onAdd !== 'function')
throw new Error('onAdd argument must be a function');
if(typeof onRemove !== 'function')
throw new Error('onRemove argument must be a function');
if(typeof onSwitch !== 'function')
throw new Error('onSwitch argument must be a function');
const tabs = new Map;
let tabIdCounter = 0;
let currentTab;
const extractElement = elementInfo => {
@ -123,7 +119,7 @@ const MamiTabsControl = function(options) {
if(tabId !== undefined)
throw 'tabInfo has already been added';
tabId = $rngs(8);
tabId = 't' + (++tabIdCounter).toString(16);
tabs.set(tabId, tabInfo);
if(title !== 'string' && 'title' in tabInfo)

View file

@ -1,12 +1,8 @@
#include args.js
const MamiViewsControl = function({ body }) {
if(!(body instanceof HTMLElement))
throw new Error('body argument must be an instance of HTMLElement');
const MamiViewsControl = function(options) {
options = MamiArgs('options', options, define => {
define('body').required().constraint(value => value instanceof Element).done();
});
const targetBody = options.body;
targetBody.classList.toggle('views', true);
body.classList.toggle('views', true);
const views = [];
@ -20,8 +16,8 @@ const MamiViewsControl = function(options) {
if(!(element instanceof Element))
throw 'element is not an instance of Element';
if(element === targetBody)
throw 'element may not be the same as targetBody';
if(element === body)
throw 'element may not be the same as the body argument';
return element;
};
@ -46,8 +42,8 @@ const MamiViewsControl = function(options) {
element.classList.toggle('views-background', false);
element.classList.toggle('hidden', false);
if(!targetBody.contains(element))
targetBody.appendChild(element);
if(!body.contains(element))
body.appendChild(element);
if(typeof elementInfo.onViewForeground === 'function')
await elementInfo.onViewForeground();
@ -118,8 +114,8 @@ const MamiViewsControl = function(options) {
if(typeof elementInfo.onViewBackground === 'function')
await elementInfo.onViewBackground();
if(targetBody.contains(element))
targetBody.removeChild(element);
if(body.contains(element))
body.removeChild(element);
if(typeof elementInfo.onViewPop === 'function')
await elementInfo.onViewPop();
@ -181,8 +177,8 @@ const MamiViewsControl = function(options) {
element.classList.toggle('views-background', true);
element.classList.toggle('hidden', true);
if(!targetBody.contains(element))
targetBody.appendChild(element);
if(!body.contains(element))
body.appendChild(element);
updateZIncides();
},
@ -193,8 +189,8 @@ const MamiViewsControl = function(options) {
const elementInfo = views.shift();
const element = extractElement(elementInfo);
if(targetBody.contains(element))
targetBody.removeChild(element);
if(body.contains(element))
body.removeChild(element);
if(typeof elementInfo.onViewPop === 'function')
await elementInfo.onViewPop();

View file

@ -14,82 +14,124 @@ const MamiEasings = (() => {
const elasticOffsetQuarter = Math.pow(2, -10) * Math.sin((.25 - elasticConst2) * elasticConst);
const inOutElasticOffset = Math.pow(2, -10) * Math.sin((1 - elasticConst2 * 1.5) * elasticConst / 1.5);
let outBounce;
return {
linear: t => t,
linear(t) {
return t;
},
inQuad: t => t * t,
outQuad: t => t * (2 - t),
inOutQuad: t => {
inQuad(t) {
return t * t;
},
outQuad(t) {
return t * (2 - t);
},
inOutQuad(t) {
if(t < .5)
return t * t * 2;
return --t * t * -2 + 1;
},
inCubic: t => t * t * t,
outCubic: t => --t * t * t + 1,
inOutCubic: t => {
inCubic(t) {
return t * t * t;
},
outCubic(t) {
return --t * t * t + 1;
},
inOutCubic(t) {
if(t < .5)
return t * t * t * 4;
return --t * t * t * 4 + 1;
},
inQuart: t => t * t * t * t,
outQuart: t => 1 - --t * t * t * t,
inOutQuart: t => {
inQuart(t) {
return t * t * t * t;
},
outQuart(t) {
return 1 - --t * t * t * t;
},
inOutQuart(t) {
if(t < .5)
return t * t * t * t * 8;
return --t * t * t * t * -8 + 1;
},
inQuint: t => t * t * t * t * t,
outQuint: t => --t * t * t * t * t + 1,
inOutQuint: t => {
inQuint(t) {
return t * t * t * t * t;
},
outQuint(t) {
return --t * t * t * t * t + 1;
},
inOutQuint(t) {
if(t < .5)
return t * t * t * t * t * 16;
return --t * t * t * t * t * 16 + 1;
},
inSine: t => 1 - Math.cos(t * Math.PI * .5),
outSine: t => Math.sin(t * Math.PI * .5),
inOutSine: t => .5 - .5 * Math.cos(Math.PI * t),
inSine(t) {
return 1 - Math.cos(t * Math.PI * .5);
},
outSine(t) {
return Math.sin(t * Math.PI * .5);
},
inOutSine(t) {
return .5 - .5 * Math.cos(Math.PI * t);
},
inCirc: t => 1 - Math.sqrt(1 - t * t),
outCirc: t => Math.sqrt(1 - --t * t),
inOutCirc: t => {
inCirc(t) {
return 1 - Math.sqrt(1 - t * t);
},
outCirc(t) {
return Math.sqrt(1 - --t * t);
},
inOutCirc(t) {
if((t *= 2) < 1)
return .5 - .5 * Math.sqrt(1 - t * t);
return .5 * Math.sqrt(1 - (t -= 2) * t) + .5;
},
inElastic: t => -Math.pow(2, -10 + 10 * t) * Math.sin((1 - elasticConst2 - t) * elasticConst) + elasticOffsetFull * (1 - t),
outElastic: t => Math.pow(2, -10 * t) * Math.sin((t - elasticConst2) * elasticConst) + 1 - elasticOffsetFull * t,
outElasticHalf: t => Math.pow(2, -10 * t) * Math.sin((.5 * t - elasticConst2) * elasticConst) + 1 - elasticOffsetHalf * t,
outElasticQuarter: t => Math.pow(2, -10 * t) * Math.sin((.25 * t - elasticConst2) * elasticConst) + 1 - elasticOffsetQuarter * t,
inOutElastic: t => {
inElastic(t) {
return -Math.pow(2, -10 + 10 * t) * Math.sin((1 - elasticConst2 - t) * elasticConst) + elasticOffsetFull * (1 - t);
},
outElastic(t) {
return Math.pow(2, -10 * t) * Math.sin((t - elasticConst2) * elasticConst) + 1 - elasticOffsetFull * t;
},
outElasticHalf(t) {
return Math.pow(2, -10 * t) * Math.sin((.5 * t - elasticConst2) * elasticConst) + 1 - elasticOffsetHalf * t;
},
outElasticQuarter(t) {
return Math.pow(2, -10 * t) * Math.sin((.25 * t - elasticConst2) * elasticConst) + 1 - elasticOffsetQuarter * t;
},
inOutElastic(t) {
if((t *= 2) < 1)
return -.5 * (Math.pow(2, -10 + 10 * t) * Math.sin((1 - elasticConst2 * 1.5 - t) * elasticConst / 1.5) - inOutElasticOffset * (1 - t));
return .5 * (Math.pow(2, -10 * --t) * Math.sin((t - elasticConst2 * 1.5) * elasticConst / 1.5) - inOutElasticOffset * t) + 1;
},
inExpo: t => Math.pow(2, 10 * (t - 1)) + expoOffset * (t - 1),
outExpo: t => -Math.pow(2, -10 * t) + 1 + expoOffset * t,
inOutExpo: t => {
inExpo(t) {
return Math.pow(2, 10 * (t - 1)) + expoOffset * (t - 1);
},
outExpo(t) {
return -Math.pow(2, -10 * t) + 1 + expoOffset * t;
},
inOutExpo(t) {
if(t < .5)
return .5 * (Math.pow(2, 20 * t - 10)) + expoOffset * (2 * t - 1);
return 1 - .5 * (Math.pow(2, -20 * t + 10)) + expoOffset * (-2 * t + 1);
},
inBack: t => t * t * ((backConst + 1) * t - backConst),
outBack: t => --t * t * ((backConst + 1) * t + backConst) + 1,
inOutBack: t => {
inBack(t) {
return t * t * ((backConst + 1) * t - backConst);
},
outBack(t) {
return --t * t * ((backConst + 1) * t + backConst) + 1;
},
inOutBack(t) {
if((t *= 2) < 1)
return .5 * t * t * ((backConst2 + 1) * t - backConst2);
return .5 * ((t -= 2) * t * ((backConst2 + 1) * t + backConst2) + 2);
},
inBounce: t => {
inBounce(t) {
t = 1 - t;
if(t < bounceConst)
return 1 - 7.5625 * t * t;
@ -99,23 +141,23 @@ const MamiEasings = (() => {
return 1 - (7.5625 * (t -= 2.25 * bounceConst) * t + .9375);
return 1 - (7.5625 * (t -= 2.625 * bounceConst) * t + .984375);
},
outBounce: (() => {
return outBounce = t => {
if(t < bounceConst)
return 7.5625 * t * t;
if(t < 2 * bounceConst)
return 7.5625 * (t -= 1.5 * bounceConst) * t + .75;
if(t < 2.5 * bounceConst)
return 7.5625 * (t -= 2.25 * bounceConst) * t + .9375;
return 7.5625 * (t -= 2.625 * bounceConst) * t + .984375;
};
})(),
inOutBounce: t => {
outBounce(t) {
if(t < bounceConst)
return 7.5625 * t * t;
if(t < 2 * bounceConst)
return 7.5625 * (t -= 1.5 * bounceConst) * t + .75;
if(t < 2.5 * bounceConst)
return 7.5625 * (t -= 2.25 * bounceConst) * t + .9375;
return 7.5625 * (t -= 2.625 * bounceConst) * t + .984375;
},
inOutBounce(t) {
if(t < .5)
return .5 - .5 * outBounce(1 - t * 2);
return outBounce((t - .5) * 2) * .5 + .5;
},
outPow10: t => --t * Math.pow(t, 10) + 1,
outPow10(t) {
return --t * Math.pow(t, 10) + 1;
},
};
})();

View file

@ -6,21 +6,16 @@ const MamiEEPROM = function(endPoint) {
const appId = '1';
return {
create: fileInput => {
create(fileInput) {
if(!(fileInput instanceof File))
throw 'fileInput must be an instance of window.File';
let userAborted = false;
let abortHandler;
const abort = new AbortController;
return {
abort: () => {
userAborted = true;
if(typeof abortHandler === 'function')
abortHandler();
},
start: async progress => {
if(userAborted)
abort() { abort.abort(); },
async start(progress) {
if(abort.signal.aborted)
throw 'File upload was cancelled by the user, it cannot be restarted.';
const reportProgress = typeof progress !== 'function' ? undefined : ev => {
@ -40,7 +35,7 @@ const MamiEEPROM = function(endPoint) {
type: 'json',
authed: true,
upload: reportProgress,
abort: handler => abortHandler = handler,
signal: abort.signal,
}, formData);
if(body === null)
throw "The upload server didn't return the metadata for some reason.";
@ -55,7 +50,7 @@ const MamiEEPROM = function(endPoint) {
return Object.freeze(body);
} catch(ex) {
if(userAborted)
if(ex.aborted || abort.signal.aborted)
throw '';
console.error(ex);
@ -65,7 +60,7 @@ const MamiEEPROM = function(endPoint) {
};
},
delete: async fileInfo => {
async delete(fileInfo) {
if(typeof fileInfo !== 'object')
throw 'fileInfo must be an object';
if(typeof fileInfo.urlf !== 'string')

View file

@ -1,15 +1,10 @@
#include args.js
const MamiEmotePicker = function(args) {
args = MamiArgs('args', args, define => {
define('getEmotes').type('function').done();
define('setKeepOpenOnPick').type('function').done();
define('onPick').type('function').done();
define('onClose').type('function').done();
});
const MamiEmotePicker = function({ getEmotes, onPick, onClose=null, setKeepOpenOnPick=null }) {
if(typeof getEmotes !== 'function')
throw new Error('getEmotes must be a function');
if(typeof onPick !== 'function')
throw new Error('onPick must be a function');
let emotes;
let listElem, searchElem, keepOpenToggleElem;
const html = <div class="emopick" style="z-index: 9001;" onfocusout={ev => {
if(!keepOpenToggleElem.checked && !html.contains(ev.relatedTarget))
@ -24,8 +19,8 @@ const MamiEmotePicker = function(args) {
<div class="emopick-actions" tabindex="0">
<label class="emopick-action-toggle" tabindex="0">
{keepOpenToggleElem = <input type="checkbox" class="emopick-action-toggle-box" onchange={() => {
if(args.setKeepOpenOnPick !== undefined)
args.setKeepOpenOnPick(keepOpenToggleElem.checked);
if(typeof setKeepOpenOnPick === 'function')
setKeepOpenOnPick(keepOpenToggleElem.checked);
}} />}
<span class="emopick-action-toggle-label">Keep picker open</span>
</label>
@ -38,7 +33,7 @@ const MamiEmotePicker = function(args) {
for(const emote of emotes)
listElem.appendChild(<button class="emopick-emote" type="button" title={`:${emote.strings[0]}:`} data-strings={emote.strings.join(' ')} onclick={() => {
args.onPick(emote);
onPick(emote);
if(!keepOpenToggleElem.checked)
close();
}}>
@ -89,8 +84,8 @@ const MamiEmotePicker = function(args) {
promiseResolve = undefined;
}
if(args.onClose !== undefined)
args.onClose();
if(typeof onClose === 'function')
onClose();
html.classList.add('hidden');
};
@ -110,7 +105,7 @@ const MamiEmotePicker = function(args) {
setPosition: setPosition,
close: close,
dialog: pos => {
emotes = args.getEmotes();
emotes = getEmotes();
buildList();
html.classList.remove('hidden');

View file

@ -59,9 +59,7 @@ const Flashii = function(baseUrl) {
headers ??= {};
if(fresh) headers['Cache-Control'] = 'no-cache';
const options = { type, headers, authed };
return await $xhr.send(method, url, options, body);
return await $xhr.send(method, url, { type, headers, authed }, body);
};
const fii = {};
@ -77,7 +75,7 @@ const Flashii = function(baseUrl) {
};
fii.v1.chat = {};
fii.v1.chat.login = function({ redirect=null, assign=true }) {
fii.v1.chat.login = function({ redirect=null, assign=true }={}) {
return new Promise(resolve => {
redirect ??= `${location.protocol}//${location.host}`;
if(typeof redirect !== 'string')
@ -93,7 +91,7 @@ const Flashii = function(baseUrl) {
resolve(url);
});
};
fii.v1.chat.servers = async function({ proto=null, secure=null, fields=null, fresh=false }) {
fii.v1.chat.servers = async function({ proto=null, secure=null, fields=null, fresh=false }={}) {
const params = {};
if(proto !== null && typeof proto !== 'string')
@ -163,7 +161,7 @@ const Flashii = function(baseUrl) {
};
fii.v1.colours = {};
fii.v1.colours.presets = async function({ fields=null, fresh=false }) {
fii.v1.colours.presets = async function({ fields=null, fresh=false }={}) {
const { status, body } = await send({ method: 'GET', path: '/v1/colours/presets', fields, fresh });
if(status === 400)
@ -196,7 +194,7 @@ const Flashii = function(baseUrl) {
return id;
};
fii.v1.emotes = async function({ fields=null, fresh=false }) {
fii.v1.emotes = async function({ fields=null, fresh=false }={}) {
const { status, body } = await send({ method: 'GET', path: '/v1/emotes', fields, fresh });
if(status === 400)
@ -243,7 +241,7 @@ const Flashii = function(baseUrl) {
return id;
};
fii.v1.kaomoji = async function({ as=null, fields=null, fresh=false }) {
fii.v1.kaomoji = async function({ as=null, fields=null, fresh=false }={}) {
const { status, body } = await send({ method: 'GET', path: '/v1/kaomoji', params: { as }, fields, fresh });
if(status === 400)

View file

@ -1,13 +0,0 @@
#include urib64.js
const MamiHash = (() => {
const textEncoder = new TextEncoder('utf-8');
return async (data, method='SHA-256', asString=true) => {
if(typeof data === 'string')
data = textEncoder.encode(data);
const hash = new Uint8Array(await crypto.subtle.digest(method, data));
return asString ? MamiUriBase64.encode(hash) : hash;
};
})();

View file

@ -1,12 +1,9 @@
window.Umi = { UI: {} };
#include array.js
#include html.js
#include uniqstr.js
#include xhr.js
#include animate.js
#include args.js
#include auth.js
#include awaitable.js
#include common.js
@ -16,7 +13,6 @@ window.Umi = { UI: {} };
#include emotes.js
#include events.js
#include flashii.js
#include mobile.js
#include parsing.js
#include themes.js
#include txtrigs.js
@ -53,31 +49,37 @@ window.Umi = { UI: {} };
#include ui/emotes.js
#include ui/loading-overlay.jsx
const MamiInit = async args => {
args = MamiArgs('args', args, define => {
define('parent').default(document.body).constraint(value => value instanceof Element).done();
define('eventTarget').required().constraint(MamiIsEventTarget).done();
define('settingsPrefix').default('umi-').done();
define('auth').default(null).constraint(MamiIsAuth).done();
});
const MamiInit = async ({ auth=null, eventTarget, parent=null, settingsPrefix='umi-' }={}) => {
if(auth !== null && !MamiIsAuth(auth))
throw new Error('auth argument must be a compatible auth object');
if(!MamiIsEventTarget(eventTarget))
throw new Error('eventTarget must be a compatible event target object');
if(parent === null)
parent = document.body;
else if(!(parent instanceof HTMLElement))
throw new Error('parent argument must be an instance of HTMLElement');
if(typeof settingsPrefix !== 'string')
throw new Error('settingsPrefix must be a string');
const flashii = new Flashii(`${window.FII_URL}/api`);
let auth = args.auth;
if(!auth) {
auth = new MamiFlashiiAuth(flashii);
await auth.refresh();
setInterval(() => { auth.refresh(); }, 600000);
}
const ctx = new MamiContext(args.eventTarget, null, auth, flashii);
const ctx = new MamiContext(eventTarget, null, auth, flashii);
// remove this later and replace with the one commented out way below
if(!('mami' in window))
Object.defineProperty(window, 'mami', { enumerable: true, value: ctx });
ctx.views = new MamiViewsControl({ body: args.parent });
ctx.msgbox = new MamiMessageBoxControl({ parent: args.parent });
ctx.views = new MamiViewsControl({ body: parent });
ctx.msgbox = new MamiMessageBoxControl({ parent });
const loadingOverlay = new Umi.UI.LoadingOverlay('spinner', 'Loading...');
await ctx.views.push(loadingOverlay);
@ -94,7 +96,7 @@ const MamiInit = async args => {
}
const settings = new MamiSettings(args.settingsPrefix, ctx.events.scopeTo('settings'));
const settings = new MamiSettings(settingsPrefix, ctx.events.scopeTo('settings'));
ctx.settings = settings;
settings.define('style').default('dark').create();
@ -123,7 +125,6 @@ const MamiInit = async args => {
settings.define('windowsLiveMessenger').default(false).create();
settings.define('seinfeld').default(false).create();
settings.define('flashTitle').default(true).create();
settings.define('onlyConnectWhenVisible2').default(MamiIsMobileDevice()).create();
settings.define('playJokeSounds').default(true).create();
settings.define('weeaboo').default(false).create();
settings.define('motivationalImages').default(false).create();
@ -134,7 +135,6 @@ const MamiInit = async args => {
settings.define('dumpPackets').default(window.DEBUG).create();
settings.define('dumpEvents').default(window.DEBUG).create();
settings.define('marqueeAllNames').default(false).create();
settings.define('dbgAnimDurationMulti').default(1).min(0).max(10).create();
settings.define('newLineOnEnter').default(false).create();
settings.define('keepEmotePickerOpen').default(true).create();
settings.define('doNotMarkLinksAsVisited').default(false).create();
@ -329,7 +329,7 @@ const MamiInit = async args => {
layout.element.classList.toggle('chat--compact', ev.detail.value);
layout.interface.messageList.element.classList.toggle('chat--compact', ev.detail.value);
});
settings.watch('preventOverflow', ev => { args.parent.classList.toggle('prevent-overflow', ev.detail.value); });
settings.watch('preventOverflow', ev => { parent.classList.toggle('prevent-overflow', ev.detail.value); });
settings.watch('doNotMarkLinksAsVisited', ev => { layout.interface.messageList.element.classList.toggle('mami-do-not-mark-links-as-visited', ev.detail.value); });
settings.watch('newLineOnEnter', ev => { chatForm.input.newLineOnEnter = ev.detail.value; });
settings.watch('expandTextBox', ev => { chatForm.input.growInputField = ev.detail.value; });
@ -509,10 +509,6 @@ const MamiInit = async args => {
});
sbSettings.category(category => {
category.header('Misc');
category.setting('onlyConnectWhenVisible2').title('Only connect when the tab is in the foreground').confirm([
'Please only disable this setting if you are using a desktop or laptop computer, this should always remain on on a phone, tablet or other device of that sort.',
'Are you sure you want to change this setting? Ignoring this warning may carry consequences.',
]).done();
category.setting('playJokeSounds').title('Run joke triggers').done();
category.setting('weeaboo').title('Weeaboo').done();
category.setting('motivationalImages').title('Make images motivational').done();
@ -596,7 +592,7 @@ const MamiInit = async args => {
sbSettings.category(category => {
category.header('Settings');
category.button('Import settings', () => {
(new MamiSettingsBackup(settings)).importUpload(args.parent);
(new MamiSettingsBackup(settings)).importUpload(parent);
}, ['Your current settings will be replaced with the ones in the export.', 'Are you sure you want to continue?']);
category.button('Export settings', () => {
const user = Umi.User.getCurrentUser();
@ -604,7 +600,7 @@ const MamiInit = async args => {
if(user !== null)
fileName = `${user.name}'s settings.mami`;
(new MamiSettingsBackup(settings)).exportDownload(args.parent, fileName);
(new MamiSettingsBackup(settings)).exportDownload(parent, fileName);
});
category.button('Reset settings', () => {
settings.clear();
@ -617,7 +613,6 @@ const MamiInit = async args => {
category.setting('dumpPackets').title('Dump packets to console').done();
category.setting('dumpEvents').title('Dump events to console').done();
category.setting('marqueeAllNames').title('Apply marquee on everyone (requires reload)').done();
category.setting('dbgAnimDurationMulti').title('Animation multiplier').type('range').done();
category.button('Test kick/ban notice', async button => {
button.disabled = true;
await ctx.views.push(new MamiForceDisconnectNotice({ perma: true, type: 'ban' }));
@ -775,7 +770,7 @@ const MamiInit = async args => {
doUpload(file);
},
});
args.parent.appendChild(uploadForm);
parent.appendChild(uploadForm);
chatForm.input.createButton({
title: 'Upload',
@ -790,10 +785,10 @@ const MamiInit = async args => {
});
// figure out how to display a UI for this someday
//args.parent.addEventListener('dragenter', ev => { console.info('dragenter', ev); });
//args.parent.addEventListener('dragleave', ev => { console.info('dragleave', ev); });
args.parent.addEventListener('dragover', ev => { ev.preventDefault(); });
args.parent.addEventListener('drop', ev => {
//parent.addEventListener('dragenter', ev => { console.info('dragenter', ev); });
//parent.addEventListener('dragleave', ev => { console.info('dragleave', ev); });
parent.addEventListener('dragover', ev => { ev.preventDefault(); });
parent.addEventListener('drop', ev => {
if(ev.dataTransfer === undefined || ev.dataTransfer === null || ev.dataTransfer.files.length < 1)
return;
@ -901,8 +896,8 @@ const MamiInit = async args => {
ctx, sockChat, setLoadingOverlay, sockChatReconnect, pingIndicator,
sbActPing, sbChannels, sbUsers
);
settings.watch('dumpEvents', ev => sockChatHandlers.setDumpEvents(ev.detail.value));
settings.watch('dumpPackets', ev => sockChat.setDumpPackets(ev.detail.value));
settings.watch('dumpEvents', ev => { sockChatHandlers.setDumpEvents(ev.detail.value); });
settings.watch('dumpPackets', ev => { sockChat.dumpPackets = ev.detail.value; });
sockChatHandlers.register();
const conManAttempt = ev => {
@ -932,35 +927,7 @@ const MamiInit = async args => {
const eventTarget = new MamiEventTargetWindow;
Object.defineProperty(window, 'mamiEventTarget', { enumerable: true, value: eventTarget });
MamiInit({
eventTarget: eventTarget,
}).then(mami => {
MamiInit({ eventTarget }).then(mami => {
//Object.defineProperty(window, 'mami', { enumerable: true, value: mami });
});
})();
const MamiDbgCreateFloatingInstance = async () => {
if(!window.DEBUG)
return;
const prefix = $rngs(8);
const parent = $element('div', {
style: {
position: 'absolute',
bottom: '100px',
right: '100px',
zIndex: '9001',
width: '640px',
height: '480px',
background: '#0f0',
},
});
document.body.appendChild(parent);
return await MamiInit({
parent: parent,
settingsPrefix: `dbg:${prefix}:`,
eventTarget: mamiEventTarget.scopeTo(prefix),
});
};

View file

@ -1,12 +0,0 @@
const MamiIsMobileDevice = () => {
if('userAgentData' in navigator)
return navigator.userAgentData.mobile;
if('matchMedia' in window)
return !window.matchMedia("(any-pointer:fine)").matches;
if('maxTouchPoints' in navigator)
return navigator.maxTouchPoints > 1;
return false;
};

View file

@ -52,59 +52,58 @@ const MamiForceDisconnectNotice = function(banInfo) {
} catch(ex) {}
bgmSrc = sfxBuf = undefined;
},
getViewTransition: mode => {
getViewTransition(mode) {
if(mode === 'push')
return ctx => MamiAnimate({
async: true,
duration: (sfxBuf?.duration ?? 1.4) * 1000,
start: () => {
if(sfxBuf !== undefined)
mami.sound.audio.createSource(sfxBuf).play();
return async ({ fromElem, toElem }) => {
if(sfxBuf !== undefined)
mami.sound.audio.createSource(sfxBuf).play();
ctx.toElem.style.top = '-100%';
},
update: t => {
const tOutBounce = MamiEasings.outBounce(t);
ctx.toElem.style.top = `${-100 + (tOutBounce * 100)}%`;
toElem.style.top = '-100%';
const tOutExpo = MamiEasings.outExpo(t);
ctx.fromElem.style.transform = `scale(${1 - (1 * tOutExpo)}) rotate(${rotate * tOutExpo}deg)`;
ctx.fromElem.style.filter = `grayscale(${tOutExpo * 100}%)`;
},
end: () => {
bgmSrc?.play();
ctx.toElem.style.top = null;
ctx.fromElem.style.transform = null;
ctx.fromElem.style.filter = null;
},
});
await MamiAnimate({
duration: (sfxBuf?.duration ?? 1.4) * 1000,
update(t) {
const tOutBounce = MamiEasings.outBounce(t);
toElem.style.top = `${-100 + (tOutBounce * 100)}%`;
const tOutExpo = MamiEasings.outExpo(t);
fromElem.style.transform = `scale(${1 - (1 * tOutExpo)}) rotate(${rotate * tOutExpo}deg)`;
fromElem.style.filter = `grayscale(${tOutExpo * 100}%)`;
},
});
bgmSrc?.play();
toElem.style.top = null;
fromElem.style.transform = null;
fromElem.style.filter = null;
};
if(mode === 'pop')
return ctx => MamiAnimate({
async: true,
duration: (sfxBuf?.duration ?? 1.4) * 1000,
start: () => {
bgmSrc?.stop();
if(sfxBuf !== undefined)
mami.sound.audio.createSource(sfxBuf, true).play();
return async ({ fromElem, toElem }) => {
bgmSrc?.stop();
if(sfxBuf !== undefined)
mami.sound.audio.createSource(sfxBuf, true).play();
ctx.toElem.style.transform = `scale(1) rotate(${rotate}deg)`;
ctx.toElem.style.filter = 'grayscale(100%)';
},
update: t => {
const tOutBounce = MamiEasings.inBounce(t);
ctx.fromElem.style.top = `${tOutBounce * -100}%`;
toElem.style.transform = `scale(1) rotate(${rotate}deg)`;
toElem.style.filter = 'grayscale(100%)';
const tOutExpo = MamiEasings.inExpo(t);
ctx.toElem.style.transform = `scale(${1 * tOutExpo}) rotate(${rotate - (rotate * tOutExpo)}deg)`;
ctx.toElem.style.filter = `grayscale(${100 - (tOutExpo * 100)}%)`;
},
end: () => {
ctx.fromElem.style.top = null;
ctx.toElem.style.transform = null;
ctx.toElem.style.filter = null;
},
});
await MamiAnimate({
duration: (sfxBuf?.duration ?? 1.4) * 1000,
update(t) {
const tOutBounce = MamiEasings.inBounce(t);
fromElem.style.top = `${tOutBounce * -100}%`;
const tOutExpo = MamiEasings.inExpo(t);
toElem.style.transform = `scale(${1 * tOutExpo}) rotate(${rotate - (rotate * tOutExpo)}deg)`;
toElem.style.filter = `grayscale(${100 - (tOutExpo * 100)}%)`;
},
});
fromElem.style.top = null;
toElem.style.transform = null;
toElem.style.filter = null;
};
},
};

View file

@ -22,7 +22,7 @@ const MamiYouAreAnIdiot = function(sndLibrary, views) {
const pub = {
get element() { return html; },
onViewPush: async () => {
async onViewPush() {
try {
soundSrc = await sndLibrary.loadSource('misc:youare');
soundSrc.muted = true;
@ -32,29 +32,34 @@ const MamiYouAreAnIdiot = function(sndLibrary, views) {
console.error(ex);
}
},
onViewForeground: async () => {
async onViewForeground() {
if(soundSrc !== undefined)
soundSrc.muted = false;
},
onViewBackground: async () => {
async onViewBackground() {
if(soundSrc !== undefined)
soundSrc.muted = true;
},
onViewPop: async () => {
async onViewPop() {
if(soundSrc !== undefined)
soundSrc.stop();
soundSrc = undefined;
},
getViewTransition: mode => {
getViewTransition(mode) {
if(mode === 'push')
return ctx => MamiAnimate({
async: true,
duration: 1500,
easing: 'outBounce',
start: () => ctx.toElem.style.top = '-100%',
update: t => ctx.toElem.style.top = `${-100 + (t * 100)}%`,
end: () => ctx.toElem.style.top = null,
});
return async ({ toElem }) => {
toElem.style.top = '-100%';
await MamiAnimate({
duration: 1500,
easing: 'outBounce',
update(t) {
toElem.style.top = `${-100 + (t * 100)}%`;
},
});
toElem.style.top = null;
};
},
};

View file

@ -1,4 +1,4 @@
#include timedp.js
#include awaitable.js
#include proto/sockchat/authed.js
#include proto/sockchat/ctx.js
#include proto/sockchat/unauthed.js
@ -125,36 +125,48 @@ const SockChatClient = function(dispatch, options) {
sock?.send(args.join("\t"));
};
const sendPing = () => {
return new Promise((resolve, reject) => {
if(ctx === undefined)
throw 'no connection opened';
if(!ctx.isAuthed)
throw 'must be authenticated';
if(ctx.pingPromise !== undefined)
throw 'already sending a ping';
const sendPing = async () => {
return await MamiTimeout(
new Promise((resolve, reject) => {
if(ctx === undefined)
throw 'no connection opened';
if(!ctx.isAuthed)
throw 'must be authenticated';
if(ctx.pingPromise !== undefined)
throw 'already sending a ping';
ctx.lastPing = Date.now();
ctx.pingPromise = new TimedPromise(resolve, reject, () => ctx.pingPromise = undefined, 2000);
ctx.lastPing = Date.now();
ctx.pingPromise = {
resolve(...args) { resolve(...args); },
reject(...args) { reject(...args); },
};
send('0', ctx.userId);
});
send('0', ctx.userId);
}),
2000
).finally(() => { ctx.pingPromise = undefined; });
};
const sendAuth = (...args) => {
return new Promise((resolve, reject) => {
if(ctx === undefined)
throw 'no connection opened';
if(ctx.isAuthed)
throw 'already authenticated';
if(ctx.authPromise !== undefined)
throw 'already authenticating';
const sendAuth = async (...args) => {
return await MamiTimeout(
new Promise((resolve, reject) => {
if(ctx === undefined)
throw 'no connection opened';
if(ctx.isAuthed)
throw 'already authenticated';
if(ctx.authPromise !== undefined)
throw 'already authenticating';
ctx.authPromise = {
resolve(...args) { resolve(...args); },
reject(...args) { reject(...args); },
};
send('1', ...args);
}),
// HttpClient in C# has its Moments, so lets give this way too long to do its thing
ctx.authPromise = new TimedPromise(resolve, reject, () => ctx.authPromise = undefined, 10000);
send('1', ...args);
});
10000
).finally(() => { ctx.authPromise = undefined; });
};
const sendMessage = text => {
@ -180,8 +192,7 @@ const SockChatClient = function(dispatch, options) {
// server doesn't send a direct ACK and we can't tell what message
// which response messages is actually associated with this
// an ACK or request/response extension to the protocol will be required
if(typeof resolve === 'function')
resolve();
resolve();
});
};
@ -206,32 +217,43 @@ const SockChatClient = function(dispatch, options) {
};
return {
setDumpPackets: state => {
dumpPackets = !!state;
get dumpPackets() { return dumpPackets; },
set dumpPackets(value) { dumpPackets = !!value; },
async open(url, timeout=5000) {
return await MamiTimeout(
new Promise((resolve, reject) => {
if(typeof url !== 'string')
throw 'url must be a string';
if(ctx?.openPromise !== undefined)
throw 'already opening a connection';
closeWebSocket();
createContext();
ctx.openPromise = {
resolve(...args) { resolve(...args); },
reject(...args) { reject(...args); },
};
sock = new WebSocket(url);
sock.addEventListener('open', handleOpen);
sock.addEventListener('close', handleClose);
sock.addEventListener('message', handleMessage);
}),
timeout
).finally(() => { ctx.openPromise = undefined; });
},
open: url => {
return new Promise((resolve, reject) => {
if(typeof url !== 'string')
throw 'url must be a string';
if(ctx?.openPromise !== undefined)
throw 'already opening a connection';
closeWebSocket();
createContext();
ctx.openPromise = new TimedPromise(resolve, reject, () => ctx.openPromise = undefined, 5000);
sock = new WebSocket(url);
sock.addEventListener('open', handleOpen);
sock.addEventListener('close', handleClose);
sock.addEventListener('message', handleMessage);
});
close() {
closeWebSocket();
},
close: () => { closeWebSocket(); },
sendPing: sendPing,
sendAuth: sendAuth,
sendMessage: sendMessage,
switchChannel: async info => {
sendPing,
sendAuth,
sendMessage,
async switchChannel(info) {
if(!ctx.isAuthed)
return;

View file

@ -1,3 +1,5 @@
#include animate.js
const MamiSidebarPanelSettings = function(settings, msgBox) {
const copyright = <div class="mami-copyright">
<a href="//patchii.net/flashii/mami" target="_blank">Mami</a> # <a href={`//patchii.net/flashii/mami/commit/${window.GIT_HASH}`} target="_blank">{window.GIT_HASH.substring(0, 7)}</a> © <a href="//flash.moe" target="_blank">flash.moe</a><br/>
@ -26,12 +28,11 @@ const MamiSidebarPanelSettings = function(settings, msgBox) {
{body}
</div>;
let animation;
let animeAbort;
header.onclick = () => {
if(animation !== undefined) {
animation.cancel();
animation = undefined;
}
animeAbort?.abort();
animeAbort = new AbortController;
const signal = animeAbort.signal;
const closed = category.classList.contains('js-settings-closed');
let start, update, end, height;
@ -53,13 +54,13 @@ const MamiSidebarPanelSettings = function(settings, msgBox) {
category.classList.toggle('js-settings-closed', !closed);
animation = MamiAnimate({
start();
MamiAnimate({
duration: 500,
easing: 'outExpo',
start: start,
update: update,
end: end,
});
signal,
update,
}).then(() => { end(); }).catch(ex => { });
};
html.insertBefore(category, copyright);

View file

@ -113,7 +113,7 @@ const MamiSidebarPanelUploadsEntry = function(fileInfo) {
const MamiSidebarPanelUploads = function() {
const html = <div class="sidebar__menu--uploads"/>;
const options = new Map;
const entries = [];
const entries = new Set;
const reloadOptionsFor = entry => {
const names = entry.getOptionNames();
@ -150,7 +150,7 @@ const MamiSidebarPanelUploads = function() {
createEntry: info => {
const entry = new MamiSidebarPanelUploadsEntry(info);
entries.push(entry);
entries.add(entry);
reloadOptionsFor(entry);
html.insertBefore(entry.element, html.firstElementChild);
@ -158,11 +158,11 @@ const MamiSidebarPanelUploads = function() {
return entry;
},
deleteEntry: entry => {
if(!entries.includes(entry))
if(!entries.has(entry))
return;
html.removeChild(entry.element);
$arrayRemoveValue(entries, entry);
entries.delete(entry);
},
addOption: option => {

View file

@ -61,7 +61,9 @@ const MamiSidebarPanelUsersEntry = function(info) {
setStatusMessage(statusMessage);
let optionsVisible = false, optionsAnim, optionsTimeout;
let optionsVisible = false;
let animeAbort;
let optionsTimeout;
const setOptionsVisible = state => {
if(state === undefined)
state = !optionsVisible;
@ -73,10 +75,9 @@ const MamiSidebarPanelUsersEntry = function(info) {
optionsTimeout = undefined;
}
if(optionsAnim !== undefined) {
optionsAnim.cancel();
optionsAnim = undefined;
}
animeAbort?.abort();
animeAbort = new AbortController;
const signal = animeAbort.signal;
let start, update, end, height;
@ -94,7 +95,7 @@ const MamiSidebarPanelUsersEntry = function(info) {
height = optsElem.clientHeight;
optsElem.style.height = curHeight;
};
update = function(t) {
update = t => {
optsElem.style.height = `${height * t}px`;
};
end = () => {
@ -113,19 +114,15 @@ const MamiSidebarPanelUsersEntry = function(info) {
};
}
optionsAnim = MamiAnimate({
async: true,
delayed: true,
duration: 500,
easing: 'outExpo',
start: start,
update: update,
end: end,
});
optionsVisible = state;
return optionsAnim.start();
start();
MamiAnimate({
duration: 500,
easing: 'outExpo',
signal,
update,
}).then(() => { end(); }).catch(ex => { });
};
let avatar;

View file

@ -1,3 +1,5 @@
#include animate.js
const MamiSidebarGutterButton = function(name, info) {
const html = <button type="button" class="sidebar__selector-mode" title={info.text ?? name}/>;
const isBottom = info.pos === 'bottom';
@ -126,7 +128,7 @@ const MamiSidebarPanelFrame = function() {
const html = <div class="sidebar__menus"/>;
const panels = new Map;
let activePanel;
let animation;
let animeAbort;
const width = 220; // should probably not be hardcoded
const isOpen = () => !html.classList.contains('js-menu-closed');
@ -148,10 +150,9 @@ const MamiSidebarPanelFrame = function() {
else if(isOpened === open)
return;
if(animation !== undefined) {
animation.cancel();
animation = undefined;
}
animeAbort?.abort();
animeAbort = new AbortController;
const signal = animeAbort.signal;
html.classList.toggle('js-menu-closed', !open);
html.dispatchEvent(new CustomEvent('mami:sidebar:toggle', {
@ -162,31 +163,26 @@ const MamiSidebarPanelFrame = function() {
? t => { html.style.width = `${width * t}px`; }
: t => { html.style.width = `${width - (width * t)}px`; };
animation = MamiAnimate({
async: true,
delayed: true,
html.style.width = null;
html.style.overflowX = 'hidden';
if(activePanel !== undefined)
activePanel.element.style.minWidth = `${width}px`;
MamiAnimate({
duration: 500,
easing: 'outExpo',
start: () => {
update,
signal,
}).then(() => {
html.style.overflowX = null;
if(open)
html.style.width = null;
html.style.overflowX = 'hidden';
if(activePanel !== undefined)
activePanel.element.style.minWidth = `${width}px`;
},
update: update,
end: () => {
html.style.overflowX = null;
if(open)
html.style.width = null;
if(activePanel !== undefined)
activePanel.element.style.minWidth = null;
},
});
return animation.start();
if(activePanel !== undefined)
activePanel.element.style.minWidth = null;
}).catch(ex => { });
},
getPanel: name => panels.get(name),

View file

@ -9,38 +9,36 @@ const MamiSockChat = function(eventTarget) {
return {
get client() { return client; },
get dumpPackets() { return dumpPackets; },
set dumpPackets(value) {
dumpPackets = !!value;
if(client) client.dumpPackets = dumpPackets;
},
watch: eventTarget.watch,
unwatch: eventTarget.unwatch,
create: async () => {
if(client !== undefined && typeof client.close === 'function')
// intentional fire & forget, worker may be gone, don't want to wait for it to time out
client.close();
async create() {
if(client) client.close();
restarting = false;
client = new SockChatClient(eventTarget.dispatch, { ping: 30 });
client.setDumpPackets(dumpPackets);
client.dumpPackets = dumpPackets;
MamiCompat('Umi.Server', { get: () => client, configurable: true });
MamiCompat('Umi.Server.SendMessage', { value: text => client.sendMessage(text), configurable: true });
MamiCompat('Umi.Protocol.SockChat.Protocol.Instance.SendMessage', { value: text => client.sendMessage(text), configurable: true });
MamiCompat('Umi.Protocol.SockLegacy.Protocol.Instance.SendMessage', { value: text => client.sendMessage(text), configurable: true });
},
connect: async url => {
async connect(url) {
try {
await client.open(url);
} catch(ex) {
return ex.wasKicked === true;
}
},
authenticate: async auth => {
async authenticate(auth) {
await client.sendAuth(auth.type, auth.token);
},
setDumpPackets: state => {
dumpPackets = !!state;
if(client !== undefined && typeof client.setDumpPackets === 'function')
client.setDumpPackets(dumpPackets);
},
};
};

View file

@ -1,3 +1,4 @@
#include animate.js
#include awaitable.js
const MamiSoundTest = function(settings, audio, manager, library, clickPos) {
@ -18,7 +19,7 @@ const MamiSoundTest = function(settings, audio, manager, library, clickPos) {
const detuneSetting = settings.info('soundDetune');
const loopStartSetting = settings.info('soundLoopStart');
const loopEndSetting = settings.info('soundLoopEnd');
const sources = [];
const sources = new Set;
let libraryButtons;
let nowPlaying;
@ -107,7 +108,7 @@ const MamiSoundTest = function(settings, audio, manager, library, clickPos) {
name.textContent += ` (${buffer.duration})`;
source = audio.createSource(buffer, settings.get('soundReverse'));
sources.push(source);
sources.add(source);
state.textContent = 'Configuring...';
const rate = settings.get('soundRate');
@ -130,7 +131,7 @@ const MamiSoundTest = function(settings, audio, manager, library, clickPos) {
console.error(ex);
state.textContent = `Error: ${ex}`;
} finally {
$arrayRemoveValue(sources, source);
sources.delete(source);
await MamiSleep(2000);
nowPlaying.removeChild(player);
}
@ -149,45 +150,48 @@ const MamiSoundTest = function(settings, audio, manager, library, clickPos) {
return {
get element() { return container; },
onViewPop: async () => {
async onViewPop() {
for(const source of sources)
source.stop();
},
getViewTransition: mode => {
getViewTransition(mode) {
if(!hasClickPos)
return;
if(mode === 'push')
return ctx => MamiAnimate({
async: true,
duration: 1500,
easing: 'inQuad',
start: () => {
library.play('mario:keyhole');
ctx.toElem.style.transform = 'scale(0) translate(25%, 25%)';
ctx.toElem.style.transformOrigin = `${clickPos[0]}px ${clickPos[1]}px`;
},
update: (t, rt) => ctx.toElem.style.transform = `scale(${t}) translate(${25 * (1 - rt)}%, ${25 * (1 - rt)}%)`,
end: () => {
ctx.toElem.style.transform = null;
ctx.toElem.style.transformOrigin = null;
},
});
return async ({ toElem }) => {
library.play('mario:keyhole');
toElem.style.transform = 'scale(0) translate(25%, 25%)';
toElem.style.transformOrigin = `${clickPos[0]}px ${clickPos[1]}px`;
await MamiAnimate({
duration: 1500,
easing: 'inQuad',
update(t, rt) {
toElem.style.transform = `scale(${t}) translate(${25 * (1 - rt)}%, ${25 * (1 - rt)}%)`;
},
});
toElem.style.transform = null;
toElem.style.transformOrigin = null;
};
if(mode === 'pop')
return ctx => MamiAnimate({
async: true,
duration: 1000,
easing: 'outQuad',
start: () => {
ctx.fromElem.style.transformOrigin = `${clickPos[0]}px ${clickPos[1]}px`;
},
update: (t, rt) => ctx.fromElem.style.transform = `scale(${1 - t}) rotate(${-1080 * t}deg) translate(${50 * rt}%, ${50 * rt}%)`,
end: () => {
ctx.fromElem.style.transform = null;
ctx.fromElem.style.transformOrigin = null;
},
});
return async ({ fromElem }) => {
fromElem.style.transformOrigin = `${clickPos[0]}px ${clickPos[1]}px`;
await MamiAnimate({
duration: 1000,
easing: 'outQuad',
update(t, rt) {
fromElem.style.transform = `scale(${1 - t}) rotate(${-1080 * t}deg) translate(${50 * rt}%, ${50 * rt}%)`;
},
})
fromElem.style.transform = null;
fromElem.style.transformOrigin = null;
};
},
};
};

View file

@ -1,63 +0,0 @@
const MamiSRLE = (() => {
return {
encode: (input, cutoff) => {
let output = '';
let last = '';
let repeat = 0;
input = (input || '').toString();
cutoff = cutoff || 1
for(let i = 0; i <= input.length; ++i) {
const chr = input[i];
if(last === chr)
++repeat;
else {
if(repeat > cutoff)
for(const repChr in repeat.toString())
output += ')!@#$%^&*('[parseInt(repChr)];
else
output += last.repeat(repeat);
repeat = 0;
if(chr !== undefined) {
output += chr;
last = chr;
}
}
}
return output;
},
decode: input => {
let output = '';
let repeat = '';
let chr;
input = (input || '').toString().split('').reverse();
for(;;) {
const chr = input.pop(), num = ')!@#$%^&*('.indexOf(chr);
if(num >= 0)
repeat += num;
else {
if(repeat) {
output += output.slice(-1).repeat(parseInt(repeat));
repeat = '';
}
if(chr === undefined)
break;
output += chr;
}
}
return output;
},
};
})();

View file

@ -1,48 +0,0 @@
const TimedPromise = function(resolve, reject, always, timeoutMs) {
let timeout, resolved = false;
const cancelTimeout = () => {
if(timeout === undefined) {
clearTimeout(timeout);
timeout = undefined;
}
};
const doResolve = (...args) => {
if(resolved) return;
resolved = true;
cancelTimeout();
reject = undefined;
if(typeof resolve === 'function')
resolve(...args);
if(typeof always === 'function')
always();
};
const doReject = (...args) => {
if(resolved) return;
resolved = true;
cancelTimeout();
resolve = undefined;
if(typeof reject === 'function')
reject(...args);
if(typeof always === 'function')
always();
};
timeout = setTimeout(() => doReject('timeout'), timeoutMs);
return {
resolve: doResolve,
reject: doReject,
cancel: () => {
if(timeout === undefined)
return;
doReject('timeout');
},
};
};

View file

@ -1,27 +1,40 @@
#include args.js
const MamiWindowTitle = function({
getName=()=>{},
setTitle=text=> { document.title = text },
strobeInterval=500,
strobeRepeat=5,
}) {
if(typeof getName !== 'function')
throw new Error('getName argument must be a function');
if(typeof setTitle !== 'function')
throw new Error('setTitle argument must be a function');
const MamiWindowTitle = function(options) {
options = MamiArgs('options', options, define => {
define('getName').default(() => {}).done();
define('setTitle').default(text => { document.title = text; }).done();
define('strobeInterval').constraint(value => typeof value === 'number' || typeof value === 'function').default(500).done();
define('strobeRepeat').constraint(value => typeof value === 'number' || typeof value === 'function').default(5).done();
});
const getName = options.getName;
const setTitleImpl = options.setTitle;
const setTitle = text => {
const setTitleWrap = text => {
if(text === undefined || text === null)
text = '';
else if(typeof text !== 'string')
text = text.toString();
setTitleImpl(text);
setTitle(text);
};
const defaultStrobeInterval = typeof options.strobeInterval === 'function' ? options.strobeInterval : () => options.strobeInterval;
const defaultStrobeRepeat = typeof options.strobeRepeat === 'function' ? options.strobeRepeat : () => options.strobeRepeat;
const getStrobeInverval = () => {
let value = strobeInterval;
if(typeof value === 'function')
value = value();
if(typeof value !== 'number' || value < 1)
return 500;
return value;
};
const getStrobeRepeat = () => {
let value = strobeRepeat;
if(typeof value === 'function')
value = value();
if(typeof value !== 'number' || value < 1)
return 5;
return value;
};
let activeStrobe;
@ -32,7 +45,7 @@ const MamiWindowTitle = function(options) {
clearInterval(activeStrobe);
activeStrobe = undefined;
setTitle(getName());
setTitleWrap(getName());
};
const strobeTitle = (titles, interval, repeat) => {
@ -41,30 +54,30 @@ const MamiWindowTitle = function(options) {
if(titles.length < 1)
throw 'titles must contain at least one item';
if(typeof interval !== 'number')
interval = defaultStrobeInterval();
interval = getStrobeInverval();
if(typeof repeat !== 'number')
repeat = defaultStrobeRepeat();
repeat = getStrobeRepeat();
const target = titles.length * repeat;
let round = 0;
clearTitle();
setTitle(titles[0]);
setTitleWrap(titles[0]);
activeStrobe = setInterval(() => {
if(round >= target) {
clearTitle();
setTitle(getName());
setTitleWrap(getName());
return;
}
++round;
setTitle(titles[round % titles.length]);
setTitleWrap(titles[round % titles.length]);
}, interval);
};
return {
set: setTitle,
set: setTitleWrap,
clear: clearTitle,
strobe: strobeTitle,
};

View file

@ -1,3 +1,4 @@
#include animate.js
#include controls/throbber.jsx
Umi.UI.LoadingOverlay = function(icon, message) {
@ -37,19 +38,19 @@ Umi.UI.LoadingOverlay = function(icon, message) {
getViewTransition(mode) {
if(mode === 'pop')
return ctx => MamiAnimate({
async: true,
duration: 200,
easing: 'inQuint',
start: () => {
html.classList.remove('overlay-filter');
html.style.pointerEvents = 'none';
},
update: t => {
wrapper.style.bottom = `${0 - (104 * t)}px`;
wrapper.style.opacity = 1 - (1 * t).toString();
},
});
return async () => {
html.classList.remove('overlay-filter');
html.style.pointerEvents = 'none';
await MamiAnimate({
duration: 200,
easing: 'inQuint',
update(t) {
wrapper.style.bottom = `${0 - (104 * t)}px`;
wrapper.style.opacity = 1 - (1 * t).toString();
},
});
};
},
};
};

View file

@ -2,7 +2,6 @@
#include parsing.js
#include title.js
#include txtrigs.js
#include url.js
#include users.js
#include weeb.js
#include sound/umisound.js

View file

@ -1,38 +0,0 @@
const $rngi = (min, max) => {
let ret = 0;
const range = max - min;
const bitsNeeded = Math.ceil(Math.log2(range));
if(bitsNeeded > 53)
return -1;
const bytesNeeded = Math.ceil(bitsNeeded / 8),
mask = Math.pow(2, bitsNeeded) - 1;
const bytes = new Uint8Array(bytesNeeded);
crypto.getRandomValues(bytes);
let p = (bytesNeeded - 1) * 8;
for(let i = 0; i < bytesNeeded; ++i) {
ret += bytes[i] * Math.pow(2, p);
p -= 8;
}
ret &= mask;
if(ret >= range)
return $rngi(min, max);
return min + ret;
};
const $rngs = (() => {
const chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789';
return length => {
let str = '';
for(let i = 0; i < length; ++i)
str += chars[$rngi(0, chars.length)];
return str;
};
})();

View file

@ -1,53 +0,0 @@
const MamiUriBase64 = (() => {
const public = {};
const textEncoder = new TextEncoder('utf-8');
const textDecoder = new TextDecoder('utf-8');
if(typeof Uint8Array.prototype.toBase64 === 'function')
public.encode = data => {
if(typeof data === 'string')
data = textEncoder.encode(data);
else if(!(data instanceof Uint8Array))
throw 'data must be a string or a Uint8Array';
return data.toBase64({
alphabet: 'base64url',
omitPadding: true,
});
};
else // toBase64 is very new, compatible impl
public.encode = data => {
if(typeof data === 'string')
data = textEncoder.encode(data);
else if(!(data instanceof Uint8Array))
throw 'data must be a string or a Uint8Array';
return btoa(Array.from(data, byte => String.fromCodePoint(byte)).join(''))
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '');
};
if(typeof Uint8Array.fromBase64 === 'function')
public.decode = (data, asString=false) => {
if(typeof data !== 'string')
throw 'data must be string';
const buffer = Uint8Array.fromBase64(data, {
alphabet: 'base64url',
});
return asString ? textDecoder.decode(buffer) : buffer;
};
else // fromBase64 is very new, compatible impl
public.decode = (data, asString=false) => {
if(typeof data !== 'string')
throw 'data must be string';
const buffer = Uint8Array.from(atob(data), str => str.codePointAt(0));
return asString ? textDecoder.decode(buffer) : buffer;
};
return public;
})();

View file

@ -1,22 +0,0 @@
Umi.URI = (function() {
const regex = new RegExp("([A-Za-z][A-Za-z0-9+\\-.]*):(?:(//)(?:((?:[A-Za-z0-9\\-._~!$&'()*+,;=:]|%[0-9A-Fa-f]{2})*)@)?((?:\\[(?:(?:(?:(?:[0-9A-Fa-f]{1,4}:){6}|::(?:[0-9A-Fa-f]{1,4}:){5}|(?:[0-9A-Fa-f]{1,4})?::(?:[0-9A-Fa-f]{1,4}:){4}|(?:(?:[0-9A-Fa-f]{1,4}:){0,1}[0-9A-Fa-f]{1,4})?::(?:[0-9A-Fa-f]{1,4}:){3}|(?:(?:[0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})?::(?:[0-9A-Fa-f]{1,4}:){2}|(?:(?:[0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})?::[0-9A-Fa-f]{1,4}:|(?:(?:[0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})?::)(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))|(?:(?:[0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})?::[0-9A-Fa-f]{1,4}|(?:(?:[0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})?::)|[Vv][0-9A-Fa-f]+\\.[A-Za-z0-9\\-._~!$&'()*+,;=:]+)\\]|(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|(?:[A-Za-z0-9\\-._~!$&'()*+,;=]|%[0-9A-Fa-f]{2})*))(?::([0-9]*))?((?:/(?:[A-Za-z0-9\\-._~!$&'()*+,;=:@]|%[0-9A-Fa-f]{2})*)*)|/((?:(?:[A-Za-z0-9\\-._~!$&'()*+,;=:@]|%[0-9A-Fa-f]{2})+(?:/(?:[A-Za-z0-9\\-._~!$&'()*+,;=:@]|%[0-9A-Fa-f]{2})*)*)?)|((?:[A-Za-z0-9\\-._~!$&'()*+,;=:@]|%[0-9A-Fa-f]{2})+(?:/(?:[A-Za-z0-9\\-._~!$&'()*+,;=:@]|%[0-9A-Fa-f]{2})*)*)|)(?:\\?((?:[A-Za-z0-9\\-._~!$&'()*+,;=:@/?]|%[0-9A-Fa-f]{2})*))?(?:\\#((?:[A-Za-z0-9\\-._~!$&'()*+,;=:@/?]|%[0-9A-Fa-f]{2})*))?");
return {
Parse: function(url) {
const match = url.match(regex);
if(match === null)
return null;
return {
Protocol: match[1] || null,
Slashes: match[2] || null,
Authority: match[3] || null,
Host: match[4] || null,
Port: match[5] || null,
Path: match[6] || match[7] || match[8] || null,
Query: match[9] || null,
Hash: match[10] || null,
};
},
};
})();

View file

@ -1,48 +1,51 @@
const $xhr = (function() {
const send = function(method, url, options, body) {
if(options === undefined)
options = {};
else if(typeof options !== 'object')
throw 'options must be undefined or an object';
Object.freeze(options);
const methods = ['GET', 'POST', 'DELETE', 'PUT', 'PATCH'];
const send = function(
method,
url,
{
authed=false,
download=null,
headers=null,
signal=null,
timeout=null,
type=null,
upload=null,
}={},
body=null
) {
const xhr = new XMLHttpRequest;
const requestHeaders = new Map;
if('headers' in options && typeof options.headers === 'object')
for(const name in options.headers)
if(options.headers.hasOwnProperty(name))
requestHeaders.set(name.toLowerCase(), options.headers[name]);
if(signal instanceof AbortSignal)
signal.onabort = () => { xhr.abort(); };
if(typeof options.download === 'function') {
xhr.onloadstart = ev => options.download(ev);
xhr.onprogress = ev => options.download(ev);
xhr.onloadend = ev => options.download(ev);
if(typeof headers === 'object')
for(const name in headers)
if(headers.hasOwnProperty(name))
requestHeaders.set(name.toLowerCase(), headers[name]);
if(typeof download === 'function') {
xhr.onloadstart = ev => { download(ev); };
xhr.onprogress = ev => { download(ev); };
xhr.onloadend = ev => { download(ev); };
}
if(typeof options.upload === 'function') {
xhr.upload.onloadstart = ev => options.upload(ev);
xhr.upload.onprogress = ev => options.upload(ev);
xhr.upload.onloadend = ev => options.upload(ev);
if(typeof upload === 'function') {
xhr.upload.onloadstart = ev => { upload(ev); };
xhr.upload.onprogress = ev => { upload(ev); };
xhr.upload.onloadend = ev => { upload(ev); };
}
if(options.authed)
xhr.withCredentials = true;
if(authed !== null)
xhr.withCredentials = authed;
if(timeout !== null)
xhr.timeout = timeout;
if(type !== null)
xhr.responseType = type;
if(typeof options.timeout === 'number')
xhr.timeout = options.timeout;
if(typeof options.type === 'string')
xhr.responseType = options.type;
if(typeof options.abort === 'function')
options.abort(() => xhr.abort());
if(typeof options.xhr === 'function')
options.xhr(() => xhr);
if(typeof body === 'object') {
if(typeof body === 'object' && body !== null) {
if(body instanceof URLSearchParams) {
requestHeaders.set('content-type', 'application/x-www-form-urlencoded');
} else if(body instanceof FormData) {
@ -87,17 +90,19 @@ const $xhr = (function() {
});
};
xhr.onabort = ev => reject({
abort: true,
xhr: xhr,
ev: ev,
});
xhr.onabort = ev => {
reject({
aborted: true,
ev,
});
};
xhr.onerror = ev => reject({
abort: false,
xhr: xhr,
ev: ev,
});
xhr.onerror = ev => {
reject({
aborted: false,
ev,
});
};
xhr.open(method, url);
for(const [name, value] of requestHeaders)
@ -106,12 +111,9 @@ const $xhr = (function() {
});
};
return {
send: send,
get: (url, options, body) => send('GET', url, options, body),
post: (url, options, body) => send('POST', url, options, body),
delete: (url, options, body) => send('DELETE', url, options, body),
patch: (url, options, body) => send('PATCH', url, options, body),
put: (url, options, body) => send('PUT', url, options, body),
};
const pub = { send };
for(const method of methods)
pub[method.toLowerCase()] = (...args) => send(method, ...args);
return pub;
})();