var patch = require("snabbdom").init([
    require("snabbdom/modules/class"),
    require("snabbdom/modules/props"),
    require("snabbdom/modules/style"),
    require("snabbdom/modules/attributes"),
    require("snabbdom/modules/eventlisteners")
])

module.exports = function app(options) {
    var model = options.model,
        view = options.view,
        reducers = options.reducers || options.update || {},
        subs = options.subscriptions || {},
        effects = options.effects || {},
        hooks = options.hooks || {},
        root = options.root || document.body.appendChild(document.createElement("div"))

    var update = function (model, name, data) {
        var reduce = reducers[name]
        if (typeof reduce !== "function") {
            throw TypeError(name + " is not a reducer or effect")
        }
        return reduce(model, data)
    }

    Object
        .keys(reducers)
        .concat(Object.keys(effects))
        .forEach(function (name) {
            if (reducers[name] !== undefined && effects[name] !== undefined) {
                throw TypeError(name + " is already defined as a reducer or effect")
            }
            dispatch[name] = function (data) {
                dispatch(name, data)
            }
        })

    document.addEventListener("DOMContentLoaded", function () {
        Object.keys(subs).forEach(function (key) {
            subs[key](model, dispatch, error)
        })
    })

    render(model, view, update, root)

    function render(model, view, update, lastNode) {
        if (view === undefined) {
            return
        }
        patch(lastNode, root = view(model, dispatch))
    }

    function error(err) {
        if (hooks.onError === undefined) {
            throw err
        }
        hooks.onError(err)
    }

    function dispatch(name, data) {
        if (hooks.onAction !== undefined) {
            hooks.onAction(name, data)
        }
        var effect = effects[name]
        if (typeof effect === "function") {
            effect(model, dispatch, error)
        } else {
            var lastModel = model
            render(model = update(model, name, data), view, update, root)
            if (hooks.onUpdate !== undefined) {
                hooks.onUpdate(lastModel, model, data)
            }
        }
    }
}

@@ -273,16 +273,17 @@
 tion app
+ 
 (options
@@ -377,78 +377,14 @@
 ons.
-reducers %7C%7C options.update %7C%7C %7B%7D,%0A        subs = options.subscriptions
+update
  %7C%7C 
@@ -420,32 +420,67 @@
 .effects %7C%7C %7B%7D,%0A
+        subs = options.subs %7C%7C %7B%7D,%0A
         hooks = 
@@ -677,128 +677,8 @@
 me%5D%0A
-        if (typeof reduce !== %22function%22) %7B%0A            throw TypeError(name + %22 is not a reducer or effect%22)%0A        %7D%0A
     
@@ -723,29 +723,25 @@
     
-Object%0A        .keys(
+for (var name in 
 redu
@@ -745,16 +745,18 @@
 educers)
+ %7B
 %0A       
@@ -760,69 +760,64 @@
     
-.concat(Object.keys(effects)
+wrap(dispatch, name
 )%0A    
+%7D%0A%0A
     
-.
 for
-Each(function (name
+ (var name in effects
 ) %7B%0A
@@ -828,55 +828,20 @@
     
- 
-   
 if (
-reducers%5Bname%5D !== undefined && effects
+dispatch
 %5Bnam
@@ -868,28 +868,24 @@
             
-   
- 
 throw TypeEr
@@ -941,36 +941,32 @@
 ffect%22)%0A        
-    
 %7D%0A            di
@@ -963,71 +963,21 @@
     
-    dispatch%5Bname%5D = function (data) %7B%0A                
+wrap(
 dispatch
 (nam
@@ -976,45 +976,21 @@
 atch
-(
+, 
 name
-, data)%0A            %7D%0A        %7D)
+)%0A    %7D
 %0A%0A  
@@ -1063,47 +1063,28 @@
     
-Object.keys(subs).forEach(function (key
+for (var key in subs
 ) %7B%0A
@@ -1138,17 +1138,16 @@
        %7D
-)
 %0A    %7D)%0A
@@ -1201,24 +1201,25 @@
 ction render
+ 
 (model, view
@@ -1257,17 +1257,17 @@
 f (view 
-=
+!
 == undef
@@ -1282,33 +1282,8 @@
     
-        return%0A        %7D%0A
     
@@ -1332,16 +1332,26 @@
 patch))%0A
+        %7D%0A
     %7D%0A%0A 
@@ -1363,24 +1363,25 @@
 nction error
+ 
 (err) %7B%0A    
@@ -1506,16 +1506,17 @@
 dispatch
+ 
 (name, d
@@ -1607,32 +1607,33 @@
 data)%0A        %7D%0A
+%0A
         var effe
@@ -1643,32 +1643,33 @@
 = effects%5Bname%5D%0A
+%0A
         if (type
@@ -1790,16 +1790,17 @@
 = model%0A
+%0A
         
@@ -1817,16 +1817,29 @@
 (model =
+ merge(model,
  update(
@@ -1856,16 +1856,17 @@
 e, data)
+)
 , view, 
@@ -1879,16 +1879,17 @@
 , root)%0A
+%0A
         
@@ -2012,11 +2012,384 @@
  %7D%0A    %7D
+%0A%0A    function merge (target, source) %7B%0A        if (typeof source === %22string%22) %7B%0A            return source%0A        %7D%0A%0A        for (var key in source) %7B%0A            target%5Bkey%5D = source%5Bkey%5D%0A        %7D%0A%0A        return key === undefined ? source : target%0A    %7D%0A%0A    function wrap (func, key) %7B%0A        func%5Bkey%5D = function (data) %7B%0A            func(key, data)%0A        %7D%0A    %7D
 %0A%7D%0A

Allow reducers to return part of a model.