Premium Addons for Elementor - Version 3.7.1

Version Description

  • Tweak: Vertical Scroll performance enhanced on all devices.
  • Fixed: Security bugs fixed.
Download this release

Release Info

Developer leap13
Plugin Icon 128x128 Premium Addons for Elementor
Version 3.7.1
Comparing to
See all releases

Code changes from version 3.7.0 to 3.7.1

admin/assets/js/admin.js CHANGED
@@ -34,7 +34,8 @@
34
url: settings.ajaxurl,
35
type: 'post',
36
data: {
37
- action: 'pa_save_admin_addons_settings',
38
fields: $( 'form#pa-settings' ).serialize(),
39
},
40
success: function( response ) {
@@ -61,6 +62,7 @@
61
type: 'post',
62
data: {
63
action: 'pa_maps_save_settings',
64
fields: $('form#pa-maps').serialize(),
65
},
66
success: function (response){
@@ -87,6 +89,7 @@
87
type: 'post',
88
data: {
89
action: 'pa_beta_save_settings',
90
fields: $('form#pa-beta-form').serialize(),
91
},
92
success: function (response){
34
url: settings.ajaxurl,
35
type: 'post',
36
data: {
37
+ action: 'pa_save_admin_addons_settings',
38
+ security: settings.nonce,
39
fields: $( 'form#pa-settings' ).serialize(),
40
},
41
success: function( response ) {
62
type: 'post',
63
data: {
64
action: 'pa_maps_save_settings',
65
+ security: settings.nonce,
66
fields: $('form#pa-maps').serialize(),
67
},
68
success: function (response){
89
type: 'post',
90
data: {
91
action: 'pa_beta_save_settings',
92
+ security: settings.nonce,
93
fields: $('form#pa-beta-form').serialize(),
94
},
95
success: function (response){
admin/includes/version-control.php CHANGED
@@ -44,7 +44,8 @@ class Version_Control {
44
public function pa_version_page() {
45
46
$js_info = array(
47
- 'ajaxurl' => admin_url( 'admin-ajax.php' )
48
);
49
50
wp_localize_script( 'pa-admin-js', 'settings', $js_info );
@@ -91,7 +92,7 @@ class Version_Control {
91
<tr class="pa-roll-row">
92
<th><?php echo __('Rollback Version', 'premium-addons-for-elementor'); ?></th>
93
<td>
94
- <div><?php echo sprintf( '<a target="_blank" href="%1$s" class="button pa-btn pa-rollback-button elementor-button-spinner">%2$s</a>', wp_nonce_url( admin_url( 'admin-post.php?action=premium_addons_rollback' ), 'premium_addons_rollback' ), __('Reinstall Version 3.6.9', 'premium-addons-for-elementor') ); ?></div>
95
<p class="pa-roll-desc">
96
<span><?php echo __('Warning: Please backup your database before making the rollback.', 'premium-addons-for-elementor'); ?></span>
97
</p>
@@ -122,19 +123,21 @@ class Version_Control {
122
123
public function pa_beta_save_settings() {
124
125
- if( isset( $_POST['fields'] ) ) {
126
- parse_str( $_POST['fields'], $settings );
127
- } else {
128
- return;
129
- }
130
-
131
- $this->pa_beta_settings = array(
132
- 'is-beta-tester' => intval( $settings['is-beta-tester'] ? 1 : 0 ),
133
- );
134
-
135
- update_option( 'pa_beta_save_settings', $this->pa_beta_settings );
136
-
137
- return true;
138
- die();
139
}
140
}
44
public function pa_version_page() {
45
46
$js_info = array(
47
+ 'ajaxurl' => admin_url( 'admin-ajax.php' ),
48
+ 'nonce' => wp_create_nonce('premium-addons-for-elementor'),
49
);
50
51
wp_localize_script( 'pa-admin-js', 'settings', $js_info );
92
<tr class="pa-roll-row">
93
<th><?php echo __('Rollback Version', 'premium-addons-for-elementor'); ?></th>
94
<td>
95
+ <div><?php echo sprintf( '<a target="_blank" href="%1$s" class="button pa-btn pa-rollback-button elementor-button-spinner">%2$s</a>', wp_nonce_url( admin_url( 'admin-post.php?action=premium_addons_rollback' ), 'premium_addons_rollback' ), __('Reinstall Version 3.7.0', 'premium-addons-for-elementor') ); ?></div>
96
<p class="pa-roll-desc">
97
<span><?php echo __('Warning: Please backup your database before making the rollback.', 'premium-addons-for-elementor'); ?></span>
98
</p>
123
124
public function pa_beta_save_settings() {
125
126
+ check_ajax_referer('premium-addons-for-elementor', 'security');
127
+
128
+ if( isset( $_POST['fields'] ) ) {
129
+ parse_str( $_POST['fields'], $settings );
130
+ } else {
131
+ return;
132
}
133
+
134
+ $this->pa_beta_settings = array(
135
+ 'is-beta-tester' => intval( $settings['is-beta-tester'] ? 1 : 0 ),
136
+ );
137
+
138
+ update_option( 'pa_beta_save_settings', $this->pa_beta_settings );
139
+
140
+ return true;
141
+ die();
142
+ }
143
}
admin/settings/maps.php CHANGED
@@ -40,7 +40,8 @@ class Maps {
40
public function pa_maps_page() {
41
42
$js_info = array(
43
- 'ajaxurl' => admin_url( 'admin-ajax.php' )
44
);
45
46
wp_localize_script( 'pa-admin-js', 'settings', $js_info );
@@ -91,7 +92,7 @@ class Maps {
91
<h4 class="pa-api-title"><?php echo __('Google Maps API Key:', 'premium-addons-for-elementor'); ?></h4>
92
</td>
93
<td>
94
- <input name="premium-map-api" id="premium-map-api" type="text" placeholder="API Key" value="<?php echo $settings['premium-map-api']; ?>">
95
</td>
96
</tr>
97
<tr>
@@ -108,7 +109,7 @@ class Maps {
108
$selected = 'selected="selected" ';
109
}
110
?>
111
- <option value="<?php echo $key; ?>" <?php echo $selected; ?>><?php echo esc_attr( $value ); ?></option>
112
<?php } ?>
113
</select>
114
</td>
@@ -158,22 +159,24 @@ class Maps {
158
159
public function pa_save_maps_settings() {
160
161
- if( isset( $_POST['fields'] ) ) {
162
- parse_str( $_POST['fields'], $settings );
163
- }else {
164
- return;
165
- }
166
-
167
- $this->pa_maps_settings = array(
168
- 'premium-map-api' => $settings['premium-map-api'],
169
- 'premium-map-disable-api' => intval( $settings['premium-map-disable-api'] ? 1 : 0 ),
170
- 'premium-map-cluster' => intval( $settings['premium-map-cluster'] ? 1 : 0 ),
171
- 'premium-map-locale' => $settings['premium-map-locale']
172
- );
173
-
174
- update_option( 'pa_maps_save_settings', $this->pa_maps_settings );
175
-
176
- return true;
177
- die();
178
}
179
}
40
public function pa_maps_page() {
41
42
$js_info = array(
43
+ 'ajaxurl' => admin_url( 'admin-ajax.php' ),
44
+ 'nonce' => wp_create_nonce('premium-addons-for-elementor'),
45
);
46
47
wp_localize_script( 'pa-admin-js', 'settings', $js_info );
92
<h4 class="pa-api-title"><?php echo __('Google Maps API Key:', 'premium-addons-for-elementor'); ?></h4>
93
</td>
94
<td>
95
+ <input name="premium-map-api" id="premium-map-api" type="text" placeholder="API Key" value="<?php echo esc_attr( $settings['premium-map-api'] ); ?>">
96
</td>
97
</tr>
98
<tr>
109
$selected = 'selected="selected" ';
110
}
111
?>
112
+ <option value="<?php echo esc_attr( $key ); ?>" <?php echo $selected; ?>><?php echo esc_attr( $value ); ?></option>
113
<?php } ?>
114
</select>
115
</td>
159
160
public function pa_save_maps_settings() {
161
162
+ check_ajax_referer('premium-addons-for-elementor', 'security');
163
+
164
+ if( isset( $_POST['fields'] ) ) {
165
+ parse_str( $_POST['fields'], $settings );
166
+ }else {
167
+ return;
168
}
169
+
170
+ $this->pa_maps_settings = array(
171
+ 'premium-map-api' => $settings['premium-map-api'],
172
+ 'premium-map-disable-api' => intval( $settings['premium-map-disable-api'] ? 1 : 0 ),
173
+ 'premium-map-cluster' => intval( $settings['premium-map-cluster'] ? 1 : 0 ),
174
+ 'premium-map-locale' => $settings['premium-map-locale']
175
+ );
176
+
177
+ update_option( 'pa_maps_save_settings', $this->pa_maps_settings );
178
+
179
+ return true;
180
+ die();
181
+ }
182
}
admin/settings/modules-setting.php CHANGED
@@ -141,7 +141,8 @@ class Modules_Settings {
141
$theme_slug = Helper_Functions::get_installed_theme();
142
143
$js_info = array(
144
- 'ajaxurl' => admin_url( 'admin-ajax.php' ),
145
'theme' => $theme_slug
146
);
147
@@ -698,6 +699,8 @@ class Modules_Settings {
698
699
public function pa_save_settings() {
700
701
if( isset( $_POST['fields'] ) ) {
702
parse_str( $_POST['fields'], $settings );
703
} else {
141
$theme_slug = Helper_Functions::get_installed_theme();
142
143
$js_info = array(
144
+ 'ajaxurl' => admin_url( 'admin-ajax.php' ),
145
+ 'nonce' => wp_create_nonce('premium-addons-for-elementor'),
146
'theme' => $theme_slug
147
);
148
699
700
public function pa_save_settings() {
701
702
+ check_ajax_referer('premium-addons-for-elementor', 'security');
703
+
704
if( isset( $_POST['fields'] ) ) {
705
parse_str( $_POST['fields'], $settings );
706
} else {
assets/frontend/css/premium-addons.css CHANGED
@@ -3526,6 +3526,9 @@
3526
text-size-adjust: none;
3527
*/
3528
}
3529
/************ Premium Image Scroll ***************/
3530
/*************************************************/
3531
.premium-image-scroll-section, .premium-image-scroll-container {
3526
text-size-adjust: none;
3527
*/
3528
}
3529
+ .premium-vscroll-temp .slimScrollBar {
3530
+ visibility: hidden;
3531
+ }
3532
/************ Premium Image Scroll ***************/
3533
/*************************************************/
3534
.premium-image-scroll-section, .premium-image-scroll-container {
assets/frontend/js/lib/iscroll.js ADDED
@@ -0,0 +1,2172 @@
1
+ /*! iScroll v5.2.0-snapshot ~ (c) 2008-2017 Matteo Spinelli ~ http://cubiq.org/license */
2
+ (function (window, document, Math) {
3
+ var rAF = window.requestAnimationFrame ||
4
+ window.webkitRequestAnimationFrame ||
5
+ window.mozRequestAnimationFrame ||
6
+ window.oRequestAnimationFrame ||
7
+ window.msRequestAnimationFrame ||
8
+ function (callback) { window.setTimeout(callback, 1000 / 60); };
9
+
10
+ var utils = (function () {
11
+ var me = {};
12
+
13
+ var _elementStyle = document.createElement('div').style;
14
+ var _vendor = (function () {
15
+ var vendors = ['t', 'webkitT', 'MozT', 'msT', 'OT'],
16
+ transform,
17
+ i = 0,
18
+ l = vendors.length;
19
+
20
+ for ( ; i < l; i++ ) {
21
+ transform = vendors[i] + 'ransform';
22
+ if ( transform in _elementStyle ) return vendors[i].substr(0, vendors[i].length-1);
23
+ }
24
+
25
+ return false;
26
+ })();
27
+
28
+ function _prefixStyle (style) {
29
+ if ( _vendor === false ) return false;
30
+ if ( _vendor === '' ) return style;
31
+ return _vendor + style.charAt(0).toUpperCase() + style.substr(1);
32
+ }
33
+
34
+ me.getTime = Date.now || function getTime () { return new Date().getTime(); };
35
+
36
+ me.extend = function (target, obj) {
37
+ for ( var i in obj ) {
38
+ target[i] = obj[i];
39
+ }
40
+ };
41
+
42
+ me.addEvent = function (el, type, fn, capture) {
43
+ el.addEventListener(type, fn, !!capture);
44
+ };
45
+
46
+ me.removeEvent = function (el, type, fn, capture) {
47
+ el.removeEventListener(type, fn, !!capture);
48
+ };
49
+
50
+ me.prefixPointerEvent = function (pointerEvent) {
51
+ return window.MSPointerEvent ?
52
+ 'MSPointer' + pointerEvent.charAt(7).toUpperCase() + pointerEvent.substr(8):
53
+ pointerEvent;
54
+ };
55
+
56
+ me.momentum = function (current, start, time, lowerMargin, wrapperSize, deceleration) {
57
+ var distance = current - start,
58
+ speed = Math.abs(distance) / time,
59
+ destination,
60
+ duration;
61
+
62
+ deceleration = deceleration === undefined ? 0.0006 : deceleration;
63
+
64
+ destination = current + ( speed * speed ) / ( 2 * deceleration ) * ( distance < 0 ? -1 : 1 );
65
+ duration = speed / deceleration;
66
+
67
+ if ( destination < lowerMargin ) {
68
+ destination = wrapperSize ? lowerMargin - ( wrapperSize / 2.5 * ( speed / 8 ) ) : lowerMargin;
69
+ distance = Math.abs(destination - current);
70
+ duration = distance / speed;
71
+ } else if ( destination > 0 ) {
72
+ destination = wrapperSize ? wrapperSize / 2.5 * ( speed / 8 ) : 0;
73
+ distance = Math.abs(current) + destination;
74
+ duration = distance / speed;
75
+ }
76
+
77
+ return {
78
+ destination: Math.round(destination),
79
+ duration: duration
80
+ };
81
+ };
82
+
83
+ var _transform = _prefixStyle('transform');
84
+
85
+ me.extend(me, {
86
+ hasTransform: _transform !== false,
87
+ hasPerspective: _prefixStyle('perspective') in _elementStyle,
88
+ hasTouch: 'ontouchstart' in window,
89
+ hasPointer: !!(window.PointerEvent || window.MSPointerEvent), // IE10 is prefixed
90
+ hasTransition: _prefixStyle('transition') in _elementStyle
91
+ });
92
+
93
+ /*
94
+ This should find all Android browsers lower than build 535.19 (both stock browser and webview)
95
+ - galaxy S2 is ok
96
+ - 2.3.6 : `AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1`
97
+ - 4.0.4 : `AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30`
98
+ - galaxy S3 is badAndroid (stock brower, webview)
99
+ `AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30`
100
+ - galaxy S4 is badAndroid (stock brower, webview)
101
+ `AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30`
102
+ - galaxy S5 is OK
103
+ `AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Mobile Safari/537.36 (Chrome/)`
104
+ - galaxy S6 is OK
105
+ `AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Mobile Safari/537.36 (Chrome/)`
106
+ */
107
+ me.isBadAndroid = (function() {
108
+ var appVersion = window.navigator.appVersion;
109
+ // Android browser is not a chrome browser.
110
+ if (/Android/.test(appVersion) && !(/Chrome\/\d/.test(appVersion))) {
111
+ var safariVersion = appVersion.match(/Safari\/(\d+.\d)/);
112
+ if(safariVersion && typeof safariVersion === "object" && safariVersion.length >= 2) {
113
+ return parseFloat(safariVersion[1]) < 535.19;
114
+ } else {
115
+ return true;
116
+ }
117
+ } else {
118
+ return false;
119
+ }
120
+ })();
121
+
122
+ me.extend(me.style = {}, {
123
+ transform: _transform,
124
+ transitionTimingFunction: _prefixStyle('transitionTimingFunction'),
125
+ transitionDuration: _prefixStyle('transitionDuration'),
126
+ transitionDelay: _prefixStyle('transitionDelay'),
127
+ transformOrigin: _prefixStyle('transformOrigin'),
128
+ touchAction: _prefixStyle('touchAction')
129
+ });
130
+
131
+ me.hasClass = function (e, c) {
132
+ var re = new RegExp("(^|\\s)" + c + "(\\s|$)");
133
+ return re.test(e.className);
134
+ };
135
+
136
+ me.addClass = function (e, c) {
137
+ if ( me.hasClass(e, c) ) {
138
+ return;
139
+ }
140
+
141
+ var newclass = e.className.split(' ');
142
+ newclass.push(c);
143
+ e.className = newclass.join(' ');
144
+ };
145
+
146
+ me.removeClass = function (e, c) {
147
+ if ( !me.hasClass(e, c) ) {
148
+ return;
149
+ }
150
+
151
+ var re = new RegExp("(^|\\s)" + c + "(\\s|$)", 'g');
152
+ e.className = e.className.replace(re, ' ');
153
+ };
154
+
155
+ me.offset = function (el) {
156
+ var left = -el.offsetLeft,
157
+ top = -el.offsetTop;
158
+
159
+ // jshint -W084
160
+ while (el = el.offsetParent) {
161
+ left -= el.offsetLeft;
162
+ top -= el.offsetTop;
163
+ }
164
+ // jshint +W084
165
+
166
+ return {
167
+ left: left,
168
+ top: top
169
+ };
170
+ };
171
+
172
+ me.preventDefaultException = function (el, exceptions) {
173
+ for ( var i in exceptions ) {
174
+ if ( exceptions[i].test(el[i]) ) {
175
+ return true;
176
+ }
177
+ }
178
+
179
+ return false;
180
+ };
181
+
182
+ me.extend(me.eventType = {}, {
183
+ touchstart: 1,
184
+ touchmove: 1,
185
+ touchend: 1,
186
+
187
+ mousedown: 2,
188
+ mousemove: 2,
189
+ mouseup: 2,
190
+
191
+ pointerdown: 3,
192
+ pointermove: 3,
193
+ pointerup: 3,
194
+
195
+ MSPointerDown: 3,
196
+ MSPointerMove: 3,
197
+ MSPointerUp: 3
198
+ });
199
+
200
+ me.extend(me.ease = {}, {
201
+ quadratic: {
202
+ style: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
203
+ fn: function (k) {
204
+ return k * ( 2 - k );
205
+ }
206
+ },
207
+ circular: {
208
+ style: 'cubic-bezier(0.1, 0.57, 0.1, 1)', // Not properly "circular" but this looks better, it should be (0.075, 0.82, 0.165, 1)
209
+ fn: function (k) {
210
+ return Math.sqrt( 1 - ( --k * k ) );
211
+ }
212
+ },
213
+ back: {
214
+ style: 'cubic-bezier(0.175, 0.885, 0.32, 1.275)',
215
+ fn: function (k) {
216
+ var b = 4;
217
+ return ( k = k - 1 ) * k * ( ( b + 1 ) * k + b ) + 1;
218
+ }
219
+ },
220
+ bounce: {
221
+ style: '',
222
+ fn: function (k) {
223
+ if ( ( k /= 1 ) < ( 1 / 2.75 ) ) {
224
+ return 7.5625 * k * k;
225
+ } else if ( k < ( 2 / 2.75 ) ) {
226
+ return 7.5625 * ( k -= ( 1.5 / 2.75 ) ) * k + 0.75;
227
+ } else if ( k < ( 2.5 / 2.75 ) ) {
228
+ return 7.5625 * ( k -= ( 2.25 / 2.75 ) ) * k + 0.9375;
229
+ } else {
230
+ return 7.5625 * ( k -= ( 2.625 / 2.75 ) ) * k + 0.984375;
231
+ }
232
+ }
233
+ },
234
+ elastic: {
235
+ style: '',
236
+ fn: function (k) {
237
+ var f = 0.22,
238
+ e = 0.4;
239
+
240
+ if ( k === 0 ) { return 0; }
241
+ if ( k == 1 ) { return 1; }
242
+
243
+ return ( e * Math.pow( 2, - 10 * k ) * Math.sin( ( k - f / 4 ) * ( 2 * Math.PI ) / f ) + 1 );
244
+ }
245
+ }
246
+ });
247
+
248
+ me.tap = function (e, eventName) {
249
+ var ev = document.createEvent('Event');
250
+ ev.initEvent(eventName, true, true);
251
+ ev.pageX = e.pageX;
252
+ ev.pageY = e.pageY;
253
+ e.target.dispatchEvent(ev);
254
+ };
255
+
256
+ me.click = function (e) {
257
+ var target = e.target,
258
+ ev;
259
+
260
+ if ( !(/(SELECT|INPUT|TEXTAREA)/i).test(target.tagName) ) {
261
+ // https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/initMouseEvent
262
+ // initMouseEvent is deprecated.
263
+ ev = document.createEvent(window.MouseEvent ? 'MouseEvents' : 'Event');
264
+ ev.initEvent('click', true, true);
265
+ ev.view = e.view || window;
266
+ ev.detail = 1;
267
+ ev.screenX = target.screenX || 0;
268
+ ev.screenY = target.screenY || 0;
269
+ ev.clientX = target.clientX || 0;
270
+ ev.clientY = target.clientY || 0;
271
+ ev.ctrlKey = !!e.ctrlKey;
272
+ ev.altKey = !!e.altKey;
273
+ ev.shiftKey = !!e.shiftKey;
274
+ ev.metaKey = !!e.metaKey;
275
+ ev.button = 0;
276
+ ev.relatedTarget = null;
277
+ ev._constructed = true;
278
+ target.dispatchEvent(ev);
279
+ }
280
+ };
281
+
282
+ me.getTouchAction = function(eventPassthrough, addPinch) {
283
+ var touchAction = 'none';
284
+ if ( eventPassthrough === 'vertical' ) {
285
+ touchAction = 'pan-y';
286
+ } else if (eventPassthrough === 'horizontal' ) {
287
+ touchAction = 'pan-x';
288
+ }
289
+ if (addPinch && touchAction != 'none') {
290
+ // add pinch-zoom support if the browser supports it, but if not (eg. Chrome <55) do nothing
291
+ touchAction += ' pinch-zoom';
292
+ }
293
+ return touchAction;
294
+ };
295
+
296
+ me.getRect = function(el) {
297
+ if (el instanceof SVGElement) {
298
+ var rect = el.getBoundingClientRect();
299
+ return {
300
+ top : rect.top,
301
+ left : rect.left,
302
+ width : rect.width,
303
+ height : rect.height
304
+ };
305
+ } else {
306
+ return {
307
+ top : el.offsetTop,
308
+ left : el.offsetLeft,
309
+ width : el.offsetWidth,
310
+ height : utils.hasTouch ? 650 : el.offsetHeight
311
+ };
312
+ }
313
+ };
314
+
315
+ return me;
316
+ })();
317
+ function IScroll (el, options) {
318
+ this.wrapper = typeof el == 'string' ? document.querySelector(el) : el;
319
+ this.scroller = this.wrapper.children[0];
320
+ this.scrollerStyle = this.scroller.style; // cache style for better performance
321
+
322
+ this.options = {
323
+
324
+ resizeScrollbars: true,
325
+
326
+ mouseWheelSpeed: 20,
327
+
328
+ snapThreshold: 0.334,
329
+
330
+ // INSERT POINT: OPTIONS
331
+ disablePointer : !utils.hasPointer,
332
+ disableTouch : utils.hasPointer || !utils.hasTouch,
333
+ disableMouse : utils.hasPointer || utils.hasTouch,
334
+ startX: 0,
335
+ startY: 0,
336
+ scrollY: true,
337
+ directionLockThreshold: 5,
338
+ momentum: true,
339
+
340
+ bounce: true,
341
+ bounceTime: 600,
342
+ bounceEasing: '',
343
+
344
+ preventDefault: true,
345
+ preventDefaultException: { tagName: /^(INPUT|TEXTAREA|BUTTON|SELECT)#x2F; },
346
+
347
+ HWCompositing: true,
348
+ useTransition: true,
349
+ useTransform: true,
350
+ bindToWrapper: typeof window.onmousedown === "undefined"
351
+ };
352
+
353
+ for ( var i in options ) {
354
+ this.options[i] = options[i];
355
+ }
356
+
357
+ // Normalize options
358
+ this.translateZ = this.options.HWCompositing && utils.hasPerspective ? ' translateZ(0)' : '';
359
+
360
+ this.options.useTransition = utils.hasTransition && this.options.useTransition;
361
+ this.options.useTransform = utils.hasTransform && this.options.useTransform;
362
+
363
+ this.options.eventPassthrough = this.options.eventPassthrough === true ? 'vertical' : this.options.eventPassthrough;
364
+ this.options.preventDefault = !this.options.eventPassthrough && this.options.preventDefault;
365
+
366
+ // If you want eventPassthrough I have to lock one of the axes
367
+ this.options.scrollY = this.options.eventPassthrough == 'vertical' ? false : this.options.scrollY;
368
+ this.options.scrollX = this.options.eventPassthrough == 'horizontal' ? false : this.options.scrollX;
369
+
370
+ // With eventPassthrough we also need lockDirection mechanism
371
+ this.options.freeScroll = this.options.freeScroll && !this.options.eventPassthrough;
372
+ this.options.directionLockThreshold = this.options.eventPassthrough ? 0 : this.options.directionLockThreshold;
373
+
374
+ this.options.bounceEasing = typeof this.options.bounceEasing == 'string' ? utils.ease[this.options.bounceEasing] || utils.ease.circular : this.options.bounceEasing;
375
+
376
+ this.options.resizePolling = this.options.resizePolling === undefined ? 60 : this.options.resizePolling;
377
+
378
+ if ( this.options.tap === true ) {
379
+ this.options.tap = 'tap';
380
+ }
381
+
382
+ // https://github.com/cubiq/iscroll/issues/1029
383
+ if (!this.options.useTransition && !this.options.useTransform) {
384
+ if(!(/relative|absolute/i).test(this.scrollerStyle.position)) {
385
+ this.scrollerStyle.position = "relative";
386
+ }
387
+ }
388
+
389
+ if ( this.options.shrinkScrollbars == 'scale' ) {
390
+ this.options.useTransition = false;
391
+ }
392
+
393
+ this.options.invertWheelDirection = this.options.invertWheelDirection ? -1 : 1;
394
+
395
+ // INSERT POINT: NORMALIZATION
396
+
397
+ // Some defaults
398
+ this.x = 0;
399
+ this.y = 0;
400
+ this.directionX = 0;
401
+ this.directionY = 0;
402
+ this._events = {};
403
+
404
+ // INSERT POINT: DEFAULTS
405
+
406
+ this._init();
407
+ this.refresh();
408
+
409
+ this.scrollTo(this.options.startX, this.options.startY);
410
+ this.enable();
411
+ }
412
+
413
+ IScroll.prototype = {
414
+ version: '5.2.0-snapshot',
415
+
416
+ _init: function () {
417
+ this._initEvents();
418
+
419
+ if ( this.options.scrollbars || this.options.indicators ) {
420
+ this._initIndicators();
421
+ }
422
+
423
+ if ( this.options.mouseWheel ) {
424
+ this._initWheel();
425
+ }
426
+
427
+ if ( this.options.snap ) {
428
+ this._initSnap();
429
+ }
430
+
431
+ if ( this.options.keyBindings ) {
432
+ this._initKeys();
433
+ }
434
+
435
+ // INSERT POINT: _init
436
+
437
+ },
438
+
439
+ destroy: function () {
440
+ this._initEvents(true);
441
+ clearTimeout(this.resizeTimeout);
442
+ this.resizeTimeout = null;
443
+ this._execEvent('destroy');
444
+ },
445
+
446
+ _transitionEnd: function (e) {
447
+ if ( e.target != this.scroller || !this.isInTransition ) {
448
+ return;
449
+ }
450
+
451
+ this._transitionTime();
452
+ if ( !this.resetPosition(this.options.bounceTime) ) {
453
+ this.isInTransition = false;
454
+ this._execEvent('scrollEnd');
455
+ }
456
+ },
457
+
458
+ _start: function (e) {
459
+ // React to left mouse button only
460
+ if ( utils.eventType[e.type] != 1 ) {
461
+ // for button property
462
+ // http://unixpapa.com/js/mouse.html
463
+ var button;
464
+ if (!e.which) {
465
+ /* IE case */
466
+ button = (e.button < 2) ? 0 :
467
+ ((e.button == 4) ? 1 : 2);
468
+ } else {
469
+ /* All others */
470
+ button = e.button;
471
+ }
472
+ if ( button !== 0 ) {
473
+ return;
474
+ }
475
+ }
476
+
477
+ if ( !this.enabled || (this.initiated && utils.eventType[e.type] !== this.initiated) ) {
478
+ return;
479
+ }
480
+
481
+ if ( this.options.preventDefault && !utils.isBadAndroid && !utils.preventDefaultException(e.target, this.options.preventDefaultException) ) {
482
+ e.preventDefault();
483
+ }
484
+
485
+ var point = e.touches ? e.touches[0] : e,
486
+ pos;
487
+
488
+ this.initiated = utils.eventType[e.type];
489
+ this.moved = false;
490
+ this.distX = 0;
491
+ this.distY = 0;
492
+ this.directionX = 0;
493
+ this.directionY = 0;
494
+ this.directionLocked = 0;
495
+
496
+ this.startTime = utils.getTime();
497
+
498
+ if ( this.options.useTransition && this.isInTransition ) {
499
+ this._transitionTime();
500
+ this.isInTransition = false;
501
+ pos = this.getComputedPosition();
502
+ this._translate(Math.round(pos.x), Math.round(pos.y));
503
+ this._execEvent('scrollEnd');
504
+ } else if ( !this.options.useTransition && this.isAnimating ) {
505
+ this.isAnimating = false;
506
+ this._execEvent('scrollEnd');
507
+ }
508
+
509
+ this.startX = this.x;
510
+ this.startY = this.y;
511
+ this.absStartX = this.x;
512
+ this.absStartY = this.y;
513
+ this.pointX = point.pageX;
514
+ this.pointY = point.pageY;
515
+
516
+ this._execEvent('beforeScrollStart');
517
+ },
518
+
519
+ _move: function (e) {
520
+ if ( !this.enabled || utils.eventType[e.type] !== this.initiated ) {
521
+ return;
522
+ }
523
+
524
+ if ( this.options.preventDefault ) { // increases performance on Android? TODO: check!
525
+ e.preventDefault();
526
+ }
527
+
528
+ var point = e.touches ? e.touches[0] : e,
529
+ deltaX = point.pageX - this.pointX,
530
+ deltaY = point.pageY - this.pointY,
531
+ timestamp = utils.getTime(),
532
+ newX, newY,
533
+ absDistX, absDistY;
534
+
535
+ this.pointX = point.pageX;
536
+ this.pointY = point.pageY;
537
+
538
+ this.distX += deltaX;
539
+ this.distY += deltaY;
540
+ absDistX = Math.abs(this.distX);
541
+ absDistY = Math.abs(this.distY);
542
+
543
+ // We need to move at least 10 pixels for the scrolling to initiate
544
+ if ( timestamp - this.endTime > 300 && (absDistX < 10 && absDistY < 10) ) {
545
+ return;
546
+ }
547
+
548
+ // If you are scrolling in one direction lock the other
549
+ if ( !this.directionLocked && !this.options.freeScroll ) {
550
+ if ( absDistX > absDistY + this.options.directionLockThreshold ) {
551
+ this.directionLocked = 'h'; // lock horizontally
552
+ } else if ( absDistY >= absDistX + this.options.directionLockThreshold ) {
553
+ this.directionLocked = 'v'; // lock vertically
554
+ } else {
555
+ this.directionLocked = 'n'; // no lock
556
+ }
557
+ }
558
+
559
+ if ( this.directionLocked == 'h' ) {
560
+ if ( this.options.eventPassthrough == 'vertical' ) {
561
+ e.preventDefault();
562
+ } else if ( this.options.eventPassthrough == 'horizontal' ) {
563
+ this.initiated = false;
564
+ return;
565
+ }
566
+
567
+ deltaY = 0;
568
+ } else if ( this.directionLocked == 'v' ) {
569
+ if ( this.options.eventPassthrough == 'horizontal' ) {
570
+ e.preventDefault();
571
+ } else if ( this.options.eventPassthrough == 'vertical' ) {
572
+ this.initiated = false;
573
+ return;
574
+ }
575
+
576
+ deltaX = 0;
577
+ }
578
+
579
+ deltaX = this.hasHorizontalScroll ? deltaX : 0;
580
+ deltaY = this.hasVerticalScroll ? deltaY : 0;
581
+
582
+ newX = this.x + deltaX;
583
+ newY = this.y + deltaY;
584
+
585
+ // Slow down if outside of the boundaries
586
+ if ( newX > 0 || newX < this.maxScrollX ) {
587
+ newX = this.options.bounce ? this.x + deltaX / 3 : newX > 0 ? 0 : this.maxScrollX;
588
+ }
589
+ if ( newY > 0 || newY < this.maxScrollY ) {
590
+ newY = this.options.bounce ? this.y + deltaY / 3 : newY > 0 ? 0 : this.maxScrollY;
591
+ }
592
+
593
+ this.directionX = deltaX > 0 ? -1 : deltaX < 0 ? 1 : 0;
594
+ this.directionY = deltaY > 0 ? -1 : deltaY < 0 ? 1 : 0;
595
+
596
+ if ( !this.moved ) {
597
+ this._execEvent('scrollStart');
598
+ }
599
+
600
+ this.moved = true;
601
+
602
+ this._translate(newX, newY);
603
+
604
+ /* REPLACE START: _move */
605
+
606
+ if ( timestamp - this.startTime > 300 ) {
607
+ this.startTime = timestamp;
608
+ this.startX = this.x;
609
+ this.startY = this.y;
610
+ }
611
+
612
+ /* REPLACE END: _move */
613
+
614
+ },
615
+
616
+ _end: function (e) {
617
+ if ( !this.enabled || utils.eventType[e.type] !== this.initiated ) {
618
+ return;
619
+ }
620
+
621
+ if ( this.options.preventDefault && !utils.preventDefaultException(e.target, this.options.preventDefaultException) ) {
622
+ e.preventDefault();
623
+ }
624
+
625
+ var point = e.changedTouches ? e.changedTouches[0] : e,
626
+ momentumX,
627
+ momentumY,
628
+ duration = utils.getTime() - this.startTime,
629
+ newX = Math.round(this.x),
630
+ newY = Math.round(this.y),
631
+ distanceX = Math.abs(newX - this.startX),
632
+ distanceY = Math.abs(newY - this.startY),
633
+ time = 0,
634
+ easing = '';
635
+
636
+ this.isInTransition = 0;
637
+ this.initiated = 0;
638
+ this.endTime = utils.getTime();
639
+
640
+ // reset if we are outside of the boundaries
641
+ if ( this.resetPosition(this.options.bounceTime) ) {
642
+ return;
643
+ }
644
+
645
+ this.scrollTo(newX, newY); // ensures that the last position is rounded
646
+
647
+ // we scrolled less than 10 pixels
648
+ if ( !this.moved ) {
649
+ if ( this.options.tap ) {
650
+ utils.tap(e, this.options.tap);
651
+ }
652
+
653
+ if ( this.options.click ) {
654
+ utils.click(e);
655
+ }
656
+
657
+ this._execEvent('scrollCancel');
658
+ return;
659
+ }
660
+
661
+ if ( this._events.flick && duration < 200 && distanceX < 100 && distanceY < 100 ) {
662
+ this._execEvent('flick');
663
+ return;
664
+ }
665
+
666
+ // start momentum animation if needed
667
+ if ( this.options.momentum && duration < 300 ) {
668
+ momentumX = this.hasHorizontalScroll ? utils.momentum(this.x, this.startX, duration, this.maxScrollX, this.options.bounce ? this.wrapperWidth : 0, this.options.deceleration) : { destination: newX, duration: 0 };
669
+ momentumY = this.hasVerticalScroll ? utils.momentum(this.y, this.startY, duration, this.maxScrollY, this.options.bounce ? this.wrapperHeight : 0, this.options.deceleration) : { destination: newY, duration: 0 };
670
+ newX = momentumX.destination;
671
+ newY = momentumY.destination;
672
+ time = Math.max(momentumX.duration, momentumY.duration);
673
+ this.isInTransition = 1;
674
+ }
675
+
676
+
677
+ if ( this.options.snap ) {
678
+ var snap = this._nearestSnap(newX, newY);
679
+ this.currentPage = snap;
680
+ time = this.options.snapSpeed || Math.max(
681
+ Math.max(
682
+ Math.min(Math.abs(newX - snap.x), 1000),
683
+ Math.min(Math.abs(newY - snap.y), 1000)
684
+ ), 300);
685
+ newX = snap.x;
686
+ newY = snap.y;
687
+
688
+ this.directionX = 0;
689
+ this.directionY = 0;
690
+ easing = this.options.bounceEasing;
691
+ }
692
+
693
+ // INSERT POINT: _end
694
+
695
+ if ( newX != this.x || newY != this.y ) {
696
+ // change easing function when scroller goes out of the boundaries
697
+ if ( newX > 0 || newX < this.maxScrollX || newY > 0 || newY < this.maxScrollY ) {
698
+ easing = utils.ease.quadratic;
699
+ }
700
+
701
+ this.scrollTo(newX, newY, time, easing);
702
+ return;
703
+ }
704
+
705
+ this._execEvent('scrollEnd');
706
+ },
707
+
708
+ _resize: function () {
709
+ var that = this;
710
+
711
+ clearTimeout(this.resizeTimeout);
712
+
713
+ this.resizeTimeout = setTimeout(function () {
714
+ that.refresh();
715
+ }, this.options.resizePolling);
716
+ },
717
+
718
+ resetPosition: function (time) {
719
+ var x = this.x,
720
+ y = this.y;
721
+
722
+ time = time || 0;
723
+
724
+ if ( !this.hasHorizontalScroll || this.x > 0 ) {
725
+ x = 0;
726
+ } else if ( this.x < this.maxScrollX ) {
727
+ x = this.maxScrollX;
728
+ }
729
+
730
+ if ( !this.hasVerticalScroll || this.y > 0 ) {
731
+ y = 0;
732
+ } else if ( this.y < this.maxScrollY ) {
733
+ y = this.maxScrollY;
734
+ }
735
+
736
+ if ( x == this.x && y == this.y ) {
737
+ return false;
738
+ }
739
+
740
+ this.scrollTo(x, y, time, this.options.bounceEasing);
741
+
742
+ return true;
743
+ },
744
+
745
+ disable: function () {
746
+ this.enabled = false;
747
+ },
748
+
749
+ enable: function () {
750
+ this.enabled = true;
751
+ },
752
+
753
+ refresh: function () {
754
+
755
+ utils.getRect(this.wrapper); // Force reflow
756
+
757
+ this.wrapperWidth = this.wrapper.clientWidth;
758
+ this.wrapperHeight = this.wrapper.clientHeight;
759
+
760
+ var rect = utils.getRect(this.scroller);
761
+ /* REPLACE START: refresh */
762
+
763
+ this.scrollerWidth = rect.width;
764
+ this.scrollerHeight = rect.height;
765
+
766
+ this.maxScrollX = this.wrapperWidth - this.scrollerWidth;
767
+ this.maxScrollY = this.wrapperHeight - this.scrollerHeight;
768
+
769
+ /* REPLACE END: refresh */
770
+
771
+ this.hasHorizontalScroll = this.options.scrollX && this.maxScrollX < 0;
772
+ this.hasVerticalScroll = this.options.scrollY && this.maxScrollY < 0;
773
+
774
+ if ( !this.hasHorizontalScroll ) {
775
+ this.maxScrollX = 0;
776
+ this.scrollerWidth = this.wrapperWidth;
777
+ }
778
+
779
+ if ( !this.hasVerticalScroll ) {
780
+ this.maxScrollY = 0;
781
+ this.scrollerHeight = this.wrapperHeight;
782
+ }
783
+
784
+ this.endTime = 0;
785
+ this.directionX = 0;
786
+ this.directionY = 0;
787
+
788
+ if(utils.hasPointer && !this.options.disablePointer) {
789
+ // The wrapper should have `touchAction` property for using pointerEvent.
790
+ this.wrapper.style[utils.style.touchAction] = utils.getTouchAction(this.options.eventPassthrough, true);
791
+
792
+ // case. not support 'pinch-zoom'
793
+ // https://github.com/cubiq/iscroll/issues/1118#issuecomment-270057583
794
+ if (!this.wrapper.style[utils.style.touchAction]) {
795
+ this.wrapper.style[utils.style.touchAction] = utils.getTouchAction(this.options.eventPassthrough, false);
796
+ }
797
+ }
798
+ this.wrapperOffset = utils.offset(this.wrapper);
799
+
800
+ this._execEvent('refresh');
801
+
802
+ this.resetPosition();
803
+
804
+ // INSERT POINT: _refresh
805
+
806
+ },
807
+
808
+ on: function (type, fn) {
809
+ if ( !this._events[type] ) {
810
+ this._events[type] = [];
811
+ }
812
+
813
+ this._events[type].push(fn);
814
+ },
815
+
816
+ off: function (type, fn) {
817
+ if ( !this._events[type] ) {
818
+ return;
819
+ }
820
+
821
+ var index = this._events[type].indexOf(fn);
822
+
823
+ if ( index > -1 ) {
824
+ this._events[type].splice(index, 1);
825
+ }
826
+ },
827
+
828
+ _execEvent: function (type) {
829
+ if ( !this._events[type] ) {
830
+ return;
831
+ }
832
+
833
+ var i = 0,
834
+ l = this._events[type].length;
835
+
836
+ if ( !l ) {
837
+ return;
838
+ }
839
+
840
+ for ( ; i < l; i++ ) {
841
+ this._events[type][i].apply(this, [].slice.call(arguments, 1));
842
+ }
843
+ },
844
+
845
+ scrollBy: function (x, y, time, easing) {
846
+ x = this.x + x;
847
+ y = this.y + y;
848
+ time = time || 0;
849
+
850
+ this.scrollTo(x, y, time, easing);
851
+ },
852
+
853
+ scrollTo: function (x, y, time, easing) {
854
+ easing = easing || utils.ease.circular;
855
+
856
+ this.isInTransition = this.options.useTransition && time > 0;
857
+ var transitionType = this.options.useTransition && easing.style;
858
+ if ( !time || transitionType ) {
859
+ if(transitionType) {
860
+ this._transitionTimingFunction(easing.style);
861
+ this._transitionTime(time);
862
+ }
863
+ this._translate(x, y);
864
+ } else {
865
+ this._animate(x, y, time, easing.fn);
866
+ }
867
+ },
868
+
869
+ scrollToElement: function (el, time, offsetX, offsetY, easing) {
870
+ el = el.nodeType ? el : this.scroller.querySelector(el);
871
+
872
+ if ( !el ) {
873
+ return;
874
+ }
875
+
876
+ var pos = utils.offset(el);
877
+
878
+ pos.left -= this.wrapperOffset.left;
879
+ pos.top -= this.wrapperOffset.top;
880
+
881
+ // if offsetX/Y are true we center the element to the screen
882
+ var elRect = utils.getRect(el);
883
+ var wrapperRect = utils.getRect(this.wrapper);
884
+ if ( offsetX === true ) {
885
+ offsetX = Math.round(elRect.width / 2 - wrapperRect.width / 2);
886
+ }
887
+ if ( offsetY === true ) {
888
+ offsetY = Math.round(elRect.height / 2 - wrapperRect.height / 2);
889
+ }
890
+
891
+ pos.left -= offsetX || 0;
892
+ pos.top -= offsetY || 0;
893
+
894
+ pos.left = pos.left > 0 ? 0 : pos.left < this.maxScrollX ? this.maxScrollX : pos.left;
895
+ pos.top = pos.top > 0 ? 0 : pos.top < this.maxScrollY ? this.maxScrollY : pos.top;
896
+
897
+ time = time === undefined || time === null || time === 'auto' ? Math.max(Math.abs(this.x-pos.left), Math.abs(this.y-pos.top)) : time;
898
+
899
+ this.scrollTo(pos.left, pos.top, time, easing);
900
+ },
901
+
902
+ _transitionTime: function (time) {
903
+ if (!this.options.useTransition) {
904
+ return;
905
+ }
906
+ time = time || 0;
907
+ var durationProp = utils.style.transitionDuration;
908
+ if(!durationProp) {
909
+ return;
910
+ }
911
+
912
+ this.scrollerStyle[durationProp] = time + 'ms';
913
+
914
+ if ( !time && utils.isBadAndroid ) {
915
+ this.scrollerStyle[durationProp] = '0.0001ms';
916
+ // remove 0.0001ms
917
+ var self = this;
918
+ rAF(function() {
919
+ if(self.scrollerStyle[durationProp] === '0.0001ms') {
920
+ self.scrollerStyle[durationProp] = '0s';
921
+ }
922
+ });
923
+ }
924
+
925
+
926
+ if ( this.indicators ) {
927
+ for ( var i = this.indicators.length; i--; ) {
928
+ this.indicators[i].transitionTime(time);
929
+ }
930
+ }
931
+
932
+
933
+ // INSERT POINT: _transitionTime
934
+
935
+ },
936
+
937
+ _transitionTimingFunction: function (easing) {
938
+ this.scrollerStyle[utils.style.transitionTimingFunction] = easing;
939
+
940
+
941
+ if ( this.indicators ) {
942
+ for ( var i = this.indicators.length; i--; ) {
943
+ this.indicators[i].transitionTimingFunction(easing);
944
+ }
945
+ }
946
+
947
+
948
+ // INSERT POINT: _transitionTimingFunction
949
+
950
+ },
951
+
952
+ _translate: function (x, y) {
953
+ if ( this.options.useTransform ) {
954
+
955
+ /* REPLACE START: _translate */
956
+
957
+ this.scrollerStyle[utils.style.transform] = 'translate(' + x + 'px,' + y + 'px)' + this.translateZ;
958
+
959
+ /* REPLACE END: _translate */
960
+
961
+ } else {
962
+ x = Math.round(x);
963
+ y = Math.round(y);
964
+ this.scrollerStyle.left = x + 'px';
965
+ this.scrollerStyle.top = y + 'px';
966
+ }
967
+
968
+ this.x = x;
969
+ this.y = y;
970
+
971
+
972
+ if ( this.indicators ) {
973
+ for ( var i = this.indicators.length; i--; ) {
974
+ this.indicators[i].updatePosition();
975
+ }
976
+ }
977
+
978
+
979
+ // INSERT POINT: _translate
980
+
981
+ },
982
+
983
+ _initEvents: function (remove) {
984
+ var eventType = remove ? utils.removeEvent : utils.addEvent,
985
+ target = this.options.bindToWrapper ? this.wrapper : window;
986
+
987
+ eventType(window, 'orientationchange', this);
988
+ eventType(window, 'resize', this);
989
+
990
+ if ( this.options.click ) {
991
+ eventType(this.wrapper, 'click', this, true);
992
+ }
993
+
994
+ if ( !this.options.disableMouse ) {
995
+ eventType(this.wrapper, 'mousedown', this);
996
+ eventType(target, 'mousemove', this);
997
+ eventType(target, 'mousecancel', this);
998
+ eventType(target, 'mouseup', this);
999
+ }
1000
+
1001
+ if ( utils.hasPointer && !this.options.disablePointer ) {
1002
+ eventType(this.wrapper, utils.prefixPointerEvent('pointerdown'), this);
1003
+ eventType(target, utils.prefixPointerEvent('pointermove'), this);
1004
+ eventType(target, utils.prefixPointerEvent('pointercancel'), this);
1005
+ eventType(target, utils.prefixPointerEvent('pointerup'), this);
1006
+ }
1007
+
1008
+ if ( utils.hasTouch && !this.options.disableTouch ) {
1009
+ eventType(this.wrapper, 'touchstart', this);
1010
+ eventType(target, 'touchmove', this);
1011
+ eventType(target, 'touchcancel', this);
1012
+ eventType(target, 'touchend', this);
1013
+ }
1014
+
1015
+ eventType(this.scroller, 'transitionend', this);
1016
+ eventType(this.scroller, 'webkitTransitionEnd', this);
1017
+ eventType(this.scroller, 'oTransitionEnd', this);
1018
+ eventType(this.scroller, 'MSTransitionEnd', this);
1019
+ },
1020
+
1021
+ getComputedPosition: function () {
1022
+ var matrix = window.getComputedStyle(this.scroller, null),
1023
+ x, y;
1024
+
1025
+ if ( this.options.useTransform ) {
1026
+ matrix = matrix[utils.style.transform].split(')')[0].split(', ');
1027
+ x = +(matrix[12] || matrix[4]);
1028
+ y = +(matrix[13] || matrix[5]);
1029
+ } else {
1030
+ x = +matrix.left.replace(/[^-\d.]/g, '');
1031
+ y = +matrix.top.replace(/[^-\d.]/g, '');
1032
+ }
1033
+
1034
+ return { x: x, y: y };
1035
+ },
1036
+ _initIndicators: function () {
1037
+ var interactive = this.options.interactiveScrollbars,
1038
+ customStyle = typeof this.options.scrollbars != 'string',
1039
+ indicators = [],
1040
+ indicator;
1041
+
1042
+ var that = this;
1043
+
1044
+ this.indicators = [];
1045
+
1046
+ if ( this.options.scrollbars ) {
1047
+ // Vertical scrollbar
1048
+ if ( this.options.scrollY ) {
1049
+ indicator = {
1050
+ el: createDefaultScrollbar('v', interactive, this.options.scrollbars),
1051
+ interactive: interactive,
1052
+ defaultScrollbars: true,
1053
+ customStyle: customStyle,
1054
+ resize: this.options.resizeScrollbars,
1055
+ shrink: this.options.shrinkScrollbars,
1056
+ fade: this.options.fadeScrollbars,
1057
+ listenX: false
1058
+ };
1059
+
1060
+ this.wrapper.appendChild(indicator.el);
1061
+ indicators.push(indicator);
1062
+ }
1063
+
1064
+ // Horizontal scrollbar
1065
+ if ( this.options.scrollX ) {
1066
+ indicator = {
1067
+ el: createDefaultScrollbar('h', interactive, this.options.scrollbars),
1068
+ interactive: interactive,
1069
+ defaultScrollbars: true,
1070
+ customStyle: customStyle,
1071
+ resize: this.options.resizeScrollbars,
1072
+ shrink: this.options.shrinkScrollbars,
1073
+ fade: this.options.fadeScrollbars,
1074
+ listenY: false
1075
+ };
1076
+
1077
+ this.wrapper.appendChild(indicator.el);
1078
+ indicators.push(indicator);
1079
+ }
1080
+ }
1081
+
1082
+ if ( this.options.indicators ) {
1083
+ // TODO: check concat compatibility
1084
+ indicators = indicators.concat(this.options.indicators);
1085
+ }
1086
+
1087
+ for ( var i = indicators.length; i--; ) {
1088
+ this.indicators.push( new Indicator(this, indicators[i]) );
1089
+ }
1090
+
1091
+ // TODO: check if we can use array.map (wide compatibility and performance issues)
1092
+ function _indicatorsMap (fn) {
1093
+ if (that.indicators) {
1094
+ for ( var i = that.indicators.length; i--; ) {
1095
+ fn.call(that.indicators[i]);
1096
+ }
1097
+ }
1098
+ }
1099
+
1100
+ if ( this.options.fadeScrollbars ) {
1101
+ this.on('scrollEnd', function () {
1102
+ _indicatorsMap(function () {
1103
+ this.fade();
1104
+ });
1105
+ });
1106
+
1107
+ this.on('scrollCancel', function () {
1108
+ _indicatorsMap(function () {
1109
+ this.fade();
1110
+ });
1111
+ });
1112
+
1113
+ this.on('scrollStart', function () {
1114
+ _indicatorsMap(function () {
1115
+ this.fade(1);
1116
+ });
1117
+ });
1118
+
1119
+ this.on('beforeScrollStart', function () {
1120
+ _indicatorsMap(function () {
1121
+ this.fade(1, true);
1122
+ });
1123
+ });
1124
+ }
1125
+
1126
+
1127
+ this.on('refresh', function () {
1128
+ _indicatorsMap(function () {
1129
+ this.refresh();
1130
+ });
1131
+ });
1132
+
1133
+ this.on('destroy', function () {
1134
+ _indicatorsMap(function () {
1135
+ this.destroy();
1136
+ });
1137
+
1138
+ delete this.indicators;
1139
+ });
1140
+ },
1141
+
1142
+ _initWheel: function () {
1143
+ utils.addEvent(this.wrapper, 'wheel', this);
1144
+ utils.addEvent(this.wrapper, 'mousewheel', this);
1145
+ utils.addEvent(this.wrapper, 'DOMMouseScroll', this);
1146
+
1147
+ this.on('destroy', function () {
1148
+ clearTimeout(this.wheelTimeout);
1149
+ this.wheelTimeout = null;
1150
+ utils.removeEvent(this.wrapper, 'wheel', this);
1151
+ utils.removeEvent(this.wrapper, 'mousewheel', this);
1152
+ utils.removeEvent(this.wrapper, 'DOMMouseScroll', this);
1153
+ });
1154
+ },
1155
+
1156
+ _wheel: function (e) {
1157
+ if ( !this.enabled ) {
1158
+ return;
1159
+ }
1160
+
1161
+ e.preventDefault();
1162
+
1163
+ var wheelDeltaX, wheelDeltaY,
1164
+ newX, newY,
1165
+ that = this;
1166
+
1167
+ if ( this.wheelTimeout === undefined ) {
1168
+ that._execEvent('scrollStart');
1169
+ }
1170
+
1171
+ // Execute the scrollEnd event after 400ms the wheel stopped scrolling
1172
+ clearTimeout(this.wheelTimeout);
1173
+ this.wheelTimeout = setTimeout(function () {
1174
+ if(!that.options.snap) {
1175
+ that._execEvent('scrollEnd');
1176
+ }
1177
+ that.wheelTimeout = undefined;
1178
+ }, 400);
1179
+
1180
+ if ( 'deltaX' in e ) {
1181
+ if (e.deltaMode === 1) {
1182
+ wheelDeltaX = -e.deltaX * this.options.mouseWheelSpeed;
1183
+ wheelDeltaY = -e.deltaY * this.options.mouseWheelSpeed;
1184
+ } else {
1185
+ wheelDeltaX = -e.deltaX;
1186
+ wheelDeltaY = -e.deltaY;
1187
+ }
1188
+ } else if ( 'wheelDeltaX' in e ) {
1189
+ wheelDeltaX = e.wheelDeltaX / 120 * this.options.mouseWheelSpeed;
1190
+ wheelDeltaY = e.wheelDeltaY / 120 * this.options.mouseWheelSpeed;
1191
+ } else if ( 'wheelDelta' in e ) {
1192
+ wheelDeltaX = wheelDeltaY = e.wheelDelta / 120 * this.options.mouseWheelSpeed;
1193
+ } else if ( 'detail' in e ) {
1194
+ wheelDeltaX = wheelDeltaY = -e.detail / 3 * this.options.mouseWheelSpeed;
1195
+ } else {
1196
+ return;
1197
+ }
1198
+
1199
+ wheelDeltaX *= this.options.invertWheelDirection;
1200
+ wheelDeltaY *= this.options.invertWheelDirection;
1201
+
1202
+ if ( !this.hasVerticalScroll ) {
1203
+ wheelDeltaX = wheelDeltaY;
1204
+ wheelDeltaY = 0;
1205
+ }
1206
+
1207
+ if ( this.options.snap ) {
1208
+ newX = this.currentPage.pageX;
1209
+ newY = this.currentPage.pageY;
1210
+
1211
+ if ( wheelDeltaX > 0 ) {
1212
+ newX--;
1213
+ } else if ( wheelDeltaX < 0 ) {
1214
+ newX++;
1215
+ }
1216
+
1217
+ if ( wheelDeltaY > 0 ) {
1218
+ newY--;
1219
+ } else if ( wheelDeltaY < 0 ) {
1220
+ newY++;
1221
+ }
1222
+
1223
+ this.goToPage(newX, newY);
1224
+
1225
+ return;
1226
+ }
1227
+
1228
+ newX = this.x + Math.round(this.hasHorizontalScroll ? wheelDeltaX : 0);
1229
+ newY = this.y + Math.round(this.hasVerticalScroll ? wheelDeltaY : 0);
1230
+
1231
+ this.directionX = wheelDeltaX > 0 ? -1 : wheelDeltaX < 0 ? 1 : 0;
1232
+ this.directionY = wheelDeltaY > 0 ? -1 : wheelDeltaY < 0 ? 1 : 0;
1233
+
1234
+ if ( newX > 0 ) {
1235
+ newX = 0;
1236
+ } else if ( newX < this.maxScrollX ) {
1237
+ newX = this.maxScrollX;
1238
+ }
1239
+
1240
+ if ( newY > 0 ) {
1241
+ newY = 0;
1242
+ } else if ( newY < this.maxScrollY ) {
1243
+ newY = this.maxScrollY;
1244
+ }
1245
+
1246
+ this.scrollTo(newX, newY, 0);
1247
+
1248
+ // INSERT POINT: _wheel
1249
+ },
1250
+
1251
+ _initSnap: function () {
1252
+ this.currentPage = {};
1253
+
1254
+ if ( typeof this.options.snap == 'string' ) {
1255
+ this.options.snap = this.scroller.querySelectorAll(this.options.snap);
1256
+ }
1257
+
1258
+ this.on('refresh', function () {
1259
+ var i = 0, l,
1260
+ m = 0, n,
1261
+ cx, cy,
1262
+ x = 0, y,
1263
+ stepX = this.options.snapStepX || this.wrapperWidth,
1264
+ stepY = this.options.snapStepY || this.wrapperHeight,
1265
+ el,
1266
+ rect;
1267
+
1268
+ this.pages = [];
1269
+
1270
+ if ( !this.wrapperWidth || !this.wrapperHeight || !this.scrollerWidth || !this.scrollerHeight ) {
1271
+ return;
1272
+ }
1273
+
1274
+ if ( this.options.snap === true ) {
1275
+ cx = Math.round( stepX / 2 );
1276
+ cy = Math.round( stepY / 2 );
1277
+
1278
+ while ( x > -this.scrollerWidth ) {
1279
+ this.pages[i] = [];
1280
+ l = 0;
1281
+ y = 0;
1282
+
1283
+ while ( y > -this.scrollerHeight ) {
1284
+ this.pages[i][l] = {
1285
+ x: Math.max(x, this.maxScrollX),
1286
+ y: Math.max(y, this.maxScrollY),
1287
+ width: stepX,
1288
+ height: stepY,
1289
+ cx: x - cx,
1290
+ cy: y - cy
1291
+ };
1292
+
1293
+ y -= stepY;
1294
+ l++;
1295
+ }
1296
+
1297
+ x -= stepX;
1298
+ i++;
1299
+ }
1300
+ } else {
1301
+ el = this.options.snap;
1302
+ l = el.length;
1303
+ n = -1;
1304
+
1305
+ for ( ; i < l; i++ ) {
1306
+ rect = utils.getRect(el[i]);
1307
+ if ( i === 0 || rect.left <= utils.getRect(el[i-1]).left ) {
1308
+ m = 0;
1309
+ n++;
1310
+ }
1311
+
1312
+ if ( !this.pages[m] ) {
1313
+ this.pages[m] = [];
1314
+ }
1315
+
1316
+ x = Math.max(-rect.left, this.maxScrollX);
1317
+ y = Math.max(-rect.top, this.maxScrollY);
1318
+ cx = x - Math.round(rect.width / 2);
1319
+ cy = y - Math.round(rect.height / 2);
1320
+
1321
+ this.pages[m][n] = {
1322
+ x: x,
1323
+ y: y,
1324
+ width: rect.width,
1325
+ height: rect.height,
1326
+ cx: cx,
1327
+ cy: cy
1328
+ };
1329
+
1330
+ if ( x > this.maxScrollX ) {
1331
+ m++;
1332
+ }
1333
+ }
1334
+ }
1335
+
1336
+ this.goToPage(this.currentPage.pageX || 0, this.currentPage.pageY || 0, 0);
1337
+
1338
+ // Update snap threshold if needed
1339
+ if ( this.options.snapThreshold % 1 === 0 ) {
1340
+ this.snapThresholdX = this.options.snapThreshold;
1341
+ this.snapThresholdY = this.options.snapThreshold;
1342
+ } else {
1343
+ this.snapThresholdX = Math.round(this.pages[this.currentPage.pageX][this.currentPage.pageY].width * this.options.snapThreshold);
1344
+ this.snapThresholdY = Math.round(this.pages[this.currentPage.pageX][this.currentPage.pageY].height * this.options.snapThreshold);
1345
+ }
1346
+ });
1347
+
1348
+ this.on('flick', function () {
1349
+ var time = this.options.snapSpeed || Math.max(
1350
+ Math.max(
1351
+ Math.min(Math.abs(this.x - this.startX), 1000),
1352
+ Math.min(Math.abs(this.y - this.startY), 1000)
1353
+ ), 300);
1354
+
1355
+ this.goToPage(
1356
+ this.currentPage.pageX + this.directionX,
1357
+ this.currentPage.pageY + this.directionY,
1358
+ time
1359
+ );
1360
+ });
1361
+ },
1362
+
1363
+ _nearestSnap: function (x, y) {
1364
+ if ( !this.pages.length ) {
1365
+ return { x: 0, y: 0, pageX: 0, pageY: 0 };
1366
+ }
1367
+
1368
+ var i = 0,
1369
+ l = this.pages.length,
1370
+ m = 0;
1371
+
1372
+ // Check if we exceeded the snap threshold
1373
+ if ( Math.abs(x - this.absStartX) < this.snapThresholdX &&
1374
+ Math.abs(y - this.absStartY) < this.snapThresholdY ) {
1375
+ return this.currentPage;
1376
+ }
1377
+
1378
+ if ( x > 0 ) {
1379
+ x = 0;
1380
+ } else if ( x < this.maxScrollX ) {
1381
+ x = this.maxScrollX;
1382
+ }
1383
+
1384
+ if ( y > 0 ) {
1385
+ y = 0;
1386
+ } else if ( y < this.maxScrollY ) {
1387
+ y = this.maxScrollY;
1388
+ }
1389
+
1390
+ for ( ; i < l; i++ ) {
1391
+ if ( x >= this.pages[i][0].cx ) {
1392
+ x = this.pages[i][0].x;
1393
+ break;
1394
+ }
1395
+ }
1396
+
1397
+ l = this.pages[i].length;
1398
+
1399
+ for ( ; m < l; m++ ) {
1400
+ if ( y >= this.pages[0][m].cy ) {
1401
+ y = this.pages[0][m].y;
1402
+ break;
1403
+ }
1404
+ }
1405
+
1406
+ if ( i == this.currentPage.pageX ) {
1407
+ i += this.directionX;
1408
+
1409
+ if ( i < 0 ) {
1410
+ i = 0;
1411
+ } else if ( i >= this.pages.length ) {
1412
+ i = this.pages.length - 1;
1413
+ }
1414
+
1415
+ x = this.pages[i][0].x;
1416
+ }
1417
+
1418
+ if ( m == this.currentPage.pageY ) {
1419
+ m += this.directionY;
1420
+
1421
+ if ( m < 0 ) {
1422
+ m = 0;
1423
+ } else if ( m >= this.pages[0].length ) {
1424
+ m = this.pages[0].length - 1;
1425
+ }
1426
+
1427
+ y = this.pages[0][m].y;
1428
+ }
1429
+
1430
+ return {
1431
+ x: x,
1432
+ y: y,
1433
+ pageX: i,
1434
+ pageY: m
1435
+ };
1436
+ },
1437
+
1438
+ goToPage: function (x, y, time, easing) {
1439
+ easing = easing || this.options.bounceEasing;
1440
+
1441
+ if ( x >= this.pages.length ) {
1442
+ x = this.pages.length - 1;
1443
+ } else if ( x < 0 ) {
1444
+ x = 0;
1445
+ }
1446
+
1447
+ if ( y >= this.pages[x].length ) {
1448
+ y = this.pages[x].length - 1;
1449
+ } else if ( y < 0 ) {
1450
+ y = 0;
1451
+ }
1452
+
1453
+ var posX = this.pages[x][y].x,
1454
+ posY = this.pages[x][y].y;
1455
+
1456
+ time = time === undefined ? this.options.snapSpeed || Math.max(
1457
+ Math.max(
1458
+ Math.min(Math.abs(posX - this.x), 1000),
1459
+ Math.min(Math.abs(posY - this.y), 1000)
1460
+ ), 300) : time;
1461
+
1462
+ this.currentPage = {
1463
+ x: posX,
1464
+ y: posY,
1465
+ pageX: x,
1466
+ pageY: y
1467
+ };
1468
+
1469
+ this.scrollTo(posX, posY, time, easing);
1470
+ },
1471
+
1472
+ next: function (time, easing) {
1473
+ var x = this.currentPage.pageX,
1474
+ y = this.currentPage.pageY;
1475
+
1476
+ x++;
1477
+
1478
+ if ( x >= this.pages.length && this.hasVerticalScroll ) {
1479
+ x = 0;
1480
+ y++;
1481
+ }
1482
+
1483
+ this.goToPage(x, y, time, easing);
1484
+ },
1485
+
1486
+ prev: function (time, easing) {
1487
+ var x = this.currentPage.pageX,
1488
+ y = this.currentPage.pageY;
1489
+
1490
+ x--;
1491
+
1492
+ if ( x < 0 && this.hasVerticalScroll ) {
1493
+ x = 0;
1494
+ y--;
1495
+ }
1496
+
1497
+ this.goToPage(x, y, time, easing);
1498
+ },
1499
+
1500
+ _initKeys: function (e) {
1501
+ // default key bindings
1502
+ var keys = {
1503
+ pageUp: 33,
1504
+ pageDown: 34,
1505
+ end: 35,
1506
+ home: 36,
1507
+ left: 37,
1508
+ up: 38,
1509
+ right: 39,
1510
+ down: 40
1511
+ };
1512
+ var i;
1513
+
1514
+ // if you give me characters I give you keycode
1515
+ if ( typeof this.options.keyBindings == 'object' ) {
1516
+ for ( i in this.options.keyBindings ) {
1517
+ if ( typeof this.options.keyBindings[i] == 'string' ) {
1518
+ this.options.keyBindings[i] = this.options.keyBindings[i].toUpperCase().charCodeAt(0);
1519
+ }
1520
+ }
1521
+ } else {
1522
+ this.options.keyBindings = {};
1523
+ }
1524
+
1525
+ for ( i in keys ) {
1526
+ this.options.keyBindings[i] = this.options.keyBindings[i] || keys[i];
1527
+ }
1528
+
1529
+ utils.addEvent(window, 'keydown', this);
1530
+
1531
+ this.on('destroy', function () {
1532
+ utils.removeEvent(window, 'keydown', this);
1533
+ });
1534
+ },
1535
+
1536
+ _key: function (e) {
1537
+ if ( !this.enabled ) {
1538
+ return;
1539
+ }
1540
+
1541
+ var snap = this.options.snap, // we are using this alot, better to cache it
1542
+ newX = snap ? this.currentPage.pageX : this.x,
1543
+ newY = snap ? this.currentPage.pageY : this.y,
1544
+ now = utils.getTime(),
1545
+ prevTime = this.keyTime || 0,
1546
+ acceleration = 0.250,
1547
+ pos;
1548
+
1549
+ if ( this.options.useTransition && this.isInTransition ) {
1550
+ pos = this.getComputedPosition();
1551
+
1552
+ this._translate(Math.round(pos.x), Math.round(pos.y));
1553
+ this.isInTransition = false;
1554
+ }
1555
+
1556
+ this.keyAcceleration = now - prevTime < 200 ? Math.min(this.keyAcceleration + acceleration, 50) : 0;
1557
+