Index: sky/examples/fn/lib/node.dart |
diff --git a/sky/examples/fn/lib/node.dart b/sky/examples/fn/lib/node.dart |
deleted file mode 100644 |
index 54853b3e83278efc6a1a4aac8e60968ba2ce74e9..0000000000000000000000000000000000000000 |
--- a/sky/examples/fn/lib/node.dart |
+++ /dev/null |
@@ -1,435 +0,0 @@ |
-// Copyright 2015 The Chromium Authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
- |
-part of fn; |
- |
-void _parentInsertBefore(sky.ParentNode parent, |
- sky.Node node, |
- sky.Node ref) { |
- if (ref != null) { |
- ref.insertBefore([node]); |
- } else { |
- parent.appendChild(node); |
- } |
-} |
- |
-abstract class Node { |
- String _key = null; |
- sky.Node _root = null; |
- |
- // TODO(abarth): Both Elements and Components have |events| but |Text| |
- // doesn't. Should we add a common base class to contain |events|? |
- final EventMap events = new EventMap(); |
- |
- Node({ Object key }) { |
- _key = key == null ? "$runtimeType" : "$runtimeType-$key"; |
- } |
- |
- // Return true IFF the old node has *become* the new node (should be |
- // retained because it is stateful) |
- bool _sync(Node old, sky.ParentNode host, sky.Node insertBefore); |
- |
- void _remove() { |
- assert(_root != null); |
- _root.remove(); |
- _root = null; |
- } |
-} |
- |
-class Text extends Node { |
- String data; |
- |
- // Text nodes are special cases of having non-unique keys (which don't need |
- // to be assigned as part of the API). Since they are unique in not having |
- // children, there's little point to reordering, so we always just re-assign |
- // the data. |
- Text(this.data) : super(key:'*text*'); |
- |
- bool _sync(Node old, sky.ParentNode host, sky.Node insertBefore) { |
- if (old == null) { |
- _root = new sky.Text(data); |
- _parentInsertBefore(host, _root, insertBefore); |
- return false; |
- } |
- |
- _root = old._root; |
- (_root as sky.Text).data = data; |
- return false; |
- } |
-} |
- |
-final List<Node> _emptyList = new List<Node>(); |
- |
-abstract class Element extends Node { |
- |
- String get _tagName; |
- |
- Element get _emptyElement; |
- |
- String inlineStyle; |
- |
- List<Node> _children = null; |
- String _className = ''; |
- |
- Element({ |
- Object key, |
- List<Node> children, |
- Style style, |
- |
- this.inlineStyle |
- }) : super(key:key) { |
- |
- _className = style == null ? '': style._className; |
- _children = children == null ? _emptyList : children; |
- |
- if (_debugWarnings()) { |
- _debugReportDuplicateIds(); |
- } |
- } |
- |
- void _remove() { |
- super._remove(); |
- if (_children != null) { |
- for (var child in _children) { |
- child._remove(); |
- } |
- } |
- _children = null; |
- } |
- |
- void _debugReportDuplicateIds() { |
- var idSet = new HashSet<String>(); |
- for (var child in _children) { |
- if (child is Text) { |
- continue; // Text nodes all have the same key and are never reordered. |
- } |
- |
- if (!idSet.add(child._key)) { |
- throw '''If multiple (non-Text) nodes of the same type exist as children |
- of another node, they must have unique keys.'''; |
- } |
- } |
- } |
- |
- void _syncEvents([Element old]) { |
- List<EventHandler> newHandlers = events._handlers; |
- int newStartIndex = 0; |
- int newEndIndex = newHandlers.length; |
- |
- List<EventHandler> oldHandlers = old.events._handlers; |
- int oldStartIndex = 0; |
- int oldEndIndex = oldHandlers.length; |
- |
- // Skip over leading handlers that match. |
- while (newStartIndex < newEndIndex && oldStartIndex < oldEndIndex) { |
- EventHandler newHander = newHandlers[newStartIndex]; |
- EventHandler oldHandler = oldHandlers[oldStartIndex]; |
- if (newHander.type != oldHandler.type |
- || newHander.listener != oldHandler.listener) |
- break; |
- ++newStartIndex; |
- ++oldStartIndex; |
- } |
- |
- // Skip over trailing handlers that match. |
- while (newStartIndex < newEndIndex && oldStartIndex < oldEndIndex) { |
- EventHandler newHander = newHandlers[newEndIndex - 1]; |
- EventHandler oldHandler = oldHandlers[oldEndIndex - 1]; |
- if (newHander.type != oldHandler.type |
- || newHander.listener != oldHandler.listener) |
- break; |
- --newEndIndex; |
- --oldEndIndex; |
- } |
- |
- sky.Element root = _root as sky.Element; |
- |
- for (int i = oldStartIndex; i < oldEndIndex; ++i) { |
- EventHandler oldHandler = oldHandlers[i]; |
- root.removeEventListener(oldHandler.type, oldHandler.listener); |
- } |
- |
- for (int i = newStartIndex; i < newEndIndex; ++i) { |
- EventHandler newHander = newHandlers[i]; |
- root.addEventListener(newHander.type, newHander.listener); |
- } |
- } |
- |
- void _syncNode([Element old]) { |
- if (old == null) { |
- old = _emptyElement; |
- } |
- |
- _syncEvents(old); |
- |
- sky.Element root = _root as sky.Element; |
- if (_className != old._className) { |
- root.setAttribute('class', _className); |
- } |
- |
- if (inlineStyle != old.inlineStyle) { |
- root.setAttribute('style', inlineStyle); |
- } |
- } |
- |
- bool _sync(Node old, sky.ParentNode host, sky.Node insertBefore) { |
- // print("---Syncing children of $_key"); |
- |
- Element oldElement = old as Element; |
- |
- if (oldElement == null) { |
- // print("...no oldElement, initial render"); |
- |
- _root = sky.document.createElement(_tagName); |
- _syncNode(); |
- |
- for (var child in _children) { |
- child._sync(null, _root, null); |
- assert(child._root is sky.Node); |
- } |
- |
- _parentInsertBefore(host, _root, insertBefore); |
- return false; |
- } |
- |
- _root = oldElement._root; |
- oldElement._root = null; |
- sky.Element root = (_root as sky.Element); |
- |
- _syncNode(oldElement); |
- |
- var startIndex = 0; |
- var endIndex = _children.length; |
- |
- var oldChildren = oldElement._children; |
- var oldStartIndex = 0; |
- var oldEndIndex = oldChildren.length; |
- |
- sky.Node nextSibling = null; |
- Node currentNode = null; |
- Node oldNode = null; |
- |
- void sync(int atIndex) { |
- if (currentNode._sync(oldNode, root, nextSibling)) { |
- // oldNode was stateful and must be retained. |
- assert(oldNode != null); |
- currentNode = oldNode; |
- _children[atIndex] = currentNode; |
- } |
- assert(currentNode._root is sky.Node); |
- } |
- |
- // Scan backwards from end of list while nodes can be directly synced |
- // without reordering. |
- // print("...scanning backwards"); |
- while (endIndex > startIndex && oldEndIndex > oldStartIndex) { |
- currentNode = _children[endIndex - 1]; |
- oldNode = oldChildren[oldEndIndex - 1]; |
- |
- if (currentNode._key != oldNode._key) { |
- break; |
- } |
- |
- // print('> syncing matched at: $endIndex : $oldEndIndex'); |
- endIndex--; |
- oldEndIndex--; |
- sync(endIndex); |
- nextSibling = currentNode._root; |
- } |
- |
- HashMap<String, Node> oldNodeIdMap = null; |
- |
- bool oldNodeReordered(String key) { |
- return oldNodeIdMap != null && |
- oldNodeIdMap.containsKey(key) && |
- oldNodeIdMap[key] == null; |
- } |
- |
- void advanceOldStartIndex() { |
- oldStartIndex++; |
- while (oldStartIndex < oldEndIndex && |
- oldNodeReordered(oldChildren[oldStartIndex]._key)) { |
- oldStartIndex++; |
- } |
- } |
- |
- void ensureOldIdMap() { |
- if (oldNodeIdMap != null) |
- return; |
- |
- oldNodeIdMap = new HashMap<String, Node>(); |
- for (int i = oldStartIndex; i < oldEndIndex; i++) { |
- var node = oldChildren[i]; |
- if (node is! Text) { |
- oldNodeIdMap.putIfAbsent(node._key, () => node); |
- } |
- } |
- } |
- |
- bool searchForOldNode() { |
- if (currentNode is Text) |
- return false; // Never re-order Text nodes. |
- |
- ensureOldIdMap(); |
- oldNode = oldNodeIdMap[currentNode._key]; |
- if (oldNode == null) |
- return false; |
- |
- oldNodeIdMap[currentNode._key] = null; // mark it reordered. |
- // print("Reparenting ${currentNode._key}"); |
- _parentInsertBefore(root, oldNode._root, nextSibling); |
- return true; |
- } |
- |
- // Scan forwards, this time we may re-order; |
- // print("...scanning forward"); |
- nextSibling = root.firstChild; |
- while (startIndex < endIndex && oldStartIndex < oldEndIndex) { |
- currentNode = _children[startIndex]; |
- oldNode = oldChildren[oldStartIndex]; |
- |
- if (currentNode._key == oldNode._key) { |
- // print('> syncing matched at: $startIndex : $oldStartIndex'); |
- assert(currentNode.runtimeType == oldNode.runtimeType); |
- nextSibling = nextSibling.nextSibling; |
- sync(startIndex); |
- startIndex++; |
- advanceOldStartIndex(); |
- continue; |
- } |
- |
- oldNode = null; |
- if (searchForOldNode()) { |
- // print('> reordered to $startIndex'); |
- } else { |
- // print('> inserting at $startIndex'); |
- } |
- |
- sync(startIndex); |
- startIndex++; |
- } |
- |
- // New insertions |
- oldNode = null; |
- // print('...processing remaining insertions'); |
- while (startIndex < endIndex) { |
- // print('> inserting at $startIndex'); |
- currentNode = _children[startIndex]; |
- sync(startIndex); |
- startIndex++; |
- } |
- |
- // Removals |
- // print('...processing remaining removals'); |
- currentNode = null; |
- while (oldStartIndex < oldEndIndex) { |
- oldNode = oldChildren[oldStartIndex]; |
- // print('> ${oldNode._key} removing from $oldEndIndex'); |
- oldNode._remove(); |
- advanceOldStartIndex(); |
- } |
- |
- oldElement._children = null; |
- return false; |
- } |
-} |
- |
-class Container extends Element { |
- |
- String get _tagName => 'div'; |
- |
- static final Container _emptyContainer = new Container(); |
- |
- Element get _emptyElement => _emptyContainer; |
- |
- Container({ |
- Object key, |
- List<Node> children, |
- Style style, |
- String inlineStyle |
- }) : super( |
- key: key, |
- children: children, |
- style: style, |
- inlineStyle: inlineStyle |
- ); |
-} |
- |
-class Image extends Element { |
- |
- String get _tagName => 'img'; |
- |
- static final Image _emptyImage = new Image(); |
- Element get _emptyElement => _emptyImage; |
- |
- String src; |
- int width; |
- int height; |
- |
- Image({ |
- Object key, |
- List<Node> children, |
- Style style, |
- String inlineStyle, |
- this.width, |
- this.height, |
- this.src |
- }) : super( |
- key: key, |
- children: children, |
- style: style, |
- inlineStyle: inlineStyle |
- ); |
- |
- void _syncNode([Element old]) { |
- super._syncNode(old); |
- |
- Image oldImage = old != null ? old : _emptyImage; |
- sky.HTMLImageElement skyImage = _root as sky.HTMLImageElement; |
- if (src != oldImage.src) { |
- skyImage.src = src; |
- } |
- |
- if (width != oldImage.width) { |
- skyImage.style['width'] = '${width}px'; |
- } |
- if (height != oldImage.height) { |
- skyImage.style['height'] = '${height}px'; |
- } |
- } |
-} |
- |
-class Anchor extends Element { |
- |
- String get _tagName => 'a'; |
- |
- static final Anchor _emptyAnchor = new Anchor(); |
- |
- String href; |
- |
- Anchor({ |
- Object key, |
- List<Node> children, |
- Style style, |
- String inlineStyle, |
- this.width, |
- this.height, |
- this.href |
- }) : super( |
- key: key, |
- children: children, |
- style: style, |
- inlineStyle: inlineStyle |
- ); |
- |
- void _syncNode([Element old]) { |
- Anchor oldAnchor = old != null ? old as Anchor : _emptyAnchor; |
- super._syncNode(oldAnchor); |
- |
- sky.HTMLAnchorElement skyAnchor = _root as sky.HTMLAnchorElement; |
- if (href != oldAnchor.href) { |
- skyAnchor.href = href; |
- } |
- } |
-} |