From 669b4bcd23dcb1adb358d86a5970e9e3b5e29845 Mon Sep 17 00:00:00 2001 From: "Alex A. Naanou" Date: Sun, 4 Oct 2020 04:40:16 +0300 Subject: [PATCH] migrated most types... Signed-off-by: Alex A. Naanou --- Array.js | 298 ++++++++++++++++++++++++++++++++++++++++ Date.js | 101 ++++++++++++++ Object.js | 44 ++++++ RegExp.js | 25 ++++ Set.js | 31 +++++ String.js | 23 ++++ extend.js => _module.js | 0 containers.js | 106 ++++++++++++++ package-lock.json | 18 +++ package.json | 27 ++++ types.js | 11 ++ 11 files changed, 684 insertions(+) create mode 100644 Array.js create mode 100644 Date.js create mode 100644 Object.js create mode 100644 RegExp.js create mode 100644 Set.js create mode 100644 String.js rename extend.js => _module.js (100%) create mode 100644 containers.js create mode 100644 package-lock.json create mode 100644 package.json diff --git a/Array.js b/Array.js new file mode 100644 index 0000000..94f560b --- /dev/null +++ b/Array.js @@ -0,0 +1,298 @@ +/********************************************************************** +* +* +* +**********************************************************************/ +((typeof define)[0]=='u'?function(f){module.exports=f(require)}:define) +(function(require){ var module={} // make module AMD/node compatible... +/*********************************************************************/ + + + + +/*********************************************************************/ + +// Array.prototype.flat polyfill... +// +// NOTE: .flat(..) is not yet supported in IE/Edge... +Array.prototype.flat + || (Array.prototype.flat = function(depth){ + depth = typeof(depth) == typeof(123) ? depth : 1 + return this.reduce(function(res, e){ + return res.concat(e instanceof Array && depth > 0 ? + e.flat(depth-1) + : [e]) }, []) }) + + +// Array.prototype.includes polyfill... +// +Array.prototype.includes + || (Array.prototype.includes = function(value){ + return this.indexOf(value) >= 0 }) + + +// first/last element access short-hands... +// +// .first() +// .last() +// -> elem +// +// .first(value) +// .last(value) +// -> array +// +// NOTE: setting a value will overwrite an existing first/last value. +// NOTE: for an empty array both .first(..)/.last(..) will return undefined +// when getting a value and set the 0'th value when setting... +Array.prototype.first + || (Array.prototype.first = function(value){ + return arguments.length > 0 ? + ((this[0] = value), this) + : this[0]}) +Array.prototype.last + || (Array.prototype.last = function(value){ + return arguments.length > 0 ? + ((this[this.length - 1 || 0] = value), this) + : this[this.length - 1]}) + + +/*/ XXX not yet sure should these be funcs or props... +'first' in Array.prototype + || Object.defineProperty(Array.prototype, 'first', { + enumerable: false, + get : function () { + return this[0] }, + set : function(value){ + this[0] = value + return this }, }) + +'last' in Array.prototype + || Object.defineProperty(Array.prototype, 'last', { + enumerable: false, + get : function () { + return this[this.length - 1] }, + set : function(value){ + this[this.length - 1 || 0] = value + return this }, }) +//*/ + + +// Compact a sparse array... +// +// NOTE: this will not compact in-place. +Array.prototype.compact = function(){ + return this.filter(function(){ return true }) } + + +// like .length but for sparse arrays will return the element count... +'len' in Array.prototype + || Object.defineProperty(Array.prototype, 'len', { + get : function () { + return Object.keys(this).length + }, + set : function(val){}, + }) + + +// Convert an array to object... +// +// Format: +// { +// : , +// ... +// } +// +// NOTE: items should be strings, other types will get converted to +// strings and thus may mess things up. +// NOTE: this will forget repeating items... +// NOTE: normalize will slow things down... +Array.prototype.toKeys = function(normalize){ + return normalize ? + this.reduce(function(r, e, i){ + r[normalize(e)] = i + return r }, {}) + : this.reduce(function(r, e, i){ + r[e] = i + return r }, {}) } + + +// Convert an array to a map... +// +// This is similar to Array.prototype.toKeys(..) but does not restrict +// value type to string. +// +// Format: +// Map([ +// [, ], +// ... +// ]) +// +// NOTE: this will forget repeating items... +// NOTE: normalize will slow things down... +Array.prototype.toMap = function(normalize){ + return normalize ? + this + .reduce(function(m, e, i){ + m.set(normalize(e), i) + return m }, new Map()) + : this + .reduce(function(m, e, i){ + m.set(e, i) + return m }, new Map()) } + + +// Return an array with duplicate elements removed... +// +// NOTE: order is preserved... +Array.prototype.unique = function(normalize){ + return normalize ? + [...new Map(this.map(function(e){ return [normalize(e), e] })).values()] + : [...new Set(this)] } +Array.prototype.tailUnique = function(normalize){ + return this + .slice() + .reverse() + .unique(normalize) + .reverse() } + +// Compare two arrays... +// +Array.prototype.cmp = function(other){ + if(this === other){ + return true } + if(this.length != other.length){ + return false } + for(var i=0; i promise(list) +// +// .filterChunks(func) +// .filterChunks(chunk_size, func) +// .filterChunks([item_handler, chunk_handler]) +// .filterChunks(chunk_size, [item_handler, chunk_handler]) +// -> promise(list) +// +// .reduceChunks(func, res) +// .reduceChunks(chunk_size, func, res) +// .reduceChunks([item_handler, chunk_handler], res) +// .reduceChunks(chunk_size, [item_handler, chunk_handler], res) +// -> promise(res) +// +// +// chunk_handler(chunk, result, offset) +// +// +// chunk_size can be: +// 20 - chunk size +// '20' - chunk size +// '20C' - number of chunks +// +// +// The main goal of this is to not block the runtime while processing a +// very long array by interrupting the processing with a timeout... +// +var makeChunkIter = function(iter, wrapper){ + wrapper = wrapper + || function(res, func, array, e){ + return func.call(this, e[1], e[0], array) } + return function(size, func, ...rest){ + var that = this + var args = [...arguments] + size = (args[0] instanceof Function + || args[0] instanceof Array) ? + (this.CHUNK_SIZE || 50) + : args.shift() + size = typeof(size) == typeof('str') ? + // number of chunks... + (size.trim().endsWith('c') || size.trim().endsWith('C') ? + Math.round(this.length / (parseInt(size) || 1)) || 1 + : parseInt(size)) + : size + var postChunk + func = args.shift() + ;[func, postChunk] = func instanceof Array ? func : [func] + rest = args + var res = [] + var _wrapper = wrapper.bind(this, res, func, this) + + return new Promise(function(resolve, reject){ + var next = function(chunks){ + setTimeout(function(){ + var chunk, val + res.push( + val = (chunk = chunks.shift())[iter](_wrapper, ...rest)) + postChunk + && postChunk.call(that, + chunk.map(function([i, v]){ return v }), + val, + chunk[0][0]) + // stop condition... + chunks.length == 0 ? + resolve(res.flat(2)) + : next(chunks) }, 0) } + next(that + // split the array into chunks... + .reduce(function(res, e, i){ + var c = res.slice(-1)[0] + c.length >= size ? + // initial element in chunk... + res.push([[i, e]]) + // rest... + : c.push([i, e]) + return res }, [[]])) }) } } + +Array.prototype.CHUNK_SIZE = 50 +Array.prototype.mapChunks = makeChunkIter('map') +Array.prototype.filterChunks = makeChunkIter('map', + function(res, func, array, e){ + return !!func.call(this, e[1], e[0], array) ? [e[1]] : [] }) +Array.prototype.reduceChunks = makeChunkIter('reduce', + function(total, func, array, res, e){ + return func.call(this, + total.length > 0 ? + total.pop() + : res, + e[1], e[0], array) }) + + + + +/********************************************************************** +* vim:set ts=4 sw=4 : */ return module }) diff --git a/Date.js b/Date.js new file mode 100644 index 0000000..871851b --- /dev/null +++ b/Date.js @@ -0,0 +1,101 @@ +/********************************************************************** +* +* +* +**********************************************************************/ +((typeof define)[0]=='u'?function(f){module.exports=f(require)}:define) +(function(require){ var module={} // make module AMD/node compatible... +/*********************************************************************/ + + + + +/*********************************************************************/ + +// NOTE: repatching a date should not lead to any side effects as this +// does not add any state... +// NOTE: this is done differently as there are contexts where there may +// be multiple Date objects in different contexts (nw/electron/..) +var patchDate = +module.patchDate = function(date){ + date = date || Date + + date.prototype.toShortDate = function(show_ms){ + return '' + + this.getFullYear() + +'-'+ ('0'+(this.getMonth()+1)).slice(-2) + +'-'+ ('0'+this.getDate()).slice(-2) + +' '+ ('0'+this.getHours()).slice(-2) + +':'+ ('0'+this.getMinutes()).slice(-2) + +':'+ ('0'+this.getSeconds()).slice(-2) + + (show_ms ? + ':'+(('000'+this.getMilliseconds()).slice(-3)) + : '') } + + date.prototype.getTimeStamp = function(show_ms){ + return '' + + this.getFullYear() + + ('0'+(this.getMonth()+1)).slice(-2) + + ('0'+this.getDate()).slice(-2) + + ('0'+this.getHours()).slice(-2) + + ('0'+this.getMinutes()).slice(-2) + + ('0'+this.getSeconds()).slice(-2) + + (show_ms ? + ('000'+this.getMilliseconds()).slice(-3) + : '') } + + date.prototype.setTimeStamp = function(ts){ + ts = ts.replace(/[^0-9]*/g, '') + this.setFullYear(ts.slice(0, 4)) + this.setMonth(ts.slice(4, 6)*1-1) + this.setDate(ts.slice(6, 8)) + this.setHours(ts.slice(8, 10)) + this.setMinutes(ts.slice(10, 12)) + this.setSeconds(ts.slice(12, 14)) + this.setMilliseconds(ts.slice(14, 17) || 0) + return this } + + date.timeStamp = function(...args){ + return (new this()).getTimeStamp(...args) } + + date.fromTimeStamp = function(ts){ + return (new this()).setTimeStamp(ts) } + + // convert string time period to milliseconds... + date.str2ms = function(str, dfl){ + dfl = dfl || 'ms' + + if(typeof(str) == typeof(123)){ + var val = str + str = dfl + + } else { + var val = parseFloat(str) + str = str.trim() + // check if a unit is given... + str = str == val ? dfl : str } + + var c = /(m(illi)?(-)?s(ec(ond(s)?)?)?)$/i.test(str) ? + 1 + : /s(ec(ond(s)?)?)?$/i.test(str) ? + 1000 + : /m(in(ute(s)?)?)?$/i.test(str) ? + 1000*60 + : /h(our(s)?)?$/i.test(str) ? + 1000*60*60 + : /d(ay(s)?)?$/i.test(str) ? + 1000*60*60*24 + : null + + return c ? + val * c + : NaN } + + return date } +// patch the root date... +patchDate() + + + +/********************************************************************** +* vim:set ts=4 sw=4 : */ return module }) diff --git a/Object.js b/Object.js new file mode 100644 index 0000000..fb2bf38 --- /dev/null +++ b/Object.js @@ -0,0 +1,44 @@ +/********************************************************************** +* +* +* +**********************************************************************/ +((typeof define)[0]=='u'?function(f){module.exports=f(require)}:define) +(function(require){ var module={} // make module AMD/node compatible... +/*********************************************************************/ + +require('object-run') + + + +/*********************************************************************/ + +// Get all the accessible keys... +// +// This is different to Object.keys(..) in that this will return keys +// from all the prototypes in the inheritance chain while .keys(..) will +// only return the keys defined in the current object only. +Object.deepKeys = function(obj){ + var res = [] + while(obj != null){ + res = res.concat(Object.keys(obj)) + obj = obj.__proto__ + } + return res.unique() } + + +// Make a full key set copy of an object... +// +// NOTE: this will not deep-copy the values... +Object.flatCopy = function(obj){ + var res = {} + Object.deepKeys(obj).forEach(function(key){ + res[key] = obj[key] + }) + return res } + + + + +/********************************************************************** +* vim:set ts=4 sw=4 : */ return module }) diff --git a/RegExp.js b/RegExp.js new file mode 100644 index 0000000..4973dbc --- /dev/null +++ b/RegExp.js @@ -0,0 +1,25 @@ +/********************************************************************** +* +* +* +**********************************************************************/ +((typeof define)[0]=='u'?function(f){module.exports=f(require)}:define) +(function(require){ var module={} // make module AMD/node compatible... +/*********************************************************************/ + + + + +/*********************************************************************/ + +// Quote a string and convert to RegExp to match self literally. +var quoteRegExp = +RegExp.quoteRegExp = +module.quoteRegExp = +function(str){ + return str.replace(/([\.\\\/\(\)\[\]\$\*\+\-\{\}\@\^\&\?\<\>])/g, '\\$1') } + + + +/********************************************************************** +* vim:set ts=4 sw=4 : */ return module }) diff --git a/Set.js b/Set.js new file mode 100644 index 0000000..6be283d --- /dev/null +++ b/Set.js @@ -0,0 +1,31 @@ +/********************************************************************** +* +* +* +**********************************************************************/ +((typeof define)[0]=='u'?function(f){module.exports=f(require)}:define) +(function(require){ var module={} // make module AMD/node compatible... +/*********************************************************************/ + + + + +/*********************************************************************/ + +// Set set operation shorthands... +Set.prototype.unite = function(other){ + return new Set([...this, ...other]) } +Set.prototype.intersect = function(other){ + var test = other.has ? 'has' : 'includes' + return new Set([...this] + .filter(function(e){ return other[test](e) })) } +Set.prototype.subtract = function(other){ + other = new Set(other) + return new Set([...this] + .filter(function(e){ return !other.has(e) })) } + + + + +/********************************************************************** +* vim:set ts=4 sw=4 : */ return module }) diff --git a/String.js b/String.js new file mode 100644 index 0000000..b739a67 --- /dev/null +++ b/String.js @@ -0,0 +1,23 @@ +/********************************************************************** +* +* +* +**********************************************************************/ +((typeof define)[0]=='u'?function(f){module.exports=f(require)}:define) +(function(require){ var module={} // make module AMD/node compatible... +/*********************************************************************/ + + + + +/*********************************************************************/ + +String.prototype.capitalize = function(){ + return this == '' ? + this + : this[0].toUpperCase() + this.slice(1) } + + + +/********************************************************************** +* vim:set ts=4 sw=4 : */ return module }) diff --git a/extend.js b/_module.js similarity index 100% rename from extend.js rename to _module.js diff --git a/containers.js b/containers.js new file mode 100644 index 0000000..1e0ac10 --- /dev/null +++ b/containers.js @@ -0,0 +1,106 @@ +/********************************************************************** +* +* +* +**********************************************************************/ +((typeof define)[0]=='u'?function(f){module.exports=f(require)}:define) +(function(require){ var module={} // make module AMD/node compatible... +/*********************************************************************/ + +var object = require('ig-object') + + + +/*********************************************************************/ + +// XXX should we have the restriction of requiring unique elements??? +// XXX move this to browse2 and use it as an option/basis for list... +// XXX BUG: UniqueKeyMap([['a', 123], ...]) breaks... +var UniqueKeyMap = +module.UniqueKeyMap = object.Constructor('UniqueKeyMap', Map, { + + // Format: + // Map([ + // [ , Set([ + // , + // ... + // ]) ], + // ... + // ]) + // + // XXX might be a good idea to change the value to a list of original + // names... + __keys: null, + + // Patter to be used to generate unique key... + __key_pattern__: '$KEY ($COUNT)', + + // If true then a value can not be stored under the same key more + // than once... + // + // Example: + // var u = UniqueKeyMap() + // u.set('x', 123) + // // if .__unique_key_value__ is true this will have no effect, + // // otherwise 123 will be stored under 'x (1)' + // u.set('x', 123) + // + __unique_key_value__: false, + + + // Extended API... + // + set: function(key, elem){ + var names + this.__keys.set(elem, + names = this.__keys.get(elem) || new Set()) + // key/elem already exists... + if(this.__unique_key_value__ && names.has(key)){ + return this } + names.add(key) + // make name unique... + var n = key + var i = 0 + while(this.has(n)){ + i++ + n = this.__key_pattern__ + .replace(/\$KEY/, key) + .replace(/\$COUNT/, i) } + // add the elem with the unique name... + return object.parentCall(UniqueKeyMap.prototype, 'set', this, n, elem) }, + delete: function(key){ + var s = this.__keys.get(this.get(key)) + if(s){ + s.delete(key) + s.size == 0 + & this.__keys.delete(this.get(key)) } + return object.parentCall(UniqueKeyMap.prototype, 'delete', this, key) }, + + + // New API... + // + rename: function(from, to){ + var e = this.get(from) + this.delete(from) + return this.set(to, e) }, + keysOf: function(elem, mode='original'){ + // get unique keys... + if(mode == 'unique'){ + return this + .entries() + .reduce(function(res, [k, e]){ + e === elem + && res.push(k) + return res }, []) } + // get keys used to set the values... + return [...(this.__keys.get(elem) || [])] }, + + __init__: function(){ + this.__keys = new Map() }, +}) + + + + +/********************************************************************** +* vim:set ts=4 sw=4 : */ return module }) diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..6fd0abe --- /dev/null +++ b/package-lock.json @@ -0,0 +1,18 @@ +{ + "name": "ig-types", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "ig-object": { + "version": "5.2.8", + "resolved": "https://registry.npmjs.org/ig-object/-/ig-object-5.2.8.tgz", + "integrity": "sha512-EzT4CP6d6lI8bnknNgT3W8mUQhSVXflO0yPbKD4dKsFcINiC6npjoEBz+8m3VQmWJhc+36pXD4JLwNxUEgzi+Q==" + }, + "object-run": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-run/-/object-run-1.0.1.tgz", + "integrity": "sha512-FkYGSw3wr5DLfEueCJvbkCh3jZE1DTVCEQNshQwH8zpUFn0ahyM1cQMNFfzTMC3DSTBHVH7+7+DAf1WDWn+EXA==" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..53bef8f --- /dev/null +++ b/package.json @@ -0,0 +1,27 @@ +{ + "name": "ig-types", + "version": "1.0.0", + "description": "Generic JavaScript types and type extensions...", + "main": "types.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/flynx/types.js.git" + }, + "keywords": [ + "JavaScript", + "types" + ], + "author": "Alex A. Naanou ", + "license": "BSD-3-Clause", + "bugs": { + "url": "https://github.com/flynx/types.js/issues" + }, + "homepage": "https://github.com/flynx/types.js#readme", + "dependencies": { + "ig-object": "^5.2.8", + "object-run": "^1.0.1" + } +} diff --git a/types.js b/types.js index a8ef802..d251fa4 100644 --- a/types.js +++ b/types.js @@ -7,6 +7,17 @@ (function(require){ var module={} // make module AMD/node compatible... /*********************************************************************/ +// Extend built-in types... +require('./Object') +require('./Array') +require('./Set') +require('./String') +require('./RegExp') +module.patchDate = require('./Date').patchDate + + +// Additional types... +module.containers = require('./containers')