Migrate source code to TypeScript v2.0
parent
8079ba7868
commit
499cd37b06
@ -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};
|
@ -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;
|
@ -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';
|
||||
},
|
||||
};
|
@ -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;
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue