'use strict' const isBigNumber = require('./bignumber/isBigNumber') /** * Clone an object * * clone(x) * * Can clone any primitive type, array, and object. * If x has a function clone, this function will be invoked to clone the object. * * @param {*} x * @return {*} clone */ exports.clone = function clone (x) { const type = typeof x // immutable primitive types if (type === 'number' || type === 'string' || type === 'boolean' || x === null || x === undefined) { return x } // use clone function of the object when available if (typeof x.clone === 'function') { return x.clone() } // array if (Array.isArray(x)) { return x.map(function (value) { return clone(value) }) } if (x instanceof Date) return new Date(x.valueOf()) if (isBigNumber(x)) return x // bignumbers are immutable if (x instanceof RegExp) throw new TypeError('Cannot clone ' + x) // TODO: clone a RegExp // object return exports.map(x, clone) } /** * Apply map to all properties of an object * @param {Object} object * @param {function} callback * @return {Object} Returns a copy of the object with mapped properties */ exports.map = function (object, callback) { const clone = {} for (const key in object) { if (exports.hasOwnProperty(object, key)) { clone[key] = callback(object[key]) } } return clone } /** * Extend object a with the properties of object b * @param {Object} a * @param {Object} b * @return {Object} a */ exports.extend = function (a, b) { for (const prop in b) { if (exports.hasOwnProperty(b, prop)) { a[prop] = b[prop] } } return a } /** * Deep extend an object a with the properties of object b * @param {Object} a * @param {Object} b * @returns {Object} */ exports.deepExtend = function deepExtend (a, b) { // TODO: add support for Arrays to deepExtend if (Array.isArray(b)) { throw new TypeError('Arrays are not supported by deepExtend') } for (const prop in b) { if (exports.hasOwnProperty(b, prop)) { if (b[prop] && b[prop].constructor === Object) { if (a[prop] === undefined) { a[prop] = {} } if (a[prop].constructor === Object) { deepExtend(a[prop], b[prop]) } else { a[prop] = b[prop] } } else if (Array.isArray(b[prop])) { throw new TypeError('Arrays are not supported by deepExtend') } else { a[prop] = b[prop] } } } return a } /** * Deep test equality of all fields in two pairs of arrays or objects. * @param {Array | Object} a * @param {Array | Object} b * @returns {boolean} */ exports.deepEqual = function deepEqual (a, b) { let prop, i, len if (Array.isArray(a)) { if (!Array.isArray(b)) { return false } if (a.length !== b.length) { return false } for (i = 0, len = a.length; i < len; i++) { if (!exports.deepEqual(a[i], b[i])) { return false } } return true } else if (a instanceof Object) { if (Array.isArray(b) || !(b instanceof Object)) { return false } for (prop in a) { // noinspection JSUnfilteredForInLoop if (!exports.deepEqual(a[prop], b[prop])) { return false } } for (prop in b) { // noinspection JSUnfilteredForInLoop if (!exports.deepEqual(a[prop], b[prop])) { return false } } return true } else { return (a === b) } } /** * Test whether the current JavaScript engine supports Object.defineProperty * @returns {boolean} returns true if supported */ exports.canDefineProperty = function () { // test needed for broken IE8 implementation try { if (Object.defineProperty) { Object.defineProperty({}, 'x', { get: function () {} }) return true } } catch (e) {} return false } /** * Attach a lazy loading property to a constant. * The given function `fn` is called once when the property is first requested. * On older browsers (