<commit_before>/**
 * Copyright 2015-present Tuan Le.
 *
 * Licensed under the MIT License.
 * You may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://opensource.org/licenses/mit-license.html
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *------------------------------------------------------------------------
 *
 * @module InterfaceFactory
 * @description - An interface component factory.
 *
 * @author Tuan Le (tuan.t.lei@gmail.com)
 */
'use strict'; // eslint-disable-line

/* load EventStreamComposite */
import EventStreamComposite from './composites/event-stream-composite';

/* load Composer */
import Composer from '../composer';

/* load Hflow */
import { Hflow } from 'hyperflow';

/* factory Ids */
import {
    STORE_FACTORY_CODE,
    INTERFACE_FACTORY_CODE
} from './factory-code';

/* delay data stream from composite interface by 10ms as default */
const DEBOUNCE_COMPOSITE_INTERFACE_IN_MS = 10;

/**
 * @description - An interface component factory.
 *
 * @module InterfaceFactory
 */
export default Composer({
    composites: [
        EventStreamComposite
    ],
    state: {
        name: {
            value: `unnamed`,
            stronglyTyped: true,
            required: true
        },
        style: {
            value: null,
            stronglyTyped: false,
            required: false
        },
        fId: {
            computable: {
                contexts: [
                    `name`
                ],
                compute () {
                    return `${INTERFACE_FACTORY_CODE}-${this.name}`;
                }
            }
        }
    },
    InterfaceFactory: function InterfaceFactory () {
        /* ----- Private Variables ------------- */
        let _stateless = true;
        let _componentLib;
        let _compositeCache = {};
        let _compositeRefCache = {};
        let _initialReflectedState = {};
        /* ----- Public Functions -------------- */
        /**
         * @description - Initialize interface.
         *
         * @method $init
         * @return void
         */
        this.$init = function $init () {
            Hflow.log(`warn0`, `InterfaceFactory.$init - Method is not implemented by default.`);
        };
        /**
         * @description - Setup interface event stream.
         *
         * @method setup
         * @param {function} done
         * @return void
         */
        this.setup = function setup (done) { // eslint-disable-line
            if (!Hflow.isFunction(done)) {
                Hflow.log(`error`, `InterfaceFactory.setup - Input done function is invalid.`);
            } else {
                done();
                Hflow.log(`warn0`, `InterfaceFactory.setup - Method is not implemented by default.`);
            }
        };
        /**
         * @description - Teardown interface event stream.
         *
         * @method teardown
         * @param {function} done
         * @return void
         */
        this.teardown = function teardown (done) { // eslint-disable-line
            if (!Hflow.isFunction(done)) {
                Hflow.log(`error`, `InterfaceFactory.teardown - Input done function is invalid.`);
            } else {
                done();
                Hflow.log(`warn0`, `InterfaceFactory.teardown - Method is not implemented by default.`);
            }
        };
        /**
         * @description - Check if interface is pure or stateless.
         *
         * @method isStateless
         * @return {boolean}
         */
        this.isStateless = function isStateless () {
            return _stateless;
        };
        /**
         * @description - Get inital reflected state value.
         *
         * @method getInitialReflectedState
         * @return {object}
         */
        this.getInitialReflectedState = function getInitialReflectedState () {
            const intf = this;
            if (_stateless) {
                Hflow.log(`warn1`, `InterfaceFactory.getInitialReflectedState - Interface:${intf.name} is pure with no state reflected to a store.`);
            }
            return _initialReflectedState;
        };
        /**
         * @description - Convert interface to a component.
         *
         * @method toComponent
         * @return {object}
         */
        this.toComponent = function toComponent () {
            Hflow.log(`error`, `InterfaceFactory.toComponent - Method is not implemented by default. Implementation required.`);
        };
        /**
         * @description - Get the registered component toolkit or library.
         *
         * @method getComponentLib
         * @returns {object}
         */
        this.getComponentLib = function getComponentLib () {
            if (!Hflow.isObject(_componentLib)) {
                Hflow.log(`error`, `InterfaceFactory.getComponentLib - Interface component library is not registered.`);
            }
            return _componentLib;
        };
        /**
         * @description - Assign the registered component's reference object.
         *
         * @method assignComponentRef
         * @param {string} key
         * @returns function
         */
        this.assignComponentRef = function assignComponentRef (key) {
            if (!Hflow.isString(key)) {
                Hflow.log(`error`, `InterfaceFactory.assignComponentRef - Input component reference key is invalid.`);
            } else {
                /* helper function to set component ref */
                const setComponentRef = function setComponentRef (componentRef) {
                    _compositeRefCache[key] = Hflow.isDefined(componentRef) ? componentRef : null;
                    Hflow.log(`warn0`, `InterfaceFactory.assignComponentRef - Assigning null to component reference at key:${key}.`);
                };
                return setComponentRef;
            }
        };
        /**
         * @description - Lookup the registered component's reference object.
         *
         * @method lookupComponentRefs
         * @param {array} keys
         * @returns {array}
         */
        this.lookupComponentRefs = function lookupComponentRefs (...keys) {
            let componentRefs = [];
            if (!Hflow.isEmpty(_compositeRefCache)) {
                if (!Hflow.isEmpty(keys)) {
                    if (!keys.every((key) => Hflow.isString(key))) {
                        Hflow.log(`error`, `InterfaceFactory.lookupComponentRefs - Input component reference key is invalid.`);
                    } else if (!keys.every((key) => _compositeRefCache.hasOwnProperty(key))) {
                        Hflow.log(`error`, `InterfaceFactory.lookupComponentRefs - Component reference is not found.`);
                    } else {
                        componentRefs = Hflow.collect(_compositeRefCache, ...keys);
                    }
                } else {
                    componentRefs = Object.keys(_compositeRefCache).map((name) => _compositeRefCache[name]);
                }
            }
            return componentRefs;
        };
        /**
         * @description - Get interface component composite set.
         *
         * @method getComponentComposites
         * @param {array} intfNames
         * @return {array}
         */
        this.getComponentComposites = function getComponentComposites (...intfNames) {
            let components = [];
            if (!Hflow.isEmpty(_compositeCache)) {
                // TODO: Allows getting composite component of composite component.
                if (!Hflow.isEmpty(intfNames)) {
                    if (!intfNames.every((name) => Hflow.isString(name))) {
                        Hflow.log(`error`, `InterfaceFactory.getComponentComposites - Input interface name is invalid.`);
                    } else if (!intfNames.every((name) => {
                        if (_compositeCache.hasOwnProperty(name)) {
                            return true;
                        }
                        Hflow.log(`warn1`, `InterfaceFactory.getComponentComposites - Composite component:${name} was not composed to component cache.`);
                        return false;
                    })) {
                        Hflow.log(`error`, `InterfaceFactory.getComponentComposites - Component is not found.`);
                    } else {
                        components = Hflow.collect(_compositeCache, ...intfNames.map((name) => `${name}.component`));
                    }
                } else {
                    components = Object.keys(_compositeCache).map((name) => _compositeCache[name].component);
                }
            }
            return components;
        };
        /**
         * @description - Get interface composite set.
         *
         * @method getInterfaceComposites
         * @param {array} intfNames
         * @return {array}
         */
        this.getInterfaceComposites = function getInterfaceComposites (...intfNames) {
            let intfs = [];
            if (!Hflow.isEmpty(_compositeCache)) {
                // TODO: Remove this method if find no use case.
                // TODO: Allows getting composite interface of composite interface.
                if (!Hflow.isEmpty(intfNames)) {
                    if (!intfNames.every((name) => Hflow.isString(name))) {
                        Hflow.log(`error`, `InterfaceFactory.getInterfaceComposites - Input interface name is invalid.`);
                    } else if (!intfNames.every((name) => _compositeCache.hasOwnProperty(name))) {
                        Hflow.log(`error`, `InterfaceFactory.getInterfaceComposites - Composite is not found.`);
                    } else {
                        intfs = Hflow.collect(_compositeCache, ...intfNames.map((name) => `${name}.intf`));
                    }
                } else {
                    intfs = Object.keys(_compositeCache).map((name) => _compositeCache[name].intf);
                }
            }
            return intfs;
        };
        /**
         * @description - Register a component toolkit or library for interface.
         *
         * @method registerComponentLib
         * @param {object} componentLib
         * @returns void
         */
        this.registerComponentLib = function registerComponentLib (componentLib) {
            if (!Hflow.isObject(componentLib)) {
                Hflow.log(`error`, `InterfaceFactory.registerComponentLib - Input component library is invalid.`);
            } else {
                _componentLib = componentLib;
                if (!Hflow.isEmpty(_compositeCache)) {
                    _compositeCache = Hflow.collect(_compositeCache, ...Object.keys(_compositeCache).map((name) => {
                        return `${name}.intf`;
                    })).reduce((compositeBundle, compositeIntf) => {
                        compositeIntf.registerComponentLib(_componentLib);

                        const component = compositeIntf.toComponent();
                        if (!Hflow.isFunction(component)) {
                            Hflow.log(`error`, `InterfaceFactory.registerComponentLib - Component is invalid.`);
                        } else {
                            compositeBundle[compositeIntf.name].component = component;
                        }
                        return compositeBundle;
                    }, _compositeCache);
                }
            }
        };
        /**
         * @description - Set the composite interfaces that this interface is composed of.
         *
         * @method composedOf
         * @param {array} compositeIntfs
         * @return void
         */
        this.composedOf = function composedOf (...compositeIntfs) {
            const intf = this;
            // TODO: If possible, rename method to compose and return a newly created interface instead.
            if (Hflow.isEmpty(compositeIntfs)) {
                Hflow.log(`warn0`, `InterfaceFactory.composedOf - Input composite interface array is empty.`);
            } else {
                if (!compositeIntfs.every((compositeIntf) => {
                    return Hflow.isSchema({
                        fId: `string`,
                        name: `string`,
                        toComponent: `function`,
                        registerComponentLib: `function`
                    }).of(compositeIntf) && compositeIntf.fId.substr(0, INTERFACE_FACTORY_CODE.length) === INTERFACE_FACTORY_CODE;
                })) {
                    Hflow.log(`error`, `InterfaceFactory.composedOf - Input composite interfaces are invalid.`);
                } else if (compositeIntfs.some((compositeIntf) => intf.name === compositeIntf.name)) {
                    Hflow.log(`error`, `InterfaceFactory.composedOf - Cannot compose interface:${intf.name} as a composite of itself.`);
                } else {
                    const newCompositeInterfaces = compositeIntfs.filter((compositeIntf) => {
                        if (Object.keys(_compositeCache).some((name) => name === compositeIntf.name)) {
                            Hflow.log(`warn1`, `InterfaceFactory.composedOf - Interface:${intf.name} is already composed of composite interface:${compositeIntf.name}.`);
                            return false;
                        }
                        return true;
                    });
                    _compositeCache = newCompositeInterfaces.map((compositeIntf) => {
                        intf.observe(compositeIntf).debounce(DEBOUNCE_COMPOSITE_INTERFACE_IN_MS);
                        Hflow.log(`info`, `Interface:${intf.name} is composed of composite interface:${compositeIntf.name}.`);
                        return compositeIntf;
                    }).reduce((compositeBundle, compositeIntf) => {
                        compositeBundle[compositeIntf.name] = {
                            intf: compositeIntf,
                            component: null
                        };
                        return compositeBundle;
                    }, _compositeCache);
                    return intf;
                }
            }
        };
        this.reflectStateOf = function reflectStateOf (store) {
            if (!Hflow.isSchema({
                fId: `string`,
                name: `string`,
                getStateAsObject: `function`
            }).of(store) || store.fId.substr(0, STORE_FACTORY_CODE.length) !== STORE_FACTORY_CODE) {
                Hflow.log(`error`, `InterfaceFactory.reflectStateOf - Input store is invalid.`);
            } else {
                const intf = this;
                if (_stateless) {
                    _initialReflectedState = Hflow.mix(store.getStateAsObject(), {
                        exclusion: {
                            keys: [
                                `name`,
                                `fId`
                            ]
                        }
                    }).with({});
                    intf.incoming(`as-state-mutated`).handle((reflectedState) => {
                        /* update initial reflected state snapshot as state from store mutated */
                        _initialReflectedState = reflectedState;
                    });
                    _stateless = false;
                    Hflow.log(`info`, `Interface:${intf.name} is reflecting state of store:${store.name}.`);
                } else {
                    Hflow.log(`warn1`, `InterfaceFactory.reflectStateOf - Interface:${intf.name} is already has its state mirrored with a store.`);
                }
            }
        };
    }
});
<commit_msg>Added mounting, dismounting, and update life cycle handler methods.<commit_after>/**
 * Copyright 2015-present Tuan Le.
 *
 * Licensed under the MIT License.
 * You may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://opensource.org/licenses/mit-license.html
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *------------------------------------------------------------------------
 *
 * @module InterfaceFactory
 * @description - An interface component factory.
 *
 * @author Tuan Le (tuan.t.lei@gmail.com)
 */
