|
/** @namespace */ |
|
var svelte = {}; |
|
|
|
/** |
|
* @typedef {Object<string, *>} |
|
*/ |
|
svelte.State; |
|
|
|
/** @abstract @constructor */ |
|
var Fragment = function() {}; |
|
/** @abstract */ |
|
Fragment.prototype.c = function() {}; |
|
/** @abstract */ |
|
Fragment.prototype.h = function() {}; |
|
/** @abstract */ |
|
Fragment.prototype.m = function() {}; |
|
/** @abstract */ |
|
Fragment.prototype.p = function() {}; |
|
/** @abstract */ |
|
Fragment.prototype.u = function() {}; |
|
/** @abstract */ |
|
Fragment.prototype.d = function() {}; |
|
|
|
/** |
|
* @returns {!svelte.State} |
|
*/ |
|
function data() { |
|
return { |
|
count: 0 |
|
}; |
|
}; |
|
|
|
/** |
|
* @param {!svelte.State} state |
|
* @param {Component} component |
|
* @constructor |
|
* @extends {Fragment} |
|
*/ |
|
function MainFragment(state, component) { |
|
this.state = state; |
|
this.component = component; |
|
this.click_handler = this.click_handler.bind(this); |
|
|
|
/** @type {*} */ |
|
this._p; |
|
/** @type {*} */ |
|
this._text; |
|
/** @type {*} */ |
|
this._text_1; |
|
/** @type {*} */ |
|
this._text_2; |
|
/** @type {*} */ |
|
this._button; |
|
} |
|
|
|
/** |
|
* @param {*} event |
|
*/ |
|
MainFragment.prototype.click_handler = function click_handler(event) { |
|
this.state = this.component.get(); |
|
this.component.set({ count: this.state.count + 1 }); |
|
}; |
|
|
|
MainFragment.prototype.c = function create() { |
|
this._p = createElement("p"); |
|
this._text = createText("Count: "); |
|
this._text_1 = createText(this.state.count); |
|
this._text_2 = createText("\n"); |
|
this._button = createElement("button"); |
|
this._button.textContent = "+1"; |
|
this.h(); |
|
}; |
|
|
|
MainFragment.prototype.h = function hydrate() { |
|
addListener(this._button, "click", this.click_handler); |
|
}; |
|
|
|
MainFragment.prototype.m = function mount(target, anchor) { |
|
insertNode(this._p, target, anchor); |
|
appendNode(this._text, this._p); |
|
appendNode(this._text_1, this._p); |
|
insertNode(this._text_2, target, anchor); |
|
insertNode(this._button, target, anchor); |
|
}; |
|
|
|
MainFragment.prototype.p = function update(changed, state) { |
|
if (changed.count) { |
|
this._text_1.data = state.count; |
|
} |
|
}; |
|
|
|
MainFragment.prototype.u = function unmount() { |
|
detachNode(this._p); |
|
detachNode(this._text_2); |
|
detachNode(this._button); |
|
}; |
|
|
|
// TODO: unused but closure doesn't get rid of it? |
|
MainFragment.prototype.d = function destroy() { |
|
removeListener(this._button, "click", this.click_handler); |
|
}; |
|
|
|
/** |
|
* @param {*} options |
|
* @constructor |
|
* @extends {Component} |
|
*/ |
|
function App(options) { |
|
init(this, options); |
|
|
|
/** @type {!svelte.State} */ |
|
this._state = assign(data(), options.data); |
|
|
|
/** @type {Fragment} */ |
|
this._fragment = new MainFragment(this._state, this); |
|
|
|
if (options.target) { |
|
this._fragment.c(); |
|
this._fragment.m(options.target, options.anchor || null); |
|
} |
|
} |
|
|
|
inherits(App, Component); |
|
App.prototype._recompute = noop; |
|
|
|
/** |
|
* @param {!Function} childCtor |
|
* @param {!Function} parentCtor |
|
*/ |
|
function inherits(childCtor, parentCtor) { |
|
/** @constructor */ |
|
function tempCtor() {} |
|
tempCtor.prototype = parentCtor.prototype; |
|
childCtor.superClass_ = parentCtor.prototype; |
|
childCtor.prototype = new tempCtor(); |
|
/** @override */ |
|
childCtor.prototype.constructor = childCtor; |
|
} |
|
|
|
function createElement(name) { |
|
return document.createElement(name); |
|
} |
|
|
|
function createText(data) { |
|
return document.createTextNode(data); |
|
} |
|
|
|
function addListener(node, event, handler) { |
|
node.addEventListener(event, handler, false); |
|
} |
|
|
|
function insertNode(node, target, anchor) { |
|
target.insertBefore(node, anchor); |
|
} |
|
|
|
function appendNode(node, target) { |
|
target.appendChild(node); |
|
} |
|
|
|
function detachNode(node) { |
|
node.parentNode.removeChild(node); |
|
} |
|
|
|
function removeListener(node, event, handler) { |
|
node.removeEventListener(event, handler, false); |
|
} |
|
|
|
function init(component, options) { |
|
component._observers = { pre: blankObject(), post: blankObject() }; |
|
component._handlers = blankObject(); |
|
component._bind = options._bind; |
|
|
|
component.options = options; |
|
component.root = options.root || component; |
|
component.store = component.root.store || options.store; |
|
} |
|
|
|
/** |
|
* @param {!svelte.State} target |
|
* @param {...!svelte.State} var_args |
|
* @returns {!svelte.State} |
|
*/ |
|
function assign(target, var_args) { |
|
var k, |
|
source, |
|
i = 1, |
|
len = arguments.length; |
|
for (; i < len; i++) { |
|
source = arguments[i]; |
|
for (k in source) target[k] = source[k]; |
|
} |
|
|
|
return target; |
|
} |
|
|
|
/** @abstract @constructor */ |
|
function Component() {}; |
|
|
|
/** |
|
* @param {!boolean=} detach |
|
*/ |
|
Component.prototype.destroy = function destroy(detach) { |
|
this.destroy = noop; |
|
this.fire('destroy'); |
|
this.set = this.get = noop; |
|
|
|
if (detach !== false) this._fragment.u(); |
|
this._fragment.d(); |
|
this._fragment = this._state = null; |
|
} |
|
|
|
/** |
|
* @param {!string=} key |
|
*/ |
|
Component.prototype.get = function get(key) { |
|
return key ? this._state[key] : this._state; |
|
} |
|
|
|
/** |
|
* @param {!string} eventName |
|
* @param {!Object<string, *>=} data |
|
*/ |
|
Component.prototype.fire = function fire(eventName, data) { |
|
var handlers = |
|
eventName in this._handlers && this._handlers[eventName].slice(); |
|
if (!handlers) return; |
|
|
|
for (var i = 0; i < handlers.length; i += 1) { |
|
handlers[i].call(this, data); |
|
} |
|
} |
|
|
|
/** |
|
* @param {!string} key |
|
* @param {*} callback |
|
* @param {!Object<string, *>=} options |
|
* @returns {*} |
|
*/ |
|
Component.prototype.observe = function observe(key, callback, options) { |
|
var group = options && options.defer |
|
? this._observers.post |
|
: this._observers.pre; |
|
|
|
(group[key] || (group[key] = [])).push(callback); |
|
|
|
if (!options || options.init !== false) { |
|
callback.__calling = true; |
|
callback.call(this, this._state[key]); |
|
callback.__calling = false; |
|
} |
|
|
|
return { |
|
cancel: function() { |
|
var index = group[key].indexOf(callback); |
|
if (~index) group[key].splice(index, 1); |
|
} |
|
}; |
|
} |
|
|
|
/** |
|
* @param {!string} eventName |
|
* @param {*} handler |
|
* @returns {*} |
|
*/ |
|
Component.prototype.on = function on(eventName, handler) { |
|
if (eventName === 'teardown') return this.on('destroy', handler); |
|
|
|
var handlers = this._handlers[eventName] || (this._handlers[eventName] = []); |
|
handlers.push(handler); |
|
|
|
return { |
|
cancel: function() { |
|
var index = handlers.indexOf(handler); |
|
if (~index) handlers.splice(index, 1); |
|
} |
|
}; |
|
} |
|
|
|
/** |
|
* @param {!svelte.State} newState |
|
*/ |
|
Component.prototype.set = function set(newState) { |
|
this._set(assign({}, newState)); |
|
if (this.root._lock) return; |
|
this.root._lock = true; |
|
callAll(this.root._beforecreate); |
|
callAll(this.root._oncreate); |
|
callAll(this.root._aftercreate); |
|
this.root._lock = false; |
|
} |
|
|
|
/** |
|
* @param {!svelte.State} newState |
|
*/ |
|
Component.prototype._set = function _set(newState) { |
|
var oldState = this._state, |
|
changed = {}, |
|
dirty = false; |
|
|
|
for (var key in newState) { |
|
if (differs(newState[key], oldState[key])) changed[key] = dirty = true; |
|
} |
|
if (!dirty) return; |
|
|
|
this._state = assign({}, oldState, newState); |
|
this._recompute(changed, this._state); |
|
if (this._bind) this._bind(changed, this._state); |
|
|
|
if (this._fragment) { |
|
dispatchObservers(this, this._observers.pre, changed, this._state, oldState); |
|
this._fragment.p(changed, this._state); |
|
dispatchObservers(this, this._observers.post, changed, this._state, oldState); |
|
} |
|
} |
|
|
|
/** |
|
* @param {*} target |
|
* @param {*} anchor |
|
*/ |
|
Component.prototype._mount = function _mount(target, anchor) { |
|
this._fragment.m(target, anchor); |
|
} |
|
|
|
Component.prototype._unmount = function _unmount() { |
|
if (this._fragment) this._fragment.u(); |
|
} |
|
|
|
function noop() {} |
|
|
|
function blankObject() { |
|
return Object.create(null); |
|
} |
|
|
|
function callAll(fns) { |
|
while (fns && fns.length) fns.pop()(); |
|
} |
|
|
|
function differs(a, b) { |
|
return a !== b || ((a && typeof a === 'object') || typeof a === 'function'); |
|
} |
|
|
|
function dispatchObservers(component, group, changed, newState, oldState) { |
|
for (var key in group) { |
|
if (!changed[key]) continue; |
|
|
|
var newValue = newState[key]; |
|
var oldValue = oldState[key]; |
|
|
|
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; |
|
} |
|
} |
|
} |
|
|
|
var app = new App({ |
|
target: document.querySelector( 'main' ) |
|
}); |
|
|
|
window.app = app; |