UNPKG

neft

Version:

Universal Platform

1,362 lines (1,019 loc) 36.5 kB
# Utils Access it with: ```javascript const { utils } = Neft; ``` 'use strict' {toString} = Object:: funcToString = Function::toString {isArray} = Array {shift, pop} = Array:: createObject = Object.create {getPrototypeOf, getOwnPropertyNames} = Object objKeys = Object.keys hasOwnProp = Object.hasOwnProperty getObjOwnPropDesc = Object.getOwnPropertyDescriptor defObjProp = Object.defineProperty {random} = Math ### Link subfiles ### require('./namespace') exports require('./stringifying') exports require('./async') exports ## ReadOnly *Boolean* utils.isNode `true` if the application is running in the node.js environment. ## ReadOnly *Boolean* utils.isServer `utils.isNode` link. ## ReadOnly *Boolean* utils.isClient `utils.isNode` inverse. ## ReadOnly *Boolean* utils.isBrowser ## ReadOnly *Boolean* utils.isQt ## ReadOnly *Boolean* utils.isAndroid ## ReadOnly *Boolean* utils.isIOS exports.isNode = exports.isServer = exports.isClient = exports.isBrowser = exports.isQt = exports.isAndroid = exports.isIOS = false switch true when Qt?.include? exports.isClient = exports.isQt = true when android? exports.isClient = exports.isAndroid = true when ios? exports.isClient = exports.isIOS = true when window?.document? exports.isClient = exports.isBrowser = true when process? and Object.prototype.toString.call(process) is '[object process]' exports.isNode = exports.isServer = true ## *Function* utils.NOP No operation (an empty function). exports.NOP = -> ## *Boolean* utils.is(*Any* value1, *Any* value2) Returns `true` if the given values are exactly the same. It's the *Object.is()* function polyfill (introduced in ECMAScript 6). In opposite to the `===` operator, this function treats two *NaN*s as equal, and `-0` and `+0` as not equal. ```javascript console.log(utils.is('a', 'a')); // true console.log(utils.is(NaN, NaN)); // true, but ... console.log(NaN === NaN); // false console.log(utils.is(-0, 0)); // false, but ... console.log(-0 === 0); // true ``` exports.is = Object.is or (val1, val2) -> if val1 is 0 and val2 is 0 return 1 / val1 is 1 / val2 else if val1 isnt val1 return val2 isnt val2 else return val1 is val2 ## *Boolean* utils.isFloat(*Any* value) Returns `true` if the given value is a finite number. ```javascript console.log(utils.isFloat(10)); // true console.log(utils.isFloat(0.99)); // true console.log(utils.isFloat(NaN)); // false console.log(utils.isFloat(Infinity)); // false console.log(utils.isFloat('10')); // false ``` exports.isFloat = (val) -> typeof val is 'number' and isFinite(val) ## *Boolean* utils.isInteger(*Any* value) Returns `true` if the given value is an integer. ```javascript console.log(utils.isInteger(10)); // true console.log(utils.isInteger(-2)); // true console.log(utils.isInteger(1.22)); // false console.log(utils.isInteger('2')); // false ``` exports.isInteger = (val) -> typeof val is 'number' and isFinite(val) and val > -9007199254740992 and val < 9007199254740992 and Math.floor(val) is val ## *Boolean* utils.isPrimitive(*Any* value) Returns `true` if the given value is a `null`, string, number, boolean or an `undefined`. ```javascript console.log(utils.isPrimitive(null)); // true console.log(utils.isPrimitive('abc')); // true console.log(utils.isPrimitive([])); // false ``` isPrimitive = exports.isPrimitive = (val) -> val is null or typeof val is 'string' or typeof val is 'number' or typeof val is 'boolean' or typeof val is 'undefined' ## *Boolean* utils.isObject(*Any* value) Returns `true` if the given value is an object (object, array, but not a `null`). ```javascript console.log(utils.isObject({})); // true console.log(utils.isObject([])); // true console.log(utils.isObject(null)); // false console.log(utils.isObject('')); // false console.log(utils.isObject(function(){})); // false ``` isObject = exports.isObject = (param) -> param isnt null and typeof param is 'object' ## *Boolean* utils.isPlainObject(*Any* value) Returns `true` if the given value is an object with no prototype, or with a prototype equal the `Object.prototype`. ```javascript console.log(utils.isPlainObject({})) // true console.log(utils.isPlainObject(Object.create(null)); // true console.log(utils.isPlainObject([])) // false console.log(utils.isPlainObject(function(){})) // false function User(){} console.log(utils.isPlainObject(new User)) // false console.log(utils.isPlainObject(Object.create({propertyInProto: 1}))) // false ``` exports.isPlainObject = (param) -> unless isObject(param) return false proto = getPrototypeOf param # comes from Object.create unless proto return true # one-proto object if (proto is Object::) and not getPrototypeOf(proto) return true false ## *Boolean* utils.isArguments(*Any* value) Returns `true` if the given value is an arguments object. ```javascript (function(){ console.log(utils.isArguments(arguments)) // true })(); console.log(utils.isArguments({})) // false ``` exports.isArguments = (param) -> toString.call(param) is '[object Arguments]' ## *NotPrimitive* utils.merge(*NotPrimitive* source, *NotPrimitive* object) Overrides the given source object properties by the given object own properties. The source object is returned. ```javascript var config = {a: 1, b: 2}; utils.merge(config, {b: 99, d: 100}); console.log(config); // {a: 1, b: 99, d: 100} ``` merge = exports.merge = (source, obj) -> null `//<development>` if isPrimitive(source) throw new Error 'utils.merge source cannot be primitive' if isPrimitive(obj) throw new Error 'utils.merge object cannot be primitive' if source is obj throw new Error 'utils.merge source and object are the same' if arguments.length > 2 throw new Error 'utils.merge expects only two arguments; ' + 'use utils.mergeAll instead' `//</development>` for key, value of obj when obj.hasOwnProperty(key) source[key] = value source ## *NotPrimitive* utils.mergeAll(*NotPrimitive* source, *NotPrimitive* objects...) Like the utils.merge(), but the amount of objects to merge is unknown. ```javascript var config = {a: 1}; utils.merge(config, {b: 2}, {c: 3}); console.log(config); // {a: 1, b: 2, c: 3} ``` exports.mergeAll = (source) -> null `//<development>` if isPrimitive(source) throw new Error 'utils.merge source cannot be primitive' `//</development>` for i in [1...arguments.length] by 1 if (obj = arguments[i])? `//<development>` if isPrimitive(obj) throw new Error 'utils.mergeAll object cannot be primitive' if source is obj throw new Error 'utils.mergeAll source and object are the same' `//</development>` for key, value of obj when obj.hasOwnProperty(key) source[key] = value source ## *NotPrimitive* utils.mergeDeep(*NotPrimitive* source, *NotPrimitive* object) Overrides the given source object properties and all its objects by the given object own properties. The source object is returned. ```javascript var user = { name: 'test', carsByName: { tiny: 'Ferrharhi', monkey: 'BMM' } } utils.mergeDeep(user, { name: 'Johny', carsByName: { nextCar: 'Fita' } }); console.log(user); // {name: 'Johny', carsByName: {tiny: 'Ferrharhi', monkey: 'BMM', nextCar: 'Fita'}} ``` mergeDeep = exports.mergeDeep = (source, obj) -> null `//<development>` if isPrimitive(source) throw new Error 'utils.mergeDeep source cannot be primitive' if isPrimitive(obj) throw new Error 'utils.mergeDeep object cannot be primitive' if source is obj throw new Error 'utils.mergeDeep source and object are the same' `//</development>` for key, value of obj when hasOwnProp.call obj, key sourceValue = source[key] if value and typeof value is 'object' and not isArray(value) and sourceValue and typeof sourceValue is 'object' and not isArray(sourceValue) mergeDeep sourceValue, value continue source[key] = value source ## *NotPrimitive* utils.fill(*NotPrimitive* source, *NotPrimitive* object) Sets the given object properties into the given source object if the property exists in the given source, but it's not defined as an own property. The source object is returned. ```javascript function User(){ } User.prototype.name = ''; var user = new User; utils.fill(user, {name: 'Johny', age: 40}); console.log(user); // {name: 'Johny'} ``` exports.fill = (source, obj) -> null `//<development>` if isPrimitive(source) throw new Error 'utils.fill source cannot be primitive' if isPrimitive(obj) throw new Error 'utils.fill object cannot be primitive' if source is obj throw new Error 'utils.fill source and object are the same' `//</development>` for key, value of obj when hasOwnProp.call(obj, key) if key of source and not hasOwnProp.call(source, key) source[key] = value source ## utils.remove(*NotPrimitive* object, *Any* element) Removes an array element or an object property from the given object. ```javascript var array = ['a', 'b', 'c']; utils.remove(array, 'b'); console.log(array); // ['a', 'c'] var object = {a: 1, b: 2}; utils.remove(object, 'a'); console.log(object); // {b: 2} ``` exports.remove = (obj, elem) -> null `//<development>` if isPrimitive(obj) throw new Error 'utils.remove object cannot be primitive' `//</development>` if isArray(obj) index = obj.indexOf elem if index isnt -1 if index is 0 obj.shift() else if index is obj.length - 1 obj.pop() else obj.splice index, 1 else delete obj[elem] return ## utils.removeFromUnorderedArray(*Array* array, *Any* element) Removes the given element from the given array. Elements order may be changed. exports.removeFromUnorderedArray = (arr, elem) -> null `//<development>` unless Array.isArray(arr) throw new Error 'utils.removeFromUnorderedArray array must be an Array' `//</development>` index = arr.indexOf elem if index isnt -1 arr[index] = arr[arr.length - 1] arr.pop() return ## *Object* utils.getPropertyDescriptor(*NotPrimitive* object, *String* property) Returns the descriptor of the given property defined in the given object. ```javascript function User(){ this.age = 0; } utils.defineProperty(User.prototype, 'isAdult', utils.CONFIGURABLE, function(){ return this.age >= 18; }, null); var user = new User; console.log(utils.getPropertyDescriptor(user, 'isAdult')); // {enumerable: false, configurable: true, get: ..., set: undefined} ``` exports.getPropertyDescriptor = (obj, prop) -> null `//<development>` if isPrimitive(obj) throw new Error 'utils.getPropertyDescriptor object cannot be primitive' if typeof prop isnt 'string' throw new Error 'utils.getPropertyDescriptor property must be a string' `//</development>` while obj and not desc desc = getObjOwnPropDesc obj, prop obj = getPrototypeOf obj desc ## *Function* utils.lookupGetter(*NotPrimitive* object, *String* property) Returns the given property getter function defined in the given object. ```javascript var object = {loaded: 2, length: 5}; utils.defineProperty(object, 'progress', null, function(){ return this.loaded / this.length; }, null); console.log(utils.lookupGetter(object, 'progress')); // function(){ return this.loaded / this.length; } ``` exports.lookupGetter = do -> # use native function if possible if Object::__lookupGetter__ {lookupGetter} = Object:: (obj, prop) -> getter = lookupGetter.call(obj, prop) getter?.trueGetter or getter # use polyfill (obj, prop) -> if desc = exports.getPropertyDescriptor(obj, prop) desc.get?.trueGetter or desc.get ## *Function* utils.lookupSetter(*NotPrimitive* object, *String* property) Returns the given property setter function defined in the given object. exports.lookupSetter = do -> # use native function if possible if Object::__lookupSetter__ return Function.call.bind Object::__lookupSetter__ # use polyfill (obj, prop) -> desc = exports.getPropertyDescriptor obj, prop desc?.set ## *NotPrimitive* utils.defineProperty(*NotPrimitive* object, *String* property, *Integer* descriptors, [*Any* value, *Function* setter]) Defines the given property in the given object. The descriptors argument is a bitmask accepting `utils.WRITABLE`, `utils.ENUMERABLE` and `utils.CONFIGURABLE`. The value argument becomes a getter function if the given setter is not an undefined. ```javascript var object = {}; var desc = utils.ENUMERABLE | utils.WRITABLE | utils.CONFIGURABLE; utils.defineProperty(object, 'name', desc, 'Emmy'); console.log(object.name); // Emmy utils.defineProperty(object, 'const', utils.ENUMERABLE, 'constantValue'); console.log(object.const); // constantValue utils.defineProperty(object, 'length', utils.ENUMERABLE | utils.CONFIGURABLE, function(){ return 2; }, null); console.log(object.length); // 2 ``` defObjProp exports, 'WRITABLE', value: 1<<0 defObjProp exports, 'ENUMERABLE', value: 1<<1 defObjProp exports, 'CONFIGURABLE', value: 1<<2 exports.defineProperty = do -> {WRITABLE, ENUMERABLE, CONFIGURABLE} = exports descCfg = enumerable: true, configurable: true valueCfg = exports.merge writable: true, value: null, descCfg accessorsCfg = exports.merge get: undefined, set: undefined, descCfg # thanks to http://stackoverflow.com/a/23522755/2021829 isSafari = if navigator? ///^((?!chrome).)*safari///i.test(navigator.userAgent) else false (obj, prop, desc, getter, setter) -> null `//<development>` if isPrimitive(obj) throw new Error 'utils.defineProperty object cannot be primitive' if typeof prop isnt 'string' throw new Error 'utils.defineProperty property must be a string' if desc? and (not exports.isInteger(desc) or desc < 0) throw new Error 'utils.defineProperty descriptors bitmask ' + 'must be a positive integer' `//</development>` # configure value if setter is undefined cfg = valueCfg valueCfg.value = getter valueCfg.writable = desc & WRITABLE # configure accessors else # HACK: safari bug # https://bugs.webkit.org/show_bug.cgi?id=132872 if isSafari and getter _getter = getter getter = -> if @ isnt obj and @hasOwnProperty(prop) @[prop] else _getter.call @ cfg = accessorsCfg accessorsCfg.get = if typeof getter is 'function' then getter else undefined accessorsCfg.set = if typeof setter is 'function' then setter else undefined # set common config cfg.enumerable = desc & ENUMERABLE cfg.configurable = desc & CONFIGURABLE # set property defObjProp obj, prop, cfg obj ## *NotPrimitive* utils.overrideProperty(*NotPrimitive* object, *String* property, [*Any* value, *Function* setter]) exports.overrideProperty = (obj, prop, getter, setter) -> unless desc = exports.getPropertyDescriptor(obj, prop) throw new Error 'utils.overrideProperty object ' + 'must has the given property' unless desc.configurable throw new Error 'utils.overrideProperty the given property ' + 'is not configurable' # get bitmask descriptors opts = exports.CONFIGURABLE if desc.writable opts |= exports.WRITABLE if desc.enumerable opts |= exports.ENUMERABLE # get values if getter isnt undefined and setter isnt undefined if desc.get? if typeof getter is 'function' getter = getter desc.get else getter = desc.get if desc.set? if typeof setter is 'function' setter = setter desc.set else setter = desc.set else if typeof getter is typeof desc.value is 'function' getter = getter desc.value exports.defineProperty obj, prop, opts, getter, setter ## *Any* utils.clone(*Any* param) Returns clone of the given array or object. ```javascript console.log(utils.clone([1, 2])) // [1, 2] console.log(utils.clone({a: 1})) // {a: 1} ``` clone = exports.clone = (param) -> if isArray(param) return param.slice() if isObject(param) proto = getPrototypeOf param if proto is Object:: result = {} else result = createObject proto for key in objKeys param result[key] = param[key] return result param ## *Any* utils.cloneDeep(*Any* param) Returns deep clone of the given array or object. ```javascript var obj2 = {ba: 1}; var obj = {a: 1, b: obj2}; var clonedObj = utils.cloneDeep(obj); console.log(clonedObj); // {a: 1, b: {ba: 1}} console.log(clonedObj.b === obj.b) // false ``` cloneDeep = exports.cloneDeep = (param) -> result = clone param if isObject result for key in objKeys result result[key] = cloneDeep result[key] result ## *Boolean* utils.isEmpty(*String*|*NotPrimitive* object) Returns `true` if the given array has no elements, of the given object has no own properties. ```javascript console.log(utils.isEmpty([])); // true console.log(utils.isEmpty([1, 2])); // false console.log(utils.isEmpty({})); // true console.log(utils.isEmpty({a: 1})); // false console.log(utils.isEmpty('')); // true ``` exports.isEmpty = (object) -> if typeof object is 'string' return object is '' `//<development>` if isPrimitive(object) throw new Error 'utils.isEmpty object must be a string or ' + 'not primitive' `//</development>` if isArray(object) return not object.length else for key of object return false return true ## *Any* utils.last(*NotPrimitive* array) Returns the last element of the given array, or an array-like object. ```javascript console.log(utils.last(['a', 'b'])) // b console.log(utils.last([])) // undefined ``` exports.last = (arg) -> null `//<development>` if isPrimitive(arg) throw new Error 'utils.last array cannot be primitive' `//</development>` arg[arg.length - 1] ## *NotPrimitive* utils.clear(*NotPrimitive* object) Removes all elements from the given array, or all own properties from the given object. ```javascript var arr = ['a', 'b']; utils.clear(arr); console.log(arr); // [] var obj = {age: 37}; utils.clear(obj); console.log(obj); // {} ``` exports.clear = (obj) -> null `//<development>` if isPrimitive(obj) throw new Error 'utils.clear object cannot be primitive' `//</development>` if isArray obj obj.pop() for _ in [0...obj.length] by 1 else delete obj[key] for key in objKeys obj obj ## *Object* utils.setPrototypeOf(*NotPrimitive* object, *NotPrimitive*|*Null* prototype) Changes the given object prototype into the given prototype. **This function on some environments returns a new object.** ```javascript var obj = {a: 1}; var prototype = {b: 100}; var newObj = utils.setPrototypeOf(obj, prototype); console.log(Object.getPrototypeOf(newObj) === prototype) // true console.log(newObj.a) // 1 console.log(newObj.b) // 100 ``` setPrototypeOf = exports.setPrototypeOf = do -> # ES6 `Object.setPrototypeOf()` if typeof Object.setPrototypeOf is 'function' return Object.setPrototypeOf # writable __proto__ tmp = {} tmp.__proto__ = a: 1 if tmp.a is 1 return (obj, proto) -> null `//<development>` if isPrimitive(obj) throw new Error 'utils.setPrototypeOf object ' + 'cannot be primitive' if proto? and isPrimitive(proto) throw new Error 'utils.setPrototypeOf prototype ' + 'cannot be primitive' `//</development>` obj.__proto__ = proto obj # object merging return (obj, proto) -> null `//<development>` if isPrimitive(obj) throw new Error 'utils.setPrototypeOf object ' + 'cannot be primitive' if proto? and isPrimitive(proto) throw new Error 'utils.setPrototypeOf prototype ' + 'cannot be primitive' `//</development>` if typeof obj is 'object' newObj = createObject proto merge newObj, obj else merge obj, proto newObj ## *Boolean* utils.has(*Any* object, *Any* value) Returns `true` if the given array contains the given value. ```javascript console.log(utils.has(['a'], 'a')) // true console.log(utils.has(['a'], 'b')) // false ``` Returns `true` if the given object has an own property names as the given value. ```javascript var object = { city: 'New York' } console.log(utils.has(object, 'New York')) // true ``` Returns `true` if the given string contains the given value. ```javascript console.log(utils.has('abc', 'b')) // true console.log(utils.has('abc', 'e')) // false ``` has = exports.has = (obj, val) -> if typeof obj is 'string' !!~obj.indexOf(val) else `//<development>` if isPrimitive(obj) throw new Error 'utils.has object must be a string or not primitive' `//</development>` if isArray(obj) !!~Array::indexOf.call obj, val else for key, value of obj when hasOwnProp.call(obj, key) if value is val return true false ## *Array* utils.objectToArray(*Object* object, [*Function* valueGen, *Array* target = `[]`]) Translates the given object into an array. Array elements are determined by the given valueGen function. The valueGen function is called with the property name, property value and the given object. By default, the valueGen returns the object property value. Created elements are set into the given target array (a new array by default). ```javascript var object = { type: 'dog', name: 'Bandit' }; console.log(utils.objectToArray(object)); // ['dog', 'Bandit'] console.log(utils.objectToArray(object, function(key, val){ return key + "_" + val; })); // ['type_dog', 'name_Bandit'] ``` exports.objectToArray = (obj, valueGen, target) -> keys = objKeys obj target ?= keys `//<development>` if not isObject(obj) throw new Error 'utils.objectToArray object must be an object' if valueGen? and typeof valueGen isnt 'function' throw new Error 'utils.objectToArray valueGen must be a function' if not isArray(target) throw new Error 'utils.objectToArray target must be an array' `//</development>` for key, i in keys value = if valueGen then valueGen(key, obj[key], obj) else obj[key] target[i] = value target ## *Object* utils.arrayToObject(*Array* array, [*Function* keyGen, *Function* valueGen, *Object* target = `{}`]) Translates the given array into an object. Object keys are determined by the given keyGen function. Object key values are determined by the given valueGen function. The keyGen and valueGen functions are called with the array element index, array element value and the array itself. By default, the keyGen function returns the array element index. By default, the valueGen function returns the array element value. Created proeprties are set into the given target object (a new object by default). ```javascript console.log(utils.arrayToObject(['a', 'b'])) // {0: 'a', 1: 'b'} console.log(utils.arrayToObject(['a'], function(i, elem){ return "value_" + elem; })); // {"value_a": "a"} console.log(utils.arrayToObject(['a'], function(i, elem){ return elem; }, function(i, elem){ return i; })); // {"a": 0} ``` exports.arrayToObject = (arr, keyGen, valueGen, target={}) -> null `//<development>` if not isArray(arr) throw new Error 'utils.arrayToObject array must be an array' if keyGen? and typeof keyGen isnt 'function' throw new Error 'utils.arrayToObject keyGen must be a function' if valueGen? and typeof valueGen isnt 'function' throw new Error 'utils.arrayToObject valueGen must be a function' if not isObject(target) throw new Error 'utils.arrayToObject target must be an object' `//</development>` for elem, i in arr key = if keyGen then keyGen(i, elem, arr) else i value = if valueGen then valueGen(i, elem, arr) else elem if key? target[key] = value target ## *String* utils.capitalize(*String* string) Capitalizes the given string. ```javascript console.log(utils.capitalize('name')) // Name ``` exports.capitalize = (str) -> null `//<development>` if typeof str isnt 'string' throw new Error 'utils.capitalize string must be a string' `//</development>` unless str.length return '' str[0].toUpperCase() + str.slice(1) ## *String* utils.addSlashes(*String* string) Adds backslashes before each `'` and `"` characters found in the given string. ```javascript console.log(utils.addSlashes('a"b')) // a\"b ``` exports.addSlashes = do -> SLASHES_RE = ///'|"///g NEW_SUB_STR = '\\$\&' (str) -> null `//<development>` if typeof str isnt 'string' throw new Error 'utils.addSlashes string must be a string' `//</development>` unless str.length return str str.replace SLASHES_RE, NEW_SUB_STR ## *String* utils.uid([*Integer* length = `8`]) Returns pseudo-unique string with the given length. This function doesn't quarantee uniqueness of the returned data. ```javascript console.log(utils.uid()) // "50" ``` exports.uid = (n = 8) -> null `//<development>` if typeof n isnt 'number' or n <= 0 or not isFinite(n) throw new Error 'utils.uid length must be a positive finite number' `//</development>` str = '' loop str += random().toString(16).slice 2 if str.length >= n then break if str.length isnt n str = str.slice 0, n str ## *Any* utils.tryFunction(*Function* function, [*Any* context, *Array* arguments, *Any* onFail]) Calls the given function with the given context and arguments. If the function throws an error, the given onFail value is returned. If the given onFail is a function, it will be called with the caught error. ```javascript function test(size){ if (size === 0){ throw "Wrong size!"; } } console.log(utils.tryFunction(test, null, [0])) // undefined console.log(utils.tryFunction(test, null, [0], 'ERROR!')) // ERROR! console.log(utils.tryFunction(test, null, [100], 'ERROR!')) // undefined ``` exports.tryFunction = (func, context, args, onFail) -> null `//<development>` if typeof func isnt 'function' throw new Error 'utils.tryFunction function must be a function' if args? and not isObject(args) throw new Error 'utils.tryFunction arguments must be an object' `//</development>` try func.apply context, args catch err if typeof onFail is 'function' onFail(err) else if onFail is undefined err else onFail ## *Any* utils.catchError(*Function* function, [*Any* context, *Array* arguments]) Calls the given function with the given context and arguments. Returns caught error. ```javascript function test(size){ if (size === 0){ throw "Wrong size!"; } } console.log(utils.catchError(test, null, [0])) // "Wrong size!" console.log(utils.catchError(test, null, [100])) // null ``` exports.catchError = (func, context, args) -> null `//<development>` if typeof func isnt 'function' throw new Error 'utils.catchError function must be a function' if args? and not isObject(args) throw new Error 'utils.catchError arguments must be an object' `//</development>` try func.apply context, args return catch err err ## *Function* utils.bindFunctionContext(*Function* function, *Any* context) Returns a new function calling the given function with the given context and arguments in an amount lower or equal the function length. ```javascript function func(arg1){ console.log(this, arg1); } var bindFunc = utils.bindFunctionContext(func, {ctx: 1}); console.log(bindFunc('a')); // {ctx: 1} "a" ``` exports.bindFunctionContext = (func, ctx) -> null `//<development>` if typeof func isnt 'function' throw new Error 'utils.bindFunctionContext function must be a function' `//</development>` switch func.length when 0 -> func.call ctx when 1 (a1) -> func.call ctx, a1 when 2 (a1, a2) -> func.call ctx, a1, a2 when 3 (a1, a2, a3) -> func.call ctx, a1, a2, a3 when 4 (a1, a2, a3, a4) -> func.call ctx, a1, a2, a3, a4 when 5 (a1, a2, a3, a4, a5) -> func.call ctx, a1, a2, a3, a4, a5 when 6 (a1, a2, a3, a4, a5, a6) -> func.call ctx, a1, a2, a3, a4, a5, a6 when 7 (a1, a2, a3, a4, a5, a6, a7) -> func.call ctx, a1, a2, a3, a4, a5, a6, a7 else -> func.apply ctx, arguments ## *Object* utils.errorToObject(*Error* error) Returns a plain object with the given error name, message and other custom properties. Standard error `name` and `message` properties are not enumerable. ```javascript var error = new ReferenceError('error message!'); console.log(utils.errorToObject(error)); // {name: 'ReferenceError', message: 'error message!'} ``` exports.errorToObject = (error) -> null `//<development>` unless error instanceof Error throw new Error 'utils.errorToObject error must be an Error instance' `//</development>` result = name: error.name message: error.message # support custom properties exports.merge result, error result ## *Object* utils.getOwnProperties(*Object* object) Returns an array or an object with own properties associated in the given object. exports.getOwnProperties = (obj) -> null `//<development>` if not isObject(obj) throw new Error 'utils.getOwnProperties object must be an object' `//</development>` result = if isArray obj then [] else {} merge result, obj result ## *Boolean* utils.isEqual(*Object* object1, *Object* object2, [*Function* compareFunction, *Integer* maxDeep = `Infinity`]) Returns `true` if the given objects have equal values. The given compareFunction is used to compare two values (which at least one them is primitive). By default the compareFunction uses triple comparison (`===`). ```javascript utils.isEqual([1, 0], [1, 0]) // true utils.isEqual({a: 1}, {a: 1}) // true utils.isEqual({a: {aa: 1}}, {a: {aa: 1}}) // true utils.isEqual([0, 1], [1, 0]) // false utils.isEqual({a: {aa: 1}}, {a: {aa: 1, ab: 2}}) // false ``` isEqual = exports.isEqual = do -> defaultComparison = (a, b) -> a is b forArrays = (a, b, compareFunc, maxDeep) -> # prototypes are the same if getPrototypeOf(a) isnt getPrototypeOf(b) return false # length is the same if a.length isnt b.length return false # values are the same if maxDeep <= 0 return true for aValue, index in a bValue = b[index] if bValue and typeof bValue is 'object' unless isEqual(aValue, bValue, compareFunc, maxDeep - 1) return false continue unless compareFunc(aValue, bValue) return false true forObjects = (a, b, compareFunc, maxDeep) -> # prototypes are the same if getPrototypeOf(a) isnt getPrototypeOf(b) return false # whether keys are the same for key, value of a when a.hasOwnProperty(key) unless b.hasOwnProperty(key) return false for key, value of b when b.hasOwnProperty(key) unless a.hasOwnProperty(key) return false # whether values are equal if maxDeep <= 0 return true for key, value of a when a.hasOwnProperty(key) if value and typeof value is 'object' unless isEqual(value, b[key], compareFunc, maxDeep - 1) return false continue unless compareFunc(value, b[key]) return false true (a, b, compareFunc = defaultComparison, maxDeep = Infinity) -> if typeof compareFunc is 'number' maxDeep = compareFunc compareFunc = defaultComparison `//<development>` if typeof compareFunc isnt 'function' throw new Error 'utils.isEqual compareFunction must be a function' if typeof maxDeep isnt 'number' throw new Error 'utils.isEqual maxDeep must be a number' `//</development>` if maxDeep < 0 return compareFunc a, b if isArray(a) and isArray(b) forArrays a, b, compareFunc, maxDeep else if isObject(a) and isObject(b) forObjects a, b, compareFunc, maxDeep else return compareFunc a, b