UNPKG

12 kBJavaScriptView Raw
1"use strict";
2var vnode_1 = require("./vnode");
3var is = require("./is");
4var htmldomapi_1 = require("./htmldomapi");
5function isUndef(s) { return s === undefined; }
6function isDef(s) { return s !== undefined; }
7var emptyNode = vnode_1.default('', {}, [], undefined, undefined);
8function sameVnode(vnode1, vnode2) {
9 return vnode1.key === vnode2.key && vnode1.sel === vnode2.sel;
10}
11function isVnode(vnode) {
12 return vnode.sel !== undefined;
13}
14function createKeyToOldIdx(children, beginIdx, endIdx) {
15 var i, map = {}, key, ch;
16 for (i = beginIdx; i <= endIdx; ++i) {
17 ch = children[i];
18 if (ch != null) {
19 key = ch.key;
20 if (key !== undefined)
21 map[key] = i;
22 }
23 }
24 return map;
25}
26var hooks = ['create', 'update', 'remove', 'destroy', 'pre', 'post'];
27var h_1 = require("./h");
28exports.h = h_1.h;
29var thunk_1 = require("./thunk");
30exports.thunk = thunk_1.thunk;
31function init(modules, domApi) {
32 var i, j, cbs = {};
33 var api = domApi !== undefined ? domApi : htmldomapi_1.default;
34 for (i = 0; i < hooks.length; ++i) {
35 cbs[hooks[i]] = [];
36 for (j = 0; j < modules.length; ++j) {
37 var hook = modules[j][hooks[i]];
38 if (hook !== undefined) {
39 cbs[hooks[i]].push(hook);
40 }
41 }
42 }
43 function emptyNodeAt(elm) {
44 var id = elm.id ? '#' + elm.id : '';
45 var c = elm.className ? '.' + elm.className.split(' ').join('.') : '';
46 return vnode_1.default(api.tagName(elm).toLowerCase() + id + c, {}, [], undefined, elm);
47 }
48 function createRmCb(childElm, listeners) {
49 return function rmCb() {
50 if (--listeners === 0) {
51 var parent_1 = api.parentNode(childElm);
52 api.removeChild(parent_1, childElm);
53 }
54 };
55 }
56 function createElm(vnode, insertedVnodeQueue) {
57 var i, data = vnode.data;
58 if (data !== undefined) {
59 if (isDef(i = data.hook) && isDef(i = i.init)) {
60 i(vnode);
61 data = vnode.data;
62 }
63 }
64 var children = vnode.children, sel = vnode.sel;
65 if (sel === '!') {
66 if (isUndef(vnode.text)) {
67 vnode.text = '';
68 }
69 vnode.elm = api.createComment(vnode.text);
70 }
71 else if (sel !== undefined) {
72 // Parse selector
73 var hashIdx = sel.indexOf('#');
74 var dotIdx = sel.indexOf('.', hashIdx);
75 var hash = hashIdx > 0 ? hashIdx : sel.length;
76 var dot = dotIdx > 0 ? dotIdx : sel.length;
77 var tag = hashIdx !== -1 || dotIdx !== -1 ? sel.slice(0, Math.min(hash, dot)) : sel;
78 var elm = vnode.elm = isDef(data) && isDef(i = data.ns) ? api.createElementNS(i, tag)
79 : api.createElement(tag);
80 if (hash < dot)
81 elm.id = sel.slice(hash + 1, dot);
82 if (dotIdx > 0)
83 elm.className = sel.slice(dot + 1).replace(/\./g, ' ');
84 for (i = 0; i < cbs.create.length; ++i)
85 cbs.create[i](emptyNode, vnode);
86 if (is.array(children)) {
87 for (i = 0; i < children.length; ++i) {
88 var ch = children[i];
89 if (ch != null) {
90 api.appendChild(elm, createElm(ch, insertedVnodeQueue));
91 }
92 }
93 }
94 else if (is.primitive(vnode.text)) {
95 api.appendChild(elm, api.createTextNode(vnode.text));
96 }
97 i = vnode.data.hook; // Reuse variable
98 if (isDef(i)) {
99 if (i.create)
100 i.create(emptyNode, vnode);
101 if (i.insert)
102 insertedVnodeQueue.push(vnode);
103 }
104 }
105 else {
106 vnode.elm = api.createTextNode(vnode.text);
107 }
108 return vnode.elm;
109 }
110 function addVnodes(parentElm, before, vnodes, startIdx, endIdx, insertedVnodeQueue) {
111 for (; startIdx <= endIdx; ++startIdx) {
112 var ch = vnodes[startIdx];
113 if (ch != null) {
114 api.insertBefore(parentElm, createElm(ch, insertedVnodeQueue), before);
115 }
116 }
117 }
118 function invokeDestroyHook(vnode) {
119 var i, j, data = vnode.data;
120 if (data !== undefined) {
121 if (isDef(i = data.hook) && isDef(i = i.destroy))
122 i(vnode);
123 for (i = 0; i < cbs.destroy.length; ++i)
124 cbs.destroy[i](vnode);
125 if (vnode.children !== undefined) {
126 for (j = 0; j < vnode.children.length; ++j) {
127 i = vnode.children[j];
128 if (i != null && typeof i !== "string") {
129 invokeDestroyHook(i);
130 }
131 }
132 }
133 }
134 }
135 function removeVnodes(parentElm, vnodes, startIdx, endIdx) {
136 for (; startIdx <= endIdx; ++startIdx) {
137 var i_1 = void 0, listeners = void 0, rm = void 0, ch = vnodes[startIdx];
138 if (ch != null) {
139 if (isDef(ch.sel)) {
140 invokeDestroyHook(ch);
141 listeners = cbs.remove.length + 1;
142 rm = createRmCb(ch.elm, listeners);
143 for (i_1 = 0; i_1 < cbs.remove.length; ++i_1)
144 cbs.remove[i_1](ch, rm);
145 if (isDef(i_1 = ch.data) && isDef(i_1 = i_1.hook) && isDef(i_1 = i_1.remove)) {
146 i_1(ch, rm);
147 }
148 else {
149 rm();
150 }
151 }
152 else {
153 api.removeChild(parentElm, ch.elm);
154 }
155 }
156 }
157 }
158 function updateChildren(parentElm, oldCh, newCh, insertedVnodeQueue) {
159 var oldStartIdx = 0, newStartIdx = 0;
160 var oldEndIdx = oldCh.length - 1;
161 var oldStartVnode = oldCh[0];
162 var oldEndVnode = oldCh[oldEndIdx];
163 var newEndIdx = newCh.length - 1;
164 var newStartVnode = newCh[0];
165 var newEndVnode = newCh[newEndIdx];
166 var oldKeyToIdx;
167 var idxInOld;
168 var elmToMove;
169 var before;
170 while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
171 if (oldStartVnode == null) {
172 oldStartVnode = oldCh[++oldStartIdx]; // Vnode might have been moved left
173 }
174 else if (oldEndVnode == null) {
175 oldEndVnode = oldCh[--oldEndIdx];
176 }
177 else if (newStartVnode == null) {
178 newStartVnode = newCh[++newStartIdx];
179 }
180 else if (newEndVnode == null) {
181 newEndVnode = newCh[--newEndIdx];
182 }
183 else if (sameVnode(oldStartVnode, newStartVnode)) {
184 patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue);
185 oldStartVnode = oldCh[++oldStartIdx];
186 newStartVnode = newCh[++newStartIdx];
187 }
188 else if (sameVnode(oldEndVnode, newEndVnode)) {
189 patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue);
190 oldEndVnode = oldCh[--oldEndIdx];
191 newEndVnode = newCh[--newEndIdx];
192 }
193 else if (sameVnode(oldStartVnode, newEndVnode)) {
194 patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue);
195 api.insertBefore(parentElm, oldStartVnode.elm, api.nextSibling(oldEndVnode.elm));
196 oldStartVnode = oldCh[++oldStartIdx];
197 newEndVnode = newCh[--newEndIdx];
198 }
199 else if (sameVnode(oldEndVnode, newStartVnode)) {
200 patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue);
201 api.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm);
202 oldEndVnode = oldCh[--oldEndIdx];
203 newStartVnode = newCh[++newStartIdx];
204 }
205 else {
206 if (oldKeyToIdx === undefined) {
207 oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx);
208 }
209 idxInOld = oldKeyToIdx[newStartVnode.key];
210 if (isUndef(idxInOld)) {
211 api.insertBefore(parentElm, createElm(newStartVnode, insertedVnodeQueue), oldStartVnode.elm);
212 newStartVnode = newCh[++newStartIdx];
213 }
214 else {
215 elmToMove = oldCh[idxInOld];
216 if (elmToMove.sel !== newStartVnode.sel) {
217 api.insertBefore(parentElm, createElm(newStartVnode, insertedVnodeQueue), oldStartVnode.elm);
218 }
219 else {
220 patchVnode(elmToMove, newStartVnode, insertedVnodeQueue);
221 oldCh[idxInOld] = undefined;
222 api.insertBefore(parentElm, elmToMove.elm, oldStartVnode.elm);
223 }
224 newStartVnode = newCh[++newStartIdx];
225 }
226 }
227 }
228 if (oldStartIdx > oldEndIdx) {
229 before = newCh[newEndIdx + 1] == null ? null : newCh[newEndIdx + 1].elm;
230 addVnodes(parentElm, before, newCh, newStartIdx, newEndIdx, insertedVnodeQueue);
231 }
232 else if (newStartIdx > newEndIdx) {
233 removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx);
234 }
235 }
236 function patchVnode(oldVnode, vnode, insertedVnodeQueue) {
237 var i, hook;
238 if (isDef(i = vnode.data) && isDef(hook = i.hook) && isDef(i = hook.prepatch)) {
239 i(oldVnode, vnode);
240 }
241 var elm = vnode.elm = oldVnode.elm;
242 var oldCh = oldVnode.children;
243 var ch = vnode.children;
244 if (oldVnode === vnode)
245 return;
246 if (vnode.data !== undefined) {
247 for (i = 0; i < cbs.update.length; ++i)
248 cbs.update[i](oldVnode, vnode);
249 i = vnode.data.hook;
250 if (isDef(i) && isDef(i = i.update))
251 i(oldVnode, vnode);
252 }
253 if (isUndef(vnode.text)) {
254 if (isDef(oldCh) && isDef(ch)) {
255 if (oldCh !== ch)
256 updateChildren(elm, oldCh, ch, insertedVnodeQueue);
257 }
258 else if (isDef(ch)) {
259 if (isDef(oldVnode.text))
260 api.setTextContent(elm, '');
261 addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue);
262 }
263 else if (isDef(oldCh)) {
264 removeVnodes(elm, oldCh, 0, oldCh.length - 1);
265 }
266 else if (isDef(oldVnode.text)) {
267 api.setTextContent(elm, '');
268 }
269 }
270 else if (oldVnode.text !== vnode.text) {
271 api.setTextContent(elm, vnode.text);
272 }
273 if (isDef(hook) && isDef(i = hook.postpatch)) {
274 i(oldVnode, vnode);
275 }
276 }
277 return function patch(oldVnode, vnode) {
278 var i, elm, parent;
279 var insertedVnodeQueue = [];
280 for (i = 0; i < cbs.pre.length; ++i)
281 cbs.pre[i]();
282 if (!isVnode(oldVnode)) {
283 oldVnode = emptyNodeAt(oldVnode);
284 }
285 if (sameVnode(oldVnode, vnode)) {
286 patchVnode(oldVnode, vnode, insertedVnodeQueue);
287 }
288 else {
289 elm = oldVnode.elm;
290 parent = api.parentNode(elm);
291 createElm(vnode, insertedVnodeQueue);
292 if (parent !== null) {
293 api.insertBefore(parent, vnode.elm, api.nextSibling(elm));
294 removeVnodes(parent, [oldVnode], 0, 0);
295 }
296 }
297 for (i = 0; i < insertedVnodeQueue.length; ++i) {
298 insertedVnodeQueue[i].data.hook.insert(insertedVnodeQueue[i]);
299 }
300 for (i = 0; i < cbs.post.length; ++i)
301 cbs.post[i]();
302 return vnode;
303 };
304}
305exports.init = init;
306//# sourceMappingURL=snabbdom.js.map
\No newline at end of file