Subversion Repositories SmartDukaan

Rev

Rev 20684 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
20685 amit.gupta 1
/*! iScroll v5.1.3 ~ (c) 2008-2014 Matteo Spinelli ~ http://cubiq.org/license */
18213 naman 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) {
20685 amit.gupta 51
		return window.MSPointerEvent ? 
52
			'MSPointer' + pointerEvent.charAt(9).toUpperCase() + pointerEvent.substr(10):
18213 naman 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,
20685 amit.gupta 89
		hasPointer: window.PointerEvent || window.MSPointerEvent, // IE10 is prefixed
18213 naman 90
		hasTransition: _prefixStyle('transition') in _elementStyle
91
	});
92
 
20685 amit.gupta 93
	// This should find all Android browsers lower than build 535.19 (both stock browser and webview)
94
	me.isBadAndroid = /Android /.test(window.navigator.appVersion) && !(/Chrome\/\d/.test(window.navigator.appVersion));
18213 naman 95
 
96
	me.extend(me.style = {}, {
97
		transform: _transform,
98
		transitionTimingFunction: _prefixStyle('transitionTimingFunction'),
99
		transitionDuration: _prefixStyle('transitionDuration'),
100
		transitionDelay: _prefixStyle('transitionDelay'),
20685 amit.gupta 101
		transformOrigin: _prefixStyle('transformOrigin')
18213 naman 102
	});
103
 
104
	me.hasClass = function (e, c) {
105
		var re = new RegExp("(^|\\s)" + c + "(\\s|$)");
106
		return re.test(e.className);
107
	};
108
 
109
	me.addClass = function (e, c) {
110
		if ( me.hasClass(e, c) ) {
111
			return;
112
		}
113
 
114
		var newclass = e.className.split(' ');
115
		newclass.push(c);
116
		e.className = newclass.join(' ');
117
	};
118
 
119
	me.removeClass = function (e, c) {
120
		if ( !me.hasClass(e, c) ) {
121
			return;
122
		}
123
 
124
		var re = new RegExp("(^|\\s)" + c + "(\\s|$)", 'g');
125
		e.className = e.className.replace(re, ' ');
126
	};
127
 
128
	me.offset = function (el) {
129
		var left = -el.offsetLeft,
130
			top = -el.offsetTop;
131
 
132
		// jshint -W084
133
		while (el = el.offsetParent) {
134
			left -= el.offsetLeft;
135
			top -= el.offsetTop;
136
		}
137
		// jshint +W084
138
 
139
		return {
140
			left: left,
141
			top: top
142
		};
143
	};
144
 
145
	me.preventDefaultException = function (el, exceptions) {
146
		for ( var i in exceptions ) {
147
			if ( exceptions[i].test(el[i]) ) {
148
				return true;
149
			}
150
		}
151
 
152
		return false;
153
	};
154
 
155
	me.extend(me.eventType = {}, {
156
		touchstart: 1,
157
		touchmove: 1,
158
		touchend: 1,
159
 
160
		mousedown: 2,
161
		mousemove: 2,
162
		mouseup: 2,
163
 
164
		pointerdown: 3,
165
		pointermove: 3,
166
		pointerup: 3,
167
 
168
		MSPointerDown: 3,
169
		MSPointerMove: 3,
170
		MSPointerUp: 3
171
	});
172
 
173
	me.extend(me.ease = {}, {
174
		quadratic: {
175
			style: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
176
			fn: function (k) {
177
				return k * ( 2 - k );
178
			}
179
		},
180
		circular: {
181
			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)
182
			fn: function (k) {
183
				return Math.sqrt( 1 - ( --k * k ) );
184
			}
185
		},
186
		back: {
187
			style: 'cubic-bezier(0.175, 0.885, 0.32, 1.275)',
188
			fn: function (k) {
189
				var b = 4;
190
				return ( k = k - 1 ) * k * ( ( b + 1 ) * k + b ) + 1;
191
			}
192
		},
193
		bounce: {
194
			style: '',
195
			fn: function (k) {
196
				if ( ( k /= 1 ) < ( 1 / 2.75 ) ) {
197
					return 7.5625 * k * k;
198
				} else if ( k < ( 2 / 2.75 ) ) {
199
					return 7.5625 * ( k -= ( 1.5 / 2.75 ) ) * k + 0.75;
200
				} else if ( k < ( 2.5 / 2.75 ) ) {
201
					return 7.5625 * ( k -= ( 2.25 / 2.75 ) ) * k + 0.9375;
202
				} else {
203
					return 7.5625 * ( k -= ( 2.625 / 2.75 ) ) * k + 0.984375;
204
				}
205
			}
206
		},
207
		elastic: {
208
			style: '',
209
			fn: function (k) {
210
				var f = 0.22,
211
					e = 0.4;
212
 
213
				if ( k === 0 ) { return 0; }
214
				if ( k == 1 ) { return 1; }
215
 
216
				return ( e * Math.pow( 2, - 10 * k ) * Math.sin( ( k - f / 4 ) * ( 2 * Math.PI ) / f ) + 1 );
217
			}
218
		}
219
	});
220
 
221
	me.tap = function (e, eventName) {
222
		var ev = document.createEvent('Event');
223
		ev.initEvent(eventName, true, true);
224
		ev.pageX = e.pageX;
225
		ev.pageY = e.pageY;
226
		e.target.dispatchEvent(ev);
227
	};
228
 
229
	me.click = function (e) {
230
		var target = e.target,
231
			ev;
232
 
233
		if ( !(/(SELECT|INPUT|TEXTAREA)/i).test(target.tagName) ) {
20685 amit.gupta 234
			ev = document.createEvent('MouseEvents');
235
			ev.initMouseEvent('click', true, true, e.view, 1,
236
				target.screenX, target.screenY, target.clientX, target.clientY,
237
				e.ctrlKey, e.altKey, e.shiftKey, e.metaKey,
238
				0, null);
239
 
18213 naman 240
			ev._constructed = true;
241
			target.dispatchEvent(ev);
242
		}
243
	};
244
 
245
	return me;
246
})();
20685 amit.gupta 247
 
18213 naman 248
function IScroll (el, options) {
249
	this.wrapper = typeof el == 'string' ? document.querySelector(el) : el;
250
	this.scroller = this.wrapper.children[0];
251
	this.scrollerStyle = this.scroller.style;		// cache style for better performance
252
 
253
	this.options = {
254
 
255
		resizeScrollbars: true,
256
 
257
		mouseWheelSpeed: 20,
258
 
259
		snapThreshold: 0.334,
260
 
20685 amit.gupta 261
// INSERT POINT: OPTIONS 
262
 
18213 naman 263
		startX: 0,
264
		startY: 0,
265
		scrollY: true,
266
		directionLockThreshold: 5,
267
		momentum: true,
268
 
269
		bounce: true,
270
		bounceTime: 600,
271
		bounceEasing: '',
272
 
273
		preventDefault: true,
274
		preventDefaultException: { tagName: /^(INPUT|TEXTAREA|BUTTON|SELECT)$/ },
275
 
276
		HWCompositing: true,
277
		useTransition: true,
20685 amit.gupta 278
		useTransform: true
18213 naman 279
	};
280
 
281
	for ( var i in options ) {
282
		this.options[i] = options[i];
283
	}
284
 
285
	// Normalize options
286
	this.translateZ = this.options.HWCompositing && utils.hasPerspective ? ' translateZ(0)' : '';
287
 
288
	this.options.useTransition = utils.hasTransition && this.options.useTransition;
289
	this.options.useTransform = utils.hasTransform && this.options.useTransform;
290
 
291
	this.options.eventPassthrough = this.options.eventPassthrough === true ? 'vertical' : this.options.eventPassthrough;
292
	this.options.preventDefault = !this.options.eventPassthrough && this.options.preventDefault;
293
 
294
	// If you want eventPassthrough I have to lock one of the axes
295
	this.options.scrollY = this.options.eventPassthrough == 'vertical' ? false : this.options.scrollY;
296
	this.options.scrollX = this.options.eventPassthrough == 'horizontal' ? false : this.options.scrollX;
297
 
298
	// With eventPassthrough we also need lockDirection mechanism
299
	this.options.freeScroll = this.options.freeScroll && !this.options.eventPassthrough;
300
	this.options.directionLockThreshold = this.options.eventPassthrough ? 0 : this.options.directionLockThreshold;
301
 
302
	this.options.bounceEasing = typeof this.options.bounceEasing == 'string' ? utils.ease[this.options.bounceEasing] || utils.ease.circular : this.options.bounceEasing;
303
 
304
	this.options.resizePolling = this.options.resizePolling === undefined ? 60 : this.options.resizePolling;
305
 
306
	if ( this.options.tap === true ) {
307
		this.options.tap = 'tap';
308
	}
309
 
310
	if ( this.options.shrinkScrollbars == 'scale' ) {
311
		this.options.useTransition = false;
312
	}
313
 
314
	this.options.invertWheelDirection = this.options.invertWheelDirection ? -1 : 1;
315
 
316
// INSERT POINT: NORMALIZATION
317
 
20685 amit.gupta 318
	// Some defaults	
18213 naman 319
	this.x = 0;
320
	this.y = 0;
321
	this.directionX = 0;
322
	this.directionY = 0;
323
	this._events = {};
324
 
325
// INSERT POINT: DEFAULTS
326
 
327
	this._init();
328
	this.refresh();
329
 
330
	this.scrollTo(this.options.startX, this.options.startY);
331
	this.enable();
332
}
333
 