'use strict'; // eslint-disable-line

/* load EventStreamComposite */
import EventStreamComposite from './composites/event-stream-composite';

/* load Composer */
import Composer from '../composer';

/* load Hflow */
import { Hflow } from 'hyperflow';

/* factory Ids */
import {
    STORE_FACTORY_CODE,
    INTERFACE_FACTORY_CODE
} from './factory-code';

/* delay data stream from composite interface by 10ms as default */
const DEBOUNCE_COMPOSITE_INTERFACE_IN_MS = 10;

/**
 * @description - An interface component factory.
 *
 * @module InterfaceFactory
 */
export default Composer({
    composites: [
        EventStreamComposite
    ],
    state: {
        name: {
            value: `unnamed`,
            stronglyTyped: true,
            required: true
        },
        style: {
            value: null,
            stronglyTyped: false,
            required: false
        },
        fId: {
            computable: {
                contexts: [
                    `name`
                ],
                compute () {
                    return `${INTERFACE_FACTORY_CODE}-${this.name}`;
                }
            }
        }
    },
    InterfaceFactory: function InterfaceFactory () {
        /* ----- Private Variables ------------- */
        let _stateless = true;
        let _componentLib;
        let _compositeCache = {};
        let _compositeRefCache = {};
        let _initialReflectedState = {};
        /* ----- Public Functions -------------- */
        /**
         * @description - Initialize interface.
         *
         * @method $init
         * @return void
         */
        this.$init = function $init () {
            Hflow.log(`warn0`, `InterfaceFactory.$init - Method is not implemented by default.`);
        };
        /**
         * @description - Setup interface event stream.
         *
         * @method setup
         * @param {function} done
         * @return void
         */
        this.setup = function setup (done) { // eslint-disable-line
            if (!Hflow.isFunction(done)) {
                Hflow.log(`error`, `InterfaceFactory.setup - Input done function is invalid.`);
            } else {
                done();
                Hflow.log(`warn0`, `InterfaceFactory.setup - Method is not implemented by default.`);
            }
        };
        /**
         * @description - Teardown interface event stream.
         *
         * @method teardown
         * @param {function} done
         * @return void
         */
        this.teardown = function teardown (done) { // eslint-disable-line
            if (!Hflow.isFunction(done)) {
                Hflow.log(`error`, `InterfaceFactory.teardown - Input done function is invalid.`);
            } else {
                done();
                Hflow.log(`warn0`, `InterfaceFactory.teardown - Method is not implemented by default.`);
            }
        };
        /**
         * @description - Handle logic at component premounting stage.
         *
         * @method preMountStage
         * @param {function} handler
         * @return void
         */
        this.preMountStage = function preMountStage (handler) {
            if (!Hflow.isFunction(handler)) {
                Hflow.log(`error`, `InterfaceFactory.preMountStage - Input handler function is invalid.`);
            } else {
                Hflow.log(`warn0`, `InterfaceFactory.preMountStage - Method is not implemented by default.`);
            }
        };
        /**
         * @description - Handle logic at component postmounting stage.
         *
         * @method postMountStage
         * @param {function} handler
         * @return void
         */
        this.postMountStage = function postMountStage (handler) {
            if (!Hflow.isFunction(handler)) {
                Hflow.log(`error`, `InterfaceFactory.postMountStage - Input handler function is invalid.`);
            } else {
                Hflow.log(`warn0`, `InterfaceFactory.postMountStage - Method is not implemented by default.`);
            }
        };
        /**
         * @description - Handle logic at component predismounting stage.
         *
         * @method preDismountStage
         * @param {function} handler
         * @return void
         */
        this.preDismountStage = function preDismountStage (handler) {
            if (!Hflow.isFunction(handler)) {
                Hflow.log(`error`, `InterfaceFactory.preDismountStage - Input handler function is invalid.`);
            } else {
                Hflow.log(`warn0`, `InterfaceFactory.preDismountStage - Method is not implemented by default.`);
            }
        };
        /**
         * @description - Handle logic at component postdismounting stage.
         *
         * @method postDismountStage
         * @param {function} handler
         * @return void
         */
        this.postDismountStage = function postDismountStage (handler) {
            if (!Hflow.isFunction(handler)) {
                Hflow.log(`error`, `InterfaceFactory.postDismountStage - Input handler function is invalid.`);
            } else {
                Hflow.log(`warn0`, `InterfaceFactory.postDismountStage - Method is not implemented by default.`);
            }
        };
        /**
         * @description - Handle logic at component prepare to update stage.
         *
         * @method preUpdateStage
         * @param {function} handler
         * @return void
         */
        this.preUpdateStage = function preUpdateStage (handler) {
            if (!Hflow.isFunction(handler)) {
                Hflow.log(`error`, `InterfaceFactory.preUpdateStage - Input handler function is invalid.`);
            } else {
                Hflow.log(`warn0`, `InterfaceFactory.preUpdateStage - Method is not implemented by default.`);
            }
        };
        /**
         * @description - Handle logic at component after updating stage.
         *
         * @method postUpdateStage
         * @param {function} handler
         * @return void
         */
        this.postUpdateStage = function postUpdateStage (handler) {
            if (!Hflow.isFunction(handler)) {
                Hflow.log(`error`, `InterfaceFactory.postUpdateStage - Input handler function is invalid.`);
            } else {
                Hflow.log(`warn0`, `InterfaceFactory.postUpdateStage - Method is not implemented by default.`);
            }
        };
        /**
         * @description - Check if interface is pure or stateless.
         *
         * @method isStateless
         * @return {boolean}
         */
        this.isStateless = function isStateless () {
            return _stateless;
        };
        /**
         * @description - Get inital reflected state value.
         *
         * @method getInitialReflectedState
         * @return {object}
         */
        this.getInitialReflectedState = function getInitialReflectedState () {
            const intf = this;
            if (_stateless) {
                Hflow.log(`warn1`, `InterfaceFactory.getInitialReflectedState - Interface:${intf.name} is pure with no state reflected to a store.`);
            }
            return _initialReflectedState;
        };
        /**
         * @description - Convert interface to a component.
         *
         * @method toComponent
         * @return {object}
         */
        this.toComponent = function toComponent () {
            Hflow.log(`error`, `InterfaceFactory.toComponent - Method is not implemented by default. Implementation required.`);
        };
        /**
         * @description - Get the registered component toolkit or library.
         *
         * @method getComponentLib
         * @returns {object}
         */
        this.getComponentLib = function getComponentLib () {
            if (!Hflow.isObject(_componentLib)) {
                Hflow.log(`error`, `InterfaceFactory.getComponentLib - Interface component library is not registered.`);
            }
            return _componentLib;
        };
        /**
         * @description - Assign the registered component's reference object.
         *
         * @method assignComponentRef
         * @param {string} key
         * @returns function
         */
        this.assignComponentRef = function assignComponentRef (key) {
            if (!Hflow.isString(key)) {
                Hflow.log(`error`, `InterfaceFactory.assignComponentRef - Input component reference key is invalid.`);
            } else {
                /* helper function to set component ref */
                const setComponentRef = function setComponentRef (componentRef) {
                    _compositeRefCache[key] = Hflow.isDefined(componentRef) ? componentRef : null;
                    Hflow.log(`warn0`, `InterfaceFactory.assignComponentRef - Assigning null to component reference at key:${key}.`);
                };
                return setComponentRef;
            }
        };
        /**
         * @description - Lookup the registered component's reference object.
         *
         * @method lookupComponentRefs
         * @param {array} keys
         * @returns {array}
         */
        this.lookupComponentRefs = function lookupComponentRefs (...keys) {
            let componentRefs = [];
            if (!Hflow.isEmpty(_compositeRefCache)) {
                if (!Hflow.isEmpty(keys)) {
                    if (!keys.every((key) => Hflow.isString(key))) {
                        Hflow.log(`error`, `InterfaceFactory.lookupComponentRefs - Input component reference key is invalid.`);
                    } else if (!keys.every((key) => _compositeRefCache.hasOwnProperty(key))) {
                        Hflow.log(`error`, `InterfaceFactory.lookupComponentRefs - Component reference is not found.`);
                    } else {
                        componentRefs = Hflow.collect(_compositeRefCache, ...keys);
                    }
                } else {
                    componentRefs = Object.keys(_compositeRefCache).map((name) => _compositeRefCache[name]);
                }
            }
            return componentRefs;
        };
        /**
         * @description - Get interface component composite set.
         *
         * @method getComponentComposites
         * @param {array} intfNames
         * @return {array}
         */
        this.getComponentComposites = function getComponentComposites (...intfNames) {
            let components = [];
            if (!Hflow.isEmpty(_compositeCache)) {
                // TODO: Allows getting composite component of composite component.
                if (!Hflow.isEmpty(intfNames)) {
                    if (!intfNames.every((name) => Hflow.isString(name))) {
                        Hflow.log(`error`, `InterfaceFactory.getComponentComposites - Input interface name is invalid.`);
                    } else if (!intfNames.every((name) => {
                        if (_compositeCache.hasOwnProperty(name)) {
                            return true;
                        }
                        Hflow.log(`warn1`, `InterfaceFactory.getComponentComposites - Composite component:${name} was not composed to component cache.`);
                        return false;
                    })) {
                        Hflow.log(`error`, `InterfaceFactory.getComponentComposites - Component is not found.`);
                    } else {
                        components = Hflow.collect(_compositeCache, ...intfNames.map((name) => `${name}.component`));
                    }
                } else {
                    components = Object.keys(_compositeCache).map((name) => _compositeCache[name].component);
                }
            }
            return components;
        };
        /**
         * @description - Get interface composite set.
         *
         * @method getInterfaceComposites
         * @param {array} intfNames
         * @return {array}
         */
        this.getInterfaceComposites = function getInterfaceComposites (...intfNames) {
            let intfs = [];
            if (!Hflow.isEmpty(_compositeCache)) {
                // TODO: Remove this method if find no use case.
                // TODO: Allows getting composite interface of composite interface.
                if (!Hflow.isEmpty(intfNames)) {
                    if (!intfNames.every((name) => Hflow.isString(name))) {
                        Hflow.log(`error`, `InterfaceFactory.getInterfaceComposites - Input interface name is invalid.`);
                    } else if (!intfNames.every((name) => _compositeCache.hasOwnProperty(name))) {
                        Hflow.log(`error`, `InterfaceFactory.getInterfaceComposites - Composite is not found.`);
                    } else {
                        intfs = Hflow.collect(_compositeCache, ...intfNames.map((name) => `${name}.intf`));
                    }
                } else {
                    intfs = Object.keys(_compositeCache).map((name) => _compositeCache[name].intf);
                }
            }
            return intfs;
        };
        /**
         * @description - Register a component toolkit or library for interface.
         *
         * @method registerComponentLib
         * @param {object} componentLib
         * @returns void
         */
        this.registerComponentLib = function registerComponentLib (componentLib) {
            if (!Hflow.isObject(componentLib)) {
                Hflow.log(`error`, `InterfaceFactory.registerComponentLib - Input component library is invalid.`);
            } else {
                _componentLib = componentLib;
                if (!Hflow.isEmpty(_compositeCache)) {
                    _compositeCache = Hflow.collect(_compositeCache, ...Object.keys(_compositeCache).map((name) => {
                        return `${name}.intf`;
                    })).reduce((compositeBundle, compositeIntf) => {
                        compositeIntf.registerComponentLib(_componentLib);

                        const component = compositeIntf.toComponent();
                        if (!Hflow.isFunction(component)) {
                            Hflow.log(`error`, `InterfaceFactory.registerComponentLib - Component is invalid.`);
                        } else {
                            compositeBundle[compositeIntf.name].component = component;
                        }
                        return compositeBundle;
                    }, _compositeCache);
                }
            }
        };
        /**
         * @description - Set the composite interfaces that this interface is composed of.
         *
         * @method composedOf
         * @param {array} compositeIntfs
         * @return void
         */
        this.composedOf = function composedOf (...compositeIntfs) {
            const intf = this;
            // TODO: If possible, rename method to compose and return a newly created interface instead.
            if (Hflow.isEmpty(compositeIntfs)) {
                Hflow.log(`warn0`, `InterfaceFactory.composedOf - Input composite interface array is empty.`);
            } else {
                if (!compositeIntfs.every((compositeIntf) => {
                    return Hflow.isSchema({
                        fId: `string`,
                        name: `string`,
                        toComponent: `function`,
                        registerComponentLib: `function`
                    }).of(compositeIntf) && compositeIntf.fId.substr(0, INTERFACE_FACTORY_CODE.length) === INTERFACE_FACTORY_CODE;
                })) {
                    Hflow.log(`error`, `InterfaceFactory.composedOf - Input composite interfaces are invalid.`);
                } else if (compositeIntfs.some((compositeIntf) => intf.name === compositeIntf.name)) {
                    Hflow.log(`error`, `InterfaceFactory.composedOf - Cannot compose interface:${intf.name} as a composite of itself.`);
                } else {
                    const newCompositeInterfaces = compositeIntfs.filter((compositeIntf) => {
                        if (Object.keys(_compositeCache).some((name) => name === compositeIntf.name)) {
                            Hflow.log(`warn1`, `InterfaceFactory.composedOf - Interface:${intf.name} is already composed of composite interface:${compositeIntf.name}.`);
                            return false;
                        }
                        return true;
                    });
                    _compositeCache = newCompositeInterfaces.map((compositeIntf) => {
                        intf.observe(compositeIntf).debounce(DEBOUNCE_COMPOSITE_INTERFACE_IN_MS);
                        Hflow.log(`info`, `Interface:${intf.name} is composed of composite interface:${compositeIntf.name}.`);
                        return compositeIntf;
                    }).reduce((compositeBundle, compositeIntf) => {
                        compositeBundle[compositeIntf.name] = {
                            intf: compositeIntf,
                            component: null
                        };
                        return compositeBundle;
                    }, _compositeCache);
                    return intf;
                }
            }
        };
        this.reflectStateOf = function reflectStateOf (store) {
            if (!Hflow.isSchema({
                fId: `string`,
                name: `string`,
                getStateAsObject: `function`
            }).of(store) || store.fId.substr(0, STORE_FACTORY_CODE.length) !== STORE_FACTORY_CODE) {
                Hflow.log(`error`, `InterfaceFactory.reflectStateOf - Input store is invalid.`);
            } else {
                const intf = this;
                if (_stateless) {
                    _initialReflectedState = Hflow.mix(store.getStateAsObject(), {
                        exclusion: {
                            keys: [
                                `name`,
                                `fId`
                            ]
                        }
                    }).with({});
                    intf.incoming(`as-state-mutated`).handle((reflectedState) => {
                        /* update initial reflected state snapshot as state from store mutated */
                        _initialReflectedState = reflectedState;
                    });
                    _stateless = false;
                    Hflow.log(`info`, `Interface:${intf.name} is reflecting state of store:${store.name}.`);
                } else {
                    Hflow.log(`warn1`, `InterfaceFactory.reflectStateOf - Interface:${intf.name} is already has its state mirrored with a store.`);
                }
            }
        };
    }
});
<|endoftext|>


