Migrate source code to TypeScript v2.0

pull/193/head
Andre Staltz 8 years ago
parent 8079ba7868
commit 499cd37b06
No known key found for this signature in database
GPG Key ID: 9EDE23EA7E8A4890

24
.gitignore vendored

@ -29,5 +29,29 @@ node_modules
# Vim
*.swp
# Generated JavaScript
test/browserified.js
browserified.js
h.d.ts
h.js
h.js.map
htmldomapi.d.ts
htmldomapi.js
htmldomapi.js.map
is.d.ts
is.js
is.js.map
snabbdom.bundle.d.ts
snabbdom.bundle.js
snabbdom.bundle.js.map
snabbdom.d.ts
snabbdom.js
snabbdom.js.map
thunk.d.ts
thunk.js
thunk.js.map
vnode.d.ts
vnode.js
vnode.js.map
/modules
/helpers

34
h.js

@ -1,34 +0,0 @@
var VNode = require('./vnode');
var is = require('./is');
function addNS(data, children, sel) {
data.ns = 'http://www.w3.org/2000/svg';
if (sel !== 'foreignObject' && children !== undefined) {
for (var i = 0; i < children.length; ++i) {
addNS(children[i].data, children[i].children, children[i].sel);
}
}
}
module.exports = function h(sel, b, c) {
var data = {}, children, text, i;
if (c !== undefined) {
data = b;
if (is.array(c)) { children = c; }
else if (is.primitive(c)) { text = c; }
} else if (b !== undefined) {
if (is.array(b)) { children = b; }
else if (is.primitive(b)) { text = b; }
else { data = b; }
}
if (is.array(children)) {
for (i = 0; i < children.length; ++i) {
if (is.primitive(children[i])) children[i] = VNode(undefined, undefined, undefined, children[i]);
}
}
if (sel[0] === 's' && sel[1] === 'v' && sel[2] === 'g') {
addNS(data, children, sel);
}
return VNode(sel, data, children, text, undefined);
};

@ -1,54 +0,0 @@
function createElement(tagName){
return document.createElement(tagName);
}
function createElementNS(namespaceURI, qualifiedName){
return document.createElementNS(namespaceURI, qualifiedName);
}
function createTextNode(text){
return document.createTextNode(text);
}
function insertBefore(parentNode, newNode, referenceNode){
parentNode.insertBefore(newNode, referenceNode);
}
function removeChild(node, child){
node.removeChild(child);
}
function appendChild(node, child){
node.appendChild(child);
}
function parentNode(node){
return node.parentElement;
}
function nextSibling(node){
return node.nextSibling;
}
function tagName(node){
return node.tagName;
}
function setTextContent(node, text){
node.textContent = text;
}
module.exports = {
createElement: createElement,
createElementNS: createElementNS,
createTextNode: createTextNode,
appendChild: appendChild,
removeChild: removeChild,
insertBefore: insertBefore,
parentNode: parentNode,
nextSibling: nextSibling,
tagName: tagName,
setTextContent: setTextContent
};

@ -1,4 +0,0 @@
module.exports = {
array: Array.isArray,
primitive: function(s) { return typeof s === 'string' || typeof s === 'number'; },
};

@ -1,23 +0,0 @@
function updateClass(oldVnode, vnode) {
var cur, name, elm = vnode.elm,
oldClass = oldVnode.data.class,
klass = vnode.data.class;
if (!oldClass && !klass) return;
oldClass = oldClass || {};
klass = klass || {};
for (name in oldClass) {
if (!klass[name]) {
elm.classList.remove(name);
}
}
for (name in klass) {
cur = klass[name];
if (cur !== oldClass[name]) {
elm.classList[cur ? 'add' : 'remove'](name);
}
}
}
module.exports = {create: updateClass, update: updateClass};

@ -1,23 +0,0 @@
function updateDataset(oldVnode, vnode) {
var elm = vnode.elm,
oldDataset = oldVnode.data.dataset,
dataset = vnode.data.dataset,
key
if (!oldDataset && !dataset) return;
oldDataset = oldDataset || {};
dataset = dataset || {};
for (key in oldDataset) {
if (!dataset[key]) {
delete elm.dataset[key];
}
}
for (key in dataset) {
if (oldDataset[key] !== dataset[key]) {
elm.dataset[key] = dataset[key];
}
}
}
module.exports = {create: updateDataset, update: updateDataset}

@ -1,23 +0,0 @@
function updateProps(oldVnode, vnode) {
var key, cur, old, elm = vnode.elm,
oldProps = oldVnode.data.props, props = vnode.data.props;
if (!oldProps && !props) return;
oldProps = oldProps || {};
props = props || {};
for (key in oldProps) {
if (!props[key]) {
delete elm[key];
}
}
for (key in props) {
cur = props[key];
old = oldProps[key];
if (old !== cur && (key !== 'value' || elm[key] !== cur)) {
elm[key] = cur;
}
}
}
module.exports = {create: updateProps, update: updateProps};

@ -1,69 +0,0 @@
var raf = (typeof window !== 'undefined' && window.requestAnimationFrame) || setTimeout;
var nextFrame = function(fn) { raf(function() { raf(fn); }); };
function setNextFrame(obj, prop, val) {
nextFrame(function() { obj[prop] = val; });
}
function updateStyle(oldVnode, vnode) {
var cur, name, elm = vnode.elm,
oldStyle = oldVnode.data.style,
style = vnode.data.style;
if (!oldStyle && !style) return;
oldStyle = oldStyle || {};
style = style || {};
var oldHasDel = 'delayed' in oldStyle;
for (name in oldStyle) {
if (!style[name]) {
elm.style[name] = '';
}
}
for (name in style) {
cur = style[name];
if (name === 'delayed') {
for (name in style.delayed) {
cur = style.delayed[name];
if (!oldHasDel || cur !== oldStyle.delayed[name]) {
setNextFrame(elm.style, name, cur);
}
}
} else if (name !== 'remove' && cur !== oldStyle[name]) {
elm.style[name] = cur;
}
}
}
function applyDestroyStyle(vnode) {
var style, name, elm = vnode.elm, s = vnode.data.style;
if (!s || !(style = s.destroy)) return;
for (name in style) {
elm.style[name] = style[name];
}
}
function applyRemoveStyle(vnode, rm) {
var s = vnode.data.style;
if (!s || !s.remove) {
rm();
return;
}
var name, elm = vnode.elm, idx, i = 0, maxDur = 0,
compStyle, style = s.remove, amount = 0, applied = [];
for (name in style) {
applied.push(name);
elm.style[name] = style[name];
}
compStyle = getComputedStyle(elm);
var props = compStyle['transition-property'].split(', ');
for (; i < props.length; ++i) {
if(applied.indexOf(props[i]) !== -1) amount++;
}
elm.addEventListener('transitionend', function(ev) {
if (ev.target === elm) --amount;
if (amount === 0) rm();
});
}
module.exports = {create: updateStyle, update: updateStyle, destroy: applyDestroyStyle, remove: applyRemoveStyle};

