1/** 2 * flipsnap.js 3 * 4 * @version 0.5.6 5 * @url http://pxgrid.github.com/js-flipsnap/ 6 * 7 * Copyright 2011 PixelGrid, Inc. 8 * Licensed under the MIT License: 9 * http://www.opensource.org/licenses/mit-license.php 10 */ 11 12(function(window, document, undefined) { 13 14var div = document.createElement('div'); 15var prefix = ['webkit', 'moz', 'o', 'ms']; 16var saveProp = {}; 17var support = Flipsnap.support = {}; 18var gestureStart = false; 19 20support.transform3d = hasProp([ 21 'perspectiveProperty', 22 'WebkitPerspective', 23 'MozPerspective', 24 'OPerspective', 25 'msPerspective' 26]); 27 28support.transform = hasProp([ 29 'transformProperty', 30 'WebkitTransform', 31 'MozTransform', 32 'OTransform', 33 'msTransform' 34]); 35 36support.transition = hasProp([ 37 'transitionProperty', 38 'WebkitTransitionProperty', 39 'MozTransitionProperty', 40 'OTransitionProperty', 41 'msTransitionProperty' 42]); 43 44support.addEventListener = 'addEventListener' in window; 45support.mspointer = window.navigator.msPointerEnabled; 46 47support.cssAnimation = (support.transform3d || support.transform) && support.transition; 48 49var eventTypes = ['touch', 'mouse']; 50var events = { 51 start: { 52 touch: 'touchstart', 53 mouse: 'mousedown' 54 }, 55 move: { 56 touch: 'touchmove', 57 mouse: 'mousemove' 58 }, 59 end: { 60 touch: 'touchend', 61 mouse: 'mouseup' 62 } 63}; 64 65if (support.addEventListener) { 66 document.addEventListener('gesturestart', function() { 67 gestureStart = true; 68 }); 69 70 document.addEventListener('gestureend', function() { 71 gestureStart = false;
72 }); 73} 74 75function Flipsnap(element, opts) { 76 return (this instanceof Flipsnap) 77 ? this.init(element, opts) 78 : new Flipsnap(element, opts); 79} 80 81Flipsnap.prototype.init = function(element, opts) { 82 var self = this; 83 84 // set element 85 self.element = element; 86 if (typeof element === 'string') { 87 self.element = document.querySelector(element); 88 } 89 90 if (!self.element) { 91 throw new Error('element not found'); 92 } 93 94 if (support.mspointer) { 95 self.element.style.msTouchAction = 'none'; 96 } 97 98 // set opts 99 opts = opts || {}; 100 self.distance = opts.distance; 101 self.maxPoint = opts.maxPoint; 102 self.disableTouch = (opts.disableTouch === undefined) ? false : opts.disableTouch; 103 self.disable3d = (opts.disable3d === undefined) ? false : opts.disable3d; 104 self.transitionDuration = (opts.transitionDuration === undefined) ? '350ms' : opts.transitionDuration + 'ms'; 105 106 // set property 107 self.currentPoint = 0; 108 self.currentX = 0; 109 self.animation = false; 110 self.use3d = support.transform3d; 111 if (self.disable3d === true) { 112 self.use3d = false; 113 } 114 115 // set default style 116 if (support.cssAnimation) { 117 self._setStyle({ 118 transitionProperty: getCSSVal('transform'), 119 transitionTimingFunction: 'cubic-bezier(0,0,0.25,1)', 120 transitionDuration: '0ms', 121 transform: self._getTranslate(0) 122 }); 123 } 124 else { 125 self._setStyle({ 126 position: 'relative', 127 left: '0px' 128 }); 129 } 130 131 // initilize 132 self.refresh(); 133 134 eventTypes.forEach(function(type) { 135 self.element.addEventListener(events.start[type], self, false); 136 }); 137 138 return self; 139}; 140 141Flipsnap.prototype.handleEvent = function(event) { 142 var self = this; 143 144 switch (event.type) { 145 case events.start.touch: 146 case events.start.mouse: 147 self._touchStart(event); 148 break; 149 case events.move.touch: 150 case events.move.mouse: 151 self._touchMove(event); 152 break; 153 case events.end.touch: 154 case events.end.mouse: 155 self._touchEnd(event); 156 break; 157 case 'click': 158 self._click(event); 159 break; 160 } 161}; 162 163Flipsnap.prototype.refresh = function() { 164 var self = this; 165 166 // setting max point 167 self._maxPoint = (self.maxPoint === undefined) ? (function() { 168 var childNodes = self.element.childNodes, 169 itemLength = -1, 170 i = 0, 171 len = childNodes.length, 172 node; 173 for(; i < len; i++) { 174 node = childNodes[i]; 175 if (node.nodeType === 1) { 176 itemLength++; 177 } 178 } 179 180 return itemLength; 181 })() : self.maxPoint; 182 183 // setting distance 184 if (self.distance === undefined) { 185 if (self._maxPoint < 0) { 186 self._distance = 0; 187 } 188 else { 189 self._distance = self.element.scrollWidth / (self._maxPoint + 1); 190 } 191 } 192 else { 193 self._distance = self.distance; 194 } 195 196 // setting maxX 197 self._maxX = -self._distance * self._maxPoint; 198 199 self.moveToPoint(); 200}; 201 202Flipsnap.prototype.hasNext = function() { 203 var self = this; 204 205 return self.currentPoint < self._maxPoint; 206}; 207 208Flipsnap.prototype.hasPrev = function() { 209 var self = this; 210 211 return self.currentPoint > 0; 212}; 213 214Flipsnap.prototype.toNext = function(transitionDuration) { 215 var self = this; 216 217 if (!self.hasNext()) { 218 return; 219 } 220 221 self.moveToPoint(self.currentPoint + 1, transitionDuration); 222}; 223 224Flipsnap.prototype.toPrev = function(transitionDuration) { 225 var self = this; 226 227 if (!self.hasPrev()) { 228 return; 229 } 230 231 self.moveToPoint(self.currentPoint - 1, transitionDuration); 232}; 233 234Flipsnap.prototype.moveToPoint = function(point, transitionDuration) { 235 var self = this; 236 237 transitionDuration = transitionDuration === undefined 238 ? self.transitionDuration : transitionDuration + 'ms'; 239 240 var beforePoint = self.currentPoint; 241 242 // not called from `refresh()` 243 if (point === undefined) { 244 point = self.currentPoint; 245 } 246 247 if (point < 0) { 248 self.currentPoint = 0; 249 } 250 else if (point > self._maxPoint) { 251 self.currentPoint = self._maxPoint; 252 } 253 else { 254 self.currentPoint = parseInt(point, 10); 255 } 256 257 if (support.cssAnimation) { 258 self._setStyle({ transitionDuration: transitionDuration }); 259 } 260 else { 261 self.animation = true; 262 } 263 self._setX(- self.currentPoint * self._distance, transitionDuration); 264 265 if (beforePoint !== self.currentPoint) { // is move? 266 // `fsmoveend` is deprecated 267 // `fspointmove` is recommend. 268 self._triggerEvent('fsmoveend', true, false); 269 self._triggerEvent('fspointmove', true, false); 270 } 271}; 272 273Flipsnap.prototype._setX = function(x, transitionDuration) { 274 var self = this; 275 276 self.currentX = x; 277 if (support.cssAnimation) { 278 self.element.style[ saveProp.transform ] = self._getTranslate(x); 279 } 280 else { 281 if (self.animation) { 282 self._animate(x, transitionDuration || self.transitionDuration); 283 } 284 else { 285 self.element.style.left = x + 'px'; 286 } 287 } 288}; 289 290Flipsnap.prototype._touchStart = function(event) { 291 var self = this; 292 293 if (self.disableTouch || self._eventType || gestureStart) { 294 return; 295 } 296 297 some(eventTypes, function(type) { 298 if (event.type === events.start[type]) { 299 self._eventType = type; 300 return true; 301 } 302 }); 303 304 self.element.addEventListener(events.move[self._eventType], self, false); 305 document.addEventListener(events.end[self._eventType], self, false); 306 307 if (self._eventType === 'mouse') { 308 event.preventDefault(); 309 } 310 311 if (support.cssAnimation) { 312 self._setStyle({ transitionDuration: '0ms' }); 313 } 314 else { 315 self.animation = false;
316 } 317 self.scrolling = true; 318 self.moveReady = false; 319 self.startPageX = getPage(event, 'pageX'); 320 self.startPageY = getPage(event, 'pageY'); 321 self.basePageX = self.startPageX; 322 self.directionX = 0; 323 self.startTime = event.timeStamp; 324 self._triggerEvent('fstouchstart', true, false); 325}; 326 327Flipsnap.prototype._touchMove = function(event) { 328 var self = this; 329 330 if (!self.scrolling || gestureStart) { 331 return; 332 } 333 334 var pageX = getPage(event, 'pageX'), 335 pageY = getPage(event, 'pageY'), 336 distX, 337 newX, 338 deltaX, 339 deltaY; 340 341 if (self.moveReady) { 342 event.preventDefault(); 343 event.stopPropagation(); 344 345 distX = pageX - self.basePageX; 346 newX = self.currentX + distX; 347 if (newX >= 0 || newX < self._maxX) { 348 newX = Math.round(self.currentX + distX / 3); 349 } 350 351 // When distX is 0, use one previous value. 352 // For android firefox. When touchend fired, touchmove also 353 // fired and distX is certainly set to 0. 354 self.directionX = 355 distX === 0 ? self.directionX : 356 distX > 0 ? -1 : 1; 357 358 // if they prevent us then stop it 359 var isPrevent = !self._triggerEvent('fstouchmove', true, true, { 360 delta: distX, 361 direction: self.directionX 362 }); 363 364 if (isPrevent) { 365 self._touchAfter({ 366 moved: false, 367 originalPoint: self.currentPoint, 368 newPoint: self.currentPoint, 369 cancelled: true 370 }); 371 } else { 372 self._setX(newX); 373 } 374 } 375 else { 376 deltaX = Math.abs(pageX - self.startPageX); 377 deltaY = Math.abs(pageY - self.startPageY); 378 if (deltaX > 5) { 379 event.preventDefault(); 380 event.stopPropagation(); 381 self.moveReady = true; 382 self.element.addEventListener('click', self, true); 383 } 384 else if (deltaY > 5) { 385 self.scrolling = false; 386 } 387 } 388 389 self.basePageX = pageX; 390}; 391 392Flipsnap.prototype._touchEnd = function(event) { 393 var self = this; 394 395 self.element.removeEventListener(events.move[self._eventType], self, false); 396 document.removeEventListener(events.end[self._eventType], self, false); 397 self._eventType = null; 398 399 if (!self.scrolling) { 400 return; 401 } 402 403 var newPoint = -self.currentX / self._distance; 404 newPoint = 405 (self.directionX > 0) ? Math.ceil(newPoint) : 406 (self.directionX < 0) ? Math.floor(newPoint) : 407 Math.round(newPoint); 408 409 if (newPoint < 0) { 410 newPoint = 0; 411 } 412 else if (newPoint > self._maxPoint) { 413 newPoint = self._maxPoint; 414 } 415 416 self._touchAfter({ 417 moved: newPoint !== self.currentPoint, 418 originalPoint: self.currentPoint, 419 newPoint: newPoint, 420 cancelled: false 421 }); 422 423 self.moveToPoint(newPoint); 424}; 425 426Flipsnap.prototype._click = function(event) { 427 var self = this; 428 429 event.stopPropagation(); 430 event.preventDefault(); 431}; 432 433Flipsnap.prototype._touchAfter = function(params) { 434 var self = this; 435 436 self.scrolling = false; 437 self.moveReady = false; 438 439 setTimeout(function() { 440 self.element.removeEventListener('click', self, true); 441 }, 200); 442 443 self._triggerEvent('fstouchend', true, false, params); 444}; 445 446Flipsnap.prototype._setStyle = function(styles) { 447 var self = this; 448 var style = self.element.style; 449 450 for (var prop in styles) { 451 setStyle(style, prop, styles[prop]); 452 } 453}; 454 455Flipsnap.prototype._animate = function(x, transitionDuration) { 456 var self = this; 457 458 var elem = self.element; 459 var begin = +new Date(); 460 var from = parseInt(elem.style.left, 10); 461 var to = x; 462 var duration = parseInt(transitionDuration, 10); 463 var easing = function(time, duration) { 464 return -(time /= duration) * (time - 2); 465 }; 466 var timer = setInterval(function() { 467 var time = new Date() - begin; 468 var pos, now; 469 if (time > duration) { 470 clearInterval(timer); 471 now = to; 472 } 473 else { 474 pos = easing(time, duration); 475 now = pos * (to - from) + from; 476 } 477 elem.style.left = now + "px"; 478 }, 10); 479}; 480 481Flipsnap.prototype.destroy = function() { 482 var self = this; 483 484 eventTypes.forEach(function(type) { 485 self.element.removeEventListener(events.start[type], self, false); 486 }); 487}; 488 489Flipsnap.prototype._getTranslate = function(x) { 490 var self = this; 491 492 return self.use3d 493 ? 'translate3d(' + x + 'px, 0, 0)' 494 : 'translate(' + x + 'px, 0)'; 495}; 496
497Flipsnap.prototype._triggerEvent = function(type, bubbles, cancelable, data) { 498 var self = this; 499 500 var ev = document.createEvent('Event'); 501 ev.initEvent(type, bubbles, cancelable); 502 503 if (data) { 504 for (var d in data) { 505 if (data.hasOwnProperty(d)) { 506 ev[d] = data[d]; 507 } 508 } 509 } 510 511 return self.element.dispatchEvent(ev); 512}; 513 514function getPage(event, page) { 515 return event.changedTouches ? event.changedTouches[0][page] : event[page]; 516} 517 518function hasProp(props) { 519 return some(props, function(prop) { 520 return div.style[ prop ] !== undefined; 521 }); 522} 523 524function setStyle(style, prop, val) { 525 var _saveProp = saveProp[ prop ]; 526 if (_saveProp) { 527 style[ _saveProp ] = val; 528 } 529 else if (style[ prop ] !== undefined) { 530 saveProp[ prop ] = prop; 531 style[ prop ] = val; 532 } 533 else { 534 some(prefix, function(_prefix) { 535 var _prop = ucFirst(_prefix) + ucFirst(prop); 536 if (style[ _prop ] !== undefined) { 537 saveProp[ prop ] = _prop; 538 style[ _prop ] = val; 539 return true; 540 } 541 }); 542 } 543} 544 545function getCSSVal(prop) { 546 if (div.style[ prop ] !== undefined) { 547 return prop; 548 } 549 else { 550 var ret; 551 some(prefix, function(_prefix) { 552 var _prop = ucFirst(_prefix) + ucFirst(prop); 553 if (div.style[ _prop ] !== undefined) { 554 ret = '-' + _prefix + '-' + prop; 555 return true; 556 } 557 }); 558 return ret; 559 } 560} 561 562function ucFirst(str) { 563 return str.charAt(0).toUpperCase() + str.substr(1); 564} 565 566function some(ary, callback) { 567 for (var i = 0, len = ary.length; i < len; i++) { 568 if (callback(ary[i], i)) { 569 return true; 570 } 571 } 572 return false; 573} 574 575window.Flipsnap = Flipsnap; 576 577})(window, window.document);
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.