334
IScroll.prototype = {
20685 amit.gupta 335
	version: '5.1.3',
18213 naman 336
 
337
	_init: function () {
338
		this._initEvents();
339
 
340
		if ( this.options.scrollbars || this.options.indicators ) {
341
			this._initIndicators();
342
		}
343
 
344
		if ( this.options.mouseWheel ) {
345
			this._initWheel();
346
		}
347
 
348
		if ( this.options.snap ) {
349
			this._initSnap();
350
		}
351
 
352
		if ( this.options.keyBindings ) {
353
			this._initKeys();
354
		}
355
 
356
// INSERT POINT: _init
357
 
358
	},
359
 
360
	destroy: function () {
361
		this._initEvents(true);
20685 amit.gupta 362
 
18213 naman 363
		this._execEvent('destroy');
364
	},
365
 
366
	_transitionEnd: function (e) {
367
		if ( e.target != this.scroller || !this.isInTransition ) {
368
			return;
369
		}
370
 
371
		this._transitionTime();
372
		if ( !this.resetPosition(this.options.bounceTime) ) {
373
			this.isInTransition = false;
374
			this._execEvent('scrollEnd');
375
		}
376
	},
377
 
378
	_start: function (e) {
379
		// React to left mouse button only
380
		if ( utils.eventType[e.type] != 1 ) {
20685 amit.gupta 381
			if ( e.button !== 0 ) {
18213 naman 382
				return;
383
			}
384
		}
385
 
386
		if ( !this.enabled || (this.initiated && utils.eventType[e.type] !== this.initiated) ) {
387
			return;
388
		}
389
 
390
		if ( this.options.preventDefault && !utils.isBadAndroid && !utils.preventDefaultException(e.target, this.options.preventDefaultException) ) {
391
			e.preventDefault();
392
		}
393
 
394
		var point = e.touches ? e.touches[0] : e,
395
			pos;
396
 
397
		this.initiated	= utils.eventType[e.type];
398
		this.moved		= false;
399
		this.distX		= 0;
400
		this.distY		= 0;
401
		this.directionX = 0;
402
		this.directionY = 0;
403
		this.directionLocked = 0;
404
 
20685 amit.gupta 405
		this._transitionTime();
406
 
18213 naman 407
		this.startTime = utils.getTime();
408
 
409
		if ( this.options.useTransition && this.isInTransition ) {
410
			this.isInTransition = false;
411
			pos = this.getComputedPosition();
412
			this._translate(Math.round(pos.x), Math.round(pos.y));
413
			this._execEvent('scrollEnd');
414
		} else if ( !this.options.useTransition && this.isAnimating ) {
415
			this.isAnimating = false;
416
			this._execEvent('scrollEnd');
417
		}
418
 
419
		this.startX    = this.x;
420
		this.startY    = this.y;
421
		this.absStartX = this.x;
422
		this.absStartY = this.y;
423
		this.pointX    = point.pageX;
424
		this.pointY    = point.pageY;
425
 
426
		this._execEvent('beforeScrollStart');
427
	},
428
 
429
	_move: function (e) {
430
		if ( !this.enabled || utils.eventType[e.type] !== this.initiated ) {
431
			return;
432
		}
433
 
434
		if ( this.options.preventDefault ) {	// increases performance on Android? TODO: check!
435
			e.preventDefault();
436
		}
437
 
438
		var point		= e.touches ? e.touches[0] : e,
439
			deltaX		= point.pageX - this.pointX,
440
			deltaY		= point.pageY - this.pointY,
441
			timestamp	= utils.getTime(),
442
			newX, newY,
443
			absDistX, absDistY;
444
 
445
		this.pointX		= point.pageX;
446
		this.pointY		= point.pageY;
447
 
448
		this.distX		+= deltaX;
449
		this.distY		+= deltaY;
450
		absDistX		= Math.abs(this.distX);
451
		absDistY		= Math.abs(this.distY);
452
 
453
		// We need to move at least 10 pixels for the scrolling to initiate
454
		if ( timestamp - this.endTime > 300 && (absDistX < 10 && absDistY < 10) ) {
455
			return;
456
		}
457
 
458
		// If you are scrolling in one direction lock the other
459
		if ( !this.directionLocked && !this.options.freeScroll ) {
460
			if ( absDistX > absDistY + this.options.directionLockThreshold ) {
461
				this.directionLocked = 'h';		// lock horizontally
462
			} else if ( absDistY >= absDistX + this.options.directionLockThreshold ) {
463
				this.directionLocked = 'v';		// lock vertically
464
			} else {
465
				this.directionLocked = 'n';		// no lock
466
			}
467
		}
468
 
469
		if ( this.directionLocked == 'h' ) {
470
			if ( this.options.eventPassthrough == 'vertical' ) {
471
				e.preventDefault();
472
			} else if ( this.options.eventPassthrough == 'horizontal' ) {
473
				this.initiated = false;
474
				return;
475
			}
476
 
477
			deltaY = 0;
478
		} else if ( this.directionLocked == 'v' ) {
479
			if ( this.options.eventPassthrough == 'horizontal' ) {
480
				e.preventDefault();
481
			} else if ( this.options.eventPassthrough == 'vertical' ) {
482
				this.initiated = false;
483
				return;
484
			}
485
 
486
			deltaX = 0;
487
		}
488
 
489
		deltaX = this.hasHorizontalScroll ? deltaX : 0;
490
		deltaY = this.hasVerticalScroll ? deltaY : 0;
491
 
492
		newX = this.x + deltaX;
493
		newY = this.y + deltaY;
494
 
495
		// Slow down if outside of the boundaries
496
		if ( newX > 0 || newX < this.maxScrollX ) {
497
			newX = this.options.bounce ? this.x + deltaX / 3 : newX > 0 ? 0 : this.maxScrollX;
498
		}
499
		if ( newY > 0 || newY < this.maxScrollY ) {
500
			newY = this.options.bounce ? this.y + deltaY / 3 : newY > 0 ? 0 : this.maxScrollY;
501
		}
502
 
503
		this.directionX = deltaX > 0 ? -1 : deltaX < 0 ? 1 : 0;
504
		this.directionY = deltaY > 0 ? -1 : deltaY < 0 ? 1 : 0;
505
 
506
		if ( !this.moved ) {
507
			this._execEvent('scrollStart');
508
		}
509
 
510
		this.moved = true;
511
 
512
		this._translate(newX, newY);
513
 
514
/* REPLACE START: _move */
515
 
516
		if ( timestamp - this.startTime > 300 ) {
517
			this.startTime = timestamp;
518
			this.startX = this.x;
519
			this.startY = this.y;
520
		}
521
 
522
/* REPLACE END: _move */
523
 
524
	},
525
 
526
	_end: function (e) {
527
		if ( !this.enabled || utils.eventType[e.type] !== this.initiated ) {
528
			return;
529
		}
530
 
531
		if ( this.options.preventDefault && !utils.preventDefaultException(e.target, this.options.preventDefaultException) ) {
532
			e.preventDefault();
533
		}
534
 
535
		var point = e.changedTouches ? e.changedTouches[0] : e,
536
			momentumX,
537
			momentumY,
538
			duration = utils.getTime() - this.startTime,
539
			newX = Math.round(this.x),
540
			newY = Math.round(this.y),
541
			distanceX = Math.abs(newX - this.startX),
542
			distanceY = Math.abs(newY - this.startY),
543
			time = 0,
544
			easing = '';
545
 
546
		this.isInTransition = 0;
547
		this.initiated = 0;
548
		this.endTime = utils.getTime();
549
 
550
		// reset if we are outside of the boundaries
551
		if ( this.resetPosition(this.options.bounceTime) ) {
552
			return;
553
		}
554
 
555
		this.scrollTo(newX, newY);	// ensures that the last position is rounded
556
 
557
		// we scrolled less than 10 pixels
558
		if ( !this.moved ) {
559
			if ( this.options.tap ) {
560
				utils.tap(e, this.options.tap);
561
			}
562
 
563
			if ( this.options.click ) {
564
				utils.click(e);
565
			}
566
 
567
			this._execEvent('scrollCancel');
568
			return;
569
		}
570
 
571
		if ( this._events.flick && duration < 200 && distanceX < 100 && distanceY < 100 ) {
572
			this._execEvent('flick');
573
			return;
574
		}
575
 
576
		// start momentum animation if needed
577
		if ( this.options.momentum && duration < 300 ) {
578
			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 };
579
			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 };
580
			newX = momentumX.destination;
581
			newY = momentumY.destination;
582
			time = Math.max(momentumX.duration, momentumY.duration);
583
			this.isInTransition = 1;
584
		}
