Skip to main content
8 of 11
deleted 619 characters in body
user avatar
user avatar

A package for basic utility functions

Compare to underscore if it pleases you. Once again, I hope this is well commented. Please let me know what comments/improvements I can add.

/***************************************************************************************************
**ARC

Goal:
 - to create a small efficient package system that can be extended.
 
How it works:
 The utility package is independent.
 The comms package requires the utility package.
 The dom package requires the utility package.
 The frame package requires the utility and comms package.
 The boot package requires the utility, dom and comms package.
 
 Notes
 - passes jslint
   - browser:   allow access to the DOM
   - forin:     allow for in with out prototype filtering
   - plusplus:  alow ++
   - eqeq:      allow == and !=
 - minifies with closure
 - is broken up into packages with listed dependencies
   - utility, dom, comms, frame, boot
 - uses a single global variable with safe extending for each package
 
 
***************************************************************************************************/

/***************************************************************************************************
**UTILITY

Goal

 - the smallest most efficient utility library
 
List of Functions

 - Type Checking
   isType, getType, isUndefined, isNumber, isGone, isHere, isFalse, isTruthy, isBoolean, isArray,
   isObjectAbstract, isArrayAbstract, 
 - Looping
   someIndex, someKey, someString, 
 - Extending 
   extend, extendSafe
 - General
   filter, clone
 
Advantages
 
 - additional coverage for utilities, removal of pointless functions - isNull() for example, 
   consistent ordering, naming conventions, input validation, fewer function branches
 
`someIndex` and `someKey` explained:

`someIndex` and `someKey` are abstractions for looping through arrays and objects respectively.  
Unlike underscore, where you can use `some` or `each` to loop through both arrays and objects, 
I chose to only use `some` and break its usage up between array literals and object literals.  This 
is b.c. when I code I always know which I am dealing with, and I wanted to express this knowledge 
in my code.  This allowed my to write two shorter functions with less dependencies.  
I use `someIndex` and `someKey` for all loops, whether breaks are needed or not.  
This confuses some underscore users because they think it is in-efficient, 
they might be unaware that even the underscore `each` 
contains a check for an internal break.  Also I wanted to emulate the raw loops as much as
possible, and you can of course break out of a for or for-in loop whenever you like.
In short, if you want to go faster, use raw `for` or `for-in` loops in either case.  

***************************************************************************************************/

