 									deferred = $q.defer()
 									deferred.resolve()
 									deferred.promise
+
+						ridActual = 0
+						first = 1
+						next = 1
+						buffer = []
+						pending = []
+						eof = false
+						bof = false
+
+						#removes items from start (including) through stop (excluding)
+						removeFromBuffer = (start, stop)->
+							for i in [start...stop]
+								buffer[i].scope.$destroy()
+								buffer[i].element.remove()
+							buffer.splice start, stop - start
+
+						reload = ->
+							ridActual++
+							first = 1
+							next = 1
+							removeFromBuffer(0, buffer.length)
+							builder.topPadding(0)
+							builder.bottomPadding(0)
+							pending = []
+							eof = false
+							bof = false
+							adjustBuffer ridActual
+
+						bottomVisiblePos = ->
+							viewport.scrollTop() + viewport.outerHeight()
+
+						topVisiblePos = ->
+							viewport.scrollTop()
+
+						shouldLoadBottom = ->
+							!eof && builder.bottomDataPos() < bottomVisiblePos() + bufferPadding()
+
+						clipBottom = ->
+							# clip the invisible items off the bottom
+							bottomHeight = 0 #builder.bottomPadding()
+							overage = 0
+
+							for i in [buffer.length-1..0]
+								item = buffer[i]
+								itemTop = item.element.offset().top
+								newRow = rowTop isnt itemTop
+								rowTop = itemTop
+								itemHeight = item.element.outerHeight(true) if newRow
+								if (builder.bottomDataPos() - bottomHeight - itemHeight > bottomVisiblePos() + bufferPadding())
+									bottomHeight += itemHeight if newRow
+									overage++
+									eof = false
+								else
+									break if newRow
+									overage++
+
+							if overage > 0
+								builder.bottomPadding(builder.bottomPadding() + bottomHeight)
+								removeFromBuffer(buffer.length - overage, buffer.length)
+								next -= overage
+								#log "clipped off bottom #{overage} bottom padding #{builder.bottomPadding()}"
+
+						shouldLoadTop = ->
+							!bof && (builder.topDataPos() > topVisiblePos() - bufferPadding())
+
+						clipTop = ->
+							# clip the invisible items off the top
+							topHeight = 0
+							overage = 0
+							for item in buffer
+								itemTop = item.element.offset().top
+								newRow = rowTop isnt itemTop
+								rowTop = itemTop
+								itemHeight = item.element.outerHeight(true) if newRow
+								if (builder.topDataPos() + topHeight + itemHeight < topVisiblePos() - bufferPadding())
+									topHeight += itemHeight if newRow
+									overage++
+									bof = false
+								else
+									break if newRow
+									overage++
+							if overage > 0
+								builder.topPadding(builder.topPadding() + topHeight)
+								removeFromBuffer(0, overage)
+								first += overage
+								#log "clipped off top #{overage} top padding #{builder.topPadding()}"
+
+						enqueueFetch = (rid, direction)->
+							if (!adapter.isLoading)
+								loading(true)
+							if pending.push(direction) == 1
+								fetch(rid)
+
+						insert = (index, item) ->
+							itemScope = $scope.$new()
+							itemScope[itemName] = item
+							toBeAppended = index > first
+							itemScope.$index = index
+							itemScope.$index-- if toBeAppended
+							wrapper =
+								scope: itemScope
+
+							linker itemScope, (clone) ->
+								wrapper.element = clone
+								wrapper.toBeAppended = toBeAppended
+								if toBeAppended
+									if index == next
+										wrapper.doInsert = ->
+											builder.append clone
+											builder.bottomPadding(Math.max(0,builder.bottomPadding() - wrapper.element.outerHeight(true)))
+										buffer.push wrapper
+									else
+										wrapper.doInsert = ->
+											buffer[index-first-1].element.after clone
+										buffer.splice index-first+1, 0, wrapper
+								else
+									wrapper.doInsert = ->
+										builder.prepend clone
+										# an element is inserted at the top
+										newHeight = builder.topPadding() - wrapper.element.outerHeight(true)
+										# adjust padding to prevent it from visually pushing everything down
+										if newHeight >= 0
+											# if possible, reduce topPadding
+											builder.topPadding(newHeight)
+										else
+											# if not, increment scrollTop
+											viewport.scrollTop(viewport.scrollTop() + wrapper.element.outerHeight(true))
+									buffer.unshift wrapper
+
+						adjustBuffer = (rid, finalize)->
+							# We need the item bindings to be processed before we can do adjustment
+							$timeout ->
+								prepended = []
+								for wrapper in buffer
+									if wrapper.element.parent().length == 0
+										if wrapper.toBeAppended
+											wrapper.doInsert()
+										else
+											prepended.unshift wrapper
+								# prepended items have to be inserted from the bottom up
+								for wrapper in prepended
+									wrapper.doInsert()
+								# doAdjustment(rid, finalize)
+								#log "top {actual=#{builder.topDataPos()} visible from=#{topVisiblePos()} bottom {visible through=#{bottomVisiblePos()} actual=#{builder.bottomDataPos()}}"
+								if shouldLoadBottom()
+									enqueueFetch(rid, true)
+								else
+									enqueueFetch(rid, false) if shouldLoadTop()
+								finalize(rid) if finalize
+								if pending.length == 0
+									topHeight = 0
+									for item in buffer
+										itemTop = item.element.offset().top
+										newRow = rowTop isnt itemTop
+										rowTop = itemTop
+										itemHeight = item.element.outerHeight(true) if newRow
+										if newRow and (builder.topDataPos() + topHeight + itemHeight < topVisiblePos())
+											topHeight += itemHeight
+										else
+											topVisible(item) if newRow
+											break
+
+						finalize = (rid) ->
+							adjustBuffer rid, ->
+								pending.shift()
+								if pending.length == 0
+									loading(false)
+								else
+									fetch(rid)
+
+						fetch = (rid) ->
+							direction = pending[0]
+							#log "Running fetch... #{{true:'bottom', false: 'top'}[direction]} pending #{pending.length}"
+							if direction
+								if buffer.length && !shouldLoadBottom()
+									finalize rid
+								else
+									#log "appending... requested #{bufferSize} records starting from #{next}"
+									datasource.get next, bufferSize,
+									(result) ->
+										return if (rid and rid isnt ridActual) or $scope.$$destroyed
+										if result.length < bufferSize
+											eof = true
+											builder.bottomPadding(0)
+											#log "eof is reached"
+										if result.length > 0
+											clipTop()
+											for item in result
+												insert ++next, item
+											#log "appended: requested #{bufferSize} received #{result.length} buffer size #{buffer.length} first #{first} next #{next}"
+										finalize rid
+							else
+								if buffer.length && !shouldLoadTop()
+									finalize rid
+								else
+									#log "prepending... requested #{size} records starting from #{start}"
+									datasource.get first-bufferSize, bufferSize,
+									(result) ->
+										return if (rid and rid isnt ridActual) or $scope.$$destroyed
+										if result.length < bufferSize
+											bof = true
+											builder.topPadding(0)
+											#log "bof is reached"
+										if result.length > 0
+											clipBottom() if buffer.length
+											for i in [result.length-1..0]
+												insert --first, result[i]
+											#log "prepended: requested #{bufferSize} received #{result.length} buffer size #{buffer.length} first #{first} next #{next}"
+										finalize rid
+
+						# events and bindings
+
+						resizeAndScrollHandler = ->
+							if !$rootScope.$$phase && !adapter.isLoading
+								adjustBuffer()
+								$scope.$apply()
+
+						wheelHandler = (event) ->
+							scrollTop = viewport[0].scrollTop
+							yMax = viewport[0].scrollHeight - viewport[0].clientHeight
+							if (scrollTop is 0 and not bof) or (scrollTop is yMax and not eof)
+								event.preventDefault()
+
+						viewport.bind 'resize', resizeAndScrollHandler
+						viewport.bind 'scroll', resizeAndScrollHandler
+						viewport.bind 'mousewheel', wheelHandler
+
+						$scope.$watch datasource.revision, reload
+
+						if datasource.scope
+							eventListener = datasource.scope.$new()
+						else
+							eventListener = $scope.$new()
+
+						$scope.$on '$destroy', ->
+							for item in buffer
+								item.scope.$destroy()
+								item.element.remove()
+							viewport.unbind 'resize', resizeAndScrollHandler
+							viewport.unbind 'scroll', resizeAndScrollHandler
+							viewport.unbind 'mousewheel', wheelHandler
+
+
+						# adapter initializing
+
+						adapter = {}
+						adapter.isLoading = false
 
 						applyUpdate = (wrapper, newItems) ->
 							animations = []

							adjustBuffer()

						doInsert = (locator, item) ->
							inserted = []
							if angular.isFunction locator
								throw new Error('not implemented - Insert with locator function')
							else
								if 0 <= locator-first-1 < buffer.length
									inserted.push (insert locator, item)
									next++
							item.scope.$index = first + i for item,i in buffer
							adjustBuffer(null, inserted)

						eventListener.$on "insert.item", (event, locator, item)->doInsert(locator, item)
						eventListener.$on "update.items", (event, locator, newItem)-> doUpdate(locator, newItem)

							adapter.isLoading = value
							setValueChain($scope, $attr.isLoading, value) if $attr.isLoading
							datasource.loading(value) if typeof datasource.loading is 'function'

						removeElement =
							if $animate
