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