# Source: core/FactoryMaker.js

    /**
     * The copyright in this software is being made available under the BSD License,
     * included below. This software may be subject to other third party and contributor
     * rights, including patent rights, and no such rights are granted under this license.
     *
     * Copyright (c) 2013, Dash Industry Forum.
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without modification,
     * are permitted provided that the following conditions are met:
     *  * Redistributions of source code must retain the above copyright notice, this
     *  list of conditions and the following disclaimer.
     *  * Redistributions in binary form must reproduce the above copyright notice,
     *  this list of conditions and the following disclaimer in the documentation and/or
     *  other materials provided with the distribution.
     *  * Neither the name of Dash Industry Forum nor the names of its
     *  contributors may be used to endorse or promote products derived from this software
     *  without specific prior written permission.
     *
     *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
     *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
     *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     *  POSSIBILITY OF SUCH DAMAGE.
     */
    /**
     * @module FactoryMaker
     * @ignore
     */
    const FactoryMaker = (function () {

        let instance;
        let singletonContexts = [];
        const singletonFactories = {};
        const classFactories = {};

        function extend(name, childInstance, override, context) {
            if (!context[name] && childInstance) {
                context[name] = {
                    instance: childInstance,
                    override: override
                };
            }
        }

        /**
         * Use this method from your extended object.  this.factory is injected into your object.
         * this.factory.getSingletonInstance(this.context, 'VideoModel')
         * will return the video model for use in the extended object.
         *
         * @param {Object} context - injected into extended object as this.context
         * @param {string} className - string name found in all dash.js objects
         * with name __dashjs_factory_name Will be at the bottom. Will be the same as the object's name.
         * @returns {*} Context aware instance of specified singleton name.
         * @memberof module:FactoryMaker
         * @instance
         */
        function getSingletonInstance(context, className) {
            for (const i in singletonContexts) {
                const obj = singletonContexts[i];
                if (obj.context === context && obj.name === className) {
                    return obj.instance;
                }
            }
            return null;
        }

        /**
         * Use this method to add an singleton instance to the system.  Useful for unit testing to mock objects etc.
         *
         * @param {Object} context
         * @param {string} className
         * @param {Object} instance
         * @memberof module:FactoryMaker
         * @instance
         */
        function setSingletonInstance(context, className, instance) {
            for (const i in singletonContexts) {
                const obj = singletonContexts[i];
                if (obj.context === context && obj.name === className) {
                    singletonContexts[i].instance = instance;
                    return;
                }
            }
            singletonContexts.push({
                name: className,
                context: context,
                instance: instance
            });
        }

        /**
         * Use this method to remove all singleton instances associated with a particular context.
         *
         * @param {Object} context
         * @memberof module:FactoryMaker
         * @instance
         */
        function deleteSingletonInstances(context) {
            singletonContexts = singletonContexts.filter(x => x.context !== context);
        }

        /*------------------------------------------------------------------------------------------*/

        // Factories storage Management

        /*------------------------------------------------------------------------------------------*/

        function getFactoryByName(name, factoriesArray) {
            return factoriesArray[name];
        }

        function updateFactory(name, factory, factoriesArray) {
            if (name in factoriesArray) {
                factoriesArray[name] = factory;
            }
        }

        /*------------------------------------------------------------------------------------------*/

        // Class Factories Management

        /*------------------------------------------------------------------------------------------*/

        function updateClassFactory(name, factory) {
            updateFactory(name, factory, classFactories);
        }

        function getClassFactoryByName(name) {
            return getFactoryByName(name, classFactories);
        }

        function getClassFactory(classConstructor) {
            let factory = getFactoryByName(classConstructor.__dashjs_factory_name, classFactories);

            if (!factory) {
                factory = function (context) {
                    if (context === undefined) {
                        context = {};
                    }
                    return {
                        create: function () {
                            return merge(classConstructor, context, arguments);
                        }
                    };
                };

                classFactories[classConstructor.__dashjs_factory_name] = factory; // store factory
            }
            return factory;
        }

        /*------------------------------------------------------------------------------------------*/

        // Singleton Factory MAangement

        /*------------------------------------------------------------------------------------------*/

        function updateSingletonFactory(name, factory) {
            updateFactory(name, factory, singletonFactories);
        }

        function getSingletonFactoryByName(name) {
            return getFactoryByName(name, singletonFactories);
        }

        function getSingletonFactory(classConstructor) {
            let factory = getFactoryByName(classConstructor.__dashjs_factory_name, singletonFactories);
            if (!factory) {
                factory = function (context) {
                    let instance;
                    if (context === undefined) {
                        context = {};
                    }
                    return {
                        getInstance: function () {
                            // If we don't have an instance yet check for one on the context
                            if (!instance) {
                                instance = getSingletonInstance(context, classConstructor.__dashjs_factory_name);
                            }
                            // If there's no instance on the context then create one
                            if (!instance) {
                                instance = merge(classConstructor, context, arguments);
                                singletonContexts.push({
                                    name: classConstructor.__dashjs_factory_name,
                                    context: context,
                                    instance: instance
                                });
                            }
                            return instance;
                        }
                    };
                };
                singletonFactories[classConstructor.__dashjs_factory_name] = factory; // store factory
            }

            return factory;
        }

        function merge(classConstructor, context, args) {

            let classInstance;
            const className = classConstructor.__dashjs_factory_name;
            const extensionObject = context[className];

            if (extensionObject) {

                let extension = extensionObject.instance;

                if (extensionObject.override) { //Override public methods in parent but keep parent.

                    classInstance = classConstructor.apply({context}, args);
                    extension = extension.apply({
                        context,
                        factory: instance,
                        parent: classInstance
                    }, args);

                    for (const prop in extension) {
                        if (classInstance.hasOwnProperty(prop)) {
                            classInstance[prop] = extension[prop];
                        }
                    }

                } else { //replace parent object completely with new object. Same as dijon.

                    return extension.apply({
                        context,
                        factory: instance
                    }, args);

                }
            } else {
                // Create new instance of the class
                classInstance = classConstructor.apply({context}, args);
            }

            // Add getClassName function to class instance prototype (used by Debug)
            classInstance.getClassName = function () {return className;};

            return classInstance;
        }

        instance = {
            extend: extend,
            getSingletonInstance: getSingletonInstance,
            setSingletonInstance: setSingletonInstance,
            deleteSingletonInstances: deleteSingletonInstances,
            getSingletonFactory: getSingletonFactory,
            getSingletonFactoryByName: getSingletonFactoryByName,
            updateSingletonFactory: updateSingletonFactory,
            getClassFactory: getClassFactory,
            getClassFactoryByName: getClassFactoryByName,
            updateClassFactory: updateClassFactory
        };

        return instance;

    }());

    export default FactoryMaker;

#### Search results