├── .github └── FUNDING.yml ├── LICENSE ├── README.md ├── abstract class example ├── async sequentializer ├── average ├── capitalize words ├── date formatter ├── debounce callback ├── deep clone ├── deep equality ├── deep freeze ├── deep-value-retriever ├── element-sibling ├── extend object ├── filter-iterables ├── fixed size array ├── forEach iterables ├── group by ├── is empty ├── key-by ├── last item getter ├── map iterables ├── merge objects ├── number range generator ├── observable ├── observe element ├── once event ├── polling function ├── promisify ├── random number generator ├── required arguments ├── stringifier ├── type check ├── unique id generators ├── until event └── uploader /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [beforesemicolon] 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Before Semicolon 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # javascript-solutions 2 | Several code solution snippets in Javascript 3 | 4 | ### Read About These in: 5 | 6 | - [25 JavaScript Tricks You Need To Know About](https://beforesemicolon.medium.com/25-javascript-code-solutions-utility-tricks-you-need-to-know-about-3023f7ed993e) 7 | - [25 More JavaScript Tricks You Need To Know About](https://beforesemicolon.medium.com/25-more-javascript-code-solutions-you-need-to-know-about-6ee344c2da58) 8 | - [Javascript Design Patterns to Improve your App with](https://beforesemicolon.medium.com/10-javascript-design-patterns-to-improve-your-code-with-44c6f6c2ea94) 9 | -------------------------------------------------------------------------------- /abstract class example: -------------------------------------------------------------------------------- 1 | class AbstractClass { 2 | constructor() { 3 | if(this.constructor.name === 'AbstractClass') { 4 | throw new Error('"AbstractClass" is an Abstract Class. It can only be extended') 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /async sequentializer: -------------------------------------------------------------------------------- 1 | const asyncSequentializer = (() => { 2 | const toPromise = (x) => { 3 | if(x instanceof Promise) { // if promise just return it 4 | return x; 5 | } 6 | 7 | if(typeof x === 'function') { 8 | // if function is not async this will turn its result into a promise 9 | // if it is async this will await for the result 10 | return (async () => await x())(); 11 | } 12 | 13 | return Promise.resolve(x) 14 | } 15 | 16 | return (list) => { 17 | const results = []; 18 | 19 | return list 20 | .reduce((lastPromise, currentPromise) => { 21 | return lastPromise.then(res => { 22 | results.push(res); // collect the results 23 | return toPromise(currentPromise); 24 | }); 25 | }, toPromise(list.shift())) 26 | // collect the final result and return the array of results as resolved promise 27 | .then(res => [...results, res]); 28 | } 29 | })(); 30 | -------------------------------------------------------------------------------- /average: -------------------------------------------------------------------------------- 1 | const average = (nList) => (nList.reduce((avg, n) => avg + n, 0)) / nList.length; 2 | -------------------------------------------------------------------------------- /capitalize words: -------------------------------------------------------------------------------- 1 | function capitalize(str) { 2 | return str.replace(/(\w+)/g, (_, word) => ( 3 | word[0].toUpperCase() + word.substring(1) 4 | )); 5 | } 6 | -------------------------------------------------------------------------------- /date formatter: -------------------------------------------------------------------------------- 1 | function formatDate(date, formatStr = "MMMM DD, YYYY", lang = 'default') { 2 | date = new Date(date); 3 | 4 | const day = date.toLocaleString(lang, { weekday: "long" }); 5 | const month = date.toLocaleString(lang, { month: "long" }); 6 | const firstDayOfYear = new Date(date.getFullYear(), 0, 1); 7 | const pastDaysOfYear = (date - firstDayOfYear) / 86400000; // one day; 8 | const week = Math.ceil((pastDaysOfYear + firstDayOfYear.getDay() + 1) / 7); 9 | 10 | return formatStr 11 | .replace(/\bYYYY\b/g, date.getFullYear()) 12 | .replace(/\bYY\b/g, `${date.getFullYear()}`.substring(2)) 13 | .replace(/\bWW\b/g, week.toString().padStart(2, "0")) 14 | .replace(/\bW\b/g, week) 15 | .replace(/\bDDDD\b/g, day) 16 | .replace(/\bDDD\b/g, day.substring(0, 3)) 17 | .replace(/\bDD\b/g, date.getDate().toString().padStart(2, "0")) 18 | .replace(/\bD\b/g, date.getDate()) 19 | .replace(/\bMMMM\b/g, month) 20 | .replace(/\bMMM\b/g, month.substring(0, 3)) 21 | .replace(/\bMM\b/g, (date.getMonth() + 1).toString().padStart(2, "0")) 22 | .replace(/\bM\b/g, date.getMonth() + 1); 23 | } 24 | -------------------------------------------------------------------------------- /debounce callback: -------------------------------------------------------------------------------- 1 | function debounce(duration = 0, cb) { 2 | let timer = null; 3 | 4 | return (...args) => { 5 | clearTimeout(timer); 6 | timer = setTimeout(() => { 7 | cb(...args); 8 | }, duration); 9 | }; 10 | } 11 | -------------------------------------------------------------------------------- /deep clone: -------------------------------------------------------------------------------- 1 | const deepCloneObj = obj => { 2 | let clone = obj; 3 | 4 | if (obj && typeof obj === "object") { 5 | clone = obj.constructor 6 | ? new obj.constructor() 7 | : Object.create(null); 8 | 9 | switch(true) { 10 | case obj instanceof Map: 11 | obj.forEach((v, k) => clone.set(k, deepCloneObj(v))) 12 | break; 13 | case obj instanceof Set: 14 | obj.forEach((v) => clone.add(deepCloneObj(v))) 15 | break; 16 | default: 17 | Object.getOwnPropertyNames(obj).forEach( 18 | prop => (clone[prop] = deepCloneObj(obj[prop])) 19 | ); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /deep equality: -------------------------------------------------------------------------------- 1 | function isEqualFunctions(fn1, fn2) { 2 | if(fn1 === fn2) return true; 3 | 4 | return typeof fn1 === 'function' 5 | && typeof fn2 === 'function' 6 | && fn1.toString() === fn2.toString(); 7 | } 8 | 9 | function isEqualSymbols(symb1, symb2) { 10 | if(symb1 === symb2) return true; 11 | 12 | return typeof symb1 === 'symbol' 13 | && typeof symb2 === 'symbol' 14 | && symb1.toString() === symb2.toString(); 15 | } 16 | 17 | function isEqualMap(map1, map2) { 18 | if(map1 === map2) return true; 19 | 20 | if( 21 | !(map1 instanceof Map) 22 | || !(map2 instanceof Map) 23 | || map1.size !== map2.size 24 | ) return false 25 | 26 | const keys1 = Array.from(map1.keys()); 27 | const keys2 = Array.from(map2.keys()); 28 | const values1 = Array.from(map1.values()); 29 | const values2 = Array.from(map2.values()); 30 | 31 | return keys1.every((k, i) => isEqual(k, keys2[i])) 32 | && values1.every((v, i) => isEqual(v, values2[i])) 33 | } 34 | 35 | function isEqualSet(set1, set2) { 36 | if(set1 === set2) return true; 37 | 38 | if( 39 | !(set1 instanceof Set) 40 | || !(set2 instanceof Set) 41 | || set1.size !== set2.size 42 | ) return false; 43 | 44 | const values1 = Array.from(set1.values()); 45 | const values2 = Array.from(set2.values()); 46 | 47 | return values1.every((v, i) => isEqual(v, values2[i])) 48 | } 49 | 50 | function isEqual(a, b) { 51 | // handles primitives and same instances 52 | if(a === b) return true; 53 | 54 | switch(`${typeof a}:${typeof b}`) { 55 | case 'symbol:symbol': 56 | return isEqualSymbols(a, b); 57 | case 'function:function': 58 | return isEqualFunctions(a, b); 59 | case 'object:object': 60 | // inner cases for objects other than Array and Object 61 | if(a instanceof Map) return isEqualMap(a, b); 62 | if(a instanceof Set) return isEqualSet(a, b); 63 | 64 | // handles Object and Array 65 | const keysA = Object.keys(a); 66 | const keysB = Object.keys(b); 67 | 68 | if (keysA.length != keysB.length) return false; 69 | 70 | for (let key of keysA) { 71 | if (!keysB.includes(key) || !isEqual(a[key], b[key])) return false; 72 | } 73 | 74 | return true; 75 | default: 76 | // handles when a and b is of different types 77 | return false; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /deep freeze: -------------------------------------------------------------------------------- 1 | const deepFreeze = obj => { 2 | if (obj && typeof obj === "object") { 3 | if(!Object.isFrozen(obj)) { 4 | Object.freeze(obj); 5 | } 6 | 7 | Object.getOwnPropertyNames(obj).forEach(prop => deepFreeze(obj[prop])); 8 | } 9 | 10 | return obj; 11 | }; 12 | -------------------------------------------------------------------------------- /deep-value-retriever: -------------------------------------------------------------------------------- 1 | function deepValue(dict, path) { 2 | path = Array.isArray(path) ? path : path.split('.'); 3 | 4 | const value = path.reduce((obj, key) => ( 5 | // prevents error on retrieving key in undefined 6 | obj === undefined 7 | ? null 8 | // check if Map otherwise it is object like 9 | : obj instanceof Map 10 | ? obj.get(key) ?? obj[key] 11 | : obj[key] 12 | ), dict); 13 | 14 | return value === undefined ? null : value; 15 | } 16 | -------------------------------------------------------------------------------- /element-sibling: -------------------------------------------------------------------------------- 1 | function element(selectorOrElement) { 2 | const element = selectorOrElement instanceof Element 3 | ? selectorOrElement 4 | : document.body.querySelector(selectorOrElement); 5 | 6 | return { 7 | get self() { 8 | return element; 9 | }, 10 | get nextElement() { 11 | return element.nextElementSibling; 12 | }, 13 | get prevElement() { 14 | return element.previousElementSibling; 15 | }, 16 | get siblings() { 17 | return [...element.parentNode.children].filter(el => el !== element); 18 | }, 19 | get nextSiblings() { 20 | const siblings = []; 21 | let nextElement = element.nextElementSibling; 22 | 23 | while(nextElement) { 24 | siblings.push(nextElement); 25 | nextElement = nextElement.nextElementSibling 26 | } 27 | 28 | return siblings; 29 | }, 30 | get previousSiblings() { 31 | const siblings = []; 32 | [...element.parentNode.children].some(el => { 33 | if(el !== element)siblings.push(el); 34 | return el === element; 35 | }); 36 | 37 | return siblings; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /extend object: -------------------------------------------------------------------------------- 1 | function extendObject(element) { 2 | const extended = new WeakMap(); 3 | extended.set(element, {}); 4 | 5 | return { 6 | obj: element, 7 | set(key, value) { 8 | if(this.obj) extended.get(this.obj)[key] = value; 9 | }, 10 | get(key) { 11 | return this.obj ? extended.get(this.obj)[key] : null; 12 | }, 13 | reflect(key, value = null) { 14 | if(!this.obj) return; 15 | 16 | if(value === null) { 17 | return this.obj[key]; 18 | } else { 19 | this.obj[key] = value; 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /filter-iterables: -------------------------------------------------------------------------------- 1 | // uses the forEach from this directory 2 | 3 | function filter(list, callback) { 4 | const newList = []; 5 | 6 | return (() => { 7 | forEach(list, (...args) => { 8 | const res = callback(...args); 9 | if(Boolean(res)) { 10 | newList.push(args[0]); 11 | } 12 | }) 13 | 14 | return newList; 15 | })() 16 | } 17 | -------------------------------------------------------------------------------- /fixed size array: -------------------------------------------------------------------------------- 1 | function createFixedSizeArray(...items) { 2 | let array = []; 3 | 4 | if (items.length === 1) { 5 | array = (new Array(items[0])).fill(undefined); 6 | } else { 7 | array = items.slice(0) 8 | } 9 | 10 | Object.seal(array); 11 | 12 | return array; 13 | } 14 | 15 | // or 16 | class FixedArray { 17 | constructor(...items) { 18 | let array = []; 19 | 20 | if (items.length === 1) { 21 | array = (new Array(items[0])).fill(undefined); 22 | } else { 23 | array = items.slice(0) 24 | } 25 | 26 | Object.seal(array); 27 | 28 | return array; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /forEach iterables: -------------------------------------------------------------------------------- 1 | function forEach(list, callback) { 2 | if(!list || typeof list !== 'object') return; 3 | 4 | const entries = list instanceof Map || list instanceof Set 5 | ? Array.from(list.entries()) 6 | : list instanceof Iterator 7 | ? list 8 | : Object.entries(list); 9 | 10 | let index = 0; 11 | for(const item of entries) { 12 | let res = false; 13 | if(Array.isArray(item)) { 14 | res = callback(item[1], item[0] || index++, list); 15 | } else { 16 | res = callback(item, index++, list); 17 | } 18 | 19 | if(res === true) break; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /group by: -------------------------------------------------------------------------------- 1 | function groupBy(obj, key) { 2 | const values = obj instanceof Map || obj instanceof Set 3 | ? Array.from(obj.values()) 4 | : Object.values(obj); 5 | 6 | return values.reduce((keyedObj, value) => { 7 | const groupKey = value[key]; 8 | if(!Array.isArray(keyedObj[groupKey])) { 9 | keyedObj[groupKey] = [value]; 10 | } else { 11 | keyedObj[groupKey].push(value); 12 | } 13 | 14 | return keyedObj; 15 | }, {}); 16 | } 17 | -------------------------------------------------------------------------------- /is empty: -------------------------------------------------------------------------------- 1 | function isEmpty(x) { 2 | if(Array.isArray(x) 3 | || typeof x === 'string' 4 | || x instanceof String 5 | ) { 6 | return x.length === 0; 7 | } 8 | 9 | if(x instanceof Map || x instanceof Set) { 10 | return x.size === 0; 11 | } 12 | 13 | if(({}).toString.call(x) === '[object Object]') { 14 | return Object.keys(x).length === 0; 15 | } 16 | 17 | return false; 18 | } 19 | -------------------------------------------------------------------------------- /key-by: -------------------------------------------------------------------------------- 1 | function keyBy(obj, key) { 2 | const values = obj instanceof Map 3 | ? Array.from(obj.values()) 4 | : Object.values(obj); 5 | 6 | return values.reduce((keyedObj, value) => ( 7 | {...keyedObj, [value[key]]: value} 8 | ), {}); 9 | } 10 | -------------------------------------------------------------------------------- /last item getter: -------------------------------------------------------------------------------- 1 | function lastItem(list) { 2 | if(Array.isArray(list)) { 3 | return list.slice(-1)[0]; 4 | } 5 | 6 | if(list instanceof Set) { 7 | return Array.from(list).slice(-1)[0]; 8 | } 9 | 10 | if(list instanceof Map) { 11 | return Array.from(list.values()).slice(-1)[0]; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /map iterables: -------------------------------------------------------------------------------- 1 | function map(list, callback) { 2 | list = ({}).toString.call(list) === '[object Object]' 3 | ? Object.values(list) : list; 4 | return Array.from(list, callback) 5 | } 6 | -------------------------------------------------------------------------------- /merge objects: -------------------------------------------------------------------------------- 1 | function mergeObjects(a, b) { 2 | if(a === null || typeof a !== 'object') return b; 3 | if(b === null || typeof b !== 'object') return b; 4 | 5 | const obj = Array.isArray(a) ? [...a] : a; 6 | 7 | for(const key in b) { 8 | if(b.hasOwnProperty(key)) { 9 | obj[key] = mergeObjects(obj[key], b[key]); 10 | } 11 | } 12 | 13 | return obj; 14 | } 15 | -------------------------------------------------------------------------------- /number range generator: -------------------------------------------------------------------------------- 1 | function range(maxOrStart, end = null, step = null) { 2 | if(!end) { 3 | return Array.from({length: maxOrStart}, (_, i) => i) 4 | } 5 | 6 | if(end <= maxOrStart) { 7 | return []; 8 | } 9 | 10 | if(step !== null) { 11 | return Array.from( 12 | {length: Math.ceil(((end - maxOrStart) / step))}, 13 | (_, i) => (i * step) + maxOrStart 14 | ); 15 | } 16 | 17 | return Array.from( 18 | {length: Math.ceil((end - maxOrStart))}, 19 | (_, i) => i + maxOrStart 20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /observable: -------------------------------------------------------------------------------- 1 | function Observer(onNext, onError = () => {}, onComplete = () => {}) { 2 | const def = () => {}; 3 | if (typeof onNext === 'object') { 4 | onError = onNext.error || def; 5 | onComplete = onNext.complete || def; 6 | onNext = onNext.next || def; 7 | } 8 | this.completed = false; 9 | this.withError = false; 10 | this.next = (val) => { 11 | if (!this.completed) onNext(val); 12 | }; 13 | this.error = (err) => { 14 | if (!this.completed) { 15 | this.completed = true; 16 | this.withError = true; 17 | onError(err); 18 | } 19 | }; 20 | this.complete = () => { 21 | if (!this.completed) { 22 | this.completed = true; 23 | onComplete(); 24 | } 25 | }; 26 | } 27 | 28 | function Observable(observer) { 29 | this.subscribe = (...fns) => { 30 | if(observer) { 31 | const obs = new Observer(...fns); 32 | observer(obs); 33 | } 34 | }; 35 | 36 | this.unsubscribe = () => { 37 | observer = null; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /observe element: -------------------------------------------------------------------------------- 1 | function observeElement(element, callback) { 2 | const config = { 3 | attributes: true, 4 | childList: true, 5 | subtree: true, 6 | attributeOldValue: true, 7 | }; 8 | 9 | const listener = function (mutationsList, observer) { 10 | for (const mutation of mutationsList) { 11 | let type = mutation.type === 'childList' ? 'content' : mutation.type; 12 | let oldValue = mutation.oldValue; 13 | let newValue = null; 14 | 15 | if (mutation.type === 'childList') { 16 | newValue = [...mutation.target.childNodes].map((node) => { 17 | return node.nodeName === '#text' ? node.nodeValue : node; 18 | }); 19 | } else if (mutation.type === 'attributes') { 20 | newValue = mutation.target.getAttribute(mutation.attributeName) 21 | } 22 | 23 | callback({type, oldValue, newValue}); 24 | } 25 | }; 26 | 27 | const observer = new MutationObserver(listener); 28 | 29 | observer.observe(element, config); 30 | 31 | return () => observer.disconnect(); 32 | } 33 | -------------------------------------------------------------------------------- /once event: -------------------------------------------------------------------------------- 1 | function once(callback) { 2 | const handler = (e) => { 3 | e.currentTarget.removeEventListener(e.type, handler); 4 | callback(e); 5 | }; 6 | 7 | return handler; 8 | } 9 | -------------------------------------------------------------------------------- /polling function: -------------------------------------------------------------------------------- 1 | async function poll(fn, validate, interval = 2500) { 2 | const resolver = async (resolve, reject) => { 3 | try { // catch any error thrown by the "fn" function 4 | const result = await fn(); // fn does not need to be asynchronous or return promise 5 | // call validator to see if the data is at the state to stop the polling 6 | const valid = validate(result); 7 | 8 | if (valid === true) { 9 | resolve(result); 10 | } else if (valid === false) { 11 | setTimeout(resolver, interval, resolve, reject); 12 | } // if validator returns anything other than "true" or "false" it stops polling 13 | } catch (e) { 14 | reject(e); 15 | } 16 | }; 17 | 18 | return new Promise(resolver); 19 | } 20 | -------------------------------------------------------------------------------- /promisify: -------------------------------------------------------------------------------- 1 | function promisify(subject) { 2 | return (...args) => new Promise(async (resolve, reject) => { 3 | const callbackHandler = (...results) => { 4 | // filter error from results to remove dependency on error, result order 5 | const {error, result} = results.reduce((obj, res) => { 6 | if(res instanceof Error) { 7 | obj['error'] = res; 8 | } else { 9 | obj['result'] = res; 10 | } 11 | 12 | return obj; 13 | }, {}); 14 | 15 | if(error) { 16 | reject(error); 17 | } else { 18 | resolve(result); 19 | } 20 | } 21 | 22 | try { 23 | const result = await subject(...args, callbackHandler); 24 | resolve(result); // only runs if callbackHandler does not resolve the promise 25 | } catch(error) { 26 | reject(error); 27 | } 28 | }); 29 | } 30 | -------------------------------------------------------------------------------- /random number generator: -------------------------------------------------------------------------------- 1 | function randomNumber(max = 1, min = 0) { 2 | if(min >= max) { 3 | return max; 4 | } 5 | 6 | return Math.floor(Math.random() * (max - min) + min); 7 | } 8 | -------------------------------------------------------------------------------- /required arguments: -------------------------------------------------------------------------------- 1 | function required(argName = 'param') { 2 | throw new Error(`"${argName}" is required`) 3 | } 4 | 5 | function iHaveRequiredOptions(arg1 = required('arg1'), arg2 = 10) { 6 | console.log(arg1, arg2) 7 | } 8 | 9 | iHaveRequiredOptions(); // throws "arg1" is required 10 | iHaveRequiredOptions(12); // prints 12, 10 11 | iHaveRequiredOptions(12, 24); // prints 12, 24 12 | iHaveRequiredOptions(undefined, 24); // throws "arg1" is required 13 | -------------------------------------------------------------------------------- /stringifier: -------------------------------------------------------------------------------- 1 | const stringify = (() => { 2 | const replacer = (key, val) => { 3 | if(typeof val === 'symbol') { 4 | return val.toString(); 5 | } 6 | 7 | if(val instanceof Set) { 8 | return Array.from(val); 9 | } 10 | 11 | if(val instanceof Map) { 12 | return Array.from(val.entries()); 13 | } 14 | 15 | if(typeof val === 'function') { 16 | return val.toString(); 17 | } 18 | 19 | return val; 20 | } 21 | 22 | return (obj, spaces = 0) => JSON.stringify(obj, replacer, spaces) 23 | })(); 24 | -------------------------------------------------------------------------------- /type check: -------------------------------------------------------------------------------- 1 | const isOfType = (() => { 2 | // create a plain object with no prototype 3 | const type = Object.create(null); 4 | 5 | // check for null type 6 | type.null = x => x === null; 7 | // check for undefined type 8 | type.undefined = x => x === undefined; 9 | // check for nil type. Either null or undefined 10 | type.nil = x => type.null(x) || type.undefined(x); 11 | // check for strings and string literal type. e.g: 's', "s", `str`, new String() 12 | type.string = x => !type.nil(x) && (typeof x === 'string' || x instanceof String); 13 | // check for number or number literal type. e.g: 12, 30.5, new Number() 14 | type.number = x => !type.nil(x) 15 | && (// NaN & Infinity have typeof "number" and this excludes that 16 | (!isNaN(x) && isFinite(x) 17 | && typeof x === 'number' 18 | ) || x instanceof Number); 19 | // check for boolean or boolean literal type. e.g: true, false, new Boolean() 20 | type.boolean = x => !type.nil(x) && (typeof x === 'boolean' || x instanceof Boolean); 21 | // check for array type 22 | type.array = x => !type.nil(x) && Array.isArray(x); 23 | // check for object or object literal type. e.g: {}, new Object(), Object.create(null) 24 | type.object = x => ({}).toString.call(x) === '[object Object]'; 25 | // check for provided type instance 26 | type.type = (x, X) => !type.nil(x) && x instanceof X; 27 | // check for set type 28 | type.set = x => type.type(x, Set); 29 | // check for map type 30 | type.map = x => type.type(x, Map); 31 | // check for date type 32 | type.date = x => type.type(x, Date); 33 | 34 | return type; 35 | })(); 36 | -------------------------------------------------------------------------------- /unique id generators: -------------------------------------------------------------------------------- 1 | // create unique id starting from current time in milliseconds 2 | // incrementing it by 1 everytime requested 3 | const uniqueId = (mil => () => mil++)(Date.now()); 4 | 5 | // create unique incrementing id starting from provided value or zero 6 | // good for temporary things or things that id resets 7 | const uniqueIncrementingId = (numb => (length = 12) => `${numb++}`.padStart(length, '0'))(0); 8 | 9 | // create unique id from letters and numbers 10 | const uniqueAlphaNumericId = (() => { 11 | const heyStack = '0123456789abcdefghijklmnopqrstuvwxyz'; 12 | const {length} = heyStack; 13 | const randomInt = () => Math.floor(Math.random() * length); 14 | 15 | return (length = 24) => Array.from({length}, () => heyStack[randomInt()]).join(''); 16 | })(); 17 | -------------------------------------------------------------------------------- /until event: -------------------------------------------------------------------------------- 1 | function until(callback, checker) { 2 | const handler = (e) => { 3 | if(checker(e)) { 4 | e.currentTarget.removeEventListener(e.type, handler); 5 | } 6 | 7 | callback(e); 8 | }; 9 | 10 | return handler; 11 | } 12 | -------------------------------------------------------------------------------- /uploader: -------------------------------------------------------------------------------- 1 | function postData( 2 | url, 3 | data = {}, 4 | {type = 'json', fileKey = 'file', headers = [], onProgress} = {} 5 | ) { 6 | const req = new XMLHttpRequest(); 7 | const formData = new FormData(); 8 | 9 | req.open('POST', url, true); 10 | 11 | if(data instanceof File) { 12 | formData.append(fileKey, data, data.name); 13 | data = formData; 14 | } else if(type === 'multipart') { 15 | for(const key in data) { 16 | formData.append(key, data[key]); 17 | } 18 | data = formData; 19 | } else { 20 | data = JSON.stringify(data); 21 | req.setRequestHeader("Content-Type", "application/json;charset=UTF-8"); 22 | } 23 | 24 | for(const key in headers) { 25 | req.setRequestHeader(key, headers[key]); 26 | } 27 | 28 | return new Promise((res, rej) => { 29 | req.onload = (e) => { 30 | if (req.status === 200) { 31 | res(e); 32 | } else { 33 | rej(e); 34 | } 35 | } 36 | 37 | if(typeof onProgress === 'function') { 38 | req.upload.onprogress = onProgress; 39 | } 40 | 41 | req.onerror = rej; 42 | req.onabort = rej; 43 | req.onabort = rej; 44 | req.ontimeout = rej; 45 | 46 | req.send(data); 47 | }); 48 | } 49 | --------------------------------------------------------------------------------