OLD | NEW |
1 part of angular.core.dom; | 1 part of angular.core.dom_internal; |
2 | 2 |
3 @NgInjectableService() | 3 abstract class Compiler implements Function { |
4 class Compiler { | 4 ViewFactory call(List<dom.Node> elements, DirectiveMap directives); |
5 final Profiler _perf; | |
6 final Parser _parser; | |
7 final Expando _expando; | |
8 | |
9 Compiler(this._perf, this._parser, this._expando); | |
10 | |
11 _compileBlock(NodeCursor domCursor, NodeCursor templateCursor, | |
12 List<DirectiveRef> useExistingDirectiveRefs, | |
13 DirectiveMap directives) { | |
14 if (domCursor.nodeList().length == 0) return null; | |
15 | |
16 var directivePositions = null; // don't pre-create to create sparse tree and
prevent GC pressure. | |
17 var cursorAlreadyAdvanced; | |
18 | |
19 do { | |
20 var declaredDirectiveRefs = useExistingDirectiveRefs == null | |
21 ? directives.selector(domCursor.nodeList()[0]) | |
22 : useExistingDirectiveRefs; | |
23 var children = NgAnnotation.COMPILE_CHILDREN; | |
24 var childDirectivePositions = null; | |
25 List<DirectiveRef> usableDirectiveRefs = null; | |
26 | |
27 cursorAlreadyAdvanced = false; | |
28 | |
29 for (var j = 0; j < declaredDirectiveRefs.length; j++) { | |
30 DirectiveRef directiveRef = declaredDirectiveRefs[j]; | |
31 NgAnnotation annotation = directiveRef.annotation; | |
32 var blockFactory = null; | |
33 | |
34 if (annotation.children != children && | |
35 children == NgAnnotation.COMPILE_CHILDREN) { | |
36 children = annotation.children; | |
37 } | |
38 | |
39 if (children == NgAnnotation.TRANSCLUDE_CHILDREN) { | |
40 var remainingDirectives = declaredDirectiveRefs.sublist(j + 1); | |
41 blockFactory = compileTransclusion( | |
42 domCursor, templateCursor, | |
43 directiveRef, remainingDirectives, directives); | |
44 | |
45 // stop processing further directives since they belong to | |
46 // transclusion | |
47 j = declaredDirectiveRefs.length; | |
48 } | |
49 if (usableDirectiveRefs == null) usableDirectiveRefs = []; | |
50 directiveRef.blockFactory = blockFactory; | |
51 createMappings(directiveRef); | |
52 usableDirectiveRefs.add(directiveRef); | |
53 } | |
54 | |
55 if (children == NgAnnotation.COMPILE_CHILDREN && domCursor.descend()) { | |
56 templateCursor.descend(); | |
57 | |
58 childDirectivePositions = | |
59 _compileBlock(domCursor, templateCursor, null, directives); | |
60 | |
61 domCursor.ascend(); | |
62 templateCursor.ascend(); | |
63 } | |
64 | |
65 if (childDirectivePositions != null || usableDirectiveRefs != null) { | |
66 if (directivePositions == null) directivePositions = []; | |
67 var directiveOffsetIndex = templateCursor.index; | |
68 | |
69 directivePositions | |
70 ..add(directiveOffsetIndex) | |
71 ..add(usableDirectiveRefs) | |
72 ..add(childDirectivePositions); | |
73 } | |
74 } while (templateCursor.microNext() && domCursor.microNext()); | |
75 | |
76 return directivePositions; | |
77 } | |
78 | |
79 BlockFactory compileTransclusion( | |
80 NodeCursor domCursor, NodeCursor templateCursor, | |
81 DirectiveRef directiveRef, | |
82 List<DirectiveRef> transcludedDirectiveRefs, | |
83 DirectiveMap directives) { | |
84 var anchorName = directiveRef.annotation.selector + (directiveRef.value != n
ull ? '=' + directiveRef.value : ''); | |
85 var blockFactory; | |
86 var blocks; | |
87 | |
88 var transcludeCursor = templateCursor.replaceWithAnchor(anchorName); | |
89 var domCursorIndex = domCursor.index; | |
90 var directivePositions = | |
91 _compileBlock(domCursor, transcludeCursor, transcludedDirectiveRefs, dir
ectives); | |
92 if (directivePositions == null) directivePositions = []; | |
93 | |
94 blockFactory = new BlockFactory(transcludeCursor.elements, directivePosition
s, _perf, _expando); | |
95 domCursor.index = domCursorIndex; | |
96 | |
97 if (domCursor.isInstance()) { | |
98 domCursor.insertAnchorBefore(anchorName); | |
99 blocks = [blockFactory(domCursor.nodeList())]; | |
100 domCursor.macroNext(); | |
101 templateCursor.macroNext(); | |
102 while (domCursor.isValid() && domCursor.isInstance()) { | |
103 blocks.add(blockFactory(domCursor.nodeList())); | |
104 domCursor.macroNext(); | |
105 templateCursor.remove(); | |
106 } | |
107 } else { | |
108 domCursor.replaceWithAnchor(anchorName); | |
109 } | |
110 | |
111 return blockFactory; | |
112 } | |
113 | |
114 BlockFactory call(List<dom.Node> elements, DirectiveMap directives) { | |
115 var timerId; | |
116 assert((timerId = _perf.startTimer('ng.compile', _html(elements))) != false)
; | |
117 List<dom.Node> domElements = elements; | |
118 List<dom.Node> templateElements = cloneElements(domElements); | |
119 var directivePositions = _compileBlock( | |
120 new NodeCursor(domElements), new NodeCursor(templateElements), | |
121 null, directives); | |
122 | |
123 var blockFactory = new BlockFactory(templateElements, | |
124 directivePositions == null ? [] : directivePositions, _perf, _expando); | |
125 | |
126 assert(_perf.stopTimer(timerId) != false); | |
127 return blockFactory; | |
128 } | |
129 | |
130 static RegExp _MAPPING = new RegExp(r'^(\@|=\>\!|\=\>|\<\=\>|\&)\s*(.*)$'); | |
131 | |
132 createMappings(DirectiveRef ref) { | |
133 NgAnnotation annotation = ref.annotation; | |
134 if (annotation.map != null) annotation.map.forEach((attrName, mapping) { | |
135 Match match = _MAPPING.firstMatch(mapping); | |
136 if (match == null) { | |
137 throw "Unknown mapping '$mapping' for attribute '$attrName'."; | |
138 } | |
139 var mode = match[1]; | |
140 var dstPath = match[2]; | |
141 | |
142 String dstExpression = dstPath.isEmpty ? attrName : dstPath; | |
143 Expression dstPathFn = _parser(dstExpression); | |
144 if (!dstPathFn.isAssignable) { | |
145 throw "Expression '$dstPath' is not assignable in mapping '$mapping' for
attribute '$attrName'."; | |
146 } | |
147 ApplyMapping mappingFn; | |
148 switch (mode) { | |
149 case '@': | |
150 mappingFn = (NodeAttrs attrs, Scope scope, Object controller, FilterMa
p filters, notify()) { | |
151 attrs.observe(attrName, (value) { | |
152 dstPathFn.assign(controller, value); | |
153 notify(); | |
154 }); | |
155 }; | |
156 break; | |
157 case '<=>': | |
158 mappingFn = (NodeAttrs attrs, Scope scope, Object controller, FilterMa
p filters, notify()) { | |
159 if (attrs[attrName] == null) return notify(); | |
160 String expression = attrs[attrName]; | |
161 Expression expressionFn = _parser(expression); | |
162 var blockOutbound = false; | |
163 var blockInbound = false; | |
164 scope.watch( | |
165 expression, | |
166 (inboundValue, _) { | |
167 if (!blockInbound) { | |
168 blockOutbound = true; | |
169 scope.rootScope.runAsync(() => blockOutbound = false); | |
170 var value = dstPathFn.assign(controller, inboundValue); | |
171 notify(); | |
172 return value; | |
173 } | |
174 }, | |
175 filters: filters | |
176 ); | |
177 if (expressionFn.isAssignable) { | |
178 scope.watch( | |
179 dstExpression, | |
180 (outboundValue, _) { | |
181 if (!blockOutbound) { | |
182 blockInbound = true; | |
183 scope.rootScope.runAsync(() => blockInbound = false); | |
184 expressionFn.assign(scope.context, outboundValue); | |
185 notify(); | |
186 } | |
187 }, | |
188 context: controller, | |
189 filters: filters | |
190 ); | |
191 } | |
192 }; | |
193 break; | |
194 case '=>': | |
195 mappingFn = (NodeAttrs attrs, Scope scope, Object controller, FilterMa
p filters, notify()) { | |
196 if (attrs[attrName] == null) return notify(); | |
197 Expression attrExprFn = _parser(attrs[attrName]); | |
198 var shadowValue = null; | |
199 scope.watch(attrs[attrName], | |
200 (v, _) { | |
201 dstPathFn.assign(controller, shadowValue = v); | |
202 notify(); | |
203 }, | |
204 filters: filters); | |
205 }; | |
206 break; | |
207 case '=>!': | |
208 mappingFn = (NodeAttrs attrs, Scope scope, Object controller, FilterMa
p filters, notify()) { | |
209 if (attrs[attrName] == null) return notify(); | |
210 Expression attrExprFn = _parser(attrs[attrName]); | |
211 var watch; | |
212 watch = scope.watch( | |
213 attrs[attrName], | |
214 (value, _) { | |
215 if (dstPathFn.assign(controller, value) != null) { | |
216 watch.remove(); | |
217 } | |
218 }, | |
219 filters: filters); | |
220 notify(); | |
221 }; | |
222 break; | |
223 case '&': | |
224 mappingFn = (NodeAttrs attrs, Scope scope, Object dst, FilterMap filte
rs, notify()) { | |
225 dstPathFn.assign(dst, _parser(attrs[attrName]).bind(scope.context, S
copeLocals.wrapper)); | |
226 notify(); | |
227 }; | |
228 break; | |
229 } | |
230 ref.mappings.add(mappingFn); | |
231 }); | |
232 } | |
233 } | 5 } |
234 | |
OLD | NEW |