<commit_before>// Rendering modes

var GL = require('./gl.js');
var GLBuilders = require('./gl_builders.js');
var GLGeometry = require('./gl_geom.js');
var GLVertexLayout = require('./gl_vertex_layout.js');
var GLProgram = require('./gl_program.js');
var GLTexture = require('./gl_texture.js');
var shader_sources = require('./gl_shaders.js'); // built-in shaders

// Base

var RenderMode = {
    init: function (gl) {
        this.gl = gl;
        this.makeGLProgram();

        if (typeof this._init == 'function') {
            this._init();
        }
    },
    refresh: function () {
        this.makeGLProgram();
    },
    defines: {},
    selection: false,
    buildPolygons: function(){}, // build functions are no-ops until overriden
    buildLines: function(){},
    buildPoints: function(){},
    makeGLGeometry: function (vertex_data) {
        return new GLGeometry(this.gl, vertex_data, this.vertex_layout);
    }
};

RenderMode.makeGLProgram = function ()
{
    // Add any custom defines to built-in mode defines
    var defines = {}; // create a new object to avoid mutating a prototype value that may be shared with other modes
    if (this.defines != null) {
        for (var d in this.defines) {
            defines[d] = this.defines[d];
        }
    }
    if (this.shaders != null && this.shaders.defines != null) {
        for (var d in this.shaders.defines) {
            defines[d] = this.shaders.defines[d];
        }
    }

    // Alter defines for selection (need to create a new object since the first is stored as a reference by the program)
    if (this.selection) {
        var selection_defines = Object.create(defines);
        selection_defines['FEATURE_SELECTION'] = true;
    }

    // Get any custom code transforms
    var transforms = (this.shaders && this.shaders.transforms);

    // Create shader from custom URLs
    if (this.shaders && this.shaders.vertex_url && this.shaders.fragment_url) {
        this.gl_program = GLProgram.createProgramFromURLs(
            this.gl,
            this.shaders.vertex_url,
            this.shaders.fragment_url,
            { defines: defines, transforms: transforms, name: this.name }
        );

        if (this.selection) {
            this.selection_gl_program = new GLProgram(
                this.gl,
                this.gl_program.vertex_shader_source,
                shader_sources['selection_fragment'],
                { defines: selection_defines, transforms: transforms, name: (this.name + ' (selection)') }
            );
        }
    }
    // Create shader from built-in source
    else {
        this.gl_program = new GLProgram(
            this.gl,
            shader_sources[this.vertex_shader_key],
            shader_sources[this.fragment_shader_key],
            { defines: defines, transforms: transforms, name: this.name }
        );

        if (this.selection) {
            this.selection_gl_program = new GLProgram(
                this.gl,
                shader_sources[this.vertex_shader_key],
                shader_sources['selection_fragment'],
                { defines: selection_defines, transforms: transforms, name: (this.name + ' (selection)') }
            );
       }
    }
};

// TODO: make this a generic ORM-like feature for setting uniforms via JS objects on GLProgram
RenderMode.setUniforms = function (options)
{
    options = options || {};
    var gl_program = GLProgram.current; // operate on currently bound program

    // TODO: only update uniforms when changed
    if (this.shaders != null && this.shaders.uniforms != null) {
        var texture_unit = 0;

        for (var u in this.shaders.uniforms) {
            var uniform = this.shaders.uniforms[u];

            // Single float
            if (typeof uniform == 'number') {
                gl_program.uniform('1f', u, uniform);
            }
            // Multiple floats - vector or array
            else if (typeof uniform == 'object') {
                // float vectors (vec2, vec3, vec4)
                if (uniform.length >= 2 && uniform.length <= 4) {
                    gl_program.uniform(uniform.length + 'fv', u, uniform);
                }
                // float array
                else if (uniform.length > 4) {
                    gl_program.uniform('1fv', u + '[0]', uniform);
                }
                // TODO: assume matrix for (typeof == Float32Array && length == 16)?
            }
            // Boolean
            else if (typeof this.shaders.uniforms[u] == 'boolean') {
                gl_program.uniform('1i', u, uniform);
            }
            // Texture
            else if (typeof uniform == 'string') {
                var texture = GLTexture.textures[uniform];
                if (texture == null) {
                    texture = new GLTexture(this.gl, uniform);
                    texture.load(uniform);
                }

                texture.bind(texture_unit);
                gl_program.uniform('1i', u, texture_unit);
                texture_unit++;
            }
            // TODO: support other non-float types? (int, etc.)
        }
    }
};

RenderMode.update = function ()
{
    // Mode-specific animation
    if (typeof this.animation == 'function') {
        this.animation();
    }
};

var Modes = {};
var ModeManager = {};

// Update built-in mode or create a new one
ModeManager.configureMode = function (name, settings)
{
    Modes[name] = Modes[name] || Object.create(Modes[settings.extends] || RenderMode);
    if (Modes[settings.extends]) {
        Modes[name].parent = Modes[settings.extends]; // explicit 'super' class access
    }

    for (var s in settings) {
        Modes[name][s] = settings[s];
    }

    Modes[name].name = name;
    return Modes[name];
};

// Built-in rendering modes

/*** Plain polygons ***/

Modes.polygons = Object.create(RenderMode);
Modes.polygons.name = 'polygons';

Modes.polygons.vertex_shader_key = 'polygon_vertex';
Modes.polygons.fragment_shader_key = 'polygon_fragment';