585
 
586
 
587
		if ( this.options.snap ) {
588
			var snap = this._nearestSnap(newX, newY);
589
			this.currentPage = snap;
590
			time = this.options.snapSpeed || Math.max(
591
					Math.max(
592
						Math.min(Math.abs(newX - snap.x), 1000),
593
						Math.min(Math.abs(newY - snap.y), 1000)
594
					), 300);
595
			newX = snap.x;
596
			newY = snap.y;
597
 
598
			this.directionX = 0;
599
			this.directionY = 0;
600
			easing = this.options.bounceEasing;
601
		}
602
 
603
// INSERT POINT: _end
604
 
605
		if ( newX != this.x || newY != this.y ) {
606
			// change easing function when scroller goes out of the boundaries
607
			if ( newX > 0 || newX < this.maxScrollX || newY > 0 || newY < this.maxScrollY ) {
608
				easing = utils.ease.quadratic;
609
			}
610
 
611
			this.scrollTo(newX, newY, time, easing);
612
			return;
613
		}
614
 
615
		this._execEvent('scrollEnd');
616
	},
617
 
618
	_resize: function () {
619
		var that = this;
620
 
621
		clearTimeout(this.resizeTimeout);
622
 
623
		this.resizeTimeout = setTimeout(function () {
624
			that.refresh();
625
		}, this.options.resizePolling);
626
	},
627
 
628
	resetPosition: function (time) {
629
		var x = this.x,
630
			y = this.y;
631
 
632
		time = time || 0;
633
 
634
		if ( !this.hasHorizontalScroll || this.x > 0 ) {
635
			x = 0;
636
		} else if ( this.x < this.maxScrollX ) {
637
			x = this.maxScrollX;
638
		}
639
 
640
		if ( !this.hasVerticalScroll || this.y > 0 ) {
641
			y = 0;
642
		} else if ( this.y < this.maxScrollY ) {
643
			y = this.maxScrollY;
644
		}
645
 
646
		if ( x == this.x && y == this.y ) {
647
			return false;
648
		}
649
 
650
		this.scrollTo(x, y, time, this.options.bounceEasing);
651
 
652
		return true;
653
	},
654
 
655
	disable: function () {
656
		this.enabled = false;
657
	},
658
 
659
	enable: function () {
660
		this.enabled = true;
661
	},
662
 
663
	refresh: function () {
20685 amit.gupta 664
		var rf = this.wrapper.offsetHeight;		// Force reflow
18213 naman 665
 
666
		this.wrapperWidth	= this.wrapper.clientWidth;
667
		this.wrapperHeight	= this.wrapper.clientHeight;
668
 
669
/* REPLACE START: refresh */
670
 
20685 amit.gupta 671
		this.scrollerWidth	= this.scroller.offsetWidth;
672
		this.scrollerHeight	= this.scroller.offsetHeight;
18213 naman 673
 
674
		this.maxScrollX		= this.wrapperWidth - this.scrollerWidth;
675
		this.maxScrollY		= this.wrapperHeight - this.scrollerHeight;
676
 
677
/* REPLACE END: refresh */
678
 
679
		this.hasHorizontalScroll	= this.options.scrollX && this.maxScrollX < 0;
680
		this.hasVerticalScroll		= this.options.scrollY && this.maxScrollY < 0;
20685 amit.gupta 681
 
18213 naman 682
		if ( !this.hasHorizontalScroll ) {
683
			this.maxScrollX = 0;
684
			this.scrollerWidth = this.wrapperWidth;
685
		}
686
 
687
		if ( !this.hasVerticalScroll ) {
688
			this.maxScrollY = 0;
689
			this.scrollerHeight = this.wrapperHeight;
690
		}
691
 
692
		this.endTime = 0;
693
		this.directionX = 0;
694
		this.directionY = 0;
695
 
696
		this.wrapperOffset = utils.offset(this.wrapper);
697
 
698
		this._execEvent('refresh');
699
 
700
		this.resetPosition();
701
 
702
// INSERT POINT: _refresh
703
 
20685 amit.gupta 704
	},
18213 naman 705
 
706
	on: function (type, fn) {
707
		if ( !this._events[type] ) {
708
			this._events[type] = [];
709
		}
710
 
711
		this._events[type].push(fn);
712
	},
713
 
714
	off: function (type, fn) {
715
		if ( !this._events[type] ) {
716
			return;
717
		}
718
 
719
		var index = this._events[type].indexOf(fn);
720
 
721
		if ( index > -1 ) {
722
			this._events[type].splice(index, 1);
723
		}
724
	},
725
 
726
	_execEvent: function (type) {
727
		if ( !this._events[type] ) {
728
			return;
729
		}
730
 
731
		var i = 0,
732
			l = this._events[type].length;
733
 
734
		if ( !l ) {
735
			return;
736
		}
737
 
738
		for ( ; i < l; i++ ) {
739
			this._events[type][i].apply(this, [].slice.call(arguments, 1));
740
		}
741
	},
742
 
743
	scrollBy: function (x, y, time, easing) {
744
		x = this.x + x;
745
		y = this.y + y;
746
		time = time || 0;
747
 
748
		this.scrollTo(x, y, time, easing);
749
	},
750
 
751
	scrollTo: function (x, y, time, easing) {
752
		easing = easing || utils.ease.circular;
753
 
754
		this.isInTransition = this.options.useTransition && time > 0;
20685 amit.gupta 755
 
756
		if ( !time || (this.options.useTransition && easing.style) ) {
757
			this._transitionTimingFunction(easing.style);
758
			this._transitionTime(time);
18213 naman 759
			this._translate(x, y);
760
		} else {
761
			this._animate(x, y, time, easing.fn);
762
		}
763
	},
764
 
765
	scrollToElement: function (el, time, offsetX, offsetY, easing) {
766
		el = el.nodeType ? el : this.scroller.querySelector(el);
767
 
768
		if ( !el ) {
769
			return;
770
		}
771
 
772
		var pos = utils.offset(el);
773
 
774
		pos.left -= this.wrapperOffset.left;
775
		pos.top  -= this.wrapperOffset.top;
776
 
777
		// if offsetX/Y are true we center the element to the screen
778
		if ( offsetX === true ) {
20685 amit.gupta 779
			offsetX = Math.round(el.offsetWidth / 2 - this.wrapper.offsetWidth / 2);
18213 naman 780
		}
781
		if ( offsetY === true ) {
20685 amit.gupta 782
			offsetY = Math.round(el.offsetHeight / 2 - this.wrapper.offsetHeight / 2);
18213 naman 783
		}
784
 
785
		pos.left -= offsetX || 0;
786
		pos.top  -= offsetY || 0;
787
 
788
		pos.left = pos.left > 0 ? 0 : pos.left < this.maxScrollX ? this.maxScrollX : pos.left;
789
		pos.top  = pos.top  > 0 ? 0 : pos.top  < this.maxScrollY ? this.maxScrollY : pos.top;
790
 
791
		time = time === undefined || time === null || time === 'auto' ? Math.max(Math.abs(this.x-pos.left), Math.abs(this.y-pos.top)) : time;
792
 
793
		this.scrollTo(pos.left, pos.top, time, easing);
794
	},
795
 
796
	_transitionTime: function (time) {
797
		time = time || 0;
798
 
20685 amit.gupta 799
		this.scrollerStyle[utils.style.transitionDuration] = time + 'ms';
18213 naman 800
 
801
		if ( !time && utils.isBadAndroid ) {
20685 amit.gupta 802
			this.scrollerStyle[utils.style.transitionDuration] = '0.001s';
18213 naman 803
		}
804
 
805
 
806
		if ( this.indicators ) {
807
			for ( var i = this.indicators.length; i--; ) {
808
				this.indicators[i].transitionTime(time);
809
			}
810
		}
811
 
812
 
813
// INSERT POINT: _transitionTime
814
 
815
	},
816
 
817
	_transitionTimingFunction: function (easing) {
818
		this.scrollerStyle[utils.style.transitionTimingFunction] = easing;
819
 
820
 
821
		if ( this.indicators ) {
822
			for ( var i = this.indicators.length; i--; ) {
823
				this.indicators[i].transitionTimingFunction(easing);
824
			}
825
		}
826
 
827
 
828
// INSERT POINT: _transitionTimingFunction
829
 
830
	},
831
 