// self used to hold client or server side global
(function (self, undef) {

    "use strict";

    // holds (P)ublic properties
    var $P = {},

    // holds p(R)ivate properties
        $R = {},

        // native methods (alphabetical order)
        nativeFilter = Array.prototype.filter,
        nativeIsArray = Array.isArray,
        nativeSlice = Array.prototype.slice,
        nativeSome = Array.prototype.some,
        nativeToString = Object.prototype.toString;

    $P.noConflict = (function () {

        // $R.g holds the single global variable, used to hold all packages
        $R.g = '$A';
        $R.previous = self[$R.g];

        // utility is required by all other packages
        $P.pack = {
            utility: true
        };
        return function () {
            var temp = self[$R.g];
            self[$R.g] = $R.previous;
            return temp;
        };
    }());

    $P.isType = function (type, obj) {
        return $P.getType(obj) === type;
    };

    // returns type in a capitalized string form
    // typeof is only accurate for function, string, number, boolean, and
    // undefined.  null and array are both incorrectly reported as object
    $P.getType = function (obj) {
        return nativeToString.call(obj).slice(8, -1);
    };

    $P.isUndefined = function (obj) {
        return obj === undef;
    };

    // not complete
    $P.isNumber = function (value) {
        return (typeof value === 'number');
    };

    // detects null or undefined
    $P.isGone = function (obj) {
        return obj == null;
    };

    // detects anything but null or undefined
    $P.isHere = function (obj) {
        return obj != null;
    };

    // detects null, undefined, NaN, ('' ""), 0, -0, false
    $P.isFalsy = function (obj) {
        return !obj;
    };

    // detects any thing but null, undefined, NaN, ('' ""), 0, -0, false
    $P.isTruthy = function (obj) {
        return !!obj;
    };

    // shortcut as their are only two primitive boolean values
    // detects a "boxed" boolean as well
    $P.isBoolean = function (obj) {
        return obj === true || obj === false ||
            nativeToString.call(obj) === '[object Boolean]';
    };

    $P.isArray = nativeIsArray || function (obj) {
        return nativeToString.call(obj) === '[object Array]';
    };

    // jslint prefers {}.constructor(obj) over Object(obj)
    $P.isObjectAbstract = function (obj) {
        // return obj === Object(obj);
        return !!(obj && (obj === {}.constructor(obj)));
    };

    $P.isArrayAbstract = function (obj) {
        return !!(obj && obj.length === +obj.length);
    };

    $P.someIndex = function (arr, func, con) {
        var ind,
            len;

        // prevent type errors
        if ((arr == null) || (arr.length !== +arr.length) || (typeof func !== 'function')) {
            return;
        }

        // delegate to native some()
        if (nativeSome && arr.some === nativeSome) {
            return arr.some(func, con);
        }

        // if the function passes back a truthy value, the loop will terminate
        for (ind = 0, len = arr.length; ind < len; ind++) {
            if (func.call(con, arr[ind], ind, arr)) {
                return true;
            }
        }
        return false;
    };

    $P.someKey = function (obj, func, con) {
        var key;

        // prevent type errors
        if ((obj == null) || (typeof func !== 'function')) {
            return;
        }
        for (key in obj) {
            if (obj.hasOwnProperty(key)) {
                if (func.call(con, obj[key], key, obj)) {
                    return true;
                }
            }
        }
        return false;
    };

    // loop through space separated "tokens" in a string
    $P.someString = function (str, func, con) {

        // prevent type errors
        if (typeof str !== 'string' || str === "" || typeof func !== 'function') {
            return;
        }
        $P.someIndex(str.split(/\s+/), func, con);
    };

    // does not extend through the prototype chain
    // implemented for objects only
    $P.extend = function (obj) {

        // loop througth elements beyond obj
        $P.someIndex(nativeSlice.call(arguments, 1), function (val) {

            // extend it
            $P.someKey(val, function (val_inner, key) {
                obj[key] = val_inner;
            });
        });
        return obj;
    };

    // extends non-prototype properties from obj2 on to obj1
    $P.extendSafe = function (obj1, obj2) {
        var key;
        for (key in obj2) {
            if (obj2.hasOwnProperty(key)) {
                if (obj1.hasOwnProperty(key)) {
                    throw "naming collision: " + key;
                }
                obj1[key] = obj2[key];
            }
        }
        return obj1;
    };

    // if incorrect types are passed, it will return an empty array
    // arrays only
    $P.filter = function (arr, func, con) {
        var results = [];
        if ((arr == null) || (typeof func !== 'function')) {
            return results;
        }
        if (nativeFilter && arr.filter === nativeFilter) {
            return arr.filter(func, con);
        }
        $P.someIndex(arr, function (val, ind, arr) {
            if (func.call(con, val, ind, arr)) {
                results.push(val);
            }
        });
        return results;
    };

    $P.clone = function (obj) {
        return $P.extend({}, obj);
    };

    $P.someIndex(['Arguments', 'Function', 'String', 'Number',
        'Date', 'RegExp', 'Object'], function (val) {
        $P['is' + val] = function (obj) {
            return $P.isType(val, obj);
        };
    });

    // equivalent to IIFE but "nicer" syntax
    $P.runTest = (function () {
        var tests = {};
        return function (name, arr, func) {
            tests[name] = func.apply(this, arr);
        };
    }());

    // ignore from here down, in the process of removal
    $P.addU = function (str, suf) {
        return str + '_' + suf;
    };

    $P.removeU = function (str) {
        if (typeof str !== 'string' || str === "") {
            return false;
        }
        return str.slice(str, str.lastIndexOf("_"));
    };

    $P.removeSuffix = function (str, len) {
        if (typeof str !== 'string' || str === "") {
            return false;
        }
        return str.slice(0, -len);
    };
    self[$R.g] = $P.extendSafe($P, {});
}(this));
anon