This code is similar to Underscore. I've added in some functions to fill in different use cases.
For example, one can use someKey
to iterate through localStorage
and sessionStorage.
Underscore does not have a good way to loop through localStorage
/ sessionStorage
as it is incorrectly detected as an array like object, i.e. it has a length property but does not have indices.
/***************************************************************************************************
UTILITY
This is a small and efficient utility library. There is additional coverage,
consistent ordering, consistent naming conventions, increased input validation, increased structure,
and fewer function branches compared to underscore.
*/
/*jslint
browser: true,
forin: true,
plusplus: true,
eqeq: true,
ass: true
*/
(function (global, undef) {
"use strict";
// holds (Pub)lic properties for the package
var Pub = {},
// holds (Priv)ate properties for the package
Priv = {},
// native prototype methods
nativeSlice = Array.prototype.slice,
nativeSome = Array.prototype.some,
nativeToString = Object.prototype.toString;
// handles global variable management
Pub.noWar = (function () {
// Priv.g holds the single user-defined global variable
Priv.g = '$A';
Priv.previous = global[Priv.g];
Pub.pack = {
utility: true
};
return function () {
var temp = global[Priv.g];
global[Priv.g] = Priv.previous;
return temp;
};
}());
// returns type in a capitalized string form
// typeof is only accurate for function, string, number, boolean, and
// undefined. null and array are both reported as objects
// also typeof does not detect "boxed" values such as `new Number(1)`
Pub.getType = function (obj) {
return nativeToString.call(obj).slice(8, -1);
};
Pub.isType = function (type, obj) {
return Pub.getType(obj) === type;
};
Pub.isGone = function (obj) {
return obj == null;
};
// detects null, undefined, NaN, '', "", 0, -0, false
Pub.isFalsy = function (obj) {
return !obj;
};
Pub.hasLength = function (obj) {
if (obj == null) {
return false;
}
return obj.length === +obj.length;
};
// *underscore calls this, isObject
Pub.isObjectWritable = function (obj) {
return Object(obj) === obj;
};
// *breaks naming convention for compatibility with underscore
Pub.isObjectLiteral = function (obj) {
return nativeToString.call(obj) === '[object Object]';
};
//compare to underscore
// - on a func truthy match returns true and on no match returns false
// - on func/obj validation fail returns false
// - does not insert identity function on func validation fail
Pub.someKey = function (obj, func, con) {
var key;
if (typeof func !== 'function') {
return false;
}
for (key in obj) {
if (obj.hasOwnProperty(key)) {
if (func.call(con, obj[key], key, obj)) {
return true;
}
}
}
return false;
};
Pub.someIndex = function (arr, func, con) {
var ind,
len;
// validation - prevent type errors
if ((arr == null) || (arr.length !== +arr.length) || (typeof func !== 'function')) {
return false;
}
// delegate to native some()
if (nativeSome && arr.some === nativeSome) {
return arr.some(func, con);
}
for (ind = 0, len = arr.length; ind < len; ind++) {
if (func.call(con, arr[ind], ind, arr)) {
return true;
}
}
return false;
};
Pub.someString = function (str, func, con) {
if (typeof str !== 'string') {
return false;
}
return Pub.someIndex(str.split(/\s+/), func, con);
};
Pub.morph = function (obj, func) {
if (typeof func !== 'function') {
return false;
}
Pub.someKey(obj, function (val, key) {
obj[key] = func(val);
});
return obj;
};
// near direct copy from underscore
Pub.lacks = function (obj) {
Pub.someIndex(nativeSlice.call(arguments, 1), function (val) {
var lacks;
if (val) {
for (lacks in val) {
if (obj[lacks] === undef) {
obj[lacks] = val[lacks];
}
}
}
});
return obj;
};
// shallow clone
Pub.cloneFlat = function (obj) {
if (!Pub.isObjectWritable(obj)) {
return obj;
}
return Pub.isType('Array', obj) ? obj.slice() : Pub.extendFlat({}, obj);
};
// extends non-prototype properties from obj2 on to obj1
// with out any over writing. does not extend up the prototype chain.
Pub.extendSafe = function (obj1, obj2) {
var key;
for (key in obj2) {
if (obj2.hasOwnProperty(key)) {
if (obj1.hasOwnProperty(key)) {
throw new Error("naming collision: " + key);
}
obj1[key] = obj2[key];
}
}
return obj1;
};
// does not extend up the prototype chain like underscore
Pub.extendFlat = function (obj) {
Pub.someIndex(nativeSlice.call(arguments, 1), function (object) {
var key;
if (object) {
for (key in object) {
if (object.hasOwnProperty(key)) {
obj[key] = object[key];
}
}
}
});
return obj;
};
// for adding underscore and a suffix
Pub.addU = function (str, suf) {
if (typeof str !== 'string' || typeof suf !== 'string') {
return false;
}
return str + '_' + suf;
};
// for removing the last underscore and suffix
Pub.removeU = function (str) {
var res;
if (typeof str !== 'string') {
return false;
}
res = str.lastIndexOf("_");
if (res !== -1) {
return str.slice(0, res);
}
return false;
};
// first key that for / in will iterate through
Pub.firstKey = function (obj) {
var prop;
for (prop in obj) {
if (obj.hasOwnProperty(prop)) {
return prop;
}
}
return false;
};
Pub.testKeys = function (keys, pattern, func) {
var test = '',
key;
for (key in keys) {
test += key;
}
if (test === pattern) {
func();
}
};
// runtTest("test_foo", [input1, input2], function(arr){//write test here});
Pub.runTest = (function () {
var tests = {};
return function (name, arr, func) {
tests[name] = func.apply(this, arr);
};
}());
Pub.prettyTime = function (post_time) {
var NORMALIZE = 1000, // 1000 milliseconds in a second
MINUTE = 60, // 60 seconds in a minute
HOUR = 3600, // 3600 seconds in an hour
DAY = 43200, // 43,200 seconds in a day
// server time is in seconds while browser time is in milliseconds
current_time = Math.round(Date.now() / NORMALIZE),
rounded_time,
elapsed_time,
string = '';
// synch factor actually exeeds transit time in some cases
// post_time originates on the server as a unix time stamp
// and current_time we calculate above
if (current_time < post_time) {
current_time = post_time;
}
elapsed_time = (current_time - post_time);
if (elapsed_time === 0) {
string = ' just a second ago';
// 0 to 1 minute ago
} else if ((elapsed_time > 0) && (elapsed_time < MINUTE)) {
string = (elapsed_time === 1) ? 'one second ago' :
(elapsed_time + ' seconds ago');
// 1 minute to 1 hour ago
} else if ((elapsed_time >= MINUTE) && (elapsed_time < HOUR)) {
rounded_time = Math.floor(elapsed_time / MINUTE);
string = (rounded_time === 1) ? 'one minute ago' :
(rounded_time + ' minutes ago');
// 1 hour to to 1 day ago
} else if ((elapsed_time >= HOUR) && (elapsed_time < DAY)) {
rounded_time = Math.floor(elapsed_time / HOUR);
string = (rounded_time === 1) ? 'one hour ago' :
(rounded_time + ' hours ago');
// more than 1 day ago
} else if ((elapsed_time >= DAY)) {
rounded_time = new Date(post_time * NORMALIZE);
string = 'on ' + rounded_time.toLocaleDateString();
}
return string;
};
// Used for primitives saved to localStorage and sessionStorage
Pub.unStringify = function (string) {
// Booleans first
if (string === "true") {
return true;
}
if (string === "false") {
return false;
}
};
// for the arc library
global[Priv.g] = Pub.extendSafe(global[Priv.g] || {}, Pub);
// for underscore mixins
global.ArcUtility = Pub;
}(this));