Comparing version 1.1.0 to 1.2.0
{ | ||
"name": "jsupack", | ||
"version": "1.1.0", | ||
"version": "1.2.0", | ||
"license": "MIT", | ||
@@ -12,7 +12,3 @@ "description": "JavaScript Utilities", | ||
"scripts": { | ||
"test": "echo Running tests... && npm run test:_index -s && npm run test:common -s && npm run test:csv_parser -s && npm run test:event -s && echo Finished!", | ||
"test:_index": "mocha ./tests/_index.js", | ||
"test:common": "mocha ./tests/jsu_common.js", | ||
"test:csv_parser": "mocha ./tests/jsu_csv_parser.js", | ||
"test:event": "mocha ./tests/jsu_event.js" | ||
"test": "echo Running tests... && mocha ./tests/_index.js ./tests/jsu*.js && echo Finished!" | ||
}, | ||
@@ -40,4 +36,5 @@ "repository": { | ||
"jsdom": "^20.0.0", | ||
"mocha": "^10.0.0" | ||
"mocha": "^10.0.0", | ||
"sinon": "^14.0.0" | ||
} | ||
} |
@@ -9,9 +9,13 @@ # jsu | ||
The features provided by jsu were originally part of the [nvc](https://github.com/arlogy/nvc) | ||
project. Most of these features, such as type checking or timer events can be | ||
project. Some of these features, such as type checking or timer events can be | ||
found in other utility libraries. In case you find these libraries too large as | ||
a dependency, or if they require too new JavaScript technologies, among other | ||
disadvantages, you can use jsu as a possible fallback. For example, | ||
[jsu_event.js](https://github.com/arlogy/jsu/blob/main/src/jsu_event.js) | ||
disadvantages, you can use jsu as a possible fallback. For example, [jsu_event.js](src/jsu_event.js) | ||
supports generic and timer events in just a few lines of code. | ||
Also note that type checking is need-specific, i.e. not all use cases will | ||
accept a boolean object as a boolean, or a numeric string or an infinite number | ||
as a number. So even jsu implementation might not be of any help in checking | ||
data types. | ||
Overall, jsu is not a substitute for libraries such as jQuery or others with | ||
@@ -23,4 +27,4 @@ convenient functions like [isEqual()](https://underscorejs.org/#isEqual) | ||
Documentation and examples are available [here](https://github.com/arlogy/jsu/tree/main/doc). | ||
Version changelog is [here](https://github.com/arlogy/jsu/blob/main/CHANGELOG.md). | ||
Documentation and examples are available [here](doc). | ||
Version changelog is [here](CHANGELOG.md). | ||
@@ -33,7 +37,8 @@ ## Tests | ||
npm install | ||
npm run test # run tests | ||
npm run test # run all tests | ||
``` | ||
We use the following Node.js packages which were all installed using `npm install <package> --save-dev`. | ||
- Mocha for unit testing. | ||
- mocha for unit testing. | ||
- sinon for spies, stubs and mocks. | ||
- jsdom to imitate in a Node.js environment the behavior of a browser. |
@@ -20,5 +20,7 @@ /* | ||
function() { | ||
var API = {}; | ||
// --- Local Storage --- | ||
function setLocalStorageItem(key, value) { | ||
API.setLocalStorageItem = function(key, value) { | ||
try { // see (1) below | ||
@@ -31,5 +33,5 @@ if(window.localStorage) { | ||
return false; | ||
} | ||
}; | ||
function getLocalStorageItem(key) { | ||
API.getLocalStorageItem = function(key) { | ||
try { // see (1) below | ||
@@ -40,3 +42,3 @@ return window.localStorage ? window.localStorage.getItem(key) | ||
return null; // returning null here too | ||
} | ||
}; | ||
@@ -49,21 +51,21 @@ // (1) An exception might be raised when trying to access window.localStorage, | ||
function setEltVisible(elt, vis, dsp) { | ||
API.setEltVisible = function(elt, vis, dsp) { | ||
elt.style.display = vis ? (dsp && dsp !== 'none' ? dsp : 'revert') | ||
: 'none'; | ||
} | ||
}; | ||
function isEltVisible(elt) { | ||
API.isEltVisible = function(elt) { | ||
return window.getComputedStyle(elt, null).display !== 'none'; | ||
// getComputedStyle() must be used so that styles in external stylesheets are inspected | ||
} | ||
}; | ||
function switchEltVisibility(elt, dsp) { | ||
setEltVisible(elt, !isEltVisible(elt), dsp); | ||
} | ||
API.switchEltVisibility = function(elt, dsp) { | ||
API.setEltVisible(elt, !API.isEltVisible(elt), dsp); | ||
}; | ||
// --- Type Checker --- | ||
function isBoolean(value) { return typeof value === 'boolean'; } | ||
API.isBoolean = function(value) { return typeof value === 'boolean'; }; | ||
var isNumber = Number.isFinite || function(value) { // polyfill for Number.isFinite() | ||
API.isNumber = Number.isFinite || function(value) { // polyfill for Number.isFinite() | ||
return typeof value === 'number' && isFinite(value); | ||
@@ -76,25 +78,25 @@ // Number.isFinite() is used instead of !Number.isNaN() for example | ||
function isNumberAlike(value) { | ||
API.isNumberAlike = function(value) { | ||
var tov = typeof value; | ||
return (tov === 'number' || tov === 'string') && isFinite(value); | ||
} | ||
}; | ||
function isString(value) { return typeof value === 'string' || value instanceof String; } | ||
API.isString = function(value) { return typeof value === 'string' || value instanceof String; }; | ||
var isArray = Array.isArray || function(arg) { // polyfill for Array.isArray() | ||
API.isArray = Array.isArray || function(arg) { // polyfill for Array.isArray() | ||
return Object.prototype.toString.call(arg) === '[object Array]'; | ||
}; | ||
function isCssColor(value) { | ||
API.isCssColor = function(value) { | ||
return typeof CSS !== 'undefined' && CSS.supports ? CSS.supports('color', value) : null; | ||
} | ||
}; | ||
function isCssColorOrString(value) { | ||
var isColor = isCssColor(value); | ||
return isColor !== null ? isColor : isString(value); | ||
} | ||
API.isCssColorOrString = function(value) { | ||
var isColor = API.isCssColor(value); | ||
return isColor !== null ? isColor : API.isString(value); | ||
}; | ||
// --- Property Accessor/Modifier --- | ||
function copyPropsNoCheck(propNames, fromObj, toObj) { | ||
API.copyPropsNoCheck = function(propNames, fromObj, toObj) { | ||
for(var i = 0; i < propNames.length; i++) { | ||
@@ -104,5 +106,5 @@ var pn = propNames[i]; | ||
} | ||
} | ||
}; | ||
function copyPropsAndCheck(propNames, fromObj, toObj, checker) { | ||
API.copyPropsAndCheck = function(propNames, fromObj, toObj, checker) { | ||
for(var i = 0; i < propNames.length; i++) { | ||
@@ -113,7 +115,7 @@ var pn = propNames[i]; | ||
} | ||
} | ||
}; | ||
// --- Formatter --- | ||
function formatString(str, fmt) { | ||
API.formatString = function(str, fmt) { | ||
return str.replace(/{(\w+)}/g, function(match, c) { // c is the value captured in the match | ||
@@ -124,6 +126,6 @@ return c in fmt ? fmt[c] : match; | ||
function setStringPrototypeFormat() { | ||
API.setStringPrototypeFormat = function() { | ||
if(String.prototype.format === undefined) { | ||
String.prototype.format = function() { | ||
return formatString(this, arguments); | ||
return API.formatString(this, arguments); | ||
}; | ||
@@ -141,13 +143,13 @@ String.prototype.format.jsu = true; | ||
return formatSetByJsu; | ||
} | ||
}; | ||
// --- Parser --- | ||
function parseInlineCssStyle(styleStr) { | ||
API.parseInlineCssStyle = function(styleStr) { | ||
var elt = document.createElement('span'); | ||
elt.style = styleStr; | ||
return elt.style; // a CSSStyleDeclaration object | ||
} | ||
}; | ||
function parseSuffixedValue(value) { | ||
API.parseSuffixedValue = function(value) { | ||
var retVal = null; | ||
@@ -162,6 +164,6 @@ var match = (value + '').match(/^\s*(-?[0-9]+\.?[0-9]*)\s*([^\s]*(?:\s+[^\s]+)*)\s*$/); // ?: allows to not create a capturing group | ||
return retVal; | ||
} | ||
}; | ||
function parseSpaceAsPerJsonStringify(space) { | ||
if(isNumber(space) && space >= 0) { | ||
API.parseSpaceAsPerJsonStringify = function(space) { | ||
if(API.isNumber(space) && space >= 0) { | ||
space = Math.min(Math.floor(space), 10); | ||
@@ -173,7 +175,7 @@ var spaceStr = ''; | ||
} | ||
if(isString(space)) return space.substring(0, 10); | ||
if(API.isString(space)) return space.substring(0, 10); | ||
return ''; | ||
} | ||
}; | ||
function matchAllAndIndex(str, pattern, ignoreCase) { | ||
API.matchAllAndIndex = function(str, pattern, ignoreCase) { | ||
// avoid implicit infinite matches (thus infinite loop) as explained in | ||
@@ -194,7 +196,7 @@ // the notes accompanying the documentation of this function concerning | ||
return Object.keys(retVal).length === 0 ? null : retVal; | ||
} | ||
}; | ||
function isolateMatchingData(str, pattern, ignoreCase) { | ||
API.isolateMatchingData = function(str, pattern, ignoreCase) { | ||
var retVal = []; | ||
var matchesByIndex = matchAllAndIndex(str, pattern, ignoreCase); | ||
var matchesByIndex = API.matchAllAndIndex(str, pattern, ignoreCase); | ||
if(!matchesByIndex) matchesByIndex = {}; | ||
@@ -212,41 +214,87 @@ for(var i = 0; i < str.length;) { | ||
return retVal; | ||
} | ||
}; | ||
function isolateMatchingValues(str, pattern, ignoreCase) { | ||
return isolateMatchingData(str, pattern, ignoreCase).map(function(data) { | ||
API.isolateMatchingValues = function(str, pattern, ignoreCase) { | ||
return API.isolateMatchingData(str, pattern, ignoreCase).map(function(data) { | ||
return data.value; | ||
}); | ||
} | ||
}; | ||
return { | ||
'setLocalStorageItem': setLocalStorageItem, | ||
'getLocalStorageItem': getLocalStorageItem, | ||
// --- Others --- | ||
'setEltVisible': setEltVisible, | ||
'isEltVisible': isEltVisible, | ||
'switchEltVisibility': switchEltVisibility, | ||
function _cloneDeep(value, isArrayFunc, cache) { | ||
switch(typeof value) { | ||
case 'undefined': | ||
case 'boolean': | ||
case 'number': | ||
case 'bigint': | ||
case 'string': | ||
case 'function': | ||
return value; | ||
'isBoolean': isBoolean, | ||
'isNumber': isNumber, | ||
'isNumberAlike': isNumberAlike, | ||
'isString': isString, | ||
'isArray': isArray, | ||
'isCssColor': isCssColor, | ||
'isCssColorOrString': isCssColorOrString, | ||
case 'symbol': | ||
return cache.get(value) || cache.add(value, Symbol(value.description)); | ||
'copyPropsNoCheck': copyPropsNoCheck, | ||
'copyPropsAndCheck': copyPropsAndCheck, | ||
case 'object': { | ||
if(value === null) return value; | ||
'formatString': formatString, | ||
'setStringPrototypeFormat': setStringPrototypeFormat, | ||
var copy = cache.get(value); | ||
if(copy) return copy; | ||
if(value instanceof Boolean) return cache.add(value, new Boolean(value.valueOf())); | ||
if(value instanceof Date) return cache.add(value, new Date(value.valueOf())); | ||
if(value instanceof Number) return cache.add(value, new Number(value.valueOf())); | ||
if(value instanceof String) return cache.add(value, new String(value.valueOf())); | ||
'parseInlineCssStyle': parseInlineCssStyle, | ||
'parseSuffixedValue': parseSuffixedValue, | ||
'parseSpaceAsPerJsonStringify': parseSpaceAsPerJsonStringify, | ||
var i = undefined; | ||
if(isArrayFunc(value)) { | ||
copy = []; | ||
cache.add(value, copy); // cache value before the recursive _cloneDeep() call below | ||
var valueLen = value.length; | ||
for(i = 0; i < valueLen; i++) { | ||
copy.push(_cloneDeep(value[i], isArrayFunc, cache)); | ||
} | ||
} | ||
else { | ||
// clone object partially based on Object.keys() | ||
// i.e. inherited and non-enumerable properties are ignored | ||
// but this is enough for object literals ({...}) only referencing value types handled in this function | ||
// e.g. {x:3, y:{}, z:[null, {}, Symbol()]} | ||
// all the JavaScript built-in objects that one might want to support can be found at | ||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects | ||
copy = {}; | ||
cache.add(value, copy); // cache value before the recursive _cloneDeep() call below | ||
var valueKeys = Object.keys(value); | ||
var valueKeysLen = valueKeys.length; | ||
for(i = 0; i < valueKeysLen; i++) { | ||
var prop = valueKeys[i]; | ||
copy[prop] = _cloneDeep(value[prop], isArrayFunc, cache); | ||
} | ||
} | ||
return copy; | ||
} | ||
'matchAllAndIndex': matchAllAndIndex, | ||
'isolateMatchingData': isolateMatchingData, | ||
'isolateMatchingValues': isolateMatchingValues, | ||
default: // will not be reached but still retained | ||
return value; | ||
} | ||
} | ||
API.cloneDeep = function(value) { | ||
return _cloneDeep(value, API.isArray, { | ||
_keys: [], | ||
_vals: [], | ||
get: function(key) { | ||
var idx = this._keys.indexOf(key); | ||
return idx !== -1 ? this._vals[idx] : undefined; | ||
}, | ||
add: function(key, val) { // must be called only if get(key) did not find an entry | ||
// this is to prevent the key from being added more than once | ||
this._keys.push(key); | ||
this._vals.push(val); | ||
return val; | ||
}, | ||
}); | ||
}; | ||
return API; | ||
} | ||
); |
@@ -20,3 +20,2 @@ /* | ||
function() { | ||
// for internal use only | ||
function isNumber(value) { | ||
@@ -26,3 +25,11 @@ return typeof value === 'number' && isFinite(value); | ||
function JsuEventTarget() { | ||
function createCustomEvent(typeStr, detailObj) { | ||
return new CustomEvent(typeStr, { | ||
'detail': detailObj, | ||
}); | ||
} | ||
var API = {}; | ||
API.EventTarget = function() { | ||
var target = null; | ||
@@ -40,6 +47,6 @@ try { | ||
}, this); | ||
} | ||
}; | ||
function createTimer(config) { | ||
var timer = new JsuEventTarget(); | ||
API.createTimer = function(config) { | ||
var timer = new API.EventTarget(); | ||
@@ -63,9 +70,11 @@ var _running = false; | ||
function createCustomEvent(typeStr, detailObj) { | ||
return new CustomEvent(typeStr, { | ||
'detail': detailObj, | ||
}); | ||
function timeout() { | ||
timer.dispatchEvent(createCustomEvent('timeout', { | ||
'count': ++_timeoutCount, | ||
'source': timer, | ||
})); | ||
if(_timeoutLimit !== -1 && _timeoutCount === _timeoutLimit) timer.stop(); | ||
} | ||
function start(delay) { | ||
timer.start = function(delay) { | ||
if(_running) return; | ||
@@ -76,13 +85,5 @@ _running = true; | ||
_id = setInterval(timeout, _delay); | ||
} | ||
}; | ||
function timeout() { | ||
timer.dispatchEvent(createCustomEvent('timeout', { | ||
'count': ++_timeoutCount, | ||
'source': timer, | ||
})); | ||
if(_timeoutLimit !== -1 && _timeoutCount === _timeoutLimit) stop(); | ||
} | ||
function stop() { | ||
timer.stop = function() { | ||
if(!_running) return; | ||
@@ -96,6 +97,4 @@ _running = false; | ||
})); | ||
} | ||
}; | ||
timer.start = start; | ||
timer.stop = stop; | ||
timer.isRunning = function() { return _running; }; | ||
@@ -108,9 +107,6 @@ timer.getDelay = function() { return _delay; }; | ||
return timer; | ||
} | ||
}; | ||
return { | ||
'EventTarget': JsuEventTarget, | ||
'createTimer': createTimer, | ||
}; | ||
return API; | ||
} | ||
); |
32529
637
42
3