@@ -69,10 +69,12 @@ angular.module('ui.scroll', [])
69
69
wrapper .scope .$destroy ()
70
70
]
71
71
72
- Buffer = (itemName , $scope , linker , datasource )->
72
+ Buffer = (itemName , $scope , linker , datasource , bufferSize )->
73
73
74
74
buffer = Object .create Array .prototype
75
75
76
+ buffer .size = bufferSize
77
+
76
78
# inserts wrapped element in the buffer
77
79
# the first argument is either operation keyword (see below) or a number for operation 'insert'
78
80
# for insert the number is the index for the buffer element the new one have to be inserted after
@@ -108,20 +110,20 @@ angular.module('ui.scroll', [])
108
110
buffer .splice buffer .indexOf (arg1), 1
109
111
removeElementAnimated arg1
110
112
111
- # clears the buffer
112
- buffer .clear = ->
113
- buffer .remove (0 , buffer .length )
113
+ reset = ->
114
114
buffer .eof = false
115
115
buffer .bof = false
116
116
buffer .first = 1
117
117
buffer .next = 1
118
118
119
- buffer .clear ()
120
-
121
- buffer .minIndex = -> datasource .minIndex || 0
122
-
123
- buffer .maxIndex = -> datasource .maxIndex || 0
119
+ # clears the buffer
120
+ buffer .clear = ->
121
+ buffer .remove (0 , buffer .length )
122
+ reset ()
124
123
124
+ reset ()
125
+ buffer .minIndex = -> datasource .minIndex || 1
126
+ buffer .maxIndex = -> datasource .maxIndex || 1
125
127
buffer
126
128
127
129
Padding = (template ) ->
@@ -146,31 +148,24 @@ angular.module('ui.scroll', [])
146
148
viewport .css ({' overflow-y' : ' auto' , ' display' : ' block' })
147
149
148
150
topPadding = null
149
-
150
151
bottomPadding = null
151
152
152
153
bufferPadding = -> viewport .outerHeight () * Math .max (0.1 , + padding || 0.1 ) # some extra space to initiate preload
153
154
154
155
viewport .createPaddingElements = (template ) ->
155
-
156
156
topPadding = new Padding template
157
- element .before topPadding
158
- # viewport.topPadding = -> topPadding.height.apply(topPadding, arguments)
159
-
160
157
bottomPadding = new Padding template
158
+ element .before topPadding
161
159
element .after bottomPadding
162
- # viewport.bottomPadding = -> bottomPadding.height.apply(bottomPadding, arguments)
163
160
164
161
viewport .bottomDataPos = ->
165
162
(viewport[0 ].scrollHeight ? viewport[0 ].document .documentElement .scrollHeight ) - bottomPadding .height ()
166
163
167
164
viewport .topDataPos = -> topPadding .height ()
168
165
169
- viewport .bottomVisiblePos = ->
170
- viewport .scrollTop () + viewport .outerHeight ()
166
+ viewport .bottomVisiblePos = -> viewport .scrollTop () + viewport .outerHeight ()
171
167
172
- viewport .topVisiblePos = ->
173
- viewport .scrollTop ()
168
+ viewport .topVisiblePos = -> viewport .scrollTop ()
174
169
175
170
viewport .insertElement = (e , sibling ) -> insertElement (e, sibling || topPadding)
176
171
@@ -181,78 +176,64 @@ angular.module('ui.scroll', [])
181
176
182
177
viewport .clipBottom = ->
183
178
# clip the invisible items off the bottom
184
- bottomHeight = 0
185
179
overage = 0
186
-
180
+ calculateAverageItemHeight ()
181
+ overageBottom = viewport .outerHeight () + viewport .averageItemHeight * (buffer .size )
187
182
for i in [buffer .length - 1 .. 0 ]
188
183
item = buffer[i]
189
- itemTop = item .element .offset ().top
190
- newRow = rowTop isnt itemTop
191
- rowTop = itemTop
192
- itemHeight = item .element .outerHeight (true ) if newRow
193
- if (viewport .bottomDataPos () - bottomHeight - itemHeight > viewport .bottomVisiblePos () + bufferPadding ())
194
- bottomHeight += itemHeight if newRow
195
- overage++
196
- buffer .eof = false
197
- else
198
- break if newRow
184
+ if item .element .offset ().top > overageBottom
199
185
overage++
200
-
186
+ else break
201
187
if overage > 0
202
- # viewport.bottomPadding(viewport.bottomPadding() + bottomHeight)
203
188
buffer .remove (buffer .length - overage, buffer .length )
204
189
buffer .next -= overage
190
+ viewport .adjustPadding ()
205
191
206
192
viewport .shouldLoadTop = ->
207
193
! buffer .bof && (viewport .topDataPos () > viewport .topVisiblePos () - bufferPadding ())
208
194
209
195
viewport .clipTop = ->
210
196
# clip the invisible items off the top
211
- topHeight = 0
212
197
overage = 0
198
+ heightIncrement = 0
199
+ calculateAverageItemHeight ()
200
+ overageTop = (- 1 ) * viewport .averageItemHeight * buffer .size
213
201
for item in buffer
214
- itemTop = item .element .offset ().top
215
- newRow = rowTop isnt itemTop
216
- rowTop = itemTop
217
- itemHeight = item .element .outerHeight (true ) if newRow
218
- if (viewport .topDataPos () + topHeight + itemHeight < viewport .topVisiblePos () - bufferPadding ())
219
- topHeight += itemHeight if newRow
220
- overage++
221
- buffer .bof = false
222
- else
223
- break if newRow
202
+ if item .element .offset ().top < overageTop
203
+ heightIncrement += item .element .outerHeight ()
224
204
overage++
205
+ else break
225
206
if overage > 0
226
- # viewport.topPadding(viewport.topPadding() + topHeight)
227
207
buffer .remove (0 , overage)
228
208
buffer .first += overage
229
-
230
- viewport .adjustPadding = ( heightIncrement ) ->
231
- if ( buffer . length > 0 )
232
- elementHeight = (buffer[ buffer . length - 1 ]. element . offset (). top +
233
- buffer[ buffer . length - 1 ]. element . outerHeight ( true ) -
234
- buffer[ 0 ]. element . offset (). top ) / buffer .length
235
- topPaddingHeight = (buffer . first - buffer . minIndex ()) * elementHeight
236
- # if ! buffer.bof
237
- # topPaddingHeight += bufferPadding()
238
- topPadding . height topPaddingHeight
239
- bottomPaddingHeight = (buffer . maxIndex ( ) - buffer . next ) * elementHeight
240
- if ! buffer .eof
241
- bottomPaddingHeight += bufferPadding ()
242
- bottomPadding .height bottomPaddingHeight
243
- console . log " top= #{ topPaddingHeight } bottom= #{ bottomPaddingHeight } "
244
- if (heightIncrement)
245
- # adjust padding to prevent it from visually pushing everything down
246
- if topPadding . height () >= heightIncrement
247
- # if possible, reduce topPadding
248
- topPadding . height ( topPadding . height () - heightIncrement)
249
- else
250
- # if not, increment scrollTop
251
- viewport . scrollTop ( viewport . scrollTop () + heightIncrement)
252
-
209
+ viewport . adjustPadding ()
210
+ viewport .adjustScrollTop { height : heightIncrement, clipTop : true }
211
+
212
+ calculateAverageItemHeight = () ->
213
+ viewport . averageItemHeight = 0
214
+ return if not buffer .length
215
+ viewport . averageItemHeight = (buffer[ buffer . length - 1 ]. element . offset (). top +
216
+ buffer[ buffer . length - 1 ]. element . outerHeight ( true ) -
217
+ buffer[ 0 ]. element . offset (). top ) / buffer . length
218
+
219
+ viewport . adjustPadding = () ->
220
+ return if not buffer .length
221
+ calculateAverageItemHeight ()
222
+ topPadding .height ( buffer . first - buffer . minIndex ()) * viewport . averageItemHeight
223
+ bottomPadding . height ( buffer . maxIndex () - buffer . next + 1 ) * viewport . averageItemHeight
224
+
225
+ viewport . adjustScrollTop = ( options ) ->
226
+ return if not options or not options . height
227
+ # edge case 1, scroll up from the very top position
228
+ if options . prepend and viewport . scrollTop () < options . height
229
+ viewport . scrollTop viewport . scrollTop () + options . height
230
+ # edge case 2, scroll down from the very bottom position
231
+ if options . clipTop and options . height - bottomPadding . height () > 0
232
+ viewport . scrollTop viewport . scrollTop () + options . height - bottomPadding . height ()
253
233
254
234
viewport
255
235
236
+
256
237
Adapter = ($attr , viewport , buffer , adjustBuffer ) ->
257
238
258
239
this .isLoading = false
@@ -299,25 +280,10 @@ angular.module('ui.scroll', [])
299
280
buffer .insert ' prepend' , item
300
281
adjustBuffer ()
301
282
302
- if $attr .topVisible
303
- setTopVisible = $parse ($attr .topVisible ).assign
304
- else
305
- setTopVisible = ->
306
-
307
- if $attr .topVisibleElement
308
- setTopVisibleElement = $parse ($attr .topVisibleElement ).assign
309
- else
310
- setTopVisibleElement = ->
311
-
312
- if $attr .topVisibleScope
313
- setTopVisibleScope = $parse ($attr .topVisibleScope ).assign
314
- else
315
- setTopVisibleScope = ->
316
-
317
- if $attr .isLoading
318
- setIsLoading = $parse ($attr .isLoading ).assign
319
- else
320
- setIsLoading = ->
283
+ setTopVisible = if $attr .topVisible then $parse ($attr .topVisible ).assign else ->
284
+ setTopVisibleElement = if $attr .topVisibleElement then $parse ($attr .topVisibleElement ).assign else ->
285
+ setTopVisibleScope = if $attr .topVisibleScope then $parse ($attr .topVisibleScope ).assign else ->
286
+ setIsLoading = if $attr .isLoading then $parse ($attr .isLoading ).assign else ->
321
287
322
288
this .loading = (value ) ->
323
289
this .isLoading = value
@@ -375,7 +341,7 @@ angular.module('ui.scroll', [])
375
341
376
342
pending = []
377
343
378
- buffer = new Buffer (itemName, $scope, linker, datasource)
344
+ buffer = new Buffer (itemName, $scope, linker, datasource, bufferSize )
379
345
380
346
viewport = new Viewport (buffer, element, controllers, $attr .padding )
381
347
@@ -411,8 +377,6 @@ angular.module('ui.scroll', [])
411
377
reload = ->
412
378
dismissPendingRequests ()
413
379
buffer .clear ()
414
- # viewport.topPadding(0)
415
- # viewport.bottomPadding(0)
416
380
adjustBuffer ridActual
417
381
418
382
adapter .reload = reload
@@ -444,51 +408,35 @@ angular.module('ui.scroll', [])
444
408
toBePrepended = []
445
409
toBeRemoved = []
446
410
447
- bottomPos = viewport .bottomDataPos ()
411
+ getPreSibling = (i ) -> if i > 0 then buffer[i- 1 ].element else undefined
412
+
448
413
for wrapper, i in buffer
449
414
switch wrapper .op
450
415
when ' prepend' then toBePrepended .unshift wrapper
451
416
when ' append'
452
- if (i == 0 ) # the first item in buffer is to be appended, therefore the buffer was empty
453
- keepFetching = insertWrapperContent (wrapper) || keepFetching
454
- else
455
- keepFetching = insertWrapperContent (wrapper, buffer[i- 1 ].element ) || keepFetching
417
+ keepFetching = insertWrapperContent (wrapper, getPreSibling (i)) || keepFetching
456
418
wrapper .op = ' none'
457
419
when ' insert'
458
- if (i == 0 )
459
- promises = promises .concat (viewport .insertElementAnimated wrapper .element )
460
- else
461
- promises = promises .concat (viewport .insertElementAnimated wrapper .element , buffer[i- 1 ].element )
420
+ promises = promises .concat (viewport .insertElementAnimated wrapper .element , getPreSibling (i))
462
421
wrapper .op = ' none'
463
422
when ' remove' then toBeRemoved .push wrapper
464
423
465
424
for wrapper in toBeRemoved
466
425
promises = promises .concat (buffer .remove wrapper)
467
426
468
- # for anything other than prepend adjust the bottomPadding height
469
- # viewport.bottomPadding(Math.max(0,viewport.bottomPadding() - (viewport.bottomDataPos() - bottomPos)))
470
-
471
427
if toBePrepended .length
472
- bottomPos = viewport . bottomDataPos ()
428
+ adjustPaddingSettings = { height : 0 , prepend : true }
473
429
for wrapper in toBePrepended
474
430
keepFetching = insertWrapperContent (wrapper) || keepFetching
475
431
wrapper .op = ' none'
476
-
477
- viewport .adjustPadding viewport .bottomDataPos () - bottomPos
478
-
479
- ###
480
- # adjust padding to prevent it from visually pushing everything down
481
- if viewport.topPadding() >= heightIncrement
482
- # if possible, reduce topPadding
483
- viewport.topPadding(viewport.topPadding() - heightIncrement)
484
- else
485
- # if not, increment scrollTop
486
- viewport.scrollTop(viewport.scrollTop() + heightIncrement)
487
- ###
432
+ adjustPaddingSettings .height += wrapper .element .height ()
488
433
489
434
# re-index the buffer
490
435
item .scope .$index = buffer .first + i for item,i in buffer
491
436
437
+ viewport .adjustPadding ()
438
+ viewport .adjustScrollTop (adjustPaddingSettings)
439
+
492
440
# schedule another adjustBuffer after animation completion
493
441
if (promises .length )
494
442
$q .all (promises).then ->
@@ -506,9 +454,7 @@ angular.module('ui.scroll', [])
506
454
else
507
455
if viewport .shouldLoadTop ()
508
456
enqueueFetch (rid, false )
509
- if pending .length == 0
510
- adapter .calculateProperties ()
511
- viewport .adjustPadding ()
457
+ adapter .calculateProperties () if not pending .length
512
458
513
459
adjustBufferAfterFetch = (rid ) ->
514
460
# We need the item bindings to be processed before we can do adjustment
@@ -523,12 +469,11 @@ angular.module('ui.scroll', [])
523
469
# BTW there will always be at least 1 element in the pending array because bottom is fetched first
524
470
enqueueFetch (rid, false ) if keepFetching || pending[0 ]
525
471
pending .shift ()
526
- if pending .length == 0
472
+ if not pending .length
527
473
adapter .loading (false )
528
474
adapter .calculateProperties ()
529
475
else
530
476
fetch (rid)
531
- viewport .adjustPadding ()
532
477
533
478
fetch = (rid ) ->
534
479
# log "Running fetch... #{{true:'bottom', false: 'top'}[direction]} pending #{pending.length}"
@@ -542,7 +487,6 @@ angular.module('ui.scroll', [])
542
487
return if (rid and rid isnt ridActual) or $scope .$$destroyed
543
488
if result .length < bufferSize
544
489
buffer .eof = true
545
- # viewport.bottomPadding(0)
546
490
# log 'eof is reached'
547
491
if result .length > 0
548
492
viewport .clipTop ()
@@ -565,7 +509,6 @@ angular.module('ui.scroll', [])
565
509
return if (rid and rid isnt ridActual) or $scope .$$destroyed
566
510
if result .length < bufferSize
567
511
buffer .bof = true
568
- # viewport.topPadding(0)
569
512
# log 'bof is reached'
570
513
if result .length > 0
571
514
viewport .clipBottom () if buffer .length
0 commit comments