Team:BIT-China/js/photobox.js
From 2013.igem.org
/*!
photobox v1.8.0 (c) 2013 Yair Even Or <http://dropthebit.com> Uses jQuery-mousewheel Version: 3.0.6 by: (c) 2009 Brandon Aaron <http://brandonaaron.net> MIT-style license.
- /
(function($, doc, win){
"use strict"; var Photobox, photoboxes = [], photobox, options, images=[], imageLinks, activeImage = -1, activeURL, lastActive, activeType, prevImage, nextImage, thumbsStripe, docElm, APControl, transitionend = "transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd", isOldIE = !('placeholder' in doc.createElement('input')),noPointerEvents = (function(){ var el = $('
')[0]; el.style.cssText = 'pointer-events:auto'; return !el.style.pointerEvents})(), isMobile = 'ontouchend' in doc, thumbsContainerWidth, thumbsTotalWidth, activeThumb = $(), blankImg = "data:image/gif;base64,R0lGODlhAQABAIAAAP///////yH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==", transformOrigin = getPrefixed('transformOrigin'), transition = getPrefixed('transition'), // Preload images preload = {}, preloadPrev = new Image(), preloadNext = new Image(), // DOM elements closeBtn, image, video, prevBtn, nextBtn, caption, captionText, pbLoader, autoplayBtn, thumbs, wrapper, defaults = { loop: true, // Allows to navigate between first and last images thumbs: true, // Show gallery thumbnails below the presented photo counter: true, // Counter text (example [24/62]) title: true, // show the original alt or title attribute of the image's thumbnail autoplay: false, // should autoplay on first time or not time: 3000, // autoplay interna, in miliseconds (less than 1000 will hide the autoplay button) history: true, // should use history hashing if possible (HTML5 API) hideFlash: true, // Hides flash elements on the page when photobox is activated. NOTE: flash elements must have wmode parameter set to "opaque" or "transparent" if this is set to false zoomable: true, // disable/enable mousewheel image zooming keys: { close: '27, 88, 67', // keycodes to close Picbox, default: Esc (27), 'x' (88), 'c' (67) prev: '37, 80', // keycodes to navigate to the previous image, default: Left arrow (37), 'p' (80) next: '39, 78' // keycodes to navigate to the next image, default: Right arrow (39), 'n' (78) } }, // DOM structure overlay = $('
image = $('<img>'),video = $('
),closeBtn = $('
),caption = $('
) ); /*--------------------------------------------------------------- Initialization (on DOM ready) */ function prepareDOM(){ // if useragent is IE < 10 (user deserves a slap on the face, but I gotta support them still...) isOldIE && overlay.addClass('msie'); noPointerEvents && overlay.hide();
autoplayBtn.off().on('click', APControl.toggle); // attach a delegated event on the thumbs container thumbs.off().on('click', 'a', thumbsStripe.click); // enable scrolling gesture on mobile isMobile && thumbs.css('overflow', 'auto'); // cancel prppogation up to the overlay container so it won't close overlay.off().on('click', 'img', function(e){ e.stopPropagation(); });
$(doc.body).prepend( $(overlay) ); // need this for later: docElm = doc.documentElement; } // @param [List of elements to work on, Custom settings, Callback after image is loaded] $.fn.photobox = function(target, settings, callback){
if( $(this).data('_photobox') ) // don't initiate the plugin more than once on the same element return this;
if( typeof target != 'string' ) target = 'a'; if( target === 'prepareDOM' ){ prepareDOM();
return this; }
var _options = $.extend({}, defaults, settings || {}), pb = new Photobox(_options, this, target);
// Saves the insance on the gallery's target element $(this).data('_photobox', pb); // add a callback to the specific gallery pb.callback = callback; // save every created gallery pointer photoboxes.push( pb ); return this; } Photobox = function(_options, object, target){ this.options = $.extend({}, _options); this.target = target; this.selector = $(object || doc); this.thumbsList = null; // filter the links which actually HAS an image as a child var filtered = this.imageLinksFilter( object.find(target) );
this.imageLinks = filtered[0]; // Array of jQuery links this.images = filtered[1]; // 2D Array of image URL & title
this.init();
};
Photobox.prototype = { init : function(){ var that = this; // only generates the thumbStripe once, and listen for any DOM changes on the selector element, if so, re-generate if( this.options.thumbs ) // generate gallery thumbnails every time (cause links might have been changed dynamicly) this.thumbsList = thumbsStripe.generate(this.imageLinks); this.selector.on('click.photobox', this.target, function(e){ e.preventDefault(); that.open(this); }); // if any node was added or removed from the Selector of the gallery this.observerTimeout = null; if( this.selector[0].nodeType == 1 ) // observe normal nodes that.observeDOM( that.selector[0] ,function(){ // use a timeout to prevent more than one DOM change event fireing at once, and also to overcome the fact that IE's DOMNodeRemoved is fired BEFORE elements were actually removed clearTimeout(that.observerTimeout); that.observerTimeout = setTimeout( function(){ var filtered = that.imageLinksFilter( that.selector.find(that.target) ),
activeIndex = 0;
that.imageLinks = filtered[0]; that.images = filtered[1];
images = that.images; imageLinks = that.imageLinks;
that.thumbsList = thumbsStripe.generate(that.imageLinks);
thumbs.html( that.thumbsList );
if( activeURL ){ activeIndex = that.thumbsList.find('a[href="'+activeURL+'"]').eq(0).parent().index(); updateIndexes(activeIndex); thumbsStripe.changeActive(activeIndex, 0); }
}, 50); }); },
open : function(link){ var startImage = $.inArray(link, this.imageLinks); // if image link does not exist in the imageLinks array (probably means it's not a valid part of the gallery) if( startImage == -1 ) return false;
// load the right gallery selector... options = this.options; images = this.images; imageLinks = this.imageLinks; photobox = this; this.setup(1);
overlay.on(transitionend, function(){ overlay.off(transitionend).addClass('on'); // class 'on' is set when the initial fade-in of the overlay is done changeImage(startImage, true); }).addClass('show'); if( isOldIE ) overlay.trigger('MSTransitionEnd'); return false; },
imageLinksFilter : function(obj){ var images = []; return [obj.filter(function(i){ var link = this, img = $(link).find('img')[0]; // if no img child found in the link if( !img ) return false; images.push([link.href, img.getAttribute('alt') || img.getAttribute('title') || ]);
return true; }), images]; },
//check if DOM nodes were added or removed, to re-build the imageLinks and thumbnails observeDOM : (function(){ var MutationObserver = win.MutationObserver || win.WebKitMutationObserver, eventListenerSupported = win.addEventListener; return function(obj, callback){ if( MutationObserver ){ // define a new observer var obs = new MutationObserver(function(mutations, observer){ if( mutations[0].addedNodes.length || mutations[0].removedNodes.length ) callback(); }); // have the observer observe foo for changes in children obs.observe( obj, { childList:true, subtree:true }); } else if( eventListenerSupported ){ obj.addEventListener('DOMNodeInserted', callback, false); obj.addEventListener('DOMNodeRemoved', callback, false); } } })(),
// things that should happend everytime the gallery opens or closes (some messed up code below..) setup : function (open){ var fn = open ? "on" : "off";
// a hack to change the image src to nothing, because you can't do that in CHROME image[0].src = blankImg; if( open ){ image.css({'transition':'0s'}).removeAttr('style'); // reset any transition that might be on the element (yes it's ugly) overlay.show(); // Clean up if another gallery was veiwed before, which had a thumbsList thumbs.html( this.thumbsList ); overlay[options.thumbs ? 'addClass' : 'removeClass']('thumbs'); if( options.thumbs ){ activeThumb.removeAttr('class'); $(win).on('resize.photobox', thumbsStripe.calc); thumbsStripe.calc(); // initiate the function for the first time without any window resize }
// things to hide if there are less than 2 images if( this.images.length < 2 ) overlay.removeClass('thumbs hasArrows hasCounter hasAutoplay'); else{ overlay.addClass('hasArrows hasCounter') // check is the autoplay button should be visible (per gallery) and if so, should it autoplay or not. if( options.time > 1000 ){ overlay.addClass('hasAutoplay'); if( options.autoplay ) APControl.progress.start(); else APControl.pause(); } else overlay.removeClass('hasAutoplay'); } } else { $(win).off('resize.photobox'); } if( options.hideFlash ){ $.each(["object", "embed"], function(i, val){ $(val).each(function(){ if (open) this._photobox = this.style.visibility; this.style.visibility = open ? "hidden" : this._photobox; }); }); } $(doc).off("keydown.photobox")[fn]({ "keydown.photobox": keyDown }); if( 'ontouchstart' in document.documentElement ){ overlay.removeClass('hasArrows'); // no need for Arros on touch-enabled wrapper[fn]('swipe', onSwipe); } if( options.zoomable ){ overlay[fn]({"mousewheel.photobox": scrollZoom }); if( !isOldIE) thumbs[fn]({"mousewheel.photobox": thumbsResize }); } }, destroy : function(){ this.selector .off('click.photobox', this.target) .removeData('_photobox'); close(); return this.selector; } } // on touch-devices only function onSwipe(e, Dx, Dy){ if( Dx == 1 ){ image.css({transform:'translateX(25%)', transition:'.7s', opacity:0}); setTimeout(function(){ changeImage(prevImage) }, 200); } else if( Dx == -1 ){ image.css({transform:'translateX(-25%)', transition:'.7s', opacity:0}); setTimeout(function(){ changeImage(nextImage) }, 200); } if( Dy == 1 ) thumbs.addClass('show'); else if( Dy == -1 ) thumbs.removeClass('show'); } // manage the (bottom) thumbs strip thumbsStripe = {// returns a
- element which is populated with all the gallery links and thumbs
generate : function(imageLinks){
var thumbsList = $('
- <a href="'+ link.href +'"><img src="'+ image[0].src +'" alt="" title="'+ title +'" /></a> ');
- '), link, elements = [], i, len = imageLinks.size(), title, image, type;
for( i = 0; i < len; i++ ){
link = imageLinks[i];
image = $(link).find('img');
title = image[0].title || image[0].alt || ;
type = link.rel ? " class='" + link.rel +"'" : ;
elements.push('
}; thumbsList.html( elements.join() ); return thumbsList; }, click : function(e){ e.preventDefault(); activeThumb.removeClass('active'); activeThumb = $(this).parent().addClass('active'); var imageIndex = $(this.parentNode).index(); return changeImage(imageIndex, 0, 1); }, changeActiveTimeout : null, /** Highlights the thumb which represents the photo and centers the thumbs viewer on it. ** @thumbClick - if a user clicked on a thumbnail, don't center on it */ changeActive : function(index, delay, thumbClick){ var lastIndex = activeThumb.index(); activeThumb.removeClass('active'); activeThumb = thumbs.find('li').eq(index).addClass('active'); if( thumbClick ) return; // set the scrollLeft position of the thumbs list to show the active thumb clearTimeout(this.changeActiveTimeout); // give the images time to to settle on their new sizes (because of css transition) and then calculate the center... this.changeActiveTimeout = setTimeout( function(){ var pos = activeThumb[0].offsetLeft + activeThumb[0].clientWidth/2 - docElm.clientWidth/2; delay ? thumbs.delay(800) : thumbs.stop(); thumbs.animate({scrollLeft: pos}, 500, 'swing'); }, 200); },
// claculate the thumbs container width is the window has been resized calc : function(){ thumbsContainerWidth = thumbs[0].clientWidth; thumbsTotalWidth = thumbs[0].firstChild.clientWidth;
var state = thumbsTotalWidth > thumbsContainerWidth ? 'on' : 'off'; !isMobile && thumbs[state]('mousemove', thumbsStripe.move); return this; },
// move the stipe left or right acording to mouse position move : function(e){ var ratio = thumbsTotalWidth / thumbsContainerWidth; thumbs[0].scrollLeft = e.pageX * ratio - 500; } }; // Autoplay controller APControl = { autoPlayTimer : false, play : function(){ APControl.autoPlayTimer = setTimeout(function(){ changeImage(nextImage) }, options.time); APControl.progress.start(); autoplayBtn.removeClass('play'); APControl.setTitle('Click to stop autoplay'); options.autoplay = true; }, pause : function(){ clearTimeout(APControl.autoPlayTimer); APControl.progress.reset(); autoplayBtn.addClass('play'); APControl.setTitle('Click to resume autoplay'); options.autoplay = false; }, progress : { reset : function(){ autoplayBtn.find('div').removeAttr('style'); setTimeout(function(){ autoplayBtn.removeClass('playing') },200); }, start : function(){ if( !isOldIE) autoplayBtn.find('div').css(transition, options.time+'ms'); autoplayBtn.addClass('playing'); } }, // sets the button Title property setTitle : function(text){ if(text) autoplayBtn.prop('title', text + ' (every ' + options.time/1000 + ' seconds)' ); }, // the button onClick handler toggle : function(e){ e.stopPropagation(); APControl[ options.autoplay ? 'pause' : 'play'](); } } function getPrefixed(prop){ var i, s = doc.createElement('p').style, v = ['ms','O','Moz','Webkit']; if( s[prop] == ) return prop; prop = prop.charAt(0).toUpperCase() + prop.slice(1); for( i = v.length; i--; ) if( s[v[i] + prop] == ) return (v[i] + prop); } function keyDown(event){ var code = event.keyCode, ok = options.keys, result; // Prevent default keyboard action (like navigating inside the page) return ok.close.indexOf(code) >= 0 && close() || ok.next.indexOf(code) >= 0 && changeImage(nextImage) || ok.prev.indexOf(code) >= 0 && changeImage(prevImage) || true; } // serves as a callback for pbPrevBtn / pbNextBtn buttons but also is called on keypress events function next_prev(){ // don't get crazy when user clicks next or prev buttons rapidly //if( !image.hasClass('zoomable') ) // return false; var img = (this.id == 'pbPrevBtn') ? prevImage : nextImage;
changeImage(img); return false; }
function updateIndexes(idx){ lastActive = activeImage;
activeImage = idx; activeURL = images[idx][0]; prevImage = (activeImage || (options.loop ? images.length : 0)) - 1; nextImage = ((activeImage + 1) % images.length) || (options.loop ? 0 : -1);
}
function changeImage(imageIndex, firstTime, thumbClick){ if( !imageIndex || imageIndex < 0 ) imageIndex = 0; overlay.removeClass('error').addClass( imageIndex > activeImage ? 'next' : 'prev' );
updateIndexes(imageIndex);
// reset things
stop();
video.empty(); preload.onerror = null; image.add(video).data('zoom', 1);
activeType = imageLinks[imageIndex].rel == 'video' ? 'video' : 'image';
// check if corrent link is a video if( activeType == 'video' ){ video.html( newVideo() ).addClass('hide'); showContent(firstTime); }
else{
// give a tiny delay to the preloader, so it won't be showed when images are already cached var loaderTimeout = setTimeout(function(){ overlay.addClass('pbLoading'); },50); // hide/show next-prev buttons if( !options.loop ){ nextBtn[ imageIndex == images.length-1 ? 'addClass' : 'removeClass' ]('hide'); prevBtn[ imageIndex == 0 ? 'addClass' : 'removeClass' ]('hide'); }
if( prevImage >= 0 ) preloadPrev.src = images[prevImage][0]; if( nextImage >= 0 ) preloadNext.src = images[nextImage][0];
if( isOldIE ) overlay.addClass('hide'); // should wait for the image onload. just hide the image while old ie display the preloader
options.autoplay && APControl.progress.reset(); preload = new Image(); preload.onload = function(){ clearTimeout(loaderTimeout); showContent(firstTime); }; preload.onerror = imageError; preload.src = activeURL;
}
// Show Caption text captionText.on(transitionend, captionTextChange).addClass('change'); if( firstTime || isOldIE ) captionTextChange();
if( options.thumbs ) thumbsStripe.changeActive(imageIndex, firstTime, thumbClick);
// Save url hash for current image history.save(); }
function newVideo(){ var url = images[activeImage][0], sign = $('<a>').prop('href',images[activeImage][0])[0].search ? '&' : '?'; url += sign + 'vq=hd720&wmode=opaque'; return $("<iframe>").prop({ scrolling:'no', frameborder:0, allowTransparency:true, src:url }).attr({webkitAllowFullScreen:true, mozallowfullscreen:true, allowFullScreen:true}); }
// show the item's Title & Counter function captionTextChange(){ captionText.off(transitionend).removeClass('change'); // change caption's text options.counter && caption.find('.counter').text('(' + (activeImage + 1) + ' / ' + images.length + ')'); options.title && caption.find('.title').text( images[activeImage][1] ); }
// Handles the history states when changing images var history = { save : function(){ // only save to history urls which are not already in the hash if('pushState' in window.history && decodeURIComponent(window.location.hash.slice(1)) != activeURL && options.history ){ window.history.pushState( 'photobox', doc.title + '-' + images[activeImage][1], window.location.pathname + window.location.search + '#' + encodeURIComponent(activeURL) ); } }, load : function(){ if( options && !options.history ) return false; var hash = decodeURIComponent( window.location.hash.slice(1) ), i, j; if( !hash && overlay.hasClass('show') ) close(); else // Scan all galleries for the image link (open the first gallery that has the link's image) for( i = 0; i < photoboxes.length; i++ ) for( j in photoboxes[i].images ) if( photoboxes[i].images[j][0] == hash ){ photoboxes[i].open( photoboxes[i].imageLinks[j] ); return true; } }, clear : function(){ if( options.history && 'pushState' in window.history ) window.history.pushState('photobox', doc.title, window.location.pathname + window.location.search); } };
// Add Photobox special `onpopstate` to the `onpopstate` function window.onpopstate = (function(){ var cached = window.onpopstate; return function(event){ cached && cached.apply(this, arguments); if( event.state == 'photobox' ) history.load(); } })(); // handles all image loading error (if image is dead) function imageError(){ overlay.addClass('error'); image[0].src = blankImg; // set the source to a blank image preload.onerror = null; }
// Shows the content (image/video) on the screen
function showContent(firstTime){
var out, showSaftyTimer; showSaftyTimer = setTimeout(show, 2000);
overlay.removeClass("pbLoading").addClass('hide');
image.add(video).removeAttr('style').removeClass('zoomable'); // while transitioning an image, do not apply the 'zoomable' class
// check which element needs to transition-out: if( !firstTime && imageLinks[lastActive].rel == 'video' ){ out = video; image.addClass('prepare'); } else out = image;
if( firstTime || isOldIE ) show(); else out.on(transitionend, show);
// in case the 'transitionend' didn't fire
// after hiding the last seen image, show the new one function show(){
clearTimeout(showSaftyTimer); out.off(transitionend).css({'transition':'none'}); overlay.removeClass('video'); if( activeType == 'video' ){ image[0].src = blankImg; video.addClass('prepare'); overlay.addClass('video'); } else image.prop({ 'src':activeURL, 'class':'prepare' });
// filthy hack for the transitionend event, but cannot work without it: setTimeout(function(){ image.add(video).removeAttr('style').removeClass('prepare'); overlay.removeClass('hide next prev'); setTimeout(function(){ image.add(video).on(transitionend, showDone); if(isOldIE) showDone(); // IE9 and below don't support transitionEnd... }, 0); },50);
} }
// a callback whenever a transition of an image or a video is done
function showDone(){ image.add(video).off(transitionend).addClass('zoomable');
if( activeType == 'video' ) video.removeClass('hide'); else autoplayBtn && options.autoplay && APControl.play();
if( typeof photobox.callback == 'function' ) photobox.callback.apply(imageLinks[activeImage]); } function scrollZoom(e, delta){
if( activeType == 'video' ){ var zoomLevel = video.data('zoom') || 1; zoomLevel += (delta / 10); if( zoomLevel < 0.5 ) return false;
video.data('zoom', zoomLevel).css({width:624*zoomLevel, height:351*zoomLevel}); } else{ var zoomLevel = image.data('zoom') || 1, getSize = image[0].getBoundingClientRect();
zoomLevel += (delta / 10);
if( zoomLevel < 0.1 ) zoomLevel = 0.1;
image.data('zoom', zoomLevel).css({'transform':'scale('+ zoomLevel +')'});
// check if dragging should take effect (if image is larger than the window if( getSize.height > docElm.clientHeight || getSize.width > docElm.clientWidth ){ $(doc).on('mousemove.photobox', imageReposition); } else{ $(doc).off('mousemove.photobox'); image[0].style[transformOrigin] = '50% 50%'; }
} return false; } function thumbsResize(e, delta){ e.preventDefault(); e.stopPropagation(); // stop the event from bubbling up to the Overlay and enlarge the content itself var thumbList = photobox.thumbsList; thumbList.css('height', thumbList[0].clientHeight + (delta * 10) ); var h = caption[0].clientHeight / 2; wrapper[0].style.cssText = "margin-top: -"+ h +"px; padding: "+ h +"px 0;"; thumbs.hide().show(0); thumbsStripe.calc(); } // moves the image around during zoom mode on mousemove event function imageReposition(e){ var y = (e.clientY / docElm.clientHeight) * (docElm.clientHeight + 200) - 100, // extend the range of the Y axis by 100 each side yDelta = y / docElm.clientHeight * 100, xDelta = e.clientX / docElm.clientWidth * 100, origin = xDelta.toFixed(2)+'% ' + yDelta.toFixed(2) +'%';
image[0].style[transformOrigin] = origin; }
function stop(){ clearTimeout(APControl.autoPlayTimer); $(doc).off('mousemove.photobox'); preload.onload = function(){}; preload.src = preloadPrev.src = preloadNext.src = activeURL; }
function close(){ stop();
video.find('iframe').prop('src',).empty();
Photobox.prototype.setup(); history.clear();
overlay.removeClass('on video').addClass('hide');
image.on(transitionend, hide); isOldIE && hide();
function hide(){ if( overlay[0].className == ) return; // if already hidden overlay.removeClass('show hide error pbLoading'); image.removeAttr('class').removeAttr('style').off().data('zoom',1); if(noPointerEvents) // pointer-events lack support in IE, so just hide the overlay setTimeout(function(){ overlay.hide(); }, 200); }
// fallback if the 'transitionend' event didn't fire setTimeout(hide, 500); }
/*! Copyright (c) 2011 Brandon Aaron (http://brandonaaron.net) * Licensed under the MIT License (LICENSE.txt). * * Version: 3.0.6 */ var types = ['DOMMouseScroll', 'mousewheel'];
if ($.event.fixHooks){ for ( var i=types.length; i; ) $.event.fixHooks[ types[--i] ] = $.event.mouseHooks; }
$.event.special.mousewheel = { setup: function(){ if( this.addEventListener ){ for ( var i=types.length; i; ) this.addEventListener( types[--i], handler, false ); }else this.onmousewheel = handler; }, teardown: function(){ if ( this.removeEventListener ){ for ( var i=types.length; i; ) this.removeEventListener( types[--i], handler, false ); }else this.onmousewheel = null; } };
$.fn.extend({ mousewheel: function(fn){ return fn ? this.bind("mousewheel", fn) : this.trigger("mousewheel"); }, unmousewheel: function(fn){ return this.unbind("mousewheel", fn); } });
function handler(event){ var orgEvent = event || win.event, args = [].slice.call( arguments, 1 ), delta = 0, returnValue = true, deltaX = 0, deltaY = 0; event = $.event.fix(orgEvent); event.type = "mousewheel"; // Old school scrollwheel delta if( orgEvent.wheelDelta ){ delta = orgEvent.wheelDelta/120; } if( orgEvent.detail ){ delta = -orgEvent.detail/3; } // New school multidimensional scroll (touchpads) deltas deltaY = delta; // Gecko if( orgEvent.axis !== undefined && orgEvent.axis === orgEvent.HORIZONTAL_AXIS ){ deltaY = 0; deltaX = -1*delta; } // Webkit if( orgEvent.wheelDeltaY !== undefined ){ deltaY = orgEvent.wheelDeltaY/120; } if( orgEvent.wheelDeltaX !== undefined ){ deltaX = -1*orgEvent.wheelDeltaX/120; } // Add event and delta to the front of the arguments args.unshift(event, delta, deltaX, deltaY); return ($.event.dispatch || $.event.handle).apply(this, args); } /** * jQuery Plugin to add basic "swipe" support on touch-enabled devices * * @author Yair Even Or * @version 1.0.0 (March 20, 2013) */ $.event.special.swipe = { setup: function(){ $(this).bind('touchstart', $.event.special.swipe.handler); },
teardown: function(){ $(this).unbind('touchstart', $.event.special.swipe.handler); },
handler: function(event){ var args = [].slice.call( arguments, 1 ), // clone arguments array, remove original event from cloned array touches = event.originalEvent.touches, startX, startY, deltaX = 0, deltaY = 0, that = this;
event = $.event.fix(event);
if( touches.length == 1 ){ startX = touches[0].pageX; startY = touches[0].pageY; this.addEventListener('touchmove', onTouchMove, false); } function cancelTouch(){ that.removeEventListener('touchmove', onTouchMove); startX = startY = null; } function onTouchMove(e){ e.preventDefault();
var Dx = startX - e.touches[0].pageX, Dy = startY - e.touches[0].pageY;
if( Math.abs(Dx) >= 20 ){ cancelTouch(); deltaX = (Dx > 0) ? -1 : 1; } else if( Math.abs(Dy) >= 20 ){ cancelTouch(); deltaY = (Dy > 0) ? 1 : -1; } event.type = 'swipe'; args.unshift(event, deltaX, deltaY); // add back the new event to the front of the arguments with the delatas return ($.event.dispatch || $.event.handle).apply(that, args); } } };
$(doc).ready(prepareDOM);
// expose outside window._photobox = { history : history };
})(jQuery, document, window);