Source: polyfill/Object.js

// Licensed Materials - Property of IBM
//
// IBM Watson Analytics
//
// (C) Copyright IBM Corp. 2015, 2018
//
// US Government Users Restricted Rights - Use, duplication or
// disclosure restricted by GSA ADP Schedule Contract with IBM Corp.

( function( exports, Object )
{
"use strict";
/*global Symbol*/

var hasOwnProperty = Object.prototype.hasOwnProperty;
var toString = Object.prototype.toString;
 // RegExp set up to match BareJS and polyfill.io symbol strings
 // polyfill uses "__\x01symbol:" and "__\x01symbol@@"
var reSymbol = /^__\d*\x01?[sS]ymbol/;
// By default, make members whose name does not start with _ or $ enumerable.
var reEnumerable = /^[^_\$]/;
var strUndef = "undefined";

var NativeSymbol = typeof Symbol !== strUndef ? Symbol : null;
//jshint -W122
var symIt = NativeSymbol && ( typeof Symbol.iterator === "symbol" ) ? Symbol.iterator : /*istanbul ignore next*/ null;
//jshint +W122

/**
 * Polyfills for {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object Object}.
 * Module that provides implementations for methods missing on Object. Methods that cannot be polyfilled close enough to
 * the spec (since they rely on Native implementations) are not added to the Object global.
 * @class module:barejs/polyfill.Object
 */

 /** @lends module:barejs/polyfill.Object */
 var stat = {};

/**
 * Shortcut method that only defines a property if it is not already known
 * @param {object} _target The object to polyfill
 * @param {object} _members The members to polyfill
 * @param {object} [_copy] Optional: an object that the definitions will be copied to.
 * @param {string} [_ownerName] Optional: name of the owner object
 * @memberof module:barejs/polyfill.Object~
 * @private
 */
function polyMixin( _target, _members, _copy, _ownerName )
{
    for ( var name in _members )
    {
        /*istanbul ignore else: we're working on clean objects, iterating prototype properties is unexpected*/
        if ( _members.hasOwnProperty( name ) )
        {
            var member = _members[name], isFn = typeof member === "function";
            if ( isFn && _ownerName )
                member.displayName = _ownerName + "." + name;
            if ( _copy )
                _copy[name] = member;
            if ( !( name in _target ) )
                exports.defineProperty( _target, name, { enumerable : false, configurable: isFn, writable: isFn, value: member } );
        }
    }
}

/**
 * Helper method that allows to easily apply static and prototype properties to a native object
 * @param {function} _native The native Object constructor to polyfill
 * @param {object} _static The static methods to polyfill. Can be null.
 * @param {object} _proto The instance members to polyfill. Can be null.
 * @param {object} [_copy] Optional: an object that the definitions will be copied to.
 * @param {string} [_ownerName] Optional: the name of the owner object.
 * Allows to add functions to "exports" as well, for unit testing.
 * @memberof module:barejs/polyfill.Object
 * @private
 */
exports.polyfill = function polyfill( _native, _static, _proto, _copy, _ownerName )
{
    if ( _static )
        polyMixin( _native, _static, _copy, _ownerName );
    if ( _proto )
        polyMixin( _native.prototype, _proto, _copy, _ownerName && ( _ownerName + ".prototype" ) );
    return _native;
};

exports.shouldBeEnumerable = function shouldBeEnumerable( _name )
{
    return typeof _name === "string" && reEnumerable.test( _name );
};

/**
 * Utility method to check if _target is an Object
 * @param _arg The argument to check.
 * @returns {boolean} True if the target is an object, false otherwise
 */
function isObject( _arg )
{
    switch ( _arg && typeof _arg )
    {
        case "object":
        case "function":
            return true;

        default:
            return false;
    }
}
exports.isObject = isObject;

/**
 * Utility method to convert target to an Object (according to Ecmascript standard)
 * @param _arg The argument to check.
 * @param _caller The function requesting the cast. If provided, changes the exception message.
 * @returns {object} The argument, as object
 * @throws {TypeError} A TypeError if _arg is null or undefined.
 */
function toObject( _arg, _caller )
{
    switch ( _arg === null ? strUndef : typeof _arg )
    {
        case "undefined":
            throw new TypeError( _caller ? _caller.displayName + " called on null or undefined" : "Cannot convert undefined or null to object" );

        case "object":
        case "function":
            return _arg;

        default:
            return Object( _arg );
    }
}

exports.toObject = toObject;

/**
 * Check if _arg is callable (i.e. a function).
 * @param _arg The argument to check.
 * @returns {boolean} True if _arg is a function, false otherwise.
 * @memberof module:barejs/polyfill.Object
 * @private
 */
exports.isCallable = function isCallable( _arg )
{
    // We can't check the internal [[Call]] property, so we rely on type checking.
    return ( typeof _arg === "function" ) || ( toString.call( _arg ) === "[object Function]" );
};

/**
 * Convenience method to check if _arg is callable (i.e. a function).
 * Note: If a second argument is provided, that is returned instead of _arg.
 * This allows inlining the ensureCallable method in convenient locations.
 * @param _arg function to check
 * @returns _arg, or second argument if present
 * @throws {TypeError} if _arg is not callable.
 * @memberof module:barejs/polyfill.Object
 * @private
 */
exports.ensureCallable = function ensureCallable( _arg )
{
    if ( !exports.isCallable( _arg ) )
        throw new TypeError( _arg + " is not a function" );

    return arguments.length > 1 ? arguments[1] : _arg;
};

/**
 * Helper function that will attempt to set the ES6 iterator Symbol for a class.
 * @param {object} _target The object to define the iterator on.
 * @param {function} _function The function that will result in the iterator.
 * @returns {object} `_target`.
 * @memberof module:barejs/polyfill.Object
 * @private
 */
function setIterator( _target, _function )
{
    var def = { configurable: true, value: _function };
    /*istanbul ignore else: always true in NodeJS*/
    if ( symIt )
        exports.defineProperty( _target, symIt, def );
    // Set @@iterator for compliancy with polyfill libraries like core.js
    exports.defineProperty( _target, "@@iterator", def );
    return _target;
}

exports.setIterator = setIterator;

/**
 * Helper function that will get the ES6 iterator Symbol for a class.
 * @param {function} _class The constructor function to define the iterator on
 * @returns {object} The iterator, or null.
 * @memberof module:barejs/polyfill.Object
 * @private
 */
function getIterator( _target )
{
    var result = null;

    if ( _target )
    {
        var obj = Object( _target );
        if ( symIt && ( symIt in obj ) )
            result = obj[symIt]();
        else if ( "@@iterator" in obj )
            result = obj["@@iterator"]();
    }

    return result;
}

exports.getIterator = getIterator;

////////////////////////////////////////////////////////////////////////////////////////////////////
// Grab some methods that we may have to provide fallbacks for
////////////////////////////////////////////////////////////////////////////////////////////////////
var modernPropertySupport = !!Object.defineProperties;
var legacyPropertySupport = ( ( "__defineGetter__" in Object.prototype ) && ( "__defineSetter__" in Object.prototype ) );
/*istanbul ignore next: We know we have Object.defineProperties in NodeJS*/
exports.propertyGetSetSupport = modernPropertySupport || legacyPropertySupport;
// Take care not to grab IE8's defineProperty that only works on DOM elements.
/*istanbul ignore else: native in NodeJS*/
if ( modernPropertySupport )
{
    exports.defineProperty      = Object.defineProperty;
    exports.defineProperties    = Object.defineProperties;
    exports.getOwnPropertyDescriptor    = Object.getOwnPropertyDescriptor;
    exports.getOwnPropertyDescriptors   = Object.getOwnPropertyDescriptors;
}
exports.getPrototypeOf  = Object.getPrototypeOf;
exports.freeze          = Object.freeze;
exports.isFrozen        = Object.isFrozen;
exports.seal            = Object.seal;
exports.isSealed        = Object.isSealed;
// ES6
exports.getOwnPropertyNames = Object.getOwnPropertyNames;
exports.getOwnPropertySymbols = Object.getOwnPropertySymbols;

/*istanbul ignore else: The tests are run with __ES__ set to 3*/
if ( __ES__ < 5 )
{
    ////////////////////////////////////////////////////////////////////////////////////////////////
    // ES5 fallback functions
    ////////////////////////////////////////////////////////////////////////////////////////////////
    // The following methods are not actually polyfills; these methods cannot be polyfilled.
    // However, if the fallback behavior provided by the methods is sufficient, these can be used.

    /*istanbul ignore if: native in NodeJS*/
    if ( !exports.defineProperty )
    {
        /*
         * Object.defineProperty cannot be emulated on browsers that do not support it.
         * However, if the intention is to just set a value (no getters/setters), and the loss of
         * enumerable, writable and configurable flags is acceptable, this method can be used.
         * Uses the native method where possible
         *
         * Note: check for compliance via Object.defineProperties, since IE8 has an Object.defineProperty,
         * but that only works on DOM elements.
         */
        exports.defineProperty = function defineProperty( _object, _name, _definition )
        {
            if ( !isObject( _object ) )
                throw new TypeError( "Object.defineProperty called on non-object" );
            /*
             * Fallback to simple assignment or __define[GS]etter__
             */
            // Only assign if it actually exists.
            if ( "value" in _definition )
            {
                _object[_name] = _definition.value;
            }
            else if ( ( "get" in _definition ) || ( "set" in _definition ) )
            {
                if ( !exports.propertyGetSetSupport )
                    throw new Error( "Property getters and setters are not supported in this environment" );

                if (  "get" in _definition )
                    _object.__defineGetter__( _name, _definition.get );
                if ( "set" in _definition )
                    _object.__defineSetter__( _name, _definition.set );
            }

            return _object;
        };
    }

    /*istanbul ignore if: native in NodeJS*/
    if ( !exports.defineProperties )
    {
        /*
         * Uses the possibly emulated defineProperty and behaves like Object.defineProperties.
         * Can only be used to set values (no getters/setters) in environments that do not support getters/setters.
         * Uses the native method where possible
         */
        exports.defineProperties = function defineProperties( _object, _properties )
        {
            if ( !isObject( _object ) )
                throw new TypeError( "Object.defineProperties called on non-object" );

            _properties = toObject( _properties );

            // Assume there is no Object.keys in an environment that requires this polyfill.
            for ( var i in _properties )
                if ( hasOwnProperty.call( _properties, i ) && ( !reSymbol.test( i ) ) ) // Ignore Symbols
                    exports.defineProperty( _object, i, _properties[i] );

            return _object;
        };
    }

    /*istanbul ignore if: native in NodeJS*/
    if ( !exports.getPrototypeOf )
    {
        /**
         * Object.getPrototypeOf cannot be fully emulated on browsers that do not support it.
         * We attempt to find a __proto__ or constructor.prototype.
         * Attempt to get an objects prototype, returns null on failure
         * @param {object} _object The object to get the prototype of
         * @returns {object} The prototype of _object
         * @memberof module:barejs/polyfill.Object
         * @private
         */
        exports.getPrototypeOf = function getPrototypeOf( _object )
        {
            switch ( _object === null ? strUndef : typeof _object )
            {
                case "undefined":
                    throw new TypeError( "Cannot convert undefined or null to object" );

                case "boolean":
                    return Boolean.prototype;

                case "number":
                    return Number.prototype;

                case "string":
                    return String.prototype;

                case "function":
                    return Function.prototype;

                // case "object", and any other host value
                default:
                    // jshint -W103
                    if ( "__proto__" in _object )
                        return _object.__proto__;
                    // jshint +W103

                    // If the object has the constructor property, this is already a prototype
                    if ( !hasOwnProperty.call( _object, "constructor" ) )
                        return _object.constructor.prototype;
                    // See if a framework set a superclass property
                    else if ( _object.constructor.superclass )
                        return _object.constructor.superclass.prototype;

                    if ( Array.isArray( _object ) )
                        return Array.prototype;

                    return null;
            }
        };
    }

    /*istanbul ignore if: native in NodeJS*/
    if ( !exports.freeze )
    {
        /*
         * Object.freeze cannot be emulated. This method is a NOOP if Object.freeze is not supported.
         */
        exports.freeze = function freeze( _o ){ return _o; };
    }

    /*istanbul ignore if: native in NodeJS*/
    if ( !exports.isFrozen )
    {
        /*
         * Object.isFrozen cannot be emulated. This method is a NOOP if Object.isFrozen is not supported.
         */
        exports.isFrozen = function isFrozen( _o ){ return false; };
    }

    /*istanbul ignore if: native in NodeJS*/
    if ( !exports.seal )
    {
        /*
         * Object.seal cannot be emulated. This method is a NOOP if Object.seal is not supported.
         */
        exports.seal = function seal( _o ){ return _o; };
    }

    /*istanbul ignore if: native in NodeJS*/
    if ( !exports.isSealed )
    {
        /*
         * Object.isSealed cannot be emulated. This method is a NOOP if Object.isSealed is not supported.
         */
        exports.isSealed = function isSealed( _o ){ return false; };
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////
    // ES5 polyfills
    ////////////////////////////////////////////////////////////////////////////////////////////////

    //
    // ES5 - Object
    //

    ( function()
    {
        /*global document, ActiveXObject*/
        var createEmpty;
        // While we generally prefer named constructors, avoid it here since it adds no value,
        // and may even be confusing when looking at the prototype chain.
        var Anonymous = function(){};

        //jshint -W103
        /*istanbul ignore else: NodeJS supports the __proto__ property*/
        if ( ( !( { __proto__: null } instanceof Object ) ) || ( typeof document === strUndef ) )
        {
            // We can use the deprecated __proto__ property to create an object with no prototype
            createEmpty = function()
            {
                return { __proto__: null };
            };
        }
        //jshint +W103
        else
        {
            // We grab a foreign Object.prototype so any object created from it has instanceof Object return false,
            // and we can safely delete all properties from it without breaking regular objects.
            createEmpty = function()
            {
                var shouldUseActiveX = ( function()
                {
                    try
                    {
                        return !!( document.domain && new ActiveXObject( "htmlfile" ) );
                    }
                    catch ( ex )
                    {
                        return false;
                    }
                }() );

                function Empty() {}

                if ( shouldUseActiveX )
                {
                    Empty.prototype = ( function( _xDoc )
                    {
                        _xDoc.write( "<script><\/script>" );
                        _xDoc.close();

                        var empty = _xDoc.parentWindow.Object.prototype;
                        _xDoc = null;

                        return empty;
                    }( new ActiveXObject( "htmlfile" ) ));
                }
                else
                {
                    Empty.prototype = ( function( _parent, _iframe )
                    {
                        _iframe.style.display = "none";
                        _parent.appendChild( _iframe );
                        // jshint -W107
                        _iframe.src = "javascript:";
                        // jshint +W107

                        var empty = _iframe.contentWindow.Object.prototype;
                        _parent.removeChild( _iframe );
                        _iframe = null;

                        return empty;
                    }( document.body || document.documentElement, document.createElement( "iframe" ) ) );
                }

                // Now delete all existing definitions on our "empty" object to make it truly empty
                ( function( _e )
                {
                    delete _e.constructor;
                    delete _e.hasOwnProperty;
                    delete _e.propertyIsEnumerable;
                    delete _e.isPrototypeOf;
                    delete _e.toLocaleString;
                    delete _e.toString;
                    delete _e.valueOf;
                }( Empty.prototype ) );

                // Shortcut createEmpty for future calls
                createEmpty = function()
                {
                    // This returns an object for which instanceof Object is false
                    return new Empty();
                };
                return createEmpty();
            };
        }

        /**
         * Create an instance of an object that has another object as its prototype
         * @param {object} _proto The prototype of the newly created object, or null.
         * @param {object} _properties
         * @returns {object} A new object that has the input object as prototype.
         */
        stat.create = function create( _proto, _properties )
        {
            var result;
            // _proto has 3 valid values: null or an Object (a function is an Object too)
            if ( _proto === null )
            {
                result = createEmpty();
            }
            else
            {
                if ( !isObject( _proto ) )
                    throw new TypeError( "Object prototype may only be an Object or null: " + _proto );
                Anonymous.prototype = _proto;
                result = new Anonymous();
                Anonymous.prototype = null; // Reset prototype so we don't hold a reference to _proto
            }

            if ( typeof _properties !== strUndef )
                exports.defineProperties( result, toObject( _properties ) );

            return result;
        };
    }() );

    ( function()
    {
        var hasDontEnumBug = !( { toString: null } ).propertyIsEnumerable( "toString" ),
            dontEnums = [
                "toString",
                "toLocaleString",
                "valueOf",
                "hasOwnProperty",
                "isPrototypeOf",
                "propertyIsEnumerable",
                "constructor"
            ],
            dontEnumsLength = dontEnums.length;

        /**
         * Return the property names defined directly on object
         * @param {object} _obj The target object.
         * @returns {Array} String[] property names.
         */
        stat.keys = function keys( _obj )
        {
            // Ensure object
            _obj = toObject( _obj );
            var result = [];

            for ( var prop in _obj )
                if ( hasOwnProperty.call( _obj, prop ) && ( !reSymbol.test( prop ) ) ) // Ignore Symbols
                    result.push(prop);

            /*istanbul ignore if: not applicable in NodeJS*/
            if ( hasDontEnumBug )
            {
                for ( var i = 0; i < dontEnumsLength; ++i )
                    if ( hasOwnProperty.call( _obj, dontEnums[i] ) )
                        result.push( dontEnums[i] );
            }
            return result;
        };
    }() );
}

/*istanbul ignore else: The tests are run with __ES__ set to 3*/
if ( __ES__ < 6 )
{
    // Technically most of the methods set on exports are in ES5, but BareJS relies on them so ensure there's a fallback
    /*istanbul ignore if: native in NodeJS*/
    if ( !exports.getOwnPropertyNames )
    {
        exports.getOwnPropertyNames = function( _object )
        {
            var result = [];

            if ( _object )
            {
                var obj = Object( _object );
                for ( var key in obj )
                {
                    if ( hasOwnProperty.call( obj, key ) && typeof key === "string" )
                        result.push( key );
                }
            }

            return result;
        };
    }

    /*istanbul ignore if: native in NodeJS*/
    if ( !exports.getOwnPropertySymbols )
    {
        exports.getOwnPropertySymbols = function getOwnPropertySymbols( _target )
        {
            // Ensure object
            _target = toObject( _target );
            var result = [];

            for ( var prop in _target )
                if ( hasOwnProperty.call( _target, prop ) && ( reSymbol.test( prop ) ) )
                    result.push(prop);
            return result;
        };
    }

    /*istanbul ignore if: native in NodeJS*/
    if ( !exports.getOwnPropertyDescriptor )
    {
        exports.getOwnPropertyDescriptor = function getOwnPropertyDescriptor( _object, _key )
        {
            var descriptor;

            if ( _object )
            {
                var obj = Object( _object );
                if ( hasOwnProperty.call( obj, _key ) )
                {
                    descriptor = { configurable: true, enumerable: true };
                    var getter;
                    var setter;

                    if ( legacyPropertySupport )
                    {
                        getter = obj.__lookupGetter__( _key );
                        setter = obj.__lookupSetter__( _key );
                    }

                    if ( getter || setter )
                    {
                        if ( getter )
                            descriptor.get = getter;
                        if ( setter )
                            descriptor.set = setter;
                    }
                    else
                    {
                        descriptor.value = obj[_key];
                    }
                }
            }

            return descriptor;
        };
    }

    /*istanbul ignore if: native in NodeJS*/
    if ( !exports.getOwnPropertyDescriptors )
    {
        exports.getOwnPropertyDescriptors = function( _object )
        {
            var descriptors = {};

            if ( _object )
            {
                var names = exports.getOwnPropertyNames( _object );

                for ( var i = 0, len = names.length; i < len; ++i )
                    descriptors[names[i]] = exports.getOwnPropertyDescriptor( _object, names[i] );
            }

            return descriptors;
        };
    }

    /**
     * Object.is() determines whether two values are the same value. Two values are the same if one of the following holds:
     *  - both undefined
     *  - both null
     *  - both true or both false
     *  - both strings of the same length with the same characters
     *  - both the same object
     *  - both numbers and
     *  - both +0
     *  - both -0
     *  - both NaN
     *  - or both non-zero and both not NaN and both have the same value
     * @param _v1 The first value to compare.
     * @param _v2 The second value to compare.
     * @returns {boolean} A new object that has the input object as prototype.
     */
    stat.is = function is( _v1, _v2 )
    {
        if ( _v1 === _v2 )
            return ( _v1 !== 0 ) || ( 1 / _v1 === 1 / _v2 ); // Ensure Object.is( +0, -0 ) returns false
        else
            return ( _v1 !== _v1 ) && ( _v2 !== _v2 ); // Ensure Object.is( NaN, NaN ) returns true
    };

    /**
     * The Object.assign() method only copies enumerable and own properties from a source object to a target object.
     * It uses [[Get]] on the source and [[Put]] on the target, so it will invoke getters and setters.
     * Therefore it assigns properties versus just copying or defining new properties.
     * This may make it unsuitable for merging new properties into a prototype if the merge sources contain getters.
     * For copying property definitions, including their enumerability, into prototypes Object.getOwnPropertyDescriptor() and Object.defineProperty() should be used instead.
     * @param {object} _target The target to assign to.
     * @param {object} _firstSource The first source object to copy from.
     * @returns {object} the _target, with properties assigned.
     */
    stat.assign = function assign( _target, _firstSource /*assign.length should be 2*/ )
    {
        // Ensure object
        _target = toObject( _target );
        for ( var arg = 1, argLen = arguments.length, source; arg < argLen; ++arg )
        {
            if ( isObject( source = arguments[arg] ) )
            {
                for ( var key in source )
                {
                    /*istanbul ignore else: clean prototypes assumed*/
                    if ( hasOwnProperty.call( source, key ) )
                        _target[key] = source[key];
                }
            }
        }
        return _target;
    };
// End of ES6 polyfill scope
}

if ( __ES__ < 7 )
{
    /**
     * The Object.values() method returns an array of a given object's own enumerable property values,
     * in the same order as that provided by a for...in loop
     * (the difference being that a for-in loop enumerates properties in the prototype chain as well).
     * @param {object} _target The object to enumerate.
     * @returns {Array} The array of values
     */
    stat.values = function values( _target )
    {
        // Avoid using Array.map for speed (and perform in-place replacement)
        for ( var t = toObject( _target ), result = Object.keys( t ), i = 0, len = result.length; i < len; ++i )
            result[i] = t[result[i]];
        return result;
    };

    /**
     * The Object.entries() method returns an array of a given object's own enumerable property [key, value] pairs,
     * in the same order as that provided by a for...in loop
     * (the difference being that a for-in loop enumerates properties in the prototype chain as well).
     * @param {object} _target The object to enumerate.
     * @returns {Array} The array of values
     */
    stat.entries = function entries( _target )
    {
        // Avoid using Array.map for speed (and perform in-place replacement)
        for ( var t = toObject( _target ), result = Object.keys( t ), i = 0, len = result.length; i < len; ++i )
            result[i] = [ result[i], t[result[i]] ];
        return result;
    };
// End of ES7 polyfill scope
}

// Apply static members (not using exports.polyfill since we only have static members)
polyMixin( Object, stat, exports, "Object" );

//
// ES6 - ES5 Patching
//
/*istanbul ignore else: The tests are run with __ES__ set to 3*/
if ( __ES__ < 6 )
{
    // It is possible Object.create exists (natively or polyfilled), but does not support the second
    // argument. In this case, we replace it with a method that will call defineProperties
    // We define this in the ES6 scope since we assume ES7 browsers will all support the optional second argument.

    /*istanbul ignore if: NodeJS's Object.create supports the second argument*/
    if ( Object.create( null, { test: { value: true } } ).test !== true )
    {
        Object.create = ( function( _originalCreate )
        {
            /**
             * Object.create wrapper that adds support for an optional second argument.
             */
            return function create( _proto, _properties )
            {
                var result = _originalCreate.call( Object, _proto ); // Call native Object.create

                if ( _properties )
                    exports.defineProperties( result, _properties );

                return result;
            };
        }( Object.create ) );
    }
}

// End of module
}( exports, Object ) );