PageSourceSearch

https://www.city.kawasaki.jp/js/iscroll.js

js city.kawasaki.jp collected 2026-09-24 08:46:14 UTC 55,104 bytes, 2,170 lines download raw bytes

1/*! iScroll v5.2.0-snapshot ~ (c) 2008-2017 Matteo Spinelli ~ http://cubiq.org/license */
2(function (window, document, Math) {
3var rAF = window.requestAnimationFrame	||
4	window.webkitRequestAnimationFrame	||
5	window.mozRequestAnimationFrame		||
6	window.oRequestAnimationFrame		||
7	window.msRequestAnimationFrame		||
8	function (callback) { window.setTimeout(callback, 1000 / 60); };
9
10var 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 : el.offsetHeight
311			};
312		}
313	};
314
315	return me;
316})();
317function 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)$/ },
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
413IScroll.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		utils.getRect(this.wrapper);		// Force reflow
755
756		this.wrapperWidth	= this.wrapper.clientWidth;
757		this.wrapperHeight	= this.wrapper.clientHeight;
758
759		var rect = utils.getRect(this.scroller);
760/* REPLACE START: refresh */
761
762		this.scrollerWidth	= rect.width;
763		this.scrollerHeight	= rect.height;
764
765		this.maxScrollX		= this.wrapperWidth - this.scrollerWidth;
766		this.maxScrollY		= this.wrapperHeight - this.scrollerHeight;
767
768/* REPLACE END: refresh */
769
770		this.hasHorizontalScroll	= this.options.scrollX && this.maxScrollX < 0;
771		this.hasVerticalScroll		= this.options.scrollY && this.maxScrollY < 0;
772		
773		if ( !this.hasHorizontalScroll ) {
774			this.maxScrollX = 0;
775			this.scrollerWidth = this.wrapperWidth;
776		}
777
778		if ( !this.hasVerticalScroll ) {
779			this.maxScrollY = 0;
780			this.scrollerHeight = this.wrapperHeight;
781		}
782
783		this.endTime = 0;
784		this.directionX = 0;
785		this.directionY = 0;
786		
787		if(utils.hasPointer && !this.options.disablePointer) {
788			// The wrapper should have `touchAction` property for using pointerEvent.
789			this.wrapper.style[utils.style.touchAction] = utils.getTouchAction(this.options.eventPassthrough, true);
790
791			// case. not support 'pinch-zoom'
792			// https://github.com/cubiq/iscroll/issues/1118#issuecomment-270057583
793			if (!this.wrapper.style[utils.style.touchAction]) {
794				this.wrapper.style[utils.style.touchAction] = utils.getTouchAction(this.options.eventPassthrough, false);
795			}
796		}
797		this.wrapperOffset = utils.offset(this.wrapper);
798
799		this._execEvent('refresh');
800
801		this.resetPosition();
802
803// INSERT POINT: _refresh
804
805	},	
806
807	on: function (type, fn) {
808		if ( !this._events[type] ) {
809			this._events[type] = [];
810		}
811
812		this._events[type].push(fn);
813	},
814
815	off: function (type, fn) {
816		if ( !this._events[type] ) {
817			return;
818		}
819
820		var index = this._events[type].indexOf(fn);
821
822		if ( index > -1 ) {
823			this._events[type].splice(index, 1);
824		}
825	},
826
827	_execEvent: function (type) {
828		if ( !this._events[type] ) {
829			return;
830		}
831
832		var i = 0,
833			l = this._events[type].length;
834
835		if ( !l ) {
836			return;
837		}
838
839		for ( ; i < l; i++ ) {
840			this._events[type][i].apply(this, [].slice.call(arguments, 1));
841		}
842	},
843
844	scrollBy: function (x, y, time, easing) {
845		x = this.x + x;
846		y = this.y + y;
847		time = time || 0;
848
849		this.scrollTo(x, y, time, easing);
850	},
851
852	scrollTo: function (x, y, time, easing) {
853		easing = easing || utils.ease.circular;
854
855		this.isInTransition = this.options.useTransition && time > 0;
856		var transitionType = this.options.useTransition && easing.style;
857		if ( !time || transitionType ) {
858				if(transitionType) {
859					this._transitionTimingFunction(easing.style);
860					this._transitionTime(time);
861				}
862			this._translate(x, y);
863		} else {
864			this._animate(x, y, time, easing.fn);
865		}
866	},
867
868	scrollToElement: function (el, time, offsetX, offsetY, easing) {
869		el = el.nodeType ? el : this.scroller.querySelector(el);
870
871		if ( !el ) {
872			return;
873		}
874
875		var pos = utils.offset(el);
876
877		pos.left -= this.wrapperOffset.left;
878		pos.top  -= this.wrapperOffset.top;
879
880		// if offsetX/Y are true we center the element to the screen
881		var elRect = utils.getRect(el);
882		var wrapperRect = utils.getRect(this.wrapper);
883		if ( offsetX === true ) {
884			offsetX = Math.round(elRect.width / 2 - wrapperRect.width / 2);
885		}
886		if ( offsetY === true ) {
887			offsetY = Math.round(elRect.height / 2 - wrapperRect.height / 2);
888		}
889
890		pos.left -= offsetX || 0;
891		pos.top  -= offsetY || 0;
892
893		pos.left = pos.left > 0 ? 0 : pos.left < this.maxScrollX ? this.maxScrollX : pos.left;
894		pos.top  = pos.top  > 0 ? 0 : pos.top  < this.maxScrollY ? this.maxScrollY : pos.top;
895
896		time = time === undefined || time === null || time === 'auto' ? Math.max(Math.abs(this.x-pos.left), Math.abs(this.y-pos.top)) : time;
897
898		this.scrollTo(pos.left, pos.top, time, easing);
899	},
900
901	_transitionTime: function (time) {
902		if (!this.options.useTransition) {
903			return;
904		}
905		time = time || 0;
906		var durationProp = utils.style.transitionDuration;
907		if(!durationProp) {
908			return;
909		}
910
911		this.scrollerStyle[durationProp] = time + 'ms';
912
913		if ( !time && utils.isBadAndroid ) {
914			this.scrollerStyle[durationProp] = '0.0001ms';
915			// remove 0.0001ms
916			var self = this;
917			rAF(function() {
918				if(self.scrollerStyle[durationProp] === '0.0001ms') {
919					self.scrollerStyle[durationProp] = '0s';
920				}
921			});
922		}
923
924
925		if ( this.indicators ) {
926			for ( var i = this.indicators.length; i--; ) {
927				this.indicators[i].transitionTime(time);
928			}
929		}
930
931
932// INSERT POINT: _transitionTime
933
934	},
935
936	_transitionTimingFunction: function (easing) {
937		this.scrollerStyle[utils.style.transitionTimingFunction] = easing;
938
939
940		if ( this.indicators ) {
941			for ( var i = this.indicators.length; i--; ) {
942				this.indicators[i].transitionTimingFunction(easing);
943			}
944		}
945
946
947// INSERT POINT: _transitionTimingFunction
948
949	},
950
951	_translate: function (x, y) {
952		if ( this.options.useTransform ) {
953
954/* REPLACE START: _translate */
955
956			this.scrollerStyle[utils.style.transform] = 'translate(' + x + 'px,' + y + 'px)' + this.translateZ;
957
958/* REPLACE END: _translate */
959
960		} else {
961			x = Math.round(x);
962			y = Math.round(y);
963			this.scrollerStyle.left = x + 'px';
964			this.scrollerStyle.top = y + 'px';
965		}
966
967		this.x = x;
968		this.y = y;
969
970
971	if ( this.indicators ) {
972		for ( var i = this.indicators.length; i--; ) {
973			this.indicators[i].updatePosition();
974		}
975	}
976
977
978// INSERT POINT: _translate
979
980	},
981
982	_initEvents: function (remove) {
983		var eventType = remove ? utils.removeEvent : utils.addEvent,
984			target = this.options.bindToWrapper ? this.wrapper : window;
985
986		eventType(window, 'orientationchange', this);
987		eventType(window, 'resize', this);
988
989		if ( this.options.click ) {
990			eventType(this.wrapper, 'click', this, true);
991		}
992
993		if ( !this.options.disableMouse ) {
994			eventType(this.wrapper, 'mousedown', this);
995			eventType(target, 'mousemove', this);
996			eventType(target, 'mousecancel', this);
997			eventType(target, 'mouseup', this);
998		}
999
1000		if ( utils.hasPointer && !this.options.disablePointer ) {
1001			eventType(this.wrapper, utils.prefixPointerEvent('pointerdown'), this);
1002			eventType(target, utils.prefixPointerEvent('pointermove'), this);
1003			eventType(target, utils.prefixPointerEvent('pointercancel'), this);
1004			eventType(target, utils.prefixPointerEvent('pointerup'), this);
1005		}
1006
1007		if ( utils.hasTouch && !this.options.disableTouch ) {
1008			eventType(this.wrapper, 'touchstart', this);
1009			eventType(target, 'touchmove', this);
1010			eventType(target, 'touchcancel', this);
1011			eventType(target, 'touchend', this);
1012		}
1013
1014		eventType(this.scroller, 'transitionend', this);
1015		eventType(this.scroller, 'webkitTransitionEnd', this);
1016		eventType(this.scroller, 'oTransitionEnd', this);
1017		eventType(this.scroller, 'MSTransitionEnd', this);
1018	},
1019
1020	getComputedPosition: function () {
1021		var matrix = window.getComputedStyle(this.scroller, null),
1022			x, y;
1023
1024		if ( this.options.useTransform ) {
1025			matrix = matrix[utils.style.transform].split(')')[0].split(', ');
1026			x = +(matrix[12] || matrix[4]);
1027			y = +(matrix[13] || matrix[5]);
1028		} else {
1029			x = +matrix.left.replace(/[^-\d.]/g, '');
1030			y = +matrix.top.replace(/[^-\d.]/g, '');
1031		}
1032
1033		return { x: x, y: y };
1034	},
1035	_initIndicators: function () {
1036		var interactive = this.options.interactiveScrollbars,
1037			customStyle = typeof this.options.scrollbars != 'string',
1038			indicators = [],
1039			indicator;
1040
1041		var that = this;
1042
1043		this.indicators = [];
1044
1045		if ( this.options.scrollbars ) {
1046			// Vertical scrollbar
1047			if ( this.options.scrollY ) {
1048				indicator = {
1049					el: createDefaultScrollbar('v', interactive, this.options.scrollbars),
1050					interactive: interactive,
1051					defaultScrollbars: true,
1052					customStyle: customStyle,
1053					resize: this.options.resizeScrollbars,
1054					shrink: this.options.shrinkScrollbars,
1055					fade: this.options.fadeScrollbars,
1056					listenX: false
1057				};
1058
1059				this.wrapper.appendChild(indicator.el);
1060				indicators.push(indicator);
1061			}
1062
1063			// Horizontal scrollbar
1064			if ( this.options.scrollX ) {
1065				indicator = {
1066					el: createDefaultScrollbar('h', interactive, this.options.scrollbars),
1067					interactive: interactive,
1068					defaultScrollbars: true,
1069					customStyle: customStyle,
1070					resize: this.options.resizeScrollbars,
1071					shrink: this.options.shrinkScrollbars,
1072					fade: this.options.fadeScrollbars,
1073					listenY: false
1074				};
1075
1076				this.wrapper.appendChild(indicator.el);
1077				indicators.push(indicator);
1078			}
1079		}
1080
1081		if ( this.options.indicators ) {
1082			// TODO: check concat compatibility
1083			indicators = indicators.concat(this.options.indicators);
1084		}
1085
1086		for ( var i = indicators.length; i--; ) {
1087			this.indicators.push( new Indicator(this, indicators[i]) );
1088		}
1089
1090		// TODO: check if we can use array.map (wide compatibility and performance issues)
1091		function _indicatorsMap (fn) {
1092			if (that.indicators) {
1093				for ( var i = that.indicators.length; i--; ) {
1094					fn.call(that.indicators[i]);
1095				}
1096			}
1097		}
1098
1099		if ( this.options.fadeScrollbars ) {
1100			this.on('scrollEnd', function () {
1101				_indicatorsMap(function () {
1102					this.fade();
1103				});
1104			});
1105
1106			this.on('scrollCancel', function () {
1107				_indicatorsMap(function () {
1108					this.fade();
1109				});
1110			});
1111
1112			this.on('scrollStart', function () {
1113				_indicatorsMap(function () {
1114					this.fade(1);
1115				});
1116			});
1117
1118			this.on('beforeScrollStart', function () {
1119				_indicatorsMap(function () {
1120					this.fade(1, true);
1121				});
1122			});
1123		}
1124
1125
1126		this.on('refresh', function () {
1127			_indicatorsMap(function () {
1128				this.refresh();
1129			});
1130		});
1131
1132		this.on('destroy', function () {
1133			_indicatorsMap(function () {
1134				this.destroy();
1135			});
1136
1137			delete this.indicators;
1138		});
1139	},
1140
1141	_initWheel: function () {
1142		utils.addEvent(this.wrapper, 'wheel', this);
1143		utils.addEvent(this.wrapper, 'mousewheel', this);
1144		utils.addEvent(this.wrapper, 'DOMMouseScroll', this);
1145
1146		this.on('destroy', function () {
1147			clearTimeout(this.wheelTimeout);
1148			this.wheelTimeout = null;
1149			utils.removeEvent(this.wrapper, 'wheel', this);
1150			utils.removeEvent(this.wrapper, 'mousewheel', this);
1151			utils.removeEvent(this.wrapper, 'DOMMouseScroll', this);
1152		});
1153	},
1154
1155	_wheel: function (e) {
1156		if ( !this.enabled ) {
1157			return;
1158		}
1159
1160		e.preventDefault();
1161
1162		var wheelDeltaX, wheelDeltaY,
1163			newX, newY,
1164			that = this;
1165
1166		if ( this.wheelTimeout === undefined ) {
1167			that._execEvent('scrollStart');
1168		}
1169
1170		// Execute the scrollEnd event after 400ms the wheel stopped scrolling
1171		clearTimeout(this.wheelTimeout);
1172		this.wheelTimeout = setTimeout(function () {
1173			if(!that.options.snap) {
1174				that._execEvent('scrollEnd');
1175			}
1176			that.wheelTimeout = undefined;
1177		}, 400);
1178
1179		if ( 'deltaX' in e ) {
1180			if (e.deltaMode === 1) {
1181				wheelDeltaX = -e.deltaX * this.options.mouseWheelSpeed;
1182				wheelDeltaY = -e.deltaY * this.options.mouseWheelSpeed;
1183			} else {
1184				wheelDeltaX = -e.deltaX;
1185				wheelDeltaY = -e.deltaY;
1186			}
1187		} else if ( 'wheelDeltaX' in e ) {
1188			wheelDeltaX = e.wheelDeltaX / 120 * this.options.mouseWheelSpeed;
1189			wheelDeltaY = e.wheelDeltaY / 120 * this.options.mouseWheelSpeed;
1190		} else if ( 'wheelDelta' in e ) {
1191			wheelDeltaX = wheelDeltaY = e.wheelDelta / 120 * this.options.mouseWheelSpeed;
1192		} else if ( 'detail' in e ) {
1193			wheelDeltaX = wheelDeltaY = -e.detail / 3 * this.options.mouseWheelSpeed;
1194		} else {
1195			return;
1196		}
1197
1198		wheelDeltaX *= this.options.invertWheelDirection;
1199		wheelDeltaY *= this.options.invertWheelDirection;
1200
1201		if ( !this.hasVerticalScroll ) {
1202			wheelDeltaX = wheelDeltaY;
1203			wheelDeltaY = 0;
1204		}
1205
1206		if ( this.options.snap ) {
1207			newX = this.currentPage.pageX;
1208			newY = this.currentPage.pageY;
1209
1210			if ( wheelDeltaX > 0 ) {
1211				newX--;
1212			} else if ( wheelDeltaX < 0 ) {
1213				newX++;
1214			}
1215
1216			if ( wheelDeltaY > 0 ) {
1217				newY--;
1218			} else if ( wheelDeltaY < 0 ) {
1219				newY++;
1220			}
1221
1222			this.goToPage(newX, newY);
1223
1224			return;
1225		}
1226
1227		newX = this.x + Math.round(this.hasHorizontalScroll ? wheelDeltaX : 0);
1228		newY = this.y + Math.round(this.hasVerticalScroll ? wheelDeltaY : 0);
1229
1230		this.directionX = wheelDeltaX > 0 ? -1 : wheelDeltaX < 0 ? 1 : 0;
1231		this.directionY = wheelDeltaY > 0 ? -1 : wheelDeltaY < 0 ? 1 : 0;
1232
1233		if ( newX > 0 ) {
1234			newX = 0;
1235		} else if ( newX < this.maxScrollX ) {
1236			newX = this.maxScrollX;
1237		}
1238
1239		if ( newY > 0 ) {
1240			newY = 0;
1241		} else if ( newY < this.maxScrollY ) {
1242			newY = this.maxScrollY;
1243		}
1244
1245		this.scrollTo(newX, newY, 0);
1246
1247// INSERT POINT: _wheel
1248	},
1249
1250	_initSnap: function () {
1251		this.currentPage = {};
1252
1253		if ( typeof this.options.snap == 'string' ) {
1254			this.options.snap = this.scroller.querySelectorAll(this.options.snap);
1255		}
1256
1257		this.on('refresh', function () {
1258			var i = 0, l,
1259				m = 0, n,
1260				cx, cy,
1261				x = 0, y,
1262				stepX = this.options.snapStepX || this.wrapperWidth,
1263				stepY = this.options.snapStepY || this.wrapperHeight,
1264				el,
1265				rect;
1266
1267			this.pages = [];
1268
1269			if ( !this.wrapperWidth || !this.wrapperHeight || !this.scrollerWidth || !this.scrollerHeight ) {
1270				return;
1271			}
1272
1273			if ( this.options.snap === true ) {
1274				cx = Math.round( stepX / 2 );
1275				cy = Math.round( stepY / 2 );
1276
1277				while ( x > -this.scrollerWidth ) {
1278					this.pages[i] = [];
1279					l = 0;
1280					y = 0;
1281
1282					while ( y > -this.scrollerHeight ) {
1283						this.pages[i][l] = {
1284							x: Math.max(x, this.maxScrollX),
1285							y: Math.max(y, this.maxScrollY),
1286							width: stepX,
1287							height: stepY,
1288							cx: x - cx,
1289							cy: y - cy
1290						};
1291
1292						y -= stepY;
1293						l++;
1294					}
1295
1296					x -= stepX;
1297					i++;
1298				}
1299			} else {
1300				el = this.options.snap;
1301				l = el.length;
1302				n = -1;
1303
1304				for ( ; i < l; i++ ) {
1305					rect = utils.getRect(el[i]);
1306					if ( i === 0 || rect.left <= utils.getRect(el[i-1]).left ) {
1307						m = 0;
1308						n++;
1309					}
1310
1311					if ( !this.pages[m] ) {
1312						this.pages[m] = [];
1313					}
1314
1315					x = Math.max(-rect.left, this.maxScrollX);
1316					y = Math.max(-rect.top, this.maxScrollY);
1317					cx = x - Math.round(rect.width / 2);
1318					cy = y - Math.round(rect.height / 2);
1319
1320					this.pages[m][n] = {
1321						x: x,
1322						y: y,
1323						width: rect.width,
1324						height: rect.height,
1325						cx: cx,
1326						cy: cy
1327					};
1328
1329					if ( x > this.maxScrollX ) {
1330						m++;
1331					}
1332				}
1333			}
1334
1335			this.goToPage(this.currentPage.pageX || 0, this.currentPage.pageY || 0, 0);
1336
1337			// Update snap threshold if needed
1338			if ( this.options.snapThreshold % 1 === 0 ) {
1339				this.snapThresholdX = this.options.snapThreshold;
1340				this.snapThresholdY = this.options.snapThreshold;
1341			} else {
1342				this.snapThresholdX = Math.round(this.pages[this.currentPage.pageX][this.currentPage.pageY].width * this.options.snapThreshold);
1343				this.snapThresholdY = Math.round(this.pages[this.currentPage.pageX][this.currentPage.pageY].height * this.options.snapThreshold);
1344			}
1345		});
1346
1347		this.on('flick', function () {
1348			var time = this.options.snapSpeed || Math.max(
1349					Math.max(
1350						Math.min(Math.abs(this.x - this.startX), 1000),
1351						Math.min(Math.abs(this.y - this.startY), 1000)
1352					), 300);
1353
1354			this.goToPage(
1355				this.currentPage.pageX + this.directionX,
1356				this.currentPage.pageY + this.directionY,
1357				time
1358			);
1359		});
1360	},
1361
1362	_nearestSnap: function (x, y) {
1363		if ( !this.pages.length ) {
1364			return { x: 0, y: 0, pageX: 0, pageY: 0 };
1365		}
1366
1367		var i = 0,
1368			l = this.pages.length,
1369			m = 0;
1370
1371		// Check if we exceeded the snap threshold
1372		if ( Math.abs(x - this.absStartX) < this.snapThresholdX &&
1373			Math.abs(y - this.absStartY) < this.snapThresholdY ) {
1374			return this.currentPage;
1375		}
1376
1377		if ( x > 0 ) {
1378			x = 0;
1379		} else if ( x < this.maxScrollX ) {
1380			x = this.maxScrollX;
1381		}
1382
1383		if ( y > 0 ) {
1384			y = 0;
1385		} else if ( y < this.maxScrollY ) {
1386			y = this.maxScrollY;
1387		}
1388
1389		for ( ; i < l; i++ ) {
1390			if ( x >= this.pages[i][0].cx ) {
1391				x = this.pages[i][0].x;
1392				break;
1393			}
1394		}
1395
1396		l = this.pages[i].length;
1397
1398		for ( ; m < l; m++ ) {
1399			if ( y >= this.pages[0][m].cy ) {
1400				y = this.pages[0][m].y;
1401				break;
1402			}
1403		}
1404
1405		if ( i == this.currentPage.pageX ) {
1406			i += this.directionX;
1407
1408			if ( i < 0 ) {
1409				i = 0;
1410			} else if ( i >= this.pages.length ) {
1411				i = this.pages.length - 1;
1412			}
1413
1414			x = this.pages[i][0].x;
1415		}
1416
1417		if ( m == this.currentPage.pageY ) {
1418			m += this.directionY;
1419
1420			if ( m < 0 ) {
1421				m = 0;
1422			} else if ( m >= this.pages[0].length ) {
1423				m = this.pages[0].length - 1;
1424			}
1425
1426			y = this.pages[0][m].y;
1427		}
1428
1429		return {
1430			x: x,
1431			y: y,
1432			pageX: i,
1433			pageY: m
1434		};
1435	},
1436
1437	goToPage: function (x, y, time, easing) {
1438		easing = easing || this.options.bounceEasing;
1439
1440		if ( x >= this.pages.length ) {
1441			x = this.pages.length - 1;
1442		} else if ( x < 0 ) {
1443			x = 0;
1444		}
1445
1446		if ( y >= this.pages[x].length ) {
1447			y = this.pages[x].length - 1;
1448		} else if ( y < 0 ) {
1449			y = 0;
1450		}
1451
1452		var posX = this.pages[x][y].x,
1453			posY = this.pages[x][y].y;
1454
1455		time = time === undefined ? this.options.snapSpeed || Math.max(
1456			Math.max(
1457				Math.min(Math.abs(posX - this.x), 1000),
1458				Math.min(Math.abs(posY - this.y), 1000)
1459			), 300) : time;
1460
1461		this.currentPage = {
1462			x: posX,
1463			y: posY,
1464			pageX: x,
1465			pageY: y
1466		};
1467
1468		this.scrollTo(posX, posY, time, easing);
1469	},
1470
1471	next: function (time, easing) {
1472		var x = this.currentPage.pageX,
1473			y = this.currentPage.pageY;
1474
1475		x++;
1476
1477		if ( x >= this.pages.length && this.hasVerticalScroll ) {
1478			x = 0;
1479			y++;
1480		}
1481
1482		this.goToPage(x, y, time, easing);
1483	},
1484
1485	prev: function (time, easing) {
1486		var x = this.currentPage.pageX,
1487			y = this.currentPage.pageY;
1488
1489		x--;
1490
1491		if ( x < 0 && this.hasVerticalScroll ) {
1492			x = 0;
1493			y--;
1494		}
1495
1496		this.goToPage(x, y, time, easing);
1497	},
1498
1499	_initKeys: function (e) {
1500		// default key bindings
1501		var keys = {
1502			pageUp: 33,
1503			pageDown: 34,
1504			end: 35,
1505			home: 36,
1506			left: 37,
1507			up: 38,
1508			right: 39,
1509			down: 40
1510		};
1511		var i;
1512
1513		// if you give me characters I give you keycode
1514		if ( typeof this.options.keyBindings == 'object' ) {
1515			for ( i in this.options.keyBindings ) {
1516				if ( typeof this.options.keyBindings[i] == 'string' ) {
1517					this.options.keyBindings[i] = this.options.keyBindings[i].toUpperCase().charCo
1517deAt(0);
1518				}
1519			}
1520		} else {
1521			this.options.keyBindings = {};
1522		}
1523
1524		for ( i in keys ) {
1525			this.options.keyBindings[i] = this.options.keyBindings[i] || keys[i];
1526		}
1527
1528		utils.addEvent(window, 'keydown', this);
1529
1530		this.on('destroy', function () {
1531			utils.removeEvent(window, 'keydown', this);
1532		});
1533	},
1534
1535	_key: function (e) {
1536		if ( !this.enabled ) {
1537			return;
1538		}
1539
1540		var snap = this.options.snap,	// we are using this alot, better to cache it
1541			newX = snap ? this.currentPage.pageX : this.x,
1542			newY = snap ? this.currentPage.pageY : this.y,
1543			now = utils.getTime(),
1544			prevTime = this.keyTime || 0,
1545			acceleration = 0.250,
1546			pos;
1547
1548		if ( this.options.useTransition && this.isInTransition ) {
1549			pos = this.getComputedPosition();
1550
1551			this._translate(Math.round(pos.x), Math.round(pos.y));
1552			this.isInTransition = false;
1553		}
1554
1555		this.keyAcceleration = now - prevTime < 200 ? Math.min(this.keyAcceleration + acceleration, 50) : 0;
1556
1557		switch ( e.keyCode ) {
1558			case this.options.keyBindings.pageUp:
1559				if ( this.hasHorizontalScroll && !this.hasVerticalScroll ) {
1560					newX += snap ? 1 : this.wrapperWidth;
1561				} else {
1562					newY += snap ? 1 : this.wrapperHeight;
1563				}
1564				break;
1565			case this.options.keyBindings.pageDown:
1566				if ( this.hasHorizontalScroll && !this.hasVerticalScroll ) {
1567					newX -= snap ? 1 : this.wrapperWidth;
1568				} else {
1569					newY -= snap ? 1 : this.wrapperHeight;
1570				}
1571				break;
1572			case this.options.keyBindings.end:
1573				newX = snap ? this.pages.length-1 : this.maxScrollX;
1574				newY = snap ? this.pages[0].length-1 : this.maxScrollY;
1575				break;
1576			case this.options.keyBindings.home:
1577				newX = 0;
1578				newY = 0;
1579				break;
1580			case this.options.keyBindings.left:
1581				newX += snap ? -1 : 5 + this.keyAcceleration>>0;
1582				break;
1583			case this.options.keyBindings.up:
1584				newY += snap ? 1 : 5 + this.keyAcceleration>>0;
1585				break;
1586			case this.options.keyBindings.right:
1587				newX -= snap ? -1 : 5 + this.keyAcceleration>>0;
1588				break;
1589			case this.options.keyBindings.down:
1590				newY -= snap ? 1 : 5 + this.keyAcceleration>>0;
1591				break;
1592			default:
1593				return;
1594		}
1595
1596		if ( snap ) {
1597			this.goToPage(newX, newY);
1598			return;
1599		}
1600
1601		if ( newX > 0 ) {
1602			newX = 0;
1603			this.keyAcceleration = 0;
1604		} else if ( newX < this.maxScrollX ) {
1605			newX = this.maxScrollX;
1606			this.keyAcceleration = 0;
1607		}
1608
1609		if ( newY > 0 ) {
1610			newY = 0;
1611			this.keyAcceleration = 0;
1612		} else if ( newY < this.maxScrollY ) {
1613			newY = this.maxScrollY;
1614			this.keyAcceleration = 0;
1615		}
1616
1617		this.scrollTo(newX, newY, 0);
1618
1619		this.keyTime = now;
1620	},
1621
1622	_animate: function (destX, destY, duration, easingFn) {
1623		var that = this,
1624			startX = this.x,
1625			startY = this.y,
1626			startTime = utils.getTime(),
1627			destTime = startTime + duration;
1628
1629		function step () {
1630			var now = utils.getTime(),
1631				newX, newY,
1632				easing;
1633
1634			if ( now >= destTime ) {
1635				that.isAnimating = false;
1636				that._translate(destX, destY);
1637
1638				if ( !that.resetPosition(that.options.bounceTime) ) {
1639					that._execEvent('scrollEnd');
1640				}
1641
1642				return;
1643			}
1644
1645			now = ( now - startTime ) / duration;
1646			easing = easingFn(now);
1647			newX = ( destX - startX ) * easing + startX;
1648			newY = ( destY - startY ) * easing + startY;
1649			that._translate(newX, newY);
1650
1651			if ( that.isAnimating ) {
1652				rAF(step);
1653			}
1654		}
1655
1656		this.isAnimating = true;
1657		step();
1658	},
1659	handleEvent: function (e) {
1660		switch ( e.type ) {
1661			case 'touchstart':
1662			case 'pointerdown':
1663			case 'MSPointerDown':
1664			case 'mousedown':
1665				this._start(e);
1666				break;
1667			case 'touchmove':
1668			case 'pointermove':
1669			case 'MSPointerMove':
1670			case 'mousemove':
1671				this._move(e);
1672				break;
1673			case 'touchend':
1674			case 'pointerup':
1675			case 'MSPointerUp':
1676			case 'mouseup':
1677			case 'touchcancel':
1678			case 'pointercancel':
1679			case 'MSPointerCancel':
1680			case 'mousecancel':
1681				this._end(e);
1682				break;
1683			case 'orientationchange':
1684			case 'resize':
1685				this._resize();
1686				break;
1687			case 'transitionend':
1688			case 'webkitTransitionEnd':
1689			case 'oTransitionEnd':
1690			case 'MSTransitionEnd':
1691				this._transitionEnd(e);
1692				break;
1693			case 'wheel':
1694			case 'DOMMouseScroll':
1695			case 'mousewheel':
1696				this._wheel(e);
1697				break;
1698			case 'keydown':
1699				this._key(e);
1700				break;
1701			case 'click':
1702				if ( this.enabled && !e._constructed ) {
1703					e.preventDefault();
1704					e.stopPropagation();
1705				}
1706				break;
1707		}
1708	}
1709};
1710function createDefaultScrollbar (direction, interactive, type) {
1711	var scrollbar = document.createElement('div'),
1712		indicator = document.createElement('div');
1713
1714	if ( type === true ) {
1715		scrollbar.style.cssText = 'position:absolute;z-index:9999';
1716		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';
1717	}
1718
1719	indicator.className = 'iScrollIndicator';
1720
1721	if ( direction == 'h' ) {
1722		if ( type === true ) {
1723			scrollbar.style.cssText += ';height:7px;left:2px;right:2px;bottom:0';
1724			indicator.style.height = '100%';
1725		}
1726		scrollbar.className = 'iScrollHorizontalScrollbar';
1727	} else {
1728		if ( type === true ) {
1729			scrollbar.style.cssText += ';width:7px;bottom:2px;top:2px;right:1px';
1730			indicator.style.width = '100%';
1731		}
1732		scrollbar.className = 'iScrollVerticalScrollbar';
1733	}
1734
1735	scrollbar.style.cssText += ';overflow:hidden';
1736
1737	if ( !interactive ) {
1738		scrollbar.style.pointerEvents = 'none';
1739	}
1740
1741	scrollbar.appendChild(indicator);
1742
1743	return scrollbar;
1744}
1745
1746function Indicator (scroller, options) {
1747	this.wrapper = typeof options.el == 'string' ? document.querySelector(options.el) : options.el;
1748	this.wrapperStyle = this.wrapper.style;
1749	this.indicator = this.wrapper.children[0];
1750	this.indicatorStyle = this.indicator.style;
1751	this.scroller = scroller;
1752
1753	this.options = {
1754		listenX: true,
1755		listenY: true,
1756		interactive: false,
1757		resize: true,
1758		defaultScrollbars: false,
1759		shrink: false,
1760		fade: false,
1761		speedRatioX: 0,
1762		speedRatioY: 0
1763	};
1764
1765	for ( var i in options ) {
1766		this.options[i] = options[i];
1767	}
1768
1769	this.sizeRatioX = 1;
1770	this.sizeRatioY = 1;
1771	this.maxPosX = 0;
1772	this.maxPosY = 0;
1773
1774	if ( this.options.interactive ) {
1775		if ( !this.options.disableTouch ) {
1776			utils.addEvent(this.indicator, 'touchstart', this);
1777			utils.addEvent(window, 'touchend', this);
1778		}
1779		if ( !this.options.disablePointer ) {
1780			utils.addEvent(this.indicator, utils.prefixPointerEvent('pointerdown'), this);
1781			utils.addEvent(window, utils.prefixPointerEvent('pointerup'), this);
1782		}
1783		if ( !this.options.disableMouse ) {
1784			utils.addEvent(this.indicator, 'mousedown', this);
1785			utils.addEvent(window, 'mouseup', this);
1786		}
1787	}
1788
1789	if ( this.options.fade ) {
1790		this.wrapperStyle[utils.style.transform] = this.scroller.translateZ;
1791		var durationProp = utils.style.transitionDuration;
1792		if(!durationProp) {
1793			return;
1794		}
1795		this.wrapperStyle[durationProp] = utils.isBadAndroid ? '0.0001ms' : '0ms';
1796		// remove 0.0001ms
1797		var self = this;
1798		if(utils.isBadAndroid) {
1799			rAF(function() {
1800				if(self.wrapperStyle[durationProp] === '0.0001ms') {
1801					self.wrapperStyle[durationProp] = '0s';
1802				}
1803			});
1804		}
1805		this.wrapperStyle.opacity = '0';
1806	}
1807}
1808
1809Indicator.prototype = {
1810	handleEvent: function (e) {
1811		switch ( e.type ) {
1812			case 'touchstart':
1813			case 'pointerdown':
1814			case 'MSPointerDown':
1815			case 'mousedown':
1816				this._start(e);
1817				break;
1818			case 'touchmove':
1819			case 'pointermove':
1820			case 'MSPointerMove':
1821			case 'mousemove':
1822				this._move(e);
1823				break;
1824			case 'touchend':
1825			case 'pointerup':
1826			case 'MSPointerUp':
1827			case 'mouseup':
1828			case 'touchcancel':
1829			case 'pointercancel':
1830			case 'MSPointerCancel':
1831			case 'mousecancel':
1832				this._end(e);
1833				break;
1834		}
1835	},
1836
1837	destroy: function () {
1838		if ( this.options.fadeScrollbars ) {
1839			clearTimeout(this.fadeTimeout);
1840			this.fadeTimeout = null;
1841		}
1842		if ( this.options.interactive ) {
1843			utils.removeEvent(this.indicator, 'touchstart', this);
1844			utils.removeEvent(this.indicator, utils.prefixPointerEvent('pointerdown'), this);
1845			utils.removeEvent(this.indicator, 'mousedown', this);
1846
1847			utils.removeEvent(window, 'touchmove', this);
1848			utils.removeEvent(window, utils.prefixPointerEvent('pointermove'), this);
1849			utils.removeEvent(window, 'mousemove', this);
1850
1851			utils.removeEvent(window, 'touchend', this);
1852			utils.removeEvent(window, utils.prefixPointerEvent('pointerup'), this);
1853			utils.removeEvent(window, 'mouseup', this);
1854		}
1855
1856		if ( this.options.defaultScrollbars && this.wrapper.parentNode ) {
1857			this.wrapper.parentNode.removeChild(this.wrapper);
1858		}
1859	},
1860
1861	_start: function (e) {
1862		var point = e.touches ? e.touches[0] : e;
1863
1864		e.preventDefault();
1865		e.stopPropagation();
1866
1867		this.transitionTime();
1868
1869		this.initiated = true;
1870		this.moved = false;
1871		this.lastPointX	= point.pageX;
1872		this.lastPointY	= point.pageY;
1873
1874		this.startTime	= utils.getTime();
1875
1876		if ( !this.options.disableTouch ) {
1877			utils.addEvent(window, 'touchmove', this);
1878		}
1879		if ( !this.options.disablePointer ) {
1880			utils.addEvent(window, utils.prefixPointerEvent('pointermove'), this);
1881		}
1882		if ( !this.options.disableMouse ) {
1883			utils.addEvent(window, 'mousemove', this);
1884		}
1885
1886		this.scroller._execEvent('beforeScrollStart');
1887	},
1888
1889	_move: function (e) {
1890		var point = e.touches ? e.touches[0] : e,
1891			deltaX, deltaY,
1892			newX, newY,
1893			timestamp = utils.getTime();
1894
1895		if ( !this.moved ) {
1896			this.scroller._execEvent('scrollStart');
1897		}
1898
1899		this.moved = true;
1900
1901		deltaX = point.pageX - this.lastPointX;
1902		this.lastPointX = point.pageX;
1903
1904		deltaY = point.pageY - this.lastPointY;
1905		this.lastPointY = point.pageY;
1906
1907		newX = this.x + deltaX;
1908		newY = this.y + deltaY;
1909
1910		this._pos(newX, newY);
1911
1912// INSERT POINT: indicator._move
1913
1914		e.preventDefault();
1915		e.stopPropagation();
1916	},
1917
1918	_end: function (e) {
1919		if ( !this.initiated ) {
1920			return;
1921		}
1922
1923		this.initiated = false;
1924
1925		e.preventDefault();
1926		e.stopPropagation();
1927
1928		utils.removeEvent(window, 'touchmove', this);
1929		utils.removeEvent(window, utils.prefixPointerEvent('pointermove'), this);
1930		utils.removeEvent(window, 'mousemove', this);
1931
1932		if ( this.scroller.options.snap ) {
1933			var snap = this.scroller._nearestSnap(this.scroller.x, this.scroller.y);
1934
1935			var time = this.options.snapSpeed || Math.max(
1936					Math.max(
1937						Math.min(Math.abs(this.scroller.x - snap.x), 1000),
1938						Math.min(Math.abs(this.scroller.y - snap.y), 1000)
1939					), 300);
1940
1941			if ( this.scroller.x != snap.x || this.scroller.y != snap.y ) {
1942				this.scroller.directionX = 0;
1943				this.scroller.directionY = 0;
1944				this.scroller.currentPage = snap;
1945				this.scroller.scrollTo(snap.x, snap.y, time, this.scroller.options.bounceEasing);
1946			}
1947		}
1948
1949		if ( this.moved ) {
1950			this.scroller._execEvent('scrollEnd');
1951		}
1952	},
1953
1954	transitionTime: function (time) {
1955		time = time || 0;
1956		var durationProp = utils.style.transitionDuration;
1957		if(!durationProp) {
1958			return;
1959		}
1960
1961		this.indicatorStyle[durationProp] = time + 'ms';
1962
1963		if ( !time && utils.isBadAndroid ) {
1964			this.indicatorStyle[durationProp] = '0.0001ms';
1965			// remove 0.0001ms
1966			var self = this;
1967			rAF(function() {
1968				if(self.indicatorStyle[durationProp] === '0.0001ms') {
1969					self.indicatorStyle[durationProp] = '0s';
1970				}
1971			});
1972		}
1973	},
1974
1975	transitionTimingFunction: function (easing) {
1976		this.indicatorStyle[utils.style.transitionTimingFunction] = easing;
1977	},
1978
1979	refresh: function () {
1980		this.transitionTime();
1981
1982		if ( this.options.listenX && !this.options.listenY ) {
1983			this.indicatorStyle.display = this.scroller.hasHorizontalScroll ? 'block' : 'none';
1984		} else if ( this.options.listenY && !this.options.listenX ) {
1985			this.indicatorStyle.display = this.scroller.hasVerticalScroll ? 'block' : 'none';
1986		} else {
1987			this.indicatorStyle.display = this.scroller.hasHorizontalScroll || this.scroller.hasVerticalScroll ? 'block' : 'none';
1988		}
1989
1990		if ( this.scroller.hasHorizontalScroll && this.scroller.hasVerticalScroll ) {
1991			utils.addClass(this.wrapper, 'iScrollBothScrollbars');
1992			utils.removeClass(this.wrapper, 'iScrollLoneScrollbar');
1993
1994			if ( this.options.defaultScrollbars && this.options.customStyle ) {
1995				if ( this.options.listenX ) {
1996					this.wrapper.style.right = '8px';
1997				} else {
1998					this.wrapper.style.bottom = '8px';
1999				}
2000			}
2001		} else {
2002			utils.removeClass(this.wrapper, 'iScrollBothScrollbars');
2003			utils.addClass(this.wrapper, 'iScrollLoneScrollbar');
2004
2005			if ( this.options.defaultScrollbars && this.options.customStyle ) {
2006				if ( this.options.listenX ) {
2007					this.wrapper.style.right = '2px';
2008				} else {
2009					this.wrapper.style.bottom = '2px';
2010				}
2011			}
2012		}
2013
2014		utils.getRect(this.wrapper);	// force refresh
2015
2016		if ( this.options.listenX ) {
2017			this.wrapperWidth = this.wrapper.clientWidth;
2018			if ( this.options.resize ) {
2019				this.indicatorWidth = Math.max(Math.round(this.wrapperWidth * this.wrapperWidth / (this.scroller.scrollerWidth || this.wrapperWidth || 1)), 8);
2020				this.indicatorStyle.width = this.indicatorWidth + 'px';
2021			} else {
2022				this.indicatorWidth = this.indicator.clientWidth;
2023			}
2024
2025			this.maxPosX = this.wrapperWidth - this.indicatorWidth;
2026
2027			if ( this.options.shrink == 'clip' ) {
2028				this.minBoundaryX = -this.indicatorWidth + 8;
2029				this.maxBoundaryX = this.wrapperWidth - 8;
2030			} else {
2031				this.minBoundaryX = 0;
2032				this.maxBoundaryX = this.maxPosX;
2033			}
2034
2035			this.sizeRatioX = this.options.speedRatioX || (this.scroller.maxScrollX && (this.maxPosX / this.scroller.maxScrollX));
2036		}
2037
2038		if ( this.options.listenY ) {
2039			this.wrapperHeight = this.wrapper.clientHeight;
2040			if ( this.options.resize ) {
2041				this.indicatorHeight = Math.max(Math.round(this.wrapperHeight * this.wrapperHeight / (this.scroller.scrollerHeight || this.wrapperHeight || 1)), 8);
2042				this.indicatorStyle.height = this.indicatorHeight + 'px';
2043			} else {
2044				this.indicatorHeight = this.indicator.clientHeight;
2045			}
2046
2047			this.maxPosY = this.wrapperHeight - this.indicatorHeight;
2048
2049			if ( this.options.shrink == 'clip' ) {
2050				this.minBoundaryY = -this.indicatorHeight + 8;
2051				this.maxBoundaryY = this.wrapperHeight - 8;
2052			} else {
2053				this.minBoundaryY = 0;
2054				this.maxBoundaryY = this.maxPosY;
2055			}
2056
2057			this.maxPosY = this.wrapperHeight - this.indicatorHeight;
2058			this.sizeRatioY = this.options.speedRatioY || (this.scroller.maxScrollY && (this.maxPosY / this.scroller.maxScrollY));
2059		}
2060
2061		this.updatePosition();
2062	},
2063
2064	updatePosition: function () {
2065		var x = this.options.listenX && Math.round(this.sizeRatioX * this.scroller.x) || 0,
2066			y = this.options.listenY && Math.round(this.sizeRatioY * this.scroller.y) || 0;
2067
2068		if ( !this.options.ignoreBoundaries ) {
2069			if ( x < this.minBoundaryX ) {
2070				if ( this.options.shrink == 'scale' ) {
2071					this.width = Math.max(this.indicatorWidth + x, 8);
2072					this.indicatorStyle.width = this.width + 'px';
2073				}
2074				x = this.minBoundaryX;
2075			} else if ( x > this.maxBoundaryX ) {
2076				if ( this.options.shrink == 'scale' ) {
2077					this.width = Math.max(this.indicatorWidth - (x - this.maxPosX), 8);
2078					this.indicatorStyle.width = this.width + 'px';
2079					x = this.maxPosX + this.indicatorWidth - this.width;
2080				} else {
2081					x = this.maxBoundaryX;
2082				}
2083			} else if ( this.options.shrink == 'scale' && this.width != this.indicatorWidth ) {
2084				this.width = this.indicatorWidth;
2085				this.indicatorStyle.width = this.width + 'px';
2086			}
2087
2088			if ( y < this.minBoundaryY ) {
2089				if ( this.options.shrink == 'scale' ) {
2090					this.height = Math.max(this.indicatorHeight + y * 3, 8);
2091					this.indicatorStyle.height = this.height + 'px';
2092				}
2093				y = this.minBoundaryY;
2094			} else if ( y > this.maxBoundaryY ) {
2095				if ( this.options.shrink == 'scale' ) {
2096					this.height = Math.max(this.indicatorHeight - (y - this.maxPosY) * 3, 8);
2097					this.indicatorStyle.height = this.height + 'px';
2098					y = this.maxPosY + this.indicatorHeight - this.height;
2099				} else {
2100					y = this.maxBoundaryY;
2101				}
2102			} else if ( this.options.shrink == 'scale' && this.height != this.indicatorHeight ) {
2103				this.height = this.indicatorHeight;
2104				this.indicatorStyle.height = this.height + 'px';
2105			}
2106		}
2107
2108		this.x = x;
2109		this.y = y;
2110
2111		if ( this.scroller.options.useTransform ) {
2112			this.indicatorStyle[utils.style.transform] = 'translate(' + x + 'px,' + y + 'px)' + this.scroller.translateZ;
2113		} else {
2114			this.indicatorStyle.left = x + 'px';
2115			this.indicatorStyle.top = y + 'px';
2116		}
2117	},
2118
2119	_pos: function (x, y) {
2120		if ( x < 0 ) {
2121			x = 0;
2122		} else if ( x > this.maxPosX ) {
2123			x = this.maxPosX;
2124		}
2125
2126		if ( y < 0 ) {
2127			y = 0;
2128		} else if ( y > this.maxPosY ) {
2129			y = this.maxPosY;
2130		}
2131
2132		x = this.options.listenX ? Math.round(x / this.sizeRatioX) : this.scroller.x;
2133		y = this.options.listenY ? Math.round(y / this.sizeRatioY) : this.scroller.y;
2134
2135		this.scroller.scrollTo(x, y);
2136	},
2137
2138	fade: function (val, hold) {
2139		if ( hold && !this.visible ) {
2140			return;
2141		}
2142
2143		clearTimeout(this.fadeTimeout);
2144		this.fadeTimeout = null;
2145
2146		var time = val ? 250 : 500,
2147			delay = val ? 0 : 300;
2148
2149		val = val ? '1' : '0';
2150
2151		this.wrapperStyle[utils.style.transitionDuration] = time + 'ms';
2152
2153		this.fadeTimeout = setTimeout((function (val) {
2154			this.wrapperStyle.opacity = val;
2155			this.visible = +val;
2156		}).bind(this, val), delay);
2157	}
2158};
2159
2160IScroll.utils = utils;
2161
2162if ( typeof module != 'undefined' && module.exports ) {
2163	module.exports = IScroll;
2164} else if ( typeof define == 'function' && define.amd ) {
2165        define( function () { return IScroll; } );
2166} else {
2167	window.IScroll = IScroll;
2168}
2169
2170})(window, document, Math);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.