Version 0.6.15.2 .

svn merge -c 25799 https://dart.googlecode.com/svn/branches/bleeding_edge trunk

git-svn-id: http://dart.googlecode.com/svn/trunk@25802 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/pkg/custom_element/lib/custom_element.dart b/pkg/custom_element/lib/custom_element.dart
index 369501c..50cc1a6 100644
--- a/pkg/custom_element/lib/custom_element.dart
+++ b/pkg/custom_element/lib/custom_element.dart
@@ -31,7 +31,7 @@
 void registerCustomElement(String localName, CustomElement create()) {
   if (_customElements == null) {
     _customElements = {};
-    CustomElement.templateCreated.add(initCustomElements);
+    mdv.instanceCreated.add(initCustomElements);
     // TODO(jmesserly): use MutationObserver to watch for inserts?
   }
 
@@ -159,46 +159,6 @@
 
   getShadowRoot(String componentName) => _generatedRoots[componentName];
 
-
-  /**
-   * *Warning*: This is an implementation helper for Custom Elements and
-   * should not be used in your code.
-   *
-   * Clones the template, instantiates custom elements and hooks events, then
-   * returns it.
-   */
-  DocumentFragment cloneTemplate(DocumentFragment shadowTemplate) {
-    var result = shadowTemplate.clone(true);
-    // TODO(jmesserly): should bindModel ensure this happens?
-    TemplateElement.bootstrap(result);
-    if (_templateCreated != null) {
-      for (var callback in _templateCreated) callback(result);
-    }
-    return result;
-  }
-
-  // TODO(jmesserly): ideally this would be a stream, but they don't allow
-  // reentrancy.
-  static Set<DocumentFragmentCreated> _templateCreated;
-
-  /**
-   * *Warning*: This is an implementation helper for Custom Elements and
-   * should not be used in your code.
-   *
-   * This event is fired whenever a template is instantiated via
-   * [cloneTemplate] or via [Element.createInstance]
-   */
-  // TODO(jmesserly): This is a hack, and is neccesary for the polyfill
-  // because custom elements are not upgraded during clone()
-  static Set<DocumentFragmentCreated> get templateCreated {
-    if (_templateCreated == null) {
-      _templateCreated = new Set<DocumentFragmentCreated>();
-      mdv.instanceCreated.listen((value) {
-        for (var callback in _templateCreated) callback(value);
-      });
-    }
-    return _templateCreated;
-  }
   /**
    * Invoked when this component gets created.
    * Note that [root] will be a [ShadowRoot] if the browser supports Shadow DOM.
@@ -230,7 +190,7 @@
       host.createInstance(model, delegate);
   createBinding(String name, model, String path) =>
       host.createBinding(name, model, path);
-  void bind(String name, model, String path) => host.bind(name, model, path);
+  bind(String name, model, String path) => host.bind(name, model, path);
   void unbind(String name) => host.unbind(name);
   void unbindAll() => host.unbindAll();
   get bindings => host.bindings;
@@ -659,8 +619,6 @@
 }
 
 
-typedef DocumentFragmentCreated(DocumentFragment fragment);
-
 Map<String, Function> _customElements;
 
 void _initCustomElement(Element node, CustomElement ctor()) {
diff --git a/pkg/mdv/lib/mdv.dart b/pkg/mdv/lib/mdv.dart
index 8ba6bf6..feec831 100644
--- a/pkg/mdv/lib/mdv.dart
+++ b/pkg/mdv/lib/mdv.dart
@@ -36,7 +36,12 @@
   TemplateElement.mdvPackage = _mdv;
 }
 
-StreamController<DocumentFragment> _instanceCreated;
+
+typedef DocumentFragmentCreated(DocumentFragment fragment);
+
+// TODO(jmesserly): ideally this would be a stream, but they don't allow
+// reentrancy.
+Set<DocumentFragmentCreated> _instanceCreated;
 
 /**
  * *Warning*: This is an implementation helper for Model-Driven Views and
@@ -49,11 +54,11 @@
 // because custom elements are not upgraded during clone()
 // TODO(jmesserly): polymer removed this in:
 // https://github.com/Polymer/platform/commit/344ffeaae475babb529403f6608588a0fc73f4e7
-Stream<DocumentFragment> get instanceCreated {
+Set<DocumentFragmentCreated> get instanceCreated {
   if (_instanceCreated == null) {
-    _instanceCreated = new StreamController<DocumentFragment>(sync: true);
+    _instanceCreated = new Set<DocumentFragmentCreated>();
   }
-  return _instanceCreated.stream;
+  return _instanceCreated;
 }
 
 
diff --git a/pkg/mdv/lib/src/node.dart b/pkg/mdv/lib/src/node.dart
index d2fa6d4..cf36831 100644
--- a/pkg/mdv/lib/src/node.dart
+++ b/pkg/mdv/lib/src/node.dart
@@ -19,9 +19,12 @@
    */
   NodeBinding bind(String name, model, String path) {
     var binding = bindings[name];
-    if (binding != null)  binding.close();
+    if (binding != null) binding.close();
 
-    binding = createBinding(name, model, path);
+    // Note: dispatch through the xtag so a custom element can override it.
+    binding = (node is Element ? (node as Element).xtag : node)
+        .createBinding(name, model, path);
+
     bindings[name] = binding;
     if (binding == null) {
       window.console.error('Unhandled binding to Node: '
diff --git a/pkg/mdv/lib/src/template.dart b/pkg/mdv/lib/src/template.dart
index 7bdc0bb..785d8c0 100644
--- a/pkg/mdv/lib/src/template.dart
+++ b/pkg/mdv/lib/src/template.dart
@@ -41,7 +41,9 @@
     var instance = _createDeepCloneAndDecorateTemplates(
         template.ref.content, delegate);
 
-    if (_instanceCreated != null) _instanceCreated.add(instance);
+    if (_instanceCreated != null) {
+      for (var callback in _instanceCreated) callback(instance);
+    }
 
     _addBindings(instance, model, delegate);
     _addTemplateInstanceRecord(instance, model);
diff --git a/pkg/mdv/test/custom_element_bindings_test.dart b/pkg/mdv/test/custom_element_bindings_test.dart
index 4b1bc54..c4dd565 100644
--- a/pkg/mdv/test/custom_element_bindings_test.dart
+++ b/pkg/mdv/test/custom_element_bindings_test.dart
@@ -126,11 +126,12 @@
           '</my-custom-element>'
         '</template>');
 
-    mdv.instanceCreated.listen((fragment) {
+    callback(fragment) {
       for (var e in fragment.queryAll('my-custom-element')) {
         new MyCustomElement.attach(e);
       }
-    });
+    }
+    mdv.instanceCreated.add(callback);
 
     div.query('template').model = model;
     performMicrotaskCheckpoint();
@@ -161,6 +162,8 @@
 
     expect(element.xtag.myPoint, null, reason: 'model was unbound');
     expect(element.xtag.scaryMonster.health, 100, reason: 'model was unbound');
+
+    mdv.instanceCreated.remove(callback);
   });
 
 }
