PageSourceSearch

https://ssmr.ro/files/megazine/INEGALITATEA_IONESCU_WEITZENBOCK/js/iscroll.js

js ssmr.ro collected 2026-09-24 10:02:15 UTC 34,342 bytes, 1,071 lines download raw bytes

1/*!
2 * iScroll v4.1.9 ~ Copyright (c) 2011 Matteo Spinelli, http://cubiq.org
3 * Released under MIT license, http://cubiq.org/license
4 */
5(function(){
6var m = Math,
7	mround = function (r) { return r >> 0; },
8	vendor = (/webkit/i).test(navigator.appVersion) ? 'webkit' :
9		(/firefox/i).test(navigator.userAgent) ? 'Moz' :
10		'opera' in window ? 'O' : '',
11
12    // Browser capabilities
13    isAndroid = (/android/gi).test(navigator.appVersion),
14    isIDevice = (/iphone|ipad/gi).test(navigator.appVersion),
15    isPlaybook = (/playbook/gi).test(navigator.appVersion),
16    isTouchPad = (/hp-tablet/gi).test(navigator.appVersion),
17
18    has3d = 'WebKitCSSMatrix' in window && 'm11' in new WebKitCSSMatrix(),
19    hasTouch = 'ontouchstart' in window && !isTouchPad,
20    hasTransform = vendor + 'Transform' in document.documentElement.style,
21    hasTransitionEnd = isIDevice || isPlaybook,
22
23	nextFrame = (function() {
24	    return window.requestAnimationFrame
25			|| window.webkitRequestAnimationFrame
26			|| window.mozRequestAnimationFrame
27			|| window.oRequestAnimationFrame
28			|| window.msRequestAnimationFrame
29			|| function(callback) { return setTimeout(callback, 1); }
30	})(),
31	cancelFrame = (function () {
32	    return window.cancelRequestAnimationFrame
33			|| window.webkitCancelRequestAnimationFrame
34			|| window.mozCancelRequestAnimationFrame
35			|| window.oCancelRequestAnimationFrame
36			|| window.msCancelRequestAnimationFrame
37			|| clearTimeout
38	})(),
39
40	// Events
41	RESIZE_EV = 'onorientationchange' in window ? 'orientationchange' : 'resize',
42	START_EV = hasTouch ? 'touchstart' : 'mousedown',
43	MOVE_EV = hasTouch ? 'touchmove' : 'mousemove',
44	END_EV = hasTouch ? 'touchend' : 'mouseup',
45	CANCEL_EV = hasTouch ? 'touchcancel' : 'mouseup',
46	WHEEL_EV = vendor == 'Moz' ? 'DOMMouseScroll' : 'mousewheel',
47
48	// Helpers
49	trnOpen = 'translate' + (has3d ? '3d(' : '('),
50	trnClose = has3d ? ',0)' : ')',
51
52	// Constructor
53	iScroll = function (el, options) {
54		var that = this,
55			doc = document,
56			i;
57
58		that.wrapper = typeof el == 'object' ? el : doc.getElementById(el);
59		that.wrapper.style.overflow = 'hidden';
60		that.scroller = that.wrapper.children[0];
61
62		// Default options
63		that.options = {
64			hScroll: true,
65			vScroll: true,
66			x: 0,
67			y: 0,
68			bounce: true,
69			bounceLock: false,
70			momentum: true,
71			lockDirection: true,
72			useTransform: true,
73			useTransition: false,
74			topOffset: 0,
75			checkDOMChanges: false,		// Experimental
76
77			// Scrollbar
78			hScrollbar: true,
79			vScrollbar: true,
80			fixedScrollbar: isAndroid,
81			hideScrollbar: isIDevice,
82			fadeScrollbar: isIDevice && has3d,
83			scrollbarClass: '',
84
85			// Zoom
86			zoom: false,
87			zoomMin: 1,
88			zoomMax: 4,
89			doubleTapZoom: 2,
90			wheelAction: 'scroll',
91
92			// Snap
93			snap: false,
94			snapThreshold: 1,
95
96			// Events
97			onRefresh: null,
98			onBeforeScrollStart: function (e) { e.preventDefault(); },
99			onScrollStart: null,
100			onBeforeScrollMove: null,
101			onScrollMove: null,
102			onBeforeScrollEnd: null,
103			onScrollEnd: null,
104			onTouchEnd: null,
105			onDestroy: null,
106			onZoomStart: null,
107			onZoom: null,
108			onZoomEnd: null
109		};
110		// Helpers FIX ANDROID BUG!
111		// translate3d and scale doesn't work together! 
112		// Ignoring 3d ONLY WHEN YOU SET that.zoom
113		if ( that.zoom && isAndroid ){
114			trnOpen = 'translate(';
115			trnClose = ')';
116		}
117		// User defined options
118		for (i in options) that.options[i] = options[i];
119		
120		// Set starting position
121		that.x = that.options.x;
122		that.y = that.options.y;
123
124		// Normalize options
125		that.options.useTransform = hasTransform ? that.options.useTransform : false;
126		that.options.hScrollbar = that.options.hScroll && that.options.hScrollbar;
127		that.options.vScrollbar = that.options.vScroll && that.options.vScrollbar;
128		that.options.zoom = that.options.useTransform && that.options.zoom;
129		that.options.useTransition = hasTransitionEnd && that.options.useTransition;
130		
131		// Set some default styles
132		that.scroller.style[vendor + 'TransitionProperty'] = that.options.useTransform ? '-' + vendor.toLowerCase() + '-transform' : 'top left';
133		that.scroller.style[vendor + 'TransitionDuration'] = '0';
134		that.scroller.style[vendor + 'TransformOrigin'] = '0 0';
135		if (that.options.useTransition) that.scroller.style[vendor + 'TransitionTimingFunction'] = 'cubic-bezier(0.33,0.66,0.66,1)';
136		
137		if (that.options.useTransform) that.scroller.style[vendor + 'Transform'] = trnOpen + that.x + 'px,' + that.y + 'px' + trnClose;
138		else that.scroller.style.cssText += ';position:absolute;top:' + that.y + 'px;left:' + that.x + 'px';
139
140		if (that.options.useTransition) that.options.fixedScrollbar = true;
141
142		that.refresh();
143
144		that._bind(RESIZE_EV, window);
145		that._bind(START_EV);
146		if (!hasTouch) {
147			that._bind('mouseout', that.wrapper);
148			that._bind(WHEEL_EV);
149		}
150
151		if (that.options.checkDOMChanges) that.checkDOMTime = setInterval(function () {
152			that._checkDOMChanges();
153		}, 500);
154	};
155
156// Prototype
157iScroll.prototype = {
158	enabled: true,
159	x: 0,
160	y: 0,
161	steps: [],
162	scale: 1,
163	currPageX: 0, currPageY: 0,
164	pagesX: [], pagesY: [],
165	aniTime: null,
166	wheelZoomCount: 0,
167	
168	handleEvent: function (e) {
169		var that = this;
170		switch(e.type) {
171			case START_EV:
172				if (!hasTouch && e.button !== 0) return;
173				that._start(e);
174				break;
175			case MOVE_EV: that._move(e); break;
176			case END_EV:
177			case CANCEL_EV: that._end(e); break;
178			case RESIZE_EV: that._resize(); break;
179			case WHEEL_EV: that._wheel(e); break;
180			case 'mouseout': that._mouseout(e); break;
181			case 'webkitTransitionEnd': that._transitionEnd(e); break;
182		}
183	},
184	
185	_checkDOMChanges: function () {
186		if (this.moved || this.zoomed || this.animating ||
187			(this.scrollerW == this.scroller.offsetWidth * this.scale && this.scrollerH == this.scroller.offsetHeight * this.scale)) return;
188
189		this.refresh();
190	},
191	
192	_scrollbar: function (dir) {
193		var that = this,
194			doc = document,
195			bar;
196
197		if (!that[dir + 'Scrollbar']) {
198			if (that[dir + 'ScrollbarWrapper']) {
199				if (hasTransform) that[dir + 'ScrollbarIndicator'].style[vendor + 'Transform'] = '';
200				that[dir + 'ScrollbarWrapper'].parentNode.removeChild(that[dir + 'ScrollbarWrapper']);
201				that[dir + 'ScrollbarWrapper'] = null;
202				that[dir + 'ScrollbarIndicator'] = null;
203			}
204
205			return;
206		}
207
208		if (!that[dir + 'ScrollbarWrapper']) {
209			// Create the scrollbar wrapper
210			bar = doc.createElement('div');
211
212			if (that.options.scrollbarClass) bar.className = that.options.scrollbarClass + dir.toUpperCase();
213			else bar.style.cssText = 'position:absolute;z-index:100;' + (dir == 'h' ? 'height:7px;bottom:1px;left:2px;right:' + (that.vScrollbar ? '7' : '2') + 'px' : 'width:7px;bottom:' + (that.hScrollbar ? '7' : '2') + 'px;top:2px;right:1px');
214
215			bar.style.cssText += ';pointer-events:none;-' + vendor + '-transition-property:opacity;-' + vendor + '-transition-duration:' + (that.options.fadeScrollbar ? '350ms' : '0') + ';overflow:hidden;opacity:' + (that.options.hideScrollbar ? '0' : '1');
216
217			that.wrapper.appendChild(bar);
218			that[dir + 'ScrollbarWrapper'] = bar;
219
220			// Create the scrollbar indicator
221			bar = doc.createElement('div');
222			if (!that.options.scrollbarClass) {
223				bar.style.cssText = 'position:absolute;z-index:100;background:rgba(0,0,0,0.5);border:1px solid rgba(255,255,255,0.9);-' + vendor + '-background-clip:padding-box;-' + vendor + '-box-sizing:border-box;' + (dir == 'h' ? 'height:100%' : 'width:100%') + ';-' + vendor + '-border-radius:3px;border-radius:3px';
224			}
225			bar.style.cssText += ';pointer-events:none;-' + vendor + '-transition-property:-' + vendor + '-transform;-' + vendor + '-transition-timing-function:cubic-bezier(0.33,0.66,0.66,1);-' + vendor + '-transition-duration:0;-' + vendor + '-transform:' + trnOpen + '0,0' + trnClose;
226			if (that.options.useTransition) bar.style.cssText += ';-' + vendor + '-transition-timing-function:cubic-bezier(0.33,0.66,0.66,1)';
227
228			that[dir + 'ScrollbarWrapper'].appendChild(bar);
229			that[dir + 'ScrollbarIndicator'] = bar;
230		}
231
232		if (dir == 'h') {
233			that.hScrollbarSize = that.hScrollbarWrapper.clientWidth;
234			that.hScrollbarIndicatorSize = m.max(mround(that.hScrollbarSize * that.hScrollbarSize / that.scrollerW), 8);
235			that.hScrollbarIndicator.style.width = that.hScrollbarIndicatorSize + 'px';
236			that.hScrollbarMaxScroll = that.hScrollbarSize - that.hScrollbarIndicatorSize;
237			that.hScrollbarProp = that.hScrollbarMaxScroll / that.maxScrollX;
238		} else {
239			that.vScrollbarSize = that.vScrollbarWrapper.clientHeight;
240			that.vScrollbarIndicatorSize = m.max(mround(that.vScrollbarSize * that.vScrollbarSize / that.scrollerH), 8);
241			that.vScrollbarIndicator.style.height = that.vScrollbarIndicatorSize + 'px';
242			that.vScrollbarMaxScroll = that.vScrollbarSize - that.vScrollbarIndicatorSize;
243			that.vScrollbarProp = that.vScrollbarMaxScroll / that.maxScrollY;
244		}
245
246		// Reset position
247		that._scrollbarPos(dir, true);
248	},
249	
250	_resize: function () {
251		var that = this;
252		setTimeout(function () { that.refresh(); }, isAndroid ? 200 : 0);
253	},
254	
255	_pos: function (x, y) {
256		x = this.hScroll ? x : 0;
257		y = this.vScroll ? y : 0;
258
259		if (this.options.useTransform) {
260			this.scroller.style[vendor + 'Transform'] = trnOpen + x + 'px,' + y + 'px' + trnClose + ' scale(' + this.scale + ')';
261		} else {
262			x = mround(x);
263			y = mround(y);
264			this.scroller.style.left = x + 'px';
265			this.scroller.style.top = y + 'px';
266		}
267
268		this.x = x;
269		this.y = y;
270
271		this._scrollbarPos('h');
272		this._scrollbarPos('v');
273	},
274
275	_scrollbarPos: function (dir, hidden) {
276		var that = this,
277			pos = dir == 'h' ? that.x : that.y,
278			size;
279
280		if (!that[dir + 'Scrollbar']) return;
281
282		pos = that[dir + 'ScrollbarProp'] * pos;
283
284		if (pos < 0) {
285			if (!that.options.fixedScrollbar) {
286				size = that[dir + 'ScrollbarIndicatorSize'] + mround(pos * 3);
287				if (size < 8) size = 8;
288				that[dir + 'ScrollbarIndicator'].style[dir == 'h' ? 'width' : 'height'] = size + 'px';
289			}
290			pos = 0;
291		} else if (pos > that[dir + 'ScrollbarMaxScroll']) {
292			if (!that.options.fixedScrollbar) {
293				size = that[dir + 'ScrollbarIndicatorSize'] - mround((pos - that[dir + 'ScrollbarMaxScroll']) * 3);
294				if (size < 8) size = 8;
295				that[dir + 'ScrollbarIndicator'].style[dir == 'h' ? 'width' : 'height'] = size + 'px';
296				pos = that[dir + 'ScrollbarMaxScroll'] + (that[dir + 'ScrollbarIndicatorSize'] - size);
297			} else {
298				pos = that[dir + 'ScrollbarMaxScroll'];
299			}
300		}
301
302		that[dir + 'ScrollbarWrapper'].style[vendor + 'TransitionDelay'] = '0';
303		that[dir + 'ScrollbarWrapper'].style.opacity = hidden && that.options.hideScrollbar ? '0' : '1';
304		that[dir + 'ScrollbarIndicator'].style[vendor + 'Transform'] = trnOpen + (dir == 'h' ? pos + 'px,0' : '0,' + pos + 'px') + trnClose;
305	},
306	
307	_start: function (e) {
308		var that = this,
309			point = hasTouch ? e.touches[0] : e,
310			matrix, x, y,
311			c1, c2;
312
313		if (!that.enabled) return;
314
315		if (that.options.onBeforeScrollStart) that.options.onBeforeScrollStart.call(that, e);
316
317		if (that.options.useTransition || that.options.zoom) that._transitionTime(0);
318
319    that.active = true;
320		that.moved = false;
321		that.animating = false;
322		that.zoomed = false;
323		that.distX = 0;
324		that.distY = 0;
325		that.absDistX = 0;
326		that.absDistY = 0;
327		that.dirX = 0;
328		that.dirY = 0;
329
330		// Gesture start
331		if (that.options.zoom && hasTouch && e.touches.length > 1) {
332			c1 = m.abs(e.touches[0].pageX-e.touches[1].pageX);
333			c2 = m.abs(e.touches[0].pageY-e.touches[1].pageY);
334			that.touchesDistStart = m.sqrt(c1 * c1 + c2 * c2);
335
336			that.originX = m.abs(e.touches[0].pageX + e.touches[1].pageX - that.wrapperOffsetLeft * 2) / 2 - that.x;
337			that.originY = m.abs(e.touches[0].pageY + e.touches[1].pageY - that.wrapperOffsetTop * 2) / 2 - that.y;
338
339			if (that.options.onZoomStart) that.options.onZoomStart.call(that, e);
340		}
341
342		if (that.options.momentum) {
343			if (that.options.useTransform) {
344				// Very lame general purpose alternative to CSSMatrix
345				matrix = getComputedStyle(that.scroller, null)[vendor + 'Transform'].replace(/[^0-9-.,]/g, '').split(',');
346				x = matrix[4] * 1;
347				y = matrix[5] * 1;
348			} else {
349				x = getComputedStyle(that.scroller, null).left.replace(/[^0-9-]/g, '') * 1;
350				y = getComputedStyle(that.scroller, null).top.replace(/[^0-9-]/g, '') * 1;
351			}
352			
353			if (x != that.x || y != that.y) {
354				if (that.options.useTransition) that._unbind('webkitTransitionEnd');
355				else cancelFrame(that.aniTime);
356				that.steps = [];
357				that._pos(x, y);
358			}
359		}
360
361		that.absStartX = that.x;	// Needed by snap threshold
362		that.absStartY = that.y;
363
364		that.startX = that.x;
365		that.startY = that.y;
366		that.pointX = point.pageX;
367		that.pointY = point.pageY;
368
369		that.startTime = e.timeStamp || Date.now();
370
371		if (that.options.onScrollStart) that.options.onScrollStart.call(that, e);
372
373		that._bind(MOVE_EV);
374		that._bind(END_EV);
375		that._bind(CANCEL_EV);
376	},
377	
378	_move: function (e) {
379		var that = this,
380			point = hasTouch ? e.touches[0] : e,
381			deltaX = point.pageX - that.pointX,
382			deltaY = point.pageY - that.pointY,
383			newX = that.x + deltaX,
384			newY = that.y + deltaY,
385			c1, c2, scale,
386			timestamp = e.timeStamp || Date.now();
387
388		if (that.options.onBeforeScrollMove) that.options.onBeforeScrollMove.call(that, e);
389
390		// Zoom
391		if (that.options.zoom && hasTouch && e.touches.length > 1) {
392			c1 = m.abs(e.touches[0].pageX - e.touches[1].pageX);
393			c2 = m.abs(e.touches[0].pageY - e.touches[1].pageY);
394			that.touchesDist = m.sqrt(c1*c1+c2*c2);
395
396			that.zoomed = true;
397
398			scale = 1 / that.touchesDistStart * that.touchesDist * this.scale;
399
400			if (scale < that.options.zoomMin) scale = 0.5 * that.options.zoomMin * Math.pow(2.0, scale / that.options.zoomMin);
401			else if (scale > that.options.zoomMax) scale = 2.0 * that.options.zoomMax * Math.pow(0.5, that.options.zoomMax / scale);
402
403			that.lastScale = scale / this.scale;
404
405			newX = this.originX - this.originX * that.lastScale + this.x,
406			newY = this.originY - this.originY * that.lastScale + this.y;
407
408			this.scroller.style[vendor + 'Transform'] = trnOpen + newX + 'px,' + newY + 'px' + trnClose + ' scale(' + scale + ')';
409
410			if (that.options.onZoom) that.options.onZoom.call(that, e);
411			return;
412		}
413
414		that.pointX = point.pageX;
415		that.pointY = point.pageY;
416
417		// Slow down if outside of the boundaries
418		if (newX > 0 || newX < that.maxScrollX) {
419			newX = that.options.bounce ? that.x + (deltaX / 2) : newX >= 0 || that.maxScrollX >= 0 ? 0 : that.maxScrollX;
420		}
421		if (newY > that.minScrollY || newY < that.maxScrollY) { 
422			newY = that.options.bounce ? that.y + (deltaY / 2) : newY >= that.minScrollY || that.maxScrollY >= 0 ? that.minScrollY : that.maxScrollY;
423		}
424
425		if (that.absDistX < 6 && that.absDistY < 6) {
426			that.distX += deltaX;
427			that.distY += deltaY;
428			that.absDistX = m.abs(that.distX);
429			that.absDistY = m.abs(that.distY);
430
431			return;
432		}
433
434		// Lock direction
435		if (that.options.lockDirection) {
436			if (that.absDistX > that.absDistY + 5) {
437				newY = that.y;
438				deltaY = 0;
439			} else if (that.absDistY > that.absDistX + 5) {
440				newX = that.x;
441				deltaX = 0;
442			}
443		}
444
445		that.moved = true;
446		that._pos(newX, newY);
447		that.dirX = deltaX > 0 ? -1 : deltaX < 0 ? 1 : 0;
448		that.dirY = deltaY > 0 ? -1 : deltaY < 0 ? 1 : 0;
449
450		if (timestamp - that.startTime > 300) {
451			that.startTime = timestamp;
452			that.startX = that.x;
453			that.startY = that.y;
454		}
455		
456		if (that.options.onScrollMove) that.options.onScrollMove.call(that, e);
457	},
458	
459	_end: function (e) {
460		if (hasTouch && e.touches.length != 0) return;
461
462		var that = this,
463			point = hasTouch ? e.changedTouches[0] : e,
464			target, ev,
465			momentumX = { dist:0, time:0 },
466			momentumY = { dist:0, time:0 },
467			duration = (e.timeStamp || Date.now()) - that.startTime,
468			newPosX = that.x,
469			newPosY = that.y,
470			distX, distY,
471			newDuration,
472			snap,
473			scale;
474
475		that._unbind(MOVE_EV);
476		that._unbind(END_EV);
477		that._unbind(CANCEL_EV);
478
479    if (!that.active) return;
480    that.active = false;
481
482		if (that.options.onBeforeScrollEnd) that.options.onBeforeScrollEnd.call(that, e);
483
484		if (that.zoomed) {
485			scale = that.scale * that.lastScale;
486			scale = Math.max(that.options.zoomMin, scale);
487			scale = Math.min(that.options.zoomMax, scale);
488			that.lastScale = scale / that.scale;
489			that.scale = scale;
490
491			that.x = that.originX - that.originX * that.lastScale + that.x;
492			that.y = that.originY - that.originY * that.lastScale + that.y;
493			
494			that.scroller.style[vendor + 'TransitionDuration'] = '200ms';
495			that.scroller.style[vendor + 'Transform'] = trnOpen + that.x + 'px,' + that.y + 'px' + trnClose + ' scale(' + that.scale + ')';
496			
497			that.zoomed = false;
498			that.refresh();
499
500			if (that.options.onZoomEnd) that.options.onZoomEnd.call(that, e);
501			return;
502		}
503
504		if (!that.moved) {
505			if (hasTouch) {
506				if (that.doubleTapTimer && that.options.zoom) {
507					// Double tapped
508					clearTimeout(that.doubleTapTimer);
509					that.doubleTapTimer = null;
510					if (that.options.onZoomStart) that.options.onZoomStart.call(that, e);
511					that.zoom(that.pointX, that.pointY, that.scale == 1 ? that.options.doubleTapZoom : 1);
512					if (that.options.onZoomEnd) {
513						setTimeout(function() {
514							that.options.onZoomEnd.call(that, e);
515						}, 200); // 200 is default zoom duration
516					}
517				} else {
518					that.doubleTapTimer = setTimeout(function () {
519						that.doubleTapTimer = null;
520
521						// Find the last touched element
522						target = point.target;
523						while (target.nodeType != 1) target = target.parentNode;
524
525						if (target.tagName != 'SELECT' && target.tagName != 'INPUT' && target.tagName != 'TEXTAREA') {
526							ev = document.createEvent('MouseEvents');
527							ev.initMouseEvent('click', true, true, e.view, 1,
528								point.screenX, point.screenY, point.clientX, point.clientY,
529								e.ctrlKey, e.altKey, e.shiftKey, e.metaKey,
530								0, null);
531							ev._fake = true;
532							target.dispatchEvent(ev);
533						}
534					}, that.options.zoom ? 250 : 0);
535				}
536			}
537
538			that._resetPos(200);
539
540			if (that.options.onTouchEnd) that.options.onTouchEnd.call(that, e);
541			return;
542		}
543
544		if (duration < 300 && that.options.momentum) {
545			momentumX = newPosX ? that._momentum(newPosX - that.startX, duration, -that.x, that.scrollerW - that.wrapperW + that.x, that.options.bounce ? that.wrapperW : 0) : momentumX;
546			momentumY = newPosY ? that._momentum(newPosY - that.startY, duration, -that.y, (that.maxScrollY < 0 ? that.scrollerH - that.wrapperH + that.y - that.minScrollY : 0), that.options.bounce ? that.wrapperH : 0) : momentumY;
547
548			newPosX = that.x + momentumX.dist;
549			newPosY = that.y + momentumY.dist;
550
551 			if ((that.x > 0 && newPosX > 0) || (that.x < that.maxScrollX && newPosX < that.maxScrollX)) momentumX = { dist:0, time:0 };
552 			if ((that.y > that.minScrollY && newPosY > that.minScrollY) || (that.y < that.maxScrollY && newPosY < that.maxScrollY)) momentumY = { dist:0, time:0 };
553		}
554
555		if (momentumX.dist || momentumY.dist) {
556			newDuration = m.max(m.max(momentumX.time, momentumY.time), 10);
557
558			// Do we need to snap?
559			if (that.options.snap) {
560				distX = newPosX - that.absStartX;
561				distY = newPosY - that.absStartY;
562				if (m.abs(distX) < that.options.snapThreshold && m.abs(distY) < that.options.snapThreshold) { that.scrollTo(that.absStartX, that.absStartY, 200); }
563				else {
564					snap = that._snap(newPosX, newPosY);
565					newPosX = snap.x;
566					newPosY = snap.y;
567					newDuration = m.max(snap.time, newDuration);
568				}
569			}
570
571			that.scrollTo(mround(newPosX), mround(newPosY), newDuration);
572
573			if (that.options.onTouchEnd) that.options.onTouchEnd.call(that, e);
574			return;
575		}
576
577		// Do we need to snap?
578		if (that.options.snap) {
579			distX = newPosX - that.absStartX;
580			distY = newPosY - that.absStartY;
581			if (m.abs(distX) < that.options.snapThreshold && m.abs(distY) < that.options.snapThreshold) that.scrollTo(that.absStartX, that.absStartY, 200);
582			else {
583				snap = that._snap(that.x, that.y);
584				if (snap.x != that.x || snap.y != that.y) that.scrollTo(snap.x, snap.y, snap.time);
585			}
586
587			if (that.options.onTouchEnd) that.options.onTouchEnd.call(that, e);
588			return;
589		}
590
591		that._resetPos(200);
592		if (that.options.onTouchEnd) that.options.onTouchEnd.call(that, e);
593	},
594	
595	_resetPos: function (time) {
596		var that = this,
597			resetX = that.x >= 0 ? 0 : that.x < that.maxScrollX ? that.maxScrollX : that.x,
598			resetY = that.y >= that.minScrollY || that.maxScrollY > 0 ? that.minScrollY : that.y < that.maxScrollY ? that.maxScrollY : that.y;
599
600		if (resetX == that.x && resetY == that.y) {
601			if (that.moved) {
602				that.moved = false;
603				if (that.options.onScrollEnd) that.options.onScrollEnd.call(that);		// Execute custom code on scroll end
604			}
605
606			if (that.hScrollbar && that.options.hideScrollbar) {
607				if (vendor == 'webkit') that.hScrollbarWrapper.style[vendor + 'TransitionDelay'] = '300ms';
608				that.hScrollbarWrapper.style.opacity = '0';
609			}
610			if (that.vScrollbar && that.options.hideScrollbar) {
611				if (vendor == 'webkit') that.vScrollbarWrapper.style[vendor + 'TransitionDelay'] = '300ms';
612				that.vScrollbarWrapper.style.opacity = '0';
613			}
614
615			return;
616		}
617
618		that.scrollTo(resetX, resetY, time || 0);
619	},
620
621	_wheel: function (e) {
622		var that = this,
623			wheelDeltaX, wheelDeltaY,
624			deltaX, deltaY,
625			deltaScale;
626
627		if ('wheelDeltaX' in e) {
628			wheelDeltaX = e.wheelDeltaX / 12;
629			wheelDeltaY = e.wheelDeltaY / 12;
630		} else if ('detail' in e) {
631			wheelDeltaX = wheelDeltaY = -e.detail * 3;
632		} else {
633			wheelDeltaX = wheelDeltaY = -e.wheelDelta;
634		}
635		
636		if (that.options.wheelAction == 'zoom') {
637			deltaScale = that.scale * Math.pow(2, 1/3 * (wheelDeltaY ? wheelDeltaY / Math.abs(wheelDeltaY) : 0));
638			if (deltaScale < that.options.zoomMin) deltaScale = that.options.zoomMin;
639			if (deltaScale > that.options.zoomMax) deltaScale = that.options.zoomMax;
640			
641			if (deltaScale != that.scale) {
642				if (!that.wheelZoomCount && that.options.onZoomStart) that.options.onZoomStart.call(that, e);
643				that.wheelZoomCount++;
644				
645				that.zoom(e.pageX, e.pageY, deltaScale, 400);
646				
647				setTimeout(function() {
648					that.wheelZoomCount--;
649					if (!that.wheelZoomCount && that.options.onZoomEnd) that.options.onZoomEnd.call(that, e);
650				}, 400);
651			}
652			
653			return;
654		}
655		
656		deltaX = that.x + wheelDeltaX;
657		deltaY = that.y + wheelDeltaY;
658
659		if (deltaX > 0) deltaX = 0;
660		else if (deltaX < that.maxScrollX) deltaX = that.maxScrollX;
661
662		if (deltaY > that.minScrollY) deltaY = that.minScrollY;
663		else if (deltaY < that.maxScrollY) deltaY = that.maxScrollY;
664
665		that.scrollTo(deltaX, deltaY, 0);
666	},
667	
668	_mouseout: function (e) {
669		var t = e.relatedTarget;
670
671		if (!t) {
672			this._end(e);
673			return;
674		}
675
676		while (t = t.parentNode) if (t == this.wrapper) return;
677		
678		this._end(e);
679	},
680
681	_transitionEnd: function (e) {
682		var that = this;
683
684		if (e.target != that.scroller) return;
685
686		that._unbind('webkitTransitionEnd');
687		
688		that._startAni();
689	},
690
691
692	/**
693	 *
694	 * Utilities
695	 *
696	 */
697	_startAni: function () {
698		var that = this,
699			startX = that.x, startY = that.y,
700			startTime = Date.now(),
701			step, easeOut,
702			animate;
703
704		if (that.animating) return;
705		
706		if (!that.steps.length) {
707			that._resetPos(400);
708			return;
709		}
710		
711		step = that.steps.shift();
712		
713		if (step.x == startX && step.y == startY) step.time = 0;
714
715		that.animating = true;
716		that.moved = true;
717		
718		if (that.options.useTransition) {
719			that._transitionTime(step.time);
720			that._pos(step.x, step.y);
721			that.animating = false;
722			if (step.time) that._bind('webkitTransitionEnd');
723			else that._resetPos(0);
724			return;
725		}
726
727		animate = function () {
728			var now = Date.now(),
729				newX, newY;
730
731			if (now >= startTime + step.time) {
732				that._pos(step.x, step.y);
733				that.animating = false;
734				if (that.options.onAnimationEnd) that.options.onAnimationEnd.call(that);			// Execute custom code on animation end
735				that._startAni();
736				return;
737			}
738
739			now = (now - startTime) / step.time - 1;
740			easeOut = m.sqrt(1 - now * now);
741			newX = (step.x - startX) * easeOut + startX;
742			newY = (step.y - startY) * easeOut + startY;
743			that._pos(newX, newY);
744			if (that.animating) that.aniTime = nextFrame(animate);
745		};
746
747		animate();
748	},
749
750	_transitionTime: function (time) {
751		time += 'ms';
752		this.scroller.style[vendor + 'TransitionDuration'] = time;
753		if (this.hScrollbar) this.hScrollbarIndicator.style[vendor + 'TransitionDuration'] = time;
754		if (this.vScrollbar) this.vScrollbarIndicator.style[vendor + 'TransitionDuration'] = time;
755	},
756
757	_momentum: function (dist, time, maxDistUpper, maxDistLower, size) {
758		var deceleration = 0.0006,
759			speed = m.abs(dist) / time,
760			newDist = (speed * speed) / (2 * deceleration),
761			newTime = 0, outsideDist = 0;
762
763		// Proportinally reduce speed if we are outside of the boundaries 
764		if (dist > 0 && newDist > maxDistUpper) {
765			outsideDist = size / (6 / (newDist / speed * deceleration));
766			maxDistUpper = maxDistUpper + outsideDist;
767			speed = speed * maxDistUpper / newDist;
768			newDist = maxDistUpper;
769		} else if (dist < 0 && newDist > maxDistLower) {
770			outsideDist = size / (6 / (newDist / speed * deceleration));
771			maxDistLower = maxDistLower + outsideDist;
772			speed = speed * maxDistLower / newDist;
773			newDist = maxDistLower;
774		}
775
776		newDist = newDist * (dist < 0 ? -1 : 1);
777		newTime = speed / deceleration;
778
779		return { dist: newDist, time: mround(newTime) };
780	},
781
782	_offset: function (el) {
783		var left = -el.offsetLeft,
784			top = -el.offsetTop;
785			
786		while (el = el.offsetParent) {
787			left -= el.offsetLeft;
788			top -= el.offsetTop;
789		}
790		
791		if (el != this.wrapper) {
792			left *= this.scale;
793			top *= this.scale;
794		}
795
796		return { left: left, top: top };
797	},
798
799	_snap: function (x, y) {
800		var that = this,
801			i, l,
802			page, time,
803			sizeX, sizeY;
804
805		// Check page X
806		page = that.pagesX.length - 1;
807		for (i=0, l=that.pagesX.length; i<l; i++) {
808			if (x >= that.pagesX[i]) {
809				page = i;
810				break;
811			}
812		}
813		if (page == that.currPageX && page > 0 && that.dirX < 0) page--;
814		x = that.pagesX[page];
815		sizeX = m.abs(x - that.pagesX[that.currPageX]);
816		sizeX = sizeX ? m.abs(that.x - x) / sizeX * 500 : 0;
817		that.currPageX = page;
818
819		// Check page Y
820		page = that.pagesY.length-1;
821		for (i=0; i<page; i++) {
822			if (y >= that.pagesY[i]) {
823				page = i;
824				break;
825			}
826		}
827		if (page == that.currPageY && page > 0 && that.dirY < 0) page--;
828		y = that.pagesY[page];
829		sizeY = m.abs(y - that.pagesY[that.currPageY]);
830		sizeY = sizeY ? m.abs(that.y - y) / sizeY * 500 : 0;
831		that.currPageY = page;
832
833		// Snap with constant speed (proportional duration)
834		time = mround(m.max(sizeX, sizeY)) || 200;
835
836		return { x: x, y: y, time: time };
837	},
838
839	_bind: function (type, el, bubble) {
840		(el || this.scroller).addEventListener(type, this, !!bubble);
841	},
842
843	_unbind: function (type, el, bubble) {
844		(el || this.scroller).removeEventListener(type, this, !!bubble);
845	},
846
847
848	/**
849	 *
850	 * Public methods
851	 *
852	 */
853	destroy: function () {
854		var that = this;
855
856		that.scroller.style[vendor + 'Transform'] = '';
857
858		// Remove the scrollbars
859		that.hScrollbar = false;
860		that.vScrollbar = false;
861		that._scrollbar('h');
862		that._scrollbar('v');
863
864		// Remove the event listeners
865		that._unbind(RESIZE_EV, window);
866		that._unbind(START_EV);
867		that._unbind(MOVE_EV);
868		that._unbind(END_EV);
869		that._unbind(CANCEL_EV);
870		
871		if (that.options.hasTouch) {
872			that._unbind('mouseout', that.wrapper);
873			that._unbind(WHEEL_EV);
874		}
875		
876		if (that.options.useTransition) that._unbind('webkitTransitionEnd');
877		
878		if (that.options.checkDOMChanges) clearInterval(that.checkDOMTime);
879		
880		if (that.options.onDestroy) that.options.onDestroy.call(that);
881	},
882
883	refresh: function () {
884		var that = this,
885			offset,
886			i, l,
887			els,
888			pos = 0,
889			page = 0;
890
891		if (that.scale < that.options.zoomMin) that.scale = that.options.zoomMin;
892		that.wrapperW = that.wrapper.clientWidth || 1;
893		that.wrapperH = that.wrapper.clientHeight || 1;
894
895		that.minScrollY = -that.options.topOffset || 0;
896		that.scrollerW = mround(that.scroller.offsetWidth * that.scale);
897		that.scrollerH = mround((that.scroller.offsetHeight + that.minScrollY) * that.scale);
898		that.maxScrollX = that.wrapperW - that.scrollerW;
899		that.maxScrollY = that.wrapperH - that.scrollerH + that.minScrollY;
900		that.dirX = 0;
901		that.dirY = 0;
902
903		if (that.options.onRefresh) that.options.onRefresh.call(that);
904
905		that.hScroll = that.options.hScroll && that.maxScrollX < 0;
906		that.vScroll = that.options.vScroll && (!that.options.bounceLock && !that.hScroll || that.scrollerH > that.wrapperH);
907
908		that.hScrollbar = that.hScroll && that.options.hScrollbar;
909		that.vScrollbar = that.vScroll && that.options.vScrollbar && that.scrollerH > that.wrapperH;
910
911		offset = that._offset(that.wrapper);
912		that.wrapperOffsetLeft = -offset.left;
913		that.wrapperOffsetTop = -offset.top;
914
915		// Prepare snap
916		if (typeof that.options.snap == 'string') {
917			that.pagesX = [];
918			that.pagesY = [];
919			els = that.scroller.querySelectorAll(that.options.snap);
920			for (i=0, l=els.length; i<l; i++) {
921				pos = that._offset(els[i]);
922				pos.left += that.wrapperOffsetLeft;
923				pos.top += that.wrapperOffsetTop;
924				that.pagesX[i] = pos.left < that.maxScrollX ? that.maxScrollX : pos.left * that.scale;
925				that.pagesY[i] = pos.top < that.maxScrollY ? that.maxScrollY : pos.top * that.
925scale;
926			}
927		} else if (that.options.snap) {
928			that.pagesX = [];
929			while (pos >= that.maxScrollX) {
930				that.pagesX[page] = pos;
931				pos = pos - that.wrapperW;
932				page++;
933			}
934			if (that.maxScrollX%that.wrapperW) that.pagesX[that.pagesX.length] = that.maxScrollX - that.pagesX[that.pagesX.length-1] + that.pagesX[that.pagesX.length-1];
935
936			pos = 0;
937			page = 0;
938			that.pagesY = [];
939			while (pos >= that.maxScrollY) {
940				that.pagesY[page] = pos;
941				pos = pos - that.wrapperH;
942				page++;
943			}
944			if (that.maxScrollY%that.wrapperH) that.pagesY[that.pagesY.length] = that.maxScrollY - that.pagesY[that.pagesY.length-1] + that.pagesY[that.pagesY.length-1];
945		}
946
947		// Prepare the scrollbars
948		that._scrollbar('h');
949		that._scrollbar('v');
950
951		if (!that.zoomed) {
952			that.scroller.style[vendor + 'TransitionDuration'] = '0';
953			that._resetPos(200);
954		}
955	},
956
957	scrollTo: function (x, y, time, relative) {
958		var that = this,
959			step = x,
960			i, l;
961
962		that.stop();
963
964		if (!step.length) step = [{ x: x, y: y, time: time, relative: relative }];
965		
966		for (i=0, l=step.length; i<l; i++) {
967			if (step[i].relative) { step[i].x = that.x - step[i].x; step[i].y = that.y - step[i].y; }
968			that.steps.push({ x: step[i].x, y: step[i].y, time: step[i].time || 0 });
969		}
970
971		that._startAni();
972	},
973
974	scrollToElement: function (el, time) {
975		var that = this, pos;
976		el = el.nodeType ? el : that.scroller.querySelector(el);
977		if (!el) return;
978
979		pos = that._offset(el);
980		pos.left += that.wrapperOffsetLeft;
981		pos.top += that.wrapperOffsetTop;
982
983		pos.left = pos.left > 0 ? 0 : pos.left < that.maxScrollX ? that.maxScrollX : pos.left;
984		pos.top = pos.top > that.minScrollY ? that.minScrollY : pos.top < that.maxScrollY ? that.maxScrollY : pos.top;
985		time = time === undefined ? m.max(m.abs(pos.left)*2, m.abs(pos.top)*2) : time;
986
987		that.scrollTo(pos.left, pos.top, time);
988	},
989
990	scrollToPage: function (pageX, pageY, time) {
991		var that = this, x, y;
992
993		if (that.options.onScrollStart) that.options.onScrollStart.call(that);
994
995		if (that.options.snap) {
996			pageX = pageX == 'next' ? that.currPageX+1 : pageX == 'prev' ? that.currPageX-1 : pageX;
997			pageY = pageY == 'next' ? that.currPageY+1 : pageY == 'prev' ? that.currPageY-1 : pageY;
998
999			pageX = pageX < 0 ? 0 : pageX > that.pagesX.length-1 ? that.pagesX.length-1 : pageX;
1000			pageY = pageY < 0 ? 0 : pageY > that.pagesY.length-1 ? that.pagesY.length-1 : pageY;
1001
1002			that.currPageX = pageX;
1003			that.currPageY = pageY;
1004			x = that.pagesX[pageX];
1005			y = that.pagesY[pageY];
1006		} else {
1007			x = -that.wrapperW * pageX;
1008			y = -that.wrapperH * pageY;
1009			if (x < that.maxScrollX) x = that.maxScrollX;
1010			if (y < that.maxScrollY) y = that.maxScrollY;
1011		}
1012
1013		that.scrollTo(x, y, time || 400);
1014	},
1015
1016	disable: function () {
1017		this.stop();
1018		this._resetPos(0);
1019		this.enabled = false;
1020
1021		// If disabled after touchstart we make sure that there are no left over events
1022		this._unbind(MOVE_EV);
1023		this._unbind(END_EV);
1024		this._unbind(CANCEL_EV);
1025	},
1026	
1027	enable: function () {
1028		this.enabled = true;
1029	},
1030	
1031	stop: function () {
1032		if (this.options.useTransition) this._unbind('webkitTransitionEnd');
1033		else cancelFrame(this.aniTime);
1034		this.steps = [];
1035		this.moved = false;
1036		this.animating = false;
1037	},
1038	
1039	zoom: function (x, y, scale, time) {
1040		var that = this,
1041			relScale = scale / that.scale;
1042
1043		if (!that.options.useTransform) return;
1044
1045		that.zoomed = true;
1046		time = time === undefined ? 200 : time;
1047		x = x - that.wrapperOffsetLeft - that.x;
1048		y = y - that.wrapperOffsetTop - that.y;
1049		that.x = x - x * relScale + that.x;
1050		that.y = y - y * relScale + that.y;
1051
1052		that.scale = scale;
1053		that.refresh();
1054
1055		that.x = that.x > 0 ? 0 : that.x < that.maxScrollX ? that.maxScrollX : that.x;
1056		that.y = that.y > that.minScrollY ? that.minScrollY : that.y < that.maxScrollY ? that.maxScrollY : that.y;
1057
1058		that.scroller.style[vendor + 'TransitionDuration'] = time + 'ms';
1059		that.scroller.style[vendor + 'Transform'] = trnOpen + that.x + 'px,' + that.y + 'px' + trnClose + ' scale(' + scale + ')';
1060		that.zoomed = false;
1061	},
1062	
1063	isReady: function () {
1064		return !this.moved && !this.zoomed && !this.animating;
1065	}
1066};
1067
1068if (typeof exports !== 'undefined') exports.iScroll = iScroll;
1069else window.iScroll = iScroll;
1070
1071})();

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.