-
Notifications
You must be signed in to change notification settings - Fork 15
/
Copy pathinfini-scroll.js
185 lines (164 loc) · 5.39 KB
/
infini-scroll.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
(function () {
var $ui = Titanium.UI;
var constructor = function (viewOptions, options) {
var $this = this;
/**
* Options for the base view (ScrollView)
*/
this.viewOptions = {
layout: "vertical"
, width: $ui.FILL
, height: $ui.FILL
};
/**
* Options for InfiniScroll
*
* - **onNewHeight** {Function} - Called when a new height for the scroll view has been calculated
* - **onBottom** {Function} - Called when scrolled within the triggerAt option
* - **triggerAt** {Unit} - (percent/dp) When to trigger the 'scrollToBottom' event
*/
this.options = {
onNewHeight: function(height, myInfiniScroll){}
, onBottom: function(myInfiniScroll){}
, triggerAt: '90%'
, checkTimeout: 6000
};
for (var key in viewOptions) this.viewOptions[key] = viewOptions[key];
for (var key in options) this.options[key] = options[key];
this.view = $ui.createScrollView(this.viewOptions);
this.wrapper = this.getNewWrapper();
this.view.add(this.wrapper);
this.scrollEndTriggered = false;
this.postLayoutAdded = false;
/**
* Current Height of the ScrollView
*/
this.height = 0;
/**
* Current dp value for trigger at - differs from the one in options because
* Someone can pass in a percentage to trigger at and this value represents the dp
* value of that percentage
*/
this.triggerAt = 0;
/**
* Keeps state for current rounds trigger status
*/
this.triggered = false;
/**
* Maintains state for whether or no we're calculating a new height
*/
this.calculatingHeight = false;
// Cache whether or not this a percentage we're dealing with and the trigger ratio
if (this.triggerIsPercentage = this.options.triggerAt.indexOf('%')) {
this.triggerRatio = parseFloat(this.options.triggerAt) / 100;
} else {
this.triggerAt = parseInt(this.options.triggerAt);
}
// Apparently fn.bind isn't working so we'll curry it
this.onScrollCurry = function (e) { $this._onScroll(e); };
this.view.addEventListener('scroll', this.onScrollCurry);
};
constructor.prototype = {
/**
* Proxy Methods
*/
add: function (view) {
if (!this.checkingHeight){
this.startHeightCheck();
}
if (Object.prototype.toString.call(view)[8] === "A"){
var intermediate = $ui.createView({ width: $ui.FILL, height: $ui.SIZE, layout: 'vertical' });
for (var i = 0; i < view.length; i++){
intermediate.add(view[i]);
}
this.wrapper.add(intermediate);
}else{
this.wrapper.add(view);
}
},
hide: function () {
return this.view.hide();
},
show: function () {
return this.view.show();
},
scrollTo: function (x, y) {
return this.view.scrollTo(x, y);
},
addEvents: function(){
this.view.addEventListener('scroll', this.onScrollCurry);
},
removeEvents: function(){
this.view.removeEventListener('scroll', this.onScrollCurry);
},
/**
* Re-calculates the new triggerAt property if needed and calls user onNewHeight function
* Also re-attaches the onscroll event
*/
triggerNewHeight: function (silent) {
this.triggerAt = (this.triggerIsPercentage)
? parseInt(this.height * this.triggerRatio)
: this.height - this.options.triggerAt;
this.calculatingHeight = false;
this.scrollEndTriggered = false;
this.addEvents();
if (!silent) this.options.onNewHeight(this.height, this);
},
triggerScrollEnd: function (scrollY) {
this.options.onScrollToEnd(scrollY, this);
},
/**
* isCalculatingHeight
* Returns the caclulation state of the scroll
* @return {Boolean}
*/
isCalculatingHeight: function () {
return this.calculatingHeight;
},
startHeightCheck: function(){
this.checkingHeight = true;
var $this = this, currentTime = 0, checkInterval = setInterval(function(){
if ((currentTime += 100) > $this.options.timeout) clearInterval(checkInterval);
var newHeight = $this.wrapper.getSize().height;
if (newHeight !== $this.height){
$this.height = newHeight;
clearInterval(checkInterval);
$this.checkingHeight = false;
$this.triggerNewHeight();
}
}, 100);
},
clearChildren: function(){
this.view.remove(this.wrapper);
this.wrapper = this.getNewWrapper();
this.view.add(this.wrapper);
this.height = 0;
},
getNewWrapper: function(){
return $ui.createView({
width: $ui.FILL
, height: $ui.SIZE
, layout: 'vertical'
});
},
/**
* Checks against scroll events and triggers and removes when necessary,
* such as when scrolling happens during handler removal or end triggering.
* @private
*/
_onScroll: function (e) {
if (this.clearingChildren) return;
if (this.scrollEndTriggered) return;
// In case there was some scrolling while the handler was being removed
if (this.isCalculatingHeight()) return;
var trigger = this.triggerAt - this.view.size.height;
if (trigger <= 0) return;
if (e.y >= trigger) {
this.scrollEndTriggered = true;
this.removeEvents()
this.triggerScrollEnd(e.y);
}
}
};
module.exports = constructor;
})();