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
}