Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(29)

Side by Side Diff: sky/examples/fn/lib/node.dart

Issue 987463002: Move fn.dart into /sky/framework (Closed) Base URL: git@github.com:domokit/mojo.git@master
Patch Set: Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « sky/examples/fn/lib/fn.dart ('k') | sky/examples/fn/lib/reflect.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 part of fn;
6
7 void _parentInsertBefore(sky.ParentNode parent,
8 sky.Node node,
9 sky.Node ref) {
10 if (ref != null) {
11 ref.insertBefore([node]);
12 } else {
13 parent.appendChild(node);
14 }
15 }
16
17 abstract class Node {
18 String _key = null;
19 sky.Node _root = null;
20
21 // TODO(abarth): Both Elements and Components have |events| but |Text|
22 // doesn't. Should we add a common base class to contain |events|?
23 final EventMap events = new EventMap();
24
25 Node({ Object key }) {
26 _key = key == null ? "$runtimeType" : "$runtimeType-$key";
27 }
28
29 // Return true IFF the old node has *become* the new node (should be
30 // retained because it is stateful)
31 bool _sync(Node old, sky.ParentNode host, sky.Node insertBefore);
32
33 void _remove() {
34 assert(_root != null);
35 _root.remove();
36 _root = null;
37 }
38 }
39
40 class Text extends Node {
41 String data;
42
43 // Text nodes are special cases of having non-unique keys (which don't need
44 // to be assigned as part of the API). Since they are unique in not having
45 // children, there's little point to reordering, so we always just re-assign
46 // the data.
47 Text(this.data) : super(key:'*text*');
48
49 bool _sync(Node old, sky.ParentNode host, sky.Node insertBefore) {
50 if (old == null) {
51 _root = new sky.Text(data);
52 _parentInsertBefore(host, _root, insertBefore);
53 return false;
54 }
55
56 _root = old._root;
57 (_root as sky.Text).data = data;
58 return false;
59 }
60 }
61
62 final List<Node> _emptyList = new List<Node>();
63
64 abstract class Element extends Node {
65
66 String get _tagName;
67
68 Element get _emptyElement;
69
70 String inlineStyle;
71
72 List<Node> _children = null;
73 String _className = '';
74
75 Element({
76 Object key,
77 List<Node> children,
78 Style style,
79
80 this.inlineStyle
81 }) : super(key:key) {
82
83 _className = style == null ? '': style._className;
84 _children = children == null ? _emptyList : children;
85
86 if (_debugWarnings()) {
87 _debugReportDuplicateIds();
88 }
89 }
90
91 void _remove() {
92 super._remove();
93 if (_children != null) {
94 for (var child in _children) {
95 child._remove();
96 }
97 }
98 _children = null;
99 }
100
101 void _debugReportDuplicateIds() {
102 var idSet = new HashSet<String>();
103 for (var child in _children) {
104 if (child is Text) {
105 continue; // Text nodes all have the same key and are never reordered.
106 }
107
108 if (!idSet.add(child._key)) {
109 throw '''If multiple (non-Text) nodes of the same type exist as children
110 of another node, they must have unique keys.''';
111 }
112 }
113 }
114
115 void _syncEvents([Element old]) {
116 List<EventHandler> newHandlers = events._handlers;
117 int newStartIndex = 0;
118 int newEndIndex = newHandlers.length;
119
120 List<EventHandler> oldHandlers = old.events._handlers;
121 int oldStartIndex = 0;
122 int oldEndIndex = oldHandlers.length;
123
124 // Skip over leading handlers that match.
125 while (newStartIndex < newEndIndex && oldStartIndex < oldEndIndex) {
126 EventHandler newHander = newHandlers[newStartIndex];
127 EventHandler oldHandler = oldHandlers[oldStartIndex];
128 if (newHander.type != oldHandler.type
129 || newHander.listener != oldHandler.listener)
130 break;
131 ++newStartIndex;
132 ++oldStartIndex;
133 }
134
135 // Skip over trailing handlers that match.
136 while (newStartIndex < newEndIndex && oldStartIndex < oldEndIndex) {
137 EventHandler newHander = newHandlers[newEndIndex - 1];
138 EventHandler oldHandler = oldHandlers[oldEndIndex - 1];
139 if (newHander.type != oldHandler.type
140 || newHander.listener != oldHandler.listener)
141 break;
142 --newEndIndex;
143 --oldEndIndex;
144 }
145
146 sky.Element root = _root as sky.Element;
147
148 for (int i = oldStartIndex; i < oldEndIndex; ++i) {
149 EventHandler oldHandler = oldHandlers[i];
150 root.removeEventListener(oldHandler.type, oldHandler.listener);
151 }
152
153 for (int i = newStartIndex; i < newEndIndex; ++i) {
154 EventHandler newHander = newHandlers[i];
155 root.addEventListener(newHander.type, newHander.listener);
156 }
157 }
158
159 void _syncNode([Element old]) {
160 if (old == null) {
161 old = _emptyElement;
162 }
163
164 _syncEvents(old);
165
166 sky.Element root = _root as sky.Element;
167 if (_className != old._className) {
168 root.setAttribute('class', _className);
169 }
170
171 if (inlineStyle != old.inlineStyle) {
172 root.setAttribute('style', inlineStyle);
173 }
174 }
175
176 bool _sync(Node old, sky.ParentNode host, sky.Node insertBefore) {
177 // print("---Syncing children of $_key");
178
179 Element oldElement = old as Element;
180
181 if (oldElement == null) {
182 // print("...no oldElement, initial render");
183
184 _root = sky.document.createElement(_tagName);
185 _syncNode();
186
187 for (var child in _children) {
188 child._sync(null, _root, null);
189 assert(child._root is sky.Node);
190 }
191
192 _parentInsertBefore(host, _root, insertBefore);
193 return false;
194 }
195
196 _root = oldElement._root;
197 oldElement._root = null;
198 sky.Element root = (_root as sky.Element);
199
200 _syncNode(oldElement);
201
202 var startIndex = 0;
203 var endIndex = _children.length;
204
205 var oldChildren = oldElement._children;
206 var oldStartIndex = 0;
207 var oldEndIndex = oldChildren.length;
208
209 sky.Node nextSibling = null;
210 Node currentNode = null;
211 Node oldNode = null;
212
213 void sync(int atIndex) {
214 if (currentNode._sync(oldNode, root, nextSibling)) {
215 // oldNode was stateful and must be retained.
216 assert(oldNode != null);
217 currentNode = oldNode;
218 _children[atIndex] = currentNode;
219 }
220 assert(currentNode._root is sky.Node);
221 }
222
223 // Scan backwards from end of list while nodes can be directly synced
224 // without reordering.
225 // print("...scanning backwards");
226 while (endIndex > startIndex && oldEndIndex > oldStartIndex) {
227 currentNode = _children[endIndex - 1];
228 oldNode = oldChildren[oldEndIndex - 1];
229
230 if (currentNode._key != oldNode._key) {
231 break;
232 }
233
234 // print('> syncing matched at: $endIndex : $oldEndIndex');
235 endIndex--;
236 oldEndIndex--;
237 sync(endIndex);
238 nextSibling = currentNode._root;
239 }
240
241 HashMap<String, Node> oldNodeIdMap = null;
242
243 bool oldNodeReordered(String key) {
244 return oldNodeIdMap != null &&
245 oldNodeIdMap.containsKey(key) &&
246 oldNodeIdMap[key] == null;
247 }
248
249 void advanceOldStartIndex() {
250 oldStartIndex++;
251 while (oldStartIndex < oldEndIndex &&
252 oldNodeReordered(oldChildren[oldStartIndex]._key)) {
253 oldStartIndex++;
254 }
255 }
256
257 void ensureOldIdMap() {
258 if (oldNodeIdMap != null)
259 return;
260
261 oldNodeIdMap = new HashMap<String, Node>();
262 for (int i = oldStartIndex; i < oldEndIndex; i++) {
263 var node = oldChildren[i];
264 if (node is! Text) {
265 oldNodeIdMap.putIfAbsent(node._key, () => node);
266 }
267 }
268 }
269
270 bool searchForOldNode() {
271 if (currentNode is Text)
272 return false; // Never re-order Text nodes.
273
274 ensureOldIdMap();
275 oldNode = oldNodeIdMap[currentNode._key];
276 if (oldNode == null)
277 return false;
278
279 oldNodeIdMap[currentNode._key] = null; // mark it reordered.
280 // print("Reparenting ${currentNode._key}");
281 _parentInsertBefore(root, oldNode._root, nextSibling);
282 return true;
283 }
284
285 // Scan forwards, this time we may re-order;
286 // print("...scanning forward");
287 nextSibling = root.firstChild;
288 while (startIndex < endIndex && oldStartIndex < oldEndIndex) {
289 currentNode = _children[startIndex];
290 oldNode = oldChildren[oldStartIndex];
291
292 if (currentNode._key == oldNode._key) {
293 // print('> syncing matched at: $startIndex : $oldStartIndex');
294 assert(currentNode.runtimeType == oldNode.runtimeType);
295 nextSibling = nextSibling.nextSibling;
296 sync(startIndex);
297 startIndex++;
298 advanceOldStartIndex();
299 continue;
300 }
301
302 oldNode = null;
303 if (searchForOldNode()) {
304 // print('> reordered to $startIndex');
305 } else {
306 // print('> inserting at $startIndex');
307 }
308
309 sync(startIndex);
310 startIndex++;
311 }
312
313 // New insertions
314 oldNode = null;
315 // print('...processing remaining insertions');
316 while (startIndex < endIndex) {
317 // print('> inserting at $startIndex');
318 currentNode = _children[startIndex];
319 sync(startIndex);
320 startIndex++;
321 }
322
323 // Removals
324 // print('...processing remaining removals');
325 currentNode = null;
326 while (oldStartIndex < oldEndIndex) {
327 oldNode = oldChildren[oldStartIndex];
328 // print('> ${oldNode._key} removing from $oldEndIndex');
329 oldNode._remove();
330 advanceOldStartIndex();
331 }
332
333 oldElement._children = null;
334 return false;
335 }
336 }
337
338 class Container extends Element {
339
340 String get _tagName => 'div';
341
342 static final Container _emptyContainer = new Container();
343
344 Element get _emptyElement => _emptyContainer;
345
346 Container({
347 Object key,
348 List<Node> children,
349 Style style,
350 String inlineStyle
351 }) : super(
352 key: key,
353 children: children,
354 style: style,
355 inlineStyle: inlineStyle
356 );
357 }
358
359 class Image extends Element {
360
361 String get _tagName => 'img';
362
363 static final Image _emptyImage = new Image();
364 Element get _emptyElement => _emptyImage;
365
366 String src;
367 int width;
368 int height;
369
370 Image({
371 Object key,
372 List<Node> children,
373 Style style,
374 String inlineStyle,
375 this.width,
376 this.height,
377 this.src
378 }) : super(
379 key: key,
380 children: children,
381 style: style,
382 inlineStyle: inlineStyle
383 );
384
385 void _syncNode([Element old]) {
386 super._syncNode(old);
387
388 Image oldImage = old != null ? old : _emptyImage;
389 sky.HTMLImageElement skyImage = _root as sky.HTMLImageElement;
390 if (src != oldImage.src) {
391 skyImage.src = src;
392 }
393
394 if (width != oldImage.width) {
395 skyImage.style['width'] = '${width}px';
396 }
397 if (height != oldImage.height) {
398 skyImage.style['height'] = '${height}px';
399 }
400 }
401 }
402
403 class Anchor extends Element {
404
405 String get _tagName => 'a';
406
407 static final Anchor _emptyAnchor = new Anchor();
408
409 String href;
410
411 Anchor({
412 Object key,
413 List<Node> children,
414 Style style,
415 String inlineStyle,
416 this.width,
417 this.height,
418 this.href
419 }) : super(
420 key: key,
421 children: children,
422 style: style,
423 inlineStyle: inlineStyle
424 );
425
426 void _syncNode([Element old]) {
427 Anchor oldAnchor = old != null ? old as Anchor : _emptyAnchor;
428 super._syncNode(oldAnchor);
429
430 sky.HTMLAnchorElement skyAnchor = _root as sky.HTMLAnchorElement;
431 if (href != oldAnchor.href) {
432 skyAnchor.href = href;
433 }
434 }
435 }
OLDNEW
« no previous file with comments | « sky/examples/fn/lib/fn.dart ('k') | sky/examples/fn/lib/reflect.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698