@@ -177,64 +180,45 @@
   Point myPoint;
   Monster scaryMonster;
 
-  StreamSubscription _sub1, _sub2;
-
   MyCustomElement() : this.attach(new Element.tag('my-custom-element'));
 
   MyCustomElement.attach(this.real) {
     real.xtag = this;
   }
 
-
   get attributes => real.attributes;
+  get bindings => real.bindings;
 
-  void bind(String name, model, String path) {
+  NodeBinding createBinding(String name, model, String path) {
     switch (name) {
       case 'my-point':
-        unbind('my-point');
-        attributes.remove('my-point');
-
-        _sub1 = new PathObserver(model, path).bindSync((v) {
-          myPoint = v;
-        });
-        return;
       case 'scary-monster':
-        unbind('scary-monster');
-        attributes.remove('scary-monster');
-
-        _sub2 = new PathObserver(model, path).bindSync((v) {
-          scaryMonster = v;
-        });
-        return;
+        return new _MyCustomBinding(this, name, model, path);
     }
-    real.bind(name, model, path);
+    return real.createBinding(name, model, path);
   }
 
-  void unbind(String name) {
-    switch (name) {
-      case 'my-point':
-        if (_sub1 != null) {
-          _sub1.cancel();
-          _sub1 = null;
-        }
-        return;
-      case 'scary-monster':
-        if (_sub2 != null) {
-          _sub2.cancel();
-          _sub2 = null;
-        }
-        return;
-    }
-    real.unbind(name);
+  bind(String name, model, String path) => real.bind(name, model, path);
+  void unbind(String name) => real.unbind(name);
+  void unbindAll() => real.unbindAll();
+}
+
+class _MyCustomBinding extends mdv.NodeBinding {
+  _MyCustomBinding(MyCustomElement node, property, model, path)
+      : super(node, property, model, path) {
+
+    node.attributes.remove(property);
   }
 
-  void unbindAll() {
-    unbind('my-point');
-    unbind('scary-monster');
-    real.unbindAll();
+  MyCustomElement get node => super.node;
+
+  void boundValueChanged(newValue) {
+    if (property == 'my-point') node.myPoint = newValue;
+    if (property == 'scary-monster') node.scaryMonster = newValue;
   }
 }
 
+
 /**
  * Demonstrates a custom element can override attributes []= and remove.
  * and see changes that the data binding system is making to the attributes.
@@ -253,7 +237,9 @@
     real.xtag = this;
   }
 
-  void bind(String name, model, String path) => real.bind(name, model, path);
+  createBinding(String name, model, String path) =>
+      real.createBinding(name, model, path);
+  bind(String name, model, String path) => real.bind(name, model, path);
   void unbind(String name) => real.unbind(name);
   void unbindAll() => real.unbindAll();
 }
diff --git a/pkg/mdv/test/mdv_test_utils.dart b/pkg/mdv/test/mdv_test_utils.dart
index 10ff172..1b068c2 100644
--- a/pkg/mdv/test/mdv_test_utils.dart
+++ b/pkg/mdv/test/mdv_test_utils.dart
@@ -9,6 +9,9 @@
 import 'package:observe/observe.dart';
 import 'package:unittest/unittest.dart';
 
+import 'package:observe/src/microtask.dart';
+export 'package:observe/src/microtask.dart';
+
 final bool parserHasNativeTemplate = () {
   var div = new DivElement()..innerHtml = '<table><template>';
   return div.firstChild.firstChild != null &&
@@ -61,42 +64,6 @@
   }
 }
 
-// TODO(jmesserly): this is a copy/paste from observe_test_utils.dart
-// Is it worth putting it in its own package, or in an existing one?
-
-void performMicrotaskCheckpoint() {
-  Observable.dirtyCheck();
-
-  while (_pending.length > 0) {
-    var pending = _pending;
-    _pending = [];
-
-    for (var callback in pending) {
-      try {
-        callback();
-      } catch (e, s) {
-        new Completer().completeError(e, s);
-      }
-    }
-
-    Observable.dirtyCheck();
-  }
-}
-
-List<Function> _pending = [];
-
-wrapMicrotask(void testCase()) {
-  return () {
-    runZonedExperimental(() {
-      try {
-        testCase();
-      } finally {
-        performMicrotaskCheckpoint();
-      }
-    }, onRunAsync: (callback) => _pending.add(callback));
-  };
-}
-
 observeTest(name, testCase) => test(name, wrapMicrotask(testCase));
 
 solo_observeTest(name, testCase) => solo_test(name, wrapMicrotask(testCase));
diff --git a/pkg/mdv/test/template_element_test.dart b/pkg/mdv/test/template_element_test.dart
index a424e9e..e90cf9c 100644
--- a/pkg/mdv/test/template_element_test.dart
+++ b/pkg/mdv/test/template_element_test.dart
@@ -1694,10 +1694,12 @@
 
   observeTest('instanceCreated hack', () {
     var called = false;
-    var sub = mdv.instanceCreated.listen((node) {
+
+    callback(node) {
       called = true;
       expect(node.nodeType, Node.DOCUMENT_FRAGMENT_NODE);
-    });
+    }
+    mdv.instanceCreated.add(callback);
 
     var div = createTestHtml('<template bind="{{}}">Foo</template>');
     expect(called, false);
@@ -1706,7 +1708,7 @@
     performMicrotaskCheckpoint();
     expect(called, true);
 
-    sub.cancel();
+    mdv.instanceCreated.remove(callback);
   });
 }
 
diff --git a/pkg/observe/lib/observe.dart b/pkg/observe/lib/observe.dart
index f2f8283..06136df 100644
--- a/pkg/observe/lib/observe.dart
+++ b/pkg/observe/lib/observe.dart
@@ -80,6 +80,10 @@
 // above.
 import 'src/dirty_check.dart';
 
+// TODO(jmesserly): should this be public? For now we're just using it inside
+// Polymer.
+import 'src/microtask.dart';
+
 part 'src/change_notifier.dart';
 part 'src/change_record.dart';
 part 'src/compound_binding.dart';
diff --git a/pkg/observe/lib/src/microtask.dart b/pkg/observe/lib/src/microtask.dart
new file mode 100644
index 0000000..e56fb62
--- /dev/null
+++ b/pkg/observe/lib/src/microtask.dart
@@ -0,0 +1,62 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+/**
+ * *Warning*: this library is **internal**, and APIs are subject to change.
+ *
+ * Wraps a callback using [wrapMicrotask] and provides the ability to pump all
+ * observable objects and [runAsync] calls via [performMicrotaskCheckpoint].
+ */
+library observe.src.microtask;
+
+import 'dart:async' show Completer, runZonedExperimental;
+import 'package:observe/observe.dart' show Observable;
+
+// TODO(jmesserly): remove "microtask" from these names and instead import
+// the library "as microtask"?
+
+/**
+ * This change pumps events relevant to observers and data-binding tests.
+ * This must be used inside an [observeTest].
+ *
+ * Executes all pending [runAsync] calls on the event loop, as well as
+ * performing [Observable.dirtyCheck], until there are no more pending events.
+ * It will always dirty check at least once.
+ */
+void performMicrotaskCheckpoint() {
+  Observable.dirtyCheck();
+
+  while (_pending.length > 0) {
+    var pending = _pending;
+    _pending = [];
+
+    for (var callback in pending) {
+      try {
+        callback();
+      } catch (e, s) {
+        new Completer().completeError(e, s);
+      }
+    }
+
+    Observable.dirtyCheck();
+  }
+}
+
+List<Function> _pending = [];
+
+/**
+ * Wraps the [testCase] in a zone that supports [performMicrotaskCheckpoint],
+ * and returns the test case.
+ */
+wrapMicrotask(void testCase()) {
+  return () {
+    runZonedExperimental(() {
+      try {
+        testCase();
+      } finally {
+        performMicrotaskCheckpoint();
+      }
+    }, onRunAsync: (callback) => _pending.add(callback));
+  };
+}
diff --git a/pkg/observe/test/observe_test_utils.dart b/pkg/observe/test/observe_test_utils.dart
index 2d4e7cb..fe3e869 100644
--- a/pkg/observe/test/observe_test_utils.dart
+++ b/pkg/observe/test/observe_test_utils.dart
@@ -8,57 +8,15 @@
 import 'package:observe/observe.dart';
 import 'package:unittest/unittest.dart';
 
+import 'package:observe/src/microtask.dart';
+export 'package:observe/src/microtask.dart';
+
 // TODO(jmesserly): use matchers when we have a way to compare ChangeRecords.
 // For now just use the toString.
 expectChanges(actual, expected, {reason}) =>
     expect('$actual', '$expected', reason: reason);
 
 /**
- * This change pumps events relevant to observers and data-binding tests.
- * This must be used inside an [observeTest].
- *
- * Executes all pending [runAsync] calls on the event loop, as well as
- * performing [Observable.dirtyCheck], until there are no more pending events.
- * It will always dirty check at least once.
- */
-void performMicrotaskCheckpoint() {
-  Observable.dirtyCheck();
-
-  while (_pending.length > 0) {
-    var pending = _pending;
-    _pending = [];
-
-    for (var callback in pending) {
-      try {
-        callback();
-      } catch (e, s) {
-        new Completer().completeError(e, s);
-      }
-    }
-
-    Observable.dirtyCheck();
-  }
-}
-
-List<Function> _pending = [];
-
-/**
- * Wraps the [testCase] in a zone that supports [performMicrotaskCheckpoint],
- * and returns the test case.
- */
-wrapMicrotask(void testCase()) {
-  return () {
-    runZonedExperimental(() {
-      try {
-        testCase();
-      } finally {
-        performMicrotaskCheckpoint();
-      }
-    }, onRunAsync: (callback) => _pending.add(callback));
-  };
-}
-
-/**
  * This is a special kind of unit [test], that supports
  * calling [performMicrotaskCheckpoint] during the test to pump events
  * that original from observable objects.
diff --git a/tools/VERSION b/tools/VERSION
index bd1f182..b7a25eb 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -1,4 +1,4 @@
 MAJOR 0
 MINOR 6
 BUILD 15
-PATCH 1
+PATCH 2