API Docs for:
Show:

File: development/js/utils/object.js

import type from './type';
import funcs from './func';


/**
 * Provides a whole bunch of utility functions
 *
 * @module Utils
 * @class Utils.Object
 * @static
 */



/**
 * Gets a property from a given object
 *
 * @method prop
 * @for Utils.Object
 * @param {string} prop The property to get
 * @param {any} obj The object/value to get the property from
 * @return {any} Value of the property
 *
 * @example
 *     import {prop} from './utils/object';
 *     
 *     var obj = {foo: 'bar'}
 *
 *     prop('foo', obj); // -> 'bar'
 *
 *     const getFoo = prop('foo');
 *
 *     getFoo(obj); // -> 'bar'
 */
export const prop = funcs.curry(function (prop, obj) {
    return obj != null ? obj[prop] : obj;
});


/**
 * Checks if a field exists on a given object and is not inherited
 *
 * @method has
 * @for Utils.Object
 * @param {string} prop Name of the field
 * @param {object} obj The object to test against
 * @return {boolean} True if field exists, false otherwise
 *
 * @example
 *     import {has} from './utils/object';
 *     
 *     var obj = {foo: 'bar'};
 *
 *     has('foo', obj);
 *     // -> true
 */
export const has = funcs.curry(function (prop, obj) {
    return Object.prototype.hasOwnProperty.call(obj, prop);
});

/**
 * Returns the names of all fields which exist on a given object
 *
 * @method keys
 * @for Utils.Object
 * @param {object} obj The object to retrieve the fields from
 * @return {array} List of fields
 *
 * @example
 *     import {keys} from './utils/object';
 *     
 *     var obj = {foo: 'bar'};
 *
 *     keys(obj);
 *     // -> ['foo']
 */
export const keys = function (obj) {
    return Object.keys(obj);
}

/**
 * Returns the values of every field on a given object which is not inherited
 *
 * @method values
 * @for Utils.Object
 * @param {object} obj The object to retrieve the values from
 * @return {array} List of values
 *
 * @example
 *     import {values} from './utils/object';
 *     
 *     var obj = {foo: 'bar'};
 *
 *     values(obj);
 *     // -> ['bar']
 */
export const values = function (obj) {
    return Object.keys(obj).map(function (key) {
        return obj[key];
    })
}

/**
 * Converts the fields of a given object into a multi dimensional list of
 *     key-value pairs
 *
 * @method pairs
 * @for Utils.Object
 * @param {object} obj The object to retrieve pairs from
 * @return {array} List of lists of key-value pairs
 *
 * @example
 *     import {pairs} from './utils/object';
 *     
 *     var obj = {foo: 'bar'};
 *
 *     pairs(obj);
 *     // -> [['foo', 'bar']]
 */
export const pairs = function (obj) {
    return Object.keys(obj).map(function (key) {
        return [key, obj[key]];
    });
}

/**
 * Merges the right object into the left one and returns the left one
 *
 * @method extend
 * @for Utils.Object
 * @param {object} base Basic object which should be extended
 * @param {object} ext Extending object
 * @return {object} Returns the base
 *
 * @example
 *     import {extend} from './utils/object';
 *     
 *     var obj1 = {foo: 'bar'};
 *
 *     var obj2 = {goo: 'baz'};
 *
 *     extend(obj1, obj2);
 *     // -> {foo: 'bar', goo: 'baz'}
 */
export const extend = function (base, ext) {
    var _prop;
    if (!type.isObject(base)) {
        return base;
    }

    for (_prop in ext) {
        if (ext.hasOwnProperty(_prop)) {
            base[_prop] = ext[_prop];
        }
    }
    return base;
}

/**
 * Merges all right objects into the left one and returns the left one
 *
 * @method mixin
 * @for Utils.Object
 * @param {object} base Basic object which should be extended
 * @param {array} extensions List of extension objects to mix into
 * @return {object} Returns the base
 *
 * @example
 *     var base = {foo: 'bar'};
 *
 *     var ext1 = {goo: 'baz'};
 *     var ext2 = {hoo: 'wicked'};
 *
 *     Utils.mixin(base, [ext1, ext2]);
 *
 *     base;
 *     // -> {foo: 'bar', goo: 'baz', hoo: 'wicked'}
 */
export const mixin = function (base, exts) {
    return exts.reduce(extend, base);
}

