|
(function () { |
|
'use strict'; |
|
|
|
function renderMainFragment$1 ( root, component ) { |
|
var p = document.createElement( 'p' ); |
|
|
|
p.appendChild( document.createTextNode( "foo is " ) ); |
|
var text1 = document.createTextNode( root.foo ); |
|
p.appendChild( text1 ); |
|
|
|
return { |
|
mount: function ( target, anchor ) { |
|
target.insertBefore( p, anchor ); |
|
}, |
|
|
|
update: function ( changed, root ) { |
|
text1.data = root.foo; |
|
}, |
|
|
|
teardown: function ( detach ) { |
|
if ( detach ) { |
|
p.parentNode.removeChild( p ); |
|
} |
|
} |
|
}; |
|
} |
|
|
|
function Nested ( options ) { |
|
options = options || {}; |
|
|
|
var component = this; |
|
var state = options.data || {}; |
|
|
|
var observers = { |
|
immediate: Object.create( null ), |
|
deferred: Object.create( null ) |
|
}; |
|
|
|
var callbacks = Object.create( null ); |
|
|
|
function dispatchObservers ( group, newState, oldState ) { |
|
for ( var key in group ) { |
|
if ( !( key in newState ) ) continue; |
|
|
|
var newValue = newState[ key ]; |
|
var oldValue = oldState[ key ]; |
|
|
|
if ( newValue === oldValue && typeof newValue !== 'object' ) continue; |
|
|
|
var callbacks = group[ key ]; |
|
if ( !callbacks ) continue; |
|
|
|
for ( var i = 0; i < callbacks.length; i += 1 ) { |
|
var callback = callbacks[i]; |
|
if ( callback.__calling ) continue; |
|
|
|
callback.__calling = true; |
|
callback.call( component, newValue, oldValue ); |
|
callback.__calling = false; |
|
} |
|
} |
|
} |
|
|
|
this.fire = function fire ( eventName, data ) { |
|
var handlers = eventName in callbacks && callbacks[ eventName ].slice(); |
|
if ( !handlers ) return; |
|
|
|
for ( var i = 0; i < handlers.length; i += 1 ) { |
|
handlers[i].call( this, data ); |
|
} |
|
}; |
|
|
|
this.get = function get ( key ) { |
|
return key ? state[ key ] : state; |
|
}; |
|
|
|
this.set = function set ( newState ) { |
|
var oldState = state; |
|
state = Object.assign( {}, oldState, newState ); |
|
|
|
dispatchObservers( observers.immediate, newState, oldState ); |
|
if ( mainFragment ) mainFragment.update( newState, state ); |
|
dispatchObservers( observers.deferred, newState, oldState ); |
|
}; |
|
|
|
this._mount = function mount ( target, anchor ) { |
|
mainFragment.mount( target, anchor ); |
|
}; |
|
|
|
this.observe = function ( key, callback, options ) { |
|
var group = ( options && options.defer ) ? observers.deferred : observers.immediate; |
|
|
|
( group[ key ] || ( group[ key ] = [] ) ).push( callback ); |
|
|
|
if ( !options || options.init !== false ) { |
|
callback.__calling = true; |
|
callback.call( component, state[ key ] ); |
|
callback.__calling = false; |
|
} |
|
|
|
return { |
|
cancel: function () { |
|
var index = group[ key ].indexOf( callback ); |
|
if ( ~index ) group[ key ].splice( index, 1 ); |
|
} |
|
}; |
|
}; |
|
|
|
this.on = function on ( eventName, handler ) { |
|
var handlers = callbacks[ eventName ] || ( callbacks[ eventName ] = [] ); |
|
handlers.push( handler ); |
|
|
|
return { |
|
cancel: function () { |
|
var index = handlers.indexOf( handler ); |
|
if ( ~index ) handlers.splice( index, 1 ); |
|
} |
|
}; |
|
}; |
|
|
|
this.teardown = function teardown ( detach ) { |
|
this.fire( 'teardown' ); |
|
|
|
mainFragment.teardown( detach !== false ); |
|
mainFragment = null; |
|
|
|
state = {}; |
|
}; |
|
|
|
this.root = options.root; |
|
this.yield = options.yield; |
|
|
|
var mainFragment = renderMainFragment$1( state, this ); |
|
if ( options.target ) this._mount( options.target ); |
|
} |
|
|
|
var template = (function () { |
|
return { |
|
data () { |
|
return { |
|
foo: 'bar' |
|
}; |
|
}, |
|
|
|
components: { |
|
Nested |
|
} |
|
}; |
|
}()); |
|
|
|
function renderMainFragment ( root, component ) { |
|
var h1 = document.createElement( 'h1' ); |
|
|
|
h1.appendChild( document.createTextNode( "the answer is " ) ); |
|
var text1 = document.createTextNode( root.answer ); |
|
h1.appendChild( text1 ); |
|
var text2 = document.createTextNode( "\n" ); |
|
|
|
var nested_initialData = { |
|
foo: root.foo |
|
}; |
|
var nested = new template.components.Nested({ |
|
target: null, |
|
root: component.root || component, |
|
data: nested_initialData |
|
}); |
|
|
|
return { |
|
mount: function ( target, anchor ) { |
|
target.insertBefore( h1, anchor ); |
|
target.insertBefore( text2, anchor ); |
|
nested._mount( target, anchor ); |
|
}, |
|
|
|
update: function ( changed, root ) { |
|
text1.data = root.answer; |
|
|
|
var nested_changes = {}; |
|
|
|
if ( 'foo' in changed ) nested_changes.foo = root.foo; |
|
|
|
if ( Object.keys( nested_changes ).length ) nested.set( nested_changes ); |
|
}, |
|
|
|
teardown: function ( detach ) { |
|
nested.teardown( detach ); |
|
|
|
if ( detach ) { |
|
h1.parentNode.removeChild( h1 ); |
|
text2.parentNode.removeChild( text2 ); |
|
} |
|
} |
|
}; |
|
} |
|
|
|
function App ( options ) { |
|
options = options || {}; |
|
|
|
var component = this; |
|
var state = Object.assign( template.data(), options.data ); |
|
|
|
var observers = { |
|
immediate: Object.create( null ), |
|
deferred: Object.create( null ) |
|
}; |
|
|
|
var callbacks = Object.create( null ); |
|
|
|
function dispatchObservers ( group, newState, oldState ) { |
|
for ( var key in group ) { |
|
if ( !( key in newState ) ) continue; |
|
|
|
var newValue = newState[ key ]; |
|
var oldValue = oldState[ key ]; |
|
|
|
if ( newValue === oldValue && typeof newValue !== 'object' ) continue; |
|
|
|
var callbacks = group[ key ]; |
|
if ( !callbacks ) continue; |
|
|
|
for ( var i = 0; i < callbacks.length; i += 1 ) { |
|
var callback = callbacks[i]; |
|
if ( callback.__calling ) continue; |
|
|
|
callback.__calling = true; |
|
callback.call( component, newValue, oldValue ); |
|
callback.__calling = false; |
|
} |
|
} |
|
} |
|
|
|
this.fire = function fire ( eventName, data ) { |
|
var handlers = eventName in callbacks && callbacks[ eventName ].slice(); |
|
if ( !handlers ) return; |
|
|
|
for ( var i = 0; i < handlers.length; i += 1 ) { |
|
handlers[i].call( this, data ); |
|
} |
|
}; |
|
|
|
this.get = function get ( key ) { |
|
return key ? state[ key ] : state; |
|
}; |
|
|
|
this.set = function set ( newState ) { |
|
var oldState = state; |
|
state = Object.assign( {}, oldState, newState ); |
|
|
|
dispatchObservers( observers.immediate, newState, oldState ); |
|
if ( mainFragment ) mainFragment.update( newState, state ); |
|
dispatchObservers( observers.deferred, newState, oldState ); |
|
|
|
while ( this.__renderHooks.length ) { |
|
var hook = this.__renderHooks.pop(); |
|
hook.fn.call( hook.context ); |
|
} |
|
}; |
|
|
|
this._mount = function mount ( target, anchor ) { |
|
mainFragment.mount( target, anchor ); |
|
}; |
|
|
|
this.observe = function ( key, callback, options ) { |
|
var group = ( options && options.defer ) ? observers.deferred : observers.immediate; |
|
|
|
( group[ key ] || ( group[ key ] = [] ) ).push( callback ); |
|
|
|
if ( !options || options.init !== false ) { |
|
callback.__calling = true; |
|
callback.call( component, state[ key ] ); |
|
callback.__calling = false; |
|
} |
|
|
|
return { |
|
cancel: function () { |
|
var index = group[ key ].indexOf( callback ); |
|
if ( ~index ) group[ key ].splice( index, 1 ); |
|
} |
|
}; |
|
}; |
|
|
|
this.on = function on ( eventName, handler ) { |
|
var handlers = callbacks[ eventName ] || ( callbacks[ eventName ] = [] ); |
|
handlers.push( handler ); |
|
|
|
return { |
|
cancel: function () { |
|
var index = handlers.indexOf( handler ); |
|
if ( ~index ) handlers.splice( index, 1 ); |
|
} |
|
}; |
|
}; |
|
|
|
this.teardown = function teardown ( detach ) { |
|
this.fire( 'teardown' ); |
|
|
|
mainFragment.teardown( detach !== false ); |
|
mainFragment = null; |
|
|
|
state = {}; |
|
}; |
|
|
|
this.root = options.root; |
|
this.yield = options.yield; |
|
|
|
this.__renderHooks = []; |
|
|
|
var mainFragment = renderMainFragment( state, this ); |
|
if ( options.target ) this._mount( options.target ); |
|
|
|
while ( this.__renderHooks.length ) { |
|
var hook = this.__renderHooks.pop(); |
|
hook.fn.call( hook.context ); |
|
} |
|
} |
|
|
|
window.app = new App({ |
|
target: document.querySelector( 'main' ), |
|
data: { |
|
answer: 42 |
|
} |
|
}); |
|
|
|
}()); |