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