1
+ /*[email protected] #can-view-scope*/
2
+ define ( [
3
+ 'require' ,
4
+ 'exports' ,
5
+ 'module' ,
6
+ 'can-stache-key' ,
7
+ 'can-observation' ,
8
+ './template-context' ,
9
+ './compute_data' ,
10
+ 'can-util/js/assign' ,
11
+ 'can-util/js/each' ,
12
+ 'can-namespace' ,
13
+ 'can-reflect' ,
14
+ 'can-log/dev'
15
+ ] , function ( require , exports , module ) {
16
+ var observeReader = require ( 'can-stache-key' ) ;
17
+ var Observation = require ( 'can-observation' ) ;
18
+ var TemplateContext = require ( './template-context' ) ;
19
+ var makeComputeData = require ( './compute_data' ) ;
20
+ var assign = require ( 'can-util/js/assign' ) ;
21
+ var each = require ( 'can-util/js/each' ) ;
22
+ var namespace = require ( 'can-namespace' ) ;
23
+ var canReflect = require ( 'can-reflect' ) ;
24
+ var canLog = require ( 'can-log/dev' ) ;
25
+ function Scope ( context , parent , meta ) {
26
+ this . _context = context ;
27
+ this . _parent = parent ;
28
+ this . _meta = meta || { } ;
29
+ this . __cache = { } ;
30
+ }
31
+ assign ( Scope , {
32
+ read : observeReader . read ,
33
+ Refs : TemplateContext ,
34
+ refsScope : function ( ) {
35
+ return new Scope ( new TemplateContext ( ) ) ;
36
+ } ,
37
+ keyInfo : function ( attr ) {
38
+ var info = { } ;
39
+ info . isDotSlash = attr . substr ( 0 , 2 ) === './' ;
40
+ info . isThisDot = attr . substr ( 0 , 5 ) === 'this.' ;
41
+ info . isThisAt = attr . substr ( 0 , 5 ) === 'this@' ;
42
+ info . isInCurrentContext = info . isDotSlash || info . isThisDot || info . isThisAt ;
43
+ info . isInParentContext = attr . substr ( 0 , 3 ) === '../' ;
44
+ info . isCurrentContext = attr === '.' || attr === 'this' ;
45
+ info . isParentContext = attr === '..' ;
46
+ info . isTemplateContext = attr === 'scope' ;
47
+ info . isInLegacyRefsScope = attr . substr ( 0 , 1 ) === '*' ;
48
+ info . isInTemplateContext = info . isTemplateContext || info . isInLegacyRefsScope || attr . substr ( 0 , 6 ) === 'scope.' ;
49
+ info . isContextBased = info . isInCurrentContext || info . isInParentContext || info . isCurrentContext || info . isParentContext || info . isInTemplateContext ;
50
+ return info ;
51
+ }
52
+ } ) ;
53
+ assign ( Scope . prototype , {
54
+ add : function ( context , meta ) {
55
+ if ( context !== this . _context ) {
56
+ return new this . constructor ( context , this , meta ) ;
57
+ } else {
58
+ return this ;
59
+ }
60
+ } ,
61
+ read : function ( attr , options ) {
62
+ if ( attr === '%root' ) {
63
+ return { value : this . getRoot ( ) } ;
64
+ }
65
+ if ( attr === '%scope' ) {
66
+ return { value : this } ;
67
+ }
68
+ if ( attr === './' ) {
69
+ attr = '.' ;
70
+ }
71
+ var keyInfo = Scope . keyInfo ( attr ) ;
72
+ if ( keyInfo . isContextBased && this . _meta . notContext ) {
73
+ return this . _parent . read ( attr , options ) ;
74
+ }
75
+ var currentScopeOnly , parent ;
76
+ if ( keyInfo . isInCurrentContext ) {
77
+ currentScopeOnly = true ;
78
+ attr = keyInfo . isDotSlash ? attr . substr ( 2 ) : attr . substr ( 5 ) ;
79
+ } else if ( keyInfo . isInParentContext || keyInfo . isParentContext ) {
80
+ parent = this . _parent ;
81
+ while ( parent . _meta . notContext ) {
82
+ parent = parent . _parent ;
83
+ }
84
+ if ( keyInfo . isParentContext ) {
85
+ return observeReader . read ( parent . _context , [ ] , options ) ;
86
+ }
87
+ return parent . read ( attr . substr ( 3 ) || '.' , options ) ;
88
+ } else if ( keyInfo . isCurrentContext ) {
89
+ return observeReader . read ( this . _context , [ ] , options ) ;
90
+ } else if ( keyInfo . isTemplateContext ) {
91
+ return { value : this . getTemplateContext ( ) } ;
92
+ }
93
+ var keyReads = observeReader . reads ( attr ) ;
94
+ if ( keyInfo . isInTemplateContext ) {
95
+ if ( keyReads [ 0 ] . key === 'scope' ) {
96
+ keyReads = keyReads . slice ( 1 ) ;
97
+ }
98
+ return this . getTemplateContext ( ) . _read ( keyReads , options , true ) ;
99
+ } else {
100
+ return this . _read ( keyReads , options , currentScopeOnly ) ;
101
+ }
102
+ } ,
103
+ _read : function ( keyReads , options , currentScopeOnly ) {
104
+ var currentScope = this , currentContext , undefinedObserves = [ ] , currentObserve , currentReads , setObserveDepth = - 1 , currentSetReads , currentSetObserve , readOptions = assign ( {
105
+ foundObservable : function ( observe , nameIndex ) {
106
+ currentObserve = observe ;
107
+ currentReads = keyReads . slice ( nameIndex ) ;
108
+ } ,
109
+ earlyExit : function ( parentValue , nameIndex ) {
110
+ if ( nameIndex > setObserveDepth || nameIndex === setObserveDepth && ( typeof parentValue === 'object' && keyReads [ nameIndex ] . key in parentValue ) ) {
111
+ currentSetObserve = currentObserve ;
112
+ currentSetReads = currentReads ;
113
+ setObserveDepth = nameIndex ;
114
+ }
115
+ }
116
+ } , options ) ;
117
+ while ( currentScope ) {
118
+ currentContext = currentScope . _context ;
119
+ if ( currentContext !== null && ( typeof currentContext === 'object' || typeof currentContext === 'function' ) ) {
120
+ var getObserves = Observation . trap ( ) ;
121
+ var data = observeReader . read ( currentContext , keyReads , readOptions ) ;
122
+ var observes = getObserves ( ) ;
123
+ if ( data . value !== undefined ) {
124
+ Observation . addAll ( observes ) ;
125
+ return {
126
+ scope : currentScope ,
127
+ rootObserve : currentObserve ,
128
+ value : data . value ,
129
+ reads : currentReads
130
+ } ;
131
+ } else {
132
+ undefinedObserves . push . apply ( undefinedObserves , observes ) ;
133
+ }
134
+ }
135
+ if ( currentScopeOnly ) {
136
+ currentScope = null ;
137
+ } else {
138
+ currentScope = currentScope . _parent ;
139
+ }
140
+ }
141
+ Observation . addAll ( undefinedObserves ) ;
142
+ return {
143
+ setRoot : currentSetObserve ,
144
+ reads : currentSetReads ,
145
+ value : undefined
146
+ } ;
147
+ } ,
148
+ get : function ( key , options ) {
149
+ options = assign ( { isArgument : true } , options ) ;
150
+ var res = this . read ( key , options ) ;
151
+ return res . value ;
152
+ } ,
153
+ peek : Observation . ignore ( function ( key , options ) {
154
+ return this . get ( key , options ) ;
155
+ } ) ,
156
+ peak : Observation . ignore ( function ( key , options ) {
157
+ return this . peek ( key , options ) ;
158
+ } ) ,
159
+ getScope : function ( tester ) {
160
+ var scope = this ;
161
+ while ( scope ) {
162
+ if ( tester ( scope ) ) {
163
+ return scope ;
164
+ }
165
+ scope = scope . _parent ;
166
+ }
167
+ } ,
168
+ getContext : function ( tester ) {
169
+ var res = this . getScope ( tester ) ;
170
+ return res && res . _context ;
171
+ } ,
172
+ getRefs : function ( ) {
173
+ return this . getTemplateContext ( ) ;
174
+ } ,
175
+ getTemplateContext : function ( ) {
176
+ var lastScope ;
177
+ var templateContext = this . getScope ( function ( scope ) {
178
+ lastScope = scope ;
179
+ return scope . _context instanceof TemplateContext ;
180
+ } ) ;
181
+ if ( ! templateContext ) {
182
+ templateContext = new Scope ( new TemplateContext ( ) ) ;
183
+ lastScope . _parent = templateContext ;
184
+ }
185
+ return templateContext ;
186
+ } ,
187
+ getRoot : function ( ) {
188
+ var cur = this , child = this ;
189
+ while ( cur . _parent ) {
190
+ child = cur ;
191
+ cur = cur . _parent ;
192
+ }
193
+ if ( cur . _context instanceof Scope . Refs ) {
194
+ cur = child ;
195
+ }
196
+ return cur . _context ;
197
+ } ,
198
+ set : function ( key , value , options ) {
199
+ options = options || { } ;
200
+ var keyInfo = Scope . keyInfo ( key ) , parent ;
201
+ if ( keyInfo . isCurrentContext ) {
202
+ return canReflect . setValue ( this . _context , value ) ;
203
+ } else if ( keyInfo . isInParentContext || keyInfo . isParentContext ) {
204
+ parent = this . _parent ;
205
+ while ( parent . _meta . notContext ) {
206
+ parent = parent . _parent ;
207
+ }
208
+ if ( keyInfo . isParentContext ) {
209
+ return canReflect . setValue ( parent . _context , value ) ;
210
+ }
211
+ return parent . set ( key . substr ( 3 ) || '.' , value , options ) ;
212
+ } else if ( keyInfo . isInTemplateContext ) {
213
+ parent = this . getTemplateContext ( ) ;
214
+ return canReflect . setKeyValue ( parent . _context , key , value ) ;
215
+ }
216
+ var dotIndex = key . lastIndexOf ( '.' ) , slashIndex = key . lastIndexOf ( '/' ) , contextPath , propName ;
217
+ if ( slashIndex > dotIndex ) {
218
+ contextPath = key . substring ( 0 , slashIndex ) ;
219
+ propName = key . substring ( slashIndex + 1 , key . length ) ;
220
+ } else {
221
+ if ( dotIndex !== - 1 ) {
222
+ contextPath = key . substring ( 0 , dotIndex ) ;
223
+ propName = key . substring ( dotIndex + 1 , key . length ) ;
224
+ } else {
225
+ contextPath = '.' ;
226
+ propName = key ;
227
+ }
228
+ }
229
+ var context = this . read ( contextPath , options ) . value ;
230
+ if ( context === undefined ) {
231
+ return ;
232
+ }
233
+ if ( ! canReflect . isObservableLike ( context ) && canReflect . isObservableLike ( context [ propName ] ) ) {
234
+ if ( canReflect . isMapLike ( context [ propName ] ) ) {
235
+ canLog . warn ( 'can-view-scope: Merging data into "' + propName + '" because its parent is non-observable' ) ;
236
+ canReflect . updateDeep ( context [ propName ] , value ) ;
237
+ } else if ( canReflect . isValueLike ( context [ propName ] ) ) {
238
+ canReflect . setValue ( context [ propName ] , value ) ;
239
+ } else {
240
+ observeReader . write ( context , propName , value , options ) ;
241
+ }
242
+ } else {
243
+ observeReader . write ( context , propName , value , options ) ;
244
+ }
245
+ } ,
246
+ attr : Observation . ignore ( function ( key , value , options ) {
247
+ canLog . warn ( 'can-view-scope::attr is deprecated, please use peek, get or set' ) ;
248
+ options = assign ( { isArgument : true } , options ) ;
249
+ if ( arguments . length === 2 ) {
250
+ return this . set ( key , value , options ) ;
251
+ } else {
252
+ return this . get ( key , options ) ;
253
+ }
254
+ } ) ,
255
+ computeData : function ( key , options ) {
256
+ return makeComputeData ( this , key , options ) ;
257
+ } ,
258
+ compute : function ( key , options ) {
259
+ return this . computeData ( key , options ) . compute ;
260
+ } ,
261
+ cloneFromRef : function ( ) {
262
+ var contexts = [ ] ;
263
+ var scope = this , context , parent ;
264
+ while ( scope ) {
265
+ context = scope . _context ;
266
+ if ( context instanceof Scope . Refs ) {
267
+ parent = scope . _parent ;
268
+ break ;
269
+ }
270
+ contexts . unshift ( context ) ;
271
+ scope = scope . _parent ;
272
+ }
273
+ if ( parent ) {
274
+ each ( contexts , function ( context ) {
275
+ parent = parent . add ( context ) ;
276
+ } ) ;
277
+ return parent ;
278
+ } else {
279
+ return this ;
280
+ }
281
+ }
282
+ } ) ;
283
+ function Options ( data , parent , meta ) {
284
+ if ( ! data . helpers && ! data . partials && ! data . tags ) {
285
+ data = { helpers : data } ;
286
+ }
287
+ Scope . call ( this , data , parent , meta ) ;
288
+ }
289
+ Options . prototype = new Scope ( ) ;
290
+ Options . prototype . constructor = Options ;
291
+ Scope . Options = Options ;
292
+ namespace . view = namespace . view || { } ;
293
+ module . exports = namespace . view . Scope = Scope ;
294
+ } ) ;
0 commit comments