Last active
August 29, 2015 14:21
-
-
Save Error601/68cb013bdf41485b2126 to your computer and use it in GitHub Desktop.
Useful helper functions inspired by (or lifted from) other libraries.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*! | |
* Global general-purpose JavaScript convenience | |
* utlity helper functions, because the world | |
* needs yet ANOTHER JavaScript library for | |
* things that should just be native JavaScript | |
* methods in the first place! | |
* | |
* Some of these functions are taken straight from | |
* other libraries and transplanted here - thus | |
* the name 'locash' - a nod to lodash/underscore | |
* and jQuery. | |
* | |
* Methods available under global '_$' and | |
* 'locash' objects. You may want to set | |
* _$ = locash in the scope of your functions | |
* to avoid possible conflicts if you use a library | |
* that uses _$ as the top-level object/funtion. | |
* | |
* Latest source available here: | |
* https://gist.github.com/Error601/68cb013bdf41485b2126 | |
*/ | |
(function(){ | |
var root = this, | |
window = root, | |
_$, locash, | |
undefined; | |
root._$ = root.locash = locash = _$ = | |
firstDefined(root._$, root.locash, {}); | |
function firstDefined(){ | |
var undefined, i = -1; | |
while (++i < arguments.length) { | |
if (arguments[i] !== undefined) { | |
return arguments[i]; | |
} | |
} | |
return undefined; | |
} | |
_$.firstDefined = firstDefined; | |
function isDefined(x){ | |
return typeof x != 'undefined' | |
} | |
_$.isDefined = isDefined; | |
function isUndefined(x){ | |
return typeof x == 'undefined' | |
} | |
_$.isUndefined = isUndefined; | |
function isString(str){ | |
return typeof str === 'string'; | |
} | |
_$.isString = isString; | |
function stringLower(str){ | |
return str.toString().toLowerCase(); | |
} | |
_$.stringLower = stringLower; | |
function stringUpper(str){ | |
return str.toString().toUpperCase(); | |
} | |
_$.stringUpper = stringUpper; | |
// split string into array of words using spaces, underscores, | |
// hyphens, periods, commas, or 'delim' (RegExp) as word delimeters | |
function splitWords(str, delim){ | |
str = str+''; | |
delim = delim || /(\s|,|_|\-|\.)+/g; | |
// insert spaces between words and remove multiple spaces | |
str = str.replace(delim,' '); | |
// split on whitespace char. | |
return str.split(/\s/); | |
} | |
_$.splitWords = splitWords; | |
// convert from camelCase 'textString', | |
// or underscored 'text_string', | |
// or space-separated 'text string' | |
// or period-separated text.string | |
// or array of separate strings | |
// to hyphenated 'text-string' | |
// optionally preserving original case where | |
// possible and allowing 'other' characters | |
// 'delim' is optional custom word delimiter | |
function toDashed(str, other, preserve, delim){ | |
if (isArray(str)){ | |
str = str.join(' '); | |
// force delim to space if 'str' is array | |
delim = /\s+/; | |
} | |
else { | |
str = str+''; | |
} | |
return splitWords(str, delim).map(function(word){ | |
// add hyphens before capital letters | |
word = word.replace(/[A-Z]/g, function(c){ | |
return '-' + c; | |
}); | |
// remove hyphens after capital letters | |
word = word.replace(/([A-Z]\-)/g, function(c){ | |
return c.replace(/\-$/, ''); | |
}); | |
if (!preserve){ | |
word = word.toLowerCase(); | |
} | |
if (!other){ | |
word = word.replace(/[^A-Za-z0-9\-]/g, '-'); | |
} | |
return word; | |
}).join('-').replace(/\-+/g, '-').replace(/^\-+|\-+$/g, ''); | |
} | |
_$.toDashed = _$.dashify = _$.dasherize = _$.hyphenize = toDashed; | |
// convert from hyphenated 'text-string', | |
// or underscored 'text_string', | |
// or space-separated 'text string' | |
// or period-separated text.string | |
// or array or separate strings | |
// to camelCase 'textString' | |
// optionally preserving original case where | |
// possible and allowing 'other' characters | |
function toCamelCase(str, other, preserve, delim){ | |
if (isArray(str)){ | |
str = str.join(' '); | |
// force delim to space if 'str' is array | |
delim = /\s+/; | |
} | |
else { | |
str = str+''; | |
} | |
return splitWords(str, delim).map(function(word, i){ | |
if (!preserve){ | |
word = word.toLowerCase(); | |
} | |
if (i > 0){ | |
word = word.charAt(0).toUpperCase() + word.substr(1); | |
} | |
if (!other){ | |
word = word.replace(/[^A-Za-z0-9]/g, ''); | |
} | |
return word; | |
}).join(''); | |
} | |
_$.toCamelCase = _$.camelCase = _$.camelify = _$.camelize = toCamelCase; | |
// force only characters after a dash to be | |
// capitalized when converting to camelCase | |
// 'Foo-BAR-blah' -> 'fooBarBlah' | |
function forceCamelCase(str, other){ | |
return toCamelCase(str, other, false); | |
} | |
_$.forceCamelCase = _$.forceCamel = _$.toCamelLower = _$.camelLower = forceCamelCase; | |
function sentenceCase(str){ | |
return str.charAt(0).toUpperCase() + str.slice(1); | |
} | |
_$.sentenceCase = _$.capitalize = sentenceCase; | |
function titleCase(str, force){ | |
var words = (str+'').split(/\s+|_+|\-+/); // allow spaces, underscores, or dashes as word delimiters | |
return words.map(function(word){ | |
if (isTrue(force)){ | |
word = word.toLowerCase(); | |
} | |
return word.charAt(0).toUpperCase() + word.slice(1); | |
}).join(' '); | |
} | |
_$.titleCase = titleCase; | |
function truncateString(str, len, suffix){ | |
str = str+''; | |
len = len || 30; // default length is 30 chars | |
if (str.length <= len) { | |
return str; | |
} | |
else { | |
return str.substring(0, len) + (suffix||'...'); | |
} | |
} | |
_$.truncate = _$.truncateString = truncateString; | |
function isTrue(val){ | |
return stringLower(val || '') === 'true'; | |
} | |
_$.isTrue = isTrue; | |
function isFalse(val){ | |
return stringLower(val || '') === 'false'; | |
} | |
_$.isFalse = isFalse; | |
// heavy-handed comparison of 2 values as strings | |
function isEqual(a, b){ | |
if (arguments.length === 2 && typeof a != 'undefined') { | |
return (a.toString() === b.toString()); | |
} | |
else { | |
return undefined; | |
} | |
} | |
_$.isEqual = isEqual; | |
function isEqualLower(a, b){ | |
return isEqual(stringLower(a || ''), stringLower(b || '')); | |
} | |
_$.isEqualLower = isEqualLower; | |
// returns true for objects, arrays, and null | |
function isObject( obj ){ | |
return typeof obj == 'object'; | |
} | |
_$.isObject = isObject; | |
function isPlainObject(obj){ | |
return Object.prototype.toString.call(obj) === '[object Object]'; | |
} | |
_$.isPlainObject = isPlainObject; | |
// return something to attach properties and methods to | |
function getObject(obj){ | |
if (isFunction(obj) || isPlainObject(obj)) { | |
return obj; | |
} | |
else { | |
return {}; | |
} | |
} | |
_$.getObject = getObject; | |
function getPlainObject(obj){ | |
return isPlainObject(obj) ? obj : {}; | |
} | |
_$.getPlainObject = getPlainObject; | |
function isArray(arr){ | |
if (Array.isArray) { | |
return Array.isArray(arr); | |
} | |
else { | |
return Object.prototype.toString.call(arr) === '[object Array]'; | |
} | |
} | |
_$.isArray = isArray; | |
function isFunction(func){ | |
return typeof func == 'function'; | |
} | |
_$.isFunction = isFunction; | |
function isNumber(num){ | |
return (typeof num == 'number' && !isNaN(num)); | |
} | |
_$.isNumber = isNumber; | |
// copy of jQuery's $.isNumeric() method | |
function isNumeric(num){ | |
// parseFloat NaNs numeric-cast false positives (null|true|false|"") | |
// ...but misinterprets leading-number strings, particularly hex literals ("0x...") | |
// subtraction forces infinities to NaN | |
// adding 1 corrects loss of precision from parseFloat (jQuery issue #15100) | |
return !isArray(num) && (num - parseFloat(num) + 1) >= 0; | |
} | |
_$.isNumeric = isNumeric; | |
function isEmptyArray(arr){ | |
return isArray(arr) && arr.length === 0; | |
} | |
_$.isEmptyArray = isEmptyArray; | |
function isEmptyObject(obj){ | |
var name; | |
for ( name in obj ) { | |
return false; | |
} | |
return true; | |
} | |
_$.isEmptyObject = isEmptyObject; | |
function isEmpty(x){ | |
if (isPlainObject(x)) { | |
return isEmptyObject(x); | |
} | |
if (isArray(x)) { | |
return isEmptyArray(x); | |
} | |
return (x === '' || x === null || isUndefined(x) || !isFunction(x)); | |
} | |
_$.isEmpty = isEmpty; | |
// copy of jQuery's $.extend() method | |
function extend(){ | |
var src, copyIsArray, copy, name, options, clone, | |
target = arguments[0] || {}, | |
i = 1, | |
length = arguments.length, | |
deep = false; | |
// Handle a deep copy situation | |
if (typeof target === "boolean") { | |
deep = target; | |
// skip the boolean and the target | |
target = arguments[i] || {}; | |
i++; | |
} | |
// Handle case when target is a string or something (possible in deep copy) | |
if (typeof target !== "object" && !isFunction(target)) { | |
target = {}; | |
} | |
// extend parent object if only one argument is passed | |
if (i === length) { | |
target = this; | |
i--; | |
} | |
for (; i < length; i++) { | |
// Only deal with non-null/undefined values | |
if ((options = arguments[i]) != null) { | |
// Extend the base object | |
for (name in options) { | |
// don't check for this - extend everything | |
//if ( !options.hasOwnProperty(name) ) { | |
// continue; | |
//} | |
src = target[name]; | |
copy = options[name]; | |
// Prevent never-ending loop | |
if (target === copy) { | |
continue; | |
} | |
// Recurse if we're merging plain objects or arrays | |
if (deep && copy && ( isPlainObject(copy) || (copyIsArray = isArray(copy)) )) { | |
if (copyIsArray) { | |
copyIsArray = false; | |
clone = src && isArray(src) ? src : []; | |
} | |
else { | |
clone = src && isPlainObject(src) ? src : {}; | |
} | |
// Never move original objects, clone them | |
target[name] = extend(deep, clone, copy); | |
// Don't bring in undefined values | |
} | |
else if (copy !== undefined) { | |
target[name] = copy; | |
} | |
} | |
} | |
} | |
// Return the modified object | |
return target; | |
} | |
_$.extend = extend; | |
// default deep extend | |
function extendDeep(){ | |
var args = toArray(arguments); | |
return extend.apply(null, [true].concat(args)); | |
} | |
_$.extendDeep = extendDeep; | |
// clone and extend | |
function extendCopy(){ | |
var args = toArray(arguments); | |
return extend.apply(null, [{}].concat(args)); | |
} | |
_$.extendCopy = extendCopy; | |
// clone and deep extend | |
function extendCopyDeep(){ | |
var args = toArray(arguments); | |
return extend.apply(null, [true, {}].concat(args)); | |
} | |
_$.extendCopyDeep = extendCopyDeep; | |
// return a deep cloned copy of a single 'obj' | |
function cloneObject(obj){ | |
return extend(true, {}, obj); | |
} | |
_$.cloneObject = cloneObject; | |
function clone(obj){ | |
if (isPlainObject(obj)){ | |
return extend(true, {}, obj); | |
} | |
if (isArray(obj)){ | |
return toArray(obj); | |
} | |
if (isFunction(obj)){ | |
return function(){ | |
obj.apply(null, arguments); | |
} | |
} | |
else { | |
return obj; | |
} | |
} | |
_$.clone = clone; | |
// return the last item in an array-like object | |
function getLast(arr){ | |
var undefined; | |
if (isEmpty(arr)) { return undefined } | |
if (!arr.length) { return arr } | |
return arr[arr.length - 1]; | |
} | |
_$.getLast = getLast; | |
// make sure we only run a function one time | |
function once(func, args){ | |
func = func || function(){}; | |
if (func.called) { return } | |
func.apply(null, args); | |
func.called = true; | |
} | |
_$.once = once; | |
// execute a function on each item in an | |
// array(-like) object with a length property | |
// works like native Array.forEach(); | |
function forEach(arr, fn){ | |
var i = -1, len; | |
if (!arr || !arr.length) { return } | |
len = arr.length; | |
if (isFunction(fn)) { | |
while (++i < len) { | |
fn(arr[i], i); | |
} | |
} | |
} | |
_$.forEach = forEach; | |
// execute a function on each | |
// of an object's own properties | |
// works like jQuery's $.each() | |
// but only for objects | |
function forOwn(obj, fn){ | |
var _key; | |
if (!isObject(obj)) { return } | |
if (isFunction(fn)) { | |
for (_key in obj) { | |
if (obj.hasOwnProperty(_key)) { | |
fn(_key, obj[_key]); | |
} | |
} | |
} | |
} | |
_$.forOwn = forOwn; | |
// convert array-like object or arguments to a real array | |
// (twice as fast as Array.prototype.slice.call(arguments)) | |
function toArray(arr){ | |
var i = -1, | |
len = arr.length, | |
_args = new Array(len); | |
while (++i < len) { | |
_args[i] = arr[i]; | |
} | |
return _args; | |
} | |
_$.toArray = toArray; | |
// check if 'item' is in an 'arr' array | |
function inArray(item, arr){ | |
var i = -1, | |
len = arr.length; | |
if (!len) { | |
return false; | |
} | |
while (++i < len){ | |
if (arr[i] === item){ | |
return true; | |
} | |
} | |
return false; | |
} | |
_$.inArray = inArray; | |
// remove duplicate values from an array | |
function dedupeArray(arr){ | |
var out = [], | |
i = -1, | |
len = arr.length, | |
item; | |
while (++i < len) { | |
item = arr[i]; | |
if (!inArray(item, out)){ | |
out.push(item); | |
} | |
} | |
return out; | |
} | |
_$.dedupeArray = _$.dedupe = dedupeArray; | |
// set default values for object | |
function setDefaults(obj, props){ | |
//obj = getObject(obj); | |
//forOwn(props, function(name, val){ | |
// obj[name] = val; | |
//}); | |
//return obj; | |
return extendCopyDeep(obj, props) | |
} | |
_$.setDefaults = setDefaults; | |
// pass an array of values to make sure ALL of them are numbers | |
// 'numeric' argument indicates allowing numeric *string*: '1' | |
function allNumbers(arr, numeric){ | |
var len = arr.length, | |
i = -1, | |
checkNumber = (numeric) ? isNumeric : isNumber; | |
if (!isArray(arr)) { return false } | |
while (++i < len) { | |
if (!checkNumber(arr[i])) { | |
return false; | |
} | |
} | |
return true; | |
} | |
_$.allNumbers = allNumbers; | |
// pass an array of values to make sure they're ALL numeric | |
function allNumeric(arr){ | |
return allNumbers(arr, true); | |
} | |
_$.allNumeric = allNumeric; | |
// pass an array of values to check for at least one number | |
// 'numeric' argument indicates allowing numeric string | |
function hasNumber(arr, numeric){ | |
var numbers = 0, | |
len = arr.length, | |
i = -1, | |
checkNumber = (numeric) ? isNumeric : isNumber; | |
if (!isArray(arr)) { return false } | |
while (++i < len) { | |
if (checkNumber(arr[i])) { | |
numbers += 1; | |
} | |
} | |
return numbers > 0; | |
} | |
_$.hasNumber = hasNumber; | |
// returns number as a string with leading zeros (or other character) | |
// thanks to - http://stackoverflow.com/a/10073788 | |
// revised here - http://jsfiddle.net/rj0rf5hg/2/ | |
// padNumber( 5 ) //=> '05' | |
// padNumber( 55, 4 ) //=> '0055' | |
// padNumber( 555, 6, 'X' ) //=> 'XXX555' | |
function padNumber(num, size, fill){ | |
// only whole numbers | |
if (parseInt(num, 10) !== +num) { return num + '' } | |
num = num + ''; // make sure 'num' is a string | |
// make sure 'size' is a whole number | |
// defaults to 2 digits | |
size = (typeof size != 'undefined') ? parseInt(size, 10) : 2; | |
fill = fill || '0'; // default fill character is '0' | |
return (num.length >= size) ? num : new Array(size - num.length + 1).join(fill) + num; | |
} | |
_$.padNumber = padNumber; | |
function zeroPad(num, size, fill){ | |
return padNumber(num, size, fill || '0'); | |
} | |
_$.zeroPad = zeroPad; | |
// add commas to numbers | |
function addCommas(nStr){ | |
nStr += ''; | |
var | |
x = nStr.split('.'), | |
x1 = x[0], | |
x2 = x.length > 1 ? '.' + x[1] : '' | |
; | |
var rgx = /(\d+)(\d{3})/; | |
while (rgx.test(x1)) { | |
x1 = x1.replace(rgx, '$1' + ',' + '$2'); | |
} | |
return x1 + x2; | |
} | |
_$.addCommas = _$.formatNumber = addCommas; | |
function roundNumber(num, dec){ | |
return Math.round(num * Math.pow(10, dec)) / Math.pow(10, dec); | |
} | |
_$.roundNumber = roundNumber; | |
// convert number to file size in KB, MB, GB | |
// rounded to 'round' decimal places | |
function sizeFormat(size, round){ | |
var KB = 1024, | |
MB = KB * KB, | |
GB = MB * KB, | |
TB = GB * KB; | |
// round to 2 decimal places by default | |
round = round || 2; | |
if (size >= TB) { | |
return roundNumber(size / TB, round) + ' TB'; | |
} | |
if (size >= GB) { | |
return roundNumber(size / GB, round) + ' GB'; | |
} | |
if (size >= MB) { | |
return roundNumber(size / MB, round) + ' MB'; | |
} | |
if (size >= KB) { | |
return roundNumber(size / KB, round) + ' KB'; | |
} | |
return size + ' B'; | |
} | |
_$.sizeFormat = _$.dataSize = sizeFormat; | |
function randomID(prefix, seq){ | |
window.autoIDcount = window.autoIDcount || 0; | |
window.autoIDs = window.autoIDs || []; | |
var pre = (isDefined(prefix)) ? prefix : 'i'; | |
var i = (isUndefined(seq) || isTrue(seq)) ? padNumber(++window.autoIDcount, 4) : ''; | |
var newID = pre + i + '-' + (Math.random() + 1).toString(36).substr(2, 8); | |
window.autoIDs.push(newID); | |
window.randomIDcount = window.autoIDcount; | |
window.randomIDs = window.autoIDs; | |
return newID; | |
} | |
window.autoID = window.randomID = randomID; | |
_$.randomID = _$.randomId = _$.autoID = _$.autoId = randomID; | |
// enhanced encodeURIComponent() that | |
// replaces more non-word characters | |
function encodeURIComponentAll(str){ | |
return encodeURIComponent(str).replace(/[!'()*]/g, function(c){ | |
return '%' + c.charCodeAt(0).toString(16); | |
}); | |
} | |
_$.encodeURIComponentAll = _$.encodeURIComponent = _$.betterEncodeURIComponent = encodeURIComponentAll; | |
var supportsDataset = !!(document.head && document.head.dataset); | |
function setElementData(element, name, val){ | |
if (supportsDataset) { | |
element.dataset[toCamelCase(name)] = val; | |
} | |
else { | |
element.setAttribute('data-' + toDashed(name), val); | |
} | |
} | |
_$.setElementData = setElementData; | |
function getElementData(element, name){ | |
if (supportsDataset) { | |
return element.dataset[toCamelCase(name)]; | |
} | |
else { | |
return element.getAttribute('data-' + toDashed(name)); | |
} | |
} | |
_$.getElementData = getElementData; | |
function elementData(element, name, val){ | |
if (isDefined(val)){ | |
return setElementData(element, name, val); | |
} | |
return getElementData(element, name); | |
} | |
_$.elementData = _$.elData = _$._data = elementData; | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment