diff --git a/dist/FormDataDeep.js b/dist/FormDataDeep.js deleted file mode 100644 index 4205fbd..0000000 --- a/dist/FormDataDeep.js +++ /dev/null @@ -1,169 +0,0 @@ -/** @module FormDataDeep */ - -/** - * Usage: `Array.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 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 \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index aa3b744..b03919c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,15 @@ { - "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" + "jsdoc": "^4.0.5", + "typescript": "^5.9.3" } }, "node_modules/@babel/helper-string-parser": { @@ -101,13 +101,6 @@ "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==", - "dev": true, - "license": "Apache-2.0" - }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -328,6 +321,20 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "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", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/uc.micro": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", diff --git a/package.json b/package.json index ac4ab05..f083bf5 100644 --- a/package.json +++ b/package.json @@ -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" + "jsdoc": "^4.0.5", + "typescript": "^5.9.3" } } diff --git a/dist/AssetLoader.js b/src/AssetLoader.ts similarity index 97% rename from dist/AssetLoader.js rename to src/AssetLoader.ts index eae390c..6cf191e 100644 --- a/dist/AssetLoader.js +++ b/src/AssetLoader.ts @@ -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) } diff --git a/dist/CSSRandom.js b/src/CSSRandom.ts similarity index 87% rename from dist/CSSRandom.js rename to src/CSSRandom.ts index dbf7fed..03c15d0 100644 --- a/dist/CSSRandom.js +++ b/src/CSSRandom.ts @@ -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) -})() \ No newline at end of file +})() + +export {} \ No newline at end of file diff --git a/dist/FileReaderAsync.js b/src/FileReaderAsync.ts similarity index 100% rename from dist/FileReaderAsync.js rename to src/FileReaderAsync.ts diff --git a/src/FormDataDeep.ts b/src/FormDataDeep.ts new file mode 100644 index 0000000..aeeb6be --- /dev/null +++ b/src/FormDataDeep.ts @@ -0,0 +1,177 @@ +/** @module FormDataDeep */ + +/** + * Usage: `Array.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 \ No newline at end of file diff --git a/dist/fontScale.js b/src/fontScale.ts similarity index 100% rename from dist/fontScale.js rename to src/fontScale.ts diff --git a/src/forAsync.ts b/src/forAsync.ts new file mode 100644 index 0000000..68267ce --- /dev/null +++ b/src/forAsync.ts @@ -0,0 +1,8 @@ + +export async function forAsync(iterable: Iterable | AsyncIterable, callback: (value: T) => Promise | U): Promise> { + const promises: Array | U> = [] + for await (const iterableValue of iterable) { + promises.push(callback(iterableValue)) + } + return Promise.all(promises) +} \ No newline at end of file diff --git a/dist/index.js b/src/index.ts similarity index 86% rename from dist/index.js rename to src/index.ts index ab3f040..62bf158 100644 --- a/dist/index.js +++ b/src/index.ts @@ -3,3 +3,4 @@ export * as CSSRandom from './CSSRandom' export { FileReaderAsync } from './FileReaderAsync' export { fontScale } from './fontScale' export { FormDataDeep, getFormDataDeep } from './FormDataDeep' +export { forAsync } from './forAsync' \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..834480c --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "target": "es2022", + "module": "es2022", + "moduleResolution": "node", + "sourceMap": true, + "declaration": true, + "declarationMap": true, + "outDir": "dist" + }, + "include": ["src/index.ts"] +} \ No newline at end of file