Modes.polygons.defines = {
    'WORLD_POSITION_WRAP': 100000 // default world coords to wrap every 100,000 meters, can turn off by setting this to 'false'
};

Modes.polygons.selection = true;

Modes.polygons._init = function () {
    this.vertex_layout = new GLVertexLayout(this.gl, [
        { name: 'a_position', size: 3, type: this.gl.FLOAT, normalized: false },
        { name: 'a_normal', size: 3, type: this.gl.FLOAT, normalized: false },
        { name: 'a_color', size: 3, type: this.gl.FLOAT, normalized: false },
        { name: 'a_selection_color', size: 4, type: this.gl.FLOAT, normalized: false },
        { name: 'a_layer', size: 1, type: this.gl.FLOAT, normalized: false }
    ]);
};

Modes.polygons.buildPolygons = function (polygons, style, vertex_data)
{
    // Color and layer number are currently constant across vertices
    var vertex_constants = [
        style.color[0], style.color[1], style.color[2],
        style.selection.color[0], style.selection.color[1], style.selection.color[2], style.selection.color[3],
        style.layer_num
    ];

    // Outlines have a slightly different set of constants, because the layer number is modified
    if (style.outline.color) {
        var outline_vertex_constants = [
            style.outline.color[0], style.outline.color[1], style.outline.color[2],
            style.selection.color[0], style.selection.color[1], style.selection.color[2], style.selection.color[3],
            style.layer_num - 0.5 // outlines sit between layers, underneath current layer but above the one below
        ];
    }

    // Extruded polygons (e.g. 3D buildings)
    if (style.extrude && style.height) {
        GLBuilders.buildExtrudedPolygons(
            polygons,
            style.z,
            style.height,
            style.min_height,
            vertex_data,
            {
                vertex_constants: vertex_constants
            }
        );
    }
    // Regular polygons
    else {
        GLBuilders.buildPolygons(
            polygons,
            style.z,
            vertex_data,
            {
                normals: true,
                vertex_constants: vertex_constants
            }
        );

        // Callback-base builder (for future exploration)
        // var normal_vertex_constants = [0, 0, 1].concat(vertex_constants);
        // GLBuilders.buildPolygons2(
        //     polygons,
        //     z,
        //     function (vertices) {
        //         // var vs = vertices.positions;
        //         // for (var v in vs) {
        //         //     // var bc = [(v % 3) ? 0 : 1, ((v + 1) % 3) ? 0 : 1, ((v + 2) % 3) ? 0 : 1];
        //         //     // var bc = [centroid.x, centroid.y, 0];
        //         //     // vs[v] = vertices.positions[v].concat(z, 0, 0, 1, bc);

        //         //     // vs[v] = vertices.positions[v].concat(z, 0, 0, 1);
        //         //     vs[v] = vertices.positions[v].concat(0, 0, 1);
        //         // }

        //         GL.addVertices(vertices.positions, normal_vertex_constants, vertex_data);

        //         // GL.addVerticesByAttributeLayout(
        //         //     [
        //         //         { name: 'a_position', data: vertices.positions },
        //         //         { name: 'a_normal', data: [0, 0, 1] },
        //         //         { name: 'a_color', data: [style.color[0], style.color[1], style.color[2]] },
        //         //         { name: 'a_layer', data: style.layer_num }
        //         //     ],
        //         //     vertex_data
        //         // );

        //         // GL.addVerticesMultipleAttributes([vertices.positions], normal_vertex_constants, vertex_data);
        //     }
        // );
    }

    // Polygon outlines
    if (style.outline.color && style.outline.width) {
        for (var mpc=0; mpc < polygons.length; mpc++) {
            GLBuilders.buildPolylines(
                polygons[mpc],
                style.z,
                style.outline.width,
                vertex_data,
                {
                    closed_polygon: true,
                    remove_tile_edges: true,
                    vertex_constants: outline_vertex_constants
                }
            );
        }
    }
};

Modes.polygons.buildLines = function (lines, style, vertex_data)
{
    // TOOD: reduce redundancy of constant calc between builders
    // Color and layer number are currently constant across vertices
    var vertex_constants = [
        style.color[0], style.color[1], style.color[2],
        style.selection.color[0], style.selection.color[1], style.selection.color[2], style.selection.color[3],
        style.layer_num
    ];

    // Outlines have a slightly different set of constants, because the layer number is modified
    if (style.outline.color) {
        var outline_vertex_constants = [
            style.outline.color[0], style.outline.color[1], style.outline.color[2],
            style.selection.color[0], style.selection.color[1], style.selection.color[2], style.selection.color[3],
            style.layer_num - 0.5 // outlines sit between layers, underneath current layer but above the one below
        ];
    }

    // Main lines
    GLBuilders.buildPolylines(
        lines,
        style.z,
        style.width,
        vertex_data,
        {
            vertex_constants: vertex_constants
        }
    );

    // Line outlines
    if (style.outline.color && style.outline.width) {
        GLBuilders.buildPolylines(
            lines,
            style.z,
            style.width + 2 * style.outline.width,
            vertex_data,
            {
                vertex_constants: outline_vertex_constants
            }
        );
    }
};

Modes.polygons.buildPoints = function (points, style, vertex_data)
{
    // TOOD: reduce redundancy of constant calc between builders
    // Color and layer number are currently constant across vertices
    var vertex_constants = [
        style.color[0], style.color[1], style.color[2],
        style.selection.color[0], style.selection.color[1], style.selection.color[2], style.selection.color[3],
        style.layer_num
    ];

    GLBuilders.buildQuadsForPoints(
        points,
        style.size * 2,
        style.size * 2,
        style.z,
        vertex_data,
        {
            normals: true,
            texcoords: false,
            vertex_constants: vertex_constants
        }
    );
};

/*** Points w/simple distance field rendering ***/

Modes.points = Object.create(RenderMode);
Modes.points.name = 'points';

Modes.points.vertex_shader_key = 'point_vertex';
Modes.points.fragment_shader_key = 'point_fragment';

Modes.points.defines = {
    'EFFECT_SCREEN_COLOR': true
};

Modes.points.selection = true;

Modes.points._init = function () {
    this.vertex_layout = new GLVertexLayout(this.gl, [
        { name: 'a_position', size: 3, type: this.gl.FLOAT, normalized: false },
        { name: 'a_texcoord', size: 2, type: this.gl.FLOAT, normalized: false },
        { name: 'a_color', size: 3, type: this.gl.FLOAT, normalized: false },
        { name: 'a_selection_color', size: 4, type: this.gl.FLOAT, normalized: false },
        { name: 'a_layer', size: 1, type: this.gl.FLOAT, normalized: false }
    ]);
};

Modes.points.buildPoints = function (points, style, vertex_data)
{
    // TOOD: reduce redundancy of constant calc between builders
    // Color and layer number are currently constant across vertices
    var vertex_constants = [
        style.color[0], style.color[1], style.color[2],
        style.selection.color[0], style.selection.color[1], style.selection.color[2], style.selection.color[3],
        style.layer_num
    ];

    GLBuilders.buildQuadsForPoints(
        points,
        style.size * 2,
        style.size * 2,
        style.z,
        vertex_data,
        {
            normals: false,
            texcoords: true,
            vertex_constants: vertex_constants
        }
    );
};

if (module !== undefined) {
    module.exports = {
        ModeManager: ModeManager,
        Modes: Modes
    };
}
<commit_msg>don't update GL program instance vars for modes until programs finish compiling<commit_after>// Rendering modes

var GL = require('./gl.js');
var GLBuilders = require('./gl_builders.js');
var GLGeometry = require('./gl_geom.js');
var GLVertexLayout = require('./gl_vertex_layout.js');
var GLProgram = require('./gl_program.js');
var GLTexture = require('./gl_texture.js');
var shader_sources = require('./gl_shaders.js'); // built-in shaders

var Queue = require('queue-async');

// Base

var RenderMode = {
    init: function (gl) {
        this.gl = gl;
        this.makeGLProgram();

        if (typeof this._init == 'function') {
            this._init();
        }
    },
    refresh: function () { // TODO: should this be async/non-blocking?
        this.makeGLProgram();
    },
    defines: {},
    selection: false,
    buildPolygons: function(){}, // build functions are no-ops until overriden
    buildLines: function(){},
    buildPoints: function(){},
    makeGLGeometry: function (vertex_data) {
        return new GLGeometry(this.gl, vertex_data, this.vertex_layout);
    }
};

// TODO: should this entire operation be async/non-blocking?
// TODO: don't re-create GLProgram instance every time, just update existing one
RenderMode.makeGLProgram = function ()
{
    // Add any custom defines to built-in mode defines
    var defines = {}; // create a new object to avoid mutating a prototype value that may be shared with other modes
    if (this.defines != null) {
        for (var d in this.defines) {
            defines[d] = this.defines[d];
        }
    }
    if (this.shaders != null && this.shaders.defines != null) {
        for (var d in this.shaders.defines) {
            defines[d] = this.shaders.defines[d];
        }
    }

    // Alter defines for selection (need to create a new object since the first is stored as a reference by the program)
    if (this.selection) {
        var selection_defines = Object.create(defines);
        selection_defines['FEATURE_SELECTION'] = true;
    }

    // Get any custom code transforms
    var transforms = (this.shaders && this.shaders.transforms);

    // Create shader from custom URLs
    var program, selection_program;
    var queue = Queue();

    if (this.shaders && this.shaders.vertex_url && this.shaders.fragment_url) {
        queue.defer(function(complete) {
            program = GLProgram.createProgramFromURLs(
                this.gl,
                this.shaders.vertex_url,
                this.shaders.fragment_url,
                { defines: defines, transforms: transforms, name: this.name, callback: complete }
            );
        }.bind(this));

        if (this.selection) {
            queue.defer(function(complete) {
                selection_program = new GLProgram(
                    this.gl,
                    this.gl_program.vertex_shader_source,
                    shader_sources['selection_fragment'],
                    { defines: selection_defines, transforms: transforms, name: (this.name + ' (selection)'), callback: complete }
                );
            }.bind(this));
        }
    }
    // Create shader from built-in source
    else {
        queue.defer(function(complete) {
            program = new GLProgram(
                this.gl,
                shader_sources[this.vertex_shader_key],
                shader_sources[this.fragment_shader_key],
                { defines: defines, transforms: transforms, name: this.name, callback: complete }
            );
        }.bind(this));

        if (this.selection) {
            queue.defer(function(complete) {
                selection_program = new GLProgram(
                    this.gl,
                    shader_sources[this.vertex_shader_key],
                    shader_sources['selection_fragment'],
                    { defines: selection_defines, transforms: transforms, name: (this.name + ' (selection)'), callback: complete }
                );
            }.bind(this));
        }
    }

    // Wait for program(s) to compile before replacing them
    queue.await(function() {
        if (program) {
            this.gl_program = program;
        }

        if (selection_program) {
            this.selection_gl_program = selection_program;
        }
    }.bind(this));
};