832
	_translate: function (x, y) {
833
		if ( this.options.useTransform ) {
834
 
835
/* REPLACE START: _translate */
836
 
837
			this.scrollerStyle[utils.style.transform] = 'translate(' + x + 'px,' + y + 'px)' + this.translateZ;
838
 
839
/* REPLACE END: _translate */
840
 
841
		} else {
842
			x = Math.round(x);
843
			y = Math.round(y);
844
			this.scrollerStyle.left = x + 'px';
845
			this.scrollerStyle.top = y + 'px';
846
		}
847
 
848
		this.x = x;
849
		this.y = y;
850
 
851
 
852
	if ( this.indicators ) {
853
		for ( var i = this.indicators.length; i--; ) {
854
			this.indicators[i].updatePosition();
855
		}
856
	}
857
 
858
 
859
// INSERT POINT: _translate
860
 
861
	},
862
 
863
	_initEvents: function (remove) {
864
		var eventType = remove ? utils.removeEvent : utils.addEvent,
865
			target = this.options.bindToWrapper ? this.wrapper : window;
866
 
867
		eventType(window, 'orientationchange', this);
868
		eventType(window, 'resize', this);
869
 
870
		if ( this.options.click ) {
871
			eventType(this.wrapper, 'click', this, true);
872
		}
873
 
874
		if ( !this.options.disableMouse ) {
875
			eventType(this.wrapper, 'mousedown', this);
876
			eventType(target, 'mousemove', this);
877
			eventType(target, 'mousecancel', this);
878
			eventType(target, 'mouseup', this);
879
		}
880
 
881
		if ( utils.hasPointer && !this.options.disablePointer ) {
882
			eventType(this.wrapper, utils.prefixPointerEvent('pointerdown'), this);
883
			eventType(target, utils.prefixPointerEvent('pointermove'), this);
884
			eventType(target, utils.prefixPointerEvent('pointercancel'), this);
885
			eventType(target, utils.prefixPointerEvent('pointerup'), this);
886
		}
887
 
888
		if ( utils.hasTouch && !this.options.disableTouch ) {
889
			eventType(this.wrapper, 'touchstart', this);
890
			eventType(target, 'touchmove', this);
891
			eventType(target, 'touchcancel', this);
892
			eventType(target, 'touchend', this);
893
		}
894
 
895
		eventType(this.scroller, 'transitionend', this);
896
		eventType(this.scroller, 'webkitTransitionEnd', this);
897
		eventType(this.scroller, 'oTransitionEnd', this);
898
		eventType(this.scroller, 'MSTransitionEnd', this);
899
	},
900
 
901
	getComputedPosition: function () {
902
		var matrix = window.getComputedStyle(this.scroller, null),
903
			x, y;
904
 
905
		if ( this.options.useTransform ) {
906
			matrix = matrix[utils.style.transform].split(')')[0].split(', ');
907
			x = +(matrix[12] || matrix[4]);
908
			y = +(matrix[13] || matrix[5]);
909
		} else {
910
			x = +matrix.left.replace(/[^-\d.]/g, '');
911
			y = +matrix.top.replace(/[^-\d.]/g, '');
912
		}
913
 
914
		return { x: x, y: y };
915
	},
20685 amit.gupta 916
 
18213 naman 917
	_initIndicators: function () {
918
		var interactive = this.options.interactiveScrollbars,
919
			customStyle = typeof this.options.scrollbars != 'string',
920
			indicators = [],
921
			indicator;
922
 
923
		var that = this;
924
 
925
		this.indicators = [];
926
 
927
		if ( this.options.scrollbars ) {
928
			// Vertical scrollbar
929
			if ( this.options.scrollY ) {
930
				indicator = {
931
					el: createDefaultScrollbar('v', interactive, this.options.scrollbars),
932
					interactive: interactive,
933
					defaultScrollbars: true,
934
					customStyle: customStyle,
935
					resize: this.options.resizeScrollbars,
936
					shrink: this.options.shrinkScrollbars,
937
					fade: this.options.fadeScrollbars,
938
					listenX: false
939
				};
940
 
941
				this.wrapper.appendChild(indicator.el);
942
				indicators.push(indicator);
943
			}
944
 
945
			// Horizontal scrollbar
946
			if ( this.options.scrollX ) {
947
				indicator = {
948
					el: createDefaultScrollbar('h', interactive, this.options.scrollbars),
949
					interactive: interactive,
950
					defaultScrollbars: true,
951
					customStyle: customStyle,
952
					resize: this.options.resizeScrollbars,
953
					shrink: this.options.shrinkScrollbars,
954
					fade: this.options.fadeScrollbars,
955
					listenY: false
956
				};
957
 
958
				this.wrapper.appendChild(indicator.el);
959
				indicators.push(indicator);
960
			}
961
		}
962
 
963
		if ( this.options.indicators ) {
964
			// TODO: check concat compatibility
965
			indicators = indicators.concat(this.options.indicators);
966
		}
967
 
968
		for ( var i = indicators.length; i--; ) {
969
			this.indicators.push( new Indicator(this, indicators[i]) );
970
		}
971
 
972
		// TODO: check if we can use array.map (wide compatibility and performance issues)
973
		function _indicatorsMap (fn) {
20685 amit.gupta 974
			for ( var i = that.indicators.length; i--; ) {
975
				fn.call(that.indicators[i]);
18213 naman 976
			}
977
		}
978
 
979
		if ( this.options.fadeScrollbars ) {
980
			this.on('scrollEnd', function () {
981
				_indicatorsMap(function () {
982
					this.fade();
983
				});
984
			});
985
 
986
			this.on('scrollCancel', function () {
987
				_indicatorsMap(function () {
988
					this.fade();
989
				});
990
			});
991
 
992
			this.on('scrollStart', function () {
993
				_indicatorsMap(function () {
994
					this.fade(1);
995
				});
996
			});
997
 
998
			this.on('beforeScrollStart', function () {
999
				_indicatorsMap(function () {
1000
					this.fade(1, true);
1001
				});
1002
			});
1003
		}
1004
 
1005
 
1006
		this.on('refresh', function () {
1007
			_indicatorsMap(function () {
1008
				this.refresh();
1009
			});
1010
		});
1011
 
1012
		this.on('destroy', function () {
1013
			_indicatorsMap(function () {
1014
				this.destroy();
1015
			});
1016
 
1017
			delete this.indicators;
1018
		});
1019
	},
1020
 
1021
	_initWheel: function () {
1022
		utils.addEvent(this.wrapper, 'wheel', this);
1023
		utils.addEvent(this.wrapper, 'mousewheel', this);
1024
		utils.addEvent(this.wrapper, 'DOMMouseScroll', this);
1025
 
1026
		this.on('destroy', function () {
1027
			utils.removeEvent(this.wrapper, 'wheel', this);
1028
			utils.removeEvent(this.wrapper, 'mousewheel', this);
1029
			utils.removeEvent(this.wrapper, 'DOMMouseScroll', this);
1030
		});
1031
	},
1032
 
1033
	_wheel: function (e) {
1034
		if ( !this.enabled ) {
1035
			return;
1036
		}
1037
 
1038
		e.preventDefault();
20685 amit.gupta 1039
		e.stopPropagation();
18213 naman 1040
 
1041
		var wheelDeltaX, wheelDeltaY,
1042
			newX, newY,
1043
			that = this;
1044
 
1045
		if ( this.wheelTimeout === undefined ) {
1046
			that._execEvent('scrollStart');
1047
		}
1048
 
1049
		// Execute the scrollEnd event after 400ms the wheel stopped scrolling
1050
		clearTimeout(this.wheelTimeout);
1051
		this.wheelTimeout = setTimeout(function () {
20685 amit.gupta 1052
			that._execEvent('scrollEnd');
18213 naman 1053
			that.wheelTimeout = undefined;
1054
		}, 400);
1055
 
1056
		if ( 'deltaX' in e ) {
1057
			if (e.deltaMode === 1) {
1058
				wheelDeltaX = -e.deltaX * this.options.mouseWheelSpeed;
1059
				wheelDeltaY = -e.deltaY * this.options.mouseWheelSpeed;
1060
			} else {
1061
				wheelDeltaX = -e.deltaX;
1062
				wheelDeltaY = -e.deltaY;
1063
			}
1064
		} else if ( 'wheelDeltaX' in e ) {
1065
			wheelDeltaX = e.wheelDeltaX / 120 * this.options.mouseWheelSpeed;
1066
			wheelDeltaY = e.wheelDeltaY / 120 * this.options.mouseWheelSpeed;
1067
		} else if ( 'wheelDelta' in e ) {
1068
			wheelDeltaX = wheelDeltaY = e.wheelDelta / 120 * this.options.mouseWheelSpeed;
1069
		} else if ( 'detail' in e ) {
1070
			wheelDeltaX = wheelDeltaY = -e.detail / 3 * this.options.mouseWheelSpeed;
1071
		} else {
1072
			return;
1073
		}
1074
 
1075
		wheelDeltaX *= this.options.invertWheelDirection;
1076
		wheelDeltaY *= this.options.invertWheelDirection;
1077
 
1078
		if ( !this.hasVerticalScroll ) {
1079
			wheelDeltaX = wheelDeltaY;
1080
			wheelDeltaY = 0;
1081
		}
1082
 
1083
		if ( this.options.snap ) {
1084
			newX = this.currentPage.pageX;
1085
			newY = this.currentPage.pageY;
1086
 
1087
			if ( wheelDeltaX > 0 ) {
1088
				newX--;
1089
			} else if ( wheelDeltaX < 0 ) {
1090
				newX++;
1091
			}
1092
 
1093
			if ( wheelDeltaY > 0 ) {
1094
				newY--;
1095
			} else if ( wheelDeltaY < 0 ) {
1096
				newY++;
1097
			}
1098
 
1099
			this.goToPage(newX, newY);
1100
 
1101
			return;
1102
		}
1103
 
1104
		newX = this.x + Math.round(this.hasHorizontalScroll ? wheelDeltaX : 0);
1105
		newY = this.y + Math.round(this.hasVerticalScroll ? wheelDeltaY : 0);
1106
 
1107
		if ( newX > 0 ) {
1108
			newX = 0;
1109
		} else if ( newX < this.maxScrollX ) {
1110
			newX = this.maxScrollX;
1111
		}
1112
 
1113
		if ( newY > 0 ) {
1114
			newY = 0;
1115
		} else if ( newY < this.maxScrollY ) {
1116
			newY = this.maxScrollY;
1117
		}
1118
 
1119
		this.scrollTo(newX, newY, 0);
1120
 
1121
// INSERT POINT: _wheel
1122
	},
