123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373 |
- import { isBigNumber } from './is.js';
- /**
- * 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
- */
- export function clone(x) {
- var 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 mapObject(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
- */
- export function mapObject(object, callback) {
- var clone = {};
- for (var key in object) {
- if (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
- */
- export function extend(a, b) {
- for (var prop in b) {
- if (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}
- */
- export 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 (var prop in b) {
- // We check against prop not being in Object.prototype or Function.prototype
- // to prevent polluting for example Object.__proto__.
- if (hasOwnProperty(b, prop) && !(prop in Object.prototype) && !(prop in Function.prototype)) {
- if (b[prop] && b[prop].constructor === Object) {
- if (a[prop] === undefined) {
- a[prop] = {};
- }
- if (a[prop] && 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.
- * Compares values and functions strictly (ie. 2 is not the same as '2').
- * @param {Array | Object} a
- * @param {Array | Object} b
- * @returns {boolean}
- */
- export function deepStrictEqual(a, b) {
- var 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 (!deepStrictEqual(a[i], b[i])) {
- return false;
- }
- }
- return true;
- } else if (typeof a === 'function') {
- return a === b;
- } else if (a instanceof Object) {
- if (Array.isArray(b) || !(b instanceof Object)) {
- return false;
- }
- for (prop in a) {
- // noinspection JSUnfilteredForInLoop
- if (!(prop in b) || !deepStrictEqual(a[prop], b[prop])) {
- return false;
- }
- }
- for (prop in b) {
- // noinspection JSUnfilteredForInLoop
- if (!(prop in a)) {
- return false;
- }
- }
- return true;
- } else {
- return a === b;
- }
- }
- /**
- * Recursively flatten a nested object.
- * @param {Object} nestedObject
- * @return {Object} Returns the flattened object
- */
- export function deepFlatten(nestedObject) {
- var flattenedObject = {};
- _deepFlatten(nestedObject, flattenedObject);
- return flattenedObject;
- }
- // helper function used by deepFlatten
- function _deepFlatten(nestedObject, flattenedObject) {
- for (var prop in nestedObject) {
- if (hasOwnProperty(nestedObject, prop)) {
- var value = nestedObject[prop];
- if (typeof value === 'object' && value !== null) {
- _deepFlatten(value, flattenedObject);
- } else {
- flattenedObject[prop] = value;
- }
- }
- }
- }
- /**
- * Test whether the current JavaScript engine supports Object.defineProperty
- * @returns {boolean} returns true if supported
- */
- export function canDefineProperty() {
- // test needed for broken IE8 implementation
- try {
- if (Object.defineProperty) {
- Object.defineProperty({}, 'x', {
- get: function get() {
- return null;
- }
- });
- 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.
- *
- * @param {Object} object Object where to add the property
- * @param {string} prop Property name
- * @param {Function} valueResolver Function returning the property value. Called
- * without arguments.
- */
- export function lazy(object, prop, valueResolver) {
- var _uninitialized = true;
- var _value;
- Object.defineProperty(object, prop, {
- get: function get() {
- if (_uninitialized) {
- _value = valueResolver();
- _uninitialized = false;
- }
- return _value;
- },
- set: function set(value) {
- _value = value;
- _uninitialized = false;
- },
- configurable: true,
- enumerable: true
- });
- }
- /**
- * Traverse a path into an object.
- * When a namespace is missing, it will be created
- * @param {Object} object
- * @param {string | string[]} path A dot separated string like 'name.space'
- * @return {Object} Returns the object at the end of the path
- */
- export function traverse(object, path) {
- if (path && typeof path === 'string') {
- return traverse(object, path.split('.'));
- }
- var obj = object;
- if (path) {
- for (var i = 0; i < path.length; i++) {
- var key = path[i];
- if (!(key in obj)) {
- obj[key] = {};
- }
- obj = obj[key];
- }
- }
- return obj;
- }
- /**
- * A safe hasOwnProperty
- * @param {Object} object
- * @param {string} property
- */
- export function hasOwnProperty(object, property) {
- return object && Object.hasOwnProperty.call(object, property);
- }
- /**
- * Test whether an object is a factory. a factory has fields:
- *
- * - factory: function (type: Object, config: Object, load: function, typed: function [, math: Object]) (required)
- * - name: string (optional)
- * - path: string A dot separated path (optional)
- * - math: boolean If true (false by default), the math namespace is passed
- * as fifth argument of the factory function
- *
- * @param {*} object
- * @returns {boolean}
- */
- export function isLegacyFactory(object) {
- return object && typeof object.factory === 'function';
- }
- /**
- * Get a nested property from an object
- * @param {Object} object
- * @param {string | string[]} path
- * @returns {Object}
- */
- export function get(object, path) {
- if (typeof path === 'string') {
- if (isPath(path)) {
- return get(object, path.split('.'));
- } else {
- return object[path];
- }
- }
- var child = object;
- for (var i = 0; i < path.length; i++) {
- var key = path[i];
- child = child ? child[key] : undefined;
- }
- return child;
- }
- /**
- * Set a nested property in an object
- * Mutates the object itself
- * If the path doesn't exist, it will be created
- * @param {Object} object
- * @param {string | string[]} path
- * @param {*} value
- * @returns {Object}
- */
- export function set(object, path, value) {
- if (typeof path === 'string') {
- if (isPath(path)) {
- return set(object, path.split('.'), value);
- } else {
- object[path] = value;
- return object;
- }
- }
- var child = object;
- for (var i = 0; i < path.length - 1; i++) {
- var key = path[i];
- if (child[key] === undefined) {
- child[key] = {};
- }
- child = child[key];
- }
- if (path.length > 0) {
- var lastKey = path[path.length - 1];
- child[lastKey] = value;
- }
- return object;
- }
- /**
- * Create an object composed of the picked object properties
- * @param {Object} object
- * @param {string[]} properties
- * @param {function} [transform] Optional value to transform a value when picking it
- * @return {Object}
- */
- export function pick(object, properties, transform) {
- var copy = {};
- for (var i = 0; i < properties.length; i++) {
- var key = properties[i];
- var value = get(object, key);
- if (value !== undefined) {
- set(copy, key, transform ? transform(value, key) : value);
- }
- }
- return copy;
- }
- /**
- * Shallow version of pick, creating an object composed of the picked object properties
- * but not for nested properties
- * @param {Object} object
- * @param {string[]} properties
- * @return {Object}
- */
- export function pickShallow(object, properties) {
- var copy = {};
- for (var i = 0; i < properties.length; i++) {
- var key = properties[i];
- var value = object[key];
- if (value !== undefined) {
- copy[key] = value;
- }
- }
- return copy;
- }
- export function values(object) {
- return Object.keys(object).map(key => object[key]);
- }
- // helper function to test whether a string contains a path like 'user.name'
- function isPath(str) {
- return str.indexOf('.') !== -1;
- }
|