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 . isInTemplateContext ) {
91
+ parent = this . getTemplateContext ( ) ;
92
+ if ( keyInfo . isTemplateContext ) {
93
+ return { value : parent } ;
94
+ }
95
+ return { value : canReflect . getKeyValue ( parent . _context , attr ) } ;
96
+ }
97
+ return this . _read ( observeReader . reads ( attr ) , options , currentScopeOnly ) ;
98
+ } ,
99
+ _read : function ( keyReads , options , currentScopeOnly ) {
100
+ var currentScope = this , currentContext , undefinedObserves = [ ] , currentObserve , currentReads , setObserveDepth = - 1 , currentSetReads , currentSetObserve , readOptions = assign ( {
101
+ foundObservable : function ( observe , nameIndex ) {
102
+ currentObserve = observe ;
103
+ currentReads = keyReads . slice ( nameIndex ) ;
104
+ } ,
105
+ earlyExit : function ( parentValue , nameIndex ) {
106
+ if ( nameIndex > setObserveDepth || nameIndex === setObserveDepth && ( typeof parentValue === 'object' && keyReads [ nameIndex ] . key in parentValue ) ) {
107
+ currentSetObserve = currentObserve ;
108
+ currentSetReads = currentReads ;
109
+ setObserveDepth = nameIndex ;
110
+ }
111
+ }
112
+ } , options ) ;
113
+ while ( currentScope ) {
114
+ currentContext = currentScope . _context ;
115
+ if ( currentContext !== null && ( typeof currentContext === 'object' || typeof currentContext === 'function' ) ) {
116
+ var getObserves = Observation . trap ( ) ;
117
+ var data = observeReader . read ( currentContext , keyReads , readOptions ) ;
118
+ var observes = getObserves ( ) ;
119
+ if ( data . value !== undefined ) {
120
+ Observation . addAll ( observes ) ;
121
+ return {
122
+ scope : currentScope ,
123
+ rootObserve : currentObserve ,
124
+ value : data . value ,
125
+ reads : currentReads
126
+ } ;
127
+ } else {
128
+ undefinedObserves . push . apply ( undefinedObserves , observes ) ;
129
+ }
130
+ }
131
+ if ( currentScopeOnly ) {
132
+ currentScope = null ;
133
+ } else {
134
+ currentScope = currentScope . _parent ;
135
+ }
136
+ }
137
+ Observation . addAll ( undefinedObserves ) ;
138
+ return {
139
+ setRoot : currentSetObserve ,
140
+ reads : currentSetReads ,
141
+ value : undefined
142
+ } ;
143
+ } ,
144
+ get : function ( key , options ) {
145
+ options = assign ( { isArgument : true } , options ) ;
146
+ var res = this . read ( key , options ) ;
147
+ return res . value ;
148
+ } ,
149
+ peek : Observation . ignore ( function ( key , options ) {
150
+ return this . get ( key , options ) ;
151
+ } ) ,
152
+ peak : Observation . ignore ( function ( key , options ) {
153
+ return this . peek ( key , options ) ;
154
+ } ) ,
155
+ getScope : function ( tester ) {
156
+ var scope = this ;
157
+ while ( scope ) {
158
+ if ( tester ( scope ) ) {
159
+ return scope ;
160
+ }
161
+ scope = scope . _parent ;
162
+ }
163
+ } ,
164
+ getContext : function ( tester ) {
165
+ var res = this . getScope ( tester ) ;
166
+ return res && res . _context ;
167
+ } ,
168
+ getRefs : function ( ) {
169
+ return this . getTemplateContext ( ) ;
170
+ } ,
171
+ getTemplateContext : function ( ) {
172
+ var lastScope ;
173
+ var templateContext = this . getScope ( function ( scope ) {
174
+ lastScope = scope ;
175
+ return scope . _context instanceof TemplateContext ;
176
+ } ) ;
177
+ if ( ! templateContext ) {
178
+ templateContext = new Scope ( new TemplateContext ( ) ) ;
179
+ lastScope . _parent = templateContext ;
180
+ }
181
+ return templateContext ;
182
+ } ,
183
+ getRoot : function ( ) {
184
+ var cur = this , child = this ;
185
+ while ( cur . _parent ) {
186
+ child = cur ;
187
+ cur = cur . _parent ;
188
+ }
189
+ if ( cur . _context instanceof Scope . Refs ) {
190
+ cur = child ;
191
+ }
192
+ return cur . _context ;
193
+ } ,
194
+ set : function ( key , value , options ) {
195
+ options = options || { } ;
196
+ var keyInfo = Scope . keyInfo ( key ) , parent ;
197
+ if ( keyInfo . isCurrentContext ) {
198
+ return canReflect . setValue ( this . _context , value ) ;
199
+ } else if ( keyInfo . isInParentContext || keyInfo . isParentContext ) {
200
+ parent = this . _parent ;
201
+ while ( parent . _meta . notContext ) {
202
+ parent = parent . _parent ;
203
+ }
204
+ if ( keyInfo . isParentContext ) {
205
+ return canReflect . setValue ( parent . _context , value ) ;
206
+ }
207
+ return parent . set ( key . substr ( 3 ) || '.' , value , options ) ;
208
+ } else if ( keyInfo . isInTemplateContext ) {
209
+ parent = this . getTemplateContext ( ) ;
210
+ return canReflect . setKeyValue ( parent . _context , key , value ) ;
211
+ }
212
+ var dotIndex = key . lastIndexOf ( '.' ) , slashIndex = key . lastIndexOf ( '/' ) , contextPath , propName ;
213
+ if ( slashIndex > dotIndex ) {
214
+ contextPath = key . substring ( 0 , slashIndex ) ;
215
+ propName = key . substring ( slashIndex + 1 , key . length ) ;
216
+ } else {
217
+ if ( dotIndex !== - 1 ) {
218
+ contextPath = key . substring ( 0 , dotIndex ) ;
219
+ propName = key . substring ( dotIndex + 1 , key . length ) ;
220
+ } else {
221
+ contextPath = '.' ;
222
+ propName = key ;
223
+ }
224
+ }
225
+ var context = this . read ( contextPath , options ) . value ;
226
+ if ( context === undefined ) {
227
+ return ;
228
+ }
229
+ if ( ! canReflect . isObservableLike ( context ) && canReflect . isObservableLike ( context [ propName ] ) ) {
230
+ if ( canReflect . isMapLike ( context [ propName ] ) ) {
231
+ canLog . warn ( 'can-view-scope: Merging data into "' + propName + '" because its parent is non-observable' ) ;
232
+ canReflect . updateDeep ( context [ propName ] , value ) ;
233
+ } else if ( canReflect . isValueLike ( context [ propName ] ) ) {
234
+ canReflect . setValue ( context [ propName ] , value ) ;
235
+ } else {
236
+ observeReader . write ( context , propName , value , options ) ;
237
+ }
238
+ } else {
239
+ observeReader . write ( context , propName , value , options ) ;
240
+ }
241
+ } ,
242
+ attr : Observation . ignore ( function ( key , value , options ) {
243
+ canLog . warn ( 'can-view-scope::attr is deprecated, please use peek, get or set' ) ;
244
+ options = assign ( { isArgument : true } , options ) ;
245
+ if ( arguments . length === 2 ) {
246
+ return this . set ( key , value , options ) ;
247
+ } else {
248
+ return this . get ( key , options ) ;
249
+ }
250
+ } ) ,
251
+ computeData : function ( key , options ) {
252
+ return makeComputeData ( this , key , options ) ;
253
+ } ,
254
+ compute : function ( key , options ) {
255
+ return this . computeData ( key , options ) . compute ;
256
+ } ,
257
+ cloneFromRef : function ( ) {
258
+ var contexts = [ ] ;
259
+ var scope = this , context , parent ;
260
+ while ( scope ) {
261
+ context = scope . _context ;
262
+ if ( context instanceof Scope . Refs ) {
263
+ parent = scope . _parent ;
264
+ break ;
265
+ }
266
+ contexts . unshift ( context ) ;
267
+ scope = scope . _parent ;
268
+ }
269
+ if ( parent ) {
270
+ each ( contexts , function ( context ) {
271
+ parent = parent . add ( context ) ;
272
+ } ) ;
273
+ return parent ;
274
+ } else {
275
+ return this ;
276
+ }
277
+ }
278
+ } ) ;
279
+ function Options ( data , parent , meta ) {
280
+ if ( ! data . helpers && ! data . partials && ! data . tags ) {
281
+ data = { helpers : data } ;
282
+ }
283
+ Scope . call ( this , data , parent , meta ) ;
284
+ }
285
+ Options . prototype = new Scope ( ) ;
286
+ Options . prototype . constructor = Options ;
287
+ Scope . Options = Options ;
288
+ namespace . view = namespace . view || { } ;
289
+ module . exports = namespace . view . Scope = Scope ;
290
+ } ) ;
0 commit comments