/*!
 * Microsoft Dynamic Proto Utility, 1.1.4
 * Copyright (c) Microsoft and contributors. All rights reserved.
 */
System.register('Microsoft.DynamicProto-JS', [], function (exports) {
    'use strict';
    return {
        execute: function () {

            exports('default', dynamicProto);

            /**
             * Constant string defined to support minimization
             * @ignore
             */
            var Constructor = 'constructor';
            /**
             * Constant string defined to support minimization
             * @ignore
             */
            var Prototype = 'prototype';
            /**
             * Constant string defined to support minimization
             * @ignore
             */
            var strFunction = 'function';
            /**
             * Used to define the name of the instance function lookup table
             * @ignore
             */
            var DynInstFuncTable = '_dynInstFuncs';
            /**
             * Name used to tag the dynamic prototype function
             * @ignore
             */
            var DynProxyTag = '_isDynProxy';
            /**
             * Name added to a prototype to define the dynamic prototype "class" name used to lookup the function table
             * @ignore
             */
            var DynClassName = '_dynClass';
            /**
             * Prefix added to the classname to avoid any name clashes with other instance level properties
             * @ignore
             */
            var DynClassNamePrefix = '_dynCls$';
            /**
             * A tag which is used to check if we have already to attempted to set the instance function if one is not present
             * @ignore
             */
            var DynInstChkTag = '_dynInstChk';
            /**
             * A tag which is used to check if we are allows to try and set an instance function is one is not present. Using the same
             * tag name as the function level but a different const name for readability only.
             */
            var DynAllowInstChkTag = DynInstChkTag;
            /**
             * The global (imported) instances where the global performance options are stored
             */
            var DynProtoDefaultOptions = '_dfOpts';
            /**
             * Value used as the name of a class when it cannot be determined
             * @ignore
             */
            var UnknownValue = '_unknown_';
            /**
             * Constant string defined to support minimization
             * @ignore
             */
            var str__Proto = "__proto__";
            /**
             * Constant string defined to support minimization
             * @ignore
             */
            var strUseBaseInst = 'useBaseInst';
            /**
             * Constant string defined to support minimization
             * @ignore
             */
            var strSetInstFuncs = 'setInstFuncs';
            var Obj = Object;
            /**
             * Pre-lookup to check if we are running on a modern browser (i.e. not IE8)
             * @ignore
             */
            var _objGetPrototypeOf = Obj["getPrototypeOf"];
            /**
             * Internal Global used to generate a unique dynamic class name, every new class will increase this value
             * @ignore
             */
            var _dynamicNames = 0;
            /**
             * Helper to check if the object contains a property of the name
             * @ignore
             */
            function _hasOwnProperty(obj, prop) {
                return obj && Obj[Prototype].hasOwnProperty.call(obj, prop);
            }
            /**
             * Helper used to check whether the target is an Object prototype or Array prototype
             * @ignore
             */
            function _isObjectOrArrayPrototype(target) {
                return target && (target === Obj[Prototype] || target === Array[Prototype]);
            }
            /**
             * Helper used to check whether the target is an Object prototype, Array prototype or Function prototype
             * @ignore
             */
            function _isObjectArrayOrFunctionPrototype(target) {
                return _isObjectOrArrayPrototype(target) || target === Function[Prototype];
            }
            /**
             * Helper used to get the prototype of the target object as getPrototypeOf is not available in an ES3 environment.
             * @ignore
             */
            function _getObjProto(target) {
                if (target) {
                    // This method doesn't existing in older browsers (e.g. IE8)
                    if (_objGetPrototypeOf) {
                        return _objGetPrototypeOf(target);
                    }
                    // target[Constructor] May break if the constructor has been changed or removed
                    var newProto = target[str__Proto] || target[Prototype] || (target[Constructor] ? target[Constructor][Prototype] : null);
                    if (newProto) {
                        return newProto;
                    }
                }
                return null;
            }
            /**
             * Helper to get the properties of an object, including none enumerable ones as functions on a prototype in ES6
             * are not enumerable.
             * @param target
             */
            function _forEachProp(target, func) {
                var props = [];
                var getOwnProps = Obj["getOwnPropertyNames"];
                if (getOwnProps) {
                    props = getOwnProps(target);
                }
                else {
                    for (var name_1 in target) {
                        if (typeof name_1 === "string" && _hasOwnProperty(target, name_1)) {
                            props.push(name_1);
                        }
                    }
                }
                if (props && props.length > 0) {
                    for (var lp = 0; lp < props.length; lp++) {
                        func(props[lp]);
                    }
                }
            }
            /**
             * Helper function to check whether the provided function name is a potential candidate for dynamic
             * callback and prototype generation.
             * @param target The target object, may be a prototype or class object
             * @param funcName The function name
             * @param skipOwn Skips the check for own property
             * @ignore
             */
            function _isDynamicCandidate(target, funcName, skipOwn) {
                return (funcName !== Constructor && typeof target[funcName] === strFunction && (skipOwn || _hasOwnProperty(target, funcName)));
            }
            /**
             * Helper to throw a TypeError exception
             * @param message the message
             * @ignore
             */
            function _throwTypeError(message) {
                throw new TypeError("DynamicProto: " + message);
            }
            /**
             * Returns a collection of the instance functions that are defined directly on the thisTarget object, it does
             * not return any inherited functions
             * @param thisTarget The object to get the instance functions from
             * @ignore
             */
            function _getInstanceFuncs(thisTarget) {
                // Get the base proto
                var instFuncs = {};
                // Save any existing instance functions
                _forEachProp(thisTarget, function (name) {
                    // Don't include any dynamic prototype instances - as we only want the real functions
                    if (!instFuncs[name] && _isDynamicCandidate(thisTarget, name, false)) {
                        // Create an instance callback for passing the base function to the caller
                        instFuncs[name] = thisTarget[name];
                    }
                });
                return instFuncs;
            }
            /**
             * Returns whether the value is included in the array
             * @param values The array of values
             * @param value  The value
             */
            function _hasVisited(values, value) {
                for (var lp = values.length - 1; lp >= 0; lp--) {
                    if (values[lp] === value) {
                        return true;
                    }
                }
                return false;
            }
            /**
             * Returns an object that contains callback functions for all "base/super" functions, this is used to "save"
             * enabling calling super.xxx() functions without requiring that the base "class" has defined a prototype references
             * @param target The current instance
             * @ignore
             */
            function _getBaseFuncs(classProto, thisTarget, instFuncs, useBaseInst) {
                function _instFuncProxy(target, funcHost, funcName) {
                    var theFunc = funcHost[funcName];
                    if (theFunc[DynProxyTag] && useBaseInst) {
                        // grab and reuse the hosted looking function (if available) otherwise the original passed function
                        var instFuncTable = target[DynInstFuncTable] || {};
                        if (instFuncTable[DynAllowInstChkTag] !== false) {
                            theFunc = (instFuncTable[funcHost[DynClassName]] || {})[funcName] || theFunc;
                        }
                    }
                    return function () {
                        return theFunc.apply(target, arguments);
                    };
                }
                // Start creating a new baseFuncs by creating proxies for the instance functions (as they may get replaced)
                var baseFuncs = {};
                _forEachProp(instFuncs, function (name) {
                    // Create an instance callback for passing the base function to the caller
                    baseFuncs[name] = _instFuncProxy(thisTarget, instFuncs, name);
                });
                // Get the base prototype functions
                var baseProto = _getObjProto(classProto);
                var visited = [];
                // Don't include base object functions for Object, Array or Function
                while (baseProto && !_isObjectArrayOrFunctionPrototype(baseProto) && !_hasVisited(visited, baseProto)) {
                    // look for prototype functions
                    _forEachProp(baseProto, function (name) {
                        // Don't include any dynamic prototype instances - as we only want the real functions
                        // For IE 7/8 the prototype lookup doesn't provide the full chain so we need to bypass the 
                        // hasOwnProperty check we get all of the methods, main difference is that IE7/8 doesn't return
                        // the Object prototype methods while bypassing the check
                        if (!baseFuncs[name] && _isDynamicCandidate(baseProto, name, !_objGetPrototypeOf)) {
                            // Create an instance callback for passing the base function to the caller
                            baseFuncs[name] = _instFuncProxy(thisTarget, baseProto, name);
                        }
                    });
                    // We need to find all possible functions that might be overloaded by walking the entire prototype chain
                    // This avoids the caller from needing to check whether it's direct base class implements the function or not
                    // by walking the entire chain it simplifies the usage and issues from upgrading any of the base classes.
                    visited.push(baseProto);
                    baseProto = _getObjProto(baseProto);
                }
                return baseFuncs;
            }
            function _getInstFunc(target, funcName, proto, currentDynProtoProxy) {
                var instFunc = null;
                // We need to check whether the class name is defined directly on this prototype otherwise
                // it will walk the proto chain and return any parent proto classname.
                if (target && _hasOwnProperty(proto, DynClassName)) {
                    var instFuncTable = target[DynInstFuncTable] || {};
                    instFunc = (instFuncTable[proto[DynClassName]] || {})[funcName];
                    if (!instFunc) {
                        // Avoid stack overflow from recursive calling the same function
                        _throwTypeError("Missing [" + funcName + "] " + strFunction);
                    }
                    // We have the instance function, lets check it we can speed up further calls
                    // by adding the instance function back directly on the instance (avoiding the dynamic func lookup)
                    if (!instFunc[DynInstChkTag] && instFuncTable[DynAllowInstChkTag] !== false) {
                        // If the instance already has an instance function we can't replace it
                        var canAddInst = !_hasOwnProperty(target, funcName);
                        // Get current prototype
                        var objProto = _getObjProto(target);
                        var visited = [];
                        // Lookup the function starting at the top (instance level prototype) and traverse down, if the first matching function
                        // if nothing is found or if the first hit is a dynamic proto instance then we can safely add an instance shortcut
                        while (canAddInst && objProto && !_isObjectArrayOrFunctionPrototype(objProto) && !_hasVisited(visited, objProto)) {
                            var protoFunc = objProto[funcName];
                            if (protoFunc) {
                                canAddInst = (protoFunc === currentDynProtoProxy);
                                break;
                            }
                            // We need to find all possible initial functions to ensure that we don't bypass a valid override function
                            visited.push(objProto);
                            objProto = _getObjProto(objProto);
                        }
                        try {
                            if (canAddInst) {
                                // This instance doesn't have an instance func and the class hierarchy does have a higher level prototype version
                                // so it's safe to directly assign for any subsequent calls (for better performance)
                                target[funcName] = instFunc;
                            }
                            // Block further attempts to set the instance function for any
                            instFunc[DynInstChkTag] = 1;
                        }
                        catch (e) {
                            // Don't crash if the object is readonly or the runtime doesn't allow changing this
                            // And set a flag so we don't try again for any function
                            instFuncTable[DynAllowInstChkTag] = false;
                        }
                    }
                }
                return instFunc;
            }
            function _getProtoFunc(funcName, proto, currentDynProtoProxy) {
                var protoFunc = proto[funcName];
                // Check that the prototype function is not a self reference -- try to avoid stack overflow!
                if (protoFunc === currentDynProtoProxy) {
                    // It is so lookup the base prototype
                    protoFunc = _getObjProto(proto)[funcName];
                }
                if (typeof protoFunc !== strFunction) {
                    _throwTypeError("[" + funcName + "] is not a " + strFunction);
                }
                return protoFunc;
            }
            /**
             * Add the required dynamic prototype methods to the the class prototype
             * @param proto - The class prototype
             * @param className - The instance classname
             * @param target - The target instance
             * @param baseInstFuncs - The base instance functions
             * @param setInstanceFunc - Flag to allow prototype function to reset the instance function if one does not exist
             * @ignore
             */
            function _populatePrototype(proto, className, target, baseInstFuncs, setInstanceFunc) {
                function _createDynamicPrototype(proto, funcName) {
                    var dynProtoProxy = function () {
                        // Use the instance or prototype function
                        var instFunc = _getInstFunc(this, funcName, proto, dynProtoProxy) || _getProtoFunc(funcName, proto, dynProtoProxy);
                        return instFunc.apply(this, arguments);
                    };
                    // Tag this function as a proxy to support replacing dynamic proxy elements (primary use case is for unit testing
                    // via which can dynamically replace the prototype function reference)
                    dynProtoProxy[DynProxyTag] = 1;
                    return dynProtoProxy;
                }
                if (!_isObjectOrArrayPrototype(proto)) {
                    var instFuncTable = target[DynInstFuncTable] = target[DynInstFuncTable] || {};
                    var instFuncs_1 = instFuncTable[className] = (instFuncTable[className] || {}); // fetch and assign if as it may not exist yet
                    // Set whether we are allow to lookup instances, if someone has set to false then do not re-enable
                    if (instFuncTable[DynAllowInstChkTag] !== false) {
                        instFuncTable[DynAllowInstChkTag] = !!setInstanceFunc;
                    }
                    _forEachProp(target, function (name) {
                        // Only add overridden functions
                        if (_isDynamicCandidate(target, name, false) && target[name] !== baseInstFuncs[name]) {
                            // Save the instance Function to the lookup table and remove it from the instance as it's not a dynamic proto function
                            instFuncs_1[name] = target[name];
                            delete target[name];
                            // Add a dynamic proto if one doesn't exist or if a prototype function exists and it's not a dynamic one
                            if (!_hasOwnProperty(proto, name) || (proto[name] && !proto[name][DynProxyTag])) {
                                proto[name] = _createDynamicPrototype(proto, name);
                            }
                        }
                    });
                }
            }
            /**
             * Checks whether the passed prototype object appears to be correct by walking the prototype hierarchy of the instance
             * @param classProto The class prototype instance
             * @param thisTarget The current instance that will be checked whether the passed prototype instance is in the hierarchy
             * @ignore
             */
            function _checkPrototype(classProto, thisTarget) {
                // This method doesn't existing in older browsers (e.g. IE8)
                if (_objGetPrototypeOf) {
                    // As this is primarily a coding time check, don't bother checking if running in IE8 or lower
                    var visited = [];
                    var thisProto = _getObjProto(thisTarget);
                    while (thisProto && !_isObjectArrayOrFunctionPrototype(thisProto) && !_hasVisited(visited, thisProto)) {
                        if (thisProto === classProto) {
                            return true;
                        }
                        // This avoids the caller from needing to check whether it's direct base class implements the function or not
                        // by walking the entire chain it simplifies the usage and issues from upgrading any of the base classes.
                        visited.push(thisProto);
                        thisProto = _getObjProto(thisProto);
                    }
                }
                return false;
            }
            /**
             * Gets the current prototype name using the ES6 name if available otherwise falling back to a use unknown as the name.
             * It's not critical for this to return a name, it's used to decorate the generated unique name for easier debugging only.
             * @param target
             * @param unknownValue
             * @ignore
             */
            function _getObjName(target, unknownValue) {
                if (_hasOwnProperty(target, Prototype)) {
                    // Look like a prototype
                    return target.name || unknownValue || UnknownValue;
                }
                return (((target || {})[Constructor]) || {}).name || unknownValue || UnknownValue;
            }
            /**
             * Helper function when creating dynamic (inline) functions for classes, this helper performs the following tasks :-
             * - Saves references to all defined base class functions
             * - Calls the delegateFunc with the current target (this) and a base object reference that can be used to call all "super" functions.
             * - Will populate the class prototype for all overridden functions to support class extension that call the prototype instance.
             * Callers should use this helper when declaring all function within the constructor of a class, as mentioned above the delegateFunc is
             * passed both the target "this" and an object that can be used to call any base (super) functions, using this based object in place of
             * super.XXX() (which gets expanded to _super.prototype.XXX()) provides a better minification outcome and also ensures the correct "this"
             * context is maintained as TypeScript creates incorrect references using super.XXXX() for dynamically defined functions i.e. Functions
             * defined in the constructor or some other function (rather than declared as complete typescript functions).
             * ### Usage
             * ```typescript
             * import dynamicProto from "@microsoft/dynamicproto-js";
             * class ExampleClass extends BaseClass {
             *     constructor() {
             *         dynamicProto(ExampleClass, this, (_self, base) => {
             *             // This will define a function that will be converted to a prototype function
             *             _self.newFunc = () => {
             *                 // Access any "this" instance property
             *                 if (_self.someProperty) {
             *                     ...
             *                 }
             *             }
             *             // This will define a function that will be converted to a prototype function
             *             _self.myFunction = () => {
             *                 // Access any "this" instance property
             *                 if (_self.someProperty) {
             *                     // Call the base version of the function that we are overriding
             *                     base.myFunction();
             *                 }
             *                 ...
             *             }
             *             _self.initialize = () => {
             *                 ...
             *             }
             *             // Warnings: While the following will work as _self is simply a reference to
             *             // this, if anyone overrides myFunction() the overridden will be called first
             *             // as the normal JavaScript method resolution will occur and the defined
             *             // _self.initialize() function is actually gets removed from the instance and
             *             // a proxy prototype version is created to reference the created method.
             *             _self.initialize();
             *         });
             *     }
             * }
             * ```
             * @typeparam DPType This is the generic type of the class, used to keep intellisense valid
             * @typeparam DPCls The type that contains the prototype of the current class
             * @param theClass - This is the current class instance which contains the prototype for the current class
             * @param target - The current "this" (target) reference, when the class has been extended this.prototype will not be the 'theClass' value.
             * @param delegateFunc - The callback function (closure) that will create the dynamic function
             * @param options - Additional options to configure how the dynamic prototype operates
             */
            function dynamicProto(theClass, target, delegateFunc, options) {
                // Make sure that the passed theClass argument looks correct
                if (!_hasOwnProperty(theClass, Prototype)) {
                    _throwTypeError("theClass is an invalid class definition.");
                }
                // Quick check to make sure that the passed theClass argument looks correct (this is a common copy/paste error)
                var classProto = theClass[Prototype];
                if (!_checkPrototype(classProto, target)) {
                    _throwTypeError("[" + _getObjName(theClass) + "] is not in class hierarchy of [" + _getObjName(target) + "]");
                }
                var className = null;
                if (_hasOwnProperty(classProto, DynClassName)) {
                    // Only grab the class name if it's defined on this prototype (i.e. don't walk the prototype chain)
                    className = classProto[DynClassName];
                }
                else {
                    // As not all browser support name on the prototype creating a unique dynamic one if we have not already
                    // assigned one, so we can use a simple string as the lookup rather than an object for the dynamic instance
                    // function table lookup.
                    className = DynClassNamePrefix + _getObjName(theClass, "_") + "$" + _dynamicNames;
                    _dynamicNames++;
                    classProto[DynClassName] = className;
                }
                var perfOptions = dynamicProto[DynProtoDefaultOptions];
                var useBaseInst = !!perfOptions[strUseBaseInst];
                if (useBaseInst && options && options[strUseBaseInst] !== undefined) {
                    useBaseInst = !!options[strUseBaseInst];
                }
                // Get the current instance functions
                var instFuncs = _getInstanceFuncs(target);
                // Get all of the functions for any base instance (before they are potentially overridden)
                var baseFuncs = _getBaseFuncs(classProto, target, instFuncs, useBaseInst);
                // Execute the delegate passing in both the current target "this" and "base" function references
                // Note casting the same type as we don't actually have the base class here and this will provide some intellisense support
                delegateFunc(target, baseFuncs);
                // Don't allow setting instance functions for older IE instances
                var setInstanceFunc = !!_objGetPrototypeOf && !!perfOptions[strSetInstFuncs];
                if (setInstanceFunc && options) {
                    setInstanceFunc = !!options[strSetInstFuncs];
                }
                // Populate the Prototype for any overridden instance functions
                _populatePrototype(classProto, className, target, instFuncs, setInstanceFunc !== false);
            }
            /**
             * Exposes the default global options to allow global configuration, if the global values are disabled these will override
             * any passed values. This is primarily exposed to support unit-testing without the need for individual classes to expose
             * their internal usage of dynamic proto.
             */
            var perfDefaults = {
                setInstFuncs: true,
                useBaseInst: true
            };
            // And expose for testing
            dynamicProto[DynProtoDefaultOptions] = perfDefaults;

        }
    };
});
//# sourceMappingURL=dynamicproto-js.js.map
