1
- export default function Adapter ( $rootScope , $parse , $attr , viewport , buffer , adjustBuffer , element ) {
2
- const viewportScope = viewport . scope ( ) || $rootScope ;
3
- let disabled = false ;
4
- let self = this ;
5
-
6
- createValueInjector ( 'adapter' ) ( self ) ;
7
- let topVisibleInjector = createValueInjector ( 'topVisible' ) ;
8
- let topVisibleElementInjector = createValueInjector ( 'topVisibleElement' ) ;
9
- let topVisibleScopeInjector = createValueInjector ( 'topVisibleScope' ) ;
10
- let isLoadingInjector = createValueInjector ( 'isLoading' ) ;
11
-
12
- // Adapter API definition
13
-
14
- Object . defineProperty ( this , 'disabled' , {
15
- get : ( ) => disabled ,
16
- set : ( value ) => ( ! ( disabled = value ) ) ? adjustBuffer ( ) : null
17
- } ) ;
18
-
19
- this . isLoading = false ;
20
- this . isBOF = ( ) => buffer . bof ;
21
- this . isEOF = ( ) => buffer . eof ;
22
- this . isEmpty = ( ) => ! buffer . length ;
23
-
24
- this . applyUpdates = ( arg1 , arg2 ) => {
1
+ function findCtrl ( scope , ctrl ) {
2
+ if ( ! scope ) {
3
+ return ;
4
+ }
5
+ if ( scope . hasOwnProperty ( ctrl ) && Object . getPrototypeOf ( scope [ ctrl ] ) . constructor . hasOwnProperty ( '$inject' ) ) {
6
+ return scope [ ctrl ] ;
7
+ }
8
+ return findCtrl ( scope . $parent , ctrl ) ;
9
+ }
10
+
11
+ function assignAttr ( attr , scope , element ) {
12
+ if ( ! attr || ! ( attr = attr . replace ( / ^ \s + | \s + $ / gm, '' ) ) ) {
13
+ return ;
14
+ }
15
+
16
+ let onSyntax = attr . match ( / ^ ( .+ ) ( \s + o n \s + ) ( .+ ) ? / ) ;
17
+ let asSyntax = attr . match ( / ^ ( [ ^ . ] + ) \. ( .+ ) ? / ) ;
18
+
19
+ if ( onSyntax && onSyntax . length === 4 ) { // controller on (backward compatibility), deprecated since v1.6.1
20
+ window . console . warn ( 'Angular ui-scroll adapter assignment warning. "Controller On" syntax has been deprecated since ui-scroll v1.6.1.' ) ;
21
+ let ctrl = onSyntax [ 3 ] ;
22
+ let tail = onSyntax [ 1 ] ;
23
+ let candidate = element ;
24
+ while ( candidate . length ) {
25
+ let candidateScope = candidate . scope ( ) ; // doesn't work when debugInfoEnabled flag = true
26
+ let candidateName = ( candidate . attr ( 'ng-controller' ) || '' ) . match ( / ( \w (?: \w | \d ) * ) (?: \s + a s \s + ( \w (?: \w | \d ) * ) ) ? / ) ;
27
+ if ( candidateName && candidateName [ 1 ] === ctrl ) {
28
+ return {
29
+ target : candidateScope ,
30
+ source : tail
31
+ } ;
32
+ }
33
+ candidate = candidate . parent ( ) ;
34
+ }
35
+ throw new Error ( 'Angular ui-scroll adapter assignment error. Failed to locate target controller "' + ctrl + '" to inject "' + tail + '"' ) ;
36
+ }
37
+ else if ( asSyntax && asSyntax . length === 3 ) { // controller as
38
+ let ctrl = asSyntax [ 1 ] ;
39
+ let tail = asSyntax [ 2 ] ;
40
+ let foundCtrl = findCtrl ( scope , ctrl ) ;
41
+ if ( foundCtrl ) {
42
+ return {
43
+ target : foundCtrl ,
44
+ source : tail
45
+ } ;
46
+ }
47
+ }
48
+
49
+ return {
50
+ target : scope ,
51
+ source : attr
52
+ } ;
53
+ }
54
+
55
+ class Adapter {
56
+
57
+ constructor ( viewport , buffer , adjustBuffer , reload , $attr , $parse , element ) {
58
+ this . viewport = viewport ;
59
+ this . buffer = buffer ;
60
+ this . adjustBuffer = adjustBuffer ;
61
+ this . reload = reload ;
62
+
63
+ this . publicContext = { } ;
64
+ this . assignAdapter ( $attr , $parse , element ) ;
65
+ this . generatePublicContext ( $attr , $parse , element ) ;
66
+
67
+ this . isLoading = false ;
68
+ this . disabled = false ;
69
+ }
70
+
71
+ assignAdapter ( $attr , $parse , element ) {
72
+ let data = assignAttr ( $attr . adapter , this . viewport . getScope ( ) , element ) ;
73
+
74
+ if ( data ) {
75
+ try {
76
+ $parse ( data . source ) . assign ( data . target , { } ) ;
77
+ let adapterOnScope = $parse ( data . source ) ( data . target ) ;
78
+
79
+ angular . extend ( adapterOnScope , this . publicContext ) ;
80
+ this . publicContext = adapterOnScope ;
81
+ }
82
+ catch ( error ) {
83
+ error . message = `Angular ui-scroll Adapter assignment exception.\n` +
84
+ `Can't parse "${ $attr . adapter } " expression.\n` +
85
+ error . message ;
86
+ throw error ;
87
+ }
88
+ }
89
+ }
90
+
91
+ generatePublicContext ( $attr , $parse , element ) {
92
+ // these methods will be accessible out of ui-scroll via user defined adapter
93
+ const publicMethods = [ 'reload' , 'applyUpdates' , 'append' , 'prepend' , 'isBOF' , 'isEOF' , 'isEmpty' ] ;
94
+ for ( let i = publicMethods . length - 1 ; i >= 0 ; i -- ) {
95
+ this . publicContext [ publicMethods [ i ] ] = this [ publicMethods [ i ] ] . bind ( this ) ;
96
+ }
97
+
98
+ // these read-only props will be accessible out of ui-scroll via user defined adapter
99
+ const publicProps = [ 'isLoading' , 'topVisible' , 'topVisibleElement' , 'topVisibleScope' ] ;
100
+ for ( let i = publicProps . length - 1 ; i >= 0 ; i -- ) {
101
+ let property , assignProp ;
102
+ let data = assignAttr ( $attr [ publicProps [ i ] ] , this . viewport . getScope ( ) , element ) ;
103
+ if ( data ) {
104
+ assignProp = $parse ( data . source ) . assign ;
105
+ }
106
+ Object . defineProperty ( this , publicProps [ i ] , {
107
+ get : ( ) => property ,
108
+ set : ( value ) => {
109
+ property = value ;
110
+ if ( assignProp ) {
111
+ assignProp ( data . target , value ) ;
112
+ }
113
+ this . publicContext [ publicProps [ i ] ] = value ;
114
+ }
115
+ } ) ;
116
+ }
117
+
118
+ // non-read-only public property
119
+ Object . defineProperty ( this . publicContext , 'disabled' , {
120
+ get : ( ) => this . disabled ,
121
+ set : ( value ) => ( ! ( this . disabled = value ) ) ? this . adjustBuffer ( ) : null
122
+ } ) ;
123
+ }
124
+
125
+ loading ( value ) {
126
+ this [ 'isLoading' ] = value ;
127
+ }
128
+
129
+ isBOF ( ) {
130
+ return this . buffer . bof ;
131
+ }
132
+
133
+ isEOF ( ) {
134
+ return this . buffer . eof ;
135
+ }
136
+
137
+ isEmpty ( ) {
138
+ return ! this . buffer . length ;
139
+ }
140
+
141
+ applyUpdates ( arg1 , arg2 ) {
25
142
if ( angular . isFunction ( arg1 ) ) {
26
143
// arg1 is the updater function, arg2 is ignored
27
- buffer . slice ( 0 ) . forEach ( ( wrapper ) => {
144
+ this . buffer . slice ( 0 ) . forEach ( ( wrapper ) => {
28
145
// we need to do it on the buffer clone, because buffer content
29
146
// may change as we iterate through
30
- applyUpdate ( wrapper , arg1 ( wrapper . item , wrapper . scope , wrapper . element ) ) ;
147
+ this . applyUpdate ( wrapper , arg1 ( wrapper . item , wrapper . scope , wrapper . element ) ) ;
31
148
} ) ;
32
149
} else {
33
150
// arg1 is item index, arg2 is the newItems array
34
151
if ( arg1 % 1 !== 0 ) { // checking if it is an integer
35
152
throw new Error ( 'applyUpdates - ' + arg1 + ' is not a valid index' ) ;
36
153
}
37
154
38
- const index = arg1 - buffer . first ;
39
- if ( ( index >= 0 && index < buffer . length ) ) {
40
- applyUpdate ( buffer [ index ] , arg2 ) ;
155
+ const index = arg1 - this . buffer . first ;
156
+ if ( ( index >= 0 && index < this . buffer . length ) ) {
157
+ this . applyUpdate ( this . buffer [ index ] , arg2 ) ;
41
158
}
42
159
}
43
160
44
- adjustBuffer ( ) ;
45
- } ;
46
-
47
- this . append = ( newItems ) => {
48
- buffer . append ( newItems ) ;
49
- adjustBuffer ( ) ;
50
- } ;
161
+ this . adjustBuffer ( ) ;
162
+ }
51
163
52
- this . prepend = ( newItems ) => {
53
- buffer . prepend ( newItems ) ;
54
- adjustBuffer ( ) ;
55
- } ;
164
+ append ( newItems ) {
165
+ this . buffer . append ( newItems ) ;
166
+ this . adjustBuffer ( ) ;
167
+ }
56
168
57
- this . loading = ( value ) => {
58
- isLoadingInjector ( value ) ;
59
- } ;
169
+ prepend ( newItems ) {
170
+ this . buffer . prepend ( newItems ) ;
171
+ this . adjustBuffer ( ) ;
172
+ }
60
173
61
- this . calculateProperties = ( ) => {
174
+ calculateProperties ( ) {
62
175
let item , itemHeight , itemTop , isNewRow , rowTop = null ;
63
176
let topHeight = 0 ;
64
- for ( let i = 0 ; i < buffer . length ; i ++ ) {
65
- item = buffer [ i ] ;
177
+ for ( let i = 0 ; i < this . buffer . length ; i ++ ) {
178
+ item = this . buffer [ i ] ;
66
179
itemTop = item . element . offset ( ) . top ;
67
180
isNewRow = rowTop !== itemTop ;
68
181
rowTop = itemTop ;
69
182
if ( isNewRow ) {
70
183
itemHeight = item . element . outerHeight ( true ) ;
71
184
}
72
- if ( isNewRow && ( viewport . topDataPos ( ) + topHeight + itemHeight <= viewport . topVisiblePos ( ) ) ) {
185
+ if ( isNewRow && ( this . viewport . topDataPos ( ) + topHeight + itemHeight <= this . viewport . topVisiblePos ( ) ) ) {
73
186
topHeight += itemHeight ;
74
187
} else {
75
188
if ( isNewRow ) {
76
- topVisibleInjector ( item . item ) ;
77
- topVisibleElementInjector ( item . element ) ;
78
- topVisibleScopeInjector ( item . scope ) ;
189
+ this [ 'topVisible' ] = item . item ;
190
+ this [ 'topVisibleElement' ] = item . element ;
191
+ this [ 'topVisibleScope' ] = item . scope ;
79
192
}
80
193
break ;
81
194
}
82
195
}
83
- } ;
84
-
85
- // private function definitions
86
-
87
- function createValueInjector ( attribute ) {
88
- let expression = $attr [ attribute ] ;
89
- let scope = viewportScope ;
90
- let assign ;
91
- if ( expression ) {
92
- // it is ok to have relaxed validation for the first part of the 'on' expression.
93
- // additional validation will be done by the $parse service below
94
- let match = expression . match ( / ^ ( \S + ) (?: \s + o n \s + ( \w (?: \w | \d ) * ) ) ? / ) ;
95
- if ( ! match )
96
- throw new Error ( 'Expected injection expression in form of \'target\' or \'target on controller\' but got \'' + expression + '\'' ) ;
97
- let target = match [ 1 ] ;
98
- let onControllerName = match [ 2 ] ;
99
-
100
- let parseController = ( controllerName , on ) => {
101
- let candidate = element ;
102
- while ( candidate . length ) {
103
- let candidateScope = candidate . scope ( ) ;
104
- // ng-controller's "Controller As" parsing
105
- let candidateName = ( candidate . attr ( 'ng-controller' ) || '' ) . match ( / ( \w (?: \w | \d ) * ) (?: \s + a s \s + ( \w (?: \w | \d ) * ) ) ? / ) ;
106
- if ( candidateName && candidateName [ on ? 1 : 2 ] === controllerName ) {
107
- scope = candidateScope ;
108
- return true ;
109
- }
110
- // directive's/component's "Controller As" parsing
111
- if ( ! on && candidateScope && candidateScope . hasOwnProperty ( controllerName ) && Object . getPrototypeOf ( candidateScope [ controllerName ] ) . constructor . hasOwnProperty ( '$inject' ) ) {
112
- scope = candidateScope ;
113
- return true ;
114
- }
115
- candidate = candidate . parent ( ) ;
116
- }
117
- } ;
118
-
119
- if ( onControllerName ) { // 'on' syntax DOM parsing (adapter="adapter on ctrl")
120
- scope = null ;
121
- parseController ( onControllerName , true ) ;
122
- if ( ! scope ) {
123
- throw new Error ( 'Failed to locate target controller \'' + onControllerName + '\' to inject \'' + target + '\'' ) ;
124
- }
125
- }
126
- else { // try to parse DOM with 'Controller As' syntax (adapter="ctrl.adapter")
127
- let controllerAsName ;
128
- let dotIndex = target . indexOf ( '.' ) ;
129
- if ( dotIndex > 0 ) {
130
- controllerAsName = target . substr ( 0 , dotIndex ) ;
131
- parseController ( controllerAsName , false ) ;
132
- }
133
- }
134
-
135
- assign = $parse ( target ) . assign ;
136
- }
137
- return ( value ) => {
138
- if ( self !== value ) // just to avoid injecting adapter reference in the adapter itself. Kludgy, I know.
139
- self [ attribute ] = value ;
140
- if ( assign )
141
- assign ( scope , value ) ;
142
- } ;
143
196
}
144
197
145
- function applyUpdate ( wrapper , newItems ) {
198
+ applyUpdate ( wrapper , newItems ) {
146
199
if ( ! angular . isArray ( newItems ) ) {
147
200
return ;
148
201
}
149
202
150
203
let keepIt ;
151
- let pos = ( buffer . indexOf ( wrapper ) ) + 1 ;
204
+ let pos = ( this . buffer . indexOf ( wrapper ) ) + 1 ;
152
205
153
206
newItems . reverse ( ) . forEach ( ( newItem ) => {
154
207
if ( newItem === wrapper . item ) {
155
208
keepIt = true ;
156
209
pos -- ;
157
210
} else {
158
- buffer . insert ( pos , newItem ) ;
211
+ this . buffer . insert ( pos , newItem ) ;
159
212
}
160
213
} ) ;
161
214
@@ -164,4 +217,6 @@ export default function Adapter($rootScope, $parse, $attr, viewport, buffer, adj
164
217
}
165
218
}
166
219
167
- }
220
+ }
221
+
222
+ export default Adapter ;
0 commit comments