1123
 
1124
	_initSnap: function () {
1125
		this.currentPage = {};
1126
 
1127
		if ( typeof this.options.snap == 'string' ) {
1128
			this.options.snap = this.scroller.querySelectorAll(this.options.snap);
1129
		}
1130
 
1131
		this.on('refresh', function () {
1132
			var i = 0, l,
1133
				m = 0, n,
1134
				cx, cy,
1135
				x = 0, y,
1136
				stepX = this.options.snapStepX || this.wrapperWidth,
1137
				stepY = this.options.snapStepY || this.wrapperHeight,
20685 amit.gupta 1138
				el;
18213 naman 1139
 
1140
			this.pages = [];
1141
 
1142
			if ( !this.wrapperWidth || !this.wrapperHeight || !this.scrollerWidth || !this.scrollerHeight ) {
1143
				return;
1144
			}
1145
 
1146
			if ( this.options.snap === true ) {
1147
				cx = Math.round( stepX / 2 );
1148
				cy = Math.round( stepY / 2 );
1149
 
1150
				while ( x > -this.scrollerWidth ) {
1151
					this.pages[i] = [];
1152
					l = 0;
1153
					y = 0;
1154
 
1155
					while ( y > -this.scrollerHeight ) {
1156
						this.pages[i][l] = {
1157
							x: Math.max(x, this.maxScrollX),
1158
							y: Math.max(y, this.maxScrollY),
1159
							width: stepX,
1160
							height: stepY,
1161
							cx: x - cx,
1162
							cy: y - cy
1163
						};
1164
 
1165
						y -= stepY;
1166
						l++;
1167
					}
1168
 
1169
					x -= stepX;
1170
					i++;
1171
				}
1172
			} else {
1173
				el = this.options.snap;
1174
				l = el.length;
1175
				n = -1;
1176
 
1177
				for ( ; i < l; i++ ) {
20685 amit.gupta 1178
					if ( i === 0 || el[i].offsetLeft <= el[i-1].offsetLeft ) {
18213 naman 1179
						m = 0;
1180
						n++;
1181
					}
1182
 
1183
					if ( !this.pages[m] ) {
1184
						this.pages[m] = [];
1185
					}
1186
 
20685 amit.gupta 1187
					x = Math.max(-el[i].offsetLeft, this.maxScrollX);
1188
					y = Math.max(-el[i].offsetTop, this.maxScrollY);
1189
					cx = x - Math.round(el[i].offsetWidth / 2);
1190
					cy = y - Math.round(el[i].offsetHeight / 2);
18213 naman 1191
 
1192
					this.pages[m][n] = {
1193
						x: x,
1194
						y: y,
20685 amit.gupta 1195
						width: el[i].offsetWidth,
1196
						height: el[i].offsetHeight,
18213 naman 1197
						cx: cx,
1198
						cy: cy
1199
					};
1200
 
1201
					if ( x > this.maxScrollX ) {
1202
						m++;
1203
					}
1204
				}
1205
			}
1206
 
1207
			this.goToPage(this.currentPage.pageX || 0, this.currentPage.pageY || 0, 0);
1208
 
1209
			// Update snap threshold if needed
1210
			if ( this.options.snapThreshold % 1 === 0 ) {
1211
				this.snapThresholdX = this.options.snapThreshold;
1212
				this.snapThresholdY = this.options.snapThreshold;
1213
			} else {
1214
				this.snapThresholdX = Math.round(this.pages[this.currentPage.pageX][this.currentPage.pageY].width * this.options.snapThreshold);
1215
				this.snapThresholdY = Math.round(this.pages[this.currentPage.pageX][this.currentPage.pageY].height * this.options.snapThreshold);
1216
			}
1217
		});
1218
 
1219
		this.on('flick', function () {
1220
			var time = this.options.snapSpeed || Math.max(
1221
					Math.max(
1222
						Math.min(Math.abs(this.x - this.startX), 1000),
1223
						Math.min(Math.abs(this.y - this.startY), 1000)
1224
					), 300);
1225
 
1226
			this.goToPage(
1227
				this.currentPage.pageX + this.directionX,
1228
				this.currentPage.pageY + this.directionY,
1229
				time
1230
			);
1231
		});
1232
	},
1233
 
1234
	_nearestSnap: function (x, y) {
1235
		if ( !this.pages.length ) {
1236
			return { x: 0, y: 0, pageX: 0, pageY: 0 };
1237
		}
1238
 
1239
		var i = 0,
1240
			l = this.pages.length,
1241
			m = 0;
1242
 
1243
		// Check if we exceeded the snap threshold
1244
		if ( Math.abs(x - this.absStartX) < this.snapThresholdX &&
1245
			Math.abs(y - this.absStartY) < this.snapThresholdY ) {
1246
			return this.currentPage;
1247
		}
1248
 
1249
		if ( x > 0 ) {
1250
			x = 0;
1251
		} else if ( x < this.maxScrollX ) {
1252
			x = this.maxScrollX;
1253
		}
1254
 
1255
		if ( y > 0 ) {
1256
			y = 0;
1257
		} else if ( y < this.maxScrollY ) {
1258
			y = this.maxScrollY;
1259
		}
1260
 
1261
		for ( ; i < l; i++ ) {
1262
			if ( x >= this.pages[i][0].cx ) {
1263
				x = this.pages[i][0].x;
1264
				break;
1265
			}
1266
		}
1267
 
1268
		l = this.pages[i].length;
1269
 
1270
		for ( ; m < l; m++ ) {
1271
			if ( y >= this.pages[0][m].cy ) {
1272
				y = this.pages[0][m].y;
1273
				break;
1274
			}
1275
		}
1276
 
1277
		if ( i == this.currentPage.pageX ) {
1278
			i += this.directionX;
1279
 
1280
			if ( i < 0 ) {
1281
				i = 0;
1282
			} else if ( i >= this.pages.length ) {
1283
				i = this.pages.length - 1;
1284
			}
1285
 
1286
			x = this.pages[i][0].x;
1287
		}
1288
 
1289
		if ( m == this.currentPage.pageY ) {
1290
			m += this.directionY;
1291
 
1292
			if ( m < 0 ) {
1293
				m = 0;
1294
			} else if ( m >= this.pages[0].length ) {
1295
				m = this.pages[0].length - 1;
1296
			}
1297
 
1298
			y = this.pages[0][m].y;
1299
		}
1300
 
1301
		return {
1302
			x: x,
1303
			y: y,
1304
			pageX: i,
1305
			pageY: m
1306
		};
1307
	},
1308
 
1309
	goToPage: function (x, y, time, easing) {
1310
		easing = easing || this.options.bounceEasing;
1311
 
1312
		if ( x >= this.pages.length ) {
1313
			x = this.pages.length - 1;
1314
		} else if ( x < 0 ) {
1315
			x = 0;
1316
		}
1317
 
1318
		if ( y >= this.pages[x].length ) {
1319
			y = this.pages[x].length - 1;
1320
		} else if ( y < 0 ) {
1321
			y = 0;
1322
		}
1323
 
1324
		var posX = this.pages[x][y].x,
1325
			posY = this.pages[x][y].y;
1326
 
1327
		time = time === undefined ? this.options.snapSpeed || Math.max(
1328
			Math.max(
1329
				Math.min(Math.abs(posX - this.x), 1000),
1330
				Math.min(Math.abs(posY - this.y), 1000)
1331
			), 300) : time;
1332
 
1333
		this.currentPage = {
1334
			x: posX,
1335
			y: posY,
1336
			pageX: x,
1337
			pageY: y
1338
		};
1339
 
1340
		this.scrollTo(posX, posY, time, easing);
1341
	},