// TODO: make this a generic ORM-like feature for setting uniforms via JS objects on GLProgram
RenderMode.setUniforms = function (options)
{
    options = options || {};
    var gl_program = GLProgram.current; // operate on currently bound program

    // TODO: only update uniforms when changed
    if (this.shaders != null && this.shaders.uniforms != null) {
        var texture_unit = 0;

        for (var u in this.shaders.uniforms) {
            var uniform = this.shaders.uniforms[u];

            // Single float
            if (typeof uniform == 'number') {
                gl_program.uniform('1f', u, uniform);
            }
            // Multiple floats - vector or array
            else if (typeof uniform == 'object') {
                // float vectors (vec2, vec3, vec4)
                if (uniform.length >= 2 && uniform.length <= 4) {
                    gl_program.uniform(uniform.length + 'fv', u, uniform);
                }
                // float array
                else if (uniform.length > 4) {
                    gl_program.uniform('1fv', u + '[0]', uniform);
                }
                // TODO: assume matrix for (typeof == Float32Array && length == 16)?
            }
            // Boolean
            else if (typeof this.shaders.uniforms[u] == 'boolean') {
                gl_program.uniform('1i', u, uniform);
            }
            // Texture
            else if (typeof uniform == 'string') {
                var texture = GLTexture.textures[uniform];
                if (texture == null) {
                    texture = new GLTexture(this.gl, uniform);
                    texture.load(uniform);
                }

                texture.bind(texture_unit);
                gl_program.uniform('1i', u, texture_unit);
                texture_unit++;
            }
            // TODO: support other non-float types? (int, etc.)
        }
    }
};

RenderMode.update = function ()
{
    // Mode-specific animation
    if (typeof this.animation == 'function') {
        this.animation();
    }
};

var Modes = {};
var ModeManager = {};

// Update built-in mode or create a new one
ModeManager.configureMode = function (name, settings)
{
    Modes[name] = Modes[name] || Object.create(Modes[settings.extends] || RenderMode);
    if (Modes[settings.extends]) {
        Modes[name].parent = Modes[settings.extends]; // explicit 'super' class access
    }

    for (var s in settings) {
        Modes[name][s] = settings[s];
    }

    Modes[name].name = name;
    return Modes[name];
};

// Built-in rendering modes

/*** Plain polygons ***/

Modes.polygons = Object.create(RenderMode);
Modes.polygons.name = 'polygons';

Modes.polygons.vertex_shader_key = 'polygon_vertex';
Modes.polygons.fragment_shader_key = 'polygon_fragment';

Modes.polygons.defines = {
    'WORLD_POSITION_WRAP': 100000 // default world coords to wrap every 100,000 meters, can turn off by setting this to 'false'
};

Modes.polygons.selection = true;

Modes.polygons._init = function () {
    this.vertex_layout = new GLVertexLayout(this.gl, [
        { name: 'a_position', size: 3, type: this.gl.FLOAT, normalized: false },
        { name: 'a_normal', size: 3, type: this.gl.FLOAT, normalized: false },
        { name: 'a_color', size: 3, type: this.gl.FLOAT, normalized: false },
        { name: 'a_selection_color', size: 4, type: this.gl.FLOAT, normalized: false },
        { name: 'a_layer', size: 1, type: this.gl.FLOAT, normalized: false }
    ]);
};

Modes.polygons.buildPolygons = function (polygons, style, vertex_data)
{
    // Color and layer number are currently constant across vertices
    var vertex_constants = [
        style.color[0], style.color[1], style.color[2],
        style.selection.color[0], style.selection.color[1], style.selection.color[2], style.selection.color[3],
        style.layer_num
    ];

    // Outlines have a slightly different set of constants, because the layer number is modified
    if (style.outline.color) {
        var outline_vertex_constants = [
            style.outline.color[0], style.outline.color[1], style.outline.color[2],
            style.selection.color[0], style.selection.color[1], style.selection.color[2], style.selection.color[3],
            style.layer_num - 0.5 // outlines sit between layers, underneath current layer but above the one below
        ];
    }

    // Extruded polygons (e.g. 3D buildings)
    if (style.extrude && style.height) {
        GLBuilders.buildExtrudedPolygons(
            polygons,
            style.z,
            style.height,
            style.min_height,
            vertex_data,
            {
                vertex_constants: vertex_constants
            }
        );
    }
    // Regular polygons
    else {
        GLBuilders.buildPolygons(
            polygons,
            style.z,
            vertex_data,
            {
                normals: true,
                vertex_constants: vertex_constants
            }
        );

        // Callback-base builder (for future exploration)
        // var normal_vertex_constants = [0, 0, 1].concat(vertex_constants);
        // GLBuilders.buildPolygons2(
        //     polygons,
        //     z,
        //     function (vertices) {
        //         // var vs = vertices.positions;
        //         // for (var v in vs) {
        //         //     // var bc = [(v % 3) ? 0 : 1, ((v + 1) % 3) ? 0 : 1, ((v + 2) % 3) ? 0 : 1];
        //         //     // var bc = [centroid.x, centroid.y, 0];
        //         //     // vs[v] = vertices.positions[v].concat(z, 0, 0, 1, bc);

        //         //     // vs[v] = vertices.positions[v].concat(z, 0, 0, 1);
        //         //     vs[v] = vertices.positions[v].concat(0, 0, 1);
        //         // }

        //         GL.addVertices(vertices.positions, normal_vertex_constants, vertex_data);

        //         // GL.addVerticesByAttributeLayout(
        //         //     [
        //         //         { name: 'a_position', data: vertices.positions },
        //         //         { name: 'a_normal', data: [0, 0, 1] },
        //         //         { name: 'a_color', data: [style.color[0], style.color[1], style.color[2]] },
        //         //         { name: 'a_layer', data: style.layer_num }
        //         //     ],
        //         //     vertex_data
        //         // );

        //         // GL.addVerticesMultipleAttributes([vertices.positions], normal_vertex_constants, vertex_data);
        //     }
        // );
    }

    // Polygon outlines
    if (style.outline.color && style.outline.width) {
        for (var mpc=0; mpc < polygons.length; mpc++) {
            GLBuilders.buildPolylines(
                polygons[mpc],
                style.z,
                style.outline.width,
                vertex_data,
                {
                    closed_polygon: true,
                    remove_tile_edges: true,
                    vertex_constants: outline_vertex_constants
                }
            );
        }
    }
};

Modes.polygons.buildLines = function (lines, style, vertex_data)
{
    // TOOD: reduce redundancy of constant calc between builders
    // Color and layer number are currently constant across vertices
    var vertex_constants = [
        style.color[0], style.color[1], style.color[2],
        style.selection.color[0], style.selection.color[1], style.selection.color[2], style.selection.color[3],
        style.layer_num
    ];

    // Outlines have a slightly different set of constants, because the layer number is modified
    if (style.outline.color) {
        var outline_vertex_constants = [
            style.outline.color[0], style.outline.color[1], style.outline.color[2],
            style.selection.color[0], style.selection.color[1], style.selection.color[2], style.selection.color[3],
            style.layer_num - 0.5 // outlines sit between layers, underneath current layer but above the one below
        ];
    }

    // Main lines
    GLBuilders.buildPolylines(
        lines,
        style.z,
        style.width,
        vertex_data,
        {
            vertex_constants: vertex_constants
        }
    );

    // Line outlines
    if (style.outline.color && style.outline.width) {
        GLBuilders.buildPolylines(
            lines,
            style.z,
            style.width + 2 * style.outline.width,
            vertex_data,
            {
                vertex_constants: outline_vertex_constants
            }
        );
    }
};

Modes.polygons.buildPoints = function (points, style, vertex_data)
{
    // TOOD: reduce redundancy of constant calc between builders
    // Color and layer number are currently constant across vertices
    var vertex_constants = [
        style.color[0], style.color[1], style.color[2],
        style.selection.color[0], style.selection.color[1], style.selection.color[2], style.selection.color[3],
        style.layer_num
    ];

    GLBuilders.buildQuadsForPoints(
        points,
        style.size * 2,
        style.size * 2,
        style.z,
        vertex_data,
        {
            normals: true,
            texcoords: false,
            vertex_constants: vertex_constants
        }
    );
};

/*** Points w/simple distance field rendering ***/

Modes.points = Object.create(RenderMode);
Modes.points.name = 'points';

Modes.points.vertex_shader_key = 'point_vertex';
Modes.points.fragment_shader_key = 'point_fragment';

Modes.points.defines = {
    'EFFECT_SCREEN_COLOR': true
};

Modes.points.selection = true;

Modes.points._init = function () {
    this.vertex_layout = new GLVertexLayout(this.gl, [
        { name: 'a_position', size: 3, type: this.gl.FLOAT, normalized: false },
        { name: 'a_texcoord', size: 2, type: this.gl.FLOAT, normalized: false },
        { name: 'a_color', size: 3, type: this.gl.FLOAT, normalized: false },
        { name: 'a_selection_color', size: 4, type: this.gl.FLOAT, normalized: false },
        { name: 'a_layer', size: 1, type: this.gl.FLOAT, normalized: false }
    ]);
};

Modes.points.buildPoints = function (points, style, vertex_data)
{
    // TOOD: reduce redundancy of constant calc between builders
    // Color and layer number are currently constant across vertices
    var vertex_constants = [
        style.color[0], style.color[1], style.color[2],
        style.selection.color[0], style.selection.color[1], style.selection.color[2], style.selection.color[3],
        style.layer_num
    ];

    GLBuilders.buildQuadsForPoints(
        points,
        style.size * 2,
        style.size * 2,
        style.z,
        vertex_data,
        {
            normals: false,
            texcoords: true,
            vertex_constants: vertex_constants
        }
    );
};

if (module !== undefined) {
    module.exports = {
        ModeManager: ModeManager,
        Modes: Modes
    };
}
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */

'use strict';

var babel = require('gulp-babel');
var del = require('del');
var cleanCSS = require('gulp-clean-css');
var concatCSS = require('gulp-concat-css');
var derequire = require('gulp-derequire');
var flatten = require('gulp-flatten');
var gulp = require('gulp');
var gulpif = require('gulp-if');
var gulpUtil = require('gulp-util');
var header = require('gulp-header');
var packageData = require('./package.json');
var rename = require('gulp-rename');
var runSequence = require('run-sequence');
var StatsPlugin = require('stats-webpack-plugin');
var through = require('through2');
var UglifyJsPlugin = require('uglifyjs-webpack-plugin');
var webpackStream = require('webpack-stream');

var fbjsConfigurePreset = require('babel-preset-fbjs/configure');
var gulpCheckDependencies = require('fbjs-scripts/gulp/check-dependencies');

var moduleMap = require('./scripts/module-map');

var paths = {
  dist: 'dist',
  lib: 'lib',
  src: [
    'src/**/*.js',
    '!src/**/__tests__/**/*.js',
    '!src/**/__mocks__/**/*.js',
  ],
  css: ['src/**/*.css'],
};

var babelOptsJS = {
  presets: [
    fbjsConfigurePreset({
      stripDEV: true,
      rewriteModules: {map: moduleMap},
    }),
  ],
};

var babelOptsFlow = {
  presets: [
    fbjsConfigurePreset({
      target: 'flow',
      rewriteModules: {map: moduleMap},
    }),
  ],
};

var COPYRIGHT_HEADER = `/**
 * Draft v<%= version %>
 *
 * Copyright (c) 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */
`;