@ -3,7 +3,7 @@
"version": "0.5.4",
"description": "A virtual DOM library with focus on simplicity, modularity, powerful features and performance.",
"main": "snabbdom.js",
"typings": "type-definitions/snabbdom.d.ts",
"typings": "snabbdom.d.ts",
"directories": {
"example": "examples",
"test": "test"
@ -20,10 +20,14 @@
"gulp-uglify": "^1.5.3",
"knuth-shuffle": "^1.0.1",
"testem": "^1.0.2",
"typescript": "^2.0.6",
"xyz": "0.5.x"
},
"scripts": {
"pretest": "npm run compile",
"test": "testem",
"compile": "tsc",
"prepublish": "npm run compile",
"release-major": "xyz --repo git@github.com:paldepind/snabbdom.git --increment major",
"release-minor": "xyz --repo git@github.com:paldepind/snabbdom.git --increment minor",
"release-patch": "xyz --repo git@github.com:paldepind/snabbdom.git --increment patch"

@ -1,11 +0,0 @@
var snabbdom = require('./snabbdom');
var patch = snabbdom.init([ // Init patch function with choosen modules
require('./modules/attributes'), // makes it easy to toggle classes
require('./modules/class'), // makes it easy to toggle classes
require('./modules/props'), // for setting properties on DOM elements
require('./modules/style'), // handles styling on elements with support for animations
require('./modules/eventlisteners'), // attaches event listeners
]);
var h = require('./h'); // helper function for creating vnodes
module.exports = { patch: patch, h: h }

@ -1,260 +0,0 @@
// jshint newcap: false
/* global require, module, document, Node */
'use strict';
var VNode = require('./vnode');
var is = require('./is');
var domApi = require('./htmldomapi');
function isUndef(s) { return s === undefined; }
function isDef(s) { return s !== undefined; }
var emptyNode = VNode('', {}, [], undefined, undefined);
function sameVnode(vnode1, vnode2) {
return vnode1.key === vnode2.key && vnode1.sel === vnode2.sel;
}
function createKeyToOldIdx(children, beginIdx, endIdx) {
var i, map = {}, key;
for (i = beginIdx; i <= endIdx; ++i) {
key = children[i].key;
if (isDef(key)) map[key] = i;
}
return map;
}
var hooks = ['create', 'update', 'remove', 'destroy', 'pre', 'post'];
function init(modules, api) {
var i, j, cbs = {};
if (isUndef(api)) api = domApi;
for (i = 0; i < hooks.length; ++i) {
cbs[hooks[i]] = [];
for (j = 0; j < modules.length; ++j) {
if (modules[j][hooks[i]] !== undefined) cbs[hooks[i]].push(modules[j][hooks[i]]);
}
}
function emptyNodeAt(elm) {
var id = elm.id ? '#' + elm.id : '';
var c = elm.className ? '.' + elm.className.split(' ').join('.') : '';
return VNode(api.tagName(elm).toLowerCase() + id + c, {}, [], undefined, elm);
}
function createRmCb(childElm, listeners) {
return function() {
if (--listeners === 0) {
var parent = api.parentNode(childElm);
api.removeChild(parent, childElm);
}
};
}
function createElm(vnode, insertedVnodeQueue) {
var i, data = vnode.data;
if (isDef(data)) {
if (isDef(i = data.hook) && isDef(i = i.init)) {
i(vnode);
data = vnode.data;
}
}
var elm, children = vnode.children, sel = vnode.sel;
if (isDef(sel)) {
// Parse selector
var hashIdx = sel.indexOf('#');
var dotIdx = sel.indexOf('.', hashIdx);
var hash = hashIdx > 0 ? hashIdx : sel.length;
var dot = dotIdx > 0 ? dotIdx : sel.length;
var tag = hashIdx !== -1 || dotIdx !== -1 ? sel.slice(0, Math.min(hash, dot)) : sel;
elm = vnode.elm = isDef(data) && isDef(i = data.ns) ? api.createElementNS(i, tag)
: api.createElement(tag);
if (hash < dot) elm.id = sel.slice(hash + 1, dot);
if (dotIdx > 0) elm.className = sel.slice(dot + 1).replace(/\./g, ' ');
if (is.array(children)) {
for (i = 0; i < children.length; ++i) {
api.appendChild(elm, createElm(children[i], insertedVnodeQueue));
}
} else if (is.primitive(vnode.text)) {
api.appendChild(elm, api.createTextNode(vnode.text));
}
for (i = 0; i < cbs.create.length; ++i) cbs.create[i](emptyNode, vnode);
i = vnode.data.hook; // Reuse variable
if (isDef(i)) {
if (i.create) i.create(emptyNode, vnode);
if (i.insert) insertedVnodeQueue.push(vnode);
}
} else {
elm = vnode.elm = api.createTextNode(vnode.text);
}
return vnode.elm;
}
function addVnodes(parentElm, before, vnodes, startIdx, endIdx, insertedVnodeQueue) {
for (; startIdx <= endIdx; ++startIdx) {
api.insertBefore(parentElm, createElm(vnodes[startIdx], insertedVnodeQueue), before);
}
}
function invokeDestroyHook(vnode) {
var i, j, data = vnode.data;
if (isDef(data)) {
if (isDef(i = data.hook) && isDef(i = i.destroy)) i(vnode);
for (i = 0; i < cbs.destroy.length; ++i) cbs.destroy[i](vnode);
if (isDef(i = vnode.children)) {
for (j = 0; j < vnode.children.length; ++j) {
invokeDestroyHook(vnode.children[j]);
}
}
}
}
function removeVnodes(parentElm, vnodes, startIdx, endIdx) {
for (; startIdx <= endIdx; ++startIdx) {
var i, listeners, rm, ch = vnodes[startIdx];
if (isDef(ch)) {
if (isDef(ch.sel)) {
invokeDestroyHook(ch);
listeners = cbs.remove.length + 1;
rm = createRmCb(ch.elm, listeners);
for (i = 0; i < cbs.remove.length; ++i) cbs.remove[i](ch, rm);
if (isDef(i = ch.data) && isDef(i = i.hook) && isDef(i = i.remove)) {
i(ch, rm);
} else {
rm();
}
} else { // Text node
api.removeChild(parentElm, ch.elm);
}
}
}
}
function updateChildren(parentElm, oldCh, newCh, insertedVnodeQueue) {
var oldStartIdx = 0, newStartIdx = 0;
var oldEndIdx = oldCh.length - 1;
var oldStartVnode = oldCh[0];
var oldEndVnode = oldCh[oldEndIdx];
var newEndIdx = newCh.length - 1;
var newStartVnode = newCh[0];
var newEndVnode = newCh[newEndIdx];
var oldKeyToIdx, idxInOld, elmToMove, before;
while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
if (isUndef(oldStartVnode)) {
oldStartVnode = oldCh[++oldStartIdx]; // Vnode has been moved left
} else if (isUndef(oldEndVnode)) {
oldEndVnode = oldCh[--oldEndIdx];
} else if (sameVnode(oldStartVnode, newStartVnode)) {
patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue);
oldStartVnode = oldCh[++oldStartIdx];
newStartVnode = newCh[++newStartIdx];
} else if (sameVnode(oldEndVnode, newEndVnode)) {
patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue);
oldEndVnode = oldCh[--oldEndIdx];
newEndVnode = newCh[--newEndIdx];
} else if (sameVnode(oldStartVnode, newEndVnode)) { // Vnode moved right
patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue);
api.insertBefore(parentElm, oldStartVnode.elm, api.nextSibling(oldEndVnode.elm));
oldStartVnode = oldCh[++oldStartIdx];
newEndVnode = newCh[--newEndIdx];
} else if (sameVnode(oldEndVnode, newStartVnode)) { // Vnode moved left
patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue);
api.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm);
oldEndVnode = oldCh[--oldEndIdx];
newStartVnode = newCh[++newStartIdx];
} else {
if (isUndef(oldKeyToIdx)) oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx);
idxInOld = oldKeyToIdx[newStartVnode.key];
if (isUndef(idxInOld)) { // New element
api.insertBefore(parentElm, createElm(newStartVnode, insertedVnodeQueue), oldStartVnode.elm);
newStartVnode = newCh[++newStartIdx];
} else {
elmToMove = oldCh[idxInOld];
patchVnode(elmToMove, newStartVnode, insertedVnodeQueue);
oldCh[idxInOld] = undefined;
api.insertBefore(parentElm, elmToMove.elm, oldStartVnode.elm);
newStartVnode = newCh[++newStartIdx];
}
}
}
if (oldStartIdx > oldEndIdx) {
before = isUndef(newCh[newEndIdx+1]) ? null : newCh[newEndIdx+1].elm;
addVnodes(parentElm, before, newCh, newStartIdx, newEndIdx, insertedVnodeQueue);
} else if (newStartIdx > newEndIdx) {
removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx);
}
}
function patchVnode(oldVnode, vnode, insertedVnodeQueue) {
var i, hook;
if (isDef(i = vnode.data) && isDef(hook = i.hook) && isDef(i = hook.prepatch)) {
i(oldVnode, vnode);
}
var elm = vnode.elm = oldVnode.elm, oldCh = oldVnode.children, ch = vnode.children;
if (oldVnode === vnode) return;
if (!sameVnode(oldVnode, vnode)) {
var parentElm = api.parentNode(oldVnode.elm);
elm = createElm(vnode, insertedVnodeQueue);
api.insertBefore(parentElm, elm, oldVnode.elm);
removeVnodes(parentElm, [oldVnode], 0, 0);
return;
}
if (isDef(vnode.data)) {
for (i = 0; i < cbs.update.length; ++i) cbs.update[i](oldVnode, vnode);
i = vnode.data.hook;
if (isDef(i) && isDef(i = i.update)) i(oldVnode, vnode);
}
if (isUndef(vnode.text)) {
if (isDef(oldCh) && isDef(ch)) {
if (oldCh !== ch) updateChildren(elm, oldCh, ch, insertedVnodeQueue);
} else if (isDef(ch)) {
if (isDef(oldVnode.text)) api.setTextContent(elm, '');
addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue);
} else if (isDef(oldCh)) {
removeVnodes(elm, oldCh, 0, oldCh.length - 1);
} else if (isDef(oldVnode.text)) {
api.setTextContent(elm, '');
}
} else if (oldVnode.text !== vnode.text) {
api.setTextContent(elm, vnode.text);
}
if (isDef(hook) && isDef(i = hook.postpatch)) {
i(oldVnode, vnode);
}
}
return function(oldVnode, vnode) {
var i, elm, parent;
var insertedVnodeQueue = [];
for (i = 0; i < cbs.pre.length; ++i) cbs.pre[i]();
if (isUndef(oldVnode.sel)) {
oldVnode = emptyNodeAt(oldVnode);
}
if (sameVnode(oldVnode, vnode)) {
patchVnode(oldVnode, vnode, insertedVnodeQueue);
} else {
elm = oldVnode.elm;
parent = api.parentNode(elm);
createElm(vnode, insertedVnodeQueue);
if (parent !== null) {
api.insertBefore(parent, vnode.elm, api.nextSibling(elm));
removeVnodes(parent, [oldVnode], 0, 0);
}
}
for (i = 0; i < insertedVnodeQueue.length; ++i) {
insertedVnodeQueue[i].data.hook.insert(insertedVnodeQueue[i]);
}
for (i = 0; i < cbs.post.length; ++i) cbs.post[i]();
return vnode;
};
}
module.exports = {init: init};

