3
3
*/
4
4
library angular.introspection;
5
5
6
+ import 'dart:async' as async;
6
7
import 'dart:html' as dom;
8
+ import 'dart:js' as js;
7
9
import 'package:di/di.dart' ;
8
- import 'package:angular/introspection_js .dart' ;
10
+ import 'package:angular/animate/module .dart' ;
9
11
import 'package:angular/core/module_internal.dart' ;
10
12
import 'package:angular/core_dom/module_internal.dart' ;
13
+ import 'package:angular/core/static_keys.dart' ;
14
+
15
+
16
+ /**
17
+ * A global write only variable which keeps track of objects attached to the
18
+ * elements. This is useful for debugging AngularDart application from the
19
+ * browser's REPL.
20
+ */
21
+ var elementExpando = new Expando ('element' );
22
+
23
+
24
+ ElementProbe _findProbeWalkingUp (dom.Node node, [dom.Node ascendUntil]) {
25
+ while (node != null && node != ascendUntil) {
26
+ var probe = elementExpando[node];
27
+ if (probe != null ) return probe;
28
+ node = node.parent;
29
+ }
30
+ return null ;
31
+ }
32
+
33
+
34
+ _walkProbesInTree (dom.Node node, Function walker) {
35
+ var probe = elementExpando[node];
36
+ if (probe == null || walker (probe) != true ) {
37
+ for (var child in node.childNodes) {
38
+ _walkProbesInTree (child, walker);
39
+ }
40
+ }
41
+ }
42
+
43
+
44
+ ElementProbe _findProbeInTree (dom.Node node, [dom.Node ascendUntil]) {
45
+ var probe;
46
+ _walkProbesInTree (node, (_probe) {
47
+ probe = _probe;
48
+ return true ;
49
+ });
50
+ return (probe != null ) ? probe : _findProbeWalkingUp (node, ascendUntil);
51
+ }
52
+
53
+
54
+ List <ElementProbe > _findAllProbesInTree (dom.Node node) {
55
+ List <ElementProbe > probes = [];
56
+ _walkProbesInTree (node, probes.add);
57
+ return probes;
58
+ }
59
+
11
60
12
61
/**
13
62
* Return the [ElementProbe] object for the closest [Element] in the hierarchy.
@@ -21,25 +70,21 @@ import 'package:angular/core_dom/module_internal.dart';
21
70
* function is not intended to be called from Angular application.
22
71
*/
23
72
ElementProbe ngProbe (nodeOrSelector) {
24
- var errorMsg;
25
- var node;
26
73
if (nodeOrSelector == null ) throw "ngProbe called without node" ;
74
+ var node = nodeOrSelector;
27
75
if (nodeOrSelector is String ) {
28
76
var nodes = ngQuery (dom.document, nodeOrSelector);
29
- if (nodes.isNotEmpty) node = nodes.first;
30
- errorMsg = "Could not find a probe for the selector '$nodeOrSelector ' nor its parents" ;
31
- } else {
32
- node = nodeOrSelector;
33
- errorMsg = "Could not find a probe for the node '$node ' nor its parents" ;
77
+ node = (nodes.isNotEmpty) ? nodes.first : null ;
34
78
}
35
- while (node != null ) {
36
- var probe = elementExpando[node];
37
- if (probe != null ) return probe;
38
- node = node.parent;
79
+ var probe = _findProbeWalkingUp (node);
80
+ if (probe != null ) {
81
+ return probe;
39
82
}
40
- throw errorMsg;
83
+ var forWhat = (nodeOrSelector is String ) ? "selector" : "node" ;
84
+ throw "Could not find a probe for the $forWhat '$nodeOrSelector ' nor its parents" ;
41
85
}
42
86
87
+
43
88
/**
44
89
* Return the [Injector] associated with a current [Element] .
45
90
*
@@ -79,6 +124,7 @@ List<dom.Element> ngQuery(dom.Node element, String selector,
79
124
return list;
80
125
}
81
126
127
+
82
128
/**
83
129
* Return a List of directives associated with a current [Element] .
84
130
*
@@ -88,3 +134,160 @@ List<dom.Element> ngQuery(dom.Node element, String selector,
88
134
*/
89
135
List <Object > ngDirectives (nodeOrSelector) => ngProbe (nodeOrSelector).directives;
90
136
137
+
138
+
139
+ js.JsObject _jsProbe (ElementProbe probe) {
140
+ return new js.JsObject .jsify ({
141
+ "element" : probe.element,
142
+ "injector" : _jsInjector (probe.injector),
143
+ "scope" : _jsScopeFromProbe (probe),
144
+ "directives" : probe.directives.map ((directive) => _jsDirective (directive)),
145
+ "bindings" : probe.bindingExpressions,
146
+ "models" : probe.modelExpressions
147
+ })..['_dart_' ] = probe;
148
+ }
149
+
150
+
151
+ js.JsObject _jsInjector (Injector injector) =>
152
+ new js.JsObject .jsify ({"get" : injector.get })..['_dart_' ] = injector;
153
+
154
+
155
+ js.JsObject _jsScopeFromProbe (ElementProbe probe) =>
156
+ _jsScope (probe.scope, probe.injector.getByKey (SCOPE_STATS_CONFIG_KEY ));
157
+
158
+
159
+ js.JsObject _jsScope (Scope scope, ScopeStatsConfig config) {
160
+ return new js.JsObject .jsify ({
161
+ "apply" : scope.apply,
162
+ "broadcast" : scope.broadcast,
163
+ "context" : scope.context,
164
+ "destroy" : scope.destroy,
165
+ "digest" : scope.rootScope.digest,
166
+ "emit" : scope.emit,
167
+ "flush" : scope.rootScope.flush,
168
+ "get" : (name) => scope.context[name],
169
+ "isAttached" : scope.isAttached,
170
+ "isDestroyed" : scope.isDestroyed,
171
+ "set" : (name, value) => scope.context[name] = value,
172
+ "scopeStatsEnable" : () => config.emit = true ,
173
+ "scopeStatsDisable" : () => config.emit = false ,
174
+ r"$eval" : (expr) => _jsify (scope.eval (expr)),
175
+ })..['_dart_' ] = scope;
176
+ }
177
+
178
+
179
+ // Helper function to JSify the result of a scope.eval() for simple cases.
180
+ _jsify (var obj) {
181
+ if (obj is js.JsObject ) {
182
+ return obj;
183
+ } else if (obj is Iterable ) {
184
+ return new js.JsObject .jsify (obj)..['_dart_' ] = obj;
185
+ } else {
186
+ return obj;
187
+ }
188
+ }
189
+
190
+
191
+ _jsDirective (directive) => directive;
192
+
193
+
194
+ abstract class _JsObjectProxyable {
195
+ js.JsObject _toJsObject ();
196
+ }
197
+
198
+
199
+ typedef List <String > _GetExpressionsFromProbe (ElementProbe probe);
200
+
201
+
202
+ /**
203
+ * Returns the "$testability service" object for JS / Protractor use.
204
+ *
205
+ * JS code expects to get a hold of this object in the following way:
206
+ *
207
+ * // Prereq: There is an "angular" object on window accessible via JS.
208
+ * var testability = angular.element(document).injector().get('$testability');
209
+ */
210
+ class _Testability implements _JsObjectProxyable {
211
+ final dom.Node node;
212
+ final ElementProbe probe;
213
+
214
+ _Testability (this .node, this .probe);
215
+ _Testability .fromNode (dom.Node node): this (node, _findProbeInTree (node));
216
+
217
+ notifyWhenNoOutstandingRequests (callback) {
218
+ probe.injector.get (VmTurnZone ).run (
219
+ () => new async .Timer (Duration .ZERO , callback));
220
+ }
221
+
222
+ /**
223
+ * Returns a list of all nodes in the selected tree that have an `ng-model`
224
+ * binding specified by the [modelString] . If the optional [exactMatch]
225
+ * parameter is provided and true, it restricts the searches to bindings that
226
+ * are exact matches for [modelString] .
227
+ */
228
+ List <dom.Node > findModels (String modelString, [bool exactMatch]) => _findByExpression (
229
+ modelString, exactMatch, (ElementProbe probe) => probe.modelExpressions);
230
+
231
+ /**
232
+ * Returns a list of all nodes in the selected tree that have `ng-bind` or
233
+ * mustache bindings specified by the [bindingString] . If the optional
234
+ * [exactMatch] parameter is provided and true, it restricts the searches to
235
+ * bindings that are exact matches for [bindingString] .
236
+ */
237
+ List <dom.Node > findBindings (String bindingString, [bool exactMatch]) => _findByExpression (
238
+ bindingString, exactMatch, (ElementProbe probe) => probe.bindingExpressions);
239
+
240
+ List <dom.Node > _findByExpression (String query, bool exactMatch, _GetExpressionsFromProbe getExpressions) {
241
+ List <ElementProbe > probes = _findAllProbesInTree (node);
242
+ if (probes.length == 0 ) {
243
+ probes.add (_findProbeWalkingUp (node));
244
+ }
245
+ List <dom.Node > results = [];
246
+ for (ElementProbe probe in probes) {
247
+ for (String expression in getExpressions (probe)) {
248
+ if (exactMatch == true ? expression == query : expression.indexOf (query) >= 0 ) {
249
+ results.add (probe.element);
250
+ }
251
+ }
252
+ }
253
+ return results;
254
+ }
255
+
256
+ allowAnimations (bool allowed) {
257
+ Animate animate = probe.injector.get (Animate );
258
+ bool previous = animate.animationsAllowed;
259
+ animate.animationsAllowed = (allowed == true );
260
+ return previous;
261
+ }
262
+
263
+ js.JsObject _toJsObject () {
264
+ return new js.JsObject .jsify ({
265
+ 'allowAnimations' : allowAnimations,
266
+ 'findBindings' : (bindingString, [exactMatch]) =>
267
+ findBindings (bindingString, exactMatch),
268
+ 'findModels' : (modelExpressions, [exactMatch]) =>
269
+ findModels (modelExpressions, exactMatch),
270
+ 'notifyWhenNoOutstandingRequests' : (callback) =>
271
+ notifyWhenNoOutstandingRequests (() => callback.apply ([])),
272
+ 'probe' : () => _jsProbe (probe),
273
+ 'scope' : () => _jsScopeFromProbe (probe),
274
+ 'eval' : (expr) => probe.scope.eval (expr),
275
+ 'query' : (String selector, [String containsText]) =>
276
+ ngQuery (node, selector, containsText),
277
+ })..['_dart_' ] = this ;
278
+ }
279
+ }
280
+
281
+
282
+ void publishToJavaScript () {
283
+ var C = js.context;
284
+ C ['ngProbe' ] = (nodeOrSelector) => _jsProbe (ngProbe (nodeOrSelector));
285
+ C ['ngInjector' ] = (nodeOrSelector) => _jsInjector (ngInjector (nodeOrSelector));
286
+ C ['ngScope' ] = (nodeOrSelector) => _jsScopeFromProbe (ngProbe (nodeOrSelector));
287
+ C ['ngQuery' ] = (dom.Node node, String selector, [String containsText]) =>
288
+ new js.JsArray .from (ngQuery (node, selector, containsText));
289
+ C ['angular' ] = new js.JsObject .jsify ({
290
+ 'resumeBootstrap' : ([arg]) {},
291
+ 'getTestability' : (node) => new _Testability .fromNode (node)._toJsObject (),
292
+ });
293
+ }
0 commit comments