
function createObserverFactory() {

    const _createObserver = function() {

        const _listeners = [];

        const _views = {};

        const _transactions = {};

        const _view = function(viewId) {
            if (!_views.hasOwnProperty(viewId)) {
                _views[viewId] = {
                    readProps: {},
                    changedProps: {}
                };
            }
            return _views[viewId];
        };

        const _changedViewPropCount = function(view) {
            let changedPropCount = 0;
            for (let propKey in view.changedProps) {
                changedPropCount += view.readProps.hasOwnProperty(propKey) ? 1 : 0;
            }
            return changedPropCount;
        };

        const _viewListenerIndex = function(view) {
            let listenerIndex = -1;
            for (let i = 0; i < view.listeners.length; i++) {
                if (view.listeners[i] === listener) {
                    listenerIndex = i;
                    break;
                }
            }
            return listenerIndex;
        };

        const _transaction = function(transactionId) {
            if (!_transactions.hasOwnProperty(transactionId)) {
                _transactions[transactionId] = {
                    readProps: {},
                    changedProps: {}
                };
            }
            return _transactions[transactionId];
        };

        const _changedTransactionPropCount = function(transaction) {
            let changedPropCount = 0;
            for (let propKey in transaction.changedProps) {
                changedPropCount += transaction.readProps.hasOwnProperty(propKey) ? 1 : 0;
            }
            return changedPropCount;
        };

        const _transactionListenerIndex = function(transaction) {
            let listenerIndex = -1;
            for (let i = 0; i < transaction.listeners.length; i++) {
                if (transaction.listeners[i] === listener) {
                    listenerIndex = i;
                    break;
                }
            }
            return listenerIndex;
        };

        const _listenerIndex = function(listener) {
            let listenerIndex = -1;
            for (let i = 0; i < _listeners.length; i++) {
                if (_listeners[i] === listener) {
                    listenerIndex = i;
                    break;
                }
            }
            return listenerIndex;
        };

        return {

            addViewListener: function(viewId, listener) {
                const view = _view(viewId);
                const listenerIndex = _viewListenerIndex(view, listener);
                if (listenerIndex < 0) {
                    view.listeners.push(listener);
                }
            },

            removeViewListener: function(viewId, listener) {
                const view = _view(viewId);
                const listenerIndex = _viewListenerIndex(view, listener);
                if (listenerIndex >= 0) {
                    _view(viewId).listeners.splice(listenerIndex, 1);
                }
            },

            markViewAsRead: function(viewId, propKey) {
                _view(viewId).readProps[propsKey] = true;
            },

            markViewAsChanged: function(viewId, propKey) {
                _view(viewId).changedProps[propsKey] = true;
            },

            addTransactionListener: function(transactionId, listener) {
                const transaction = _transaction(transactionId);
                const listenerIndex = _transactionListenerIndex(transaction, listener);
                if (listenerIndex < 0) {
                    transaction.listeners.push(listener);
                }
            },

            removeTransactionListener: function(transactionId, listener) {
                const transaction = _transaction(transactionId);
                const listenerIndex = _transactionListenerIndex(transaction, listener);
                if (listenerIndex >= 0) {
                    _transaction(transactionId).listeners.splice(listenerIndex, 1);
                }
            },

            markTransactionAsRead: function(transactionId, propKey) {
                _transaction(transactionId).readProps[propsKey] = true;
            },

            markTransactionAsChanged: function(transactionId, propKey) {
               _transaction(transactionId).changedProps[propsKey] = true;
            },

            addListener: function(listener) {
                const listenerIndex = _listenerIndex(listener);
                if (listenerIndex < 0) {
                    _listeners.push(listener);
                }
            },

            removeListener: function(listener) {
                const listenerIndex = _listenerIndex(listener);
                if (listenerIndex >= 0) {
                    _listeners.splice(listenerIndex, 1);
                }
            },

            dispatchChanges: function() {
                let changedPropCount = 0;
                const listenersToDispatch = []
                for (let view in _views) {
                    const changedViewPropCount = _changedViewPropCount(view);
                    changedPropCount += changedViewPropCount;
                    view.changedProps = {};
                    if (changedViewPropCount) {
                        for (let i = 0; i < view.listeners.length; i++) {
                            listenersToDispatch.push(view.listeners[i]);
                        }
                    }
                }
                for (let transaction in _transactions) {
                    const changedTransactionPropCount = _changedTransactionPropCount(transaction);
                    changedPropCount += changedTransactionPropCount;
                    transaction.changedProps = {};
                    if (changedTransactionPropCount) {
                        for (let i = 0; i < transaction.listeners.length; i++) {
                            listenersToDispatch.push(transaction.listeners);
                        }
                    }
                }
                if (changedPropCount > 0) {
                    for (let i = 0; i < _listeners.length; i++) {
                        listenersToDispatch.push(_listeners[i]);
                    }
                }
                const uniqueListeners = listenersToDispatch.filter((v, i, a) => a.indexOf(v) === i);
                for (let i = 0; i < uniqueListeners.length; i++) {
                    uniqueListeners[i]();
                }
            }
        }
    }

    return {

        createObserver: _createObserver,

    };

}

export default createObserverFactory; 

@@ -806,273 +806,46 @@
     
-let listenerIndex = -1;%0A            for (let i = 0; i %3C view.listeners.length; i++) %7B%0A                if (view.listeners%5Bi%5D === listener) %7B%0A                    listenerIndex = i;%0A                    break;%0A                %7D%0A            %7D%0A            return 
+return view.listeners.indexOf(
 listener
 Inde
@@ -832,37 +832,33 @@
 indexOf(listener
-Index
+)
 ;%0A        %7D;%0A%0A  
@@ -1592,287 +1592,53 @@
     
-let listenerIndex = -1;%0A            for (let i = 0; i %3C transaction.listeners.length; i++) %7B%0A                if (transaction.listeners%5Bi%5D === listener) %7B%0A                    listenerIndex = i;%0A                    break;%0A                %7D%0A            %7D%0A            return 
+return transaction.listeners.indexOf(
 listener
 Inde
@@ -1625,37 +1625,33 @@
 indexOf(listener
-Index
+)
 ;%0A        %7D;%0A%0A  
@@ -1716,265 +1716,42 @@
     
-let listenerIndex = -1;%0A            for (let i = 0; i %3C _listeners.length; i++) %7B%0A                if (_listeners%5Bi%5D === listener) %7B%0A                    listenerIndex = i;%0A                    break;%0A                %7D%0A            %7D%0A            return 
+return _listeners.indexOf(
 listener
 Inde
@@ -1746,21 +1746,17 @@
 listener
-Index
+)
 ;%0A      

use indexOf function