Index: lib/src/common/behavior.dart |
diff --git a/lib/src/common/behavior.dart b/lib/src/common/behavior.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..a26f139d4b1072e1387a599af8543b004a32b06f |
--- /dev/null |
+++ b/lib/src/common/behavior.dart |
@@ -0,0 +1,78 @@ |
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
Siggi Cherem (dart-lang)
2015/08/13 18:52:41
2015 :)
jakemac
2015/08/13 19:14:29
Acknowledged.
|
+// 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. |
+library polymer.src.common.behavior; |
+ |
+import 'dart:js'; |
+import 'package:reflectable/reflectable.dart'; |
+import 'js_proxy.dart'; |
+ |
+// Interface for behavior annotations. |
+abstract class BehaviorAnnotation { |
+ // Returns the JsObject created for this behavior. |
+ JsObject getBehavior(Type type); |
+} |
+ |
+Map<Type, JsObject> _behaviorsByType = {}; |
+ |
+const String _lifecycleMethodsPattern = |
+ r'^created|attached|detached|attributeChanged$'; |
+final RegExp _lifecycleMethodsRegex = new RegExp(_lifecycleMethodsPattern); |
+ |
+// Annotation class for behaviors written in dart. |
+class Behavior extends Reflectable implements BehaviorAnnotation { |
+ JsObject getBehavior(Type type) { |
+ return _behaviorsByType.putIfAbsent(type, () { |
+ var obj = new JsObject(context['Object']); |
+ |
+ // Add an entry for each static lifecycle method. These methods must take |
+ // a `this` arg as the first argument. |
+ var typeMirror = this.reflectType(type); |
+ typeMirror.staticMembers.forEach((String name, MethodMirror method) { |
+ if (!_lifecycleMethodsRegex.hasMatch(name)) return; |
+ if (name == 'attributeChanged') { |
+ obj[name] = new JsFunction.withThis( |
+ (thisArg, String attributeName, Type type, value) { |
+ typeMirror.invoke( |
+ name, [dartValue(thisArg), attributeName, type, value]); |
+ }); |
+ } else { |
+ obj[name] = new JsFunction.withThis((thisArg) { |
+ typeMirror.invoke(name, [thisArg]); |
+ }); |
+ } |
+ }); |
+ |
+ return obj; |
+ }); |
+ } |
+ |
+ const Behavior() |
+ : super(declarationsCapability, typeCapability, |
+ const StaticInvokeCapability(_lifecycleMethodsPattern)); |
+} |
+ |
+const behavior = const Behavior(); |
+ |
+// Annotation class for wrappers around behaviors written in javascript. |
+class BehaviorProxy implements BehaviorAnnotation { |
+ // Path within js global context object to the original js behavior object. |
+ final List<String> _jsPath; |
+ |
+ // Returns the actual behavior. |
+ JsObject getBehavior(Type type) { |
+ return _behaviorsByType.putIfAbsent(type, () { |
+ if (_jsPath.isEmpty) { |
+ throw 'Invalid empty path for BehaviorProxy $_jsPath.'; |
+ } |
+ var obj = context; |
+ for (var part in _jsPath) { |
+ obj = obj[part]; |
+ } |
+ return obj; |
+ }); |
+ } |
+ |
+ // TODO(jakemac): Support dot separated Strings for paths? |
+ const BehaviorProxy(this._jsPath); |
+} |