1/* 2 * Swipe 2.0 3 * 4 * Brad Birdsall 5 * Copyright 2013, MIT License 6 * 7*/ 8 9function Swipe(container, options) { 10 11 "use strict"; 12 13 // utilities 14 var noop = function() {}; // simple no operation function 15 var offloadFn = function(fn) { setTimeout(fn || noop, 0) }; // offload a functions execution 16 17 // check browser capabilities 18 var browser = { 19 addEventListener: !!window.addEventListener, 20 touch: ('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch, 21 transitions: (function(temp) { 22 var props = ['transitionProperty', 'WebkitTransition', 'MozTransition', 'OTransition', 'msTransition']; 23 for ( var i in props ) if (temp.style[ props[i] ] !== undefined) return true; 24 return false; 25 })(document.createElement('swipe')) 26 }; 27 28 // quit if no root element 29 if (!container) return; 30 var element = container.children[0]; 31 var slides, slidePos, width, length; 32 options = options || {}; 33 var index = parseInt(options.startSlide, 10) || 0; 34 var speed = options.speed || 300; 35 options.continuous = options.continuous !== undefined ? options.continuous : true; 36 37 function setup() { 38 39 // cache slides 40 slides = element.children; 41 length = slides.length; 42 43 // set continuous to false if only one slide 44 if (slides.length < 2) options.continuous = false; 45 46 //special case if two slides 47 if (browser.transitions && options.continuous && slides.length < 3) { 48 element.appendChild(slides[0].cloneNode(true)); 49 element.appendChild(element.children[1].cloneNode(true)); 50 slides = element.children; 51 } 52 53 // create an array to store current positions of each slide 54 slidePos = new Array(slides.length); 55 56 // determine width of each slide 57 width = container.getBoundingClientRect().width || container.offsetWidth; 58 59 element.style.width = (slides.length * width) + 'px'; 60 61 // stack elements 62 var pos = slides.length; 63 while(pos--) { 64 65 var slide = slides[pos]; 66 67 slide.style.width = width + 'px'; 68 slide.setAttribute('data-index', pos); 69 70 if (browser.transitions) { 71 slide.style.left = (pos * -width) + 'px'; 72 move(pos, index > pos ? -width : (index < pos ? width : 0), 0); 73 } 74 75 } 76 77 // reposition elements before and after index 78 if (options.continuous && browser.transitions) { 79 move(circle(index-1), -width, 0); 80 move(circle(index+1), width, 0); 81 } 82 83 if (!browser.transitions) element.style.left = (index * -width) + 'px'; 84 85 container.style.visibility = 'visible'; 86 87 } 88 89 function prev() { 90 91 if (options.continuous) slide(index-1); 92 else if (index) slide(index-1); 93 94 } 95 96 function next() { 97 98 if (options.continuous) slide(index+1); 99 else if (index < slides.length - 1) slide(index+1); 100 101 } 102 103 function circle(index) { 104 105 // a simple positive modulo using slides.length 106 return (slides.length + (index % slides.length)) % slides.length; 107 108 } 109 110 function slide(to, slideSpeed) { 111 112 // do nothing if already on requested slide 113 if (index == to) return; 114 115 if (browser.transitions) { 116 117 var direction = Math.abs(index-to) / (index-to); // 1: backward, -1: forward 118 119 // get the actual position of the slide 120 if (options.continuous) { 121 var natural_direction = direction; 122 direction = -slidePos[circle(to)] / width; 123 124 // if going forward but to < index, use to = slides.length + to 125 // if going backward but to > index, use to = -slides.length + to 126 if (direction !== natural_direction) to = -direction * slides.length + to; 127 128 } 129 130 var diff = Math.abs(index-to) - 1; 131 132 // move all the slides between index and to in the right direction 133 while (diff--) move( circle((to > index ? to : index) - diff - 1), width * direction, 0); 134 135 to = circle(to); 136 137 move(index, width * direction, slideSpeed || speed); 138 move(to, 0, slideSpeed || speed); 139 140 if (options.continuous) move(circle(to - direction), -(width * direction), 0); // we need to get the next in place 141 142 } else { 143 144 to = circle(to); 145 animate(index * -width, to * -width, slideSpeed || speed); 146 //no fallback for a circular continuous if the browser does not accept transitions 147 } 148 149 index = to; 150 offloadFn(options.callback && options.callback(index, slides[index])); 151 } 152 153 function move(index, dist, speed) { 154 155 translate(index, dist, speed); 156 slidePos[index] = dist; 157 158 } 159 160 function translate(index, dist, speed) { 161 162 var slide = slides[index]; 163 var style = slide && slide.style; 164 165 if (!style) return; 166 167 style.webkitTransitionDuration = 168 style.MozTransitionDuration = 169 style.msTransitionDuration = 170 style.OTransitionDuration = 171 style.transitionDuration = speed + 'ms'; 172 173 style.webkitTransform = 'translate(' + dist + 'px,0)' + 'translateZ(0)'; 174 style.msTransform = 175 style.MozTransform = 176 style.OTransform = 'translateX(' + dist + 'px)'; 177 178 } 179 180 function animate(from, to, speed) { 181 182 // if not an animation, just reposition 183 if (!speed) { 184 185 element.style.left = to + 'px'; 186 return; 187 188 } 189 190 var start = +new Date; 191 192 var timer = setInterval(function() { 193 194 var timeElap = +new Date - start; 195 196 if (timeElap > speed) { 197 198 element.style.left = to + 'px'; 199 200 if (delay) begin(); 201 202 options.transitionEnd && options.transitionEnd.call(event, index, slides[index]); 203 204 clearInterval(timer); 205 return; 206 207 } 208 209 element.style.left = (( (to - from) * (Math.floor((timeElap / speed) * 100) / 100) ) + from) + 'px'; 210 211 }, 4); 212 213 } 214 215 // setup auto slideshow
216 var delay = options.auto || 0; 217 var interval; 218 219 function begin() { 220 221 interval = setTimeout(next, delay); 222 223 } 224 225 function stop() { 226 227 delay = 0; 228 clearTimeout(interval); 229 230 } 231 232 233 // setup initial vars 234 var start = {}; 235 var delta = {}; 236 var isScrolling; 237 238 // setup event capturing 239 var events = { 240 241 handleEvent: function(event) { 242 243 switch (event.type) { 244 case 'touchstart': this.start(event); break; 245 case 'touchmove': this.move(event); break; 246 case 'touchend': offloadFn(this.end(event)); break; 247 case 'webkitTransitionEnd': 248 case 'msTransitionEnd': 249 case 'oTransitionEnd': 250 case 'otransitionend': 251 case 'transitionend': offloadFn(this.transitionEnd(event)); break; 252 case 'resize': offloadFn(setup); break; 253 } 254 255 if (options.stopPropagation) event.stopPropagation(); 256 257 }, 258 start: function(event) { 259 260 var touches = event.touches[0]; 261 262 // measure start values 263 start = { 264 265 // get initial touch coords 266 x: touches.pageX, 267 y: touches.pageY, 268 269 // store time to determine touch duration 270 time: +new Date 271 272 }; 273 274 // used for testing first move event 275 isScrolling = undefined; 276 277 // reset delta and end measurements 278 delta = {}; 279 280 // attach touchmove and touchend listeners 281 element.addEventListener('touchmove', this, false); 282 element.addEventListener('touchend', this, false); 283 284 }, 285 move: function(event) { 286 287 // ensure swiping with one touch and not pinching 288 if ( event.touches.length > 1 || event.scale && event.scale !== 1) return 289 290 if (options.disableScroll) event.preventDefault(); 291 292 var touches = event.touches[0]; 293 294 // measure change in x and y 295 delta = { 296 x: touches.pageX - start.x, 297 y: touches.pageY - start.y 298 } 299 300 // determine if scrolling test has run - one time test 301 if ( typeof isScrolling == 'undefined') { 302 isScrolling = !!( isScrolling || Math.abs(delta.x) < Math.abs(delta.y) ); 303 } 304 305 // if user is not trying to scroll vertically 306 if (!isScrolling) { 307 308 // prevent native scrolling 309 event.preventDefault(); 310 311 // stop slideshow 312 stop(); 313 314 // increase resistance if first or last slide 315 if (options.continuous) { // we don't add resistance at the end 316 317 translate(circle(index-1), delta.x + slidePos[circle(index-1)], 0); 318 translate(index, delta.x + slidePos[index], 0); 319 translate(circle(index+1), delta.x + slidePos[circle(index+1)], 0); 320 321 } else { 322 323 delta.x = 324 delta.x / 325 ( (!index && delta.x > 0 // if first slide and sliding left 326 || index == slides.length - 1 // or if last slide and sliding right 327 && delta.x < 0 // and if sliding at all 328 ) ? 329 ( Math.abs(delta.x) / width + 1 ) // determine resistance level 330 : 1 ); // no resistance if false 331 332 // translate 1:1 333 translate(index-1, delta.x + slidePos[index-1], 0); 334 translate(index, delta.x + slidePos[index], 0); 335 translate(index+1, delta.x + slidePos[index+1], 0); 336 } 337 338 } 339 340 }, 341 end: function(event) { 342 343 // measure duration 344 var duration = +new Date - start.time; 345 346 // determine if slide attempt triggers next/prev slide 347 var isValidSlide = 348 Number(duration) < 250 // if slide duration is less than 250ms 349 && Math.abs(delta.x) > 20 // and if slide amt is greater than 20px 350 || Math.abs(delta.x) > width/2; // or if slide amt is greater than half the width 351 352 // determine if slide attempt is past start and end 353 var isPastBounds = 354 !index && delta.x > 0 // if first slide and slide amt is greater than 0 355 || index == slides.length - 1 && delta.x < 0; // or if last slide and slide amt is less than 0 356 357 if (options.continuous) isPastBounds = false; 358 359 // determine direction of swipe (true:right, false:left) 360 var direction = delta.x < 0; 361 362 // if not scrolling vertically 363 if (!isScrolling) { 364 365 if (isValidSlide && !isPastBounds) { 366 367 if (direction) { 368 369 if (options.continuous) { // we need to get the next in this direction in place 370 371 move(circle(index-1), -width, 0); 372 move(circle(index+2), width, 0); 373 374 } else { 375 move(index-1, -width, 0); 376 } 377 378 move(index, slidePos[index]-width, speed); 379 move(circle(index+1), slidePos[circle(index+1)]-width, speed); 380 index = circle(index+1); 381 382 } else { 383 if (options.continuous) { // we need to get the next in this direction in place 384 385 move(circle(index+1), width, 0); 386 move(circle(index-2), -width, 0); 387 388 } else { 389 move(index+1, width, 0); 390 } 391 392 move(index, slidePos[index]+width, speed); 393 move(circle(index-1), slidePos[circle(index-1)]+width, speed); 394 index = circle(index-1); 395 396 } 397 398 options.callback && options.callback(index, slides[index]); 399 400 } else { 401 402 if (options.continuous) { 403 404 move(circle(index-1), -width, speed); 405 move(index, 0, speed); 406 move(circle(index+1), width, speed); 407 408 } else { 409 410 move(index-1, -width, speed); 411 move(index, 0, speed); 412 move(index+1, width, speed); 413 } 414 415 } 416 417 } 418 419 // kill touchmove and touchend event listeners until touchstart called again 420 element.removeEventListener('touchmove', events, false) 421 element.removeEventListener('touchend', events, false) 422 423 }, 424 transitionEnd: function(event) { 425 426 if (parseInt(event.target.getAttribute('data-index'), 10) == index) { 427 428 if (delay) begin(); 429 430 options.transitionEnd && options.transitionEnd.call(event, index, slides[index]); 431 432 } 433 434 } 435 436 } 437 438 // trigger setup 439 setup(); 440 441 // start auto slideshow if applicable 442 if (delay) begin(); 443 444 445 // add event listeners 446 if (browser.addEventListener) { 447 448 // set touchstart event on element 449 if (browser.touch) element.addEventListener('touchstart', events, false); 450 451 if (browser.transitions) { 452 element.addEventListener('webkitTransitionEnd', events, false); 453 element.addEventListener('msTransitionEnd', events, false); 454 element.addEventListener('oTransitionEnd', events, false); 455 element.addEventListener('otransitionend', events, false); 456 element.addEventListener('transitionend', events, false); 457 } 458 459 // set resize event on window 460 window.addEventListener('resize', events, false); 461 462 } else { 463 464 window.onresize = function () { setup() }; // to play nice with old IE 465 466 } 467 468 // expose the Swipe API 469 return { 470 setup: function() { 471 472 setup(); 473 474 }, 475 slide: function(to, speed) { 476 477 // cancel slideshow 478 stop(); 479 480 slide(to, speed); 481 482 }, 483 prev: function() { 484 485 // cancel slideshow 486 stop(); 487 488 prev(); 489 490 }, 491 next: function() { 492 493 // cancel slideshow 494 stop(); 495 496 next(); 497 498 }, 499 stop: function() { 500 501 // cancel slideshow 502 stop(); 503 504 }, 505 getPos: function() { 506 507 // return current index position 508 return index; 509 510 }, 511 getNumSlides: function() { 512 513 // return total number of slides 514 return length; 515 }, 516 kill: function() { 517 518 // cancel slideshow 519 stop(); 520 521 // reset element 522 element.style.width = ''; 523 element.style.left = ''; 524 525 // reset slides 526 var pos = slides.length; 527 while(pos--) { 528 529 var slide = slides[pos]; 530 slide.style.width = ''; 531 slide.style.left = ''; 532 533 if (browser.transitions) translate(pos, 0, 0); 534 535 } 536 537 // removed event listeners 538 if (browser.addEventListener) { 539 540 // remove current event listeners 541 element.removeEventListener('touchstart', events, false); 542 element.removeEventListener('webkitTransitionEnd', events, false); 543 element.removeEventListener('msTransitionEnd', events, false); 544 element.removeEventListener('oTransitionEnd', events, false); 545 element.removeEventListener('otransitionend', events, false); 546 element.removeEventListener('transitionend', events, false); 547 window.removeEventListener('resize', events, false); 548 549 } 550 else { 551 552 window.onresize = null; 553 554 } 555 556 } 557 } 558 559} 560 561 562if ( window.jQuery || window.Zepto ) { 563 (function($) { 564 $.fn.Swipe = function(params) { 565 return this.each(function() { 566 $(this).data('Swipe', new Swipe($(this)[0], params)); 567 }); 568 } 569 })( window.jQuery || window.Zepto ) 570}
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.