var buildDist = function(opts) {
  var webpackOpts = {
    externals: {
      immutable: {
        root: 'Immutable',
        commonjs2: 'immutable',
        commonjs: 'immutable',
        amd: 'immutable',
      },
      react: {
        root: 'React',
        commonjs2: 'react',
        commonjs: 'react',
        amd: 'react',
      },
      'react-dom': {
        root: 'ReactDOM',
        commonjs2: 'react-dom',
        commonjs: 'react-dom',
        amd: 'react-dom',
      },
    },
    output: {
      filename: opts.output,
      libraryTarget: 'umd',
      library: 'Draft',
    },
    plugins: [
      new webpackStream.webpack.DefinePlugin({
        'process.env.NODE_ENV': JSON.stringify(
          opts.debug ? 'development' : 'production',
        ),
      }),
      new webpackStream.webpack.LoaderOptionsPlugin({
        debug: opts.debug,
      }),
      new StatsPlugin(`../meta/bundle-size-stats/${opts.output}.json`, {
        chunkModules: true,
      }),
    ],
  };
  if (!opts.debug) {
    webpackOpts.plugins.push(new UglifyJsPlugin());
  }
  return webpackStream(webpackOpts, null, function(err, stats) {
    if (err) {
      throw new gulpUtil.PluginError('webpack', err);
    }
    if (stats.compilation.errors.length) {
      gulpUtil.log('webpack', '\n' + stats.toString({colors: true}));
    }
  });
};

gulp.task('clean', function() {
  return del([paths.dist, paths.lib]);
});

gulp.task('modules', function() {
  return gulp
    .src(paths.src)
    .pipe(babel(babelOptsJS))
    .pipe(flatten())
    .pipe(gulp.dest(paths.lib));
});

gulp.task('flow', function() {
  return gulp
    .src(paths.src)
    .pipe(babel(babelOptsFlow))
    .pipe(flatten())
    .pipe(rename({extname: '.js.flow'}))
    .pipe(gulp.dest(paths.lib));
});

gulp.task('css', function() {
  return (gulp
      .src(paths.css)
      .pipe(
        through.obj(function(file, encoding, callback) {
          var contents = file.contents.toString();
          var replaced = contents.replace(
            // Regex based on MakeHasteCssModuleTransform: ignores comments,
            // strings, and URLs
            /\/\*.*?\*\/|'(?:\\.|[^'])*'|"(?:\\.|[^"])*"|url\([^)]*\)|(\.(?:public\/)?[\w-]*\/{1,2}[\w-]+)/g,
            function(match, cls) {
              if (cls) {
                return cls.replace(/\//g, '-');
              } else {
                return match;
              }
            },
          );
          replaced = replaced.replace(
            // MakeHasteCssVariablesTransform
            /\bvar\(([\w-]+)\)/g,
            function(match, name) {
              var vars = {
                'fig-secondary-text': '#9197a3',
                'fig-light-20': '#bdc1c9',
              };
              if (vars[name]) {
                return vars[name];
              } else {
                throw new Error('Unknown CSS variable ' + name);
              }
            },
          );
          file.contents = new Buffer(replaced);
          callback(null, file);
        }),
      )
      .pipe(concatCSS('Draft.css'))
      // Avoid rewriting rules *just in case*, just compress
      .pipe(cleanCSS({advanced: false}))
      .pipe(header(COPYRIGHT_HEADER, {version: packageData.version}))
      .pipe(gulp.dest(paths.dist)) );
});

gulp.task('dist', ['modules', 'css'], function() {
  var opts = {
    debug: true,
    output: 'Draft.js',
  };
  return gulp
    .src('./lib/Draft.js')
    .pipe(buildDist(opts))
    .pipe(derequire())
    .pipe(
      gulpif('*.js', header(COPYRIGHT_HEADER, {version: packageData.version})),
    )
    .pipe(gulp.dest(paths.dist));
});

gulp.task('dist:min', ['modules'], function() {
  var opts = {
    debug: false,
    output: 'Draft.min.js',
  };
  return gulp
    .src('./lib/Draft.js')
    .pipe(buildDist(opts))
    .pipe(
      gulpif('*.js', header(COPYRIGHT_HEADER, {version: packageData.version})),
    )
    .pipe(gulp.dest(paths.dist));
});

gulp.task('check-dependencies', function() {
  return gulp.src('package.json').pipe(gulpCheckDependencies());
});

gulp.task('watch', function() {
  gulp.watch(paths.src, ['modules']);
});

gulp.task('dev', function() {
  gulp.watch(paths.src, ['modules', 'dist']);
});

gulp.task('default', function(cb) {
  runSequence(
    'check-dependencies',
    'clean',
    ['modules', 'flow'],
    ['dist', 'dist:min'],
    cb,
  );
});
<commit_msg>Refactor buffer constructor (#1912)<commit_after>/**
 * Copyright (c) 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */

'use strict';

var babel = require('gulp-babel');
var del = require('del');
var cleanCSS = require('gulp-clean-css');
var concatCSS = require('gulp-concat-css');
var derequire = require('gulp-derequire');
var flatten = require('gulp-flatten');
var gulp = require('gulp');
var gulpif = require('gulp-if');
var gulpUtil = require('gulp-util');
var header = require('gulp-header');
var packageData = require('./package.json');
var rename = require('gulp-rename');
var runSequence = require('run-sequence');
var StatsPlugin = require('stats-webpack-plugin');
var through = require('through2');
var UglifyJsPlugin = require('uglifyjs-webpack-plugin');
var webpackStream = require('webpack-stream');

var fbjsConfigurePreset = require('babel-preset-fbjs/configure');
var gulpCheckDependencies = require('fbjs-scripts/gulp/check-dependencies');

var moduleMap = require('./scripts/module-map');

var paths = {
  dist: 'dist',
  lib: 'lib',
  src: [
    'src/**/*.js',
    '!src/**/__tests__/**/*.js',
    '!src/**/__mocks__/**/*.js',
  ],
  css: ['src/**/*.css'],
};

var babelOptsJS = {
  presets: [
    fbjsConfigurePreset({
      stripDEV: true,
      rewriteModules: {map: moduleMap},
    }),
  ],
};

var babelOptsFlow = {
  presets: [
    fbjsConfigurePreset({
      target: 'flow',
      rewriteModules: {map: moduleMap},
    }),
  ],
};

var COPYRIGHT_HEADER = `/**
 * Draft v<%= version %>
 *
 * Copyright (c) 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */
`;

var buildDist = function(opts) {
  var webpackOpts = {
    externals: {
      immutable: {
        root: 'Immutable',
        commonjs2: 'immutable',
        commonjs: 'immutable',
        amd: 'immutable',
      },
      react: {
        root: 'React',
        commonjs2: 'react',
        commonjs: 'react',
        amd: 'react',
      },
      'react-dom': {
        root: 'ReactDOM',
        commonjs2: 'react-dom',
        commonjs: 'react-dom',
        amd: 'react-dom',
      },
    },
    output: {
      filename: opts.output,
      libraryTarget: 'umd',
      library: 'Draft',
    },
    plugins: [
      new webpackStream.webpack.DefinePlugin({
        'process.env.NODE_ENV': JSON.stringify(
          opts.debug ? 'development' : 'production',
        ),
      }),
      new webpackStream.webpack.LoaderOptionsPlugin({
        debug: opts.debug,
      }),
      new StatsPlugin(`../meta/bundle-size-stats/${opts.output}.json`, {
        chunkModules: true,
      }),
    ],
  };
  if (!opts.debug) {
    webpackOpts.plugins.push(new UglifyJsPlugin());
  }
  return webpackStream(webpackOpts, null, function(err, stats) {
    if (err) {
      throw new gulpUtil.PluginError('webpack', err);
    }
    if (stats.compilation.errors.length) {
      gulpUtil.log('webpack', '\n' + stats.toString({colors: true}));
    }
  });
};

gulp.task('clean', function() {
  return del([paths.dist, paths.lib]);
});

gulp.task('modules', function() {
  return gulp
    .src(paths.src)
    .pipe(babel(babelOptsJS))
    .pipe(flatten())
    .pipe(gulp.dest(paths.lib));
});

gulp.task('flow', function() {
  return gulp
    .src(paths.src)
    .pipe(babel(babelOptsFlow))
    .pipe(flatten())
    .pipe(rename({extname: '.js.flow'}))
    .pipe(gulp.dest(paths.lib));
});

gulp.task('css', function() {
  return (gulp
      .src(paths.css)
      .pipe(
        through.obj(function(file, encoding, callback) {
          var contents = file.contents.toString();
          var replaced = contents.replace(
            // Regex based on MakeHasteCssModuleTransform: ignores comments,
            // strings, and URLs
            /\/\*.*?\*\/|'(?:\\.|[^'])*'|"(?:\\.|[^"])*"|url\([^)]*\)|(\.(?:public\/)?[\w-]*\/{1,2}[\w-]+)/g,
            function(match, cls) {
              if (cls) {
                return cls.replace(/\//g, '-');
              } else {
                return match;
              }
            },
          );
          replaced = replaced.replace(
            // MakeHasteCssVariablesTransform
            /\bvar\(([\w-]+)\)/g,
            function(match, name) {
              var vars = {
                'fig-secondary-text': '#9197a3',
                'fig-light-20': '#bdc1c9',
              };
              if (vars[name]) {
                return vars[name];
              } else {
                throw new Error('Unknown CSS variable ' + name);
              }
            },
          );
          file.contents = Buffer.from(replaced);
          callback(null, file);
        }),
      )
      .pipe(concatCSS('Draft.css'))
      // Avoid rewriting rules *just in case*, just compress
      .pipe(cleanCSS({advanced: false}))
      .pipe(header(COPYRIGHT_HEADER, {version: packageData.version}))
      .pipe(gulp.dest(paths.dist)) );
});

gulp.task('dist', ['modules', 'css'], function() {
  var opts = {
    debug: true,
    output: 'Draft.js',
  };
  return gulp
    .src('./lib/Draft.js')
    .pipe(buildDist(opts))
    .pipe(derequire())
    .pipe(
      gulpif('*.js', header(COPYRIGHT_HEADER, {version: packageData.version})),
    )
    .pipe(gulp.dest(paths.dist));
});

gulp.task('dist:min', ['modules'], function() {
  var opts = {
    debug: false,
    output: 'Draft.min.js',
  };
  return gulp
    .src('./lib/Draft.js')
    .pipe(buildDist(opts))
    .pipe(
      gulpif('*.js', header(COPYRIGHT_HEADER, {version: packageData.version})),
    )
    .pipe(gulp.dest(paths.dist));
});

gulp.task('check-dependencies', function() {
  return gulp.src('package.json').pipe(gulpCheckDependencies());
});

gulp.task('watch', function() {
  gulp.watch(paths.src, ['modules']);
});

gulp.task('dev', function() {
  gulp.watch(paths.src, ['modules', 'dist']);
});

gulp.task('default', function(cb) {
  runSequence(
    'check-dependencies',
    'clean',
    ['modules', 'flow'],
    ['dist', 'dist:min'],
    cb,
  );
});
<|endoftext|>