@ -0,0 +1,42 @@
import {VNode} from './interfaces';
import vnode = require('./vnode');
import is = require('./is');
function addNS(data: any, children: Array<VNode> | undefined, sel: string | undefined): void {
data.ns = 'http://www.w3.org/2000/svg';
if (sel !== 'foreignObject' && children !== undefined) {
for (let i = 0; i < children.length; ++i) {
addNS(children[i].data, (children[i] as VNode).children as Array<VNode>, children[i].sel);
}
}
}
function h(sel: string): VNode;
function h(sel: string, data: any): VNode;
function h(sel: string, text: string): VNode;
function h(sel: string, children: Array<VNode>): VNode;
function h(sel: string, data: any, text: string): VNode;
function h(sel: string, data: any, children: Array<VNode>): VNode;
function h(sel: any, b?: any, c?: any): VNode {
var data = {}, children: any, text: any, i: number;
if (c !== undefined) {
data = b;
if (is.array(c)) { children = c; }
else if (is.primitive(c)) { text = c; }
} else if (b !== undefined) {
if (is.array(b)) { children = b; }
else if (is.primitive(b)) { text = b; }
else { data = b; }
}
if (is.array(children)) {
for (i = 0; i < children.length; ++i) {
if (is.primitive(children[i])) children[i] = (vnode as any)(undefined, undefined, undefined, children[i]);
}
}
if (sel[0] === 's' && sel[1] === 'v' && sel[2] === 'g') {
addNS(data, children, sel);
}
return vnode(sel, data, children, text, undefined);
};
export = h;