1342
 
1343
	next: function (time, easing) {
1344
		var x = this.currentPage.pageX,
1345
			y = this.currentPage.pageY;
1346
 
1347
		x++;
1348
 
1349
		if ( x >= this.pages.length && this.hasVerticalScroll ) {
1350
			x = 0;
1351
			y++;
1352
		}
1353
 
1354
		this.goToPage(x, y, time, easing);
1355
	},
1356
 
1357
	prev: function (time, easing) {
1358
		var x = this.currentPage.pageX,
1359
			y = this.currentPage.pageY;
1360
 
1361
		x--;
1362
 
1363
		if ( x < 0 && this.hasVerticalScroll ) {
1364
			x = 0;
1365
			y--;
1366
		}
1367
 
1368
		this.goToPage(x, y, time, easing);
1369
	},
1370
 
1371
	_initKeys: function (e) {
1372
		// default key bindings
1373
		var keys = {
1374
			pageUp: 33,
1375
			pageDown: 34,
1376
			end: 35,
1377
			home: 36,
1378
			left: 37,
1379
			up: 38,
1380
			right: 39,
1381
			down: 40
1382
		};
1383
		var i;
1384
 
1385
		// if you give me characters I give you keycode
1386
		if ( typeof this.options.keyBindings == 'object' ) {
1387
			for ( i in this.options.keyBindings ) {
1388
				if ( typeof this.options.keyBindings[i] == 'string' ) {
1389
					this.options.keyBindings[i] = this.options.keyBindings[i].toUpperCase().charCodeAt(0);
1390
				}
1391
			}
1392
		} else {
1393
			this.options.keyBindings = {};
1394
		}
1395
 
1396
		for ( i in keys ) {
1397
			this.options.keyBindings[i] = this.options.keyBindings[i] || keys[i];
1398
		}
1399
 
1400
		utils.addEvent(window, 'keydown', this);
1401
 
1402
		this.on('destroy', function () {
1403
			utils.removeEvent(window, 'keydown', this);
1404
		});
1405
	},
1406
 
1407
	_key: function (e) {
1408
		if ( !this.enabled ) {
1409
			return;
1410
		}
1411
 
1412
		var snap = this.options.snap,	// we are using this alot, better to cache it
1413
			newX = snap ? this.currentPage.pageX : this.x,
1414
			newY = snap ? this.currentPage.pageY : this.y,
1415
			now = utils.getTime(),
1416
			prevTime = this.keyTime || 0,
1417
			acceleration = 0.250,
1418
			pos;
1419
 
1420
		if ( this.options.useTransition && this.isInTransition ) {
1421
			pos = this.getComputedPosition();
1422
 
1423
			this._translate(Math.round(pos.x), Math.round(pos.y));
1424
			this.isInTransition = false;
1425
		}
1426
 
1427
		this.keyAcceleration = now - prevTime < 200 ? Math.min(this.keyAcceleration + acceleration, 50) : 0;
1428
 
1429
		switch ( e.keyCode ) {
1430
			case this.options.keyBindings.pageUp:
1431
				if ( this.hasHorizontalScroll && !this.hasVerticalScroll ) {
1432
					newX += snap ? 1 : this.wrapperWidth;
1433
				} else {
1434
					newY += snap ? 1 : this.wrapperHeight;
1435
				}
1436
				break;
1437
			case this.options.keyBindings.pageDown:
1438
				if ( this.hasHorizontalScroll && !this.hasVerticalScroll ) {
1439
					newX -= snap ? 1 : this.wrapperWidth;
1440
				} else {
1441
					newY -= snap ? 1 : this.wrapperHeight;
1442
				}
1443
				break;
1444
			case this.options.keyBindings.end:
1445
				newX = snap ? this.pages.length-1 : this.maxScrollX;
1446
				newY = snap ? this.pages[0].length-1 : this.maxScrollY;
1447
				break;
1448
			case this.options.keyBindings.home:
1449
				newX = 0;
1450
				newY = 0;
1451
				break;
1452
			case this.options.keyBindings.left:
1453
				newX += snap ? -1 : 5 + this.keyAcceleration>>0;
1454
				break;
1455
			case this.options.keyBindings.up:
1456
				newY += snap ? 1 : 5 + this.keyAcceleration>>0;
1457
				break;
1458
			case this.options.keyBindings.right:
1459
				newX -= snap ? -1 : 5 + this.keyAcceleration>>0;
1460
				break;
1461
			case this.options.keyBindings.down:
1462
				newY -= snap ? 1 : 5 + this.keyAcceleration>>0;
1463
				break;
1464
			default:
1465
				return;
1466
		}
1467
 
1468
		if ( snap ) {
1469
			this.goToPage(newX, newY);
1470
			return;
1471
		}
1472
 
1473
		if ( newX > 0 ) {
1474
			newX = 0;
1475
			this.keyAcceleration = 0;
1476
		} else if ( newX < this.maxScrollX ) {
1477
			newX = this.maxScrollX;
1478
			this.keyAcceleration = 0;
1479
		}
1480
 
1481
		if ( newY > 0 ) {
1482
			newY = 0;
1483
			this.keyAcceleration = 0;
1484
		} else if ( newY < this.maxScrollY ) {
1485
			newY = this.maxScrollY;
1486
			this.keyAcceleration = 0;
1487
		}
1488
 
1489
		this.scrollTo(newX, newY, 0);
1490
 
1491
		this.keyTime = now;
1492
	},
1493
 
1494
	_animate: function (destX, destY, duration, easingFn) {
1495
		var that = this,
1496
			startX = this.x,
1497
			startY = this.y,
1498
			startTime = utils.getTime(),
1499
			destTime = startTime + duration;
1500
 
1501
		function step () {
1502
			var now = utils.getTime(),
1503
				newX, newY,
1504
				easing;
1505
 
1506
			if ( now >= destTime ) {
1507
				that.isAnimating = false;
1508
				that._translate(destX, destY);
1509
 
1510
				if ( !that.resetPosition(that.options.bounceTime) ) {
1511
					that._execEvent('scrollEnd');
1512
				}
1513
 
1514
				return;
1515
			}
1516
 
1517
			now = ( now - startTime ) / duration;
1518
			easing = easingFn(now);
1519
			newX = ( destX - startX ) * easing + startX;
1520
			newY = ( destY - startY ) * easing + startY;
1521
			that._translate(newX, newY);
1522
 
1523
			if ( that.isAnimating ) {
1524
				rAF(step);
1525
			}
1526
		}
1527
 
1528
		this.isAnimating = true;
1529
		step();
1530
	},
1531
	handleEvent: function (e) {
1532
		switch ( e.type ) {
1533
			case 'touchstart':
1534
			case 'pointerdown':
1535
			case 'MSPointerDown':
1536
			case 'mousedown':
1537
				this._start(e);
1538
				break;
1539
			case 'touchmove':
1540
			case 'pointermove':
1541
			case 'MSPointerMove':
1542
			case 'mousemove':
1543
				this._move(e);
1544
				break;
1545
			case 'touchend':
1546
			case 'pointerup':
1547
			case 'MSPointerUp':
1548
			case 'mouseup':
1549
			case 'touchcancel':
1550
			case 'pointercancel':
1551
			case 'MSPointerCancel':
1552
			case 'mousecancel':
1553
				this._end(e);
1554
				break;
1555
			case 'orientationchange':
1556
			case 'resize':
1557
				this._resize();
1558
				break;
1559
			case 'transitionend':
1560
			case 'webkitTransitionEnd':
1561
			case 'oTransitionEnd':
1562
			case 'MSTransitionEnd':
1563
				this._transitionEnd(e);
1564
				break;
1565
			case 'wheel':
1566
			case 'DOMMouseScroll':
1567
			case 'mousewheel':
1568
				this._wheel(e);
1569
				break;
1570
			case 'keydown':
1571
				this._key(e);
1572
				break;
1573
			case 'click':
20685 amit.gupta 1574
				if ( !e._constructed ) {
18213 naman 1575
					e.preventDefault();
1576
					e.stopPropagation();
1577
				}
1578
				break;
1579
		}
1580
	}