<commit_before>var gulp = require('gulp'),
    concat = require('gulp-concat'),
    livereload = require('gulp-livereload'),
    neat = require('node-neat'),
    please = require('gulp-pleeease'),
    plumber = require('gulp-plumber'),
    sass = require('gulp-sass'),
    sourcemaps = require('gulp-sourcemaps'),
    util = require('gulp-util'),

    styleguide = require('./lib/styleguide'),
    markdownPath = util.env.markdown ? util.env.markdown.replace(/\/$/, '') : 'demo/source/overview.md',
    outputPath = util.env.output ? util.env.output.replace(/\/$/, '') : 'demo/output',
    sourcePath = util.env.source ? util.env.source.replace(/\/$/, '') : 'demo/source';

/* Tasks for development */
gulp.task('serve', function() {

  var app = require('./lib/server').app,
    server = require('./lib/server').server;

  serverModule = require('./lib/server')(sourcePath, outputPath);
  app = serverModule.app;
  server = serverModule.server;
  app.set('port', util.env.port || 3000);
  server = server.listen(app.get('port'), function() {
    console.log('Express server listening on port ' + server.address().port);
  });
});

gulp.task('styleguide', ['build'], function() {
  return gulp.src(['demo/source/**/*.scss'])
    .pipe(styleguide({
      dest: outputPath,
      markdownPath: markdownPath,
      sass: {
        loadPath: neat.includePaths
      }
    }));
});

gulp.task('js:app', function() {
  return gulp.src(['lib/app/js/**/*.js', '!lib/app/js/vendor/**/*.js'])
    .pipe(plumber())
    .pipe(concat('app.js'))
    .pipe(gulp.dest(outputPath + '/js'));
});

gulp.task('js:vendor', function() {
  return gulp.src('lib/app/js/vendor/**/*.js')
    .pipe(plumber())
    .pipe(concat('vendor.js'))
    .pipe(gulp.dest(outputPath + '/js'));
});

gulp.task('sass', function() {
  return gulp.src('lib/app/sass/**/*.scss')
    .pipe(plumber())
    .pipe(sass({
      // Include bourbon & neat
      includePaths: neat.includePaths
    }))
    .pipe(sourcemaps.init())
    .pipe(please({
      minifier: false
    }))
    .pipe(gulp.dest(outputPath + '/css'));
});

gulp.task('html', function() {
  return gulp.src('lib/app/**/*.html')
    .pipe(gulp.dest(outputPath + '/'));
});

gulp.task('assets', function() {
  return gulp.src('lib/app/assets/**')
    .pipe(gulp.dest(outputPath + '/assets'));
});

gulp.task('watch', ['build', 'styleguide', 'serve'], function() {

  var app, serverModule, server;

  // TODO: configure livereload
  // livereload.listen();

  gulp.watch('lib/app/sass/**/*.scss', ['sass']);
  gulp.watch(['lib/app/js/**/*.js', '!lib/app/js/vendor/**/*.js'], ['js:app']);
  gulp.watch('lib/app/js/vendor/**/*.js', ['js:vendor']);
  gulp.watch('lib/app/**/*.html', ['html']);
  gulp.watch(sourcePath + '/**', ['styleguide']);
});

gulp.task('build', ['sass', 'js:app', 'js:vendor', 'html', 'assets']);

<commit_msg>Fix source path when running generator via bin script<commit_after>var gulp = require('gulp'),
    concat = require('gulp-concat'),
    livereload = require('gulp-livereload'),
    neat = require('node-neat'),
    please = require('gulp-pleeease'),
    plumber = require('gulp-plumber'),
    sass = require('gulp-sass'),
    sourcemaps = require('gulp-sourcemaps'),
    util = require('gulp-util'),

    styleguide = require('./lib/styleguide'),
    markdownPath = util.env.markdown ? util.env.markdown.replace(/\/$/, '') : 'demo/source/overview.md',
    outputPath = util.env.output ? util.env.output.replace(/\/$/, '') : 'demo/output',
    sourcePath = util.env.source ? util.env.source.replace(/\/$/, '') : 'demo/source';

/* Tasks for development */
gulp.task('serve', function() {

  var app = require('./lib/server').app,
    server = require('./lib/server').server;

  serverModule = require('./lib/server')(sourcePath, outputPath);
  app = serverModule.app;
  server = serverModule.server;
  app.set('port', util.env.port || 3000);
  server = server.listen(app.get('port'), function() {
    console.log('Express server listening on port ' + server.address().port);
  });
});

gulp.task('styleguide', ['build'], function() {
  return gulp.src([sourcePath + '/**/*.scss'])
    .pipe(styleguide({
      dest: outputPath,
      markdownPath: markdownPath,
      sass: {
        loadPath: neat.includePaths
      }
    }));
});

gulp.task('js:app', function() {
  return gulp.src(['lib/app/js/**/*.js', '!lib/app/js/vendor/**/*.js'])
    .pipe(plumber())
    .pipe(concat('app.js'))
    .pipe(gulp.dest(outputPath + '/js'));
});

gulp.task('js:vendor', function() {
  return gulp.src('lib/app/js/vendor/**/*.js')
    .pipe(plumber())
    .pipe(concat('vendor.js'))
    .pipe(gulp.dest(outputPath + '/js'));
});

gulp.task('sass', function() {
  return gulp.src('lib/app/sass/**/*.scss')
    .pipe(plumber())
    .pipe(sass({
      // Include bourbon & neat
      includePaths: neat.includePaths
    }))
    .pipe(sourcemaps.init())
    .pipe(please({
      minifier: false
    }))
    .pipe(gulp.dest(outputPath + '/css'));
});

gulp.task('html', function() {
  return gulp.src('lib/app/**/*.html')
    .pipe(gulp.dest(outputPath + '/'));
});

gulp.task('assets', function() {
  return gulp.src('lib/app/assets/**')
    .pipe(gulp.dest(outputPath + '/assets'));
});

gulp.task('watch', ['build', 'styleguide', 'serve'], function() {

  var app, serverModule, server;

  // TODO: configure livereload
  // livereload.listen();

  gulp.watch('lib/app/sass/**/*.scss', ['sass']);
  gulp.watch(['lib/app/js/**/*.js', '!lib/app/js/vendor/**/*.js'], ['js:app']);
  gulp.watch('lib/app/js/vendor/**/*.js', ['js:vendor']);
  gulp.watch('lib/app/**/*.html', ['html']);
  gulp.watch(sourcePath + '/**', ['styleguide']);
});

gulp.task('build', ['sass', 'js:app', 'js:vendor', 'html', 'assets']);

<|endoftext|>


<commit_before>import { RESTORE_FOCUS_TIMEOUT } from './constants.js'

const globalState = {}

export default globalState