/**
 * Works like Utils.extend() but for constructor functions
 *
 * @method inherit
 * @for Utils.Object
 * @param {function} base Constructor of inherited class
 * @param {object} extension Extension object
 * @return {object} Inherited prototype object
 *
 * @example
 *     var Base = function () {};
 *
 *     var Ext = function () {};
 *     Ext.prototype = Utils.inherit(Base, {
 *         // definition
 *     });
 */
export const inherit = function (base, extension) {
    return mixin({}, [base.prototype, extension]);
}

/**
 * Delegates method invocations from one object to another. The first argument
 *     is the receiving object, the second one is the providing object
 *
 * @method delegate
 * @for Utils.Object
 * @deprecated This is likely to be removed in the future
 * @param {object} to Receiving object
 * @param {object} from Providing object
 * @param {array} methods Array of method names to delegate
 * @return {object} The receiving object
 *
 * @example
 *     var oRec = {
 *         name: 'Test'
 *     };
 *
 *     var oProv = {
 *         upperName: function () {
 *             return this.name.toUpperCase();
 *         }
 *     };
 *
 *     Utils.delegate(oRec, oProv, ['upperName']);
 *
 *     oRec.upperName();
 *     // -> "TEST"
 *     
 */
export const delegate = function (to, from, methods) {
    methods.forEach((m) => {
        if (type.isFunction(from[m])) {
            to[m] = function (...args) {
                return from[m].apply(to, args);
            }
        }
    });

    return to;
}

/**
 * Forwards method invocations from one object to another. The first argument
 *     is the forwarding object, the second one is the receiving object. This
 *     helps for example to create private properties
 *
 * @method forward
 * @for Utils.Object
 * @param {object} from Forwarding object
 * @param {object} to Receiving object
 * @param {array} methods Array of method names to forward
 * @return {object} The forwarding object
 *
 * @example
 *     var oForw = {};
 *
 *     var oProv = {
 *         name: 'Test',
 *         upperName: function () {
 *             return this.name.toUpperCase();
 *         }
 *     };
 *
 *     Utils.forward(oForw, oProv, ['upperName']);
 *
 *     oForw.upperName();
 *     // -> "TEST"
 *
 *     oForw.name;
 *     // -> undefined
 *     
 */
export const forward = function (from, to, methods) {
    methods.forEach((m) => {
        if (type.isFunction(to[m])) {
            from[m] = function (...args) {
                return to[m].apply(to, args);
            }
        }
    });

    return from;
}

/**
 * Creates a predefined method invocation and allows to skip the receiver of
 *     the invocation. All arguments after the first parameter are considered
 *     to be the parameters which shall be used (partial application)
 *
 * @method exec
 * @for Utils.Object
 * @param {string|function} f Method to invoke
 * @return {function} Function which accepts a receiver and optional arguments
 *
 * @example
 *     // general uppercase function
 *     var upper = Utils.exec('toUpperCase');
 *     upper('hello world'); // -> 'HELLO WORLD'
 *
 *
 *     // general splitter function for spaces
 *     var splitSpace = Utils.exec('split', ' ');
 *     splitSpace('hello world'); // -> ['hello', 'world']
 *
 *
 *     // splitter a bit more general
 *     var split = Utils.exec('split');
 *
 *     // pass parameters later
 *     split('hello world', ' '); // -> ['hello', 'world']
 *
 *
 *     // if a function returns undefined/void, the receiver is
 *     // returned
 *     // ---
 *     
 *     var preventDefault = Utils.exec('preventDefault');
 *     var handleEvent = Utils.pipe(preventDefault, console.log.bind(console));
 *
 *     document.querySelector('a').addEventListener('click', handleEvent);
 *
 *     // logs the Event object on each click while preventing the default
 *     // action
 */
export const exec = function (f, ...pArgs) {
    return function (target, ...rArgs) {
        var args = pArgs.concat(rArgs),
            rVal = null;
        if (!!target) {
            if (type.isString(f) && type.isFunction(target[f])) {
                rVal = target[f].apply(target, args);
            } else if (type.isFunction(f)) {
                rVal = f.apply(target, args);
            }
            return rVal === void 0 ? target : rVal;
        }
        return null; 
    }
}



export default {
    has, values, keys, pairs, forward, delegate, exec, inherit,
    mixin, extend, prop
}