1581
};
1582
function createDefaultScrollbar (direction, interactive, type) {
1583
	var scrollbar = document.createElement('div'),
1584
		indicator = document.createElement('div');
1585
 
1586
	if ( type === true ) {
1587
		scrollbar.style.cssText = 'position:absolute;z-index:9999';
1588
		indicator.style.cssText = '-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;position:absolute;background:rgba(0,0,0,0.5);border:1px solid rgba(255,255,255,0.9);border-radius:3px';
1589
	}
1590
 
1591
	indicator.className = 'iScrollIndicator';
1592
 
1593
	if ( direction == 'h' ) {
1594
		if ( type === true ) {
1595
			scrollbar.style.cssText += ';height:7px;left:2px;right:2px;bottom:0';
1596
			indicator.style.height = '100%';
1597
		}
1598
		scrollbar.className = 'iScrollHorizontalScrollbar';
1599
	} else {
1600
		if ( type === true ) {
1601
			scrollbar.style.cssText += ';width:7px;bottom:2px;top:2px;right:1px';
1602
			indicator.style.width = '100%';
1603
		}
1604
		scrollbar.className = 'iScrollVerticalScrollbar';
1605
	}
1606
 
1607
	scrollbar.style.cssText += ';overflow:hidden';
1608
 
1609
	if ( !interactive ) {
1610
		scrollbar.style.pointerEvents = 'none';
1611
	}
1612
 
1613
	scrollbar.appendChild(indicator);
1614
 
1615
	return scrollbar;
1616
}
1617
 
1618
function Indicator (scroller, options) {
1619
	this.wrapper = typeof options.el == 'string' ? document.querySelector(options.el) : options.el;
1620
	this.wrapperStyle = this.wrapper.style;
1621
	this.indicator = this.wrapper.children[0];
1622
	this.indicatorStyle = this.indicator.style;
1623
	this.scroller = scroller;
1624
 
1625
	this.options = {
1626
		listenX: true,
1627
		listenY: true,
1628
		interactive: false,
1629
		resize: true,
1630
		defaultScrollbars: false,
1631
		shrink: false,
1632
		fade: false,
1633
		speedRatioX: 0,
1634
		speedRatioY: 0
1635
	};
1636
 
1637
	for ( var i in options ) {
1638
		this.options[i] = options[i];
1639
	}
1640
 
1641
	this.sizeRatioX = 1;
1642
	this.sizeRatioY = 1;
1643
	this.maxPosX = 0;
1644
	this.maxPosY = 0;
1645
 
1646
	if ( this.options.interactive ) {
1647
		if ( !this.options.disableTouch ) {
1648
			utils.addEvent(this.indicator, 'touchstart', this);
1649
			utils.addEvent(window, 'touchend', this);
1650
		}
1651
		if ( !this.options.disablePointer ) {
1652
			utils.addEvent(this.indicator, utils.prefixPointerEvent('pointerdown'), this);
1653
			utils.addEvent(window, utils.prefixPointerEvent('pointerup'), this);
1654
		}
1655
		if ( !this.options.disableMouse ) {
1656
			utils.addEvent(this.indicator, 'mousedown', this);
1657
			utils.addEvent(window, 'mouseup', this);
1658
		}
1659
	}
1660
 
1661
	if ( this.options.fade ) {
1662
		this.wrapperStyle[utils.style.transform] = this.scroller.translateZ;
20685 amit.gupta 1663
		this.wrapperStyle[utils.style.transitionDuration] = utils.isBadAndroid ? '0.001s' : '0ms';
18213 naman 1664
		this.wrapperStyle.opacity = '0';
1665
	}
1666
}
1667
 
