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