michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: "use strict"; michael@0: michael@0: module.metadata = { michael@0: "stability": "unstable" michael@0: }; michael@0: michael@0: const { flatten } = require('./array'); michael@0: michael@0: /** michael@0: * Merges all the properties of all arguments into first argument. If two or michael@0: * more argument objects have own properties with the same name, the property michael@0: * is overridden, with precedence from right to left, implying, that properties michael@0: * of the object on the left are overridden by a same named property of the michael@0: * object on the right. michael@0: * michael@0: * Any argument given with "falsy" value - commonly `null` and `undefined` in michael@0: * case of objects - are skipped. michael@0: * michael@0: * @examples michael@0: * var a = { bar: 0, a: 'a' } michael@0: * var b = merge(a, { foo: 'foo', bar: 1 }, { foo: 'bar', name: 'b' }); michael@0: * b === a // true michael@0: * b.a // 'a' michael@0: * b.foo // 'bar' michael@0: * b.bar // 1 michael@0: * b.name // 'b' michael@0: */ michael@0: function merge(source) { michael@0: let descriptor = {}; michael@0: michael@0: // `Boolean` converts the first parameter to a boolean value. Any object is michael@0: // converted to `true` where `null` and `undefined` becames `false`. Therefore michael@0: // the `filter` method will keep only objects that are defined and not null. michael@0: Array.slice(arguments, 1).filter(Boolean).forEach(function onEach(properties) { michael@0: Object.getOwnPropertyNames(properties).forEach(function(name) { michael@0: descriptor[name] = Object.getOwnPropertyDescriptor(properties, name); michael@0: }); michael@0: }); michael@0: return Object.defineProperties(source, descriptor); michael@0: } michael@0: exports.merge = merge; michael@0: michael@0: /** michael@0: * Returns an object that inherits from the first argument and contains all the michael@0: * properties from all following arguments. michael@0: * `extend(source1, source2, source3)` is equivalent of michael@0: * `merge(Object.create(source1), source2, source3)`. michael@0: */ michael@0: function extend(source) { michael@0: let rest = Array.slice(arguments, 1); michael@0: rest.unshift(Object.create(source)); michael@0: return merge.apply(null, rest); michael@0: } michael@0: exports.extend = extend; michael@0: michael@0: function has(obj, key) obj.hasOwnProperty(key); michael@0: exports.has = has; michael@0: michael@0: function each(obj, fn) { michael@0: for (let key in obj) has(obj, key) && fn(obj[key], key, obj); michael@0: } michael@0: exports.each = each; michael@0: michael@0: /** michael@0: * Like `merge`, except no property descriptors are manipulated, for use michael@0: * with platform objects. Identical to underscore's `extend`. Useful for michael@0: * merging XPCOM objects michael@0: */ michael@0: function safeMerge(source) { michael@0: Array.slice(arguments, 1).forEach(function onEach (obj) { michael@0: for (let prop in obj) source[prop] = obj[prop]; michael@0: }); michael@0: return source; michael@0: } michael@0: exports.safeMerge = safeMerge; michael@0: michael@0: /* michael@0: * Returns a copy of the object without blacklisted properties michael@0: */ michael@0: function omit(source, ...values) { michael@0: let copy = {}; michael@0: let keys = flatten(values); michael@0: for (let prop in source) michael@0: if (!~keys.indexOf(prop)) michael@0: copy[prop] = source[prop]; michael@0: return copy; michael@0: } michael@0: exports.omit = omit;