3 var _
= require('lodash');
4 var Backbone
= require('backbone');
5 var debug
= require('debug-proxy')('app:nes');
6 var isMobile
= require('./is-mobile');
7 var passiveEventListener
= require('./passive-event-listener');
9 /* Put your scrolling panels on rollers */
10 function NeverEndingStory(target
, options
) {
11 this._target
= target
;
12 this._reverse
= options
&& options
.reverse
;
13 this._prevScrollTop
= 0;
14 this._prevScrollTime
= Date
.now();
15 this._nearTop
= false;
16 this._nearBottom
= false;
17 this._scrollHandler
= _
.throttle(
18 isMobile() ? this.mobileScroll
.bind(this) : this.scroll
.bind(this),
21 this._contentWrapper
= options
&& options
.contentWrapper
;
25 _
.extend(NeverEndingStory
.prototype, Backbone
.Events
, {
27 var target
= this._target
;
29 var scrollTop
= target
.scrollTop
;
30 var scrollBottom
= target
.scrollHeight
- target
.scrollTop
- target
.clientHeight
;
32 var prevScrollTop
= this._prevScrollTop
;
33 var prevScrollBottom
= this._prevScrollBottom
;
35 this._prevScrollTop
= scrollTop
;
36 this._prevScrollBottom
= scrollBottom
;
38 var deltaTop
= prevScrollTop
- scrollTop
;
39 var deltaBottom
= prevScrollBottom
- scrollBottom
;
41 var halfClientHeight
= target
.clientHeight
/ 2;
42 var nearTop
= scrollTop
< halfClientHeight
;
43 var nearBottom
= scrollBottom
< halfClientHeight
;
45 if (deltaTop
> 0 && nearTop
) {
46 /* We're scrolling towards the top */
47 this.trigger('approaching.top');
48 } else if (deltaBottom
> 0 && nearBottom
) {
49 /* We're scrolling towards the bottom */
50 this.trigger('approaching.bottom');
56 mobileScroll: function() {
57 // ios and android have to stop scrolling in order to load more content above/below the fold (see rollers.js)
58 // the best time to do that is at a natural stop, which happens to be at the start or end of the
59 // current loaded content.
61 var target
= this._target
;
63 var scrollTop
= target
.scrollTop
;
64 var scrollBottom
= target
.scrollHeight
- target
.scrollTop
- target
.clientHeight
;
66 var prevScrollTop
= this._prevScrollTop
;
67 var prevScrollBottom
= this._prevScrollBottom
;
69 this._prevScrollTop
= scrollTop
;
70 this._prevScrollBottom
= scrollBottom
;
72 var deltaTop
= prevScrollTop
- scrollTop
;
73 var deltaBottom
= prevScrollBottom
- scrollBottom
;
75 var isAtTop
= scrollTop
<= 0;
76 var isAtBottom
= scrollBottom
<= 0;
78 if (deltaTop
> 0 && isAtTop
) {
79 /* We're scrolling towards the top */
80 this.trigger('approaching.top');
81 } else if (deltaBottom
> 0 && isAtBottom
) {
82 /* We're scrolling towards the bottom */
83 this.trigger('approaching.bottom');
87 scrollRate: function() {
88 var target
= this._target
;
91 var scrollTop
= target
.scrollTop
;
92 var scrollBottom
= target
.scrollHeight
- target
.scrollTop
- target
.clientHeight
;
94 var prevScrollTime
= this._prevScrollTimeRate
;
95 var prevScrollTop
= this._prevScrollTopRate
;
96 var prevScrollBottom
= this._prevScrollBottomRate
;
98 this._prevScrollTopRate
= scrollTop
;
99 this._prevScrollBottomRate
= scrollBottom
;
100 this._prevScrollTimeRate
= now
;
102 if (!prevScrollTime
) return;
104 var deltaTop
= prevScrollTop
- scrollTop
;
105 var deltaBottom
= prevScrollBottom
- scrollBottom
;
106 var timeDelta
= now
- prevScrollTime
;
107 var speed
, timeToLimit
;
110 if (scrollTop
> target
.clientHeight
) return;
112 speed
= deltaTop
/ timeDelta
;
113 timeToLimit
= scrollTop
/ speed
;
114 if (timeToLimit
< 600) {
115 this.trigger('approaching.top');
120 if (deltaBottom
> 0) {
121 if (scrollBottom
> target
.clientHeight
) return;
123 speed
= deltaBottom
/ timeDelta
;
124 timeToLimit
= scrollBottom
/ speed
;
126 if (timeToLimit
< 600) {
127 this.trigger('approaching.bottom');
132 scrollToOrigin: function() {
133 var target
= this._target
;
135 var scrollTop
= target
.scrollHeight
- target
.clientHeight
;
136 target
.scrollTop
= scrollTop
;
138 target
.scrollTop
= 0;
141 this._scrollHandler();
145 var target
= this._target
;
146 var scrollTop
= target
.scrollTop
;
147 var pageHeight
= Math
.floor(target
.offsetHeight
* 0.8);
148 target
.scrollTop
= scrollTop
- pageHeight
;
151 pageDown: function() {
152 var target
= this._target
;
153 var scrollTop
= target
.scrollTop
;
154 var pageHeight
= Math
.floor(target
.offsetHeight
* 0.8);
155 target
.scrollTop
= scrollTop
+ pageHeight
;
161 if (!this._enabled
) {
162 debug('enabling scroll listener');
163 passiveEventListener
.addEventListener(this._target
, 'scroll', this._scrollHandler
);
165 this._enabled
= true;
167 // If we have a content wrapper and it's smaller than the
168 // client area, we need to load more content immediately
169 if (this._contentWrapper
) {
170 setTimeout(function() {
171 if (self
._contentWrapper
.offsetHeight
< self
._target
.clientHeight
) {
172 self
.trigger('approaching.top');
179 disable: function() {
181 debug('disabling scroll listener');
182 passiveEventListener
.removeEventListener(this._target
, 'scroll', this._scrollHandler
);
183 this._enabled
= false;
188 module
.exports
= NeverEndingStory
;