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