1668
Indicator.prototype = {
1669
	handleEvent: function (e) {
1670
		switch ( e.type ) {
1671
			case 'touchstart':
1672
			case 'pointerdown':
1673
			case 'MSPointerDown':
1674
			case 'mousedown':
1675
				this._start(e);
1676
				break;
1677
			case 'touchmove':
1678
			case 'pointermove':
1679
			case 'MSPointerMove':
1680
			case 'mousemove':
1681
				this._move(e);
1682
				break;
1683
			case 'touchend':
1684
			case 'pointerup':
1685
			case 'MSPointerUp':
1686
			case 'mouseup':
1687
			case 'touchcancel':
1688
			case 'pointercancel':
1689
			case 'MSPointerCancel':
1690
			case 'mousecancel':
1691
				this._end(e);
1692
				break;
1693
		}
1694
	},
1695
 
1696
	destroy: function () {
1697
		if ( this.options.interactive ) {
1698
			utils.removeEvent(this.indicator, 'touchstart', this);
1699
			utils.removeEvent(this.indicator, utils.prefixPointerEvent('pointerdown'), this);
1700
			utils.removeEvent(this.indicator, 'mousedown', this);
1701
 
1702
			utils.removeEvent(window, 'touchmove', this);
1703
			utils.removeEvent(window, utils.prefixPointerEvent('pointermove'), this);
1704
			utils.removeEvent(window, 'mousemove', this);
1705
 
1706
			utils.removeEvent(window, 'touchend', this);
1707
			utils.removeEvent(window, utils.prefixPointerEvent('pointerup'), this);
1708
			utils.removeEvent(window, 'mouseup', this);
1709
		}
1710
 
20685 amit.gupta 1711
		if ( this.options.defaultScrollbars ) {
18213 naman 1712
			this.wrapper.parentNode.removeChild(this.wrapper);
1713
		}
1714
	},
1715
 
1716
	_start: function (e) {
1717
		var point = e.touches ? e.touches[0] : e;
1718
 
1719
		e.preventDefault();
1720
		e.stopPropagation();
1721
 
1722
		this.transitionTime();
1723
 
1724
		this.initiated = true;
1725
		this.moved = false;
1726
		this.lastPointX	= point.pageX;
1727
		this.lastPointY	= point.pageY;
1728
 
1729
		this.startTime	= utils.getTime();
1730
 
1731
		if ( !this.options.disableTouch ) {
1732
			utils.addEvent(window, 'touchmove', this);
1733
		}
1734
		if ( !this.options.disablePointer ) {
1735
			utils.addEvent(window, utils.prefixPointerEvent('pointermove'), this);
1736
		}
1737
		if ( !this.options.disableMouse ) {
1738
			utils.addEvent(window, 'mousemove', this);
1739
		}
1740
 
1741
		this.scroller._execEvent('beforeScrollStart');
1742
	},
1743
 
1744
	_move: function (e) {
1745
		var point = e.touches ? e.touches[0] : e,
1746
			deltaX, deltaY,
1747
			newX, newY,
1748
			timestamp = utils.getTime();
1749
 
1750
		if ( !this.moved ) {
1751
			this.scroller._execEvent('scrollStart');
1752
		}
1753
 
1754
		this.moved = true;
1755
 
1756
		deltaX = point.pageX - this.lastPointX;
1757
		this.lastPointX = point.pageX;
1758
 
1759
		deltaY = point.pageY - this.lastPointY;
1760
		this.lastPointY = point.pageY;
1761
 
1762
		newX = this.x + deltaX;
1763
		newY = this.y + deltaY;
1764
 
1765
		this._pos(newX, newY);
1766
 
1767
// INSERT POINT: indicator._move
1768
 
1769
		e.preventDefault();
1770
		e.stopPropagation();
1771
	},
1772
 
1773
	_end: function (e) {
1774
		if ( !this.initiated ) {
1775
			return;
1776
		}
1777
 
1778
		this.initiated = false;
1779
 
1780
		e.preventDefault();
1781
		e.stopPropagation();
1782
 
1783
		utils.removeEvent(window, 'touchmove', this);
1784
		utils.removeEvent(window, utils.prefixPointerEvent('pointermove'), this);
1785
		utils.removeEvent(window, 'mousemove', this);
1786
 
1787
		if ( this.scroller.options.snap ) {
1788
			var snap = this.scroller._nearestSnap(this.scroller.x, this.scroller.y);
1789
 
1790
			var time = this.options.snapSpeed || Math.max(
1791
					Math.max(
1792
						Math.min(Math.abs(this.scroller.x - snap.x), 1000),
1793
						Math.min(Math.abs(this.scroller.y - snap.y), 1000)
1794
					), 300);
1795
 
1796
			if ( this.scroller.x != snap.x || this.scroller.y != snap.y ) {
1797
				this.scroller.directionX = 0;
1798
				this.scroller.directionY = 0;
1799
				this.scroller.currentPage = snap;
1800
				this.scroller.scrollTo(snap.x, snap.y, time, this.scroller.options.bounceEasing);
1801
			}
1802
		}
1803
 
1804
		if ( this.moved ) {
1805
			this.scroller._execEvent('scrollEnd');
1806
		}
1807
	},
1808
 
1809
	transitionTime: function (time) {
1810
		time = time || 0;
20685 amit.gupta 1811
		this.indicatorStyle[utils.style.transitionDuration] = time + 'ms';
18213 naman 1812
 
1813
		if ( !time && utils.isBadAndroid ) {
20685 amit.gupta 1814
			this.indicatorStyle[utils.style.transitionDuration] = '0.001s';
18213 naman 1815
		}
1816
	},
1817
 
1818
	transitionTimingFunction: function (easing) {
1819
		this.indicatorStyle[utils.style.transitionTimingFunction] = easing;
1820
	},
1821
 
1822
	refresh: function () {
1823
		this.transitionTime();
1824
 
1825
		if ( this.options.listenX && !this.options.listenY ) {
1826
			this.indicatorStyle.display = this.scroller.hasHorizontalScroll ? 'block' : 'none';
1827
		} else if ( this.options.listenY && !this.options.listenX ) {
1828
			this.indicatorStyle.display = this.scroller.hasVerticalScroll ? 'block' : 'none';
1829
		} else {
1830
			this.indicatorStyle.display = this.scroller.hasHorizontalScroll || this.scroller.hasVerticalScroll ? 'block' : 'none';
1831
		}
1832
 
1833
		if ( this.scroller.hasHorizontalScroll && this.scroller.hasVerticalScroll ) {
1834
			utils.addClass(this.wrapper, 'iScrollBothScrollbars');
1835
			utils.removeClass(this.wrapper, 'iScrollLoneScrollbar');
1836
 
1837
			if ( this.options.defaultScrollbars && this.options.customStyle ) {
1838
				if ( this.options.listenX ) {
1839
					this.wrapper.style.right = '8px';
1840
				} else {
1841
					this.wrapper.style.bottom = '8px';
1842
				}
1843
			}
1844
		} else {
1845
			utils.removeClass(this.wrapper, 'iScrollBothScrollbars');
1846
			utils.addClass(this.wrapper, 'iScrollLoneScrollbar');
1847
 
1848
			if ( this.options.defaultScrollbars && this.options.customStyle ) {
1849
				if ( this.options.listenX ) {
1850
					this.wrapper.style.right = '2px';
1851
				} else {
1852
					this.wrapper.style.bottom = '2px';
1853
				}
1854
			}
1855
		}
1856
 
20685 amit.gupta 1857
		var r = this.wrapper.offsetHeight;	// force refresh
18213 naman 1858
 
1859
		if ( this.options.listenX ) {
1860
			this.wrapperWidth = this.wrapper.clientWidth;
1861
			if ( this.options.resize ) {
1862
				this.indicatorWidth = Math.max(Math.round(this.wrapperWidth * this.wrapperWidth / (this.scroller.scrollerWidth || this.wrapperWidth || 1)), 8);
1863
				this.indicatorStyle.width = this.indicatorWidth + 'px';
1864
			} else {
1865
				this.indicatorWidth = this.indicator.clientWidth;
1866
			}
1867
 
1868
			this.maxPosX = this.wrapperWidth - this.indicatorWidth;
1869
 
1870
			if ( this.options.shrink == 'clip' ) {
1871
				this.minBoundaryX = -this.indicatorWidth + 8;
1872
				this.maxBoundaryX = this.wrapperWidth - 8;
1873
			} else {
1874
				this.minBoundaryX = 0;
1875
				this.maxBoundaryX = this.maxPosX;
1876
			}
1877
 
20685 amit.gupta 1878
			this.sizeRatioX = this.options.speedRatioX || (this.scroller.maxScrollX && (this.maxPosX / this.scroller.maxScrollX));	
18213 naman 1879
		}
1880
 
1881
		if ( this.options.listenY ) {
1882
			this.wrapperHeight = this.wrapper.clientHeight;
1883
			if ( this.options.resize ) {
1884
				this.indicatorHeight = Math.max(Math.round(this.wrapperHeight * this.wrapperHeight / (this.scroller.scrollerHeight || this.wrapperHeight || 1)), 8);
1885
				this.indicatorStyle.height = this.indicatorHeight + 'px';
1886
			} else {
1887
				this.indicatorHeight = this.indicator.clientHeight;
1888
			}
1889
 
1890
			this.maxPosY = this.wrapperHeight - this.indicatorHeight;
1891
 
1892
			if ( this.options.shrink == 'clip' ) {
1893
				this.minBoundaryY = -this.indicatorHeight + 8;
1894
				this.maxBoundaryY = this.wrapperHeight - 8;
1895
			} else {
1896
				this.minBoundaryY = 0;
1897
				this.maxBoundaryY = this.maxPosY;
1898
			}
1899
 
1900
			this.maxPosY = this.wrapperHeight - this.indicatorHeight;
1901
			this.sizeRatioY = this.options.speedRatioY || (this.scroller.maxScrollY && (this.maxPosY / this.scroller.maxScrollY));
1902
		}
1903
 
1904
		this.updatePosition();
1905
	},
1906
 
1907
	updatePosition: function () {
1908
		var x = this.options.listenX && Math.round(this.sizeRatioX * this.scroller.x) || 0,
1909
			y = this.options.listenY && Math.round(this.sizeRatioY * this.scroller.y) || 0;
1910
 
1911
		if ( !this.options.ignoreBoundaries ) {
1912
			if ( x < this.minBoundaryX ) {
1913
				if ( this.options.shrink == 'scale' ) {
1914
					this.width = Math.max(this.indicatorWidth + x, 8);
1915
					this.indicatorStyle.width = this.width + 'px';
1916
				}
1917
				x = this.minBoundaryX;
1918
			} else if ( x > this.maxBoundaryX ) {
1919
				if ( this.options.shrink == 'scale' ) {
1920
					this.width = Math.max(this.indicatorWidth - (x - this.maxPosX), 8);
1921
					this.indicatorStyle.width = this.width + 'px';
1922
					x = this.maxPosX + this.indicatorWidth - this.width;
1923
				} else {
1924
					x = this.maxBoundaryX;
1925
				}
1926
			} else if ( this.options.shrink == 'scale' && this.width != this.indicatorWidth ) {
1927
				this.width = this.indicatorWidth;
1928
				this.indicatorStyle.width = this.width + 'px';
1929
			}
1930
 
1931
			if ( y < this.minBoundaryY ) {
1932
				if ( this.options.shrink == 'scale' ) {
1933
					this.height = Math.max(this.indicatorHeight + y * 3, 8);
1934
					this.indicatorStyle.height = this.height + 'px';
1935
				}
1936
				y = this.minBoundaryY;
1937
			} else if ( y > this.maxBoundaryY ) {
1938
				if ( this.options.shrink == 'scale' ) {
1939
					this.height = Math.max(this.indicatorHeight - (y - this.maxPosY) * 3, 8);
1940
					this.indicatorStyle.height = this.height + 'px';
1941
					y = this.maxPosY + this.indicatorHeight - this.height;
1942
				} else {
1943
					y = this.maxBoundaryY;
1944
				}
1945
			} else if ( this.options.shrink == 'scale' && this.height != this.indicatorHeight ) {
1946
				this.height = this.indicatorHeight;
1947
				this.indicatorStyle.height = this.height + 'px';
1948
			}
1949
		}
1950
 
1951
		this.x = x;
1952
		this.y = y;
1953
 
1954
		if ( this.scroller.options.useTransform ) {
1955
			this.indicatorStyle[utils.style.transform] = 'translate(' + x + 'px,' + y + 'px)' + this.scroller.translateZ;
1956
		} else {
1957
			this.indicatorStyle.left = x + 'px';
1958
			this.indicatorStyle.top = y + 'px';
1959
		}
1960
	},
1961
 
1962
	_pos: function (x, y) {
1963
		if ( x < 0 ) {
1964
			x = 0;
1965
		} else if ( x > this.maxPosX ) {
1966
			x = this.maxPosX;
1967
		}
1968
 
1969
		if ( y < 0 ) {
1970
			y = 0;
1971
		} else if ( y > this.maxPosY ) {
1972
			y = this.maxPosY;
1973
		}
1974
 
1975
		x = this.options.listenX ? Math.round(x / this.sizeRatioX) : this.scroller.x;
1976
		y = this.options.listenY ? Math.round(y / this.sizeRatioY) : this.scroller.y;
1977
 
1978
		this.scroller.scrollTo(x, y);
1979
	},
1980
 
1981
	fade: function (val, hold) {
1982
		if ( hold && !this.visible ) {
1983
			return;
1984
		}
1985
 
1986
		clearTimeout(this.fadeTimeout);
1987
		this.fadeTimeout = null;
1988
 
1989
		var time = val ? 250 : 500,
1990
			delay = val ? 0 : 300;
1991
 
1992
		val = val ? '1' : '0';
1993
 
1994
		this.wrapperStyle[utils.style.transitionDuration] = time + 'ms';
1995
 
1996
		this.fadeTimeout = setTimeout((function (val) {
1997
			this.wrapperStyle.opacity = val;
1998
			this.visible = +val;
1999
		}).bind(this, val), delay);
2000
	}
2001
};
2002
 
2003
IScroll.utils = utils;
2004
 
2005
if ( typeof module != 'undefined' && module.exports ) {
2006
	module.exports = IScroll;
2007
} else {
2008
	window.IScroll = IScroll;
2009
}
2010
 
2011
})(window, document, Math);