// Restore previous active (focused) element
export const restoreActiveElement = () => {
  return new Promise(resolve => {
    const x = window.scrollX
    const y = window.scrollY
    globalState.restoreFocusTimeout = setTimeout(() => {
      if (globalState.previousActiveElement && globalState.previousActiveElement.focus) {
        globalState.previousActiveElement.focus()
        globalState.previousActiveElement = null
      } else if (document.body) {
        document.body.focus()
      }
      resolve()
    }, RESTORE_FOCUS_TIMEOUT) // issues/900
    if (typeof x !== 'undefined' && typeof y !== 'undefined') { // IE doesn't have scrollX/scrollY support
      window.scrollTo(x, y)
    }
  })
}
<commit_msg>chore: eliminate cognitive complexity of restoreActiveElement() (#1511)<commit_after>import { RESTORE_FOCUS_TIMEOUT } from './constants.js'

const globalState = {}

export default globalState

const focusPreviousActiveElement = () => {
  if (globalState.previousActiveElement && globalState.previousActiveElement.focus) {
    globalState.previousActiveElement.focus()
    globalState.previousActiveElement = null
  } else if (document.body) {
    document.body.focus()
  }
}

// Restore previous active (focused) element
export const restoreActiveElement = () => {
  return new Promise(resolve => {
    const x = window.scrollX
    const y = window.scrollY
    globalState.restoreFocusTimeout = setTimeout(() => {
      focusPreviousActiveElement()
      resolve()
    }, RESTORE_FOCUS_TIMEOUT) // issues/900
    if (typeof x !== 'undefined' && typeof y !== 'undefined') { // IE doesn't have scrollX/scrollY support
      window.scrollTo(x, y)
    }
  })
}
<|endoftext|>


<commit_before>'use strict';

var browserify = require('browserify'),
gulp = require('gulp'),
gutil = require('gulp-util'),
livereload = require('gulp-livereload'),
notify = require('gulp-notify'),
rename = require('gulp-rename'),
rimraf = require('gulp-rimraf'),
source = require('vinyl-source-stream'),
uglify = require('gulp-uglify'),
watchify = require('watchify');

var staticDir = './openfisca_web_ui/static';
var jsDir = staticDir + '/js';
var indexJsFile = jsDir + '/index.js';
var distDir = staticDir + '/dist';
var vendorJsFiles = [
  './node_modules/jquery/dist/jquery.js',
  './node_modules/lazy.js/lazy.js',
];
var vendorDir = distDir + '/vendor',
vendorBootstrapDir = vendorDir + '/bootstrap';

function buildScripts(entryFile, options) {
  var debug = options && options.debug,
    watch = options && options.watch;
  var bundlerConstructor = options && watch ? watchify : browserify;
  var bundler = bundlerConstructor(entryFile);
//  bundler.transform(reactify, {es6: true});
  function rebundle() {
    var stream = bundler.bundle({debug: debug});
    if (watch) {
      stream = stream.on('error', handleError);
    }
    stream = stream
      .pipe(source('bundle.js'))
      .pipe(gulp.dest(distDir));
    return stream;
  }
  bundler.on('update', function() {
    gutil.log('Rebundle...');
    rebundle()
      .on('end', function() { gutil.log('Rebundle done.'); });
  });
  return rebundle();
}

function handleError() {
  /* jshint validthis: true */
  var args = Array.prototype.slice.call(arguments);
  var filePathRegex = /Error: Parsing file (.+): Line \d+/;
  var match = filePathRegex.exec(args[0]);
  var filePath = match[1];
  notify.onError({
    message: '<%= error.message %> <a href="file://<%= options.filePath %>">open',
    templateOptions: {filePath: filePath},
    title: 'Compile Error',
  }).apply(this, args);
  this.emit('end'); // Keep gulp from hanging on this task
}

function startLiveReload() {
  var port = 35731;
  var liveReloadServer = livereload(port);
  var reloadPage = function(event) {
    gutil.log('Reload browser page.')
    liveReloadServer.changed(event.path);
  };
  return gulp.watch([distDir + '/**/*'], reloadPage);
}

gulp.task('bundle', function() {
  return buildScripts(indexJsFile);
});

gulp.task('bundle-dev', function() {
  return buildScripts(indexJsFile, {debug: true});
});

gulp.task('clean', function() {
  return gulp.src(distDir, {read: false})
    .pipe(rimraf());
});

gulp.task('default', ['dev']);

gulp.task('dev', ['bundle-dev', 'vendor']);

gulp.task('prod', ['bundle', 'uglify', 'vendor']);

gulp.task('uglify', ['bundle'], function() {
  gulp.src(distDir + '/bundle.js')
    .pipe(uglify())
    .pipe(rename({suffix: '.min'}))
    .pipe(gulp.dest(distDir));
});

gulp.task('vendor', ['vendor-bootstrap', 'vendor-js']);

gulp.task('vendor-bootstrap', function() {
  return gulp.src('./node_modules/bootstrap/dist/**')
    .pipe(gulp.dest(vendorBootstrapDir));
});

gulp.task('vendor-js', function() {
  return gulp.src(vendorJsFiles)
    .pipe(gulp.dest(vendorDir));
});

gulp.task('watch', ['vendor'], function() {
  startLiveReload();
  buildScripts(indexJsFile, {debug: true, watch: true});
});
<commit_msg>Enhance handleError in gulpfile.<commit_after>'use strict';

var browserify = require('browserify'),
gulp = require('gulp'),
gutil = require('gulp-util'),
livereload = require('gulp-livereload'),
notify = require('gulp-notify'),
rename = require('gulp-rename'),
rimraf = require('gulp-rimraf'),
source = require('vinyl-source-stream'),
uglify = require('gulp-uglify'),
watchify = require('watchify');

var staticDir = './openfisca_web_ui/static';
var jsDir = staticDir + '/js';
var indexJsFile = jsDir + '/index.js';
var distDir = staticDir + '/dist';
var vendorJsFiles = [
  './node_modules/jquery/dist/jquery.js',
  './node_modules/lazy.js/lazy.js',
];
var vendorDir = distDir + '/vendor',
vendorBootstrapDir = vendorDir + '/bootstrap';

function buildScripts(entryFile, options) {
  var debug = options && options.debug,
    watch = options && options.watch;
  var bundlerConstructor = options && watch ? watchify : browserify;
  var bundler = bundlerConstructor(entryFile);
//  bundler.transform(reactify, {es6: true});
  function rebundle() {
    var stream = bundler.bundle({debug: debug});
    if (watch) {
      stream = stream.on('error', handleError);
    }
    stream = stream
      .pipe(source('bundle.js'))
      .pipe(gulp.dest(distDir));
    return stream;
  }
  bundler.on('update', function() {
    gutil.log('Rebundle...');
    rebundle()
      .on('end', function() { gutil.log('Rebundle done.'); });
  });
  return rebundle();
}

function handleError() {
  /* jshint validthis: true */
  var args = Array.prototype.slice.call(arguments);
  gutil.log(args);
  var errorData = {
    message: '<%= error.message %>',
    title: 'Compile Error',
  };
  var filePath;
  if (args[0] && args[0].fileName) {
    // React JSX source files.
    filePath = args[0].fileName;
  } else {
    // Vanilla JS source files.
    var filePathRegex = /Error: Parsing file (.+): Line \d+/;
    var match = filePathRegex.exec(args[0]);
    if (match) {
      filePath = match[1];
    }
  }
  if (filePath) {
    console.log(errorData);
    errorData.message += ' <a href="file://<%= options.filePath %>">open';
    console.log(errorData);
    errorData.templateOptions = {filePath: filePath};
  }
  notify.onError(errorData).apply(this, args);
  this.emit('end'); // Keep gulp from hanging on this task
}

function startLiveReload() {
  var port = 35731;
  var liveReloadServer = livereload(port);
  var reloadPage = function(event) {
    gutil.log('Reload browser page.')
    liveReloadServer.changed(event.path);
  };
  return gulp.watch([distDir + '/**/*'], reloadPage);
}

gulp.task('bundle', function() {
  return buildScripts(indexJsFile);
});

gulp.task('bundle-dev', function() {
  return buildScripts(indexJsFile, {debug: true});
});

gulp.task('clean', function() {
  return gulp.src(distDir, {read: false})
    .pipe(rimraf());
});

gulp.task('default', ['dev']);

gulp.task('dev', ['bundle-dev', 'vendor']);

gulp.task('prod', ['bundle', 'uglify', 'vendor']);

gulp.task('uglify', ['bundle'], function() {
  gulp.src(distDir + '/bundle.js')
    .pipe(uglify())
    .pipe(rename({suffix: '.min'}))
    .pipe(gulp.dest(distDir));
});

gulp.task('vendor', ['vendor-bootstrap', 'vendor-js']);

gulp.task('vendor-bootstrap', function() {
  return gulp.src('./node_modules/bootstrap/dist/**')
    .pipe(gulp.dest(vendorBootstrapDir));
});

gulp.task('vendor-js', function() {
  return gulp.src(vendorJsFiles)
    .pipe(gulp.dest(vendorDir));
});

gulp.task('watch', ['vendor'], function() {
  startLiveReload();
  buildScripts(indexJsFile, {debug: true, watch: true});
});
<|endoftext|>


<commit_before>/**
 * # Gulpfile
 */

 "use strict";

/**
 * Gulp Dependencies
 */
var gulp = require("gulp"),
  rename = require("gulp-rename");

/**
 * Build Dependencies
 */
var browserify = require("gulp-browserify"),
  uglify = require("gulp-uglify");

/**
 * Style Dependencies
 */
var less = require("gulp-less"),
  prefix = require("gulp-autoprefixer"),
  minifyCSS = require("gulp-minify-css");

/**
 * Dev Dependencies
 */
var jshint = require("gulp-jshint");

/**
 * Test Dependencies
 */
var mochaPhantomjs = require("gulp-mocha-phantomjs");

/**
 * JSHINT tasks
 */
gulp.task("lint-client", function() {
  return gulp.src("./lib/client/scripts/**/*.js")
    .pipe(jshint())
    .pipe(jshint.reporter("default"));
});

gulp.task("lint-test-client", function() {
  return gulp.src("./lib/test/client/**/*.js")
    .pipe(jshint())
    .pipe(jshint.reporter("default"));
});

gulp.task("lint-server", function() {
  return gulp.src("./lib/server/**/*.js")
    .pipe(jshint())
    .pipe(jshint.reporter("default"));
});
/**
 * Browserify tasks
 */
gulp.task("browserify-client", ["lint-client"], function() {
  return gulp.src("./lib/client/scripts/client.js")//TODO: Add this file
    .pipe(browserify({
      insertGlobals: true
    }))
    .pipe(rename("main.js"))
    .pipe(gulp.dest("./static/js"));
});

gulp.task("browserify-test", ["lint-test-client"], function() {
  return gulp.src("./lib/test/client/client.js")
    .pipe(browserify({
      insertGlobals: true
    }))
    .pipe(rename("client-test.js"))
    .pipe(gulp.dest("./static/test"));
});

/**
 * Testing tasks
 */
gulp.task("test", ["lint-test-client", "browserify-test"], function() {
  return gulp.src("./lib/test/client/index.html")
    .pipe(mochaPhantomjs({reporter: "spec", dump:"./logs/test.log"}));
});

/**
 * Asset building tasks
 */
gulp.task("styles", function() {
  return gulp.src("./lib/client/less/main.less")
    .pipe(less())
    .pipe(prefix({ cascade: true }))
    .pipe(rename("main.css"))
    .pipe(gulp.dest("./static/css"));
});

gulp.task("minify", ["styles"], function() {
  return gulp.src("./static/css/main.css")
    .pipe(minifyCSS())
    .pipe(rename("main.min.css"))
    .pipe(gulp.dest("./static/css"));
});

/**
 * Client Watch
 */
gulp.task("watch", function() {
  gulp.watch("./lib/client/less/**/*.less", ["minify"]);
  gulp.watch("./lib/client/scripts/**/*.js", ["browserify-client", "test"]);
  gulp.watch("./lib/test/client/**/*.js", ["test"]);
  gulp.watch("./lib/server/**/*.js", ["lint-server"]);
});

gulp.task("uglify", ["browserify-client"], function() {
  return gulp.src("./static/js/main.js")
    .pipe(uglify())
    .pipe(rename("main.min.js"))
    .pipe(gulp.dest("./static/js"));
});

gulp.task("build", ["lint-server", "uglify", "minify"]);

gulp.task("default", ["test", "build", "watch"]);<commit_msg>Fixed script name that prevented tests from running.<commit_after>/**
 * # Gulpfile
 */

 "use strict";

/**
 * Gulp Dependencies
 */
var gulp = require("gulp"),
  rename = require("gulp-rename");

/**
 * Build Dependencies
 */
var browserify = require("gulp-browserify"),
  uglify = require("gulp-uglify");

/**
 * Style Dependencies
 */
var less = require("gulp-less"),
  prefix = require("gulp-autoprefixer"),
  minifyCSS = require("gulp-minify-css");

/**
 * Dev Dependencies
 */
var jshint = require("gulp-jshint");

/**
 * Test Dependencies
 */
var mochaPhantomjs = require("gulp-mocha-phantomjs");

/**
 * JSHINT tasks
 */
gulp.task("lint-client", function() {
  return gulp.src("./lib/client/scripts/**/*.js")
    .pipe(jshint())
    .pipe(jshint.reporter("default"));
});

gulp.task("lint-test-client", function() {
  return gulp.src("./lib/test/client/**/*.js")
    .pipe(jshint())
    .pipe(jshint.reporter("default"));
});

gulp.task("lint-server", function() {
  return gulp.src("./lib/server/**/*.js")
    .pipe(jshint())
    .pipe(jshint.reporter("default"));
});
/**
 * Browserify tasks
 */
gulp.task("browserify-client", ["lint-client"], function() {
  return gulp.src("./lib/client/scripts/client.js")//TODO: Add this file
    .pipe(browserify({
      insertGlobals: true
    }))
    .pipe(rename("main.js"))
    .pipe(gulp.dest("./static/js"));
});

gulp.task("browserify-test", ["lint-test-client"], function() {
  return gulp.src("./lib/test/client/index.js")
    .pipe(browserify({
      insertGlobals: true
    }))
    .pipe(rename("client-test.js"))
    .pipe(gulp.dest("./static/test"));
});

/**
 * Testing tasks
 */
gulp.task("test", ["lint-test-client", "browserify-test"], function() {
  return gulp.src("./lib/test/client/index.html")
    .pipe(mochaPhantomjs({reporter: "spec", dump:"./logs/test.log"}));
});

/**
 * Asset building tasks
 */
gulp.task("styles", function() {
  return gulp.src("./lib/client/less/main.less")
    .pipe(less())
    .pipe(prefix({ cascade: true }))
    .pipe(rename("main.css"))
    .pipe(gulp.dest("./static/css"));
});

gulp.task("minify", ["styles"], function() {
  return gulp.src("./static/css/main.css")
    .pipe(minifyCSS())
    .pipe(rename("main.min.css"))
    .pipe(gulp.dest("./static/css"));
});

/**
 * Client Watch
 */
gulp.task("watch", function() {
  gulp.watch("./lib/client/less/**/*.less", ["minify"]);
  gulp.watch("./lib/client/scripts/**/*.js", ["browserify-client", "test"]);
  gulp.watch("./lib/test/client/**/*.js", ["test"]);
  gulp.watch("./lib/server/**/*.js", ["lint-server"]);
});

gulp.task("uglify", ["browserify-client"], function() {
  return gulp.src("./static/js/main.js")
    .pipe(uglify())
    .pipe(rename("main.min.js"))
    .pipe(gulp.dest("./static/js"));
});

gulp.task("build", ["lint-server", "uglify", "minify"]);

gulp.task("default", ["test", "build", "watch"]);<|endoftext|>