simple-squiggle

A restricted subset of Squiggle
Log | Files | Refs | README

object.js (10188B)


      1 "use strict";
      2 
      3 var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
      4 
      5 Object.defineProperty(exports, "__esModule", {
      6   value: true
      7 });
      8 exports.canDefineProperty = canDefineProperty;
      9 exports.clone = clone;
     10 exports.deepExtend = deepExtend;
     11 exports.deepFlatten = deepFlatten;
     12 exports.deepStrictEqual = deepStrictEqual;
     13 exports.extend = extend;
     14 exports.get = get;
     15 exports.hasOwnProperty = hasOwnProperty;
     16 exports.isLegacyFactory = isLegacyFactory;
     17 exports.lazy = lazy;
     18 exports.mapObject = mapObject;
     19 exports.pick = pick;
     20 exports.pickShallow = pickShallow;
     21 exports.set = set;
     22 exports.traverse = traverse;
     23 exports.values = values;
     24 
     25 var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
     26 
     27 var _is = require("./is.js");
     28 
     29 /**
     30  * Clone an object
     31  *
     32  *     clone(x)
     33  *
     34  * Can clone any primitive type, array, and object.
     35  * If x has a function clone, this function will be invoked to clone the object.
     36  *
     37  * @param {*} x
     38  * @return {*} clone
     39  */
     40 function clone(x) {
     41   var type = (0, _typeof2.default)(x); // immutable primitive types
     42 
     43   if (type === 'number' || type === 'string' || type === 'boolean' || x === null || x === undefined) {
     44     return x;
     45   } // use clone function of the object when available
     46 
     47 
     48   if (typeof x.clone === 'function') {
     49     return x.clone();
     50   } // array
     51 
     52 
     53   if (Array.isArray(x)) {
     54     return x.map(function (value) {
     55       return clone(value);
     56     });
     57   }
     58 
     59   if (x instanceof Date) return new Date(x.valueOf());
     60   if ((0, _is.isBigNumber)(x)) return x; // bignumbers are immutable
     61 
     62   if (x instanceof RegExp) throw new TypeError('Cannot clone ' + x); // TODO: clone a RegExp
     63   // object
     64 
     65   return mapObject(x, clone);
     66 }
     67 /**
     68  * Apply map to all properties of an object
     69  * @param {Object} object
     70  * @param {function} callback
     71  * @return {Object} Returns a copy of the object with mapped properties
     72  */
     73 
     74 
     75 function mapObject(object, callback) {
     76   var clone = {};
     77 
     78   for (var key in object) {
     79     if (hasOwnProperty(object, key)) {
     80       clone[key] = callback(object[key]);
     81     }
     82   }
     83 
     84   return clone;
     85 }
     86 /**
     87  * Extend object a with the properties of object b
     88  * @param {Object} a
     89  * @param {Object} b
     90  * @return {Object} a
     91  */
     92 
     93 
     94 function extend(a, b) {
     95   for (var prop in b) {
     96     if (hasOwnProperty(b, prop)) {
     97       a[prop] = b[prop];
     98     }
     99   }
    100 
    101   return a;
    102 }
    103 /**
    104  * Deep extend an object a with the properties of object b
    105  * @param {Object} a
    106  * @param {Object} b
    107  * @returns {Object}
    108  */
    109 
    110 
    111 function deepExtend(a, b) {
    112   // TODO: add support for Arrays to deepExtend
    113   if (Array.isArray(b)) {
    114     throw new TypeError('Arrays are not supported by deepExtend');
    115   }
    116 
    117   for (var prop in b) {
    118     // We check against prop not being in Object.prototype or Function.prototype
    119     // to prevent polluting for example Object.__proto__.
    120     if (hasOwnProperty(b, prop) && !(prop in Object.prototype) && !(prop in Function.prototype)) {
    121       if (b[prop] && b[prop].constructor === Object) {
    122         if (a[prop] === undefined) {
    123           a[prop] = {};
    124         }
    125 
    126         if (a[prop] && a[prop].constructor === Object) {
    127           deepExtend(a[prop], b[prop]);
    128         } else {
    129           a[prop] = b[prop];
    130         }
    131       } else if (Array.isArray(b[prop])) {
    132         throw new TypeError('Arrays are not supported by deepExtend');
    133       } else {
    134         a[prop] = b[prop];
    135       }
    136     }
    137   }
    138 
    139   return a;
    140 }
    141 /**
    142  * Deep test equality of all fields in two pairs of arrays or objects.
    143  * Compares values and functions strictly (ie. 2 is not the same as '2').
    144  * @param {Array | Object} a
    145  * @param {Array | Object} b
    146  * @returns {boolean}
    147  */
    148 
    149 
    150 function deepStrictEqual(a, b) {
    151   var prop, i, len;
    152 
    153   if (Array.isArray(a)) {
    154     if (!Array.isArray(b)) {
    155       return false;
    156     }
    157 
    158     if (a.length !== b.length) {
    159       return false;
    160     }
    161 
    162     for (i = 0, len = a.length; i < len; i++) {
    163       if (!deepStrictEqual(a[i], b[i])) {
    164         return false;
    165       }
    166     }
    167 
    168     return true;
    169   } else if (typeof a === 'function') {
    170     return a === b;
    171   } else if (a instanceof Object) {
    172     if (Array.isArray(b) || !(b instanceof Object)) {
    173       return false;
    174     }
    175 
    176     for (prop in a) {
    177       // noinspection JSUnfilteredForInLoop
    178       if (!(prop in b) || !deepStrictEqual(a[prop], b[prop])) {
    179         return false;
    180       }
    181     }
    182 
    183     for (prop in b) {
    184       // noinspection JSUnfilteredForInLoop
    185       if (!(prop in a)) {
    186         return false;
    187       }
    188     }
    189 
    190     return true;
    191   } else {
    192     return a === b;
    193   }
    194 }
    195 /**
    196  * Recursively flatten a nested object.
    197  * @param {Object} nestedObject
    198  * @return {Object} Returns the flattened object
    199  */
    200 
    201 
    202 function deepFlatten(nestedObject) {
    203   var flattenedObject = {};
    204 
    205   _deepFlatten(nestedObject, flattenedObject);
    206 
    207   return flattenedObject;
    208 } // helper function used by deepFlatten
    209 
    210 
    211 function _deepFlatten(nestedObject, flattenedObject) {
    212   for (var prop in nestedObject) {
    213     if (hasOwnProperty(nestedObject, prop)) {
    214       var value = nestedObject[prop];
    215 
    216       if ((0, _typeof2.default)(value) === 'object' && value !== null) {
    217         _deepFlatten(value, flattenedObject);
    218       } else {
    219         flattenedObject[prop] = value;
    220       }
    221     }
    222   }
    223 }
    224 /**
    225  * Test whether the current JavaScript engine supports Object.defineProperty
    226  * @returns {boolean} returns true if supported
    227  */
    228 
    229 
    230 function canDefineProperty() {
    231   // test needed for broken IE8 implementation
    232   try {
    233     if (Object.defineProperty) {
    234       Object.defineProperty({}, 'x', {
    235         get: function get() {}
    236       });
    237       return true;
    238     }
    239   } catch (e) {}
    240 
    241   return false;
    242 }
    243 /**
    244  * Attach a lazy loading property to a constant.
    245  * The given function `fn` is called once when the property is first requested.
    246  *
    247  * @param {Object} object         Object where to add the property
    248  * @param {string} prop           Property name
    249  * @param {Function} valueResolver Function returning the property value. Called
    250  *                                without arguments.
    251  */
    252 
    253 
    254 function lazy(object, prop, valueResolver) {
    255   var _uninitialized = true;
    256 
    257   var _value;
    258 
    259   Object.defineProperty(object, prop, {
    260     get: function get() {
    261       if (_uninitialized) {
    262         _value = valueResolver();
    263         _uninitialized = false;
    264       }
    265 
    266       return _value;
    267     },
    268     set: function set(value) {
    269       _value = value;
    270       _uninitialized = false;
    271     },
    272     configurable: true,
    273     enumerable: true
    274   });
    275 }
    276 /**
    277  * Traverse a path into an object.
    278  * When a namespace is missing, it will be created
    279  * @param {Object} object
    280  * @param {string | string[]} path   A dot separated string like 'name.space'
    281  * @return {Object} Returns the object at the end of the path
    282  */
    283 
    284 
    285 function traverse(object, path) {
    286   if (path && typeof path === 'string') {
    287     return traverse(object, path.split('.'));
    288   }
    289 
    290   var obj = object;
    291 
    292   if (path) {
    293     for (var i = 0; i < path.length; i++) {
    294       var key = path[i];
    295 
    296       if (!(key in obj)) {
    297         obj[key] = {};
    298       }
    299 
    300       obj = obj[key];
    301     }
    302   }
    303 
    304   return obj;
    305 }
    306 /**
    307  * A safe hasOwnProperty
    308  * @param {Object} object
    309  * @param {string} property
    310  */
    311 
    312 
    313 function hasOwnProperty(object, property) {
    314   return object && Object.hasOwnProperty.call(object, property);
    315 }
    316 /**
    317  * Test whether an object is a factory. a factory has fields:
    318  *
    319  * - factory: function (type: Object, config: Object, load: function, typed: function [, math: Object])   (required)
    320  * - name: string (optional)
    321  * - path: string    A dot separated path (optional)
    322  * - math: boolean   If true (false by default), the math namespace is passed
    323  *                   as fifth argument of the factory function
    324  *
    325  * @param {*} object
    326  * @returns {boolean}
    327  */
    328 
    329 
    330 function isLegacyFactory(object) {
    331   return object && typeof object.factory === 'function';
    332 }
    333 /**
    334  * Get a nested property from an object
    335  * @param {Object} object
    336  * @param {string | string[]} path
    337  * @returns {Object}
    338  */
    339 
    340 
    341 function get(object, path) {
    342   if (typeof path === 'string') {
    343     if (isPath(path)) {
    344       return get(object, path.split('.'));
    345     } else {
    346       return object[path];
    347     }
    348   }
    349 
    350   var child = object;
    351 
    352   for (var i = 0; i < path.length; i++) {
    353     var key = path[i];
    354     child = child ? child[key] : undefined;
    355   }
    356 
    357   return child;
    358 }
    359 /**
    360  * Set a nested property in an object
    361  * Mutates the object itself
    362  * If the path doesn't exist, it will be created
    363  * @param {Object} object
    364  * @param {string | string[]} path
    365  * @param {*} value
    366  * @returns {Object}
    367  */
    368 
    369 
    370 function set(object, path, value) {
    371   if (typeof path === 'string') {
    372     if (isPath(path)) {
    373       return set(object, path.split('.'), value);
    374     } else {
    375       object[path] = value;
    376       return object;
    377     }
    378   }
    379 
    380   var child = object;
    381 
    382   for (var i = 0; i < path.length - 1; i++) {
    383     var key = path[i];
    384 
    385     if (child[key] === undefined) {
    386       child[key] = {};
    387     }
    388 
    389     child = child[key];
    390   }
    391 
    392   if (path.length > 0) {
    393     var lastKey = path[path.length - 1];
    394     child[lastKey] = value;
    395   }
    396 
    397   return object;
    398 }
    399 /**
    400  * Create an object composed of the picked object properties
    401  * @param {Object} object
    402  * @param {string[]} properties
    403  * @param {function} [transform] Optional value to transform a value when picking it
    404  * @return {Object}
    405  */
    406 
    407 
    408 function pick(object, properties, transform) {
    409   var copy = {};
    410 
    411   for (var i = 0; i < properties.length; i++) {
    412     var key = properties[i];
    413     var value = get(object, key);
    414 
    415     if (value !== undefined) {
    416       set(copy, key, transform ? transform(value, key) : value);
    417     }
    418   }
    419 
    420   return copy;
    421 }
    422 /**
    423  * Shallow version of pick, creating an object composed of the picked object properties
    424  * but not for nested properties
    425  * @param {Object} object
    426  * @param {string[]} properties
    427  * @return {Object}
    428  */
    429 
    430 
    431 function pickShallow(object, properties) {
    432   var copy = {};
    433 
    434   for (var i = 0; i < properties.length; i++) {
    435     var key = properties[i];
    436     var value = object[key];
    437 
    438     if (value !== undefined) {
    439       copy[key] = value;
    440     }
    441   }
    442 
    443   return copy;
    444 }
    445 
    446 function values(object) {
    447   return Object.keys(object).map(function (key) {
    448     return object[key];
    449   });
    450 } // helper function to test whether a string contains a path like 'user.name'
    451 
    452 
    453 function isPath(str) {
    454   return str.indexOf('.') !== -1;
    455 }