@ -1,26 +1,28 @@
function pre(vnode, newVnode) {
var attachData = vnode.data.attachData;
import {VNode, VNodeData} from '../interfaces';
function pre(vnode: VNode, newVnode: VNode): void {
var attachData = (vnode.data as VNodeData).attachData;
// Copy created placeholder and real element from old vnode
newVnode.data.attachData.placeholder = attachData.placeholder;
newVnode.data.attachData.real = attachData.real;
(newVnode.data as VNodeData).attachData.placeholder = attachData.placeholder;
(newVnode.data as VNodeData).attachData.real = attachData.real;
// Mount real element in vnode so the patch process operates on it
vnode.elm = vnode.data.attachData.real;
vnode.elm = (vnode.data as VNodeData).attachData.real;
}
function post(_, vnode) {
function post(_: any, vnode: VNode): void {
// Mount dummy placeholder in vnode so potential reorders use it
vnode.elm = vnode.data.attachData.placeholder;
vnode.elm = (vnode.data as VNodeData).attachData.placeholder;
}
function destroy(vnode) {
function destroy(vnode: VNode): void {
// Remove placeholder
vnode.elm.parentElement.removeChild(vnode.elm);
vnode.elm && vnode.elm.parentElement.removeChild(vnode.elm);
// Remove real element from where it was inserted
vnode.elm = vnode.data.attachData.real;
vnode.elm = (vnode.data as VNodeData).attachData.real;
}
function create(_, vnode) {
var real = vnode.elm, attachData = vnode.data.attachData;
function create(_: any, vnode: VNode): void {
var real = vnode.elm, attachData = (vnode.data as VNodeData).attachData;
var placeholder = document.createElement('span');
// Replace actual element with dummy placeholder
// Snabbdom will then insert placeholder instead
@ -30,7 +32,7 @@ function create(_, vnode) {
attachData.placeholder = placeholder;
}
module.exports = function(target, vnode) {
export = function attachTo(target: Element, vnode: VNode): VNode {
if (vnode.data === undefined) vnode.data = {};
if (vnode.data.hook === undefined) vnode.data.hook = {};
var data = vnode.data;

@ -0,0 +1,54 @@
import {DOMAPI} from './interfaces';
function createElement(tagName: any): HTMLElement {
return document.createElement(tagName);
}
function createElementNS(namespaceURI: string, qualifiedName: string): Element {
return document.createElementNS(namespaceURI, qualifiedName);
}
function createTextNode(text: string): Text {
return document.createTextNode(text);
}
function insertBefore(parentNode: Node, newNode: Node, referenceNode: Node | null): void {
parentNode.insertBefore(newNode, referenceNode);
}
function removeChild(node: Node, child: Node): void {
node.removeChild(child);
}
function appendChild(node: Node, child: Node): void {
node.appendChild(child);
}
function parentNode(node: Node): HTMLElement {
return node.parentElement;
}
function nextSibling(node: Node): Node {
return node.nextSibling;
}
function tagName(elm: Element): string {
return elm.tagName;
}
function setTextContent(node: Node, text: string | null): void {
node.textContent = text;
}
export = {
createElement,
createElementNS,
createTextNode,
insertBefore,
removeChild,
appendChild,
parentNode,
nextSibling,
tagName,
setTextContent,
} as DOMAPI;

@ -0,0 +1,87 @@
export interface VNodeData {
// modules - use any because Object type is useless
props?: any;
attrs?: any;
class?: any;
style?: any;
dataset?: any;
on?: any;
hero?: any;
attachData?: any;
[key: string]: any; // for any other 3rd party module
// end of modules
hook?: Hooks;
key?: string | number;
ns?: string; // for SVGs
fn?: () => VNode; // for thunks
args?: Array<any>; // for thunks
}
export interface VNode {
sel: string | undefined;
data: VNodeData | undefined;
children: Array<VNode | string> | undefined;
elm: Element | Text | undefined;
text: string | undefined;
key: string | number | undefined;
}
export interface ThunkData extends VNodeData {
fn: () => VNode;
args: Array<any>;
}
export interface Thunk extends VNode {
data: ThunkData;
}
export interface ThunkFn {
(sel: string, fn: Function, args: Array<any>): Thunk;
(sel: string, key: any, fn: Function, args: Array<any>): Thunk;
}
export type PreHook = () => any;
export type InitHook = (vNode: VNode) => any;
export type CreateHook = (emptyVNode: VNode, vNode: VNode) => any;
export type InsertHook = (vNode: VNode) => any;
export type PrePatchHook = (oldVNode: VNode, vNode: VNode) => any;
export type UpdateHook = (oldVNode: VNode, vNode: VNode) => any;
export type PostPatchHook = (oldVNode: VNode, vNode: VNode) => any;
export type DestroyHook = (vNode: VNode) => any;
export type RemoveHook = (vNode: VNode, removeCallback: () => void) => any;
export type PostHook = () => any;
export interface Hooks {
pre?: PreHook;
init?: InitHook;
create?: CreateHook;
insert?: InsertHook;
prepatch?: PrePatchHook;
update?: UpdateHook;
postpatch?: PostPatchHook;
destroy?: DestroyHook;
remove?: RemoveHook;
post?: PostHook;
}
export interface Module {
pre?: PreHook;
create?: CreateHook;
update?: UpdateHook;
destroy?: DestroyHook;
remove?: RemoveHook;
post?: PostHook;
}
export interface DOMAPI {
createElement: (tagName: any) => HTMLElement;
createElementNS: (namespaceURI: string, qualifiedName: string) => Element;
createTextNode: (text: string) => Text;
insertBefore: (parentNode: Node, newNode: Node, referenceNode: Node | null) => void;
removeChild: (node: Node, child: Node) => void;
appendChild: (node: Node, child: Node) => void;
parentNode: (node: Node) => HTMLElement;
nextSibling: (node: Node) => Node;
tagName: (elm: Element) => string;
setTextContent: (node: Node, text: string | null) => void;
}

@ -0,0 +1,6 @@
export = {
array: Array.isArray,
primitive: function primitive(s: any): boolean {
return typeof s === 'string' || typeof s === 'number';
},
};

@ -1,22 +1,25 @@
var NamespaceURIs = {
import {VNode, VNodeData, Module} from '../interfaces';
const NamespaceURIs = {
"xlink": "http://www.w3.org/1999/xlink"
};
var booleanAttrs = ["allowfullscreen", "async", "autofocus", "autoplay", "checked", "compact", "controls", "declare",
const booleanAttrs = ["allowfullscreen", "async", "autofocus", "autoplay", "checked", "compact", "controls", "declare",
"default", "defaultchecked", "defaultmuted", "defaultselected", "defer", "disabled", "draggable",
"enabled", "formnovalidate", "hidden", "indeterminate", "inert", "ismap", "itemscope", "loop", "multiple",
"muted", "nohref", "noresize", "noshade", "novalidate", "nowrap", "open", "pauseonexit", "readonly",
"required", "reversed", "scoped", "seamless", "selected", "sortable", "spellcheck", "translate",
"truespeed", "typemustmatch", "visible"];
var booleanAttrsDict = Object.create(null);
for(var i=0, len = booleanAttrs.length; i < len; i++) {
const booleanAttrsDict = Object.create(null);
for (let i = 0, len = booleanAttrs.length; i < len; i++) {
booleanAttrsDict[booleanAttrs[i]] = true;
}
function updateAttrs(oldVnode, vnode) {
var key, cur, old, elm = vnode.elm,
oldAttrs = oldVnode.data.attrs, attrs = vnode.data.attrs, namespaceSplit;
function updateAttrs(oldVnode: VNode, vnode: VNode): void {
var key: string, cur: any, old: any, elm: Element = vnode.elm as Element,
oldAttrs = (oldVnode.data as VNodeData).attrs,
attrs = (vnode.data as VNodeData).attrs, namespaceSplit: Array<string>;
if (!oldAttrs && !attrs) return;
oldAttrs = oldAttrs || {};
@ -27,12 +30,12 @@ function updateAttrs(oldVnode, vnode) {
cur = attrs[key];
old = oldAttrs[key];
if (old !== cur) {
if(!cur && booleanAttrsDict[key])
if (!cur && booleanAttrsDict[key])
elm.removeAttribute(key);
else {
namespaceSplit = key.split(":");
if(namespaceSplit.length > 1 && NamespaceURIs.hasOwnProperty(namespaceSplit[0]))
elm.setAttributeNS(NamespaceURIs[namespaceSplit[0]], key, cur);
if (namespaceSplit.length > 1 && NamespaceURIs.hasOwnProperty(namespaceSplit[0]))
elm.setAttributeNS((NamespaceURIs as any)[namespaceSplit[0]], key, cur);
else
elm.setAttribute(key, cur);
}
@ -48,4 +51,4 @@ function updateAttrs(oldVnode, vnode) {
}
}
module.exports = {create: updateAttrs, update: updateAttrs};
export = {create: updateAttrs, update: updateAttrs} as Module;

@ -0,0 +1,25 @@
import {VNode, VNodeData, Module} from '../interfaces';
function updateClass(oldVnode: VNode, vnode: VNode): void {
var cur: any, name: string, elm: Element = vnode.elm as Element,
oldClass = (oldVnode.data as VNodeData).class,
klass = (vnode.data as VNodeData).class;
if (!oldClass && !klass) return;
oldClass = oldClass || {};
klass = klass || {};
for (name in oldClass) {
if (!klass[name]) {
elm.classList.remove(name);
}
}
for (name in klass) {
cur = klass[name];
if (cur !== oldClass[name]) {
(elm.classList as any)[cur ? 'add' : 'remove'](name);
}
}
}
export = {create: updateClass, update: updateClass} as Module;

@ -0,0 +1,25 @@
import {VNode, VNodeData, Module} from '../interfaces';
function updateDataset(oldVnode: VNode, vnode: VNode): void {
var elm: HTMLElement = vnode.elm as HTMLElement,
oldDataset = (oldVnode.data as VNodeData).dataset,
dataset = (vnode.data as VNodeData).dataset,
key: string;
if (!oldDataset && !dataset) return;
oldDataset = oldDataset || {};
dataset = dataset || {};
for (key in oldDataset) {
if (!dataset[key]) {
delete elm.dataset[key];
}
}
for (key in dataset) {
if (oldDataset[key] !== dataset[key]) {
elm.dataset[key] = dataset[key];
}
}
}
export = {create: updateDataset, update: updateDataset} as Module;

@ -1,4 +1,6 @@
function invokeHandler(handler, vnode, event) {
import {VNode, VNodeData, Module} from '../interfaces';
function invokeHandler(handler: any, vnode?: VNode, event?: Event): void {
if (typeof handler === "function") {
// call function handler
handler.call(vnode, event, vnode);
@ -23,9 +25,9 @@ function invokeHandler(handler, vnode, event) {
}
}
function handleEvent(event, vnode) {
function handleEvent(event: Event, vnode: VNode) {
var name = event.type,
on = vnode.data.on;
on = (vnode.data as VNodeData).on;
// call event handler(s) if exists
if (on && on[name]) {
@ -34,18 +36,18 @@ function handleEvent(event, vnode) {
}
function createListener() {
return function handler(event) {
handleEvent(event, handler.vnode);
return function handler(event: Event) {
handleEvent(event, (handler as any).vnode);
}
}
function updateEventListeners(oldVnode, vnode) {
var oldOn = oldVnode.data.on,
oldListener = oldVnode.listener,
oldElm = oldVnode.elm,
on = vnode && vnode.data.on,
elm = vnode && vnode.elm,
name;
function updateEventListeners(oldVnode: VNode, vnode?: VNode): void {
var oldOn = (oldVnode.data as VNodeData).on,
oldListener = (oldVnode as any).listener,
oldElm: Element = oldVnode.elm as Element,
on = vnode && (vnode.data as VNodeData).on,
elm: Element = (vnode && vnode.elm) as Element,
name: string;
// optimization for reused immutable handlers
if (oldOn === on) {
@ -73,7 +75,7 @@ function updateEventListeners(oldVnode, vnode) {
// add new listeners which has not already attached
if (on) {
// reuse existing listener or create new
var listener = vnode.listener = oldVnode.listener || createListener();
var listener = (vnode as any).listener = (oldVnode as any).listener || createListener();
// update vnode for listener
listener.vnode = vnode;
@ -94,8 +96,8 @@ function updateEventListeners(oldVnode, vnode) {
}
}
module.exports = {
export = {
create: updateEventListeners,
update: updateEventListeners,
destroy: updateEventListeners
};
} as Module;

@ -1,12 +1,14 @@
import {VNode, VNodeData, Module} from '../interfaces';
var raf = (typeof window !== 'undefined' && window.requestAnimationFrame) || setTimeout;
var nextFrame = function(fn) { raf(function() { raf(fn); }); };
var nextFrame = function(fn: any) { raf(function() { raf(fn); }); };
function setNextFrame(obj, prop, val) {
function setNextFrame(obj: any, prop: string, val: any): void {
nextFrame(function() { obj[prop] = val; });
}
function getTextNodeRect(textNode) {
var rect;
function getTextNodeRect(textNode: Text): ClientRect | undefined {
var rect: ClientRect | undefined;
if (document.createRange) {
var range = document.createRange();
range.selectNodeContents(textNode);
@ -17,7 +19,9 @@ function getTextNodeRect(textNode) {
return rect;
}
function calcTransformOrigin(isTextNode, textRect, boundingRect) {
function calcTransformOrigin(isTextNode: boolean,
textRect: ClientRect | undefined,
boundingRect: ClientRect): string {
if (isTextNode) {
if (textRect) {
//calculate pixels to center of text from left edge of bounding box
@ -29,73 +33,78 @@ function calcTransformOrigin(isTextNode, textRect, boundingRect) {
return '0 0'; //top left
}
function getTextDx(oldTextRect, newTextRect) {
function getTextDx(oldTextRect: ClientRect | undefined,
newTextRect: ClientRect | undefined): number {
if (oldTextRect && newTextRect) {
return ((oldTextRect.left + oldTextRect.width/2) - (newTextRect.left + newTextRect.width/2));
}
return 0;
}
function getTextDy(oldTextRect, newTextRect) {
function getTextDy(oldTextRect: ClientRect | undefined,
newTextRect: ClientRect | undefined): number {
if (oldTextRect && newTextRect) {
return ((oldTextRect.top + oldTextRect.height/2) - (newTextRect.top + newTextRect.height/2));
}
return 0;
}
function isTextElement(elm) {
function isTextElement(elm: Element | Text): elm is Text {
return elm.childNodes.length === 1 && elm.childNodes[0].nodeType === 3;
}
var removed, created;
var removed: any, created: any;
function pre(oldVnode, vnode) {
function pre() {
removed = {};
created = [];
}
function create(oldVnode, vnode) {
var hero = vnode.data.hero;
function create(oldVnode: VNode, vnode: VNode): void {
var hero = (vnode.data as VNodeData).hero;
if (hero && hero.id) {
created.push(hero.id);
created.push(vnode);
}
}
function destroy(vnode) {
var hero = vnode.data.hero;
function destroy(vnode: VNode): void {
var hero = (vnode.data as VNodeData).hero;
if (hero && hero.id) {
var elm = vnode.elm;
vnode.isTextNode = isTextElement(elm); //is this a text node?
vnode.boundingRect = elm.getBoundingClientRect(); //save the bounding rectangle to a new property on the vnode
vnode.textRect = vnode.isTextNode ? getTextNodeRect(elm.childNodes[0]) : null; //save bounding rect of inner text node
var computedStyle = window.getComputedStyle(elm, null); //get current styles (includes inherited properties)
vnode.savedStyle = JSON.parse(JSON.stringify(computedStyle)); //save a copy of computed style values
(vnode as any).isTextNode = isTextElement(elm as Element | Text); //is this a text node?
(vnode as any).boundingRect = (elm as Element).getBoundingClientRect(); //save the bounding rectangle to a new property on the vnode
(vnode as any).textRect = (vnode as any).isTextNode ? getTextNodeRect((elm as Element).childNodes[0] as Text) : null; //save bounding rect of inner text node
var computedStyle = window.getComputedStyle(elm as Element, void 0); //get current styles (includes inherited properties)
(vnode as any).savedStyle = JSON.parse(JSON.stringify(computedStyle)); //save a copy of computed style values
removed[hero.id] = vnode;
}
}
function post() {
var i, id, newElm, oldVnode, oldElm, hRatio, wRatio,
oldRect, newRect, dx, dy, origTransform, origTransition,
newStyle, oldStyle, newComputedStyle, isTextNode,
newTextRect, oldTextRect;
var i: number, id: any, newElm: Element, oldVnode: VNode, oldElm: Element,
hRatio: number, wRatio: number,
oldRect: ClientRect, newRect: ClientRect, dx: number, dy: number,
origTransform: string | null, origTransition: string | null,
newStyle: CSSStyleDeclaration, oldStyle: CSSStyleDeclaration,
newComputedStyle: CSSStyleDeclaration, isTextNode: boolean,
newTextRect: ClientRect | undefined, oldTextRect: ClientRect | undefined;
for (i = 0; i < created.length; i += 2) {
id = created[i];
newElm = created[i+1].elm;
oldVnode = removed[id];
if (oldVnode) {
isTextNode = oldVnode.isTextNode && isTextElement(newElm); //Are old & new both text?
newStyle = newElm.style;
newComputedStyle = window.getComputedStyle(newElm, null); //get full computed style for new element
oldElm = oldVnode.elm;
oldStyle = oldElm.style;
isTextNode = (oldVnode as any).isTextNode && isTextElement(newElm); //Are old & new both text?
newStyle = (newElm as HTMLElement).style;
newComputedStyle = window.getComputedStyle(newElm, void 0); //get full computed style for new element
oldElm = oldVnode.elm as Element;
oldStyle = (oldElm as HTMLElement).style;
//Overall element bounding boxes
newRect = newElm.getBoundingClientRect();
oldRect = oldVnode.boundingRect; //previously saved bounding rect
oldRect = (oldVnode as any).boundingRect; //previously saved bounding rect
//Text node bounding boxes & distances
if (isTextNode) {
newTextRect = getTextNodeRect(newElm.childNodes[0]);
oldTextRect = oldVnode.textRect;
newTextRect = getTextNodeRect(newElm.childNodes[0] as Text);
oldTextRect = (oldVnode as any).textRect;
dx = getTextDx(oldTextRect, newTextRect);
dy = getTextDy(oldTextRect, newTextRect);
} else {
@ -119,13 +128,13 @@ function post() {
setNextFrame(newStyle, 'transform', origTransform);
setNextFrame(newStyle, 'opacity', '1');
// Animate old element
for (var key in oldVnode.savedStyle) { //re-apply saved inherited properties
if (parseInt(key) != key) {
for (var key in (oldVnode as any).savedStyle) { //re-apply saved inherited properties
if (parseInt(key) != key as any as number) {
var ms = key.substring(0,2) === 'ms';
var moz = key.substring(0,3) === 'moz';
var webkit = key.substring(0,6) === 'webkit';
if (!ms && !moz && !webkit) //ignore prefixed style properties
oldStyle[key] = oldVnode.savedStyle[key];
if (!ms && !moz && !webkit) //ignore prefixed style properties
(oldStyle as any)[key] = (oldVnode as any).savedStyle[key];
}
}
oldStyle.position = 'absolute';
@ -133,20 +142,20 @@ function post() {
oldStyle.left = oldRect.left + 'px';
oldStyle.width = oldRect.width + 'px'; //Needed for elements who were sized relative to their parents
oldStyle.height = oldRect.height + 'px'; //Needed for elements who were sized relative to their parents
oldStyle.margin = 0; //Margin on hero element leads to incorrect positioning
oldStyle.margin = '0'; //Margin on hero element leads to incorrect positioning
oldStyle.transformOrigin = calcTransformOrigin(isTextNode, oldTextRect, oldRect);
oldStyle.transform = '';
oldStyle.opacity = '1';
document.body.appendChild(oldElm);
setNextFrame(oldStyle, 'transform', 'translate('+ -dx +'px, '+ -dy +'px) scale('+wRatio+', '+hRatio+')'); //scale must be on far right for translate to be correct
setNextFrame(oldStyle, 'opacity', '0');
oldElm.addEventListener('transitionend', function(ev) {
oldElm.addEventListener('transitionend', function (ev: TransitionEvent) {
if (ev.propertyName === 'transform')
document.body.removeChild(ev.target);
document.body.removeChild(ev.target as Node);
});
}
}
removed = created = undefined;
}
module.exports = {pre: pre, create: create, destroy: destroy, post: post};
export = {pre: pre, create: create, destroy: destroy, post: post} as Module;

@ -0,0 +1,26 @@
import {VNode, VNodeData, Module} from '../interfaces';
function updateProps(oldVnode: VNode, vnode: VNode): void {
var key: string, cur: any, old: any, elm = vnode.elm,
oldProps = (oldVnode.data as VNodeData).props,
props = (vnode.data as VNodeData).props;
if (!oldProps && !props) return;
oldProps = oldProps || {};
props = props || {};
for (key in oldProps) {
if (!props[key]) {
delete (elm as any)[key];
}
}
for (key in props) {
cur = props[key];
old = oldProps[key];
if (old !== cur && (key !== 'value' || (elm as any)[key] !== cur)) {
(elm as any)[key] = cur;
}
}
}
export = {create: updateProps, update: updateProps} as Module;

@ -0,0 +1,76 @@
import {VNode, VNodeData, Module} from '../interfaces';
var raf = (typeof window !== 'undefined' && window.requestAnimationFrame) || setTimeout;
var nextFrame = function(fn: any) { raf(function() { raf(fn); }); };
function setNextFrame(obj: any, prop: string, val: any): void {
nextFrame(function() { obj[prop] = val; });
}
function updateStyle(oldVnode: VNode, vnode: VNode): void {
var cur: any, name: string, elm = vnode.elm,
oldStyle = (oldVnode.data as VNodeData).style,
style = (vnode.data as VNodeData).style;
if (!oldStyle && !style) return;
oldStyle = oldStyle || {};
style = style || {};
var oldHasDel = 'delayed' in oldStyle;
for (name in oldStyle) {
if (!style[name]) {
(elm as any).style[name] = '';
}
}
for (name in style) {
cur = style[name];
if (name === 'delayed') {
for (name in style.delayed) {
cur = style.delayed[name];
if (!oldHasDel || cur !== oldStyle.delayed[name]) {
setNextFrame((elm as any).style, name, cur);
}
}
} else if (name !== 'remove' && cur !== oldStyle[name]) {
(elm as any).style[name] = cur;
}
}
}
function applyDestroyStyle(vnode: VNode): void {
var style: any, name: string, elm = vnode.elm, s = (vnode.data as VNodeData).style;
if (!s || !(style = s.destroy)) return;
for (name in style) {
(elm as any).style[name] = style[name];
}
}
function applyRemoveStyle(vnode: VNode, rm: () => void): void {
var s = (vnode.data as VNodeData).style;
if (!s || !s.remove) {
rm();
return;
}
var name: string, elm = vnode.elm, i = 0, compStyle: CSSStyleDeclaration,
style = s.remove, amount = 0, applied: Array<string> = [];
for (name in style) {
applied.push(name);
(elm as any).style[name] = style[name];
}
compStyle = getComputedStyle(elm as Element);
var props = (compStyle as any)['transition-property'].split(', ');
for (; i < props.length; ++i) {
if(applied.indexOf(props[i]) !== -1) amount++;
}
(elm as Element).addEventListener('transitionend', function (ev: TransitionEvent) {
if (ev.target === elm) --amount;
if (amount === 0) rm();
});
}
export = {
create: updateStyle,
update: updateStyle,
destroy: applyDestroyStyle,
remove: applyRemoveStyle
} as Module;

@ -0,0 +1,16 @@
import snabbdom = require('./snabbdom');
import attributesModule = require('./modules/attributes'); // for setting attributes on DOM elements
import classModule = require('./modules/class'); // makes it easy to toggle classes
import propsModule = require('./modules/props'); // for setting properties on DOM elements
import styleModule = require('./modules/style'); // handles styling on elements with support for animations
import eventListenersModule = require('./modules/eventlisteners'); // attaches event listeners
import h = require('./h'); // helper function for creating vnodes
var patch = snabbdom.init([ // Init patch function with choosen modules
attributesModule,
classModule,
propsModule,
styleModule,
eventListenersModule
]) as (oldVNode: any, vnode: any) => any;
export = { patch, h: h as any };

@ -0,0 +1,272 @@
/* global require, module, document, Node */
import {VNode, VNodeData, Hooks, DOMAPI} from './interfaces';
import vnode = require('./vnode');
import is = require('./is');
import htmlDomApi = require('./htmldomapi');
function isUndef(s: any): boolean { return s === undefined; }
function isDef(s: any): boolean { return s !== undefined; }
type VNodeQueue = Array<VNode>;
const emptyNode = vnode('', {}, [], undefined, undefined);
function sameVnode(vnode1: VNode, vnode2: VNode): boolean {
return vnode1.key === vnode2.key && vnode1.sel === vnode2.sel;
}
function createKeyToOldIdx(children: Array<VNode>, beginIdx: number, endIdx: number): any {
let i: number, map: any = {}, key: any;
for (i = beginIdx; i <= endIdx; ++i) {
key = children[i].key;
if (isDef(key)) map[key] = i;
}
return map;
}
const hooks = ['create', 'update', 'remove', 'destroy', 'pre', 'post'];
function init(modules: Array<Hooks>, domApi?: DOMAPI) {
let i: number, j: number, cbs: any = {};
let api: DOMAPI = domApi as DOMAPI;
if (isUndef(domApi)) api = htmlDomApi;
for (i = 0; i < hooks.length; ++i) {
cbs[hooks[i]] = [];
for (j = 0; j < modules.length; ++j) {
if ((modules[j] as any)[hooks[i]] !== undefined) cbs[hooks[i]].push((modules[j] as any)[hooks[i]]);
}
}
function emptyNodeAt(elm: Element) {
const id = elm.id ? '#' + elm.id : '';
const c = elm.className ? '.' + elm.className.split(' ').join('.') : '';
return vnode(api.tagName(elm).toLowerCase() + id + c, {}, [], undefined, elm);
}
function createRmCb(childElm: Element | Text, listeners: number) {
return function rmCb() {
if (--listeners === 0) {
const parent = api.parentNode(childElm);
api.removeChild(parent, childElm);
}
};
}
function createElm(vnode: VNode, insertedVnodeQueue: VNodeQueue): Element | Text {
let i: any, data = vnode.data;
if (isDef(data)) {
if (isDef(i = (data as VNodeData).hook) && isDef(i = i.init)) {
i(vnode);
data = vnode.data;
}
}
let elm: Element | Text, children = vnode.children, sel = vnode.sel;
if (isDef(sel)) {
// Parse selector
const hashIdx = (sel as string).indexOf('#');
const dotIdx = (sel as string).indexOf('.', hashIdx);
const hash = hashIdx > 0 ? hashIdx : (sel as string).length;
const dot = dotIdx > 0 ? dotIdx : (sel as string).length;
const tag = hashIdx !== -1 || dotIdx !== -1 ? (sel as string).slice(0, Math.min(hash, dot)) : sel;
elm = vnode.elm = isDef(data) && isDef(i = (data as VNodeData).ns) ? api.createElementNS(i, tag as string)
: api.createElement(tag);
if (hash < dot) elm.id = (sel as string).slice(hash + 1, dot);
if (dotIdx > 0) elm.className = (sel as string).slice(dot + 1).replace(/\./g, ' ');
if (is.array(children)) {
for (i = 0; i < children.length; ++i) {
api.appendChild(elm, createElm(children[i] as VNode, insertedVnodeQueue));
}
} else if (is.primitive(vnode.text)) {
api.appendChild(elm, api.createTextNode(vnode.text as string));
}
for (i = 0; i < cbs.create.length; ++i) cbs.create[i](emptyNode, vnode);
i = (vnode.data as VNodeData).hook; // Reuse variable
if (isDef(i)) {
if (i.create) i.create(emptyNode, vnode);
if (i.insert) insertedVnodeQueue.push(vnode);
}
} else {
elm = vnode.elm = api.createTextNode(vnode.text as string);
}
return vnode.elm;
}
function addVnodes(parentElm: Node,
before: Node | null,
vnodes: Array<VNode>,
startIdx: number,
endIdx: number,
insertedVnodeQueue: VNodeQueue) {
for (; startIdx <= endIdx; ++startIdx) {
api.insertBefore(parentElm, createElm(vnodes[startIdx], insertedVnodeQueue), before);
}
}
function invokeDestroyHook(vnode: VNode) {
let i: any, j: number, data = vnode.data;
if (isDef(data)) {
if (isDef(i = (data as VNodeData).hook) && isDef(i = i.destroy)) i(vnode);
for (i = 0; i < cbs.destroy.length; ++i) cbs.destroy[i](vnode);
if (isDef(i = vnode.children)) {
for (j = 0; j < (vnode.children as Array<VNode>).length; ++j) {
invokeDestroyHook((vnode.children as Array<VNode>)[j]);
}
}
}
}
function removeVnodes(parentElm: Element,
vnodes: Array<VNode>,
startIdx: number,
endIdx: number): void {
for (; startIdx <= endIdx; ++startIdx) {
let i: any, listeners: number, rm: () => void, ch = vnodes[startIdx];
if (isDef(ch)) {
if (isDef(ch.sel)) {
invokeDestroyHook(ch);
listeners = cbs.remove.length + 1;
rm = createRmCb(ch.elm as Element | Text, listeners);
for (i = 0; i < cbs.remove.length; ++i) cbs.remove[i](ch, rm);
if (isDef(i = ch.data) && isDef(i = i.hook) && isDef(i = i.remove)) {
i(ch, rm);
} else {
rm();
}
} else { // Text node
api.removeChild(parentElm, ch.elm as Element | Text);
}
}
}
}
function updateChildren(parentElm: Element,
oldCh: Array<VNode>,
newCh: Array<VNode>,
insertedVnodeQueue: VNodeQueue) {
let oldStartIdx = 0, newStartIdx = 0;
let oldEndIdx = oldCh.length - 1;
let oldStartVnode = oldCh[0];
let oldEndVnode = oldCh[oldEndIdx];
let newEndIdx = newCh.length - 1;
let newStartVnode = newCh[0];
let newEndVnode = newCh[newEndIdx];
let oldKeyToIdx: any, idxInOld: number, elmToMove: any, before: any;
while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
if (isUndef(oldStartVnode)) {
oldStartVnode = oldCh[++oldStartIdx]; // Vnode has been moved left
} else if (isUndef(oldEndVnode)) {
oldEndVnode = oldCh[--oldEndIdx];
} else if (sameVnode(oldStartVnode, newStartVnode)) {
patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue);
oldStartVnode = oldCh[++oldStartIdx];
newStartVnode = newCh[++newStartIdx];
} else if (sameVnode(oldEndVnode, newEndVnode)) {
patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue);
oldEndVnode = oldCh[--oldEndIdx];
newEndVnode = newCh[--newEndIdx];
} else if (sameVnode(oldStartVnode, newEndVnode)) { // Vnode moved right
patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue);
api.insertBefore(parentElm, oldStartVnode.elm as Element, api.nextSibling(oldEndVnode.elm as Element));
oldStartVnode = oldCh[++oldStartIdx];
newEndVnode = newCh[--newEndIdx];
} else if (sameVnode(oldEndVnode, newStartVnode)) { // Vnode moved left
patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue);
api.insertBefore(parentElm, oldEndVnode.elm as Element, oldStartVnode.elm as Element);
oldEndVnode = oldCh[--oldEndIdx];
newStartVnode = newCh[++newStartIdx];
} else {
if (isUndef(oldKeyToIdx)) oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx);
idxInOld = oldKeyToIdx[newStartVnode.key as string | number];
if (isUndef(idxInOld)) { // New element
api.insertBefore(parentElm, createElm(newStartVnode, insertedVnodeQueue), oldStartVnode.elm as Element);
newStartVnode = newCh[++newStartIdx];
} else {
elmToMove = oldCh[idxInOld];
patchVnode(elmToMove, newStartVnode, insertedVnodeQueue);
oldCh[idxInOld] = undefined as any;
api.insertBefore(parentElm, elmToMove.elm, oldStartVnode.elm as Element);
newStartVnode = newCh[++newStartIdx];
}
}
}
if (oldStartIdx > oldEndIdx) {
before = isUndef(newCh[newEndIdx+1]) ? null : newCh[newEndIdx+1].elm;
addVnodes(parentElm, before, newCh, newStartIdx, newEndIdx, insertedVnodeQueue);
} else if (newStartIdx > newEndIdx) {
removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx);
}
}
function patchVnode(oldVnode: VNode, vnode: VNode, insertedVnodeQueue: VNodeQueue) {
let i: any, hook: any;
if (isDef(i = vnode.data) && isDef(hook = i.hook) && isDef(i = hook.prepatch)) {
i(oldVnode, vnode);
}
let elm = vnode.elm = oldVnode.elm, oldCh = oldVnode.children, ch = vnode.children;
if (oldVnode === vnode) return;
if (!sameVnode(oldVnode, vnode)) {
const parentElm = api.parentNode(oldVnode.elm as Element);
elm = createElm(vnode, insertedVnodeQueue);
api.insertBefore(parentElm, elm, oldVnode.elm as Element);
removeVnodes(parentElm, [oldVnode], 0, 0);
return;
}
if (isDef(vnode.data)) {
for (i = 0; i < cbs.update.length; ++i) cbs.update[i](oldVnode, vnode);
i = (vnode.data as VNodeData).hook;
if (isDef(i) && isDef(i = i.update)) i(oldVnode, vnode);
}
if (isUndef(vnode.text)) {
if (isDef(oldCh) && isDef(ch)) {
if (oldCh !== ch) updateChildren(elm as Element, oldCh as Array<VNode>, ch as Array<VNode>, insertedVnodeQueue);
} else if (isDef(ch)) {
if (isDef(oldVnode.text)) api.setTextContent(elm as Element, '');
addVnodes(elm as Element, null, ch as Array<VNode>, 0, (ch as Array<VNode>).length - 1, insertedVnodeQueue);
} else if (isDef(oldCh)) {
removeVnodes(elm as Element, oldCh as Array<VNode>, 0, (oldCh as Array<VNode>).length - 1);
} else if (isDef(oldVnode.text)) {
api.setTextContent(elm as Element, '');
}
} else if (oldVnode.text !== vnode.text) {
api.setTextContent(elm as Element, vnode.text as string);
}
if (isDef(hook) && isDef(i = hook.postpatch)) {
i(oldVnode, vnode);
}
}
return function patch(oldVnode: VNode | Element, vnode: VNode): VNode {
let i: number, elm: Element, parent: Element;
const insertedVnodeQueue: VNodeQueue = [];
for (i = 0; i < cbs.pre.length; ++i) cbs.pre[i]();
if (isUndef((oldVnode as VNode).sel)) {
oldVnode = emptyNodeAt(oldVnode as Element);
}
if (sameVnode(oldVnode as VNode, vnode)) {
patchVnode(oldVnode as VNode, vnode, insertedVnodeQueue);
} else {
elm = (oldVnode as VNode).elm as Element;
parent = api.parentNode(elm);
createElm(vnode, insertedVnodeQueue);
if (parent !== null) {
api.insertBefore(parent, vnode.elm as Element, api.nextSibling(elm));
removeVnodes(parent, [oldVnode as VNode], 0, 0);
}
}
for (i = 0; i < insertedVnodeQueue.length; ++i) {
(((insertedVnodeQueue[i].data as VNodeData).hook as Hooks).insert as any)(insertedVnodeQueue[i]);
}
for (i = 0; i < cbs.post.length; ++i) cbs.post[i]();
return vnode;
};
}
export = {init: init};

@ -0,0 +1,49 @@
import {VNode, VNodeData, ThunkFn} from './interfaces';
import h = require('./h');
function copyToThunk(vnode: VNode, thunk: VNode): void {
thunk.elm = vnode.elm;
(vnode.data as VNodeData).fn = (thunk.data as VNodeData).fn;
(vnode.data as VNodeData).args = (thunk.data as VNodeData).args;
thunk.data = vnode.data;
thunk.children = vnode.children;
thunk.text = vnode.text;
thunk.elm = vnode.elm;
}
function init(thunk: VNode): void {
const cur = thunk.data as VNodeData;
const vnode = (cur.fn as any).apply(undefined, cur.args);
copyToThunk(vnode, thunk);
}
function prepatch(oldVnode: VNode, thunk: VNode): void {
let i: number, old = oldVnode.data as VNodeData, cur = thunk.data as VNodeData;
const oldArgs = old.args, args = cur.args;
if (old.fn !== cur.fn || (oldArgs as any).length !== (args as any).length) {
copyToThunk((cur.fn as any).apply(undefined, args), thunk);
}
for (i = 0; i < (args as any).length; ++i) {
if ((oldArgs as any)[i] !== (args as any)[i]) {
copyToThunk((cur.fn as any).apply(undefined, args), thunk);
return;
}
}
copyToThunk(oldVnode, thunk);
}
function thunk(sel: string, key?: any, fn?: any, args?: any): VNode {
if (args === undefined) {
args = fn;
fn = key;
key = undefined;
}
return h(sel, {
key: key,
hook: {init: init, prepatch: prepatch},
fn: fn,
args: args
});
};
export = thunk as ThunkFn;

@ -0,0 +1,13 @@
import {VNode} from './interfaces';
function vnode(sel: string,
data: any | undefined,
children: Array<VNode | string> | undefined,
text: string | undefined,
elm: Element | Text | undefined): VNode {
let key = data === undefined ? undefined : data.key;
return {sel: sel, data: data, children: children,
text: text, elm: elm, key: key};
}
export = vnode;

@ -1,46 +0,0 @@
var h = require('./h');
function copyToThunk(vnode, thunk) {
thunk.elm = vnode.elm;
vnode.data.fn = thunk.data.fn;
vnode.data.args = thunk.data.args;
thunk.data = vnode.data;
thunk.children = vnode.children;
thunk.text = vnode.text;
thunk.elm = vnode.elm;
}
function init(thunk) {
var cur = thunk.data;
var vnode = cur.fn.apply(undefined, cur.args);
copyToThunk(vnode, thunk);
}
function prepatch(oldVnode, thunk) {
var i, old = oldVnode.data, cur = thunk.data, vnode;
var oldArgs = old.args, args = cur.args;
if (old.fn !== cur.fn || oldArgs.length !== args.length) {
copyToThunk(cur.fn.apply(undefined, args), thunk);
}
for (i = 0; i < args.length; ++i) {
if (oldArgs[i] !== args[i]) {
copyToThunk(cur.fn.apply(undefined, args), thunk);
return;
}
}
copyToThunk(oldVnode, thunk);
}
module.exports = function(sel, key, fn, args) {
if (args === undefined) {
args = fn;
fn = key;
key = undefined;
}
return h(sel, {
key: key,
hook: {init: init, prepatch: prepatch},
fn: fn,
args: args
});
};

@ -0,0 +1,35 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "ES5",
"outDir": "./",
"noImplicitAny": true,
"sourceMap": true,
"strictNullChecks": true,
"declaration": true,
"removeComments": false,
"noUnusedLocals": true,
"lib": [
"dom",
"es5"
]
},
"files": [
"src/helpers/attachto.ts",
"src/modules/attributes.ts",
"src/modules/class.ts",
"src/modules/dataset.ts",
"src/modules/eventlisteners.ts",
"src/modules/hero.ts",
"src/modules/props.ts",
"src/modules/style.ts",
"src/h.ts",
"src/htmldomapi.ts",
"src/interfaces.d.ts",
"src/is.ts",
"src/snabbdom.bundle.ts",
"src/snabbdom.ts",
"src/thunk.ts",
"src/vnode.ts"
]
}

@ -1,148 +0,0 @@
export interface VNodeData {
// modules - use any because Object type is useless
props?: any;
attrs?: any;
class?: any;
style?: any;
dataset?: any;
on?: any;
hero?: any;
// end of modules
hook?: Hooks;
key?: string | number;
ns?: string; // for SVGs
fn?: () => VNode; // for thunks
args?: Array<any>; // for thunks
}
export interface VNode {
sel: string;
data?: VNodeData;
children?: Array<VNode | string>;
elm?: Element | Text;
text?: string;
key?: string | number;
}
export interface ThunkData extends VNodeData {
fn: () => VNode;
args: Array<any>;
}
export interface Thunk extends VNode {
data: ThunkData;
}
export type PreHook = () => any;
export type InitHook = (vNode: VNode) => any;
export type CreateHook = (emptyVNode: VNode, vNode: VNode) => any;
export type InsertHook = (vNode: VNode) => any;
export type PrePatchHook = (oldVNode: VNode, vNode: VNode) => any;
export type UpdateHook = (oldVNode: VNode, vNode: VNode) => any;
export type PostPatchHook = (oldVNode: VNode, vNode: VNode) => any;
export type DestroyHook = (vNode: VNode) => any;
export type RemoveHook = (vNode: VNode, removeCallback: () => void) => any;
export type PostHook = () => any;
export interface Hooks {
pre?: PreHook;
init?: InitHook;
create?: CreateHook;
insert?: InsertHook;
prepatch?: PrePatchHook;
update?: UpdateHook;
postpatch?: PostPatchHook;
destroy?: DestroyHook;
remove?: RemoveHook;
post?: PostHook;
}
export interface Module {
pre?: PreHook;
create?: CreateHook;
update?: UpdateHook;
destroy?: DestroyHook;
remove?: RemoveHook;
post?: PostHook;
}
export interface SnabbdomAPI<T> {
createElement(tagName: string): T;
createElementNS(namespaceURI: string, qualifiedName: string): T;
createTextNode(text: string): T;
insertBefore(parentNode: T, newNode: T, referenceNode: T): void;
removeChild(node: T, child: T): void;
appendChild(node: T, child: T): void;
parentNode(node: T): T;
nextSibling(node: T): T;
tagName(node: T): string;
setTextContent(node: T, text: string): void;
}
declare module "snabbdom" {
export interface PatchFunction {
(oldVNode: VNode, vnode: VNode): VNode;
}
export function init(modules: Object, api?: SnabbdomAPI<any>): PatchFunction;
}
declare module "snabbdom/vnode" {
export default function vnode(sel: string,
data: VNodeData,
children: Array<VNode | string>,
text: string,
elm: any): VNode;
}
declare module "snabbdom/is" {
export function array(x: any): boolean;
export function primitive(x: any): boolean;
}
declare module "snabbdom/thunk" {
export default function thunk(sel: string,
key: string,
render: (...state: Array<any>) => VNode,
...state: Array<any>): Thunk;
}
declare module "snabbdom/htmldomapi" {
let api: SnabbdomAPI<Element>;
export = api;
}
declare module "snabbdom/modules/class" {
let ClassModule: Module;
export = ClassModule;
}
declare module "snabbdom/modules/props" {
let PropsModule: Module;
export = PropsModule;
}
declare module "snabbdom/modules/attributes" {
let AttrsModule: Module;
export = AttrsModule;
}
declare module "snabbdom/modules/eventlisteners" {
let EventsModule: Module;
export = EventsModule;
}
declare module "snabbdom/modules/hero" {
let HeroModule: Module;
export = HeroModule;
}
declare module "snabbdom/modules/style" {
let StyleModule: Module;
export = StyleModule;
}
declare module "snabbdom/modules/dataset" {
let DatasetModule: Module;
export = DatasetModule;
}

@ -1,5 +0,0 @@
module.exports = function(sel, data, children, text, elm) {
var key = data === undefined ? undefined : data.key;
return {sel: sel, data: data, children: children,
text: text, elm: elm, key: key};
};
Loading…
Cancel
Save