4 Commits
Author SHA1 Message Date
sugoidogo 5afe6f2cea fix node20+ imports
Release / release (push) Successful in 22s
2025-12-31 02:18:46 -08:00
sugoidogo e5fb6bf89c fix release script
Release / release (push) Successful in 22s
2025-12-28 18:20:09 -08:00
sugoidogo a1632a3520 update project files
Release / release (push) Failing after 19s
2025-12-28 18:16:35 -08:00
sugoidogo 98c2dac434 switch to typescript 2025-12-28 18:08:57 -08:00
16 changed files with 237 additions and 523 deletions
+1
View File
@@ -13,6 +13,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- run: npm ci
- run: npm version from-git --allow-same-version --no-git-tag-version
- run: npm config set registry https://gitea.sugoidogo.com/api/packages/sugoidogo/npm/
- run: npm config set -- '//gitea.sugoidogo.com/api/packages/sugoidogo/npm/:_authToken' "$PACKAGES_KEY"
+2 -1
View File
@@ -1,3 +1,4 @@
node_modules
*.tgz
docs
docs
dist
-169
View File
@@ -1,169 +0,0 @@
/** @module FormDataDeep */
/**
* Usage: `Array<HTMLElement>.filter(removeChildren)`
*
* Removes elements from the array which are children of other elements in the array.
*/
export function removeChildren(element,index,array){
for(const sibling of array){
if(!sibling.isSameNode(element) && sibling.contains(element)){
return false
}
}
return true
}
/**
* Appends a new value onto an existing key inside an object, or adds the key if it does not already exist.
* @param {Object} object
* @param {String} name
* @param {*} value
*/
export function append(object,name,value){
if(name in object){
if(object[name] instanceof Array){
object[name].push(value)
}else{
object[name]=[object[name],value]
}
}else{
object[name]=value
}
}
/**
* Get `form` data as an object, including nested forms.
* @param {HTMLFormElement} form
* @param {Boolean} primitives whether to convert string values to primitives. Defaults `false`.
*/
export function getFormDataDeep(form,primitives=false){
const formDataDeep={}
const formData=new FormData(form)
for(const name of formData.keys()){
const value=formData.getAll(name)
if(value.length==0){
continue
}
if(primitives){
for(let i=0;i<value.length;i++){
if(!isNaN(+value[i])){
value[i]=+value[i]
}
if(value[i]=='on'){
value[i]=true
}
}
}
if(value.length==1){
formDataDeep[name]=value[0]
}else{
formDataDeep[name]=value
}
}
for(const child of [...form.querySelectorAll('form')].filter(removeChildren)){
const value=getFormDataDeep(child,primitives)
if(Object.keys(value)==0){
continue
}
const name=child.name
append(formDataDeep,name,value)
}
return formDataDeep
}
/**
* Convenience class to have a similar interface to FormData
*/
export class FormDataDeep extends FormData {
/**
* Get `form` data as an object, including nested forms.
* @param {HTMLFormElement | Object} form the root form element, or a JSON Object containing form data.
* @param {Boolean} primitives whether to convert string values to primitives. Defaults `false`.
*/
constructor(form,primitives=false){
super()
if(form instanceof HTMLFormElement){
Object.assign(this,getFormDataDeep(form,primitives))
}else{
Object.assign(this,form)
}
}
append(name,value,filename=undefined){
if(filename!==undefined){
value=new File([value],filename)
}
append(this,name,value)
}
delete(name){
delete this[name]
}
entries(){
return Object.entries(this).filter((value)=>!value instanceof Function)
}
get(name){
if(this[name] instanceof Array){
return this[name].at(-1)
}else{
return this[name]
}
}
getAll(name){
if(this[name] instanceof Array){
return this[name]
}else{
return [this[name]]
}
}
has(name){
return name in this
}
keys(){
return this.entries().keys()
}
set(name,value,filename=undefined){
if(filename!==undefined){
value=new File([value],filename)
}
this[name]=value
}
values(){
return this.entries().values()
}
/**
* Applies the values in this object to the target form.
* @param {HTMLFormElement} form
*/
apply(form){
for(const element of form.elements){
const name=element.name
if(! name in this){
continue
}
let value
if(this[name] instanceof Array){
value=this[name].shift()
}else{
value=this[name]
delete this[name]
}
switch(element.type){
case 'radio':
case 'checkbox':{
element.checked=true
break
}
default:{
element.value=value
break
}
}
}
for(const child of [...form.querySelectorAll('form')].filter(removeChildren)){
const name=child.name
if(! name in this){
continue
}
new FormDataDeep(this[name]).apply(form)
}
}
}
export default FormDataDeep
-5
View File
@@ -1,5 +0,0 @@
export { loadAsset } from './AssetLoader'
export * as CSSRandom from './CSSRandom'
export { FileReaderAsync } from './FileReaderAsync'
export { fontScale } from './fontScale'
export { FormDataDeep, getFormDataDeep } from './FormDataDeep'
+1 -1
View File
@@ -2,6 +2,6 @@
"compilerOptions": {
"target": "es2022",
"module": "es2022",
"lib": ["ESNext"],
"lib": ["ES2022"],
}
}
-11
View File
@@ -1,11 +0,0 @@
{
"source":{
"include": "./dist/"
},
"opts":{
"destination": "./docs/",
"recurse": true,
"readme": "./README.md"
},
"plugins": ["plugins/markdown"]
}
+14 -330
View File
@@ -1,353 +1,37 @@
{
"name": "js-util",
"name": "@sugoidogo/js-util",
"version": "0.1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "js-util",
"name": "@sugoidogo/js-util",
"version": "0.1.0",
"devDependencies": {
"@types/web": "^0.0.301",
"jsdoc": "^4.0.5"
"@types/web": "^0.0.309",
"typescript": "^5.9.3"
}
},
"node_modules/@babel/helper-string-parser": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
"integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-validator-identifier": {
"version": "7.28.5",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
"integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/parser": {
"version": "7.28.5",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz",
"integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/types": "^7.28.5"
},
"bin": {
"parser": "bin/babel-parser.js"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@babel/types": {
"version": "7.28.5",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz",
"integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-string-parser": "^7.27.1",
"@babel/helper-validator-identifier": "^7.28.5"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@jsdoc/salty": {
"version": "0.2.9",
"resolved": "https://registry.npmjs.org/@jsdoc/salty/-/salty-0.2.9.tgz",
"integrity": "sha512-yYxMVH7Dqw6nO0d5NIV8OQWnitU8k6vXH8NtgqAfIa/IUqRMxRv/NUJJ08VEKbAakwxlgBl5PJdrU0dMPStsnw==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"lodash": "^4.17.21"
},
"engines": {
"node": ">=v12.0.0"
}
},
"node_modules/@types/linkify-it": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz",
"integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/markdown-it": {
"version": "14.1.2",
"resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz",
"integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@types/linkify-it": "^5",
"@types/mdurl": "^2"
}
},
"node_modules/@types/mdurl": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz",
"integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/web": {
"version": "0.0.301",
"resolved": "https://registry.npmjs.org/@types/web/-/web-0.0.301.tgz",
"integrity": "sha512-GAoFovImstIY39EAwGazoeal9+uy+zZz0vLhz/MDbqaHAbK0d8yzF8EKtlSkwNzZu9WEIyYdfSGwVTUSnJMtpw==",
"version": "0.0.309",
"resolved": "https://registry.npmjs.org/@types/web/-/web-0.0.309.tgz",
"integrity": "sha512-8PEVqnrkAxokSEJPYjNqzmoOELlIrlHG4xsEb6oLOxMzsgqn/n6eYrgSYKG1QgptAcP88mmL7h2cpJQU60F3Qw==",
"dev": true,
"license": "Apache-2.0"
},
"node_modules/argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"dev": true,
"license": "Python-2.0"
},
"node_modules/bluebird": {
"version": "3.7.2",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
"integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
"dev": true,
"license": "MIT"
},
"node_modules/catharsis": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz",
"integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==",
"dev": true,
"license": "MIT",
"dependencies": {
"lodash": "^4.17.15"
},
"engines": {
"node": ">= 10"
}
},
"node_modules/entities": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
"dev": true,
"license": "BSD-2-Clause",
"engines": {
"node": ">=0.12"
},
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/escape-string-regexp": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz",
"integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/graceful-fs": {
"version": "4.2.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
"dev": true,
"license": "ISC"
},
"node_modules/js2xmlparser": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz",
"integrity": "sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==",
"node_modules/typescript": {
"version": "5.9.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"xmlcreate": "^2.0.4"
}
},
"node_modules/jsdoc": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.5.tgz",
"integrity": "sha512-P4C6MWP9yIlMiK8nwoZvxN84vb6MsnXcHuy7XzVOvQoCizWX5JFCBsWIIWKXBltpoRZXddUOVQmCTOZt9yDj9g==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@babel/parser": "^7.20.15",
"@jsdoc/salty": "^0.2.1",
"@types/markdown-it": "^14.1.1",
"bluebird": "^3.7.2",
"catharsis": "^0.9.0",
"escape-string-regexp": "^2.0.0",
"js2xmlparser": "^4.0.2",
"klaw": "^3.0.0",
"markdown-it": "^14.1.0",
"markdown-it-anchor": "^8.6.7",
"marked": "^4.0.10",
"mkdirp": "^1.0.4",
"requizzle": "^0.2.3",
"strip-json-comments": "^3.1.0",
"underscore": "~1.13.2"
},
"bin": {
"jsdoc": "jsdoc.js"
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=12.0.0"
"node": ">=14.17"
}
},
"node_modules/klaw": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz",
"integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==",
"dev": true,
"license": "MIT",
"dependencies": {
"graceful-fs": "^4.1.9"
}
},
"node_modules/linkify-it": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz",
"integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"uc.micro": "^2.0.0"
}
},
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"dev": true,
"license": "MIT"
},
"node_modules/markdown-it": {
"version": "14.1.0",
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz",
"integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"argparse": "^2.0.1",
"entities": "^4.4.0",
"linkify-it": "^5.0.0",
"mdurl": "^2.0.0",
"punycode.js": "^2.3.1",
"uc.micro": "^2.1.0"
},
"bin": {
"markdown-it": "bin/markdown-it.mjs"
}
},
"node_modules/markdown-it-anchor": {
"version": "8.6.7",
"resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.7.tgz",
"integrity": "sha512-FlCHFwNnutLgVTflOYHPW2pPcl2AACqVzExlkGQNsi4CJgqOHN7YTgDd4LuhgN1BFO3TS0vLAruV1Td6dwWPJA==",
"dev": true,
"license": "Unlicense",
"peerDependencies": {
"@types/markdown-it": "*",
"markdown-it": "*"
}
},
"node_modules/marked": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz",
"integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==",
"dev": true,
"license": "MIT",
"bin": {
"marked": "bin/marked.js"
},
"engines": {
"node": ">= 12"
}
},
"node_modules/mdurl": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz",
"integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==",
"dev": true,
"license": "MIT"
},
"node_modules/mkdirp": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
"dev": true,
"license": "MIT",
"bin": {
"mkdirp": "bin/cmd.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/punycode.js": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz",
"integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/requizzle": {
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.4.tgz",
"integrity": "sha512-JRrFk1D4OQ4SqovXOgdav+K8EAhSB/LJZqCz8tbX0KObcdeM15Ss59ozWMBWmmINMagCwmqn4ZNryUGpBsl6Jw==",
"dev": true,
"license": "MIT",
"dependencies": {
"lodash": "^4.17.21"
}
},
"node_modules/strip-json-comments": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
"integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/uc.micro": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz",
"integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==",
"dev": true,
"license": "MIT"
},
"node_modules/underscore": {
"version": "1.13.7",
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.7.tgz",
"integrity": "sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g==",
"dev": true,
"license": "MIT"
},
"node_modules/xmlcreate": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz",
"integrity": "sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==",
"dev": true,
"license": "Apache-2.0"
}
}
}
+9 -3
View File
@@ -2,10 +2,16 @@
"name": "@sugoidogo/js-util",
"version": "0.1.0",
"main": "dist/index.js",
"files": ["dist/"],
"scripts": {
"prepack": "tsc"
},
"files": [
"dist/",
"src/"
],
"type": "module",
"devDependencies": {
"@types/web": "^0.0.301",
"jsdoc": "^4.0.5"
"@types/web": "^0.0.309",
"typescript": "^5.9.3"
}
}
+1 -1
View File
@@ -77,7 +77,7 @@ export async function loadAsset(dataURL,id=undefined,unsafe=false){
}
default:{
if(unsafe){
window[id]=await fetch(dataURL)
Object.defineProperty(window,id,await fetch(dataURL))
}
return await fetch(dataURL)
}
+4 -2
View File
@@ -3,6 +3,8 @@
* @module CSSRandom
*/
(function CSSRandom() {
document.documentElement.style.setProperty('--random', Math.random())
document.documentElement.style.setProperty('--random', Math.random().toString())
requestAnimationFrame(CSSRandom)
})()
})()
export {}
+177
View File
@@ -0,0 +1,177 @@
/** @module FormDataDeep */
/**
* Usage: `Array<HTMLElement>.filter(removeChildren)`
*
* Removes elements from the array which are children of other elements in the array.
*/
export function removeChildren(element, index, array) {
for (const sibling of array) {
if (!sibling.isSameNode(element) && sibling.contains(element)) {
return false
}
}
return true
}
/**
* Appends a new value onto an existing key inside an object, or adds the key if it does not already exist.
* @param {Object} object
* @param {String} name
* @param {*} value
*/
export function append(object, name, value) {
if (name in object) {
if (object[name] instanceof Array) {
object[name].push(value)
} else {
object[name] = [object[name], value]
}
} else {
object[name] = value
}
}
/**
* Get `form` data as an object, including nested forms.
* @param {HTMLFormElement} form
* @param {Boolean} primitives whether to convert string values to primitives. Defaults `false`.
*/
export function getFormDataDeep(form, primitives = false) {
const formDataDeep = {}
const formData = new FormData(form)
for (const name of formData.keys()) {
const value: any = formData.getAll(name)
if (value.length == 0) {
continue
}
if (primitives) {
for (let i = 0; i < value.length; i++) {
if (!isNaN(+value[i])) {
value[i] = +value[i]
}
if (value[i] == 'on') {
value[i] = true
}
}
}
if (value.length == 1) {
formDataDeep[name] = value[0]
} else {
formDataDeep[name] = value
}
}
for (const child of [...form.querySelectorAll('form')].filter(removeChildren)) {
const value = getFormDataDeep(child, primitives)
if (Object.keys(value).length === 0) {
continue
}
const name = child.name
append(formDataDeep, name, value)
}
return formDataDeep
}
/**
* Convenience class to have a similar interface to FormData
*/
export class FormDataDeep implements FormData {
#formdatadeep:{[key:string]: any} = {}
/**
* Get `form` data as an object, including nested forms.
* @param {HTMLFormElement | Object} form the root form element, or a JSON Object containing form data.
* @param {Boolean} primitives whether to convert string values to primitives. Defaults `false`.
*/
constructor(form, primitives = false) {
if (form instanceof HTMLFormElement) {
Object.assign(this.#formdatadeep, getFormDataDeep(form, primitives))
} else {
Object.assign(this.#formdatadeep, form)
}
}
forEach(callbackfn: (value: FormDataEntryValue, key: string, parent: FormData) => void, thisArg: any=this): void {
for (const [key,value] of thisArg.entries()) {
callbackfn(value,key,thisArg)
}
}
[Symbol.iterator](): FormDataIterator<[string, FormDataEntryValue]> {
return this.entries()
}
append(name, value, filename = undefined) {
if (filename !== undefined) {
value = new File([value], filename)
}
append(this.#formdatadeep, name, value)
}
delete(name) {
delete this.#formdatadeep[name]
}
entries() {
return Object.entries(this.#formdatadeep)[Symbol.iterator]()
}
get(name) {
if (this.#formdatadeep[name] instanceof Array) {
return this.#formdatadeep[name].at(-1)
} else {
return this.#formdatadeep[name]
}
}
getAll(name) {
if (this.#formdatadeep[name] instanceof Array) {
return this.#formdatadeep[name]
} else {
return [this.#formdatadeep[name]]
}
}
has(name) {
return name in this.#formdatadeep
}
keys() {
return Object.keys(this.#formdatadeep)[Symbol.iterator]()
}
set(name, value, filename = undefined) {
if (filename !== undefined) {
value = new File([value], filename)
}
this.#formdatadeep[name] = value
}
values() {
return Object.values(this.#formdatadeep)[Symbol.iterator]()
}
/**
* Applies the values in this object to the target form.
* @param {HTMLFormElement} form
*/
apply(form) {
for (const element of form.elements) {
const name = element.name
if (!(name in this.#formdatadeep)) {
continue
}
let value
if (this.#formdatadeep[name] instanceof Array) {
value = this.#formdatadeep[name].shift()
} else {
value = this.#formdatadeep[name]
delete this.#formdatadeep[name]
}
switch (element.type) {
case 'radio':
case 'checkbox': {
element.checked = true
break
}
default: {
element.value = value
break
}
}
}
for (const child of [...form.querySelectorAll('form')].filter(removeChildren)) {
const name = child.name
if (!(name in this.#formdatadeep)) {
continue
}
new FormDataDeep(this.#formdatadeep[name]).apply(form)
}
}
}
export default FormDataDeep
+8
View File
@@ -0,0 +1,8 @@
export async function forAsync<T, U>(iterable: Iterable<T> | AsyncIterable<T>, callback: (value: T) => Promise<U> | U): Promise<Array<U>> {
const promises: Array<Promise<U> | U> = []
for await (const iterableValue of iterable) {
promises.push(callback(iterableValue))
}
return Promise.all(promises)
}
+6
View File
@@ -0,0 +1,6 @@
export { loadAsset } from './AssetLoader.ts'
export * as CSSRandom from './CSSRandom.ts'
export { FileReaderAsync } from './FileReaderAsync.ts'
export { fontScale } from './fontScale.ts'
export { FormDataDeep, getFormDataDeep } from './FormDataDeep.ts'
export { forAsync } from './forAsync.ts'
+14
View File
@@ -0,0 +1,14 @@
{
"compilerOptions": {
"target": "es2022",
"module": "es2022",
"lib": ["ES2022"],
"moduleResolution": "node",
"sourceMap": true,
"declaration": true,
"declarationMap": true,
"outDir": "dist",
"rewriteRelativeImportExtensions": true
},
"include": ["src/index.ts"]
}