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